Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > C/C++: Сетевое программирование > Winsock. SMTP connection


Автор: linuxoid 01.12.17, 05:21
Всем привет. Недавно начал разбираться с сокетами, написал пару простых программ для обмена данными по локальной сети. Сейчас захотел попробовать подключиться к smtp-серверу для отправки email письма и возникли некоторые сложности, решения которых я не нашел в google.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    DWORD SendMessage(const char * email, const char * msg, const char * from)
    {
            WSADATA _wsa;
            WSAStartup(MAKEWORD(2,2), &_wsa);
            hostent * h;
        int handle = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
        if(handle <= 0)
            printf("Error with creation socket %i", GetLastError());
     
        char * em = getSMTPServer(email);
        h = gethostbyname(em);
        delete em;
        if(h == NULL)
        {
            printf("ERROR WITH GETTING HOSTBYNAME - %i\n", GetLastError());
            closesocket(handle);
            return EXIT_FAILURE;
        }
        sockaddr_in destination;
        destination.sin_family = AF_INET;
        destination.sin_port = htons(25);
        destination.sin_addr.s_addr = *((unsigned long *) h->h_addr);
        if(connect(handle,(const sockaddr *)&destination, sizeof(sockaddr_in) == SOCKET_ERROR))
        {
            printf("ERROR WITH CONNECT SMTP-SERVER - %i\n", GetLastError());
            closesocket(handle);
            return EXIT_FAILURE;
        }
     
            ...
     
           WSACleanup();
    }


Функция getSMTPServer() самописная и возвращает smtp-сервер, основываясь на email-адресе.
После вызова функции connect() возвращается SOCKET_ERROR и GetLastError() == 10014

Автор: ter_nk_ 01.12.17, 06:07
Тебе интересно чисто разобраться?

Есть два момента, я не знаю с кем ты хочешь связать по 25 порту, но это обычный SMTP вроде и многие так уже не работают, а только с SSL ну и другим портом. И еще если ты пользуешься например gmail или еще кем не своим, полезь в настройки почты и сделай доступным для внешних программ.

Автор: ЫукпШ 01.12.17, 17:53
Цитата linuxoid @
После вызова функции connect() возвращается SOCKET_ERROR и GetLastError() == 10014

А что означает эта ошибка, ты посмотрел ?
Попробуй так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      
    ..
    if(connect(handle,(const sockaddr *)&destination, sizeof(destination) == SOCKET_ERROR))
    ..

connect, пример
Исходники sources.ru

Автор: linuxoid 01.12.17, 18:34
Цитата ter_nk_ @
Тебе интересно чисто разобраться?

Есть два момента, я не знаю с кем ты хочешь связать по 25 порту, но это обычный SMTP вроде и многие так уже не работают, а только с SSL ну и другим портом. И еще если ты пользуешься например gmail или еще кем не своим, полезь в настройки почты и сделай доступным для внешних программ.

не просто разобраться, а сделать так, чтобы письмо все-таки отправилось куда нужно. Наткнулся на статью, в которой говорилось что smtp использует 25 порт, вот я и пробую, возможно, это и правда уже неактуально и следует пробовать другой порт. Но если я буду использовать порт с ssl тогда мне нужно использовать шифрование. Я хотел написать на чистом С, без подключения сторонних библиотек.

Автор: Oleg2004 01.12.17, 19:37
Вполне возможно что самописная функция неверно формирует аргумент для gethostbyname()
Проверьте - поле h_addr структуры hostent
Скажем распечатайте.

Автор: ter_nk_ 02.12.17, 06:40
Цитата linuxoid @
не просто разобраться, а сделать так, чтобы письмо все-таки отправилось куда нужно. Наткнулся на статью, в которой говорилось что smtp использует 25 порт, вот я и пробую, возможно, это и правда уже неактуально и следует пробовать другой порт. Но если я буду использовать порт с ssl тогда мне нужно использовать шифрование. Я хотел написать на чистом С, без подключения сторонних библиотек.


А ты пробовал смотреть не статью, а конкретные настройки сервера, они могут убирать такие дыры безопасности, кто сейчас 25 порт использует?

