Үндсэн агуулга руу алгасах

DNS бодлогын модуль (v2)

Энэхүү Linux C ABI v2 жишээ нь DNS асуулгыг удирдах болон DNS хариуг солих аргыг харуулна. libresolv-оор DNS мессежийг парс хийж, модулийн эзэмшлийн буферт өөрчилсөн хариуг хадгална.

Жишээний ажиллагаа​

Асуулгын нэрҮр дүн
allow.testАсуулгын хук гинжийг төгсгөж ердийн DNS хайлтыг үргэлжлүүлнэ.
block.testДээд (upstream) серверээс хайлгүйгээр REFUSED буцаана.
drop.testДээд хайлт эсвэл DNS хариу өгөлгүйгээр асуулгыг хаяна.
redirect.testA асуулгад 192.0.2.42, AAAA асуулгад 2001:db8::42-ийг 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 хөрвүүлэгч (compiler), стандарт 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 өгөгдлийг баталгаажуулах эсвэл дахин гарын үсэг зурахгүй. Үүсгэсэн эсвэл орлуулсан хариуг хостын кэшид хадгалахгүй.

Бүрэн эх код​

policy.c
#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);
}