text: /* Copyright (c) 2016-2020, The Tor Project, Inc. */
  /* See LICENSE for licensing information */
  
  /**
   * \file test_hs_config.c
   * \brief Test hidden service configuration functionality.
   */
  
  #define CONFIG_PRIVATE
  #define HS_SERVICE_PRIVATE
  
  #include "test/test.h"
  #include "test/test_helpers.h"
  #include "test/log_test_helpers.h"
  #include "test/resolve_test_helpers.h"
  
  #include "app/config/config.h"
  #include "feature/hs/hs_common.h"
  #include "feature/hs/hs_config.h"
  #include "feature/hs/hs_service.h"
  
  static int
  helper_config_service(const char *conf, int validate_only)
  {
    int ret = 0;
    or_options_t *options = NULL;
    tt_assert(conf);
    options = helper_parse_options(conf);
    tt_assert(options);
    ret = hs_config_service_all(options, validate_only);
   done:
    or_options_free(options);
    return ret;
  }
  
  static void
  test_invalid_service(void *arg)
  {
    int ret;
  
    (void) arg;
  
    /* Try with a missing port configuration. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 1\n"; /* Wrong not supported version. */
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("HiddenServiceVersion must be between 3 and 3");
      teardown_capture_of_logs();
    }
  
    /* Bad value of HiddenServiceAllowUnknownPorts. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServiceAllowUnknownPorts 2\n"; /* Should be 0 or 1. */
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("Could not parse "
                                "HiddenServiceAllowUnknownPorts: Unrecognized "
                                "value 2. Allowed values are 0 and 1.");
      teardown_capture_of_logs();
    }
  
    /* Bad value of HiddenServiceDirGroupReadable */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServiceDirGroupReadable 2\n"; /* Should be 0 or 1. */
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("Could not parse "
                                "HiddenServiceDirGroupReadable: "
                                "Unrecognized value 2.");
      teardown_capture_of_logs();
    }
  
    /* Bad value of HiddenServiceMaxStreamsCloseCircuit */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServiceMaxStreamsCloseCircuit 2\n"; /* Should be 0 or 1. */
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("Could not parse "
                                "HiddenServiceMaxStreamsCloseCircuit: "
                                "Unrecognized value 2");
      teardown_capture_of_logs();
    }
  
    /* Too much max streams. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 80\n"
        "HiddenServiceMaxStreams 65536\n"; /* One too many. */
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("HiddenServiceMaxStreams must be between "
                                "0 and 65535, not 65536");
      teardown_capture_of_logs();
    }
  
    /* Duplicate directory directive. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 80\n"
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 81\n";
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("Another hidden service is already "
                                "configured for directory");
      teardown_capture_of_logs();
    }
  
    /* Bad port. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 65536\n";
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("Missing or invalid port");
      teardown_capture_of_logs();
    }
  
    /* Bad target addr:port separation. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 80 127.0.0.1 8000\n";
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("HiddenServicePort parse error: "
                                "invalid port mapping");
      teardown_capture_of_logs();
    }
  
    /* Out of order directives. */
    {
      const char *conf =
        "HiddenServiceVersion 3\n"
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServicePort 80\n";
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("HiddenServiceVersion with no preceding "
                                "HiddenServiceDir directive");
      teardown_capture_of_logs();
    }
  
   done:
    ;
  }
  
  static void
  test_valid_service(void *arg)
  {
    int ret;
  
    (void) arg;
  
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 81\n";
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, 0);
    }
  
   done:
    ;
  }
  
  static void
  test_invalid_service_v3(void *arg)
  {
    int validate_only = 1, ret;
  
    (void) arg;
  
    /* Try with a missing port configuration. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n";
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, validate_only);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("with no ports configured.");
      teardown_capture_of_logs();
    }
  
    /* Too many introduction points. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 80\n"
        "HiddenServiceNumIntroductionPoints 21\n"; /* One too many. */
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, validate_only);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("HiddenServiceNumIntroductionPoints must "
                                "be between 3 and 20, not 21.");
      teardown_capture_of_logs();
    }
  
    /* Too little introduction points. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 80\n"
        "HiddenServiceNumIntroductionPoints 1\n";
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, validate_only);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("HiddenServiceNumIntroductionPoints must "
                                "be between 3 and 20, not 1.");
      teardown_capture_of_logs();
    }
  
   done:
    ;
  }
  
  static void
  test_valid_service_v3(void *arg)
  {
    int ret;
  
    (void) arg;
    mock_hostname_resolver();
  
    /* Valid complex configuration. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 80\n"
        "HiddenServicePort 22 localhost:22\n"
  #ifdef HAVE_SYS_UN_H
        "HiddenServicePort 42 unix:/path/to/socket\n"
  #endif
        "HiddenServiceAllowUnknownPorts 1\n"
        "HiddenServiceMaxStreams 42\n"
        "HiddenServiceMaxStreamsCloseCircuit 0\n"
        "HiddenServiceDirGroupReadable 1\n"
        "HiddenServiceNumIntroductionPoints 7\n";
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, 0);
    }
  
    /* Valid complex configuration. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 65535\n"
        "HiddenServicePort 22 1.1.1.1:22\n"
  #ifdef HAVE_SYS_UN_H
        "HiddenServicePort 9000 unix:/path/to/socket\n"
  #endif
        "HiddenServiceAllowUnknownPorts 0\n"
        "HiddenServiceMaxStreams 42\n"
        "HiddenServiceMaxStreamsCloseCircuit 0\n"
        "HiddenServiceDirGroupReadable 1\n"
        "HiddenServiceNumIntroductionPoints 20\n";
      ret = helper_config_service(conf, 1);
      tt_int_op(ret, OP_EQ, 0);
    }
  
   done:
    unmock_hostname_resolver();
  }
  
  static void
  test_staging_service_v3(void *arg)
  {
    int ret;
  
    (void) arg;
  
    /* We don't validate a service object, this is the service test that are in
     * charge of doing so. We just check for the stable state after
     * registration. */
  
    hs_init();
  
    /* Time for a valid v3 service that should get staged. */
    const char *conf =
      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
      "HiddenServiceVersion 3\n"
      "HiddenServicePort 65535\n"
      "HiddenServicePort 22 1.1.1.1:22\n"
  #ifdef HAVE_SYS_UN_H
      "HiddenServicePort 9000 unix:/path/to/socket\n"
  #endif
      "HiddenServiceAllowUnknownPorts 0\n"
      "HiddenServiceMaxStreams 42\n"
      "HiddenServiceMaxStreamsCloseCircuit 0\n"
      "HiddenServiceDirGroupReadable 1\n"
      "HiddenServiceNumIntroductionPoints 20\n";
    ret = helper_config_service(conf, 0);
    tt_int_op(ret, OP_EQ, 0);
    /* Ok, we have a service in our map! Registration went well. */
    tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
  
   done:
    hs_free_all();
  }
  
  static void
  test_dos_parameters(void *arg)
  {
    int ret;
  
    (void) arg;
  
    hs_init();
  
    /* Valid configuration. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 22 1.1.1.1:22\n"
        "HiddenServiceEnableIntroDoSDefense 1\n"
        "HiddenServiceEnableIntroDoSRatePerSec 42\n"
        "HiddenServiceEnableIntroDoSBurstPerSec 87\n";
  
      setup_full_capture_of_logs(LOG_INFO);
      ret = helper_config_service(conf, 0);
      tt_int_op(ret, OP_EQ, 0);
      expect_log_msg_containing("Service INTRO2 DoS defenses rate set to: 42");
      expect_log_msg_containing("Service INTRO2 DoS defenses burst set to: 87");
      teardown_capture_of_logs();
    }
  
    /* Invalid rate. Value of 2^37. Max allowed is 2^31. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 22 1.1.1.1:22\n"
        "HiddenServiceEnableIntroDoSDefense 1\n"
        "HiddenServiceEnableIntroDoSRatePerSec 137438953472\n"
        "HiddenServiceEnableIntroDoSBurstPerSec 87\n";
  
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 0);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("Could not parse "
                                "HiddenServiceEnableIntroDoSRatePerSec: "
                                "Integer 137438953472 is malformed or out of "
                                "bounds.");
      teardown_capture_of_logs();
    }
  
    /* Invalid burst. Value of 2^38. Max allowed is 2^31. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 22 1.1.1.1:22\n"
        "HiddenServiceEnableIntroDoSDefense 1\n"
        "HiddenServiceEnableIntroDoSRatePerSec 42\n"
        "HiddenServiceEnableIntroDoSBurstPerSec 274877906944\n";
  
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 0);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("Could not parse "
                                "HiddenServiceEnableIntroDoSBurstPerSec: "
                                "Integer 274877906944 is malformed or out "
                                "of bounds.");
      teardown_capture_of_logs();
    }
  
    /* Burst is smaller than rate. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 22 1.1.1.1:22\n"
        "HiddenServiceEnableIntroDoSDefense 1\n"
        "HiddenServiceEnableIntroDoSRatePerSec 42\n"
        "HiddenServiceEnableIntroDoSBurstPerSec 27\n";
  
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 0);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("Hidden service DoS defenses burst (27) can "
                                "not be smaller than the rate value (42).");
      teardown_capture_of_logs();
    }
  
    /* Negative value. */
    {
      const char *conf =
        "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
        "HiddenServiceVersion 3\n"
        "HiddenServicePort 22 1.1.1.1:22\n"
        "HiddenServiceEnableIntroDoSDefense 1\n"
        "HiddenServiceEnableIntroDoSRatePerSec -1\n"
        "HiddenServiceEnableIntroDoSBurstPerSec 42\n";
  
      setup_full_capture_of_logs(LOG_WARN);
      ret = helper_config_service(conf, 0);
      tt_int_op(ret, OP_EQ, -1);
      expect_log_msg_containing("Could not parse "
                                "HiddenServiceEnableIntroDoSRatePerSec: "
                                "Integer -1 is malformed or out of bounds.");
      teardown_capture_of_logs();
    }
  
   done:
    hs_free_all();
  }
  
  struct testcase_t hs_config_tests[] = {
    /* Invalid service not specific to any version. */
    { "invalid_service", test_invalid_service, TT_FORK,
      NULL, NULL },
    { "valid_service", test_valid_service, TT_FORK,
      NULL, NULL },
  
    /* Test case only for version 3. */
    { "invalid_service_v3", test_invalid_service_v3, TT_FORK,
      NULL, NULL },
    { "valid_service_v3", test_valid_service_v3, TT_FORK,
      NULL, NULL },
  
    /* Test service staging. */
    { "staging_service_v3", test_staging_service_v3, TT_FORK,
      NULL, NULL },
  
    /* Test HS DoS parameters. */
    { "dos_parameters", test_dos_parameters, TT_FORK,
      NULL, NULL },
  
    END_OF_TESTCASES
  };
  