X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Fftp-basic.c;h=b3b14a7aa2c9f5d574d85f3c8d6473b58f513bdd;hp=8d433a69fe0d79fdfc5bfdc22a3920ce2f836fa0;hb=cb4003403509b46d2f6ef6936baf969906ff1430;hpb=eef4a668b754cf3a7bbee58a65c654df5d414ec6 diff --git a/src/ftp-basic.c b/src/ftp-basic.c index 8d433a69..b3b14a7a 100644 --- a/src/ftp-basic.c +++ b/src/ftp-basic.c @@ -1,37 +1,42 @@ /* Basic FTP routines. Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc. -This file is part of Wget. +This file is part of GNU Wget. -This program is free software; you can redistribute it and/or modify +GNU Wget is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. -This program is distributed in the hope that it will be useful, +GNU Wget is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software +along with Wget; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include +#include + #ifdef HAVE_STRING_H # include #else # include #endif -#include #ifdef HAVE_UNISTD_H # include #endif #include +/* For inet_ntop. */ +#include +#include + #ifdef WINDOWS # include #endif @@ -41,13 +46,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "rbuf.h" #include "connect.h" #include "host.h" - -#ifndef errno -extern int errno; -#endif -#ifndef h_errno -extern int h_errno; -#endif +#include "ftp.h" char ftp_last_respline[128]; @@ -66,33 +65,33 @@ ftp_response (struct rbuf *rbuf, char **line) do { for (i = 0; 1; i++) - { - int res; - if (i > bufsize - 1) - *line = (char *)xrealloc (*line, (bufsize <<= 1)); - res = RBUF_READCHAR (rbuf, *line + i); - /* RES is number of bytes read. */ - if (res == 1) - { - if ((*line)[i] == '\n') - { - (*line)[i] = '\0'; - /* Get rid of \r. */ - if (i > 0 && (*line)[i - 1] == '\r') - (*line)[i - 1] = '\0'; - break; - } - } - else - return FTPRERR; - } + { + int res; + if (i > bufsize - 1) + *line = (char *)xrealloc (*line, (bufsize <<= 1)); + res = RBUF_READCHAR (rbuf, *line + i); + /* RES is number of bytes read. */ + if (res == 1) + { + if ((*line)[i] == '\n') + { + (*line)[i] = '\0'; + /* Get rid of \r. */ + if (i > 0 && (*line)[i - 1] == '\r') + (*line)[i - 1] = '\0'; + break; + } + } + else + return FTPRERR; + } if (opt.server_response) - logprintf (LOG_ALWAYS, "%s\n", *line); + logprintf (LOG_ALWAYS, "%s\n", *line); else - DEBUGP (("%s\n", *line)); + DEBUGP (("%s\n", *line)); } while (!(i >= 3 && ISDIGIT (**line) && ISDIGIT ((*line)[1]) && - ISDIGIT ((*line)[2]) && (*line)[3] == ' ')); + ISDIGIT ((*line)[2]) && (*line)[3] == ' ')); strncpy (ftp_last_respline, *line, sizeof (ftp_last_respline)); ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0'; return FTPOK; @@ -105,16 +104,16 @@ static char * ftp_request (const char *command, const char *value) { char *res = (char *)xmalloc (strlen (command) - + (value ? (1 + strlen (value)) : 0) - + 2 + 1); + + (value ? (1 + strlen (value)) : 0) + + 2 + 1); sprintf (res, "%s%s%s\r\n", command, value ? " " : "", value ? value : ""); if (opt.server_response) { /* Hack: don't print out password. */ if (strncmp (res, "PASS", 4) != 0) - logprintf (LOG_ALWAYS, "--> %s\n", res); + logprintf (LOG_ALWAYS, "--> %s\n", res); else - logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n"); + logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n"); } else DEBUGP (("\n--> %s\n", res)); @@ -138,41 +137,41 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass) err = ftp_response (rbuf, &respline); if (err != FTPOK) { - free (respline); + xfree (respline); return err; } if (*respline != '2') { - free (respline); + xfree (respline); return FTPSRVERR; } - free (respline); + xfree (respline); /* Send USER username. */ request = ftp_request ("USER", acc); nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); if (nwritten < 0) { - free (request); + xfree (request); return WRITEFAILED; } - free (request); + xfree (request); /* Get appropriate response. */ err = ftp_response (rbuf, &respline); if (err != FTPOK) { - free (respline); + xfree (respline); return err; } /* An unprobable possibility of logging without a password. */ if (*respline == '2') { - free (respline); + xfree (respline); return FTPOK; } /* Else, only response 3 is appropriate. */ if (*respline != '3') { - free (respline); + xfree (respline); return FTPLOGREFUSED; } #ifdef USE_OPIE @@ -185,61 +184,117 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass) for (i = 0; i < ARRAY_SIZE (skey_head); i++) { - if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0) - break; + if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0) + break; } if (i < ARRAY_SIZE (skey_head)) { - const char *cp; - int skey_sequence = 0; - - for (cp = respline + strlen (skey_head[i]); - '0' <= *cp && *cp <= '9'; - cp++) - { - skey_sequence = skey_sequence * 10 + *cp - '0'; - } - if (*cp == ' ') - cp++; - else - { - bad: - free (respline); - return FTPLOGREFUSED; - } - if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0) - goto bad; - pass = cp; + const char *cp; + int skey_sequence = 0; + + for (cp = respline + strlen (skey_head[i]); + '0' <= *cp && *cp <= '9'; + cp++) + { + skey_sequence = skey_sequence * 10 + *cp - '0'; + } + if (*cp == ' ') + cp++; + else + { + bad: + xfree (respline); + return FTPLOGREFUSED; + } + if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0) + goto bad; + pass = cp; } } #endif /* USE_OPIE */ - free (respline); + xfree (respline); /* Send PASS password. */ request = ftp_request ("PASS", pass); nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); if (nwritten < 0) { - free (request); + xfree (request); return WRITEFAILED; } - free (request); + xfree (request); /* Get appropriate response. */ err = ftp_response (rbuf, &respline); if (err != FTPOK) { - free (respline); + xfree (respline); return err; } if (*respline != '2') { - free (respline); + xfree (respline); return FTPLOGINC; } - free (respline); + xfree (respline); /* All OK. */ return FTPOK; } +#ifdef INET6 +uerr_t +ftp_eprt (struct rbuf *rbuf) +{ + uerr_t err; + + char *request, *respline; + ip_address in_addr; + unsigned short port; + + char ipv6 [8 * (4 * 3 + 3) + 8]; + char *bytes; + + /* Setting port to 0 lets the system choose a free port. */ + port = 0; + err = bindport (&port, ip_default_family); + if (err != BINDOK) /* Bind the port. */ + return err; + + /* Get the address of this side of the connection. */ + if (!conaddr (RBUF_FD (rbuf), &in_addr)) + /* Huh? This is not BINDERR! */ + return BINDERR; + inet_ntop (AF_INET6, &in_addr, ipv6, sizeof (ipv6)); + + /* Construct the argument of EPRT (of the form |2|IPv6.ascii|PORT.ascii|). */ + bytes = alloca (3 + strlen (ipv6) + 1 + numdigit (port) + 1 + 1); + sprintf (bytes, "|2|%s|%u|", ipv6, port); + /* Send PORT request. */ + request = ftp_request ("EPRT", bytes); + if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request))) + { + closeport (port); + xfree (request); + return WRITEFAILED; + } + xfree (request); + /* Get appropriate response. */ + err = ftp_response (rbuf, &respline); + if (err != FTPOK) + { + closeport (port); + xfree (respline); + return err; + } + if (*respline != '2') + { + closeport (port); + xfree (respline); + return FTPPORTERR; + } + xfree (respline); + return FTPOK; +} +#endif + /* Bind a port and send the appropriate PORT command to the FTP server. Use acceptport after RETR, to get the socket of data connection. */ @@ -247,102 +302,218 @@ uerr_t ftp_port (struct rbuf *rbuf) { uerr_t err; - char *request, *respline, *bytes; - unsigned char *in_addr; + char *request, *respline; + char bytes[6 * 4 +1]; + + ip_address in_addr; + ip4_address in_addr_4; + unsigned char *in_addr4_ptr = (unsigned char *)&in_addr_4; + int nwritten; unsigned short port; - +#ifdef INET6 + /* + Only try the Extented Version if we actually use IPv6 + */ + if (ip_default_family == AF_INET6) + { + err = ftp_eprt (rbuf); + if (err == FTPOK) + return err; + } +#endif /* Setting port to 0 lets the system choose a free port. */ port = 0; - /* Bind the port. */ - err = bindport (&port); + + err = bindport (&port, AF_INET); if (err != BINDOK) return err; - /* Get the address of this side of the connection. */ - if (!(in_addr = conaddr (RBUF_FD (rbuf)))) - return HOSTERR; - /* Construct the argument of PORT (of the form a,b,c,d,e,f). */ - bytes = (char *)alloca (6 * 4 + 1); - sprintf (bytes, "%d,%d,%d,%d,%d,%d", in_addr[0], in_addr[1], - in_addr[2], in_addr[3], (unsigned) (port & 0xff00) >> 8, - port & 0xff); + + /* Get the address of this side of the connection and convert it + (back) to IPv4. */ + if (!conaddr (RBUF_FD (rbuf), &in_addr)) + /* Huh? This is not BINDERR! */ + return BINDERR; + if (!map_ip_to_ipv4 (&in_addr, &in_addr_4)) + return BINDERR; + + /* Construct the argument of PORT (of the form a,b,c,d,e,f). Port + is unsigned short so (unsigned) (port & 0xff000) >> 8 is the same + like port >> 8 + */ + sprintf (bytes, "%d,%d,%d,%d,%d,%d", + in_addr4_ptr[0], in_addr4_ptr[1], in_addr4_ptr[2], in_addr4_ptr[3], + port >> 8, port & 0xff); /* Send PORT request. */ request = ftp_request ("PORT", bytes); nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); if (nwritten < 0) { - free (request); + xfree (request); return WRITEFAILED; } - free (request); + xfree (request); /* Get appropriate response. */ err = ftp_response (rbuf, &respline); if (err != FTPOK) { - free (respline); + xfree (respline); return err; } if (*respline != '2') { - free (respline); + xfree (respline); return FTPPORTERR; } - free (respline); + xfree (respline); return FTPOK; } +#ifdef INET6 +uerr_t +ftp_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port, + char *typ) +{ + int err; + char *s, *respline; + char *request = ftp_request ("EPSV", typ); + if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request))) + { + xfree (request); + return WRITEFAILED; + } + /* Get the server response. */ + err = ftp_response (rbuf, &respline); + if (err != FTPOK) + { + xfree (respline); + return err; + } + if (*respline != '2') + { + xfree (respline); + return FTPNOPASV; + } + /* Parse the request. */ + s = respline; + /* respline::=229 Entering Extended Passive Mode (|||6446|) */ + for (s += 4; *s && !ISDIGIT (*s); s++); + if (!*s) + return FTPINVPASV; + *port=0; + for (; ISDIGIT (*s); s++) + *port = (*s - '0') + 10 * (*port); + xfree (respline); + /* Now we have the port but we need the IPv6 :-( */ + { + wget_sockaddr remote; + int len = sizeof (remote); + struct sockaddr_in *ipv4_sock = ( struct sockaddr_in *)&remote; + getpeername (RBUF_FD (rbuf), (struct sockaddr*)&remote, &len); + switch(remote.sa.sa_family) + { + case AF_INET6: + memcpy (addr, &remote.sin6.sin6_addr, 16); + break; + case AF_INET: + map_ipv4_to_ip ((ip4_address *)&ipv4_sock->sin_addr, addr); + break; + default: + abort(); + return FTPINVPASV; + /* realy bad */ + } + } + return FTPOK; +} +#endif + + /* Similar to ftp_port, but uses `PASV' to initiate the passive FTP transfer. Reads the response from server and parses it. Reads the host and port addresses and returns them. */ uerr_t -ftp_pasv (struct rbuf *rbuf, unsigned char *addr) +ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port) { char *request, *respline, *s; int nwritten, i; uerr_t err; + unsigned char addr4[4]; +#ifdef INET6 + if (ip_default_family == AF_INET6) + { + err = ftp_epsv (rbuf, addr, port, "2"); /* try IPv6 with EPSV */ + if (FTPOK == err) + return FTPOK; + err = ftp_epsv (rbuf, addr, port, "1"); /* try IPv4 with EPSV */ + if (FTPOK == err) + return FTPOK; + } +#endif /* Form the request. */ request = ftp_request ("PASV", NULL); /* And send it. */ nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); if (nwritten < 0) { - free (request); + xfree (request); return WRITEFAILED; } - free (request); + xfree (request); /* Get the server response. */ err = ftp_response (rbuf, &respline); if (err != FTPOK) { - free (respline); + xfree (respline); return err; } if (*respline != '2') { - free (respline); + xfree (respline); return FTPNOPASV; } /* Parse the request. */ + /* respline::=227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ s = respline; for (s += 4; *s && !ISDIGIT (*s); s++); if (!*s) return FTPINVPASV; - for (i = 0; i < 6; i++) + for (i = 0; i < 4; i++) { - addr[i] = 0; + addr4[i] = 0; for (; ISDIGIT (*s); s++) - addr[i] = (*s - '0') + 10 * addr[i]; + addr4[i] = (*s - '0') + 10 * addr4[i]; if (*s == ',') - s++; - else if (i < 5) - { - /* When on the last number, anything can be a terminator. */ - free (respline); - return FTPINVPASV; - } + s++; + else + { + xfree (respline); + return FTPINVPASV; + } } - free (respline); + + /* Eventually make an IPv4 in IPv6 adress if needed */ + map_ipv4_to_ip ((ip4_address *)addr4, addr); + + *port=0; + for (; ISDIGIT (*s); s++) + *port = (*s - '0') + 10 * (*port); + if (*s == ',') + s++; + else + { + xfree (respline); + return FTPINVPASV; + } + + { + unsigned short port2 = 0; + for (; ISDIGIT (*s); s++) + port2 = (*s - '0') + 10 * port2; + *port = (*port) * 256 + port2; + } + xfree (respline); return FTPOK; } @@ -363,23 +534,23 @@ ftp_type (struct rbuf *rbuf, int type) nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); if (nwritten < 0) { - free (request); + xfree (request); return WRITEFAILED; } - free (request); + xfree (request); /* Get appropriate response. */ err = ftp_response (rbuf, &respline); if (err != FTPOK) { - free (respline); + xfree (respline); return err; } if (*respline != '2') { - free (respline); + xfree (respline); return FTPUNKNOWNTYPE; } - free (respline); + xfree (respline); /* All OK. */ return FTPOK; } @@ -398,28 +569,28 @@ ftp_cwd (struct rbuf *rbuf, const char *dir) nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); if (nwritten < 0) { - free (request); + xfree (request); return WRITEFAILED; } - free (request); + xfree (request); /* Get appropriate response. */ err = ftp_response (rbuf, &respline); if (err != FTPOK) { - free (respline); + xfree (respline); return err; } if (*respline == '5') { - free (respline); + xfree (respline); return FTPNSFOD; } if (*respline != '2') { - free (respline); + xfree (respline); return FTPRERR; } - free (respline); + xfree (respline); /* All OK. */ return FTPOK; } @@ -431,30 +602,30 @@ ftp_rest (struct rbuf *rbuf, long offset) char *request, *respline; int nwritten; uerr_t err; - static char numbuf[20]; /* Buffer for the number */ + static char numbuf[24]; /* Buffer for the number */ - long_to_string (numbuf, offset); + number_to_string (numbuf, offset); request = ftp_request ("REST", numbuf); nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); if (nwritten < 0) { - free (request); + xfree (request); return WRITEFAILED; } - free (request); + xfree (request); /* Get appropriate response. */ err = ftp_response (rbuf, &respline); if (err != FTPOK) { - free (respline); + xfree (respline); return err; } if (*respline != '3') { - free (respline); + xfree (respline); return FTPRESTFAIL; } - free (respline); + xfree (respline); /* All OK. */ return FTPOK; } @@ -472,28 +643,28 @@ ftp_retr (struct rbuf *rbuf, const char *file) nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); if (nwritten < 0) { - free (request); + xfree (request); return WRITEFAILED; } - free (request); + xfree (request); /* Get appropriate response. */ err = ftp_response (rbuf, &respline); if (err != FTPOK) { - free (respline); + xfree (respline); return err; } if (*respline == '5') { - free (respline); + xfree (respline); return FTPNSFOD; } if (*respline != '1') { - free (respline); + xfree (respline); return FTPRERR; } - free (respline); + xfree (respline); /* All OK. */ return FTPOK; } @@ -512,28 +683,198 @@ ftp_list (struct rbuf *rbuf, const char *file) nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); if (nwritten < 0) { - free (request); + xfree (request); return WRITEFAILED; } - free (request); + xfree (request); /* Get appropriate respone. */ err = ftp_response (rbuf, &respline); if (err != FTPOK) { - free (respline); + xfree (respline); return err; } if (*respline == '5') { - free (respline); + xfree (respline); return FTPNSFOD; } if (*respline != '1') { - free (respline); + xfree (respline); return FTPRERR; } - free (respline); + xfree (respline); /* All OK. */ return FTPOK; } + +/* Sends the SYST command to the server. */ +uerr_t +ftp_syst (struct rbuf *rbuf, enum stype *server_type) +{ + char *request, *respline; + int nwritten; + uerr_t err; + + /* Send SYST request. */ + request = ftp_request ("SYST", NULL); + nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); + if (nwritten < 0) + { + xfree (request); + return WRITEFAILED; + } + xfree (request); + + /* Get appropriate response. */ + err = ftp_response (rbuf, &respline); + if (err != FTPOK) + { + xfree (respline); + return err; + } + if (*respline == '5') + { + xfree (respline); + return FTPSRVERR; + } + + /* Skip the number (215, but 200 (!!!) in case of VMS) */ + strtok (respline, " "); + + /* Which system type has been reported (we are interested just in the + first word of the server response)? */ + request = strtok (NULL, " "); + + if (!strcasecmp (request, "VMS")) + *server_type = ST_VMS; + else if (!strcasecmp (request, "UNIX")) + *server_type = ST_UNIX; + else if (!strcasecmp (request, "WINDOWS_NT")) + *server_type = ST_WINNT; + else if (!strcasecmp (request, "MACOS")) + *server_type = ST_MACOS; + else + *server_type = ST_OTHER; + + xfree (respline); + /* All OK. */ + return FTPOK; +} + +/* Sends the PWD command to the server. */ +uerr_t +ftp_pwd (struct rbuf *rbuf, char **pwd) +{ + char *request, *respline; + int nwritten; + uerr_t err; + + /* Send PWD request. */ + request = ftp_request ("PWD", NULL); + nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); + if (nwritten < 0) + { + xfree (request); + return WRITEFAILED; + } + xfree (request); + /* Get appropriate response. */ + err = ftp_response (rbuf, &respline); + if (err != FTPOK) + { + xfree (respline); + return err; + } + if (*respline == '5') + { + xfree (respline); + return FTPSRVERR; + } + + /* Skip the number (257), leading citation mark, trailing citation mark + and everything following it. */ + strtok (respline, "\""); + request = strtok (NULL, "\""); + + /* Has the `pwd' been already allocated? Free! */ + FREE_MAYBE (*pwd); + + *pwd = xstrdup (request); + + xfree (respline); + /* All OK. */ + return FTPOK; +} + +/* Sends the SIZE command to the server, and returns the value in 'size'. + * If an error occurs, size is set to zero. */ +uerr_t +ftp_size (struct rbuf *rbuf, const char *file, long int *size) +{ + char *request, *respline; + int nwritten; + uerr_t err; + + /* Send PWD request. */ + request = ftp_request ("SIZE", file); + nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request)); + if (nwritten < 0) + { + xfree (request); + *size = 0; + return WRITEFAILED; + } + xfree (request); + /* Get appropriate response. */ + err = ftp_response (rbuf, &respline); + if (err != FTPOK) + { + xfree (respline); + *size = 0; + return err; + } + if (*respline == '5') + { + /* + * Probably means SIZE isn't supported on this server. + * Error is nonfatal since SIZE isn't in RFC 959 + */ + xfree (respline); + *size = 0; + return FTPOK; + } + + errno = 0; + *size = strtol (respline + 4, NULL, 0); + if (errno) + { + /* + * Couldn't parse the response for some reason. On the (few) + * tests I've done, the response is 213 with nothing else - + * maybe something a bit more resilient is necessary. It's not a + * fatal error, however. + */ + xfree (respline); + *size = 0; + return FTPOK; + } + + xfree (respline); + /* All OK. */ + return FTPOK; +} + +/* If URL's params are of the form "type=X", return character X. + Otherwise, return 'I' (the default type). */ +char +ftp_process_type (const char *params) +{ + if (params + && 0 == strncasecmp (params, "type=", 5) + && params[5] != '\0') + return TOUPPER (params[5]); + else + return 'I'; +}