From a43c2f845e59b833b17e45994cd81c631c071236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 24 Jul 2001 20:34:08 +0000 Subject: Patch #401196: Use getaddrinfo and AF_INET6 in TCP servers and clients. --- Lib/BaseHTTPServer.py | 5 ++- Lib/SocketServer.py | 2 +- Lib/ftplib.py | 112 ++++++++++++++++++++++++++++++++++++++++---------- Lib/httplib.py | 20 +++++++-- Lib/poplib.py | 24 +++++++---- Lib/smtplib.py | 32 +++++++++------ Lib/telnetlib.py | 14 ++++++- 7 files changed, 160 insertions(+), 49 deletions(-) diff --git a/Lib/BaseHTTPServer.py b/Lib/BaseHTTPServer.py index 3e78d8b..ecb40d0 100644 --- a/Lib/BaseHTTPServer.py +++ b/Lib/BaseHTTPServer.py @@ -68,8 +68,10 @@ __all__ = ["HTTPServer", "BaseHTTPRequestHandler"] 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 @@ def test(HandlerClass = BaseHTTPRequestHandler, 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() diff --git a/Lib/SocketServer.py b/Lib/SocketServer.py index e52dddc..6e1f78a 100644 --- a/Lib/SocketServer.py +++ b/Lib/SocketServer.py @@ -5,7 +5,7 @@ This module tries to capture the various aspects of defining a server: 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: diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 3263281..693d5a9 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -108,17 +108,29 @@ class FTP: 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 @@ class FTP: 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 @@ class FTP: """ 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) @@ -523,6 +569,28 @@ def parse227(resp): 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 + + def parse257(resp): '''Parse the '257' response for a MKD or PWD request. This is a response to a MKD or PWD request: a directory name. diff --git a/Lib/httplib.py b/Lib/httplib.py index 1e08539..d566b35 100644 --- a/Lib/httplib.py +++ b/Lib/httplib.py @@ -357,10 +357,22 @@ class HTTPConnection: 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.""" diff --git a/Lib/poplib.py b/Lib/poplib.py index fb24a0f..c6c432d 100644 --- a/Lib/poplib.py +++ b/Lib/poplib.py @@ -73,13 +73,23 @@ class POP3: 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): diff --git a/Lib/smtplib.py b/Lib/smtplib.py index f1e4a27..fde6c69 100755 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -208,24 +208,32 @@ class SMTP: 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.""" diff --git a/Lib/telnetlib.py b/Lib/telnetlib.py index 6b249e8..77ed39e 100644 --- a/Lib/telnetlib.py +++ b/Lib/telnetlib.py @@ -136,8 +136,18 @@ class Telnet: 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.""" -- cgit v0.12