-
Notifications
You must be signed in to change notification settings - Fork 60
/
dns.lua
146 lines (132 loc) · 3.93 KB
/
dns.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
local _M = {}
local mt = {__index = _M}
function _M.new(payload, size)
local self = {}
self.payload = payload
self.size = size
self.pos = 1
setmetatable(self, mt)
return self
end
function _M.read_int8(self)
if self.pos > self.size then
return nil, "Read out of bound."
end
local b = string.byte(self.payload, self.pos), nil
self.pos = self.pos + 1
return b
end
function _M.read_int16(self)
if self.pos + 1 > self.size then
return nil, "Read out of bound."
end
local b1 = string.byte(self.payload, self.pos)
local b2 = string.byte(self.payload, self.pos + 1)
self.pos = self.pos + 2
return bit32.lshift(b1, 8) + b2, nil
end
function _M.read_int32(self)
if self.pos + 3 > self.size then
return nil, "Read out of bound."
end
local b1 = string.byte(self.payload, self.pos)
b1 = bit32.lshift(b1, 24)
local b2 = string.byte(self.payload, self.pos + 1)
b2 = bit32.lshift(b2, 16)
local b3 = string.byte(self.payload, self.pos + 2)
b3 = bit32.lshift(b3, 8)
local b4 = string.byte(self.payload, self.pos + 3)
self.pos = self.pos + 4
return (b1 + b2 + b3 + b4), nil
end
-- unsupport int64 in lua
function _M.read_int64(self)
local h, h_err = self:read_int32()
local l, l_err = self:read_int32()
if h_err or l_err then
return nil, h_err or l_err
end
return h * 4294967296 + l, nil
end
function _M.read_domain(self)
local start
local domain = ""
local size, err = self:read_int8()
while size ~= 0 do
if err then
return nil, err
end
if self.pos + size - 1 > self.size then
return nil, "size is out of bound"
end
start = self.pos
self.pos = self.pos + size
domain = domain .. string.sub(self.payload, start, start + size -1).."."
size, err = self:read_int8()
end
return domain, nil
end
local qtype_map = {
"A", -- 1
"NS", -- 2
"MD", -- 3
"MD", -- 4
"MF", -- 5
"CNAME", -- 6
"SOA", -- 7
"MB", -- 8
"MG", -- 9
"MR", -- 10
"NULL", -- 11
"WKS", -- 12
"PTR", -- 13
"HINFO", -- 14
"MINFO", -- 15
"MX", -- 16
"TXT", -- 17
[28] = "AAAA"
}
local storage = {}
function process(packet)
-- dns query or response header is 24 bytes
if packet.size < 12 then
return 0
end
local decoder = _M.new(packet.payload, packet.size)
local id = decoder:read_int16()
local code = decoder:read_int16()
local qcount = decoder:read_int16()
local acount = decoder:read_int16()
local nscount = decoder:read_int16()
local arcount = decoder:read_int16()
local qr = bit32.rshift(code, 15)
if qr == 0 then
-- query
local domains = ""
for i = 1, qcount, 1 do
domain, err= decoder:read_domain()
qtype, err = decoder:read_int16()
qname = qtype
-- skip qclass
decoder:read_int16()
if qtype_map[qtype] then
qname = qtype_map[qtype]
end
domains = domains .. " " .. domain .. " | " ..qname
end
local key = packet.sip..":".. packet.sport.." => ".. packet.dip..":".. packet.dport.." ".. id
storage[key] = {domains=domains, tv_sec = packet.tv_sec, tv_usec = packet.tv_usec}
else
-- response
local key = packet.dip..":".. packet.dport.." => ".. packet.sip..":".. packet.sport.." ".. id
if storage[key] then
query = storage[key]
local cost = (packet.tv_sec - query.tv_sec) * 1000 + (packet.tv_usec - query.tv_usec) / 1000
local time_str = os.date('%Y-%m-%d %H:%M:%S', packet.tv_sec).."."..string.format("%06d", packet.tv_usec)
print(string.format("%s | %s | %s | %3.3fms", time_str, key, query.domains, cost))
storage[key] = nil
else
print("recevie unknown response:", key)
end
end
end