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