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 *
irs_gen_ho(struct irs_acc * this)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
ho_close(struct irs_ho * this)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 *
ho_byname(struct irs_ho * this,const char * name)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 *
ho_byname2(struct irs_ho * this,const char * name,int af)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 *
ho_byaddr(struct irs_ho * this,const void * addr,int len,int af)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 *
ho_next(struct irs_ho * this)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
ho_rewind(struct irs_ho * this)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
ho_minimize(struct irs_ho * this)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 *
ho_res_get(struct irs_ho * this)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
ho_res_set(struct irs_ho * this,struct __res_state * res,void (* free_res)(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 *
ho_addrinfo(struct irs_ho * this,const char * name,const struct addrinfo * pai)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
init(struct irs_ho * this)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