Browse Source

Merge remote-tracking branch 'asn/bug4567_rebased'

Nick Mathewson 13 years ago
parent
commit
e9684405ac

+ 3 - 0
changes/bug4567

@@ -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 - 0
changes/bug4567_2

@@ -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.

+ 2 - 12
doc/tor-fw-helper.1.txt

@@ -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__::
-    Inform **tor-fw-helper** of your internal OR port. This is the only
-    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.
+**-p** or **--forward-port** __external_port__:__internal_port__::
+    Forward external_port to internal_port.
 
 
 BUGS
 BUGS
 ----
 ----

+ 243 - 29
src/common/util.c

@@ -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;
-  const char *argv[10];
-  char s_dirport[6], s_orport[6];
+  int stderr_status, retval;
+  int stdout_status = 0;
 
 
   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_WARN);
-    /* If we got this far (on Windows), the process started */
-    retval = 0;
+    stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_INFO);
 #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;

+ 12 - 1
src/common/util.h

@@ -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);

+ 4 - 0
src/or/circuitbuild.h

@@ -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
 
 

+ 37 - 0
src/or/config.c

@@ -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

+ 2 - 0
src/or/config.h

@@ -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);

+ 9 - 5
src/or/main.c

@@ -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! */
-    tor_check_port_forwarding(options->PortForwardingHelper,
-                              get_primary_dir_port(),
-                              get_primary_or_port(),
-                              now);
+    smartlist_t *ports_to_forward = get_list_of_ports_to_forward();
+    if (ports_to_forward) {
+      tor_check_port_forwarding(options->PortForwardingHelper,
+                                ports_to_forward,
+                                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;
   }
   }
 
 

+ 40 - 73
src/or/transports.c

@@ -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;
-  char stdout_buf[200];
-  smartlist_t *lines = NULL;
+  smartlist_t *proxy_output = NULL;
+  enum stream_status stream_status = 0;
 
 
   /* 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),
-                            stdout_buf, sizeof(stdout_buf) - 1, NULL);
-  if (pos < 0) {
-    log_notice(LD_GENERAL, "Failed to read data from managed proxy '%s'.",
-               mp->argv[0]);
-    mp->conf_state = PT_PROTO_BROKEN;
+  proxy_output =
+    tor_get_lines_from_handle(tor_process_get_stdout_pipe(mp->process_handle),
+                              &stream_status);
+  if (!proxy_output) { /* failed to get input from proxy */
+    if (stream_status != IO_STREAM_EAGAIN)
+      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,59 +626,12 @@ 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)
-    smartlist_free(lines);
-}
-
-#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;
-    }
+  if (proxy_output) {
+    SMARTLIST_FOREACH(proxy_output, char *, cp, tor_free(cp));
+    smartlist_free(proxy_output);
   }
   }
 }
 }
 
 
-#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
 register_server_proxy(const managed_proxy_t *mp)
 register_server_proxy(const managed_proxy_t *mp)
@@ -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

+ 2 - 0
src/or/transports.h

@@ -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 {

+ 27 - 31
src/tools/tor-fw-helper/tor-fw-helper-natpmp.c

@@ -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,
-                           void *backend_state)
+tor_natpmp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port,
+                           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)
-    fprintf(stdout, "V: sending natpmp portmapping request...\n");
+  if (is_verbose)
+    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,
-                                tor_fw_options->external_port,
+                                internal_port,
+                                external_port,
                                 state->lease);
                                 state->lease);
