packetgen 1.4.3 → 2.0.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: 4b5b7c4c6b94e7e5e2323d99a06f687109495539
4
- data.tar.gz: 363abe5a21f82d8f0fe8f791912109c55d795218
3
+ metadata.gz: 77561a943f0b532168700f1752631dd1f8080ebf
4
+ data.tar.gz: 36a505013a6ff0e37a35e64d62ec627dcba5dc22
5
5
  SHA512:
6
- metadata.gz: 7782f886a780d71bd9e976bb9185742979f8a5d0a4818a07a9b8c249d4628616db690a4620e80a346ec19357df288c4d2b598804095b648e1373cf9f99c15a73
7
- data.tar.gz: e8838e0e6ba458472090661066cdd90ac4358f02e4eea53adf4c2c09867ee83c0e4c318241a30f0cb255d91cf393319002b378c174161744596b2bd2f84e2e2d
6
+ metadata.gz: f72762403800113f9816892b1d7d0df0e5fa417a28afbbc7cf56a5b15d340100721ce46bdc2fcb003a2818dec57780bdb8d3172cbe4bdcad2ba8a2dc4ed8ab16
7
+ data.tar.gz: cae3e091c5423afd66ab45a2ebea34a265e9b5a751b14bfb42e9a04bb92deb6945af0c5a85ed9520d96e721f33c815817b9898aca74bb6e51277eb3855809ed5
data/.travis.yml CHANGED
@@ -1,10 +1,9 @@
1
1
  language: ruby
2
2
  sudo: required
3
3
  rvm:
4
- - 2.1
5
- - 2.2
6
- - 2.3.3
7
- - 2.4.0
4
+ - 2.2.7
5
+ - 2.3.4
6
+ - 2.4.1
8
7
 
9
8
  install:
10
9
  - sudo apt-get update -qq
data/README.md CHANGED
@@ -36,11 +36,11 @@ PacketGen.gen('IP').to_s
36
36
  ### Send packets on wire
37
37
  ```ruby
38
38
  # send Ethernet packet
39
- PacketGen.gen('Eth', src: '00:00:00:00:01', dst: '00:00:00:00:02').to_w
39
+ PacketGen.gen('Eth', src: '00:00:00:00:00:01', dst: '00:00:00:00:00:02').to_w
40
40
  # send IP packet
41
41
  PacketGen.gen('IP', src: '192.168.1.1', dst: '192.168.1.2').to_w
42
42
  # send forged IP packet over Ethernet
43
- PacketGen.gen('Eth', src: '00:00:00:00:01', dst: '00:00:00:00:02').add('IP').to_w('eth1')
43
+ PacketGen.gen('Eth', src: '00:00:00:00:00:01', dst: '00:00:00:00:00:02').add('IP').to_w('eth1')
44
44
  ```
45
45
 
46
46
  ### Parse packets from binary data
@@ -50,16 +50,16 @@ packet = PacketGen.parse(binary_data)
50
50
 
51
51
  ### Capture packets from wire
52
52
  ```ruby
53
- # Capture packets, action from a block
54
- PacketGen.capture('eth0') do |packet|
53
+ # Capture packets from first network interface, action from a block
54
+ PacketGen.capture do |packet|
55
55
  do_stuffs_with_packet
56
56
  end
57
57
 
58
58
  # Capture some packets, and act on them afterward
59
- packets = PacketGen.capture('eth0', max: 10) # return when 10 packets were captured
59
+ packets = PacketGen.capture(iface: 'eth0', max: 10) # return when 10 packets were captured
60
60
 
61
61
  # Use filters
62
- packets = PacketGen.capture('eth0', filter: 'ip src 1.1.1.2', max: 1)
62
+ packets = PacketGen.capture(iface: 'eth0', filter: 'ip src 1.1.1.2', max: 1)
63
63
  ```
64
64
 
65
65
  ### Easily manipulate packets
data/lib/packetgen.rb CHANGED
@@ -38,7 +38,6 @@ module PacketGen
38
38
  end
39
39
 
40
40
  # Shortcut for {Packet.capture}
41
- # @param [String] iface interface name
42
41
  # @param [Hash] options capture options. See {Packet.capture}.
43
42
  # @yieldparam [Packet] packet
44
43
  # @return [Array<Packet>]
@@ -25,49 +25,27 @@ module PacketGen
25
25
  # @return [String]
26
26
  attr_reader :iface
27
27
 
