DNS မူဝါဒ Module (v2)
ဤ Linux C ABI v2 နမူနာသည် DNS query များကို ထိန်းချုပ်ပုံနှင့် DNS response များကို အစားထိုးပုံကို ပြသပေးပါသည်။ libresolv ဖြင့် DNS message များကို parse လုပ်ပြီး module ပိုင်ဆိုင်သော buffer ထဲတွင် ပြင်ဆင်ထားသော response ကို သိမ်းဆည်းပါသည်။
နမူနာ လည်ပတ်မှု ပုံစံ
| Query အမည် | ရလဒ် |
|---|---|
allow.test | Query hook chain ကို အဆုံးသတ်ပြီး ပုံမှန် DNS စုံစမ်းမှုကို ဆက်လက်လုပ်ဆောင်ပါမည်။ |
block.test | Upstream ဆာဗာသို့ စုံစမ်းခြင်း မပြုဘဲ REFUSED ကို ပြန်ပို့ပါမည်။ |
drop.test | Upstream စုံစမ်းမှု သို့မဟုတ် DNS response မပေးဘဲ query ကို စွန့်ပစ်လိုက်ပါမည်။ |
redirect.test | A query အတွက် 192.0.2.42၊ AAAA query အတွက် 2001:db8::42 ကို TTL 60 စက္ကန့်ဖြင့် ပြန်ပို့ပါမည်။ |
rewrite.test | လက်မှတ်မထိုးထားသော response ၏ A record လိပ်စာကို 203.0.113.7 သို့ ပြောင်းလဲပြီး TTL ကို 60 စက္ကန့် သတ်မှတ်ပါမည်။ |
| အခြား အမည်များ | နောက် module နှင့် ပုံမှန် လုပ်ငန်းစဉ်ကို ဆက်လက်လုပ်ဆောင်ပါမည်။ |
allow.test နှင့် rewrite.test အတွက် သက်ဆိုင်ရာ အမည်များ၏ response ကို ပေးနိုင်ရန် စမ်းသပ် DNS ဆာဗာတွင် သတ်မှတ်ပေးထားပါ။ ဤနမူနာသည် upstream records များကို ဖန်တီးပေးမည် မဟုတ်ပါ။ rewrite.test ၏ response တွင် ကိုက်ညီသော A record မရှိပါက သို့မဟုတ် signature record ရှိနေပါက response ကို မူလအတိုင်း ပြန်ပို့ပါမည်။
ALLOW သည် လက်ရှိ hook chain ကိုသာ အဆုံးသတ်စေပါသည်။ Domain စည်းမျဉ်းများ သို့မဟုတ် transport စစ်ဆေးမှုများကို ကျော်လွှားမည် မဟုတ်ဘဲ response hook တွင် ရလဒ် ပြောင်းလဲသွားနိုင်ပါသည်။ DNS Action စည်းမျဉ်းများ ကို ကိုးကားပါ။
ဒေါင်းလုဒ်လုပ်ခြင်းနှင့် Build ပြုလုပ်ခြင်း
policy.c နှင့် mudfish_dns_module.h တို့ကို directory တစ်ခုတည်းတွင် ဒေါင်းလုဒ်လုပ်ပါ။ Header လမ်းညွှန် တွင် header အပြည့်အစုံကို browser ဖြင့် ဖတ်ရှုနိုင်ပါသည်။
Linux တွင် C compiler၊ standard C library နှင့် libresolv ၏ development headers များကို ထည့်သွင်းပါ။ ဒေါင်းလုဒ်လုပ်ထားသော directory တွင် အောက်ပါ command ကို run ပါ-
cc -std=c11 -shared -fPIC -Wall -Wextra -Werror -I. \
policy.c -lresolv -o policy.so
Module ရယူထည့်သွင်းခြင်း အတိုင်း policy.so ကို ထည့်သွင်းပြီး service ကို ပြန်လည်စတင်ပါ။ Query များကို မစမ်းသပ်မီ အက်ပ်တွင် DNS ကို စတင်ပါ။
မူရင်း DNS listening address ကို အသုံးပြုသော local test တွင် အောက်ပါ query များဖြင့် ပိတ်ပင်ခြင်းနှင့် address response ဖန်တီးခြင်းကို စစ်ဆေးနိုင်ပါသည်။ 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
ပထမ query သည် REFUSED ကိုလည်းကောင်း၊ ကျန် query နှစ်ခုသည် ဇယားတွင် ဖော်ပြထားသော လိပ်စာများကိုလည်းကောင်း ပြန်ပို့ပေးရပါမည်။ Listening address သို့မဟုတ် port ကို ပြောင်းလဲထားပါက command များကိုလည်း လိုက်လျောညီထွေ ပြင်ဆင်ပါ။ drop.test query သည် တမင်တကာ response မပေးပို့သဖြင့် timeout ဖြစ်ပေါ်ပါမည်။
အလုပ်လုပ်ပုံ အဆင့်ဆင့်
Query နှင့် response callback များသည် အောင်မြင်ပါက 0 ကို ပြန်ပို့ပေးပြီး action ကို result->action တွင် သတ်မှတ်ပေးပါသည်။ Action constants များကို တိုက်ရိုက် return မလုပ်ပါ။
Initialization လုပ်ငန်းစဉ်အတွင်း module context တွင် response buffer ကို နေရာချထားပေးပါသည်။ Response hook သည် ငှားရမ်းထားသော response ကို ဤ buffer ထဲသို့ ကူးယူပြီးနောက် ပြင်ဆင်ကာ destroy() တွင် context ကို ရှင်းလင်းပေးပါသည်။ Buffer ownership စည်းမျဉ်းများ အတိုင်း callback ပြီးဆုံးပြီးနောက်တွင်လည်း အစားထိုး buffer သည် တရားဝင် အခြေအနေကို ဆက်လက်ထိန်းသိမ်းထားပါသည်။
Response hook သည် လက်မှတ်ထိုးထားသော message များကို မပြောင်းလဲပါ။ DNSSEC data များကို အတည်ပြုခြင်း သို့မဟုတ် ပြန်လည်လက်မှတ်ထိုးခြင်း မပြုလုပ်ပါ။ ဖန်တီးထားသော သို့မဟုတ် အစားထိုးထားသော response များကို host cache တွင် သိမ်းဆည်းမည် မဟုတ်ပါ။
Source Code အပြည့်အစုံ
#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);
}