DNS politikos modulis (v2)
Šis Linux C ABI v2 pavyzdys rodo, kaip valdyti DNS užklausas ir keisti atsakymus. DNS pranešimai analizuojami su libresolv, o pakeistas atsakymas saugomas moduliui priklausančiame buferyje.
Pavyzdžio veikimas
| Užklausos vardas | Rezultatas |
|---|---|
allow.test | Baigia užklausos funkcijų grandinę ir tęsia įprastą DNS užklausą. |
block.test | Grąžina REFUSED nesikreipdamas į aukštesnio lygio serverį. |
drop.test | Atmeta užklausą nesikreipdamas į aukštesnio lygio serverį ir nesiųsdamas DNS atsakymo. |
redirect.test | A užklausai grąžina 192.0.2.42, AAAA – 2001:db8::42, su 60 sekundžių TTL. |
rewrite.test | Nepasirašyto atsakymo A įrašo adresą pakeičia į 203.0.113.7, o TTL – į 60 sekundžių. |
| Kiti vardai | Tęsia kitą modulį ir įprastą apdorojimą. |
Testiniame DNS serveryje nustatykite atsakymus vardams allow.test ir rewrite.test: pats pavyzdys aukštesnio lygio įrašų nekuria. Jei rewrite.test atsakyme nėra atitinkamo A įrašo arba yra parašo įrašų, atsakymas grąžinamas nepakeistas.
ALLOW baigia tik dabartinę funkcijų grandinę. Jis neapeina domenų taisyklių ar ryšio būdo patikrų; rezultatą dar gali pakeisti atsakymo funkcijos. Žr. DNS veiksmų taisyklės.
Atsisiuntimas ir kompiliavimas
Atsisiųskite policy.c ir mudfish_dns_module.h į tą patį katalogą. Visą antraštę naršyklėje galite perskaityti antraštės vadove.
Linux įdiekite C kompiliatorių, standartinės C bibliotekos ir libresolv kūrimo antraštes. Atsisiuntimų kataloge vykdykite:
cc -std=c11 -shared -fPIC -Wall -Wextra -Werror -I. \
policy.c -lresolv -o policy.so
Pagal modulio įkėlimo instrukciją įdiekite policy.so ir paleiskite paslaugą iš naujo. Prieš testuodami užklausas programėlėje paleiskite DNS.
Vietiniam testui su numatytuoju DNS klausymo adresu šiomis užklausomis patikrinsite blokavimą ir adreso atsakymo kūrimą. Būtina įdiegta 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
Pirmoji užklausa turi grąžinti REFUSED, kitos dvi – lentelėje nurodytus adresus. Jei pakeitėte klausymo adresą ar prievadą, atitinkamai pakeiskite komandas. drop.test tyčia nesiunčia atsakymo, todėl užklausa baigiasi laukimo laiko klaida.
Veikimo principas
Užklausų ir atsakymų funkcijos sėkmės atveju grąžina 0, o veiksmą nustato per result->action. Veiksmo konstanta tiesiogiai negrąžinama.
Inicijuojant modulio kontekste paskiriamas atsakymo buferis. Atsakymo funkcija nukopijuoja pasiskolintą atsakymą į šį buferį ir jį pakeičia, o destroy() atlaisvina kontekstą. Pagal buferio nuosavybės taisykles pakaitinis buferis išlieka galiojantis ir funkcijai grįžus.
Atsakymo funkcija nekeičia pasirašytų pranešimų. Ji netikrina ir iš naujo nepasirašo DNSSEC duomenų. Sukurti ar pakeisti atsakymai nesaugomi pagrindinės paslaugos talpykloje.
Visas šaltinio kodas
#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);
}