xref: /freebsd/usr.sbin/ypbind/ypbind.c (revision 17ee9d00bc1ae1e598c38f25826f861e4bc6c3ce)
1 /*
2  * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote
14  *    products derived from this software without specific prior written
15  *    permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #ifndef LINT
31 static char rcsid[] = "$Id: ypbind.c,v 1.2 1994/09/23 10:25:38 davidg Exp $";
32 #endif
33 
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
37 #include <sys/signal.h>
38 #include <sys/socket.h>
39 #include <sys/file.h>
40 #include <sys/fcntl.h>
41 #include <sys/uio.h>
42 #include <syslog.h>
43 #include <stdio.h>
44 #include <errno.h>
45 #include <ctype.h>
46 #include <dirent.h>
47 #include <netdb.h>
48 #include <string.h>
49 #include <rpc/rpc.h>
50 #include <rpc/xdr.h>
51 #include <net/if.h>
52 #include <arpa/inet.h>
53 #include <rpc/pmap_clnt.h>
54 #include <rpc/pmap_prot.h>
55 #include <rpc/pmap_rmt.h>
56 #include <unistd.h>
57 #include <rpcsvc/yp_prot.h>
58 #include <rpcsvc/ypclnt.h>
59 
60 #ifndef BINDINGDIR
61 #define BINDINGDIR "/var/yp/binding"
62 #endif
63 
64 /*
65  * Number of seconds to wait before for ping replies before we
66  * decide that our server is dead.
67  */
68 #ifndef FAIL_THRESHOLD
69 #define FAIL_THRESHOLD 20
70 #endif
71 
72 struct _dom_binding {
73 	struct _dom_binding *dom_pnext;
74 	char dom_domain[YPMAXDOMAIN + 1];
75 	struct sockaddr_in dom_server_addr;
76 	unsigned short int dom_server_port;
77 	int dom_socket;
78 	CLIENT *dom_client;
79 	long int dom_vers;
80 	time_t dom_check_t;
81 	int dom_lockfd;
82 	int dom_alive;
83 	int dom_answered;
84 	int dom_interval;
85 };
86 
87 extern bool_t xdr_domainname(), xdr_ypbind_resp();
88 extern bool_t xdr_ypreq_key(), xdr_ypresp_val();
89 extern bool_t xdr_ypbind_setdom();
90 
91 char *domainname;
92 
93 struct _dom_binding *ypbindlist;
94 int check;
95 
96 #define YPSET_NO	0
97 #define YPSET_LOCAL	1
98 #define YPSET_ALL	2
99 int ypsetmode = YPSET_NO;
100 
101 int rpcsock;
102 struct rmtcallargs rmtca;
103 struct rmtcallres rmtcr;
104 char rmtcr_outval;
105 u_long rmtcr_port;
106 SVCXPRT *udptransp, *tcptransp;
107 
108 void *
109 ypbindproc_null_2(transp, argp, clnt)
110 SVCXPRT *transp;
111 void *argp;
112 CLIENT *clnt;
113 {
114 	static char res;
115 
116 	bzero((char *)&res, sizeof(res));
117 	return (void *)&res;
118 }
119 
120 struct ypbind_resp *
121 ypbindproc_domain_2(transp, argp, clnt)
122 SVCXPRT *transp;
123 char *argp;
124 CLIENT *clnt;
125 {
126 	static struct ypbind_resp res;
127 	struct _dom_binding *ypdb;
128 	char path[MAXPATHLEN];
129 
130 	bzero((char *)&res, sizeof res);
131 	res.ypbind_status = YPBIND_FAIL_VAL;
132 
133 	for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext)
134 		if( strcmp(ypdb->dom_domain, argp) == 0)
135 			break;
136 
137 	if(ypdb==NULL) {
138 		ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
139 		bzero((char *)ypdb, sizeof *ypdb);
140 		strncpy(ypdb->dom_domain, argp, sizeof ypdb->dom_domain);
141 		ypdb->dom_vers = YPVERS;
142 		ypdb->dom_alive = 0;
143 		ypdb->dom_lockfd = -1;
144 		sprintf(path, "%s/%s.%d", BINDINGDIR, ypdb->dom_domain, ypdb->dom_vers);
145 		unlink(path);
146 		ypdb->dom_pnext = ypbindlist;
147 		ypbindlist = ypdb;
148 		return NULL;
149 	}
150 
151 	if(ypdb->dom_alive==0)
152 		return NULL;
153 
154 #if 0
155 	delta = ypdb->dom_check_t - ypdb->dom_ask_t;
156 	if( !(ypdb->dom_ask_t==0 || delta > 5)) {
157 		ypdb->dom_ask_t = time(NULL);
158 		/*
159 		 * Hmm. More than 2 requests in 5 seconds have indicated that my
160 		 * binding is possibly incorrect. Ok, make myself unalive, and
161 		 * find out what the actual state is.
162 		 */
163 		if(ypdb->dom_lockfd!=-1)
164 			close(ypdb->dom_lockfd);
165 		ypdb->dom_lockfd = -1;
166 		ypdb->dom_alive = 0;
167 		ypdb->dom_lockfd = -1;
168 		sprintf(path, "%s/%s.%d", BINDINGDIR, ypdb->dom_domain, ypdb->dom_vers);
169 		unlink(path);
170 		return NULL;
171 	}
172 #endif
173 
174 answer:
175 	res.ypbind_status = YPBIND_SUCC_VAL;
176 	res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr.s_addr =
177 		ypdb->dom_server_addr.sin_addr.s_addr;
178 	res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
179 		ypdb->dom_server_port;
180 	/*printf("domain %s at %s/%d\n", ypdb->dom_domain,
181 		inet_ntoa(ypdb->dom_server_addr.sin_addr),
182 		ntohs(ypdb->dom_server_addr.sin_port));*/
183 	return &res;
184 }
185 
186 bool_t *
187 ypbindproc_setdom_2(transp, argp, clnt)
188 SVCXPRT *transp;
189 struct ypbind_setdom *argp;
190 CLIENT *clnt;
191 {
192 	struct sockaddr_in *fromsin, bindsin;
193 	static char res;
194 
195 	bzero((char *)&res, sizeof(res));
196 	fromsin = svc_getcaller(transp);
197 
198 	switch(ypsetmode) {
199 	case YPSET_LOCAL:
200 		if( fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
201 			return (void *)NULL;
202 		break;
203 	case YPSET_ALL:
204 		break;
205 	case YPSET_NO:
206 	default:
207 		return (void *)NULL;
208 	}
209 
210 	if(ntohs(fromsin->sin_port) >= IPPORT_RESERVED)
211 		return (void *)&res;
212 
213 	if(argp->ypsetdom_vers != YPVERS)
214 		return (void *)&res;
215 
216 	bzero((char *)&bindsin, sizeof bindsin);
217 	bindsin.sin_family = AF_INET;
218 	bindsin.sin_addr.s_addr = argp->ypsetdom_addr.s_addr;
219 	bindsin.sin_port = argp->ypsetdom_port;
220 	rpc_received(argp->ypsetdom_domain, &bindsin, 1);
221 
222 	res = 1;
223 	return (void *)&res;
224 }
225 
226 static void
227 ypbindprog_2(rqstp, transp)
228 struct svc_req *rqstp;
229 register SVCXPRT *transp;
230 {
231 	union {
232 		char ypbindproc_domain_2_arg[MAXHOSTNAMELEN];
233 		struct ypbind_setdom ypbindproc_setdom_2_arg;
234 	} argument;
235 	struct authunix_parms *creds;
236 	char *result;
237 	bool_t (*xdr_argument)(), (*xdr_result)();
238 	char *(*local)();
239 
240 	switch (rqstp->rq_proc) {
241 	case YPBINDPROC_NULL:
242 		xdr_argument = xdr_void;
243 		xdr_result = xdr_void;
244 		local = (char *(*)()) ypbindproc_null_2;
245 		break;
246 
247 	case YPBINDPROC_DOMAIN:
248 		xdr_argument = xdr_domainname;
249 		xdr_result = xdr_ypbind_resp;
250 		local = (char *(*)()) ypbindproc_domain_2;
251 		break;
252 
253 	case YPBINDPROC_SETDOM:
254 		switch(rqstp->rq_cred.oa_flavor) {
255 		case AUTH_UNIX:
256 			creds = (struct authunix_parms *)rqstp->rq_clntcred;
257 			if( creds->aup_uid != 0) {
258 				svcerr_auth(transp, AUTH_BADCRED);
259 				return;
260 			}
261 			break;
262 		default:
263 			svcerr_auth(transp, AUTH_TOOWEAK);
264 			return;
265 		}
266 
267 		xdr_argument = xdr_ypbind_setdom;
268 		xdr_result = xdr_void;
269 		local = (char *(*)()) ypbindproc_setdom_2;
270 		break;
271 
272 	default:
273 		svcerr_noproc(transp);
274 		return;
275 	}
276 	bzero((char *)&argument, sizeof(argument));
277 	if (!svc_getargs(transp, xdr_argument, &argument)) {
278 		svcerr_decode(transp);
279 		return;
280 	}
281 	result = (*local)(transp, &argument, rqstp);
282 	if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
283 		svcerr_systemerr(transp);
284 	}
285 	return;
286 }
287 
288 main(argc, argv)
289 char **argv;
290 {
291 	char path[MAXPATHLEN];
292 	struct timeval tv;
293 	fd_set fdsr;
294 	int width;
295 	int i;
296 
297 	yp_get_default_domain(&domainname);
298 	if( domainname[0] == '\0') {
299 		fprintf(stderr, "domainname not set. Aborting.\n");
300 		exit(1);
301 	}
302 
303 	for(i=1; i<argc; i++) {
304 		if( strcmp("-ypset", argv[i]) == 0)
305 			ypsetmode = YPSET_ALL;
306 		else if (strcmp("-ypsetme", argv[i]) == 0)
307 			ypsetmode = YPSET_LOCAL;
308 	}
309 
310 	/* blow away everything in BINDINGDIR */
311 
312 
313 
314 #ifdef DAEMON
315 	switch(fork()) {
316 	case 0:
317 		break;
318 	case -1:
319 		perror("fork");
320 		exit(1);
321 	default:
322 		exit(0);
323 	}
324 	setsid();
325 #endif
326 
327 	pmap_unset(YPBINDPROG, YPBINDVERS);
328 
329 	udptransp = svcudp_create(RPC_ANYSOCK);
330 	if (udptransp == NULL) {
331 		fprintf(stderr, "cannot create udp service.");
332 		exit(1);
333 	}
334 	if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
335 	    IPPROTO_UDP)) {
336 		fprintf(stderr, "unable to register (YPBINDPROG, YPBINDVERS, udp).");
337 		exit(1);
338 	}
339 
340 	tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
341 	if (tcptransp == NULL) {
342 		fprintf(stderr, "cannot create tcp service.");
343 		exit(1);
344 	}
345 
346 	if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
347 	    IPPROTO_TCP)) {
348 		fprintf(stderr, "unable to register (YPBINDPROG, YPBINDVERS, tcp).");
349 		exit(1);
350 	}
351 
352 	if( (rpcsock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
353 		perror("socket");
354 		return -1;
355 	}
356 
357 	fcntl(rpcsock, F_SETFL, fcntl(rpcsock, F_GETFL, 0) | FNDELAY);
358 	i = 1;
359 	setsockopt(rpcsock, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i));
360 	rmtca.prog = YPPROG;
361 	rmtca.vers = YPVERS;
362 	rmtca.proc = YPPROC_DOMAIN_NONACK;
363 	rmtca.xdr_args = NULL;		/* set at call time */
364 	rmtca.args_ptr = NULL;		/* set at call time */
365 	rmtcr.port_ptr = &rmtcr_port;
366 	rmtcr.xdr_results = xdr_bool;
367 	rmtcr.results_ptr = (caddr_t)&rmtcr_outval;
368 
369 	/* build initial domain binding, make it "unsuccessful" */
370 	ypbindlist = (struct _dom_binding *)malloc(sizeof *ypbindlist);
371 	bzero((char *)ypbindlist, sizeof *ypbindlist);
372 	strncpy(ypbindlist->dom_domain, domainname, sizeof ypbindlist->dom_domain);
373 	ypbindlist->dom_vers = YPVERS;
374 	ypbindlist->dom_interval = 0;
375 	ypbindlist->dom_alive = 0;
376 	ypbindlist->dom_answered = 0;
377 	ypbindlist->dom_lockfd = -1;
378 	ypbindlist->dom_check_t = time(NULL) + ypbindlist->dom_interval;
379 	sprintf(path, "%s/%s.%d", BINDINGDIR, ypbindlist->dom_domain,
380 		ypbindlist->dom_vers);
381 	(void)unlink(path);
382 
383 	openlog(argv[0], LOG_PID, LOG_AUTH);
384 	width = getdtablesize();
385 
386 	while(1) {
387 		fdsr = svc_fdset;
388 		FD_SET(rpcsock, &fdsr);
389 		tv.tv_sec = 1;
390 		tv.tv_usec = 0;
391 
392 		switch(select(width, &fdsr, NULL, NULL, &tv)) {
393 		case 0:
394 			checkwork();
395 			break;
396 		case -1:
397 			perror("select\n");
398 			break;
399 		default:
400 			if(FD_ISSET(rpcsock, &fdsr)) {
401 				FD_CLR(rpcsock, &fdsr);
402 				handle_replies();
403 			}
404 			svc_getreqset(&fdsr);
405 			break;
406 		}
407 	}
408 }
409 
410 /*
411  * change to do something like this:
412  *
413  * STATE	TIME		ACTION		NEWTIME	NEWSTATE
414  * no binding	t==*		broadcast 	t=2	no binding
415  * binding	t==60		check server	t=10	binding
416  * binding	t=10		broadcast	t=2	no binding
417  */
418 
419 /*
420  * The above comment makes no sense whatsoever. This is what should
421  * be happening:
422  *
423  * - If we have any servers in our binding list marked alive, ping
424  *   them _and them alone_ only once every 60 seconds to make sure
425  *   they haven't crapped out on us (i.e. bother only the servers
426  *   we know about: *DON'T* continually bother everybody with broadcast
427  *   packets every 5 seconds like we used to).
428  * - If they don't respond within FAIL_THRESHOLD seconds after the ping,
429  *   they're toast: mark them unalive and start to scream and shout.
430  * - If we don't have any servers marked alive, then we're in desperate
431  *   trouble and we need to broadcast a cry for help every 5 seconds
432  *   until somebody answers us.
433  */
434 
435 checkwork()
436 {
437 	struct _dom_binding *ypdb;
438 	time_t t;
439 
440 	for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext) {
441 		time(&t);
442 		if (ypdb->dom_check_t < t) {
443 			broadcast(ypdb->dom_domain,
444 				ypdb->dom_server_addr, ypdb->dom_alive);
445 			ypdb->dom_check_t = t + ypdb->dom_interval;
446 			ypdb->dom_answered = 0;
447 			if (ypdb->dom_vers == 0)
448 				syslog (LOG_NOTICE,
449 				"NIS server [%s] for domain %s not responding.",
450 				inet_ntoa(ypdb->dom_server_addr.sin_addr),
451 				ypdb->dom_domain);
452 		} else
453 		if (!ypdb->dom_answered && ypdb->dom_alive &&
454 				ypdb->dom_check_t < (t + FAIL_THRESHOLD)) {
455 			ypdb->dom_check_t = ypdb->dom_alive =
456 				ypdb->dom_vers = 0;
457 			ypdb->dom_interval = 5;
458 		}
459 	}
460 }
461 
462 broadcast(dom, saddr, direct)
463 char *dom;
464 struct sockaddr_in saddr;
465 int direct;
466 {
467 	struct rpc_msg rpcmsg;
468 	char buf[1400], inbuf[8192];
469 	enum clnt_stat st;
470 	struct timeval tv;
471 	int outlen, i, sock, len;
472 	struct sockaddr_in bsin;
473 	struct ifconf ifc;
474 	struct ifreq ifreq, *ifr;
475 	struct in_addr in;
476 	AUTH *rpcua;
477 	XDR rpcxdr;
478 
479 	rmtca.xdr_args = xdr_domainname;
480 	rmtca.args_ptr = dom;
481 
482 	bzero((char *)&bsin, sizeof bsin);
483 	bsin.sin_family = AF_INET;
484 	bsin.sin_port = htons(PMAPPORT);
485 
486 	bzero((char *)&rpcxdr, sizeof rpcxdr);
487 	bzero((char *)&rpcmsg, sizeof rpcmsg);
488 
489 	rpcua = authunix_create_default();
490 	if( rpcua == (AUTH *)NULL) {
491 		/*printf("cannot get unix auth\n");*/
492 		return RPC_SYSTEMERROR;
493 	}
494 	rpcmsg.rm_direction = CALL;
495 	rpcmsg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
496 	rpcmsg.rm_call.cb_prog = PMAPPROG;
497 	rpcmsg.rm_call.cb_vers = PMAPVERS;
498 	rpcmsg.rm_call.cb_proc = PMAPPROC_CALLIT;
499 	rpcmsg.rm_call.cb_cred = rpcua->ah_cred;
500 	rpcmsg.rm_call.cb_verf = rpcua->ah_verf;
501 
502 	gettimeofday(&tv, (struct timezone *)0);
503 	rpcmsg.rm_xid = (int)dom;
504 	tv.tv_usec = 0;
505 	xdrmem_create(&rpcxdr, buf, sizeof buf, XDR_ENCODE);
506 	if( (!xdr_callmsg(&rpcxdr, &rpcmsg)) ) {
507 		st = RPC_CANTENCODEARGS;
508 		AUTH_DESTROY(rpcua);
509 		return st;
510 	}
511 	if( (!xdr_rmtcall_args(&rpcxdr, &rmtca)) ) {
512 		st = RPC_CANTENCODEARGS;
513 		AUTH_DESTROY(rpcua);
514 		return st;
515 	}
516 	outlen = (int)xdr_getpos(&rpcxdr);
517 	xdr_destroy(&rpcxdr);
518 	if(outlen<1) {
519 		AUTH_DESTROY(rpcua);
520 		return st;
521 	}
522 	AUTH_DESTROY(rpcua);
523 
524 	/* find all networks and send the RPC packet out them all */
525 	if( (sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
526 		perror("socket");
527 		return -1;
528 	}
529 
530 	/*
531 	 * It's impolite to blast broadcast packets all over the place
532 	 * when we don't really have to.
533 	 */
534 	if (!direct) {
535 		ifc.ifc_len = sizeof inbuf;
536 		ifc.ifc_buf = inbuf;
537 		if( ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
538 			close(sock);
539 			perror("ioctl(SIOCGIFCONF)");
540 			return -1;
541 		}
542 		ifr = ifc.ifc_req;
543 		ifreq.ifr_name[0] = '\0';
544 		for(i=0; i<ifc.ifc_len; i+=len, ifr=(struct ifreq *)((caddr_t)ifr+len)) {
545 #if defined(BSD) && BSD >= 199103
546 			len = sizeof ifr->ifr_name + ifr->ifr_addr.sa_len;
547 #else
548 			len = sizeof ifc.ifc_len / sizeof(struct ifreq);
549 #endif
550 			ifreq = *ifr;
551 			if( ifreq.ifr_addr.sa_family != AF_INET)
552 				continue;
553 			if( ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) {
554 				perror("ioctl(SIOCGIFFLAGS)");
555 				continue;
556 			}
557 			if( (ifreq.ifr_flags & IFF_UP) == 0)
558 				continue;
559 
560 			ifreq.ifr_flags &= (IFF_LOOPBACK | IFF_BROADCAST);
561 			if( ifreq.ifr_flags==IFF_BROADCAST ) {
562 				if( ioctl(sock, SIOCGIFBRDADDR, &ifreq) < 0 ) {
563 					perror("ioctl(SIOCGIFBRDADDR)");
564 					continue;
565 				}
566 			} else if( ifreq.ifr_flags==IFF_LOOPBACK ) {
567 				if( ioctl(sock, SIOCGIFADDR, &ifreq) < 0 ) {
568 					perror("ioctl(SIOCGIFADDR)");
569 					continue;
570 				}
571 			} else
572 				continue;
573 
574 			in = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
575 			bsin.sin_addr = in;
576 			if( sendto(rpcsock, buf, outlen, 0,
577 				(struct sockaddr *)&bsin, sizeof bsin) < 0 )
578 				perror("sendto");
579 		}
580 	} else {
581 		in = ((struct sockaddr_in *)&saddr)->sin_addr;
582 		bsin.sin_addr = in;
583 		if( sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bsin,
584 		   sizeof bsin) < 0 )
585 			perror("sendto");
586 	}
587 
588 	close(sock);
589 	return 0;
590 }
591 
592 /*enum clnt_stat*/
593 handle_replies()
594 {
595 	char buf[1400];
596 	int fromlen, inlen;
597 	struct sockaddr_in raddr;
598 	struct rpc_msg msg;
599 	XDR xdr;
600 
601 recv_again:
602 	bzero((char *)&xdr, sizeof(xdr));
603 	bzero((char *)&msg, sizeof(msg));
604 	msg.acpted_rply.ar_verf = _null_auth;
605 	msg.acpted_rply.ar_results.where = (caddr_t)&rmtcr;
606 	msg.acpted_rply.ar_results.proc = xdr_rmtcallres;
607 
608 try_again:
609 	fromlen = sizeof (struct sockaddr);
610 	inlen = recvfrom(rpcsock, buf, sizeof buf, 0,
611 		(struct sockaddr *)&raddr, &fromlen);
612 	if(inlen<0) {
613 		if(errno==EINTR)
614 			goto try_again;
615 		return RPC_CANTRECV;
616 	}
617 	if(inlen<sizeof(u_long))
618 		goto recv_again;
619 
620 	/*
621 	 * see if reply transaction id matches sent id.
622 	 * If so, decode the results.
623 	 */
624 	xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
625 	if( xdr_replymsg(&xdr, &msg)) {
626 		if( (msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
627 		   (msg.acpted_rply.ar_stat == SUCCESS)) {
628 			raddr.sin_port = htons((u_short)rmtcr_port);
629 			rpc_received(msg.rm_xid, &raddr, 0);
630 		}
631 	}
632 	xdr.x_op = XDR_FREE;
633 	msg.acpted_rply.ar_results.proc = xdr_void;
634 	xdr_destroy(&xdr);
635 
636 	return RPC_SUCCESS;
637 }
638 
639 /*
640  * LOOPBACK IS MORE IMPORTANT: PUT IN HACK
641  */
642 rpc_received(dom, raddrp, force)
643 char *dom;
644 struct sockaddr_in *raddrp;
645 int force;
646 {
647 	struct _dom_binding *ypdb;
648 	struct iovec iov[2];
649 	struct ypbind_resp ybr;
650 	char path[MAXPATHLEN];
651 	int fd;
652 
653 	/*printf("returned from %s about %s\n", inet_ntoa(raddrp->sin_addr), dom);*/
654 
655 	if(dom==NULL)
656 		return;
657 
658 	for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext)
659 		if( strcmp(ypdb->dom_domain, dom) == 0)
660 			break;
661 
662 	if(ypdb==NULL) {
663 		if(force==0)
664 			return;
665 		ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
666 		bzero((char *)ypdb, sizeof *ypdb);
667 		strncpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain);
668 		ypdb->dom_lockfd = -1;
669 		ypdb->dom_pnext = ypbindlist;
670 		ypbindlist = ypdb;
671 	}
672 
673 	/* soft update, alive, less than FAIL_THRESHOLD seconds old */
674 	if(ypdb->dom_alive==1 && (force==0 || ypdb->dom_answered == 0)
675 		&& (ypdb->dom_check_t - FAIL_THRESHOLD) > time(NULL)) {
676 		ypdb->dom_answered = 1;
677 		ypdb->dom_interval = 60;
678 		return;
679 	}
680 
681 	/*
682 	 * We've recovered from a crash: inform the world.
683 	 */
684 	if (ypdb->dom_vers == 0)
685 		syslog(LOG_NOTICE, "NIS server [%s] for domain %s OK.",
686 			inet_ntoa(ypdb->dom_server_addr.sin_addr),
687                         ypdb->dom_domain);
688 
689 	bcopy((char *)raddrp, (char *)&ypdb->dom_server_addr,
690 		sizeof ypdb->dom_server_addr);
691 
692 	ypdb->dom_vers = YPVERS;
693 	ypdb->dom_alive = 1;
694 	ypdb->dom_answered = 1;
695 	ypdb->dom_interval = 60;
696 	ypdb->dom_check_t = time(NULL) + ypdb->dom_interval;
697 
698 	if(ypdb->dom_lockfd != -1)
699 		close(ypdb->dom_lockfd);
700 
701 	sprintf(path, "%s/%s.%d", BINDINGDIR,
702 		ypdb->dom_domain, ypdb->dom_vers);
703 #ifdef O_SHLOCK
704 	if( (fd=open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
705 		(void)mkdir(BINDINGDIR, 0755);
706 		if( (fd=open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1)
707 			return;
708 	}
709 #else
710 	if( (fd=open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
711 		(void)mkdir(BINDINGDIR, 0755);
712 		if( (fd=open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1)
713 			return;
714 	}
715 	flock(fd, LOCK_SH);
716 #endif
717 
718 	/*
719 	 * ok, if BINDINGDIR exists, and we can create the binding file,
720 	 * then write to it..
721 	 */
722 	ypdb->dom_lockfd = fd;
723 
724 	iov[0].iov_base = (caddr_t)&(udptransp->xp_port);
725 	iov[0].iov_len = sizeof udptransp->xp_port;
726 	iov[1].iov_base = (caddr_t)&ybr;
727 	iov[1].iov_len = sizeof ybr;
728 
729 	bzero(&ybr, sizeof ybr);
730 	ybr.ypbind_status = YPBIND_SUCC_VAL;
731 	ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr = raddrp->sin_addr;
732 	ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port = raddrp->sin_port;
733 
734 	if( writev(ypdb->dom_lockfd, iov, 2) != iov[0].iov_len + iov[1].iov_len) {
735 		perror("write");
736 		close(ypdb->dom_lockfd);
737 		ypdb->dom_lockfd = -1;
738 		return;
739 	}
740 }
741