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