tor-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
April 2014
- 22 participants
- 2020 discussions
commit 50fff4de31168dd5214e446843955652dafaf779
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Tue Jun 11 21:50:56 2013 -0400
Add SOCKS5 interface to connect
This changes the connection ABI to support IPv4/IPV6.
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/common/Makefile.am | 2 +-
src/common/connection.h | 22 ++-
src/common/defaults.h | 6 +
src/common/socks5.c | 345 +++++++++++++++++++++++++++++++++++++++++++++++
src/common/socks5.h | 14 +-
5 files changed, 383 insertions(+), 6 deletions(-)
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index d2123de..ab4d166 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -4,4 +4,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
+ compat.c compat.h socks5.c socks5.h
diff --git a/src/common/connection.h b/src/common/connection.h
index c8711ad..75c3945 100644
--- a/src/common/connection.h
+++ b/src/common/connection.h
@@ -18,18 +18,34 @@
#ifndef TORSOCKS_CONNECTION_H
#define TORSOCKS_CONNECTION_H
+#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include "defaults.h"
+
+enum connection_domain {
+ CONNECTION_DOMAIN_INET = 1,
+ CONNECTION_DOMAIN_INET6 = 2,
+};
+
+struct connection_addr {
+ enum connection_domain domain;
+ union {
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } u;
+};
+
struct connection {
/* Socket fd and also unique ID. */
int fd;
/* Location of the SOCKS5 server. */
- struct sockaddr_in socks5_addr;
+ struct connection_addr socks5_addr;
- /* Remove destination that passes through Tor. */
- struct sockaddr_in dest_addr;
+ /* Remote destination that passes through Tor. */
+ struct connection_addr dest_addr;
/* Next connection of the linked list. */
struct connection *next, *prev;
diff --git a/src/common/defaults.h b/src/common/defaults.h
index 6e11e83..3f4c8d6 100644
--- a/src/common/defaults.h
+++ b/src/common/defaults.h
@@ -31,4 +31,10 @@
#define DEFAULT_LOG_TIME_STATUS LOG_TIME_ADD
#define DEFAULT_LOG_LEVEL MSGWARN
+/*
+ * RFC 1035 specifies a maxium of 255 possibe for domain name.
+ * (https://www.ietf.org/rfc/rfc1035.txt)
+ */
+#define DEFAULT_DOMAIN_NAME_SIZE 255
+
#endif /* TORSOCKS_DEFAULTS_H */
diff --git a/src/common/socks5.c b/src/common/socks5.c
index 68a1d2f..817f120 100644
--- a/src/common/socks5.c
+++ b/src/common/socks5.c
@@ -15,4 +15,349 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <assert.h>
+#include <errno.h>
+
+#include <lib/torsocks.h>
+
+#include "log.h"
#include "socks5.h"
+
+/*
+ * Receive data on a given file descriptor using recv(2). This handles partial
+ * send and EINTR.
+ *
+ * Return the number of bytes received or a negative errno error.
+ */
+static ssize_t recv_data(int fd, void *buf, size_t len)
+{
+ ssize_t ret, read_len, read_left, index;
+
+ assert(buf);
+ assert(fd >= 0);
+
+ read_left = len;
+ index = 0;
+ do {
+ read_len = recv(fd, buf + index, read_left, 0);
+ if (read_len < 0) {
+ ret = -errno;
+ if (errno == EINTR) {
+ /* Try again after interruption. */
+ continue;
+ } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ if (index) {
+ /* Return the number of bytes received up to this point. */
+ ret = index;
+ }
+ goto error;
+ } else {
+ PERROR("recv socks5 data");
+ goto error;
+ }
+ }
+ read_left -= read_len;
+ index += read_len;
+ } while (read_left > 0);
+
+ /* Everything was received. */
+ ret = index;
+
+error:
+ return ret;
+}
+
+/*
+ * Send data to a given file descriptor using send(2). This handles partial
+ * send and EINTR.
+ *
+ * Return the number of bytes sent or a negative errno error.
+ */
+static ssize_t send_data(int fd, const void *buf, size_t len)
+{
+ ssize_t ret, sent_len, sent_left, index;
+
+ assert(buf);
+ assert(fd >= 0);
+
+ sent_left = len;
+ index = 0;
+ do {
+ sent_len = send(fd, buf + index, sent_left, 0);
+ if (sent_len < 0) {
+ ret = -errno;
+ if (errno == EINTR) {
+ /* Send again after interruption. */
+ continue;
+ } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ if (index) {
+ /* Return the number of bytes sent up to this point. */
+ ret = index;
+ }
+ goto error;
+ } else {
+ PERROR("send socks5 data");
+ goto error;
+ }
+ }
+ sent_left -= sent_len;
+ index += sent_len;
+ } while (sent_left > 0);
+
+ /* Everything was sent. */
+ ret = index;
+
+error:
+ return ret;
+}
+
+/*
+ * Connect to socks5 server address in the connection object.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int socks5_connect(struct connection *conn)
+{
+ int ret;
+ struct sockaddr *socks5_addr = NULL;
+
+ assert(conn);
+ assert(conn->fd >= 0);
+
+ switch (conn->socks5_addr.domain) {
+ case CONNECTION_DOMAIN_INET:
+ socks5_addr = (struct sockaddr *) &conn->socks5_addr.u.sin;
+ break;
+ case CONNECTION_DOMAIN_INET6:
+ socks5_addr = (struct sockaddr *) &conn->socks5_addr.u.sin6;
+ break;
+ default:
+ ERR("Socks5 connect domain unknown %d", conn->socks5_addr.domain);
+ assert(0);
+ ret = -EBADF;
+ goto error;
+ }
+
+ /* Use the original libc connect() to the Tor. */
+ ret = tsocks_libc_connect(conn->fd, socks5_addr, sizeof(*socks5_addr));
+ if (ret < 0) {
+ ret = -errno;
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * Send socks5 method packet to server.
+ *
+ * Return 0 on success or else a negative errno value.
+ */
+int socks5_send_method(struct connection *conn)
+{
+ int ret = 0;
+ ssize_t ret_send;
+ struct socks5_method_req msg;
+
+ assert(conn);
+ assert(conn->fd >= 0);
+
+ msg.ver = SOCKS5_VERSION;
+ msg.nmethods = 0x01;
+ msg.methods = SOCKS5_NO_AUTH_METHOD;
+
+ DBG("Socks5 sending method ver: %d, nmethods 0x%02x, methods 0x%02x",
+ msg.ver, msg.nmethods, msg.methods);
+
+ ret_send = send_data(conn->fd, &msg, sizeof(msg));
+ if (ret_send < 0) {
+ ret = ret_send;
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * Receive socks5 method response packet from server.
+ *
+ * Return 0 on success or else a negative errno value.
+ */
+int socks5_recv_method(struct connection *conn)
+{
+ int ret;
+ ssize_t ret_recv;
+ struct socks5_method_res msg;
+
+ assert(conn);
+ assert(conn->fd >= 0);
+
+ ret_recv = recv_data(conn->fd, &msg, sizeof(msg));
+ if (ret_recv < 0) {
+ ret = ret_recv;
+ goto error;
+ }
+
+ DBG("Socks5 received method ver: %d, method 0x%02x", msg.ver, msg.method);
+
+ if (msg.ver != SOCKS5_VERSION ||
+ msg.method == SOCKS5_NO_ACCPT_METHOD) {
+ ret = -ECONNABORTED;
+ goto error;
+ }
+
+ /* Successfully received. */
+ ret = 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Send a connect request to the SOCKS5 server using the given connection and
+ * the destination address in it pointing to the destination that needs to be
+ * reached through Tor.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int socks5_send_connect_request(struct connection *conn)
+{
+ int ret;
+ /* Buffer to send won't go over a full TCP size. */
+ char buffer[1500];
+ ssize_t buf_len, ret_send;
+ struct socks5_request msg;
+
+ assert(conn);
+ assert(conn->fd >= 0);
+
+ memset(buffer, 0, sizeof(buffer));
+ buf_len = sizeof(msg);
+
+ msg.ver = SOCKS5_VERSION;
+ msg.cmd = SOCKS5_CMD_CONNECT;
+ /* Always zeroed. */
+ msg.rsv = 0;
+
+ /* Select connection socket domain. */
+ if (conn->dest_addr.domain == CONNECTION_DOMAIN_INET) {
+ struct socks5_request_ipv4 req_ipv4;
+
+ msg.atyp = SOCKS5_ATYP_IPV4;
+ /* Copy the first part of the request. */
+ memcpy(buffer, &msg, buf_len);
+
+ /* Prepare the ipv4 payload to be copied in the send buffer. */
+ memcpy(req_ipv4.addr, &conn->dest_addr.u.sin.sin_addr,
+ sizeof(req_ipv4.addr));
+ req_ipv4.port = conn->dest_addr.u.sin.sin_port;
+
+ /* Copy ipv4 request portion in the buffer. */
+ memcpy(buffer + buf_len, &req_ipv4, sizeof(req_ipv4));
+ buf_len += sizeof(req_ipv4);
+ } else if (conn->dest_addr.domain == CONNECTION_DOMAIN_INET6) {
+ struct socks5_request_ipv6 req_ipv6;
+
+ msg.atyp = SOCKS5_ATYP_IPV6;
+ /* Copy the first part of the request. */
+ memcpy(buffer, &msg, buf_len);
+
+ /* Prepare the ipv6 payload to be copied in the send buffer. */
+ memcpy(req_ipv6.addr, &conn->dest_addr.u.sin6.sin6_addr,
+ sizeof(req_ipv6.addr));
+ req_ipv6.port = conn->dest_addr.u.sin6.sin6_port;
+
+ /* Copy ipv6 request portion in the buffer. */
+ memcpy(buffer + buf_len, &req_ipv6, sizeof(req_ipv6));
+ buf_len += sizeof(req_ipv6);
+ } else {
+ ERR("Socks5 connection domain unknown %d", conn->dest_addr.domain);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ DBG("Socks5 sending connect request to fd %d", conn->fd);
+
+ ret_send = send_data(conn->fd, &buffer, buf_len);
+ if (ret_send < 0) {
+ ret = ret_send;
+ goto error;
+ }
+
+ /* Data was sent successfully. */
+ ret = 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Receive on the given connection the SOCKS5 connect reply.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int socks5_recv_connect_reply(struct connection *conn)
+{
+ int ret;
+ ssize_t ret_recv;
+ struct socks5_reply msg;
+
+ assert(conn);
+ assert(conn >= 0);
+
+ ret_recv = recv_data(conn->fd, &msg, sizeof(msg));
+ if (ret_recv < 0) {
+ ret = ret_recv;
+ goto error;
+ }
+
+ DBG("Socks5 received connect reply - ver: %d, rep: 0x%02x, atype: 0x%02x",
+ msg.ver, msg.rep, msg.atyp);
+
+ switch (msg.rep) {
+ case SOCKS5_REPLY_SUCCESS:
+ DBG("Socks5 connection is successful.");
+ ret = 0;
+ break;
+ case SOCKS5_REPLY_FAIL:
+ ERR("General SOCKS server failure");
+ ret = -ECONNREFUSED;
+ break;
+ case SOCKS5_REPLY_DENY_RULE:
+ ERR("Connection not allowed by ruleset");
+ ret = -ECONNABORTED;
+ break;
+ case SOCKS5_REPLY_NO_NET:
+ ERR("Network unreachable");
+ ret = -ENETUNREACH;
+ break;
+ case SOCKS5_REPLY_NO_HOST:
+ ERR("Host unreachable");
+ ret = -EHOSTUNREACH;
+ break;
+ case SOCKS5_REPLY_REFUSED:
+ ERR("Connection refused to Tor SOCKS");
+ ret = -ECONNREFUSED;
+ break;
+ case SOCKS5_REPLY_TTL_EXP:
+ ERR("Connection timed out");
+ ret = -ETIMEDOUT;
+ break;
+ case SOCKS5_REPLY_CMD_NOTSUP:
+ ERR("Command not supported");
+ ret = -ECONNREFUSED;
+ break;
+ case SOCKS5_REPLY_ADR_NOTSUP:
+ ERR("Address type not supported");
+ ret = -ECONNREFUSED;
+ break;
+ default:
+ ERR("Socks5 server replied an unknown code %d", msg.rep);
+ ret = -ECONNABORTED;
+ break;
+ }
+
+error:
+ return ret;
+}
diff --git a/src/common/socks5.h b/src/common/socks5.h
index b8e0e69..34935f4 100644
--- a/src/common/socks5.h
+++ b/src/common/socks5.h
@@ -32,6 +32,7 @@
* METHOD" [00] is supported and should be used.
*/
#define SOCKS5_NO_AUTH_METHOD 0x00
+#define SOCKS5_NO_ACCPT_METHOD 0xFF
/* Request to connect. */
#define SOCKS5_CMD_CONNECT 0x01
@@ -56,6 +57,7 @@
struct socks5_method_req {
uint8_t ver;
uint8_t nmethods;
+ uint8_t methods;
};
/* Reply data structure for the method. */
@@ -74,14 +76,12 @@ struct socks5_request {
/* IPv4 destination addr for a request. */
struct socks5_request_ipv4 {
- uint8_t len;
uint8_t addr[4];
uint16_t port;
};
/* IPv6 destination addr for a request. */
struct socks5_request_ipv6 {
- uint8_t len;
uint8_t addr[16];
uint16_t port;
};
@@ -102,4 +102,14 @@ struct socks5_reply {
uint8_t atyp;
};
+int socks5_connect(struct connection *conn);
+
+/* Method messaging. */
+int socks5_send_method(struct connection *conn);
+int socks5_recv_method(struct connection *conn);
+
+/* Connect request. */
+int socks5_send_connect_request(struct connection *conn);
+int socks5_recv_connect_reply(struct connection *conn);
+
#endif /* TORSOCKS_SOCKS_H */
1
0

04 Apr '14
commit edc557f83905eb2940f315e31d1841971357aa6a
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Thu Jun 6 20:39:56 2013 -0400
Add basic connection object interface
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/common/connection.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++
src/common/connection.h | 38 ++++++++++++++++++++++++++++++
2 files changed, 98 insertions(+)
diff --git a/src/common/connection.c b/src/common/connection.c
new file mode 100644
index 0000000..0831340
--- /dev/null
+++ b/src/common/connection.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet(a)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 "connection.h"
+
+/*
+ * Create a new connection with the given fd and destination address.
+ *
+ * Return a newly allocated connection object or else NULL.
+ */
+struct connection *connection_create(int fd, struct sockaddr_in *dest)
+{
+ struct connection *conn;
+
+ assert(dest);
+
+ conn = zmalloc(sizeof(*conn));
+ if (!conn) {
+ PERROR("zmalloc connection");
+ goto error;
+ }
+
+ conn->fd = fd;
+ memcpy(conn->dest_addr, dest, sizeof(conn->dest_addr));
+
+ return conn;
+
+error:
+ return NULL;
+}
+
+/*
+ * Destroy a connection by freeing its memory.
+ */
+void connection_destroy(struct connection *conn)
+{
+ if (!conn) {
+ return;
+ }
+
+ /* Remove from the double linked list. */
+ conn->prev->next = conn->next;
+ conn->next->prev = conn->prev;
+
+ free(conn);
+}
diff --git a/src/common/connection.h b/src/common/connection.h
new file mode 100644
index 0000000..c8711ad
--- /dev/null
+++ b/src/common/connection.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet(a)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_CONNECTION_H
+#define TORSOCKS_CONNECTION_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+struct connection {
+ /* Socket fd and also unique ID. */
+ int fd;
+
+ /* Location of the SOCKS5 server. */
+ struct sockaddr_in socks5_addr;
+
+ /* Remove destination that passes through Tor. */
+ struct sockaddr_in dest_addr;
+
+ /* Next connection of the linked list. */
+ struct connection *next, *prev;
+};
+
+#endif /* TORSOCKS_CONNECTION_H */
1
0
commit d2eec3629d0d9bd9749934b687ea2070b73809de
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Sat Jun 15 19:50:36 2013 -0400
Add comments in torsocks.h
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/lib/torsocks.h | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/lib/torsocks.h b/src/lib/torsocks.h
index 14d0694..9ba2955 100644
--- a/src/lib/torsocks.h
+++ b/src/lib/torsocks.h
@@ -23,6 +23,11 @@
#include <common/compat.h>
#include <common/config-file.h>
+/*
+ * This defines a function pointer to the original libc call of "name" so the
+ * libc call outside of torsocks can be used. These are declared for each
+ * symbol torsocks hijacked.
+ */
#define TSOCKS_LIBC_DECL(name, type, sig) \
type (*tsocks_libc_##name)(sig);
@@ -53,11 +58,16 @@ TSOCKS_LIBC_DECL(connect, LIBC_CONNECT_RET_TYPE, LIBC_CONNECT_SIG)
#define LIBC_CONNECT_DECL \
LIBC_CONNECT_RET_TYPE LIBC_CONNECT_NAME(LIBC_CONNECT_SIG)
+/*
+ * Those are actions to do during the lookup process of libc symbols. For
+ * instance the connect(2) syscall is essential to Torsocks so the function
+ * call exits if not found.
+ */
enum tsocks_sym_action {
TSOCKS_SYM_EXIT_NOT_FOUND = 1,
};
-/* Global configuration of torsocks. */
+/* Global configuration. Initialized once in the library constructor. */
extern struct configuration tsocks_config;
#endif /* TORSOCKS_H */
1
0

04 Apr '14
commit 501ee46377144fc1fc769c82fe77b67efd4c9e6c
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Wed Jun 12 18:48:42 2013 -0400
Multiple changes accross the code base
This is a pretty big commit because a lot of inter connected things had
to be changed across the code base.
The config file part is completely reengineered because it turns out the
torsocks.conf was not really "Tor centric" and was adding features that
are IMHO out of the scope of torsocks. For now only two options are
supported which are "TorAddress" and "TorPort" having a format like Tor
config file has.
The global configuration object is added also which is initialized
within the constructor of the library using the new config file API.
Some utils functions were added to support that as well.
Also, the connection.c/.h is improved.
At this commit, torsocks can parse a config file or use default values
if none found, do logging and connect to Tor using only SOCKS5.
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
doc/torsocks.conf | 47 +--
src/common/Makefile.am | 3 +-
src/common/config-file.c | 876 +++++++++-------------------------------------
src/common/config-file.h | 71 ++--
src/common/connection.c | 93 ++++-
src/common/connection.h | 17 +-
src/common/defaults.h | 12 +-
src/common/log.h | 2 +-
src/common/socks5.c | 11 +-
src/common/utils.c | 99 ++++--
src/common/utils.h | 5 +-
src/lib/Makefile.am | 2 +-
src/lib/torsocks.c | 83 +++++
src/lib/torsocks.h | 4 +
14 files changed, 483 insertions(+), 842 deletions(-)
diff --git a/doc/torsocks.conf b/doc/torsocks.conf
index 8cc1b2c..60eb697 100644
--- a/doc/torsocks.conf
+++ b/doc/torsocks.conf
@@ -2,16 +2,9 @@
# with tor, which is providing a socks server on port 9050 by default.
#
# Lines beginning with # and blank lines are ignored
-#
-# The basic idea is to specify:
-# - Local subnets - Networks that can be accessed directly without
-# assistance from a socks server
-# - Paths - Paths are basically lists of networks and a socks server
-# which can be used to reach these networks
-# - Default server - A socks server which should be used to access
-# networks for which no path is available
# Much more documentation than provided in these comments can be found in
-# torsocks.conf(5) and usewithtor(1) manpages.
+#
+# torsocks.conf(5) and torsocks(1) manpages.
# We specify local as 127.0.0.0 - 127.191.255.255 because the
# Tor MAPADDRESS virtual IP range is the rest of net 127.
@@ -21,36 +14,8 @@ local = 127.128.0.0/255.192.0.0
local = 169.254.0.0/255.255.0.0
local = 172.16.0.0/255.240.0.0
local = 192.168.0.0/255.255.0.0
-
-# Default server
-# For connections that aren't to the local subnets
-# the server at 127.0.0.1 should be used (again, hostnames could be used
-# too, see note above)
-server = 127.0.0.1
-
-# SOCKS server type defaults to 4
-#server_type = 5
-
-# The port defaults to 1080 but I've stated it here for clarity
-server_port = 9050
-# Username and password (if required on a SOCKSv5 server)
-#default_user =
-#default_pass =
-
-# Paths
-# For this example this machine needs to access 150.0.0.0/255.255.0.0 as
-# well as port 80 on the network 150.1.0.0/255.255.0.0 through
-# the socks 5 server at 10.1.7.25 (if this machines hostname was
-# "socks.hello.com" we could also specify that, unless --disable-hostnames
-# was specified to ./configure).
-
-#path {
-# reaches = 150.0.0.0/255.255.0.0
-# reaches = 150.1.0.0:80/255.255.0.0
-# server = 10.1.7.25
-# server_type = 5
-# default_user = delius
-# default_pass = hello
-#}
-#
+# Default Tor address and port. By default, Tor will listen on localhost for
+# any SOCKS connection and relay the traffic on the Tor network.
+TorAddress 127.0.0.1
+TorPort 9050
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index ab4d166..13d4d41 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -4,4 +4,5 @@ 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
+ compat.c compat.h socks5.c socks5.h \
+ connection.c connection.h
diff --git a/src/common/config-file.c b/src/common/config-file.c
index 47f6a09..0a05325 100644
--- a/src/common/config-file.c
+++ b/src/common/config-file.c
@@ -17,690 +17,163 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include <arpa/inet.h>
-#include <errno.h>
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include <config.h>
#include "config-file.h"
#include "log.h"
#include "utils.h"
-/* Global configuration variables. */
-static struct config_server_entry *currentcontext = NULL;
+/*
+ * These are the torsocks.conf string values.
+ */
+static const char *conf_toraddr_str = "TorAddress";
+static const char *conf_torport_str = "TorPort";
-/* Check server entries (and establish defaults) */
-static int check_server(struct config_server_entry *server)
+/*
+ * Set the given string port in a configuration object.
+ *
+ * Return 0 on success or else a negative EINVAL if the port is equal to 0 or
+ * over 65535.
+ */
+static int set_tor_port(const char *port, struct configuration *config)
{
- /* Default to the default Tor Socks port */
- if (server->port == 0) {
- server->port = 9050;
- }
-
- /* Default to a presumably local installation of Tor */
- if (server->address == NULL) {
- server->address = strdup("127.0.0.1");
- }
-
- /* Default to SOCKS V4 */
- if (server->type == 0) {
- server->type = 4;
- }
-
- return(0);
-}
-
-static int handle_endpath(struct config_parsed *config, int lineno, int nowords) {
-
- if (nowords != 1) {
- ERR("Badly formed path close statement on line %d in configuration"
- "file (should look like \"}\")\n", lineno);
- } else {
- currentcontext = &(config->default_server);
- }
-
- /* We could perform some checking on the validty of data in */
- /* the completed path here, but thats what verifyconf is */
- /* designed to do, no point in weighing down libtorsocks */
-
- return(0);
-}
-
-/* Construct a config_network_entry given a string like */
-/* "198.126.0.1[:portno[-portno]]/255.255.255.0" */
-int make_config_network_entry(char *value, struct config_network_entry **ent) {
- char *ip;
- char *subnet;
- char *start_port = NULL;
- char *end_port = NULL;
- char *badchar;
- char separator;
- static char buf[200];
- char *split;
-
- /* Get a copy of the string so we can modify it */
- strncpy(buf, value, sizeof(buf) - 1);
- buf[sizeof(buf) - 1] = (char) 0;
- split = buf;
-
- /* Now rip it up */
- ip = utils_strsplit(&separator, &split, "/:");
- if (separator == ':') {
- /* We have a start port */
- start_port = utils_strsplit(&separator, &split, "-/");
- if (separator == '-')
- /* We have an end port */
- end_port = utils_strsplit(&separator, &split, "/");
- }
- subnet = utils_strsplit(NULL, &split, " \n");
-
- if ((ip == NULL) || (subnet == NULL)) {
- /* Network specification not validly constructed */
- return(1);
- }
-
- /* Allocate the new entry */
- if ((*ent = (struct config_network_entry *) malloc(sizeof(struct config_network_entry)))
- == NULL) {
- /* If we couldn't malloc some storage, leave */
- exit(1);
- }
-
- DBG("New network entry for %s going to 0x%08x\n", ip, *ent);
-
- if (!start_port)
- (*ent)->start_port = 0;
- if (!end_port)
- (*ent)->end_port = 0;
-
-#ifdef HAVE_INET_ADDR
- if (((*ent)->local_ip.s_addr = inet_addr(ip)) == -1) {
-#elif defined(HAVE_INET_ATON)
- if (!(inet_aton(ip, &((*ent)->local_ip)))) {
-#endif
- /* Badly constructed IP */
- free(*ent);
- return(2);
- }
-#ifdef HAVE_INET_ADDR
- else if (((*ent)->local_net.s_addr = inet_addr(subnet)) == -1) {
-#elif defined(HAVE_INET_ATON)
- else if (!(inet_aton(subnet, &((*ent)->local_net)))) {
-#endif
- /* Badly constructed subnet */
- free(*ent);
- return(3);
- } else if (((*ent)->local_ip.s_addr &
- (*ent)->local_net.s_addr) !=
- (*ent)->local_ip.s_addr) {
- /* Subnet and Ip != Ip */
- free(*ent);
- return(4);
- } else if (start_port &&
- (!((*ent)->start_port = strtol(start_port, &badchar, 10)) ||
- (*badchar != 0) || ((*ent)->start_port > 65535))) {
- /* Bad start port */
- free(*ent);
- return(5);
- } else if (end_port &&
- (!((*ent)->end_port = strtol(end_port, &badchar, 10)) ||
- (*badchar != 0) || ((*ent)->end_port > 65535))) {
- /* Bad end port */
- free(*ent);
- return(6);
- } else if (((*ent)->start_port > (*ent)->end_port) && !(start_port && !end_port)) {
- /* End port is less than start port */
- free(*ent);
- return(7);
- }
-
- if (start_port && !end_port)
- (*ent)->end_port = (*ent)->start_port;
-
- return(0);
- }
-
-
-static int handle_reaches(int lineno, char *value) {
- int rc;
- struct config_network_entry *ent;
-
- rc = make_config_network_entry(value, &ent);
- switch(rc) {
- case 1:
- ERR("Local network specification (%s) is not validly "
- "constructed in reach statement on line "
- "%d in configuration "
- "file\n", value, lineno);
- return(0);
- break;
- case 2:
- ERR("IP in reach statement "
- "network specification (%s) is not valid on line "
- "%d in configuration file\n", value, lineno);
- return(0);
- break;
- case 3:
- ERR("SUBNET in reach statement "
- "network specification (%s) is not valid on "
- "line %d in configuration file\n", value,
- lineno);
- return(0);
- break;
- case 4:
- ERR("IP (%s) & ", inet_ntoa(ent->local_ip));
- ERR("SUBNET (%s) != IP on line %d in "
- "configuration file, ignored\n",
- inet_ntoa(ent->local_net), lineno);
- return(0);
- break;
- case 5:
- ERR("Start port in reach statement "
- "network specification (%s) is not valid on line "
- "%d in configuration file\n", value, lineno);
- return(0);
- break;
- case 6:
- ERR("End port in reach statement "
- "network specification (%s) is not valid on line "
- "%d in configuration file\n", value, lineno);
- return(0);
- break;
- case 7:
- ERR("End port in reach statement "
- "network specification (%s) is less than the start "
- "port on line %d in configuration file\n", value,
- lineno);
- return(0);
- break;
- }
-
- /* The entry is valid so add it to linked list */
- ent -> next = currentcontext -> reachnets;
- currentcontext -> reachnets = ent;
+ int ret = 0;
+ char *endptr;
+ unsigned long _port;
- return(0);
-}
+ assert(port);
+ assert(config);
-static int handle_server(struct config_parsed *config, int lineno, char *value) {
- char *ip;
-
- ip = utils_strsplit(NULL, &value, " ");
-
- /* We don't verify this ip/hostname at this stage, */
- /* its resolved immediately before use in torsocks.c */
- if (currentcontext->address == NULL)
- currentcontext->address = strdup(ip);
- else {
- if (currentcontext == &(config->default_server))
- ERR("Only one default SOCKS server "
- "may be specified at line %d in "
- "configuration file\n", lineno);
- else
- ERR("Only one SOCKS server may be specified "
- "per path on line %d in configuration "
- "file. (Path begins on line %d)\n",
- lineno, currentcontext->lineno);
+ /* Let's avoid a integer overflow here ;). */
+ _port = strtoul(port, &endptr, 10);
+ if (_port == 0 || _port > 65535) {
+ ret = -EINVAL;
+ ERR("Config file invalid port: %s", port);
+ goto error;
}
- return(0);
-}
+ config->conf_file.tor_port = (in_port_t) _port;
-static int handle_port(struct config_parsed *config, int lineno, char *value) {
-
- if (currentcontext->port != 0) {
- if (currentcontext == &(config->default_server))
- ERR("Server port may only be specified "
- "once for default server, at line %d "
- "in configuration file\n", lineno);
- else
- ERR("Server port may only be specified "
- "once per path on line %d in configuration "
- "file. (Path begins on line %d)\n",
- lineno, currentcontext->lineno);
- } else {
- errno = 0;
- currentcontext->port = (unsigned short int)
- (strtol(value, (char **)NULL, 10));
- if ((errno != 0) || (currentcontext->port == 0)) {
- ERR("Invalid server port number "
- "specified in configuration file "
- "(%s) on line %d\n", value, lineno);
- currentcontext->port = 0;
- }
- }
+ DBG("Config file setting tor port to %lu", _port);
- return(0);
+error:
+ return ret;
}
-static int handle_username(struct config_parsed *config, int lineno, char *value) {
-
- if (currentcontext->username != NULL) {
- if (currentcontext == &(config->default_server))
- ERR("Default username may only be specified "
- "once for default server, at line %d "
- "in configuration file\n", lineno);
- else
- ERR("Default username may only be specified "
- "once per path on line %d in configuration "
- "file. (Path begins on line %d)\n",
- lineno, currentcontext->lineno);
- } else {
- currentcontext->username = strdup(value);
- }
-
- return(0);
-}
-
-static int handle_password(struct config_parsed *config, int lineno, char *value) {
-
- if (currentcontext->password != NULL) {
- if (currentcontext == &(config->default_server))
- ERR("Default password may only be specified "
- "once for default server, at line %d "
- "in configuration file\n", lineno);
- else
- ERR("Default password may only be specified "
- "once per path on line %d in configuration "
- "file. (Path begins on line %d)\n",
- lineno, currentcontext->lineno);
- } else {
- currentcontext->password = strdup(value);
- }
+/*
+ * Set the given string address in a configuration object.
+ *
+ * Return 0 on success or else a negative value. On error, the address was not
+ * recognized.
+ */
+static int set_tor_address(const char *addr, struct configuration *config)
+{
+ int ret;
- return(0);
-}
+ assert(addr);
+ assert(config);
-static int handle_type(struct config_parsed *config, int lineno, char *value) {
-
- if (currentcontext->type != 0) {
- if (currentcontext == &(config->default_server))
- ERR("Server type may only be specified "
- "once for default server, at line %d "
- "in configuration file\n", lineno);
- else
- ERR("Server type may only be specified "
- "once per path on line %d in configuration "
- "file. (Path begins on line %d)\n",
- lineno, currentcontext->lineno);
+ ret = utils_is_address_ipv4(addr);
+ if (ret == 1 ) {
+ config->conf_file.tor_domain = CONNECTION_DOMAIN_INET;
} else {
- errno = 0;
- currentcontext->type = (int) strtol(value, (char **)NULL, 10);
- if ((errno != 0) || (currentcontext->type == 0) ||
- ((currentcontext->type != 4) && (currentcontext->type != 5))) {
- ERR("Invalid server type (%s) "
- "specified in configuration file "
- "on line %d, only 4 or 5 may be "
- "specified\n", value, lineno);
- currentcontext->type = 0;
+ ret = utils_is_address_ipv6(addr);
+ if (ret != 1) {
+ /* At this point, the addr is either v4 nor v6 so error. */
+ ERR("Config file unknown tor address: %s", addr);
+ goto error;
}
+ config->conf_file.tor_domain = CONNECTION_DOMAIN_INET6;
}
+ config->conf_file.tor_address = strdup(addr);
- return(0);
-}
-
-static int handle_flag(char *value)
-{
- if(!strcasecmp(value, "true") || !strcasecmp(value, "yes")
- || !strcmp(value, "1")) {
- return 1;
- } else if (!strcasecmp(value, "false") || !strcasecmp(value, "no")
- || !strcmp(value, "0")) {
- return 0;
- } else {
- return -1;
- }
-}
-
-static int handle_tordns_enabled(struct config_parsed *config, int lineno,
- char *value)
-{
- int val = handle_flag(value);
- if(val == -1) {
- ERR("Invalid value %s supplied for tordns_enabled at "
- "line %d in config file, IGNORED\n", value, lineno);
- } else {
- config->tordns_enabled = val;
- }
- return 0;
-}
+ DBG("Config file setting tor address to %s", addr);
+ ret = 0;
-static int handle_tordns_cache_size(struct config_parsed *config,
- char *value)
-{
- char *endptr;
- long size = strtol(value, &endptr, 10);
- if(*endptr != '\0') {
- ERR("Error parsing integer value for "
- "tordns_cache_size (%s), using default %d\n",
- value, config->tordns_cache_size);
- } else if(size < 128) {
- ERR("The value supplied for tordns_cache_size (%d) "
- "is too small (<128), using default %d\n", size,
- config->tordns_cache_size);
- } else if(size > 4096) {
- ERR("The value supplied for tordns_cache_range (%d) "
- "is too large (>4096), using default %d\n", size,
- config->tordns_cache_size);
- } else {
- config->tordns_cache_size = size;
- }
- return 0;
+error:
+ return ret;
}
-static int handle_path(struct config_parsed *config, int lineno, int nowords, char *words[])
+/*
+ * Parse a single line of from a configuration file and set the value found in
+ * the configuration object.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int parse_config_line(const char *line, struct configuration *config)
{
- struct config_server_entry *newserver;
-
- if ((nowords != 2) || (strcmp(words[1], "{"))) {
- ERR("Badly formed path open statement on line %d "
- "in configuration file (should look like "
- "\"path {\")\n", lineno);
- } else if (currentcontext != &(config->default_server)) {
- /* You cannot nest path statements so check that */
- /* the current context is default_server */
- ERR("Path statements cannot be nested on line %d "
- "in configuration file\n", lineno);
+ int ret, nb_token;
+ char *tokens[DEFAULT_MAX_CONF_TOKEN];
+
+ assert(line);
+ assert(config);
+
+ /*
+ * The line is tokenized and each token is NULL terminated.
+ */
+ nb_token = utils_tokenize_ignore_comments(line, sizeof(tokens), tokens);
+ if (nb_token <= 0) {
+ /* Nothing on this line that is useful to parse. */
+ ret = 0;
+ goto end;
+ }
+
+ if (!strcmp(tokens[0], conf_toraddr_str)) {
+ ret = set_tor_address(tokens[1], config);
+ if (ret < 0) {
+ goto error;
+ }
+ } else if (!strcmp(tokens[0], conf_torport_str)) {
+ ret = set_tor_port(tokens[1], config);
+ if (ret < 0) {
+ goto error;
+ }
} else {
- /* Open up a new config_server_entry, put it on the list */
- /* then set the current context */
- if ((newserver = malloc(sizeof(*newserver))) == NULL)
- exit(-1);
-
- /* Initialize the structure */
- DBG("New server structure from line %d in configuration file going "
- "to 0x%08x\n", lineno, newserver);
- memset(newserver, 0x0, sizeof(*newserver));
- newserver->next = config->paths;
- newserver->lineno = lineno;
- config->paths = newserver;
- currentcontext = newserver;
+ WARN("Config file contains unknown value: %s", line);
}
- return(0);
-}
+ /* Everything went well. */
+ ret = 0;
-static int handle_local(struct config_parsed *config, int lineno, const char *value) {
- int rc;
- struct config_network_entry *ent;
-
- if (currentcontext != &(config->default_server)) {
- ERR("Local networks cannot be specified in path "
- "block at line %d in configuration file. "
- "(Path block started at line %d)\n",
- lineno, currentcontext->lineno);
- return(0);
- }
-
- rc = make_config_network_entry((char *)value, &ent);
- switch(rc) {
- case 1:
- ERR("Local network specification (%s) is not validly "
- "constructed on line %d in configuration "
- "file\n", value, lineno);
- return(0);
- break;
- case 2:
- ERR("IP for local "
- "network specification (%s) is not valid on line "
- "%d in configuration file\n", value, lineno);
- return(0);
- break;
- case 3:
- ERR("SUBNET for "
- "local network specification (%s) is not valid on "
- "line %d in configuration file\n", value,
- lineno);
- return(0);
- break;
- case 4:
- ERR("IP (%s) & ", inet_ntoa(ent->local_ip));
- ERR("SUBNET (%s) != IP on line %d in "
- "configuration file, ignored\n",
- inet_ntoa(ent->local_net), lineno);
- return(0);
- case 5:
- case 6:
- case 7:
- ERR("Port specification is invalid and "
- "not allowed in local network specification "
- "(%s) on line %d in configuration file\n",
- value, lineno);
- return(0);
- break;
- }
-
- if (ent->start_port || ent->end_port) {
- ERR("Port specification is "
- "not allowed in local network specification "
- "(%s) on line %d in configuration file\n",
- value, lineno);
- return(0);
- }
-
- /* The entry is valid so add it to linked list */
- ent -> next = config->local_nets;
- (config->local_nets) = ent;
-
- return(0);
+end:
+error:
+ return ret;
}
-static int handle_tordns_deadpool_range(struct config_parsed *config, int lineno,
- const char *value)
+/*
+ * Parse the configuration file with the given file pointer into the
+ * configuration object.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int parse_config_file(FILE *fp, struct configuration *config)
{
- int rc;
- struct config_network_entry *ent;
-
- if (config->tordns_deadpool_range != NULL) {
- ERR("Only one 'deadpool' entry permitted, found a "
- "second at line %d in configuration file.\n");
- return(0);
- }
-
- if (currentcontext != &(config->default_server)) {
- ERR("Deadpool cannot be specified in path "
- "block at line %d in configuration file. "
- "(Path block started at line %d)\n",
- lineno, currentcontext->lineno);
- return(0);
- }
+ int ret = -1;
+ /* Usually, this value is 8192 on most Unix systems. */
+ char line[BUFSIZ];
+
+ assert(fp);
+ assert(config);
+
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ /*
+ * Remove the \n at the end of the buffer and replace it by a NULL
+ * bytes so we handle the line without this useless char.
+ */
+ if (strlen(line) > 0) {
+ line[strlen(line) - 1] = '\0';
+ }
- rc = make_config_network_entry((char *)value, &ent);
- /* This is copied from handle_local and should probably be folded into
- a generic whinge() function or something */
- switch(rc) {
- case 1:
- ERR("The deadpool specification (%s) is not validly "
- "constructed on line %d in configuration "
- "file\n", value, lineno);
- return(0);
- break;
- case 2:
- ERR("IP for deadpool "
- "network specification (%s) is not valid on line "
- "%d in configuration file\n", value, lineno);
- return(0);
- break;
- case 3:
- ERR("SUBNET for "
- "deadpool network specification (%s) is not valid on "
- "line %d in configuration file\n", value,
- lineno);
- return(0);
- break;
- case 4:
- ERR("IP (%s) & ", inet_ntoa(ent->local_ip));
- ERR("SUBNET (%s) != IP on line %d in "
- "configuration file, ignored\n",
- inet_ntoa(ent->local_net), lineno);
- return(0);
- case 5:
- case 6:
- case 7:
- ERR("Port specification is invalid and "
- "not allowed in deadpool specification "
- "(%s) on line %d in configuration file\n",
- value, lineno);
- return(0);
- break;
- }
- if (ent->start_port || ent->end_port) {
- ERR("Port specification is "
- "not allowed in deadpool specification "
- "(%s) on line %d in configuration file\n",
- value, lineno);
- return(0);
+ ret = parse_config_line(line, config);
+ if (ret < 0) {
+ goto error;
+ }
}
- config->tordns_deadpool_range = ent;
- return 0;
-}
-
-static int handle_line(struct config_parsed *config, char *line, int lineno)
-{
- char *words[10];
- static char savedline[CONFIG_MAXLINE];
- int nowords = 0, i;
-
- /* Save the input string */
- strncpy(savedline, line, CONFIG_MAXLINE - 1);
- savedline[CONFIG_MAXLINE - 1] = (char) 0;
- /* Tokenize the input string */
- nowords = utils_tokenize_ignore_comments(line, 10, words);
-
- /* Set the spare slots to an empty string to simplify */
- /* processing */
- for (i = nowords; i < 10; i++)
- words[i] = NULL;
-
- if (nowords > 0) {
- /* Now this can either be a "path" block starter or */
- /* ender, otherwise it has to be a pair (<name> = */
- /* <value>) */
- if (!strcmp(words[0], "path")) {
- handle_path(config, lineno, nowords, words);
- } else if (!strcmp(words[0], "}")) {
- handle_endpath(config, lineno, nowords);
- } else {
- /* Has to be a pair */
- if ((nowords != 3) || (strcmp(words[1], "="))) {
- ERR("Malformed configuration pair "
- "on line %d in configuration "
- "file, \"%s\"\n", lineno, savedline);
- } else if (!strcmp(words[0], "reaches")) {
- handle_reaches(lineno, words[2]);
- } else if (!strcmp(words[0], "server")) {
- handle_server(config, lineno, words[2]);
- } else if (!strcmp(words[0], "server_port")) {
- handle_port(config, lineno, words[2]);
- } else if (!strcmp(words[0], "server_type")) {
- handle_type(config, lineno, words[2]);
- } else if (!strcmp(words[0], "default_user")) {
- handle_username(config, lineno, words[2]);
- } else if (!strcmp(words[0], "default_pass")) {
- handle_password(config, lineno, words[2]);
- } else if (!strcmp(words[0], "local")) {
- handle_local(config, lineno, words[2]);
- } else if (!strcmp(words[0], "tordns_enable")) {
- handle_tordns_enabled(config, lineno, words[2]);
- } else if (!strcmp(words[0], "tordns_deadpool_range")) {
- handle_tordns_deadpool_range(config, lineno, words[2]);
- } else if (!strcmp(words[0], "tordns_cache_size")) {
- handle_tordns_cache_size(config, words[2]);
- } else {
- ERR("Invalid pair type (%s) specified "
- "on line %d in configuration file, "
- "\"%s\"\n", words[0], lineno,
- savedline);
- }
- }
- }
-
- return(0);
-}
-
-int is_local(struct config_parsed *config, struct in_addr *testip) {
- struct config_network_entry *ent;
- char buf[16];
- inet_ntop(AF_INET, testip, buf, sizeof(buf));
- DBG("checking if address: %s is local"
- "\n",
- buf);
-
- for (ent = (config->local_nets); ent != NULL; ent = ent -> next) {
- inet_ntop(AF_INET, &ent->local_net, buf, sizeof(buf));
- DBG("local_net addr: %s"
- "\n",
- buf);
- inet_ntop(AF_INET, &ent->local_ip, buf, sizeof(buf));
- DBG("local_ip addr: %s"
- "\n",
- buf);
- DBG("result testip->s_addr & ent->local_net.s_addr : %i"
- "\n",
- testip->s_addr & ent->local_net.s_addr);
- DBG("result ent->local_ip.s_addr & ent->local_net.s_addr : %i"
- "\n",
- ent->local_ip.s_addr & ent->local_net.s_addr);
- DBG("result ent->local_ip.s_addr : %i"
- "\n",
- ent->local_ip.s_addr);
- if ((testip->s_addr & ent->local_net.s_addr) ==
- (ent->local_ip.s_addr & ent->local_net.s_addr)) {
- DBG("address: %s is local"
- "\n",
- buf);
- return(0);
- }
- }
-
- inet_ntop(AF_INET, testip, buf, sizeof(buf));
- DBG("address: %s is not local"
- "\n",
- buf);
- return(1);
-}
-
-/* Find the appropriate server to reach an ip */
-int pick_server(struct config_parsed *config, struct config_server_entry **ent,
- struct in_addr *ip, unsigned int port) {
- struct config_network_entry *net;
- char ipbuf[64];
-
- DBG("Picking appropriate server for %s\n", inet_ntoa(*ip));
- *ent = (config->paths);
- while (*ent != NULL) {
- /* Go through all the servers looking for one */
- /* with a path to this network */
- DBG("Checking SOCKS server %s\n",
- ((*ent)->address ? (*ent)->address : "(No Address)"));
- net = (*ent)->reachnets;
- while (net != NULL) {
- strcpy(ipbuf, inet_ntoa(net->local_ip));
- DBG("Server can reach %s/%s\n",
- ipbuf, inet_ntoa(net->local_net));
- if (((ip->s_addr & net->local_net.s_addr) ==
- (net->local_ip.s_addr & net->local_net.s_addr)) &&
- (!net->start_port ||
- ((net->start_port <= port) && (net->end_port >= port))))
- {
- DBG("This server can reach target\n");
- /* Found the net, return */
- return(0);
- }
- net = net->next;
- }
- (*ent) = (*ent)->next;
- }
-
- *ent = &(config->default_server);
-
- return(0);
+error:
+ return ret;
}
/*
@@ -708,83 +181,58 @@ int pick_server(struct config_parsed *config, struct config_server_entry **ent,
*
* Return 0 on success or else a negative value.
*/
-int config_file_read(const char *filename, struct config_parsed *config)
+int config_file_read(const char *filename, struct configuration *config)
{
- FILE *conf;
- char line[CONFIG_MAXLINE];
- int rc = 0;
- int lineno = 1;
- struct config_server_entry *server;
+ int ret;
+ FILE *fp;
+
+ assert(config);
/* Clear out the structure */
memset(config, 0x0, sizeof(*config));
- /* Initialization */
- currentcontext = &(config->default_server);
-
- /* Tordns defaults */
- config->tordns_cache_size = 256;
- config->tordns_enabled = 1;
-
+ /* If a filename wasn't provided, use the default. */
+ if (!filename) {
+ filename = DEFAULT_CONF_FILE;
+ DBG("Config file not provided by TORSOCKS_CONF_FILE. Using default %s",
+ filename);
+ }
- /* If a filename wasn't provided, use the default */
- if (filename == NULL) {
- strncpy(line, CONF_FILE, sizeof(line) - 1);
- /* Insure null termination */
- line[sizeof(line) - 1] = (char) 0;
- filename = line;
- DBG("Configuration file not provided by TORSOCKS_CONF_FILE "
- "environment variable, attempting to use defaults in %s.\n", filename);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ WARN("Config file not found: %s. Using default for Tor", filename);
+ (void) set_tor_address(DEFAULT_TOR_ADDRESS, config);
+ /*
+ * We stringify the default value here so we can print the debug
+ * statement in the function call to set port.
+ */
+ (void) set_tor_port(XSTR(DEFAULT_TOR_PORT), config);
+ ret = 0;
+ goto end;
}
- /* If there is no configuration file use reasonable defaults for Tor */
- if ((conf = fopen(filename, "r")) == NULL) {
- ERR("Could not open socks configuration file "
- "(%s) errno (%d), assuming sensible defaults for Tor.\n", filename, errno);
- memset(&(config->default_server), 0x0, sizeof(config->default_server));
- check_server(&(config->default_server));
- handle_local(config, 0, "127.0.0.0/255.0.0.0");
- handle_local(config, 0, "10.0.0.0/255.0.0.0");
- handle_local(config, 0, "192.168.0.0/255.255.0.0");
- handle_local(config, 0, "172.16.0.0/255.240.0.0");
- handle_local(config, 0, "169.254.0.0/255.255.0.0");
- rc = 1; /* Severe errors reading configuration */
- } else {
- memset(&(config->default_server), 0x0, sizeof(config->default_server));
-
- while (NULL != fgets(line, CONFIG_MAXLINE, conf)) {
- /* This line _SHOULD_ end in \n so we */
- /* just chop off the \n and hand it on */
- if (strlen(line) > 0)
- line[strlen(line) - 1] = '\0';
- handle_line(config, line, lineno);
- lineno++;
- }
- fclose(conf);
-
- /* Always add the 127.0.0.1/255.0.0.0 subnet to local */
- handle_local(config, 0, "127.0.0.0/255.0.0.0");
- /* We always consider this local, because many users' dsl
- routers act as their DNS. */
- handle_local(config, 0, "10.0.0.0/255.0.0.0");
- handle_local(config, 0, "192.168.0.0/255.255.0.0");
- handle_local(config, 0, "172.16.0.0/255.240.0.0");
- handle_local(config, 0, "169.254.0.0/255.255.0.0");
- handle_local(config, 0, "192.168.0.0/255.255.0.0");
-
- /* Check default server */
- check_server(&(config->default_server));
- server = (config->paths);
- while (server != NULL) {
- check_server(server);
- server = server->next;
- }
+ ret = parse_config_file(fp, config);
+ if (ret < 0) {
+ goto error;
}
- /* Initialize tordns deadpool_range if not supplied */
- if(config->tordns_deadpool_range == NULL) {
- handle_tordns_deadpool_range(config, 0, "127.0.69.0/255.255.255.0");
+ DBG("Config file %s opened and parsed.", filename);
+
+end:
+error:
+ if (fp) {
+ fclose(fp);
}
+ return ret;
+}
+
+/*
+ * Free everything inside a configuration file object. It is the caller
+ * responsability to free the object if needed.
+ */
+void config_file_destroy(struct config_file *conf)
+{
+ assert(conf);
- return(rc);
+ free(conf->tor_address);
}
diff --git a/src/common/config-file.h b/src/common/config-file.h
index dcd3a58..b0c2e84 100644
--- a/src/common/config-file.h
+++ b/src/common/config-file.h
@@ -22,64 +22,37 @@
#include <netinet/in.h>
-/* Max length of a configuration line. */
-#define CONFIG_MAXLINE BUFSIZ
+#include "connection.h"
/*
- * Structure representing one server entry specified in the config.
+ * Represent the values in a configuration file (torsocks.conf). Basically,
+ * this is the data structure of a parsed config file.
*/
-struct config_server_entry {
- /* Line number in conf file this path started on. */
- int lineno;
- /* Address/hostname of server. */
- const char *address;
- /* Port number of server. */
- in_port_t port;
- /* Type of server (4/5). */
- int type;
- /* Username for this socks server. */
- const char *username;
- /* Password for this socks server. */
- const char *password;
- /* Linked list of nets from this serveri. */
- struct config_network_entry *reachnets;
- /* Pointer to next server entry. */
- struct config_server_entry *next;
-};
-
-/*
- * Structure representing a network.
- */
-struct config_network_entry {
- /* Base IP of the network */
- struct in_addr local_ip;
- /* Mask for the network */
- struct in_addr local_net;
- /* Range of ports for the network */
- in_port_t start_port;
- in_port_t end_port;
- /* Pointer to next network entry */
- struct config_network_entry *next;
+struct config_file {
+ /* The tor address is inet or inet 6. */
+ enum connection_domain tor_domain;
+ /* The IP of the Tor SOCKS. */
+ char *tor_address;
+ /* The port of the Tor SOCKS. */
+ in_port_t tor_port;
};
/*
* Structure representing a complete parsed file.
*/
-struct config_parsed {
- struct config_network_entry *local_nets;
- struct config_server_entry default_server;
- struct config_server_entry *paths;
- int tordns_enabled;
- int tordns_failopen;
- unsigned int tordns_cache_size;
- struct config_network_entry *tordns_deadpool_range;
+struct configuration {
+ /*
+ * Parsed config file (torsocks.conf).
+ */
+ struct config_file conf_file;
+
+ /*
+ * Socks5 address so basically where to connect to Tor.
+ */
+ struct connection_addr socks5_addr;
};
-/* Functions provided by parser module */
-int config_file_read(const char *filename, struct config_parsed *config);
-
-int is_local(struct config_parsed *, struct in_addr *);
-int pick_server(struct config_parsed *, struct config_server_entry **, struct in_addr *, unsigned int port);
-char *strsplit(char *separator, char **text, const char *search);
+int config_file_read(const char *filename, struct configuration *config);
+void config_file_destroy(struct config_file *conf);
#endif /* CONFIG_FILE_H */
diff --git a/src/common/connection.c b/src/common/connection.c
index 0831340..f19d289 100644
--- a/src/common/connection.c
+++ b/src/common/connection.c
@@ -15,16 +15,82 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <arpa/inet.h>
+#include <assert.h>
+#include <stdlib.h>
+
#include "connection.h"
+#include "macros.h"
+
+/*
+ * Set an already allocated connection address using the given IPv4/6 address,
+ * domain and port.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int connection_addr_set(enum connection_domain domain, const char *ip,
+ in_port_t port, struct connection_addr *addr)
+{
+ int ret;
+
+ assert(ip);
+ assert(addr);
+
+ if (port == 0 || port >= 65535) {
+ ret = -EINVAL;
+ ERR("Connection addr set port out of range: %d", port);
+ goto error;
+ }
+
+ memset(addr, 0, sizeof(*addr));
+
+ switch (domain) {
+ case CONNECTION_DOMAIN_INET:
+ addr->domain = domain;
+ addr->u.sin.sin_family = AF_INET;
+ addr->u.sin.sin_port = htons(port);
+ ret = inet_pton(addr->u.sin.sin_family, ip,
+ &addr->u.sin.sin_addr);
+ if (ret != 1) {
+ PERROR("Connection addr set inet_pton");
+ ret = -EINVAL;
+ goto error;
+ }
+ break;
+ case CONNECTION_DOMAIN_INET6:
+ addr->domain = domain;
+ addr->u.sin6.sin6_family = AF_INET6;
+ addr->u.sin6.sin6_port = htons(port);
+ ret = inet_pton(addr->u.sin6.sin6_family, ip,
+ &addr->u.sin6.sin6_addr);
+ if (ret != 1) {
+ PERROR("Connection addr6 set inet_pton");
+ ret = -EINVAL;
+ goto error;
+ }
+ break;
+ default:
+ ERR("Connection addr set unknown domain %d", domain);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Everything is set and good. */
+ ret = 0;
+
+error:
+ return ret;
+}
/*
* Create a new connection with the given fd and destination address.
*
* Return a newly allocated connection object or else NULL.
*/
-struct connection *connection_create(int fd, struct sockaddr_in *dest)
+struct connection *connection_create(int fd, enum connection_domain domain,
+ struct sockaddr *dest)
{
- struct connection *conn;
+ struct connection *conn = NULL;
assert(dest);
@@ -34,12 +100,24 @@ struct connection *connection_create(int fd, struct sockaddr_in *dest)
goto error;
}
+ switch (domain) {
+ case CONNECTION_DOMAIN_INET:
+ memcpy(&conn->dest_addr.u.sin, dest, sizeof(conn->dest_addr.u.sin));
+ break;
+ case CONNECTION_DOMAIN_INET6:
+ memcpy(&conn->dest_addr.u.sin6, dest, sizeof(conn->dest_addr.u.sin6));
+ break;
+ default:
+ ERR("Connection domain unknown %d", domain);
+ goto error;
+ }
+
conn->fd = fd;
- memcpy(conn->dest_addr, dest, sizeof(conn->dest_addr));
return conn;
error:
+ free(conn);
return NULL;
}
@@ -53,8 +131,13 @@ void connection_destroy(struct connection *conn)
}
/* Remove from the double linked list. */
- conn->prev->next = conn->next;
- conn->next->prev = conn->prev;
+ if (conn->prev) {
+ conn->prev->next = conn->next;
+ }
+
+ if (conn->next) {
+ conn->next->prev = conn->prev;
+ }
free(conn);
}
diff --git a/src/common/connection.h b/src/common/connection.h
index 75c3945..18b55ce 100644
--- a/src/common/connection.h
+++ b/src/common/connection.h
@@ -29,6 +29,9 @@ enum connection_domain {
CONNECTION_DOMAIN_INET6 = 2,
};
+/*
+ * Connection address which both supports IPv4 and IPv6.
+ */
struct connection_addr {
enum connection_domain domain;
union {
@@ -37,13 +40,14 @@ struct connection_addr {
} u;
};
+/*
+ * Connection object representing a connect we did to the Tor network from a
+ * connect(2) hijacked call.
+ */
struct connection {
/* Socket fd and also unique ID. */
int fd;
- /* Location of the SOCKS5 server. */
- struct connection_addr socks5_addr;
-
/* Remote destination that passes through Tor. */
struct connection_addr dest_addr;
@@ -51,4 +55,11 @@ struct connection {
struct connection *next, *prev;
};
+int connection_addr_set(enum connection_domain domain, const char *ip,
+ in_port_t port, struct connection_addr *addr);
+
+struct connection *connection_create(int fd, enum connection_domain domain,
+ struct sockaddr *dest);
+void connection_destroy(struct connection *conn);
+
#endif /* TORSOCKS_CONNECTION_H */
diff --git a/src/common/defaults.h b/src/common/defaults.h
index 3f4c8d6..c127eec 100644
--- a/src/common/defaults.h
+++ b/src/common/defaults.h
@@ -18,11 +18,12 @@
#ifndef TORSOCKS_DEFAULTS_H
#define TORSOCKS_DEFAULTS_H
+#include "connection.h"
#include "log.h"
#define DEFAULT_TOR_PORT 9050
#define DEFAULT_TOR_ADDRESS "127.0.0.1"
-#define DEFAULT_TOR_SOCKS 5
+#define DEFAULT_TOR_DOMAIN CONNECTION_DOMAIN_INET
/* Logging defaults. */
#define DEFAULT_LOG_LEVEL_ENV "TORSOCKS_LOG_LEVEL"
@@ -37,4 +38,13 @@
*/
#define DEFAULT_DOMAIN_NAME_SIZE 255
+#define DEFAULT_CONF_FILE "/etc/torsocks.conf"
+#define DEFAULT_CONF_FILE_ENV "TORSOCKS_CONF_FILE"
+
+/*
+ * Maximum number of token in a single line of the torsocks configuration file.
+ * For instance, "TorAddress 127.0.0.1" is two tokens.
+ */
+#define DEFAULT_MAX_CONF_TOKEN 5
+
#endif /* TORSOCKS_DEFAULTS_H */
diff --git a/src/common/log.h b/src/common/log.h
index 29460df..0423aee 100644
--- a/src/common/log.h
+++ b/src/common/log.h
@@ -65,7 +65,7 @@ void log_destroy(void);
#define MSG(fmt, args...) __tsocks_print(MSGNOTICE, fmt "\n", ## args)
#define ERR(fmt, args...) _ERRMSG("ERROR", MSGERR, fmt, ## args)
-#define WARN(fmt, args...) _ERRMSG("Warning", MSGWARN, fmt, ## args)
+#define WARN(fmt, args...) _ERRMSG("WARNING", MSGWARN, fmt, ## args)
#define DBG(fmt, args...) _ERRMSG("DEBUG", MSGDEBUG, fmt, ## args)
/*
diff --git a/src/common/socks5.c b/src/common/socks5.c
index 817f120..eb3baef 100644
--- a/src/common/socks5.c
+++ b/src/common/socks5.c
@@ -112,7 +112,7 @@ error:
}
/*
- * Connect to socks5 server address in the connection object.
+ * Connect to socks5 server address from the global configuration.
*
* Return 0 on success or else a negative value.
*/
@@ -124,15 +124,16 @@ int socks5_connect(struct connection *conn)
assert(conn);
assert(conn->fd >= 0);
- switch (conn->socks5_addr.domain) {
+ switch (tsocks_config.socks5_addr.domain) {
case CONNECTION_DOMAIN_INET:
- socks5_addr = (struct sockaddr *) &conn->socks5_addr.u.sin;
+ socks5_addr = (struct sockaddr *) &tsocks_config.socks5_addr.u.sin;
break;
case CONNECTION_DOMAIN_INET6:
- socks5_addr = (struct sockaddr *) &conn->socks5_addr.u.sin6;
+ socks5_addr = (struct sockaddr *) &tsocks_config.socks5_addr.u.sin6;
break;
default:
- ERR("Socks5 connect domain unknown %d", conn->socks5_addr.domain);
+ ERR("Socks5 connect domain unknown %d",
+ tsocks_config.socks5_addr.domain);
assert(0);
ret = -EBADF;
goto error;
diff --git a/src/common/utils.c b/src/common/utils.c
index 3c537fc..f041cd1 100644
--- a/src/common/utils.c
+++ b/src/common/utils.c
@@ -17,42 +17,101 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
#include <string.h>
#include "macros.h"
#include "utils.h"
/*
+ * Return 1 if the given IP belongs in the af domain else return a negative
+ * value.
+ */
+static int check_addr(const char *ip, int af)
+{
+ int ret = 0;
+ char buf[128];
+
+ assert(ip);
+
+ ret = inet_pton(af, ip, buf);
+ if (ret != 1) {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/*
+ * Return 1 if the given IP is an IPv4.
+ */
+ATTR_HIDDEN
+int utils_is_address_ipv4(const char *ip)
+{
+ return check_addr(ip, AF_INET);
+}
+
+/*
+ * Return 1 if the given IP is an IPv6.
+ */
+ATTR_HIDDEN
+int utils_is_address_ipv6(const char *ip)
+{
+ return check_addr(ip, AF_INET6);
+}
+
+/*
* This routines breaks up input lines into tokens and places these tokens into
* the array specified by tokens
*
* Return the number of token plus one set in the given array.
*/
ATTR_HIDDEN
-int utils_tokenize_ignore_comments(char *line, int arrsize, char **tokens)
+int utils_tokenize_ignore_comments(const char *_line, size_t size, char **tokens)
{
- int tokenno = -1;
- int finished = 0;
-
- /* Whitespace is ignored before and after tokens */
- while ((tokenno < (arrsize - 1)) &&
- (line = line + strspn(line, " \t")) &&
- (*line != (char) 0) &&
- (!finished)) {
- tokenno++;
- tokens[tokenno] = line;
- line = line + strcspn(line, " \t");
- *line = '\0';
- line++;
-
- /* We ignore everything after a # */
- if (*tokens[tokenno] == '#') {
- finished = 1;
- tokenno--;
+ int ret, i = 0, argc = 0;
+ char *c, *line = NULL;
+
+ assert(_line);
+ assert(tokens);
+
+ line = strdup(_line);
+
+ /* Ignore line if it starts with a # meaning a comment. */
+ if (*line == '#') {
+ goto end;
+ }
+
+ /* Count number of token. If larger than size, we return an error. */
+ c = line;
+ while ((c = strchr(c + 1, ' '))) {
+ /* Skip consecutive spaces. */
+ if (*(c + 1) == ' ') {
+ continue;
}
+ argc++;
+ }
+
+ if (argc > size) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ c = strtok(line, " \t");
+ while (c != NULL) {
+ tokens[i] = strdup(c);
+ c = strtok(NULL, " \t");
+ i++;
}
- return tokenno + 1;
+end:
+ ret = i;
+error:
+ free(line);
+ return ret;
}
/*
diff --git a/src/common/utils.h b/src/common/utils.h
index 26a5092..f6a25e9 100644
--- a/src/common/utils.h
+++ b/src/common/utils.h
@@ -23,6 +23,9 @@
#include <netinet/in.h>
char *utils_strsplit(char *separator, char **text, const char *search);
-int utils_tokenize_ignore_comments(char *line, int arrsize, char **tokens);
+int utils_tokenize_ignore_comments(const char *_line, size_t size, char **tokens);
+
+int utils_is_address_ipv4(const char *ip);
+int utils_is_address_ipv6(const char *ip);
#endif /* TORSOCKS_UTILS_H */
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 7a1586d..3c7b3df 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,5 +1,5 @@
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(builddir)
-AM_CFLAGS = -fno-strict-aliasing
+#AM_CFLAGS = -fno-strict-aliasing
libdir = @libdir@/torsocks
diff --git a/src/lib/torsocks.c b/src/lib/torsocks.c
index b927201..9d7408f 100644
--- a/src/lib/torsocks.c
+++ b/src/lib/torsocks.c
@@ -21,12 +21,23 @@
#include <dlfcn.h>
#include <stdlib.h>
+#include <common/config-file.h>
+#include <common/connection.h>
#include <common/defaults.h>
#include <common/log.h>
+#include <common/socks5.h>
#include "torsocks.h"
/*
+ * Global configuration of torsocks taken from the configuration file or set
+ * with the defaults. This is initialized in the constructor once and only
+ * once. Once done, this object is *immutable* thus no locking is needed to
+ * read this object as long as there is not write action.
+ */
+struct configuration tsocks_config;
+
+/*
* 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.
*/
@@ -66,6 +77,63 @@ static void *find_libc_symbol(const char *symbol,
}
/*
+ * Initialize torsocks configuration from a given conf file or the default one.
+ */
+static void init_config(void)
+{
+ int ret;
+ const char *filename = NULL;
+
+ if (!is_suid) {
+ filename = getenv("TORSOCKS_CONF_FILE");
+ }
+
+ ret = config_file_read(filename, &tsocks_config);
+ if (ret < 0) {
+ /*
+ * Failing to get the configuration means torsocks can not function
+ * properly so stops everything.
+ */
+ clean_exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Setup configuration from config file. Use defaults if some attributes
+ * are missing.
+ */
+ if (!tsocks_config.conf_file.tor_address) {
+ tsocks_config.conf_file.tor_address = strdup(DEFAULT_TOR_ADDRESS);
+ }
+ if (tsocks_config.conf_file.tor_port == 0) {
+ tsocks_config.conf_file.tor_port = DEFAULT_TOR_PORT;
+ }
+ if (tsocks_config.conf_file.tor_domain == 0) {
+ tsocks_config.conf_file.tor_domain = DEFAULT_TOR_DOMAIN;
+ }
+
+ /* Create the Tor SOCKS5 connection address. */
+ ret = connection_addr_set(tsocks_config.conf_file.tor_domain,
+ tsocks_config.conf_file.tor_address,
+ tsocks_config.conf_file.tor_port, &tsocks_config.socks5_addr);
+ if (ret < 0) {
+ /*
+ * Without a valid connection address object to Tor well torsocks can't
+ * work properly at all so abort everything.
+ */
+ clean_exit(EXIT_FAILURE);
+ }
+}
+
+/*
+ * Save all the original libc function calls that torsocks needs.
+ */
+static void init_libc_symbols(void)
+{
+ tsocks_libc_connect = find_libc_symbol(LIBC_CONNECT_NAME_STR,
+ TSOCKS_SYM_EXIT_NOT_FOUND);
+}
+
+/*
* Initialize logging subsytem using either the default values or the one given
* by the environment variables.
*/
@@ -119,6 +187,17 @@ static void __attribute__((constructor)) tsocks_init(void)
is_suid = (getuid() != geteuid());
init_logging();
+
+ /*
+ * We need to save libc symbols *before* we override them so torsocks can
+ * use the original libc calls.
+ */
+ init_libc_symbols();
+
+ /*
+ * Read configuration file and set the global config.
+ */
+ init_config();
}
/*
@@ -126,10 +205,14 @@ static void __attribute__((constructor)) tsocks_init(void)
*/
static void __attribute__((destructor)) tsocks_exit(void)
{
+ /* Cleanup allocated memory in the config file. */
+ config_file_destroy(&tsocks_config.conf_file);
/* Clean up logging. */
log_destroy();
}
+#include <arpa/inet.h>
+
/*
* Torsocks call for connect(2).
*/
diff --git a/src/lib/torsocks.h b/src/lib/torsocks.h
index 3d0b1b6..14d0694 100644
--- a/src/lib/torsocks.h
+++ b/src/lib/torsocks.h
@@ -21,6 +21,7 @@
#define TORSOCKS_H
#include <common/compat.h>
+#include <common/config-file.h>
#define TSOCKS_LIBC_DECL(name, type, sig) \
type (*tsocks_libc_##name)(sig);
@@ -56,4 +57,7 @@ enum tsocks_sym_action {
TSOCKS_SYM_EXIT_NOT_FOUND = 1,
};
+/* Global configuration of torsocks. */
+extern struct configuration tsocks_config;
+
#endif /* TORSOCKS_H */
1
0

[torsocks/master] Add hashtable from Tor code and add support for in with connection
by dgoulet@torproject.org 04 Apr '14
by dgoulet@torproject.org 04 Apr '14
04 Apr '14
commit 278960b19133e912f30cd341793096af4bf7e62b
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Tue Jun 18 21:01:00 2013 -0400
Add hashtable from Tor code and add support for in with connection
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/common/Makefile.am | 2 +-
src/common/connection.c | 98 +++++++++-
src/common/connection.h | 10 +-
src/common/ht.h | 490 +++++++++++++++++++++++++++++++++++++++++++++++
src/lib/torsocks.c | 3 +
5 files changed, 591 insertions(+), 12 deletions(-)
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 13d4d41..6714617 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
+ connection.c connection.h ht.h
diff --git a/src/common/connection.c b/src/common/connection.c
index f19d289..8ee60b9 100644
--- a/src/common/connection.c
+++ b/src/common/connection.c
@@ -23,6 +23,59 @@
#include "macros.h"
/*
+ * Return 0 if the two connections are equal else 1.
+ */
+static inline int conn_equal_fct(struct connection *c1,
+ struct connection *c2)
+{
+ return (c1->fd == c2->fd);
+}
+
+/*
+ * Return a hash value based on the unique fd of the given connection.
+ */
+static inline unsigned int conn_hash_fct(struct connection *c)
+{
+ unsigned int mask;
+
+ assert(c);
+
+ switch (sizeof(mask)) {
+ case 1:
+ mask = 0xff;
+ break;
+ case 2:
+ mask = 0xffff;
+ break;
+ case 4:
+ default:
+ mask = 0xffffffff;
+ break;
+ }
+
+ return (((unsigned int)(c->fd) << 8) ^
+ ((unsigned int)((c->fd >> sizeof(mask)) & mask)) ^
+ ((unsigned int)(c->fd & mask)));
+}
+
+/*
+ * Declare the connection registry.
+ */
+static HT_HEAD(connection_registry, connection) connection_registry_root;
+HT_PROTOTYPE(connection_registry, connection, node, conn_hash_fct,
+ conn_equal_fct);
+HT_GENERATE(connection_registry, connection, node, conn_hash_fct,
+ conn_equal_fct, 0.5, malloc, realloc, free);
+
+/*
+ * Initialize connection registry.
+ */
+void connection_registry_init(void)
+{
+ HT_INIT(connection_registry, &connection_registry_root);
+}
+
+/*
* Set an already allocated connection address using the given IPv4/6 address,
* domain and port.
*
@@ -122,6 +175,42 @@ error:
}
/*
+ * Return the matching element with the given key or NULL if not found.
+ */
+struct connection *connection_find(int key)
+{
+ struct connection c_tmp;
+
+ c_tmp.fd = key;
+ return HT_FIND(connection_registry, &connection_registry_root, &c_tmp);
+}
+
+/*
+ * Insert a connection object into the hash table.
+ */
+void connection_insert(struct connection *conn)
+{
+ struct connection *c_tmp;
+
+ assert(conn);
+
+ /* An existing element is a code flow error. */
+ c_tmp = connection_find(conn->fd);
+ assert(!c_tmp);
+
+ HT_INSERT(connection_registry, &connection_registry_root, conn);
+}
+
+/*
+ * Remove a given connection object from the registry.
+ */
+void connection_remove(struct connection *conn)
+{
+ assert(conn);
+ HT_REMOVE(connection_registry, &connection_registry_root, conn);
+}
+
+/*
* Destroy a connection by freeing its memory.
*/
void connection_destroy(struct connection *conn)
@@ -130,14 +219,5 @@ void connection_destroy(struct connection *conn)
return;
}
- /* Remove from the double linked list. */
- if (conn->prev) {
- conn->prev->next = conn->next;
- }
-
- if (conn->next) {
- conn->next->prev = conn->prev;
- }
-
free(conn);
}
diff --git a/src/common/connection.h b/src/common/connection.h
index 18b55ce..058a1f6 100644
--- a/src/common/connection.h
+++ b/src/common/connection.h
@@ -23,6 +23,7 @@
#include <sys/socket.h>
#include "defaults.h"
+#include "ht.h"
enum connection_domain {
CONNECTION_DOMAIN_INET = 1,
@@ -51,8 +52,8 @@ struct connection {
/* Remote destination that passes through Tor. */
struct connection_addr dest_addr;
- /* Next connection of the linked list. */
- struct connection *next, *prev;
+ /* Hash table node. */
+ HT_ENTRY(connection) node;
};
int connection_addr_set(enum connection_domain domain, const char *ip,
@@ -60,6 +61,11 @@ int connection_addr_set(enum connection_domain domain, const char *ip,
struct connection *connection_create(int fd, enum connection_domain domain,
struct sockaddr *dest);
+struct connection *connection_find(int key);
void connection_destroy(struct connection *conn);
+void connection_remove(struct connection *conn);
+void connection_insert(struct connection *conn);
+
+void connection_registry_init(void);
#endif /* TORSOCKS_CONNECTION_H */
diff --git a/src/common/ht.h b/src/common/ht.h
new file mode 100644
index 0000000..9883ea5
--- /dev/null
+++ b/src/common/ht.h
@@ -0,0 +1,490 @@
+/* Copyright (c) 2002, Christopher Clark.
+ * Copyright (c) 2005-2006, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See license at end. */
+
+/* Based on ideas by Christopher Clark and interfaces from Niels Provos. */
+
+#ifndef HT_H_INCLUDED_
+#define HT_H_INCLUDED_
+
+#define HT_HEAD(name, type) \
+ struct name { \
+ /* The hash table itself. */ \
+ struct type **hth_table; \
+ /* How long is the hash table? */ \
+ unsigned hth_table_length; \
+ /* How many elements does the table contain? */ \
+ unsigned hth_n_entries; \
+ /* How many elements will we allow in the table before resizing it? */ \
+ unsigned hth_load_limit; \
+ /* Position of hth_table_length in the primes table. */ \
+ int hth_prime_idx; \
+ }
+
+#define HT_INITIALIZER() \
+ { NULL, 0, 0, 0, -1 }
+
+#ifdef HT_NO_CACHE_HASH_VALUES
+#define HT_ENTRY(type) \
+ struct { \
+ struct type *hte_next; \
+ }
+#else
+#define HT_ENTRY(type) \
+ struct { \
+ struct type *hte_next; \
+ unsigned hte_hash; \
+ }
+#endif
+
+#define HT_EMPTY(head) \
+ ((head)->hth_n_entries == 0)
+
+/* How many elements in 'head'? */
+#define HT_SIZE(head) \
+ ((head)->hth_n_entries)
+
+/* Return memory usage for a hashtable (not counting the entries themselves) */
+#define HT_MEM_USAGE(head) \
+ (sizeof(*head) + (head)->hth_table_length * sizeof(void*))
+
+#define HT_FIND(name, head, elm) name##_HT_FIND((head), (elm))
+#define HT_INSERT(name, head, elm) name##_HT_INSERT((head), (elm))
+#define HT_REPLACE(name, head, elm) name##_HT_REPLACE((head), (elm))
+#define HT_REMOVE(name, head, elm) name##_HT_REMOVE((head), (elm))
+#define HT_START(name, head) name##_HT_START(head)
+#define HT_NEXT(name, head, elm) name##_HT_NEXT((head), (elm))
+#define HT_NEXT_RMV(name, head, elm) name##_HT_NEXT_RMV((head), (elm))
+#define HT_CLEAR(name, head) name##_HT_CLEAR(head)
+#define HT_INIT(name, head) name##_HT_INIT(head)
+/* Helper: */
+static inline unsigned
+ht_improve_hash(unsigned h)
+{
+ /* Aim to protect against poor hash functions by adding logic here
+ * - logic taken from java 1.4 hashtable source */
+ h += ~(h << 9);
+ h ^= ((h >> 14) | (h << 18)); /* >>> */
+ h += (h << 4);
+ h ^= ((h >> 10) | (h << 22)); /* >>> */
+ return h;
+}
+
+#if 0
+/** Basic string hash function, from Java standard String.hashCode(). */
+static inline unsigned
+ht_string_hash(const char *s)
+{
+ unsigned h = 0;
+ int m = 1;
+ while (*s) {
+ h += ((signed char)*s++)*m;
+ m = (m<<5)-1; /* m *= 31 */
+ }
+ return h;
+}
+#endif
+
+/** Basic string hash function, from Python's str.__hash__() */
+static inline unsigned
+ht_string_hash(const char *s)
+{
+ unsigned h;
+ const unsigned char *cp = (const unsigned char *)s;
+ h = *cp << 7;
+ while (*cp) {
+ h = (1000003*h) ^ *cp++;
+ }
+ /* This conversion truncates the length of the string, but that's ok. */
+ h ^= (unsigned)(cp-(const unsigned char*)s);
+ return h;
+}
+
+#ifndef HT_NO_CACHE_HASH_VALUES
+#define HT_SET_HASH_(elm, field, hashfn) \
+ do { (elm)->field.hte_hash = hashfn(elm); } while (0)
+#define HT_SET_HASHVAL_(elm, field, val) \
+ do { (elm)->field.hte_hash = (val); } while (0)
+#define HT_ELT_HASH_(elm, field, hashfn) \
+ ((elm)->field.hte_hash)
+#else
+#define HT_SET_HASH_(elm, field, hashfn) \
+ ((void)0)
+#define HT_ELT_HASH_(elm, field, hashfn) \
+ (hashfn(elm))
+#define HT_SET_HASHVAL_(elm, field, val) \
+ ((void)0)
+#endif
+
+/* Helper: alias for the bucket containing 'elm'. */
+#define HT_BUCKET_(head, field, elm, hashfn) \
+ ((head)->hth_table[HT_ELT_HASH_(elm,field,hashfn) \
+ % head->hth_table_length])
+
+#define HT_FOREACH(x, name, head) \
+ for ((x) = HT_START(name, head); \
+ (x) != NULL; \
+ (x) = HT_NEXT(name, head, x))
+
+#define HT_PROTOTYPE(name, type, field, hashfn, eqfn) \
+ int name##_HT_GROW(struct name *ht, unsigned min_capacity); \
+ void name##_HT_CLEAR(struct name *ht); \
+ int name##_HT_REP_IS_BAD_(const struct name *ht); \
+ static inline void \
+ name##_HT_INIT(struct name *head) { \
+ head->hth_table_length = 0; \
+ head->hth_table = NULL; \
+ head->hth_n_entries = 0; \
+ head->hth_load_limit = 0; \
+ head->hth_prime_idx = -1; \
+ } \
+ /* Helper: returns a pointer to the right location in the table \
+ * 'head' to find or insert the element 'elm'. */ \
+ static inline struct type ** \
+ name##_HT_FIND_P_(struct name *head, struct type *elm) \
+ { \
+ struct type **p; \
+ if (!head->hth_table) \
+ return NULL; \
+ p = &HT_BUCKET_(head, field, elm, hashfn); \
+ while (*p) { \
+ if (eqfn(*p, elm)) \
+ return p; \
+ p = &(*p)->field.hte_next; \
+ } \
+ return p; \
+ } \
+ /* Return a pointer to the element in the table 'head' matching 'elm', \
+ * or NULL if no such element exists */ \
+ static inline struct type * \
+ name##_HT_FIND(const struct name *head, struct type *elm) \
+ { \
+ struct type **p; \
+ struct name *h = (struct name *) head; \
+ HT_SET_HASH_(elm, field, hashfn); \
+ p = name##_HT_FIND_P_(h, elm); \
+ return p ? *p : NULL; \
+ } \
+ /* Insert the element 'elm' into the table 'head'. Do not call this \
+ * function if the table might already contain a matching element. */ \
+ static inline void \
+ name##_HT_INSERT(struct name *head, struct type *elm) \
+ { \
+ struct type **p; \
+ if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \
+ name##_HT_GROW(head, head->hth_n_entries+1); \
+ ++head->hth_n_entries; \
+ HT_SET_HASH_(elm, field, hashfn); \
+ p = &HT_BUCKET_(head, field, elm, hashfn); \
+ elm->field.hte_next = *p; \
+ *p = elm; \
+ } \
+ /* Insert the element 'elm' into the table 'head'. If there already \
+ * a matching element in the table, replace that element and return \
+ * it. */ \
+ static inline struct type * \
+ name##_HT_REPLACE(struct name *head, struct type *elm) \
+ { \
+ struct type **p, *r; \
+ if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \
+ name##_HT_GROW(head, head->hth_n_entries+1); \
+ HT_SET_HASH_(elm, field, hashfn); \
+ p = name##_HT_FIND_P_(head, elm); \
+ r = *p; \
+ *p = elm; \
+ if (r && (r!=elm)) { \
+ elm->field.hte_next = r->field.hte_next; \
+ r->field.hte_next = NULL; \
+ return r; \
+ } else { \
+ ++head->hth_n_entries; \
+ return NULL; \
+ } \
+ } \
+ /* Remove any element matching 'elm' from the table 'head'. If such \
+ * an element is found, return it; otherwise return NULL. */ \
+ static inline struct type * \
+ name##_HT_REMOVE(struct name *head, struct type *elm) \
+ { \
+ struct type **p, *r; \
+ HT_SET_HASH_(elm, field, hashfn); \
+ p = name##_HT_FIND_P_(head,elm); \
+ if (!p || !*p) \
+ return NULL; \
+ r = *p; \
+ *p = r->field.hte_next; \
+ r->field.hte_next = NULL; \
+ --head->hth_n_entries; \
+ return r; \
+ } \
+ /* Invoke the function 'fn' on every element of the table 'head', \
+ * using 'data' as its second argument. If the function returns \
+ * nonzero, remove the most recently examined element before invoking \
+ * the function again. */ \
+ static inline void \
+ name##_HT_FOREACH_FN(struct name *head, \
+ int (*fn)(struct type *, void *), \
+ void *data) \
+ { \
+ unsigned idx; \
+ struct type **p, **nextp, *next; \
+ if (!head->hth_table) \
+ return; \
+ for (idx=0; idx < head->hth_table_length; ++idx) { \
+ p = &head->hth_table[idx]; \
+ while (*p) { \
+ nextp = &(*p)->field.hte_next; \
+ next = *nextp; \
+ if (fn(*p, data)) { \
+ --head->hth_n_entries; \
+ *p = next; \
+ } else { \
+ p = nextp; \
+ } \
+ } \
+ } \
+ } \
+ /* Return a pointer to the first element in the table 'head', under \
+ * an arbitrary order. This order is stable under remove operations, \
+ * but not under others. If the table is empty, return NULL. */ \
+ static inline struct type ** \
+ name##_HT_START(struct name *head) \
+ { \
+ unsigned b = 0; \
+ while (b < head->hth_table_length) { \
+ if (head->hth_table[b]) \
+ return &head->hth_table[b]; \
+ ++b; \
+ } \
+ return NULL; \
+ } \
+ /* Return the next element in 'head' after 'elm', under the arbitrary \
+ * order used by HT_START. If there are no more elements, return \
+ * NULL. If 'elm' is to be removed from the table, you must call \
+ * this function for the next value before you remove it. \
+ */ \
+ static inline struct type ** \
+ name##_HT_NEXT(struct name *head, struct type **elm) \
+ { \
+ if ((*elm)->field.hte_next) { \
+ return &(*elm)->field.hte_next; \
+ } else { \
+ unsigned b = (HT_ELT_HASH_(*elm, field, hashfn) \
+ % head->hth_table_length)+1; \
+ while (b < head->hth_table_length) { \
+ if (head->hth_table[b]) \
+ return &head->hth_table[b]; \
+ ++b; \
+ } \
+ return NULL; \
+ } \
+ } \
+ static inline struct type ** \
+ name##_HT_NEXT_RMV(struct name *head, struct type **elm) \
+ { \
+ unsigned h = HT_ELT_HASH_(*elm, field, hashfn); \
+ *elm = (*elm)->field.hte_next; \
+ --head->hth_n_entries; \
+ if (*elm) { \
+ return elm; \
+ } else { \
+ unsigned b = (h % head->hth_table_length)+1; \
+ while (b < head->hth_table_length) { \
+ if (head->hth_table[b]) \
+ return &head->hth_table[b]; \
+ ++b; \
+ } \
+ return NULL; \
+ } \
+ }
+
+#define HT_GENERATE(name, type, field, hashfn, eqfn, load, mallocfn, \
+ reallocfn, freefn) \
+ static unsigned name##_PRIMES[] = { \
+ 53, 97, 193, 389, \
+ 769, 1543, 3079, 6151, \
+ 12289, 24593, 49157, 98317, \
+ 196613, 393241, 786433, 1572869, \
+ 3145739, 6291469, 12582917, 25165843, \
+ 50331653, 100663319, 201326611, 402653189, \
+ 805306457, 1610612741 \
+ }; \
+ static unsigned name##_N_PRIMES = \
+ (unsigned)(sizeof(name##_PRIMES)/sizeof(name##_PRIMES[0])); \
+ /* Expand the internal table of 'head' until it is large enough to \
+ * hold 'size' elements. Return 0 on success, -1 on allocation \
+ * failure. */ \
+ int \
+ name##_HT_GROW(struct name *head, unsigned size) \
+ { \
+ unsigned new_len, new_load_limit; \
+ int prime_idx; \
+ struct type **new_table; \
+ if (head->hth_prime_idx == (int)name##_N_PRIMES - 1) \
+ return 0; \
+ if (head->hth_load_limit > size) \
+ return 0; \
+ prime_idx = head->hth_prime_idx; \
+ do { \
+ new_len = name##_PRIMES[++prime_idx]; \
+ new_load_limit = (unsigned)(load*new_len); \
+ } while (new_load_limit <= size && \
+ prime_idx < (int)name##_N_PRIMES); \
+ if ((new_table = mallocfn(new_len*sizeof(struct type*)))) { \
+ unsigned b; \
+ memset(new_table, 0, new_len*sizeof(struct type*)); \
+ for (b = 0; b < head->hth_table_length; ++b) { \
+ struct type *elm, *next; \
+ unsigned b2; \
+ elm = head->hth_table[b]; \
+ while (elm) { \
+ next = elm->field.hte_next; \
+ b2 = HT_ELT_HASH_(elm, field, hashfn) % new_len; \
+ elm->field.hte_next = new_table[b2]; \
+ new_table[b2] = elm; \
+ elm = next; \
+ } \
+ } \
+ if (head->hth_table) \
+ freefn(head->hth_table); \
+ head->hth_table = new_table; \
+ } else { \
+ unsigned b, b2; \
+ new_table = reallocfn(head->hth_table, new_len*sizeof(struct type*)); \
+ if (!new_table) return -1; \
+ memset(new_table + head->hth_table_length, 0, \
+ (new_len - head->hth_table_length)*sizeof(struct type*)); \
+ for (b=0; b < head->hth_table_length; ++b) { \
+ struct type *e, **pE; \
+ for (pE = &new_table[b], e = *pE; e != NULL; e = *pE) { \
+ b2 = HT_ELT_HASH_(e, field, hashfn) % new_len; \
+ if (b2 == b) { \
+ pE = &e->field.hte_next; \
+ } else { \
+ *pE = e->field.hte_next; \
+ e->field.hte_next = new_table[b2]; \
+ new_table[b2] = e; \
+ } \
+ } \
+ } \
+ head->hth_table = new_table; \
+ } \
+ head->hth_table_length = new_len; \
+ head->hth_prime_idx = prime_idx; \
+ head->hth_load_limit = new_load_limit; \
+ return 0; \
+ } \
+ /* Free all storage held by 'head'. Does not free 'head' itself, or \
+ * individual elements. */ \
+ void \
+ name##_HT_CLEAR(struct name *head) \
+ { \
+ if (head->hth_table) \
+ freefn(head->hth_table); \
+ head->hth_table_length = 0; \
+ name##_HT_INIT(head); \
+ } \
+ /* Debugging helper: return false iff the representation of 'head' is \
+ * internally consistent. */ \
+ int \
+ name##_HT_REP_IS_BAD_(const struct name *head) \
+ { \
+ unsigned n, i; \
+ struct type *elm; \
+ if (!head->hth_table_length) { \
+ if (!head->hth_table && !head->hth_n_entries && \
+ !head->hth_load_limit && head->hth_prime_idx == -1) \
+ return 0; \
+ else \
+ return 1; \
+ } \
+ if (!head->hth_table || head->hth_prime_idx < 0 || \
+ !head->hth_load_limit) \
+ return 2; \
+ if (head->hth_n_entries > head->hth_load_limit) \
+ return 3; \
+ if (head->hth_table_length != name##_PRIMES[head->hth_prime_idx]) \
+ return 4; \
+ if (head->hth_load_limit != (unsigned)(load*head->hth_table_length)) \
+ return 5; \
+ for (n = i = 0; i < head->hth_table_length; ++i) { \
+ for (elm = head->hth_table[i]; elm; elm = elm->field.hte_next) { \
+ if (HT_ELT_HASH_(elm, field, hashfn) != hashfn(elm)) \
+ return 1000 + i; \
+ if ((HT_ELT_HASH_(elm, field, hashfn) % head->hth_table_length) != i) \
+ return 10000 + i; \
+ ++n; \
+ } \
+ } \
+ if (n != head->hth_n_entries) \
+ return 6; \
+ return 0; \
+ }
+
+/** Implements an over-optimized "find and insert if absent" block;
+ * not meant for direct usage by typical code, or usage outside the critical
+ * path.*/
+#define HT_FIND_OR_INSERT_(name, field, hashfn, head, eltype, elm, var, y, n) \
+ { \
+ struct name *var##_head_ = head; \
+ struct eltype **var; \
+ if (!var##_head_->hth_table || \
+ var##_head_->hth_n_entries >= var##_head_->hth_load_limit) \
+ name##_HT_GROW(var##_head_, var##_head_->hth_n_entries+1); \
+ HT_SET_HASH_((elm), field, hashfn); \
+ var = name##_HT_FIND_P_(var##_head_, (elm)); \
+ if (*var) { \
+ y; \
+ } else { \
+ n; \
+ } \
+ }
+#define HT_FOI_INSERT_(field, head, elm, newent, var) \
+ { \
+ HT_SET_HASHVAL_(newent, field, (elm)->field.hte_hash); \
+ newent->field.hte_next = NULL; \
+ *var = newent; \
+ ++((head)->hth_n_entries); \
+ }
+
+/*
+ * Copyright 2005, Nick Mathewson. Implementation logic is adapted from code
+ * by Christopher Clark, retrofit to allow drop-in memory management, and to
+ * use the same interface as Niels Provos's tree.h. This is probably still
+ * a derived work, so the original license below still applies.
+ *
+ * Copyright (c) 2002, Christopher Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#endif
+
diff --git a/src/lib/torsocks.c b/src/lib/torsocks.c
index 9d7408f..94abc97 100644
--- a/src/lib/torsocks.c
+++ b/src/lib/torsocks.c
@@ -198,6 +198,9 @@ static void __attribute__((constructor)) tsocks_init(void)
* Read configuration file and set the global config.
*/
init_config();
+
+ /* Initialize connection reigstry. */
+ connection_registry_init();
}
/*
1
0
commit d1d2a4ab5e14dfd77c5f38b09efc021166df07d7
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Thu Jun 20 20:17:14 2013 -0400
Change connection create function
Don't need anymore the connection domain, take it directly form the
sockaddr passed to the function.
Also, set the refcount to 1 on creation.
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/common/connection.c | 14 ++++++++------
src/common/connection.h | 3 +--
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/src/common/connection.c b/src/common/connection.c
index ba1890a..b926f15 100644
--- a/src/common/connection.c
+++ b/src/common/connection.c
@@ -170,8 +170,7 @@ error:
*
* Return a newly allocated connection object or else NULL.
*/
-struct connection *connection_create(int fd, enum connection_domain domain,
- struct sockaddr *dest)
+struct connection *connection_create(int fd, const struct sockaddr *dest)
{
struct connection *conn = NULL;
@@ -183,19 +182,22 @@ struct connection *connection_create(int fd, enum connection_domain domain,
goto error;
}
- switch (domain) {
- case CONNECTION_DOMAIN_INET:
+ 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 CONNECTION_DOMAIN_INET6:
+ 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", domain);
+ ERR("Connection domain unknown %d", dest->sa_family);
goto error;
}
conn->fd = fd;
+ conn->refcount.count = 1;
return conn;
diff --git a/src/common/connection.h b/src/common/connection.h
index d1163a9..17f940c 100644
--- a/src/common/connection.h
+++ b/src/common/connection.h
@@ -68,8 +68,7 @@ struct connection {
int connection_addr_set(enum connection_domain domain, const char *ip,
in_port_t port, struct connection_addr *addr);
-struct connection *connection_create(int fd, enum connection_domain domain,
- struct sockaddr *dest);
+struct connection *connection_create(int fd, const struct sockaddr *dest);
struct connection *connection_find(int key);
void connection_destroy(struct connection *conn);
void connection_remove(struct connection *conn);
1
0

04 Apr '14
commit f2e2a66268406680e33165d4f13efa244dd29835
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Tue Jun 11 21:47:56 2013 -0400
Fix: add missing includes in log.h
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/common/log.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/common/log.h b/src/common/log.h
index a60618b..29460df 100644
--- a/src/common/log.h
+++ b/src/common/log.h
@@ -20,6 +20,8 @@
#ifndef TORSOCKS_LOG_H
#define TORSOCKS_LOG_H
+#include <errno.h>
+#include <string.h>
#include <sys/types.h>
#include <unistd.h>
1
0

[torsocks/master] Add registry lock API and connection's refcount
by dgoulet@torproject.org 04 Apr '14
by dgoulet@torproject.org 04 Apr '14
04 Apr '14
commit d00d7dab5a31c9967e14971493e9650621e5d656
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Tue Jun 18 22:14:24 2013 -0400
Add registry lock API and connection's refcount
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/common/Makefile.am | 2 +-
src/common/connection.c | 47 ++++++++++++++++++++++++++++++++++++++
src/common/connection.h | 14 ++++++++++++
src/common/macros.h | 18 ++++++++++++++-
src/common/ref.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 137 insertions(+), 2 deletions(-)
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 6714617..8d8e074 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
+ connection.c connection.h ht.h ref.h
diff --git a/src/common/connection.c b/src/common/connection.c
index 8ee60b9..ba1890a 100644
--- a/src/common/connection.c
+++ b/src/common/connection.c
@@ -22,6 +22,20 @@
#include "connection.h"
#include "macros.h"
+/* Connection registry mutex. */
+static TSOCKS_INIT_MUTEX(connection_registry_mutex);
+
+/*
+ * Release connection using the given refcount located inside the connection
+ * object. This is ONLY called from the connection put reference. After this
+ * call, the connection object associated with that refcount object is freed.
+ */
+static void release_conn(struct ref *ref)
+{
+ struct connection *conn = container_of(ref, struct connection, refcount);
+ connection_destroy(conn);
+}
+
/*
* Return 0 if the two connections are equal else 1.
*/
@@ -68,6 +82,22 @@ HT_GENERATE(connection_registry, connection, node, conn_hash_fct,
conn_equal_fct, 0.5, malloc, realloc, free);
/*
+ * Acquire connection registry mutex.
+ */
+void connection_registry_lock(void)
+{
+ tsocks_mutex_lock(&connection_registry_mutex);
+}
+
+/*
+ * Release connection registry mutex.
+ */
+void connection_registry_unlock(void)
+{
+ tsocks_mutex_unlock(&connection_registry_mutex);
+}
+
+/*
* Initialize connection registry.
*/
void connection_registry_init(void)
@@ -221,3 +251,20 @@ void connection_destroy(struct connection *conn)
free(conn);
}
+
+/*
+ * Get a reference of the given connection object.
+ */
+void connection_get_ref(struct connection *c)
+{
+ ref_get(&c->refcount);
+}
+
+/*
+ * Put back a reference of the given connection object. If the refcount drops
+ * to 0, the release connection function is called which frees the object.
+ */
+void connection_put_ref(struct connection *c)
+{
+ ref_put(&c->refcount, release_conn);
+}
diff --git a/src/common/connection.h b/src/common/connection.h
index 058a1f6..d1163a9 100644
--- a/src/common/connection.h
+++ b/src/common/connection.h
@@ -24,6 +24,8 @@
#include "defaults.h"
#include "ht.h"
+#include "macros.h"
+#include "ref.h"
enum connection_domain {
CONNECTION_DOMAIN_INET = 1,
@@ -52,6 +54,13 @@ struct connection {
/* Remote destination that passes through Tor. */
struct connection_addr dest_addr;
+ /*
+ * Object refcount needed to access this object outside the registry lock.
+ * This is always initialized to 1 so only the destroy process can bring
+ * the refcount to 0 so to delete it.
+ */
+ struct ref refcount;
+
/* Hash table node. */
HT_ENTRY(connection) node;
};
@@ -67,5 +76,10 @@ void connection_remove(struct connection *conn);
void connection_insert(struct connection *conn);
void connection_registry_init(void);
+void connection_registry_lock(void);
+void connection_registry_unlock(void);
+
+void connection_get_ref(struct connection *c);
+void connection_put_ref(struct connection *c);
#endif /* TORSOCKS_CONNECTION_H */
diff --git a/src/common/macros.h b/src/common/macros.h
index 5702b2e..1e98fd6 100644
--- a/src/common/macros.h
+++ b/src/common/macros.h
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2013 - David Goulet <dgoulet(a)ev0ke.net>
+ * Copyright (c) 2009 - Mathieu Desnoyers <mathieu.desnoyers(a)efficios.com>
+ * 2013 - David Goulet <dgoulet(a)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
@@ -18,6 +19,21 @@
#ifndef TORSOCKS_MACROS_H
#define TORSOCKS_MACROS_H
+#include <stddef.h> /* for offsetof */
+
+/*
+ * container_of - Get the address of an object containing a field.
+ *
+ * @ptr: pointer to the field.
+ * @type: type of the object.
+ * @member: name of the field within the object.
+ */
+#define container_of(ptr, type, member) \
+ ({ \
+ const __typeof__(((type *) NULL)->member) * __ptr = (ptr); \
+ (type *)((char *)__ptr - offsetof(type, member)); \
+ })
+
/* Memory allocation zeroed. */
#define zmalloc(x) calloc(1, x)
diff --git a/src/common/ref.h b/src/common/ref.h
new file mode 100644
index 0000000..e1ae6d9
--- /dev/null
+++ b/src/common/ref.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet(a)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_REF_H
+#define TORSOCKS_REF_H
+
+struct ref {
+ long count;
+};
+
+#if (defined(__linux__) || defined(__FreeBSD__) || defined(__darwin__))
+
+/*
+ * Get a reference by incrementing the refcount.
+ */
+static inline void ref_get(struct ref *r)
+{
+ (void) __sync_add_and_fetch(&r->count, 1);
+}
+
+/*
+ * Put a reference back by decrementing the refcount.
+ *
+ * The release function MUST use container_of to get back the object pointer in
+ * which the ref structure is located.
+ */
+static inline void ref_put(struct ref *r,
+ void (*release)(struct ref *))
+{
+ long ret;
+
+ assert(release);
+ ret = __sync_sub_and_fetch(&r->count, 1);
+ assert(ret >= 0);
+ if (ret == 0) {
+ release(r);
+ }
+}
+
+#else
+#error "OS not supported"
+#endif /* __linux__, __FreeBSD__, __darwin__ */
+
+#endif /* TORSOCKS_REF_H */
1
0

[torsocks/master] Implement the connect(2) torsocks call. Not final but working
by dgoulet@torproject.org 04 Apr '14
by dgoulet@torproject.org 04 Apr '14
04 Apr '14
commit d3525f2b14c6d2b5c267dd5aaed747d36af95bfe
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Fri Jun 21 14:15:52 2013 -0400
Implement the connect(2) torsocks call. Not final but working
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/lib/torsocks.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 113 insertions(+), 1 deletion(-)
diff --git a/src/lib/torsocks.c b/src/lib/torsocks.c
index 94abc97..0382d38 100644
--- a/src/lib/torsocks.c
+++ b/src/lib/torsocks.c
@@ -214,15 +214,127 @@ static void __attribute__((destructor)) tsocks_exit(void)
log_destroy();
}
-#include <arpa/inet.h>
+/*
+ * 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.
+ *
+ * Return 0 on success or else a negative value being the errno value that
+ * needs to be sent back.
+ */
+static int connect_to_tor_network(struct connection *conn)
+{
+ int ret;
+
+ assert(conn);
+
+ DBG("Connecting to the Tor network on fd %d", conn->fd);
+
+ ret = socks5_connect(conn);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = socks5_send_method(conn);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = socks5_recv_method(conn);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = socks5_send_connect_request(conn);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = socks5_recv_connect_reply(conn);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ return ret;
+}
/*
* Torsocks call for connect(2).
*/
LIBC_CONNECT_RET_TYPE tsocks_connect(LIBC_CONNECT_SIG)
{
+ int ret, sock_type;
+ socklen_t optlen;
+ struct connection *new_conn;
+
DBG("Connect catched on fd %d", __sockfd);
+
+ ret = getsockopt(__sockfd, SOL_SOCKET, SO_TYPE, &sock_type, &optlen);
+ if (ret < 0) {
+ /* Use the getsockopt() errno value. */
+ goto error;
+ }
+
+ /* We can't handle a non inet socket. */
+ if (__addr->sa_family != AF_INET &&
+ __addr->sa_family != AF_INET6) {
+ DBG("[conect] Connection is not IPv4/v6. Ignoring.");
+ goto libc_connect;
+ }
+
+ /*
+ * Refuse non stream socket. There is a chance that this might be a DNS
+ * request that we can't pass through Tor using raw UDP packet.
+ */
+ if (sock_type != SOCK_STREAM) {
+ ERR("[connect] UDP or ICMP stream can't be handled. Rejecting.");
+ errno = EBADF;
+ goto error;
+ }
+
+ /*
+ * 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
+ * connect() on the same file descriptor is an error so the registry is
+ * quickly unlocked and no reference is needed.
+ */
+ connection_registry_lock();
+ new_conn = connection_find(__sockfd);
+ connection_registry_unlock();
+ if (new_conn) {
+ /* Double connect() for the same fd. */
+ errno = EISCONN;
+ goto error;
+ }
+
+ new_conn = connection_create(__sockfd, __addr);
+ if (!new_conn) {
+ errno = ENOMEM;
+ goto error;
+ }
+
+ /* Connect the socket to the Tor network. */
+ ret = connect_to_tor_network(new_conn);
+ if (ret < 0) {
+ errno = -ret;
+ goto error;
+ }
+
+ connection_registry_lock();
+ /* This can't fail since a lookup was done previously. */
+ connection_insert(new_conn);
+ connection_registry_unlock();
+
+ /* Flag errno for success */
+ ret = errno = 0;
+ return ret;
+
+libc_connect:
return tsocks_libc_connect(LIBC_CONNECT_ARGS);
+error:
+ /* At this point, errno MUST be set to a valid connect() error value. */
+ return -1;
}
/*
1
0

[torsocks/master] Support the RESOLVE PTR Tor command in the socks5 API
by dgoulet@torproject.org 04 Apr '14
by dgoulet@torproject.org 04 Apr '14
04 Apr '14
commit c48811e759850815048713f91e30140f6df63aac
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Sun Jun 23 14:32:58 2013 -0400
Support the RESOLVE PTR Tor command in the socks5 API
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/common/socks5.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/common/socks5.h | 10 ++++
2 files changed, 138 insertions(+)
diff --git a/src/common/socks5.c b/src/common/socks5.c
index 8a16b9a..d007599 100644
--- a/src/common/socks5.c
+++ b/src/common/socks5.c
@@ -15,9 +15,11 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
+#include <stdlib.h>
#include <lib/torsocks.h>
@@ -493,3 +495,129 @@ int socks5_recv_resolve_reply(struct connection *conn, uint32_t *ip_addr)
error:
return ret;
}
+
+/*
+ * Send a SOCKS5 Tor resolve ptr request for a given ip address using an
+ * already connected connection.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int socks5_send_resolve_ptr_request(const void *ip, struct connection *conn)
+{
+ int ret, ret_send;
+ char buffer[20]; /* Can't go higher than that (with IPv6). */
+ char ip_str[INET6_ADDRSTRLEN];
+ size_t msg_len, data_len;
+ struct socks5_request msg;
+ struct socks5_request_resolve_ptr req;
+
+ assert(conn);
+ assert(conn->fd >= 0);
+
+ DBG("[socks5] Resolve ptr request for ip %u", ip);
+
+ memset(buffer, 0, sizeof(buffer));
+ msg_len = sizeof(msg);
+
+ msg.ver = SOCKS5_VERSION;
+ msg.cmd = SOCKS5_CMD_RESOLVE_PTR;
+ /* Always zeroed. */
+ msg.rsv = 0;
+
+ if (inet_ntop(AF_INET, ip, ip_str, sizeof(ip_str))) {
+ msg.atyp = SOCKS5_ATYP_IPV4;
+ memcpy(req.addr.ipv4, ip, 4);
+ } else if (inet_ntop(AF_INET6, ip, ip_str, sizeof(ip_str))) {
+ msg.atyp = SOCKS5_ATYP_IPV6;
+ memcpy(req.addr.ipv4, ip, 16);
+ } else {
+ ERR("Unknown address domain of %d", ip);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Copy final buffer. */
+ memcpy(buffer, &msg, msg_len);
+ memcpy(buffer + msg_len, &req, sizeof(req));
+ data_len = msg_len + sizeof(req);
+
+ ret_send = send_data(conn->fd, &buffer, data_len);
+ if (ret_send < 0) {
+ ret = ret_send;
+ goto error;
+ }
+
+ /* Data was sent successfully. */
+ ret = 0;
+ DBG("[socks5] Resolve PTR for %u sent successfully", ip);
+
+error:
+ return ret;
+}
+
+/*
+ * Receive a Tor resolve ptr reply on the given connection. The hostname value
+ * is populated with the returned name from Tor. On error, it's untouched. The
+ * memory is allocated so the caller needs to free the memory on success.
+ *
+ * Return 0 on success else a negative value.
+ */
+int socks5_recv_resolve_ptr_reply(struct connection *conn, char **_hostname)
+{
+ int ret;
+ ssize_t ret_recv;
+ char *hostname = NULL;
+ struct {
+ struct socks5_reply msg;
+ uint8_t len;
+ } buffer;
+
+ assert(conn);
+ assert(conn >= 0);
+ assert(_hostname);
+
+ ret_recv = recv_data(conn->fd, &buffer, sizeof(buffer));
+ if (ret_recv < 0) {
+ ret = ret_recv;
+ goto error;
+ }
+
+ if (buffer.msg.ver != SOCKS5_VERSION) {
+ ERR("Bad SOCKS5 version reply");
+ ret = -ECONNABORTED;
+ goto error;
+ }
+
+ if (buffer.msg.rep != SOCKS5_REPLY_SUCCESS) {
+ ERR("Unable to resolve. Status reply: %d", buffer.msg.rep);
+ ret = -ECONNABORTED;
+ goto error;
+ }
+
+ if (buffer.msg.atyp == SOCKS5_ATYP_DOMAIN) {
+ /* Allocate hostname len plus an extra for the null byte. */
+ hostname = zmalloc(buffer.len + 1);
+ if (!hostname) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ ret_recv = recv_data(conn->fd, hostname, buffer.len);
+ if (ret_recv < 0) {
+ ret = ret_recv;
+ goto error;
+ }
+ hostname[buffer.len] = '\0';
+ } else {
+ ERR("Bad SOCKS5 atyp reply %d", buffer.msg.atyp);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ *_hostname = hostname;
+ DBG("[socks5] Resolve reply received: %s", *_hostname);
+ return 0;
+
+error:
+ free(hostname);
+ return ret;
+}
diff --git a/src/common/socks5.h b/src/common/socks5.h
index c11c097..9b47aa1 100644
--- a/src/common/socks5.h
+++ b/src/common/socks5.h
@@ -102,6 +102,14 @@ struct socks5_request_resolve {
char name[UINT8_MAX];
};
+/* Use for the Tor resolve ptr command. */
+struct socks5_request_resolve_ptr {
+ union {
+ uint8_t ipv4[4];
+ uint8_t ipv6[16];
+ } addr;
+};
+
/* Non variable part of a reply. */
struct socks5_reply {
uint8_t ver;
@@ -123,5 +131,7 @@ int socks5_recv_connect_reply(struct connection *conn);
/* Tor DNS resolve. */
int socks5_send_resolve_request(const char *hostname, struct connection *conn);
int socks5_recv_resolve_reply(struct connection *conn, uint32_t *ip_addr);
+int socks5_recv_resolve_ptr_reply(struct connection *conn, char **_hostname);
+int socks5_send_resolve_ptr_request(const void *ip, struct connection *conn);
#endif /* TORSOCKS_SOCKS_H */
1
0