28
- # @overload initialize(iface=Pcap.lookupdev, options={})
29
- # @param [String] iface interface on which capture packets
30
- # @param [Hash] options
31
- # @option options [Integer] :max maximum number of packets to capture.
32
- # @option options [Integer] :timeout maximum number of seconds before end
33
- # of capture. Default: +nil+ (no timeout)
34
- # @option options [String] :filter bpf filter
35
- # @option options [Boolean] :promiscuous (default: +false+)
36
- # @option options [Boolean] :parse parse raw data to generate packets before
37
- # yielding. Default: +true+
38
- # @option options [Integer] :snaplen maximum number of bytes to capture for
39
- # each packet.
40
- # @overload initialize(options={})
41
- # @param [Hash] options
42
- # @option options [String] :iface interface on which capture
43
- # packets on. Default: Use default interface lookup. If no interface found,
44
- # use loopback one.
45
- # @option options [Integer] :max maximum number of packets to capture.
46
- # @option options [Integer] :timeout maximum number of seconds before end
47
- # of capture. Default: +nil+ (no timeout)
48
- # @option options [String] :filter bpf filter
49
- # @option options [Boolean] :promiscuous (default: +false+)
50
- # @option options [Boolean] :parse parse raw data to generate packets before
51
- # yielding. Default: +true+
52
- # @option options [Integer] :snaplen maximum number of bytes to capture for
53
- # each packet.
54
- def initialize(iface_or_options={}, options={})
28
+ # @param [Hash] options
29
+ # @option options [String] :iface interface on which capture
30
+ # packets on. Default: Use default interface lookup. If no interface found,
31
+ # use loopback one.
32
+ # @option options [Integer] :max maximum number of packets to capture.
33
+ # @option options [Integer] :timeout maximum number of seconds before end
34
+ # of capture. Default: +nil+ (no timeout)
35
+ # @option options [String] :filter bpf filter
36
+ # @option options [Boolean] :promiscuous (default: +false+)
37
+ # @option options [Boolean] :parse parse raw data to generate packets before
38
+ # yielding. Default: +true+
39
+ # @option options [Integer] :snaplen maximum number of bytes to capture for
40
+ # each packet.
41
+ # @since 2.0.0 remove old 1.x API
42
+ def initialize(options={})
55
43
  begin
56
44
  @iface = Pcap.lookupdev
57
45
  rescue PCAPRUB::BindingError
58
46
  @iface = 'lo'
59
47
  end
60
48
 
61
- case iface_or_options
62
- when Hash
63
- options = iface_or_options
64
- else
65
- warn "[deprecation] use of PacketGen::Capture#initialize with iface name as\n" \
66
- " first argument is deprecated. Instead, use:\n" \
67
- ' PacketGen::Capture.new(iface: \'name\').'
68
- @iface = iface_or_options.to_s
69
- end
70
-
71
49
  @packets = []
72
50
  @raw_packets = []
73
51
  @promisc = false
@@ -48,7 +48,7 @@ module PacketGen
48
48
  # @return [void]
49
49
  # @since 1.1.0
50
50
  def self.add_class(klass)
51
- protocol_name = klass.to_s.sub(/.*::/, '')
51
+ protocol_name = klass.new.protocol_name
52
52
  @added_header_classes[protocol_name] = klass
53
53
  @header_classes = nil
54
54
  end
@@ -59,7 +59,7 @@ module PacketGen
59
59
  # @return [void]
60
60
  # @since 1.1.0
61
61
  def self.remove_class(klass)
62
- protocol_name = klass.to_s.sub(/.*::/, '')
62
+ protocol_name = klass.new.protocol_name
63
63
  @added_header_classes.delete protocol_name
64
64
  @header_classes = nil
65
65
  end
@@ -78,6 +78,7 @@ module PacketGen
78
78
  end
79
79
  end
80
80
 
81
+ require_relative 'header/crypto'
81
82
  require_relative 'header/base'
82
83
  require_relative 'header/eth'
83
84
  require_relative 'header/dot11'
@@ -92,4 +93,7 @@ require_relative 'header/icmpv6'
92
93
  require_relative 'header/udp'
93
94
  require_relative 'header/tcp'
94
95
  require_relative 'header/esp'
96
+ require_relative 'header/ike'
95
97
  require_relative 'header/dns'
