source: branches/samba-3.0/source/libaddns/dnsmarshall.c@ 223

Last change on this file since 223 was 1, checked in by Paul Smedley, 18 years ago

Initial code import

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