xref: /freebsd/lib/libc/yp/yplib.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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 			ysd->dom_server_addr.sin_addr.s_addr =
406 			    *(u_long *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr;
407 			ysd->dom_server_addr.sin_port =
408 			    *(u_short *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port;
409 
410 			ysd->dom_server_port = ysd->dom_server_addr.sin_port;
411 			_close(fd);
412 			goto gotit;
413 		} else {
414 			/* no lock on binding file, YP is dead. */
415 			/* Try to bring it back to life. */
416 			_close(fd);
417 			goto skipit;
418 		}
419 	}
420 skipit:
421 #endif
422 	if(ysd->dom_vers==-1 || ysd->dom_vers==0) {
423 		/*
424 		 * We're trying to make a new binding: zorch the
425 		 * existing handle now (if any).
426 		 */
427 		if(ysd->dom_client != NULL) {
428 			clnt_destroy(ysd->dom_client);
429 			ysd->dom_client = NULL;
430 			ysd->dom_socket = -1;
431 		}
432 		bzero((char *)&clnt_sin, sizeof clnt_sin);
433 		clnt_sin.sin_family = AF_INET;
434 		clnt_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
435 
436 		clnt_sock = RPC_ANYSOCK;
437 		client = clnttcp_create(&clnt_sin, YPBINDPROG, YPBINDVERS, &clnt_sock,
438 			0, 0);
439 		if(client==NULL) {
440 			/*
441 			 * These conditions indicate ypbind just isn't
442 			 * alive -- we probably don't want to shoot our
443 			 * mouth off in this case; instead generate error
444 			 * messages only for really exotic problems.
445 			 */
446 			if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED &&
447 			   (rpc_createerr.cf_stat != RPC_SYSTEMERROR &&
448 			   rpc_createerr.cf_error.re_errno == ECONNREFUSED))
449 				clnt_pcreateerror("clnttcp_create");
450 			if(new)
451 				free(ysd);
452 			return (YPERR_YPBIND);
453 		}
454 
455 		/*
456 		 * Check the port number -- should be < IPPORT_RESERVED.
457 		 * If not, it's possible someone has registered a bogus
458 		 * ypbind with the portmapper and is trying to trick us.
459 		 */
460 		if (ntohs(clnt_sin.sin_port) >= IPPORT_RESERVED) {
461 			if (client != NULL)
462 				clnt_destroy(client);
463 			if (new)
464 				free(ysd);
465 			return(YPERR_YPBIND);
466 		}
467 		tv.tv_sec = _yplib_timeout/2;
468 		tv.tv_usec = 0;
469 		r = clnt_call(client, YPBINDPROC_DOMAIN,
470 			xdr_domainname, (char *)&dom, xdr_ypbind_resp, &ypbr, tv);
471 		if(r != RPC_SUCCESS) {
472 			clnt_destroy(client);
473 			ysd->dom_vers = -1;
474 			if (r == RPC_PROGUNAVAIL || r == RPC_PROCUNAVAIL) {
475 				if (new)
476 					free(ysd);
477 				return(YPERR_YPBIND);
478 			}
479 			fprintf(stderr,
480 			"YP: server for domain %s not responding, retrying\n", dom);
481 			goto again;
482 		} else {
483 			if (ypbr.ypbind_status != YPBIND_SUCC_VAL) {
484 				struct timespec time_to_sleep, time_remaining;
485 
486 				clnt_destroy(client);
487 				ysd->dom_vers = -1;
488 
489 				time_to_sleep.tv_sec = _yplib_timeout/2;
490 				time_to_sleep.tv_nsec = 0;
491 				_nanosleep(&time_to_sleep,
492 				    &time_remaining);
493 				goto again;
494 			}
495 		}
496 		clnt_destroy(client);
497 
498 		bzero((char *)&ysd->dom_server_addr, sizeof ysd->dom_server_addr);
499 		ysd->dom_server_addr.sin_family = AF_INET;
500 		ysd->dom_server_addr.sin_port =
501 			*(u_short *)&ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port;
502 		ysd->dom_server_addr.sin_addr.s_addr =
503 			*(u_long *)&ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr;
504 
505 		/*
506 		 * We could do a reserved port check here too, but this
507 		 * could pose compatibility problems. The local ypbind is
508 		 * supposed to decide whether or not to trust yp servers
509 		 * on insecure ports. For now, we trust its judgement.
510 		 */
511 		ysd->dom_server_port =
512 			*(u_short *)&ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port;
513 gotit:
514 		ysd->dom_vers = YPVERS;
515 		strlcpy(ysd->dom_domain, dom, sizeof(ysd->dom_domain));
516 	}
517 
518 	/* Don't rebuild the connection to the server unless we have to. */
519 	if (ysd->dom_client == NULL) {
520 		tv.tv_sec = _yplib_timeout/2;
521 		tv.tv_usec = 0;
522 		ysd->dom_socket = RPC_ANYSOCK;
523 		ysd->dom_client = clntudp_bufcreate(&ysd->dom_server_addr,
524 			YPPROG, YPVERS, tv, &ysd->dom_socket, 1280, 2304);
525 		if(ysd->dom_client==NULL) {
526 			clnt_pcreateerror("clntudp_create");
527 			ysd->dom_vers = -1;
528 			goto again;
529 		}
530 		if(_fcntl(ysd->dom_socket, F_SETFD, 1) == -1)
531 			perror("fcntl: F_SETFD");
532 		/*
533 		 * We want a port number associated with this socket
534 		 * so that we can check its authenticity later.
535 		 */
536 		checklen = sizeof(struct sockaddr_in);
537 		bzero((char *)&check, checklen);
538 		_bind(ysd->dom_socket, (struct sockaddr *)&check, checklen);
539 		check.sin_family = AF_INET;
540 		if (!_getsockname(ysd->dom_socket,
541 		    (struct sockaddr *)&check, &checklen)) {
542 			ysd->dom_local_port = check.sin_port;
543 		} else {
544 			clnt_destroy(ysd->dom_client);
545 			if (new)
546 				free(ysd);
547 			return(YPERR_YPBIND);
548 		}
549 	}
550 
551 	if(new) {
552 		ysd->dom_pnext = _ypbindlist;
553 		_ypbindlist = ysd;
554 	}
555 
556 	if(ypdb!=NULL)
557 		*ypdb = ysd;
558 	return 0;
559 }
560 
561 static void
562 _yp_unbind(ypb)
563 	struct dom_binding *ypb;
564 {
565 	struct sockaddr_in check;
566 	int checklen = sizeof(struct sockaddr_in);
567 
568 	if (ypb->dom_client != NULL) {
569 		/* Check the socket -- may have been hosed by the caller. */
570 		if (_getsockname(ypb->dom_socket, (struct sockaddr *)&check,
571 	    	&checklen) == -1 || check.sin_family != AF_INET ||
572 	    	check.sin_port != ypb->dom_local_port) {
573 			int save, sock;
574 
575 			sock = ypb->dom_socket;
576 			save = _dup(ypb->dom_socket);
577 			clnt_destroy(ypb->dom_client);
578 			sock = _dup2(save, sock);
579 			_close(save);
580 		} else
581 			clnt_destroy(ypb->dom_client);
582 	}
583 
584 	ypb->dom_client = NULL;
585 	ypb->dom_socket = -1;
586 	ypb->dom_vers = -1;
587 #ifdef YPMATCHCACHE
588 	ypmatch_cache_flush(ypb);
589 #endif
590 }
591 
592 int
593 yp_bind(dom)
594 	char *dom;
595 {
596 	return _yp_dobind(dom, NULL);
597 }
598 
599 void
600 yp_unbind(dom)
601 	char *dom;
602 {
603 	struct dom_binding *ypb, *ypbp;
604 
605 	ypbp = NULL;
606 	for(ypb=_ypbindlist; ypb; ypb=ypb->dom_pnext) {
607 		if( strcmp(dom, ypb->dom_domain) == 0) {
608 			_yp_unbind(ypb);
609 			if(ypbp)
610 				ypbp->dom_pnext = ypb->dom_pnext;
611 			else
612 				_ypbindlist = ypb->dom_pnext;
613 			free(ypb);
614 			return;
615 		}
616 		ypbp = ypb;
617 	}
618 	return;
619 }
620 
621 int
622 yp_match(indomain, inmap, inkey, inkeylen, outval, outvallen)
623 	char *indomain;
624 	char *inmap;
625 	const char *inkey;
626 	int inkeylen;
627 	char **outval;
628 	int *outvallen;
629 {
630 	struct dom_binding *ysd;
631 	struct ypresp_val yprv;
632 	struct timeval tv;
633 	struct ypreq_key yprk;
634 	int r;
635 
636 	*outval = NULL;
637 	*outvallen = 0;
638 
639 	/* Sanity check */
640 
641 	if (inkey == NULL || !strlen(inkey) || inkeylen <= 0 ||
642 	    inmap == NULL || !strlen(inmap) ||
643 	    indomain == NULL || !strlen(indomain))
644 		return YPERR_BADARGS;
645 
646 	if (_yp_dobind(indomain, &ysd) != 0)
647 		return(YPERR_DOMAIN);
648 
649 	yprk.domain = indomain;
650 	yprk.map = inmap;
651 	yprk.key.keydat_val = (char *)inkey;
652 	yprk.key.keydat_len = inkeylen;
653 
654 #ifdef YPMATCHCACHE
655 	if (ypmatch_cache_lookup(ysd, yprk.map, &yprk.key, &yprv.val) == TRUE) {
656 /*
657 	if( !strcmp(_yp_domain, indomain) && ypmatch_find(inmap, inkey,
658 	    inkeylen, &yprv.val.valdat_val, &yprv.val.valdat_len)) {
659 */
660 		*outvallen = yprv.val.valdat_len;
661 		*outval = (char *)malloc(*outvallen+1);
662 		bcopy(yprv.val.valdat_val, *outval, *outvallen);
663 		(*outval)[*outvallen] = '\0';
664 		return 0;
665 	}
666 #endif
667 
668 again:
669 	if( _yp_dobind(indomain, &ysd) != 0)
670 		return YPERR_DOMAIN;
671 
672 	tv.tv_sec = _yplib_timeout;
673 	tv.tv_usec = 0;
674 
675 	bzero((char *)&yprv, sizeof yprv);
676 
677 	r = clnt_call(ysd->dom_client, YPPROC_MATCH,
678 		xdr_ypreq_key, &yprk, xdr_ypresp_val, &yprv, tv);
679 	if(r != RPC_SUCCESS) {
680 		clnt_perror(ysd->dom_client, "yp_match: clnt_call");
681 		_yp_unbind(ysd);
682 		goto again;
683 	}
684 
685 	if( !(r=ypprot_err(yprv.stat)) ) {
686 		*outvallen = yprv.val.valdat_len;
687 		*outval = (char *)malloc(*outvallen+1);
688 		bcopy(yprv.val.valdat_val, *outval, *outvallen);
689 		(*outval)[*outvallen] = '\0';
690 #ifdef YPMATCHCACHE
691 		ypmatch_cache_insert(ysd, yprk.map, &yprk.key, &yprv.val);
692 #endif
693 	}
694 
695 	xdr_free(xdr_ypresp_val, (char *)&yprv);
696 	return r;
697 }
698 
699 int
700 yp_get_default_domain(domp)
701 char **domp;
702 {
703 	*domp = NULL;
704 	if(_yp_domain[0] == '\0')
705 		if( getdomainname(_yp_domain, sizeof _yp_domain))
706 			return YPERR_NODOM;
707 	*domp = _yp_domain;
708 	return 0;
709 }
710 
711 int
712 yp_first(indomain, inmap, outkey, outkeylen, outval, outvallen)
713 	char *indomain;
714 	char *inmap;
715 	char **outkey;
716 	int *outkeylen;
717 	char **outval;
718 	int *outvallen;
719 {
720 	struct ypresp_key_val yprkv;
721 	struct ypreq_nokey yprnk;
722 	struct dom_binding *ysd;
723 	struct timeval tv;
724 	int r;
725 
726 	/* Sanity check */
727 
728 	if (indomain == NULL || !strlen(indomain) ||
729 	    inmap == NULL || !strlen(inmap))
730 		return YPERR_BADARGS;
731 
732 	*outkey = *outval = NULL;
733 	*outkeylen = *outvallen = 0;
734 
735 again:
736 	if( _yp_dobind(indomain, &ysd) != 0)
737 		return YPERR_DOMAIN;
738 
739 	tv.tv_sec = _yplib_timeout;
740 	tv.tv_usec = 0;
741 
742 	yprnk.domain = indomain;
743 	yprnk.map = inmap;
744 	bzero((char *)&yprkv, sizeof yprkv);
745 
746 	r = clnt_call(ysd->dom_client, YPPROC_FIRST,
747 		xdr_ypreq_nokey, &yprnk, xdr_ypresp_key_val, &yprkv, tv);
748 	if(r != RPC_SUCCESS) {
749 		clnt_perror(ysd->dom_client, "yp_first: clnt_call");
750 		_yp_unbind(ysd);
751 		goto again;
752 	}
753 	if( !(r=ypprot_err(yprkv.stat)) ) {
754 		*outkeylen = yprkv.key.keydat_len;
755 		*outkey = (char *)malloc(*outkeylen+1);
756 		bcopy(yprkv.key.keydat_val, *outkey, *outkeylen);
757 		(*outkey)[*outkeylen] = '\0';
758 		*outvallen = yprkv.val.valdat_len;
759 		*outval = (char *)malloc(*outvallen+1);
760 		bcopy(yprkv.val.valdat_val, *outval, *outvallen);
761 		(*outval)[*outvallen] = '\0';
762 	}
763 
764 	xdr_free(xdr_ypresp_key_val, (char *)&yprkv);
765 	return r;
766 }
767 
768 int
769 yp_next(indomain, inmap, inkey, inkeylen, outkey, outkeylen, outval, outvallen)
770 	char *indomain;
771 	char *inmap;
772 	char *inkey;
773 	int inkeylen;
774 	char **outkey;
775 	int *outkeylen;
776 	char **outval;
777 	int *outvallen;
778 {
779 	struct ypresp_key_val yprkv;
780 	struct ypreq_key yprk;
781 	struct dom_binding *ysd;
782 	struct timeval tv;
783 	int r;
784 
785 	/* Sanity check */
786 
787 	if (inkey == NULL || !strlen(inkey) || inkeylen <= 0 ||
788 	    inmap == NULL || !strlen(inmap) ||
789 	    indomain == NULL || !strlen(indomain))
790 		return YPERR_BADARGS;
791 
792 	*outkey = *outval = NULL;
793 	*outkeylen = *outvallen = 0;
794 
795 again:
796 	if( _yp_dobind(indomain, &ysd) != 0)
797 		return YPERR_DOMAIN;
798 
799 	tv.tv_sec = _yplib_timeout;
800 	tv.tv_usec = 0;
801 
802 	yprk.domain = indomain;
803 	yprk.map = inmap;
804 	yprk.key.keydat_val = inkey;
805 	yprk.key.keydat_len = inkeylen;
806 	bzero((char *)&yprkv, sizeof yprkv);
807 
808 	r = clnt_call(ysd->dom_client, YPPROC_NEXT,
809 		xdr_ypreq_key, &yprk, xdr_ypresp_key_val, &yprkv, tv);
810 	if(r != RPC_SUCCESS) {
811 		clnt_perror(ysd->dom_client, "yp_next: clnt_call");
812 		_yp_unbind(ysd);
813 		goto again;
814 	}
815 	if( !(r=ypprot_err(yprkv.stat)) ) {
816 		*outkeylen = yprkv.key.keydat_len;
817 		*outkey = (char *)malloc(*outkeylen+1);
818 		bcopy(yprkv.key.keydat_val, *outkey, *outkeylen);
819 		(*outkey)[*outkeylen] = '\0';
820 		*outvallen = yprkv.val.valdat_len;
821 		*outval = (char *)malloc(*outvallen+1);
822 		bcopy(yprkv.val.valdat_val, *outval, *outvallen);
823 		(*outval)[*outvallen] = '\0';
824 	}
825 
826 	xdr_free(xdr_ypresp_key_val, (char *)&yprkv);
827 	return r;
828 }
829 
830 int
831 yp_all(indomain, inmap, incallback)
832 	char *indomain;
833 	char *inmap;
834 	struct ypall_callback *incallback;
835 {
836 	struct ypreq_nokey yprnk;
837 	struct dom_binding *ysd;
838 	struct timeval tv;
839 	struct sockaddr_in clnt_sin;
840 	CLIENT *clnt;
841 	u_long status, savstat;
842 	int clnt_sock;
843 
844 	/* Sanity check */
845 
846 	if (indomain == NULL || !strlen(indomain) ||
847 	    inmap == NULL || !strlen(inmap))
848 		return YPERR_BADARGS;
849 
850 again:
851 
852 	if( _yp_dobind(indomain, &ysd) != 0)
853 		return YPERR_DOMAIN;
854 
855 	tv.tv_sec = _yplib_timeout;
856 	tv.tv_usec = 0;
857 
858 	/* YPPROC_ALL manufactures its own channel to ypserv using TCP */
859 
860 	clnt_sock = RPC_ANYSOCK;
861 	clnt_sin = ysd->dom_server_addr;
862 	clnt_sin.sin_port = 0;
863 	clnt = clnttcp_create(&clnt_sin, YPPROG, YPVERS, &clnt_sock, 0, 0);
864 	if(clnt==NULL) {
865 		printf("clnttcp_create failed\n");
866 		return YPERR_PMAP;
867 	}
868 
869 	yprnk.domain = indomain;
870 	yprnk.map = inmap;
871 	ypresp_allfn = incallback->foreach;
872 	ypresp_data = (void *)incallback->data;
873 
874 	if (clnt_call(clnt, YPPROC_ALL,
875 		xdr_ypreq_nokey, &yprnk,
876 		xdr_ypresp_all_seq, &status, tv) != RPC_SUCCESS) {
877 			clnt_perror(ysd->dom_client, "yp_all: clnt_call");
878 			clnt_destroy(clnt);
879 			_yp_unbind(ysd);
880 			goto again;
881 	}
882 
883 	clnt_destroy(clnt);
884 	savstat = status;
885 	xdr_free(xdr_ypresp_all_seq, (char *)&status);	/* not really needed... */
886 	if(savstat != YP_NOMORE)
887 		return ypprot_err(savstat);
888 	return 0;
889 }
890 
891 int
892 yp_order(indomain, inmap, outorder)
893 	char *indomain;
894 	char *inmap;
895 	int *outorder;
896 {
897  	struct dom_binding *ysd;
898 	struct ypresp_order ypro;
899 	struct ypreq_nokey yprnk;
900 	struct timeval tv;
901 	int r;
902 
903 	/* Sanity check */
904 
905 	if (indomain == NULL || !strlen(indomain) ||
906 	    inmap == NULL || !strlen(inmap))
907 		return YPERR_BADARGS;
908 
909 again:
910 	if( _yp_dobind(indomain, &ysd) != 0)
911 		return YPERR_DOMAIN;
912 
913 	tv.tv_sec = _yplib_timeout;
914 	tv.tv_usec = 0;
915 
916 	yprnk.domain = indomain;
917 	yprnk.map = inmap;
918 
919 	bzero((char *)(char *)&ypro, sizeof ypro);
920 
921 	r = clnt_call(ysd->dom_client, YPPROC_ORDER,
922 		xdr_ypreq_nokey, &yprnk, xdr_ypresp_order, &ypro, tv);
923 
924 	/*
925 	 * NIS+ in YP compat mode doesn't support the YPPROC_ORDER
926 	 * procedure.
927 	 */
928 	if (r == RPC_PROCUNAVAIL) {
929 		return(YPERR_YPERR);
930 	}
931 
932 	if(r != RPC_SUCCESS) {
933 		clnt_perror(ysd->dom_client, "yp_order: clnt_call");
934 		_yp_unbind(ysd);
935 		goto again;
936 	}
937 
938 	if( !(r=ypprot_err(ypro.stat)) ) {
939 		*outorder = ypro.ordernum;
940 	}
941 
942 	xdr_free(xdr_ypresp_order, (char *)&ypro);
943 	return (r);
944 }
945 
946 int
947 yp_master(indomain, inmap, outname)
948 	char *indomain;
949 	char *inmap;
950 	char **outname;
951 {
952 	struct dom_binding *ysd;
953 	struct ypresp_master yprm;
954 	struct ypreq_nokey yprnk;
955 	struct timeval tv;
956 	int r;
957 
958 	/* Sanity check */
959 
960 	if (indomain == NULL || !strlen(indomain) ||
961 	    inmap == NULL || !strlen(inmap))
962 		return YPERR_BADARGS;
963 again:
964 	if( _yp_dobind(indomain, &ysd) != 0)
965 		return YPERR_DOMAIN;
966 
967 	tv.tv_sec = _yplib_timeout;
968 	tv.tv_usec = 0;
969 
970 	yprnk.domain = indomain;
971 	yprnk.map = inmap;
972 
973 	bzero((char *)&yprm, sizeof yprm);
974 
975 	r = clnt_call(ysd->dom_client, YPPROC_MASTER,
976 		xdr_ypreq_nokey, &yprnk, xdr_ypresp_master, &yprm, tv);
977 	if(r != RPC_SUCCESS) {
978 		clnt_perror(ysd->dom_client, "yp_master: clnt_call");
979 		_yp_unbind(ysd);
980 		goto again;
981 	}
982 
983 	if( !(r=ypprot_err(yprm.stat)) ) {
984 		*outname = (char *)strdup(yprm.peer);
985 	}
986 
987 	xdr_free(xdr_ypresp_master, (char *)&yprm);
988 	return (r);
989 }
990 int
991 yp_maplist(indomain, outmaplist)
992 	char *indomain;
993 	struct ypmaplist **outmaplist;
994 {
995 	struct dom_binding *ysd;
996 	struct ypresp_maplist ypml;
997 	struct timeval tv;
998 	int r;
999 
1000 	/* Sanity check */
1001 
1002 	if (indomain == NULL || !strlen(indomain))
1003 		return YPERR_BADARGS;
1004 
1005 again:
1006 	if( _yp_dobind(indomain, &ysd) != 0)
1007 		return YPERR_DOMAIN;
1008 
1009 	tv.tv_sec = _yplib_timeout;
1010 	tv.tv_usec = 0;
1011 
1012 	bzero((char *)&ypml, sizeof ypml);
1013 
1014 	r = clnt_call(ysd->dom_client, YPPROC_MAPLIST,
1015 		xdr_domainname,(char *)&indomain,xdr_ypresp_maplist,&ypml,tv);
1016 	if (r != RPC_SUCCESS) {
1017 		clnt_perror(ysd->dom_client, "yp_maplist: clnt_call");
1018 		_yp_unbind(ysd);
1019 		goto again;
1020 	}
1021 	if( !(r=ypprot_err(ypml.stat)) ) {
1022 		*outmaplist = ypml.maps;
1023 	}
1024 
1025 	/* NO: xdr_free(xdr_ypresp_maplist, &ypml);*/
1026 	return (r);
1027 }
1028 
1029 char *
1030 yperr_string(incode)
1031 	int incode;
1032 {
1033 	static char err[80];
1034 
1035 	switch(incode) {
1036 	case 0:
1037 		return "Success";
1038 	case YPERR_BADARGS:
1039 		return "Request arguments bad";
1040 	case YPERR_RPC:
1041 		return "RPC failure";
1042 	case YPERR_DOMAIN:
1043 		return "Can't bind to server which serves this domain";
1044 	case YPERR_MAP:
1045 		return "No such map in server's domain";
1046 	case YPERR_KEY:
1047 		return "No such key in map";
1048 	case YPERR_YPERR:
1049 		return "YP server error";
1050 	case YPERR_RESRC:
1051 		return "Local resource allocation failure";
1052 	case YPERR_NOMORE:
1053 		return "No more records in map database";
1054 	case YPERR_PMAP:
1055 		return "Can't communicate with portmapper";
1056 	case YPERR_YPBIND:
1057 		return "Can't communicate with ypbind";
1058 	case YPERR_YPSERV:
1059 		return "Can't communicate with ypserv";
1060 	case YPERR_NODOM:
1061 		return "Local domain name not set";
1062 	case YPERR_BADDB:
1063 		return "Server data base is bad";
1064 	case YPERR_VERS:
1065 		return "YP server version mismatch - server can't supply service.";
1066 	case YPERR_ACCESS:
1067 		return "Access violation";
1068 	case YPERR_BUSY:
1069 		return "Database is busy";
1070 	}
1071 	sprintf(err, "YP unknown error %d\n", incode);
1072 	return err;
1073 }
1074 
1075 int
1076 ypprot_err(incode)
1077 	unsigned int incode;
1078 {
1079 	switch(incode) {
1080 	case YP_TRUE:
1081 		return 0;
1082 	case YP_FALSE:
1083 		return YPERR_YPBIND;
1084 	case YP_NOMORE:
1085 		return YPERR_NOMORE;
1086 	case YP_NOMAP:
1087 		return YPERR_MAP;
1088 	case YP_NODOM:
1089 		return YPERR_DOMAIN;
1090 	case YP_NOKEY:
1091 		return YPERR_KEY;
1092 	case YP_BADOP:
1093 		return YPERR_YPERR;
1094 	case YP_BADDB:
1095 		return YPERR_BADDB;
1096 	case YP_YPERR:
1097 		return YPERR_YPERR;
1098 	case YP_BADARGS:
1099 		return YPERR_BADARGS;
1100 	case YP_VERS:
1101 		return YPERR_VERS;
1102 	}
1103 	return YPERR_YPERR;
1104 }
1105 
1106 int
1107 _yp_check(dom)
1108 	char **dom;
1109 {
1110 	char *unused;
1111 
1112 	if( _yp_domain[0]=='\0' )
1113 		if( yp_get_default_domain(&unused) )
1114 			return 0;
1115 
1116 	if(dom)
1117 		*dom = _yp_domain;
1118 
1119 	if( yp_bind(_yp_domain)==0 ) {
1120 		yp_unbind(_yp_domain);
1121 		return 1;
1122 	}
1123 	return 0;
1124 }
1125