commit a9ca4591c551bd485c9083bd855f87fd28c05876
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Sun Jun 2 14:10:45 2013 -0400
Add config-file.c/.h containing config file parser
Calls in this file has been copied from the old source. Nothing was
changed except some renaming of data structure to respect name spacing.
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/common/Makefile.am | 2 +-
src/common/config-file.c | 854 ++++++++++++++++++++++++++++++++++++++++++++++
src/common/config-file.h | 85 +++++
3 files changed, 940 insertions(+), 1 deletion(-)
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 4e97ac2..f23e439 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -3,4 +3,4 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
AM_CFLAGS = -fno-strict-aliasing
noinst_LTLIBRARIES = libcommon.la
-libcommon_la_SOURCES = log.c log.h
+libcommon_la_SOURCES = log.c log.h config-file.c config-file.h
diff --git a/src/common/config-file.c b/src/common/config-file.c
new file mode 100644
index 0000000..a085a19
--- /dev/null
+++ b/src/common/config-file.c
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2000-2008 - Shaun Clowes <delius(a)progsoc.org>
+ * 2008-2011 - Robert Hogan <robert(a)roberthogan.net>
+ * 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 <arpa/inet.h>
+#include <errno.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"
+
+/* Global configuration variables. */
+static struct config_server_entry *currentcontext = NULL;
+
+/* This routines breaks up input lines into tokens */
+/* and places these tokens into the array specified */
+/* by tokens */
+static int tokenize(char *line, int arrsize, 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 = (char) 0;
+ line++;
+
+ /* We ignore everything after a # */
+ if (*tokens[tokenno] == '#') {
+ finished = 1;
+ tokenno--;
+ }
+ }
+
+ return(tokenno + 1);
+}
+
+/* Check server entries (and establish defaults) */
+static int check_server(struct config_server_entry *server)
+{
+ /* 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) {
+ show_msg(MSGERR, "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 = strsplit(&separator, &split, "/:");
+ if (separator == ':') {
+ /* We have a start port */
+ start_port = strsplit(&separator, &split, "-/");
+ if (separator == '-')
+ /* We have an end port */
+ end_port = strsplit(&separator, &split, "/");
+ }
+ subnet = 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);
+ }
+
+ show_msg(MSGDEBUG, "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:
+ show_msg(MSGERR, "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:
+ show_msg(MSGERR, "IP in reach statement "
+ "network specification (%s) is not valid on line "
+ "%d in configuration file\n", value, lineno);
+ return(0);
+ break;
+ case 3:
+ show_msg(MSGERR, "SUBNET in reach statement "
+ "network specification (%s) is not valid on "
+ "line %d in configuration file\n", value,
+ lineno);
+ return(0);
+ break;
+ case 4:
+ show_msg(MSGERR, "IP (%s) & ", inet_ntoa(ent->local_ip));
+ show_msg(MSGERR, "SUBNET (%s) != IP on line %d in "
+ "configuration file, ignored\n",
+ inet_ntoa(ent->local_net), lineno);
+ return(0);
+ break;
+ case 5:
+ show_msg(MSGERR, "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:
+ show_msg(MSGERR, "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:
+ show_msg(MSGERR, "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;
+
+ return(0);
+}
+
+static int handle_server(struct config_parsed *config, int lineno, char *value) {
+ char *ip;
+
+ ip = 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))
+ show_msg(MSGERR, "Only one default SOCKS server "
+ "may be specified at line %d in "
+ "configuration file\n", lineno);
+ else
+ show_msg(MSGERR, "Only one SOCKS server may be specified "
+ "per path on line %d in configuration "
+ "file. (Path begins on line %d)\n",
+ lineno, currentcontext->lineno);
+ }
+
+ return(0);
+}
+
+static int handle_port(struct config_parsed *config, int lineno, char *value) {
+
+ if (currentcontext->port != 0) {
+ if (currentcontext == &(config->default_server))
+ show_msg(MSGERR, "Server port may only be specified "
+ "once for default server, at line %d "
+ "in configuration file\n", lineno);
+ else
+ show_msg(MSGERR, "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)) {
+ show_msg(MSGERR, "Invalid server port number "
+ "specified in configuration file "
+ "(%s) on line %d\n", value, lineno);
+ currentcontext->port = 0;
+ }
+ }
+
+ return(0);
+}
+
+static int handle_username(struct config_parsed *config, int lineno, char *value) {
+
+ if (currentcontext->username != NULL) {
+ if (currentcontext == &(config->default_server))
+ show_msg(MSGERR, "Default username may only be specified "
+ "once for default server, at line %d "
+ "in configuration file\n", lineno);
+ else
+ show_msg(MSGERR, "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))
+ show_msg(MSGERR, "Default password may only be specified "
+ "once for default server, at line %d "
+ "in configuration file\n", lineno);
+ else
+ show_msg(MSGERR, "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);
+ }
+
+ return(0);
+}
+
+static int handle_type(struct config_parsed *config, int lineno, char *value) {
+
+ if (currentcontext->type != 0) {
+ if (currentcontext == &(config->default_server))
+ show_msg(MSGERR, "Server type may only be specified "
+ "once for default server, at line %d "
+ "in configuration file\n", lineno);
+ else
+ show_msg(MSGERR, "Server type 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->type = (int) strtol(value, (char **)NULL, 10);
+ if ((errno != 0) || (currentcontext->type == 0) ||
+ ((currentcontext->type != 4) && (currentcontext->type != 5))) {
+ show_msg(MSGERR, "Invalid server type (%s) "
+ "specified in configuration file "
+ "on line %d, only 4 or 5 may be "
+ "specified\n", value, lineno);
+ currentcontext->type = 0;
+ }
+ }
+
+ 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) {
+ show_msg(MSGERR, "Invalid value %s supplied for tordns_enabled at "
+ "line %d in config file, IGNORED\n", value, lineno);
+ } else {
+ config->tordns_enabled = val;
+ }
+ return 0;
+}
+
+static int handle_tordns_cache_size(struct config_parsed *config,
+ char *value)
+{
+ char *endptr;
+ long size = strtol(value, &endptr, 10);
+ if(*endptr != '\0') {
+ show_msg(MSGERR, "Error parsing integer value for "
+ "tordns_cache_size (%s), using default %d\n",
+ value, config->tordns_cache_size);
+ } else if(size < 128) {
+ show_msg(MSGERR, "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) {
+ show_msg(MSGERR, "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;
+}
+
+static int handle_path(struct config_parsed *config, int lineno, int nowords, char *words[])
+{
+ struct config_server_entry *newserver;
+
+ if ((nowords != 2) || (strcmp(words[1], "{"))) {
+ show_msg(MSGERR, "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 */
+ show_msg(MSGERR, "Path statements cannot be nested on line %d "
+ "in configuration file\n", lineno);
+ } 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 */
+ show_msg(MSGDEBUG, "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;
+ }
+
+ return(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)) {
+ show_msg(MSGERR, "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:
+ show_msg(MSGERR, "Local network specification (%s) is not validly "
+ "constructed on line %d in configuration "
+ "file\n", value, lineno);
+ return(0);
+ break;
+ case 2:
+ show_msg(MSGERR, "IP for local "
+ "network specification (%s) is not valid on line "
+ "%d in configuration file\n", value, lineno);
+ return(0);
+ break;
+ case 3:
+ show_msg(MSGERR, "SUBNET for "
+ "local network specification (%s) is not valid on "
+ "line %d in configuration file\n", value,
+ lineno);
+ return(0);
+ break;
+ case 4:
+ show_msg(MSGERR, "IP (%s) & ", inet_ntoa(ent->local_ip));
+ show_msg(MSGERR, "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:
+ show_msg(MSGERR, "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) {
+ show_msg(MSGERR, "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);
+}
+
+static int handle_tordns_deadpool_range(struct config_parsed *config, int lineno,
+ const char *value)
+{
+ int rc;
+ struct config_network_entry *ent;
+
+ if (config->tordns_deadpool_range != NULL) {
+ show_msg(MSGERR, "Only one 'deadpool' entry permitted, found a "
+ "second at line %d in configuration file.\n");
+ return(0);
+ }
+
+ if (currentcontext != &(config->default_server)) {
+ show_msg(MSGERR, "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);
+ }
+
+ 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:
+ show_msg(MSGERR, "The deadpool specification (%s) is not validly "
+ "constructed on line %d in configuration "
+ "file\n", value, lineno);
+ return(0);
+ break;
+ case 2:
+ show_msg(MSGERR, "IP for deadpool "
+ "network specification (%s) is not valid on line "
+ "%d in configuration file\n", value, lineno);
+ return(0);
+ break;
+ case 3:
+ show_msg(MSGERR, "SUBNET for "
+ "deadpool network specification (%s) is not valid on "
+ "line %d in configuration file\n", value,
+ lineno);
+ return(0);
+ break;
+ case 4:
+ show_msg(MSGERR, "IP (%s) & ", inet_ntoa(ent->local_ip));
+ show_msg(MSGERR, "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:
+ show_msg(MSGERR, "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) {
+ show_msg(MSGERR, "Port specification is "
+ "not allowed in deadpool specification "
+ "(%s) on line %d in configuration file\n",
+ value, lineno);
+ return(0);
+ }
+
+ 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 = tokenize(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], "="))) {
+ show_msg(MSGERR, "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 {
+ show_msg(MSGERR, "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));
+ show_msg(MSGDEBUG, "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));
+ show_msg(MSGDEBUG, "local_net addr: %s"
+ "\n",
+ buf);
+ inet_ntop(AF_INET, &ent->local_ip, buf, sizeof(buf));
+ show_msg(MSGDEBUG, "local_ip addr: %s"
+ "\n",
+ buf);
+ show_msg(MSGDEBUG, "result testip->s_addr & ent->local_net.s_addr : %i"
+ "\n",
+ testip->s_addr & ent->local_net.s_addr);
+ show_msg(MSGDEBUG, "result ent->local_ip.s_addr & ent->local_net.s_addr : %i"
+ "\n",
+ ent->local_ip.s_addr & ent->local_net.s_addr);
+ show_msg(MSGDEBUG, "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)) {
+ show_msg(MSGDEBUG, "address: %s is local"
+ "\n",
+ buf);
+ return(0);
+ }
+ }
+
+ inet_ntop(AF_INET, testip, buf, sizeof(buf));
+ show_msg(MSGDEBUG, "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];
+
+ show_msg(MSGDEBUG, "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 */
+ show_msg(MSGDEBUG, "Checking SOCKS server %s\n",
+ ((*ent)->address ? (*ent)->address : "(No Address)"));
+ net = (*ent)->reachnets;
+ while (net != NULL) {
+ strcpy(ipbuf, inet_ntoa(net->local_ip));
+ show_msg(MSGDEBUG, "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))))
+ {
+ show_msg(MSGDEBUG, "This server can reach target\n");
+ /* Found the net, return */
+ return(0);
+ }
+ net = net->next;
+ }
+ (*ent) = (*ent)->next;
+ }
+
+ *ent = &(config->default_server);
+
+ return(0);
+}
+
+/* This function is very much like strsep, it looks in a string for */
+/* a character from a list of characters, when it finds one it */
+/* replaces it with a \0 and returns the start of the string */
+/* (basically spitting out tokens with arbitrary separators). If no */
+/* match is found the remainder of the string is returned and */
+/* the start pointer is set to be NULL. The difference between */
+/* standard strsep and this function is that this one will */
+/* set *separator to the character separator found if it isn't null */
+char *strsplit(char *separator, char **text, const char *search) {
+ unsigned int len;
+ char *ret;
+
+ ret = *text;
+
+ if (*text == NULL) {
+ if (separator)
+ *separator = '\0';
+ return(NULL);
+ } else {
+ len = strcspn(*text, search);
+ if (len == strlen(*text)) {
+ if (separator)
+ *separator = '\0';
+ *text = NULL;
+ } else {
+ *text = *text + len;
+ if (separator)
+ *separator = **text;
+ **text = '\0';
+ *text = *text + 1;
+ }
+ }
+
+ return(ret);
+}
+
+/*
+ * Read and populate the given config parsed data structure.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int config_file_read(const char *filename, struct config_parsed *config)
+{
+ FILE *conf;
+ char line[CONFIG_MAXLINE];
+ int rc = 0;
+ int lineno = 1;
+ struct config_server_entry *server;
+
+ /* 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 == NULL) {
+ strncpy(line, CONF_FILE, sizeof(line) - 1);
+ /* Insure null termination */
+ line[sizeof(line) - 1] = (char) 0;
+ filename = line;
+ show_msg(MSGDEBUG, "Configuration file not provided by TORSOCKS_CONF_FILE "
+ "environment variable, attempting to use defaults in %s.\n", filename);
+ }
+
+ /* If there is no configuration file use reasonable defaults for Tor */
+ if ((conf = fopen(filename, "r")) == NULL) {
+ show_msg(MSGERR, "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;
+ }
+ }
+
+ /* 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");
+ }
+
+ return(rc);
+}
diff --git a/src/common/config-file.h b/src/common/config-file.h
new file mode 100644
index 0000000..dcd3a58
--- /dev/null
+++ b/src/common/config-file.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2000-2008 - Shaun Clowes <delius(a)progsoc.org>
+ * 2008-2011 - Robert Hogan <robert(a)roberthogan.net>
+ * 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 CONFIG_FILE_H
+#define CONFIG_FILE_H
+
+#include <netinet/in.h>
+
+/* Max length of a configuration line. */
+#define CONFIG_MAXLINE BUFSIZ
+
+/*
+ * Structure representing one server entry specified in the config.
+ */
+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;
+};
+
+/*
+ * 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;
+};
+
+/* 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);
+
+#endif /* CONFIG_FILE_H */