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