Отправить запрос на DNS-сервер через UDP-сокет - PullRequest
1 голос
/ 30 октября 2019

В настоящее время я изучаю DNS-серверы в своих лекциях. Поскольку у нас были сокеты Беркли и раньше, я подумал, что могу объединить свои знания и попытаться отправить запрос в DNS через сокет и обработать ответ самостоятельно. Я написал небольшую программу на C для Linux, которая принимает 3 аргумента: IP-порт (скорее всего, 53, как я выяснил) и данные, которые должны быть отправлены на данный сервер.

Сначала я попытался просто отправитьимя хоста в виде строки. Затем я обнаружил RFC1034 , где становится ясно, что запрос является более сложным и содержит несколько полей. Однако я как-то не могу найти возможные значения, которые я могу заполнить в полях. Например, в разделе 6.2.1 в ссылке написано Header: OPCODE=SQUERY, тогда как OPCODE указан как 4-битное целое число, поэтому я считаю, что эти коды определяются реализацией и определяются DNS-сервером? Или я пропустил раздел RFC? Он только указывает, что тип и класс являются «закодированными 16-битными значениями».

, когда я пытаюсь вызвать мою программу, например,

client 8.8.8.8 53 www.example.com

для Google DNS, он фактически соединяется и отправляет данныено затем ждет ответа, который никогда не приходит. При использовании TCP я заметил, что DNS-сервер мгновенно закрывает соединение после получения доменного имени.

Как я теперь понимаю, мне нужно собрать запрос в соответствии с RFC с заголовком, содержащим код операции для запроса и вопрос. содержащий имя домена и дополнительные параметры, такие как тип и класс, в виде двоичной информации. Но где я могу получить возможные значения для OPCODE, QCLASS, QTYPE?

Я знаю, что getaddrinfo, по сути, делает это для меня, и что я уже могу получить IP из структуры sockaddr, но это вопрос экспериментов. и обучение.

Я ценю любую помощь:)

Моя программа

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>

int main (int argc, char **argv) {
    if (argc < 4) {
        printf("Error to few arguments.\nUsage: client <ip/host name> <port> <query>\n");
        exit(1);
    }

    struct addrinfo *server_addr;
    struct addrinfo hint;
    memset(&hint, 0, sizeof(hint));

    hint.ai_family = AF_UNSPEC;
    hint.ai_socktype = SOCK_DGRAM;
    hint.ai_protocol = IPPROTO_UDP;

    printf("getting server info\n");
    /* Heh...ironic */
    int err = getaddrinfo(argv[1], argv[2], &hint, &server_addr);
    if (err != 0) {
        perror("Error while getting server address info");
        exit(1);
    }

    int client;
    /* Iterate over the provided addresses and try to establish a connection */
    for (struct addrinfo *cur_info = server_addr; cur_info != NULL; cur_info = cur_info->ai_next) {
        /* Create corresponding socket */
        client = socket(cur_info->ai_family, cur_info->ai_socktype, cur_info->ai_protocol);

        printf("Trying to establish connection\n");
        err = connect(client, cur_info->ai_addr, sizeof(*(cur_info->ai_addr)));
        /* If connection was established leave loop */
        if (err == 0) break;
        /* If an error occured notify */
        if (err == -1) perror("Error while connecting");
        close(client);
        /* If the current info was the last one consider connection failed */
        if (cur_info->ai_next == NULL) {
            printf("Error could not connect to any service\n");
            exit(1);
        }

    }
    /* Release information as it is no longer needed */
    freeaddrinfo(server_addr);

    printf("Sending request\n");
    err = send(client, argv[3], strlen(argv[3]), 0);
    if (err < 1) {
        perror("Error while sending request");
        exit(1);
    }
    printf("%d b sent\n", err);

    char buffer[512];
    int bytes_received = 0;

    printf("receiving answer\n");
    while ((bytes_received = recv(client, buffer, sizeof(buffer), 0)) != 0) {
        if (bytes_received == -1) {
            perror("Error while receiving message from server");
            break;
        }
        fwrite(buffer, bytes_received, 1, stdout);
    }

    close(client);
}

1 Ответ

2 голосов
/ 30 октября 2019

Функция res_mkquery, которая изначально была из BIND / libresolv, но присутствует почти во всех версиях libc (хотя она и не определена ни в одном стандарте), может создавать пакеты запросов для вас, и вы можете проверить вывод безотправив это. Поиграть с ним было бы очень информативно.

Что касается вашего конкретного вопроса о OPCODE и формате DNS-запроса в целом, вы должны ссылаться на RFC 1035 , а не 1034;последнее - это принципы более высокого уровня, а не протокол. На странице 26 определены значения OPCODE, а 0 (стандартный запрос "SQUERY") является единственным значимым.

Обратите внимание, что функции ns_* из <arpa/nameser.h> (также нестандартные, но широко доступные) могутиспользоваться для анализа пакета запроса или полученных ответов, отправив его на сервер имен и получив ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...