comm: add -Wall
[unleashed.git] / usr / src / cmd / nscd / nscd_frontend.c
blobf1729164192a11421c7216e25692ecbaab2d9111
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012 Milan Jurik. All rights reserved.
27 #include <stdlib.h>
28 #include <alloca.h>
29 #include <signal.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <pthread.h>
33 #include <time.h>
34 #include <errno.h>
35 #include <door.h>
36 #include <zone.h>
37 #include <resolv.h>
38 #include <priv.h>
39 #include <sys/socket.h>
40 #include <net/route.h>
41 #include <string.h>
42 #include <net/if.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include "nscd_common.h"
46 #include "nscd_door.h"
47 #include "nscd_config.h"
48 #include "nscd_switch.h"
49 #include "nscd_log.h"
50 #include "nscd_selfcred.h"
51 #include "nscd_frontend.h"
52 #include "nscd_admin.h"
54 static void rts_mon(void);
55 static void keep_open_dns_socket(void);
57 extern nsc_ctx_t *cache_ctx_p[];
60 * Current active Configuration data for the frontend component
62 static nscd_cfg_global_frontend_t frontend_cfg_g;
63 static nscd_cfg_frontend_t *frontend_cfg;
65 static int max_servers = 0;
66 static int max_servers_set = 0;
67 static int per_user_is_on = 1;
69 static char *main_execname;
70 static char **main_argv;
71 extern int _whoami;
72 extern long activity;
73 extern mutex_t activity_lock;
75 static sema_t common_sema;
77 static thread_key_t lookup_state_key;
78 static mutex_t create_lock = DEFAULTMUTEX;
79 static int num_servers = 0;
80 static thread_key_t server_key;
83 * Bind a TSD value to a server thread. This enables the destructor to
84 * be called if/when this thread exits. This would be a programming
85 * error, but better safe than sorry.
87 /*ARGSUSED*/
88 static void *
89 server_tsd_bind(void *arg)
91 static void *value = 0;
93 /* disable cancellation to avoid hangs if server threads disappear */
94 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
95 (void) thr_setspecific(server_key, value);
96 (void) door_return(NULL, 0, NULL, 0);
98 /* make lint happy */
99 return (NULL);
103 * Server threads are created here.
105 /*ARGSUSED*/
106 static void
107 server_create(door_info_t *dip)
109 (void) mutex_lock(&create_lock);
110 if (++num_servers > max_servers) {
111 num_servers--;
112 (void) mutex_unlock(&create_lock);
113 return;
115 (void) mutex_unlock(&create_lock);
116 (void) thr_create(NULL, 0, server_tsd_bind, NULL,
117 THR_BOUND|THR_DETACHED, NULL);
121 * Server thread are destroyed here
123 /*ARGSUSED*/
124 static void
125 server_destroy(void *arg)
127 (void) mutex_lock(&create_lock);
128 num_servers--;
129 (void) mutex_unlock(&create_lock);
133 * get clearance
136 _nscd_get_clearance(sema_t *sema) {
137 if (sema_trywait(&common_sema) == 0) {
138 (void) thr_setspecific(lookup_state_key, NULL);
139 return (0);
142 if (sema_trywait(sema) == 0) {
143 (void) thr_setspecific(lookup_state_key, (void*)1);
144 return (0);
147 return (1);
152 * release clearance
155 _nscd_release_clearance(sema_t *sema) {
156 int which;
158 (void) thr_getspecific(lookup_state_key, (void**)&which);
159 if (which == 0) /* from common pool */ {
160 (void) sema_post(&common_sema);
161 return (0);
164 (void) sema_post(sema);
165 return (1);
168 static void
169 dozip(void)
171 /* not much here */
175 * _nscd_restart_if_cfgfile_changed()
176 * Restart if modification times of nsswitch.conf or resolv.conf have changed.
178 * If nsswitch.conf has changed then it is possible that sources for
179 * various backends have changed and therefore the current cached
180 * data may not be consistent with the new data sources. By
181 * restarting the cache will be cleared and the new configuration will
182 * be used.
184 * The check for resolv.conf is made as only the first call to
185 * res_gethostbyname() or res_getaddrbyname() causes a call to
186 * res_ninit() to occur which in turn parses resolv.conf. Therefore
187 * to benefit from changes to resolv.conf nscd must be restarted when
188 * resolv.conf is updated, removed or created. If res_getXbyY calls
189 * are removed from NSS then this check could be removed.
192 void
193 _nscd_restart_if_cfgfile_changed()
196 static mutex_t nsswitch_lock = DEFAULTMUTEX;
197 static timestruc_t last_nsswitch_check = { 0 };
198 static timestruc_t last_nsswitch_modified = { 0 };
199 static timestruc_t last_resolv_modified = { -1, 0 };
200 static mutex_t restarting_lock = DEFAULTMUTEX;
201 static int restarting = 0;
202 int restart = 0;
203 time_t now = time(NULL);
204 char *me = "_nscd_restart_if_cfgfile_changed";
206 #define FLAG_RESTART_REQUIRED if (restarting == 0) {\
207 (void) mutex_lock(&restarting_lock);\
208 if (restarting == 0) {\
209 restarting = 1;\
210 restart = 1;\
212 (void) mutex_unlock(&restarting_lock);\
215 if (restarting == 1)
216 return;
218 if (now - last_nsswitch_check.tv_sec < _NSC_FILE_CHECK_TIME)
219 return;
221 (void) mutex_lock(&nsswitch_lock);
223 if (now - last_nsswitch_check.tv_sec >= _NSC_FILE_CHECK_TIME) {
224 struct stat nss_buf;
225 struct stat res_buf;
227 last_nsswitch_check.tv_sec = now;
228 last_nsswitch_check.tv_nsec = 0;
230 (void) mutex_unlock(&nsswitch_lock); /* let others continue */
232 if (stat("/etc/nsswitch.conf", &nss_buf) < 0) {
233 return;
234 } else if (last_nsswitch_modified.tv_sec == 0) {
235 last_nsswitch_modified = nss_buf.st_mtim;
238 if (last_nsswitch_modified.tv_sec < nss_buf.st_mtim.tv_sec ||
239 (last_nsswitch_modified.tv_sec == nss_buf.st_mtim.tv_sec &&
240 last_nsswitch_modified.tv_nsec < nss_buf.st_mtim.tv_nsec)) {
241 FLAG_RESTART_REQUIRED;
244 if (restart == 0) {
245 if (stat("/etc/resolv.conf", &res_buf) < 0) {
246 /* Unable to stat file, were we previously? */
247 if (last_resolv_modified.tv_sec > 0) {
248 /* Yes, it must have been removed. */
249 FLAG_RESTART_REQUIRED;
250 } else if (last_resolv_modified.tv_sec == -1) {
251 /* No, then we've never seen it. */
252 last_resolv_modified.tv_sec = 0;
254 } else if (last_resolv_modified.tv_sec == -1) {
255 /* We've just started and file is present. */
256 last_resolv_modified = res_buf.st_mtim;
257 } else if (last_resolv_modified.tv_sec == 0) {
258 /* Wasn't there at start-up. */
259 FLAG_RESTART_REQUIRED;
260 } else if (last_resolv_modified.tv_sec <
261 res_buf.st_mtim.tv_sec ||
262 (last_resolv_modified.tv_sec ==
263 res_buf.st_mtim.tv_sec &&
264 last_resolv_modified.tv_nsec <
265 res_buf.st_mtim.tv_nsec)) {
266 FLAG_RESTART_REQUIRED;
270 if (restart == 1) {
271 char *fmri;
274 * if in self cred mode, kill the forker and
275 * child nscds
277 if (_nscd_is_self_cred_on(0, NULL)) {
278 _nscd_kill_forker();
279 _nscd_kill_all_children();
283 * time for restart
285 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
286 (me, "nscd restart due to %s or %s change\n",
287 "/etc/nsswitch.conf", "resolv.conf");
289 * try to restart under smf
291 if ((fmri = getenv("SMF_FMRI")) == NULL) {
292 /* not running under smf - reexec */
293 (void) execv(main_execname, main_argv);
294 exit(1); /* just in case */
297 if (smf_restart_instance(fmri) == 0)
298 (void) sleep(10); /* wait a bit */
299 exit(1); /* give up waiting for resurrection */
302 } else
303 (void) mutex_unlock(&nsswitch_lock);
306 uid_t
307 _nscd_get_client_euid()
309 ucred_t *uc = NULL;
310 uid_t id;
311 char *me = "get_client_euid";
313 if (door_ucred(&uc) != 0) {
314 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
315 (me, "door_ucred: %s\n", strerror(errno));
316 return ((uid_t)-1);
319 id = ucred_geteuid(uc);
320 ucred_free(uc);
321 return (id);
325 * Check to see if the door client's euid is 0 or if it has required_priv
326 * privilege. Return 0 if yes, -1 otherwise.
327 * Supported values for required_priv are:
328 * - NSCD_ALL_PRIV: for all zones privileges
329 * - NSCD_READ_PRIV: for PRIV_FILE_DAC_READ privilege
332 _nscd_check_client_priv(int required_priv)
334 int rc = 0;
335 ucred_t *uc = NULL;
336 const priv_set_t *eset;
337 char *me = "_nscd_check_client_read_priv";
338 priv_set_t *zs; /* zone */
340 if (door_ucred(&uc) != 0) {
341 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
342 (me, "door_ucred: %s\n", strerror(errno));
343 return (-1);
346 if (ucred_geteuid(uc) == 0) {
347 ucred_free(uc);
348 return (0);
351 eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
352 switch (required_priv) {
353 case NSCD_ALL_PRIV:
354 zs = priv_str_to_set("zone", ",", NULL);
355 if (!priv_isequalset(eset, zs)) {
356 _NSCD_LOG(NSCD_LOG_FRONT_END,
357 NSCD_LOG_LEVEL_ERROR)
358 (me, "missing all zones privileges\n");
359 rc = -1;
361 priv_freeset(zs);
362 break;
363 case NSCD_READ_PRIV:
364 if (!priv_ismember(eset, PRIV_FILE_DAC_READ))
365 rc = -1;
366 break;
367 default:
368 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
369 (me, "unknown required_priv: %d\n", required_priv);
370 rc = -1;
371 break;
373 ucred_free(uc);
374 return (rc);
377 static void
378 N2N_check_priv(
379 void *buf,
380 char *dc_str)
382 nss_pheader_t *phdr = (nss_pheader_t *)buf;
383 ucred_t *uc = NULL;
384 const priv_set_t *eset;
385 zoneid_t zoneid;
386 int errnum;
387 char *me = "N2N_check_priv";
389 if (door_ucred(&uc) != 0) {
390 errnum = errno;
391 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
392 (me, "door_ucred: %s\n", strerror(errno));
394 NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
395 return;
398 eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
399 zoneid = ucred_getzoneid(uc);
401 if ((zoneid != GLOBAL_ZONEID && zoneid != getzoneid()) ||
402 eset != NULL ? !priv_ismember(eset, PRIV_SYS_ADMIN) :
403 ucred_geteuid(uc) != 0) {
405 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
406 (me, "%s call failed(cred): caller pid %d, uid %d, "
407 "euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
408 ucred_getruid(uc), ucred_geteuid(uc), zoneid);
409 ucred_free(uc);
411 NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
412 return;
415 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
416 (me, "nscd received %s cmd from pid %d, uid %d, "
417 "euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
418 ucred_getruid(uc), ucred_geteuid(uc), zoneid);
420 ucred_free(uc);
422 NSCD_SET_STATUS_SUCCESS(phdr);
425 void
426 _nscd_APP_check_cred(
427 void *buf,
428 pid_t *pidp,
429 char *dc_str,
430 int log_comp,
431 int log_level)
433 nss_pheader_t *phdr = (nss_pheader_t *)buf;
434 ucred_t *uc = NULL;
435 uid_t ruid;
436 uid_t euid;
437 pid_t pid;
438 int errnum;
439 char *me = "_nscd_APP_check_cred";
441 if (door_ucred(&uc) != 0) {
442 errnum = errno;
443 _NSCD_LOG(log_comp, NSCD_LOG_LEVEL_ERROR)
444 (me, "door_ucred: %s\n", strerror(errno));
446 NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
447 return;
450 NSCD_SET_STATUS_SUCCESS(phdr);
451 pid = ucred_getpid(uc);
452 if (NSS_PACKED_CRED_CHECK(buf, ruid = ucred_getruid(uc),
453 euid = ucred_geteuid(uc))) {
454 if (pidp != NULL) {
455 if (*pidp == (pid_t)-1)
456 *pidp = pid;
457 else if (*pidp != pid) {
458 NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
461 } else {
462 NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
465 ucred_free(uc);
467 if (NSCD_STATUS_IS_NOT_OK(phdr)) {
468 _NSCD_LOG(log_comp, log_level)
469 (me, "%s call failed: caller pid %d (input pid = %d), ruid %d, "
470 "euid %d, header ruid %d, header euid %d\n", dc_str,
471 pid, (pidp != NULL) ? *pidp : -1, ruid, euid,
472 ((nss_pheader_t *)(buf))->p_ruid,
473 ((nss_pheader_t *)(buf))->p_euid);
477 /* log error and return -1 when an invalid packed buffer header is found */
478 static int
479 pheader_error(nss_pheader_t *phdr, uint32_t call_number)
481 char *call_num_str;
483 switch (call_number) {
484 case NSCD_SEARCH:
485 call_num_str = "NSCD_SEARCH";
486 break;
487 case NSCD_SETENT:
488 call_num_str = "NSCD_SETENT";
489 break;
490 case NSCD_GETENT:
491 call_num_str = "NSCD_GETENT";
492 break;
493 case NSCD_ENDENT:
494 call_num_str = "NSCD_ENDENT";
495 break;
496 case NSCD_PUT:
497 call_num_str = "NSCD_PUT";
498 break;
499 case NSCD_GETHINTS:
500 call_num_str = "NSCD_GETHINTS";
501 break;
502 default:
503 call_num_str = "UNKNOWN";
504 break;
507 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
508 ("pheader_error", "call number %s: invalid packed buffer header\n",
509 call_num_str);
511 NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
512 return (-1);
516 * Validate the header of a getXbyY or setent/getent/endent request.
517 * Return 0 if good, -1 otherwise.
519 * A valid header looks like the following (size is arg_size, does
520 * not include the output area):
521 * +----------------------------------+ --
522 * | nss_pheader_t (header fixed part)| ^
523 * | | |
524 * | pbufsiz, dbd,off, key_off, | len = sizeof(nss_pheader_t)
525 * | data_off .... | |
526 * | | v
527 * +----------------------------------+ <----- dbd_off
528 * | dbd (database description) | ^
529 * | nss_dbd_t + up to 3 strings | |
530 * | length = sizeof(nss_dbd_t) + | len = key_off - dbd_off
531 * | length of 3 strings + | |
532 * | length of padding | |
533 * | (total length in multiple of 4) | v
534 * +----------------------------------+ <----- key_off
535 * | lookup key | ^
536 * | nss_XbyY_key_t, content varies, | |
537 * | based on database and lookup op | len = data_off - key_off
538 * | length = data_off - key_off | |
539 * | including padding, multiple of 4 | v
540 * +----------------------------------+ <----- data_off (= arg_size)
541 * | | ^
542 * | area to hold results | |
543 * | | len = data_len (= pbufsiz -
544 * | | | data_off)
545 * | | v
546 * +----------------------------------+ <----- pbufsiz
548 static int
549 validate_pheader(
550 void *argp,
551 size_t arg_size,
552 uint32_t call_number)
554 nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp;
555 nssuint_t l1, l2;
558 * current version is NSCD_HEADER_REV, length of the fixed part
559 * of the header must match the size of nss_pheader_t
561 if (phdr->p_version != NSCD_HEADER_REV ||
562 phdr->dbd_off != sizeof (nss_pheader_t))
563 return (pheader_error(phdr, call_number));
566 * buffer size and offsets must be in multiple of 4
568 if ((arg_size & 3) || (phdr->dbd_off & 3) || (phdr->key_off & 3) ||
569 (phdr->data_off & 3))
570 return (pheader_error(phdr, call_number));
573 * the input arg_size is the length of the request header
574 * and should be less than NSCD_PHDR_MAXLEN
576 if (phdr->data_off != arg_size || arg_size > NSCD_PHDR_MAXLEN)
577 return (pheader_error(phdr, call_number));
579 /* get length of the dbd area */
580 l1 = phdr->key_off - phdr-> dbd_off;
583 * dbd area may contain padding, so length of dbd should
584 * not be less than the length of the actual data
586 if (l1 < phdr->dbd_len)
587 return (pheader_error(phdr, call_number));
589 /* get length of the key area */
590 l2 = phdr->data_off - phdr->key_off;
593 * key area may contain padding, so length of key area should
594 * not be less than the length of the actual data
596 if (l2 < phdr->key_len)
597 return (pheader_error(phdr, call_number));
600 * length of fixed part + lengths of dbd and key area = length of
601 * the request header
603 if (sizeof (nss_pheader_t) + l1 + l2 != phdr->data_off)
604 return (pheader_error(phdr, call_number));
606 /* header length + data length = buffer length */
607 if (phdr->data_off + phdr->data_len != phdr->pbufsiz)
608 return (pheader_error(phdr, call_number));
610 return (0);
613 /* log error and return -1 when an invalid nscd to nscd buffer is found */
614 static int
615 N2Nbuf_error(nss_pheader_t *phdr, uint32_t call_number)
617 char *call_num_str;
619 switch (call_number) {
620 case NSCD_PING:
621 call_num_str = "NSCD_PING";
622 break;
624 case NSCD_IMHERE:
625 call_num_str = "NSCD_IMHERE";
626 break;
628 case NSCD_PULSE:
629 call_num_str = "NSCD_PULSE";
630 break;
632 case NSCD_FORK:
633 call_num_str = "NSCD_FORK";
634 break;
636 case NSCD_KILL:
637 call_num_str = "NSCD_KILL";
638 break;
640 case NSCD_REFRESH:
641 call_num_str = "NSCD_REFRESH";
642 break;
644 case NSCD_GETPUADMIN:
645 call_num_str = "NSCD_GETPUADMIN";
646 break;
648 case NSCD_GETADMIN:
649 call_num_str = "NSCD_GETADMIN";
650 break;
652 case NSCD_SETADMIN:
653 call_num_str = "NSCD_SETADMIN";
654 break;
656 case NSCD_KILLSERVER:
657 call_num_str = "NSCD_KILLSERVER";
658 break;
659 default:
660 call_num_str = "UNKNOWN";
661 break;
664 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
665 ("N2Nbuf_error", "call number %s: invalid N2N buffer\n", call_num_str);
667 NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
668 NSCD_DOOR_BUFFER_CHECK_FAILED);
670 return (-1);
674 * Validate the buffer of an nscd to nscd request.
675 * Return 0 if good, -1 otherwise.
677 * A valid buffer looks like the following (size is arg_size):
678 * +----------------------------------+ --
679 * | nss_pheader_t (header fixed part)| ^
680 * | | |
681 * | pbufsiz, dbd,off, key_off, | len = sizeof(nss_pheader_t)
682 * | data_off .... | |
683 * | | v
684 * +----------------------------------+ <---dbd_off = key_off = data_off
685 * | | ^
686 * | input data/output data | |
687 * | OR no data | len = data_len (= pbufsiz -
688 * | | | data_off)
689 * | | | len could be zero
690 * | | v
691 * +----------------------------------+ <--- pbufsiz
693 static int
694 validate_N2Nbuf(
695 void *argp,
696 size_t arg_size,
697 uint32_t call_number)
699 nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp;
702 * current version is NSCD_HEADER_REV, length of the fixed part
703 * of the header must match the size of nss_pheader_t
705 if (phdr->p_version != NSCD_HEADER_REV ||
706 phdr->dbd_off != sizeof (nss_pheader_t))
707 return (N2Nbuf_error(phdr, call_number));
710 * There are no dbd and key data, so the dbd, key, data
711 * offsets should be equal
713 if (phdr->dbd_off != phdr->key_off ||
714 phdr->dbd_off != phdr->data_off)
715 return (N2Nbuf_error(phdr, call_number));
718 * the input arg_size is the buffer length and should
719 * be less or equal than NSCD_N2NBUF_MAXLEN
721 if (phdr->pbufsiz != arg_size || arg_size > NSCD_N2NBUF_MAXLEN)
722 return (N2Nbuf_error(phdr, call_number));
724 /* header length + data length = buffer length */
725 if (phdr->data_off + phdr->data_len != phdr->pbufsiz)
726 return (N2Nbuf_error(phdr, call_number));
728 return (0);
731 static void
732 lookup(char *argp, size_t arg_size)
734 nsc_lookup_args_t largs;
735 char space[NSCD_LOOKUP_BUFSIZE];
736 nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp;
738 NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space,
739 sizeof (space));
742 * make sure the first couple bytes of the data area is null,
743 * so that bad strings in the packed header stop here
745 (void) memset((char *)phdr + phdr->data_off, 0, 16);
747 (void) memset(&largs, 0, sizeof (largs));
748 largs.buffer = argp;
749 largs.bufsize = arg_size;
750 nsc_lookup(&largs, 0);
753 * only the PUN needs to keep track of the
754 * activity count to determine when to
755 * terminate itself
757 if (_whoami == NSCD_CHILD) {
758 (void) mutex_lock(&activity_lock);
759 ++activity;
760 (void) mutex_unlock(&activity_lock);
763 NSCD_SET_RETURN_ARG(phdr, arg_size);
764 (void) door_return(argp, arg_size, NULL, 0);
767 static void
768 getent(char *argp, size_t arg_size)
770 char space[NSCD_LOOKUP_BUFSIZE];
771 nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp;
773 NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space, sizeof (space));
775 nss_pgetent(argp, arg_size);
777 NSCD_SET_RETURN_ARG(phdr, arg_size);
778 (void) door_return(argp, arg_size, NULL, 0);
781 static int
782 is_db_per_user(void *buf, char *dblist)
784 nss_pheader_t *phdr = (nss_pheader_t *)buf;
785 nss_dbd_t *pdbd;
786 char *dbname, *dbn;
787 int len;
789 /* copy db name into a temp buffer */
790 pdbd = (nss_dbd_t *)((void *)((char *)buf + phdr->dbd_off));
791 dbname = (char *)pdbd + pdbd->o_name;
792 len = strlen(dbname);
793 dbn = alloca(len + 2);
794 (void) memcpy(dbn, dbname, len);
796 /* check if <dbname> + ',' can be found in the dblist string */
797 dbn[len] = ',';
798 dbn[len + 1] = '\0';
799 if (strstr(dblist, dbn) != NULL)
800 return (1);
803 * check if <dbname> can be found in the last part
804 * of the dblist string
806 dbn[len] = '\0';
807 if (strstr(dblist, dbn) != NULL)
808 return (1);
810 return (0);
814 * Check to see if all conditions are met for processing per-user
815 * requests. Returns 1 if yes, -1 if backend is not configured,
816 * 0 otherwise.
818 static int
819 need_per_user_door(void *buf, int whoami, uid_t uid, char **dblist)
821 nss_pheader_t *phdr = (nss_pheader_t *)buf;
823 NSCD_SET_STATUS_SUCCESS(phdr);
825 /* if already a per-user nscd, no need to get per-user door */
826 if (whoami == NSCD_CHILD)
827 return (0);
829 /* forker shouldn't be asked */
830 if (whoami == NSCD_FORKER) {
831 NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
832 return (0);
835 /* if door client is root, no need for a per-user door */
836 if (uid == 0)
837 return (0);
840 * if per-user lookup is not configured, no per-user
841 * door available
843 if (_nscd_is_self_cred_on(0, dblist) == 0)
844 return (-1);
847 * if per-user lookup is not configured for the db,
848 * don't bother
850 if (is_db_per_user(phdr, *dblist) == 0)
851 return (0);
853 return (1);
856 static void
857 if_selfcred_return_per_user_door(char *argp, size_t arg_size,
858 door_desc_t *dp, int whoami)
860 nss_pheader_t *phdr = (nss_pheader_t *)((void *)argp);
861 char *dblist;
862 int door = -1;
863 int rc = 0;
864 door_desc_t desc;
865 char *space;
866 int len;
869 * check to see if self-cred is configured and
870 * need to return an alternate PUN door
872 if (per_user_is_on == 1) {
873 rc = need_per_user_door(argp, whoami,
874 _nscd_get_client_euid(), &dblist);
875 if (rc == -1)
876 per_user_is_on = 0;
878 if (rc <= 0) {
880 * self-cred not configured, and no error detected,
881 * return to continue the door call processing
883 if (NSCD_STATUS_IS_OK(phdr))
884 return;
885 else
887 * configured but error detected,
888 * stop the door call processing
890 (void) door_return(argp, phdr->data_off, NULL, 0);
893 /* get the alternate PUN door */
894 _nscd_proc_alt_get(argp, &door);
895 if (NSCD_GET_STATUS(phdr) != NSS_ALTRETRY) {
896 (void) door_return(argp, phdr->data_off, NULL, 0);
899 /* return the alternate door descriptor */
900 len = strlen(dblist) + 1;
901 space = alloca(arg_size + len);
902 phdr->data_len = len;
903 (void) memcpy(space, phdr, arg_size);
904 (void) strncpy((char *)space + arg_size, dblist, len);
905 dp = &desc;
906 dp->d_attributes = DOOR_DESCRIPTOR;
907 dp->d_data.d_desc.d_descriptor = door;
908 arg_size += len;
909 (void) door_return(space, arg_size, dp, 1);
912 /*ARGSUSED*/
913 static void
914 switcher(void *cookie, char *argp, size_t arg_size,
915 door_desc_t *dp, uint_t n_desc)
917 int iam;
918 pid_t ent_pid = -1;
919 nss_pheader_t *phdr = (nss_pheader_t *)((void *)argp);
920 void *uptr;
921 int len;
922 size_t buflen;
923 int callnum;
924 char *me = "switcher";
926 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
927 (me, "switcher ...\n");
929 if (argp == DOOR_UNREF_DATA) {
930 (void) printf("Door Slam... exiting\n");
931 exit(0);
934 if (argp == NULL) { /* empty door call */
935 (void) door_return(NULL, 0, 0, 0); /* return the favor */
939 * need to restart if main nscd and config file(s) changed
941 if (_whoami == NSCD_MAIN)
942 _nscd_restart_if_cfgfile_changed();
944 if ((phdr->nsc_callnumber & NSCDV2CATMASK) == NSCD_CALLCAT_APP) {
946 /* make sure the packed buffer header is good */
947 if (validate_pheader(argp, arg_size,
948 phdr->nsc_callnumber) == -1)
949 (void) door_return(argp, arg_size, NULL, 0);
951 switch (phdr->nsc_callnumber) {
953 case NSCD_SEARCH:
955 /* if a fallback to main nscd, skip per-user setup */
956 if (phdr->p_status != NSS_ALTRETRY)
957 if_selfcred_return_per_user_door(argp, arg_size,
958 dp, _whoami);
959 lookup(argp, arg_size);
961 break;
963 case NSCD_SETENT:
965 _nscd_APP_check_cred(argp, &ent_pid, "NSCD_SETENT",
966 NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT);
967 if (NSCD_STATUS_IS_OK(phdr)) {
968 if_selfcred_return_per_user_door(argp, arg_size,
969 dp, _whoami);
970 nss_psetent(argp, arg_size, ent_pid);
972 break;
974 case NSCD_GETENT:
976 getent(argp, arg_size);
977 break;
979 case NSCD_ENDENT:
981 nss_pendent(argp, arg_size);
982 break;
984 case NSCD_PUT:
986 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
987 (me, "door call NSCD_PUT not supported yet\n");
989 NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
990 break;
992 case NSCD_GETHINTS:
994 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
995 (me, "door call NSCD_GETHINTS not supported yet\n");
997 NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
998 break;
1000 default:
1002 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1003 (me, "Unknown name service door call op %x\n",
1004 phdr->nsc_callnumber);
1006 NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
1007 break;
1010 (void) door_return(argp, arg_size, NULL, 0);
1013 iam = NSCD_MAIN;
1014 callnum = phdr->nsc_callnumber & ~NSCD_WHOAMI;
1015 if (callnum == NSCD_IMHERE ||
1016 callnum == NSCD_PULSE || callnum == NSCD_FORK)
1017 iam = phdr->nsc_callnumber & NSCD_WHOAMI;
1018 else
1019 callnum = phdr->nsc_callnumber;
1021 /* nscd -> nscd v2 calls */
1023 /* make sure the buffer is good */
1024 if (validate_N2Nbuf(argp, arg_size, callnum) == -1)
1025 (void) door_return(argp, arg_size, NULL, 0);
1027 switch (callnum) {
1029 case NSCD_PING:
1030 NSCD_SET_STATUS_SUCCESS(phdr);
1031 break;
1033 case NSCD_IMHERE:
1034 _nscd_proc_iamhere(argp, dp, n_desc, iam);
1035 break;
1037 case NSCD_PULSE:
1038 N2N_check_priv(argp, "NSCD_PULSE");
1039 if (NSCD_STATUS_IS_OK(phdr))
1040 _nscd_proc_pulse(argp, iam);
1041 break;
1043 case NSCD_FORK:
1044 N2N_check_priv(argp, "NSCD_FORK");
1045 if (NSCD_STATUS_IS_OK(phdr))
1046 _nscd_proc_fork(argp, iam);
1047 break;
1049 case NSCD_KILL:
1050 N2N_check_priv(argp, "NSCD_KILL");
1051 if (NSCD_STATUS_IS_OK(phdr))
1052 exit(0);
1053 break;
1055 case NSCD_REFRESH:
1056 N2N_check_priv(argp, "NSCD_REFRESH");
1057 if (NSCD_STATUS_IS_OK(phdr)) {
1058 if (_nscd_refresh() != NSCD_SUCCESS)
1059 exit(1);
1060 NSCD_SET_STATUS_SUCCESS(phdr);
1062 break;
1064 case NSCD_GETPUADMIN:
1066 if (_nscd_is_self_cred_on(0, NULL)) {
1067 _nscd_peruser_getadmin(argp, sizeof (nscd_admin_t));
1068 } else {
1069 NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
1070 NSCD_SELF_CRED_NOT_CONFIGURED);
1072 break;
1074 case NSCD_GETADMIN:
1076 len = _nscd_door_getadmin((void *)argp);
1077 if (len == 0)
1078 break;
1080 /* size of door buffer not big enough, allocate one */
1081 NSCD_ALLOC_DOORBUF(NSCD_GETADMIN, len, uptr, buflen);
1083 /* copy packed header */
1084 *(nss_pheader_t *)uptr = *(nss_pheader_t *)((void *)argp);
1086 /* set new buffer size */
1087 ((nss_pheader_t *)uptr)->pbufsiz = buflen;
1089 /* try one more time */
1090 (void) _nscd_door_getadmin((void *)uptr);
1091 (void) door_return(uptr, buflen, NULL, 0);
1092 break;
1094 case NSCD_SETADMIN:
1095 N2N_check_priv(argp, "NSCD_SETADMIN");
1096 if (NSCD_STATUS_IS_OK(phdr))
1097 _nscd_door_setadmin(argp);
1098 break;
1100 case NSCD_KILLSERVER:
1101 N2N_check_priv(argp, "NSCD_KILLSERVER");
1102 if (NSCD_STATUS_IS_OK(phdr)) {
1103 /* also kill the forker nscd if one is running */
1104 _nscd_kill_forker();
1105 exit(0);
1107 break;
1109 default:
1110 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1111 (me, "Unknown name service door call op %d\n",
1112 phdr->nsc_callnumber);
1114 NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
1116 (void) door_return(argp, arg_size, NULL, 0);
1117 break;
1120 (void) door_return(argp, arg_size, NULL, 0);
1124 _nscd_setup_server(char *execname, char **argv)
1127 int fd;
1128 int errnum;
1129 int bind_failed = 0;
1130 mode_t old_mask;
1131 struct stat buf;
1132 sigset_t myset;
1133 struct sigaction action;
1134 char *me = "_nscd_setup_server";
1136 main_execname = execname;
1137 main_argv = argv;
1139 /* Any nscd process is to ignore SIGPIPE */
1140 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
1141 errnum = errno;
1142 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1143 (me, "signal (SIGPIPE): %s\n", strerror(errnum));
1144 return (-1);
1147 keep_open_dns_socket();
1150 * the max number of server threads should be fixed now, so
1151 * set flag to indicate that no in-flight change is allowed
1153 max_servers_set = 1;
1155 (void) thr_keycreate(&lookup_state_key, NULL);
1156 (void) sema_init(&common_sema, frontend_cfg_g.common_worker_threads,
1157 USYNC_THREAD, 0);
1159 /* Establish server thread pool */
1160 (void) door_server_create(server_create);
1161 if (thr_keycreate(&server_key, server_destroy) != 0) {
1162 errnum = errno;
1163 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1164 (me, "thr_keycreate (server thread): %s\n",
1165 strerror(errnum));
1166 return (-1);
1169 /* Create a door */
1170 if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
1171 DOOR_UNREF | DOOR_NO_CANCEL)) < 0) {
1172 errnum = errno;
1173 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1174 (me, "door_create: %s\n", strerror(errnum));
1175 return (-1);
1178 /* if not main nscd, no more setup to do */
1179 if (_whoami != NSCD_MAIN)
1180 return (fd);
1182 /* bind to file system */
1183 if (stat(NAME_SERVICE_DOOR, &buf) < 0) {
1184 int newfd;
1186 /* make sure the door will be readable by all */
1187 old_mask = umask(0);
1188 if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) {
1189 errnum = errno;
1190 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1191 (me, "Cannot create %s: %s\n", NAME_SERVICE_DOOR,
1192 strerror(errnum));
1193 bind_failed = 1;
1195 /* rstore the old file mode creation mask */
1196 (void) umask(old_mask);
1197 (void) close(newfd);
1200 if (bind_failed == 1) {
1201 (void) door_revoke(fd);
1202 return (-1);
1205 if (fattach(fd, NAME_SERVICE_DOOR) < 0) {
1206 if ((errno != EBUSY) ||
1207 (fdetach(NAME_SERVICE_DOOR) < 0) ||
1208 (fattach(fd, NAME_SERVICE_DOOR) < 0)) {
1209 errnum = errno;
1210 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1211 (me, "fattach: %s\n", strerror(errnum));
1212 (void) door_revoke(fd);
1213 return (-1);
1218 * kick off routing socket monitor thread
1220 if (thr_create(NULL, 0,
1221 (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
1222 errnum = errno;
1223 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1224 (me, "thr_create (routing socket monitor): %s\n",
1225 strerror(errnum));
1227 (void) door_revoke(fd);
1228 return (-1);
1232 * set up signal handler for SIGHUP
1234 action.sa_handler = dozip;
1235 action.sa_flags = 0;
1236 (void) sigemptyset(&action.sa_mask);
1237 (void) sigemptyset(&myset);
1238 (void) sigaddset(&myset, SIGHUP);
1240 if (sigaction(SIGHUP, &action, NULL) < 0) {
1241 errnum = errno;
1242 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1243 (me, "sigaction (SIGHUP): %s\n", strerror(errnum));
1245 (void) door_revoke(fd);
1246 return (-1);
1249 return (fd);
1253 _nscd_setup_child_server(int did)
1256 int errnum;
1257 int fd;
1258 nscd_rc_t rc;
1259 char *me = "_nscd_setup_child_server";
1261 /* Re-establish our own server thread pool */
1262 (void) door_server_create(server_create);
1263 if (thr_keycreate(&server_key, server_destroy) != 0) {
1264 errnum = errno;
1265 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
1266 (me, "thr_keycreate failed: %s", strerror(errnum));
1267 return (-1);
1271 * Create a new door.
1272 * Keep DOOR_REFUSE_DESC (self-cred nscds don't fork)
1274 (void) close(did);
1275 if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
1276 DOOR_REFUSE_DESC|DOOR_UNREF|DOOR_NO_CANCEL)) < 0) {
1277 errnum = errno;
1278 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
1279 (me, "door_create failed: %s", strerror(errnum));
1280 return (-1);
1284 * kick off routing socket monitor thread
1286 if (thr_create(NULL, 0,
1287 (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
1288 errnum = errno;
1289 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1290 (me, "thr_create (routing socket monitor): %s\n",
1291 strerror(errnum));
1292 (void) door_revoke(fd);
1293 return (-1);
1297 * start monitoring the states of the name service clients
1299 rc = _nscd_init_smf_monitor();
1300 if (rc != NSCD_SUCCESS) {
1301 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1302 (me, "unable to start the SMF monitor (rc = %d)\n", rc);
1304 (void) door_revoke(fd);
1305 return (-1);
1308 return (fd);
1311 nscd_rc_t
1312 _nscd_alloc_frontend_cfg()
1314 frontend_cfg = calloc(NSCD_NUM_DB, sizeof (nscd_cfg_frontend_t));
1315 if (frontend_cfg == NULL)
1316 return (NSCD_NO_MEMORY);
1318 return (NSCD_SUCCESS);
1322 /* ARGSUSED */
1323 nscd_rc_t
1324 _nscd_cfg_frontend_notify(
1325 void *data,
1326 struct nscd_cfg_param_desc *pdesc,
1327 nscd_cfg_id_t *nswdb,
1328 nscd_cfg_flag_t dflag,
1329 nscd_cfg_error_t **errorp,
1330 void *cookie)
1332 void *dp;
1335 * At init time, the whole group of config params are received.
1336 * At update time, group or individual parameter value could
1337 * be received.
1340 if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_INIT) ||
1341 _nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {
1343 * group data is received, copy in the
1344 * entire strcture
1346 if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
1347 frontend_cfg_g = *(nscd_cfg_global_frontend_t *)data;
1348 else
1349 frontend_cfg[nswdb->index] =
1350 *(nscd_cfg_frontend_t *)data;
1352 } else {
1354 * individual paramater is received: copy in the
1355 * parameter value.
1357 if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
1358 dp = (char *)&frontend_cfg_g + pdesc->p_offset;
1359 else
1360 dp = (char *)&frontend_cfg[nswdb->index] +
1361 pdesc->p_offset;
1362 (void) memcpy(dp, data, pdesc->p_size);
1365 return (NSCD_SUCCESS);
1368 /* ARGSUSED */
1369 nscd_rc_t
1370 _nscd_cfg_frontend_verify(
1371 void *data,
1372 struct nscd_cfg_param_desc *pdesc,
1373 nscd_cfg_id_t *nswdb,
1374 nscd_cfg_flag_t dflag,
1375 nscd_cfg_error_t **errorp,
1376 void **cookie)
1379 char *me = "_nscd_cfg_frontend_verify";
1382 * if max. number of server threads is set and in effect,
1383 * don't allow changing of the frontend configuration
1385 if (max_servers_set) {
1386 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
1387 (me, "changing of the frontend configuration not allowed now");
1389 return (NSCD_CFG_CHANGE_NOT_ALLOWED);
1392 return (NSCD_SUCCESS);
1395 /* ARGSUSED */
1396 nscd_rc_t
1397 _nscd_cfg_frontend_get_stat(
1398 void **stat,
1399 struct nscd_cfg_stat_desc *sdesc,
1400 nscd_cfg_id_t *nswdb,
1401 nscd_cfg_flag_t *dflag,
1402 void (**free_stat)(void *stat),
1403 nscd_cfg_error_t **errorp)
1405 return (NSCD_SUCCESS);
1408 void
1409 _nscd_init_cache_sema(sema_t *sema, char *cache_name)
1411 int i, j;
1412 char *dbn;
1414 if (max_servers == 0)
1415 max_servers = frontend_cfg_g.common_worker_threads +
1416 frontend_cfg_g.cache_hit_threads;
1418 for (i = 0; i < NSCD_NUM_DB; i++) {
1420 dbn = NSCD_NSW_DB_NAME(i);
1421 if (strcasecmp(dbn, cache_name) == 0) {
1422 j = frontend_cfg[i].worker_thread_per_nsw_db;
1423 (void) sema_init(sema, j, USYNC_THREAD, 0);
1424 max_servers += j;
1425 break;
1431 * Monitor the routing socket. Address lists stored in the ipnodes
1432 * cache are sorted based on destination address selection rules,
1433 * so when things change that could affect that sorting (interfaces
1434 * go up or down, flags change, etc.), we clear that cache so the
1435 * list will be re-ordered the next time the hostname is resolved.
1437 static void
1438 rts_mon(void)
1440 int rt_sock, rdlen, idx;
1441 union {
1442 struct {
1443 struct rt_msghdr rtm;
1444 struct sockaddr_storage addrs[RTA_NUMBITS];
1445 } r;
1446 struct if_msghdr ifm;
1447 struct ifa_msghdr ifam;
1448 } mbuf;
1449 struct ifa_msghdr *ifam = &mbuf.ifam;
1450 char *me = "rts_mon";
1452 rt_sock = socket(PF_ROUTE, SOCK_RAW, 0);
1453 if (rt_sock < 0) {
1454 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1455 (me, "Failed to open routing socket: %s\n", strerror(errno));
1456 thr_exit(0);
1459 for (;;) {
1460 rdlen = read(rt_sock, &mbuf, sizeof (mbuf));
1461 if (rdlen <= 0) {
1462 if (rdlen == 0 || (errno != EINTR && errno != EAGAIN)) {
1463 _NSCD_LOG(NSCD_LOG_FRONT_END,
1464 NSCD_LOG_LEVEL_ERROR)
1465 (me, "routing socket read: %s\n",
1466 strerror(errno));
1467 thr_exit(0);
1469 continue;
1471 if (ifam->ifam_version != RTM_VERSION) {
1472 _NSCD_LOG(NSCD_LOG_FRONT_END,
1473 NSCD_LOG_LEVEL_ERROR)
1474 (me, "rx unknown version (%d) on "
1475 "routing socket.\n",
1476 ifam->ifam_version);
1477 continue;
1479 switch (ifam->ifam_type) {
1480 case RTM_NEWADDR:
1481 case RTM_DELADDR:
1482 /* if no ipnodes cache, then nothing to do */
1483 idx = get_cache_idx("ipnodes");
1484 if (cache_ctx_p[idx] == NULL ||
1485 cache_ctx_p[idx]->reaper_on != nscd_true)
1486 break;
1487 nsc_invalidate(cache_ctx_p[idx], NULL, NULL);
1488 break;
1489 case RTM_ADD:
1490 case RTM_DELETE:
1491 case RTM_CHANGE:
1492 case RTM_GET:
1493 case RTM_LOSING:
1494 case RTM_REDIRECT:
1495 case RTM_MISS:
1496 case RTM_LOCK:
1497 case RTM_OLDADD:
1498 case RTM_OLDDEL:
1499 case RTM_RESOLVE:
1500 case RTM_IFINFO:
1501 case RTM_CHGADDR:
1502 case RTM_FREEADDR:
1503 break;
1504 default:
1505 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1506 (me, "rx unknown msg type (%d) on routing socket.\n",
1507 ifam->ifam_type);
1508 break;
1513 static void
1514 keep_open_dns_socket(void)
1516 _res.options |= RES_STAYOPEN; /* just keep this udp socket open */