1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/Leeyuxun-Simple-Mail-Server

В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Программа на языке C для создания простого почтового сервера

Описание эксперимента и экспериментальной среды

Задачи эксперимента

Написать программу SMTP-почтового сервера с использованием Socket API

Содержание эксперимента

  1. В качестве SMTP-сервера принимать TCP-соединения от клиентских программ, принимать команды SMTP и данные писем, сохранять письма в файл;
  2. В качестве SMTP-клиента устанавливать TCP-соединение с реальным почтовым сервером, отправлять команды SMTP, отправлять сохраненные письма на реальный почтовый сервер;
  3. Предоставлять отчеты об ошибках писем: пересылать отчеты об ошибках от реального почтового сервера клиентским программам;
  4. Поддерживать отправку одного письма нескольким получателям, требуя, чтобы получатели принадлежали к разным доменам (например, bupt.edu.cn, 163.com, aliyun.com, ...);
  5. Предоставлять функцию проверки формата адресов электронной почты отправителя и получателя, например, следующие адреса электронной почты являются неверными: 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; // для проверки возможности соединения
  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; // для хранения счетчика
  2. char* getIP()

    Функциональность: получает IP-адрес клиента электронной почты. Значение параметров:

char* hostIP;				// Сохраняет локальный IP в теле функции
char hostName[256];		// Сохраняет имя хоста почтового клиента
struct hostent *hostEntry;	// Преобразует имя хоста почтового клиента в IP
  1. 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);     // Преобразует в структуру времени
  1. void Error() —— Функция определения типа ошибки

    Функционал: Предоставляет отчет об ошибках, пересылает отчет об ошибках сервера почты клиенту

Схема взаимосвязей подпрограмм

1570456967883

Алгоритмические схемы

Общая схема алгоритма

1570457055794

Схема NS-боксов для основной функции

1570457068039

Схема NS-боксов для функции сервера

1570457082800

Схема NS-боксов для функции клиента

1570457090449

(Примечание: Из-за использования большого количества операторов if, NS-схемы трудно рисовать с большим количеством if, поэтому if заменены на текст)

Основные функциональные модули

  1. main() —— Основная функция

    Основные моменты реализации: Последовательно вызывает функции сервера и клиента и проверяет успешность вызова

  2. 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) // Отправка данных
  1. ValidEmail(char *) —— Проверка корректности адресов электронной почты отправителя и получателя

    Основные моменты реализации: проверка корректности адресов электронной почты на соответствие стандартному формату адреса электронной почты.

Показания и анализ результатов экспериментов

