/* Variables shared by bindport and acceptport: */
static int msock = -1;
-static struct sockaddr *addr;
+/*static struct sockaddr *addr;*/
-static ip_address bind_address;
-static int bind_address_resolved;
-
-static void
-resolve_bind_address (void)
+static int
+resolve_bind_address (int flags, ip_address *addr)
{
- struct address_list *al;
+ struct address_list *al = NULL;
+ int bind_address_resolved = 0;
- if (bind_address_resolved || opt.bind_address == NULL)
- /* Nothing to do. */
- return;
+ if (opt.bind_address != NULL)
+ {
+ al = lookup_host (opt.bind_address, flags | LH_SILENT | LH_PASSIVE);
+
+ if (al == NULL)
+ {
+ logprintf (LOG_NOTQUIET,
+ _("Unable to convert `%s' to a bind address. Reverting to ANY.\n"),
+ opt.bind_address);
+ }
+ else
+ bind_address_resolved = 1;
+ }
- al = lookup_host (opt.bind_address, 1);
- if (!al)
+ if (al == NULL)
{
- logprintf (LOG_NOTQUIET,
- _("Unable to convert `%s' to a bind address. Reverting to ANY.\n"),
- opt.bind_address);
- return;
+ const char *unspecified_address = "0.0.0.0";
+#ifdef ENABLE_IPV6
+ if (flags & BIND_ON_IPV6_ONLY)
+ unspecified_address = "::";
+#endif
+ al = lookup_host (unspecified_address, LH_SILENT | LH_PASSIVE);
}
- address_list_copy_one (al, 0, &bind_address);
+ assert (al != NULL);
+
+ address_list_copy_one (al, 0, addr);
address_list_release (al);
- bind_address_resolved = 1;
+
+ return bind_address_resolved;
}
\f
struct cwt_context {
int
connect_to_one (ip_address *addr, unsigned short port, int silent)
{
- wget_sockaddr sa;
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
int sock, save_errno;
/* Set port and protocol */
- wget_sockaddr_set_address (&sa, ip_default_family, port, addr);
+ sockaddr_set_address (sa, port, addr);
if (!silent)
{
- char *pretty_addr = pretty_print_address (addr);
+ const char *pretty_addr = pretty_print_address (addr);
if (connection_host_name
&& 0 != strcmp (connection_host_name, pretty_addr))
logprintf (LOG_VERBOSE, _("Connecting to %s[%s]:%hu... "),
}
/* Make an internet socket, stream type. */
- sock = socket (ip_default_family, SOCK_STREAM, 0);
+ sock = socket (sa->sa_family, SOCK_STREAM, 0);
if (sock < 0)
goto out;
`--post-file', also set SO_SNDBUF here. */
}
- resolve_bind_address ();
- if (bind_address_resolved)
+ if (opt.bind_address)
{
/* Bind the client side to the requested address. */
- wget_sockaddr bsa;
- wget_sockaddr_set_address (&bsa, ip_default_family, 0, &bind_address);
- if (bind (sock, &bsa.sa, sockaddr_len ()))
- {
- CLOSE (sock);
- sock = -1;
- goto out;
+ ip_address bind_address;
+ if (resolve_bind_address (0, &bind_address))
+ {
+ struct sockaddr_storage bss;
+ struct sockaddr *bsa = (struct sockaddr *)&bss;
+ sockaddr_set_address (bsa, 0, &bind_address);
+ if (bind (sock, bsa, sockaddr_len (bsa)))
+ {
+ CLOSE (sock);
+ sock = -1;
+ goto out;
+ }
}
}
/* Connect the socket to the remote host. */
- if (connect_with_timeout (sock, &sa.sa, sockaddr_len (),
+ if (connect_with_timeout (sock, sa, sockaddr_len (sa),
opt.connect_timeout) < 0)
{
CLOSE (sock);
chosen by the system, and its value is stored to *PORT. The
internal variable MPORT is set to the value of the ensuing master
socket. Call acceptport() to block for and accept a connection. */
+
uerr_t
-bindport (unsigned short *port, int family)
+bindport (const ip_address *bind_address, unsigned short *port)
{
- int optval = 1;
- wget_sockaddr srv;
- memset (&srv, 0, sizeof (wget_sockaddr));
+ int family = AF_INET;
+ int optval;
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ memset (&ss, 0, sizeof (ss));
msock = -1;
+#ifdef ENABLE_IPV6
+ if (bind_address->type == IPv6_ADDRESS)
+ family = AF_INET6;
+#endif
+
if ((msock = socket (family, SOCK_STREAM, 0)) < 0)
return CONSOCKERR;
#ifdef SO_REUSEADDR
+ optval = 1;
if (setsockopt (msock, SOL_SOCKET, SO_REUSEADDR,
(char *)&optval, sizeof (optval)) < 0)
return CONSOCKERR;
#endif
- resolve_bind_address ();
- wget_sockaddr_set_address (&srv, ip_default_family, htons (*port),
- bind_address_resolved ? &bind_address : NULL);
- if (bind (msock, &srv.sa, sockaddr_len ()) < 0)
+#ifdef ENABLE_IPV6
+# ifdef HAVE_IPV6_V6ONLY
+ if (family == AF_INET6)
+ {
+ optval = 1;
+ /* if setsockopt fails, go on anyway */
+ setsockopt (msock, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *)&optval, sizeof (optval));
+ }
+# endif
+#endif
+
+ sockaddr_set_address (sa, htons (*port), bind_address);
+ if (bind (msock, sa, sockaddr_len (sa)) < 0)
{
CLOSE (msock);
msock = -1;
DEBUGP (("Master socket fd %d bound.\n", msock));
if (!*port)
{
- socklen_t sa_len = sockaddr_len ();
- if (getsockname (msock, &srv.sa, &sa_len) < 0)
+ socklen_t sa_len = sockaddr_len (sa);
+ if (getsockname (msock, sa, &sa_len) < 0)
{
CLOSE (msock);
msock = -1;
return CONPORTERR;
}
- *port = wget_sockaddr_get_port (&srv);
- DEBUGP (("using port %i.\n", *port));
+ *port = sockaddr_get_port (sa);
+ DEBUGP (("binding to address %s using port %i.\n",
+ pretty_print_address (bind_address), *port));
}
if (listen (msock, 1) < 0)
{
uerr_t
acceptport (int *sock)
{
- socklen_t addrlen = sockaddr_len ();
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ socklen_t addrlen = sizeof (ss);
#ifdef HAVE_SELECT
if (select_fd (msock, opt.connect_timeout, 0) <= 0)
return ACCEPTERR;
#endif
- if ((*sock = accept (msock, addr, &addrlen)) < 0)
+ if ((*sock = accept (msock, sa, &addrlen)) < 0)
return ACCEPTERR;
DEBUGP (("Created socket fd %d.\n", *sock));
return ACCEPTOK;
int
conaddr (int fd, ip_address *ip)
{
- wget_sockaddr mysrv;
- socklen_t addrlen = sizeof (mysrv);
- if (getsockname (fd, &mysrv.sa, &addrlen) < 0)
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ socklen_t addrlen = sizeof (ss);
+
+ if (getsockname (fd, sa, &addrlen) < 0)
return 0;
- switch (mysrv.sa.sa_family)
+ switch (sa->sa_family)
{
#ifdef ENABLE_IPV6
case AF_INET6:
- memcpy (ip, &mysrv.sin6.sin6_addr, 16);
+ ip->type = IPv6_ADDRESS;
+ ip->addr.ipv6.addr = ((struct sockaddr_in6 *)sa)->sin6_addr;
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ ip->addr.ipv6.scope_id = ((struct sockaddr_in6 *)sa)->sin6_scope_id;
+#endif
+ DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
return 1;
#endif
case AF_INET:
- map_ipv4_to_ip ((ip4_address *)&mysrv.sin.sin_addr, ip);
+ ip->type = IPv4_ADDRESS;
+ ip->addr.ipv4.addr = ((struct sockaddr_in *)sa)->sin_addr;
+ DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
return 1;
default:
abort ();
}
+
return 0;
}
#include "host.h"
+/* bindport flags */
+#define BIND_ON_IPV4_ONLY LH_IPv4_ONLY
+#ifdef ENABLE_IPV6
+#define BIND_ON_IPV6_ONLY LH_IPv6_ONLY
+#endif /* ENABLE_IPV6 */
+
/* Function declarations */
int connect_to_one PARAMS ((ip_address *, unsigned short, int));
int test_socket_open PARAMS ((int));
int select_fd PARAMS ((int, double, int));
-uerr_t bindport PARAMS ((unsigned short *, int));
+uerr_t bindport PARAMS ((const ip_address *, unsigned short *));
uerr_t acceptport PARAMS ((int *));
void closeport PARAMS ((int));
int conaddr PARAMS ((int, ip_address *));
#include <config.h>
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
if (strncmp (res, "PASS", 4) != 0)
logprintf (LOG_ALWAYS, "--> %s\n", res);
else
- logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
+ logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n\n");
}
else
DEBUGP (("\n--> %s\n", res));
return FTPOK;
}
-#ifdef ENABLE_IPV6
+static void
+ip_address_to_port_repr (const ip_address *addr, unsigned short port, char *buf,
+ size_t buflen)
+{
+ unsigned char *ptr;
+
+ assert (addr != NULL);
+ assert (addr->type == IPv4_ADDRESS);
+ assert (buf != NULL);
+ /* buf must contain the argument of PORT (of the form a,b,c,d,e,f). */
+ assert (buflen >= 6 * 4);
+
+ ptr = (unsigned char *)(&addr->addr.ipv4.addr.s_addr);
+ snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d", ptr[0], ptr[1],
+ ptr[2], ptr[3], (unsigned) (port & 0xff00) >> 8, port & 0xff);
+ buf[buflen - 1] = '\0';
+}
+
+/* Bind a port and send the appropriate PORT command to the FTP
+ server. Use acceptport after RETR, to get the socket of data
+ connection. */
uerr_t
-ftp_eprt (struct rbuf *rbuf)
+ftp_port (struct rbuf *rbuf)
{
uerr_t err;
-
char *request, *respline;
- ip_address in_addr;
+ ip_address addr;
+ int nwritten;
unsigned short port;
+ /* Must contain the argument of PORT (of the form a,b,c,d,e,f). */
+ char bytes[6 * 4 + 1];
+
+ assert (rbuf != NULL);
+ assert (rbuf_initialized_p (rbuf));
+
+ /* Get the address of this side of the connection. */
+ if (!conaddr (RBUF_FD (rbuf), &addr))
+ return BINDERR;
- char ipv6 [8 * (4 * 3 + 3) + 8];
- char *bytes;
+ assert (addr.type == IPv4_ADDRESS);
/* 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. */
+
+ /* Bind the port. */
+ err = bindport (&addr, &port);
+ if (err != BINDOK)
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 PORT (of the form a,b,c,d,e,f). */
+ ip_address_to_port_repr (&addr, port, bytes, sizeof (bytes));
- /* 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)))
+ request = ftp_request ("PORT", bytes);
+ nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+ if (nwritten < 0)
{
- closeport (port);
xfree (request);
+ closeport (-1);
return WRITEFAILED;
}
xfree (request);
+
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- closeport (port);
xfree (respline);
+ closeport (-1);
return err;
}
if (*respline != '2')
{
- closeport (port);
xfree (respline);
+ closeport (-1);
return FTPPORTERR;
}
xfree (respline);
return FTPOK;
}
-#endif
+
+#ifdef ENABLE_IPV6
+static void
+ip_address_to_lprt_repr (const ip_address *addr, unsigned short port, char *buf,
+ size_t buflen)
+{
+ unsigned char *ptr;
+
+ assert (addr != NULL);
+ assert (addr->type == IPv4_ADDRESS || addr->type == IPv6_ADDRESS);
+ assert (buf != NULL);
+ /* buf must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
+ assert (buflen >= 21 * 4);
+
+ /* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
+ switch (addr->type)
+ {
+ case IPv4_ADDRESS:
+ ptr = (unsigned char *)(&addr->addr.ipv4.addr);
+ snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d", 4, 4,
+ ptr[0], ptr[1], ptr[2], ptr[3], 2,
+ (unsigned) (port & 0xff00) >> 8, port & 0xff);
+ buf[buflen - 1] = '\0';
+ break;
+ case IPv6_ADDRESS:
+ ptr = (unsigned char *)(&addr->addr.ipv6.addr);
+ snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
+ 6, 16, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7],
+ ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15], 2,
+ (unsigned) (port & 0xff00) >> 8, port & 0xff);
+ buf[buflen - 1] = '\0';
+ break;
+ }
+}
/* Bind a port and send the appropriate PORT command to the FTP
server. Use acceptport after RETR, to get the socket of data
connection. */
uerr_t
-ftp_port (struct rbuf *rbuf)
+ftp_lprt (struct rbuf *rbuf)
{
uerr_t err;
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;
-
+ ip_address addr;
int nwritten;
unsigned short port;
-#ifdef ENABLE_IPV6
- /*
- Only try the Extented Version if we actually use IPv6
- */
- if (ip_default_family == AF_INET6)
+ /* Must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
+ char bytes[21 * 4 + 1];
+
+ assert (rbuf != NULL);
+ assert (rbuf_initialized_p (rbuf));
+
+ /* Get the address of this side of the connection. */
+ if (!conaddr (RBUF_FD (rbuf), &addr))
+ return BINDERR;
+
+ assert (addr.type == IPv4_ADDRESS || addr.type == IPv6_ADDRESS);
+
+ /* Setting port to 0 lets the system choose a free port. */
+ port = 0;
+
+ /* Bind the port. */
+ err = bindport (&addr, &port);
+ if (err != BINDOK)
+ return err;
+
+ /* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
+ ip_address_to_lprt_repr (&addr, port, bytes, sizeof (bytes));
+
+ /* Send PORT request. */
+ request = ftp_request ("LPRT", bytes);
+ nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+ if (nwritten < 0)
{
- err = ftp_eprt (rbuf);
- if (err == FTPOK)
- return err;
+ xfree (request);
+ closeport (-1);
+ return WRITEFAILED;
}
-#endif
+ xfree (request);
+ /* Get appropriate response. */
+ err = ftp_response (rbuf, &respline);
+ if (err != FTPOK)
+ {
+ xfree (respline);
+ closeport (-1);
+ return err;
+ }
+ if (*respline != '2')
+ {
+ xfree (respline);
+ closeport (-1);
+ return FTPPORTERR;
+ }
+ xfree (respline);
+ return FTPOK;
+}
+
+static void
+ip_address_to_eprt_repr (const ip_address *addr, unsigned short port, char *buf,
+ size_t buflen)
+{
+ int afnum;
+
+ assert (addr != NULL);
+ assert (addr->type == IPv4_ADDRESS || addr->type == IPv6_ADDRESS);
+ assert (buf != NULL);
+ /* buf must contain the argument of EPRT (of the form |af|addr|port|).
+ * 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr
+ * 1 char for af (1-2) and 5 chars for port (0-65535) */
+ assert (buflen >= 4 + INET6_ADDRSTRLEN + 1 + 5);
+
+ /* Construct the argument of EPRT (of the form |af|addr|port|). */
+ afnum = (addr->type == IPv4_ADDRESS ? 1 : 2);
+ snprintf (buf, buflen, "|%d|%s|%d|", afnum, pretty_print_address (addr), port);
+ buf[buflen - 1] = '\0';
+}
+
+/* Bind a port and send the appropriate PORT command to the FTP
+ server. Use acceptport after RETR, to get the socket of data
+ connection. */
+uerr_t
+ftp_eprt (struct rbuf *rbuf)
+{
+ uerr_t err;
+ char *request, *respline;
+ ip_address addr;
+ int nwritten;
+ unsigned short port;
+ /* Must contain the argument of EPRT (of the form |af|addr|port|).
+ * 4 chars for the | separators, ENABLE_IPV6_ADDRSTRLEN chars for addr
+ * 1 char for af (1-2) and 5 chars for port (0-65535) */
+ char bytes[4 + INET6_ADDRSTRLEN + 1 + 5 + 1];
+
+ assert (rbuf != NULL);
+ assert (rbuf_initialized_p(rbuf));
+
+ /* Get the address of this side of the connection. */
+ if (!conaddr (RBUF_FD (rbuf), &addr))
+ return BINDERR;
+
+ assert (addr.type == IPv4_ADDRESS || addr.type == IPv6_ADDRESS);
+
/* Setting port to 0 lets the system choose a free port. */
port = 0;
- err = bindport (&port, AF_INET);
+ /* Bind the port. */
+ err = bindport (&addr, &port);
if (err != BINDOK)
return err;
- /* 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 LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
+ ip_address_to_eprt_repr (&addr, port, bytes, sizeof (bytes));
- /* 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);
+ request = ftp_request ("EPRT", bytes);
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
xfree (request);
+ closeport (-1);
return WRITEFAILED;
}
xfree (request);
if (err != FTPOK)
{
xfree (respline);
+ closeport (-1);
return err;
}
if (*respline != '2')
{
xfree (respline);
+ closeport (-1);
return FTPPORTERR;
}
xfree (respline);
return FTPOK;
}
+#endif
-#ifdef ENABLE_IPV6
+/* 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_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port,
- char *typ)
+ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
{
- int err;
- char *s, *respline;
- char *request = ftp_request ("EPSV", typ);
- if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
+ char *request, *respline, *s;
+ int nwritten, i;
+ uerr_t err;
+ unsigned char tmp[6];
+
+ assert (rbuf != NULL);
+ assert (rbuf_initialized_p(rbuf));
+ assert (addr != NULL);
+ assert (port != NULL);
+
+ memset (addr, 0, sizeof (ip_address));
+
+ /* Form the request. */
+ request = ftp_request ("PASV", NULL);
+ /* And send it. */
+ nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+ if (nwritten < 0)
{
xfree (request);
return WRITEFAILED;
}
+ xfree (request);
/* Get the server response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
}
/* 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);
+ for (i = 0; i < 6; i++)
+ {
+ tmp[i] = 0;
+ for (; ISDIGIT (*s); s++)
+ tmp[i] = (*s - '0') + 10 * tmp[i];
+ if (*s == ',')
+ s++;
+ else if (i < 5)
+ {
+ /* When on the last number, anything can be a terminator. */
+ xfree (respline);
+ return FTPINVPASV;
+ }
+ }
xfree (respline);
- /* Now we have the port but we need the IPv6 :-( */
- {
- wget_sockaddr remote;
- socklen_t addrlen = sizeof (remote);
- struct sockaddr_in *ipv4_sock = (struct sockaddr_in *)&remote;
- getpeername (RBUF_FD (rbuf), (struct sockaddr *)&remote, &addrlen);
- 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 */
- }
- }
+
+ addr->type = IPv4_ADDRESS;
+ /* Mauro Tortonesi: is this safe and/or elegant enough? */
+ memcpy (&addr->addr.ipv4.addr, tmp, 4);
+ *port = ((tmp[4] << 8) & 0xff00) + tmp[5];
+
return FTPOK;
}
-#endif
-
-/* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
+#ifdef ENABLE_IPV6
+/* Similar to ftp_lprt, but uses `LPSV' 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, ip_address *addr, unsigned short *port)
+ftp_lpsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
{
char *request, *respline, *s;
- int nwritten, i;
+ int nwritten, i, af, addrlen, portlen;
uerr_t err;
- unsigned char addr4[4];
+ unsigned char tmp[16];
+ unsigned char tmpprt[2];
+
+ assert (rbuf != NULL);
+ assert (rbuf_initialized_p(rbuf));
+ assert (addr != NULL);
+ assert (port != NULL);
+
+ memset (addr, 0, sizeof (ip_address));
-#ifdef ENABLE_IPV6
- 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);
+ request = ftp_request ("LPSV", NULL);
+
/* And send it. */
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
return WRITEFAILED;
}
xfree (request);
+
/* Get the server response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
xfree (respline);
return FTPNOPASV;
- }
- /* Parse the request. */
- /* respline::=227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
+ }
+
+ /* Parse the response. */
s = respline;
for (s += 4; *s && !ISDIGIT (*s); s++);
if (!*s)
return FTPINVPASV;
- for (i = 0; i < 4; i++)
+
+ /* First, get the address family */
+ af = 0;
+ for (; ISDIGIT (*s); s++)
+ af = (*s - '0') + 10 * af;
+
+ if (af != 4 && af != 6)
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ if (!*s || *s++ != ',')
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ /* Then, get the address length */
+ addrlen = 0;
+ for (; ISDIGIT (*s); s++)
+ addrlen = (*s - '0') + 10 * addrlen;
+
+ if (!*s || *s++ != ',')
{
- addr4[i] = 0;
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ if (addrlen > 16)
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ if ((af == 4 && addrlen != 4)
+ || (af == 6 && addrlen != 16))
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ /* Now, we get the actual address */
+ for (i = 0; i < addrlen; i++)
+ {
+ tmp[i] = 0;
for (; ISDIGIT (*s); s++)
- addr4[i] = (*s - '0') + 10 * addr4[i];
+ tmp[i] = (*s - '0') + 10 * tmp[i];
if (*s == ',')
s++;
else
}
}
- /* Eventually make an IPv4 in IPv6 adress if needed */
- map_ipv4_to_ip ((ip4_address *)addr4, addr);
+ /* Now, get the port length */
+ portlen = 0;
+ for (; ISDIGIT (*s); s++)
+ portlen = (*s - '0') + 10 * portlen;
- *port=0;
+ if (!*s || *s++ != ',')
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ if (portlen > 2)
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ /* Finally, we get the port number */
+ tmpprt[0] = 0;
for (; ISDIGIT (*s); s++)
- *port = (*s - '0') + 10 * (*port);
- if (*s == ',')
- s++;
+ tmpprt[0] = (*s - '0') + 10 * tmpprt[0];
+
+ if (!*s || *s++ != ',')
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ tmpprt[1] = 0;
+ for (; ISDIGIT (*s); s++)
+ tmpprt[1] = (*s - '0') + 10 * tmpprt[1];
+
+ assert (s != NULL);
+
+ if (af == 4)
+ {
+ addr->type = IPv4_ADDRESS;
+ memcpy (&addr->addr.ipv4.addr, tmp, 4);
+ *port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1];
+ DEBUGP (("lpsv addr is: %s\n", pretty_print_address(addr)));
+ DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0]));
+ DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1]));
+ DEBUGP (("*port is: %d\n", *port));
+ }
else
+ {
+ assert (af == 6);
+ addr->type = IPv6_ADDRESS;
+ memcpy (&addr->addr.ipv6.addr, tmp, 16);
+ *port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1];
+ DEBUGP (("lpsv addr is: %s\n", pretty_print_address(addr)));
+ DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0]));
+ DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1]));
+ DEBUGP (("*port is: %d\n", *port));
+ }
+
+ xfree (respline);
+ return FTPOK;
+}
+
+/* Similar to ftp_eprt, but uses `EPSV' 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_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
+{
+ char *request, *respline, *start, delim, *s;
+ int nwritten, i;
+ uerr_t err;
+ unsigned short tport;
+ socklen_t addrlen;
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+
+ assert (rbuf != NULL);
+ assert (rbuf_initialized_p(rbuf));
+ assert (addr != NULL);
+ assert (port != NULL);
+
+ addrlen = sizeof (ss);
+ if (getpeername (rbuf->fd, sa, &addrlen) < 0)
+ /* Mauro Tortonesi: HOW DO WE HANDLE THIS ERROR? */
+ return CONPORTERR;
+
+ assert (sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+
+ sockaddr_get_address (sa, NULL, addr);
+
+ /* Form the request. */
+ /* EPSV 1 means that we ask for IPv4 and EPSV 2 means that we ask for IPv6. */
+ request = ftp_request ("EPSV", (sa->sa_family == AF_INET ? "1" : "2"));
+
+ /* And send it. */
+ nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+ if (nwritten < 0)
+ {
+ xfree (request);
+ return WRITEFAILED;
+ }
+ xfree (request);
+
+ /* Get the server response. */
+ err = ftp_response (rbuf, &respline);
+ if (err != FTPOK)
+ {
+ xfree (respline);
+ return err;
+ }
+ if (*respline != '2')
+ {
+ xfree (respline);
+ return FTPNOPASV;
+ }
+
+ assert (respline != NULL);
+
+ DEBUGP(("respline is %s\n", respline));
+
+ /* Parse the response. */
+ s = respline;
+
+ /* Skip the useless stuff and get what's inside the parentheses */
+ start = strchr (respline, '(');
+ if (start == NULL)
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ /* Skip the first two void fields */
+ s = start + 1;
+ delim = *s++;
+ if (delim < 33 || delim > 126)
{
xfree (respline);
return FTPINVPASV;
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ if (*s++ != delim)
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
}
- {
- unsigned short port2 = 0;
- for (; ISDIGIT (*s); s++)
- port2 = (*s - '0') + 10 * port2;
- *port = (*port) * 256 + port2;
- }
+ /* Finally, get the port number */
+ tport = 0;
+ for (i = 1; ISDIGIT (*s); s++)
+ {
+ if (i > 5)
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+ tport = (*s - '0') + 10 * tport;
+ }
+
+ /* Make sure that the response terminates correcty */
+ if (*s++ != delim)
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ if (*s++ != ')')
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+
+ *port = tport;
+
xfree (respline);
return FTPOK;
}
+#endif
/* Sends the TYPE request to the server. */
uerr_t
/* 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, " ");
and everything following it. */
strtok (respline, "\"");
request = strtok (NULL, "\"");
-
+
/* Has the `pwd' been already allocated? Free! */
FREE_MAYBE (*pwd);
return res;
}
+#ifdef ENABLE_IPV6
+static int
+getfamily (int fd)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ socklen_t len = sizeof (ss);
+
+ assert (fd >= 0);
+
+ if (getpeername (fd, sa, &len) < 0)
+ /* Mauro Tortonesi: HOW DO WE HANDLE THIS ERROR? */
+ abort ();
+
+ return sa->sa_family;
+}
+
+/*
+ * This function sets up a passive data connection with the FTP server.
+ * It is merely a wrapper around ftp_epsv, ftp_lpsv and ftp_pasv.
+ */
+static uerr_t
+ftp_do_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
+{
+ uerr_t err;
+ int family;
+
+ family = getfamily (rbuf->fd);
+ assert (family == AF_INET || family == AF_INET6);
+
+ /* If our control connection is over IPv6, then we first try EPSV and then
+ * LPSV if the former is not supported. If the control connection is over
+ * IPv4, we simply issue the good old PASV request. */
+ if (family == AF_INET6)
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> EPSV ... ");
+ err = ftp_epsv (rbuf, addr, port);
+
+ /* If EPSV is not supported try LPSV */
+ if (err == FTPNOPASV)
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> LPSV ... ");
+ err = ftp_lpsv (rbuf, addr, port);
+ }
+ }
+ else
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> PASV ... ");
+ err = ftp_pasv (rbuf, addr, port);
+ }
+
+ return err;
+}
+
+/*
+ * This function sets up an active data connection with the FTP server.
+ * It is merely a wrapper around ftp_eprt, ftp_lprt and ftp_port.
+ */
+static uerr_t
+ftp_do_port (struct rbuf *rbuf)
+{
+ uerr_t err;
+ int family;
+
+ assert (rbuf != NULL);
+ assert (rbuf_initialized_p (rbuf));
+
+ family = getfamily (rbuf->fd);
+ assert (family == AF_INET || family == AF_INET6);
+
+ /* If our control connection is over IPv6, then we first try EPRT and then
+ * LPRT if the former is not supported. If the control connection is over
+ * IPv4, we simply issue the good old PORT request. */
+ if (family == AF_INET6)
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> EPRT ... ");
+ err = ftp_eprt (rbuf);
+
+ /* If EPRT is not supported try LPRT */
+ if (err == FTPPORTERR)
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> LPRT ... ");
+ err = ftp_lprt (rbuf);
+ }
+ }
+ else
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> PORT ... ");
+ err = ftp_port (rbuf);
+ }
+
+ return err;
+}
+#else
+#define ftp_do_pasv ftp_pasv
+#define ftp_do_port ftp_port
+#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. */
unsigned short passive_port;
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> PASV ... ");
- err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port);
+ err = ftp_do_pasv (&con->rbuf, &passive_addr, &passive_port);
/* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
switch (err)
{
} /* switch(err) */
if (err==FTPOK)
{
+ DEBUGP (("trying to connect to %s port %d\n",
+ pretty_print_address (&passive_addr),
+ passive_port));
dtsock = connect_to_one (&passive_addr, passive_port, 1);
if (dtsock < 0)
{
int save_errno = errno;
CLOSE (csock);
rbuf_uninitialize (&con->rbuf);
- logprintf (LOG_VERBOSE, _("couldn't connect to %s:%hu: %s\n"),
+ logprintf (LOG_VERBOSE, _("couldn't connect to %s port %hu: %s\n"),
pretty_print_address (&passive_addr), passive_port,
strerror (save_errno));
return CONNECT_ERROR (save_errno);
{
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> PORT ... ");
- err = ftp_port (&con->rbuf);
+ err = ftp_do_port (&con->rbuf);
/* FTPRERR, WRITEFAILED, bindport (CONSOCKERR, CONPORTERR, BINDERR,
LISTENERR), HOSTERR, FTPPORTERR */
switch (err)
logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
}
}
+
err = ftp_retr (&con->rbuf, u->file);
/* FTPRERR, WRITEFAILED, FTPNSFOD */
switch (err)
if (flush_res == EOF)
res = -2;
}
+
/* If get_contents couldn't write to fp, bail out. */
if (res == -2)
{
logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
}
}
-
+
/* Restore the original leave-pendingness. */
if (orig_lp)
con->cmd |= LEAVE_PENDING;
uerr_t ftp_port PARAMS ((struct rbuf *));
uerr_t ftp_pasv PARAMS ((struct rbuf *, ip_address *, unsigned short *));
#ifdef ENABLE_IPV6
-uerr_t ftp_epsv PARAMS ((struct rbuf *, ip_address *, unsigned short *,
- char *));
+uerr_t ftp_lprt PARAMS ((struct rbuf *));
+uerr_t ftp_lpsv PARAMS ((struct rbuf *, ip_address *, unsigned short *));
+uerr_t ftp_eprt PARAMS ((struct rbuf *));
+uerr_t ftp_epsv PARAMS ((struct rbuf *, ip_address *, unsigned short *));
#endif
uerr_t ftp_type PARAMS ((struct rbuf *, int));
uerr_t ftp_cwd PARAMS ((struct rbuf *, const char *));
#ifdef WINDOWS
# include <winsock.h>
-# define SET_H_ERRNO(err) WSASetLastError(err)
+# define SET_H_ERRNO(err) WSASetLastError (err)
#else
# include <sys/socket.h>
# include <netinet/in.h>
#endif
#ifdef ENABLE_IPV6
-int ip_default_family = AF_INET6;
+int ip_default_family = AF_UNSPEC;
#else
-int ip_default_family = AF_INET;
+int ip_default_family = AF_INET;
#endif
/* Mapping between known hosts and to lists of their addresses. */
/* Get the bounds of the address list. */
void
-address_list_get_bounds (struct address_list *al, int *start, int *end)
+address_list_get_bounds (const struct address_list *al, int *start, int *end)
{
*start = al->faulty;
*end = al->count;
/* Copy address number INDEX to IP_STORE. */
void
-address_list_copy_one (struct address_list *al, int index, ip_address *ip_store)
+address_list_copy_one (const struct address_list *al, int index, ip_address *ip_store)
{
assert (index >= al->faulty && index < al->count);
memcpy (ip_store, al->addresses + index, sizeof (ip_address));
/* Check whether two address lists have all their IPs in common. */
int
-address_list_match_all (struct address_list *al1, struct address_list *al2)
+address_list_match_all (const struct address_list *al1, const struct address_list *al2)
{
+ int i;
if (al1 == al2)
return 1;
if (al1->count != al2->count)
return 0;
- return 0 == memcmp (al1->addresses, al2->addresses,
- al1->count * sizeof (ip_address));
+ for (i = 0; i < al1->count; ++i)
+ {
+#ifdef ENABLE_IPv6
+ if (al1->addresses[i].type != al2->addresses[i].type)
+ return 0;
+ if (al1->addresses[i].type == IPv6_ADDRESS)
+ {
+ const struct in6_addr *addr1 = &al1->addresses[i].addr.ipv6.addr;
+ const struct in6_addr *addr2 = &al2->addresses[i].addr.ipv6.addr;
+
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ if ((al1->addresses[i].address.scope_id
+ != al2->addresses[i].address.scope_id)
+ || !IN6_ARE_ADDR_EQUAL (addr1, addr2))
+#else
+ if (!IN6_ARE_ADDR_EQUAL (addr1, addr2))
+#endif
+ return 0;
+ }
+ else
+#endif
+ {
+ const struct in_addr *addr1 = (const struct in_addr *)&al1->addresses[i].addr.ipv4.addr;
+ const struct in_addr *addr2 = (const struct in_addr *)&al2->addresses[i].addr.ipv4.addr;
+
+ if (addr1->s_addr != addr2->s_addr)
+ return 0;
+ }
+ }
+ return 1;
}
/* Mark the INDEXth element of AL as faulty, so that the next time
al->faulty = 0;
}
-#ifdef HAVE_GETADDRINFO
+#ifdef ENABLE_IPV6
/**
* address_list_from_addrinfo
*
* address_list* New allocated address_list
*/
static struct address_list *
-address_list_from_addrinfo (struct addrinfo *ai)
+address_list_from_addrinfo (const struct addrinfo *ai)
{
struct address_list *al;
- struct addrinfo *ai_head = ai;
+ const struct addrinfo *ptr;
int cnt = 0;
int i;
- for (ai = ai_head; ai; ai = ai->ai_next)
- if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6)
+ for (ptr = ai; ptr != NULL ; ptr = ptr->ai_next)
+ if (ptr->ai_family == AF_INET || ptr->ai_family == AF_INET6)
++cnt;
if (cnt == 0)
return NULL;
al->faulty = 0;
al->refcount = 1;
- for (i = 0, ai = ai_head; ai; ai = ai->ai_next)
- if (ai->ai_family == AF_INET6)
+ for (i = 0, ptr = ai; ptr != NULL; ptr = ptr->ai_next)
+ if (ptr->ai_family == AF_INET6)
{
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
- memcpy (al->addresses + i, &sin6->sin6_addr, 16);
+ const struct sockaddr_in6 *sin6 =
+ (const struct sockaddr_in6 *)ptr->ai_addr;
+ al->addresses[i].addr.ipv6.addr = sin6->sin6_addr;
+ al->addresses[i].type = IPv6_ADDRESS;
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ al->addresses[i].addr.ipv6.scope_id = sin6->sin6_scope_id;
+#endif
++i;
}
- else if (ai->ai_family == AF_INET)
+ else if (ptr->ai_family == AF_INET)
{
- struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
- map_ipv4_to_ip ((ip4_address *)&sin->sin_addr, al->addresses + i);
+ const struct sockaddr_in *sin =
+ (const struct sockaddr_in *)ptr->ai_addr;
+ al->addresses[i].addr.ipv4.addr = sin->sin_addr;
+ al->addresses[i].type = IPv4_ADDRESS;
++i;
}
assert (i == cnt);
al->addresses = xmalloc (count * sizeof (ip_address));
al->refcount = 1;
- for (i = 0; i < count; i++)
- map_ipv4_to_ip ((ip4_address *)h_addr_list[i], al->addresses + i);
+ for (i = 0; i < count; i++) {
+ /* Mauro Tortonesi: is this safe? */
+ memcpy (&((al->addresses + i)->addr.ipv4.addr.s_addr), h_addr_list[i], 4);
+ (al->addresses + i)->type = IPv4_ADDRESS;
+ }
return al;
}
-#endif
/* Like address_list_from_vector, but initialized with a single
address. */
static struct address_list *
-address_list_from_single (ip_address *addr)
+address_list_from_single (const ip_address *addr)
{
struct address_list *al = xmalloc (sizeof (struct address_list));
al->count = 1;
return al;
}
+#endif
static void
address_list_delete (struct address_list *al)
}
\f
/**
- * wget_sockaddr_set_address
+ * sockaddr_set_address
*
- * This function takes an wget_sockaddr and fill in the protocol type,
- * the port number and the address, there NULL in address means wildcard.
- * Unsuported adress family will abort the whole programm.
+ * This function takes a sockaddr struct and fills in the protocol type,
+ * the port number and the address. If ENABLE_IPV6 is defined, the sa
+ * parameter should point to a sockaddr_storage structure; if not, it
+ * should point to a sockaddr_in structure.
+ * If the address parameter is NULL, the function will use the unspecified
+ * address (0.0.0.0 for IPv4 and :: for IPv6).
+ * Unsupported address family will abort the whole programm.
*
* Input:
- * wget_sockaddr* The space to be filled
- * int The wished protocol
+ * struct sockaddr* The space to be filled
* unsigned short The port
- * const ip_address The Binary IP adress
+ * const ip_address The IP address
*
* Return:
- * - Only modify 1. param
+ * - Only modifies 1st parameter.
*/
void
-wget_sockaddr_set_address (wget_sockaddr *sa,
- int ip_family, unsigned short port, ip_address *addr)
+sockaddr_set_address (struct sockaddr *sa, unsigned short port,
+ const ip_address *addr)
{
- if (ip_family == AF_INET)
+ if (addr->type == IPv4_ADDRESS)
{
- sa->sin.sin_family = ip_family;
- sa->sin.sin_port = htons (port);
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons (port);
if (addr == NULL)
- memset (&sa->sin.sin_addr, 0, sizeof(ip4_address));
+ sin->sin_addr.s_addr = INADDR_ANY;
else
- {
- ip4_address addr4;
- if (!map_ip_to_ipv4 (addr, &addr4))
- /* should the callers have prevented this? */
- abort ();
- memcpy (&sa->sin.sin_addr, &addr4, sizeof(ip4_address));
- }
- return;
+ sin->sin_addr = addr->addr.ipv4.addr;
}
#ifdef ENABLE_IPV6
- if (ip_family == AF_INET6)
+ else if (addr->type == IPv6_ADDRESS)
{
- sa->sin6.sin6_family = ip_family;
- sa->sin6.sin6_port = htons (port);
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons (port);
if (addr == NULL)
- memset (&sa->sin6.sin6_addr, 0 , 16);
- else
- memcpy (&sa->sin6.sin6_addr, addr, 16);
- return;
+ sin6->sin6_addr = in6addr_any;
+ else
+ sin6->sin6_addr = addr->addr.ipv6.addr;
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ sin6->sin6_scope_id = addr->addr.ipv6.scope_id;
+#endif /* HAVE_SOCKADDR_IN6_SCOPE_ID */
+ }
+#endif /* ENABLE_IPV6 */
+ else
+ abort ();
+}
+
+void
+sockaddr_get_address (const struct sockaddr *sa, unsigned short *port,
+ ip_address *addr)
+{
+ if (sa->sa_family == AF_INET)
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+
+ addr->type = IPv4_ADDRESS;
+ addr->addr.ipv4.addr = sin->sin_addr;
+ if (port != NULL)
+ *port = ntohs (sin->sin_port);
+ }
+#ifdef ENABLE_IPV6
+ else if (sa->sa_family == AF_INET6)
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+ addr->type = IPv6_ADDRESS;
+ addr->addr.ipv6.addr = sin6->sin6_addr;
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ addr->addr.ipv6.scope_id = sin6->sin6_scope_id;
+#endif
+ if (port != NULL)
+ *port = ntohs (sin6->sin6_port);
}
#endif
- abort();
+ else
+ abort ();
}
+#if 0 /* currently unused */
/**
- * wget_sockaddr_set_port
+ * sockaddr_set_port
*
* This funtion only fill the port of the socket information.
* If the protocol is not supported nothing is done.
* - Only modify 1. param
*/
void
-wget_sockaddr_set_port (wget_sockaddr *sa, unsigned short port)
+sockaddr_set_port (struct sockaddr *sa, unsigned short port)
{
- if (sa->sa.sa_family == AF_INET)
+ if (sa->sa_family == AF_INET)
{
- sa->sin.sin_port = htons (port);
- return;
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ sin->sin_port = htons (port);
}
#ifdef ENABLE_IPV6
- if (sa->sa.sa_family == AF_INET6)
+ else if (sa->sa_family == AF_INET6)
{
- sa->sin6.sin6_port = htons (port);
- return;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ sin6->sin6_port = htons (port);
}
#endif
- abort();
+ else
+ abort ();
}
-
-/**
- * wget_sockaddr_get_addr
- *
- * This function return the adress from an sockaddr as byte string.
- * Unsuported adress family will abort the whole programm.
- *
- * Require:
- * that the IP-Protocol already is set.
- *
- * Input:
- * wget_sockaddr* Socket Information
- *
- * Output:
- * unsigned char * IP address as byte string.
- */
-void *
-wget_sockaddr_get_addr (wget_sockaddr *sa)
-{
- if (sa->sa.sa_family == AF_INET)
- return &sa->sin.sin_addr;
-#ifdef ENABLE_IPV6
- if (sa->sa.sa_family == AF_INET6)
- return &sa->sin6.sin6_addr;
#endif
- abort();
- /* unreached */
- return NULL;
-}
/**
- * wget_sockaddr_get_port
+ * sockaddr_get_port
*
* This function only return the port from the input structure
* Unsuported adress family will abort the whole programm.
* unsigned short Port Number in host order.
*/
unsigned short
-wget_sockaddr_get_port (const wget_sockaddr *sa)
+sockaddr_get_port (const struct sockaddr *sa)
{
- if (sa->sa.sa_family == AF_INET)
- return htons (sa->sin.sin_port);
+ if (sa->sa_family == AF_INET) {
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
+ return htons (sin->sin_port);
#ifdef ENABLE_IPV6
- if (sa->sa.sa_family == AF_INET6)
- return htons (sa->sin6.sin6_port);
+ } else if (sa->sa_family == AF_INET6) {
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
+ return htons (sin6->sin6_port);
#endif
- abort();
+ } else
+ abort ();
/* do not complain about return nothing */
return -1;
}
* - Public IP-Family Information
*
* Output:
- * socklen_t structure length for socket options
+ * int structure length for socket options
*/
socklen_t
-sockaddr_len ()
+sockaddr_len (const struct sockaddr *sa)
{
- if (ip_default_family == AF_INET)
- return sizeof (struct sockaddr_in);
+ if (sa->sa_family == AF_INET)
+ {
+ return sizeof (struct sockaddr_in);
+ }
#ifdef ENABLE_IPV6
- if (ip_default_family == AF_INET6)
- return sizeof (struct sockaddr_in6);
+ else if (sa->sa_family == AF_INET6)
+ {
+ return sizeof (struct sockaddr_in6);
+ }
#endif
- abort();
+ else
+ abort ();
/* do not complain about return nothing */
return 0;
}
-
-/**
- * Map an IPv4 adress to the internal adress format.
- */
-void
-map_ipv4_to_ip (ip4_address *ipv4, ip_address *ip)
-{
-#ifdef ENABLE_IPV6
- static unsigned char ipv64[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
- memcpy ((char *)ip + 12, ipv4 , 4);
- memcpy ((char *)ip + 0, ipv64, 12);
-#else
- if ((char *)ip != (char *)ipv4)
- memcpy (ip, ipv4, 4);
-#endif
-}
-
-/* Detect whether an IP adress represents an IPv4 address and, if so,
- copy it to IPV4. 0 is returned on failure.
- This operation always succeeds when Wget is compiled without IPv6.
- If IPV4 is NULL, don't copy, just detect. */
-
-int
-map_ip_to_ipv4 (ip_address *ip, ip4_address *ipv4)
-{
-#ifdef ENABLE_IPV6
- static unsigned char ipv64[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
- if (0 != memcmp (ip, ipv64, 12))
- return 0;
- if (ipv4)
- memcpy (ipv4, (char *)ip + 12, 4);
-#else
- if (ipv4)
- memcpy (ipv4, (char *)ip, 4);
-#endif
- return 1;
-}
\f
/* Versions of gethostbyname and getaddrinfo that support timeout. */
inet_ntoa. With IPv6, it either prints an IPv6 address or an IPv4
address. */
-char *
-pretty_print_address (ip_address *addr)
+const char *
+pretty_print_address (const ip_address *addr)
{
+ switch (addr->type)
+ {
+ case IPv4_ADDRESS:
+ return inet_ntoa (addr->addr.ipv4.addr);
#ifdef ENABLE_IPV6
- ip4_address addr4;
- static char buf[128];
-
- if (map_ip_to_ipv4 (addr, &addr4))
- return inet_ntoa (*(struct in_addr *)&addr4);
-
- if (!inet_ntop (AF_INET6, addr, buf, sizeof (buf)))
- return "<unknown>";
- return buf;
+ case IPv6_ADDRESS:
+ {
+ int len;
+ static char buf[128];
+ inet_ntop (AF_INET6, &addr->addr.ipv6.addr, buf, sizeof (buf));
+#if 0
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ /* print also scope_id for all ?non-global? addresses */
+ snprintf (buf + len, sizeof (buf) - len, "%%%d", addr->addr.ipv6.scope_id);
+#endif
#endif
- return inet_ntoa (*(struct in_addr *)addr);
+ len = strlen (buf);
+ buf[sizeof (buf) - 1] = '\0';
+ return buf;
+ }
+#endif
+ }
+ abort ();
+ return NULL;
}
/* Add host name HOST with the address ADDR_TEXT to the cache.
}
struct address_list *
-lookup_host (const char *host, int silent)
+lookup_host (const char *host, int flags)
{
struct address_list *al = NULL;
- uint32_t addr_ipv4;
- ip_address addr;
-
- /* First, try to check whether the address is already a numeric
- address. */
#ifdef ENABLE_IPV6
- if (inet_pton (AF_INET6, host, &addr) > 0)
- return address_list_from_single (&addr);
+ int err, family;
+ struct addrinfo hints, *res;
+
+ /* This ip_default_family+flags business looks like bad design to
+ me. This function should rather accept a FAMILY argument. */
+ if (flags & LH_IPv4_ONLY)
+ family = AF_INET;
+ else if (flags & LH_IPv6_ONLY)
+ family = AF_INET6;
+ else
+ family = ip_default_family;
#endif
+
+ /* First, try to check whether the address is already a numeric
+ address. Where getaddrinfo is available, we do it using the
+ AI_NUMERICHOST flag. Without IPv6, we check whether inet_addr
+ succeeds. */
- addr_ipv4 = (uint32_t)inet_addr (host);
- if (addr_ipv4 != (uint32_t)-1)
+#ifdef ENABLE_IPV6
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ if (flags & LH_PASSIVE)
+ hints.ai_flags = AI_PASSIVE;
+
+ /* no need to call getaddrinfo_with_timeout here, as we're not
+ * relying on the DNS, but we're only doing an address translation
+ * from presentation (ASCII) to network format */
+ err = getaddrinfo (host, NULL, &hints, &res);
+ if (err == 0 && res != NULL)
{
- /* ADDR is defined to be in network byte order, which is what
- this returns, so we can just copy it to STORE_IP. */
- map_ipv4_to_ip ((ip4_address *)&addr_ipv4, &addr);
- return address_list_from_single (&addr);
+ al = address_list_from_addrinfo (res);
+ freeaddrinfo (res);
+ return al;
}
+#else
+ {
+ uint32_t addr_ipv4 = (uint32_t)inet_addr (host);
+ if (addr_ipv4 != (uint32_t) -1)
+ {
+ /* The return value of inet_addr is in network byte order, so
+ we can just copy it to ADDR. */
+ ip_address addr;
+ /* This has a huge number of dereferences because C doesn't
+ support anonymous unions and because struct in_addr adds a
+ cast. */
+ addr.addr.ipv4.addr.s_addr = addr_ipv4;
+ addr.type = IPv4_ADDRESS;
+ return address_list_from_single (&addr);
+ }
+ }
+#endif
+
+ /* Then, try to find the host in the cache. */
if (host_name_addresses_map)
{
}
}
- if (!silent)
+ if (!(flags & LH_SILENT))
logprintf (LOG_VERBOSE, _("Resolving %s... "), host);
/* Host name lookup goes on below. */
-#ifdef HAVE_GETADDRINFO
+#ifdef ENABLE_IPV6
{
- struct addrinfo hints, *ai;
- int err;
-
memset (&hints, 0, sizeof (hints));
- if (ip_default_family == AF_INET)
- hints.ai_family = AF_INET;
- else
- hints.ai_family = PF_UNSPEC;
+ hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
- err = getaddrinfo_with_timeout (host, NULL, &hints, &ai, opt.dns_timeout);
+ if (flags & LH_PASSIVE)
+ hints.ai_flags = AI_PASSIVE;
+
+ err = getaddrinfo_with_timeout (host, NULL, &hints, &res, opt.dns_timeout);
- if (err != 0 || ai == NULL)
+ if (err != 0 || res == NULL)
{
- if (!silent)
+ if (!(flags & LH_SILENT))
logprintf (LOG_VERBOSE, _("failed: %s.\n"),
err != EAI_SYSTEM ? gai_strerror (err) : strerror (errno));
return NULL;
}
- al = address_list_from_addrinfo (ai);
- freeaddrinfo (ai);
+ al = address_list_from_addrinfo (res);
+ freeaddrinfo (res);
}
#else
{
- struct hostent *hptr;
- hptr = gethostbyname_with_timeout (host, opt.dns_timeout);
+ struct hostent *hptr = gethostbyname_with_timeout (host, opt.dns_timeout);
if (!hptr)
{
- if (!silent)
+ if (!(flags & LH_SILENT))
{
if (errno != ETIMEDOUT)
logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
}
return NULL;
}
+ assert (hptr->h_length == 4);
/* Do all systems have h_addr_list, or is it a newer thing? If
the latter, use address_list_from_single. */
al = address_list_from_vector (hptr->h_addr_list);
/* Print the addresses determined by DNS lookup, but no more than
three. */
- if (!silent)
+ if (!(flags & LH_SILENT))
{
int i;
int printmax = al->count <= 3 ? al->count : 3;
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.
+ (at your option) any later version.
GNU Wget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
struct url;
struct address_list;
-/* wget_sockaddr is used instead of sockaddr where an IPV6 address
- must fit. */
-
-typedef union {
- struct sockaddr sa; /* Generic but too small */
- struct sockaddr_in sin; /* IPv4 socket address */
-#ifdef ENABLE_IPV6
- struct sockaddr_in6 sin6; /* IPv6 socket address */
-#endif
-} wget_sockaddr;
+extern int ip_default_family; /* defined in host.c */
typedef struct {
- unsigned char bytes[4];
-} ip4_address;
+ enum {
+ IPv4_ADDRESS,
+#ifdef ENABLE_IPV6
+ IPv6_ADDRESS
+#endif /* ENABLE_IPV6 */
+ } type;
+ union {
+#ifdef ENABLE_IPV6
+ struct {
+ struct in6_addr addr;
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ unsigned int scope_id;
+#endif /* HAVE_SOCKADDR_IN6_SCOPE_ID */
+ } ipv6;
+#endif /* ENABLE_IPV6 */
+ struct {
+ struct in_addr addr;
+ } ipv4;
+ } addr;
+} ip_address;
-/* If compiled with IPv6 support, we internally represent all IP
- addresses as IPv6 addresses. IPv4 addresses are dynamically mapped
- to IPv6, i.e. stored in the format ::ffff:<Ipv4>. */
+#ifndef ENABLE_IPV6
-#ifdef ENABLE_IPV6
-# define MAX_IP_ADDRESS_SIZE 16
-#else
-# define MAX_IP_ADDRESS_SIZE 4
+#ifndef HAVE_SOCKADDR_STORAGE
+#define sockaddr_storage sockaddr_in
#endif
-typedef struct {
- unsigned char bytes[MAX_IP_ADDRESS_SIZE];
-} ip_address;
+#endif /* ENABLE_IPV6 */
+
+/* Flags for lookup_host */
+#define LH_SILENT 0x0001
+#define LH_PASSIVE 0x0002
+#define LH_IPv4_ONLY 0x0004
+#ifdef ENABLE_IPV6
+#define LH_IPv6_ONLY 0x0008
+#endif /* ENABLE_IPV6 */
/* Function declarations */
struct address_list *lookup_host PARAMS ((const char *, int));
char *herrmsg PARAMS ((int));
-void address_list_get_bounds PARAMS ((struct address_list *, int *, int *));
-void address_list_copy_one PARAMS ((struct address_list *, int,
+void address_list_get_bounds PARAMS ((const struct address_list *,
+ int *, int *));
+void address_list_copy_one PARAMS ((const struct address_list *, int,
ip_address *));
-int address_list_match_all PARAMS ((struct address_list *,
- struct address_list *));
+int address_list_match_all PARAMS ((const struct address_list *,
+ const struct address_list *));
void address_list_set_faulty PARAMS ((struct address_list *, int));
void address_list_release PARAMS ((struct address_list *));
-char *pretty_print_address PARAMS ((ip_address *));
+const char *pretty_print_address PARAMS ((const ip_address *));
int accept_domain PARAMS ((struct url *));
int sufmatch PARAMS ((const char **, const char *));
-void host_cleanup PARAMS ((void));
-
-void wget_sockaddr_set_address PARAMS((wget_sockaddr *, int,
- unsigned short, ip_address *));
-void wget_sockaddr_set_port PARAMS((wget_sockaddr *, unsigned short));
-void *wget_sockaddr_get_addr PARAMS((wget_sockaddr *));
-unsigned short wget_sockaddr_get_port PARAMS((const wget_sockaddr *));
-socklen_t sockaddr_len PARAMS(());
-void map_ipv4_to_ip PARAMS((ip4_address *, ip_address *));
-int map_ip_to_ipv4 PARAMS((ip_address *, ip4_address *));
-
-extern int ip_default_family; /* defined in host.c */
+void sockaddr_set_address PARAMS ((struct sockaddr *, unsigned short,
+ const ip_address *));
+void sockaddr_get_address PARAMS ((const struct sockaddr *, unsigned short *,
+ ip_address *));
+unsigned short sockaddr_get_port PARAMS ((const struct sockaddr *));
+socklen_t sockaddr_len PARAMS ((const struct sockaddr *sa));
+void host_cleanup PARAMS ((void));
#endif /* HOST_H */
/* This lookup_host cannot fail, because it has the results in the
cache. */
- pc_last_host_ip = lookup_host (host, 1);
+ pc_last_host_ip = lookup_host (host, LH_SILENT);
assert (pc_last_host_ip != NULL);
pc_last_port = port;
return 0;
#endif /* HAVE_SSL */
- this_host_ip = lookup_host (host, 1);
+ this_host_ip = lookup_host (host, LH_SILENT);
if (!this_host_ip)
return 0;