gen-smtp / gen_smtp

The extensible Erlang SMTP client and server library.
Other
686 stars 266 forks source link

Transparently try both ipv6 and ipv4 in gen_smtp_client #213

Open seriyps opened 4 years ago

seriyps commented 4 years ago

Why?

Well, shortage of ipv4 addresses. More flexibility. But at the same time, not all mail receivers support ipv6, so, you can't just be ipv6-only

Problem

gen_smtp_client uses gen_tcp:connect/3 and this one always uses ipv4:

> f(), {ok, S} = gen_tcp:connect("google.com", 443, []), inet:peername(S).           
{ok,{{216,58,207,46},443}}

It's possible to tell gen_tcp which protocol to use by providing inet or inet6 options, but if you'll provide both, only 1st one will be used:

1> f(), {ok, S} = gen_tcp:connect("ipv6.google.com", 443, [inet6, inet]), inet:peername(S). 
{ok,{{10752,5200,16385,2057,0,0,0,8206},443}}
2> f(), {ok, S} = gen_tcp:connect("ipv6.google.com", 443, [inet, inet6]), inet:peername(S).
** exception error: no match of right hand side value {error,nxdomain}

Also, smtp_util:mxlookup explicitly resolves A dns if no MX is found and ignores AAAA ones.

https://github.com/gen-smtp/gen_smtp/blob/62f585bf8d630908c9f958c978140f1012be0343/src/smtp_util.erl#L52

Proposed solutions

So it seems like OTP does not provide an easy way to do this. What we can do instead is to: A: manually resolve MX - A/AAAA records and try them one-by-one. B: call gen_tcp:connect("example.com", 25, [inet6]) and if that fails gen_tcp:connect("example.com", 25, [inet]). Both are quite intrusive changes I would say (can increase latency), so, probably should be turned on by some option? Say {ip_versions, [inet:address_family()]} - this way one can define both priority (elements in a list are tried one-by-one) and can enable/disable ip families by not including to a list. Also, smth should be done with smtp_util:mxlookup. Either we always return all IPs from MX and A or we just return unresolved domain from mxlookup fallback.

seriyps commented 3 years ago

TIL: there is a recommended way of doing this: https://en.m.wikipedia.org/wiki/Happy_Eyeballs

UPD: even more concrete RFC specifically for dual-stack SMTP servers and clients: https://datatracker.ietf.org/doc/html/rfc3974