xref: /illumos-gate/usr/src/cmd/ypcmd/yp_b_subr.c (revision 1f5207b7604fb44407eb4342aff613f7c4508508)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /*	  All Rights Reserved   */
29 
30 /*
31  * Portions of this source code were derived from Berkeley
32  * under license from the Regents of the University of
33  * California.
34  */
35 
36 #pragma ident	"%Z%%M%	%I%	%E% SMI"
37 
38 #include "ypsym.h"
39 #include <stdlib.h>
40 #include "yp_b.h"
41 #include <string.h>
42 #include <limits.h>
43 #include <netconfig.h>
44 #include <netdir.h>
45 #include <rpc/clnt.h>
46 #include <syslog.h>
47 #include <sys/time.h>
48 #include <unistd.h>
49 #include <netinet/in.h>
50 #include <sys/statvfs.h>
51 #include <rpcsvc/nis.h>
52 #include <sys/systeminfo.h>
53 
54 #ifndef NULL
55 #define	NULL	0
56 #endif
57 
58 #define	YPSERVERS	"ypservers"
59 
60 void ypbind_init_default();
61 static int ypbind_pipe_setdom();
62 
63 static bool firsttime = TRUE;
64 static struct domain *known_domains;
65 
66 extern struct netconfig *__rpc_getconf();
67 extern void *__rpc_setconf(), *__rpc_endconf();
68 extern CLIENT *__clnt_tp_create_bootstrap();
69 extern char *inet_ntoa();
70 extern int __rpc_get_local_uid();
71 
72 extern listofnames *names();
73 extern void free_listofnames();
74 
75 #define	PINGTIME	10	/* Timeout for the ypservers list */
76 #define	PINGTOTTIM 5	/* Total seconds for ping timeout */
77 
78 static void broadcast_setup();
79 static void sigcld_handler();
80 static struct ypbind_binding *dup_ypbind_binding();
81 static struct netbuf *dup_netbuf();
82 static void free_ypbind_binding();
83 static void enable_exit();
84 static void ypbind_ping();
85 static struct domain *ypbind_point_to_domain();
86 static bool ypbind_broadcast_ack();
87 static int pong_servers();
88 void cache_binding();
89 void uncache_binding();
90 
91 extern int setok;
92 extern int broadcast;
93 extern int cache_okay;
94 
95 /*
96  * Need to differentiate between RPC_UNKNOWNHOST returned by the RPC
97  * library, and the same error caused by a local lookup failure in
98  * /etc/hosts and/or /etc/inet/ipnodes.
99  */
100 int hostNotKnownLocally;
101 
102 /*ARGSUSED*/
103 void	*
104 ypbindproc_null_3(argp, clnt)
105 void	*argp;
106 CLIENT *clnt;
107 {
108 	static char	res;
109 
110 	return ((void *) & res);
111 }
112 
113 static void
114 enable_exit()
115 {
116 	static bool	done = FALSE;
117 
118 	if (!done) {
119 		done = TRUE;
120 		sigset(SIGCHLD, (void (*)())sigcld_handler);
121 	}
122 }
123 
124 int sigcld_event = 0;
125 
126 static void
127 sigcld_handler()
128 {
129 	sigcld_event++;
130 #ifdef DEBUG
131 	fprintf(stderr, "ypbind sighandler: got SIGCLD signal (event=%d)\n",
132 		sigcld_event);
133 #endif
134 }
135 
136 
137 /*
138  * This is a Unix SIGCHILD handler that notices when a broadcaster child
139  * process has exited, and retrieves the exit status.  The broadcaster pid
140  * is set to 0.  If the broadcaster succeeded, dom_report_success will be
141  * be set to -1.
142  */
143 
144 void
145 broadcast_proc_exit()
146 {
147 	int pid, ret;
148 	siginfo_t infop;
149 	register struct domain *pdom;
150 	bool	succeeded = FALSE;
151 
152 	sigcld_event = 0;
153 					/* ==== Why WEXITED? */
154 	while ((ret = waitid(P_ALL, 0, &infop, WNOHANG | WEXITED)) != -1) {
155 		switch (infop.si_code) {
156 		case CLD_EXITED:
157 			succeeded = infop.si_status == 0;
158 			break;
159 		case CLD_KILLED:
160 		case CLD_DUMPED:
161 			succeeded = FALSE;
162 			break;
163 		case CLD_TRAPPED:
164 		case CLD_STOPPED:
165 		case CLD_CONTINUED:
166 			enable_exit();
167 			return;
168 		}
169 		pid = infop.si_pid;
170 
171 #ifdef DEBUG
172 		fprintf(stderr,
173 			"ypbind event_handler: got wait from %d status = %d\n",
174 			pid, infop.si_status);
175 #endif
176 
177 	/* to aid the progeny print the infamous "not responding" message */
178 		firsttime = FALSE;
179 
180 		for (pdom = known_domains; pdom != (struct domain *)NULL;
181 				pdom = pdom->dom_pnext) {
182 
183 			if (pdom->dom_broadcaster_pid == pid) {
184 #ifdef DEBUG
185 			fprintf(stderr,
186 			"ypbind event_handler: got match %s\n", pdom->dom_name);
187 #endif
188 				if (succeeded) {
189 					broadcast_setup(pdom);
190 				}
191 				if (pdom->broadcaster_pipe != 0) {
192 					xdr_destroy(&(pdom->broadcaster_xdr));
193 					fclose(pdom->broadcaster_pipe);
194 					pdom->broadcaster_pipe = 0;
195 					pdom->broadcaster_fd = -1;
196 				}
197 				pdom->dom_broadcaster_pid = 0;
198 
199 				break;
200 			}
201 		}
202 	}	/* while loop */
203 	enable_exit();
204 }
205 
206 static void
207 broadcast_setup(pdom)
208 struct domain *pdom;
209 {
210 	ypbind_setdom req;
211 
212 	memset(&req, 0, sizeof (req));
213 	if (pdom->broadcaster_pipe) {
214 		pdom->dom_report_success = -1;
215 		if (xdr_ypbind_setdom(&(pdom->broadcaster_xdr), &req)) {
216 #ifdef DEBUG
217 	fprintf(stderr, "parent: broadcast_setup: got xdr ok \n");
218 #endif
219 			ypbindproc_setdom_3(&req, (struct svc_req *)NULL,
220 			    (SVCXPRT *)NULL);
221 			xdr_free(xdr_ypbind_setdom, (char *)&req);
222 			gettimeofday(&(pdom->lastping), NULL);
223 		}
224 #ifdef DEBUG
225 		    else {
226 	fprintf(stderr, "ypbind parent: xdr_ypbind_setdom failed\n");
227 		}
228 #endif
229 	}
230 #ifdef DEBUG
231 	    else {
232 	fprintf(stderr, "ypbind: internal error -- no broadcaster pipe\n");
233 	}
234 #endif
235 }
236 
237 #define	YPBIND_PINGHOLD_DOWN 5
238 /* Same as the ypbind_get_binding() routine in SunOS */
239 /*ARGSUSED*/
240 ypbind_resp *
241 ypbindproc_domain_3(argp, clnt)
242 ypbind_domain *argp;
243 CLIENT *clnt;
244 {
245 	static ypbind_resp resp;
246 	struct domain *cur_domain;
247 	int bpid;
248 	int fildes[2];
249 
250 	memset((char *)&resp, 0, sizeof (resp));
251 
252 #ifdef DEBUG
253 	fprintf(stderr, "\nypbindproc_domain_3: domain: %s\n",
254 		argp->ypbind_domainname);
255 #endif
256 
257 	if ((int)strlen(argp->ypbind_domainname) > YPMAXDOMAIN) {
258 
259 		resp.ypbind_status = YPBIND_FAIL_VAL;
260 		resp.ypbind_resp_u.ypbind_error = YPBIND_ERR_NOSERV;
261 		return (&resp);
262 	}
263 
264 	if ((cur_domain = ypbind_point_to_domain(argp->ypbind_domainname)) !=
265 	    (struct domain *)NULL) {
266 		if (cur_domain->dom_boundp) {
267 
268 			struct timeval tp;
269 
270 			(void) gettimeofday(&tp, NULL);
271 			if ((tp.tv_sec - cur_domain->lastping.tv_sec) >
272 					YPBIND_PINGHOLD_DOWN) {
273 #ifdef DEBUG
274 	fprintf(stderr, "domain is bound pinging: %s\n",
275 		argp->ypbind_domainname);
276 #endif
277 				(void) ypbind_ping(cur_domain);
278 			}
279 		}
280 
281 		/*
282 		 * Bound or not, return the current state of the binding.
283 		 */
284 
285 		if (cur_domain->dom_boundp) {
286 #ifdef DEBUG
287 	fprintf(stderr, "server is up for domain: %s\n",
288 		argp->ypbind_domainname);
289 #endif
290 			resp.ypbind_status = YPBIND_SUCC_VAL;
291 			resp.ypbind_resp_u.ypbind_bindinfo =
292 			    cur_domain->dom_binding;
293 		} else {
294 #ifdef DEBUG
295 	fprintf(stderr, "domain is NOT bound returning: %s %d\n",
296 		argp->ypbind_domainname, cur_domain->dom_error);
297 #endif
298 			resp.ypbind_status = YPBIND_FAIL_VAL;
299 			resp.ypbind_resp_u.ypbind_error =
300 			    cur_domain->dom_error;
301 		}
302 
303 	} else {
304 		resp.ypbind_status = YPBIND_FAIL_VAL;
305 		resp.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
306 	}
307 	/*
308 	 * RETURN NOW: if successful, otherwise
309 	 * RETURN LATER: after spawning off a child to do the "broadcast" work.
310 	 */
311 	if (resp.ypbind_status == YPBIND_SUCC_VAL) {
312 #ifdef DEBUG
313 	fprintf(stderr, "yp_b_subr: returning success to yp_b_svc %d\n",
314 		resp.ypbind_status);
315 #endif
316 		return (&resp);
317 	}
318 
319 	/* Go about the broadcast (really, pinging here) business */
320 
321 	if ((cur_domain) && (!cur_domain->dom_boundp) &&
322 	    (!cur_domain->dom_broadcaster_pid)) {
323 #ifdef DEBUG
324 	fprintf(stderr, "yp_b_subr: fork: boundp=%d broadcast_pid=%d\n",
325 		cur_domain->dom_boundp, cur_domain->dom_broadcaster_pid);
326 #endif
327 		/*
328 		 * The current domain is unbound, and there is no child
329 		 * process active now.  Fork off a child who will beg to the
330 		 * ypservers list one by one or broadcast and accept whoever
331 		 * commands the right domain.
332 		 */
333 		if (pipe(fildes) < 0) {
334 #ifdef DEBUG
335 	fprintf(stderr, "yp_b_subr: returning pipe failure to yp_b_svc %d\n",
336 		resp.ypbind_status);
337 #endif
338 			return (&resp);
339 		}
340 
341 		enable_exit();
342 		sighold(SIGCLD); /* add it to ypbind's signal mask */
343 		cur_domain->dom_report_success++;
344 		bpid = fork();
345 		if (bpid != 0) { /* parent */
346 			if (bpid > 0) { /* parent started */
347 				close(fildes[1]);
348 				cur_domain->dom_broadcaster_pid = bpid;
349 				cur_domain->broadcaster_fd = fildes[0];
350 				cur_domain->broadcaster_pipe =
351 						fdopen(fildes[0], "r");
352 				if (cur_domain->broadcaster_pipe)
353 			xdrstdio_create(&(cur_domain->broadcaster_xdr),
354 			(cur_domain->broadcaster_pipe), XDR_DECODE);
355 
356 #ifdef DEBUG
357 	fprintf(stderr, "ypbindproc_domain_3: %s starting pid = %d try = %d\n",
358 	    cur_domain->dom_name, bpid,
359 	    cur_domain->dom_report_success);
360 	fprintf(stderr, "yp_b_subr: returning after spawning, to yp_b_svc %d\n",
361 		resp.ypbind_status);
362 #endif
363 				sigrelse(SIGCLD);
364 				/* remove it from ypbind's signal mask */
365 				return (&resp);
366 			} else { /* fork failed */
367 				perror("fork");
368 				close(fildes[0]);
369 				close(fildes[1]);
370 #ifdef DEBUG
371 	fprintf(stderr, "yp_b_subr: returning fork failure to yp_b_svc %d\n",
372 		resp.ypbind_status);
373 #endif
374 				sigrelse(SIGCLD);
375 				return (&resp);
376 			}
377 		} /* end parent */
378 		/* child only code */
379 		sigrelse(SIGCLD);
380 		close(fildes[0]);
381 		cur_domain->broadcaster_fd = fildes[1];
382 		cur_domain->broadcaster_pipe = fdopen(fildes[1], "w");
383 		if (cur_domain->broadcaster_pipe)
384 			xdrstdio_create(&(cur_domain->broadcaster_xdr),
385 				(cur_domain->broadcaster_pipe), XDR_ENCODE);
386 		else {
387 			perror("fdopen-pipe");
388 			exit(-1);
389 		}
390 		exit(pong_servers(cur_domain));
391 	}
392 #ifdef DEBUG
393 	fprintf(stderr, "yp_b_subr: lazy returns failure status yp_b_svc %d\n",
394 			resp.ypbind_status);
395 #endif
396 	return (&resp);
397 }
398 
399 
400 /*
401  * call ypbindproc_domain_3 and convert results
402  *
403  * This adds support for YP clients that send requests on
404  * ypbind version 1 & 2 (i.e. clients before we started
405  * using universal addresses and netbufs). This is supported
406  * for binary compatibility for static 4.x programs. The
407  * assumption used to be that clients coming in with ypbind vers 1
408  * should be given the address of a server serving ypserv version 1.
409  * However, since yp_bind routines in 4.x YP library try
410  * to connect with ypserv version 2, even if they requested
411  * binding using ypbind version 1, the ypbind process will
412  * "always" look for only ypserv version 2 servers for all
413  * (ypbind vers 1, 2, & 3) clients.
414  */
415 ypbind_resp_2 *
416 ypbindproc_domain_2(argp, clnt)
417 domainname_2 *argp;
418 CLIENT *clnt;
419 {
420 	ypbind_domain arg_3;
421 	ypbind_resp *resp_3;
422 	static ypbind_resp_2 resp;
423 
424 	arg_3.ypbind_domainname = *argp;
425 	resp_3 = ypbindproc_domain_3(&arg_3, clnt);
426 	if (resp_3 == NULL)
427 		return (NULL);
428 	resp.ypbind_status = resp_3->ypbind_status;
429 	if (resp_3->ypbind_status == YPBIND_SUCC_VAL) {
430 		struct sockaddr_in *sin;
431 		struct ypbind_binding_2 *bi;
432 
433 		sin = (struct sockaddr_in *)
434 		    resp_3->ypbind_resp_u.ypbind_bindinfo->ypbind_svcaddr->buf;
435 		if (sin->sin_family == AF_INET) {
436 			bi = &resp.ypbind_respbody_2.ypbind_bindinfo;
437 			memcpy(&(bi->ypbind_binding_port), &sin->sin_port, 2);
438 			memcpy(&(bi->ypbind_binding_addr), &sin->sin_addr, 4);
439 		} else {
440 			resp.ypbind_respbody_2.ypbind_error = YPBIND_ERR_NOSERV;
441 		}
442 	} else {
443 		resp.ypbind_respbody_2.ypbind_error =
444 		    resp_3->ypbind_resp_u.ypbind_error;
445 	}
446 	return (&resp);
447 }
448 
449 /* used to exchange information between pong_servers and ypbind_broadcast_ack */
450 struct domain *process_current_domain;
451 
452 int
453 pong_servers(domain_struct)
454 struct domain *domain_struct; /* to pass back */
455 {
456 	char *domain = domain_struct->dom_name;
457 	CLIENT *clnt2;
458 	char *servername;
459 	listofnames *list, *lin;
460 	char serverfile[MAXNAMLEN];
461 	struct timeval timeout;
462 	int isok = 0, res = -1;
463 	struct netconfig *nconf;
464 	void *handle;
465 	int nconf_count;
466 	char rpcdomain[YPMAXDOMAIN+1];
467 	long inforet;
468 
469 	/*
470 	 * If the ``domain'' name passed in is not the same as the RPC
471 	 * domain set from /etc/defaultdomain.  Then we set ``firsttime''
472 	 * to TRUE so no error messages are ever syslog()-ed this
473 	 * prevents a possible Denial of Service attack.
474 	 */
475 	inforet = sysinfo(SI_SRPC_DOMAIN, &(rpcdomain[0]), YPMAXDOMAIN);
476 	if ((inforet > 0) && (strcmp(domain, rpcdomain) != 0))
477 		firsttime = TRUE;
478 
479 	if (broadcast) {
480 		enum clnt_stat stat = RPC_SUCCESS;
481 #ifdef DEBUG
482 	fprintf(stderr, "pong_servers: doing an rpc_broadcast\n");
483 #endif
484 		/*
485 		 * Here we do the real SunOS thing that users love. Do a
486 		 * broadcast on the network and find out the ypserv. No need
487 		 * to do "ypinit -c", no setting up /etc/hosts file, and no
488 		 * recursion looking up the server's IP address.
489 		 */
490 		process_current_domain = domain_struct;
491 		stat = rpc_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK,
492 			(xdrproc_t)xdr_ypdomain_wrap_string, (caddr_t)&domain,
493 			xdr_int,  (caddr_t)&isok,
494 			(resultproc_t)ypbind_broadcast_ack, "udp");
495 		if (stat == RPC_SYSTEMERROR || stat == RPC_UNKNOWNPROTO ||
496 			stat == RPC_CANTRECV || stat == RPC_CANTSEND ||
497 			stat == RPC_NOBROADCAST ||
498 			stat == RPC_N2AXLATEFAILURE) {
499 			syslog(LOG_ERR, "RPC/Transport subsystem failure %s\n",
500 				clnt_sperrno(stat));
501 			exit(-1);
502 		}
503 		if (domain_struct->broadcaster_pipe == 0)
504 			/* init binding case */
505 			return (domain_struct->dom_boundp - 1);
506 		if (domain_struct->dom_boundp) {
507 			res = ypbind_pipe_setdom(NULL, domain,
508 					NULL, domain_struct);
509 			if (domain_struct->dom_report_success > 0)
510 				syslog(LOG_ERR,
511 			"NIS server for domain \"%s\" OK", domain);
512 		} else if (firsttime == FALSE)
513 			syslog(LOG_ERR,
514 	"NIS server not responding for domain \"%s\"; still trying", domain);
515 		return (res);
516 	}
517 #ifdef DEBUG
518 	fprintf(stderr, "pong_servers: ponging servers one by one\n");
519 #endif
520 	/*
521 	 * Do the politically correct thing.. transport independent and
522 	 * secure (trusts only listed servers).
523 	 */
524 
525 	/*
526 	 * get list of possible servers for this domain
527 	 */
528 
529 	/*
530 	 * get alias for domain: Things of the past..
531 	 * sysvconfig();
532 	 * (void) yp_getalias(domain, domain_alias, NAME_MAX);
533 	 */
534 	sprintf(serverfile, "%s/%s/%s", BINDING, domain, YPSERVERS);
535 #ifdef DEBUG
536 	fprintf(stderr, "pong_servers: serverfile %s\n", serverfile);
537 #endif
538 	list = names(serverfile);
539 	if (list == NULL) {
540 		if (firsttime == FALSE)
541 		    syslog(LOG_ERR,
542 			"service not installed, use /usr/sbin/ypinit -c");
543 		return (-1);
544 	}
545 	lin = list;
546 	for (list = lin; list; list = list->nextname) {
547 		servername = strtok(list->name, " \t\n");
548 		if (servername == NULL) continue;
549 
550 		/* Check all datagram_v transports for this server */
551 		if ((handle = __rpc_setconf("datagram_v")) == NULL) {
552 			syslog(LOG_ERR,
553 			"ypbind: RPC operation on /etc/netconfig failed");
554 			free_listofnames(lin);
555 			return (-1);
556 		}
557 
558 		nconf_count = 0;
559 		clnt2 = 0;
560 		while (clnt2 == 0 && (nconf = __rpc_getconf(handle)) != 0) {
561 			nconf_count++;
562 			/*
563 			 * We use only datagram here. It is expected to be udp.
564 			 * VERY IMPORTANT: __clnt_tp_create_bootstrap is a
565 			 * hacked up version that does not do netdir_getbyname.
566 			 */
567 			hostNotKnownLocally = 0;
568 			clnt2 =
569 	__clnt_tp_create_bootstrap(servername, YPPROG, YPVERS, nconf);
570 		}
571 		if (nconf_count == 0) {
572 			syslog(LOG_ERR,
573 			"ypbind: RPC operation on /etc/netconfig failed");
574 			free_listofnames(lin);
575 			return (-1);
576 		}
577 
578 		if (clnt2 == 0) {
579 			if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST &&
580 				hostNotKnownLocally) {
581 				syslog(LOG_ERR,
582 		"NIS server %s is not in local host files !", servername);
583 			}
584 			perror(servername);
585 			clnt_pcreateerror("ypbind");
586 			continue;
587 		}
588 
589 		timeout.tv_sec = PINGTIME;
590 		timeout.tv_usec = 0;
591 		if ((enum clnt_stat) clnt_call(clnt2,
592 		    YPPROC_DOMAIN, (xdrproc_t)xdr_ypdomain_wrap_string,
593 		    (char *)&domain, xdr_int,
594 		    (char *)&isok, timeout) == RPC_SUCCESS) {
595 			if (isok) {
596 				if (domain_struct->dom_report_success > 0) {
597 					syslog(LOG_ERR,
598 				"NIS server for domain \"%s\" OK", domain);
599 				}
600 				if (domain_struct->broadcaster_pipe == 0) {
601 					/* init binding case --parent */
602 					struct netconfig *setnc;
603 					struct netbuf setua;
604 					struct ypbind_binding *b =
605 						domain_struct->dom_binding;
606 
607 					setnc =
608 				getnetconfigent(clnt2->cl_netid);
609 					if (b == NULL) {
610 					/* ASSERT: This shouldn't happen ! */
611 						b =
612 				(struct ypbind_binding *)calloc(1, sizeof (*b));
613 						domain_struct->dom_binding = b;
614 						if (b == NULL) {
615 							__rpc_endconf(handle);
616 							clnt_destroy(clnt2);
617 							free_listofnames(lin);
618 							return (-2);
619 						}
620 					}
621 
622 
623 					b->ypbind_nconf = setnc;
624 					clnt_control(clnt2, CLGET_SVC_ADDR,
625 						(char *)&setua);
626 					if (b->ypbind_svcaddr) {
627 						if (b->ypbind_svcaddr->buf)
628 				free(b->ypbind_svcaddr->buf);
629 						free(b->ypbind_svcaddr);
630 					}
631 					b->ypbind_svcaddr = dup_netbuf(&setua);
632 					if (b->ypbind_servername)
633 						free(b->ypbind_servername);
634 					b->ypbind_servername =
635 						strdup(servername);
636 					b->ypbind_hi_vers = YPVERS;
637 					b->ypbind_lo_vers = YPVERS;
638 					__rpc_endconf(handle);
639 					domain_struct->dom_boundp = TRUE;
640 					clnt_destroy(clnt2);
641 					free_listofnames(lin);
642 					return (0);
643 				}
644 				res = ypbind_pipe_setdom(clnt2, domain,
645 					servername, domain_struct);
646 				__rpc_endconf(handle);
647 				clnt_destroy(clnt2);
648 				free_listofnames(lin);
649 				return (res);
650 			} else {
651 				syslog(LOG_ERR,
652 				    "server %s doesn't serve domain %s\n",
653 				    servername, domain);
654 			}
655 		} else {
656 			clnt_perror(clnt2, servername);
657 		}
658 		clnt_destroy(clnt2);
659 	}
660 	/*
661 	 * We tried all servers, none obliged !
662 	 * After ypbind is started up it will not be bound
663 	 * immediately.  This is normal, no error message
664 	 * is needed. Although, with the ypbind_init_default
665 	 * it will be bound immediately.
666 	 */
667 	if (firsttime == FALSE) {
668 		syslog(LOG_ERR,
669 "NIS server not responding for domain \"%s\"; still trying", domain);
670 	}
671 	free_listofnames(lin);
672 	__rpc_endconf(handle);
673 	return (-2);
674 }
675 
676 struct netbuf *
677 dup_netbuf(inbuf)
678 	struct netbuf *inbuf;
679 {
680 	struct netbuf *outbuf;
681 
682 	if (inbuf == NULL)
683 		return (NULL);
684 	if ((outbuf =
685 		(struct netbuf *)calloc(1, sizeof (struct netbuf))) == NULL)
686 		return (NULL);
687 	if ((outbuf->buf = malloc(inbuf->len)) == NULL) {
688 		free(outbuf);
689 		return (NULL);
690 	}
691 	outbuf->len = inbuf->len;
692 	outbuf->maxlen = inbuf->len;
693 	(void) memcpy(outbuf->buf, inbuf->buf, inbuf->len);
694 	return (outbuf);
695 }
696 
697 /*
698  * This is called by the broadcast rpc routines to process the responses
699  * coming back from the broadcast request. Since the form of the request
700  * which is used in ypbind_broadcast_bind is "respond only in the positive
701  * case", we know that we have a server.
702  * The internet address of the responding server will be picked up from
703  * the saddr parameter, and stuffed into the domain.  The domain's boundp
704  * field will be set TRUE.  The first responding server (or the first one
705  * which is on a reserved port) will be the bound server for the domain.
706  */
707 bool
708 ypbind_broadcast_ack(ptrue, nbuf, nconf)
709 	bool *ptrue;
710 	struct netbuf *nbuf;
711 	struct netconfig *nconf;
712 {
713 	struct ypbind_binding b;
714 
715 	process_current_domain->dom_boundp = TRUE;
716 	b.ypbind_nconf = nconf;
717 	b.ypbind_svcaddr = nbuf;
718 	b.ypbind_servername = "\000";
719 	b.ypbind_hi_vers = YPVERS;
720 	b.ypbind_lo_vers = YPVERS;
721 	free_ypbind_binding(process_current_domain->dom_binding);
722 	process_current_domain->dom_binding = dup_ypbind_binding(&b);
723 	return (TRUE);
724 }
725 
726 /*
727  * WARNING: This routine is entered only by the child process.
728  * Called if it pongs/broadcasts okay.
729  */
730 static int
731 ypbind_pipe_setdom(client, domain, servername, opaque_domain)
732 CLIENT *client;
733 char *servername;
734 char *domain;
735 struct domain *opaque_domain;
736 {
737 	struct netconfig *setnc;
738 	struct netbuf setua;
739 	ypbind_binding setb;
740 	ypbind_setdom setd;
741 	int retval;
742 
743 	setd.ypsetdom_domain = domain;
744 	if (client == NULL && opaque_domain->dom_binding) {
745 #ifdef DEBUG
746 	fprintf(stderr, "ypbind_pipe_setdom: child broadcast case ");
747 #endif
748 		/* ypbind_broadcast_ack already setup dom_binding for us */
749 		setd.ypsetdom_bindinfo = opaque_domain->dom_binding;
750 	} else if (client) {
751 #ifdef DEBUG
752 	fprintf(stderr, "ypbind_pipe_setdom: child unicast case ");
753 #endif
754 		setnc = getnetconfigent(client->cl_netid);
755 		if (setnc == NULL) {
756 #ifdef DEBUG
757 	fprintf(stderr, "PANIC: shouldn't happen\n");
758 #endif
759 			fclose(opaque_domain->broadcaster_pipe);
760 			close(opaque_domain->broadcaster_fd);
761 			return (-2);
762 		}
763 		clnt_control(client, CLGET_SVC_ADDR, (char *)&setua);
764 		setb.ypbind_nconf = setnc;
765 		setb.ypbind_svcaddr = &setua;
766 		setb.ypbind_servername = servername;
767 		setb.ypbind_hi_vers = YPVERS;
768 		setb.ypbind_lo_vers = YPVERS;
769 		setd.ypsetdom_bindinfo = &setb;
770 	/*
771 	 * Let's hardcode versions, that is the only ypserv we support anyway.
772 	 * Avoid the song and dance of recursively calling ypbind_ping
773 	 * for no reason. Consistent with the 4.1 policy, that if ypbind gets
774 	 * a request on new binder protocol, the requestor is looking for the
775 	 * new ypserv. And, we have even higher binder protocol version i.e. 3.
776 	 */
777 	} else
778 		return (-1);
779 #ifdef DEBUG
780 	fprintf(stderr,
781 		" saving server settings, \nsupports versions %d thru %d\n",
782 		setd.ypsetdom_bindinfo->ypbind_lo_vers,
783 		setd.ypsetdom_bindinfo->ypbind_hi_vers);
784 #endif
785 
786 	if (opaque_domain->broadcaster_pipe == 0) {
787 #ifdef DEBUG
788 	fprintf(stderr, "PANIC: shouldn't be in this function\n");
789 #endif
790 		return (-2);
791 	}
792 #ifdef DEBUG
793 	fprintf(stderr, "child: doing xdr_ypbind_setdom\n");
794 #endif
795 	retval = xdr_ypbind_setdom(&(opaque_domain->broadcaster_xdr), &setd);
796 	xdr_destroy(&(opaque_domain->broadcaster_xdr));
797 	fclose(opaque_domain->broadcaster_pipe);
798 	close(opaque_domain->broadcaster_fd);
799 	/*
800 	 * This child process is about to exit. Don't bother freeing memory.
801 	 */
802 	if (!retval) {
803 #ifdef DEBUG
804 	fprintf(stderr,
805 		"YPBIND pipe_setdom failed \n(xdr failure) to server %s\n",
806 		servername ? servername : "");
807 #endif
808 		return (-3);
809 	}
810 #ifdef DEBUG
811 	fprintf(stderr, "ypbind_pipe_setdom: YPBIND OK-set to server %s\n",
812 		servername ? servername : "");
813 #endif
814 	return (0);
815 }
816 
817 /* Same as ypbind_set_binding in SunOS */
818 /*
819  *  We use a trick from SunOS to return an error to the ypset command
820  *  when we are not allowing the domain to be set.  We do a svcerr_noprog()
821  *  to send RPC_PROGUNAVAIL to ypset.  We also return NULL so that
822  *  our caller (ypbindprog_3) won't try to return a result.  This
823  *  hack is necessary because the YPBINDPROC_SETDOM procedure is defined
824  *  in the protocol to return xdr_void, so we don't have a direct way to
825  *  return an error to the client.
826  */
827 /*ARGSUSED*/
828 void	*
829 ypbindproc_setdom_3(argp, rqstp, transp)
830 ypbind_setdom *argp;
831 struct svc_req *rqstp;
832 SVCXPRT *transp;
833 {
834 	struct domain *a_domain;
835 	struct netbuf *who;
836 	static char res; /* dummy for void * return */
837 	uid_t caller_uid;
838 
839 	if ((int)strlen(argp->ypsetdom_domain) > YPMAXDOMAIN) {
840 
841 		if (transp) {
842 			svcerr_systemerr(transp);
843 			return (0);
844 		}
845 		return (&res);
846 	}
847 
848 	if (transp != NULL) {
849 		/* find out who originated the request */
850 		char *uaddr;
851 		struct netconfig *nconf;
852 
853 		who = svc_getrpccaller(transp);
854 		if ((nconf = getnetconfigent(transp->xp_netid))
855 			== (struct netconfig *)NULL) {
856 			svcerr_systemerr(transp);
857 			return (0);
858 		}
859 		if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
860 			uaddr = strdup("local host");
861 		} else {
862 			uaddr = taddr2uaddr(nconf, who);
863 		}
864 		if (setok != YPSETALL) {
865 		/* for -ypset, it falls through and let anybody do a setdom ! */
866 			if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0) {
867 				syslog(LOG_ERR,
868 "ypset request from %s not on loopback, \
869 cannot set ypbind to %s", uaddr ? uaddr : "unknown source",
870 argp->ypsetdom_bindinfo->ypbind_servername);
871 				if (uaddr)
872 					free(uaddr);
873 				freenetconfigent(nconf);
874 				svcerr_noprog(transp);
875 				return (0);
876 			}
877 			switch (setok) {
878 			case YPSETNONE:
879 				if (strcmp(nconf->nc_protofmly,
880 					NC_LOOPBACK) == 0)
881 					syslog(LOG_ERR,
882 "ypset request to %s from %s failed - ypset not allowed",
883 argp->ypsetdom_bindinfo->ypbind_servername, uaddr);
884 				if (uaddr)
885 					free(uaddr);
886 				freenetconfigent(nconf);
887 				svcerr_noprog(transp);
888 				return (0);
889 			case YPSETLOCAL:
890 				if (__rpc_get_local_uid(transp,
891 					&caller_uid) < 0) {
892 					syslog(LOG_ERR, "ypset request from \
893 unidentified local user on %s - ypset not allowed",
894 transp->xp_netid);
895 					if (uaddr)
896 						free(uaddr);
897 					freenetconfigent(nconf);
898 					svcerr_noprog(transp);
899 					return (0);
900 				}
901 				if (caller_uid != 0) {
902 					syslog(LOG_ERR,
903 "Set domain request to host %s \
904 from local non-root user %ld failed - ypset not allowed",
905 argp->ypsetdom_bindinfo->ypbind_servername, caller_uid);
906 					if (uaddr)
907 						free(uaddr);
908 					freenetconfigent(nconf);
909 					svcerr_noprog(transp);
910 					return (0);
911 				}
912 			}
913 		}
914 		syslog(LOG_ERR, "Set domain request from %s : \
915 setting server for domain %s to %s", uaddr ? uaddr : "UNKNOWN SOURCE",
916 argp->ypsetdom_domain, argp->ypsetdom_bindinfo->ypbind_servername);
917 		if (uaddr)
918 			free(uaddr);
919 		freenetconfigent(nconf);
920 	}
921 
922 	if ((a_domain = ypbind_point_to_domain(argp->ypsetdom_domain))
923 	    != (struct domain *)NULL) {
924 		/* setting binding; old may be invalid */
925 		uncache_binding(a_domain);
926 
927 		/* this does the set -- should copy the structure */
928 		free_ypbind_binding(a_domain->dom_binding);
929 		if ((a_domain->dom_binding =
930 		    dup_ypbind_binding(argp->ypsetdom_bindinfo)) == NULL) {
931 			syslog(LOG_ERR, "ypbindproc_setdom_3: out of memory, ",
932 				"dup_ypbind_binding failed\n");
933 			if (transp) {
934 				svcerr_noprog(transp);
935 				return (0);
936 			}
937 			return (&res);
938 		}
939 		gettimeofday(&(a_domain->lastping), NULL);
940 		a_domain->dom_boundp = TRUE;
941 		cache_binding(a_domain);
942 #ifdef DEBUG
943 	fprintf(stderr, "ypbindproc_setdom_3: setting domain %s to server %s\n",
944 		argp->ypsetdom_domain,
945 		argp->ypsetdom_bindinfo->ypbind_servername);
946 #endif
947 	}
948 
949 	return (&res);
950 }
951 
952 /*
953  * This returns a pointer to a domain entry.  If no such domain existed on
954  * the list previously, an entry will be allocated, initialized, and linked
955  * to the list.  Note:  If no memory can be malloc-ed for the domain structure,
956  * the functional value will be (struct domain *) NULL.
957  */
958 static struct domain *
959 ypbind_point_to_domain(pname)
960 register char	*pname;
961 {
962 	register struct domain *pdom;
963 	char buf[300];
964 
965 	for (pdom = known_domains; pdom != (struct domain *)NULL;
966 	    pdom = pdom->dom_pnext) {
967 		if (strcmp(pname, pdom->dom_name) == 0)
968 			return (pdom);
969 	}
970 
971 	/* Not found.  Add it to the list */
972 
973 	if (pdom = (struct domain *)calloc(1, sizeof (struct domain))) {
974 		pdom->dom_name = strdup(pname);
975 		if (pdom->dom_name == NULL) {
976 			free((char *)pdom);
977 			syslog(LOG_ERR,
978 				"ypbind_point_to_domain: strdup failed\n");
979 			return (NULL);
980 		}
981 		pdom->dom_pnext = known_domains;
982 		known_domains = pdom;
983 		pdom->dom_boundp = FALSE;
984 		pdom->dom_vers = YPVERS; /* This doesn't talk to old ypserv */
985 		pdom->dom_binding = NULL;
986 		pdom->dom_error = YPBIND_ERR_NOSERV;
987 		pdom->ping_clnt = (CLIENT *)NULL;
988 		pdom->dom_report_success = -1;
989 		pdom->dom_broadcaster_pid = 0;
990 		pdom->broadcaster_pipe = 0;
991 		pdom->bindfile = -1;
992 		pdom->lastping.tv_sec = 0;
993 		pdom->lastping.tv_usec = 0; /* require ping */
994 		pdom->cache_fp = 0;
995 		sprintf(buf, "%s/%s/cache_binding", BINDING, pdom->dom_name);
996 		pdom->cache_file = strdup(buf);
997 		/*
998 		 *  We don't give an error if pdom->cache_file is not set.
999 		 *  If we got null (out of memory), then we just won't use
1000 		 *  the cache file in cache_binding() (assuming the
1001 		 *  application gets that far.
1002 		 */
1003 	}
1004 	else
1005 		syslog(LOG_ERR, "ypbind_point_to_domain: malloc failed\n");
1006 
1007 	return (pdom);
1008 }
1009 
1010 static void
1011 ypbind_ping(pdom)
1012 struct domain *pdom;
1013 {
1014 	struct timeval timeout;
1015 	int	vers;
1016 	int	isok;
1017 
1018 	if (pdom->dom_boundp == FALSE)
1019 		return;
1020 	vers = pdom->dom_vers;
1021 
1022 	if (pdom->ping_clnt == (CLIENT *) NULL) {
1023 		pdom->ping_clnt = __nis_clnt_create(RPC_ANYFD,
1024 					pdom->dom_binding->ypbind_nconf, 0,
1025 					pdom->dom_binding->ypbind_svcaddr, 0,
1026 					YPPROG, vers, 0, 0);
1027 	}
1028 
1029 	if (pdom->ping_clnt == (CLIENT *) NULL) {
1030 		perror("clnt_tli_create");
1031 		clnt_pcreateerror("ypbind_ping()");
1032 		pdom->dom_boundp = FALSE;
1033 		pdom->dom_error = YPBIND_ERR_NOSERV;
1034 		return;
1035 	}
1036 
1037 
1038 #ifdef DEBUG
1039 	fprintf(stderr, "ypbind: ypbind_ping()\n");
1040 #endif
1041 	timeout.tv_sec = PINGTOTTIM;
1042 	timeout.tv_usec =  0;
1043 	if (clnt_call(pdom->ping_clnt,
1044 	    YPPROC_DOMAIN, (xdrproc_t)xdr_ypdomain_wrap_string,
1045 		(char *)&pdom->dom_name, xdr_int, (char *)&isok,
1046 		timeout) == RPC_SUCCESS) {
1047 			pdom->dom_boundp = isok;
1048 			pdom->dom_binding->ypbind_lo_vers = vers;
1049 			pdom->dom_binding->ypbind_hi_vers = vers;
1050 #ifdef DEBUG
1051 	fprintf(stderr,
1052 		"Server pinged successfully, supports versions %d thru %d\n",
1053 		pdom->dom_binding->ypbind_lo_vers,
1054 		pdom->dom_binding->ypbind_hi_vers);
1055 #endif
1056 	} else {
1057 		clnt_perror(pdom->ping_clnt, "ping");
1058 		pdom->dom_boundp = FALSE;
1059 		pdom->dom_error = YPBIND_ERR_NOSERV;
1060 	}
1061 	(void) gettimeofday(&(pdom->lastping), NULL);
1062 	if (pdom->ping_clnt)
1063 		clnt_destroy(pdom->ping_clnt);
1064 	pdom->ping_clnt = (CLIENT *)NULL;
1065 	if (pdom->dom_boundp)
1066 		cache_binding(pdom);
1067 }
1068 
1069 static struct ypbind_binding *
1070 dup_ypbind_binding(a)
1071 struct ypbind_binding *a;
1072 {
1073 	struct ypbind_binding *b;
1074 	struct netconfig *nca, *ncb;
1075 	struct netbuf *nxa, *nxb;
1076 	int i;
1077 
1078 	b = (struct ypbind_binding *)calloc(1, sizeof (*b));
1079 	if (b == NULL)
1080 		return (b);
1081 	b->ypbind_hi_vers = a->ypbind_hi_vers;
1082 	b->ypbind_lo_vers = a->ypbind_lo_vers;
1083 	b->ypbind_servername =
1084 		a->ypbind_servername ? strdup(a->ypbind_servername) : NULL;
1085 	ncb = (b->ypbind_nconf =
1086 		(struct netconfig *)calloc(1, sizeof (struct netconfig)));
1087 	nxb = (b->ypbind_svcaddr =
1088 		(struct netbuf *)calloc(1, sizeof (struct netbuf)));
1089 	nca = a->ypbind_nconf;
1090 	nxa = a->ypbind_svcaddr;
1091 	ncb->nc_flag = nca->nc_flag;
1092 	ncb->nc_protofmly =
1093 		nca->nc_protofmly ? strdup(nca->nc_protofmly) : NULL;
1094 	ncb->nc_proto =
1095 		nca->nc_proto ? strdup(nca->nc_proto) : NULL;
1096 	ncb->nc_semantics = nca->nc_semantics;
1097 	ncb->nc_netid =
1098 		nca->nc_netid ? strdup(nca->nc_netid) : NULL;
1099 	ncb->nc_device =
1100 		nca->nc_device ? strdup(nca->nc_device) : NULL;
1101 	ncb->nc_nlookups = nca->nc_nlookups;
1102 	ncb->nc_lookups = (char **)calloc(nca->nc_nlookups, sizeof (char *));
1103 	if (ncb->nc_lookups == NULL) {
1104 		if (ncb->nc_device)
1105 			free(ncb->nc_device);
1106 		if (ncb->nc_netid)
1107 			free(ncb->nc_netid);
1108 		if (ncb->nc_proto)
1109 			free(ncb->nc_proto);
1110 		if (ncb->nc_protofmly)
1111 			free(ncb->nc_protofmly);
1112 		if (nxb)
1113 			free(nxb);
1114 		if (ncb)
1115 			free(ncb);
1116 		if (b->ypbind_servername)
1117 			free(b->ypbind_servername);
1118 		if (b)
1119 			free(b);
1120 		return (NULL);
1121 	}
1122 	for (i = 0; i < nca->nc_nlookups; i++)
1123 		ncb->nc_lookups[i] =
1124 			nca->nc_lookups[i] ? strdup(nca->nc_lookups[i]) : NULL;
1125 	for (i = 0; i < 8; i++)
1126 		ncb->nc_unused[i] = nca->nc_unused[i];
1127 	nxb->maxlen = nxa->maxlen;
1128 	nxb->len = nxa->len;
1129 	nxb->buf = malloc(nxa->maxlen);
1130 	if (nxb->buf == NULL) {
1131 		for (i = 0; i < nca->nc_nlookups; i++)
1132 			if (ncb->nc_lookups[i])
1133 				free(ncb->nc_lookups[i]);
1134 		free(ncb->nc_lookups);
1135 		if (ncb->nc_device)
1136 			free(ncb->nc_device);
1137 		if (ncb->nc_netid)
1138 			free(ncb->nc_netid);
1139 		if (ncb->nc_proto)
1140 			free(ncb->nc_proto);
1141 		if (ncb->nc_protofmly)
1142 			free(ncb->nc_protofmly);
1143 		if (nxb)
1144 			free(nxb);
1145 		if (ncb)
1146 			free(ncb);
1147 		if (b->ypbind_servername)
1148 			free(b->ypbind_servername);
1149 		if (b)
1150 			free(b);
1151 		return (NULL);
1152 	}
1153 	memcpy(nxb->buf, nxa->buf, nxb->len);
1154 	return (b);
1155 }
1156 
1157 static void
1158 free_ypbind_binding(b)
1159 struct ypbind_binding *b;
1160 {
1161 	if (b == NULL)
1162 		return;
1163 	netdir_free((char *)b->ypbind_svcaddr, ND_ADDR);
1164 	free(b->ypbind_servername);
1165 	freenetconfigent(b->ypbind_nconf);
1166 	free(b);
1167 }
1168 
1169 /*
1170  * Preloads teh default domain's domain binding. Domain binding for the
1171  * local node's default domain for ypserv version 2 (YPVERS) will be
1172  * set up. This may make it a little slower to start ypbind during
1173  * boot time, but would make it easy on other domains that rely on
1174  * this binding.
1175  */
1176 void
1177 ypbind_init_default()
1178 {
1179 	char domain[256];
1180 	struct domain *cur_domain;
1181 
1182 	if (getdomainname(domain, 256) == 0) {
1183 		cur_domain = ypbind_point_to_domain(domain);
1184 
1185 		if (cur_domain == (struct domain *)NULL) {
1186 			abort();
1187 		}
1188 		(void) pong_servers(cur_domain);
1189 	}
1190 }
1191 
1192 bool_t
1193 xdr_ypbind_binding_2(xdrs, objp)
1194 	register XDR *xdrs;
1195 	ypbind_binding_2 *objp;
1196 {
1197 	if (!xdr_opaque(xdrs, (char *)&(objp->ypbind_binding_addr), 4))
1198 		return (FALSE);
1199 	if (!xdr_opaque(xdrs, (char *)&(objp->ypbind_binding_port), 2))
1200 		return (FALSE);
1201 	return (TRUE);
1202 }
1203 
1204 bool_t
1205 xdr_ypbind_resp_2(xdrs, objp)
1206 	register XDR *xdrs;
1207 	ypbind_resp_2 *objp;
1208 {
1209 	if (!xdr_ypbind_resptype(xdrs, &objp->ypbind_status))
1210 		return (FALSE);
1211 	switch (objp->ypbind_status) {
1212 	case YPBIND_FAIL_VAL:
1213 		if (!xdr_u_long(xdrs, &objp->ypbind_respbody_2.ypbind_error))
1214 			return (FALSE);
1215 		break;
1216 	case YPBIND_SUCC_VAL:
1217 		if (!xdr_ypbind_binding_2(xdrs,
1218 		    &objp->ypbind_respbody_2.ypbind_bindinfo))
1219 			return (FALSE);
1220 		break;
1221 	default:
1222 		return (FALSE);
1223 	}
1224 	return (TRUE);
1225 }
1226 
1227 /*
1228  *  The following is some caching code to improve the performance of
1229  *  yp clients.  In the days of yore, a client would talk to rpcbind
1230  *  to get the address for ypbind, then talk to ypbind to get the
1231  *  address of the server.  If a lot of clients are doing this at
1232  *  the same time, then rpcbind and ypbind get bogged down and clients
1233  *  start to time out.
1234  *
1235  *  We cache two things:  the current address for ypserv, and the
1236  *  transport addresses for talking to ypbind.  These are saved in
1237  *  files in /var/yp.  To get the address of ypserv, the client opens
1238  *  a file and reads the address.  It does not have to talk to rpcbind
1239  *  or ypbind.  If this file is not available, then it can read the
1240  *  the transport address for talking to ypbind without bothering
1241  *  rpcbind.  If this also fails, then it uses the old method of
1242  *  talking to rpcbind and then ypbind.
1243  *
1244  *  We lock the first byte of the cache files after writing to them.
1245  *  This indicates to the client that they contents are valid.  The
1246  *  client should test the lock.  If the lock is held, then it can
1247  *  use the contents.  If the lock test fails, then the contents should
1248  *  be ignored.
1249  */
1250 
1251 /*
1252  *  Cache new binding information for a domain in a file.  If the
1253  *  new binding is the same as the old, then we skip it.  We xdr
1254  *  a 'ypbind_resp', which is what would be returned by a call to
1255  *  the YBINDPROCP_DOMAIN service.  We xdr the data because it is
1256  *  easier than writing the data out field by field.  It would be
1257  *  nice if there were an xdrfd_create() that was similar to
1258  *  xdrstdio_create().  Instead, we do an fdopen and use xdrstdio_create().
1259  */
1260 void
1261 cache_binding(pdom)
1262 	struct domain *pdom;
1263 {
1264 	int st;
1265 	int fd;
1266 	XDR xdrs;
1267 	struct ypbind_resp resp;
1268 
1269 	if (!cache_okay)
1270 		return;
1271 
1272 	/* if the domain doesn't have a cache file, then skip it */
1273 	if (pdom->cache_file == 0)
1274 		return;
1275 
1276 	/*
1277 	 *  If we already had a cache file for this domain, remove it.  If
1278 	 *  a client just started accessing it, then it will either find
1279 	 *  it unlocked (and not use it), or continue to use it with
1280 	 *  old information.  This is not a problem, the client will
1281 	 *  either fail to talk to ypserv and try to bind again, or
1282 	 *  will continue to use the old server.
1283 	 */
1284 	if (pdom->cache_fp) {
1285 		fclose(pdom->cache_fp);    /* automatically unlocks */
1286 		unlink(pdom->cache_file);
1287 		pdom->cache_fp = 0;
1288 	}
1289 
1290 	fd = open(pdom->cache_file, O_CREAT|O_WRONLY, 0444);
1291 	if (fd == -1)
1292 		return;
1293 
1294 	pdom->cache_fp = fdopen(fd, "w");
1295 	if (pdom->cache_fp == 0) {
1296 		close(fd);
1297 		return;
1298 	}
1299 
1300 	xdrstdio_create(&xdrs, pdom->cache_fp, XDR_ENCODE);
1301 	resp.ypbind_status = YPBIND_SUCC_VAL;
1302 	resp.ypbind_resp_u.ypbind_bindinfo = pdom->dom_binding;
1303 
1304 	if (!xdr_ypbind_resp(&xdrs, &resp)) {
1305 		xdr_destroy(&xdrs);
1306 		unlink(pdom->cache_file);
1307 		fclose(pdom->cache_fp);
1308 		pdom->cache_fp = 0;
1309 		return;
1310 	}
1311 	xdr_destroy(&xdrs);    /* flushes xdr but leaves fp open */
1312 
1313 	/* we lock the first byte to indicate that the file is valid */
1314 	lseek(fd, 0L, SEEK_SET);
1315 	st = lockf(fd, F_LOCK, 1);
1316 	if (st == -1) {
1317 		unlink(pdom->cache_file);
1318 		fclose(pdom->cache_fp);
1319 		pdom->cache_fp = 0;
1320 	}
1321 }
1322 
1323 void
1324 uncache_binding(pdom)
1325 	struct domain *pdom;
1326 {
1327 	if (!cache_okay)
1328 		return;
1329 
1330 	if (pdom->cache_fp != 0) {
1331 		unlink(pdom->cache_file);
1332 		fclose(pdom->cache_fp);
1333 		pdom->cache_fp = 0;
1334 	}
1335 }
1336 
1337 /*
1338  *  Cache a transport address for talking to ypbind.  We convert the
1339  *  transport address to a universal address and save that in a file.
1340  *  The file lives in the binding directory because it does not depend
1341  *  on the domain.
1342  */
1343 void
1344 cache_transport(nconf, xprt, vers)
1345 	struct netconfig *nconf;
1346 	SVCXPRT *xprt;
1347 	int vers;
1348 {
1349 	char filename[300];
1350 	char *uaddr;
1351 	int fd;
1352 	int st;
1353 	int len;
1354 
1355 	if (!cache_okay)
1356 		return;
1357 
1358 	sprintf(filename, "%s/xprt.%s.%d",
1359 		BINDING, nconf->nc_netid, vers);
1360 
1361 	unlink(filename);    /* remove any old version */
1362 
1363 	uaddr = taddr2uaddr(nconf, &xprt->xp_ltaddr);
1364 	if (uaddr == 0)
1365 		return;
1366 
1367 	fd = open(filename, O_CREAT|O_WRONLY, 0444);    /* readable by all */
1368 	if (fd == -1) {
1369 		free(uaddr);
1370 		return;
1371 	}
1372 
1373 	len = strlen(uaddr) + 1;    /* include terminating null */
1374 	st = write(fd, uaddr, len);
1375 	if (st != len) {
1376 		close(fd);
1377 		unlink(filename);
1378 		free(uaddr);
1379 		return;
1380 	}
1381 
1382 	free(uaddr);
1383 
1384 	/* we lock the first byte to indicate that the file is valid */
1385 	lseek(fd, 0L, SEEK_SET);
1386 	st = lockf(fd, F_LOCK, 1);
1387 	if (st == -1) {
1388 		close(fd);
1389 		unlink(filename);
1390 	}
1391 }
1392 
1393 /*
1394  *  Create a file that clients can check to see if we are running.
1395  */
1396 void
1397 cache_pid()
1398 {
1399 	char filename[300];
1400 	char spid[15];
1401 	int fd;
1402 	int st;
1403 	int len;
1404 
1405 	if (!cache_okay)
1406 		return;
1407 
1408 	sprintf(filename, "%s/ypbind.pid", BINDING);
1409 
1410 	unlink(filename);    /* remove any old version */
1411 
1412 	fd = open(filename, O_CREAT|O_WRONLY, 0444);    /* readable by all */
1413 	if (fd == -1) {
1414 		return;
1415 	}
1416 
1417 	sprintf(spid, "%d\n", getpid());
1418 
1419 	len = strlen(spid);
1420 	st = write(fd, spid, len);
1421 	if (st != len) {
1422 		close(fd);
1423 		unlink(filename);
1424 		return;
1425 	}
1426 
1427 	/* we lock the first byte to indicate that the file is valid */
1428 	lseek(fd, 0L, SEEK_SET);
1429 	st = lockf(fd, F_LOCK, 1);
1430 	if (st == -1) {
1431 		close(fd);
1432 		unlink(filename);
1433 	}
1434 
1435 	/* we keep 'fd' open so that the lock will continue to be held */
1436 }
1437 
1438 /*
1439  *  We are called once at startup (when the known_domains list is empty)
1440  *  to clean up left-over files.  We are also called right before
1441  *  exiting.  In the latter case case we don't bother closing descriptors
1442  *  in the entries in the domain list because they will be closed
1443  *  automatically (and unlocked) when we exit.
1444  *
1445  *  We ignore the cache_okay flag because it is important that we remove
1446  *  all cache files (left-over files can temporarily confuse clients).
1447  */
1448 void
1449 clean_cache()
1450 {
1451 	struct domain *pdom;
1452 	DIR *dir;
1453 	struct dirent *dirent;
1454 	char filename[300];
1455 
1456 	/* close and unlink cache files for each domain */
1457 	for (pdom = known_domains; pdom != (struct domain *)NULL;
1458 	    pdom = pdom->dom_pnext) {
1459 		if (pdom->cache_file)
1460 			unlink(pdom->cache_file);
1461 	}
1462 
1463 	sprintf(filename, "%s/ypbind.pid", BINDING);
1464 	unlink(filename);
1465 
1466 	dir = opendir(BINDING);
1467 
1468 	if (dir == NULL) {
1469 		/* Directory could not be opened. */
1470 		syslog(LOG_ERR, "opendir failed with [%s]", strerror(errno));
1471 		return;
1472 	}
1473 
1474 	while ((dirent = readdir(dir)) != 0) {
1475 		if (strncmp(dirent->d_name, "xprt.", 5) == 0) {
1476 			sprintf(filename, "%s/%s", BINDING, dirent->d_name);
1477 			unlink(filename);
1478 			rewinddir(dir);  /* removing file may harm iteration */
1479 		}
1480 	}
1481 	closedir(dir);
1482 }
1483 
1484 /*
1485  *  We only want to use the cache stuff on local file systems.
1486  *  For remote file systems (e.g., NFS, the locking overhead is
1487  *  worse than the overhead of loopback RPC, so the caching
1488  *  wouldn't buy us anything.  In addition, if the remote locking
1489  *  software isn't configured before we start, then we would
1490  *  block when we try to lock.
1491  *
1492  *  We don't have a direct way to tell if a file system is local
1493  *  or remote, so we assume it is local unless it is NFS.
1494  */
1495 int
1496 cache_check()
1497 {
1498 	int st;
1499 	struct statvfs stbuf;
1500 
1501 	st = statvfs(BINDING, &stbuf);
1502 	if (st == -1) {
1503 		syslog(LOG_ERR, "statvfs failed with [%s]", strerror(errno));
1504 		return (0);
1505 	}
1506 
1507 	/* we use strncasecmp to get NFS, NFS3, nfs, nfs3, etc. */
1508 	if (strncasecmp(stbuf.f_basetype, "NFS", 3) == 0)
1509 		return (0);
1510 	return (1);
1511 }
1512