Merge remote-tracking branch 'asn/bug4567_rebased'

This commit is contained in:
Nick Mathewson 2012-09-06 10:12:28 -04:00
commit e9684405ac
17 changed files with 657 additions and 319 deletions

3
changes/bug4567 Normal file
View File

@ -0,0 +1,3 @@
o Major features:
- Automatically forward the TCP ports of pluggable transport
proxies using tor-fw-helper if PortForwarding is enabled.

4
changes/bug4567_2 Normal file
View File

@ -0,0 +1,4 @@
o Code refactoring:
- Tweak tor-fw-helper to accept an arbitrary amount of arbitrary
TCP ports to forward. In the past it only accepted two ports:
the ORPort and the DirPort.

View File

@ -41,18 +41,8 @@ OPTIONS
**-g** or **--fetch-public-ip**:: **-g** or **--fetch-public-ip**::
Fetch the the public ip address for each supported NAT helper method. Fetch the the public ip address for each supported NAT helper method.
**-i** or **--internal-or-port** __port__:: **-p** or **--forward-port** __external_port__:__internal_port__::
Inform **tor-fw-helper** of your internal OR port. This is the only Forward external_port to internal_port.
required argument.
**-e** or **--external-or-port** __port__::
Inform **tor-fw-helper** of your external OR port.
**-d** or **--internal-dir-port** __port__::
Inform **tor-fw-helper** of your internal Dir port.
**-p** or **--external-dir-port** __port__::
Inform **tor-fw-helper** of your external Dir port.
BUGS BUGS
---- ----

View File