Автор: ЫукпШ 02.12.17, 11:51
Цитата ter_nk_ @
А ты пробовал смотреть не статью, а конкретные настройки сервера, они могут убирать такие дыры безопасности, кто сейчас 25 порт использует?

я попробовал.
Можно законнектится на smtp.mail.ru:25.
С операцией "connect" нет проблем.

Автор: ter_nk_ 02.12.17, 12:51
Цитата ЫукпШ @
я попробовал.
Можно законнектится на smtp.mail.ru:25.
С операцией "connect" нет проблем.


С gmail это не пройдет.

Автор: Oleg2004 02.12.17, 14:13
Согласен, что почтовик может и требовать аутентификации, и совсем на другой порт и прочая.
Но в любом случае коннект все равно проходил бы, если связка Ip:port была бы реальной. А тут сам коннект не проходит - т.е. первый SYN не находит адресат...

Автор: ЫукпШ 02.12.17, 14:29
Цитата ter_nk_ @
Цитата ЫукпШ @
я попробовал.
Можно законнектится на smtp.mail.ru:25.
С операцией "connect" нет проблем.


С gmail это не пройдет.

Предлагаешь все сервера проверять ?
user posted image

Автор: ter_nk_ 02.12.17, 14:47
Извините не знал. У них в настройках для клиента указан только порт с SSL, да я думал на хрена типа оставлять обычный.

Автор: Oleg2004 02.12.17, 14:51
Именно поэтому я считаю что засада в формировании адреса почтовика...

Автор: linuxoid 05.12.17, 06:39
Доброго всем дня. Не был дома несколько дней, не мог отвечать вам на форуме. По поводу ваших доводов о неправильной работе getSMTPServer(const char * email);
Возвращаемое значение функцией перед инициализацией gethostbyname();
1.png (, : 1195)
структура hostent после инициализации:
2.png (, : 1139)
Кажется, что все в порядке с этим

Автор: Oleg2004 05.12.17, 09:37
И тем не менее для коннекта эта ошибка обозначает неверно сформированный адрес назначения.
У меня вызывает подозрение приведение типа
destination.sin_addr.s_addr = *((unsigned long *) h->h_addr);
Такая запись характерна для связывания локального сокета для BIND на сервере.
Попробуйте привести к типу struct in_addr таким образом
destination.sin_addr = * ((struct in_addr *) h-> h_addr);
И еще проверка - просто возьмите значение возвращенное значение h_addr и посмотрите - получается ли IP-адрес назначения. Адрес идет уже в сетевом порядке.

Автор: linuxoid 05.12.17, 10:17
Цитата Oleg2004 @
И тем не менее для коннекта эта ошибка обозначает неверно сформированный адрес назначения.
У меня вызывает подозрение приведение типа
destination.sin_addr.s_addr = *((unsigned long *) h->h_addr);
Такая запись характерна для связывания локального сокета для BIND на сервере.
Попробуйте привести к типу struct in_addr таким образом
destination.sin_addr = * ((struct in_addr *) h-> h_addr);
И еще проверка - просто возьмите значение возвращенное значение h_addr и посмотрите - получается ли IP-адрес назначения. Адрес идет уже в сетевом порядке.

