Ticket #121: Fixed IPV4 FTP connection scheme

* Fixed endianness issue introduced by IPV6 patch, which prevented
      active connections to be established via PORT mechanism.

      FTP server always expects to see the port broken down into bytes
      in network byte order, so the transformation to host byte order is
      only needed when using EPRT.

    * Some IPV4 FTP servers and FTP ALGs / NATs with connection tracking
      become confused when you try to use EPSV or EPRT instead of PASV or
      PORT respectively.

      This commit changes current FTP connection scheme: if we deal with
      an IPV4 host, instead of first trying to use EPRT, first try to
      use PORT and only if it fails try EPRT as a fallback.

Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
This commit is contained in:
Yury V. Zaytsev 2010-09-08 21:03:41 +02:00
parent 0406a18dd4
commit a402ebc981

View File

@ -1137,8 +1137,6 @@ ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
ERRNOR (EINVAL, -1);
}
port = ntohs (port);
addr = g_try_malloc (NI_MAXHOST);
if (addr == NULL)
ERRNOR (ENOMEM, -1);
@ -1151,13 +1149,7 @@ ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
ERRNOR (EIO, -1);
}
if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
{
g_free (addr);
return data_sock;
}
g_free (addr);
/* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
if (FTP_INET == af)
{
unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
@ -1166,8 +1158,28 @@ ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
if (ftpfs_command (me, super, WAIT_REPLY,
"PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
p[0], p[1]) == COMPLETE)
{
g_free (addr);
return data_sock;
}
}
/*
* Converts network MSB first order to host byte order (LSB
* first on i386). If we do it earlier, we will run into an
* endianness issue, because the server actually expects to see
* "PORT A,D,D,R,MSB,LSB" in the PORT command.
*/
port = ntohs (port);
/* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
if (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == COMPLETE)
{
g_free (addr);
return data_sock;
}
g_free (addr);
}
close (data_sock);
ftpfs_errno = EIO;