当前位置: 首页 > 工具软件 > dns.c > 使用案例 >

linux c实现dns域名解析

钱运浩
2023-12-01
//DNS Query Program on Linux
//Author : Silver Moon (m00n.silv3r@gmail.com)
//Dated : 29/4/2009

//Header Files
#include<stdio.h> //printf
#include<string.h>    //strlen
#include<stdlib.h>    //malloc
#include<sys/socket.h>    //you know what this is for
#include<arpa/inet.h> //inet_addr , inet_ntoa , ntohs etc
#include<netinet/in.h>
#include<unistd.h>    //getpid

//List of DNS Servers registered on the system
char dns_servers[10][100];
int dns_server_count = 0;
//Types of DNS resource records :)

#define T_A 1 //Ipv4 address
#define T_NS 2 //Nameserver
#define T_CNAME 5 // canonical name
#define T_SOA 6 /* start of authority zone */
#define T_PTR 12 /* domain name pointer */
#define T_MX 15 //Mail server

//Function Prototypes
void ngethostbyname(unsigned char*, int);
void ChangetoDnsNameFormat(unsigned char*, unsigned char*);
unsigned char* ReadName(unsigned char*, unsigned char*, int*);
void get_dns_servers();

//DNS header structure
struct DNS_HEADER {
	unsigned short id; // identification number

	unsigned char rd :1; // recursion desired
	unsigned char tc :1; // truncated message
	unsigned char aa :1; // authoritive answer
	unsigned char opcode :4; // purpose of message
	unsigned char qr :1; // query/response flag

	unsigned char rcode :4; // response code
	unsigned char cd :1; // checking disabled
	unsigned char ad :1; // authenticated data
	unsigned char z :1; // its z! reserved
	unsigned char ra :1; // recursion available

	unsigned short q_count; // number of question entries
	unsigned short ans_count; // number of answer entries
	unsigned short auth_count; // number of authority entries
	unsigned short add_count; // number of resource entries
};

//Constant sized fields of query structure
struct QUESTION {
	unsigned short qtype;
	unsigned short qclass;
};

//Constant sized fields of the resource record structure
#pragma pack(push, 1)
struct R_DATA {
	unsigned short type;
	unsigned short _class;
	unsigned int ttl;
	unsigned short data_len;
};
#pragma pack(pop)

//Pointers to resource record contents
struct RES_RECORD {
	unsigned char *name;
	struct R_DATA *resource;
	unsigned char *rdata;
};

//Structure of a Query
typedef struct {
	unsigned char *name;
	struct QUESTION *ques;
} QUERY;

int main(int argc, char *argv[]) {
	unsigned char hostname[100];

	//Get the DNS servers from the resolv.conf file
	get_dns_servers();

	//Get the hostname from the terminal
	printf("Enter Hostname to Lookup : ");
	scanf("%s", hostname);

	//Now get the ip of this hostname , A record
	ngethostbyname(hostname, T_A);

	return 0;
}

/*
 * Perform a DNS query by sending a packet
 * */
