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