]> git.proxmox.com Git - mirror_edk2.git/blame - StdLib/BsdSocketLib/getaddrinfo.c
Merged socket development branch:
[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
559/*\r
560 * FQDN hostname, DNS lookup\r
561 */\r
562static int\r
563explore_fqdn(const struct addrinfo *pai, const char *hostname,\r
564 const char *servname, struct addrinfo **res, struct servent_data *svd)\r
565{\r
566 struct addrinfo *result;\r
567 struct addrinfo *cur;\r
568 int error = 0;\r
569 static const ns_dtab dtab[] = {\r
570 NS_FILES_CB(_files_getaddrinfo, NULL)\r
571 { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */\r
572 NS_NIS_CB(_yp_getaddrinfo, NULL)\r
573 NS_NULL_CB\r
574 };\r
575\r
576 _DIAGASSERT(pai != NULL);\r
577 /* hostname may be NULL */\r
578 /* servname may be NULL */\r
579 _DIAGASSERT(res != NULL);\r
580\r
581 result = NULL;\r
582\r
583 /*\r
584 * if the servname does not match socktype/protocol, ignore it.\r
585 */\r
586 if (get_portmatch(pai, servname, svd) != 0)\r
587 return 0;\r
588\r
589 switch (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo",\r
590 default_dns_files, hostname, pai)) {\r
591 case NS_TRYAGAIN:\r
592 error = EAI_AGAIN;\r
593 goto free;\r
594 case NS_UNAVAIL:\r
595 error = EAI_FAIL;\r
596 goto free;\r
597 case NS_NOTFOUND:\r
598 error = EAI_NODATA;\r
599 goto free;\r
600 case NS_SUCCESS:\r
601 error = 0;\r
602 for (cur = result; cur; cur = cur->ai_next) {\r
603 GET_PORT(cur, servname, svd);\r
604 /* canonname should be filled already */\r
605 }\r
606 break;\r
607 }\r
608\r
609 *res = result;\r
610\r
611 return 0;\r
612\r
613free:\r
614 if (result)\r
615 freeaddrinfo(result);\r
616 return error;\r
617}\r
618\r
619/*\r
620 * hostname == NULL.\r
621 * passive socket -> anyaddr (0.0.0.0 or ::)\r
622 * non-passive socket -> localhost (127.0.0.1 or ::1)\r
623 */\r
624static int\r
625explore_null(const struct addrinfo *pai, const char *servname,\r
626 struct addrinfo **res, struct servent_data *svd)\r
627{\r
628 int s;\r
629 const struct afd *afd;\r
630 struct addrinfo *cur;\r
631 struct addrinfo sentinel;\r
632 int error;\r
633\r
634 _DIAGASSERT(pai != NULL);\r
635 /* servname may be NULL */\r
636 _DIAGASSERT(res != NULL);\r
637\r
638 *res = NULL;\r
639 sentinel.ai_next = NULL;\r
640 cur = &sentinel;\r
641\r
642 /*\r
643 * filter out AFs that are not supported by the kernel\r
644 * XXX errno?\r
645 */\r
646 s = socket(pai->ai_family, SOCK_DGRAM, 0);\r
647 if (s < 0) {\r
648 if (errno != EMFILE)\r
649 return 0;\r
650 } else\r
651 close(s);\r
652\r
653 /*\r
654 * if the servname does not match socktype/protocol, ignore it.\r
655 */\r
656 if (get_portmatch(pai, servname, svd) != 0)\r
657 return 0;\r
658\r
659 afd = find_afd(pai->ai_family);\r
660 if (afd == NULL)\r
661 return 0;\r
662\r
663 if (pai->ai_flags & AI_PASSIVE) {\r
664 GET_AI(cur->ai_next, afd, afd->a_addrany);\r
665 /* xxx meaningless?\r
666 * GET_CANONNAME(cur->ai_next, "anyaddr");\r
667 */\r
668 GET_PORT(cur->ai_next, servname, svd);\r
669 } else {\r
670 GET_AI(cur->ai_next, afd, afd->a_loopback);\r
671 /* xxx meaningless?\r
672 * GET_CANONNAME(cur->ai_next, "localhost");\r
673 */\r
674 GET_PORT(cur->ai_next, servname, svd);\r
675 }\r
676 cur = cur->ai_next;\r
677\r
678 *res = sentinel.ai_next;\r
679 return 0;\r
680\r
681free:\r
682 if (sentinel.ai_next)\r
683 freeaddrinfo(sentinel.ai_next);\r
684 return error;\r
685}\r
686\r
687/*\r
688 * numeric hostname\r
689 */\r
690static int\r
691explore_numeric(const struct addrinfo *pai, const char *hostname,\r
692 const char *servname, struct addrinfo **res, const char *canonname,\r
693 struct servent_data *svd)\r
694{\r
695 const struct afd *afd;\r
696 struct addrinfo *cur;\r
697 struct addrinfo sentinel;\r
698 int error;\r
699 char pton[PTON_MAX];\r
700\r
701 _DIAGASSERT(pai != NULL);\r
702 /* hostname may be NULL */\r
703 /* servname may be NULL */\r
704 _DIAGASSERT(res != NULL);\r
705\r
706 *res = NULL;\r
707 sentinel.ai_next = NULL;\r
708 cur = &sentinel;\r
709\r
710 /*\r
711 * if the servname does not match socktype/protocol, ignore it.\r
712 */\r
713 if (get_portmatch(pai, servname, svd) != 0)\r
714 return 0;\r
715\r
716 afd = find_afd(pai->ai_family);\r
717 if (afd == NULL)\r
718 return 0;\r
719\r
720 switch (afd->a_af) {\r
721#if 0 /*X/Open spec*/\r
722 case AF_INET:\r
723 if (inet_aton(hostname, (struct in_addr *)pton) == 1) {\r
724 if (pai->ai_family == afd->a_af ||\r
725 pai->ai_family == PF_UNSPEC /*?*/) {\r
726 GET_AI(cur->ai_next, afd, pton);\r
727 GET_PORT(cur->ai_next, servname, svd);\r
728 if ((pai->ai_flags & AI_CANONNAME)) {\r
729 /*\r
730 * Set the numeric address itself as\r
731 * the canonical name, based on a\r
732 * clarification in rfc2553bis-03.\r
733 */\r
734 GET_CANONNAME(cur->ai_next, canonname);\r
735 }\r
736 while (cur && cur->ai_next)\r
737 cur = cur->ai_next;\r
738 } else\r
739 ERR(EAI_FAMILY); /*xxx*/\r
740 }\r
741 break;\r
742#endif\r
743 default:\r
744 if (inet_pton(afd->a_af, hostname, pton) == 1) {\r
745 if (pai->ai_family == afd->a_af ||\r
746 pai->ai_family == PF_UNSPEC /*?*/) {\r
747 GET_AI(cur->ai_next, afd, pton);\r
748 GET_PORT(cur->ai_next, servname, svd);\r
749 if ((pai->ai_flags & AI_CANONNAME)) {\r
750 /*\r
751 * Set the numeric address itself as\r
752 * the canonical name, based on a\r
753 * clarification in rfc2553bis-03.\r
754 */\r
755 GET_CANONNAME(cur->ai_next, canonname);\r
756 }\r
757 while (cur->ai_next)\r
758 cur = cur->ai_next;\r
759 } else\r
760 ERR(EAI_FAMILY); /*xxx*/\r
761 }\r
762 break;\r
763 }\r
764\r
765 *res = sentinel.ai_next;\r
766 return 0;\r
767\r
768free:\r
769bad:\r
770 if (sentinel.ai_next)\r
771 freeaddrinfo(sentinel.ai_next);\r
772 return error;\r
773}\r
774\r
775/*\r
776 * numeric hostname with scope\r
777 */\r
778static int\r
779explore_numeric_scope(const struct addrinfo *pai, const char *hostname,\r
780 const char *servname, struct addrinfo **res, struct servent_data *svd)\r
781{\r
782#if !defined(SCOPE_DELIMITER) || !defined(INET6)\r
783 return explore_numeric(pai, hostname, servname, res, hostname, svd);\r
784#else\r
785 const struct afd *afd;\r
786 struct addrinfo *cur;\r
787 int error;\r
788 char *cp, *hostname2 = NULL, *scope, *addr;\r
789 struct sockaddr_in6 *sin6;\r
790\r
791 _DIAGASSERT(pai != NULL);\r
792 /* hostname may be NULL */\r
793 /* servname may be NULL */\r
794 _DIAGASSERT(res != NULL);\r
795\r
796 /*\r
797 * if the servname does not match socktype/protocol, ignore it.\r
798 */\r
799 if (get_portmatch(pai, servname, svd) != 0)\r
800 return 0;\r
801\r
802 afd = find_afd(pai->ai_family);\r
803 if (afd == NULL)\r
804 return 0;\r
805\r
806 if (!afd->a_scoped)\r
807 return explore_numeric(pai, hostname, servname, res, hostname,\r
808 svd);\r
809\r
810 cp = strchr(hostname, SCOPE_DELIMITER);\r
811 if (cp == NULL)\r
812 return explore_numeric(pai, hostname, servname, res, hostname,\r
813 svd);\r
814\r
815 /*\r
816 * Handle special case of <scoped_address><delimiter><scope id>\r
817 */\r
818 hostname2 = strdup(hostname);\r
819 if (hostname2 == NULL)\r
820 return EAI_MEMORY;\r
821 /* terminate at the delimiter */\r
822 hostname2[cp - hostname] = '\0';\r
823 addr = hostname2;\r
824 scope = cp + 1;\r
825\r
826 error = explore_numeric(pai, addr, servname, res, hostname, svd);\r
827 if (error == 0) {\r
828 u_int32_t scopeid;\r
829\r
830 for (cur = *res; cur; cur = cur->ai_next) {\r
831 if (cur->ai_family != AF_INET6)\r
832 continue;\r
833 sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr;\r
834 if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) {\r
835 free(hostname2);\r
836 return(EAI_NODATA); /* XXX: is return OK? */\r
837 }\r
838 sin6->sin6_scope_id = scopeid;\r
839 }\r
840 }\r
841\r
842 free(hostname2);\r
843\r
844 return error;\r
845#endif\r
846}\r
847\r
848static int\r
849get_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str)\r
850{\r
851\r
852 _DIAGASSERT(pai != NULL);\r
853 _DIAGASSERT(ai != NULL);\r
854 _DIAGASSERT(str != NULL);\r
855\r
856 if ((pai->ai_flags & AI_CANONNAME) != 0) {\r
857 ai->ai_canonname = strdup(str);\r
858 if (ai->ai_canonname == NULL)\r
859 return EAI_MEMORY;\r
860 }\r
861 return 0;\r
862}\r
863\r
864static struct addrinfo *\r
865get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr)\r
866{\r
867 char *p;\r
868 struct addrinfo *ai;\r
869\r
870 _DIAGASSERT(pai != NULL);\r
871 _DIAGASSERT(afd != NULL);\r
872 _DIAGASSERT(addr != NULL);\r
873\r
874 ai = (struct addrinfo *)malloc(sizeof(struct addrinfo)\r
875 + (afd->a_socklen));\r
876 if (ai == NULL)\r
877 return NULL;\r
878\r
879 memcpy(ai, pai, sizeof(struct addrinfo));\r
880 ai->ai_addr = (struct sockaddr *)(void *)(ai + 1);\r
881 memset(ai->ai_addr, 0, (size_t)afd->a_socklen);\r
882 ai->ai_addr->sa_len = (uint8_t)afd->a_socklen;\r
883 ai->ai_addrlen = afd->a_socklen;\r
884 ai->ai_family = afd->a_af;\r
885 ai->ai_addr->sa_family = (sa_family_t)ai->ai_family;\r
886 p = (char *)(void *)(ai->ai_addr);\r
887 memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen);\r
888 return ai;\r
889}\r
890\r
891static int\r
892get_portmatch(const struct addrinfo *ai, const char *servname,\r
893 struct servent_data *svd)\r
894{\r
895\r
896 _DIAGASSERT(ai != NULL);\r
897 /* servname may be NULL */\r
898\r
899 return get_port(ai, servname, 1, svd);\r
900}\r
901\r
902static int\r
903get_port(const struct addrinfo *ai, const char *servname, int matchonly,\r
904 struct servent_data *svd)\r
905{\r
906 const char *proto;\r
907 struct servent *sp;\r
908 int port;\r
909 int allownumeric;\r
910\r
911 _DIAGASSERT(ai != NULL);\r
912 /* servname may be NULL */\r
913\r
914 if (servname == NULL)\r
915 return 0;\r
916 switch (ai->ai_family) {\r
917 case AF_INET:\r
918#ifdef AF_INET6\r
919 case AF_INET6:\r
920#endif\r
921 break;\r
922 default:\r
923 return 0;\r
924 }\r
925\r
926 switch (ai->ai_socktype) {\r
927 case SOCK_RAW:\r
928 return EAI_SERVICE;\r
929 case SOCK_DGRAM:\r
930 case SOCK_STREAM:\r
931 allownumeric = 1;\r
932 break;\r
933 case ANY:\r
934 /*\r
935 * This was 0. It is now 1 so that queries specifying\r
936 * a NULL hint, or hint without socktype (but, hopefully,\r
937 * with protocol) and numeric address actually work.\r
938 */\r
939 allownumeric = 1;\r
940 break;\r
941 default:\r
942 return EAI_SOCKTYPE;\r
943 }\r
944\r
945 port = str2number(servname);\r
946 if (port >= 0) {\r
947 if (!allownumeric)\r
948 return EAI_SERVICE;\r
949 if (port < 0 || port > 65535)\r
950 return EAI_SERVICE;\r
951 port = htons(port);\r
952 } else {\r
953// struct servent sv;\r
954 if (ai->ai_flags & AI_NUMERICSERV)\r
955 return EAI_NONAME;\r
956\r
957 switch (ai->ai_socktype) {\r
958 case SOCK_DGRAM:\r
959 proto = "udp";\r
960 break;\r
961 case SOCK_STREAM:\r
962 proto = "tcp";\r
963 break;\r
964 default:\r
965 proto = NULL;\r
966 break;\r
967 }\r
968\r
969// sp = getservbyname_r(servname, proto, &sv, svd);\r
970 sp = getservbyname ( servname, proto );\r
971 if (sp == NULL)\r
972 return EAI_SERVICE;\r
973 port = sp->s_port;\r
974 }\r
975\r
976 if (!matchonly) {\r
977 switch (ai->ai_family) {\r
978 case AF_INET:\r
979 ((struct sockaddr_in *)(void *)\r
980 ai->ai_addr)->sin_port = (in_port_t)port;\r
981 break;\r
982#ifdef INET6\r
983 case AF_INET6:\r
984 ((struct sockaddr_in6 *)(void *)\r
985 ai->ai_addr)->sin6_port = (in_port_t)port;\r
986 break;\r
987#endif\r
988 }\r
989 }\r
990\r
991 return 0;\r
992}\r
993\r
994static const struct afd *\r
995find_afd(int af)\r
996{\r
997 const struct afd *afd;\r
998\r
999 if (af == PF_UNSPEC)\r
1000 return NULL;\r
1001 for (afd = afdl; afd->a_af; afd++) {\r
1002 if (afd->a_af == af)\r
1003 return afd;\r
1004 }\r
1005 return NULL;\r
1006}\r
1007\r
1008#ifdef INET6\r
1009/* convert a string to a scope identifier. XXX: IPv6 specific */\r
1010static int\r
1011ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid)\r
1012{\r
1013 u_long lscopeid;\r
1014 struct in6_addr *a6;\r
1015 char *ep;\r
1016\r
1017 _DIAGASSERT(scope != NULL);\r
1018 _DIAGASSERT(sin6 != NULL);\r
1019 _DIAGASSERT(scopeid != NULL);\r
1020\r
1021 a6 = &sin6->sin6_addr;\r
1022\r
1023 /* empty scopeid portion is invalid */\r
1024 if (*scope == '\0')\r
1025 return -1;\r
1026\r
1027 if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) {\r
1028 /*\r
1029 * We currently assume a one-to-one mapping between links\r
1030 * and interfaces, so we simply use interface indices for\r
1031 * like-local scopes.\r
1032 */\r
1033/*\r
1034 *scopeid = if_nametoindex(scope);\r
1035 if (*scopeid == 0)\r
1036 goto trynumeric;\r
1037 return 0;\r
1038*/\r
1039 return -1;\r
1040 }\r
1041\r
1042 /* still unclear about literal, allow numeric only - placeholder */\r
1043 if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6))\r
1044 goto trynumeric;\r
1045 if (IN6_IS_ADDR_MC_ORGLOCAL(a6))\r
1046 goto trynumeric;\r
1047 else\r
1048 goto trynumeric; /* global */\r
1049\r
1050 /* try to convert to a numeric id as a last resort */\r
1051 trynumeric:\r
1052 errno = 0;\r
1053 lscopeid = strtoul(scope, &ep, 10);\r
1054 *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL);\r
1055 if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid)\r
1056 return 0;\r
1057 else\r
1058 return -1;\r
1059}\r
1060#endif\r
1061\r
1062/* code duplicate with gethnamaddr.c */\r
1063\r
1064static const char AskedForGot[] =\r
1065 "gethostby*.getanswer: asked for \"%s\", got \"%s\"";\r
1066\r
1067static struct addrinfo *\r
1068getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,\r
1069 const struct addrinfo *pai)\r
1070{\r
1071 struct addrinfo sentinel, *cur;\r
1072 struct addrinfo ai;\r
1073 const struct afd *afd;\r
1074 char *canonname;\r
1075 const HEADER *hp;\r
1076 const u_char *cp;\r
1077 int n;\r
1078 const u_char *eom;\r
1079 char *bp, *ep;\r
1080 int type, class, ancount, qdcount;\r
1081 int haveanswer, had_error;\r
1082 char tbuf[MAXDNAME];\r
1083 int (*name_ok) (const char *);\r
1084 static char hostbuf[8*1024];\r
1085\r
1086 _DIAGASSERT(answer != NULL);\r
1087 _DIAGASSERT(qname != NULL);\r
1088 _DIAGASSERT(pai != NULL);\r
1089\r
1090 memset(&sentinel, 0, sizeof(sentinel));\r
1091 cur = &sentinel;\r
1092\r
1093 canonname = NULL;\r
1094 eom = answer->buf + anslen;\r
1095 switch (qtype) {\r
1096 case T_A:\r
1097 case T_AAAA:\r
1098 case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/\r
1099 name_ok = res_hnok;\r
1100 break;\r
1101 default:\r
1102 return NULL; /* XXX should be abort(); */\r
1103 }\r
1104 /*\r
1105 * find first satisfactory answer\r
1106 */\r
1107 hp = &answer->hdr;\r
1108 ancount = ntohs(hp->ancount);\r
1109 qdcount = ntohs(hp->qdcount);\r
1110 bp = hostbuf;\r
1111 ep = hostbuf + sizeof hostbuf;\r
1112 cp = answer->buf + HFIXEDSZ;\r
1113 if (qdcount != 1) {\r
1114 h_errno = NO_RECOVERY;\r
1115 return (NULL);\r
1116 }\r
1117 n = dn_expand(answer->buf, eom, cp, bp, (int)(ep - bp));\r
1118 if ((n < 0) || !(*name_ok)(bp)) {\r
1119 h_errno = NO_RECOVERY;\r
1120 return (NULL);\r
1121 }\r
1122 cp += n + QFIXEDSZ;\r
1123 if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) {\r
1124 /* res_send() has already verified that the query name is the\r
1125 * same as the one we sent; this just gets the expanded name\r
1126 * (i.e., with the succeeding search-domain tacked on).\r
1127 */\r
1128 n = (int)strlen(bp) + 1; /* for the \0 */\r
1129 if (n >= MAXHOSTNAMELEN) {\r
1130 h_errno = NO_RECOVERY;\r
1131 return (NULL);\r
1132 }\r
1133 canonname = bp;\r
1134 bp += n;\r
1135 /* The qname can be abbreviated, but h_name is now absolute. */\r
1136 qname = canonname;\r
1137 }\r
1138 haveanswer = 0;\r
1139 had_error = 0;\r
1140 while (ancount-- > 0 && cp < eom && !had_error) {\r
1141 n = dn_expand(answer->buf, eom, cp, bp, (int)(ep - bp));\r
1142 if ((n < 0) || !(*name_ok)(bp)) {\r
1143 had_error++;\r
1144 continue;\r
1145 }\r
1146 cp += n; /* name */\r
1147 type = _getshort(cp);\r
1148 cp += INT16SZ; /* type */\r
1149 class = _getshort(cp);\r
1150 cp += INT16SZ + INT32SZ; /* class, TTL */\r
1151 n = _getshort(cp);\r
1152 cp += INT16SZ; /* len */\r
1153 if (class != C_IN) {\r
1154 /* XXX - debug? syslog? */\r
1155 cp += n;\r
1156 continue; /* XXX - had_error++ ? */\r
1157 }\r
1158 if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) &&\r
1159 type == T_CNAME) {\r
1160 n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);\r
1161 if ((n < 0) || !(*name_ok)(tbuf)) {\r
1162 had_error++;\r
1163 continue;\r
1164 }\r
1165 cp += n;\r
1166 /* Get canonical name. */\r
1167 n = (int)strlen(tbuf) + 1; /* for the \0 */\r
1168 if (n > ep - bp || n >= MAXHOSTNAMELEN) {\r
1169 had_error++;\r
1170 continue;\r
1171 }\r
1172 strlcpy(bp, tbuf, (size_t)(ep - bp));\r
1173 canonname = bp;\r
1174 bp += n;\r
1175 continue;\r
1176 }\r
1177 if (qtype == T_ANY) {\r
1178 if (!(type == T_A || type == T_AAAA)) {\r
1179 cp += n;\r
1180 continue;\r
1181 }\r
1182 } else if (type != qtype) {\r
1183 if (type != T_KEY && type != T_SIG) {\r
1184#ifdef _ORG_FREEBSD_\r
1185 struct syslog_data sd = SYSLOG_DATA_INIT;\r
1186 syslog_r(LOG_NOTICE|LOG_AUTH, &sd,\r
1187 "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"",\r
1188 qname, p_class(C_IN), p_type(qtype),\r
1189 p_type(type));\r
1190#endif\r
1191 }\r
1192 cp += n;\r
1193 continue; /* XXX - had_error++ ? */\r
1194 }\r
1195 switch (type) {\r
1196 case T_A:\r
1197 case T_AAAA:\r
1198 if (strcasecmp(canonname, bp) != 0) {\r
1199#ifdef _ORG_FREEBSD_\r
1200 struct syslog_data sd = SYSLOG_DATA_INIT;\r
1201 syslog_r(LOG_NOTICE|LOG_AUTH, &sd,\r
1202 AskedForGot, canonname, bp);\r
1203#endif\r
1204 cp += n;\r
1205 continue; /* XXX - had_error++ ? */\r
1206 }\r
1207 if (type == T_A && n != INADDRSZ) {\r
1208 cp += n;\r
1209 continue;\r
1210 }\r
1211 if (type == T_AAAA && n != IN6ADDRSZ) {\r
1212 cp += n;\r
1213 continue;\r
1214 }\r
1215 if (type == T_AAAA) {\r
1216 struct in6_addr in6;\r
1217 memcpy(&in6, cp, IN6ADDRSZ);\r
1218 if (IN6_IS_ADDR_V4MAPPED(&in6)) {\r
1219 cp += n;\r
1220 continue;\r
1221 }\r
1222 }\r
1223 if (!haveanswer) {\r
1224 int nn;\r
1225\r
1226 canonname = bp;\r
1227 nn = (int)strlen(bp) + 1; /* for the \0 */\r
1228 bp += nn;\r
1229 }\r
1230\r
1231 /* don't overwrite pai */\r
1232 ai = *pai;\r
1233 ai.ai_family = (type == T_A) ? AF_INET : AF_INET6;\r
1234 afd = find_afd(ai.ai_family);\r
1235 if (afd == NULL) {\r
1236 cp += n;\r
1237 continue;\r
1238 }\r
1239 cur->ai_next = get_ai(&ai, afd, (const char *)cp);\r
1240 if (cur->ai_next == NULL)\r
1241 had_error++;\r
1242 while (cur && cur->ai_next)\r
1243 cur = cur->ai_next;\r
1244 cp += n;\r
1245 break;\r
1246 default:\r
1247 abort();\r
1248 }\r
1249 if (!had_error)\r
1250 haveanswer++;\r
1251 }\r
1252 if (haveanswer) {\r
1253 if (!canonname)\r
1254 (void)get_canonname(pai, sentinel.ai_next, qname);\r
1255 else\r
1256 (void)get_canonname(pai, sentinel.ai_next, canonname);\r
1257 h_errno = NETDB_SUCCESS;\r
1258 return sentinel.ai_next;\r
1259 }\r
1260\r
1261 h_errno = NO_RECOVERY;\r
1262 return NULL;\r
1263}\r
1264\r
1265#define SORTEDADDR(p) (((struct sockaddr_in *)(void *)(p->ai_next->ai_addr))->sin_addr.s_addr)\r
1266#define SORTMATCH(p, s) ((SORTEDADDR(p) & (s).mask) == (s).addr.s_addr)\r
1267\r
1268static void\r
1269aisort(struct addrinfo *s, res_state res)\r
1270{\r
1271 struct addrinfo head, *t, *p;\r
1272 int i;\r
1273\r
1274 head.ai_next = NULL;\r
1275 t = &head;\r
1276\r
1277 for (i = 0; i < (int)res->nsort; i++) {\r
1278 p = s;\r
1279 while (p->ai_next) {\r
1280 if ((p->ai_next->ai_family != AF_INET)\r
1281 || SORTMATCH(p, res->sort_list[i])) {\r
1282 t->ai_next = p->ai_next;\r
1283 t = t->ai_next;\r
1284 p->ai_next = p->ai_next->ai_next;\r
1285 } else {\r
1286 p = p->ai_next;\r
1287 }\r
1288 }\r
1289 }\r
1290\r
1291 /* add rest of list and reset s to the new list*/\r
1292 t->ai_next = s->ai_next;\r
1293 s->ai_next = head.ai_next;\r
1294}\r
1295\r
1296/*ARGSUSED*/\r
1297static int\r
1298_dns_getaddrinfo(void *rv, void *cb_data, va_list ap)\r
1299{\r
1300 struct addrinfo *ai;\r
1301 querybuf *buf, *buf2;\r
1302 const char *name;\r
1303 const struct addrinfo *pai;\r
1304 struct addrinfo sentinel, *cur;\r
1305 struct res_target q, q2;\r
1306 res_state res;\r
1307\r
1308 name = va_arg(ap, char *);\r
1309 pai = va_arg(ap, const struct addrinfo *);\r
1310\r
1311 memset(&q, 0, sizeof(q));\r
1312 memset(&q2, 0, sizeof(q2));\r
1313 memset(&sentinel, 0, sizeof(sentinel));\r
1314 cur = &sentinel;\r
1315\r
1316 buf = malloc(sizeof(*buf));\r
1317 if (buf == NULL) {\r
1318 h_errno = NETDB_INTERNAL;\r
1319 return NS_NOTFOUND;\r
1320 }\r
1321 buf2 = malloc(sizeof(*buf2));\r
1322 if (buf2 == NULL) {\r
1323 free(buf);\r
1324 h_errno = NETDB_INTERNAL;\r
1325 return NS_NOTFOUND;\r
1326 }\r
1327\r
1328 switch (pai->ai_family) {\r
1329 case AF_UNSPEC:\r
1330 /* prefer IPv6 */\r
1331 q.name = name;\r
1332 q.qclass = C_IN;\r
1333 q.qtype = T_AAAA;\r
1334 q.answer = buf->buf;\r
1335 q.anslen = sizeof(buf->buf);\r
1336 q.next = &q2;\r
1337 q2.name = name;\r
1338 q2.qclass = C_IN;\r
1339 q2.qtype = T_A;\r
1340 q2.answer = buf2->buf;\r
1341 q2.anslen = sizeof(buf2->buf);\r
1342 break;\r
1343 case AF_INET:\r
1344 q.name = name;\r
1345 q.qclass = C_IN;\r
1346 q.qtype = T_A;\r
1347 q.answer = buf->buf;\r
1348 q.anslen = sizeof(buf->buf);\r
1349 break;\r
1350 case AF_INET6:\r
1351 q.name = name;\r
1352 q.qclass = C_IN;\r
1353 q.qtype = T_AAAA;\r
1354 q.answer = buf->buf;\r
1355 q.anslen = sizeof(buf->buf);\r
1356 break;\r
1357 default:\r
1358 free(buf);\r
1359 free(buf2);\r
1360 return NS_UNAVAIL;\r
1361 }\r
1362\r
1363 res = __res_get_state();\r
1364 if (res == NULL) {\r
1365 free(buf);\r
1366 free(buf2);\r
1367 return NS_NOTFOUND;\r
1368 }\r
1369\r
1370 if (res_searchN(name, &q, res) < 0) {\r
1371 __res_put_state(res);\r
1372 free(buf);\r
1373 free(buf2);\r
1374 return NS_NOTFOUND;\r
1375 }\r
1376 ai = getanswer(buf, q.n, q.name, q.qtype, pai);\r
1377 if (ai) {\r
1378 cur->ai_next = ai;\r
1379 while (cur && cur->ai_next)\r
1380 cur = cur->ai_next;\r
1381 }\r
1382 if (q.next) {\r
1383 ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai);\r
1384 if (ai)\r
1385 cur->ai_next = ai;\r
1386 }\r
1387 free(buf);\r
1388 free(buf2);\r
1389 if (sentinel.ai_next == NULL) {\r
1390 __res_put_state(res);\r
1391 switch (h_errno) {\r
1392 case HOST_NOT_FOUND:\r
1393 return NS_NOTFOUND;\r
1394 case TRY_AGAIN:\r
1395 return NS_TRYAGAIN;\r
1396 default:\r
1397 return NS_UNAVAIL;\r
1398 }\r
1399 }\r
1400\r
1401 if (res->nsort)\r
1402 aisort(&sentinel, res);\r
1403\r
1404 __res_put_state(res);\r
1405\r
1406 *((struct addrinfo **)rv) = sentinel.ai_next;\r
1407 return NS_SUCCESS;\r
1408}\r
1409\r
1410static void\r
1411_sethtent(FILE **hostf)\r
1412{\r
1413\r
1414 if (!*hostf)\r
1415 *hostf = fopen(_PATH_HOSTS, "r" );\r
1416 else\r
1417 rewind(*hostf);\r
1418}\r
1419\r
1420static void\r
1421_endhtent(FILE **hostf)\r
1422{\r
1423\r
1424 if (*hostf) {\r
1425 (void) fclose(*hostf);\r
1426 *hostf = NULL;\r
1427 }\r
1428}\r
1429\r
1430static struct addrinfo *\r
1431_gethtent(FILE **hostf, const char *name, const struct addrinfo *pai)\r
1432{\r
1433 char *p;\r
1434 char *cp, *tname, *cname;\r
1435 struct addrinfo hints, *res0, *res;\r
1436 int error;\r
1437 const char *addr;\r
1438 static char hostbuf[8*1024];\r
1439\r
1440 _DIAGASSERT(name != NULL);\r
1441 _DIAGASSERT(pai != NULL);\r
1442\r
1443 if (!*hostf && ( NULL == (*hostf = fopen(_PATH_HOSTS, "r" ))))\r
1444 return (NULL);\r
1445 again:\r
1446 if ( NULL == (p = fgets(hostbuf, sizeof hostbuf, *hostf)))\r
1447 return (NULL);\r
1448 if (*p == '#')\r
1449 goto again;\r
1450 if ( NULL == (cp = strpbrk(p, "#\n")))\r
1451 goto again;\r
1452 *cp = '\0';\r
1453 if ( NULL == (cp = strpbrk(p, " \t")))\r
1454 goto again;\r
1455 *cp++ = '\0';\r
1456 addr = p;\r
1457 /* if this is not something we're looking for, skip it. */\r
1458 cname = NULL;\r
1459 while (cp && *cp) {\r
1460 if (*cp == ' ' || *cp == '\t') {\r
1461 cp++;\r
1462 continue;\r
1463 }\r
1464 if (!cname)\r
1465 cname = cp;\r
1466 tname = cp;\r
1467 if ((cp = strpbrk(cp, " \t")) != NULL)\r
1468 *cp++ = '\0';\r
1469 if (strcasecmp(name, tname) == 0)\r
1470 goto found;\r
1471 }\r
1472 goto again;\r
1473\r
1474found:\r
1475 hints = *pai;\r
1476 hints.ai_flags = AI_NUMERICHOST;\r
1477 error = getaddrinfo(addr, NULL, &hints, &res0);\r
1478 if (error)\r
1479 goto again;\r
1480 for (res = res0; res; res = res->ai_next) {\r
1481 /* cover it up */\r
1482 res->ai_flags = pai->ai_flags;\r
1483\r
1484 if (pai->ai_flags & AI_CANONNAME) {\r
1485 if (get_canonname(pai, res, cname) != 0) {\r
1486 freeaddrinfo(res0);\r
1487 goto again;\r
1488 }\r
1489 }\r
1490 }\r
1491 return res0;\r
1492}\r
1493\r
1494/*ARGSUSED*/\r
1495static int\r
1496_files_getaddrinfo(void *rv, void *cb_data, va_list ap)\r
1497{\r
1498 const char *name;\r
1499 const struct addrinfo *pai;\r
1500 struct addrinfo sentinel, *cur;\r
1501 struct addrinfo *p;\r
1502#ifndef _REENTRANT\r
1503 static\r
1504#endif\r
1505 FILE *hostf = NULL;\r
1506\r
1507 name = va_arg(ap, char *);\r
1508 pai = va_arg(ap, const struct addrinfo *);\r
1509\r
1510 memset(&sentinel, 0, sizeof(sentinel));\r
1511 cur = &sentinel;\r
1512\r
1513 _sethtent(&hostf);\r
1514 while ((p = _gethtent(&hostf, name, pai)) != NULL) {\r
1515 cur->ai_next = p;\r
1516 while (cur && cur->ai_next)\r
1517 cur = cur->ai_next;\r
1518 }\r
1519 _endhtent(&hostf);\r
1520\r
1521 *((struct addrinfo **)rv) = sentinel.ai_next;\r
1522 if (sentinel.ai_next == NULL)\r
1523 return NS_NOTFOUND;\r
1524 return NS_SUCCESS;\r
1525}\r
1526\r
1527#ifdef YP\r
1528/*ARGSUSED*/\r
1529static struct addrinfo *\r
1530_yphostent(char *line, const struct addrinfo *pai)\r
1531{\r
1532 struct addrinfo sentinel, *cur;\r
1533 struct addrinfo hints, *res, *res0;\r
1534 int error;\r
1535 char *p;\r
1536 const char *addr, *canonname;\r
1537 char *nextline;\r
1538 char *cp;\r
1539\r
1540 _DIAGASSERT(line != NULL);\r
1541 _DIAGASSERT(pai != NULL);\r
1542\r
1543 p = line;\r
1544 addr = canonname = NULL;\r
1545\r
1546 memset(&sentinel, 0, sizeof(sentinel));\r
1547 cur = &sentinel;\r
1548\r
1549nextline:\r
1550 /* terminate line */\r
1551 cp = strchr(p, '\n');\r
1552 if (cp) {\r
1553 *cp++ = '\0';\r
1554 nextline = cp;\r
1555 } else\r
1556 nextline = NULL;\r
1557\r
1558 cp = strpbrk(p, " \t");\r
1559 if (cp == NULL) {\r
1560 if (canonname == NULL)\r
1561 return (NULL);\r
1562 else\r
1563 goto done;\r
1564 }\r
1565 *cp++ = '\0';\r
1566\r
1567 addr = p;\r
1568\r
1569 while (cp && *cp) {\r
1570 if (*cp == ' ' || *cp == '\t') {\r
1571 cp++;\r
1572 continue;\r
1573 }\r
1574 if (!canonname)\r
1575 canonname = cp;\r
1576 if ((cp = strpbrk(cp, " \t")) != NULL)\r
1577 *cp++ = '\0';\r
1578 }\r
1579\r
1580 hints = *pai;\r
1581 hints.ai_flags = AI_NUMERICHOST;\r
1582 error = getaddrinfo(addr, NULL, &hints, &res0);\r
1583 if (error == 0) {\r
1584 for (res = res0; res; res = res->ai_next) {\r
1585 /* cover it up */\r
1586 res->ai_flags = pai->ai_flags;\r
1587\r
1588 if (pai->ai_flags & AI_CANONNAME)\r
1589 (void)get_canonname(pai, res, canonname);\r
1590 }\r
1591 } else\r
1592 res0 = NULL;\r
1593 if (res0) {\r
1594 cur->ai_next = res0;\r
1595 while (cur->ai_next)\r
1596 cur = cur->ai_next;\r
1597 }\r
1598\r
1599 if (nextline) {\r
1600 p = nextline;\r
1601 goto nextline;\r
1602 }\r
1603\r
1604done:\r
1605 return sentinel.ai_next;\r
1606}\r
1607\r
1608/*ARGSUSED*/\r
1609static int\r
1610_yp_getaddrinfo(void *rv, void *cb_data, va_list ap)\r
1611{\r
1612 struct addrinfo sentinel, *cur;\r
1613 struct addrinfo *ai = NULL;\r
1614 char *ypbuf;\r
1615 int ypbuflen, r;\r
1616 const char *name;\r
1617 const struct addrinfo *pai;\r
1618 char *ypdomain;\r
1619\r
1620 if (_yp_check(&ypdomain) == 0)\r
1621 return NS_UNAVAIL;\r
1622\r
1623 name = va_arg(ap, char *);\r
1624 pai = va_arg(ap, const struct addrinfo *);\r
1625\r
1626 memset(&sentinel, 0, sizeof(sentinel));\r
1627 cur = &sentinel;\r
1628\r
1629 /* hosts.byname is only for IPv4 (Solaris8) */\r
1630 if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) {\r
1631 r = yp_match(ypdomain, "hosts.byname", name,\r
1632 (int)strlen(name), &ypbuf, &ypbuflen);\r
1633 if (r == 0) {\r
1634 struct addrinfo ai4;\r
1635\r
1636 ai4 = *pai;\r
1637 ai4.ai_family = AF_INET;\r
1638 ai = _yphostent(ypbuf, &ai4);\r
1639 if (ai) {\r
1640 cur->ai_next = ai;\r
1641 while (cur && cur->ai_next)\r
1642 cur = cur->ai_next;\r
1643 }\r
1644 }\r
1645 free(ypbuf);\r
1646 }\r
1647\r
1648 /* ipnodes.byname can hold both IPv4/v6 */\r
1649 r = yp_match(ypdomain, "ipnodes.byname", name,\r
1650 (int)strlen(name), &ypbuf, &ypbuflen);\r
1651 if (r == 0) {\r
1652 ai = _yphostent(ypbuf, pai);\r
1653 if (ai)\r
1654 cur->ai_next = ai;\r
1655 free(ypbuf);\r
1656 }\r
1657\r
1658 if (sentinel.ai_next == NULL) {\r
1659 h_errno = HOST_NOT_FOUND;\r
1660 return NS_NOTFOUND;\r
1661 }\r
1662 *((struct addrinfo **)rv) = sentinel.ai_next;\r
1663 return NS_SUCCESS;\r
1664}\r
1665#endif\r
1666\r
1667/* resolver logic */\r
1668\r
1669/*\r
1670 * Formulate a normal query, send, and await answer.\r
1671 * Returned answer is placed in supplied buffer "answer".\r
1672 * Perform preliminary check of answer, returning success only\r
1673 * if no error is indicated and the answer count is nonzero.\r
1674 * Return the size of the response on success, -1 on error.\r
1675 * Error number is left in h_errno.\r
1676 *\r
1677 * Caller must parse answer and determine whether it answers the question.\r
1678 */\r
1679static int\r
1680res_queryN(const char *name, /* domain name */ struct res_target *target,\r
1681 res_state res)\r
1682{\r
1683 static u_char buf[MAXPACKET];\r
1684 HEADER *hp;\r
1685 int n;\r
1686 struct res_target *t;\r
1687 int rcode;\r
1688 int ancount;\r
1689\r
1690 _DIAGASSERT(name != NULL);\r
1691 /* XXX: target may be NULL??? */\r
1692\r
1693 rcode = NOERROR;\r
1694 ancount = 0;\r
1695\r
1696 for (t = target; t; t = t->next) {\r
1697 int class, type;\r
1698 u_char *answer;\r
1699 int anslen;\r
1700\r
1701 hp = (HEADER *)(void *)t->answer;\r
1702 hp->rcode = NOERROR; /* default */\r
1703\r
1704 /* make it easier... */\r
1705 class = t->qclass;\r
1706 type = t->qtype;\r
1707 answer = t->answer;\r
1708 anslen = t->anslen;\r
1709#ifdef DEBUG\r
1710 if (res->options & RES_DEBUG)\r
1711 printf(";; res_nquery(%s, %d, %d)\n", name, class, type);\r
1712#endif\r
1713\r
1714 n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL,\r
1715 buf, sizeof(buf));\r
1716#ifdef RES_USE_EDNS0\r
1717 if (n > 0 && (res->options & RES_USE_EDNS0) != 0)\r
1718 n = res_nopt(res, n, buf, sizeof(buf), anslen);\r
1719#endif\r
1720 if (n <= 0) {\r
1721#ifdef DEBUG\r
1722 if (res->options & RES_DEBUG)\r
1723 printf(";; res_nquery: mkquery failed\n");\r
1724#endif\r
1725 h_errno = NO_RECOVERY;\r
1726 return n;\r
1727 }\r
1728 n = res_nsend(res, buf, n, answer, anslen);\r
1729#if 0\r
1730 if (n < 0) {\r
1731#ifdef DEBUG\r
1732 if (res->options & RES_DEBUG)\r
1733 printf(";; res_query: send error\n");\r
1734#endif\r
1735 h_errno = TRY_AGAIN;\r
1736 return n;\r
1737 }\r
1738#endif\r
1739\r
1740 if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {\r
1741 rcode = hp->rcode; /* record most recent error */\r
1742#ifdef DEBUG\r
1743 if (res->options & RES_DEBUG)\r
1744 printf(";; rcode = %u, ancount=%u\n", hp->rcode,\r
1745 ntohs(hp->ancount));\r
1746#endif\r
1747 continue;\r
1748 }\r
1749\r
1750 ancount += ntohs(hp->ancount);\r
1751\r
1752 t->n = n;\r
1753 }\r
1754\r
1755 if (ancount == 0) {\r
1756 switch (rcode) {\r
1757 case NXDOMAIN:\r
1758 h_errno = HOST_NOT_FOUND;\r
1759 break;\r
1760 case SERVFAIL:\r
1761 h_errno = TRY_AGAIN;\r
1762 break;\r
1763 case NOERROR:\r
1764 h_errno = NO_DATA;\r
1765 break;\r
1766 case FORMERR:\r
1767 case NOTIMP:\r
1768 case REFUSED:\r
1769 default:\r
1770 h_errno = NO_RECOVERY;\r
1771 break;\r
1772 }\r
1773 return -1;\r
1774 }\r
1775 return ancount;\r
1776}\r
1777\r
1778/*\r
1779 * Formulate a normal query, send, and retrieve answer in supplied buffer.\r
1780 * Return the size of the response on success, -1 on error.\r
1781 * If enabled, implement search rules until answer or unrecoverable failure\r
1782 * is detected. Error code, if any, is left in h_errno.\r
1783 */\r
1784static int\r
1785res_searchN(const char *name, struct res_target *target, res_state res)\r
1786{\r
1787 const char *cp, * const *domain;\r
1788 HEADER *hp;\r
1789 u_int dots;\r
1790 int trailing_dot, ret, saved_herrno;\r
1791 int got_nodata = 0, got_servfail = 0, tried_as_is = 0;\r
1792\r
1793 _DIAGASSERT(name != NULL);\r
1794 _DIAGASSERT(target != NULL);\r
1795\r
1796 hp = (HEADER *)(void *)target->answer; /*XXX*/\r
1797\r
1798 errno = 0;\r
1799 h_errno = HOST_NOT_FOUND; /* default, if we never query */\r
1800 dots = 0;\r
1801 for (cp = name; *cp; cp++)\r
1802 dots += (*cp == '.');\r
1803 trailing_dot = 0;\r
1804 if (cp > name && *--cp == '.')\r
1805 trailing_dot++;\r
1806\r
1807 /*\r
1808 * if there aren't any dots, it could be a user-level alias\r
1809 */\r
1810 if (!dots && (cp = __hostalias(name)) != NULL) {\r
1811 ret = res_queryN(cp, target, res);\r
1812 return ret;\r
1813 }\r
1814\r
1815 /*\r
1816 * If there are dots in the name already, let's just give it a try\r
1817 * 'as is'. The threshold can be set with the "ndots" option.\r
1818 */\r
1819 saved_herrno = -1;\r
1820 if (dots >= res->ndots) {\r
1821 ret = res_querydomainN(name, NULL, target, res);\r
1822 if (ret > 0)\r
1823 return (ret);\r
1824 saved_herrno = h_errno;\r
1825 tried_as_is++;\r
1826 }\r
1827\r
1828 /*\r
1829 * We do at least one level of search if\r
1830 * - there is no dot and RES_DEFNAME is set, or\r
1831 * - there is at least one dot, there is no trailing dot,\r
1832 * and RES_DNSRCH is set.\r
1833 */\r
1834 if ((!dots && (res->options & RES_DEFNAMES)) ||\r
1835 (dots && !trailing_dot && (res->options & RES_DNSRCH))) {\r
1836 int done = 0;\r
1837\r
1838 for (domain = (const char * const *)res->dnsrch;\r
1839 *domain && !done;\r
1840 domain++) {\r
1841\r
1842 ret = res_querydomainN(name, *domain, target, res);\r
1843 if (ret > 0)\r
1844 return ret;\r
1845\r
1846 /*\r
1847 * If no server present, give up.\r
1848 * If name isn't found in this domain,\r
1849 * keep trying higher domains in the search list\r
1850 * (if that's enabled).\r
1851 * On a NO_DATA error, keep trying, otherwise\r
1852 * a wildcard entry of another type could keep us\r
1853 * from finding this entry higher in the domain.\r
1854 * If we get some other error (negative answer or\r
1855 * server failure), then stop searching up,\r
1856 * but try the input name below in case it's\r
1857 * fully-qualified.\r
1858 */\r
1859 if (errno == ECONNREFUSED) {\r
1860 h_errno = TRY_AGAIN;\r
1861 return -1;\r
1862 }\r
1863\r
1864 switch (h_errno) {\r
1865 case NO_DATA:\r
1866 got_nodata++;\r
1867 /* FALLTHROUGH */\r
1868 case HOST_NOT_FOUND:\r
1869 /* keep trying */\r
1870 break;\r
1871 case TRY_AGAIN:\r
1872 if (hp->rcode == SERVFAIL) {\r
1873 /* try next search element, if any */\r
1874 got_servfail++;\r
1875 break;\r
1876 }\r
1877 /* FALLTHROUGH */\r
1878 default:\r
1879 /* anything else implies that we're done */\r
1880 done++;\r
1881 }\r
1882 /*\r
1883 * if we got here for some reason other than DNSRCH,\r
1884 * we only wanted one iteration of the loop, so stop.\r
1885 */\r
1886 if (!(res->options & RES_DNSRCH))\r
1887 done++;\r
1888 }\r
1889 }\r
1890\r
1891 /*\r
1892 * if we have not already tried the name "as is", do that now.\r
1893 * note that we do this regardless of how many dots were in the\r
1894 * name or whether it ends with a dot.\r
1895 */\r
1896 if (!tried_as_is) {\r
1897 ret = res_querydomainN(name, NULL, target, res);\r
1898 if (ret > 0)\r
1899 return ret;\r
1900 }\r
1901\r
1902 /*\r
1903 * if we got here, we didn't satisfy the search.\r
1904 * if we did an initial full query, return that query's h_errno\r
1905 * (note that we wouldn't be here if that query had succeeded).\r
1906 * else if we ever got a nodata, send that back as the reason.\r
1907 * else send back meaningless h_errno, that being the one from\r
1908 * the last DNSRCH we did.\r
1909 */\r
1910 if (saved_herrno != -1)\r
1911 h_errno = saved_herrno;\r
1912 else if (got_nodata)\r
1913 h_errno = NO_DATA;\r
1914 else if (got_servfail)\r
1915 h_errno = TRY_AGAIN;\r
1916 return -1;\r
1917}\r
1918\r
1919/*\r
1920 * Perform a call on res_query on the concatenation of name and domain,\r
1921 * removing a trailing dot from name if domain is NULL.\r
1922 */\r
1923static int\r
1924res_querydomainN(const char *name, const char *domain,\r
1925 struct res_target *target, res_state res)\r
1926{\r
1927 char nbuf[MAXDNAME];\r
1928 const char *longname = nbuf;\r
1929 size_t n, d;\r
1930\r
1931 _DIAGASSERT(name != NULL);\r
1932 /* XXX: target may be NULL??? */\r
1933\r
1934#ifdef DEBUG\r
1935 if (res->options & RES_DEBUG)\r
1936 printf(";; res_querydomain(%s, %s)\n",\r
1937 name, domain?domain:"<Nil>");\r
1938#endif\r
1939 if (domain == NULL) {\r
1940 /*\r
1941 * Check for trailing '.';\r
1942 * copy without '.' if present.\r
1943 */\r
1944 n = strlen(name);\r
1945 if (n + 1 > sizeof(nbuf)) {\r
1946 h_errno = NO_RECOVERY;\r
1947 return -1;\r
1948 }\r
1949 if (n > 0 && name[--n] == '.') {\r
1950 strncpy(nbuf, name, n);\r
1951 nbuf[n] = '\0';\r
1952 } else\r
1953 longname = name;\r
1954 } else {\r
1955 n = strlen(name);\r
1956 d = strlen(domain);\r
1957 if (n + 1 + d + 1 > sizeof(nbuf)) {\r
1958 h_errno = NO_RECOVERY;\r
1959 return -1;\r
1960 }\r
1961 snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain);\r
1962 }\r
1963 return res_queryN(longname, target, res);\r
1964}\r