-
Notifications
You must be signed in to change notification settings - Fork 1
/
dns_server.lua
160 lines (137 loc) · 4.17 KB
/
dns_server.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
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/env lua
-- -*-lua-*-
--
-- $Id: dns_server.lua $
--
-- Author: Markus Stenberg <markus [email protected]>
--
-- Copyright (c) 2013 cisco Systems, Inc.
--
-- Created: Wed May 15 12:29:52 2013 mstenber
-- Last modified: Thu Jul 18 15:22:31 2013 mstenber
-- Edit time: 35 min
--
-- This is VERY minimalist DNS server. It abstracts away dns_tree
-- which is used for low-level storage of (number of zones) of RRs,
-- and performs lookups there. Anything beyond that is out of scope.
-- This is _relatively_ high performance, as lookups are performed on
-- tree of hashes, so typical time is proportional only to number of
-- labels in label list.
require 'mst'
require 'mst_eventful'
require 'dns_tree'
module(..., package.seeall)
local _eventful = mst_eventful.eventful
dns_server = _eventful:new_subclass{class='dns_server'}
RESULT_NXDOMAIN='nxdomain'
function dns_server:recreate_tree()
self:d('recreate_tree')
local root = dns_tree.node:new{label=''}
self.root = root
return root
end
function dns_server:get_root()
if not self.root
then
self:recreate_tree()
self:a(self.root, 'root not created despite recreate_tree call?')
end
return self.root
end
function dns_server:match(req)
self:a(req, 'no req')
local msg = req:get_msg()
if not msg
then
return nil, 'broken down msg ' .. mst.repr(msg)
end
if not msg.qd or #msg.qd ~= 1
then
return nil, 'no question/too many questions ' .. mst.repr(msg)
end
local root = self:get_root()
local q = msg.qd[1]
local r = {root:match_ll(q.name)}
self:d('got match', r)
return unpack(r)
end
function dns_server:process_match(req, r, o)
-- only result code we supply is NXDOMAIN reply; use that if relevant
if r == RESULT_NXDOMAIN
then
local r = self:create_dns_reply(req, {h={rcode=dns_const.RCODE_NXDOMAIN}})
return r
end
-- has to be a list of rr's from our own storage
-- (if something else, someone must've done handling before us!
self:a(type(r) == 'table')
local r = self:create_dns_reply(req, {an=r})
return r
end
function dns_server:process(req)
-- by default, assume it's query
-- (this may occur when testing locally and it is not an error)
local msg = req:get_msg()
local opcode = msg.opcode or dns_const.OPCODE_QUERY
if opcode ~= dns_const.OPCODE_QUERY
then
local r = self:create_dns_reply(req, {h={rcode=dns_const.RCODE_NOTIMP}})
return r
end
local r, err = self:match(req)
self:d('match result', r, err)
if not r
then
return nil, 'match error ' .. mst.repr(err)
end
return self:process_match(req, r, err)
end
function dns_server:create_dns_reply(req, o)
self:a(req, 'req missing')
local msg = req:get_msg()
self:a(msg)
o = o or {}
o.an = o.an or mst.array:new{}
o.ar = o.ar or mst.array:new{}
o.h = o.h or {}
-- these are always true
o.h.ra = true -- recursion available
o.h.qr = true -- reply
-- these are copied from req, if not specified in o
o.h.id = o.h.id or msg.h.id
o.h.rd = o.h.rd or msg.h.rd
o.qd = o.qd or msg.qd
return dns_channel.msg:new{msg=o, ip=req.ip, port=req.port, tcp=req.tcp}
end
function create_default_nxdomain_node_callback(o)
local n = dns_tree.create_node_callback(o)
function n:get_default(req)
self:d('returning nxdomain [create_default_nxdomain_node_callback]')
return RESULT_NXDOMAIN
end
mst.d('created default nxdomain node', n)
return n
end
function dns_server:add_rr(rr)
-- intermediate nodes will be nxdomain ones
local root = self:get_root()
self:d('add_rr', rr)
local o = root:find_or_create_subtree(rr.name,
-- end node
dns_tree.create_leaf_node_callback,
-- intermediate nodes
create_default_nxdomain_node_callback)
if not o.value then o.value = {} end
local l = o.value
for i, v in ipairs(l)
do
if v:equals(rr)
then
self:d('duplicate, skipping')
return
end
end
local prr = dns_db.rr:new(mst.table_copy(rr))
table.insert(l, prr)
return o
end