xref: /freebsd/lib/libc/yp/yplib.c (revision ce834215a70ff69e7e222827437116eee2f9ac6f)
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: yplib.c,v 1.28 1997/02/22 15:05:02 peter Exp $";
32 #endif
33 
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/file.h>
38 #include <sys/uio.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <rpc/rpc.h>
45 #include <rpc/xdr.h>
46 #include <rpcsvc/yp.h>
47 
48 
49 /*
50  * We have to define these here due to clashes between yp_prot.h and
51  * yp.h.
52  */
53 
54 struct dom_binding {
55 	struct dom_binding *dom_pnext;
56 	char dom_domain[YPMAXDOMAIN + 1];
57 	struct sockaddr_in dom_server_addr;
58 	u_short dom_server_port;
59 	int dom_socket;
60 	CLIENT *dom_client;
61 	u_short dom_local_port;	/* now I finally know what this is for. */
62 	long dom_vers;
63 };
64 
65 #include <rpcsvc/ypclnt.h>
66 
67 #ifndef BINDINGDIR
68 #define BINDINGDIR "/var/yp/binding"
69 #endif
70 #define YPMATCHCACHE
71 #define MAX_RETRIES 20
72 
73 extern bool_t xdr_domainname(), xdr_ypbind_resp();
74 extern bool_t xdr_ypreq_key(), xdr_ypresp_val();
75 extern bool_t xdr_ypreq_nokey(), xdr_ypresp_key_val();
76 extern bool_t xdr_ypresp_all(), xdr_ypresp_all_seq();
77 extern bool_t xdr_ypresp_master();
78 
79 int (*ypresp_allfn)();
80 void *ypresp_data;
81 
82 static void _yp_unbind __P(( struct dom_binding * ));
83 struct dom_binding *_ypbindlist;
84 static char _yp_domain[MAXHOSTNAMELEN];
85 int _yplib_timeout = 10;
86 
87 #ifdef YPMATCHCACHE
88 int _yplib_cache = 5;
89 
90 static struct ypmatch_ent {
91 	struct ypmatch_ent *next;
92 	char *map, *key, *val;
93 	int keylen, vallen;
94 	time_t expire_t;
95 } *ypmc;
96 
97 static void
98 ypmatch_add(map, key, keylen, val, vallen)
99 	char *map;
100 	char *key;
101 	int keylen;
102 	char *val;
103 	int vallen;
104 {
105 	struct ypmatch_ent *ep;
106 	time_t t;
107 
108 	time(&t);
109 
110 	for(ep=ypmc; ep; ep=ep->next)
111 		if(ep->expire_t < t)
112 			break;
113 	if(ep==NULL) {
114 		ep = (struct ypmatch_ent *)malloc(sizeof *ep);
115 		bzero((char *)ep, sizeof *ep);
116 		if(ypmc)
117 			ep->next = ypmc;
118 		ypmc = ep;
119 	}
120 
121 	if(ep->key)
122 		free(ep->key);
123 	if(ep->val)
124 		free(ep->val);
125 
126 	ep->key = NULL;
127 	ep->val = NULL;
128 
129 	ep->key = (char *)malloc(keylen);
130 	if(ep->key==NULL)
131 		return;
132 
133 	ep->val = (char *)malloc(vallen);
134 	if(ep->key==NULL) {
135 		free(ep->key);
136 		ep->key = NULL;
137 		return;
138 	}
139 	ep->keylen = keylen;
140 	ep->vallen = vallen;
141 
142 	bcopy(key, ep->key, ep->keylen);
143 	bcopy(val, ep->val, ep->vallen);
144 
145 	if(ep->map) {
146 		if( strcmp(ep->map, map) ) {
147 			free(ep->map);
148 			ep->map = strdup(map);
149 		}
150 	} else {
151 		ep->map = strdup(map);
152 	}
153 
154 	ep->expire_t = t + _yplib_cache;
155 }
156 
157 static bool_t
158 ypmatch_find(map, key, keylen, val, vallen)
159 	char *map;
160 	char *key;
161 	int keylen;
162 	char **val;
163 	int *vallen;
164 {
165 	struct ypmatch_ent *ep;
166 	time_t t;
167 
168 	if(ypmc==NULL)
169 		return 0;
170 
171 	time(&t);
172 
173 	for(ep=ypmc; ep; ep=ep->next) {
174 		if(ep->keylen != keylen)
175 			continue;
176 		if(strcmp(ep->map, map))
177 			continue;
178 		if(bcmp(ep->key, key, keylen))
179 			continue;
180 		if(t > ep->expire_t)
181 			continue;
182 
183 		*val = ep->val;
184 		*vallen = ep->vallen;
185 		return 1;
186 	}
187 	return 0;
188 }
189 #endif
190 
191 char *
192 ypbinderr_string(incode)
193 	int incode;
194 {
195 	static char err[80];
196 	switch(incode) {
197 	case 0:
198 		return "Success";
199 	case YPBIND_ERR_ERR:
200 		return "Internal ypbind error";
201 	case YPBIND_ERR_NOSERV:
202 		return "Domain not bound";
203 	case YPBIND_ERR_RESC:
204 		return "System resource allocation failure";
205 	}
206 	sprintf(err, "Unknown ypbind error: #%d\n", incode);
207 	return err;
208 }
209 
210 int
211 _yp_dobind(dom, ypdb)
212 	char *dom;
213 	struct dom_binding **ypdb;
214 {
215 	static pid_t pid = -1;
216 	char path[MAXPATHLEN];
217 	struct dom_binding *ysd, *ysd2;
218 	struct ypbind_resp ypbr;
219 	struct timeval tv;
220 	struct sockaddr_in clnt_sin;
221 	int clnt_sock, fd;
222 	pid_t gpid;
223 	CLIENT *client;
224 	int new = 0, r;
225 	int retries = 0;
226 	struct sockaddr_in check;
227 	int checklen = sizeof(struct sockaddr_in);
228 
229 	/* Not allowed; bad doggie. Bad. */
230 	if (strchr(dom, '/') != NULL)
231 		return(YPERR_BADARGS);
232 
233 	gpid = getpid();
234 	if( !(pid==-1 || pid==gpid) ) {
235 		ysd = _ypbindlist;
236 		while(ysd) {
237 			if(ysd->dom_client != NULL)
238 				_yp_unbind(ysd);
239 			ysd2 = ysd->dom_pnext;
240 			free(ysd);
241 			ysd = ysd2;
242 		}
243 		_ypbindlist = NULL;
244 	}
245 	pid = gpid;
246 
247 	if(ypdb!=NULL)
248 		*ypdb = NULL;
249 
250 	if(dom==NULL || strlen(dom)==0)
251 		return YPERR_BADARGS;
252 
253 	for(ysd = _ypbindlist; ysd; ysd = ysd->dom_pnext)
254 		if( strcmp(dom, ysd->dom_domain) == 0)
255 			break;
256 
257 
258 	if(ysd==NULL) {
259 		ysd = (struct dom_binding *)malloc(sizeof *ysd);
260 		bzero((char *)ysd, sizeof *ysd);
261 		ysd->dom_socket = -1;
262 		ysd->dom_vers = 0;
263 		new = 1;
264 	} else {
265 	/* Check the socket -- may have been hosed by the caller. */
266 		if (getsockname(ysd->dom_socket, (struct sockaddr *)&check,
267 		    &checklen) == -1 || check.sin_family != AF_INET ||
268 		    check.sin_port != ysd->dom_local_port) {
269 		/* Socket became bogus somehow... need to rebind. */
270 			int save, sock;
271 
272 			sock = ysd->dom_socket;
273 			save = dup(ysd->dom_socket);
274 			if (ysd->dom_client != NULL)
275 				clnt_destroy(ysd->dom_client);
276 			ysd->dom_vers = 0;
277 			ysd->dom_client = NULL;
278 			sock = dup2(save, sock);
279 			close(save);
280 		}
281 	}
282 
283 again:
284 	retries++;
285 	if (retries > MAX_RETRIES) {
286 		if (new)
287 			free(ysd);
288 		return(YPERR_YPBIND);
289 	}
290 #ifdef BINDINGDIR
291 	if(ysd->dom_vers==0) {
292 		/*
293 		 * We're trying to make a new binding: zorch the
294 		 * existing handle now (if any).
295 		 */
296 		if(ysd->dom_client != NULL) {
297 			clnt_destroy(ysd->dom_client);
298 			ysd->dom_client = NULL;
299 			ysd->dom_socket = -1;
300 		}
301 		sprintf(path, "%s/%s.%d", BINDINGDIR, dom, 2);
302 		if( (fd=open(path, O_RDONLY)) == -1) {
303 			/* no binding file, YP is dead. */
304 			/* Try to bring it back to life. */
305 			close(fd);
306 			goto skipit;
307 		}
308 		if( flock(fd, LOCK_EX|LOCK_NB) == -1 && errno==EWOULDBLOCK) {
309 			struct iovec iov[2];
310 			struct ypbind_resp ybr;
311 			u_short	ypb_port;
312 
313 			iov[0].iov_base = (caddr_t)&ypb_port;
314 			iov[0].iov_len = sizeof ypb_port;
315 			iov[1].iov_base = (caddr_t)&ybr;
316 			iov[1].iov_len = sizeof ybr;
317 
318 			r = readv(fd, iov, 2);
319 			if(r != iov[0].iov_len + iov[1].iov_len) {
320 				close(fd);
321 				ysd->dom_vers = -1;
322 				goto again;
323 			}
324 
325 			bzero(&ysd->dom_server_addr, sizeof ysd->dom_server_addr);
326 			ysd->dom_server_addr.sin_family = AF_INET;
327 			ysd->dom_server_addr.sin_len = sizeof(struct sockaddr_in);
328 			ysd->dom_server_addr.sin_addr.s_addr =
329 			    *(u_long *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr;
330 			ysd->dom_server_addr.sin_port =
331 			    *(u_short *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port;
332 
333 			ysd->dom_server_port = ysd->dom_server_addr.sin_port;
334 			close(fd);
335 			goto gotit;
336 		} else {
337 			/* no lock on binding file, YP is dead. */
338 			/* Try to bring it back to life. */
339 			close(fd);
340 			goto skipit;
341 		}
342 	}
343 skipit:
344 #endif
345 	if(ysd->dom_vers==-1 || ysd->dom_vers==0) {
346 		/*
347 		 * We're trying to make a new binding: zorch the
348 		 * existing handle now (if any).
349 		 */
350 		if(ysd->dom_client != NULL) {
351 			clnt_destroy(ysd->dom_client);
352 			ysd->dom_client = NULL;
353 			ysd->dom_socket = -1;
354 		}
355 		bzero((char *)&clnt_sin, sizeof clnt_sin);
356 		clnt_sin.sin_family = AF_INET;
357 		clnt_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
358 
359 		clnt_sock = RPC_ANYSOCK;
360 		client = clnttcp_create(&clnt_sin, YPBINDPROG, YPBINDVERS, &clnt_sock,
361 			0, 0);
362 		if(client==NULL) {
363 			/*
364 			 * These conditions indicate ypbind just isn't
365 			 * alive -- we probably don't want to shoot our
366 			 * mouth off in this case; instead generate error
367 			 * messages only for really exotic problems.
368 			 */
369 			if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED &&
370 			   (rpc_createerr.cf_stat != RPC_SYSTEMERROR &&
371 			   rpc_createerr.cf_error.re_errno == ECONNREFUSED))
372 				clnt_pcreateerror("clnttcp_create");
373 			if(new)
374 				free(ysd);
375 			return (YPERR_YPBIND);
376 		}
377 
378 		/*
379 		 * Check the port number -- should be < IPPORT_RESERVED.
380 		 * If not, it's possible someone has registered a bogus
381 		 * ypbind with the portmapper and is trying to trick us.
382 		 */
383 		if (ntohs(clnt_sin.sin_port) >= IPPORT_RESERVED) {
384 			if (client != NULL)
385 				clnt_destroy(client);
386 			if (new)
387 				free(ysd);
388 			return(YPERR_YPBIND);
389 		}
390 		tv.tv_sec = _yplib_timeout/2;
391 		tv.tv_usec = 0;
392 		r = clnt_call(client, YPBINDPROC_DOMAIN,
393 			xdr_domainname, (char *)&dom, xdr_ypbind_resp, &ypbr, tv);
394 		if(r != RPC_SUCCESS) {
395 			clnt_destroy(client);
396 			ysd->dom_vers = -1;
397 			if (r == RPC_PROGUNAVAIL || r == RPC_PROCUNAVAIL) {
398 				if (new)
399 					free(ysd);
400 				return(YPERR_YPBIND);
401 			}
402 			fprintf(stderr,
403 			"YP: server for domain %s not responding, retrying\n", dom);
404 			goto again;
405 		} else {
406 			if (ypbr.ypbind_status != YPBIND_SUCC_VAL) {
407 				clnt_destroy(client);
408 				ysd->dom_vers = -1;
409 				sleep(_yplib_timeout/2);
410 				goto again;
411 			}
412 		}
413 		clnt_destroy(client);
414 
415 		bzero((char *)&ysd->dom_server_addr, sizeof ysd->dom_server_addr);
416 		ysd->dom_server_addr.sin_family = AF_INET;
417 		ysd->dom_server_addr.sin_port =
418 			*(u_short *)&ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port;
419 		ysd->dom_server_addr.sin_addr.s_addr =
420 			*(u_long *)&ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr;
421 
422 		/*
423 		 * We could do a reserved port check here too, but this
424 		 * could pose compatibility problems. The local ypbind is
425 		 * supposed to decide whether or not to trust yp servers
426 		 * on insecure ports. For now, we trust its judgement.
427 		 */
428 		ysd->dom_server_port =
429 			*(u_short *)&ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port;
430 gotit:
431 		ysd->dom_vers = YPVERS;
432 		strcpy(ysd->dom_domain, dom);
433 	}
434 
435 	/* Don't rebuild the connection to the server unless we have to. */
436 	if (ysd->dom_client == NULL) {
437 		tv.tv_sec = _yplib_timeout/2;
438 		tv.tv_usec = 0;
439 		ysd->dom_socket = RPC_ANYSOCK;
440 		ysd->dom_client = clntudp_create(&ysd->dom_server_addr,
441 			YPPROG, YPVERS, tv, &ysd->dom_socket);
442 		if(ysd->dom_client==NULL) {
443 			clnt_pcreateerror("clntudp_create");
444 			ysd->dom_vers = -1;
445 			goto again;
446 		}
447 		if( fcntl(ysd->dom_socket, F_SETFD, 1) == -1)
448 			perror("fcntl: F_SETFD");
449 		/*
450 		 * We want a port number associated with this socket
451 		 * so that we can check its authenticity later.
452 		 */
453 		checklen = sizeof(struct sockaddr_in);
454 		bzero((char *)&check, checklen);
455 		bind(ysd->dom_socket, (struct sockaddr *)&check, checklen);
456 		check.sin_family = AF_INET;
457 		if (!getsockname(ysd->dom_socket,
458 		    (struct sockaddr *)&check, &checklen)) {
459 			ysd->dom_local_port = check.sin_port;
460 		} else {
461 			clnt_destroy(ysd->dom_client);
462 			if (new)
463 				free(ysd);
464 			return(YPERR_YPBIND);
465 		}
466 	}
467 
468 	if(new) {
469 		ysd->dom_pnext = _ypbindlist;
470 		_ypbindlist = ysd;
471 	}
472 
473 	if(ypdb!=NULL)
474 		*ypdb = ysd;
475 	return 0;
476 }
477 
478 static void
479 _yp_unbind(ypb)
480 	struct dom_binding *ypb;
481 {
482 	struct sockaddr_in check;
483 	int checklen = sizeof(struct sockaddr_in);
484 
485 	if (ypb->dom_client != NULL) {
486 		/* Check the socket -- may have been hosed by the caller. */
487 		if (getsockname(ypb->dom_socket, (struct sockaddr *)&check,
488 	    	&checklen) == -1 || check.sin_family != AF_INET ||
489 	    	check.sin_port != ypb->dom_local_port) {
490 			int save, sock;
491 
492 			sock = ypb->dom_socket;
493 			save = dup(ypb->dom_socket);
494 			clnt_destroy(ypb->dom_client);
495 			sock = dup2(save, sock);
496 			close(save);
497 		} else
498 			clnt_destroy(ypb->dom_client);
499 	}
500 
501 	ypb->dom_client = NULL;
502 	ypb->dom_socket = -1;
503 	ypb->dom_vers = -1;
504 }
505 
506 int
507 yp_bind(dom)
508 	char *dom;
509 {
510 	return _yp_dobind(dom, NULL);
511 }
512 
513 void
514 yp_unbind(dom)
515 	char *dom;
516 {
517 	struct dom_binding *ypb, *ypbp;
518 
519 	ypbp = NULL;
520 	for(ypb=_ypbindlist; ypb; ypb=ypb->dom_pnext) {
521 		if( strcmp(dom, ypb->dom_domain) == 0) {
522 			_yp_unbind(ypb);
523 			if(ypbp)
524 				ypbp->dom_pnext = ypb->dom_pnext;
525 			else
526 				_ypbindlist = ypb->dom_pnext;
527 			free(ypb);
528 			return;
529 		}
530 		ypbp = ypb;
531 	}
532 	return;
533 }
534 
535 int
536 yp_match(indomain, inmap, inkey, inkeylen, outval, outvallen)
537 	char *indomain;
538 	char *inmap;
539 	const char *inkey;
540 	int inkeylen;
541 	char **outval;
542 	int *outvallen;
543 {
544 	struct dom_binding *ysd;
545 	struct ypresp_val yprv;
546 	struct timeval tv;
547 	struct ypreq_key yprk;
548 	int r;
549 
550 	*outval = NULL;
551 	*outvallen = 0;
552 
553 	/* Sanity check */
554 
555 	if (inkey == NULL || !strlen(inkey) || inkeylen <= 0 ||
556 	    inmap == NULL || !strlen(inmap) ||
557 	    indomain == NULL || !strlen(indomain))
558 		return YPERR_BADARGS;
559 
560 #ifdef YPMATCHCACHE
561 	if( !strcmp(_yp_domain, indomain) && ypmatch_find(inmap, inkey,
562 	    inkeylen, &yprv.val.valdat_val, &yprv.val.valdat_len)) {
563 		*outvallen = yprv.val.valdat_len;
564 		*outval = (char *)malloc(*outvallen+1);
565 		bcopy(yprv.val.valdat_val, *outval, *outvallen);
566 		(*outval)[*outvallen] = '\0';
567 		return 0;
568 	}
569 #endif
570 
571 again:
572 	if( _yp_dobind(indomain, &ysd) != 0)
573 		return YPERR_DOMAIN;
574 
575 	tv.tv_sec = _yplib_timeout;
576 	tv.tv_usec = 0;
577 
578 	yprk.domain = indomain;
579 	yprk.map = inmap;
580 	yprk.key.keydat_val = (char *)inkey;
581 	yprk.key.keydat_len = inkeylen;
582 
583 	bzero((char *)&yprv, sizeof yprv);
584 
585 	r = clnt_call(ysd->dom_client, YPPROC_MATCH,
586 		xdr_ypreq_key, &yprk, xdr_ypresp_val, &yprv, tv);
587 	if(r != RPC_SUCCESS) {
588 		clnt_perror(ysd->dom_client, "yp_match: clnt_call");
589 		_yp_unbind(ysd);
590 		goto again;
591 	}
592 
593 	if( !(r=ypprot_err(yprv.stat)) ) {
594 		*outvallen = yprv.val.valdat_len;
595 		*outval = (char *)malloc(*outvallen+1);
596 		bcopy(yprv.val.valdat_val, *outval, *outvallen);
597 		(*outval)[*outvallen] = '\0';
598 #ifdef YPMATCHCACHE
599 		if( strcmp(_yp_domain, indomain)==0 )
600 			 ypmatch_add(inmap, inkey, inkeylen, *outval, *outvallen);
601 #endif
602 	}
603 
604 	xdr_free(xdr_ypresp_val, (char *)&yprv);
605 	return r;
606 }
607 
608 int
609 yp_get_default_domain(domp)
610 char **domp;
611 {
612 	*domp = NULL;
613 	if(_yp_domain[0] == '\0')
614 		if( getdomainname(_yp_domain, sizeof _yp_domain))
615 			return YPERR_NODOM;
616 	*domp = _yp_domain;
617 	return 0;
618 }
619 
620 int
621 yp_first(indomain, inmap, outkey, outkeylen, outval, outvallen)
622 	char *indomain;
623 	char *inmap;
624 	char **outkey;
625 	int *outkeylen;
626 	char **outval;
627 	int *outvallen;
628 {
629 	struct ypresp_key_val yprkv;
630 	struct ypreq_nokey yprnk;
631 	struct dom_binding *ysd;
632 	struct timeval tv;
633 	int r;
634 
635 	/* Sanity check */
636 
637 	if (indomain == NULL || !strlen(indomain) ||
638 	    inmap == NULL || !strlen(inmap))
639 		return YPERR_BADARGS;
640 
641 	*outkey = *outval = NULL;
642 	*outkeylen = *outvallen = 0;
643 
644 again:
645 	if( _yp_dobind(indomain, &ysd) != 0)
646 		return YPERR_DOMAIN;
647 
648 	tv.tv_sec = _yplib_timeout;
649 	tv.tv_usec = 0;
650 
651 	yprnk.domain = indomain;
652 	yprnk.map = inmap;
653 	bzero((char *)&yprkv, sizeof yprkv);
654 
655 	r = clnt_call(ysd->dom_client, YPPROC_FIRST,
656 		xdr_ypreq_nokey, &yprnk, xdr_ypresp_key_val, &yprkv, tv);
657 	if(r != RPC_SUCCESS) {
658 		clnt_perror(ysd->dom_client, "yp_first: clnt_call");
659 		_yp_unbind(ysd);
660 		goto again;
661 	}
662 	if( !(r=ypprot_err(yprkv.stat)) ) {
663 		*outkeylen = yprkv.key.keydat_len;
664 		*outkey = (char *)malloc(*outkeylen+1);
665 		bcopy(yprkv.key.keydat_val, *outkey, *outkeylen);
666 		(*outkey)[*outkeylen] = '\0';
667 		*outvallen = yprkv.val.valdat_len;
668 		*outval = (char *)malloc(*outvallen+1);
669 		bcopy(yprkv.val.valdat_val, *outval, *outvallen);
670 		(*outval)[*outvallen] = '\0';
671 	}
672 
673 	xdr_free(xdr_ypresp_key_val, (char *)&yprkv);
674 	return r;
675 }
676 
677 int
678 yp_next(indomain, inmap, inkey, inkeylen, outkey, outkeylen, outval, outvallen)
679 	char *indomain;
680 	char *inmap;
681 	char *inkey;
682 	int inkeylen;
683 	char **outkey;
684 	int *outkeylen;
685 	char **outval;
686 	int *outvallen;
687 {
688 	struct ypresp_key_val yprkv;
689 	struct ypreq_key yprk;
690 	struct dom_binding *ysd;
691 	struct timeval tv;
692 	int r;
693 
694 	/* Sanity check */
695 
696 	if (inkey == NULL || !strlen(inkey) || inkeylen <= 0 ||
697 	    inmap == NULL || !strlen(inmap) ||
698 	    indomain == NULL || !strlen(indomain))
699 		return YPERR_BADARGS;
700 
701 	*outkey = *outval = NULL;
702 	*outkeylen = *outvallen = 0;
703 
704 again:
705 	if( _yp_dobind(indomain, &ysd) != 0)
706 		return YPERR_DOMAIN;
707 
708 	tv.tv_sec = _yplib_timeout;
709 	tv.tv_usec = 0;
710 
711 	yprk.domain = indomain;
712 	yprk.map = inmap;
713 	yprk.key.keydat_val = inkey;
714 	yprk.key.keydat_len = inkeylen;
715 	bzero((char *)&yprkv, sizeof yprkv);
716 
717 	r = clnt_call(ysd->dom_client, YPPROC_NEXT,
718 		xdr_ypreq_key, &yprk, xdr_ypresp_key_val, &yprkv, tv);
719 	if(r != RPC_SUCCESS) {
720 		clnt_perror(ysd->dom_client, "yp_next: clnt_call");
721 		_yp_unbind(ysd);
722 		goto again;
723 	}
724 	if( !(r=ypprot_err(yprkv.stat)) ) {
725 		*outkeylen = yprkv.key.keydat_len;
726 		*outkey = (char *)malloc(*outkeylen+1);
727 		bcopy(yprkv.key.keydat_val, *outkey, *outkeylen);
728 		(*outkey)[*outkeylen] = '\0';
729 		*outvallen = yprkv.val.valdat_len;
730 		*outval = (char *)malloc(*outvallen+1);
731 		bcopy(yprkv.val.valdat_val, *outval, *outvallen);
732 		(*outval)[*outvallen] = '\0';
733 	}
734 
735 	xdr_free(xdr_ypresp_key_val, (char *)&yprkv);
736 	return r;
737 }
738 
739 int
740 yp_all(indomain, inmap, incallback)
741 	char *indomain;
742 	char *inmap;
743 	struct ypall_callback *incallback;
744 {
745 	struct ypreq_nokey yprnk;
746 	struct dom_binding *ysd;
747 	struct timeval tv;
748 	struct sockaddr_in clnt_sin;
749 	CLIENT *clnt;
750 	u_long status, savstat;
751 	int clnt_sock;
752 
753 	/* Sanity check */
754 
755 	if (indomain == NULL || !strlen(indomain) ||
756 	    inmap == NULL || !strlen(inmap))
757 		return YPERR_BADARGS;
758 
759 again:
760 
761 	if( _yp_dobind(indomain, &ysd) != 0)
762 		return YPERR_DOMAIN;
763 
764 	tv.tv_sec = _yplib_timeout;
765 	tv.tv_usec = 0;
766 
767 	/* YPPROC_ALL manufactures its own channel to ypserv using TCP */
768 
769 	clnt_sock = RPC_ANYSOCK;
770 	clnt_sin = ysd->dom_server_addr;
771 	clnt_sin.sin_port = 0;
772 	clnt = clnttcp_create(&clnt_sin, YPPROG, YPVERS, &clnt_sock, 0, 0);
773 	if(clnt==NULL) {
774 		printf("clnttcp_create failed\n");
775 		return YPERR_PMAP;
776 	}
777 
778 	yprnk.domain = indomain;
779 	yprnk.map = inmap;
780 	ypresp_allfn = incallback->foreach;
781 	ypresp_data = (void *)incallback->data;
782 
783 	if (clnt_call(clnt, YPPROC_ALL,
784 		xdr_ypreq_nokey, &yprnk,
785 		xdr_ypresp_all_seq, &status, tv) != RPC_SUCCESS) {
786 			clnt_perror(ysd->dom_client, "yp_next: clnt_call");
787 			clnt_destroy(clnt);
788 			_yp_unbind(ysd);
789 			goto again;
790 	}
791 
792 	clnt_destroy(clnt);
793 	savstat = status;
794 	xdr_free(xdr_ypresp_all_seq, (char *)&status);	/* not really needed... */
795 	if(savstat != YP_NOMORE)
796 		return ypprot_err(savstat);
797 	return 0;
798 }
799 
800 int
801 yp_order(indomain, inmap, outorder)
802 	char *indomain;
803 	char *inmap;
804 	int *outorder;
805 {
806  	struct dom_binding *ysd;
807 	struct ypresp_order ypro;
808 	struct ypreq_nokey yprnk;
809 	struct timeval tv;
810 	int r;
811 
812 	/* Sanity check */
813 
814 	if (indomain == NULL || !strlen(indomain) ||
815 	    inmap == NULL || !strlen(inmap))
816 		return YPERR_BADARGS;
817 
818 again:
819 	if( _yp_dobind(indomain, &ysd) != 0)
820 		return YPERR_DOMAIN;
821 
822 	tv.tv_sec = _yplib_timeout;
823 	tv.tv_usec = 0;
824 
825 	yprnk.domain = indomain;
826 	yprnk.map = inmap;
827 
828 	bzero((char *)(char *)&ypro, sizeof ypro);
829 
830 	r = clnt_call(ysd->dom_client, YPPROC_ORDER,
831 		xdr_ypreq_nokey, &yprnk, xdr_ypresp_order, &ypro, tv);
832 
833 	/*
834 	 * NIS+ in YP compat mode doesn't support the YPPROC_ORDER
835 	 * procedure.
836 	 */
837 	if (r == RPC_PROCUNAVAIL) {
838 		return(YPERR_YPERR);
839 	}
840 
841 	if(r != RPC_SUCCESS) {
842 		clnt_perror(ysd->dom_client, "yp_order: clnt_call");
843 		_yp_unbind(ysd);
844 		goto again;
845 	}
846 
847 	if( !(r=ypprot_err(ypro.stat)) ) {
848 		*outorder = ypro.ordernum;
849 	}
850 
851 	xdr_free(xdr_ypresp_order, (char *)&ypro);
852 	return (r);
853 }
854 
855 int
856 yp_master(indomain, inmap, outname)
857 	char *indomain;
858 	char *inmap;
859 	char **outname;
860 {
861 	struct dom_binding *ysd;
862 	struct ypresp_master yprm;
863 	struct ypreq_nokey yprnk;
864 	struct timeval tv;
865 	int r;
866 
867 	/* Sanity check */
868 
869 	if (indomain == NULL || !strlen(indomain) ||
870 	    inmap == NULL || !strlen(inmap))
871 		return YPERR_BADARGS;
872 again:
873 	if( _yp_dobind(indomain, &ysd) != 0)
874 		return YPERR_DOMAIN;
875 
876 	tv.tv_sec = _yplib_timeout;
877 	tv.tv_usec = 0;
878 
879 	yprnk.domain = indomain;
880 	yprnk.map = inmap;
881 
882 	bzero((char *)&yprm, sizeof yprm);
883 
884 	r = clnt_call(ysd->dom_client, YPPROC_MASTER,
885 		xdr_ypreq_nokey, &yprnk, xdr_ypresp_master, &yprm, tv);
886 	if(r != RPC_SUCCESS) {
887 		clnt_perror(ysd->dom_client, "yp_master: clnt_call");
888 		_yp_unbind(ysd);
889 		goto again;
890 	}
891 
892 	if( !(r=ypprot_err(yprm.stat)) ) {
893 		*outname = (char *)strdup(yprm.peer);
894 	}
895 
896 	xdr_free(xdr_ypresp_master, (char *)&yprm);
897 	return (r);
898 }
899 int
900 yp_maplist(indomain, outmaplist)
901 	char *indomain;
902 	struct ypmaplist **outmaplist;
903 {
904 	struct dom_binding *ysd;
905 	struct ypresp_maplist ypml;
906 	struct timeval tv;
907 	int r;
908 
909 	/* Sanity check */
910 
911 	if (indomain == NULL || !strlen(indomain))
912 		return YPERR_BADARGS;
913 
914 again:
915 	if( _yp_dobind(indomain, &ysd) != 0)
916 		return YPERR_DOMAIN;
917 
918 	tv.tv_sec = _yplib_timeout;
919 	tv.tv_usec = 0;
920 
921 	bzero((char *)&ypml, sizeof ypml);
922 
923 	r = clnt_call(ysd->dom_client, YPPROC_MAPLIST,
924 		xdr_domainname,(char *)&indomain,xdr_ypresp_maplist,&ypml,tv);
925 	if (r != RPC_SUCCESS) {
926 		clnt_perror(ysd->dom_client, "yp_maplist: clnt_call");
927 		_yp_unbind(ysd);
928 		goto again;
929 	}
930 	if( !(r=ypprot_err(ypml.stat)) ) {
931 		*outmaplist = ypml.maps;
932 	}
933 
934 	/* NO: xdr_free(xdr_ypresp_maplist, &ypml);*/
935 	return (r);
936 }
937 
938 char *
939 yperr_string(incode)
940 	int incode;
941 {
942 	static char err[80];
943 
944 	switch(incode) {
945 	case 0:
946 		return "Success";
947 	case YPERR_BADARGS:
948 		return "Request arguments bad";
949 	case YPERR_RPC:
950 		return "RPC failure";
951 	case YPERR_DOMAIN:
952 		return "Can't bind to server which serves this domain";
953 	case YPERR_MAP:
954 		return "No such map in server's domain";
955 	case YPERR_KEY:
956 		return "No such key in map";
957 	case YPERR_YPERR:
958 		return "YP server error";
959 	case YPERR_RESRC:
960 		return "Local resource allocation failure";
961 	case YPERR_NOMORE:
962 		return "No more records in map database";
963 	case YPERR_PMAP:
964 		return "Can't communicate with portmapper";
965 	case YPERR_YPBIND:
966 		return "Can't communicate with ypbind";
967 	case YPERR_YPSERV:
968 		return "Can't communicate with ypserv";
969 	case YPERR_NODOM:
970 		return "Local domain name not set";
971 	case YPERR_BADDB:
972 		return "Server data base is bad";
973 	case YPERR_VERS:
974 		return "YP server version mismatch - server can't supply service.";
975 	case YPERR_ACCESS:
976 		return "Access violation";
977 	case YPERR_BUSY:
978 		return "Database is busy";
979 	}
980 	sprintf(err, "YP unknown error %d\n", incode);
981 	return err;
982 }
983 
984 int
985 ypprot_err(incode)
986 	unsigned int incode;
987 {
988 	switch(incode) {
989 	case YP_TRUE:
990 		return 0;
991 	case YP_FALSE:
992 		return YPERR_YPBIND;
993 	case YP_NOMORE:
994 		return YPERR_NOMORE;
995 	case YP_NOMAP:
996 		return YPERR_MAP;
997 	case YP_NODOM:
998 		return YPERR_DOMAIN;
999 	case YP_NOKEY:
1000 		return YPERR_KEY;
1001 	case YP_BADOP:
1002 		return YPERR_YPERR;
1003 	case YP_BADDB:
1004 		return YPERR_BADDB;
1005 	case YP_YPERR:
1006 		return YPERR_YPERR;
1007 	case YP_BADARGS:
1008 		return YPERR_BADARGS;
1009 	case YP_VERS:
1010 		return YPERR_VERS;
1011 	}
1012 	return YPERR_YPERR;
1013 }
1014 
1015 int
1016 _yp_check(dom)
1017 	char **dom;
1018 {
1019 	char *unused;
1020 
1021 	if( _yp_domain[0]=='\0' )
1022 		if( yp_get_default_domain(&unused) )
1023 			return 0;
1024 
1025 	if(dom)
1026 		*dom = _yp_domain;
1027 
1028 	if( yp_bind(_yp_domain)==0 ) {
1029 		yp_unbind(_yp_domain);
1030 		return 1;
1031 	}
1032 	return 0;
1033 }
1034