2 * Copyright (C) 2003 Jeremie Miller <jer@jabber.org>
3 * Copyright (c) 2009 Simon Budig <simon@budig.org>
4 * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the copyright holders nor the names of
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
31 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * For additional information see http://www.ethernut.de/
37 /* This code is based on
38 * Based on BSD licensed mdnsd implementation by Jer <jer@jabber.org>
39 * http://dotlocal.org/mdnsd/
41 * Unfortunately this site is now longer alive. You can still find it at:
42 * http://web.archive.org/web/20080705131510/http://dotlocal.org/mdnsd/
44 * mdnsd - embeddable Multicast DNS Daemon
45 * =======================================
47 * "mdnsd" is a very lightweight, simple, portable, and easy to integrate
48 * open source implementation of Multicast DNS (part of Zeroconf, also called
49 * Rendezvous by Apple) for developers. It supports both acting as a Query and
50 * a Responder, allowing any software to participate fully on the .localnetwork
51 * just by including a few files and calling a few functions. All of the
52 * complexity of handling the Multicast DNS retransmit timing, duplicate
53 * suppression, probing, conflict detection, and other facets of the DNS
54 * protocol is hidden behind a very simple and very easy to use interface,
55 * described in the header file. The single small c source file has almost no
56 * dependencies, and is portable to almost any embedded platform.
57 * Multiple example applications and usages are included in the download,
58 * including a simple persistent query browser and a tool to advertise .local
61 * The code is licensed under both the GPL and BSD licenses, for use in any
62 * free software or commercial application. If there is a licensing need not
63 * covered by either of those, alternative licensing is available upon request.
69 * \brief Standalone DNS parsing functions
71 * Implementation follows RF1035 [http://www.rfc-editor.org/rfc/rfc1035.txt] and
72 * includes decoding functions for svr rr's code type 33. See RFC2782
73 * [http://www.rfc-editor.org/rfc/rfc2782.txt]
87 * \addtogroup xgMulticastDns
93 * \brief Conversion of the network byte order buffer content into uint16_t value.
95 * This functions increments the buffer pointer accordingly
99 * \return Converted value
101 uint16_t DnsNet2Short(uint8_t **buf)
104 val = (*((*buf) ++)) << 8;
111 * \brief Conversion of the network byte order buffer content into uint32_t value.
113 * This functions increments the buffer pointer accordingly
117 * \return Converted value
119 uint32_t DnsNet2Long(uint8_t **buf)
122 val = (*((*buf) ++)) << 24;
123 val |= (*((*buf) ++)) << 16;
124 val |= (*((*buf) ++)) << 8;
131 * \brief Conversion of uint16_t value into buffer content in network byte order
133 * This functions increments the buffer pointer accordingly
138 void DnsShort2Net(uint16_t val, uint8_t **buf)
140 *((*buf) ++) = (uint8_t) (val >> 8);
141 *((*buf) ++) = (uint8_t) val;
145 * \brief Conversion of uint32_t value into buffer content in network byte order
147 * This functions increments the buffer pointer accordingly
152 void DnsLong2Net(uint32_t val, uint8_t **buf)
154 *((*buf) ++) = (uint8_t) (val >> 24);
155 *((*buf) ++) = (uint8_t) (val >> 16);
156 *((*buf) ++) = (uint8_t) (val >> 8);
157 *((*buf) ++) = (uint8_t) val;
162 * \brief "Decompress" a label by calculating the offset of the original label string
164 * This functions increments the buffer pointer accordingly
166 * \param ptr Pointer to the compressed label
168 * \return Offset of the original label string
170 static uint16_t LabelDecompress(uint8_t *ptr)
176 if (val > PACKET_BUFFER_LEN - 1) {
177 val = PACKET_BUFFER_LEN - 1;
184 * \brief Extracts the label from the buffer.
186 * In case the label is compressed, it will be decompressed.
187 * This functions increments the buffer pointer accordingly.
188 * If the label is not yet cached, it will be added to the cached labels
190 * \param msg DNS message
191 * \param buf Pointer to the package data buffer
192 * \param namep Pointer to the string buffer, where the label will be copied to
194 static void ExtractLabel(DNSMESSAGE *msg, uint8_t **buf, char **namep)
200 /* Set the name pointer to the end of the data block */
201 name = msg->packet + msg->len;
204 /* loop storing label in the block */
206 while (*label != 0) {
207 /* Skip any compression pointer until end encountered */
208 while (*label & 0xc0) {
209 label = msg->buf + LabelDecompress(label);
215 /* Check the limits */
216 if (((name + *label) - *namep > 255) ||
217 (msg->len + ((name + *label) - *namep) > PACKET_BUFFER_LEN -1 )) {
221 /* Copy label to the name buffer */
222 memcpy(name, label+1, *label);
229 /* Advance in the buffer */
230 for (label = *buf; (*label != 0) && (!((*label & 0xc0) && label++)); label += *label + 1);
233 /* Add a traling \0 and check if the name is yet cached */
236 for (x = 0; x < MAX_LABEL; x++) {
237 if (msg->labels[x]) {
238 if (strcmp(*namep, msg->labels[x]) != 0) {
242 *namep = msg->labels[x];
247 /* The label was not yet cached, so cache it if there is still room */
248 if ((x < MAX_LABEL) && (msg->labels[x] == 0)) {
249 msg->labels[x] = *namep;
251 msg->len += (name - *namep) + 1;
255 * \brief Check if labels are matching
257 * If labels are compressed, they will be decompressed first.
259 * \param msg DNS message
260 * \param label1 Pointer to label1
261 * \param label1 Pointer to label2
263 * \return 1 in case they are matching, else 0
265 static int MatchLabel(DNSMESSAGE *msg, uint8_t *label1, uint8_t *label2)
269 /* If we were calles with a pointer, call MatchLabel with dereferenced pointer again */
271 if (*label1 & 0xc0) {
272 return MatchLabel(msg, msg->buf + LabelDecompress(label1), label2);
275 if (*label2 & 0xc0) {
276 return MatchLabel(msg, label1, msg->buf + LabelDecompress(label2));
279 /* Return in case of a match */
280 if (label1 == label2) {
284 /* Compare the label */
285 if (*label1 != *label2){
289 for (len = 1; len <= *label1; len++) {
290 if (label1[len] != label2[len]) {
295 /* Get the new labels */
296 label1 += *label1 + 1;
297 label2 += *label2 + 1;
299 /* Finally all labels should be matched */
300 if ((*label1 == 0) && (*label2 == 0)) {
304 /* Recursivly call match with the next labels */
306 return MatchLabel(msg, label1, label2);
311 * \brief Convert host name to label using compression
313 * \param msg DNS message
314 * \param buf Pointer to the buffer where the label shall be placed
315 * \param name Hostname
317 * \return Length of the label
319 static int Host2Label(DNSMESSAGE *msg, uint8_t **buf, char *name)
321 uint8_t label[MAX_LABEL_SIZE];
332 /* Let's make the label */
334 if (name[y] == '.') {
335 if (name[y + 1] == 0) {
338 label[last] = x - (last + 1);
343 if (x++ == MAX_LABEL_SIZE - 1) {
349 label[last] = x - (last + 1);
352 x--; // special case, bad names, but handle correctly
356 /* \0 terminate the label */
359 /* Check each label against all msg->labels for a match */
360 for(x = 0; label[x]; x += label[x] + 1) {
361 for( y = 0; msg->labels[y]; y++) {
362 if (MatchLabel(msg, label+x, msg->labels[y])) {
363 /* If the label matches, create the pointer */
365 DnsShort2Net(msg->labels[y] - msg->packet, &l);
372 if (label[x] & 0xc0) {
377 /* Copy the label into the buffer and let the buffer pointer point to the label */
378 memcpy(*buf, label, len);
382 /* Save the label location of each new label for future compression */
383 for (x = 0; l[x]; x += l[x] + 1) {
388 if (msg->label + 1 >= MAX_LABEL - 1) {
392 msg->labels[msg->label++] = l + x;
400 * \brief Parse a ressource record packet and fill the rr struct
402 * \param msg DNS message
403 * \param rr Pointer to ressource record struct where the informations shall be stored
404 * \param count Number of ressource records in the package
405 * \param buf Package buffer
407 * \return 0: success, -1 in case of an error
409 static int ParseRR(DNSMESSAGE *msg, DNSRESOURCE *rr, int count, uint8_t **buf)
413 for (i=0; i < count; i++) {
414 /* Extract the name, type, class, tt and record data length from the
415 buffer. The buffer pointer is automatically incremented by each call
418 ExtractLabel(msg, buf, &(rr[i].name));
419 rr[i].type = DnsNet2Short(buf);
420 rr[i].class = DnsNet2Short(buf);
421 rr[i].ttl = DnsNet2Long(buf);
422 rr[i].rdlength = DnsNet2Short(buf);
424 /* Sanity checking the length */
425 if ((rr[i].rdlength + (*buf - msg->buf) > MAX_PACKET_LEN) ||
426 (msg->len + rr[i].rdlength > MAX_PACKET_LEN)) {
430 /* Length was ok, make a copy of the data */
431 rr[i].rdata = msg->packet + msg->len;
433 msg->len += rr[i].rdlength;
434 memcpy(rr[i].rdata, *buf, rr[i].rdlength);
436 /* Parse known message types */
437 switch (rr[i].type) {
439 if(msg->len + 16 > MAX_PACKET_LEN) {
442 rr[i].known.a.name = msg->packet + msg->len;
444 sprintf(rr[i].known.a.name, "%d.%d.%d.%d", (*buf)[0], (*buf)[1], (*buf)[2], (*buf)[3]);
445 rr[i].known.a.ip = DnsNet2Long(buf);
449 ExtractLabel(msg, buf, &(rr[i].known.ns.name));
453 ExtractLabel(msg, buf, &(rr[i].known.cname.name));
457 ExtractLabel(msg, buf, &(rr[i].known.ptr.name));
461 rr[i].known.srv.priority = DnsNet2Short(buf);
462 rr[i].known.srv.weight = DnsNet2Short(buf);
463 rr[i].known.srv.port = DnsNet2Short(buf);
464 ExtractLabel(msg, buf, &(rr[i].known.srv.name));
468 *buf += rr[i].rdlength;
477 * \brief Parse DNS packet into message format
479 * The packet must be at least MAX_PACKET_LEN bytes in size and must be allocated clean (zeroed)
481 * \param msg DNS message
482 * \param packet Package buffer
484 void DnsParseMsg(DNSMESSAGE *msg, uint8_t *packet)
489 if((packet == 0) || (msg == 0)) {
493 /* Bit decoding of the record header */
496 msg->id = DnsNet2Short(&buf);
497 // TODO: Ohne shiften auf die Bits prüfen, korrekte Bitwerte hinterlegen
503 /* Shift opcode field 3 bits right for easy comparision */
504 msg->header.opcode = (buf[0] & 0x78) >> 3;
522 msg->header.z = (buf[1] & 0x70) >> 4;
523 msg->header.rcode = buf[1] & 0x0F;
526 msg->qdcount = DnsNet2Short(&buf);
527 if (msg->len + (sizeof(DNSQUESTION) * msg->qdcount) > MAX_PACKET_LEN - 8) {
532 /* Get number of AN records */
533 msg->ancount = DnsNet2Short(&buf);
534 if (msg->len + (sizeof(DNSRESOURCE) * msg->ancount) > MAX_PACKET_LEN - 8) {
539 /* Get number of NS records */
540 msg->nscount = DnsNet2Short(&buf);
541 if (msg->len + (sizeof(DNSRESOURCE) * msg->nscount) > MAX_PACKET_LEN - 8) {
546 /* Get number of AR records */
547 msg->arcount = DnsNet2Short(&buf);
548 if (msg->len + (sizeof(DNSRESOURCE) * msg->arcount) > MAX_PACKET_LEN - 8) {
553 /* Process the questions */
554 while (msg->len & 0x07) msg->len++;
555 /* Process AN record */
556 msg->qd= (DNSQUESTION *) (msg->packet + msg->len);
557 msg->len += sizeof(DNSQUESTION) * msg->qdcount;
559 for(i = 0; i < msg->qdcount; i++) {
560 ExtractLabel(msg, &buf, &(msg->qd[i].name));
561 msg->qd[i].type = DnsNet2Short(&buf);
562 msg->qd[i].class = DnsNet2Short(&buf);
566 /* Process Ressource records and keep us always at a 8 byte boundary */
569 while (msg->len & 0x07) msg->len++;
570 /* Process AN record */
571 msg->an = (DNSRESOURCE *) (msg->packet + msg->len);
572 msg->len += sizeof(DNSRESOURCE) * msg->ancount;
575 while (msg->len & 0x07) msg->len++;
576 /* Process NS record */
577 msg->ns = (DNSRESOURCE *) (msg->packet + msg->len);
578 msg->len += sizeof(DNSRESOURCE) * msg->nscount;
581 while (msg->len & 0x07) msg->len++;
582 /* Process AR record */
583 msg->ar = (DNSRESOURCE *) (msg->packet + msg->len);
584 msg->len += sizeof(DNSRESOURCE) * msg->arcount;
586 /* Parse AN record */
587 if (ParseRR(msg, msg->an, msg->ancount, &buf)) {
588 /* Size limit checking failed */
592 /* Parse NS record */
593 if (ParseRR(msg, msg->ns, msg->nscount, &buf)) {
594 /* Size limit checking failed */
598 /* Parse AR record */
599 if (ParseRR(msg, msg->ar, msg->arcount, &buf)) {
600 /* Size limit checking failed */
607 * \brief Append a ressource record to the message
609 * \param msg DNS message
610 * \param name Hostname
611 * \param type Query type
612 * \param class Query class
613 * \param ttl TTL value
615 static void DnsAppendRR(DNSMESSAGE *msg, char *name, uint16_t type, uint16_t class, uint32_t ttl)
618 /* Initialise the buffer pointer */
619 msg->buf = msg->packet + 12;
621 Host2Label(msg, &(msg->buf), name);
622 DnsShort2Net(type, &(msg->buf));
623 DnsShort2Net(class, &(msg->buf));
624 DnsLong2Net(ttl, &(msg->buf));
629 * \brief Append a QD ressource record to the message
631 * Should be called first before calling DnsMsgAdd_an.
633 * \param msg DNS message
634 * \param name Hostname
635 * \param type Query type
636 * \param class Query class
638 void DnsMsgAdd_qd(DNSMESSAGE *msg, char *name, uint16_t type, uint16_t class)
642 /* Initialise the buffer pointer */
643 msg->buf = msg->packet + 12;
646 Host2Label(msg, &(msg->buf), name);
647 DnsShort2Net(type, &(msg->buf));
648 DnsShort2Net(class, &(msg->buf));
653 * \brief Append a AN ressource record to the message
655 * Should be called first before calling DnsMsgAdd_ns.
657 * \param msg DNS message
658 * \param name Hostname
659 * \param type Query type
660 * \param class Query class
662 void DnsMsgAdd_an(DNSMESSAGE *msg, char *name, uint16_t type, uint16_t class, uint32_t ttl)
665 DnsAppendRR(msg, name, type, class, ttl);
670 * \brief Append a NS ressource record to the message
672 * Should be called first before calling DnsMsgAdd_ar.
674 * \param msg DNS message
675 * \param name Hostname
676 * \param type Query type
677 * \param class Query class
679 void DnsMsgAdd_ns(DNSMESSAGE *msg, char *name, uint16_t type, uint16_t class, uint32_t ttl)
682 DnsAppendRR(msg, name, type, class, ttl);
687 * \brief Append a AR ressource record to the message
689 * \param msg DNS message
690 * \param name Hostname
691 * \param type Query type
692 * \param class Query class
694 void DnsMsgAdd_ar(DNSMESSAGE *msg, char *name, uint16_t type, uint16_t class, uint32_t ttl)
697 DnsAppendRR(msg, name, type, class, ttl);
702 * \brief Append resource data types block: uint32_t value
704 * \param msg DNS message
707 void DnsMsgAdd_rdata_long(DNSMESSAGE *msg, uint32_t val)
709 DnsShort2Net(4, &(msg->buf));
710 DnsLong2Net(val, &(msg->buf));
715 * \brief Append resource data types block: Hostname
717 * \param msg DNS message
718 * \param name Hostname
720 void DnsMsgAdd_rdata_name(DNSMESSAGE *msg, char *name)
722 uint8_t *tmp_buf = msg->buf;
724 DnsShort2Net(Host2Label(msg, &(msg->buf), name), &tmp_buf);
728 * \brief Append resource data types block: Service data
730 * \param msg DNS message
731 * \param priority Priority of the target host: lower value means more preferred.
732 * \param weight Relative weight for records with the same priority.
733 * \param port TCP / UDP port number of the service
734 * \param name The canonical hostname of the machine providing the service.
736 void DnsMsgAdd_rdata_srv(DNSMESSAGE *msg, uint16_t priority, uint16_t weight, uint16_t port, char *name)
738 uint8_t *tmp_buf = msg->buf;
740 DnsShort2Net(priority, &(msg->buf));
741 DnsShort2Net(weight, &(msg->buf));
742 DnsShort2Net(port, &(msg->buf));
743 DnsShort2Net(Host2Label(msg, &(msg->buf), name) + 6, &tmp_buf);
748 * \brief Append resource data types block: Raw data
750 * \param msg DNS message
751 * \param rdata Pointer to the raw data buffer
752 * \param rdlength Length of the raw data block
754 void DnsMsgAdd_rdata_raw(DNSMESSAGE *msg, uint8_t *rdata, uint16_t rdlength)
756 if ((msg->buf - msg->packet) + rdlength > MAX_PACKET_LEN) {
759 DnsShort2Net(rdlength, &(msg->buf));
760 memcpy(msg->buf, rdata, rdlength);
761 msg->buf += rdlength;
766 * \brief Generate the message packet to be send out.
768 * \return Packet buffer
770 uint8_t *DnsMsg2Pkt(DNSMESSAGE *msg)
772 uint8_t c, *buf = msg->buf;
773 msg->buf = msg->packet;
774 DnsShort2Net(msg->id, &(msg->buf));
775 // TODO: Bits direkt ohne shiften
776 if (msg->header.qr) {
780 if ((c = msg->header.opcode)) {
781 msg->buf[0] |= (c << 3);
783 if (msg->header.aa) {
787 if (msg->header.tc) {
791 if (msg->header.rd) {
795 if (msg->header.ra) {
799 if ((c = msg->header.z)) {
800 msg->buf[1] |= (c << 4);
803 if (msg->header.rcode) {
804 msg->buf[1] |= msg->header.rcode;
809 DnsShort2Net(msg->qdcount, &(msg->buf));
810 DnsShort2Net(msg->ancount, &(msg->buf));
811 DnsShort2Net(msg->nscount, &(msg->buf));
812 DnsShort2Net(msg->arcount, &(msg->buf));
814 /* Restore the packet pointer. It is necessary for DnsMsgLen() */
817 /* Return the modified packet */
823 * \brief Calculate message packet length
825 * \return Message packet length
828 int DnsMsgLen(DNSMESSAGE *msg)
830 if(msg->buf == 0) return 12;
831 return msg->buf - msg->packet;