mirror of
https://github.com/mirror/wget.git
synced 2024-12-29 14:30:48 +08:00
Added support for FTPS
* doc/wget.texi: updated documentation to reflect the new FTPS functionality. * src/ftp-basic.c (ftp_greeting): new function to read the server's greeting. (ftp_login): greeting code was previously here. Moved to ftp_greeting to support FTPS implicit mode. (ftp_auth): wrapper around the AUTH TLS command. (ftp_ccc): wrapper around the CCC command. (ftp_pbsz): wrapper around the PBSZ command. (ftp_prot): wraooer around the PROT command. * src/ftp.c (get_ftp_greeting): new static function. (init_control_ssl_connection): new static function to start SSL/TLS on the control channel. (getftp): added hooks to support FTPS commands (RFCs 2228 and 4217). (ftp_loop_internal): test for new FTPS error codes. * src/ftp.h: new enum 'prot_level' with available FTPS protection levels + prototypes of previous functions. New flag for enum 'wget_ftp_fstatus' to track whether the data channel has some security mechanism enabled or not. * src/gnutls.c (struct wgnutls_transport_context): new field 'session_data'. (wgnutls_close): free GnuTLS session data before exiting. (ssl_connect_wget): save/resume SSL/TLS session. * src/http.c (establish_connection): refactor ssl_connect_wget call. (metalink_from_http): take into account SCHEME_FTPS as well. * src/init.c, src/main.c, src/options.h: new command line/wgetrc options. (main): in recursive downloads, check for SCHEME_FTPS as well. * src/openssl.c (struct openssl_transport_context): new field 'sess'. (ssl_connect_wget): save/resume SSL/TLS session. * src/retr.c (retrieve_url): check new scheme SCHEME_FTPS. * src/ssl.h (ssl_connect_wget): refactor. New parameter of type 'int *'. * src/url.c. src/url.h: new scheme SCHEME_FTPS. * src/wget.h: new FTPS error codes. * src/metalink.h: support FTPS scheme.
This commit is contained in:
parent
e624732563
commit
f8901af4e0
@ -2008,6 +2008,43 @@ this option has no effect. Symbolic links are always traversed in this
|
||||
case.
|
||||
@end table
|
||||
|
||||
@section FTPS Options
|
||||
|
||||
@table @samp
|
||||
@item --ftps-implicit
|
||||
This option tells Wget to use FTPS implicitly. Implicit FTPS consists of initializing
|
||||
SSL/TLS from the very beginning of the control connection. This option does not send
|
||||
an @code{AUTH TLS} command: it assumes the server speaks FTPS and directly starts an
|
||||
SSL/TLS connection. If the attempt is successful, the session continues just like
|
||||
regular FTPS (@code{PBSZ} and @code{PROT} are sent, etc.).
|
||||
Implicit FTPS is no longer a requirement for FTPS implementations, and thus
|
||||
many servers may not support it. If @samp{--ftps-implicit} is passed and no explicit
|
||||
port number specified, the default port for implicit FTPS, 990, will be used, instead
|
||||
of the default port for the "normal" (explicit) FTPS which is the same as that of FTP,
|
||||
21.
|
||||
|
||||
@item --no-ftps-resume-ssl
|
||||
Do not resume the SSL/TLS session in the data channel. When starting a data connection,
|
||||
Wget tries to resume the SSL/TLS session previously started in the control connection.
|
||||
SSL/TLS session resumption avoids performing an entirely new handshake by reusing
|
||||
the SSL/TLS parameters of a previous session. Typically, the FTPS servers want it that way,
|
||||
so Wget does this by default. Under rare circumstances however, one might want to
|
||||
start an entirely new SSL/TLS session in every data connection.
|
||||
This is what @samp{--no-ftps-resume-ssl} is for.
|
||||
|
||||
@item --ftps-clear-data-connection
|
||||
All the data connections will be in plain text. Only the control connection will be
|
||||
under SSL/TLS. Wget will send a @code{PROT C} command to achieve this, which must be
|
||||
approved by the server.
|
||||
|
||||
@item --ftps-fallback-to-ftp
|
||||
Fall back to FTP if FTPS is not supported by the target server. For security reasons,
|
||||
this option is not asserted by default. The default behaviour is to exit with an error.
|
||||
If a server does not successfully reply to the initial @code{AUTH TLS} command, or in the
|
||||
case of implicit FTPS, if the initial SSL/TLS connection attempt is rejected, it is
|
||||
considered that such server does not support FTPS.
|
||||
@end table
|
||||
|
||||
@node Recursive Retrieval Options, Recursive Accept/Reject Options, FTP Options, Invoking
|
||||
@section Recursive Retrieval Options
|
||||
|
||||
|
140
src/ftp-basic.c
140
src/ftp-basic.c
@ -135,6 +135,23 @@ ftp_request (const char *command, const char *value)
|
||||
return res;
|
||||
}
|
||||
|
||||
uerr_t
|
||||
ftp_greeting (int csock)
|
||||
{
|
||||
uerr_t err = FTPOK;
|
||||
char *response = NULL;
|
||||
|
||||
err = ftp_response (csock, &response);
|
||||
if (err != FTPOK)
|
||||
goto bail;
|
||||
if (*response != '2')
|
||||
err = FTPSRVERR;
|
||||
|
||||
bail:
|
||||
if (response)
|
||||
xfree (response);
|
||||
return err;
|
||||
}
|
||||
/* Sends the USER and PASS commands to the server, to control
|
||||
connection socket csock. */
|
||||
uerr_t
|
||||
@ -144,16 +161,6 @@ ftp_login (int csock, const char *acc, const char *pass)
|
||||
char *request, *respline;
|
||||
int nwritten;
|
||||
|
||||
/* Get greeting. */
|
||||
err = ftp_response (csock, &respline);
|
||||
if (err != FTPOK)
|
||||
return err;
|
||||
if (*respline != '2')
|
||||
{
|
||||
xfree (respline);
|
||||
return FTPSRVERR;
|
||||
}
|
||||
xfree (respline);
|
||||
/* Send USER username. */
|
||||
request = ftp_request ("USER", acc);
|
||||
nwritten = fd_write (csock, request, strlen (request), -1);
|
||||
@ -422,6 +429,119 @@ ip_address_to_eprt_repr (const ip_address *addr, int port, char *buf,
|
||||
buf[buflen - 1] = '\0';
|
||||
}
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
/*
|
||||
* The following three functions defined into this #ifdef block
|
||||
* wrap the extended FTP commands defined in RFC 2228 (FTP Security Extensions).
|
||||
* Currently, only FTPS is supported, so these functions are only compiled when SSL
|
||||
* support is available, because there's no point in using FTPS when there's no SSL.
|
||||
* Shall someone add new secure FTP protocols in the future, feel free to remove this
|
||||
* #ifdef, or add new constants to it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sends an AUTH command as defined by RFC 2228,
|
||||
* deriving its argument from the scheme. For example, if the provided scheme
|
||||
* is SCHEME_FTPS, the command sent will be "AUTH TLS". Currently, this is the only
|
||||
* scheme supported, so this function will return FTPNOAUTH when supplied a different
|
||||
* one. It will also return FTPNOAUTH if the target server does not support FTPS.
|
||||
*/
|
||||
uerr_t
|
||||
ftp_auth (int csock, enum url_scheme scheme)
|
||||
{
|
||||
uerr_t err = 0;
|
||||
int written = 0;
|
||||
char *request = NULL, *response = NULL;
|
||||
|
||||
if (scheme == SCHEME_FTPS)
|
||||
{
|
||||
request = ftp_request ("AUTH", "TLS");
|
||||
written = fd_write (csock, request, strlen (request), -1);
|
||||
if (written < 0)
|
||||
{
|
||||
err = WRITEFAILED;
|
||||
goto bail;
|
||||
}
|
||||
err = ftp_response (csock, &response);
|
||||
if (err != FTPOK)
|
||||
goto bail;
|
||||
if (*response != '2')
|
||||
err = FTPNOAUTH;
|
||||
}
|
||||
else
|
||||
err = FTPNOAUTH;
|
||||
|
||||
bail:
|
||||
xfree (request);
|
||||
xfree (response);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
uerr_t
|
||||
ftp_pbsz (int csock, int pbsz)
|
||||
{
|
||||
uerr_t err = 0;
|
||||
int written = 0;
|
||||
char spbsz[5];
|
||||
char *request = NULL, *response = NULL;
|
||||
|
||||
snprintf (spbsz, 5, "%d", pbsz);
|
||||
request = ftp_request ("PBSZ", spbsz);
|
||||
written = fd_write (csock, request, strlen (request), -1);
|
||||
if (written < 0)
|
||||
{
|
||||
err = WRITEFAILED;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
err = ftp_response (csock, &response);
|
||||
if (err != FTPOK)
|
||||
goto bail;
|
||||
if (*response != '2')
|
||||
err = FTPNOPBSZ;
|
||||
|
||||
bail:
|
||||
xfree (request);
|
||||
xfree (response);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
uerr_t
|
||||
ftp_prot (int csock, enum prot_level prot)
|
||||
{
|
||||
uerr_t err = 0;
|
||||
int written = 0;
|
||||
char *request = NULL, *response = NULL;
|
||||
/* value must be a single character value */
|
||||
char value[2];
|
||||
|
||||
value[0] = prot;
|
||||
value[1] = '\0';
|
||||
|
||||
request = ftp_request ("PROT", value);
|
||||
written = fd_write (csock, request, strlen (request), -1);
|
||||
if (written < 0)
|
||||
{
|
||||
err = WRITEFAILED;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
err = ftp_response (csock, &response);
|
||||
if (err != FTPOK)
|
||||
goto bail;
|
||||
if (*response != '2')
|
||||
err = FTPNOPROT;
|
||||
|
||||
bail:
|
||||
xfree (request);
|
||||
xfree (response);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif /* HAVE_SSL */
|
||||
|
||||
/* Bind a port and send the appropriate PORT command to the FTP
|
||||
server. Use acceptport after RETR, to get the socket of data
|
||||
connection. */
|
||||
|
237
src/ftp.c
237
src/ftp.c
@ -44,6 +44,7 @@ as that of the covered work. */
|
||||
#include "url.h"
|
||||
#include "retr.h"
|
||||
#include "ftp.h"
|
||||
#include "ssl.h"
|
||||
#include "connect.h"
|
||||
#include "host.h"
|
||||
#include "netrc.h"
|
||||
@ -237,6 +238,78 @@ print_length (wgint size, wgint start, bool authoritative)
|
||||
|
||||
static uerr_t ftp_get_listing (struct url *, ccon *, struct fileinfo **);
|
||||
|
||||
static uerr_t
|
||||
get_ftp_greeting(int csock, ccon *con)
|
||||
{
|
||||
uerr_t err = 0;
|
||||
|
||||
/* Get the server's greeting */
|
||||
err = ftp_greeting (csock);
|
||||
if (err != FTPOK)
|
||||
{
|
||||
logputs (LOG_NOTQUIET, "Error in server response. Closing.\n");
|
||||
fd_close (csock);
|
||||
con->csock = -1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
static uerr_t
|
||||
init_control_ssl_connection (int csock, struct url *u, bool *using_control_security)
|
||||
{
|
||||
bool using_security = false;
|
||||
|
||||
/* If '--ftps-implicit' was passed, perform the SSL handshake directly,
|
||||
* and do not send an AUTH command.
|
||||
* Otherwise send an AUTH sequence before login,
|
||||
* and perform the SSL handshake if accepted by server.
|
||||
*/
|
||||
if (!opt.ftps_implicit && !opt.server_response)
|
||||
logputs (LOG_VERBOSE, "==> AUTH TLS ... ");
|
||||
if (opt.ftps_implicit || ftp_auth (csock, SCHEME_FTPS) == FTPOK)
|
||||
{
|
||||
if (!ssl_connect_wget (csock, u->host, NULL))
|
||||
{
|
||||
fd_close (csock);
|
||||
return CONSSLERR;
|
||||
}
|
||||
else if (!ssl_check_certificate (csock, u->host))
|
||||
{
|
||||
fd_close (csock);
|
||||
return VERIFCERTERR;
|
||||
}
|
||||
|
||||
if (!opt.ftps_implicit && !opt.server_response)
|
||||
logputs (LOG_VERBOSE, " done.\n");
|
||||
|
||||
/* If implicit FTPS was requested, we act as "normal" FTP, but over SSL.
|
||||
* We're not using RFC 2228 commands.
|
||||
*/
|
||||
using_security = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The server does not support 'AUTH TLS'.
|
||||
* Check if --ftps-fallback-to-ftp was passed. */
|
||||
if (opt.ftps_fallback_to_ftp)
|
||||
{
|
||||
logputs (LOG_NOTQUIET, "Server does not support AUTH TLS. Falling back to FTP.\n");
|
||||
using_security = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
fd_close (csock);
|
||||
return FTPNOAUTH;
|
||||
}
|
||||
}
|
||||
|
||||
*using_control_security = using_security;
|
||||
return NOCONERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Retrieves a file with denoted parameters through opening an FTP
|
||||
connection to the server. It always closes the data connection,
|
||||
and closes the control connection in case of error. If warc_tmp
|
||||
@ -260,6 +333,15 @@ getftp (struct url *u, wgint passed_expected_bytes, wgint *qtyread,
|
||||
char type_char;
|
||||
bool try_again;
|
||||
bool list_a_used = false;
|
||||
#ifdef HAVE_SSL
|
||||
enum prot_level prot = (opt.ftps_clear_data_connection ? PROT_CLEAR : PROT_PRIVATE);
|
||||
/* these variables tell whether the target server
|
||||
* accepts the security extensions (RFC 2228) or not,
|
||||
* and whether we're actually using any of them
|
||||
* (encryption at the control connection only,
|
||||
* or both at control and data connections) */
|
||||
bool using_control_security = false, using_data_security = false;
|
||||
#endif
|
||||
|
||||
assert (con != NULL);
|
||||
assert (con->target != NULL);
|
||||
@ -285,8 +367,34 @@ getftp (struct url *u, wgint passed_expected_bytes, wgint *qtyread,
|
||||
local_sock = -1;
|
||||
con->dltime = 0;
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
if (u->scheme == SCHEME_FTPS)
|
||||
{
|
||||
/* Initialize SSL layer first */
|
||||
if (!ssl_init ())
|
||||
{
|
||||
scheme_disable (SCHEME_FTPS);
|
||||
logprintf (LOG_NOTQUIET, _("Could not initialize SSL. It will be disabled."));
|
||||
err = SSLINITFAILED;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* If we're using the default FTP port and implicit FTPS was requested,
|
||||
* rewrite the port to the default *implicit* FTPS port.
|
||||
*/
|
||||
if (opt.ftps_implicit && u->port == DEFAULT_FTP_PORT)
|
||||
{
|
||||
DEBUGP (("Implicit FTPS was specified. Rewriting default port to %d.\n", DEFAULT_FTPS_IMPLICIT_PORT));
|
||||
u->port = DEFAULT_FTPS_IMPLICIT_PORT;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(cmd & DO_LOGIN))
|
||||
csock = con->csock;
|
||||
{
|
||||
csock = con->csock;
|
||||
using_data_security = con->st & DATA_CHANNEL_SECURITY;
|
||||
}
|
||||
else /* cmd & DO_LOGIN */
|
||||
{
|
||||
char *host = con->proxy ? con->proxy->host : u->host;
|
||||
@ -308,6 +416,43 @@ getftp (struct url *u, wgint passed_expected_bytes, wgint *qtyread,
|
||||
else
|
||||
con->csock = -1;
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
if (u->scheme == SCHEME_FTPS)
|
||||
{
|
||||
/* If we're in implicit FTPS mode, we have to set up SSL/TLS before everything else.
|
||||
* Otherwise we first read the server's greeting, and then send an "AUTH TLS".
|
||||
*/
|
||||
if (opt.ftps_implicit)
|
||||
{
|
||||
err = init_control_ssl_connection (csock, u, &using_control_security);
|
||||
if (err != NOCONERROR)
|
||||
return err;
|
||||
err = get_ftp_greeting (csock, con);
|
||||
if (err != FTPOK)
|
||||
return err;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = get_ftp_greeting (csock, con);
|
||||
if (err != FTPOK)
|
||||
return err;
|
||||
err = init_control_ssl_connection (csock, u, &using_control_security);
|
||||
if (err != NOCONERROR)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
err = get_ftp_greeting (csock, con);
|
||||
if (err != FTPOK)
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
err = get_ftp_greeting (csock, con);
|
||||
if (err != FTPOK)
|
||||
return err;
|
||||
#endif
|
||||
|
||||
/* Second: Login with proper USER/PASS sequence. */
|
||||
logprintf (LOG_VERBOSE, _("Logging in as %s ... "),
|
||||
quotearg_style (escape_quoting_style, user));
|
||||
@ -365,6 +510,46 @@ Error in server response, closing control connection.\n"));
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
if (using_control_security)
|
||||
{
|
||||
/* Send the PBSZ and PROT commands, in that order.
|
||||
* If we are here it means that the server has already accepted
|
||||
* some form of FTPS. Thus, these commands must work.
|
||||
* If they don't work, that's an error. There's no sense in honoring
|
||||
* --ftps-fallback-to-ftp or similar options. */
|
||||
if (u->scheme == SCHEME_FTPS)
|
||||
{
|
||||
if (!opt.server_response)
|
||||
logputs (LOG_VERBOSE, "==> PBSZ 0 ... ");
|
||||
if ((err = ftp_pbsz (csock, 0)) == FTPNOPBSZ)
|
||||
{
|
||||
logputs (LOG_NOTQUIET, _("Server did not accept the 'PBSZ 0' command.\n"));
|
||||
return err;
|
||||
}
|
||||
if (!opt.server_response)
|
||||
logputs (LOG_VERBOSE, "done.");
|
||||
|
||||
if (!opt.server_response)
|
||||
logprintf (LOG_VERBOSE, " ==> PROT %c ... ", prot);
|
||||
if ((err = ftp_prot (csock, prot)) == FTPNOPROT)
|
||||
{
|
||||
logprintf (LOG_NOTQUIET, _("Server did not accept the 'PROT %c' command.\n"), prot);
|
||||
return err;
|
||||
}
|
||||
if (!opt.server_response)
|
||||
logputs (LOG_VERBOSE, "done.\n");
|
||||
|
||||
if (prot != PROT_CLEAR)
|
||||
{
|
||||
using_data_security = true;
|
||||
con->st |= DATA_CHANNEL_SECURITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Third: Get the system type */
|
||||
if (!opt.server_response)
|
||||
logprintf (LOG_VERBOSE, "==> SYST ... ");
|
||||
@ -1313,6 +1498,36 @@ Error in server response, closing control connection.\n"));
|
||||
else if (expected_bytes)
|
||||
print_length (expected_bytes, restval, false);
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
if (u->scheme == SCHEME_FTPS && using_data_security)
|
||||
{
|
||||
/* We should try to restore the existing SSL session in the data connection
|
||||
* and fall back to establishing a new session if the server doesn't want to restore it.
|
||||
*/
|
||||
if (!opt.ftps_resume_ssl || !ssl_connect_wget (dtsock, u->host, &csock))
|
||||
{
|
||||
if (opt.ftps_resume_ssl)
|
||||
logputs (LOG_NOTQUIET, "Server does not want to resume the SSL session. Trying with a new one.\n");
|
||||
if (!ssl_connect_wget (dtsock, u->host, NULL))
|
||||
{
|
||||
fd_close (csock);
|
||||
fd_close (dtsock);
|
||||
logputs (LOG_NOTQUIET, "Could not perform SSL handshake.\n");
|
||||
return CONERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
logputs (LOG_NOTQUIET, "Resuming SSL session in data connection.\n");
|
||||
|
||||
if (!ssl_check_certificate (dtsock, u->host))
|
||||
{
|
||||
fd_close (csock);
|
||||
fd_close (dtsock);
|
||||
return CONERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Get the contents of the document. */
|
||||
flags = 0;
|
||||
if (restval && rest_failed)
|
||||
@ -1377,10 +1592,18 @@ Error in server response, closing control connection.\n"));
|
||||
become apparent later. */
|
||||
if (*respline != '2')
|
||||
{
|
||||
xfree (respline);
|
||||
if (res != -1)
|
||||
logprintf (LOG_NOTQUIET, "%s (%s) - ", tms, tmrate);
|
||||
logputs (LOG_NOTQUIET, _("Data transfer aborted.\n"));
|
||||
#ifdef HAVE_SSL
|
||||
if (!c_strncasecmp (respline, "425", 3) && u->scheme == SCHEME_FTPS)
|
||||
{
|
||||
logputs (LOG_NOTQUIET, "FTPS server rejects new SSL sessions in the data connection.\n");
|
||||
xfree (respline);
|
||||
return FTPRESTFAIL;
|
||||
}
|
||||
#endif
|
||||
xfree (respline);
|
||||
return FTPRETRINT;
|
||||
}
|
||||
xfree (respline);
|
||||
@ -1700,8 +1923,14 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con, char **local_fi
|
||||
switch (err)
|
||||
{
|
||||
case HOSTERR: case CONIMPOSSIBLE: case FWRITEERR: case FOPENERR:
|
||||
case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED:
|
||||
case UNLINKERR: case WARC_TMP_FWRITEERR:
|
||||
case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case FTPNOAUTH: case FTPNOPBSZ: case FTPNOPROT:
|
||||
case UNLINKERR: case WARC_TMP_FWRITEERR: case CONSSLERR: case CONTNOTSUPPORTED:
|
||||
#ifdef HAVE_SSL
|
||||
if (err == FTPNOAUTH)
|
||||
logputs (LOG_NOTQUIET, "Server does not support AUTH TLS.\n");
|
||||
if (opt.ftps_implicit)
|
||||
logputs (LOG_NOTQUIET, "Server does not like implicit FTPS connections.\n");
|
||||
#endif
|
||||
/* Fatal errors, give up. */
|
||||
if (warc_tmp != NULL)
|
||||
fclose (warc_tmp);
|
||||
|
21
src/ftp.h
21
src/ftp.h
@ -33,6 +33,7 @@ as that of the covered work. */
|
||||
#define FTP_H
|
||||
|
||||
#include "host.h"
|
||||
#include "url.h"
|
||||
|
||||
/* System types. */
|
||||
enum stype
|
||||
@ -53,10 +54,27 @@ enum ustype
|
||||
UST_OTHER
|
||||
};
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
/* Data channel protection levels (to be used with PBSZ) */
|
||||
enum prot_level
|
||||
{
|
||||
PROT_CLEAR = 'C',
|
||||
PROT_SAFE = 'S',
|
||||
PROT_CONFIDENTIAL = 'E',
|
||||
PROT_PRIVATE = 'P'
|
||||
};
|
||||
#endif
|
||||
|
||||
uerr_t ftp_response (int, char **);
|
||||
uerr_t ftp_greeting (int);
|
||||
uerr_t ftp_login (int, const char *, const char *);
|
||||
uerr_t ftp_port (int, int *);
|
||||
uerr_t ftp_pasv (int, ip_address *, int *);
|
||||
#ifdef HAVE_SSL
|
||||
uerr_t ftp_auth (int, enum url_scheme);
|
||||
uerr_t ftp_pbsz (int, int);
|
||||
uerr_t ftp_prot (int, enum prot_level);
|
||||
#endif
|
||||
#ifdef ENABLE_IPV6
|
||||
uerr_t ftp_lprt (int, int *);
|
||||
uerr_t ftp_lpsv (int, ip_address *, int *);
|
||||
@ -142,11 +160,12 @@ enum wget_ftp_fstatus
|
||||
AVOID_LIST = 0x0008, /* It tells us if during this
|
||||
session we have to avoid to use
|
||||
"LIST". */
|
||||
LIST_AFTER_LIST_A_CHECK_DONE = 0x0010
|
||||
LIST_AFTER_LIST_A_CHECK_DONE = 0x0010,
|
||||
/* It tells us if we have already
|
||||
checked "LIST" after the first
|
||||
"LIST -a" to handle the case of
|
||||
file/folders named "-a". */
|
||||
DATA_CHANNEL_SECURITY = 0x0020 /* Establish a secure data channel */
|
||||
};
|
||||
|
||||
struct fileinfo *ftp_parse_ls (const char *, const enum stype);
|
||||
|
35
src/gnutls.c
35
src/gnutls.c
@ -219,6 +219,7 @@ cert to be of the same type.\n"));
|
||||
struct wgnutls_transport_context
|
||||
{
|
||||
gnutls_session_t session; /* GnuTLS session handle */
|
||||
gnutls_datum_t *session_data;
|
||||
int last_error; /* last error returned by read/write/... */
|
||||
|
||||
/* Since GnuTLS doesn't support the equivalent to recv(...,
|
||||
@ -405,6 +406,11 @@ wgnutls_close (int fd, void *arg)
|
||||
{
|
||||
struct wgnutls_transport_context *ctx = arg;
|
||||
/*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
|
||||
if (ctx->session_data)
|
||||
{
|
||||
gnutls_free (ctx->session_data->data);
|
||||
gnutls_free (ctx->session_data);
|
||||
}
|
||||
gnutls_deinit (ctx->session);
|
||||
xfree (ctx);
|
||||
close (fd);
|
||||
@ -420,7 +426,7 @@ static struct transport_implementation wgnutls_transport =
|
||||
};
|
||||
|
||||
bool
|
||||
ssl_connect_wget (int fd, const char *hostname)
|
||||
ssl_connect_wget (int fd, const char *hostname, int *continue_session)
|
||||
{
|
||||
#ifdef F_GETFL
|
||||
int flags = 0;
|
||||
@ -531,6 +537,27 @@ ssl_connect_wget (int fd, const char *hostname)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (continue_session)
|
||||
{
|
||||
ctx = (struct wgnutls_transport_context *) fd_transport_context (*continue_session);
|
||||
if (!gnutls_session_is_resumed (session))
|
||||
{
|
||||
if (!ctx || !ctx->session_data || gnutls_session_set_data (session, ctx->session_data->data, ctx->session_data->size))
|
||||
{
|
||||
/* server does not want to continue the session */
|
||||
gnutls_free (ctx->session_data->data);
|
||||
gnutls_free (ctx->session_data);
|
||||
gnutls_deinit (session);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logputs (LOG_ALWAYS, "SSL session has already been resumed. Continuing.\n");
|
||||
continue_session = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (opt.connect_timeout)
|
||||
{
|
||||
#ifdef F_GETFL
|
||||
@ -612,7 +639,13 @@ ssl_connect_wget (int fd, const char *hostname)
|
||||
}
|
||||
|
||||
ctx = xnew0 (struct wgnutls_transport_context);
|
||||
ctx->session_data = xnew0 (gnutls_datum_t);
|
||||
ctx->session = session;
|
||||
if (gnutls_session_get_data2 (session, ctx->session_data))
|
||||
{
|
||||
xfree (ctx->session_data);
|
||||
logprintf (LOG_NOTQUIET, "WARNING: Could not save SSL session data for socket %d\n", fd);
|
||||
}
|
||||
fd_register_transport (fd, &wgnutls_transport, ctx);
|
||||
return true;
|
||||
}
|
||||
|
@ -2141,7 +2141,7 @@ establish_connection (struct url *u, struct url **conn_ref,
|
||||
|
||||
if (conn->scheme == SCHEME_HTTPS)
|
||||
{
|
||||
if (!ssl_connect_wget (sock, u->host))
|
||||
if (!ssl_connect_wget (sock, u->host, NULL))
|
||||
{
|
||||
CLOSE_INVALIDATE (sock);
|
||||
return CONSSLERR;
|
||||
@ -2742,6 +2742,9 @@ metalink_from_http (const struct response *resp, const struct http_stat *hs,
|
||||
case SCHEME_HTTPS:
|
||||
mres.type = xstrdup ("https");
|
||||
break;
|
||||
case SCHEME_FTPS:
|
||||
mres.type = xstrdup ("ftps");
|
||||
break;
|
||||
#endif
|
||||
case SCHEME_FTP:
|
||||
mres.type = xstrdup ("ftp");
|
||||
|
10
src/init.c
10
src/init.c
@ -188,6 +188,12 @@ static const struct {
|
||||
{ "ftppasswd", &opt.ftp_passwd, cmd_string }, /* deprecated */
|
||||
{ "ftppassword", &opt.ftp_passwd, cmd_string },
|
||||
{ "ftpproxy", &opt.ftp_proxy, cmd_string },
|
||||
#ifdef HAVE_SSL
|
||||
{ "ftpscleardataconnection", &opt.ftps_clear_data_connection, cmd_boolean },
|
||||
{ "ftpsfallbacktoftp", &opt.ftps_fallback_to_ftp, cmd_boolean },
|
||||
{ "ftpsimplicit", &opt.ftps_implicit, cmd_boolean },
|
||||
{ "ftpsresumessl", &opt.ftps_resume_ssl, cmd_boolean },
|
||||
#endif
|
||||
#ifdef __VMS
|
||||
{ "ftpstmlf", &opt.ftp_stmlf, cmd_boolean },
|
||||
#endif /* def __VMS */
|
||||
@ -408,6 +414,10 @@ defaults (void)
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
opt.check_cert = true;
|
||||
opt.ftps_resume_ssl = true;
|
||||
opt.ftps_fallback_to_ftp = false;
|
||||
opt.ftps_implicit = false;
|
||||
opt.ftps_clear_data_connection = false;
|
||||
#endif
|
||||
|
||||
/* The default for file name restriction defaults to the OS type. */
|
||||
|
25
src/main.c
25
src/main.c
@ -293,6 +293,12 @@ static struct cmdline_option option_data[] =
|
||||
{ "ftp-stmlf", 0, OPT_BOOLEAN, "ftpstmlf", -1 },
|
||||
#endif /* def __VMS */
|
||||
{ "ftp-user", 0, OPT_VALUE, "ftpuser", -1 },
|
||||
#ifdef HAVE_SSL
|
||||
{ "ftps-clear-data-connection", 0, OPT_BOOLEAN, "ftpscleardataconnection", -1 },
|
||||
{ "ftps-fallback-to-ftp", 0, OPT_BOOLEAN, "ftpsfallbacktoftp", -1 },
|
||||
{ "ftps-implicit", 0, OPT_BOOLEAN, "ftpsimplicit", -1 },
|
||||
{ "ftps-resume-ssl", 0, OPT_BOOLEAN, "ftpsresumessl", -1 },
|
||||
#endif
|
||||
{ "glob", 0, OPT_BOOLEAN, "glob", -1 },
|
||||
{ "header", 0, OPT_VALUE, "header", -1 },
|
||||
{ "help", 'h', OPT_FUNCALL, (void *)print_help, no_argument },
|
||||
@ -820,6 +826,20 @@ FTP options:\n"),
|
||||
--retr-symlinks when recursing, get linked-to files (not dir)\n"),
|
||||
"\n",
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
N_("\
|
||||
FTPS options:\n"),
|
||||
N_("\
|
||||
--ftps-implicit use implicit FTPS (default port is 990)\n"),
|
||||
N_("\
|
||||
--ftps-resume-ssl resume the SSL/TLS session started in the control connection when\n"
|
||||
" opening a data connection\n"),
|
||||
N_("\
|
||||
--ftps-clear-data-connection cipher the control channel only; all the data will be in plaintext\n"),
|
||||
N_("\
|
||||
--ftps-fallback-to-ftp fall back to FTP if FTPS is not supported in the target server\n"),
|
||||
#endif
|
||||
|
||||
N_("\
|
||||
WARC options:\n"),
|
||||
N_("\
|
||||
@ -1810,12 +1830,13 @@ outputting to a regular file.\n"));
|
||||
else
|
||||
{
|
||||
if ((opt.recursive || opt.page_requisites)
|
||||
&& (url_scheme (*t) != SCHEME_FTP || url_uses_proxy (url_parsed)))
|
||||
&& ((url_scheme (*t) != SCHEME_FTP && url_scheme (*t) != SCHEME_FTPS)
|
||||
|| url_uses_proxy (url_parsed)))
|
||||
{
|
||||
int old_follow_ftp = opt.follow_ftp;
|
||||
|
||||
/* Turn opt.follow_ftp on in case of recursive FTP retrieval */
|
||||
if (url_scheme (*t) == SCHEME_FTP)
|
||||
if (url_scheme (*t) == SCHEME_FTP || url_scheme (*t) == SCHEME_FTPS)
|
||||
opt.follow_ftp = 1;
|
||||
|
||||
retrieve_tree (url_parsed, NULL);
|
||||
|
@ -34,7 +34,7 @@ as that of the covered work. */
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
# define RES_TYPE_SUPPORTED(x)\
|
||||
((!x) || !strcmp (x, "ftp") || !strcmp (x, "http") || !strcmp (x, "https"))
|
||||
((!x) || !strcmp (x, "http") || !strcmp (x, "https") || !strcmp (x, "ftp") || !strcmp (x, "ftps"))
|
||||
#else
|
||||
# define RES_TYPE_SUPPORTED(x)\
|
||||
((!x) || !strcmp (x, "ftp") || !strcmp (x, "http"))
|
||||
|
@ -334,6 +334,7 @@ ssl_init (void)
|
||||
struct openssl_transport_context
|
||||
{
|
||||
SSL *conn; /* SSL connection handle */
|
||||
SSL_SESSION *sess; /* SSL session info */
|
||||
char *last_error; /* last error printed with openssl_errstr */
|
||||
};
|
||||
|
||||
@ -514,7 +515,7 @@ ssl_connect_with_timeout_callback(void *arg)
|
||||
Returns true on success, false on failure. */
|
||||
|
||||
bool
|
||||
ssl_connect_wget (int fd, const char *hostname)
|
||||
ssl_connect_wget (int fd, const char *hostname, int *continue_session)
|
||||
{
|
||||
SSL *conn;
|
||||
struct scwt_context scwt_ctx;
|
||||
@ -527,7 +528,7 @@ ssl_connect_wget (int fd, const char *hostname)
|
||||
if (!conn)
|
||||
goto error;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
|
||||
/* If the SSL library was build with support for ServerNameIndication
|
||||
/* If the SSL library was built with support for ServerNameIndication
|
||||
then use it whenever we have a hostname. If not, don't, ever. */
|
||||
if (! is_valid_ip_address (hostname))
|
||||
{
|
||||
@ -539,6 +540,14 @@ ssl_connect_wget (int fd, const char *hostname)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (continue_session)
|
||||
{
|
||||
/* attempt to resume a previous SSL session */
|
||||
ctx = (struct openssl_transport_context *) fd_transport_context (*continue_session);
|
||||
if (!ctx || !ctx->sess || !SSL_set_session (conn, ctx->sess))
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifndef FD_TO_SOCKET
|
||||
# define FD_TO_SOCKET(X) (X)
|
||||
#endif
|
||||
@ -557,6 +566,9 @@ ssl_connect_wget (int fd, const char *hostname)
|
||||
|
||||
ctx = xnew0 (struct openssl_transport_context);
|
||||
ctx->conn = conn;
|
||||
ctx->sess = SSL_get0_session (conn);
|
||||
if (!ctx->sess)
|
||||
logprintf (LOG_NOTQUIET, "WARNING: Could not save SSL session data for socket %d\n", fd);
|
||||
|
||||
/* Register FD with Wget's transport layer, i.e. arrange that our
|
||||
functions are used for reading, writing, and polling. */
|
||||
|
@ -229,6 +229,10 @@ struct options
|
||||
char *random_file; /* file with random data to seed the PRNG */
|
||||
char *egd_file; /* file name of the egd daemon socket */
|
||||
bool https_only; /* whether to follow HTTPS only */
|
||||
bool ftps_resume_ssl;
|
||||
bool ftps_fallback_to_ftp;
|
||||
bool ftps_implicit;
|
||||
bool ftps_clear_data_connection;
|
||||
#endif /* HAVE_SSL */
|
||||
|
||||
bool cookies; /* whether cookies are used. */
|
||||
|
15
src/recur.c
15
src/recur.c
@ -610,7 +610,7 @@ download_child (const struct urlpos *upos, struct url *parent, int depth,
|
||||
u_scheme_like_http = schemes_are_similar_p (u->scheme, SCHEME_HTTP);
|
||||
|
||||
/* 1. Schemes other than HTTP are normally not recursed into. */
|
||||
if (!u_scheme_like_http && !(u->scheme == SCHEME_FTP && opt.follow_ftp))
|
||||
if (!u_scheme_like_http && !((u->scheme == SCHEME_FTP || u->scheme == SCHEME_FTPS) && opt.follow_ftp))
|
||||
{
|
||||
DEBUGP (("Not following non-HTTP schemes.\n"));
|
||||
reason = WG_RR_NONHTTP;
|
||||
@ -832,12 +832,13 @@ write_reject_log_url (FILE *fp, const struct url *url)
|
||||
|
||||
switch (url->scheme)
|
||||
{
|
||||
case SCHEME_HTTP: scheme_str = "SCHEME_HTTP"; break;
|
||||
#ifdef HAVE_SSL
|
||||
case SCHEME_HTTPS: scheme_str = "SCHEME_HTTPS"; break;
|
||||
#endif
|
||||
case SCHEME_FTP: scheme_str = "SCHEME_FTP"; break;
|
||||
default: scheme_str = "SCHEME_INVALID"; break;
|
||||
case SCHEME_HTTP: scheme_str = "SCHEME_HTTP"; break;
|
||||
#ifdef HAVE_SSL
|
||||
case SCHEME_HTTPS: scheme_str = "SCHEME_HTTPS"; break;
|
||||
case SCHEME_FTPS: scheme_str = "SCHEME_FTPS"; break;
|
||||
#endif
|
||||
case SCHEME_FTP: scheme_str = "SCHEME_FTP"; break;
|
||||
default: scheme_str = "SCHEME_INVALID"; break;
|
||||
}
|
||||
|
||||
fprintf (fp, "%s\t%s\t%s\t%i\t%s\t%s\t%s\t%s",
|
||||
|
15
src/retr.c
15
src/retr.c
@ -817,7 +817,11 @@ retrieve_url (struct url * orig_parsed, const char *origurl, char **file,
|
||||
result = http_loop (u, orig_parsed, &mynewloc, &local_file, refurl, dt,
|
||||
proxy_url, iri);
|
||||
}
|
||||
else if (u->scheme == SCHEME_FTP)
|
||||
else if (u->scheme == SCHEME_FTP
|
||||
#ifdef HAVE_SSL
|
||||
|| u->scheme == SCHEME_FTPS
|
||||
#endif
|
||||
)
|
||||
{
|
||||
/* If this is a redirection, temporarily turn off opt.ftp_glob
|
||||
and opt.recursive, both being undesirable when following
|
||||
@ -833,7 +837,7 @@ retrieve_url (struct url * orig_parsed, const char *origurl, char **file,
|
||||
FTP. In these cases we must decide whether the text is HTML
|
||||
according to the suffix. The HTML suffixes are `.html',
|
||||
`.htm' and a few others, case-insensitive. */
|
||||
if (redirection_count && local_file && u->scheme == SCHEME_FTP)
|
||||
if (redirection_count && local_file && (u->scheme == SCHEME_FTP || u->scheme == SCHEME_FTPS))
|
||||
{
|
||||
if (has_html_suffix_p (local_file))
|
||||
*dt |= TEXTHTML;
|
||||
@ -1095,12 +1099,12 @@ retrieve_from_file (const char *file, bool html, int *count)
|
||||
|
||||
proxy = getproxy (cur_url->url);
|
||||
if ((opt.recursive || opt.page_requisites)
|
||||
&& (cur_url->url->scheme != SCHEME_FTP || proxy))
|
||||
&& ((cur_url->url->scheme != SCHEME_FTP && cur_url->url->scheme != SCHEME_FTPS) || proxy))
|
||||
{
|
||||
int old_follow_ftp = opt.follow_ftp;
|
||||
|
||||
/* Turn opt.follow_ftp on in case of recursive FTP retrieval */
|
||||
if (cur_url->url->scheme == SCHEME_FTP)
|
||||
if (cur_url->url->scheme == SCHEME_FTP || cur_url->url->scheme == SCHEME_FTPS)
|
||||
opt.follow_ftp = 1;
|
||||
|
||||
status = retrieve_tree (parsed_url ? parsed_url : cur_url->url,
|
||||
@ -1281,6 +1285,9 @@ getproxy (struct url *u)
|
||||
case SCHEME_HTTPS:
|
||||
proxy = opt.https_proxy ? opt.https_proxy : getenv ("https_proxy");
|
||||
break;
|
||||
case SCHEME_FTPS:
|
||||
proxy = opt.ftp_proxy ? opt.ftp_proxy : getenv ("ftps_proxy");
|
||||
break;
|
||||
#endif
|
||||
case SCHEME_FTP:
|
||||
proxy = opt.ftp_proxy ? opt.ftp_proxy : getenv ("ftp_proxy");
|
||||
|
@ -33,7 +33,7 @@ as that of the covered work. */
|
||||
#define GEN_SSLFUNC_H
|
||||
|
||||
bool ssl_init (void);
|
||||
bool ssl_connect_wget (int, const char *);
|
||||
bool ssl_connect_wget (int, const char *, int *);
|
||||
bool ssl_check_certificate (int, const char *);
|
||||
|
||||
#endif /* GEN_SSLFUNC_H */
|
||||
|
11
src/url.c
11
src/url.c
@ -78,6 +78,13 @@ static struct scheme_data supported_schemes[] =
|
||||
{ "https", "https://", DEFAULT_HTTPS_PORT, scm_has_query|scm_has_fragment },
|
||||
#endif
|
||||
{ "ftp", "ftp://", DEFAULT_FTP_PORT, scm_has_params|scm_has_fragment },
|
||||
#ifdef HAVE_SSL
|
||||
/*
|
||||
* Explicit FTPS uses the same port as FTP.
|
||||
* Implicit FTPS has its own port (990), but it is disabled by default.
|
||||
*/
|
||||
{ "ftps", "ftps://", DEFAULT_FTP_PORT, scm_has_params|scm_has_fragment },
|
||||
#endif
|
||||
|
||||
/* SCHEME_INVALID */
|
||||
{ NULL, NULL, -1, 0 }
|
||||
@ -1772,7 +1779,7 @@ path_simplify (enum url_scheme scheme, char *path)
|
||||
else if (h[0] == '.' && h[1] == '.' && (h[2] == '/' || h[2] == '\0'))
|
||||
{
|
||||
/* Handle "../" by retreating the tortoise by one path
|
||||
element -- but not past beggining. */
|
||||
element -- but not past beginning. */
|
||||
if (t > beg)
|
||||
{
|
||||
/* Move backwards until T hits the beginning of the
|
||||
@ -1780,7 +1787,7 @@ path_simplify (enum url_scheme scheme, char *path)
|
||||
for (--t; t > beg && t[-1] != '/'; t--)
|
||||
;
|
||||
}
|
||||
else if (scheme == SCHEME_FTP)
|
||||
else if (scheme == SCHEME_FTP || scheme == SCHEME_FTPS)
|
||||
{
|
||||
/* If we're at the beginning, copy the "../" literally
|
||||
and move the beginning so a later ".." doesn't remove
|
||||
|
@ -36,6 +36,7 @@ as that of the covered work. */
|
||||
#define DEFAULT_HTTP_PORT 80
|
||||
#define DEFAULT_FTP_PORT 21
|
||||
#define DEFAULT_HTTPS_PORT 443
|
||||
#define DEFAULT_FTPS_IMPLICIT_PORT 990
|
||||
|
||||
/* This represents how many characters less than the OS max name length a file
|
||||
* should be. More precisely, a file name should be at most
|
||||
@ -70,6 +71,9 @@ enum url_scheme {
|
||||
SCHEME_HTTPS,
|
||||
#endif
|
||||
SCHEME_FTP,
|
||||
#ifdef HAVE_SSL
|
||||
SCHEME_FTPS,
|
||||
#endif
|
||||
SCHEME_INVALID
|
||||
};
|
||||
|
||||
|
@ -349,7 +349,8 @@ typedef enum
|
||||
FTPSRVERR, FTPRETRINT, FTPRESTFAIL, URLERROR, FOPENERR,
|
||||
FOPEN_EXCL_ERR, FWRITEERR, HEOF, GATEWAYTIMEOUT,
|
||||
HERR, RETROK, RECLEVELEXC, WRONGCODE,
|
||||
FTPINVPASV, FTPNOPASV, CONTNOTSUPPORTED, RETRUNNEEDED, RETRFINISHED,
|
||||
FTPINVPASV, FTPNOPASV, FTPNOPBSZ, FTPNOPROT, FTPNOAUTH,
|
||||
CONTNOTSUPPORTED, RETRUNNEEDED, RETRFINISHED,
|
||||
READERR, TRYLIMEXC, FILEBADFILE, RANGEERR,
|
||||
RETRBADPATTERN, PROXERR,
|
||||
AUTHFAILED, QUOTEXC, WRITEFAILED, SSLINITFAILED, VERIFCERTERR,
|
||||
|
Loading…
Reference in New Issue
Block a user