~ubuntu-branches/debian/jessie/lftp/jessie

« back to all changes in this revision

Viewing changes to src/Resolver.cc

  • Committer: Bazaar Package Importer
  • Author(s): Noèl Köthe
  • Date: 2004-06-01 04:01:39 UTC
  • Revision ID: james.westby@ubuntu.com-20040601040139-negt39q17jhkv3i4
Tags: upstream-3.0.5
Import upstream version 3.0.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * lftp and utils
 
3
 *
 
4
 * Copyright (c) 1996-2001 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 2 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, write to the Free Software
 
18
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 */
 
20
 
 
21
/* $Id: Resolver.cc,v 1.61 2004/05/28 07:57:05 lav Exp $ */
 
22
 
 
23
#include <config.h>
 
24
 
 
25
#include "Resolver.h"
 
26
#include "SignalHook.h"
 
27
#include <errno.h>
 
28
#include <unistd.h>
 
29
#include "trio.h"
 
30
#include <time.h>
 
31
#include <sys/types.h>
 
32
#include <sys/socket.h>
 
33
#include <ctype.h>
 
34
#include <fcntl.h>
 
35
 
 
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>
 
40
# undef class
 
41
#endif
 
42
#ifdef HAVE_RESOLV_H
 
43
# include <resolv.h>
 
44
#endif
 
45
 
 
46
#include "xstring.h"
 
47
#include "xmalloc.h"
 
48
#include "ResMgr.h"
 
49
#include "log.h"
 
50
#include "plural.h"
 
51
 
 
52
#ifndef C_IN
 
53
# define C_IN 1
 
54
#endif
 
55
#ifndef T_SRV
 
56
# define T_SRV 33
 
57
#endif
 
58
 
 
59
#if !HAVE_DECL_HSTRERROR
 
60
extern "C" { const char *hstrerror(int); }
 
61
#endif
 
62
 
 
63
#ifdef HAVE_H_ERRNO
 
64
#if !HAVE_DECL_H_ERRNO
 
65
CDECL int h_errno;
 
66
#endif
 
67
#endif
 
68
 
 
69
#if HAVE_RES_SEARCH && !HAVE_DECL_RES_SEARCH
 
70
CDECL int res_search(const char*,int,int,unsigned char*,int);
 
71
#endif
 
72
 
 
73
#if INET6
 
74
# define DEFAULT_ORDER "inet inet6"
 
75
#else
 
76
# define DEFAULT_ORDER "inet"
 
77
#endif
 
78
 
 
79
 
 
80
struct address_family
 
81
{
 
82
   int number;
 
83
   const char *name;
 
84
};
 
85
static const address_family af_list[]=
 
86
{
 
87
   { AF_INET,  "inet"  },
 
88
#if INET6
 
89
   { AF_INET6, "inet6" },
 
90
#endif
 
91
   { -1, 0 }
 
92
};
 
93
 
 
94
ResolverCache *Resolver::cache;
 
95
 
 
96
Resolver::Resolver(const char *h,const char *p,const char *defp,
 
97
                   const char *ser,const char *pr)
 
98
{
 
99
   hostname=xstrdup(h);
 
100
   portname=xstrdup(p);
 
101
   port_number=0;
 
102
 
 
103
   service=xstrdup(ser);
 
104
   proto=xstrdup(pr);
 
105
   defport=xstrdup(defp);
 
106
 
 
107
   pipe_to_child[0]=pipe_to_child[1]=-1;
 
108
   w=0;
 
109
   err_msg=0;
 
110
   done=false;
 
111
   timeout=0;
 
112
   Reconfig();
 
113
   time(&start_time);
 
114
   addr=0;
 
115
   addr_num=0;
 
116
   buf=0;
 
117
   use_fork=ResMgr::QueryBool("dns:use-fork",0);
 
118
 
 
119
   error=0;
 
120
 
 
121
   no_cache=false;
 
122
}
 
123
 
 
124
Resolver::~Resolver()
 
125
{
 
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]);
 
130
 
 
131
   xfree(hostname);
 
132
   xfree(portname);
 
133
   xfree(service);
 
134
   xfree(proto);
 
135
   xfree(defport);
 
136
 
 
137
   xfree(err_msg);
 
138
   xfree(addr);
 
139
   if(w)
 
140
   {
 
141
      w->Kill(SIGKILL);
 
142
      w->Auto();
 
143
   }
 
