/* Copyright (c) 2018, The Tor Project, Inc. */
/* See LICENSE for licensing information     */

/**
 * \file test_bridges.c
 * \brief Unittests for code in src/or/bridges.c
 **/

#define TOR_BRIDGES_PRIVATE
#define PT_PRIVATE /* Only needed for the mock_* items below */

#include <stdbool.h>

#include "or/or.h"
#include "lib/net/address.h"
#include "or/bridges.h"
#include "or/config.h"
#include "or/transports.h"

/* Test suite stuff */
#include "test/test.h"

/**
 * A mocked transport_t, constructed via mock_transport_get_by_name().
 */
static transport_t *mock_transport = NULL;

/**
 * Mock transport_get_by_name() to simply return a transport_t for the
 * transport name that was input to it.
 */
static transport_t *
mock_transport_get_by_name(const char *name)
{
  tor_addr_t *addr = tor_malloc(sizeof(tor_addr_t));
  uint16_t port = 9999;
  int socksv = 9;
  char *args = tor_strdup("foo=bar");

  if (!mock_transport) {
    tor_addr_parse(addr, "99.99.99.99");
    mock_transport = transport_new(addr, port, name, socksv, args);
  }

  tor_free(addr);
  tor_free(args);

  return mock_transport;
}

#undef PT_PRIVATE /* defined(PT_PRIVATE) */

/**
 * Test helper: Add a variety of bridges to our global bridgelist.
 */
