ithewei 6 years ago
parent
commit
50e6f513c8
4 changed files with 451 additions and 9 deletions
  1. 9 8
      Makefile
  2. 0 1
      base/hsocket.h
  3. 334 0
      protocol/dns.c
  4. 108 0
      protocol/dns.h

+ 9 - 8
Makefile

@@ -67,14 +67,15 @@ curl: prepare
 	#$(MAKEF) TARGET=$@ SRCDIRS="$(CURL_SRCDIRS)" SRCDIRS=". base utils http http/client $(TMPDIR)" DEFINES="$(DEFINES) WITH_CURL CURL_STATICLIB"
 
 unittest: prepare
-	$(CC)  -std=c99   -I. -Ibase         -o bin/ping       unittest/ping_test.c          base/hsocket.c base/htime.c -DPRINT_DEBUG
-	$(CC)  -std=c99   -I. -Ibase         -o bin/connect    unittest/connect_test.c       base/hsocket.c base/htime.c
-	$(CC)  -std=c99   -I. -Ibase         -o bin/socketpair unittest/socketpair_test.c    base/hsocket.c base/htime.c
-	$(CXX) -std=c++11 -I. -Ibase         -o bin/defer      unittest/defer_test.cpp
-	$(CXX) -std=c++11 -I. -Ibase         -o bin/threadpool unittest/threadpool_test.cpp  -pthread
-	$(CXX) -std=c++11 -I. -Ibase         -o bin/objectpool unittest/objectpool_test.cpp  -pthread
-	$(CXX) -std=c++11 -I. -Ibase         -o bin/ls         unittest/listdir_test.cpp     base/hdir.cpp base/hbase.c
-	$(CXX) -std=c++11 -I. -Ibase -Iutils -o bin/ifconfig   unittest/ifconfig_test.cpp    utils/ifconfig.cpp
+	$(CC)  -g -Wall -std=c99   -I. -Ibase            -o bin/ping       unittest/ping_test.c          base/hsocket.c base/htime.c -DPRINT_DEBUG
+	$(CC)  -g -Wall -std=c99   -I. -Ibase            -o bin/connect    unittest/connect_test.c       base/hsocket.c base/htime.c
+	$(CC)  -g -Wall -std=c99   -I. -Ibase            -o bin/socketpair unittest/socketpair_test.c    base/hsocket.c base/htime.c
+	$(CXX) -g -Wall -std=c++11 -I. -Ibase            -o bin/defer      unittest/defer_test.cpp
+	$(CXX) -g -Wall -std=c++11 -I. -Ibase            -o bin/threadpool unittest/threadpool_test.cpp  -pthread
+	$(CXX) -g -Wall -std=c++11 -I. -Ibase            -o bin/objectpool unittest/objectpool_test.cpp  -pthread
+	$(CXX) -g -Wall -std=c++11 -I. -Ibase            -o bin/ls         unittest/listdir_test.cpp     base/hdir.cpp base/hbase.c
+	$(CXX) -g -Wall -std=c++11 -I. -Ibase -Iutils    -o bin/ifconfig   unittest/ifconfig_test.cpp    utils/ifconfig.cpp
+	$(CC)  -g -Wall -std=c99   -I. -Ibase -Iprotocol -o bin/nslookup   unittest/nslookup_test.c      protocol/dns.c
 
 # UNIX only
 webbench: prepare

+ 0 - 1
base/hsocket.h

@@ -3,7 +3,6 @@
 
 #include "hplatform.h"
 #include "hdef.h"
-#include "hbase.h"
 
 #ifdef _MSC_VER
 #pragma comment(lib, "ws2_32.lib")

+ 334 - 0
protocol/dns.c

@@ -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;
+}

+ 108 - 0
protocol/dns.h

@@ -0,0 +1,108 @@
+#ifndef HW_DNS_H_
+#define HW_DNS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hdef.h"
+
+#define DNS_PORT        53
+
+#define DNS_QUERY       0
+#define DNS_RESPONSE    1
+
+#define DNS_TYPE_A      1   // ipv4
+#define DNS_TYPE_NS     2
+#define DNS_TYPE_CNAME  5
+#define DNS_TYPE_SOA    6
+#define DNS_TYPE_WKS    11
+#define DNS_TYPE_PTR    12
+#define DNS_TYPE_HINFO  13
+#define DNS_TYPE_MX     15
+#define DNS_TYPE_AAAA   28  // ipv6
+#define DNS_TYPE_AXFR   252
+#define DNS_TYPE_ANY    255
+
+#define DNS_CLASS_IN    1
+
+#define DNS_NAME_MAXLEN 256
+
+// sizeof(dnshdr_t) = 12
+typedef struct dnshdr_s {
+    uint16_t    transaction_id;
+    // flags
+#if BYTE_ORDER == LITTLE_ENDIAN
+    uint8_t     rd:1;
+    uint8_t     tc:1;
+    uint8_t     aa:1;
+    uint8_t     opcode:4;
+    uint8_t     qr:1;
+
+    uint8_t     rcode:4;
+    uint8_t     cd:1;
+    uint8_t     ad:1;
+    uint8_t     res:1;
+    uint8_t     ra:1;
+#elif BYTE_ORDER == BIG_ENDIAN
+    uint8_t    qr:1;   // DNS_QUERY or DNS_RESPONSE
+    uint8_t    opcode:4;
+    uint8_t    aa:1;   // authoritative
+    uint8_t    tc:1;   // truncated
+    uint8_t    rd:1;   // recursion desired
+
+    uint8_t    ra:1;   // recursion available
+    uint8_t    res:1;  // reserved
+    uint8_t    ad:1;   // authenticated data
+    uint8_t    cd:1;   // checking disable
+    uint8_t    rcode:4;
+#else
+#error "BYTE_ORDER undefined!"
+#endif
+    uint16_t    nquestion;
+    uint16_t    nanswer;
+    uint16_t    nauthority;
+    uint16_t    naddtional;
+} dnshdr_t;
+
+typedef struct dns_rr_s {
+    char        name[DNS_NAME_MAXLEN]; // original domain, such as www.example.com
+    uint16_t    rtype;
+    uint16_t    rclass;
+    uint32_t    ttl;
+    uint16_t    datalen;
+    char*       data;
+} dns_rr_t;
+
+typedef struct dns_s {
+    dnshdr_t        hdr;
+    dns_rr_t*       questions;
+    dns_rr_t*       answers;
+    dns_rr_t*       authorities;
+    dns_rr_t*       addtionals;
+} dns_t;
+
+// www.example.com => 3www7example3com
+int dns_name_encode(const char* domain, char* buf);
+// 3www7example3com => www.example.com
+int dns_name_decode(const char* buf, char* domain);
+
+int dns_rr_pack(dns_rr_t* rr, char* buf, int len);
+int dns_rr_unpack(char* buf, int len, dns_rr_t* rr, int is_question);
+
+int dns_pack(dns_t* dns, char* buf, int len);
+int dns_unpack(char* buf, int len, dns_t* dns);
+// NOTE: free dns->rrs
+void dns_free(dns_t* dns);
+
+// dns_pack -> sendto -> recvfrom -> dns_unpack
+int dns_query(dns_t* query, dns_t* response, const char* nameserver DEFAULT("127.0.1,1"));
+
+// domain -> dns_t query; -> dns_query -> dns_t response; -> addrs
+int nslookup(const char* domain, uint32_t* addrs, int naddr, const char* nameserver DEFAULT("127.0.1.1"));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // HW_DNS_H_