aboutsummaryrefslogtreecommitdiff
path: root/src/tools/tor-fw-helper
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/tor-fw-helper')
-rw-r--r--src/tools/tor-fw-helper/Makefile.am38
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper-natpmp.c233
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper-natpmp.h47
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper-upnp.c189
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper-upnp.h43
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper.c396
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper.h57
7 files changed, 1003 insertions, 0 deletions
diff --git a/src/tools/tor-fw-helper/Makefile.am b/src/tools/tor-fw-helper/Makefile.am
new file mode 100644
index 000000000..8f64ad2ba
--- /dev/null
+++ b/src/tools/tor-fw-helper/Makefile.am
@@ -0,0 +1,38 @@
+if USE_FW_HELPER
+bin_PROGRAMS = tor-fw-helper
+else
+bin_PROGRAMS =
+endif
+
+tor_fw_helper_SOURCES = \
+ tor-fw-helper.c \
+ tor-fw-helper-natpmp.c \
+ tor-fw-helper-upnp.c
+noinst_HEADERS = \
+ tor-fw-helper.h \
+ tor-fw-helper-natpmp.h \
+ tor-fw-helper-upnp.h
+
+if NAT_PMP
+nat_pmp_ldflags = @TOR_LDFLAGS_libnatpmp@
+nat_pmp_ldadd = -lnatpmp
+nat_pmp_cppflags = @TOR_CPPFLAGS_libnatpmp@
+else
+nat_pmp_ldflags =
+nat_pmp_ldadd =
+nat_pmp_cppflags =
+endif
+
+if MINIUPNPC
+miniupnpc_ldflags = @TOR_LDFLAGS_libminiupnpc@
+miniupnpc_ldadd = -lminiupnpc -lm @TOR_LIB_IPHLPAPI@
+miniupnpc_cppflags = @TOR_CPPFLAGS_libminiupnpc@
+else
+miniupnpc_ldflags =
+miniupnpc_ldadd =
+miniupnpc_cppflags =
+endif
+
+tor_fw_helper_LDFLAGS = $(nat_pmp_ldflags) $(miniupnpc_ldflags)
+tor_fw_helper_LDADD = ../../common/libor.a $(nat_pmp_ldadd) $(miniupnpc_ldadd) @TOR_LIB_WS32@
+tor_fw_helper_CPPFLAGS = $(nat_pmp_cppflags) $(miniupnpc_cppflags)
diff --git a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c
new file mode 100644
index 000000000..f9d5d0d58
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c
@@ -0,0 +1,233 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper-natpmp.c
+ * \brief The implementation of our NAT-PMP firewall helper.
+ **/
+
+#include "orconfig.h"
+#ifdef NAT_PMP
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+// debugging stuff
+#include <assert.h>
+
+#include "tor-fw-helper.h"
+#include "tor-fw-helper-natpmp.h"
+
+/** This hooks NAT-PMP into our multi-backend API. */
+static tor_fw_backend_t tor_natpmp_backend = {
+ "natpmp",
+ sizeof(struct natpmp_state_t),
+ tor_natpmp_init,
+ tor_natpmp_cleanup,
+ tor_natpmp_fetch_public_ip,
+ tor_natpmp_add_tcp_mapping
+};
+
+/** Return the backend for NAT-PMP. */
+const tor_fw_backend_t *
+tor_fw_get_natpmp_backend(void)
+{
+ return &tor_natpmp_backend;
+}
+
+/** Initialize the NAT-PMP backend and store the results in
+ * <b>backend_state</b>.*/
+int
+tor_natpmp_init(tor_fw_options_t *tor_fw_options, void *backend_state)
+{
+ natpmp_state_t *state = (natpmp_state_t *) backend_state;
+ int r = 0;
+
+ memset(&(state->natpmp), 0, sizeof(natpmp_t));
+ memset(&(state->response), 0, sizeof(natpmpresp_t));
+ state->init = 0;
+ state->protocol = NATPMP_PROTOCOL_TCP;
+ state->lease = NATPMP_DEFAULT_LEASE;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: natpmp init...\n");
+
+ r = initnatpmp(&(state->natpmp), 0, 0);
+ if (r == 0) {
+ state->init = 1;
+ fprintf(stdout, "tor-fw-helper: natpmp initialized...\n");
+ return r;
+ } else {
+ fprintf(stderr, "tor-fw-helper: natpmp failed to initialize...\n");
+ return r;
+ }
+}
+
+/** Tear down the NAT-PMP connection stored in <b>backend_state</b>.*/
+int
+tor_natpmp_cleanup(tor_fw_options_t *tor_fw_options, void *backend_state)
+{
+ natpmp_state_t *state = (natpmp_state_t *) backend_state;
+ int r = 0;
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: natpmp cleanup...\n");
+ r = closenatpmp(&(state->natpmp));
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: closing natpmp socket: %d\n", r);
+ return r;
+}
+
+/** Use select() to wait until we can read on fd. */
+static int
+wait_until_fd_readable(int fd, struct timeval *timeout)
+{
+ int r;
+ fd_set fds;
+ if (fd >= FD_SETSIZE) {
+ fprintf(stderr, "E: NAT-PMP FD_SETSIZE error %d\n", fd);
+ return -1;
+ }
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ r = select(fd+1, &fds, NULL, NULL, timeout);
+ if (r == -1) {
+ fprintf(stdout, "V: select failed in wait_until_fd_readable: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ /* XXXX we should really check to see whether fd was readable, or we timed
+ out. */
+ return 0;
+}
+
+/** Add a TCP port mapping for a single port stored in <b>tor_fw_options</b>
+ * using the <b>natpmp_t</b> stored in <b>backend_state</b>. */
+int
+tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options,
+ void *backend_state)
+{
+ natpmp_state_t *state = (natpmp_state_t *) backend_state;
+ int r = 0;
+ int x = 0;
+ int sav_errno;
+
+ struct timeval timeout;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: sending natpmp portmapping request...\n");
+ r = sendnewportmappingrequest(&(state->natpmp), state->protocol,
+ tor_fw_options->internal_port,
+ tor_fw_options->external_port,
+ state->lease);
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "tor-fw-helper: NAT-PMP sendnewportmappingrequest "
+ "returned %d (%s)\n", r, r==12?"SUCCESS":"FAILED");
+
+ do {
+ getnatpmprequesttimeout(&(state->natpmp), &timeout);
+ x = wait_until_fd_readable(state->natpmp.s, &timeout);
+ if (x == -1)
+ return -1;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: attempting to readnatpmpreponseorretry...\n");
+ r = readnatpmpresponseorretry(&(state->natpmp), &(state->response));
+ sav_errno = errno;
+
+ if (r<0 && r!=NATPMP_TRYAGAIN) {
+ fprintf(stderr, "E: readnatpmpresponseorretry failed %d\n", r);
+ fprintf(stderr, "E: errno=%d '%s'\n", sav_errno,
+ strerror(sav_errno));
+ }
+
+ } while (r == NATPMP_TRYAGAIN);
+
+ if (r != 0) {
+ /* XXX TODO: NATPMP_* should be formatted into useful error strings */
+ fprintf(stderr, "E: NAT-PMP It appears that something went wrong:"
+ " %d\n", r);
+ if (r == -51)
+ fprintf(stderr, "E: NAT-PMP It appears that the request was "
+ "unauthorized\n");
+ return r;
+ }
+
+ if (r == NATPMP_SUCCESS) {
+ fprintf(stdout, "tor-fw-helper: NAT-PMP mapped public port %hu to"
+ " localport %hu liftime %u\n",
+ (state->response).pnu.newportmapping.mappedpublicport,
+ (state->response).pnu.newportmapping.privateport,
+ (state->response).pnu.newportmapping.lifetime);
+ }
+
+ tor_fw_options->nat_pmp_status = 1;
+
+ return r;
+}
+
+/** Fetch our likely public IP from our upstream NAT-PMP enabled NAT device.
+ * Use the connection context stored in <b>backend_state</b>. */
+int
+tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options,
+ void *backend_state)
+{
+ int r = 0;
+ int x = 0;
+ int sav_errno;
+ natpmp_state_t *state = (natpmp_state_t *) backend_state;
+
+ struct timeval timeout;
+
+ r = sendpublicaddressrequest(&(state->natpmp));
+ fprintf(stdout, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned"
+ " %d (%s)\n", r, r==2?"SUCCESS":"FAILED");
+
+ do {
+ getnatpmprequesttimeout(&(state->natpmp), &timeout);
+
+ x = wait_until_fd_readable(state->natpmp.s, &timeout);
+ if (x == -1)
+ return -1;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: NAT-PMP attempting to read reponse...\n");
+ r = readnatpmpresponseorretry(&(state->natpmp), &(state->response));
+ sav_errno = errno;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: NAT-PMP readnatpmpresponseorretry returned"
+ " %d\n", r);
+
+ if ( r < 0 && r != NATPMP_TRYAGAIN) {
+ fprintf(stderr, "E: NAT-PMP readnatpmpresponseorretry failed %d\n",
+ r);
+ fprintf(stderr, "E: NAT-PMP errno=%d '%s'\n", sav_errno,
+ strerror(sav_errno));
+ }
+
+ } while (r == NATPMP_TRYAGAIN );
+
+ if (r != 0) {
+ fprintf(stderr, "E: NAT-PMP It appears that something went wrong:"
+ " %d\n", r);
+ return r;
+ }
+
+ fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n",
+ inet_ntoa((state->response).pnu.publicaddress.addr));
+ tor_fw_options->public_ip_status = 1;
+
+ if (tor_fw_options->verbose) {
+ fprintf(stdout, "V: result = %u\n", r);
+ fprintf(stdout, "V: type = %u\n", (state->response).type);
+ fprintf(stdout, "V: resultcode = %u\n", (state->response).resultcode);
+ fprintf(stdout, "V: epoch = %u\n", (state->response).epoch);
+ }
+
+ return r;
+}
+#endif
+
diff --git a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h
new file mode 100644
index 000000000..0190379a2
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper-natpmp.h
+ **/
+
+#ifdef NAT_PMP
+#ifndef _TOR_FW_HELPER_NATPMP_H
+#define _TOR_FW_HELPER_NATPMP_H
+
+#include <natpmp.h>
+
+/** This is the default NAT-PMP lease time in seconds. */
+#define NATPMP_DEFAULT_LEASE 3600
+/** NAT-PMP has many codes for success; this is one of them. */
+#define NATPMP_SUCCESS 0
+
+/** This is our NAT-PMP meta structure - it holds our request data, responses,
+ * various NAT-PMP parameters, and of course the status of the motion in the
+ * NAT-PMP ocean. */
+typedef struct natpmp_state_t {
+ natpmp_t natpmp;
+ natpmpresp_t response;
+ int fetch_public_ip;
+ int status;
+ int init; /**< Have we been initialized? */
+ int protocol; /**< This will only be TCP. */
+ int lease;
+} natpmp_state_t;
+
+const tor_fw_backend_t *tor_fw_get_natpmp_backend(void);
+
+int tor_natpmp_init(tor_fw_options_t *tor_fw_options, void *backend_state);
+
+int tor_natpmp_cleanup(tor_fw_options_t *tor_fw_options, void *backend_state);
+
+int tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options,
+ void *backend_state);
+
+int tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options,
+ void *backend_state);
+
+#endif
+#endif
+
diff --git a/src/tools/tor-fw-helper/tor-fw-helper-upnp.c b/src/tools/tor-fw-helper/tor-fw-helper-upnp.c
new file mode 100644
index 000000000..c4b14a84e
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper-upnp.c
@@ -0,0 +1,189 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper-upnp.c
+ * \brief The implementation of our UPnP firewall helper.
+ **/
+
+#include "orconfig.h"
+#ifdef MINIUPNPC
+#ifdef MS_WINDOWS
+#define STATICLIB
+#endif
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <assert.h>
+
+#include "compat.h"
+#include "tor-fw-helper.h"
+#include "tor-fw-helper-upnp.h"
+
+/** UPnP timeout value. */
+#define UPNP_DISCOVER_TIMEOUT 2000
+/** Description of the port mapping in the UPnP table. */
+#define UPNP_DESC "Tor relay"
+
+/* XXX TODO: We should print these as a useful user string when we return the
+ * number to a user */
+/** Magic numbers as miniupnpc return codes. */
+#define UPNP_ERR_SUCCESS 0
+#define UPNP_ERR_NODEVICESFOUND 1
+#define UPNP_ERR_NOIGDFOUND 2
+#define UPNP_ERR_ADDPORTMAPPING 3
+#define UPNP_ERR_GETPORTMAPPING 4
+#define UPNP_ERR_DELPORTMAPPING 5
+#define UPNP_ERR_GETEXTERNALIP 6
+#define UPNP_ERR_INVAL 7
+#define UPNP_ERR_OTHER 8
+#define UPNP_SUCCESS 1
+
+/** This hooks miniupnpc into our multi-backend API. */
+static tor_fw_backend_t tor_miniupnp_backend = {
+ "miniupnp",
+ sizeof(struct miniupnpc_state_t),
+ tor_upnp_init,
+ tor_upnp_cleanup,
+ tor_upnp_fetch_public_ip,
+ tor_upnp_add_tcp_mapping
+};
+
+/** Return the backend for miniupnp. */
+const tor_fw_backend_t *
+tor_fw_get_miniupnp_backend(void)
+{
+ return &tor_miniupnp_backend;
+}
+
+/** Initialize the UPnP backend and store the results in
+ * <b>backend_state</b>.*/
+int
+tor_upnp_init(tor_fw_options_t *options, void *backend_state)
+{
+ /*
+ This leaks the user agent from the client to the router - perhaps we don't
+ want to do that? eg:
+
+ User-Agent: Ubuntu/10.04, UPnP/1.0, MiniUPnPc/1.4
+
+ */
+ miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
+ struct UPNPDev *devlist;
+ int r;
+
+ memset(&(state->urls), 0, sizeof(struct UPNPUrls));
+ memset(&(state->data), 0, sizeof(struct IGDdatas));
+ state->init = 0;
+
+ devlist = upnpDiscover(UPNP_DISCOVER_TIMEOUT, NULL, NULL, 0);
+ if (NULL == devlist) {
+ fprintf(stderr, "E: upnpDiscover returned: NULL\n");
+ return UPNP_ERR_NODEVICESFOUND;
+ }
+
+ assert(options);
+ r = UPNP_GetValidIGD(devlist, &(state->urls), &(state->data),
+ state->lanaddr, UPNP_LANADDR_SZ);
+ fprintf(stdout, "tor-fw-helper: UPnP GetValidIGD returned: %d (%s)\n", r,
+ r==UPNP_SUCCESS?"SUCCESS":"FAILED");
+
+ freeUPNPDevlist(devlist);
+
+ if (r != 1 && r != 2)
+ return UPNP_ERR_NOIGDFOUND;
+
+ state->init = 1;
+ return UPNP_ERR_SUCCESS;
+}
+
+/** Tear down the UPnP connection stored in <b>backend_state</b>.*/
+int
+tor_upnp_cleanup(tor_fw_options_t *options, void *backend_state)
+{
+
+ miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
+ assert(options);
+
+ if (state->init)
+ FreeUPNPUrls(&(state->urls));
+ state->init = 0;
+
+ return UPNP_ERR_SUCCESS;
+}
+
+/** Fetch our likely public IP from our upstream UPnP IGD enabled NAT device.
+ * Use the connection context stored in <b>backend_state</b>. */
+int
+tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state)
+{
+ miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
+ int r;
+ char externalIPAddress[16];
+
+ if (!state->init) {
+ r = tor_upnp_init(options, state);
+ if (r != UPNP_ERR_SUCCESS)
+ return r;
+ }
+
+ r = UPNP_GetExternalIPAddress(state->urls.controlURL,
+ state->data.first.servicetype,
+ externalIPAddress);
+
+ if (r != UPNPCOMMAND_SUCCESS)
+ goto err;
+
+ if (externalIPAddress[0]) {
+ fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n",
+ externalIPAddress); tor_upnp_cleanup(options, state);
+ options->public_ip_status = 1;
+ return UPNP_ERR_SUCCESS;
+ } else {
+ goto err;
+ }
+
+ err:
+ tor_upnp_cleanup(options, state);
+ return UPNP_ERR_GETEXTERNALIP;
+}
+
+/** Add a TCP port mapping for a single port stored in <b>tor_fw_options</b>
+ * and store the results in <b>backend_state</b>. */
+int
+tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state)
+{
+ miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
+ int r;
+ char internal_port_str[6];
+ char external_port_str[6];
+
+ if (!state->init) {
+ r = tor_upnp_init(options, state);
+ if (r != UPNP_ERR_SUCCESS)
+ return r;
+ }
+
+ if (options->verbose)
+ fprintf(stdout, "V: internal port: %d, external port: %d\n",
+ (int)options->internal_port, (int)options->external_port);
+
+ tor_snprintf(internal_port_str, sizeof(internal_port_str),
+ "%d", (int)options->internal_port);
+ tor_snprintf(external_port_str, sizeof(external_port_str),
+ "%d", (int)options->external_port);
+
+ r = UPNP_AddPortMapping(state->urls.controlURL,
+ state->data.first.servicetype,
+ external_port_str, internal_port_str,
+ state->lanaddr, UPNP_DESC, "TCP", 0);
+ if (r != UPNPCOMMAND_SUCCESS)
+ return UPNP_ERR_ADDPORTMAPPING;
+
+ options->upnp_status = 1;
+ return UPNP_ERR_SUCCESS;
+}
+#endif
+
diff --git a/src/tools/tor-fw-helper/tor-fw-helper-upnp.h b/src/tools/tor-fw-helper/tor-fw-helper-upnp.h
new file mode 100644
index 000000000..021a8e0aa
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper-upnp.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper-upnp.h
+ * \brief The main header for our firewall helper.
+ **/
+
+#ifdef MINIUPNPC
+#ifndef _TOR_FW_HELPER_UPNP_H
+#define _TOR_FW_HELPER_UPNP_H
+
+#include <miniupnpc/miniwget.h>
+#include <miniupnpc/miniupnpc.h>
+#include <miniupnpc/upnpcommands.h>
+#include <miniupnpc/upnperrors.h>
+
+/** This is a magic number for miniupnpc lan address size. */
+#define UPNP_LANADDR_SZ 64
+
+/** This is our miniupnpc meta structure - it holds our request data,
+ * responses, and various miniupnpc parameters. */
+typedef struct miniupnpc_state_t {
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ char lanaddr[UPNP_LANADDR_SZ];
+ int init;
+} miniupnpc_state_t;
+
+const tor_fw_backend_t *tor_fw_get_miniupnp_backend(void);
+
+int tor_upnp_init(tor_fw_options_t *options, void *backend_state);
+
+int tor_upnp_cleanup(tor_fw_options_t *options, void *backend_state);
+
+int tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state);
+
+int tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state);
+
+#endif
+#endif
+
diff --git a/src/tools/tor-fw-helper/tor-fw-helper.c b/src/tools/tor-fw-helper/tor-fw-helper.c
new file mode 100644
index 000000000..002239745
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper.c
@@ -0,0 +1,396 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper.c
+ * \brief The main wrapper around our firewall helper logic.
+ **/
+
+/*
+ * tor-fw-helper is a tool for opening firewalls with NAT-PMP and UPnP; this
+ * tool is designed to be called by hand or by Tor by way of a exec() at a
+ * later date.
+ */
+
+#include "orconfig.h"
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <time.h>
+#include <string.h>
+
+#ifdef MS_WINDOWS
+#include <winsock2.h>
+#endif
+
+#include "tor-fw-helper.h"
+#ifdef NAT_PMP
+#include "tor-fw-helper-natpmp.h"
+#endif
+#ifdef MINIUPNPC
+#include "tor-fw-helper-upnp.h"
+#endif
+
+/** This is our meta storage type - it holds information about each helper
+ including the total number of helper backends, function pointers, and helper
+ state. */
+typedef struct backends_t {
+ /** The total number of backends */
+ int n_backends;
+ /** The backend functions as an array */
+ tor_fw_backend_t backend_ops[MAX_BACKENDS];
+ /** The internal backend state */
+ void *backend_state[MAX_BACKENDS];
+} backends_t;
+
+/** Initalize each backend helper with the user input stored in <b>options</b>
+ * and put the results in the <b>backends</b> struct. */
+static int
+init_backends(tor_fw_options_t *options, backends_t *backends)
+{
+ int n_available = 0;
+ int i, r, n;
+ tor_fw_backend_t *backend_ops_list[MAX_BACKENDS];
+ void *data = NULL;
+ /* First, build a list of the working backends. */
+ n = 0;
+#ifdef MINIUPNPC
+ backend_ops_list[n++] = (tor_fw_backend_t *) tor_fw_get_miniupnp_backend();
+#endif
+#ifdef NAT_PMP
+ backend_ops_list[n++] = (tor_fw_backend_t *) tor_fw_get_natpmp_backend();
+#endif
+ n_available = n;
+
+ /* Now, for each backend that might work, try to initialize it.
+ * That's how we roll, initialized.
+ */
+ n = 0;
+ for (i=0; i<n_available; ++i) {
+ data = calloc(1, backend_ops_list[i]->state_len);
+ if (!data) {
+ perror("calloc");
+ exit(1);
+ }
+ r = backend_ops_list[i]->init(options, data);
+ if (r == 0) {
+ backends->backend_ops[n] = *backend_ops_list[i];
+ backends->backend_state[n] = data;
+ n++;
+ } else {
+ free(data);
+ }
+ }
+ backends->n_backends = n;
+
+ return n;
+}
+
+/** Return the proper commandline switches when the user needs information. */
+static void
+usage(void)
+{
+ fprintf(stderr, "tor-fw-helper usage:\n"
+ " [-h|--help]\n"
+ " [-T|--Test]\n"
+ " [-v|--verbose]\n"
+ " [-g|--fetch-public-ip]\n"
+ " -i|--internal-or-port [TCP port]\n"
+ " [-e|--external-or-port [TCP port]]\n"
+ " [-d|--internal-dir-port [TCP port]\n"
+ " [-p|--external-dir-port [TCP port]]]\n");
+}
+
+/** Log commandline options to a hardcoded file <b>tor-fw-helper.log</b> in the
+ * current working directory. */
+static int
+log_commandline_options(int argc, char **argv)
+{
+ int i, retval;
+ FILE *logfile;
+ time_t now;
+
+ /* Open the log file */
+ logfile = fopen("tor-fw-helper.log", "a");
+ if (NULL == logfile)
+ return -1;
+
+ /* Send all commandline arguments to the file */
+ now = time(NULL);
+ retval = fprintf(logfile, "START: %s\n", ctime(&now));
+ for (i = 0; i < argc; i++) {
+ retval = fprintf(logfile, "ARG: %d: %s\n", i, argv[i]);
+ if (retval < 0)
+ goto error;
+
+ retval = fprintf(stdout, "ARG: %d: %s\n", i, argv[i]);
+ if (retval < 0)
+ goto error;
+ }
+ now = time(NULL);
+ retval = fprintf(logfile, "END: %s\n", ctime(&now));
+
+ /* Close and clean up */
+ retval = fclose(logfile);
+ return retval;
+
+ /* If there was an error during writing */
+ error:
+ fclose(logfile);
+ return -1;
+}
+
+/** Iterate over over each of the supported <b>backends</b> and attempt to
+ * fetch the public ip. */
+static void
+tor_fw_fetch_public_ip(tor_fw_options_t *tor_fw_options,
+ backends_t *backends)
+{
+ int i;
+ int r = 0;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: tor_fw_fetch_public_ip\n");
+
+ for (i=0; i<backends->n_backends; ++i) {
+ if (tor_fw_options->verbose) {
+ fprintf(stdout, "V: running backend_state now: %i\n", i);
+ fprintf(stdout, "V: size of backend state: %u\n",
+ (int)(backends->backend_ops)[i].state_len);
+ fprintf(stdout, "V: backend state name: %s\n",
+ (char *)(backends->backend_ops)[i].name);
+ }
+ r = backends->backend_ops[i].fetch_public_ip(tor_fw_options,
+ backends->backend_state[i]);
+ fprintf(stdout, "tor-fw-helper: tor_fw_fetch_public_ip backend %s "
+ " returned: %i\n", (char *)(backends->backend_ops)[i].name, r);
+ }
+}
+
+/** Iterate over each of the supported <b>backends</b> and attempt to add a
+ * port forward for the OR port stored in <b>tor_fw_options</b>. */
+static void
+tor_fw_add_or_port(tor_fw_options_t *tor_fw_options,
+ backends_t *backends)
+{
+ int i;
+ int r = 0;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: tor_fw_add_or_port\n");
+
+ for (i=0; i<backends->n_backends; ++i) {
+ if (tor_fw_options->verbose) {
+ fprintf(stdout, "V: running backend_state now: %i\n", i);
+ fprintf(stdout, "V: size of backend state: %u\n",
+ (int)(backends->backend_ops)[i].state_len);
+ fprintf(stdout, "V: backend state name: %s\n",
+ (const char *) backends->backend_ops[i].name);
+ }
+ r = backends->backend_ops[i].add_tcp_mapping(tor_fw_options,
+ backends->backend_state[i]);
+ fprintf(stdout, "tor-fw-helper: tor_fw_add_or_port backend %s "
+ "returned: %i\n", (const char *) backends->backend_ops[i].name, r);
+ }
+}
+
+/** Iterate over each of the supported <b>backends</b> and attempt to add a
+ * port forward for the Dir port stored in <b>tor_fw_options</b>. */
+static void
+tor_fw_add_dir_port(tor_fw_options_t *tor_fw_options,
+ backends_t *backends)
+{
+ int i;
+ int r = 0;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: tor_fw_add_dir_port\n");
+
+ for (i=0; i<backends->n_backends; ++i) {
+ if (tor_fw_options->verbose) {
+ fprintf(stdout, "V: running backend_state now: %i\n", i);
+ fprintf(stdout, "V: size of backend state: %u\n",
+ (int)(backends->backend_ops)[i].state_len);
+ fprintf(stdout, "V: backend state name: %s\n",
+ (char *)(backends->backend_ops)[i].name);
+ }
+ r = backends->backend_ops[i].add_tcp_mapping(tor_fw_options,
+ backends->backend_state[i]);
+ fprintf(stdout, "tor-fw-helper: tor_fw_add_dir_port backend %s "
+ "returned: %i\n", (const char *)backends->backend_ops[i].name, r);
+ }
+}
+
+/** Called before we make any calls to network-related functions.
+ * (Some operating systems require their network libraries to be
+ * initialized.) (from common/compat.c) */
+static int
+network_init(void)
+{
+#ifdef MS_WINDOWS
+ /* This silly exercise is necessary before windows will allow
+ * gethostbyname to work. */
+ WSADATA WSAData;
+ int r;
+ r = WSAStartup(0x101, &WSAData);
+ if (r) {
+ fprintf(stderr, "E: Error initializing Windows network layer "
+ "- code was %d", r);
+ return -1;
+ }
+ /* WSAData.iMaxSockets might show the max sockets we're allowed to use.
+ * We might use it to complain if we're trying to be a server but have
+ * too few sockets available. */
+#endif
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int r = 0;
+ int c = 0;
+
+ tor_fw_options_t tor_fw_options;
+ backends_t backend_state;
+
+ memset(&tor_fw_options, 0, sizeof(tor_fw_options));
+ memset(&backend_state, 0, sizeof(backend_state));
+
+ while (1) {
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"verbose", 0, 0, 'v'},
+ {"help", 0, 0, 'h'},
+ {"internal-or-port", 1, 0, 'i'},
+ {"external-or-port", 1, 0, 'e'},
+ {"internal-dir-port", 1, 0, 'd'},
+ {"external-dir-port", 1, 0, 'p'},
+ {"fetch-public-ip", 0, 0, 'g'},
+ {"test-commandline", 0, 0, 'T'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "vhi:e:d:p:gT",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'v': tor_fw_options.verbose = 1; break;
+ case 'h': tor_fw_options.help = 1; usage(); exit(1); break;
+ case 'i': sscanf(optarg, "%hu", &tor_fw_options.private_or_port);
+ break;
+ case 'e': sscanf(optarg, "%hu", &tor_fw_options.public_or_port);
+ break;
+ case 'd': sscanf(optarg, "%hu", &tor_fw_options.private_dir_port);
+ break;
+ case 'p': sscanf(optarg, "%hu", &tor_fw_options.public_dir_port);
+ break;
+ case 'g': tor_fw_options.fetch_public_ip = 1; break;
+ case 'T': tor_fw_options.test_commandline = 1; break;
+ case '?': break;
+ default : fprintf(stderr, "Unknown option!\n"); usage(); exit(1);
+ }
+ }
+
+ if (tor_fw_options.verbose) {
+ fprintf(stderr, "V: tor-fw-helper version %s\n"
+ "V: We were called with the following arguments:\n"
+ "V: verbose = %d, help = %d, pub or port = %u, "
+ "priv or port = %u\n"
+ "V: pub dir port = %u, priv dir port = %u\n"
+ "V: fetch_public_ip = %u\n",
+ tor_fw_version, tor_fw_options.verbose, tor_fw_options.help,
+ tor_fw_options.private_or_port, tor_fw_options.public_or_port,
+ tor_fw_options.private_dir_port, tor_fw_options.public_dir_port,
+ tor_fw_options.fetch_public_ip);
+ }
+
+ if (tor_fw_options.test_commandline) {
+ return log_commandline_options(argc, argv);
+ }
+
+ /* At the very least, we require an ORPort;
+ Given a private ORPort, we can ask for a mapping that matches the port
+ externally.
+ */
+ if (!tor_fw_options.private_or_port && !tor_fw_options.fetch_public_ip) {
+ fprintf(stderr, "E: We require an ORPort or fetch_public_ip"
+ " request!\n");
+ usage();
+ exit(1);
+ } else {
+ /* When we only have one ORPort, internal/external are
+ set to be the same.*/
+ if (!tor_fw_options.public_or_port && tor_fw_options.private_or_port) {
+ if (tor_fw_options.verbose)
+ fprintf(stdout, "V: We're setting public_or_port = "
+ "private_or_port.\n");
+ tor_fw_options.public_or_port = tor_fw_options.private_or_port;
+ }
+ }
+ if (!tor_fw_options.private_dir_port) {
+ if (tor_fw_options.verbose)
+ fprintf(stdout, "V: We have no DirPort; no hole punching for "
+ "DirPorts\n");
+
+ } else {
+ /* When we only have one DirPort, internal/external are
+ set to be the same.*/
+ if (!tor_fw_options.public_dir_port && tor_fw_options.private_dir_port) {
+ if (tor_fw_options.verbose)
+ fprintf(stdout, "V: We're setting public_or_port = "
+ "private_or_port.\n");
+
+ tor_fw_options.public_dir_port = tor_fw_options.private_dir_port;
+ }
+ }
+
+ if (tor_fw_options.verbose) {
+ fprintf(stdout, "V: pub or port = %u, priv or port = %u\n"
+ "V: pub dir port = %u, priv dir port = %u\n",
+ tor_fw_options.private_or_port, tor_fw_options.public_or_port,
+ tor_fw_options.private_dir_port,
+ tor_fw_options.public_dir_port);
+ }
+
+ // Initialize networking
+ if (network_init())
+ exit(1);
+
+ // Initalize the various fw-helper backend helpers
+ r = init_backends(&tor_fw_options, &backend_state);
+ if (r)
+ printf("tor-fw-helper: %i NAT traversal helper(s) loaded\n", r);
+
+ if (tor_fw_options.fetch_public_ip) {
+ tor_fw_fetch_public_ip(&tor_fw_options, &backend_state);
+ }
+
+ if (tor_fw_options.private_or_port) {
+ tor_fw_options.internal_port = tor_fw_options.private_or_port;
+ tor_fw_options.external_port = tor_fw_options.private_or_port;
+ tor_fw_add_or_port(&tor_fw_options, &backend_state);
+ }
+
+ if (tor_fw_options.private_dir_port) {
+ tor_fw_options.internal_port = tor_fw_options.private_dir_port;
+ tor_fw_options.external_port = tor_fw_options.private_dir_port;
+ tor_fw_add_dir_port(&tor_fw_options, &backend_state);
+ }
+
+ r = (((tor_fw_options.nat_pmp_status | tor_fw_options.upnp_status)
+ |tor_fw_options.public_ip_status));
+ if (r > 0) {
+ fprintf(stdout, "tor-fw-helper: SUCCESS\n");
+ } else {
+ fprintf(stderr, "tor-fw-helper: FAILURE\n");
+ }
+
+ exit(r);
+}
+
diff --git a/src/tools/tor-fw-helper/tor-fw-helper.h b/src/tools/tor-fw-helper/tor-fw-helper.h
new file mode 100644
index 000000000..39d852d21
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper.h
+ * \brief The main header for our firewall helper.
+ **/
+
+#ifndef _TOR_FW_HELPER_H
+#define _TOR_FW_HELPER_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <time.h>
+
+/** The current version of tor-fw-helper. */
+#define tor_fw_version "0.1"
+
+/** This is an arbitrary hard limit - We currently have two (NAT-PMP and UPnP).
+ We're likely going to add the Intel UPnP library but nothing else comes to
+ mind at the moment. */
+#define MAX_BACKENDS 23
+
+/** This is where we store parsed commandline options. */
+typedef struct {
+ int verbose;
+ int help;
+ int test_commandline;
+ uint16_t private_dir_port;
+ uint16_t private_or_port;
+ uint16_t public_dir_port;
+ uint16_t public_or_port;
+ uint16_t internal_port;
+ uint16_t external_port;
+ int fetch_public_ip;
+ int nat_pmp_status;
+ int upnp_status;
+ int public_ip_status;
+} tor_fw_options_t;
+
+/** This is our main structure that defines our backend helper API; each helper
+ * must conform to these public methods if it expects to be handled in a
+ * non-special way. */
+typedef struct tor_fw_backend_t {
+ const char *name;
+ size_t state_len;
+ int (*init)(tor_fw_options_t *options, void *backend_state);
+ int (*cleanup)(tor_fw_options_t *options, void *backend_state);
+ int (*fetch_public_ip)(tor_fw_options_t *options, void *backend_state);
+ int (*add_tcp_mapping)(tor_fw_options_t *options, void *backend_state);
+} tor_fw_backend_t;
+
+#endif
+