commit bf2db7bc1f0a734410e5988475755abad016ce37
Author: David Goulet <dgou...@ev0ke.net>
Date:   Mon Jun 24 21:25:20 2013 -0400

    Add .onion address support
    
    An onion pool subsystem is added which uses an IP subnet defined in the
    configuration file acting as a cookie pool that are picked and returned
    to the application. In the connect() process, if the cookie is seen, a
    lookup is done to find the corresponding onion address and used to
    connect to Tor.
    
    Some modifications was needed in the sock5 and connection ABI/API. Two
    files are added, onion.c/.h.
    
    The connect() call supports hidden service's address at this commit.
    Some optimization is still possible especially in the onion entry lookup
    by IP as well as the pool data structure which is now an array.
    
    Signed-off-by: David Goulet <dgou...@ev0ke.net>
---
 doc/torsocks.conf        |    6 +
 src/common/Makefile.am   |    2 +-
 src/common/config-file.c |   77 ++++++++++++-
 src/common/config-file.h |    7 ++
 src/common/connection.c  |   30 ++---
 src/common/defaults.h    |   11 ++
 src/common/macros.h      |    4 +
 src/common/onion.c       |  272 ++++++++++++++++++++++++++++++++++++++++++++++
 src/common/onion.h       |  134 +++++++++++++++++++++++
 src/common/socks5.c      |   15 ++-
 src/common/utils.c       |   17 +++
 src/common/utils.h       |    1 +
 src/lib/connect.c        |   37 ++++++-
 src/lib/torsocks.c       |   84 ++++++++++++++
 src/lib/torsocks.h       |    3 +
 15 files changed, 674 insertions(+), 26 deletions(-)

diff --git a/doc/torsocks.conf b/doc/torsocks.conf
index 60eb697..6f4fc04 100644
--- a/doc/torsocks.conf
+++ b/doc/torsocks.conf
@@ -19,3 +19,9 @@ local = 192.168.0.0/255.255.0.0
 # any SOCKS connection and relay the traffic on the Tor network.
 TorAddress 127.0.0.1
 TorPort 9050
+
+# Tor hidden sites do not have real IP addresses. This specifies what range of
+# IP addresses will be handed to the application as "cookies" for .onion names.
+# Of course, you should pick a block of addresses which you aren't going to
+# ever need to actually connect to.
+OnionAddrRange 127.42.42.0/24
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 8d8e074..7e75c32 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -5,4 +5,4 @@ AM_CFLAGS = -fno-strict-aliasing
 noinst_LTLIBRARIES = libcommon.la
 libcommon_la_SOURCES = log.c log.h config-file.c config-file.h utils.c utils.h 
\
                        compat.c compat.h socks5.c socks5.h \
-                       connection.c connection.h ht.h ref.h
+                       connection.c connection.h ht.h ref.h onion.c onion.h
diff --git a/src/common/config-file.c b/src/common/config-file.c
index 0a05325..32c93ea 100644
--- a/src/common/config-file.c
+++ b/src/common/config-file.c
@@ -17,9 +17,13 @@
  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#include <arpa/inet.h>
 #include <assert.h>
+#include <limits.h>
+#include <netinet/in.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/socket.h>
 
 #include "config-file.h"
 #include "log.h"
@@ -30,6 +34,66 @@
  */
 static const char *conf_toraddr_str = "TorAddress";
 static const char *conf_torport_str = "TorPort";
