diff --git a/llarp/dns/question.cpp b/llarp/dns/question.cpp index 2f7082af5..630a490ea 100644 --- a/llarp/dns/question.cpp +++ b/llarp/dns/question.cpp @@ -49,6 +49,22 @@ namespace llarp return true; } + bool + Question::IsName(const std::string& other) const + { + // does other have a . at the end? + if(other.find_last_of('.') == (other.size() - 1)) + return other == qname; + else // no, add it and retry + return IsName(other + "."); + } + + std::string + Question::Name() const + { + return qname.substr(0, qname.find_last_of('.')); + } + std::ostream& Question::print(std::ostream& stream, int level, int spaces) const { diff --git a/llarp/dns/question.hpp b/llarp/dns/question.hpp index ac93ea422..4bad44441 100644 --- a/llarp/dns/question.hpp +++ b/llarp/dns/question.hpp @@ -36,6 +36,14 @@ namespace llarp Name_t qname; QType_t qtype; QClass_t qclass; + + /// determine if we match a name + bool + IsName(const std::string& other) const; + + /// return qname with no trailing . + std::string + Name() const; }; inline std::ostream& diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 0c60a2c3b..80d5d92b8 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -73,12 +73,11 @@ namespace llarp || msg.questions[0].qtype == dns::qTypeCNAME || msg.questions[0].qtype == dns::qTypeAAAA) { - // hook for forward dns or cname when using snode tld - if(msg.questions[0].qname.find(".snode.") - == (msg.questions[0].qname.size() - 7)) + if(msg.questions[0].IsName("localhost.loki") + || msg.questions[0].IsName("random.snode")) return true; - return msg.questions[0].qname == "localhost.loki" - || msg.questions[0].qname == "localhost.loki."; + service::Address addr; + return addr.FromString(msg.questions[0].Name(), ".snode"); } else return false; @@ -113,8 +112,7 @@ namespace llarp } else if(msg.questions[0].qtype == dns::qTypeCNAME) { - if(msg.questions[0].qname == "random.snode" - || msg.questions[0].qname == "random.snode.") + if(msg.questions[0].IsName("random.snode")) { RouterID random; if(GetRouter()->GetRandomGoodRouter(random)) @@ -122,8 +120,7 @@ namespace llarp else msg.AddNXReply(); } - else if(msg.questions[0].qname == "localhost.loki" - || msg.questions[0].qname == "localhost.loki.") + else if(msg.questions[0].IsName("localhost.loki")) { RouterID us = m_Router->pubkey(); msg.AddAReply(us.ToString(), 1); @@ -135,8 +132,7 @@ namespace llarp || msg.questions[0].qtype == dns::qTypeAAAA) { const bool isV6 = msg.questions[0].qtype == dns::qTypeAAAA; - if(msg.questions[0].qname == "random.snode" - || msg.questions[0].qname == "random.snode.") + if(msg.questions[0].IsName("random.snode")) { RouterID random; if(GetRouter()->GetRandomGoodRouter(random)) @@ -146,8 +142,7 @@ namespace llarp reply(msg); return true; } - if(msg.questions[0].qname == "localhost.loki." - || msg.questions[0].qname == "localhost.loki") + if(msg.questions[0].IsName("localhost.loki")) { msg.AddINReply(GetIfAddr(), isV6); reply(msg); @@ -155,7 +150,7 @@ namespace llarp } // forward dns for snode RouterID r; - if(r.FromString(msg.questions[0].qname)) + if(r.FromString(msg.questions[0].Name())) { huint32_t ip; PubKey pubKey(r); diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index b30fe1fb7..ee25a7ed0 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -233,15 +233,13 @@ namespace llarp static bool is_random_snode(const dns::Message &msg) { - return msg.questions[0].qname == "random.snode" - || msg.questions[0].qname == "random.snode."; + return msg.questions[0].IsName("random.snode"); } static bool is_localhost_loki(const dns::Message &msg) { - return msg.questions[0].qname == "localhost.loki" - || msg.questions[0].qname == "localhost.loki."; + return msg.questions[0].IsName("localhost.loki"); } bool @@ -255,7 +253,7 @@ namespace llarp llarp::LogWarn("bad number of dns questions: ", msg.questions.size()); return false; } - std::string qname = msg.questions[0].qname; + const std::string qname = msg.questions[0].Name(); if(msg.questions[0].qtype == dns::qTypeMX) { // mx record @@ -340,8 +338,8 @@ namespace llarp using service::OutboundContext; return EnsurePathToService( addr, - [=](const Address &remote, OutboundContext *ctx) { - SendDNSReply(remote, ctx, replyMsg, reply, false, isV6); + [=](const Address &, OutboundContext *ctx) { + SendDNSReply(addr, ctx, replyMsg, reply, false, isV6); }, 2000); } @@ -350,9 +348,8 @@ namespace llarp { dns::Message *replyMsg = new dns::Message(std::move(msg)); EnsurePathToSNode( - addr.as_array(), - [=](const RouterID &remote, exit::BaseSession_ptr s) { - SendDNSReply(remote, s, replyMsg, reply, true, isV6); + addr.as_array(), [=](const RouterID &, exit::BaseSession_ptr s) { + SendDNSReply(addr, s, replyMsg, reply, true, isV6); }); return true; } @@ -407,18 +404,17 @@ namespace llarp if(msg.questions.size() == 1) { // hook random.snode - if(msg.questions[0].qname == "random.snode" - || msg.questions[0].qname == "random.snode.") + if(msg.questions[0].IsName("random.snode")) return true; // hook localhost.loki - if(msg.questions[0].qname == "localhost.loki" - || msg.questions[0].qname == "localhost.loki.") + if(msg.questions[0].IsName("localhost.loki")) return true; + const std::string name = msg.questions[0].Name(); // hook .loki - if(addr.FromString(msg.questions[0].qname, ".loki")) + if(addr.FromString(name, ".loki")) return true; // hook .snode - if(addr.FromString(msg.questions[0].qname, ".snode")) + if(addr.FromString(name, ".snode")) return true; // hook any ranges we own if(msg.questions[0].qtype == llarp::dns::qTypePTR) diff --git a/llarp/service/address.cpp b/llarp/service/address.cpp index 3622487fa..7752f5e94 100644 --- a/llarp/service/address.cpp +++ b/llarp/service/address.cpp @@ -6,25 +6,62 @@ namespace llarp { namespace service { + const std::vector< std::string > Address::AllowedTLDs = {".loki", ".snode"}; + + bool + Address::PermitTLD(const char* tld) + { + std::string gtld(tld); + std::transform(gtld.begin(), gtld.end(), gtld.begin(), ::tolower); + for(const auto& allowed : AllowedTLDs) + if(allowed == tld) + return true; + return false; + } + std::string Address::ToString(const char* tld) const { + if(!PermitTLD(tld)) + return ""; char tmp[(1 + 32) * 2] = {0}; std::string str = Base32Encode(*this, tmp); + if(subdomain.size()) + str = subdomain + "." + str; return str + tld; } bool Address::FromString(const std::string& str, const char* tld) { - auto pos = str.find(tld); + if(!PermitTLD(tld)) + return false; + static auto lowercase = [](const std::string s, + bool stripDots) -> std::string { + std::string ret(s.size(), ' '); + std::transform(s.begin(), s.end(), ret.begin(), + [stripDots](const char& ch) -> char { + if(ch == '.' && stripDots) + return 0; + return ::tolower(ch); + }); + return ret.substr(0, ret.find_last_of(' ')); + }; + const auto pos = str.find_last_of('.'); if(pos == std::string::npos) return false; + if(str.substr(pos) != tld) + return false; auto sub = str.substr(0, pos); + // set subdomains if they are there + const auto idx = sub.find_last_of('.'); + if(idx != std::string::npos) + { + subdomain = lowercase(sub.substr(0, idx), false); + sub = sub.substr(idx + 1); + } // make sure it's lowercase - std::transform(sub.begin(), sub.end(), sub.begin(), ::tolower); - - return Base32Decode(sub, *this); + return Base32Decode(lowercase(sub, true), *this); } } // namespace service } // namespace llarp diff --git a/llarp/service/address.hpp b/llarp/service/address.hpp index b251d6cc8..60d20200d 100644 --- a/llarp/service/address.hpp +++ b/llarp/service/address.hpp @@ -16,6 +16,18 @@ namespace llarp /// Snapp/Snode Address struct Address : public AlignedBuffer< 32 > { + /// if parsed using FromString this contains the subdomain + /// this member is not used when comparing it's extra data for dns + std::string subdomain; + + /// list of whitelisted gtld to permit + static const std::vector< std::string > AllowedTLDs; + + /// return true if we permit using this tld + /// otherwise return false + static bool + PermitTLD(const char* tld); + std::string ToString(const char* tld = ".loki") const; @@ -30,7 +42,8 @@ namespace llarp { } - Address(const Address& other) : AlignedBuffer< SIZE >(other.as_array()) + Address(const Address& other) + : AlignedBuffer< SIZE >(other.as_array()), subdomain(other.subdomain) { } diff --git a/llarp/util/encode.hpp b/llarp/util/encode.hpp index 0dda44703..dee452a89 100644 --- a/llarp/util/encode.hpp +++ b/llarp/util/encode.hpp @@ -42,9 +42,9 @@ namespace llarp Base32Decode(const Stack& stack, V& value) { int tmp = 0, bits = 0; - size_t ret = 0; - size_t len = Base32DecodeSize(value.size()); - size_t outLen = value.size(); + size_t idx = 0; + const size_t len = Base32DecodeSize(value.size()); + const size_t outLen = value.size(); for(size_t i = 0; i < len; i++) { char ch = stack[i]; @@ -57,21 +57,21 @@ namespace llarp } else { - return ret == outLen; + return idx == outLen; } tmp |= ch; bits += 5; if(bits >= 8) { - if(ret >= outLen) + if(idx >= outLen) return false; - value[ret] = tmp >> (bits - 8); + value[idx] = tmp >> (bits - 8); bits -= 8; - ret++; + idx++; } tmp <<= 5; } - return true; + return idx == outLen; } /// adapted from i2pd diff --git a/test/service/test_llarp_service_address.cpp b/test/service/test_llarp_service_address.cpp index e8d313830..01d0ba526 100644 --- a/test/service/test_llarp_service_address.cpp +++ b/test/service/test_llarp_service_address.cpp @@ -8,8 +8,32 @@ struct ServiceAddressTest : public ::testing::Test "8zfiwpgonsu5zpddpxwdurxyb19x6r96xy4qbikff99jwsziws9y.snode"; const std::string loki = "7okic5x5do3uh3usttnqz9ek3uuoemdrwzto1hciwim9f947or6y.loki"; + const std::string sub = "lokinet.test"; + const std::string invalid = + "7okic5x5do3uh3usttnqz9ek3uuoemdrwzto1hciwim9f947or6y.net"; }; +TEST_F(ServiceAddressTest, TestParseBadTLD) +{ + llarp::service::Address addr; + ASSERT_FALSE(addr.FromString(snode, ".net")); + ASSERT_FALSE(addr.FromString(invalid, ".net")); +} + +TEST_F(ServiceAddressTest, TestParseBadTLDAppenedOnEnd) +{ + llarp::service::Address addr; + const std::string bad = loki + ".net"; + ASSERT_FALSE(addr.FromString(bad, ".net")); +} + +TEST_F(ServiceAddressTest, TestParseBadTLDAppenedOnEndWithSubdomain) +{ + llarp::service::Address addr; + const std::string bad = sub + "." + loki + ".net"; + ASSERT_FALSE(addr.FromString(bad, ".net")); +} + TEST_F(ServiceAddressTest, TestParseSNodeNotLoki) { llarp::service::Address addr; @@ -23,3 +47,21 @@ TEST_F(ServiceAddressTest, TestParseLokiNotSNode) ASSERT_FALSE(addr.FromString(loki, ".snode")); ASSERT_TRUE(addr.FromString(loki, ".loki")); } + +TEST_F(ServiceAddressTest, TestParseLokiWithSubdomain) +{ + llarp::service::Address addr; + const std::string addr_str = sub + "." + loki; + ASSERT_TRUE(addr.FromString(addr_str, ".loki")); + ASSERT_EQ(addr.subdomain, sub); + ASSERT_EQ(addr.ToString(), addr_str); +}; + +TEST_F(ServiceAddressTest, TestParseSnodeWithSubdomain) +{ + llarp::service::Address addr; + const std::string addr_str = sub + "." + snode; + ASSERT_TRUE(addr.FromString(addr_str, ".snode")); + ASSERT_EQ(addr.subdomain, sub); + ASSERT_EQ(addr.ToString(".snode"), addr_str); +};