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