1/*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
7 * Reserved.  This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License").  You may not use this file
10 * except in compliance with the License.  Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT.  Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24/*
25 * Copyright (c) 1992, 1993 Theo de Raadt <deraadt@theos.com>
26 * All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 *    notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 *    notice, this list of conditions and the following disclaimer in the
35 *    documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 *    must display the following acknowledgement:
38 *	This product includes software developed by Theo de Raadt.
39 * 4. The name of the author may not be used to endorse or promote products
40 *    derived from this software without specific prior written permission.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
43 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
44 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
46 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 */
54
55#if defined(LIBC_SCCS) && !defined(lint)
56static char *rcsid = "$OpenBSD: ypmatch_cache.c,v 1.6 1996/12/03 08:20:06 deraadt Exp $";
57#endif /* LIBC_SCCS and not lint */
58
59#include <sys/param.h>
60#include <sys/types.h>
61#include <sys/socket.h>
62#include <sys/file.h>
63#include <sys/uio.h>
64#include <errno.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <unistd.h>
69#include <rpc/rpc.h>
70#include <rpc/xdr.h>
71#include <rpcsvc/yp.h>
72#include <rpcsvc/ypclnt.h>
73/* #define YPMATCHCACHE */
74#include "ypinternal.h"
75
76int _yplib_cache = 5;
77
78#ifdef YPMATCHCACHE
79static bool_t
80ypmatch_add(map, key, keylen, val, vallen)
81	const char     *map;
82	const char     *key;
83	u_int           keylen;
84	char           *val;
85	u_int           vallen;
86{
87	struct ypmatch_ent *ep;
88	time_t t;
89
90	(void)time(&t);
91
92	for (ep = ypmc; ep; ep = ep->next)
93		if (ep->expire_t < t)
94			break;
95	if (ep == NULL) {
96		if ((ep = malloc(sizeof *ep)) == NULL)
97			return 0;
98		(void)memset(ep, 0, sizeof *ep);
99		if (ypmc)
100			ep->next = ypmc;
101		ypmc = ep;
102	}
103
104	if (ep->key) {
105		free(ep->key);
106		ep->key = NULL;
107	}
108	if (ep->val) {
109		free(ep->val);
110		ep->val = NULL;
111	}
112
113	if ((ep->key = malloc(keylen)) == NULL)
114		return 0;
115
116	if ((ep->val = malloc(vallen)) == NULL) {
117		free(ep->key);
118		ep->key = NULL;
119		return 0;
120	}
121
122	ep->keylen = keylen;
123	ep->vallen = vallen;
124
125	(void)memcpy(ep->key, key, ep->keylen);
126	(void)memcpy(ep->val, val, ep->vallen);
127
128	if (ep->map) {
129		if (strcmp(ep->map, map)) {
130			free(ep->map);
131			if ((ep->map = strdup(map)) == NULL)
132				return 0;
133		}
134	} else {
135		if ((ep->map = strdup(map)) == NULL)
136			return 0;
137	}
138
139	ep->expire_t = t + _yplib_cache;
140	return 1;
141}
142
143static bool_t
144ypmatch_find(map, key, keylen, val, vallen)
145	const char     *map;
146	const char     *key;
147	u_int           keylen;
148	char          **val;
149	u_int          *vallen;
150{
151	struct ypmatch_ent *ep;
152	time_t          t;
153
154	if (ypmc == NULL)
155		return 0;
156
157	(void) time(&t);
158
159	for (ep = ypmc; ep; ep = ep->next) {
160		if (ep->keylen != keylen)
161			continue;
162		if (strcmp(ep->map, map))
163			continue;
164		if (memcmp(ep->key, key, keylen))
165			continue;
166		if (t > ep->expire_t)
167			continue;
168
169		*val = ep->val;
170		*vallen = ep->vallen;
171		return 1;
172	}
173	return 0;
174}
175#endif
176
177int
178yp_match(indomain, inmap, inkey, inkeylen, outval, outvallen)
179	const char     *indomain;
180	const char     *inmap;
181	const char     *inkey;
182	int             inkeylen;
183	char          **outval;
184	int            *outvallen;
185{
186	struct dom_binding *ysd;
187	struct ypresp_val yprv;
188	struct timeval  tv;
189	struct ypreq_key yprk;
190	int tries = 0, r;
191	static int proto = YP_BIND_UDP;
192
193	if (indomain == NULL || *indomain == '\0' ||
194	    strlen(indomain) > YPMAXDOMAIN || inmap == NULL ||
195	    *inmap == '\0' || strlen(inmap) > YPMAXMAP ||
196	    inkey == NULL || inkeylen == 0)
197		return YPERR_BADARGS;
198
199	*outval = NULL;
200	*outvallen = 0;
201
202again:
203	if (_yp_dobind(indomain, &ysd) != 0)
204		return YPERR_DOMAIN;
205
206#ifdef YPMATCHCACHE
207	if (!strcmp(_yp_domain, indomain) && ypmatch_find(inmap, inkey,
208	    inkeylen, &yprv.val.valdat_val, &yprv.val.valdat_len)) {
209		*outvallen = yprv.val.valdat_len;
210		if ((*outval = malloc(*outvallen + 1)) == NULL) {
211			_yp_unbind(ysd);
212			return YPERR_YPERR;
213		}
214		(void)memcpy(*outval, yprv.val.valdat_val, *outvallen);
215		(*outval)[*outvallen] = '\0';
216		_yp_unbind(ysd);
217		return 0;
218	}
219#endif
220
221	tv.tv_sec = _yplib_timeout;
222	tv.tv_usec = 0;
223
224	yprk.domain = (char *)indomain;
225	yprk.map = (char *)inmap;
226	yprk.key.keydat_val = (char *) inkey;
227	yprk.key.keydat_len = inkeylen;
228
229	memset(&yprv, 0, sizeof yprv);
230
231	r = clnt_call(ysd->dom_client, YPPROC_MATCH, (xdrproc_t)xdr_ypreq_key, &yprk, (xdrproc_t)xdr_ypresp_val, &yprv, tv);
232	if (r != RPC_SUCCESS)
233	{
234		/* call failed - switch protocols and try again */
235		if (tries++) clnt_perror(ysd->dom_client, "yp_match: clnt_call");
236
237		if (proto == YP_BIND_UDP) proto = YP_BIND_TCP;
238		else proto = YP_BIND_UDP;
239		ysd->dom_vers = proto;
240
241		goto again;
242	}
243
244	if (!(r = ypprot_err(yprv.stat))) {
245		*outvallen = yprv.val.valdat_len;
246		if ((*outval = malloc(*outvallen + 1)) == NULL) {
247			r = YPERR_YPERR;
248			goto out;
249		}
250		(void)memcpy(*outval, yprv.val.valdat_val, *outvallen);
251		(*outval)[*outvallen] = '\0';
252#ifdef YPMATCHCACHE
253		if (strcmp(_yp_domain, indomain) == 0)
254			if (!ypmatch_add(inmap, inkey, inkeylen,
255			    *outval, *outvallen))
256				r = YPERR_RESRC;
257#endif
258	}
259out:
260	xdr_free((xdrproc_t)xdr_ypresp_val, (char *) &yprv);
261	_yp_unbind(ysd);
262	return r;
263}
264
265int
266yp_next(indomain, inmap, inkey, inkeylen, outkey, outkeylen, outval, outvallen)
267	const char     *indomain;
268	const char     *inmap;
269	const char     *inkey;
270	int             inkeylen;
271	char          **outkey;
272	int            *outkeylen;
273	char          **outval;
274	int            *outvallen;
275{
276	struct ypresp_key_val yprkv;
277	struct ypreq_key yprk;
278	struct dom_binding *ysd;
279	struct timeval  tv;
280	int tries = 0, r;
281	static int proto = YP_BIND_UDP;
282
283	if (indomain == NULL || *indomain == '\0' ||
284	    strlen(indomain) > YPMAXDOMAIN || inmap == NULL ||
285	    *inmap == '\0' || strlen(inmap) > YPMAXMAP)
286		return YPERR_BADARGS;
287
288	*outkey = *outval = NULL;
289	*outkeylen = *outvallen = 0;
290
291again:
292	if (_yp_dobind(indomain, &ysd) != 0)
293		return YPERR_DOMAIN;
294
295	tv.tv_sec = _yplib_timeout;
296	tv.tv_usec = 0;
297
298	yprk.domain = (char *)indomain;
299	yprk.map = (char *)inmap;
300	yprk.key.keydat_val = (char *)inkey;
301	yprk.key.keydat_len = inkeylen;
302	(void)memset(&yprkv, 0, sizeof yprkv);
303
304	r = clnt_call(ysd->dom_client, YPPROC_NEXT, (xdrproc_t)xdr_ypreq_key, &yprk, (xdrproc_t)xdr_ypresp_key_val, &yprkv, tv);
305	if (r != RPC_SUCCESS)
306	{
307		/* call failed - switch protocols and try again */
308		if (tries++) clnt_perror(ysd->dom_client, "yp_next: clnt_call");
309
310		if (proto == YP_BIND_UDP) proto = YP_BIND_TCP;
311		else proto = YP_BIND_UDP;
312		ysd->dom_vers = proto;
313
314		goto again;
315	}
316
317	if (!(r = ypprot_err(yprkv.stat))) {
318		*outkeylen = yprkv.key.keydat_len;
319		if ((*outkey = malloc(*outkeylen + 1)) == NULL)
320			r = YPERR_RESRC;
321		else {
322			(void)memcpy(*outkey, yprkv.key.keydat_val, *outkeylen);
323			(*outkey)[*outkeylen] = '\0';
324		}
325		*outvallen = yprkv.val.valdat_len;
326		if ((*outval = malloc(*outvallen + 1)) == NULL)
327			r = YPERR_RESRC;
328		else {
329			(void)memcpy(*outval, yprkv.val.valdat_val, *outvallen);
330			(*outval)[*outvallen] = '\0';
331		}
332	}
333	xdr_free((xdrproc_t)xdr_ypresp_key_val, (char *) &yprkv);
334	_yp_unbind(ysd);
335	return r;
336}
337