Saltar al contenido principal

Módulo de política DNS (v2)

Este ejemplo de Linux C ABI v2 muestra cómo controlar las consultas DNS y reemplazar las respuestas DNS. Analiza el mensaje DNS con libresolv y almacena la respuesta modificada en un búfer propiedad del módulo.

Comportamiento del ejemplo​

Nombre de la consultaResultado
allow.testTermina la cadena de hooks de consulta y continúa con la consulta DNS normal.
block.testDevuelve REFUSED sin consultar al servidor DNS de destino.
drop.testDescarta la consulta sin consultar a otro servidor ni enviar una respuesta DNS.
redirect.testDevuelve 192.0.2.42 para consultas A y 2001:db8::42 para consultas AAAA con un TTL de 60 segundos.
rewrite.testSustituye la dirección del registro A de una respuesta sin firmar por 203.0.113.7 y establece un TTL de 60 segundos.
Otros nombresContinúa con los siguientes módulos y el procesamiento normal.

Configure el servidor DNS de prueba para que responda a allow.test y rewrite.test. El ejemplo no crea registros en ese servidor. Si la respuesta de rewrite.test no contiene un registro A correspondiente o contiene registros de firma, se devuelve sin cambios.

ALLOW solo termina la cadena de hooks actual. No omite las reglas de dominio ni las comprobaciones del método de comunicación; el hook de respuesta aún puede modificar el resultado. Consulte Reglas de operación DNS.

Descargar y compilar​

Descargue policy.c y mudfish_dns_module.h en el mismo directorio. Puede leer la cabecera completa en el navegador desde Guía de la cabecera.

En Linux, instale un compilador de C, la biblioteca estándar de C y las cabeceras de desarrollo de libresolv. Ejecute el siguiente comando en el directorio donde descargó los archivos:

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

Instale policy.so siguiendo Cargar módulo y reinicie el servicio. Antes de probar las consultas, inicie DNS desde la aplicación.

Para una prueba local con la dirección de escucha DNS predeterminada, utilice las siguientes consultas para comprobar el bloqueo y la generación de respuestas con direcciones. Debe tener instalado 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

La primera consulta debe devolver REFUSED; las otras dos, las direcciones de la tabla. Si ha cambiado la dirección o el puerto de escucha, ajuste los comandos. La consulta drop.test no recibe respuesta deliberadamente, por lo que se agota su tiempo de espera.

Cómo funciona​

Los callbacks de consulta y respuesta devuelven 0 si se completan correctamente y establecen la acción en result->action. No devuelven directamente una constante de acción.

Durante la inicialización, se reserva un búfer de respuesta en el contexto del módulo. El hook de respuesta copia en él la respuesta prestada y la modifica. destroy() libera el contexto. Según Reglas de propiedad del búfer, el búfer alternativo sigue siendo válido después de que termine el callback.

El hook de respuesta no modifica mensajes firmados ni valida o vuelve a firmar datos DNSSEC. Las respuestas generadas o sustituidas no se guardan en la caché del host.

Código fuente completo​

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