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