1 /*
2 * tinysvcmdns - a tiny MDNS implementation for publishing services
3 * Copyright (C) 2011 Darell Tan
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. 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 * 3. 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 #ifdef _WIN32
30 #include <winsock2.h>
31 #include <ws2tcpip.h>
32 #define LOG_ERR 3
33 #else
34 #include <sys/select.h>
35 #include <sys/socket.h>
36 #include <sys/ioctl.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <net/if.h>
40 #include <syslog.h>
41 #endif
42
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #include <signal.h>
47 #include <string.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <stdarg.h>
51 #include <unistd.h>
52 #include <assert.h>
53 #include <pthread.h>
54
55 /*
56 * Define a proper IP socket level if not already done.
57 * Required to compile on OS X
58 */
59 #ifndef SOL_IP
60 #define SOL_IP IPPROTO_IP
61 #endif
62
63 #include "mdns.h"
64 #include "mdnsd.h"
65
66 #define MDNS_ADDR "224.0.0.251"
67 #define MDNS_PORT 5353
68
69 #define PACKET_SIZE 65536
70
71 #define SERVICES_DNS_SD_NLABEL \
72 ((uint8_t *) "\x09_services\x07_dns-sd\x04_udp\x05local")
73
74 struct mdnsd {
75 pthread_mutex_t data_lock;
76 int sockfd;
77 int notify_pipe[2];
78 int stop_flag;
79
80 struct rr_group *group;
81 struct rr_list *announce;
82 struct rr_list *services;
83 uint8_t *hostname;
84 };
85
86 struct mdns_service {
87 struct rr_list *entries;
88 };
89
90 /////////////////////////////////
91
92
log_message(int loglevel,char * fmt_str,...)93 static void log_message(int loglevel, char *fmt_str, ...) {
94 va_list ap;
95 char buf[2048];
96
97 va_start(ap, fmt_str);
98 vsnprintf(buf, 2047, fmt_str, ap);
99 va_end(ap);
100 buf[2047] = 0;
101
102 fprintf(stderr, "%s\n", buf);
103 }
104
create_recv_sock(uint32_t bind_ip)105 static int create_recv_sock(uint32_t bind_ip) {
106 int sd = socket(AF_INET, SOCK_DGRAM, 0);
107 if (sd < 0) {
108 log_message(LOG_ERR, "recv socket(): %m");
109 return sd;
110 }
111
112 int r = -1;
113
114 int on = 1;
115 if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on))) < 0) {
116 log_message(LOG_ERR, "recv setsockopt(SO_REUSEADDR): %m");
117 return r;
118 }
119
120 /* bind to an address */
121 struct sockaddr_in serveraddr;
122 memset(&serveraddr, 0, sizeof(serveraddr));
123 serveraddr.sin_family = AF_INET;
124 serveraddr.sin_port = htons(MDNS_PORT);
125 serveraddr.sin_addr.s_addr = bind_ip; /* receive multicast */
126 if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) {
127 log_message(LOG_ERR, "recv bind(): %m");
128 }
129
130 // add membership to receiving socket
131 struct ip_mreq mreq;
132 memset(&mreq, 0, sizeof(struct ip_mreq));
133 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
134 mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR);
135 if ((r = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq))) < 0) {
136 log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %m");
137 return r;
138 }
139
140 // enable loopback in case someone else needs the data
141 if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &on, sizeof(on))) < 0) {
142 log_message(LOG_ERR, "recv setsockopt(IP_MULTICAST_LOOP): %m");
143 return r;
144 }
145
146
147 #ifdef IP_PKTINFO
148 if ((r = setsockopt(sd, SOL_IP, IP_PKTINFO, (char *) &on, sizeof(on))) < 0) {
149 log_message(LOG_ERR, "recv setsockopt(IP_PKTINFO): %m");
150 return r;
151 }
152 #endif
153
154 return sd;
155 }
156
send_packet(int fd,const void * data,size_t len)157 static ssize_t send_packet(int fd, const void *data, size_t len) {
158 static struct sockaddr_in toaddr;
159 if (toaddr.sin_family != AF_INET) {
160 memset(&toaddr, 0, sizeof(struct sockaddr_in));
161 toaddr.sin_family = AF_INET;
162 toaddr.sin_port = htons(MDNS_PORT);
163 toaddr.sin_addr.s_addr = inet_addr(MDNS_ADDR);
164 }
165
166 return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in));
167 }
168
169
170 // populate the specified list which matches the RR name and type
171 // type can be RR_ANY, which populates all entries EXCEPT RR_NSEC
populate_answers(struct mdnsd * svr,struct rr_list ** rr_head,uint8_t * name,enum rr_type type)172 static int populate_answers(struct mdnsd *svr, struct rr_list **rr_head, uint8_t *name, enum rr_type type) {
173 int num_ans = 0;
174
175 // check if we have the records
176 pthread_mutex_lock(&svr->data_lock);
177 struct rr_group *ans_grp = rr_group_find(svr->group, name);
178 if (ans_grp == NULL) {
179 pthread_mutex_unlock(&svr->data_lock);
180 return num_ans;
181 }
182
183 // decide which records should go into answers
184 struct rr_list *n = ans_grp->rr;
185 for (; n; n = n->next) {
186 // exclude NSEC for RR_ANY
187 if (type == RR_ANY && n->e->type == RR_NSEC)
188 continue;
189
190 if ((type == n->e->type || type == RR_ANY) && cmp_nlabel(name, n->e->name) == 0) {
191 num_ans += rr_list_append(rr_head, n->e);
192 }
193 }
194
195 pthread_mutex_unlock(&svr->data_lock);
196
197 return num_ans;
198 }
199
200 // given a list of RRs, look up related records and add them
add_related_rr(struct mdnsd * svr,struct rr_list * list,struct mdns_pkt * reply)201 static void add_related_rr(struct mdnsd *svr, struct rr_list *list, struct mdns_pkt *reply) {
202 for (; list; list = list->next) {
203 struct rr_entry *ans = list->e;
204
205 switch (ans->type) {
206 case RR_PTR:
207 // target host A, AAAA records
208 reply->num_add_rr += populate_answers(svr, &reply->rr_add,
209 MDNS_RR_GET_PTR_NAME(ans), RR_ANY);
210 break;
211
212 case RR_SRV:
213 // target host A, AAAA records
214 reply->num_add_rr += populate_answers(svr, &reply->rr_add,
215 ans->data.SRV.target, RR_ANY);
216
217 // perhaps TXT records of the same name?
218 // if we use RR_ANY, we risk pulling in the same RR_SRV
219 reply->num_add_rr += populate_answers(svr, &reply->rr_add,
220 ans->name, RR_TXT);
221 break;
222
223 case RR_A:
224 case RR_AAAA:
225 reply->num_add_rr += populate_answers(svr, &reply->rr_add,
226 ans->name, RR_NSEC);
227 break;
228
229 default:
230 // nothing to add
231 break;
232 }
233 }
234 }
235
236 // creates an announce packet given the type name PTR
announce_srv(struct mdnsd * svr,struct mdns_pkt * reply,uint8_t * name)237 static void announce_srv(struct mdnsd *svr, struct mdns_pkt *reply, uint8_t *name) {
238 mdns_init_reply(reply, 0);
239
240 reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, name, RR_PTR);
241
242 // remember to add the services dns-sd PTR too
243 reply->num_ans_rr += populate_answers(svr, &reply->rr_ans,
244 SERVICES_DNS_SD_NLABEL, RR_PTR);
245
246 // see if we can match additional records for answers
247 add_related_rr(svr, reply->rr_ans, reply);
248
249 // additional records for additional records
250 add_related_rr(svr, reply->rr_add, reply);
251 }
252
253 // processes the incoming MDNS packet
254 // returns >0 if processed, 0 otherwise
process_mdns_pkt(struct mdnsd * svr,struct mdns_pkt * pkt,struct mdns_pkt * reply)255 static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns_pkt *reply) {
256 int i;
257
258 assert(pkt != NULL);
259
260 // is it standard query?
261 if ((pkt->flags & MDNS_FLAG_RESP) == 0 &&
262 MDNS_FLAG_GET_OPCODE(pkt->flags) == 0) {
263 mdns_init_reply(reply, pkt->id);
264
265 DEBUG_PRINTF("flags = %04x, qn = %d, ans = %d, add = %d\n",
266 pkt->flags,
267 pkt->num_qn,
268 pkt->num_ans_rr,
269 pkt->num_add_rr);
270
271 // loop through questions
272 struct rr_list *qnl = pkt->rr_qn;
273 for (i = 0; i < pkt->num_qn; i++, qnl = qnl->next) {
274 struct rr_entry *qn = qnl->e;
275 int num_ans_added = 0;
276
277 char *namestr = nlabel_to_str(qn->name);
278 DEBUG_PRINTF("qn #%d: type %s (%02x) %s - ", i, rr_get_type_name(qn->type), qn->type, namestr);
279 free(namestr);
280
281 // check if it's a unicast query - we ignore those
282 if (qn->unicast_query) {
283 DEBUG_PRINTF("skipping unicast query\n");
284 continue;
285 }
286
287 num_ans_added = populate_answers(svr, &reply->rr_ans, qn->name, qn->type);
288 reply->num_ans_rr += num_ans_added;
289
290 DEBUG_PRINTF("added %d answers\n", num_ans_added);
291 }
292
293 // remove our replies if they were already in their answers
294 struct rr_list *ans = NULL, *prev_ans = NULL;
295 for (ans = reply->rr_ans; ans; ) {
296 struct rr_list *next_ans = ans->next;
297 struct rr_entry *known_ans = rr_entry_match(pkt->rr_ans, ans->e);
298
299 // discard answers that have at least half of the actual TTL
300 if (known_ans != NULL && known_ans->ttl >= ans->e->ttl / 2) {
301 char *namestr = nlabel_to_str(ans->e->name);
302 DEBUG_PRINTF("removing answer for %s\n", namestr);
303 free(namestr);
304
305 // check if list item is head
306 if (prev_ans == NULL)
307 reply->rr_ans = ans->next;
308 else
309 prev_ans->next = ans->next;
310 free(ans);
311
312 ans = prev_ans;
313
314 // adjust answer count
315 reply->num_ans_rr--;
316 }
317
318 prev_ans = ans;
319 ans = next_ans;
320 }
321
322
323 // see if we can match additional records for answers
324 add_related_rr(svr, reply->rr_ans, reply);
325
326 // additional records for additional records
327 add_related_rr(svr, reply->rr_add, reply);
328
329 DEBUG_PRINTF("\n");
330
331 return reply->num_ans_rr;
332 }
333
334 return 0;
335 }
336
create_pipe(int handles[2])337 int create_pipe(int handles[2]) {
338 #ifdef _WIN32
339 SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
340 if (sock == INVALID_SOCKET) {
341 return -1;
342 }
343 struct sockaddr_in serv_addr;
344 memset(&serv_addr, 0, sizeof(serv_addr));
345 serv_addr.sin_family = AF_INET;
346 serv_addr.sin_port = htons(0);
347 serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
348 if (bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) {
349 closesocket(sock);
350 return -1;
351 }
352 if (listen(sock, 1) == SOCKET_ERROR) {
353 closesocket(sock);
354 return -1;
355 }
356 int len = sizeof(serv_addr);
357 if (getsockname(sock, (SOCKADDR*)&serv_addr, &len) == SOCKET_ERROR) {
358 closesocket(sock);
359 return -1;
360 }
361 if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
362 closesocket(sock);
363 return -1;
364 }
365 if (connect(handles[1], (struct sockaddr*)&serv_addr, len) == SOCKET_ERROR) {
366 closesocket(sock);
367 return -1;
368 }
369 if ((handles[0] = accept(sock, (struct sockaddr*)&serv_addr, &len)) == INVALID_SOCKET) {
370 closesocket((SOCKET)handles[1]);
371 handles[1] = INVALID_SOCKET;
372 closesocket(sock);
373 return -1;
374 }
375 closesocket(sock);
376 return 0;
377 #else
378 return pipe(handles);
379 #endif
380 }
381
read_pipe(int s,char * buf,int len)382 int read_pipe(int s, char* buf, int len) {
383 #ifdef _WIN32
384 int ret = recv(s, buf, len, 0);
385 if (ret < 0 && WSAGetLastError() == WSAECONNRESET) {
386 ret = 0;
387 }
388 return ret;
389 #else
390 return read(s, buf, len);
391 #endif
392 }
393
write_pipe(int s,char * buf,int len)394 int write_pipe(int s, char* buf, int len) {
395 #ifdef _WIN32
396 return send(s, buf, len, 0);
397 #else
398 return write(s, buf, len);
399 #endif
400 }
401
402 // main loop to receive, process and send out MDNS replies
403 // also handles MDNS service announces
main_loop(struct mdnsd * svr)404 static void main_loop(struct mdnsd *svr) {
405 fd_set sockfd_set;
406 int max_fd = svr->sockfd;
407 char notify_buf[2]; // buffer for reading of notify_pipe
408
409 void *pkt_buffer = malloc(PACKET_SIZE);
410
411 if (svr->notify_pipe[0] > max_fd)
412 max_fd = svr->notify_pipe[0];
413
414 struct mdns_pkt *mdns_reply = malloc(sizeof(struct mdns_pkt));
415 memset(mdns_reply, 0, sizeof(struct mdns_pkt));
416
417 while (! svr->stop_flag) {
418 FD_ZERO(&sockfd_set);
419 FD_SET(svr->sockfd, &sockfd_set);
420 FD_SET(svr->notify_pipe[0], &sockfd_set);
421 select(max_fd + 1, &sockfd_set, NULL, NULL, NULL);
422
423 if (FD_ISSET(svr->notify_pipe[0], &sockfd_set)) {
424 // flush the notify_pipe
425 read_pipe(svr->notify_pipe[0], (char*)¬ify_buf, 1);
426 } else if (FD_ISSET(svr->sockfd, &sockfd_set)) {
427 struct sockaddr_in fromaddr;
428 socklen_t sockaddr_size = sizeof(struct sockaddr_in);
429
430 ssize_t recvsize = recvfrom(svr->sockfd, pkt_buffer, PACKET_SIZE, 0,
431 (struct sockaddr *) &fromaddr, &sockaddr_size);
432 if (recvsize < 0) {
433 log_message(LOG_ERR, "recv(): %m");
434 }
435
436 DEBUG_PRINTF("data from=%s size=%ld\n", inet_ntoa(fromaddr.sin_addr), (long) recvsize);
437 struct mdns_pkt *mdns = mdns_parse_pkt(pkt_buffer, recvsize);
438 if (mdns != NULL) {
439 if (process_mdns_pkt(svr, mdns, mdns_reply)) {
440 size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
441 send_packet(svr->sockfd, pkt_buffer, replylen);
442 } else if (mdns->num_qn == 0) {
443 DEBUG_PRINTF("(no questions in packet)\n\n");
444 }
445
446 mdns_pkt_destroy(mdns);
447 }
448 }
449
450 // send out announces
451 while (1) {
452 struct rr_entry *ann_e = NULL;
453
454 // extract from head of list
455 pthread_mutex_lock(&svr->data_lock);
456 if (svr->announce)
457 ann_e = rr_list_remove(&svr->announce, svr->announce->e);
458 pthread_mutex_unlock(&svr->data_lock);
459
460 if (! ann_e)
461 break;
462
463 char *namestr = nlabel_to_str(ann_e->name);
464 DEBUG_PRINTF("sending announce for %s\n", namestr);
465 free(namestr);
466
467 announce_srv(svr, mdns_reply, ann_e->name);
468
469 if (mdns_reply->num_ans_rr > 0) {
470 size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
471 send_packet(svr->sockfd, pkt_buffer, replylen);
472 }
473 }
474 }
475
476 // main thread terminating. send out "goodbye packets" for services
477 mdns_init_reply(mdns_reply, 0);
478
479 pthread_mutex_lock(&svr->data_lock);
480 struct rr_list *svc_le = svr->services;
481 for (; svc_le; svc_le = svc_le->next) {
482 // set TTL to zero
483 svc_le->e->ttl = 0;
484 mdns_reply->num_ans_rr += rr_list_append(&mdns_reply->rr_ans, svc_le->e);
485 }
486 pthread_mutex_unlock(&svr->data_lock);
487
488 // send out packet
489 if (mdns_reply->num_ans_rr > 0) {
490 size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
491 send_packet(svr->sockfd, pkt_buffer, replylen);
492 }
493
494 // destroy packet
495 mdns_init_reply(mdns_reply, 0);
496 free(mdns_reply);
497
498 free(pkt_buffer);
499
500 #ifdef _WIN32
501 closesocket(svr->sockfd);
502 #else
503 close(svr->sockfd);
504 #endif
505
506 svr->stop_flag = 2;
507 }
508
509 /////////////////////////////////////////////////////
510
511
mdnsd_set_hostname(struct mdnsd * svr,const char * hostname,uint32_t ip)512 void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip) {
513 struct rr_entry *a_e = NULL,
514 *nsec_e = NULL;
515
516 // currently can't be called twice
517 // dont ask me what happens if the IP changes
518 assert(svr->hostname == NULL);
519
520 a_e = rr_create_a(create_nlabel(hostname), ip);
521
522 nsec_e = rr_create(create_nlabel(hostname), RR_NSEC);
523 rr_set_nsec(nsec_e, RR_A);
524
525 pthread_mutex_lock(&svr->data_lock);
526 svr->hostname = create_nlabel(hostname);
527 rr_group_add(&svr->group, a_e);
528 rr_group_add(&svr->group, nsec_e);
529 pthread_mutex_unlock(&svr->data_lock);
530 }
531
mdnsd_add_rr(struct mdnsd * svr,struct rr_entry * rr)532 void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr) {
533 pthread_mutex_lock(&svr->data_lock);
534 rr_group_add(&svr->group, rr);
535 pthread_mutex_unlock(&svr->data_lock);
536 }
537
mdnsd_register_svc(struct mdnsd * svr,const char * instance_name,const char * type,uint16_t port,const char * hostname,const char * txt[])538 struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name,
539 const char *type, uint16_t port, const char *hostname, const char *txt[]) {
540 struct rr_entry *txt_e = NULL,
541 *srv_e = NULL,
542 *ptr_e = NULL,
543 *bptr_e = NULL;
544 uint8_t *target;
545 uint8_t *inst_nlabel, *type_nlabel, *nlabel;
546 struct mdns_service *service = malloc(sizeof(struct mdns_service));
547 memset(service, 0, sizeof(struct mdns_service));
548
549 // combine service name
550 type_nlabel = create_nlabel(type);
551 inst_nlabel = create_nlabel(instance_name);
552 nlabel = join_nlabel(inst_nlabel, type_nlabel);
553
554 // create TXT record
555 if (txt && *txt) {
556 txt_e = rr_create(dup_nlabel(nlabel), RR_TXT);
557 rr_list_append(&service->entries, txt_e);
558
559 // add TXTs
560 for (; *txt; txt++)
561 rr_add_txt(txt_e, *txt);
562 }
563
564 // create SRV record
565 assert(hostname || svr->hostname); // either one as target
566 target = hostname ?
567 create_nlabel(hostname) :
568 dup_nlabel(svr->hostname);
569
570 srv_e = rr_create_srv(dup_nlabel(nlabel), port, target);
571 rr_list_append(&service->entries, srv_e);
572
573 // create PTR record for type
574 ptr_e = rr_create_ptr(type_nlabel, srv_e);
575
576 // create services PTR record for type
577 // this enables the type to show up as a "service"
578 bptr_e = rr_create_ptr(dup_nlabel(SERVICES_DNS_SD_NLABEL), ptr_e);
579
580 // modify lists here
581 pthread_mutex_lock(&svr->data_lock);
582
583 if (txt_e)
584 rr_group_add(&svr->group, txt_e);
585 rr_group_add(&svr->group, srv_e);
586 rr_group_add(&svr->group, ptr_e);
587 rr_group_add(&svr->group, bptr_e);
588
589 // append PTR entry to announce list
590 rr_list_append(&svr->announce, ptr_e);
591 rr_list_append(&svr->services, ptr_e);
592
593 pthread_mutex_unlock(&svr->data_lock);
594
595 // don't free type_nlabel - it's with the PTR record
596 free(nlabel);
597 free(inst_nlabel);
598
599 // notify server
600 write_pipe(svr->notify_pipe[1], ".", 1);
601
602 return service;
603 }
604
mdns_service_destroy(struct mdns_service * srv)605 void mdns_service_destroy(struct mdns_service *srv) {
606 assert(srv != NULL);
607 rr_list_destroy(srv->entries, 0);
608 free(srv);
609 }
610
mdnsd_start()611 struct mdnsd *mdnsd_start() {
612 return mdnsd_start_bind(htonl(INADDR_ANY));
613 }
614
mdnsd_start_bind(uint32_t bind_ip)615 struct mdnsd *mdnsd_start_bind(uint32_t bind_ip) {
616 pthread_t tid;
617 pthread_attr_t attr;
618
619 struct mdnsd *server = malloc(sizeof(struct mdnsd));
620 memset(server, 0, sizeof(struct mdnsd));
621
622 if (create_pipe(server->notify_pipe) != 0) {
623 log_message(LOG_ERR, "pipe(): %m\n");
624 free(server);
625 return NULL;
626 }
627
628 server->sockfd = create_recv_sock(bind_ip);
629 if (server->sockfd < 0) {
630 log_message(LOG_ERR, "unable to create recv socket");
631 free(server);
632 return NULL;
633 }
634
635 pthread_mutex_init(&server->data_lock, NULL);
636
637 // init thread
638 pthread_attr_init(&attr);
639 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
640
641 if (pthread_create(&tid, &attr, (void *(*)(void *)) main_loop, (void *) server) != 0) {
642 pthread_mutex_destroy(&server->data_lock);
643 free(server);
644 return NULL;
645 }
646
647 return server;
648 }
649
mdnsd_stop(struct mdnsd * s)650 void mdnsd_stop(struct mdnsd *s) {
651 assert(s != NULL);
652
653 struct timeval tv = {
654 .tv_sec = 0,
655 .tv_usec = 500 * 1000,
656 };
657
658 s->stop_flag = 1;
659 write_pipe(s->notify_pipe[1], ".", 1);
660
661 while (s->stop_flag != 2)
662 select(0, NULL, NULL, NULL, &tv);
663
664 #ifdef _WIN32
665 closesocket(s->notify_pipe[0]);
666 closesocket(s->notify_pipe[1]);
667 #else
668 close(s->notify_pipe[0]);
669 close(s->notify_pipe[1]);
670 #endif
671
672 pthread_mutex_destroy(&s->data_lock);
673 rr_group_destroy(s->group);
674 rr_list_destroy(s->announce, 0);
675 rr_list_destroy(s->services, 0);
676
677 if (s->hostname)
678 free(s->hostname);
679
680 free(s);
681 }
682
683