c - 从 DNS 响应获取 IP

标签 c dns

我正在尝试编写一个程序,该程序将从 DNS 回复中抓取一个 IP 地址,而不使用 c 中的任何 DNS 函数。由于某种原因,此代码有效,但仅适用于 google、facebook 和 imgur.com。例如,如果我输入“www.facebook.com”,它会给出正确的 IP 地址,并且与其他两个站点相同,但输入的任何其他站点都会给出错误的 IP 地址。有谁知道我做错了什么?

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>

#define PORT 53
#define SITEMAXLEN 40
#define QUERYMAXLEN SITEMAXLEN + 16
#define DNSRESULTMAXLEN 100
#define IPARRAYLEN 20

typedef unsigned char char_type;

void enter_record(char_type* query, char_type* site, int* let_set);
void print_query(char_type* query, int len);
void send_query(char_type* query, char_type* result, int len, int* answer_set);
char_type* parse_result(char_type* result, int query_len, char_type* ip);
void print_ip(int* ip);

int sock;
struct sockaddr_in server_addr;
struct sockaddr_in from_addr;
int main(int argc, char_type** argv) {
    char_type* site;
    char_type* query;
    char_type* dns_result;
    char_type* ip;

    int query_len;
    int answer_len;

    site = (char_type*)malloc(SITEMAXLEN * sizeof(char_type));
    query = (char_type*)malloc((QUERYMAXLEN) * sizeof(char_type));
    dns_result = (char_type*)malloc(DNSRESULTMAXLEN * sizeof(char_type));
    ip = (char_type*)malloc(IPARRAYLEN * sizeof(char_type));

    if((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
        fprintf(stderr, "error creating socket to the name server\n");
        return 1;
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("208.67.220.220");
    server_addr.sin_port = htons(PORT);

    printf("enter in website name\n");
    scanf("%s", site);

    enter_record(query, site, &query_len);
    print_query(query, query_len);
    send_query(query, dns_result, query_len, &answer_len);
    printf("\n\nresult is\n");
    print_query(dns_result, answer_len);
    parse_result(dns_result, query_len, ip);
    printf("\n\nip address is: %s", ip);

    return 0;
}

void enter_record(char_type* query, char_type* site, int* len_set) {
    char_type* loc;
    char_type* len;
    char_type* tld;
    int query_end;

    loc = strchr(site, '.'); //. after www
    len = strchr(loc + 1, '.'); //. after the hostname
    *loc = (char_type)(len - loc - 1);
    *len = (char_type)0x0003;

    query[0] = 'B';
    query[1] = 'L';
    query[2] = (char_type)0x0001;
    query[3] = (char_type)0x0000;
    query[4] = (char_type)0x0000;
    query[5] = (char_type)0x0001;
    query[6] = (char_type)0x0000;
    query[7] = (char_type)0x0000;
    query[8] = (char_type)0x0000;
    query[9] = (char_type)0x0000;
    query[10] = (char_type)0x0000;
    query[11] = (char_type)0x0000;
    query[12] = (char_type)0x0003;
    strncat(query + 13, site, strlen(site));

    query_end = 13 + strlen(site);
    query[query_end] = 0x0000;
    query[query_end + 1] = 0x0000;
    query[query_end + 2] = 0x0001;
    query[query_end + 3] = 0x0000;
    query[query_end + 4] = 0x0001;

    *len_set = query_end + 5;
}

void print_query(char_type* query, int len) {
    int i;

    printf("in hex\n");
    for(i = 0; i < len; i++) {
        printf("%02hhX ", query[i]);

        if(i % 16 == 0 && i != 0) {
            printf("\n");
        }
    }
    printf("\nin ascii\n");

    for(i = 0; i < len; i++) {
        printf("%c ", query[i] < (char_type)0x21 ? '.' : query[i]);

        if(i % 16 == 0 && i != 0) {
            printf("\n");
        }
    }
    printf("\n");
}

void send_query(char_type* query, char_type* result, int len, int* set_len) {
    int from_size;
    int response_len;

    if(sendto(sock, query, len, 0,
    (struct sockaddr*)&server_addr, sizeof(server_addr)) != len) {
        //message couldn't be sent
        fprintf(stderr, "couldn't send the query\n");
        return;
    }

    from_size = sizeof(from_addr);
    while((response_len = recvfrom(sock, result, DNSRESULTMAXLEN, 0,
    (struct sockaddr*)&from_addr, &from_size)) < 0) {
    }

    //null terminate the buffer
    //printf("%d\n", response_len);
    //result[response_len] = '\0';

    *set_len = response_len;
}

char_type* parse_result(char_type* result, int query_len, char_type* ip) {
    /*int offset = -1;

    //offset = query_len + 32;
    int count = 0;
    while(count < 3) {
        offset++;
        if(result[offset] == 0x00C0) {
            count++;
        }
    }

    offset += 32;*/

    int offset = -1;
    int found = 0;
    while(found == 0) {
        offset++;
        if(result[offset] == 0x00C0) {
            if(result[offset + 3] == 0x0001 && result[offset + 2] == 0x0000) {
                offset = offset + result[offset + 1];
                found = 1;
            }
        }
    }

    sprintf(ip, "%d.%d.%d.%d\n", 
    result[offset], result[offset + 1], 
    result[offset + 2], result[offset + 3]);

    return ip;
}

最佳答案

parse_result()中,不是跳转result[offset + 1]字节,而是跳转12个字节。

            if(result[offset + 3] == 0x0001 && result[offset + 2] == 0x0000) {
                /* ********
                offset = offset + result[offset + 1];
                ******** */
                offset = offset + 12;
                found = 1;
            }

结果[offset + 1]处的值是从消息开头到名称的偏移量,而不是IP地址。对于 yahoo,偏移量最终指向“any-fp.wa1.b.yahoo.com”

<小时/>

它适用于 facebook(和其他),因为答案中姓名的偏移量为 12。当问题中的姓名与答案中的姓名相同时,偏移量为 12。

<小时/>

更新

并非所有 DNS 答案都以指针 (0xC0 0x??) 开始每个答案部分。他们可能有一个标签,而你的程序将惨败。即使指针的第一个字节也不需要是 0xC0:要求是设置前 2 位。

关于c - 从 DNS 响应获取 IP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5280345/

相关文章:

c - libxml2 解析问题 : Namespace error

c - c中的strcat模拟

grails - 域有一些共同的领域,扩展域或嵌入?

linux - 无法根据主机名 ping 到 ip

NHibernate - 从数据库生成域

c - 如何阻止程序将多个字符存储到 char 变量中?

c - uint8_t 双指针的段错误

windows-7 - 重新启动/恢复 Windows 后 DNS 查找出现长时间延迟

python - 使用 Django、Heroku 和 Name.com 的错误请求 (400)

c - 在哪里使用这个 while 循环?