4
* Copyright (c) 1996-2001 by Alexander V. Lukyanov (lav@yars.free.net)
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 2 of the License, or
9
* (at your option) any later version.
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.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
/* $Id: Resolver.cc,v 1.61 2004/05/28 07:57:05 lav Exp $ */
26
#include "SignalHook.h"
31
#include <sys/types.h>
32
#include <sys/socket.h>
36
#include <netinet/in.h>
37
#ifdef HAVE_ARPA_NAMESER_H
38
# define class _class // workaround for FreeBSD 3.2.
39
# include <arpa/nameser.h>
59
#if !HAVE_DECL_HSTRERROR
60
extern "C" { const char *hstrerror(int); }
64
#if !HAVE_DECL_H_ERRNO
69
#if HAVE_RES_SEARCH && !HAVE_DECL_RES_SEARCH
70
CDECL int res_search(const char*,int,int,unsigned char*,int);
74
# define DEFAULT_ORDER "inet inet6"
76
# define DEFAULT_ORDER "inet"
85
static const address_family af_list[]=
89
{ AF_INET6, "inet6" },
94
ResolverCache *Resolver::cache;
96
Resolver::Resolver(const char *h,const char *p,const char *defp,
97
const char *ser,const char *pr)
103
service=xstrdup(ser);
105
defport=xstrdup(defp);
107
pipe_to_child[0]=pipe_to_child[1]=-1;
117
use_fork=ResMgr::QueryBool("dns:use-fork",0);
124
Resolver::~Resolver()
126
if(pipe_to_child[0]!=-1)
127
close(pipe_to_child[0]);
128
if(pipe_to_child[1]!=-1)
129
close(pipe_to_child[1]);
154
if(!no_cache && cache)
158
cache->Find(hostname,portname,defport,service,proto,&a,&n);
161
Log::global->Write(10,"dns cache hit\n");
163
addr=(sockaddr_u*)xmalloc(n*sizeof(*addr));
164
memcpy(addr,a,n*sizeof(*addr));
173
if(pipe_to_child[0]==-1)
175
int res=pipe(pipe_to_child);
178
if(NonFatalError(errno))
180
MakeErrMsg("pipe()");
183
fcntl(pipe_to_child[0],F_SETFL,O_NONBLOCK);
184
fcntl(pipe_to_child[0],F_SETFD,FD_CLOEXEC);
185
fcntl(pipe_to_child[1],F_SETFD,FD_CLOEXEC);
187
Log::global->Format(4,"---- %s\n",_("Resolving host address..."));
200
SignalHook::Ignore(SIGINT);
201
SignalHook::Ignore(SIGTSTP);
202
SignalHook::Ignore(SIGQUIT);
203
SignalHook::Ignore(SIGHUP);
204
close(0); // no input will be needed.
205
close(pipe_to_child[0]);
207
buf=new IOBufferFDStream(new FDStream(pipe_to_child[1],"<pipe-out>"),IOBuffer::PUT);
212
close(pipe_to_child[1]);
215
w=new ProcWait(proc);
220
buf=new IOBufferFDStream(new FDStream(pipe_to_child[0],"<pipe-in>"),IOBuffer::GET);
229
Log::global->Format(4,"---- %s\n",_("Resolving host address..."));
230
buf=new IOBuffer(IOBuffer::GET);
239
err_msg=xstrdup(buf->ErrorText());
244
if(!buf->Eof()) // wait for all data to arrive (not too much)
248
if(now >= start_time+timeout)
250
err_msg=xstrdup(_("host name resolve timeout"));
254
TimeoutS(timeout-(time_t(now)-start_time));
269
if(c=='E' || c=='P') // error
271
const char *tport=portname?portname:defport;
272
err_msg=(char*)xmalloc(strlen(hostname)+strlen(tport)+n+3);
273
sprintf(err_msg,"%s: ",(c=='E'?hostname:tport));
274
char *e=err_msg+strlen(err_msg);
280
if((unsigned)n<sizeof(sockaddr_u))
285
// e.g. under gdb child fails.
286
Log::global->Format(4,"**** %s\n","child failed, retrying with dns:use-fork=no");
292
err_msg=xstrdup("BUG: internal class Resolver error");
296
addr_num=n/sizeof(*addr);
297
addr=(sockaddr_u*)xmalloc(n);
301
cache=new ResolverCache;
302
cache->Add(hostname,portname,defport,service,proto,addr,addr_num);
303
Log::global->Format(4,plural("---- %d address$|es$ found\n",addr_num),addr_num);
307
void Resolver::MakeErrMsg(const char *f)
309
const char *e=strerror(errno);
310
err_msg=(char*)xmalloc(strlen(e)+strlen(f)+3);
311
sprintf(err_msg,"%s: %s",f,e);
315
void Resolver::AddAddress(int family,const char *address,int len)
317
addr=(sockaddr_u*)xrealloc(addr,(addr_num+1)*sizeof(*addr));
318
sockaddr_u *add=addr+addr_num;
321
memset(add,0,sizeof(*add));
323
add->sa.sa_family=family;
327
if(sizeof(add->in.sin_addr) != len)
332
memcpy(&add->in.sin_addr,address,len);
333
add->in.sin_port=port_number;
334
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
335
add->sa.sa_len=sizeof(add->in);
341
if(sizeof(add->in6.sin6_addr) != len)
346
memcpy(&add->in6.sin6_addr,address,len);
347
add->in6.sin6_port=port_number;
348
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
349
add->sa.sa_len=sizeof(add->in6);
360
int Resolver::FindAddressFamily(const char *name)
362
for(const address_family *f=af_list; f->name; f++)
364
if(!strcasecmp(name,f->name))
370
void Resolver::ParseOrder(const char *s,int *o)
372
const char * const delim="\t ";
373
char *s1=alloca_strdup(s);
376
for(s1=strtok(s1,delim); s1; s1=strtok(0,delim))
378
int af=FindAddressFamily(s1);
388
#ifdef HAVE_RES_SEARCH
390
int extract_domain(const unsigned char *answer,const unsigned char *scan,int len,
391
char *store,int store_len)
393
int count=1; // reserve space for \0
396
const unsigned char *start=scan;
405
if((label_len & 0xC0) == 0xC0) // compression
409
int offset=((label_len&0x3F)<<8) + *scan;
416
if(answer+offset>=scan+len)
419
len=scan+len-answer+offset;
422
break; // too many hops.
433
if(store && count<store_len)
440
if(store && count<store_len)
461
int SRV_compare(const void *a,const void *b)
463
struct SRV *sa=(struct SRV*)a;
464
struct SRV *sb=(struct SRV*)b;
465
if(sa->priority < sb->priority)
467
if(sa->priority > sb->priority)
469
if(sa->order < sb->order)
471
if(sa->order > sb->order)
473
if(sa->weight > sb->weight)
475
if(sa->weight < sb->weight)
481
void Resolver::LookupSRV_RR()
483
if(!ResMgr::QueryBool("dns:SRV-query",hostname))
485
#ifdef HAVE_RES_SEARCH
486
const char *tproto=proto?proto:"tcp";
488
unsigned char answer[0x1000];
489
char *srv_name=string_alloca(strlen(service)+1+strlen(tproto)+1+strlen(hostname)+1);
490
sprintf(srv_name,"_%s._%s.%s",service,tproto,hostname);
502
len=res_search(srv_name, C_IN, T_SRV, answer, sizeof(answer));
506
if(h_errno!=TRY_AGAIN)
510
sleep(5-(t-try_time));
516
if(len>(int)sizeof(answer))
522
int question_count=(answer[4]<<8)+answer[5];
523
int answer_count =(answer[6]<<8)+answer[7];
526
unsigned char *scan=answer+12;
529
// skip questions section
530
for( ; question_count>0; question_count--)
532
int dom_len=extract_domain(answer,scan,len,0,0);
544
// now process answers section
545
for( ; answer_count>0; answer_count--)
547
int dom_len=extract_domain(answer,scan,len,0,0);
553
len-=8; // skip type,class,ttl
558
int data_len=(scan[0]<<8)+scan[1];
569
SRVs=(struct SRV*)xrealloc(SRVs,sizeof(*SRVs)*SRV_num);
570
struct SRV *t=SRVs+SRV_num-1;
572
t->priority=(scan[0]<<8)+scan[1];
573
t->weight =(scan[2]<<8)+scan[3];
574
t->port =(scan[4]<<8)+scan[5];
581
dom_len=extract_domain(answer,scan,len,t->domain,sizeof(t->domain));
585
// check if the service is decidedly not available at this domain.
586
if(!strcmp(t->domain,"."))
590
// now sort and randomize the list.
591
qsort(SRVs,SRV_num,sizeof(*SRVs),SRV_compare);
595
struct SRV *SRVscan,*base=0;
596
int curr_priority=-1;
598
for(SRVscan=SRVs; ; SRVscan++)
600
if(SRVscan-SRVs==SRV_num || SRVscan->priority!=curr_priority)
608
int r=int(rand()/(RAND_MAX+1.0)*weight_sum);
612
for(s=base; s<SRVscan; s++)
621
weight_sum-=s->weight;
627
if(SRVscan-SRVs==SRV_num)
630
curr_priority=SRVscan->priority;
633
weight_sum+=SRVscan->weight;
636
qsort(SRVs,SRV_num,sizeof(*SRVs),SRV_compare);
638
int oldport=port_number;
639
for(SRVscan=SRVs; SRVscan-SRVs<SRV_num; SRVscan++)
641
port_number=htons(SRVscan->port);
642
LookupOne(SRVscan->domain);
646
#endif // HAVE_RES_SEARCH
649
void Resolver::LookupOne(const char *name)
655
const char *order=ResMgr::Query("dns:order",name);
657
const char *proto_delim=strchr(name,',');
660
char *o=string_alloca(proto_delim-name+1);
661
memcpy(o,name,proto_delim-name);
662
o[proto_delim-name]=0;
663
// check if the protocol name is valid.
664
if(FindAddressFamily(o)!=-1)
669
ParseOrder(order,af_order);
682
#if defined(HAVE_GETADDRINFO) && INET6 \
683
&& !defined(HAVE_GETHOSTBYNAME2) \
684
&& !defined(HAVE_GETIPNODEBYNAME)
686
// getaddrinfo support by Brandon Hume
687
struct addrinfo *ainfo=0,
691
struct sockaddr *sockname;
692
struct sockaddr_in *inet_addr;
693
struct sockaddr_in6 *inet6_addr;
694
const char *addr_data;
696
a_hint.ai_flags = AI_PASSIVE;
697
a_hint.ai_family = PF_UNSPEC;
698
a_hint.ai_socktype = 0;
699
a_hint.ai_protocol = 0;
700
a_hint.ai_addrlen = 0;
701
a_hint.ai_canonname = NULL;
702
a_hint.ai_addr = NULL;
703
a_hint.ai_next = NULL;
705
ainfo_res = getaddrinfo(name, NULL, &a_hint, &ainfo);
709
// by lav: add addresses in specified order.
710
for(int af=af_order[af_index]; af!=-1; af=af_order[++af_index])
712
for(a_res = ainfo; a_res != NULL; a_res = a_res->ai_next)
714
if(a_res->ai_family!=af)
717
sockname = a_res->ai_addr;
719
switch(a_res->ai_family)
722
inet_addr = (sockaddr_in *)sockname;
723
addr_data = (const char *)&inet_addr->sin_addr.s_addr;
726
inet6_addr = (sockaddr_in6 *)sockname;
727
addr_data = (const char *)inet6_addr->sin6_addr.s6_addr;
733
AddAddress(a_res->ai_family, addr_data, a_res->ai_addrlen);
741
if(ainfo_res != EAI_AGAIN)
743
error = gai_strerror(ainfo_res);
747
#else // !HAVE_GETADDRINFO
749
int af=af_order[af_index];
754
# if defined(HAVE_GETIPNODEBYNAME)
755
# ifndef HAVE_H_ERRNO
756
# define HAVE_H_ERRNO 1
758
# undef h_errno // it could be a macro, but we want it to be local variable.
760
ha=getipnodebyname(name,af,0,&h_errno);
761
# elif defined(HAVE_GETHOSTBYNAME2)
762
ha=gethostbyname2(name,af);
765
ha=gethostbyname(name);
775
const char * const *a;
776
for(a=ha->h_addr_list; *a; a++)
777
AddAddress(ha->h_addrtype, *a, ha->h_length);
779
# if defined(HAVE_GETIPNODEBYNAME)
786
if(h_errno!=TRY_AGAIN)
792
error=hstrerror(h_errno);
794
error=_("Host name lookup failure");
798
continue; // try other address families
800
#endif /* HAVE_GETADDRINFO */
803
if((t=time(0))-try_time<5)
804
sleep(5-(t-try_time));
808
void Resolver::DoGethostbyname()
812
const char *tproto=proto?proto:"tcp";
813
const char *tport=portname?portname:defport;
815
if(isdigit((unsigned char)tport[0]))
816
port_number=htons(atoi(tport));
819
struct servent *se=getservbyname(tport,tproto);
821
port_number=se->s_port;
825
char *msg=string_alloca(64+strlen(tproto));
826
sprintf(msg,_("no such %s service"),tproto);
833
if(service && !portname && !isdigit((unsigned char)hostname[0]))
836
if(!use_fork && deleting)
841
if(!use_fork && deleting)
848
error=_("No address found");
853
buf->Put((char*)addr,addr_num*sizeof(*addr));
861
while(buf->Size()>0 && !buf->Error() && !buf->Broken())
862
Roll(buf); // should flush quickly.
866
void Resolver::Reconfig(const char *name)
868
timeout = ResMgr::Query("dns:fatal-timeout",hostname);
869
if(!name || strncmp(name,"dns:",4))
877
ResolverCache::ResolverCache()
881
void ResolverCache::Add(const char *h,const char *p,const char *defp,
882
const char *ser,const char *pr,const sockaddr_u *a,int n)
884
Entry **ptr=FindPtr(h,p,defp,ser,pr);
888
Entry *next=(*ptr)->next;
892
chain=new Entry(chain,h,p,defp,ser,pr,a,n);
894
ResolverCache::Entry **ResolverCache::FindPtr(const char *h,const char *p,
895
const char *defp,const char *ser,const char *pr)
902
if(!xstrcasecmp(s->hostname,h)
903
&& !xstrcmp(s->portname,p)
904
&& !xstrcmp(s->defport,defp)
905
&& !xstrcmp(s->service,ser)
906
&& !xstrcmp(s->proto,pr))
912
void ResolverCache::Clear()
916
Entry *next=chain->next;
921
void ResolverCache::Find(const char *h,const char *p,const char *defp,
922
const char *ser,const char *pr,const sockaddr_u **a,int *n)
927
// if cache is disabled for this host, return nothing.
928
if(!ResMgr::QueryBool("dns:cache-enable",h))
931
Entry **ptr=FindPtr(h,p,defp,ser,pr);
940
// FIXME: this function can be speed-optimized.
941
void ResolverCache::CacheCheck()
943
int countlimit=ResMgr::Query("dns:cache-size",0);
949
TimeInterval expire((const char *)ResMgr::Query("dns:cache-expire",s->hostname));
950
if((!expire.IsInfty() && SMTask::now>=s->timestamp+expire.Seconds())
951
|| (count>=countlimit))