]> git.saurik.com Git - apple/libinfo.git/blob - lookup.subproj/lu_service.c
Libinfo-222.0.4.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 <rpc/types.h>
33 #include <rpc/xdr.h>
34 #include <netdb.h>
35 #include <netinet/in.h>
36 #include <pthread.h>
37
38 #include "_lu_types.h"
39 #include "lookup.h"
40 #include "lu_utils.h"
41
42 #define SERVICE_CACHE_SIZE 10
43 #define DEFAULT_SERVICE_CACHE_TTL 10
44
45 static pthread_mutex_t _service_cache_lock = PTHREAD_MUTEX_INITIALIZER;
46 static void *_service_cache[SERVICE_CACHE_SIZE] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
47 static unsigned int _service_cache_best_before[SERVICE_CACHE_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
48 static unsigned int _service_cache_index = 0;
49 static unsigned int _service_cache_ttl = DEFAULT_SERVICE_CACHE_TTL;
50
51 static pthread_mutex_t _service_lock = PTHREAD_MUTEX_INITIALIZER;
52
53 #define S_GET_NAME 1
54 #define S_GET_PORT 2
55 #define S_GET_ENT 3
56
57 extern struct servent *_old_getservbyport();
58 extern struct servent *_old_getservbyname();
59 extern struct servent *_old_getservent();
60 extern void _old_setservent();
61 extern void _old_endservent();
62 extern void _old_setservfile();
63
64 static void
65 free_service_data(struct servent *s)
66 {
67 char **aliases;
68
69 if (s == NULL) return;
70
71 if (s->s_name != NULL) free(s->s_name);
72 if (s->s_proto != NULL) free(s->s_proto);
73
74 aliases = s->s_aliases;
75 if (aliases != NULL)
76 {
77 while (*aliases != NULL) free(*aliases++);
78 free(s->s_aliases);
79 }
80 }
81
82 static void
83 free_service(struct servent *s)
84 {
85 if (s == NULL) return;
86 free_service_data(s);
87 free(s);
88 }
89
90 static void
91 free_lu_thread_info_service(void *x)
92 {
93 struct lu_thread_info *tdata;
94
95 if (x == NULL) return;
96
97 tdata = (struct lu_thread_info *)x;
98
99 if (tdata->lu_entry != NULL)
100 {
101 free_service((struct servent *)tdata->lu_entry);
102 tdata->lu_entry = NULL;
103 }
104
105 _lu_data_free_vm_xdr(tdata);
106
107 free(tdata);
108 }
109
110 static struct servent *
111 extract_service(XDR *xdr, const char *proto)
112 {
113 struct servent *s;
114 int i, j, nvals, nkeys, status;
115 char *key, **vals;
116
117 if (xdr == NULL) return NULL;
118
119 if (!xdr_int(xdr, &nkeys)) return NULL;
120
121 s = (struct servent *)calloc(1, sizeof(struct servent));
122
123 for (i = 0; i < nkeys; i++)
124 {
125 key = NULL;
126 vals = NULL;
127 nvals = 0;
128
129 status = _lu_xdr_attribute(xdr, &key, &vals, &nvals);
130 if (status < 0)
131 {
132 free_service(s);
133 return NULL;
134 }
135
136 if (nvals == 0)
137 {
138 free(key);
139 continue;
140 }
141
142 j = 0;
143
144 if ((s->s_name == NULL) && (!strcmp("name", key)))
145 {
146 s->s_name = vals[0];
147 if (nvals > 1)
148 {
149 s->s_aliases = (char **)calloc(nvals, sizeof(char *));
150 for (j = 1; j < nvals; j++) s->s_aliases[j-1] = vals[j];
151 }
152 j = nvals;
153 }
154 else if ((s->s_proto == NULL) && (!strcmp("protocol", key)))
155 {
156 if ((proto == NULL) || (proto[0] == '\0'))
157 {
158 s->s_proto = vals[0];
159 j = 1;
160 }
161 else
162 {
163 s->s_proto = strdup(proto);
164 }
165 }
166 else if ((s->s_port == 0) && (!strcmp("port", key)))
167 {
168 s->s_port = htons(atoi(vals[0]));
169 }
170
171 free(key);
172 if (vals != NULL)
173 {
174 for (; j < nvals; j++) free(vals[j]);
175 free(vals);
176 }
177 }
178
179 if (s->s_name == NULL) s->s_name = strdup("");
180 if (s->s_proto == NULL) s->s_proto = strdup("");
181 if (s->s_aliases == NULL) s->s_aliases = (char **)calloc(1, sizeof(char *));
182
183 return s;
184 }
185
186 static struct servent *
187 copy_service(struct servent *in)
188 {
189 int i, len;
190 struct servent *s;
191
192 if (in == NULL) return NULL;
193
194 s = (struct servent *)calloc(1, sizeof(struct servent));
195
196 s->s_name = LU_COPY_STRING(in->s_name);
197
198 len = 0;
199 if (in->s_aliases != NULL)
200 {
201 for (len = 0; in->s_aliases[len] != NULL; len++);
202 }
203
204 s->s_aliases = (char **)calloc(len + 1, sizeof(char *));
205 for (i = 0; i < len; i++)
206 {
207 s->s_aliases[i] = strdup(in->s_aliases[i]);
208 }
209
210 s->s_proto = LU_COPY_STRING(in->s_proto);
211 s->s_port = in->s_port;
212
213 return s;
214 }
215
216 static void
217 recycle_service(struct lu_thread_info *tdata, struct servent *in)
218 {
219 struct servent *s;
220
221 if (tdata == NULL) return;
222 s = (struct servent *)tdata->lu_entry;
223
224 if (in == NULL)
225 {
226 free_service(s);
227 tdata->lu_entry = NULL;
228 }
229
230 if (tdata->lu_entry == NULL)
231 {
232 tdata->lu_entry = in;
233 return;
234 }
235
236 free_service_data(s);
237
238 s->s_name = in->s_name;
239 s->s_aliases = in->s_aliases;
240 s->s_proto = in->s_proto;
241 s->s_port = in->s_port;
242
243 free(in);
244 }
245
246 __private_extern__ unsigned int
247 get_service_cache_ttl()
248 {
249 return _service_cache_ttl;
250 }
251
252 __private_extern__ void
253 set_service_cache_ttl(unsigned int ttl)
254 {
255 int i;
256
257 pthread_mutex_lock(&_service_cache_lock);
258
259 _service_cache_ttl = ttl;
260
261 if (ttl == 0)
262 {
263 for (i = 0; i < SERVICE_CACHE_SIZE; i++)
264 {
265 if (_service_cache[i] == NULL) continue;
266
267 free_service((struct servent *)_service_cache[i]);
268 _service_cache[i] = NULL;
269 _service_cache_best_before[i] = 0;
270 }
271 }
272
273 pthread_mutex_unlock(&_service_cache_lock);
274 }
275
276 static void
277 cache_service(struct servent *s)
278 {
279 struct timeval now;
280 struct servent *scache;
281
282 if (_service_cache_ttl == 0) return;
283 if (s == NULL) return;
284
285 pthread_mutex_lock(&_service_cache_lock);
286
287 scache = copy_service(s);
288
289 gettimeofday(&now, NULL);
290
291 if (_service_cache[_service_cache_index] != NULL)
292 free_service((struct servent *)_service_cache[_service_cache_index]);
293
294 _service_cache[_service_cache_index] = scache;
295 _service_cache_best_before[_service_cache_index] = now.tv_sec + _service_cache_ttl;
296 _service_cache_index = (_service_cache_index + 1) % SERVICE_CACHE_SIZE;
297
298 pthread_mutex_unlock(&_service_cache_lock);
299 }
300
301 static struct servent *
302 cache_getservbyname(const char *name, const char *proto)
303 {
304 int i;
305 struct servent *s, *res;
306 struct timeval now;
307 char **aliases;
308
309 if (_service_cache_ttl == 0) return NULL;
310 if (name == NULL) return NULL;
311
312 pthread_mutex_lock(&_service_cache_lock);
313
314 gettimeofday(&now, NULL);
315
316 for (i = 0; i < SERVICE_CACHE_SIZE; i++)
317 {
318 if (_service_cache_best_before[i] == 0) continue;
319 if ((unsigned int)now.tv_sec > _service_cache_best_before[i]) continue;
320
321 s = (struct servent *)_service_cache[i];
322
323 if (s->s_name != NULL)
324 {
325 if (!strcmp(name, s->s_name))
326 {
327 if ((proto == NULL) || ((s->s_proto != NULL) && (!strcmp(proto, s->s_proto))))
328 {
329 res = copy_service(s);
330 pthread_mutex_unlock(&_service_cache_lock);
331 return res;
332 }
333 }
334 }
335
336 aliases = s->s_aliases;
337 if (aliases == NULL)
338 {
339 pthread_mutex_unlock(&_service_cache_lock);
340 return NULL;
341 }
342
343 for (; *aliases != NULL; *aliases++)
344 {
345 if (!strcmp(name, *aliases))
346 {
347 if ((proto == NULL) || ((s->s_proto != NULL) && (!strcmp(proto, s->s_proto))))
348 {
349 res = copy_service(s);
350 pthread_mutex_unlock(&_service_cache_lock);
351 return res;
352 }
353 }
354 }
355 }
356
357 pthread_mutex_unlock(&_service_cache_lock);
358 return NULL;
359 }
360
361 static struct servent *
362 cache_getservbyport(int port, const char *proto)
363 {
364 int i;
365 struct servent *s, *res;
366 struct timeval now;
367
368 if (_service_cache_ttl == 0) return NULL;
369
370 pthread_mutex_lock(&_service_cache_lock);
371
372 gettimeofday(&now, NULL);
373
374 for (i = 0; i < SERVICE_CACHE_SIZE; i++)
375 {
376 if (_service_cache_best_before[i] == 0) continue;
377 if ((unsigned int)now.tv_sec > _service_cache_best_before[i]) continue;
378
379 s = (struct servent *)_service_cache[i];
380
381 if (port == s->s_port)
382 {
383 if ((proto == NULL) || ((s->s_proto != NULL) && (!strcmp(proto, s->s_proto))))
384 {
385 res = copy_service(s);
386 pthread_mutex_unlock(&_service_cache_lock);
387 return res;
388 }
389 }
390 }
391
392 pthread_mutex_unlock(&_service_cache_lock);
393 return NULL;
394 }
395
396 static struct servent *
397 lu_getservbyport(int port, const char *proto)
398 {
399 struct servent *s;
400 unsigned int datalen;
401 XDR outxdr, inxdr;
402 static int proc = -1;
403 char output_buf[_LU_MAXLUSTRLEN + 3 * BYTES_PER_XDR_UNIT];
404 char *lookup_buf;
405 int count;
406
407 if (proc < 0)
408 {
409 if (_lookup_link(_lu_port, "getservbyport", &proc) != KERN_SUCCESS)
410 {
411 return NULL;
412 }
413 }
414
415 /* Encode NULL for xmission to lookupd. */
416 if (proto == NULL) proto = "";
417
418 /* convert to host order */
419 port = ntohs(port);
420
421 xdrmem_create(&outxdr, output_buf, sizeof(output_buf), XDR_ENCODE);
422 if (!xdr_int(&outxdr, &port) || !xdr__lu_string(&outxdr, (_lu_string *)&proto))
423 {
424 xdr_destroy(&outxdr);
425 return NULL;
426 }
427
428 datalen = 0;
429 lookup_buf = NULL;
430
431 if (_lookup_all(_lu_port, proc, (unit *)output_buf,
432 xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen)
433 != KERN_SUCCESS)
434 {
435 xdr_destroy(&outxdr);
436 return NULL;
437 }
438
439 xdr_destroy(&outxdr);
440
441 datalen *= BYTES_PER_XDR_UNIT;
442 if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
443
444 xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
445
446 count = 0;
447 if (!xdr_int(&inxdr, &count))
448 {
449 xdr_destroy(&inxdr);
450 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
451 return NULL;
452 }
453
454 if (count == 0)
455 {
456 xdr_destroy(&inxdr);
457 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
458 return NULL;
459 }
460
461 /*
462 * lookupd will only send back a reply for a service with the protocol specified
463 * if it finds a match. We pass the protocol name to extract_service, which
464 * copies the requested protocol name into the returned servent. This is a
465 * bit of a kludge, but since NetInfo / lookupd treat services as single entities
466 * with multiple protocols, we are forced to do some special-case handling.
467 */
468 s = extract_service(&inxdr, proto);
469 xdr_destroy(&inxdr);
470 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
471
472 return s;
473 }
474
475 static struct servent *
476 lu_getservbyname(const char *name, const char *proto)
477 {
478 struct servent *s;
479 unsigned int datalen;
480 char *lookup_buf;
481 char output_buf[2 * (_LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT)];
482 XDR outxdr, inxdr;
483 static int proc = -1;
484 int count;
485
486 if (proc < 0)
487 {
488 if (_lookup_link(_lu_port, "getservbyname", &proc) != KERN_SUCCESS)
489 {
490 return NULL;
491 }
492 }
493
494 /* Encode NULL for xmission to lookupd. */
495 if (proto == NULL) proto = "";
496
497 xdrmem_create(&outxdr, output_buf, sizeof(output_buf), XDR_ENCODE);
498 if (!xdr__lu_string(&outxdr, (_lu_string *)&name) ||
499 !xdr__lu_string(&outxdr, (_lu_string *)&proto))
500 {
501 xdr_destroy(&outxdr);
502 return NULL;
503 }
504
505 datalen = 0;
506 lookup_buf = NULL;
507
508 if (_lookup_all(_lu_port, proc, (unit *)output_buf,
509 xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen)
510 != KERN_SUCCESS)
511 {
512 xdr_destroy(&outxdr);
513 return NULL;
514 }
515
516 xdr_destroy(&outxdr);
517
518 datalen *= BYTES_PER_XDR_UNIT;
519 if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
520
521 xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
522
523 count = 0;
524 if (!xdr_int(&inxdr, &count))
525 {
526 xdr_destroy(&inxdr);
527 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
528 return NULL;
529 }
530
531 if (count == 0)
532 {
533 xdr_destroy(&inxdr);
534 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
535 return NULL;
536 }
537
538 /*
539 * lookupd will only send back a reply for a service with the protocol specified
540 * if it finds a match. We pass the protocol name to extract_service, which
541 * copies the requested protocol name into the returned servent. This is a
542 * bit of a kludge, but since NetInfo / lookupd treat services as single entities
543 * with multiple protocols, we are forced to do some special-case handling.
544 */
545 s = extract_service(&inxdr, proto);
546 xdr_destroy(&inxdr);
547 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
548
549 return s;
550 }
551
552 static void
553 lu_endservent()
554 {
555 struct lu_thread_info *tdata;
556
557 tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
558 _lu_data_free_vm_xdr(tdata);
559 }
560
561 static void
562 lu_setservent()
563 {
564 lu_endservent();
565 }
566
567 static struct servent *
568 lu_getservent()
569 {
570 struct servent *s;
571 static int proc = -1;
572 struct lu_thread_info *tdata;
573
574 tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
575 if (tdata == NULL)
576 {
577 tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
578 _lu_data_set_key(_lu_data_key_service, tdata);
579 }
580
581 if (tdata->lu_vm == NULL)
582 {
583 if (proc < 0)
584 {
585 if (_lookup_link(_lu_port, "getservent", &proc) != KERN_SUCCESS)
586 {
587 lu_endservent();
588 return NULL;
589 }
590 }
591
592 if (_lookup_all(_lu_port, proc, NULL, 0, &(tdata->lu_vm), &(tdata->lu_vm_length)) != KERN_SUCCESS)
593 {
594 lu_endservent();
595 return NULL;
596 }
597
598 /* mig stubs measure size in words (4 bytes) */
599 tdata->lu_vm_length *= 4;
600
601 if (tdata->lu_xdr != NULL)
602 {
603 xdr_destroy(tdata->lu_xdr);
604 free(tdata->lu_xdr);
605 }
606 tdata->lu_xdr = (XDR *)calloc(1, sizeof(XDR));
607
608 xdrmem_create(tdata->lu_xdr, tdata->lu_vm, tdata->lu_vm_length, XDR_DECODE);
609 if (!xdr_int(tdata->lu_xdr, &tdata->lu_vm_cursor))
610 {
611 lu_endservent();
612 return NULL;
613 }
614 }
615
616 if (tdata->lu_vm_cursor == 0)
617 {
618 lu_endservent();
619 return NULL;
620 }
621
622 s = extract_service(tdata->lu_xdr, NULL);
623 if (s == NULL)
624 {
625 lu_endservent();
626 return NULL;
627 }
628
629 tdata->lu_vm_cursor--;
630
631 return s;
632 }
633
634 static struct servent *
635 getserv(const char *name, const char *proto, int port, int source)
636 {
637 struct servent *res = NULL;
638 struct lu_thread_info *tdata;
639 int from_cache;
640
641 tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
642 if (tdata == NULL)
643 {
644 tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
645 _lu_data_set_key(_lu_data_key_service, tdata);
646 }
647
648 from_cache = 0;
649 res = NULL;
650
651 switch (source)
652 {
653 case S_GET_NAME:
654 res = cache_getservbyname(name, proto);
655 break;
656 case S_GET_PORT:
657 res = cache_getservbyport(port, proto);
658 break;
659 default: res = NULL;
660 }
661
662 if (res != NULL)
663 {
664 from_cache = 1;
665 }
666 else if (_lu_running())
667 {
668 switch (source)
669 {
670 case S_GET_NAME:
671 res = lu_getservbyname(name, proto);
672 break;
673 case S_GET_PORT:
674 res = lu_getservbyport(port, proto);
675 break;
676 case S_GET_ENT:
677 res = lu_getservent();
678 break;
679 default: res = NULL;
680 }
681 }
682 else
683 {
684 pthread_mutex_lock(&_service_lock);
685 switch (source)
686 {
687 case S_GET_NAME:
688 res = copy_service(_old_getservbyname(name, proto));
689 break;
690 case S_GET_PORT:
691 res = copy_service(_old_getservbyport(port, proto));
692 break;
693 case S_GET_ENT:
694 res = copy_service(_old_getservent());
695 break;
696 default: res = NULL;
697 }
698 pthread_mutex_unlock(&_service_lock);
699 }
700
701 if (from_cache == 0) cache_service(res);
702
703 recycle_service(tdata, res);
704 return (struct servent *)tdata->lu_entry;
705 }
706
707 struct servent *
708 getservbyport(int port, const char *proto)
709 {
710 return getserv(NULL, proto, port, S_GET_PORT);
711 }
712
713 struct servent *
714 getservbyname(const char *name, const char *proto)
715 {
716 return getserv(name, proto, 0, S_GET_NAME);
717 }
718
719 struct servent *
720 getservent(void)
721 {
722 return getserv(NULL, NULL, 0, S_GET_ENT);
723 }
724
725 void
726 setservent(int stayopen)
727 {
728 if (_lu_running()) lu_setservent();
729 else _old_setservent();
730 }
731
732 void
733 endservent(void)
734 {
735 if (_lu_running()) lu_endservent();
736 else _old_endservent();
737 }