1 /* ioloop.h 2 * 3 * Copyright (c) 2018-2024 Apple 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 * https://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 * Definitions for simple dispatch implementation. 18 */ 19 20 #ifndef __IOLOOP_H 21 #define __IOLOOP_H 22 23 24 #include <sys/socket.h> 25 #include <netinet/in.h> 26 #include <sys/un.h> 27 28 #include <dns_sd.h> 29 30 #ifndef NSEC_PER_SEC 31 #define NSEC_PER_SEC 1000000000ull 32 #endif 33 #ifndef NSEC_PER_MSEC 34 #define NSEC_PER_MSEC 1000000ull 35 #endif 36 37 #ifndef MSEC_PER_SEC 38 #define MSEC_PER_SEC (NSEC_PER_SEC / NSEC_PER_MSEC) 39 #endif 40 41 #ifndef IN_LINKLOCAL 42 #define IN_LINKLOCAL(x) (((uint32_t)(x) & 0xffff0000) == 0xA9FE0000) // 169.254.* 43 #endif 44 #ifndef IN_LOOPBACK 45 #define IN_LOOPBACK(x) (((uint32_t)(x) & 0xff000000) == 0x7f000000) // 127.* 46 #endif 47 48 #ifndef UDP_LISTENER_USES_CONNECTION_GROUPS 49 #define UDP_LISTENER_USES_CONNECTION_GROUPS 0 50 #endif 51 52 #ifndef __DSO_H 53 typedef struct dso_state dso_state_t; 54 #endif 55 56 typedef union addr addr_t; 57 union addr { 58 struct sockaddr sa; 59 struct sockaddr_in sin; 60 struct sockaddr_in6 sin6; 61 struct { 62 char len; 63 char family; 64 int index; 65 uint8_t addr[8]; 66 } ether_addr; 67 }; 68 69 #define IOLOOP_NTOP(addr, buf) \ 70 (((addr)->sa.sa_family == AF_INET || (addr)->sa.sa_family == AF_INET6) \ 71 ? (inet_ntop((addr)->sa.sa_family, ((addr)->sa.sa_family == AF_INET \ 72 ? (void *)&(addr)->sin.sin_addr \ 73 : (void *)&(addr)->sin6.sin6_addr), buf, sizeof buf) != NULL) \ 74 : snprintf(buf, sizeof buf, "Address type %d", (addr)->sa.sa_family)) 75 76 struct message { 77 int ref_count; 78 #if !defined(IOLOOP_MACOS) || !UDP_LISTENER_USES_CONNECTION_GROUPS 79 addr_t src; 80 addr_t local; 81 #endif 82 int ifindex; 83 uint16_t length; 84 time_t received_time; // Only for SRP Replication, zero otherwise. 85 uint32_t lease, key_lease; // For SRP replication, leases agreed to by original registrar 86 dns_wire_t wire; 87 }; 88 89 90 typedef struct dso_transport comm_t; 91 typedef struct io io_t; 92 typedef struct subproc subproc_t; 93 typedef struct wakeup wakeup_t; 94 typedef struct dnssd_txn dnssd_txn_t; 95 typedef struct interface_address_state interface_address_state_t; 96 typedef struct ifpermit_list ifpermit_list_t; 97 98 typedef void (*dnssd_txn_finalize_callback_t)(void *NONNULL context); 99 typedef void (*dnssd_txn_failure_callback_t)(void *NONNULL context, int status); 100 typedef void (*wakeup_callback_t)(void *NONNULL context); 101 typedef void (*finalize_callback_t)(void *NONNULL context); 102 typedef void (*cancel_callback_t)(comm_t *NONNULL comm, void *NONNULL context); 103 typedef void (*ready_callback_t)(void *NONNULL context, uint16_t port); 104 typedef void (*io_callback_t)(io_t *NONNULL io, void *NONNULL context); 105 typedef void (*comm_callback_t)(comm_t *NONNULL comm); 106 typedef void (*datagram_callback_t)(comm_t *NONNULL comm, message_t *NONNULL message, void *NULLABLE context); 107 typedef void (*connect_callback_t)(comm_t *NONNULL connection, void *NULLABLE context); 108 typedef void (*disconnect_callback_t)(comm_t *NONNULL comm, void *NULLABLE context, int error); 109 enum interface_address_change { interface_address_added, interface_address_deleted, interface_address_unchanged }; 110 typedef struct srp_server_state srp_server_t; 111 typedef void (*interface_callback_t)(srp_server_t *NULLABLE server_state, void *NULLABLE context, 112 const char *NONNULL name, const addr_t *NONNULL address, 113 const addr_t *NONNULL netmask, uint32_t flags, 114 enum interface_address_change event_type); 115 typedef void (*subproc_callback_t)(void *NULLABLE context, int status, const char *NULLABLE error); 116 typedef void (*tls_config_callback_t)(void *NONNULL context); 117 typedef void (*async_callback_t)(void *NULLABLE context); 118 119 typedef struct tls_context tls_context_t; 120 121 #define IOLOOP_SECOND 1000LL 122 #define IOLOOP_MINUTE 60 * IOLOOP_SECOND 123 #define IOLOOP_HOUR 60 * IOLOOP_MINUTE 124 #define IOLOOP_DAY 24 * IOLOOP_HOUR 125 126 struct interface_address_state { 127 interface_address_state_t *NULLABLE next; 128 char *NONNULL name; 129 addr_t addr; 130 addr_t mask; 131 uint32_t flags; 132 }; 133 134 struct io { 135 int ref_count; 136 io_t *NULLABLE next; 137 io_callback_t NULLABLE read_callback; 138 io_callback_t NULLABLE write_callback; 139 finalize_callback_t NULLABLE finalize; 140 finalize_callback_t NULLABLE io_finalize; 141 finalize_callback_t NULLABLE context_release; 142 void *NULLABLE context; 143 io_t *NULLABLE cancel_on_close; 144 io_callback_t NULLABLE ready; 145 bool want_read : 1; 146 bool want_write : 1; 147 int fd; 148 }; 149 150 struct wakeup { 151 int ref_count; 152 wakeup_t *NULLABLE next; 153 void *NULLABLE context; 154 wakeup_callback_t NULLABLE wakeup; 155 finalize_callback_t NULLABLE finalize; 156 #ifdef IOLOOP_MACOS 157 dispatch_source_t NULLABLE dispatch_source; 158 #else 159 int64_t wakeup_time; 160 #endif 161 }; 162 163 struct dso_transport { 164 #ifdef IOLOOP_MACOS 165 nw_connection_t NULLABLE connection; 166 nw_listener_t NULLABLE listener; 167 #if UDP_LISTENER_USES_CONNECTION_GROUPS 168 nw_connection_group_t NULLABLE connection_group; 169 nw_content_context_t NULLABLE content_context; 170 #endif 171 nw_parameters_t NULLABLE parameters; 172 comm_t *NULLABLE listener_state; 173 int ref_count; 174 int writes_pending; 175 wakeup_t *NULLABLE idle_timer; 176 // nw_connection objects aren't necessarily ready to write to immediately. But when we create an outgoing connection, we 177 // typically want to write to it immediately. So we have a one-datum queue in case this happens; if the connection takes 178 // so long to get ready that another write happens, we drop the first write. This will work okay for UDP connections, where 179 // the retransmit logic is in the application. For future, we may want to rearchitect the flow so that the write is always 180 // done in a callback. 181 dispatch_data_t NULLABLE pending_write; 182 #if !UDP_LISTENER_USES_CONNECTION_GROUPS 183 io_t io; 184 message_t *NULLABLE message; 185 #endif 186 #else 187 io_t io; 188 message_t *NULLABLE message; 189 int multicast_ifindex; 190 #endif 191 uint16_t listen_port; 192 uint16_t *NULLABLE avoid_ports; 193 int num_avoid_ports; 194 int serial; 195 bool avoiding; 196 char *NONNULL name; 197 void *NULLABLE context; 198 #ifdef SRP_TEST_SERVER 199 void *NULLABLE test_context; 200 bool (*NULLABLE test_send_intercept)(comm_t *NONNULL connection, message_t *NULLABLE responding_to, 201 struct iovec *NONNULL iov, int iov_len, bool final, bool send_length); 202 void *NULLABLE srp_server; 203 #endif 204 datagram_callback_t NULLABLE datagram_callback; 205 comm_callback_t NULLABLE close_callback; 206 connect_callback_t NULLABLE connected; 207 disconnect_callback_t NULLABLE disconnected; 208 finalize_callback_t NULLABLE finalize; 209 cancel_callback_t NULLABLE cancel; 210 ready_callback_t NULLABLE ready; 211 uint8_t *NULLABLE buf; 212 dso_state_t *NULLABLE dso; 213 tls_context_t *NULLABLE tls_context; 214 addr_t address, multicast, local; 215 size_t message_length_len; 216 size_t message_length, message_cur; 217 uint8_t message_length_bytes[2]; 218 219 220 #ifdef IOLOOP_MACOS 221 bool read_pending: 1; // Only ever one. 222 bool server: 1; // Indicates that this connection was created by a listener 223 bool connection_ready: 1; 224 bool final_data : 1; // Indicates that the next message written will be the final message, so send a FIN. 225 #else 226 bool tls_handshake_incomplete: 1; 227 #endif // IOLOOP_MACOS 228 bool tls_rotation_ready: 1; // Indicates if the listener should rotate its TLS certificate. 229 bool tcp_stream: 1; 230 bool is_multicast: 1; 231 bool is_connected: 1; 232 bool is_listener: 1; 233 bool opportunistic: 1; 234 bool canceled: 1; 235 }; 236 237 #define MAX_SUBPROC_ARGS 20 238 struct subproc { 239 int ref_count; 240 #ifdef IOLOOP_MACOS 241 dispatch_source_t NULLABLE dispatch_source; 242 #else 243 subproc_t *NULLABLE next; 244 #endif 245 int pipe_fds[2]; 246 io_t *NULLABLE output_fd; 247 void *NULLABLE context; 248 subproc_callback_t NONNULL callback; 249 finalize_callback_t NULLABLE finalize; 250 char *NULLABLE argv[MAX_SUBPROC_ARGS + 1]; 251 int argc; 252 pid_t pid; 253 bool finished : 1; 254 }; 255 256 struct dnssd_txn { 257 #ifndef IOLOOP_MACOS 258 io_t *NULLABLE io; 259 #endif 260 int ref_count; 261 DNSServiceRef NULLABLE sdref; 262 void *NULLABLE context; 263 void *NULLABLE aux_pointer; 264 dnssd_txn_finalize_callback_t NULLABLE finalize_callback; 265 dnssd_txn_failure_callback_t NULLABLE failure_callback; 266 }; 267 268 extern int64_t ioloop_now; 269 int getipaddr(addr_t *NONNULL addr, const char *NONNULL p); 270 int64_t ioloop_timenow(void); 271 message_t *NULLABLE message_allocate(size_t message_size); 272 void message_free(message_t *NONNULL message); 273 void ioloop_close(io_t *NONNULL io); 274 void ioloop_add_reader(io_t *NONNULL io, io_callback_t NONNULL callback); 275 #define ioloop_wakeup_create() ioloop_wakeup_create_(__FILE__, __LINE__) 276 wakeup_t *NULLABLE ioloop_wakeup_create_(const char *NONNULL file, int line); 277 #define ioloop_wakeup_retain(wakeup) ioloop_wakeup_retain_(wakeup, __FILE__, __LINE__) 278 void ioloop_wakeup_retain_(wakeup_t *NONNULL wakeup, const char *NONNULL file, int line); 279 #define ioloop_wakeup_release(wakeup) ioloop_wakeup_release_(wakeup, __FILE__, __LINE__) 280 #ifndef ioloop_wakeup_forget 281 #define ioloop_wakeup_forget(ptr) _SRP_STRICT_DISPOSE_TEMPLATE(ptr, ioloop_wakeup_release) 282 #endif 283 void ioloop_wakeup_release_(wakeup_t *NONNULL wakeup, const char *NONNULL file, int line); 284 bool ioloop_add_wake_event(wakeup_t *NONNULL wakeup, void *NULLABLE context, 285 wakeup_callback_t NONNULL callback, finalize_callback_t NULLABLE finalize, 286 int32_t milliseconds); 287 void ioloop_cancel_wake_event(wakeup_t *NONNULL wakeup); 288 289 bool ioloop_init(void); 290 int ioloop(void); 291 292 #define ioloop_comm_retain(comm) ioloop_comm_retain_(comm, __FILE__, __LINE__) 293 void ioloop_comm_retain_(comm_t *NONNULL comm, const char *NONNULL file, int line); 294 #define ioloop_comm_release(wakeup) ioloop_comm_release_(wakeup, __FILE__, __LINE__) 295 void ioloop_comm_release_(comm_t *NONNULL comm, const char *NONNULL file, int line); 296 void ioloop_comm_context_set(comm_t *NONNULL connection, 297 void *NULLABLE context, finalize_callback_t NULLABLE callback); 298 void ioloop_comm_connect_callback_set(comm_t *NONNULL comm, connect_callback_t NULLABLE callback); 299 void ioloop_comm_disconnect_callback_set(comm_t *NONNULL comm, disconnect_callback_t NULLABLE callback); 300 void ioloop_comm_cancel(comm_t *NONNULL comm); 301 #define ioloop_listener_retain(comm) ioloop_listener_retain_(comm, __FILE__, __LINE__) 302 void ioloop_listener_retain_(comm_t *NONNULL listener, const char *NONNULL file, int line); 303 #define ioloop_listener_release(wakeup) ioloop_listener_release_(wakeup, __FILE__, __LINE__) 304 void ioloop_listener_release_(comm_t *NONNULL listener, const char *NONNULL file, int line); 305 void ioloop_listener_cancel(comm_t *NONNULL comm); 306 comm_t *NULLABLE ioloop_listener_create(bool stream, bool tls, bool init_daemon, uint16_t *NULLABLE avoid_ports, 307 int num_avoid_ports, const addr_t *NULLABLE ip_address, 308 const char *NULLABLE multicast, const char *NONNULL name, 309 datagram_callback_t NONNULL datagram_callback, 310 connect_callback_t NULLABLE connected, cancel_callback_t NULLABLE cancel, 311 ready_callback_t NULLABLE ready, finalize_callback_t NULLABLE finalize, 312 tls_config_callback_t NULLABLE tls_config, unsigned ifindex, 313 void *NULLABLE context); 314 comm_t *NULLABLE ioloop_connection_create(addr_t *NONNULL remote_address, bool tls, bool stream, bool stable, 315 bool opportunistic, datagram_callback_t NONNULL datagram_callback, 316 connect_callback_t NULLABLE connected, 317 disconnect_callback_t NULLABLE disconnected, 318 finalize_callback_t NULLABLE finalize, 319 void *NULLABLE context); 320 #define ioloop_message_create(x) ioloop_message_create_(x, __FILE__, __LINE__) 321 message_t *NULLABLE ioloop_message_create_(size_t message_size, const char *NONNULL file, int line); 322 #define ioloop_message_retain(wakeup) ioloop_message_retain_(wakeup, __FILE__, __LINE__) 323 void ioloop_message_retain_(message_t *NONNULL message, const char *NONNULL file, int line); 324 #define ioloop_message_release(wakeup) ioloop_message_release_(wakeup, __FILE__, __LINE__) 325 void ioloop_message_release_(message_t *NONNULL message, const char *NONNULL file, int line); 326 bool ioloop_send_multicast(comm_t *NONNULL comm, int ifindex, struct iovec *NONNULL iov, int iov_len); 327 bool ioloop_send_message(comm_t *NONNULL connection, message_t *NULLABLE responding_to, 328 struct iovec *NONNULL iov, int iov_len); 329 bool ioloop_send_final_message(comm_t *NONNULL connection, message_t *NULLABLE responding_to, 330 struct iovec *NONNULL iov, int iov_len); 331 bool ioloop_send_data(comm_t *NONNULL connection, message_t *NULLABLE responding_to, 332 struct iovec *NONNULL iov, int iov_len); 333 bool ioloop_send_final_data(comm_t *NONNULL connection, message_t *NULLABLE responding_to, 334 struct iovec *NONNULL iov, int iov_len); 335 void ioloop_dump_object_allocation_stats(void); 336 void ioloop_strcpy(char *NONNULL dest, const char *NONNULL src, size_t lim); 337 bool ioloop_map_interface_addresses(srp_server_t *NULLABLE server_state, 338 const char *NULLABLE ifname, void *NULLABLE context, interface_callback_t NULLABLE callback); 339 #define ioloop_map_interface_addresses_here(server_state, here, ifname, context, callback) \ 340 ioloop_map_interface_addresses_here_(server_state, here, ifname, context, callback, __FILE__, __LINE__) 341 bool ioloop_map_interface_addresses_here_(srp_server_t *NULLABLE server_state, 342 interface_address_state_t *NONNULL *NULLABLE here, 343 const char *NULLABLE ifname, 344 void *NULLABLE context, interface_callback_t NULLABLE callback, 345 const char *NONNULL file, int line); 346 ssize_t ioloop_recvmsg(int sock, uint8_t *NONNULL buffer, size_t buffer_length, int *NONNULL ifindex, 347 int *NONNULL hoplimit, addr_t *NONNULL source, addr_t *NONNULL destination); 348 #define ioloop_subproc_release(subproc) ioloop_subproc_release_(subproc, __FILE__, __LINE__) 349 void ioloop_subproc_release_(subproc_t *NONNULL subproc, const char *NONNULL file, int line); 350 #define ioloop_subproc_retain(subproc) ioloop_subproc_retain_(subproc, __FILE__, __LINE__) 351 void ioloop_subproc_retain_(subproc_t *NONNULL subproc, const char *NONNULL file, int line); 352 subproc_t *NULLABLE ioloop_subproc(const char *NONNULL exepath, char *NULLABLE *NONNULL argv, int argc, 353 subproc_callback_t NULLABLE callback, io_callback_t NULLABLE output_callback, 354 void *NULLABLE context); 355 void ioloop_subproc_run_sync(subproc_t *NONNULL subproc); 356 #define ioloop_dnssd_txn_add(ref, context, finalize_callback, failure_callback) \ 357 ioloop_dnssd_txn_add_(ref, context, finalize_callback, failure_callback, __FILE__, __LINE__) 358 dnssd_txn_t *NULLABLE 359 ioloop_dnssd_txn_add_(DNSServiceRef NONNULL ref, void *NULLABLE context, 360 dnssd_txn_finalize_callback_t NULLABLE callback, 361 dnssd_txn_failure_callback_t NULLABLE failure_callback, const char *NONNULL file, int line); 362 #define ioloop_dnssd_txn_add_subordinate(ref, context, finalize_callback, failure_callback) \ 363 ioloop_dnssd_txn_add_subordinate_(ref, context, finalize_callback, failure_callback, __FILE__, __LINE__) 364 dnssd_txn_t *NULLABLE 365 ioloop_dnssd_txn_add_subordinate_(DNSServiceRef NONNULL ref, void *NULLABLE context, 366 dnssd_txn_finalize_callback_t NULLABLE callback, 367 dnssd_txn_failure_callback_t NULLABLE failure_callback, const char *NONNULL file, int line); 368 void ioloop_dnssd_txn_cancel(dnssd_txn_t *NONNULL txn); 369 #define ioloop_dnssd_txn_retain(txn) ioloop_dnssd_txn_retain_(txn, __FILE__, __LINE__) 370 void ioloop_dnssd_txn_retain_(dnssd_txn_t *NONNULL txn, const char *NONNULL file, int line); 371 #define ioloop_dnssd_txn_release(txn) ioloop_dnssd_txn_release_(txn, __FILE__, __LINE__) 372 #ifndef ioloop_dnssd_txn_forget 373 #define ioloop_dnssd_txn_forget(ptr) _SRP_STRICT_DISPOSE_TEMPLATE(ptr, ioloop_dnssd_txn_release) 374 #endif 375 void ioloop_dnssd_txn_release_(dnssd_txn_t *NONNULL txn, const char *NONNULL file, int line); 376 #endif 377 void ioloop_dnssd_txn_set_aux_pointer(dnssd_txn_t *NONNULL txn, void *NULLABLE aux_pointer); 378 void *NULLABLE ioloop_dnssd_txn_get_aux_pointer(dnssd_txn_t *NONNULL txn); 379 void *NULLABLE ioloop_dnssd_txn_get_context(dnssd_txn_t *NONNULL txn); 380 381 #define ioloop_file_descriptor_create(fd, context, finalize) \ 382 ioloop_file_descriptor_create_(fd, context, finalize, __FILE__, __LINE__) 383 io_t *NULLABLE ioloop_file_descriptor_create_(int fd, void *NULLABLE context, finalize_callback_t NULLABLE finalize, 384 const char *NONNULL file, int line); 385 #define ioloop_file_descriptor_retain(file_descriptor) ioloop_file_descriptor_retain_(file_descriptor, __FILE__, \ 386 __LINE__) 387 void ioloop_file_descriptor_retain_(io_t *NONNULL file_descriptor, const char *NONNULL file, int line); 388 #define ioloop_file_descriptor_release(file_descriptor) ioloop_file_descriptor_release_(file_descriptor, __FILE__, \ 389 __LINE__) 390 void ioloop_file_descriptor_release_(io_t *NONNULL file_descriptor, const char *NONNULL file, int line); 391 392 bool ioloop_interface_monitor_start(void); 393 void ioloop_run_async(async_callback_t NULLABLE callback, void *NULLABLE context); 394 395 396 bool srp_load_file_data(void *NULLABLE host_context, const char *NONNULL filename, uint8_t *NONNULL buffer, 397 uint16_t *NONNULL length, uint16_t buffer_size); 398 bool srp_store_file_data(void *NULLABLE host_context, const char *NONNULL filename, uint8_t *NONNULL buffer, 399 uint16_t length); 400 time_t srp_time(void); 401 double srp_fractional_time(void); 402 int64_t srp_utime(void); 403 void srp_format_time_offset(char *NONNULL buf, size_t buf_len, time_t offset); 404 405 const struct sockaddr *NULLABLE connection_get_local_address(message_t *NULLABLE message); 406 407 #if !UDP_LISTENER_USES_CONNECTION_GROUPS 408 bool ioloop_udp_send_message(comm_t *NONNULL comm, addr_t *NULLABLE source, addr_t *NONNULL dest, int ifindex, 409 struct iovec *NONNULL iov, int iov_len); 410 void ioloop_udp_read_callback(io_t *NONNULL io, void *NULLABLE context); 411 #endif 412 int get_num_fds(void); 413 414 415 // Local Variables: 416 // mode: C 417 // tab-width: 4 418 // c-file-style: "bsd" 419 // c-basic-offset: 4 420 // fill-column: 108 421 // indent-tabs-mode: nil 422 // End: 423