Libinfo-278.0.3.tar.gz
[apple/libinfo.git] / lookup.subproj / lu_service.c
1 /*
2 * Copyright (c) 1999-2002 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 * Services file lookup
26 * Copyright (C) 1989 by NeXT, Inc.
27 */
28 #include <stdlib.h>
29 #include <mach/mach.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <netdb.h>
33 #include <netinet/in.h>
34 #include <pthread.h>
35 #include "lu_utils.h"
36
37 #define SERVICE_CACHE_SIZE 10
38
39 static pthread_mutex_t _service_cache_lock = PTHREAD_MUTEX_INITIALIZER;
40 static void *_service_cache[SERVICE_CACHE_SIZE] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
41 static unsigned int _service_cache_index = 0;
42 static unsigned int _service_cache_init = 0;
43
44 static pthread_mutex_t _service_lock = PTHREAD_MUTEX_INITIALIZER;
45
46 #define S_GET_NAME 1
47 #define S_GET_PORT 2
48 #define S_GET_ENT 3
49
50 extern struct servent *_old_getservbyport();
51 extern struct servent *_old_getservbyname();
52 extern struct servent *_old_getservent();
53 extern void _old_setservent();
54 extern void _old_endservent();
55 extern void _old_setservfile();
56
57 #define ENTRY_SIZE sizeof(struct servent)
58 #define ENTRY_KEY _li_data_key_service
59
60 static struct servent *
61 copy_service(struct servent *in)
62 {
63 if (in == NULL) return NULL;
64
65 return LI_ils_create("s*4s", in->s_name, in->s_aliases, in->s_port, in->s_proto);
66 }
67
68 /*
69 * Extract the next service entry from a kvarray.
70 */
71 static void *
72 extract_service(kvarray_t *in)
73 {
74 struct servent tmp;
75 uint32_t d, k, kcount;
76 char *empty[1];
77
78 if (in == NULL) return NULL;
79
80 d = in->curr;
81 in->curr++;
82
83 if (d >= in->count) return NULL;
84
85 empty[0] = NULL;
86 memset(&tmp, 0, ENTRY_SIZE);
87
88 kcount = in->dict[d].kcount;
89
90 for (k = 0; k < kcount; k++)
91 {
92 if (!strcmp(in->dict[d].key[k], "s_name"))
93 {
94 if (tmp.s_name != NULL) continue;
95 if (in->dict[d].vcount[k] == 0) continue;
96
97 tmp.s_name = (char *)in->dict[d].val[k][0];
98 }
99 else if (!strcmp(in->dict[d].key[k], "s_aliases"))
100 {
101 if (tmp.s_aliases != NULL) continue;
102 if (in->dict[d].vcount[k] == 0) continue;
103
104 tmp.s_aliases = (char **)in->dict[d].val[k];
105 }
106 else if (!strcmp(in->dict[d].key[k], "s_port"))
107 {
108 if (in->dict[d].vcount[k] == 0) continue;
109 tmp.s_port = htons(atoi(in->dict[d].val[k][0]));
110 }
111 else if (!strcmp(in->dict[d].key[k], "s_proto"))
112 {
113 if (tmp.s_proto != NULL) continue;
114 if (in->dict[d].vcount[k] == 0) continue;
115
116 tmp.s_proto = (char *)in->dict[d].val[k][0];
117 }
118 }
119
120 if (tmp.s_name == NULL) tmp.s_name = "";
121 if (tmp.s_proto == NULL) tmp.s_proto = "";
122 if (tmp.s_aliases == NULL) tmp.s_aliases = empty;
123
124 return copy_service(&tmp);
125 }
126
127 static void
128 cache_service(struct servent *s)
129 {
130 struct servent *scache;
131
132 if (s == NULL) return;
133
134 pthread_mutex_lock(&_service_cache_lock);
135
136 scache = copy_service(s);
137
138 if (_service_cache[_service_cache_index] != NULL) LI_ils_free(_service_cache[_service_cache_index], ENTRY_SIZE);
139 _service_cache[_service_cache_index] = scache;
140 _service_cache_index = (_service_cache_index + 1) % SERVICE_CACHE_SIZE;
141
142 _service_cache_init = 1;
143
144 pthread_mutex_unlock(&_service_cache_lock);
145 }
146
147 static int
148 service_cache_check()
149 {
150 uint32_t i, status;
151
152 /* don't consult cache if it has not been initialized */
153 if (_service_cache_init == 0) return 1;
154
155 status = LI_L1_cache_check(ENTRY_KEY);
156
157 /* don't consult cache if it is disabled or if we can't validate */
158 if ((status == LI_L1_CACHE_DISABLED) || (status == LI_L1_CACHE_FAILED)) return 1;
159
160 /* return 0 if cache is OK */
161 if (status == LI_L1_CACHE_OK) return 0;
162
163 /* flush cache */
164 pthread_mutex_lock(&_service_cache_lock);
165
166 for (i = 0; i < SERVICE_CACHE_SIZE; i++)
167 {
168 LI_ils_free(_service_cache[i], ENTRY_SIZE);
169 _service_cache[i] = NULL;
170 }
171
172 _service_cache_index = 0;
173
174 pthread_mutex_unlock(&_service_cache_lock);
175
176 /* don't consult cache - it's now empty */
177 return 1;
178 }
179
180
181 static struct servent *
182 cache_getservbyname(const char *name, const char *proto)
183 {
184 int i;
185 struct servent *s, *res;
186 char **aliases;
187
188 if (name == NULL) return NULL;
189 if (service_cache_check() != 0) return NULL;
190
191 pthread_mutex_lock(&_service_cache_lock);
192
193 for (i = 0; i < SERVICE_CACHE_SIZE; i++)
194 {
195 s = (struct servent *)_service_cache[i];
196 if (s == NULL) continue;
197
198 if (s->s_name != NULL)
199 {
200 if (!strcmp(name, s->s_name))
201 {
202 if ((proto == NULL) || ((s->s_proto != NULL) && (!strcmp(proto, s->s_proto))))
203 {
204 res = copy_service(s);
205 pthread_mutex_unlock(&_service_cache_lock);
206 return res;
207 }
208 }
209 }
210
211 aliases = s->s_aliases;
212 if (aliases == NULL)
213 {
214 pthread_mutex_unlock(&_service_cache_lock);
215 return NULL;
216 }
217
218 for (; *aliases != NULL; *aliases++)
219 {
220 if (!strcmp(name, *aliases))
221 {
222 if ((proto == NULL) || ((s->s_proto != NULL) && (!strcmp(proto, s->s_proto))))
223 {
224 res = copy_service(s);
225 pthread_mutex_unlock(&_service_cache_lock);
226 return res;
227 }
228 }
229 }
230 }
231
232 pthread_mutex_unlock(&_service_cache_lock);
233 return NULL;
234 }
235
236 static struct servent *
237 cache_getservbyport(int port, const char *proto)
238 {
239 int i;
240 struct servent *s, *res;
241
242 if (service_cache_check() != 0) return NULL;
243
244 pthread_mutex_lock(&_service_cache_lock);
245
246 for (i = 0; i < SERVICE_CACHE_SIZE; i++)
247 {
248 s = (struct servent *)_service_cache[i];
249 if (s == NULL) continue;
250
251 if (port == s->s_port)
252 {
253 if ((proto == NULL) || ((s->s_proto != NULL) && (!strcmp(proto, s->s_proto))))
254 {
255 res = copy_service(s);
256 pthread_mutex_unlock(&_service_cache_lock);
257 return res;
258 }
259 }
260 }
261
262 pthread_mutex_unlock(&_service_cache_lock);
263 return NULL;
264 }
265
266 static struct servent *
267 ds_getservbyport(int port, const char *proto)
268 {
269 struct servent *entry;
270 kvbuf_t *request;
271 kvarray_t *reply;
272 kern_return_t status;
273 static int proc = -1;
274 uint16_t sport;
275 char val[16];
276
277 if (proc < 0)
278 {
279 status = LI_DSLookupGetProcedureNumber("getservbyport", &proc);
280 if (status != KERN_SUCCESS) return NULL;
281 }
282
283 /* Encode NULL */
284 if (proto == NULL) proto = "";
285
286 sport = port;
287 snprintf(val, sizeof(val), "%d", ntohs(sport));
288
289 request = kvbuf_query("ksks", "port", val, "proto", proto);
290 if (request == NULL) return NULL;
291
292 reply = NULL;
293 status = LI_DSLookupQuery(proc, request, &reply);
294 kvbuf_free(request);
295
296 if (status != KERN_SUCCESS) return NULL;
297
298 entry = extract_service(reply);
299 kvarray_free(reply);
300
301 return entry;
302 }
303
304 static struct servent *
305 ds_getservbyname(const char *name, const char *proto)
306 {
307 struct servent *entry;
308 kvbuf_t *request;
309 kvarray_t *reply;
310 kern_return_t status;
311 static int proc = -1;
312
313 if (proc < 0)
314 {
315 status = LI_DSLookupGetProcedureNumber("getservbyname", &proc);
316 if (status != KERN_SUCCESS) return NULL;
317 }
318
319 /* Encode NULL */
320 if (name == NULL) name = "";
321 if (proto == NULL) proto = "";
322
323 request = kvbuf_query("ksks", "name", name, "proto", proto);
324 if (request == NULL) return NULL;
325
326 reply = NULL;
327 status = LI_DSLookupQuery(proc, request, &reply);
328 kvbuf_free(request);
329
330 if (status != KERN_SUCCESS) return NULL;
331
332 entry = extract_service(reply);
333 kvarray_free(reply);
334
335 return entry;
336 }
337
338 static void
339 ds_endservent()
340 {
341 LI_data_free_kvarray(LI_data_find_key(ENTRY_KEY));
342 }
343
344 static void
345 ds_setservent()
346 {
347 ds_endservent();
348 }
349
350 static struct servent *
351 ds_getservent()
352 {
353 static int proc = -1;
354
355 return (struct servent *)LI_getent("getservent", &proc, extract_service, ENTRY_KEY, ENTRY_SIZE);
356 }
357
358 static struct servent *
359 getserv(const char *name, const char *proto, int port, int source)
360 {
361 struct servent *res = NULL;
362 struct li_thread_info *tdata;
363 int add_to_cache;
364
365 tdata = LI_data_create_key(ENTRY_KEY, ENTRY_SIZE);
366 if (tdata == NULL) return NULL;
367
368 add_to_cache = 0;
369 res = NULL;
370
371 switch (source)
372 {
373 case S_GET_NAME:
374 res = cache_getservbyname(name, proto);
375 break;
376 case S_GET_PORT:
377 res = cache_getservbyport(port, proto);
378 break;
379 default: res = NULL;
380 }
381
382 if (res != NULL)
383 {
384 }
385 else if (_ds_running())
386 {
387 switch (source)
388 {
389 case S_GET_NAME:
390 res = ds_getservbyname(name, proto);
391 break;
392 case S_GET_PORT:
393 res = ds_getservbyport(port, proto);
394 break;
395 case S_GET_ENT:
396 res = ds_getservent();
397 break;
398 default: res = NULL;
399 }
400
401 if (res != NULL) add_to_cache = 1;
402 }
403 else
404 {
405 pthread_mutex_lock(&_service_lock);
406 switch (source)
407 {
408 case S_GET_NAME:
409 res = copy_service(_old_getservbyname(name, proto));
410 break;
411 case S_GET_PORT:
412 res = copy_service(_old_getservbyport(port, proto));
413 break;
414 case S_GET_ENT:
415 res = copy_service(_old_getservent());
416 break;
417 default: res = NULL;
418 }
419 pthread_mutex_unlock(&_service_lock);
420 }
421
422 if (add_to_cache == 1) cache_service(res);
423
424 LI_data_recycle(tdata, res, ENTRY_SIZE);
425 return (struct servent *)tdata->li_entry;
426 }
427
428 struct servent *
429 getservbyport(int port, const char *proto)
430 {
431 return getserv(NULL, proto, port, S_GET_PORT);
432 }
433
434 struct servent *
435 getservbyname(const char *name, const char *proto)
436 {
437 return getserv(name, proto, 0, S_GET_NAME);
438 }
439
440 struct servent *
441 getservent(void)
442 {
443 return getserv(NULL, NULL, 0, S_GET_ENT);
444 }
445
446 void
447 setservent(int stayopen)
448 {
449 if (_ds_running()) ds_setservent();
450 else _old_setservent();
451 }
452
453 void
454 endservent(void)
455 {
456 if (_ds_running()) ds_endservent();
457 else _old_endservent();
458 }