Forums

Socket error when connecting gmail smtp

[admin update 2013-10-28: see the wiki section on email/smtp]

when trying to execute this code gets error

smtpserver = smtplib.SMTP("smtp.gmail.com",587)

error is

[Errno 101] Network is unreachable /usr/local/lib/python2.7/socket.py in create_connection, line 571

Hello,

I think access to not HTTP/HTTPS ports is blocked for non-paying users; there were problems with people using PA to spam people and run DOS attacks.

Yes, rcs1000 is correct. Free accounts only have internet access via a http/https proxy. All other traffic is blocked.

A correction to what Hansel said -- free accounts should be able to access smtp.gmail.com specifically -- there's an exception in our config for that particular host (and a couple of others, github.com springs to mind).

However, it's possible that our config to open that up is using a different set of IP addresses to the ones that gmail is now supporting (I think they use round-robin DNS, so the IP address can change over time). Could you tell me what you see if you run the following:

import socket
socket.gethostbyname("smtp.gmail.com")

I just did it from this and a non-paid account. My results were:

paid: 173.194.76.109

non: 173.194.68.109

If you need all the addresses from an A record, getaddrinfo() is probably more useful:

>>> print "\n".join(repr(i) for i in socket.getaddrinfo("smtp.gmail.com", 587))
(2, 1, 6, '', ('173.194.68.108', 587))
(2, 2, 17, '', ('173.194.68.108', 587))
(2, 3, 0, '', ('173.194.68.108', 587))
(2, 1, 6, '', ('173.194.68.109', 587))
(2, 2, 17, '', ('173.194.68.109', 587))
(2, 3, 0, '', ('173.194.68.109', 587))
(10, 1, 6, '', ('2607:f8b0:400d:c01::6c', 587, 0, 0))
(10, 2, 17, '', ('2607:f8b0:400d:c01::6c', 587, 0, 0))
(10, 3, 0, '', ('2607:f8b0:400d:c01::6c', 587, 0, 0))

In general I would suggest always using this function instead of gethostbyname() as really we should all be writing dual-stack compliant software (i.e. supporting both IPv4 and IPv6) in this day and age.

I didn't bother decoding the family and protocol identifiers as I thought it was fairly obvious from the address format - de-duplicating the addresses is left as an exercise to the reader! (^_^)

EDIT: Now I'm in the office and have a few spare minutes (and aren't feeding a baby with the other hand...) I thought I'd add for anybody unfamiliar with getaddrinfo(), you can get the function to filter for you by supplying additional hints. For example, in Python you could call the following to get just a list of IPv4 endpoints suitable for TCP:

>>> print "\n".join(repr(i) for i in socket.getaddrinfo("smtp.gmail.com", 587, socket.AF_INET, socket.SOCK_STREAM))
(2, 1, 6, '', ('173.194.76.108', 587))
(2, 1, 6, '', ('173.194.76.109', 587))

And similarly for IPv6:

>>> print "\n".join(repr(i) for i in socket.getaddrinfo("smtp.gmail.com", 587, socket.AF_INET6, socket.SOCK_STREAM))
(10, 1, 6, '', ('2607:f8b0:400d:c01::6c', 587, 0, 0))

Typically, though, my preferred approach is to not provide any hints and iterate through all the results, picking out the ones that I need. This is generally because I want to support both IPv4 and IPv6 addresses and it's annoying to have to perform two DNS resolutions (even if many systems have a local caching resolver which will make the second query very fast).

The result tuple (on Python) consists of:

  • Address family (common values: AF_INET for IPv4, AF_INET6 for IPv6)
  • Socket type (common values: SOCK_STREAM for TCP, SOCK_DGRAM for UDP)
  • Protocol type (common values: IPPROTO_TCP for TCP, IPPROTO_UDP for UDP)
  • Canonical host name (only set if AI_CANONNAME was included in the flags parameter)
  • Address, probably what you want. In Python this is a tuple of (addr, port) where addr is a string and port is an integer.