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 "ednsoptions.hh"
26 bool getNextEDNSOption(const char* data
, size_t dataLen
, uint16_t& optionCode
, uint16_t& optionLen
)
28 if (data
== nullptr || dataLen
< (sizeof(uint16_t) + sizeof(uint16_t))) {
33 const uint8_t* p
= reinterpret_cast<const uint8_t*>(data
);
35 optionCode
= (static_cast<uint16_t>(p
[pos
]) * 256) + p
[pos
+ 1];
36 pos
+= EDNS_OPTION_CODE_SIZE
;
38 optionLen
= (static_cast<uint16_t>(p
[pos
]) * 256) + p
[pos
+ 1];
39 pos
+= EDNS_OPTION_LENGTH_SIZE
;
45 /* extract the position (relative to the optRR pointer!) and size of a specific EDNS0 option from a pointer on the beginning rdLen of the OPT RR */
46 int getEDNSOption(const char* optRR
, const size_t len
, uint16_t wantedOption
, size_t* optionValuePosition
, size_t * optionValueSize
)
48 assert(optRR
!= nullptr);
49 assert(optionValuePosition
!= nullptr);
50 assert(optionValueSize
!= nullptr);
52 if (len
< DNS_RDLENGTH_SIZE
)
55 const uint16_t rdLen
= (((unsigned char) optRR
[pos
]) * 256) + ((unsigned char) optRR
[pos
+1]);
57 pos
+= DNS_RDLENGTH_SIZE
;
58 if ((pos
+ rdLen
) > len
) {
62 while(len
>= (pos
+ EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
) &&
63 rdLen
>= (rdPos
+ EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
)) {
66 if (!getNextEDNSOption(optRR
+ pos
, len
-pos
, optionCode
, optionLen
)) {
70 pos
+= EDNS_OPTION_CODE_SIZE
;
71 rdPos
+= EDNS_OPTION_CODE_SIZE
;
72 pos
+= EDNS_OPTION_LENGTH_SIZE
;
73 rdPos
+= EDNS_OPTION_LENGTH_SIZE
;
75 if (optionLen
> (rdLen
- rdPos
) || optionLen
> (len
- pos
)) {
79 if (optionCode
== wantedOption
) {
80 *optionValuePosition
= pos
- (EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
);
81 *optionValueSize
= optionLen
+ EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
;
85 /* skip this option */
94 /* extract all EDNS0 options from a pointer on the beginning rdLen of the OPT RR */
95 int getEDNSOptions(const char* optRR
, const size_t len
, EDNSOptionViewMap
& options
)
97 assert(optRR
!= nullptr);
99 if (len
< DNS_RDLENGTH_SIZE
)
102 const uint16_t rdLen
= (((unsigned char) optRR
[pos
]) * 256) + ((unsigned char) optRR
[pos
+1]);
104 pos
+= DNS_RDLENGTH_SIZE
;
105 if ((pos
+ rdLen
) > len
) {
109 while(len
>= (pos
+ EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
) &&
110 rdLen
>= (rdPos
+ EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
)) {
111 const uint16_t optionCode
= (((unsigned char) optRR
[pos
]) * 256) + ((unsigned char) optRR
[pos
+1]);
112 pos
+= EDNS_OPTION_CODE_SIZE
;
113 rdPos
+= EDNS_OPTION_CODE_SIZE
;
114 const uint16_t optionLen
= (((unsigned char) optRR
[pos
]) * 256) + ((unsigned char) optRR
[pos
+1]);
115 pos
+= EDNS_OPTION_LENGTH_SIZE
;
116 rdPos
+= EDNS_OPTION_LENGTH_SIZE
;
117 if (optionLen
> (rdLen
- rdPos
) || optionLen
> (len
- pos
))
120 EDNSOptionViewValue value
;
121 value
.content
= optRR
+ pos
;
122 value
.size
= optionLen
;
123 options
[optionCode
].values
.push_back(value
);
125 /* skip this option */
133 bool getEDNSOptionsFromContent(const std::string
& content
, std::vector
<std::pair
<uint16_t, std::string
>>& options
)
137 const size_t contentLength
= content
.size();
139 while (pos
< contentLength
&& (contentLength
- pos
) >= (EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
)) {
140 code
= (static_cast<unsigned char>(content
.at(pos
)) * 256) + static_cast<unsigned char>(content
.at(pos
+1));
141 pos
+= EDNS_OPTION_CODE_SIZE
;
142 len
= (static_cast<unsigned char>(content
.at(pos
)) * 256) + static_cast<unsigned char>(content
.at(pos
+1));
143 pos
+= EDNS_OPTION_LENGTH_SIZE
;
145 if (pos
> contentLength
|| len
> (contentLength
- pos
)) {
149 options
.emplace_back(code
, std::string(&content
.at(pos
), len
));
156 void generateEDNSOption(uint16_t optionCode
, const std::string
& payload
, std::string
& res
)
158 const uint16_t ednsOptionCode
= htons(optionCode
);
159 const uint16_t payloadLen
= htons(payload
.length());
160 res
.append((const char *) &ednsOptionCode
, sizeof ednsOptionCode
);
161 res
.append((const char *) &payloadLen
, sizeof payloadLen
);