Ejemplo n.º 1
0
int connect(CONNECT_SIGNATURE) {
	struct sockaddr_in *connaddr;
	struct sockaddr_in peer_address;
	struct sockaddr_in server_address;
   int gotvalidserver = 0, rc, namelen = sizeof(peer_address);
	int sock_type = -1;
	int sock_type_len = sizeof(sock_type);
	unsigned int res = -1;
	struct serverent *path;
   struct connreq *newconn;

   get_environment();

	/* If the real connect doesn't exist, we're stuffed */
	if (realconnect == NULL) {
		show_msg(MSGERR, "Unresolved symbol: connect\n");
		return(-1);
	}

   show_msg(MSGDEBUG, "Got connection request\n");

	connaddr = (struct sockaddr_in *) __addr;

	/* Get the type of the socket */
	getsockopt(__fd, SOL_SOCKET, SO_TYPE, 
		   (void *) &sock_type, &sock_type_len);

	/* If this isn't an INET socket for a TCP stream we can't  */
	/* handle it, just call the real connect now               */
   if ((connaddr->sin_family != AF_INET) ||
       (sock_type != SOCK_STREAM)) {
      show_msg(MSGDEBUG, "Connection isn't a TCP stream ignoring\n");
		return(realconnect(__fd, __addr, __len));
   }

   /* If we haven't initialized yet, do it now */
   get_config();

   /* Are we already handling this connect? */
   if ((newconn = find_socks_request(__fd, 1))) {
      if (memcmp(&newconn->connaddr, connaddr, sizeof(*connaddr))) {
         /* Ok, they're calling connect on a socket that is in our
          * queue but this connect() isn't to the same destination, 
          * they're obviously not trying to check the status of 
          * they're non blocking connect, they must have close()d 
          * the other socket and created a new one which happens
          * to have the same fd as a request we haven't had the chance
          * to delete yet, so we delete it here. */
         show_msg(MSGDEBUG, "Call to connect received on old "
                            "tsocks request for socket %d but to "
                            "new destination, deleting old request\n",
                  newconn->sockid);
         kill_socks_request(newconn);
      } else {
         /* Ok, this call to connect() is to check the status of 
          * a current non blocking connect(). */
         if (newconn->state == FAILED) {
            show_msg(MSGDEBUG, "Call to connect received on failed "
                               "request %d, returning %d\n",
                     newconn->sockid, newconn->err);
            errno = newconn->err;
            rc = -1;
         } else if (newconn->state == DONE) {
            show_msg(MSGERR, "Call to connect received on completed "
                             "request %d\n",
                     newconn->sockid, newconn->err);
            rc = 0;
         } else {
            show_msg(MSGDEBUG, "Call to connect received on current request %d\n",
                     newconn->sockid);
            rc = handle_request(newconn);
            errno = rc;
         }
         if ((newconn->state == FAILED) || (newconn->state == DONE))
            kill_socks_request(newconn);
         return((rc ? -1 : 0));
      }
   }

   /* If the socket is already connected, just call connect  */
   /* and get its standard reply                             */
   if (!getpeername(__fd, (struct sockaddr *) &peer_address, &namelen)) {
      show_msg(MSGDEBUG, "Socket is already connected, defering to "
                         "real connect\n");
		return(realconnect(__fd, __addr, __len));
   }
      
   show_msg(MSGDEBUG, "Got connection request for socket %d to "
                      "%s\n", __fd, inet_ntoa(connaddr->sin_addr));

   /* If the address is local call realconnect */
   if (!(is_local(config, &(connaddr->sin_addr)))) {
      show_msg(MSGDEBUG, "Connection for socket %d is local\n", __fd);
      return(realconnect(__fd, __addr, __len));
   }

   /* Ok, so its not local, we need a path to the net */
   pick_server(config, &path, &(connaddr->sin_addr), ntohs(connaddr->sin_port));

   show_msg(MSGDEBUG, "Picked server %s for connection\n",
            (path->address ? path->address : "(Not Provided)"));
   if (path->address == NULL) {
      if (path == &(config->defaultserver)) 
         show_msg(MSGERR, "Connection needs to be made "
                          "via default server but "
                          "the default server has not "
                          "been specified\n");
      else 
         show_msg(MSGERR, "Connection needs to be made "
                          "via path specified at line "
                          "%d in configuration file but "
                          "the server has not been "
                          "specified for this path\n",
                  path->lineno);
   } else if ((res = resolve_ip(path->address, 0, HOSTNAMES)) == -1) {
      show_msg(MSGERR, "The SOCKS server (%s) listed in the configuration "
                       "file which needs to be used for this connection "
                       "is invalid\n", path->address);
   } else {	
      /* Construct the addr for the socks server */
      server_address.sin_family = AF_INET; /* host byte order */
      server_address.sin_addr.s_addr = res;
      server_address.sin_port = htons(path->port);
      bzero(&(server_address.sin_zero), 8);

      /* Complain if this server isn't on a localnet */
      if (is_local(config, &server_address.sin_addr)) {
         show_msg(MSGERR, "SOCKS server %s (%s) is not on a local subnet!\n", 
                  path->address, inet_ntoa(server_address.sin_addr));
      } else 
         gotvalidserver = 1;
   }

   /* If we haven't found a valid server we return connection refused */
   if (!gotvalidserver || 
       !(newconn = new_socks_request(__fd, connaddr, &server_address, path))) {
      errno = ECONNREFUSED;
      return(-1);
   } else {
      /* Now we call the main function to handle the connect. */
      rc = handle_request(newconn);
      /* If the request completed immediately it mustn't have been
       * a non blocking socket, in this case we don't need to know
       * about this socket anymore. */
      if ((newconn->state == FAILED) || (newconn->state == DONE))
         kill_socks_request(newconn);
      errno = rc;
      return((rc ? -1 : 0));
   }
}
Ejemplo n.º 2
0
int connect(CONNECT_SIGNATURE) {
	struct sockaddr_in *connaddr;
	struct sockaddr_in peer_address;
	struct sockaddr_in server_address;
   int gotvalidserver = 0, rc, namelen = sizeof(peer_address);
	int sock_type = -1;
	int sock_type_len = sizeof(sock_type);
	unsigned int res = -1;
	struct serverent *path;
   struct connreq *newconn;

   get_environment();

	/* If the real connect doesn't exist, we're stuffed */
	if (realconnect == NULL) {
		show_msg(MSGERR, "Unresolved symbol: connect\n");
		return(-1);
	}

   show_msg(MSGDEBUG, "Got connection request\n");

	connaddr = (struct sockaddr_in *) __addr;

	/* Get the type of the socket */
	getsockopt(__fd, SOL_SOCKET, SO_TYPE, 
		   (void *) &sock_type, &sock_type_len);

	/* If this isn't an INET socket for a TCP stream we can't  */
	/* handle it, just call the real connect now               */
   if ((connaddr->sin_family != AF_INET) ||
       (sock_type != SOCK_STREAM)) {
      show_msg(MSGDEBUG, "Connection isn't a TCP stream ignoring\n");
		return(realconnect(__fd, __addr, __len));
   }

   /* If we haven't initialized yet, do it now */
   get_config();

   /* Are we already handling this connect? */
   if ((newconn = find_socks_request(__fd, 1))) {
      if (memcmp(&newconn->connaddr, connaddr, sizeof(*connaddr))) {
         /* Ok, they're calling connect on a socket that is in our
          * queue but this connect() isn't to the same destination, 
          * they're obviously not trying to check the status of 
          * they're non blocking connect, they must have close()d 
          * the other socket and created a new one which happens
          * to have the same fd as a request we haven't had the chance
          * to delete yet, so we delete it here. */
         show_msg(MSGDEBUG, "Call to connect received on old "
                            "tsocks request for socket %d but to "
                            "new destination, deleting old request\n",
                  newconn->sockid);
         kill_socks_request(newconn);
      } else {
         /* Ok, this call to connect() is to check the status of 
          * a current non blocking connect(). */
         if (newconn->state == FAILED) {
            show_msg(MSGDEBUG, "Call to connect received on failed "
                               "request %d, returning %d\n",
                     newconn->sockid, newconn->err);
            errno = newconn->err;
            rc = -1;
         } else if (newconn->state == DONE) {
            show_msg(MSGERR, "Call to connect received on completed "
                             "request %d\n",
                     newconn->sockid, newconn->err);
            rc = 0;
         } else {
            show_msg(MSGDEBUG, "Call to connect received on current request %d\n",
                     newconn->sockid);
            rc = handle_request(newconn);
            errno = rc;
         }
         if ((newconn->state == FAILED) || (newconn->state == DONE))
            kill_socks_request(newconn);
         return((rc ? -1 : 0));
      }
   }

   /* If the socket is already connected, just call connect  */
   /* and get its standard reply                             */
   if (!getpeername(__fd, (struct sockaddr *) &peer_address, &namelen)) {
      show_msg(MSGDEBUG, "Socket is already connected, defering to "
                         "real connect\n");
		return(realconnect(__fd, __addr, __len));
   }
     
   show_msg(MSGDEBUG, "Got connection request for socket %d to "
                      "%s\n", __fd, inet_ntoa(connaddr->sin_addr));

   /* If the address is local call realconnect */
#ifdef USE_TOR_DNS
   if (!(is_local(config, &(connaddr->sin_addr))) && 
       !is_dead_address(pool, connaddr->sin_addr.s_addr)) {
#else 
   if (!(is_local(config, &(connaddr->sin_addr)))) {
#endif
      show_msg(MSGDEBUG, "Connection for socket %d is local\n", __fd);
      return(realconnect(__fd, __addr, __len));
   }

   /* Ok, so its not local, we need a path to the net */
   pick_server(config, &path, &(connaddr->sin_addr), ntohs(connaddr->sin_port));

   show_msg(MSGDEBUG, "Picked server %s for connection\n",
            (path->address ? path->address : "(Not Provided)"));
   if (path->address == NULL) {
      if (path == &(config->defaultserver)) 
         show_msg(MSGERR, "Connection needs to be made "
                          "via default server but "
                          "the default server has not "
                          "been specified\n");
      else 
         show_msg(MSGERR, "Connection needs to be made "
                          "via path specified at line "
                          "%d in configuration file but "
                          "the server has not been "
                          "specified for this path\n",
                  path->lineno);
   } else if ((res = resolve_ip(path->address, 0, HOSTNAMES)) == -1) {
      show_msg(MSGERR, "The SOCKS server (%s) listed in the configuration "
                       "file which needs to be used for this connection "
                       "is invalid\n", path->address);
   } else {	
      /* Construct the addr for the socks server */
      server_address.sin_family = AF_INET; /* host byte order */
      server_address.sin_addr.s_addr = res;
      server_address.sin_port = htons(path->port);
      bzero(&(server_address.sin_zero), 8);

      /* Complain if this server isn't on a localnet */
      if (is_local(config, &server_address.sin_addr)) {
         show_msg(MSGERR, "SOCKS server %s (%s) is not on a local subnet!\n", 
                  path->address, inet_ntoa(server_address.sin_addr));
      } else 
         gotvalidserver = 1;
   }

   /* If we haven't found a valid server we return connection refused */
   if (!gotvalidserver || 
       !(newconn = new_socks_request(__fd, connaddr, &server_address, path))) {
      errno = ECONNREFUSED;
      return(-1);
   } else {
      /* Now we call the main function to handle the connect. */
      rc = handle_request(newconn);
      /* If the request completed immediately it mustn't have been
       * a non blocking socket, in this case we don't need to know
       * about this socket anymore. */
      if ((newconn->state == FAILED) || (newconn->state == DONE))
         kill_socks_request(newconn);
      errno = rc;
      return((rc ? -1 : 0));
   }
}

int select(SELECT_SIGNATURE) {
   int nevents = 0;
   int rc = 0;
   int setevents = 0;
   int monitoring = 0;
   struct connreq *conn, *nextconn;
   fd_set mywritefds, myreadfds, myexceptfds;

   /* If we're not currently managing any requests we can just 
    * leave here */
   if (!requests) {
      show_msg(MSGDEBUG, "No requests waiting, calling real select\n");
      return(realselect(n, readfds, writefds, exceptfds, timeout));
   }

   get_environment();

   show_msg(MSGDEBUG, "Intercepted call to select with %d fds, "
            "0x%08x 0x%08x 0x%08x, timeout %08x\n", n, 
            readfds, writefds, exceptfds, timeout);

   for (conn = requests; conn != NULL; conn = conn->next) {
      if ((conn->state == FAILED) || (conn->state == DONE))
         continue;
      conn->selectevents = 0;
      show_msg(MSGDEBUG, "Checking requests for socks enabled socket %d\n",
               conn->sockid);
      conn->selectevents |= (writefds ? (FD_ISSET(conn->sockid, writefds) ? WRITE : 0) : 0);
      conn->selectevents |= (readfds ? (FD_ISSET(conn->sockid, readfds) ? READ : 0) : 0);
      conn->selectevents |= (exceptfds ? (FD_ISSET(conn->sockid, exceptfds) ? EXCEPT : 0) : 0);
      if (conn->selectevents) {
         show_msg(MSGDEBUG, "Socket %d was set for events\n", conn->sockid);
         monitoring = 1;
      }
   }

   if (!monitoring)
      return(realselect(n, readfds, writefds, exceptfds, timeout));

   /* This is our select loop. In it we repeatedly call select(). We 
    * pass select the same fdsets as provided by the caller except we
    * modify the fdsets for the sockets we're managing to get events
    * we're interested in (while negotiating with the socks server). When
    * events we're interested in happen we go off and process the result
    * ourselves, without returning the events to the caller. The loop
    * ends when an event which isn't one we need to handle occurs or 
    * the select times out */
   do {
      /* Copy the clients fd events, we'll change them as we wish */
      if (readfds)
         memcpy(&myreadfds, readfds, sizeof(myreadfds));
      else
         FD_ZERO(&myreadfds);
      if (writefds)
         memcpy(&mywritefds, writefds, sizeof(mywritefds));
      else
         FD_ZERO(&mywritefds);
      if (exceptfds)
         memcpy(&myexceptfds, exceptfds, sizeof(myexceptfds));
      else
         FD_ZERO(&myexceptfds);

      /* Now enable our sockets for the events WE want to hear about */
      for (conn = requests; conn != NULL; conn = conn->next) {
         if ((conn->state == FAILED) || (conn->state == DONE) ||
             (conn->selectevents == 0))
            continue;
         /* We always want to know about socket exceptions */
         FD_SET(conn->sockid, &myexceptfds);
         /* If we're waiting for a connect or to be able to send
          * on a socket we want to get write events */
         if ((conn->state == SENDING) || (conn->state == CONNECTING))
            FD_SET(conn->sockid,&mywritefds);
         else
            FD_CLR(conn->sockid,&mywritefds);
         /* If we're waiting to receive data we want to get 
          * read events */
         if (conn->state == RECEIVING)
            FD_SET(conn->sockid,&myreadfds);
         else
            FD_CLR(conn->sockid,&myreadfds);
      }

      nevents = realselect(n, &myreadfds, &mywritefds, &myexceptfds, timeout);
      /* If there were no events we must have timed out or had an error */
      if (nevents <= 0)
         break;

      /* Loop through all the sockets we're monitoring and see if 
       * any of them have had events */
      for (conn = requests; conn != NULL; conn = nextconn) {
         nextconn = conn->next;
         if ((conn->state == FAILED) || (conn->state == DONE))
            continue;
         show_msg(MSGDEBUG, "Checking socket %d for events\n", conn->sockid);
         /* Clear all the events on the socket (if any), we'll reset
          * any that are necessary later. */
         setevents = 0;
         if (FD_ISSET(conn->sockid, &mywritefds))  {
            nevents--;
            setevents |= WRITE;
            show_msg(MSGDEBUG, "Socket had write event\n");
            FD_CLR(conn->sockid, &mywritefds);
         }
         if (FD_ISSET(conn->sockid, &myreadfds))  {
            nevents--;
            setevents |= READ;
            show_msg(MSGDEBUG, "Socket had write event\n");
            FD_CLR(conn->sockid, &myreadfds);
         }
         if (FD_ISSET(conn->sockid, &myexceptfds))  {
            nevents--;
            setevents |= EXCEPT;
            show_msg(MSGDEBUG, "Socket had except event\n");
            FD_CLR(conn->sockid, &myexceptfds);
         }

         if (!setevents) {
            show_msg(MSGDEBUG, "No events on socket %d\n", conn->sockid);
            continue;
         }

         if (setevents & EXCEPT) {
            conn->state = FAILED;
         } else {
            rc = handle_request(conn);
         }
         /* If the connection hasn't failed or completed there is nothing
          * to report to the client */
         if ((conn->state != FAILED) && 
             (conn->state != DONE))  
            continue;

         /* Ok, the connection is completed, for good or for bad. We now
          * hand back the relevant events to the caller. We don't delete the
          * connection though since the caller should call connect() to 
          * check the status, we delete it then */

         if (conn->state == FAILED) {
            /* Damn, the connection failed. Whatever the events the socket
             * was selected for we flag */
            if (conn->selectevents & EXCEPT) {
               FD_SET(conn->sockid, &myexceptfds);
               nevents++;
            }
            if (conn->selectevents & READ) {
               FD_SET(conn->sockid, &myreadfds);
               nevents++;
            }
            if (conn->selectevents & WRITE) {
               FD_SET(conn->sockid, &mywritefds);
               nevents++;
            }
            /* We should use setsockopt to set the SO_ERROR errno for this 
             * socket, but this isn't allowed for some silly reason which 
             * leaves us a bit hamstrung.
             * We don't delete the request so that hopefully we can 
             * return the error on the socket if they call connect() on it */
         } else {
            /* The connection is done,  if the client selected for 
             * writing we can go ahead and signal that now (since the socket must
             * be ready for writing), otherwise we'll just let the select loop
             * come around again (since we can't flag it for read, we don't know
             * if there is any data to be read and can't be bothered checking) */
            if (conn->selectevents & WRITE) {
               FD_SET(conn->sockid, &mywritefds);
               nevents++;
            }
         }
      }
   } while (nevents == 0);

   show_msg(MSGDEBUG, "Finished intercepting select(), %d events\n", nevents);

   /* Now copy our event blocks back to the client blocks */
   if (readfds)
      memcpy(readfds, &myreadfds, sizeof(myreadfds));
   if (writefds)
      memcpy(writefds, &mywritefds, sizeof(mywritefds));
   if (exceptfds)
      memcpy(exceptfds, &myexceptfds, sizeof(myexceptfds));

   return(nevents);
}