]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Make EPSV selection based on data socket protocol.
authorAmos Jeffries <squid3@treenet.co.nz>
Thu, 23 Jul 2009 12:13:42 +0000 (00:13 +1200)
committerAmos Jeffries <squid3@treenet.co.nz>
Thu, 23 Jul 2009 12:13:42 +0000 (00:13 +1200)
* EPSV ALL still sent by preference.
* EPSV 2 only attempted on IPv6 data links
* EPSV 1 only attempted on IPv4 data links

src/ftp.cc

index b50d299a6e05ace4b39aa6c26d81fbeca58589e1..cf607d9336e61b0f0d743314fff7062fbf89dcdf 100644 (file)
@@ -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);
 
     /*