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 extern const char *_res_hostalias(const char *, char *, size_t); 55 56 static int gethostbyname_internal(const char *, int, struct hostent *, 57 struct hostent_data *); 58 59 /* Host lookup order if nsswitch.conf is broken or nonexistant */ 60 static const ns_src default_src[] = { 61 { NSSRC_FILES, NS_SUCCESS }, 62 { NSSRC_DNS, NS_SUCCESS }, 63 { 0 } 64 }; 65 66 static struct hostdata hostdata; 67 static thread_key_t hostdata_key; 68 static once_t hostdata_init_once = ONCE_INITIALIZER; 69 static int hostdata_thr_keycreated = 0; 70 71 static void 72 hostdata_free(void *ptr) 73 { 74 struct hostdata *hd = ptr; 75 76 if (hd == NULL) 77 return; 78 hd->data.stayopen = 0; 79 _endhosthtent(&hd->data); 80 free(hd); 81 } 82 83 static void 84 hostdata_keycreate(void) 85 { 86 hostdata_thr_keycreated = 87 (thr_keycreate(&hostdata_key, hostdata_free) == 0); 88 } 89 90 struct hostdata * 91 __hostdata_init(void) 92 { 93 struct hostdata *hd; 94 95 if (thr_main() != 0) 96 return &hostdata; 97 if (thr_once(&hostdata_init_once, hostdata_keycreate) != 0 || 98 !hostdata_thr_keycreated) 99 return NULL; 100 if ((hd = thr_getspecific(hostdata_key)) != NULL) 101 return hd; 102 if ((hd = calloc(1, sizeof(*hd))) == NULL) 103 return NULL; 104 if (thr_setspecific(hostdata_key, hd) == 0) 105 return hd; 106 free(hd); 107 return NULL; 108 } 109 110 int 111 gethostbyname_r(const char *name, struct hostent *he, struct hostent_data *hed) 112 { 113 int error; 114 115 if ((_res.options & RES_INIT) == 0 && res_init() == -1) { 116 h_errno = NETDB_INTERNAL; 117 return -1; 118 } 119 if (_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 if ((_res.options & RES_INIT) == 0 && res_init() == -1) { 132 h_errno = NETDB_INTERNAL; 133 return -1; 134 } 135 return gethostbyname_internal(name, af, he, hed); 136 } 137 138 static int 139 gethostbyname_internal(const char *name, int af, struct hostent *he, 140 struct hostent_data *hed) 141 { 142 const char *cp; 143 char *bp, *ep; 144 int size, rval; 145 char abuf[MAXDNAME]; 146 147 static const ns_dtab dtab[] = { 148 NS_FILES_CB(_ht_gethostbyname, NULL) 149 { NSSRC_DNS, _dns_gethostbyname, NULL }, 150 NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */ 151 { 0 } 152 }; 153 154 switch (af) { 155 case AF_INET: 156 size = INADDRSZ; 157 break; 158 case AF_INET6: 159 size = IN6ADDRSZ; 160 break; 161 default: 162 h_errno = NETDB_INTERNAL; 163 errno = EAFNOSUPPORT; 164 return -1; 165 } 166 167 he->h_addrtype = af; 168 he->h_length = size; 169 170 /* 171 * if there aren't any dots, it could be a user-level alias. 172 * this is also done in res_query() since we are not the only 173 * function that looks up host names. 174 */ 175 if (!strchr(name, '.') && 176 (cp = _res_hostalias(name, abuf, sizeof abuf))) 177 name = cp; 178 179 /* 180 * disallow names consisting only of digits/dots, unless 181 * they end in a dot. 182 */ 183 if (isdigit((u_char)name[0])) 184 for (cp = name;; ++cp) { 185 if (!*cp) { 186 if (*--cp == '.') 187 break; 188 /* 189 * All-numeric, no dot at the end. 190 * Fake up a hostent as if we'd actually 191 * done a lookup. 192 */ 193 if (inet_pton(af, name, hed->host_addr) <= 0) { 194 h_errno = HOST_NOT_FOUND; 195 return -1; 196 } 197 strncpy(hed->hostbuf, name, MAXDNAME); 198 hed->hostbuf[MAXDNAME] = '\0'; 199 bp = hed->hostbuf + MAXDNAME + 1; 200 ep = hed->hostbuf + sizeof hed->hostbuf; 201 he->h_name = hed->hostbuf; 202 he->h_aliases = hed->host_aliases; 203 hed->host_aliases[0] = NULL; 204 hed->h_addr_ptrs[0] = (char *)hed->host_addr; 205 hed->h_addr_ptrs[1] = NULL; 206 he->h_addr_list = hed->h_addr_ptrs; 207 if (_res.options & RES_USE_INET6) 208 _map_v4v6_hostent(he, &bp, ep); 209 h_errno = NETDB_SUCCESS; 210 return 0; 211 } 212 if (!isdigit((u_char)*cp) && *cp != '.') 213 break; 214 } 215 if ((isxdigit((u_char)name[0]) && strchr(name, ':') != NULL) || 216 name[0] == ':') 217 for (cp = name;; ++cp) { 218 if (!*cp) { 219 if (*--cp == '.') 220 break; 221 /* 222 * All-IPv6-legal, no dot at the end. 223 * Fake up a hostent as if we'd actually 224 * done a lookup. 225 */ 226 if (inet_pton(af, name, hed->host_addr) <= 0) { 227 h_errno = HOST_NOT_FOUND; 228 return -1; 229 } 230 strncpy(hed->hostbuf, name, MAXDNAME); 231 hed->hostbuf[MAXDNAME] = '\0'; 232 he->h_name = hed->hostbuf; 233 he->h_aliases = hed->host_aliases; 234 hed->host_aliases[0] = NULL; 235 hed->h_addr_ptrs[0] = (char *)hed->host_addr; 236 hed->h_addr_ptrs[1] = NULL; 237 he->h_addr_list = hed->h_addr_ptrs; 238 h_errno = NETDB_SUCCESS; 239 return 0; 240 } 241 if (!isxdigit((u_char)*cp) && *cp != ':' && *cp != '.') 242 break; 243 } 244 245 rval = _nsdispatch(NULL, dtab, NSDB_HOSTS, "gethostbyname", 246 default_src, name, af, he, hed); 247 248 return (rval == NS_SUCCESS) ? 0 : -1; 249 } 250 251 int 252 gethostbyaddr_r(const char *addr, int len, int af, struct hostent *he, 253 struct hostent_data *hed) 254 { 255 const u_char *uaddr = (const u_char *)addr; 256 const struct in6_addr *addr6; 257 socklen_t size; 258 int rval; 259 260 static const ns_dtab dtab[] = { 261 NS_FILES_CB(_ht_gethostbyaddr, NULL) 262 { NSSRC_DNS, _dns_gethostbyaddr, NULL }, 263 NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */ 264 { 0 } 265 }; 266 267 if ((_res.options & RES_INIT) == 0 && res_init() == -1) { 268 h_errno = NETDB_INTERNAL; 269 return -1; 270 } 271 272 if (af == AF_INET6 && len == IN6ADDRSZ) { 273 addr6 = (const struct in6_addr *)(const void *)uaddr; 274 if (IN6_IS_ADDR_LINKLOCAL(addr6)) { 275 h_errno = HOST_NOT_FOUND; 276 return -1; 277 } 278 if (IN6_IS_ADDR_V4MAPPED(addr6) || 279 IN6_IS_ADDR_V4COMPAT(addr6)) { 280 /* Unmap. */ 281 uaddr += IN6ADDRSZ - INADDRSZ; 282 af = AF_INET; 283 len = INADDRSZ; 284 } 285 } 286 switch (af) { 287 case AF_INET: 288 size = INADDRSZ; 289 break; 290 case AF_INET6: 291 size = IN6ADDRSZ; 292 break; 293 default: 294 errno = EAFNOSUPPORT; 295 h_errno = NETDB_INTERNAL; 296 return -1; 297 } 298 if (size != len) { 299 errno = EINVAL; 300 h_errno = NETDB_INTERNAL; 301 return -1; 302 } 303 304 rval = _nsdispatch(NULL, dtab, NSDB_HOSTS, "gethostbyaddr", 305 default_src, uaddr, len, af, he, hed); 306 307 return (rval == NS_SUCCESS) ? 0 : -1; 308 } 309 310 void 311 sethostent_r(int stayopen, struct hostent_data *hed) 312 { 313 _sethosthtent(stayopen, hed); 314 _sethostdnsent(stayopen); 315 } 316 317 void 318 endhostent_r(struct hostent_data *hed) 319 { 320 _endhosthtent(hed); 321 _endhostdnsent(); 322 } 323 324 struct hostent * 325 gethostbyname(const char *name) 326 { 327 struct hostdata *hd; 328 329 if ((hd = __hostdata_init()) == NULL) 330 return NULL; 331 if (gethostbyname_r(name, &hd->host, &hd->data) != 0) 332 return NULL; 333 return &hd->host; 334 } 335 336 struct hostent * 337 gethostbyname2(const char *name, int af) 338 { 339 struct hostdata *hd; 340 341 if ((hd = __hostdata_init()) == NULL) 342 return NULL; 343 if (gethostbyname2_r(name, af, &hd->host, &hd->data) != 0) 344 return NULL; 345 return &hd->host; 346 } 347 348 struct hostent * 349 gethostbyaddr(const char *addr, int len, int af) 350 { 351 struct hostdata *hd; 352 353 if ((hd = __hostdata_init()) == NULL) 354 return NULL; 355 if (gethostbyaddr_r(addr, len, af, &hd->host, &hd->data) != 0) 356 return NULL; 357 return &hd->host; 358 } 359 360 void 361 sethostent(int stayopen) 362 { 363 struct hostdata *hd; 364 365 if ((hd = __hostdata_init()) == NULL) 366 return; 367 sethostent_r(stayopen, &hd->data); 368 } 369 370 void 371 endhostent(void) 372 { 373 struct hostdata *hd; 374 375 if ((hd = __hostdata_init()) == NULL) 376 return; 377 endhostent_r(&hd->data); 378 } 379