s3-talloc Change TALLOC_ZERO_P() to talloc_zero()
[metze/samba/wip.git] / lib / addns / dnsmarshall.c
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
26 struct 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
50 void 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(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
96 void 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
102 void 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
108 void 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
124 void 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
134 void 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
144 void 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
167 static 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
245 void 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                 return;
256         }
257
258         dns_unmarshall_label(name, 0, buf, &name->pLabelList);
259
260         if (!ERR_DNS_IS_OK(buf->error)) {
261                 return;
262         }
263
264         *pname = name;
265         return;
266 }
267
268 static void dns_marshall_question(struct dns_buffer *buf,
269                                   const struct dns_question *q)
270 {
271         dns_marshall_domain_name(buf, q->name);
272         dns_marshall_uint16(buf, q->q_type);
273         dns_marshall_uint16(buf, q->q_class);
274 }
275
276 static void dns_unmarshall_question(TALLOC_CTX *mem_ctx,
277                                     struct dns_buffer *buf,
278                                     struct dns_question **pq)
279 {
280         struct dns_question *q;
281
282         if (!(ERR_DNS_IS_OK(buf->error))) return;
283
284         if (!(q = talloc(mem_ctx, struct dns_question))) {
285                 buf->error = ERROR_DNS_NO_MEMORY;
286                 return;
287         }
288
289         dns_unmarshall_domain_name(q, buf, &q->name);
290         dns_unmarshall_uint16(buf, &q->q_type);
291         dns_unmarshall_uint16(buf, &q->q_class);
292
293         if (!(ERR_DNS_IS_OK(buf->error))) return;
294
295         *pq = q;
296 }
297
298 static void dns_marshall_rr(struct dns_buffer *buf,
299                             const struct dns_rrec *r)
300 {
301         dns_marshall_domain_name(buf, r->name);
302         dns_marshall_uint16(buf, r->type);
303         dns_marshall_uint16(buf, r->r_class);
304         dns_marshall_uint32(buf, r->ttl);
305         dns_marshall_uint16(buf, r->data_length);
306         dns_marshall_buffer(buf, r->data, r->data_length);
307 }
308
309 static void dns_unmarshall_rr(TALLOC_CTX *mem_ctx,
310                               struct dns_buffer *buf,
311                               struct dns_rrec **pr)
312 {
313         struct dns_rrec *r;
314
315         if (!(ERR_DNS_IS_OK(buf->error))) return;
316
317         if (!(r = talloc(mem_ctx, struct dns_rrec))) {
318                 buf->error = ERROR_DNS_NO_MEMORY;
319                 return;
320         }
321
322         dns_unmarshall_domain_name(r, buf, &r->name);
323         dns_unmarshall_uint16(buf, &r->type);
324         dns_unmarshall_uint16(buf, &r->r_class);
325         dns_unmarshall_uint32(buf, &r->ttl);
326         dns_unmarshall_uint16(buf, &r->data_length);
327         r->data = NULL;
328
329         if (!(ERR_DNS_IS_OK(buf->error))) return;
330
331         if (r->data_length != 0) {
332                 if (!(r->data = talloc_array(r, uint8, r->data_length))) {
333                         buf->error = ERROR_DNS_NO_MEMORY;
334                         return;
335                 }
336                 dns_unmarshall_buffer(buf, r->data, r->data_length);
337         }
338
339         if (!(ERR_DNS_IS_OK(buf->error))) return;
340
341         *pr = r;
342 }
343
344 DNS_ERROR dns_marshall_request(TALLOC_CTX *mem_ctx,
345                                const struct dns_request *req,
346                                struct dns_buffer **pbuf)
347 {
348         struct dns_buffer *buf;
349         uint16 i;
350
351         if (!(buf = dns_create_buffer(mem_ctx))) {
352                 return ERROR_DNS_NO_MEMORY;
353         }
354
355         dns_marshall_uint16(buf, req->id);
356         dns_marshall_uint16(buf, req->flags);
357         dns_marshall_uint16(buf, req->num_questions);
358         dns_marshall_uint16(buf, req->num_answers);
359         dns_marshall_uint16(buf, req->num_auths);
360         dns_marshall_uint16(buf, req->num_additionals);
361
362         for (i=0; i<req->num_questions; i++) {
363                 dns_marshall_question(buf, req->questions[i]);
364         }
365         for (i=0; i<req->num_answers; i++) {
366                 dns_marshall_rr(buf, req->answers[i]);
367         }
368         for (i=0; i<req->num_auths; i++) {
369                 dns_marshall_rr(buf, req->auths[i]);
370         }
371         for (i=0; i<req->num_additionals; i++) {
372                 dns_marshall_rr(buf, req->additionals[i]);
373         }
374
375         if (!ERR_DNS_IS_OK(buf->error)) {
376                 DNS_ERROR err = buf->error;
377                 TALLOC_FREE(buf);
378                 return err;
379         }
380
381         *pbuf = buf;
382         return ERROR_DNS_SUCCESS;
383 }
384
385 DNS_ERROR dns_unmarshall_request(TALLOC_CTX *mem_ctx,
386                                  struct dns_buffer *buf,
387                                  struct dns_request **preq)
388 {
389         struct dns_request *req;
390         uint16 i;
391         DNS_ERROR err;
392
393         if (!(req = talloc_zero(mem_ctx, struct dns_request))) {
394                 return ERROR_DNS_NO_MEMORY;
395         }
396
397         dns_unmarshall_uint16(buf, &req->id);
398         dns_unmarshall_uint16(buf, &req->flags);
399         dns_unmarshall_uint16(buf, &req->num_questions);
400         dns_unmarshall_uint16(buf, &req->num_answers);
401         dns_unmarshall_uint16(buf, &req->num_auths);
402         dns_unmarshall_uint16(buf, &req->num_additionals);
403
404         if (!ERR_DNS_IS_OK(buf->error)) goto error;
405
406         err = ERROR_DNS_NO_MEMORY;
407
408         if ((req->num_questions != 0) &&
409             !(req->questions = talloc_array(req, struct dns_question *,
410                                             req->num_questions))) {
411                 goto error;
412         }
413         if ((req->num_answers != 0) &&
414             !(req->answers = talloc_array(req, struct dns_rrec *,
415                                           req->num_answers))) {
416                 goto error;
417         }
418         if ((req->num_auths != 0) &&
419             !(req->auths = talloc_array(req, struct dns_rrec *,
420                                         req->num_auths))) {
421                 goto error;
422         }
423         if ((req->num_additionals != 0) &&
424             !(req->additionals = talloc_array(req, struct dns_rrec *,
425                                               req->num_additionals))) {
426                 goto error;
427         }
428
429         for (i=0; i<req->num_questions; i++) {
430                 dns_unmarshall_question(req->questions, buf,
431                                         &req->questions[i]);
432         }
433         for (i=0; i<req->num_answers; i++) {
434                 dns_unmarshall_rr(req->answers, buf,
435                                   &req->answers[i]);
436         }
437         for (i=0; i<req->num_auths; i++) {
438                 dns_unmarshall_rr(req->auths, buf,
439                                   &req->auths[i]);
440         }
441         for (i=0; i<req->num_additionals; i++) {
442                 dns_unmarshall_rr(req->additionals, buf,
443                                   &req->additionals[i]);
444         }
445
446         if (!ERR_DNS_IS_OK(buf->error)) {
447                 err = buf->error;
448                 goto error;
449         }
450
451         *preq = req;
452         return ERROR_DNS_SUCCESS;
453
454  error:
455         err = buf->error;
456         TALLOC_FREE(req);
457         return err;
458 }
459
460 struct dns_request *dns_update2request(struct dns_update_request *update)
461 {
462         struct dns_request *req;
463
464         /*
465          * This is a non-specified construct that happens to work on Linux/gcc
466          * and I would expect it to work everywhere else. dns_request and
467          * dns_update_request are essentially the same structures with
468          * different names, so any difference would mean that the compiler
469          * applied two different variations of padding given the same types in
470          * the structures.
471          */
472
473         req = (struct dns_request *)(void *)update;
474
475         /*
476          * The assert statement here looks like we could do the equivalent
477          * assignments to get portable, but it would mean that we have to
478          * allocate the dns_question record for the dns_zone records. We
479          * assume that if this assert works then the same holds true for
480          * dns_zone<>dns_question as well.
481          */
482
483 #ifdef DEVELOPER
484         assert((req->id == update->id) && (req->flags == update->flags) &&
485                (req->num_questions == update->num_zones) &&
486                (req->num_answers == update->num_preqs) &&
487                (req->num_auths == update->num_updates) &&
488                (req->num_additionals == update->num_additionals) &&
489                (req->questions ==
490                 (struct dns_question **)(void *)update->zones) &&
491                (req->answers == update->preqs) &&
492                (req->auths == update->updates) &&
493                (req->additionals == update->additionals));
494 #endif
495
496         return req;
497 }
498
499 struct dns_update_request *dns_request2update(struct dns_request *request)
500 {
501         /*
502          * For portability concerns see dns_update2request;
503          */
504         return (struct dns_update_request *)(void *)request;
505 }
506
507 DNS_ERROR dns_marshall_update_request(TALLOC_CTX *mem_ctx,
508                                       struct dns_update_request *update,
509                                       struct dns_buffer **pbuf)
510 {
511         return dns_marshall_request(mem_ctx, dns_update2request(update), pbuf);
512 }
513
514 DNS_ERROR dns_unmarshall_update_request(TALLOC_CTX *mem_ctx,
515                                         struct dns_buffer *buf,
516                                         struct dns_update_request **pupreq)
517 {
518         /*
519          * See comments above about portability. If the above works, this will
520          * as well.
521          */
522
523         return dns_unmarshall_request(mem_ctx, buf,
524                                       (struct dns_request **)(void *)pupreq);
525 }
526
527 uint16 dns_response_code(uint16 flags)
528 {
529         return flags & 0xF;
530 }