98
+ require_relative 'header/asn1_base'
99
+ require_relative 'header/snmp'
@@ -0,0 +1,86 @@
1
+ # This file is part of PacketGen
2
+ # See https://github.com/sdaubert/packetgen for more informations
3
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
+ # This program is published under MIT license.
5
+ require 'rasn1'
6
+
7
+ module PacketGen
8
+ module Header
9
+
10
+ # @abstract Base class for ASN.1 header types.
11
+ # Subclasses may define magic methods:
12
+ # * {#parse?}.
13
+ # @author Sylvain Daubert
14
+ class ASN1Base < RASN1::Model
15
+
16
+ # @api private
17
+ # Reference on packet which owns this header
18
+ attr_accessor :packet
19
+
20
+ # Give protocol name for this class
21
+ # @return [String]
22
+ # @since 2.0.0
23
+ def self.protocol_name
24
+ self.new.protocol_name
25
+ end
26
+
27
+ # Define some methods from given ASN.1 fields to mimic {Base} attributes
28
+ # @param [Array<Symbol>] attributes
29
+ # @return [void]
30
+ def self.define_attributes(*attributes)
31
+ @attributes = attributes
32
+ attributes.each do |attr|
33
+ class_eval "def #{attr}; @elements[:#{attr}].value; end\n" \
34
+ "def #{attr}=(v); @elements[:#{attr}].value = v; end"
35
+ end
36
+ end
37
+
38
+ # Return header protocol name
39
+ # @return [String]
40
+ def protocol_name
41
+ return @protocol_name if @protocol_name
42
+
43
+ classname = self.class.to_s
44
+ @protocol_name = if classname.start_with?('PacketGen::Header')
45
+ classname.sub(/.*Header::/, '')
46
+ else
47
+ classname.sub(/.*::/, '')
48
+ end
49
+ end
50
+ # return header method name
51
+ # @return [String]
52
+ # @since 2.0.0
53
+ def method_name
54
+ return @method_name if @method_name
55
+
56
+ @method_name = protocol_name.downcase.sub(/::/, '_')
57
+ end
58
+
59
+ # @return [true]
60
+ def parse?
61
+ true
62
+ end
63
+
64
+ alias :parse :parse!
65
+ alias :to_s :to_der
66
+
67
+ # Read a BER string
68
+ # @param [String] str
69
+ # @return [ASN1Base] self
70
+ def read(str)
71
+ parse(str, ber: true)
72
+ self
73
+ end
74
+
75
+ # Common inspect method for ASN.1 headers
76
+ # @return [String]
77
+ def inspect
78
+ str = Inspect.dashed_line(self.class, 2)
79
+ self.class.class_eval { @attributes }.each do |attr|
80
+ str << Inspect.inspect_asn1_attribute(attr, self[attr], 2)
81
+ end
82
+ str
83
+ end
84
+ end
85
+ end
86
+ end
@@ -6,8 +6,9 @@
6
6
  module PacketGen
7
7
  module Header
8
8
 
9
- # @abstract
10
- # Base class for all header types
9
+ # @abstract Base class for all header types.
10
+ # Subclasses may define magic methods:
11
+ # * {#parse?}.
11
12
  # @author Sylvain Daubert
12
13
  class Base < Types::Fields
13
14
 
@@ -39,6 +40,32 @@ module PacketGen
39
40
  end
40
41
  end
41
42
 
43
+ # @api private
44
+ # Class to handle a header association from procs
45
+ class ProcBinding
46
+
47
+ # @param [Array<Proc>] procs first proc is used to set fields, second proc is
48
+ # used to check binding
49
+ def initialize(procs)
50
+ @set = procs.shift
51
+ @check = procs.shift
52
+ end
53
+
54
+ # Check +fields+ responds to binding
55
+ # @param [Types::Fields] fields
56
+ # @return [Boolean]
57
+ def check?(fields)
58
+ @check.call(fields)
59
+ end
60
+
61
+ # Set +fields+ field to binding value
62
+ # @param [Types::Fields] fields
63
+ # @return [void]
64
+ def set(fields)
65
+ @set.call(fields)
66
+ end
67
+ end
68
+
42
69
  # @api private
43
70
  # Class to handle header associations
44
71
  class Bindings
@@ -100,41 +127,6 @@ module PacketGen
100
127
  end
101
128
  end
102
129
 
103
- # @api private
104
- # Class to handle header associations
105
- class Bindings
106
- include Enumerable
107
-
108
- # op type
109
- # @return [:or,:and]
110
- attr_accessor :op
111
- # @return [Array<Binding>]
112
- attr_accessor :bindings
113
-
114
- # @param [:or, :and] op
115
- def initialize(op)
116
- @op = op
117
- @bindings = []
118
- end
119
-
120
- # @param [Object] arg
121
- # @return [Bindings] self
122
- def <<(arg)
123
- @bindings << arg
124
- end
125
-
126
- # each iterator
127
- # @return [void]
128
- def each
129
- @bindings.each { |b| yield b }
130
- end
131
-
132
- # @return [Boolean]
133
- def empty?
134
- @bindings.empty?
135
- end
136
- end
137
-
138
130
  # @api private
139
131
  # Reference on packet which owns this header
140
132
  attr_accessor :packet
@@ -149,9 +141,10 @@ module PacketGen
149
141
 
150
142
  # Bind a upper header to current class
151
143
  # Header1.bind_header Header2, field1: 43
