مرکزی مواد پر جائیں

DNS پالیسی ماڈیول (v2)

یہ Linux C ABI v2 مثال یہ ظاہر کرتی ہے کہ DNS کوئریز کو کیسے کنٹرول کیا جائے اور DNS جوابات کو کیسے بدلا جائے۔ یہ libresolv کے ذریعے DNS پیغامات کو پارس کرتی ہے اور ماڈیول کے ملکیتی بفر میں ترمیم شدہ جواب کو محفوظ کرتی ہے۔

مثال کا رویہ​

کوئری کا نامنتیجہ
allow.testکوئری ہک چین کو ختم کرتا ہے اور عام DNS تلاش کو جاری رکھتا ہے۔
block.testاپ اسٹریم سرور کو تلاش بھیجے بغیر REFUSED لوٹاتا ہے۔
drop.testاپ اسٹریم تلاش یا DNS جواب کے بغیر کوئری کو ڈراپ کر دیتا ہے۔
redirect.testA کوئری کے لیے 192.0.2.42 اور AAAA کوئری کے لیے 2001:db8::42 کو TTL 60 سیکنڈ کے ساتھ واپس کرتا ہے۔
rewrite.testغیر دستخط شدہ جواب کے A ریکارڈ ایڈریس کو 203.0.113.7 میں بدل دیتا ہے اور TTL کو 60 سیکنڈ پر سیٹ کرتا ہے۔
دیگر تمام ناماگلے ماڈیول اور نارمل پروسیسنگ کو جاری رکھتے ہیں۔

allow.test اور rewrite.test کے لیے ٹیسٹ DNS سرور کو اس طرح کنفیگر کریں کہ وہ متعلقہ نام کا جواب فراہم کرے۔ یہ مثال بذاتِ خود اپ اسٹریم ریکارڈز تیار نہیں کرتی۔ اگر rewrite.test کے جواب میں مطلوبہ A ریکارڈ موجود نہ ہو یا دستخط شدہ ریکارڈز موجود ہوں تو جواب کو بغیر تبدیلی کے لوٹا دیا جاتا ہے۔

ALLOW صرف موجودہ ہک چین کو مکمل کرتا ہے۔ یہ ڈومین قواعد یا کنکشن پروٹوکول کی جانچ کو نظر انداز نہیں کرتا، اور رسپانس ہک میں نتیجہ تبدیل بھی ہو سکتا ہے۔ مزید معلومات کے لیے 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 کوئری جان بوجھ کر کوئی جواب نہیں بھیجتی، لہٰذا اس پر ٹائم آؤٹ ہو گا۔

کام کرنے کا طریقہ کار​

کوئری اور رسپانس کال بیکس کامیابی پر 0 لوٹاتے ہیں اور ایکشن کو result->action میں سیٹ کرتے ہیں۔ ایکشن کنسٹینٹ کو براہِ راست واپس نہیں کیا جاتا۔

انیشلائزیشن کے عمل میں ماڈیول سیاق کے لیے ایک رسپانس بفر مختص کیا جاتا ہے۔ رسپانس ہک مستعار لیے گئے جواب کو اس بفر میں کاپی کر کے ترمیم کرتا ہے، اور destroy() میں کانٹیکسٹ کو آزاد کر دیتا ہے۔ بفر ملکیت کے قواعد کے مطابق کال بیک لوٹنے کے بعد بھی متبادل بفر درست حالت میں برقرار رہتا ہے۔

رسپانس ہک دستخط شدہ پیغامات میں کوئی ترمیم نہیں کرتا۔ یہ DNSSEC ڈیٹا کی توثیق یا دوبارہ دستخط بھی نہیں کرتا۔ بنائے گئے یا تبدیل کیے گئے جوابات کو ہوسٹ کیشے میں محفوظ نہیں کیا جاتا۔

مکمل سورس​

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