+ /* Fetch dns query response and decide where it belongs */
+ DNSHeader header;
+ DNSRequest *req;
+ unsigned char buffer[sizeof(DNSHeader)];
+ sockaddr from;
+ socklen_t x = sizeof(from);
+ const char* ipaddr_from = "";
+ unsigned short int port_from = 0;
+
+ int length = recvfrom(this->GetFd(),buffer,sizeof(DNSHeader),0,&from,&x);
+
+ if (length < 0)
+ ServerInstance->Log(DEBUG,"Error in recvfrom()! (%s)",strerror(errno));
+
+ /* Did we get the whole header? */
+ if (length < 12)
+ {
+ /* Nope - something screwed up. */
+ ServerInstance->Log(DEBUG,"Whole header not read!");
+ return std::make_pair(-1,"");
+ }
+
+ /* Check wether the reply came from a different DNS
+ * server to the one we sent it to, or the source-port
+ * is not 53.
+ * A user could in theory still spoof dns packets anyway
+ * but this is less trivial than just sending garbage
+ * to the client, which is possible without this check.
+ *
+ * -- Thanks jilles for pointing this one out.
+ */
+#ifdef IPV6
+ ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
+ port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
+#else
+ ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
+ port_from = ntohs(((sockaddr_in*)&from)->sin_port);
+#endif
+
+ /* We cant perform this security check if you're using 4in6.
+ * Tough luck to you, choose one or't other!
+ */
+ if (!ip6munge)
+ {
+ if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
+ {
+ ServerInstance->Log(DEBUG,"port %d is not 53, or %s is not %s",port_from, ipaddr_from, ServerInstance->Config->DNSServer);
+ return std::make_pair(-1,"");
+ }
+ }
+
+ /* Put the read header info into a header class */
+ DNS::FillHeader(&header,buffer,length - 12);
+
+ /* Get the id of this request.
+ * Its a 16 bit value stored in two char's,
+ * so we use logic shifts to create the value.
+ */
+ unsigned long this_id = header.id[1] + (header.id[0] << 8);
+
+ /* Do we have a pending request matching this id? */
+ requestlist_iter n_iter = requests.find(this_id);
+ if (n_iter == requests.end())
+ {
+ /* Somehow we got a DNS response for a request we never made... */
+ ServerInstance->Log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",this->GetFd(),this_id);
+ return std::make_pair(-1,"");
+ }
+ else
+ {
+ /* Remove the query from the list of pending queries */
+ req = (DNSRequest*)n_iter->second;
+ requests.erase(n_iter);
+ }
+
+ /* Inform the DNSRequest class that it has a result to be read.
+ * When its finished it will return a DNSInfo which is a pair of
+ * unsigned char* resource record data, and an error message.
+ */
+ DNSInfo data = req->ResultIsReady(header, length);
+ std::string resultstr;
+
+ /* Check if we got a result, if we didnt, its an error */
+ if (data.first == NULL)
+ {
+ /* An error.
+ * Mask the ID with the value of ERROR_MASK, so that
+ * the dns_deal_with_classes() function knows that its
+ * an error response and needs to be treated uniquely.
+ * Put the error message in the second field.
+ */
+ delete req;
+ return std::make_pair(this_id | ERROR_MASK, data.second);
+ }
+ else
+ {
+ char formatted[128];
+
+ /* Forward lookups come back as binary data. We must format them into ascii */
+ switch (req->type)
+ {
+ case DNS_QUERY_A:
+ snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
+ resultstr = formatted;
+ break;
+
+ case DNS_QUERY_AAAA:
+ {
+ snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
+ (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
+ (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
+ (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
+ (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
+ (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
+ (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
+ (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
+ (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
+ char* c = strstr(formatted,":0:");
+ if (c != NULL)
+ {
+ memmove(c+1,c+2,strlen(c+2) + 1);
+ c += 2;
+ while (memcmp(c,"0:",2) == 0)
+ memmove(c,c+2,strlen(c+2) + 1);
+ if (memcmp(c,"0",2) == 0)
+ *c = 0;
+ if (memcmp(formatted,"0::",3) == 0)
+ memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
+ }
+ resultstr = formatted;
+
+ /* Special case. Sending ::1 around between servers
+ * and to clients is dangerous, because the : on the
+ * start makes the client or server interpret the IP
+ * as the last parameter on the line with a value ":1".
+ */
+ if (*formatted == ':')
+ resultstr = "0" + resultstr;
+ }
+ break;
+
+ case DNS_QUERY_CNAME:
+ /* Identical handling to PTR */
+
+ case DNS_QUERY_PTR:
+ /* Reverse lookups just come back as char* */
+ resultstr = std::string((const char*)data.first);
+ break;
+
+ default:
+ ServerInstance->Log(DEBUG,"WARNING: Somehow we made a request for a DNS_QUERY_PTR4 or DNS_QUERY_PTR6, but these arent real rr types!");
+ break;
+
+ }
+
+ /* Build the reply with the id and hostname/ip in it */
+ delete req;
+ return std::make_pair(this_id,resultstr);
+ }