+static const char *conf_onion_str = "OnionAddrRange";
+
+/*
+ * Set the onion pool address range in the configuration object using the value
+ * found in the conf file.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int set_onion_info(const char *addr, struct configuration *config)
+{
+       int ret;
+       unsigned long bit_mask;
+       char *ip = NULL, *mask = NULL;
+       in_addr_t net;
+
+       assert(addr);
+       assert(config);
+
+       ip = strchr(addr, '/');
+       if (!ip) {
+               ERR("[config] Invalid %s value for %s", addr, conf_onion_str);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       mask = strdup(addr + (ip - addr) + 1);
+       ip = strndup(addr, ip - addr);
+       if (!ip || !mask) {
+               PERROR("[config] strdup onion addr");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       net = inet_addr(ip);
+       if (net == INADDR_NONE) {
+               ERR("[config] Invalid IP subnet %s for %s", ip, conf_onion_str);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       /* Expressed in base 10. */
+       bit_mask = strtoul(mask, NULL, 10);
+       if (bit_mask == ULONG_MAX) {
+               ERR("[config] Invalid mask %s for %s", mask, conf_onion_str);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       memcpy(&config->conf_file.onion_base, &net,
+                       sizeof(config->conf_file.onion_base));
+       config->conf_file.onion_mask = (uint8_t) bit_mask;
+
+       DBG("[config] Onion address range set to %s", addr);
+       ret = 0;
+
+error:
+       free(ip);
+       free(mask);
+       return ret;
+}
 
 /*
  * Set the given string port in a configuration object.
@@ -130,6 +194,11 @@ static int parse_config_line(const char *line, struct 
configuration *config)
                if (ret < 0) {
                        goto error;
                }
+       } else if (!strcmp(tokens[0], conf_onion_str)) {
+               ret = set_onion_info(tokens[1], config);
+               if (ret < 0) {
+                       goto error;
+               }
        } else {
                WARN("Config file contains unknown value: %s", line);
        }
@@ -207,7 +276,13 @@ int config_file_read(const char *filename, struct 
configuration *config)
                 * statement in the function call to set port.
                 */
                (void) set_tor_port(XSTR(DEFAULT_TOR_PORT), config);
-               ret = 0;
+
+               ret = set_onion_info(
+                               DEFAULT_ONION_ADDR_RANGE "/" 
DEFAULT_ONION_ADDR_MASK, config);
+               if (!ret) {
+                       /* ENOMEM is probably the only case here. */
+                       goto error;
+               }
                goto end;
        }
 
diff --git a/src/common/config-file.h b/src/common/config-file.h
index b0c2e84..1fc23bf 100644
--- a/src/common/config-file.h
+++ b/src/common/config-file.h
@@ -35,6 +35,13 @@ struct config_file {
        char *tor_address;
        /* The port of the Tor SOCKS. */
        in_port_t tor_port;
+
+       /*
+        * Base for onion address pool and the mask. In the config file, this is
+        * represented by BASE/MASK like so: 127.0.69.0/24
+        */
+       in_addr_t onion_base;
+       uint8_t onion_mask;
 };
 
 /*
diff --git a/src/common/connection.c b/src/common/connection.c
index 8fcbcf3..52d7acb 100644
--- a/src/common/connection.c
+++ b/src/common/connection.c
@@ -174,26 +174,28 @@ struct connection *connection_create(int fd, const struct 
sockaddr *dest)
 {
        struct connection *conn = NULL;
 
-       assert(dest);
-
        conn = zmalloc(sizeof(*conn));
        if (!conn) {
                PERROR("zmalloc connection");
                goto error;
        }
 
-       switch (dest->sa_family) {
-       case AF_INET:
-               conn->dest_addr.domain = CONNECTION_DOMAIN_INET;
-               memcpy(&conn->dest_addr.u.sin, dest, 
sizeof(conn->dest_addr.u.sin));
-               break;
-       case AF_INET6:
-               conn->dest_addr.domain = CONNECTION_DOMAIN_INET6;
-               memcpy(&conn->dest_addr.u.sin6, dest, 
sizeof(conn->dest_addr.u.sin6));
-               break;
-       default:
-               ERR("Connection domain unknown %d", dest->sa_family);
-               goto error;
+       if (dest) {
+               switch (dest->sa_family) {
+               case AF_INET:
+                       conn->dest_addr.domain = CONNECTION_DOMAIN_INET;
+                       memcpy(&conn->dest_addr.u.sin, dest,
+                                       sizeof(conn->dest_addr.u.sin));
+                       break;
+               case AF_INET6:
+                       conn->dest_addr.domain = CONNECTION_DOMAIN_INET6;
+                       memcpy(&conn->dest_addr.u.sin6, dest,
+                                       sizeof(conn->dest_addr.u.sin6));
+                       break;
+               default:
+                       ERR("Connection domain unknown %d", dest->sa_family);
+                       goto error;
+               }
        }
 
        conn->fd = fd;
diff --git a/src/common/defaults.h b/src/common/defaults.h
index c127eec..1a2c2fc 100644
--- a/src/common/defaults.h
+++ b/src/common/defaults.h
@@ -47,4 +47,15 @@
  */
 #define DEFAULT_MAX_CONF_TOKEN         5
 
