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