packetgen 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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