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