关于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_ */
阅读(24084) | 评论(0) | 转发(0) |