示例#1
0
/* Getaddrinfo implementation */
static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client_state *csp, int is_proxy)
{
    struct addrinfo hints, *result, *rp;
    char service[6];
    int retval;
    jb_socket fd;
    fd_set wfds;
    struct timeval timeout;
    #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
    int   flags;
    #endif
    int connect_failed;
    /*
    * XXX: Initializeing it here is only necessary
    *      because not all situations are properly
    *      covered yet.
    */
    int socket_error = 0;
    if (is_proxy) {
        log_time_stage(csp, TIME_STAGE_PROXY_DNS_START);
    }else {
        log_time_stage(csp, TIME_STAGE_DNS_START);
    }

    struct access_control_addr dst[1];

    /* Don't leak memory when retrying. */
    freez(csp->error_message);
    freez(csp->http->host_ip_addr_str);

    retval = snprintf(service, sizeof(service), "%d", portnum);
    if ((-1 == retval) || (sizeof(service) <= retval))
    {
        log_error(LOG_LEVEL_ERROR,
         "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes",
         portnum);
        csp->error_message = strdup("Invalid port number");
        csp->http->host_ip_addr_str = strdup("unknown");
        return(JB_INVALID_SOCKET);
    }

    memset((char *)&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV; /* avoid service look-up */
    #ifdef AI_ADDRCONFIG
    hints.ai_flags |= AI_ADDRCONFIG;
    #endif
    if ((retval = getaddrinfo(host, service, &hints, &result)))
    {
        if (is_proxy) {
            log_time_stage(csp, TIME_STAGE_PROXY_DNS_FAIL);
        }else {
            log_time_stage(csp, TIME_STAGE_DNS_FAIL);
            if (proxy_list) {
                csp->routing = ROUTE_PROXY;
                csp->current_forward_stage = FORWARD_STAGE_DNS_FAILURE;
                return connect_to_forward(csp, proxy_list, 1);
            }
        }
        log_error(LOG_LEVEL_INFO,
         "Can not resolve %s: %s", host, gai_strerror(retval));
        /* XXX: Should find a better way to propagate this error. */
        errno = EINVAL;
        csp->error_message = strdup(gai_strerror(retval));
        csp->http->host_ip_addr_str = strdup("unknown");
        return(JB_INVALID_SOCKET);
    }

    csp->http->host_ip_addr_str = malloc_or_die(NI_MAXHOST);

    for (rp = result; rp != NULL; rp = rp->ai_next)
    {
        memcpy(&dst->addr, rp->ai_addr, rp->ai_addrlen);
    #ifdef FEATURE_ACL
        if (block_acl(dst, csp))
        {
    #ifdef __OS2__
         socket_error = errno = SOCEPERM;
    #else
         socket_error = errno = EPERM;
    #endif
         continue;
        }
    #endif /* def FEATURE_ACL */

        retval = getnameinfo(rp->ai_addr, rp->ai_addrlen, csp->http->host_ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
        if (retval)
        {
         log_error(LOG_LEVEL_ERROR,
            "Failed to get the host name from the socket structure: %s",
            gai_strerror(retval));
         continue;
        }
        if (is_proxy) {
            log_time_stage(csp, TIME_STAGE_PROXY_DNS_END);
        }else {
            csp->http->remote_host_ip_addr_str = strdup_or_die(csp->http->host_ip_addr_str);
            log_time_stage(csp, TIME_STAGE_DNS_END);
        }
        if (!is_proxy) {
            if (dst->addr.ss_family == AF_INET) {
                // IPv4
                if (csp->current_forward_stage == FORWARD_STAGE_NONE && !csp->forward_determined) {
                    log_time_stage(csp, TIME_STAGE_IP_RULE_MATCH_START);
                    struct forward_spec *fwd = forward_ip(csp, dst->addr);
                    log_time_stage(csp, TIME_STAGE_IP_RULE_MATCH_END);
                    csp->forward_determined = 1;
                    if (csp->routing == ROUTE_NONE) {
                        log_time_stage(csp, TIME_STAGE_DNS_IP_RULE_MATCH_START);
                        fwd = forward_dns_pollution_ip(csp, dst->addr);
                        log_time_stage(csp, TIME_STAGE_DNS_IP_RULE_MATCH_END);
                        if (csp->routing == ROUTE_NONE) {
                            if (global_mode && proxy_list) {
                                log_time_stage(csp, TIME_STAGE_GLOBAL_MODE);
                                fwd = proxy_list;
                                csp->routing = ROUTE_PROXY;
                            }else {
                                log_time_stage(csp, TIME_STAGE_NON_GLOBAL_MODE);
                                csp->routing = ROUTE_DIRECT;
                            }
                        }
                    }
                    if (csp->routing == ROUTE_NONE || csp->routing == ROUTE_DIRECT) {

                    }else if (csp->routing == ROUTE_BLOCK) {
                        log_error(LOG_LEVEL_CONNECT,
                                  "Block request to ip: %s", csp->http->host_ip_addr_str);
                        return JB_INVALID_SOCKET;
                    }else {
                        if (fwd) {
                            return connect_to_forward(csp, fwd, 1);
                        }else {
                            // No proxy provided.
                        }
                    }
                }
            }else {
                csp->is_ipv6 = 1;
                csp->routing = ROUTE_DIRECT;
                log_time_stage(csp, TIME_STAGE_IPV6);
            }
        }
        if (is_proxy) {
            log_time_stage(csp, TIME_STAGE_PROXY_START);
        }else {
            log_time_stage(csp, TIME_STAGE_REMOTE_START);
        }

        fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
    #ifdef _WIN32
        if (fd == JB_INVALID_SOCKET)
    #else
        if (fd < 0)
    #endif
        {
         continue;
        }

    #ifndef _WIN32
        if (fd >= FD_SETSIZE)
        {
            log_error(LOG_LEVEL_ERROR,
            "Server socket number too high to use select(): %d >= %d",
            fd, FD_SETSIZE);
            close_socket(fd);
            freeaddrinfo(result);
            return JB_INVALID_SOCKET;
        }
    #endif

#ifdef FEATURE_EXTERNAL_FILTERS
        mark_socket_for_close_on_execute(fd);
#endif

        set_no_delay_flag(fd);
        int yes = 1;
        setsockopt (fd, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof (yes));

#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
        if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
        {
            flags |= O_NDELAY;
            fcntl(fd, F_SETFL, flags);
        }
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */

        connect_failed = 0;
        while (connect(fd, rp->ai_addr, rp->ai_addrlen) == JB_INVALID_SOCKET)
        {
#ifdef __OS2__
            errno = sock_errno();
#endif /* __OS2__ */

#ifdef _WIN32
            if (errno == WSAEINPROGRESS)
#else /* ifndef _WIN32 */
            if (errno == EINPROGRESS)
#endif /* ndef _WIN32 || __OS2__ */
            {
                break;
            }

            if (errno != EINTR)
            {
                socket_error = errno;
                close_socket(fd);
                connect_failed = 1;
                break;
            }
        }
        if (connect_failed)
        {
            continue;
        }

#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
        if (flags != -1)
        {
            flags &= ~O_NDELAY;
            fcntl(fd, F_SETFL, flags);
        }
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */

        /* wait for connection to complete */
        FD_ZERO(&wfds);
        FD_SET(fd, &wfds);

        memset(&timeout, 0, sizeof(timeout));
        timeout.tv_sec  = 30;

        /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */
        if ((select((int)fd + 1, NULL, &wfds, NULL, &timeout) > 0) && FD_ISSET(fd, &wfds))
        {
            socklen_t optlen = sizeof(socket_error);
            if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &optlen))
            {
                if (!socket_error)
                {
                    /* Connection established, no need to try other addresses. */
                    break;
                }
                if (rp->ai_next != NULL)
                {
                    /*
                    * There's another address we can try, so log that this
                    * one didn't work out. If the last one fails, too,
                    * it will get logged outside the loop body so we don't
                    * have to mention it here.
                    */
                    log_error(LOG_LEVEL_CONNECT, "Could not connect to [%s]:%s: %s.",
                      csp->http->host_ip_addr_str, service, strerror(socket_error));
                }
            }
            else
            {
                socket_error = errno;
                log_error(LOG_LEVEL_ERROR, "Could not get the state of "
                          "the connection to [%s]:%s: %s; dropping connection.",
                          csp->http->host_ip_addr_str, service, strerror(errno));
            }
        }

        /* Connection failed, try next address */
        close_socket(fd);
    }

    freeaddrinfo(result);
    if (!rp)
    {
        log_error(LOG_LEVEL_CONNECT, "Could not connect to [%s]:%s: %s.",
         host, service, strerror(socket_error));
        csp->error_message = strdup(strerror(socket_error));
        return(JB_INVALID_SOCKET);
    }
    log_error(LOG_LEVEL_CONNECT, "Connected to %s[%s]:%s.",
      host, csp->http->host_ip_addr_str, service);
    if (is_proxy) {
        log_time_stage(csp, TIME_STAGE_PROXY_CONNECTED);
    }else {
        log_time_stage(csp, TIME_STAGE_REMOTE_CONNECTED);
    }
    return(fd);

}
示例#2
0
static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct client_state *csp)
{
   struct sockaddr_in inaddr;
   jb_socket fd;
   unsigned int addr;
   fd_set wfds;
   struct timeval tv[1];
#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
   int   flags;
#endif

#ifdef FEATURE_ACL
   struct access_control_addr dst[1];
#endif /* def FEATURE_ACL */

   /* Don't leak memory when retrying. */
   freez(csp->http->host_ip_addr_str);

   memset((char *)&inaddr, 0, sizeof inaddr);

   if ((addr = resolve_hostname_to_ip(host)) == INADDR_NONE)
   {
      csp->http->host_ip_addr_str = strdup("unknown");
      return(JB_INVALID_SOCKET);
   }

#ifdef FEATURE_ACL
   dst->addr = ntohl(addr);
   dst->port = portnum;

   if (block_acl(dst, csp))
   {
#ifdef __OS2__
      errno = SOCEPERM;
#else
      errno = EPERM;
#endif
      return(JB_INVALID_SOCKET);
   }
#endif /* def FEATURE_ACL */

   inaddr.sin_addr.s_addr = addr;
   inaddr.sin_family      = AF_INET;
   csp->http->host_ip_addr_str = strdup(inet_ntoa(inaddr.sin_addr));

#ifndef _WIN32
   if (sizeof(inaddr.sin_port) == sizeof(short))
#endif /* ndef _WIN32 */
   {
      inaddr.sin_port = htons((unsigned short) portnum);
   }
#ifndef _WIN32
   else
   {
      inaddr.sin_port = htonl((unsigned long)portnum);
   }
#endif /* ndef _WIN32 */

   fd = socket(inaddr.sin_family, SOCK_STREAM, 0);
#ifdef _WIN32
   if (fd == JB_INVALID_SOCKET)
#else
   if (fd < 0)
#endif
   {
      return(JB_INVALID_SOCKET);
   }

#ifndef _WIN32
   if (fd >= FD_SETSIZE)
   {
      log_error(LOG_LEVEL_ERROR,
         "Server socket number too high to use select(): %d >= %d",
         fd, FD_SETSIZE);
      close_socket(fd);
      return JB_INVALID_SOCKET;
   }
#endif

   set_no_delay_flag(fd);
    int yes = 1;
    setsockopt (fd, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof (yes));

#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
   if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
   {
      flags |= O_NDELAY;
      fcntl(fd, F_SETFL, flags);
#ifdef FEATURE_EXTERNAL_FILTERS
      mark_socket_for_close_on_execute(fd);
#endif
   }
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */

   while (connect(fd, (struct sockaddr *) & inaddr, sizeof inaddr) == JB_INVALID_SOCKET)
   {
#ifdef _WIN32
      if (errno == WSAEINPROGRESS)
#elif __OS2__
      if (sock_errno() == EINPROGRESS)
#else /* ifndef _WIN32 */
      if (errno == EINPROGRESS)
#endif /* ndef _WIN32 || __OS2__ */
      {
         break;
      }

#ifdef __OS2__
      if (sock_errno() != EINTR)
#else
      if (errno != EINTR)
#endif /* __OS2__ */
      {
         close_socket(fd);
         return(JB_INVALID_SOCKET);
      }
   }

#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
   if (flags != -1)
   {
      flags &= ~O_NDELAY;
      fcntl(fd, F_SETFL, flags);
   }
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */

   /* wait for connection to complete */
   FD_ZERO(&wfds);
   FD_SET(fd, &wfds);

   tv->tv_sec  = 30;
   tv->tv_usec = 0;

   /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */
   if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0)
   {
      close_socket(fd);
      return(JB_INVALID_SOCKET);
   }
   return(fd);

}
/*********************************************************************
 *
 * Function    :  connect_to
 *
 * Description :  Open a socket and connect to it.  Will check
 *                that this is allowed according to ACL.
 *
 * Parameters  :
 *          1  :  host = hostname to connect to
 *          2  :  portnum = port to connent on
 *          3  :  csp = Current client state (buffers, headers, etc...)
 *                      Not modified, only used for source IP and ACL.
 *
 * Returns     :  JB_INVALID_SOCKET => failure, else it is the socket
 *                file descriptor.
 *
 *********************************************************************/
