Module ng Patakaran sa DNS (v2)
Ipinapakita ng halimbawang ito ng Linux C ABI v2 kung paano kontrolin ang mga DNS query at palitan ang mga tugon sa DNS. Nagpa-parse ito ng mga mensahe ng DNS gamit ang libresolv at sine-save ang binagong tugon sa buffer na pagmamay-ari ng module.
Operasyon ng Halimbawa
| Pangalan ng Query | Resulta |
|---|---|
allow.test | Tinatapos ang query hook chain at ipinagpapatuloy ang karaniwang DNS query. |
block.test | Nagbabalik ng REFUSED nang hindi nagtatanong sa upstream server. |
drop.test | Itinatapon ang query nang walang upstream query o tugon sa DNS. |
redirect.test | Nagbabalik ng 192.0.2.42 para sa A query, at 2001:db8::42 para sa AAAA query nang may TTL na 60 segundo. |
rewrite.test | Pinapalitan ng 203.0.113.7 ang A record address ng hindi nilagdaang tugon at itinatakda ang TTL sa 60 segundo. |
| Iba pang mga pangalan | Ipinagpapatuloy ang susunod na module at karaniwang pagpoproseso. |
Para sa allow.test at rewrite.test, i-configure ang iyong pansubok na DNS server upang magbigay ng tugon para sa mga nasabing pangalan. Ang halimbawa ay hindi gumagawa ng upstream record. Kung walang tumutugmang A record sa tugon ng rewrite.test o kung mayroong signature record, ibabalik nito ang tugon nang walang pagbabago.
Tinatapos lamang ng ALLOW ang kasalukuyang hook chain. Hindi nito bina-bypass ang mga panuntunan sa domain o pagsusuri sa paraan ng komunikasyon, at maaari pa ring mabago ang resulta sa response hook. Sumangguni sa Mga Panuntunan sa Aksyon sa DNS.
Pag-download at Pag-build
I-download ang policy.c at mudfish_dns_module.h sa parehong direktoryo. Maaari mong basahin ang buong header sa browser sa Gabay sa Header.
Sa Linux, mag-install ng C compiler at mga development header ng standard C library at libresolv. Patakbuhin ang sumusunod na command sa direktoryo kung saan nag-download:
cc -std=c11 -shared -fPIC -Wall -Wextra -Werror -I. \
policy.c -lresolv -o policy.so
I-install ang policy.so ayon sa Pag-load ng Module at i-restart ang serbisyo. Simulan ang DNS sa app bago subukan ang mga query.
Sa lokal na pagsusuri na gumagamit ng default na DNS listening address, maaari mong suriin ang pagharang at paggawa ng tugon sa address gamit ang mga sumusunod na query. Kailangang naka-install ang 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
Ang unang query ay dapat magbalik ng REFUSED, at ang dalawang natitirang query ay dapat magbalik ng mga address na nakalagay sa talahanayan. Kung binago mo ang listening address o port, iakma rin ang command ayon dito. Ang query na drop.test ay sadyang hindi nagpapadala ng tugon kaya magkakaroon ito ng timeout.
Paano Ito Gumagana
Ang mga callback ng query at tugon ay nagbabalik ng 0 kapag matagumpay at itinatakda ang aksyon sa result->action. Hindi ito direktang nagbabalik ng mga constant ng aksyon.
Sa proseso ng pag-initialize, naglalaan ito ng response buffer sa module context. Kinokopya ng response hook ang hiniram na tugon papunta sa buffer na ito bago baguhin, at pinapalaya ang context sa destroy(). Ayon sa Mga Panuntunan sa Pagmamay-ari ng Buffer, nananatiling nasa valid na estado ang kapalit na buffer kahit nakabalik na ang callback.
Hindi binabago ng response hook ang mga nilagdaang mensahe. Hindi rin nito vine-verify o muling nilalagdaan ang DNSSEC data. Ang mga ginawa o pinalitang tugon ay hindi sine-save sa cache ng host.
Buong Source
#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);
}