From 6be77463b78b2d2e4d81f531d166fb459b6c9f59 Mon Sep 17 00:00:00 2001 From: Guo Wenxue <guowenxue@gmail.com> Date: Mon, 29 Oct 2018 22:10:14 +0800 Subject: [PATCH] Add DNS client Parser domain source code --- apue/3.UDP_dns/makefile | 13 + apue/3.UDP_dns/dns_main.c | 44 +++++ apue/3.UDP_dns/dns_api.c | 371 ++++++++++++++++++++++++++++++++++++++++++++++ apue/3.UDP_dns/dns_api.h | 23 ++ 4 files changed, 451 insertions(+), 0 deletions(-) diff --git a/apue/3.UDP_dns/dns_api.c b/apue/3.UDP_dns/dns_api.c new file mode 100644 index 0000000..42da0c2 --- /dev/null +++ b/apue/3.UDP_dns/dns_api.c @@ -0,0 +1,371 @@ +/********************************************************************************* + * 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; +} + diff --git a/apue/3.UDP_dns/dns_api.h b/apue/3.UDP_dns/dns_api.h new file mode 100644 index 0000000..815fabd --- /dev/null +++ b/apue/3.UDP_dns/dns_api.h @@ -0,0 +1,23 @@ +/********************************************************************************* + * Copyright: (C) 2018 LingYun IoT Studio + * All rights reserved. + * + * Filename: dns_api.h + * 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" + * + ********************************************************************************/ + +#ifndef __DNS_API_ +#define __DNS_API_ + +#define IPADDR_LEN 16 +#define DNS_HEAD_MIN 12 +#define DNS_TAIL_MIN 5 + +int query_dns(char *dns_server_ip, char *domain, char *ipaddr, int ipaddr_size); + +#endif diff --git a/apue/3.UDP_dns/dns_main.c b/apue/3.UDP_dns/dns_main.c new file mode 100644 index 0000000..f65cbbd --- /dev/null +++ b/apue/3.UDP_dns/dns_main.c @@ -0,0 +1,44 @@ +/********************************************************************************* + * Copyright: (C) 2018 LingYun IoT Studio + * All rights reserved. + * + * Filename: dns_main.c + * Description: This file is DNS client API test entry(main) file + * + * 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 "dns_api.h" + +int main(int argc,char* argv[]) +{ + char *hostname; + char ipaddr[IPADDR_LEN]; + + if(argc != 2) + { + printf("Usage: %s [domain]\n", argv[0]); + return 1; + } + + hostname=argv[1]; + + memset( ipaddr, 0, sizeof(ipaddr) ); + query_dns("114.114.114.114", hostname, ipaddr, IPADDR_LEN); + + printf("DNS query: %s->%s\n", hostname, ipaddr); + + return 0; +} + + diff --git a/apue/3.UDP_dns/makefile b/apue/3.UDP_dns/makefile new file mode 100644 index 0000000..cc038d1 --- /dev/null +++ b/apue/3.UDP_dns/makefile @@ -0,0 +1,13 @@ +INST_PATH=/usr/bin + +BIN_FILES=dns + +all: clean + gcc *.c -o ${BIN_FILES} + + +clean: + rm -f ${BIN_FILES} + +install: + @cp ${BIN_FILES} ${INST_PATH} -- Gitblit v1.9.1