1 /* ioloop.h
2  *
3  * Copyright (c) 2018-2019 Apple Computer, Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * Definitions for simple dispatch implementation.
18  */
19 
20 #ifndef __IOLOOP_H
21 #define __IOLOOP_H
22 
23 #ifdef IOLOOP_MACOS
24 #include <nw/private.h>
25 #include <Network/Network.h>
26 #include <xpc/xpc.h>
27 #include <xpc/private.h>
28 #endif
29 
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <net/if_dl.h>
33 
34 #ifndef __DSO_H
35 typedef struct dso_state dso_state_t;
36 #endif
37 
38 typedef union addr addr_t;
39 union addr {
40     struct sockaddr sa;
41     struct sockaddr_in sin;
42     struct sockaddr_in6 sin6;
43     struct {
44         char len;
45         char family;
46         int index;
47         uint8_t addr[8];
48     } ether_addr;
49 };
50 
51 #define IOLOOP_NTOP(addr, buf) \
52     (((addr)->sa.sa_family == AF_INET || (addr)->sa.sa_family == AF_INET6) \
53      ? (inet_ntop((addr)->sa.sa_family, ((addr)->sa.sa_family == AF_INET \
54                                         ? (void *)&(addr)->sin.sin_addr \
55                                         : (void *)&(addr)->sin6.sin6_addr), buf, sizeof buf) != NULL) \
56     : snprintf(buf, sizeof buf, "Address type %d", (addr)->sa.sa_family))
57 
58 struct message {
59     int ref_count;
60 #ifndef IOLOOP_MACOS
61     addr_t src;
62     addr_t local;
63 #endif
64     int ifindex;
65     uint16_t length;
66     dns_wire_t wire;
67 };
68 
69 
70 typedef struct dso_transport comm_t;
71 typedef struct io io_t;
72 typedef struct subproc subproc_t;
73 typedef struct wakeup wakeup_t;
74 typedef struct dnssd_txn dnssd_txn_t;
75 
76 typedef void (*dnssd_txn_finalize_callback_t)(void *NONNULL context);
77 typedef void (*wakeup_callback_t)(void *NONNULL context);
78 typedef void (*finalize_callback_t)(void *NONNULL context);
79 typedef void (*cancel_callback_t)(void *NONNULL context);
80 typedef void (*ready_callback_t)(void *NONNULL context, uint16_t port);
81 typedef void (*io_callback_t)(io_t *NONNULL io, void *NONNULL context);
82 typedef void (*comm_callback_t)(comm_t *NONNULL comm);
83 typedef void (*datagram_callback_t)(comm_t *NONNULL comm, message_t *NONNULL message, void *NULLABLE context);
84 typedef void (*connect_callback_t)(comm_t *NONNULL connection, void *NULLABLE context);
85 typedef void (*disconnect_callback_t)(comm_t *NONNULL comm, int error);
86 enum interface_address_change { interface_address_added, interface_address_deleted, interface_address_unchanged };
87 typedef void (*interface_callback_t)(void *NULLABLE context, const char *NONNULL name,
88                                      const addr_t *NONNULL address, const addr_t *NONNULL netmask,
89                                      uint32_t flags, enum interface_address_change event_type);
90 typedef void (*subproc_callback_t)(void *NULLABLE context, int status, const char *NULLABLE error);
91 #ifdef IOLOOP_MACOS
92 typedef bool (*ioloop_xpc_callback_t)(xpc_connection_t NULLABLE conn, xpc_object_t NULLABLE request);
93 #endif
94 
95 typedef struct tls_context tls_context_t;
96 
97 #define IOLOOP_SECOND   1000LL
98 #define IOLOOP_MINUTE   60 * IOLOOP_SECOND
99 #define IOLOOP_HOUR     60 * IOLOOP_MINUTE
100 #define IOLOOP_DAY      24 * IOLOOP_HOUR
101 
102 struct io {
103     int ref_count;
104     io_t *NULLABLE next;
105     io_callback_t NULLABLE read_callback;
106     io_callback_t NULLABLE write_callback;
107     finalize_callback_t NULLABLE finalize;
108     void *NULLABLE context;
109     io_t *NULLABLE cancel_on_close;
110 #ifdef IOLOOP_MACOS
111     dispatch_source_t NULLABLE read_source;
112     dispatch_source_t NULLABLE write_source;
113 #else
114     bool want_read : 1;
115     bool want_write : 1;
116 #endif
117     int fd;
118 };
119 
120 struct wakeup {
121     int ref_count;
122     wakeup_t *NULLABLE next;
123     void *NULLABLE context;
124     wakeup_callback_t NULLABLE wakeup;
125     finalize_callback_t NULLABLE finalize;
126 #ifdef IOLOOP_MACOS
127     dispatch_source_t NULLABLE dispatch_source;
128 #else
129     int64_t wakeup_time;
130 #endif
131 };
132 
133 struct dso_transport {
134     int ref_count;
135 #ifdef IOLOOP_MACOS
136     nw_connection_t NULLABLE connection;
137     nw_listener_t NULLABLE listener;
138     nw_parameters_t NULLABLE parameters;
139     int writes_pending;
140     bool read_pending; // Only ever one.
141     bool server;       // Indicates that this connection was created by a listener
142     bool connection_ready;
143     wakeup_t *NULLABLE idle_timer;
144     // nw_connection objects aren't necessarily ready to write to immediately. But when we create an outgoing connection, we
145     // typically want to write to it immediately. So we have a one-datum queue in case this happens; if the connection takes
146     // so long to get ready that another write happens, we drop the first write. This will work okay for UDP connections, where
147     // the retransmit logic is in the application. For future, we may want to rearchitect the flow so that the write is always
148     // done in a callback.
149     dispatch_data_t NULLABLE pending_write;
150 #else
151     io_t io;
152 #endif
153     uint16_t listen_port;
154     uint16_t *NULLABLE avoid_ports;
155     int num_avoid_ports;
156     bool avoiding;
157     char *NONNULL name;
158     void *NULLABLE context;
159     datagram_callback_t NULLABLE datagram_callback;
160     comm_callback_t NULLABLE close_callback;
161     connect_callback_t NULLABLE connected;
162     disconnect_callback_t NULLABLE disconnected;
163     finalize_callback_t NULLABLE finalize;
164     cancel_callback_t NULLABLE cancel;
165     ready_callback_t NULLABLE ready;
166     message_t *NULLABLE message;
167     uint8_t *NULLABLE buf;
168     dso_state_t *NULLABLE dso;
169     tls_context_t *NULLABLE tls_context;
170     addr_t address, multicast;
171     size_t message_length_len;
172     size_t message_length, message_cur;
173     uint8_t message_length_bytes[2];
174     bool tcp_stream: 1;
175     bool is_multicast: 1;
176 };
177 
178 #define MAX_SUBPROC_ARGS 20
179 struct subproc {
180     int ref_count;
181 #ifdef IOLOOP_MACOS
182     dispatch_source_t NULLABLE dispatch_source;
183 #else
184     subproc_t *NULLABLE next;
185 #endif
186     int pipe_fds[2];
187     io_t *NULLABLE output_fd;
188     void *NULLABLE context;
189     subproc_callback_t NONNULL callback;
190     finalize_callback_t NULLABLE finalize;
191     char *NULLABLE argv[MAX_SUBPROC_ARGS + 1];
192     int argc;
193     pid_t pid;
194 };
195 
196 struct dnssd_txn {
197     int ref_count;
198     DNSServiceRef NULLABLE sdref;
199     void *NULLABLE context;
200     void *NULLABLE aux_pointer;
201     dnssd_txn_finalize_callback_t NULLABLE finalize_callback;
202 };
203 
204 extern int64_t ioloop_now;
205 int getipaddr(addr_t *NONNULL addr, const char *NONNULL p);
206 int64_t ioloop_timenow(void);
207 message_t *NULLABLE message_allocate(size_t message_size);
208 void message_free(message_t *NONNULL message);
209 void ioloop_close(io_t *NONNULL io);
210 void ioloop_add_reader(io_t *NONNULL io, io_callback_t NONNULL callback);
211 wakeup_t *NULLABLE ioloop_wakeup_create(void);
212 #define ioloop_wakeup_retain(wakeup) ioloop_wakeup_retain_(wakeup, __FILE__, __LINE__)
213 void ioloop_wakeup_retain_(wakeup_t *NONNULL wakeup, const char *NONNULL file, int line);
214 #define ioloop_wakeup_release(wakeup) ioloop_wakeup_release_(wakeup, __FILE__, __LINE__)
215 void ioloop_wakeup_release_(wakeup_t *NONNULL wakeup, const char *NONNULL file, int line);
216 bool ioloop_add_wake_event(wakeup_t *NONNULL wakeup, void *NULLABLE context,
217                            wakeup_callback_t NONNULL callback, finalize_callback_t NULLABLE finalize,
218                            int milliseconds);
219 void ioloop_cancel_wake_event(wakeup_t *NONNULL wakeup);
220 
221 bool ioloop_init(void);
222 int ioloop(void);
223 
224 #define ioloop_comm_retain(comm) ioloop_comm_retain_(comm, __FILE__, __LINE__)
225 void ioloop_comm_retain_(comm_t *NONNULL comm, const char *NONNULL file, int line);
226 #define ioloop_comm_release(wakeup) ioloop_comm_release_(wakeup, __FILE__, __LINE__)
227 void ioloop_comm_release_(comm_t *NONNULL comm, const char *NONNULL file, int line);
228 void ioloop_comm_cancel(comm_t *NONNULL comm);
229 #define ioloop_listener_retain(comm) ioloop_listener_retain_(comm, __FILE__, __LINE__)
230 void ioloop_listener_retain_(comm_t *NONNULL listener, const char *NONNULL file, int line);
231 #define ioloop_listener_release(wakeup) ioloop_listener_release_(wakeup, __FILE__, __LINE__)
232 void ioloop_listener_release_(comm_t *NONNULL listener, const char *NONNULL file, int line);
233 void ioloop_listener_cancel(comm_t *NONNULL comm);
234 comm_t *NULLABLE ioloop_listener_create(bool stream, bool tls, uint16_t *NULLABLE avoid_ports, int num_avoid_ports,
235                                         const addr_t *NULLABLE ip_address, const char *NULLABLE multicast,
236                                         const char *NONNULL name, datagram_callback_t NONNULL datagram_callback,
237                                         connect_callback_t NULLABLE connected, cancel_callback_t NULLABLE cancel,
238                                         ready_callback_t NULLABLE ready, finalize_callback_t NULLABLE finalize,
239                                         void *NULLABLE context);
240 comm_t *NULLABLE ioloop_connection_create(addr_t *NONNULL remote_address, bool tls, bool stream,
241                                           datagram_callback_t NONNULL datagram_callback,
242                                           connect_callback_t NULLABLE connected,
243                                           disconnect_callback_t NULLABLE disconnected,
244                                           finalize_callback_t NULLABLE finalize,
245                                           void *NONNULL context);
246 #define ioloop_message_retain(wakeup) ioloop_message_retain_(wakeup, __FILE__, __LINE__)
247 void ioloop_message_retain_(message_t *NONNULL message, const char *NONNULL file, int line);
248 #define ioloop_message_release(wakeup) ioloop_message_release_(wakeup, __FILE__, __LINE__)
249 void ioloop_message_release_(message_t *NONNULL message, const char *NONNULL file, int line);
250 bool ioloop_send_message(comm_t *NONNULL connection, message_t *NULLABLE responding_to,
251                          struct iovec *NONNULL iov, int iov_len);
252 bool ioloop_map_interface_addresses(void *NULLABLE context, interface_callback_t NONNULL callback);
253 ssize_t ioloop_recvmsg(int sock, uint8_t *NONNULL buffer, size_t buffer_length, int *NONNULL ifindex,
254                        int *NONNULL hoplimit, addr_t *NONNULL source, addr_t *NONNULL destination);
255 #define ioloop_subproc_release(subproc) ioloop_subproc_release_(subproc, __FILE__, __LINE__)
256 void ioloop_subproc_release_(subproc_t *NONNULL subproc, const char *NONNULL file, int line);
257 #define ioloop_subproc_retain(subproc) ioloop_subproc_retain_(subproc, __FILE__, __LINE__)
258 void ioloop_subproc_retain_(subproc_t *NONNULL subproc, const char *NONNULL file, int line);
259 subproc_t *NULLABLE ioloop_subproc(const char *NONNULL exepath, char *NULLABLE *NONNULL argv, int argc,
260                                    subproc_callback_t NULLABLE callback, io_callback_t NULLABLE output_callback,
261                                    void *NULLABLE context);
262 #define ioloop_dnssd_txn_add(ref, context, finalize) ioloop_dnssd_txn_add_(ref, context, finalize, __FILE__, __LINE__)
263 dnssd_txn_t *NULLABLE
264 ioloop_dnssd_txn_add_(DNSServiceRef NONNULL ref, void *NULLABLE context,
265                       dnssd_txn_finalize_callback_t NULLABLE callback, const char *NONNULL file, int line);
266 void ioloop_dnssd_txn_cancel(dnssd_txn_t *NONNULL txn);
267 #define ioloop_dnssd_txn_retain(txn) ioloop_dnssd_txn_retain_(txn, __FILE__, __LINE__)
268 void ioloop_dnssd_txn_retain_(dnssd_txn_t *NONNULL txn, const char *NONNULL file, int line);
269 #define ioloop_dnssd_txn_release(txn) ioloop_dnssd_txn_release_(txn, __FILE__, __LINE__)
270 void ioloop_dnssd_txn_release_(dnssd_txn_t *NONNULL txn, const char *NONNULL file, int line);
271 #endif
272 void ioloop_dnssd_txn_set_aux_pointer(dnssd_txn_t *NONNULL txn, void *NULLABLE aux_pointer);
273 void *NULLABLE ioloop_dnssd_txn_get_aux_pointer(dnssd_txn_t *NONNULL txn);
274 void *NULLABLE ioloop_dnssd_txn_get_context(dnssd_txn_t *NONNULL txn);
275 
276 #define ioloop_file_descriptor_create(fd, context, finalize) \
277     ioloop_file_descriptor_create_(fd, context, finalize, __FILE__, __LINE__)
278 io_t *NULLABLE ioloop_file_descriptor_create_(int fd, void *NULLABLE context, finalize_callback_t NULLABLE finalize,
279                                               const char *NONNULL file, int line);
280 #define ioloop_file_descriptor_retain(file_descriptor) ioloop_file_descriptor_retain_(file_descriptor, __FILE__, \
281                                                             __LINE__)
282 void ioloop_file_descriptor_retain_(io_t *NONNULL file_descriptor, const char *NONNULL file, int line);
283 #define ioloop_file_descriptor_release(file_descriptor) ioloop_file_descriptor_release_(file_descriptor, __FILE__, \
284                                                             __LINE__)
285 void ioloop_file_descriptor_release_(io_t *NONNULL file_descriptor, const char *NONNULL file, int line);
286 
287 bool ioloop_interface_monitor_start(void);
288 
289 #ifdef IOLOOP_MACOS
290 xpc_connection_t NULLABLE ioloop_create_xpc_service(const char *NONNULL name, ioloop_xpc_callback_t NONNULL callback);
291 #endif
292 
293 // Local Variables:
294 // mode: C
295 // tab-width: 4
296 // c-file-style: "bsd"
297 // c-basic-offset: 4
298 // fill-column: 108
299 // indent-tabs-mode: nil
300 // End:
301