libsl 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +50 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/lib/_packets.rb +10346 -0
- data/lib/agent.rb +119 -0
- data/lib/client.rb +57 -0
- data/lib/dsl.rb +54 -0
- data/lib/events.rb +59 -0
- data/lib/libsl.rb +14 -0
- data/lib/network.rb +305 -0
- data/lib/packet.rb +279 -0
- data/lib/types.rb +490 -0
- data/libsl.gemspec +30 -0
- data/test/tc_packet.rb +99 -0
- data/test/tc_types.rb +342 -0
- data/test/ts_all.rb +2 -0
- metadata +101 -0
data/lib/packet.rb
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
|
2
|
+
module LibSL
|
3
|
+
class Packet
|
4
|
+
def self.packet_id()
|
5
|
+
0
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.decode(data)
|
9
|
+
flags, data = LLU8.decode(data)
|
10
|
+
sequence_number, data = LLSequenceNumber.decode(data)
|
11
|
+
extra, data = LLU8.decode(data)
|
12
|
+
if extra.value > 0 then
|
13
|
+
extra, data = LLFixed.decode(data, extra)
|
14
|
+
end
|
15
|
+
data = zero_decode(data) if flags.value & 0x80 == 0x80
|
16
|
+
packet_number, data = LLPacketNumber.decode(data)
|
17
|
+
klass = MESSAGE_MAP[packet_number.number]
|
18
|
+
throw "Unknown message type!" if klass.nil?
|
19
|
+
|
20
|
+
packet = klass.new
|
21
|
+
packet.zero_coded_flag = flags.value & 0x80 == 0x80
|
22
|
+
packet.reliable_flag = flags.value & 0x40 == 0x40
|
23
|
+
packet.resent_flag = flags.value & 0x20 == 0x20
|
24
|
+
packet.acks_flag = flags.value & 0x10 == 0x10
|
25
|
+
packet.sequence_number = sequence_number
|
26
|
+
data = packet.decode_msg(data)
|
27
|
+
if packet.acks_flag && data.length > 0 then
|
28
|
+
# Extract acks
|
29
|
+
ack_count = data[-1]
|
30
|
+
packet.acks = data.unpack('N'+ack_count)
|
31
|
+
end
|
32
|
+
packet
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.zero_decode(data)
|
36
|
+
# Decode 0x00 0xXX to a sequence of 0xXX zeros
|
37
|
+
decoded = []
|
38
|
+
zero = false
|
39
|
+
data.bytes do |byte|
|
40
|
+
if byte == 0x00
|
41
|
+
zero = true
|
42
|
+
elsif zero == true
|
43
|
+
byte.times{ decoded << 0x00 }
|
44
|
+
zero = false
|
45
|
+
else
|
46
|
+
decoded << byte
|
47
|
+
end
|
48
|
+
end
|
49
|
+
decoded.pack('C*')
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_accessor :acks, :zero_coded_flag, :reliable_flag, :resent_flag, :acks_flag,
|
53
|
+
:sequence_number, :resent_count
|
54
|
+
|
55
|
+
# Create a new packet
|
56
|
+
# @param [Hash] blocks (optional) Data blocks
|
57
|
+
# Example:
|
58
|
+
# {
|
59
|
+
# :Block1 => { # Block
|
60
|
+
# :Var1 => LLU8.new(0xff),
|
61
|
+
# :Var2 => LLVariable1.new("abcd")
|
62
|
+
# },
|
63
|
+
# :Block2 => [ # Block collection
|
64
|
+
# {
|
65
|
+
# :Var1 => LLFixed.new("block1"),
|
66
|
+
# :Var2 => LLS32.new(0x12345678)
|
67
|
+
# },
|
68
|
+
# {
|
69
|
+
# :Var1 => LLFixed.new("block2"),
|
70
|
+
# :Var2 => LLS32.new(0x12345678)
|
71
|
+
# }
|
72
|
+
# ]
|
73
|
+
# }
|
74
|
+
def initialize(blocks={})
|
75
|
+
@zero_coded_flag = false
|
76
|
+
@reliable_flag = false
|
77
|
+
@resent_flag = false
|
78
|
+
@acks_flag = false
|
79
|
+
@acks = []
|
80
|
+
@resent_count = 0
|
81
|
+
build_structure
|
82
|
+
blocks.each do |name, block|
|
83
|
+
case block
|
84
|
+
when Array then
|
85
|
+
block.each_index do |i|
|
86
|
+
collection = self.send(name)
|
87
|
+
case collection
|
88
|
+
when VariableBlockCollection then
|
89
|
+
b = collection.add
|
90
|
+
when FixedBlockCollection then
|
91
|
+
b = collection[i]
|
92
|
+
end
|
93
|
+
|
94
|
+
block[i].each do |v|
|
95
|
+
b.send((v[0].to_s + "=").to_sym, v[1])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
when Hash then
|
99
|
+
block.each do |v|
|
100
|
+
self.send(name).send((v[0].to_s + "=").to_sym, v[1])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def encode()
|
107
|
+
flags = 0
|
108
|
+
flags |= 0x80 if @zero_coded_flag
|
109
|
+
flags |= 0x40 if @reliable_flag
|
110
|
+
flags |= 0x20 if @resent_flag
|
111
|
+
flags |= 0x10 if @acks_flag
|
112
|
+
|
113
|
+
data = LLU8.new(flags).encode +
|
114
|
+
LLSequenceNumber.new(@sequence_number).encode +
|
115
|
+
LLU8.new(0).encode
|
116
|
+
pid = LLPacketNumber.new(self.class.packet_id).encode
|
117
|
+
data += pid
|
118
|
+
data += encode_msg
|
119
|
+
# append acks
|
120
|
+
data += ([*@acks] + [@acks.length]).pack("N#{@acks.length}C") if @acks_flag
|
121
|
+
data
|
122
|
+
end
|
123
|
+
|
124
|
+
# Subclasses have to override this method to build the message structure
|
125
|
+
# IMPORTANT: Use arrays to describe the structure, as hashes don't preserve the order
|
126
|
+
# Example:
|
127
|
+
# def build_structure()
|
128
|
+
# @blocks = [
|
129
|
+
# [:AgentData, Block.new([
|
130
|
+
# [:AgentID, :LLUUID],
|
131
|
+
# [:SessionID, :LLUUID]
|
132
|
+
# ])],
|
133
|
+
# [:SomeData, FixedBlockCollection.new(4, [
|
134
|
+
# [:Data, :LLVariable1]
|
135
|
+
# ])]
|
136
|
+
# ]
|
137
|
+
# end
|
138
|
+
def build_structure()
|
139
|
+
end
|
140
|
+
|
141
|
+
def decode_msg(data)
|
142
|
+
@blocks.each do |block|
|
143
|
+
data = block[1].decode(data)
|
144
|
+
end
|
145
|
+
data
|
146
|
+
rescue => e
|
147
|
+
puts "[ERROR] #{self.class.name}: #{self.inspect}"
|
148
|
+
raise e
|
149
|
+
end
|
150
|
+
|
151
|
+
def encode_msg()
|
152
|
+
data = ""
|
153
|
+
@blocks.each{ |block| data += block[1].encode }
|
154
|
+
data
|
155
|
+
end
|
156
|
+
|
157
|
+
def method_missing(name, *args)
|
158
|
+
block = @blocks.assoc(name)
|
159
|
+
throw "The method #{name} does not exist in #{self.class.name}" if block.nil?
|
160
|
+
block[1]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class Block
|
165
|
+
def initialize(structure)
|
166
|
+
@structure = structure
|
167
|
+
@data = []
|
168
|
+
@structure.length.times{ @data << nil }
|
169
|
+
end
|
170
|
+
|
171
|
+
def decode(data)
|
172
|
+
@data = []
|
173
|
+
@structure.each do |var|
|
174
|
+
begin
|
175
|
+
args = [data]
|
176
|
+
args << var[2] if var[1] == :LLFixed
|
177
|
+
value, data = LibSL.const_get(var[1]).decode(*args)
|
178
|
+
#rescue => e
|
179
|
+
# throw e
|
180
|
+
# throw "Type #{type.to_s} does not exist!"
|
181
|
+
end
|
182
|
+
@data << [var[0], value]
|
183
|
+
end
|
184
|
+
data
|
185
|
+
end
|
186
|
+
|
187
|
+
def encode()
|
188
|
+
throw "Data has to be an array." unless @data.is_a? Array
|
189
|
+
throw "Block is missing data." if @data.length != @structure.length
|
190
|
+
data = ""
|
191
|
+
@data.each{ |var| data += var[1].encode }
|
192
|
+
data
|
193
|
+
end
|
194
|
+
|
195
|
+
def method_missing(name, *args)
|
196
|
+
name = name.to_s
|
197
|
+
if name[-1..-1] == "=" then
|
198
|
+
# Setter
|
199
|
+
var = name[0..-2].to_sym
|
200
|
+
throw "The method #{name} does not exist in #{self.class.name}" if @structure.assoc(var).nil?
|
201
|
+
index = @structure.index{|v| v[0] == var}
|
202
|
+
@data[index] = [var, args[0]]
|
203
|
+
else
|
204
|
+
# Getter
|
205
|
+
var = name.to_sym
|
206
|
+
throw "The method #{name} does not exist in #{self.class.name}" if @structure.assoc(var).nil?
|
207
|
+
@data.assoc(var)[1]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class FixedBlockCollection
|
213
|
+
attr_reader :blocks
|
214
|
+
def initialize(size, structure)
|
215
|
+
@size = size
|
216
|
+
@blocks = []
|
217
|
+
@size.times{@blocks << Block.new(structure)}
|
218
|
+
end
|
219
|
+
|
220
|
+
def length()
|
221
|
+
@size
|
222
|
+
end
|
223
|
+
|
224
|
+
def [](index)
|
225
|
+
throw "Index out of range: #{index} is not in [0, #{length - 1}]" if index < 0 or index >= length
|
226
|
+
@blocks[index]
|
227
|
+
end
|
228
|
+
|
229
|
+
def each(&block)
|
230
|
+
@blocks.each{|block| yield block}
|
231
|
+
end
|
232
|
+
|
233
|
+
def decode(data)
|
234
|
+
@blocks.each{ |block| data = block.decode(data) }
|
235
|
+
data
|
236
|
+
end
|
237
|
+
|
238
|
+
def encode()
|
239
|
+
data = ""
|
240
|
+
@blocks.each{ |block| data += block.encode }
|
241
|
+
data
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
class VariableBlockCollection < FixedBlockCollection
|
246
|
+
def initialize(structure)
|
247
|
+
@structure = structure
|
248
|
+
@blocks = []
|
249
|
+
end
|
250
|
+
|
251
|
+
def length()
|
252
|
+
@blocks.length
|
253
|
+
end
|
254
|
+
|
255
|
+
def add()
|
256
|
+
@blocks << Block.new(@structure)
|
257
|
+
@blocks.last
|
258
|
+
end
|
259
|
+
|
260
|
+
def decode(data)
|
261
|
+
size, data = LLU8.decode(data)
|
262
|
+
size.value.times{ data = add.decode(data) }
|
263
|
+
data
|
264
|
+
end
|
265
|
+
|
266
|
+
def encode()
|
267
|
+
data = LLU8.new(length).encode
|
268
|
+
@blocks.each{ |block| data += block.encode }
|
269
|
+
data
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Include packet types generated from the specification
|
275
|
+
begin
|
276
|
+
require File.join File.dirname(__FILE__), "_packets"
|
277
|
+
rescue LoadError => e
|
278
|
+
puts "Packets not found! Please build them."
|
279
|
+
end
|