Libinfo-173.1.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 xdrmem_create(&outxdr, output_buf, sizeof(output_buf), XDR_ENCODE);
419 if (!xdr_int(&outxdr, &port) || !xdr__lu_string(&outxdr, (_lu_string *)&proto))
420 {
421 xdr_destroy(&outxdr);
422 return NULL;
423 }
424
425 datalen = 0;
426 lookup_buf = NULL;
427
428 if (_lookup_all(_lu_port, proc, (unit *)output_buf,
429 xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen)
430 != KERN_SUCCESS)
431 {
432 xdr_destroy(&outxdr);
433 return NULL;
434 }
435
436 xdr_destroy(&outxdr);
437
438 datalen *= BYTES_PER_XDR_UNIT;
439 if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
440
441 xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
442
443 count = 0;
444 if (!xdr_int(&inxdr, &count))
445 {
446 xdr_destroy(&inxdr);
447 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
448 return NULL;
449 }
450
451 if (count == 0)
452 {
453 xdr_destroy(&inxdr);
454 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
455 return NULL;
456 }
457
458 /*
459 * lookupd will only send back a reply for a service with the protocol specified
460 * if it finds a match. We pass the protocol name to extract_service, which
461 * copies the requested protocol name into the returned servent. This is a
462 * bit of a kludge, but since NetInfo / lookupd treat services as single entities
463 * with multiple protocols, we are forced to do some special-case handling.
464 */
465 s = extract_service(&inxdr, proto);
466 xdr_destroy(&inxdr);
467 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
468
469 return s;
470 }
471
472 static struct servent *
473 lu_getservbyname(const char *name, const char *proto)
474 {
475 struct servent *s;
476 unsigned int datalen;
477 char *lookup_buf;
478 char output_buf[2 * (_LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT)];
479 XDR outxdr, inxdr;
480 static int proc = -1;
481 int count;
482
483 if (proc < 0)
484 {
485 if (_lookup_link(_lu_port, "getservbyname", &proc) != KERN_SUCCESS)
486 {
487 return NULL;
488 }
489 }
490
491 /* Encode NULL for xmission to lookupd. */
492 if (proto == NULL) proto = "";
493
494 xdrmem_create(&outxdr, output_buf, sizeof(output_buf), XDR_ENCODE);
495 if (!xdr__lu_string(&outxdr, (_lu_string *)&name) ||
496 !xdr__lu_string(&outxdr, (_lu_string *)&proto))
497 {
498 xdr_destroy(&outxdr);
499 return NULL;
500 }
501
502 datalen = 0;
503 lookup_buf = NULL;
504
505 if (_lookup_all(_lu_port, proc, (unit *)output_buf,
506 xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen)
507 != KERN_SUCCESS)
508 {
509 xdr_destroy(&outxdr);
510 return NULL;
511 }
512
513 xdr_destroy(&outxdr);
514
515 datalen *= BYTES_PER_XDR_UNIT;
516 if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
517
518 xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
519
520 count = 0;
521 if (!xdr_int(&inxdr, &count))
522 {
523 xdr_destroy(&inxdr);
524 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
525 return NULL;
526 }
527
528 if (count == 0)
529 {
530 xdr_destroy(&inxdr);
531 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
532 return NULL;
533 }
534
535 /*
536 * lookupd will only send back a reply for a service with the protocol specified
537 * if it finds a match. We pass the protocol name to extract_service, which
538 * copies the requested protocol name into the returned servent. This is a
539 * bit of a kludge, but since NetInfo / lookupd treat services as single entities
540 * with multiple protocols, we are forced to do some special-case handling.
541 */
542 s = extract_service(&inxdr, proto);
543 xdr_destroy(&inxdr);
544 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
545
546 return s;
547 }
548
549 static void
550 lu_endservent()
551 {
552 struct lu_thread_info *tdata;
553
554 tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
555 _lu_data_free_vm_xdr(tdata);
556 }
557
558 static void
559 lu_setservent()
560 {
561 lu_endservent();
562 }
563
564 static struct servent *
565 lu_getservent()
566 {
567 struct servent *s;
568 static int proc = -1;
569 struct lu_thread_info *tdata;
570
571 tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
572 if (tdata == NULL)
573 {
574 tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
575 _lu_data_set_key(_lu_data_key_service, tdata);
576 }
577
578 if (tdata->lu_vm == NULL)
579 {
580 if (proc < 0)
581 {
582 if (_lookup_link(_lu_port, "getservent", &proc) != KERN_SUCCESS)
583 {
584 lu_endservent();
585 return NULL;
586 }
587 }
588
589 if (_lookup_all(_lu_port, proc, NULL, 0, &(tdata->lu_vm), &(tdata->lu_vm_length)) != KERN_SUCCESS)
590 {
591 lu_endservent();
592 return NULL;
593 }
594
595 /* mig stubs measure size in words (4 bytes) */
596 tdata->lu_vm_length *= 4;
597
598 if (tdata->lu_xdr != NULL)
599 {
600 xdr_destroy(tdata->lu_xdr);
601 free(tdata->lu_xdr);
602 }
603 tdata->lu_xdr = (XDR *)calloc(1, sizeof(XDR));
604
605 xdrmem_create(tdata->lu_xdr, tdata->lu_vm, tdata->lu_vm_length, XDR_DECODE);
606 if (!xdr_int(tdata->lu_xdr, &tdata->lu_vm_cursor))
607 {
608 lu_endservent();
609 return NULL;
610 }
611 }
612
613 if (tdata->lu_vm_cursor == 0)
614 {
615 lu_endservent();
616 return NULL;
617 }
618
619 s = extract_service(tdata->lu_xdr, NULL);
620 if (s == NULL)
621 {
622 lu_endservent();
623 return NULL;
624 }
625
626 tdata->lu_vm_cursor--;
627
628 return s;
629 }
630
631 static struct servent *
632 getserv(const char *name, const char *proto, int port, int source)
633 {
634 struct servent *res = NULL;
635 struct lu_thread_info *tdata;
636 int from_cache;
637
638 tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
639 if (tdata == NULL)
640 {
641 tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
642 _lu_data_set_key(_lu_data_key_service, tdata);
643 }
644
645 from_cache = 0;
646 res = NULL;
647
648 switch (source)
649 {
650 case S_GET_NAME:
651 res = cache_getservbyname(name, proto);
652 break;
653 case S_GET_PORT:
654 res = cache_getservbyport(port, proto);
655 break;
656 default: res = NULL;
657 }
658
659 if (res != NULL)
660 {
661 from_cache = 1;
662 }
663 else if (_lu_running())
664 {
665 switch (source)
666 {
667 case S_GET_NAME:
668 res = lu_getservbyname(name, proto);
669 break;
670 case S_GET_PORT:
671 res = lu_getservbyport(port, proto);
672 break;
673 case S_GET_ENT:
674 res = lu_getservent();
675 break;
676 default: res = NULL;
677 }
678 }
679 else
680 {
681 pthread_mutex_lock(&_service_lock);
682 switch (source)
683 {
684 case S_GET_NAME:
685 res = copy_service(_old_getservbyname(name, proto));
686 break;
687 case S_GET_PORT:
688 res = copy_service(_old_getservbyport(port, proto));
689 break;
690 case S_GET_ENT:
691 res = copy_service(_old_getservent());
692 break;
693 default: res = NULL;
694 }
695 pthread_mutex_unlock(&_service_lock);
696 }
697
698 if (from_cache == 0) cache_service(res);
699
700 recycle_service(tdata, res);
701 return (struct servent *)tdata->lu_entry;
702 }
703
704 struct servent *
705 getservbyport(int port, const char *proto)
706 {
707 return getserv(NULL, proto, port, S_GET_PORT);
708 }
709
710 struct servent *
711 getservbyname(const char *name, const char *proto)
712 {
713 return getserv(name, proto, 0, S_GET_NAME);
714 }
715
716 struct servent *
717 getservent(void)
718 {
719 return getserv(NULL, NULL, 0, S_GET_ENT);
720 }
721
722 void
723 setservent(int stayopen)
724 {
725 if (_lu_running()) lu_setservent();
726 else _old_setservent();
727 }
728
729 void
730 endservent(void)
731 {
732 if (_lu_running()) lu_endservent();
733 else _old_endservent();
734 }