New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2018 LingYun IoT Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: dns_api.c |
| | | * Description: This file is DNS client API based on UDP socket |
| | | * |
| | | * Version: 1.0.0(10/29/2018) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "2018-10-28 01:38:08 PM" |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <errno.h> |
| | | #include <string.h> |
| | | #include <time.h> |
| | | #include <stdlib.h> |
| | | #include <unistd.h> |
| | | #include <sys/types.h> |
| | | #include <sys/socket.h> |
| | | #include <netinet/in.h> |
| | | #include <arpa/inet.h> |
| | | |
| | | #include "dns_api.h" |
| | | |
| | | |
| | | /*+-----------------------------------------------------------------------------------+ |
| | | *| DNS Protocal packet format definition |
| | | *| Reference: https://www.ietf.org/rfc/rfc1035.txt |
| | | *| https://www2.cs.duke.edu/courses/fall16/compsci356/DNS/DNS-primer.pdf |
| | | *+-----------------------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | /* |
| | | DNS packet format: |
| | | +---------------------+ |
| | | | Header | |
| | | +---------------------+ |
| | | | Question | the question for the name server |
| | | +---------------------+ |
| | | | Answer | RRs answering the question |
| | | +---------------------+ |
| | | | Authority | RRs pointing toward an authority |
| | | +---------------------+ |
| | | | Additional | RRs holding additional information |
| | | +---------------------+ |
| | | |
| | | Header section format: |
| | | 1 1 1 1 1 1 |
| | | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | ID | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | |QR| Opcode |AA|TC|RD|RA| Z | RCODE | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | QDCOUNT | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | ANCOUNT | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | NSCOUNT | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | ARCOUNT | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | |
| | | |
| | | Question section format: |
| | | |
| | | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | | |
| | | / QNAME / |
| | | / / |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | QTYPE | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | QCLASS | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | |
| | | Resource record format: |
| | | 1 1 1 1 1 1 |
| | | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | | |
| | | / / |
| | | / NAME / |
| | | | | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | TYPE | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | CLASS | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | TTL | |
| | | | | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | | RDLENGTH | |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| |
| | | / RDATA / |
| | | / / |
| | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| | | */ |
| | | |
| | | |
| | | void dump_buf(const char *prompt, char *data, int len) |
| | | { |
| | | int i; |
| | | |
| | | if(prompt) |
| | | { |
| | | printf("%s", prompt); |
| | | } |
| | | |
| | | for(i=0; i<len; i++) |
| | | { |
| | | printf("%02x ", data[i]); |
| | | } |
| | | |
| | | printf("\n"); |
| | | } |
| | | |
| | | int fill_dns_head(char *packet, int size) |
| | | { |
| | | unsigned short tmp; |
| | | int ofset = 0; |
| | | |
| | | if( !packet || size<DNS_HEAD_MIN ) |
| | | { |
| | | printf("Invalid input arguments in %s()\n", __FUNCTION__); |
| | | return -1; |
| | | } |
| | | |
| | | /* DNS Head: Transaction ID */ |
| | | srand((int)time(0)); |
| | | tmp = (unsigned short)random(); |
| | | memcpy(&packet[ofset], &tmp, 2); |
| | | ofset += 2; |
| | | |
| | | /* DNS Head: Flags */ |
| | | tmp = 0x0001; /* MSB */ |
| | | memcpy(&packet[ofset], &tmp, 2); |
| | | ofset += 2; |
| | | |
| | | /* DNS Head: Questions */ |
| | | tmp = 0x0100; /* MSB */ |
| | | memcpy(&packet[ofset], &tmp, 2); |
| | | ofset += 2; |
| | | |
| | | /* DNS Head: Answer RRs */ |
| | | tmp = 0x0000; |
| | | memcpy(&packet[ofset], &tmp, 2); |
| | | ofset += 2; |
| | | |
| | | /* DNS Head: Authority RRs */ |
| | | tmp = 0x0000; |
| | | memcpy(&packet[ofset], &tmp, 2); |
| | | ofset += 2; |
| | | |
| | | /* DNS Head: Additional RRs */ |
| | | tmp = 0x0000; |
| | | memcpy(&packet[ofset], &tmp, 2); |
| | | ofset += 2; |
| | | |
| | | //dump_buf("DNS packet head: \n", packet, ofset); |
| | | |
| | | return ofset; |
| | | } |
| | | |
| | | int fill_dns_question_qtype_qclass(char *packet, int size) |
| | | { |
| | | unsigned short tmp; |
| | | int ofset = 0; |
| | | |
| | | if( !packet || size<DNS_HEAD_MIN ) |
| | | { |
| | | printf("Invalid input arguments in %s()\n", __FUNCTION__); |
| | | return -1; |
| | | } |
| | | |
| | | /* DNS QType: 0x0001(A record) 0x0005(CNAME)*/ |
| | | tmp = 0x0100; /* MSB */ |
| | | memcpy(&packet[ofset], &tmp, 2); |
| | | ofset += 2; |
| | | |
| | | /* DNS QClass, 0x0001: representing Internet addresses */ |
| | | tmp = 0x0100; /* MSB */ |
| | | memcpy(&packet[ofset], &tmp, 2); |
| | | ofset += 2; |
| | | |
| | | //dump_buf("DNS packet tail: \n", packet, ofset); |
| | | |
| | | return ofset; |
| | | } |
| | | |
| | | /* www.baidu.com ==> 3www5baidu3com0 */ |
| | | |
| | | int fill_dns_question_qname(char *domain, char *packet, int size) |
| | | { |
| | | int ofset = 0; |
| | | const char *start; |
| | | char *ptr; |
| | | int len; |
| | | |
| | | /* Check arguments */ |
| | | if( !domain || !packet || size<strlen(domain) ) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | start = domain; |
| | | while( (ptr=(strchr(start, '.'))) != NULL ) |
| | | { |
| | | len=ptr-start; /* get segemnent length by '.' */ |
| | | packet[ofset++] = len; |
| | | |
| | | len = ptr-start; |
| | | strncpy(&packet[ofset], start, len); /* copy segemnent string */ |
| | | ofset += len; |
| | | |
| | | start = ptr + 1; /* let start point to next character follow '.' */ |
| | | } |
| | | |
| | | /* last segemnent by '.' is pointed by start */ |
| | | len = strlen(domain)- (start-domain); |
| | | packet[ofset++] = len; |
| | | |
| | | /* check strncpy buffer limits */ |
| | | if(size-ofset < len) |
| | | len = size-ofset; |
| | | |
| | | strncpy(&packet[ofset], start, len); |
| | | ofset += len; |
| | | |
| | | packet[ofset++] = 0x00; |
| | | |
| | | return ofset; |
| | | } |
| | | |
| | | |
| | | |
| | | int query_dns(char *dns_server_ip, char *domain, char *ipaddr, int ipaddr_size) |
| | | { |
| | | int sockfd; |
| | | char msg_buf[512]; |
| | | int ofset = 0; |
| | | int rv = -1; |
| | | short records; |
| | | struct sockaddr_in servaddr; |
| | | |
| | | if(!domain || !ipaddr || strlen(domain)<=0 || ipaddr_size<IPADDR_LEN) |
| | | { |
| | | printf("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | memset(ipaddr, 0, ipaddr_size); |
| | | |
| | | |
| | | /*+-------------------------------------------------------------------------------------------------------------- |
| | | *| Parser baidu.com: |
| | | *| Send DNS Query Packet: |
| | | *| a7 c3 01 00 00 01 00 00 00 00 00 00 05 62 61 69 64 75 03 63 6f 6d 00 00 01 00 01 |
| | | *| |
| | | *| Header section format: |
| | | *| Session ID(2B): A7 C3 |
| | | *| Flag(2B): 01 00 |
| | | *| QDCOUNT(2B): 01 00 1 entry in the question section |
| | | *| ANCOUNT(2B): 00 00 0 resource records in the answer section |
| | | *| NSCOUNT(2B): 00 00 0 resource records in the authority records section |
| | | *| ARCOUNT(2B): 00 00 0 resource records in the additional records section |
| | | *| |
| | | *| Question section format: |
| | | *| QNAME(nB): 05 62 61 69 64 75 03 63 6f 6d 00: 05baidu03com0 |
| | | *| QTYPE(2B): 00 01 1: (A record) |
| | | *| QCLASS(2B): 00 01 1: (Internet addresses) |
| | | *+-------------------------------------------------------------------------------------------------------------- |
| | | */ |
| | | |
| | | rv=fill_dns_head(&msg_buf[ofset], sizeof(msg_buf)-ofset); |
| | | if( rv < 0 ) |
| | | { |
| | | printf("Fill DNS head failure\n"); |
| | | return -2; |
| | | } |
| | | ofset += rv; |
| | | |
| | | rv = fill_dns_question_qname(domain, &msg_buf[ofset], sizeof(msg_buf)-ofset); |
| | | if(rv < 0) |
| | | { |
| | | printf("Fill DNS Question Qname failure\n"); |
| | | return -2; |
| | | } |
| | | ofset += rv; |
| | | |
| | | rv= fill_dns_question_qtype_qclass(&msg_buf[ofset], sizeof(msg_buf)-ofset); |
| | | if( rv < 0 ) |
| | | { |
| | | printf("Fill DNS Question QType and QClass\n"); |
| | | return -3; |
| | | } |
| | | ofset += rv; |
| | | |
| | | //dump_buf("DNS packet : \n", msg_buf, ofset); |
| | | |
| | | if ( (sockfd=socket(PF_INET, SOCK_DGRAM, 0)) < 0 ) |
| | | { |
| | | printf("Create UDP socket failure: %s\n", strerror(errno)); |
| | | return -4; |
| | | } |
| | | |
| | | memset(&servaddr, 0, sizeof(servaddr)); |
| | | servaddr.sin_family = AF_INET; |
| | | servaddr.sin_port = htons(53); /*DNS default port 53 */ |
| | | servaddr.sin_addr.s_addr = inet_addr(dns_server_ip); |
| | | |
| | | if( sendto(sockfd, msg_buf, ofset, 0, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0 ) |
| | | { |
| | | printf("Send DNS packet to DNS server[%s] failure: %s\n", dns_server_ip, strerror(errno)); |
| | | return -5; |
| | | } |
| | | |
| | | if( (rv=recvfrom(sockfd, msg_buf, sizeof(msg_buf), 0, NULL, NULL) ) < 0 ) |
| | | { |
| | | printf("Receive DNS reply from DNS server[%s] failure: %s\n", dns_server_ip, strerror(errno)); |
| | | return -6; |
| | | } |
| | | |
| | | /*+-------------------------------------------------------------------------------------------------------------- |
| | | *| Receive DNS Server Reply Packet: |
| | | *| a7 c3 81 80 00 01 [00 02] 00 00 00 00 05 62 61 69 64 75 03 63 6f 6d 00 00 01 00 01 |
| | | *| c0 0c 00 01 00 01 00 00 00 21 00 04 dc b5 39 d8 c0 0c 00 01 00 01 00 00 00 21 00 04 7b 7d 73 6e |
| | | *| Question Section: a7 c3 ... 00 01, but bytes[8:7] is reply resource entries, here is [00 02] so get 2 entries |
| | | *| |
| | | *| First resource |
| | | *| Name Pointer(2B): c0 0c |
| | | *| Type(2B): 01 00 |
| | | *| Class: 01 00 |
| | | *| TTL(4B): 00 00 00 21 |
| | | *| RDLENGTH(2B): 00 04 |
| | | *| IP address: dc b5 39 d8 "220.181.57.216" |
| | | *| |
| | | *| Second resource |
| | | *| Name Pointer(2B): c0 0c |
| | | *| Type(2B): 01 00 |
| | | *| Class: 01 00 |
| | | *| TTL(4B): 00 00 00 21 |
| | | *| RDLENGTH(2B): 00 04 |
| | | *| IP address: 7b 7d 73 6e "123.125.115.110" |
| | | *| |
| | | *+-------------------------------------------------------------------------------------------------------------- |
| | | */ |
| | | |
| | | //dump_buf("Recieve DNS reply: \n", msg_buf, rv); |
| | | records = *(short *)&msg_buf[7]; |
| | | |
| | | /* 1, Reply resource will followed by request packet |
| | | * 2, 2B Name Pointer + 2B Type+2B Class + 4B TTL + 2B RDlength = 12B */ |
| | | ofset += 12; |
| | | |
| | | snprintf(ipaddr, ipaddr_size, "%d.%d.%d.%d",msg_buf[ofset],msg_buf[ofset+1],msg_buf[ofset+2],msg_buf[ofset+3]); |
| | | #if 0 |
| | | printf("DNS server[%s] resolve domain \"%s\" get %d records:\n", dns_server_ip, domain, records); |
| | | while( records-- ) |
| | | { |
| | | printf("%d.%d.%d.%d\n",msg_buf[ofset],msg_buf[ofset+1],msg_buf[ofset+2],msg_buf[ofset+3]); |
| | | ofset += 16; |
| | | } |
| | | #endif |
| | | |
| | | close(sockfd); |
| | | return 0; |
| | | } |
| | | |