Μονάδα πολιτικής DNS (v2)
Αυτό το παράδειγμα Linux C ABI v2 δείχνει πώς ελέγχονται τα ερωτήματα DNS και αντικαθίστανται οι αποκρίσεις. Αναλύει μηνύματα DNS με libresolv και αποθηκεύει την τροποποιημένη απόκριση σε buffer που ανήκει στη μονάδα.
Συμπεριφορά παραδείγματος
| Όνομα ερωτήματος | Αποτέλεσμα |
|---|---|
allow.test | Τερματίζει την αλυσίδα hooks ερωτήματος και συνεχίζει την κανονική αναζήτηση DNS. |
block.test | Επιστρέφει REFUSED χωρίς ερώτημα στον upstream διακομιστή. |
drop.test | Απορρίπτει το ερώτημα χωρίς upstream αναζήτηση ή απόκριση DNS. |
redirect.test | Επιστρέφει 192.0.2.42 για ερωτήματα A και 2001:db8::42 για AAAA, με TTL 60 δευτερόλεπτα. |
rewrite.test | Αλλάζει τη διεύθυνση εγγραφών A μη υπογεγραμμένης απόκρισης σε 203.0.113.7 και το TTL σε 60 δευτερόλεπτα. |
| Άλλα ονόματα | Συνεχίζει στην επόμενη μονάδα και στην κανονική επεξεργασία. |
Για τα allow.test και rewrite.test, ρυθμίστε τον δοκιμαστικό διακομιστή DNS να παρέχει αποκρίσεις για αυτά τα ονόματα. Το παράδειγμα δεν δημιουργεί upstream εγγραφές. Αν η απόκριση του rewrite.test δεν έχει κατάλληλη εγγραφή A ή περιέχει εγγραφές υπογραφής, επιστρέφεται αμετάβλητη.
Το ALLOW τερματίζει μόνο την τρέχουσα αλυσίδα hooks. Δεν παρακάμπτει κανόνες τομέων ή ελέγχους τρόπου επικοινωνίας και το αποτέλεσμα μπορεί να αλλάξει από hook απόκρισης. Δείτε τους κανόνες ενεργειών DNS.
Λήψη και μεταγλώττιση
Κατεβάστε τα policy.c και mudfish_dns_module.h στον ίδιο κατάλογο. Μπορείτε να διαβάσετε ολόκληρη την κεφαλίδα στο πρόγραμμα περιήγησης από τον οδηγό κεφαλίδας.
Στο Linux, εγκαταστήστε μεταγλωττιστή C και τις κεφαλίδες ανάπτυξης της τυπικής βιβλιοθήκης C και του libresolv. Στον κατάλογο λήψης εκτελέστε:
cc -std=c11 -shared -fPIC -Wall -Wextra -Werror -I. \
policy.c -lresolv -o policy.so
Εγκαταστήστε το policy.so σύμφωνα με τη φόρτωση μονάδας και επανεκκινήστε την υπηρεσία. Πριν δοκιμάσετε ερωτήματα, ξεκινήστε το DNS από την εφαρμογή.
Σε τοπική δοκιμή με την προεπιλεγμένη διεύθυνση ακρόασης DNS, τα παρακάτω ερωτήματα ελέγχουν τον αποκλεισμό και τη δημιουργία απαντήσεων διεύθυνσης. Πρέπει να είναι εγκατεστημένο το 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
Το πρώτο ερώτημα πρέπει να επιστρέψει REFUSED και τα άλλα δύο τις διευθύνσεις του πίνακα. Αν αλλάξατε διεύθυνση ή θύρα ακρόασης, προσαρμόστε τις εντολές ανάλογα. Το ερώτημα drop.test λήγει λόγω χρονικού ορίου, επειδή σκόπιμα δεν αποστέλλεται απόκριση.
Τρόπος λειτουργίας
Τα callbacks ερωτήματος και απόκρισης επιστρέφουν 0 σε επιτυχία και ορίζουν την ενέργεια στο result->action. Δεν επιστρέφουν απευθείας σταθερές ενεργειών.
Κατά την αρχικοποίηση, δεσμεύεται buffer απόκρισης στο context της μονάδας. Το hook απόκρισης αντιγράφει τη δανεισμένη απόκριση σε αυτό το buffer και την τροποποιεί, ενώ το destroy() αποδεσμεύει το context. Σύμφωνα με τους κανόνες ιδιοκτησίας buffers, το εναλλακτικό buffer παραμένει έγκυρο μετά την επιστροφή του callback.
Το hook απόκρισης δεν τροποποιεί υπογεγραμμένα μηνύματα. Δεν επαληθεύει ούτε υπογράφει ξανά δεδομένα DNSSEC. Οι δημιουργημένες ή αντικαταστημένες αποκρίσεις δεν αποθηκεύονται στην προσωρινή μνήμη του host.
Πλήρης πηγαίος κώδικας
#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);
}