Попробовал изменить преобразование типов как указали Вы, но результата это особого не дало. Структура sin_addr заполнилась тем же самым IP-адресом, с которым я и пытался установить соединение.
[attach=#0][/attach]

Кстати, попробовал вывести на консоль значение h->h_addr;
Результат следующий:
[attach=#1][/attach]

Возможно, в этом и таится проблема? или такое значение это норма?

Автор: ЫукпШ 05.12.17, 12:16
Цитата Oleg2004 @
Попробуйте привести к типу struct in_addr таким образом
destination.sin_addr = * ((struct in_addr *) h-> h_addr);

Тест я делал так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      hostent       *sh;
    //..
      sh = ::gethostbyname(pName);
    //..
      DWORD dwIP  = ((in_addr* )sh->h_addr)->s_addr;
    //..
     peer.sin_addr.s_addr = dwIP;
    //..

Автор: Oleg2004 05.12.17, 15:38
Скорее всего необходимые преобразования и приведения к типу можно выполнить несколькими способами. :)
Однако эта хрень что на втором аттаче это вообще ни в какие ворота...
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    struct hostent
    {
       char *h_name;        /* Официальное имя хоста.*/
       char **h_aliases;    /* Массив псевдонимов данного хоста, заканчивающийся нулем */
       int  h_addrtype;     /* Тип возвращенного функцией адреса; для Интернет это AF_INET */
       int  h_length;       /* Длина адреса в байтах. Для типа AF_INET длина всегда 4 байта */
       char **h_addr_list;  /* Указатель на массив указателей IPv4 (IPv6) адресов хоста. Он заканчивается нулем, порядок  следования байтов сетевой. */
    #define h_addr h_addr_list[0]/*Определяет h_addr как самый первый адрес хоста в списке*/
    };

Ведь h_addr - это 4-х байтовый адрес хоста в сетевом порядке.
Я пропинговал и получилось
Pinging smtp.mail.ru [94.100.180.160] with 32 bytes of data:
Адрес именно тот, который у вас на первой картинке - в s_b1 s_b2 s_b3 s_b4
А вот что и откуда взялось на второй? кракозябрыsmtp.mail.ru?
Кстати, на той же первой картинке порт не 25-й, а 6400.
Это так надо? Связка 94.100.180.160:6400 тоже даст bad address
Кстати, было бы интересно посмотреть уходящий от коннекта первый TCP-SYN-сегмент программой типа TCPView, потому что в таблице TCP соединений соединение не зафиксировано, и адрес точки соединения там не виден.

Автор: ЫукпШ 06.12.17, 18:21
Цитата Oleg2004 @
Скорее всего необходимые преобразования и приведения к типу можно выполнить несколькими способами. :)

Всё это будет работать:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      dwIP  = ((in_addr* )sh->h_addr)->s_addr;
      dwIP  = *(DWORD *) sh->h_addr_list[0];
      dwIP  = *(DWORD *) sh->h_addr;

Выяснилось вот что: результат connect-а зависит от провайдера.
Или системного администратора.
С домашнего компа connect всегда удачный. С рабочего компа - всегда нет.
Кто-то запретил..

Автор: Oleg2004 06.12.17, 20:05
Цитата ЫукпШ @
С домашнего компа connect всегда удачный. С рабочего компа - всегда нет.
Кто-то запретил..

Ну, честно говоря я бы такие выводы не делал.
Если бы такое происходило, реакция на коннект была бы другая.
Все TCP- соединения начинаются с SYN-сегмента. И он должен уходить всегда. Если только на провайдере не отслеживаются все такие сегменты. Это практически невозможно. Т.е. бессмысленно. Разве что на каком то суперзасекреченном компе. :)

Добавлено
Однако судя по описанию ошибки
Цитата
Bad address (неверный адрес).
Система обнаружила неверный указатель на адрес при попытке использовать его в вызове функции. Эта ошибка происходит при передачи приложением неверного указателя, или если размер буфера слишком мал - например, если длина аргумента, представляющего собой структуру типа sockaddr, меньше, чем sizeof(struct sockaddr).

сегмент TCP вообще не инициируется.
Т.е. из локального хоста он не уходит, так как функция встретила проблемы с указателем на ее параметр
Блин, просто какой то бред - еще раз проверил код ТС - да нет, ошибки не вижу... :wall:

Добавлено
Единственная совершенно нетипичная команда для подобного кода, которая присутствует в коде, так это удаление аргумента функции gethost...
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    h = gethostbyname(em);
        delete em;

Зачем???

Автор: ЫукпШ 06.12.17, 21:14
Цитата Oleg2004 @
Цитата ЫукпШ @
С домашнего компа connect всегда удачный. С рабочего компа - всегда нет.
Кто-то запретил..

Ну, честно говоря я бы такие выводы не делал.
Если бы такое происходило, реакция на коннект была бы другая.

Да, я забыл упомянуть, что ошибка совсем другая.
А именно - 10061. Очень часто это результат деятельности фиревола.
В нашей организации так - что сделаешь..
До smtp - сервера конторы я дотягиваюсь, а во внешнии сети - нет.

