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