验证方式
终端验证:
nslookup www.microsoft.com
Server: 192.168.1.1
Address: 192.168.1.1#53
Non-authoritative answer:
www.microsoft.com canonical name = www.microsoft.com-c-3.edgekey.net.
www.microsoft.com-c-3.edgekey.net canonical name = www.microsoft.com-c-3.edgekey.net.globalredir.akadns.net.
www.microsoft.com-c-3.edgekey.net.globalredir.akadns.net canonical name = e13678.ca.s.tl88.net.
Name: e13678.ca.s.tl88.net
Address: 27.148.139.88
最初的cname 获取方案
- 只能获取其中一个
#import <Foundation/Foundation.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdio.h>
#include<netdb.h>
#include<sys/socket.h>
#include<errno.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
int main(int argc, const char * argv[]) {
NSString *host = @"www.microsoft.com";
struct addrinfo hints, *res = NULL;
int rc;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET;
hints.ai_protocol = IPPROTO_IP|IPPROTO_TCP;
rc = getaddrinfo([host UTF8String], NULL, &hints, &res);
if (rc != 0) {
return -1;
}
struct sockaddr_in sin;
memcpy(&sin, (res->ai_addr), sizeof(struct sockaddr_in));
printf("%s \n", res->ai_next);
printf("%s \n", res->ai_canonname);
return 0;
}
问题: 结构体 addrinfo 中 ai_next 一直为空,暂时不明白是什么原因?
解决方案一
- 该方案在手机网络环境下无法正常获取cname
- 局限于PC端或者wifi网络
#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(void);
//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];
do {
//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);
} while (hostname != EOF);
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';
}
解决方案二, 最终解决方案
- 该方案解析成功几率比较高(90%以上),失败情况忽略
- 因为涉及到网络,开启线程避免延时
#import "DNS.h"
#include<netdb.h>
@implementation DNS
+ (void)getCNames:(NSString *)host action:(void(^)(NSArray<NSString *>* cnames))action {
if (host == nil || host.length == 0) {
action(@[]);
}
NSMutableArray *cNames = [[NSMutableArray alloc] initWithCapacity:20];
dispatch_async(dispatch_get_main_queue(), ^{
struct hostent * remoteHost;
remoteHost = [self getHostByName:[host UTF8String]];
if (remoteHost != NULL) {
//追加域名的所有aliases
if (remoteHost->h_aliases != NULL) {
int count = 1;
while (remoteHost->h_aliases[count] != NULL && *(remoteHost->h_aliases) != NULL) {
[cNames addObject:[NSString stringWithUTF8String:remoteHost->h_aliases[count]]];
count++;
}
}
if (remoteHost->h_name != NULL){
[cNames addObject:[NSString stringWithUTF8String:remoteHost->h_name]];
}
}
action(cNames);
});
}
+ (struct hostent*)getHostByName:(const char *)hostName {
__block struct hostent *phost = NULL;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
phost = gethostbyname(hostName);
dispatch_semaphore_signal(semaphore);
}];
// 设置超时时间2秒
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC));
[queue cancelAllOperations];
return phost;
}
@end
网友评论