/*****************************************************************************
* ftp.c: FTP input module
*****************************************************************************
- * Copyright (C) 2001-2005 VideoLAN (Centrale Réseaux) and its contributors
+ * Copyright (C) 2001-2005 the VideoLAN team
* $Id$
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr> - original code
- * Rémi Denis-Courmont <rem # videolan.org> - EPSV support
+ * Rémi Denis-Courmont <rem # videolan.org> - EPSV support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
#include <vlc/input.h>
#include "network.h"
-#if defined( UNDER_CE )
-# include <winsock.h>
-#elif defined( WIN32 )
-# include <winsock2.h>
-#else
-# include <sys/socket.h>
-#endif
+#include "vlc_url.h"
/*****************************************************************************
* Module descriptor
int fd_cmd;
int fd_data;
- char *psz_epsv_ip;
+ char sz_epsv_ip[NI_MAXNUMERICHOST];
};
static int ftp_SendCommand( access_t *, char *, ... );
static int ftp_StartStream( access_t *, int64_t );
static int ftp_StopStream ( access_t *);
-/****************************************************************************
- * Open: connect to ftp server and ask for file
- ****************************************************************************/
-static int Open( vlc_object_t *p_this )
+static int Connect( access_t *p_access, access_sys_t *p_sys )
{
- access_t *p_access = (access_t*)p_this;
- access_sys_t *p_sys;
- char *psz;
-
- int i_answer;
- char *psz_arg;
-
- /* Init p_access */
- p_access->pf_read = Read;
- p_access->pf_block = NULL;
- p_access->pf_seek = Seek;
- p_access->pf_control = Control;
- p_access->info.i_update = 0;
- p_access->info.i_size = 0;
- p_access->info.i_pos = 0;
- p_access->info.b_eof = VLC_FALSE;
- p_access->info.i_title = 0;
- p_access->info.i_seekpoint = 0;
- p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
- memset( p_sys, 0, sizeof( access_sys_t ) );
- p_sys->fd_cmd = -1;
- p_sys->fd_data = -1;
- p_sys->psz_epsv_ip = NULL;
-
- /* *** Parse URL and get server addr/port and path *** */
- psz = p_access->psz_path;
- while( *psz == '/' )
- {
- psz++;
- }
- vlc_UrlParse( &p_sys->url, psz, 0 );
-
- if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
- {
- msg_Err( p_access, "invalid server name" );
- goto exit_error;
- }
- if( p_sys->url.i_port <= 0 )
- {
- p_sys->url.i_port = 21; /* default port */
- }
+ int fd, i_answer;
+ char *psz;
/* *** Open a TCP connection with server *** */
msg_Dbg( p_access, "waiting for connection..." );
- p_sys->fd_cmd = net_OpenTCP( p_access, p_sys->url.psz_host,
- p_sys->url.i_port );
- if( p_sys->fd_cmd < 0 )
+ p_sys->fd_cmd = fd = net_ConnectTCP( p_access, p_sys->url.psz_host,
+ p_sys->url.i_port );
+ if( fd < 0 )
{
msg_Err( p_access, "failed to connect with server" );
- goto exit_error;
+ return -1;
}
for( ;; )
if( i_answer / 100 != 2 )
{
msg_Err( p_access, "connection rejected" );
- goto exit_error;
+ return -1;
}
msg_Dbg( p_access, "connection accepted (%d)", i_answer );
ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
{
free( psz );
- goto exit_error;
+ return -1;
}
free( psz );
ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
{
free( psz );
- goto exit_error;
+ return -1;
}
free( psz );
ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
{
free( psz );
- goto exit_error;
+ return -1;
}
free( psz );
if( i_answer / 100 != 2 )
{
msg_Err( p_access, "account rejected" );
- goto exit_error;
+ return -1;
}
msg_Dbg( p_access, "account accepted" );
break;
default:
msg_Err( p_access, "password rejected" );
- goto exit_error;
+ return -1;
}
break;
default:
msg_Err( p_access, "user rejected" );
- goto exit_error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Open: connect to ftp server and ask for file
+ ****************************************************************************/
+static int Open( vlc_object_t *p_this )
+{
+ access_t *p_access = (access_t*)p_this;
+ access_sys_t *p_sys;
+ char *psz;
+
+ int i_answer;
+ char *psz_arg;
+
+ /* Init p_access */
+ p_access->pf_read = Read;
+ p_access->pf_block = NULL;
+ p_access->pf_seek = Seek;
+ p_access->pf_control = Control;
+ p_access->info.i_update = 0;
+ p_access->info.i_size = 0;
+ p_access->info.i_pos = 0;
+ p_access->info.b_eof = VLC_FALSE;
+ p_access->info.i_title = 0;
+ p_access->info.i_seekpoint = 0;
+ p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
+ memset( p_sys, 0, sizeof( access_sys_t ) );
+ p_sys->fd_cmd = -1;
+ p_sys->fd_data = -1;
+
+ /* *** Parse URL and get server addr/port and path *** */
+ psz = p_access->psz_path;
+ while( *psz == '/' )
+ {
+ psz++;
+ }
+ vlc_UrlParse( &p_sys->url, psz, 0 );
+
+ if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
+ {
+ msg_Err( p_access, "invalid server name" );
+ goto exit_error;
+ }
+ if( p_sys->url.i_port <= 0 )
+ {
+ p_sys->url.i_port = 21; /* default port */
}
+ /* FTP URLs are relative to user's default directory (RFC1738)
+ For absolute path use ftp://foo.bar//usr/local/etc/filename */
+
+ if( *p_sys->url.psz_path == '/' )
+ p_sys->url.psz_path++;
+
+ if( Connect( p_access, p_sys ) < 0 )
+ goto exit_error;
+
/* Extended passive mode */
if( ftp_SendCommand( p_access, "EPSV ALL" ) < 0 )
{
msg_Err( p_access, "cannot request extended passive mode" );
- goto exit_error;
+ return -1;
}
if( ftp_ReadCommand( p_access, &i_answer, NULL ) == 2 )
{
- char hostaddr[NI_MAXNUMERICHOST];
- struct sockaddr_storage addr;
- socklen_t len = sizeof (addr);
-
- if( getpeername( p_sys->fd_cmd, (struct sockaddr *)&addr, &len ) )
- {
- msg_Err( p_access, "getpeername failed" );
+ if( net_GetPeerAddress( p_sys->fd_cmd, p_sys->sz_epsv_ip, NULL ) )
goto exit_error;
- }
-
- i_answer = vlc_getnameinfo( (struct sockaddr *)&addr, len, hostaddr,
- sizeof( hostaddr ), NULL, NI_NUMERICHOST );
- if( i_answer )
- {
- msg_Err( p_access, "getnameinfo failed: %s",
- vlc_gai_strerror( i_answer ) );
- goto exit_error;
- }
- p_sys->psz_epsv_ip = strdup( hostaddr );
}
- if( p_sys->psz_epsv_ip == NULL )
- msg_Info( p_access, "FTP Extended passive mode disabled" );
+ else
+ {
+ /* If ESPV ALL fails, we fallback to PASV.
+ * We have to restart the connection in case there is a NAT that
+ * understands EPSV ALL in the way, and hence won't allow PASV on
+ * the initial connection.
+ */
+ net_Close( p_sys->fd_cmd );
+ p_sys->fd_cmd = -1;
+ *p_sys->sz_epsv_ip = '\0';
+
+ if( ( p_sys->fd_cmd = Connect( p_access, p_sys ) ) < 0 )
+ goto exit_error;
+ msg_Info( p_access, "FTP Extended passive mode disabled" );
+ }
+
/* binary mode */
if( ftp_SendCommand( p_access, "TYPE I" ) < 0 ||
ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 )
return VLC_SUCCESS;
exit_error:
- if( p_sys->fd_cmd > 0 )
- {
+ if( p_sys->fd_cmd >= 0 )
net_Close( p_sys->fd_cmd );
- }
vlc_UrlClean( &p_sys->url );
free( p_sys );
return VLC_EGENERIC;
ftp_ReadCommand( p_access, NULL, NULL );
}
net_Close( p_sys->fd_cmd );
- if( p_sys->psz_epsv_ip != NULL )
- free( p_sys->psz_epsv_ip );
/* free memory */
vlc_UrlClean( &p_sys->url );
char psz_ipv4[16], *psz_ip;
int i_answer;
char *psz_arg, *psz_parser;
- unsigned a1, a2, a3, a4, p1, p2;
int i_port;
- if( ( ftp_SendCommand( p_access, p_sys->psz_epsv_ip != NULL
- ? "EPSV" : "PASV" ) < 0 )
+ psz_ip = p_sys->sz_epsv_ip;
+
+ if( ( ftp_SendCommand( p_access, *psz_ip ? "EPSV" : "PASV" ) < 0 )
|| ( ftp_ReadCommand( p_access, &i_answer, &psz_arg ) != 2 ) )
{
msg_Err( p_access, "cannot set passive mode" );
return VLC_EGENERIC;
}
- psz_ip = p_sys->psz_epsv_ip;
if( psz_ip != NULL )
{
char psz_fmt[7] = "(|||%u";
}
else
{
+ unsigned a1, a2, a3, a4, p1, p2;
+
if( ( sscanf( psz_parser, "(%u,%u,%u,%u,%u,%u", &a1, &a2, &a3, &a4,
&p1, &p2 ) < 6 ) || ( a1 > 255 ) || ( a2 > 255 )
|| ( a3 > 255 ) || ( a4 > 255 ) || ( p1 > 255 ) || ( p2 > 255 ) )
}
msg_Dbg( p_access, "waiting for data connection..." );
- p_sys->fd_data = net_OpenTCP( p_access, psz_ip, i_port );
+ p_sys->fd_data = net_ConnectTCP( p_access, psz_ip, i_port );
if( p_sys->fd_data < 0 )
{
msg_Err( p_access, "failed to connect with server" );
if( ftp_SendCommand( p_access, "ABOR" ) < 0 )
{
- msg_Warn( p_access, "cannot abord file" );
+ msg_Warn( p_access, "cannot abort file" );
if( p_sys->fd_data > 0 )
net_Close( p_sys->fd_data );
p_sys->fd_data = -1;