⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 connect.c

📁 一个从网络上自动下载文件的自由工具
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Establishing and handling network connections.   Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.This file is part of GNU Wget.GNU Wget is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 3 of the License, or (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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with Wget.  If not, see <http://www.gnu.org/licenses/>.Additional permission under GNU GPL version 3 section 7If you modify this program, or any covered work, by linking orcombining it with the OpenSSL project's OpenSSL library (or amodified version of that library), containing parts covered by theterms of the OpenSSL or SSLeay licenses, the Free Software Foundationgrants you additional permission to convey the resulting work.Corresponding Source for a non-source form of such a combinationshall include the source code for the parts of OpenSSL used as wellas that of the covered work.  */#include <config.h>#include <stdio.h>#include <stdlib.h>#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#include <assert.h>#ifndef WINDOWS# include <sys/socket.h># include <netdb.h># include <netinet/in.h># ifndef __BEOS__#  include <arpa/inet.h># endif#endif /* not WINDOWS */#include <errno.h>#include <string.h>#ifdef HAVE_SYS_SELECT_H# include <sys/select.h>#endif /* HAVE_SYS_SELECT_H */#include "wget.h"#include "utils.h"#include "host.h"#include "connect.h"#include "hash.h"/* Define sockaddr_storage where unavailable (presumably on IPv4-only   hosts).  */#ifndef ENABLE_IPV6# ifndef HAVE_STRUCT_SOCKADDR_STORAGE#  define sockaddr_storage sockaddr_in# endif#endif /* ENABLE_IPV6 *//* Fill SA as per the data in IP and PORT.  SA shoult point to struct   sockaddr_storage if ENABLE_IPV6 is defined, to struct sockaddr_in   otherwise.  */static voidsockaddr_set_data (struct sockaddr *sa, const ip_address *ip, int port){  switch (ip->family)    {    case AF_INET:      {        struct sockaddr_in *sin = (struct sockaddr_in *)sa;        xzero (*sin);        sin->sin_family = AF_INET;        sin->sin_port = htons (port);        sin->sin_addr = ip->data.d4;        break;      }#ifdef ENABLE_IPV6    case AF_INET6:      {        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;        xzero (*sin6);        sin6->sin6_family = AF_INET6;        sin6->sin6_port = htons (port);        sin6->sin6_addr = ip->data.d6;#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID        sin6->sin6_scope_id = ip->ipv6_scope;#endif        break;      }#endif /* ENABLE_IPV6 */    default:      abort ();    }}/* Get the data of SA, specifically the IP address and the port.  If   you're not interested in one or the other information, pass NULL as   the pointer.  */static voidsockaddr_get_data (const struct sockaddr *sa, ip_address *ip, int *port){  switch (sa->sa_family)    {    case AF_INET:      {        struct sockaddr_in *sin = (struct sockaddr_in *)sa;        if (ip)          {            ip->family = AF_INET;            ip->data.d4 = sin->sin_addr;          }        if (port)          *port = ntohs (sin->sin_port);        break;      }#ifdef ENABLE_IPV6    case AF_INET6:      {        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;        if (ip)          {            ip->family = AF_INET6;            ip->data.d6 = sin6->sin6_addr;#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID            ip->ipv6_scope = sin6->sin6_scope_id;#endif          }        if (port)          *port = ntohs (sin6->sin6_port);        break;      }#endif    default:      abort ();    }}/* Return the size of the sockaddr structure depending on its   family.  */static socklen_tsockaddr_size (const struct sockaddr *sa){  switch (sa->sa_family)    {    case AF_INET:      return sizeof (struct sockaddr_in);#ifdef ENABLE_IPV6    case AF_INET6:      return sizeof (struct sockaddr_in6);#endif    default:      abort ();    }}/* Resolve the bind address specified via --bind-address and store it   to SA.  The resolved value is stored in a static variable and   reused after the first invocation of this function.   Returns true on success, false on failure.  */static boolresolve_bind_address (struct sockaddr *sa){  struct address_list *al;  /* Make sure this is called only once.  opt.bind_address doesn't     change during a Wget run.  */  static bool called, should_bind;  static ip_address ip;  if (called)    {      if (should_bind)        sockaddr_set_data (sa, &ip, 0);      return should_bind;    }  called = true;  al = lookup_host (opt.bind_address, LH_BIND | LH_SILENT);  if (!al)    {      /* #### We should be able to print the error message here. */      logprintf (LOG_NOTQUIET,                 _("%s: unable to resolve bind address `%s'; disabling bind.\n"),                 exec_name, opt.bind_address);      should_bind = false;      return false;    }  /* Pick the first address in the list and use it as bind address.     Perhaps we should try multiple addresses in succession, but I     don't think that's necessary in practice.  */  ip = *address_list_address_at (al, 0);  address_list_release (al);  sockaddr_set_data (sa, &ip, 0);  should_bind = true;  return true;}struct cwt_context {  int fd;  const struct sockaddr *addr;  socklen_t addrlen;  int result;};static voidconnect_with_timeout_callback (void *arg){  struct cwt_context *ctx = (struct cwt_context *)arg;  ctx->result = connect (ctx->fd, ctx->addr, ctx->addrlen);}/* Like connect, but specifies a timeout.  If connecting takes longer   than TIMEOUT seconds, -1 is returned and errno is set to   ETIMEDOUT.  */static intconnect_with_timeout (int fd, const struct sockaddr *addr, socklen_t addrlen,                      double timeout){  struct cwt_context ctx;  ctx.fd = fd;  ctx.addr = addr;  ctx.addrlen = addrlen;  if (run_with_timeout (timeout, connect_with_timeout_callback, &ctx))    {      errno = ETIMEDOUT;      return -1;    }  if (ctx.result == -1 && errno == EINTR)    errno = ETIMEDOUT;  return ctx.result;}/* Connect via TCP to the specified address and port.   If PRINT is non-NULL, it is the host name to print that we're   connecting to.  */intconnect_to_ip (const ip_address *ip, int port, const char *print){  struct sockaddr_storage ss;  struct sockaddr *sa = (struct sockaddr *)&ss;  int sock;  /* If PRINT is non-NULL, print the "Connecting to..." line, with     PRINT being the host name we're connecting to.  */  if (print)    {      const char *txt_addr = print_address (ip);      if (print && 0 != strcmp (print, txt_addr))        logprintf (LOG_VERBOSE, _("Connecting to %s|%s|:%d... "),                   escnonprint (print), txt_addr, port);      else        logprintf (LOG_VERBOSE, _("Connecting to %s:%d... "), txt_addr, port);    }  /* Store the sockaddr info to SA.  */  sockaddr_set_data (sa, ip, port);  /* Create the socket of the family appropriate for the address.  */  sock = socket (sa->sa_family, SOCK_STREAM, 0);  if (sock < 0)    goto err;#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY)  if (opt.ipv6_only) {    int on = 1;    /* In case of error, we will go on anyway... */    int err = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on));    IF_DEBUG      if (err < 0)         DEBUGP (("Failed setting IPV6_V6ONLY: %s", strerror (errno)));  }#endif  /* For very small rate limits, set the buffer size (and hence,     hopefully, the kernel's TCP window size) to the per-second limit.     That way we should never have to sleep for more than 1s between     network reads.  */  if (opt.limit_rate && opt.limit_rate < 8192)    {      int bufsize = opt.limit_rate;      if (bufsize < 512)        bufsize = 512;          /* avoid pathologically small values */#ifdef SO_RCVBUF      setsockopt (sock, SOL_SOCKET, SO_RCVBUF,                  (void *)&bufsize, (socklen_t)sizeof (bufsize));#endif      /* When we add limit_rate support for writing, which is useful         for POST, we should also set SO_SNDBUF here.  */    }  if (opt.bind_address)    {      /* Bind the client side of the socket to the requested         address.  */      struct sockaddr_storage bind_ss;      struct sockaddr *bind_sa = (struct sockaddr *)&bind_ss;      if (resolve_bind_address (bind_sa))        {          if (bind (sock, bind_sa, sockaddr_size (bind_sa)) < 0)            goto err;        }    }  /* Connect the socket to the remote endpoint.  */  if (connect_with_timeout (sock, sa, sockaddr_size (sa),                            opt.connect_timeout) < 0)    goto err;  /* Success. */  assert (sock >= 0);  if (print)    logprintf (LOG_VERBOSE, _("connected.\n"));  DEBUGP (("Created socket %d.\n", sock));  return sock; err:  {    /* Protect errno from possible modifications by close and       logprintf.  */    int save_errno = errno;    if (sock >= 0)      fd_close (sock);    if (print)      logprintf (LOG_VERBOSE, _("failed: %s.\n"), strerror (errno));    errno = save_errno;    return -1;  }}/* Connect via TCP to a remote host on the specified port.   HOST is resolved as an Internet host name.  If HOST resolves to   more than one IP address, they are tried in the order returned by   DNS until connecting to one of them succeeds.  */intconnect_to_host (const char *host, int port){  int i, start, end;  int sock;  struct address_list *al = lookup_host (host, 0); retry:  if (!al)    {      logprintf (LOG_NOTQUIET,                 _("%s: unable to resolve host address `%s'\n"),                 exec_name, host);      return E_HOST;    }  address_list_get_bounds (al, &start, &end);  for (i = start; i < end; i++)    {      const ip_address *ip = address_list_address_at (al, i);      sock = connect_to_ip (ip, port, host);      if (sock >= 0)        {          /* Success. */          address_list_set_connected (al);          address_list_release (al);          return sock;        }      /* The attempt to connect has failed.  Continue with the loop         and try next address. */      address_list_set_faulty (al, i);    }  /* Failed to connect to any of the addresses in AL. */  if (address_list_connected_p (al))    {      /* We connected to AL before, but cannot do so now.  That might         indicate that our DNS cache entry for HOST has expired.  */      address_list_release (al);      al = lookup_host (host, LH_REFRESH);      goto retry;    }  address_list_release (al);  return -1;}/* Create a socket, bind it to local interface BIND_ADDRESS on port   *PORT, set up a listen backlog, and return the resulting socket, or   -1 in case of error.   BIND_ADDRESS is the address of the interface to bind to.  If it is   NULL, the socket is bound to the default address.  PORT should   point to the port number that will be used for the binding.  If   that number is 0, the system will choose a suitable port, and the   chosen value will be written to *PORT.   Calling accept() on such a socket waits for and accepts incoming   TCP connections.  */intbind_local (const ip_address *bind_address, int *port){  int sock;  struct sockaddr_storage ss;  struct sockaddr *sa = (struct sockaddr *)&ss;  /* For setting options with setsockopt. */  int setopt_val = 1;  void *setopt_ptr = (void *)&setopt_val;  socklen_t setopt_size = sizeof (setopt_val);  sock = socket (bind_address->family, SOCK_STREAM, 0);  if (sock < 0)    return -1;#ifdef SO_REUSEADDR  setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, setopt_ptr, setopt_size);#endif  xzero (ss);  sockaddr_set_data (sa, bind_address, *port);  if (bind (sock, sa, sockaddr_size (sa)) < 0)    {      fd_close (sock);      return -1;    }  DEBUGP (("Local socket fd %d bound.\n", sock));  /* If *PORT is 0, find out which port we've bound to.  */  if (*port == 0)    {      socklen_t addrlen = sockaddr_size (sa);      if (getsockname (sock, sa, &addrlen) < 0)        {          /* If we can't find out the socket's local address ("name"),             something is seriously wrong with the socket, and it's             unusable for us anyway because we must know the chosen             port.  */          fd_close (sock);          return -1;        }      sockaddr_get_data (sa, NULL, port);      DEBUGP (("binding to address %s using port %i.\n",               print_address (bind_address), *port));    }  if (listen (sock, 1) < 0)    {      fd_close (sock);      return -1;    }  return sock;}/* Like a call to accept(), but with the added check for timeout.   In other words, accept a client connection on LOCAL_SOCK, and   return the new socket used for communication with the client.   LOCAL_SOCK should have been bound, e.g. using bind_local().   The caller is blocked until a connection is established.  If no   connection is established for opt.connect_timeout seconds, the   function exits with an error status.  */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -