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