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)
19 static 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 
48 struct 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 
58 static void		ho_close(struct irs_ho *this);
59 static struct hostent *	ho_byname(struct irs_ho *this, const char *name);
60 static struct hostent *	ho_byname2(struct irs_ho *this, const char *name,
61 				   int af);
62 static struct hostent *	ho_byaddr(struct irs_ho *this, const void *addr,
63 				  int len, int af);
64 static struct hostent *	ho_next(struct irs_ho *this);
65 static void		ho_rewind(struct irs_ho *this);
66 static void		ho_minimize(struct irs_ho *this);
67 static struct __res_state * ho_res_get(struct irs_ho *this);
68 static void		ho_res_set(struct irs_ho *this,
69 				   struct __res_state *res,
70 				   void (*free_res)(void *));
71 static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name,
72 				     const struct addrinfo *pai);
73 
74 static int		init(struct irs_ho *this);
75 
76 /* Exports */
77 
78 struct irs_ho *
79 irs_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 
113 static void
114 ho_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 
124 static struct hostent *
125 ho_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 
167 static struct hostent *
168 ho_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 
206 static struct hostent *
207 ho_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 
247 static struct hostent *
248 ho_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 
269 static void
270 ho_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 
281 static void
282 ho_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 
295 static struct __res_state *
296 ho_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 
313 static void
314 ho_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 
334 static struct addrinfo *
335 ho_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 
377 static int
378 init(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