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$"); 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 48 extern int _ht_gethostbyname(void *, void *, va_list); 49 extern int _dns_gethostbyname(void *, void *, va_list); 50 extern int _nis_gethostbyname(void *, void *, va_list); 51 extern int _ht_gethostbyaddr(void *, void *, va_list); 52 extern int _dns_gethostbyaddr(void *, void *, va_list); 53 extern int _nis_gethostbyaddr(void *, void *, va_list); 54 55 static int gethostbyname_internal(const char *, int, struct hostent *, 56 struct hostent_data *); 57 58 /* Host lookup order if nsswitch.conf is broken or nonexistant */ 59 static const ns_src default_src[] = { 60 { NSSRC_FILES, NS_SUCCESS }, 61 { NSSRC_DNS, NS_SUCCESS }, 62 { 0 } 63 }; 64 65 static struct hostdata hostdata; 66 static thread_key_t hostdata_key; 67 static once_t hostdata_init_once = ONCE_INITIALIZER; 68 static int hostdata_thr_keycreated = 0; 69 70 static void 71 hostdata_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 82 static void 83 hostdata_keycreate(void) 84 { 85 hostdata_thr_keycreated = 86 (thr_keycreate(&hostdata_key, hostdata_free) == 0); 87 } 88 89 struct 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 109 int 110 gethostbyname_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 127 int 128 gethostbyname2_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 139 static int 140 gethostbyname_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 254 int 255 gethostbyaddr_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 314 void 315 sethostent_r(int stayopen, struct hostent_data *hed) 316 { 317 _sethosthtent(stayopen, hed); 318 _sethostdnsent(stayopen); 319 } 320 321 void 322 endhostent_r(struct hostent_data *hed) 323 { 324 _endhosthtent(hed); 325 _endhostdnsent(); 326 } 327 328 struct hostent * 329 gethostbyname(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 340 struct hostent * 341 gethostbyname2(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 352 struct hostent * 353 gethostbyaddr(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 364 void 365 sethostent(int stayopen) 366 { 367 struct hostdata *hd; 368 369 if ((hd = __hostdata_init()) == NULL) 370 return; 371 sethostent_r(stayopen, &hd->data); 372 } 373 374 void 375 endhostent(void) 376 { 377 struct hostdata *hd; 378 379 if ((hd = __hostdata_init()) == NULL) 380 return; 381 endhostent_r(&hd->data); 382 } 383