-
Notifications
You must be signed in to change notification settings - Fork 1
/
codec.lua
144 lines (121 loc) · 3.82 KB
/
codec.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
#!/usr/bin/env lua
-- -*-lua-*-
--
-- $Id: codec.lua $
--
-- Author: Markus Stenberg <[email protected]>
--
-- Copyright (c) 2012 cisco Systems, Inc.
--
-- Created: Thu Sep 27 13:46:47 2012 mstenber
-- Last modified: Sat Oct 19 00:31:52 2013 mstenber
-- Edit time: 233 min
--
-- object-oriented codec stuff that handles encoding and decoding of
-- the network packets (or their parts)
-- key ideas
-- - employ vstruct for heavy lifting
-- - nestable (TLV inside LSA inside OSPF, for example)
-- - extensible
local mst = require 'mst'
local vstruct = require 'vstruct'
local ipv6s = require 'ipv6s'
module(..., package.seeall)
--mst.enable_debug = true
-- abstract_base = base class, which just defines encode/decode API
-- but leaves do_encode/try_decode entirely up to subclasses
abstract_base = mst.create_class{class='abstract_base'}
function abstract_base:decode(cur, context)
mst.a(cur, 'nothing to decode?!?')
mst.a(not self._cur, '_cur left?!?', self, self._cur)
if type(cur) == 'string'
then
cur = vstruct.cursor(cur)
end
local old_pos = cur.pos
local o, err = self:try_decode(cur, context)
if o
then
return o, cur.pos
end
-- decode failed => restore cursor to wherever it was
cur.pos = old_pos
return nil, err
end
function abstract_base:encode(o, context)
--self:d('encode', o)
-- work on shallow copy if required
if self.copy_on_encode
then
o = mst.table_copy(o)
end
-- call do_encode to do real things (with the optional context parameter)
return self:do_encode(o, context)
end
-- abstract_data = data with at least one thing from vstruct
abstract_data = abstract_base:new_subclass{class='abstract_data'}
--- abstract_data baseclass
function abstract_data:init()
if not self.header
then
--mst.d('init header', self.format)
self:a(self.format, "no header AND no format?!?")
self.header=vstruct.compile('>' .. self.format)
end
if not self.header_length
then
--mst.d('init header_length', self.header, self.header_default)
self:a(self.header, 'header missing')
self:a(self.header_default, 'header_default missing')
self:a(self.header.pack, 'no self.header.pack?')
self.header_length = #self.header.pack(self.header_default)
end
end
function abstract_data:repr_data()
return mst.repr{format=self.format,
header_length=self.header_length,
header_default=self.header_default}
end
function abstract_data:try_decode(cur)
if not cursor_has_left(cur, self.header_length)
then
return nil, string.format('%s not enough left for header (%d<%d+%d) - %s',
self.class,
#cur.str, self.header_length, cur.pos,
mst.string_to_hex(string.sub(cur.str, cur.pos)))
end
local o = self.header.unpack(cur)
return o
end
function abstract_data:do_encode(o)
self:a(self.header, 'header missing - using class method instead of instance?')
-- copy in defaults if they haven't been filled in by someone yet
if self.header_default
then
for k, v in pairs(self.header_default)
do
if not o[k]
then
o[k] = v
end
end
end
local r = self.header.pack(o)
--mst.d('do_encode', mst.string_to_hex(r))
return r
end
function cursor_has_left(cur, n)
mst.a(type(n) == 'number')
mst.a(type(cur) == 'table', 'got wierd cursor', cur)
return (#cur.str - cur.pos) >= n
end
-- value -> network binary order data conversion functions
function u16_to_nb(v)
return string.char(math.floor(v / 256)) .. string.char(v % 256)
end
function nb_to_u16(b, ofs)
local ofs = ofs or 1
local b1 = string.byte(b, ofs, ofs)
local b2 = string.byte(b, ofs+1, ofs+1)
return b1 * 256 + b2, ofs + 2
end