152
- # Header1.bind_header Header2, field1: 43, field2: 43
153
- # Header1.bind_header Header2, op: :and, field1: 43, field2: 43
154
- # Header1.bind_header Header2, field1: ->(v) { v.nil? ? 128 : v > 127 }
144
+ # Header1.bind_header Header3, field1: 43, field2: 43
145
+ # Header1.bind_header Header4, op: :and, field1: 43, field2: 43
146
+ # Header1.bind_header Header5, field1: ->(v) { v.nil? ? 128 : v > 127 }
147
+ # Header1.bind_header Header6, proc: ->(pkt) { pkt.header1.field1 == 1 && pkt.header1.body[0..1] == "\x00\x00" }
155
148
  # @param [Class] header_klass header class to bind to current class
156
149
  # @param [Hash] args current class fields and their value when +header_klass+
157
150
  # is embedded in current class. Given value may be a lambda, whose alone argument
@@ -165,13 +158,28 @@ module PacketGen
165
158
  # @return [void]
166
159
  def self.bind_header(header_klass, args={})
167
160
  op = args.delete(:op) || :or
168
- bindings = Bindings.new(op)
169
- @known_headers[header_klass] = bindings
161
+ if @known_headers[header_klass].nil? || @known_headers[header_klass].op != op
162
+ bindings = Bindings.new(op)
163
+ @known_headers[header_klass] = bindings
164
+ else
165
+ bindings = @known_headers[header_klass]
166
+ end
170
167
  args.each do |key, value|
171
- bindings << Binding.new(key, value)
168
+ if key == :procs
169
+ bindings << ProcBinding.new(value)
170
+ else
171
+ bindings << Binding.new(key, value)
172
+ end
172
173
  end
173
174
  end
174
175
 
176
+ # Give protocol name for this class
177
+ # @return [String]
178
+ # @since 2.0.0
179
+ def self.protocol_name
180
+ self.new.protocol_name
181
+ end
182
+
175
183
  # @api private
176
184
  # Get knwon headers
177
185
  # @return [Hash] keys: header classes, values: hashes
@@ -182,7 +190,23 @@ module PacketGen
182
190
  # Return header protocol name
183
191
  # @return [String]
184
192
  def protocol_name
185
- self.class.to_s.sub(/.*::/, '')
193
+ return @protocol_name if @protocol_name
194
+
195
+ classname = self.class.to_s
196
+ @protocol_name = if classname.start_with?('PacketGen::Header')
197
+ classname.sub(/.*Header::/, '')
198
+ else
199
+ classname.sub(/.*::/, '')
200
+ end
201
+ end
202
+
203
+ # return header method name
204
+ # @return [String]
205
+ # @since 2.0.0
206
+ def method_name
207
+ return @method_name if @method_name
208
+
209
+ @method_name = protocol_name.downcase.sub(/::/, '_')
186
210
  end
187
211
 
188
212
  # @abstract Should be redefined by subclasses. This method should check invariant
@@ -0,0 +1,62 @@
1
+ module PacketGen
2
+ module Header
3
+
4
+ # Mixin for cryptographic classes
5
+ # @api private
6
+ # @author Sylvain Daubert
7
+ module Crypto
8
+
9
+ # Cryptographic error
10
+ class Error < PacketGen::Error; end
11
+
12
+ # Register cryptographic modes
13
+ # @param [OpenSSL::Cipher] conf
14
+ # @param [OpenSSL::HMAC] intg
15
+ # @return [void]
16
+ def set_crypto(conf, intg)
17
+ @conf, @intg = conf, intg
18
+ if conf.authenticated?
19
+ # #auth_tag_len only supported from ruby 2.4.0
20
+ @conf.auth_tag_len = @trunc if @conf.respond_to? :auth_tag_len
21
+ end
22
+ end
23
+
24
+ # Get confidentiality mode name
25
+ # @return [String]
26
+ def confidentiality_mode
27
+ mode = @conf.name.match(/-([^-]*)$/)[1]
28
+ raise Error, 'unknown cipher mode' if mode.nil?
29
+ mode.downcase
30
+ end
31
+
32
+ # Say if crypto modes permit authentication
33
+ # @return [Boolean]
34
+ def authenticated?
35
+ @conf.authenticated? or !!@intg
36
+ end
37
+
38
+ def authenticate!
39
+ @conf.final
40
+ if @intg
41
+ @intg.update @esn.to_s if @esn
42
+ @intg.digest[0, @icv_length] == @icv
43
+ else
44
+ true
45
+ end
46
+ rescue OpenSSL::Cipher::CipherError
47
+ false
48
+ end
49
+
50
+ def encipher(data)
51
+ enciphered_data = @conf.update(data)
52
+ @intg.update(enciphered_data) if @intg
53
+ enciphered_data
54
+ end
55
+
56
+ def decipher(data)
57
+ @intg.update(data) if @intg
58
+ @conf.update(data)
59
+ end
60
+ end
61
+ end
62
+ end