source: mainline/uspace/srv/net/dnsrsrv/dns_msg.c@ 3f932a7e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3f932a7e was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 16.1 KB
Line 
1/*
2 * Copyright (c) 2013 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup dnsres
30 * @{
31 */
32/**
33 * @file
34 */
35
36#include <bitops.h>
37#include <byteorder.h>
38#include <errno.h>
39#include <io/log.h>
40#include <macros.h>
41#include <stdint.h>
42#include <stdlib.h>
43#include <str.h>
44
45#include "dns_msg.h"
46#include "dns_std.h"
47
48#define NAME "dnsres"
49
50static uint16_t dns_uint16_t_decode(uint8_t *, size_t);
51
52/** Extend dynamically allocated string with suffix.
53 *
54 * @a *dstr points to a dynamically alocated buffer containing a string.
55 * Reallocate this buffer so that concatenation of @a *dstr and @a suff can
56 * fit in and append @a suff.
57 */
58static errno_t dns_dstr_ext(char **dstr, const char *suff)
59{
60 size_t s1, s2;
61 size_t nsize;
62 char *nstr;
63
64 if (*dstr == NULL) {
65 *dstr = str_dup(suff);
66 if (*dstr == NULL)
67 return ENOMEM;
68 return EOK;
69 }
70
71 s1 = str_size(*dstr);
72 s2 = str_size(suff);
73 nsize = s1 + s2 + 1;
74
75 nstr = realloc(*dstr, nsize);
76 if (nstr == NULL)
77 return ENOMEM;
78
79 str_cpy(nstr + s1, nsize - s1, suff);
80
81 *dstr = nstr;
82 return EOK;
83}
84
85/** Encode DNS name.
86 *
87 * Encode DNS name or measure the size of encoded name (with @a buf NULL,
88 * and @a buf_size 0).
89 *
90 * @param name String to encode
91 * @param buf Buffer or NULL
92 * @param buf_size Buffer size or 0 if @a buf is NULL
93 * @param act_size Place to store actual encoded size
94 */
95static errno_t dns_name_encode(char *name, uint8_t *buf, size_t buf_size,
96 size_t *act_size)
97{
98 size_t off;
99 wchar_t c;
100 size_t lsize;
101 size_t pi, di;
102
103 pi = 0;
104 di = 1;
105 off = 0;
106
107 lsize = 0;
108 while (true) {
109 c = str_decode(name, &off, STR_NO_LIMIT);
110 if (c >= 127) {
111 /* Non-ASCII character */
112 log_msg(LOG_DEFAULT, LVL_DEBUG, "Non-ascii character");
113 return EINVAL;
114 }
115
116 if (c == '.' || c == '\0') {
117 /* Empty string, starting with period or two consecutive periods. */
118 if (lsize == 0) {
119 log_msg(LOG_DEFAULT, LVL_DEBUG, "Empty token");
120 return EINVAL;
121 }
122
123 if (lsize > DNS_LABEL_MAX_SIZE) {
124 /* Label too long */
125 log_msg(LOG_DEFAULT, LVL_DEBUG, "Label too long");
126 return EINVAL;
127 }
128
129 if (buf != NULL && pi < buf_size)
130 buf[pi] = (uint8_t)lsize;
131
132 lsize = 0;
133 pi = di;
134 ++di;
135
136 if (c == '\0')
137 break;
138 } else {
139 if (buf != NULL && di < buf_size)
140 buf[di] = c;
141 ++di;
142 ++lsize;
143 }
144 }
145
146 if (buf != NULL && pi < buf_size)
147 buf[pi] = 0;
148
149 *act_size = di;
150 return EOK;
151}
152
153/** Decode DNS name.
154 *
155 * @param pdu PDU from which we are decoding
156 * @param boff Starting offset within PDU
157 * @param rname Place to return dynamically allocated string
158 * @param eoff Place to store end offset (offset after last decoded byte)
159 */
160errno_t dns_name_decode(dns_pdu_t *pdu, size_t boff, char **rname,
161 size_t *eoff)
162{
163 uint8_t *bp;
164 size_t bsize;
165 size_t lsize;
166 size_t i;
167 size_t ptr;
168 size_t eptr;
169 char *name;
170 char dbuf[2];
171 errno_t rc;
172 bool first;
173
174 name = NULL;
175
176 if (boff > pdu->size)
177 return EINVAL;
178
179 bp = pdu->data + boff;
180 bsize = min(pdu->size - boff, DNS_NAME_MAX_SIZE);
181 first = true;
182 *eoff = 0;
183
184 while (true) {
185 if (bsize == 0) {
186 rc = EINVAL;
187 goto error;
188 }
189
190 lsize = *bp;
191 ++bp;
192 --bsize;
193
194 if (lsize == 0)
195 break;
196
197 if ((lsize & 0xc0) == 0xc0) {
198 /* Pointer */
199 if (bsize < 1) {
200 log_msg(LOG_DEFAULT, LVL_DEBUG, "Pointer- bsize < 1");
201 rc = EINVAL;
202 goto error;
203 }
204
205 ptr = dns_uint16_t_decode(bp - 1, bsize) & 0x3fff;
206 ++bp;
207 --bsize;
208
209 if (ptr >= (size_t)(bp - pdu->data)) {
210 log_msg(LOG_DEFAULT, LVL_DEBUG,
211 "Pointer- forward ref %zu, pos=%zu",
212 ptr, (size_t)(bp - pdu->data));
213 /* Forward reference */
214 rc = EINVAL;
215 goto error;
216 }
217
218 /*
219 * Make sure we will not decode any byte twice.
220 * XXX Is assumption correct?
221 */
222 eptr = bp - pdu->data;
223 /*
224 * This is where encoded name ends in terms where
225 * the message continues
226 */
227 if (*eoff == 0)
228 *eoff = eptr;
229
230 bp = pdu->data + ptr;
231 bsize = eptr - ptr;
232 continue;
233 }
234
235 if (lsize > bsize) {
236 rc = EINVAL;
237 goto error;
238 }
239
240 if (!first) {
241 rc = dns_dstr_ext(&name, ".");
242 if (rc != EOK) {
243 rc = ENOMEM;
244 goto error;
245 }
246 }
247
248 for (i = 0; i < lsize; i++) {
249 if (*bp < 32 || *bp >= 127) {
250 rc = EINVAL;
251 goto error;
252 }
253
254 dbuf[0] = *bp;
255 dbuf[1] = '\0';
256
257 rc = dns_dstr_ext(&name, dbuf);
258 if (rc != EOK) {
259 rc = ENOMEM;
260 goto error;
261 }
262 ++bp;
263 --bsize;
264 }
265
266 first = false;
267 }
268
269 *rname = name;
270 if (*eoff == 0)
271 *eoff = bp - pdu->data;
272 return EOK;
273error:
274 free(name);
275 return rc;
276}
277
278/** Decode unaligned big-endian 16-bit integer */
279static uint16_t dns_uint16_t_decode(uint8_t *buf, size_t buf_size)
280{
281 assert(buf_size >= 2);
282
283 return ((uint16_t)buf[0] << 8) + buf[1];
284}
285
286/** Encode unaligned big-endian 16-bit integer */
287static void dns_uint16_t_encode(uint16_t w, uint8_t *buf, size_t buf_size)
288{
289 if (buf != NULL && buf_size >= 1)
290 buf[0] = w >> 8;
291
292 if (buf != NULL && buf_size >= 2)
293 buf[1] = w & 0xff;
294}
295
296/** Decode unaligned big-endian 32-bit integer */
297uint32_t dns_uint32_t_decode(uint8_t *buf, size_t buf_size)
298{
299 assert(buf_size >= 4);
300
301 uint32_t w = ((uint32_t) buf[0] << 24) +
302 ((uint32_t) buf[1] << 16) +
303 ((uint32_t) buf[2] << 8) +
304 buf[3];
305
306 return w;
307}
308
309/** Decode unaligned big-endian 128-bit integer */
310void dns_addr128_t_decode(uint8_t *buf, size_t buf_size, addr128_t addr)
311{
312 assert(buf_size >= 16);
313
314 addr128_t_be2host(buf, addr);
315}
316
317/** Encode DNS question.
318 *
319 * Encode DNS question or measure the size of encoded question (with @a buf NULL,
320 * and @a buf_size 0).
321 *
322 * @param question Question to encode
323 * @param buf Buffer or NULL
324 * @param buf_size Buffer size or 0 if @a buf is NULL
325 * @param act_size Place to store actual encoded size
326 */
327static errno_t dns_question_encode(dns_question_t *question, uint8_t *buf,
328 size_t buf_size, size_t *act_size)
329{
330 size_t name_size;
331 size_t di;
332 errno_t rc;
333
334 rc = dns_name_encode(question->qname, buf, buf_size, &name_size);
335 if (rc != EOK)
336 return rc;
337
338 *act_size = name_size + sizeof(uint16_t) + sizeof(uint16_t);
339 if (buf == NULL)
340 return EOK;
341
342 di = name_size;
343
344 dns_uint16_t_encode(question->qtype, buf + di, buf_size - di);
345 di += sizeof(uint16_t);
346
347 dns_uint16_t_encode(question->qclass, buf + di, buf_size - di);
348 di += sizeof(uint16_t);
349
350 return EOK;
351}
352
353/** Decode DNS question.
354 *
355 * @param pdu PDU from which we are decoding
356 * @param boff Starting offset within PDU
357 * @param rquestion Place to return dynamically allocated question
358 * @param eoff Place to store end offset (offset after last decoded byte)
359 */
360static errno_t dns_question_decode(dns_pdu_t *pdu, size_t boff,
361 dns_question_t **rquestion, size_t *eoff)
362{
363 dns_question_t *question;
364 size_t name_eoff;
365 errno_t rc;
366
367 question = calloc(1, sizeof (dns_question_t));
368 if (question == NULL)
369 return ENOMEM;
370
371 rc = dns_name_decode(pdu, boff, &question->qname, &name_eoff);
372 if (rc != EOK) {
373 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error decoding name");
374 free(question);
375 return ENOMEM;
376 }
377
378 if (name_eoff + 2 * sizeof(uint16_t) > pdu->size) {
379 free(question);
380 return EINVAL;
381 }
382
383 question->qtype = dns_uint16_t_decode(pdu->data + name_eoff,
384 pdu->size - name_eoff);
385 question->qclass = dns_uint16_t_decode(pdu->data + sizeof(uint16_t)
386 + name_eoff, pdu->size - sizeof(uint16_t) - name_eoff);
387 *eoff = name_eoff + 2 * sizeof(uint16_t);
388
389 *rquestion = question;
390 return EOK;
391}
392
393/** Decode DNS resource record.
394 *
395 * @param pdu PDU from which we are decoding
396 * @param boff Starting offset within PDU
397 * @param retrr Place to return dynamically allocated resource record
398 * @param eoff Place to store end offset (offset after last decoded byte)
399 */
400static errno_t dns_rr_decode(dns_pdu_t *pdu, size_t boff, dns_rr_t **retrr,
401 size_t *eoff)
402{
403 dns_rr_t *rr;
404 size_t name_eoff;
405 uint8_t *bp;
406 size_t bsz;
407 size_t rdlength;
408 errno_t rc;
409
410 rr = calloc(1, sizeof(dns_rr_t));
411 if (rr == NULL)
412 return ENOMEM;
413
414 rc = dns_name_decode(pdu, boff, &rr->name, &name_eoff);
415 if (rc != EOK) {
416 log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_rr_decode: error decoding name");
417 free(rr);
418 return ENOMEM;
419 }
420
421 if (name_eoff + 2 * sizeof(uint16_t) > pdu->size) {
422 free(rr->name);
423 free(rr);
424 log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_rr_decode: error name_off");
425 return EINVAL;
426 }
427
428 bp = pdu->data + name_eoff;
429 bsz = pdu->size - name_eoff;
430
431 if (bsz < 3 * sizeof(uint16_t) + sizeof(uint32_t)) {
432 free(rr->name);
433 free(rr);
434 log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_rr_decode: error bsz");
435 return EINVAL;
436 }
437
438 rr->rtype = dns_uint16_t_decode(bp, bsz);
439 bp += sizeof(uint16_t);
440 bsz -= sizeof(uint16_t);
441
442 rr->rclass = dns_uint16_t_decode(bp, bsz);
443 bp += sizeof(uint16_t);
444 bsz -= sizeof(uint16_t);
445
446 rr->ttl = dns_uint32_t_decode(bp, bsz);
447 bp += sizeof(uint32_t);
448 bsz -= sizeof(uint32_t);
449
450 log_msg(LOG_DEFAULT, LVL_DEBUG2, "dns_rr_decode: rtype=0x%x, rclass=0x%x, ttl=0x%x",
451 rr->rtype, rr->rclass, rr->ttl );
452
453 rdlength = dns_uint16_t_decode(bp, bsz);
454 bp += sizeof(uint16_t);
455 bsz -= sizeof(uint16_t);
456
457 if (rdlength > bsz) {
458 free(rr->name);
459 free(rr);
460 log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_rr_decode: Error rdlength %zu > bsz %zu", rdlength, bsz);
461 return EINVAL;
462 }
463
464 rr->rdata_size = rdlength;
465 log_msg(LOG_DEFAULT, LVL_DEBUG2, "dns_rr_decode: rdlength=%zu", rdlength);
466 rr->rdata = calloc(1, rdlength);
467 if (rr->rdata == NULL) {
468 free(rr->name);
469 free(rr);
470 log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_rr_decode: Error memory");
471 return ENOMEM;
472 }
473
474 memcpy(rr->rdata, bp, rdlength);
475 rr->roff = bp - pdu->data;
476 bp += rdlength;
477 bsz -= rdlength;
478
479 *eoff = bp - pdu->data;
480 *retrr = rr;
481 log_msg(LOG_DEFAULT, LVL_DEBUG2, "dns_rr_decode: done");
482 return EOK;
483}
484
485/** Encode DNS message.
486 *
487 * @param msg Message
488 * @param rdata Place to store encoded data pointer
489 * @param rsize Place to store encoded data size
490 *
491 * @return EOK on success, EINVAL if message contains invalid data,
492 * ENOMEM if out of memory
493 */
494errno_t dns_message_encode(dns_message_t *msg, void **rdata, size_t *rsize)
495{
496 uint8_t *data;
497 size_t size;
498 dns_header_t hdr;
499 size_t q_size = 0;
500 size_t di;
501 errno_t rc;
502
503 hdr.id = host2uint16_t_be(msg->id);
504
505 hdr.opbits = host2uint16_t_be(
506 (msg->qr << OPB_QR) |
507 (msg->opcode << OPB_OPCODE_l) |
508 (msg->aa ? BIT_V(uint16_t, OPB_AA) : 0) |
509 (msg->tc ? BIT_V(uint16_t, OPB_TC) : 0) |
510 (msg->rd ? BIT_V(uint16_t, OPB_RD) : 0) |
511 (msg->ra ? BIT_V(uint16_t, OPB_RA) : 0) |
512 msg->rcode
513 );
514
515 hdr.qd_count = host2uint16_t_be(list_count(&msg->question));
516 hdr.an_count = 0;
517 hdr.ns_count = 0;
518 hdr.ar_count = 0;
519
520 size = sizeof(dns_header_t);
521
522 list_foreach(msg->question, msg, dns_question_t, q) {
523 rc = dns_question_encode(q, NULL, 0, &q_size);
524 if (rc != EOK)
525 return rc;
526
527 assert(q_size > 0);
528
529 size += q_size;
530 }
531
532 data = calloc(1, size);
533 if (data == NULL)
534 return ENOMEM;
535
536 memcpy(data, &hdr, sizeof(dns_header_t));
537 di = sizeof(dns_header_t);
538
539 list_foreach(msg->question, msg, dns_question_t, q) {
540 rc = dns_question_encode(q, data + di, size - di, &q_size);
541 if (rc != EOK) {
542 assert(rc == ENOMEM || rc == EINVAL);
543 free(data);
544 return rc;
545 }
546
547 assert(q_size > 0);
548
549 di += q_size;
550 }
551
552 *rdata = data;
553 *rsize = size;
554 return EOK;
555}
556
557/** Decode DNS message.
558 *
559 * @param data Encoded PDU data
560 * @param size Encoded PDU size
561 * @param rmsg Place to store pointer to decoded message
562 *
563 * @return EOK on success, EINVAL if message contains invalid data,
564 * ENOMEM if out of memory
565 */
566errno_t dns_message_decode(void *data, size_t size, dns_message_t **rmsg)
567{
568 dns_message_t *msg;
569 dns_header_t *hdr;
570 size_t doff;
571 size_t field_eoff;
572 dns_question_t *question;
573 dns_rr_t *rr;
574 size_t qd_count;
575 size_t an_count;
576 size_t i;
577 errno_t rc;
578
579 msg = dns_message_new();
580 if (msg == NULL)
581 return ENOMEM;
582
583 if (size < sizeof(dns_header_t)) {
584 rc = EINVAL;
585 goto error;
586 }
587
588 /* Store a copy of raw message data for string decompression */
589
590 msg->pdu.data = malloc(size);
591 if (msg->pdu.data == NULL) {
592 rc = ENOMEM;
593 goto error;
594 }
595
596 memcpy(msg->pdu.data, data, size);
597 msg->pdu.size = size;
598 log_msg(LOG_DEFAULT, LVL_DEBUG2, "dns_message_decode: pdu->data = %p, "
599 "pdu->size=%zu", msg->pdu.data, msg->pdu.size);
600
601 hdr = data;
602
603 msg->id = uint16_t_be2host(hdr->id);
604 msg->qr = BIT_RANGE_EXTRACT(uint16_t, OPB_QR, OPB_QR, hdr->opbits);
605 msg->opcode = BIT_RANGE_EXTRACT(uint16_t, OPB_OPCODE_h, OPB_OPCODE_l,
606 hdr->opbits);
607 msg->aa = BIT_RANGE_EXTRACT(uint16_t, OPB_AA, OPB_AA, hdr->opbits);
608 msg->tc = BIT_RANGE_EXTRACT(uint16_t, OPB_TC, OPB_TC, hdr->opbits);
609 msg->rd = BIT_RANGE_EXTRACT(uint16_t, OPB_RD, OPB_RD, hdr->opbits);
610 msg->ra = BIT_RANGE_EXTRACT(uint16_t, OPB_RA, OPB_RA, hdr->opbits);
611 msg->rcode = BIT_RANGE_EXTRACT(uint16_t, OPB_RCODE_h, OPB_RCODE_l,
612 hdr->opbits);
613
614 doff = sizeof(dns_header_t);
615
616 qd_count = uint16_t_be2host(hdr->qd_count);
617 log_msg(LOG_DEFAULT, LVL_DEBUG2, "qd_count=%zu", qd_count);
618
619
620 for (i = 0; i < qd_count; i++) {
621 rc = dns_question_decode(&msg->pdu, doff, &question, &field_eoff);
622 if (rc != EOK) {
623 log_msg(LOG_DEFAULT, LVL_DEBUG, "error decoding question");
624 goto error;
625 }
626
627 list_append(&question->msg, &msg->question);
628 doff = field_eoff;
629 }
630
631 an_count = uint16_t_be2host(hdr->an_count);
632 log_msg(LOG_DEFAULT, LVL_DEBUG2, "an_count=%zu", an_count);
633
634 for (i = 0; i < an_count; i++) {
635 rc = dns_rr_decode(&msg->pdu, doff, &rr, &field_eoff);
636 if (rc != EOK) {
637 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error decoding answer");
638 goto error;
639 }
640
641 list_append(&rr->msg, &msg->answer);
642 doff = field_eoff;
643 }
644
645 *rmsg = msg;
646 return EOK;
647error:
648 dns_message_destroy(msg);
649 return rc;
650}
651
652/** Destroy question. */
653static void dns_question_destroy(dns_question_t *question)
654{
655 free(question->qname);
656 free(question);
657}
658
659/** Destroy resource record. */
660static void dns_rr_destroy(dns_rr_t *rr)
661{
662 free(rr->name);
663 free(rr->rdata);
664 free(rr);
665}
666
667/** Create new empty message. */
668dns_message_t *dns_message_new(void)
669{
670 dns_message_t *msg;
671
672 msg = calloc(1, sizeof(dns_message_t));
673 if (msg == NULL)
674 return NULL;
675
676 list_initialize(&msg->question);
677 list_initialize(&msg->answer);
678 list_initialize(&msg->authority);
679 list_initialize(&msg->additional);
680
681 return msg;
682}
683
684/** Destroy message. */
685void dns_message_destroy(dns_message_t *msg)
686{
687 link_t *link;
688 dns_question_t *question;
689 dns_rr_t *rr;
690
691 while (!list_empty(&msg->question)) {
692 link = list_first(&msg->question);
693 question = list_get_instance(link, dns_question_t, msg);
694 list_remove(&question->msg);
695 dns_question_destroy(question);
696 }
697
698 while (!list_empty(&msg->answer)) {
699 link = list_first(&msg->answer);
700 rr = list_get_instance(link, dns_rr_t, msg);
701 list_remove(&rr->msg);
702 dns_rr_destroy(rr);
703 }
704
705 while (!list_empty(&msg->authority)) {
706 link = list_first(&msg->authority);
707 rr = list_get_instance(link, dns_rr_t, msg);
708 list_remove(&rr->msg);
709 dns_rr_destroy(rr);
710 }
711
712 while (!list_empty(&msg->additional)) {
713 link = list_first(&msg->additional);
714 rr = list_get_instance(link, dns_rr_t, msg);
715 list_remove(&rr->msg);
716 dns_rr_destroy(rr);
717 }
718
719 free(msg->pdu.data);
720 free(msg);
721}
722
723/** @}
724 */
Note: See TracBrowser for help on using the repository browser.