-  if (tor_fw_options->verbose)
-    fprintf(stdout, "tor-fw-helper: NAT-PMP sendnewportmappingrequest "
+  if (is_verbose)
+    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)
-      fprintf(stdout, "V: attempting to readnatpmpreponseorretry...\n");
+    if (is_verbose)
+      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;
+  return (r == NATPMP_SUCCESS) ? 0 : -1;
 }
 }
 
 
 /** 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(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);
+    fprintf(stderr, "V: result = %u\n", r);
+    fprintf(stderr, "V: type = %u\n", (state->response).type);
+    fprintf(stderr, "V: resultcode = %u\n", (state->response).resultcode);
+    fprintf(stderr, "V: epoch = %u\n", (state->response).epoch);
   }
   }
 
 
   return r;
   return r;

+ 2 - 2
src/tools/tor-fw-helper/tor-fw-helper-natpmp.h

@@ -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,
-                               void *backend_state);
+int tor_natpmp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port,
+                               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);

+ 20 - 24
src/tools/tor-fw-helper/tor-fw-helper-upnp.c

@@ -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,44 +154,40 @@ 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 r;
+  int retval;
   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);
-    if (r != UPNP_ERR_SUCCESS)
-      return r;
+    fprintf(stderr, "E: %s but state is not initialized.\n", __func__);
+    return -1;
   }
   }
 
 
-  if (options->verbose)
-    fprintf(stdout, "V: internal port: %d, external port: %d\n",
-            (int)options->internal_port, (int)options->external_port);
+  if (is_verbose)
+    fprintf(stderr, "V: UPnP: internal port: %u, external port: %u\n",
+            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,
-                          state->data.first.servicetype,
-                          external_port_str, internal_port_str,
+  retval = UPNP_AddPortMapping(state->urls.controlURL,
+                               state->data.first.servicetype,
+                               external_port_str, internal_port_str,
 #ifdef MINIUPNPC15
 #ifdef MINIUPNPC15
-                          state->lanaddr, UPNP_DESC, "TCP", 0);
+                               state->lanaddr, UPNP_DESC, "TCP", 0);
 #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 UPNP_ERR_SUCCESS;
+  return (retval == UPNP_ERR_SUCCESS) ? 0 : -1;
 }
 }
+
 #endif
 #endif
 
 

+ 2 - 1
src/tools/tor-fw-helper/tor-fw-helper-upnp.h

@@ -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

+ 236 - 131
src/tools/tor-fw-helper/tor-fw-helper.c

@@ -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"
-          " [-e|--external-or-port [TCP port]]\n"
-          " [-d|--internal-dir-port [TCP port]\n"
-          " [-p|--external-dir-port [TCP port]]]\n");
+          " [-p|--forward-port ([<external port>]:<internal 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(stdout, "V: size of backend state: %u\n",
+        fprintf(stderr, "V: running backend_state now: %i\n", i);
+        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
- * port forward for the OR port stored in <b>tor_fw_options</b>. */
+/** Print a spec-conformant string to stdout describing the results of
+ *  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,
-                       backends_t *backends)
+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;
   int i;
-  int r = 0;
+  smartlist_t *backend_names_sl = smartlist_new();
 
 
-  if (tor_fw_options->verbose)
-    fprintf(stdout, "V: tor_fw_add_or_port\n");
+  assert(backends->n_backends);
 
 
-  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);
-  }
+  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
 /** 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>. */
+ *  port forward for the port stored in <b>tor_fw_options</b>. */
 static void
 static void
-tor_fw_add_dir_port(tor_fw_options_t *tor_fw_options,
-                       backends_t *backends)
+tor_fw_add_ports(tor_fw_options_t *tor_fw_options,
+                 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_dir_port\n");
+    fprintf(stderr, "V: %s\n", __func__);
 
 
-  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);
+  /** 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) {
+      if (tor_fw_options->verbose) {
+        fprintf(stderr, "V: running backend_state now: %i\n", i);
+        fprintf(stderr, "V: size of backend state: %u\n",
+                (int)(backends->backend_ops)[i].state_len);
+        fprintf(stderr, "V: backend state name: %s\n",
+                (const char *) backends->backend_ops[i].name);
+      }
+
+      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]);
+      if (r == 0) { /* backend success */
+        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;
+      }
+
+      fprintf(stderr, "tor-fw-helper: tor_fw_add_port backend %s "
+              "returned: %i\n",
+              (const char *) backends->backend_ops[i].name, r);
     }
     }
-    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);
-  }
+
+    if (!succeeded) { /* all backends failed */
+      char *list_of_backends_str = get_list_of_backends_string(backends);
+      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'},
-        {"external-or-port", 1, 0, 'e'},
-        {"internal-dir-port", 1, 0, 'd'},
-        {"external-dir-port", 1, 0, 'p'},
+        {"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);
-        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);
+      case 'p': {
+        port_to_forward_t *port_to_forward = parse_port(optarg);
+        if (!port_to_forward) {
+          fprintf(stderr, "E: Failed to parse '%s'.\n", optarg);
+          usage();
+          exit(1);
+        }
+
+        /* 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) {
-    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);
+  { // Verbose output
+
+    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, fetch_public_ip = %u\n",
+              tor_fw_version, tor_fw_options.verbose, tor_fw_options.help,
+              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;
-     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");
+  // See if the user actually wants us to do something.
+  if (!tor_fw_options.fetch_public_ip && !tor_fw_options.ports_to_forward) {
+    fprintf(stderr, "E: We require a port to be forwarded 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)
-    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 (!r) { // all backends failed:
+    // 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);
   }
   }
 
 
-  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);
+  // Forward TCP ports.
+  if (tor_fw_options.ports_to_forward) {
+    tor_fw_add_ports(&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);
+  // Fetch our public IP.
+  if (tor_fw_options.fetch_public_ip) {
+    tor_fw_fetch_public_ip(&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");
+  // Cleanup and exit.
+  if (tor_fw_options.ports_to_forward) {
+    SMARTLIST_FOREACH(tor_fw_options.ports_to_forward,
+                      port_to_forward_t *, port,
+                      tor_free(port));
+    smartlist_free(tor_fw_options.ports_to_forward);
   }
   }
 
 
   exit(r);
   exit(r);

+ 11 - 9
src/tools/tor-fw-helper/tor-fw-helper.h

@@ -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;
-  uint16_t private_or_port;
-  uint16_t public_dir_port;
-  uint16_t public_or_port;
-  uint16_t internal_port;
-  uint16_t external_port;
+  struct smartlist_t *ports_to_forward;
   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