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