144
   Delete(buf);
 
145
}
 
146
 
 
147
int   Resolver::Do()
 
148
{
 
149
   if(done)
 
150
      return STALL;
 
151
 
 
152
   int m=STALL;
 
153
 
 
154
   if(!no_cache && cache)
 
155
   {
 
156
      const sockaddr_u *a;
 
157
      int n;
 
158
      cache->Find(hostname,portname,defport,service,proto,&a,&n);
 
159
      if(a && n>0)
 
160
      {
 
161
         Log::global->Write(10,"dns cache hit\n");
 
162
         addr_num=n;
 
163
         addr=(sockaddr_u*)xmalloc(n*sizeof(*addr));
 
164
         memcpy(addr,a,n*sizeof(*addr));
 
165
         done=true;
 
166
         return MOVED;
 
167
      }
 
168
      no_cache=true;
 
169
   }
 
170
 
 
171
   if(use_fork)
 
172
   {
 
173
      if(pipe_to_child[0]==-1)
 
174
      {
 
175
         int res=pipe(pipe_to_child);
 
176
         if(res==-1)
 
177
         {
 
178
            if(NonFatalError(errno))
 
179
               return m;
 
180
            MakeErrMsg("pipe()");
 
181
            return MOVED;
 
182
         }
 
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);
 
186
         m=MOVED;
 
187
         Log::global->Format(4,"---- %s\n",_("Resolving host address..."));
 
188
      }
 
189
 
 
190
      if(!w && !buf)
 
191
      {
 
192
         pid_t proc=fork();
 
193
         if(proc==-1)
 
194
         {
 
195
            TimeoutS(1);
 
196
            return m;
 
197
         }
 
198
         if(proc==0)
 
199
         {       // child
 
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]);
 
206
            pipe_to_child[0]=-1;
 
207
            buf=new IOBufferFDStream(new FDStream(pipe_to_child[1],"<pipe-out>"),IOBuffer::PUT);
 
208
            DoGethostbyname();
 
209
            _exit(0);
 
210
         }
 
211
         // parent
 
212
         close(pipe_to_child[1]);
 
213
         pipe_to_child[1]=-1;
 
214
 
 
215
         w=new ProcWait(proc);
 
216
         m=MOVED;
 
217
      }
 
218
      if(!buf)
 
219
      {
 
220
         buf=new IOBufferFDStream(new FDStream(pipe_to_child[0],"<pipe-in>"),IOBuffer::GET);
 
221
//       Roll(buf);
 
222
         m=MOVED;
 
223
      }
 
224
   }
 
225
   else /* !use_fork */
 
226
   {
 
227
      if(!buf)
 
228
      {
 
229
         Log::global->Format(4,"---- %s\n",_("Resolving host address..."));
 
230
         buf=new IOBuffer(IOBuffer::GET);
 
231
         DoGethostbyname();
 
232
         if(deleting)
 
233
            return MOVED;
 
234
      }
 
235
   }
 
236
 
 
237
   if(buf->Error())
 
238
   {
 
239
      err_msg=xstrdup(buf->ErrorText());
 
240
      done=true;
 
241
      return MOVED;
 
242
   }
 
243
 
 
244
   if(!buf->Eof())   // wait for all data to arrive (not too much)
 
245
   {
 
246
      if(timeout>0)
 
247
      {
 
248
         if(now >= start_time+timeout)
 
249
         {
 
250
            err_msg=xstrdup(_("host name resolve timeout"));
 
251
            done=true;
 
252
            return MOVED;
 
253
         }
 
254
         TimeoutS(timeout-(time_t(now)-start_time));
 
255
      }
 
256
      return m;
 
257
   }
 
258
 
 
259
   const char *s;
 
260
   char c;
 
261
   int n;
 
262
 
 
263
   buf->Get(&s,&n);
 
264
   if(n<1)
 
265
      goto proto_error;
 
266
   c=*s;
 
267
   buf->Skip(1);
 
268
   buf->Get(&s,&n);
 
269
   if(c=='E' || c=='P') // error
 
270
   {
 
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);
 
275
      memcpy(e,s,n);
 
276
      e[n]=0;
 
277
      done=true;
 
278
      return MOVED;
 
279
   }
 
280
   if((unsigned)n<sizeof(sockaddr_u))
 
