2 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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.
23 * @APPLE_LICENSE_HEADER_END@
26 * Services file lookup
27 * Copyright (C) 1989 by NeXT, Inc.
30 #include <mach/mach.h>
33 #include <rpc/types.h>
36 #include <netinet/in.h>
39 #include "_lu_types.h"
43 #define SERVICE_CACHE_SIZE 10
44 #define DEFAULT_SERVICE_CACHE_TTL 10
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
;
52 static pthread_mutex_t _service_lock
= PTHREAD_MUTEX_INITIALIZER
;
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();
66 free_service_data(struct servent
*s
)
70 if (s
== NULL
) return;
72 if (s
->s_name
!= NULL
) free(s
->s_name
);
73 if (s
->s_proto
!= NULL
) free(s
->s_proto
);
75 aliases
= s
->s_aliases
;
78 while (*aliases
!= NULL
) free(*aliases
++);
84 free_service(struct servent
*s
)
86 if (s
== NULL
) return;
92 free_lu_thread_info_service(void *x
)
94 struct lu_thread_info
*tdata
;
96 if (x
== NULL
) return;
98 tdata
= (struct lu_thread_info
*)x
;
100 if (tdata
->lu_entry
!= NULL
)
102 free_service((struct servent
*)tdata
->lu_entry
);
103 tdata
->lu_entry
= NULL
;
106 _lu_data_free_vm_xdr(tdata
);
111 static struct servent
*
112 extract_service(XDR
*xdr
, const char *proto
)
115 int i
, j
, nvals
, nkeys
, status
;
118 if (xdr
== NULL
) return NULL
;
120 if (!xdr_int(xdr
, &nkeys
)) return NULL
;
122 s
= (struct servent
*)calloc(1, sizeof(struct servent
));
124 for (i
= 0; i
< nkeys
; i
++)
130 status
= _lu_xdr_attribute(xdr
, &key
, &vals
, &nvals
);
145 if ((s
->s_name
== NULL
) && (!strcmp("name", key
)))
150 s
->s_aliases
= (char **)calloc(nvals
, sizeof(char *));
151 for (j
= 1; j
< nvals
; j
++) s
->s_aliases
[j
-1] = vals
[j
];
155 else if ((s
->s_proto
== NULL
) && (!strcmp("protocol", key
)))
157 if ((proto
== NULL
) || (proto
[0] == '\0'))
159 s
->s_proto
= vals
[0];
164 s
->s_proto
= strdup(proto
);
167 else if ((s
->s_port
== 0) && (!strcmp("port", key
)))
169 s
->s_port
= htons(atoi(vals
[0]));
175 for (; j
< nvals
; j
++) free(vals
[j
]);
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 *));
187 static struct servent
*
188 copy_service(struct servent
*in
)
193 if (in
== NULL
) return NULL
;
195 s
= (struct servent
*)calloc(1, sizeof(struct servent
));
197 s
->s_name
= LU_COPY_STRING(in
->s_name
);
200 if (in
->s_aliases
!= NULL
)
202 for (len
= 0; in
->s_aliases
[len
] != NULL
; len
++);
205 s
->s_aliases
= (char **)calloc(len
+ 1, sizeof(char *));
206 for (i
= 0; i
< len
; i
++)
208 s
->s_aliases
[i
] = strdup(in
->s_aliases
[i
]);
211 s
->s_proto
= LU_COPY_STRING(in
->s_proto
);
212 s
->s_port
= in
->s_port
;
218 recycle_service(struct lu_thread_info
*tdata
, struct servent
*in
)
222 if (tdata
== NULL
) return;
223 s
= (struct servent
*)tdata
->lu_entry
;
228 tdata
->lu_entry
= NULL
;
231 if (tdata
->lu_entry
== NULL
)
233 tdata
->lu_entry
= in
;
237 free_service_data(s
);
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
;
247 __private_extern__
unsigned int
248 get_service_cache_ttl()
250 return _service_cache_ttl
;
253 __private_extern__
void
254 set_service_cache_ttl(unsigned int ttl
)
258 pthread_mutex_lock(&_service_cache_lock
);
260 _service_cache_ttl
= ttl
;
264 for (i
= 0; i
< SERVICE_CACHE_SIZE
; i
++)
266 if (_service_cache
[i
] == NULL
) continue;
268 free_service((struct servent
*)_service_cache
[i
]);
269 _service_cache
[i
] = NULL
;
270 _service_cache_best_before
[i
] = 0;
274 pthread_mutex_unlock(&_service_cache_lock
);
278 cache_service(struct servent
*s
)
281 struct servent
*scache
;
283 if (_service_cache_ttl
== 0) return;
284 if (s
== NULL
) return;
286 pthread_mutex_lock(&_service_cache_lock
);
288 scache
= copy_service(s
);
290 gettimeofday(&now
, NULL
);
292 if (_service_cache
[_service_cache_index
] != NULL
)
293 free_service((struct servent
*)_service_cache
[_service_cache_index
]);
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
;
299 pthread_mutex_unlock(&_service_cache_lock
);
302 static struct servent
*
303 cache_getservbyname(const char *name
, const char *proto
)
306 struct servent
*s
, *res
;
310 if (_service_cache_ttl
== 0) return NULL
;
311 if (name
== NULL
) return NULL
;
313 pthread_mutex_lock(&_service_cache_lock
);
315 gettimeofday(&now
, NULL
);
317 for (i
= 0; i
< SERVICE_CACHE_SIZE
; i
++)
319 if (_service_cache_best_before
[i
] == 0) continue;
320 if ((unsigned int)now
.tv_sec
> _service_cache_best_before
[i
]) continue;
322 s
= (struct servent
*)_service_cache
[i
];
324 if (s
->s_name
!= NULL
)
326 if (!strcmp(name
, s
->s_name
))
328 if ((proto
== NULL
) || ((s
->s_proto
!= NULL
) && (!strcmp(proto
, s
->s_proto
))))
330 res
= copy_service(s
);
331 pthread_mutex_unlock(&_service_cache_lock
);
337 aliases
= s
->s_aliases
;
340 pthread_mutex_unlock(&_service_cache_lock
);
344 for (; *aliases
!= NULL
; *aliases
++)
346 if (!strcmp(name
, *aliases
))
348 if ((proto
== NULL
) || ((s
->s_proto
!= NULL
) && (!strcmp(proto
, s
->s_proto
))))
350 res
= copy_service(s
);
351 pthread_mutex_unlock(&_service_cache_lock
);
358 pthread_mutex_unlock(&_service_cache_lock
);
362 static struct servent
*
363 cache_getservbyport(int port
, const char *proto
)
366 struct servent
*s
, *res
;
369 if (_service_cache_ttl
== 0) return NULL
;
371 pthread_mutex_lock(&_service_cache_lock
);
373 gettimeofday(&now
, NULL
);
375 for (i
= 0; i
< SERVICE_CACHE_SIZE
; i
++)
377 if (_service_cache_best_before
[i
] == 0) continue;
378 if ((unsigned int)now
.tv_sec
> _service_cache_best_before
[i
]) continue;
380 s
= (struct servent
*)_service_cache
[i
];
382 if (port
== s
->s_port
)
384 if ((proto
== NULL
) || ((s
->s_proto
!= NULL
) && (!strcmp(proto
, s
->s_proto
))))
386 res
= copy_service(s
);
387 pthread_mutex_unlock(&_service_cache_lock
);
393 pthread_mutex_unlock(&_service_cache_lock
);
397 static struct servent
*
398 lu_getservbyport(int port
, const char *proto
)
401 unsigned int datalen
;
403 static int proc
= -1;
404 char output_buf
[_LU_MAXLUSTRLEN
+ 3 * BYTES_PER_XDR_UNIT
];
410 if (_lookup_link(_lu_port
, "getservbyport", &proc
) != KERN_SUCCESS
)
416 /* Encode NULL for xmission to lookupd. */
417 if (proto
== NULL
) proto
= "";
419 xdrmem_create(&outxdr
, output_buf
, sizeof(output_buf
), XDR_ENCODE
);
420 if (!xdr_int(&outxdr
, &port
) || !xdr__lu_string(&outxdr
, (_lu_string
*)&proto
))
422 xdr_destroy(&outxdr
);
429 if (_lookup_all(_lu_port
, proc
, (unit
*)output_buf
,
430 xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
)
433 xdr_destroy(&outxdr
);
437 xdr_destroy(&outxdr
);
439 datalen
*= BYTES_PER_XDR_UNIT
;
440 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
442 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
445 if (!xdr_int(&inxdr
, &count
))
448 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
455 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
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.
466 s
= extract_service(&inxdr
, proto
);
468 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
473 static struct servent
*
474 lu_getservbyname(const char *name
, const char *proto
)
477 unsigned int datalen
;
479 char output_buf
[2 * (_LU_MAXLUSTRLEN
+ BYTES_PER_XDR_UNIT
)];
481 static int proc
= -1;
486 if (_lookup_link(_lu_port
, "getservbyname", &proc
) != KERN_SUCCESS
)
492 /* Encode NULL for xmission to lookupd. */
493 if (proto
== NULL
) proto
= "";
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
))
499 xdr_destroy(&outxdr
);
506 if (_lookup_all(_lu_port
, proc
, (unit
*)output_buf
,
507 xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
)
510 xdr_destroy(&outxdr
);
514 xdr_destroy(&outxdr
);
516 datalen
*= BYTES_PER_XDR_UNIT
;
517 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
519 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
522 if (!xdr_int(&inxdr
, &count
))
525 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
532 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
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.
543 s
= extract_service(&inxdr
, proto
);
545 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
553 struct lu_thread_info
*tdata
;
555 tdata
= _lu_data_create_key(_lu_data_key_service
, free_lu_thread_info_service
);
556 _lu_data_free_vm_xdr(tdata
);
565 static struct servent
*
569 static int proc
= -1;
570 struct lu_thread_info
*tdata
;
572 tdata
= _lu_data_create_key(_lu_data_key_service
, free_lu_thread_info_service
);
575 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
576 _lu_data_set_key(_lu_data_key_service
, tdata
);
579 if (tdata
->lu_vm
== NULL
)
583 if (_lookup_link(_lu_port
, "getservent", &proc
) != KERN_SUCCESS
)
590 if (_lookup_all(_lu_port
, proc
, NULL
, 0, &(tdata
->lu_vm
), &(tdata
->lu_vm_length
)) != KERN_SUCCESS
)
596 /* mig stubs measure size in words (4 bytes) */
597 tdata
->lu_vm_length
*= 4;
599 if (tdata
->lu_xdr
!= NULL
)
601 xdr_destroy(tdata
->lu_xdr
);
604 tdata
->lu_xdr
= (XDR
*)calloc(1, sizeof(XDR
));
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
))
614 if (tdata
->lu_vm_cursor
== 0)
620 s
= extract_service(tdata
->lu_xdr
, NULL
);
627 tdata
->lu_vm_cursor
--;
632 static struct servent
*
633 getserv(const char *name
, const char *proto
, int port
, int source
)
635 struct servent
*res
= NULL
;
636 struct lu_thread_info
*tdata
;
639 tdata
= _lu_data_create_key(_lu_data_key_service
, free_lu_thread_info_service
);
642 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
643 _lu_data_set_key(_lu_data_key_service
, tdata
);
652 res
= cache_getservbyname(name
, proto
);
655 res
= cache_getservbyport(port
, proto
);
664 else if (_lu_running())
669 res
= lu_getservbyname(name
, proto
);
672 res
= lu_getservbyport(port
, proto
);
675 res
= lu_getservent();
682 pthread_mutex_lock(&_service_lock
);
686 res
= copy_service(_old_getservbyname(name
, proto
));
689 res
= copy_service(_old_getservbyport(port
, proto
));
692 res
= copy_service(_old_getservent());
696 pthread_mutex_unlock(&_service_lock
);
699 if (from_cache
== 0) cache_service(res
);
701 recycle_service(tdata
, res
);
702 return (struct servent
*)tdata
->lu_entry
;
706 getservbyport(int port
, const char *proto
)
708 return getserv(NULL
, proto
, port
, S_GET_PORT
);
712 getservbyname(const char *name
, const char *proto
)
714 return getserv(name
, proto
, 0, S_GET_NAME
);
720 return getserv(NULL
, NULL
, 0, S_GET_ENT
);
724 setservent(int stayopen
)
726 if (_lu_running()) lu_setservent();
727 else _old_setservent();
733 if (_lu_running()) lu_endservent();
734 else _old_endservent();