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*)&notify_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