337 lines
8.9 KiB
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;
|
||
|
}
|