Əsas məzmuna keç

DNS siyasəti modulu (v2)

Bu Linux C ABI v2 nümunəsi DNS sorğularını idarə etməyi və DNS cavablarını əvəzləməyi göstərir. DNS mesajlarını libresolv ilə təhlil edir və dəyişdirilmiş cavabı modulun sahib olduğu buferdə saxlayır.

Nümunənin davranışı​

Sorğu adıNəticə
allow.testSorğu hook zəncirini bitirir və adi DNS sorğusunu davam etdirir.
block.testYuxarı serverə sorğu göndərmədən REFUSED qaytarır.
drop.testYuxarı serverə sorğu göndərmədən və DNS cavabı vermədən sorğunu atır.
redirect.testA sorğusuna 192.0.2.42, AAAA sorğusuna 2001:db8::42 ünvanını 60 saniyə TTL ilə qaytarır.
rewrite.testİmzalanmamış cavabın A qeydinin ünvanını 203.0.113.7 edir və TTL-i 60 saniyəyə təyin edir.
Digər adlarNövbəti modulla və adi emalla davam edir.

Sınaq DNS serverini allow.test və rewrite.test adları üçün cavab verəcək şəkildə sazlayın. Nümunə yuxarı serverdə qeydlər yaratmır. rewrite.test cavabında uyğun A qeydi yoxdursa və ya imza qeydi varsa, cavab dəyişdirilmədən qaytarılır.

ALLOW yalnız cari hook zəncirini bitirir. Domen qaydalarını və ya rabitə üsulu yoxlamalarını keçmir; cavab hook funksiyası da nəticəni dəyişə bilər. DNS əməliyyatı qaydalarına baxın.

Endirmə və yığma​

policy.c və mudfish_dns_module.h fayllarını eyni qovluğa endirin. Başlıq faylı təlimatında tam başlıq faylını brauzerdə oxuya bilərsiniz.

Linux-da C kompilyatorunu, standart C kitabxanasının və libresolv kitabxanasının hazırlama üçün başlıq fayllarını quraşdırın. Faylları endirdiyiniz qovluqda aşağıdakı əmri icra edin.

cc -std=c11 -shared -fPIC -Wall -Wextra -Werror -I. \
policy.c -lresolv -o policy.so

Modul yükləmək təlimatına uyğun olaraq policy.so faylını quraşdırın və xidməti yenidən başladın. Sorğuları sınamazdan əvvəl tətbiqdə DNS-i başladın.

Standart DNS dinləmə ünvanı ilə yerli sınaqda aşağıdakı sorğularla bloklamanı və ünvan cavablarının yaradılmasını yoxlaya bilərsiniz. dig quraşdırılmış olmalıdır.

dig @127.0.0.1 block.test A
dig @127.0.0.1 redirect.test A
dig @127.0.0.1 redirect.test AAAA

İlk sorğu REFUSED, digər iki sorğu isə cədvəldə göstərilən ünvanları qaytarmalıdır. Dinləmə ünvanını və ya portunu dəyişmisinizsə, əmrləri də uyğunlaşdırın. drop.test sorğusuna qəsdən cavab göndərilmədiyinə görə gözləmə müddəti bitir.

İş prinsipi​

Sorğu və cavab geri çağırışları uğur zamanı 0 qaytarır, əməliyyat isə result->action sahəsində təyin edilir. Əməliyyat sabitləri birbaşa qaytarılmır.

Başlanğıc sazlanması zamanı modul kontekstində cavab buferi ayrılır. Cavab hook funksiyası götürülmüş cavabı bu buferə köçürüb dəyişir, destroy() isə konteksti azad edir. Bufer sahibliyi qaydalarına uyğun olaraq geri çağırış qayıtdıqdan sonra da əvəzləyici bufer etibarlı qalır.

Cavab hook funksiyası imzalanmış mesajları dəyişmir. DNSSEC məlumatlarını yoxlamır və yenidən imzalamır. Yaradılmış və ya əvəzlənmiş cavablar hostun keşində saxlanmır.

Tam mənbə kodu​

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);
}