-
Notifications
You must be signed in to change notification settings - Fork 1
/
dns_name.lua
173 lines (160 loc) · 4.61 KB
/
dns_name.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#!/usr/bin/env lua
-- -*-lua-*-
--
-- $Id: dns_name.lua $
--
-- Author: Markus Stenberg <markus [email protected]>
--
-- Copyright (c) 2013 cisco Systems, Inc.
--
-- Created: Mon Jan 14 13:08:37 2013 mstenber
-- Last modified: Mon Oct 21 12:09:08 2013 mstenber
-- Edit time: 66 min
--
-- TODO: Figure how to make this new, faster name compression correct
-- (note use of _null below; DNS labels with nulls in them will be
-- encoded incorrectly with bad luck). However, in the typical hybrid
-- proxy case, there's only one level on which user-provided strings
-- can be used, and this should work just fine..
require 'dns_const'
require 'codec'
module(..., package.seeall)
local cursor_has_left = codec.cursor_has_left
--- general utilities to deal with FQDN en/decode
function _try_decode_name_rec(cur, h, n)
--mst.d('try_decode_name_rec', h)
if not cursor_has_left(cur, 1)
then
return nil, 'out of bytes (reading name)'
end
local npos = cur.pos
local b0 = cur:read(1)
local v = string.byte(b0)
if v >= 64
then
if not cursor_has_left(cur, 1)
then
return nil, 'out of bytes (reading compression offset)'
end
if v < (64+128)
then
return nil, 'invalid high bits in name label ' .. tostring(v)
end
v = v - 64 - 128
local b = cur:read(1)
if not h.disable_decode_names
then
local v2 = string.byte(b)
local ofs = v * 256 + v2
mst.a(h, 'h not set when decoding name with message compression')
if not h[ofs]
then
--mst.d('eek - about to blow up - dump', h, 'missing offset', ofs)
return nil, 'unable to find value at ofs ' .. tostring(ofs)
end
local on, oofs = unpack(h[ofs])
-- 'other name' = on. 'oofs' = which entry to start at copying
for i=oofs,#on
do
n[#n+1] = on[i]
end
--mst.d('found from', ofs, mst.array_slice(n, oofs))
else
n[#n+1] = {b0, b}
end
return n
end
-- let's see if it's the end
if v == 0
then
return n
end
-- not end -> have to have the bytes
if not cursor_has_left(cur, v)
then
return nil, 'out of bytes (label body)'
end
-- read the actual string
b = cur:read(v)
-- store position to hash for message compression use
if h
then
--mst.d('adding', npos, #n+1, b)
h[npos] = {n, #n + 1}
end
n[#n+1] = b
-- and recurse (can't end on non-0 string)
return _try_decode_name_rec(cur, h, n)
end
function try_decode_name(cur, h)
return _try_decode_name_rec(cur, h, {})
end
local _null = string.char(0)
function _encode_name_rec(n, h, t, ofs)
mst.a(type(n) == 'table', 'non-table given to encode_name_rec', n)
-- handle eof
if ofs > #n
then
table.insert(t, string.char(0))
if h
then
h.pos = h.pos + 1
end
return t
end
local v = n[ofs]
-- name compression handling
if h and h.nt
then
-- figure if this array(sub)string exists
local sn = ofs > 1 and mst.array_slice(n, ofs) or n
local k = table.concat(sn, _null)
local ofs2 = h.nt[k]
if ofs2
then
--mst.d('found at', ofs2, sn)
local o1 = math.floor(ofs2/256)
local o2 = ofs2%256
table.insert(t, string.char(64 + 128 + o1))
table.insert(t, string.char(o2))
h.pos = h.pos + 2
return t
end
--mst.d('inserting', h.pos, sn)
-- store the current position as where we can be found
h.nt[k] = h.pos
-- and update the position with the encoded data length
h.pos = h.pos + 1 + #v
end
mst.a(#v > 0, 'we do not support empty labels in middle!')
mst.a(#v < 64, 'too long label', v)
table.insert(t, string.char(#v))
table.insert(t, v)
return _encode_name_rec(n, h, t, ofs+1)
end
function encode_name(n, h)
local t = {}
return _encode_name_rec(n, h, t, 1)
end
function try_encode_name(n, h)
mst.a(n, 'trying to encode nil name')
mst.a(type(n) == 'table', 'wrong type name', type(n), n)
-- XXX - RFC1035 is not very clear about interaction between name
-- compression and maximum name length. here we check
-- _uncompressed_ length, even if h and h.ns is provided (=name
-- compression is enabled)
local cnt = 1 -- final 0 label's length
for i, v in ipairs(n)
do
if #v > dns_const.MAXIMUM_LABEL_SIZE
then
return nil, 'too long label ' .. mst.repr(n)
end
cnt = cnt + 1 + #v
end
if cnt > dns_const.MAXIMUM_NAME_SIZE
then
return nil, 'too long name ' .. mst.repr(n)
end
return encode_name(n, h)
end