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