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