]>
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 | { |
341d2553 RG |
48 | assert(optRR != nullptr); |
49 | assert(optionValuePosition != nullptr); | |
50 | assert(optionValueSize != nullptr); | |
5c3b5e7f RG |
51 | size_t pos = 0; |
52 | if (len < DNS_RDLENGTH_SIZE) | |
53 | return EINVAL; | |
54 | ||
55 | const uint16_t rdLen = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]); | |
56 | size_t rdPos = 0; | |
57 | pos += DNS_RDLENGTH_SIZE; | |
58 | if ((pos + rdLen) > len) { | |
59 | return EINVAL; | |
60 | } | |
61 | ||
62 | while(len >= (pos + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE) && | |
63 | rdLen >= (rdPos + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)) { | |
fa980c59 RG |
64 | uint16_t optionCode; |
65 | uint16_t optionLen; | |
66 | if (!getNextEDNSOption(optRR + pos, len-pos, optionCode, optionLen)) { | |
67 | break; | |
68 | } | |
69 | ||
5c3b5e7f RG |
70 | pos += EDNS_OPTION_CODE_SIZE; |
71 | rdPos += EDNS_OPTION_CODE_SIZE; | |
5c3b5e7f RG |
72 | pos += EDNS_OPTION_LENGTH_SIZE; |
73 | rdPos += EDNS_OPTION_LENGTH_SIZE; | |
fa980c59 RG |
74 | |
75 | if (optionLen > (rdLen - rdPos) || optionLen > (len - pos)) { | |
5c3b5e7f | 76 | return EINVAL; |
fa980c59 | 77 | } |
5c3b5e7f RG |
78 | |
79 | if (optionCode == wantedOption) { | |
341d2553 | 80 | *optionValuePosition = pos - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE); |
5c3b5e7f RG |
81 | *optionValueSize = optionLen + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE; |
82 | return 0; | |
83 | } | |
84 | else { | |
85 | /* skip this option */ | |
86 | pos += optionLen; | |
87 | rdPos += optionLen; | |
88 | } | |
89 | } | |
90 | ||
91 | return ENOENT; | |
92 | } | |
93 | ||
00b8cadc | 94 | /* extract all EDNS0 options from a pointer on the beginning rdLen of the OPT RR */ |
29e6303a | 95 | int getEDNSOptions(const char* optRR, const size_t len, EDNSOptionViewMap& options) |
00b8cadc | 96 | { |
4646277d | 97 | assert(optRR != nullptr); |
00b8cadc RG |
98 | size_t pos = 0; |
99 | if (len < DNS_RDLENGTH_SIZE) | |
100 | return EINVAL; | |
101 | ||
102 | const uint16_t rdLen = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]); | |
103 | size_t rdPos = 0; | |
104 | pos += DNS_RDLENGTH_SIZE; | |
105 | if ((pos + rdLen) > len) { | |
106 | return EINVAL; | |
107 | } | |
108 | ||
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)) | |
118 | return EINVAL; | |
119 | ||
29e6303a RG |
120 | EDNSOptionViewValue value; |
121 | value.content = optRR + pos; | |
122 | value.size = optionLen; | |
beceefde | 123 | options[optionCode].values.push_back(value); |
00b8cadc RG |
124 | |
125 | /* skip this option */ | |
126 | pos += optionLen; | |
127 | rdPos += optionLen; | |
128 | } | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
be90d6bd RG |
133 | bool getEDNSOptionsFromContent(const std::string& content, std::vector<std::pair<uint16_t, std::string>>& options) |
134 | { | |
135 | size_t pos = 0; | |
136 | uint16_t code, len; | |
137 | const size_t contentLength = content.size(); | |
138 | ||
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; | |
144 | ||
145 | if (pos > contentLength || len > (contentLength - pos)) { | |
146 | return false; | |
147 | } | |
148 | ||
149 | options.emplace_back(code, std::string(&content.at(pos), len)); | |
150 | pos += len; | |
151 | } | |
152 | ||
153 | return true; | |
154 | } | |
155 | ||
5c3b5e7f RG |
156 | void generateEDNSOption(uint16_t optionCode, const std::string& payload, std::string& res) |
157 | { | |
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); | |
162 | res.append(payload); | |
163 | } |