iOS杂技网络

iOS-DNS解析之cname

2018-09-11  本文已影响218人  child_cool

验证方式

终端验证:
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 一直为空,暂时不明白是什么原因?

解决方案一

#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';
}

解决方案二, 最终解决方案

#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
上一篇下一篇

猜你喜欢

热点阅读