+/*
+ * Default initial size of the onion pool.
+ */
+#define DEFAULT_ONION_POOL_SIZE                8
+
+/*
+ * The default onion pool cookie range starting at 0 up to 255.
+ */
+#define DEFAULT_ONION_ADDR_RANGE       "127.42.42.0"
+#define DEFAULT_ONION_ADDR_MASK                "24"
+
 #endif /* TORSOCKS_DEFAULTS_H */
diff --git a/src/common/macros.h b/src/common/macros.h
index 1e98fd6..8b52814 100644
--- a/src/common/macros.h
+++ b/src/common/macros.h
@@ -41,4 +41,8 @@
 #define ATTR_HIDDEN __attribute__((visibility("hidden")))
 #endif
 
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
 #endif /* TORSOCKS_MACROS_H */
diff --git a/src/common/onion.c b/src/common/onion.c
new file mode 100644
index 0000000..85585e3
--- /dev/null
+++ b/src/common/onion.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgou...@ev0ke.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <assert.h>
+
+#include "defaults.h"
+#include "log.h"
+#include "onion.h"
+
+/*
+ * Resize pool entries of new_size.
+ *
+ * Return 0 on success or else -1.
+ */
+static int resize_onion_pool(struct onion_pool *pool, uint32_t new_size)
+{
+       struct onion_entry **tmp;
+
+       assert(new_size > pool->size);
+
+       tmp = realloc(pool->entries, new_size * sizeof(*tmp));
+       if (!tmp) {
+               PERROR("[onion] resize onion pool");
+               goto error;
+       }
+
+       DBG("[onion] Onion pool resized from size %lu to new size %lu", 
pool->size,
+                       new_size);
+
+       pool->entries = tmp;
+       pool->size = new_size;
+       return 0;
+
+error:
+       return -1;
+}
+
+/*
+ * Insert an onion entry in the given pool.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int insert_onion_entry(struct onion_entry *entry,
+               struct onion_pool *pool)
+{
+       int ret;
+
+       assert(entry);
+       assert(pool);
+
+       if (pool->count > pool->size) {
+               /* Double the size of the pool. */
+               ret = resize_onion_pool(pool, pool->size * 2);
+               if (ret < 0) {
+                       goto error;
+               }
+       }
+
+       pool->entries[pool->next_entry_pos] = entry;
+       pool->next_entry_pos++;
+       pool->count++;
+       ret = 0;
+
+       DBG("[onion] Entry added to the onion pool at index %lu",
+                       pool->next_entry_pos - 1);
+
+error:
+       return ret;
+}
+
+/*
+ * Initialize an already allocated onion pool using the given values.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int onion_pool_init(struct onion_pool *pool, in_addr_t addr, uint8_t mask)
+{
+       int ret = 0;
+
+       assert(pool);
+
+       if (mask == 0 || mask > 32) {
+               ERR("[onion] Pool initialized with mask set to %u.", mask);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       DBG("[onion] Pool init with subnet %s and mask %u",
+                       inet_ntoa(*((struct in_addr *) &addr)), mask);
+
+       /*
+        * Get base of subnet. For example, 127.0.0.68/27 will set the base to 
64
+        * and the max_pos to 95.
+        */
+       pool->base = (((ntohl(addr) >> (32 - mask)) << (32 - mask)) << 24) >> 
24;
+       pool->max_pos = pool->base + ((1UL << (32 - mask)) - 1);
+       pool->next_entry_pos = 0;
+       pool->count = 0;
+       tsocks_mutex_init(&pool->lock);
+
+       /*
+        * Get the minimum value between the two to avoid allocating more memory
+        * than we need.
+        */
+       pool->size = min(DEFAULT_ONION_POOL_SIZE, (pool->max_pos - pool->base) 
+ 1);
+
+       memcpy(&pool->ip_subnet, &addr, sizeof(pool->ip_subnet));
+
+       pool->entries = zmalloc(sizeof(struct onion_entry *) * pool->size);
+       if (!pool->entries) {
+               PERROR("[onion] zmalloc pool init");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       DBG("[onion] Pool initialized with base %lu, max_pos %lu and size %lu",
+                       pool->base, pool->max_pos, pool->size);
+
+error:
+       return ret;
+}
+
+/*
+ * Destroy onion pool by freeing every entry.
+ */
+void onion_pool_destroy(struct onion_pool *pool)
+{
+       int i;
+
+       assert(pool);
+
+       DBG("[onion] Destroying onion pool containing %u entry", pool->count);
+
+       for (i = 0; i < pool->count; i++) {
+               onion_entry_destroy(pool->entries[i]);
+       }
+
+       free(pool->entries);
+}
+
+/*
+ * Allocate an onion entry object and return the pointer. This MUST be called
+ * with the onion pool lock acquired.
+ *
+ * Return a newly allocated onion entry or else NULL.
+ */
+struct onion_entry *onion_entry_create(struct onion_pool *pool,
+               const char *onion_name)
+{
+       int ret;
+       uint32_t ip_host_order;
+       struct onion_entry *entry = NULL;
+
+       assert(pool);
+       assert(onion_name);
+
+       DBG("[onion] Creating onion entry for name %s", onion_name);
+
+       if (pool->next_entry_pos == pool->max_pos) {
+               ERR("[onion] Can't create anymore onion entry. Maximum reached 
(%u)",
+                               pool->max_pos);
+               goto error;
+       }
+
+       entry = zmalloc(sizeof(struct onion_entry));
+       if (!entry) {
+               PERROR("[onion] zmalloc entry");
+               goto error;
+       }
+
+       /* Copy hostname and force NULL byte at the end. */
+       strncpy(entry->hostname, onion_name, sizeof(entry->hostname));
+       entry->hostname[sizeof(entry->hostname) - 1] = '\0';
+
+       /*
+        * Create the new IP from the onion pool which will be the cookie 
returned
+        * to the caller.
+        */
+       ip_host_order = ntohl(pool->ip_subnet) + pool->next_entry_pos;
+       entry->ip = htonl(ip_host_order);
+
+       ret = insert_onion_entry(entry, pool);
+       if (ret < 0) {
+               onion_entry_destroy(entry);
+               entry = NULL;
+               goto error;
+       }
+
+       DBG("[onion] Entry added with IP address %s used as cookie",
+                       inet_ntoa(*((struct in_addr *) &entry->ip)));
+
+error:
+       return entry;
+}
+
+/*
+ * Find an onion entry by onion address name. The pool lock MUST be acquired
+ * before calling this.
+ *
+ * Return entry on success or else NULL.
+ */
+struct onion_entry *onion_entry_find_by_name(const char *onion_name,
+               struct onion_pool *pool)
+{
+       int i;
+       struct onion_entry *entry = NULL;
+
+       assert(onion_name);
+       assert(pool);
+
+       DBG("[onion] Finding onion entry for name %s", onion_name);
+
+       for (i = 0; i < pool->count; i++) {
+               if (strcmp(onion_name, pool->entries[i]->hostname) == 0) {
+                       entry = pool->entries[i];
+                       DBG("[onion] Onion entry name %s found in pool.",
+                                       entry->hostname);
+                       goto end;
+               }
+       }
+
+end:
+       return entry;
+}
+
+/*
+ * Find an onion entry by IP cookie. The pool lock MUST be acquired before
+ * calling this.
+ *
+ * Return entry on success or else NULL.
+ */
+struct onion_entry *onion_entry_find_by_ip(in_addr_t ip,
+               struct onion_pool *pool)
+{
+       int i;
+       struct onion_entry *entry = NULL;
+
+       DBG("[onion] Finding onion entry for IP %s",
+                       inet_ntoa(*((struct in_addr *) &ip)));
+
+       /*
+        * XXX: This can be improved by simply getting the offset of the IP with
+        * the pool subnet which gives the index in the pool entries. For 
instance,
+        * 127.0.0.45 with a ip_subnet set to 127.0.0.0/24, the index in the 
pool
+        * entries is 45.
+        */
+       for (i = 0; i < pool->count; i++) {
+               if (pool->entries[i]->ip == ip) {
+                       entry = pool->entries[i];
+                       DBG("[onion] Onion entry name %s found in pool.",
+                                       entry->hostname);
+                       goto end;
+               }
+       }
+
+end:
+       return entry;
+}
diff --git a/src/common/onion.h b/src/common/onion.h
new file mode 100644
index 0000000..90b4d5b
--- /dev/null
+++ b/src/common/onion.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgou...@ev0ke.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TORSOCKS_ONION_H
+#define TORSOCKS_ONION_H
+
+#include <arpa/inet.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "compat.h"
+#include "macros.h"    /* zmalloc */
+
+/*
+ * Onion entry in the pool. This is to map a cookie IP to an .onion address in
+ * the connect() process.
+ */
+struct onion_entry {
+       /*
+        * It's always an IPv4 which is taken from the onion IP range.
+        */
+       in_addr_t ip;
+
+       /*
+        * Maximum host name length plus one for the NULL terminated byte.
+        */
+       char hostname[256];
+};
+
+/*
+ * For .onion address representing an address to a hidden service, a "cookie"
+ * is used during DNS resolution which represents a specific dead IP address
+ * that is not routable on the Internet. This cookie is returned to the caller
+ * and once a connect arrives with an address being a cookie, the connection to
+ * Tor is done using the corresponding onion address.
+ *
+ * This object MUST be accessed and modified inside the torsocks registry lock
+ * to avoid cookie allocation race. Once an entry object reference is acquired,
+ * the lock can be released since the object is immutable.
+ */
+struct onion_pool {
+       /*
+        * Subnet used for the cookie address.
+        */
+       in_addr_t ip_subnet;
+
+       /*
+        * Protects every lookup and insertion in this pool object.
+        *
+        * This is nested INSIDE the connection registry lock.
+        */
+       tsocks_mutex_t lock;
+
+       /* Number of valid entry in the pool. */
+       uint32_t count;
+
+       /*
+        * Starting base of available cookie. For a range of 127.0.69.64/26, 
this
+        * base value would be 64 and the max value in this case is 127.
+        *
+        * If the maxium value is reached, the DNS resolution will fail thus 
never
+        * returning any cookie to the caller.
+        */
+       uint32_t base;
+       uint32_t max_pos;
+
+       /*
+        * Current size of the array. This is the number of allocated entry in 
the
+        * pool which does not represent the number of entry.
+        */
+       uint32_t size;
+
+       /*
+        * This is the next available entry position. Once the onion entry is 
added
+        * to the pool, this counter is incremented. If the pool needs to be
+        * resized, a reallocation is done and size is updated accordingly.
+        */
+       uint32_t next_entry_pos;
+
+       /*
+        * Array of onion entry indexed by cookie position. For instance, using 
the
+        * IP range 127.0.69.0/24, the array is of maximum size 255 and address
+        * 127.0.69.32 points to the 32th position in the array.
+        */
+       struct onion_entry **entries;
+};
+
+/*
+ * Destroy an onion entry object.
+ */
+static inline void onion_entry_destroy(struct onion_entry *entry)
+{
+       free(entry);
+}
+
+/* Onion entry family functions. */
+struct onion_entry *onion_entry_create(struct onion_pool *pool,
+               const char *onion_name);
+struct onion_entry *onion_entry_find_by_name(const char *onion_name,
+               struct onion_pool *pool);
+struct onion_entry *onion_entry_find_by_ip(in_addr_t ip,
+               struct onion_pool *pool);
+
+static inline void onion_pool_lock(struct onion_pool *pool)
+{
+       tsocks_mutex_lock(&pool->lock);
+}
+
+static inline void onion_pool_unlock(struct onion_pool *pool)
+{
+       tsocks_mutex_unlock(&pool->lock);
+}
+
+/*
+ * Onion pool function calls.
+ */
+int onion_pool_init(struct onion_pool *pool, in_addr_t base, uint8_t mask);
+void onion_pool_destroy(struct onion_pool *pool);
+
+#endif /* TORSOCKS_ONION_H */
diff --git a/src/common/socks5.c b/src/common/socks5.c
index 91beb05..d24f191 100644
--- a/src/common/socks5.c
+++ b/src/common/socks5.c
@@ -290,14 +290,17 @@ int socks5_send_connect_request(struct connection *conn)
                memcpy(buffer, &msg, buf_len);
 
                /* Setup domain name request buffer. */