void ngethostbyname(unsigned char *host, int query_type) {
	unsigned char buf[65536], *qname, *reader;
	int i, j, stop, s;

	struct sockaddr_in a;

	struct RES_RECORD answers[20], auth[20], addit[20]; //the replies from the DNS server
	struct sockaddr_in dest;

	struct DNS_HEADER *dns = NULL;
	struct QUESTION *qinfo = NULL;

	printf("Resolving %s", host);

	s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //UDP packet for DNS queries

	dest.sin_family = AF_INET;
	dest.sin_port = htons(53);
	dest.sin_addr.s_addr = inet_addr(dns_servers[0]); //dns servers

	//Set the DNS structure to standard queries
	dns = (struct DNS_HEADER *) &buf;

	dns->id = (unsigned short) htons(getpid());
	dns->qr = 0; //This is a query
	dns->opcode = 0; //This is a standard query
	dns->aa = 0; //Not Authoritative
	dns->tc = 0; //This message is not truncated
	dns->rd = 1; //Recursion Desired
	dns->ra = 0; //Recursion not available! hey we dont have it (lol)
	dns->z = 0;
	dns->ad = 0;
	dns->cd = 0;
	dns->rcode = 0;
	dns->q_count = htons(1); //we have only 1 question
	dns->ans_count = 0;
	dns->auth_count = 0;
	dns->add_count = 0;

	//point to the query portion
	qname = (unsigned char*) &buf[sizeof(struct DNS_HEADER)];

	ChangetoDnsNameFormat(qname, host);
	qinfo = (struct QUESTION*) &buf[sizeof(struct DNS_HEADER)
			+ (strlen((const char*) qname) + 1)]; //fill it

	qinfo->qtype = htons(query_type); //type of the query , A , MX , CNAME , NS etc
	qinfo->qclass = htons(1); //its internet (lol)

	printf("\nSending Packet...");
	if (sendto(s, (char*) buf,
			sizeof(struct DNS_HEADER) + (strlen((const char*) qname) + 1)
					+ sizeof(struct QUESTION), 0, (struct sockaddr*) &dest,
			sizeof(dest)) < 0) {
		perror("sendto failed");
	}
	printf("Done");

	//Receive the answer
	i = sizeof dest;
	printf("\nReceiving answer...");
	if (recvfrom(s, (char*) buf, 65536, 0, (struct sockaddr*) &dest,
			(socklen_t*) &i) < 0) {
		perror("recvfrom failed");
	}
	printf("Done");

	dns = (struct DNS_HEADER*) buf;

	//move ahead of the dns header and the query field
	reader = &buf[sizeof(struct DNS_HEADER) + (strlen((const char*) qname) + 1)
			+ sizeof(struct QUESTION)];

	printf("\nThe response contains : ");
	printf("\n %d Questions.", ntohs(dns->q_count));
	printf("\n %d Answers.", ntohs(dns->ans_count));
	printf("\n %d Authoritative Servers.", ntohs(dns->auth_count));
	printf("\n %d Additional records.\n\n", ntohs(dns->add_count));

	//Start reading answers
	stop = 0;

	for (i = 0; i < ntohs(dns->ans_count); i++) {
		answers[i].name = ReadName(reader, buf, &stop);
		reader = reader + stop;

		answers[i].resource = (struct R_DATA*) (reader);
		reader = reader + sizeof(struct R_DATA);

		if (ntohs(answers[i].resource->type) == 1) //if its an ipv4 address
				{
			answers[i].rdata = (unsigned char*) malloc(
					ntohs(answers[i].resource->data_len));

			for (j = 0; j < ntohs(answers[i].resource->data_len); j++) {
				answers[i].rdata[j] = reader[j];
			}

			answers[i].rdata[ntohs(answers[i].resource->data_len)] = '\0';

			reader = reader + ntohs(answers[i].resource->data_len);
		} else {
			answers[i].rdata = ReadName(reader, buf, &stop);
			reader = reader + stop;
		}
	}

	//read authorities
	for (i = 0; i < ntohs(dns->auth_count); i++) {
		auth[i].name = ReadName(reader, buf, &stop);
		reader += stop;

		auth[i].resource = (struct R_DATA*) (reader);
		reader += sizeof(struct R_DATA);

		auth[i].rdata = ReadName(reader, buf, &stop);
		reader += stop;
	}

	//read additional
	for (i = 0; i < ntohs(dns->add_count); i++) {
		addit[i].name = ReadName(reader, buf, &stop);
		reader += stop;

		addit[i].resource = (struct R_DATA*) (reader);
		reader += sizeof(struct R_DATA);

		if (ntohs(addit[i].resource->type) == 1) {
			addit[i].rdata = (unsigned char*) malloc(
					ntohs(addit[i].resource->data_len));
			for (j = 0; j < ntohs(addit[i].resource->data_len); j++)
				addit[i].rdata[j] = reader[j];

			addit[i].rdata[ntohs(addit[i].resource->data_len)] = '\0';
			reader += ntohs(addit[i].resource->data_len);
		} else {
			addit[i].rdata = ReadName(reader, buf, &stop);
			reader += stop;
		}
	}

	//print answers
	printf("\nAnswer Records : %d \n", ntohs(dns->ans_count));
	for (i = 0; i < ntohs(dns->ans_count); i++) {
		printf("Name : %s ", answers[i].name);

		if (ntohs(answers[i].resource->type) == T_A) //IPv4 address
		{
			long *p;
			p = (long*) answers[i].rdata;
			a.sin_addr.s_addr = (*p); //working without ntohl
			printf("has IPv4 address : %s", inet_ntoa(a.sin_addr));
		}

		if (ntohs(answers[i].resource->type) == 5) {
			//Canonical name for an alias
			printf("has alias name : %s", answers[i].rdata);
		}

		printf("\n");
	}

	//print authorities
	printf("\nAuthoritive Records : %d \n", ntohs(dns->auth_count));
	for (i = 0; i < ntohs(dns->auth_count); i++) {

		printf("Name : %s ", auth[i].name);
		if (ntohs(auth[i].resource->type) == 2) {
			printf("has nameserver : %s", auth[i].rdata);
		}
		printf("\n");
	}

	//print additional resource records
	printf("\nAdditional Records : %d \n", ntohs(dns->add_count));
	for (i = 0; i < ntohs(dns->add_count); i++) {
		printf("Name : %s ", addit[i].name);
		if (ntohs(addit[i].resource->type) == 1) {
			long *p;
			p = (long*) addit[i].rdata;
			a.sin_addr.s_addr = (*p);
			printf("has IPv4 address : %s", inet_ntoa(a.sin_addr));
		}
		printf("\n");
	}
	return;
}

