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