|
@@ -0,0 +1,334 @@
|
|
|
|
|
+#include "dns.h"
|
|
|
|
|
+#include "hsocket.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);
|
|
|
|
|
+ dns->questions = (dns_rr_t*)malloc(bytes);
|
|
|
|
|
+ memset(dns->questions, 0, 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);
|
|
|
|
|
+ dns->answers = (dns_rr_t*)malloc(bytes);
|
|
|
|
|
+ memset(dns->answers, 0, 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);
|
|
|
|
|
+ dns->authorities = (dns_rr_t*)malloc(bytes);
|
|
|
|
|
+ memset(dns->authorities, 0, 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);
|
|
|
|
|
+ dns->addtionals = (dns_rr_t*)malloc(bytes);
|
|
|
|
|
+ memset(dns->addtionals, 0, 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;
|
|
|
|
|
+ }
|
|
|
|
|
+ int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
|
+ if (sockfd < 0) {
|
|
|
|
|
+ perror("socket");
|
|
|
|
|
+ return -10;
|
|
|
|
|
+ }
|
|
|
|
|
+ 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 = -20;
|
|
|
|
|
+ goto error;
|
|
|
|
|
+ }
|
|
|
|
|
+ nrecv = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &addrlen);
|
|
|
|
|
+ if (nrecv <= 0) {
|
|
|
|
|
+ ret = -30;
|
|
|
|
|
+ goto error;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ nparse = dns_unpack(buf, nrecv, response);
|
|
|
|
|
+ if (nparse != nrecv) {
|
|
|
|
|
+ ret = -40;
|
|
|
|
|
+ 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 = -1;
|
|
|
|
|
+ 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;
|
|
|
|
|
+}
|