xref: /netbsd-src/external/mpl/dhcp/dist/omapip/isclib.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
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@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
dhcp_dns_client_setservers(void)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
isclib_cleanup(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
handle_signal(int sig,void (* handler)(int))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
set_ctx_running(isc_task_t * task,isc_event_t * event)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
dhcp_context_create(int flags,struct in_addr * local4,struct in6_addr * local6)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
dhcp_isc_name(unsigned char * namestr,dns_fixedname_t * namefix,dns_name_t ** name)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
isclib_make_dst_key(char * inname,char * algorithm,unsigned char * secret,int length,dst_key_t ** dstkey)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  */
dhcp_signal_handler(int signal)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)
dns_client_init()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