281
   {
 
282
   proto_error:
 
283
      if(use_fork)
 
284
      {
 
285
         // e.g. under gdb child fails.
 
286
         Log::global->Format(4,"**** %s\n","child failed, retrying with dns:use-fork=no");
 
287
         use_fork=false;
 
288
         Delete(buf);
 
289
         buf=0;
 
290
         return MOVED;
 
291
      }
 
292
      err_msg=xstrdup("BUG: internal class Resolver error");
 
293
      done=true;
 
294
      return MOVED;
 
295
   }
 
296
   addr_num=n/sizeof(*addr);
 
297
   addr=(sockaddr_u*)xmalloc(n);
 
298
   memcpy(addr,s,n);
 
299
   done=true;
 
300
   if(!cache)
 
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);
 
304
   return MOVED;
 
305
}
 
306
 
 
307
void Resolver::MakeErrMsg(const char *f)
 
308
{
 
309
   const char *e=strerror(errno);
 
310
   err_msg=(char*)xmalloc(strlen(e)+strlen(f)+3);
 
311
   sprintf(err_msg,"%s: %s",f,e);
 
312
   done=true;
 
313
}
 
314
 
 
315
void Resolver::AddAddress(int family,const char *address,int len)
 
316
{
 
317
   addr=(sockaddr_u*)xrealloc(addr,(addr_num+1)*sizeof(*addr));
 
318
   sockaddr_u *add=addr+addr_num;
 
319
   addr_num++;
 
320
 
 
321
   memset(add,0,sizeof(*add));
 
322
 
 
323
   add->sa.sa_family=family;
 
324
   switch(family)
 
325
   {
 
326
   case AF_INET:
 
327
      if(sizeof(add->in.sin_addr) != len)
 
328
      {
 
329
         addr_num--;
 
330
         return;
 
331
      }
 
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);
 
336
#endif
 
337
      break;
 
338
 
 
339
#if INET6
 
340
   case AF_INET6:
 
341
      if(sizeof(add->in6.sin6_addr) != len)
 
342
      {
 
343
         addr_num--;
 
344
         return;
 
345
      }
 
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);
 
350
#endif
 
351
      break;
 
352
#endif
 
353
 
 
354
   default:
 
355
      addr_num--;
 
356
      return;
 
357
   }
 
358
}
 
359
 
 
360
int Resolver::FindAddressFamily(const char *name)
 
361
{
 
362
   for(const address_family *f=af_list; f->name; f++)
 
363
   {
 
364
      if(!strcasecmp(name,f->name))
 
365
         return f->number;
 
366
   }
 
367
   return -1;
 
368
}
 
369
 
 
370
void Resolver::ParseOrder(const char *s,int *o)
 
371
{
 
372
   const char * const delim="\t ";
 
373
   char *s1=alloca_strdup(s);
 
374
   int idx=0;
 
375
 
 
376
   for(s1=strtok(s1,delim); s1; s1=strtok(0,delim))
 
377
   {
 
378
      int af=FindAddressFamily(s1);
 
379
      if(af!=-1 && idx<15)
 
380
      {
 
381
         if(o) o[idx]=af;
 
382
         idx++;
 
383
      }
 
384
   }
 
385
   if(o) o[idx]=-1;
 
386
}
 
387
 
 
388
#ifdef HAVE_RES_SEARCH
 
389
static
 
390
int extract_domain(const unsigned char *answer,const unsigned char *scan,int len,
 
391
                     char *store,int store_len)
 
392
{
 
393
   int count=1;   // reserve space for \0
 
394
   int refs=0;
 
395
   int consumed=0;
 
396
   const unsigned char *start=scan;
 
397
   for(;;)
 
398
   {
 
399
      if(len<=0)
 
400
         break;
 
401
      int label_len=*scan;
 
402
      scan++;
 
403
      len--;
 
404
 
 
405
      if((label_len & 0xC0) == 0xC0)   // compression
 
406
      {
 
407
         if(len<=0)
 
408
            break;
 
409
         int offset=((label_len&0x3F)<<8) + *scan;
 
410
         scan++;
 
411
         len--;
 
412
 
 
413
         if(refs==0)
 
414
            consumed=scan-start;
 
415
 
 
416
         if(answer+offset>=scan+len)
 
417
            break; // error
 
418
 
 
419
         len=scan+len-answer+offset;
 
420
         scan=answer+offset;
 
421
         if(++refs > 256)
 
422
            break;   // too many hops.
 
423
         continue;
 
424
      }
 
425
 
 
426
      if(label_len==0)
 
427
         break;
 
428
 
 
429
      while(label_len>0)
 
430
      {
 
431
         if(len<=0)
 
432
            break;
 
433
         if(store && count<store_len)
 
434
            *store++=*scan;
 
435
         count++;
 
436
         scan++;
 
437
         len--;
 
438
         label_len--;
 
439
      }
 
440
      if(store && count<store_len)
 
441
         *store++='.';
 
442
      count++;
 
443
   }
 
444
   if(store)
 
445
      *store=0;
 
446
   if(refs==0)
 
447
      consumed=scan-start;
 
448
   return consumed;
 
449
}
 
