nscache.c revision 172730
1/*-
2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/lib/libc/net/nscache.c 172730 2007-10-17 23:20:49Z tmclaugh $");
30
31#include "namespace.h"
32#include <nsswitch.h>
33#include <stdlib.h>
34#include <string.h>
35#include "un-namespace.h"
36#include "nscachedcli.h"
37#include "nscache.h"
38
39#define NSS_CACHE_KEY_INITIAL_SIZE	(256)
40#define NSS_CACHE_KEY_SIZE_LIMIT	(NSS_CACHE_KEY_INITIAL_SIZE << 4)
41
42#define NSS_CACHE_BUFFER_INITIAL_SIZE	(1024)
43#define NSS_CACHE_BUFFER_SIZE_LIMIT	(NSS_CACHE_BUFFER_INITIAL_SIZE << 8)
44
45#define CACHED_SOCKET_PATH 		"/var/run/nscd"
46
47int
48__nss_cache_handler(void *retval, void *mdata, va_list ap)
49{
50	return (NS_UNAVAIL);
51}
52
53int
54__nss_common_cache_read(void *retval, void *mdata, va_list ap)
55{
56	struct cached_connection_params params;
57	cached_connection connection;
58
59	char *buffer;
60	size_t buffer_size, size;
61
62	nss_cache_info const *cache_info;
63	nss_cache_data *cache_data;
64	va_list ap_new;
65	int res;
66
67	cache_data = (nss_cache_data *)mdata;
68	cache_info = cache_data->info;
69
70	memset(&params, 0, sizeof(struct cached_connection_params));
71	params.socket_path = CACHED_SOCKET_PATH;
72
73	cache_data->key = (char *)malloc(NSS_CACHE_KEY_INITIAL_SIZE);
74	memset(cache_data->key, 0, NSS_CACHE_KEY_INITIAL_SIZE);
75	cache_data->key_size = NSS_CACHE_KEY_INITIAL_SIZE;
76	va_copy(ap_new, ap);
77
78	do {
79		size = cache_data->key_size;
80		res = cache_info->id_func(cache_data->key, &size, ap_new,
81		    cache_info->mdata);
82		va_end(ap_new);
83		if (res == NS_RETURN) {
84			if (cache_data->key_size > NSS_CACHE_KEY_SIZE_LIMIT)
85				break;
86
87			cache_data->key_size <<= 1;
88			cache_data->key = realloc(cache_data->key,
89			    cache_data->key_size);
90			memset(cache_data->key, 0, cache_data->key_size);
91			va_copy(ap_new, ap);
92		}
93	} while (res == NS_RETURN);
94
95	if (res != NS_SUCCESS) {
96		free(cache_data->key);
97		cache_data->key = NULL;
98		cache_data->key_size = 0;
99		return (res);
100	} else
101		cache_data->key_size = size;
102
103	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
104	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
105	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
106
107	do {
108		connection = __open_cached_connection(&params);
109		if (connection == NULL) {
110			res = -1;
111			break;
112		}
113		res = __cached_read(connection, cache_info->entry_name,
114		    cache_data->key, cache_data->key_size, buffer,
115		    &buffer_size);
116		__close_cached_connection(connection);
117		if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) {
118			buffer = (char *)realloc(buffer, buffer_size);
119			memset(buffer, 0, buffer_size);
120		}
121	} while (res == -2);
122
123	if (res == 0) {
124		if (buffer_size == 0) {
125			free(buffer);
126			free(cache_data->key);
127			cache_data->key = NULL;
128			cache_data->key_size = 0;
129			return (NS_RETURN);
130		}
131
132		va_copy(ap_new, ap);
133		res = cache_info->unmarshal_func(buffer, buffer_size, retval,
134		    ap_new, cache_info->mdata);
135		va_end(ap_new);
136
137		if (res != NS_SUCCESS) {
138			free(buffer);
139			free(cache_data->key);
140			cache_data->key = NULL;
141			cache_data->key_size = 0;
142			return (res);
143		} else
144			res = 0;
145	}
146
147	if (res == 0) {
148		free(cache_data->key);
149		cache_data->key = NULL;
150		cache_data->key_size = 0;
151	}
152
153	free(buffer);
154	return (res == 0 ? NS_SUCCESS : NS_NOTFOUND);
155}
156
157int
158__nss_common_cache_write(void *retval, void *mdata, va_list ap)
159{
160	struct cached_connection_params params;
161	cached_connection connection;
162
163	char *buffer;
164	size_t buffer_size;
165
166	nss_cache_info const *cache_info;
167	nss_cache_data *cache_data;
168	va_list ap_new;
169	int res;
170
171	cache_data = (nss_cache_data *)mdata;
172	cache_info = cache_data->info;
173
174	if (cache_data->key == NULL)
175		return (NS_UNAVAIL);
176
177	memset(&params, 0, sizeof(struct cached_connection_params));
178	params.socket_path = CACHED_SOCKET_PATH;
179
180	connection = __open_cached_connection(&params);
181	if (connection == NULL) {
182		free(cache_data->key);
183		return (NS_UNAVAIL);
184	}
185
186	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
187	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
188	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
189
190	do {
191		size_t size;
192
193		size = buffer_size;
194		va_copy(ap_new, ap);
195		res = cache_info->marshal_func(buffer, &size, retval, ap_new,
196		    cache_info->mdata);
197		va_end(ap_new);
198
199		if (res == NS_RETURN) {
200			if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT)
201				break;
202
203			buffer_size <<= 1;
204			buffer = (char *)realloc(buffer, buffer_size);
205			memset(buffer, 0, buffer_size);
206		}
207	} while (res == NS_RETURN);
208
209	if (res != NS_SUCCESS) {
210		__close_cached_connection(connection);
211		free(cache_data->key);
212		free(buffer);
213		return (res);
214	}
215
216	res = __cached_write(connection, cache_info->entry_name,
217	    cache_data->key, cache_data->key_size, buffer, buffer_size);
218	__close_cached_connection(connection);
219
220	free(cache_data->key);
221	free(buffer);
222
223	return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
224}
225
226int
227__nss_common_cache_write_negative(void *mdata)
228{
229	struct cached_connection_params params;
230	cached_connection connection;
231	int res;
232
233	nss_cache_info const *cache_info;
234	nss_cache_data *cache_data;
235
236	cache_data = (nss_cache_data *)mdata;
237	cache_info = cache_data->info;
238
239	if (cache_data->key == NULL)
240		return (NS_UNAVAIL);
241
242	memset(&params, 0, sizeof(struct cached_connection_params));
243	params.socket_path = CACHED_SOCKET_PATH;
244
245	connection = __open_cached_connection(&params);
246	if (connection == NULL) {
247		free(cache_data->key);
248		return (NS_UNAVAIL);
249	}
250
251	res = __cached_write(connection, cache_info->entry_name,
252	    cache_data->key, cache_data->key_size, NULL, 0);
253	__close_cached_connection(connection);
254
255	free(cache_data->key);
256	return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
257}
258
259int
260__nss_mp_cache_read(void *retval, void *mdata, va_list ap)
261{
262	struct cached_connection_params params;
263	cached_mp_read_session rs;
264
265	char *buffer;
266	size_t buffer_size;
267
268	nss_cache_info const *cache_info;
269	nss_cache_data *cache_data;
270	va_list ap_new;
271	int res;
272
273	cache_data = (nss_cache_data *)mdata;
274	cache_info = cache_data->info;
275
276	if (cache_info->get_mp_ws_func() != INVALID_CACHED_MP_WRITE_SESSION)
277		return (NS_UNAVAIL);
278
279	rs = cache_info->get_mp_rs_func();
280	if (rs == INVALID_CACHED_MP_READ_SESSION) {
281		memset(&params, 0, sizeof(struct cached_connection_params));
282		params.socket_path = CACHED_SOCKET_PATH;
283
284		rs = __open_cached_mp_read_session(&params,
285		    cache_info->entry_name);
286		if (rs == INVALID_CACHED_MP_READ_SESSION)
287			return (NS_UNAVAIL);
288
289		cache_info->set_mp_rs_func(rs);
290	}
291
292	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
293	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
294	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
295
296	do {
297		res = __cached_mp_read(rs, buffer, &buffer_size);
298		if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) {
299			buffer = (char *)realloc(buffer, buffer_size);
300			memset(buffer, 0, buffer_size);
301		}
302	} while (res == -2);
303
304	if (res == 0) {
305		va_copy(ap_new, ap);
306		res = cache_info->unmarshal_func(buffer, buffer_size, retval,
307		    ap_new, cache_info->mdata);
308		va_end(ap_new);
309
310		if (res != NS_SUCCESS) {
311			free(buffer);
312			return (res);
313		} else
314			res = 0;
315	} else {
316		free(buffer);
317		__close_cached_mp_read_session(rs);
318		rs = INVALID_CACHED_MP_READ_SESSION;
319		cache_info->set_mp_rs_func(rs);
320		return (res == -1 ? NS_RETURN : NS_UNAVAIL);
321	}
322
323	free(buffer);
324	return (res == 0 ? NS_SUCCESS : NS_NOTFOUND);
325}
326
327int
328__nss_mp_cache_write(void *retval, void *mdata, va_list ap)
329{
330	struct cached_connection_params params;
331	cached_mp_write_session ws;
332
333	char *buffer;
334	size_t buffer_size;
335
336	nss_cache_info const *cache_info;
337	nss_cache_data *cache_data;
338	va_list ap_new;
339	int res;
340
341	cache_data = (nss_cache_data *)mdata;
342	cache_info = cache_data->info;
343
344	ws = cache_info->get_mp_ws_func();
345	if (ws == INVALID_CACHED_MP_WRITE_SESSION) {
346		memset(&params, 0, sizeof(struct cached_connection_params));
347		params.socket_path = CACHED_SOCKET_PATH;
348
349		ws = __open_cached_mp_write_session(&params,
350		    cache_info->entry_name);
351		if (ws == INVALID_CACHED_MP_WRITE_SESSION)
352			return (NS_UNAVAIL);
353
354		cache_info->set_mp_ws_func(ws);
355	}
356
357	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
358	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
359	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
360
361	do {
362		size_t size;
363
364		size = buffer_size;
365		va_copy(ap_new, ap);
366		res = cache_info->marshal_func(buffer, &size, retval, ap_new,
367		    cache_info->mdata);
368		va_end(ap_new);
369
370		if (res == NS_RETURN) {
371			if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT)
372				break;
373
374			buffer_size <<= 1;
375			buffer = (char *)realloc(buffer, buffer_size);
376			memset(buffer, 0, buffer_size);
377		}
378	} while (res == NS_RETURN);
379
380	if (res != NS_SUCCESS) {
381		free(buffer);
382		return (res);
383	}
384
385	res = __cached_mp_write(ws, buffer, buffer_size);
386
387	free(buffer);
388	return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
389}
390
391int
392__nss_mp_cache_write_submit(void *retval, void *mdata, va_list ap)
393{
394	cached_mp_write_session ws;
395
396	nss_cache_info const *cache_info;
397	nss_cache_data *cache_data;
398
399	cache_data = (nss_cache_data *)mdata;
400	cache_info = cache_data->info;
401
402	ws = cache_info->get_mp_ws_func();
403	if (ws != INVALID_CACHED_MP_WRITE_SESSION) {
404		__close_cached_mp_write_session(ws);
405		ws = INVALID_CACHED_MP_WRITE_SESSION;
406		cache_info->set_mp_ws_func(ws);
407	}
408	return (NS_UNAVAIL);
409}
410
411int
412__nss_mp_cache_end(void *retval, void *mdata, va_list ap)
413{
414	cached_mp_write_session ws;
415	cached_mp_read_session rs;
416
417	nss_cache_info const *cache_info;
418	nss_cache_data *cache_data;
419
420	cache_data = (nss_cache_data *)mdata;
421	cache_info = cache_data->info;
422
423	ws = cache_info->get_mp_ws_func();
424	if (ws != INVALID_CACHED_MP_WRITE_SESSION) {
425		__abandon_cached_mp_write_session(ws);
426		ws = INVALID_CACHED_MP_WRITE_SESSION;
427		cache_info->set_mp_ws_func(ws);
428	}
429
430	rs = cache_info->get_mp_rs_func();
431	if (rs != INVALID_CACHED_MP_READ_SESSION) {
432		__close_cached_mp_read_session(rs);
433		rs = INVALID_CACHED_MP_READ_SESSION;
434		cache_info->set_mp_rs_func(rs);
435	}
436
437	return (NS_UNAVAIL);
438}
439