1 /*
2 * lftp - file transfer program
3 *
4 * Copyright (c) 1996-2020 by Alexander V. Lukyanov (lav@yars.free.net)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <config.h>
21
22 #include <stddef.h>
23 #include "Resolver.h"
24 #include "SignalHook.h"
25 #include <errno.h>
26 #include <unistd.h>
27 #include "trio.h"
28 #include <time.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netdb.h>
32 #include <ctype.h>
33 #include <fcntl.h>
34
35 #include <netinet/in.h>
36 #ifdef HAVE_ARPA_NAMESER_H
37 # define class _class // workaround for FreeBSD 3.2.
38 # include <arpa/nameser.h>
39 # undef class
40 #endif
41 #ifdef HAVE_RESOLV_H
42 # include <resolv.h>
43 #endif
44
45 #if LIBIDN2
46 # include <idn2.h>
47 #endif
48
49 #ifdef DNSSEC_LOCAL_VALIDATION
50 # include "validator/validator.h"
51 #endif
52
53 #include "xstring.h"
54 #include "ResMgr.h"
55 #include "log.h"
56 #include "plural.h"
57
58 #ifndef C_IN
59 # define C_IN 1
60 #endif
61 #ifndef T_SRV
62 # define T_SRV 33
63 #endif
64
65 #if !HAVE_DECL_HSTRERROR
66 extern "C" { const char *hstrerror(int); }
67 #endif
68
69 #ifdef HAVE_H_ERRNO
70 #if !HAVE_DECL_H_ERRNO
71 CDECL int h_errno;
72 #endif
73 #endif
74
75 #if HAVE_RES_SEARCH && !HAVE_DECL_RES_SEARCH
76 CDECL int res_search(const char*,int,int,unsigned char*,int);
77 #endif
78
79 #if INET6
80 # define DEFAULT_ORDER "inet inet6"
81 #else
82 # define DEFAULT_ORDER "inet"
83 #endif
84
85
86 struct address_family
87 {
88 int number;
89 const char *name;
90 };
91 static const address_family af_list[]=
92 {
93 { AF_INET, "inet" },
94 #if INET6
95 { AF_INET6, "inet6" },
96 #endif
97 { -1, 0 }
98 };
99
100 ResolverCache *Resolver::cache;
101
102
Resolver(const char * h,const char * p,const char * defp,const char * ser,const char * pr)103 Resolver::Resolver(const char *h,const char *p,const char *defp,
104 const char *ser,const char *pr)
105 : hostname(h), portname(p), service(ser), proto(pr), defport(defp)
106 {
107 port_number=0;
108
109 pipe_to_child[0]=pipe_to_child[1]=-1;
110 done=false;
111 timeout_timer.SetResource("dns:fatal-timeout",hostname);
112 Reconfig();
113 use_fork=ResMgr::QueryBool("dns:use-fork",0);
114
115 error=0;
116
117 no_cache=false;
118 }
119
~Resolver()120 Resolver::~Resolver()
121 {
122 if(pipe_to_child[0]!=-1)
123 close(pipe_to_child[0]);
124 if(pipe_to_child[1]!=-1)
125 close(pipe_to_child[1]);
126
127 if(w)
128 {
129 w->Kill(SIGKILL);
130 w.borrow()->Auto();
131 }
132 }
133
Do()134 int Resolver::Do()
135 {
136 if(done)
137 return STALL;
138
139 int m=STALL;
140
141 if(!no_cache && cache)
142 {
143 const sockaddr_u *a;
144 int n;
145 cache->Find(hostname,portname,defport,service,proto,&a,&n);
146 if(a && n>0)
147 {
148 LogNote(10,"dns cache hit");
149 addr.nset(a,n);
150 done=true;
151 return MOVED;
152 }
153 no_cache=true;
154 }
155
156 if(use_fork)
157 {
158 if(pipe_to_child[0]==-1)
159 {
160 int res=pipe(pipe_to_child);
161 if(res==-1)
162 {
163 if(NonFatalError(errno))
164 return m;
165 MakeErrMsg("pipe()");
166 return MOVED;
167 }
168 fcntl(pipe_to_child[0],F_SETFL,O_NONBLOCK);
169 fcntl(pipe_to_child[0],F_SETFD,FD_CLOEXEC);
170 fcntl(pipe_to_child[1],F_SETFD,FD_CLOEXEC);
171 m=MOVED;
172 LogNote(4,_("Resolving host address..."));
173 }
174
175 if(!w && !buf)
176 {
177 pid_t proc=fork();
178 if(proc==-1)
179 {
180 TimeoutS(1);
181 return m;
182 }
183 if(proc==0)
184 { // child
185 SignalHook::Ignore(SIGINT);
186 SignalHook::Ignore(SIGTSTP);
187 SignalHook::Ignore(SIGQUIT);
188 SignalHook::Ignore(SIGHUP);
189 close(0); // no input will be needed.
190 close(pipe_to_child[0]);
191 pipe_to_child[0]=-1;
192 buf=new IOBufferFDStream(new FDStream(pipe_to_child[1],"<pipe-out>"),IOBuffer::PUT);
193 DoGethostbyname();
194 buf->PutEOF();
195 while(buf->Size()>0 && !buf->Error() && !buf->Broken())
196 buf->Roll(); // should flush quickly.
197 _exit(0);
198 }
199 // parent
200 close(pipe_to_child[1]);
201 pipe_to_child[1]=-1;
202
203 w=new ProcWait(proc);
204 m=MOVED;
205 }
206 if(!buf)
207 {
208 buf=new IOBufferFDStream(new FDStream(pipe_to_child[0],"<pipe-in>"),IOBuffer::GET);
209 // Roll(buf);
210 m=MOVED;
211 }
212 }
213 else /* !use_fork */
214 {
215 if(!buf)
216 {
217 LogNote(4,_("Resolving host address..."));
218 buf=new IOBuffer(IOBuffer::GET);
219 DoGethostbyname();
220 if(Deleted())
221 return MOVED;
222 }
223 }
224
225 if(buf->Error())
226 {
227 err_msg.set(buf->ErrorText());
228 done=true;
229 return MOVED;
230 }
231
232 if(!buf->Eof()) // wait for all data to arrive (not too much)
233 {
234 if(timeout_timer.Stopped())
235 {
236 err_msg.set(_("host name resolve timeout"));
237 done=true;
238 return MOVED;
239 }
240 return m;
241 }
242
243 const char *s;
244 char c;
245 int n;
246
247 buf->Get(&s,&n);
248 if(n<1)
249 goto proto_error;
250 c=*s;
251 buf->Skip(1);
252 buf->Get(&s,&n);
253 if(c=='E' || c=='P') // error
254 {
255 const char *tport=portname?portname.get():defport.get();
256 err_msg.vset(c=='E'?hostname.get():tport,": ",s,NULL);
257 done=true;
258 return MOVED;
259 }
260 if((unsigned)n<addr.get_element_size())
261 {
262 proto_error:
263 if(use_fork)
264 {
265 // e.g. under gdb child fails.
266 LogError(4,"child failed, retrying with dns:use-fork=no");
267 use_fork=false;
268 buf=0;
269 return MOVED;
270 }
271 err_msg.set("BUG: internal class Resolver error");
272 done=true;
273 return MOVED;
274 }
275 addr.nset((const sockaddr_u*)s,n/addr.get_element_size());
276 done=true;
277 if(!cache)
278 cache=new ResolverCache;
279 cache->Add(hostname,portname,defport,service,proto,addr.get(),addr.count());
280
281 xstring report;
282 report.set(xstring::format(plural("%d address$|es$ found",addr.count()),addr.count()));
283 if(addr.count()>0) {
284 report.append(": ");
285 for(int i=0; i<addr.count(); i++) {
286 report.append(addr[i].address());
287 if(i<addr.count()-1)
288 report.append(", ");
289 }
290 }
291 LogNote(4,"%s",report.get());
292
293 return MOVED;
294 }
295
MakeErrMsg(const char * f)296 void Resolver::MakeErrMsg(const char *f)
297 {
298 const char *e=strerror(errno);
299 err_msg.vset(f,": ",e,NULL);
300 done=true;
301 }
302
AddAddress(int family,const char * address,int len,unsigned int scope)303 void Resolver::AddAddress(int family,const char *address,int len, unsigned int scope)
304 {
305 sockaddr_u add;
306 memset(&add,0,sizeof(add));
307
308 add.sa.sa_family=family;
309 switch(family)
310 {
311 case AF_INET:
312 if(sizeof(add.in.sin_addr) != len)
313 return;
314 memcpy(&add.in.sin_addr,address,len);
315 add.in.sin_port=port_number;
316 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
317 add.sa.sa_len=sizeof(add.in);
318 #endif
319 break;
320
321 #if INET6
322 case AF_INET6:
323 if(sizeof(add.in6.sin6_addr) != len)
324 return;
325 memcpy(&add.in6.sin6_addr,address,len);
326 if(IN6_IS_ADDR_LINKLOCAL(&add.in6.sin6_addr) && scope==0) {
327 error=_("Link-local IPv6 address should have a scope");
328 return;
329 }
330 add.in6.sin6_port=port_number;
331 # ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
332 add.in6.sin6_scope_id=scope;
333 # endif
334 # ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
335 add.sa.sa_len=sizeof(add.in6);
336 # endif
337 break;
338 #endif
339
340 default:
341 return;
342 }
343 if(addr.count()>0 && addr.last()==add)
344 return;
345 addr.append(add);
346 }
347
FindAddressFamily(const char * name)348 int Resolver::FindAddressFamily(const char *name)
349 {
350 for(const address_family *f=af_list; f->name; f++)
351 {
352 if(!strcasecmp(name,f->name))
353 return f->number;
354 }
355 return -1;
356 }
357
IsAddressFamilySupporded(int af)358 bool Resolver::IsAddressFamilySupporded(int af)
359 {
360 #if INET6
361 // check if ipv6 is really supported
362 if(af==AF_INET6 && (!FindGlobalIPv6Address() || !CanCreateIpv6Socket()))
363 {
364 LogNote(4, "IPv6 is not supported or configured");
365 return false;
366 }
367 #endif // INET6
368 return true;
369 }
370
ParseOrder(const char * s,int * o)371 void Resolver::ParseOrder(const char *s,int *o)
372 {
373 const char * const delim="\t ";
374 char *s1=alloca_strdup(s);
375 int idx=0;
376
377 for(s1=strtok(s1,delim); s1; s1=strtok(0,delim))
378 {
379 int af=FindAddressFamily(s1);
380 if(af!=-1 && idx<15 && IsAddressFamilySupporded(af))
381 {
382 if(o) o[idx]=af;
383 idx++;
384 }
385 }
386 if(o) o[idx]=-1;
387 }
388
389 #if defined(HAVE_DN_EXPAND) && !HAVE_DECL_DN_EXPAND
390 CDECL int dn_expand(const unsigned char *msg,const unsigned char *eomorig,const unsigned char *comp_dn,char *exp_dn,int length);
391 CDECL int dn_skipname(const unsigned char *msg,const unsigned char *eomorig);
392 #endif
393
394 #ifdef HAVE_RES_SEARCH
395 static
extract_domain(const unsigned char * answer,const unsigned char * scan,int len,char * store,int store_len)396 int extract_domain(const unsigned char *answer,const unsigned char *scan,int len,
397 char *store,int store_len)
398 {
399 #ifdef HAVE_DN_EXPAND // newer resolver versions have dn_expand and dn_skipname
400 if(store)
401 dn_expand(answer,scan+len,scan,store,store_len);
402 return dn_skipname(scan,scan+len);
403 #else // ...older don't.
404 int count=1; // reserve space for \0
405 int refs=0;
406 int consumed=0;
407 const unsigned char *start=scan;
408 for(;;)
409 {
410 if(len<=0)
411 break;
412 int label_len=*scan;
413 scan++;
414 len--;
415
416 if((label_len & 0xC0) == 0xC0) // compression
417 {
418 if(len<=0)
419 break;
420 int offset=((label_len&0x3F)<<8) + *scan;
421 scan++;
422 len--;
423
424 if(refs==0)
425 consumed=scan-start;
426
427 if(answer+offset>=scan+len)
428 break; // error
429
430 len=scan+len-answer+offset;
431 scan=answer+offset;
432 if(++refs > 256)
433 break; // too many hops.
434 continue;
435 }
436
437 if(label_len==0)
438 break;
439
440 while(label_len>0)
441 {
442 if(len<=0)
443 break;
444 if(store && count<store_len)
445 *store++=*scan;
446 count++;
447 scan++;
448 len--;
449 label_len--;
450 }
451 if(store && count<store_len)
452 *store++='.';
453 count++;
454 }
455 if(store)
456 *store=0;
457 if(refs==0)
458 consumed=scan-start;
459 return consumed;
460 #endif // DN_EXPAND
461 }
462
463 #ifndef NS_MAXDNAME
464 # define NS_MAXDNAME 1025
465 #endif
466 #ifndef NS_HFIXEDSZ
467 # define NS_HFIXEDSZ 12
468 #endif
469
470 struct SRV
471 {
472 char domain[NS_MAXDNAME];
473 int port;
474 int priority;
475 int weight;
476 int order;
477 };
478
479 static
SRV_compare(const SRV * sa,const SRV * sb)480 int SRV_compare(const SRV *sa,const SRV *sb)
481 {
482 if(sa->priority < sb->priority)
483 return -1;
484 if(sa->priority > sb->priority)
485 return 1;
486 if(sa->order < sb->order)
487 return -1;
488 if(sa->order > sb->order)
489 return 1;
490 if(sa->weight > sb->weight)
491 return -1;
492 if(sa->weight < sb->weight)
493 return 1;
494 return 0;
495 }
496 #endif // RES_SEARCH
497
LookupSRV_RR()498 void Resolver::LookupSRV_RR()
499 {
500 if(!ResMgr::QueryBool("dns:SRV-query",hostname))
501 return;
502 #ifdef HAVE_RES_SEARCH
503 const char *tproto=proto?proto.get():"tcp";
504 time_t try_time;
505 unsigned char answer[0x1000];
506 const char *srv_name=xstring::format("_%s._%s.%s",service.get(),tproto,hostname.get());
507 srv_name=alloca_strdup(srv_name);
508
509 int retries=0;
510 int max_retries=ResMgr::Query("dns:max-retries",hostname);
511 int len;
512 for(;;)
513 {
514 if(!use_fork)
515 {
516 Schedule();
517 if(Deleted())
518 return;
519 }
520 time(&try_time);
521
522 #ifndef DNSSEC_LOCAL_VALIDATION
523 len=res_search(srv_name, C_IN, T_SRV, answer, sizeof(answer));
524 if(len>=0)
525 break;
526 #else
527 val_status_t val_status;
528 bool require_trust = ResMgr::QueryBool("dns:strict-dnssec",hostname);
529 len=val_res_search(NULL, srv_name, C_IN, T_SRV, answer, sizeof(answer), &val_status);
530 if(len>=0) {
531 if(require_trust && !val_istrusted(val_status))
532 return;
533 else
534 break;
535 }
536 #endif
537 #ifdef HAVE_H_ERRNO
538 if(h_errno!=TRY_AGAIN)
539 return;
540 if(++retries>=max_retries && max_retries)
541 return;
542 time_t t=time(0);
543 if(t-try_time<5)
544 sleep(5-(t-try_time));
545 #else // no h_errno
546 return;
547 #endif
548 }
549
550 if(len>(int)sizeof(answer))
551 len=sizeof(answer);
552
553 if(len<NS_HFIXEDSZ)
554 return;
555
556 int question_count=(answer[4]<<8)+answer[5];
557 int answer_count =(answer[6]<<8)+answer[7];
558
559 // skip header
560 unsigned char *scan=answer+NS_HFIXEDSZ;
561 len-=NS_HFIXEDSZ;
562
563 // skip questions section
564 for( ; question_count>0; question_count--)
565 {
566 int dom_len=extract_domain(answer,scan,len,0,0);
567 if(dom_len<0)
568 return;
569 scan+=dom_len;
570 len-=dom_len;
571 if(len<4)
572 return;
573 scan+=4;
574 len-=4;
575 }
576
577 xarray<SRV> SRVs;
578
579 // now process answers section
580 for( ; answer_count>0; answer_count--)
581 {
582 int dom_len=extract_domain(answer,scan,len,0,0);
583 if(dom_len<0)
584 return;
585 scan+=dom_len;
586 len-=dom_len;
587 if(len<8)
588 return;
589 scan+=8;
590 len-=8; // skip type,class,ttl
591
592 if(len<2)
593 return;
594
595 int data_len=(scan[0]<<8)+scan[1];
596 scan+=2;
597 len-=2;
598
599 if(len<data_len)
600 return;
601
602 if(data_len<6)
603 return;
604
605 struct SRV t;
606 t.priority=(scan[0]<<8)+scan[1];
607 t.weight =(scan[2]<<8)+scan[3];
608 t.port =(scan[4]<<8)+scan[5];
609 t.order=0;
610
611 scan+=6;
612 len-=6;
613
614 dom_len=extract_domain(answer,scan,len,t.domain,sizeof(t.domain));
615 if(dom_len<0)
616 return;
617
618 scan+=dom_len;
619 len-=dom_len;
620
621 // add unless the service is decidedly not available at this domain.
622 if(strcmp(t.domain,"."))
623 SRVs.append(t);
624 }
625
626 // now sort and randomize the list.
627 SRVs.qsort(SRV_compare);
628
629 srand(time(0));
630
631 int SRVscan;
632 int base=0;
633 int curr_priority=-1;
634 int weight_sum=0;
635 for(SRVscan=0; ; SRVscan++)
636 {
637 if(SRVscan==SRVs.count() || SRVs[SRVscan].priority!=curr_priority)
638 {
639 if(base)
640 {
641 int o=1;
642 int s;
643 while(weight_sum>0)
644 {
645 int r=int(rand()/(RAND_MAX+1.0)*weight_sum);
646 if(r>=weight_sum)
647 r=weight_sum-1;
648 int w=0;
649 for(s=base; s<SRVscan; s++)
650 {
651 if(SRVs[s].order!=0)
652 continue;
653 w+=SRVs[s].weight;
654 if(r<w)
655 {
656 SRVs[s].order=o;
657 o++;
658 weight_sum-=SRVs[s].weight;
659 break;
660 }
661 }
662 }
663 }
664 if(SRVscan==SRVs.count())
665 break;
666 base=SRVscan;
667 curr_priority=SRVs[SRVscan].priority;
668 weight_sum=0;
669 }
670 weight_sum+=SRVs[SRVscan].weight;
671 }
672
673 SRVs.qsort(SRV_compare);
674
675 int oldport=port_number;
676 for(SRVscan=0; SRVscan<SRVs.count(); SRVscan++)
677 {
678 port_number=htons(SRVs[SRVscan].port);
679 LookupOne(SRVs[SRVscan].domain);
680 }
681 port_number=oldport;
682
683 #endif // HAVE_RES_SEARCH
684 }
685
LookupOne(const char * name)686 void Resolver::LookupOne(const char *name)
687 {
688 time_t try_time;
689 int af_index=0;
690 int af_order[16];
691
692 const char *order=ResMgr::Query("dns:order",name);
693
694 const char *proto_delim=strchr(name,',');
695 if(proto_delim)
696 {
697 char *o=string_alloca(proto_delim-name+1);
698 memcpy(o,name,proto_delim-name);
699 o[proto_delim-name]=0;
700 // check if the protocol name is valid.
701 if(FindAddressFamily(o)!=-1)
702 order=o;
703 name=proto_delim+1;
704 }
705
706 #if LIBIDN2
707 xstring_c ascii_name;
708 int rc=idn2_lookup_ul(name,ascii_name.buf_ptr(),0);
709 if(rc!=IDN2_OK) {
710 error=idn2_strerror(rc);
711 return;
712 }
713 name=ascii_name;
714 #endif//LIBIDN2
715
716 ParseOrder(order,af_order);
717
718 int retries=0;
719 int max_retries=ResMgr::Query("dns:max-retries",name);
720 for(;;)
721 {
722 if(!use_fork)
723 {
724 Schedule();
725 if(Deleted())
726 return;
727 }
728
729 time(&try_time);
730
731 // Prefer getaddrinfo over gethostbyname2 and getipnodebyname, as
732 // approach with multiple lookups works badly when host name is in hosts file
733 // and no dns servers are reachable.
734 #if defined(HAVE_GETADDRINFO) && INET6
735 /* && !defined(HAVE_GETHOSTBYNAME2) \
736 && !defined(HAVE_GETIPNODEBYNAME) */
737
738 // getaddrinfo support by Brandon Hume
739 struct addrinfo *ainfo=0,
740 *a_res,
741 a_hint;
742 int ainfo_res;
743 struct sockaddr *sockname;
744 struct sockaddr_in *inet_addr;
745 struct sockaddr_in6 *inet6_addr;
746 const char *addr_data;
747 int addr_len;
748 unsigned int addr_scope;
749
750 memset(&a_hint, 0, sizeof(a_hint));
751 a_hint.ai_flags = AI_PASSIVE;
752 a_hint.ai_family = PF_UNSPEC;
753
754 #ifndef DNSSEC_LOCAL_VALIDATION
755 ainfo_res = getaddrinfo(name, NULL, &a_hint, &ainfo);
756 #else
757 val_status_t val_status;
758 bool require_trust=ResMgr::QueryBool("dns:strict-dnssec",name);
759 ainfo_res = val_getaddrinfo(NULL, name, NULL, &a_hint, &ainfo,
760 &val_status);
761 if(VAL_GETADDRINFO_HAS_STATUS(ainfo_res) && !val_istrusted(val_status))
762 {
763 if(require_trust) {
764 // untrusted answer
765 error = _("DNS resolution not trusted.");
766 break;
767 } else {
768 fprintf(stderr,"\nWARNING: DNS lookup failed validation: %s\n",
769 p_val_status(val_status));
770 fflush(stderr);
771 }
772 }
773 #endif
774
775 if(ainfo_res == 0)
776 {
777 // by lav: add addresses in specified order.
778 for(int af=af_order[af_index]; af!=-1; af=af_order[++af_index])
779 {
780 for(a_res = ainfo; a_res != NULL; a_res = a_res->ai_next)
781 {
782 if(a_res->ai_family!=af)
783 continue;
784
785 sockname = a_res->ai_addr;
786 addr_scope = 0;
787
788 switch(a_res->ai_family)
789 {
790 case AF_INET:
791 inet_addr = (sockaddr_in *)sockname;
792 addr_data = (const char *)&(inet_addr->sin_addr.s_addr);
793 addr_len = sizeof(inet_addr->sin_addr.s_addr);
794 break;
795 case AF_INET6:
796 inet6_addr = (sockaddr_in6 *)sockname;
797 addr_data = (const char *)&(inet6_addr->sin6_addr.s6_addr);
798 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
799 addr_scope = inet6_addr->sin6_scope_id;
800 #endif
801 addr_len = sizeof(inet6_addr->sin6_addr.s6_addr);
802 break;
803 default:
804 continue;
805 }
806 AddAddress(a_res->ai_family, addr_data, addr_len, addr_scope);
807 }
808 }
809
810 freeaddrinfo(ainfo);
811 break;
812 }
813
814 if(ainfo_res != EAI_AGAIN
815 || (++retries>=max_retries && max_retries))
816 {
817 error = gai_strerror(ainfo_res);
818 break;
819 }
820
821 #else // !HAVE_GETADDRINFO
822
823 int af=af_order[af_index];
824 if(af==-1)
825 break;
826
827 struct hostent *ha;
828 # if defined(HAVE_GETIPNODEBYNAME)
829 # ifndef HAVE_H_ERRNO
830 # define HAVE_H_ERRNO 1
831 # endif
832 # undef h_errno // it could be a macro, but we want it to be local variable.
833 int h_errno=0;
834 ha=getipnodebyname(name,af,0,&h_errno);
835 # elif defined(HAVE_GETHOSTBYNAME2)
836 ha=gethostbyname2(name,af);
837 # else
838 if(af==AF_INET)
839 ha=gethostbyname(name);
840 else
841 {
842 retries=0;
843 af_index++;
844 continue;
845 }
846 # endif
847
848 if(ha)
849 {
850 const char * const *a;
851 for(a=ha->h_addr_list; *a; a++)
852 AddAddress(ha->h_addrtype, *a, ha->h_length, 0);
853 retries=0;
854 af_index++;
855 # if defined(HAVE_GETIPNODEBYNAME)
856 freehostent(ha);
857 # endif
858 continue;
859 }
860
861 # ifdef HAVE_H_ERRNO
862 if(h_errno!=TRY_AGAIN
863 || (++retries>=max_retries && max_retries))
864 # endif
865 {
866 if(error==0)
867 {
868 # ifdef HAVE_H_ERRNO
869 error=hstrerror(h_errno);
870 # else
871 error=_("Host name lookup failure");
872 # endif
873 }
874 retries=0;
875 af_index++;
876 continue; // try other address families
877 }
878 #endif /* HAVE_GETADDRINFO */
879
880 time_t t;
881 if((t=time(0))-try_time<5)
882 sleep(5-(t-try_time));
883 }
884 }
885
DoGethostbyname()886 void Resolver::DoGethostbyname()
887 {
888 if(port_number==0)
889 {
890 const char *tproto=proto?proto.get():"tcp";
891 const char *tport=portname?portname.get():defport.get();
892
893 if(isdigit((unsigned char)tport[0]))
894 port_number=htons(atoi(tport));
895 else
896 {
897 struct servent *se=getservbyname(tport,tproto);
898 if(se)
899 port_number=se->s_port;
900 else
901 {
902 buf->Put("P");
903 buf->Format(_("no such %s service"),tproto);
904 return;
905 }
906 }
907 }
908
909 if(service && !portname && !isdigit((unsigned char)hostname[0]))
910 LookupSRV_RR();
911
912 if(!use_fork && Deleted())
913 return;
914
915 const char *h=ResMgr::Query("dns:name",hostname);
916 if(!h || !*h)
917 h=hostname;
918 char *hs=alloca_strdup(h);
919 char *tok;
920 for(hs=strtok_r(hs,",",&tok); hs; hs=strtok_r(NULL,",",&tok))
921 LookupOne(hs);
922
923 if(!use_fork && Deleted())
924 return;
925
926 if(addr.count()==0)
927 {
928 buf->Put("E");
929 if(error==0)
930 error=_("No address found");
931 buf->Put(error);
932 return;
933 }
934 buf->Put("O");
935 buf->Put((const char*)addr.get(),addr.count()*addr.get_element_size());
936 addr.unset();
937 }
938
Reconfig(const char * name)939 void Resolver::Reconfig(const char *name)
940 {
941 if(!name || strncmp(name,"dns:",4))
942 return;
943 }
944
945
ResolverCache()946 ResolverCache::ResolverCache()
947 : Cache(ResMgr::FindRes("dns:cache-size"),ResMgr::FindRes("dns:cache-enable"))
948 {
949 }
Reconfig(const char * r)950 void ResolverCache::Reconfig(const char *r)
951 {
952 if(!xstrcmp(r,"dns:SRV-query")
953 || !xstrcmp(r,"dns:order"))
954 Flush();
955 }
Find(const char * h,const char * p,const char * defp,const char * ser,const char * pr)956 ResolverCacheEntry *ResolverCache::Find(const char *h,const char *p,const char *defp,const char *ser,const char *pr)
957 {
958 for(ResolverCacheEntry *c=IterateFirst(); c; c=IterateNext())
959 {
960 if(c->Matches(h,p,defp,ser,pr))
961 return c;
962 }
963 return 0;
964 }
Add(const char * h,const char * p,const char * defp,const char * ser,const char * pr,const sockaddr_u * a,int n)965 void ResolverCache::Add(const char *h,const char *p,const char *defp,
966 const char *ser,const char *pr,const sockaddr_u *a,int n)
967 {
968 Trim();
969 ResolverCacheEntry *c=Find(h,p,defp,ser,pr);
970 if(c)
971 c->SetData(a,n);
972 else
973 {
974 if(!IsEnabled(h))
975 return;
976 AddCacheEntry(new ResolverCacheEntry(h,p,defp,ser,pr,a,n));
977 }
978 }
Matches(const char * h,const char * p,const char * defp,const char * ser,const char * pr)979 bool ResolverCacheEntryLoc::Matches(const char *h,const char *p,
980 const char *defp,const char *ser,const char *pr)
981 {
982 return (!xstrcasecmp(hostname,h)
983 && !xstrcmp(portname,p)
984 && !xstrcmp(defport,defp)
985 && !xstrcmp(service,ser)
986 && !xstrcmp(proto,pr));
987 }
Find(const char * h,const char * p,const char * defp,const char * ser,const char * pr,const sockaddr_u ** a,int * n)988 void ResolverCache::Find(const char *h,const char *p,const char *defp,
989 const char *ser,const char *pr,const sockaddr_u **a,int *n)
990 {
991 *a=0;
992 *n=0;
993
994 // if cache is disabled for this host, return nothing.
995 if(!IsEnabled(h))
996 return;
997
998 ResolverCacheEntry *c=Find(h,p,defp,ser,pr);
999 if(c)
1000 {
1001 if(c->Stopped())
1002 {
1003 Trim();
1004 return;
1005 }
1006 c->GetData(a,n);
1007 }
1008 }
1009