diff options
Diffstat (limited to 'lib/libc/net')
-rw-r--r-- | lib/libc/net/Makefile.inc | 7 | ||||
-rw-r--r-- | lib/libc/net/getaddrinfo.c | 197 | ||||
-rw-r--r-- | lib/libc/net/gethostnamadr.c | 296 | ||||
-rw-r--r-- | lib/libc/net/getnetnamadr.c | 241 | ||||
-rw-r--r-- | lib/libc/net/getproto.c | 90 | ||||
-rw-r--r-- | lib/libc/net/getprotoent.c | 339 | ||||
-rw-r--r-- | lib/libc/net/getprotoname.c | 88 | ||||
-rw-r--r-- | lib/libc/net/getservbyname.c | 101 | ||||
-rw-r--r-- | lib/libc/net/getservbyport.c | 95 | ||||
-rw-r--r-- | lib/libc/net/getservent.c | 1308 | ||||
-rw-r--r-- | lib/libc/net/name6.c | 259 | ||||
-rw-r--r-- | lib/libc/net/netdb_private.h | 27 | ||||
-rw-r--r-- | lib/libc/net/nscache.c | 438 | ||||
-rw-r--r-- | lib/libc/net/nscachedcli.c | 576 | ||||
-rw-r--r-- | lib/libc/net/nsdispatch.c | 95 |
15 files changed, 3635 insertions, 522 deletions
diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index 3c363cd..9921f8d 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -9,13 +9,16 @@ SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c eui64.c \ gethostbydns.c gethostbyht.c gethostbynis.c gethostnamadr.c \ getifaddrs.c getifmaddrs.c getnameinfo.c \ getnetbydns.c getnetbyht.c getnetbynis.c getnetnamadr.c \ - getproto.c getprotoent.c getprotoname.c getservbyname.c \ - getservbyport.c getservent.c \ + getproto.c getprotoent.c getprotoname.c getservent.c \ if_indextoname.c if_nameindex.c if_nametoindex.c \ ip6opt.c linkaddr.c map_v4v6.c name6.c \ nsdispatch.c nslexer.c nsparser.c nss_compat.c \ rcmd.c rcmdsh.c recv.c rthdr.c send.c sockatmark.c vars.c +.if ${MK_NS_CACHING} != "no" +SRCS+= nscache.c nscachedcli.c +.endif + # for binary backward compatibility against FreeBSD 6.X and earlier SRCS+= res_mkupdate.c res_update.c diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c index a2552a9..b422c4b 100644 --- a/lib/libc/net/getaddrinfo.c +++ b/lib/libc/net/getaddrinfo.c @@ -103,6 +103,9 @@ __FBSDID("$FreeBSD$"); #include <nsswitch.h> #include "un-namespace.h" #include "libc_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif #if defined(__KAME__) && defined(INET6) # define FAITH @@ -279,6 +282,11 @@ static int _files_getaddrinfo(void *, void *, va_list); static struct addrinfo *_yphostent(char *, const struct addrinfo *); static int _yp_getaddrinfo(void *, void *, va_list); #endif +#ifdef NS_CACHING +static int addrinfo_id_func(char *, size_t *, va_list, void *); +static int addrinfo_marshal_func(char *, size_t *, void *, va_list, void *); +static int addrinfo_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif static int res_queryN(const char *, struct res_target *, res_state); static int res_searchN(const char *, struct res_target *, res_state); @@ -1525,6 +1533,185 @@ ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid) } #endif + +#ifdef NS_CACHING +static int +addrinfo_id_func(char *buffer, size_t *buffer_size, va_list ap, + void *cache_mdata) +{ + res_state statp; + u_long res_options; + + const int op_id = 0; /* identifies the getaddrinfo for the cache */ + char *hostname; + struct addrinfo *hints; + + char *p; + int ai_flags, ai_family, ai_socktype, ai_protocol; + size_t desired_size, size; + + statp = __res_state(); + res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | + RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); + + hostname = va_arg(ap, char *); + hints = va_arg(ap, struct addrinfo *); + + desired_size = sizeof(res_options) + sizeof(int) + sizeof(int) * 4; + if (hostname != NULL) { + size = strlen(hostname); + desired_size += size + 1; + } else + size = 0; + + if (desired_size > *buffer_size) { + *buffer_size = desired_size; + return (NS_RETURN); + } + + if (hints == NULL) + ai_flags = ai_family = ai_socktype = ai_protocol = 0; + else { + ai_flags = hints->ai_flags; + ai_family = hints->ai_family; + ai_socktype = hints->ai_socktype; + ai_protocol = hints->ai_protocol; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_flags, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_family, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_socktype, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_protocol, sizeof(int)); + p += sizeof(int); + + if (hostname != NULL) + memcpy(p, hostname, size); + + *buffer_size = desired_size; + return (NS_SUCCESS); +} + +static int +addrinfo_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct addrinfo *ai, *cai; + char *p; + size_t desired_size, size, ai_size; + + ai = *((struct addrinfo **)retval); + + desired_size = sizeof(size_t); + ai_size = 0; + for (cai = ai; cai != NULL; cai = cai->ai_next) { + desired_size += sizeof(struct addrinfo) + cai->ai_addrlen; + if (cai->ai_canonname != NULL) + desired_size += sizeof(size_t) + + strlen(cai->ai_canonname); + ++ai_size; + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + errno = ERANGE; + *buffer_size = desired_size; + return (NS_RETURN); + } + + memset(buffer, 0, desired_size); + p = buffer; + + memcpy(p, &ai_size, sizeof(size_t)); + p += sizeof(size_t); + for (cai = ai; cai != NULL; cai = cai->ai_next) { + memcpy(p, cai, sizeof(struct addrinfo)); + p += sizeof(struct addrinfo); + + memcpy(p, cai->ai_addr, cai->ai_addrlen); + p += cai->ai_addrlen; + + if (cai->ai_canonname != NULL) { + size = strlen(cai->ai_canonname); + memcpy(p, &size, sizeof(size_t)); + p += sizeof(size_t); + + memcpy(p, cai->ai_canonname, size); + p += size; + } + } + + return (NS_SUCCESS); +} + +static int +addrinfo_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct addrinfo new_ai, *result, *sentinel, *lasts; + + char *p; + size_t ai_size, ai_i, size; + + p = buffer; + memcpy(&ai_size, p, sizeof(size_t)); + p += sizeof(size_t); + + result = NULL; + lasts = NULL; + for (ai_i = 0; ai_i < ai_size; ++ai_i) { + memcpy(&new_ai, p, sizeof(struct addrinfo)); + p += sizeof(struct addrinfo); + size = new_ai.ai_addrlen + sizeof(struct addrinfo) + + _ALIGNBYTES; + + sentinel = (struct addrinfo *)malloc(size); + memset(sentinel, 0, size); + + memcpy(sentinel, &new_ai, sizeof(struct addrinfo)); + sentinel->ai_addr = (struct sockaddr *)_ALIGN((char *)sentinel + + sizeof(struct addrinfo)); + + memcpy(sentinel->ai_addr, p, new_ai.ai_addrlen); + p += new_ai.ai_addrlen; + + if (new_ai.ai_canonname != NULL) { + memcpy(&size, p, sizeof(size_t)); + p += sizeof(size_t); + + sentinel->ai_canonname = (char *)malloc(size + 1); + memset(sentinel->ai_canonname, 0, size + 1); + + memcpy(sentinel->ai_canonname, p, size); + p += size; + } + + if (result == NULL) { + result = sentinel; + lasts = sentinel; + } else { + lasts->ai_next = sentinel; + lasts = sentinel; + } + } + + *((struct addrinfo **)retval) = result; + return (NS_SUCCESS); +} +#endif /* NS_CACHING */ + /* * FQDN hostname, DNS lookup */ @@ -1535,10 +1722,20 @@ explore_fqdn(const struct addrinfo *pai, const char *hostname, struct addrinfo *result; struct addrinfo *cur; int error = 0; + +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, NULL, addrinfo_id_func, addrinfo_marshal_func, + addrinfo_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_files_getaddrinfo, NULL) { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ NS_NIS_CB(_yp_getaddrinfo, NULL) +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; diff --git a/lib/libc/net/gethostnamadr.c b/lib/libc/net/gethostnamadr.c index c8867e5..965af66 100644 --- a/lib/libc/net/gethostnamadr.c +++ b/lib/libc/net/gethostnamadr.c @@ -44,6 +44,9 @@ __FBSDID("$FreeBSD$"); #include <resolv.h> /* XXX hack for _res */ #include "un-namespace.h" #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif extern int _ht_gethostbyname(void *, void *, va_list); extern int _dns_gethostbyname(void *, void *, va_list); @@ -61,6 +64,11 @@ static const ns_src default_src[] = { { NSSRC_DNS, NS_SUCCESS }, { 0 } }; +#ifdef NS_CACHING +static int host_id_func(char *, size_t *, va_list, void *); +static int host_marshal_func(char *, size_t *, void *, va_list, void *); +static int host_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif NETDB_THREAD_ALLOC(hostent) NETDB_THREAD_ALLOC(hostent_data) @@ -152,9 +160,277 @@ __copy_hostent(struct hostent *he, struct hostent *hptr, char *buf, return (0); } +#ifdef NS_CACHING +static int +host_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) +{ + res_state statp; + u_long res_options; + + const int op_id = 1; + char *str; + int len, type; + + size_t desired_size, size; + enum nss_lookup_type lookup_type; + char *p; + int res = NS_UNAVAIL; + + statp = __res_state(); + res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | + RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + str = va_arg(ap, char *); + type = va_arg(ap, int); + + size = strlen(str); + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) + size + 1; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(int); + + memcpy(p, &type, sizeof(int)); + p += sizeof(int); + + memcpy(p, str, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + str = va_arg(ap, char *); + len = va_arg(ap, int); + type = va_arg(ap, int); + + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) * 2 + len; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(int); + + memcpy(p, &type, sizeof(int)); + p += sizeof(int); + + memcpy(p, &len, sizeof(int)); + p += sizeof(int); + + memcpy(p, str, len); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +static int +host_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *str; + int len, type; + struct hostent *ht; + + struct hostent new_ht; + size_t desired_size, aliases_size, addr_size, size; + char *p, **iter; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + str = va_arg(ap, char *); + type = va_arg(ap, int); + break; + case nss_lt_id: + str = va_arg(ap, char *); + len = va_arg(ap, int); + type = va_arg(ap, int); + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + ht = va_arg(ap, struct hostent *); + + desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *); + if (ht->h_name != NULL) + desired_size += strlen(ht->h_name) + 1; + + if (ht->h_aliases != NULL) { + aliases_size = 0; + for (iter = ht->h_aliases; *iter; ++iter) { + desired_size += strlen(*iter) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + + (aliases_size + 1) * sizeof(char *); + } + + if (ht->h_addr_list != NULL) { + addr_size = 0; + for (iter = ht->h_addr_list; *iter; ++iter) + ++addr_size; + + desired_size += addr_size * _ALIGN(ht->h_length); + desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *); + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_ht, ht, sizeof(struct hostent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct hostent) + sizeof(char *); + memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_ht.h_name != NULL) { + size = strlen(new_ht.h_name); + memcpy(p, new_ht.h_name, size); + new_ht.h_name = p; + p += size + 1; + } + + if (new_ht.h_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size); + new_ht.h_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (iter = new_ht.h_aliases; *iter; ++iter) { + size = strlen(*iter); + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + + if (new_ht.h_addr_list != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size); + new_ht.h_addr_list = (char **)p; + p += sizeof(char *) * (addr_size + 1); + + size = _ALIGN(new_ht.h_length); + for (iter = new_ht.h_addr_list; *iter; ++iter) { + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + memcpy(buffer, &new_ht, sizeof(struct hostent)); + return (NS_SUCCESS); +} + +static int +host_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *str; + int len, type; + struct hostent *ht; + + char *p; + char **iter; + char *orig_buf; + size_t orig_buf_size; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + str = va_arg(ap, char *); + type = va_arg(ap, int); + break; + case nss_lt_id: + str = va_arg(ap, char *); + len = va_arg(ap, int); + type = va_arg(ap, int); + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + ht = va_arg(ap, struct hostent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + if (orig_buf_size < + buffer_size - sizeof(struct hostent) - sizeof(char *)) { + errno = ERANGE; + return (NS_RETURN); + } + + memcpy(ht, buffer, sizeof(struct hostent)); + memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct hostent) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct hostent) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *); + if (ht->h_aliases != NULL) { + NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **); + + for (iter = ht->h_aliases; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + if (ht->h_addr_list != NULL) { + NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **); + + for (iter = ht->h_addr_list; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + *((struct hostent **)retval) = ht; + return (NS_SUCCESS); +} +#endif /* NS_CACHING */ + static int fakeaddr(const char *name, int af, struct hostent *hp, char *buf, -size_t buflen, res_state statp) + size_t buflen, res_state statp) { struct hostent_data *hed; struct hostent he; @@ -248,10 +524,19 @@ gethostbyname_internal(const char *name, int af, struct hostent *hp, char *buf, int rval, ret_errno; char abuf[MAXDNAME]; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_name, + host_id_func, host_marshal_func, host_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_gethostbyname, NULL) { NSSRC_DNS, _dns_gethostbyname, NULL }, NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; @@ -297,10 +582,19 @@ gethostbyaddr_r(const char *addr, int len, int af, struct hostent *hp, int rval, ret_errno; res_state statp; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_id, + host_id_func, host_marshal_func, host_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_gethostbyaddr, NULL) { NSSRC_DNS, _dns_gethostbyaddr, NULL }, NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; diff --git a/lib/libc/net/getnetnamadr.c b/lib/libc/net/getnetnamadr.c index 633cb30..ec7e94b 100644 --- a/lib/libc/net/getnetnamadr.c +++ b/lib/libc/net/getnetnamadr.c @@ -42,6 +42,9 @@ __FBSDID("$FreeBSD$"); #include <nsswitch.h> #include "un-namespace.h" #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif extern int _ht_getnetbyname(void *, void *, va_list); extern int _dns_getnetbyname(void *, void *, va_list); @@ -60,6 +63,220 @@ static const ns_src default_src[] = { NETDB_THREAD_ALLOC(netent_data) NETDB_THREAD_ALLOC(netdata) +#ifdef NS_CACHING +static int +net_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) +{ + char *name; + uint32_t net; + int type; + + size_t desired_size, size; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + net = va_arg(ap, uint32_t); + type = va_arg(ap, int); + + desired_size = sizeof(enum nss_lookup_type) + + sizeof(uint32_t) + sizeof(int); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &net, + sizeof(uint32_t)); + memcpy(buffer + sizeof(enum nss_lookup_type) + sizeof(uint32_t), + &type, sizeof(int)); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + + +static int +net_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + uint32_t net; + int type; + struct netent *ne; + char *orig_buf; + size_t orig_buf_size; + + struct netent new_ne; + size_t desired_size, size, aliases_size; + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + net = va_arg(ap, uint32_t); + type = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + ne = va_arg(ap, struct netent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct netent) + sizeof(char *); + if (ne->n_name != NULL) + desired_size += strlen(ne->n_name) + 1; + + if (ne->n_aliases != NULL) { + aliases_size = 0; + for (alias = ne->n_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + + (aliases_size + 1) * sizeof(char *); + } + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_ne, ne, sizeof(struct netent)); + + *buffer_size = desired_size; + memset(buffer, 0, desired_size); + p = buffer + sizeof(struct netent) + sizeof(char *); + memcpy(buffer + sizeof(struct netent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_ne.n_name != NULL) { + size = strlen(new_ne.n_name); + memcpy(p, new_ne.n_name, size); + new_ne.n_name = p; + p += size + 1; + } + + if (new_ne.n_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ne.n_aliases, sizeof(char *) * aliases_size); + new_ne.n_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (alias = new_ne.n_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_ne, sizeof(struct netent)); + return (NS_SUCCESS); +} + +static int +net_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + uint32_t net; + int type; + struct netent *ne; + char *orig_buf; + size_t orig_buf_size; + int *ret_errno; + + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + net = va_arg(ap, uint32_t); + type = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + ne = va_arg(ap, struct netent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct netent) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(ne, buffer, sizeof(struct netent)); + memcpy(&p, buffer + sizeof(struct netent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct netent) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct netent) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(ne->n_name, orig_buf, p, char *); + if (ne->n_aliases != NULL) { + NS_APPLY_OFFSET(ne->n_aliases, orig_buf, p, char **); + + for (alias = ne->n_aliases; *alias; ++alias) + NS_APPLY_OFFSET(*alias, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct netent **)retval) = ne; + + return (NS_SUCCESS); +} +#endif /* NS_CACHING */ + static void netent_data_free(void *ptr) { @@ -128,14 +345,22 @@ int getnetbyname_r(const char *name, struct netent *ne, char *buffer, size_t buflen, struct netent **result, int *h_errorp) { - int rval, ret_errno; - +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + networks, (void *)nss_lt_name, + net_id_func, net_marshal_func, net_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_getnetbyname, NULL) { NSSRC_DNS, _dns_getnetbyname, NULL }, NS_NIS_CB(_nis_getnetbyname, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; + int rval, ret_errno; rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS, "getnetbyname_r", default_src, name, ne, buffer, buflen, @@ -148,14 +373,22 @@ int getnetbyaddr_r(uint32_t addr, int af, struct netent *ne, char *buffer, size_t buflen, struct netent **result, int *h_errorp) { - int rval, ret_errno; - +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + networks, (void *)nss_lt_id, + net_id_func, net_marshal_func, net_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_getnetbyaddr, NULL) { NSSRC_DNS, _dns_getnetbyaddr, NULL }, NS_NIS_CB(_nis_getnetbyaddr, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; + int rval, ret_errno; rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS, "getnetbyaddr_r", default_src, addr, af, ne, buffer, buflen, diff --git a/lib/libc/net/getproto.c b/lib/libc/net/getproto.c index 9a32983..b2a3fe7 100644 --- a/lib/libc/net/getproto.c +++ b/lib/libc/net/getproto.c @@ -38,30 +38,96 @@ static char sccsid[] = "@(#)getproto.c 8.1 (Berkeley) 6/4/93"; __FBSDID("$FreeBSD$"); #include <netdb.h> +#include <nsswitch.h> #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" -int -getprotobynumber_r(int proto, struct protoent *pptr, char *buffer, - size_t buflen, struct protoent **result) +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; + +#ifdef NS_CACHING +extern int __proto_id_func(char *, size_t *, va_list, void *); +extern int __proto_marshal_func(char *, size_t *, void *, va_list, void *); +extern int __proto_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int +files_getprotobynumber(void *retval, void *mdata, va_list ap) { struct protoent pe; struct protoent_data *ped; int error; - if ((ped = __protoent_data_init()) == NULL) - return (-1); + int number; + struct protoent *pptr; + char *buffer; + size_t buflen; + int *errnop; + + number = va_arg(ap, int); + pptr = va_arg(ap, struct protoent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + if ((ped = __protoent_data_init()) == NULL) { + *errnop = -1; + return (NS_NOTFOUND); + } + __setprotoent_p(ped->stayopen, ped); while ((error = __getprotoent_p(&pe, ped)) == 0) - if (pe.p_proto == proto) + if (pe.p_proto == number) break; if (!ped->stayopen) __endprotoent_p(ped); - if (error != 0) - return (-1); - if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) - return (-1); - *result = pptr; - return (0); + if (error != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + + *((struct protoent **)retval) = pptr; + return (NS_SUCCESS); +} + +int +getprotobynumber_r(int proto, struct protoent *pptr, char *buffer, + size_t buflen, struct protoent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_id, + __proto_id_func, __proto_marshal_func, __proto_unmarshal_func); +#endif + + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_getprotobynumber, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotobynumber_r", + defaultsrc, proto, pptr, buffer, buflen, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } struct protoent * diff --git a/lib/libc/net/getprotoent.c b/lib/libc/net/getprotoent.c index 28f055b..3d3a57e 100644 --- a/lib/libc/net/getprotoent.c +++ b/lib/libc/net/getprotoent.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <errno.h> #include <limits.h> #include <netdb.h> +#include <nsswitch.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -50,6 +51,15 @@ __FBSDID("$FreeBSD$"); #include "reentrant.h" #include "un-namespace.h" #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" + +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; NETDB_THREAD_ALLOC(protoent_data) NETDB_THREAD_ALLOC(protodata) @@ -78,6 +88,214 @@ protodata_free(void *ptr) free(ptr); } +#ifdef NS_CACHING +int +__proto_id_func(char *buffer, size_t *buffer_size, va_list ap, + void *cache_mdata) +{ + char *name; + int proto; + + size_t desired_size, size; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + proto = va_arg(ap, int); + + desired_size = sizeof(enum nss_lookup_type) + sizeof(int); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &proto, + sizeof(int)); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + + +int +__proto_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + char *name; + int num; + struct protoent *proto; + char *orig_buf; + size_t orig_buf_size; + + struct protoent new_proto; + size_t desired_size, size, aliases_size; + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + num = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + proto = va_arg(ap, struct protoent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct protoent) + sizeof(char *); + if (proto->p_name != NULL) + desired_size += strlen(proto->p_name) + 1; + + if (proto->p_aliases != NULL) { + aliases_size = 0; + for (alias = proto->p_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + (aliases_size + 1) * + sizeof(char *); + } + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_proto, proto, sizeof(struct protoent)); + + *buffer_size = desired_size; + memset(buffer, 0, desired_size); + p = buffer + sizeof(struct protoent) + sizeof(char *); + memcpy(buffer + sizeof(struct protoent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_proto.p_name != NULL) { + size = strlen(new_proto.p_name); + memcpy(p, new_proto.p_name, size); + new_proto.p_name = p; + p += size + 1; + } + + if (new_proto.p_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_proto.p_aliases, sizeof(char *) * aliases_size); + new_proto.p_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (alias = new_proto.p_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_proto, sizeof(struct protoent)); + return (NS_SUCCESS); +} + +int +__proto_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + char *name; + int num; + struct protoent *proto; + char *orig_buf; + size_t orig_buf_size; + int *ret_errno; + + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + num = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + proto = va_arg(ap, struct protoent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct protoent) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(proto, buffer, sizeof(struct protoent)); + memcpy(&p, buffer + sizeof(struct protoent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct protoent) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct protoent) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(proto->p_name, orig_buf, p, char *); + if (proto->p_aliases != NULL) { + NS_APPLY_OFFSET(proto->p_aliases, orig_buf, p, char **); + + for (alias = proto->p_aliases; *alias; ++alias) + NS_APPLY_OFFSET(*alias, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct protoent **)retval) = proto; + + return (NS_SUCCESS); +} + +NSS_MP_CACHE_HANDLING(protocols); +#endif /* NS_CACHING */ + int __copy_protoent(struct protoent *pe, struct protoent *pptr, char *buf, size_t buflen) @@ -194,42 +412,133 @@ again: return (0); } -int -getprotoent_r(struct protoent *pptr, char *buffer, size_t buflen, - struct protoent **result) +static int +files_getprotoent_r(void *retval, void *mdata, va_list ap) { struct protoent pe; struct protoent_data *ped; + struct protoent *pptr; + char *buffer; + size_t buflen; + int *errnop; + + pptr = va_arg(ap, struct protoent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + if ((ped = __protoent_data_init()) == NULL) return (-1); - if (__getprotoent_p(&pe, ped) != 0) - return (-1); - if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) - return (-1); - *result = pptr; - return (0); + if (__getprotoent_p(&pe, ped) != 0) { + *errnop = errno; + return (NS_NOTFOUND); + } + + if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { + *errnop = errno; + return (NS_NOTFOUND); + } + + *((struct protoent **)retval) = pptr; + return (NS_SUCCESS); } -void -setprotoent(int f) +static int +files_setprotoent(void *retval, void *mdata, va_list ap) { struct protoent_data *ped; + int f; + f = va_arg(ap, int); if ((ped = __protoent_data_init()) == NULL) - return; + return (NS_UNAVAIL); + __setprotoent_p(f, ped); + return (NS_UNAVAIL); } -void -endprotoent(void) +static int +files_endprotoent(void *retval, void *mdata, va_list ap) { struct protoent_data *ped; if ((ped = __protoent_data_init()) == NULL) - return; + return (NS_UNAVAIL); + __endprotoent_p(ped); + return (NS_UNAVAIL); +} + +int +getprotoent_r(struct protoent *pptr, char *buffer, size_t buflen, + struct protoent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_all, + __proto_marshal_func, __proto_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_getprotoent_r, (void *)nss_lt_all }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotoent_r", + defaultsrc, pptr, buffer, buflen, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); +} + +void +setprotoent(int stayopen) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_all, + NULL, NULL); +#endif + + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setprotoent, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + + (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "setprotoent", defaultsrc, + stayopen); +} + +void +endprotoent(void) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_all, + NULL, NULL); +#endif + + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_endprotoent, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + + (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "endprotoent", defaultsrc); } struct protoent * diff --git a/lib/libc/net/getprotoname.c b/lib/libc/net/getprotoname.c index afe3f9d47..4ef50e3 100644 --- a/lib/libc/net/getprotoname.c +++ b/lib/libc/net/getprotoname.c @@ -38,20 +38,50 @@ static char sccsid[] = "@(#)getprotoname.c 8.1 (Berkeley) 6/4/93"; __FBSDID("$FreeBSD$"); #include <netdb.h> +#include <nsswitch.h> #include <string.h> #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" -int -getprotobyname_r(const char *name, struct protoent *pptr, char *buffer, - size_t buflen, struct protoent **result) +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; + +#ifdef NS_CACHING +extern int __proto_id_func(char *, size_t *, va_list, void *); +extern int __proto_marshal_func(char *, size_t *, void *, va_list, void *); +extern int __proto_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int +files_getprotobyname(void *retval, void *mdata, va_list ap) { struct protoent pe; struct protoent_data *ped; char **cp; int error; - if ((ped = __protoent_data_init()) == NULL) - return (-1); + char *name; + struct protoent *pptr; + char *buffer; + size_t buflen; + int *errnop; + + name = va_arg(ap, char *); + pptr = va_arg(ap, struct protoent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + + if ((ped = __protoent_data_init()) == NULL) { + *errnop = -1; + return (NS_NOTFOUND); + } __setprotoent_p(ped->stayopen, ped); while ((error = __getprotoent_p(&pe, ped)) == 0) { @@ -64,12 +94,48 @@ getprotobyname_r(const char *name, struct protoent *pptr, char *buffer, found: if (!ped->stayopen) __endprotoent_p(ped); - if (error != 0) - return (-1); - if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) - return (-1); - *result = pptr; - return (0); + if (error != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + + *((struct protoent **)retval) = pptr; + return (NS_SUCCESS); +} + + +int +getprotobyname_r(const char *name, struct protoent *pptr, char *buffer, + size_t buflen, struct protoent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_name, + __proto_id_func, __proto_marshal_func, __proto_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_getprotobyname, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotobyname_r", + defaultsrc, name, pptr, buffer, buflen, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } struct protoent * diff --git a/lib/libc/net/getservbyname.c b/lib/libc/net/getservbyname.c deleted file mode 100644 index 8870c6c..0000000 --- a/lib/libc/net/getservbyname.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 1983, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)getservbyname.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <netdb.h> -#include <string.h> -#include "netdb_private.h" - -int -getservbyname_r(const char *name, const char *proto, struct servent *sptr, - char *buffer, size_t buflen, struct servent **result) -{ - struct servent se; - struct servent_data *sed; - char **cp; - int error; - - if ((sed = __servent_data_init()) == NULL) - return (-1); - -#ifdef YP - sed->yp_name = (char *)name; - sed->yp_proto = (char *)proto; -#endif - - __setservent_p(sed->stayopen, sed); - while ((error = __getservent_p(&se, sed)) == 0) { - if (strcmp(name, se.s_name) == 0) - goto gotname; - for (cp = se.s_aliases; *cp; cp++) - if (strcmp(name, *cp) == 0) - goto gotname; - continue; -gotname: - if (proto == 0 || strcmp(se.s_proto, proto) == 0) - break; - } - if (!sed->stayopen) - __endservent_p(sed); - -#ifdef YP - sed->yp_name = NULL; - sed->yp_proto = NULL; -#endif - - if (error != 0) - return (-1); - if (__copy_servent(&se, sptr, buffer, buflen) != 0) - return (-1); - *result = sptr; - return (0); -} - -struct servent * -getservbyname(const char *name, const char *proto) -{ - struct servdata *sd; - struct servent *rval; - - if ((sd = __servdata_init()) == NULL) - return (NULL); - if (getservbyname_r(name, proto, &sd->serv, sd->data, sizeof(sd->data), - &rval) != 0) - return (NULL); - return (rval); -} diff --git a/lib/libc/net/getservbyport.c b/lib/libc/net/getservbyport.c deleted file mode 100644 index 827e5bf..0000000 --- a/lib/libc/net/getservbyport.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 1983, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)getservbyport.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <netdb.h> -#include <string.h> -#include "netdb_private.h" - -int -getservbyport_r(int port, const char *proto, struct servent *sptr, - char *buffer, size_t buflen, struct servent **result) -{ - struct servent se; - struct servent_data *sed; - int error; - - if ((sed = __servent_data_init()) == NULL) - return (-1); - -#ifdef YP - sed->yp_port = port; - sed->yp_proto = (char *)proto; -#endif - - __setservent_p(sed->stayopen, sed); - while ((error = __getservent_p(&se, sed)) == 0) { - if (se.s_port != port) - continue; - if (proto == 0 || strcmp(se.s_proto, proto) == 0) - break; - } - if (!sed->stayopen) - __endservent_p(sed); - -#ifdef YP - sed->yp_port = 0; - sed->yp_proto = NULL; -#endif - - if (error != 0) - return (-1); - if (__copy_servent(&se, sptr, buffer, buflen) != 0) - return (-1); - *result = sptr; - return (0); -} - -struct servent * -getservbyport(int port, const char *proto) -{ - struct servdata *sd; - struct servent *rval; - - if ((sd = __servdata_init()) == NULL) - return (NULL); - if (getservbyport_r(port, proto, &sd->serv, sd->data, - sizeof(sd->data), &rval) != 0) - return (NULL); - return (rval); -} diff --git a/lib/libc/net/getservent.c b/lib/libc/net/getservent.c index ff4864a9..b616e25 100644 --- a/lib/libc/net/getservent.c +++ b/lib/libc/net/getservent.c @@ -44,9 +44,11 @@ __FBSDID("$FreeBSD$"); #include <errno.h> #include <limits.h> #include <netdb.h> +#include <nsswitch.h> #include <stdio.h> #include <string.h> #include <stdlib.h> +#include <unistd.h> #ifdef YP #include <rpc/rpc.h> #include <rpcsvc/yp_prot.h> @@ -56,350 +58,1156 @@ __FBSDID("$FreeBSD$"); #include "reentrant.h" #include "un-namespace.h" #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" -NETDB_THREAD_ALLOC(servent_data) -NETDB_THREAD_ALLOC(servdata) +enum constants +{ + SETSERVENT = 1, + ENDSERVENT = 2, + SERVENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ + SERVENT_STORAGE_MAX = 1 << 20, /* 1 MByte */ +}; -static void -servent_data_clear(struct servent_data *sed) +struct servent_mdata { - if (sed->fp) { - fclose(sed->fp); - sed->fp = NULL; - } + enum nss_lookup_type how; + int compat_mode; +}; + +static const ns_src defaultsrc[] = { + { NSSRC_COMPAT, NS_SUCCESS }, + { NULL, 0 } +}; + +static int servent_unpack(char *, struct servent *, char **, size_t, int *); + +/* files backend declarations */ +struct files_state +{ + FILE *fp; + int stayopen; + + int compat_mode_active; +}; +static void files_endstate(void *); +NSS_TLS_HANDLING(files); + +static int files_servent(void *, void *, va_list); +static int files_setservent(void *, void *, va_list); + #ifdef YP - free(sed->yp_key); - sed->yp_key = NULL; +/* nis backend declarations */ +static int nis_servent(void *, void *, va_list); +static int nis_setservent(void *, void *, va_list); + +struct nis_state +{ + int yp_stepping; + char yp_domain[MAXHOSTNAMELEN]; + char *yp_key; + int yp_keylen; +}; +static void nis_endstate(void *); +NSS_TLS_HANDLING(nis); + +static int nis_servent(void *, void *, va_list); +static int nis_setservent(void *, void *, va_list); #endif -} -static void -servent_data_free(void *ptr) +/* compat backend declarations */ +static int compat_setservent(void *, void *, va_list); + +/* get** wrappers for get**_r functions declarations */ +struct servent_state { + struct servent serv; + char *buffer; + size_t bufsize; +}; +static void servent_endstate(void *); +NSS_TLS_HANDLING(servent); + +struct key { + const char *proto; + union { + const char *name; + int port; + }; +}; + +static int wrap_getservbyname_r(struct key, struct servent *, char *, size_t, + struct servent **); +static int wrap_getservbyport_r(struct key, struct servent *, char *, size_t, + struct servent **); +static int wrap_getservent_r(struct key, struct servent *, char *, size_t, + struct servent **); +static struct servent *getserv(int (*fn)(struct key, struct servent *, char *, + size_t, struct servent **), struct key); + +#ifdef NS_CACHING +static int serv_id_func(char *, size_t *, va_list, void *); +static int serv_marshal_func(char *, size_t *, void *, va_list, void *); +static int serv_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int +servent_unpack(char *p, struct servent *serv, char **aliases, + size_t aliases_size, int *errnop) { - struct servent_data *sed = ptr; + char *cp, **q, *endp; + long l; + + if (*p == '#') + return -1; + + memset(serv, 0, sizeof(struct servent)); + + cp = strpbrk(p, "#\n"); + if (cp != NULL) + *cp = '\0'; + serv->s_name = p; + + p = strpbrk(p, " \t"); + if (p == NULL) + return -1; + *p++ = '\0'; + while (*p == ' ' || *p == '\t') + p++; + cp = strpbrk(p, ",/"); + if (cp == NULL) + return -1; + + *cp++ = '\0'; + l = strtol(p, &endp, 10); + if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX) + return -1; + serv->s_port = htons((in_port_t)l); + serv->s_proto = cp; + + q = serv->s_aliases = aliases; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &aliases[aliases_size - 1]) { + *q++ = cp; + } else { + *q = NULL; + *errnop = ERANGE; + return -1; + } + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + *q = NULL; - servent_data_clear(sed); - free(sed); + return 0; } -static void -servdata_free(void *ptr) +/* files backend implementation */ +static void +files_endstate(void *p) { - free(ptr); + FILE * f; + + if (p == NULL) + return; + + f = ((struct files_state *)p)->fp; + if (f != NULL) + fclose(f); + + free(p); } -int -__copy_servent(struct servent *se, struct servent *sptr, char *buf, - size_t buflen) +/* + * compat structures. compat and files sources functionalities are almost + * equal, so they all are managed by files_servent function + */ +static int +files_servent(void *retval, void *mdata, va_list ap) { - char *cp; - int i, n; - int numptr, len; - - /* Find out the amount of space required to store the answer. */ - numptr = 1; /* NULL ptr */ - len = (char *)ALIGN(buf) - buf; - for (i = 0; se->s_aliases[i]; i++, numptr++) { - len += strlen(se->s_aliases[i]) + 1; + static const ns_src compat_src[] = { +#ifdef YP + { NSSRC_NIS, NS_SUCCESS }, +#endif + { NULL, 0 } + }; + ns_dtab compat_dtab[] = { +#ifdef YP + { NSSRC_NIS, nis_servent, + (void *)((struct servent_mdata *)mdata)->how }, +#endif + { NULL, NULL, NULL } + }; + + struct files_state *st; + int rv; + int stayopen; + + struct servent_mdata *serv_mdata; + char *name; + char *proto; + int port; + + struct servent *serv; + char *buffer; + size_t bufsize; + int *errnop; + + char **aliases; + int aliases_size; + size_t linesize; + char *line; + char **cp; + + name = NULL; + proto = NULL; + serv_mdata = (struct servent_mdata *)mdata; + switch (serv_mdata->how) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + return NS_NOTFOUND; + }; + + serv = va_arg(ap, struct servent *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap,int *); + + *errnop = files_getstate(&st); + if (*errnop != 0) + return (NS_UNAVAIL); + + if (st->fp == NULL) + st->compat_mode_active = 0; + + if (st->fp == NULL && (st->fp = fopen(_PATH_SERVICES, "r")) == NULL) { + *errnop = errno; + return (NS_UNAVAIL); } - len += strlen(se->s_name) + 1; - len += strlen(se->s_proto) + 1; - len += numptr * sizeof(char*); - if (len > (int)buflen) { - errno = ERANGE; - return (-1); + if (serv_mdata->how == nss_lt_all) + stayopen = 1; + else { + rewind(st->fp); + stayopen = st->stayopen; } - /* copy port value */ - sptr->s_port = se->s_port; + rv = NS_NOTFOUND; + do { + if (!st->compat_mode_active) { + if ((line = fgetln(st->fp, &linesize)) == NULL) { + *errnop = errno; + rv = NS_RETURN; + break; + } + + if (*line=='+') { + if (serv_mdata->compat_mode != 0) + st->compat_mode_active = 1; + } else { + if (bufsize <= linesize + _ALIGNBYTES + + sizeof(char *)) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + aliases = (char **)_ALIGN(&buffer[linesize+1]); + aliases_size = (buffer + bufsize - + (char *)aliases) / sizeof(char *); + if (aliases_size < 1) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + memcpy(buffer, line, linesize); + buffer[linesize] = '\0'; + } + } + + if (st->compat_mode_active != 0) { + switch (serv_mdata->how) { + case nss_lt_name: + rv = nsdispatch(retval, compat_dtab, + NSDB_SERVICES_COMPAT, "getservbyname_r", + compat_src, name, proto, serv, buffer, + bufsize, errnop); + break; + case nss_lt_id: + rv = nsdispatch(retval, compat_dtab, + NSDB_SERVICES_COMPAT, "getservbyport_r", + compat_src, port, proto, serv, buffer, + bufsize, errnop); + break; + case nss_lt_all: + rv = nsdispatch(retval, compat_dtab, + NSDB_SERVICES_COMPAT, "getservent_r", + compat_src, serv, buffer, bufsize, errnop); + break; + } + + if (!(rv & NS_TERMINATE) || + serv_mdata->how != nss_lt_all) + st->compat_mode_active = 0; + + continue; + } + + rv = servent_unpack(buffer, serv, aliases, aliases_size, + errnop); + if (rv !=0 ) { + if (*errnop == 0) { + rv = NS_NOTFOUND; + continue; + } + else { + rv = NS_RETURN; + break; + } + } + + rv = NS_NOTFOUND; + switch (serv_mdata->how) { + case nss_lt_name: + if (strcmp(name, serv->s_name) == 0) + goto gotname; + for (cp = serv->s_aliases; *cp; cp++) + if (strcmp(name, *cp) == 0) + goto gotname; - cp = (char *)ALIGN(buf) + numptr * sizeof(char *); + continue; + gotname: + if (proto == 0 || strcmp(serv->s_proto, proto) == 0) + rv = NS_SUCCESS; + break; + case nss_lt_id: + if (port != serv->s_port) + continue; + + if (proto == 0 || strcmp(serv->s_proto, proto) == 0) + rv = NS_SUCCESS; + break; + case nss_lt_all: + rv = NS_SUCCESS; + break; + } - /* copy official name */ - n = strlen(se->s_name) + 1; - strcpy(cp, se->s_name); - sptr->s_name = cp; - cp += n; + } while (!(rv & NS_TERMINATE)); - /* copy aliases */ - sptr->s_aliases = (char **)ALIGN(buf); - for (i = 0 ; se->s_aliases[i]; i++) { - n = strlen(se->s_aliases[i]) + 1; - strcpy(cp, se->s_aliases[i]); - sptr->s_aliases[i] = cp; - cp += n; + if (!stayopen && st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; } - sptr->s_aliases[i] = NULL; - /* copy proto */ - n = strlen(se->s_proto) + 1; - strcpy(cp, se->s_proto); - sptr->s_proto = cp; - cp += n; + if ((rv == NS_SUCCESS) && (retval != NULL)) + *(struct servent **)retval=serv; - return (0); + return (rv); } +static int +files_setservent(void *retval, void *mdata, va_list ap) +{ + struct files_state *st; + int rv; + int f; + + rv = files_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + + switch ((enum constants)mdata) { + case SETSERVENT: + f = va_arg(ap,int); + if (st->fp == NULL) + st->fp = fopen(_PATH_SERVICES, "r"); + else + rewind(st->fp); + st->stayopen |= f; + break; + case ENDSERVENT: + if (st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; + } + st->stayopen = 0; + break; + default: + break; + }; + + st->compat_mode_active = 0; + return (NS_UNAVAIL); +} + +/* nis backend implementation */ #ifdef YP +static void +nis_endstate(void *p) +{ + if (p == NULL) + return; + + free(((struct nis_state *)p)->yp_key); + free(p); +} + static int -_getservbyport_yp(struct servent_data *sed) +nis_servent(void *retval, void *mdata, va_list ap) { - char *result; - int resultlen; + char *resultbuf, *lastkey; + int resultbuflen; char buf[YPMAXRECORD + 2]; + + struct nis_state *st; int rv; - snprintf(buf, sizeof(buf), "%d/%s", ntohs(sed->yp_port), - sed->yp_proto); + enum nss_lookup_type how; + char *name; + char *proto; + int port; - sed->yp_port = 0; - sed->yp_proto = NULL; + struct servent *serv; + char *buffer; + size_t bufsize; + int *errnop; - if (!sed->yp_domain) { - if (yp_get_default_domain(&sed->yp_domain)) - return (0); + char **aliases; + int aliases_size; + + name = NULL; + proto = NULL; + how = (enum nss_lookup_type)mdata; + switch (how) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + return NS_NOTFOUND; + }; + + serv = va_arg(ap, struct servent *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + *errnop = nis_getstate(&st); + if (*errnop != 0) + return (NS_UNAVAIL); + + if (st->yp_domain[0] == '\0') { + if (getdomainname(st->yp_domain, sizeof st->yp_domain)) { + *errnop = errno; + return (NS_UNAVAIL); + } } - /* - * We have to be a little flexible here. Ideally you're supposed - * to have both a services.byname and a services.byport map, but - * some systems have only services.byname. FreeBSD cheats a little - * by putting the services.byport information in the same map as - * services.byname so that either case will work. We allow for both - * possibilities here: if there is no services.byport map, we try - * services.byname instead. - */ - if ((rv = yp_match(sed->yp_domain, "services.byport", buf, strlen(buf), - &result, &resultlen))) { - if (rv == YPERR_MAP) { - if (yp_match(sed->yp_domain, "services.byname", buf, - strlen(buf), &result, &resultlen)) - return(0); + do { + switch (how) { + case nss_lt_name: + snprintf(buf, sizeof(buf), "%s/%s", name, proto); + if (yp_match(st->yp_domain, "services.byname", buf, + strlen(buf), &resultbuf, &resultbuflen)) { + rv = NS_NOTFOUND; + goto fin; + } + break; + case nss_lt_id: + snprintf(buf, sizeof(buf), "%d/%s", ntohs(port), + proto); + + /* + * We have to be a little flexible + * here. Ideally you're supposed to have both + * a services.byname and a services.byport + * map, but some systems have only + * services.byname. FreeBSD cheats a little by + * putting the services.byport information in + * the same map as services.byname so that + * either case will work. We allow for both + * possibilities here: if there is no + * services.byport map, we try services.byname + * instead. + */ + rv = yp_match(st->yp_domain, "services.byport", buf, + strlen(buf), &resultbuf, &resultbuflen); + if (rv) { + if (rv == YPERR_MAP) { + if (yp_match(st->yp_domain, + "services.byname", buf, + strlen(buf), &resultbuf, + &resultbuflen)) { + rv = NS_NOTFOUND; + goto fin; + } + } else { + rv = NS_NOTFOUND; + goto fin; + } + } + + break; + case nss_lt_all: + if (!st->yp_stepping) { + free(st->yp_key); + rv = yp_first(st->yp_domain, "services.byname", + &st->yp_key, &st->yp_keylen, &resultbuf, + &resultbuflen); + if (rv) { + rv = NS_NOTFOUND; + goto fin; + } + st->yp_stepping = 1; + } else { + lastkey = st->yp_key; + rv = yp_next(st->yp_domain, "services.byname", + st->yp_key, st->yp_keylen, &st->yp_key, + &st->yp_keylen, &resultbuf, &resultbuflen); + free(lastkey); + if (rv) { + st->yp_stepping = 0; + rv = NS_NOTFOUND; + goto fin; + } + } + break; + }; + + /* we need a room for additional \n symbol */ + if (bufsize <= + resultbuflen + 1 + _ALIGNBYTES + sizeof(char *)) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + aliases = (char **)_ALIGN(&buffer[resultbuflen + 2]); + aliases_size = + (buffer + bufsize - (char *)aliases) / sizeof(char *); + if (aliases_size < 1) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + /* + * servent_unpack expects lines terminated with \n -- + * make it happy + */ + memcpy(buffer, resultbuf, resultbuflen); + buffer[resultbuflen] = '\n'; + buffer[resultbuflen + 1] = '\0'; + + if (servent_unpack(buffer, serv, aliases, aliases_size, + errnop) != 0) { + if (*errnop == 0) + rv = NS_NOTFOUND; + else + rv = NS_RETURN; } else - return(0); - } + rv = NS_SUCCESS; + free(resultbuf); + + } while (!(rv & NS_TERMINATE) && how == nss_lt_all); - /* getservent() expects lines terminated with \n -- make it happy */ - snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result); +fin: + if (rv == NS_SUCCESS && retval != NULL) + *(struct servent **)retval = serv; - free(result); - return(1); + return (rv); } static int -_getservbyname_yp(struct servent_data *sed) +nis_setservent(void *result, void *mdata, va_list ap) { - char *result; - int resultlen; - char buf[YPMAXRECORD + 2]; + struct nis_state *st; + int rv; - if(!sed->yp_domain) { - if(yp_get_default_domain(&sed->yp_domain)) - return (0); - } + rv = nis_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + + switch ((enum constants)mdata) { + case SETSERVENT: + case ENDSERVENT: + free(st->yp_key); + st->yp_key = NULL; + st->yp_stepping = 0; + break; + default: + break; + }; - snprintf(buf, sizeof(buf), "%s/%s", sed->yp_name, sed->yp_proto); + return (NS_UNAVAIL); +} +#endif + +/* compat backend implementation */ +static int +compat_setservent(void *retval, void *mdata, va_list ap) +{ + static const ns_src compat_src[] = { +#ifdef YP + { NSSRC_NIS, NS_SUCCESS }, +#endif + { NULL, 0 } + }; + ns_dtab compat_dtab[] = { +#ifdef YP + { NSSRC_NIS, nis_setservent, mdata }, +#endif + { NULL, NULL, NULL } + }; + int f; - sed->yp_name = 0; - sed->yp_proto = NULL; + (void)files_setservent(retval, mdata, ap); - if (yp_match(sed->yp_domain, "services.byname", buf, strlen(buf), - &result, &resultlen)) { - return(0); + switch ((enum constants)mdata) { + case SETSERVENT: + f = va_arg(ap,int); + (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, + "setservent", compat_src, f); + break; + case ENDSERVENT: + (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, + "endservent", compat_src); + break; + default: + break; } - /* getservent() expects lines terminated with \n -- make it happy */ - snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result); - - free(result); - return(1); + return (NS_UNAVAIL); } +#ifdef NS_CACHING static int -_getservent_yp(struct servent_data *sed) +serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { - char *lastkey, *result; - int resultlen; - int rv; + char *name; + char *proto; + int port; - if (!sed->yp_domain) { - if (yp_get_default_domain(&sed->yp_domain)) - return (0); - } + size_t desired_size, size, size2; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (proto != NULL) { + size2 = strlen(proto); + desired_size += size2 + 1; + } else + size2 = 0; - if (!sed->yp_stepping) { - free(sed->yp_key); - rv = yp_first(sed->yp_domain, "services.byname", &sed->yp_key, - &sed->yp_keylen, &result, &resultlen); - if (rv) { - sed->yp_stepping = 0; - return(0); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; } - sed->yp_stepping = 1; - } else { - lastkey = sed->yp_key; - rv = yp_next(sed->yp_domain, "services.byname", sed->yp_key, - sed->yp_keylen, &sed->yp_key, &sed->yp_keylen, &result, - &resultlen); - free(lastkey); - if (rv) { - sed->yp_stepping = 0; - return (0); + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + if (proto != NULL) + memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1, + proto, size2 + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + + desired_size = sizeof(enum nss_lookup_type) + sizeof(int); + if (proto != NULL) { + size = strlen(proto); + desired_size += size + 1; + } else + size = 0; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &port, + sizeof(int)); + + if (proto != NULL) + memcpy(buffer + sizeof(enum nss_lookup_type) + + sizeof(int), proto, size + 1); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +int +serv_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + char *proto; + int port; + struct servent *serv; + char *orig_buf; + size_t orig_buf_size; + + struct servent new_serv; + size_t desired_size; + char **alias; + char *p; + size_t size; + size_t aliases_size; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + serv = va_arg(ap, struct servent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *); + if (serv->s_name != NULL) + desired_size += strlen(serv->s_name) + 1; + if (serv->s_proto != NULL) + desired_size += strlen(serv->s_proto) + 1; + + aliases_size = 0; + if (serv->s_aliases != NULL) { + for (alias = serv->s_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; } + + desired_size += _ALIGNBYTES + + sizeof(char *) * (aliases_size + 1); + } + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_serv, serv, sizeof(struct servent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct servent) + sizeof(char *); + memcpy(buffer + sizeof(struct servent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_serv.s_name != NULL) { + size = strlen(new_serv.s_name); + memcpy(p, new_serv.s_name, size); + new_serv.s_name = p; + p += size + 1; } - /* getservent() expects lines terminated with \n -- make it happy */ - snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result); + if (new_serv.s_proto != NULL) { + size = strlen(new_serv.s_proto); + memcpy(p, new_serv.s_proto, size); + new_serv.s_proto = p; + p += size + 1; + } - free(result); + if (new_serv.s_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size); + new_serv.s_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); - return(1); + for (alias = new_serv.s_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_serv, sizeof(struct servent)); + return (NS_SUCCESS); } -#endif -void -__setservent_p(int f, struct servent_data *sed) +int +serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) { - if (sed->fp == NULL) - sed->fp = fopen(_PATH_SERVICES, "r"); - else - rewind(sed->fp); - sed->stayopen |= f; + char *name; + char *proto; + int port; + struct servent *serv; + char *orig_buf; + char *p; + char **alias; + size_t orig_buf_size; + int *ret_errno; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + serv = va_arg(ap, struct servent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct servent) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(serv, buffer, sizeof(struct servent)); + memcpy(&p, buffer + sizeof(struct servent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) + + (_ALIGN(p) - (size_t)p), + buffer_size - sizeof(struct servent) - sizeof(char *) - + (_ALIGN(p) - (size_t)p)); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *); + NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *); + if (serv->s_aliases != NULL) { + NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **); + + for (alias = serv->s_aliases; *alias; ++alias) + NS_APPLY_OFFSET(*alias, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct servent **)retval) = serv; + return (NS_SUCCESS); } -void -__endservent_p(struct servent_data *sed) +NSS_MP_CACHE_HANDLING(services); +#endif /* NS_CACHING */ + +/* get**_r functions implementation */ +int +getservbyname_r(const char *name, const char *proto, struct servent *serv, + char *buffer, size_t bufsize, struct servent **result) { - servent_data_clear(sed); - sed->stayopen = 0; + static const struct servent_mdata mdata = { nss_lt_name, 0 }; + static const struct servent_mdata compat_mdata = { nss_lt_name, 1 }; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_name, + serv_id_func, serv_marshal_func, serv_unmarshal_func); +#endif /* NS_CACHING */ + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_servent, (void *)&mdata }, #ifdef YP - sed->yp_stepping = 0; - sed->yp_domain = NULL; + { NSSRC_NIS, nis_servent, (void *)nss_lt_name }, +#endif + { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) #endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r", + defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } int -__getservent_p(struct servent *se, struct servent_data *sed) +getservbyport_r(int port, const char *proto, struct servent *serv, + char *buffer, size_t bufsize, struct servent **result) { - char *p; - char *cp, **q, *endp; - long l; - -#ifdef YP - if (sed->yp_stepping && _getservent_yp(sed)) { - p = sed->line; - goto unpack; - } -tryagain: + static const struct servent_mdata mdata = { nss_lt_id, 0 }; + static const struct servent_mdata compat_mdata = { nss_lt_id, 1 }; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_id, + serv_id_func, serv_marshal_func, serv_unmarshal_func); #endif - if (sed->fp == NULL && (sed->fp = fopen(_PATH_SERVICES, "r")) == NULL) - return (-1); -again: - if ((p = fgets(sed->line, sizeof sed->line, sed->fp)) == NULL) - return (-1); + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_servent, (void *)&mdata }, #ifdef YP - if (*p == '+' && _yp_check(NULL)) { - if (sed->yp_name != NULL) { - if (!_getservbyname_yp(sed)) - goto tryagain; - } - else if (sed->yp_port != 0) { - if (!_getservbyport_yp(sed)) - goto tryagain; - } - else if (!_getservent_yp(sed)) - goto tryagain; - } -unpack: + { NSSRC_NIS, nis_servent, (void *)nss_lt_id }, #endif - if (*p == '#') - goto again; - cp = strpbrk(p, "#\n"); - if (cp != NULL) - *cp = '\0'; - se->s_name = p; - p = strpbrk(p, " \t"); - if (p == NULL) - goto again; - *p++ = '\0'; - while (*p == ' ' || *p == '\t') - p++; - cp = strpbrk(p, ",/"); - if (cp == NULL) - goto again; - *cp++ = '\0'; - l = strtol(p, &endp, 10); - if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX) - goto again; - se->s_port = htons((in_port_t)l); - se->s_proto = cp; - q = se->s_aliases = sed->aliases; - cp = strpbrk(cp, " \t"); - if (cp != NULL) - *cp++ = '\0'; - while (cp && *cp) { - if (*cp == ' ' || *cp == '\t') { - cp++; - continue; - } - if (q < &sed->aliases[_MAXALIASES - 1]) - *q++ = cp; - cp = strpbrk(cp, " \t"); - if (cp != NULL) - *cp++ = '\0'; - } - *q = NULL; - return (0); + { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r", + defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } int -getservent_r(struct servent *sptr, char *buffer, size_t buflen, +getservent_r(struct servent *serv, char *buffer, size_t bufsize, struct servent **result) { - struct servent se; - struct servent_data *sed; - - if ((sed = __servent_data_init()) == NULL) - return (-1); - - if (__getservent_p(&se, sed) != 0) - return (-1); - if (__copy_servent(&se, sptr, buffer, buflen) != 0) - return (-1); - *result = sptr; - return (0); + static const struct servent_mdata mdata = { nss_lt_all, 0 }; + static const struct servent_mdata compat_mdata = { nss_lt_all, 1 }; +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_all, + serv_marshal_func, serv_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_servent, (void *)&mdata }, +#ifdef YP + { NSSRC_NIS, nis_servent, (void *)nss_lt_all }, +#endif + { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r", + defaultsrc, serv, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } void -setservent(int f) +setservent(int stayopen) { - struct servent_data *sed; +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setservent, (void *)SETSERVENT }, +#ifdef YP + { NSSRC_NIS, nis_setservent, (void *)SETSERVENT }, +#endif + { NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; - if ((sed = __servent_data_init()) == NULL) - return; - __setservent_p(f, sed); + (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc, + stayopen); } void -endservent(void) +endservent() { - struct servent_data *sed; +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setservent, (void *)ENDSERVENT }, +#ifdef YP + { NSSRC_NIS, nis_setservent, (void *)ENDSERVENT }, +#endif + { NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; - if ((sed = __servent_data_init()) == NULL) + (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc); +} + +/* get** wrappers for get**_r functions implementation */ +static void +servent_endstate(void *p) +{ + if (p == NULL) return; - __endservent_p(sed); + + free(((struct servent_state *)p)->buffer); + free(p); +} + +static int +wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer, + size_t bufsize, struct servent **res) +{ + return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize, + res)); +} + +static int +wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer, + size_t bufsize, struct servent **res) +{ + return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize, + res)); +} + +static int +wrap_getservent_r(struct key key, struct servent *serv, char *buffer, + size_t bufsize, struct servent **res) +{ + return (getservent_r(serv, buffer, bufsize, res)); +} + +static struct servent * +getserv(int (*fn)(struct key, struct servent *, char *, size_t, + struct servent **), struct key key) +{ + int rv; + struct servent *res; + struct servent_state * st; + + rv = servent_getstate(&st); + if (rv != 0) { + errno = rv; + return NULL; + } + + if (st->buffer == NULL) { + st->buffer = malloc(SERVENT_STORAGE_INITIAL); + if (st->buffer == NULL) + return (NULL); + st->bufsize = SERVENT_STORAGE_INITIAL; + } + do { + rv = fn(key, &st->serv, st->buffer, st->bufsize, &res); + if (res == NULL && rv == ERANGE) { + free(st->buffer); + if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) { + st->buffer = NULL; + errno = ERANGE; + return (NULL); + } + st->bufsize <<= 1; + st->buffer = malloc(st->bufsize); + if (st->buffer == NULL) + return (NULL); + } + } while (res == NULL && rv == ERANGE); + if (rv != 0) + errno = rv; + + return (res); +} + +struct servent * +getservbyname(const char *name, const char *proto) +{ + struct key key; + + key.name = name; + key.proto = proto; + + return (getserv(wrap_getservbyname_r, key)); } struct servent * -getservent(void) +getservbyport(int port, const char *proto) { - struct servdata *sd; - struct servent *rval; - - if ((sd = __servdata_init()) == NULL) - return (NULL); - if (getservent_r(&sd->serv, sd->data, sizeof(sd->data), &rval) != 0) - return (NULL); - return (rval); + struct key key; + + key.port = port; + key.proto = proto; + + return (getserv(wrap_getservbyport_r, key)); +} + +struct servent * +getservent() +{ + struct key key; + + key.proto = NULL; + key.port = 0; + + return (getserv(wrap_getservent_r, key)); } diff --git a/lib/libc/net/name6.c b/lib/libc/net/name6.c index 18f2fa2..dc6521a 100644 --- a/lib/libc/net/name6.c +++ b/lib/libc/net/name6.c @@ -123,6 +123,9 @@ __FBSDID("$FreeBSD$"); #include "netdb_private.h" #include "res_config.h" #include "res_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif #ifndef _PATH_HOSTS #define _PATH_HOSTS "/etc/hosts" @@ -235,6 +238,11 @@ static void _dns_ehent(void) __unused; #ifdef ICMPNL static int _icmp_ghbyaddr(void *, void *, va_list); #endif /* ICMPNL */ +#ifdef NS_CACHING +static int ipnode_id_func(char *, size_t *, va_list, void *); +static int ipnode_marshal_func(char *, size_t *, void *, va_list, void *); +static int ipnode_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif #ifdef ICMPNL static mutex_t _getipnodeby_thread_lock = MUTEX_INITIALIZER; @@ -281,6 +289,239 @@ _mapped_addr_enabled(void) return 0; } +#ifdef NS_CACHING +static int +ipnode_id_func(char *buffer, size_t *buffer_size, va_list ap, + void *cache_mdata) +{ + res_state statp; + u_long res_options; + + const int op_id = 2; + char *name; + int af; + size_t len; + void *src; + + char *p; + size_t desired_size, size; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + statp = __res_state(); + res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | + RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + af = va_arg(ap, int); + + size = strlen(name); + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) + size + 1; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(enum nss_lookup_type); + + memcpy(p, &af, sizeof(int)); + p += sizeof(int); + + memcpy(p, name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + src = va_arg(ap, void *); + len = va_arg(ap, size_t); + af = va_arg(ap, int); + + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) + + sizeof(size_t) + len; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(enum nss_lookup_type); + + memcpy(p, &af, sizeof(int)); + p += sizeof(int); + + memcpy(p, &len, sizeof(size_t)); + p += sizeof(size_t); + + memcpy(p, src, len); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +static int +ipnode_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct hostent *ht; + + struct hostent new_ht; + size_t desired_size, aliases_size, addr_size, size; + char *p, **iter; + + ht = *((struct hostent **)retval); + + desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *); + if (ht->h_name != NULL) + desired_size += strlen(ht->h_name) + 1; + + if (ht->h_aliases != NULL) { + aliases_size = 0; + for (iter = ht->h_aliases; *iter; ++iter) { + desired_size += strlen(*iter) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + + (aliases_size + 1) * sizeof(char *); + } + + if (ht->h_addr_list != NULL) { + addr_size = 0; + for (iter = ht->h_addr_list; *iter; ++iter) + ++addr_size; + + desired_size += addr_size * _ALIGN(ht->h_length); + desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *); + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_ht, ht, sizeof(struct hostent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct hostent) + sizeof(char *); + memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_ht.h_name != NULL) { + size = strlen(new_ht.h_name); + memcpy(p, new_ht.h_name, size); + new_ht.h_name = p; + p += size + 1; + } + + if (new_ht.h_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size); + new_ht.h_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (iter = new_ht.h_aliases; *iter; ++iter) { + size = strlen(*iter); + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + + if (new_ht.h_addr_list != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size); + new_ht.h_addr_list = (char **)p; + p += sizeof(char *) * (addr_size + 1); + + size = _ALIGN(new_ht.h_length); + for (iter = new_ht.h_addr_list; *iter; ++iter) { + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + memcpy(buffer, &new_ht, sizeof(struct hostent)); + return (NS_SUCCESS); +} + +static int +ipnode_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct hostent new_ht; + struct hostent *ht; + + char *p; + char **iter; + char *orig_buf; + int err; + + ht = &new_ht; + + memcpy(ht, buffer, sizeof(struct hostent)); + memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *)); + + orig_buf = buffer + sizeof(struct hostent) + sizeof(char *) + + _ALIGN(p) - (size_t)p; + p = (char *)_ALIGN(p); + + + NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *); + if (ht->h_aliases != NULL) { + NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **); + + for (iter = ht->h_aliases; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + if (ht->h_addr_list != NULL) { + NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **); + + for (iter = ht->h_addr_list; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + ht = _hpcopy(ht, &err); + if (ht == NULL) + return (NS_UNAVAIL); + + *((struct hostent **)retval) = ht; + return (NS_SUCCESS); +} +#endif + /* * Functions defined in RFC2553 * getipnodebyname, getipnodebyaddr, freehostent @@ -292,10 +533,19 @@ _ghbyname(const char *name, int af, int flags, int *errp) struct hostent *hp; int rval; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_name, + ipnode_id_func, ipnode_marshal_func, ipnode_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_files_ghbyname, NULL) { NSSRC_DNS, _dns_ghbyname, NULL }, NS_NIS_CB(_nis_ghbyname, NULL) +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; @@ -399,6 +649,12 @@ getipnodebyaddr(const void *src, size_t len, int af, int *errp) struct in_addr addrbuf; #endif +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_id, + ipnode_id_func, ipnode_marshal_func, ipnode_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_files_ghbyaddr, NULL) { NSSRC_DNS, _dns_ghbyaddr, NULL }, @@ -406,6 +662,9 @@ getipnodebyaddr(const void *src, size_t len, int af, int *errp) #ifdef ICMPNL { NSSRC_ICMP, _icmp_ghbyaddr, NULL }, #endif +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; diff --git a/lib/libc/net/netdb_private.h b/lib/libc/net/netdb_private.h index fa1de62..e5535f6 100644 --- a/lib/libc/net/netdb_private.h +++ b/lib/libc/net/netdb_private.h @@ -100,22 +100,6 @@ struct protoent_data { char line[_MAXLINELEN + 1]; }; -struct servent_data { - FILE *fp; - char *aliases[_MAXALIASES]; - int stayopen; - char line[_MAXLINELEN + 1]; -#ifdef YP - int yp_stepping; - char *yp_name; - char *yp_proto; - int yp_port; - char *yp_domain; - char *yp_key; - int yp_keylen; -#endif -}; - struct hostdata { struct hostent host; char data[sizeof(struct hostent_data)]; @@ -131,11 +115,6 @@ struct protodata { char data[sizeof(struct protoent_data)]; }; -struct servdata { - struct servent serv; - char data[sizeof(struct servent_data)]; -}; - struct hostdata *__hostdata_init(void); struct hostent *__hostent_init(void); struct hostent_data *__hostent_data_init(void); @@ -143,19 +122,13 @@ struct netdata *__netdata_init(void); struct netent_data *__netent_data_init(void); struct protodata *__protodata_init(void); struct protoent_data *__protoent_data_init(void); -struct servdata *__servdata_init(void); -struct servent_data *__servent_data_init(void); int __copy_hostent(struct hostent *, struct hostent *, char *, size_t); int __copy_netent(struct netent *, struct netent *, char *, size_t); int __copy_protoent(struct protoent *, struct protoent *, char *, size_t); -int __copy_servent(struct servent *, struct servent *, char *, size_t); void __endprotoent_p(struct protoent_data *); -void __endservent_p(struct servent_data *); int __getprotoent_p(struct protoent *, struct protoent_data *); -int __getservent_p(struct servent *, struct servent_data *); void __setprotoent_p(int, struct protoent_data *); -void __setservent_p(int, struct servent_data *); void _endhostdnsent(void); void _endhosthtent(struct hostent_data *); void _endnetdnsent(void); diff --git a/lib/libc/net/nscache.c b/lib/libc/net/nscache.c new file mode 100644 index 0000000..98a4367 --- /dev/null +++ b/lib/libc/net/nscache.c @@ -0,0 +1,438 @@ +/*- + * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <nsswitch.h> +#include <stdlib.h> +#include <string.h> +#include "un-namespace.h" +#include "nscachedcli.h" +#include "nscache.h" + +#define NSS_CACHE_KEY_INITIAL_SIZE (256) +#define NSS_CACHE_KEY_SIZE_LIMIT (NSS_CACHE_KEY_INITIAL_SIZE << 4) + +#define NSS_CACHE_BUFFER_INITIAL_SIZE (1024) +#define NSS_CACHE_BUFFER_SIZE_LIMIT (NSS_CACHE_BUFFER_INITIAL_SIZE << 8) + +#define CACHED_SOCKET_PATH "/var/run/cached" + +int +__nss_cache_handler(void *retval, void *mdata, va_list ap) +{ + return (NS_UNAVAIL); +} + +int +__nss_common_cache_read(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_connection connection; + + char *buffer; + size_t buffer_size, size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + cache_data->key = (char *)malloc(NSS_CACHE_KEY_INITIAL_SIZE); + memset(cache_data->key, 0, NSS_CACHE_KEY_INITIAL_SIZE); + cache_data->key_size = NSS_CACHE_KEY_INITIAL_SIZE; + va_copy(ap_new, ap); + + do { + size = cache_data->key_size; + res = cache_info->id_func(cache_data->key, &size, ap_new, + cache_info->mdata); + va_end(ap_new); + if (res == NS_RETURN) { + if (cache_data->key_size > NSS_CACHE_KEY_SIZE_LIMIT) + break; + + cache_data->key_size <<= 1; + cache_data->key = realloc(cache_data->key, + cache_data->key_size); + memset(cache_data->key, 0, cache_data->key_size); + va_copy(ap_new, ap); + } + } while (res == NS_RETURN); + + if (res != NS_SUCCESS) { + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + return (res); + } else + cache_data->key_size = size; + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + connection = __open_cached_connection(¶ms); + if (connection == NULL) { + res = -1; + break; + } + res = __cached_read(connection, cache_info->entry_name, + cache_data->key, cache_data->key_size, buffer, + &buffer_size); + __close_cached_connection(connection); + if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) { + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == -2); + + if (res == 0) { + if (buffer_size == 0) { + free(buffer); + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + return (NS_RETURN); + } + + va_copy(ap_new, ap); + res = cache_info->unmarshal_func(buffer, buffer_size, retval, + ap_new, cache_info->mdata); + va_end(ap_new); + + if (res != NS_SUCCESS) { + free(buffer); + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + return (res); + } else + res = 0; + } + + if (res == 0) { + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + } + + free(buffer); + return (res == 0 ? NS_SUCCESS : NS_NOTFOUND); +} + +int +__nss_common_cache_write(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_connection connection; + + char *buffer; + size_t buffer_size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + if (cache_data->key == NULL) + return (NS_UNAVAIL); + + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + connection = __open_cached_connection(¶ms); + if (connection == NULL) { + free(cache_data->key); + return (NS_UNAVAIL); + } + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + size_t size; + + size = buffer_size; + va_copy(ap_new, ap); + res = cache_info->marshal_func(buffer, &size, retval, ap_new, + cache_info->mdata); + va_end(ap_new); + + if (res == NS_RETURN) { + if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT) + break; + + buffer_size <<= 1; + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == NS_RETURN); + + if (res != NS_SUCCESS) { + __close_cached_connection(connection); + free(cache_data->key); + free(buffer); + return (res); + } + + res = __cached_write(connection, cache_info->entry_name, + cache_data->key, cache_data->key_size, buffer, buffer_size); + __close_cached_connection(connection); + + free(cache_data->key); + free(buffer); + + return (res == 0 ? NS_SUCCESS : NS_UNAVAIL); +} + +int +__nss_common_cache_write_negative(void *mdata) +{ + struct cached_connection_params params; + cached_connection connection; + int res; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + if (cache_data->key == NULL) + return (NS_UNAVAIL); + + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + connection = __open_cached_connection(¶ms); + if (connection == NULL) { + free(cache_data->key); + return (NS_UNAVAIL); + } + + res = __cached_write(connection, cache_info->entry_name, + cache_data->key, cache_data->key_size, NULL, 0); + __close_cached_connection(connection); + + free(cache_data->key); + return (res == 0 ? NS_SUCCESS : NS_UNAVAIL); +} + +int +__nss_mp_cache_read(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_mp_read_session rs; + + char *buffer; + size_t buffer_size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + if (cache_info->get_mp_ws_func() != INVALID_CACHED_MP_WRITE_SESSION) + return (NS_UNAVAIL); + + rs = cache_info->get_mp_rs_func(); + if (rs == INVALID_CACHED_MP_READ_SESSION) { + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + rs = __open_cached_mp_read_session(¶ms, + cache_info->entry_name); + if (rs == INVALID_CACHED_MP_READ_SESSION) + return (NS_UNAVAIL); + + cache_info->set_mp_rs_func(rs); + } + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + res = __cached_mp_read(rs, buffer, &buffer_size); + if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) { + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == -2); + + if (res == 0) { + va_copy(ap_new, ap); + res = cache_info->unmarshal_func(buffer, buffer_size, retval, + ap_new, cache_info->mdata); + va_end(ap_new); + + if (res != NS_SUCCESS) { + free(buffer); + return (res); + } else + res = 0; + } else { + free(buffer); + __close_cached_mp_read_session(rs); + rs = INVALID_CACHED_MP_READ_SESSION; + cache_info->set_mp_rs_func(rs); + return (res == -1 ? NS_RETURN : NS_UNAVAIL); + } + + free(buffer); + return (res == 0 ? NS_SUCCESS : NS_NOTFOUND); +} + +int +__nss_mp_cache_write(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_mp_write_session ws; + + char *buffer; + size_t buffer_size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + ws = cache_info->get_mp_ws_func(); + if (ws == INVALID_CACHED_MP_WRITE_SESSION) { + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + ws = __open_cached_mp_write_session(¶ms, + cache_info->entry_name); + if (ws == INVALID_CACHED_MP_WRITE_SESSION) + return (NS_UNAVAIL); + + cache_info->set_mp_ws_func(ws); + } + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + size_t size; + + size = buffer_size; + va_copy(ap_new, ap); + res = cache_info->marshal_func(buffer, &size, retval, ap_new, + cache_info->mdata); + va_end(ap_new); + + if (res == NS_RETURN) { + if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT) + break; + + buffer_size <<= 1; + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == NS_RETURN); + + if (res != NS_SUCCESS) { + free(buffer); + return (res); + } + + res = __cached_mp_write(ws, buffer, buffer_size); + + free(buffer); + return (res == 0 ? NS_SUCCESS : NS_UNAVAIL); +} + +int +__nss_mp_cache_write_submit(void *retval, void *mdata, va_list ap) +{ + cached_mp_write_session ws; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + ws = cache_info->get_mp_ws_func(); + if (ws != INVALID_CACHED_MP_WRITE_SESSION) { + __close_cached_mp_write_session(ws); + ws = INVALID_CACHED_MP_WRITE_SESSION; + cache_info->set_mp_ws_func(ws); + } + return (NS_UNAVAIL); +} + +int +__nss_mp_cache_end(void *retval, void *mdata, va_list ap) +{ + cached_mp_write_session ws; + cached_mp_read_session rs; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + ws = cache_info->get_mp_ws_func(); + if (ws != INVALID_CACHED_MP_WRITE_SESSION) { + __abandon_cached_mp_write_session(ws); + ws = INVALID_CACHED_MP_WRITE_SESSION; + cache_info->set_mp_ws_func(ws); + } + + rs = cache_info->get_mp_rs_func(); + if (rs != INVALID_CACHED_MP_READ_SESSION) { + __close_cached_mp_read_session(rs); + rs = INVALID_CACHED_MP_READ_SESSION; + cache_info->set_mp_rs_func(rs); + } + + return (NS_UNAVAIL); +} diff --git a/lib/libc/net/nscachedcli.c b/lib/libc/net/nscachedcli.c new file mode 100644 index 0000000..374f370 --- /dev/null +++ b/lib/libc/net/nscachedcli.c @@ -0,0 +1,576 @@ +/*- + * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/event.h> +#include <sys/uio.h> +#include <sys/un.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "un-namespace.h" +#include "nscachedcli.h" + +#define NS_DEFAULT_CACHED_IO_TIMEOUT 4 + +static int safe_write(struct cached_connection_ *, const void *, size_t); +static int safe_read(struct cached_connection_ *, void *, size_t); +static int send_credentials(struct cached_connection_ *, int); + +/* + * safe_write writes data to the specified connection and tries to do it in + * the very safe manner. We ensure, that we can write to the socket with + * kevent. If the data_size can't be sent in one piece, then it would be + * splitted. + */ +static int +safe_write(struct cached_connection_ *connection, const void *data, + size_t data_size) +{ + struct kevent eventlist; + int nevents; + size_t result; + ssize_t s_result; + struct timespec timeout; + + if (data_size == 0) + return (0); + + timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; + timeout.tv_nsec = 0; + result = 0; + do { + nevents = kevent(connection->write_queue, NULL, 0, &eventlist, + 1, &timeout); + if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { + s_result = _write(connection->sockfd, data + result, + eventlist.data < data_size - result ? + eventlist.data : data_size - result); + if (s_result == -1) + return (-1); + else + result += s_result; + + if (eventlist.flags & EV_EOF) + return (result < data_size ? -1 : 0); + } else + return (-1); + } while (result < data_size); + + return (0); +} + +/* + * safe_read reads data from connection and tries to do it in the very safe + * and stable way. It uses kevent to ensure, that the data are availabe for + * reading. If the amount of data to be read is too large, then they would + * be splitted. + */ +static int +safe_read(struct cached_connection_ *connection, void *data, size_t data_size) +{ + struct kevent eventlist; + size_t result; + ssize_t s_result; + struct timespec timeout; + int nevents; + + if (data_size == 0) + return (0); + + timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; + timeout.tv_nsec = 0; + result = 0; + do { + nevents = kevent(connection->read_queue, NULL, 0, &eventlist, + 1, &timeout); + if (nevents == 1 && eventlist.filter == EVFILT_READ) { + s_result = _read(connection->sockfd, data + result, + eventlist.data <= data_size - result ? + eventlist.data : data_size - result); + if (s_result == -1) + return (-1); + else + result += s_result; + + if (eventlist.flags & EV_EOF) + return (result < data_size ? -1 : 0); + } else + return (-1); + } while (result < data_size); + + return (0); +} + +/* + * Sends the credentials information to the connection along with the + * communication element type. + */ +static int +send_credentials(struct cached_connection_ *connection, int type) +{ + struct kevent eventlist; + int nevents; + ssize_t result; + int res; + + struct msghdr cred_hdr; + struct iovec iov; + + struct { + struct cmsghdr hdr; + struct cmsgcred creds; + } cmsg; + + memset(&cmsg, 0, sizeof(cmsg)); + cmsg.hdr.cmsg_len = sizeof(cmsg); + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_CREDS; + + memset(&cred_hdr, 0, sizeof(struct msghdr)); + cred_hdr.msg_iov = &iov; + cred_hdr.msg_iovlen = 1; + cred_hdr.msg_control = &cmsg; + cred_hdr.msg_controllen = sizeof(cmsg); + + iov.iov_base = &type; + iov.iov_len = sizeof(int); + + EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, + NOTE_LOWAT, sizeof(int), NULL); + res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); + + nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, + NULL); + if (nevents == 1 && eventlist.filter == EVFILT_WRITE) { + result = (_sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? + -1 : 0; + EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, + 0, 0, NULL); + kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); + return (result); + } else + return (-1); +} + +/* + * Opens the connection with the specified params. Initializes all kqueues. + */ +struct cached_connection_ * +__open_cached_connection(struct cached_connection_params const *params) +{ + struct cached_connection_ *retval; + struct kevent eventlist; + struct sockaddr_un client_address; + int client_address_len, client_socket; + int res; + + assert(params != NULL); + + client_socket = _socket(PF_LOCAL, SOCK_STREAM, 0); + client_address.sun_family = PF_LOCAL; + strncpy(client_address.sun_path, params->socket_path, + sizeof(client_address.sun_path)); + client_address_len = sizeof(client_address.sun_family) + + strlen(client_address.sun_path) + 1; + + res = _connect(client_socket, (struct sockaddr *)&client_address, + client_address_len); + if (res == -1) { + _close(client_socket); + return (NULL); + } + _fcntl(client_socket, F_SETFL, O_NONBLOCK); + + retval = malloc(sizeof(struct cached_connection_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cached_connection_)); + + retval->sockfd = client_socket; + + retval->write_queue = kqueue(); + assert(retval->write_queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); + res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL); + + retval->read_queue = kqueue(); + assert(retval->read_queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL); + res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL); + + return (retval); +} + +void +__close_cached_connection(struct cached_connection_ *connection) +{ + assert(connection != NULL); + + _close(connection->sockfd); + _close(connection->read_queue); + _close(connection->write_queue); + free(connection); +} + +/* + * This function is very close to the cache_write function of the caching + * library, which is used in the caching daemon. It caches the data with the + * specified key in the cache entry with entry_name. + */ +int +__cached_write(struct cached_connection_ *connection, const char *entry_name, + const char *key, size_t key_size, const char *data, size_t data_size) +{ + size_t name_size; + int error_code; + int result; + + error_code = -1; + result = 0; + result = send_credentials(connection, CET_WRITE_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &key_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &data_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_write(connection, key, key_size); + if (result != 0) + goto fin; + + result = safe_write(connection, data, data_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + error_code = -1; + +fin: + return (error_code); +} + +/* + * This function is very close to the cache_read function of the caching + * library, which is used in the caching daemon. It reads cached data with the + * specified key from the cache entry with entry_name. + */ +int +__cached_read(struct cached_connection_ *connection, const char *entry_name, + const char *key, size_t key_size, char *data, size_t *data_size) +{ + size_t name_size, result_size; + int error_code, rec_error_code; + int result; + + assert(connection != NULL); + result = 0; + error_code = -1; + + result = send_credentials(connection, CET_READ_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &key_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_write(connection, key, key_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &rec_error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (rec_error_code != 0) { + error_code = rec_error_code; + goto fin; + } + + result = safe_read(connection, &result_size, sizeof(size_t)); + if (result != 0) + goto fin; + + if (result_size > *data_size) { + *data_size = result_size; + error_code = -2; + goto fin; + } + + result = safe_read(connection, data, result_size); + if (result != 0) + goto fin; + + *data_size = result_size; + error_code = 0; + +fin: + return (error_code); +} + +/* + * Initializes the mp_write_session. For such a session the new connection + * would be opened. The data should be written to the session with + * __cached_mp_write function. The __close_cached_mp_write_session function + * should be used to submit session and __abandon_cached_mp_write_session - to + * abandon it. When the session is submitted, the whole se + */ +struct cached_connection_ * +__open_cached_mp_write_session(struct cached_connection_params const *params, + const char *entry_name) +{ + struct cached_connection_ *connection, *retval; + size_t name_size; + int error_code; + int result; + + retval = NULL; + connection = __open_cached_connection(params); + if (connection == NULL) + return (NULL); + connection->mp_flag = 1; + + result = send_credentials(connection, CET_MP_WRITE_SESSION_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (error_code != 0) + result = error_code; + +fin: + if (result != 0) + __close_cached_connection(connection); + else + retval = connection; + return (retval); +} + +/* + * Adds new portion of data to the opened write session + */ +int +__cached_mp_write(struct cached_connection_ *ws, const char *data, + size_t data_size) +{ + int request, result; + int error_code; + + error_code = -1; + + request = CET_MP_WRITE_SESSION_WRITE_REQUEST; + result = safe_write(ws, &request, sizeof(int)); + if (result != 0) + goto fin; + + result = safe_write(ws, &data_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(ws, data, data_size); + if (result != 0) + goto fin; + + result = safe_read(ws, &error_code, sizeof(int)); + if (result != 0) + error_code = -1; + +fin: + return (error_code); +} + +/* + * Abandons all operations with the write session. All data, that were written + * to the session before, are discarded. + */ +int +__abandon_cached_mp_write_session(struct cached_connection_ *ws) +{ + int notification; + int result; + + notification = CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION; + result = safe_write(ws, ¬ification, sizeof(int)); + __close_cached_connection(ws); + return (result); +} + +/* + * Gracefully closes the write session. The data, that were previously written + * to the session, are committed. + */ +int +__close_cached_mp_write_session(struct cached_connection_ *ws) +{ + int notification; + int result; + + notification = CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION; + result = safe_write(ws, ¬ification, sizeof(int)); + __close_cached_connection(ws); + return (0); +} + +struct cached_connection_ * +__open_cached_mp_read_session(struct cached_connection_params const *params, + const char *entry_name) +{ + struct cached_connection_ *connection, *retval; + size_t name_size; + int error_code; + int result; + + retval = NULL; + connection = __open_cached_connection(params); + if (connection == NULL) + return (NULL); + connection->mp_flag = 1; + + result = send_credentials(connection, CET_MP_READ_SESSION_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (error_code != 0) + result = error_code; + +fin: + if (result != 0) + __close_cached_connection(connection); + else + retval = connection; + return (retval); +} + +int +__cached_mp_read(struct cached_connection_ *rs, char *data, size_t *data_size) +{ + size_t result_size; + int error_code, rec_error_code; + int request, result; + + error_code = -1; + request = CET_MP_READ_SESSION_READ_REQUEST; + result = safe_write(rs, &request, sizeof(int)); + if (result != 0) + goto fin; + + result = safe_read(rs, &rec_error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (rec_error_code != 0) { + error_code = rec_error_code; + goto fin; + } + + result = safe_read(rs, &result_size, sizeof(size_t)); + if (result != 0) + goto fin; + + if (result_size > *data_size) { + *data_size = result_size; + error_code = -2; + goto fin; + } + + result = safe_read(rs, data, result_size); + if (result != 0) + goto fin; + + *data_size = result_size; + error_code = 0; + +fin: + return (error_code); +} + +int +__close_cached_mp_read_session(struct cached_connection_ *rs) +{ + + __close_cached_connection(rs); + return (0); +} diff --git a/lib/libc/net/nsdispatch.c b/lib/libc/net/nsdispatch.c index c9be763..0e3c419 100644 --- a/lib/libc/net/nsdispatch.c +++ b/lib/libc/net/nsdispatch.c @@ -86,6 +86,9 @@ __FBSDID("$FreeBSD$"); #include <syslog.h> #include <unistd.h> #include "un-namespace.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif enum _nss_constants { /* Number of elements allocated when we grow a vector */ @@ -124,6 +127,14 @@ static ns_mod *_nsmod; static int __nss_builtin_handle; static void *nss_builtin_handle = &__nss_builtin_handle; +#ifdef NS_CACHING +/* + * Cache lookup cycle prevention function - if !NULL then no cache lookups + * will be made + */ +static void *nss_cache_cycle_prevention_func = NULL; +#endif + /* * Attempt to spew relatively uniform messages to syslog. */ @@ -231,8 +242,6 @@ vector_free(void *vec, unsigned int *count, size_t esize, *count = 0; } - - /* * Comparison functions for vector_search. */ @@ -256,8 +265,6 @@ mtab_compare(const void *a, const void *b) ((const ns_mtab *)b)->database)); } - - /* * NSS nsmap management. */ @@ -318,6 +325,9 @@ nss_configure(void) struct stat statbuf; int result, isthreaded; const char *path; +#ifdef NS_CACHING + void *handle; +#endif result = 0; isthreaded = __isthreaded; @@ -356,6 +366,15 @@ nss_configure(void) if (confmod == 0) (void)atexit(nss_atexit); confmod = statbuf.st_mtime; + +#ifdef NS_CACHING + handle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL); + if (handle != NULL) { + nss_cache_cycle_prevention_func = dlsym(handle, + "_nss_cache_cycle_prevention_function"); + dlclose(handle); + } +#endif fin: if (isthreaded) { (void)_pthread_rwlock_unlock(&nss_lock); @@ -583,6 +602,12 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, void *mdata; int isthreaded, serrno, i, result, srclistsize; +#ifdef NS_CACHING + nss_cache_data cache_data; + nss_cache_data *cache_data_p; + int cache_flag; +#endif + isthreaded = __isthreaded; serrno = errno; if (isthreaded) { @@ -608,18 +633,80 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, while (srclist[srclistsize].name != NULL) srclistsize++; } + +#ifdef NS_CACHING + cache_data_p = NULL; + cache_flag = 0; +#endif for (i = 0; i < srclistsize; i++) { result = NS_NOTFOUND; method = nss_method_lookup(srclist[i].name, database, method_name, disp_tab, &mdata); + if (method != NULL) { +#ifdef NS_CACHING + if (strcmp(srclist[i].name, NSSRC_CACHE) == 0 && + nss_cache_cycle_prevention_func == NULL) { +#ifdef NS_STRICT_LIBC_EID_CHECKING + if (issetugid() != 0) + continue; +#endif + cache_flag = 1; + + memset(&cache_data, 0, sizeof(nss_cache_data)); + cache_data.info = (nss_cache_info const *)mdata; + cache_data_p = &cache_data; + + va_start(ap, defaults); + if (cache_data.info->id_func != NULL) + result = __nss_common_cache_read(retval, + cache_data_p, ap); + else if (cache_data.info->marshal_func != NULL) + result = __nss_mp_cache_read(retval, + cache_data_p, ap); + else + result = __nss_mp_cache_end(retval, + cache_data_p, ap); + va_end(ap); + } else { + cache_flag = 0; + va_start(ap, defaults); + result = method(retval, mdata, ap); + va_end(ap); + } +#else /* NS_CACHING */ va_start(ap, defaults); result = method(retval, mdata, ap); va_end(ap); +#endif /* NS_CACHING */ + if (result & (srclist[i].flags)) break; } } + +#ifdef NS_CACHING + if (cache_data_p != NULL && + (result & (NS_NOTFOUND | NS_SUCCESS)) && cache_flag == 0) { + va_start(ap, defaults); + if (result == NS_SUCCESS) { + if (cache_data.info->id_func != NULL) + __nss_common_cache_write(retval, cache_data_p, + ap); + else if (cache_data.info->marshal_func != NULL) + __nss_mp_cache_write(retval, cache_data_p, ap); + } else if (result == NS_NOTFOUND) { + if (cache_data.info->id_func == NULL) { + if (cache_data.info->marshal_func != NULL) + __nss_mp_cache_write_submit(retval, + cache_data_p, ap); + } else + __nss_common_cache_write_negative(cache_data_p); + } + va_end(ap); + } +#endif /* NS_CACHING */ + if (isthreaded) (void)_pthread_rwlock_unlock(&nss_lock); fin: |