#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; }