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