450
 
 
451
struct SRV
 
452
{
 
453
   char domain[256];
 
454
   int port;
 
455
   int priority;
 
456
   int weight;
 
457
   int order;
 
458
};
 
459
 
 
460
static
 
461
int SRV_compare(const void *a,const void *b)
 
462
{
 
463
   struct SRV *sa=(struct SRV*)a;
 
464
   struct SRV *sb=(struct SRV*)b;
 
465
   if(sa->priority < sb->priority)
 
466
      return -1;
 
467
   if(sa->priority > sb->priority)
 
468
      return 1;
 
469
   if(sa->order < sb->order)
 
470
      return -1;
 
471
   if(sa->order > sb->order)
 
472
      return 1;
 
473
   if(sa->weight > sb->weight)
 
474
      return -1;
 
475
   if(sa->weight < sb->weight)
 
476
      return 1;
 
477
   return 0;
 
478
}
 
479
#endif // RES_SEARCH
 
480
 
 
481
void Resolver::LookupSRV_RR()
 
482
{
 
483
   if(!ResMgr::QueryBool("dns:SRV-query",hostname))
 
484
      return;
 
485
#ifdef HAVE_RES_SEARCH
 
486
   const char *tproto=proto?proto:"tcp";
 
487
   time_t try_time;
 
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);
 
491
 
 
492
   int len;
 
493
   for(;;)
 
494
   {
 
495
      if(!use_fork)
 
496
      {
 
497
         Schedule();
 
498
         if(deleting)
 
499
            return;
 
500
      }
 
501
      time(&try_time);
 
502
      len=res_search(srv_name, C_IN, T_SRV, answer, sizeof(answer));
 
503
      if(len>=0)
 
504
         break;
 
505
#ifdef HAVE_H_ERRNO
 
506
      if(h_errno!=TRY_AGAIN)
 
507
         return;
 
508
      time_t t=time(0);
 
509
      if(t-try_time<5)
 
510
         sleep(5-(t-try_time));
 
511
#else // no h_errno
 
512
      return;
 
513
#endif
 
514
   }
 
515
 
 
516
   if(len>(int)sizeof(answer))
 
517
      len=sizeof(answer);
 
518
 
 
519
   if(len<12)
 
520
      return;
 
521
 
 
522
   int question_count=(answer[4]<<8)+answer[5];
 
523
   int answer_count  =(answer[6]<<8)+answer[7];
 
524
 
 
525
   // skip header
 
526
   unsigned char *scan=answer+12;
 
527
   len-=12;
 
528
 
 
529
   // skip questions section
 
530
   for( ; question_count>0; question_count--)
 
531
   {
 
532
      int dom_len=extract_domain(answer,scan,len,0,0);
 
533
      scan+=dom_len;
 
534
      len-=dom_len;
 
535
      if(len<4)
 
536
         return;
 
537
      scan+=4;
 
538
      len-=4;
 
539
   }
 
540
 
 
541
   struct SRV *SRVs=0;
 
542
   int SRV_num=0;
 
543
 
 
544
   // now process answers section
 
545
   for( ; answer_count>0; answer_count--)
 
