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
getipaddr(addr_t * addr,const char * p)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
ioloop_timenow()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 *
message_allocate(size_t message_size)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
message_free(message_t * message)101 message_free(message_t *message)
102 {
103     free(message);
104 }
105 
106 void
comm_free(comm_t * comm)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
ioloop_close(io_t * io)122 ioloop_close(io_t *io)
123 {
124     close(io->sock);
125     io->sock = -1;
126 }
127 
128 static void
add_io(io_t * io)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
ioloop_add_reader(io_t * io,io_callback_t callback,io_callback_t finalize)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
ioloop_add_writer(io_t * io,io_callback_t callback,io_callback_t finalize)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
drop_writer(io_t * io)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
add_remove_wakeup(wakeup_t * io,bool remove)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
ioloop_add_wake_event(wakeup_t * wakeup,void * context,wakeup_callback_t callback,int milliseconds)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
ioloop_cancel_wake_event(wakeup_t * wakeup)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
subproc_free(subproc_t * subproc)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
ioloop_init(void)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
ioloop_events(int64_t timeout_when)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
udp_read_callback(io_t * io)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
tcp_read_callback(io_t * context)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
tcp_send_response(comm_t * comm,message_t * responding_to,struct iovec * iov,int iov_len)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
udp_send_message(comm_t * comm,addr_t * source,addr_t * dest,int ifindex,struct iovec * iov,int iov_len)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
udp_send_response(comm_t * comm,message_t * responding_to,struct iovec * iov,int iov_len)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
udp_send_multicast(comm_t * comm,int ifindex,struct iovec * iov,int iov_len)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
udp_send_connected_response(comm_t * comm,message_t * responding_to,struct iovec * iov,int iov_len)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
comm_finalize(io_t * io_in)755 comm_finalize(io_t *io_in)
756 {
757     comm_t *comm = (comm_t *)io_in;
758     comm_free(comm);
759 }
760 
761 bool
comm_valid(comm_t * comm)762 comm_valid(comm_t *comm)
763 {
764     if (comm->io.sock != -1) {
765         return true;
766     }
767     return false;
768 }
769 
770 void
comm_close(comm_t * comm)771 comm_close(comm_t *comm)
772 {
773     close(comm->io.sock);
774     comm->io.sock = -1;
775 }
776 
777 static void
listen_callback(io_t * context)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 *
ioloop_setup_listener(int family,bool stream,bool tls,uint16_t port,const char * ip_address,const char * multicast,const char * name,comm_callback_t datagram_callback,comm_callback_t connected,comm_callback_t disconnected,io_callback_t finalize,io_callback_t connection_finalize,void * context)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
connect_callback(io_t * context)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 *
ioloop_connect(addr_t * NONNULL remote_address,bool tls,bool stream,comm_callback_t datagram_callback,comm_callback_t connected,disconnect_callback_t disconnected,io_callback_t finalize,void * context)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
ioloop_map_interface_addresses(void * context,interface_callback_t callback)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 *
ioloop_subproc(const char * exepath,char * NULLABLE * argv,int argc,subproc_callback_t callback)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
dnssd_txn_callback(io_t * io)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
dnssd_txn_finalize(io_t * io)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 *
ioloop_dnssd_txn_add(DNSServiceRef ref,dnssd_txn_finalize_callback_t finalize_callback,dnssd_txn_close_callback_t close_callback)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:
1371