]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsname.hh
namespace sanitation
[thirdparty/pdns.git] / pdns / dnsname.hh
CommitLineData
3c115e0f 1#pragma once
2#include <string>
97e5c6bc 3#include <vector>
ceee6652 4#include <set>
97e5c6bc 5#include <deque>
ceee6652 6#include <strings.h>
0fae5c1e 7#include <stdexcept>
ddb7e6c6 8
76cca09f 9uint32_t burtleCI(const unsigned char* k, uint32_t lengh, uint32_t init);
10
675fa24c 11// #include "dns.hh"
7abbc40f 12// #include "logger.hh"
3c115e0f 13
8ca15224 14//#include <ext/vstring.h>
d8b778ff 15
3c115e0f 16/* Quest in life:
17 accept escaped ascii presentations of DNS names and store them "natively"
18 accept a DNS packet with an offset, and extract a DNS name from it
19 build up DNSNames with prepend and append of 'raw' unescaped labels
20
21 Be able to turn them into ASCII and "DNS name in a packet" again on request
22
23 Provide some common operators for comparison, detection of being part of another domain
24
25 NOTE: For now, everything MUST be . terminated, otherwise it is an error
26*/
27
561434a6 28
3c115e0f 29class DNSName
30{
31public:
ae14c1f3 32 DNSName() {} //!< Constructs an *empty* DNSName, NOT the root!
8171ab83 33 explicit DNSName(const char* p); //!< Constructs from a human formatted, escaped presentation
34 explicit DNSName(const std::string& str) : DNSName(str.c_str()) {} //!< Constructs from a human formatted, escaped presentation
35 DNSName(const char* p, int len, int offset, bool uncompress, uint16_t* qtype=0, uint16_t* qclass=0, unsigned int* consumed=0); //!< Construct from a DNS Packet, taking the first question if offset=12
3c115e0f 36
37 bool isPartOf(const DNSName& rhs) const; //!< Are we part of the rhs name?
ae14c1f3 38 bool operator==(const DNSName& rhs) const; //!< DNS-native comparison (case insensitive) - empty compares to empty
5fca2e23
PD
39 bool operator!=(const DNSName& other) const { return !(*this == other); }
40
a61e8e59
KM
41 std::string toString(const std::string& separator=".", const bool trailing=true) const; //!< Our human-friendly, escaped, representation
42 std::string toStringNoDot() const { return toString(".", false); }
3c115e0f 43 std::string toDNSString() const; //!< Our representation in DNS native format
44 void appendRawLabel(const std::string& str); //!< Append this unescaped label
45 void prependRawLabel(const std::string& str); //!< Prepend this unescaped label
97e5c6bc 46 std::vector<std::string> getRawLabels() const; //!< Individual raw unescaped labels
3c115e0f 47 bool chopOff(); //!< Turn www.powerdns.com. into powerdns.com., returns false for .
a61e8e59 48 DNSName makeRelative(const DNSName& zone) const;
8ca15224 49 void makeUsRelative(const DNSName& zone);
a61e8e59 50 DNSName labelReverse() const;
8aa5a28c 51 bool isWildcard() const;
97e5c6bc 52 unsigned int countLabels() const;
3155c04a 53 size_t wirelength() const; //!< Number of total bytes in the name
ae14c1f3 54 bool empty() const { return d_storage.empty(); }
55 bool isRoot() const { return d_storage.size()==1 && d_storage[0]==0; }
56 void clear() { d_storage.clear(); }
c9262563 57 void trimToLabels(unsigned int);
76cca09f 58 size_t hash() const
59 {
60 return burtleCI((const unsigned char*)d_storage.c_str(), d_storage.size(), 0);
61 }
6dcedea6 62 DNSName& operator+=(const DNSName& rhs)
63 {
ae14c1f3 64 if(d_storage.size() + rhs.d_storage.size() > 256) // reserve one byte for the root label
ac7921b0 65 throw std::range_error("name too long");
ae14c1f3 66 if(rhs.empty())
67 return *this;
68
69 if(d_storage.empty())
70 d_storage+=rhs.d_storage;
71 else
72 d_storage.replace(d_storage.length()-1, rhs.d_storage.length(), rhs.d_storage);
ac7921b0 73
6dcedea6 74 return *this;
75 }
c9262563 76
6d8bc3c6 77 bool operator<(const DNSName& rhs) const // this delivers _some_ kind of ordering, but not one useful in a DNS context. Really fast though.
c9262563 78 {
97e5c6bc 79 return std::lexicographical_compare(d_storage.rbegin(), d_storage.rend(),
80 rhs.d_storage.rbegin(), rhs.d_storage.rend(),
81 [](const char& a, const char& b) {
82 return tolower(a) < tolower(b);
6d8bc3c6 83 }); // note that this is case insensitive, including on the label lengths
c9262563 84 }
85
dab7ccbb
PD
86 template<class Archive>
87 void serialize(Archive &ar, const unsigned int version)
88 {
89 ar & d_storage;
90 }
b1b0f079 91
ddb7e6c6 92 inline bool canonCompare(const DNSName& rhs) const;
f3da403d 93 bool slowCanonCompare(const DNSName& rhs) const;
3c115e0f 94private:
8ca15224 95 //typedef __gnu_cxx::__sso_string string_t;
d8b778ff 96 typedef std::string string_t;
f3da403d 97
d8b778ff 98 string_t d_storage;
520eb5a0 99
4c6ce4f1 100 void packetParser(const char* p, int len, int offset, bool uncompress, uint16_t* qtype=0, uint16_t* qclass=0, unsigned int* consumed=0, int depth=0);
3c115e0f 101 static std::string escapeLabel(const std::string& orig);
102 static std::string unescapeLabel(const std::string& orig);
103};
ceee6652 104
5e1031cd 105size_t hash_value(DNSName const& d);
6d8bc3c6 106
ddb7e6c6 107inline char dns2_tolower(char c)
108{
109 if(c>='A' && c<='Z')
110 c+='a'-'A';
111 return c;
112}
113
114
115inline bool DNSName::canonCompare(const DNSName& rhs) const
116{
117 // 01234567890abcd
118 // us: 1a3www4ds9a2nl
119 // rhs: 3www6online3com
120 // to compare, we start at the back, is nl < com? no -> done
121 //
122 // 0,2,6,a
123 // 0,4,a
124
125 uint8_t ourpos[64], rhspos[64];
126 uint8_t ourcount=0, rhscount=0;
127 //cout<<"Asked to compare "<<toString()<<" to "<<rhs.toString()<<endl;
ae14c1f3 128 for(const unsigned char* p = (const unsigned char*)d_storage.c_str(); p < (const unsigned char*)d_storage.c_str() + d_storage.size() && *p && ourcount < sizeof(ourpos); p+=*p+1)
f3da403d 129 ourpos[ourcount++]=(p-(const unsigned char*)d_storage.c_str());
ae14c1f3 130 for(const unsigned char* p = (const unsigned char*)rhs.d_storage.c_str(); p < (const unsigned char*)rhs.d_storage.c_str() + rhs.d_storage.size() && *p && rhscount < sizeof(rhspos); p+=*p+1)
f3da403d 131 rhspos[rhscount++]=(p-(const unsigned char*)rhs.d_storage.c_str());
ddb7e6c6 132
133 if(ourcount == sizeof(ourpos) || rhscount==sizeof(rhspos)) {
134 return slowCanonCompare(rhs);
135 }
136
137 for(;;) {
138 if(ourcount == 0 && rhscount != 0)
139 return true;
140 if(ourcount == 0 && rhscount == 0)
141 return false;
142 if(ourcount !=0 && rhscount == 0)
143 return false;
144 ourcount--;
145 rhscount--;
146
ddb7e6c6 147 bool res=std::lexicographical_compare(
148 d_storage.c_str() + ourpos[ourcount] + 1,
149 d_storage.c_str() + ourpos[ourcount] + 1 + *(d_storage.c_str() + ourpos[ourcount]),
150 rhs.d_storage.c_str() + rhspos[rhscount] + 1,
151 rhs.d_storage.c_str() + rhspos[rhscount] + 1 + *(rhs.d_storage.c_str() + rhspos[rhscount]),
152 [](const char& a, const char& b) {
153 return dns2_tolower(a) < dns2_tolower(b);
154 });
155
156 // cout<<"Forward: "<<res<<endl;
157 if(res)
158 return true;
159
160 res=std::lexicographical_compare( rhs.d_storage.c_str() + rhspos[rhscount] + 1,
161 rhs.d_storage.c_str() + rhspos[rhscount] + 1 + *(rhs.d_storage.c_str() + rhspos[rhscount]),
162 d_storage.c_str() + ourpos[ourcount] + 1,
163 d_storage.c_str() + ourpos[ourcount] + 1 + *(d_storage.c_str() + ourpos[ourcount]),
164 [](const char& a, const char& b) {
165 return dns2_tolower(a) < dns2_tolower(b);
166 });
167 // cout<<"Reverse: "<<res<<endl;
168 if(res)
169 return false;
170 }
171 return false;
172}
173
174
6d8bc3c6 175struct CanonDNSNameCompare: public std::binary_function<DNSName, DNSName, bool>
176{
177 bool operator()(const DNSName&a, const DNSName& b) const
178 {
179 return a.canonCompare(b);
180 }
181};
182
6dcedea6 183inline DNSName operator+(const DNSName& lhs, const DNSName& rhs)
184{
185 DNSName ret=lhs;
186 ret += rhs;
187 return ret;
188}
ceee6652 189
190/* Quest in life: serve as a rapid block list. If you add a DNSName to a root SuffixMatchNode,
191 anything part of that domain will return 'true' in check */
192struct SuffixMatchNode
193{
194 SuffixMatchNode(const std::string& name_="", bool endNode_=false) : name(name_), endNode(endNode_)
195 {}
196 std::string name;
7c0860e1 197 std::string d_human;
ceee6652 198 mutable std::set<SuffixMatchNode> children;
1b6c9527 199 mutable bool endNode;
ceee6652 200 bool operator<(const SuffixMatchNode& rhs) const
201 {
202 return strcasecmp(name.c_str(), rhs.name.c_str()) < 0;
203 }
204
205 void add(const DNSName& name)
206 {
7c0860e1 207 if(!d_human.empty())
208 d_human.append(", ");
209 d_human += name.toString();
ceee6652 210 add(name.getRawLabels());
211 }
212
97e5c6bc 213 void add(std::vector<std::string> labels) const
ceee6652 214 {
215 if(labels.empty()) { // this allows insertion of the root
216 endNode=true;
217 }
218 else if(labels.size()==1) {
0b7d7191 219 children.insert(SuffixMatchNode(*labels.begin(), true));
ceee6652 220 }
221 else {
0b7d7191 222 auto res=children.insert(SuffixMatchNode(*labels.rbegin(), false));
ceee6652 223 labels.pop_back();
224 res.first->add(labels);
225 }
226 }
227
228 bool check(const DNSName& name) const
229 {
08199f78 230 if(children.empty()) // speed up empty set
231 return endNode;
ceee6652 232 return check(name.getRawLabels());
233 }
234
97e5c6bc 235 bool check(std::vector<std::string> labels) const
ceee6652 236 {
237 if(labels.empty()) // optimization
238 return endNode;
239
0b7d7191 240 SuffixMatchNode smn(*labels.rbegin());
ceee6652 241 auto child = children.find(smn);
242 if(child == children.end())
243 return endNode;
244 labels.pop_back();
245 return child->check(labels);
246 }
7c0860e1 247
248 std::string toString() const
249 {
250 return d_human;
251 }
ceee6652 252
253};
8171ab83 254
255std::ostream & operator<<(std::ostream &os, const DNSName& d);