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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3cb4095a7e2fda39a7560ad82d4296016c5a633a
4
- data.tar.gz: aed1db17fd5274a7d842ac6ba855ed73b7d271fe
3
+ metadata.gz: 21f7a146ddb2ba2b7e8b86933360a97d95344fef
4
+ data.tar.gz: e26c103bc3ef15ca5d7064163cd747716215a71b
5
5
  SHA512:
6
- metadata.gz: a520f1dfaf0a6960c33fb27c5f564923c4de11c8d310b77c7dcf151c688df70d15396b1ce74987b7a8ca811e86f58a2f6eaf1d0a18ba03613ed02aebde9f9730
7
- data.tar.gz: e8396d8782c437335f50a3b8ee2e63138a9b0c4ea874b46838249fcb58e8f1acc89f1a7c24c6fd0fda5169c856f9724296e2c285c826223c91eebca5eba60a95
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 (TODO)
98
- pkt2 = PacketGen.gen('IP').add('ESP', spi: 1234)
99
- pkt.encap pkt2 # pkt is now a IP/ESP/IP/TCP packet
100
- # eq. to pkt.encap('IP', 'ESP', esp_spi: 1234)
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
 
@@ -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
- constants.map { |sym| const_get sym }.
15
- select { |klass| klass < Struct && klass < HeaderMethods }
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
@@ -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
- prev_header = @headers.last
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(protocol, layer=1)
278
- # @param [String] protocol
288
+ # @overload header(klass, layer=1)
289
+ # @param [Class] klass
279
290
  # @param [Integer] layer
280
- # @overload header(protocol, options)
281
- # @param [String] protocol
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
- # @raise [ArgumentError] unknown protocol
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 #{header.class}"
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
- unless Header.const_defined? protocol
309
- raise ArgumentError, "unknown #{protocol} protocol"
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
 
@@ -8,5 +8,5 @@
8
8
  # @author Sylvain Daubert
9
9
  module PacketGen
10
10
  # PacketGen version
11
- VERSION = "1.0.1"
11
+ VERSION = "1.1.0"
12
12
  end
data/lib/packetgen.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>
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.1
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-19 00:00:00.000000000 Z
11
+ date: 2016-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pcaprub