Commit 9cc76cf0 authored by Jacob Appelbaum's avatar Jacob Appelbaum Committed by Nick Mathewson
Browse files

First implementation of tor-fw-helper.

tor-fw-helper is a command-line tool to wrap and abstract various
firewall port-forwarding tools.

This commit matches the state of Jacob's tor-fw-helper branch as of
23 September 2010.

  (commit msg by Nick)
parent 3ad43ef7
......@@ -161,6 +161,12 @@
/src/tools/Makefile
/src/tools/Makefile.in
# /src/tools/tor-fw-helper/
/src/tools/tor-fw-helper/tor-fw-helper
/src/tools/tor-fw-helper/tor-fw-helper.exe
/src/tools/tor-fw-helper/Makefile
/src/tools/tor-fw-helper/Makefile.in
# /src/win32/
/src/win32/Makefile
/src/win32/Makefile.in
......@@ -68,7 +68,8 @@ check-spaces:
src/common/*.h \
src/common/[^asO]*.c src/common/address.c \
src/or/[^e]*.[ch] src/or/eventdns_tor.h \
src/test/test*.[ch] src/tools/*.[ch]
src/test/test*.[ch] src/tools/*.[ch] \
src/tools/tor-fw-helper/*.[ch]
check-docs:
./contrib/checkOptionDocs.pl
......
......@@ -59,6 +59,24 @@ AC_ARG_ENABLE(asciidoc,
*) AC_MSG_ERROR(bad value for --disable-asciidoc) ;;
esac], [asciidoc=true])
# By default, we're not ready to ship a NAT-PMP aware Tor
AC_ARG_ENABLE(nat-pmp,
AS_HELP_STRING(--enable-nat-pmp, enable NAT-PMP support),
[case "${enableval}" in
yes) natpmp=true ;;
no) natpmp=false ;;
* ) AC_MSG_ERROR(bad value for --enable-nat-pmp) ;;
esac], [natpmp=false])
# By default, we're not ready to ship a UPnP aware Tor
AC_ARG_ENABLE(upnp,
AS_HELP_STRING(--enable-upnp, enable UPnP support),
[case "${enableval}" in
yes) upnp=true ;;
no) upnp=false ;;
* ) AC_MSG_ERROR(bad value for --enable-upnp) ;;
esac], [upnp=false])
AC_ARG_ENABLE(threads,
AS_HELP_STRING(--disable-threads, disable multi-threading support))
......@@ -135,6 +153,8 @@ AC_PATH_PROG([A2X], [a2x], none)
AM_CONDITIONAL(USE_ASCIIDOC, test x$asciidoc = xtrue)
AM_CONDITIONAL(USE_FW_HELPER, test x$natpmp = xtrue || x$upnp = xtrue)
AC_PATH_PROG([SHA1SUM], [sha1sum], none)
AC_PATH_PROG([OPENSSL], [openssl], none)
......@@ -440,6 +460,44 @@ AC_SUBST(TOR_ZLIB_LIBS)
dnl Make sure to enable support for large off_t if available.
dnl ------------------------------------------------------
dnl Where do you live, libnatpmp? And how do we call you?
dnl There are no packages for Debian or Redhat as of this patch
if test "$natpmp" = "true"; then
AC_DEFINE(NAT_PMP, 1, [Define to 1 if we are building with nat-pmp.])
TOR_SEARCH_LIBRARY(libnatpmp, $trylibnatpmpdir, [-lnatpmp],
[#include <natpmp.h>],
[#include <natpmp.h>],
[ int r;
natpmp_t natpmp;
natpmpresp_t response;
r = initnatpmp(&natpmp);],
[printf("initnatpmp() returned %d (%s)\n", r, r?"FAILED":"SUCCESS");
exit(0);],
[--with-libnatpmp-dir],
[/usr/lib/])
fi
dnl ------------------------------------------------------
dnl Where do you live, libminiupnpc? And how do we call you?
dnl There are no packages for Debian or Redhat as of this patch
if test "$upnp" = "true"; then
AC_DEFINE(UPNP, 1, [Define to 1 if we are building with UPnP.])
TOR_SEARCH_LIBRARY(libminiupnpc, $trylibminiupnpcdir, [-lminiupnpc],
[#include <miniupnpc/miniwget.h>
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>],
[void upnpDiscover(int delay, const char * multicastif,
const char * minissdpdsock, int sameport);],
[upnpDiscover(1, 0, 0, 0); exit(0);],
[--with-libminiupnpc-dir],
[/usr/lib/])
fi
AC_SYS_LARGEFILE
AC_CHECK_HEADERS(unistd.h string.h signal.h sys/stat.h sys/types.h fcntl.h sys/fcntl.h sys/time.h errno.h assert.h time.h, , AC_MSG_WARN(Some headers were not found, compilation may fail. If compilation succeeds, please send your orconfig.h to the developers so we can fix this warning.))
......@@ -967,7 +1025,7 @@ fi
CPPFLAGS="$CPPFLAGS $TOR_CPPFLAGS_libevent $TOR_CPPFLAGS_openssl $TOR_CPPFLAGS_zlib"
AC_CONFIG_FILES([Makefile tor.spec Doxyfile contrib/tor.sh contrib/torctl contrib/torify contrib/tor.logrotate contrib/Makefile contrib/osx/Makefile contrib/osx/TorBundleDesc.plist contrib/osx/TorBundleInfo.plist contrib/osx/TorDesc.plist contrib/osx/TorInfo.plist contrib/osx/TorStartupDesc.plist src/config/torrc.sample src/Makefile doc/Makefile doc/spec/Makefile src/config/Makefile src/common/Makefile src/or/Makefile src/test/Makefile src/win32/Makefile src/tools/Makefile contrib/suse/Makefile contrib/suse/tor.sh])
AC_CONFIG_FILES([Makefile tor.spec Doxyfile contrib/tor.sh contrib/torctl contrib/torify contrib/tor.logrotate contrib/Makefile contrib/osx/Makefile contrib/osx/TorBundleDesc.plist contrib/osx/TorBundleInfo.plist contrib/osx/TorDesc.plist contrib/osx/TorInfo.plist contrib/osx/TorStartupDesc.plist src/config/torrc.sample src/Makefile doc/Makefile doc/spec/Makefile src/config/Makefile src/common/Makefile src/or/Makefile src/test/Makefile src/win32/Makefile src/tools/Makefile src/tools/tor-fw-helper/Makefile contrib/suse/Makefile contrib/suse/tor.sh])
AC_OUTPUT
if test -x /usr/bin/perl && test -x ./contrib/updateVersions.pl ; then
......
Tor's (little) Firewall Helper specification
Jacob Appelbaum
0. Preface
This document describes issues faced by Tor users who are behind NAT devices
and wish to share their resources with the rest of the Tor network. It also
explains a possible solution for some NAT devices.
1. Overview
Tor users often wish to relay traffic for the Tor network and their upstream
firewall thwarts their attempted generosity. Automatic port forwarding
configuration for many consumer NAT devices is often available with two common
protocols NAT-PMP[0] and UPnP[1].
2. Implementation
tor-fw-helper is a program that implements basic port forwarding requests; it
may be used alone or called from Tor itself.
2.1 Output format
When tor-fw-helper has completed the requested action successfully, it will
report the following message to standard output:
tor-fw-helper: SUCCESS
If tor-fw-helper was unable to complete the requested action successfully, it
will report the following message to standard error:
tor-fw-helper: FAILURE
All informational messages are printed to standard output; all error messages
are printed to standard error.
3. Security Concerns
It is probably best to hand configure port forwarding and in the process, we
suggest disabling NAT-PMP and/or UPnP.
[0] http://en.wikipedia.org/wiki/NAT_Port_Mapping_Protocol
[1] http://en.wikipedia.org/wiki/Universal_Plug_and_Play
......@@ -16,3 +16,7 @@ tor_checkkey_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
tor_checkkey_LDADD = ../common/libor.a ../common/libor-crypto.a \
-lm @TOR_ZLIB_LIBS@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
SUBDIRS = tor-fw-helper
DIST_SUBDIRS = tor-fw-helper
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
tor_fw_helper_INCLUDES = tor-fw-helper.h tor-fw-helper-natpmp.h tor-fw-helper-upnp.h
tor_fw_helper_LDFLAGS = @TOR_LDFLAGS_libnatpmp@ @TOR_LDFLAGS_libminiupnpc@
tor_fw_helper_LDADD = -lnatpmp -lminiupnpc ../../common/libor.a @TOR_LIB_WS32@
tor_fw_helper_CPPFLAGS = @TOR_CPPFLAGS_libnatpmp@ @TOR_CPPFLAGS_libminiupnpc@
/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
* Copyright (c) 2010, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "tor-fw-helper.h"
#include "tor-fw-helper-natpmp.h"
int
tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options)
{
int r = 0;
int x = 0;
int sav_errno;
int protocol = NATPMP_PROTOCOL_TCP;
int lease = NATPMP_DEFAULT_LEASE;
natpmp_t natpmp;
natpmpresp_t response;
fd_set fds;
struct timeval timeout;
if (tor_fw_options->verbose)
fprintf(stdout, "V: natpmp init...\n");
initnatpmp(&natpmp);
if (tor_fw_options->verbose)
fprintf(stdout, "V: sending natpmp portmapping request...\n");
r = sendnewportmappingrequest(&natpmp, protocol,
tor_fw_options->internal_port,
tor_fw_options->external_port,
lease);
fprintf(stdout, "tor-fw-helper: NAT-PMP sendnewportmappingrequest returned"
" %d (%s)\n", r, r==12?"SUCCESS":"FAILED");
do {
FD_ZERO(&fds);
FD_SET(natpmp.s, &fds);
getnatpmprequesttimeout(&natpmp, &timeout);
select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
if (tor_fw_options->verbose)
fprintf(stdout, "V: attempting to readnatpmpreponseorretry...\n");
r = readnatpmpresponseorretry(&natpmp, &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 == NATPMP_SUCCESS) {
fprintf(stdout, "tor-fw-helper: NAT-PMP mapped public port %hu to"
" localport %hu liftime %u\n",
response.pnu.newportmapping.mappedpublicport,
response.pnu.newportmapping.privateport,
response.pnu.newportmapping.lifetime);
}
x = closenatpmp(&natpmp);
if (tor_fw_options->verbose)
fprintf(stdout, "V: closing natpmp socket: %d\n", x);
return r;
}
int
tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options)
{
int r = 0;
int x = 0;
int sav_errno;
natpmp_t natpmp;
natpmpresp_t response;
struct timeval timeout;
fd_set fds;
r = initnatpmp(&natpmp);
if (tor_fw_options->verbose)
fprintf(stdout, "V: NAT-PMP init: %d\n", r);
r = sendpublicaddressrequest(&natpmp);
fprintf(stdout, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned"
" %d (%s)\n", r, r==2?"SUCCESS":"FAILED");
do {
FD_ZERO(&fds);
FD_SET(natpmp.s, &fds);
getnatpmprequesttimeout(&natpmp, &timeout);
select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
if (tor_fw_options->verbose)
fprintf(stdout, "V: NAT-PMP attempting to read reponse...\n");
r = readnatpmpresponseorretry(&natpmp, &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(response.pnu.publicaddress.addr));
x = closenatpmp(&natpmp);
if (tor_fw_options->verbose)
{
fprintf(stdout, "V: result = %u\n", r);
fprintf(stdout, "V: type = %u\n", response.type);
fprintf(stdout, "V: resultcode = %u\n", response.resultcode);
fprintf(stdout, "V: epoch = %u\n", response.epoch);
fprintf(stdout, "V: closing natpmp result: %d\n", r);
}
return r;
}
/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
* Copyright (c) 2010, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef _TOR_FW_HELPER_NATPMP_H
#define _TOR_FW_HELPER_NATPMP_H
#include <natpmp.h>
#define NATPMP_DEFAULT_LEASE 3600
#define NATPMP_SUCCESS 0
int tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options);
int tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options);
#endif
/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
* Copyright (c) 2010, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "tor-fw-helper.h"
#include "tor-fw-helper-upnp.h"
#define UPNP_DISCOVER_TIMEOUT 2000
/* Description of the port mapping in the UPnP table */
#define UPNP_DESC "Tor relay"
#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
int
tor_upnp_init(miniupnpc_state_t *state)
{
struct UPNPDev *devlist;
int r;
memset(&(state->urls), 0, sizeof(struct UPNPUrls));
memset(&(state->data), 0, sizeof(struct IGDdatas));
devlist = upnpDiscover(UPNP_DISCOVER_TIMEOUT, NULL, NULL, 0);
if (NULL == devlist) {
fprintf(stderr, "E: upnpDiscover returned: NULL\n");
return UPNP_ERR_NODEVICESFOUND;
}
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;
}
int
tor_upnp_cleanup(miniupnpc_state_t *state)
{
if (state->init)
FreeUPNPUrls(&(state->urls));
state->init = 0;
return UPNP_ERR_SUCCESS;
}
int
tor_upnp_fetch_public_ip(miniupnpc_state_t *state)
{
int r;
char externalIPAddress[16];
if (!state->init) {
r = tor_upnp_init(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(state);
return UPNP_ERR_SUCCESS;
} else
goto err;
err:
tor_upnp_cleanup(state);
return UPNP_ERR_GETEXTERNALIP;
}
int
tor_upnp_add_tcp_mapping(miniupnpc_state_t *state,
uint16_t internal_port, uint16_t external_port)
{
int r;
char internal_port_str[6];
char external_port_str[6];
if (!state->init) {
r = tor_upnp_init(state);
if (r != UPNP_ERR_SUCCESS)
return r;
}
snprintf(internal_port_str, sizeof(internal_port_str),
"%d", internal_port);
snprintf(external_port_str, sizeof(external_port_str),
"%d", 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;
return UPNP_ERR_SUCCESS;
}
/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
* Copyright (c) 2010, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#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>
#define UPNP_LANADDR_SZ 64
typedef struct miniupnpc_state_t {
struct UPNPUrls urls;
struct IGDdatas data;
char lanaddr[UPNP_LANADDR_SZ];
int init;
} miniupnpc_state_t;
int tor_upnp_init(miniupnpc_state_t *state);
int tor_upnp_cleanup(miniupnpc_state_t *state);
int tor_upnp_fetch_public_ip(miniupnpc_state_t *state);
int tor_upnp_add_tcp_mapping(miniupnpc_state_t *state,
uint16_t internal_port, uint16_t external_port);
#endif
/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
* Copyright (c) 2010, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/*
* 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <getopt.h>
#include <time.h>
#include "orconfig.h"
#include "tor-fw-helper.h"
#include "tor-fw-helper-natpmp.h"
#include "tor-fw-helper-upnp.h"
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 */
static int
test_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;
}
static void
tor_fw_fetch_public_ip(tor_fw_options_t *tor_fw_options,
miniupnpc_state_t *miniupnpc_state)
{
int r = 0;
r = tor_natpmp_fetch_public_ip(tor_fw_options);
if (tor_fw_options->verbose)
fprintf(stdout, "V: Attempts to fetch public ip (natpmp) resulted in: "
"%d\n", r);
if (r == 0)
tor_fw_options->public_ip_status = 1;
r = tor_upnp_fetch_public_ip(miniupnpc_state);
if (tor_fw_options->verbose)
fprintf(stdout, "V: Attempts to fetch public ip (upnp) resulted in: "
"%d\n", r);
if (r == 0)
tor_fw_options->public_ip_status = 1;
}
static void
tor_fw_add_or_port(tor_fw_options_t *tor_fw_options, miniupnpc_state_t
*miniupnpc_state)
{
int r = 0;
tor_fw_options->internal_port = tor_fw_options->private_or_port;
tor_fw_options->external_port = tor_fw_options->public_or_port;
r = tor_natpmp_add_tcp_mapping(tor_fw_options);
fprintf(stdout, "tor-fw-helper: Attempts to add ORPort mapping (natpmp)"
"resulted in: %d\n", r);
if (r == 0)
tor_fw_options->nat_pmp_status = 1;
r = tor_upnp_add_tcp_mapping(miniupnpc_state,
tor_fw_options->private_or_port,
tor_fw_options->public_or_port);
fprintf(stdout, "tor-fw-helper: Attempts to add ORPort mapping (upnp)"
"resulted in: %d\n", r);
if (r == 0)