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 *
gethostbyname(const char * nam)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 *
gethostbyaddr(const void * addr,socklen_t len,int type)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 *
gethostent(void)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
__str2hostent(int af,const char * instr,int lenstr,void * ent,char * buffer,int buflen)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