如何在C中解析网络格式的DNS数据包

问题描述 投票:0回答:1

我正在编写一个 netfilter 内核模块,用于删除来自我的计算机的特定 DNS 请求。为此,我需要从 DNS 数据包问题部分 (rfc1035) 中提取域名。此部分内的

QNAME
字段的格式为长度字节序列,后跟该字节数,以
0
字节终止符结尾,例如
12cppreference3com0

但是我的机器是小端字节序,所以我得到了

0moc3ecnereferppc21
的相反字节顺序。 我如何解析这个?我无法通过空终止符检测结尾,并且我不知道整个 DNS 数据包字段的长度来反转其中的字节。

insmod
之后(在用户进行任何交互之前),我尝试访问网站并获得内核日志
Blocked DNS request for 0
。我获得
0
哈希值的唯一方法是立即在
0
中遇到
QNAME
。奇怪的是,再次运行后,我的操作系统完全崩溃了。

更新:如果我不这样做,我仍然会崩溃

- '\0'
。但崩溃发生得晚一些。当我加载模块然后卸载它时,我无法再次加载它
insmod
:

insmod: ERROR: could not insert module dns_filter_module.ko: Device or resource busy

内核日志表明存在一些 NULL 指针取消引用。我也得到了

watchdog bug: soft lockup CPU stuck

代码:

附注我知道我会遇到哈希冲突,并且我可能会遇到多个问题部分,我不明白为什么我的系统会从这个特定的非常简单的程序中崩溃

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");

#define DEVICE_NAME "dns_blocker_device"
#define BLOCKED_URLS_SIZE 120

#define DNS_PACKET_SECTION_SIZE 2
#define DNS_HEADER_SECTIONS_COUNT 6
#define UDP_HEADER_SIZE 8

static int blocked_url_hashes[BLOCKED_URLS_SIZE] = {0};

static struct nf_hook_ops nfho = {0};

static int major_number;
static struct class* class_blocker = NULL;
static struct device* device_blocker = NULL;
static atomic_t is_device_open = {0};

static int hash_func(unsigned char *start, int len)
{
    int hash = 0;
    int i = 0;
    
    for (i = 0; i < len; ++i)
    {
        hash += start[i];
    }

    return hash;
}

// get domain hash as the sum of values of every its character
static int extract_qname_hash(unsigned char *start)
{
    int len = *start - '0';
    int hash = 0;

    while (len != 0)
    {
        ++start;

        hash += hash_func(start, len);

        start += len;
        len = *start - '0';
    }

    return hash;
}

static int get_domain_name_hash(unsigned char *header) 
{

    unsigned char *curr = header;

    // move past headers, to QUESTION section
    curr += DNS_HEADER_SECTIONS_COUNT * DNS_PACKET_SECTION_SIZE;

    // return QNAME hash
    return extract_qname_hash(curr);
}

static bool is_url_blocked(int url_hash) 
{
    int i = 0;

    for (i = 0; i < BLOCKED_URLS_SIZE; ++i) 
    {
        if (url_hash == blocked_url_hashes[i]) 
        {
            return true;
        }
    }

    return false;
}

static unsigned int hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) 
{
    struct iphdr *ip_header;
    struct udphdr *udp_header;
    unsigned char *data_start;
    int url_hash = -1;

    if (skb == NULL) 
    {
        return NF_ACCEPT;
    }

    ip_header = ip_hdr(skb);

    if (ip_header->protocol == IPPROTO_UDP) 
    {
        udp_header = udp_hdr(skb);

        if (ntohs(udp_header->dest) == 53) 
        {

            data_start = (unsigned char *)udp_header + UDP_HEADER_SIZE;
            
            url_hash = get_domain_name_hash(data_start);

            if (is_url_blocked(url_hash)) 
            {
                printk(KERN_INFO "Blocked DNS request for %d\n", url_hash);
                return NF_DROP; 
            }
        }
    }

    return NF_ACCEPT;
}

