]> git.saurik.com Git - apple/mdnsresponder.git/blob - ServiceRegistration/ioloop.c
mDNSResponder-1310.80.1.tar.gz
[apple/mdnsresponder.git] / ServiceRegistration / ioloop.c
1 /* ioloop.c
2 *
3 * Copyright (c) 2018-2019 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * Simple event dispatcher for DNS.
18 */
19
20 #define __APPLE_USE_RFC_3542
21 #define _GNU_SOURCE
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <sys/uio.h>
28 #include <errno.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #ifdef USE_KQUEUE
33 #include <sys/event.h>
34 #endif
35 #include <sys/wait.h>
36 #include <fcntl.h>
37 #include <sys/time.h>
38 #include <signal.h>
39 #include <net/if.h>
40 #include <ifaddrs.h>
41
42 #include "dns_sd.h"
43
44 #include "srp.h"
45 #include "dns-msg.h"
46 #include "srp-crypto.h"
47 #include "ioloop.h"
48 #ifndef EXCLUDE_TLS
49 #include "srp-tls.h"
50 #endif
51
52 io_t *ios;
53 wakeup_t *wakeups;
54 subproc_t *subprocesses;
55 int64_t ioloop_now;
56
57 #ifdef USE_KQUEUE
58 int kq;
59 #endif
60
61 int
62 getipaddr(addr_t *addr, const char *p)
63 {
64 if (inet_pton(AF_INET, p, &addr->sin.sin_addr)) {
65 addr->sa.sa_family = AF_INET;
66 #ifndef NOT_HAVE_SA_LEN
67 addr->sa.sa_len = sizeof addr->sin;
68 #endif
69 return sizeof addr->sin;
70 } else if (inet_pton(AF_INET6, p, &addr->sin6.sin6_addr)) {
71 addr->sa.sa_family = AF_INET6;
72 #ifndef NOT_HAVE_SA_LEN
73 addr->sa.sa_len = sizeof addr->sin6;
74 #endif
75 return sizeof addr->sin6;
76 } else {
77 return 0;
78 }
79 }
80
81 int64_t
82 ioloop_timenow()
83 {
84 int64_t now;
85 struct timeval tv;
86 gettimeofday(&tv, 0);
87 now = (int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec / 1000;
88 return now;
89 }
90
91 message_t *
92 message_allocate(size_t message_size)
93 {
94 message_t *message = (message_t *)malloc(message_size + (sizeof (message_t)) - (sizeof (dns_wire_t)));
95 if (message)
96 memset(message, 0, (sizeof (message_t)) - (sizeof (dns_wire_t)));
97 return message;
98 }
99
100 void
101 message_free(message_t *message)
102 {
103 free(message);
104 }
105
106 void
107 comm_free(comm_t *comm)
108 {
109 if (comm->name) {
110 free(comm->name);
111 comm->name = NULL;
112 }
113 if (comm->message) {
114 message_free(comm->message);
115 comm->message = NULL;
116 comm->buf = NULL;
117 }
118 free(comm);
119 }
120
121 void
122 ioloop_close(io_t *io)
123 {
124 close(io->sock);
125 io->sock = -1;
126 }
127
128 static void
129 add_io(io_t *io)
130 {
131 io_t **iop;
132
133 // Add the new reader to the end of the list if it's not on the list.
134 for (iop = &ios; *iop != NULL && *iop != io; iop = &((*iop)->next))
135 ;
136 if (*iop == NULL) {
137 *iop = io;
138 io->next = NULL;
139 }
140 }
141
142 void
143 ioloop_add_reader(io_t *io, io_callback_t callback, io_callback_t finalize)
144 {
145 add_io(io);
146
147 io->read_callback = callback;
148 io->finalize = finalize;
149 #ifdef USE_SELECT
150 io->want_read = true;
151 #endif
152 #ifdef USE_EPOLL
153 #endif
154 #ifdef USE_KQUEUE
155 struct kevent ev;
156 int rv;
157 EV_SET(&ev, io->sock, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, io);
158 rv = kevent(kq, &ev, 1, NULL, 0, NULL);
159 if (rv < 0) {
160 ERROR("kevent add: %s", strerror(errno));
161 return;
162 }
163 #endif // USE_EPOLL
164 }
165
166 void
167 ioloop_add_writer(io_t *io, io_callback_t callback, io_callback_t finalize)
168 {
169 add_io(io);
170
171 io->write_callback = callback;
172 #ifdef USE_SELECT
173 io->want_write = true;
174 #endif
175 #ifdef USE_EPOLL
176 #endif
177 #ifdef USE_KQUEUE
178 struct kevent ev;
179 int rv;
180 EV_SET(&ev, io->sock, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, io);
181 rv = kevent(kq, &ev, 1, NULL, 0, NULL);
182 if (rv < 0) {
183 ERROR("kevent add: %s", strerror(errno));
184 return;
185 }
186 #endif // USE_EPOLL
187 }
188
189 void
190 drop_writer(io_t *io)
191 {
192 #ifdef USE_SELECT
193 io->want_write = false;
194 #endif
195 #ifdef USE_EPOLL
196 #endif
197 #ifdef USE_KQUEUE
198 struct kevent ev;
199 int rv;
200 EV_SET(&ev, io->sock, EVFILT_WRITE, EV_ADD | EV_DISABLE, 0, 0, io);
201 rv = kevent(kq, &ev, 1, NULL, 0, NULL);
202 if (rv < 0) {
203 ERROR("kevent add: %s", strerror(errno));
204 return;
205 }
206 #endif // USE_EPOLL
207 }
208
209 static void
210 add_remove_wakeup(wakeup_t *io, bool remove)
211 {
212 wakeup_t **p_wakeups;
213
214 // Add the new reader to the end of the list if it's not on the list.
215 for (p_wakeups = &wakeups; *p_wakeups != NULL && *p_wakeups != io; p_wakeups = &((*p_wakeups)->next))
216 ;
217 if (remove) {
218 if (*p_wakeups != NULL) {
219 *p_wakeups = io->next;
220 io->next = NULL;
221 }
222 } else {
223 if (*p_wakeups == NULL) {
224 *p_wakeups = io;
225 io->next = NULL;
226 }
227 }
228 }
229
230 void
231 ioloop_add_wake_event(wakeup_t *wakeup, void *context, wakeup_callback_t callback, int milliseconds)
232 {
233 add_remove_wakeup(wakeup, false);
234 wakeup->wakeup_time = ioloop_timenow() + milliseconds;
235 wakeup->wakeup = callback;
236 wakeup->context = context;
237 }
238
239 void
240 ioloop_cancel_wake_event(wakeup_t *wakeup)
241 {
242 add_remove_wakeup(wakeup, true);
243 wakeup->wakeup_time = 0;
244 }
245
246 static void
247 subproc_free(subproc_t *subproc)
248 {
249 int i;
250 for (i = 0; i < subproc->argc; i++) {
251 free(subproc->argv[i]);
252 }
253 free(subproc);
254 }
255
256 bool
257 ioloop_init(void)
258 {
259 signal(SIGPIPE, SIG_IGN); // because why ever?
260 #ifdef USE_KQUEUE
261 kq = kqueue();
262 if (kq < 0) {
263 ERROR("kqueue(): %s", strerror(errno));
264 return false;
265 }
266 #endif
267 return true;
268 }
269
270 int
271 ioloop_events(int64_t timeout_when)
272 {
273 io_t *io, **iop;
274 wakeup_t *wakeup, **p_wakeup;
275 int nev = 0, rv;
276 int64_t now = ioloop_timenow();
277 int64_t next_event = timeout_when;
278 int64_t timeout = 0;
279
280 if (ioloop_now != 0) {
281 INFO("%lld.%03lld seconds have passed on entry to ioloop_events",
282 (long long)((now - ioloop_now) / 1000), (long long)((now - ioloop_now) % 1000));
283 }
284 ioloop_now = now;
285
286 // A timeout of zero means don't time out.
287 if (timeout_when == 0) {
288 next_event = INT64_MAX;
289 } else {
290 next_event = timeout_when;
291 }
292
293 #ifdef USE_SELECT
294 int nfds = 0;
295 fd_set reads, writes, errors;
296 struct timeval tv;
297
298 FD_ZERO(&reads);
299 FD_ZERO(&writes);
300 FD_ZERO(&errors);
301 #endif
302 #ifdef USE_KQUEUE
303 struct timespec ts;
304 #endif
305 p_wakeup = &wakeups;
306 while (*p_wakeup) {
307 wakeup = *p_wakeup;
308 if (wakeup->wakeup_time != 0) {
309 // We loop here in case the wakeup callback sets another wakeup--if it does, we check
310 // again.
311 while (wakeup->wakeup_time <= ioloop_now) {
312 wakeup->wakeup_time = 0;
313 wakeup->wakeup(wakeup->context);
314 ++nev;
315 if (wakeup->wakeup_time == 0) {
316 // Take the wakeup off the list.
317 *p_wakeup = wakeup->next;
318 wakeup->next = NULL;
319 break;
320 }
321 }
322 if (wakeup->wakeup_time < next_event) {
323 next_event = wakeup->wakeup_time;
324 }
325 }
326 }
327
328 iop = &ios;
329 while (*iop) {
330 io = *iop;
331 // If the I/O is dead, finalize or free it.
332 if (io->sock == -1) {
333 *iop = io->next;
334 if (io->finalize) {
335 io->finalize(io);
336 } else {
337 free(io);
338 }
339 continue;
340 }
341
342 iop = &io->next;
343 }
344
345 // INFO("now: %ld io %d wakeup_time %ld next_event %ld", ioloop_now, io->sock, io->wakeup_time, next_event);
346
347 // If we were given a timeout in the future, or told to wait indefinitely, wait until the next event.
348 if (timeout_when == 0 || timeout_when > ioloop_now) {
349 timeout = next_event - ioloop_now;
350 // Don't choose a time so far in the future that it might overflow some math in the kernel.
351 if (timeout > IOLOOP_DAY * 100) {
352 timeout = IOLOOP_DAY * 100;
353 }
354 #ifdef USE_SELECT
355 tv.tv_sec = timeout / 1000;
356 tv.tv_usec = (timeout % 1000) * 1000;
357 #endif
358 #ifdef USE_KQUEUE
359 ts.tv_sec = timeout / 1000;
360 ts.tv_nsec = (timeout % 1000) * 1000 * 1000;
361 #endif
362 }
363
364 while (subprocesses != NULL) {
365 int status;
366 pid_t pid;
367 pid = waitpid(-1, &status, WNOHANG);
368 if (pid <= 0) {
369 break;
370 }
371 subproc_t **sp, *subproc;
372 for (sp = &subprocesses; (*sp) != NULL; sp = &(*sp)->next) {
373 subproc = *sp;
374 if (subproc->pid == pid) {
375 if (!WIFSTOPPED(status)) {
376 *sp = subproc->next;
377 }
378 subproc->callback(subproc, status, NULL);
379 if (!WIFSTOPPED(status)) {
380 subproc_free(subproc);
381 break;
382 }
383 }
384 }
385 }
386
387 #ifdef USE_SELECT
388 for (io = ios; io; io = io->next) {
389 if (io->sock != -1 && (io->want_read || io->want_write)) {
390 if (io->sock >= nfds) {
391 nfds = io->sock + 1;
392 }
393 if (io->want_read) {
394 FD_SET(io->sock, &reads);
395 }
396 if (io->want_write) {
397 FD_SET(io->sock, &writes);
398 }
399 }
400 }
401 #endif
402
403 #ifdef USE_SELECT
404 INFO("waiting %lld %lld seconds", (long long)tv.tv_sec, (long long)tv.tv_usec);
405 rv = select(nfds, &reads, &writes, &errors, &tv);
406 if (rv < 0) {
407 ERROR("select: %s", strerror(errno));
408 exit(1);
409 }
410 now = ioloop_timenow();
411 INFO("%lld.%03lld seconds passed waiting, got %d events", (long long)((now - ioloop_now) / 1000),
412 (long long)((now - ioloop_now) % 1000), rv);
413 ioloop_now = now;
414 for (io = ios; io; io = io->next) {
415 if (io->sock != -1) {
416 if (FD_ISSET(io->sock, &reads)) {
417 io->read_callback(io);
418 } else if (FD_ISSET(io->sock, &writes)) {
419 io->write_callback(io);
420 }
421 }
422 }
423 nev += rv;
424 #endif // USE_SELECT
425 #ifdef USE_KQUEUE
426 #define KEV_MAX 20
427 struct kevent evs[KEV_MAX];
428 int i;
429
430 INFO("waiting %lld/%lld seconds", (long long)ts.tv_sec, (long long)ts.tv_nsec);
431 do {
432 rv = kevent(kq, NULL, 0, evs, KEV_MAX, &ts);
433 now = ioloop_timenow();
434 INFO("%lld.%03lld seconds passed waiting, got %d events", (long long)((now - ioloop_now) / 1000),
435 (long long)((now - ioloop_now) % 1000), rv);
436 ioloop_now = now;
437 ts.tv_sec = 0;
438 ts.tv_nsec = 0;
439 if (rv < 0) {
440 if (errno == EINTR) {
441 rv = 0;
442 } else {
443 ERROR("kevent poll: %s", strerror(errno));
444 exit(1);
445 }
446 }
447 for (i = 0; i < rv; i++) {
448 io = evs[i].udata;
449 if (evs[i].filter == EVFILT_WRITE) {
450 io->write_callback(io);
451 } else if (evs[i].filter == EVFILT_READ) {
452 io->read_callback(io);
453 }
454 }
455 nev += rv;
456 } while (rv == KEV_MAX);
457 #endif
458 return nev;
459 }
460
461 static void
462 udp_read_callback(io_t *io)
463 {
464 comm_t *connection = (comm_t *)io;
465 addr_t src;
466 int rv;
467 struct msghdr msg;
468 struct iovec bufp;
469 uint8_t msgbuf[DNS_MAX_UDP_PAYLOAD];
470 char cmsgbuf[128];
471 struct cmsghdr *cmh;
472 message_t *message;
473
474 bufp.iov_base = msgbuf;
475 bufp.iov_len = DNS_MAX_UDP_PAYLOAD;
476 msg.msg_iov = &bufp;
477 msg.msg_iovlen = 1;
478 msg.msg_name = &src;
479 msg.msg_namelen = sizeof src;
480 msg.msg_control = cmsgbuf;
481 msg.msg_controllen = sizeof cmsgbuf;
482
483 rv = recvmsg(connection->io.sock, &msg, 0);
484 if (rv < 0) {
485 ERROR("udp_read_callback: %s", strerror(errno));
486 return;
487 }
488 message = message_allocate(rv);
489 if (!message) {
490 ERROR("udp_read_callback: out of memory");
491 return;
492 }
493 memcpy(&message->src, &src, sizeof src);
494 message->length = rv;
495 memcpy(&message->wire, msgbuf, rv);
496
497 // For UDP, we use the interface index as part of the validation strategy, so go get
498 // the interface index.
499 for (cmh = CMSG_FIRSTHDR(&msg); cmh; cmh = CMSG_NXTHDR(&msg, cmh)) {
500 if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO) {
501 struct in6_pktinfo pktinfo;
502
503 memcpy(&pktinfo, CMSG_DATA(cmh), sizeof pktinfo);
504 message->ifindex = pktinfo.ipi6_ifindex;
505
506 /* Get the destination address, for use when replying. */
507 message->local.sin6.sin6_family = AF_INET6;
508 message->local.sin6.sin6_port = 0;
509 message->local.sin6.sin6_addr = pktinfo.ipi6_addr;
510 #ifndef NOT_HAVE_SA_LEN
511 message->local.sin6.sin6_len = sizeof message->local;
512 #endif
513 } else if (cmh->cmsg_level == IPPROTO_IP && cmh->cmsg_type == IP_PKTINFO) {
514 struct in_pktinfo pktinfo;
515
516 memcpy(&pktinfo, CMSG_DATA(cmh), sizeof pktinfo);
517 message->ifindex = pktinfo.ipi_ifindex;
518
519 message->local.sin.sin_family = AF_INET;
520 message->local.sin.sin_port = 0;
521 message->local.sin.sin_addr = pktinfo.ipi_addr;
522 #ifndef NOT_HAVE_SA_LEN
523 message->local.sin.sin_len = sizeof message->local;
524 #endif
525 }
526 }
527 connection->message = message;
528 connection->datagram_callback(connection);
529 }
530
531 static void
532 tcp_read_callback(io_t *context)
533 {
534 uint8_t *read_ptr;
535 size_t read_len;
536 comm_t *connection = (comm_t *)context;
537 ssize_t rv;
538 if (connection->message_length_len < 2) {
539 read_ptr = connection->message_length_bytes;
540 read_len = 2 - connection->message_length_len;
541 } else {
542 read_ptr = &connection->buf[connection->message_cur];
543 read_len = connection->message_length - connection->message_cur;
544 }
545
546 if (connection->tls_context != NULL) {
547 #ifndef EXCLUDE_TLS
548 rv = srp_tls_read(connection, read_ptr, read_len);
549 if (rv == 0) {
550 // This isn't an EOF: that's returned as an error status. This just means that
551 // whatever data was available to be read was consumed by the TLS protocol without
552 // producing anything to read at the app layer.
553 return;
554 } else if (rv < 0) {
555 ERROR("TLS return that we can't handle.");
556 close(connection->io.sock);
557 connection->io.sock = -1;
558 srp_tls_context_free(connection);
559 return;
560 }
561 #else
562 ERROR("tls context with TLS excluded in tcp_read_callback.");
563 return;
564 #endif
565 } else {
566 rv = read(connection->io.sock, read_ptr, read_len);
567
568 if (rv < 0) {
569 ERROR("tcp_read_callback: %s", strerror(errno));
570 close(connection->io.sock);
571 connection->io.sock = -1;
572 // connection->io.finalize() will be called from the io loop.
573 return;
574 }
575
576 // If we read zero here, the remote endpoint has closed or shutdown the connection. Either case is
577 // effectively the same--if we are sensitive to read events, that means that we are done processing
578 // the previous message.
579 if (rv == 0) {
580 ERROR("tcp_read_callback: remote end (%s) closed connection on %d", connection->name, connection->io.sock);
581 close(connection->io.sock);
582 connection->io.sock = -1;
583 // connection->io.finalize() will be called from the io loop.
584 return;
585 }
586 }
587 if (connection->message_length_len < 2) {
588 connection->message_length_len += rv;
589 if (connection->message_length_len == 2) {
590 connection->message_length = (((uint16_t)connection->message_length_bytes[0] << 8) |
591 ((uint16_t)connection->message_length_bytes[1]));
592
593 if (connection->message == NULL) {
594 connection->message = message_allocate(connection->message_length);
595 if (!connection->message) {
596 ERROR("udp_read_callback: out of memory");
597 return;
598 }
599 connection->buf = (uint8_t *)&connection->message->wire;
600 connection->message->length = connection->message_length;
601 memset(&connection->message->src, 0, sizeof connection->message->src);
602 }
603 }
604 } else {
605 connection->message_cur += rv;
606 if (connection->message_cur == connection->message_length) {
607 connection->message_cur = 0;
608 connection->datagram_callback(connection);
609 // Caller is expected to consume the message, we are immediately ready for the next read.
610 connection->message_length = connection->message_length_len = 0;
611 }
612 }
613 }
614
615
616 static void
617 tcp_send_response(comm_t *comm, message_t *responding_to, struct iovec *iov, int iov_len)
618 {
619 struct msghdr mh;
620 struct iovec iovec[4];
621 char lenbuf[2];
622 ssize_t status;
623 size_t payload_length = 0;
624 int i;
625
626 // We don't anticipate ever needing more than four hunks, but if we get more, handle then?
627 if (iov_len > 3) {
628 ERROR("tcp_send_response: too many io buffers");
629 close(comm->io.sock);
630 comm->io.sock = -1;
631 return;
632 }
633
634 iovec[0].iov_base = &lenbuf[0];
635 iovec[0].iov_len = 2;
636 for (i = 0; i < iov_len; i++) {
637 iovec[i + 1] = iov[i];
638 payload_length += iov[i].iov_len;
639 }
640 lenbuf[0] = payload_length / 256;
641 lenbuf[1] = payload_length & 0xff;
642 payload_length += 2;
643
644 #ifndef MSG_NOSIGNAL
645 #define MSG_NOSIGNAL 0
646 #endif
647 if (comm->tls_context != NULL) {
648 #ifndef EXCLUDE_TLS
649 status = srp_tls_write(comm, iovec, iov_len + 1);
650 #else
651 ERROR("TLS context not null with TLS excluded.");
652 status = -1;
653 errno = ENOTSUP;
654 #endif
655 } else {
656 memset(&mh, 0, sizeof mh);
657 mh.msg_iov = &iovec[0];
658 mh.msg_iovlen = iov_len + 1;
659 mh.msg_name = 0;
660
661 status = sendmsg(comm->io.sock, &mh, MSG_NOSIGNAL);
662 }
663 if (status < 0 || status != payload_length) {
664 if (status < 0) {
665 ERROR("tcp_send_response: write failed: %s", strerror(errno));
666 } else {
667 ERROR("tcp_send_response: short write (%zd out of %zu bytes)", status, payload_length);
668 }
669 close(comm->io.sock);
670 comm->io.sock = -1;
671 }
672 }
673
674 static void
675 udp_send_message(comm_t *comm, addr_t *source, addr_t *dest, int ifindex, struct iovec *iov, int iov_len)
676 {
677 struct msghdr mh;
678 uint8_t cmsg_buf[128];
679 struct cmsghdr *cmsg;
680 int status;
681
682 memset(&mh, 0, sizeof mh);
683 mh.msg_iov = iov;
684 mh.msg_iovlen = iov_len;
685 mh.msg_name = dest;
686 mh.msg_control = cmsg_buf;
687 if (source == NULL && ifindex == 0) {
688 mh.msg_controllen = 0;
689 } else {
690 mh.msg_controllen = sizeof cmsg_buf;
691 cmsg = CMSG_FIRSTHDR(&mh);
692
693 if (source->sa.sa_family == AF_INET) {
694 struct in_pktinfo *inp;
695 mh.msg_namelen = sizeof (struct sockaddr_in);
696 mh.msg_controllen = CMSG_SPACE(sizeof *inp);
697 cmsg->cmsg_level = IPPROTO_IP;
698 cmsg->cmsg_type = IP_PKTINFO;
699 cmsg->cmsg_len = CMSG_LEN(sizeof *inp);
700 inp = (struct in_pktinfo *)CMSG_DATA(cmsg);
701 memset(inp, 0, sizeof *inp);
702 inp->ipi_ifindex = ifindex;
703 if (source) {
704 inp->ipi_spec_dst = source->sin.sin_addr;
705 inp->ipi_addr = source->sin.sin_addr;
706 }
707 } else if (source->sa.sa_family == AF_INET6) {
708 struct in6_pktinfo *inp;
709 mh.msg_namelen = sizeof (struct sockaddr_in6);
710 mh.msg_controllen = CMSG_SPACE(sizeof *inp);
711 cmsg->cmsg_level = IPPROTO_IPV6;
712 cmsg->cmsg_type = IPV6_PKTINFO;
713 cmsg->cmsg_len = CMSG_LEN(sizeof *inp);
714 inp = (struct in6_pktinfo *)CMSG_DATA(cmsg);
715 memset(inp, 0, sizeof *inp);
716 inp->ipi6_ifindex = ifindex;
717 if (source) {
718 inp->ipi6_addr = source->sin6.sin6_addr;
719 }
720 } else {
721 ERROR("udp_send_response: unknown family %d", source->sa.sa_family);
722 abort();
723 }
724 }
725 status = sendmsg(comm->io.sock, &mh, 0);
726 if (status < 0) {
727 ERROR("udp_send_message: %s", strerror(errno));
728 }
729 }
730
731 static void
732 udp_send_response(comm_t *comm, message_t *responding_to, struct iovec *iov, int iov_len)
733 {
734 udp_send_message(comm, &responding_to->local, &responding_to->src, responding_to->ifindex, iov, iov_len);
735 }
736
737 static void
738 udp_send_multicast(comm_t *comm, int ifindex, struct iovec *iov, int iov_len)
739 {
740 udp_send_message(comm, NULL, &comm->multicast, ifindex, iov, iov_len);
741 }
742
743 static void
744 udp_send_connected_response(comm_t *comm, message_t *responding_to, struct iovec *iov, int iov_len)
745 {
746 int status = writev(comm->io.sock, iov, iov_len);
747 (void)responding_to;
748 if (status < 0) {
749 ERROR("udp_send_connected: %s", strerror(errno));
750 }
751 }
752
753 // When a communication is closed, scan the io event list to see if any other ios are referencing this one.
754 void
755 comm_finalize(io_t *io_in)
756 {
757 comm_t *comm = (comm_t *)io_in;
758 comm_free(comm);
759 }
760
761 bool
762 comm_valid(comm_t *comm)
763 {
764 if (comm->io.sock != -1) {
765 return true;
766 }
767 return false;
768 }
769
770 void
771 comm_close(comm_t *comm)
772 {
773 close(comm->io.sock);
774 comm->io.sock = -1;
775 }
776
777 static void
778 listen_callback(io_t *context)
779 {
780 comm_t *listener = (comm_t *)context;
781 int rv;
782 addr_t addr;
783 socklen_t addr_len = sizeof addr;
784 comm_t *comm;
785 char addrbuf[INET6_ADDRSTRLEN + 7];
786 int addrlen;
787
788 rv = accept(listener->io.sock, &addr.sa, &addr_len);
789 if (rv < 0) {
790 ERROR("accept: %s", strerror(errno));
791 close(listener->io.sock);
792 listener->io.sock = -1;
793 return;
794 }
795 inet_ntop(addr.sa.sa_family, (addr.sa.sa_family == AF_INET
796 ? (void *)&addr.sin.sin_addr
797 : (void *)&addr.sin6.sin6_addr), addrbuf, sizeof addrbuf);
798 addrlen = strlen(addrbuf);
799 snprintf(&addrbuf[addrlen], (sizeof addrbuf) - addrlen, "%%%d",
800 ntohs((addr.sa.sa_family == AF_INET ? addr.sin.sin_port : addr.sin6.sin6_port)));
801 comm = calloc(1, sizeof *comm);
802 comm->name = strdup(addrbuf);
803 comm->io.sock = rv;
804 comm->io.container = comm;
805 comm->address = addr;
806 comm->datagram_callback = listener->datagram_callback;
807 comm->send_response = tcp_send_response;
808 comm->tcp_stream = true;
809
810 if (listener->tls_context == (tls_context_t *)-1) {
811 #ifndef EXCLUDE_TLS
812 if (!srp_tls_listen_callback(comm)) {
813 ERROR("TLS setup failed.");
814 close(comm->io.sock);
815 free(comm);
816 return;
817 }
818 #else
819 ERROR("TLS context not null in listen_callback when TLS excluded.");
820 return;
821 #endif
822 }
823 if (listener->connected) {
824 listener->connected(comm);
825 }
826 ioloop_add_reader(&comm->io, tcp_read_callback, listener->connection_finalize);
827
828 #ifdef SO_NOSIGPIPE
829 int one = 1;
830 rv = setsockopt(comm->io.sock, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof one);
831 if (rv < 0) {
832 ERROR("SO_NOSIGPIPE failed: %s", strerror(errno));
833 }
834 #endif
835 }
836
837 comm_t *
838 ioloop_setup_listener(int family, bool stream, bool tls, uint16_t port, const char *ip_address, const char *multicast,
839 const char *name, comm_callback_t datagram_callback,
840 comm_callback_t connected, comm_callback_t disconnected,
841 io_callback_t finalize, io_callback_t connection_finalize, void *context)
842 {
843 comm_t *listener;
844 socklen_t sl;
845 int rv;
846 int false_flag = 0;
847 int true_flag = 1;
848
849 listener = calloc(1, sizeof *listener);
850 if (listener == NULL) {
851 return NULL;
852 }
853 listener->io.container = listener;
854 listener->name = strdup(name);
855 if (!listener->name) {
856 free(listener);
857 return NULL;
858 }
859 listener->io.sock = socket(family, stream ? SOCK_STREAM : SOCK_DGRAM, stream ? IPPROTO_TCP : IPPROTO_UDP);
860 if (listener->io.sock < 0) {
861 ERROR("Can't get socket: %s", strerror(errno));
862 goto out;
863 }
864 rv = setsockopt(listener->io.sock, SOL_SOCKET, SO_REUSEADDR, &true_flag, sizeof true_flag);
865 if (rv < 0) {
866 ERROR("SO_REUSEADDR failed: %s", strerror(errno));
867 goto out;
868 }
869
870 rv = setsockopt(listener->io.sock, SOL_SOCKET, SO_REUSEPORT, &true_flag, sizeof true_flag);
871 if (rv < 0) {
872 ERROR("SO_REUSEPORT failed: %s", strerror(errno));
873 goto out;
874 }
875
876 if (ip_address != NULL) {
877 sl = getipaddr(&listener->address, ip_address);
878 if (sl == 0) {
879 goto out;
880 }
881 if (family == AF_UNSPEC) {
882 family = listener->address.sa.sa_family;
883 } else if (listener->address.sa.sa_family != family) {
884 ERROR("%s is not a %s address.", ip_address, family == AF_INET ? "IPv4" : "IPv6");
885 goto out;
886 }
887 }
888
889 if (multicast != 0) {
890 if (stream) {
891 ERROR("Unable to do non-datagram multicast.");
892 goto out;
893 }
894 sl = getipaddr(&listener->multicast, multicast);
895 if (sl == 0) {
896 goto out;
897 }
898 if (listener->multicast.sa.sa_family != family) {
899 ERROR("multicast address %s from different family than listen address %s.", multicast, ip_address);
900 goto out;
901 }
902 listener->is_multicast = true;
903
904 if (family == AF_INET) {
905 struct ip_mreq im;
906 int ttl = 255;
907 im.imr_multiaddr = listener->multicast.sin.sin_addr;
908 im.imr_interface.s_addr = 0;
909 rv = setsockopt(listener->io.sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &im, sizeof im);
910 if (rv < 0) {
911 ERROR("Unable to join %s multicast group: %s", multicast, strerror(errno));
912 goto out;
913 }
914 rv = setsockopt(listener->io.sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof ttl);
915 if (rv < 0) {
916 ERROR("Unable to set IP multicast TTL to 255 for %s: %s", multicast, strerror(errno));
917 goto out;
918 }
919 rv = setsockopt(listener->io.sock, IPPROTO_IP, IP_TTL, &ttl, sizeof ttl);
920 if (rv < 0) {
921 ERROR("Unable to set IP TTL to 255 for %s: %s", multicast, strerror(errno));
922 goto out;
923 }
924 rv = setsockopt(listener->io.sock, IPPROTO_IP, IP_MULTICAST_LOOP, &false_flag, sizeof false_flag);
925 if (rv < 0) {
926 ERROR("Unable to set IP Multcast loopback to false for %s: %s", multicast, strerror(errno));
927 goto out;
928 }
929 } else {
930 struct ipv6_mreq im;
931 int hops = 255;
932 im.ipv6mr_multiaddr = listener->multicast.sin6.sin6_addr;
933 im.ipv6mr_interface = 0;
934 rv = setsockopt(listener->io.sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &im, sizeof im);
935 if (rv < 0) {
936 ERROR("Unable to join %s multicast group: %s", multicast, strerror(errno));
937 goto out;
938 }
939 rv = setsockopt(listener->io.sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof hops);
940 if (rv < 0) {
941 ERROR("Unable to set IPv6 multicast hops to 255 for %s: %s", multicast, strerror(errno));
942 goto out;
943 }
944 rv = setsockopt(listener->io.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, sizeof hops);
945 if (rv < 0) {
946 ERROR("Unable to set IPv6 hops to 255 for %s: %s", multicast, strerror(errno));
947 goto out;
948 }
949 rv = setsockopt(listener->io.sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &false_flag, sizeof false_flag);
950 if (rv < 0) {
951 ERROR("Unable to set IPv6 Multcast loopback to false for %s: %s", multicast, strerror(errno));
952 goto out;
953 }
954 }
955 }
956
957 if (family == AF_INET) {
958 sl = sizeof listener->address.sin;
959 listener->address.sin.sin_port = port ? htons(port) : htons(53);
960 } else {
961 sl = sizeof listener->address.sin6;
962 listener->address.sin6.sin6_port = port ? htons(port) : htons(53);
963 // Don't use a dual-stack socket.
964 rv = setsockopt(listener->io.sock, IPPROTO_IPV6, IPV6_V6ONLY, &true_flag, sizeof true_flag);
965 if (rv < 0) {
966 ERROR("Unable to set IPv6-only flag on %s socket for %s",
967 tls ? "TLS" : (stream ? "TCP" : "UDP"), ip_address == NULL ? "<0>" : ip_address);
968 goto out;
969 }
970 }
971
972 listener->address.sa.sa_family = family;
973 #ifndef NOT_HAVE_SA_LEN
974 listener->address.sa.sa_len = sl;
975 #endif
976 if (bind(listener->io.sock, &listener->address.sa, sl) < 0) {
977 ERROR("Can't bind to %s#%d/%s%s: %s", ip_address == NULL ? "<0>" : ip_address, port,
978 stream ? "tcp" : "udp", family == AF_INET ? "v4" : "v6",
979 strerror(errno));
980 out:
981 close(listener->io.sock);
982 free(listener);
983 return NULL;
984 }
985
986 if (tls) {
987 #ifndef EXCLUDE_TLS
988 if (!stream) {
989 ERROR("Asked to do TLS over UDP, which we don't do yet.");
990 goto out;
991 }
992 listener->tls_context = (tls_context_t *)-1;
993 #else
994 ERROR("TLS requested when TLS is excluded.");
995 goto out;
996 #endif
997 }
998
999 if (stream) {
1000 if (listen(listener->io.sock, 5 /* xxx */) < 0) {
1001 ERROR("Can't listen on %s#%d/%s%s: %s", ip_address == NULL ? "<0>" : ip_address, ntohs(port),
1002 tls ? "tls" : "tcp", family == AF_INET ? "v4" : "v6",
1003 strerror(errno));
1004 goto out;
1005 }
1006 listener->connection_finalize = connection_finalize;
1007 ioloop_add_reader(&listener->io, listen_callback, finalize);
1008 } else {
1009 rv = setsockopt(listener->io.sock, family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6,
1010 family == AF_INET ? IP_PKTINFO : IPV6_RECVPKTINFO, &true_flag, sizeof true_flag);
1011 if (rv < 0) {
1012 ERROR("Can't set %s: %s.", family == AF_INET ? "IP_PKTINFO" : "IPV6_RECVPKTINFO",
1013 strerror(errno));
1014 goto out;
1015 }
1016 ioloop_add_reader(&listener->io, udp_read_callback, finalize);
1017 listener->send_response = udp_send_response;
1018 listener->send_message = udp_send_message;
1019 if (listener->is_multicast) {
1020 listener->send_multicast = udp_send_multicast;
1021 }
1022 }
1023 listener->datagram_callback = datagram_callback;
1024 listener->connected = connected;
1025 return listener;
1026 }
1027
1028 static void
1029 connect_callback(io_t *context)
1030 {
1031 int result;
1032 socklen_t len = sizeof result;
1033 comm_t *connection = (comm_t *)context;
1034
1035 // If connect failed, indicate that it failed.
1036 if (getsockopt(context->sock, SOL_SOCKET, SO_ERROR, &result, &len) < 0) {
1037 ERROR("connect_callback: unable to get connect error: socket %d: Error %d (%s)",
1038 context->sock, result, strerror(result));
1039 connection->disconnected(connection, result);
1040 comm_close(connection);
1041 return;
1042 }
1043
1044 // If this is a TLS connection, set up TLS.
1045 if (connection->tls_context == (tls_context_t *)-1) {
1046 #ifndef EXCLUDE_TLS
1047 srp_tls_connect_callback(connection);
1048 #else
1049 ERROR("connect_callback: tls_context triggered with TLS excluded.");
1050 connection->disconnected(connection, 0);
1051 comm_close(connection);
1052 return;
1053 #endif
1054 }
1055
1056 connection->send_response = tcp_send_response;
1057 connection->connected(connection);
1058 drop_writer(&connection->io);
1059 ioloop_add_reader(&connection->io, tcp_read_callback, connection->io.finalize);
1060 }
1061
1062 // Currently we don't do DNS lookups, despite the host identifier being an IP address.
1063 comm_t *
1064 ioloop_connect(addr_t *NONNULL remote_address, bool tls, bool stream,
1065 comm_callback_t datagram_callback, comm_callback_t connected,
1066 disconnect_callback_t disconnected, io_callback_t finalize, void *context)
1067 {
1068 comm_t *connection;
1069 socklen_t sl;
1070 char buf[INET6_ADDRSTRLEN + 7];
1071 char *s;
1072
1073 if (!stream && (connected != NULL || disconnected != NULL)) {
1074 ERROR("connected and disconnected callbacks not valid for datagram connections");
1075 return NULL;
1076 }
1077 if (stream && (connected == NULL || disconnected == NULL)) {
1078 ERROR("connected and disconnected callbacks are required for stream connections");
1079 return NULL;
1080 }
1081 connection = calloc(1, sizeof *connection);
1082 if (connection == NULL) {
1083 ERROR("No memory for connection structure.");
1084 return NULL;
1085 }
1086 connection->io.container = connection;
1087 if (inet_ntop(remote_address->sa.sa_family, (remote_address->sa.sa_family == AF_INET
1088 ? (void *)&remote_address->sin.sin_addr
1089 : (void *)&remote_address->sin6.sin6_addr), buf,
1090 INET6_ADDRSTRLEN) == NULL) {
1091 ERROR("inet_ntop failed to convert remote address: %s", strerror(errno));
1092 free(connection);
1093 return NULL;
1094 }
1095 s = buf + strlen(buf);
1096 sprintf(s, "%%%hu", ntohs(remote_address->sa.sa_family == AF_INET
1097 ? remote_address->sin.sin_port
1098 : remote_address->sin6.sin6_port));
1099 connection->name = strdup(buf);
1100 if (!connection->name) {
1101 free(connection);
1102 return NULL;
1103 }
1104 connection->io.sock = socket(remote_address->sa.sa_family,
1105 stream ? SOCK_STREAM : SOCK_DGRAM, stream ? IPPROTO_TCP : IPPROTO_UDP);
1106 if (connection->io.sock < 0) {
1107 ERROR("Can't get socket: %s", strerror(errno));
1108 comm_free(connection);
1109 return NULL;
1110 }
1111 connection->address = *remote_address;
1112 if (fcntl(connection->io.sock, F_SETFL, O_NONBLOCK) < 0) {
1113 ERROR("connect_to_host: %s: Can't set O_NONBLOCK: %s", connection->name, strerror(errno));
1114 comm_free(connection);
1115 return NULL;
1116 }
1117 #ifdef NOT_HAVE_SA_LEN
1118 sl = (remote_address->sa.sa_family == AF_INET
1119 ? sizeof remote_address->sin
1120 : sizeof remote_address->sin6);
1121 #else
1122 sl = remote_address->sa.sa_len;
1123 #endif
1124 // Connect to the host
1125 if (connect(connection->io.sock, &connection->address.sa, sl) < 0) {
1126 if (errno != EINPROGRESS && errno != EAGAIN) {
1127 ERROR("Can't connect to %s: %s", connection->name, strerror(errno));
1128 comm_free(connection);
1129 return NULL;
1130 }
1131 }
1132 // At this point if we are doing TCP, we do not yet have a connection, but the connection should be in
1133 // progress, and we should get a write select event when the connection succeeds or fails.
1134 // UDP is connectionless, so the connect() call just sets the default destination for send() on
1135 // the socket.
1136
1137 if (tls) {
1138 #ifndef TLS_EXCLUDED
1139 connection->tls_context = (tls_context_t *)-1;
1140 #else
1141 ERROR("connect_to_host: tls requested when excluded.");
1142 comm_free(connection);
1143 return NULL;
1144 #endif
1145 }
1146
1147 connection->connected = connected;
1148 connection->disconnected = disconnected;
1149 connection->datagram_callback = datagram_callback;
1150 connection->context = context;
1151 if (!stream) {
1152 connection->send_response = udp_send_connected_response;
1153 ioloop_add_reader(&connection->io, udp_read_callback, finalize);
1154 } else {
1155 ioloop_add_writer(&connection->io, connect_callback, finalize);
1156 }
1157
1158 return connection;
1159 }
1160
1161 typedef struct interface_addr interface_addr_t;
1162 struct interface_addr {
1163 interface_addr_t *next;
1164 char *name;
1165 addr_t addr;
1166 addr_t mask;
1167 int index;
1168 };
1169 interface_addr_t *interface_addresses;
1170
1171 bool
1172 ioloop_map_interface_addresses(void *context, interface_callback_t callback)
1173 {
1174 struct ifaddrs *ifaddrs, *ifp;
1175 interface_addr_t *kept_ifaddrs = NULL, **ki_end = &kept_ifaddrs;
1176 interface_addr_t *new_ifaddrs = NULL, **ni_end = &new_ifaddrs;
1177 interface_addr_t **ip, *nif;
1178 char *ifname = NULL;
1179 int ifindex = 0;
1180
1181 if (getifaddrs(&ifaddrs) < 0) {
1182 ERROR("getifaddrs failed: %s", strerror(errno));
1183 return false;
1184 }
1185
1186 for (ifp = ifaddrs; ifp; ifp = ifp->ifa_next) {
1187 // Is this an interface address we can use?
1188 if (ifp->ifa_addr != NULL && ifp->ifa_netmask != NULL &&
1189 (ifp->ifa_addr->sa_family == AF_INET ||
1190 ifp->ifa_addr->sa_family == AF_INET6) &&
1191 (ifp->ifa_flags & IFF_UP) &&
1192 !(ifp->ifa_flags & IFF_POINTOPOINT))
1193 {
1194 bool keep = false;
1195 for (ip = &interface_addresses; *ip != NULL; ) {
1196 interface_addr_t *ia = *ip;
1197 // Same interface and address?
1198 if (!strcmp(ia->name, ifp->ifa_name) &&
1199 ifp->ifa_addr->sa_family == ia->addr.sa.sa_family &&
1200 ((ifp->ifa_addr->sa_family == AF_INET &&
1201 ((struct sockaddr_in *)ifp->ifa_addr)->sin_addr.s_addr == ia->addr.sin.sin_addr.s_addr) ||
1202 (ifp->ifa_addr->sa_family == AF_INET6 &&
1203 !memcmp(&((struct sockaddr_in6 *)ifp->ifa_addr)->sin6_addr,
1204 &ia->addr.sin6.sin6_addr, sizeof ia->addr.sin6.sin6_addr))) &&
1205 ((ifp->ifa_netmask->sa_family == AF_INET &&
1206 ((struct sockaddr_in *)ifp->ifa_netmask)->sin_addr.s_addr == ia->mask.sin.sin_addr.s_addr) ||
1207 (ifp->ifa_netmask->sa_family == AF_INET6 &&
1208 !memcmp(&((struct sockaddr_in6 *)ifp->ifa_netmask)->sin6_addr,
1209 &ia->mask.sin6.sin6_addr, sizeof ia->mask.sin6.sin6_addr))))
1210 {
1211 *ki_end = ia;
1212 ki_end = &ia->next;
1213 keep = true;
1214 break;
1215 } else {
1216 ip = &ia->next;
1217 }
1218 }
1219 // If keep is false, this is a new interface.
1220 if (!keep) {
1221 nif = calloc(1, strlen(ifp->ifa_name) + 1 + sizeof *nif);
1222 // We don't have a way to fix nif being null; what this means is that we don't detect a new
1223 // interface address.
1224 if (nif != NULL) {
1225 nif->name = (char *)(nif + 1);
1226 strcpy(nif->name, ifp->ifa_name);
1227 if (ifp->ifa_addr->sa_family == AF_INET) {
1228 nif->addr.sin = *((struct sockaddr_in *)ifp->ifa_addr);
1229 nif->mask.sin = *((struct sockaddr_in *)ifp->ifa_netmask);
1230 } else {
1231 nif->addr.sin6 = *((struct sockaddr_in6 *)ifp->ifa_addr);
1232 nif->mask.sin6 = *((struct sockaddr_in6 *)ifp->ifa_netmask);
1233 }
1234 *ni_end = nif;
1235 ni_end = &nif->next;
1236 }
1237 }
1238 }
1239 }
1240
1241 // Report and free deleted interface addresses...
1242 for (nif = interface_addresses; nif; ) {
1243 interface_addr_t *next = nif->next;
1244 callback(context, nif->name, &nif->addr, &nif->mask, nif->index, interface_address_deleted);
1245 free(nif);
1246 nif = next;
1247 }
1248
1249 // Report added interface addresses...
1250 for (nif = new_ifaddrs; nif; nif = nif->next) {
1251 // Get interface index using standard API if AF_LINK didn't work.
1252 if (nif->index == 0) {
1253 if (ifindex != 0 && ifname != NULL && !strcmp(ifname, nif->name)) {
1254 nif->index = ifindex;
1255 } else {
1256 ifname = nif->name;
1257 ifindex = if_nametoindex(nif->name);
1258 nif->index = ifindex;
1259 INFO("Got interface index for " PUB_S_SRP " the hard way: %d", nif->name, nif->index);
1260 }
1261 }
1262 callback(context, nif->name, &nif->addr, &nif->mask, nif->index, interface_address_added);
1263 }
1264
1265 // Restore kept interface addresses and append new addresses to the list.
1266 interface_addresses = kept_ifaddrs;
1267 for (ip = &new_ifaddrs; *ip; ip = &(*ip)->next)
1268 ;
1269 *ip = new_ifaddrs;
1270 return true;
1271 }
1272
1273 // Invoke the specified executable with the specified arguments. Call callback when it exits.
1274 // All failures are reported through the callback.
1275 subproc_t *
1276 ioloop_subproc(const char *exepath, char *NULLABLE *argv, int argc, subproc_callback_t callback)
1277 {
1278 subproc_t *subproc = calloc(1, sizeof *subproc);
1279 int i;
1280 pid_t pid;
1281
1282 if (subproc == NULL) {
1283 callback(NULL, 0, "out of memory");
1284 return NULL;
1285 }
1286 if (argc > MAX_SUBPROC_ARGS) {
1287 callback(NULL, 0, "too many subproc args");
1288 subproc_free(subproc);
1289 return NULL;
1290 }
1291
1292 subproc->argv[0] = strdup(exepath);
1293 if (subproc->argv[0] == NULL) {
1294 subproc_free(subproc);
1295 return NULL;
1296 }
1297 subproc->argc++;
1298 for (i = 0; i < argc; i++) {
1299 subproc->argv[i + 1] = strdup(argv[i]);
1300 if (subproc->argv[i + 1] == NULL) {
1301 subproc_free(subproc);
1302 return NULL;
1303 }
1304 subproc->argc++;
1305 }
1306 pid = vfork();
1307 if (pid == 0) {
1308 execv(exepath, subproc->argv);
1309 _exit(errno);
1310 // NOTREACHED
1311 }
1312 if (pid == -1) {
1313 callback(subproc, 0, strerror(errno));
1314 subproc_free(subproc);
1315 return NULL;
1316 }
1317
1318 subproc->callback = callback;
1319 subproc->pid = pid;
1320 subproc->next = subprocesses;
1321 subprocesses = subproc;
1322 return subproc;
1323 }
1324
1325 static void
1326 dnssd_txn_callback(io_t *io)
1327 {
1328 dnssd_txn_t *txn = (dnssd_txn_t *)io;
1329 int status = DNSServiceProcessResult(txn->sdref);
1330 if (status != kDNSServiceErr_NoError) {
1331 if (txn->close_callback != NULL) {
1332 txn->close_callback(txn->context, status);
1333 }
1334 }
1335 }
1336
1337 void
1338 dnssd_txn_finalize(io_t *io)
1339 {
1340 dnssd_txn_t *txn = (dnssd_txn_t *)io;
1341
1342 if (txn->finalize_callback) {
1343 txn->finalize_callback(txn->context);
1344 }
1345 }
1346
1347 dnssd_txn_t *
1348 ioloop_dnssd_txn_add(DNSServiceRef ref,
1349 dnssd_txn_finalize_callback_t finalize_callback, dnssd_txn_close_callback_t close_callback)
1350 {
1351 dnssd_txn_t *txn = calloc(1, sizeof(*txn));
1352 if (txn != NULL) {
1353 RETAIN(txn);
1354 io->sdref = sdref;
1355 txn->io.sock = DNSServiceRefSockFD(txn->sdref);
1356 txn->finalize_callback = finalize_callback;
1357 txn->close_callback = close_callback;
1358 ioloop_add_reader(&txn->io, dnssd_txn_callback, dnssd_txn_finalize);
1359 }
1360 return txn;
1361 }
1362
1363 // Local Variables:
1364 // mode: C
1365 // tab-width: 4
1366 // c-file-style: "bsd"
1367 // c-basic-offset: 4
1368 // fill-column: 108
1369 // indent-tabs-mode: nil
1370 // End: