[tor-commits] [torsocks/osx] Rename tsocks.c to torsocks.c
hoganrobert at torproject.org
hoganrobert at torproject.org
Sun Oct 23 18:27:14 UTC 2011
commit 896c413ef4b226caa45e20c4f8bb89b5ed057d6e
Author: Robert Hogan <robert at roberthogan.net>
Date: Mon Feb 14 20:02:41 2011 +0000
Rename tsocks.c to torsocks.c
---
configure.in | 14 +-
src/Makefile.am | 2 +-
src/torsocks.c | 1757 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/tsocks.c | 1757 -------------------------------------------------------
4 files changed, 1765 insertions(+), 1765 deletions(-)
diff --git a/configure.in b/configure.in
index 7cfa02c..a962b55 100644
--- a/configure.in
+++ b/configure.in
@@ -4,12 +4,12 @@ AC_CONFIG_HEADER(config.h)
AM_INIT_AUTOMAKE(torsocks, 0.1)
-dnl Our default prefix is /usr/ since most people will be using tsocks
+dnl Our default prefix is /usr/ since most people will be using torsocks
dnl on Linux systems and that /usr/local/ stuff annoys them
dnl AC_PREFIX_DEFAULT(/usr)
dnl if libdir hasn't been set by the user default it to /lib since
-dnl tsocks needs to be on the root partition if put in the
+dnl torsocks needs to be on the root partition if put in the
dnl /etc/ld.so.preload file
dnl test "$libdir" = "\${exec_prefix}/lib" && libdir="/lib"
@@ -19,13 +19,13 @@ AC_ARG_ENABLE(socksdns,
AC_ARG_ENABLE(tordns,
[ --disable-tordns don't override name lookup calls to use SOCKS ])
AC_ARG_ENABLE(debug,
-[ --disable-debug disable ALL error messages from tsocks ])
+[ --disable-debug disable ALL error messages from torsocks ])
AC_ARG_ENABLE(oldmethod,
[ --enable-oldmethod use the old method to override connect ])
AC_ARG_ENABLE(hostnames,
[ --enable-hostnames enable hostname lookups for socks servers ])
AC_ARG_ENABLE(envconf,
-[ --disable-envconf do not allow TSOCKS_CONF_FILE to specify configuration file ])
+[ --disable-envconf do not allow TORSOCKS_CONF_FILE to specify configuration file ])
dnl -----------------------------------
dnl Get hostname and other information.
@@ -108,11 +108,11 @@ AC_CHECK_FUNC(inet_aton, AC_DEFINE([HAVE_INET_ATON],[],[Description]), [
LIBS="${LIBS} -lnsl" ], [
AC_MSG_ERROR("Neither inet_aton or inet_addr present")])])])
-dnl Look for gethostbyname (needed by tsocks and inspectsocks)
+dnl Look for gethostbyname (needed by torsocks)
AC_CHECK_FUNC(gethostbyname, AC_DEFINE([HAVE_GETHOSTBYNAME],[],[Description]), [
AC_CHECK_LIB(xnet, gethostbyname, AC_DEFINE([HAVE_GETHOSTBYNAME],[],[Description]), [
AC_MSG_ERROR(["gethostbyname not found, name lookups in " \
- "tsocks and inspectsocks disabled"])])])
+ "torsocks disabled"])])])
dnl The simple programs (saveme and inspectsocks) have no further
dnl requirements, so save the libs needed here and use them in the
@@ -539,7 +539,7 @@ NAMES='s, msg, flags'
AC_DEFINE_UNQUOTED(SENDMSG_SIGNATURE, [${PROTO}], [Description])
AC_DEFINE_UNQUOTED([SENDMSG_ARGNAMES],[${NAMES}],[Argument names])
-dnl Output the special librarys (libdl etc needed for tsocks)
+dnl Output the special librarys (libdl etc needed for torsocks)
SPECIALLIBS=${LIBS}
AC_SUBST(SPECIALLIBS)
LIBS=${SIMPLELIBS}
diff --git a/src/Makefile.am b/src/Makefile.am
index aa61a21..134dc42 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -26,7 +26,7 @@ torsocksconfmanpage_DATA = torsocks.conf.5
# Install main library to $(prefix)/lib/tor (must match torsocks.in)
lib_LTLIBRARIES = libtorsocks.la
-libtorsocks_la_SOURCES = tsocks.c common.c parser.c dead_pool.c darwin_warts.c
+libtorsocks_la_SOURCES = torsocks.c common.c parser.c dead_pool.c darwin_warts.c
libtorsocks_la_LDFLAGS = -version-info 1:0:0
#libtorsocks_la_CFLAGS = -nostartfiles
diff --git a/src/torsocks.c b/src/torsocks.c
new file mode 100644
index 0000000..a227469
--- /dev/null
+++ b/src/torsocks.c
@@ -0,0 +1,1757 @@
+/***************************************************************************
+ * *
+ * $Id: tsocks.c,v 1.5 2008-07-06 15:17:35 hoganrobert Exp $ *
+ * *
+ * Copyright (C) 2008 by Robert Hogan *
+ * robert at roberthogan.net *
+ * *
+ * This program 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, *
+ * 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 Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************
+ * *
+ * This is a modified version of a source file from the tsocks project. *
+ * Original copyright notice from tsocks source file follows: *
+ * *
+ ***************************************************************************/
+/*
+
+ TSOCKS - Wrapper library for transparent SOCKS
+
+ Copyright (C) 2000 Shaun Clowes
+
+ This program 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,
+ 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
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* PreProcessor Defines */
+#include <config.h>
+
+/*Defining _NONSTD_SOURCE causes library and kernel calls to behave as closely
+to Mac OS X 10.3's library and kernel calls as possible.*/
+#if defined(__APPLE__) || defined(__darwin__)
+/*
+From 'man compat' in OSX:
+64-BIT COMPILATION
+ When compiling for 64-bit architectures, the __LP64__ macro will be defined to 1, and UNIX conformance
+ is always on (the _DARWIN_FEATURE_UNIX_CONFORMANCE macro will also be defined to the SUS conformance
+ level). Defining _NONSTD_SOURCE will cause a compilation error.
+*/
+#if !defined(__LP64__)
+#define _NONSTD_SOURCE 1
+#endif
+#include <sys/socket.h>
+#endif
+
+
+#ifdef USE_GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+/* Global configuration variables */
+const char *torsocks_progname = "libtorsocks"; /* Name used in err msgs */
+
+/* Header Files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <common.h>
+#include <pthread.h>
+#include <stdarg.h>
+#if !defined(__APPLE__) && !defined(__darwin__)
+#include <sys/socket.h>
+#endif
+#include <resolv.h>
+#include <parser.h>
+#include <tsocks.h>
+#include "dead_pool.h"
+
+/* Some function names are macroized on Darwin. Allow those names
+ to expand accordingly. */
+#define EXPAND_GUTS(x) tsocks_##x##_guts
+#define EXPAND_GUTS_NAME(x) EXPAND_GUTS(x)
+
+/* Global Declarations */
+static dead_pool *pool = NULL;
+
+/* Function prototypes for original functions that we patch */
+#ifdef SUPPORT_RES_API
+int (*realres_init)(void);
+#endif
+#define PATCH_TABLE_EXPANSION(e,r,s,n,b,m) r (*real##n)(s##SIGNATURE);
+#include "expansion_table.h"
+#undef PATCH_TABLE_EXPANSION
+#undef DARWIN_EXPANSION
+
+static struct parsedfile config;
+static struct connreq *requests = NULL;
+static int suid = 0;
+static char *conffile = NULL;
+static volatile int tsocks_init_complete = 0;
+
+/* Exported Function Prototypes */
+void __attribute__ ((constructor)) tsocks_init(void);
+
+/* Function prototypes for our patches */
+#ifdef SUPPORT_RES_API
+int res_init(void);
+#endif
+
+#define PATCH_TABLE_EXPANSION(e,r,s,n,b,m) r n(s##SIGNATURE);
+#define DARWIN_EXPANSION(e,r,s,n,b,m) r n(s##SIGNATURE) __asm("_" m);
+#include "expansion_table.h"
+#undef PATCH_TABLE_EXPANSION
+#undef DARWIN_EXPANSION
+
+/* Private Function Prototypes */
+/* no tsocks_res_init_guts */
+#define PATCH_TABLE_EXPANSION(e,r,s,n,b,m) r tsocks_##b##_guts(s##SIGNATURE, r (*original_##b)(s##SIGNATURE));
+#include "expansion_table.h"
+#undef PATCH_TABLE_EXPANSION
+
+
+static int get_config();
+static int get_environment();
+static int connect_server(struct connreq *conn);
+static int send_socks_request(struct connreq *conn);
+static struct connreq *new_socks_request(int sockid, struct sockaddr_in *connaddr,
+ struct sockaddr_in *serveraddr,
+ struct serverent *path);
+static void kill_socks_request(struct connreq *conn);
+static int handle_request(struct connreq *conn);
+static struct connreq *find_socks_request(int sockid, int includefailed);
+static int connect_server(struct connreq *conn);
+static int send_socks_request(struct connreq *conn);
+static int send_socksv4_request(struct connreq *conn);
+static int send_socksv5_method(struct connreq *conn);
+static int send_socksv5_connect(struct connreq *conn);
+static int send_buffer(struct connreq *conn);
+static int recv_buffer(struct connreq *conn);
+static int read_socksv5_method(struct connreq *conn);
+static int read_socksv4_req(struct connreq *conn);
+static int read_socksv5_connect(struct connreq *conn);
+static int read_socksv5_auth(struct connreq *conn);
+static int deadpool_init(void);
+static int send_socksv4a_request(struct connreq *conn, const char *onion_host);
+
+static pthread_mutex_t tsocks_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void tsocks_init(void)
+{
+#define LOAD_ERROR(s,l) { \
+ char *error; \
+ error = dlerror(); \
+ show_msg(l, "The symbol %s() was not found in any shared " \
+ "library. The error reported was: %s!\n", s, \
+ (error)?error:"not found"); \
+ dlerror(); \
+ }
+ pthread_mutex_lock(&tsocks_init_mutex);
+
+ /* We only need to be called once */
+ if (tsocks_init_complete)
+ return;
+
+ /* Not strictly true yet, but prevents us getting called while still in progress.*/
+ /* This has been observed on Snow Leopard for instance. */
+ tsocks_init_complete = 1;
+
+ show_msg(MSGWARN, "In tsocks_init \n");
+
+ get_environment();
+ get_config();
+
+ show_msg(MSGWARN, "In tsocks_init after env/config\n");
+
+#ifdef USE_OLD_DLSYM
+ void *lib;
+#endif
+
+ /* We could do all our initialization here, but to be honest */
+ /* most programs that are run won't use our services, so */
+ /* we do our general initialization on first call */
+
+ /* Determine the logging level */
+ suid = (getuid() != geteuid());
+
+ dlerror();
+#ifndef USE_OLD_DLSYM
+ #ifdef SUPPORT_RES_API
+ if ((realres_init = dlsym(RTLD_NEXT, "res_init")) == NULL)
+ LOAD_ERROR("res_init", MSGERR);
+ #endif
+ #define PATCH_TABLE_EXPANSION(e,r,s,n,b,m) if ((real##n = dlsym(RTLD_NEXT, m)) == NULL) LOAD_ERROR(m, MSG##e);
+ #include "expansion_table.h"
+ #undef PATCH_TABLE_EXPANSION
+#else
+ lib = dlopen(LIBCONNECT, RTLD_LAZY);
+ realconnect = dlsym(lib, "connect");
+ realselect = dlsym(lib, "select");
+ realpoll = dlsym(lib, "poll");
+ realgethostbyname = dlsym(lib, "gethostbyname");
+ realgethostbyaddr = dlsym(lib, "gethostbyaddr");
+ realgetaddrinfo = dlsym(lib, "getaddrinfo");
+ realgetipnodebyname = dlsym(lib, "getipnodebyname");
+ realsendto = dlsym(lib, "sendto");
+ realsendmsg = dlsym(lib, "sendmsg");
+ dlclose(lib);
+ lib = dlopen(LIBC, RTLD_LAZY);
+ realclose = dlsym(lib, "close");
+ dlclose(lib);
+ #ifdef SUPPORT_RES_API
+ lib = dlopen(LIBRESOLV, RTLD_LAZY);
+ realres_init = dlsym(lib, "res_init");
+ realresquery = dlsym(lib, "res_query");
+ realressend = dlsym(lib, "res_send");
+ realresquerydomain = dlsym(lib, "res_querydomain");
+ realressearch = dlsym(lib, "res_search");
+ dlclose(lib);
+ #endif
+#endif
+ /* Unfortunately, we can't do this lazily because otherwise our mmap'd
+ area won't be shared across fork()s. */
+ if (!deadpool_init()) {
+ show_msg(MSGERR, "Fatal error: exiting\n");
+ exit(1);
+ }
+
+ tsocks_init_complete=1;
+ pthread_mutex_unlock(&tsocks_init_mutex);
+
+ show_msg(MSGWARN, "Exit tsocks_init \n");
+}
+
+static int get_environment()
+{
+ static int done = 0;
+ int loglevel = MSGERR;
+ char *logfile = NULL;
+ char *env;
+
+ if (done)
+ return(0);
+
+ /* Determine the logging level */
+ if ((env = getenv("TORSOCKS_DEBUG")))
+ loglevel = atoi(env);
+ if (((env = getenv("TORSOCKS_DEBUG_FILE"))) && !suid)
+ logfile = env;
+ set_log_options(loglevel, logfile, 1);
+
+ done = 1;
+
+ return(0);
+}
+
+static int get_config ()
+{
+ static int done = 0;
+
+ if (done)
+ return(0);
+
+ /* Determine the location of the config file */
+#ifdef ALLOW_ENV_CONFIG
+ if (!suid)
+ conffile = getenv("TORSOCKS_CONF_FILE");
+#endif
+
+ /* Read in the config file */
+ read_config(conffile, &config);
+ if (config.paths)
+ show_msg(MSGDEBUG, "First lineno for first path is %d\n", config.paths->lineno);
+
+ done = 1;
+
+ return(0);
+}
+
+/* Patch trampoline functions */
+/* no tsocks_res_init_guts */
+#define PATCH_TABLE_EXPANSION(e,r,s,n,b,m) \
+ r n(s##SIGNATURE) { \
+ if (!real##n) { \
+ dlerror(); \
+ if ((real##n = dlsym(RTLD_NEXT, m)) == NULL) \
+ LOAD_ERROR(m, MSG##e); \
+ } \
+ return tsocks_##b##_guts(s##ARGNAMES, real##n); \
+ }
+#include "expansion_table.h"
+#undef PATCH_TABLE_EXPANSION
+
+int tsocks_connect_guts(CONNECT_SIGNATURE, int (*original_connect)(CONNECT_SIGNATURE))
+{
+ struct sockaddr_in *connaddr;
+ struct sockaddr_in peer_address;
+ struct sockaddr_in server_address;
+ int gotvalidserver = 0, rc;
+ socklen_t namelen = sizeof(peer_address);
+ int sock_type = -1;
+ socklen_t sock_type_len = sizeof(sock_type);
+ int res = -1;
+ struct serverent *path;
+ struct connreq *newconn;
+
+ /* See comment in close() */
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ /* If the real connect doesn't exist, we're stuffed */
+ if (original_connect == 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);
+
+ show_msg(MSGDEBUG, "sin_family: %i\n", connaddr->sin_family);
+
+ show_msg(MSGDEBUG, "sockopt: %i \n", sock_type);
+
+ /* If the address is local refuse it. We do this because it could
+ be a TCP DNS request to a local DNS server.*/
+ if (!(is_local(&config, &(connaddr->sin_addr))) &&
+ !is_dead_address(pool, connaddr->sin_addr.s_addr)) {
+ char buf[16];
+ inet_ntop(AF_INET, &(connaddr->sin_addr), buf, sizeof(buf));
+ show_msg(MSGERR, "connect: Connection is to a local address (%s), may be a "
+ "TCP DNS request to a local DNS server so have to reject to be safe. "
+ "Please report a bug to http://code.google.com/p/torsocks/issues/entry if "
+ "this is preventing a program from working properly with torsocks.\n", buf);
+ return -1;
+ }
+
+ /* If this isn't an INET socket we can't */
+ /* handle it, just call the real connect now */
+ if ((connaddr->sin_family != AF_INET)) {
+ show_msg(MSGDEBUG, "connect: Connection isn't IPv4, ignoring\n");
+ return(original_connect(__fd, __addr, __len));
+ }
+
+ /* If this a UDP socket */
+ /* then we refuse it, since it is probably a DNS request */
+ if ((sock_type != SOCK_STREAM)) {
+ show_msg(MSGERR, "connect: Connection is a UDP or ICMP stream, may be a "
+ "DNS request or other form of leak: rejecting.\n");
+ return -1;
+ }
+
+ /* 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(original_connect(__fd, __addr, __len));
+ }
+
+ show_msg(MSGDEBUG, "Got connection request for socket %d to "
+ "%s\n", __fd, inet_ntoa(connaddr->sin_addr));
+
+ /* 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 tsocks_select_guts(SELECT_SIGNATURE, int (*original_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(original_select(n, readfds, writefds, exceptfds, timeout));
+ }
+
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ 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(original_select(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 = original_select(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);
+}
+
+int tsocks_poll_guts(POLL_SIGNATURE, int (*original_poll)(POLL_SIGNATURE))
+{
+ int nevents = 0;
+ int rc = 0;
+ unsigned int i;
+ int setevents = 0;
+ int monitoring = 0;
+ struct connreq *conn, *nextconn;
+
+ /* If we're not currently managing any requests we can just
+ * leave here */
+ if (!requests)
+ return(original_poll(ufds, nfds, timeout));
+
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ show_msg(MSGDEBUG, "Intercepted call to poll with %d fds, "
+ "0x%08x timeout %d\n", nfds, ufds, timeout);
+
+ for (conn = requests; conn != NULL; conn = conn->next)
+ conn->selectevents = 0;
+
+ /* Record what events on our sockets the caller was interested
+ * in */
+ for (i = 0; i < nfds; i++) {
+ if (!(conn = find_socks_request(ufds[i].fd, 0)))
+ continue;
+ show_msg(MSGDEBUG, "Have event checks for socks enabled socket %d\n",
+ conn->sockid);
+ conn->selectevents = ufds[i].events;
+ monitoring = 1;
+ }
+
+ if (!monitoring)
+ return(original_poll(ufds, nfds, timeout));
+
+ /* This is our poll loop. In it we repeatedly call poll(). We
+ * pass select the same event list as provided by the caller except we
+ * modify the events 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 poll times out */
+ do {
+ /* Enable our sockets for the events WE want to hear about */
+ for (i = 0; i < nfds; i++) {
+ if (!(conn = find_socks_request(ufds[i].fd, 0)))
+ continue;
+
+ /* We always want to know about socket exceptions but they're
+ * always returned (i.e they don't need to be in the list of
+ * wanted events to be returned by the kernel */
+ ufds[i].events = 0;
+
+ /* 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))
+ ufds[i].events |= POLLOUT;
+ /* If we're waiting to receive data we want to get
+ * read events */
+ if (conn->state == RECEIVING)
+ ufds[i].events |= POLLIN;
+ }
+
+ nevents = original_poll(ufds, nfds, 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;
+
+ /* Find the socket in the poll list */
+ for (i = 0; ((i < nfds) && (ufds[i].fd != conn->sockid)); i++)
+ /* Empty Loop */;
+ if (i == nfds)
+ continue;
+
+ show_msg(MSGDEBUG, "Checking socket %d for events\n", conn->sockid);
+
+ if (!ufds[i].revents) {
+ show_msg(MSGDEBUG, "No events on socket\n");
+ continue;
+ }
+
+ /* Clear any read or write events on the socket, we'll reset
+ * any that are necessary later. */
+ setevents = ufds[i].revents;
+ if (setevents & POLLIN) {
+ show_msg(MSGDEBUG, "Socket had read event\n");
+ ufds[i].revents &= ~POLLIN;
+ nevents--;
+ }
+ if (setevents & POLLOUT) {
+ show_msg(MSGDEBUG, "Socket had write event\n");
+ ufds[i].revents &= ~POLLOUT;
+ nevents--;
+ }
+ if (setevents & (POLLERR | POLLNVAL | POLLHUP))
+ show_msg(MSGDEBUG, "Socket had error event\n");
+
+ /* Now handle this event */
+ if (setevents & (POLLERR | POLLNVAL | POLLHUP)) {
+ 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. Just copy back the error events
+ * from the poll call, error events are always valid even if not
+ * requested by the client */
+ /* 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 polled 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 & POLLOUT) {
+ setevents |= POLLOUT;
+ nevents++;
+ }
+ }
+ }
+ } while (nevents == 0);
+
+ show_msg(MSGDEBUG, "Finished intercepting poll(), %d events\n", nevents);
+
+ /* Now restore the events polled in each of the blocks */
+ for (i = 0; i < nfds; i++) {
+ if (!(conn = find_socks_request(ufds[i].fd, 1)))
+ continue;
+ ufds[i].events = conn->selectevents;
+ }
+
+ return(nevents);
+}
+
+int tsocks_close_guts(CLOSE_SIGNATURE, int (*original_close)(CLOSE_SIGNATURE))
+{
+ int rc;
+ struct connreq *conn;
+
+ /* If we're not currently managing any requests we can just
+ * leave here */
+ if (!requests) {
+ show_msg(MSGDEBUG, "No requests waiting, calling real close\n");
+ return(original_close(fd));
+ }
+
+ /* If we are called before this symbol has been dlopened then try
+ loading symbols now. This is a workaround for a problem I don't
+ really understand and have only encountered when using torsocks
+ with svn on Fedora 10, so definitely a hack. */
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ if (original_close == NULL) {
+ show_msg(MSGERR, "Unresolved symbol: close\n");
+ return(-1);
+ }
+
+ show_msg(MSGDEBUG, "Call to close(%d)\n", fd);
+
+ rc = original_close(fd);
+
+ /* If we have this fd in our request handling list we
+ * remove it now */
+ if ((conn = find_socks_request(fd, 1))) {
+ show_msg(MSGDEBUG, "Call to close() received on file descriptor "
+ "%d which is a connection request of status %d\n",
+ conn->sockid, conn->state);
+ kill_socks_request(conn);
+ }
+
+ return(rc);
+}
+
+/* If we are not done setting up the connection yet, return
+ * -1 and ENOTCONN, otherwise call getpeername
+ *
+ * This is necessary since some applications, when using non-blocking connect,
+ * (like ircII) use getpeername() to find out if they are connected already.
+ *
+ * This results in races sometimes, where the client sends data to the socket
+ * before we are done with the socks connection setup. Another solution would
+ * be to intercept send().
+ *
+ * This could be extended to actually set the peername to the peer the
+ * client application has requested, but not for now.
+ *
+ * PP, Sat, 27 Mar 2004 11:30:23 +0100
+ */
+
+int tsocks_getpeername_guts(GETPEERNAME_SIGNATURE,
+ int (*original_getpeername)(GETPEERNAME_SIGNATURE))
+{
+ struct connreq *conn;
+ int rc;
+
+ /* See comment in close() */
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ if (original_getpeername == NULL) {
+ show_msg(MSGERR, "Unresolved symbol: getpeername\n");
+ return(-1);
+ }
+
+ show_msg(MSGDEBUG, "Call to getpeername for fd %d\n", __fd);
+
+
+ rc = original_getpeername(__fd, __name, __namelen);
+ if (rc == -1)
+ return rc;
+
+ /* Are we handling this connect? */
+ if ((conn = find_socks_request(__fd, 1))) {
+ /* While we are at it, we might was well try to do something useful */
+ handle_request(conn);
+
+ if (conn->state != DONE) {
+ errno = ENOTCONN;
+ return(-1);
+ }
+ }
+ return rc;
+}
+
+static struct connreq *new_socks_request(int sockid, struct sockaddr_in *connaddr,
+ struct sockaddr_in *serveraddr,
+ struct serverent *path)
+{
+ struct connreq *newconn;
+
+ if ((newconn = malloc(sizeof(*newconn))) == NULL) {
+ /* Could not malloc, we're stuffed */
+ show_msg(MSGERR, "Could not allocate memory for new socks request\n");
+ return(NULL);
+ }
+
+ /* Add this connection to be proxied to the list */
+ memset(newconn, 0x0, sizeof(*newconn));
+ newconn->sockid = sockid;
+ newconn->state = UNSTARTED;
+ newconn->path = path;
+ memcpy(&(newconn->connaddr), connaddr, sizeof(newconn->connaddr));
+ memcpy(&(newconn->serveraddr), serveraddr, sizeof(newconn->serveraddr));
+ newconn->next = requests;
+ requests = newconn;
+
+ return(newconn);
+}
+
+static void kill_socks_request(struct connreq *conn)
+{
+ struct connreq *connnode;
+
+ if (requests == conn)
+ requests = conn->next;
+ else {
+ for (connnode = requests; connnode != NULL; connnode = connnode->next) {
+ if (connnode->next == conn) {
+ connnode->next = conn->next;
+ break;
+ }
+ }
+ }
+
+ free(conn);
+}
+
+static struct connreq *find_socks_request(int sockid, int includefinished)
+{
+ struct connreq *connnode;
+
+ for (connnode = requests; connnode != NULL; connnode = connnode->next) {
+ if (connnode->sockid == sockid) {
+ if (((connnode->state == FAILED) || (connnode->state == DONE)) &&
+ !includefinished)
+ break;
+ else
+ return(connnode);
+ }
+ }
+
+ return(NULL);
+}
+
+static int handle_request(struct connreq *conn)
+{
+ int rc = 0;
+ int i = 0;
+
+ show_msg(MSGDEBUG, "Beginning handle loop for socket %d\n", conn->sockid);
+
+ while ((rc == 0) &&
+ (conn->state != FAILED) &&
+ (conn->state != DONE) &&
+ (i++ < 20)) {
+ show_msg(MSGDEBUG, "In request handle loop for socket %d, "
+ "current state of request is %d\n", conn->sockid,
+ conn->state);
+ switch(conn->state) {
+ case UNSTARTED:
+ case CONNECTING:
+ rc = connect_server(conn);
+ break;
+ case CONNECTED:
+ rc = send_socks_request(conn);
+ break;
+ case SENDING:
+ rc = send_buffer(conn);
+ break;
+ case RECEIVING:
+ rc = recv_buffer(conn);
+ break;
+ case SENTV4REQ:
+ show_msg(MSGDEBUG, "Receiving reply to SOCKS V4 connect request\n");
+ conn->datalen = sizeof(struct sockrep);
+ conn->datadone = 0;
+ conn->state = RECEIVING;
+ conn->nextstate = GOTV4REQ;
+ break;
+ case GOTV4REQ:
+ rc = read_socksv4_req(conn);
+ break;
+ case SENTV5METHOD:
+ show_msg(MSGDEBUG, "Receiving reply to SOCKS V5 method negotiation\n");
+ conn->datalen = 2;
+ conn->datadone = 0;
+ conn->state = RECEIVING;
+ conn->nextstate = GOTV5METHOD;
+ break;
+ case GOTV5METHOD:
+ rc = read_socksv5_method(conn);
+ break;
+ case SENTV5AUTH:
+ show_msg(MSGDEBUG, "Receiving reply to SOCKS V5 authentication negotiation\n");
+ conn->datalen = 2;
+ conn->datadone = 0;
+ conn->state = RECEIVING;
+ conn->nextstate = GOTV5AUTH;
+ break;
+ case GOTV5AUTH:
+ rc = read_socksv5_auth(conn);
+ break;
+ case SENTV5CONNECT:
+ show_msg(MSGDEBUG, "Receiving reply to SOCKS V5 connect request\n");
+ conn->datalen = 10;
+ conn->datadone = 0;
+ conn->state = RECEIVING;
+ conn->nextstate = GOTV5CONNECT;
+ break;
+ case GOTV5CONNECT:
+ rc = read_socksv5_connect(conn);
+ break;
+ }
+ conn->err = errno;
+ }
+
+ if (i == 20)
+ show_msg(MSGERR, "Ooops, state loop while handling request %d\n",
+ conn->sockid);
+
+ show_msg(MSGDEBUG, "Handle loop completed for socket %d in state %d, "
+ "returning %d\n", conn->sockid, conn->state, rc);
+ return(rc);
+}
+
+static int connect_server(struct connreq *conn)
+{
+ int rc;
+
+ /* Connect this socket to the socks server */
+ show_msg(MSGDEBUG, "Connecting to %s port %d\n",
+ inet_ntoa(conn->serveraddr.sin_addr), ntohs(conn->serveraddr.sin_port));
+
+ rc = realconnect(conn->sockid, (CONNECT_SOCKARG) &(conn->serveraddr),
+ sizeof(conn->serveraddr));
+
+ show_msg(MSGDEBUG, "Connect returned %d, errno is %d\n", rc, errno);
+ if (rc && errno == EISCONN) {
+ rc = 0;
+ show_msg(MSGDEBUG, "Socket %d already connected to SOCKS server\n", conn->sockid);
+ conn->state = CONNECTED;
+ } else if (rc) {
+ if (errno != EINPROGRESS) {
+ show_msg(MSGERR, "Error %d attempting to connect to SOCKS "
+ "server (%s)\n", errno, strerror(errno));
+ conn->state = FAILED;
+ } else {
+ show_msg(MSGDEBUG, "Connection in progress\n");
+ conn->state = CONNECTING;
+ }
+ } else {
+ show_msg(MSGDEBUG, "Socket %d connected to SOCKS server\n", conn->sockid);
+ conn->state = CONNECTED;
+ }
+
+ return((rc ? errno : 0));
+}
+
+static int send_socks_request(struct connreq *conn)
+{
+ int rc = 0;
+
+ if (conn->path->type == 4) {
+ char *name = get_pool_entry(pool, &(conn->connaddr.sin_addr));
+ if(name != NULL)
+ rc = send_socksv4a_request(conn,name);
+ else
+ rc = send_socksv4_request(conn);
+ } else
+ rc = send_socksv5_method(conn);
+ return(rc);
+}
+
+static int send_socksv4a_request(struct connreq *conn,const char *onion_host)
+{
+ struct passwd *user;
+ struct sockreq *thisreq;
+ int endOfUser;
+ /* Determine the current username */
+ user = getpwuid(getuid());
+
+ thisreq = (struct sockreq *) conn->buffer;
+ endOfUser=sizeof(struct sockreq) +
+ (user == NULL ? 0 : strlen(user->pw_name)) + 1;
+
+ /* Check the buffer has enough space for the request */
+ /* and the user name */
+ conn->datalen = endOfUser+
+ (onion_host == NULL ? 0 : strlen(onion_host)) + 1;
+ if (sizeof(conn->buffer) < conn->datalen) {
+ show_msg(MSGERR, "The SOCKS username is too long");
+ conn->state = FAILED;
+ return(ECONNREFUSED);
+ }
+
+ /* Create the request */
+ thisreq->version = 4;
+ thisreq->command = 1;
+ thisreq->dstport = conn->connaddr.sin_port;
+ thisreq->dstip = htonl(1);
+
+ /* Copy the username */
+ strcpy((char *) thisreq + sizeof(struct sockreq),
+ (user == NULL ? "" : user->pw_name));
+
+ /* Copy the onion host */
+ strcpy((char *) thisreq + endOfUser,
+ (onion_host == NULL ? "" : onion_host));
+
+ conn->datadone = 0;
+ conn->state = SENDING;
+ conn->nextstate = SENTV4REQ;
+
+ return(0);
+}
+
+static int send_socksv4_request(struct connreq *conn)
+{
+ struct passwd *user;
+ struct sockreq *thisreq;
+
+ /* Determine the current username */
+ user = getpwuid(getuid());
+
+ thisreq = (struct sockreq *) conn->buffer;
+
+ /* Check the buffer has enough space for the request */
+ /* and the user name */
+ conn->datalen = sizeof(struct sockreq) +
+ (user == NULL ? 0 : strlen(user->pw_name)) + 1;
+ if (sizeof(conn->buffer) < conn->datalen) {
+ show_msg(MSGERR, "The SOCKS username is too long");
+ conn->state = FAILED;
+ return(ECONNREFUSED);
+ }
+
+ /* Create the request */
+ thisreq->version = 4;
+ thisreq->command = 1;
+ thisreq->dstport = conn->connaddr.sin_port;
+ thisreq->dstip = conn->connaddr.sin_addr.s_addr;
+
+ /* Copy the username */
+ strcpy((char *) thisreq + sizeof(struct sockreq),
+ (user == NULL ? "" : user->pw_name));
+
+ conn->datadone = 0;
+ conn->state = SENDING;
+ conn->nextstate = SENTV4REQ;
+
+ return(0);
+}
+
+static int send_socksv5_method(struct connreq *conn)
+{
+ char verstring[] = { 0x05, /* Version 5 SOCKS */
+ 0x02, /* No. Methods */
+ 0x00, /* Null Auth */
+ 0x02 }; /* User/Pass Auth */
+
+ show_msg(MSGDEBUG, "Constructing V5 method negotiation\n");
+ conn->state = SENDING;
+ conn->nextstate = SENTV5METHOD;
+ memcpy(conn->buffer, verstring, sizeof(verstring));
+ conn->datalen = sizeof(verstring);
+ conn->datadone = 0;
+
+ return(0);
+}
+
+static int send_socksv5_connect(struct connreq *conn)
+{
+ int namelen = 0;
+ char *name = NULL;
+ char constring[] = { 0x05, /* Version 5 SOCKS */
+ 0x01, /* Connect request */
+ 0x00, /* Reserved */
+ 0x01 }; /* IP Version 4 */
+
+ show_msg(MSGDEBUG, "Constructing V5 connect request\n");
+ conn->datadone = 0;
+ conn->state = SENDING;
+ conn->nextstate = SENTV5CONNECT;
+ memcpy(conn->buffer, constring, sizeof(constring));
+ conn->datalen = sizeof(constring);
+
+ show_msg(MSGDEBUG, "send_socksv5_connect: looking for: %s\n",
+ inet_ntoa(conn->connaddr.sin_addr));
+
+ name = get_pool_entry(pool, &(conn->connaddr.sin_addr));
+ if(name != NULL) {
+ namelen = strlen(name);
+ if(namelen > 255) /* "Can't happen" */
+ name = NULL;
+ }
+ if(name != NULL) {
+ show_msg(MSGDEBUG, "send_socksv5_connect: found it!\n");
+ /* Substitute the domain name from the pool into the SOCKS request. */
+ conn->buffer[3] = 0x03; /* Change the ATYP field */
+ conn->buffer[4] = namelen; /* Length of name */
+ conn->datalen++;
+ memcpy(&conn->buffer[conn->datalen], name, namelen);
+ conn->datalen += namelen;
+ } else {
+ show_msg(MSGDEBUG, "send_socksv5_connect: ip address not found\n");
+ /* Use the raw IP address */
+ memcpy(&conn->buffer[conn->datalen], &(conn->connaddr.sin_addr.s_addr),
+ sizeof(conn->connaddr.sin_addr.s_addr));
+ conn->datalen += sizeof(conn->connaddr.sin_addr.s_addr);
+ }
+ memcpy(&conn->buffer[conn->datalen], &(conn->connaddr.sin_port),
+ sizeof(conn->connaddr.sin_port));
+ conn->datalen += sizeof(conn->connaddr.sin_port);
+
+ return(0);
+}
+
+static int send_buffer(struct connreq *conn)
+{
+ int rc = 0;
+
+ show_msg(MSGDEBUG, "Writing to server (sending %d bytes)\n", conn->datalen);
+ while ((rc == 0) && (conn->datadone != conn->datalen)) {
+ rc = send(conn->sockid, conn->buffer + conn->datadone,
+ conn->datalen - conn->datadone, 0);
+ if (rc > 0) {
+ conn->datadone += rc;
+ rc = 0;
+ } else {
+ if (errno != EWOULDBLOCK)
+ show_msg(MSGDEBUG, "Write failed, %s\n", strerror(errno));
+ rc = errno;
+ }
+ }
+
+ if (conn->datadone == conn->datalen)
+ conn->state = conn->nextstate;
+
+ show_msg(MSGDEBUG, "Sent %d bytes of %d bytes in buffer, return code is %d\n",
+ conn->datadone, conn->datalen, rc);
+ return(rc);
+}
+
+static int recv_buffer(struct connreq *conn)
+{
+ int rc = 0;
+
+ show_msg(MSGDEBUG, "Reading from server (expecting %d bytes)\n", conn->datalen);
+ while ((rc == 0) && (conn->datadone != conn->datalen)) {
+ rc = recv(conn->sockid, conn->buffer + conn->datadone,
+ conn->datalen - conn->datadone, 0);
+ if (rc > 0) {
+ conn->datadone += rc;
+ rc = 0;
+ } else if (rc == 0) {
+ show_msg(MSGDEBUG, "Peer has shutdown but we only read %d of %d bytes.\n",
+ conn->datadone, conn->datalen);
+ rc = ENOTCONN; /* ENOTCONN seems like the most fitting error message */
+ } else {
+ if (errno != EWOULDBLOCK)
+ show_msg(MSGDEBUG, "Read failed, %s\n", strerror(errno));
+ rc = errno;
+ }
+ }
+
+ if (conn->datadone == conn->datalen)
+ conn->state = conn->nextstate;
+
+ show_msg(MSGDEBUG, "Received %d bytes of %d bytes expected, return code is %d\n",
+ conn->datadone, conn->datalen, rc);
+ return(rc);
+}
+
+static int read_socksv5_method(struct connreq *conn)
+{
+ struct passwd *nixuser;
+ char *uname, *upass;
+
+ /* See if we offered an acceptable method */
+ if (conn->buffer[1] == '\xff') {
+ show_msg(MSGERR, "SOCKS V5 server refused authentication methods\n");
+ conn->state = FAILED;
+ return(ECONNREFUSED);
+ }
+
+ /* If the socks server chose username/password authentication */
+ /* (method 2) then do that */
+ if ((unsigned short int) conn->buffer[1] == 2) {
+ show_msg(MSGDEBUG, "SOCKS V5 server chose username/password authentication\n");
+
+ /* Determine the current *nix username */
+ nixuser = getpwuid(getuid());
+
+ if (((uname = conn->path->defuser) == NULL) &&
+ ((uname = getenv("TORSOCKS_USERNAME")) == NULL) &&
+ ((uname = (nixuser == NULL ? NULL : nixuser->pw_name)) == NULL)) {
+ show_msg(MSGERR, "Could not get SOCKS username from "
+ "local passwd file, torsocks.conf "
+ "or $TORSOCKS_USERNAME to authenticate "
+ "with");
+ conn->state = FAILED;
+ return(ECONNREFUSED);
+ }
+
+ if (((upass = getenv("TORSOCKS_PASSWORD")) == NULL) &&
+ ((upass = conn->path->defpass) == NULL)) {
+ show_msg(MSGERR, "Need a password in torsocks.conf or "
+ "$TORSOCKS_PASSWORD to authenticate with");
+ conn->state = FAILED;
+ return(ECONNREFUSED);
+ }
+
+ /* Check that the username / pass specified will */
+ /* fit into the buffer */
+ if ((3 + strlen(uname) + strlen(upass)) >= sizeof(conn->buffer)) {
+ show_msg(MSGERR, "The supplied socks username or "
+ "password is too long");
+ conn->state = FAILED;
+ return(ECONNREFUSED);
+ }
+
+ conn->datalen = 0;
+ conn->buffer[conn->datalen] = '\x01';
+ conn->datalen++;
+ conn->buffer[conn->datalen] = (int8_t) strlen(uname);
+ conn->datalen++;
+ memcpy(&(conn->buffer[conn->datalen]), uname, strlen(uname));
+ conn->datalen = conn->datalen + strlen(uname);
+ conn->buffer[conn->datalen] = (int8_t) strlen(upass);
+ conn->datalen++;
+ memcpy(&(conn->buffer[conn->datalen]), upass, strlen(upass));
+ conn->datalen = conn->datalen + strlen(upass);
+
+ conn->state = SENDING;
+ conn->nextstate = SENTV5AUTH;
+ conn->datadone = 0;
+ } else
+ return(send_socksv5_connect(conn));
+
+ return(0);
+}
+
+static int read_socksv5_auth(struct connreq *conn)
+{
+
+ if (conn->buffer[1] != '\x00') {
+ show_msg(MSGERR, "SOCKS authentication failed, check username and password\n");
+ conn->state = FAILED;
+ return(ECONNREFUSED);
+ }
+
+ /* Ok, we authenticated ok, send the connection request */
+ return(send_socksv5_connect(conn));
+}
+
+static int read_socksv5_connect(struct connreq *conn)
+{
+
+ /* See if the connection succeeded */
+ if (conn->buffer[1] != '\x00') {
+ show_msg(MSGERR, "SOCKS V5 connect failed: ");
+ conn->state = FAILED;
+ switch ((int8_t) conn->buffer[1]) {
+ case 1:
+ show_msg(MSGERR, "General SOCKS server failure\n");
+ return(ECONNABORTED);
+ case 2:
+ show_msg(MSGERR, "Connection denied by rule\n");
+ return(ECONNABORTED);
+ case 3:
+ show_msg(MSGERR, "Network unreachable\n");
+ return(ENETUNREACH);
+ case 4:
+ show_msg(MSGERR, "Host unreachable\n");
+ return(EHOSTUNREACH);
+ case 5:
+ show_msg(MSGERR, "Connection refused\n");
+ return(ECONNREFUSED);
+ case 6:
+ show_msg(MSGERR, "TTL Expired\n");
+ return(ETIMEDOUT);
+ case 7:
+ show_msg(MSGERR, "Command not supported\n");
+ return(ECONNABORTED);
+ case 8:
+ show_msg(MSGERR, "Address type not supported\n");
+ return(ECONNABORTED);
+ default:
+ show_msg(MSGERR, "Unknown error\n");
+ return(ECONNABORTED);
+ }
+ }
+ conn->state = DONE;
+
+ return(0);
+}
+
+static int read_socksv4_req(struct connreq *conn)
+{
+ struct sockrep *thisrep;
+
+ thisrep = (struct sockrep *) conn->buffer;
+
+ if (thisrep->result != 90) {
+ show_msg(MSGERR, "SOCKS V4 connect rejected:\n");
+ conn->state = FAILED;
+ switch(thisrep->result) {
+ case 91:
+ show_msg(MSGERR, "SOCKS server refused connection\n");
+ return(ECONNREFUSED);
+ case 92:
+ show_msg(MSGERR, "SOCKS server refused connection "
+ "because of failed connect to identd "
+ "on this machine\n");
+ return(ECONNREFUSED);
+ case 93:
+ show_msg(MSGERR, "SOCKS server refused connection "
+ "because identd and this library "
+ "reported different user-ids\n");
+ return(ECONNREFUSED);
+ default:
+ show_msg(MSGERR, "Unknown reason\n");
+ return(ECONNREFUSED);
+ }
+ }
+ conn->state = DONE;
+
+ return(0);
+}
+
+#ifdef SUPPORT_RES_API
+int res_init(void)
+{
+ int rc;
+
+ if (!realres_init && ((realres_init = dlsym(RTLD_NEXT, "res_init")) == NULL))
+ LOAD_ERROR("res_init", MSGERR);
+
+ show_msg(MSGDEBUG, "Got res_init request\n");
+
+ /* See comment in close() */
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ if (realres_init == NULL) {
+ show_msg(MSGERR, "Unresolved symbol: res_init\n");
+ return(-1);
+ }
+ /* Call normal res_init */
+ rc = realres_init();
+
+ /* Force using TCP protocol for DNS queries */
+ _res.options |= RES_USEVC;
+ return(rc);
+}
+
+int EXPAND_GUTS_NAME(res_query)(RES_QUERY_SIGNATURE, int (*original_res_query)(RES_QUERY_SIGNATURE))
+{
+ int rc;
+
+ if (!original_res_query && ((original_res_query = dlsym(RTLD_NEXT, "res_query")) == NULL))
+ LOAD_ERROR("res_query", MSGERR);
+
+ show_msg(MSGDEBUG, "Got res_query request\n");
+
+ /* See comment in close() */
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ if (original_res_query == NULL) {
+ show_msg(MSGERR, "Unresolved symbol: res_query\n");
+ return(-1);
+ }
+
+ /* Ensure we force using TCP for DNS queries by calling res_init
+ above if it has not already been called.*/
+ if (!(_res.options & RES_INIT) || !(_res.options & RES_USEVC))
+ res_init();
+
+ /* Call normal res_query */
+ rc = original_res_query(dname, class, type, answer, anslen);
+
+ return(rc);
+}
+
+int EXPAND_GUTS_NAME(res_querydomain)(RES_QUERYDOMAIN_SIGNATURE, int (*original_res_querydomain)(RES_QUERYDOMAIN_SIGNATURE))
+{
+ int rc;
+
+ if (!original_res_querydomain &&
+ ((original_res_querydomain = dlsym(RTLD_NEXT, "res_querydomain")) == NULL))
+ LOAD_ERROR("res_querydoimain", MSGERR);
+
+ show_msg(MSGDEBUG, "Got res_querydomain request\n");
+
+ /* See comment in close() */
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ if (original_res_querydomain == NULL) {
+ show_msg(MSGERR, "Unresolved symbol: res_querydomain\n");
+ return(-1);
+ }
+
+ /* Ensure we force using TCP for DNS queries by calling res_init
+ above if it has not already been called.*/
+ if (!(_res.options & RES_INIT) || !(_res.options & RES_USEVC))
+ res_init();
+
+ /* Call normal res_querydomain */
+ rc = original_res_querydomain(name, domain, class, type, answer, anslen);
+
+ return(rc);
+}
+
+int EXPAND_GUTS_NAME(res_search)(RES_SEARCH_SIGNATURE, int (*original_res_search)(RES_SEARCH_SIGNATURE))
+{
+ int rc;
+
+ if (!original_res_search &&
+ ((original_res_search = dlsym(RTLD_NEXT, "res_search")) == NULL))
+ LOAD_ERROR("res_search", MSGERR);
+
+ show_msg(MSGDEBUG, "Got res_search request\n");
+
+ /* See comment in close() */
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ if (original_res_search == NULL) {
+ show_msg(MSGERR, "Unresolved symbol: res_search\n");
+ return(-1);
+ }
+
+ /* Ensure we force using TCP for DNS queries by calling res_init
+ above if it has not already been called.*/
+ if (!(_res.options & RES_INIT) || !(_res.options & RES_USEVC))
+ res_init();
+
+ /* Call normal res_search */
+ rc = original_res_search(dname, class, type, answer, anslen);
+
+ return(rc);
+}
+
+int EXPAND_GUTS_NAME(res_send)(RES_SEND_SIGNATURE, int (*original_res_send)(RES_SEND_SIGNATURE))
+{
+ int rc;
+
+ if (!original_res_send && ((original_res_send = dlsym(RTLD_NEXT, "res_send")) == NULL))
+ LOAD_ERROR("res_send", MSGERR);
+
+ show_msg(MSGDEBUG, "Got res_send request\n");
+
+ /* See comment in close() */
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ if (original_res_send == NULL) {
+ show_msg(MSGERR, "Unresolved symbol: res_send\n");
+ return(-1);
+ }
+
+ /* Ensure we force using TCP for DNS queries by calling res_init
+ above if it has not already been called.*/
+ if (!(_res.options & RES_INIT) || !(_res.options & RES_USEVC))
+ res_init();
+
+ /* Call normal res_send */
+ rc = original_res_send(msg, msglen, answer, anslen);
+
+ return(rc);
+}
+#endif
+
+static int deadpool_init(void)
+{
+ if (pool)
+ return 1;
+
+ if (!config.tordns_enabled) {
+ show_msg(MSGERR, "Tor DNS is disabled. Check your configuration.\n");
+ return 0;
+ }
+
+ get_environment();
+ get_config();
+ pool = init_pool(config.tordns_cache_size,
+ config.tordns_deadpool_range->localip,
+ config.tordns_deadpool_range->localnet,
+ config.defaultserver.address,
+ config.defaultserver.port);
+
+ if (!pool) {
+ show_msg(MSGERR, "Could not initialize reserved addresses for "
+ ".onion addresses. Torsocks will not work properly.\n");
+ return 0;
+ }
+ return 1;
+}
+
+struct hostent *tsocks_gethostbyname_guts(GETHOSTBYNAME_SIGNATURE, struct hostent *(*original_gethostbyname)(GETHOSTBYNAME_SIGNATURE))
+{
+ if (pool)
+ return our_gethostbyname(pool, name);
+ return original_gethostbyname(name);
+}
+
+struct hostent *tsocks_gethostbyaddr_guts(GETHOSTBYADDR_SIGNATURE, struct hostent *(*original_gethostbyaddr)(GETHOSTBYADDR_SIGNATURE))
+{
+ if (pool)
+ return our_gethostbyaddr(pool, addr, len, type);
+ return original_gethostbyaddr(addr, len, type);
+}
+
+int tsocks_getaddrinfo_guts(GETADDRINFO_SIGNATURE, int (*original_getaddrinfo)(GETADDRINFO_SIGNATURE))
+{
+ if (pool)
+ return our_getaddrinfo(pool, node, service, hints, res);
+ return original_getaddrinfo(node, service, hints, res);
+}
+
+struct hostent *tsocks_getipnodebyname_guts(GETIPNODEBYNAME_SIGNATURE, struct hostent *(*original_getipnodebyname)(GETIPNODEBYNAME_SIGNATURE))
+{
+ if (pool)
+ return our_getipnodebyname(pool, name, af, flags, error_num);
+ return original_getipnodebyname(name, af, flags, error_num);
+}
+
+ssize_t tsocks_sendto_guts(SENDTO_SIGNATURE, ssize_t (*original_sendto)(SENDTO_SIGNATURE))
+{
+ int sock_type = -1;
+ unsigned int sock_type_len = sizeof(sock_type);
+ struct sockaddr_in *connaddr;
+
+ /* See comment in close() */
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ /* If the real sendto doesn't exist, we're stuffed */
+ if (original_sendto == NULL) {
+ show_msg(MSGERR, "Unresolved symbol: sendto\n");
+ return(-1);
+ }
+
+ show_msg(MSGDEBUG, "Got sendto request\n");
+
+ /* Get the type of the socket */
+ getsockopt(s, SOL_SOCKET, SO_TYPE,
+ (void *) &sock_type, &sock_type_len);
+
+ show_msg(MSGDEBUG, "sockopt: %i\n", sock_type);
+
+ /* If this a UDP socket then we refuse it, since it is probably a DNS
+ request */
+ if ((sock_type != SOCK_STREAM)) {
+ show_msg(MSGERR, "sendto: Connection is a UDP or ICMP stream, may be a "
+ "DNS request or other form of leak: rejecting.\n");
+ return -1;
+ }
+
+ connaddr = (struct sockaddr_in *) to;
+
+ /* If there is no address in 'to', sendto will only work if we
+ already allowed the socket to connect(), so we let it through.
+ Likewise if the socket is not an Internet connection. */
+ if (connaddr && (connaddr->sin_family != AF_INET)) {
+ show_msg(MSGDEBUG, "Connection isn't an Internet socket ignoring\n");
+ }
+
+ return (ssize_t) original_sendto(s, buf, len, flags, to, tolen);
+}
+
+ssize_t tsocks_sendmsg_guts(SENDMSG_SIGNATURE, ssize_t (*original_sendmsg)(SENDMSG_SIGNATURE))
+{
+ int sock_type = -1;
+ unsigned int sock_type_len = sizeof(sock_type);
+ struct sockaddr_in *connaddr;
+
+ /* See comment in close() */
+ if (!tsocks_init_complete)
+ tsocks_init();
+
+ /* If the real sendmsg doesn't exist, we're stuffed */
+ if (original_sendmsg == NULL) {
+ show_msg(MSGERR, "Unresolved symbol: sendmsg\n");
+ return(-1);
+ }
+
+ show_msg(MSGDEBUG, "Got sendmsg request\n");
+
+ /* Get the type of the socket */
+ getsockopt(s, SOL_SOCKET, SO_TYPE,
+ (void *) &sock_type, &sock_type_len);
+
+ show_msg(MSGDEBUG, "sockopt: %i\n", sock_type);
+
+ if ((sock_type != SOCK_STREAM)) {
+ show_msg(MSGERR, "sendmsg: Connection is a UDP or ICMP stream, may be a "
+ "DNS request or other form of leak: rejecting.\n");
+ return -1;
+ }
+
+ connaddr = (struct sockaddr_in *) msg->msg_name;
+
+ /* If there is no address in msg_name, sendmsg will only work if we
+ already allowed the socket to connect(), so we let it through.
+ Likewise if the socket is not an Internet connection. */
+ if (connaddr && (connaddr->sin_family != AF_INET)) {
+ show_msg(MSGDEBUG, "Connection isn't an Internet socket\n");
+ }
+
+ return (ssize_t) original_sendmsg(s, msg, flags);
+}
+
diff --git a/src/tsocks.c b/src/tsocks.c
deleted file mode 100644
index a227469..0000000
--- a/src/tsocks.c
+++ /dev/null
@@ -1,1757 +0,0 @@
-/***************************************************************************
- * *
- * $Id: tsocks.c,v 1.5 2008-07-06 15:17:35 hoganrobert Exp $ *
- * *
- * Copyright (C) 2008 by Robert Hogan *
- * robert at roberthogan.net *
- * *
- * This program 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, *
- * 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 Foundation, Inc., *
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
- ***************************************************************************
- * *
- * This is a modified version of a source file from the tsocks project. *
- * Original copyright notice from tsocks source file follows: *
- * *
- ***************************************************************************/
-/*
-
- TSOCKS - Wrapper library for transparent SOCKS
-
- Copyright (C) 2000 Shaun Clowes
-
- This program 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,
- 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
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/* PreProcessor Defines */
-#include <config.h>
-
-/*Defining _NONSTD_SOURCE causes library and kernel calls to behave as closely
-to Mac OS X 10.3's library and kernel calls as possible.*/
-#if defined(__APPLE__) || defined(__darwin__)
-/*
-From 'man compat' in OSX:
-64-BIT COMPILATION
- When compiling for 64-bit architectures, the __LP64__ macro will be defined to 1, and UNIX conformance
- is always on (the _DARWIN_FEATURE_UNIX_CONFORMANCE macro will also be defined to the SUS conformance
- level). Defining _NONSTD_SOURCE will cause a compilation error.
-*/
-#if !defined(__LP64__)
-#define _NONSTD_SOURCE 1
-#endif
-#include <sys/socket.h>
-#endif
-
-
-#ifdef USE_GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-/* Global configuration variables */
-const char *torsocks_progname = "libtorsocks"; /* Name used in err msgs */
-
-/* Header Files */
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <dlfcn.h>
-#include <sys/types.h>
-#include <string.h>
-#include <strings.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/poll.h>
-#include <sys/time.h>
-#include <pwd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <common.h>
-#include <pthread.h>
-#include <stdarg.h>
-#if !defined(__APPLE__) && !defined(__darwin__)
-#include <sys/socket.h>
-#endif
-#include <resolv.h>
-#include <parser.h>
-#include <tsocks.h>
-#include "dead_pool.h"
-
-/* Some function names are macroized on Darwin. Allow those names
- to expand accordingly. */
-#define EXPAND_GUTS(x) tsocks_##x##_guts
-#define EXPAND_GUTS_NAME(x) EXPAND_GUTS(x)
-
-/* Global Declarations */
-static dead_pool *pool = NULL;
-
-/* Function prototypes for original functions that we patch */
-#ifdef SUPPORT_RES_API
-int (*realres_init)(void);
-#endif
-#define PATCH_TABLE_EXPANSION(e,r,s,n,b,m) r (*real##n)(s##SIGNATURE);
-#include "expansion_table.h"
-#undef PATCH_TABLE_EXPANSION
-#undef DARWIN_EXPANSION
-
-static struct parsedfile config;
-static struct connreq *requests = NULL;
-static int suid = 0;
-static char *conffile = NULL;
-static volatile int tsocks_init_complete = 0;
-
-/* Exported Function Prototypes */
-void __attribute__ ((constructor)) tsocks_init(void);
-
-/* Function prototypes for our patches */
-#ifdef SUPPORT_RES_API
-int res_init(void);
-#endif
-
-#define PATCH_TABLE_EXPANSION(e,r,s,n,b,m) r n(s##SIGNATURE);
-#define DARWIN_EXPANSION(e,r,s,n,b,m) r n(s##SIGNATURE) __asm("_" m);
-#include "expansion_table.h"
-#undef PATCH_TABLE_EXPANSION
-#undef DARWIN_EXPANSION
-
-/* Private Function Prototypes */
-/* no tsocks_res_init_guts */
-#define PATCH_TABLE_EXPANSION(e,r,s,n,b,m) r tsocks_##b##_guts(s##SIGNATURE, r (*original_##b)(s##SIGNATURE));
-#include "expansion_table.h"
-#undef PATCH_TABLE_EXPANSION
-
-
-static int get_config();
-static int get_environment();
-static int connect_server(struct connreq *conn);
-static int send_socks_request(struct connreq *conn);
-static struct connreq *new_socks_request(int sockid, struct sockaddr_in *connaddr,
- struct sockaddr_in *serveraddr,
- struct serverent *path);
-static void kill_socks_request(struct connreq *conn);
-static int handle_request(struct connreq *conn);
-static struct connreq *find_socks_request(int sockid, int includefailed);
-static int connect_server(struct connreq *conn);
-static int send_socks_request(struct connreq *conn);
-static int send_socksv4_request(struct connreq *conn);
-static int send_socksv5_method(struct connreq *conn);
-static int send_socksv5_connect(struct connreq *conn);
-static int send_buffer(struct connreq *conn);
-static int recv_buffer(struct connreq *conn);
-static int read_socksv5_method(struct connreq *conn);
-static int read_socksv4_req(struct connreq *conn);
-static int read_socksv5_connect(struct connreq *conn);
-static int read_socksv5_auth(struct connreq *conn);
-static int deadpool_init(void);
-static int send_socksv4a_request(struct connreq *conn, const char *onion_host);
-
-static pthread_mutex_t tsocks_init_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-void tsocks_init(void)
-{
-#define LOAD_ERROR(s,l) { \
- char *error; \
- error = dlerror(); \
- show_msg(l, "The symbol %s() was not found in any shared " \
- "library. The error reported was: %s!\n", s, \
- (error)?error:"not found"); \
- dlerror(); \
- }
- pthread_mutex_lock(&tsocks_init_mutex);
-
- /* We only need to be called once */
- if (tsocks_init_complete)
- return;
-
- /* Not strictly true yet, but prevents us getting called while still in progress.*/
- /* This has been observed on Snow Leopard for instance. */
- tsocks_init_complete = 1;
-
- show_msg(MSGWARN, "In tsocks_init \n");
-
- get_environment();
- get_config();
-
- show_msg(MSGWARN, "In tsocks_init after env/config\n");
-
-#ifdef USE_OLD_DLSYM
- void *lib;
-#endif
-
- /* We could do all our initialization here, but to be honest */
- /* most programs that are run won't use our services, so */
- /* we do our general initialization on first call */
-
- /* Determine the logging level */
- suid = (getuid() != geteuid());
-
- dlerror();
-#ifndef USE_OLD_DLSYM
- #ifdef SUPPORT_RES_API
- if ((realres_init = dlsym(RTLD_NEXT, "res_init")) == NULL)
- LOAD_ERROR("res_init", MSGERR);
- #endif
- #define PATCH_TABLE_EXPANSION(e,r,s,n,b,m) if ((real##n = dlsym(RTLD_NEXT, m)) == NULL) LOAD_ERROR(m, MSG##e);
- #include "expansion_table.h"
- #undef PATCH_TABLE_EXPANSION
-#else
- lib = dlopen(LIBCONNECT, RTLD_LAZY);
- realconnect = dlsym(lib, "connect");
- realselect = dlsym(lib, "select");
- realpoll = dlsym(lib, "poll");
- realgethostbyname = dlsym(lib, "gethostbyname");
- realgethostbyaddr = dlsym(lib, "gethostbyaddr");
- realgetaddrinfo = dlsym(lib, "getaddrinfo");
- realgetipnodebyname = dlsym(lib, "getipnodebyname");
- realsendto = dlsym(lib, "sendto");
- realsendmsg = dlsym(lib, "sendmsg");
- dlclose(lib);
- lib = dlopen(LIBC, RTLD_LAZY);
- realclose = dlsym(lib, "close");
- dlclose(lib);
- #ifdef SUPPORT_RES_API
- lib = dlopen(LIBRESOLV, RTLD_LAZY);
- realres_init = dlsym(lib, "res_init");
- realresquery = dlsym(lib, "res_query");
- realressend = dlsym(lib, "res_send");
- realresquerydomain = dlsym(lib, "res_querydomain");
- realressearch = dlsym(lib, "res_search");
- dlclose(lib);
- #endif
-#endif
- /* Unfortunately, we can't do this lazily because otherwise our mmap'd
- area won't be shared across fork()s. */
- if (!deadpool_init()) {
- show_msg(MSGERR, "Fatal error: exiting\n");
- exit(1);
- }
-
- tsocks_init_complete=1;
- pthread_mutex_unlock(&tsocks_init_mutex);
-
- show_msg(MSGWARN, "Exit tsocks_init \n");
-}
-
-static int get_environment()
-{
- static int done = 0;
- int loglevel = MSGERR;
- char *logfile = NULL;
- char *env;
-
- if (done)
- return(0);
-
- /* Determine the logging level */
- if ((env = getenv("TORSOCKS_DEBUG")))
- loglevel = atoi(env);
- if (((env = getenv("TORSOCKS_DEBUG_FILE"))) && !suid)
- logfile = env;
- set_log_options(loglevel, logfile, 1);
-
- done = 1;
-
- return(0);
-}
-
-static int get_config ()
-{
- static int done = 0;
-
- if (done)
- return(0);
-
- /* Determine the location of the config file */
-#ifdef ALLOW_ENV_CONFIG
- if (!suid)
- conffile = getenv("TORSOCKS_CONF_FILE");
-#endif
-
- /* Read in the config file */
- read_config(conffile, &config);
- if (config.paths)
- show_msg(MSGDEBUG, "First lineno for first path is %d\n", config.paths->lineno);
-
- done = 1;
-
- return(0);
-}
-
-/* Patch trampoline functions */
-/* no tsocks_res_init_guts */
-#define PATCH_TABLE_EXPANSION(e,r,s,n,b,m) \
- r n(s##SIGNATURE) { \
- if (!real##n) { \
- dlerror(); \
- if ((real##n = dlsym(RTLD_NEXT, m)) == NULL) \
- LOAD_ERROR(m, MSG##e); \
- } \
- return tsocks_##b##_guts(s##ARGNAMES, real##n); \
- }
-#include "expansion_table.h"
-#undef PATCH_TABLE_EXPANSION
-
-int tsocks_connect_guts(CONNECT_SIGNATURE, int (*original_connect)(CONNECT_SIGNATURE))
-{
- struct sockaddr_in *connaddr;
- struct sockaddr_in peer_address;
- struct sockaddr_in server_address;
- int gotvalidserver = 0, rc;
- socklen_t namelen = sizeof(peer_address);
- int sock_type = -1;
- socklen_t sock_type_len = sizeof(sock_type);
- int res = -1;
- struct serverent *path;
- struct connreq *newconn;
-
- /* See comment in close() */
- if (!tsocks_init_complete)
- tsocks_init();
-
- /* If the real connect doesn't exist, we're stuffed */
- if (original_connect == 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);
-
- show_msg(MSGDEBUG, "sin_family: %i\n", connaddr->sin_family);
-
- show_msg(MSGDEBUG, "sockopt: %i \n", sock_type);
-
- /* If the address is local refuse it. We do this because it could
- be a TCP DNS request to a local DNS server.*/
- if (!(is_local(&config, &(connaddr->sin_addr))) &&
- !is_dead_address(pool, connaddr->sin_addr.s_addr)) {
- char buf[16];
- inet_ntop(AF_INET, &(connaddr->sin_addr), buf, sizeof(buf));
- show_msg(MSGERR, "connect: Connection is to a local address (%s), may be a "
- "TCP DNS request to a local DNS server so have to reject to be safe. "
- "Please report a bug to http://code.google.com/p/torsocks/issues/entry if "
- "this is preventing a program from working properly with torsocks.\n", buf);
- return -1;
- }
-
- /* If this isn't an INET socket we can't */
- /* handle it, just call the real connect now */
- if ((connaddr->sin_family != AF_INET)) {
- show_msg(MSGDEBUG, "connect: Connection isn't IPv4, ignoring\n");
- return(original_connect(__fd, __addr, __len));
- }
-
- /* If this a UDP socket */
- /* then we refuse it, since it is probably a DNS request */
- if ((sock_type != SOCK_STREAM)) {
- show_msg(MSGERR, "connect: Connection is a UDP or ICMP stream, may be a "
- "DNS request or other form of leak: rejecting.\n");
- return -1;
- }
-
- /* 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(original_connect(__fd, __addr, __len));
- }
-
- show_msg(MSGDEBUG, "Got connection request for socket %d to "
- "%s\n", __fd, inet_ntoa(connaddr->sin_addr));
-
- /* 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 tsocks_select_guts(SELECT_SIGNATURE, int (*original_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(original_select(n, readfds, writefds, exceptfds, timeout));
- }
-
- if (!tsocks_init_complete)
- tsocks_init();
-
- 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(original_select(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 = original_select(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);
-}
-
-int tsocks_poll_guts(POLL_SIGNATURE, int (*original_poll)(POLL_SIGNATURE))
-{
- int nevents = 0;
- int rc = 0;
- unsigned int i;
- int setevents = 0;
- int monitoring = 0;
- struct connreq *conn, *nextconn;
-
- /* If we're not currently managing any requests we can just
- * leave here */
- if (!requests)
- return(original_poll(ufds, nfds, timeout));
-
- if (!tsocks_init_complete)
- tsocks_init();
-
- show_msg(MSGDEBUG, "Intercepted call to poll with %d fds, "
- "0x%08x timeout %d\n", nfds, ufds, timeout);
-
- for (conn = requests; conn != NULL; conn = conn->next)
- conn->selectevents = 0;
-
- /* Record what events on our sockets the caller was interested
- * in */
- for (i = 0; i < nfds; i++) {
- if (!(conn = find_socks_request(ufds[i].fd, 0)))
- continue;
- show_msg(MSGDEBUG, "Have event checks for socks enabled socket %d\n",
- conn->sockid);
- conn->selectevents = ufds[i].events;
- monitoring = 1;
- }
-
- if (!monitoring)
- return(original_poll(ufds, nfds, timeout));
-
- /* This is our poll loop. In it we repeatedly call poll(). We
- * pass select the same event list as provided by the caller except we
- * modify the events 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 poll times out */
- do {
- /* Enable our sockets for the events WE want to hear about */
- for (i = 0; i < nfds; i++) {
- if (!(conn = find_socks_request(ufds[i].fd, 0)))
- continue;
-
- /* We always want to know about socket exceptions but they're
- * always returned (i.e they don't need to be in the list of
- * wanted events to be returned by the kernel */
- ufds[i].events = 0;
-
- /* 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))
- ufds[i].events |= POLLOUT;
- /* If we're waiting to receive data we want to get
- * read events */
- if (conn->state == RECEIVING)
- ufds[i].events |= POLLIN;
- }
-
- nevents = original_poll(ufds, nfds, 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;
-
- /* Find the socket in the poll list */
- for (i = 0; ((i < nfds) && (ufds[i].fd != conn->sockid)); i++)
- /* Empty Loop */;
- if (i == nfds)
- continue;
-
- show_msg(MSGDEBUG, "Checking socket %d for events\n", conn->sockid);
-
- if (!ufds[i].revents) {
- show_msg(MSGDEBUG, "No events on socket\n");
- continue;
- }
-
- /* Clear any read or write events on the socket, we'll reset
- * any that are necessary later. */
- setevents = ufds[i].revents;
- if (setevents & POLLIN) {
- show_msg(MSGDEBUG, "Socket had read event\n");
- ufds[i].revents &= ~POLLIN;
- nevents--;
- }
- if (setevents & POLLOUT) {
- show_msg(MSGDEBUG, "Socket had write event\n");
- ufds[i].revents &= ~POLLOUT;
- nevents--;
- }
- if (setevents & (POLLERR | POLLNVAL | POLLHUP))
- show_msg(MSGDEBUG, "Socket had error event\n");
-
- /* Now handle this event */
- if (setevents & (POLLERR | POLLNVAL | POLLHUP)) {
- 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. Just copy back the error events
- * from the poll call, error events are always valid even if not
- * requested by the client */
- /* 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 polled 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 & POLLOUT) {
- setevents |= POLLOUT;
- nevents++;
- }
- }
- }
- } while (nevents == 0);
-
- show_msg(MSGDEBUG, "Finished intercepting poll(), %d events\n", nevents);
-
- /* Now restore the events polled in each of the blocks */
- for (i = 0; i < nfds; i++) {
- if (!(conn = find_socks_request(ufds[i].fd, 1)))
- continue;
- ufds[i].events = conn->selectevents;
- }
-
- return(nevents);
-}
-
-int tsocks_close_guts(CLOSE_SIGNATURE, int (*original_close)(CLOSE_SIGNATURE))
-{
- int rc;
- struct connreq *conn;
-
- /* If we're not currently managing any requests we can just
- * leave here */
- if (!requests) {
- show_msg(MSGDEBUG, "No requests waiting, calling real close\n");
- return(original_close(fd));
- }
-
- /* If we are called before this symbol has been dlopened then try
- loading symbols now. This is a workaround for a problem I don't
- really understand and have only encountered when using torsocks
- with svn on Fedora 10, so definitely a hack. */
- if (!tsocks_init_complete)
- tsocks_init();
-
- if (original_close == NULL) {
- show_msg(MSGERR, "Unresolved symbol: close\n");
- return(-1);
- }
-
- show_msg(MSGDEBUG, "Call to close(%d)\n", fd);
-
- rc = original_close(fd);
-
- /* If we have this fd in our request handling list we
- * remove it now */
- if ((conn = find_socks_request(fd, 1))) {
- show_msg(MSGDEBUG, "Call to close() received on file descriptor "
- "%d which is a connection request of status %d\n",
- conn->sockid, conn->state);
- kill_socks_request(conn);
- }
-
- return(rc);
-}
-
-/* If we are not done setting up the connection yet, return
- * -1 and ENOTCONN, otherwise call getpeername
- *
- * This is necessary since some applications, when using non-blocking connect,
- * (like ircII) use getpeername() to find out if they are connected already.
- *
- * This results in races sometimes, where the client sends data to the socket
- * before we are done with the socks connection setup. Another solution would
- * be to intercept send().
- *
- * This could be extended to actually set the peername to the peer the
- * client application has requested, but not for now.
- *
- * PP, Sat, 27 Mar 2004 11:30:23 +0100
- */
-
-int tsocks_getpeername_guts(GETPEERNAME_SIGNATURE,
- int (*original_getpeername)(GETPEERNAME_SIGNATURE))
-{
- struct connreq *conn;
- int rc;
-
- /* See comment in close() */
- if (!tsocks_init_complete)
- tsocks_init();
-
- if (original_getpeername == NULL) {
- show_msg(MSGERR, "Unresolved symbol: getpeername\n");
- return(-1);
- }
-
- show_msg(MSGDEBUG, "Call to getpeername for fd %d\n", __fd);
-
-
- rc = original_getpeername(__fd, __name, __namelen);
- if (rc == -1)
- return rc;
-
- /* Are we handling this connect? */
- if ((conn = find_socks_request(__fd, 1))) {
- /* While we are at it, we might was well try to do something useful */
- handle_request(conn);
-
- if (conn->state != DONE) {
- errno = ENOTCONN;
- return(-1);
- }
- }
- return rc;
-}
-
-static struct connreq *new_socks_request(int sockid, struct sockaddr_in *connaddr,
- struct sockaddr_in *serveraddr,
- struct serverent *path)
-{
- struct connreq *newconn;
-
- if ((newconn = malloc(sizeof(*newconn))) == NULL) {
- /* Could not malloc, we're stuffed */
- show_msg(MSGERR, "Could not allocate memory for new socks request\n");
- return(NULL);
- }
-
- /* Add this connection to be proxied to the list */
- memset(newconn, 0x0, sizeof(*newconn));
- newconn->sockid = sockid;
- newconn->state = UNSTARTED;
- newconn->path = path;
- memcpy(&(newconn->connaddr), connaddr, sizeof(newconn->connaddr));
- memcpy(&(newconn->serveraddr), serveraddr, sizeof(newconn->serveraddr));
- newconn->next = requests;
- requests = newconn;
-
- return(newconn);
-}
-
-static void kill_socks_request(struct connreq *conn)
-{
- struct connreq *connnode;
-
- if (requests == conn)
- requests = conn->next;
- else {
- for (connnode = requests; connnode != NULL; connnode = connnode->next) {
- if (connnode->next == conn) {
- connnode->next = conn->next;
- break;
- }
- }
- }
-
- free(conn);
-}
-
-static struct connreq *find_socks_request(int sockid, int includefinished)
-{
- struct connreq *connnode;
-
- for (connnode = requests; connnode != NULL; connnode = connnode->next) {
- if (connnode->sockid == sockid) {
- if (((connnode->state == FAILED) || (connnode->state == DONE)) &&
- !includefinished)
- break;
- else
- return(connnode);
- }
- }
-
- return(NULL);
-}
-
-static int handle_request(struct connreq *conn)
-{
- int rc = 0;
- int i = 0;
-
- show_msg(MSGDEBUG, "Beginning handle loop for socket %d\n", conn->sockid);
-
- while ((rc == 0) &&
- (conn->state != FAILED) &&
- (conn->state != DONE) &&
- (i++ < 20)) {
- show_msg(MSGDEBUG, "In request handle loop for socket %d, "
- "current state of request is %d\n", conn->sockid,
- conn->state);
- switch(conn->state) {
- case UNSTARTED:
- case CONNECTING:
- rc = connect_server(conn);
- break;
- case CONNECTED:
- rc = send_socks_request(conn);
- break;
- case SENDING:
- rc = send_buffer(conn);
- break;
- case RECEIVING:
- rc = recv_buffer(conn);
- break;
- case SENTV4REQ:
- show_msg(MSGDEBUG, "Receiving reply to SOCKS V4 connect request\n");
- conn->datalen = sizeof(struct sockrep);
- conn->datadone = 0;
- conn->state = RECEIVING;
- conn->nextstate = GOTV4REQ;
- break;
- case GOTV4REQ:
- rc = read_socksv4_req(conn);
- break;
- case SENTV5METHOD:
- show_msg(MSGDEBUG, "Receiving reply to SOCKS V5 method negotiation\n");
- conn->datalen = 2;
- conn->datadone = 0;
- conn->state = RECEIVING;
- conn->nextstate = GOTV5METHOD;
- break;
- case GOTV5METHOD:
- rc = read_socksv5_method(conn);
- break;
- case SENTV5AUTH:
- show_msg(MSGDEBUG, "Receiving reply to SOCKS V5 authentication negotiation\n");
- conn->datalen = 2;
- conn->datadone = 0;
- conn->state = RECEIVING;
- conn->nextstate = GOTV5AUTH;
- break;
- case GOTV5AUTH:
- rc = read_socksv5_auth(conn);
- break;
- case SENTV5CONNECT:
- show_msg(MSGDEBUG, "Receiving reply to SOCKS V5 connect request\n");
- conn->datalen = 10;
- conn->datadone = 0;
- conn->state = RECEIVING;
- conn->nextstate = GOTV5CONNECT;
- break;
- case GOTV5CONNECT:
- rc = read_socksv5_connect(conn);
- break;
- }
- conn->err = errno;
- }
-
- if (i == 20)
- show_msg(MSGERR, "Ooops, state loop while handling request %d\n",
- conn->sockid);
-
- show_msg(MSGDEBUG, "Handle loop completed for socket %d in state %d, "
- "returning %d\n", conn->sockid, conn->state, rc);
- return(rc);
-}
-
-static int connect_server(struct connreq *conn)
-{
- int rc;
-
- /* Connect this socket to the socks server */
- show_msg(MSGDEBUG, "Connecting to %s port %d\n",
- inet_ntoa(conn->serveraddr.sin_addr), ntohs(conn->serveraddr.sin_port));
-
- rc = realconnect(conn->sockid, (CONNECT_SOCKARG) &(conn->serveraddr),
- sizeof(conn->serveraddr));
-
- show_msg(MSGDEBUG, "Connect returned %d, errno is %d\n", rc, errno);
- if (rc && errno == EISCONN) {
- rc = 0;
- show_msg(MSGDEBUG, "Socket %d already connected to SOCKS server\n", conn->sockid);
- conn->state = CONNECTED;
- } else if (rc) {
- if (errno != EINPROGRESS) {
- show_msg(MSGERR, "Error %d attempting to connect to SOCKS "
- "server (%s)\n", errno, strerror(errno));
- conn->state = FAILED;
- } else {
- show_msg(MSGDEBUG, "Connection in progress\n");
- conn->state = CONNECTING;
- }
- } else {
- show_msg(MSGDEBUG, "Socket %d connected to SOCKS server\n", conn->sockid);
- conn->state = CONNECTED;
- }
-
- return((rc ? errno : 0));
-}
-
-static int send_socks_request(struct connreq *conn)
-{
- int rc = 0;
-
- if (conn->path->type == 4) {
- char *name = get_pool_entry(pool, &(conn->connaddr.sin_addr));
- if(name != NULL)
- rc = send_socksv4a_request(conn,name);
- else
- rc = send_socksv4_request(conn);
- } else
- rc = send_socksv5_method(conn);
- return(rc);
-}
-
-static int send_socksv4a_request(struct connreq *conn,const char *onion_host)
-{
- struct passwd *user;
- struct sockreq *thisreq;
- int endOfUser;
- /* Determine the current username */
- user = getpwuid(getuid());
-
- thisreq = (struct sockreq *) conn->buffer;
- endOfUser=sizeof(struct sockreq) +
- (user == NULL ? 0 : strlen(user->pw_name)) + 1;
-
- /* Check the buffer has enough space for the request */
- /* and the user name */
- conn->datalen = endOfUser+
- (onion_host == NULL ? 0 : strlen(onion_host)) + 1;
- if (sizeof(conn->buffer) < conn->datalen) {
- show_msg(MSGERR, "The SOCKS username is too long");
- conn->state = FAILED;
- return(ECONNREFUSED);
- }
-
- /* Create the request */
- thisreq->version = 4;
- thisreq->command = 1;
- thisreq->dstport = conn->connaddr.sin_port;
- thisreq->dstip = htonl(1);
-
- /* Copy the username */
- strcpy((char *) thisreq + sizeof(struct sockreq),
- (user == NULL ? "" : user->pw_name));
-
- /* Copy the onion host */
- strcpy((char *) thisreq + endOfUser,
- (onion_host == NULL ? "" : onion_host));
-
- conn->datadone = 0;
- conn->state = SENDING;
- conn->nextstate = SENTV4REQ;
-
- return(0);
-}
-
-static int send_socksv4_request(struct connreq *conn)
-{
- struct passwd *user;
- struct sockreq *thisreq;
-
- /* Determine the current username */
- user = getpwuid(getuid());
-
- thisreq = (struct sockreq *) conn->buffer;
-
- /* Check the buffer has enough space for the request */
- /* and the user name */
- conn->datalen = sizeof(struct sockreq) +
- (user == NULL ? 0 : strlen(user->pw_name)) + 1;
- if (sizeof(conn->buffer) < conn->datalen) {
- show_msg(MSGERR, "The SOCKS username is too long");
- conn->state = FAILED;
- return(ECONNREFUSED);
- }
-
- /* Create the request */
- thisreq->version = 4;
- thisreq->command = 1;
- thisreq->dstport = conn->connaddr.sin_port;
- thisreq->dstip = conn->connaddr.sin_addr.s_addr;
-
- /* Copy the username */
- strcpy((char *) thisreq + sizeof(struct sockreq),
- (user == NULL ? "" : user->pw_name));
-
- conn->datadone = 0;
- conn->state = SENDING;
- conn->nextstate = SENTV4REQ;
-
- return(0);
-}
-
-static int send_socksv5_method(struct connreq *conn)
-{
- char verstring[] = { 0x05, /* Version 5 SOCKS */
- 0x02, /* No. Methods */
- 0x00, /* Null Auth */
- 0x02 }; /* User/Pass Auth */
-
- show_msg(MSGDEBUG, "Constructing V5 method negotiation\n");
- conn->state = SENDING;
- conn->nextstate = SENTV5METHOD;
- memcpy(conn->buffer, verstring, sizeof(verstring));
- conn->datalen = sizeof(verstring);
- conn->datadone = 0;
-
- return(0);
-}
-
-static int send_socksv5_connect(struct connreq *conn)
-{
- int namelen = 0;
- char *name = NULL;
- char constring[] = { 0x05, /* Version 5 SOCKS */
- 0x01, /* Connect request */
- 0x00, /* Reserved */
- 0x01 }; /* IP Version 4 */
-
- show_msg(MSGDEBUG, "Constructing V5 connect request\n");
- conn->datadone = 0;
- conn->state = SENDING;
- conn->nextstate = SENTV5CONNECT;
- memcpy(conn->buffer, constring, sizeof(constring));
- conn->datalen = sizeof(constring);
-
- show_msg(MSGDEBUG, "send_socksv5_connect: looking for: %s\n",
- inet_ntoa(conn->connaddr.sin_addr));
-
- name = get_pool_entry(pool, &(conn->connaddr.sin_addr));
- if(name != NULL) {
- namelen = strlen(name);
- if(namelen > 255) /* "Can't happen" */
- name = NULL;
- }
- if(name != NULL) {
- show_msg(MSGDEBUG, "send_socksv5_connect: found it!\n");
- /* Substitute the domain name from the pool into the SOCKS request. */
- conn->buffer[3] = 0x03; /* Change the ATYP field */
- conn->buffer[4] = namelen; /* Length of name */
- conn->datalen++;
- memcpy(&conn->buffer[conn->datalen], name, namelen);
- conn->datalen += namelen;
- } else {
- show_msg(MSGDEBUG, "send_socksv5_connect: ip address not found\n");
- /* Use the raw IP address */
- memcpy(&conn->buffer[conn->datalen], &(conn->connaddr.sin_addr.s_addr),
- sizeof(conn->connaddr.sin_addr.s_addr));
- conn->datalen += sizeof(conn->connaddr.sin_addr.s_addr);
- }
- memcpy(&conn->buffer[conn->datalen], &(conn->connaddr.sin_port),
- sizeof(conn->connaddr.sin_port));
- conn->datalen += sizeof(conn->connaddr.sin_port);
-
- return(0);
-}
-
-static int send_buffer(struct connreq *conn)
-{
- int rc = 0;
-
- show_msg(MSGDEBUG, "Writing to server (sending %d bytes)\n", conn->datalen);
- while ((rc == 0) && (conn->datadone != conn->datalen)) {
- rc = send(conn->sockid, conn->buffer + conn->datadone,
- conn->datalen - conn->datadone, 0);
- if (rc > 0) {
- conn->datadone += rc;
- rc = 0;
- } else {
- if (errno != EWOULDBLOCK)
- show_msg(MSGDEBUG, "Write failed, %s\n", strerror(errno));
- rc = errno;
- }
- }
-
- if (conn->datadone == conn->datalen)
- conn->state = conn->nextstate;
-
- show_msg(MSGDEBUG, "Sent %d bytes of %d bytes in buffer, return code is %d\n",
- conn->datadone, conn->datalen, rc);
- return(rc);
-}
-
-static int recv_buffer(struct connreq *conn)
-{
- int rc = 0;
-
- show_msg(MSGDEBUG, "Reading from server (expecting %d bytes)\n", conn->datalen);
- while ((rc == 0) && (conn->datadone != conn->datalen)) {
- rc = recv(conn->sockid, conn->buffer + conn->datadone,
- conn->datalen - conn->datadone, 0);
- if (rc > 0) {
- conn->datadone += rc;
- rc = 0;
- } else if (rc == 0) {
- show_msg(MSGDEBUG, "Peer has shutdown but we only read %d of %d bytes.\n",
- conn->datadone, conn->datalen);
- rc = ENOTCONN; /* ENOTCONN seems like the most fitting error message */
- } else {
- if (errno != EWOULDBLOCK)
- show_msg(MSGDEBUG, "Read failed, %s\n", strerror(errno));
- rc = errno;
- }
- }
-
- if (conn->datadone == conn->datalen)
- conn->state = conn->nextstate;
-
- show_msg(MSGDEBUG, "Received %d bytes of %d bytes expected, return code is %d\n",
- conn->datadone, conn->datalen, rc);
- return(rc);
-}
-
-static int read_socksv5_method(struct connreq *conn)
-{
- struct passwd *nixuser;
- char *uname, *upass;
-
- /* See if we offered an acceptable method */
- if (conn->buffer[1] == '\xff') {
- show_msg(MSGERR, "SOCKS V5 server refused authentication methods\n");
- conn->state = FAILED;
- return(ECONNREFUSED);
- }
-
- /* If the socks server chose username/password authentication */
- /* (method 2) then do that */
- if ((unsigned short int) conn->buffer[1] == 2) {
- show_msg(MSGDEBUG, "SOCKS V5 server chose username/password authentication\n");
-
- /* Determine the current *nix username */
- nixuser = getpwuid(getuid());
-
- if (((uname = conn->path->defuser) == NULL) &&
- ((uname = getenv("TORSOCKS_USERNAME")) == NULL) &&
- ((uname = (nixuser == NULL ? NULL : nixuser->pw_name)) == NULL)) {
- show_msg(MSGERR, "Could not get SOCKS username from "
- "local passwd file, torsocks.conf "
- "or $TORSOCKS_USERNAME to authenticate "
- "with");
- conn->state = FAILED;
- return(ECONNREFUSED);
- }
-
- if (((upass = getenv("TORSOCKS_PASSWORD")) == NULL) &&
- ((upass = conn->path->defpass) == NULL)) {
- show_msg(MSGERR, "Need a password in torsocks.conf or "
- "$TORSOCKS_PASSWORD to authenticate with");
- conn->state = FAILED;
- return(ECONNREFUSED);
- }
-
- /* Check that the username / pass specified will */
- /* fit into the buffer */
- if ((3 + strlen(uname) + strlen(upass)) >= sizeof(conn->buffer)) {
- show_msg(MSGERR, "The supplied socks username or "
- "password is too long");
- conn->state = FAILED;
- return(ECONNREFUSED);
- }
-
- conn->datalen = 0;
- conn->buffer[conn->datalen] = '\x01';
- conn->datalen++;
- conn->buffer[conn->datalen] = (int8_t) strlen(uname);
- conn->datalen++;
- memcpy(&(conn->buffer[conn->datalen]), uname, strlen(uname));
- conn->datalen = conn->datalen + strlen(uname);
- conn->buffer[conn->datalen] = (int8_t) strlen(upass);
- conn->datalen++;
- memcpy(&(conn->buffer[conn->datalen]), upass, strlen(upass));
- conn->datalen = conn->datalen + strlen(upass);
-
- conn->state = SENDING;
- conn->nextstate = SENTV5AUTH;
- conn->datadone = 0;
- } else
- return(send_socksv5_connect(conn));
-
- return(0);
-}
-
-static int read_socksv5_auth(struct connreq *conn)
-{
-
- if (conn->buffer[1] != '\x00') {
- show_msg(MSGERR, "SOCKS authentication failed, check username and password\n");
- conn->state = FAILED;
- return(ECONNREFUSED);
- }
-
- /* Ok, we authenticated ok, send the connection request */
- return(send_socksv5_connect(conn));
-}
-
-static int read_socksv5_connect(struct connreq *conn)
-{
-
- /* See if the connection succeeded */
- if (conn->buffer[1] != '\x00') {
- show_msg(MSGERR, "SOCKS V5 connect failed: ");
- conn->state = FAILED;
- switch ((int8_t) conn->buffer[1]) {
- case 1:
- show_msg(MSGERR, "General SOCKS server failure\n");
- return(ECONNABORTED);
- case 2:
- show_msg(MSGERR, "Connection denied by rule\n");
- return(ECONNABORTED);
- case 3:
- show_msg(MSGERR, "Network unreachable\n");
- return(ENETUNREACH);
- case 4:
- show_msg(MSGERR, "Host unreachable\n");
- return(EHOSTUNREACH);
- case 5:
- show_msg(MSGERR, "Connection refused\n");
- return(ECONNREFUSED);
- case 6:
- show_msg(MSGERR, "TTL Expired\n");
- return(ETIMEDOUT);
- case 7:
- show_msg(MSGERR, "Command not supported\n");
- return(ECONNABORTED);
- case 8:
- show_msg(MSGERR, "Address type not supported\n");
- return(ECONNABORTED);
- default:
- show_msg(MSGERR, "Unknown error\n");
- return(ECONNABORTED);
- }
- }
- conn->state = DONE;
-
- return(0);
-}
-
-static int read_socksv4_req(struct connreq *conn)
-{
- struct sockrep *thisrep;
-
- thisrep = (struct sockrep *) conn->buffer;
-
- if (thisrep->result != 90) {
- show_msg(MSGERR, "SOCKS V4 connect rejected:\n");
- conn->state = FAILED;
- switch(thisrep->result) {
- case 91:
- show_msg(MSGERR, "SOCKS server refused connection\n");
- return(ECONNREFUSED);
- case 92:
- show_msg(MSGERR, "SOCKS server refused connection "
- "because of failed connect to identd "
- "on this machine\n");
- return(ECONNREFUSED);
- case 93:
- show_msg(MSGERR, "SOCKS server refused connection "
- "because identd and this library "
- "reported different user-ids\n");
- return(ECONNREFUSED);
- default:
- show_msg(MSGERR, "Unknown reason\n");
- return(ECONNREFUSED);
- }
- }
- conn->state = DONE;
-
- return(0);
-}
-
-#ifdef SUPPORT_RES_API
-int res_init(void)
-{
- int rc;
-
- if (!realres_init && ((realres_init = dlsym(RTLD_NEXT, "res_init")) == NULL))
- LOAD_ERROR("res_init", MSGERR);
-
- show_msg(MSGDEBUG, "Got res_init request\n");
-
- /* See comment in close() */
- if (!tsocks_init_complete)
- tsocks_init();
-
- if (realres_init == NULL) {
- show_msg(MSGERR, "Unresolved symbol: res_init\n");
- return(-1);
- }
- /* Call normal res_init */
- rc = realres_init();
-
- /* Force using TCP protocol for DNS queries */
- _res.options |= RES_USEVC;
- return(rc);
-}
-
-int EXPAND_GUTS_NAME(res_query)(RES_QUERY_SIGNATURE, int (*original_res_query)(RES_QUERY_SIGNATURE))
-{
- int rc;
-
- if (!original_res_query && ((original_res_query = dlsym(RTLD_NEXT, "res_query")) == NULL))
- LOAD_ERROR("res_query", MSGERR);
-
- show_msg(MSGDEBUG, "Got res_query request\n");
-
- /* See comment in close() */
- if (!tsocks_init_complete)
- tsocks_init();
-
- if (original_res_query == NULL) {
- show_msg(MSGERR, "Unresolved symbol: res_query\n");
- return(-1);
- }
-
- /* Ensure we force using TCP for DNS queries by calling res_init
- above if it has not already been called.*/
- if (!(_res.options & RES_INIT) || !(_res.options & RES_USEVC))
- res_init();
-
- /* Call normal res_query */
- rc = original_res_query(dname, class, type, answer, anslen);
-
- return(rc);
-}
-
-int EXPAND_GUTS_NAME(res_querydomain)(RES_QUERYDOMAIN_SIGNATURE, int (*original_res_querydomain)(RES_QUERYDOMAIN_SIGNATURE))
-{
- int rc;
-
- if (!original_res_querydomain &&
- ((original_res_querydomain = dlsym(RTLD_NEXT, "res_querydomain")) == NULL))
- LOAD_ERROR("res_querydoimain", MSGERR);
-
- show_msg(MSGDEBUG, "Got res_querydomain request\n");
-
- /* See comment in close() */
- if (!tsocks_init_complete)
- tsocks_init();
-
- if (original_res_querydomain == NULL) {
- show_msg(MSGERR, "Unresolved symbol: res_querydomain\n");
- return(-1);
- }
-
- /* Ensure we force using TCP for DNS queries by calling res_init
- above if it has not already been called.*/
- if (!(_res.options & RES_INIT) || !(_res.options & RES_USEVC))
- res_init();
-
- /* Call normal res_querydomain */
- rc = original_res_querydomain(name, domain, class, type, answer, anslen);
-
- return(rc);
-}
-
-int EXPAND_GUTS_NAME(res_search)(RES_SEARCH_SIGNATURE, int (*original_res_search)(RES_SEARCH_SIGNATURE))
-{
- int rc;
-
- if (!original_res_search &&
- ((original_res_search = dlsym(RTLD_NEXT, "res_search")) == NULL))
- LOAD_ERROR("res_search", MSGERR);
-
- show_msg(MSGDEBUG, "Got res_search request\n");
-
- /* See comment in close() */
- if (!tsocks_init_complete)
- tsocks_init();
-
- if (original_res_search == NULL) {
- show_msg(MSGERR, "Unresolved symbol: res_search\n");
- return(-1);
- }
-
- /* Ensure we force using TCP for DNS queries by calling res_init
- above if it has not already been called.*/
- if (!(_res.options & RES_INIT) || !(_res.options & RES_USEVC))
- res_init();
-
- /* Call normal res_search */
- rc = original_res_search(dname, class, type, answer, anslen);
-
- return(rc);
-}
-
-int EXPAND_GUTS_NAME(res_send)(RES_SEND_SIGNATURE, int (*original_res_send)(RES_SEND_SIGNATURE))
-{
- int rc;
-
- if (!original_res_send && ((original_res_send = dlsym(RTLD_NEXT, "res_send")) == NULL))
- LOAD_ERROR("res_send", MSGERR);
-
- show_msg(MSGDEBUG, "Got res_send request\n");
-
- /* See comment in close() */
- if (!tsocks_init_complete)
- tsocks_init();
-
- if (original_res_send == NULL) {
- show_msg(MSGERR, "Unresolved symbol: res_send\n");
- return(-1);
- }
-
- /* Ensure we force using TCP for DNS queries by calling res_init
- above if it has not already been called.*/
- if (!(_res.options & RES_INIT) || !(_res.options & RES_USEVC))
- res_init();
-
- /* Call normal res_send */
- rc = original_res_send(msg, msglen, answer, anslen);
-
- return(rc);
-}
-#endif
-
-static int deadpool_init(void)
-{
- if (pool)
- return 1;
-
- if (!config.tordns_enabled) {
- show_msg(MSGERR, "Tor DNS is disabled. Check your configuration.\n");
- return 0;
- }
-
- get_environment();
- get_config();
- pool = init_pool(config.tordns_cache_size,
- config.tordns_deadpool_range->localip,
- config.tordns_deadpool_range->localnet,
- config.defaultserver.address,
- config.defaultserver.port);
-
- if (!pool) {
- show_msg(MSGERR, "Could not initialize reserved addresses for "
- ".onion addresses. Torsocks will not work properly.\n");
- return 0;
- }
- return 1;
-}
-
-struct hostent *tsocks_gethostbyname_guts(GETHOSTBYNAME_SIGNATURE, struct hostent *(*original_gethostbyname)(GETHOSTBYNAME_SIGNATURE))
-{
- if (pool)
- return our_gethostbyname(pool, name);
- return original_gethostbyname(name);
-}
-
-struct hostent *tsocks_gethostbyaddr_guts(GETHOSTBYADDR_SIGNATURE, struct hostent *(*original_gethostbyaddr)(GETHOSTBYADDR_SIGNATURE))
-{
- if (pool)
- return our_gethostbyaddr(pool, addr, len, type);
- return original_gethostbyaddr(addr, len, type);
-}
-
-int tsocks_getaddrinfo_guts(GETADDRINFO_SIGNATURE, int (*original_getaddrinfo)(GETADDRINFO_SIGNATURE))
-{
- if (pool)
- return our_getaddrinfo(pool, node, service, hints, res);
- return original_getaddrinfo(node, service, hints, res);
-}
-
-struct hostent *tsocks_getipnodebyname_guts(GETIPNODEBYNAME_SIGNATURE, struct hostent *(*original_getipnodebyname)(GETIPNODEBYNAME_SIGNATURE))
-{
- if (pool)
- return our_getipnodebyname(pool, name, af, flags, error_num);
- return original_getipnodebyname(name, af, flags, error_num);
-}
-
-ssize_t tsocks_sendto_guts(SENDTO_SIGNATURE, ssize_t (*original_sendto)(SENDTO_SIGNATURE))
-{
- int sock_type = -1;
- unsigned int sock_type_len = sizeof(sock_type);
- struct sockaddr_in *connaddr;
-
- /* See comment in close() */
- if (!tsocks_init_complete)
- tsocks_init();
-
- /* If the real sendto doesn't exist, we're stuffed */
- if (original_sendto == NULL) {
- show_msg(MSGERR, "Unresolved symbol: sendto\n");
- return(-1);
- }
-
- show_msg(MSGDEBUG, "Got sendto request\n");
-
- /* Get the type of the socket */
- getsockopt(s, SOL_SOCKET, SO_TYPE,
- (void *) &sock_type, &sock_type_len);
-
- show_msg(MSGDEBUG, "sockopt: %i\n", sock_type);
-
- /* If this a UDP socket then we refuse it, since it is probably a DNS
- request */
- if ((sock_type != SOCK_STREAM)) {
- show_msg(MSGERR, "sendto: Connection is a UDP or ICMP stream, may be a "
- "DNS request or other form of leak: rejecting.\n");
- return -1;
- }
-
- connaddr = (struct sockaddr_in *) to;
-
- /* If there is no address in 'to', sendto will only work if we
- already allowed the socket to connect(), so we let it through.
- Likewise if the socket is not an Internet connection. */
- if (connaddr && (connaddr->sin_family != AF_INET)) {
- show_msg(MSGDEBUG, "Connection isn't an Internet socket ignoring\n");
- }
-
- return (ssize_t) original_sendto(s, buf, len, flags, to, tolen);
-}
-
-ssize_t tsocks_sendmsg_guts(SENDMSG_SIGNATURE, ssize_t (*original_sendmsg)(SENDMSG_SIGNATURE))
-{
- int sock_type = -1;
- unsigned int sock_type_len = sizeof(sock_type);
- struct sockaddr_in *connaddr;
-
- /* See comment in close() */
- if (!tsocks_init_complete)
- tsocks_init();
-
- /* If the real sendmsg doesn't exist, we're stuffed */
- if (original_sendmsg == NULL) {
- show_msg(MSGERR, "Unresolved symbol: sendmsg\n");
- return(-1);
- }
-
- show_msg(MSGDEBUG, "Got sendmsg request\n");
-
- /* Get the type of the socket */
- getsockopt(s, SOL_SOCKET, SO_TYPE,
- (void *) &sock_type, &sock_type_len);
-
- show_msg(MSGDEBUG, "sockopt: %i\n", sock_type);
-
- if ((sock_type != SOCK_STREAM)) {
- show_msg(MSGERR, "sendmsg: Connection is a UDP or ICMP stream, may be a "
- "DNS request or other form of leak: rejecting.\n");
- return -1;
- }
-
- connaddr = (struct sockaddr_in *) msg->msg_name;
-
- /* If there is no address in msg_name, sendmsg will only work if we
- already allowed the socket to connect(), so we let it through.
- Likewise if the socket is not an Internet connection. */
- if (connaddr && (connaddr->sin_family != AF_INET)) {
- show_msg(MSGDEBUG, "Connection isn't an Internet socket\n");
- }
-
- return (ssize_t) original_sendmsg(s, msg, flags);
-}
-
More information about the tor-commits
mailing list