နားလည်ထားရမယ့် အချက်
DNS ကို 'domain name တွေရဲ့ ဖုန်းစာအုပ်' လို့ ပြောလေ့ရှိပေမယ့် ဒီဥပမာက အရေးအကြီးဆုံး အချက်ကို ဖုံးကွယ်ထားတယ် — ဖုန်းစာအုပ် ဆိုတာ တစ်နေရာတည်းမှာ ရှိတဲ့ စာရင်းတစ်ခု ဖြစ်ပေမယ့် DNS က မဟုတ်ဘူး။ ကမ္ဘာပေါ်က domain အားလုံးကို server တစ်ခုတည်းက သိထားရင် အဲဒီ server ဟာ traffic အရေအတွက်ရော၊ update လုပ်ခွင့်ရော၊ ပျက်စီးမှုရော အားလုံးအတွက် single point ဖြစ်သွားမယ်။ ဒါကြောင့် DNS ကို hierarchy အဖြစ် ဖွဲ့စည်းထားပြီး အလွှာတစ်ခုစီက အောက်အလွှာကို လက်ညှိုးထိုးပြပေးရုံသာ လုပ်တယ်။ root servers က .com, .org, .mm စတဲ့ TLD တွေအတွက် ဘယ်သူတွေက တာဝန်ယူထားလဲ ဆိုတာပဲ သိတယ်။ TLD servers က domain တစ်ခုစီအတွက် authoritative nameserver ဘယ်သူလဲ ဆိုတာပဲ သိတယ်။ တကယ့် record ကို authoritative server ကပဲ ကိုင်ထားပြီး၊ ဒါက ကိုယ့် domain ရဲ့ record ကို ကိုယ်တိုင် ချက်ချင်း ပြင်နိုင်စေတယ်။
လက်တွေ့မှာ browser က ဒီခရီးကို ကိုယ်တိုင် မလျှောက်ဘူး။ operating system ထဲက stub resolver က query တစ်ခုကို recursive resolver (ISP ရဲ့ ဟာ (သို့) public resolver တစ်ခု) ဆီ ပို့လိုက်ပြီး 'အဖြေ ယူပေးပါ' လို့ ပြောလိုက်ရုံပဲ။ recursive resolver ကသာ root ကနေ TLD ကနေ authoritative အထိ တစ်ဆင့်ချင်း မေးသွားတာ ဖြစ်တယ်။
record အမျိုးအစားတွေက name တစ်ခုကို ဘာနဲ့ ချိတ်လဲ ဆိုတာ သတ်မှတ်တယ် — A က IPv4 address, AAAA က IPv6, CNAME က နာမည်တစ်ခုကို နောက်နာမည်တစ်ခုဆီ လွှဲပေးတယ်၊ MX က mail server, NS က authoritative nameserver, TXT က domain ownership အတည်ပြုချက် စတဲ့ စာသား။ ဒီအဖြေတိုင်းမှာ TTL တစ်ခု ပါလာပြီး၊ ဒါက 'ဒီအဖြေကို ဘယ်လောက်ကြာအောင် cache ထားလို့ ရတယ်' ဆိုတာ ဖြစ်တယ်။ ဒီနေရာမှာ လက်တွေ့ အကျိုးဆက် ကြီးကြီးမားမား တစ်ခု ရှိတယ် — server အသစ်ဆီ migrate မလုပ်ခင် နာရီပေါင်းများစွာ ကြိုတင်ပြီး TTL ကို 300 စက္ကန့်လောက် လျှော့ထားရမယ်။ မလျှော့ခဲ့ရင် TTL 86400 နဲ့ cache ထဲ ရောက်နှင့်ပြီးသား resolver တွေက IP အဟောင်းကို နောက်တစ်ရက်လုံး ဆက်သုံးနေဦးမယ်၊ ကိုယ်က record ကို ဘယ်လောက် မြန်မြန် ပြင်ပြင် ဖြစ်တယ်။
RECURSIVE DNS RESOLUTION, STEP BY STEP
--------------------------------------
Stub Recursive Root TLD Auth
resolver resolver servers (.test) NS
| | | | |
|-----(1)---->| | | |
| A? www.shop.test | | |
| |------(2)----->| | |
| |<-----(3)------| | |
| | "ask the .test servers" | |
| |-------------(4)------------>| |
| |<------------(5)-------------| |
| | "ask ns1.shop.test" | |
| |---------------------(6)-------------------->|
| |<--------------------(7)---------------------|
| | A 203.0.113.10 TTL 300 | |
|<----(8)-----| | | |
| answer, cached for 300 s | | |လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
ရှေ့တစ်ပတ်မှာ web server ကို hosting provider အသစ်တစ်ခုဆီ ပြောင်းရေးဆွဲထားတယ် ဆိုပါစို့။ မှားလေ့ရှိတဲ့ လုပ်ငန်းစဉ်က 'ည ၁၂ နာရီမှာ A record ကို ပြောင်း၊ ပြီးရင် အဆင်ပြေသွားပြီ' ဆိုတာ ဖြစ်တယ်။ တကယ်ဖြစ်တာက user တစ်ချို့က ချက်ချင်း server အသစ်ကို ရောက်ပြီး ကျန်တဲ့သူတွေက နောက်တစ်ရက်လုံး server အဟောင်းဆီ ဆက်ရောက်နေတာ ဖြစ်တယ် — ဒါက user တစ်ချို့ရဲ့ order တွေဟာ ပိတ်တော့မယ့် database ထဲ ဝင်နေတယ် ဆိုတာ ဖြစ်တယ်။
မှန်ကန်တဲ့ လုပ်ငန်းစဉ်က အဆင့်လေးဆင့် ဖြစ်တယ်။ ပထမ၊ migration မလုပ်ခင် ၄၈ နာရီမှာ A record ရဲ့ TTL ကို 86400 ကနေ 300 အထိ လျှော့ပါ။ ဒီပြောင်းလဲမှုကိုယ်တိုင်က TTL အဟောင်းအတိုင်း ပျံ့နှံ့ရမှာ ဖြစ်လို့ ကြိုတင် လုပ်ရတာ ဖြစ်တယ်။ ဒုတိယ၊ server အသစ်ကို အရင် တင်ပြီး၊ ကိုယ့် laptop ရဲ့ hosts file မှာ ချိန်ကြည့်ပြီး အလုပ်လုပ်မလုပ် အတည်ပြုပါ။ တတိယ၊ record ကို ပြောင်းပါ — အခုဆိုရင် ကမ္ဘာတစ်ဝှမ်း ငါးမိနစ်အတွင်း ကူးပြောင်းသွားပြီ။ စတုတ္ထ၊ server အဟောင်းကို ချက်ချင်း မပိတ်ဘဲ နာရီအနည်းငယ် ဆက်ဖွင့်ထားပါ၊ log ကို ကြည့်ပြီး request တွေ သုညရောက်မှ ပိတ်ပါ။ တည်ငြိမ်သွားပြီးမှ TTL ကို ပြန်တင်လိုက်ပါ။ မှတ်ထားရမယ့် အချက်က DNS ဟာ ချက်ချင်း ပြောင်းလဲတဲ့ switch မဟုတ်ဘဲ cache ပေါင်းများစွာနဲ့ ဖွဲ့စည်းထားတဲ့ စနစ်တစ်ခု ဖြစ်လို့ ကူးပြောင်းချိန်ကို TTL က ကြိုတင် ဆုံးဖြတ်ထားပြီး သားပဲ ဆိုတာ ဖြစ်တယ်။
အတူတူ စမ်းရေးကြည့်မယ်
import struct
# A real DNS response, captured once and hard-coded so this runs offline.
# Query was "www.example.test A"; the answer uses a documentation address.
MESSAGE = bytes.fromhex(
"1a2b" # ID: echoed back so the client can match reply to request
"8180" # flags: response, recursion desired + available, no error
"0001" # QDCOUNT: one question
"0001" # ANCOUNT: one answer
"0000" # NSCOUNT
"0000" # ARCOUNT
"03777777076578616d706c65047465737400" # www.example.test as labels
"0001" # QTYPE = A
"0001" # QCLASS = IN
"c00c" # answer name: pointer back to offset 12 (compression)
"0001" # TYPE = A
"0001" # CLASS = IN
"0000012c" # TTL, in seconds
"0004" # RDLENGTH
"cb00710a" # RDATA: the IPv4 address
)
TYPES = {1: "A", 2: "NS", 5: "CNAME", 15: "MX", 16: "TXT", 28: "AAAA"}
RCODES = {0: "NOERROR", 2: "SERVFAIL", 3: "NXDOMAIN"}
# ---- 1. The fixed 12-byte header ---------------------------------------
ident, flags, qd, an, ns, ar = struct.unpack("!HHHHHH", MESSAGE[:12])
print("transaction id :", hex(ident))
print("QR :", "response" if flags >> 15 else "query")
print("recursion : desired=%d available=%d"
% ((flags >> 8) & 1, (flags >> 7) & 1))
print("authoritative :", bool((flags >> 10) & 1))
print("rcode :", RCODES.get(flags & 0xF, "OTHER"))
print("counts : qd=%d an=%d ns=%d ar=%d" % (qd, an, ns, ar))
# ---- 2. Names are length-prefixed labels, not dotted strings ------------
def read_name(msg, pos):
labels = []
while True:
n = msg[pos]
if n == 0:
return ".".join(labels), pos + 1
if n & 0xC0 == 0xC0: # compression pointer
target = struct.unpack("!H", msg[pos:pos + 2])[0] & 0x3FFF
name, _ = read_name(msg, target)
labels.append(name)
return ".".join(labels), pos + 2
labels.append(msg[pos + 1:pos + 1 + n].decode())
pos += 1 + n
qname, pos = read_name(MESSAGE, 12)
qtype, qclass = struct.unpack("!HH", MESSAGE[pos:pos + 4])
pos += 4
print()
print("question : %s type=%s" % (qname, TYPES.get(qtype, qtype)))
# ---- 3. The answer record ----------------------------------------------
aname, pos = read_name(MESSAGE, pos)
atype, aclass, ttl, rdlen = struct.unpack("!HHIH", MESSAGE[pos:pos + 10])
pos += 10
rdata = MESSAGE[pos:pos + rdlen]
print("answer name :", aname)
print("answer type :", TYPES.get(atype, atype))
print("ttl : %d seconds (%d minutes)" % (ttl, ttl // 60))
print("rdata :", ".".join(str(b) for b in rdata))
print()
print("whole message : %d bytes (%d of them the header)"
% (len(MESSAGE), 12))transaction id : 0x1a2b
QR : response
recursion : desired=1 available=1
authoritative : False
rcode : NOERROR
counts : qd=1 an=1 ns=0 ar=0
question : www.example.test type=A
answer name : www.example.test
answer type : A
ttl : 300 seconds (5 minutes)
rdata : 203.0.113.10
whole message : 50 bytes (12 of them the header)၅ မိနစ် စမ်းကြည့်
hex string ထဲက TTL ကို 0000012c ကနေ 00015180 (86400) အဖြစ် ပြောင်းပြီး run ကြည့်ပါ။ ပြီးရင် ANCOUNT ကို 0002 အဖြစ် ပြောင်းရင် parser က ဘာလို့ မှားသွားလဲ စဉ်းစားပါ — record အရေအတွက်ကို parser က ဘယ်လို သိရသလဲ။
သတိလေးတစ်ချက်
DNS record ပြောင်းပြီးတာနဲ့ ကမ္ဘာတစ်ဝှမ်း ချက်ချင်း ပြောင်းသွားပြီ လို့ ယူဆတာ။ ရှိပြီးသား cache entry တွေက သူတို့ရဲ့ TTL ကုန်မှ ပျောက်မှာ ဖြစ်တယ်။
domain ရဲ့ အမြစ် (apex) မှာ CNAME ထားဖို့ ကြိုးစားတာ။ standard က apex မှာ CNAME ကို ခွင့်မပြုဘူး — provider တွေရဲ့ ALIAS (သို့) ANAME လိုမျိုး တိုးချဲ့ချက်ကို သုံးရတယ်။
Cloudflare Learning Center - What is DNS? — Computer Networking