]> git.saurik.com Git - apple/mdnsresponder.git/blob - ServiceRegistration/macos-ioloop.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / ServiceRegistration / macos-ioloop.c
1 /* macos-ioloop.c
2 *
3 * Copyright (c) 2018-2020 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 _GNU_SOURCE
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <sys/uio.h>
27 #include <errno.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <sys/wait.h>
32 #include <fcntl.h>
33 #include <sys/time.h>
34 #include <signal.h>
35 #include <net/if.h>
36 #include <ifaddrs.h>
37 #include <dns_sd.h>
38
39 #include <dispatch/dispatch.h>
40
41 #include "srp.h"
42 #include "dns-msg.h"
43 #include "srp-crypto.h"
44 #include "ioloop.h"
45 #include "xpc_client_advertising_proxy.h"
46
47 static bool connection_write_now(comm_t *NONNULL connection);
48
49 dispatch_queue_t ioloop_main_queue;
50
51 // Forward references
52 static void tcp_start(comm_t *NONNULL connection);
53
54 int64_t
55 ioloop_timenow(void)
56 {
57 int64_t now;
58 struct timeval tv;
59 gettimeofday(&tv, 0);
60 now = (int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec / 1000;
61 return now;
62 }
63
64 static void
65 wakeup_event(void *context)
66 {
67 wakeup_t *wakeup = context;
68
69 // All ioloop wakeups are one-shot.
70 ioloop_cancel_wake_event(wakeup);
71
72 // Call the callback, which mustn't be null.
73 wakeup->wakeup(wakeup->context);
74 }
75
76 static void
77 wakeup_finalize(void *context)
78 {
79 wakeup_t *wakeup = context;
80 if (wakeup->ref_count == 0) {
81 if (wakeup->dispatch_source != NULL) {
82 dispatch_release(wakeup->dispatch_source);
83 wakeup->dispatch_source = NULL;
84 }
85 if (wakeup->finalize != NULL) {
86 wakeup->finalize(wakeup->context);
87 }
88 free(wakeup);
89 }
90 }
91
92 void
93 ioloop_wakeup_retain_(wakeup_t *wakeup, const char *file, int line)
94 {
95 (void)file; (void)line;
96 RETAIN(wakeup);
97 }
98
99 void
100 ioloop_wakeup_release_(wakeup_t *wakeup, const char *file, int line)
101 {
102 (void)file; (void)line;
103 RELEASE(wakeup, wakeup_finalize);
104 }
105
106 wakeup_t *
107 ioloop_wakeup_create(void)
108 {
109 wakeup_t *ret = calloc(1, sizeof(*ret));
110 if (ret) {
111 RETAIN_HERE(ret);
112 }
113 return ret;
114 }
115
116 bool
117 ioloop_add_wake_event(wakeup_t *wakeup, void *context, wakeup_callback_t callback, wakeup_callback_t finalize,
118 int milliseconds)
119 {
120 if (callback == NULL) {
121 ERROR("ioloop_add_wake_event called with null callback");
122 return false;
123 }
124 if (wakeup->dispatch_source != NULL) {
125 ioloop_cancel_wake_event(wakeup);
126 }
127 wakeup->wakeup = callback;
128 wakeup->context = context;
129 wakeup->finalize = finalize;
130
131 wakeup->dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, ioloop_main_queue);
132 if (wakeup->dispatch_source == NULL) {
133 ERROR("dispatch_source_create failed in ioloop_add_wake_event().");
134 return false;
135 }
136 dispatch_source_set_event_handler_f(wakeup->dispatch_source, wakeup_event);
137 dispatch_set_context(wakeup->dispatch_source, wakeup);
138
139 // libdispatch doesn't allow events that are scheduled to happen right now. But it is actually useful to be
140 // able to trigger an event to happen immediately, and this is the easiest way to do it from ioloop-we
141 // can't rely on just scheduling an asynchronous event on an event loop because that's specific to Mac.
142 if (milliseconds <= 0) {
143 ERROR("ioloop_add_wake_event: milliseconds = %d", milliseconds);
144 milliseconds = 10;
145 }
146 dispatch_source_set_timer(wakeup->dispatch_source,
147 dispatch_time(DISPATCH_TIME_NOW, (uint64_t)milliseconds * NSEC_PER_SEC / 1000),
148 (uint64_t)milliseconds * NSEC_PER_SEC / 1000, NSEC_PER_SEC / 100);
149 dispatch_resume(wakeup->dispatch_source);
150
151 return true;
152 }
153
154 void
155 ioloop_cancel_wake_event(wakeup_t *wakeup)
156 {
157 if (wakeup->dispatch_source != NULL) {
158 dispatch_source_cancel(wakeup->dispatch_source);
159 dispatch_release(wakeup->dispatch_source);
160 wakeup->dispatch_source = NULL;
161 }
162 }
163
164 bool
165 ioloop_init(void)
166 {
167 ioloop_main_queue = dispatch_get_main_queue();
168 dispatch_retain(ioloop_main_queue);
169 return true;
170 }
171
172 int
173 ioloop(void)
174 {
175 dispatch_main();
176 return 0;
177 }
178
179 #define connection_cancel(conn) connection_cancel_(conn, __FILE__, __LINE__)
180 static void
181 connection_cancel_(nw_connection_t connection, const char *file, int line)
182 {
183 if (connection == NULL) {
184 INFO("connection_cancel: null connection at " PUB_S_SRP ":%d", file, line);
185 } else {
186 INFO("connection_cancel: " PUB_S_SRP ":%d", file, line);
187 nw_connection_cancel(connection);
188 }
189 }
190
191 static void
192 comm_finalize(comm_t *comm)
193 {
194 ERROR("comm_finalize");
195 if (comm->connection != NULL) {
196 nw_release(comm->connection);
197 comm->connection = NULL;
198 }
199 if (comm->listener != NULL) {
200 nw_release(comm->listener);
201 comm->listener = NULL;
202 }
203 if (comm->parameters) {
204 nw_release(comm->parameters);
205 comm->parameters = NULL;
206 }
207 if (comm->pending_write != NULL) {
208 dispatch_release(comm->pending_write);
209 comm->pending_write = NULL;
210 }
211 // If there is an nw_connection_t or nw_listener_t outstanding, then we will get an asynchronous callback
212 // later on. So we can't actually free the data structure yet, but the good news is that comm_finalize() will
213 // be called again later when the last outstanding asynchronous cancel is done, and then all of the stuff
214 // that follows this will happen.
215 #ifndef __clang_analyzer__
216 if (comm->ref_count > 0) {
217 return;
218 }
219 #endif
220 if (comm->idle_timer != NULL) {
221 ioloop_cancel_wake_event(comm->idle_timer);
222 RELEASE_HERE(comm->idle_timer, wakeup_finalize);
223 }
224 if (comm->name != NULL) {
225 free(comm->name);
226 }
227 if (comm->finalize != NULL) {
228 comm->finalize(comm->context);
229 }
230 free(comm);
231 }
232
233 void
234 ioloop_comm_retain_(comm_t *comm, const char *file, int line)
235 {
236 (void)file; (void)line;
237 RETAIN(comm);
238 }
239
240 void
241 ioloop_comm_release_(comm_t *comm, const char *file, int line)
242 {
243 (void)file; (void)line;
244 RELEASE(comm, comm_finalize);
245 }
246
247 static message_t *
248 message_create(size_t message_size)
249 {
250 message_t *message;
251
252 // Never should have a message shorter than this.
253 if (message_size < DNS_HEADER_SIZE) {
254 return NULL;
255 }
256
257 message = (message_t *)malloc(message_size + (sizeof (message_t)) - (sizeof (dns_wire_t)));
258 if (message) {
259 memset(message, 0, (sizeof (message_t)) - (sizeof (dns_wire_t)));
260 RETAIN_HERE(message);
261 }
262 return message;
263 }
264
265 void
266 ioloop_comm_cancel(comm_t *connection)
267 {
268 if (connection->connection != NULL) {
269 connection_cancel(connection->connection);
270 }
271 }
272
273 static void
274 message_finalize(message_t *message)
275 {
276 free(message);
277 }
278
279 void
280 ioloop_message_retain_(message_t *message, const char *file, int line)
281 {
282 (void)file; (void)line;
283 RETAIN(message);
284 }
285
286 void
287 ioloop_message_release_(message_t *message, const char *file, int line)
288 {
289 (void)file; (void)line;
290 RELEASE(message, message_finalize);
291 }
292
293 bool
294 ioloop_send_message(comm_t *connection, message_t *responding_to, struct iovec *iov, int iov_len)
295 {
296 dispatch_data_t data = NULL, new_data, combined;
297 int i;
298 uint16_t len = 0;
299
300 // Not needed on OSX because UDP conversations are treated as "connections."
301 (void)responding_to;
302
303 if (connection->connection == NULL) {
304 return false;
305 }
306
307 // Create a dispatch_data_t object that contains the data in the iov.
308 for (i = 0; i < iov_len; i++) {
309 new_data = dispatch_data_create(iov->iov_base, iov->iov_len,
310 ioloop_main_queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
311 len += iov->iov_len;
312 if (data != NULL) {
313 if (new_data != NULL) {
314 // Subsequent times through
315 combined = dispatch_data_create_concat(data, new_data);
316 dispatch_release(data);
317 dispatch_release(new_data);
318 data = combined;
319 } else {
320 // Fail
321 dispatch_release(data);
322 data = NULL;
323 }
324 } else {
325 // First time through
326 data = new_data;
327 }
328 if (data == NULL) {
329 ERROR("ioloop_send_message: no memory.");
330 return false;
331 }
332 }
333
334 if (len == 0) {
335 if (data) {
336 dispatch_release(data);
337 }
338 return false;
339 }
340
341 // TCP requires a length as well as the payload.
342 if (connection->tcp_stream) {
343 len = htons(len);
344 new_data = dispatch_data_create(&len, sizeof (len), ioloop_main_queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
345 if (new_data == NULL) {
346 if (data != NULL) {
347 dispatch_release(data);
348 }
349 return false;
350 }
351 // Length is at beginning.
352 combined = dispatch_data_create_concat(new_data, data);
353 dispatch_release(data);
354 dispatch_release(new_data);
355 if (combined == NULL) {
356 return false;
357 }
358 data = combined;
359 }
360
361 if (connection->pending_write != NULL) {
362 ERROR("Dropping pending write on " PRI_S_SRP, connection->name ? connection->name : "<null>");
363 }
364 connection->pending_write = data;
365 if (connection->connection_ready) {
366 return connection_write_now(connection);
367 }
368 return true;
369 }
370
371 static bool
372 connection_write_now(comm_t *connection)
373 {
374 // Retain the connection once for each write that's pending, so that it's never finalized while
375 // there's a write in progress.
376 connection->writes_pending++;
377 RETAIN_HERE(connection);
378 nw_connection_send(connection->connection, connection->pending_write, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true,
379 ^(nw_error_t _Nullable error) {
380 if (error != NULL) {
381 ERROR("ioloop_send_message: write failed: " PUB_S_SRP,
382 strerror(nw_error_get_error_code(error)));
383 connection_cancel(connection->connection);
384 }
385 if (connection->writes_pending > 0) {
386 connection->writes_pending--;
387 RELEASE_HERE(connection, comm_finalize);
388 } else {
389 ERROR("ioloop_send_message: write callback reached with no writes marked pending.");
390 }
391 });
392 // nw_connection_send should retain this, so let go of our reference to it.
393 dispatch_release(connection->pending_write);
394 connection->pending_write = NULL;
395 return true;
396 }
397
398 static bool
399 datagram_read(comm_t *connection, size_t length, dispatch_data_t content, nw_error_t error)
400 {
401 message_t *message = NULL;
402 bool ret = true, *retp = &ret;
403
404 if (error != NULL) {
405 ERROR("datagram_read: " PUB_S_SRP, strerror(nw_error_get_error_code(error)));
406 ret = false;
407 goto out;
408 }
409 if (length > UINT16_MAX) {
410 ERROR("datagram_read: oversized datagram length %zd", length);
411 ret = false;
412 goto out;
413 }
414 message = message_create(length);
415 if (message == NULL) {
416 ERROR("datagram_read: unable to allocate message.");
417 ret = false;
418 goto out;
419 }
420 message->length = (uint16_t)length;
421 dispatch_data_apply(content,
422 ^bool (dispatch_data_t __unused region, size_t offset, const void *buffer, size_t size) {
423 if (message->length < offset + size) {
424 ERROR("datagram_read: data region %zd:%zd is out of range for message length %d",
425 offset, size, message->length);
426 *retp = false;
427 return false;
428 }
429 memcpy(((uint8_t *)&message->wire) + offset, buffer, size);
430 return true;
431 });
432 if (ret == true) {
433 // Process the message.
434 connection->datagram_callback(connection, message, connection->context);
435 }
436
437 out:
438 if (message != NULL) {
439 ioloop_message_release(message);
440 }
441 if (!ret) {
442 connection_cancel(connection->connection);
443 }
444 return ret;
445 }
446
447 static void
448 tcp_read(comm_t *connection, size_t length, dispatch_data_t content, nw_error_t error)
449 {
450 if (error != NULL) {
451 connection_cancel(connection->connection);
452 return;
453 }
454 if (datagram_read(connection, length, content, error)) {
455 // Wait for the next frame
456 tcp_start(connection);
457 }
458 }
459
460 static void
461 tcp_read_length(comm_t *connection, dispatch_data_t content, nw_error_t error)
462 {
463 size_t length;
464 uint32_t bytes_to_read;
465 const uint8_t *lenbuf;
466 dispatch_data_t map;
467
468 if (error != NULL) {
469 ERROR("tcp_read_length: " PUB_S_SRP, strerror(nw_error_get_error_code(error)));
470 fail:
471 connection_cancel(connection->connection);
472 return;
473 }
474 if (connection->connection == NULL) {
475 return;
476 }
477 if (content == NULL) {
478 INFO("tcp_read_length: remote end closed connection.");
479 goto fail;
480 }
481
482 map = dispatch_data_create_map(content, (const void **)&lenbuf, &length);
483 if (map == NULL) {
484 ERROR("tcp_read_length: map create failed");
485 goto fail;
486 } else if (length != 2) {
487 ERROR("tcp_read_length: invalid length = %zu", length);
488 goto fail;
489 }
490 bytes_to_read = ((unsigned)(lenbuf[0]) << 8) | ((unsigned)lenbuf[1]);
491 nw_connection_receive(connection->connection, bytes_to_read, bytes_to_read,
492 ^(dispatch_data_t new_content, nw_content_context_t __unused new_context,
493 bool __unused is_complete, nw_error_t new_error) {
494 tcp_read(connection, bytes_to_read, new_content, new_error);
495 });
496 }
497
498 static void __unused
499 connection_idle_wakeup_callback(void *context)
500 {
501 comm_t *connection = context;
502 ERROR("Connection " PRI_S_SRP " has gone idle", connection->name);
503 connection_cancel(connection->connection);
504 }
505
506 static void __unused
507 connection_idle_wakeup_finalize(void *context)
508 {
509 comm_t *connection = context;
510 connection->idle_timer = NULL;
511 }
512
513 static void
514 tcp_start(comm_t *connection)
515 {
516 if (connection->connection == NULL) {
517 return;
518 }
519 // We want to disconnect if the connection is idle for more than a short while.
520 if (connection->idle_timer == NULL) {
521 connection->idle_timer = ioloop_wakeup_create();
522 if (connection->idle_timer == NULL) {
523 // If we can't set up a timer, drop the connection now.
524 connection_cancel(connection->connection);
525 return;
526 }
527 }
528 ioloop_add_wake_event(connection->idle_timer, connection,
529 connection_idle_wakeup_callback, connection_idle_wakeup_finalize,
530 60 * 1000); // One minute
531 nw_connection_receive(connection->connection, 2, 2,
532 ^(dispatch_data_t content, nw_content_context_t __unused context,
533 bool is_complete, nw_error_t error) {
534 // For TCP connections, is_complete means the other end closed the connection.
535 if (is_complete || content == NULL) {
536 INFO("tcp_start: remote end closed connection.");
537 connection_cancel(connection->connection);
538 } else {
539 tcp_read_length(connection, content, error);
540 }
541 });
542 }
543
544 static void
545 udp_start(comm_t *connection)
546 {
547 if (connection->connection == NULL) {
548 return;
549 }
550
551 // UDP is connectionless; the "connection" is just a placeholder that allows us to reply to the source.
552 // In principle, the five-tuple that is represented by the connection object should die as soon as the
553 // client is done retransmitting, since a later transaction should come from a different source port.
554 // Consequently, we set an idle timer: if we don't see any packets on this five-tuple after twenty seconds,
555 // it's unlikely that we will see any more, so it's time to collect the connection. If another packet
556 // does come in after this, a new connection will be created. The only risk is that if the cancel comes
557 // after a packet has arrived and been consumed by the nw_connection, but before we've called nw_connection_read,
558 // it will be lost. This should never happen for an existing SRP client, since the longest retry interval
559 // by default is 15 seconds; as the retry intervals get longer, it becomes safer to collect the connection
560 // and allow it to be recreated.
561 if (connection->server) {
562 if (connection->idle_timer == NULL) {
563 connection->idle_timer = ioloop_wakeup_create();
564 if (connection->idle_timer == NULL) {
565 // If we can't set up a timer, drop the connection now.
566 connection_cancel(connection->connection);
567 return;
568 }
569 }
570 ioloop_add_wake_event(connection->idle_timer, connection,
571 connection_idle_wakeup_callback, connection_idle_wakeup_finalize,
572 20 * 1000); // 20 seconds (15 seconds is the SRP client retry interval)
573 }
574
575 connection->read_pending = true; // When a read is pending, we have an extra refcount on the connection
576 RETAIN_HERE(connection);
577 nw_connection_receive_message(connection->connection,
578 ^(dispatch_data_t content, nw_content_context_t __unused context,
579 bool __unused is_complete, nw_error_t error) {
580 bool proceed = true;
581 if (content != NULL) {
582 proceed = datagram_read(connection, dispatch_data_get_size(content),
583 content, error);
584 }
585 if (content == NULL || error != NULL) {
586 connection_cancel(connection->connection);
587 }
588 // Once we have a five-tuple connection, we can't easily get rid of it, so keep
589 // reading.
590 else if (proceed) {
591 udp_start(connection);
592 }
593 RELEASE_HERE(connection, comm_finalize);
594 });
595 }
596
597 static void
598 connection_state_changed(comm_t *connection, nw_connection_state_t state, nw_error_t error)
599 {
600 (void)error;
601 if (state == nw_connection_state_ready) {
602 INFO("connection_state_changed: " PRI_S_SRP " state is ready; error = %p",
603 connection->name != NULL ? connection->name : "<no name>", error);
604 // Set up a reader.
605 if (connection->tcp_stream) {
606 tcp_start(connection);
607 } else {
608 udp_start(connection);
609 }
610 connection->connection_ready = true;
611 // If there's a write pending, send it now.
612 if (connection->pending_write) {
613 connection_write_now(connection);
614 }
615 } else if (state == nw_connection_state_failed) {
616 INFO("connection_state_changed: " PRI_S_SRP " state is failed; error = %p",
617 connection->name != NULL ? connection->name : "<no name>", error);
618 connection_cancel(connection->connection);
619 } else if (state == nw_connection_state_cancelled) {
620 INFO("connection_state_changed: " PRI_S_SRP " state is canceled; error = %p",
621 connection->name != NULL ? connection->name : "<no name>", error);
622 // This releases the final reference to the connection object, which was held by the nw_connection_t.
623 RELEASE_HERE(connection, comm_finalize);
624 } else {
625 INFO("connection_state_changed: " PRI_S_SRP " state is %d; error = %p",
626 connection->name != NULL ? connection->name : "<no name>", state, error);
627 }
628 }
629
630 static void
631 connection_callback(comm_t *listener, nw_connection_t new_connection)
632 {
633 comm_t *connection = calloc(1, sizeof *connection);
634 if (connection == NULL) {
635 ERROR("Unable to receive connection: no memory.");
636 // Assuming that since we haven't retained the connection, it will be released?
637 // XXX RefCount Check.
638 return;
639 }
640
641 connection->connection = new_connection;
642 nw_retain(connection->connection);
643
644 connection->name = nw_connection_copy_description(connection->connection);
645 if (connection->name != NULL) {
646 INFO("Received connection from " PRI_S_SRP, connection->name);
647 } else {
648 ERROR("Unable to get description of new connection.");
649 }
650 connection->datagram_callback = listener->datagram_callback;
651 connection->tcp_stream = listener->tcp_stream;
652 connection->server = true;
653 nw_connection_set_state_changed_handler(connection->connection,
654 ^(nw_connection_state_t state, nw_error_t error)
655 { connection_state_changed(connection, state, error); });
656 nw_connection_set_queue(connection->connection, ioloop_main_queue);
657 nw_connection_start(connection->connection);
658 // new_connection holds a reference to the connection until it is canceled.
659 RETAIN_HERE(connection);
660 if (listener->connected != NULL) {
661 listener->connected(connection, listener->context);
662 }
663 }
664
665 static void
666 listener_finalize(comm_t *listener)
667 {
668 if (listener->listener != NULL) {
669 nw_release(listener->listener);
670 listener->listener = NULL;
671 }
672 if (listener->name != NULL) {
673 free(listener->name);
674 }
675 if (listener->parameters) {
676 nw_release(listener->parameters);
677 }
678 if (listener->avoid_ports != NULL) {
679 free(listener->avoid_ports);
680 }
681 if (listener->finalize) {
682 listener->finalize(listener->context);
683 }
684 free(listener);
685 }
686
687 void
688 ioloop_listener_retain_(comm_t *listener, const char *file, int line)
689 {
690 RETAIN(listener);
691 }
692
693 void
694 ioloop_listener_release_(comm_t *listener, const char *file, int line)
695 {
696 RELEASE(listener, listener_finalize);
697 }
698
699 void
700 ioloop_listener_cancel(comm_t *connection)
701 {
702 if (connection->listener != NULL) {
703 nw_listener_cancel(connection->listener);
704 nw_release(connection->listener);
705 connection->listener = NULL;
706 }
707 }
708
709 static void
710 ioloop_listener_state_changed_handler(comm_t *listener, nw_listener_state_t state, nw_error_t error)
711 {
712 int i;
713 if (error != NULL) {
714 INFO("nw_listener_create:state changed: error");
715 } else {
716 if (state == nw_listener_state_waiting) {
717 INFO("nw_listener_create: waiting");
718 return;
719 } else if (state == nw_listener_state_failed) {
720 INFO("nw_listener_create: failed");
721 nw_listener_cancel(listener->listener);
722 } else if (state == nw_listener_state_ready) {
723 INFO("nw_listener_create: ready");
724 if (listener->avoiding) {
725 listener->listen_port = nw_listener_get_port(listener->listener);
726 if (listener->avoid_ports != NULL) {
727 for (i = 0; i < listener->num_avoid_ports; i++) {
728 if (listener->avoid_ports[i] == listener->listen_port) {
729 INFO("ioloop_listener_state_changed_handler: Got port %d, which we are avoiding.",
730 listener->listen_port);
731 listener->avoiding = true;
732 listener->listen_port = 0;
733 nw_listener_cancel(listener->listener);
734 return;
735 }
736 }
737 }
738 INFO("ioloop_listener_state_changed_handler: Got port %d.", listener->listen_port);
739 listener->avoiding = false;
740 if (listener->ready) {
741 listener->ready(listener->context, listener->listen_port);
742 }
743 }
744 } else if (state == nw_listener_state_cancelled) {
745 INFO("ioloop_listener_state_changed_handler: cancelled");
746 nw_release(listener->listener);
747 listener->listener = NULL;
748 if (listener->avoiding) {
749 listener->listener = nw_listener_create(listener->parameters);
750 if (listener->listener == NULL) {
751 ERROR("ioloop_listener_state_changed_handler: Unable to recreate listener.");
752 goto cancel;
753 } else {
754 RETAIN_HERE(listener);
755 nw_listener_set_state_changed_handler(listener->listener,
756 ^(nw_listener_state_t ev_state, nw_error_t ev_error) {
757 ioloop_listener_state_changed_handler(listener, ev_state, ev_error);
758 });
759 }
760 } else {
761 ;
762 cancel:
763 if (listener->cancel) {
764 listener->cancel(listener->context);
765 }
766 RELEASE_HERE(listener, listener_finalize);
767 }
768 }
769 }
770 }
771
772 comm_t *
773 ioloop_listener_create(bool stream, bool tls, uint16_t *avoid_ports, int num_avoid_ports,
774 const addr_t *ip_address, const char *multicast, const char *name,
775 datagram_callback_t datagram_callback, connect_callback_t connected, cancel_callback_t cancel,
776 ready_callback_t ready, finalize_callback_t finalize, void *context)
777 {
778 comm_t *listener;
779 int family = (ip_address != NULL) ? ip_address->sa.sa_family : AF_UNSPEC;
780 uint16_t port;
781 char portbuf[10];
782 nw_endpoint_t endpoint;
783
784 if (ip_address == NULL) {
785 port = 0;
786 } else {
787 port = (family == AF_INET) ? ip_address->sin.sin_port : ip_address->sin6.sin6_port;
788 }
789
790 if (multicast != NULL) {
791 ERROR("ioloop_setup_listener: multicast not supported.");
792 return NULL;
793 }
794
795 if (datagram_callback == NULL) {
796 ERROR("ioloop_setup: no datagram callback provided.");
797 return NULL;
798 }
799
800 sprintf(portbuf, "%d", port);
801 listener = calloc(1, sizeof(*listener));
802 if (listener == NULL) {
803 if (ip_address == NULL) {
804 ERROR("No memory for listener on <NULL>#%d", port);
805 } else if (family == AF_INET) {
806 IPv4_ADDR_GEN_SRP(&ip_address->sin.sin_addr.s_addr, ipv4_addr_buf);
807 ERROR("No memory for listener on " PRI_IPv4_ADDR_SRP "#%d",
808 IPv4_ADDR_PARAM_SRP(&ip_address->sin.sin_addr.s_addr, ipv4_addr_buf), port);
809 } else if (family == AF_INET6) {
810 SEGMENTED_IPv6_ADDR_GEN_SRP(ip_address->sin6.sin6_addr.s6_addr, ipv6_addr_buf);
811 ERROR("No memory for listener on " PRI_SEGMENTED_IPv6_ADDR_SRP "#%d",
812 SEGMENTED_IPv6_ADDR_PARAM_SRP(ip_address->sin6.sin6_addr.s6_addr, ipv6_addr_buf), port);
813 } else {
814 ERROR("No memory for listener on <family address other than AF_INET or AF_INET6: %d>#%d", family, port);
815 }
816 return NULL;
817 }
818 if (avoid_ports != NULL) {
819 listener->avoid_ports = malloc(num_avoid_ports * sizeof(uint16_t));
820 if (listener->avoid_ports == NULL) {
821 if (ip_address == NULL) {
822 ERROR("No memory for listener avoid_ports on <NULL>#%d", port);
823 } else if (family == AF_INET) {
824 IPv4_ADDR_GEN_SRP(&ip_address->sin.sin_addr.s_addr, ipv4_addr_buf);
825 ERROR("No memory for listener avoid_ports on " PRI_IPv4_ADDR_SRP "#%d",
826 IPv4_ADDR_PARAM_SRP(&ip_address->sin.sin_addr.s_addr, ipv4_addr_buf), port);
827 } else if (family == AF_INET6) {
828 SEGMENTED_IPv6_ADDR_GEN_SRP(ip_address->sin6.sin6_addr.s6_addr, ipv6_addr_buf);
829 ERROR("No memory for listener avoid_ports on " PRI_SEGMENTED_IPv6_ADDR_SRP "#%d",
830 SEGMENTED_IPv6_ADDR_PARAM_SRP(ip_address->sin6.sin6_addr.s6_addr, ipv6_addr_buf), port);
831 } else {
832 ERROR("No memory for listener avoid_ports on <family address other than AF_INET or AF_INET6: %d>#%d",
833 family, port);
834 }
835 free(listener);
836 return NULL;
837 }
838 listener->num_avoid_ports = num_avoid_ports;
839 listener->avoiding = true;
840 }
841 RETAIN_HERE(listener);
842 if (port == 0) {
843 endpoint = NULL;
844 // Even though we don't have any ports to avoid, we still want the "avoiding" behavior in this case, since that
845 // is what triggers a call to the ready handler, which passes the port number that we got to it.
846 listener->avoiding = true;
847 } else {
848 listener->listen_port = port;
849 char ip_address_str[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
850 if (ip_address == NULL) {
851 if (family == AF_INET) {
852 snprintf(ip_address_str, sizeof(ip_address_str), "0.0.0.0");
853 } else {
854 // AF_INET6 or AF_UNSPEC
855 snprintf(ip_address_str, sizeof(ip_address_str), "::");
856 }
857 } else {
858 inet_ntop(family, ip_address->sa.sa_data, ip_address_str, sizeof(ip_address_str));
859 }
860 endpoint = nw_endpoint_create_host(ip_address_str, portbuf);
861 if (endpoint == NULL) {
862 ERROR("No memory for listener endpoint.");
863 RELEASE_HERE(listener, listener_finalize);
864 return NULL;
865 }
866 }
867
868 if (stream) {
869 listener->parameters = nw_parameters_create_secure_tcp(tls ? NW_PARAMETERS_DEFAULT_CONFIGURATION
870 : NW_PARAMETERS_DISABLE_PROTOCOL,
871 NW_PARAMETERS_DEFAULT_CONFIGURATION);
872 } else {
873 if (tls) {
874 ERROR("DTLS support not implemented.");
875 nw_release(endpoint);
876 RELEASE_HERE(listener, listener_finalize);
877 return NULL;
878 }
879 listener->parameters = nw_parameters_create_secure_udp(NW_PARAMETERS_DISABLE_PROTOCOL,
880 NW_PARAMETERS_DEFAULT_CONFIGURATION);
881 }
882 if (listener->parameters == NULL) {
883 ERROR("No memory for listener parameters.");
884 nw_release(endpoint);
885 RELEASE_HERE(listener, listener_finalize);
886 return NULL;
887 }
888
889 if (endpoint != NULL) {
890 nw_parameters_set_local_endpoint(listener->parameters, endpoint);
891 nw_release(endpoint);
892 }
893
894 if (tls) {
895 nw_protocol_options_t tls_options = nw_tls_create_options();
896 if (tls_options == NULL) {
897 ERROR("No memory for tls protocol options.");
898 RELEASE_HERE(listener, listener_finalize);
899 return NULL;
900 }
901 // XXX set up the listener certificate(s).
902 // XXX how to configure this onto the parameters object?
903 }
904
905 // Set SO_REUSEADDR.
906 nw_parameters_set_reuse_local_address(listener->parameters, true);
907
908 // Create the nw_listener_t.
909 listener->listener = nw_listener_create(listener->parameters);
910 if (listener->listener == NULL) {
911 ERROR("no memory for nw_listener object");
912 RELEASE_HERE(listener, listener_finalize);
913 return NULL;
914 }
915 nw_listener_set_new_connection_handler(listener->listener,
916 ^(nw_connection_t connection) { connection_callback(listener, connection); }
917 );
918
919 RETAIN_HERE(listener); // for the nw_listener_t
920 nw_listener_set_state_changed_handler(listener->listener, ^(nw_listener_state_t state, nw_error_t error) {
921 ioloop_listener_state_changed_handler(listener, state, error);
922 });
923
924 listener->name = strdup(name);
925 listener->datagram_callback = datagram_callback;
926 listener->cancel = cancel;
927 listener->ready = ready;
928 listener->finalize = finalize;
929 listener->context = context;
930 listener->connected = connected;
931 listener->tcp_stream = stream;
932
933 nw_listener_set_queue(listener->listener, ioloop_main_queue);
934 nw_listener_start(listener->listener);
935 // Listener has one refcount
936 return listener;
937 }
938
939 comm_t *
940 ioloop_connection_create(addr_t *NONNULL remote_address, bool tls, bool stream,
941 datagram_callback_t datagram_callback, connect_callback_t connected,
942 disconnect_callback_t disconnected, finalize_callback_t finalize, void *context)
943 {
944 comm_t *connection;
945 char portbuf[10];
946 nw_parameters_t parameters;
947 nw_endpoint_t endpoint;
948 char addrbuf[INET6_ADDRSTRLEN];
949
950 inet_ntop(remote_address->sa.sa_family, (remote_address->sa.sa_family == AF_INET
951 ? (void *)&remote_address->sin.sin_addr
952 : (void *)&remote_address->sin6.sin6_addr), addrbuf, sizeof addrbuf);
953 sprintf(portbuf, "%d", (remote_address->sa.sa_family == AF_INET
954 ? ntohs(remote_address->sin.sin_port)
955 : ntohs(remote_address->sin6.sin6_port)));
956 connection = calloc(1, sizeof(*connection));
957 if (connection == NULL) {
958 ERROR("No memory for connection");
959 return NULL;
960 }
961 // If we don't release this because of an error, this is the caller's reference to the comm_t.
962 RETAIN_HERE(connection);
963 endpoint = nw_endpoint_create_host(addrbuf, portbuf);
964 if (endpoint == NULL) {
965 ERROR("No memory for connection endpoint.");
966 RELEASE_HERE(connection, comm_finalize);
967 return NULL;
968 }
969
970 if (stream) {
971 parameters = nw_parameters_create_secure_tcp(tls ? NW_PARAMETERS_DEFAULT_CONFIGURATION
972 : NW_PARAMETERS_DISABLE_PROTOCOL,
973 NW_PARAMETERS_DEFAULT_CONFIGURATION);
974 } else {
975 if (tls) {
976 ERROR("DTLS support not implemented.");
977 nw_release(endpoint);
978 RELEASE_HERE(connection, comm_finalize);
979 return NULL;
980 }
981 parameters = nw_parameters_create_secure_udp(NW_PARAMETERS_DISABLE_PROTOCOL,
982 NW_PARAMETERS_DEFAULT_CONFIGURATION);
983 }
984 if (parameters == NULL) {
985 ERROR("No memory for connection parameters.");
986 nw_release(endpoint);
987 RELEASE_HERE(connection, comm_finalize);
988 return NULL;
989 }
990
991 if (tls) {
992 #ifdef NOTYET
993 nw_protocol_options_t tls_options = nw_tls_create_options();
994 if (tls_options == NULL) {
995 ERROR("No memory for tls protocol options.");
996 RELEASE_HERE(connection, comm_finalize);
997 return NULL;
998 }
999 // XXX set up the connection certificate(s).
1000 // XXX how to configure this onto the parameters object?
1001 #endif
1002 }
1003
1004 connection->name = strdup(addrbuf);
1005
1006 // Create the nw_connection_t.
1007 connection->connection = nw_connection_create(endpoint, parameters);
1008 nw_release(endpoint);
1009 nw_release(parameters);
1010 if (connection->connection == NULL) {
1011 ERROR("no memory for nw_connection object");
1012 RELEASE_HERE(connection, comm_finalize);
1013 return NULL;
1014 }
1015
1016 connection->datagram_callback = datagram_callback;
1017 connection->connected = connected;
1018 connection->disconnected = disconnected;
1019 connection->finalize = finalize;
1020 connection->tcp_stream = stream;
1021 connection->context = context;
1022 nw_connection_set_state_changed_handler(connection->connection,
1023 ^(nw_connection_state_t state, nw_error_t error)
1024 { connection_state_changed(connection, state, error); });
1025 nw_connection_set_queue(connection->connection, ioloop_main_queue);
1026 // Until we get the canceled callback in connection_state_changed, the nw_connection_t holds a reference to this
1027 // comm_t object.
1028 RETAIN_HERE(connection);
1029 nw_connection_start(connection->connection);
1030 return connection;
1031 }
1032
1033 static void
1034 subproc_finalize(subproc_t *subproc)
1035 {
1036 int i;
1037 for (i = 0; i < subproc->argc; i++) {
1038 if (subproc->argv[i] != NULL) {
1039 free(subproc->argv[i]);
1040 subproc->argv[i] = NULL;
1041 }
1042 }
1043 if (subproc->dispatch_source != NULL) {
1044 dispatch_release(subproc->dispatch_source);
1045 }
1046 if (subproc->output_fd != NULL) {
1047 ioloop_file_descriptor_release(subproc->output_fd);
1048 }
1049 if (subproc->finalize != NULL) {
1050 subproc->finalize(subproc->context);
1051 }
1052 free(subproc);
1053 }
1054
1055 static void subproc_cancel(void *context)
1056 {
1057 subproc_t *subproc = context;
1058 subproc->dispatch_source = NULL;
1059 RELEASE_HERE(subproc, subproc_finalize);
1060 }
1061
1062 static void
1063 subproc_event(void *context)
1064 {
1065 subproc_t *subproc = context;
1066 pid_t pid;
1067 int status;
1068
1069 pid = waitpid(subproc->pid, &status, WNOHANG);
1070 if (pid <= 0) {
1071 return;
1072 }
1073 subproc->callback(subproc, status, NULL);
1074 if (!WIFSTOPPED(status)) {
1075 dispatch_source_cancel(subproc->dispatch_source);
1076 }
1077 }
1078
1079 static void subproc_output_finalize(void *context)
1080 {
1081 subproc_t *subproc = context;
1082 if (subproc->output_fd) {
1083 subproc->output_fd = NULL;
1084 }
1085 }
1086
1087 void
1088 ioloop_subproc_release_(subproc_t *subproc, const char *file, int line)
1089 {
1090 RELEASE(subproc, subproc_finalize);
1091 }
1092
1093 // Invoke the specified executable with the specified arguments. Call callback when it exits.
1094 // All failures are reported through the callback.
1095 subproc_t *
1096 ioloop_subproc(const char *exepath, char *NULLABLE *argv, int argc,
1097 subproc_callback_t callback, io_callback_t output_callback, void *context)
1098 {
1099 subproc_t *subproc;
1100 int i, rv;
1101 posix_spawn_file_actions_t actions;
1102 posix_spawnattr_t attrs;
1103
1104 if (callback == NULL) {
1105 ERROR("ioloop_add_wake_event called with null callback");
1106 return NULL;
1107 }
1108
1109 if (argc > MAX_SUBPROC_ARGS) {
1110 callback(NULL, 0, "too many subproc args");
1111 return NULL;
1112 }
1113
1114 subproc = calloc(1, sizeof *subproc);
1115 if (subproc == NULL) {
1116 callback(NULL, 0, "out of memory");
1117 return NULL;
1118 }
1119 RETAIN_HERE(subproc);
1120 if (output_callback != NULL) {
1121 rv = pipe(subproc->pipe_fds);
1122 if (rv < 0) {
1123 callback(NULL, 0, "unable to create pipe.");
1124 RELEASE_HERE(subproc, subproc_finalize);
1125 return NULL;
1126 }
1127 subproc->output_fd = ioloop_file_descriptor_create(subproc->pipe_fds[0], subproc, subproc_output_finalize);
1128 if (subproc->output_fd == NULL) {
1129 callback(NULL, 0, "out of memory.");
1130 close(subproc->pipe_fds[0]);
1131 close(subproc->pipe_fds[1]);
1132 RELEASE_HERE(subproc, subproc_finalize);
1133 return NULL;
1134 }
1135 }
1136
1137 subproc->argv[0] = strdup(exepath);
1138 if (subproc->argv[0] == NULL) {
1139 RELEASE_HERE(subproc, subproc_finalize);
1140 callback(NULL, 0, "out of memory");
1141 return NULL;
1142 }
1143 subproc->argc++;
1144 for (i = 0; i < argc; i++) {
1145 subproc->argv[i + 1] = strdup(argv[i]);
1146 if (subproc->argv[i + 1] == NULL) {
1147 RELEASE_HERE(subproc, subproc_finalize);
1148 callback(NULL, 0, "out of memory");
1149 return NULL;
1150 }
1151 subproc->argc++;
1152 }
1153
1154 // Set up for posix_spawn
1155 posix_spawn_file_actions_init(&actions);
1156 if (output_callback != NULL) {
1157 posix_spawn_file_actions_adddup2(&actions, subproc->pipe_fds[1], STDOUT_FILENO);
1158 posix_spawn_file_actions_addclose(&actions, subproc->pipe_fds[0]);
1159 posix_spawn_file_actions_addclose(&actions, subproc->pipe_fds[1]);
1160 }
1161 posix_spawnattr_init(&attrs);
1162 extern char **environ;
1163 rv = posix_spawn(&subproc->pid, exepath, &actions, &attrs, subproc->argv, environ);
1164 posix_spawn_file_actions_destroy(&actions);
1165 posix_spawnattr_destroy(&attrs);
1166 if (rv < 0) {
1167 ERROR("posix_spawn failed for " PUB_S_SRP ": " PUB_S_SRP, subproc->argv[0], strerror(errno));
1168 callback(subproc, 0, strerror(errno));
1169 RELEASE_HERE(subproc, subproc_finalize);
1170 return NULL;
1171 }
1172 subproc->callback = callback;
1173 subproc->context = context;
1174
1175 subproc->dispatch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, subproc->pid, DISPATCH_PROC_EXIT,
1176 ioloop_main_queue);
1177 if (subproc->dispatch_source == NULL) {
1178 ERROR("dispatch_source_create failed in ioloop_add_wake_event().");
1179 return false;
1180 }
1181 dispatch_retain(subproc->dispatch_source);
1182 dispatch_source_set_event_handler_f(subproc->dispatch_source, subproc_event);
1183 dispatch_source_set_cancel_handler_f(subproc->dispatch_source, subproc_cancel);
1184 dispatch_set_context(subproc->dispatch_source, subproc);
1185 dispatch_activate(subproc->dispatch_source);
1186 RETAIN_HERE(subproc); // Dispatch has a reference
1187
1188 // Now that we have a viable subprocess, add the reader callback.
1189 if (output_callback != NULL && subproc->output_fd != NULL) {
1190 close(subproc->pipe_fds[1]);
1191 ioloop_add_reader(subproc->output_fd, output_callback);
1192 }
1193 return subproc;
1194 }
1195
1196 void
1197 ioloop_dnssd_txn_cancel(dnssd_txn_t *txn)
1198 {
1199 if (txn->sdref != NULL) {
1200 DNSServiceRefDeallocate(txn->sdref);
1201 txn->sdref = NULL;
1202 } else {
1203 INFO("ioloop_dnssd_txn_cancel: dead transaction.");
1204 }
1205 }
1206
1207 static void
1208 dnssd_txn_finalize(dnssd_txn_t *txn)
1209 {
1210 if (txn->sdref != NULL) {
1211 ioloop_dnssd_txn_cancel(txn);
1212 }
1213 if (txn->finalize_callback) {
1214 txn->finalize_callback(txn->context);
1215 }
1216 free(txn);
1217 }
1218
1219 void
1220 ioloop_dnssd_txn_retain_(dnssd_txn_t *dnssd_txn, const char *file, int line)
1221 {
1222 (void)file; (void)line;
1223 RETAIN(dnssd_txn);
1224 }
1225
1226 void
1227 ioloop_dnssd_txn_release_(dnssd_txn_t *dnssd_txn, const char *file, int line)
1228 {
1229 (void)file; (void)line;
1230 RELEASE(dnssd_txn, dnssd_txn_finalize);
1231 }
1232
1233 dnssd_txn_t *
1234 ioloop_dnssd_txn_add_(DNSServiceRef ref, void *context, finalize_callback_t finalize_callback, const char *file,
1235 int line)
1236 {
1237 dnssd_txn_t *txn = calloc(1, sizeof(*txn));
1238 (void)file; (void)line;
1239
1240 if (txn != NULL) {
1241 RETAIN(txn);
1242 txn->sdref = ref;
1243 txn->context = context;
1244 txn->finalize_callback = finalize_callback;
1245 DNSServiceSetDispatchQueue(ref, ioloop_main_queue);
1246 }
1247 return txn;
1248 }
1249
1250 void
1251 ioloop_dnssd_txn_set_aux_pointer(dnssd_txn_t *NONNULL txn, void *aux_pointer)
1252 {
1253 txn->aux_pointer = aux_pointer;
1254 }
1255
1256 void *
1257 ioloop_dnssd_txn_get_aux_pointer(dnssd_txn_t *NONNULL txn)
1258 {
1259 return txn->aux_pointer;
1260 }
1261
1262 void *
1263 ioloop_dnssd_txn_get_context(dnssd_txn_t *NONNULL txn)
1264 {
1265 return txn->context;
1266 }
1267
1268 static bool
1269 ioloop_xpc_client_is_entitled(xpc_connection_t conn, const char *entitlement_name)
1270 {
1271 bool entitled = false;
1272 xpc_object_t entitled_obj = xpc_connection_copy_entitlement_value(conn, entitlement_name);
1273
1274 if (entitled_obj) {
1275 if (xpc_get_type(entitled_obj) == XPC_TYPE_BOOL && xpc_bool_get_value(entitled_obj)) {
1276 entitled = true;
1277 }
1278 xpc_release(entitled_obj);
1279 } else {
1280 ERROR("ioloop_xpc_client_is_entitled: Client Entitlement is NULL");
1281 }
1282
1283 if (!entitled) {
1284 ERROR("ioloop_xpc_client_is_entitled: Client is missing Entitlement!");
1285 }
1286
1287 return entitled;
1288 }
1289
1290 static void
1291 ioloop_xpc_accept(xpc_connection_t conn, const char *name, ioloop_xpc_callback_t callback)
1292 {
1293 struct state {
1294 xpc_connection_t conn;
1295 ioloop_xpc_callback_t callback;
1296 } *state;
1297
1298 if (conn == NULL) {
1299 ERROR("ioloop_xpc_accept: listener has been canceled.");
1300 return;
1301 }
1302
1303 state = calloc(1, sizeof(*state));
1304 if (state == NULL) {
1305 ERROR("ioloop_xpc_accept: no memory for xpc connection state.");
1306 return;
1307 }
1308
1309 int pid = xpc_connection_get_pid(conn);
1310 int uid = xpc_connection_get_euid(conn);
1311
1312 if (!ioloop_xpc_client_is_entitled(conn, name)) {
1313 ERROR("ioloop_xpc_accept: connection from uid %d pid %d is missing entitlement " PUB_S_SRP ".", uid, pid, name);
1314 xpc_connection_cancel(conn);
1315 free(state);
1316 return;
1317 }
1318
1319 state->conn = conn;
1320 xpc_retain(conn);
1321 state->callback = callback;
1322 xpc_connection_set_target_queue(conn, ioloop_main_queue);
1323 xpc_connection_set_event_handler(conn, ^(xpc_object_t request) {
1324 xpc_type_t type = xpc_get_type(request);
1325
1326 if (request == XPC_ERROR_CONNECTION_INVALID) {
1327 INFO("ioloop_xpc_accept event handler: connection has been finalized.");
1328 if (state->callback != NULL) {
1329 state->callback(state->conn, NULL);
1330 }
1331 // We are guaranteed that this is the last callback, so we can safely free state.
1332 if (state->conn != NULL) {
1333 xpc_release(state->conn);
1334 state->conn = NULL;
1335 }
1336 free(state);
1337 } else if (type == XPC_TYPE_DICTIONARY) {
1338 // If the callback returns false, that means that we're done.
1339 if (state->callback != NULL) {
1340 if (!state->callback(state->conn, request)) {
1341 INFO("ioloop_xpc_accept event handler: callback indicated done.");
1342 xpc_connection_cancel(state->conn);
1343 state->callback = NULL;
1344 } else {
1345 INFO("ioloop_xpc_accept event handler: continuing.");
1346 }
1347 }
1348 } else {
1349 INFO("ioloop_xpc_accept event handler: client went away.");
1350 // Passing a null request to the callback means the client went away.
1351 xpc_connection_cancel(state->conn);
1352 if (state->callback != NULL) {
1353 callback(state->conn, NULL);
1354 }
1355 state->callback = NULL;
1356 }
1357 });
1358 xpc_connection_resume(conn);
1359 }
1360
1361 xpc_connection_t
1362 ioloop_create_xpc_service(const char *name, ioloop_xpc_callback_t callback)
1363 {
1364 xpc_connection_t listener = xpc_connection_create_mach_service(name, ioloop_main_queue,
1365 XPC_CONNECTION_MACH_SERVICE_LISTENER);
1366 if (listener == NULL || xpc_get_type(listener) != XPC_TYPE_CONNECTION) {
1367 ERROR("ioloop_create_xpc_service: " PUB_S_SRP ": unable to create listener %p", name, listener);
1368 if (listener != NULL) {
1369 xpc_release(listener);
1370 }
1371 return NULL;
1372 }
1373
1374 xpc_connection_set_event_handler(listener, ^(xpc_object_t eventmsg) {
1375 xpc_type_t type = xpc_get_type(eventmsg);
1376
1377 if (type == XPC_TYPE_CONNECTION) {
1378 INFO("ioloop_create_xpc_service: New " PUB_S_SRP " Client %p", name, eventmsg);
1379 ioloop_xpc_accept((xpc_connection_t)eventmsg, name, callback);
1380 }
1381 else if (type == XPC_TYPE_ERROR) // Ideally, we would never hit these cases
1382 {
1383 ERROR("ioloop_create_xpc_service: XPCError: " PUB_S_SRP,
1384 xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION));
1385 callback(NULL, NULL);
1386 }
1387 else
1388 {
1389 INFO("ioloop_create_xpc_service: Unknown EventMsg type");
1390 }
1391 });
1392 xpc_connection_resume(listener);
1393 return listener;
1394 }
1395
1396 static void
1397 file_descriptor_finalize(void *context)
1398 {
1399 io_t *file_descriptor = context;
1400 if (file_descriptor->ref_count == 0) {
1401 if (file_descriptor->finalize) {
1402 file_descriptor->finalize(file_descriptor->context);
1403 }
1404 free(file_descriptor);
1405 }
1406 }
1407
1408 void
1409 ioloop_file_descriptor_retain_(io_t *file_descriptor, const char *file, int line)
1410 {
1411 (void)file; (void)line;
1412 RETAIN(file_descriptor);
1413 }
1414
1415 void
1416 ioloop_file_descriptor_release_(io_t *file_descriptor, const char *file, int line)
1417 {
1418 (void)file; (void)line;
1419 RELEASE(file_descriptor, file_descriptor_finalize);
1420 }
1421
1422 io_t *
1423 ioloop_file_descriptor_create_(int fd, void *context, finalize_callback_t finalize, const char *file, int line)
1424 {
1425 io_t *ret;
1426 ret = calloc(1, sizeof(*ret));
1427 if (ret) {
1428 ret->fd = fd;
1429 ret->context = context;
1430 ret->finalize = finalize;
1431 RETAIN(ret);
1432 }
1433 return ret;
1434 }
1435
1436 static void
1437 ioloop_read_cancel(void *context)
1438 {
1439 io_t *io = context;
1440
1441 if (io->read_source != NULL) {
1442 dispatch_release(io->read_source);
1443 io->read_source = NULL;
1444 // Release the reference count that dispatch was holding.
1445 RELEASE_HERE(io, file_descriptor_finalize);
1446 }
1447 }
1448
1449 static void
1450 ioloop_read_event(void *context)
1451 {
1452 io_t *io = context;
1453
1454 if (io->read_callback != NULL) {
1455 io->read_callback(io, io->context);
1456 }
1457 }
1458
1459 void
1460 ioloop_close(io_t *io)
1461 {
1462 if (io->read_source != NULL) {
1463 dispatch_cancel(io->read_source);
1464 }
1465 if (io->write_source != NULL) {
1466 dispatch_cancel(io->write_source);
1467 }
1468 io->fd = -1;
1469 }
1470
1471 void
1472 ioloop_add_reader(io_t *NONNULL io, io_callback_t NONNULL callback)
1473 {
1474 io->read_callback = callback;
1475 if (io->read_source == NULL) {
1476 io->read_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, io->fd, 0, ioloop_main_queue);
1477 }
1478 if (io->read_source == NULL) {
1479 ERROR("dispatch_source_create: unable to create read dispatch source.");
1480 return;
1481 }
1482 dispatch_source_set_event_handler_f(io->read_source, ioloop_read_event);
1483 dispatch_source_set_cancel_handler_f(io->read_source, ioloop_read_cancel);
1484 dispatch_set_context(io->read_source, io);
1485 RETAIN_HERE(io); // Dispatch will hold a reference.
1486 dispatch_resume(io->read_source);
1487 }
1488
1489 // Local Variables:
1490 // mode: C
1491 // tab-width: 4
1492 // c-file-style: "bsd"
1493 // c-basic-offset: 4
1494 // fill-column: 108
1495 // indent-tabs-mode: nil
1496 // End: