1 /* $NetBSD: isclib.c,v 1.8 2022/04/03 01:10:59 christos Exp $ */ 2 3 /* 4 * Copyright(C) 2009-2022 Internet Systems Consortium, Inc.("ISC") 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * Internet Systems Consortium, Inc. 19 * PO Box 360 20 * Newmarket, NH 03857 USA 21 * <info (at) isc.org> 22 * http://www.isc.org/ 23 * 24 */ 25 26 #include <sys/cdefs.h> 27 __RCSID("$NetBSD: isclib.c,v 1.8 2022/04/03 01:10:59 christos Exp $"); 28 29 /*Trying to figure out what we need to define to get things to work. 30 It looks like we want/need the library but need the fdwatchcommand 31 which may be a problem */ 32 33 #include "dhcpd.h" 34 35 #include <sys/time.h> 36 #include <signal.h> 37 38 dhcp_context_t dhcp_gbl_ctx; 39 int shutdown_signal = 0; 40 41 #if defined (NSUPDATE) 42 43 /* This routine will open up the /etc/resolv.conf file and 44 * send any nameservers it finds to the DNS client code. 45 * It may be moved to be part of the dns client code instead 46 * of being in the DHCP code 47 */ 48 static isc_result_t 49 dhcp_dns_client_setservers(void) 50 { 51 isc_result_t result; 52 irs_resconf_t *resconf = NULL; 53 isc_sockaddrlist_t *nameservers; 54 isc_sockaddr_t *sa; 55 56 result = irs_resconf_load(dhcp_gbl_ctx.mctx, _PATH_RESOLV_CONF, 57 &resconf); 58 if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { 59 log_error("irs_resconf_load failed: %d.", result); 60 return (result); 61 } 62 63 nameservers = irs_resconf_getnameservers(resconf); 64 65 /* Initialize port numbers */ 66 for (sa = ISC_LIST_HEAD(*nameservers); 67 sa != NULL; 68 sa = ISC_LIST_NEXT(sa, link)) { 69 switch (sa->type.sa.sa_family) { 70 case AF_INET: 71 sa->type.sin.sin_port = htons(NS_DEFAULTPORT); 72 break; 73 case AF_INET6: 74 sa->type.sin6.sin6_port = htons(NS_DEFAULTPORT); 75 break; 76 default: 77 break; 78 } 79 } 80 81 result = dns_client_setservers(dhcp_gbl_ctx.dnsclient, 82 dns_rdataclass_in, 83 NULL, nameservers); 84 if (result != ISC_R_SUCCESS) { 85 log_error("dns_client_setservers failed: %d.", 86 result); 87 } 88 return (result); 89 } 90 #endif /* defined NSUPDATE */ 91 92 void 93 isclib_cleanup(void) 94 { 95 #if defined (NSUPDATE) 96 if (dhcp_gbl_ctx.dnsclient != NULL) 97 dns_client_destroy((dns_client_t **)&dhcp_gbl_ctx.dnsclient); 98 #endif /* defined NSUPDATE */ 99 100 if (dhcp_gbl_ctx.task != NULL) { 101 isc_task_shutdown(dhcp_gbl_ctx.task); 102 isc_task_detach(&dhcp_gbl_ctx.task); 103 } 104 105 if (dhcp_gbl_ctx.timermgr != NULL) 106 isc_timermgr_destroy(&dhcp_gbl_ctx.timermgr); 107 108 if (dhcp_gbl_ctx.socketmgr != NULL) 109 isc_socketmgr_destroy(&dhcp_gbl_ctx.socketmgr); 110 111 if (dhcp_gbl_ctx.taskmgr != NULL) 112 isc_managers_destroy(&dhcp_gbl_ctx.netmgr, 113 &dhcp_gbl_ctx.taskmgr); 114 115 if (dhcp_gbl_ctx.actx_started != ISC_FALSE) { 116 isc_app_ctxfinish(dhcp_gbl_ctx.actx); 117 dhcp_gbl_ctx.actx_started = ISC_FALSE; 118 } 119 120 if (dhcp_gbl_ctx.actx != NULL) 121 isc_appctx_destroy(&dhcp_gbl_ctx.actx); 122 123 if (dhcp_gbl_ctx.mctx != NULL) 124 isc_mem_detach(&dhcp_gbl_ctx.mctx); 125 126 return; 127 } 128 129 /* Installs a handler for a signal using sigaction */ 130 static void 131 handle_signal(int sig, void (*handler)(int)) { 132 struct sigaction sa; 133 134 memset(&sa, 0, sizeof(sa)); 135 sa.sa_handler = handler; 136 sigfillset(&sa.sa_mask); 137 if (sigaction(sig, &sa, NULL) != 0) { 138 log_debug("handle_signal() failed for signal %d error: %s", 139 sig, strerror(errno)); 140 } 141 } 142 143 /* Callback passed to isc_app_ctxonrun 144 * 145 * BIND9 context code will invoke this handler once the context has 146 * entered the running state. We use it to set a global marker so that 147 * we can tell if the context is running. Several of the isc_app_ 148 * calls REQUIRE that the context is running and we need a way to 149 * know that. 150 * 151 * We also check to see if we received a shutdown signal prior to 152 * the context entering the run state. If we did, then we can just 153 * simply shut the context down now. This closes the relatively 154 * small window between start up and entering run via the call 155 * to dispatch(). 156 * 157 */ 158 static void 159 set_ctx_running(isc_task_t *task, isc_event_t *event) { 160 IGNORE_UNUSED(task); 161 dhcp_gbl_ctx.actx_running = ISC_TRUE; 162 163 if (shutdown_signal) { 164 // We got signaled shutdown before we entered running state. 165 // Now that we've reached running state, shut'er down. 166 isc_app_ctxsuspend(dhcp_gbl_ctx.actx); 167 } 168 169 isc_event_free(&event); 170 } 171 172 isc_result_t 173 dhcp_context_create(int flags, 174 struct in_addr *local4, 175 struct in6_addr *local6) { 176 isc_result_t result; 177 178 if ((flags & DHCP_CONTEXT_PRE_DB) != 0) { 179 dhcp_gbl_ctx.actx_started = ISC_FALSE; 180 dhcp_gbl_ctx.actx_running = ISC_FALSE; 181 182 /* 183 * Set up the error messages, this isn't the right place 184 * for this call but it is convienent for now. 185 */ 186 result = dhcp_result_register(); 187 if (result != ISC_R_SUCCESS) { 188 log_fatal("register_table() %s: %u", "failed", result); 189 } 190 191 memset(&dhcp_gbl_ctx, 0, sizeof (dhcp_gbl_ctx)); 192 193 isc_lib_register(); 194 195 #if 0 196 /* get the current time for use as the random seed */ 197 gettimeofday(&cur_tv, (struct timezone *)0); 198 isc_random_seed(cur_tv.tv_sec); 199 #endif 200 201 /* we need to create the memory context before 202 * the lib inits in case we aren't doing NSUPDATE 203 * in which case dst needs a memory context 204 */ 205 isc_mem_create(&dhcp_gbl_ctx.mctx); 206 207 #if defined (NSUPDATE) 208 result = dns_lib_init(); 209 if (result != ISC_R_SUCCESS) 210 goto cleanup; 211 #else /* defined NSUPDATE */ 212 /* The dst library is inited as part of dns_lib_init, we don't 213 * need it if NSUPDATE is enabled */ 214 result = dst_lib_init(dhcp_gbl_ctx.mctx, NULL, 0); 215 if (result != ISC_R_SUCCESS) 216 goto cleanup; 217 218 #endif /* defined NSUPDATE */ 219 220 result = isc_appctx_create(dhcp_gbl_ctx.mctx, 221 &dhcp_gbl_ctx.actx); 222 223 result = isc_managers_create(dhcp_gbl_ctx.mctx, 2, 0, 224 &dhcp_gbl_ctx.netmgr, &dhcp_gbl_ctx.taskmgr); 225 if (result != ISC_R_SUCCESS) 226 goto cleanup; 227 228 result = isc_socketmgr_create(dhcp_gbl_ctx.mctx, 229 &dhcp_gbl_ctx.socketmgr); 230 if (result != ISC_R_SUCCESS) 231 goto cleanup; 232 233 result = isc_timermgr_create(dhcp_gbl_ctx.mctx, 234 &dhcp_gbl_ctx.timermgr); 235 if (result != ISC_R_SUCCESS) 236 goto cleanup; 237 238 result = isc_task_create(dhcp_gbl_ctx.taskmgr, 0, 239 &dhcp_gbl_ctx.task); 240 if (result != ISC_R_SUCCESS) 241 goto cleanup; 242 243 result = isc_app_ctxstart(dhcp_gbl_ctx.actx); 244 if (result != ISC_R_SUCCESS) 245 goto cleanup; 246 247 dhcp_gbl_ctx.actx_started = ISC_TRUE; 248 249 // Install the onrun callback. 250 result = isc_app_ctxonrun(dhcp_gbl_ctx.actx, dhcp_gbl_ctx.mctx, 251 dhcp_gbl_ctx.task, set_ctx_running, 252 dhcp_gbl_ctx.actx); 253 if (result != ISC_R_SUCCESS) 254 goto cleanup; 255 256 /* Not all OSs support suppressing SIGPIPE through socket 257 * options, so set the sigal action to be ignore. This allows 258 * broken connections to fail gracefully with EPIPE on writes */ 259 handle_signal(SIGPIPE, SIG_IGN); 260 261 /* Reset handlers installed by isc_app_ctxstart() 262 * to default for control-c and kill */ 263 handle_signal(SIGINT, SIG_DFL); 264 handle_signal(SIGTERM, SIG_DFL); 265 } 266 267 #if defined (NSUPDATE) 268 if ((flags & DHCP_CONTEXT_POST_DB) != 0) { 269 /* Setting addresses only. 270 * All real work will be done later on if needed to avoid 271 * listening on ddns port if client/server was compiled with 272 * ddns support but not using it. */ 273 if (local4 != NULL) { 274 dhcp_gbl_ctx.use_local4 = 1; 275 isc_sockaddr_fromin(&dhcp_gbl_ctx.local4_sockaddr, 276 local4, 0); 277 } 278 279 if (local6 != NULL) { 280 dhcp_gbl_ctx.use_local6 = 1; 281 isc_sockaddr_fromin6(&dhcp_gbl_ctx.local6_sockaddr, 282 local6, 0); 283 } 284 285 if (!(flags & DHCP_DNS_CLIENT_LAZY_INIT)) { 286 result = dns_client_init(); 287 } 288 } 289 #endif /* defined NSUPDATE */ 290 291 return(ISC_R_SUCCESS); 292 293 cleanup: 294 /* 295 * Currently we don't try and cleanup, just return an error 296 * expecting that our caller will log the error and exit. 297 */ 298 299 return(result); 300 } 301 302 /* 303 * Convert a string name into the proper structure for the isc routines 304 * 305 * Previously we allowed names without a trailing '.' however the current 306 * dns and dst code requires the names to end in a period. If the 307 * name doesn't have a trailing period add one as part of creating 308 * the dns name. 309 */ 310 311 isc_result_t 312 dhcp_isc_name(unsigned char *namestr, 313 dns_fixedname_t *namefix, 314 dns_name_t **name) 315 { 316 size_t namelen; 317 isc_buffer_t b; 318 isc_result_t result; 319 320 namelen = strlen((char *)namestr); 321 isc_buffer_init(&b, namestr, namelen); 322 isc_buffer_add(&b, namelen); 323 dns_fixedname_init(namefix); 324 *name = dns_fixedname_name(namefix); 325 result = dns_name_fromtext(*name, &b, dns_rootname, 0, NULL); 326 isc_buffer_invalidate(&b); 327 return(result); 328 } 329 330 isc_result_t 331 isclib_make_dst_key(char *inname, 332 char *algorithm, 333 unsigned char *secret, 334 int length, 335 dst_key_t **dstkey) 336 { 337 isc_result_t result; 338 dns_name_t *name; 339 dns_fixedname_t name0; 340 isc_buffer_t b; 341 unsigned int algorithm_code; 342 343 isc_buffer_init(&b, secret, length); 344 isc_buffer_add(&b, length); 345 346 if (strcasecmp(algorithm, DHCP_HMAC_MD5_NAME) == 0) { 347 algorithm_code = DST_ALG_HMACMD5; 348 } else if (strcasecmp(algorithm, DHCP_HMAC_SHA1_NAME) == 0) { 349 algorithm_code = DST_ALG_HMACSHA1; 350 } else if (strcasecmp(algorithm, DHCP_HMAC_SHA224_NAME) == 0) { 351 algorithm_code = DST_ALG_HMACSHA224; 352 } else if (strcasecmp(algorithm, DHCP_HMAC_SHA256_NAME) == 0) { 353 algorithm_code = DST_ALG_HMACSHA256; 354 } else if (strcasecmp(algorithm, DHCP_HMAC_SHA384_NAME) == 0) { 355 algorithm_code = DST_ALG_HMACSHA384; 356 } else if (strcasecmp(algorithm, DHCP_HMAC_SHA512_NAME) == 0) { 357 algorithm_code = DST_ALG_HMACSHA512; 358 } else { 359 return(DHCP_R_INVALIDARG); 360 } 361 362 result = dhcp_isc_name((unsigned char *)inname, &name0, &name); 363 if (result != ISC_R_SUCCESS) { 364 return(result); 365 } 366 367 return(dst_key_frombuffer(name, algorithm_code, DNS_KEYOWNER_ENTITY, 368 DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, 369 &b, dhcp_gbl_ctx.mctx, dstkey)); 370 } 371 372 /** 373 * signal handler that initiates server shutdown 374 * 375 * @param signal signal code that we received 376 */ 377 void dhcp_signal_handler(int signal) { 378 if (shutdown_signal != 0) { 379 /* Already in shutdown. */ 380 return; 381 } 382 383 /* Possible race but does it matter? */ 384 shutdown_signal = signal; 385 386 /* If the application context is running tell it to shut down */ 387 if (dhcp_gbl_ctx.actx_running == ISC_TRUE) { 388 (void) isc_app_ctxsuspend(dhcp_gbl_ctx.actx); 389 } 390 } 391 392 #if defined (NSUPDATE) 393 isc_result_t dns_client_init() { 394 isc_result_t result; 395 if (dhcp_gbl_ctx.dnsclient == NULL) { 396 result = dns_client_create(dhcp_gbl_ctx.mctx, 397 dhcp_gbl_ctx.actx, 398 dhcp_gbl_ctx.taskmgr, 399 dhcp_gbl_ctx.socketmgr, 400 dhcp_gbl_ctx.timermgr, 401 0, 402 &dhcp_gbl_ctx.dnsclient, 403 (dhcp_gbl_ctx.use_local4 ? 404 &dhcp_gbl_ctx.local4_sockaddr 405 : NULL), 406 (dhcp_gbl_ctx.use_local6 ? 407 &dhcp_gbl_ctx.local6_sockaddr 408 : NULL)); 409 410 if (result != ISC_R_SUCCESS) { 411 log_error("Unable to create DNS client context:" 412 " result: %d", result); 413 return result; 414 } 415 416 /* If we can't set up the servers we may not be able to 417 * do DDNS but we should continue to try and perform 418 * our basic functions and let the user sort it out. */ 419 result = dhcp_dns_client_setservers(); 420 if (result != ISC_R_SUCCESS) { 421 log_error("Unable to set resolver from resolv.conf; " 422 "startup continuing but DDNS support " 423 "may be affected: result %d", result); 424 } 425 } 426 427 return ISC_R_SUCCESS; 428 } 429 #endif /* defined (NSUPDATE) */ 430