Тестирование реализованных функцийДля каждой реализованной функции проведено тестирование, и результаты выводятся в виде скриншотов.

  1. Поскольку SSL не реализован, отметка для SSL-портов не ставится.

    1570457582715

    1570457615784

  2. Отправка одного письма нескольким получателям, которые принадлежат разным доменам.

    1570457659516

    Ниже приведены скриншоты вывода программы.

    1570457668896

    1570457685949

    1570457697414

    1570457711773

    Ниже приведены скриншоты сохраненного после выполнения программы лог-файла, названного по времени.

    1570457723898

    Содержимое лог-файла представлено ниже.

    1570457747310

    1570457754007

    1570457762168

    1570457771324

    1570457776111

    1570457787256

    1570457792426

    1570457798935

    Логи отправки на целевые адреса 2 и 3 похожи на логи отправки на адрес 1, поэтому подробное описание не требуется, логи будут включены в отчет об эксперименте.

    Используйте base64 для декодирования содержимого письма (сайт декодирования: https://1024tools.com/base64) для получения реального содержимого письма.

    1570458208569

    1570458220376

    Ниже приведены письма, полученные с разных доменов. 1570458252177

    1570458292381

    1570458299259

  3. Генерация отчета об ошибках при отправке писем

    1. Ошибка отправителя

      Поскольку Foxmail не выдает ошибки отправителя, мы изменили код, чтобы получить отчет об ошибках

      1570458368163

      1570458374129

      Логи по времени

      1570458393735

    2. Ошибка получателя

      1570458420577

      1570458428161

      Логи по времени содержат следующее

      1570458447126

    3. Ошибка при получении EHLO

      Поскольку Foxmail обычно не выдает ошибки при получении EHLO, мы изменили код, чтобы получить отчет об ошибках

      1570458522622

      1570458529130

      Логи по времени содержат следующее

      1570458536158

    4. Ошибка при получении AUTH LOGIN

      Поскольку Foxmail обычно не выдает ошибки при получении AUTH LOGIN, мы изменили код, чтобы получить отчет об ошибках

      1570458595554

      1570458601831

      Логи по времени содержат следующее

      1570458616613

    5. Ошибка при получении DATA

      Поскольку Foxmail обычно не выдает ошибки при получении DATA, мы изменили код, чтобы получить отчет об ошибках

      1570458665510

      1570458677278

      Логи по времени содержат следующее 1570458682084 6. Обработка ошибки .

      Поскольку ошибки при обработке . в Foxmail обычно не возникают, мы вначале изменили код, чтобы получить отчет об ошибках.

      1570458758374

      1570458782507

      Содержимое лога с отметками времени

      1570458791157

    6. По результатам наблюдений было установлено, что Foxmail отправляет QUIT и сразу завершает работу, поэтому нет необходимости проверять quit, и, следовательно, нет отчета об ошибках QUIT.

  4. Реализация локального сервера для приема нескольких электронных адресов, то есть не фиксированных пользователей и паролей. Вышеупомянутый отправленный электронный адрес был адресом QQ, далее реализуется отправка электронного адреса 163.

    1570458900597

    1570458906161

    1570458913048

Обработка ошибок

Предоставлена функция обработки ошибок, подробности следующие:

  1. При отправке электронной почты клиентом, если локальный сервер электронной почты не принимает команду, будет возвращен следующий отчет об ошибке (скриншоты отчетов об ошибках уже приведены выше):

    1. Не получен EHLO——503 Can't receive EHLO\r\n
    2. Не получен AUTH LOGIN——503 Can't receive AUTH LOGIN\r\n
    3. Не получен DATA——503 Can't receive DATA\r\n
    4. Не получен . ——503 Can't receive ' . '\r\n
  2. При возникновении ошибки при установлении соединения процесс завершается и выводится сообщение об ошибке.3. Предоставлен метод Error для определения типа ошибки и сохранения его в лог-файле. Если возникает проблема при соединении сокета, возвращается код ошибки и завершается программа, что облегчает отладку.

  3. В программе широко используются инструкции if-else для проверки ошибок.

Сравнение с протоколом SMTP

  1. Программа не полностью соответствует протоколу SMTP, изученному на уроках, для реализации процесса связи.

  2. Различия

    В изученном протоколе SMTP клиент после отправки EHLO сразу отправляет адрес отправителя и получателя, то есть после получения Yöntem 250-го кода состояния клиент не получает AUTH LOGIN. В нашей программе после получения EHLO от клиента отправляется 250-й код состояния, содержащий AUTH LOGIN, то есть требуется от клиента отправить имя пользователя и пароль. Таким образом, наша программа не ограничивается приемом одного адреса, а может принимать несколько адресов, таких как адреса QQ, 163 и Gmail, то есть не фиксированы имя пользователя и пароль.3. Преимущества

  3. На основе реализации основных функций протокола SMTP были внедрены отчеты об ошибках, то есть пользователи могут подключиться к почтовому серверу с помощью протокола POP3 и скачать отчеты об ошибках;

  4. Реализована проверка корректности адреса электронной почты;

  5. Поддерживается несколько адресов получателей, принадлежащих к разным доменам;

  6. Проводится проверка различных ошибок, возникающих при отправке электронной почты;

  7. По формату электронной почты отправителя можно автоматически определить IP-адреса серверов электронной почты и установить с ними соединение, без необходимости ввода IP-адресов серверов электронной почты вручную.

  8. Недостатки 1. Не удалось реализовать SSL-шифрование; 2. В случае возникновения ошибки при отправке писем через настоящий сервер электронной почты клиента, невозможно отправить сообщение об ошибке клиенту, ошибка отображается только в лог-файле, хотя в обычных условиях ошибки не возникают; 3. Эффективность работы ниже, чем у настоящего сервера; 4. В процессе обработки ошибок возвращаемые коды состояния и содержимое могут быть неполными.

Недостатки

Не удалось реализовать SSL-шифрование.

b) Если при отправке писем через настоящий сервер электронной почты клиента возникает ошибка, сообщение об ошибке не может быть отправлено клиенту, а отображается только в лог-файле, хотя в обычных условиях ошибки не возникают.

c) Эффективность работы ниже, чем у настоящего сервера.

