emsApplication/3rdPartner/libhv/protocol/dns.c

337 lines
8.9 KiB
C

#include "dns.h"
#include "hdef.h"
#include "hsocket.h"
#include "herr.h"
void dns_free(dns_t* dns) {
SAFE_FREE(dns->questions);
SAFE_FREE(dns->answers);
SAFE_FREE(dns->authorities);
SAFE_FREE(dns->addtionals);
}
// www.example.com => 3www7example3com
int dns_name_encode(const char* domain, char* buf) {
const char* p = domain;
char* plen = buf++;
int buflen = 1;
int len = 0;
while (*p != '\0') {
if (*p != '.') {
++len;
*buf = *p;
}
else {
*plen = len;
//printf("len=%d\n", len);
plen = buf;
len = 0;
}
++p;
++buf;
++buflen;
}
*plen = len;
//printf("len=%d\n", len);
*buf = '\0';
if (len != 0) {
++buflen; // include last '\0'
}
return buflen;
}
// 3www7example3com => www.example.com
int dns_name_decode(const char* buf, char* domain) {
const char* p = buf;
int len = *p++;
//printf("len=%d\n", len);
int buflen = 1;
while (*p != '\0') {
if (len-- == 0) {
len = *p;
//printf("len=%d\n", len);
*domain = '.';
}
else {
*domain = *p;
}
++p;
++domain;
++buflen;
}
*domain = '\0';
++buflen; // include last '\0'
return buflen;
}
int dns_rr_pack(dns_rr_t* rr, char* buf, int len) {
char* p = buf;
char encoded_name[256];
int encoded_namelen = dns_name_encode(rr->name, encoded_name);
int packetlen = encoded_namelen + 2 + 2 + (rr->data ? (4+2+rr->datalen) : 0);
if (len < packetlen) {
return -1;
}
memcpy(p, encoded_name, encoded_namelen);
p += encoded_namelen;
uint16_t* pushort = (uint16_t*)p;
*pushort = htons(rr->rtype);
p += 2;
pushort = (uint16_t*)p;
*pushort = htons(rr->rclass);
p += 2;
// ...
if (rr->datalen && rr->data) {
uint32_t* puint = (uint32_t*)p;
*puint = htonl(rr->ttl);
p += 4;
pushort = (uint16_t*)p;
*pushort = htons(rr->datalen);
p += 2;
memcpy(p, rr->data, rr->datalen);
p += rr->datalen;
}
return packetlen;
}
int dns_rr_unpack(char* buf, int len, dns_rr_t* rr, int is_question) {
char* p = buf;
int off = 0;
int namelen = 0;
if (*(uint8_t*)p >= 192) {
// name off, we ignore
namelen = 2;
//uint16_t nameoff = (*(uint8_t*)p - 192) * 256 + *(uint8_t*)(p+1);
}
else {
namelen = dns_name_decode(buf, rr->name);
}
if (namelen < 0) return -1;
p += namelen;
off += namelen;
if (len < off + 4) return -1;
uint16_t* pushort = (uint16_t*)p;
rr->rtype = ntohs(*pushort);
p += 2;
pushort = (uint16_t*)p;
rr->rclass = ntohs(*pushort);
p += 2;
off += 4;
if (!is_question) {
if (len < off + 6) return -1;
uint32_t* puint = (uint32_t*)p;
rr->ttl = ntohl(*puint);
p += 4;
pushort = (uint16_t*)p;
rr->datalen = ntohs(*pushort);
p += 2;
off += 6;
if (len < off + rr->datalen) return -1;
rr->data = p;
p += rr->datalen;
off += rr->datalen;
}
return off;
}
int dns_pack(dns_t* dns, char* buf, int len) {
if (len < sizeof(dnshdr_t)) return -1;
int off = 0;
dnshdr_t* hdr = &dns->hdr;
dnshdr_t htonhdr = dns->hdr;
htonhdr.transaction_id = htons(hdr->transaction_id);
htonhdr.nquestion = htons(hdr->nquestion);
htonhdr.nanswer = htons(hdr->nanswer);
htonhdr.nauthority = htons(hdr->nauthority);
htonhdr.naddtional = htons(hdr->naddtional);
memcpy(buf, &htonhdr, sizeof(dnshdr_t));
off += sizeof(dnshdr_t);
int i;
for (i = 0; i < hdr->nquestion; ++i) {
int packetlen = dns_rr_pack(dns->questions+i, buf+off, len-off);
if (packetlen < 0) return -1;
off += packetlen;
}
for (i = 0; i < hdr->nanswer; ++i) {
int packetlen = dns_rr_pack(dns->answers+i, buf+off, len-off);
if (packetlen < 0) return -1;
off += packetlen;
}
for (i = 0; i < hdr->nauthority; ++i) {
int packetlen = dns_rr_pack(dns->authorities+i, buf+off, len-off);
if (packetlen < 0) return -1;
off += packetlen;
}
for (i = 0; i < hdr->naddtional; ++i) {
int packetlen = dns_rr_pack(dns->addtionals+i, buf+off, len-off);
if (packetlen < 0) return -1;
off += packetlen;
}
return off;
}
int dns_unpack(char* buf, int len, dns_t* dns) {
memset(dns, 0, sizeof(dns_t));
if (len < sizeof(dnshdr_t)) return -1;
int off = 0;
dnshdr_t* hdr = &dns->hdr;
memcpy(hdr, buf, sizeof(dnshdr_t));
off += sizeof(dnshdr_t);
hdr->transaction_id = ntohs(hdr->transaction_id);
hdr->nquestion = ntohs(hdr->nquestion);
hdr->nanswer = ntohs(hdr->nanswer);
hdr->nauthority = ntohs(hdr->nauthority);
hdr->naddtional = ntohs(hdr->naddtional);
int i;
if (hdr->nquestion) {
int bytes = hdr->nquestion * sizeof(dns_rr_t);
SAFE_ALLOC(dns->questions, bytes);
for (i = 0; i < hdr->nquestion; ++i) {
int packetlen = dns_rr_unpack(buf+off, len-off, dns->questions+i, 1);
if (packetlen < 0) return -1;
off += packetlen;
}
}
if (hdr->nanswer) {
int bytes = hdr->nanswer * sizeof(dns_rr_t);
SAFE_ALLOC(dns->answers, bytes);
for (i = 0; i < hdr->nanswer; ++i) {
int packetlen = dns_rr_unpack(buf+off, len-off, dns->answers+i, 0);
if (packetlen < 0) return -1;
off += packetlen;
}
}
if (hdr->nauthority) {
int bytes = hdr->nauthority * sizeof(dns_rr_t);
SAFE_ALLOC(dns->authorities, bytes);
for (i = 0; i < hdr->nauthority; ++i) {
int packetlen = dns_rr_unpack(buf+off, len-off, dns->authorities+i, 0);
if (packetlen < 0) return -1;
off += packetlen;
}
}
if (hdr->naddtional) {
int bytes = hdr->naddtional * sizeof(dns_rr_t);
SAFE_ALLOC(dns->addtionals, bytes);
for (i = 0; i < hdr->naddtional; ++i) {
int packetlen = dns_rr_unpack(buf+off, len-off, dns->addtionals+i, 0);
if (packetlen < 0) return -1;
off += packetlen;
}
}
return off;
}
// dns_pack -> sendto -> recvfrom -> dns_unpack
int dns_query(dns_t* query, dns_t* response, const char* nameserver) {
char buf[1024];
int buflen = sizeof(buf);
buflen = dns_pack(query, buf, buflen);
if (buflen < 0) {
return buflen;
}
#ifdef OS_WIN
WSAInit();
#endif
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return ERR_SOCKET;
}
so_sndtimeo(sockfd, 5000);
so_rcvtimeo(sockfd, 5000);
int ret = 0;
int nsend, nrecv;
int nparse;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
memset(&addr, 0, addrlen);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(nameserver);
addr.sin_port = htons(DNS_PORT);
nsend = sendto(sockfd, buf, buflen, 0, (struct sockaddr*)&addr, addrlen);
if (nsend != buflen) {
ret = ERR_SENDTO;
goto error;
}
nrecv = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &addrlen);
if (nrecv <= 0) {
ret = ERR_RECVFROM;
goto error;
}
nparse = dns_unpack(buf, nrecv, response);
if (nparse != nrecv) {
ret = -ERR_INVALID_PACKAGE;
goto error;
}
error:
if (sockfd != INVALID_SOCKET) {
closesocket(sockfd);
}
return ret;
}
int nslookup(const char* domain, uint32_t* addrs, int naddr, const char* nameserver) {
dns_t query;
memset(&query, 0, sizeof(query));
query.hdr.transaction_id = getpid();
query.hdr.qr = DNS_QUERY;
query.hdr.rd = 1;
query.hdr.nquestion = 1;
dns_rr_t question;
memset(&question, 0, sizeof(question));
strncpy(question.name, domain, sizeof(question.name));
question.rtype = DNS_TYPE_A;
question.rclass = DNS_CLASS_IN;
query.questions = &question;
dns_t resp;
memset(&resp, 0, sizeof(resp));
int ret = dns_query(&query, &resp, nameserver);
if (ret != 0) {
return ret;
}
dns_rr_t* rr = resp.answers;
int addr_cnt = 0;
if (resp.hdr.transaction_id != query.hdr.transaction_id ||
resp.hdr.qr != DNS_RESPONSE ||
resp.hdr.rcode != 0) {
ret = -ERR_MISMATCH;
goto end;
}
if (resp.hdr.nanswer == 0) {
ret = 0;
goto end;
}
for (int i = 0; i < resp.hdr.nanswer; ++i, ++rr) {
if (rr->rtype == DNS_TYPE_A) {
if (addr_cnt < naddr && rr->datalen == 4) {
memcpy(addrs+addr_cnt, rr->data, 4);
}
++addr_cnt;
}
/*
else if (rr->rtype == DNS_TYPE_CNAME) {
char name[256];
dns_name_decode(rr->data, name);
}
*/
}
ret = addr_cnt;
end:
dns_free(&resp);
return ret;
}