Modul pravil DNS (v2)
Ta primer C ABI v2 za Linux prikazuje nadzor poizvedb DNS in zamenjavo odgovorov DNS. S knjižnico libresolv razčleni sporočila DNS in spremenjeni odgovor shrani v medpomnilnik v lasti modula.
Delovanje primera
| Ime poizvedbe | Rezultat |
|---|---|
allow.test | Konča verigo poizvedbenih kavljev in nadaljuje običajno poizvedbo DNS. |
block.test | Vrne REFUSED brez poizvedbe navzgornjemu strežniku. |
drop.test | Zavrže poizvedbo brez navzgornje poizvedbe ali odgovora DNS. |
redirect.test | Za poizvedbe A vrne 192.0.2.42, za AAAA pa 2001:db8::42, s TTL 60 sekund. |
rewrite.test | Naslov zapisa A v nepodpisanem odgovoru spremeni v 203.0.113.7 in TTL nastavi na 60 sekund. |
| Druga imena | Nadaljuje naslednji modul in običajno obdelavo. |
Za allow.test in rewrite.test nastavite testni strežnik DNS, da vrača odgovore za ti imeni. Primer ne ustvarja zapisov na navzgornjem strežniku. Če odgovor rewrite.test ne vsebuje ustreznega zapisa A ali vsebuje podpisne zapise, se vrne nespremenjen.
ALLOW konča samo trenutno verigo kavljev. Ne obide pravil domen ali preverjanja načinov prenosa, rezultat pa lahko spremeni tudi odzivni kavelj. Glejte pravila dejanj DNS.
Prenos in gradnja
Prenesite policy.c in mudfish_dns_module.h v isti imenik. Celotno glavo lahko v brskalniku preberete v vodniku po glavi.
V Linuxu namestite prevajalnik C ter razvojne glave standardne knjižnice C in libresolv. V imeniku s prenesenima datotekama izvedite:
cc -std=c11 -shared -fPIC -Wall -Wextra -Werror -I. \
policy.c -lresolv -o policy.so
Namestite policy.so po navodilih za nalaganje modula in znova zaženite storitev. Pred preizkusom poizvedb v aplikaciji zaženite DNS.
Pri lokalnem preizkusu s privzetim poslušalnim naslovom DNS lahko z naslednjimi poizvedbami preverite blokiranje in ustvarjanje naslovnih odgovorov. Nameščen mora biti 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
Prva poizvedba mora vrniti REFUSED, drugi dve pa naslova iz tabele. Če ste spremenili poslušalni naslov ali vrata, ustrezno prilagodite ukaze. Poizvedba drop.test namenoma ne dobi odgovora, zato poteče časovna omejitev.
Način delovanja
Poizvedbeni in odzivni povratni klici ob uspehu vrnejo 0, dejanje pa nastavijo v result->action. Konstant dejanj ne vračajo neposredno.
Med inicializacijo se v kontekstu modula dodeli medpomnilnik odgovora. Odzivni kavelj izposojeni odgovor kopira vanj in ga spremeni, destroy() pa sprosti kontekst. Po pravilih lastništva medpomnilnikov nadomestni medpomnilnik ostane veljaven tudi po vrnitvi povratnega klica.
Odzivni kavelj ne spreminja podpisanih sporočil, ne preverja podatkov DNSSEC in jih ne podpisuje znova. Ustvarjeni ali zamenjani odgovori se ne shranijo v predpomnilnik gostitelja.
Celotna izvorna koda
#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);
}