关于dns报文格式和一些基础知识本文不表,网上一堆。
https://blog.csdn.net/liao152/article/details/45252387

下面来点干货,是一个项目中,需要对请求的dns进行黑白名单划分,并且对解析结果(ip地址)同样进行黑白名单划分。
1, 系统中预置一个黑名单列表,一个白名单列表(采用字典树);
2, 系统中预置一个黑名单ipset,一个白名单ipset;
3, 解析dns请求报文,并对解析的dns进行匹配,如果匹配黑名单,则重定向该域名请求到特定的dns服务器;同理,如果匹配到白名单,则重定向到另一个dns服务器。
4,解析dns的应答报文,对解析的dns进行匹配,如果匹配到黑/白名单,那么:1,如果应答记录中是cname,那么将此cname域名添加到对应的域名记录中;2,如果应答记录中是ip地址,那么将该IP地址添加到对应的ipset中。
5, 业务报文到达时,通过匹配黑白名单的ipset可以进行进一步分流。

下面的代码是该实现中,dns解析部分:
dns.c

点击(此处)折叠或打开

  1. /*********************************************************************************************
  2. * Description: kernel module to parse dns request and replay packages
  3. *
  4. * Author: Husker <huskerTang@gmail.com>
  5. *
  6. * Date: 2019-2-12
  7. ***********************************************************************************************/

  8. #include <linux/fs.h>
  9. #include <linux/module.h>
  10. #include <linux/proc_fs.h>
  11. #include <linux/seq_file.h>
  12. #include <linux/types.h>
  13. #include <linux/version.h>
  14. #include <linux/timer.h>
  15. #include <linux/ip.h>
  16. #include <linux/slab.h>
  17. #include <linux/list.h>
  18. #include "dns.h"

  19. static struct kmem_cache* dns_pkg_cache; //__read_mostly;
  20. static struct kmem_cache* dns_query_cache; // __read_mostly;
  21. static struct kmem_cache* dns_record_cache; // __read_mostly;

  22. void DNS_finit(void)
  23. {
  24.     if (dns_pkg_cache)
  25.     {
  26.         kmem_cache_destroy(dns_pkg_cache);
  27.     }
  28.     if (dns_query_cache)
  29.     {
  30.         kmem_cache_destroy(dns_query_cache);
  31.     }
  32.     if (dns_record_cache)
  33.     {
  34.         kmem_cache_destroy(dns_record_cache);
  35.     }
  36. }

  37. int DNS_init(void)
  38. {
  39.     dns_pkg_cache = kmem_cache_create("dns_pkg_cache",
  40.      sizeof(struct dns_package),
  41.      0, 0, NULL);
  42.     if (dns_pkg_cache == NULL)
  43.     {
  44.         printk(KERN_ERR "[DNS] failed to create dns package cache\n");
  45.         goto ERROR_OUT;
  46.     }
  47.     dns_query_cache = kmem_cache_create("dns_query_cache",
  48.      sizeof(struct dns_query),
  49.      0, 0, NULL);
  50.     if (dns_query_cache == NULL)
  51.     {
  52.         printk(KERN_ERR "[DNS] failed to create dns query node cache\n");
  53.         goto ERROR_OUT;
  54.     }

  55.     dns_record_cache = kmem_cache_create("dns_record_cache",
  56.      sizeof(struct dns_resource),
  57.      0, 0, NULL);
  58.     if (dns_record_cache == NULL)
  59.     {
  60.         printk(KERN_ERR "[DNS] failed to create dns resource recorder cache\n");
  61.         goto ERROR_OUT;
  62.     }
  63.     return 0;

  64. ERROR_OUT:
  65.     DNS_finit();
  66.     return -1;
  67. }


  68. static int parse_domain_name(uint8_t* pkg, uint16_t pkglen, uint8_t* pstart, uint8_t* name, uint16_t* nmlen)
  69. {
  70.     int ret;
  71.     uint8_t tnm[128] = {0};
  72.     uint16_t tnm_len = 128;
  73.     int cnt = 0;
  74.     int total = 0;
  75.     uint16_t offset = 0;

  76.     uint8_t* ptr;
  77.     uint16_t len = 0;

  78.     if (DNS_HDR_LEN >= pkglen || pstart < pkg || pstart >= (pkg + pkglen) || !name || !nmlen || *nmlen <= 0)
  79.     {
  80.         return -1;
  81.     }

  82.     total = pstart - pkg;
  83.     ptr = tnm;

  84.     while (*pstart != 0)
  85.     {
  86.         if (*pstart >= DNS_COMMPRESS_FLAG)
  87.         {
  88.             offset = (*pstart) * 256 + *(pstart + 1) - 0xc000;
  89.             if (offset >= pkglen)
  90.             {
  91.                 return -1;
  92.             }

  93.             tnm_len -= len;
  94.             ret = parse_domain_name(pkg, pkglen, pkg + offset, ptr, &tnm_len);
  95.             if (ret < 0 )
  96.             {
  97.                 return ret;
  98.             }
  99.             cnt += 2;
  100.             len += tnm_len;
  101.             goto success_out;
  102.         }
  103.         else
  104.         {
  105.             uint16_t dm_part_len = *pstart;

  106.             if ( (pstart + dm_part_len >= pkg + pkglen) || (128 - len <= 0))
  107.             {
  108.                 return -1;
  109.             }

  110.             memcpy(ptr, pstart + 1, dm_part_len);
  111.             ptr += dm_part_len;
  112.             len += dm_part_len;

  113.             // add '.' to every part of domain
  114.             *ptr = '.';
  115.             ptr++;
  116.             len++;

  117.             cnt += dm_part_len + 1;
  118.             pstart += dm_part_len + 1;
  119.         }
  120.     }

  121.     if (*pstart == 0)
  122.     {
  123.         cnt++;
  124.     }

  125. success_out:
  126.     if (tnm[len - 1] == '.' )
  127.     {
  128.         tnm[len - 1] = 0;
  129.         len--;
  130.     }
  131.     if (*nmlen <= len )
  132.     {
  133.         return -1;
  134.     }

  135.     memcpy(name, tnm, len);
  136.     name[len] = 0;
  137.     *nmlen = len;
  138.     return cnt;
  139. }

  140. static int parse_query(uint8_t* pkg, uint16_t pkglen, uint8_t* pstart, struct dns_query* query)
  141. {
  142.     int ret;
  143.     uint16_t nmlen = 128;
  144.     uint8_t* ptr = pstart;
  145.     int cnt = 0;

  146.     // parse name
  147.     ret = parse_domain_name(pkg, pkglen, ptr, query->name, &nmlen);
  148.     if (ret < 0 )
  149.     {
  150.         return ret;
  151.     }
  152.     ptr += ret;
  153.     cnt += ret;


  154.     // parse domain type
  155.     if (ptr + 1 >= pkg + pkglen )
  156.     {
  157.         return -1;
  158.     }
  159.     query->type = (*ptr) * 256 + *(ptr + 1);
  160.     ptr += 2;
  161.     cnt += 2;

  162.     // parse domain class
  163.     if (ptr + 1 >= pkg + pkglen )
  164.     {
  165.         return -1;
  166.     }
  167.     query->class = (*ptr) * 256 + *(ptr + 1);
  168.     ptr += 2;
  169.     cnt += 2;
  170.     return cnt;
  171. }

  172. static int parse_resource(uint8_t* pkg, uint16_t pkglen, uint8_t* pstart, struct dns_resource* res)
  173. {
  174.     int ret;
  175.     uint16_t nmlen = 128;
  176.     uint8_t* ptr = pstart;
  177.     int cnt = 0;

  178.     // parse name
  179.     ret = parse_domain_name(pkg, pkglen, ptr, res->name, &nmlen);
  180.     if (ret < 0 )
  181.     {
  182.         return ret;
  183.     }
  184.     ptr += ret;
  185.     cnt += ret;


  186.     // parse domain type
  187.     if (ptr + 1 >= pkg + pkglen )
  188.     {
  189.         return -1;
  190.     }
  191.     res->type = (*ptr) * 256 + *(ptr + 1);
  192.     ptr += 2;
  193.     cnt += 2;

  194.     // parse domain class
  195.     if (ptr + 1 >= pkg + pkglen )
  196.     {
  197.         return -1;
  198.     }
  199.     res->class = (*ptr) * 256 + *(ptr + 1);
  200.     ptr += 2;
  201.     cnt += 2;

  202.     // parse ttl
  203.     if (ptr + 3 >= pkg + pkglen )
  204.     {
  205.         return -1;
  206.     }
  207.     res->ttl = ((*ptr) << 24) + (*(ptr + 1) << 16) + (*(ptr + 2) << 8) + (*(ptr + 3));
  208.     ptr += 4;
  209.     cnt += 4;

  210.     // parse date len
  211.     if (ptr + 1 >= pkg + pkglen)
  212.     {
  213.         return -1;
  214.     }
  215.     res->data_len = (*ptr) * 256 + *(ptr + 1);
  216.     if (res->data_len > 256)
  217.     {
  218.         return -1;
  219.     }
  220.     ptr += 2;
  221.     cnt += 2;

  222.     // parse date
  223.     if (ptr + res->data_len > pkg + pkglen)
  224.     {
  225.         return -1;
  226.     }
  227.     if (res->type == TYPE_CNAME)
  228.     {
  229.         uint16_t cnlen = 256;
  230.         int offset = 0;
  231.         offset = parse_domain_name(pkg, pkglen, ptr, res->data, &cnlen);
  232.         if (offset < 0)
  233.         {
  234.             return -1;
  235.         }
  236.         ptr += offset;
  237.         cnt += offset;
  238.     }
  239.     else
  240.     {
  241.         memcpy(res->data, ptr, res->data_len);
  242.         ptr += res->data_len;
  243.         cnt += res->data_len;
  244.     }
  245.     return cnt;
  246. }

  247. static void free_pkg_recoders(struct list_head* hd)
  248. {
  249.     struct dns_resource* rcd;
  250.     struct dns_resource* tmp;

  251.     if (list_empty(hd))
  252.     {
  253.         return;
  254.     }
  255.     list_for_each_entry_safe(rcd, tmp, hd, list)
  256.     {
  257.         list_del(&rcd->list);
  258.         kmem_cache_free(dns_record_cache, rcd);

  259.     }
  260. }
  261. static void free_pkg_querys(struct list_head* hd)
  262. {
  263.     struct dns_query* query;
  264.     struct dns_query* tmp;
  265.     if (list_empty(hd))
  266.     {
  267.         return;
  268.     }
  269.     list_for_each_entry_safe(query, tmp, hd, list)
  270.     {
  271.         list_del(&query->list);
  272.         kmem_cache_free(dns_query_cache, query);
  273.     }
  274. }

  275. void DNS_free_package(struct dns_package* spkg)
  276. {
  277.     free_pkg_querys(&spkg->query_hd);
  278.     free_pkg_recoders(&spkg->answer_hd);
  279.     free_pkg_recoders(&spkg->auth_hd);
  280.     free_pkg_recoders(&spkg->addit_hd);
  281.     kmem_cache_free(dns_pkg_cache, spkg);
  282. }

  283. struct dns_package* DNS_new_package(void)
  284. {
  285.     struct dns_package* spkg = NULL;
  286.     spkg = (struct dns_package*)kmem_cache_alloc(dns_pkg_cache, GFP_ATOMIC);
  287.     if (NULL == spkg)
  288.     {
  289.         return NULL;
  290.     }
  291.     memset(spkg, 0, sizeof(struct dns_package));
  292.     INIT_LIST_HEAD(&spkg->query_hd);
  293.     INIT_LIST_HEAD(&spkg->answer_hd);
  294.     INIT_LIST_HEAD(&spkg->auth_hd);
  295.     INIT_LIST_HEAD(&spkg->addit_hd);
  296.     return spkg;
  297. }

  298. int DNS_parse_package(uint8_t* pkg, uint16_t pkglen, struct dns_package* spkg)
  299. {
  300.     struct dns_header* dnshdr;
  301.     struct dns_query* query;
  302.     struct dns_resource* rcd;
  303.     uint8_t* ptr;
  304.     int offset = 0;
  305.     int i;

  306.     if (!pkg || pkglen <= DNS_HDR_LEN)
  307.     {
  308.         return -1;
  309.     }

  310.     memcpy(&spkg->hdr, pkg, sizeof(struct dns_header));
  311.     dnshdr = &spkg->hdr;
  312.     dnshdr->id = ntohs(dnshdr->id);
  313.     dnshdr->q_count = ntohs(dnshdr->q_count);
  314.     dnshdr->ans_count = ntohs(dnshdr->ans_count);
  315.     dnshdr->auth_count = ntohs(dnshdr->auth_count);
  316.     dnshdr->add_count = ntohs(dnshdr->add_count);

  317.     if (dnshdr->q_count > 64 || dnshdr->add_count > 64 || dnshdr->ans_count > 64 || dnshdr->auth_count > 64)
  318.     {
  319.         printk( KERN_DEBUG "too much recoders, query, add, ans, auth\n",
  320.          dnshdr->q_count, dnshdr->add_count, dnshdr->ans_count, dnshdr->auth_count);
  321.         return -1;
  322.     }
  323.     ptr = pkg + DNS_HDR_LEN;

  324.     // parse querys
  325.     for(i = 0; i < dnshdr->q_count; i++)
  326.     {
  327.         query = (struct dns_query*)kmem_cache_alloc(dns_query_cache, GFP_ATOMIC);
  328.         if (NULL == query)
  329.         {
  330.             return -1;
  331.         }

  332.         offset = parse_query(pkg, pkglen, ptr, query);
  333.         if (offset <= 0 )
  334.         {
  335.             printk(KERN_DEBUG "fail to parse query recorder,err:%d, %d\n", offset, i);
  336.             return offset;
  337.         }
  338.         ptr += offset;
  339.         list_add(&query->list, &spkg->query_hd);
  340.     }

  341.     // parse answers
  342.     for (i = 0; i < dnshdr->ans_count; i++)
  343.     {
  344.         rcd = (struct dns_resource*)kmem_cache_alloc(dns_record_cache, GFP_ATOMIC);
  345.         if (NULL == rcd)
  346.         {
  347.             return -1;
  348.         }

  349.         offset = parse_resource(pkg, pkglen, ptr, rcd);
  350.         if (offset <= 0 )
  351.         {
  352.             printk(KERN_DEBUG "fail to parse answers, %d\n", i);
  353.             return offset;
  354.         }
  355.         ptr += offset;
  356.         list_add(&rcd->list, &spkg->answer_hd);
  357.     }

  358.     // parse auth
  359.     for (i = 0; i < dnshdr->auth_count; i++)
  360.     {
  361.         rcd = (struct dns_resource*)kmem_cache_alloc(dns_record_cache, GFP_ATOMIC);
  362.         if (NULL == rcd)
  363.         {
  364.             return -1;
  365.         }

  366.         offset = parse_resource(pkg, pkglen, ptr, rcd);
  367.         if (offset <= 0 )
  368.         {
  369.             printk(KERN_DEBUG "fail to parse auths, %d\n", i);
  370.             return offset;
  371.         }
  372.         ptr += offset;
  373.         list_add(&rcd->list, &spkg->auth_hd);
  374.     }

  375.     for (i = 0; i < dnshdr->add_count; i++)
  376.     {
  377.         rcd = (struct dns_resource*)kmem_cache_alloc(dns_record_cache, GFP_ATOMIC);
  378.         if (NULL == rcd)
  379.         {
  380.             return -1;
  381.         }
  382.         offset = parse_resource(pkg, pkglen, ptr, rcd);
  383.         if (offset <= 0 )
  384.         {
  385.             printk( KERN_DEBUG "fail to parse additions, %d\n", i);
  386.             return offset;
  387.         }
  388.         ptr += offset;
  389.         list_add(&rcd->list, &spkg->addit_hd);
  390.     }
  391.     return 0;
  392. }

