Mô-đun chính sách DNS (v2)
Ví dụ C ABI v2 cho Linux này minh họa cách kiểm soát truy vấn DNS và thay thế phản hồi DNS. Ví dụ dùng libresolv để phân tích thông điệp DNS và lưu phản h ồi đã sửa đổi vào bộ đệm do mô-đun sở hữu.
Hành vi của ví dụ
| Tên truy vấn | Kết quả |
|---|---|
allow.test | Kết thúc chuỗi hook truy vấn và tiếp tục truy vấn DNS thông thường. |
block.test | Trả về REFUSED mà không truy vấn máy chủ thượng nguồn. |
drop.test | Loại bỏ truy vấn mà không truy vấn thượng nguồn hoặc gửi phản hồi DNS. |
redirect.test | Trả về 192.0.2.42 cho truy vấn A và 2001:db8::42 cho truy vấn AAAA, với TTL 60 giây. |
rewrite.test | Đổi địa chỉ bản ghi A trong phản hồi chưa ký thành 203.0.113.7 và đặt TTL thành 60 giây. |
| Các tên khác | Tiếp tục với mô-đun tiếp theo và quá trình xử lý thông thường. |
Với allow.test và rewrite.test, hãy cấu hình máy chủ DNS thử nghiệm để cung cấp phản hồi cho các tên đó. Ví dụ không tạo bản ghi thượng nguồn. Nếu phản hồi cho rewrite.test không có bản ghi A tương ứng hoặc có bản ghi chữ ký, phản hồi được trả về nguyên trạng.
ALLOW chỉ kết thúc chuỗi hook hiện tại. Nó không bỏ qua quy tắc tên miền hoặc kiểm tra phương thức truyền thông, và hook phản hồi vẫn có thể thay đổi kết quả. Tham khảo Quy t ắc hành động DNS.
Tải xuống và biên dịch
Tải policy.c và mudfish_dns_module.h vào cùng một thư mục. Bạn có thể đọc toàn bộ tệp tiêu đề trong trình duyệt tại hướng dẫn tệp tiêu đề.
Trên Linux, cài trình biên dịch C cùng các tệp tiêu đề phát triển của thư viện C chuẩn và libresolv. Chạy lệnh sau trong thư mục đã tải xuống.
cc -std=c11 -shared -fPIC -Wall -Wextra -Werror -I. \
policy.c -lresolv -o policy.so
Cài đặt policy.so theo Tải mô-đun rồi khởi động lại dịch vụ. Trước khi thử truy vấn, hãy bắt đầu DNS trong ứng dụng.
Khi thử cục bộ bằng địa chỉ lắng nghe DNS mặc định, bạn có thể dùng các truy vấn sau để kiểm tra việc chặn và tạo phản hồi địa chỉ. Cần cài đặt dig.
dig @127.0.0.1 block.test A
dig @127.0.0.1 redirect.test A
dig @127.0.0.1 redirect.test AAAA
Truy vấn đầu tiên phải trả về REFUSED, hai truy vấn còn lại phải trả về địa chỉ trong bảng. Nếu đã đổi địa chỉ lắng nghe hoặc cổng, hãy điều chỉnh lệnh cho phù hợp. Truy vấn drop.test cố ý không gửi phản hồi nên sẽ hết thời gian chờ.
Cách hoạt động
Các callback truy vấn và phản hồi trả về 0 khi thành công, còn hành động được đặt trong result->action. Không trả trực tiếp hằng số hành động.
Trong quá trình khởi tạo, bộ đệm phản hồi được cấp phát trong ngữ cảnh mô-đun. Hook phản hồi sao chép phản hồi mượn vào bộ đệm này rồi sửa đổi, và giải phóng ngữ cảnh trong destroy(). Theo Quy tắc sở hữu bộ đệm, bộ đệm thay thế vẫn hợp lệ sau khi callback trả về.
Hook phản hồi không sửa đổi thông điệp đã ký. Nó cũng không xác minh dữ liệu DNSSEC hoặc ký lại. Phản hồi được tạo hoặc thay thế không được lưu vào bộ nhớ đệm của chương trình chủ.
Toàn bộ mã nguồn
#include "mudfish_dns_module.h"
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
struct policy {
uint8_t response[65535];
};
static int
question(const struct mudfish_dns_event *event, ns_rr *rr)
{
ns_msg message;
if (event->query.len > 65535 ||
ns_initparse(event->query.data, (int)event->query.len, &message) < 0)
return (-1);
return (ns_parserr(&message, ns_s_qd, 0, rr));
}
static int32_t
dns_query(void *context, const struct mudfish_dns_event *event,
struct mudfish_dns_result *result)
{
ns_rr rr;
(void)context;
if (question(event, &rr) < 0)
return (-1);
if (strcasecmp(ns_rr_name(rr), "allow.test") == 0)
result->action = MUDFISH_DNS_ALLOW;
else if (strcasecmp(ns_rr_name(rr), "block.test") == 0)
result->action = MUDFISH_DNS_BLOCK;
else if (strcasecmp(ns_rr_name(rr), "drop.test") == 0)
result->action = MUDFISH_DNS_DROP;
else if (strcasecmp(ns_rr_name(rr), "redirect.test") == 0) {
result->action = MUDFISH_DNS_ANSWER;
result->ttl = 60;
if (ns_rr_type(rr) == ns_t_aaaa) {
result->address_family = MUDFISH_DNS_IPV6;
inet_pton(AF_INET6, "2001:db8::42", result->address);
} else {
result->address_family = MUDFISH_DNS_IPV4;
inet_pton(AF_INET, "192.0.2.42", result->address);
}
}
return (0);
}
static int32_t
dns_response(void *context, const struct mudfish_dns_event *event,
struct mudfish_dns_result *result)
{
struct policy *policy = context;
ns_msg message;
ns_rr rr;
uint8_t *rdata;
int section, i, changed = 0;
if (question(event, &rr) < 0)
return (-1);
if (strcasecmp(ns_rr_name(rr), "rewrite.test") != 0)
return (0);
if (event->response.len > sizeof(policy->response))
return (-1);
memcpy(policy->response, event->response.data, event->response.len);
if (ns_initparse(policy->response, (int)event->response.len, &message) < 0)
return (-1);
/* This example only edits unsigned answers. A policy that edits signed
* data must also rebuild/remove the affected signatures.
*/
for (section = ns_s_an; section <= ns_s_ar; section++) {
for (i = 0; i < ns_msg_count(message, section); i++) {
if (ns_parserr(&message, (ns_sect)section, i, &rr) < 0)
return (-1);
if (ns_rr_type(rr) == ns_t_rrsig || ns_rr_type(rr) == ns_t_sig ||
ns_rr_type(rr) == ns_t_tsig)
return (0);
}
}
for (i = 0; i < ns_msg_count(message, ns_s_an); i++) {
if (ns_parserr(&message, ns_s_an, i, &rr) < 0)
return (-1);
if (ns_rr_class(rr) != ns_c_in || ns_rr_type(rr) != ns_t_a ||
ns_rr_rdlen(rr) != 4)
continue;
/* rdata belongs to our copy. TTL precedes RDLENGTH (2 bytes). */
rdata = policy->response + (ns_rr_rdata(rr) - policy->response);
inet_pton(AF_INET, "203.0.113.7", rdata);
ns_put32(60, rdata - 6);
changed = 1;
}
if (changed) {
result->action = MUDFISH_DNS_REPLACE;
result->response.data = policy->response;
result->response.len = event->response.len;
}
return (0);
}
static void
destroy(void *context)
{
free(context);
}
int32_t
mudfish_dns_module_init_v2(uint32_t abi_version, size_t module_size,
struct mudfish_dns_module_v2 *module)
{
if (abi_version != MUDFISH_DNS_MODULE_ABI_V2 || module_size != sizeof(*module))
return (-1);
module->context = calloc(1, sizeof(struct policy));
if (module->context == NULL)
return (-1);
module->on_dns_query = dns_query;
module->on_dns_response = dns_response;
module->destroy = destroy;
return (0);
}