我正在寻找一种通用的方式来获取邮件提交服务器的主机名,方法是仅使用带有c / c ++的电子邮件地址,以便我可以连接到端口587和/或465 smtp提交。
例如example@gmail.com-> smtp.gmail.com或gmail-smtp-msa.l.google.com
基本上,我代表客户发送一些特定的电子邮件。所以最后看起来他们已经发送了电子邮件。到目前为止,我仍在使用一个简单的域名来映射主机名映射,例如{gmail.com,smtp.gmail.com},并在需要时进行扩展。但是,我非常想避免使用此地图并自动执行此过程。
前一段时间,我发布了c++ sockets - smtp server hostname from email address,应该是相同的问题,也许还不够清楚。在回答中,建议我执行DNS查找并询问MX记录。一本书和一个实现之后,我注意到这些mx记录不适合邮件提交(无法连接到端口587,只有25个)。我检查了resolv.h中定义的所有其他类型,并检查了nslookup(无论如何可能仍在使用resolv.h),现在我真的认为这无法通过DNS查找来实现。如果有人想尝试,请检查nslookup -type=mx gmail.com
,您将看不到任何-msa ...内容。
我现在有点绝望,因为我真的很想完成它。但是我也不想花几个星期。我也很高兴得到一个答案,告诉我它在合理的时间内无法实现。
编辑:
下面以一个最小的示例为例,我通过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
请注意这些主机名中的“ in” 。我对 smtp.gmail.com 的cname进行了dns查询,它给了我 gmail-smtp-msa.l.google.com ,所以“ msa” < / em>,该端口对端口587/465开放。我真的认为这些mx记录如今仅适合25端口,不适用于邮件提交。
编辑#2:
关于Internet上的25、465、587端口有很多困惑。只是为了给我清理一下,这些端口在ssl / tls中没有区别。端口25例如当您要将电子邮件从example@yourDomain.com发送到example@gmail.com时。端口587例如当您要将电子邮件从example1@gmail.com发送到example2@gmail.com时。最后,端口465 [deprecated]与587相同,另外它已经以ssl / tls开头。如果我错了,请指正我,但我敢肯定我没有错!在我的实际应用程序中运行以下3条EHLO的日志。首先使用mx记录主机名和端口25,其次使用“ smtp.gmail.com”和端口587,最后一个使用“ smtp.gmail.com”和端口465。请注意,在587/465之后,具有AUTH命令选项的区别安全连接,并且25个没有AUTH选项。因此,当通过端口25发送电子邮件时,接收者会收到通知,指出发件人未经过验证,您通常很快就会被阻止(SPAM)。无论如何,我都需要此AUTH命令,应用程序是完全合法的,没有垃圾邮件等。这就是为什么我需要开放端口587的smtp服务器,并且我认为这些MX服务器不是正确的原因。
PORT-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
PORT-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
PORT-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
答案 0 :(得分:0)
从技术上讲,这并不是必须要支持的,但是对于每个RFC 6186 Sec. 3.1,您应该在_submission
子域中查询_tcp
主机的SRV记录,如下所示:
$ 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
一些事情要记住:
myname@acme.com
,那么acme.com可能会(也可能不会)在_tcp.acme.com
中公开这些RFC 6186记录,以允许其用户的邮件客户端程序自动发现服务,例如POP3 ,IMAP等。xyz.com
内部网络外部机器的连接,或者要求内部信任的证书,依此类推。 MSA用于与邮件客户端MUA交互,而不用于常规邮件传递。