@ -4374,6 +4374,50 @@ tor_split_lines(smartlist_t *sl, char *buf, int len)
} }
#ifdef _WIN32 #ifdef _WIN32
/** Return a smartlist containing lines outputted from
* <b>handle</b>. Return NULL on error, and set
* <b>stream_status_out</b> appropriately. */
smartlist_t *
tor_get_lines_from_handle(HANDLE *handle,
enum stream_status *stream_status_out)
{
int pos;
char stdout_buf[600] = {0};
smartlist_t *lines = NULL;
tor_assert(stream_status_out);
*stream_status_out = IO_STREAM_TERM;
pos = tor_read_all_handle(handle, stdout_buf, sizeof(stdout_buf) - 1, NULL);
if (pos < 0) {
*stream_status_out = IO_STREAM_TERM;
return NULL;
}
if (pos == 0) {
*stream_status_out = IO_STREAM_EAGAIN;
return NULL;
}
/* End with a null even if there isn't a \r\n at the end */
/* TODO: What if this is a partial line? */
stdout_buf[pos] = '\0';
/* Split up the buffer */
lines = smartlist_new();
tor_split_lines(lines, stdout_buf, pos);
/* Currently 'lines' is populated with strings residing on the
stack. Replace them with their exact copies on the heap: */
SMARTLIST_FOREACH(lines, char *, line,
SMARTLIST_REPLACE_CURRENT(lines, line, tor_strdup(line)));
*stream_status_out = IO_STREAM_OKAY;
return lines;
}
/** Read from stream, and send lines to log at the specified log level. /** Read from stream, and send lines to log at the specified log level.
* Returns -1 if there is a error reading, and 0 otherwise. * Returns -1 if there is a error reading, and 0 otherwise.
* If the generated stream is flushed more often than on new lines, or * If the generated stream is flushed more often than on new lines, or
@ -4421,6 +4465,33 @@ log_from_handle(HANDLE *pipe, int severity)
#else #else
/** Return a smartlist containing lines outputted from
* <b>handle</b>. Return NULL on error, and set
* <b>stream_status_out</b> appropriately. */
smartlist_t *
tor_get_lines_from_handle(FILE *handle, enum stream_status *stream_status_out)
{
enum stream_status stream_status;
char stdout_buf[400];
smartlist_t *lines = NULL;
while (1) {
memset(stdout_buf, 0, sizeof(stdout_buf));
stream_status = get_string_from_pipe(handle,
stdout_buf, sizeof(stdout_buf) - 1);
if (stream_status != IO_STREAM_OKAY)
goto done;
if (!lines) lines = smartlist_new();
smartlist_add(lines, tor_strdup(stdout_buf));
}
done:
*stream_status_out = stream_status;
return lines;
}
/** Read from stream, and send lines to log at the specified log level. /** Read from stream, and send lines to log at the specified log level.
* Returns 1 if stream is closed normally, -1 if there is a error reading, and * Returns 1 if stream is closed normally, -1 if there is a error reading, and
* 0 otherwise. Handles lines from tor-fw-helper and * 0 otherwise. Handles lines from tor-fw-helper and
@ -4539,9 +4610,130 @@ get_string_from_pipe(FILE *stream, char *buf_out, size_t count)
return IO_STREAM_TERM; return IO_STREAM_TERM;
} }
/* DOCDOC tor_check_port_forwarding */ /** Parse a <b>line</b> from tor-fw-helper and issue an appropriate
* log message to our user. */
static void
handle_fw_helper_line(const char *line)
{
smartlist_t *tokens = smartlist_new();
char *message = NULL;
char *message_for_log = NULL;
const char *external_port = NULL;
const char *internal_port = NULL;
const char *result = NULL;
int port = 0;
int success = 0;
smartlist_split_string(tokens, line, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
if (smartlist_len(tokens) < 5)
goto err;
if (strcmp(smartlist_get(tokens, 0), "tor-fw-helper") ||
strcmp(smartlist_get(tokens, 1), "tcp-forward"))
goto err;
external_port = smartlist_get(tokens, 2);
internal_port = smartlist_get(tokens, 3);
result = smartlist_get(tokens, 4);
if (smartlist_len(tokens) > 5) {
/* If there are more than 5 tokens, they are part of [<message>].
Let's use a second smartlist to form the whole message;
strncat loops suck. */
int i;
int message_words_n = smartlist_len(tokens) - 5;
smartlist_t *message_sl = smartlist_new();
for (i = 0; i < message_words_n; i++)
smartlist_add(message_sl, smartlist_get(tokens, 5+i));
tor_assert(smartlist_len(message_sl) > 0);
message = smartlist_join_strings(message_sl, " ", 0, NULL);
/* wrap the message in log-friendly wrapping */
tor_asprintf(&message_for_log, " ('%s')", message);
smartlist_free(message_sl);
}
port = atoi(external_port);
if (port < 1 || port > 65535)
goto err;
port = atoi(internal_port);
if (port < 1 || port > 65535)
goto err;
if (!strcmp(result, "SUCCESS"))
success = 1;
else if (!strcmp(result, "FAIL"))
success = 0;
else
goto err;
if (!success) {
log_warn(LD_GENERAL, "Tor was unable to forward TCP port '%s' to '%s'%s. "
"Please make sure that your router supports port "
"forwarding protocols (like NAT-PMP). Note that if '%s' is "
"your ORPort, your relay will be unable to receive inbound "
"traffic.", external_port, internal_port,
message_for_log ? message_for_log : "",
internal_port);
} else {
log_info(LD_GENERAL,
"Tor successfully forwarded TCP port '%s' to '%s'%s.",
external_port, internal_port,
message_for_log ? message_for_log : "");
}
goto done;
err:
log_warn(LD_GENERAL, "tor-fw-helper sent us a string we could not "
"parse (%s).", line);
done:
SMARTLIST_FOREACH(tokens, char *, cp, tor_free(cp));
smartlist_free(tokens);
tor_free(message);
tor_free(message_for_log);
}
/** Read what tor-fw-helper has to say in its stdout and handle it
* appropriately */
static int
handle_fw_helper_output(process_handle_t *process_handle)
{
smartlist_t *fw_helper_output = NULL;
enum stream_status stream_status = 0;
fw_helper_output =
tor_get_lines_from_handle(tor_process_get_stdout_pipe(process_handle),
&stream_status);
if (!fw_helper_output) { /* didn't get any output from tor-fw-helper */
/* if EAGAIN we should retry in the future */
return (stream_status == IO_STREAM_EAGAIN) ? 0 : -1;
}
/* Handle the lines we got: */
SMARTLIST_FOREACH_BEGIN(fw_helper_output, char *, line) {
handle_fw_helper_line(line);
tor_free(line);
} SMARTLIST_FOREACH_END(line);
smartlist_free(fw_helper_output);
return 0;
}
/** Spawn tor-fw-helper and ask it to forward the ports in
* <b>ports_to_forward</b>. <b>ports_to_forward</b> contains strings
* of the form "<external port>:<internal port>", which is the format
* that tor-fw-helper expects. */
void void
tor_check_port_forwarding(const char *filename, int dir_port, int or_port, tor_check_port_forwarding(const char *filename,
smartlist_t *ports_to_forward,
time_t now) time_t now)
{ {
/* When fw-helper succeeds, how long do we wait until running it again */ /* When fw-helper succeeds, how long do we wait until running it again */
@ -4555,32 +4747,51 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
static process_handle_t *child_handle=NULL; static process_handle_t *child_handle=NULL;
static time_t time_to_run_helper = 0; static time_t time_to_run_helper = 0;
int stdout_status, stderr_status, retval; int stderr_status, retval;
const char *argv[10]; int stdout_status = 0;
char s_dirport[6], s_orport[6];
tor_assert(filename); tor_assert(filename);
/* Set up command line for tor-fw-helper */
snprintf(s_dirport, sizeof s_dirport, "%d", dir_port);
snprintf(s_orport, sizeof s_orport, "%d", or_port);
/* TODO: Allow different internal and external ports */
argv[0] = filename;
argv[1] = "--internal-or-port";
argv[2] = s_orport;
argv[3] = "--external-or-port";
argv[4] = s_orport;
argv[5] = "--internal-dir-port";
argv[6] = s_dirport;
argv[7] = "--external-dir-port";
argv[8] = s_dirport;
argv[9] = NULL;
/* Start the child, if it is not already running */ /* Start the child, if it is not already running */
if ((!child_handle || child_handle->status != PROCESS_STATUS_RUNNING) && if ((!child_handle || child_handle->status != PROCESS_STATUS_RUNNING) &&
time_to_run_helper < now) { time_to_run_helper < now) {
int status; /*tor-fw-helper cli looks like this: tor_fw_helper -p :5555 -p 4555:1111 */
const char **argv; /* cli arguments */
int args_n, status;
int argv_index = 0; /* index inside 'argv' */
tor_assert(smartlist_len(ports_to_forward) > 0);
/* check for overflow during 'argv' allocation:
(len(ports_to_forward)*2 + 2)*sizeof(char*) > SIZE_MAX ==
len(ports_to_forward) > (((SIZE_MAX/sizeof(char*)) - 2)/2) */
if ((size_t) smartlist_len(ports_to_forward) >
(((SIZE_MAX/sizeof(char*)) - 2)/2)) {
log_warn(LD_GENERAL,
"Overflow during argv allocation. This shouldn't happen.");
return;
}
/* check for overflow during 'argv_index' increase:
((len(ports_to_forward)*2 + 2) > INT_MAX) ==
len(ports_to_forward) > (INT_MAX - 2)/2 */
if (smartlist_len(ports_to_forward) > (INT_MAX - 2)/2) {
log_warn(LD_GENERAL,
"Overflow during argv_index increase. This shouldn't happen.");
return;
}
/* Calculate number of cli arguments: one for the filename, two
for each smartlist element (one for "-p" and one for the
ports), and one for the final NULL. */
args_n = 1 + 2*smartlist_len(ports_to_forward) + 1;
argv = tor_malloc_zero(sizeof(char*)*args_n);
argv[argv_index++] = filename;
SMARTLIST_FOREACH_BEGIN(ports_to_forward, const char *, port) {
argv[argv_index++] = "-p";
argv[argv_index++] = port;
} SMARTLIST_FOREACH_END(port);
argv[argv_index] = NULL;
/* Assume tor-fw-helper will succeed, start it later*/ /* Assume tor-fw-helper will succeed, start it later*/
time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS; time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS;
@ -4597,6 +4808,8 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
status = tor_spawn_background(filename, argv, NULL, &child_handle); status = tor_spawn_background(filename, argv, NULL, &child_handle);
#endif #endif
tor_free(argv);
if (PROCESS_STATUS_ERROR == status) { if (PROCESS_STATUS_ERROR == status) {
log_warn(LD_GENERAL, "Failed to start port forwarding helper %s", log_warn(LD_GENERAL, "Failed to start port forwarding helper %s",
filename); filename);
@ -4614,16 +4827,17 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
/* Read from stdout/stderr and log result */ /* Read from stdout/stderr and log result */
retval = 0; retval = 0;
#ifdef _WIN32 #ifdef _WIN32
stdout_status = log_from_handle(child_handle->stdout_pipe, LOG_INFO); stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_INFO);
stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_WARN);
/* If we got this far (on Windows), the process started */
retval = 0;
#else #else
stdout_status = log_from_pipe(child_handle->stdout_handle,
LOG_INFO, filename, &retval);
stderr_status = log_from_pipe(child_handle->stderr_handle, stderr_status = log_from_pipe(child_handle->stderr_handle,
LOG_WARN, filename, &retval); LOG_INFO, filename, &retval);
#endif #endif
if (handle_fw_helper_output(child_handle) < 0) {
log_warn(LD_GENERAL, "Failed to handle fw helper output.");
stdout_status = -1;
retval = -1;
}
if (retval) { if (retval) {
/* There was a problem in the child process */ /* There was a problem in the child process */
time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL; time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL;

View File

@ -373,7 +373,8 @@ void write_pidfile(char *filename);
/* Port forwarding */ /* Port forwarding */
void tor_check_port_forwarding(const char *filename, void tor_check_port_forwarding(const char *filename,
int dir_port, int or_port, time_t now); struct smartlist_t *ports_to_forward,
time_t now);
typedef struct process_handle_t process_handle_t; typedef struct process_handle_t process_handle_t;
typedef struct process_environment_t process_environment_t; typedef struct process_environment_t process_environment_t;
@ -464,6 +465,16 @@ HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle);
FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle); FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle);
#endif #endif
#ifdef _WIN32
struct smartlist_t *
tor_get_lines_from_handle(HANDLE *handle,
enum stream_status *stream_status);
#else
struct smartlist_t *
tor_get_lines_from_handle(FILE *handle,
enum stream_status *stream_status);
#endif
int tor_terminate_process(process_handle_t *process_handle); int tor_terminate_process(process_handle_t *process_handle);
void tor_process_handle_destroy(process_handle_t *process_handle, void tor_process_handle_destroy(process_handle_t *process_handle,
int also_terminate_process); int also_terminate_process);

View File

@ -137,6 +137,10 @@ const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr,
struct transport_t; struct transport_t;
int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
const struct transport_t **transport); const struct transport_t **transport);
const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr,
uint16_t port);
int validate_pluggable_transports_config(void);
#endif #endif

View File

@ -7253,6 +7253,43 @@ remove_file_if_very_old(const char *fname, time_t now)
} }
} }
/** Return a smartlist of ports that must be forwarded by
* tor-fw-helper. The smartlist contains the ports in a string format
* that is understandable by tor-fw-helper. */
smartlist_t *
get_list_of_ports_to_forward(void)
{
smartlist_t *ports_to_forward = smartlist_new();
int port = 0;
/** XXX TODO tor-fw-helper does not support forwarding ports to
other hosts than the local one. If the user is binding to a
different IP address, tor-fw-helper won't work. */
port = router_get_advertised_or_port(get_options()); /* Get ORPort */
if (port)
smartlist_add_asprintf(ports_to_forward, "%d:%d", port, port);
port = router_get_advertised_dir_port(get_options(), 0); /* Get DirPort */
if (port)
smartlist_add_asprintf(ports_to_forward, "%d:%d", port, port);
/* Get ports of transport proxies */
{
smartlist_t *transport_ports = get_transport_proxy_ports();
if (transport_ports) {
smartlist_add_all(ports_to_forward, transport_ports);
smartlist_free(transport_ports);
}
}
if (!smartlist_len(ports_to_forward)) {
smartlist_free(ports_to_forward);
ports_to_forward = NULL;
}
return ports_to_forward;
}
/** Helper to implement GETINFO functions about configuration variables (not /** Helper to implement GETINFO functions about configuration variables (not
* their values). Given a "config/names" question, set *<b>answer</b> to a * their values). Given a "config/names" question, set *<b>answer</b> to a
* new string describing the supported configuration variables and their * new string describing the supported configuration variables and their

View File

@ -82,6 +82,8 @@ void save_transport_to_state(const char *transport_name,
const tor_addr_t *addr, uint16_t port); const tor_addr_t *addr, uint16_t port);
char *get_stored_bindaddr_for_server_transport(const char *transport); char *get_stored_bindaddr_for_server_transport(const char *transport);
smartlist_t *get_list_of_ports_to_forward(void);
int getinfo_helper_config(control_connection_t *conn, int getinfo_helper_config(control_connection_t *conn,
const char *question, char **answer, const char *question, char **answer,
const char **errmsg); const char **errmsg);

View File

@ -1547,11 +1547,15 @@ run_scheduled_events(time_t now)
options->PortForwarding && options->PortForwarding &&
is_server) { is_server) {
#define PORT_FORWARDING_CHECK_INTERVAL 5 #define PORT_FORWARDING_CHECK_INTERVAL 5
/* XXXXX this should take a list of ports, not just two! */ smartlist_t *ports_to_forward = get_list_of_ports_to_forward();
if (ports_to_forward) {
tor_check_port_forwarding(options->PortForwardingHelper, tor_check_port_forwarding(options->PortForwardingHelper,
get_primary_dir_port(), ports_to_forward,
get_primary_or_port(),
now); now);
SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp));
smartlist_free(ports_to_forward);
}
time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL; time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
} }

