gethostent.c revision 2830:5228d1267a01
1263348Sjmmv/*
2263348Sjmmv * CDDL HEADER START
3266074Sjmmv *
4263348Sjmmv * The contents of this file are subject to the terms of the
5263348Sjmmv * Common Development and Distribution License (the "License").
6263348Sjmmv * You may not use this file except in compliance with the License.
7263348Sjmmv *
8263348Sjmmv * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9263348Sjmmv * 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#pragma ident	"%Z%%M%	%I%	%E% SMI"
32
33#include "mt.h"
34#include <stdlib.h>
35#include <ctype.h>
36#include <string.h>
37#include <strings.h>
38#include <netdb.h>
39#include <stdio.h>
40#include <arpa/inet.h>
41#include <nss_dbdefs.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 */
49int 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 */
58static 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
63struct hostent *
64gethostbyname(const char *nam)
65{
66	nss_XbyY_buf_t  *b;
67
68	if ((b = GETBUF()) == 0)
69		return (NULL);
70	return (gethostbyname_r(nam, b->result, b->buffer, b->buflen,
71		    &h_errno));
72}
73
74struct hostent *
75gethostbyaddr(const void *addr, socklen_t len, int type)
76{
77	nss_XbyY_buf_t	*b;
78
79	h_errno = 0;
80	if (type == AF_INET6)
81		return (getipnodebyaddr(addr, len, type, &h_errno));
82
83	if ((b = GETBUF()) == 0)
84		return (NULL);
85	return (gethostbyaddr_r(addr, len, type,
86		    b->result, b->buffer, b->buflen, &h_errno));
87}
88
89struct hostent *
90gethostent(void)
91{
92	nss_XbyY_buf_t	*b;
93
94	if ((b = GETBUF()) == 0)
95		return (NULL);
96	return (gethostent_r(b->result, b->buffer, b->buflen, &h_errno));
97}
98
99/*
100 * Return values: 0 = success, 1 = parse error, 2 = erange ...
101 * The structure pointer passed in is a structure in the caller's space
102 * wherein the field pointers would be set to areas in the buffer if
103 * need be. instring and buffer should be separate areas.
104 */
105int
106__str2hostent(int af, const char *instr, int lenstr, void *ent, char *buffer,
107    int buflen)
108{
109	struct hostent	*host	= (struct hostent *)ent;
110	const char	*p, *addrstart, *limit;
111	int		naddr, i, aliases_erange = 0;
112	int		addrlen, res;
113	char		addrbuf[100];  /* Why 100? */
114	struct in_addr	*addrp;
115	struct in6_addr	*addrp6;
116	char		**addrvec;
117
118	if ((instr >= buffer && (buffer + buflen) > instr) ||
119	    (buffer >= instr && (instr + lenstr) > buffer))
120		return (NSS_STR_PARSE_PARSE);
121	if (af != AF_INET && af != AF_INET6) {
122		/*
123		 * XXX - Returning ERANGE here is completely bogus.
124		 * Unfortunately, there's no error code identifying
125		 * bogus calls from the backend (and nothing the user
126		 * can do about our bugs anyway).
127		 */
128		return (NSS_STR_PARSE_ERANGE);
129	}
130
131	/*
132	 * The DNS-via-YP code returns multiple lines for a key.
133	 * Normal YP return values do not contain newlines (nor do
134	 * lines from /etc/hosts or other sources)
135	 * We count the number of newlines; this should give us
136	 * the number of IP addresses specified.
137	 * We'll also call the aliases code and instruct it to
138	 * stop at the first newline as the remaining lines will
139	 * all contain the same hostname/aliases (no aliases, unfortunately).
140	 *
141	 * When confronted with a string with embedded newlines,
142	 * this code will take the hostname/aliases on the first line
143	 * and each of the IP addresses at the start of all lines.
144	 * Because the NIS protocol limits return values to 1024 bytes,
145	 * we still do not get all addresses.  If you want to fix
146	 * that problem, do not look here.
147	 */
148
149	p = instr;
150
151	/* Strip trailing newlines */
152	while (lenstr > 0 && p[lenstr - 1] == '\n')
153		lenstr--;
154
155	naddr = 1;
156	limit = p + lenstr;
157
158	for (; p < limit && (p = memchr(p, '\n', limit - p)); p++)
159		naddr++;
160
161	/* Allocate space for naddr addresses and h_addr_list */
162
163	if (af == AF_INET6) {
164		addrp6 = (struct in6_addr *)ROUND_DOWN(buffer + buflen,
165		    sizeof (*addrp6));
166		addrp6 -= naddr;
167		addrvec = (char **)ROUND_DOWN(addrp6, sizeof (*addrvec));
168		addrvec -= naddr + 1;
169	} else {
170		addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen,
171		    sizeof (*addrp));
172		addrp -= naddr;
173		addrvec = (char **)ROUND_DOWN(addrp, sizeof (*addrvec));
174		addrvec -= naddr + 1;
175	}
176
177	if ((char *)addrvec < buffer)
178		return (NSS_STR_PARSE_ERANGE);
179
180	/* For each addr, parse and get it */
181
182	p = instr;
183
184	for (i = 0; i < naddr; i ++) {
185
186		limit = memchr(p, '\n', lenstr - (p - instr));
187		if (limit == NULL)
188			limit = instr + lenstr;
189
190		while (p < limit && isspace(*p))
191			p++;
192		addrstart = p;
193		while (p < limit && !isspace(*p))
194			p++;
195		if (p >= limit)
196		    /* Syntax error - no hostname present or truncated line */
197		    return (NSS_STR_PARSE_PARSE);
198		addrlen = p - addrstart;
199		if (addrlen >= sizeof (addrbuf))
200			/* Syntax error -- supposed IP address is too long */
201			return (NSS_STR_PARSE_PARSE);
202		(void) memcpy(addrbuf, addrstart, addrlen);
203		addrbuf[addrlen] = '\0';
204
205		if (addrlen > ((af == AF_INET6) ? INET6_ADDRSTRLEN
206							: INET_ADDRSTRLEN))
207			/* Syntax error -- supposed IP address is too long */
208			return (NSS_STR_PARSE_PARSE);
209		if (af == AF_INET) {
210			/*
211			 * inet_pton() doesn't handle d.d.d, d.d, or d formats,
212			 * so we must use inet_addr() for IPv4 addresses.
213			 */
214			addrvec[i] = (char *)&addrp[i];
215			if ((addrp[i].s_addr = inet_addr(addrbuf)) ==
216								0xffffffffU)
217				/* Syntax error -- bogus IPv4 address */
218				return (NSS_STR_PARSE_PARSE);
219		} else {
220			/*
221			 * In the case of AF_INET6, we can have both v4 and v6
222			 * addresses, so we convert v4's to v4 mapped addresses
223			 * and return them as such.
224			 */
225			addrvec[i] = (char *)&addrp6[i];
226			if (strchr(addrbuf, ':') != 0) {
227				if (inet_pton(af, addrbuf, &addrp6[i]) != 1)
228					return (NSS_STR_PARSE_PARSE);
229			} else {
230				struct in_addr in4;
231				if ((in4.s_addr = inet_addr(addrbuf)) ==
232								0xffffffffU)
233					return (NSS_STR_PARSE_PARSE);
234				IN6_INADDR_TO_V4MAPPED(&in4, &addrp6[i]);
235			}
236		}
237
238		/* First address, this is where we get the hostname + aliases */
239		if (i == 0) {
240			while (p < limit && isspace(*p)) {
241				p++;
242			}
243			host->h_aliases = _nss_netdb_aliases(p, limit - p,
244				buffer, ((char *)addrvec) - buffer);
245			if (host->h_aliases == NULL)
246				aliases_erange = 1; /* too big for buffer */
247		}
248		if (limit >= instr + lenstr)
249			break;
250		else
251			p = limit + 1;		/* skip NL */
252	}
253
254	if (host->h_aliases == 0) {
255		if (aliases_erange)
256			res = NSS_STR_PARSE_ERANGE;
257		else
258			res = NSS_STR_PARSE_PARSE;
259	} else {
260		/* Success */
261		host->h_name = host->h_aliases[0];
262		host->h_aliases++;
263		res = NSS_STR_PARSE_SUCCESS;
264	}
265	/*
266	 * If i < naddr, we quit the loop early and addrvec[i+1] needs NULL
267	 * otherwise, we ran naddr iterations and addrvec[naddr] needs NULL
268	 */
269	addrvec[i >= naddr ? naddr : i + 1] = 0;
270	if (af == AF_INET6) {
271		host->h_length    = sizeof (struct in6_addr);
272	} else {
273		host->h_length    = sizeof (struct in_addr);
274	}
275	host->h_addrtype  = af;
276	host->h_addr_list = addrvec;
277
278	return (res);
279}
280#endif	/* NSS_INCLUDE_UNSAFE */
281