Автор: Oleg2004 06.12.17, 21:17
Цитата ЫукпШ @
А именно - 10061.

:yes:

Автор: linuxoid 07.12.17, 04:16
Цитата Oleg2004 @

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    h = gethostbyname(em);
        delete em;

Зачем???

В переменной em хранится строка с адресом (smtp.mail.ru). После того, как я вызываю функцию gethostbyname() я очищаю память этой переменной, т.к. она больше не нужна.

Добавлено
Сейчас ради интереса попробовал пропинговать любой внешний хост и результат был отрицательный. Пинг таких хостов как google.com, mail.ru, yandex.ru, smtp.mail.ru заканчиваются тем, что команде ping не удается обнаружить данные узлы. Не сильный эксперт в сетях, но, кажется, что существует какая-то блокировка или какой-то прокси. Кстати, пингую я и запускаю программу не с домашнего компьютера, а с рабочего. И как упомянул [ЫукпШ] возможно, с рабочего компьютера connect всегда завершается ошибкой.

Автор: Oleg2004 07.12.17, 14:43
Цитата linuxoid @
Пинг таких хостов как google.com, mail.ru, yandex.ru, smtp.mail.ru заканчиваются тем, что команде ping не удается обнаружить данные узлы. Не сильный эксперт в сетях, но, кажется, что существует какая-то блокировка или какой-то прокси. Кстати, пингую я и запускаю программу не с домашнего компьютера, а с рабочего.

С моего домашнего пингуются все.
Цитата
И как упомянул [ЫукпШ] возможно, с рабочего компьютера connect всегда завершается ошибкой.

Ну так что мешает вашу прогу запустить с домашнего???
Но еще раз повторю - коннект с ошибкой 10014 вообще не покидает комп.
Это не ошибка соединения или его блокировка файрволлом или провом. Это онибка в запуске коннекта - он вообще не запускается.
Цитата linuxoid @
В переменной em хранится строка с адресом (smtp.mail.ru). После того, как я вызываю функцию gethostbyname() я очищаю память этой переменной, т.к. она больше не нужна.

Это понятно... а смысл какой глубокий? Эта строка занимает пусть десяток байтов...Что за экономия?

Автор: linuxoid 07.12.17, 17:40
Цитата Oleg2004 @
Цитата linuxoid @
Пинг таких хостов как google.com, mail.ru, yandex.ru, smtp.mail.ru заканчиваются тем, что команде ping не удается обнаружить данные узлы. Не сильный эксперт в сетях, но, кажется, что существует какая-то блокировка или какой-то прокси. Кстати, пингую я и запускаю программу не с домашнего компьютера, а с рабочего.

С моего домашнего пингуются все.
Цитата
И как упомянул [ЫукпШ] возможно, с рабочего компьютера connect всегда завершается ошибкой.

Ну так что мешает вашу прогу запустить с домашнего???
Но еще раз повторю - коннект с ошибкой 10014 вообще не покидает комп.
Это не ошибка соединения или его блокировка файрволлом или провом. Это онибка в запуске коннекта - он вообще не запускается.
Цитата linuxoid @
В переменной em хранится строка с адресом (smtp.mail.ru). После того, как я вызываю функцию gethostbyname() я очищаю память этой переменной, т.к. она больше не нужна.

Это понятно... а смысл какой глубокий? Эта строка занимает пусть десяток байтов...Что за экономия?

Экономии может и нет, но это уже привычка. Так научили. Ладно, я с ошибкой разобрался. Тему можно закрыть! Всем спасибо

Автор: Oleg2004 07.12.17, 19:03
Цитата linuxoid @
Ладно, я с ошибкой разобрался. Тему можно закрыть! Всем спасибо

Разобрались - это плюс. :)
А вот не поделились с коллегами о причине ее - это минус.
Мы тут старались, время свое тратили на поиск ошибки, пытаясь вам помочь, и на тебе...секрет.
Ведь форум учит не только ТС, но и всех других - чтобы они не делали подобных ошибок. <_<
А для закрытия темы нажмите справа вверху - Вопрос решен.

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)