source: branches/samba-3.2.x/source/libaddns/dnsmarshall.c@ 205

Last change on this file since 205 was 133, checked in by Paul Smedley, 17 years ago

Update trunk to 3.2.0pre3

File size: 12.4 KB
Line 
1/*
2 Linux DNS client library implementation
3 Copyright (C) 2006 Gerald Carter <jerry@samba.org>
4
5 ** NOTE! The following LGPL license applies to the libaddns
6 ** library. This does NOT imply that all of Samba is released
7 ** under the LGPL
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, see <http://www.gnu.org/licenses/>.
21*/
22
23#include "dns.h"
24#include "assert.h"
25
26struct dns_buffer *dns_create_buffer(TALLOC_CTX *mem_ctx)
27{
28 struct dns_buffer *result;
29
30 if (!(result = talloc(mem_ctx, struct dns_buffer))) {
31 return NULL;
32 }
33
34 result->offset = 0;
35 result->error = ERROR_DNS_SUCCESS;
36
37 /*
38 * Small inital size to excercise the realloc code
39 */
40 result->size = 2;
41
42 if (!(result->data = TALLOC_ARRAY(result, uint8, result->size))) {
43 TALLOC_FREE(result);
44 return NULL;
45 }
46
47 return result;
48}
49
50void dns_marshall_buffer(struct dns_buffer *buf, const uint8 *data,
51 size_t len)
52{
53 if (!ERR_DNS_IS_OK(buf->error)) return;
54
55 if (buf->offset + len < buf->offset) {
56 /*
57 * Wraparound!
58 */
59 buf->error = ERROR_DNS_INVALID_PARAMETER;
60 return;
61 }
62
63 if ((buf->offset + len) > 0xffff) {
64 /*
65 * Only 64k possible
66 */
67 buf->error = ERROR_DNS_INVALID_PARAMETER;
68 return;
69 }
70
71 if (buf->offset + len > buf->size) {
72 size_t new_size = buf->offset + len;
73 uint8 *new_data;
74
75 /*
76 * Don't do too many reallocs, round up to some multiple
77 */
78
79 new_size += (64 - (new_size % 64));
80
81 if (!(new_data = TALLOC_REALLOC_ARRAY(buf, buf->data, uint8,
82 new_size))) {
83 buf->error = ERROR_DNS_NO_MEMORY;
84 return;
85 }
86
87 buf->size = new_size;
88 buf->data = new_data;
89 }
90
91 memcpy(buf->data + buf->offset, data, len);
92 buf->offset += len;
93 return;
94}
95
96void dns_marshall_uint16(struct dns_buffer *buf, uint16 val)
97{
98 uint16 n_val = htons(val);
99 dns_marshall_buffer(buf, (uint8 *)&n_val, sizeof(n_val));
100}
101
102void dns_marshall_uint32(struct dns_buffer *buf, uint32 val)
103{
104 uint32 n_val = htonl(val);
105 dns_marshall_buffer(buf, (uint8 *)&n_val, sizeof(n_val));
106}
107
108void dns_unmarshall_buffer(struct dns_buffer *buf, uint8 *data,
109 size_t len)
110{
111 if (!(ERR_DNS_IS_OK(buf->error))) return;
112
113 if ((len > buf->size) || (buf->offset + len > buf->size)) {
114 buf->error = ERROR_DNS_INVALID_MESSAGE;
115 return;
116 }
117
118 memcpy((void *)data, (const void *)(buf->data + buf->offset), len);
119 buf->offset += len;
120
121 return;
122}
123
124void dns_unmarshall_uint16(struct dns_buffer *buf, uint16 *val)
125{
126 uint16 n_val;
127
128 dns_unmarshall_buffer(buf, (uint8 *)&n_val, sizeof(n_val));
129 if (!(ERR_DNS_IS_OK(buf->error))) return;
130
131 *val = ntohs(n_val);
132}
133
134void dns_unmarshall_uint32(struct dns_buffer *buf, uint32 *val)
135{
136 uint32 n_val;
137
138 dns_unmarshall_buffer(buf, (uint8 *)&n_val, sizeof(n_val));
139 if (!(ERR_DNS_IS_OK(buf->error))) return;
140
141 *val = ntohl(n_val);
142}
143
144void dns_marshall_domain_name(struct dns_buffer *buf,
145 const struct dns_domain_name *name)
146{
147 struct dns_domain_label *label;
148 char end_char = '\0';
149
150 /*
151 * TODO: Implement DNS compression
152 */
153
154 for (label = name->pLabelList; label != NULL; label = label->next) {
155 uint8 len = label->len;
156
157 dns_marshall_buffer(buf, (uint8 *)&len, sizeof(len));
158 if (!ERR_DNS_IS_OK(buf->error)) return;
159
160 dns_marshall_buffer(buf, (uint8 *)label->label, len);
161 if (!ERR_DNS_IS_OK(buf->error)) return;
162 }
163
164 dns_marshall_buffer(buf, (uint8 *)&end_char, 1);
165}
166
167static void dns_unmarshall_label(TALLOC_CTX *mem_ctx,
168 int level,
169 struct dns_buffer *buf,
170 struct dns_domain_label **plabel)
171{
172 struct dns_domain_label *label;
173 uint8 len;
174
175 if (!ERR_DNS_IS_OK(buf->error)) return;
176
177 if (level > 128) {
178 /*
179 * Protect against recursion
180 */
181 buf->error = ERROR_DNS_INVALID_MESSAGE;
182 return;
183 }
184
185 dns_unmarshall_buffer(buf, &len, sizeof(len));
186 if (!ERR_DNS_IS_OK(buf->error)) return;
187
188 if (len == 0) {
189 *plabel = NULL;
190 return;
191 }
192
193 if ((len & 0xc0) == 0xc0) {
194 /*
195 * We've got a compressed name. Build up a new "fake" buffer
196 * and using the calculated offset.
197 */
198 struct dns_buffer new_buf;
199 uint8 low;
200
201 dns_unmarshall_buffer(buf, &low, sizeof(low));
202 if (!ERR_DNS_IS_OK(buf->error)) return;
203
204 new_buf = *buf;
205 new_buf.offset = len & 0x3f;
206 new_buf.offset <<= 8;
207 new_buf.offset |= low;
208
209 dns_unmarshall_label(mem_ctx, level+1, &new_buf, plabel);
210 buf->error = new_buf.error;
211 return;
212 }
213
214 if ((len & 0xc0) != 0) {
215 buf->error = ERROR_DNS_INVALID_NAME;
216 return;
217 }
218
219 if (!(label = talloc(mem_ctx, struct dns_domain_label))) {
220 buf->error = ERROR_DNS_NO_MEMORY;
221 return;
222 }
223
224 label->len = len;
225
226 if (!(label->label = TALLOC_ARRAY(label, char, len+1))) {
227 buf->error = ERROR_DNS_NO_MEMORY;
228 goto error;
229 }
230
231 dns_unmarshall_buffer(buf, (uint8 *)label->label, len);
232 if (!ERR_DNS_IS_OK(buf->error)) goto error;
233
234 dns_unmarshall_label(label, level+1, buf, &label->next);
235 if (!ERR_DNS_IS_OK(buf->error)) goto error;
236
237 *plabel = label;
238 return;
239
240 error:
241 TALLOC_FREE(label);
242 return;
243}
244
245void dns_unmarshall_domain_name(TALLOC_CTX *mem_ctx,
246 struct dns_buffer *buf,
247 struct dns_domain_name **pname)
248{
249 struct dns_domain_name *name;
250
251 if (!ERR_DNS_IS_OK(buf->error)) return;
252
253 if (!(name = talloc(mem_ctx, struct dns_domain_name))) {
254 buf->error = ERROR_DNS_NO_MEMORY;
255 }
256
257 dns_unmarshall_label(name, 0, buf, &name->pLabelList);
258
259 if (!ERR_DNS_IS_OK(buf->error)) {
260 return;
261 }
262
263 *pname = name;
264 return;
265}
266
267static void dns_marshall_question(struct dns_buffer *buf,
268 const struct dns_question *q)
269{
270 dns_marshall_domain_name(buf, q->name);
271 dns_marshall_uint16(buf, q->q_type);
272 dns_marshall_uint16(buf, q->q_class);
273}
274
275static void dns_unmarshall_question(TALLOC_CTX *mem_ctx,
276 struct dns_buffer *buf,
277 struct dns_question **pq)
278{
279 struct dns_question *q;
280
281 if (!(ERR_DNS_IS_OK(buf->error))) return;
282
283 if (!(q = talloc(mem_ctx, struct dns_question))) {
284 buf->error = ERROR_DNS_NO_MEMORY;
285 return;
286 }
287
288 dns_unmarshall_domain_name(q, buf, &q->name);
289 dns_unmarshall_uint16(buf, &q->q_type);
290 dns_unmarshall_uint16(buf, &q->q_class);
291
292 if (!(ERR_DNS_IS_OK(buf->error))) return;
293
294 *pq = q;
295}
296
297static void dns_marshall_rr(struct dns_buffer *buf,
298 const struct dns_rrec *r)
299{
300 dns_marshall_domain_name(buf, r->name);
301 dns_marshall_uint16(buf, r->type);
302 dns_marshall_uint16(buf, r->r_class);
303 dns_marshall_uint32(buf, r->ttl);
304 dns_marshall_uint16(buf, r->data_length);
305 dns_marshall_buffer(buf, r->data, r->data_length);
306}
307
308static void dns_unmarshall_rr(TALLOC_CTX *mem_ctx,
309 struct dns_buffer *buf,
310 struct dns_rrec **pr)
311{
312 struct dns_rrec *r;
313
314 if (!(ERR_DNS_IS_OK(buf->error))) return;
315
316 if (!(r = talloc(mem_ctx, struct dns_rrec))) {
317 buf->error = ERROR_DNS_NO_MEMORY;
318 return;
319 }
320
321 dns_unmarshall_domain_name(r, buf, &r->name);
322 dns_unmarshall_uint16(buf, &r->type);
323 dns_unmarshall_uint16(buf, &r->r_class);
324 dns_unmarshall_uint32(buf, &r->ttl);
325 dns_unmarshall_uint16(buf, &r->data_length);
326 r->data = NULL;
327
328 if (!(ERR_DNS_IS_OK(buf->error))) return;
329
330 if (r->data_length != 0) {
331 if (!(r->data = TALLOC_ARRAY(r, uint8, r->data_length))) {
332 buf->error = ERROR_DNS_NO_MEMORY;
333 return;
334 }
335 dns_unmarshall_buffer(buf, r->data, r->data_length);
336 }
337
338 if (!(ERR_DNS_IS_OK(buf->error))) return;
339
340 *pr = r;
341}
342
343DNS_ERROR dns_marshall_request(TALLOC_CTX *mem_ctx,
344 const struct dns_request *req,
345 struct dns_buffer **pbuf)
346{
347 struct dns_buffer *buf;
348 uint16 i;
349
350 if (!(buf = dns_create_buffer(mem_ctx))) {
351 return ERROR_DNS_NO_MEMORY;
352 }
353
354 dns_marshall_uint16(buf, req->id);
355 dns_marshall_uint16(buf, req->flags);
356 dns_marshall_uint16(buf, req->num_questions);
357 dns_marshall_uint16(buf, req->num_answers);
358 dns_marshall_uint16(buf, req->num_auths);
359 dns_marshall_uint16(buf, req->num_additionals);
360
361 for (i=0; i<req->num_questions; i++) {
362 dns_marshall_question(buf, req->questions[i]);
363 }
364 for (i=0; i<req->num_answers; i++) {
365 dns_marshall_rr(buf, req->answers[i]);
366 }
367 for (i=0; i<req->num_auths; i++) {
368 dns_marshall_rr(buf, req->auths[i]);
369 }
370 for (i=0; i<req->num_additionals; i++) {
371 dns_marshall_rr(buf, req->additionals[i]);
372 }
373
374 if (!ERR_DNS_IS_OK(buf->error)) {
375 DNS_ERROR err = buf->error;
376 TALLOC_FREE(buf);
377 return err;
378 }
379
380 *pbuf = buf;
381 return ERROR_DNS_SUCCESS;
382}
383
384DNS_ERROR dns_unmarshall_request(TALLOC_CTX *mem_ctx,
385 struct dns_buffer *buf,
386 struct dns_request **preq)
387{
388 struct dns_request *req;
389 uint16 i;
390 DNS_ERROR err;
391
392 if (!(req = TALLOC_ZERO_P(mem_ctx, struct dns_request))) {
393 return ERROR_DNS_NO_MEMORY;
394 }
395
396 dns_unmarshall_uint16(buf, &req->id);
397 dns_unmarshall_uint16(buf, &req->flags);
398 dns_unmarshall_uint16(buf, &req->num_questions);
399 dns_unmarshall_uint16(buf, &req->num_answers);
400 dns_unmarshall_uint16(buf, &req->num_auths);
401 dns_unmarshall_uint16(buf, &req->num_additionals);
402
403 if (!ERR_DNS_IS_OK(buf->error)) goto error;
404
405 err = ERROR_DNS_NO_MEMORY;
406
407 if ((req->num_questions != 0) &&
408 !(req->questions = TALLOC_ARRAY(req, struct dns_question *,
409 req->num_questions))) {
410 goto error;
411 }
412 if ((req->num_answers != 0) &&
413 !(req->answers = TALLOC_ARRAY(req, struct dns_rrec *,
414 req->num_answers))) {
415 goto error;
416 }
417 if ((req->num_auths != 0) &&
418 !(req->auths = TALLOC_ARRAY(req, struct dns_rrec *,
419 req->num_auths))) {
420 goto error;
421 }
422 if ((req->num_additionals != 0) &&
423 !(req->additionals = TALLOC_ARRAY(req, struct dns_rrec *,
424 req->num_additionals))) {
425 goto error;
426 }
427
428 for (i=0; i<req->num_questions; i++) {
429 dns_unmarshall_question(req->questions, buf,
430 &req->questions[i]);
431 }
432 for (i=0; i<req->num_answers; i++) {
433 dns_unmarshall_rr(req->answers, buf,
434 &req->answers[i]);
435 }
436 for (i=0; i<req->num_auths; i++) {
437 dns_unmarshall_rr(req->auths, buf,
438 &req->auths[i]);
439 }
440 for (i=0; i<req->num_additionals; i++) {
441 dns_unmarshall_rr(req->additionals, buf,
442 &req->additionals[i]);
443 }
444
445 if (!ERR_DNS_IS_OK(buf->error)) {
446 err = buf->error;
447 goto error;
448 }
449
450 *preq = req;
451 return ERROR_DNS_SUCCESS;
452
453 error:
454 err = buf->error;
455 TALLOC_FREE(req);
456 return err;
457}
458
459struct dns_request *dns_update2request(struct dns_update_request *update)
460{
461 struct dns_request *req;
462
463 /*
464 * This is a non-specified construct that happens to work on Linux/gcc
465 * and I would expect it to work everywhere else. dns_request and
466 * dns_update_request are essentially the same structures with
467 * different names, so any difference would mean that the compiler
468 * applied two different variations of padding given the same types in
469 * the structures.
470 */
471
472 req = (struct dns_request *)(void *)update;
473
474 /*
475 * The assert statement here looks like we could do the equivalent
476 * assignments to get portable, but it would mean that we have to
477 * allocate the dns_question record for the dns_zone records. We
478 * assume that if this assert works then the same holds true for
479 * dns_zone<>dns_question as well.
480 */
481
482#ifdef DEVELOPER
483 assert((req->id == update->id) && (req->flags == update->flags) &&
484 (req->num_questions == update->num_zones) &&
485 (req->num_answers == update->num_preqs) &&
486 (req->num_auths == update->num_updates) &&
487 (req->num_additionals == update->num_additionals) &&
488 (req->questions ==
489 (struct dns_question **)(void *)update->zones) &&
490 (req->answers == update->preqs) &&
491 (req->auths == update->updates) &&
492 (req->additionals == update->additionals));
493#endif
494
495 return req;
496}
497
498struct dns_update_request *dns_request2update(struct dns_request *request)
499{
500 /*
501 * For portability concerns see dns_update2request;
502 */
503 return (struct dns_update_request *)(void *)request;
504}
505
506DNS_ERROR dns_marshall_update_request(TALLOC_CTX *mem_ctx,
507 struct dns_update_request *update,
508 struct dns_buffer **pbuf)
509{
510 return dns_marshall_request(mem_ctx, dns_update2request(update), pbuf);
511}
512
513DNS_ERROR dns_unmarshall_update_request(TALLOC_CTX *mem_ctx,
514 struct dns_buffer *buf,
515 struct dns_update_request **pupreq)
516{
517 /*
518 * See comments above about portability. If the above works, this will
519 * as well.
520 */
521
522 return dns_unmarshall_request(mem_ctx, buf,
523 (struct dns_request **)(void *)pupreq);
524}
525
526uint16 dns_response_code(uint16 flags)
527{
528 return flags & 0xF;
529}
Note: See TracBrowser for help on using the repository browser.