-               memcpy(req_name.name, conn->dest_addr.hostname.addr,
-                               sizeof(req_name.name));
-               req_name.port = conn->dest_addr.hostname.port;
                req_name.len = strlen(conn->dest_addr.hostname.addr);
+               memcpy(req_name.name, conn->dest_addr.hostname.addr, 
req_name.len);
+               req_name.port = conn->dest_addr.hostname.port;
 
                /* Copy ipv6 request portion in the buffer. */
-               memcpy(buffer + buf_len, &req_name, sizeof(req_name));
-               buf_len += sizeof(req_name);
+               memcpy(buffer + buf_len, &req_name.len, sizeof(req_name.len));
+               buf_len += sizeof(req_name.len);
+               memcpy(buffer + buf_len, req_name.name, req_name.len);
+               buf_len += req_name.len;
+               memcpy(buffer + buf_len, &req_name.port, sizeof(req_name.port));
+               buf_len += sizeof(req_name.port);
                break;
        }
        default:
@@ -342,7 +345,7 @@ int socks5_recv_connect_reply(struct connection *conn)
        /* Len of BND.PORT */
        recv_len += sizeof(uint16_t);
 
-       switch (tsocks_config.socks5_addr.domain) {
+       switch (conn->dest_addr.domain) {
        case CONNECTION_DOMAIN_NAME:
                /*
                 * Tor returns and IPv4 upon resolution. Same for .onion 
address.
diff --git a/src/common/utils.c b/src/common/utils.c
index f041cd1..11a3652 100644
--- a/src/common/utils.c
+++ b/src/common/utils.c
@@ -157,3 +157,20 @@ char *utils_strsplit(char *separator, char **text, const 
char *search)
 
        return ret;
 }
+
+/*
+ * Compares the last strlen(s2) characters of s1 with s2.
+ *
+ * Returns as for strcasecmp.
+ */
+int utils_strcasecmpend(const char *s1, const char *s2)
+{
+       size_t n1 = strlen(s1), n2 = strlen(s2);
+
+       if (n2 > n1) {
+               /* Then they can't be the same; figure out which is bigger */
+               return strcasecmp(s1, s2);
+       } else {
+               return strncasecmp(s1 + (n1 - n2), s2, n2);
+       }
+}
diff --git a/src/common/utils.h b/src/common/utils.h
index f6a25e9..aeda872 100644
--- a/src/common/utils.h
+++ b/src/common/utils.h
@@ -23,6 +23,7 @@
 #include <netinet/in.h>
 
 char *utils_strsplit(char *separator, char **text, const char *search);
+int utils_strcasecmpend(const char *s1, const char *s2);
 int utils_tokenize_ignore_comments(const char *_line, size_t size, char 
**tokens);
 
 int utils_is_address_ipv4(const char *ip);
diff --git a/src/lib/connect.c b/src/lib/connect.c
index cee4d76..6ab2a82 100644
--- a/src/lib/connect.c
+++ b/src/lib/connect.c
@@ -21,6 +21,7 @@
 
 #include <common/connection.h>
 #include <common/log.h>
+#include <common/onion.h>
 
 #include "torsocks.h"
 
@@ -32,6 +33,8 @@ LIBC_CONNECT_RET_TYPE tsocks_connect(LIBC_CONNECT_SIG)
        int ret, sock_type;
        socklen_t optlen;
        struct connection *new_conn;
+       struct onion_entry *on_entry;
+       struct sockaddr_in *inet_addr;
 
        DBG("Connect catched on fd %d", __sockfd);
 
@@ -61,6 +64,8 @@ LIBC_CONNECT_RET_TYPE tsocks_connect(LIBC_CONNECT_SIG)
        DBG("[connect] Socket family %s and type %d",
                        __addr->sa_family == AF_INET ? "AF_INET" : "AF_INET6", 
sock_type);
 
+       inet_addr = (struct sockaddr_in *) __addr;
+
        /*
         * Lock registry to get the connection reference if one. In this code 
path,
         * if a connection object is found, it will not be used since a double
@@ -76,10 +81,34 @@ LIBC_CONNECT_RET_TYPE tsocks_connect(LIBC_CONNECT_SIG)
                goto error;
        }
 
-       new_conn = connection_create(__sockfd, __addr);
-       if (!new_conn) {
-               errno = ENOMEM;
-               goto error;
+       /*
+        * See if the IP being connected is an onion IP cookie mapping to an
+        * existing .onion address.
+        */
+       onion_pool_lock(&tsocks_onion_pool);
+       on_entry = onion_entry_find_by_ip(inet_addr->sin_addr.s_addr,
+                       &tsocks_onion_pool);
+       onion_pool_unlock(&tsocks_onion_pool);
+
+       if (on_entry) {
+               /*
+                * Create a connection without a destination address since we 
will set
+                * the onion address name found before.
+                */
+               new_conn = connection_create(__sockfd, NULL);
+               if (!new_conn) {
+                       errno = ENOMEM;
+                       goto error;
+               }
+               new_conn->dest_addr.domain = CONNECTION_DOMAIN_NAME;
+               new_conn->dest_addr.hostname.addr = strdup(on_entry->hostname);
+               new_conn->dest_addr.hostname.port = inet_addr->sin_port;
+       } else {
+               new_conn = connection_create(__sockfd, __addr);
+               if (!new_conn) {
+                       errno = ENOMEM;
+                       goto error;
+               }
        }
 
        /* Connect the socket to the Tor network. */
diff --git a/src/lib/torsocks.c b/src/lib/torsocks.c
index bc5959a..f40de3c 100644
--- a/src/lib/torsocks.c
+++ b/src/lib/torsocks.c
@@ -26,7 +26,9 @@
 #include <common/connection.h>
 #include <common/defaults.h>
 #include <common/log.h>
+#include <common/onion.h>
 #include <common/socks5.h>
+#include <common/utils.h>
 
 #include "torsocks.h"
 
@@ -39,6 +41,13 @@
 struct configuration tsocks_config;
 
 /*
+ * This is the onion address pool for the library. It is initialized once in
+ * the constructor. This object changes over time and every access is nested
+ * inside the registry lock.
+ */
+struct onion_pool tsocks_onion_pool;
+
+/*
  * Set to 1 if the binary is set with suid or 0 if not. This is set once during
  * initialization so after that it can be read without any protection.
  */
@@ -87,6 +96,10 @@ static void init_config(void)
        if (tsocks_config.conf_file.tor_domain == 0) {
                tsocks_config.conf_file.tor_domain = DEFAULT_TOR_DOMAIN;
        }
+       if (tsocks_config.conf_file.onion_base == 0) {
+               tsocks_config.conf_file.onion_base = 
inet_addr(DEFAULT_ONION_ADDR_RANGE);
+               tsocks_config.conf_file.onion_mask = 
atoi(DEFAULT_ONION_ADDR_MASK);
+       }
 
        /* Create the Tor SOCKS5 connection address. */
        ret = connection_addr_set(tsocks_config.conf_file.tor_domain,
@@ -160,6 +173,8 @@ static void init_logging(void)
  */
 static void __attribute__((constructor)) tsocks_init(void)
 {
+       int ret;
+
        /* UID and effective UID MUST be the same or else we are SUID. */
        is_suid = (getuid() != geteuid());
 
@@ -178,6 +193,17 @@ static void __attribute__((constructor)) tsocks_init(void)
 
        /* Initialize connection reigstry. */
        connection_registry_init();
+
+       /*
+        * Initalized the onion pool which maps cookie address to hidden service
+        * onion address.
+        */
+       ret = onion_pool_init(&tsocks_onion_pool,
+                       tsocks_config.conf_file.onion_base,
+                       tsocks_config.conf_file.onion_mask);
+       if (ret < 0) {
+               clean_exit(EXIT_FAILURE);
+       }
 }
 
 /*
@@ -185,6 +211,8 @@ static void __attribute__((constructor)) tsocks_init(void)
  */
 static void __attribute__((destructor)) tsocks_exit(void)
 {
+       /* Cleanup every entries in the onion pool. */
+       onion_pool_destroy(&tsocks_onion_pool);
        /* Cleanup allocated memory in the config file. */
        config_file_destroy(&tsocks_config.conf_file);
        /* Clean up logging. */
@@ -224,6 +252,45 @@ error:
 }
 
 /*
+ * Lookup by hostname for an onion entry in a given pool. The entry is returned
+ * if found or else a new one is created, added to the pool and finally
+ * returned.
+ *
+ * NOTE: The pool lock MUST NOT be acquired before calling this.
+ *
+ * On success the entry is returned else a NULL value.
+ */
+static struct onion_entry *get_onion_entry(const char *hostname,
+               struct onion_pool *pool)
+{
+       struct onion_entry *entry = NULL;
+
+       assert(hostname);
+       assert(pool);
+
+       tsocks_mutex_lock(&pool->lock);
+
+       entry = onion_entry_find_by_name(hostname, pool);
+       if (entry) {
+               goto end;
+       }
+
+       /*
+        * On success, the onion entry is automatically added to the onion pool 
and
+        * the reference is returned.
+        */
+       entry = onion_entry_create(pool, hostname);
+       if (!entry) {
+               goto error;
+       }
+
+end:
+error:
+       tsocks_mutex_unlock(&pool->lock);
+       return entry;
+}
+
+/*
  * Initiate a SOCK5 connection to the Tor network using the given connection.
  * The socks5 API will use the torsocks configuration object to find the tor
  * daemon.
@@ -273,6 +340,22 @@ int tsocks_tor_resolve(const char *hostname, uint32_t 
*ip_addr)
 
        DBG("Resolving %s on the Tor network", hostname);
 
+       /*
+        * Tor hidden service address have no IP address so we send back an 
onion
+        * reserved IP address that acts as a cookie that we will use to find 
the
+        * onion hostname at the connect() stage.
+        */
+       if (utils_strcasecmpend(hostname, ".onion") == 0) {
+               struct onion_entry *entry;
+
+               entry = get_onion_entry(hostname, &tsocks_onion_pool);
+               if (entry) {
+                       memcpy(ip_addr, &entry->ip, sizeof(entry->ip));
+                       ret = 0;
+                       goto end;
+               }
+       }
+
        conn.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (conn.fd < 0) {
                PERROR("socket");
@@ -301,6 +384,7 @@ int tsocks_tor_resolve(const char *hostname, uint32_t 
*ip_addr)
                PERROR("close");
        }
 
+end:
 error:
        return ret;
 }
diff --git a/src/lib/torsocks.h b/src/lib/torsocks.h
index 2e6fd50..2d40470 100644
--- a/src/lib/torsocks.h
+++ b/src/lib/torsocks.h
@@ -143,6 +143,9 @@ enum tsocks_sym_action {
 /* Global configuration. Initialized once in the library constructor. */
 extern struct configuration tsocks_config;
 
+/* Global pool for .onion address. Initialized once in the constructor. */
+extern struct onion_pool tsocks_onion_pool;
+
 int tsocks_connect_to_tor(struct connection *conn);
 void *tsocks_find_libc_symbol(const char *symbol,
                enum tsocks_sym_action action);



_______________________________________________
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits

Reply via email to