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 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * Ye olde non-reentrant interface (MT-unsafe, caveat utor) 27 * 28 * lib/libnsl/nss/gethostent.c 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <stdlib.h> 34 #include <ctype.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <netdb.h> 38 #include <stdio.h> 39 #include <arpa/inet.h> 40 #include <nss_dbdefs.h> 41 #include <rpc/trace.h> 42 #include <netinet/in.h> 43 #include <sys/socket.h> 44 45 /* 46 * Still just a global. If you want per-thread h_errno, 47 * use the reentrant interfaces (gethostbyname_r et al) 48 */ 49 int h_errno; 50 51 #ifdef NSS_INCLUDE_UNSAFE 52 53 /* 54 * Don't free this, even on an endhostent(), because bitter experience shows 55 * that there's production code that does getXXXbyYYY(), then endXXXent(), 56 * and then continues to use the pointer it got back. 57 */ 58 static nss_XbyY_buf_t *buffer; 59 #define GETBUF() \ 60 NSS_XbyY_ALLOC(&buffer, sizeof (struct hostent), NSS_BUFLEN_HOSTS) 61 /* === ?? set ENOMEM on failure? */ 62 63 struct hostent * 64 gethostbyname(const char *nam) 65 { 66 nss_XbyY_buf_t *b; 67 struct hostent *res = 0; 68 69 trace1(TR_gethostbyname, 0); 70 if ((b = GETBUF()) != 0) { 71 res = gethostbyname_r(nam, 72 b->result, b->buffer, b->buflen, 73 &h_errno); 74 } 75 trace1(TR_gethostbyname, 1); 76 return (res); 77 } 78 79 struct hostent * 80 gethostbyaddr(const void *addr, socklen_t len, int type) 81 { 82 nss_XbyY_buf_t *b; 83 struct hostent *res = 0; 84 char *c; 85 86 trace2(TR_gethostbyaddr, 0, len); 87 h_errno = 0; 88 if (type == AF_INET6) 89 return (getipnodebyaddr(addr, len, type, &h_errno)); 90 91 if ((b = GETBUF()) != 0) { 92 res = gethostbyaddr_r(addr, len, type, 93 b->result, b->buffer, b->buflen, 94 &h_errno); 95 } 96 trace2(TR_gethostbyaddr, 1, len); 97 return (res); 98 } 99 100 struct hostent * 101 gethostent(void) 102 { 103 nss_XbyY_buf_t *b; 104 struct hostent *res = 0; 105 106 trace1(TR_gethostent, 0); 107 if ((b = GETBUF()) != 0) { 108 res = gethostent_r(b->result, b->buffer, b->buflen, &h_errno); 109 } 110 trace1(TR_gethostent, 1); 111 return (res); 112 } 113 114 /* 115 * Return values: 0 = success, 1 = parse error, 2 = erange ... 116 * The structure pointer passed in is a structure in the caller's space 117 * wherein the field pointers would be set to areas in the buffer if 118 * need be. instring and buffer should be separate areas. 119 */ 120 int 121 __str2hostent(int af, const char *instr, int lenstr, void *ent, char *buffer, 122 int buflen) 123 { 124 struct hostent *host = (struct hostent *)ent; 125 const char *p, *addrstart, *limit; 126 int naddr, i, aliases_erange = 0; 127 int addrlen, res; 128 char addrbuf[100]; /* Why 100? */ 129 struct in_addr *addrp; 130 struct in6_addr *addrp6; 131 char **addrvec; 132 133 trace3(TR_str2hostent, 0, lenstr, buflen); 134 if ((instr >= buffer && (buffer + buflen) > instr) || 135 (buffer >= instr && (instr + lenstr) > buffer)) { 136 trace3(TR_str2hostent, 1, lenstr, buflen); 137 return (NSS_STR_PARSE_PARSE); 138 } 139 if (af != AF_INET && af != AF_INET6) { 140 trace3(TR_str2hostent, 1, lenstr, buflen); 141 /* 142 * XXX - Returning ERANGE here is completely bogus. 143 * Unfortunately, there's no error code identifying 144 * bogus calls from the backend (and nothing the user 145 * can do about our bugs anyway). 146 */ 147 return (NSS_STR_PARSE_ERANGE); 148 } 149 150 /* 151 * The DNS-via-YP code returns multiple lines for a key. 152 * Normal YP return values do not contain newlines (nor do 153 * lines from /etc/hosts or other sources) 154 * We count the number of newlines; this should give us 155 * the number of IP addresses specified. 156 * We'll also call the aliases code and instruct it to 157 * stop at the first newline as the remaining lines will 158 * all contain the same hostname/aliases (no aliases, unfortunately). 159 * 160 * When confronted with a string with embedded newlines, 161 * this code will take the hostname/aliases on the first line 162 * and each of the IP addresses at the start of all lines. 163 * Because the NIS protocol limits return values to 1024 bytes, 164 * we still do not get all addresses. If you want to fix 165 * that problem, do not look here. 166 */ 167 168 p = instr; 169 170 /* Strip trailing newlines */ 171 while (lenstr > 0 && p[lenstr - 1] == '\n') 172 lenstr--; 173 174 naddr = 1; 175 limit = p + lenstr; 176 177 for (; p < limit && (p = memchr(p, '\n', limit - p)); p++) 178 naddr++; 179 180 /* Allocate space for naddr addresses and h_addr_list */ 181 182 if (af == AF_INET6) { 183 addrp6 = (struct in6_addr *)ROUND_DOWN(buffer + buflen, 184 sizeof (*addrp6)); 185 addrp6 -= naddr; 186 addrvec = (char **)ROUND_DOWN(addrp6, sizeof (*addrvec)); 187 addrvec -= naddr + 1; 188 } else { 189 addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen, 190 sizeof (*addrp)); 191 addrp -= naddr; 192 addrvec = (char **)ROUND_DOWN(addrp, sizeof (*addrvec)); 193 addrvec -= naddr + 1; 194 } 195 196 if ((char *)addrvec < buffer) { 197 trace3(TR_str2hostent, 1, lenstr, buflen); 198 return (NSS_STR_PARSE_ERANGE); 199 } 200 201 /* For each addr, parse and get it */ 202 203 p = instr; 204 205 for (i = 0; i < naddr; i ++) { 206 207 limit = memchr(p, '\n', lenstr - (p - instr)); 208 if (limit == NULL) 209 limit = instr + lenstr; 210 211 while (p < limit && isspace(*p)) { 212 p++; 213 } 214 addrstart = p; 215 while (p < limit && !isspace(*p)) { 216 p++; 217 } 218 if (p >= limit) { 219 /* Syntax error - no hostname present or truncated line */ 220 trace3(TR_str2hostent, 1, lenstr, buflen); 221 return (NSS_STR_PARSE_PARSE); 222 } 223 addrlen = p - addrstart; 224 if (addrlen >= sizeof (addrbuf)) { 225 /* Syntax error -- supposed IP address is too long */ 226 trace3(TR_str2hostent, 1, lenstr, buflen); 227 return (NSS_STR_PARSE_PARSE); 228 } 229 memcpy(addrbuf, addrstart, addrlen); 230 addrbuf[addrlen] = '\0'; 231 232 if (addrlen > ((af == AF_INET6) ? INET6_ADDRSTRLEN 233 : INET_ADDRSTRLEN)) { 234 /* Syntax error -- supposed IP address is too long */ 235 trace3(TR_str2hostent, 4, lenstr, buflen); 236 return (NSS_STR_PARSE_PARSE); 237 } 238 if (af == AF_INET) { 239 /* 240 * inet_pton() doesn't handle d.d.d, d.d, or d formats, 241 * so we must use inet_addr() for IPv4 addresses. 242 */ 243 addrvec[i] = (char *)&addrp[i]; 244 if ((addrp[i].s_addr = inet_addr(addrbuf)) == -1) { 245 /* Syntax error -- bogus IPv4 address */ 246 trace3(TR_str2hostent, 4, lenstr, buflen); 247 return (NSS_STR_PARSE_PARSE); 248 } 249 } else { 250 /* 251 * In the case of AF_INET6, we can have both v4 and v6 252 * addresses, so we convert v4's to v4 mapped addresses 253 * and return them as such. 254 */ 255 addrvec[i] = (char *)&addrp6[i]; 256 if (strchr(addrbuf, ':') != 0) { 257 if (inet_pton(af, addrbuf, &addrp6[i]) != 1) { 258 trace3(TR_str2hostent, 4, lenstr, 259 buflen); 260 return (NSS_STR_PARSE_PARSE); 261 } 262 } else { 263 struct in_addr in4; 264 if ((in4.s_addr = inet_addr(addrbuf)) == -1) { 265 trace3(TR_str2hostent, 4, lenstr, 266 buflen); 267 return (NSS_STR_PARSE_PARSE); 268 } 269 IN6_INADDR_TO_V4MAPPED(&in4, &addrp6[i]); 270 } 271 } 272 273 /* First address, this is where we get the hostname + aliases */ 274 if (i == 0) { 275 while (p < limit && isspace(*p)) { 276 p++; 277 } 278 host->h_aliases = _nss_netdb_aliases(p, limit - p, 279 buffer, ((char *)addrvec) - buffer); 280 if (host->h_aliases == NULL) 281 aliases_erange = 1; /* too big for buffer */ 282 } 283 if (limit >= instr + lenstr) 284 break; 285 else 286 p = limit + 1; /* skip NL */ 287 } 288 289 if (host->h_aliases == 0) { 290 if (aliases_erange) 291 res = NSS_STR_PARSE_ERANGE; 292 else 293 res = NSS_STR_PARSE_PARSE; 294 } else { 295 /* Success */ 296 host->h_name = host->h_aliases[0]; 297 host->h_aliases++; 298 res = NSS_STR_PARSE_SUCCESS; 299 } 300 /* 301 * If i < naddr, we quit the loop early and addrvec[i+1] needs NULL 302 * otherwise, we ran naddr iterations and addrvec[naddr] needs NULL 303 */ 304 addrvec[i >= naddr ? naddr : i + 1] = 0; 305 if (af == AF_INET6) { 306 host->h_length = sizeof (struct in6_addr); 307 } else { 308 host->h_length = sizeof (struct in_addr); 309 } 310 host->h_addrtype = af; 311 host->h_addr_list = addrvec; 312 313 trace3(TR_str2hostent, 1, lenstr, buflen); 314 return (res); 315 } 316 317 #endif /* NSS_INCLUDE_UNSAFE */ 318