546
   {
 
547
      int dom_len=extract_domain(answer,scan,len,0,0);
 
548
      scan+=dom_len;
 
549
      len-=dom_len;
 
550
      if(len<8)
 
551
         return;
 
552
      scan+=8;
 
553
      len-=8;  // skip type,class,ttl
 
554
 
 
555
      if(len<2)
 
556
         return;
 
557
 
 
558
      int data_len=(scan[0]<<8)+scan[1];
 
559
      scan+=2;
 
560
      len-=2;
 
561
 
 
562
      if(len<data_len)
 
563
         return;
 
564
 
 
565
      if(data_len<6)
 
566
         return;
 
567
 
 
568
      SRV_num++;
 
569
      SRVs=(struct SRV*)xrealloc(SRVs,sizeof(*SRVs)*SRV_num);
 
570
      struct SRV *t=SRVs+SRV_num-1;
 
571
 
 
572
      t->priority=(scan[0]<<8)+scan[1];
 
573
      t->weight  =(scan[2]<<8)+scan[3];
 
574
      t->port    =(scan[4]<<8)+scan[5];
 
575
 
 
576
      t->order=0;
 
577
 
 
578
      scan+=6;
 
579
      len-=6;
 
580
 
 
581
      dom_len=extract_domain(answer,scan,len,t->domain,sizeof(t->domain));
 
582
      scan+=dom_len;
 
583
      len-=dom_len;
 
584
 
 
585
      // check if the service is decidedly not available at this domain.
 
586
      if(!strcmp(t->domain,"."))
 
587
         SRV_num--;
 
588
   }
 
589
 
 
590
   // now sort and randomize the list.
 
591
   qsort(SRVs,SRV_num,sizeof(*SRVs),SRV_compare);
 
592
 
 
593
   srand(time(0));
 
594
 
 
595
   struct SRV *SRVscan,*base=0;
 
596
   int curr_priority=-1;
 
597
   int weight_sum=0;
 
598
   for(SRVscan=SRVs; ; SRVscan++)
 
599
   {
 
600
      if(SRVscan-SRVs==SRV_num || SRVscan->priority!=curr_priority)
 
601
      {
 
602
         if(base)
 
603
         {
 
604
            int o=1;
 
605
            struct SRV *s;
 
606
            while(weight_sum>0)
 
607
            {
 
608
               int r=int(rand()/(RAND_MAX+1.0)*weight_sum);
 
609
               if(r>=weight_sum)
 
610
                  r=weight_sum-1;
 
611
               int w=0;
 
612
               for(s=base; s<SRVscan; s++)
 
613
               {
 
614
                  if(s->order!=0)
 
615
                     continue;
 
616
                  w+=s->weight;
 
617
                  if(r<w)
 
618
                  {
 
619
                     s->order=o;
 
620
                     o++;
 
621
                     weight_sum-=s->weight;
 
622
                     break;
 
623
                  }
 
624
               }
 
625
            }
 
626
         }
 
627
         if(SRVscan-SRVs==SRV_num)
 
628
            break;
 
629
         base=SRVscan;
 
630
         curr_priority=SRVscan->priority;
 
631
         weight_sum=0;
 
632
      }
 
633
      weight_sum+=SRVscan->weight;
 
634
   }
 
635
 
 
636
   qsort(SRVs,SRV_num,sizeof(*SRVs),SRV_compare);
 
637
 
 
638
   int oldport=port_number;
 
639
   for(SRVscan=SRVs; SRVscan-SRVs<SRV_num; SRVscan++)
 
640
   {
 
641
      port_number=htons(SRVscan->port);
 
642
      LookupOne(SRVscan->domain);
 
643
   }
 
644
   port_number=oldport;
 
645
 
 
646
#endif // HAVE_RES_SEARCH
 
647
}
 
648
 
 
649
void Resolver::LookupOne(const char *name)
 
650
{
 
651
   time_t try_time;
 
652
   int af_index=0;
 
653
   int af_order[16];
 
654
 
 
655
   const char *order=ResMgr::Query("dns:order",name);
 
656
 
 
657
   const char *proto_delim=strchr(name,',');
 
658
   if(proto_delim)
 
659
   {
 
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)
 
665
         order=o;
 
666
      name=proto_delim+1;
 
667
   }
 
668
 
 
669
   ParseOrder(order,af_order);
 
670
 
 
671
   for(;;)
 
