โมดูลนโยบาย DNS (v2)
ตัวอย่าง C ABI v2 บน Linux นี้แสดงวิธีควบคุมคำถาม DNS และแทนที่คำตอบ DNS ใช้ libresolv แยกวิเคราะห์ข้อความ DNS และเก็บคำตอบที่แก้ไขในบัฟเฟอร์ที่โมดูลเป็นเจ้าของ
การทำงานของตัวอย่าง
| ชื่อที่ค้นหา | ผลลัพธ์ |
|---|---|
allow.test | จบชุดฮุกคำถาม แล้วดำเนินการค้นหา DNS ตามปกติต่อ |
block.test | คืนค่า REFUSED โดยไม่ส่งคำถามไปยังเซิร์ฟเวอร์ต้นทาง |
drop.test | ทิ้งคำถามโดยไม่ค้นหาจากต้นทางหรือส่งคำตอบ DNS |
redirect.test | คืน 192.0.2.42 สำหรับคำถาม A และ 2001:db8::42 สำหรับคำถาม AAAA โดยใช้ TTL 60 วินาที |
rewrite.test | เปลี่ยนที่อยู่ของระเบียน A ในคำตอบที่ไม่ได้ลงลายเซ็นเป็น 203.0.113.7 และตั้ง TTL เป็น 60 วินาที |
| ชื่ออื่น ๆ | ดำเนินการโมดูลถัดไปและการประมวลผลตามปกติต่อ |
สำหรับ allow.test และ rewrite.test ให้ตั้งค่าเซิร์ฟเวอร์ DNS ทดสอบให้ตอบชื่อเหล่านั้น ตัวอย่างไม่ได้สร้างระเบียนต้นทางให้ หากคำตอบของ rewrite.test ไม่มีระเบียน A ที่เกี่ยวข้อง หรือมีระเบียนลายเซ็น จะคืนคำตอบโดยไม่เปลี่ยนแปลง
ALLOW จบเฉพาะชุดฮุกปัจจุบัน ไม่ข้ามกฎโดเมนหรือการตรวจสอบวิธีสื่อสาร และฮุกคำตอบอาจเปลี่ยนผลลัพธ์ได้ โปรดดู กฎการทำงานของ DNS
ดาวน์โหลดและบิลด์
ดาวน์โหลด policy.c และ mudfish_dns_module.h ไว้ในไดเรกทอรีเดียวกัน คุณสามารถอ่านเฮดเดอร์ทั้งหมดในเบราว์เซอร์ได้ที่ คำแนะนำเฮดเดอร์
บน Linux ให้ติดตั้งคอมไพเลอร์ C ไลบรารีมาตรฐาน C และเฮดเดอร์สำหรับพัฒนาของ libresolv เรียกคำสั่งต่อไปนี้ในไดเรกทอรีที่ดาวน์โหลดไฟล์ไว้
cc -std=c11 -shared -fPIC -Wall -Wextra -Werror -I. \
policy.c -lresolv -o policy.so
ติดตั้ง policy.so ตาม โหลดโมดูล แล้วเริ่มบริการใหม่ ก่อนทดสอบคำถาม ให้เริ่ม DNS ในแอป
ในการทดสอบภายในเครื่องที่ใช้ที่อยู่รับฟัง DNS เริ่มต้น คุณสามารถใช้คำถามต่อไปนี้ตรวจสอบการปิดกั้นและการสร้างคำตอบที่อยู่ ต้องติดตั้ง 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
คำถามแรกควรคืน REFUSED ส่วนอีกสองคำถามควรคืนที่อยู่ตามตาราง หากเปลี่ยนที่อยู่รับฟังหรือพอร์ต ให้ปรับคำสั่งให้ตรงกัน คำถาม drop.test ตั้งใจไม่ส่งคำตอบ จึงจะหมดเวลา
วิธีการทำงาน
callback ของคำถามและคำตอบจะ คืนค่า 0 เมื่อสำเร็จ และกำหนดการทำงานใน result->action โดยไม่คืนค่าคงที่ของการทำงานโดยตรง
ระหว่างเริ่มต้น จะจัดสรรบัฟเฟอร์คำตอบในบริบทของโมดูล ฮุกคำตอบคัดลอกคำตอบที่ยืมมาลงในบัฟเฟอร์นี้แล้วแก้ไข จากนั้นปล่อยบริบทใน destroy() ตาม กฎความเป็นเจ้าของบัฟเฟอร์ บัฟเฟอร์ทดแทนยังคงใช้ได้หลัง callback คืนค่าแล้ว
ฮุกคำตอบไม่แ ก้ไขข้อความที่ลงลายเซ็น และไม่ตรวจสอบหรือลงลายเซ็นข้อมูล DNSSEC ใหม่ คำตอบที่สร้างหรือแทนที่จะไม่บันทึกในแคชของโฮสต์
ซอร์สทั้งหมด
#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);
}