Move DNS stuff to single new file rsdnsutils.cc.

This commit is contained in:
Phenom 2021-11-03 19:41:57 +01:00
parent 81d2ddd7c7
commit 7755f85c4c
4 changed files with 361 additions and 339 deletions

View File

@ -635,6 +635,7 @@ SOURCES += util/folderiterator.cc \
util/rsdiscspace.cc \
util/rsnet.cc \
util/rsnet_ss.cc \
util/rsdnsutils.cc \
util/extaddrfinder.cc \
util/dnsresolver.cc \
util/rsprint.cc \

View File

@ -0,0 +1,360 @@
/*******************************************************************************
* libretroshare/src/util: rsdnsutils.cc *
* *
* libretroshare: retroshare core library *
* *
* Copyright 2021 Phenom <retrosharephenom@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
//#define DEBUG_SPEC_DNS 1
#include "util/rsnet.h"
#include "util/rsdebug.h"
#include "util/rsthreads.h"
#include "util/rsstring.h"
#ifdef WINDOWS_SYS
#else
#include <netdb.h>
#endif
//https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
constexpr uint16_t DNSC_IN = 1; //Internet (IN)
//https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
constexpr uint16_t DNST_A = 1; //Ipv4 address
constexpr uint16_t DNST_AAAA =28; //Ipv6 address
///Need to pack as we use sizeof them. (avoid padding)
#pragma pack(1)
//DNS header structure
struct DNS_HEADER
{
unsigned short id; // identification number
//Header flags https://www.bind9.net/dns-header-flags
unsigned char rd :1; //bit 07 Recursion Desired, indicates if the client means a recursive query
unsigned char tc :1; //bit 06 TrunCation, indicates that this message was truncated due to excessive length
unsigned char aa :1; //bit 05 Authoritative Answer, in a response, indicates if the DNS server is authoritative for the queried hostname
unsigned char opcode :4;//bit 01-04 The type can be QUERY (standard query, 0), IQUERY (inverse query, 1), or STATUS (server status request, 2)
unsigned char qr :1; //bit 00 Indicates if the message is a query (0) or a reply (1)
unsigned char rcode :4; //bit 12-15 Response Code can be NOERROR (0), FORMERR (1, Format error), SERVFAIL (2), NXDOMAIN (3, Nonexistent domain), etc.
unsigned char cd :1; //bit 11 Checking Disabled [RFC 4035][RFC 6840][RFC Errata 4927] used by DNSSEC
unsigned char ad :1; //bit 10 Authentic Data [RFC 4035][RFC 6840][RFC Errata 4924] used by DNSSEC
unsigned char z :1; //bit 09 Zero, reserved for future use
unsigned char ra :1; //bit 08 Recursion Available [RFC 1035] in a response, indicates if the replying DNS server supports recursion
unsigned short q_count; // number of question entries
unsigned short ans_count; // number of answer entries
unsigned short auth_count; // number of authority resource records entries
unsigned short add_count; // number of additional resource record entries
};
//// OpCode text https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5
//static const char *opcodetext[] = { "QUERY", "IQUERY", "STATUS",
// "RESERVED3", "NOTIFY", "UPDATE",
// "STATEFUL", "RESERVED7", "RESERVED8",
// "RESERVED9", "RESERVED10", "RESERVED11",
// "RESERVED12", "RESERVED13", "RESERVED14",
// "RESERVED15" };
//// RCode text https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
//static const char *rcodetext[] = { "NOERROR", "FORMERR", "SERVFAIL",
// "NXDOMAIN", "NOTIMP", "REFUSED",
// "YXDOMAIN", "YXRRSET", "NXRRSET",
// "NOTAUTH", "NOTZONE", "DSOTYPENI",
// "RESERVED12", "RESERVED13", "RESERVED14",
// "RESERVED15", "BADVERS", "BADKEY",
// "BADTIME", "BADMODE", "BADNAME",
// "BADALG", "BADTRUNC", "BADCOOKIE"};
//Constant sized fields of query structure
//https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
struct QUESTION
{
unsigned short qtype; //https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
unsigned short qclass; //https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
};
//Constant sized fields of the resource record structure
struct RR_DATA
{
unsigned short rtype; //https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
unsigned short rclass; //https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
unsigned int rttl; //Time To Live is the number of seconds left before the information expires. (32bits integer, so maximum 140 years)
unsigned short data_len;//Lenght of following data
};
#pragma pack()
bool rsGetHostByNameSpecDNS(const std::string& servername, const std::string& hostname, std::string& returned_addr, int timeout_s /*= -1*/)
{
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<<" servername="<< servername << " hostname=" << hostname << std::endl;
#endif
if (strlen(servername.c_str()) > 256)
{
RsErr()<<__PRETTY_FUNCTION__<<": servername is too long > 256 chars:"<<servername<<std::endl;
return false;
}
if (strlen(hostname.c_str()) > 256)
{
RsErr()<<__PRETTY_FUNCTION__<<": hostname is too long > 256 chars:"<<hostname<<std::endl;
return false;
}
bool isIPV4 = false;
in_addr dns_server_4; in6_addr dns_server_6;
if (inet_pton(AF_INET, servername.c_str(), &dns_server_4))
isIPV4 = true;
else if (inet_pton(AF_INET6, servername.c_str(), &dns_server_6))
isIPV4 = false;
else if (rsGetHostByName(servername, dns_server_4))
isIPV4 = true;
else
{
RsErr()<<__PRETTY_FUNCTION__<<": servername is on an unknow format: "<<servername<<std::endl;
return false;
}
unsigned char buf[65536];
struct sockaddr_in dest4;struct sockaddr_in6 dest6;
if (isIPV4)
{
dest4.sin_family = AF_INET;
dest4.sin_port = htons(53);
dest4.sin_addr.s_addr = dns_server_4.s_addr;
} else {
dest6.sin6_family = AF_INET6;
dest6.sin6_port = htons(53);
dest6.sin6_flowinfo = 0;
dest6.sin6_addr = dns_server_6;
dest6.sin6_scope_id = 0;
}
//Set the DNS structure to standard queries
struct DNS_HEADER* dns = (struct DNS_HEADER *)&buf;
dns->id = static_cast<unsigned short>(htons(getpid())); //Transaction Id
//dns flags = 0x0100 Standard Query
dns->qr = 0; //Query/Response: Message is a query
dns->opcode = 0; //OpCode: Standard query
dns->aa = 0; //Authoritative: Server is not an authority for domain
dns->tc = 0; //TrunCated: Message is not truncated
dns->rd = 1; //Recursion Desired: Do query recursively
dns->ra = 0; //Recursion Available: Server cannot do recursive queries
dns->z = 0; //Z: reserved
dns->ad = 0; //Authentic Data: Answer/authority portion was not authenticated by the server
dns->cd = 0; //Checking Disabled: Unacceptable
dns->rcode = 0; //Response Code: No error
dns->q_count = htons(1); //1 Question
dns->ans_count = 0; //0 Answer
dns->auth_count = 0; //0 Authority RRs
dns->add_count = 0; //0 Additional RRs
size_t curSendSize = sizeof(struct DNS_HEADER);
//Point to the query server name portion
unsigned char* qname =static_cast<unsigned char*>(&buf[curSendSize]);
//First byte is Label Type: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-10
qname[0] = 0x04; //One Label with Normal label lower 6 bits is the length of the label
memcpy(&qname[1],hostname.c_str(),strlen(hostname.c_str()));
size_t qnameSize = strlen((const char*)qname);
// Format Hostname like www.google.com to 3www6google3com
{
size_t last = qnameSize;
for(size_t i = qnameSize-1 ; i > 0 ; i--)
if(qname[i]=='.')
{
qname[i]=last-i-1;
last = i;
}
}
curSendSize += qnameSize +1; //With \0 terminator
//Point to the query constant portion
struct QUESTION* qinfo =(struct QUESTION*)&buf[curSendSize];
qinfo->qtype = htons(isIPV4 ? DNST_A : DNST_AAAA); //Type: A / AAAA(Host Address)
qinfo->qclass = htons(DNSC_IN); //Class: IN
curSendSize += sizeof(struct QUESTION);
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<< " Sending Packet: " << std::endl << hexDump(buf, curSendSize) << std::endl;
#endif
int s = isIPV4 ? socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP)
: socket(AF_INET6 , SOCK_DGRAM , IPPROTO_UDP) ; //UDP packet for DNS queries
if (timeout_s > -1)
{
#ifdef WINDOWS_SYS
DWORD timeout = timeout_s * 1000;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
#else
struct timeval tv;
tv.tv_sec = timeout_s;
tv.tv_usec = 0;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
#endif
}
ssize_t send_size = sendto(s, (char*)buf, curSendSize, 0
,isIPV4 ? (struct sockaddr*)&dest4
: (struct sockaddr*)&dest6
,isIPV4 ? sizeof(dest4)
: sizeof(dest6)
);
if( send_size < 0)
{
RsErr()<<__PRETTY_FUNCTION__<<": Send Failed with size = " << send_size << std::endl;
return false;
}
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<< " Waiting answer..." << std::endl;
#endif
//****************************************************************************************//
//--- Receive the answer ---//
//****************************************************************************************//
socklen_t dest_size = static_cast<socklen_t>(sizeof dest4);
ssize_t rec_size=recvfrom(s,(char*)buf , 65536 , 0 , (struct sockaddr*)&dest4 , &dest_size );
if(rec_size <= 0)
{
RsErr()<<__PRETTY_FUNCTION__<<": Receive Failed"<<std::endl;
return false;
}
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<< " Received: " << hexDump(buf, rec_size) << std::endl;
#endif
if (rec_size< static_cast<ssize_t>(sizeof(struct DNS_HEADER)) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received too small to get DNSHeader."<<std::endl;
return false;
}
#ifdef DEBUG_SPEC_DNS
//Point to the header portion
dns = (struct DNS_HEADER*) buf;
RsDbg()<<__PRETTY_FUNCTION__<<" The response contains : " << std::endl
<<ntohs(dns->q_count) << " Questions." << std::endl
<<ntohs(dns->ans_count) << " Answers." << std::endl
<<ntohs(dns->auth_count) << " Authoritative Servers." << std::endl
<<ntohs(dns->add_count) << " Additional records." << std::endl;
#endif
size_t curRecSize = sizeof(struct DNS_HEADER);
if (rec_size< static_cast<ssize_t>(curRecSize + 1 + sizeof(struct QUESTION)) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received too small to get Question return."<<std::endl;
return false;
}
//Point to the query portion
unsigned char* qnameRecv =static_cast<unsigned char*>(&buf[curRecSize]);
if (memcmp(qname,qnameRecv,qnameSize + 1 + sizeof(struct QUESTION)) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received different from that sent."<<std::endl;
return false;
}
curRecSize += qnameSize + 1 + sizeof(struct QUESTION);
if (rec_size< static_cast<ssize_t>(curRecSize + 2) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received too small to get Answer return."<<std::endl;
return false;
}
//Point to the Answer portion
unsigned char* reader = &buf[curRecSize];
size_t rLabelSize=0;
if((reader[0]&0xC0) == 0)
{
//Normal label lower 6 bits is the length of the label
rLabelSize=(reader[0]&~(0xC0)*256) + reader[1];
}
else if ((reader[0]&0xC0) == 0xC0)
{
//Compressed label the lower 6 bits and the 8 bits from next octet form a pointer to the compression target.
//Don't need to read it, maybe the same as in Query
rLabelSize=0;
}
else
{
RsErr()<<__PRETTY_FUNCTION__<<": Answer received with unmanaged label format."<<std::endl;
return false;
}
curRecSize += 2 + rLabelSize;
if (rec_size< static_cast<ssize_t>(curRecSize + sizeof(struct RR_DATA)) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received too small to get Data return."<<std::endl;
return false;
}
//Point to the query portion
struct RR_DATA* rec_data = (struct RR_DATA *)&buf[curRecSize];
if (rec_data->rtype!=qinfo->qtype)
{
RsErr()<<__PRETTY_FUNCTION__<<": Answer's type received different from query sent."<<std::endl;
return false;
}
if (rec_data->rclass!=qinfo->qclass)
{
RsErr()<<__PRETTY_FUNCTION__<<": Answer's class received different from query sent."<<std::endl;
return false;
}
curRecSize += sizeof(struct RR_DATA);
if (rec_size< static_cast<ssize_t>(curRecSize + ntohs(rec_data->data_len)) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received too small to get Full Data return."<<std::endl;
return false;
}
//Retrieve Address
if(ntohs(rec_data->data_len)==4)
{
if (isIPV4)
{
in_addr ipv4Add;
ipv4Add.s_addr=*(in_addr_t*)&buf[curRecSize];
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<< " Retrieve address: " << rs_inet_ntoa(ipv4Add) << std::endl;
#endif
returned_addr = rs_inet_ntoa(ipv4Add);
return true;
}
}
else if(ntohs(rec_data->data_len)==16)
{
if (!isIPV4)
{
in6_addr ipv6Add;
ipv6Add =*(in6_addr*)&buf[curRecSize];
struct sockaddr_storage ss;
sockaddr_storage_clear(ss);
sockaddr_in6 addr_ipv6;
addr_ipv6.sin6_addr = ipv6Add;
sockaddr_storage_setipv6(ss,&addr_ipv6);
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<< " Retrieve address: " << sockaddr_storage_iptostring(ss).c_str() << std::endl;
#endif
returned_addr = sockaddr_storage_iptostring(ss);
return true;
}
}
RsErr()<<__PRETTY_FUNCTION__<< " Retrieve unmanaged data size=" << ntohs(rec_data->data_len) << std::endl;
return false;
}

