res_query не следует res_init, отменяет то, что было установлено - PullRequest
0 голосов
/ 16 февраля 2019

Пожалуйста, рассмотрите этот код, где я звоню res_init(), и установив 192.168.1.77 в качестве единственного сервера имен.Однако, когда res_query запускается, очевидно, он повторно выполняет res_init и возвращается к первым 3 серверам имен из resolv.conf.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <resolv.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>


int main (int argc, char *argv[])
{
    u_char nsbuf[4096];
    char dispbuf[4096];
    ns_msg msg;
    ns_rr rr;
    int i, j, l;

    if (argc < 2) {
        printf ("Usage: %s <domain>[...]\n", argv[0]);
        exit (1);
    }
    res_init();
    char str[INET_ADDRSTRLEN];

    for (int i = 0 ; i < _res.nscount; i++){
        inet_ntop(AF_INET,&(_res.nsaddr_list[i].sin_addr.s_addr) , str, INET_ADDRSTRLEN);
        printf("Before:  nameserver %d : %s\n", i,str);
    }

    // set to use 192.168.1.77 as nameserver
     _res.nscount = 1;
     _res.nsaddr_list[0].sin_family = AF_INET;
     _res.nsaddr_list[0].sin_addr.s_addr = inet_addr("192.168.1.77");
     _res.nsaddr_list[0].sin_port = htons(53);

    for (int i = 0 ; i < _res.nscount; i++){
        inet_ntop(AF_INET,&(_res.nsaddr_list[i].sin_addr.s_addr) , str, INET_ADDRSTRLEN);
        printf("After:  nameserver %d : %s\n", i,str);
    }

    for (i = 1; i < argc; i++) {
    printf("ns count before res_query %d\n", _res.nscount);
       l = res_query (argv[i], ns_c_any, ns_t_a, nsbuf, sizeof (nsbuf));
    printf("ns count after res_query %d\n", _res.nscount);

    for (int i = 0 ; i < _res.nscount; i++){
        inet_ntop(AF_INET,&(_res.nsaddr_list[i].sin_addr.s_addr) , str, INET_ADDRSTRLEN);
        printf("After res_query:  nameserver %d : %s\n", i,str);
    }

        if (l < 0) {
            perror (argv[i]);
        } else {
            ns_initparse (nsbuf, l, &msg);
            printf ("---------------------\n%s :\n", argv[i]);
            l = ns_msg_count (msg, ns_s_an);
            for (j = 0; j < l; j++) {
                ns_parserr (&msg, ns_s_an, j, &rr);
                ns_sprintrr (&msg, &rr, NULL, NULL, dispbuf, sizeof (dispbuf));
                printf ("%s\n", dispbuf);
            }
        }
    }

    exit (0);
}

Ниже приведен вывод, где мы можем увидеть это:

$ ./a.out cnn.com
Before:  nameserver 0 : 127.0.0.1
Before:  nameserver 1 : 192.168.1.1
Before:  nameserver 2 : 1.1.1.1
After:  nameserver 0 : 192.168.1.77
ns count before res_query 1
ns count after res_query 3
After res_query:  nameserver 0 : 127.0.0.1     /// <----- these... 
After res_query:  nameserver 1 : 192.168.1.1   /// <----- these... 
After res_query:  nameserver 2 : 1.1.1.1       /// <----- these... 
---------------------
cnn.com :
cnn.com.        58S IN A    151.101.129.67
cnn.com.        58S IN A    151.101.1.67
cnn.com.        58S IN A    151.101.65.67
cnn.com.        58S IN A    151.101.193.67

Что я делаю не так?Как я могу сделать res_query, не делая еще один res_init() за моей спиной?

Обновление 1

Спасибо за подробный и подробный ответ.Ниже я могу сохранить добавленные мной серверы имен после того, как я изменил их на res_init, а затем res_mkquery().

Однако, это все еще большая ошибка:

 $ time ./warm cnn.com 8.8.8.8
PACKETSZ: 512
Before:  nameserver 0 : 127.0.0.1
Before:  nameserver 1 : 192.168.1.1
Before:  nameserver 2 : 1.1.1.1
queryLen is 25
setting 8.8.8.8 as nameserver
After:  nameserver 0 : 8.8.8.8
ns count before res_query 1
res_query failed!
ns count after res_query 1
After res_query:  nameserver 0 : 8.8.8.8
cnn.com: Connection timed out
ns count before res_query 1
res_query failed!
ns count after res_query 1
After res_query:  nameserver 0 : 8.8.8.8
8.8.8.8: Connection timed out

real    0m0.131s
user    0m0.008s
sys     0m0.000s

Хотя я вижу трафик, идущий к 8.8.8.8.

warm.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <resolv.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>


