រំលងទៅមាតិកាសំខាន់

ម៉ូឌុលគោលការណ៍ DNS (v2)

ឧទាហរណ៍ Linux C ABI v2 នេះបង្ហាញពីរបៀបគ្រប់គ្រងការសាកសួរ DNS និងរបៀបជំនួសការឆ្លើយតប DNS។ វា parse សារ DNS ដោយប្រើ libresolv និងរក្សាទុកការឆ្លើយតបដែលបានកែប្រែនៅក្នុង buffer ដែលជាកម្មសិទ្ធិរបស់ម៉ូឌុល។

ប្រតិបត្តិការនៃឧទាហរណ៍​

ឈ្មោះសាកសួរលទ្ធផល
allow.testបញ្ចប់ខ្សែច្រវាក់ query hook ហើយបន្តការសាកសួរ 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 ឡើយ។ ប្រសិនបើគ្មានកំណត់ត្រា A ដែលត្រូវគ្នានឹងការឆ្លើយតបរបស់ rewrite.test ឬប្រសិនបើមានកំណត់ត្រាហត្ថលេខា វានឹងត្រឡប់ការឆ្លើយតបដើមវិញ។

ALLOW គ្រាន់តែបញ្ចប់ខ្សែច្រវាក់ hook បច្ចុប្បន្នប៉ុណ្ណោះ។ វាមិនវាងកាត់វិធានឈ្មោះដែន ឬការត្រួតពិនិត្យវិធីសាស្ត្រទំនាក់ទំនងឡើយ ហើយលទ្ធផលអាចនឹងត្រូវបានផ្លាស់ប្តូរនៅក្នុង response hook។ សូមមើល វិធានសកម្មភាព DNS។

ការទាញយក និងការបង្កើត (Build)​

សូមទាញយក policy.c និង mudfish_dns_module.h ទៅក្នុងថតតែមួយ។ អ្នកអាចអានបឋមកថាពេញលេញនៅក្នុងកម្មវិធីរុករកនៅ ការណែនាំអំពីបឋមកថា។

នៅលើ Linux សូមដំឡើង C compiler, បណ្ណាល័យ C ស្តង់ដារ និង development headers សម្រាប់ 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 មានបំណងមិនផ្ញើការឆ្លើយតប ដូច្នេះ timeout នឹងកើតឡើង។

របៀបដំណើរការ​

Query និង response callbacks ត្រឡប់ 0 នៅពេលជោគជ័យ ហើយសកម្មភាពត្រូវបានកំណត់នៅក្នុង result->action។ ពួកវាមិនត្រឡប់តម្លៃថេរសកម្មភាពដោយផ្ទាល់ឡើយ។

អំឡុងពេលចាប់ផ្តើមដំបូង response buffer ត្រូវបានបម្រុងទុកនៅក្នុងបរិបទម៉ូឌុល (module context)។ Response hook ចម្លងការឆ្លើយតបដែលបានខ្ចីទៅក្នុង buffer នេះ កែប្រែវា ហើយ destroy() នឹងដោះលែងបរិបទ។ ស្របតាម វិធានភាពជាម្ចាស់ buffer buffer ជំនួសនៅតែរក្សាស្ថានភាពត្រឹមត្រូវ ទោះបីជា callback ត្រូវបានត្រឡប់ក៏ដោយ។

Response hook មិនផ្លាស់ប្តូរសារដែលបានចុះហត្ថលេខាឡើយ។ វាក៏មិនផ្ទៀងផ្ទាត់ ឬចុះហត្ថលេខាឡើងវិញលើទិន្នន័យ DNSSEC ដែរ។ ការឆ្លើយតបដែលបានបង្កើត ឬជំនួស មិនត្រូវបានរក្សាទុកក្នុងឃ្លាំងសម្ងាត់របស់ host ឡើយ។

ប្រភពកូដពេញលេញ​

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