Написать программу SMTP-почтового сервера с использованием Socket API
bupt.edu.cn
, 163.com
, aliyun.com
, ...);chengli
, chengli@
, bupt.edu.cn
, ...DEV C++
Структура данных является одним из ключевых аспектов всего проекта, и полное понимание структуры данных позволяет поддерживать основные алгоритмы и процессы обработки. Ниже описаны глобальные переменные и переменные в функциях, а также определенные в проекте идентификаторы.```c char ehlo[BUFSIZE]; // Сохраняет ehlo клиента, используется для подключения к реальному почтовому серверу char mailFrom[BUFSIZE]; // Сохраняет адрес электронной почты клиента char rcptTo[5][BUFSIZE]; // Сохраняет адреса получателей char clientIP[5][BUFSIZE]; // IP-адрес клиента char ip[BUFSIZE]; // Временно сохраняет введенный пользователем IP-адрес char data[BUFSIZE]; // Сохраняет данные char imf[BUFSIZE * 10]; // Сохраняет стандартный формат письма char recvData[BUFSIZE]; // Временно сохраняет принимаемые данные char rcptJudge[BUFSIZE]; // Используется для проверки количества адресов получателей int i = 0; // Записывает количество адресов получателей int j = 0; // Записывает количество целевых адресов int error; // Определяет успешность вызова функций клиента int now[6]; // Записывает время char fname[256] = {0}; // Записывает имя файла для создания текстового файла FILE *fp; // Открывает файл char nativeIP[BUFSIZE]; // Сохраняет IP-адрес текущего компьютера char temp[3]; // Сохраняет код состояния char username[BUFSIZE]; // Сохраняет имя пользователя для входа char password[BUFSIZE]; // Сохраняет пароль для входа r1-r28 // Коды состояния для коммуникации протокола SMTP BUFSIZE // Размер буфера 4096 PORT // Номер порта 25
### Подпрограммы
Функции, которые выполняют подпрограммы, и значение каждого параметра.1. `int main()` — основная функция
Функциональность: вызывает функцию сервера `server()` и проверяет, был ли вызов успешным; если вызов успешен, вызывает функцию клиента `client()` и проверяет, был ли вызов успешным.
2. `int server()` — функция сервера
Функциональность: выполняет функции SMTP-сервера, принимает запросы TCP от клиентских программ электронной почты, принимает команды SMTP и данные электронной почты, сохраняет электронную почту в файле и создает журнал.
Значение параметров:
```c
int Ret; // для проверки успешности инициализации WSADATA
WSADATA wsaData; // для хранения данных Windows Sockets, возвращенных функцией WSAStartup
SOCKET ListeningSocket; // для прослушивания соединений клиентов
SOCKET socketConnection; // для соединения с клиентами
SOCKADDR_IN ServerAddr; // для хранения адреса сервера
SOCKADDR_IN ClientAddr; // для хранения адреса клиента
int ClientAddrLen = sizeof(ClientAddr); // для хранения длины адреса клиента
int flag = 1; // для проверки возможности соединения
int client()
— функция клиента
Функциональность: выполняет функции SMTP-клиента, устанавливает TCP-соединение с реальным сервером электронной почты, отправляет команды SMTP, отправляет сохраненные электронные письма на реальный сервер электронной почты.
Значение параметров: c int Ret1; // для проверки успешности инициализации WSADATA WSADATA wsaData1; // для хранения данных Windows Sockets, возвращенных функцией WSAStartup SOCKET socketclient; // для хранения сокета клиента SOCKADDR_IN nativeAddr; // для хранения локального IP-адреса SOCKADDR_IN clientAddr1; // для хранения IP-адреса клиента
4. int validEmail(char* addr)
— проверка корректности адреса электронной почты
Функциональность: предоставляет функцию проверки формата адреса электронной почты для отправителя и получателя, например, следующие адреса электронной почты некорректны: chengli
, chengli@
, bupt.edu.cn
, ...
Значение параметров:
int colonAddr = 0; // для хранения позиции символа ":"
int atAddr = 0; // для хранения позиции символа "@"
int pointAddr = 0; // для хранения позиции символа "."
int bracketAddr = 0; // для хранения позиции символа ">"
int error1 = 0; // для хранения значения ошибки
unsigned int a = 0; // для хранения счетчика
char* getIP()
Функциональность: получает IP-адрес клиента электронной почты. Значение параметров:
char* hostIP; // Сохраняет локальный IP в теле функции
char hostName[256]; // Сохраняет имя хоста почтового клиента
struct hostent *hostEntry; // Преобразует имя хоста почтового клиента в IP
char* translateIP(char* mail)
Функциональность: преобразует адрес сервера отправителя в IP-адрес; Значение параметров:
char ip[100] = { "smtp." }; // Сохраняет "smtp." для удобства нахождения домена сервера
char* IP; // Сохраняет IP сервера почты
int atAddr = 0; // Запоминает позицию символа "@"
int bracketAddr = 0; // Запоминает позицию символа ">"
unsigned int x = 0; // Счетчик
int y = 5; // Счетчик
struct hostent *hostEntry; // Находит IP сервера почты клиента
struct in_addr **addr_list; // Преобразует найденный IP в нужный формат
```7. `int time1()` —— Функция получения временной метки
Функционал: Получает текущее время для удобства записи в лог-файл
Значение параметров:
```c
time_t t; // Сохраняет время
time(&t); // Получает временну метку
lt = localtime(&t); // Преобразует в структуру времени
void Error()
—— Функция определения типа ошибки
Функционал: Предоставляет отчет об ошибках, пересылает отчет об ошибках сервера почты клиенту
(Примечание: Из-за использования большого количества операторов if, NS-схемы трудно рисовать с большим количеством if, поэтому if заменены на текст)
main()
—— Основная функция
Основные моменты реализации: Последовательно вызывает функции сервера и клиента и проверяет успешность вызова
int server()
—— Функция сервера Основные моменты реализации: Использует сокет для прослушивания сервера, запроса клиента, подтверждения соединения, чтобы клиент мог установить TCP-соединение. Затем, согласно протоколу SMTP, получает EHLO, AUTH LOGIN, имя пользователя, пароль, адрес отправителя, адрес получателя, содержимое письма, длину письма. После этого закрывает сокет и освобождает ресурсы. В документации использованы следующие часто используемые функции сокета:```c
int socket(int domain, int type, int protocol) // Создание сокета
int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len) // Привязка сокета
int recv(SOCKET socket, char FAR* buf, int len, int flags) // Получение данных
int send(SOCKET socket, char FAR* buf, int len, int flags) // Отправка данных
3. `int client()` —— Клиентская функция
Основные моменты реализации: сначала устанавливается соединение, получается адрес настоящего сервера электронной почты клиента, затем начинается общение с этим сервером, последовательно отправляются команды EHLO, AUTH LOGIN, имя пользователя, пароль, адрес электронной почты назначения, DATA, IMAP, конец письма, затем отправляется команда QUIT для завершения общения.
В реализации использованы следующие часто используемые функции сокета:
```c
int socket(int domain, int type, int protocol) // Создание сокета
int connect(SOCKET socket, const struct sockaddr* address, socklen_t address_len) // Установка соединения
int recv(SOCKET socket, char FAR* buf, int len, int flags) // Получение данных
int send(SOCKET socket, char FAR* buf, int len, int flags) // Отправка данных
ValidEmail(char *)
—— Проверка корректности адресов электронной почты отправителя и получателя
Основные моменты реализации: проверка корректности адресов электронной почты на соответствие стандартному формату адреса электронной почты.
Поскольку SSL не реализован, отметка для SSL-портов не ставится.
Отправка одного письма нескольким получателям, которые принадлежат разным доменам.
Ниже приведены скриншоты вывода программы.
Ниже приведены скриншоты сохраненного после выполнения программы лог-файла, названного по времени.
Содержимое лог-файла представлено ниже.
Логи отправки на целевые адреса 2 и 3 похожи на логи отправки на адрес 1, поэтому подробное описание не требуется, логи будут включены в отчет об эксперименте.
Используйте base64 для декодирования содержимого письма (сайт декодирования: https://1024tools.com/base64) для получения реального содержимого письма.
Ниже приведены письма, полученные с разных доменов.
Генерация отчета об ошибках при отправке писем
Ошибка отправителя
Поскольку Foxmail не выдает ошибки отправителя, мы изменили код, чтобы получить отчет об ошибках
Логи по времени
Ошибка получателя
Логи по времени содержат следующее
Ошибка при получении EHLO
Поскольку Foxmail обычно не выдает ошибки при получении EHLO
, мы изменили код, чтобы получить отчет об ошибках
Логи по времени содержат следующее
Ошибка при получении AUTH LOGIN
Поскольку Foxmail обычно не выдает ошибки при получении AUTH LOGIN
, мы изменили код, чтобы получить отчет об ошибках
Логи по времени содержат следующее
Ошибка при получении DATA
Поскольку Foxmail обычно не выдает ошибки при получении DATA
, мы изменили код, чтобы получить отчет об ошибках
Логи по времени содержат следующее 6. Обработка ошибки
.
Поскольку ошибки при обработке .
в Foxmail обычно не возникают, мы вначале изменили код, чтобы получить отчет об ошибках.
Содержимое лога с отметками времени
По результатам наблюдений было установлено, что Foxmail отправляет QUIT
и сразу завершает работу, поэтому нет необходимости проверять quit
, и, следовательно, нет отчета об ошибках QUIT
.
Реализация локального сервера для приема нескольких электронных адресов, то есть не фиксированных пользователей и паролей. Вышеупомянутый отправленный электронный адрес был адресом QQ, далее реализуется отправка электронного адреса 163.
Предоставлена функция обработки ошибок, подробности следующие:
При отправке электронной почты клиентом, если локальный сервер электронной почты не принимает команду, будет возвращен следующий отчет об ошибке (скриншоты отчетов об ошибках уже приведены выше):
EHLO——503 Can't receive EHLO\r\n
AUTH LOGIN——503 Can't receive AUTH LOGIN\r\n
DATA——503 Can't receive DATA\r\n
. ——503 Can't receive ' . '\r\n
При возникновении ошибки при установлении соединения процесс завершается и выводится сообщение об ошибке.3. Предоставлен метод Error для определения типа ошибки и сохранения его в лог-файле. Если возникает проблема при соединении сокета, возвращается код ошибки и завершается программа, что облегчает отладку.
В программе широко используются инструкции if-else для проверки ошибок.
Программа не полностью соответствует протоколу SMTP, изученному на уроках, для реализации процесса связи.
Различия
В изученном протоколе SMTP клиент после отправки EHLO сразу отправляет адрес отправителя и получателя, то есть после получения Yöntem 250-го кода состояния клиент не получает AUTH LOGIN. В нашей программе после получения EHLO от клиента отправляется 250-й код состояния, содержащий AUTH LOGIN, то есть требуется от клиента отправить имя пользователя и пароль. Таким образом, наша программа не ограничивается приемом одного адреса, а может принимать несколько адресов, таких как адреса QQ, 163 и Gmail, то есть не фиксированы имя пользователя и пароль.3. Преимущества
На основе реализации основных функций протокола SMTP были внедрены отчеты об ошибках, то есть пользователи могут подключиться к почтовому серверу с помощью протокола POP3 и скачать отчеты об ошибках;
Реализована проверка корректности адреса электронной почты;
Поддерживается несколько адресов получателей, принадлежащих к разным доменам;
Проводится проверка различных ошибок, возникающих при отправке электронной почты;
По формату электронной почты отправителя можно автоматически определить IP-адреса серверов электронной почты и установить с ними соединение, без необходимости ввода IP-адресов серверов электронной почты вручную.
Недостатки 1. Не удалось реализовать SSL-шифрование; 2. В случае возникновения ошибки при отправке писем через настоящий сервер электронной почты клиента, невозможно отправить сообщение об ошибке клиенту, ошибка отображается только в лог-файле, хотя в обычных условиях ошибки не возникают; 3. Эффективность работы ниже, чем у настоящего сервера; 4. В процессе обработки ошибок возвращаемые коды состояния и содержимое могут быть неполными.
Недостатки
Не удалось реализовать SSL-шифрование.
b) Если при отправке писем через настоящий сервер электронной почты клиента возникает ошибка, сообщение об ошибке не может быть отправлено клиенту, а отображается только в лог-файле, хотя в обычных условиях ошибки не возникают.
c) Эффективность работы ниже, чем у настоящего сервера.
d) В процессе обработки ошибок возвращаемые коды состояния и содержимое могут быть неполными.
Сколько времени ушло на практические занятия по отладке?
Время практической отладки составило около 10 часов.
Какие проблемы возникли с использованием средств программирования? Включая установку окружения Windows и программы VC. 1. При использовании Dev-C++ необходимо изменить конфигурацию компилятора, добавив следующие параметры в командную строку коннектора: -static-libgcc -lwsock32
.
2. Из-за сложности настройки SSL-окружения, вначале SSL-шифрование не использовалось, вместо этого использовалась программа, написанная с помощью Dev-C++. После завершения написания программы и переноса её в среду VS-2017 возникло множество ошибок, большинство из которых были связаны с изменением названий функций и количества переменных.
3. Настройка SSL-окружения в среде компиляции VS является сложной задачей, а также процесс изменения исходного кода на SSL-код является сложным, что является причиной неудачной реализации SSL.3. Какие проблемы возникли с использованием языка программирования? Включая использование C и навыки работы с C.
При отправке содержимого электронной почты клиент разбивает его на DATA fragment
, ...bytes
, imf
и .
. Сервер проверяет только последнюю точку и отправляет 250 OK
только после того, как точка будет считаться легитимной.
При отправке 250 Server ready
сервер должен включать AUTH LOGIN
, чтобы клиент отправил имя пользователя и пароль.
При возврате команды со статусом сервер должен добавлять другие символы после статусного кода, иначе произойдет ошибка, и сервер не сможет принять следующую команду SMTP от клиента.
По результатам данного эксперимента, какие недостатки вы видите в протоколе SMTP? Какие идеи по его улучшению у вас есть? 1. Недостатки
Протокол SMTP не обеспечивает достаточной безопасности. В протоколе SMTP, кроме имени пользователя, пароля и содержимого письма, которое кодируется с помощью Base64, все остальные данные передаются в открытом виде. Кодировка Base64 также легко поддается расшифровке, что делает протокол уязвимым для атак со стороны третьих лиц. Третьи лица могут перехватить содержимое писем, изменить адреса отправителя и получателя, а также содержимое писем. Протокол легко подвержен атакам посредника и повторному использованию.
Улучшения
Все команды, отправляемые протоколом SMTP, содержимое писем и адреса можно зашифровать. Уже существуют улучшенные версии, такие как SSL и X.25.
Подведите итоги данного эксперимента. В каких аспектах вы улучшили свои навыки в языке C, в разработке протоколов, в теоретическом обучении, в инженерии программного обеспечения и т.д.? 1. Впервые написали программу на языке C, состоящую из более чем тысячи строк кода. Этот эксперимент по созданию SMTP-сервера электронной почты позволил нам углубить знания в языке C, например, в чтении файлов, вызове функций сокета, логике программы и т.д. 2. В области разработки протоколов, мы углубили понимание протокола SMTP, более подробно изучили команды и коды состояния, укрепили знания о работе серверов электронной почты, о том, как пользователи могут использовать эти серверы для отправки писем, а также о том, как с помощью сокета устанавливается соединение. Использование Wireshark для отслеживания пакетов подтвердило необходимость улучшения безопасности SMTP. 3. В теоретическом обучении, мы более четко понимаем модель C/S и протоколы передачи электронной почты.Приложение: исходный код эксперимента
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )