Ana içeriğe atla

DNS politika modülü (v2)

Bu Linux C ABI v2 örneği, DNS sorgularını kontrol etmeyi ve yanıtları değiştirmeyi gösterir. libresolv ile DNS mesajlarını ayrıştırır; değiştirilmiş yanıtı modülün sahip olduğu tamponda saklar.

Örneğin davranışı​

Sorgu adıSonuç
allow.testSorgu kancası zincirini bitirir ve normal DNS sorgusuna devam eder.
block.testÜst sunucuya sorgu göndermeden REFUSED döndürür.
drop.testÜst sunucu sorgusu veya DNS yanıtı olmadan sorguyu atar.
redirect.testA sorgusuna 192.0.2.42, AAAA sorgusuna 2001:db8::42 döndürür; TTL 60 saniyedir.
rewrite.testİmzasız yanıttaki A kaydını 203.0.113.7, TTL'yi 60 saniye yapar.
Diğer adlarSonraki modüle ve normal işlemeye devam eder.

Test DNS sunucusunu allow.test ve rewrite.test için yanıt verecek şekilde ayarlayın. Örnek üst sunucuda kayıt oluşturmaz. rewrite.test yanıtında ilgili A kaydı yoksa veya imza kaydı varsa yanıt değiştirilmeden döndürülür.

ALLOW yalnızca mevcut kanca zincirini bitirir. Alan adı kurallarını veya iletişim kontrollerini atlamaz; yanıt kancası sonucu değiştirebilir. DNS eylem kurallarına bakın.

İndirme ve derleme​

policy.c ve mudfish_dns_module.h dosyalarını aynı dizine indirin. Başlık kılavuzunda tam başlığı tarayıcıda okuyabilirsiniz.

Linux'ta C derleyicisi, standart C kütüphanesi ve libresolv geliştirme başlıklarını yükleyin. İndirdiğiniz dizinde şu komutu çalıştırın:

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

Modül yükleme bölümüne göre policy.so dosyasını kurup hizmeti yeniden başlatın. Sorguları test etmeden önce uygulamada DNS'i başlatın.

Varsayılan DNS dinleme adresiyle yerel testte aşağıdaki sorgularla engelleme ve adres yanıtı üretimini kontrol edebilirsiniz. dig yüklü 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 sorgu REFUSED, diğer ikisi tablodaki adresleri döndürmelidir. Dinleme adresi veya bağlantı noktasını değiştirdiyseniz komutları uyarlayın. drop.test kasıtlı olarak yanıt göndermediğinden zaman aşımına uğrar.

Nasıl çalışır​

Sorgu ve yanıt geri çağırımları başarıda 0 döndürür; eylem result->action alanına yazılır. Eylem sabiti doğrudan döndürülmez.

Başlatmada modül bağlamına yanıt tamponu ayrılır. Yanıt kancası ödünç alınan yanıtı bu tampona kopyalayıp değiştirir; destroy() bağlamı serbest bırakır. Tampon sahipliği kurallarına göre yedek tampon, geri çağırım döndükten sonra da geçerli kalır.

Yanıt kancası imzalı mesajları değiştirmez. DNSSEC verisini doğrulamaz veya yeniden imzalamaz. Oluşturulan veya değiştirilen yanıtlar ana uygulama önbelleğine kaydedilmez.

Tam kaynak​

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