/* Establishing and handling network connections.
Copyright (C) 1995, 1996, 1997 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 <config.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
+#include <assert.h>
#ifdef WINDOWS
# include <winsock.h>
# include <sys/socket.h>
# include <netdb.h>
# include <netinet/in.h>
+#ifndef __BEOS__
# include <arpa/inet.h>
+#endif
#endif /* WINDOWS */
#include <errno.h>
static int msock = -1;
static struct sockaddr *addr;
+/* A kludge, but still better than passing the host name all the way
+ to connect_to_one. */
+static const char *connection_host_name;
-/* Create an internet connection to HOSTNAME on PORT. The created
- socket will be stored to *SOCK. */
-uerr_t
-make_connection (int *sock, char *hostname, unsigned short port)
+void
+set_connection_host_name (const char *host)
+{
+ if (host)
+ assert (connection_host_name == NULL);
+ else
+ assert (connection_host_name != NULL);
+
+ connection_host_name = host;
+}
+
+/* Connect to a remote host whose address has been resolved. */
+int
+connect_to_one (ip_address *addr, unsigned short port, int silent)
{
- struct sockaddr_in sock_name;
- /* struct hostent *hptr; */
-
- /* Get internet address of the host. We can do it either by calling
- ngethostbyname, or by calling store_hostaddress, from host.c.
- storehostaddress is better since it caches calls to
- gethostbyname. */
-#if 1
- if (!store_hostaddress ((unsigned char *)&sock_name.sin_addr, hostname))
- return HOSTERR;
-#else /* never */
- if (!(hptr = ngethostbyname (hostname)))
- return HOSTERR;
- /* Copy the address of the host to socket description. */
- memcpy (&sock_name.sin_addr, hptr->h_addr, hptr->h_length);
-#endif /* never */
+ wget_sockaddr sa;
+ int sock, save_errno;
/* Set port and protocol */
- sock_name.sin_family = AF_INET;
- sock_name.sin_port = htons (port);
+ wget_sockaddr_set_address (&sa, ip_default_family, port, addr);
+
+ if (!silent)
+ {
+ 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... "),
+ connection_host_name, pretty_addr, port);
+ else
+ logprintf (LOG_VERBOSE, _("Connecting to %s:%hu... "),
+ pretty_addr, port);
+ }
/* Make an internet socket, stream type. */
- if ((*sock = socket (AF_INET, SOCK_STREAM, 0)) == -1)
- return CONSOCKERR;
+ sock = socket (ip_default_family, SOCK_STREAM, 0);
+ if (sock < 0)
+ goto out;
- if (opt.bind_address != NULL)
+ if (opt.bind_address)
{
/* Bind the client side to the requested address. */
- if (bind (*sock, (struct sockaddr *) opt.bind_address,
- sizeof (*opt.bind_address)))
- return CONSOCKERR;
+ if (bind (sock, (struct sockaddr *)opt.bind_address, sockaddr_len ()))
+ {
+ close (sock);
+ sock = -1;
+ goto out;
+ }
}
/* Connect the socket to the remote host. */
- if (connect (*sock, (struct sockaddr *) &sock_name, sizeof (sock_name)))
+ if (connect (sock, &sa.sa, sockaddr_len ()) < 0)
{
- if (errno == ECONNREFUSED)
- return CONREFUSED;
- else
- return CONERROR;
+ close (sock);
+ sock = -1;
+ goto out;
+ }
+
+ out:
+ if (sock >= 0)
+ {
+ /* Success. */
+ if (!silent)
+ logprintf (LOG_VERBOSE, _("connected.\n"));
+ DEBUGP (("Created socket %d.\n", sock));
}
- DEBUGP (("Created fd %d.\n", *sock));
- return NOCONERROR;
+ else
+ {
+ save_errno = errno;
+ if (!silent)
+ logprintf (LOG_VERBOSE, "failed: %s.\n", strerror (errno));
+ errno = save_errno;
+ }
+
+ return sock;
+}
+
+/* Connect to a remote host whose address has been resolved. */
+int
+connect_to_many (struct address_list *al, unsigned short port, int silent)
+{
+ int i, start, end;
+
+ address_list_get_bounds (al, &start, &end);
+ for (i = start; i < end; i++)
+ {
+ ip_address addr;
+ int sock;
+ address_list_copy_one (al, i, &addr);
+
+ sock = connect_to_one (&addr, port, silent);
+ if (sock >= 0)
+ /* Success. */
+ return sock;
+
+ address_list_set_faulty (al, i);
+
+ /* The attempt to connect has failed. Continue with the loop
+ and try next address. */
+ }
+
+ return -1;
+}
+
+int
+test_socket_open (int sock)
+{
+#ifdef HAVE_SELECT
+ fd_set check_set;
+ struct timeval to;
+
+ /* Check if we still have a valid (non-EOF) connection. From Andrew
+ * Maholski's code in the Unix Socket FAQ. */
+
+ FD_ZERO (&check_set);
+ FD_SET (sock, &check_set);
+
+ /* Wait one microsecond */
+ to.tv_sec = 0;
+ to.tv_usec = 1;
+
+ /* If we get a timeout, then that means still connected */
+ if (select (sock + 1, &check_set, NULL, NULL, &to) == 0)
+ {
+ /* Connection is valid (not EOF), so continue */
+ return 1;
+ }
+ else
+ return 0;
+#else
+ /* Without select, it's hard to know for sure. */
+ return 1;
+#endif
}
/* Bind the local port PORT. This does all the necessary work, which
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)
+bindport (unsigned short *port, int family)
{
int optval = 1;
- static struct sockaddr_in srv;
+ wget_sockaddr srv;
+ memset (&srv, 0, sizeof (wget_sockaddr));
msock = -1;
- addr = (struct sockaddr *) &srv;
- if ((msock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+
+ if ((msock = socket (family, SOCK_STREAM, 0)) < 0)
return CONSOCKERR;
if (setsockopt (msock, SOL_SOCKET, SO_REUSEADDR,
(char *)&optval, sizeof (optval)) < 0)
return CONSOCKERR;
if (opt.bind_address == NULL)
- {
- srv.sin_family = AF_INET;
- srv.sin_addr.s_addr = htonl (INADDR_ANY);
- }
+ wget_sockaddr_set_address (&srv, ip_default_family, htons (*port), NULL);
else
srv = *opt.bind_address;
-
- srv.sin_port = htons (*port);
- if (bind (msock, addr, sizeof (struct sockaddr_in)) < 0)
+ wget_sockaddr_set_port (&srv, *port);
+ if (bind (msock, &srv.sa, sockaddr_len ()) < 0)
{
CLOSE (msock);
msock = -1;
DEBUGP (("Master socket fd %d bound.\n", msock));
if (!*port)
{
- size_t addrlen = sizeof (struct sockaddr_in);
- if (getsockname (msock, addr, (int *)&addrlen) < 0)
+ /* #### addrlen should be a 32-bit type, which int is not
+ guaranteed to be. Oh, and don't try to make it a size_t,
+ because that can be 64-bit. */
+ int sa_len = sockaddr_len ();
+ if (getsockname (msock, &srv.sa, &sa_len) < 0)
{
CLOSE (msock);
msock = -1;
return CONPORTERR;
}
- *port = ntohs (srv.sin_port);
+ *port = wget_sockaddr_get_port (&srv);
+ DEBUGP (("using port %i.\n", *port));
}
if (listen (msock, 1) < 0)
{
Returns 1 if FD is accessible, 0 for timeout and -1 for error in
select(). */
-static int
+int
select_fd (int fd, int maxtime, int writep)
{
fd_set fds, exceptfds;
uerr_t
acceptport (int *sock)
{
- int addrlen = sizeof (struct sockaddr_in);
+ int addrlen = sockaddr_len ();
#ifdef HAVE_SELECT
if (select_fd (msock, opt.timeout, 0) <= 0)
msock = -1;
}
-/* Return the local IP address associated with the connection on FD.
- It is returned in a static buffer. */
-unsigned char *
-conaddr (int fd)
+/* Return the local IP address associated with the connection on FD. */
+
+int
+conaddr (int fd, ip_address *ip)
{
- static unsigned char res[4];
- struct sockaddr_in mysrv;
- struct sockaddr *myaddr;
- size_t addrlen = sizeof (mysrv);
-
- myaddr = (struct sockaddr *) (&mysrv);
- if (getsockname (fd, myaddr, (int *)&addrlen) < 0)
- return NULL;
- memcpy (res, &mysrv.sin_addr, 4);
- return res;
+ wget_sockaddr mysrv;
+
+ /* see bindport() for discussion of using `int' here. */
+ int addrlen = sizeof (mysrv);
+
+ if (getsockname (fd, &mysrv.sa, (int *)&addrlen) < 0)
+ return 0;
+
+ switch (mysrv.sa.sa_family)
+ {
+#ifdef INET6
+ case AF_INET6:
+ memcpy (ip, &mysrv.sin6.sin6_addr, 16);
+ return 1;
+#endif
+ case AF_INET:
+ map_ipv4_to_ip ((ip4_address *)&mysrv.sin.sin_addr, ip);
+ return 1;
+ default:
+ abort ();
+ }
+ return 0;
}
/* Read at most LEN bytes from FD, storing them to BUF. This is