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 /* convert to host order */
421 xdrmem_create(&outxdr
, output_buf
, sizeof(output_buf
), XDR_ENCODE
);
422 if (!xdr_int(&outxdr
, &port
) || !xdr__lu_string(&outxdr
, (_lu_string
*)&proto
))
424 xdr_destroy(&outxdr
);
431 if (_lookup_all(_lu_port
, proc
, (unit
*)output_buf
,
432 xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
)
435 xdr_destroy(&outxdr
);
439 xdr_destroy(&outxdr
);
441 datalen
*= BYTES_PER_XDR_UNIT
;
442 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
444 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
447 if (!xdr_int(&inxdr
, &count
))
450 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
457 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
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.
468 s
= extract_service(&inxdr
, proto
);
470 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
475 static struct servent
*
476 lu_getservbyname(const char *name
, const char *proto
)
479 unsigned int datalen
;
481 char output_buf
[2 * (_LU_MAXLUSTRLEN
+ BYTES_PER_XDR_UNIT
)];
483 static int proc
= -1;
488 if (_lookup_link(_lu_port
, "getservbyname", &proc
) != KERN_SUCCESS
)
494 /* Encode NULL for xmission to lookupd. */
495 if (proto
== NULL
) proto
= "";
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
))
501 xdr_destroy(&outxdr
);
508 if (_lookup_all(_lu_port
, proc
, (unit
*)output_buf
,
509 xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
)
512 xdr_destroy(&outxdr
);
516 xdr_destroy(&outxdr
);
518 datalen
*= BYTES_PER_XDR_UNIT
;
519 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
521 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
524 if (!xdr_int(&inxdr
, &count
))
527 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
534 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
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.
545 s
= extract_service(&inxdr
, proto
);
547 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
555 struct lu_thread_info
*tdata
;
557 tdata
= _lu_data_create_key(_lu_data_key_service
, free_lu_thread_info_service
);
558 _lu_data_free_vm_xdr(tdata
);
567 static struct servent
*
571 static int proc
= -1;
572 struct lu_thread_info
*tdata
;
574 tdata
= _lu_data_create_key(_lu_data_key_service
, free_lu_thread_info_service
);
577 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
578 _lu_data_set_key(_lu_data_key_service
, tdata
);
581 if (tdata
->lu_vm
== NULL
)
585 if (_lookup_link(_lu_port
, "getservent", &proc
) != KERN_SUCCESS
)
592 if (_lookup_all(_lu_port
, proc
, NULL
, 0, &(tdata
->lu_vm
), &(tdata
->lu_vm_length
)) != KERN_SUCCESS
)
598 /* mig stubs measure size in words (4 bytes) */
599 tdata
->lu_vm_length
*= 4;
601 if (tdata
->lu_xdr
!= NULL
)
603 xdr_destroy(tdata
->lu_xdr
);
606 tdata
->lu_xdr
= (XDR
*)calloc(1, sizeof(XDR
));
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
))
616 if (tdata
->lu_vm_cursor
== 0)
622 s
= extract_service(tdata
->lu_xdr
, NULL
);
629 tdata
->lu_vm_cursor
--;
634 static struct servent
*
635 getserv(const char *name
, const char *proto
, int port
, int source
)
637 struct servent
*res
= NULL
;
638 struct lu_thread_info
*tdata
;
641 tdata
= _lu_data_create_key(_lu_data_key_service
, free_lu_thread_info_service
);
644 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
645 _lu_data_set_key(_lu_data_key_service
, tdata
);
654 res
= cache_getservbyname(name
, proto
);
657 res
= cache_getservbyport(port
, proto
);
666 else if (_lu_running())
671 res
= lu_getservbyname(name
, proto
);
674 res
= lu_getservbyport(port
, proto
);
677 res
= lu_getservent();
684 pthread_mutex_lock(&_service_lock
);
688 res
= copy_service(_old_getservbyname(name
, proto
));
691 res
= copy_service(_old_getservbyport(port
, proto
));
694 res
= copy_service(_old_getservent());
698 pthread_mutex_unlock(&_service_lock
);
701 if (from_cache
== 0) cache_service(res
);
703 recycle_service(tdata
, res
);
704 return (struct servent
*)tdata
->lu_entry
;
708 getservbyport(int port
, const char *proto
)
710 return getserv(NULL
, proto
, port
, S_GET_PORT
);
714 getservbyname(const char *name
, const char *proto
)
716 return getserv(name
, proto
, 0, S_GET_NAME
);
722 return getserv(NULL
, NULL
, 0, S_GET_ENT
);
726 setservent(int stayopen
)
728 if (_lu_running()) lu_setservent();
729 else _old_setservent();
735 if (_lu_running()) lu_endservent();
736 else _old_endservent();