Siirry pääsisältöön

DNS-käytäntömoduuli (v2)

Tämä Linuxin C ABI v2 -esimerkki näyttää DNS-kyselyiden hallinnan ja vastausten korvaamisen. Se jäsentää DNS-viestit libresolv-kirjastolla ja tallentaa muutetut vastaukset moduulin omistamaan puskuriin.

Esimerkin toiminta​

Kyselyn nimiTulos
allow.testPäättää kyselykoukkujen ketjun ja jatkaa tavallista DNS-kyselyä.
block.testPalauttaa REFUSED kysymättä ylemmältä DNS-palvelimelta.
drop.testHylkää kyselyn ilman ylemmän palvelimen kyselyä tai DNS-vastausta.
redirect.testPalauttaa A-kyselyyn 192.0.2.42 ja AAAA-kyselyyn 2001:db8::42, TTL 60 sekuntia.
rewrite.testMuuttaa allekirjoittamattoman vastauksen A-tietueen osoitteeksi 203.0.113.7 ja TTL:ksi 60 sekuntia.
Muut nimetJatkaa seuraavaan moduuliin ja tavalliseen käsittelyyn.

Määritä testi-DNS-palvelin vastaamaan nimiin allow.test ja rewrite.test. Esimerkki ei luo ylemmän palvelimen tietueita. Jos rewrite.test-vastauksessa ei ole kyseistä A-tietuetta tai siinä on allekirjoitustietueita, vastaus palautetaan muuttamatta.

ALLOW päättää vain nykyisen koukkuketjun. Se ei ohita verkkotunnussääntöjä tai yhteystavan tarkistuksia, ja vastauskoukku voi vielä muuttaa tulosta. Katso DNS-toimintojen säännöt.

Lataaminen ja kääntäminen​

Lataa policy.c ja mudfish_dns_module.h samaan hakemistoon. Koko otsakkeen voit lukea selaimessa otsakeohjeesta.

Asenna Linuxiin C-kääntäjä sekä C-standardikirjaston ja libresolv-kirjaston kehitysotsakkeet. Suorita lataushakemistossa:

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

Asenna policy.so moduulin latausohjeen mukaan ja käynnistä palvelu uudelleen. Käynnistä DNS sovelluksesta ennen kyselyiden testaamista.

Paikallisessa testissä DNS:n oletuskuunteluosoitteella voit tarkistaa eston ja osoitevastauksen luonnin seuraavilla kyselyillä. dig täytyy olla asennettuna.

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

Ensimmäisen kyselyn pitäisi palauttaa REFUSED ja kahden muun taulukon osoitteet. Jos muutit kuunteluosoitetta tai porttia, muuta komennot vastaavasti. drop.test ei tarkoituksellisesti lähetä vastausta, joten kysely aikakatkaistaan.

Toimintaperiaate​

Kysely- ja vastauscallbackit palauttavat onnistuessaan 0, ja toiminto asetetaan kenttään result->action. Toimintovakiota ei palauteta suoraan.

Alustuksessa moduulikontekstiin varataan vastauspuskuri. Vastauskoukku kopioi lainatun vastauksen puskuriin ja muuttaa sitä. destroy() vapauttaa kontekstin. Puskurin omistussääntöjen mukaisesti korvaava puskuri pysyy voimassa callbackin paluun jälkeen.

Vastauskoukku ei muuta allekirjoitettuja viestejä eikä tarkista tai allekirjoita DNSSEC-dataa uudelleen. Luotuja tai korvattuja vastauksia ei tallenneta isännän välimuistiin.

Koko lähdekoodi​

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