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