]>
Commit | Line | Data |
---|---|---|
12471842 PL |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
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. | |
8 | * | |
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. | |
12 | * | |
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. | |
17 | * | |
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. | |
21 | */ | |
5c3b5e7f RG |
22 | #include "dns.hh" |
23 | #include "ednsoptions.hh" | |
24 | #include "iputils.hh" | |
25 | ||
fa980c59 RG |
26 | bool getNextEDNSOption(const char* data, size_t dataLen, uint16_t& optionCode, uint16_t& optionLen) |
27 | { | |
28 | if (data == nullptr || dataLen < (sizeof(uint16_t) + sizeof(uint16_t))) { | |
29 | return false; | |
30 | } | |
31 | ||
32 | size_t pos = 0; | |
3e35ea9e | 33 | const uint8_t* p = reinterpret_cast<const uint8_t*>(data); |
fa980c59 | 34 | |
3e35ea9e | 35 | optionCode = (static_cast<uint16_t>(p[pos]) * 256) + p[pos + 1]; |
fa980c59 RG |
36 | pos += EDNS_OPTION_CODE_SIZE; |
37 | ||
3e35ea9e | 38 | optionLen = (static_cast<uint16_t>(p[pos]) * 256) + p[pos + 1]; |
fa980c59 | 39 | pos += EDNS_OPTION_LENGTH_SIZE; |
90ee15b3 | 40 | (void) pos; |
fa980c59 RG |
41 | |
42 | return true; | |
43 | } | |
44 | ||
341d2553 RG |
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) | |
5c3b5e7f | 47 | { |
14f437f2 RG |
48 | if (optRR == nullptr || optionValuePosition == nullptr || optionValueSize == nullptr) { |
49 | return EINVAL; | |
50 | } | |
51 | ||
5c3b5e7f | 52 | size_t pos = 0; |
14f437f2 | 53 | if (len < DNS_RDLENGTH_SIZE) { |
5c3b5e7f | 54 | return EINVAL; |
14f437f2 | 55 | } |
5c3b5e7f RG |
56 | |
57 | const uint16_t rdLen = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]); | |
58 | size_t rdPos = 0; | |
59 | pos += DNS_RDLENGTH_SIZE; | |
60 | if ((pos + rdLen) > len) { | |
61 | return EINVAL; | |
62 | } | |
63 | ||
64 | while(len >= (pos + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE) && | |
65 | rdLen >= (rdPos + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)) { | |
fa980c59 RG |
66 | uint16_t optionCode; |
67 | uint16_t optionLen; | |
68 | if (!getNextEDNSOption(optRR + pos, len-pos, optionCode, optionLen)) { | |
69 | break; | |
70 | } | |
71 | ||
5c3b5e7f RG |
72 | pos += EDNS_OPTION_CODE_SIZE; |
73 | rdPos += EDNS_OPTION_CODE_SIZE; | |
5c3b5e7f RG |
74 | pos += EDNS_OPTION_LENGTH_SIZE; |
75 | rdPos += EDNS_OPTION_LENGTH_SIZE; | |
fa980c59 RG |
76 | |
77 | if (optionLen > (rdLen - rdPos) || optionLen > (len - pos)) { | |
5c3b5e7f | 78 | return EINVAL; |
fa980c59 | 79 | } |
5c3b5e7f RG |
80 | |
81 | if (optionCode == wantedOption) { | |
341d2553 | 82 | *optionValuePosition = pos - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE); |
5c3b5e7f RG |
83 | *optionValueSize = optionLen + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE; |
84 | return 0; | |
85 | } | |
86 | else { | |
87 | /* skip this option */ | |
88 | pos += optionLen; | |
89 | rdPos += optionLen; | |
90 | } | |
91 | } | |
92 | ||
93 | return ENOENT; | |
94 | } | |
95 | ||
00b8cadc | 96 | /* extract all EDNS0 options from a pointer on the beginning rdLen of the OPT RR */ |
29e6303a | 97 | int getEDNSOptions(const char* optRR, const size_t len, EDNSOptionViewMap& options) |
00b8cadc | 98 | { |
00b8cadc | 99 | size_t pos = 0; |
14f437f2 | 100 | if (optRR == nullptr || len < DNS_RDLENGTH_SIZE) { |
00b8cadc | 101 | return EINVAL; |
14f437f2 | 102 | } |
00b8cadc RG |
103 | |
104 | const uint16_t rdLen = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]); | |
105 | size_t rdPos = 0; | |
106 | pos += DNS_RDLENGTH_SIZE; | |
107 | if ((pos + rdLen) > len) { | |
108 | return EINVAL; | |
109 | } | |
110 | ||
111 | while(len >= (pos + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE) && | |
112 | rdLen >= (rdPos + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)) { | |
113 | const uint16_t optionCode = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]); | |
114 | pos += EDNS_OPTION_CODE_SIZE; | |
115 | rdPos += EDNS_OPTION_CODE_SIZE; | |
116 | const uint16_t optionLen = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]); | |
117 | pos += EDNS_OPTION_LENGTH_SIZE; | |
118 | rdPos += EDNS_OPTION_LENGTH_SIZE; | |
119 | if (optionLen > (rdLen - rdPos) || optionLen > (len - pos)) | |
120 | return EINVAL; | |
121 | ||
29e6303a RG |
122 | EDNSOptionViewValue value; |
123 | value.content = optRR + pos; | |
124 | value.size = optionLen; | |
beceefde | 125 | options[optionCode].values.push_back(value); |
00b8cadc RG |
126 | |
127 | /* skip this option */ | |
128 | pos += optionLen; | |
129 | rdPos += optionLen; | |
130 | } | |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
be90d6bd RG |
135 | bool getEDNSOptionsFromContent(const std::string& content, std::vector<std::pair<uint16_t, std::string>>& options) |
136 | { | |
137 | size_t pos = 0; | |
138 | uint16_t code, len; | |
139 | const size_t contentLength = content.size(); | |
140 | ||
141 | while (pos < contentLength && (contentLength - pos) >= (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)) { | |
142 | code = (static_cast<unsigned char>(content.at(pos)) * 256) + static_cast<unsigned char>(content.at(pos+1)); | |
143 | pos += EDNS_OPTION_CODE_SIZE; | |
144 | len = (static_cast<unsigned char>(content.at(pos)) * 256) + static_cast<unsigned char>(content.at(pos+1)); | |
145 | pos += EDNS_OPTION_LENGTH_SIZE; | |
146 | ||
147 | if (pos > contentLength || len > (contentLength - pos)) { | |
148 | return false; | |
149 | } | |
150 | ||
151 | options.emplace_back(code, std::string(&content.at(pos), len)); | |
152 | pos += len; | |
153 | } | |
154 | ||
155 | return true; | |
156 | } | |
157 | ||
5c3b5e7f RG |
158 | void generateEDNSOption(uint16_t optionCode, const std::string& payload, std::string& res) |
159 | { | |
160 | const uint16_t ednsOptionCode = htons(optionCode); | |
161 | const uint16_t payloadLen = htons(payload.length()); | |
162 | res.append((const char *) &ednsOptionCode, sizeof ednsOptionCode); | |
163 | res.append((const char *) &payloadLen, sizeof payloadLen); | |
164 | res.append(payload); | |
165 | } |