packetgen 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +48 -6
- data/lib/packetgen/header.rb +70 -2
- data/lib/packetgen/packet.rb +66 -33
- data/lib/packetgen/version.rb +1 -1
- data/lib/packetgen.rb +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21f7a146ddb2ba2b7e8b86933360a97d95344fef
|
4
|
+
data.tar.gz: e26c103bc3ef15ca5d7064163cd747716215a71b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 304723a5e11714b3092606fd7f7529fd1a4d6ec0b427e364d3603b63cca20fbfc410a996d80a1a3cfb58106e855dcf5fbc76dd957b589eee6b97c1fa70379312
|
7
|
+
data.tar.gz: d95e4b3fa118e33ae9296626fd2855d6d3eaad9a4d2e0fd4605cfca5bdfd193b4397fd4411164a018dfbbf37868a35026134acf01cd2fdb52729cb90c05bc7fd
|
data/README.md
CHANGED
@@ -94,11 +94,10 @@ pkt.is? 'TCP' # => true
|
|
94
94
|
pkt.is? 'IP' # => true
|
95
95
|
pkt.is? 'UDP' # => false
|
96
96
|
|
97
|
-
# encapulsate/decapsulate packets
|
98
|
-
pkt2 = PacketGen.gen('IP')
|
99
|
-
|
100
|
-
|
101
|
-
pkt.decap('IP', 'ESP') # pkt is now inner IP/TCP packet
|
97
|
+
# encapulsate/decapsulate packets
|
98
|
+
pkt2 = PacketGen.gen('IP')
|
99
|
+
pkt2.encapsulate pkt # pkt2 is now a IP/IP/TCP packet
|
100
|
+
pkt2.decapsulate(pkt2.ip) # pkt2 is now inner IP/TCP packet
|
102
101
|
```
|
103
102
|
|
104
103
|
### Read/write PcapNG files
|
@@ -112,12 +111,55 @@ pkt.write('one_packet.pcapng')
|
|
112
111
|
PacketGen.write('more_packets.pcapng', packets)
|
113
112
|
```
|
114
113
|
|
114
|
+
### Add custom header/protocol
|
115
|
+
Since v1.1.0, PacketGen permits adding you own header classes.
|
116
|
+
First, define the new header class. By example:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
module MyModule
|
120
|
+
class MyHeader < Struct.new(:field1, :field2)
|
121
|
+
include PacketGen::StructFu
|
122
|
+
include PacketGen::Header::HeaderMethods
|
123
|
+
extend PacketGen::Header::HeaderClassMethods
|
124
|
+
|
125
|
+
def initialize(options={})
|
126
|
+
super Int32.new(options[:field1]), Int32.new(options[:field2])
|
127
|
+
end
|
128
|
+
|
129
|
+
def read(str)
|
130
|
+
self[:field1].read str[0, 4]
|
131
|
+
self[:field2].read str[4, 4]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
Then, class must be declared to PacketGen:
|
138
|
+
|
139
|
+
```
|
140
|
+
PacketGen::Header.add_class MyModule::MyHeader
|
141
|
+
```
|
142
|
+
|
143
|
+
Finally, bindings must be declared:
|
144
|
+
|
145
|
+
```
|
146
|
+
# bind MyHeader as IP protocol number 254 (needed by Packet#parse)
|
147
|
+
PacketGen::Header::IP.bind_header MyModule::MyHeader, protocol: 254
|
148
|
+
```
|
149
|
+
|
150
|
+
And use it:
|
151
|
+
|
152
|
+
```
|
153
|
+
pkt = Packet.gen('IP').add('MyHeader', field1: 0x12345678)
|
154
|
+
pkt.myheader.field2.read 0x01
|
155
|
+
```
|
156
|
+
|
115
157
|
## Pull requests?
|
116
158
|
|
117
159
|
yes
|
118
160
|
|
119
161
|
## License
|
120
|
-
MIT License (see [LICENSE](https://github.com/sdaubert/packetgen/LICENSE))
|
162
|
+
MIT License (see [LICENSE](https://github.com/sdaubert/packetgen/blob/master/LICENSE))
|
121
163
|
|
122
164
|
Copyright © 2016 Sylvain Daubert
|
123
165
|
|
data/lib/packetgen/header.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
# This file is part of PacketGen
|
2
3
|
# See https://github.com/sdaubert/packetgen for more informations
|
3
4
|
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
@@ -5,14 +6,81 @@
|
|
5
6
|
|
6
7
|
module PacketGen
|
7
8
|
# Namespace for protocol header classes
|
9
|
+
# == Add a foreign header class
|
10
|
+
# Since v1.1.0, PacketGen permits adding you own header classes.
|
11
|
+
# First, define the new header class. By example:
|
12
|
+
# module MyModule
|
13
|
+
# class MyHeader < Struct.new(:field1, :field2)
|
14
|
+
# include PacketGen::StructFu
|
15
|
+
# include PacketGen::Header::HeaderMethods
|
16
|
+
# extend PacketGen::Header::HeaderClassMethods
|
17
|
+
#
|
18
|
+
# def initialize(options={})
|
19
|
+
# super Int32.new(options[:field1]), Int32.new(options[:field2])
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def read(str)
|
23
|
+
# self[:field1].read str[0, 4]
|
24
|
+
# self[:field2].read str[4, 4]
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
# Then, class must be declared to PacketGen:
|
29
|
+
# PacketGen::Header.add_class MyModule::MyHeader
|
30
|
+
# Finally, bindings must be declared:
|
31
|
+
# # bind MyHeader as IP protocol number 254 (needed by Packet#parse)
|
32
|
+
# PacketGen::Header::IP.bind_header MyModule::MyHeader, protocol: 254
|
33
|
+
# And use it:
|
34
|
+
# pkt = Packet.gen('IP').add('MyHeader', field1: 0x12345678)
|
35
|
+
# pkt.myheader.field2.read 0x01
|
8
36
|
# @author Sylvain Daubert
|
9
37
|
module Header
|
10
38
|
|
39
|
+
@added_header_classes = {}
|
40
|
+
|
11
41
|
# Get known header classes
|
12
42
|
# @return [Array<Class>]
|
13
43
|
def self.all
|
14
|
-
|
15
|
-
|
44
|
+
return @header_classes if @header_classes
|
45
|
+
|
46
|
+
@builtin ||= constants.map { |sym| const_get sym }.
|
47
|
+
select { |klass| klass < Struct && klass < HeaderMethods }
|
48
|
+
@header_classes = @builtin + @added_header_classes.values
|
49
|
+
end
|
50
|
+
|
51
|
+
# Add a foreign header class to known header classes. This is
|
52
|
+
# needed by {Packet.gen} and {Packet#add}.
|
53
|
+
# @param [Class] klass a header class, which should include
|
54
|
+
# {Header::HeaderMethods} and {Header::HeaderClassMethods}
|
55
|
+
# @return [void]
|
56
|
+
# @since 1.1.0
|
57
|
+
def self.add_class(klass)
|
58
|
+
protocol_name = klass.to_s.sub(/.*::/, '')
|
59
|
+
@added_header_classes[protocol_name] = klass
|
60
|
+
@header_classes = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
# Remove a foreign header (previously added by {.add_header_class}à
|
64
|
+
# from known header classes.
|
65
|
+
# @param [Class] klass
|
66
|
+
# @return [void]
|
67
|
+
# @since 1.1.0
|
68
|
+
def self.remove_class(klass)
|
69
|
+
protocol_name = klass.to_s.sub(/.*::/, '')
|
70
|
+
@added_header_classes.delete protocol_name
|
71
|
+
@header_classes = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Get header class from its name
|
75
|
+
# @param [String] name
|
76
|
+
# @return [Class,nil]
|
77
|
+
# @since 1.1.0
|
78
|
+
def self.get_header_class_by_name(name)
|
79
|
+
if Header.const_defined? name
|
80
|
+
Header.const_get name
|
81
|
+
else
|
82
|
+
@added_header_classes[name]
|
83
|
+
end
|
16
84
|
end
|
17
85
|
end
|
18
86
|
end
|
data/lib/packetgen/packet.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
# This file is part of PacketGen
|
2
3
|
# See https://github.com/sdaubert/packetgen for more informations
|
3
4
|
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
@@ -163,25 +164,7 @@ module PacketGen
|
|
163
164
|
klass = check_protocol(protocol)
|
164
165
|
|
165
166
|
header = klass.new(options)
|
166
|
-
|
167
|
-
if prev_header
|
168
|
-
binding = prev_header.class.known_headers[klass]
|
169
|
-
if binding.nil?
|
170
|
-
msg = "#{prev_header.class} knowns no layer association with #{protocol}. "
|
171
|
-
msg << "Try #{prev_header.class}.bind_layer(PacketGen::Header::#{protocol}, "
|
172
|
-
msg << "#{prev_header.protocol_name.downcase}_proto_field: "
|
173
|
-
msg << "value_for_#{protocol.downcase})"
|
174
|
-
raise ArgumentError, msg
|
175
|
-
end
|
176
|
-
prev_header[binding.key].read binding.value
|
177
|
-
prev_header.body = header
|
178
|
-
end
|
179
|
-
header.packet = self
|
180
|
-
@headers << header
|
181
|
-
unless respond_to? protocol.downcase
|
182
|
-
self.class.class_eval "def #{protocol.downcase}(arg=nil);" \
|
183
|
-
"header('#{protocol}', arg); end"
|
184
|
-
end
|
167
|
+
add_header header
|
185
168
|
self
|
186
169
|
end
|
187
170
|
|
@@ -257,6 +240,34 @@ module PacketGen
|
|
257
240
|
end
|
258
241
|
end
|
259
242
|
|
243
|
+
# Encapulate another packet in +self+
|
244
|
+
# @param [Packet] other
|
245
|
+
# @return [self] +self+ with new headers from +other+
|
246
|
+
# @since 1.1.0
|
247
|
+
def encapsulate(other)
|
248
|
+
other.headers.each { |h| add_header h }
|
249
|
+
end
|
250
|
+
|
251
|
+
# Remove headers from +self+
|
252
|
+
# @param [Array<Header>] headers
|
253
|
+
# @return [self] +self+ with some headers removed
|
254
|
+
# @raise [FormatError] any headers not in +self+
|
255
|
+
# @raise [FormatError] removed headers result in an unknown binding
|
256
|
+
# @since 1.1.0
|
257
|
+
def decapsulate(*headers)
|
258
|
+
headers.each do |header|
|
259
|
+
idx = @headers.index(header)
|
260
|
+
raise FormatError, 'header not in packet!' if idx.nil?
|
261
|
+
|
262
|
+
prev_header = idx > 0 ? @headers[idx - 1] : nil
|
263
|
+
next_header = (idx+1) < @headers.size ? @headers[idx + 1] : nil
|
264
|
+
@headers.delete_at(idx)
|
265
|
+
add_header(next_header, prev_header) if prev_header and next_header
|
266
|
+
end
|
267
|
+
rescue ArgumentError => ex
|
268
|
+
raise FormatError, ex.message
|
269
|
+
end
|
270
|
+
|
260
271
|
# @return [String]
|
261
272
|
def inspect
|
262
273
|
str = Inspect.dashed_line(self.class)
|
@@ -274,17 +285,15 @@ module PacketGen
|
|
274
285
|
|
275
286
|
private
|
276
287
|
|
277
|
-
# @overload header(
|
278
|
-
# @param [
|
288
|
+
# @overload header(klass, layer=1)
|
289
|
+
# @param [Class] klass
|
279
290
|
# @param [Integer] layer
|
280
|
-
# @overload header(
|
281
|
-
# @param [String]
|
291
|
+
# @overload header(klass, options={})
|
292
|
+
# @param [String] klass
|
282
293
|
# @param [Hash] options
|
294
|
+
# @raise [ArgumentError] unknown option
|
283
295
|
# @return [Header::Base]
|
284
|
-
|
285
|
-
def header(protocol, arg)
|
286
|
-
klass = check_protocol protocol
|
287
|
-
|
296
|
+
def header(klass, arg)
|
288
297
|
headers = @headers.select { |h| h.is_a? klass }
|
289
298
|
layer = arg.is_a?(Integer) ? arg : 1
|
290
299
|
header = headers[layer - 1]
|
@@ -292,7 +301,7 @@ module PacketGen
|
|
292
301
|
if arg.is_a? Hash
|
293
302
|
arg.each do |key, value|
|
294
303
|
unless header.respond_to? "#{key}="
|
295
|
-
raise ArgumentError, "unknown #{key} attribute for #{
|
304
|
+
raise ArgumentError, "unknown #{key} attribute for #{klass}"
|
296
305
|
end
|
297
306
|
header.send "#{key}=", value
|
298
307
|
end
|
@@ -305,13 +314,37 @@ module PacketGen
|
|
305
314
|
# @param [String] protocol
|
306
315
|
# @raise [ArgumentError] unknown protocol
|
307
316
|
def check_protocol(protocol)
|
308
|
-
|
309
|
-
|
310
|
-
end
|
311
|
-
klass = Header.const_get(protocol)
|
312
|
-
raise ArgumentError, "unknown #{protocol} protocol" unless klass.is_a? Class
|
317
|
+
klass = Header.get_header_class_by_name(protocol)
|
318
|
+
raise ArgumentError, "unknown #{protocol} protocol" if klass.nil?
|
313
319
|
klass
|
314
320
|
end
|
321
|
+
|
322
|
+
# Add a header to packet
|
323
|
+
# @param [Header::HeaderMethods] header
|
324
|
+
# @param [Header::HeaderMethods] previous_header
|
325
|
+
# @return [void]
|
326
|
+
def add_header(header, previous_header=nil)
|
327
|
+
protocol = header.protocol_name
|
328
|
+
prev_header = previous_header || @headers.last
|
329
|
+
if prev_header
|
330
|
+
binding = prev_header.class.known_headers[header.class]
|
331
|
+
if binding.nil?
|
332
|
+
msg = "#{prev_header.class} knowns no layer association with #{protocol}. "
|
333
|
+
msg << "Try #{prev_header.class}.bind_layer(PacketGen::Header::#{protocol}, "
|
334
|
+
msg << "#{prev_header.protocol_name.downcase}_proto_field: "
|
335
|
+
msg << "value_for_#{protocol.downcase})"
|
336
|
+
raise ArgumentError, msg
|
337
|
+
end
|
338
|
+
prev_header[binding.key].read binding.value
|
339
|
+
prev_header.body = header
|
340
|
+
end
|
341
|
+
header.packet = self
|
342
|
+
@headers << header unless previous_header
|
343
|
+
unless respond_to? protocol.downcase
|
344
|
+
self.class.class_eval "def #{protocol.downcase}(arg=nil);" \
|
345
|
+
"header(#{header.class}, arg); end"
|
346
|
+
end
|
347
|
+
end
|
315
348
|
end
|
316
349
|
end
|
317
350
|
|
data/lib/packetgen/version.rb
CHANGED
data/lib/packetgen.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: packetgen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Daubert
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pcaprub
|