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 *
ypbindproc_null_3(argp,clnt)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
enable_exit()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
sigcld_handler()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
broadcast_proc_exit()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
broadcast_setup(pdom)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 *
ypbindproc_domain_3(argp,clnt)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 *
ypbindproc_domain_2(argp,clnt)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
pong_servers(domain_struct)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 *
dup_netbuf(inbuf)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
ypbind_broadcast_ack(ptrue,nbuf,nconf)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
ypbind_pipe_setdom(client,domain,servername,opaque_domain)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 *
ypbindproc_setdom_3(argp,rqstp,transp)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 *
ypbind_point_to_domain(pname)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
ypbind_ping(pdom)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 *
dup_ypbind_binding(a)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
free_ypbind_binding(b)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
ypbind_init_default()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
xdr_ypbind_binding_2(xdrs,objp)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
xdr_ypbind_resp_2(xdrs,objp)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
cache_binding(pdom)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
uncache_binding(pdom)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
cache_transport(nconf,xprt,vers)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
cache_pid()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
clean_cache()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
cache_check()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