int main (int argc, char *argv[])
{
    u_char nsbuf[4096];
    u_char warm_buf[4096];
    char dispbuf[4096];

    //char nameserver[] = "127.0.0.1";
    //char nameserver[] = "192.168.1.166";
    char *nameserver = argv[2];
    ns_msg msg;
    ns_rr rr;
    int i, j, l;

    if (argc < 2) {
        printf ("Usage: %s <domain>[...]\n", argv[0]);
        exit (1);
    }


    char str[INET_ADDRSTRLEN];

     //res_init();

    printf("PACKETSZ: %d\n",PACKETSZ);
    for (int i = 0 ; i < _res.nscount; i++){
        inet_ntop(AF_INET,&(_res.nsaddr_list[i].sin_addr.s_addr) , str, INET_ADDRSTRLEN);
        printf("Before:  nameserver %d : %s\n", i,str);
    }
     int queryLen = res_mkquery(
                     ns_o_query,      /* regular query         */
                     argv[1],          /* the domain to look up */
                     ns_c_any,         /* Internet type         */
                     ns_t_a,        /* Look up an A record */
                     (u_char *)NULL,  /* always NULL       */
                     0,               /* length of NULL        */
                     (u_char *)NULL,  /* always NULL       */
                     warm_buf,/* buffer for the query  */
                     sizeof(warm_buf));  /* size of the buffer    */


    printf("queryLen is %d\n" , queryLen);
    //printf("queryLen is %d\n" , strlen(warm_buf) );

    // set to use 192.168.1.77 as nameserver
    printf("setting %s as nameserver\n", nameserver);
     _res.nscount = 1;
     _res.nsaddr_list[0].sin_family = AF_INET;
     _res.nsaddr_list[0].sin_addr.s_addr = inet_addr(nameserver);
     _res.nsaddr_list[0].sin_port = htons(53);

    for (int i = 0 ; i < _res.nscount; i++){
        inet_ntop(AF_INET,&(_res.nsaddr_list[i].sin_addr.s_addr) , str, INET_ADDRSTRLEN);
        printf("After:  nameserver %d : %s\n", i,str);
    }

    for (i = 1; i < argc; i++) {
    printf("ns count before res_query %d\n", _res.nscount);
       l = res_query (argv[i], ns_c_any, ns_t_a, nsbuf, sizeof (nsbuf));
    printf(" -------------------------- >>> l is %d\n", l);
    printf("ns count after res_query %d\n", _res.nscount);

    for (int i = 0 ; i < _res.nscount; i++){
        inet_ntop(AF_INET,&(_res.nsaddr_list[i].sin_addr.s_addr) , str, INET_ADDRSTRLEN);
        printf("After res_query:  nameserver %d : %s\n", i,str);
    }

        if (l < 0) {
            perror (argv[i]);
        } else {
            ns_initparse (nsbuf, l, &msg);
            printf ("---------------------\n%s :\n", argv[i]);
            l = ns_msg_count (msg, ns_s_an);
            for (j = 0; j < l; j++) {
                ns_parserr (&msg, ns_s_an, j, &rr);
                ns_sprintrr (&msg, &rr, NULL, NULL, dispbuf, sizeof (dispbuf));
                printf ("%s\n", dispbuf);
            }
        }
    }

    exit (0);
}

1 Ответ

0 голосов
/ 16 февраля 2019

Debian 9 (растяжка) и более ранние версии используют пользовательский патч только для нисходящего потока для автоматической перезагрузки /etc/resolv.conf, если он изменился .Как написано в этом патче, кеш обновляется только при вызове внутренней функции __res_maybe_init, чего не делает res_init.Это означает, что вызов реальных функций распознавателя (таких как res_query) приводит к холодному кешу в __res_maybe_init, происходит перезагрузка, и ваши изменения в _res отбрасываются.

Реализация в восходящем направлении реализована автоматический /etc/resolv.conf перезагрузка glibc 2.26 .Восходящий подход очень отличается и пытается обработать _res исправления приложениями:

  • Существует унифицированный кэш, и вызов res_init обновляет его.
  • Если перезагрузка/etc/resolv.conf необходимо, glibc проверяет, отражают ли значения _res то, что было ранее загружено из /etc/resolv.conf, и если значения другие, новое содержимое /etc/resolv.conf равно , а не применено и текущие настройки _res не перезаписываются.
  • Существует новая опция распознавателя RES_NORELOAD / noreload, которая полностью отключает автоматическую перезагрузку.

Эти изменениябудет частью будущей версии Debian 10 (buster).

Если вы не хотите обновлять glibc, вы можете значительно снизить вероятность того, что специфичный для Debian код перезагрузки отменяет ваши изменения, если вы инициируете вызовдо __res_maybe_init до исправления _res вместо вызова res_init.Один из способов сделать это без отправки запроса - вызвать res_mkquery с некоторыми фиктивными аргументами.Это предварительно прогреет кэш, и перезагрузка произойдет только в том случае, если файл /etc/resolv.conf будет изменен на диске (в этот момент ваши изменения все равно будут перезаписаны - я не думаю, что есть способ предотвратить это со старым Debianверсии).

...