d) В процессе обработки ошибок возвращаемые коды состояния и содержимое могут быть неполными.

Заключение и выводы

  1. Сколько времени ушло на практические занятия по отладке?

    Время практической отладки составило около 10 часов.

  2. Какие проблемы возникли с использованием средств программирования? Включая установку окружения Windows и программы VC. 1. При использовании Dev-C++ необходимо изменить конфигурацию компилятора, добавив следующие параметры в командную строку коннектора: -static-libgcc -lwsock32. 2. Из-за сложности настройки SSL-окружения, вначале SSL-шифрование не использовалось, вместо этого использовалась программа, написанная с помощью Dev-C++. После завершения написания программы и переноса её в среду VS-2017 возникло множество ошибок, большинство из которых были связаны с изменением названий функций и количества переменных. 3. Настройка SSL-окружения в среде компиляции VS является сложной задачей, а также процесс изменения исходного кода на SSL-код является сложным, что является причиной неудачной реализации SSL.3. Какие проблемы возникли с использованием языка программирования? Включая использование C и навыки работы с C.

    1. Впервые столкнулся с программированием сокетов на C, в процессе этого пришлось изучить множество материалов для использования некоторых функций сокетов.
    2. Использование C для чтения данных из файла и записи данных в файл является сложной задачей, для этого также пришлось изучить множество материалов.
    3. Функция времени была написана самостоятельно, не было обнаружено встроенной функции времени в C.
    4. Использование вложенных подфункций может привести к логическим ошибкам.
    5. В начале не было реализовано единое возвращаемое значение, что привело к логическим ошибкам при написании основной функции.
  3. При отправке содержимого электронной почты клиент разбивает его на DATA fragment, ...bytes, imf и .. Сервер проверяет только последнюю точку и отправляет 250 OK только после того, как точка будет считаться легитимной.

  4. При отправке 250 Server ready сервер должен включать AUTH LOGIN, чтобы клиент отправил имя пользователя и пароль.

  5. При возврате команды со статусом сервер должен добавлять другие символы после статусного кода, иначе произойдет ошибка, и сервер не сможет принять следующую команду SMTP от клиента.

  6. По результатам данного эксперимента, какие недостатки вы видите в протоколе SMTP? Какие идеи по его улучшению у вас есть? 1. Недостатки

    Протокол SMTP не обеспечивает достаточной безопасности. В протоколе SMTP, кроме имени пользователя, пароля и содержимого письма, которое кодируется с помощью Base64, все остальные данные передаются в открытом виде. Кодировка Base64 также легко поддается расшифровке, что делает протокол уязвимым для атак со стороны третьих лиц. Третьи лица могут перехватить содержимое писем, изменить адреса отправителя и получателя, а также содержимое писем. Протокол легко подвержен атакам посредника и повторному использованию.

    1. Улучшения

      Все команды, отправляемые протоколом SMTP, содержимое писем и адреса можно зашифровать. Уже существуют улучшенные версии, такие как SSL и X.25.

  7. Подведите итоги данного эксперимента. В каких аспектах вы улучшили свои навыки в языке C, в разработке протоколов, в теоретическом обучении, в инженерии программного обеспечения и т.д.? 1. Впервые написали программу на языке C, состоящую из более чем тысячи строк кода. Этот эксперимент по созданию SMTP-сервера электронной почты позволил нам углубить знания в языке C, например, в чтении файлов, вызове функций сокета, логике программы и т.д. 2. В области разработки протоколов, мы углубили понимание протокола SMTP, более подробно изучили команды и коды состояния, укрепили знания о работе серверов электронной почты, о том, как пользователи могут использовать эти серверы для отправки писем, а также о том, как с помощью сокета устанавливается соединение. Использование Wireshark для отслеживания пакетов подтвердило необходимость улучшения безопасности SMTP. 3. В теоретическом обучении, мы более четко понимаем модель C/S и протоколы передачи электронной почты.Приложение: исходный код эксперимента

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Простой почтовый сервер, написанный на языке C Развернуть Свернуть
Отмена

Обновления

Пока нет обновлений

Участники

все

Язык

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/Leeyuxun-Simple-Mail-Server.git
git@api.gitlife.ru:oschina-mirror/Leeyuxun-Simple-Mail-Server.git
oschina-mirror
Leeyuxun-Simple-Mail-Server
Leeyuxun-Simple-Mail-Server
master