1 /* $NetBSD: isclib.c,v 1.3 2019/01/10 17:41:47 christos Exp $ */ 2 3 /* 4 * Copyright(c) 2009-2017 by 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 * 950 Charter Street 20 * Redwood City, CA 94063 21 * <info@isc.org> 22 * http://www.isc.org/ 23 * 24 */ 25 26 #include <sys/cdefs.h> 27 __RCSID("$NetBSD: isclib.c,v 1.3 2019/01/10 17:41:47 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 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 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_taskmgr_destroy(&dhcp_gbl_ctx.taskmgr); 113 114 if (dhcp_gbl_ctx.actx_started != ISC_FALSE) { 115 isc_app_ctxfinish(dhcp_gbl_ctx.actx); 116 dhcp_gbl_ctx.actx_started = ISC_FALSE; 117 } 118 119 if (dhcp_gbl_ctx.actx != NULL) 120 isc_appctx_destroy(&dhcp_gbl_ctx.actx); 121 122 if (dhcp_gbl_ctx.mctx != NULL) 123 isc_mem_detach(&dhcp_gbl_ctx.mctx); 124 125 return; 126 } 127 128 /* Installs a handler for a signal using sigaction */ 129 static void 130 handle_signal(int sig, void (*handler)(int)) { 131 struct sigaction sa; 132 133 memset(&sa, 0, sizeof(sa)); 134 sa.sa_handler = handler; 135 sigfillset(&sa.sa_mask); 136 if (sigaction(sig, &sa, NULL) != 0) { 137 log_debug("handle_signal() failed for signal %d error: %s", 138 sig, strerror(errno)); 139 } 140 } 141 142 isc_result_t 143 dhcp_context_create(int flags, 144 struct in_addr *local4, 145 struct in6_addr *local6) { 146 isc_result_t result; 147 148 if ((flags & DHCP_CONTEXT_PRE_DB) != 0) { 149 /* 150 * Set up the error messages, this isn't the right place 151 * for this call but it is convienent for now. 152 */ 153 result = dhcp_result_register(); 154 if (result != ISC_R_SUCCESS) { 155 log_fatal("register_table() %s: %u", "failed", result); 156 } 157 158 memset(&dhcp_gbl_ctx, 0, sizeof (dhcp_gbl_ctx)); 159 160 isc_lib_register(); 161 162 #if 0 163 /* get the current time for use as the random seed */ 164 gettimeofday(&cur_tv, (struct timezone *)0); 165 isc_random_seed(cur_tv.tv_sec); 166 #endif 167 168 /* we need to create the memory context before 169 * the lib inits in case we aren't doing NSUPDATE 170 * in which case dst needs a memory context 171 */ 172 result = isc_mem_create(0, 0, &dhcp_gbl_ctx.mctx); 173 if (result != ISC_R_SUCCESS) 174 goto cleanup; 175 176 177 #if defined (NSUPDATE) 178 result = dns_lib_init(); 179 if (result != ISC_R_SUCCESS) 180 goto cleanup; 181 #else 182 /* The dst library is inited as part of dns_lib_init, we don't 183 * need it if NSUPDATE is enabled */ 184 result = dst_lib_init(dhcp_gbl_ctx.mctx, NULL, 0); 185 if (result != ISC_R_SUCCESS) 186 goto cleanup; 187 188 #endif 189 190 result = isc_appctx_create(dhcp_gbl_ctx.mctx, 191 &dhcp_gbl_ctx.actx); 192 if (result != ISC_R_SUCCESS) 193 goto cleanup; 194 195 result = isc_taskmgr_createinctx(dhcp_gbl_ctx.mctx, 196 dhcp_gbl_ctx.actx, 197 1, 0, 198 &dhcp_gbl_ctx.taskmgr); 199 if (result != ISC_R_SUCCESS) 200 goto cleanup; 201 202 result = isc_socketmgr_createinctx(dhcp_gbl_ctx.mctx, 203 dhcp_gbl_ctx.actx, 204 &dhcp_gbl_ctx.socketmgr); 205 if (result != ISC_R_SUCCESS) 206 goto cleanup; 207 208 result = isc_timermgr_createinctx(dhcp_gbl_ctx.mctx, 209 dhcp_gbl_ctx.actx, 210 &dhcp_gbl_ctx.timermgr); 211 if (result != ISC_R_SUCCESS) 212 goto cleanup; 213 214 result = isc_task_create(dhcp_gbl_ctx.taskmgr, 0, &dhcp_gbl_ctx.task); 215 if (result != ISC_R_SUCCESS) 216 goto cleanup; 217 218 result = isc_app_ctxstart(dhcp_gbl_ctx.actx); 219 if (result != ISC_R_SUCCESS) 220 return (result); 221 dhcp_gbl_ctx.actx_started = ISC_TRUE; 222 223 /* Not all OSs support suppressing SIGPIPE through socket 224 * options, so set the sigal action to be ignore. This allows 225 * broken connections to fail gracefully with EPIPE on writes */ 226 handle_signal(SIGPIPE, SIG_IGN); 227 228 /* Reset handlers installed by isc_app_ctxstart() 229 * to default for control-c and kill */ 230 handle_signal(SIGINT, SIG_DFL); 231 handle_signal(SIGTERM, SIG_DFL); 232 } 233 234 #if defined (NSUPDATE) 235 if ((flags & DHCP_CONTEXT_POST_DB) != 0) { 236 /* Setting addresses only. 237 * All real work will be done later on if needed to avoid 238 * listening on ddns port if client/server was compiled with 239 * ddns support but not using it. */ 240 if (local4 != NULL) { 241 dhcp_gbl_ctx.use_local4 = 1; 242 isc_sockaddr_fromin(&dhcp_gbl_ctx.local4_sockaddr, 243 local4, 0); 244 } 245 246 if (local6 != NULL) { 247 dhcp_gbl_ctx.use_local6 = 1; 248 isc_sockaddr_fromin6(&dhcp_gbl_ctx.local6_sockaddr, 249 local6, 0); 250 } 251 252 if (!(flags & DHCP_DNS_CLIENT_LAZY_INIT)) { 253 result = dns_client_init(); 254 } 255 } 256 #endif 257 258 return(ISC_R_SUCCESS); 259 260 cleanup: 261 /* 262 * Currently we don't try and cleanup, just return an error 263 * expecting that our caller will log the error and exit. 264 */ 265 266 return(result); 267 } 268 269 /* 270 * Convert a string name into the proper structure for the isc routines 271 * 272 * Previously we allowed names without a trailing '.' however the current 273 * dns and dst code requires the names to end in a period. If the 274 * name doesn't have a trailing period add one as part of creating 275 * the dns name. 276 */ 277 278 isc_result_t 279 dhcp_isc_name(unsigned char *namestr, 280 dns_fixedname_t *namefix, 281 dns_name_t **name) 282 { 283 size_t namelen; 284 isc_buffer_t b; 285 isc_result_t result; 286 287 namelen = strlen((char *)namestr); 288 isc_buffer_init(&b, namestr, namelen); 289 isc_buffer_add(&b, namelen); 290 dns_fixedname_init(namefix); 291 *name = dns_fixedname_name(namefix); 292 result = dns_name_fromtext(*name, &b, dns_rootname, 0, NULL); 293 isc_buffer_invalidate(&b); 294 return(result); 295 } 296 297 isc_result_t 298 isclib_make_dst_key(char *inname, 299 char *algorithm, 300 unsigned char *secret, 301 int length, 302 dst_key_t **dstkey) 303 { 304 isc_result_t result; 305 dns_name_t *name; 306 dns_fixedname_t name0; 307 isc_buffer_t b; 308 unsigned int algorithm_code; 309 310 isc_buffer_init(&b, secret, length); 311 isc_buffer_add(&b, length); 312 313 if (strcasecmp(algorithm, DHCP_HMAC_MD5_NAME) == 0) { 314 algorithm_code = DST_ALG_HMACMD5; 315 } else if (strcasecmp(algorithm, DHCP_HMAC_SHA1_NAME) == 0) { 316 algorithm_code = DST_ALG_HMACSHA1; 317 } else if (strcasecmp(algorithm, DHCP_HMAC_SHA224_NAME) == 0) { 318 algorithm_code = DST_ALG_HMACSHA224; 319 } else if (strcasecmp(algorithm, DHCP_HMAC_SHA256_NAME) == 0) { 320 algorithm_code = DST_ALG_HMACSHA256; 321 } else if (strcasecmp(algorithm, DHCP_HMAC_SHA384_NAME) == 0) { 322 algorithm_code = DST_ALG_HMACSHA384; 323 } else if (strcasecmp(algorithm, DHCP_HMAC_SHA512_NAME) == 0) { 324 algorithm_code = DST_ALG_HMACSHA512; 325 } else { 326 return(DHCP_R_INVALIDARG); 327 } 328 329 result = dhcp_isc_name((unsigned char *)inname, &name0, &name); 330 if (result != ISC_R_SUCCESS) { 331 return(result); 332 } 333 334 return(dst_key_frombuffer(name, algorithm_code, DNS_KEYOWNER_ENTITY, 335 DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, 336 &b, dhcp_gbl_ctx.mctx, dstkey)); 337 } 338 339 /** 340 * signal handler that initiates server shutdown 341 * 342 * @param signal signal code that we received 343 */ 344 void dhcp_signal_handler(int signal) { 345 isc_appctx_t *ctx = dhcp_gbl_ctx.actx; 346 int prev = shutdown_signal; 347 348 if (prev != 0) { 349 /* Already in shutdown. */ 350 return; 351 } 352 /* Possible race but does it matter? */ 353 shutdown_signal = signal; 354 355 /* Use reload (aka suspend) for easier dispatch() reenter. */ 356 if (ctx) { 357 (void) isc_app_ctxsuspend(ctx); 358 } 359 } 360 361 isc_result_t dns_client_init() { 362 isc_result_t result; 363 if (dhcp_gbl_ctx.dnsclient == NULL) { 364 result = dns_client_createx(dhcp_gbl_ctx.mctx, 365 dhcp_gbl_ctx.actx, 366 dhcp_gbl_ctx.taskmgr, 367 dhcp_gbl_ctx.socketmgr, 368 dhcp_gbl_ctx.timermgr, 369 0, 370 &dhcp_gbl_ctx.dnsclient, 371 (dhcp_gbl_ctx.use_local4 ? 372 &dhcp_gbl_ctx.local4_sockaddr 373 : NULL), 374 (dhcp_gbl_ctx.use_local6 ? 375 &dhcp_gbl_ctx.local6_sockaddr 376 : NULL)); 377 378 if (result != ISC_R_SUCCESS) { 379 log_error("Unable to create DNS client context:" 380 " result: %d", result); 381 return result; 382 } 383 384 /* If we can't set up the servers we may not be able to 385 * do DDNS but we should continue to try and perform 386 * our basic functions and let the user sort it out. */ 387 result = dhcp_dns_client_setservers(); 388 if (result != ISC_R_SUCCESS) { 389 log_error("Unable to set resolver from resolv.conf; " 390 "startup continuing but DDNS support " 391 "may be affected: result %d", result); 392 } 393 } 394 395 return ISC_R_SUCCESS; 396 } 397