static int device_open(struct inode *inode, struct file *file)
{

    if (atomic_cmpxchg(&is_device_open, 0, 1)) return -EBUSY;

    return 0;
}

static int device_release(struct inode *inode, struct file *file)
{
    atomic_set(&is_device_open, 0);

    return 0;
}

// copy int array of domain hashes from user
static ssize_t device_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) 
{
    if (len >= BLOCKED_URLS_SIZE) 
    {
        printk(KERN_ALERT "Max URLs reached\n");
        return -EINVAL;
    }

    if (copy_from_user(blocked_url_hashes, buffer, len) != 0) 
    {
        printk(KERN_ALERT "Failed to copy URL from user space\n");
        return -EFAULT;
    }

    return len;
}

static struct file_operations fops = 
{
    .open = device_open,
    .write = device_write,
    .release = device_release
};

static int __init init_blocker_module(void) 
{
    int ret = 0;

    nfho.hook = hook_func;
    nfho.hooknum = NF_INET_PRE_ROUTING;
    nfho.pf = PF_INET;
    nfho.priority = NF_IP_PRI_FIRST;
    nf_register_net_hook(&init_net, &nfho);

    major_number = register_chrdev(0, DEVICE_NAME, &fops);

    if (major_number < 0) 
    {
        printk(KERN_ALERT "Failed to register a major number\n");
        ret = major_number;
        goto device_register_fail;
    }

    class_blocker = class_create(THIS_MODULE, "blocker_class");

    if (IS_ERR(class_blocker)) 
    {
        printk(KERN_ALERT "Failed to create device class\n");
        ret = PTR_ERR(class_blocker);
        goto class_create_fail;
    }

    device_blocker = device_create(class_blocker, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME);

    if (IS_ERR(device_blocker)) 
    {
        printk(KERN_ALERT "Failed to create the device\n");
        ret = PTR_ERR(device_blocker);
        goto device_create_fail;
    }

    return ret;

device_create_fail:
    class_destroy(class_blocker);
class_create_fail:
    unregister_chrdev(major_number, DEVICE_NAME);
device_register_fail:
    nf_unregister_net_hook(&init_net, &nfho);

    return ret;
}

static void __exit exit_blocker_module(void) 
{
    unregister_chrdev(major_number, DEVICE_NAME);
    class_destroy(class_blocker);
    device_destroy(class_blocker, MKDEV(major_number, 0));
    nf_unregister_net_hook(&init_net, &nfho);

    printk(KERN_INFO "Blocker module removed\n");
}

module_init(init_blocker_module);
module_exit(exit_blocker_module);

c parsing linux-kernel dns endianness
1个回答
0
投票

此代码有 3 个错误:

  1. len = *start - '0';
    应该是
    len = *start;
    因为长度表示为字节,而不是
    char
    数字
if (len >= BLOCKED_URLS_SIZE) 

应该是

if (len > BLOCKED_URLS_SIZE * sizeof(int)) 

因为

blocked_url_hashes
是一个大小为
BLOCKED_URLS_SIZE * sizeof(int)
字节的数组。

  1. NULL
     被销毁后调用 
    device_destroy(class_blocker, MKDEV(major_number, 0));
     时,退出函数会导致 
    class_blocker
     指针取消引用。
    另外,应在设备被销毁后调用 
    unregister_chrdev(major_number, DEVICE_NAME);
    ,因为它会销毁与设备关联的 
    major_number
这就是模块退出的样子:

static void __exit exit_blocker_module(void) { device_destroy(class_blocker, MKDEV(major_number, 0)); class_destroy(class_blocker); unregister_chrdev(major_number, DEVICE_NAME); nf_unregister_net_hook(&init_net, &nfho); printk(KERN_INFO "Blocker module removed\n"); }
    
© www.soinside.com 2019 - 2024. All rights reserved.