View File

@ -21,9 +21,6 @@
* *
*******************************************************************************/
//#define DEBUG_SPEC_DNS 1
#include "util/rsdebug.h"
#include "util/rsnet.h"
#include "util/rsthreads.h"
#include "util/rsstring.h"
@ -43,72 +40,6 @@
#define BIG_ENDIAN 4321
#endif
//https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
constexpr uint16_t DNSC_IN = 1; //Internet (IN)
//https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
constexpr uint16_t DNST_A = 1; //Ipv4 address
constexpr uint16_t DNST_AAAA =28; //Ipv6 address
///Need to pack as we use sizeof them. (avoid padding)
#pragma pack(1)
//DNS header structure
struct DNS_HEADER
{
unsigned short id; // identification number
//Header flags https://www.bind9.net/dns-header-flags
unsigned char rd :1; //bit 07 Recursion Desired, indicates if the client means a recursive query
unsigned char tc :1; //bit 06 TrunCation, indicates that this message was truncated due to excessive length
unsigned char aa :1; //bit 05 Authoritative Answer, in a response, indicates if the DNS server is authoritative for the queried hostname
unsigned char opcode :4;//bit 01-04 The type can be QUERY (standard query, 0), IQUERY (inverse query, 1), or STATUS (server status request, 2)
unsigned char qr :1; //bit 00 Indicates if the message is a query (0) or a reply (1)
unsigned char rcode :4; //bit 12-15 Response Code can be NOERROR (0), FORMERR (1, Format error), SERVFAIL (2), NXDOMAIN (3, Nonexistent domain), etc.
unsigned char cd :1; //bit 11 Checking Disabled [RFC 4035][RFC 6840][RFC Errata 4927] used by DNSSEC
unsigned char ad :1; //bit 10 Authentic Data [RFC 4035][RFC 6840][RFC Errata 4924] used by DNSSEC
unsigned char z :1; //bit 09 Zero, reserved for future use
unsigned char ra :1; //bit 08 Recursion Available [RFC 1035] in a response, indicates if the replying DNS server supports recursion
unsigned short q_count; // number of question entries
unsigned short ans_count; // number of answer entries
unsigned short auth_count; // number of authority resource records entries
unsigned short add_count; // number of additional resource record entries
};
//// OpCode text https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5
//static const char *opcodetext[] = { "QUERY", "IQUERY", "STATUS",
// "RESERVED3", "NOTIFY", "UPDATE",
// "STATEFUL", "RESERVED7", "RESERVED8",
// "RESERVED9", "RESERVED10", "RESERVED11",
// "RESERVED12", "RESERVED13", "RESERVED14",
// "RESERVED15" };
//// RCode text https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
//static const char *rcodetext[] = { "NOERROR", "FORMERR", "SERVFAIL",
// "NXDOMAIN", "NOTIMP", "REFUSED",
// "YXDOMAIN", "YXRRSET", "NXRRSET",
// "NOTAUTH", "NOTZONE", "DSOTYPENI",
// "RESERVED12", "RESERVED13", "RESERVED14",
// "RESERVED15", "BADVERS", "BADKEY",
// "BADTIME", "BADMODE", "BADNAME",
// "BADALG", "BADTRUNC", "BADCOOKIE"};
//Constant sized fields of query structure
//https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
struct QUESTION
{
unsigned short qtype; //https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
unsigned short qclass; //https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
};
//Constant sized fields of the resource record structure
struct RR_DATA
{
unsigned short rtype; //https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
unsigned short rclass; //https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
unsigned int rttl; //Time To Live is the number of seconds left before the information expires. (32bits integer, so maximum 140 years)
unsigned short data_len;//Lenght of following data
};
#pragma pack()
#ifndef ntohll
uint64_t ntohll(uint64_t x)
{
@ -168,260 +99,6 @@ bool rsGetHostByName(const std::string& hostname, in_addr& returned_addr)
return ok;
}
bool rsGetHostByNameSpecDNS(const std::string& servername, const std::string& hostname, std::string& returned_addr, int timeout_s /*= -1*/)
{
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<<" servername="<< servername << " hostname=" << hostname << std::endl;
#endif
if (strlen(servername.c_str()) > 256)
{
RsErr()<<__PRETTY_FUNCTION__<<": servername is too long > 256 chars:"<<servername<<std::endl;
return false;
}
if (strlen(hostname.c_str()) > 256)
{
RsErr()<<__PRETTY_FUNCTION__<<": hostname is too long > 256 chars:"<<hostname<<std::endl;
return false;
}
bool isIPV4 = false;
in_addr dns_server_4; in6_addr dns_server_6;
if (inet_pton(AF_INET, servername.c_str(), &dns_server_4))
isIPV4 = true;
else if (inet_pton(AF_INET6, servername.c_str(), &dns_server_6))
isIPV4 = false;
else if (rsGetHostByName(servername, dns_server_4))
isIPV4 = true;
else
{
RsErr()<<__PRETTY_FUNCTION__<<": servername is on an unknow format: "<<servername<<std::endl;
return false;
}
unsigned char buf[65536];
struct sockaddr_in dest4;struct sockaddr_in6 dest6;
if (isIPV4)
{
dest4.sin_family = AF_INET;
dest4.sin_port = htons(53);
dest4.sin_addr.s_addr = dns_server_4.s_addr;
} else {
dest6.sin6_family = AF_INET6;
dest6.sin6_port = htons(53);
dest6.sin6_flowinfo = 0;
dest6.sin6_addr = dns_server_6;
dest6.sin6_scope_id = 0;
}
//Set the DNS structure to standard queries
struct DNS_HEADER* dns = (struct DNS_HEADER *)&buf;
dns->id = static_cast<unsigned short>(htons(getpid())); //Transaction Id
//dns flags = 0x0100 Standard Query
dns->qr = 0; //Query/Response: Message is a query
dns->opcode = 0; //OpCode: Standard query
dns->aa = 0; //Authoritative: Server is not an authority for domain
dns->tc = 0; //TrunCated: Message is not truncated
dns->rd = 1; //Recursion Desired: Do query recursively
dns->ra = 0; //Recursion Available: Server cannot do recursive queries
dns->z = 0; //Z: reserved
dns->ad = 0; //Authentic Data: Answer/authority portion was not authenticated by the server
dns->cd = 0; //Checking Disabled: Unacceptable
dns->rcode = 0; //Response Code: No error
dns->q_count = htons(1); //1 Question
dns->ans_count = 0; //0 Answer
dns->auth_count = 0; //0 Authority RRs
dns->add_count = 0; //0 Additional RRs
size_t curSendSize = sizeof(struct DNS_HEADER);
//Point to the query server name portion
unsigned char* qname =static_cast<unsigned char*>(&buf[curSendSize]);
//First byte is Label Type: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-10
qname[0] = 0x04; //One Label with Normal label lower 6 bits is the length of the label
memcpy(&qname[1],hostname.c_str(),strlen(hostname.c_str()));
size_t qnameSize = strlen((const char*)qname);
// Format Hostname like www.google.com to 3www6google3com
{
size_t last = qnameSize;
for(size_t i = qnameSize-1 ; i > 0 ; i--)
if(qname[i]=='.')
{
qname[i]=last-i-1;
last = i;
}
}
curSendSize += qnameSize +1; //With \0 terminator
//Point to the query constant portion
struct QUESTION* qinfo =(struct QUESTION*)&buf[curSendSize];
qinfo->qtype = htons(isIPV4 ? DNST_A : DNST_AAAA); //Type: A / AAAA(Host Address)
qinfo->qclass = htons(DNSC_IN); //Class: IN
curSendSize += sizeof(struct QUESTION);
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<< " Sending Packet: " << std::endl << hexDump(buf, curSendSize) << std::endl;
#endif
int s = isIPV4 ? socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP)
: socket(AF_INET6 , SOCK_DGRAM , IPPROTO_UDP) ; //UDP packet for DNS queries
if (timeout_s > -1)
{
#ifdef WINDOWS_SYS
DWORD timeout = timeout_s * 1000;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
#else
struct timeval tv;
tv.tv_sec = timeout_s;
tv.tv_usec = 0;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
#endif
}
ssize_t send_size = sendto(s, (char*)buf, curSendSize, 0
,isIPV4 ? (struct sockaddr*)&dest4
: (struct sockaddr*)&dest6
,isIPV4 ? sizeof(dest4)
: sizeof(dest6)
);
if( send_size < 0)
{
RsErr()<<__PRETTY_FUNCTION__<<": Send Failed with size = " << send_size << std::endl;
return false;
}
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<< " Waiting answer..." << std::endl;
#endif
//****************************************************************************************//
//--- Receive the answer ---//
//****************************************************************************************//
socklen_t dest_size = static_cast<socklen_t>(sizeof dest4);
ssize_t rec_size=recvfrom(s,(char*)buf , 65536 , 0 , (struct sockaddr*)&dest4 , &dest_size );
if(rec_size <= 0)
{
RsErr()<<__PRETTY_FUNCTION__<<": Receive Failed"<<std::endl;
return false;
}
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<< " Received: " << hexDump(buf, rec_size) << std::endl;
#endif
if (rec_size< static_cast<ssize_t>(sizeof(struct DNS_HEADER)) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received too small to get DNSHeader."<<std::endl;
return false;
}
#ifdef DEBUG_SPEC_DNS
//Point to the header portion
dns = (struct DNS_HEADER*) buf;
RsDbg()<<__PRETTY_FUNCTION__<<" The response contains : " << std::endl
<<ntohs(dns->q_count) << " Questions." << std::endl
<<ntohs(dns->ans_count) << " Answers." << std::endl
<<ntohs(dns->auth_count) << " Authoritative Servers." << std::endl
<<ntohs(dns->add_count) << " Additional records." << std::endl;
#endif
size_t curRecSize = sizeof(struct DNS_HEADER);
if (rec_size< static_cast<ssize_t>(curRecSize + 1 + sizeof(struct QUESTION)) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received too small to get Question return."<<std::endl;
return false;
}
//Point to the query portion
unsigned char* qnameRecv =static_cast<unsigned char*>(&buf[curRecSize]);
if (memcmp(qname,qnameRecv,qnameSize + 1 + sizeof(struct QUESTION)) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received different from that sent."<<std::endl;
return false;
}
curRecSize += qnameSize + 1 + sizeof(struct QUESTION);
if (rec_size< static_cast<ssize_t>(curRecSize + 2) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received too small to get Answer return."<<std::endl;
return false;
}
//Point to the Answer portion
unsigned char* reader = &buf[curRecSize];
size_t rLabelSize=0;
if((reader[0]&0xC0) == 0)
{
//Normal label lower 6 bits is the length of the label
rLabelSize=(reader[0]&~(0xC0)*256) + reader[1];
}
else if ((reader[0]&0xC0) == 0xC0)
{
//Compressed label the lower 6 bits and the 8 bits from next octet form a pointer to the compression target.
//Don't need to read it, maybe the same as in Query
rLabelSize=0;
}
else
{
RsErr()<<__PRETTY_FUNCTION__<<": Answer received with unmanaged label format."<<std::endl;
return false;
}
curRecSize += 2 + rLabelSize;
if (rec_size< static_cast<ssize_t>(curRecSize + sizeof(struct RR_DATA)) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received too small to get Data return."<<std::endl;
return false;
}
//Point to the query portion
struct RR_DATA* rec_data = (struct RR_DATA *)&buf[curRecSize];
if (rec_data->rtype!=qinfo->qtype)
{
RsErr()<<__PRETTY_FUNCTION__<<": Answer's type received different from query sent."<<std::endl;
return false;
}
if (rec_data->rclass!=qinfo->qclass)
{
RsErr()<<__PRETTY_FUNCTION__<<": Answer's class received different from query sent."<<std::endl;
return false;
}
curRecSize += sizeof(struct RR_DATA);
if (rec_size< static_cast<ssize_t>(curRecSize + ntohs(rec_data->data_len)) )
{
RsErr()<<__PRETTY_FUNCTION__<<": Request received too small to get Full Data return."<<std::endl;
return false;
}
//Retrieve Address
if(ntohs(rec_data->data_len)==4)
{
if (isIPV4)
{
in_addr ipv4Add;
ipv4Add.s_addr=*(in_addr_t*)&buf[curRecSize];
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<< " Retrieve address: " << rs_inet_ntoa(ipv4Add) << std::endl;
#endif
returned_addr = rs_inet_ntoa(ipv4Add);
return true;
}
}
else if(ntohs(rec_data->data_len)==16)
{
if (!isIPV4)
{
in6_addr ipv6Add;
ipv6Add =*(in6_addr*)&buf[curRecSize];
#ifdef DEBUG_SPEC_DNS
RsDbg()<<__PRETTY_FUNCTION__<< " Retrieve address: " << rs_inet_ntoa(ipv6Add) << std::endl;
#endif
returned_addr = rs_inet_ntoa(ipv6Add);
return true;
}
}
RsErr()<<__PRETTY_FUNCTION__<< " Retrieve unmanaged data size=" << ntohs(rec_data->data_len) << std::endl;
return false;
}
bool isValidNet(const struct in_addr *addr)
{
// invalid address.
@ -497,18 +174,3 @@ std::string rs_inet_ntoa(struct in_addr in)
rs_sprintf(str, "%u.%u.%u.%u", (int) bytes[0], (int) bytes[1], (int) bytes[2], (int) bytes[3]);
return str;
}
std::string rs_inet_ntoa(const in6_addr& in)
{
std::string str;
rs_sprintf(str, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
(int)in.s6_addr[0] , (int)in.s6_addr[1],
(int)in.s6_addr[2] , (int)in.s6_addr[3],
(int)in.s6_addr[4] , (int)in.s6_addr[5],
(int)in.s6_addr[6] , (int)in.s6_addr[7],
(int)in.s6_addr[8] , (int)in.s6_addr[9],
(int)in.s6_addr[10], (int)in.s6_addr[11],
(int)in.s6_addr[12], (int)in.s6_addr[13],
(int)in.s6_addr[14], (int)in.s6_addr[15]);
return str;
}

View File

@ -94,7 +94,6 @@ std::ostream& operator<<(std::ostream& o, const sockaddr_storage&);
/* thread-safe version of inet_ntoa */
std::string rs_inet_ntoa(struct in_addr in);
std::string rs_inet_ntoa(const in6_addr &in);
/***************************/