]>
Commit | Line | Data |
---|---|---|
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 | 27 | class DNSName |
28 | { | |
29 | public: | |
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 | 83 | private: |
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 | 96 | size_t hash_value(DNSName const& d); |
6d8bc3c6 | 97 | |
ddb7e6c6 | 98 | inline char dns2_tolower(char c) |
99 | { | |
100 | if(c>='A' && c<='Z') | |
101 | c+='a'-'A'; | |
102 | return c; | |
103 | } | |
104 | ||
105 | ||
106 | inline 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 | 172 | struct 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 | 180 | inline 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 */ | |
189 | struct 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 | |
252 | std::ostream & operator<<(std::ostream &os, const DNSName& d); |