DNS Richtlinienmodul (v2)
Dieses Linux C ABI v2-Beispiel zeigt, wie die DNS-Abfrage gesteuert und die DNS-Antwort ersetzt wird. Analysieren Sie die DNS-Nachricht mit libresolv und speichern Sie die geänderte Antwort in einem Puffer, der dem Modul gehört.
Beispielhaftes Verhalten
| Abfragename | Ergebnis |
|---|---|
allow.test | Beendet die -Abfrage-Hook-Kette und fährt mit der normalen DNS-Abfrage fort. |
block.test | Gibt REFUSED zurück, ohne den Upstream-Server abzufragen. |
drop.test | Verwirft die Abfrage ohne Upstream-Anfrage oder DNS-Antwort. |
redirect.test | A gibt 192.0.2.42 zurück und AAAA gibt 2001:db8::42 als TTL 60 Sekunden zurück. |
rewrite.test | Ersetzen Sie die Adresse des Datensatzes A in der vorzeichenlosen Antwort durch 203.0.113.7 und setzen Sie TTL auf 60 Sekunden. |
| Andere Namen | Setzt die normale Verarbeitung mit dem nächsten Modul fort. |
Konfigurieren Sie für allow.test und rewrite.test den DNS-Server zum Testen, um Antworten mit diesen Namen bereitzustellen. Das Beispiel erstellt keinen Upstream-Datensatz. Wenn die Antwort von rewrite.test keinen entsprechenden A-Datensatz hat oder ein Signaturdatensatz vorhanden ist, wird die Antwort unverändert zurückgegeben.
ALLOW beendet nur die aktuelle Hook-Kette. Es umgeht keine Domänenregeln oder Kommunikationsmethodenprüfungen und kann die Ergebnisse im Antwort-Hook ändern. Bitte beachten Sie die DNS Betriebsregeln.
Herunterladen und erstellen
Laden Sie policy.c und mudfish_dns_module.h im selben Verzeichnis herunter. Sie können den gesamten Header in Ihrem Browser im Header Guide lesen.
Installieren Sie den C-Compiler und die Standard-C-Bibliotheken und Entwicklungsheader von libresolv von Linux. Führen Sie den folgenden Befehl aus dem heruntergeladenen Verzeichnis aus:
cc -std=c11 -shared -fPIC -Wall -Wextra -Werror -I. \
policy.c -lresolv -o policy.so
Installieren Sie policy.so gemäß Modullast und starten Sie den Dienst neu. Starten Sie DNS in der App, bevor Sie Ihre Abfrage testen.
In einem lokalen Test mit der Standard-Empfangsadresse DNS können Sie die Blockierung und Generierung von Adressantworten mit den folgenden Abfragen überprüfen: dig muss installiert sein.
dig @127.0.0.1 block.test A
dig @127.0.0.1 redirect.test A
dig @127.0.0.1 redirect.test AAAA
Die erste Abfrage sollte REFUSED zurückgeben, und die anderen beiden Abfragen sollten die Adressen in der Tabelle zurückgeben. Wenn Sie die Empfangsadresse oder den Port ändern, passen Sie den Befehl entsprechend an. Die Abfrage drop.test sendet absichtlich keine Antwort, was zu einer Zeitüberschreitung führt.
Wie es funktioniert
Der Abfrage- und Antwortrückruf gibt bei Erfolg 0 zurück und setzt die Aktion auf result->action. Eine Aktionskonstante wird nicht direkt zurückgegeben.
Während der Initialisierung weisen wir einen Antwortpuffer im Modulkontext zu. Der Antwort-Hook kopiert die geliehene Antwort in diesen Puffer, ändert sie und gibt den Kontext in destroy() frei. Gemäß Pufferbesitzregeln bleibt der Ersatzpuffer auch nach Rückkehr des Rückrufs gültig.
Der Antwort-Hook ändert die signierte Nachricht nicht. Wir verifizieren oder signieren DNSSEC-Daten auch nicht neu. Von Ihnen erstellte oder ersetzte Antworten werden nicht im Host-Cache gespeichert.
vollständige Quelle
#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);
}