Как создать пакет DNS-запросов на языке C с помощью SOCK_RAW? - PullRequest
0 голосов
/ 04 апреля 2019

Я изучаю программирование TCP и UDP.Чтобы лучше понять и использовать эти протоколы, я написал небольшую программу, в которой модуль будет отправлять запрос DNS-запроса, который является частью моего кода.

Я определю заголовок IP, заголовок UDP и DNSсам заголовок, затем часть данных нашего DNS-запроса, например:

typedef struct query
{
    //           0123456789
    // test name github.com
    char name[10];
    struct question *question;
} Query, *pQuery;

typedef struct question
{
    unsigned short int qtype;
    unsigned short int qclass;
} Question, *pQuestion;

// DNS header structure
typedef struct dns_header
{
    unsigned short int id; // identification number

    // flag
    // unsigned short int == uint16_t
#if __BYTE_ORDER == __BIG_ENDIAN
    unsigned short int qr : 1;
    unsigned short int opcode : 4;
    unsigned short int aa : 1;
    unsigned short int tc : 1;
    unsigned short int rd : 1;
    unsigned short int ra : 1;
    unsigned short int z : 3;
    unsigned short int rcode : 4;
#elif __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned short int rd : 1;
    unsigned short int tc : 1;
    unsigned short int aa : 1;
    unsigned short int opcode : 4;
    unsigned short int qr : 1;
    unsigned short int rcode : 4;
    unsigned short int z : 3;
    unsigned short int ra : 1;
#endif

    unsigned short qcount;  // question count
    unsigned short ancount; // answer record count
    unsigned short nscount; // name server count
    unsigned short adcount; // additional record count

} DNSHeader, *pDNSHeader;


static int SendDNS(const pDNSStruct ds, const int debug_level)
{
    // Perform a DNS query by sending a packet

    int socket_fd;
    socket_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (socket_fd < 0)
    {
        return 1;
    }

    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    // dst
    sin.sin_port = htons((int)ds->src_port); // set the destination address
    sin.sin_addr.s_addr = inet_addr(ds->src_ip); // set the port

    char *datagram;
    //char *data;
    size_t pksize = sizeof(struct ip) + sizeof(struct udphdr) + sizeof(DNSHeader) + sizeof(Query);
    datagram = (char *)malloc(pksize);

    struct ip *iph;
    iph = (struct ip *)datagram;

    struct udphdr *udph;
    memset(datagram, 0, pksize);
    // filed the data

    int one = 1;
    const int *val = &one;
    if (setsockopt(socket_fd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0)
    {
        //exit(0);
        return 1;
    }
    // entete ip
    iph->ip_v = 4;
    iph->ip_hl = 5;
    iph->ip_tos = 0;
    iph->ip_len = pksize;
    iph->ip_ttl = 255;
    iph->ip_off = 0;
    iph->ip_id = sizeof(45);
    iph->ip_p = IPPROTO_UDP;
    iph->ip_sum = 0; // a remplir aprés
    iph->ip_src.s_addr = inet_addr(ds->src_ip);
    iph->ip_dst.s_addr = inet_addr(ds->dst_ip);

    udph = (struct udphdr *)(datagram + sizeof(struct ip));
    // entete udp
    udph->uh_sport = htons(ds->src_port);
    udph->uh_dport = htons(ds->dst_port);
    udph->uh_ulen = htons(sizeof(struct udphdr));
    // use the UDP to send the data
    pDNSHeader dnsh = (pDNSHeader)(datagram + sizeof(struct ip) + sizeof(struct udphdr));
    // set the DNS structure to standard queries
    dnsh->id = (unsigned short)htons(getpid());
    dnsh->qr = 0;     // this is a query
    dnsh->opcode = 0; // this is a standard query
    dnsh->aa = 0;     // not authoritative
    dnsh->tc = 0;     // this message is not truncated
    dnsh->rd = 1;     // recursion desired
    dnsh->ra = 0;     // recursion not available! hey we dont have it (lol)
    dnsh->z = 0;
    dnsh->rcode = 0;

    dnsh->qcount = htons(1); //we have only 1 question
    dnsh->ancount = 0;
    dnsh->nscount = 0;
    dnsh->adcount = 0;

    // point to the query portion
    // filed the data
    pQuery query = (pQuery)(datagram + sizeof(struct ip) + sizeof(struct udphdr) + sizeof(DNSHeader));
    // DNS_QUERY_NAME_DEFAULT in here is "github.com"
    memcpy(query->name, DNS_QUERY_NAME_DEFAULT, strlen(DNS_QUERY_NAME_DEFAULT));

    pQuestion question = (pQuestion)(datagram + sizeof(struct ip) + sizeof(struct udphdr) + sizeof(DNSHeader) + sizeof(Query));
    question->qtype = htons(DNS_QUERY_TYPE_DEFAULT); //type of the query , A , MX , CNAME , NS etc
    question->qclass = htons(1);                     //its internet (lol)

    if (sendto(socket_fd, datagram, pksize, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        return 1;
    }

    free(datagram);
    close(socket_fd);
    return 0;
}

Теперь эта программа работает нормально, но есть проблема, которая не была решена.Когда я использовал Wireshark для проверки отправленных пакетов, я обнаружил некоторые проблемы.

Это пакет, отправленный программой.

0000   dc fe 18 84 cb e4 3c f8 62 7b 0f d9 08 00 45 00   Üþ..Ëä<øb{.Ù..E.
0010   00 40 04 00 00 00 ff 11 11 1f c0 a8 01 01 72 72   .@....ÿ...À¨..rr
0020   72 72 00 50 00 35 00 08 ff 72 75 6a 01 00 00 01   rr.P.5..ÿruj....
0030   00 00 00 00 00 00 67 69 74 68 75 62 2e 63 00 01   ......github.c..
0040   00 01 00 00 00 00 00 00 00 00 00 00 00 00         ..............

Вы видите, что эти данные не распознаютсяв качестве DNS это был только UDP.

program packets

Тогда это пакет, сгенерированный командой nslookup github.com.

0000   dc fe 18 84 cb e4 3c f8 62 7b 0f d9 08 00 45 00   Üþ..Ëä<øb{.Ù..E.
0010   00 38 3d 03 40 00 40 11 7a 54 c0 a8 01 0c c0 a8   .8=.@.@.zTˬ..ˬ
0020   01 01 bd a0 00 35 00 24 aa de 07 cd 01 00 00 01   ..½ .5.$ªÞ.Í....
0030   00 00 00 00 00 00 06 67 69 74 68 75 62 03 63 6f   .......github.co
0040   6d 00 00 01 00 01                                 m.....

command packets

Это выглядит очень похоже, но в Wireshark оно совершенно другое.Пакеты, отправленные моей программой, могут быть распознаны как UDP только по Wireshark, и DNS-сервер не может вернуть результат (я думаю, DNS-сервер может не понимать этот запрос), но используемый мной пакет nslookup может бытьраспознается как DNS, и результат также возвращается.

Итак, вопрос в том, что-то не так с моим кодом?

Спасибо.

...