672
   {
 
673
      if(!use_fork)
 
674
      {
 
675
         Schedule();
 
676
         if(deleting)
 
677
            return;
 
678
      }
 
679
 
 
680
      time(&try_time);
 
681
 
 
682
#if defined(HAVE_GETADDRINFO) && INET6 \
 
683
   && !defined(HAVE_GETHOSTBYNAME2) \
 
684
   && !defined(HAVE_GETIPNODEBYNAME)
 
685
 
 
686
      // getaddrinfo support by Brandon Hume
 
687
      struct addrinfo       *ainfo=0,
 
688
                            *a_res,
 
689
                            a_hint;
 
690
      int                   ainfo_res;
 
691
      struct sockaddr       *sockname;
 
692
      struct sockaddr_in    *inet_addr;
 
693
      struct sockaddr_in6   *inet6_addr;
 
694
      const char            *addr_data;
 
695
 
 
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;
 
704
 
 
705
      ainfo_res = getaddrinfo(name, NULL, &a_hint, &ainfo);
 
706
 
 
707
      if(ainfo_res == 0)
 
708
      {
 
709
        // by lav: add addresses in specified order.
 
710
        for(int af=af_order[af_index]; af!=-1; af=af_order[++af_index])
 
711
        {
 
712
            for(a_res = ainfo; a_res != NULL; a_res = a_res->ai_next)
 
713
            {
 
714
               if(a_res->ai_family!=af)
 
715
                  continue;
 
716
 
 
717
               sockname = a_res->ai_addr;
 
718
 
 
719
               switch(a_res->ai_family)
 
720
               {
 
721
               case AF_INET:
 
722
                  inet_addr     = (sockaddr_in *)sockname;
 
723
                  addr_data     = (const char *)&inet_addr->sin_addr.s_addr;
 
724
                  break;
 
725
               case AF_INET6:
 
726
                  inet6_addr    = (sockaddr_in6 *)sockname;
 
727
                  addr_data     = (const char *)inet6_addr->sin6_addr.s6_addr;
 
728
                  break;
 
729
               default:
 
730
                  continue;
 
731
               }
 
732
 
 
733
               AddAddress(a_res->ai_family, addr_data, a_res->ai_addrlen);
 
734
            }
 
735
         }
 
736
 
 
737
         freeaddrinfo(ainfo);
 
738
         break;
 
739
      }
 
740
 
 
741
      if(ainfo_res != EAI_AGAIN)
 
742
      {
 
743
         error = gai_strerror(ainfo_res);
 
744
         break;
 
745
      }
 
746
 
 
747
#else // !HAVE_GETADDRINFO
 
748
 
 
749
      int af=af_order[af_index];
 
750
      if(af==-1)
 
751
         break;
 
752
 
 
753
      struct hostent *ha;
 
754
# if defined(HAVE_GETIPNODEBYNAME)
 
755
#  ifndef HAVE_H_ERRNO
 
756
#   define HAVE_H_ERRNO 1
 
757
#  endif
 
758
#  undef h_errno // it could be a macro, but we want it to be local variable.
 
759
      int h_errno=0;
 
760
      ha=getipnodebyname(name,af,0,&h_errno);
 
761
# elif defined(HAVE_GETHOSTBYNAME2)
 
762
      ha=gethostbyname2(name,af);
 
763
# else
 
764
      if(af==AF_INET)
 
765
         ha=gethostbyname(name);
 
766
      else
 
767
      {
 
768
         af_index++;
 
769
         continue;
 
770
      }
 
771
# endif
 
772
 
 
773
      if(ha)
 
774
      {
 
775
         const char * const *a;
 
776
         for(a=ha->h_addr_list; *a; a++)
 
777
            AddAddress(ha->h_addrtype, *a, ha->h_length);
 
778
         af_index++;
 
779
# if defined(HAVE_GETIPNODEBYNAME)
 
780
         freehostent(ha);
 
781
# endif
 
782
         continue;
 
783
      }
 
784
 
 
785
# ifdef HAVE_H_ERRNO
 
786
      if(h_errno!=TRY_AGAIN)
 
787
# endif
 
788
      {
 
789
         if(error==0)
 
790
         {
 
791
# ifdef HAVE_H_ERRNO
 
792
            error=hstrerror(h_errno);
 
793
# else
 
794
            error=_("Host name lookup failure");
 
795
# endif
 
796
         }
 
797
         af_index++;
 
798
         continue; // try other address families
 
799
      }
 
800
#endif /* HAVE_GETADDRINFO */
 
801
 
 
802
      time_t t;
 
803
      if((t=time(0))-try_time<5)
 
804
         sleep(5-(t-try_time));
 
805
   }
 
806
}
 
807
 
 
808
void Resolver::DoGethostbyname()
 