jb_socket connect_to(const char *host, int portnum, struct client_state *csp)
{
#define	MAX_EVENTS	1024
   struct sockaddr_in inaddr;
   jb_socket fd, epollfd;
   int addr;
   //fd_set wfds;
   //struct timeval tv[1];
   struct epoll_event ev;
   struct epoll_event evlist[MAX_EVENTS];
   
#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA)
   int   flags;
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */

#ifdef FEATURE_ACL
   struct access_control_addr dst[1];
#endif /* def FEATURE_ACL */

   memset((char *)&inaddr, 0, sizeof inaddr);

   if ((addr = resolve_hostname_to_ip(host)) == INADDR_NONE)
   {
      csp->http->host_ip_addr_str = strdup("unknown");
      return(JB_INVALID_SOCKET);
   }

   log_error(LOG_LEVEL_GPC, "host == %s, port == %d", host, portnum);
   
   
#ifdef FEATURE_ACL
   dst->addr = ntohl((unsigned long) addr);
   dst->port = portnum;

   if (block_acl(dst, csp))
   {
#ifdef __OS2__
      errno = SOCEPERM;
#else
      errno = EPERM;
#endif
      return(JB_INVALID_SOCKET);
   }
#endif /* def FEATURE_ACL */

   inaddr.sin_addr.s_addr = addr;
   inaddr.sin_family      = AF_INET;
   csp->http->host_ip_addr_str = strdup(inet_ntoa(inaddr.sin_addr));

#ifndef _WIN32
   if (sizeof(inaddr.sin_port) == sizeof(short))
#endif /* ndef _WIN32 */
   {
      inaddr.sin_port = htons((unsigned short) portnum);
   }
#ifndef _WIN32
   else
   {
      inaddr.sin_port = htonl((unsigned long)portnum);
   }
#endif /* ndef _WIN32 */

#ifdef _WIN32
   if ((fd = socket(inaddr.sin_family, SOCK_STREAM, 0)) == JB_INVALID_SOCKET)
#else
   if ((fd = socket(inaddr.sin_family, SOCK_STREAM, 0)) < 0)
#endif
   {
      return(JB_INVALID_SOCKET);
   }

#ifdef TCP_NODELAY
   {  /* turn off TCP coalescence */
      int mi = 1;
      setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int));
   }
