diff options
author | Charles-François Natali <cf.natali@gmail.com> | 2013-09-13 17:53:08 (GMT) |
---|---|---|
committer | Charles-François Natali <cf.natali@gmail.com> | 2013-09-13 17:53:08 (GMT) |
commit | 0cc86850b69c23ab834ae2c97c5dc8b717420a38 (patch) | |
tree | f23470e923bbc8686452e4ece2ee32babcb76f85 | |
parent | 12f18289c0481ffc1731dfa606baa9f65baa6478 (diff) | |
download | cpython-0cc86850b69c23ab834ae2c97c5dc8b717420a38.zip cpython-0cc86850b69c23ab834ae2c97c5dc8b717420a38.tar.gz cpython-0cc86850b69c23ab834ae2c97c5dc8b717420a38.tar.bz2 |
Issue #16201: socket: Use inet_pton()/inet_addr() instead of ad-hoc parsing for
numeric IP addresses.
-rw-r--r-- | Lib/test/test_socket.py | 14 | ||||
-rw-r--r-- | Modules/socketmodule.c | 69 |
2 files changed, 66 insertions, 17 deletions
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 490f776..e995c99 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -757,6 +757,20 @@ class GeneralModuleTests(unittest.TestCase): if not fqhn in all_host_names: self.fail("Error testing host resolution mechanisms. (fqdn: %s, all: %s)" % (fqhn, repr(all_host_names))) + def test_host_resolution(self): + for addr in ['0.1.1.~1', '1+.1.1.1', '::1q', '::1::2', + '1:1:1:1:1:1:1:1:1']: + self.assertRaises(OSError, socket.gethostbyname, addr) + self.assertRaises(OSError, socket.gethostbyaddr, addr) + + for addr in [support.HOST, '10.0.0.1', '255.255.255.255']: + self.assertEqual(socket.gethostbyname(addr), addr) + + # we don't test support.HOSTv6 because there's a chance it doesn't have + # a matching name entry (e.g. 'ip6-localhost') + for host in [support.HOST]: + self.assertIn(host, socket.gethostbyaddr(host)[2]) + @unittest.skipUnless(hasattr(socket, 'sethostname'), "test needs socket.sethostname()") @unittest.skipUnless(hasattr(socket, 'gethostname'), "test needs socket.gethostname()") def test_sethostname(self): diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index b9bc932..02cf540 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -425,6 +425,10 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); #define INVALID_SOCKET (-1) #endif +#ifndef INADDR_NONE +#define INADDR_NONE (-1) +#endif + /* XXX There's a problem here: *static* functions are not supposed to have a Py prefix (or use CapitalizedWords). Later... */ @@ -787,8 +791,6 @@ setipaddr(char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int af) { struct addrinfo hints, *res; int error; - int d1, d2, d3, d4; - char ch; memset((void *) addr_ret, '\0', sizeof(*addr_ret)); if (name[0] == '\0') { @@ -837,7 +839,10 @@ setipaddr(char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int af) freeaddrinfo(res); return siz; } - if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) { + /* special-case broadcast - inet_addr() below can return INADDR_NONE for + * this */ + if (strcmp(name, "255.255.255.255") == 0 || + strcmp(name, "<broadcast>") == 0) { struct sockaddr_in *sin; if (af != AF_INET && af != AF_UNSPEC) { PyErr_SetString(PyExc_OSError, @@ -853,20 +858,53 @@ setipaddr(char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int af) sin->sin_addr.s_addr = INADDR_BROADCAST; return sizeof(sin->sin_addr); } - 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) { - struct sockaddr_in *sin; - sin = (struct sockaddr_in *)addr_ret; - sin->sin_addr.s_addr = htonl( - ((long) d1 << 24) | ((long) d2 << 16) | - ((long) d3 << 8) | ((long) d4 << 0)); - sin->sin_family = AF_INET; + + /* avoid a name resolution in case of numeric address */ +#ifdef HAVE_INET_PTON + /* check for an IPv4 address */ + if (af == AF_UNSPEC || af == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)addr_ret; + memset(sin, 0, sizeof(*sin)); + if (inet_pton(AF_INET, name, &sin->sin_addr) > 0) { + sin->sin_family = AF_INET; #ifdef HAVE_SOCKADDR_SA_LEN - sin->sin_len = sizeof(*sin); + sin->sin_len = sizeof(*sin); #endif - return 4; + return 4; + } } +#ifdef ENABLE_IPV6 + /* check for an IPv6 address - if the address contains a scope ID, we + * fallback to getaddrinfo(), which can handle translation from interface + * name to interface index */ + if ((af == AF_UNSPEC || af == AF_INET6) && !strchr(name, '%')) { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)addr_ret; + memset(sin, 0, sizeof(*sin)); + if (inet_pton(AF_INET6, name, &sin->sin6_addr) > 0) { + sin->sin6_family = AF_INET6; +#ifdef HAVE_SOCKADDR_SA_LEN + sin->sin6_len = sizeof(*sin); +#endif + return 16; + } + } +#endif /* ENABLE_IPV6 */ +#else /* HAVE_INET_PTON */ + /* check for an IPv4 address */ + if (af == AF_INET || af == AF_UNSPEC) { + struct sockaddr_in *sin = (struct sockaddr_in *)addr_ret; + memset(sin, 0, sizeof(*sin)); + if ((sin->sin_addr.s_addr = inet_addr(name)) != INADDR_NONE) { + sin->sin_family = AF_INET; +#ifdef HAVE_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +#endif + return 4; + } + } +#endif /* HAVE_INET_PTON */ + + /* perform a name resolution */ memset(&hints, 0, sizeof(hints)); hints.ai_family = af; Py_BEGIN_ALLOW_THREADS @@ -4896,9 +4934,6 @@ binary format used in low-level network functions."); static PyObject* socket_inet_aton(PyObject *self, PyObject *args) { -#ifndef INADDR_NONE -#define INADDR_NONE (-1) -#endif #ifdef HAVE_INET_ATON struct in_addr buf; #endif |