| /* Copyright (C) 1997-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| Contributed by H.J. Lu <hjl@gnu.ai.mit.edu>, 1997. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <wctype.h> |
| #include <resolv.h> |
| #include <netdb.h> |
| #include <arpa/inet.h> |
| #include "nsswitch.h" |
| |
| #ifdef USE_NSCD |
| # define inet_aton __inet_aton |
| # include <nscd/nscd_proto.h> |
| #endif |
| |
| int |
| __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, |
| char **buffer, size_t *buffer_size, |
| size_t buflen, struct hostent **result, |
| enum nss_status *status, int af, int *h_errnop) |
| { |
| int save; |
| |
| /* We have to test for the use of IPv6 which can only be done by |
| examining `_res'. */ |
| if (__res_maybe_init (&_res, 0) == -1) |
| { |
| if (h_errnop) |
| *h_errnop = NETDB_INTERNAL; |
| if (buffer_size == NULL) |
| *status = NSS_STATUS_TRYAGAIN; |
| else |
| *result = NULL; |
| return -1; |
| } |
| |
| /* |
| * disallow names consisting only of digits/dots, unless |
| * they end in a dot. |
| */ |
| if (isdigit (name[0]) || isxdigit (name[0]) || name[0] == ':') |
| { |
| const char *cp; |
| char *hostname; |
| typedef unsigned char host_addr_t[16]; |
| host_addr_t *host_addr; |
| typedef char *host_addr_list_t[2]; |
| host_addr_list_t *h_addr_ptrs; |
| char **h_alias_ptr; |
| size_t size_needed; |
| int addr_size; |
| |
| switch (af) |
| { |
| case AF_INET: |
| addr_size = INADDRSZ; |
| break; |
| |
| case AF_INET6: |
| addr_size = IN6ADDRSZ; |
| break; |
| |
| default: |
| af = (_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET; |
| addr_size = af == AF_INET6 ? IN6ADDRSZ : INADDRSZ; |
| break; |
| } |
| |
| size_needed = (sizeof (*host_addr) |
| + sizeof (*h_addr_ptrs) |
| + sizeof (*h_alias_ptr) + strlen (name) + 1); |
| |
| if (buffer_size == NULL) |
| { |
| if (buflen < size_needed) |
| { |
| *status = NSS_STATUS_TRYAGAIN; |
| if (h_errnop != NULL) |
| *h_errnop = NETDB_INTERNAL; |
| __set_errno (ERANGE); |
| goto done; |
| } |
| } |
| else if (buffer_size != NULL && *buffer_size < size_needed) |
| { |
| char *new_buf; |
| *buffer_size = size_needed; |
| new_buf = (char *) realloc (*buffer, *buffer_size); |
| |
| if (new_buf == NULL) |
| { |
| save = errno; |
| free (*buffer); |
| *buffer = NULL; |
| *buffer_size = 0; |
| __set_errno (save); |
| if (h_errnop != NULL) |
| *h_errnop = NETDB_INTERNAL; |
| *result = NULL; |
| goto done; |
| } |
| *buffer = new_buf; |
| } |
| |
| memset (*buffer, '\0', size_needed); |
| |
| host_addr = (host_addr_t *) *buffer; |
| h_addr_ptrs = (host_addr_list_t *) |
| ((char *) host_addr + sizeof (*host_addr)); |
| h_alias_ptr = (char **) ((char *) h_addr_ptrs + sizeof (*h_addr_ptrs)); |
| hostname = (char *) h_alias_ptr + sizeof (*h_alias_ptr); |
| |
| if (isdigit (name[0])) |
| { |
| for (cp = name;; ++cp) |
| { |
| if (*cp == '\0') |
| { |
| int ok; |
| |
| if (*--cp == '.') |
| break; |
| |
| /* All-numeric, no dot at the end. Fake up a hostent as if |
| we'd actually done a lookup. What if someone types |
| 255.255.255.255? The test below will succeed |
| spuriously... ??? */ |
| if (af == AF_INET) |
| ok = __inet_aton (name, (struct in_addr *) host_addr); |
| else |
| { |
| assert (af == AF_INET6); |
| ok = inet_pton (af, name, host_addr) > 0; |
| } |
| if (! ok) |
| { |
| *h_errnop = HOST_NOT_FOUND; |
| if (buffer_size == NULL) |
| *status = NSS_STATUS_NOTFOUND; |
| else |
| *result = NULL; |
| goto done; |
| } |
| |
| resbuf->h_name = strcpy (hostname, name); |
| h_alias_ptr[0] = NULL; |
| resbuf->h_aliases = h_alias_ptr; |
| (*h_addr_ptrs)[0] = (char *) host_addr; |
| (*h_addr_ptrs)[1] = NULL; |
| resbuf->h_addr_list = *h_addr_ptrs; |
| if (af == AF_INET && (_res.options & RES_USE_INET6)) |
| { |
| /* We need to change the IP v4 address into the |
| IP v6 address. */ |
| char tmp[INADDRSZ]; |
| char *p = (char *) host_addr; |
| int i; |
| |
| /* Save a copy of the IP v4 address. */ |
| memcpy (tmp, host_addr, INADDRSZ); |
| /* Mark this ipv6 addr as a mapped ipv4. */ |
| for (i = 0; i < 10; i++) |
| *p++ = 0x00; |
| *p++ = 0xff; |
| *p++ = 0xff; |
| /* Copy the IP v4 address. */ |
| memcpy (p, tmp, INADDRSZ); |
| resbuf->h_addrtype = AF_INET6; |
| resbuf->h_length = IN6ADDRSZ; |
| } |
| else |
| { |
| resbuf->h_addrtype = af; |
| resbuf->h_length = addr_size; |
| } |
| if (h_errnop != NULL) |
| *h_errnop = NETDB_SUCCESS; |
| if (buffer_size == NULL) |
| *status = NSS_STATUS_SUCCESS; |
| else |
| *result = resbuf; |
| goto done; |
| } |
| |
| if (!isdigit (*cp) && *cp != '.') |
| break; |
| } |
| } |
| |
| if ((isxdigit (name[0]) && strchr (name, ':') != NULL) || name[0] == ':') |
| { |
| switch (af) |
| { |
| default: |
| af = (_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET; |
| if (af == AF_INET6) |
| { |
| addr_size = IN6ADDRSZ; |
| break; |
| } |
| /* FALLTHROUGH */ |
| |
| case AF_INET: |
| /* This is not possible. We cannot represent an IPv6 address |
| in an `struct in_addr' variable. */ |
| *h_errnop = HOST_NOT_FOUND; |
| if (buffer_size == NULL) |
| *status = NSS_STATUS_NOTFOUND; |
| else |
| *result = NULL; |
| goto done; |
| |
| case AF_INET6: |
| addr_size = IN6ADDRSZ; |
| break; |
| } |
| |
| for (cp = name;; ++cp) |
| { |
| if (!*cp) |
| { |
| if (*--cp == '.') |
| break; |
| |
| /* All-IPv6-legal, no dot at the end. Fake up a |
| hostent as if we'd actually done a lookup. */ |
| if (inet_pton (AF_INET6, name, host_addr) <= 0) |
| { |
| *h_errnop = HOST_NOT_FOUND; |
| if (buffer_size == NULL) |
| *status = NSS_STATUS_NOTFOUND; |
| else |
| *result = NULL; |
| goto done; |
| } |
| |
| resbuf->h_name = strcpy (hostname, name); |
| h_alias_ptr[0] = NULL; |
| resbuf->h_aliases = h_alias_ptr; |
| (*h_addr_ptrs)[0] = (char *) host_addr; |
| (*h_addr_ptrs)[1] = (char *) 0; |
| resbuf->h_addr_list = *h_addr_ptrs; |
| resbuf->h_addrtype = AF_INET6; |
| resbuf->h_length = addr_size; |
| *h_errnop = NETDB_SUCCESS; |
| if (buffer_size == NULL) |
| *status = NSS_STATUS_SUCCESS; |
| else |
| *result = resbuf; |
| goto done; |
| } |
| |
| if (!isxdigit (*cp) && *cp != ':' && *cp != '.') |
| break; |
| } |
| } |
| } |
| |
| return 0; |
| |
| done: |
| return 1; |
| } |
| libc_hidden_def (__nss_hostname_digits_dots) |