#endif /* def TCP_NODELAY */

#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
   if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
   {
      flags |= O_NDELAY;
      fcntl(fd, F_SETFL, flags);
   }
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */

   while (connect(fd, (struct sockaddr *) & inaddr, sizeof inaddr) == JB_INVALID_SOCKET)
   {
#ifdef _WIN32
      if (errno == WSAEINPROGRESS)
#elif __OS2__ 
      if (sock_errno() == EINPROGRESS)
#else /* ifndef _WIN32 */
      if (errno == EINPROGRESS)
#endif /* ndef _WIN32 || __OS2__ */
      {
         break;
      }

#ifdef __OS2__ 
      if (sock_errno() != EINTR)
#else
      if (errno != EINTR)
#endif /* __OS2__ */
      {
         close_socket(fd);
         return(JB_INVALID_SOCKET);
      }
   }

#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
   if (flags != -1)
   {
      flags &= ~O_NDELAY;
      fcntl(fd, F_SETFL, flags);
   }
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */

   /* wait for connection to complete */
   //FD_ZERO(&wfds);
   //FD_SET(fd, &wfds);

   //tv->tv_sec  = 30;
   //tv->tv_usec = 0;
   
   epollfd = epoll_create(MAX_EVENTS);
   if (epollfd == -1)
   {
		log_error(LOG_LEVEL_GPC, "epoll_create error");
		return JB_INVALID_SOCKET;
   }

   ev.data.fd = fd;
   ev.events = EPOLLIN | EPOLLOUT;
   
   if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1)
   {
		log_error(LOG_LEVEL_GPC, "epoll_ctl error");
		return JB_INVALID_SOCKET;
   }
 
   log_error(LOG_LEVEL_GPC, "int connect() : before select");
   

   /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Wierd! */
#if 0
   if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0)
#endif
	if (epoll_wait(epollfd, evlist, MAX_EVENTS, -1) <= 0)
   {
      log_error(LOG_LEVEL_GPC, "in select");
      close_socket(fd);
      return(JB_INVALID_SOCKET);
   }
   
   log_error(LOG_LEVEL_GPC, "int connect()  : after select");
   
   return(fd);

}