美文网首页iOS杂技网络
iOS-DNS解析之cname

iOS-DNS解析之cname

作者: child_cool | 来源:发表于2018-09-11 15:33 被阅读218次

    验证方式

    终端验证:
    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
    

    相关文章

      网友评论

        本文标题:iOS-DNS解析之cname

        本文链接:https://www.haomeiwen.com/subject/vmeugftx.html