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