]> git.proxmox.com Git - mirror_edk2.git/blame - StdLib/BsdSocketLib/getaddrinfo.c
Fix a bug about the iSCSI DHCP dependency issue.
[mirror_edk2.git] / StdLib / BsdSocketLib / getaddrinfo.c
CommitLineData
3bdf9aae 1/* $NetBSD: getaddrinfo.c,v 1.91.6.1 2009/01/26 00:27:34 snj Exp $ */\r
2/* $KAME: getaddrinfo.c,v 1.29 2000/08/31 17:26:57 itojun Exp $ */\r
3\r
4/*\r
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.\r
6 * All rights reserved.\r
7 *\r
8 * Redistribution and use in source and binary forms, with or without\r
9 * modification, are permitted provided that the following conditions\r
10 * are met:\r
11 * 1. Redistributions of source code must retain the above copyright\r
12 * notice, this list of conditions and the following disclaimer.\r
13 * 2. Redistributions in binary form must reproduce the above copyright\r
14 * notice, this list of conditions and the following disclaimer in the\r
15 * documentation and/or other materials provided with the distribution.\r
16 * 3. Neither the name of the project nor the names of its contributors\r
17 * may be used to endorse or promote products derived from this software\r
18 * without specific prior written permission.\r
19 *\r
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND\r
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE\r
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
30 * SUCH DAMAGE.\r
31 */\r
32\r
33/*\r
34 * Issues to be discussed:\r
35 * - Return values. There are nonstandard return values defined and used\r
36 * in the source code. This is because RFC2553 is silent about which error\r
37 * code must be returned for which situation.\r
38 * - IPv4 classful (shortened) form. RFC2553 is silent about it. XNET 5.2\r
39 * says to use inet_aton() to convert IPv4 numeric to binary (alows\r
40 * classful form as a result).\r
41 * current code - disallow classful form for IPv4 (due to use of inet_pton).\r
42 * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is\r
43 * invalid.\r
44 * current code - SEGV on freeaddrinfo(NULL)\r
45 * Note:\r
46 * - The code filters out AFs that are not supported by the kernel,\r
47 * when globbing NULL hostname (to loopback, or wildcard). Is it the right\r
48 * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG\r
49 * in ai_flags?\r
50 * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague.\r
51 * (1) what should we do against numeric hostname (2) what should we do\r
52 * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready?\r
53 * non-loopback address configured? global address configured?\r
54 */\r
55\r
56#include <sys/cdefs.h>\r
57#if defined(LIBC_SCCS) && !defined(lint)\r
58__RCSID("$NetBSD: getaddrinfo.c,v 1.91.6.1 2009/01/26 00:27:34 snj Exp $");\r
59#endif /* LIBC_SCCS and not lint */\r
60\r
61#define INET6 1\r
62\r
63#include "namespace.h"\r
64#include <sys/types.h>\r
65#include <sys/param.h>\r
66#include <sys/socket.h>\r
67#include <net/if.h>\r
68#include <netinet/in.h>\r
69#include <arpa/inet.h>\r
70#include <arpa/nameser.h>\r
71#include <assert.h>\r
72#include <ctype.h>\r
73#include <errno.h>\r
74#include <netdb.h>\r
75#include <resolv.h>\r
76#include <stddef.h>\r
77#include <stdio.h>\r
78#include <stdlib.h>\r
79#include <string.h>\r
80#include <unistd.h>\r
81\r
82#include <stdarg.h>\r
83#include <nsswitch.h>\r
84#include <resolv.h>\r
85\r
86//#ifdef YP\r
87//#include <rpc/rpc.h>\r
88//#include <rpcsvc/yp_prot.h>\r
89//#include <rpcsvc/ypclnt.h>\r
90//#endif\r
91\r
92#include <net/servent.h>\r
93\r
94#define endservent_r(svd) endservent()\r
95#define nsdispatch(pResult,dtab,database,routine,files,hostname,pai) NS_NOTFOUND\r
96#define res_nmkquery(state,op,dname,class,type,data,datalen,newrr_in,buf,buflen) res_mkquery( op, dname, class, type, data, datalen, newrr_in, buf, buflen )\r
97#define res_nsend(state,buf,buflen,ans,anssiz) res_send ( buf, buflen, ans, anssiz )\r
98\r
99/* Things involving an internal (static) resolver context. */\r
100__BEGIN_DECLS\r
101#define __res_get_state() (( 0 != _res.nscount ) ? &_res : NULL )\r
102#define __res_put_state(state)\r
103#define __res_state() _res\r
104__END_DECLS\r
105\r
106#ifdef __weak_alias\r
107__weak_alias(getaddrinfo,_getaddrinfo)\r
108__weak_alias(freeaddrinfo,_freeaddrinfo)\r
109__weak_alias(gai_strerror,_gai_strerror)\r
110#endif\r
111\r
112#define SUCCESS 0\r
113#define ANY 0\r
114#define YES 1\r
115#define NO 0\r
116\r
117static const char in_addrany[] = { 0, 0, 0, 0 };\r
118static const char in_loopback[] = { 127, 0, 0, 1 };\r
119#ifdef INET6\r
120static const char in6_addrany[] = {\r
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\r
122};\r
123static const char in6_loopback[] = {\r
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\r
125};\r
126#endif\r
127\r
128static const struct afd {\r
129 int a_af;\r
130 int a_addrlen;\r
131 int a_socklen;\r
132 int a_off;\r
133 const char *a_addrany;\r
134 const char *a_loopback; \r
135 int a_scoped;\r
136} afdl [] = {\r
137#ifdef INET6\r
138 {PF_INET6, sizeof(struct in6_addr),\r
139 sizeof(struct sockaddr_in6),\r
140 offsetof(struct sockaddr_in6, sin6_addr),\r
141 in6_addrany, in6_loopback, 1},\r
142#endif\r
143 {PF_INET, sizeof(struct in_addr),\r
144 sizeof(struct sockaddr_in),\r
145 offsetof(struct sockaddr_in, sin_addr),\r
146 in_addrany, in_loopback, 0},\r
147 {0, 0, 0, 0, NULL, NULL, 0},\r
148};\r
149\r
150struct explore {\r
151 int e_af;\r
152 int e_socktype;\r
153 int e_protocol;\r
154 const char *e_protostr;\r
155 int e_wild;\r
156#define WILD_AF(ex) ((ex)->e_wild & 0x01)\r
157#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02)\r
158#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04)\r
159};\r
160\r
161static const struct explore explore[] = {\r
162#if 0\r
163 { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 },\r
164#endif\r
165#ifdef INET6\r
166 { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },\r
167 { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },\r
168 { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 },\r
169#endif\r
170 { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },\r
171 { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },\r
172 { PF_INET, SOCK_RAW, ANY, NULL, 0x05 },\r
173 { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },\r
174 { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },\r
175 { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 },\r
176 { -1, 0, 0, NULL, 0 },\r
177};\r
178\r
179#ifdef INET6\r
180#define PTON_MAX 16\r
181#else\r
182#define PTON_MAX 4\r
183#endif\r
184\r
185static const ns_src default_dns_files[] = {\r
186 { NSSRC_FILES, NS_SUCCESS },\r
187 { NSSRC_DNS, NS_SUCCESS },\r
188 { 0, 0 }\r
189};\r
190\r
191#define MAXPACKET (64*1024)\r
192\r
193typedef union {\r
194 HEADER hdr;\r
195 u_char buf[MAXPACKET];\r
196} querybuf;\r
197\r
198struct res_target {\r
199 struct res_target *next;\r
200 const char *name; /* domain name */\r
201 int qclass, qtype; /* class and type of query */\r
202 u_char *answer; /* buffer to put answer */\r
203 int anslen; /* size of answer buffer */\r
204 int n; /* result length */\r
205};\r
206\r
207static int str2number(const char *);\r
208static int explore_fqdn(const struct addrinfo *, const char *,\r
209 const char *, struct addrinfo **, struct servent_data *);\r
210static int explore_null(const struct addrinfo *,\r
211 const char *, struct addrinfo **, struct servent_data *);\r
212static int explore_numeric(const struct addrinfo *, const char *,\r
213 const char *, struct addrinfo **, const char *, struct servent_data *);\r
214static int explore_numeric_scope(const struct addrinfo *, const char *,\r
215 const char *, struct addrinfo **, struct servent_data *);\r
216static int get_canonname(const struct addrinfo *,\r
217 struct addrinfo *, const char *);\r
218static struct addrinfo *get_ai(const struct addrinfo *,\r
219 const struct afd *, const char *);\r
220static int get_portmatch(const struct addrinfo *, const char *,\r
221 struct servent_data *);\r
222static int get_port(const struct addrinfo *, const char *, int,\r
223 struct servent_data *);\r
224static const struct afd *find_afd(int);\r
225#ifdef INET6\r
226static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *);\r
227#endif\r
228\r
229static struct addrinfo *getanswer(const querybuf *, int, const char *, int,\r
230 const struct addrinfo *);\r
231static void aisort(struct addrinfo *s, res_state res);\r
232static int _dns_getaddrinfo(void *, void *, va_list);\r
233static void _sethtent(FILE **);\r
234static void _endhtent(FILE **);\r
235static struct addrinfo *_gethtent(FILE **, const char *,\r
236 const struct addrinfo *);\r
237static int _files_getaddrinfo(void *, void *, va_list);\r
238#ifdef YP\r
239static struct addrinfo *_yphostent(char *, const struct addrinfo *);\r
240static int _yp_getaddrinfo(void *, void *, va_list);\r
241#endif\r
242\r
243static int res_queryN(const char *, struct res_target *, res_state);\r
244static int res_searchN(const char *, struct res_target *, res_state);\r
245static int res_querydomainN(const char *, const char *,\r
246 struct res_target *, res_state);\r
247\r
248static const char * const ai_errlist[] = {\r
249 "Success",\r
250 "Address family for hostname not supported", /* EAI_ADDRFAMILY */\r
251 "Temporary failure in name resolution", /* EAI_AGAIN */\r
252 "Invalid value for ai_flags", /* EAI_BADFLAGS */\r
253 "Non-recoverable failure in name resolution", /* EAI_FAIL */\r
254 "ai_family not supported", /* EAI_FAMILY */\r
255 "Memory allocation failure", /* EAI_MEMORY */\r
256 "No address associated with hostname", /* EAI_NODATA */\r
257 "hostname nor servname provided, or not known", /* EAI_NONAME */\r
258 "servname not supported for ai_socktype", /* EAI_SERVICE */\r
259 "ai_socktype not supported", /* EAI_SOCKTYPE */\r
260 "System error returned in errno", /* EAI_SYSTEM */\r
261 "Invalid value for hints", /* EAI_BADHINTS */\r
262 "Resolved protocol is unknown", /* EAI_PROTOCOL */\r
263 "Argument buffer overflow", /* EAI_OVERFLOW */\r
264 "Unknown error", /* EAI_MAX */\r
265};\r
266\r
267/* XXX macros that make external reference is BAD. */\r
268\r
269#define GET_AI(ai, afd, addr) \\r
270do { \\r
271 /* external reference: pai, error, and label free */ \\r
272 (ai) = get_ai(pai, (afd), (addr)); \\r
273 if ((ai) == NULL) { \\r
274 error = EAI_MEMORY; \\r
275 goto free; \\r
276 } \\r
277} while (/*CONSTCOND*/0)\r
278\r
279#define GET_PORT(ai, serv, svd) \\r
280do { \\r
281 /* external reference: error and label free */ \\r
282 error = get_port((ai), (serv), 0, (svd)); \\r
283 if (error != 0) \\r
284 goto free; \\r
285} while (/*CONSTCOND*/0)\r
286\r
287#define GET_CANONNAME(ai, str) \\r
288do { \\r
289 /* external reference: pai, error and label free */ \\r
290 error = get_canonname(pai, (ai), (str)); \\r
291 if (error != 0) \\r
292 goto free; \\r
293} while (/*CONSTCOND*/0)\r
294\r
295#define ERR(err) \\r
296do { \\r
297 /* external reference: error, and label bad */ \\r
298 error = (err); \\r
299 goto bad; \\r
300 /*NOTREACHED*/ \\r
301} while (/*CONSTCOND*/0)\r
302\r
303#define MATCH_FAMILY(x, y, w) \\r
304 ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || \\r
305 (y) == PF_UNSPEC))) \r
306#define MATCH(x, y, w) \\r
307 ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY)))\r
308\r
309const char *\r
310gai_strerror(int ecode)\r
311{\r
312 if (ecode < 0 || ecode > EAI_MAX)\r
313 ecode = EAI_MAX;\r
314 return ai_errlist[ecode];\r
315}\r
316\r
317void\r
318freeaddrinfo(struct addrinfo *ai)\r
319{\r
320 struct addrinfo *next;\r
321\r
322 _DIAGASSERT(ai != NULL);\r
323\r
324 do {\r
325 next = ai->ai_next;\r
326 if (ai->ai_canonname)\r
327 free(ai->ai_canonname);\r
328 /* no need to free(ai->ai_addr) */\r
329 free(ai);\r
330 ai = next;\r
331 } while (ai);\r
332}\r
333\r
334static int\r
335str2number(const char *p)\r
336{\r
337 char *ep;\r
338 unsigned long v;\r
339\r
340 _DIAGASSERT(p != NULL);\r
341\r
342 if (*p == '\0')\r
343 return -1;\r
344 ep = NULL;\r
345 errno = 0;\r
346 v = strtoul(p, &ep, 10);\r
347 if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX)\r
348 return v;\r
349 else\r
350 return -1;\r
351}\r
352\r
353int\r
354getaddrinfo(const char *hostname, const char *servname,\r
355 const struct addrinfo *hints, struct addrinfo **res)\r
356{\r
357 struct addrinfo sentinel;\r
358 struct addrinfo *cur;\r
359 int error = 0;\r
360 struct addrinfo ai;\r
361 struct addrinfo ai0;\r
362 struct addrinfo *pai;\r
363 const struct explore *ex;\r
364 struct servent_data svd;\r
365\r
366 /* hostname is allowed to be NULL */\r
367 /* servname is allowed to be NULL */\r
368 /* hints is allowed to be NULL */\r
369 _DIAGASSERT(res != NULL);\r
370\r
371 (void)memset(&svd, 0, sizeof(svd));\r
372 memset(&sentinel, 0, sizeof(sentinel));\r
373 cur = &sentinel;\r
374 memset(&ai, 0, sizeof(ai));\r
375 pai = &ai;\r
376 pai->ai_flags = 0;\r
377 pai->ai_family = PF_UNSPEC;\r
378 pai->ai_socktype = ANY;\r
379 pai->ai_protocol = ANY;\r
380 pai->ai_addrlen = 0;\r
381 pai->ai_canonname = NULL;\r
382 pai->ai_addr = NULL;\r
383 pai->ai_next = NULL;\r
384 \r
385 if (hostname == NULL && servname == NULL)\r
386 return EAI_NONAME;\r
387 if (hints) {\r
388 /* error check for hints */\r
389 if (hints->ai_addrlen || hints->ai_canonname ||\r
390 hints->ai_addr || hints->ai_next)\r
391 ERR(EAI_BADHINTS); /* xxx */\r
392 if (hints->ai_flags & ~AI_MASK)\r
393 ERR(EAI_BADFLAGS);\r
394 switch (hints->ai_family) {\r
395 case PF_UNSPEC:\r
396 case PF_INET:\r
397#ifdef INET6\r
398 case PF_INET6:\r
399#endif\r
400 break;\r
401 default:\r
402 ERR(EAI_FAMILY);\r
403 }\r
404 memcpy(pai, hints, sizeof(*pai));\r
405\r
406 /*\r
407 * if both socktype/protocol are specified, check if they\r
408 * are meaningful combination.\r
409 */\r
410 if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {\r
411 for (ex = explore; ex->e_af >= 0; ex++) {\r
412 if (pai->ai_family != ex->e_af)\r
413 continue;\r
414 if (ex->e_socktype == ANY)\r
415 continue;\r
416 if (ex->e_protocol == ANY)\r
417 continue;\r
418 if (pai->ai_socktype == ex->e_socktype\r
419 && pai->ai_protocol != ex->e_protocol) {\r
420 ERR(EAI_BADHINTS);\r
421 }\r
422 }\r
423 }\r
424 }\r
425\r
426 /*\r
427 * check for special cases. (1) numeric servname is disallowed if\r
428 * socktype/protocol are left unspecified. (2) servname is disallowed\r
429 * for raw and other inet{,6} sockets.\r
430 */\r
431 if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)\r
432#ifdef PF_INET6\r
433 || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)\r
434#endif\r
435 ) {\r
436 ai0 = *pai; /* backup *pai */\r
437\r
438 if (pai->ai_family == PF_UNSPEC) {\r
439#ifdef PF_INET6\r
440 pai->ai_family = PF_INET6;\r
441#else\r
442 pai->ai_family = PF_INET;\r
443#endif\r
444 }\r
445 error = get_portmatch(pai, servname, &svd);\r
446 if (error)\r
447 ERR(error);\r
448\r
449 *pai = ai0;\r
450 }\r
451\r
452 ai0 = *pai;\r
453\r
454 /* NULL hostname, or numeric hostname */\r
455 for (ex = explore; ex->e_af >= 0; ex++) {\r
456 *pai = ai0;\r
457\r
458 /* PF_UNSPEC entries are prepared for DNS queries only */\r
459 if (ex->e_af == PF_UNSPEC)\r
460 continue;\r
461\r
462 if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))\r
463 continue;\r
464 if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex)))\r
465 continue;\r
466 if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex)))\r
467 continue;\r
468\r
469 if (pai->ai_family == PF_UNSPEC)\r
470 pai->ai_family = ex->e_af;\r
471 if (pai->ai_socktype == ANY && ex->e_socktype != ANY)\r
472 pai->ai_socktype = ex->e_socktype;\r
473 if (pai->ai_protocol == ANY && ex->e_protocol != ANY)\r
474 pai->ai_protocol = ex->e_protocol;\r
475\r
476 if (hostname == NULL)\r
477 error = explore_null(pai, servname, &cur->ai_next,\r
478 &svd);\r
479 else\r
480 error = explore_numeric_scope(pai, hostname, servname,\r
481 &cur->ai_next, &svd);\r
482\r
483 if (error)\r
484 goto free;\r
485\r
486 while (cur->ai_next)\r
487 cur = cur->ai_next;\r
488 }\r
489\r
490 /*\r
491 * XXX\r
492 * If numeric representation of AF1 can be interpreted as FQDN\r
493 * representation of AF2, we need to think again about the code below.\r
494 */\r
495 if (sentinel.ai_next)\r
496 goto good;\r
497\r
498 if (hostname == NULL)\r
499 ERR(EAI_NODATA);\r
500 if (pai->ai_flags & AI_NUMERICHOST)\r
501 ERR(EAI_NONAME);\r
502\r
503 /*\r
504 * hostname as alphabetical name.\r
505 * we would like to prefer AF_INET6 than AF_INET, so we'll make a\r
506 * outer loop by AFs.\r
507 */\r
508 for (ex = explore; ex->e_af >= 0; ex++) {\r
509 *pai = ai0;\r
510\r
511 /* require exact match for family field */\r
512 if (pai->ai_family != ex->e_af)\r
513 continue;\r
514\r
515 if (!MATCH(pai->ai_socktype, ex->e_socktype,\r
516 WILD_SOCKTYPE(ex))) {\r
517 continue;\r
518 }\r
519 if (!MATCH(pai->ai_protocol, ex->e_protocol,\r
520 WILD_PROTOCOL(ex))) {\r
521 continue;\r
522 }\r
523\r
524 if (pai->ai_socktype == ANY && ex->e_socktype != ANY)\r
525 pai->ai_socktype = ex->e_socktype;\r
526 if (pai->ai_protocol == ANY && ex->e_protocol != ANY)\r
527 pai->ai_protocol = ex->e_protocol;\r
528\r
529 error = explore_fqdn(pai, hostname, servname, &cur->ai_next,\r
530 &svd);\r
531\r
532 while (cur && cur->ai_next)\r
533 cur = cur->ai_next;\r
534 }\r
535\r
536 /* XXX */\r
537 if (sentinel.ai_next)\r
538 error = 0;\r
539\r
540 if (error)\r
541 goto free;\r
542\r
543 if (sentinel.ai_next) {\r
544 good:\r
545 endservent_r(&svd);\r
546 *res = sentinel.ai_next;\r
547 return SUCCESS;\r
548 } else\r
549 error = EAI_FAIL;\r
550 free:\r
551 bad:\r
552 endservent_r(&svd);\r
553 if (sentinel.ai_next)\r
554 freeaddrinfo(sentinel.ai_next);\r
555 *res = NULL;\r
556 return error;\r
557}\r
558\r
826f9005 559static const ns_dtab dtab[] = {\r
7700f0f5 560 NS_FILES_CB(((nss_method)_files_getaddrinfo), NULL)\r
561 { NSSRC_DNS, ((nss_method)_dns_getaddrinfo), NULL }, /* force -DHESIOD */\r
826f9005 562 NS_NIS_CB(_yp_getaddrinfo, NULL)\r
563 NS_NULL_CB\r
564};\r
565\r
3bdf9aae 566/*\r
567 * FQDN hostname, DNS lookup\r
568 */\r
569static int\r
570explore_fqdn(const struct addrinfo *pai, const char *hostname,\r
571 const char *servname, struct addrinfo **res, struct servent_data *svd)\r
572{\r
573 struct addrinfo *result;\r
574 struct addrinfo *cur;\r
575 int error = 0;\r
3bdf9aae 576\r
577 _DIAGASSERT(pai != NULL);\r
578 /* hostname may be NULL */\r
579 /* servname may be NULL */\r
580 _DIAGASSERT(res != NULL);\r
581\r
582 result = NULL;\r
583\r
584 /*\r
585 * if the servname does not match socktype/protocol, ignore it.\r
586 */\r
587 if (get_portmatch(pai, servname, svd) != 0)\r
588 return 0;\r
589\r
590 switch (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo",\r
591 default_dns_files, hostname, pai)) {\r
592 case NS_TRYAGAIN:\r
593 error = EAI_AGAIN;\r
594 goto free;\r
595 case NS_UNAVAIL:\r
596 error = EAI_FAIL;\r
597 goto free;\r
598 case NS_NOTFOUND:\r
599 error = EAI_NODATA;\r
600 goto free;\r
601 case NS_SUCCESS:\r
602 error = 0;\r
603 for (cur = result; cur; cur = cur->ai_next) {\r
604 GET_PORT(cur, servname, svd);\r
605 /* canonname should be filled already */\r
606 }\r
607 break;\r
608 }\r
609\r
610 *res = result;\r
611\r
612 return 0;\r
613\r
614free:\r
615 if (result)\r
616 freeaddrinfo(result);\r
617 return error;\r
618}\r
619\r
620/*\r
621 * hostname == NULL.\r
622 * passive socket -> anyaddr (0.0.0.0 or ::)\r
623 * non-passive socket -> localhost (127.0.0.1 or ::1)\r
624 */\r
625static int\r
626explore_null(const struct addrinfo *pai, const char *servname,\r
627 struct addrinfo **res, struct servent_data *svd)\r
628{\r
629 int s;\r
630 const struct afd *afd;\r
631 struct addrinfo *cur;\r
632 struct addrinfo sentinel;\r
633 int error;\r
634\r
635 _DIAGASSERT(pai != NULL);\r
636 /* servname may be NULL */\r
637 _DIAGASSERT(res != NULL);\r
638\r
639 *res = NULL;\r
640 sentinel.ai_next = NULL;\r
641 cur = &sentinel;\r
642\r
643 /*\r
644 * filter out AFs that are not supported by the kernel\r
645 * XXX errno?\r
646 */\r
647 s = socket(pai->ai_family, SOCK_DGRAM, 0);\r
648 if (s < 0) {\r
649 if (errno != EMFILE)\r
650 return 0;\r
651 } else\r
652 close(s);\r
653\r
654 /*\r
655 * if the servname does not match socktype/protocol, ignore it.\r
656 */\r
657 if (get_portmatch(pai, servname, svd) != 0)\r
658 return 0;\r
659\r
660 afd = find_afd(pai->ai_family);\r
661 if (afd == NULL)\r
662 return 0;\r
663\r
664 if (pai->ai_flags & AI_PASSIVE) {\r
665 GET_AI(cur->ai_next, afd, afd->a_addrany);\r
666 /* xxx meaningless?\r
667 * GET_CANONNAME(cur->ai_next, "anyaddr");\r
668 */\r
669 GET_PORT(cur->ai_next, servname, svd);\r
670 } else {\r
671 GET_AI(cur->ai_next, afd, afd->a_loopback);\r
672 /* xxx meaningless?\r
673 * GET_CANONNAME(cur->ai_next, "localhost");\r
674 */\r
675 GET_PORT(cur->ai_next, servname, svd);\r
676 }\r
677 cur = cur->ai_next;\r
678\r
679 *res = sentinel.ai_next;\r
680 return 0;\r
681\r
682free:\r
683 if (sentinel.ai_next)\r
684 freeaddrinfo(sentinel.ai_next);\r
685 return error;\r
686}\r
687\r
688/*\r
689 * numeric hostname\r
690 */\r
691static int\r
692explore_numeric(const struct addrinfo *pai, const char *hostname,\r
693 const char *servname, struct addrinfo **res, const char *canonname,\r
694 struct servent_data *svd)\r
695{\r
696 const struct afd *afd;\r
697 struct addrinfo *cur;\r
698 struct addrinfo sentinel;\r
699 int error;\r
700 char pton[PTON_MAX];\r
701\r
702 _DIAGASSERT(pai != NULL);\r
703 /* hostname may be NULL */\r
704 /* servname may be NULL */\r
705 _DIAGASSERT(res != NULL);\r
706\r
707 *res = NULL;\r
708 sentinel.ai_next = NULL;\r
709 cur = &sentinel;\r
710\r
711 /*\r
712 * if the servname does not match socktype/protocol, ignore it.\r
713 */\r
714 if (get_portmatch(pai, servname, svd) != 0)\r
715 return 0;\r
716\r
717 afd = find_afd(pai->ai_family);\r
718 if (afd == NULL)\r
719 return 0;\r
720\r
721 switch (afd->a_af) {\r
722#if 0 /*X/Open spec*/\r
723 case AF_INET:\r
724 if (inet_aton(hostname, (struct in_addr *)pton) == 1) {\r
725 if (pai->ai_family == afd->a_af ||\r
726 pai->ai_family == PF_UNSPEC /*?*/) {\r
727 GET_AI(cur->ai_next, afd, pton);\r
728 GET_PORT(cur->ai_next, servname, svd);\r
729 if ((pai->ai_flags & AI_CANONNAME)) {\r
730 /*\r
731 * Set the numeric address itself as\r
732 * the canonical name, based on a\r
733 * clarification in rfc2553bis-03.\r
734 */\r
735 GET_CANONNAME(cur->ai_next, canonname);\r
736 }\r
737 while (cur && cur->ai_next)\r
738 cur = cur->ai_next;\r
739 } else\r
740 ERR(EAI_FAMILY); /*xxx*/\r
741 }\r
742 break;\r
743#endif\r
744 default:\r
745 if (inet_pton(afd->a_af, hostname, pton) == 1) {\r
746 if (pai->ai_family == afd->a_af ||\r
747 pai->ai_family == PF_UNSPEC /*?*/) {\r
748 GET_AI(cur->ai_next, afd, pton);\r
749 GET_PORT(cur->ai_next, servname, svd);\r
750 if ((pai->ai_flags & AI_CANONNAME)) {\r
751 /*\r
752 * Set the numeric address itself as\r
753 * the canonical name, based on a\r
754 * clarification in rfc2553bis-03.\r
755 */\r
756 GET_CANONNAME(cur->ai_next, canonname);\r
757 }\r
758 while (cur->ai_next)\r
759 cur = cur->ai_next;\r
760 } else\r
761 ERR(EAI_FAMILY); /*xxx*/\r
762 }\r
763 break;\r
764 }\r
765\r
766 *res = sentinel.ai_next;\r
767 return 0;\r
768\r
769free:\r
770bad:\r
771 if (sentinel.ai_next)\r
772 freeaddrinfo(sentinel.ai_next);\r
773 return error;\r
774}\r
775\r
776/*\r
777 * numeric hostname with scope\r
778 */\r
779static int\r
780explore_numeric_scope(const struct addrinfo *pai, const char *hostname,\r
781 const char *servname, struct addrinfo **res, struct servent_data *svd)\r
782{\r
783#if !defined(SCOPE_DELIMITER) || !defined(INET6)\r
784 return explore_numeric(pai, hostname, servname, res, hostname, svd);\r
785#else\r
786 const struct afd *afd;\r
787 struct addrinfo *cur;\r
788 int error;\r
789 char *cp, *hostname2 = NULL, *scope, *addr;\r
790 struct sockaddr_in6 *sin6;\r
791\r
792 _DIAGASSERT(pai != NULL);\r
793 /* hostname may be NULL */\r
794 /* servname may be NULL */\r
795 _DIAGASSERT(res != NULL);\r
796\r
797 /*\r
798 * if the servname does not match socktype/protocol, ignore it.\r
799 */\r
800 if (get_portmatch(pai, servname, svd) != 0)\r
801 return 0;\r
802\r
803 afd = find_afd(pai->ai_family);\r
804 if (afd == NULL)\r
805 return 0;\r
806\r
807 if (!afd->a_scoped)\r
808 return explore_numeric(pai, hostname, servname, res, hostname,\r
809 svd);\r
810\r
811 cp = strchr(hostname, SCOPE_DELIMITER);\r
812 if (cp == NULL)\r
813 return explore_numeric(pai, hostname, servname, res, hostname,\r
814 svd);\r
815\r
816 /*\r
817 * Handle special case of <scoped_address><delimiter><scope id>\r
818 */\r
819 hostname2 = strdup(hostname);\r
820 if (hostname2 == NULL)\r
821 return EAI_MEMORY;\r
822 /* terminate at the delimiter */\r
823 hostname2[cp - hostname] = '\0';\r
824 addr = hostname2;\r
825 scope = cp + 1;\r
826\r
827 error = explore_numeric(pai, addr, servname, res, hostname, svd);\r
828 if (error == 0) {\r
829 u_int32_t scopeid;\r
830\r
831 for (cur = *res; cur; cur = cur->ai_next) {\r
832 if (cur->ai_family != AF_INET6)\r
833 continue;\r
834 sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr;\r
835 if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) {\r
836 free(hostname2);\r
837 return(EAI_NODATA); /* XXX: is return OK? */\r
838 }\r
839 sin6->sin6_scope_id = scopeid;\r
840 }\r
841 }\r
842\r
843 free(hostname2);\r
844\r
845 return error;\r
846#endif\r
847}\r
848\r
849static int\r
850get_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str)\r
851{\r
852\r
853 _DIAGASSERT(pai != NULL);\r
854 _DIAGASSERT(ai != NULL);\r
855 _DIAGASSERT(str != NULL);\r
856\r
857 if ((pai->ai_flags & AI_CANONNAME) != 0) {\r
858 ai->ai_canonname = strdup(str);\r
859 if (ai->ai_canonname == NULL)\r
860 return EAI_MEMORY;\r
861 }\r
862 return 0;\r
863}\r
864\r
865static struct addrinfo *\r
866get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr)\r
867{\r
868 char *p;\r
869 struct addrinfo *ai;\r
870\r
871 _DIAGASSERT(pai != NULL);\r
872 _DIAGASSERT(afd != NULL);\r
873 _DIAGASSERT(addr != NULL);\r
874\r
875 ai = (struct addrinfo *)malloc(sizeof(struct addrinfo)\r
876 + (afd->a_socklen));\r
877 if (ai == NULL)\r
878 return NULL;\r
879\r
880 memcpy(ai, pai, sizeof(struct addrinfo));\r
881 ai->ai_addr = (struct sockaddr *)(void *)(ai + 1);\r
882 memset(ai->ai_addr, 0, (size_t)afd->a_socklen);\r
883 ai->ai_addr->sa_len = (uint8_t)afd->a_socklen;\r
884 ai->ai_addrlen = afd->a_socklen;\r
885 ai->ai_family = afd->a_af;\r
886 ai->ai_addr->sa_family = (sa_family_t)ai->ai_family;\r
887 p = (char *)(void *)(ai->ai_addr);\r
888 memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen);\r
889 return ai;\r
890}\r
891\r
892static int\r
893get_portmatch(const struct addrinfo *ai, const char *servname,\r
894 struct servent_data *svd)\r
895{\r
896\r
897 _DIAGASSERT(ai != NULL);\r
898 /* servname may be NULL */\r
899\r
900 return get_port(ai, servname, 1, svd);\r
901}\r
902\r
903static int\r
904get_port(const struct addrinfo *ai, const char *servname, int matchonly,\r
905 struct servent_data *svd)\r
906{\r
907 const char *proto;\r
908 struct servent *sp;\r
909 int port;\r
910 int allownumeric;\r
911\r
912 _DIAGASSERT(ai != NULL);\r
913 /* servname may be NULL */\r
914\r
915 if (servname == NULL)\r
916 return 0;\r
917 switch (ai->ai_family) {\r
918 case AF_INET:\r
919#ifdef AF_INET6\r
920 case AF_INET6:\r
921#endif\r
922 break;\r
923 default:\r
924 return 0;\r
925 }\r
926\r
927 switch (ai->ai_socktype) {\r
928 case SOCK_RAW:\r
929 return EAI_SERVICE;\r
930 case SOCK_DGRAM:\r
931 case SOCK_STREAM:\r
932 allownumeric = 1;\r
933 break;\r
934 case ANY:\r
935 /*\r
936 * This was 0. It is now 1 so that queries specifying\r
937 * a NULL hint, or hint without socktype (but, hopefully,\r
938 * with protocol) and numeric address actually work.\r
939 */\r
940 allownumeric = 1;\r
941 break;\r
942 default:\r
943 return EAI_SOCKTYPE;\r
944 }\r
945\r
946 port = str2number(servname);\r
947 if (port >= 0) {\r
948 if (!allownumeric)\r
949 return EAI_SERVICE;\r
950 if (port < 0 || port > 65535)\r
951 return EAI_SERVICE;\r
952 port = htons(port);\r
953 } else {\r
954// struct servent sv;\r
955 if (ai->ai_flags & AI_NUMERICSERV)\r
956 return EAI_NONAME;\r
957\r
958 switch (ai->ai_socktype) {\r
959 case SOCK_DGRAM:\r
960 proto = "udp";\r
961 break;\r
962 case SOCK_STREAM:\r
963 proto = "tcp";\r
964 break;\r
965 default:\r
966 proto = NULL;\r
967 break;\r
968 }\r
969\r
970// sp = getservbyname_r(servname, proto, &sv, svd);\r
971 sp = getservbyname ( servname, proto );\r
972 if (sp == NULL)\r
973 return EAI_SERVICE;\r
974 port = sp->s_port;\r
975 }\r
976\r
977 if (!matchonly) {\r
978 switch (ai->ai_family) {\r
979 case AF_INET:\r
980 ((struct sockaddr_in *)(void *)\r
981 ai->ai_addr)->sin_port = (in_port_t)port;\r
982 break;\r
983#ifdef INET6\r
984 case AF_INET6:\r
985 ((struct sockaddr_in6 *)(void *)\r
986 ai->ai_addr)->sin6_port = (in_port_t)port;\r
987 break;\r
988#endif\r
989 }\r
990 }\r
991\r
992 return 0;\r
993}\r
994\r
995static const struct afd *\r
996find_afd(int af)\r
997{\r
998 const struct afd *afd;\r
999\r
1000 if (af == PF_UNSPEC)\r
1001 return NULL;\r
1002 for (afd = afdl; afd->a_af; afd++) {\r
1003 if (afd->a_af == af)\r
1004 return afd;\r
1005 }\r
1006 return NULL;\r
1007}\r
1008\r
1009#ifdef INET6\r
1010/* convert a string to a scope identifier. XXX: IPv6 specific */\r
1011static int\r
1012ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid)\r
1013{\r
1014 u_long lscopeid;\r
1015 struct in6_addr *a6;\r
1016 char *ep;\r
1017\r
1018 _DIAGASSERT(scope != NULL);\r
1019 _DIAGASSERT(sin6 != NULL);\r
1020 _DIAGASSERT(scopeid != NULL);\r
1021\r
1022 a6 = &sin6->sin6_addr;\r
1023\r
1024 /* empty scopeid portion is invalid */\r
1025 if (*scope == '\0')\r
1026 return -1;\r
1027\r
1028 if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) {\r
1029 /*\r
1030 * We currently assume a one-to-one mapping between links\r
1031 * and interfaces, so we simply use interface indices for\r
1032 * like-local scopes.\r
1033 */\r
1034/*\r
1035 *scopeid = if_nametoindex(scope);\r
1036 if (*scopeid == 0)\r
1037 goto trynumeric;\r
1038 return 0;\r
1039*/\r
1040 return -1;\r
1041 }\r
1042\r
1043 /* still unclear about literal, allow numeric only - placeholder */\r
1044 if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6))\r
1045 goto trynumeric;\r
1046 if (IN6_IS_ADDR_MC_ORGLOCAL(a6))\r
1047 goto trynumeric;\r
1048 else\r
1049 goto trynumeric; /* global */\r
1050\r
1051 /* try to convert to a numeric id as a last resort */\r
1052 trynumeric:\r
1053 errno = 0;\r
1054 lscopeid = strtoul(scope, &ep, 10);\r
1055 *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL);\r
1056 if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid)\r
1057 return 0;\r
1058 else\r
1059 return -1;\r
1060}\r
1061#endif\r
1062\r
1063/* code duplicate with gethnamaddr.c */\r
1064\r
1065static const char AskedForGot[] =\r
1066 "gethostby*.getanswer: asked for \"%s\", got \"%s\"";\r
1067\r
1068static struct addrinfo *\r
1069getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,\r
1070 const struct addrinfo *pai)\r
1071{\r
1072 struct addrinfo sentinel, *cur;\r
1073 struct addrinfo ai;\r
1074 const struct afd *afd;\r
1075 char *canonname;\r
1076 const HEADER *hp;\r
1077 const u_char *cp;\r
1078 int n;\r
1079 const u_char *eom;\r
1080 char *bp, *ep;\r
1081 int type, class, ancount, qdcount;\r
1082 int haveanswer, had_error;\r
1083 char tbuf[MAXDNAME];\r
1084 int (*name_ok) (const char *);\r
1085 static char hostbuf[8*1024];\r
1086\r
1087 _DIAGASSERT(answer != NULL);\r
1088 _DIAGASSERT(qname != NULL);\r
1089 _DIAGASSERT(pai != NULL);\r
1090\r
1091 memset(&sentinel, 0, sizeof(sentinel));\r
1092 cur = &sentinel;\r
1093\r
1094 canonname = NULL;\r
1095 eom = answer->buf + anslen;\r
1096 switch (qtype) {\r
1097 case T_A:\r
1098 case T_AAAA:\r
1099 case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/\r
1100 name_ok = res_hnok;\r
1101 break;\r
1102 default:\r
1103 return NULL; /* XXX should be abort(); */\r
1104 }\r
1105 /*\r
1106 * find first satisfactory answer\r
1107 */\r
1108 hp = &answer->hdr;\r
1109 ancount = ntohs(hp->ancount);\r
1110 qdcount = ntohs(hp->qdcount);\r
1111 bp = hostbuf;\r
1112 ep = hostbuf + sizeof hostbuf;\r
1113 cp = answer->buf + HFIXEDSZ;\r
1114 if (qdcount != 1) {\r
1115 h_errno = NO_RECOVERY;\r
1116 return (NULL);\r
1117 }\r
1118 n = dn_expand(answer->buf, eom, cp, bp, (int)(ep - bp));\r
1119 if ((n < 0) || !(*name_ok)(bp)) {\r
1120 h_errno = NO_RECOVERY;\r
1121 return (NULL);\r
1122 }\r
1123 cp += n + QFIXEDSZ;\r
1124 if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) {\r
1125 /* res_send() has already verified that the query name is the\r
1126 * same as the one we sent; this just gets the expanded name\r
1127 * (i.e., with the succeeding search-domain tacked on).\r
1128 */\r
1129 n = (int)strlen(bp) + 1; /* for the \0 */\r
1130 if (n >= MAXHOSTNAMELEN) {\r
1131 h_errno = NO_RECOVERY;\r
1132 return (NULL);\r
1133 }\r
1134 canonname = bp;\r
1135 bp += n;\r
1136 /* The qname can be abbreviated, but h_name is now absolute. */\r
1137 qname = canonname;\r
1138 }\r
1139 haveanswer = 0;\r
1140 had_error = 0;\r
1141 while (ancount-- > 0 && cp < eom && !had_error) {\r
1142 n = dn_expand(answer->buf, eom, cp, bp, (int)(ep - bp));\r
1143 if ((n < 0) || !(*name_ok)(bp)) {\r
1144 had_error++;\r
1145 continue;\r
1146 }\r
1147 cp += n; /* name */\r
1148 type = _getshort(cp);\r
1149 cp += INT16SZ; /* type */\r
1150 class = _getshort(cp);\r
1151 cp += INT16SZ + INT32SZ; /* class, TTL */\r
1152 n = _getshort(cp);\r
1153 cp += INT16SZ; /* len */\r
1154 if (class != C_IN) {\r
1155 /* XXX - debug? syslog? */\r
1156 cp += n;\r
1157 continue; /* XXX - had_error++ ? */\r
1158 }\r
1159 if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) &&\r
1160 type == T_CNAME) {\r
1161 n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);\r
1162 if ((n < 0) || !(*name_ok)(tbuf)) {\r
1163 had_error++;\r
1164 continue;\r
1165 }\r
1166 cp += n;\r
1167 /* Get canonical name. */\r
1168 n = (int)strlen(tbuf) + 1; /* for the \0 */\r
1169 if (n > ep - bp || n >= MAXHOSTNAMELEN) {\r
1170 had_error++;\r
1171 continue;\r
1172 }\r
1173 strlcpy(bp, tbuf, (size_t)(ep - bp));\r
1174 canonname = bp;\r
1175 bp += n;\r
1176 continue;\r
1177 }\r
1178 if (qtype == T_ANY) {\r
1179 if (!(type == T_A || type == T_AAAA)) {\r
1180 cp += n;\r
1181 continue;\r
1182 }\r
1183 } else if (type != qtype) {\r
1184 if (type != T_KEY && type != T_SIG) {\r
1185#ifdef _ORG_FREEBSD_\r
1186 struct syslog_data sd = SYSLOG_DATA_INIT;\r
1187 syslog_r(LOG_NOTICE|LOG_AUTH, &sd,\r
1188 "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"",\r
1189 qname, p_class(C_IN), p_type(qtype),\r
1190 p_type(type));\r
1191#endif\r
1192 }\r
1193 cp += n;\r
1194 continue; /* XXX - had_error++ ? */\r
1195 }\r
1196 switch (type) {\r
1197 case T_A:\r
1198 case T_AAAA:\r
1199 if (strcasecmp(canonname, bp) != 0) {\r
1200#ifdef _ORG_FREEBSD_\r
1201 struct syslog_data sd = SYSLOG_DATA_INIT;\r
1202 syslog_r(LOG_NOTICE|LOG_AUTH, &sd,\r
1203 AskedForGot, canonname, bp);\r
1204#endif\r
1205 cp += n;\r
1206 continue; /* XXX - had_error++ ? */\r
1207 }\r
1208 if (type == T_A && n != INADDRSZ) {\r
1209 cp += n;\r
1210 continue;\r
1211 }\r
1212 if (type == T_AAAA && n != IN6ADDRSZ) {\r
1213 cp += n;\r
1214 continue;\r
1215 }\r
1216 if (type == T_AAAA) {\r
1217 struct in6_addr in6;\r
1218 memcpy(&in6, cp, IN6ADDRSZ);\r
1219 if (IN6_IS_ADDR_V4MAPPED(&in6)) {\r
1220 cp += n;\r
1221 continue;\r
1222 }\r
1223 }\r
1224 if (!haveanswer) {\r
1225 int nn;\r
1226\r
1227 canonname = bp;\r
1228 nn = (int)strlen(bp) + 1; /* for the \0 */\r
1229 bp += nn;\r
1230 }\r
1231\r
1232 /* don't overwrite pai */\r
1233 ai = *pai;\r
1234 ai.ai_family = (type == T_A) ? AF_INET : AF_INET6;\r
1235 afd = find_afd(ai.ai_family);\r
1236 if (afd == NULL) {\r
1237 cp += n;\r
1238 continue;\r
1239 }\r
1240 cur->ai_next = get_ai(&ai, afd, (const char *)cp);\r
1241 if (cur->ai_next == NULL)\r
1242 had_error++;\r
1243 while (cur && cur->ai_next)\r
1244 cur = cur->ai_next;\r
1245 cp += n;\r
1246 break;\r
1247 default:\r
1248 abort();\r
1249 }\r
1250 if (!had_error)\r
1251 haveanswer++;\r
1252 }\r
1253 if (haveanswer) {\r
1254 if (!canonname)\r
1255 (void)get_canonname(pai, sentinel.ai_next, qname);\r
1256 else\r
1257 (void)get_canonname(pai, sentinel.ai_next, canonname);\r
1258 h_errno = NETDB_SUCCESS;\r
1259 return sentinel.ai_next;\r
1260 }\r
1261\r
1262 h_errno = NO_RECOVERY;\r
1263 return NULL;\r
1264}\r
1265\r
1266#define SORTEDADDR(p) (((struct sockaddr_in *)(void *)(p->ai_next->ai_addr))->sin_addr.s_addr)\r
1267#define SORTMATCH(p, s) ((SORTEDADDR(p) & (s).mask) == (s).addr.s_addr)\r
1268\r
1269static void\r
1270aisort(struct addrinfo *s, res_state res)\r
1271{\r
1272 struct addrinfo head, *t, *p;\r
1273 int i;\r
1274\r
1275 head.ai_next = NULL;\r
1276 t = &head;\r
1277\r
1278 for (i = 0; i < (int)res->nsort; i++) {\r
1279 p = s;\r
1280 while (p->ai_next) {\r
1281 if ((p->ai_next->ai_family != AF_INET)\r
1282 || SORTMATCH(p, res->sort_list[i])) {\r
1283 t->ai_next = p->ai_next;\r
1284 t = t->ai_next;\r
1285 p->ai_next = p->ai_next->ai_next;\r
1286 } else {\r
1287 p = p->ai_next;\r
1288 }\r
1289 }\r
1290 }\r
1291\r
1292 /* add rest of list and reset s to the new list*/\r
1293 t->ai_next = s->ai_next;\r
1294 s->ai_next = head.ai_next;\r
1295}\r
1296\r
1297/*ARGSUSED*/\r
1298static int\r
1299_dns_getaddrinfo(void *rv, void *cb_data, va_list ap)\r
1300{\r
1301 struct addrinfo *ai;\r
1302 querybuf *buf, *buf2;\r
1303 const char *name;\r
1304 const struct addrinfo *pai;\r
1305 struct addrinfo sentinel, *cur;\r
1306 struct res_target q, q2;\r
1307 res_state res;\r
1308\r
1309 name = va_arg(ap, char *);\r
1310 pai = va_arg(ap, const struct addrinfo *);\r
1311\r
1312 memset(&q, 0, sizeof(q));\r
1313 memset(&q2, 0, sizeof(q2));\r
1314 memset(&sentinel, 0, sizeof(sentinel));\r
1315 cur = &sentinel;\r
1316\r
1317 buf = malloc(sizeof(*buf));\r
1318 if (buf == NULL) {\r
1319 h_errno = NETDB_INTERNAL;\r
1320 return NS_NOTFOUND;\r
1321 }\r
1322 buf2 = malloc(sizeof(*buf2));\r
1323 if (buf2 == NULL) {\r
1324 free(buf);\r
1325 h_errno = NETDB_INTERNAL;\r
1326 return NS_NOTFOUND;\r
1327 }\r
1328\r
1329 switch (pai->ai_family) {\r
1330 case AF_UNSPEC:\r
1331 /* prefer IPv6 */\r
1332 q.name = name;\r
1333 q.qclass = C_IN;\r
1334 q.qtype = T_AAAA;\r
1335 q.answer = buf->buf;\r
1336 q.anslen = sizeof(buf->buf);\r
1337 q.next = &q2;\r
1338 q2.name = name;\r
1339 q2.qclass = C_IN;\r
1340 q2.qtype = T_A;\r
1341 q2.answer = buf2->buf;\r
1342 q2.anslen = sizeof(buf2->buf);\r
1343 break;\r
1344 case AF_INET:\r
1345 q.name = name;\r
1346 q.qclass = C_IN;\r
1347 q.qtype = T_A;\r
1348 q.answer = buf->buf;\r
1349 q.anslen = sizeof(buf->buf);\r
1350 break;\r
1351 case AF_INET6:\r
1352 q.name = name;\r
1353 q.qclass = C_IN;\r
1354 q.qtype = T_AAAA;\r
1355 q.answer = buf->buf;\r
1356 q.anslen = sizeof(buf->buf);\r
1357 break;\r
1358 default:\r
1359 free(buf);\r
1360 free(buf2);\r
1361 return NS_UNAVAIL;\r
1362 }\r
1363\r
1364 res = __res_get_state();\r
1365 if (res == NULL) {\r
1366 free(buf);\r
1367 free(buf2);\r
1368 return NS_NOTFOUND;\r
1369 }\r
1370\r
1371 if (res_searchN(name, &q, res) < 0) {\r
1372 __res_put_state(res);\r
1373 free(buf);\r
1374 free(buf2);\r
1375 return NS_NOTFOUND;\r
1376 }\r
1377 ai = getanswer(buf, q.n, q.name, q.qtype, pai);\r
1378 if (ai) {\r
1379 cur->ai_next = ai;\r
1380 while (cur && cur->ai_next)\r
1381 cur = cur->ai_next;\r
1382 }\r
1383 if (q.next) {\r
1384 ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai);\r
1385 if (ai)\r
1386 cur->ai_next = ai;\r
1387 }\r
1388 free(buf);\r
1389 free(buf2);\r
1390 if (sentinel.ai_next == NULL) {\r
1391 __res_put_state(res);\r
1392 switch (h_errno) {\r
1393 case HOST_NOT_FOUND:\r
1394 return NS_NOTFOUND;\r
1395 case TRY_AGAIN:\r
1396 return NS_TRYAGAIN;\r
1397 default:\r
1398 return NS_UNAVAIL;\r
1399 }\r
1400 }\r
1401\r
1402 if (res->nsort)\r
1403 aisort(&sentinel, res);\r
1404\r
1405 __res_put_state(res);\r
1406\r
1407 *((struct addrinfo **)rv) = sentinel.ai_next;\r
1408 return NS_SUCCESS;\r
1409}\r
1410\r
1411static void\r
1412_sethtent(FILE **hostf)\r
1413{\r
1414\r
1415 if (!*hostf)\r
1416 *hostf = fopen(_PATH_HOSTS, "r" );\r
1417 else\r
1418 rewind(*hostf);\r
1419}\r
1420\r
1421static void\r
1422_endhtent(FILE **hostf)\r
1423{\r
1424\r
1425 if (*hostf) {\r
1426 (void) fclose(*hostf);\r
1427 *hostf = NULL;\r
1428 }\r
1429}\r
1430\r
1431static struct addrinfo *\r
1432_gethtent(FILE **hostf, const char *name, const struct addrinfo *pai)\r
1433{\r
1434 char *p;\r
1435 char *cp, *tname, *cname;\r
1436 struct addrinfo hints, *res0, *res;\r
1437 int error;\r
1438 const char *addr;\r
1439 static char hostbuf[8*1024];\r
1440\r
1441 _DIAGASSERT(name != NULL);\r
1442 _DIAGASSERT(pai != NULL);\r
1443\r
1444 if (!*hostf && ( NULL == (*hostf = fopen(_PATH_HOSTS, "r" ))))\r
1445 return (NULL);\r
1446 again:\r
1447 if ( NULL == (p = fgets(hostbuf, sizeof hostbuf, *hostf)))\r
1448 return (NULL);\r
1449 if (*p == '#')\r
1450 goto again;\r
1451 if ( NULL == (cp = strpbrk(p, "#\n")))\r
1452 goto again;\r
1453 *cp = '\0';\r
1454 if ( NULL == (cp = strpbrk(p, " \t")))\r
1455 goto again;\r
1456 *cp++ = '\0';\r
1457 addr = p;\r
1458 /* if this is not something we're looking for, skip it. */\r
1459 cname = NULL;\r
1460 while (cp && *cp) {\r
1461 if (*cp == ' ' || *cp == '\t') {\r
1462 cp++;\r
1463 continue;\r
1464 }\r
1465 if (!cname)\r
1466 cname = cp;\r
1467 tname = cp;\r
1468 if ((cp = strpbrk(cp, " \t")) != NULL)\r
1469 *cp++ = '\0';\r
1470 if (strcasecmp(name, tname) == 0)\r
1471 goto found;\r
1472 }\r
1473 goto again;\r
1474\r
1475found:\r
1476 hints = *pai;\r
1477 hints.ai_flags = AI_NUMERICHOST;\r
1478 error = getaddrinfo(addr, NULL, &hints, &res0);\r
1479 if (error)\r
1480 goto again;\r
1481 for (res = res0; res; res = res->ai_next) {\r
1482 /* cover it up */\r
1483 res->ai_flags = pai->ai_flags;\r
1484\r
1485 if (pai->ai_flags & AI_CANONNAME) {\r
1486 if (get_canonname(pai, res, cname) != 0) {\r
1487 freeaddrinfo(res0);\r
1488 goto again;\r
1489 }\r
1490 }\r
1491 }\r
1492 return res0;\r
1493}\r
1494\r
1495/*ARGSUSED*/\r
1496static int\r
1497_files_getaddrinfo(void *rv, void *cb_data, va_list ap)\r
1498{\r
1499 const char *name;\r
1500 const struct addrinfo *pai;\r
1501 struct addrinfo sentinel, *cur;\r
1502 struct addrinfo *p;\r
1503#ifndef _REENTRANT\r
1504 static\r
1505#endif\r
1506 FILE *hostf = NULL;\r
1507\r
1508 name = va_arg(ap, char *);\r
1509 pai = va_arg(ap, const struct addrinfo *);\r
1510\r
1511 memset(&sentinel, 0, sizeof(sentinel));\r
1512 cur = &sentinel;\r
1513\r
1514 _sethtent(&hostf);\r
1515 while ((p = _gethtent(&hostf, name, pai)) != NULL) {\r
1516 cur->ai_next = p;\r
1517 while (cur && cur->ai_next)\r
1518 cur = cur->ai_next;\r
1519 }\r
1520 _endhtent(&hostf);\r
1521\r
1522 *((struct addrinfo **)rv) = sentinel.ai_next;\r
1523 if (sentinel.ai_next == NULL)\r
1524 return NS_NOTFOUND;\r
1525 return NS_SUCCESS;\r
1526}\r
1527\r
1528#ifdef YP\r
1529/*ARGSUSED*/\r
1530static struct addrinfo *\r
1531_yphostent(char *line, const struct addrinfo *pai)\r
1532{\r
1533 struct addrinfo sentinel, *cur;\r
1534 struct addrinfo hints, *res, *res0;\r
1535 int error;\r
1536 char *p;\r
1537 const char *addr, *canonname;\r
1538 char *nextline;\r
1539 char *cp;\r
1540\r
1541 _DIAGASSERT(line != NULL);\r
1542 _DIAGASSERT(pai != NULL);\r
1543\r
1544 p = line;\r
1545 addr = canonname = NULL;\r
1546\r
1547 memset(&sentinel, 0, sizeof(sentinel));\r
1548 cur = &sentinel;\r
1549\r
1550nextline:\r
1551 /* terminate line */\r
1552 cp = strchr(p, '\n');\r
1553 if (cp) {\r
1554 *cp++ = '\0';\r
1555 nextline = cp;\r
1556 } else\r
1557 nextline = NULL;\r
1558\r
1559 cp = strpbrk(p, " \t");\r
1560 if (cp == NULL) {\r
1561 if (canonname == NULL)\r
1562 return (NULL);\r
1563 else\r
1564 goto done;\r
1565 }\r
1566 *cp++ = '\0';\r
1567\r
1568 addr = p;\r
1569\r
1570 while (cp && *cp) {\r
1571 if (*cp == ' ' || *cp == '\t') {\r
1572 cp++;\r
1573 continue;\r
1574 }\r
1575 if (!canonname)\r
1576 canonname = cp;\r
1577 if ((cp = strpbrk(cp, " \t")) != NULL)\r
1578 *cp++ = '\0';\r
1579 }\r
1580\r
1581 hints = *pai;\r
1582 hints.ai_flags = AI_NUMERICHOST;\r
1583 error = getaddrinfo(addr, NULL, &hints, &res0);\r
1584 if (error == 0) {\r
1585 for (res = res0; res; res = res->ai_next) {\r
1586 /* cover it up */\r
1587 res->ai_flags = pai->ai_flags;\r
1588\r
1589 if (pai->ai_flags & AI_CANONNAME)\r
1590 (void)get_canonname(pai, res, canonname);\r
1591 }\r
1592 } else\r
1593 res0 = NULL;\r
1594 if (res0) {\r
1595 cur->ai_next = res0;\r
1596 while (cur->ai_next)\r
1597 cur = cur->ai_next;\r
1598 }\r
1599\r
1600 if (nextline) {\r
1601 p = nextline;\r
1602 goto nextline;\r
1603 }\r
1604\r
1605done:\r
1606 return sentinel.ai_next;\r
1607}\r
1608\r
1609/*ARGSUSED*/\r
1610static int\r
1611_yp_getaddrinfo(void *rv, void *cb_data, va_list ap)\r
1612{\r
1613 struct addrinfo sentinel, *cur;\r
1614 struct addrinfo *ai = NULL;\r
1615 char *ypbuf;\r
1616 int ypbuflen, r;\r
1617 const char *name;\r
1618 const struct addrinfo *pai;\r
1619 char *ypdomain;\r
1620\r
1621 if (_yp_check(&ypdomain) == 0)\r
1622 return NS_UNAVAIL;\r
1623\r
1624 name = va_arg(ap, char *);\r
1625 pai = va_arg(ap, const struct addrinfo *);\r
1626\r
1627 memset(&sentinel, 0, sizeof(sentinel));\r
1628 cur = &sentinel;\r
1629\r
1630 /* hosts.byname is only for IPv4 (Solaris8) */\r
1631 if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) {\r
1632 r = yp_match(ypdomain, "hosts.byname", name,\r
1633 (int)strlen(name), &ypbuf, &ypbuflen);\r
1634 if (r == 0) {\r
1635 struct addrinfo ai4;\r
1636\r
1637 ai4 = *pai;\r
1638 ai4.ai_family = AF_INET;\r
1639 ai = _yphostent(ypbuf, &ai4);\r
1640 if (ai) {\r
1641 cur->ai_next = ai;\r
1642 while (cur && cur->ai_next)\r
1643 cur = cur->ai_next;\r
1644 }\r
1645 }\r
1646 free(ypbuf);\r
1647 }\r
1648\r
1649 /* ipnodes.byname can hold both IPv4/v6 */\r
1650 r = yp_match(ypdomain, "ipnodes.byname", name,\r
1651 (int)strlen(name), &ypbuf, &ypbuflen);\r
1652 if (r == 0) {\r
1653 ai = _yphostent(ypbuf, pai);\r
1654 if (ai)\r
1655 cur->ai_next = ai;\r
1656 free(ypbuf);\r
1657 }\r
1658\r
1659 if (sentinel.ai_next == NULL) {\r
1660 h_errno = HOST_NOT_FOUND;\r
1661 return NS_NOTFOUND;\r
1662 }\r
1663 *((struct addrinfo **)rv) = sentinel.ai_next;\r
1664 return NS_SUCCESS;\r
1665}\r
1666#endif\r
1667\r
1668/* resolver logic */\r
1669\r
1670/*\r
1671 * Formulate a normal query, send, and await answer.\r
1672 * Returned answer is placed in supplied buffer "answer".\r
1673 * Perform preliminary check of answer, returning success only\r
1674 * if no error is indicated and the answer count is nonzero.\r
1675 * Return the size of the response on success, -1 on error.\r
1676 * Error number is left in h_errno.\r
1677 *\r
1678 * Caller must parse answer and determine whether it answers the question.\r
1679 */\r
1680static int\r
1681res_queryN(const char *name, /* domain name */ struct res_target *target,\r
1682 res_state res)\r
1683{\r
1684 static u_char buf[MAXPACKET];\r
1685 HEADER *hp;\r
1686 int n;\r
1687 struct res_target *t;\r
1688 int rcode;\r
1689 int ancount;\r
1690\r
1691 _DIAGASSERT(name != NULL);\r
1692 /* XXX: target may be NULL??? */\r
1693\r
1694 rcode = NOERROR;\r
1695 ancount = 0;\r
1696\r
1697 for (t = target; t; t = t->next) {\r
1698 int class, type;\r
1699 u_char *answer;\r
1700 int anslen;\r
1701\r
1702 hp = (HEADER *)(void *)t->answer;\r
1703 hp->rcode = NOERROR; /* default */\r
1704\r
1705 /* make it easier... */\r
1706 class = t->qclass;\r
1707 type = t->qtype;\r
1708 answer = t->answer;\r
1709 anslen = t->anslen;\r
1710#ifdef DEBUG\r
1711 if (res->options & RES_DEBUG)\r
1712 printf(";; res_nquery(%s, %d, %d)\n", name, class, type);\r
1713#endif\r
1714\r
1715 n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL,\r
1716 buf, sizeof(buf));\r
1717#ifdef RES_USE_EDNS0\r
1718 if (n > 0 && (res->options & RES_USE_EDNS0) != 0)\r
1719 n = res_nopt(res, n, buf, sizeof(buf), anslen);\r
1720#endif\r
1721 if (n <= 0) {\r
1722#ifdef DEBUG\r
1723 if (res->options & RES_DEBUG)\r
1724 printf(";; res_nquery: mkquery failed\n");\r
1725#endif\r
1726 h_errno = NO_RECOVERY;\r
1727 return n;\r
1728 }\r
1729 n = res_nsend(res, buf, n, answer, anslen);\r
1730#if 0\r
1731 if (n < 0) {\r
1732#ifdef DEBUG\r
1733 if (res->options & RES_DEBUG)\r
1734 printf(";; res_query: send error\n");\r
1735#endif\r
1736 h_errno = TRY_AGAIN;\r
1737 return n;\r
1738 }\r
1739#endif\r
1740\r
1741 if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {\r
1742 rcode = hp->rcode; /* record most recent error */\r
1743#ifdef DEBUG\r
1744 if (res->options & RES_DEBUG)\r
1745 printf(";; rcode = %u, ancount=%u\n", hp->rcode,\r
1746 ntohs(hp->ancount));\r
1747#endif\r
1748 continue;\r
1749 }\r
1750\r
1751 ancount += ntohs(hp->ancount);\r
1752\r
1753 t->n = n;\r
1754 }\r
1755\r
1756 if (ancount == 0) {\r
1757 switch (rcode) {\r
1758 case NXDOMAIN:\r
1759 h_errno = HOST_NOT_FOUND;\r
1760 break;\r
1761 case SERVFAIL:\r
1762 h_errno = TRY_AGAIN;\r
1763 break;\r
1764 case NOERROR:\r
1765 h_errno = NO_DATA;\r
1766 break;\r
1767 case FORMERR:\r
1768 case NOTIMP:\r
1769 case REFUSED:\r
1770 default:\r
1771 h_errno = NO_RECOVERY;\r
1772 break;\r
1773 }\r
1774 return -1;\r
1775 }\r
1776 return ancount;\r
1777}\r
1778\r
1779/*\r
1780 * Formulate a normal query, send, and retrieve answer in supplied buffer.\r
1781 * Return the size of the response on success, -1 on error.\r
1782 * If enabled, implement search rules until answer or unrecoverable failure\r
1783 * is detected. Error code, if any, is left in h_errno.\r
1784 */\r
1785static int\r
1786res_searchN(const char *name, struct res_target *target, res_state res)\r
1787{\r
1788 const char *cp, * const *domain;\r
1789 HEADER *hp;\r
1790 u_int dots;\r
1791 int trailing_dot, ret, saved_herrno;\r
1792 int got_nodata = 0, got_servfail = 0, tried_as_is = 0;\r
1793\r
1794 _DIAGASSERT(name != NULL);\r
1795 _DIAGASSERT(target != NULL);\r
1796\r
1797 hp = (HEADER *)(void *)target->answer; /*XXX*/\r
1798\r
1799 errno = 0;\r
1800 h_errno = HOST_NOT_FOUND; /* default, if we never query */\r
1801 dots = 0;\r
1802 for (cp = name; *cp; cp++)\r
1803 dots += (*cp == '.');\r
1804 trailing_dot = 0;\r
1805 if (cp > name && *--cp == '.')\r
1806 trailing_dot++;\r
1807\r
1808 /*\r
1809 * if there aren't any dots, it could be a user-level alias\r
1810 */\r
1811 if (!dots && (cp = __hostalias(name)) != NULL) {\r
1812 ret = res_queryN(cp, target, res);\r
1813 return ret;\r
1814 }\r
1815\r
1816 /*\r
1817 * If there are dots in the name already, let's just give it a try\r
1818 * 'as is'. The threshold can be set with the "ndots" option.\r
1819 */\r
1820 saved_herrno = -1;\r
1821 if (dots >= res->ndots) {\r
1822 ret = res_querydomainN(name, NULL, target, res);\r
1823 if (ret > 0)\r
1824 return (ret);\r
1825 saved_herrno = h_errno;\r
1826 tried_as_is++;\r
1827 }\r
1828\r
1829 /*\r
1830 * We do at least one level of search if\r
1831 * - there is no dot and RES_DEFNAME is set, or\r
1832 * - there is at least one dot, there is no trailing dot,\r
1833 * and RES_DNSRCH is set.\r
1834 */\r
1835 if ((!dots && (res->options & RES_DEFNAMES)) ||\r
1836 (dots && !trailing_dot && (res->options & RES_DNSRCH))) {\r
1837 int done = 0;\r
1838\r
1839 for (domain = (const char * const *)res->dnsrch;\r
1840 *domain && !done;\r
1841 domain++) {\r
1842\r
1843 ret = res_querydomainN(name, *domain, target, res);\r
1844 if (ret > 0)\r
1845 return ret;\r
1846\r
1847 /*\r
1848 * If no server present, give up.\r
1849 * If name isn't found in this domain,\r
1850 * keep trying higher domains in the search list\r
1851 * (if that's enabled).\r
1852 * On a NO_DATA error, keep trying, otherwise\r
1853 * a wildcard entry of another type could keep us\r
1854 * from finding this entry higher in the domain.\r
1855 * If we get some other error (negative answer or\r
1856 * server failure), then stop searching up,\r
1857 * but try the input name below in case it's\r
1858 * fully-qualified.\r
1859 */\r
1860 if (errno == ECONNREFUSED) {\r
1861 h_errno = TRY_AGAIN;\r
1862 return -1;\r
1863 }\r
1864\r
1865 switch (h_errno) {\r
1866 case NO_DATA:\r
1867 got_nodata++;\r
1868 /* FALLTHROUGH */\r
1869 case HOST_NOT_FOUND:\r
1870 /* keep trying */\r
1871 break;\r
1872 case TRY_AGAIN:\r
1873 if (hp->rcode == SERVFAIL) {\r
1874 /* try next search element, if any */\r
1875 got_servfail++;\r
1876 break;\r
1877 }\r
1878 /* FALLTHROUGH */\r
1879 default:\r
1880 /* anything else implies that we're done */\r
1881 done++;\r
1882 }\r
1883 /*\r
1884 * if we got here for some reason other than DNSRCH,\r
1885 * we only wanted one iteration of the loop, so stop.\r
1886 */\r
1887 if (!(res->options & RES_DNSRCH))\r
1888 done++;\r
1889 }\r
1890 }\r
1891\r
1892 /*\r
1893 * if we have not already tried the name "as is", do that now.\r
1894 * note that we do this regardless of how many dots were in the\r
1895 * name or whether it ends with a dot.\r
1896 */\r
1897 if (!tried_as_is) {\r
1898 ret = res_querydomainN(name, NULL, target, res);\r
1899 if (ret > 0)\r
1900 return ret;\r
1901 }\r
1902\r
1903 /*\r
1904 * if we got here, we didn't satisfy the search.\r
1905 * if we did an initial full query, return that query's h_errno\r
1906 * (note that we wouldn't be here if that query had succeeded).\r
1907 * else if we ever got a nodata, send that back as the reason.\r
1908 * else send back meaningless h_errno, that being the one from\r
1909 * the last DNSRCH we did.\r
1910 */\r
1911 if (saved_herrno != -1)\r
1912 h_errno = saved_herrno;\r
1913 else if (got_nodata)\r
1914 h_errno = NO_DATA;\r
1915 else if (got_servfail)\r
1916 h_errno = TRY_AGAIN;\r
1917 return -1;\r
1918}\r
1919\r
1920/*\r
1921 * Perform a call on res_query on the concatenation of name and domain,\r
1922 * removing a trailing dot from name if domain is NULL.\r
1923 */\r
1924static int\r
1925res_querydomainN(const char *name, const char *domain,\r
1926 struct res_target *target, res_state res)\r
1927{\r
1928 char nbuf[MAXDNAME];\r
1929 const char *longname = nbuf;\r
1930 size_t n, d;\r
1931\r
1932 _DIAGASSERT(name != NULL);\r
1933 /* XXX: target may be NULL??? */\r
1934\r
1935#ifdef DEBUG\r
1936 if (res->options & RES_DEBUG)\r
1937 printf(";; res_querydomain(%s, %s)\n",\r
1938 name, domain?domain:"<Nil>");\r
1939#endif\r
1940 if (domain == NULL) {\r
1941 /*\r
1942 * Check for trailing '.';\r
1943 * copy without '.' if present.\r
1944 */\r
1945 n = strlen(name);\r
1946 if (n + 1 > sizeof(nbuf)) {\r
1947 h_errno = NO_RECOVERY;\r
1948 return -1;\r
1949 }\r
1950 if (n > 0 && name[--n] == '.') {\r
1951 strncpy(nbuf, name, n);\r
1952 nbuf[n] = '\0';\r
1953 } else\r
1954 longname = name;\r
1955 } else {\r
1956 n = strlen(name);\r
1957 d = strlen(domain);\r
1958 if (n + 1 + d + 1 > sizeof(nbuf)) {\r
1959 h_errno = NO_RECOVERY;\r
1960 return -1;\r
1961 }\r
1962 snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain);\r
1963 }\r
1964 return res_queryN(longname, target, res);\r
1965}\r