| /* |
| * This file is part of the UCB release of Plan 9. It is subject to the license |
| * terms in the LICENSE file found in the top-level directory of this |
| * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No |
| * part of the UCB release of Plan 9, including this file, may be copied, |
| * modified, propagated, or distributed except according to the terms contained |
| * in the LICENSE file. |
| */ |
| |
| /* posix */ |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| /* bsd extensions */ |
| #include <sys/uio.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <netdb.h> |
| #include <arpa/inet.h> |
| |
| #include <sys/plan9_helpers.h> |
| |
| enum { |
| Nname = 6, |
| }; |
| |
| int __gethostbyname2_r(const char *name, int af, struct hostent *ret, |
| char *buf, size_t buflen, |
| struct hostent **result, int *h_errnop) |
| { |
| int i, t, fd, m; |
| char *p, *bp; |
| int nn, na; |
| unsigned long x; |
| size_t csmsg_len = 0; |
| /* These three used to be: |
| static char *nptr[Nname + 1]; |
| static char *aptr[Nname + 1]; |
| static char addr[Nname][4]; |
| * we need to use space in buf for them */ |
| char **nptr, **aptr; |
| char (*addr)[4]; |
| size_t nptr_sz, aptr_sz, addr_sz; |
| nptr_sz = sizeof(char *) * (Nname + 1); |
| aptr_sz = sizeof(char *) * (Nname + 1); |
| addr_sz = sizeof(char[Nname][4]); |
| |
| if (nptr_sz + aptr_sz + addr_sz >= buflen) { |
| *result = 0; |
| return -ERANGE; |
| } |
| nptr = buf; buf += nptr_sz; buflen -= nptr_sz; |
| aptr = buf; buf += aptr_sz; buflen -= aptr_sz; |
| addr = buf; buf += addr_sz; buflen -= addr_sz; |
| |
| /* for inet addresses only */ |
| if (af != AF_INET) { |
| *result = 0; |
| return -EAFNOSUPPORT; |
| } |
| |
| ret->h_name = 0; |
| t = _sock_ipattr(name); |
| |
| /* connect to server */ |
| fd = open("/net/cs", O_RDWR); |
| if (fd < 0) { |
| *h_errnop = NO_RECOVERY; |
| *result = 0; |
| return -errno; |
| } |
| |
| /* construct the query, always expect an ip# back */ |
| switch (t) { |
| case Tsys: |
| csmsg_len = snprintf(buf, buflen, "!sys=%s ip=*", name); |
| break; |
| case Tdom: |
| csmsg_len = snprintf(buf, buflen, "!dom=%s ip=*", name); |
| break; |
| case Tip: |
| csmsg_len = snprintf(buf, buflen, "!ip=%s", name); |
| break; |
| default: |
| /* we can't get here, but want to be safe for changes to |
| * _sock_ipattr() */ |
| close(fd); |
| *result = 0; |
| return -EINVAL; |
| } |
| /* we don't update buflen, since we're just going to reuse the space |
| * after our nptr/aptr/addr/etc. */ |
| if (csmsg_len >= buflen) { |
| close(fd); |
| *result = 0; |
| return -ERANGE; |
| } |
| /* query the server */ |
| if (write(fd, buf, csmsg_len) < 0) { |
| *h_errnop = TRY_AGAIN; |
| close(fd); |
| *result = 0; |
| return -1; |
| } |
| lseek(fd, 0, 0); |
| for (i = 0; i < buflen - 1; i += m) { |
| m = read(fd, buf + i, buflen - 1 - i); |
| if (m <= 0) |
| break; |
| buf[i + m++] = ' '; |
| } |
| close(fd); |
| buf[i] = 0; |
| |
| /* parse the reply */ |
| nn = na = 0; |
| for (bp = buf;;) { |
| p = strchr(bp, '='); |
| if (p == 0) |
| break; |
| *p++ = 0; |
| if (strcmp(bp, "dom") == 0) { |
| if (ret->h_name == 0) |
| ret->h_name = p; |
| if (nn < Nname) |
| nptr[nn++] = p; |
| } else if (strcmp(bp, "sys") == 0) { |
| if (nn < Nname) |
| nptr[nn++] = p; |
| } else if (strcmp(bp, "ip") == 0) { |
| x = inet_addr(p); |
| x = ntohl(x); |
| if (na < Nname) { |
| addr[na][0] = x >> 24; |
| addr[na][1] = x >> 16; |
| addr[na][2] = x >> 8; |
| addr[na][3] = x; |
| aptr[na] = addr[na]; |
| na++; |
| } |
| } |
| while (*p && *p != ' ') |
| p++; |
| if (*p) |
| *p++ = 0; |
| bp = p; |
| } |
| if (nn + na == 0) { |
| *h_errnop = HOST_NOT_FOUND; |
| *result = 0; |
| return -1; |
| } |
| |
| nptr[nn] = 0; |
| aptr[na] = 0; |
| ret->h_aliases = nptr; |
| ret->h_addr_list = aptr; |
| ret->h_length = 4; |
| ret->h_addrtype = AF_INET; |
| if (ret->h_name == 0) |
| ret->h_name = nptr[0]; |
| if (ret->h_name == 0) |
| ret->h_name = aptr[0]; |
| |
| *result = ret; |
| return 0; |
| } |
| weak_alias(__gethostbyname2_r, gethostbyname2_r); |