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