summaryrefslogtreecommitdiffstats
path: root/test-dnsdistsvc_cc.cc
blob: 6350f3ae5c29146680814e16147c0515918b5a1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#ifndef BOOST_TEST_DYN_LINK
#define BOOST_TEST_DYN_LINK
#endif

#define BOOST_TEST_NO_MAIN

#include <boost/test/unit_test.hpp>

#include "dnsdist-svc.hh"
#include "svc-records.hh"
#include "dnsparser.hh"

BOOST_AUTO_TEST_SUITE(dnsdistsvc_cc)

BOOST_AUTO_TEST_CASE(test_Basic)
{
  DNSName target("powerdns.com.");

  {
    // invalid priority of 0 + parameters
    std::vector<uint8_t> payload;
    const uint16_t priority = 0;
    BOOST_CHECK(!generateSVCPayload(payload, priority, target, {SvcParam::SvcParamKey::port}, {"dot"}, false, 853, std::string(), {ComboAddress("192.0.2.1")}, {ComboAddress("2001:db8::1")}, {}));
  }

  {
    std::vector<uint8_t> payload;
    const uint16_t priority = 1;
    BOOST_CHECK(generateSVCPayload(payload, priority, target, {SvcParam::SvcParamKey::port}, {"dot"}, false, 853, std::string(), {ComboAddress("192.0.2.1")}, {ComboAddress("2001:db8::1")}, {}));
    /* 2 octet field for SvcPriority as an integer in network byte order */
    /* uncompressed, fully-qualified TargetName */
    /* list of SvcParams as:
       - 2 octet field containing the SvcParamKey as an integer in network byte order
       - 2 octet field containing the length of the SvcParamValue as an integer between 0 and 65535 in network byte order (but constrained by the RDATA and DNS message sizes)
       - an octet string of this length whose contents are in a format determined by the SvcParamKey
       SvcParamKeys SHALL appear in increasing numeric order
    */
    size_t expectedSize = (/* priority */ 2) + target.wirelength() + (/* mandatory */ 2 + 2 + 2) + (/* alpns with 1-byte length field for each value */ 2 + 2 + 4) + (/* no-alpn-default is false */ 0) + (/* port */ 2 + 2 + 2) + (/* ech */ 0) + (/* v4 hints */ 2 + 2 + 9) + (/* v6 hints */ 2 + 2 + 11);
    BOOST_CHECK_EQUAL(payload.size(), expectedSize);

    std::set<SvcParam> params;
    PacketReader pr(std::string_view(reinterpret_cast<const char*>(payload.data()), payload.size()), 0);
    BOOST_CHECK_EQUAL(pr.get16BitInt(), priority);

    /* we can't use getName() directly because it assumes that there has to be a dnsheader before the name */
    DNSName parsedTarget(reinterpret_cast<const char*>(payload.data()), payload.size(), pr.getPosition(), false /* uncompress */, nullptr /* qtype */, nullptr /* qclass */, nullptr /* consumed */, 0);
    pr.skip(parsedTarget.wirelength());
    BOOST_CHECK_EQUAL(target.toString(), parsedTarget.toString());

    pr.xfrSvcParamKeyVals(params);
    BOOST_REQUIRE_EQUAL(params.size(), 5U);
    auto param = params.begin();
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::mandatory);
    ++param;
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::alpn);
    ++param;
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::port);
    ++param;
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::ipv4hint);
    ++param;
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::ipv6hint);
  }

  {
    std::vector<uint8_t> payload;
    const uint16_t priority = 2;
    const std::string ech("whatever");
    const std::string dohParam("/dns-query{?dns}");

    BOOST_CHECK(generateSVCPayload(payload, priority, target, {SvcParam::SvcParamKey::port}, {"h2"}, true, 443, ech, {ComboAddress("192.0.2.2")}, {ComboAddress("2001:db8::2")}, {std::pair<uint16_t, std::string>(42, dohParam)}));

    size_t expectedSize = (/* priority */ 2) + target.wirelength() + (/* mandatory */ 2 + 2 + 2) + (/* alpns */ 2 + 2 + 3) + (/* no-alpn-default is true */ 2 + 2) + (/* port */ 2 + 2 + 2) + (/* ech */ 2 + 2 + ech.size()) + (/* v4 hints */ 2 + 2 + 9) + (/* v6 hints */ 2 + 2 + 11) + (/* doh parameter */ 2 + 2 + dohParam.size());
    BOOST_CHECK_EQUAL(payload.size(), expectedSize);

    std::set<SvcParam> params;
    PacketReader pr(std::string_view(reinterpret_cast<const char*>(payload.data()), payload.size()), 0);
    BOOST_CHECK_EQUAL(pr.get16BitInt(), priority);

    /* we can't use getName() directly because it assumes that there has to be a dnsheader before the name */
    DNSName parsedTarget(reinterpret_cast<const char*>(payload.data()), payload.size(), pr.getPosition(), false /* uncompress */, nullptr /* qtype */, nullptr /* qclass */, nullptr /* consumed */, 0);
    pr.skip(parsedTarget.wirelength());
    BOOST_CHECK_EQUAL(target.toString(), parsedTarget.toString());

    pr.xfrSvcParamKeyVals(params);
    BOOST_REQUIRE_EQUAL(params.size(), 8U);
    auto param = params.begin();
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::mandatory);
    ++param;
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::alpn);
    ++param;
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::no_default_alpn);
    ++param;
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::port);
    ++param;
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::ipv4hint);
    ++param;
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::ech);
    ++param;
    BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::ipv6hint);
    ++param;
    BOOST_CHECK_EQUAL(static_cast<uint16_t>(param->getKey()), 42U);
  }
}

BOOST_AUTO_TEST_CASE(test_Parsing)
{
  svcParamsLua_t params;
  params["mandatory"] = std::vector<std::pair<int, std::string>>({
    {1, "port"},
  });
  params["alpn"] = std::vector<std::pair<int, std::string>>({
    {1, "h2"},
  });
  params["noDefaultAlpn"] = static_cast<bool>(true);
  params["port"] = static_cast<uint16_t>(443);
  params["ipv4hint"] = std::vector<std::pair<int, std::string>>({
    {1, "192.0.2.1"},
  });
  params["ipv6hint"] = std::vector<std::pair<int, std::string>>({
    {1, "2001:db8::1"},
  });
  params["ech"] = std::string("test");

  auto parsed = parseSVCParameters(params);
  BOOST_CHECK(parsed.mandatoryParams == std::set<uint16_t>{SvcParam::SvcParamKey::port});
  BOOST_CHECK(parsed.alpns == std::vector<std::string>{"h2"});
  BOOST_CHECK(parsed.ipv4hints == std::vector<ComboAddress>{ComboAddress("192.0.2.1")});
  BOOST_CHECK(parsed.ipv6hints == std::vector<ComboAddress>{ComboAddress("2001:db8::1")});
  BOOST_CHECK_EQUAL(parsed.ech, "test");
  BOOST_CHECK_EQUAL(*parsed.port, 443);
  BOOST_CHECK_EQUAL(parsed.noDefaultAlpn, true);
}

BOOST_AUTO_TEST_SUITE_END()