2 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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
22 * @APPLE_LICENSE_HEADER_END@
25 * Services file lookup
26 * Copyright (C) 1989 by NeXT, Inc.
29 #include <mach/mach.h>
32 #include <rpc/types.h>
35 #include <netinet/in.h>
38 #include "_lu_types.h"
42 #define SERVICE_CACHE_SIZE 10
43 #define DEFAULT_SERVICE_CACHE_TTL 10
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
;
51 static pthread_mutex_t _service_lock
= PTHREAD_MUTEX_INITIALIZER
;
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();
65 free_service_data(struct servent
*s
)
69 if (s
== NULL
) return;
71 if (s
->s_name
!= NULL
) free(s
->s_name
);
72 if (s
->s_proto
!= NULL
) free(s
->s_proto
);
74 aliases
= s
->s_aliases
;
77 while (*aliases
!= NULL
) free(*aliases
++);
83 free_service(struct servent
*s
)
85 if (s
== NULL
) return;
91 free_lu_thread_info_service(void *x
)
93 struct lu_thread_info
*tdata
;
95 if (x
== NULL
) return;
97 tdata
= (struct lu_thread_info
*)x
;
99 if (tdata
->lu_entry
!= NULL
)
101 free_service((struct servent
*)tdata
->lu_entry
);
102 tdata
->lu_entry
= NULL
;
105 _lu_data_free_vm_xdr(tdata
);
110 static struct servent
*
111 extract_service(XDR
*xdr
, const char *proto
)
114 int i
, j
, nvals
, nkeys
, status
;
117 if (xdr
== NULL
) return NULL
;
119 if (!xdr_int(xdr
, &nkeys
)) return NULL
;
121 s
= (struct servent
*)calloc(1, sizeof(struct servent
));
123 for (i
= 0; i
< nkeys
; i
++)
129 status
= _lu_xdr_attribute(xdr
, &key
, &vals
, &nvals
);
144 if ((s
->s_name
== NULL
) && (!strcmp("name", key
)))
149 s
->s_aliases
= (char **)calloc(nvals
, sizeof(char *));
150 for (j
= 1; j
< nvals
; j
++) s
->s_aliases
[j
-1] = vals
[j
];
154 else if ((s
->s_proto
== NULL
) && (!strcmp("protocol", key
)))
156 if ((proto
== NULL
) || (proto
[0] == '\0'))
158 s
->s_proto
= vals
[0];
163 s
->s_proto
= strdup(proto
);
166 else if ((s
->s_port
== 0) && (!strcmp("port", key
)))
168 s
->s_port
= htons(atoi(vals
[0]));
174 for (; j
< nvals
; j
++) free(vals
[j
]);
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 *));
186 static struct servent
*
187 copy_service(struct servent
*in
)
192 if (in
== NULL
) return NULL
;
194 s
= (struct servent
*)calloc(1, sizeof(struct servent
));
196 s
->s_name
= LU_COPY_STRING(in
->s_name
);
199 if (in
->s_aliases
!= NULL
)
201 for (len
= 0; in
->s_aliases
[len
] != NULL
; len
++);
204 s
->s_aliases
= (char **)calloc(len
+ 1, sizeof(char *));
205 for (i
= 0; i
< len
; i
++)
207 s
->s_aliases
[i
] = strdup(in
->s_aliases
[i
]);
210 s
->s_proto
= LU_COPY_STRING(in
->s_proto
);
211 s
->s_port
= in
->s_port
;
217 recycle_service(struct lu_thread_info
*tdata
, struct servent
*in
)
221 if (tdata
== NULL
) return;
222 s
= (struct servent
*)tdata
->lu_entry
;
227 tdata
->lu_entry
= NULL
;
230 if (tdata
->lu_entry
== NULL
)
232 tdata
->lu_entry
= in
;
236 free_service_data(s
);
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
;
246 __private_extern__
unsigned int
247 get_service_cache_ttl()
249 return _service_cache_ttl
;
252 __private_extern__
void
253 set_service_cache_ttl(unsigned int ttl
)
257 pthread_mutex_lock(&_service_cache_lock
);
259 _service_cache_ttl
= ttl
;
263 for (i
= 0; i
< SERVICE_CACHE_SIZE
; i
++)
265 if (_service_cache
[i
] == NULL
) continue;
267 free_service((struct servent
*)_service_cache
[i
]);
268 _service_cache
[i
] = NULL
;
269 _service_cache_best_before
[i
] = 0;
273 pthread_mutex_unlock(&_service_cache_lock
);
277 cache_service(struct servent
*s
)
280 struct servent
*scache
;
282 if (_service_cache_ttl
== 0) return;
283 if (s
== NULL
) return;
285 pthread_mutex_lock(&_service_cache_lock
);
287 scache
= copy_service(s
);
289 gettimeofday(&now
, NULL
);
291 if (_service_cache
[_service_cache_index
] != NULL
)
292 free_service((struct servent
*)_service_cache
[_service_cache_index
]);
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
;
298 pthread_mutex_unlock(&_service_cache_lock
);
301 static struct servent
*
302 cache_getservbyname(const char *name
, const char *proto
)
305 struct servent
*s
, *res
;
309 if (_service_cache_ttl
== 0) return NULL
;
310 if (name
== NULL
) return NULL
;
312 pthread_mutex_lock(&_service_cache_lock
);
314 gettimeofday(&now
, NULL
);
316 for (i
= 0; i
< SERVICE_CACHE_SIZE
; i
++)
318 if (_service_cache_best_before
[i
] == 0) continue;
319 if ((unsigned int)now
.tv_sec
> _service_cache_best_before
[i
]) continue;
321 s
= (struct servent
*)_service_cache
[i
];
323 if (s
->s_name
!= NULL
)
325 if (!strcmp(name
, s
->s_name
))
327 if ((proto
== NULL
) || ((s
->s_proto
!= NULL
) && (!strcmp(proto
, s
->s_proto
))))
329 res
= copy_service(s
);
330 pthread_mutex_unlock(&_service_cache_lock
);
336 aliases
= s
->s_aliases
;
339 pthread_mutex_unlock(&_service_cache_lock
);
343 for (; *aliases
!= NULL
; *aliases
++)
345 if (!strcmp(name
, *aliases
))
347 if ((proto
== NULL
) || ((s
->s_proto
!= NULL
) && (!strcmp(proto
, s
->s_proto
))))
349 res
= copy_service(s
);
350 pthread_mutex_unlock(&_service_cache_lock
);
357 pthread_mutex_unlock(&_service_cache_lock
);
361 static struct servent
*
362 cache_getservbyport(int port
, const char *proto
)
365 struct servent
*s
, *res
;
368 if (_service_cache_ttl
== 0) return NULL
;
370 pthread_mutex_lock(&_service_cache_lock
);
372 gettimeofday(&now
, NULL
);
374 for (i
= 0; i
< SERVICE_CACHE_SIZE
; i
++)
376 if (_service_cache_best_before
[i
] == 0) continue;
377 if ((unsigned int)now
.tv_sec
> _service_cache_best_before
[i
]) continue;
379 s
= (struct servent
*)_service_cache
[i
];
381 if (port
== s
->s_port
)
383 if ((proto
== NULL
) || ((s
->s_proto
!= NULL
) && (!strcmp(proto
, s
->s_proto
))))
385 res
= copy_service(s
);
386 pthread_mutex_unlock(&_service_cache_lock
);
392 pthread_mutex_unlock(&_service_cache_lock
);
396 static struct servent
*
397 lu_getservbyport(int port
, const char *proto
)
400 unsigned int datalen
;
402 static int proc
= -1;
403 char output_buf
[_LU_MAXLUSTRLEN
+ 3 * BYTES_PER_XDR_UNIT
];
409 if (_lookup_link(_lu_port
, "getservbyport", &proc
) != KERN_SUCCESS
)
415 /* Encode NULL for xmission to lookupd. */
416 if (proto
== NULL
) proto
= "";
418 xdrmem_create(&outxdr
, output_buf
, sizeof(output_buf
), XDR_ENCODE
);
419 if (!xdr_int(&outxdr
, &port
) || !xdr__lu_string(&outxdr
, (_lu_string
*)&proto
))
421 xdr_destroy(&outxdr
);
428 if (_lookup_all(_lu_port
, proc
, (unit
*)output_buf
,
429 xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
)
432 xdr_destroy(&outxdr
);
436 xdr_destroy(&outxdr
);
438 datalen
*= BYTES_PER_XDR_UNIT
;
439 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
441 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
444 if (!xdr_int(&inxdr
, &count
))
447 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
454 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
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.
465 s
= extract_service(&inxdr
, proto
);
467 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
472 static struct servent
*
473 lu_getservbyname(const char *name
, const char *proto
)
476 unsigned int datalen
;
478 char output_buf
[2 * (_LU_MAXLUSTRLEN
+ BYTES_PER_XDR_UNIT
)];
480 static int proc
= -1;
485 if (_lookup_link(_lu_port
, "getservbyname", &proc
) != KERN_SUCCESS
)
491 /* Encode NULL for xmission to lookupd. */
492 if (proto
== NULL
) proto
= "";
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
))
498 xdr_destroy(&outxdr
);
505 if (_lookup_all(_lu_port
, proc
, (unit
*)output_buf
,
506 xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
)
509 xdr_destroy(&outxdr
);
513 xdr_destroy(&outxdr
);
515 datalen
*= BYTES_PER_XDR_UNIT
;
516 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
518 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
521 if (!xdr_int(&inxdr
, &count
))
524 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
531 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
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.
542 s
= extract_service(&inxdr
, proto
);
544 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
552 struct lu_thread_info
*tdata
;
554 tdata
= _lu_data_create_key(_lu_data_key_service
, free_lu_thread_info_service
);
555 _lu_data_free_vm_xdr(tdata
);
564 static struct servent
*
568 static int proc
= -1;
569 struct lu_thread_info
*tdata
;
571 tdata
= _lu_data_create_key(_lu_data_key_service
, free_lu_thread_info_service
);
574 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
575 _lu_data_set_key(_lu_data_key_service
, tdata
);
578 if (tdata
->lu_vm
== NULL
)
582 if (_lookup_link(_lu_port
, "getservent", &proc
) != KERN_SUCCESS
)
589 if (_lookup_all(_lu_port
, proc
, NULL
, 0, &(tdata
->lu_vm
), &(tdata
->lu_vm_length
)) != KERN_SUCCESS
)
595 /* mig stubs measure size in words (4 bytes) */
596 tdata
->lu_vm_length
*= 4;
598 if (tdata
->lu_xdr
!= NULL
)
600 xdr_destroy(tdata
->lu_xdr
);
603 tdata
->lu_xdr
= (XDR
*)calloc(1, sizeof(XDR
));
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
))
613 if (tdata
->lu_vm_cursor
== 0)
619 s
= extract_service(tdata
->lu_xdr
, NULL
);
626 tdata
->lu_vm_cursor
--;
631 static struct servent
*
632 getserv(const char *name
, const char *proto
, int port
, int source
)
634 struct servent
*res
= NULL
;
635 struct lu_thread_info
*tdata
;
638 tdata
= _lu_data_create_key(_lu_data_key_service
, free_lu_thread_info_service
);
641 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
642 _lu_data_set_key(_lu_data_key_service
, tdata
);
651 res
= cache_getservbyname(name
, proto
);
654 res
= cache_getservbyport(port
, proto
);
663 else if (_lu_running())
668 res
= lu_getservbyname(name
, proto
);
671 res
= lu_getservbyport(port
, proto
);
674 res
= lu_getservent();
681 pthread_mutex_lock(&_service_lock
);
685 res
= copy_service(_old_getservbyname(name
, proto
));
688 res
= copy_service(_old_getservbyport(port
, proto
));
691 res
= copy_service(_old_getservent());
695 pthread_mutex_unlock(&_service_lock
);
698 if (from_cache
== 0) cache_service(res
);
700 recycle_service(tdata
, res
);
701 return (struct servent
*)tdata
->lu_entry
;
705 getservbyport(int port
, const char *proto
)
707 return getserv(NULL
, proto
, port
, S_GET_PORT
);
711 getservbyname(const char *name
, const char *proto
)
713 return getserv(name
, proto
, 0, S_GET_NAME
);
719 return getserv(NULL
, NULL
, 0, S_GET_ENT
);
723 setservent(int stayopen
)
725 if (_lu_running()) lu_setservent();
726 else _old_setservent();
732 if (_lu_running()) lu_endservent();
733 else _old_endservent();