/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <mach/mach.h>
#include <stdio.h>
#include <string.h>
-#include "lookup.h"
#include <rpc/types.h>
#include <rpc/xdr.h>
-#include "_lu_types.h"
#include <netdb.h>
+#include <netinet/in.h>
+#include <pthread.h>
+
+#include "_lu_types.h"
+#include "lookup.h"
#include "lu_utils.h"
-#import <netinet/in.h>
+
+static pthread_mutex_t _service_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define S_GET_NAME 1
+#define S_GET_PORT 2
+#define S_GET_ENT 3
extern struct servent *_old_getservbyport();
extern struct servent *_old_getservbyname();
extern void _old_endservent();
extern void _old_setservfile();
-static lookup_state s_state = LOOKUP_CACHE;
-static struct servent global_s;
-static int global_free = 1;
-static char *s_data = NULL;
-static unsigned s_datalen;
-static int s_nentries;
-static int s_start = 1;
-static XDR s_xdr;
-
static void
-freeold(void)
+free_service_data(struct servent *s)
{
char **aliases;
- if (global_free == 1) return;
+ if (s == NULL) return;
- if (global_s.s_name != NULL) free(global_s.s_name);
- global_s.s_name = NULL;
+ if (s->s_name != NULL) free(s->s_name);
+ if (s->s_proto != NULL) free(s->s_proto);
- if (global_s.s_proto != NULL) free(global_s.s_proto);
- global_s.s_proto = NULL;
-
- aliases = global_s.s_aliases;
+ aliases = s->s_aliases;
if (aliases != NULL)
{
while (*aliases != NULL) free(*aliases++);
- free(global_s.s_aliases);
- global_s.s_aliases = NULL;
+ free(s->s_aliases);
}
+}
- global_free = 1;
+static void
+free_service(struct servent *s)
+{
+ if (s == NULL) return;
+ free_service_data(s);
+ free(s);
}
static void
-convert_s(_lu_servent *lu_s)
+free_lu_thread_info_service(void *x)
+{
+ struct lu_thread_info *tdata;
+
+ if (x == NULL) return;
+
+ tdata = (struct lu_thread_info *)x;
+
+ if (tdata->lu_entry != NULL)
+ {
+ free_service((struct servent *)tdata->lu_entry);
+ tdata->lu_entry = NULL;
+ }
+
+ _lu_data_free_vm_xdr(tdata);
+
+ free(tdata);
+}
+
+static struct servent *
+extract_service(XDR *xdr, const char *proto)
+{
+ struct servent *s;
+ int i, j, nvals, nkeys, status;
+ char *key, **vals;
+
+ if (xdr == NULL) return NULL;
+
+ if (!xdr_int(xdr, &nkeys)) return NULL;
+
+ s = (struct servent *)calloc(1, sizeof(struct servent));
+
+ for (i = 0; i < nkeys; i++)
+ {
+ key = NULL;
+ vals = NULL;
+ nvals = 0;
+
+ status = _lu_xdr_attribute(xdr, &key, &vals, &nvals);
+ if (status < 0)
+ {
+ free_service(s);
+ return NULL;
+ }
+
+ if (nvals == 0)
+ {
+ free(key);
+ continue;
+ }
+
+ j = 0;
+
+ if ((s->s_name == NULL) && (!strcmp("name", key)))
+ {
+ s->s_name = vals[0];
+ if (nvals > 1)
+ {
+ s->s_aliases = (char **)calloc(nvals, sizeof(char *));
+ for (j = 1; j < nvals; j++) s->s_aliases[j-1] = vals[j];
+ }
+ j = nvals;
+ }
+ else if ((s->s_proto == NULL) && (!strcmp("protocol", key)))
+ {
+ if ((proto == NULL) || (proto[0] == '\0'))
+ {
+ s->s_proto = vals[0];
+ j = 1;
+ }
+ else
+ {
+ s->s_proto = strdup(proto);
+ }
+ }
+ else if ((s->s_port == 0) && (!strcmp("port", key)))
+ {
+ s->s_port = htons(atoi(vals[0]));
+ }
+
+ free(key);
+ if (vals != NULL)
+ {
+ for (; j < nvals; j++) free(vals[j]);
+ free(vals);
+ }
+ }
+
+ if (s->s_name == NULL) s->s_name = strdup("");
+ if (s->s_proto == NULL) s->s_proto = strdup("");
+ if (s->s_aliases == NULL) s->s_aliases = (char **)calloc(1, sizeof(char *));
+
+ return s;
+}
+
+static struct servent *
+copy_service(struct servent *in)
{
int i, len;
+ struct servent *s;
- freeold();
+ if (in == NULL) return NULL;
- global_s.s_name = strdup(lu_s->s_names.s_names_val[0]);
+ s = (struct servent *)calloc(1, sizeof(struct servent));
- len = lu_s->s_names.s_names_len - 1;
- global_s.s_aliases = (char **)malloc((len + 1) * sizeof(char *));
+ s->s_name = LU_COPY_STRING(in->s_name);
+ len = 0;
+ if (in->s_aliases != NULL)
+ {
+ for (len = 0; in->s_aliases[len] != NULL; len++);
+ }
+
+ s->s_aliases = (char **)calloc(len + 1, sizeof(char *));
for (i = 0; i < len; i++)
{
- global_s.s_aliases[i] = strdup(lu_s->s_names.s_names_val[i+1]);
+ s->s_aliases[i] = strdup(in->s_aliases[i]);
+ }
+
+ s->s_proto = LU_COPY_STRING(in->s_proto);
+ s->s_port = in->s_port;
+
+ return s;
+}
+
+static void
+recycle_service(struct lu_thread_info *tdata, struct servent *in)
+{
+ struct servent *s;
+
+ if (tdata == NULL) return;
+ s = (struct servent *)tdata->lu_entry;
+
+ if (in == NULL)
+ {
+ free_service(s);
+ tdata->lu_entry = NULL;
+ }
+
+ if (tdata->lu_entry == NULL)
+ {
+ tdata->lu_entry = in;
+ return;
}
- global_s.s_aliases[len] = NULL;
+ free_service_data(s);
- if (lu_s->s_proto != NULL) global_s.s_proto = strdup(lu_s->s_proto);
- global_s.s_port = lu_s->s_port;
+ s->s_name = in->s_name;
+ s->s_aliases = in->s_aliases;
+ s->s_proto = in->s_proto;
+ s->s_port = in->s_port;
- global_free = 0;
+ free(in);
}
static struct servent *
lu_getservbyport(int port, const char *proto)
{
- unsigned datalen;
- _lu_servent_ptr lu_s;
- XDR xdr;
+ struct servent *s;
+ unsigned int datalen;
+ XDR outxdr, inxdr;
static int proc = -1;
char output_buf[_LU_MAXLUSTRLEN + 3 * BYTES_PER_XDR_UNIT];
- unit lookup_buf[MAX_INLINE_UNITS];
- XDR outxdr;
+ char *lookup_buf;
+ int count;
if (proc < 0)
{
if (_lookup_link(_lu_port, "getservbyport", &proc) != KERN_SUCCESS)
{
- return (NULL);
+ return NULL;
}
}
/* Encode NULL for xmission to lookupd. */
- if (!proto) proto = "";
+ if (proto == NULL) proto = "";
xdrmem_create(&outxdr, output_buf, sizeof(output_buf), XDR_ENCODE);
- if (!xdr_int(&outxdr, &port) || !xdr__lu_string(&outxdr, &proto))
+ if (!xdr_int(&outxdr, &port) || !xdr__lu_string(&outxdr, (_lu_string *)&proto))
{
xdr_destroy(&outxdr);
- return (NULL);
+ return NULL;
}
- datalen = MAX_INLINE_UNITS;
- if (_lookup_one(_lu_port, proc, (unit *)output_buf,
- xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, lookup_buf, &datalen)
+ datalen = 0;
+ lookup_buf = NULL;
+
+ if (_lookup_all(_lu_port, proc, (unit *)output_buf,
+ xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen)
!= KERN_SUCCESS)
{
xdr_destroy(&outxdr);
- return (NULL);
+ return NULL;
}
xdr_destroy(&outxdr);
datalen *= BYTES_PER_XDR_UNIT;
- xdrmem_create(&xdr, lookup_buf, datalen, XDR_DECODE);
- lu_s = NULL;
- if (!xdr__lu_servent_ptr(&xdr, &lu_s) || (lu_s == NULL))
+ if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
+
+ xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
+
+ count = 0;
+ if (!xdr_int(&inxdr, &count))
+ {
+ xdr_destroy(&inxdr);
+ vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
+ return NULL;
+ }
+
+ if (count == 0)
{
- xdr_destroy(&xdr);
- return (NULL);
+ xdr_destroy(&inxdr);
+ vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
+ return NULL;
}
- xdr_destroy(&xdr);
+ /*
+ * lookupd will only send back a reply for a service with the protocol specified
+ * if it finds a match. We pass the protocol name to extract_service, which
+ * copies the requested protocol name into the returned servent. This is a
+ * bit of a kludge, but since NetInfo / lookupd treat services as single entities
+ * with multiple protocols, we are forced to do some special-case handling.
+ */
+ s = extract_service(&inxdr, proto);
+ xdr_destroy(&inxdr);
+ vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
- convert_s(lu_s);
- xdr_free(xdr__lu_servent_ptr, &lu_s);
- return (&global_s);
+ return s;
}
static struct servent *
lu_getservbyname(const char *name, const char *proto)
{
- unsigned datalen;
- unit lookup_buf[MAX_INLINE_UNITS];
+ struct servent *s;
+ unsigned int datalen;
+ char *lookup_buf;
char output_buf[2 * (_LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT)];
- XDR outxdr;
- XDR inxdr;
- _lu_servent_ptr lu_s;
+ XDR outxdr, inxdr;
static int proc = -1;
+ int count;
if (proc < 0)
{
if (_lookup_link(_lu_port, "getservbyname", &proc) != KERN_SUCCESS)
{
- return (NULL);
+ return NULL;
}
}
/* Encode NULL for xmission to lookupd. */
- if (!proto) proto = "";
+ if (proto == NULL) proto = "";
xdrmem_create(&outxdr, output_buf, sizeof(output_buf), XDR_ENCODE);
- if (!xdr__lu_string(&outxdr, &name) || !xdr__lu_string(&outxdr, &proto))
+ if (!xdr__lu_string(&outxdr, (_lu_string *)&name) ||
+ !xdr__lu_string(&outxdr, (_lu_string *)&proto))
{
xdr_destroy(&outxdr);
- return (NULL);
+ return NULL;
}
- datalen = MAX_INLINE_UNITS;
- if (_lookup_one(_lu_port, proc, (unit *)output_buf,
- xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, lookup_buf, &datalen)
+ datalen = 0;
+ lookup_buf = NULL;
+
+ if (_lookup_all(_lu_port, proc, (unit *)output_buf,
+ xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen)
!= KERN_SUCCESS)
{
xdr_destroy(&outxdr);
- return (NULL);
+ return NULL;
}
xdr_destroy(&outxdr);
datalen *= BYTES_PER_XDR_UNIT;
- xdrmem_create(&inxdr, lookup_buf, datalen,
- XDR_DECODE);
- lu_s = NULL;
- if (!xdr__lu_servent_ptr(&inxdr, &lu_s) || (lu_s == NULL))
+ if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
+
+ xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
+
+ count = 0;
+ if (!xdr_int(&inxdr, &count))
+ {
+ xdr_destroy(&inxdr);
+ vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
+ return NULL;
+ }
+
+ if (count == 0)
{
xdr_destroy(&inxdr);
- return (NULL);
+ vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
+ return NULL;
}
+ /*
+ * lookupd will only send back a reply for a service with the protocol specified
+ * if it finds a match. We pass the protocol name to extract_service, which
+ * copies the requested protocol name into the returned servent. This is a
+ * bit of a kludge, but since NetInfo / lookupd treat services as single entities
+ * with multiple protocols, we are forced to do some special-case handling.
+ */
+ s = extract_service(&inxdr, proto);
xdr_destroy(&inxdr);
+ vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
- convert_s(lu_s);
- xdr_free(xdr__lu_servent_ptr, &lu_s);
- return (&global_s);
+ return s;
}
static void
lu_endservent()
{
- s_nentries = 0;
- if (s_data != NULL)
- {
- freeold();
- vm_deallocate(mach_task_self(), (vm_address_t)s_data, s_datalen);
- s_data = NULL;
- }
+ struct lu_thread_info *tdata;
+
+ tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
+ _lu_data_free_vm_xdr(tdata);
}
static void
lu_setservent()
{
lu_endservent();
- s_start = 1;
}
static struct servent *
lu_getservent()
{
+ struct servent *s;
static int proc = -1;
- _lu_servent lu_s;
+ struct lu_thread_info *tdata;
- if (s_start == 1)
+ tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
+ if (tdata == NULL)
+ {
+ tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
+ _lu_data_set_key(_lu_data_key_service, tdata);
+ }
+\r
+ if (tdata->lu_vm == NULL)
{
- s_start = 0;
-
if (proc < 0)
{
if (_lookup_link(_lu_port, "getservent", &proc) != KERN_SUCCESS)
{
lu_endservent();
- return (NULL);
+ return NULL;
}
}
- if (_lookup_all(_lu_port, proc, NULL, 0, &s_data, &s_datalen)
- != KERN_SUCCESS)
+ if (_lookup_all(_lu_port, proc, NULL, 0, &(tdata->lu_vm), &(tdata->lu_vm_length)) != KERN_SUCCESS)
{
lu_endservent();
- return (NULL);
+ return NULL;
}
-#ifdef NOTDEF
-/* NOTDEF because OOL buffers are counted in bytes with untyped IPC */
- s_datalen *= BYTES_PER_XDR_UNIT;
-#endif
+ /* mig stubs measure size in words (4 bytes) */
+ tdata->lu_vm_length *= 4;
+
+ if (tdata->lu_xdr != NULL)
+ {
+ xdr_destroy(tdata->lu_xdr);
+ free(tdata->lu_xdr);
+ }
+ tdata->lu_xdr = (XDR *)calloc(1, sizeof(XDR));
- xdrmem_create(&s_xdr, s_data, s_datalen,
- XDR_DECODE);
- if (!xdr_int(&s_xdr, &s_nentries))
+ xdrmem_create(tdata->lu_xdr, tdata->lu_vm, tdata->lu_vm_length, XDR_DECODE);
+ if (!xdr_int(tdata->lu_xdr, &tdata->lu_vm_cursor))
{
- xdr_destroy(&s_xdr);
lu_endservent();
- return (NULL);
+ return NULL;
}
}
- if (s_nentries == 0)
+ if (tdata->lu_vm_cursor == 0)
{
- xdr_destroy(&s_xdr);
lu_endservent();
- return (NULL);
+ return NULL;
}
- bzero(&lu_s, sizeof(lu_s));
- if (!xdr__lu_servent(&s_xdr, &lu_s))
+ s = extract_service(tdata->lu_xdr, NULL);
+ if (s == NULL)
{
- xdr_destroy(&s_xdr);
lu_endservent();
- return (NULL);
+ return NULL;
+ }
+
+ tdata->lu_vm_cursor--;
+
+ return s;
+}
+
+static struct servent *
+getserv(const char *name, const char *proto, int port, int source)
+{
+ struct servent *res = NULL;
+ struct lu_thread_info *tdata;
+
+ tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
+ if (tdata == NULL)
+ {
+ tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
+ _lu_data_set_key(_lu_data_key_service, tdata);
+ }
+
+ if (_lu_running())
+ {
+ switch (source)
+ {
+ case S_GET_NAME:
+ res = lu_getservbyname(name, proto);
+ break;
+ case S_GET_PORT:
+ res = lu_getservbyport(port, proto);
+ break;
+ case S_GET_ENT:
+ res = lu_getservent();
+ break;
+ default: res = NULL;
+ }
+ }
+ else
+ {
+ pthread_mutex_lock(&_service_lock);
+ switch (source)
+ {
+ case S_GET_NAME:
+ res = copy_service(_old_getservbyname(name, proto));
+ break;
+ case S_GET_PORT:
+ res = copy_service(_old_getservbyport(port, proto));
+ break;
+ case S_GET_ENT:
+ res = copy_service(_old_getservent());
+ break;
+ default: res = NULL;
+ }
+ pthread_mutex_unlock(&_service_lock);
}
- s_nentries--;
- convert_s(&lu_s);
- xdr_free(xdr__lu_servent, &lu_s);
- return (&global_s);
+ recycle_service(tdata, res);
+ return (struct servent *)tdata->lu_entry;
}
struct servent *
getservbyport(int port, const char *proto)
{
- LOOKUP2(lu_getservbyport, _old_getservbyport, port, proto, struct servent);
+ return getserv(NULL, proto, port, S_GET_PORT);
}
struct servent *
getservbyname(const char *name, const char *proto)
{
- LOOKUP2(lu_getservbyname, _old_getservbyname, name, proto,
- struct servent);
+ return getserv(name, proto, 0, S_GET_NAME);
}
struct servent *
getservent(void)
{
- GETENT(lu_getservent, _old_getservent, &s_state, struct servent);
+ return getserv(NULL, NULL, 0, S_GET_ENT);
}
void
setservent(int stayopen)
{
- SETSTATE(lu_setservent, _old_setservent, &s_state, stayopen);
+ if (_lu_running()) lu_setservent();
+ else _old_setservent();
}
void
endservent(void)
{
- UNSETSTATE(lu_endservent, _old_endservent, &s_state);
+ if (_lu_running()) lu_endservent();
+ else _old_endservent();
}