1 // This file is the concatenation of mdnsd.c and mdns.c
2 // from tinysvcmdns with minor modifications
3 // The code was taken from https://bitbucket.org/geekman/tinysvcmdns at revision e34b562
6 * tinysvcmdns - a tiny MDNS implementation for publishing services
7 * Copyright (C) 2011 Darell Tan
9 * Updated many times by Mike Brady (c) 2014 -- 2019
10 * Includes fixes for CVE-12087 and CVE-2017-12130
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include "tinysvcmdns.h"
38 #define DEBUG_PRINTF(...) debug(3, __VA_ARGS__)
39 #define log_message(level, ...) \
46 debug(3, __VA_ARGS__); \
50 //******************************************************//
52 //******************************************************//
63 #include <netinet/in.h>
66 // See RFC 6762 Section 10 for an account of two TTLs -- 120 seconds for rrs with a host name as the
68 // or a host name in the record's rdata
69 // 75 minutes for everything else.
70 // https://tools.ietf.org/html/rfc6762
72 #define DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME 120
73 #define DEFAULT_TTL 4500
76 uint8_t *label
; // label
77 size_t pos
; // position in msg
79 struct name_comp
*next
;
82 // ----- label functions -----
85 inline uint8_t *dup_nlabel(const uint8_t *n
) {
88 assert(n
[0] <= 63); // prevent mis-use
89 return (uint8_t *)strdup((char *)n
);
93 uint8_t *dup_label(const uint8_t *label
) {
97 uint8_t *newlabel
= malloc(len
+ 1);
99 strncpy((char *)newlabel
, (char *)label
, len
);
101 die("could not allocate memory for \"newlabel\" in tinysvcmdns");
102 newlabel
[len
] = '\0';
106 uint8_t *join_nlabel(const uint8_t *n1
, const uint8_t *n2
) {
110 assert(n1
[0] <= 63 && n2
[0] <= 63); // detect misuse
112 len1
= strlen((char *)n1
);
113 len2
= strlen((char *)n2
);
115 s
= malloc(len1
+ len2
+ 1);
117 memcpy((char *)s
, (char *)n1
, len1
);
118 memcpy((char *)s
+ len1
, (char *)n2
, len2
);
119 s
[len1
+ len2
] = '\0';
121 die("can not allocate memory for \"s\" in tinysvcmdns");
126 // returns a human-readable name label in dotted form
127 char *nlabel_to_str(const uint8_t *name
) {
128 char *label
, *labelp
;
130 size_t buf_len
= 256;
134 label
= labelp
= malloc(buf_len
);
137 for (p
= name
; *p
; p
++) {
138 uint8_t label_len
= *p
;
139 if (buf_len
<= label_len
)
142 strncpy(labelp
, (char *)p
+ 1, label_len
);
148 buf_len
-= label_len
+ 1;
153 // avoid writing NULL past end of buffer
159 die("could not allocate memory for \"label\" in tinysvcmdns.c.");
165 // returns the length of a label field
166 // does NOT uncompress the field, so it could be as small as 2 bytes
168 static size_t label_len(uint8_t *pkt_buf
, size_t pkt_len
, size_t off
) {
170 uint8_t *e
= pkt_buf
+ pkt_len
;
173 for (p
= pkt_buf
+ off
; p
< e
; p
++) {
176 } else if ((*p
& 0xC0) == 0xC0) {
189 uint8_t *create_label(const char *txt
) {
193 // assert(txt != NULL);
203 memcpy((char *)s
+ 1, txt
, len
);
206 die("can not allocate memory for \"s\" 2 in tinysvcmdns.");
211 // creates a uncompressed name label given a DNS name like "apple.b.com"
213 uint8_t *create_nlabel(const char *name
) {
215 char *p
, *e
, *lenpos
;
218 assert(name
!= NULL
);
221 label
= malloc(len
+ 1 + 1);
225 memcpy((char *)label
+ 1, name
, len
);
226 label
[len
+ 1] = '\0';
234 char *dot
= memchr(p
+ 1, '.', e
- p
- 1);
237 *lenpos
= dot
- p
- 1;
243 return (uint8_t *)label
;
246 // copies a label from the buffer into a newly-allocated string
248 static uint8_t *copy_label(uint8_t *pkt_buf
, size_t pkt_len
, size_t off
) {
254 len
= pkt_buf
[off
] + 1;
255 if (off
+ len
> pkt_len
) {
256 DEBUG_PRINTF("label length exceeds packet buffer\n");
260 return dup_label(pkt_buf
+ off
);
263 // uncompresses a name
265 static uint8_t *uncompress_nlabel(uint8_t *pkt_buf
, size_t pkt_len
, size_t off
) {
267 uint8_t *e
= pkt_buf
+ pkt_len
;
273 // calculate length of uncompressed label
274 for (p
= pkt_buf
+ off
; *p
&& p
< e
; p
++) {
276 if ((*p
& 0xC0) == 0xC0) {
277 uint8_t *p2
= pkt_buf
+ (((p
[0] & ~0xC0) << 8) | p
[1]);
287 str
= sp
= malloc(len
+ 1);
291 // FIXME: must merge this with above code
292 for (p
= pkt_buf
+ off
; *p
&& p
< e
; p
++) {
294 if ((*p
& 0xC0) == 0xC0) {
295 uint8_t *p2
= pkt_buf
+ (((p
[0] & ~0xC0) << 8) | p
[1]);
297 strncpy(sp
, (char *)p2
, llen
);
301 strncpy(sp
, (char *)p
, llen
);
308 return (uint8_t *)str
;
311 // ----- RR list & group functions -----
313 const char *rr_get_type_name(enum rr_type type
) {
333 void rr_entry_destroy(struct rr_entry
*rr
) {
334 struct rr_data_txt
*txt_rec
;
337 // check rr_type and free data elements
340 if (rr
->data
.PTR
.name
)
341 free(rr
->data
.PTR
.name
);
346 txt_rec
= &rr
->data
.TXT
;
348 struct rr_data_txt
*next
= txt_rec
->next
;
352 // only free() if it wasn't part of the struct
353 if (txt_rec
!= &rr
->data
.TXT
)
361 if (rr
->data
.SRV
.target
)
362 free(rr
->data
.SRV
.target
);
374 // destroys an RR list (and optionally, items)
375 void rr_list_destroy(struct rr_list
*rr
, char destroy_items
) {
376 struct rr_list
*rr_next
;
378 for (; rr
; rr
= rr_next
) {
381 rr_entry_destroy(rr
->e
);
386 int rr_list_count(struct rr_list
*rr
) {
388 for (; rr
; i
++, rr
= rr
->next
)
393 struct rr_entry
*rr_list_remove(struct rr_list
**rr_head
, struct rr_entry
*rr
) {
394 struct rr_list
*le
= *rr_head
, *pe
= NULL
;
395 for (; le
; le
= le
->next
) {
412 // appends an rr_entry to an RR list
413 // if the RR is already in the list, it will not be added
414 // RRs are compared by memory location - not its contents
415 // return value of 0 means item not added
416 int rr_list_append(struct rr_list
**rr_head
, struct rr_entry
*rr
) {
417 struct rr_list
*node
= malloc(sizeof(struct rr_list
));
422 if (*rr_head
== NULL
) {
425 struct rr_list
*e
= *rr_head
, *taile
= NULL
;
426 for (; e
; e
= e
->next
) {
427 // already in list - don't add
438 DEBUG_PRINTF("taile not given a value.\n");
441 die("can not allocate memory for \"node\" in tinysvcmdns.");
446 #define FILL_RR_ENTRY(rr, _name, _type) \
449 rr->ttl = DEFAULT_TTL; \
450 rr->cache_flush = 1; \
453 struct rr_entry
*rr_create_a(uint8_t *name
, uint32_t addr
) {
454 DECL_MALLOC_ZERO_STRUCT(rr
, rr_entry
);
456 FILL_RR_ENTRY(rr
, name
, RR_A
);
457 rr
->data
.A
.addr
= addr
;
458 rr
->ttl
= DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME
; // 120 seconds -- see RFC 6762 Section 10
460 die("could not allocate an RR data structure in tinysvcmdns.c.");
465 struct rr_entry
*rr_create_aaaa(uint8_t *name
, struct in6_addr
*addr
) {
466 DECL_MALLOC_ZERO_STRUCT(rr
, rr_entry
);
468 FILL_RR_ENTRY(rr
, name
, RR_AAAA
);
469 rr
->data
.AAAA
.addr
= addr
;
470 rr
->ttl
= DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME
; // 120 seconds -- see RFC 6762 Section 10
472 die("could not allocate an RR 2 data structure in tinysvcmdns.c.");
477 struct rr_entry
*rr_create_srv(uint8_t *name
, uint16_t port
, uint8_t *target
) {
478 DECL_MALLOC_ZERO_STRUCT(rr
, rr_entry
);
480 FILL_RR_ENTRY(rr
, name
, RR_SRV
);
481 rr
->data
.SRV
.port
= port
;
482 rr
->data
.SRV
.target
= target
;
484 die("could not allocate an RR 3 data structure in tinysvcmdns.c.");
489 struct rr_entry
*rr_create_ptr(uint8_t *name
, struct rr_entry
*d_rr
) {
490 DECL_MALLOC_ZERO_STRUCT(rr
, rr_entry
);
492 FILL_RR_ENTRY(rr
, name
, RR_PTR
);
493 rr
->cache_flush
= 0; // PTRs shouldn't have their cache flush bit set
494 rr
->data
.PTR
.entry
= d_rr
;
496 die("could not allocate an RR 4 data structure in tinysvcmdns.c.");
501 struct rr_entry
*rr_create(uint8_t *name
, enum rr_type type
) {
502 DECL_MALLOC_ZERO_STRUCT(rr
, rr_entry
);
504 FILL_RR_ENTRY(rr
, name
, type
);
506 die("could not allocate an RR 4 data structure in tinysvcmdns.c.");
511 void rr_set_nsec(struct rr_entry
*rr_nsec
, enum rr_type type
) {
512 assert((rr_nsec
->type
= RR_NSEC
));
513 assert((type
/ 8) < sizeof(rr_nsec
->data
.NSEC
.bitmap
));
515 rr_nsec
->data
.NSEC
.bitmap
[type
/ 8] = 1 << (7 - (type
% 8));
518 void rr_add_txt(struct rr_entry
*rr_txt
, const char *txt
) {
519 struct rr_data_txt
*txt_rec
;
520 assert(rr_txt
->type
== RR_TXT
);
522 txt_rec
= &rr_txt
->data
.TXT
;
524 // is current data filled?
525 if (txt_rec
->txt
== NULL
) {
526 txt_rec
->txt
= create_label(txt
);
530 // find the last node
531 for (; txt_rec
->next
; txt_rec
= txt_rec
->next
)
534 // create a new empty node
535 txt_rec
->next
= malloc(sizeof(struct rr_data_txt
));
537 txt_rec
= txt_rec
->next
;
538 txt_rec
->txt
= create_label(txt
);
539 txt_rec
->next
= NULL
;
542 // adds a record to an rr_group
543 void rr_group_add(struct rr_group
**group
, struct rr_entry
*rr
) {
549 g
= rr_group_find(*group
, rr
->name
);
551 rr_list_append(&g
->rr
, rr
);
556 MALLOC_ZERO_STRUCT(g
, rr_group
);
558 g
->name
= dup_nlabel(rr
->name
);
559 rr_list_append(&g
->rr
, rr
);
565 die("can not allocate memory for \"g\" in tinysvcmdns");
569 // finds a rr_group matching the given name
570 struct rr_group
*rr_group_find(struct rr_group
*g
, uint8_t *name
) {
571 for (; g
; g
= g
->next
) {
572 if (cmp_nlabel(g
->name
, name
) == 0)
578 struct rr_entry
*rr_entry_find(struct rr_list
*rr_list
, uint8_t *name
, uint16_t type
) {
579 struct rr_list
*rr
= rr_list
;
580 for (; rr
; rr
= rr
->next
) {
581 if (rr
->e
->type
== type
&& cmp_nlabel(rr
->e
->name
, name
) == 0)
587 // looks for a matching entry in rr_list
588 // if entry is a PTR, we need to check if the PTR target also matches
589 struct rr_entry
*rr_entry_match(struct rr_list
*rr_list
, struct rr_entry
*entry
) {
590 struct rr_list
*rr
= rr_list
;
591 for (; rr
; rr
= rr
->next
) {
592 if (rr
->e
->type
== entry
->type
&& cmp_nlabel(rr
->e
->name
, entry
->name
) == 0) {
593 if (entry
->type
!= RR_PTR
) {
595 } else if (cmp_nlabel(MDNS_RR_GET_PTR_NAME(entry
), MDNS_RR_GET_PTR_NAME(rr
->e
)) == 0) {
596 // if it's a PTR, we need to make sure PTR target also matches
604 void rr_group_destroy(struct rr_group
*group
) {
605 struct rr_group
*g
= group
;
608 struct rr_group
*nextg
= g
->next
;
610 rr_list_destroy(g
->rr
, 1);
616 uint8_t *mdns_write_u16(uint8_t *ptr
, const uint16_t v
) {
617 *ptr
++ = (uint8_t)(v
>> 8) & 0xFF;
618 *ptr
++ = (uint8_t)(v
>> 0) & 0xFF;
622 uint8_t *mdns_write_u32(uint8_t *ptr
, const uint32_t v
) {
623 *ptr
++ = (uint8_t)(v
>> 24) & 0xFF;
624 *ptr
++ = (uint8_t)(v
>> 16) & 0xFF;
625 *ptr
++ = (uint8_t)(v
>> 8) & 0xFF;
626 *ptr
++ = (uint8_t)(v
>> 0) & 0xFF;
630 uint16_t mdns_read_u16(const uint8_t *ptr
) {
631 return ((ptr
[0] & 0xFF) << 8) | ((ptr
[1] & 0xFF) << 0);
634 uint32_t mdns_read_u32(const uint8_t *ptr
) {
635 return ((ptr
[0] & 0xFF) << 24) | ((ptr
[1] & 0xFF) << 16) | ((ptr
[2] & 0xFF) << 8) |
636 ((ptr
[3] & 0xFF) << 0);
639 // initialize the packet for reply
640 // clears the packet of list structures but not its list items
641 void mdns_init_reply(struct mdns_pkt
*pkt
, uint16_t id
) {
642 // copy transaction ID
646 pkt
->flags
= MDNS_FLAG_RESP
| MDNS_FLAG_AA
;
648 rr_list_destroy(pkt
->rr_qn
, 0);
649 rr_list_destroy(pkt
->rr_ans
, 0);
650 rr_list_destroy(pkt
->rr_auth
, 0);
651 rr_list_destroy(pkt
->rr_add
, 0);
660 pkt
->num_auth_rr
= 0;
664 // destroys an mdns_pkt struct, including its contents
665 void mdns_pkt_destroy(struct mdns_pkt
*p
) {
666 rr_list_destroy(p
->rr_qn
, 1);
667 rr_list_destroy(p
->rr_ans
, 1);
668 rr_list_destroy(p
->rr_auth
, 1);
669 rr_list_destroy(p
->rr_add
, 1);
674 // parse the MDNS questions section
675 // stores the parsed data in the given mdns_pkt struct
676 static size_t mdns_parse_qn(uint8_t *pkt_buf
, size_t pkt_len
, size_t off
, struct mdns_pkt
*pkt
) {
677 const uint8_t *p
= pkt_buf
+ off
;
683 rr
= malloc(sizeof(struct rr_entry
));
685 memset(rr
, 0, sizeof(struct rr_entry
));
689 name
= uncompress_nlabel(pkt_buf
, pkt_len
, off
);
693 p
+= label_len(pkt_buf
, pkt_len
, off
);
696 rr
->type
= mdns_read_u16(p
);
697 p
+= sizeof(uint16_t);
699 rr
->unicast_query
= (*p
& 0x80) == 0x80;
700 rr
->rr_class
= mdns_read_u16(p
) & ~0x80;
701 p
+= sizeof(uint16_t);
703 rr_list_append(&pkt
->rr_qn
, rr
);
705 return p
- (pkt_buf
+ off
);
712 // parse the MDNS RR section
713 // stores the parsed data in the given mdns_pkt struct
714 static size_t mdns_parse_rr(uint8_t *pkt_buf
, size_t pkt_len
, size_t off
, struct mdns_pkt
*pkt
) {
715 const uint8_t *p
= pkt_buf
+ off
;
716 const uint8_t *e
= pkt_buf
+ pkt_len
;
719 size_t rr_data_len
= 0;
720 struct rr_data_txt
*txt_rec
;
728 rr
= malloc(sizeof(struct rr_entry
));
730 memset(rr
, 0, sizeof(struct rr_entry
));
734 name
= uncompress_nlabel(pkt_buf
, pkt_len
, off
);
738 // parse the MDNS RR section
739 p
+= label_len(pkt_buf
, pkt_len
, off
);
742 rr
->type
= mdns_read_u16(p
);
743 p
+= sizeof(uint16_t);
745 rr
->cache_flush
= (*p
& 0x80) == 0x80;
746 rr
->rr_class
= mdns_read_u16(p
) & ~0x80;
747 p
+= sizeof(uint16_t);
749 rr
->ttl
= mdns_read_u32(p
);
750 p
+= sizeof(uint32_t);
753 rr_data_len
= mdns_read_u16(p
);
754 p
+= sizeof(uint16_t);
756 if (p
+ rr_data_len
> e
) {
757 DEBUG_PRINTF("rr_data_len goes beyond packet buffer: %lu > %lu\n", rr_data_len
, e
- p
);
758 rr_entry_destroy(rr
);
764 // see if we can parse the RR data
767 if (rr_data_len
< sizeof(uint32_t)) {
768 DEBUG_PRINTF("invalid rr_data_len=%lu for A record\n", rr_data_len
);
772 rr
->data
.A
.addr
= ntohl(mdns_read_u32(p
)); /* addr already in net order */
773 p
+= sizeof(uint32_t);
777 if (rr_data_len
< sizeof(struct in6_addr
)) {
778 DEBUG_PRINTF("invalid rr_data_len=%lu for AAAA record\n", rr_data_len
);
782 rr
->data
.AAAA
.addr
= malloc(sizeof(struct in6_addr
));
784 for (i
= 0; i
< sizeof(struct in6_addr
); i
++)
785 rr
->data
.AAAA
.addr
->s6_addr
[i
] = p
[i
];
786 p
+= sizeof(struct in6_addr
);
790 rr
->data
.PTR
.name
= uncompress_nlabel(pkt_buf
, pkt_len
, p
- pkt_buf
);
791 if (rr
->data
.PTR
.name
== NULL
) {
792 DEBUG_PRINTF("unable to parse/uncompress label for PTR name\n");
800 txt_rec
= &rr
->data
.TXT
;
802 // not supposed to happen, but we should handle it
803 if (rr_data_len
== 0) {
804 DEBUG_PRINTF("WARN: rr_data_len for TXT is 0\n");
805 txt_rec
->txt
= create_label("");
810 txt_rec
->txt
= copy_label(pkt_buf
, pkt_len
, p
- pkt_buf
);
811 if (txt_rec
->txt
== NULL
) {
812 DEBUG_PRINTF("unable to copy label for TXT record\n");
816 p
+= txt_rec
->txt
[0] + 1;
821 // allocate another record
822 txt_rec
->next
= malloc(sizeof(struct rr_data_txt
));
823 txt_rec
= txt_rec
->next
;
824 txt_rec
->next
= NULL
;
829 // skip to end of RR data
833 // if there was a parse error, destroy partial rr_entry
835 rr_entry_destroy(rr
);
839 rr_list_append(&pkt
->rr_ans
, rr
);
841 return p
- (pkt_buf
+ off
);
848 // parse a MDNS packet into an mdns_pkt struct
849 struct mdns_pkt
*mdns_parse_pkt(uint8_t *pkt_buf
, size_t pkt_len
) {
850 uint8_t *p
= pkt_buf
;
852 struct mdns_pkt
*pkt
;
858 MALLOC_ZERO_STRUCT(pkt
, mdns_pkt
);
861 die("cannot allocate memory for \"pkt\" in tinysvcmdns.c.");
864 pkt
->id
= mdns_read_u16(p
);
865 p
+= sizeof(uint16_t);
866 pkt
->flags
= mdns_read_u16(p
);
867 p
+= sizeof(uint16_t);
868 pkt
->num_qn
= mdns_read_u16(p
);
869 p
+= sizeof(uint16_t);
870 pkt
->num_ans_rr
= mdns_read_u16(p
);
871 p
+= sizeof(uint16_t);
872 pkt
->num_auth_rr
= mdns_read_u16(p
);
873 p
+= sizeof(uint16_t);
874 pkt
->num_add_rr
= mdns_read_u16(p
);
875 p
+= sizeof(uint16_t);
880 for (i
= 0; i
< pkt
->num_qn
; i
++) {
881 size_t l
= mdns_parse_qn(pkt_buf
, pkt_len
, off
, pkt
);
883 DEBUG_PRINTF("error parsing question #%d\n", i
);
884 mdns_pkt_destroy(pkt
);
892 for (i
= 0; i
< pkt
->num_ans_rr
; i
++) {
893 size_t l
= mdns_parse_rr(pkt_buf
, pkt_len
, off
, pkt
);
895 DEBUG_PRINTF("error parsing answer #%d\n", i
);
896 mdns_pkt_destroy(pkt
);
903 // TODO: parse the authority and additional RR sections
908 // encodes a name (label) into a packet using the name compression scheme
909 // encoded names will be added to the compression list for subsequent use
910 static size_t mdns_encode_name(uint8_t *pkt_buf
, __attribute__((unused
)) size_t pkt_len
, size_t off
,
911 const uint8_t *name
, struct name_comp
*comp
) {
912 struct name_comp
*c
, *c_tail
= NULL
;
913 uint8_t *p
= pkt_buf
+ off
;
918 // find match for compression
919 for (c
= comp
; c
; c
= c
->next
) {
920 if (cmp_nlabel(name
, c
->label
) == 0) {
921 mdns_write_u16(p
, 0xC000 | (c
->pos
& ~0xC000));
922 return len
+ sizeof(uint16_t);
930 int segment_len
= *name
+ 1;
931 strncpy((char *)p
, (char *)name
, segment_len
);
933 // cache the name for subsequent compression
934 DECL_MALLOC_ZERO_STRUCT(new_c
, name_comp
);
936 new_c
->label
= (uint8_t *)name
;
937 new_c
->pos
= p
- pkt_buf
;
938 c_tail
->next
= new_c
;
940 // advance to next name segment
947 *p
= '\0'; // root "label"
953 // encodes an RR entry at the given offset
954 // returns the size of the entire RR entry
955 static size_t mdns_encode_rr(uint8_t *pkt_buf
, size_t pkt_len
, size_t off
, struct rr_entry
*rr
,
956 struct name_comp
*comp
) {
957 uint8_t *p
= pkt_buf
+ off
, *p_data
;
959 struct rr_data_txt
*txt_rec
;
963 assert(off
< pkt_len
);
966 l
= mdns_encode_name(pkt_buf
, pkt_len
, off
, rr
->name
, comp
);
971 p
= mdns_write_u16(p
, rr
->type
);
973 // class & cache flush
974 p
= mdns_write_u16(p
, (rr
->rr_class
& ~0x8000) | (rr
->cache_flush
<< 15));
977 p
= mdns_write_u32(p
, rr
->ttl
);
979 // data length (filled in later)
980 p
+= sizeof(uint16_t);
982 // start of data marker
987 /* htonl() needed coz addr already in net order */
988 p
= mdns_write_u32(p
, htonl(rr
->data
.A
.addr
));
992 for (i
= 0; i
< sizeof(struct in6_addr
); i
++)
993 *p
++ = rr
->data
.AAAA
.addr
->s6_addr
[i
];
997 label
= rr
->data
.PTR
.name
? rr
->data
.PTR
.name
: rr
->data
.PTR
.entry
->name
;
998 p
+= mdns_encode_name(pkt_buf
, pkt_len
, p
- pkt_buf
, label
, comp
);
1002 txt_rec
= &rr
->data
.TXT
;
1003 for (; txt_rec
; txt_rec
= txt_rec
->next
) {
1004 int len
= txt_rec
->txt
[0] + 1;
1005 strncpy((char *)p
, (char *)txt_rec
->txt
, len
);
1011 p
= mdns_write_u16(p
, rr
->data
.SRV
.priority
);
1013 p
= mdns_write_u16(p
, rr
->data
.SRV
.weight
);
1015 p
= mdns_write_u16(p
, rr
->data
.SRV
.port
);
1017 p
+= mdns_encode_name(pkt_buf
, pkt_len
, p
- pkt_buf
, rr
->data
.SRV
.target
, comp
);
1021 p
+= mdns_encode_name(pkt_buf
, pkt_len
, p
- pkt_buf
, rr
->name
, comp
);
1023 *p
++ = 0; // bitmap window/block number
1025 *p
++ = sizeof(rr
->data
.NSEC
.bitmap
); // bitmap length
1027 for (i
= 0; i
< sizeof(rr
->data
.NSEC
.bitmap
); i
++)
1028 *p
++ = rr
->data
.NSEC
.bitmap
[i
];
1033 DEBUG_PRINTF("unhandled rr type 0x%02x\n", rr
->type
);
1036 // calculate data length based on p
1039 // fill in the length
1040 mdns_write_u16(p
- l
- sizeof(uint16_t), l
);
1042 return p
- pkt_buf
- off
;
1045 // encodes a MDNS packet from the given mdns_pkt struct into a buffer
1046 // returns the size of the entire MDNS packet
1047 size_t mdns_encode_pkt(struct mdns_pkt
*answer
, uint8_t *pkt_buf
, size_t pkt_len
) {
1048 struct name_comp
*comp
;
1049 uint8_t *p
= pkt_buf
;
1050 // uint8_t *e = pkt_buf + pkt_len;
1054 assert(answer
!= NULL
);
1055 assert(pkt_len
>= 12);
1060 // this is an Answer - number of qns should be zero
1061 assert(answer
->num_qn
== 0);
1063 p
= mdns_write_u16(p
, answer
->id
);
1064 p
= mdns_write_u16(p
, answer
->flags
);
1065 p
= mdns_write_u16(p
, answer
->num_qn
);
1066 p
= mdns_write_u16(p
, answer
->num_ans_rr
);
1067 p
= mdns_write_u16(p
, answer
->num_auth_rr
);
1068 p
= mdns_write_u16(p
, answer
->num_add_rr
);
1072 // allocate list for name compression
1073 comp
= malloc(sizeof(struct name_comp
));
1076 memset(comp
, 0, sizeof(struct name_comp
));
1079 comp
->label
= (uint8_t *)"";
1082 // skip encoding of qn
1084 struct rr_list
*rr_set
[] = {answer
->rr_ans
, answer
->rr_auth
, answer
->rr_add
};
1086 // encode answer, authority and additional RRs
1087 for (i
= 0; i
< sizeof(rr_set
) / sizeof(rr_set
[0]); i
++) {
1088 struct rr_list
*rr
= rr_set
[i
];
1089 for (; rr
; rr
= rr
->next
) {
1090 size_t l
= mdns_encode_rr(pkt_buf
, pkt_len
, off
, rr
->e
, comp
);
1093 if (off
>= pkt_len
) {
1094 DEBUG_PRINTF("packet buffer too small\n");
1100 // free name compression list
1102 struct name_comp
*c
= comp
->next
;
1110 //******************************************************//
1112 //******************************************************//
1115 #include <winsock2.h>
1116 #include <ws2tcpip.h>
1119 #include <arpa/inet.h>
1121 #include <netinet/in.h>
1122 #include <sys/ioctl.h>
1123 #include <sys/select.h>
1124 #include <sys/socket.h>
1130 #include <pthread.h>
1136 #include <sys/stat.h>
1137 #include <sys/types.h>
1141 * Define a proper IP socket level if not already done.
1142 * Required to compile on OS X
1145 #define SOL_IP IPPROTO_IP
1148 #define MDNS_ADDR "224.0.0.251"
1149 #define MDNS_PORT 5353
1151 #define PACKET_SIZE 65536
1153 #define SERVICES_DNS_SD_NLABEL ((uint8_t *)"\x09_services\x07_dns-sd\x04_udp\x05local")
1156 pthread_mutex_t data_lock
;
1161 struct rr_group
*group
;
1162 struct rr_list
*announce
;
1163 struct rr_list
*services
;
1167 struct mdns_service
{
1168 struct rr_list
*entries
;
1171 /////////////////////////////////
1173 static int create_recv_sock() {
1174 int sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
1176 log_message(LOG_ERR
, "recv socket(): %m");
1183 if ((r
= setsockopt(sd
, SOL_SOCKET
, SO_REUSEADDR
, (char *)&on
, sizeof(on
))) < 0) {
1184 log_message(LOG_ERR
, "recv setsockopt(SO_REUSEADDR): %m");
1188 /* bind to an address */
1189 struct sockaddr_in serveraddr
;
1190 memset(&serveraddr
, 0, sizeof(serveraddr
));
1191 serveraddr
.sin_family
= AF_INET
;
1192 serveraddr
.sin_port
= htons(MDNS_PORT
);
1193 serveraddr
.sin_addr
.s_addr
= htonl(INADDR_ANY
); /* receive multicast */
1194 if ((r
= bind(sd
, (struct sockaddr
*)&serveraddr
, sizeof(serveraddr
))) < 0) {
1195 log_message(LOG_ERR
, "recv bind(): %m");
1198 // add membership to receiving socket
1199 struct ip_mreq mreq
;
1200 memset(&mreq
, 0, sizeof(struct ip_mreq
));
1201 mreq
.imr_interface
.s_addr
= htonl(INADDR_ANY
);
1202 mreq
.imr_multiaddr
.s_addr
= inet_addr(MDNS_ADDR
);
1203 if ((r
= setsockopt(sd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, (char *)&mreq
, sizeof(mreq
))) < 0) {
1204 log_message(LOG_ERR
, "recv setsockopt(IP_ADD_MEMBERSHIP): %m");
1208 // enable loopback in case someone else needs the data
1209 if ((r
= setsockopt(sd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *)&on
, sizeof(on
))) < 0) {
1210 log_message(LOG_ERR
, "recv setsockopt(IP_MULTICAST_LOOP): %m");
1215 if ((r
= setsockopt(sd
, SOL_IP
, IP_PKTINFO
, (char *)&on
, sizeof(on
))) < 0) {
1216 log_message(LOG_ERR
, "recv setsockopt(IP_PKTINFO): %m");
1224 static ssize_t
send_packet(int fd
, const void *data
, size_t len
) {
1225 static struct sockaddr_in toaddr
;
1226 if (toaddr
.sin_family
!= AF_INET
) {
1227 memset(&toaddr
, 0, sizeof(struct sockaddr_in
));
1228 toaddr
.sin_family
= AF_INET
;
1229 toaddr
.sin_port
= htons(MDNS_PORT
);
1230 toaddr
.sin_addr
.s_addr
= inet_addr(MDNS_ADDR
);
1233 return sendto(fd
, data
, len
, 0, (struct sockaddr
*)&toaddr
, sizeof(struct sockaddr_in
));
1236 // populate the specified list which matches the RR name and type
1237 // type can be RR_ANY, which populates all entries EXCEPT RR_NSEC
1238 static int populate_answers(struct mdnsd
*svr
, struct rr_list
**rr_head
, uint8_t *name
,
1239 enum rr_type type
) {
1242 // check if we have the records
1243 pthread_mutex_lock(&svr
->data_lock
);
1244 struct rr_group
*ans_grp
= rr_group_find(svr
->group
, name
);
1245 if (ans_grp
== NULL
) {
1246 pthread_mutex_unlock(&svr
->data_lock
);
1250 // decide which records should go into answers
1251 struct rr_list
*n
= ans_grp
->rr
;
1252 for (; n
; n
= n
->next
) {
1253 // exclude NSEC for RR_ANY
1254 if (type
== RR_ANY
&& n
->e
->type
== RR_NSEC
)
1257 if ((type
== n
->e
->type
|| type
== RR_ANY
) && cmp_nlabel(name
, n
->e
->name
) == 0) {
1258 num_ans
+= rr_list_append(rr_head
, n
->e
);
1262 pthread_mutex_unlock(&svr
->data_lock
);
1267 // given a list of RRs, look up related records and add them
1268 static void add_related_rr(struct mdnsd
*svr
, struct rr_list
*list
, struct mdns_pkt
*reply
) {
1269 for (; list
; list
= list
->next
) {
1270 struct rr_entry
*ans
= list
->e
;
1272 switch (ans
->type
) {
1274 // target host A, AAAA records
1275 reply
->num_add_rr
+= populate_answers(svr
, &reply
->rr_add
, MDNS_RR_GET_PTR_NAME(ans
), RR_ANY
);
1279 // target host A, AAAA records
1280 reply
->num_add_rr
+= populate_answers(svr
, &reply
->rr_add
, ans
->data
.SRV
.target
, RR_ANY
);
1282 // perhaps TXT records of the same name?
1283 // if we use RR_ANY, we risk pulling in the same RR_SRV
1284 reply
->num_add_rr
+= populate_answers(svr
, &reply
->rr_add
, ans
->name
, RR_TXT
);
1289 reply
->num_add_rr
+= populate_answers(svr
, &reply
->rr_add
, ans
->name
, RR_NSEC
);
1299 // creates an announce packet given the type name PTR
1300 static void announce_srv(struct mdnsd
*svr
, struct mdns_pkt
*reply
, uint8_t *name
) {
1301 mdns_init_reply(reply
, 0);
1303 reply
->num_ans_rr
+= populate_answers(svr
, &reply
->rr_ans
, name
, RR_PTR
);
1305 // remember to add the services dns-sd PTR too
1306 reply
->num_ans_rr
+= populate_answers(svr
, &reply
->rr_ans
, SERVICES_DNS_SD_NLABEL
, RR_PTR
);
1308 // see if we can match additional records for answers
1309 add_related_rr(svr
, reply
->rr_ans
, reply
);
1311 // additional records for additional records
1312 add_related_rr(svr
, reply
->rr_add
, reply
);
1315 // processes the incoming MDNS packet
1316 // returns >0 if processed, 0 otherwise
1317 static int process_mdns_pkt(struct mdnsd
*svr
, struct mdns_pkt
*pkt
, struct mdns_pkt
*reply
) {
1320 assert(pkt
!= NULL
);
1322 // is it standard query?
1323 if ((pkt
->flags
& MDNS_FLAG_RESP
) == 0 && MDNS_FLAG_GET_OPCODE(pkt
->flags
) == 0) {
1324 mdns_init_reply(reply
, pkt
->id
);
1326 DEBUG_PRINTF("flags = %04x, qn = %d, ans = %d, add = %d\n", pkt
->flags
, pkt
->num_qn
,
1327 pkt
->num_ans_rr
, pkt
->num_add_rr
);
1329 // loop through questions
1330 struct rr_list
*qnl
= pkt
->rr_qn
;
1331 for (i
= 0; i
< pkt
->num_qn
; i
++, qnl
= qnl
->next
) {
1332 struct rr_entry
*qn
= qnl
->e
;
1333 int num_ans_added
= 0;
1335 char *namestr
= nlabel_to_str(qn
->name
);
1336 DEBUG_PRINTF("qn #%d: type %s (%02x) %s - ", i
, rr_get_type_name(qn
->type
), qn
->type
,
1340 // check if it's a unicast query - we ignore those
1341 if (qn
->unicast_query
) {
1342 DEBUG_PRINTF("skipping unicast query\n");
1346 num_ans_added
= populate_answers(svr
, &reply
->rr_ans
, qn
->name
, qn
->type
);
1347 reply
->num_ans_rr
+= num_ans_added
;
1349 DEBUG_PRINTF("added %d answers\n", num_ans_added
);
1352 // remove our replies if they were already in their answers
1353 struct rr_list
*ans
= NULL
, *prev_ans
= NULL
;
1354 for (ans
= reply
->rr_ans
; ans
;) {
1355 struct rr_list
*next_ans
= ans
->next
;
1356 struct rr_entry
*known_ans
= rr_entry_match(pkt
->rr_ans
, ans
->e
);
1358 // discard answers that have at least half of the actual TTL
1359 if (known_ans
!= NULL
&& known_ans
->ttl
>= ans
->e
->ttl
/ 2) {
1360 char *namestr
= nlabel_to_str(ans
->e
->name
);
1361 DEBUG_PRINTF("removing answer for %s\n", namestr
);
1364 // check if list item is head
1365 if (prev_ans
== NULL
)
1366 reply
->rr_ans
= ans
->next
;
1368 prev_ans
->next
= ans
->next
;
1373 // adjust answer count
1374 reply
->num_ans_rr
--;
1381 // see if we can match additional records for answers
1382 add_related_rr(svr
, reply
->rr_ans
, reply
);
1384 // additional records for additional records
1385 add_related_rr(svr
, reply
->rr_add
, reply
);
1389 return reply
->num_ans_rr
;
1395 int create_pipe(int handles
[2]) {
1397 SOCKET sock
= socket(AF_INET
, SOCK_STREAM
, 0);
1398 if (sock
== INVALID_SOCKET
) {
1401 struct sockaddr_in serv_addr
;
1402 memset(&serv_addr
, 0, sizeof(serv_addr
));
1403 serv_addr
.sin_family
= AF_INET
;
1404 serv_addr
.sin_port
= htons(0);
1405 serv_addr
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
1406 if (bind(sock
, (struct sockaddr
*)&serv_addr
, sizeof(serv_addr
)) == SOCKET_ERROR
) {
1410 if (listen(sock
, 1) == SOCKET_ERROR
) {
1414 int len
= sizeof(serv_addr
);
1415 if (getsockname(sock
, (SOCKADDR
*)&serv_addr
, &len
) == SOCKET_ERROR
) {
1419 if ((handles
[1] = socket(PF_INET
, SOCK_STREAM
, 0)) == INVALID_SOCKET
) {
1423 if (connect(handles
[1], (struct sockaddr
*)&serv_addr
, len
) == SOCKET_ERROR
) {
1427 if ((handles
[0] = accept(sock
, (struct sockaddr
*)&serv_addr
, &len
)) == INVALID_SOCKET
) {
1428 closesocket((SOCKET
)handles
[1]);
1429 handles
[1] = INVALID_SOCKET
;
1436 return pipe(handles
);
1440 int read_pipe(int s
, char *buf
, int len
) {
1442 int ret
= recv(s
, buf
, len
, 0);
1443 if (ret
< 0 && WSAGetLastError() == WSAECONNRESET
) {
1448 return read(s
, buf
, len
);
1452 int write_pipe(int s
, char *buf
, int len
) {
1454 return send(s
, buf
, len
, 0);
1456 return write(s
, buf
, len
);
1460 int close_pipe(int s
) {
1462 return closesocket(s
);
1468 // main loop to receive, process and send out MDNS replies
1469 // also handles MDNS service announces
1470 void *main_loop(struct mdnsd
*svr
) {
1472 int max_fd
= svr
->sockfd
;
1473 char notify_buf
[2]; // buffer for reading of notify_pipe
1475 void *pkt_buffer
= malloc(PACKET_SIZE
);
1477 if (svr
->notify_pipe
[0] > max_fd
)
1478 max_fd
= svr
->notify_pipe
[0];
1480 struct mdns_pkt
*mdns_reply
= malloc(sizeof(struct mdns_pkt
));
1482 memset(mdns_reply
, 0, sizeof(struct mdns_pkt
));
1484 die("could not allocate memory for \"mdns_reply\" in tinysvcmdns");
1486 while (!svr
->stop_flag
) {
1487 FD_ZERO(&sockfd_set
);
1488 FD_SET(svr
->sockfd
, &sockfd_set
);
1489 FD_SET(svr
->notify_pipe
[0], &sockfd_set
);
1490 select(max_fd
+ 1, &sockfd_set
, NULL
, NULL
, NULL
);
1492 if (FD_ISSET(svr
->notify_pipe
[0], &sockfd_set
)) {
1493 // flush the notify_pipe
1494 read_pipe(svr
->notify_pipe
[0], (char *)¬ify_buf
, 1);
1495 } else if (FD_ISSET(svr
->sockfd
, &sockfd_set
)) {
1496 struct sockaddr_in fromaddr
;
1497 socklen_t sockaddr_size
= sizeof(struct sockaddr_in
);
1499 ssize_t recvsize
= recvfrom(svr
->sockfd
, pkt_buffer
, PACKET_SIZE
, 0,
1500 (struct sockaddr
*)&fromaddr
, &sockaddr_size
);
1502 log_message(LOG_ERR
, "recv(): %m");
1505 DEBUG_PRINTF("data from=%s size=%ld\n", inet_ntoa(fromaddr
.sin_addr
), (long)recvsize
);
1506 struct mdns_pkt
*mdns
= mdns_parse_pkt(pkt_buffer
, recvsize
);
1508 if (process_mdns_pkt(svr
, mdns
, mdns_reply
)) {
1509 size_t replylen
= mdns_encode_pkt(mdns_reply
, pkt_buffer
, PACKET_SIZE
);
1510 send_packet(svr
->sockfd
, pkt_buffer
, replylen
);
1511 } else if (mdns
->num_qn
== 0) {
1512 DEBUG_PRINTF("(no questions in packet)\n\n");
1515 mdns_pkt_destroy(mdns
);
1519 // send out announces
1521 struct rr_entry
*ann_e
= NULL
;
1523 // extract from head of list
1524 pthread_mutex_lock(&svr
->data_lock
);
1526 ann_e
= rr_list_remove(&svr
->announce
, svr
->announce
->e
);
1527 pthread_mutex_unlock(&svr
->data_lock
);
1532 char *namestr
= nlabel_to_str(ann_e
->name
);
1533 DEBUG_PRINTF("sending announce for %s\n", namestr
);
1536 announce_srv(svr
, mdns_reply
, ann_e
->name
);
1538 if (mdns_reply
->num_ans_rr
> 0) {
1539 size_t replylen
= mdns_encode_pkt(mdns_reply
, pkt_buffer
, PACKET_SIZE
);
1540 send_packet(svr
->sockfd
, pkt_buffer
, replylen
);
1545 // main thread terminating. send out "goodbye packets" for services
1546 mdns_init_reply(mdns_reply
, 0);
1548 pthread_mutex_lock(&svr
->data_lock
);
1549 struct rr_list
*svc_le
= svr
->services
;
1550 for (; svc_le
; svc_le
= svc_le
->next
) {
1553 mdns_reply
->num_ans_rr
+= rr_list_append(&mdns_reply
->rr_ans
, svc_le
->e
);
1555 pthread_mutex_unlock(&svr
->data_lock
);
1558 if (mdns_reply
->num_ans_rr
> 0) {
1559 size_t replylen
= mdns_encode_pkt(mdns_reply
, pkt_buffer
, PACKET_SIZE
);
1560 send_packet(svr
->sockfd
, pkt_buffer
, replylen
);
1564 mdns_init_reply(mdns_reply
, 0);
1569 close_pipe(svr
->sockfd
);
1575 /////////////////////////////////////////////////////
1577 void mdnsd_set_hostname(struct mdnsd
*svr
, const char *hostname
, uint32_t ip
) {
1578 struct rr_entry
*a_e
= NULL
, *nsec_e
= NULL
;
1580 // currently can't be called twice
1581 // don't ask me what happens if the IP changes
1582 assert(svr
->hostname
== NULL
);
1584 a_e
= rr_create_a(create_nlabel(hostname
), ip
); // 120 seconds automatically
1586 nsec_e
= rr_create(create_nlabel(hostname
), RR_NSEC
);
1587 nsec_e
->ttl
= DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME
; // set to 120 seconds (default is 4500)
1588 rr_set_nsec(nsec_e
, RR_A
);
1590 pthread_mutex_lock(&svr
->data_lock
);
1591 svr
->hostname
= create_nlabel(hostname
);
1592 rr_group_add(&svr
->group
, a_e
);
1593 rr_group_add(&svr
->group
, nsec_e
);
1594 pthread_mutex_unlock(&svr
->data_lock
);
1597 void mdnsd_set_hostname_v6(struct mdnsd
*svr
, const char *hostname
, struct in6_addr
*addr
) {
1598 struct rr_entry
*aaaa_e
= NULL
, *nsec_e
= NULL
;
1600 // currently can't be called twice
1601 // don't ask me what happens if the IP changes
1602 assert(svr
->hostname
== NULL
);
1604 aaaa_e
= rr_create_aaaa(create_nlabel(hostname
), addr
); // 120 seconds automatically
1606 nsec_e
= rr_create(create_nlabel(hostname
), RR_NSEC
);
1607 nsec_e
->ttl
= DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME
; // set to 120 seconds (default is 4500)
1608 rr_set_nsec(nsec_e
, RR_AAAA
);
1610 pthread_mutex_lock(&svr
->data_lock
);
1611 svr
->hostname
= create_nlabel(hostname
);
1612 rr_group_add(&svr
->group
, aaaa_e
);
1613 rr_group_add(&svr
->group
, nsec_e
);
1614 pthread_mutex_unlock(&svr
->data_lock
);
1617 void mdnsd_add_rr(struct mdnsd
*svr
, struct rr_entry
*rr
) {
1618 pthread_mutex_lock(&svr
->data_lock
);
1619 rr_group_add(&svr
->group
, rr
);
1620 pthread_mutex_unlock(&svr
->data_lock
);
1623 struct mdns_service
*mdnsd_register_svc(struct mdnsd
*svr
, const char *instance_name
,
1624 const char *type
, uint16_t port
, const char *hostname
,
1625 const char *txt
[]) {
1626 struct rr_entry
*txt_e
= NULL
, *srv_e
= NULL
, *ptr_e
= NULL
, *bptr_e
= NULL
;
1628 uint8_t *inst_nlabel
, *type_nlabel
, *nlabel
= NULL
;
1629 struct mdns_service
*service
= malloc(sizeof(struct mdns_service
));
1631 memset(service
, 0, sizeof(struct mdns_service
));
1633 die("could not allocate memory for \"service\" in tinysvcmdns");
1634 // combine service name
1635 type_nlabel
= create_nlabel(type
);
1636 inst_nlabel
= create_label(instance_name
);
1638 nlabel
= join_nlabel(inst_nlabel
, type_nlabel
);
1640 die("could not allocate memory for \"inst_nlabel\" in tinysvcmdns");
1643 // create TXT record
1645 txt_e
= rr_create(dup_nlabel(nlabel
), RR_TXT
); // automatically 4500 seconds
1646 rr_list_append(&service
->entries
, txt_e
);
1650 rr_add_txt(txt_e
, *txt
);
1653 // create SRV record
1654 assert(hostname
|| svr
->hostname
); // either one as target
1655 target
= hostname
? create_nlabel(hostname
) : dup_nlabel(svr
->hostname
);
1657 srv_e
= rr_create_srv(dup_nlabel(nlabel
), port
, target
); // automatically 4500 seconds
1658 rr_list_append(&service
->entries
, srv_e
);
1660 // create PTR record for type
1661 ptr_e
= rr_create_ptr(type_nlabel
, srv_e
); // automatically 4500 seconds
1663 // create services PTR record for type
1664 // this enables the type to show up as a "service"
1665 bptr_e
= rr_create_ptr(dup_nlabel(SERVICES_DNS_SD_NLABEL
), ptr_e
); // automatically 4500 seconds
1667 // modify lists here
1668 pthread_mutex_lock(&svr
->data_lock
);
1671 rr_group_add(&svr
->group
, txt_e
);
1672 rr_group_add(&svr
->group
, srv_e
);
1673 rr_group_add(&svr
->group
, ptr_e
);
1674 rr_group_add(&svr
->group
, bptr_e
);
1676 // append PTR entry to announce list
1677 rr_list_append(&svr
->announce
, ptr_e
);
1678 rr_list_append(&svr
->services
, ptr_e
);
1680 pthread_mutex_unlock(&svr
->data_lock
);
1682 // don't free type_nlabel - it's with the PTR record
1688 write_pipe(svr
->notify_pipe
[1], ".", 1);
1693 void mdns_service_destroy(struct mdns_service
*srv
) {
1694 assert(srv
!= NULL
);
1695 rr_list_destroy(srv
->entries
, 0);
1699 struct mdnsd
*mdnsd_start() {
1701 pthread_attr_t attr
;
1703 struct mdnsd
*server
= malloc(sizeof(struct mdnsd
));
1705 memset(server
, 0, sizeof(struct mdnsd
));
1707 die("could not allocate memory for \"server\" in tinysvcmdns");
1709 if (create_pipe(server
->notify_pipe
) != 0) {
1710 log_message(LOG_ERR
, "pipe(): %m\n");
1715 server
->sockfd
= create_recv_sock();
1716 if (server
->sockfd
< 0) {
1717 log_message(LOG_ERR
, "unable to create recv socket");
1722 pthread_mutex_init(&server
->data_lock
, NULL
);
1725 pthread_attr_init(&attr
);
1726 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1728 if (pthread_create(&tid
, &attr
, (void *(*)(void *)) & main_loop
, (void *)server
) != 0) {
1729 pthread_mutex_destroy(&server
->data_lock
);
1737 void mdnsd_stop(struct mdnsd
*s
) {
1740 struct timeval tv
= {
1742 .tv_usec
= 500 * 1000,
1746 write_pipe(s
->notify_pipe
[1], ".", 1);
1748 while (s
->stop_flag
!= 2)
1749 select(0, NULL
, NULL
, NULL
, &tv
);
1751 close_pipe(s
->notify_pipe
[0]);
1752 close_pipe(s
->notify_pipe
[1]);
1754 pthread_mutex_destroy(&s
->data_lock
);
1755 rr_group_destroy(s
->group
);
1756 rr_list_destroy(s
->announce
, 0);
1757 rr_list_destroy(s
->services
, 0);