390 lines
8.6 KiB
C++
390 lines
8.6 KiB
C++
#include "Time.h"
|
|
#include "Stream.h"
|
|
#include "ITransport.h"
|
|
|
|
#include "DNS.h"
|
|
#include "Ethernet.h"
|
|
|
|
#define NET_DEBUG DEBUG
|
|
|
|
#define MAX_DOMAIN_NAME 16 // for example "www.google.com"
|
|
|
|
#define IPPORT_DOMAIN 53 ///< DNS server port number
|
|
|
|
#define INITRTT 2000L /* Initial smoothed response time */
|
|
#define MAXCNAME (MAX_DOMAIN_NAME + (MAX_DOMAIN_NAME>>1)) /* Maximum amount of cname recursion */
|
|
|
|
#define TYPE_A 1 /* Host address */
|
|
#define TYPE_NS 2 /* Name server */
|
|
#define TYPE_MD 3 /* Mail destination (obsolete) */
|
|
#define TYPE_MF 4 /* Mail forwarder (obsolete) */
|
|
#define TYPE_CNAME 5 /* Canonical name */
|
|
#define TYPE_SOA 6 /* Start of Authority */
|
|
#define TYPE_MB 7 /* Mailbox name (experimental) */
|
|
#define TYPE_MG 8 /* Mail group member (experimental) */
|
|
#define TYPE_MR 9 /* Mail rename name (experimental) */
|
|
#define TYPE_NULL 10 /* Null (experimental) */
|
|
#define TYPE_WKS 11 /* Well-known sockets */
|
|
#define TYPE_PTR 12 /* Pointer record */
|
|
#define TYPE_HINFO 13 /* Host information */
|
|
#define TYPE_MINFO 14 /* Mailbox information (experimental)*/
|
|
#define TYPE_MX 15 /* Mail exchanger */
|
|
#define TYPE_TXT 16 /* Text strings */
|
|
#define TYPE_ANY 255 /* Matches any type */
|
|
|
|
#define CLASS_IN 1 /* The ARPA Internet */
|
|
|
|
/* Round trip timing parameters */
|
|
#define AGAIN 8 /* Average RTT gain = 1/8 */
|
|
#define LAGAIN 3 /* Log2(AGAIN) */
|
|
#define DGAIN 4 /* Mean deviation gain = 1/4 */
|
|
#define LDGAIN 2 /* log2(DGAIN) */
|
|
|
|
/* Header for all domain messages */
|
|
typedef struct dhdr
|
|
{
|
|
ushort id; /* Identification */
|
|
byte qr; /* Query/Response */
|
|
#define QUERY 0
|
|
#define RESPONSE 1
|
|
byte opcode;
|
|
#define IQUERY 1
|
|
byte aa; /* Authoratative answer */
|
|
byte tc; /* Truncation */
|
|
byte rd; /* Recursion desired */
|
|
byte ra; /* Recursion available */
|
|
byte z;
|
|
byte rcode; /* Response code */
|
|
#define NO_ERROR 0
|
|
#define FORMAT_ERROR 1
|
|
#define SERVER_FAIL 2
|
|
#define NAME_ERROR 3
|
|
#define NOT_IMPL 4
|
|
#define REFUSED 5
|
|
ushort qdcount; /* Question count */
|
|
ushort ancount; /* Answer count */
|
|
ushort nscount; /* Authority (name server) count */
|
|
ushort arcount; /* Additional record count */
|
|
} TDNS;
|
|
|
|
DNS::DNS(ISocket* socket)
|
|
{
|
|
Socket = socket;
|
|
|
|
socket->Remote.Port = 53;
|
|
socket->Remote.Address = socket->Host->DNSServer;
|
|
|
|
ITransport* port = dynamic_cast<ITransport*>(Socket);
|
|
port->Register(OnReceive, this);
|
|
}
|
|
|
|
DNS::~DNS()
|
|
{
|
|
}
|
|
|
|
// 转换域名为可读格式
|
|
int parse_name(Stream& ms, char* buf, short len)
|
|
{
|
|
ushort slen; /* Length of current segment */
|
|
int clen = 0; /* Total length of compressed name */
|
|
int indirect = 0; /* Set if indirection encountered */
|
|
int nseg = 0; /* Total number of segments in name */
|
|
|
|
byte* msg = ms.GetBuffer();
|
|
byte* cp = ms.Current();
|
|
|
|
for (;;)
|
|
{
|
|
slen = *cp++; /* Length of this segment */
|
|
|
|
if (!indirect) clen++;
|
|
|
|
if ((slen & 0xc0) == 0xc0)
|
|
{
|
|
if (!indirect)
|
|
clen++;
|
|
indirect = 1;
|
|
/* Follow indirection */
|
|
cp = &msg[((slen & 0x3f)<<8) + *cp];
|
|
slen = *cp++;
|
|
}
|
|
|
|
if (slen == 0) /* zero length == all done */
|
|
break;
|
|
|
|
len -= slen + 1;
|
|
|
|
if (len < 0) return -1;
|
|
|
|
if (!indirect) clen += slen;
|
|
|
|
while (slen-- != 0) *buf++ = (char)*cp++;
|
|
*buf++ = '.';
|
|
nseg++;
|
|
}
|
|
|
|
if (nseg == 0)
|
|
{
|
|
/* Root name; represent as single dot */
|
|
*buf++ = '.';
|
|
len--;
|
|
}
|
|
|
|
*buf++ = '\0';
|
|
len--;
|
|
|
|
ms.Seek(clen);
|
|
|
|
return clen; /* Length of compressed message */
|
|
}
|
|
|
|
// 分析请求段
|
|
bool dns_question(Stream& ms)
|
|
{
|
|
char name[MAXCNAME];
|
|
|
|
int len = parse_name(ms, name, MAXCNAME);
|
|
if (len == -1) return false;
|
|
|
|
//cp += len;
|
|
//cp += 2; /* type */
|
|
//cp += 2; /* class */
|
|
ms.Seek(2 + 2);
|
|
|
|
return true;
|
|
}
|
|
|
|
// 分析应答记录
|
|
bool dns_answer(Stream& ms, byte* ip_from_dns)
|
|
{
|
|
char name[MAXCNAME];
|
|
|
|
int len = parse_name(ms, name, MAXCNAME);
|
|
if (len == -1) return 0;
|
|
|
|
int type = ms.ReadUInt16();
|
|
//cp += 2; /* type */
|
|
//cp += 2; /* class */
|
|
//cp += 4; /* ttl */
|
|
//cp += 2; /* len */
|
|
//ms.Seek(2 + 2 + 4 + 2);
|
|
// 上面已经读取了type
|
|
ms.Seek(2 + 4 + 2);
|
|
|
|
switch (type)
|
|
{
|
|
case TYPE_A:
|
|
/* Just read the address directly into the structure */
|
|
ms.Read(ip_from_dns, 0, 4);
|
|
break;
|
|
case TYPE_CNAME:
|
|
case TYPE_MB:
|
|
case TYPE_MG:
|
|
case TYPE_MR:
|
|
case TYPE_NS:
|
|
case TYPE_PTR:
|
|
/* These types all consist of a single domain name */
|
|
/* convert it to ascii format */
|
|
len = parse_name(ms, name, MAXCNAME);
|
|
if (len == -1) return 0;
|
|
|
|
break;
|
|
case TYPE_HINFO:
|
|
len = ms.ReadByte();
|
|
ms.Seek(len);
|
|
len = ms.ReadByte();
|
|
ms.Seek(len);
|
|
|
|
break;
|
|
case TYPE_MX:
|
|
ms.Seek(2);
|
|
/* Get domain name of exchanger */
|
|
len = parse_name(ms, name, MAXCNAME);
|
|
if (len == -1) return 0;
|
|
|
|
break;
|
|
case TYPE_SOA:
|
|
/* Get domain name of name server */
|
|
len = parse_name(ms, name, MAXCNAME);
|
|
if (len == -1) return 0;
|
|
|
|
/* Get domain name of responsible person */
|
|
len = parse_name(ms, name, MAXCNAME);
|
|
if (len == -1) return 0;
|
|
|
|
ms.Seek(4 + 4 + 4 + 4 + 4);
|
|
|
|
break;
|
|
case TYPE_TXT:
|
|
/* Just stash */
|
|
break;
|
|
default:
|
|
/* Ignore */
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// 分析响应
|
|
bool parseDNSMSG(TDNS* hdr, const Array& bs, byte* ip_from_dns)
|
|
{
|
|
Stream ms(bs);
|
|
ms.Little = false;
|
|
|
|
memset(hdr, 0, sizeof(hdr));
|
|
|
|
hdr->id = ms.ReadUInt16();
|
|
ushort tmp = ms.ReadUInt16();
|
|
if (tmp & 0x8000) hdr->qr = 1;
|
|
|
|
hdr->opcode = (tmp >> 11) & 0xf;
|
|
|
|
if (tmp & 0x0400) hdr->aa = 1;
|
|
if (tmp & 0x0200) hdr->tc = 1;
|
|
if (tmp & 0x0100) hdr->rd = 1;
|
|
if (tmp & 0x0080) hdr->ra = 1;
|
|
|
|
hdr->rcode = tmp & 0xf;
|
|
hdr->qdcount = ms.ReadUInt16();
|
|
hdr->ancount = ms.ReadUInt16();
|
|
hdr->nscount = ms.ReadUInt16();
|
|
hdr->arcount = ms.ReadUInt16();
|
|
//ms.Read((byte*)hdr, 0, sizeof(TDNS));
|
|
|
|
// 开始分析变长部分
|
|
|
|
/* Question section */
|
|
for (int i = 0; i < hdr->qdcount; i++)
|
|
{
|
|
// 域名太长
|
|
if(!dns_question(ms)) return false;
|
|
}
|
|
|
|
/* Answer section */
|
|
for (int i = 0; i < hdr->ancount; i++)
|
|
{
|
|
if(!dns_answer(ms, ip_from_dns)) return -1;
|
|
}
|
|
|
|
/* Name server (authority) section */
|
|
//for (i = 0; i < hdr->nscount; i++);
|
|
|
|
/* Additional section */
|
|
//for (i = 0; i < hdr->arcount; i++);
|
|
|
|
return hdr->rcode == 0;
|
|
}
|
|
|
|
// DNS查询消息
|
|
short dns_makequery(short op, const String& name, Array& bs)
|
|
{
|
|
Stream ms(bs);
|
|
ms.Little = false;
|
|
|
|
ms.Write((ushort)Time.Seconds);
|
|
ms.Write((ushort)((op << 11) | 0x0100)); // Recursion desired
|
|
ms.Write((ushort)1);
|
|
ms.Write((ushort)0);
|
|
ms.Write((ushort)0);
|
|
ms.Write((ushort)0);
|
|
|
|
const char* dname = name.GetBuffer();
|
|
ushort dlen = strlen(dname);
|
|
for (;;)
|
|
{
|
|
// 查找下一个小圆点
|
|
const char* cp1 = strchr(dname, '.');
|
|
|
|
int len = 0;
|
|
if (cp1 != NULL)
|
|
len = cp1 - dname; /* More to come */
|
|
else
|
|
len = dlen; /* Last component */
|
|
|
|
//*cp++ = len; /* Write length of component */
|
|
// 写长度
|
|
ms.Write((byte)len);
|
|
if (len == 0) break;
|
|
|
|
/* Copy component up to (but not including) dot */
|
|
//strncpy((char *)cp, dname, len);
|
|
//cp += len;
|
|
ms.Write((const byte*)dname, 0, len);
|
|
if (cp1 == NULL)
|
|
{
|
|
//*cp++ = 0; /* Last one; write null and finish */
|
|
// 最后一个,写空,完成
|
|
ms.Write((byte)0);
|
|
break;
|
|
}
|
|
dname += len+1;
|
|
dlen -= len+1;
|
|
}
|
|
|
|
ms.Write((ushort)0x0001); /* type */
|
|
ms.Write((ushort)0x0001); /* class */
|
|
|
|
bs.SetLength(ms.Position());
|
|
|
|
return ms.Position();
|
|
}
|
|
|
|
IPAddress DNS::Query(const String& domain, int msTimeout)
|
|
{
|
|
#if NET_DEBUG
|
|
IPAddress& server = Socket->Host->DNSServer;
|
|
debug_printf("DNS::Query %s DNS Server : ", domain.GetBuffer());
|
|
server.Show(true);
|
|
#endif
|
|
|
|
byte buf[1024];
|
|
Array bs(buf, ArrayLength(buf));
|
|
Array rs(buf, ArrayLength(buf));
|
|
// 同时作为响应缓冲区,别浪费了
|
|
rs.SetLength(0);
|
|
_Buffer = &rs;
|
|
|
|
dns_makequery(0, domain, bs);
|
|
Socket->Send(bs);
|
|
|
|
IPAddress ip;
|
|
TimeWheel tw(0, msTimeout);
|
|
tw.Sleep = 100;
|
|
while(!tw.Expired())
|
|
{
|
|
if(rs.Length() > 0)
|
|
{
|
|
TDNS dns;
|
|
parseDNSMSG(&dns, rs, (byte*)&ip.Value);
|
|
break;
|
|
}
|
|
}
|
|
_Buffer = NULL;
|
|
|
|
return ip;
|
|
}
|
|
|
|
uint DNS::OnReceive(ITransport* port, Array& bs, void* param, void* param2)
|
|
{
|
|
((DNS*)param)->Process(bs, *(const IPEndPoint*)param2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void DNS::Process(Array& bs, const IPEndPoint& server)
|
|
{
|
|
// 只要来自服务器的
|
|
if(server.Address != Socket->Host->DNSServer) return;
|
|
|
|
if(_Buffer)
|
|
_Buffer->Copy(bs);
|
|
else
|
|
{
|
|
#if NET_DEBUG
|
|
debug_printf("DNS::Process \r\n");
|
|
server.Show(true);
|
|
#endif
|
|
}
|
|
}
|