View File

@ -585,15 +585,12 @@ pt_configure_remaining_proxies(void)
mark_my_descriptor_dirty("configured managed proxies"); mark_my_descriptor_dirty("configured managed proxies");
} }
#ifdef _WIN32
/** Attempt to continue configuring managed proxy <b>mp</b>. */ /** Attempt to continue configuring managed proxy <b>mp</b>. */
static void static void
configure_proxy(managed_proxy_t *mp) configure_proxy(managed_proxy_t *mp)
{ {
int pos; smartlist_t *proxy_output = NULL;
char stdout_buf[200]; enum stream_status stream_status = 0;
smartlist_t *lines = NULL;
/* if we haven't launched the proxy yet, do it now */ /* if we haven't launched the proxy yet, do it now */
if (mp->conf_state == PT_PROTO_INFANT) { if (mp->conf_state == PT_PROTO_INFANT) {
@ -607,28 +604,18 @@ configure_proxy(managed_proxy_t *mp)
tor_assert(mp->conf_state != PT_PROTO_INFANT); tor_assert(mp->conf_state != PT_PROTO_INFANT);
tor_assert(mp->process_handle); tor_assert(mp->process_handle);
pos = tor_read_all_handle(tor_process_get_stdout_pipe(mp->process_handle), proxy_output =
stdout_buf, sizeof(stdout_buf) - 1, NULL); tor_get_lines_from_handle(tor_process_get_stdout_pipe(mp->process_handle),
if (pos < 0) { &stream_status);
log_notice(LD_GENERAL, "Failed to read data from managed proxy '%s'.", if (!proxy_output) { /* failed to get input from proxy */
mp->argv[0]); if (stream_status != IO_STREAM_EAGAIN)
mp->conf_state = PT_PROTO_BROKEN; mp->conf_state = PT_PROTO_BROKEN;
goto done; goto done;
} }
if (pos == 0) /* proxy has nothing interesting to say. */
return;
/* End with a null even if there isn't a \r\n at the end */
/* TODO: What if this is a partial line? */
stdout_buf[pos] = '\0';
/* Split up the buffer */
lines = smartlist_new();
tor_split_lines(lines, stdout_buf, pos);
/* Handle lines. */ /* Handle lines. */
SMARTLIST_FOREACH_BEGIN(lines, const char *, line) { SMARTLIST_FOREACH_BEGIN(proxy_output, const char *, line) {
handle_proxy_line(line, mp); handle_proxy_line(line, mp);
if (proxy_configuration_finished(mp)) if (proxy_configuration_finished(mp))
goto done; goto done;
@ -639,58 +626,11 @@ configure_proxy(managed_proxy_t *mp)
if (proxy_configuration_finished(mp)) if (proxy_configuration_finished(mp))
handle_finished_proxy(mp); handle_finished_proxy(mp);
if (lines) if (proxy_output) {
smartlist_free(lines); SMARTLIST_FOREACH(proxy_output, char *, cp, tor_free(cp));
} smartlist_free(proxy_output);
#else /* _WIN32 */
/** Attempt to continue configuring managed proxy <b>mp</b>. */
static void
configure_proxy(managed_proxy_t *mp)
{
enum stream_status r;
char stdout_buf[200];
/* if we haven't launched the proxy yet, do it now */
if (mp->conf_state == PT_PROTO_INFANT) {
if (launch_managed_proxy(mp) < 0) { /* launch fail */
mp->conf_state = PT_PROTO_FAILED_LAUNCH;
handle_finished_proxy(mp);
}
return;
}
tor_assert(mp->conf_state != PT_PROTO_INFANT);
tor_assert(mp->process_handle);
while (1) {
r = get_string_from_pipe(tor_process_get_stdout_pipe(mp->process_handle),
stdout_buf, sizeof(stdout_buf) - 1);
if (r == IO_STREAM_OKAY) { /* got a line; handle it! */
handle_proxy_line((const char *)stdout_buf, mp);
} else if (r == IO_STREAM_EAGAIN) { /* check back later */
return;
} else if (r == IO_STREAM_CLOSED || r == IO_STREAM_TERM) { /* snap! */
log_warn(LD_GENERAL, "Our communication channel with the managed proxy "
"'%s' closed. Most probably application stopped running.",
mp->argv[0]);
mp->conf_state = PT_PROTO_BROKEN;
} else { /* unknown stream status */
log_warn(LD_BUG, "Unknown stream status '%d' while configuring managed "
"proxy '%s'.", (int)r, mp->argv[0]);
}
/* if the proxy finished configuring, exit the loop. */
if (proxy_configuration_finished(mp)) {
handle_finished_proxy(mp);
return;
} }
} }
}
#endif /* _WIN32 */
/** Register server managed proxy <b>mp</b> transports to state */ /** Register server managed proxy <b>mp</b> transports to state */
static void static void
@ -1384,6 +1324,33 @@ pt_prepare_proxy_list_for_config_read(void)
tor_assert(unconfigured_proxies_n == 0); tor_assert(unconfigured_proxies_n == 0);
} }
/** Return a smartlist containing the ports where our pluggable
* transports are listening. */
smartlist_t *
get_transport_proxy_ports(void)
{
smartlist_t *sl = NULL;
if (!managed_proxy_list)
return NULL;
/** XXX assume that external proxy ports have been forwarded
manually */
SMARTLIST_FOREACH_BEGIN(managed_proxy_list, const managed_proxy_t *, mp) {
if (!mp->is_server || mp->conf_state != PT_PROTO_COMPLETED)
continue;
if (!sl) sl = smartlist_new();
tor_assert(mp->transports);
SMARTLIST_FOREACH(mp->transports, const transport_t *, t,
smartlist_add_asprintf(sl, "%u:%u", t->port, t->port));
} SMARTLIST_FOREACH_END(mp);
return sl;
}
/** Return the pluggable transport string that we should display in /** Return the pluggable transport string that we should display in
* our extra-info descriptor. If we shouldn't display such a string, * our extra-info descriptor. If we shouldn't display such a string,
* or we have nothing to display, return NULL. The string is * or we have nothing to display, return NULL. The string is

View File

@ -54,6 +54,8 @@ void pt_free_all(void);
void pt_prepare_proxy_list_for_config_read(void); void pt_prepare_proxy_list_for_config_read(void);
void sweep_proxy_list(void); void sweep_proxy_list(void);
smartlist_t *get_transport_proxy_ports(void);
#ifdef PT_PRIVATE #ifdef PT_PRIVATE
/** State of the managed proxy configuration protocol. */ /** State of the managed proxy configuration protocol. */
enum pt_proto_state { enum pt_proto_state {

View File

@ -60,15 +60,15 @@ tor_natpmp_init(tor_fw_options_t *tor_fw_options, void *backend_state)
state->lease = NATPMP_DEFAULT_LEASE; state->lease = NATPMP_DEFAULT_LEASE;
if (tor_fw_options->verbose) if (tor_fw_options->verbose)
fprintf(stdout, "V: natpmp init...\n"); fprintf(stderr, "V: natpmp init...\n");
r = initnatpmp(&(state->natpmp), 0, 0); r = initnatpmp(&(state->natpmp), 0, 0);
if (r == 0) { if (r == 0) {
state->init = 1; state->init = 1;
fprintf(stdout, "tor-fw-helper: natpmp initialized...\n"); fprintf(stderr, "V: natpmp initialized...\n");
return r; return r;
} else { } else {
fprintf(stderr, "tor-fw-helper: natpmp failed to initialize...\n"); fprintf(stderr, "V: natpmp failed to initialize...\n");
return r; return r;
} }
} }
@ -80,10 +80,10 @@ tor_natpmp_cleanup(tor_fw_options_t *tor_fw_options, void *backend_state)
natpmp_state_t *state = (natpmp_state_t *) backend_state; natpmp_state_t *state = (natpmp_state_t *) backend_state;
int r = 0; int r = 0;
if (tor_fw_options->verbose) if (tor_fw_options->verbose)
fprintf(stdout, "V: natpmp cleanup...\n"); fprintf(stderr, "V: natpmp cleanup...\n");
r = closenatpmp(&(state->natpmp)); r = closenatpmp(&(state->natpmp));
if (tor_fw_options->verbose) if (tor_fw_options->verbose)
fprintf(stdout, "V: closing natpmp socket: %d\n", r); fprintf(stderr, "V: closing natpmp socket: %d\n", r);
return r; return r;
} }
@ -101,7 +101,7 @@ wait_until_fd_readable(tor_socket_t fd, struct timeval *timeout)
FD_SET(fd, &fds); FD_SET(fd, &fds);
r = select(fd+1, &fds, NULL, NULL, timeout); r = select(fd+1, &fds, NULL, NULL, timeout);
if (r == -1) { if (r == -1) {
fprintf(stdout, "V: select failed in wait_until_fd_readable: %s\n", fprintf(stderr, "V: select failed in wait_until_fd_readable: %s\n",
strerror(errno)); strerror(errno));
return -1; return -1;
} }
@ -110,27 +110,25 @@ wait_until_fd_readable(tor_socket_t fd, struct timeval *timeout)
return 0; 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 int
tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options, tor_natpmp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port,
void *backend_state) int is_verbose, void *backend_state)
{ {
natpmp_state_t *state = (natpmp_state_t *) backend_state;
int r = 0; int r = 0;
int x = 0; int x = 0;
int sav_errno; int sav_errno;
natpmp_state_t *state = (natpmp_state_t *) backend_state;
struct timeval timeout; struct timeval timeout;
if (tor_fw_options->verbose) if (is_verbose)
fprintf(stdout, "V: sending natpmp portmapping request...\n"); fprintf(stderr, "V: sending natpmp portmapping request...\n");
r = sendnewportmappingrequest(&(state->natpmp), state->protocol, r = sendnewportmappingrequest(&(state->natpmp), state->protocol,
tor_fw_options->internal_port, internal_port,
tor_fw_options->external_port, external_port,
state->lease); state->lease);
if (tor_fw_options->verbose) if (is_verbose)
fprintf(stdout, "tor-fw-helper: NAT-PMP sendnewportmappingrequest " fprintf(stderr, "tor-fw-helper: NAT-PMP sendnewportmappingrequest "
"returned %d (%s)\n", r, r==12?"SUCCESS":"FAILED"); "returned %d (%s)\n", r, r==12?"SUCCESS":"FAILED");
do { do {
@ -139,8 +137,8 @@ tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options,
if (x == -1) if (x == -1)
return -1; return -1;
if (tor_fw_options->verbose) if (is_verbose)
fprintf(stdout, "V: attempting to readnatpmpreponseorretry...\n"); fprintf(stderr, "V: attempting to readnatpmpreponseorretry...\n");
r = readnatpmpresponseorretry(&(state->natpmp), &(state->response)); r = readnatpmpresponseorretry(&(state->natpmp), &(state->response));
sav_errno = errno; sav_errno = errno;
@ -163,16 +161,14 @@ tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options,
} }
if (r == NATPMP_SUCCESS) { if (r == NATPMP_SUCCESS) {
fprintf(stdout, "tor-fw-helper: NAT-PMP mapped public port %hu to" fprintf(stderr, "tor-fw-helper: NAT-PMP mapped public port %hu to"
" localport %hu liftime %u\n", " localport %hu liftime %u\n",
(state->response).pnu.newportmapping.mappedpublicport, (state->response).pnu.newportmapping.mappedpublicport,
(state->response).pnu.newportmapping.privateport, (state->response).pnu.newportmapping.privateport,
(state->response).pnu.newportmapping.lifetime); (state->response).pnu.newportmapping.lifetime);
} }
tor_fw_options->nat_pmp_status = 1; return (r == NATPMP_SUCCESS) ? 0 : -1;
return r;
} }
/** Fetch our likely public IP from our upstream NAT-PMP enabled NAT device. /** Fetch our likely public IP from our upstream NAT-PMP enabled NAT device.
@ -189,7 +185,7 @@ tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options,
struct timeval timeout; struct timeval timeout;
r = sendpublicaddressrequest(&(state->natpmp)); r = sendpublicaddressrequest(&(state->natpmp));
fprintf(stdout, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned" fprintf(stderr, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned"
" %d (%s)\n", r, r==2?"SUCCESS":"FAILED"); " %d (%s)\n", r, r==2?"SUCCESS":"FAILED");
do { do {
@ -200,12 +196,12 @@ tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options,
return -1; return -1;
if (tor_fw_options->verbose) if (tor_fw_options->verbose)
fprintf(stdout, "V: NAT-PMP attempting to read reponse...\n"); fprintf(stderr, "V: NAT-PMP attempting to read reponse...\n");
r = readnatpmpresponseorretry(&(state->natpmp), &(state->response)); r = readnatpmpresponseorretry(&(state->natpmp), &(state->response));
sav_errno = errno; sav_errno = errno;
if (tor_fw_options->verbose) if (tor_fw_options->verbose)
fprintf(stdout, "V: NAT-PMP readnatpmpresponseorretry returned" fprintf(stderr, "V: NAT-PMP readnatpmpresponseorretry returned"
" %d\n", r); " %d\n", r);
if ( r < 0 && r != NATPMP_TRYAGAIN) { if ( r < 0 && r != NATPMP_TRYAGAIN) {
@ -223,15 +219,15 @@ tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options,
return r; return r;
} }
fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n", fprintf(stderr, "tor-fw-helper: ExternalIPAddress = %s\n",
inet_ntoa((state->response).pnu.publicaddress.addr)); inet_ntoa((state->response).pnu.publicaddress.addr));
tor_fw_options->public_ip_status = 1; tor_fw_options->public_ip_status = 1;
if (tor_fw_options->verbose) { if (tor_fw_options->verbose) {
fprintf(stdout, "V: result = %u\n", r); fprintf(stderr, "V: result = %u\n", r);
fprintf(stdout, "V: type = %u\n", (state->response).type); fprintf(stderr, "V: type = %u\n", (state->response).type);
fprintf(stdout, "V: resultcode = %u\n", (state->response).resultcode); fprintf(stderr, "V: resultcode = %u\n", (state->response).resultcode);
fprintf(stdout, "V: epoch = %u\n", (state->response).epoch); fprintf(stderr, "V: epoch = %u\n", (state->response).epoch);
} }
return r; return r;

View File

@ -36,8 +36,8 @@ 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_cleanup(tor_fw_options_t *tor_fw_options, void *backend_state);
int tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options, int tor_natpmp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port,
void *backend_state); int is_verbose, void *backend_state);
int tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, int tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options,
void *backend_state); void *backend_state);

View File

@ -91,7 +91,7 @@ tor_upnp_init(tor_fw_options_t *options, void *backend_state)
assert(options); assert(options);
r = UPNP_GetValidIGD(devlist, &(state->urls), &(state->data), r = UPNP_GetValidIGD(devlist, &(state->urls), &(state->data),
state->lanaddr, UPNP_LANADDR_SZ); state->lanaddr, UPNP_LANADDR_SZ);
fprintf(stdout, "tor-fw-helper: UPnP GetValidIGD returned: %d (%s)\n", r, fprintf(stderr, "tor-fw-helper: UPnP GetValidIGD returned: %d (%s)\n", r,
r==UPNP_SUCCESS?"SUCCESS":"FAILED"); r==UPNP_SUCCESS?"SUCCESS":"FAILED");
freeUPNPDevlist(devlist); freeUPNPDevlist(devlist);
@ -141,7 +141,7 @@ tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state)
goto err; goto err;
if (externalIPAddress[0]) { if (externalIPAddress[0]) {
fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n", fprintf(stderr, "tor-fw-helper: ExternalIPAddress = %s\n",
externalIPAddress); tor_upnp_cleanup(options, state); externalIPAddress); tor_upnp_cleanup(options, state);
options->public_ip_status = 1; options->public_ip_status = 1;
return UPNP_ERR_SUCCESS; return UPNP_ERR_SUCCESS;
@ -154,32 +154,30 @@ tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state)
return UPNP_ERR_GETEXTERNALIP; 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 int
tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state) tor_upnp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port,
int is_verbose, void *backend_state)
{ {
miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state; int retval;
int r;
char internal_port_str[6]; char internal_port_str[6];
char external_port_str[6]; char external_port_str[6];
miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
if (!state->init) { if (!state->init) {
r = tor_upnp_init(options, state); fprintf(stderr, "E: %s but state is not initialized.\n", __func__);
if (r != UPNP_ERR_SUCCESS) return -1;
return r;
} }
if (options->verbose) if (is_verbose)
fprintf(stdout, "V: internal port: %d, external port: %d\n", fprintf(stderr, "V: UPnP: internal port: %u, external port: %u\n",
(int)options->internal_port, (int)options->external_port); internal_port, external_port);
tor_snprintf(internal_port_str, sizeof(internal_port_str), tor_snprintf(internal_port_str, sizeof(internal_port_str),
"%d", (int)options->internal_port); "%u", internal_port);
tor_snprintf(external_port_str, sizeof(external_port_str), tor_snprintf(external_port_str, sizeof(external_port_str),
"%d", (int)options->external_port); "%u", external_port);
r = UPNP_AddPortMapping(state->urls.controlURL, retval = UPNP_AddPortMapping(state->urls.controlURL,
state->data.first.servicetype, state->data.first.servicetype,
external_port_str, internal_port_str, external_port_str, internal_port_str,
#ifdef MINIUPNPC15 #ifdef MINIUPNPC15
@ -187,11 +185,9 @@ tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state)
#else #else
state->lanaddr, UPNP_DESC, "TCP", 0, 0); state->lanaddr, UPNP_DESC, "TCP", 0, 0);
#endif #endif
if (r != UPNPCOMMAND_SUCCESS)
return UPNP_ERR_ADDPORTMAPPING;
options->upnp_status = 1; return (retval == UPNP_ERR_SUCCESS) ? 0 : -1;
return UPNP_ERR_SUCCESS;
} }
#endif #endif

View File

@ -36,7 +36,8 @@ 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_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); int tor_upnp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port,
int is_verbose, void *backend_state);
#endif #endif
#endif #endif

View File

@ -20,6 +20,9 @@
#include <getopt.h> #include <getopt.h>
#include <time.h> #include <time.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include "container.h"
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
@ -45,7 +48,7 @@ typedef struct backends_t {
void *backend_state[MAX_BACKENDS]; void *backend_state[MAX_BACKENDS];
} backends_t; } backends_t;
/** Initalize each backend helper with the user input stored in <b>options</b> /** Initialize each backend helper with the user input stored in <b>options</b>
* and put the results in the <b>backends</b> struct. */ * and put the results in the <b>backends</b> struct. */
static int static int
init_backends(tor_fw_options_t *options, backends_t *backends) init_backends(tor_fw_options_t *options, backends_t *backends)
@ -97,10 +100,7 @@ usage(void)
" [-T|--Test]\n" " [-T|--Test]\n"
" [-v|--verbose]\n" " [-v|--verbose]\n"
" [-g|--fetch-public-ip]\n" " [-g|--fetch-public-ip]\n"
" -i|--internal-or-port [TCP port]\n" " [-p|--forward-port ([<external port>]:<internal 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 /** Log commandline options to a hardcoded file <b>tor-fw-helper.log</b> in the
@ -125,7 +125,7 @@ log_commandline_options(int argc, char **argv)
if (retval < 0) if (retval < 0)
goto error; goto error;
retval = fprintf(stdout, "ARG: %d: %s\n", i, argv[i]); retval = fprintf(stderr, "ARG: %d: %s\n", i, argv[i]);
if (retval < 0) if (retval < 0)
goto error; goto error;
} }
@ -152,82 +152,141 @@ tor_fw_fetch_public_ip(tor_fw_options_t *tor_fw_options,
int r = 0; int r = 0;
if (tor_fw_options->verbose) if (tor_fw_options->verbose)
fprintf(stdout, "V: tor_fw_fetch_public_ip\n"); fprintf(stderr, "V: tor_fw_fetch_public_ip\n");
for (i=0; i<backends->n_backends; ++i) { for (i=0; i<backends->n_backends; ++i) {
if (tor_fw_options->verbose) { if (tor_fw_options->verbose) {
fprintf(stdout, "V: running backend_state now: %i\n", i); fprintf(stderr, "V: running backend_state now: %i\n", i);
fprintf(stdout, "V: size of backend state: %u\n", fprintf(stderr, "V: size of backend state: %u\n",
(int)(backends->backend_ops)[i].state_len); (int)(backends->backend_ops)[i].state_len);
fprintf(stdout, "V: backend state name: %s\n", fprintf(stderr, "V: backend state name: %s\n",
(char *)(backends->backend_ops)[i].name); (char *)(backends->backend_ops)[i].name);
} }
r = backends->backend_ops[i].fetch_public_ip(tor_fw_options, r = backends->backend_ops[i].fetch_public_ip(tor_fw_options,
backends->backend_state[i]); backends->backend_state[i]);
fprintf(stdout, "tor-fw-helper: tor_fw_fetch_public_ip backend %s " fprintf(stderr, "tor-fw-helper: tor_fw_fetch_public_ip backend %s "
" returned: %i\n", (char *)(backends->backend_ops)[i].name, r); " returned: %i\n", (char *)(backends->backend_ops)[i].name, r);
} }
} }
/** Iterate over each of the supported <b>backends</b> and attempt to add a /** Print a spec-conformant string to stdout describing the results of
* port forward for the OR port stored in <b>tor_fw_options</b>. */ * the TCP port forwarding operation from <b>external_port</b> to
* <b>internal_port</b>. */
static void static void
tor_fw_add_or_port(tor_fw_options_t *tor_fw_options, tor_fw_helper_report_port_fw_results(uint16_t internal_port,
uint16_t external_port,
int succeded,
const char *message)
{
char *report_string = NULL;
tor_asprintf(&report_string, "%s %s %u %u %s %s\n",
"tor-fw-helper",
"tcp-forward",
external_port, internal_port,
succeded ? "SUCCESS" : "FAIL",
message);
fprintf(stdout, "%s", report_string);
fflush(stdout);
tor_free(report_string);
}
#define tor_fw_helper_report_port_fw_fail(i, e, m) \
tor_fw_helper_report_port_fw_results((i), (e), 0, (m))
#define tor_fw_helper_report_port_fw_success(i, e, m) \
tor_fw_helper_report_port_fw_results((i), (e), 1, (m))
/** Return a heap-allocated string containing the list of our
* backends. It can be used in log messages. Be sure to free it
* afterwards! */
static char *
get_list_of_backends_string(backends_t *backends)
{
char *backend_names = NULL;
int i;
smartlist_t *backend_names_sl = smartlist_new();
assert(backends->n_backends);
for (i=0; i<backends->n_backends; ++i)
smartlist_add(backend_names_sl, (char *) backends->backend_ops[i].name);
backend_names = smartlist_join_strings(backend_names_sl, ", ", 0, NULL);
smartlist_free(backend_names_sl);
return backend_names;
}
/** Iterate over each of the supported <b>backends</b> and attempt to add a
* port forward for the port stored in <b>tor_fw_options</b>. */
static void
tor_fw_add_ports(tor_fw_options_t *tor_fw_options,
backends_t *backends) backends_t *backends)
{ {
int i; int i;
int r = 0; int r = 0;
int succeeded = 0;
if (tor_fw_options->verbose) if (tor_fw_options->verbose)
fprintf(stdout, "V: tor_fw_add_or_port\n"); fprintf(stderr, "V: %s\n", __func__);
/** Loop all ports that need to be forwarded, and try to use our
* backends for each port. If a backend succeeds, break the loop,
* report success and get to the next port. If all backends fail,
* report failure for that port. */
SMARTLIST_FOREACH_BEGIN(tor_fw_options->ports_to_forward,
port_to_forward_t *, port_to_forward) {
succeeded = 0;
for (i=0; i<backends->n_backends; ++i) { for (i=0; i<backends->n_backends; ++i) {
if (tor_fw_options->verbose) { if (tor_fw_options->verbose) {
fprintf(stdout, "V: running backend_state now: %i\n", i); fprintf(stderr, "V: running backend_state now: %i\n", i);
fprintf(stdout, "V: size of backend state: %u\n", fprintf(stderr, "V: size of backend state: %u\n",
(int)(backends->backend_ops)[i].state_len); (int)(backends->backend_ops)[i].state_len);
fprintf(stdout, "V: backend state name: %s\n", fprintf(stderr, "V: backend state name: %s\n",
(const char *) backends->backend_ops[i].name); (const char *) backends->backend_ops[i].name);
} }
r = backends->backend_ops[i].add_tcp_mapping(tor_fw_options,
r =
backends->backend_ops[i].add_tcp_mapping(port_to_forward->internal_port,
port_to_forward->external_port,
tor_fw_options->verbose,
backends->backend_state[i]); backends->backend_state[i]);
fprintf(stdout, "tor-fw-helper: tor_fw_add_or_port backend %s " if (r == 0) { /* backend success */
"returned: %i\n", (const char *) backends->backend_ops[i].name, r); tor_fw_helper_report_port_fw_success(port_to_forward->internal_port,
} port_to_forward->external_port,
backends->backend_ops[i].name);
succeeded = 1;
break;
} }
/** Iterate over each of the supported <b>backends</b> and attempt to add a fprintf(stderr, "tor-fw-helper: tor_fw_add_port backend %s "
* port forward for the Dir port stored in <b>tor_fw_options</b>. */ "returned: %i\n",
static void (const char *) backends->backend_ops[i].name, r);
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]); if (!succeeded) { /* all backends failed */
fprintf(stdout, "tor-fw-helper: tor_fw_add_dir_port backend %s " char *list_of_backends_str = get_list_of_backends_string(backends);
"returned: %i\n", (const char *)backends->backend_ops[i].name, r); char *fail_msg = NULL;
tor_asprintf(&fail_msg, "All port forwarding backends (%s) failed.",
list_of_backends_str);
tor_fw_helper_report_port_fw_fail(port_to_forward->internal_port,
port_to_forward->external_port,
fail_msg);
tor_free(list_of_backends_str);
tor_free(fail_msg);
} }
} SMARTLIST_FOREACH_END(port_to_forward);
} }
/** Called before we make any calls to network-related functions. /** Called before we make any calls to network-related functions.
* (Some operating systems require their network libraries to be * (Some operating systems require their network libraries to be
* initialized.) (from common/compat.c) */ * initialized.) (from common/compat.c) */
static int static int
network_init(void) tor_fw_helper_network_init(void)
{ {
#ifdef _WIN32 #ifdef _WIN32
/* This silly exercise is necessary before windows will allow /* This silly exercise is necessary before windows will allow
@ -247,6 +306,67 @@ network_init(void)
return 0; return 0;
} }
/** Parse the '-p' argument of tor-fw-helper. Its format is
* [<external port>]:<internal port>, and <external port> is optional.
* Return NULL if <b>arg</b> was c0rrupted. */
static port_to_forward_t *
parse_port(const char *arg)
{
smartlist_t *sl = smartlist_new();
port_to_forward_t *port_to_forward = NULL;
char *port_str = NULL;
int ok;
int port;
smartlist_split_string(sl, arg, ":", 0, 0);
if (smartlist_len(sl) != 2)
goto err;
port_to_forward = tor_malloc(sizeof(port_to_forward_t));
if (!port_to_forward)
goto err;
port_str = smartlist_get(sl, 0); /* macroify ? */
port = (int)tor_parse_long(port_str, 10, 1, 65535, &ok, NULL);
if (!ok && strlen(port_str)) /* ":1555" is valid */
goto err;
port_to_forward->external_port = port;
port_str = smartlist_get(sl, 1);
port = (int)tor_parse_long(port_str, 10, 1, 65535, &ok, NULL);
if (!ok)
goto err;
port_to_forward->internal_port = port;
goto done;
err:
tor_free(port_to_forward);
done:
SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
smartlist_free(sl);
return port_to_forward;
}
/** Report a failure of epic proportions: We didn't manage to
* initialize any port forwarding backends. */
static void
report_full_fail(const smartlist_t *ports_to_forward)
{
if (!ports_to_forward)
return;
SMARTLIST_FOREACH_BEGIN(ports_to_forward,
const port_to_forward_t *, port_to_forward) {
tor_fw_helper_report_port_fw_fail(port_to_forward->internal_port,
port_to_forward->external_port,
"All backends (NAT-PMP, UPnP) failed "
"to initialize!"); /* XXX hardcoded */
} SMARTLIST_FOREACH_END(port_to_forward);
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -259,22 +379,20 @@ main(int argc, char **argv)
memset(&tor_fw_options, 0, sizeof(tor_fw_options)); memset(&tor_fw_options, 0, sizeof(tor_fw_options));
memset(&backend_state, 0, sizeof(backend_state)); memset(&backend_state, 0, sizeof(backend_state));
// Parse CLI arguments.
while (1) { while (1) {
int option_index = 0; int option_index = 0;
static struct option long_options[] = static struct option long_options[] =
{ {
{"verbose", 0, 0, 'v'}, {"verbose", 0, 0, 'v'},
{"help", 0, 0, 'h'}, {"help", 0, 0, 'h'},
{"internal-or-port", 1, 0, 'i'}, {"port", 1, 0, 'p'},
{"external-or-port", 1, 0, 'e'},
{"internal-dir-port", 1, 0, 'd'},
{"external-dir-port", 1, 0, 'p'},
{"fetch-public-ip", 0, 0, 'g'}, {"fetch-public-ip", 0, 0, 'g'},
{"test-commandline", 0, 0, 'T'}, {"test-commandline", 0, 0, 'T'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
c = getopt_long(argc, argv, "vhi:e:d:p:gT", c = getopt_long(argc, argv, "vhp:gT",
long_options, &option_index); long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
@ -282,14 +400,31 @@ main(int argc, char **argv)
switch (c) { switch (c) {
case 'v': tor_fw_options.verbose = 1; break; case 'v': tor_fw_options.verbose = 1; break;
case 'h': tor_fw_options.help = 1; usage(); exit(1); break; case 'h': tor_fw_options.help = 1; usage(); exit(1); break;
case 'i': sscanf(optarg, "%hu", &tor_fw_options.private_or_port); case 'p': {
break; port_to_forward_t *port_to_forward = parse_port(optarg);
case 'e': sscanf(optarg, "%hu", &tor_fw_options.public_or_port); if (!port_to_forward) {
break; fprintf(stderr, "E: Failed to parse '%s'.\n", optarg);
case 'd': sscanf(optarg, "%hu", &tor_fw_options.private_dir_port); usage();
break; exit(1);
case 'p': sscanf(optarg, "%hu", &tor_fw_options.public_dir_port); }
/* If no external port was given (it's optional), set it to be
* equal with the internal port. */
if (!port_to_forward->external_port) {
assert(port_to_forward->internal_port);
if (tor_fw_options.verbose)
fprintf(stderr, "V: No external port was given. Setting to %u.\n",
port_to_forward->internal_port);
port_to_forward->external_port = port_to_forward->internal_port;
}
if (!tor_fw_options.ports_to_forward)
tor_fw_options.ports_to_forward = smartlist_new();
smartlist_add(tor_fw_options.ports_to_forward, port_to_forward);
break; break;
}
case 'g': tor_fw_options.fetch_public_ip = 1; break; case 'g': tor_fw_options.fetch_public_ip = 1; break;
case 'T': tor_fw_options.test_commandline = 1; break; case 'T': tor_fw_options.test_commandline = 1; break;
case '?': break; case '?': break;
@ -297,98 +432,68 @@ main(int argc, char **argv)
} }
} }
if (tor_fw_options.verbose) { { // Verbose output
if (tor_fw_options.verbose)
fprintf(stderr, "V: tor-fw-helper version %s\n" fprintf(stderr, "V: tor-fw-helper version %s\n"
"V: We were called with the following arguments:\n" "V: We were called with the following arguments:\n"
"V: verbose = %d, help = %d, pub or port = %u, " "V: verbose = %d, help = %d, fetch_public_ip = %u\n",
"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_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); tor_fw_options.fetch_public_ip);
if (tor_fw_options.verbose && tor_fw_options.ports_to_forward) {
fprintf(stderr, "V: TCP forwarding:\n");
SMARTLIST_FOREACH(tor_fw_options.ports_to_forward,
const port_to_forward_t *, port_to_forward,
fprintf(stderr, "V: External: %u, Internal: %u\n",
port_to_forward->external_port,
port_to_forward->internal_port));
}
} }
if (tor_fw_options.test_commandline) { if (tor_fw_options.test_commandline) {
return log_commandline_options(argc, argv); return log_commandline_options(argc, argv);
} }
/* At the very least, we require an ORPort; // See if the user actually wants us to do something.
Given a private ORPort, we can ask for a mapping that matches the port if (!tor_fw_options.fetch_public_ip && !tor_fw_options.ports_to_forward) {
externally. fprintf(stderr, "E: We require a port to be forwarded or "
*/ "fetch_public_ip request!\n");
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(); usage();
exit(1); 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 // Initialize networking
if (network_init()) if (tor_fw_helper_network_init())
exit(1); exit(1);
// Initalize the various fw-helper backend helpers // Initalize the various fw-helper backend helpers
r = init_backends(&tor_fw_options, &backend_state); r = init_backends(&tor_fw_options, &backend_state);
if (r) if (!r) { // all backends failed:
printf("tor-fw-helper: %i NAT traversal helper(s) loaded\n", r); // report our failure
report_full_fail(tor_fw_options.ports_to_forward);
fprintf(stderr, "tor-fw-helper: All backends failed.\n");
exit(1);
} else { // some backends succeeded:
fprintf(stderr, "tor-fw-helper: %i NAT traversal helper(s) loaded\n", r);
}
// Forward TCP ports.
if (tor_fw_options.ports_to_forward) {
tor_fw_add_ports(&tor_fw_options, &backend_state);
}
// Fetch our public IP.
if (tor_fw_options.fetch_public_ip) { if (tor_fw_options.fetch_public_ip) {
tor_fw_fetch_public_ip(&tor_fw_options, &backend_state); tor_fw_fetch_public_ip(&tor_fw_options, &backend_state);
} }
if (tor_fw_options.private_or_port) { // Cleanup and exit.
tor_fw_options.internal_port = tor_fw_options.private_or_port; if (tor_fw_options.ports_to_forward) {
tor_fw_options.external_port = tor_fw_options.private_or_port; SMARTLIST_FOREACH(tor_fw_options.ports_to_forward,
tor_fw_add_or_port(&tor_fw_options, &backend_state); port_to_forward_t *, port,
} tor_free(port));
smartlist_free(tor_fw_options.ports_to_forward);
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); exit(r);

View File

@ -17,24 +17,26 @@
#include <time.h> #include <time.h>
/** The current version of tor-fw-helper. */ /** The current version of tor-fw-helper. */
#define tor_fw_version "0.1" #define tor_fw_version "0.2"
/** This is an arbitrary hard limit - We currently have two (NAT-PMP and UPnP). /** 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 We're likely going to add the Intel UPnP library but nothing else comes to
mind at the moment. */ mind at the moment. */
#define MAX_BACKENDS 23 #define MAX_BACKENDS 23
/** Forward traffic received in port <b>external_port</b> in the
* external side of our NAT to <b>internal_port</b> in this host. */
typedef struct {
uint16_t external_port;
uint16_t internal_port;
} port_to_forward_t;
/** This is where we store parsed commandline options. */ /** This is where we store parsed commandline options. */
typedef struct { typedef struct {
int verbose; int verbose;
int help; int help;
int test_commandline; int test_commandline;
uint16_t private_dir_port; struct smartlist_t *ports_to_forward;
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 fetch_public_ip;
int nat_pmp_status; int nat_pmp_status;
int upnp_status; int upnp_status;
@ -50,8 +52,8 @@ typedef struct tor_fw_backend_t {
int (*init)(tor_fw_options_t *options, void *backend_state); int (*init)(tor_fw_options_t *options, void *backend_state);
int (*cleanup)(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 (*fetch_public_ip)(tor_fw_options_t *options, void *backend_state);
int (*add_tcp_mapping)(tor_fw_options_t *options, void *backend_state); int (*add_tcp_mapping)(uint16_t internal_port, uint16_t external_port,
int is_verbose, void *backend_state);
} tor_fw_backend_t; } tor_fw_backend_t;
#endif #endif