Имя хоста сервера почтового агента (MSA) с адреса электронной почты через C++

Я ищу общий способ получения имени хоста серверов отправки почты, используя только адрес электронной почты с помощью c/ C++, чтобы я мог подключиться к порту 587 и / или 465 отправки smtp.

Например, example@gmail.com -> smtp.gmail.com или gmail-smtp-msa.l.google.com.

В основном я посылаю некоторые конкретные письма от имени клиентов. В конце концов, похоже, что они отправили письмо. До сих пор я все еще использую простую карту имен доменов, например {gmail.com, smtp.gmail.com}, которую я расширяю всякий раз, когда это необходимо. Однако я бы очень хотел избежать этой карты и автоматизировать этот процесс.

Некоторое время назад я опубликовал сокеты C++ - имя хоста сервера smtp с адреса электронной почты, который должен был быть тем же вопросом, может быть, он был недостаточно ясен. В ответ мне посоветовали выполнить поиск DNS и запросить записи MX. Книга и реализация позже я заметил, что эти записи mx не подходят для отправки почты (не может подключиться к порту 587, только 25). Я проверил все другие типы, определенные в resolv.h, и проверил nslookup (который, вероятно, в любом случае использует resolv.h), и теперь я действительно думаю, что это невозможно сделать с помощью поиска DNS. Если кто-то хочет попробовать, проверьте nslookup -type=mx gmail.com вы не увидите ничего-мса... вещи.

Сейчас я немного отчаялся, потому что очень хочу это сделать. Однако я также не хочу тратить на это недели. Я был бы также очень рад ответу, говорящему мне, что это невозможно в разумные сроки.


РЕДАКТИРОВАТЬ:

Следуя минимальному примеру, я только что сделал очень быстро с помощью подхода DNS MX и где я попытался подключиться к портам 25, 465, 587.

/*c++1z, Don't forget -lresolv*/
#include <resolv.h>
#include <string>
#include <map>
#include <sstream>
#include <cerrno>
#include <netdb.h>
#include <iostream>
#include <cstddef>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netinet/tcp.h>

//like assert but throws instead
#define CHECK(condition)\
    if(!(condition)){\
        std::ostringstream oss;\
        oss << __FILE__ << ":" << __LINE__ << "\nerrno:" << errno << "\nh_errno:" << h_errno << std::endl;\
        throw std::runtime_error(oss.str());\
    }

//!
//! \brief mxRecords performs dns T_MX lookup
//! \param domain the domain part of the email e.g. example@gmail.com -> gmail.com
//! \return multimap of {priority, server hostname} pairs
//!
auto mxRecords(std::string const&domain)
->std::multimap<size_t/*priority*/,std::string/*server name*/>
{
    std::basic_string<unsigned char> response(PACKETSZ,'0');
    int response_size = res_search(domain.data(),C_IN,T_MX,response.data(),response.size());
    CHECK(response_size != -1);
    response.resize(response_size);

    ns_msg handle;
    CHECK(ns_initparse(response.data(),response.size(),&handle) != -1);

    std::multimap<size_t,std::string> ret;
    for(int i = 0; i < ns_msg_count(handle,ns_s_an); ++i)
    {
        ns_rr rr;
        if(ns_parserr(&handle,ns_s_an,i,&rr) == -1)
            continue;
        if(ns_rr_type(rr) != ns_t_mx)
            continue;
        std::string exp_dn(MAXDNAME,0);
        if(ns_name_uncompress(ns_msg_base(handle),ns_msg_end(handle),ns_rr_rdata(rr)+NS_INT16SZ,exp_dn.data(),exp_dn.size()) == -1)
            continue;
        ret.emplace(ns_get16(ns_rr_rdata(rr)),exp_dn.data());
    }
    return ret;
}
bool __connect(std::string const&host, std::string const&port)
{
    addrinfo const __req{0,AF_UNSPEC,SOCK_STREAM,IPPROTO_TCP,0,nullptr,nullptr,nullptr};
    addrinfo *__pai(nullptr);
    if(getaddrinfo(host.data(),port.data(),&__req,&__pai) != 0)
        return false;
    for(addrinfo *iter = __pai; iter != nullptr; iter = iter->ai_next)
    {
        int fd = socket(iter->ai_family, iter->ai_socktype, iter->ai_protocol);
        if(fd == -1)
            continue;
        int retries = 1;
        setsockopt(fd, IPPROTO_TCP, TCP_SYNCNT, &retries, sizeof(retries));
        if(::connect(fd, iter->ai_addr, iter->ai_addrlen) == -1)
        {
            close(fd);
            continue;
        }
        freeaddrinfo(__pai);
        close(fd);
        return true;
    }
    freeaddrinfo(__pai);
    return false;
}
int main()
{
    std::string domain = "gmail.com";

    //get mx records and try to connect for port 25,465,587
    auto mx_records = mxRecords(domain);
    std::cout << "----------MX RECORDS <priority|host|port25|port465|port587> for " << domain << std::endl;
    for(auto iter = mx_records.begin(); iter != mx_records.end(); ++iter)
    {
        std::cout << iter->first << " | " << iter->second;
        //try port 25|port 465 | port 587
        for(auto port : {"25","465","587"})
        {
            if(__connect(iter->second,port))
                std::cout << " | y";
            else
                std::cout << " | n";
        }
        std::cout << std::endl;
    }
    return 0;
}

дает в качестве выхода

