@@ -66,7 +66,11 @@ def __init__(self, conn):
6666 self .next_response = ''
6767 self .rest = None
6868 self .next_retr_data = RETR_DATA
69- self .push ('220 welcome' )
69+ self .push ('220 welcome' )
70+ # We use this as the string IPv4 address to direct the client
71+ # to in response to a PASV command. To test security behavior.
72+ # https://bugs.python.org/issue43285/.
73+ self .fake_pasv_server_ip = '252.253.254.255'
7074
7175 def collect_incoming_data (self , data ):
7276 self .in_buffer .append (data )
@@ -109,7 +113,8 @@ def cmd_pasv(self, arg):
109113 sock .bind ((self .socket .getsockname ()[0 ], 0 ))
110114 sock .listen (5 )
111115 sock .settimeout (10 )
112- ip , port = sock .getsockname ()[:2 ]
116+ port = sock .getsockname ()[1 ]
117+ ip = self .fake_pasv_server_ip
113118 ip = ip .replace ('.' , ',' )
114119 p1 , p2 = divmod (port , 256 )
115120 self .push ('227 entering passive mode (%s,%d,%d)' % (ip , p1 , p2 ))
@@ -576,6 +581,26 @@ def test_makepasv(self):
576581 conn .close ()
577582 # IPv4 is in use, just make sure send_epsv has not been used
578583 self .assertEqual (self .server .handler_instance .last_received_cmd , 'pasv' )
584+
585+ def test_makepasv_issue43285_security_disabled (self ):
586+ """Test the opt-in to the old vulnerable behavior."""
587+ self .client .trust_server_pasv_ipv4_address = True
588+ bad_host , port = self .client .makepasv ()
589+ self .assertEqual (
590+ bad_host , self .server .handler_instance .fake_pasv_server_ip )
591+ # Opening and closing a connection keeps the dummy server happy
592+ # instead of timing out on accept.
593+ socket .create_connection ((self .client .sock .getpeername ()[0 ], port ),
594+ timeout = TIMEOUT ).close ()
595+
596+ def test_makepasv_issue43285_security_enabled_default (self ):
597+ self .assertFalse (self .client .trust_server_pasv_ipv4_address )
598+ trusted_host , port = self .client .makepasv ()
599+ self .assertNotEqual (
600+ trusted_host , self .server .handler_instance .fake_pasv_server_ip )
601+ # Opening and closing a connection keeps the dummy server happy
602+ # instead of timing out on accept.
603+ socket .create_connection ((trusted_host , port ), timeout = TIMEOUT ).close ()
579604
580605 def test_line_too_long (self ):
581606 self .assertRaises (ftplib .Error , self .client .sendcmd ,
0 commit comments