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