? Include/addrinfo.h ? Python/getaddrinfo.c ? Python/getnameinfo.c Index: acconfig.h =================================================================== RCS file: /cvsroot/python/python/dist/src/acconfig.h,v retrieving revision 1.47 diff -u -r1.47 acconfig.h --- acconfig.h 2001/06/23 16:30:13 1.47 +++ acconfig.h 2001/06/24 04:41:42 @@ -184,11 +184,23 @@ /* Define if you want to use BSD db. */ #undef WITH_LIBDB +/* Define if --enable-ipv6 is specified */ +#undef ENABLE_IPV6 + /* Define if you want to use ndbm. */ #undef WITH_LIBNDBM /* Define if you want to compile in Python-specific mallocs */ #undef WITH_PYMALLOC + +/* struct addrinfo (netdb.h) */ +#undef HAVE_ADDRINFO + +/* Define if sockaddr has sa_len member */ +#undef HAVE_SOCKADDR_SA_LEN + +/* struct sockaddr_storage (sys/socket.h) */ +#undef HAVE_SOCKADDR_STORAGE /* Define if you want to produce an OpenStep/Rhapsody framework (shared library plus accessory files). */ Index: configure =================================================================== RCS file: /cvsroot/python/python/dist/src/configure,v retrieving revision 1.212 diff -u -r1.212 configure --- configure 2001/06/23 16:30:12 1.212 +++ configure 2001/06/24 04:41:46 @@ -1,6 +1,6 @@ #! /bin/sh -# From configure.in Revision: 1.219 +# From configure.in Revision: 1.220 # Guess values for system-dependent variables and create Makefiles. # Generated automatically using autoconf version 2.13 Index: Lib/BaseHTTPServer.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/BaseHTTPServer.py,v retrieving revision 1.15 diff -u -r1.15 BaseHTTPServer.py --- Lib/BaseHTTPServer.py 2001/02/09 05:38:46 1.15 +++ Lib/BaseHTTPServer.py 2001/06/24 04:41:47 @@ -68,8 +68,10 @@ import sys import time import socket # For gethostbyaddr() +import string import mimetools import SocketServer +import re # Default error message DEFAULT_ERROR_MESSAGE = """\ @@ -474,7 +476,8 @@ httpd = ServerClass(server_address, HandlerClass) - print "Serving HTTP on port", port, "..." + sa = httpd.socket.getsockname() + print "Serving HTTP on", sa[0], "port", sa[1], "..." httpd.serve_forever() Index: Lib/SocketServer.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/SocketServer.py,v retrieving revision 1.24 diff -u -r1.24 SocketServer.py --- Lib/SocketServer.py 2001/04/11 04:02:05 1.24 +++ Lib/SocketServer.py 2001/06/24 04:41:47 @@ -5,7 +5,7 @@ For socket-based servers: - address family: - - AF_INET: IP (Internet Protocol) sockets (default) + - AF_INET{,6}: IP (Internet Protocol) sockets (default) - AF_UNIX: Unix domain sockets - others, e.g. AF_DECNET are conceivable (see - socket type: Index: Lib/ftplib.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/ftplib.py,v retrieving revision 1.53 diff -u -r1.53 ftplib.py --- Lib/ftplib.py 2001/04/09 04:31:50 1.53 +++ Lib/ftplib.py 2001/06/24 04:41:47 @@ -108,17 +108,29 @@ self.connect(host) if user: self.login(user, passwd, acct) - def connect(self, host='', port=0): - '''Connect to host. Arguments are: - - host: hostname to connect to (string, default previous host) - - port: port to connect to (integer, default previous port)''' - if host: self.host = host - if port: self.port = port - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((self.host, self.port)) - self.file = self.sock.makefile('rb') - self.welcome = self.getresp() - return self.welcome + def connect(self, host = '', port = 0): + '''Connect to host. Arguments are: + - host: hostname to connect to (string, default previous host) + - port: port to connect to (integer, default previous port)''' + if host: self.host = host + if port: self.port = port + self.passiveserver = 0 + for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + try: + self.sock = socket.socket(af, socktype, proto) + self.sock.connect(sa) + except socket.error, msg: + self.sock.close() + self.sock = None + continue + break + if not self.sock: + raise socket.error, msg + self.af = af + self.file = self.sock.makefile('rb') + self.welcome = self.getresp() + return self.welcome def getwelcome(self): '''Get the welcome message from the server. @@ -243,15 +255,48 @@ cmd = 'PORT ' + ','.join(bytes) return self.voidcmd(cmd) + def sendeprt(self, host, port): + '''Send a EPRT command with the current host and the given port number.''' + af = 0 + if self.af == socket.AF_INET: + af = 1 + if self.af == socket.AF_INET6: + af = 2 + if af == 0: + raise error_proto, 'unsupported address family' + fields = ['', `af`, host, `port`, ''] + cmd = 'EPRT ' + string.joinfields(fields, '|') + return self.voidcmd(cmd) + def makeport(self): - '''Create a new socket and send a PORT command for it.''' - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.bind(('', 0)) - sock.listen(1) - dummyhost, port = sock.getsockname() # Get proper port - host, dummyport = self.sock.getsockname() # Get proper host - resp = self.sendport(host, port) - return sock + '''Create a new socket and send a PORT command for it.''' + for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): + af, socktype, proto, canonname, sa = res + try: + sock = socket.socket(af, socktype, proto) + sock.bind(sa) + except socket.error, msg: + sock.close() + sock = None + continue + break + if not sock: + raise socket.error, msg + sock.listen(1) + port = sock.getsockname()[1] # Get proper port + host = self.sock.getsockname()[0] # Get proper host + if self.af == socket.AF_INET: + resp = self.sendport(host, port) + else: + resp = self.sendeprt(host, port) + return sock + + def makepasv(self): + if self.af == socket.AF_INET: + host, port = parse227(self.sendcmd('PASV')) + else: + host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername()) + return host, port def ntransfercmd(self, cmd, rest=None): """Initiate a transfer over the data connection. @@ -270,9 +315,10 @@ """ size = None if self.passiveserver: - host, port = parse227(self.sendcmd('PASV')) - conn=socket.socket(socket.AF_INET, socket.SOCK_STREAM) - conn.connect((host, port)) + host, port = self.makepasv() + af, socktype, proto, canon, sa = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0] + conn = socket.socket(af, socktype, proto) + conn.connect(sa) if rest is not None: self.sendcmd("REST %s" % rest) resp = self.sendcmd(cmd) @@ -520,6 +566,28 @@ raise error_proto, resp host = '.'.join(numbers[:4]) port = (int(numbers[4]) << 8) + int(numbers[5]) + return host, port + + +def parse229(resp, peer): + '''Parse the '229' response for a EPSV request. + Raises error_proto if it does not contain '(|||port|)' + Return ('host.addr.as.numbers', port#) tuple.''' + + if resp[:3] <> '229': + raise error_reply, resp + left = string.find(resp, '(') + if left < 0: raise error_proto, resp + right = string.find(resp, ')', left + 1) + if right < 0: + raise error_proto, resp # should contain '(|||port|)' + if resp[left + 1] <> resp[right - 1]: + raise error_proto, resp + parts = string.split(resp[left + 1:right], resp[left+1]) + if len(parts) <> 5: + raise error_proto, resp + host = peer[0] + port = string.atoi(parts[3]) return host, port Index: Lib/httplib.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/httplib.py,v retrieving revision 1.35 diff -u -r1.35 httplib.py --- Lib/httplib.py 2001/06/01 16:25:38 1.35 +++ Lib/httplib.py 2001/06/24 04:41:48 @@ -357,10 +357,22 @@ def connect(self): """Connect to the host and port specified in __init__.""" - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if self.debuglevel > 0: - print "connect: (%s, %s)" % (self.host, self.port) - self.sock.connect((self.host, self.port)) + for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + try: + self.sock = socket.socket(af, socktype, proto) + if self.debuglevel > 0: + print "connect: (%s, %s)" % (self.host, self.port) + self.sock.connect(sa) + except socket.error, msg: + if self.debuglevel > 0: + print 'connect fail:', (self.host, self.port) + self.sock.close() + self.sock = None + continue + break + if not self.sock: + raise socket.error, msg def close(self): """Close the connection to the HTTP server.""" Index: Lib/poplib.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/poplib.py,v retrieving revision 1.14 diff -u -r1.14 poplib.py --- Lib/poplib.py 2001/02/12 02:00:42 1.14 +++ Lib/poplib.py 2001/06/24 04:41:48 @@ -73,13 +73,23 @@ def __init__(self, host, port = POP3_PORT): - self.host = host - self.port = port - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((self.host, self.port)) - self.file = self.sock.makefile('rb') - self._debugging = 0 - self.welcome = self._getresp() + self.host = host + self.port = port + for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + try: + self.sock = socket.socket(af, socktype, proto) + self.sock.connect(sa) + except socket.error, msg: + self.sock.close() + self.sock = None + continue + break + if not self.sock: + raise socket.error, msg + self.file = self.sock.makefile('rb') + self._debugging = 0 + self.welcome = self._getresp() def _putline(self, line): Index: Lib/smtplib.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/smtplib.py,v retrieving revision 1.36 diff -u -r1.36 smtplib.py --- Lib/smtplib.py 2001/02/15 22:15:13 1.36 +++ Lib/smtplib.py 2001/06/24 04:41:48 @@ -208,24 +208,32 @@ specified during instantiation. """ - if not port: - i = host.find(':') + if not port and (host.find(':') == host.rfind(':')): + i = host.rfind(':') if i >= 0: host, port = host[:i], host[i+1:] try: port = int(port) except ValueError: raise socket.error, "nonnumeric port" if not port: port = SMTP_PORT - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if self.debuglevel > 0: print 'connect:', (host, port) - try: - self.sock.connect((host, port)) - except socket.error: - self.close() - raise - (code,msg)=self.getreply() - if self.debuglevel >0 : print "connect:", msg - return (code,msg) + if self.debuglevel > 0: print 'connect:', (host, port) + for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + try: + self.sock = socket.socket(af, socktype, proto) + if self.debuglevel > 0: print 'connect:', (host, port) + self.sock.connect(sa) + except socket.error, msg: + if self.debuglevel > 0: print 'connect fail:', (host, port) + self.sock.close() + self.sock = None + continue + break + if not self.sock: + raise socket.error, msg + (code, msg) = self.getreply() + if self.debuglevel > 0: print "connect:", msg + return (code, msg) def send(self, str): """Send `str' to the server.""" Index: Lib/telnetlib.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/telnetlib.py,v retrieving revision 1.11 diff -u -r1.11 telnetlib.py --- Lib/telnetlib.py 2001/03/01 04:27:19 1.11 +++ Lib/telnetlib.py 2001/06/24 04:41:48 @@ -136,8 +136,18 @@ port = TELNET_PORT self.host = host self.port = port - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((self.host, self.port)) + for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + try: + self.sock = socket.socket(af, socktype, proto) + self.sock.connect(sa) + except socket.error, msg: + self.sock.close() + self.sock = None + continue + break + if not self.sock: + raise socket.error, msg def __del__(self): """Destructor -- close the connection.""" Index: Modules/socketmodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/socketmodule.c,v retrieving revision 1.144 diff -u -r1.144 socketmodule.c --- Modules/socketmodule.c 2001/06/23 16:30:13 1.144 +++ Modules/socketmodule.c 2001/06/24 04:41:51 @@ -7,7 +7,7 @@ Limitations: -- only AF_INET and AF_UNIX address families are supported in a +- only AF_INET, AF_INET6 and AF_UNIX address families are supported in a portable manner, though AF_PACKET is supported under Linux. - no read/write operations (use send/recv or makefile instead) - additional restrictions apply on Windows @@ -16,6 +16,7 @@ - socket.error: exception raised for socket specific errors - socket.gethostbyname(hostname) --> host IP address (string: 'dd.dd.dd.dd') +- socket.gethostbyname2(hostname, af) --> host IP address (string) - socket.gethostbyaddr(IP address) --> (hostname, [alias, ...], [IP addr, ...]) - socket.gethostname() --> host name (string: 'spam' or 'spam.domain.com') - socket.getprotobyname(protocolname) --> protocol number @@ -25,6 +26,9 @@ - socket.ntohl(32 bit value) --> new int object - socket.htons(16 bit value) --> new int object - socket.htonl(32 bit value) --> new int object +- socket.getaddrinfo(host, port [, family, socktype, proto, flags]) + --> List of (family, socktype, proto, canonname, sockaddr) +- socket.getnameinfo(sockaddr, flags) --> (host, port) - socket.AF_INET, socket.SOCK_STREAM, etc.: constants from - socket.inet_aton(IP address) -> 32-bit packed IP representation - socket.inet_ntoa(packed IP) -> IP address string @@ -178,6 +182,10 @@ #include #endif +#ifndef offsetof +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) +#endif + #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK /* For QNX only? */ #endif @@ -395,6 +403,10 @@ #ifdef AF_UNIX struct sockaddr_un un; #endif +#ifdef INET6 + struct sockaddr_in6 in6; + struct sockaddr_storage storage; +#endif #if defined(linux) && defined(AF_PACKET) struct sockaddr_ll ll; #endif @@ -471,85 +483,87 @@ /* Convert a string specifying a host name or one of a few symbolic names to a numeric IP address. This usually calls gethostbyname() to do the work; the names "" and "" are special. - Return the length (should always be 4 bytes), or negative if + Return the length (IPv4 should be 4 bytes), or negative if an error occurred; then an exception is raised. */ static int -setipaddr(char* name, struct sockaddr_in * addr_ret) +setipaddr(char* name, struct sockaddr * addr_ret, int af) { - struct hostent *hp; - int d1, d2, d3, d4; - int h_length; - char ch; -#ifdef HAVE_GETHOSTBYNAME_R - struct hostent hp_allocated; -#ifdef HAVE_GETHOSTBYNAME_R_3_ARG - struct hostent_data data; -#else - char buf[1001]; - int buf_len = (sizeof buf) - 1; - int errnop; -#endif -#if defined(HAVE_GETHOSTBYNAME_R_3_ARG) || defined(HAVE_GETHOSTBYNAME_R_6_ARG) - int result; -#endif -#endif /* HAVE_GETHOSTBYNAME_R */ + struct addrinfo hints, *res; + int error; memset((void *) addr_ret, '\0', sizeof(*addr_ret)); if (name[0] == '\0') { - addr_ret->sin_addr.s_addr = INADDR_ANY; - return 4; + int siz; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(NULL, "0", &hints, &res); + if (error) { + PyErr_SetString(PySocket_Error, gai_strerror(error)); + return -1; + } + switch (res->ai_family) { + case AF_INET: + siz = 4; + break; +#ifdef INET6 + case AF_INET6: + siz = 16; + break; +#endif + default: + freeaddrinfo(res); + PyErr_SetString(PySocket_Error, + "unsupported address family"); + return -1; + } + if (res->ai_next) { + PyErr_SetString(PySocket_Error, + "wildcard resolved to multiple address"); + return -1; + } + memcpy(addr_ret, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + return siz; } if (name[0] == '<' && strcmp(name, "") == 0) { - addr_ret->sin_addr.s_addr = INADDR_BROADCAST; - return 4; + struct sockaddr_in *sin; + if (af != PF_INET && af != PF_UNSPEC) { + PyErr_SetString(PySocket_Error, + "address family mismatched"); + return -1; + } + sin = (struct sockaddr_in *)addr_ret; + memset((void *) sin, '\0', sizeof(*sin)); + sin->sin_family = AF_INET; +#ifdef HAVE_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +#endif + sin->sin_addr.s_addr = INADDR_BROADCAST; + return sizeof(sin->sin_addr); + } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + error = getaddrinfo(name, NULL, &hints, &res); + if (error) { + PyErr_SetString(PySocket_Error, gai_strerror(error)); + return -1; } - if (sscanf(name, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 && - 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 && - 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) { - addr_ret->sin_addr.s_addr = htonl( - ((long) d1 << 24) | ((long) d2 << 16) | - ((long) d3 << 8) | ((long) d4 << 0)); + memcpy((char *) addr_ret, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + switch (addr_ret->sa_family) { + case AF_INET: return 4; - } - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_GETHOSTBYNAME_R -#if defined(HAVE_GETHOSTBYNAME_R_6_ARG) - result = gethostbyname_r(name, &hp_allocated, buf, buf_len, &hp, &errnop); -#elif defined(HAVE_GETHOSTBYNAME_R_5_ARG) - hp = gethostbyname_r(name, &hp_allocated, buf, buf_len, &errnop); -#else /* HAVE_GETHOSTBYNAME_R_3_ARG */ - memset((void *) &data, '\0', sizeof(data)); - result = gethostbyname_r(name, &hp_allocated, &data); - hp = (result != 0) ? NULL : &hp_allocated; -#endif -#else /* not HAVE_GETHOSTBYNAME_R */ -#ifdef USE_GETHOSTBYNAME_LOCK - PyThread_acquire_lock(gethostbyname_lock, 1); -#endif - hp = gethostbyname(name); -#endif /* HAVE_GETHOSTBYNAME_R */ - Py_END_ALLOW_THREADS - - if (hp == NULL) { -#ifdef HAVE_HSTRERROR - /* Let's get real error message to return */ - extern int h_errno; - PyErr_SetString(PySocket_Error, (char *)hstrerror(h_errno)); -#else - PyErr_SetString(PySocket_Error, "host not found"); +#ifdef INET6 + case AF_INET6: + return 16; #endif -#ifdef USE_GETHOSTBYNAME_LOCK - PyThread_release_lock(gethostbyname_lock); -#endif + default: + PyErr_SetString(PySocket_Error, "unknown address family"); return -1; } - memcpy((char *) &addr_ret->sin_addr, hp->h_addr, hp->h_length); - h_length = hp->h_length; -#ifdef USE_GETHOSTBYNAME_LOCK - PyThread_release_lock(gethostbyname_lock); -#endif - return h_length; } @@ -558,13 +572,17 @@ size numbers). */ static PyObject * -makeipaddr(struct sockaddr_in *addr) +makeipaddr(struct sockaddr *addr, int addrlen) { - long x = ntohl(addr->sin_addr.s_addr); - char buf[100]; - sprintf(buf, "%d.%d.%d.%d", - (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, - (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); + char buf[NI_MAXHOST]; + int error; + + error = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, + NI_NUMERICHOST); + if (error) { + PyErr_SetString(PySocket_Error, gai_strerror(error)); + return NULL; + } return PyString_FromString(buf); } @@ -593,10 +611,11 @@ case AF_INET: { - struct sockaddr_in *a = (struct sockaddr_in *) addr; - PyObject *addrobj = makeipaddr(a); + struct sockaddr_in *a; + PyObject *addrobj = makeipaddr(addr, sizeof(*a)); PyObject *ret = NULL; if (addrobj) { + a = (struct sockaddr_in *)addr; ret = Py_BuildValue("Oi", addrobj, ntohs(a->sin_port)); Py_DECREF(addrobj); } @@ -609,6 +628,22 @@ struct sockaddr_un *a = (struct sockaddr_un *) addr; return PyString_FromString(a->sun_path); } +#endif /* AF_UNIX */ + +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 *a; + PyObject *addrobj = makeipaddr(addr, sizeof(*a)); + PyObject *ret = NULL; + if (addrobj) { + a = (struct sockaddr_in6 *)addr; + ret = Py_BuildValue("Oiii", addrobj, ntohs(a->sin6_port), + a->sin6_flowinfo, a->sin6_scope_id); + Py_DECREF(addrobj); + } + return ret; + } #endif #if defined(linux) && defined(AF_PACKET) @@ -668,7 +703,7 @@ "AF_UNIX path too long"); return 0; } - addr->sun_family = AF_UNIX; + addr->sun_family = s->sock_family; memcpy(addr->sun_path, path, len); addr->sun_path[len] = 0; *addr_ret = (struct sockaddr *) addr; @@ -691,7 +726,7 @@ } if (!PyArg_ParseTuple(args, "si:getsockaddrarg", &host, &port)) return 0; - if (setipaddr(host, addr) < 0) + if (setipaddr(host, (struct sockaddr *)addr, AF_INET) < 0) return 0; addr->sin_family = AF_INET; addr->sin_port = htons((short)port); @@ -700,6 +735,30 @@ return 1; } +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6* addr; + char *host; + int port, flowinfo, scope_id; + addr = (struct sockaddr_in6*)&(s->sock_addr).in6; + flowinfo = scope_id = 0; + if (!PyArg_ParseTuple(args, "si|ii", &host, &port, &flowinfo, + &scope_id)) { + return 0; + } + if (setipaddr(host, (struct sockaddr *)addr, AF_INET6) < 0) + return 0; + addr->sin6_family = s->sock_family; + addr->sin6_port = htons((short)port); + addr->sin6_flowinfo = flowinfo; + addr->sin6_scope_id = scope_id; + *addr_ret = (struct sockaddr *) addr; + *len_ret = sizeof *addr; + return 1; + } +#endif + #if defined(linux) && defined(AF_PACKET) case AF_PACKET: { @@ -732,7 +791,6 @@ } #endif - /* More cases here... */ default: @@ -766,6 +824,14 @@ return 1; } +#ifdef INET6 + case AF_INET6: + { + *len_ret = sizeof (struct sockaddr_in6); + return 1; + } +#endif + #if defined(linux) && defined(AF_PACKET) case AF_PACKET: { @@ -1632,6 +1698,52 @@ Return the current host name."; +/* Python interface to gethostbyname2(name, af). */ + +/*ARGSUSED*/ +static PyObject * +PySocket_gethostbyname2_common(char *name, int af) +{ + struct sockaddr_storage addrbuf; + + if (setipaddr(name, (struct sockaddr *)&addrbuf, af) < 0) + return NULL; + switch (addrbuf.ss_family) { + case AF_INET: + return makeipaddr((struct sockaddr *)&addrbuf, + sizeof(struct sockaddr_in)); +#ifdef INET6 + case AF_INET6: + return makeipaddr((struct sockaddr *)&addrbuf, + sizeof(struct sockaddr_in6)); +#endif + case AF_UNIX: + return makeipaddr((struct sockaddr *)&addrbuf, + sizeof(struct sockaddr_un)); + default: + return makeipaddr((struct sockaddr *)&addrbuf, + sizeof(struct sockaddr)); + } +} + +/*ARGSUSED*/ +static PyObject * +PySocket_gethostbyname2(PyObject *self, PyObject *args) +{ + char *name; + int af; + + if (!PyArg_ParseTuple(args, "si:gethostbyname2", &name, &af)) + return NULL; + return PySocket_gethostbyname2_common(name, af); +} + +static char gethostbyname2_doc[] = +"gethostbyname2(host, af) -> address\n\ +\n\ +Return the IP address (a string) for a host."; + + /* Python interface to gethostbyname(name). */ /*ARGSUSED*/ @@ -1639,12 +1751,10 @@ PySocket_gethostbyname(PyObject *self, PyObject *args) { char *name; - struct sockaddr_in addrbuf; + if (!PyArg_ParseTuple(args, "s:gethostbyname", &name)) - return NULL; - if (setipaddr(name, &addrbuf) < 0) return NULL; - return makeipaddr(&addrbuf); + return PySocket_gethostbyname2_common(name, AF_INET); } static char gethostbyname_doc[] = @@ -1656,13 +1766,14 @@ /* Convenience function common to gethostbyname_ex and gethostbyaddr */ static PyObject * -gethost_common(struct hostent *h, struct sockaddr_in *addr) +gethost_common(struct hostent *h, struct sockaddr *addr, int alen, int af) { char **pch; PyObject *rtn_tuple = (PyObject *)NULL; PyObject *name_list = (PyObject *)NULL; PyObject *addr_list = (PyObject *)NULL; PyObject *tmp; + if (h == NULL) { #ifdef HAVE_HSTRERROR /* Let's get real error message to return */ @@ -1673,6 +1784,28 @@ #endif return NULL; } + if (h->h_addrtype != af) { +#ifdef HAVE_STRERROR + /* Let's get real error message to return */ + PyErr_SetString(PySocket_Error, (char *)strerror(EAFNOSUPPORT)); +#else + PyErr_SetString(PySocket_Error, + "Address family not supported by protocol family"); +#endif + return NULL; + } + switch (af) { + case AF_INET: + if (alen < sizeof(struct sockaddr_in)) + return NULL; + break; +#ifdef INET6 + case AF_INET6: + if (alen < sizeof(struct sockaddr_in6)) + return NULL; + break; +#endif + } if ((name_list = PyList_New(0)) == NULL) goto err; if ((addr_list = PyList_New(0)) == NULL) @@ -1689,8 +1822,43 @@ } for (pch = h->h_addr_list; *pch != NULL; pch++) { int status; - memcpy((char *) &addr->sin_addr, *pch, h->h_length); - tmp = makeipaddr(addr); + switch (af) { + case AF_INET: + { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = af; +#ifdef HAVE_SOCKADDR_SA_LEN + sin.sin_len = sizeof(sin); +#endif + memcpy(&sin.sin_addr, *pch, sizeof(sin.sin_addr)); + tmp = makeipaddr((struct sockaddr *)&sin, sizeof(sin)); + if (pch == h->h_addr_list && alen >= sizeof(sin)) + memcpy((char *) addr, &sin, sizeof(sin)); + break; + } +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = af; +#ifdef HAVE_SOCKADDR_SA_LEN + sin6.sin6_len = sizeof(sin6); +#endif + memcpy(&sin6.sin6_addr, *pch, sizeof(sin6.sin6_addr)); + tmp = makeipaddr((struct sockaddr *)&sin6, + sizeof(sin6)); + if (pch == h->h_addr_list && alen >= sizeof(sin6)) + memcpy((char *) addr, &sin6, sizeof(sin6)); + break; + } +#endif + default: /* can't happen */ + PyErr_SetString(PySocket_Error, + "unsupported address family"); + return NULL; + } if (tmp == NULL) goto err; status = PyList_Append(addr_list, tmp); @@ -1714,7 +1882,7 @@ { char *name; struct hostent *h; - struct sockaddr_in addr; + struct sockaddr_storage addr; PyObject *ret; #ifdef HAVE_GETHOSTBYNAME_R struct hostent hp_allocated; @@ -1729,9 +1897,15 @@ int result; #endif #endif /* HAVE_GETHOSTBYNAME_R */ +#ifdef INET6 + int af = PF_UNSPEC; +#else + int af = PF_INET; +#endif + if (!PyArg_ParseTuple(args, "s:gethostbyname_ex", &name)) return NULL; - if (setipaddr(name, &addr) < 0) + if (setipaddr(name, (struct sockaddr *)&addr, af) < 0) return NULL; Py_BEGIN_ALLOW_THREADS #ifdef HAVE_GETHOSTBYNAME_R @@ -1751,7 +1925,7 @@ h = gethostbyname(name); #endif /* HAVE_GETHOSTBYNAME_R */ Py_END_ALLOW_THREADS - ret = gethost_common(h, &addr); + ret = gethost_common(h, (struct sockaddr *)&addr, sizeof(addr), addr.ss_family); #ifdef USE_GETHOSTBYNAME_LOCK PyThread_release_lock(gethostbyname_lock); #endif @@ -1771,7 +1945,12 @@ static PyObject * PySocket_gethostbyaddr(PyObject *self, PyObject *args) { +#ifdef INET6 + struct sockaddr_storage addr; +#else struct sockaddr_in addr; +#endif + struct sockaddr *sa = (struct sockaddr *)&addr; char *ip_num; struct hostent *h; PyObject *ret; @@ -1788,40 +1967,55 @@ int result; #endif #endif /* HAVE_GETHOSTBYNAME_R */ + char *ap; + int al; + int af; if (!PyArg_ParseTuple(args, "s:gethostbyaddr", &ip_num)) + return NULL; + af = PF_UNSPEC; + if (setipaddr(ip_num, sa, af) < 0) return NULL; - if (setipaddr(ip_num, &addr) < 0) + af = sa->sa_family; + ap = NULL; + al = 0; + switch (af) { + case AF_INET: + ap = (char *)&((struct sockaddr_in *)sa)->sin_addr; + al = sizeof(((struct sockaddr_in *)sa)->sin_addr); + break; +#ifdef INET6 + case AF_INET6: + ap = (char *)&((struct sockaddr_in6 *)sa)->sin6_addr; + al = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); + break; +#endif + default: + PyErr_SetString(PySocket_Error, "unsupported address family"); return NULL; + } Py_BEGIN_ALLOW_THREADS #ifdef HAVE_GETHOSTBYNAME_R #if defined(HAVE_GETHOSTBYNAME_R_6_ARG) - result = gethostbyaddr_r((char *)&addr.sin_addr, - sizeof(addr.sin_addr), - AF_INET, &hp_allocated, buf, buf_len, + result = gethostbyaddr_r(ap, al, af, + &hp_allocated, buf, buf_len, &h, &errnop); #elif defined(HAVE_GETHOSTBYNAME_R_5_ARG) - h = gethostbyaddr_r((char *)&addr.sin_addr, - sizeof(addr.sin_addr), - AF_INET, + h = gethostbyaddr_r(ap, al, af, &hp_allocated, buf, buf_len, &errnop); #else /* HAVE_GETHOSTBYNAME_R_3_ARG */ memset((void *) &data, '\0', sizeof(data)); - result = gethostbyaddr_r((char *)&addr.sin_addr, - sizeof(addr.sin_addr), - AF_INET, &hp_allocated, &data); + result = gethostbyaddr_r(ap, al, af, &hp_allocated, &data); h = (result != 0) ? NULL : &hp_allocated; #endif #else /* not HAVE_GETHOSTBYNAME_R */ #ifdef USE_GETHOSTBYNAME_LOCK PyThread_acquire_lock(gethostbyname_lock, 1); #endif - h = gethostbyaddr((char *)&addr.sin_addr, - sizeof(addr.sin_addr), - AF_INET); + h = gethostbyaddr(ap, al, af); #endif /* HAVE_GETHOSTBYNAME_R */ Py_END_ALLOW_THREADS - ret = gethost_common(h, &addr); + ret = gethost_common(h, (struct sockaddr *)&addr, sizeof(addr), af); #ifdef USE_GETHOSTBYNAME_LOCK PyThread_release_lock(gethostbyname_lock); #endif @@ -2121,6 +2315,156 @@ return PyString_FromString(inet_ntoa(packed_addr)); } +/* Python interface to getaddrinfo(host, port). */ + +/*ARGSUSED*/ +static PyObject * +PySocket_getaddrinfo(PyObject *self, PyObject *args) +{ + struct addrinfo hints, *res0, *res; + PyObject *pobj = (PyObject *)NULL; + char pbuf[10]; + char *hptr, *pptr; + int family, socktype, protocol, flags; + int error; + PyObject *all = (PyObject *)NULL; + PyObject *single = (PyObject *)NULL; + + family = socktype = protocol = flags = 0; + family = PF_UNSPEC; + if (!PyArg_ParseTuple(args, "zO|iiii:getaddrinfo", + &hptr, &pobj, &family, &socktype, + &protocol, &flags)) { + return NULL; + } + if (PyInt_Check(pobj)) { + snprintf(pbuf, sizeof(pbuf), "%ld", PyInt_AsLong(pobj)); + pptr = pbuf; + } else if (PyString_Check(pobj)) { + pptr = PyString_AsString(pobj); + } else if (pobj == Py_None) { + pptr = (char *)NULL; + } else { + PyErr_SetString(PySocket_Error, "Int or String expected"); + return NULL; + } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = socktype; + hints.ai_protocol = protocol; + hints.ai_flags = flags; + error = getaddrinfo(hptr, pptr, &hints, &res0); + if (error) { + PyErr_SetString(PySocket_Error, gai_strerror(error)); + return NULL; + } + + if ((all = PyList_New(0)) == NULL) + goto err; + for (res = res0; res; res = res->ai_next) { + single = Py_BuildValue("iiisO", res->ai_family, + res->ai_socktype, res->ai_protocol, + res->ai_canonname ? res->ai_canonname : "", + makesockaddr(-1, res->ai_addr, res->ai_addrlen)); + if (single == NULL) + goto err; + + if (PyList_Append(all, single)) + goto err; + Py_XDECREF(single); + } + Py_XDECREF(pobj); + return all; + err: + Py_XDECREF(single); + Py_XDECREF(all); + Py_XDECREF(pobj); + return (PyObject *)NULL; +} + +static char getaddrinfo_doc[] = +"socket.getaddrinfo(host, port [, family, socktype, proto, flags])\n\ + --> List of (family, socktype, proto, canonname, sockaddr)\n\ +\n\ +Resolve host and port into addrinfo struct."; + +/* Python interface to getnameinfo(sa, flags). */ + +/*ARGSUSED*/ +static PyObject * +PySocket_getnameinfo(PyObject *self, PyObject *args) +{ + PyObject *sa = (PyObject *)NULL; + int flags; + char *hostp; + int n, port, flowinfo, scope_id; + char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; + struct addrinfo hints, *res = NULL; + int error; + PyObject *ret = (PyObject *)NULL; + + flags = flowinfo = scope_id = 0; + if (PyArg_ParseTuple(args, "Oi:getnameinfo", &sa, &flags) == 0) + return NULL; + n = PyArg_ParseTuple(sa, "si|ii", &hostp, &port, &flowinfo, scope_id); + if (n == 0) + goto fail; + snprintf(pbuf, sizeof(pbuf), "%d", port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + error = getaddrinfo(hostp, pbuf, &hints, &res); + if (error) { + PyErr_SetString(PySocket_Error, gai_strerror(error)); + goto fail; + } + if (res->ai_next) { + PyErr_SetString(PySocket_Error, + "sockaddr resolved to multiple addresses"); + goto fail; + } + switch (res->ai_family) { + case AF_INET: + { + char *t1; + int t2; + if (PyArg_ParseTuple(sa, "si", &t1, &t2) == 0) { + PyErr_SetString(PySocket_Error, + "IPv4 sockaddr must be 2 tuple"); + goto fail; + } + break; + } +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)res->ai_addr; + sin6->sin6_flowinfo = flowinfo; + sin6->sin6_scope_id = scope_id; + break; + } +#endif + } + error = getnameinfo(res->ai_addr, res->ai_addrlen, + hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), flags); + if (error) { + PyErr_SetString(PySocket_Error, gai_strerror(error)); + goto fail; + } + ret = Py_BuildValue("ss", hbuf, pbuf); + +fail: + if (res) + freeaddrinfo(res); + Py_XDECREF(sa); + return ret; +} + +static char getnameinfo_doc[] = +"socket.getnameinfo(sockaddr, flags) --> (host, port)\n\ +\n\ +Get host and port for a sockaddr."; + #ifdef USE_SSL @@ -2348,7 +2692,9 @@ static PyMethodDef PySocket_methods[] = { {"gethostbyname", PySocket_gethostbyname, METH_VARARGS, gethostbyname_doc}, - {"gethostbyname_ex", PySocket_gethostbyname_ex, + {"gethostbyname2", PySocket_gethostbyname2, + METH_VARARGS, gethostbyname2_doc}, + {"gethostbyname_ex", PySocket_gethostbyname_ex, METH_VARARGS, ghbn_ex_doc}, {"gethostbyaddr", PySocket_gethostbyaddr, METH_VARARGS, gethostbyaddr_doc}, @@ -2376,6 +2722,10 @@ METH_VARARGS, inet_aton_doc}, {"inet_ntoa", PySocket_inet_ntoa, METH_VARARGS, inet_ntoa_doc}, + {"getaddrinfo", PySocket_getaddrinfo, + METH_VARARGS, getaddrinfo_doc}, + {"getnameinfo", PySocket_getnameinfo, + METH_VARARGS, getnameinfo_doc}, #ifdef USE_SSL {"ssl", PySocket_ssl, METH_VARARGS, ssl_doc}, @@ -2571,6 +2921,9 @@ insint(d, "AF_UNSPEC", AF_UNSPEC); #endif insint(d, "AF_INET", AF_INET); +#ifdef AF_INET6 + insint(d, "AF_INET6", AF_INET6); +#endif /* AF_INET6 */ #ifdef AF_UNIX insint(d, "AF_UNIX", AF_UNIX); #endif /* AF_UNIX */ @@ -2924,6 +3277,98 @@ /* IPX options */ #ifdef IPX_TYPE insint(d, "IPX_TYPE", IPX_TYPE); +#endif + + /* get{addr,name}info parameters */ +#ifdef EAI_ADDRFAMILY + insint(d, "EAI_ADDRFAMILY", EAI_ADDRFAMILY); +#endif +#ifdef EAI_AGAIN + insint(d, "EAI_AGAIN", EAI_AGAIN); +#endif +#ifdef EAI_BADFLAGS + insint(d, "EAI_BADFLAGS", EAI_BADFLAGS); +#endif +#ifdef EAI_FAIL + insint(d, "EAI_FAIL", EAI_FAIL); +#endif +#ifdef EAI_FAMILY + insint(d, "EAI_FAMILY", EAI_FAMILY); +#endif +#ifdef EAI_MEMORY + insint(d, "EAI_MEMORY", EAI_MEMORY); +#endif +#ifdef EAI_NODATA + insint(d, "EAI_NODATA", EAI_NODATA); +#endif +#ifdef EAI_NONAME + insint(d, "EAI_NONAME", EAI_NONAME); +#endif +#ifdef EAI_SERVICE + insint(d, "EAI_SERVICE", EAI_SERVICE); +#endif +#ifdef EAI_SOCKTYPE + insint(d, "EAI_SOCKTYPE", EAI_SOCKTYPE); +#endif +#ifdef EAI_SYSTEM + insint(d, "EAI_SYSTEM", EAI_SYSTEM); +#endif +#ifdef EAI_BADHINTS + insint(d, "EAI_BADHINTS", EAI_BADHINTS); +#endif +#ifdef EAI_PROTOCOL + insint(d, "EAI_PROTOCOL", EAI_PROTOCOL); +#endif +#ifdef EAI_MAX + insint(d, "EAI_MAX", EAI_MAX); +#endif +#ifdef AI_PASSIVE + insint(d, "AI_PASSIVE", AI_PASSIVE); +#endif +#ifdef AI_CANONNAME + insint(d, "AI_CANONNAME", AI_CANONNAME); +#endif +#ifdef AI_NUMERICHOST + insint(d, "AI_NUMERICHOST", AI_NUMERICHOST); +#endif +#ifdef AI_MASK + insint(d, "AI_MASK", AI_MASK); +#endif +#ifdef AI_ALL + insint(d, "AI_ALL", AI_ALL); +#endif +#ifdef AI_V4MAPPED_CFG + insint(d, "AI_V4MAPPED_CFG", AI_V4MAPPED_CFG); +#endif +#ifdef AI_ADDRCONFIG + insint(d, "AI_ADDRCONFIG", AI_ADDRCONFIG); +#endif +#ifdef AI_V4MAPPED + insint(d, "AI_V4MAPPED", AI_V4MAPPED); +#endif +#ifdef AI_DEFAULT + insint(d, "AI_DEFAULT", AI_DEFAULT); +#endif +#ifdef NI_MAXHOST + insint(d, "NI_MAXHOST", NI_MAXHOST); +#endif +#ifdef NI_MAXSERV + insint(d, "NI_MAXSERV", NI_MAXSERV); +#endif +#ifdef NI_NOFQDN + insint(d, "NI_NOFQDN", NI_NOFQDN); +#endif +#ifdef NI_NUMERICHOST + insint(d, "NI_NUMERICHOST", NI_NUMERICHOST); +#endif +#ifdef NI_NAMEREQD + insint(d, "NI_NAMEREQD", NI_NAMEREQD); +#endif +#ifdef NI_NUMERICSERV + insint(d, "NI_NUMERICSERV", NI_NUMERICSERV); +#endif +#ifdef NI_DGRAM + insint(d, "NI_DGRAM", NI_DGRAM); #endif /* Initialize gethostbyname lock */