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