1
1

Fixed passive FTP connection logic

In current implementation few issues were fixed making passive
connection scheme more reliable:

    * If it's IPV4, try PASV first, as some servers and ALGs get
      confused by EPSV, and only then EPSV as a fallback

    * When trying PASV or EPSV, actually try to connect to the provided
      port and if the connection fails, still try the next method

    * PASV and EPSV response parsing code was factored out in separate
      routines for clarity

Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
Этот коммит содержится в:
Yury V. Zaytsev 2010-09-11 10:57:43 +02:00
родитель a402ebc981
Коммит ada1848268

Просмотреть файл

@ -977,77 +977,104 @@ ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
return NULL;
}
/* Setup Passive PASV FTP connection */
static int
ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
{
char *c;
char n[6];
int xa, xb, xc, xd, xe, xf;
if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
return 0;
/* Parse remote parameters */
for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
if (!*c)
return 0;
if (!isdigit ((unsigned char) *c))
return 0;
if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
return 0;
n[0] = (unsigned char) xa;
n[1] = (unsigned char) xb;
n[2] = (unsigned char) xc;
n[3] = (unsigned char) xd;
n[4] = (unsigned char) xe;
n[5] = (unsigned char) xf;
memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
return 0;
return 1;
}
/* Setup Passive EPSV FTP connection */
static int
ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
{
char *c;
int port;
if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") != COMPLETE)
return 0;
/* (|||<port>|) */
c = strchr (reply_str, '|');
if (c == NULL)
return 0;
if (strlen (c) > 3)
c += 3;
else
return 0;
port = atoi (c);
if (port < 0 || port > 65535)
return 0;
port = htons (port);
switch (sa->ss_family)
{
case AF_INET:
((struct sockaddr_in *) sa)->sin_port = port;
break;
case AF_INET6:
((struct sockaddr_in6 *) sa)->sin6_port = port;
break;
}
if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
return 0;
return 1;
}
/* Setup Passive ftp connection, we use it for source routed connections */
static int
ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
int my_socket, struct sockaddr_storage *sa, socklen_t * salen)
int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
{
char *c;
if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "EPSV") == COMPLETE)
/* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
if (sa->ss_family == AF_INET)
{
int port;
/* (|||<port>|) */
c = strchr (reply_str, '|');
if (c == NULL)
return 0;
if (strlen (c) > 3)
c += 3;
else
return 0;
port = atoi (c);
if (port < 0 || port > 65535)
return 0;
port = htons (port);
switch (sa->ss_family)
{
case AF_INET:
((struct sockaddr_in *) sa)->sin_port = port;
break;
case AF_INET6:
((struct sockaddr_in6 *) sa)->sin6_port = port;
break;
default:
print_vfs_message (_("ftpfs: invalid address family"));
ERRNOR (EINVAL, -1);
}
}
else if (sa->ss_family == AF_INET)
{
int xa, xb, xc, xd, xe, xf;
char n[6];
if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
return 0;
/* Parse remote parameters */
for (c = reply_str + 4; (*c) && (!isdigit ((unsigned char) *c)); c++);
if (!*c)
return 0;
if (!isdigit ((unsigned char) *c))
return 0;
if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
return 0;
n[0] = (unsigned char) xa;
n[1] = (unsigned char) xb;
n[2] = (unsigned char) xc;
n[3] = (unsigned char) xd;
n[4] = (unsigned char) xe;
n[5] = (unsigned char) xf;
memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
/* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
return 0;
}
/* It's IPV6, so EPSV is our only hope */
else
return 0;
if (connect (my_socket, (struct sockaddr *) sa, *salen) < 0)
return 0;
{
if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
return 0;
}
return 1;
}