dns.h

点击(此处)折叠或打开

  1. /*********************************************************************************************
  2. * Description: kernel module to parse a dns request or replay
  3. *
  4. * Author: Husker <huskerTang@gmail.com>
  5. *
  6. * Date: 2018-10-8
  7. ***********************************************************************************************/

  8. #ifndef __DNS_H__
  9. #define __DNS_H__

  10. typedef enum
  11. {
  12.     TYPE_A = 1,    // Ipv4 address
  13.     TYPE_NS = 2,    // Authoritative name server
  14.     TYPE_MD        = 3,    // Mail destination (Obsolete - use MX)
  15.     TYPE_MF        = 4,    // Mail forwarder (Obsolete - use MX)
  16.     TYPE_CNAME = 5,    // Canonical name for an alias
  17.     TYPE_SOA    = 6,    // Marks the start of a zone of authority
  18.     TYPE_MB        = 7,    // Mailbox domain name (EXPERIMENTAL)
  19.     TYPE_PTR = 12,    // domain name pointer
  20.     TYPE_MX = 15,    // Mail server
  21.     TYPE_TXT = 16,
  22.     TYPE_AAAA = 28,
  23.     TYPE_SRV = 33,
  24.     TYPE_SPF = 99,
  25. } qtype_t;

  26. #define DNS_HDR_LEN 12
  27. #define DNS_COMMPRESS_FLAG 0xc0

  28. #pragma pack(push, 1)
  29. struct dns_header
  30. {
  31.     uint16_t id;    // identification number
  32. #ifdef __BIG_ENDIAN
  33.     uint8_t qr: 1;
  34.     uint8_t opcode: 4;
  35.     uint8_t aa: 1;
  36.     uint8_t tc: 1;
  37.     uint8_t rd: 1;
  38.     uint8_t ra: 1;
  39.     uint8_t z: 3;
  40.     uint8_t rcode: 4;
  41. #else
  42.     uint8_t rd: 1;
  43.     uint8_t tc: 1;
  44.     uint8_t aa: 1;
  45.     uint8_t opcode: 4;
  46.     uint8_t qr: 1;
  47.     uint8_t rcode: 4;
  48.     uint8_t z: 3;
  49.     uint8_t ra: 1;
  50. #endif
  51.     uint16_t q_count;
  52.     uint16_t ans_count;
  53.     uint16_t auth_count;
  54.     uint16_t add_count;
  55. };
  56. #pragma pack(pop)

  57. struct dns_query
  58. {
  59.     struct list_head list;
  60.     uint8_t name[128];
  61.     uint16_t type;
  62.     uint16_t class;
  63. };

  64. struct dns_resource
  65. {
  66.     struct list_head list;
  67.     uint8_t name[128];
  68.     uint16_t type;
  69.     uint16_t class;
  70.     uint32_t ttl;
  71.     uint16_t data_len;
  72.     uint8_t data[256];
  73. };

  74. struct dns_package
  75. {
  76.     struct dns_header hdr;
  77.     struct list_head query_hd;
  78.     struct list_head answer_hd;
  79.     struct list_head auth_hd;
  80.     struct list_head addit_hd;
  81. };

  82. void DNS_free_package(struct dns_package* spkg);
  83. struct dns_package* DNS_new_package(void);
  84. int DNS_parse_package(uint8_t* pkg, uint16_t pkglen, struct dns_package* spkg);
  85. int DNS_init(void);
  86. void DNS_finit(void);

  87. #endif /* _DNS_H_ */

10-27 15:45