]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsname.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <boost/format.hpp>
26 #include "dnswriter.hh"
29 #include <boost/functional/hash.hpp>
31 const DNSName
g_rootdnsname("."), g_wildcarddnsname("*");
34 in DNS label format, with trailing 0. W/o trailing 0, we are 'empty'
35 www.powerdns.com = 3www8powerdns3com0
38 std::ostream
& operator<<(std::ostream
&os
, const DNSName
& d
)
40 return os
<<d
.toLogString();
43 DNSName::DNSName(const char* p
)
45 if(p
[0]==0 || (p
[0]=='.' && p
[1]==0)) {
46 d_storage
.assign(1, (char)0);
48 if(!strchr(p
, '\\')) {
49 unsigned char lenpos
=0;
50 unsigned char labellen
=0;
51 size_t plen
=strlen(p
);
52 const char* const pbegin
=p
, *pend
=p
+plen
;
53 d_storage
.reserve(plen
+1);
54 for(auto iter
= pbegin
; iter
!= pend
; ) {
55 lenpos
= d_storage
.size();
57 throw std::runtime_error("Found . in wrong position in DNSName "+string(p
));
58 d_storage
.append(1, (char)0);
61 for(; iter
!= pend
&& *iter
!='.'; ++iter
) {
64 d_storage
.append(begiter
,iter
);
68 throw std::range_error("label too long to append");
70 if(iter
-pbegin
> 254) // reserve two bytes, one for length and one for the root label
71 throw std::range_error("name too long to append");
73 d_storage
[lenpos
]=labellen
;
75 d_storage
.append(1, (char)0);
78 d_storage
=segmentDNSNameRaw(p
);
79 if(d_storage
.size() > 255) {
80 throw std::range_error("name too long");
87 DNSName::DNSName(const char* pos
, int len
, int offset
, bool uncompress
, uint16_t* qtype
, uint16_t* qclass
, unsigned int* consumed
, uint16_t minOffset
)
90 throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset
)+ " >= "+std::to_string(len
)+")");
93 if(const void * fnd
=memchr(pos
+offset
, 0, len
-offset
)) {
94 d_storage
.reserve(2+(const char*)fnd
-(pos
+offset
));
98 packetParser(pos
, len
, offset
, uncompress
, qtype
, qclass
, consumed
, 0, minOffset
);
101 // this should be the __only__ dns name parser in PowerDNS.
102 void DNSName::packetParser(const char* qpos
, int len
, int offset
, bool uncompress
, uint16_t* qtype
, uint16_t* qclass
, unsigned int* consumed
, int depth
, uint16_t minOffset
)
104 const unsigned char* pos
=(const unsigned char*)qpos
;
105 unsigned char labellen
;
106 const unsigned char *opos
= pos
;
109 throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset
)+ " >= "+std::to_string(len
)+")");
110 if (offset
< (int) minOffset
)
111 throw std::range_error("Trying to read before the beginning of the buffer ("+std::to_string(offset
)+ " < "+std::to_string(minOffset
)+")");
113 const unsigned char* end
= pos
+ len
;
115 while((labellen
=*pos
++) && pos
< end
) { // "scan and copy"
116 if(labellen
>= 0xc0) {
118 throw std::range_error("Found compressed label, instructed not to follow");
121 int newpos
= (labellen
<< 8) + *(const unsigned char*)pos
;
123 if(newpos
< offset
) {
124 if(newpos
< (int) minOffset
)
125 throw std::range_error("Invalid label position during decompression ("+std::to_string(newpos
)+ " < "+std::to_string(minOffset
)+")");
127 throw std::range_error("Abort label decompression after 100 redirects");
128 packetParser((const char*)opos
, len
, newpos
, true, 0, 0, 0, depth
, minOffset
);
130 throw std::range_error("Found a forward reference during label decompression");
133 } else if(labellen
& 0xc0) {
134 throw std::range_error("Found an invalid label length in qname (only one of the first two bits is set)");
136 if (pos
+ labellen
< end
) {
137 appendRawLabel((const char*)pos
, labellen
);
140 throw std::range_error("Found an invalid label length in qname");
143 if(d_storage
.empty())
144 d_storage
.append(1, (char)0); // we just parsed the root
146 *consumed
= pos
- opos
- offset
;
149 throw std::range_error("Trying to read qtype past the end of the buffer ("+std::to_string((pos
- opos
) + 2)+ " > "+std::to_string(len
)+")");
151 *qtype
=(*(const unsigned char*)pos
)*256 + *((const unsigned char*)pos
+1);
156 throw std::range_error("Trying to read qclass past the end of the buffer ("+std::to_string((pos
- opos
) + 2)+ " > "+std::to_string(len
)+")");
158 *qclass
=(*(const unsigned char*)pos
)*256 + *((const unsigned char*)pos
+1);
162 std::string
DNSName::toString(const std::string
& separator
, const bool trailing
) const
165 throw std::out_of_range("Attempt to print an unset dnsname");
169 return trailing
? separator
: "";
172 ret
.reserve(d_storage
.size());
175 // iterate over the raw labels
176 const char* p
= d_storage
.c_str();
177 const char* end
= p
+ d_storage
.size();
179 while (p
< end
&& *p
) {
180 ret
+= escapeLabel(p
+ 1, static_cast<size_t>(*p
)) + separator
;
185 ret
.resize(ret
.size() - separator
.size());
190 std::string
DNSName::toLogString() const
196 return toStringRootDot();
199 std::string
DNSName::toDNSString() const
202 throw std::out_of_range("Attempt to DNSString an unset dnsname");
204 return std::string(d_storage
.c_str(), d_storage
.length());
207 std::string
DNSName::toDNSStringLC() const
209 return toLower(toDNSString()); // label lengths are always < 'A'
213 * Get the length of the DNSName on the wire
215 * @return the total wirelength of the DNSName
217 size_t DNSName::wirelength() const {
218 return d_storage
.length();
221 // Are WE part of parent
222 bool DNSName::isPartOf(const DNSName
& parent
) const
224 if(parent
.empty() || empty())
225 throw std::out_of_range("empty dnsnames aren't part of anything");
227 if(parent
.d_storage
.size() > d_storage
.size())
230 // this is slightly complicated since we can't start from the end, since we can't see where a label begins/ends then
231 for(auto us
=d_storage
.cbegin(); us
<d_storage
.cend(); us
+=*us
+1) {
232 auto distance
= std::distance(us
,d_storage
.cend());
233 if (distance
< 0 || static_cast<size_t>(distance
) < parent
.d_storage
.size()) {
236 if (static_cast<size_t>(distance
) == parent
.d_storage
.size()) {
237 auto p
= parent
.d_storage
.cbegin();
238 for(; us
!= d_storage
.cend(); ++us
, ++p
) {
239 if(dns_tolower(*p
) != dns_tolower(*us
))
245 throw std::out_of_range("negative label length in dnsname");
251 DNSName
DNSName::makeRelative(const DNSName
& zone
) const
254 ret
.makeUsRelative(zone
);
255 return ret
.empty() ? zone
: ret
; // HACK FIXME400
257 void DNSName::makeUsRelative(const DNSName
& zone
)
259 if (isPartOf(zone
)) {
260 d_storage
.erase(d_storage
.size()-zone
.d_storage
.size());
261 d_storage
.append(1, (char)0); // put back the trailing 0
267 DNSName
DNSName::getCommonLabels(const DNSName
& other
) const
271 const std::vector
<std::string
> ours
= getRawLabels();
272 const std::vector
<std::string
> others
= other
.getRawLabels();
274 for (size_t pos
= 0; ours
.size() > pos
&& others
.size() > pos
; pos
++) {
275 const std::string
& ourLabel
= ours
.at(ours
.size() - pos
- 1);
276 const std::string
& otherLabel
= others
.at(others
.size() - pos
- 1);
278 if (!pdns_iequals(ourLabel
, otherLabel
)) {
282 result
.prependRawLabel(ourLabel
);
288 DNSName
DNSName::labelReverse() const
293 return *this; // we don't create the root automatically below
296 vector
<string
> l
=getRawLabels();
298 ret
.appendRawLabel(l
.back());
305 void DNSName::appendRawLabel(const std::string
& label
)
307 appendRawLabel(label
.c_str(), label
.length());
310 void DNSName::appendRawLabel(const char* start
, unsigned int length
)
313 throw std::range_error("no such thing as an empty label to append");
315 throw std::range_error("label too long to append");
316 if(d_storage
.size() + length
> 254) // reserve one byte for the label length
317 throw std::range_error("name too long to append");
319 if(d_storage
.empty()) {
320 d_storage
.append(1, (char)length
);
323 *d_storage
.rbegin()=(char)length
;
325 d_storage
.append(start
, length
);
326 d_storage
.append(1, (char)0);
329 void DNSName::prependRawLabel(const std::string
& label
)
332 throw std::range_error("no such thing as an empty label to prepend");
333 if(label
.size() > 63)
334 throw std::range_error("label too long to prepend");
335 if(d_storage
.size() + label
.size() > 254) // reserve one byte for the label length
336 throw std::range_error("name too long to prepend");
338 if(d_storage
.empty())
339 d_storage
.append(1, (char)0);
341 string_t
prep(1, (char)label
.size());
342 prep
.append(label
.c_str(), label
.size());
343 d_storage
= prep
+d_storage
;
346 bool DNSName::slowCanonCompare(const DNSName
& rhs
) const
348 auto ours
=getRawLabels(), rhsLabels
= rhs
.getRawLabels();
349 return std::lexicographical_compare(ours
.rbegin(), ours
.rend(), rhsLabels
.rbegin(), rhsLabels
.rend(), CIStringCompare());
352 vector
<std::string
> DNSName::getRawLabels() const
354 vector
<std::string
> ret
;
355 ret
.reserve(countLabels());
357 for(const unsigned char* p
= (const unsigned char*) d_storage
.c_str(); p
< ((const unsigned char*) d_storage
.c_str()) + d_storage
.size() && *p
; p
+=*p
+1) {
358 ret
.push_back({(const char*)p
+1, (size_t)*p
}); // XXX FIXME
363 std::string
DNSName::getRawLabel(unsigned int pos
) const
365 unsigned int currentPos
= 0;
366 for(const unsigned char* p
= (const unsigned char*) d_storage
.c_str(); p
< ((const unsigned char*) d_storage
.c_str()) + d_storage
.size() && *p
; p
+=*p
+1, currentPos
++) {
367 if (currentPos
== pos
) {
368 return std::string((const char*)p
+1, (size_t)*p
);
372 throw std::out_of_range("trying to get label at position "+std::to_string(pos
)+" of a DNSName that only has "+std::to_string(currentPos
)+" labels");
375 DNSName
DNSName::getLastLabel() const
382 bool DNSName::chopOff()
384 if(d_storage
.empty() || d_storage
[0]==0)
386 d_storage
.erase(0, (unsigned int)d_storage
[0]+1);
390 bool DNSName::isWildcard() const
392 if(d_storage
.size() < 2)
394 auto p
= d_storage
.begin();
395 return (*p
== 0x01 && *++p
== '*');
399 * Returns true if the DNSName is a valid RFC 1123 hostname, this function uses
400 * a regex on the string, so it is probably best not used when speed is essential.
402 bool DNSName::isHostname() const
404 static Regex hostNameRegex
= Regex("^(([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)\\.)+$");
405 return hostNameRegex
.match(this->toString());
408 unsigned int DNSName::countLabels() const
410 unsigned int count
=0;
411 const unsigned char* p
= reinterpret_cast<const unsigned char*>(d_storage
.c_str());
412 const unsigned char* end
= reinterpret_cast<const unsigned char*>(p
+ d_storage
.size());
414 while (p
< end
&& *p
) {
421 void DNSName::trimToLabels(unsigned int to
)
423 while(countLabels() > to
&& chopOff())
428 size_t hash_value(DNSName
const& d
)
433 string
DNSName::escapeLabel(const std::string
& label
)
435 return escapeLabel(label
.c_str(), label
.size());
438 string
DNSName::escapeLabel(const char* orig
, size_t len
)
445 auto p
= static_cast<uint8_t>(orig
[pos
]);
450 else if(p
> 0x20 && p
< 0x7f)
451 ret
.append(1, (char)p
);
453 ret
+="\\" + (boost::format("%03d") % (unsigned int)p
).str();