gethostnamadr.c revision 156960
1/*- 2 * Copyright (c) 1994, Garrett Wollman 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h> 27__FBSDID("$FreeBSD: head/lib/libc/net/gethostnamadr.c 156960 2006-03-21 16:11:11Z ume $"); 28 29#include "namespace.h" 30#include "reentrant.h" 31#include <sys/param.h> 32#include <sys/socket.h> 33#include <netinet/in.h> 34#include <arpa/inet.h> 35#include <netdb.h> 36#include <stdio.h> 37#include <ctype.h> 38#include <errno.h> 39#include <stdlib.h> 40#include <string.h> 41#include <stdarg.h> 42#include <nsswitch.h> 43#include <arpa/nameser.h> /* XXX hack for _res */ 44#include <resolv.h> /* XXX hack for _res */ 45#include "un-namespace.h" 46#include "netdb_private.h" 47 48extern int _ht_gethostbyname(void *, void *, va_list); 49extern int _dns_gethostbyname(void *, void *, va_list); 50extern int _nis_gethostbyname(void *, void *, va_list); 51extern int _ht_gethostbyaddr(void *, void *, va_list); 52extern int _dns_gethostbyaddr(void *, void *, va_list); 53extern int _nis_gethostbyaddr(void *, void *, va_list); 54 55static int gethostbyname_internal(const char *, int, struct hostent *, 56 struct hostent_data *); 57 58/* Host lookup order if nsswitch.conf is broken or nonexistant */ 59static const ns_src default_src[] = { 60 { NSSRC_FILES, NS_SUCCESS }, 61 { NSSRC_DNS, NS_SUCCESS }, 62 { 0 } 63}; 64 65static struct hostdata hostdata; 66static thread_key_t hostdata_key; 67static once_t hostdata_init_once = ONCE_INITIALIZER; 68static int hostdata_thr_keycreated = 0; 69 70static void 71hostdata_free(void *ptr) 72{ 73 struct hostdata *hd = ptr; 74 75 if (hd == NULL) 76 return; 77 hd->data.stayopen = 0; 78 _endhosthtent(&hd->data); 79 free(hd); 80} 81 82static void 83hostdata_keycreate(void) 84{ 85 hostdata_thr_keycreated = 86 (thr_keycreate(&hostdata_key, hostdata_free) == 0); 87} 88 89struct hostdata * 90__hostdata_init(void) 91{ 92 struct hostdata *hd; 93 94 if (thr_main() != 0) 95 return &hostdata; 96 if (thr_once(&hostdata_init_once, hostdata_keycreate) != 0 || 97 !hostdata_thr_keycreated) 98 return NULL; 99 if ((hd = thr_getspecific(hostdata_key)) != NULL) 100 return hd; 101 if ((hd = calloc(1, sizeof(*hd))) == NULL) 102 return NULL; 103 if (thr_setspecific(hostdata_key, hd) == 0) 104 return hd; 105 free(hd); 106 return NULL; 107} 108 109int 110gethostbyname_r(const char *name, struct hostent *he, struct hostent_data *hed) 111{ 112 int error; 113 114 hed->res = __res_state(); 115 if ((hed->res->options & RES_INIT) == 0 && res_ninit(hed->res) == -1) { 116 RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL); 117 return -1; 118 } 119 if (hed->res->options & RES_USE_INET6) { 120 error = gethostbyname_internal(name, AF_INET6, he, hed); 121 if (error == 0) 122 return 0; 123 } 124 return gethostbyname_internal(name, AF_INET, he, hed); 125} 126 127int 128gethostbyname2_r(const char *name, int af, struct hostent *he, 129 struct hostent_data *hed) 130{ 131 hed->res = __res_state(); 132 if ((hed->res->options & RES_INIT) == 0 && res_ninit(hed->res) == -1) { 133 RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL); 134 return -1; 135 } 136 return gethostbyname_internal(name, af, he, hed); 137} 138 139static int 140gethostbyname_internal(const char *name, int af, struct hostent *he, 141 struct hostent_data *hed) 142{ 143 const char *cp; 144 char *bp, *ep; 145 int size, rval; 146 char abuf[MAXDNAME]; 147 148 static const ns_dtab dtab[] = { 149 NS_FILES_CB(_ht_gethostbyname, NULL) 150 { NSSRC_DNS, _dns_gethostbyname, NULL }, 151 NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */ 152 { 0 } 153 }; 154 155 switch (af) { 156 case AF_INET: 157 size = INADDRSZ; 158 break; 159 case AF_INET6: 160 size = IN6ADDRSZ; 161 break; 162 default: 163 RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL); 164 errno = EAFNOSUPPORT; 165 return -1; 166 } 167 168 he->h_addrtype = af; 169 he->h_length = size; 170 171 /* 172 * if there aren't any dots, it could be a user-level alias. 173 * this is also done in res_query() since we are not the only 174 * function that looks up host names. 175 */ 176 if (!strchr(name, '.') && 177 (cp = res_hostalias(hed->res, name, abuf, sizeof abuf))) 178 name = cp; 179 180 /* 181 * disallow names consisting only of digits/dots, unless 182 * they end in a dot. 183 */ 184 if (isdigit((u_char)name[0])) 185 for (cp = name;; ++cp) { 186 if (!*cp) { 187 if (*--cp == '.') 188 break; 189 /* 190 * All-numeric, no dot at the end. 191 * Fake up a hostent as if we'd actually 192 * done a lookup. 193 */ 194 if (inet_pton(af, name, hed->host_addr) <= 0) { 195 RES_SET_H_ERRNO(hed->res, 196 HOST_NOT_FOUND); 197 return -1; 198 } 199 strncpy(hed->hostbuf, name, MAXDNAME); 200 hed->hostbuf[MAXDNAME] = '\0'; 201 bp = hed->hostbuf + MAXDNAME + 1; 202 ep = hed->hostbuf + sizeof hed->hostbuf; 203 he->h_name = hed->hostbuf; 204 he->h_aliases = hed->host_aliases; 205 hed->host_aliases[0] = NULL; 206 hed->h_addr_ptrs[0] = (char *)hed->host_addr; 207 hed->h_addr_ptrs[1] = NULL; 208 he->h_addr_list = hed->h_addr_ptrs; 209 if (hed->res->options & RES_USE_INET6) 210 _map_v4v6_hostent(he, &bp, ep); 211 RES_SET_H_ERRNO(hed->res, NETDB_SUCCESS); 212 return 0; 213 } 214 if (!isdigit((u_char)*cp) && *cp != '.') 215 break; 216 } 217 if ((isxdigit((u_char)name[0]) && strchr(name, ':') != NULL) || 218 name[0] == ':') 219 for (cp = name;; ++cp) { 220 if (!*cp) { 221 if (*--cp == '.') 222 break; 223 /* 224 * All-IPv6-legal, no dot at the end. 225 * Fake up a hostent as if we'd actually 226 * done a lookup. 227 */ 228 if (inet_pton(af, name, hed->host_addr) <= 0) { 229 RES_SET_H_ERRNO(hed->res, 230 HOST_NOT_FOUND); 231 return -1; 232 } 233 strncpy(hed->hostbuf, name, MAXDNAME); 234 hed->hostbuf[MAXDNAME] = '\0'; 235 he->h_name = hed->hostbuf; 236 he->h_aliases = hed->host_aliases; 237 hed->host_aliases[0] = NULL; 238 hed->h_addr_ptrs[0] = (char *)hed->host_addr; 239 hed->h_addr_ptrs[1] = NULL; 240 he->h_addr_list = hed->h_addr_ptrs; 241 RES_SET_H_ERRNO(hed->res, NETDB_SUCCESS); 242 return 0; 243 } 244 if (!isxdigit((u_char)*cp) && *cp != ':' && *cp != '.') 245 break; 246 } 247 248 rval = _nsdispatch(NULL, dtab, NSDB_HOSTS, "gethostbyname", 249 default_src, name, af, he, hed); 250 251 return (rval == NS_SUCCESS) ? 0 : -1; 252} 253 254int 255gethostbyaddr_r(const char *addr, int len, int af, struct hostent *he, 256 struct hostent_data *hed) 257{ 258 const u_char *uaddr = (const u_char *)addr; 259 const struct in6_addr *addr6; 260 socklen_t size; 261 int rval; 262 263 static const ns_dtab dtab[] = { 264 NS_FILES_CB(_ht_gethostbyaddr, NULL) 265 { NSSRC_DNS, _dns_gethostbyaddr, NULL }, 266 NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */ 267 { 0 } 268 }; 269 270 hed->res = __res_state(); 271 if ((hed->res->options & RES_INIT) == 0 && res_ninit(hed->res) == -1) { 272 RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL); 273 return -1; 274 } 275 276 if (af == AF_INET6 && len == IN6ADDRSZ) { 277 addr6 = (const struct in6_addr *)(const void *)uaddr; 278 if (IN6_IS_ADDR_LINKLOCAL(addr6)) { 279 RES_SET_H_ERRNO(hed->res, HOST_NOT_FOUND); 280 return -1; 281 } 282 if (IN6_IS_ADDR_V4MAPPED(addr6) || 283 IN6_IS_ADDR_V4COMPAT(addr6)) { 284 /* Unmap. */ 285 uaddr += IN6ADDRSZ - INADDRSZ; 286 af = AF_INET; 287 len = INADDRSZ; 288 } 289 } 290 switch (af) { 291 case AF_INET: 292 size = INADDRSZ; 293 break; 294 case AF_INET6: 295 size = IN6ADDRSZ; 296 break; 297 default: 298 errno = EAFNOSUPPORT; 299 RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL); 300 return -1; 301 } 302 if (size != len) { 303 errno = EINVAL; 304 RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL); 305 return -1; 306 } 307 308 rval = _nsdispatch(NULL, dtab, NSDB_HOSTS, "gethostbyaddr", 309 default_src, uaddr, len, af, he, hed); 310 311 return (rval == NS_SUCCESS) ? 0 : -1; 312} 313 314void 315sethostent_r(int stayopen, struct hostent_data *hed) 316{ 317 _sethosthtent(stayopen, hed); 318 _sethostdnsent(stayopen); 319} 320 321void 322endhostent_r(struct hostent_data *hed) 323{ 324 _endhosthtent(hed); 325 _endhostdnsent(); 326} 327 328struct hostent * 329gethostbyname(const char *name) 330{ 331 struct hostdata *hd; 332 333 if ((hd = __hostdata_init()) == NULL) 334 return NULL; 335 if (gethostbyname_r(name, &hd->host, &hd->data) != 0) 336 return NULL; 337 return &hd->host; 338} 339 340struct hostent * 341gethostbyname2(const char *name, int af) 342{ 343 struct hostdata *hd; 344 345 if ((hd = __hostdata_init()) == NULL) 346 return NULL; 347 if (gethostbyname2_r(name, af, &hd->host, &hd->data) != 0) 348 return NULL; 349 return &hd->host; 350} 351 352struct hostent * 353gethostbyaddr(const char *addr, int len, int af) 354{ 355 struct hostdata *hd; 356 357 if ((hd = __hostdata_init()) == NULL) 358 return NULL; 359 if (gethostbyaddr_r(addr, len, af, &hd->host, &hd->data) != 0) 360 return NULL; 361 return &hd->host; 362} 363 364void 365sethostent(int stayopen) 366{ 367 struct hostdata *hd; 368 369 if ((hd = __hostdata_init()) == NULL) 370 return; 371 sethostent_r(stayopen, &hd->data); 372} 373 374void 375endhostent(void) 376{ 377 struct hostdata *hd; 378 379 if ((hd = __hostdata_init()) == NULL) 380 return; 381 endhostent_r(&hd->data); 382} 383