如何使用SOCK_RAW以C语言创建DNS查询程序包?

时间:2019-04-04 14:51:29

标签: c sockets dns udp

我正在学习有关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中却完全不同。我的程序发送的数据包只能被Wireshark识别为UDP,DNS服务器无法返回结果(我认为DNS服务器可能不理解此请求),但是我使用的数据包{{1 }}可以识别为DNS,并且还返回了结果。

问题是,我的代码有问题吗?

谢谢。

0 个答案:

没有答案