809
{
 
810
   if(port_number==0)
 
811
   {
 
812
      const char *tproto=proto?proto:"tcp";
 
813
      const char *tport=portname?portname:defport;
 
814
 
 
815
      if(isdigit((unsigned char)tport[0]))
 
816
         port_number=htons(atoi(tport));
 
817
      else
 
818
      {
 
819
         struct servent *se=getservbyname(tport,tproto);
 
820
         if(se)
 
821
            port_number=se->s_port;
 
822
         else
 
823
         {
 
824
            buf->Put("P");
 
825
            char *msg=string_alloca(64+strlen(tproto));
 
826
            sprintf(msg,_("no such %s service"),tproto);
 
827
            buf->Put(msg);
 
828
            goto flush;
 
829
         }
 
830
      }
 
831
   }
 
832
 
 
833
   if(service && !portname && !isdigit((unsigned char)hostname[0]))
 
834
      LookupSRV_RR();
 
835
 
 
836
   if(!use_fork && deleting)
 
837
      return;
 
838
 
 
839
   LookupOne(hostname);
 
840
 
 
841
   if(!use_fork && deleting)
 
842
      return;
 
843
 
 
844
   if(addr_num==0)
 
845
   {
 
846
      buf->Put("E");
 
847
      if(error==0)
 
848
         error=_("No address found");
 
849
      buf->Put(error);
 
850
      goto flush;
 
851
   }
 
852
   buf->Put("O");
 
853
   buf->Put((char*)addr,addr_num*sizeof(*addr));
 
854
   xfree(addr);
 
855
   addr=0;
 
856
 
 
857
flush:
 
858
   buf->PutEOF();
 
859
   if(use_fork)
 
860
   {
 
861
      while(buf->Size()>0 && !buf->Error() && !buf->Broken())
 
862
         Roll(buf);  // should flush quickly.
 
863
   }
 
864
}
 
865
 
 
866
void Resolver::Reconfig(const char *name)
 
867
{
 
868
   timeout = ResMgr::Query("dns:fatal-timeout",hostname);
 
869
   if(!name || strncmp(name,"dns:",4))
 
870
      return;
 
871
   if(cache)
 
872
      cache->Clear();
 
873
}
 
874
 
 
875
 
 
876
 
 
877
ResolverCache::ResolverCache()
 
878
{
 
879
   chain=0;
 
880
}
 
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)
 
883
{
 
884
   Entry **ptr=FindPtr(h,p,defp,ser,pr);
 
885
   if(ptr && *ptr)
 
886
   {
 
887
      // delete old
 
888
      Entry *next=(*ptr)->next;
 
889
      delete *ptr;
 
890
      *ptr=next;
 
891
   }
 
892
   chain=new Entry(chain,h,p,defp,ser,pr,a,n);
 
893
}
 
894
ResolverCache::Entry **ResolverCache::FindPtr(const char *h,const char *p,
 
895
         const char *defp,const char *ser,const char *pr)
 
896
{
 
897
   CacheCheck();
 
898
   Entry **scan=&chain;
 
899
   while(*scan)
 
900
   {
 
901
      Entry *s=*scan;
 
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))
 
907
         return scan;
 
908
      scan=&s->next;
 
909
   }
 
910
   return 0;
 
911
}
 
912
void ResolverCache::Clear()
 
913
{
 
914
   while(chain)
 
915
   {
 
916
      Entry *next=chain->next;
 
917
      delete chain;
 
918
      chain=next;
 
919
   }
 
920
}
 
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)
 
923
{
 
924
   *a=0;
 
925
   *n=0;
 
926
 
 
927
   // if cache is disabled for this host, return nothing.
 
928
   if(!ResMgr::QueryBool("dns:cache-enable",h))
 
929
      return;
 
930
 
 
931
   Entry **ptr=FindPtr(h,p,defp,ser,pr);
 
932
   if(ptr && *ptr)
 
933
   {
 
934
      Entry *s=*ptr;
 
935
      *a=s->addr;
 
936
      *n=s->addr_num;
 
937
   }
 
938
}
 
939
 
 
940
// FIXME: this function can be speed-optimized.
 
941
void ResolverCache::CacheCheck()
 
942
{
 
943
   int countlimit=ResMgr::Query("dns:cache-size",0);
 
944
   int count=0;
 
945
   Entry **scan=&chain;
 
946
   while(*scan)
 
947
   {
 
948
      Entry *s=*scan;
 
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))
 
952
      {
 
953
         *scan=s->next;
 
954
         delete s;
 
955
         continue;
 
956
      }
 
957
      scan=&s->next;
 
958
      count++;
 
959
   }
 
960
}