def test_handle_accept_typeerror(self): # https://github.com/giampaolo/pyftpdlib/issues/91 ac = Acceptor() with mock.patch.object(ac, "accept", side_effect=TypeError) as m: ac.handle_accept() assert m.called self.assertIsNone(ac.socket)
def __init__(self, address, handler, ioloop=None): """Initiate the FTP server opening listening on address. - (tuple) address: the host:port pair on which the command channel will listen. - (classobj) handler: the handler class to use. """ Acceptor.__init__(self, ioloop=ioloop) self.handler = handler self.ip_map = [] host, port = address # in case of FTPS class not properly configured we want errors # to be raised here rather than later, when client connects if hasattr(handler, 'get_ssl_context'): handler.get_ssl_context() # AF_INET or AF_INET6 socket # Get the correct address family for our host (allows IPv6 addresses) try: self._af = self.bind_af_unspecified((host, port)) except socket.gaierror: # Probably a DNS issue. Assume IPv4. self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((host, port)) self._af = socket.AF_INET self.listen(5)
def __init__(self, address, handler, ioloop=None): """Initiate the FTP server opening listening on address. - (tuple) address: the host:port pair on which the command channel will listen. - (classobj) handler: the handler class to use. """ Acceptor.__init__(self, ioloop=ioloop) self.handler = handler self.ip_map = [] host, port = address # in case of FTPS class not properly configured we want errors # to be raised here rather than later, when client connects if hasattr(handler, "get_ssl_context"): handler.get_ssl_context() # AF_INET or AF_INET6 socket # Get the correct address family for our host (allows IPv6 addresses) try: self._af = self.bind_af_unspecified((host, port)) except socket.gaierror: # Probably a DNS issue. Assume IPv4. self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((host, port)) self._af = socket.AF_INET self.listen(5)
def test_handle_accept_econnacorted(self): # https://github.com/giampaolo/pyftpdlib/issues/105 ac = Acceptor() with mock.patch.object( ac, "accept", side_effect=socket.error(errno.ECONNABORTED, "")) as m: ac.handle_accept() assert m.called self.assertIsNone(ac.socket)
def test_handle_accept_econnacorted(self): # https://github.com/giampaolo/pyftpdlib/issues/105 ac = Acceptor() with mock.patch.object(ac, "accept", side_effect=socket.error( errno.ECONNABORTED, "")) as m: ac.handle_accept() assert m.called self.assertIsNone(ac.socket)
def test_bind_af_unspecified_err(self): ac = Acceptor() with mock.patch.object(ac, "bind", side_effect=socket.error(errno.EBADF, "")) as m: self.assertRaises(socket.error, ac.bind_af_unspecified, ("localhost", 0)) assert m.called self.assertIsNone(ac.socket)
def __init__(self, address_or_socket, handler, ioloop=None, backlog=100): """Creates a socket listening on 'address' dispatching connections to a 'handler'. - (tuple) address_or_socket: the (host, port) pair on which the command channel will listen for incoming connections or an existent socket object. - (instance) handler: the handler class to use. - (instance) ioloop: a pyftpdlib.ioloop.IOLoop instance - (int) backlog: the maximum number of queued connections passed to listen(). If a connection request arrives when the queue is full the client may raise ECONNRESET. Defaults to 5. """ Acceptor.__init__(self, ioloop=ioloop) self.handler = handler self.backlog = backlog self.ip_map = [] # in case of FTPS class not properly configured we want errors # to be raised here rather than later, when client connects if hasattr(handler, 'get_ssl_context'): handler.get_ssl_context() if callable(getattr(address_or_socket, 'listen', None)): sock = address_or_socket sock.setblocking(0) self.set_socket(sock) if hasattr(sock, 'family'): self._af = sock.family else: # python 2.4 ip, port = self.socket.getsockname()[:2] self._af = socket.getaddrinfo(ip, port, socket.AF_UNSPEC, socket.SOCK_STREAM)[0][0] else: self._af = self.bind_af_unspecified(address_or_socket) self.listen(backlog)
def __init__(self, cmd_channel, extmode=False): """Initialize the passive data server. - (instance) cmd_channel: the command channel class instance. - (bool) extmode: wheter use extended passive mode response type. """ self.cmd_channel = cmd_channel self.log = cmd_channel.log self.log_exception = cmd_channel.log_exception self._closed = False self._idler = None Acceptor.__init__(self, ioloop=cmd_channel.ioloop) local_ip = self.cmd_channel.socket.getsockname()[0] if local_ip in self.cmd_channel.masquerade_address_map: masqueraded_ip = self.cmd_channel.masquerade_address_map[local_ip] elif self.cmd_channel.masquerade_address: masqueraded_ip = self.cmd_channel.masquerade_address else: masqueraded_ip = None if self.cmd_channel.server._af != socket.AF_INET: # dual stack IPv4/IPv6 support af = self.bind_af_unspecified((local_ip, 0)) self.socket.close() else: af = self.cmd_channel._af self.create_socket(af, socket.SOCK_STREAM) if self.cmd_channel.passive_ports is None: # By using 0 as port number value we let kernel choose a # free unprivileged random port. self.bind((local_ip, 0)) else: ports = list(self.cmd_channel.passive_ports) while ports: port = ports.pop(random.randint(0, len(ports) - 1)) self.set_reuse_addr() try: self.bind((local_ip, port)) except socket.error: err = sys.exc_info()[1] if err.args[0] == errno.EADDRINUSE: # port already in use if ports: continue # If cannot use one of the ports in the configured # range we'll use a kernel-assigned port, and log # a message reporting the issue. # By using 0 as port number value we let kernel # choose a free unprivileged random port. else: self.bind((local_ip, 0)) self.cmd_channel.log( "Can't find a valid passive port in the " "configured range. A random kernel-assigned " "port will be used.", logfun=logger.warning) else: raise else: break self.listen(self.backlog or self.cmd_channel.server.backlog) port = self.socket.getsockname()[1] if not extmode: ip = masqueraded_ip or local_ip if ip.startswith('::ffff:'): # In this scenario, the server has an IPv6 socket, but # the remote client is using IPv4 and its address is # represented as an IPv4-mapped IPv6 address which # looks like this ::ffff:151.12.5.65, see: # http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_addresses # http://tools.ietf.org/html/rfc3493.html#section-3.7 # We truncate the first bytes to make it look like a # common IPv4 address. ip = ip[7:] # The format of 227 response in not standardized. # This is the most expected: self.cmd_channel.respond( '227 Entering passive mode (%s,%d,%d).' % (ip.replace('.', ','), port // 256, port % 256)) else: self.cmd_channel.respond('229 Entering extended passive mode ' '(|||%d|).' % port) if self.timeout: self._idler = self.ioloop.call_later(self.timeout, self.handle_timeout, _errback=self.handle_error)
def close(self): if not self._closed: self._closed = True Acceptor.close(self) if self._idler is not None and not self._idler.cancelled: self._idler.cancel()