3 * Copyright (c) 2018-2020 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * Simple event dispatcher for DNS.
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
39 #include <dispatch/dispatch.h>
43 #include "srp-crypto.h"
45 #include "xpc_client_advertising_proxy.h"
47 static bool connection_write_now(comm_t
*NONNULL connection
);
49 dispatch_queue_t ioloop_main_queue
;
52 static void tcp_start(comm_t
*NONNULL connection
);
60 now
= (int64_t)tv
.tv_sec
* 1000 + (int64_t)tv
.tv_usec
/ 1000;
65 wakeup_event(void *context
)
67 wakeup_t
*wakeup
= context
;
69 // All ioloop wakeups are one-shot.
70 ioloop_cancel_wake_event(wakeup
);
72 // Call the callback, which mustn't be null.
73 wakeup
->wakeup(wakeup
->context
);
77 wakeup_finalize(void *context
)
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
;
85 if (wakeup
->finalize
!= NULL
) {
86 wakeup
->finalize(wakeup
->context
);
93 ioloop_wakeup_retain_(wakeup_t
*wakeup
, const char *file
, int line
)
95 (void)file
; (void)line
;
100 ioloop_wakeup_release_(wakeup_t
*wakeup
, const char *file
, int line
)
102 (void)file
; (void)line
;
103 RELEASE(wakeup
, wakeup_finalize
);
107 ioloop_wakeup_create(void)
109 wakeup_t
*ret
= calloc(1, sizeof(*ret
));
117 ioloop_add_wake_event(wakeup_t
*wakeup
, void *context
, wakeup_callback_t callback
, wakeup_callback_t finalize
,
120 if (callback
== NULL
) {
121 ERROR("ioloop_add_wake_event called with null callback");
124 if (wakeup
->dispatch_source
!= NULL
) {
125 ioloop_cancel_wake_event(wakeup
);
127 wakeup
->wakeup
= callback
;
128 wakeup
->context
= context
;
129 wakeup
->finalize
= finalize
;
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().");
136 dispatch_source_set_event_handler_f(wakeup
->dispatch_source
, wakeup_event
);
137 dispatch_set_context(wakeup
->dispatch_source
, wakeup
);
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
);
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
);
155 ioloop_cancel_wake_event(wakeup_t
*wakeup
)
157 if (wakeup
->dispatch_source
!= NULL
) {
158 dispatch_source_cancel(wakeup
->dispatch_source
);
159 dispatch_release(wakeup
->dispatch_source
);
160 wakeup
->dispatch_source
= NULL
;
167 ioloop_main_queue
= dispatch_get_main_queue();
168 dispatch_retain(ioloop_main_queue
);
179 #define connection_cancel(conn) connection_cancel_(conn, __FILE__, __LINE__)
181 connection_cancel_(nw_connection_t connection
, const char *file
, int line
)
183 if (connection
== NULL
) {
184 INFO("connection_cancel: null connection at " PUB_S_SRP
":%d", file
, line
);
186 INFO("connection_cancel: " PUB_S_SRP
":%d", file
, line
);
187 nw_connection_cancel(connection
);
192 comm_finalize(comm_t
*comm
)
194 ERROR("comm_finalize");
195 if (comm
->connection
!= NULL
) {
196 nw_release(comm
->connection
);
197 comm
->connection
= NULL
;
199 if (comm
->listener
!= NULL
) {
200 nw_release(comm
->listener
);
201 comm
->listener
= NULL
;
203 if (comm
->parameters
) {
204 nw_release(comm
->parameters
);
205 comm
->parameters
= NULL
;
207 if (comm
->pending_write
!= NULL
) {
208 dispatch_release(comm
->pending_write
);
209 comm
->pending_write
= NULL
;
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) {
220 if (comm
->idle_timer
!= NULL
) {
221 ioloop_cancel_wake_event(comm
->idle_timer
);
222 RELEASE_HERE(comm
->idle_timer
, wakeup_finalize
);
224 if (comm
->name
!= NULL
) {
227 if (comm
->finalize
!= NULL
) {
228 comm
->finalize(comm
->context
);
234 ioloop_comm_retain_(comm_t
*comm
, const char *file
, int line
)
236 (void)file
; (void)line
;
241 ioloop_comm_release_(comm_t
*comm
, const char *file
, int line
)
243 (void)file
; (void)line
;
244 RELEASE(comm
, comm_finalize
);
248 message_create(size_t message_size
)
252 // Never should have a message shorter than this.
253 if (message_size
< DNS_HEADER_SIZE
) {
257 message
= (message_t
*)malloc(message_size
+ (sizeof (message_t
)) - (sizeof (dns_wire_t
)));
259 memset(message
, 0, (sizeof (message_t
)) - (sizeof (dns_wire_t
)));
260 RETAIN_HERE(message
);
266 ioloop_comm_cancel(comm_t
*connection
)
268 if (connection
->connection
!= NULL
) {
269 connection_cancel(connection
->connection
);
274 message_finalize(message_t
*message
)
280 ioloop_message_retain_(message_t
*message
, const char *file
, int line
)
282 (void)file
; (void)line
;
287 ioloop_message_release_(message_t
*message
, const char *file
, int line
)
289 (void)file
; (void)line
;
290 RELEASE(message
, message_finalize
);
294 ioloop_send_message(comm_t
*connection
, message_t
*responding_to
, struct iovec
*iov
, int iov_len
)
296 dispatch_data_t data
= NULL
, new_data
, combined
;
300 // Not needed on OSX because UDP conversations are treated as "connections."
303 if (connection
->connection
== NULL
) {
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
);
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
);
321 dispatch_release(data
);
325 // First time through
329 ERROR("ioloop_send_message: no memory.");
336 dispatch_release(data
);
341 // TCP requires a length as well as the payload.
342 if (connection
->tcp_stream
) {
344 new_data
= dispatch_data_create(&len
, sizeof (len
), ioloop_main_queue
, DISPATCH_DATA_DESTRUCTOR_DEFAULT
);
345 if (new_data
== NULL
) {
347 dispatch_release(data
);
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
) {
361 if (connection
->pending_write
!= NULL
) {
362 ERROR("Dropping pending write on " PRI_S_SRP
, connection
->name
? connection
->name
: "<null>");
364 connection
->pending_write
= data
;
365 if (connection
->connection_ready
) {
366 return connection_write_now(connection
);
372 connection_write_now(comm_t
*connection
)
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
) {
381 ERROR("ioloop_send_message: write failed: " PUB_S_SRP
,
382 strerror(nw_error_get_error_code(error
)));
383 connection_cancel(connection
->connection
);
385 if (connection
->writes_pending
> 0) {
386 connection
->writes_pending
--;
387 RELEASE_HERE(connection
, comm_finalize
);
389 ERROR("ioloop_send_message: write callback reached with no writes marked pending.");
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
;
399 datagram_read(comm_t
*connection
, size_t length
, dispatch_data_t content
, nw_error_t error
)
401 message_t
*message
= NULL
;
402 bool ret
= true, *retp
= &ret
;
405 ERROR("datagram_read: " PUB_S_SRP
, strerror(nw_error_get_error_code(error
)));
409 if (length
> UINT16_MAX
) {
410 ERROR("datagram_read: oversized datagram length %zd", length
);
414 message
= message_create(length
);
415 if (message
== NULL
) {
416 ERROR("datagram_read: unable to allocate message.");
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
);
429 memcpy(((uint8_t *)&message
->wire
) + offset
, buffer
, size
);
433 // Process the message.
434 connection
->datagram_callback(connection
, message
, connection
->context
);
438 if (message
!= NULL
) {
439 ioloop_message_release(message
);
442 connection_cancel(connection
->connection
);
448 tcp_read(comm_t
*connection
, size_t length
, dispatch_data_t content
, nw_error_t error
)
451 connection_cancel(connection
->connection
);
454 if (datagram_read(connection
, length
, content
, error
)) {
455 // Wait for the next frame
456 tcp_start(connection
);
461 tcp_read_length(comm_t
*connection
, dispatch_data_t content
, nw_error_t error
)
464 uint32_t bytes_to_read
;
465 const uint8_t *lenbuf
;
469 ERROR("tcp_read_length: " PUB_S_SRP
, strerror(nw_error_get_error_code(error
)));
471 connection_cancel(connection
->connection
);
474 if (connection
->connection
== NULL
) {
477 if (content
== NULL
) {
478 INFO("tcp_read_length: remote end closed connection.");
482 map
= dispatch_data_create_map(content
, (const void **)&lenbuf
, &length
);
484 ERROR("tcp_read_length: map create failed");
486 } else if (length
!= 2) {
487 ERROR("tcp_read_length: invalid length = %zu", length
);
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
);
499 connection_idle_wakeup_callback(void *context
)
501 comm_t
*connection
= context
;
502 ERROR("Connection " PRI_S_SRP
" has gone idle", connection
->name
);
503 connection_cancel(connection
->connection
);
507 connection_idle_wakeup_finalize(void *context
)
509 comm_t
*connection
= context
;
510 connection
->idle_timer
= NULL
;
514 tcp_start(comm_t
*connection
)
516 if (connection
->connection
== NULL
) {
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
);
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
);
539 tcp_read_length(connection
, content
, error
);
545 udp_start(comm_t
*connection
)
547 if (connection
->connection
== NULL
) {
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
);
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)
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
) {
581 if (content
!= NULL
) {
582 proceed
= datagram_read(connection
, dispatch_data_get_size(content
),
585 if (content
== NULL
|| error
!= NULL
) {
586 connection_cancel(connection
->connection
);
588 // Once we have a five-tuple connection, we can't easily get rid of it, so keep
591 udp_start(connection
);
593 RELEASE_HERE(connection
, comm_finalize
);
598 connection_state_changed(comm_t
*connection
, nw_connection_state_t state
, nw_error_t 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
);
605 if (connection
->tcp_stream
) {
606 tcp_start(connection
);
608 udp_start(connection
);
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
);
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
);
625 INFO("connection_state_changed: " PRI_S_SRP
" state is %d; error = %p",
626 connection
->name
!= NULL
? connection
->name
: "<no name>", state
, error
);
631 connection_callback(comm_t
*listener
, nw_connection_t new_connection
)
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.
641 connection
->connection
= new_connection
;
642 nw_retain(connection
->connection
);
644 connection
->name
= nw_connection_copy_description(connection
->connection
);
645 if (connection
->name
!= NULL
) {
646 INFO("Received connection from " PRI_S_SRP
, connection
->name
);
648 ERROR("Unable to get description of new connection.");
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
);
666 listener_finalize(comm_t
*listener
)
668 if (listener
->listener
!= NULL
) {
669 nw_release(listener
->listener
);
670 listener
->listener
= NULL
;
672 if (listener
->name
!= NULL
) {
673 free(listener
->name
);
675 if (listener
->parameters
) {
676 nw_release(listener
->parameters
);
678 if (listener
->avoid_ports
!= NULL
) {
679 free(listener
->avoid_ports
);
681 if (listener
->finalize
) {
682 listener
->finalize(listener
->context
);
688 ioloop_listener_retain_(comm_t
*listener
, const char *file
, int line
)
694 ioloop_listener_release_(comm_t
*listener
, const char *file
, int line
)
696 RELEASE(listener
, listener_finalize
);
700 ioloop_listener_cancel(comm_t
*connection
)
702 if (connection
->listener
!= NULL
) {
703 nw_listener_cancel(connection
->listener
);
704 nw_release(connection
->listener
);
705 connection
->listener
= NULL
;
710 ioloop_listener_state_changed_handler(comm_t
*listener
, nw_listener_state_t state
, nw_error_t error
)
714 INFO("nw_listener_create:state changed: error");
716 if (state
== nw_listener_state_waiting
) {
717 INFO("nw_listener_create: waiting");
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
);
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
);
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.");
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
);
763 if (listener
->cancel
) {
764 listener
->cancel(listener
->context
);
766 RELEASE_HERE(listener
, listener_finalize
);
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
)
779 int family
= (ip_address
!= NULL
) ? ip_address
->sa
.sa_family
: AF_UNSPEC
;
782 nw_endpoint_t endpoint
;
784 if (ip_address
== NULL
) {
787 port
= (family
== AF_INET
) ? ip_address
->sin
.sin_port
: ip_address
->sin6
.sin6_port
;
790 if (multicast
!= NULL
) {
791 ERROR("ioloop_setup_listener: multicast not supported.");
795 if (datagram_callback
== NULL
) {
796 ERROR("ioloop_setup: no datagram callback provided.");
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
);
814 ERROR("No memory for listener on <family address other than AF_INET or AF_INET6: %d>#%d", family
, port
);
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
);
832 ERROR("No memory for listener avoid_ports on <family address other than AF_INET or AF_INET6: %d>#%d",
838 listener
->num_avoid_ports
= num_avoid_ports
;
839 listener
->avoiding
= true;
841 RETAIN_HERE(listener
);
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;
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");
854 // AF_INET6 or AF_UNSPEC
855 snprintf(ip_address_str
, sizeof(ip_address_str
), "::");
858 inet_ntop(family
, ip_address
->sa
.sa_data
, ip_address_str
, sizeof(ip_address_str
));
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
);
869 listener
->parameters
= nw_parameters_create_secure_tcp(tls
? NW_PARAMETERS_DEFAULT_CONFIGURATION
870 : NW_PARAMETERS_DISABLE_PROTOCOL
,
871 NW_PARAMETERS_DEFAULT_CONFIGURATION
);
874 ERROR("DTLS support not implemented.");
875 nw_release(endpoint
);
876 RELEASE_HERE(listener
, listener_finalize
);
879 listener
->parameters
= nw_parameters_create_secure_udp(NW_PARAMETERS_DISABLE_PROTOCOL
,
880 NW_PARAMETERS_DEFAULT_CONFIGURATION
);
882 if (listener
->parameters
== NULL
) {
883 ERROR("No memory for listener parameters.");
884 nw_release(endpoint
);
885 RELEASE_HERE(listener
, listener_finalize
);
889 if (endpoint
!= NULL
) {
890 nw_parameters_set_local_endpoint(listener
->parameters
, endpoint
);
891 nw_release(endpoint
);
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
);
901 // XXX set up the listener certificate(s).
902 // XXX how to configure this onto the parameters object?
906 nw_parameters_set_reuse_local_address(listener
->parameters
, true);
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
);
915 nw_listener_set_new_connection_handler(listener
->listener
,
916 ^(nw_connection_t connection
) { connection_callback(listener
, connection
); }
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
);
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
;
933 nw_listener_set_queue(listener
->listener
, ioloop_main_queue
);
934 nw_listener_start(listener
->listener
);
935 // Listener has one refcount
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
)
946 nw_parameters_t parameters
;
947 nw_endpoint_t endpoint
;
948 char addrbuf
[INET6_ADDRSTRLEN
];
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");
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
);
971 parameters
= nw_parameters_create_secure_tcp(tls
? NW_PARAMETERS_DEFAULT_CONFIGURATION
972 : NW_PARAMETERS_DISABLE_PROTOCOL
,
973 NW_PARAMETERS_DEFAULT_CONFIGURATION
);
976 ERROR("DTLS support not implemented.");
977 nw_release(endpoint
);
978 RELEASE_HERE(connection
, comm_finalize
);
981 parameters
= nw_parameters_create_secure_udp(NW_PARAMETERS_DISABLE_PROTOCOL
,
982 NW_PARAMETERS_DEFAULT_CONFIGURATION
);
984 if (parameters
== NULL
) {
985 ERROR("No memory for connection parameters.");
986 nw_release(endpoint
);
987 RELEASE_HERE(connection
, comm_finalize
);
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
);
999 // XXX set up the connection certificate(s).
1000 // XXX how to configure this onto the parameters object?
1004 connection
->name
= strdup(addrbuf
);
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
);
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
1028 RETAIN_HERE(connection
);
1029 nw_connection_start(connection
->connection
);
1034 subproc_finalize(subproc_t
*subproc
)
1037 for (i
= 0; i
< subproc
->argc
; i
++) {
1038 if (subproc
->argv
[i
] != NULL
) {
1039 free(subproc
->argv
[i
]);
1040 subproc
->argv
[i
] = NULL
;
1043 if (subproc
->dispatch_source
!= NULL
) {
1044 dispatch_release(subproc
->dispatch_source
);
1046 if (subproc
->output_fd
!= NULL
) {
1047 ioloop_file_descriptor_release(subproc
->output_fd
);
1049 if (subproc
->finalize
!= NULL
) {
1050 subproc
->finalize(subproc
->context
);
1055 static void subproc_cancel(void *context
)
1057 subproc_t
*subproc
= context
;
1058 subproc
->dispatch_source
= NULL
;
1059 RELEASE_HERE(subproc
, subproc_finalize
);
1063 subproc_event(void *context
)
1065 subproc_t
*subproc
= context
;
1069 pid
= waitpid(subproc
->pid
, &status
, WNOHANG
);
1073 subproc
->callback(subproc
, status
, NULL
);
1074 if (!WIFSTOPPED(status
)) {
1075 dispatch_source_cancel(subproc
->dispatch_source
);
1079 static void subproc_output_finalize(void *context
)
1081 subproc_t
*subproc
= context
;
1082 if (subproc
->output_fd
) {
1083 subproc
->output_fd
= NULL
;
1088 ioloop_subproc_release_(subproc_t
*subproc
, const char *file
, int line
)
1090 RELEASE(subproc
, subproc_finalize
);
1093 // Invoke the specified executable with the specified arguments. Call callback when it exits.
1094 // All failures are reported through the callback.
1096 ioloop_subproc(const char *exepath
, char *NULLABLE
*argv
, int argc
,
1097 subproc_callback_t callback
, io_callback_t output_callback
, void *context
)
1101 posix_spawn_file_actions_t actions
;
1102 posix_spawnattr_t attrs
;
1104 if (callback
== NULL
) {
1105 ERROR("ioloop_add_wake_event called with null callback");
1109 if (argc
> MAX_SUBPROC_ARGS
) {
1110 callback(NULL
, 0, "too many subproc args");
1114 subproc
= calloc(1, sizeof *subproc
);
1115 if (subproc
== NULL
) {
1116 callback(NULL
, 0, "out of memory");
1119 RETAIN_HERE(subproc
);
1120 if (output_callback
!= NULL
) {
1121 rv
= pipe(subproc
->pipe_fds
);
1123 callback(NULL
, 0, "unable to create pipe.");
1124 RELEASE_HERE(subproc
, subproc_finalize
);
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
);
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");
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");
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]);
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
);
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
);
1172 subproc
->callback
= callback
;
1173 subproc
->context
= context
;
1175 subproc
->dispatch_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC
, subproc
->pid
, DISPATCH_PROC_EXIT
,
1177 if (subproc
->dispatch_source
== NULL
) {
1178 ERROR("dispatch_source_create failed in ioloop_add_wake_event().");
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
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
);
1197 ioloop_dnssd_txn_cancel(dnssd_txn_t
*txn
)
1199 if (txn
->sdref
!= NULL
) {
1200 DNSServiceRefDeallocate(txn
->sdref
);
1203 INFO("ioloop_dnssd_txn_cancel: dead transaction.");
1208 dnssd_txn_finalize(dnssd_txn_t
*txn
)
1210 if (txn
->sdref
!= NULL
) {
1211 ioloop_dnssd_txn_cancel(txn
);
1213 if (txn
->finalize_callback
) {
1214 txn
->finalize_callback(txn
->context
);
1220 ioloop_dnssd_txn_retain_(dnssd_txn_t
*dnssd_txn
, const char *file
, int line
)
1222 (void)file
; (void)line
;
1227 ioloop_dnssd_txn_release_(dnssd_txn_t
*dnssd_txn
, const char *file
, int line
)
1229 (void)file
; (void)line
;
1230 RELEASE(dnssd_txn
, dnssd_txn_finalize
);
1234 ioloop_dnssd_txn_add_(DNSServiceRef ref
, void *context
, finalize_callback_t finalize_callback
, const char *file
,
1237 dnssd_txn_t
*txn
= calloc(1, sizeof(*txn
));
1238 (void)file
; (void)line
;
1243 txn
->context
= context
;
1244 txn
->finalize_callback
= finalize_callback
;
1245 DNSServiceSetDispatchQueue(ref
, ioloop_main_queue
);
1251 ioloop_dnssd_txn_set_aux_pointer(dnssd_txn_t
*NONNULL txn
, void *aux_pointer
)
1253 txn
->aux_pointer
= aux_pointer
;
1257 ioloop_dnssd_txn_get_aux_pointer(dnssd_txn_t
*NONNULL txn
)
1259 return txn
->aux_pointer
;
1263 ioloop_dnssd_txn_get_context(dnssd_txn_t
*NONNULL txn
)
1265 return txn
->context
;
1269 ioloop_xpc_client_is_entitled(xpc_connection_t conn
, const char *entitlement_name
)
1271 bool entitled
= false;
1272 xpc_object_t entitled_obj
= xpc_connection_copy_entitlement_value(conn
, entitlement_name
);
1275 if (xpc_get_type(entitled_obj
) == XPC_TYPE_BOOL
&& xpc_bool_get_value(entitled_obj
)) {
1278 xpc_release(entitled_obj
);
1280 ERROR("ioloop_xpc_client_is_entitled: Client Entitlement is NULL");
1284 ERROR("ioloop_xpc_client_is_entitled: Client is missing Entitlement!");
1291 ioloop_xpc_accept(xpc_connection_t conn
, const char *name
, ioloop_xpc_callback_t callback
)
1294 xpc_connection_t conn
;
1295 ioloop_xpc_callback_t callback
;
1299 ERROR("ioloop_xpc_accept: listener has been canceled.");
1303 state
= calloc(1, sizeof(*state
));
1304 if (state
== NULL
) {
1305 ERROR("ioloop_xpc_accept: no memory for xpc connection state.");
1309 int pid
= xpc_connection_get_pid(conn
);
1310 int uid
= xpc_connection_get_euid(conn
);
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
);
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
);
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
);
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
);
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
;
1345 INFO("ioloop_xpc_accept event handler: continuing.");
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
);
1355 state
->callback
= NULL
;
1358 xpc_connection_resume(conn
);
1362 ioloop_create_xpc_service(const char *name
, ioloop_xpc_callback_t callback
)
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
);
1374 xpc_connection_set_event_handler(listener
, ^(xpc_object_t eventmsg
) {
1375 xpc_type_t type
= xpc_get_type(eventmsg
);
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
);
1381 else if (type
== XPC_TYPE_ERROR
) // Ideally, we would never hit these cases
1383 ERROR("ioloop_create_xpc_service: XPCError: " PUB_S_SRP
,
1384 xpc_dictionary_get_string(eventmsg
, XPC_ERROR_KEY_DESCRIPTION
));
1385 callback(NULL
, NULL
);
1389 INFO("ioloop_create_xpc_service: Unknown EventMsg type");
1392 xpc_connection_resume(listener
);
1397 file_descriptor_finalize(void *context
)
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
);
1404 free(file_descriptor
);
1409 ioloop_file_descriptor_retain_(io_t
*file_descriptor
, const char *file
, int line
)
1411 (void)file
; (void)line
;
1412 RETAIN(file_descriptor
);
1416 ioloop_file_descriptor_release_(io_t
*file_descriptor
, const char *file
, int line
)
1418 (void)file
; (void)line
;
1419 RELEASE(file_descriptor
, file_descriptor_finalize
);
1423 ioloop_file_descriptor_create_(int fd
, void *context
, finalize_callback_t finalize
, const char *file
, int line
)
1426 ret
= calloc(1, sizeof(*ret
));
1429 ret
->context
= context
;
1430 ret
->finalize
= finalize
;
1437 ioloop_read_cancel(void *context
)
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
);
1450 ioloop_read_event(void *context
)
1454 if (io
->read_callback
!= NULL
) {
1455 io
->read_callback(io
, io
->context
);
1460 ioloop_close(io_t
*io
)
1462 if (io
->read_source
!= NULL
) {
1463 dispatch_cancel(io
->read_source
);
1465 if (io
->write_source
!= NULL
) {
1466 dispatch_cancel(io
->write_source
);
1472 ioloop_add_reader(io_t
*NONNULL io
, io_callback_t NONNULL callback
)
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
);
1478 if (io
->read_source
== NULL
) {
1479 ERROR("dispatch_source_create: unable to create read dispatch source.");
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
);
1492 // c-file-style: "bsd"
1493 // c-basic-offset: 4
1495 // indent-tabs-mode: nil