Libinfo-129.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 static pthread_mutex_t _service_lock = PTHREAD_MUTEX_INITIALIZER;
43
44 #define S_GET_NAME 1
45 #define S_GET_PORT 2
46 #define S_GET_ENT 3
47
48 extern struct servent *_old_getservbyport();
49 extern struct servent *_old_getservbyname();
50 extern struct servent *_old_getservent();
51 extern void _old_setservent();
52 extern void _old_endservent();
53 extern void _old_setservfile();
54
55 static void
56 free_service_data(struct servent *s)
57 {
58 char **aliases;
59
60 if (s == NULL) return;
61
62 if (s->s_name != NULL) free(s->s_name);
63 if (s->s_proto != NULL) free(s->s_proto);
64
65 aliases = s->s_aliases;
66 if (aliases != NULL)
67 {
68 while (*aliases != NULL) free(*aliases++);
69 free(s->s_aliases);
70 }
71 }
72
73 static void
74 free_service(struct servent *s)
75 {
76 if (s == NULL) return;
77 free_service_data(s);
78 free(s);
79 }
80
81 static void
82 free_lu_thread_info_service(void *x)
83 {
84 struct lu_thread_info *tdata;
85
86 if (x == NULL) return;
87
88 tdata = (struct lu_thread_info *)x;
89
90 if (tdata->lu_entry != NULL)
91 {
92 free_service((struct servent *)tdata->lu_entry);
93 tdata->lu_entry = NULL;
94 }
95
96 _lu_data_free_vm_xdr(tdata);
97
98 free(tdata);
99 }
100
101 static struct servent *
102 extract_service(XDR *xdr, const char *proto)
103 {
104 struct servent *s;
105 int i, j, nvals, nkeys, status;
106 char *key, **vals;
107
108 if (xdr == NULL) return NULL;
109
110 if (!xdr_int(xdr, &nkeys)) return NULL;
111
112 s = (struct servent *)calloc(1, sizeof(struct servent));
113
114 for (i = 0; i < nkeys; i++)
115 {
116 key = NULL;
117 vals = NULL;
118 nvals = 0;
119
120 status = _lu_xdr_attribute(xdr, &key, &vals, &nvals);
121 if (status < 0)
122 {
123 free_service(s);
124 return NULL;
125 }
126
127 if (nvals == 0)
128 {
129 free(key);
130 continue;
131 }
132
133 j = 0;
134
135 if ((s->s_name == NULL) && (!strcmp("name", key)))
136 {
137 s->s_name = vals[0];
138 if (nvals > 1)
139 {
140 s->s_aliases = (char **)calloc(nvals, sizeof(char *));
141 for (j = 1; j < nvals; j++) s->s_aliases[j-1] = vals[j];
142 }
143 j = nvals;
144 }
145 else if ((s->s_proto == NULL) && (!strcmp("protocol", key)))
146 {
147 if ((proto == NULL) || (proto[0] == '\0'))
148 {
149 s->s_proto = vals[0];
150 j = 1;
151 }
152 else
153 {
154 s->s_proto = strdup(proto);
155 }
156 }
157 else if ((s->s_port == 0) && (!strcmp("port", key)))
158 {
159 s->s_port = htons(atoi(vals[0]));
160 }
161
162 free(key);
163 if (vals != NULL)
164 {
165 for (; j < nvals; j++) free(vals[j]);
166 free(vals);
167 }
168 }
169
170 if (s->s_name == NULL) s->s_name = strdup("");
171 if (s->s_proto == NULL) s->s_proto = strdup("");
172 if (s->s_aliases == NULL) s->s_aliases = (char **)calloc(1, sizeof(char *));
173
174 return s;
175 }
176
177 static struct servent *
178 copy_service(struct servent *in)
179 {
180 int i, len;
181 struct servent *s;
182
183 if (in == NULL) return NULL;
184
185 s = (struct servent *)calloc(1, sizeof(struct servent));
186
187 s->s_name = LU_COPY_STRING(in->s_name);
188
189 len = 0;
190 if (in->s_aliases != NULL)
191 {
192 for (len = 0; in->s_aliases[len] != NULL; len++);
193 }
194
195 s->s_aliases = (char **)calloc(len + 1, sizeof(char *));
196 for (i = 0; i < len; i++)
197 {
198 s->s_aliases[i] = strdup(in->s_aliases[i]);
199 }
200
201 s->s_proto = LU_COPY_STRING(in->s_proto);
202 s->s_port = in->s_port;
203
204 return s;
205 }
206
207 static void
208 recycle_service(struct lu_thread_info *tdata, struct servent *in)
209 {
210 struct servent *s;
211
212 if (tdata == NULL) return;
213 s = (struct servent *)tdata->lu_entry;
214
215 if (in == NULL)
216 {
217 free_service(s);
218 tdata->lu_entry = NULL;
219 }
220
221 if (tdata->lu_entry == NULL)
222 {
223 tdata->lu_entry = in;
224 return;
225 }
226
227 free_service_data(s);
228
229 s->s_name = in->s_name;
230 s->s_aliases = in->s_aliases;
231 s->s_proto = in->s_proto;
232 s->s_port = in->s_port;
233
234 free(in);
235 }
236
237 static struct servent *
238 lu_getservbyport(int port, const char *proto)
239 {
240 struct servent *s;
241 unsigned int datalen;
242 XDR outxdr, inxdr;
243 static int proc = -1;
244 char output_buf[_LU_MAXLUSTRLEN + 3 * BYTES_PER_XDR_UNIT];
245 char *lookup_buf;
246 int count;
247
248 if (proc < 0)
249 {
250 if (_lookup_link(_lu_port, "getservbyport", &proc) != KERN_SUCCESS)
251 {
252 return NULL;
253 }
254 }
255
256 /* Encode NULL for xmission to lookupd. */
257 if (proto == NULL) proto = "";
258
259 xdrmem_create(&outxdr, output_buf, sizeof(output_buf), XDR_ENCODE);
260 if (!xdr_int(&outxdr, &port) || !xdr__lu_string(&outxdr, (_lu_string *)&proto))
261 {
262 xdr_destroy(&outxdr);
263 return NULL;
264 }
265
266 datalen = 0;
267 lookup_buf = NULL;
268
269 if (_lookup_all(_lu_port, proc, (unit *)output_buf,
270 xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen)
271 != KERN_SUCCESS)
272 {
273 xdr_destroy(&outxdr);
274 return NULL;
275 }
276
277 xdr_destroy(&outxdr);
278
279 datalen *= BYTES_PER_XDR_UNIT;
280 if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
281
282 xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
283
284 count = 0;
285 if (!xdr_int(&inxdr, &count))
286 {
287 xdr_destroy(&inxdr);
288 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
289 return NULL;
290 }
291
292 if (count == 0)
293 {
294 xdr_destroy(&inxdr);
295 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
296 return NULL;
297 }
298
299 /*
300 * lookupd will only send back a reply for a service with the protocol specified
301 * if it finds a match. We pass the protocol name to extract_service, which
302 * copies the requested protocol name into the returned servent. This is a
303 * bit of a kludge, but since NetInfo / lookupd treat services as single entities
304 * with multiple protocols, we are forced to do some special-case handling.
305 */
306 s = extract_service(&inxdr, proto);
307 xdr_destroy(&inxdr);
308 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
309
310 return s;
311 }
312
313 static struct servent *
314 lu_getservbyname(const char *name, const char *proto)
315 {
316 struct servent *s;
317 unsigned int datalen;
318 char *lookup_buf;
319 char output_buf[2 * (_LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT)];
320 XDR outxdr, inxdr;
321 static int proc = -1;
322 int count;
323
324 if (proc < 0)
325 {
326 if (_lookup_link(_lu_port, "getservbyname", &proc) != KERN_SUCCESS)
327 {
328 return NULL;
329 }
330 }
331
332 /* Encode NULL for xmission to lookupd. */
333 if (proto == NULL) proto = "";
334
335 xdrmem_create(&outxdr, output_buf, sizeof(output_buf), XDR_ENCODE);
336 if (!xdr__lu_string(&outxdr, (_lu_string *)&name) ||
337 !xdr__lu_string(&outxdr, (_lu_string *)&proto))
338 {
339 xdr_destroy(&outxdr);
340 return NULL;
341 }
342
343 datalen = 0;
344 lookup_buf = NULL;
345
346 if (_lookup_all(_lu_port, proc, (unit *)output_buf,
347 xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen)
348 != KERN_SUCCESS)
349 {
350 xdr_destroy(&outxdr);
351 return NULL;
352 }
353
354 xdr_destroy(&outxdr);
355
356 datalen *= BYTES_PER_XDR_UNIT;
357 if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
358
359 xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
360
361 count = 0;
362 if (!xdr_int(&inxdr, &count))
363 {
364 xdr_destroy(&inxdr);
365 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
366 return NULL;
367 }
368
369 if (count == 0)
370 {
371 xdr_destroy(&inxdr);
372 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
373 return NULL;
374 }
375
376 /*
377 * lookupd will only send back a reply for a service with the protocol specified
378 * if it finds a match. We pass the protocol name to extract_service, which
379 * copies the requested protocol name into the returned servent. This is a
380 * bit of a kludge, but since NetInfo / lookupd treat services as single entities
381 * with multiple protocols, we are forced to do some special-case handling.
382 */
383 s = extract_service(&inxdr, proto);
384 xdr_destroy(&inxdr);
385 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
386
387 return s;
388 }
389
390 static void
391 lu_endservent()
392 {
393 struct lu_thread_info *tdata;
394
395 tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
396 _lu_data_free_vm_xdr(tdata);
397 }
398
399 static void
400 lu_setservent()
401 {
402 lu_endservent();
403 }
404
405 static struct servent *
406 lu_getservent()
407 {
408 struct servent *s;
409 static int proc = -1;
410 struct lu_thread_info *tdata;
411
412 tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
413 if (tdata == NULL)
414 {
415 tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
416 _lu_data_set_key(_lu_data_key_service, tdata);
417 }
418
419 if (tdata->lu_vm == NULL)
420 {
421 if (proc < 0)
422 {
423 if (_lookup_link(_lu_port, "getservent", &proc) != KERN_SUCCESS)
424 {
425 lu_endservent();
426 return NULL;
427 }
428 }
429
430 if (_lookup_all(_lu_port, proc, NULL, 0, &(tdata->lu_vm), &(tdata->lu_vm_length)) != KERN_SUCCESS)
431 {
432 lu_endservent();
433 return NULL;
434 }
435
436 /* mig stubs measure size in words (4 bytes) */
437 tdata->lu_vm_length *= 4;
438
439 if (tdata->lu_xdr != NULL)
440 {
441 xdr_destroy(tdata->lu_xdr);
442 free(tdata->lu_xdr);
443 }
444 tdata->lu_xdr = (XDR *)calloc(1, sizeof(XDR));
445
446 xdrmem_create(tdata->lu_xdr, tdata->lu_vm, tdata->lu_vm_length, XDR_DECODE);
447 if (!xdr_int(tdata->lu_xdr, &tdata->lu_vm_cursor))
448 {
449 lu_endservent();
450 return NULL;
451 }
452 }
453
454 if (tdata->lu_vm_cursor == 0)
455 {
456 lu_endservent();
457 return NULL;
458 }
459
460 s = extract_service(tdata->lu_xdr, NULL);
461 if (s == NULL)
462 {
463 lu_endservent();
464 return NULL;
465 }
466
467 tdata->lu_vm_cursor--;
468
469 return s;
470 }
471
472 static struct servent *
473 getserv(const char *name, const char *proto, int port, int source)
474 {
475 struct servent *res = NULL;
476 struct lu_thread_info *tdata;
477
478 tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
479 if (tdata == NULL)
480 {
481 tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
482 _lu_data_set_key(_lu_data_key_service, tdata);
483 }
484
485 if (_lu_running())
486 {
487 switch (source)
488 {
489 case S_GET_NAME:
490 res = lu_getservbyname(name, proto);
491 break;
492 case S_GET_PORT:
493 res = lu_getservbyport(port, proto);
494 break;
495 case S_GET_ENT:
496 res = lu_getservent();
497 break;
498 default: res = NULL;
499 }
500 }
501 else
502 {
503 pthread_mutex_lock(&_service_lock);
504 switch (source)
505 {
506 case S_GET_NAME:
507 res = copy_service(_old_getservbyname(name, proto));
508 break;
509 case S_GET_PORT:
510 res = copy_service(_old_getservbyport(port, proto));
511 break;
512 case S_GET_ENT:
513 res = copy_service(_old_getservent());
514 break;
515 default: res = NULL;
516 }
517 pthread_mutex_unlock(&_service_lock);
518 }
519
520 recycle_service(tdata, res);
521 return (struct servent *)tdata->lu_entry;
522 }
523
524 struct servent *
525 getservbyport(int port, const char *proto)
526 {
527 return getserv(NULL, proto, port, S_GET_PORT);
528 }
529
530 struct servent *
531 getservbyname(const char *name, const char *proto)
532 {
533 return getserv(name, proto, 0, S_GET_NAME);
534 }
535
536 struct servent *
537 getservent(void)
538 {
539 return getserv(NULL, NULL, 0, S_GET_ENT);
540 }
541
542 void
543 setservent(int stayopen)
544 {
545 if (_lu_running()) lu_setservent();
546 else _old_setservent();
547 }
548
549 void
550 endservent(void)
551 {
552 if (_lu_running()) lu_endservent();
553 else _old_endservent();
554 }