Fix an uninitialized variable, Coverity ID 481
[ira/wip.git] / source3 / libaddns / 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_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
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         }
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
267 static 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
275 static 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
297 static 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
308 static 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
343 DNS_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
384 DNS_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
459 struct 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
498 struct 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
506 DNS_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
513 DNS_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
526 uint16 dns_response_code(uint16 flags)
527 {
528         return flags & 0xF;
529 }