static void
helper_add_bridges_to_bridgelist(void *arg)
{
  /* Note: the two bridges which do not have specified fingerprints will be
   * internally stored as both having the same fingerprint of all-zero bytes.
   */

  (void)arg;
  char *bridge0 = tor_strdup("6.6.6.6:6666");
  char *bridge1 = tor_strdup("6.6.6.7:6667 "
                             "A10C4F666D27364036B562823E5830BC448E046A");
  char *bridge2 = tor_strdup("obfs4 198.245.60.51:443 "
                             "752CF7825B3B9EA6A98C83AC41F7099D67007EA5 "
                  "cert=xpmQtKUqQ/6v5X7ijgYE/f03+l2/EuQ1dexjyUhh16wQlu/"
                             "cpXUGalmhDIlhuiQPNEKmKw iat-mode=0");
  char *bridge3 = tor_strdup("banana 5.5.5.5:5555 "
                             "9D6AE1BD4FDF39721CE908966E79E16F9BFCCF2F");
  char *bridge4 = tor_strdup("obfs4 1.2.3.4:1234 "
                             "foo=abcdefghijklmnopqrstuvwxyz");
  char *bridge5 = tor_strdup("apple 4.4.4.4:4444 "
                             "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "
                             "foo=abcdefghijklmnopqrstuvwxyz");

  mark_bridge_list();

#define ADD_BRIDGE(bridge)                                          \
  bridge_line_t *bridge_line_ ##bridge = parse_bridge_line(bridge); \
  if (!bridge_line_ ##bridge) {                                     \
    printf("Unparseable bridge line: '%s'", #bridge);               \
  } else {                                                          \
    bridge_add_from_config(bridge_line_ ##bridge);                  \
  }                                                                 \
  tor_free(bridge);

  ADD_BRIDGE(bridge0);
  ADD_BRIDGE(bridge1);
  ADD_BRIDGE(bridge2);
  ADD_BRIDGE(bridge3);
  ADD_BRIDGE(bridge4);
  ADD_BRIDGE(bridge5);
#undef ADD_BRIDGES

  sweep_bridge_list();
}

/**
 * Make sure our test helper works too.
 */
static void
test_bridges_helper_func_add_bridges_to_bridgelist(void *arg)
{
  helper_add_bridges_to_bridgelist(arg);
  tt_finished();

 done:
  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling bridge_list_get() should create a new bridgelist if we
 * didn't have one before.
 */
static void
test_bridges_bridge_list_get_creates_new_bridgelist(void *arg)
{
  const smartlist_t *bridgelist = bridge_list_get();

  (void)arg;

  tt_ptr_op(bridgelist, OP_NE, NULL);

 done:
  return;
}

/**
 * Calling clear_bridge_list() should remove all bridges from the bridgelist.
 */
static void
test_bridges_clear_bridge_list(void *arg)
{
  const smartlist_t *bridgelist;
  const smartlist_t *bridgelist_after;
  const bridge_info_t *bridge;

  helper_add_bridges_to_bridgelist(arg);
  bridgelist = bridge_list_get();
  tt_ptr_op(bridgelist, OP_NE, NULL);

  bridge = smartlist_get(bridgelist, 0);
  tt_ptr_op(bridge, OP_NE, NULL);

  clear_bridge_list();
  bridgelist_after = bridge_list_get();
  tt_ptr_op(bridgelist_after, OP_NE, NULL);
  tt_int_op(smartlist_len(bridgelist_after), OP_EQ, 0);

 done:
  return;
}

/**
 * Calling bridge_get_addrport() should give me the address and port
 * of the bridge.  In this case, we sort the smartlist of bridges on
 * fingerprints and choose the first one.
 */
static void
test_bridges_bridge_get_addrport(void *arg)
{
  smartlist_t *bridgelist;
  const bridge_info_t *bridge;
  const tor_addr_port_t *addrport;

  helper_add_bridges_to_bridgelist(arg);
  bridgelist = (smartlist_t*)bridge_list_get();
  tt_ptr_op(bridgelist, OP_NE, NULL);

  // This should be the bridge at 6.6.6.6:6666 with fingerprint
  // 0000000000000000000000000000000000000000
  bridge = smartlist_get(bridgelist, 0);
  tt_ptr_op(bridge, OP_NE, NULL);

  addrport = bridge_get_addr_port(bridge);
  tt_int_op(addrport->port, OP_EQ, 6666);

 done:
  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling get_configured_bridge_by_orports_digest() with two
 * configured bridge orports and an invalid digest should return the
 * bridge of the first addrport in the list.
 */
static void
test_bridges_get_configured_bridge_by_orports_digest(void *arg)
{
  smartlist_t *orports = NULL;
  const smartlist_t *bridgelist;
  const bridge_info_t *bridge1;
  const bridge_info_t *bridge2;
  const bridge_info_t *ret;
  tor_addr_port_t *addrport1;
  tor_addr_port_t *addrport2;
  const char *digest;

  helper_add_bridges_to_bridgelist(arg);
  bridgelist = bridge_list_get();
  tt_ptr_op(bridgelist, OP_NE, NULL);

  // This should be the bridge at 6.6.6.6:6666 with fingerprint
  // 0000000000000000000000000000000000000000
  bridge1 = smartlist_get(bridgelist, 0);
  tt_ptr_op(bridge1, OP_NE, NULL);
  // This should be the bridge at 6.6.6.7:6667 with fingerprint
  // A10C4F666D27364036B562823E5830BC448E046A
  bridge2 = smartlist_get(bridgelist, 1);
  tt_ptr_op(bridge2, OP_NE, NULL);

  addrport1 = (tor_addr_port_t*)bridge_get_addr_port(bridge1);
  tt_int_op(addrport1->port, OP_EQ, 6666);
  addrport2 = (tor_addr_port_t*)bridge_get_addr_port(bridge2);
  tt_int_op(addrport2->port, OP_EQ, 6667);

  orports = smartlist_new();
  smartlist_add(orports, addrport1);
  smartlist_add(orports, addrport2);

  digest = "zzzzzzzzzzzzzzzz";

  ret = get_configured_bridge_by_orports_digest(digest, orports);
  tt_ptr_op(ret, OP_NE, NULL);

  tt_assert(tor_addr_port_eq(addrport1, bridge_get_addr_port(ret)));

 done:
  smartlist_free(orports);

  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling get_configured_bridge_by_addr_port_digest() with a digest that we do
 * have and an addr:port pair we don't should return the bridge for that
 * digest.
 */
static void
test_bridges_get_configured_bridge_by_addr_port_digest_digest_only(void *arg)
{
  char digest[DIGEST_LEN];
  bridge_info_t *bridge;
  const char fingerprint[HEX_DIGEST_LEN] =
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  tor_addr_t *addr = tor_malloc(sizeof(tor_addr_t));
  char ret_addr[16];
  uint16_t port = 11111;
  int ret;

  helper_add_bridges_to_bridgelist(arg);

  // We don't actually have a bridge with this addr:port pair
  base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN);
  ret = tor_addr_parse(addr, "111.111.111.111");
  tt_int_op(ret, OP_EQ, 2); // it returns the address family on success

  bridge = get_configured_bridge_by_addr_port_digest(addr, port, digest);
  tt_ptr_op(bridge, OP_NE, NULL);

  tor_addr_to_str(ret_addr, &bridge_get_addr_port(bridge)->addr, 16, 0);
  tt_str_op("4.4.4.4", OP_EQ, ret_addr);

 done:
  tor_free(addr);

  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling get_configured_bridge_by_addr_port_digest() with only an
 * addr:port (i.e. digest set to NULL) should return the bridge for
 * that digest when there is such a bridge.
 */
static void
test_bridges_get_configured_bridge_by_addr_port_digest_address_only(void *arg)
{
  bridge_info_t *bridge;
  tor_addr_t *addr = tor_malloc(sizeof(tor_addr_t));
  char ret_addr[16];
  uint16_t port = 6666;
  int ret;

  helper_add_bridges_to_bridgelist(arg);

  ret = tor_addr_parse(addr, "6.6.6.6");
  tt_int_op(ret, OP_EQ, 2); // it returns the address family on success

  bridge = get_configured_bridge_by_addr_port_digest(addr, port, NULL);
  tt_ptr_op(bridge, OP_NE, NULL);

  tor_addr_to_str(ret_addr, &bridge_get_addr_port(bridge)->addr, 16, 0);
  tt_str_op("6.6.6.6", OP_EQ, ret_addr);

 done:
  tor_free(addr);

  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling get_configured_bridge_by_exact_addr_port_digest() with a digest that
 * we do have, and an addr:port pair we don't have, should return NULL.
 */
static void
test_bridges_get_configured_bridge_by_exact_addr_port_digest_donly(void *arg)
{
  char digest[DIGEST_LEN];
  bridge_info_t *bridge;
  const char fingerprint[HEX_DIGEST_LEN] =
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  tor_addr_t *addr = tor_malloc(sizeof(tor_addr_t));
  uint16_t port = 11111;
  int ret;

  helper_add_bridges_to_bridgelist(arg);

  // We don't actually have a bridge with this addr:port pair
  base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN);
  ret = tor_addr_parse(addr, "111.111.111.111");
  tt_int_op(ret, OP_EQ, 2); // it returns the address family on success

  bridge = get_configured_bridge_by_exact_addr_port_digest(addr, port, digest);
  tt_ptr_op(bridge, OP_EQ, NULL);

 done:
  tor_free(addr);

  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling get_configured_bridge_by_exact_addr_port_digest() with a digest that
 * we do have, and an addr:port pair we do have, should return the bridge.
 */
static void
test_bridges_get_configured_bridge_by_exact_addr_port_digest_both(void *arg)
{
  char digest[DIGEST_LEN];
  bridge_info_t *bridge;
  const char fingerprint[HEX_DIGEST_LEN] =
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  tor_addr_t *addr = tor_malloc(sizeof(tor_addr_t));
  uint16_t port = 4444;
  char ret_addr[16];
  int ret;

  helper_add_bridges_to_bridgelist(arg);

  base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN);
  ret = tor_addr_parse(addr, "4.4.4.4");
  tt_int_op(ret, OP_EQ, 2); // it returns the address family on success

  bridge = get_configured_bridge_by_exact_addr_port_digest(addr, port, digest);
  tt_ptr_op(bridge, OP_NE, NULL);

  tor_addr_to_str(ret_addr, &bridge_get_addr_port(bridge)->addr, 16, 0);
  tt_str_op("4.4.4.4", OP_EQ, ret_addr);

 done:
  tor_free(addr);

  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling get_configured_bridge_by_exact_addr_port_digest() with no digest,
 * and an addr:port pair we do have, should return the bridge.
 */
static void
test_bridges_get_configured_bridge_by_exact_addr_port_digest_aonly(void *arg)
{
  bridge_info_t *bridge;
  tor_addr_t *addr = tor_malloc(sizeof(tor_addr_t));
  uint16_t port = 4444;
  char ret_addr[16];
  int ret;

  helper_add_bridges_to_bridgelist(arg);

  ret = tor_addr_parse(addr, "4.4.4.4");
  tt_int_op(ret, OP_EQ, 2); // it returns the address family on success

  bridge = get_configured_bridge_by_exact_addr_port_digest(addr, port, NULL);
  tt_ptr_op(bridge, OP_NE, NULL);

  tor_addr_to_str(ret_addr, &bridge_get_addr_port(bridge)->addr, 16, 0);
  tt_str_op("4.4.4.4", OP_EQ, ret_addr);

 done:
  tor_free(addr);

  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling find_bridge_by_digest() when we have a bridge with a known
 * identity digest should return the bridge's information.
 */
static void
test_bridges_find_bridge_by_digest_known(void *arg)
{
  char digest1[DIGEST_LEN];
  bridge_info_t *bridge;
  const char fingerprint[HEX_DIGEST_LEN] =
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

  helper_add_bridges_to_bridgelist(arg);

  base16_decode(digest1, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN);
  bridge = find_bridge_by_digest(digest1);

  tt_ptr_op(bridge, OP_NE, NULL);

  /* We have to call bridge_get_rsa_id_digest() here because the bridge_info_t
   * struct is opaquely defined in bridges.h. */
  const uint8_t *digest2 = bridge_get_rsa_id_digest(bridge);

  tt_mem_op((char*)digest2, OP_EQ, digest1, DIGEST_LEN);

 done:
  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling find_bridge_by_digest() when we do NOT have a bridge with that
 * identity digest should return NULL.
 */
static void
test_bridges_find_bridge_by_digest_unknown(void *arg)
{
  const char *fingerprint = "cccccccccccccccccccccccccccccccccccccccc";
  bridge_info_t *bridge;

  helper_add_bridges_to_bridgelist(arg);

  bridge = find_bridge_by_digest(fingerprint);

  tt_ptr_op(bridge, OP_EQ, NULL);

 done:
  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling bridge_resolve_conflicts() with an identical bridge to one we've
 * already configure should mark the pre-configured bridge for removal.
 */
static void
test_bridges_bridge_resolve_conflicts(void *arg)
{
  tor_addr_t *addr = tor_malloc(sizeof(tor_addr_t));
  uint16_t port = 4444;
  const char *digest = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
  const char *transport = "apple";
  int ret;

  helper_add_bridges_to_bridgelist(arg);

  ret = tor_addr_parse(addr, "4.4.4.4");
  tt_int_op(ret, OP_EQ, 2); // it returns the address family on success

  bridge_resolve_conflicts((const tor_addr_t*)addr, port, digest, transport);

  /* The bridge should now be marked for removal, and removed when we sweep the
   * bridge_list */
  sweep_bridge_list();
  ret = addr_is_a_configured_bridge((const tor_addr_t*)addr, port, digest);
  tt_int_op(ret, OP_EQ, 0);

 done:
  tor_free(addr);

  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling transport_is_needed() with a transport we do need ("obfs4") and a
 * bogus transport that we don't need should return 1 and 0, respectively.
 */
static void
test_bridges_transport_is_needed(void *arg)
{
  int ret;

  helper_add_bridges_to_bridgelist(arg);

  ret = transport_is_needed("obfs4");
  tt_int_op(ret, OP_EQ, 1);

  ret = transport_is_needed("apowefjaoewpaief");
  tt_int_op(ret, OP_EQ, 0);

 done:
  mark_bridge_list();
  sweep_bridge_list();
}

/**
 * Calling get_transport_by_bridge_addrport() with the address and port of a
 * configured bridge which uses a pluggable transport when there is no global
 * transport_list should return -1 and the transport_t should be NULL.
 */
static void
test_bridges_get_transport_by_bridge_addrport_no_ptlist(void *arg)
{
  transport_t *transport = NULL;
  tor_addr_t *addr = tor_malloc(sizeof(tor_addr_t));
  uint16_t port = 1234;
  int ret;

  helper_add_bridges_to_bridgelist(arg);

  ret = tor_addr_parse(addr, "1.2.3.4");
  tt_int_op(ret, OP_EQ, 2); // it returns the address family on success?

  /* This will fail because the global transport_list has nothing in it, and so
   * transport_get_by_name() has nothing to return, even the the bridge *did*
   * say it had an obfs4 transport.
   */
  ret = get_transport_by_bridge_addrport((const tor_addr_t*)addr, port,
                                         (const transport_t**)&transport);
  tt_int_op(ret, OP_EQ, -1); // returns -1 on failure
  tt_ptr_op(transport, OP_EQ, NULL);

 done:
  tor_free(addr);

  mark_bridge_list();
  sweep_bridge_list();
}

#define PT_PRIVATE

/**
 * Calling get_transport_by_bridge_addrport() with the address and port of a
 * configured bridge which uses a pluggable transport should return 0 and set
 * appropriate transport_t.
 */
static void
test_bridges_get_transport_by_bridge_addrport(void *arg)
{
  transport_t *transport = NULL;
  tor_addr_t *addr = tor_malloc(sizeof(tor_addr_t));
  uint16_t port = 1234;
  int ret;

  helper_add_bridges_to_bridgelist(arg);
  mark_transport_list(); // Also initialise our transport_list

  ret = tor_addr_parse(addr, "1.2.3.4");
  tt_int_op(ret, OP_EQ, 2); // it returns the address family on success?

  /* After we mock transport_get_by_name() to return a bogus transport_t with
   * the name it was asked for, the call should succeed.
   */
  MOCK(transport_get_by_name, mock_transport_get_by_name);
  ret = get_transport_by_bridge_addrport((const tor_addr_t*)addr, port,
                                         (const transport_t**)&transport);
  tt_int_op(ret, OP_EQ, 0); // returns 0 on success
  tt_ptr_op(transport, OP_NE, NULL);
  tt_str_op(transport->name, OP_EQ, "obfs4");

 done:
  UNMOCK(transport_get_by_name);

  tor_free(addr);
  transport_free(transport);

  mark_bridge_list();
  sweep_bridge_list();
}

#undef PT_PRIVATE /* defined(PT_PRIVATE) */

#define B_TEST(name, flags) \
  { #name, test_bridges_ ##name, (flags), NULL, NULL }

struct testcase_t bridges_tests[] = {
  B_TEST(helper_func_add_bridges_to_bridgelist, 0),
  B_TEST(bridge_list_get_creates_new_bridgelist, 0),
  B_TEST(clear_bridge_list, 0),
  B_TEST(bridge_get_addrport, 0),
  B_TEST(get_configured_bridge_by_orports_digest, 0),
  B_TEST(get_configured_bridge_by_addr_port_digest_digest_only, 0),
  B_TEST(get_configured_bridge_by_addr_port_digest_address_only, 0),
  B_TEST(get_configured_bridge_by_exact_addr_port_digest_donly, 0),
  B_TEST(get_configured_bridge_by_exact_addr_port_digest_both, 0),
  B_TEST(get_configured_bridge_by_exact_addr_port_digest_aonly, 0),
  B_TEST(find_bridge_by_digest_known, 0),
  B_TEST(find_bridge_by_digest_unknown, 0),
  B_TEST(bridge_resolve_conflicts, 0),
  B_TEST(get_transport_by_bridge_addrport_no_ptlist, 0),
  B_TEST(get_transport_by_bridge_addrport, 0),
  B_TEST(transport_is_needed, 0),
  END_OF_TESTCASES
};

