Skip to content
Snippets Groups Projects
transport_config.c 9.75 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* Copyright (c) 2001 Matej Pfajfar.
     * Copyright (c) 2001-2004, Roger Dingledine.
     * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
    
     * Copyright (c) 2007-2021, The Tor Project, Inc. */
    
    /* See LICENSE for licensing information */
    
    /**
     * @file transport_config.c
     * @brief Code to interpret the user's configuration of Tor's server
     *        pluggable transports.
     **/
    
    #include "orconfig.h"
    
    #define RELAY_TRANSPORT_CONFIG_PRIVATE
    
    #include "feature/relay/transport_config.h"
    
    #include "lib/encoding/confline.h"
    #include "lib/encoding/keyval.h"
    
    #include "lib/container/smartlist.h"
    
    /* Required for dirinfo_type_t in or_options_t */
    #include "core/or/or.h"
    #include "app/config/config.h"
    
    #include "feature/relay/ext_orport.h"
    #include "feature/relay/routermode.h"
    
    /* Copied from config.c, we will refactor later in 29211. */
    #define REJECT(arg) \
      STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
    
    /** Given a ServerTransportListenAddr <b>line</b>, return its
     *  <address:port> string. Return NULL if the line was not
     *  well-formed.
     *
     *  If <b>transport</b> is set, return NULL if the line is not
     *  referring to <b>transport</b>.
     *
     *  The returned string is allocated on the heap and it's the
     *  responsibility of the caller to free it. */
    static char *
    
    get_bindaddr_from_transport_listen_line(const char *line,
                                            const char *transport)
    
    {
      smartlist_t *items = NULL;
      const char *parsed_transport = NULL;
      char *addrport = NULL;
      tor_addr_t addr;
      uint16_t port = 0;
    
      items = smartlist_new();
      smartlist_split_string(items, line, NULL,
                             SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
    
      if (smartlist_len(items) < 2) {
        log_warn(LD_CONFIG,"Too few arguments on ServerTransportListenAddr line.");
        goto err;
      }
    
      parsed_transport = smartlist_get(items, 0);
      addrport = tor_strdup(smartlist_get(items, 1));
    
      /* If 'transport' is given, check if it matches the one on the line */
      if (transport && strcmp(transport, parsed_transport))
        goto err;
    
      /* Validate addrport */
      if (tor_addr_port_parse(LOG_WARN, addrport, &addr, &port, -1)<0) {
        log_warn(LD_CONFIG, "Error parsing ServerTransportListenAddr "
                 "address '%s'", addrport);
        goto err;
      }
    
      goto done;
    
     err:
      tor_free(addrport);
      addrport = NULL;
    
     done:
      SMARTLIST_FOREACH(items, char*, s, tor_free(s));
      smartlist_free(items);
    
      return addrport;
    }
    
    /** Given the name of a pluggable transport in <b>transport</b>, check
     *  the configuration file to see if the user has explicitly asked for
     *  it to listen on a specific port. Return a <address:port> string if
     *  so, otherwise NULL. */
    char *
    
    pt_get_bindaddr_from_config(const char *transport)
    
    {
      config_line_t *cl;
      const or_options_t *options = get_options();
    
      for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) {
        char *bindaddr =
          get_bindaddr_from_transport_listen_line(cl->value, transport);
        if (bindaddr)
          return bindaddr;
      }
    
      return NULL;
    }
    
    /** Given a ServerTransportOptions <b>line</b>, return a smartlist
     *  with the options. Return NULL if the line was not well-formed.
     *
     *  If <b>transport</b> is set, return NULL if the line is not
     *  referring to <b>transport</b>.
     *
     *  The returned smartlist and its strings are allocated on the heap
     *  and it's the responsibility of the caller to free it. */
    
    STATIC smartlist_t *
    
    get_options_from_transport_options_line(const char *line,
                                            const char *transport)
    
    {
      smartlist_t *items = smartlist_new();
    
      smartlist_t *pt_options = smartlist_new();
    
      const char *parsed_transport = NULL;
    
      smartlist_split_string(items, line, NULL,
                             SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
    
      if (smartlist_len(items) < 2) {
        log_warn(LD_CONFIG,"Too few arguments on ServerTransportOptions line.");
        goto err;
      }
    
      parsed_transport = smartlist_get(items, 0);
      /* If 'transport' is given, check if it matches the one on the line */
      if (transport && strcmp(transport, parsed_transport))
        goto err;
    
      SMARTLIST_FOREACH_BEGIN(items, const char *, option) {
        if (option_sl_idx == 0) /* skip the transport field (first field)*/
          continue;
    
        /* validate that it's a k=v value */
        if (!string_is_key_value(LOG_WARN, option)) {
          log_warn(LD_CONFIG, "%s is not a k=v value.", escaped(option));
          goto err;
        }
    
        /* add it to the options smartlist */
    
        smartlist_add_strdup(pt_options, option);
    
        log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option));
      } SMARTLIST_FOREACH_END(option);
    
      goto done;
    
     err:
    
      SMARTLIST_FOREACH(pt_options, char*, s, tor_free(s));
      smartlist_free(pt_options);
      pt_options = NULL;
    
    
     done:
      SMARTLIST_FOREACH(items, char*, s, tor_free(s));
      smartlist_free(items);
    
    
      return pt_options;
    
    }
    
    /** Given the name of a pluggable transport in <b>transport</b>, check
     *  the configuration file to see if the user has asked us to pass any
     *  parameters to the pluggable transport. Return a smartlist
     *  containing the parameters, otherwise NULL. */
    smartlist_t *
    
    pt_get_options_for_server_transport(const char *transport)
    
    {
      config_line_t *cl;
      const or_options_t *options = get_options();
    
      for (cl = options->ServerTransportOptions; cl; cl = cl->next) {
        smartlist_t *options_sl =
          get_options_from_transport_options_line(cl->value, transport);
        if (options_sl)
          return options_sl;
      }
    
      return NULL;
    }
    
    /**
     * Legacy validation/normalization function for the server transport options.
     * Uses old_options as the previous options.
     *
     * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
     * on error.
     */
    int
    options_validate_server_transport(const or_options_t *old_options,
                                      or_options_t *options,
                                      char **msg)
    {
      (void)old_options;
    
      if (BUG(!options))
        return -1;
    
      if (BUG(!msg))
        return -1;
    
      config_line_t *cl;
    
      if (options->ServerTransportPlugin && !server_mode(options)) {
        log_notice(LD_GENERAL, "Tor is not configured as a relay but you specified"
                   " a ServerTransportPlugin line (%s). The ServerTransportPlugin "
                   "line will be ignored.",
                   escaped(options->ServerTransportPlugin->value));
      }
    
    
      if (options->ServerTransportListenAddr && !options->ServerTransportPlugin) {
        log_notice(LD_GENERAL, "You need at least a single managed-proxy to "
                   "specify a transport listen address. The "
                   "ServerTransportListenAddr line will be ignored.");
      }
    
      for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
    
        if (pt_parse_transport_line(options, cl->value, 1, 1) < 0)
    
          REJECT("Invalid server transport line. See logs for details.");
      }
    
    
      for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) {
        /** If get_bindaddr_from_transport_listen_line() fails with
            'transport' being NULL, it means that something went wrong
            while parsing the ServerTransportListenAddr line. */
        char *bindaddr = get_bindaddr_from_transport_listen_line(cl->value, NULL);
        if (!bindaddr)
          REJECT("ServerTransportListenAddr did not parse. See logs for details.");
        tor_free(bindaddr);
      }
    
      for (cl = options->ServerTransportOptions; cl; cl = cl->next) {
        /** If get_options_from_transport_options_line() fails with
            'transport' being NULL, it means that something went wrong
            while parsing the ServerTransportOptions line. */
        smartlist_t *options_sl =
          get_options_from_transport_options_line(cl->value, NULL);
        if (!options_sl)
          REJECT("ServerTransportOptions did not parse. See logs for details.");
    
        SMARTLIST_FOREACH(options_sl, char *, cp, tor_free(cp));
        smartlist_free(options_sl);
      }
    
      return 0;
    }
    
    
    /** Fetch the active option list, and take server pluggable transport actions
     * based on it. All of the things we do should survive being done repeatedly.
     * If present, <b>old_options</b> contains the previous value of the options.
     *
     * Return 0 if all goes well, return -1 if it's time to die.
     *
     * Note: We haven't moved all the "act on new configuration" logic
     * into the options_act* functions yet.  Some is still in do_hup() and other
     * places.
     */
    int
    options_act_server_transport(const or_options_t *old_options)
    {
      (void)old_options;
    
      config_line_t *cl;
      const or_options_t *options = get_options();
      int running_tor = options->command == CMD_RUN_TOR;
    
      /* If we are a bridge with a pluggable transport proxy but no
         Extended ORPort, inform the user that they are missing out. */
    
      if (options->ServerTransportPlugin &&
    
          !options->ExtORPort_lines) {
        log_notice(LD_CONFIG, "We use pluggable transports but the Extended "
                   "ORPort is disabled. Tor and your pluggable transports proxy "
                   "communicate with each other via the Extended ORPort so it "
                   "is suggested you enable it: it will also allow your Bridge "
                   "to collect statistics about its clients that use pluggable "
                   "transports. Please enable it using the ExtORPort torrc option "
                   "(e.g. set 'ExtORPort auto').");
      }
    
      /* If we have an ExtORPort, initialize its auth cookie. */
      if (running_tor &&
          init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) {
        log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file.");
        return -1;
      }
    
      if (!options->DisableNetwork) {
    
        if (options->ServerTransportPlugin) {
    
          for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
    
            if (pt_parse_transport_line(options, cl->value, 0, 1) < 0) {
    
              // LCOV_EXCL_START
              log_warn(LD_BUG,
                       "Previously validated ServerTransportPlugin line "
                       "could not be added!");
              return -1;
              // LCOV_EXCL_STOP
            }
          }
        }
      }
    
      return 0;
    }