1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 #pragma once
13 
14 #include <isc/mem.h>
15 #include <isc/region.h>
16 #include <isc/result.h>
17 #include <isc/tls.h>
18 #include <isc/types.h>
19 
20 /*
21  * Replacement for isc_sockettype_t provided by socket.h.
22  */
23 typedef enum {
24 	isc_socktype_tcp = 1,
25 	isc_socktype_udp = 2,
26 	isc_socktype_unix = 3,
27 	isc_socktype_raw = 4
28 } isc_socktype_t;
29 
30 typedef void (*isc_nm_recv_cb_t)(isc_nmhandle_t *handle, isc_result_t eresult,
31 				 isc_region_t *region, void *cbarg);
32 /*%<
33  * Callback function to be used when receiving a packet.
34  *
35  * 'handle' the handle that can be used to send back the answer.
36  * 'eresult' the result of the event.
37  * 'region' contains the received data, if any. It will be freed
38  *          after return by caller.
39  * 'cbarg'  the callback argument passed to isc_nm_listenudp(),
40  *          isc_nm_listentcpdns(), or isc_nm_read().
41  */
42 typedef isc_result_t (*isc_nm_accept_cb_t)(isc_nmhandle_t *handle,
43 					   isc_result_t result, void *cbarg);
44 /*%<
45  * Callback function to be used when accepting a connection. (This differs
46  * from isc_nm_cb_t below in that it returns a result code.)
47  *
48  * 'handle' the handle that can be used to send back the answer.
49  * 'eresult' the result of the event.
50  * 'cbarg'  the callback argument passed to isc_nm_listentcp() or
51  * isc_nm_listentcpdns().
52  */
53 
54 typedef void (*isc_nm_cb_t)(isc_nmhandle_t *handle, isc_result_t result,
55 			    void *cbarg);
56 /*%<
57  * Callback function for other network completion events (send, connect).
58  *
59  * 'handle' the handle on which the event took place.
60  * 'eresult' the result of the event.
61  * 'cbarg'  the callback argument passed to isc_nm_send(),
62  *          isc_nm_tcp_connect(), or isc_nm_listentcp()
63  */
64 
65 typedef void (*isc_nm_opaquecb_t)(void *arg);
66 /*%<
67  * Opaque callback function, used for isc_nmhandle 'reset' and 'free'
68  * callbacks.
69  */
70 
71 typedef void (*isc_nm_workcb_t)(void *arg);
72 typedef void (*isc_nm_after_workcb_t)(void *arg, isc_result_t result);
73 /*%<
74  * Callback functions for libuv threadpool work (see uv_work_t)
75  */
76 
77 void
78 isc_nm_attach(isc_nm_t *mgr, isc_nm_t **dst);
79 void
80 isc_nm_detach(isc_nm_t **mgr0);
81 /*%<
82  * Attach/detach a network manager. When all references have been
83  * released, the network manager is shut down, freeing all resources.
84  * Destroy is working the same way as detach, but it actively waits
85  * for all other references to be gone.
86  */
87 
88 /* Return thread ID of current thread, or ISC_NETMGR_TID_UNKNOWN */
89 int
90 isc_nm_tid(void);
91 
92 void
93 isc_nmsocket_close(isc_nmsocket_t **sockp);
94 /*%<
95  * isc_nmsocket_close() detaches a listening socket that was
96  * created by isc_nm_listenudp(), isc_nm_listentcp(), or
97  * isc_nm_listentcpdns(). Once there are no remaining child
98  * sockets with active handles, the socket will be closed.
99  */
100 
101 #ifdef NETMGR_TRACE
102 #define isc_nmhandle_attach(handle, dest) \
103 	isc__nmhandle_attach(handle, dest, __FILE__, __LINE__, __func__)
104 #define isc_nmhandle_detach(handlep) \
105 	isc__nmhandle_detach(handlep, __FILE__, __LINE__, __func__)
106 #define FLARG , const char *file, unsigned int line, const char *func
107 #else
108 #define isc_nmhandle_attach(handle, dest) isc__nmhandle_attach(handle, dest)
109 #define isc_nmhandle_detach(handlep)	  isc__nmhandle_detach(handlep)
110 #define FLARG
111 #endif
112 
113 void
114 isc__nmhandle_attach(isc_nmhandle_t *handle, isc_nmhandle_t **dest FLARG);
115 void
116 isc__nmhandle_detach(isc_nmhandle_t **handlep FLARG);
117 /*%<
118  * Increment/decrement the reference counter in a netmgr handle,
119  * but (unlike the attach/detach functions) do not change the pointer
120  * value. If reference counters drop to zero, the handle can be
121  * marked inactive, possibly triggering deletion of its associated
122  * socket.
123  *
124  * (This will be used to prevent a client from being cleaned up when
125  * it's passed to an isc_task event handler. The libuv code would not
126  * otherwise know that the handle was in use and might free it, along
127  * with the client.)
128  */
129 #undef FLARG
130 
131 void *
132 isc_nmhandle_getdata(isc_nmhandle_t *handle);
133 
134 void *
135 isc_nmhandle_getextra(isc_nmhandle_t *handle);
136 
137 bool
138 isc_nmhandle_is_stream(isc_nmhandle_t *handle);
139 
140 void
141 isc_nmhandle_setdata(isc_nmhandle_t *handle, void *arg,
142 		     isc_nm_opaquecb_t doreset, isc_nm_opaquecb_t dofree);
143 /*%<
144  * isc_nmhandle_t has a void* opaque field (for example, ns_client_t).
145  * We reuse handle and `opaque` can also be reused between calls.
146  * This function sets this field and two callbacks:
147  * - doreset resets the `opaque` to initial state
148  * - dofree frees everything associated with `opaque`
149  */
150 
151 void
152 isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
153 void
154 isc_nmhandle_cleartimeout(isc_nmhandle_t *handle);
155 /*%<
156  * Set/clear the read/recv timeout for the socket connected to 'handle'
157  * to 'timeout' (in milliseconds), and reset the timer.
158  *
159  * When this is called on a 'wrapper' socket handle (for example,
160  * a TCPDNS socket wrapping a TCP connection), the timer is set for
161  * both socket layers.
162  */
163 bool
164 isc_nmhandle_timer_running(isc_nmhandle_t *handle);
165 /*%<
166  * Return true if the timer for the socket connected to 'handle'
167  * is running.
168  */
169 
170 void
171 isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value);
172 /*%<
173  * Enable/disable keepalive on this connection by setting it to 'value'.
174  *
175  * When keepalive is active, we switch to using the keepalive timeout
176  * to determine when to close a connection, rather than the idle timeout.
177  *
178  * This applies only to TCP-based DNS connections (i.e., TCPDNS or
179  * TLSDNS). On other types of connection it has no effect.
180  */
181 
182 isc_sockaddr_t
183 isc_nmhandle_peeraddr(isc_nmhandle_t *handle);
184 /*%<
185  * Return the peer address for the given handle.
186  */
187 isc_sockaddr_t
188 isc_nmhandle_localaddr(isc_nmhandle_t *handle);
189 /*%<
190  * Return the local address for the given handle.
191  */
192 
193 isc_nm_t *
194 isc_nmhandle_netmgr(isc_nmhandle_t *handle);
195 /*%<
196  * Return a pointer to the netmgr object for the given handle.
197  */
198 
199 isc_result_t
200 isc_nm_listenudp(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb,
201 		 void *cbarg, size_t extrasize, isc_nmsocket_t **sockp);
202 /*%<
203  * Start listening for UDP packets on interface 'iface' using net manager
204  * 'mgr'.
205  *
206  * On success, 'sockp' will be updated to contain a new listening UDP socket.
207  *
208  * When a packet is received on the socket, 'cb' will be called with 'cbarg'
209  * as its argument.
210  *
211  * When handles are allocated for the socket, 'extrasize' additional bytes
212  * can be allocated along with the handle for an associated object, which
213  * can then be freed automatically when the handle is destroyed.
214  */
215 
216 void
217 isc_nm_udpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
218 		  isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
219 		  size_t extrahandlesize);
220 /*%<
221  * Open a UDP socket, bind to 'local' and connect to 'peer', and
222  * immediately call 'cb' with a handle so that the caller can begin
223  * sending packets over UDP.
224  *
225  * When handles are allocated for the socket, 'extrasize' additional bytes
226  * can be allocated along with the handle for an associated object, which
227  * can then be freed automatically when the handle is destroyed.
228  *
229  * 'timeout' specifies the timeout interval in milliseconds.
230  *
231  * The connected socket can only be accessed via the handle passed to
232  * 'cb'.
233  */
234 
235 void
236 isc_nm_stoplistening(isc_nmsocket_t *sock);
237 /*%<
238  * Stop listening on socket 'sock'.
239  */
240 
241 void
242 isc_nm_pause(isc_nm_t *mgr);
243 /*%<
244  * Pause all processing, equivalent to taskmgr exclusive tasks.
245  * It won't return until all workers have been paused.
246  */
247 
248 void
249 isc_nm_resume(isc_nm_t *mgr);
250 /*%<
251  * Resume paused processing. It will return immediately after signalling
252  * workers to resume.
253  */
254 
255 void
256 isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
257 /*
258  * Begin (or continue) reading on the socket associated with 'handle', and
259  * update its recv callback to 'cb', which will be called as soon as there
260  * is data to process.
261  */
262 
263 void
264 isc_nm_pauseread(isc_nmhandle_t *handle);
265 /*%<
266  * Pause reading on this handle's socket, but remember the callback.
267  *
268  * Requires:
269  * \li	'handle' is a valid netmgr handle.
270  */
271 
272 void
273 isc_nm_cancelread(isc_nmhandle_t *handle);
274 /*%<
275  * Cancel reading on a connected socket. Calls the read/recv callback on
276  * active handles with a result code of ISC_R_CANCELED.
277  *
278  * Requires:
279  * \li	'sock' is a valid netmgr socket
280  * \li	...for which a read/recv callback has been defined.
281  */
282 
283 void
284 isc_nm_resumeread(isc_nmhandle_t *handle);
285 /*%<
286  * Resume reading on the handle's socket.
287  *
288  * Requires:
289  * \li	'handle' is a valid netmgr handle.
290  * \li	...for a socket with a defined read/recv callback.
291  */
292 
293 void
294 isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
295 	    void *cbarg);
296 /*%<
297  * Send the data in 'region' via 'handle'. Afterward, the callback 'cb' is
298  * called with the argument 'cbarg'.
299  *
300  * 'region' is not copied; it has to be allocated beforehand and freed
301  * in 'cb'.
302  */
303 
304 isc_result_t
305 isc_nm_listentcp(isc_nm_t *mgr, isc_sockaddr_t *iface,
306 		 isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
307 		 size_t extrahandlesize, int backlog, isc_quota_t *quota,
308 		 isc_nmsocket_t **sockp);
309 /*%<
310  * Start listening for raw messages over the TCP interface 'iface', using
311  * net manager 'mgr'.
312  *
313  * On success, 'sockp' will be updated to contain a new listening TCP
314  * socket.
315  *
316  * When connection is accepted on the socket, 'accept_cb' will be called with
317  * 'accept_cbarg' as its argument. The callback is expected to start a read.
318  *
319  * When handles are allocated for the socket, 'extrasize' additional bytes
320  * will be allocated along with the handle for an associated object.
321  *
322  * If 'quota' is not NULL, then the socket is attached to the specified
323  * quota. This allows us to enforce TCP client quota limits.
324  *
325  */
326 
327 void
328 isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
329 		  isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
330 		  size_t extrahandlesize);
331 /*%<
332  * Create a socket using netmgr 'mgr', bind it to the address 'local',
333  * and connect it to the address 'peer'.
334  *
335  * When the connection is complete or has timed out, call 'cb' with
336  * argument 'cbarg'. Allocate 'extrahandlesize' additional bytes along
337  * with the handle to use for an associated object.
338  *
339  * 'timeout' specifies the timeout interval in milliseconds.
340  *
341  * The connected socket can only be accessed via the handle passed to
342  * 'cb'.
343  */
344 
345 isc_result_t
346 isc_nm_listentcpdns(isc_nm_t *mgr, isc_sockaddr_t *iface,
347 		    isc_nm_recv_cb_t recv_cb, void *recv_cbarg,
348 		    isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
349 		    size_t extrahandlesize, int backlog, isc_quota_t *quota,
350 		    isc_nmsocket_t **sockp);
351 /*%<
352  * Start listening for DNS messages over the TCP interface 'iface', using
353  * net manager 'mgr'.
354  *
355  * On success, 'sockp' will be updated to contain a new listening TCPDNS
356  * socket. This is a wrapper around a raw TCP socket, which sends and
357  * receives DNS messages via that socket. It handles message buffering
358  * and pipelining, and automatically prepends messages with a two-byte
359  * length field.
360  *
361  * When a complete DNS message is received on the socket, 'cb' will be
362  * called with 'cbarg' as its argument.
363  *
364  * When a new TCPDNS connection is accepted, 'accept_cb' will be called
365  * with 'accept_cbarg' as its argument.
366  *
367  * When handles are allocated for the socket, 'extrasize' additional bytes
368  * will be allocated along with the handle for an associated object
369  * (typically ns_client).
370  *
371  * 'quota' is passed to isc_nm_listentcp() when opening the raw TCP socket.
372  */
373 
374 isc_result_t
375 isc_nm_listentlsdns(isc_nm_t *mgr, isc_sockaddr_t *iface,
376 		    isc_nm_recv_cb_t recv_cb, void *recv_cbarg,
377 		    isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
378 		    size_t extrahandlesize, int backlog, isc_quota_t *quota,
379 		    isc_tlsctx_t *sslctx, isc_nmsocket_t **sockp);
380 /*%<
381  * Same as isc_nm_listentcpdns but for an SSL (DoT) socket.
382  */
383 
384 void
385 isc_nm_sequential(isc_nmhandle_t *handle);
386 /*%<
387  * Disable pipelining on this connection. Each DNS packet will be only
388  * processed after the previous completes.
389  *
390  * The socket must be unpaused after the query is processed.  This is done
391  * the response is sent, or if we're dropping the query, it will be done
392  * when a handle is fully dereferenced by calling the socket's
393  * closehandle_cb callback.
394  *
395  * Note: This can only be run while a message is being processed; if it is
396  * run before any messages are read, no messages will be read.
397  *
398  * Also note: once this has been set, it cannot be reversed for a given
399  * connection.
400  */
401 
402 void
403 isc_nm_settimeouts(isc_nm_t *mgr, uint32_t init, uint32_t idle,
404 		   uint32_t keepalive, uint32_t advertised);
405 /*%<
406  * Sets the initial, idle, and keepalive timeout values (in milliseconds) to use
407  * for TCP connections, and the timeout value to advertise in responses using
408  * the EDNS TCP Keepalive option (which should ordinarily be the same
409  * as 'keepalive'), in milliseconds.
410  *
411  * Requires:
412  * \li	'mgr' is a valid netmgr.
413  */
414 
415 void
416 isc_nm_setnetbuffers(isc_nm_t *mgr, int32_t recv_tcp, int32_t send_tcp,
417 		     int32_t recv_udp, int32_t send_udp);
418 /*%<
419  * If not 0, sets the SO_RCVBUF and SO_SNDBUF socket options for TCP and UDP
420  * respectively.
421  *
422  * Requires:
423  * \li	'mgr' is a valid netmgr.
424  */
425 
426 void
427 isc_nm_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
428 		   uint32_t *keepalive, uint32_t *advertised);
429 /*%<
430  * Gets the initial, idle, keepalive, or advertised timeout values,
431  * in milliseconds.
432  *
433  * Any integer pointer parameter not set to NULL will be updated to
434  * contain the corresponding timeout value.
435  *
436  * Requires:
437  * \li	'mgr' is a valid netmgr.
438  */
439 
440 void
441 isc_nm_maxudp(isc_nm_t *mgr, uint32_t maxudp);
442 /*%<
443  * Simulate a broken firewall that blocks UDP messages larger than a given
444  * size.
445  */
446 
447 void
448 isc_nm_setstats(isc_nm_t *mgr, isc_stats_t *stats);
449 /*%<
450  * Set a socket statistics counter set 'stats' for 'mgr'.
451  *
452  * Requires:
453  *\li	'mgr' is valid and doesn't have stats already set.
454  *
455  *\li	stats is a valid set of statistics counters supporting the
456  *	full range of socket-related stats counter numbers.
457  */
458 
459 isc_result_t
460 isc_nm_checkaddr(const isc_sockaddr_t *addr, isc_socktype_t type);
461 /*%<
462  * Check whether the specified address is available on the local system
463  * by opening a socket and immediately closing it.
464  *
465  * Requires:
466  *\li	'addr' is not NULL.
467  */
468 
469 void
470 isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
471 		     isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
472 		     size_t extrahandlesize);
473 void
474 isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
475 		     isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
476 		     size_t extrahandlesize, isc_tlsctx_t *sslctx);
477 /*%<
478  * Establish a DNS client connection via a TCP or TLS connection, bound to
479  * the address 'local' and connected to the address 'peer'.
480  *
481  * When the connection is complete or has timed out, call 'cb' with
482  * argument 'cbarg'. Allocate 'extrahandlesize' additional bytes along
483  * with the handle to use for an associated object.
484  *
485  * 'timeout' specifies the timeout interval in milliseconds.
486  *
487  * The connected socket can only be accessed via the handle passed to
488  * 'cb'.
489  */
490 
491 bool
492 isc_nm_is_tlsdns_handle(isc_nmhandle_t *handle);
493 
494 #if HAVE_LIBNGHTTP2
495 
496 #define ISC_NM_HTTP_DEFAULT_PATH "/dns-query"
497 
498 isc_result_t
499 isc_nm_listentls(isc_nm_t *mgr, isc_sockaddr_t *iface,
500 		 isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
501 		 size_t extrahandlesize, int backlog, isc_quota_t *quota,
502 		 isc_tlsctx_t *sslctx, isc_nmsocket_t **sockp);
503 
504 void
505 isc_nm_tlsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
506 		  isc_nm_cb_t cb, void *cbarg, isc_tlsctx_t *ctx,
507 		  unsigned int timeout, size_t extrahandlesize);
508 
509 void
510 isc_nm_httpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
511 		   const char *uri, bool POST, isc_nm_cb_t cb, void *cbarg,
512 		   isc_tlsctx_t *ctx, unsigned int timeout,
513 		   size_t extrahandlesize);
514 
515 isc_result_t
516 isc_nm_listenhttp(isc_nm_t *mgr, isc_sockaddr_t *iface, int backlog,
517 		  isc_quota_t *quota, isc_tlsctx_t *ctx,
518 		  isc_nm_http_endpoints_t *eps, uint32_t max_concurrent_streams,
519 		  isc_nmsocket_t **sockp);
520 
521 isc_nm_http_endpoints_t *
522 isc_nm_http_endpoints_new(isc_mem_t *mctx);
523 /*%<
524  * Create a new, empty HTTP endpoints set object.
525  *
526  * Requires:
527  * \li 'mctx' a valid memory context object.
528  */
529 
530 isc_result_t
531 isc_nm_http_endpoints_add(isc_nm_http_endpoints_t *restrict eps,
532 			  const char *uri, const isc_nm_recv_cb_t cb,
533 			  void *cbarg, const size_t extrahandlesize);
534 /*%< Adds a new endpoint to the given HTTP endpoints set object.
535  *
536  * NOTE: adding an endpoint is allowed only if the endpoint object has
537  * not been passed to isc_nm_listenhttp() yet.
538  *
539  * Requires:
540  * \li 'eps' is a valid pointer to a valid isc_nm_http_endpoints_t
541  * object;
542  * \li 'uri' is a valid pointer to a string of length > 0;
543  * \li 'cb' is a valid pointer to a read callback function.
544  */
545 
546 void
547 isc_nm_http_endpoints_attach(isc_nm_http_endpoints_t * source,
548 			     isc_nm_http_endpoints_t **targetp);
549 /*%<
550  * Attaches to an HTTP endpoints set object.
551  *
552  * Requires:
553  * \li 'source' is a non-NULL pointer to a valid
554  * isc_nm_http_endpoints_t object;
555  * \li 'target' is a pointer to a pointer, containing NULL.
556  */
557 
558 void
559 isc_nm_http_endpoints_detach(isc_nm_http_endpoints_t **restrict epsp);
560 /*%<
561  * Detaches from an HTTP endpoints set object. When reference count
562  * reaches 0, the object get deleted.
563  *
564  * Requires:
565  * \li 'epsp' is a pointer to a pointer to a valid
566  * isc_nm_http_endpoints_t object.
567  */
568 
569 bool
570 isc_nm_is_http_handle(isc_nmhandle_t *handle);
571 
572 bool
573 isc_nm_http_path_isvalid(const char *path);
574 
575 void
576 isc_nm_http_makeuri(const bool https, const isc_sockaddr_t *sa,
577 		    const char *hostname, const uint16_t http_port,
578 		    const char *abs_path, char *outbuf,
579 		    const size_t outbuf_len);
580 /*%<
581  * Makes a URI connection string out of na isc_sockaddr_t object 'sa'
582  * or the specified 'hostname' and 'http_port'.
583  *
584  * Requires:
585  * \li 'abs_path' is a valid absolute HTTP path string;
586  * \li 'outbuf' is a valid pointer to a buffer which will get the result;
587  * \li 'outbuf_len' is a size of the result buffer and is greater than zero.
588  */
589 #endif /* HAVE_LIBNGHTTP2 */
590 
591 void
592 isc_nm_bad_request(isc_nmhandle_t *handle);
593 /*%<
594  * Perform a transport protocol specific action on the handle in case of a
595  * bad/malformed incoming DNS message.
596  *
597  * NOTE: The function currently is no-op for any protocol except HTTP/2.
598  *
599  * Requires:
600  *  \li 'handle' is a valid netmgr handle object.
601  */
602 
603 bool
604 isc_nm_xfr_allowed(isc_nmhandle_t *handle);
605 /*%<
606  * Check if it is possible to do a zone transfer over the given handle.
607  *
608  * Requires:
609  * \li	'handle' is a valid connection handle.
610  */
611 
612 void
613 isc_nm_task_enqueue(isc_nm_t *mgr, isc_task_t *task, int threadid);
614 /*%<
615  * Enqueue the 'task' onto the netmgr ievents queue.
616  *
617  * Requires:
618  * \li 'mgr' is a valid netmgr object
619  * \li 'task' is a valid task
620  * \li 'threadid' is either the preferred netmgr tid or -1, in which case
621  *     tid will be picked randomly. The threadid is capped (by modulo) to
622  *     maximum number of 'workers' as specifed in isc_nm_start()
623  */
624 
625 void
626 isc_nm_work_offload(isc_nm_t *mgr, isc_nm_workcb_t work_cb,
627 		    isc_nm_after_workcb_t after_work_cb, void *data);
628 /*%<
629  * Schedules a job to be handled by the libuv thread pool (see uv_work_t).
630  * The function specified in `work_cb` will be run by a thread in the
631  * thread pool; when complete, the `after_work_cb` function will run.
632  *
633  * Requires:
634  * \li 'mgr' is a valid netmgr object.
635  * \li We are currently running in a network manager thread.
636  */
637 
638 void
639 isc__nm_force_tid(int tid);
640 /*%<
641  * Force the thread ID to 'tid'. This is STRICTLY for use in unit
642  * tests and should not be used in any production code.
643  */
644