关于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
-
/*********************************************************************************************
-
* description: kernel module to parse dns request and replay packages
-
*
-
* author: husker <huskertang@gmail.com>
-
*
-
* date: 2019-2-12
-
***********************************************************************************************/
-
-
#include <linux/fs.h>
-
#include <linux/module.h>
-
#include <linux/proc_fs.h>
-
#include <linux/seq_file.h>
-
#include <linux/types.h>
-
#include <linux/version.h>
-
#include <linux/timer.h>
-
#include <linux/ip.h>
-
#include <linux/slab.h>
-
#include <linux/list.h>
-
#include "dns.h"
-
-
static struct kmem_cache* dns_pkg_cache; //__read_mostly;
-
static struct kmem_cache* dns_query_cache; // __read_mostly;
-
static struct kmem_cache* dns_record_cache; // __read_mostly;
-
-
void dns_finit(void)
-
{
-
if (dns_pkg_cache)
-
{
-
kmem_cache_destroy(dns_pkg_cache);
-
}
-
if (dns_query_cache)
-
{
-
kmem_cache_destroy(dns_query_cache);
-
}
-
if (dns_record_cache)
-
{
-
kmem_cache_destroy(dns_record_cache);
-
}
-
}
-
-
int dns_init(void)
-
{
-
dns_pkg_cache = kmem_cache_create("dns_pkg_cache",
-
sizeof(struct dns_package),
-
0, 0, null);
-
if (dns_pkg_cache == null)
-
{
-
printk(kern_err "[dns] failed to create dns package cache\n");
-
goto error_out;
-
}
-
dns_query_cache = kmem_cache_create("dns_query_cache",
-
sizeof(struct dns_query),
-
0, 0, null);
-
if (dns_query_cache == null)
-
{
-
printk(kern_err "[dns] failed to create dns query node cache\n");
-
goto error_out;
-
}
-
-
dns_record_cache = kmem_cache_create("dns_record_cache",
-
sizeof(struct dns_resource),
-
0, 0, null);
-
if (dns_record_cache == null)
-
{
-
printk(kern_err "[dns] failed to create dns resource recorder cache\n");
-
goto error_out;
-
}
-
return 0;
-
-
error_out:
-
dns_finit();
-
return -1;
-
}
-
-
-
static int parse_domain_name(uint8_t* pkg, uint16_t pkglen, uint8_t* pstart, uint8_t* name, uint16_t* nmlen)
-
{
-
int ret;
-
uint8_t tnm[128] = {0};
-
uint16_t tnm_len = 128;
-
int cnt = 0;
-
int total = 0;
-
uint16_t offset = 0;
-
-
uint8_t* ptr;
-
uint16_t len = 0;
-
-
if (dns_hdr_len >= pkglen || pstart < pkg || pstart >= (pkg pkglen) || !name || !nmlen || *nmlen <= 0)
-
{
-
return -1;
-
}
-
-
total = pstart - pkg;
-
ptr = tnm;
-
-
while (*pstart != 0)
-
{
-
if (*pstart >= dns_commpress_flag)
-
{
-
offset = (*pstart) * 256 *(pstart 1) - 0xc000;
-
if (offset >= pkglen)
-
{
-
return -1;
-
}
-
-
tnm_len -= len;
-
ret = parse_domain_name(pkg, pkglen, pkg offset, ptr, &tnm_len);
-
if (ret < 0 )
-
{
-
return ret;
-
}
-
cnt = 2;
-
len = tnm_len;
-
goto success_out;
-
}
-
else
-
{
-
uint16_t dm_part_len = *pstart;
-
-
if ( (pstart dm_part_len >= pkg pkglen) || (128 - len <= 0))
-
{
-
return -1;
-
}
-
-
memcpy(ptr, pstart 1, dm_part_len);
-
ptr = dm_part_len;
-
len = dm_part_len;
-
-
// add '.' to every part of domain
-
*ptr = '.';
-
ptr;
-
len;
-
-
cnt = dm_part_len 1;
-
pstart = dm_part_len 1;
-
}
-
}
-
-
if (*pstart == 0)
-
{
-
cnt;
-
}
-
-
success_out:
-
if (tnm[len - 1] == '.' )
-
{
-
tnm[len - 1] = 0;
-
len--;
-
}
-
if (*nmlen <= len )
-
{
-
return -1;
-
}
-
-
memcpy(name, tnm, len);
-
name[len] = 0;
-
*nmlen = len;
-
return cnt;
-
}
-
-
static int parse_query(uint8_t* pkg, uint16_t pkglen, uint8_t* pstart, struct dns_query* query)
-
{
-
int ret;
-
uint16_t nmlen = 128;
-
uint8_t* ptr = pstart;
-
int cnt = 0;
-
-
// parse name
-
ret = parse_domain_name(pkg, pkglen, ptr, query->name, &nmlen);
-
if (ret < 0 )
-
{
-
return ret;
-
}
-
ptr = ret;
-
cnt = ret;
-
-
-
// parse domain type
-
if (ptr 1 >= pkg pkglen )
-
{
-
return -1;
-
}
-
query->type = (*ptr) * 256 *(ptr 1);
-
ptr = 2;
-
cnt = 2;
-
-
// parse domain class
-
if (ptr 1 >= pkg pkglen )
-
{
-
return -1;
-
}
-
query->class = (*ptr) * 256 *(ptr 1);
-
ptr = 2;
-
cnt = 2;
-
return cnt;
-
}
-
-
static int parse_resource(uint8_t* pkg, uint16_t pkglen, uint8_t* pstart, struct dns_resource* res)
-
{
-
int ret;
-
uint16_t nmlen = 128;
-
uint8_t* ptr = pstart;
-
int cnt = 0;
-
-
// parse name
-
ret = parse_domain_name(pkg, pkglen, ptr, res->name, &nmlen);
-
if (ret < 0 )
-
{
-
return ret;
-
}
-
ptr = ret;
-
cnt = ret;
-
-
-
// parse domain type
-
if (ptr 1 >= pkg pkglen )
-
{
-
return -1;
-
}
-
res->type = (*ptr) * 256 *(ptr 1);
-
ptr = 2;
-
cnt = 2;
-
-
// parse domain class
-
if (ptr 1 >= pkg pkglen )
-
{
-
return -1;
-
}
-
res->class = (*ptr) * 256 *(ptr 1);
-
ptr = 2;
-
cnt = 2;
-
-
// parse ttl
-
if (ptr 3 >= pkg pkglen )
-
{
-
return -1;
-
}
-
res->ttl = ((*ptr) << 24) (*(ptr 1) << 16) (*(ptr 2) << 8) (*(ptr 3));
-
ptr = 4;
-
cnt = 4;
-
-
// parse date len
-
if (ptr 1 >= pkg pkglen)
-
{
-
return -1;
-
}
-
res->data_len = (*ptr) * 256 *(ptr 1);
-
if (res->data_len > 256)
-
{
-
return -1;
-
}
-
ptr = 2;
-
cnt = 2;
-
-
// parse date
-
if (ptr res->data_len > pkg pkglen)
-
{
-
return -1;
-
}
-
if (res->type == type_cname)
-
{
-
uint16_t cnlen = 256;
-
int offset = 0;
-
offset = parse_domain_name(pkg, pkglen, ptr, res->data, &cnlen);
-
if (offset < 0)
-
{
-
return -1;
-
}
-
ptr = offset;
-
cnt = offset;
-
}
-
else
-
{
-
memcpy(res->data, ptr, res->data_len);
-
ptr = res->data_len;
-
cnt = res->data_len;
-
}
-
return cnt;
-
}
-
-
static void free_pkg_recoders(struct list_head* hd)
-
{
-
struct dns_resource* rcd;
-
struct dns_resource* tmp;
-
-
if (list_empty(hd))
-
{
-
return;
-
}
-
list_for_each_entry_safe(rcd, tmp, hd, list)
-
{
-
list_del(&rcd->list);
-
kmem_cache_free(dns_record_cache, rcd);
-
-
}
-
}
-
static void free_pkg_querys(struct list_head* hd)
-
{
-
struct dns_query* query;
-
struct dns_query* tmp;
-
if (list_empty(hd))
-
{
-
return;
-
}
-
list_for_each_entry_safe(query, tmp, hd, list)
-
{
-
list_del(&query->list);
-
kmem_cache_free(dns_query_cache, query);
-
}
-
}
-
-
void dns_free_package(struct dns_package* spkg)
-
{
-
free_pkg_querys(&spkg->query_hd);
-
free_pkg_recoders(&spkg->answer_hd);
-
free_pkg_recoders(&spkg->auth_hd);
-
free_pkg_recoders(&spkg->addit_hd);
-
kmem_cache_free(dns_pkg_cache, spkg);
-
}
-
-
struct dns_package* dns_new_package(void)
-
{
-
struct dns_package* spkg = null;
-
spkg = (struct dns_package*)kmem_cache_alloc(dns_pkg_cache, gfp_atomic);
-
if (null == spkg)
-
{
-
return null;
-
}
-
memset(spkg, 0, sizeof(struct dns_package));
-
init_list_head(&spkg->query_hd);
-
init_list_head(&spkg->answer_hd);
-
init_list_head(&spkg->auth_hd);
-
init_list_head(&spkg->addit_hd);
-
return spkg;
-
}
-
-
int dns_parse_package(uint8_t* pkg, uint16_t pkglen, struct dns_package* spkg)
-
{
-
struct dns_header* dnshdr;
-
struct dns_query* query;
-
struct dns_resource* rcd;
-
uint8_t* ptr;
-
int offset = 0;
-
int i;
-
-
if (!pkg || pkglen <= dns_hdr_len)
-
{
-
return -1;
-
}
-
-
memcpy(&spkg->hdr, pkg, sizeof(struct dns_header));
-
dnshdr = &spkg->hdr;
-
dnshdr->id = ntohs(dnshdr->id);
-
dnshdr->q_count = ntohs(dnshdr->q_count);
-
dnshdr->ans_count = ntohs(dnshdr->ans_count);
-
dnshdr->auth_count = ntohs(dnshdr->auth_count);
-
dnshdr->add_count = ntohs(dnshdr->add_count);
-
-
if (dnshdr->q_count > 64 || dnshdr->add_count > 64 || dnshdr->ans_count > 64 || dnshdr->auth_count > 64)
-
{
-
printk( kern_debug "too much recoders, query<%u>, add<%u>, ans<%u>, auth<%u>\n",
-
dnshdr->q_count, dnshdr->add_count, dnshdr->ans_count, dnshdr->auth_count);
-
return -1;
-
}
-
ptr = pkg dns_hdr_len;
-
-
// parse querys
-
for(i = 0; i < dnshdr->q_count; i)
-
{
-
query = (struct dns_query*)kmem_cache_alloc(dns_query_cache, gfp_atomic);
-
if (null == query)
-
{
-
return -1;
-
}
-
-
offset = parse_query(pkg, pkglen, ptr, query);
-
if (offset <= 0 )
-
{
-
printk(kern_debug "fail to parse query recorder,err:%d, %d\n", offset, i);
-
return offset;
-
}
-
ptr = offset;
-
list_add(&query->list, &spkg->query_hd);
-
}
-
-
// parse answers
-
for (i = 0; i < dnshdr->ans_count; i)
-
{
-
rcd = (struct dns_resource*)kmem_cache_alloc(dns_record_cache, gfp_atomic);
-
if (null == rcd)
-
{
-
return -1;
-
}
-
-
offset = parse_resource(pkg, pkglen, ptr, rcd);
-
if (offset <= 0 )
-
{
-
printk(kern_debug "fail to parse answers, %d\n", i);
-
return offset;
-
}
-
ptr = offset;
-
list_add(&rcd->list, &spkg->answer_hd);
-
}
-
-
// parse auth
-
for (i = 0; i < dnshdr->auth_count; i)
-
{
-
rcd = (struct dns_resource*)kmem_cache_alloc(dns_record_cache, gfp_atomic);
-
if (null == rcd)
-
{
-
return -1;
-
}
-
-
offset = parse_resource(pkg, pkglen, ptr, rcd);
-
if (offset <= 0 )
-
{
-
printk(kern_debug "fail to parse auths, %d\n", i);
-
return offset;
-
}
-
ptr = offset;
-
list_add(&rcd->list, &spkg->auth_hd);
-
}
-
-
for (i = 0; i < dnshdr->add_count; i)
-
{
-
rcd = (struct dns_resource*)kmem_cache_alloc(dns_record_cache, gfp_atomic);
-
if (null == rcd)
-
{
-
return -1;
-
}
-
offset = parse_resource(pkg, pkglen, ptr, rcd);
-
if (offset <= 0 )
-
{
-
printk( kern_debug "fail to parse additions, %d\n", i);
-
return offset;
-
}
-
ptr = offset;
-
list_add(&rcd->list, &spkg->addit_hd);
-
}
-
return 0;
-
}
dns.h
-
/*********************************************************************************************
-
* description: kernel module to parse a dns request or replay
-
*
-
* author: husker <huskertang@gmail.com>
-
*
-
* date: 2018-10-8
-
***********************************************************************************************/
-
-
#ifndef __dns_h__
-
#define __dns_h__
-
-
typedef enum
-
{
-
type_a = 1, // ipv4 address
-
type_ns = 2, // authoritative name server
-
type_md = 3, // mail destination (obsolete - use mx)
-
type_mf = 4, // mail forwarder (obsolete - use mx)
-
type_cname = 5, // canonical name for an alias
-
type_soa = 6, // marks the start of a zone of authority
-
type_mb = 7, // mailbox domain name (experimental)
-
type_ptr = 12, // domain name pointer
-
type_mx = 15, // mail server
-
type_txt = 16,
-
type_aaaa = 28,
-
type_srv = 33,
-
type_spf = 99,
-
} qtype_t;
-
-
#define dns_hdr_len 12
-
#define dns_commpress_flag 0xc0
-
-
#pragma pack(push, 1)
-
struct dns_header
-
{
-
uint16_t id; // identification number
-
#ifdef __big_endian
-
uint8_t qr: 1;
-
uint8_t opcode: 4;
-
uint8_t aa: 1;
-
uint8_t tc: 1;
-
uint8_t rd: 1;
-
uint8_t ra: 1;
-
uint8_t z: 3;
-
uint8_t rcode: 4;
-
#else
-
uint8_t rd: 1;
-
uint8_t tc: 1;
-
uint8_t aa: 1;
-
uint8_t opcode: 4;
-
uint8_t qr: 1;
-
uint8_t rcode: 4;
-
uint8_t z: 3;
-
uint8_t ra: 1;
-
#endif
-
uint16_t q_count;
-
uint16_t ans_count;
-
uint16_t auth_count;
-
uint16_t add_count;
-
};
-
#pragma pack(pop)
-
-
struct dns_query
-
{
-
struct list_head list;
-
uint8_t name[128];
-
uint16_t type;
-
uint16_t class;
-
};
-
-
struct dns_resource
-
{
-
struct list_head list;
-
uint8_t name[128];
-
uint16_t type;
-
uint16_t class;
-
uint32_t ttl;
-
uint16_t data_len;
-
uint8_t data[256];
-
};
-
-
struct dns_package
-
{
-
struct dns_header hdr;
-
struct list_head query_hd;
-
struct list_head answer_hd;
-
struct list_head auth_hd;
-
struct list_head addit_hd;
-
};
-
-
void dns_free_package(struct dns_package* spkg);
-
struct dns_package* dns_new_package(void);
-
int dns_parse_package(uint8_t* pkg, uint16_t pkglen, struct dns_package* spkg);
-
int dns_init(void);
-
void dns_finit(void);
-
-
#endif /* _dns_h_ */
阅读(24048) | 评论(0) | 转发(0) |