]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
- Fixed UDP Overlapped I/O under windows. (not the best way this could be done by...
[user/henk/code/inspircd.git] / src / dns.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /*
15 dns.cpp - Nonblocking DNS functions.
16 Very very loosely based on the firedns library,
17 Copyright (C) 2002 Ian Gulliver. This file is no
18 longer anything like firedns, there are many major
19 differences between this code and the original.
20 Please do not assume that firedns works like this,
21 looks like this, walks like this or tastes like this.
22 */
23
24 #ifndef WIN32
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <errno.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #else
31 #include "inspircd_win32wrapper.h"
32 #include "inspircd_se_config.h"
33 #endif
34
35 #include "dns.h"
36 #include "inspircd.h"
37 #include "socketengine.h"
38 #include "configreader.h"
39 #include "socket.h"
40
41 using irc::sockets::insp_inaddr;
42 using irc::sockets::insp_ntoa;
43 using irc::sockets::insp_aton;
44 using irc::sockets::OpenTCPSocket;
45
46 /** Masks to mask off the responses we get from the DNSRequest methods
47  */
48 enum QueryInfo
49 {
50         ERROR_MASK      = 0x10000       /* Result is an error */
51 };
52
53 /** Flags which can be ORed into a request or reply for different meanings
54  */
55 enum QueryFlags
56 {
57         FLAGS_MASK_RD           = 0x01, /* Recursive */
58         FLAGS_MASK_TC           = 0x02,
59         FLAGS_MASK_AA           = 0x04, /* Authoritative */
60         FLAGS_MASK_OPCODE       = 0x78,
61         FLAGS_MASK_QR           = 0x80,
62         FLAGS_MASK_RCODE        = 0x0F, /* Request */
63         FLAGS_MASK_Z            = 0x70,
64         FLAGS_MASK_RA           = 0x80
65 };
66
67
68 /** Represents a dns resource record (rr)
69  */
70 struct ResourceRecord
71 {
72         QueryType       type;           /* Record type */
73         unsigned int    rr_class;       /* Record class */
74         unsigned long   ttl;            /* Time to live */
75         unsigned int    rdlength;       /* Record length */
76 };
77
78 /** Represents a dns request/reply header, and its payload as opaque data.
79  */
80 class DNSHeader
81 {
82  public:
83         unsigned char   id[2];          /* Request id */
84         unsigned int    flags1;         /* Flags */
85         unsigned int    flags2;         /* Flags */
86         unsigned int    qdcount;
87         unsigned int    ancount;        /* Answer count */
88         unsigned int    nscount;        /* Nameserver count */
89         unsigned int    arcount;
90         unsigned char   payload[512];   /* Packet payload */
91 };
92
93 class DNSRequest
94 {
95  public:
96         unsigned char   id[2];          /* Request id */
97         unsigned char*  res;            /* Result processing buffer */
98         unsigned int    rr_class;       /* Request class */
99         QueryType       type;           /* Request type */
100         DNS*            dnsobj;         /* DNS caller (where we get our FD from) */
101         unsigned long   ttl;            /* Time to live */
102         std::string     orig;           /* Original requested name/ip */
103
104         DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
105         ~DNSRequest();
106         DNSInfo ResultIsReady(DNSHeader &h, int length);
107         int SendRequests(const DNSHeader *header, const int length, QueryType qt);
108 };
109
110 class CacheTimer : public InspTimer
111 {
112  private:
113         InspIRCd* ServerInstance;
114         DNS* dns;
115  public:
116         CacheTimer(InspIRCd* Instance, DNS* thisdns)
117                 : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
118
119         virtual void Tick(time_t TIME)
120         {
121                 dns->PruneCache();
122         }
123 };
124
125 class RequestTimeout : public InspTimer
126 {
127         InspIRCd* ServerInstance;
128         DNSRequest* watch;
129         int watchid;
130  public:
131         RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
132         {
133         }
134
135         void Tick(time_t TIME)
136         {
137                 if (ServerInstance->Res->requests[watchid] == watch)
138                 {
139                         /* Still exists, whack it */
140                         if (ServerInstance->Res->Classes[watchid])
141                         {
142                                 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
143                                 delete ServerInstance->Res->Classes[watchid];
144                                 ServerInstance->Res->Classes[watchid] = NULL;
145                         }
146                         ServerInstance->Res->requests[watchid] = NULL;
147                         DELETE(watch);
148                         return;
149                 }
150         }
151 };
152
153 /* Allocate the processing buffer */
154 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns)
155 {
156         res = new unsigned char[512];
157         *res = 0;
158         orig = original;
159         RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
160         Instance->Timers->AddTimer(RT); /* The timer manager frees this */
161 }
162
163 /* Deallocate the processing buffer */
164 DNSRequest::~DNSRequest()
165 {
166         delete[] res;
167 }
168
169 /** Fill a ResourceRecord class based on raw data input */
170 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
171 {
172         rr->type = (QueryType)((input[0] << 8) + input[1]);
173         rr->rr_class = (input[2] << 8) + input[3];
174         rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
175         rr->rdlength = (input[8] << 8) + input[9];
176 }
177
178 /** Fill a DNSHeader class based on raw data input of a given length */
179 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
180 {
181         header->id[0] = input[0];
182         header->id[1] = input[1];
183         header->flags1 = input[2];
184         header->flags2 = input[3];
185         header->qdcount = (input[4] << 8) + input[5];
186         header->ancount = (input[6] << 8) + input[7];
187         header->nscount = (input[8] << 8) + input[9];
188         header->arcount = (input[10] << 8) + input[11];
189         memcpy(header->payload,&input[12],length);
190 }
191
192 /** Empty a DNSHeader class out into raw data, ready for transmission */
193 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
194 {
195         output[0] = header->id[0];
196         output[1] = header->id[1];
197         output[2] = header->flags1;
198         output[3] = header->flags2;
199         output[4] = header->qdcount >> 8;
200         output[5] = header->qdcount & 0xFF;
201         output[6] = header->ancount >> 8;
202         output[7] = header->ancount & 0xFF;
203         output[8] = header->nscount >> 8;
204         output[9] = header->nscount & 0xFF;
205         output[10] = header->arcount >> 8;
206         output[11] = header->arcount & 0xFF;
207         memcpy(&output[12],header->payload,length);
208 }
209
210 /** Send requests we have previously built down the UDP socket */
211 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
212 {
213         unsigned char payload[sizeof(DNSHeader)];
214
215         this->rr_class = 1;
216         this->type = qt;
217                 
218         DNS::EmptyHeader(payload,header,length);
219
220 #ifdef IPV6
221         if (this->dnsobj->socketfamily == AF_INET6)
222         {
223                 sockaddr_in6 addr;
224                 memset(&addr,0,sizeof(addr));
225                 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
226                 addr.sin6_family = AF_INET6;
227                 addr.sin6_port = htons(DNS::QUERY_PORT);
228                 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
229                         return -1;
230         }
231         else
232         {
233                 sockaddr_in addr;
234                 memset(&addr,0,sizeof(addr));
235                 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
236                 addr.sin_family = AF_INET;
237                 addr.sin_port = htons(DNS::QUERY_PORT);
238                 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
239                         return -1;
240         }
241 #else
242         sockaddr_in addr;
243         memset(&addr,0,sizeof(addr));
244         memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr));
245         addr.sin_family = AF_INET;
246         addr.sin_port = htons(DNS::QUERY_PORT);
247         if (sendto(dnsobj->GetFd(), (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
248                 return -1;
249 #endif
250
251         return 0;
252 }
253
254 /** Add a query with a predefined header, and allocate an ID for it. */
255 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
256 {
257         /* Is the DNS connection down? */
258         if (this->GetFd() == -1)
259                 return NULL;
260         
261         /* Create an id */
262         id = this->PRNG() & DNS::MAX_REQUEST_ID;
263
264         /* If this id is already 'in flight', pick another. */
265         while (requests[id])
266                 id = this->PRNG() & DNS::MAX_REQUEST_ID;
267
268         DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
269
270         header->id[0] = req->id[0] = id >> 8;
271         header->id[1] = req->id[1] = id & 0xFF;
272         header->flags1 = FLAGS_MASK_RD;
273         header->flags2 = 0;
274         header->qdcount = 1;
275         header->ancount = 0;
276         header->nscount = 0;
277         header->arcount = 0;
278
279         /* At this point we already know the id doesnt exist,
280          * so there needs to be no second check for the ::end()
281          */
282         requests[id] = req;
283
284         /* According to the C++ spec, new never returns NULL. */
285         return req;
286 }
287
288 int DNS::ClearCache()
289 {
290         /* This ensures the buckets are reset to sane levels */
291         int rv = this->cache->size();
292         delete this->cache;
293         this->cache = new dnscache();
294         return rv;
295 }
296
297 int DNS::PruneCache()
298 {
299         int n = 0;
300         dnscache* newcache = new dnscache();
301         for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
302                 /* Dont include expired items (theres no point) */
303                 if (i->second.CalcTTLRemaining())
304                         newcache->insert(*i);
305                 else
306                         n++;
307
308         delete this->cache;
309         this->cache = newcache;
310         return n;
311 }
312
313 void DNS::Rehash()
314 {
315         ip6munge = false;
316         int portpass = 0;
317
318         if (this->GetFd() > -1)
319         {
320                 if (ServerInstance && ServerInstance->SE)
321                         ServerInstance->SE->DelFd(this);
322                 shutdown(this->GetFd(), 2);
323                 close(this->GetFd());
324                 this->SetFd(-1);
325
326                 /* Rehash the cache */
327                 this->PruneCache();
328         }
329         else
330         {
331                 /* Create initial dns cache */
332                 this->cache = new dnscache();
333         }
334
335         if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) ||  (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
336         {
337                 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
338                 ServerInstance->Log(DEFAULT,"         This should not cause a problem, however it is recommended you migrate");
339                 ServerInstance->Log(DEFAULT,"         to a true IPv6 environment.");
340                 this->ip6munge = true;
341         }
342
343         this->socketfamily = AF_INET;
344 #ifdef IPV6
345         if (strchr(ServerInstance->Config->DNSServer,':'))
346         {
347                 this->socketfamily = AF_INET6;
348                 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
349         }
350         else
351         {
352                 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
353                 portpass = -1;
354         }
355 #else
356         inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
357 #endif
358
359         /* Initialize mastersocket */
360         int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
361         this->SetFd(s);
362
363         /* Have we got a socket and is it nonblocking? */
364         if (this->GetFd() != -1)
365         {
366                 /* Bind the port - port 0 INADDR_ANY */
367                 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
368                 {
369                         /* Failed to bind */
370                         shutdown(this->GetFd(),2);
371                         close(this->GetFd());
372                         this->SetFd(-1);
373                 }
374
375                 if (this->GetFd() >= 0)
376                 {
377                         /* Hook the descriptor into the socket engine */
378                         if (ServerInstance && ServerInstance->SE)
379                         {
380                                 if (!ServerInstance->SE->AddFd(this))
381                                 {
382                                         ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
383                                         shutdown(this->GetFd(),2);
384                                         close(this->GetFd());
385                                         this->SetFd(-1);
386                                 }
387                         }
388                 }
389         }
390 }
391
392 /** Initialise the DNS UDP socket so that we can send requests */
393 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
394 {
395         /* Clear the Resolver class table */
396         memset(Classes,0,sizeof(Classes));
397
398         /* Clear the requests class table */
399         memset(requests,0,sizeof(requests));
400
401         /* Set the id of the next request to 0
402          */
403         currid = 0;
404
405         /* DNS::Rehash() sets this to a valid ptr
406          */
407         this->cache = NULL;
408         
409         /* Again, DNS::Rehash() sets this to a
410          * valid value
411          */
412         this->SetFd(-1);
413
414         /* Actually read the settings
415          */
416         this->Rehash();
417
418         this->PruneTimer = new CacheTimer(ServerInstance, this);
419
420         ServerInstance->Timers->AddTimer(this->PruneTimer);
421 }
422
423 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
424 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
425 {
426         short payloadpos = 0;
427         const char* tempchr, *tempchr2 = name;
428         unsigned short length;
429
430         /* split name up into labels, create query */
431         while ((tempchr = strchr(tempchr2,'.')) != NULL)
432         {
433                 length = tempchr - tempchr2;
434                 if (payloadpos + length + 1 > 507)
435                         return -1;
436                 payload[payloadpos++] = length;
437                 memcpy(&payload[payloadpos],tempchr2,length);
438                 payloadpos += length;
439                 tempchr2 = &tempchr[1];
440         }
441         length = strlen(tempchr2);
442         if (length)
443         {
444                 if (payloadpos + length + 2 > 507)
445                         return -1;
446                 payload[payloadpos++] = length;
447                 memcpy(&payload[payloadpos],tempchr2,length);
448                 payloadpos += length;
449                 payload[payloadpos++] = 0;
450         }
451         if (payloadpos > 508)
452                 return -1;
453         length = htons(rr);
454         memcpy(&payload[payloadpos],&length,2);
455         length = htons(rr_class);
456         memcpy(&payload[payloadpos + 2],&length,2);
457         return payloadpos + 4;
458 }
459
460 /** Start lookup of an hostname to an IP address */
461 int DNS::GetIP(const char *name)
462 {
463         DNSHeader h;
464         int id;
465         int length;
466         
467         if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
468                 return -1;
469
470         DNSRequest* req = this->AddQuery(&h, id, name);
471
472         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
473                 return -1;
474
475         return id;
476 }
477
478 /** Start lookup of an hostname to an IPv6 address */
479 int DNS::GetIP6(const char *name)
480 {
481         DNSHeader h;
482         int id;
483         int length;
484
485         if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
486                 return -1;
487
488         DNSRequest* req = this->AddQuery(&h, id, name);
489
490         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
491                 return -1;
492
493         return id;
494 }
495
496 /** Start lookup of a cname to another name */
497 int DNS::GetCName(const char *alias)
498 {
499         DNSHeader h;
500         int id;
501         int length;
502
503         if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
504                 return -1;
505
506         DNSRequest* req = this->AddQuery(&h, id, alias);
507
508         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
509                 return -1;
510
511         return id;
512 }
513
514 /** Start lookup of an IP address to a hostname */
515 int DNS::GetName(const insp_inaddr *ip)
516 {
517         char query[128];
518         DNSHeader h;
519         int id;
520         int length;
521
522 #ifdef IPV6
523         unsigned char* c = (unsigned char*)&ip->s6_addr;
524         if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
525             c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
526             c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
527                 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
528         else
529                 DNS::MakeIP6Int(query, (in6_addr*)ip);
530 #else
531         unsigned char* c = (unsigned char*)&ip->s_addr;
532         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
533 #endif
534
535         if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
536                 return -1;
537
538         DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
539
540         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
541                 return -1;
542
543         return id;
544 }
545
546 /** Start lookup of an IP address to a hostname */
547 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
548 {
549         char query[128];
550         DNSHeader h;
551         int id;
552         int length;
553 #ifdef SUPPORT_IP6LINKS
554         if (fp == PROTOCOL_IPV6)
555         {
556                 in6_addr i;
557                 if (inet_pton(AF_INET6, ip, &i) > 0)
558                 {
559                         DNS::MakeIP6Int(query, &i);
560                 }
561                 else
562                         /* Invalid IP address */
563                         return -1;
564         }
565         else
566 #endif
567         {
568                 in_addr i;
569                 if (inet_aton(ip, &i))
570                 {
571                         unsigned char* c = (unsigned char*)&i.s_addr;
572                         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
573                 }
574                 else
575                         /* Invalid IP address */
576                         return -1;
577         }
578
579         if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
580                 return -1;
581
582         DNSRequest* req = this->AddQuery(&h, id, ip);
583
584         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
585                 return -1;
586
587         return id;
588 }
589
590 /** Build an ipv6 reverse domain from an in6_addr
591  */
592 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
593 {
594 #ifdef SUPPORT_IP6LINKS
595         const char* hex = "0123456789abcdef";
596         for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
597         {
598                 if (index % 2)
599                         /* low nibble */
600                         *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
601                 else
602                         /* high nibble */
603                         *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
604                 *query++ = '.'; /* Seperator */
605         }
606         strcpy(query,"ip6.arpa"); /* Suffix the string */
607 #else
608         *query = 0;
609 #endif
610 }
611
612 /** Return the next id which is ready, and the result attached to it */
613 DNSResult DNS::GetResult()
614 {
615         /* Fetch dns query response and decide where it belongs */
616         DNSHeader header;
617         DNSRequest *req;
618         unsigned char buffer[sizeof(DNSHeader)];
619         sockaddr* from = new sockaddr[2];
620 #ifdef IPV6
621         socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
622 #else
623         socklen_t x = sizeof(sockaddr_in);
624 #endif
625         const char* ipaddr_from;
626         unsigned short int port_from = 0;
627 #ifdef USING_IOCP
628         /** Dirty hack for IOCP UDP sockets **/
629         udp_overlap * ov = ((IOCPEngine*)ServerInstance->SE)->udp_ov;
630         memcpy(buffer, ov->udp_buffer, ov->udp_len);
631         memcpy(from, ov->udp_sockaddr, ov->udp_sockaddr_len);
632         int length = ov->udp_len;
633 #else
634         int length = recvfrom(this->GetFd(),(char*)buffer,sizeof(DNSHeader),0,from,&x);
635 #endif
636
637         /* Did we get the whole header? */
638         if (length < 12)
639         {
640                 /* Nope - something screwed up. */
641                 delete[] from;
642                 return DNSResult(-1,"",0,"");
643         }
644
645         /* Check wether the reply came from a different DNS
646          * server to the one we sent it to, or the source-port
647          * is not 53.
648          * A user could in theory still spoof dns packets anyway
649          * but this is less trivial than just sending garbage
650          * to the client, which is possible without this check.
651          *
652          * -- Thanks jilles for pointing this one out.
653          */
654 #ifdef IPV6
655         char nbuf[MAXBUF];
656         if (this->socketfamily == AF_INET6)
657         {
658                 ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
659                 port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
660         }
661         else
662 #endif
663         {
664                 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
665                 port_from = ntohs(((sockaddr_in*)from)->sin_port);
666         }
667
668         delete[] from;
669
670         /* We cant perform this security check if you're using 4in6.
671          * Tough luck to you, choose one or't other!
672          */
673         if (!ip6munge)
674         {
675                 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
676                 {
677                         return DNSResult(-1,"",0,"");
678                 }
679         }
680
681         /* Put the read header info into a header class */
682         DNS::FillHeader(&header,buffer,length - 12);
683
684         /* Get the id of this request.
685          * Its a 16 bit value stored in two char's,
686          * so we use logic shifts to create the value.
687          */
688         unsigned long this_id = header.id[1] + (header.id[0] << 8);
689
690         /* Do we have a pending request matching this id? */
691         if (!requests[this_id])
692         {
693                 /* Somehow we got a DNS response for a request we never made... */
694                 return DNSResult(-1,"",0,"");
695         }
696         else
697         {
698                 /* Remove the query from the list of pending queries */
699                 req = requests[this_id];
700                 requests[this_id] = NULL;
701         }
702
703         /* Inform the DNSRequest class that it has a result to be read.
704          * When its finished it will return a DNSInfo which is a pair of
705          * unsigned char* resource record data, and an error message.
706          */
707         DNSInfo data = req->ResultIsReady(header, length);
708         std::string resultstr;
709
710         /* Check if we got a result, if we didnt, its an error */
711         if (data.first == NULL)
712         {
713                 /* An error.
714                  * Mask the ID with the value of ERROR_MASK, so that
715                  * the dns_deal_with_classes() function knows that its
716                  * an error response and needs to be treated uniquely.
717                  * Put the error message in the second field.
718                  */
719                 std::string ro = req->orig;
720                 delete req;
721                 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
722         }
723         else
724         {
725                 unsigned long ttl = req->ttl;
726                 char formatted[128];
727
728                 /* Forward lookups come back as binary data. We must format them into ascii */
729                 switch (req->type)
730                 {
731                         case DNS_QUERY_A:
732                                 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
733                                 resultstr = formatted;
734                         break;
735
736                         case DNS_QUERY_AAAA:
737                         {
738                                 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
739                                                 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
740                                                 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
741                                                 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
742                                                 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
743                                                 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
744                                                 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
745                                                 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
746                                                 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
747                                 char* c = strstr(formatted,":0:");
748                                 if (c != NULL)
749                                 {
750                                         memmove(c+1,c+2,strlen(c+2) + 1);
751                                         c += 2;
752                                         while (memcmp(c,"0:",2) == 0)
753                                                 memmove(c,c+2,strlen(c+2) + 1);
754                                         if (memcmp(c,"0",2) == 0)
755                                                 *c = 0;
756                                         if (memcmp(formatted,"0::",3) == 0)
757                                                 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
758                                 }
759                                 resultstr = formatted;
760
761                                 /* Special case. Sending ::1 around between servers
762                                  * and to clients is dangerous, because the : on the
763                                  * start makes the client or server interpret the IP
764                                  * as the last parameter on the line with a value ":1".
765                                  */
766                                 if (*formatted == ':')
767                                         resultstr = "0" + resultstr;
768                         }
769                         break;
770
771                         case DNS_QUERY_CNAME:
772                                 /* Identical handling to PTR */
773
774                         case DNS_QUERY_PTR:
775                                 /* Reverse lookups just come back as char* */
776                                 resultstr = std::string((const char*)data.first);
777                         break;
778
779                         default:
780                         break;
781                         
782                 }
783
784                 /* Build the reply with the id and hostname/ip in it */
785                 std::string ro = req->orig;
786                 delete req;
787                 return DNSResult(this_id,resultstr,ttl,ro);
788         }
789 }
790
791 /** A result is ready, process it */
792 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
793 {
794         int i = 0;
795         int q = 0;
796         int curanswer, o;
797         ResourceRecord rr;
798         unsigned short ptr;
799
800         /* This is just to keep _FORTIFY_SOURCE happy */
801         rr.type = DNS_QUERY_NONE;
802         rr.rdlength = 0;
803         rr.ttl = 1;     /* GCC is a whiney bastard -- see the XXX below. */
804
805         if (!(header.flags1 & FLAGS_MASK_QR))
806                 return std::make_pair((unsigned char*)NULL,"Not a query result");
807
808         if (header.flags1 & FLAGS_MASK_OPCODE)
809                 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
810
811         if (header.flags2 & FLAGS_MASK_RCODE)
812                 return std::make_pair((unsigned char*)NULL,"Domain name not found");
813
814         if (header.ancount < 1)
815                 return std::make_pair((unsigned char*)NULL,"No resource records returned");
816
817         /* Subtract the length of the header from the length of the packet */
818         length -= 12;
819
820         while ((unsigned int)q < header.qdcount && i < length)
821         {
822                 if (header.payload[i] > 63)
823                 {
824                         i += 6;
825                         q++;
826                 }
827                 else
828                 {
829                         if (header.payload[i] == 0)
830                         {
831                                 q++;
832                                 i += 5;
833                         }
834                         else i += header.payload[i] + 1;
835                 }
836         }
837         curanswer = 0;
838         while ((unsigned)curanswer < header.ancount)
839         {
840                 q = 0;
841                 while (q == 0 && i < length)
842                 {
843                         if (header.payload[i] > 63)
844                         {
845                                 i += 2;
846                                 q = 1;
847                         }
848                         else
849                         {
850                                 if (header.payload[i] == 0)
851                                 {
852                                         i++;
853                                         q = 1;
854                                 }
855                                 else i += header.payload[i] + 1; /* skip length and label */
856                         }
857                 }
858                 if (length - i < 10)
859                         return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
860
861                 /* XXX: We actually initialise 'rr' here including its ttl field */
862                 DNS::FillResourceRecord(&rr,&header.payload[i]);
863                 i += 10;
864                 if (rr.type != this->type)
865                 {
866                         curanswer++;
867                         i += rr.rdlength;
868                         continue;
869                 }
870                 if (rr.rr_class != this->rr_class)
871                 {
872                         curanswer++;
873                         i += rr.rdlength;
874                         continue;
875                 }
876                 break;
877         }
878         if ((unsigned int)curanswer == header.ancount)
879                 return std::make_pair((unsigned char*)NULL,"No valid answers");
880
881         if (i + rr.rdlength > (unsigned int)length)
882                 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
883
884         if (rr.rdlength > 1023)
885                 return std::make_pair((unsigned char*)NULL,"Resource record too large");
886
887         this->ttl = rr.ttl;
888
889         switch (rr.type)
890         {
891                 case DNS_QUERY_CNAME:
892                         /* CNAME and PTR have the same processing code */
893                 case DNS_QUERY_PTR:
894                         o = 0;
895                         q = 0;
896                         while (q == 0 && i < length && o + 256 < 1023)
897                         {
898                                 if (header.payload[i] > 63)
899                                 {
900                                         memcpy(&ptr,&header.payload[i],2);
901                                         i = ntohs(ptr) - 0xC000 - 12;
902                                 }
903                                 else
904                                 {
905                                         if (header.payload[i] == 0)
906                                         {
907                                                 q = 1;
908                                         }
909                                         else
910                                         {
911                                                 res[o] = 0;
912                                                 if (o != 0)
913                                                         res[o++] = '.';
914                                                 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
915                                                 o += header.payload[i];
916                                                 i += header.payload[i] + 1;
917                                         }
918                                 }
919                         }
920                         res[o] = 0;
921                 break;
922                 case DNS_QUERY_AAAA:
923                         memcpy(res,&header.payload[i],rr.rdlength);
924                         res[rr.rdlength] = 0;
925                 break;
926                 case DNS_QUERY_A:
927                         memcpy(res,&header.payload[i],rr.rdlength);
928                         res[rr.rdlength] = 0;
929                 break;
930                 default:
931                         memcpy(res,&header.payload[i],rr.rdlength);
932                         res[rr.rdlength] = 0;
933                 break;
934         }
935         return std::make_pair(res,"No error");;
936 }
937
938 /** Close the master socket */
939 DNS::~DNS()
940 {
941         shutdown(this->GetFd(), 2);
942         close(this->GetFd());
943         ServerInstance->Timers->DelTimer(this->PruneTimer);
944         delete this->PruneTimer;
945 }
946
947 CachedQuery* DNS::GetCache(const std::string &source)
948 {
949         dnscache::iterator x = cache->find(source.c_str());
950         if (x != cache->end())
951                 return &(x->second);
952         else
953                 return NULL;
954 }
955                 
956 void DNS::DelCache(const std::string &source)
957 {
958         cache->erase(source.c_str());
959 }
960
961 void Resolver::TriggerCachedResult()
962 {
963         if (CQ)
964                 OnLookupComplete(CQ->data, time_left, true);
965 }
966
967 /** High level abstraction of dns used by application at large */
968 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
969 {
970         cached = false;
971
972         CQ = ServerInstance->Res->GetCache(source);
973         if (CQ)
974         {
975                 time_left = CQ->CalcTTLRemaining();
976                 if (!time_left)
977                 {
978                         ServerInstance->Res->DelCache(source);
979                 }
980                 else
981                 {
982                         cached = true;
983                         return;
984                 }
985         }
986
987         insp_inaddr binip;
988
989         switch (querytype)
990         {
991                 case DNS_QUERY_A:
992                         this->myid = ServerInstance->Res->GetIP(source.c_str());
993                 break;
994
995                 case DNS_QUERY_PTR:
996                         if (insp_aton(source.c_str(), &binip) > 0)
997                         {
998                                 /* Valid ip address */
999                                 this->myid = ServerInstance->Res->GetName(&binip);
1000                         }
1001                         else
1002                         {
1003                                 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
1004                                 throw ModuleException("Resolver: Bad IP address");
1005                                 return;
1006                         }
1007                 break;
1008
1009                 case DNS_QUERY_PTR4:
1010                         querytype = DNS_QUERY_PTR;
1011                         this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1012                 break;
1013
1014                 case DNS_QUERY_PTR6:
1015                         querytype = DNS_QUERY_PTR;
1016                         this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1017                 break;
1018
1019                 case DNS_QUERY_AAAA:
1020                         this->myid = ServerInstance->Res->GetIP6(source.c_str());
1021                 break;
1022
1023                 case DNS_QUERY_CNAME:
1024                         this->myid = ServerInstance->Res->GetCName(source.c_str());
1025                 break;
1026
1027                 default:
1028                         this->myid = -1;
1029                 break;
1030         }
1031         if (this->myid == -1)
1032         {
1033                 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1034                 throw ModuleException("Resolver: Couldnt get an id to make a request");
1035                 /* We shouldnt get here really */
1036                 return;
1037         }
1038 }
1039
1040 /** Called when an error occurs */
1041 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1042 {
1043         /* Nothing in here */
1044 }
1045
1046 /** Destroy a resolver */
1047 Resolver::~Resolver()
1048 {
1049         /* Nothing here (yet) either */
1050 }
1051
1052 /** Get the request id associated with this class */
1053 int Resolver::GetId()
1054 {
1055         return this->myid;
1056 }
1057
1058 Module* Resolver::GetCreator()
1059 {
1060         return this->Creator;
1061 }
1062
1063 /** Process a socket read event */
1064 void DNS::HandleEvent(EventType et, int errornum)
1065 {
1066         /* Fetch the id and result of the next available packet */
1067         DNSResult res = this->GetResult();
1068         /* Is there a usable request id? */
1069         if (res.id != -1)
1070         {
1071                 /* Its an error reply */
1072                 if (res.id & ERROR_MASK)
1073                 {
1074                         /* Mask off the error bit */
1075                         res.id -= ERROR_MASK;
1076                         /* Marshall the error to the correct class */
1077                         if (Classes[res.id])
1078                         {
1079                                 if (ServerInstance && ServerInstance->stats)
1080                                         ServerInstance->stats->statsDnsBad++;
1081                                 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1082                                 delete Classes[res.id];
1083                                 Classes[res.id] = NULL;
1084                         }
1085                 }
1086                 else
1087                 {
1088                         /* It is a non-error result, marshall the result to the correct class */
1089                         if (Classes[res.id])
1090                         {
1091                                 if (ServerInstance && ServerInstance->stats)
1092                                         ServerInstance->stats->statsDnsGood++;
1093
1094                                 if (!this->GetCache(res.original.c_str()))
1095                                         this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1096
1097                                 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1098                                 delete Classes[res.id];
1099                                 Classes[res.id] = NULL;
1100                         }
1101                 }
1102
1103                 if (ServerInstance && ServerInstance->stats)
1104                         ServerInstance->stats->statsDns++;
1105         }
1106 }
1107
1108 /** Add a derived Resolver to the working set */
1109 bool DNS::AddResolverClass(Resolver* r)
1110 {
1111         /* Check the pointers validity and the id's validity */
1112         if ((r) && (r->GetId() > -1))
1113         {
1114                 /* Check the slot isnt already occupied -
1115                  * This should NEVER happen unless we have
1116                  * a severely broken DNS server somewhere
1117                  */
1118                 if (!Classes[r->GetId()])
1119                 {
1120                         /* Set up the pointer to the class */
1121                         Classes[r->GetId()] = r;
1122                         return true;
1123                 }
1124                 else
1125                         /* Duplicate id */
1126                         return false;
1127         }
1128         else
1129         {
1130                 /* Pointer or id not valid.
1131                  * Free the item and return
1132                  */
1133                 if (r)
1134                         delete r;
1135
1136                 return false;
1137         }
1138 }
1139
1140 void DNS::CleanResolvers(Module* module)
1141 {
1142         for (int i = 0; i < MAX_REQUEST_ID; i++)
1143         {
1144                 if (Classes[i])
1145                 {
1146                         if (Classes[i]->GetCreator() == module)
1147                         {
1148                                 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1149                                 delete Classes[i];
1150                                 Classes[i] = NULL;
1151                         }
1152                 }
1153         }
1154 }
1155
1156 /** Generate pseudo-random number */
1157 unsigned long DNS::PRNG()
1158 {
1159 #ifndef WIN32
1160         unsigned long val = 0;
1161         timeval n;
1162         serverstats* s = ServerInstance->stats;
1163         gettimeofday(&n,NULL);
1164         val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1165         val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1166         val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();
1167         return val;
1168 #else
1169         unsigned long val = 0;
1170         serverstats* s = ServerInstance->stats;
1171         val = (time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId() ^ (this->currid++)) ^ s->statsAccept + time(NULL);
1172         val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1173         val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv);
1174         return val;
1175 #endif
1176 }
1177