/*
 *
 * */
u_char* ReadName(unsigned char* reader, unsigned char* buffer, int* count) {
	unsigned char *name;
	unsigned int p = 0, jumped = 0, offset;
	int i, j;

	*count = 1;
	name = (unsigned char*) malloc(256);

	name[0] = '\0';

	//read the names in 3www6google3com format
	while (*reader != 0) {
		if (*reader >= 192) {
			offset = (*reader) * 256 + *(reader + 1) - 49152; //49152 = 11000000 00000000 ;)
			reader = buffer + offset - 1;
			jumped = 1; //we have jumped to another location so counting wont go up!
		} else {
			name[p++] = *reader;
		}

		reader = reader + 1;

		if (jumped == 0) {
			*count = *count + 1; //if we havent jumped to another location then we can count up
		}
	}

	name[p] = '\0'; //string complete
	if (jumped == 1) {
		*count = *count + 1; //number of steps we actually moved forward in the packet
	}

	//now convert 3www6google3com0 to www.google.com
	for (i = 0; i < (int) strlen((const char*) name); i++) {
		p = name[i];
		for (j = 0; j < (int) p; j++) {
			name[i] = name[i + 1];
			i = i + 1;
		}
		name[i] = '.';
	}
	name[i - 1] = '\0'; //remove the last dot
	return name;
}

/*
 * Get the DNS servers from /etc/resolv.conf file on Linux
 * */
void get_dns_servers() {
	FILE *fp;
	char line[200], *p;
	if ((fp = fopen("/etc/resolv.conf", "r")) == NULL) {
		printf("Failed opening /etc/resolv.conf file \n");
	}

	while (fgets(line, 200, fp)) {
		if (line[0] == '#') {
			continue;
		}
		if (strncmp(line, "nameserver", 10) == 0) {
			p = strtok(line, " ");
			p = strtok(NULL, " ");

			//p now is the dns ip :)
			//????
		}
	}

	strcpy(dns_servers[0], "208.67.222.222");
	strcpy(dns_servers[1], "208.67.220.220");
}

/*
 * This will convert www.google.com to 3www6google3com
 * got it :)
 * */
void ChangetoDnsNameFormat(unsigned char* dns, unsigned char* host) {
	int lock = 0, i;
	strcat((char*) host, ".");

	for (i = 0; i < strlen((char*) host); i++) {
		if (host[i] == '.') {
			*dns++ = i - lock;
			for (; lock < i; lock++) {
				*dns++ = host[lock];
			}
			lock++; //or lock=i+1;
		}
	}
	*dns++ = '\0';
}

原文连接:http://www.binarytides.com/dns-query-code-in-c-with-linux-sockets/

 类似资料: