/src/samba/librpc/ndr/ndr_dns.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | manipulate dns name structures |
5 | | |
6 | | Copyright (C) 2010 Kai Blin <kai@samba.org> |
7 | | |
8 | | Heavily based on nbtname.c which is: |
9 | | |
10 | | Copyright (C) Andrew Tridgell 2005 |
11 | | |
12 | | This program is free software; you can redistribute it and/or modify |
13 | | it under the terms of the GNU General Public License as published by |
14 | | the Free Software Foundation; either version 3 of the License, or |
15 | | (at your option) any later version. |
16 | | |
17 | | This program is distributed in the hope that it will be useful, |
18 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20 | | GNU General Public License for more details. |
21 | | |
22 | | You should have received a copy of the GNU General Public License |
23 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
24 | | */ |
25 | | |
26 | | /* |
27 | | see rfc1002 for the detailed format of compressed names |
28 | | */ |
29 | | |
30 | | #include "includes.h" |
31 | | #include "librpc/gen_ndr/ndr_dns.h" |
32 | | #include "librpc/gen_ndr/ndr_misc.h" |
33 | | #include "librpc/gen_ndr/ndr_dnsp.h" |
34 | | #include "system/locale.h" |
35 | | #include "lib/util/util_net.h" |
36 | | #include "ndr_dns_utils.h" |
37 | | |
38 | | /* don't allow an unlimited number of name components */ |
39 | 7.48M | #define MAX_COMPONENTS 128 |
40 | | |
41 | | /** |
42 | | print a dns string |
43 | | */ |
44 | | _PUBLIC_ void ndr_print_dns_string(struct ndr_print *ndr, |
45 | | const char *name, |
46 | | const char *s) |
47 | 965k | { |
48 | 965k | ndr_print_string(ndr, name, s); |
49 | 965k | } |
50 | | |
51 | | /* |
52 | | pull one component of a dns_string |
53 | | */ |
54 | | static enum ndr_err_code ndr_pull_component(struct ndr_pull *ndr, |
55 | | uint8_t **component, |
56 | | uint32_t *offset, |
57 | | uint32_t *max_offset) |
58 | 3.82M | { |
59 | 3.82M | uint8_t len; |
60 | 3.82M | unsigned int loops = 0; |
61 | 3.85M | while (loops < 5) { |
62 | 3.85M | if (*offset >= ndr->data_size) { |
63 | 276 | return ndr_pull_error(ndr, NDR_ERR_STRING, |
64 | 276 | "BAD DNS NAME component, bad offset"); |
65 | 276 | } |
66 | 3.85M | len = ndr->data[*offset]; |
67 | 3.85M | if (len == 0) { |
68 | 3.66M | *offset += 1; |
69 | 3.66M | *max_offset = MAX(*max_offset, *offset); |
70 | 3.66M | *component = NULL; |
71 | 3.66M | return NDR_ERR_SUCCESS; |
72 | 3.66M | } |
73 | 193k | if ((len & 0xC0) == 0xC0) { |
74 | | /* its a label pointer */ |
75 | 39.5k | if (1 + *offset >= ndr->data_size) { |
76 | 7 | return ndr_pull_error(ndr, NDR_ERR_STRING, |
77 | 7 | "BAD DNS NAME component, " \ |
78 | 7 | "bad label offset"); |
79 | 7 | } |
80 | 39.5k | *max_offset = MAX(*max_offset, *offset + 2); |
81 | 39.5k | *offset = ((len&0x3F)<<8) | ndr->data[1 + *offset]; |
82 | 39.5k | *max_offset = MAX(*max_offset, *offset); |
83 | 39.5k | loops++; |
84 | 39.5k | continue; |
85 | 39.5k | } |
86 | 153k | if ((len & 0xC0) != 0) { |
87 | | /* its a reserved length field */ |
88 | 12 | return ndr_pull_error(ndr, NDR_ERR_STRING, |
89 | 12 | "BAD DNS NAME component, " \ |
90 | 12 | "reserved length field: 0x%02"PRIx8, |
91 | 12 | (len &0xC)); |
92 | 12 | } |
93 | 153k | if (*offset + len + 1 > ndr->data_size) { |
94 | 16 | return ndr_pull_error(ndr, NDR_ERR_STRING, |
95 | 16 | "BAD DNS NAME component, "\ |
96 | 16 | "length too long"); |
97 | 16 | } |
98 | 153k | *component = (uint8_t*)talloc_strndup(ndr, |
99 | 153k | (const char *)&ndr->data[1 + *offset], len); |
100 | 153k | NDR_ERR_HAVE_NO_MEMORY(*component); |
101 | 153k | *offset += len + 1; |
102 | 153k | *max_offset = MAX(*max_offset, *offset); |
103 | 153k | return NDR_ERR_SUCCESS; |
104 | 153k | } |
105 | | |
106 | | /* too many pointers */ |
107 | 5 | return ndr_pull_error(ndr, NDR_ERR_STRING, |
108 | 3.82M | "BAD DNS NAME component, too many pointers"); |
109 | 3.82M | } |
110 | | |
111 | | /** |
112 | | pull a dns_string from the wire |
113 | | */ |
114 | | _PUBLIC_ enum ndr_err_code ndr_pull_dns_string(struct ndr_pull *ndr, |
115 | | ndr_flags_type ndr_flags, |
116 | | const char **s) |
117 | 3.66M | { |
118 | 3.66M | uint32_t offset = ndr->offset; |
119 | 3.66M | uint32_t max_offset = offset; |
120 | 3.66M | unsigned num_components; |
121 | 3.66M | char *name; |
122 | | |
123 | 3.66M | if (!(ndr_flags & NDR_SCALARS)) { |
124 | 0 | return NDR_ERR_SUCCESS; |
125 | 0 | } |
126 | | |
127 | 3.66M | name = talloc_strdup(ndr->current_mem_ctx, ""); |
128 | | |
129 | | /* break up name into a list of components */ |
130 | 3.82M | for (num_components=0; num_components<MAX_COMPONENTS; |
131 | 3.82M | num_components++) { |
132 | 3.82M | uint8_t *component = NULL; |
133 | 3.82M | NDR_CHECK(ndr_pull_component(ndr, &component, &offset, |
134 | 3.82M | &max_offset)); |
135 | 3.82M | if (component == NULL) break; |
136 | 153k | if (num_components > 0) { |
137 | 31.7k | name = talloc_asprintf_append_buffer(name, ".%s", |
138 | 31.7k | component); |
139 | 122k | } else { |
140 | 122k | name = talloc_asprintf_append_buffer(name, "%s", |
141 | 122k | component); |
142 | 122k | } |
143 | 153k | NDR_ERR_HAVE_NO_MEMORY(name); |
144 | 153k | } |
145 | 3.66M | if (num_components == MAX_COMPONENTS) { |
146 | 30 | return ndr_pull_error(ndr, NDR_ERR_STRING, |
147 | 30 | "BAD DNS NAME too many components"); |
148 | 30 | } |
149 | | |
150 | 3.66M | (*s) = name; |
151 | 3.66M | ndr->offset = max_offset; |
152 | | |
153 | 3.66M | return NDR_ERR_SUCCESS; |
154 | 3.66M | } |
155 | | |
156 | | /** |
157 | | push a dns string to the wire |
158 | | */ |
159 | | _PUBLIC_ enum ndr_err_code ndr_push_dns_string(struct ndr_push *ndr, |
160 | | ndr_flags_type ndr_flags, |
161 | | const char *s) |
162 | 859k | { |
163 | 859k | return ndr_push_dns_string_list(ndr, |
164 | 859k | &ndr->dns_string_list, |
165 | 859k | ndr_flags, |
166 | 859k | s, |
167 | 859k | false); |
168 | 859k | } |
169 | | |
170 | | _PUBLIC_ enum ndr_err_code ndr_pull_dns_txt_record(struct ndr_pull *ndr, ndr_flags_type ndr_flags, struct dns_txt_record *r) |
171 | 2.16k | { |
172 | 2.16k | NDR_PULL_CHECK_FLAGS(ndr, ndr_flags); |
173 | 2.16k | if (ndr_flags & NDR_SCALARS) { |
174 | 2.16k | enum ndr_err_code ndr_err; |
175 | 2.16k | uint32_t data_size = ndr->data_size; |
176 | 2.16k | uint32_t record_size = 0; |
177 | 2.16k | ndr_err = ndr_token_retrieve(&ndr->array_size_list, r, |
178 | 2.16k | &record_size); |
179 | 2.16k | if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
180 | 2.03k | NDR_PULL_NEED_BYTES(ndr, record_size); |
181 | 1.96k | ndr->data_size = ndr->offset + record_size; |
182 | 1.96k | } |
183 | 2.09k | NDR_CHECK(ndr_pull_align(ndr, 1)); |
184 | 2.09k | NDR_CHECK(ndr_pull_dnsp_string_list(ndr, NDR_SCALARS, &r->txt)); |
185 | 2.02k | NDR_CHECK(ndr_pull_trailer_align(ndr, 1)); |
186 | 2.02k | ndr->data_size = data_size; |
187 | 2.02k | } |
188 | 2.02k | if (ndr_flags & NDR_BUFFERS) { |
189 | 76 | } |
190 | 2.02k | return NDR_ERR_SUCCESS; |
191 | 2.16k | } |
192 | | |
193 | | _PUBLIC_ enum ndr_err_code ndr_push_dns_res_rec(struct ndr_push *ndr, |
194 | | ndr_flags_type ndr_flags, |
195 | | const struct dns_res_rec *r) |
196 | 1.31M | { |
197 | 1.31M | libndr_flags _flags_save_STRUCT = ndr->flags; |
198 | 1.31M | uint32_t _saved_offset1, _saved_offset2; |
199 | 1.31M | uint16_t length; |
200 | 1.31M | ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX | |
201 | 1.31M | LIBNDR_FLAG_NOALIGN); |
202 | 1.31M | if (ndr_flags & NDR_SCALARS) { |
203 | 668k | libndr_flags _flags_save_name = ndr->flags; |
204 | | |
205 | 668k | NDR_CHECK(ndr_push_align(ndr, 4)); |
206 | | |
207 | 668k | switch (r->rr_type) { |
208 | 6.22k | case DNS_QTYPE_TKEY: |
209 | 6.87k | case DNS_QTYPE_TSIG: |
210 | 6.87k | ndr_set_flags(&ndr->flags, LIBNDR_FLAG_NO_COMPRESSION); |
211 | 6.87k | break; |
212 | 661k | default: |
213 | 661k | break; |
214 | 668k | } |
215 | 668k | NDR_CHECK(ndr_push_dns_string(ndr, NDR_SCALARS, r->name)); |
216 | 668k | ndr->flags = _flags_save_name; |
217 | | |
218 | 668k | NDR_CHECK(ndr_push_dns_qtype(ndr, NDR_SCALARS, r->rr_type)); |
219 | 668k | NDR_CHECK(ndr_push_dns_qclass(ndr, NDR_SCALARS, r->rr_class)); |
220 | 668k | NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, r->ttl)); |
221 | 668k | _saved_offset1 = ndr->offset; |
222 | 668k | NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, 0)); |
223 | 668k | if (r->length > 0) { |
224 | 15.2k | uint32_t _saved_offset3; |
225 | | |
226 | 15.2k | NDR_CHECK(ndr_push_set_switch_value(ndr, &r->rdata, |
227 | 15.2k | r->rr_type)); |
228 | 15.2k | _saved_offset3 = ndr->offset; |
229 | 15.2k | NDR_CHECK(ndr_push_dns_rdata(ndr, NDR_SCALARS, |
230 | 15.2k | &r->rdata)); |
231 | 15.1k | if ((ndr->offset != _saved_offset3) && |
232 | 10.2k | (r->unexpected.length > 0)) { |
233 | | /* |
234 | | * ndr_push_dns_rdata pushed a known |
235 | | * record, but we have something |
236 | | * unexpected. That's invalid. |
237 | | */ |
238 | 38 | return ndr_push_error(ndr, |
239 | 38 | NDR_ERR_LENGTH, |
240 | 38 | "Invalid...Unexpected " \ |
241 | 38 | "blob length is too " \ |
242 | 38 | "large"); |
243 | 38 | } |
244 | 15.1k | } |
245 | 667k | if (r->unexpected.length > UINT16_MAX) { |
246 | 0 | return ndr_push_error(ndr, NDR_ERR_LENGTH, |
247 | 0 | "Unexpected blob length "\ |
248 | 0 | "is too large"); |
249 | 0 | } |
250 | | |
251 | 667k | NDR_CHECK(ndr_push_bytes(ndr, r->unexpected.data, |
252 | 667k | r->unexpected.length)); |
253 | 667k | NDR_CHECK(ndr_push_trailer_align(ndr, 4)); |
254 | 667k | length = ndr->offset - (_saved_offset1 + 2); |
255 | 667k | _saved_offset2 = ndr->offset; |
256 | 667k | ndr->offset = _saved_offset1; |
257 | 667k | NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, length)); |
258 | 667k | ndr->offset = _saved_offset2; |
259 | 667k | } |
260 | 1.31M | if (ndr_flags & NDR_BUFFERS) { |
261 | 650k | NDR_CHECK(ndr_push_dns_rdata(ndr, NDR_BUFFERS, |
262 | 650k | &r->rdata)); |
263 | 650k | } |
264 | 1.31M | ndr->flags = _flags_save_STRUCT; |
265 | 1.31M | return NDR_ERR_SUCCESS; |
266 | 1.31M | } |
267 | | |
268 | | _PUBLIC_ enum ndr_err_code ndr_pull_dns_res_rec(struct ndr_pull *ndr, |
269 | | ndr_flags_type ndr_flags, |
270 | | struct dns_res_rec *r) |
271 | 3.01M | { |
272 | 3.01M | libndr_flags _flags_save_STRUCT = ndr->flags; |
273 | 3.01M | uint32_t _saved_offset1; |
274 | 3.01M | uint32_t pad, length; |
275 | | |
276 | 3.01M | ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX | |
277 | 3.01M | LIBNDR_FLAG_NOALIGN); |
278 | 3.01M | if (ndr_flags & NDR_SCALARS) { |
279 | 2.31M | NDR_CHECK(ndr_pull_align(ndr, 4)); |
280 | 2.31M | NDR_CHECK(ndr_pull_dns_string(ndr, NDR_SCALARS, &r->name)); |
281 | 2.31M | NDR_CHECK(ndr_pull_dns_qtype(ndr, NDR_SCALARS, &r->rr_type)); |
282 | 2.31M | NDR_CHECK(ndr_pull_dns_qclass(ndr, NDR_SCALARS, &r->rr_class)); |
283 | 2.31M | NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->ttl)); |
284 | 2.31M | NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->length)); |
285 | 2.31M | _saved_offset1 = ndr->offset; |
286 | 2.31M | if (r->length > 0) { |
287 | 1.31M | NDR_CHECK(ndr_token_store(ndr, &ndr->array_size_list, |
288 | 1.31M | &r->rdata, |
289 | 1.31M | r->length)); |
290 | 1.31M | NDR_CHECK(ndr_pull_set_switch_value(ndr, &r->rdata, |
291 | 1.31M | r->rr_type)); |
292 | 1.31M | NDR_CHECK(ndr_pull_dns_rdata(ndr, NDR_SCALARS, |
293 | 1.31M | &r->rdata)); |
294 | 1.31M | } else { |
295 | 1.00M | ZERO_STRUCT(r->rdata); |
296 | 1.00M | } |
297 | 2.31M | length = ndr->offset - _saved_offset1; |
298 | 2.31M | if (length > r->length) { |
299 | 37 | return ndr_pull_error(ndr, NDR_ERR_LENGTH, "TODO"); |
300 | 37 | } |
301 | | |
302 | 2.31M | r->unexpected = data_blob_null; |
303 | 2.31M | pad = r->length - length; |
304 | 2.31M | if (pad > 0) { |
305 | 23.7k | NDR_PULL_NEED_BYTES(ndr, pad); |
306 | 23.6k | r->unexpected = data_blob_talloc(ndr->current_mem_ctx, |
307 | 23.6k | ndr->data + |
308 | 23.6k | ndr->offset, |
309 | 23.6k | pad); |
310 | 23.6k | if (r->unexpected.data == NULL) { |
311 | 0 | return ndr_pull_error(ndr, |
312 | 0 | NDR_ERR_ALLOC, |
313 | 0 | "Failed to allocate a " \ |
314 | 0 | "data blob"); |
315 | 0 | } |
316 | 23.6k | ndr->offset += pad; |
317 | 23.6k | } |
318 | | |
319 | | |
320 | 2.31M | NDR_CHECK(ndr_pull_trailer_align(ndr, 4)); |
321 | 2.31M | } |
322 | 3.01M | if (ndr_flags & NDR_BUFFERS) { |
323 | 700k | NDR_CHECK(ndr_pull_dns_rdata(ndr, NDR_BUFFERS, &r->rdata)); |
324 | 700k | } |
325 | 3.01M | ndr->flags = _flags_save_STRUCT; |
326 | 3.01M | return NDR_ERR_SUCCESS; |
327 | 3.01M | } |