----------MX RECORDS <priority|host|port25|port465|port587> for gmail.com
5 | gmail-smtp-in.l.google.com | y | n | n
10 | alt1.gmail-smtp-in.l.google.com | y | n | n
20 | alt2.gmail-smtp-in.l.google.com | y | n | n
30 | alt3.gmail-smtp-in.l.google.com | y | n | n
40 | alt4.gmail-smtp-in.l.google.com | y | n | n

Обратите внимание на " внутри " внутри этих имен хостов. Я сделал запрос DNS для cname smtp.gmail.com, и он дает мне gmail-smtp-msa.l.google.com, так что "msa", который открыт для портов 587/465. Я действительно думаю, что эти записи mx в настоящее время хороши только для порта 25, а не для отправки почты.


РЕДАКТИРОВАТЬ № 2:

Существует большая путаница с портами 25, 465, 587 в Интернете. Просто, чтобы прояснить ситуацию для меня, эти порты не отличаются по ssl/tls априори. Порт 25, например, когда вы хотите отправить электронное письмо с example@yourDomain.com на example@gmail.com. Порт 587, например, когда вы хотите отправить электронное письмо с example1@gmail.com на example2@gmail.com. Наконец, порт 465[устарел] точно такой же, как и 587, с добавлением, что он начинается уже с ssl/tls. Пожалуйста, поправьте меня, если я ошибаюсь, но я уверен, что нет! После логов 3 EHLO запускается из моего реального приложения. Сначала используется имя хоста mx-записи и порт 25, затем - "smtp.gmail.com" и порт 587, а последний - "smtp.gmail.com" и порт 465. Обратите внимание на разницу для 587/465 с параметром команды AUTH после безопасное соединение и 25 без опции AUTH. Таким образом, при отправке электронных писем через порт 25 получатель получает уведомление о том, что отправитель не проверен, и вы, как правило, вскоре получаете блокировку (СПАМ). В любом случае мне нужна эта команда AUTH, приложение полностью легально, нет спама и т. Д., И именно поэтому мне нужны SMTP-серверы с открытым портом 587, и я не думаю, что эти MX-серверы являются правильными.

ПОРТ-25

S:220 mx.google.com ESMTP ############### - gsmtp
C:EHLO ###############
S:250-mx.google.com at your service, ###############
  250-SIZE 157286400
  250-8BITMIME
  250-STARTTLS
  250-ENHANCEDSTATUSCODES
  250-PIPELINING
  250-CHUNKING
  250 SMTPUTF8
C:STARTTLS
S:220 2.0.0 Ready to start TLS
C:EHLO ###############
S:250-mx.google.com at your service, ###############
  250-SIZE 157286400
  250-8BITMIME
  250-ENHANCEDSTATUSCODES
  250-PIPELINING
  250-CHUNKING
  250 SMTPUTF8
C:QUIT
S:221 2.0.0 closing connection ############### - gsmtp

ПОРТ-587

S:220 smtp.gmail.com ESMTP ############### - gsmtp
C:EHLO ###############
S:250-smtp.gmail.com at your service, ###############
  250-SIZE 35882577
  250-8BITMIME
  250-STARTTLS
  250-ENHANCEDSTATUSCODES
  250-PIPELINING
  250-CHUNKING
  250 SMTPUTF8
C:STARTTLS
S:220 2.0.0 Ready to start TLS
C:EHLO ###############
S:250-smtp.gmail.com at your service, ###############
  250-SIZE 35882577
  250-8BITMIME
  250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
  250-ENHANCEDSTATUSCODES
  250-PIPELINING
  250-CHUNKING
  250 SMTPUTF8
C:QUIT
S:221 2.0.0 closing connection ############### - gsmtp

ПОРТ-465

S:220 smtp.gmail.com ESMTP ############### - gsmtp
C:EHLO ###############
S:250-smtp.gmail.com at your service, ###############
  250-SIZE 35882577
  250-8BITMIME
  250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
  250-ENHANCEDSTATUSCODES
  250-PIPELINING
  250-CHUNKING
  250 SMTPUTF8
C:QUIT
S:221 2.0.0 closing connection ############### - gsmtp

1 ответ

Решение

Технически это не обязательно должно поддерживаться, но согласно RFC 6186 сек. 3.1 вы должны запросить записи SRV для _submission хозяин в _tcp субдомен, вот так:

$ nslookup -type=srv _submission._tcp.gmail.com
Server:  google-public-dns-a.google.com
Address:  8.8.8.8

Non-authoritative answer:
_submission._tcp.gmail.com      SRV service location:
          priority       = 5
          weight         = 0
          port           = 587
          svr hostname   = smtp.gmail.com

Некоторые вещи, которые вы должны иметь в виду:

  1. В некотором смысле это "удобная функция". Если у вас есть адрес электронной почты myname@acme.com, тогда acme.com может (или не может) выставлять эти записи RFC 6186 в _tcp.acme.com разрешить программе почтового клиента своих пользователей автоматически обнаруживать такие службы, как POP3, IMAP и т. д.
  2. Могут быть особые ограничения, налагаемые на подключение к домену MSA. Например, сервисы могут просто отказаться от соединений с компьютеров за пределами xyz.com внутренняя сеть, или требуется внутренне доверенный сертификат, и так далее. MSA предназначен для взаимодействия с почтовым клиентом, MUA, а не для обычной доставки почты.
Другие вопросы по тегам