]>
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 for(const auto& s
: getRawLabels()) {
173 ret
+= escapeLabel(s
) + separator
;
176 return ret
.substr(0, ret
.size()-!trailing
);
179 std::string
DNSName::toLogString() const
185 return toStringRootDot();
188 std::string
DNSName::toDNSString() const
191 throw std::out_of_range("Attempt to DNSString an unset dnsname");
193 return std::string(d_storage
.c_str(), d_storage
.length());
196 std::string
DNSName::toDNSStringLC() const
198 return toLower(toDNSString()); // label lengths are always < 'A'
202 * Get the length of the DNSName on the wire
204 * @return the total wirelength of the DNSName
206 size_t DNSName::wirelength() const {
207 return d_storage
.length();
210 // Are WE part of parent
211 bool DNSName::isPartOf(const DNSName
& parent
) const
213 if(parent
.empty() || empty())
214 throw std::out_of_range("empty dnsnames aren't part of anything");
216 if(parent
.d_storage
.size() > d_storage
.size())
219 // this is slightly complicated since we can't start from the end, since we can't see where a label begins/ends then
220 for(auto us
=d_storage
.cbegin(); us
<d_storage
.cend(); us
+=*us
+1) {
221 auto distance
= std::distance(us
,d_storage
.cend());
222 if (distance
< 0 || static_cast<size_t>(distance
) < parent
.d_storage
.size()) {
225 if (static_cast<size_t>(distance
) == parent
.d_storage
.size()) {
226 auto p
= parent
.d_storage
.cbegin();
227 for(; us
!= d_storage
.cend(); ++us
, ++p
) {
228 if(dns_tolower(*p
) != dns_tolower(*us
))
234 throw std::out_of_range("negative label length in dnsname");
240 DNSName
DNSName::makeRelative(const DNSName
& zone
) const
243 ret
.makeUsRelative(zone
);
244 return ret
.empty() ? zone
: ret
; // HACK FIXME400
246 void DNSName::makeUsRelative(const DNSName
& zone
)
248 if (isPartOf(zone
)) {
249 d_storage
.erase(d_storage
.size()-zone
.d_storage
.size());
250 d_storage
.append(1, (char)0); // put back the trailing 0
256 DNSName
DNSName::labelReverse() const
261 return *this; // we don't create the root automatically below
264 vector
<string
> l
=getRawLabels();
266 ret
.appendRawLabel(l
.back());
273 void DNSName::appendRawLabel(const std::string
& label
)
275 appendRawLabel(label
.c_str(), label
.length());
278 void DNSName::appendRawLabel(const char* start
, unsigned int length
)
281 throw std::range_error("no such thing as an empty label to append");
283 throw std::range_error("label too long to append");
284 if(d_storage
.size() + length
> 254) // reserve one byte for the label length
285 throw std::range_error("name too long to append");
287 if(d_storage
.empty()) {
288 d_storage
.append(1, (char)length
);
291 *d_storage
.rbegin()=(char)length
;
293 d_storage
.append(start
, length
);
294 d_storage
.append(1, (char)0);
297 void DNSName::prependRawLabel(const std::string
& label
)
300 throw std::range_error("no such thing as an empty label to prepend");
301 if(label
.size() > 63)
302 throw std::range_error("label too long to prepend");
303 if(d_storage
.size() + label
.size() > 254) // reserve one byte for the label length
304 throw std::range_error("name too long to prepend");
306 if(d_storage
.empty())
307 d_storage
.append(1, (char)0);
309 string_t
prep(1, (char)label
.size());
310 prep
.append(label
.c_str(), label
.size());
311 d_storage
= prep
+d_storage
;
314 bool DNSName::slowCanonCompare(const DNSName
& rhs
) const
316 auto ours
=getRawLabels(), rhsLabels
= rhs
.getRawLabels();
317 return std::lexicographical_compare(ours
.rbegin(), ours
.rend(), rhsLabels
.rbegin(), rhsLabels
.rend(), CIStringCompare());
320 vector
<string
> DNSName::getRawLabels() const
323 ret
.reserve(countLabels());
325 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) {
326 ret
.push_back({(const char*)p
+1, (size_t)*p
}); // XXX FIXME
331 std::string
DNSName::getRawLabel(unsigned int pos
) const
333 unsigned int currentPos
= 0;
334 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
++) {
335 if (currentPos
== pos
) {
336 return std::string((const char*)p
+1, (size_t)*p
);
340 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");
343 DNSName
DNSName::getLastLabel() const
350 bool DNSName::chopOff()
352 if(d_storage
.empty() || d_storage
[0]==0)
354 d_storage
.erase(0, (unsigned int)d_storage
[0]+1);
358 bool DNSName::isWildcard() const
360 if(d_storage
.size() < 2)
362 auto p
= d_storage
.begin();
363 return (*p
== 0x01 && *++p
== '*');
367 * Returns true if the DNSName is a valid RFC 1123 hostname, this function uses
368 * a regex on the string, so it is probably best not used when speed is essential.
370 bool DNSName::isHostname() const
372 static Regex hostNameRegex
= Regex("^(([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)\\.)+$");
373 return hostNameRegex
.match(this->toString());
376 unsigned int DNSName::countLabels() const
378 unsigned int count
=0;
379 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)
384 void DNSName::trimToLabels(unsigned int to
)
386 while(countLabels() > to
&& chopOff())
391 size_t hash_value(DNSName
const& d
)
396 string
DNSName::escapeLabel(const std::string
& label
)
399 ret
.reserve(label
.size()); // saves 15% on bulk .COM load
400 for(uint8_t p
: label
) {
405 else if(p
> 0x21 && p
< 0x7e)
406 ret
.append(1, (char)p
);
408 ret
+="\\" + (boost::format("%03d") % (unsigned int)p
).str();