From: Amos Jeffries Date: Thu, 23 Jul 2009 12:13:42 +0000 (+1200) Subject: Make EPSV selection based on data socket protocol. X-Git-Tag: SQUID_3_2_0_1~854 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9adb5bc5118c69c738c549ad454c2c8c6179df7c;p=thirdparty%2Fsquid.git Make EPSV selection based on data socket protocol. * EPSV ALL still sent by preference. * EPSV 2 only attempted on IPv6 data links * EPSV 1 only attempted on IPv4 data links --- diff --git a/src/ftp.cc b/src/ftp.cc index b50d299a6e..cf607d9336 100644 --- a/src/ftp.cc +++ b/src/ftp.cc @@ -2597,49 +2597,41 @@ ftpSendPassive(FtpStateData * ftpState) } addr = *AI; - addr.FreeAddrInfo(AI); - /** Otherwise, Open data channel with the same local address as control channel (on a new random port!) */ - addr.SetPort(0); - int fd = comm_open(SOCK_STREAM, - IPPROTO_TCP, - addr, - COMM_NONBLOCKING, - ftpState->entry->url()); - - debugs(9, 3, HERE << "Unconnected data socket created on FD " << fd << " to " << addr); - - if (fd < 0) { - ftpFail(ftpState); - return; - } - - ftpState->data.opened(fd, ftpState->dataCloser()); - /** \par * Send EPSV (ALL,2,1) or PASV on the control channel. * * - EPSV ALL is used if enabled. - * - EPSV 2 is used if ALL is disabled and IPv6 is available. - * - EPSV 1 is used if EPSV 2 (IPv6) fails or is not available. + * - EPSV 2 is used if ALL is disabled and IPv6 is available and ctrl channel is IPv6. + * - EPSV 1 is used if EPSV 2 (IPv6) fails or is not available or ctrl channel is IPv4. * - PASV is used if EPSV 1 fails. */ switch (ftpState->state) { - case SENT_EPSV_1: /* EPSV options exhausted. Try PASV now. */ - snprintf(cbuf, 1024, "PASV\r\n"); - ftpState->state = SENT_PASV; - break; + case SENT_EPSV_ALL: /* EPSV ALL resulted in a bad response. Try ther EPSV methods. */ + ftpState->flags.epsv_all_sent = true; + if (addr.IsIPv6()) { + snprintf(cbuf, 1024, "EPSV 2\r\n"); + ftpState->state = SENT_EPSV_2; + break; + } + // else fall through to skip EPSV 2 case SENT_EPSV_2: /* EPSV IPv6 failed. Try EPSV IPv4 */ - snprintf(cbuf, 1024, "EPSV 1\r\n"); - ftpState->state = SENT_EPSV_1; - break; + if (addr.IsIPv4()) { + snprintf(cbuf, 1024, "EPSV 1\r\n"); + ftpState->state = SENT_EPSV_1; + } + else if (ftpState->flags.epsv_all_sent) { + debugs(9, DBG_IMPORTANT, "FTP does not allow PASV method after 'EPSV ALL' has been sent."); + ftpFail(ftpState); + return; + } + // else fall through to skip EPSV 1 - case SENT_EPSV_ALL: /* EPSV ALL resulted in a bad response. Try ther EPSV methods. */ - ftpState->flags.epsv_all_sent = true; - snprintf(cbuf, 1024, "EPSV 2\r\n"); - ftpState->state = SENT_EPSV_2; + case SENT_EPSV_1: /* EPSV options exhausted. Try PASV now. */ + snprintf(cbuf, 1024, "PASV\r\n"); + ftpState->state = SENT_PASV; break; default: @@ -2663,6 +2655,22 @@ ftpSendPassive(FtpStateData * ftpState) break; } + /** Otherwise, Open data channel with the same local address as control channel (on a new random port!) */ + addr.SetPort(0); + int fd = comm_open(SOCK_STREAM, + IPPROTO_TCP, + addr, + COMM_NONBLOCKING, + ftpState->entry->url()); + + debugs(9, 3, HERE << "Unconnected data socket created on FD " << fd << " to " << addr); + + if (fd < 0) { + ftpFail(ftpState); + return; + } + + ftpState->data.opened(fd, ftpState->dataCloser()); ftpState->writeCommand(cbuf); /*