1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * files/gethostent.c -- "files" backend for nsswitch "hosts" database 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <netdb.h> 32 #include "files_common.h" 33 #include <string.h> 34 #include <strings.h> 35 #include <stddef.h> 36 #include <stdlib.h> 37 #include <sys/types.h> 38 #include <sys/socket.h> 39 #include <netinet/in.h> 40 #include <arpa/nameser.h> 41 #include <ctype.h> 42 43 static int check_name(); 44 static char *do_aliases(); 45 static char *strcasestr(); 46 nss_status_t __nss_files_XY_hostbyname(); 47 int __nss_files_2herrno(); 48 49 static int 50 check_name(host, args) 51 struct hostent *host; 52 nss_XbyY_args_t *args; 53 { 54 const char *name = args->key.name; 55 char **aliasp; 56 57 if (!host->h_name) 58 return (0); 59 if (strcasecmp(host->h_name, name) == 0) { 60 return (1); 61 } 62 for (aliasp = host->h_aliases; *aliasp != 0; aliasp++) { 63 if (strcasecmp(*aliasp, name) == 0) { 64 return (1); 65 } 66 } 67 return (0); 68 } 69 70 static nss_status_t 71 getbyname(be, a) 72 files_backend_ptr_t be; 73 void *a; 74 { 75 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 76 nss_status_t res; 77 78 res = __nss_files_XY_hostbyname(be, argp, argp->key.name, AF_INET); 79 if (res != NSS_SUCCESS) 80 argp->h_errno = __nss_files_2herrno(res); 81 return (res); 82 } 83 84 85 int 86 __nss_files_check_addr(argp) 87 nss_XbyY_args_t *argp; 88 { 89 struct hostent *host = (struct hostent *)argp->returnval; 90 91 /* 92 * We know that /etc/hosts can only store one address per host, so... 93 */ 94 return (host->h_length == argp->key.hostaddr.len && 95 host->h_addrtype == argp->key.hostaddr.type && 96 memcmp(host->h_addr_list[0], argp->key.hostaddr.addr, 97 argp->key.hostaddr.len) == 0); 98 } 99 100 101 static nss_status_t 102 getbyaddr(be, a) 103 files_backend_ptr_t be; 104 void *a; 105 { 106 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 107 nss_status_t res; 108 109 res = _nss_files_XY_all(be, argp, 1, 0, __nss_files_check_addr); 110 if (res != NSS_SUCCESS) 111 argp->h_errno = __nss_files_2herrno(res); 112 return (res); 113 } 114 115 116 static files_backend_op_t host_ops[] = { 117 _nss_files_destr, 118 _nss_files_endent, 119 _nss_files_setent, 120 _nss_files_getent_netdb, 121 getbyname, 122 getbyaddr, 123 }; 124 125 /*ARGSUSED*/ 126 nss_backend_t * 127 _nss_files_hosts_constr(dummy1, dummy2, dummy3) 128 const char *dummy1, *dummy2, *dummy3; 129 { 130 return (_nss_files_constr(host_ops, 131 sizeof (host_ops) / sizeof (host_ops[0]), 132 _PATH_HOSTS, 133 NSS_LINELEN_HOSTS, 134 NULL)); 135 } 136 137 138 /* 139 * XXX - this duplicates code from files_common.c because we need to keep 140 * going after we've found a match to satisfy the multihomed host case. 141 */ 142 nss_status_t 143 __nss_files_XY_hostbyname(be, args, filter, type) 144 files_backend_ptr_t be; 145 nss_XbyY_args_t *args; 146 const char *filter; /* hint for name string */ 147 int type; 148 { 149 nss_status_t res; 150 int parsestat; 151 char *first; 152 char *last; 153 int i, nhosts = 0; 154 struct hostent he, *hp, *thp; 155 in_addr_t taddr[MAXADDRS]; 156 struct in6_addr taddr6[MAXADDRS]; 157 char *abuf = 0; /* alias buffer */ 158 char *abuf_start = 0, *abuf_end; 159 int (*func)(); 160 161 if (be->buf == 0 && 162 (be->buf = malloc(be->minbuf)) == 0) { 163 return (NSS_UNAVAIL); 164 } 165 166 if (be->f == 0) { 167 if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) 168 return (res); 169 } 170 171 res = NSS_NOTFOUND; 172 args->erange = 0; 173 args->returnval = (char *)0; 174 hp = thp = (struct hostent *)args->buf.result; 175 176 for (;;) { 177 char *instr = be->buf; 178 int linelen; 179 180 if ((linelen = _nss_files_read_line(be->f, 181 instr, be->minbuf)) < 0) { 182 break; /* EOF */ 183 } 184 185 /* 186 * This check avoids a malloc()/free() for the common 187 * case. Also, if we're trying to match an alias and an 188 * already matched entry doesn't share a canonical name 189 * with the current one, bail. 190 */ 191 if (nhosts == 0 && strcasestr(instr, filter) == 0) { 192 continue; 193 } 194 195 if ((last = strchr(instr, '#')) == 0) 196 last = instr + linelen; 197 *last-- = '\0'; 198 for (first = instr; isspace(*first); first++) 199 ; 200 /* Ignore blank and comment lines */ 201 if (*first == '\0') 202 continue; 203 204 while (isspace(*last)) 205 --last; 206 linelen = last - first + 1; 207 if (first != instr) 208 instr = first; 209 210 if (nhosts && strcasestr(instr, hp->h_name) == 0) { 211 break; 212 } 213 /* 214 * If we've already matched once and have a possible match 215 * on this line, copy the aliases where they're safe from 216 * being overwritten when we look at the next entry. They're 217 * saved as a string of blank separated names for the alias 218 * parser. On errors, we return failure whether or not we 219 * have already obtained a valid address. 220 */ 221 if (nhosts == 1 && !abuf) { 222 abuf = malloc(args->buf.buflen); 223 if (abuf == NULL) { 224 res = NSS_UNAVAIL; 225 break; 226 } 227 abuf_start = &abuf[0]; 228 abuf_end = abuf_start + args->buf.buflen; 229 if (abuf + strlen(hp->h_name) + 1 > abuf_end) { 230 free(abuf_start); 231 abuf = NULL; 232 args->erange = 1; 233 res = NSS_NOTFOUND; 234 break; 235 } 236 (void) strcpy(abuf, hp->h_name); 237 abuf += strlen(hp->h_name); 238 *abuf++ = ' '; 239 abuf = do_aliases(hp, abuf, abuf_start, abuf_end); 240 if (abuf == NULL) { 241 args->erange = 1; 242 res = NSS_NOTFOUND; 243 break; 244 } 245 } 246 func = args->str2ent; 247 parsestat = (*func)(instr, linelen, thp, 248 args->buf.buffer, args->buf.buflen); 249 250 if (parsestat != NSS_STR_PARSE_SUCCESS) { 251 if (parsestat == NSS_STR_PARSE_ERANGE) 252 args->erange = 1; 253 continue; 254 } 255 256 /* 257 * Still need to check, strcasestr() above is just a hint. 258 */ 259 260 if (type == thp->h_addrtype) 261 if (check_name(thp, args)) { 262 if (type == AF_INET) 263 taddr[nhosts++] = 264 (*(in_addr_t *)thp->h_addr_list[0]); 265 else { 266 memcpy(&taddr6[nhosts++], thp->h_addr_list[0], 267 sizeof (struct in6_addr)); 268 } 269 270 271 if (nhosts == 1) { 272 res = NSS_SUCCESS; 273 args->returnval = args->buf.result; 274 thp = &he; /* switch to tmp hostent */ 275 continue; 276 } 277 if (nhosts >= MAXADDRS) 278 break; 279 abuf = do_aliases(thp, abuf, abuf_start, abuf_end); 280 if (abuf == NULL) { 281 args->erange = 1; 282 res = NSS_NOTFOUND; 283 break; 284 } 285 } else if (abuf && 286 strcasecmp(hp->h_name, thp->h_name) == 0) { 287 /* 288 * This line didn't have the requested name but 289 * is part of the same multihomed host (i.e. it 290 * has the same canonical name as the previous 291 * line), so march on... 292 */ 293 continue; 294 } else if (nhosts) { 295 break; 296 } 297 } 298 299 if (abuf) { 300 struct in_addr *addrp; 301 struct in6_addr *addrp6; 302 303 if (type == AF_INET) { 304 addrp = (struct in_addr *)(ROUND_DOWN(args->buf.buffer + 305 args->buf.buflen, sizeof (*addrp))); 306 hp->h_addr_list = (char **)(ROUND_DOWN(addrp - 307 ((nhosts + 1) * sizeof (char *) + 308 (nhosts * sizeof (*addrp))), sizeof (char *))); 309 for (i = 0, --addrp; i < nhosts; i++, --addrp) { 310 (*(in_addr_t *)addrp) = taddr[i]; 311 hp->h_addr_list[i] = (char *)addrp; 312 } 313 } else { 314 addrp6 = (struct in6_addr *) 315 (ROUND_DOWN(args->buf.buffer + args->buf.buflen, 316 sizeof (*addrp6))); 317 hp->h_addr_list = (char **)(ROUND_DOWN(addrp6 - 318 ((nhosts + 1) * sizeof (char *) + 319 (nhosts * sizeof (*addrp6))), sizeof (char *))); 320 for (i = 0, --addrp6; i < nhosts; i++, --addrp6) { 321 memcpy(addrp6, &taddr6[i], 322 sizeof (struct in6_addr)); 323 hp->h_addr_list[i] = (char *)addrp6; 324 } 325 } 326 327 hp->h_addr_list[nhosts] = 0; 328 hp->h_aliases = _nss_netdb_aliases(abuf_start, 329 abuf - abuf_start, args->buf.buffer, 330 (char *)hp->h_addr_list - args->buf.buffer); 331 if (hp->h_aliases == 0) { 332 args->erange = 1; 333 res = NSS_STR_PARSE_ERANGE; 334 } else { 335 hp->h_name = hp->h_aliases[0]; 336 hp->h_aliases++; 337 } 338 free(abuf_start); 339 } 340 341 /* 342 * stayopen is set to 0 by default in order to close the opened 343 * file. Some applications may break if it is set to 1. 344 */ 345 if (!args->stayopen) 346 (void) _nss_files_endent(be, 0); 347 348 return (res); 349 } 350 351 /* 352 * A case-insensitive version of strstr(). 353 */ 354 static char * 355 strcasestr(as1, as2) 356 char *as1; 357 char *as2; 358 { 359 int c2; 360 register char *tptr; 361 register char *s1, *s2; 362 363 s1 = as1; 364 s2 = as2; 365 366 if (s2 == NULL || *s2 == '\0') 367 return (0); 368 369 while (*s1) { 370 if (tolower(*s1++) == tolower(c2 = *s2)) { 371 tptr = s1; 372 while ((tolower(c2 = *++s2) == 373 tolower(*s1++)) && c2 != 0) 374 ; 375 if (c2 == 0) 376 return ((char *)tptr - 1); 377 s1 = tptr; 378 s2 = as2; 379 } 380 } 381 return (0); 382 } 383 384 385 static char * 386 do_aliases(hp, abuf, start, end) 387 struct hostent *hp; 388 char *abuf; 389 char *start; 390 char *end; 391 { 392 char **cp; 393 394 for (cp = hp->h_aliases; cp && *cp && **cp; cp++) { 395 size_t len; 396 397 len = strlen(*cp); 398 if (abuf+len+1 >= end) { 399 free(start); 400 return ((char *)0); 401 } 402 (void) strcpy(abuf, *cp); 403 abuf += len; 404 *abuf++ = ' '; 405 } 406 *abuf = '\0'; 407 408 return (abuf); 409 } 410 411 412 /* 413 * This is a copy of a routine in libnsl/nss/netdir_inet.c. It is 414 * here because /etc/lib/nss_files.so.1 cannot call routines 415 * in libnsl. Care should be taken to keep the two copies in sync. 416 */ 417 int 418 __nss_files_2herrno(nsstat) 419 nss_status_t nsstat; 420 { 421 switch (nsstat) { 422 case NSS_SUCCESS: 423 /* no macro-defined success code for h_errno */ 424 return (0); 425 case NSS_NOTFOUND: 426 return (HOST_NOT_FOUND); 427 case NSS_TRYAGAIN: 428 return (TRY_AGAIN); 429 case NSS_UNAVAIL: 430 return (NO_RECOVERY); 431 } 432 /* anything else */ 433 return (NO_RECOVERY); 434 } 435