1 /*
2  * Copyright 1997-2002 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1996,1999 by Internet Software Consortium.
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
14  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
16  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
17  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20  * SOFTWARE.
21  */
22 
23 #pragma ident	"%Z%%M%	%I%	%E% SMI"
24 
25 #if defined(LIBC_SCCS) && !defined(lint)
26 static const char rcsid[] = "$Id: gen_ho.c,v 1.16 2001/05/29 05:48:36 marka Exp $";
27 #endif /* LIBC_SCCS and not lint */
28 
29 /* Imports */
30 
31 #include "port_before.h"
32 
33 #include <sys/types.h>
34 
35 #include <netinet/in.h>
36 #include <arpa/nameser.h>
37 
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <netdb.h>
41 #include <resolv.h>
42 #include <stdio.h>
43 #include <string.h>
44 
45 #include <isc/memcluster.h>
46 #include <irs.h>
47 
48 #include "port_after.h"
49 
50 #include "irs_p.h"
51 #include "gen_p.h"
52 
53 /* Definitions */
54 
55 struct pvt {
56 	struct irs_rule *	rules;
57 	struct irs_rule *	rule;
58 	struct irs_ho *		ho;
59 	struct __res_state *	res;
60 	void			(*free_res)(void *);
61 };
62 
63 /* Forwards */
64 
65 static void		ho_close(struct irs_ho *this);
66 static struct hostent *	ho_byname(struct irs_ho *this, const char *name);
67 static struct hostent *	ho_byname2(struct irs_ho *this, const char *name,
68 				   int af);
69 static struct hostent *	ho_byaddr(struct irs_ho *this, const void *addr,
70 				  int len, int af);
71 static struct hostent *	ho_next(struct irs_ho *this);
72 static void		ho_rewind(struct irs_ho *this);
73 static void		ho_minimize(struct irs_ho *this);
74 static struct __res_state * ho_res_get(struct irs_ho *this);
75 static void		ho_res_set(struct irs_ho *this,
76 				   struct __res_state *res,
77 				   void (*free_res)(void *));
78 static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name,
79 				     const struct addrinfo *pai);
80 
81 static int		init(struct irs_ho *this);
82 
83 /* Exports */
84 
85 struct irs_ho *
86 irs_gen_ho(struct irs_acc *this) {
87 	struct gen_p *accpvt = (struct gen_p *)this->private;
88 	struct irs_ho *ho;
89 	struct pvt *pvt;
90 
91 	if (!(pvt = memget(sizeof *pvt))) {
92 		errno = ENOMEM;
93 		return (NULL);
94 	}
95 	memset(pvt, 0, sizeof *pvt);
96 	if (!(ho = memget(sizeof *ho))) {
97 		memput(pvt, sizeof *pvt);
98 		errno = ENOMEM;
99 		return (NULL);
100 	}
101 	memset(ho, 0x5e, sizeof *ho);
102 	pvt->rules = accpvt->map_rules[irs_ho];
103 	pvt->rule = pvt->rules;
104 	ho->private = pvt;
105 	ho->close = ho_close;
106 	ho->byname = ho_byname;
107 	ho->byname2 = ho_byname2;
108 	ho->byaddr = ho_byaddr;
109 	ho->next = ho_next;
110 	ho->rewind = ho_rewind;
111 	ho->minimize = ho_minimize;
112 	ho->res_get = ho_res_get;
113 	ho->res_set = ho_res_set;
114 	ho->addrinfo = ho_addrinfo;
115 	return (ho);
116 }
117 
118 /* Methods. */
119 
120 static void
121 ho_close(struct irs_ho *this) {
122 	struct pvt *pvt = (struct pvt *)this->private;
123 
124 	ho_minimize(this);
125 	if (pvt->res && pvt->free_res)
126 		(*pvt->free_res)(pvt->res);
127 	memput(pvt, sizeof *pvt);
128 	memput(this, sizeof *this);
129 }
130 
131 static struct hostent *
132 ho_byname(struct irs_ho *this, const char *name) {
133 	struct pvt *pvt = (struct pvt *)this->private;
134 	struct irs_rule *rule;
135 	struct hostent *rval;
136 	struct irs_ho *ho;
137 	int therrno = NETDB_INTERNAL;
138 	int softerror = 0;
139 
140 	if (init(this) == -1)
141 		return (NULL);
142 
143 	for (rule = pvt->rules; rule; rule = rule->next) {
144 		ho = rule->inst->ho;
145 		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
146 		errno = 0;
147 		rval = (*ho->byname)(ho, name);
148 		if (rval != NULL)
149 			return (rval);
150 		if (softerror == 0 &&
151 		    pvt->res->res_h_errno != HOST_NOT_FOUND &&
152 		    pvt->res->res_h_errno != NETDB_INTERNAL) {
153 			softerror = 1;
154 			therrno = pvt->res->res_h_errno;
155 		}
156 		if (rule->flags & IRS_CONTINUE)
157 			continue;
158 		/*
159 		 * The value TRY_AGAIN can mean that the service
160 		 * is not available, or just that this particular name
161 		 * cannot be resolved now.  We use the errno ECONNREFUSED
162 		 * to distinguish.  If a lookup sets that errno when
163 		 * H_ERRNO is TRY_AGAIN, we continue to try other lookup
164 		 * functions, otherwise we return the TRY_AGAIN error.
165 		 */
166 		if (pvt->res->res_h_errno != TRY_AGAIN || errno != ECONNREFUSED)
167 			break;
168 	}
169 	if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND)
170 		RES_SET_H_ERRNO(pvt->res, therrno);
171 	return (NULL);
172 }
173 
174 static struct hostent *
175 ho_byname2(struct irs_ho *this, const char *name, int af) {
176 	struct pvt *pvt = (struct pvt *)this->private;
177 	struct irs_rule *rule;
178 	struct hostent *rval;
179 	struct irs_ho *ho;
180 	int therrno = NETDB_INTERNAL;
181 	int softerror = 0;
182 
183 	if (init(this) == -1)
184 		return (NULL);
185 
186 	for (rule = pvt->rules; rule; rule = rule->next) {
187 		ho = rule->inst->ho;
188 		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
189 		errno = 0;
190 		rval = (*ho->byname2)(ho, name, af);
191 		if (rval != NULL)
192 			return (rval);
193 		if (softerror == 0 &&
194 		    pvt->res->res_h_errno != HOST_NOT_FOUND &&
195 		    pvt->res->res_h_errno != NETDB_INTERNAL) {
196 			softerror = 1;
197 			therrno = pvt->res->res_h_errno;
198 		}
199 		if (rule->flags & IRS_CONTINUE)
200 			continue;
201 		/*
202 		 * See the comments in ho_byname() explaining
203 		 * the interpretation of TRY_AGAIN and ECONNREFUSED.
204 		 */
205 		if (pvt->res->res_h_errno != TRY_AGAIN || errno != ECONNREFUSED)
206 			break;
207 	}
208 	if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND)
209 		RES_SET_H_ERRNO(pvt->res, therrno);
210 	return (NULL);
211 }
212 
213 static struct hostent *
214 ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) {
215 	struct pvt *pvt = (struct pvt *)this->private;
216 	struct irs_rule *rule;
217 	struct hostent *rval;
218 	struct irs_ho *ho;
219 	int therrno = NETDB_INTERNAL;
220 	int softerror = 0;
221 
222 
223 	if (init(this) == -1)
224 		return (NULL);
225 
226 	for (rule = pvt->rules; rule; rule = rule->next) {
227 		ho = rule->inst->ho;
228 		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
229 		errno = 0;
230 		rval = (*ho->byaddr)(ho, addr, len, af);
231 		if (rval != NULL)
232 			return (rval);
233 		if (softerror == 0 &&
234 		    pvt->res->res_h_errno != HOST_NOT_FOUND &&
235 		    pvt->res->res_h_errno != NETDB_INTERNAL) {
236 			softerror = 1;
237 			therrno = pvt->res->res_h_errno;
238 		}
239 
240 		if (rule->flags & IRS_CONTINUE)
241 			continue;
242 		/*
243 		 * See the comments in ho_byname() explaining
244 		 * the interpretation of TRY_AGAIN and ECONNREFUSED.
245 		 */
246 		if (pvt->res->res_h_errno != TRY_AGAIN || errno != ECONNREFUSED)
247 			break;
248 	}
249 	if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND)
250 		RES_SET_H_ERRNO(pvt->res, therrno);
251 	return (NULL);
252 }
253 
254 static struct hostent *
255 ho_next(struct irs_ho *this) {
256 	struct pvt *pvt = (struct pvt *)this->private;
257 	struct hostent *rval;
258 	struct irs_ho *ho;
259 
260 	while (pvt->rule) {
261 		ho = pvt->rule->inst->ho;
262 		rval = (*ho->next)(ho);
263 		if (rval)
264 			return (rval);
265 		if (!(pvt->rule->flags & IRS_CONTINUE))
266 			break;
267 		pvt->rule = pvt->rule->next;
268 		if (pvt->rule) {
269 			ho = pvt->rule->inst->ho;
270 			(*ho->rewind)(ho);
271 		}
272 	}
273 	return (NULL);
274 }
275 
276 static void
277 ho_rewind(struct irs_ho *this) {
278 	struct pvt *pvt = (struct pvt *)this->private;
279 	struct irs_ho *ho;
280 
281 	pvt->rule = pvt->rules;
282 	if (pvt->rule) {
283 		ho = pvt->rule->inst->ho;
284 		(*ho->rewind)(ho);
285 	}
286 }
287 
288 static void
289 ho_minimize(struct irs_ho *this) {
290 	struct pvt *pvt = (struct pvt *)this->private;
291 	struct irs_rule *rule;
292 
293 	if (pvt->res)
294 		res_nclose(pvt->res);
295 	for (rule = pvt->rules; rule != NULL; rule = rule->next) {
296 		struct irs_ho *ho = rule->inst->ho;
297 
298 		(*ho->minimize)(ho);
299 	}
300 }
301 
302 static struct __res_state *
303 ho_res_get(struct irs_ho *this) {
304 	struct pvt *pvt = (struct pvt *)this->private;
305 
306 	if (!pvt->res) {
307 		struct __res_state *res;
308 		res = (struct __res_state *)malloc(sizeof *res);
309 		if (!res) {
310 			errno = ENOMEM;
311 			return (NULL);
312 		}
313 		memset(res, 0, sizeof *res);
314 		ho_res_set(this, res, free);
315 	}
316 
317 	return (pvt->res);
318 }
319 
320 static void
321 ho_res_set(struct irs_ho *this, struct __res_state *res,
322 		void (*free_res)(void *)) {
323 	struct pvt *pvt = (struct pvt *)this->private;
324 	struct irs_rule *rule;
325 
326 	if (pvt->res && pvt->free_res) {
327 		res_nclose(pvt->res);
328 		(*pvt->free_res)(pvt->res);
329 	}
330 
331 	pvt->res = res;
332 	pvt->free_res = free_res;
333 
334 	for (rule = pvt->rules; rule != NULL; rule = rule->next) {
335 		struct irs_ho *ho = rule->inst->ho;
336 
337 		(*ho->res_set)(ho, pvt->res, NULL);
338 	}
339 }
340 
341 static struct addrinfo *
342 ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai)
343 {
344 	struct pvt *pvt = (struct pvt *)this->private;
345 	struct irs_rule *rule;
346 	struct addrinfo *rval = NULL;
347 	struct irs_ho *ho;
348 	int therrno = NETDB_INTERNAL;
349 	int softerror = 0;
350 
351 	if (init(this) == -1)
352 		return (NULL);
353 
354 	for (rule = pvt->rules; rule; rule = rule->next) {
355 		ho = rule->inst->ho;
356 		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
357 		errno = 0;
358 		if (ho->addrinfo == NULL) /* for safety */
359 			continue;
360 		rval = (*ho->addrinfo)(ho, name, pai);
361 		if (rval != NULL)
362 			return (rval);
363 		if (softerror == 0 &&
364 		    pvt->res->res_h_errno != HOST_NOT_FOUND &&
365 		    pvt->res->res_h_errno != NETDB_INTERNAL) {
366 			softerror = 1;
367 			therrno = pvt->res->res_h_errno;
368 		}
369 		if (rule->flags & IRS_CONTINUE)
370 			continue;
371 		/*
372 		 * See the comments in ho_byname() explaining
373 		 * the interpretation of TRY_AGAIN and ECONNREFUSED.
374 		 */
375 		if (pvt->res->res_h_errno != TRY_AGAIN ||
376 		    errno != ECONNREFUSED)
377 			break;
378 	}
379 	if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND)
380 		RES_SET_H_ERRNO(pvt->res, therrno);
381 	if (rval)
382 		freeaddrinfo(rval);
383 	return (NULL);
384 }
385 
386 static int
387 init(struct irs_ho *this) {
388 	struct pvt *pvt = (struct pvt *)this->private;
389 
390         if (!pvt->res && !ho_res_get(this))
391                 return (-1);
392 
393         if (((pvt->res->options & RES_INIT) == 0) &&
394             (res_ninit(pvt->res) == -1))
395                 return (-1);
396 
397         return (0);
398 }
399