packetgen 2.1.4 → 2.2.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.
@@ -47,7 +47,7 @@ module PacketGen
47
47
 
48
48
  # IPv6 address, as a group of 8 2-byte words
49
49
  # @author Sylvain Daubert
50
- class Addr < Base
50
+ class Addr < Types::Fields
51
51
 
52
52
  # @!attribute a1
53
53
  # 1st 2-byte word of IPv6 address
@@ -385,7 +385,9 @@ module PacketGen
385
385
  hdr = hdr.read(binary_str)
386
386
  # First header is found when:
387
387
  # * for one known header,
388
+ # * +#parse?+ is true
388
389
  # * it exists a known binding with a upper header
390
+ next unless hdr.parse?
389
391
  search_header(hdr) do
390
392
  first_header = hklass.to_s.gsub(/.*::/, '')
391
393
  end
@@ -404,8 +406,8 @@ module PacketGen
404
406
  str = last_known_hdr.body
405
407
  nheader = nh.new
406
408
  nheader = nheader.read(str)
409
+ next unless nheader.parse?
407
410
  add_header nheader, parsing: true
408
- nheader.dissect if nheader.respond_to? :dissect
409
411
  end
410
412
  decode_packet_bottom_up = (@headers.last != last_known_hdr)
411
413
  end
@@ -413,7 +415,7 @@ module PacketGen
413
415
 
414
416
  def search_header(hdr)
415
417
  hdr.class.known_headers.each do |nh, bindings|
416
- if bindings.check?(hdr) and hdr.parse?
418
+ if bindings.check?(hdr)
417
419
  yield nh
418
420
  break
419
421
  end
@@ -13,6 +13,7 @@ require_relative 'types/int'
13
13
  require_relative 'types/enum'
14
14
  require_relative 'types/string'
15
15
  require_relative 'types/int_string'
16
+ require_relative 'types/cstring'
16
17
  require_relative 'types/fields'
17
18
  require_relative 'types/array'
18
19
  require_relative 'types/tlv'
@@ -0,0 +1,67 @@
1
+ # coding: utf-8
2
+ # This file is part of PacketGen
3
+ # See https://github.com/sdaubert/packetgen for more informations
4
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
5
+ # This program is published under MIT license.
6
+
7
+ module PacketGen
8
+ module Types
9
+
10
+ # This class handles null-terminated strings (aka C strings).
11
+ # @author Sylvain Daubert
12
+ class CString < ::String
13
+
14
+ # @param [String] str
15
+ # @param [Hash] options
16
+ # @option options [Integer] :static_length set a static length for this string
17
+ def initialize(options={})
18
+ super()
19
+ @static_length = options[:static_length]
20
+ end
21
+
22
+ # @param [::String] str
23
+ # @return [String] self
24
+ def read(str)
25
+ s = str.to_s
26
+ if @static_length.is_a? Integer
27
+ s = s[0, @static_length]
28
+ end
29
+ idx = s.index(0.chr)
30
+ s = s[0, idx] unless idx.nil?
31
+ self.replace s
32
+ self
33
+ end
34
+
35
+ # get null-terminated string
36
+ # @return [String]
37
+ def to_s
38
+ if @static_length.is_a? Integer
39
+ if self.size >= @static_length
40
+ s = self[0, @static_length]
41
+ s[-1] = 0.chr
42
+ PacketGen.force_binary s
43
+ else
44
+ PacketGen.force_binary(self + "\0" * (@static_length - self.length))
45
+ end
46
+ else
47
+ PacketGen.force_binary(self + 0.chr)
48
+ end
49
+ end
50
+
51
+ # @return [Integer]
52
+ def sz
53
+ if @static_length.is_a? Integer
54
+ @static_length
55
+ else
56
+ to_s.size
57
+ end
58
+ end
59
+
60
+ # @return [String]
61
+ def to_human
62
+ idx = self.index(0.chr) || self.sz
63
+ self[0, idx].inspect
64
+ end
65
+ end
66
+ end
67
+ end
@@ -132,7 +132,7 @@ module PacketGen
132
132
  # +:builder+ option is not set.
133
133
  # @option options [Object] :default default value
134
134
  # @option options [Lambda] :builder lambda to construct this field.
135
- # Parameter to this lambda is the caller object.
135
+ # Parameters to this lambda is the caller object and the field type class.
136
136
  # @option options [Lambda] :optional define this field as optional. Given lambda
137
137
  # is used to known if this field is present or not. Parameter to this lambda is
138
138
  # the being defined Field object.
@@ -296,7 +296,7 @@ module PacketGen
296
296
  type, default, builder, optional, enum, field_options = ary
297
297
  default = default.call if default.is_a?(Proc)
298
298
  @fields[field] = if builder
299
- builder.call(self)
299
+ builder.call(self, type)
300
300
  elsif enum
301
301
  type.new(enum)
302
302
  elsif !field_options.empty?
@@ -376,18 +376,20 @@ module PacketGen
376
376
  start = 0
377
377
  fields.each do |field|
378
378
  next unless is_present?(field)
379
+ obj = nil
379
380
  if self[field].respond_to? :width
380
381
  width = self[field].width
381
- self[field].read str[start, width]
382
+ obj = self[field].read str[start, width]
382
383
  start += width
383
384
  elsif self[field].respond_to? :sz
384
- self[field].read str[start..-1]
385
+ obj = self[field].read str[start..-1]
385
386
  size = self[field].sz
386
387
  start += size
387
388
  else
388
- self[field].read str[start..-1]
389
+ obj = self[field].read str[start..-1]
389
390
  start = str.size
390
391
  end
392
+ self[field] = obj unless obj == self[field]
391
393
  end
392
394
 
393
395
  self
@@ -64,6 +64,7 @@ module PacketGen
64
64
  def to_i
65
65
  @value || @default
66
66
  end
67
+ alias to_human to_i
67
68
 
68
69
  # Convert Int to Float
69
70
  # @return [Float]
@@ -16,10 +16,10 @@ module PacketGen
16
16
  # @return [String]
17
17
  attr_reader :string
18
18
 
19
- # @param [::String] str
20
19
  # @param [Class] len_type should be a {Int} subclass
21
- def initialize(str='', len_type=Int8)
22
- @string = Types::String.new(str)
20
+ # @param [::String] string
21
+ def initialize(len_type=Int8, string: '')
22
+ @string = Types::String.new.read(string)
23
23
  @length = len_type.new
24
24
  calc_length
25
25
  end
@@ -61,6 +61,13 @@ module PacketGen
61
61
  @length.to_s << @string.to_s
62
62
  end
63
63
 
64
+ # Get human readable string
65
+ # @return [::String]
66
+ # @since 2.2.0
67
+ def to_human
68
+ @string.to_s.inspect
69
+ end
70
+
64
71
  # Set length from internal string length
65
72
  # @return [Integer]
66
73
  def calc_length
@@ -16,9 +16,11 @@ module PacketGen
16
16
  # @param [Hash] options
17
17
  # @option options [Types::Int,Proc] :length_from object or proc from which
18
18
  # takes length when reading
19
- def initialize(str='', options={})
20
- super(str)
19
+ # @option options [Integer] :static_length set a static length for this string
20
+ def initialize(options={})
21
+ super()
21
22
  @length_from = options[:length_from]
23
+ @static_length = options[:static_length]
22
24
  end
23
25
 
24
26
  # @param [::String] str
@@ -26,10 +28,16 @@ module PacketGen
26
28
  def read(str)
27
29
  s = str.to_s
28
30
  s = case @length_from
29
- when Int
31
+ when Types::Int
30
32
  s[0, @length_from.to_i]
33
+ when Proc
34
+ s[0, @length_from.call]
31
35
  else
32
- s
36
+ if @static_length.is_a? Integer
37
+ s[0, @static_length]
38
+ else
39
+ s
40
+ end
33
41
  end
34
42
  self.replace s
35
43
  self
@@ -30,8 +30,7 @@ module PacketGen
30
30
  define_field :length, Int8
31
31
  # @!attribute value
32
32
  # @return [String]
33
- define_field :value, String,
34
- builder: ->(tlv) { String.new('', length_from: tlv[:length]) }
33
+ define_field :value, String
35
34
 
36
35
  # @param [Hash] options
37
36
  # @option options [Integer] :type
@@ -41,16 +40,31 @@ module PacketGen
41
40
  # Default: {Int8}.
42
41
  # @option options [Class] :l {Int} subclass for +:length+ attribute.
43
42
  # Default: {Int8}.
43
+ # @option options [Class] :v {String} subclass for +:value+ attribute.
44
+ # Default: {Types::String}.
44
45
  def initialize(options={})
45
46
  super
46
- self[:type] = options[:t].new(type) if options[:t]
47
- self[:length] = options[:l].new(length) if options[:l]
48
- self[:value] = String.new('', length_from: self[:length])
47
+ self[:type] = options[:t].new(self.type) if options[:t]
48
+ self[:length] = options[:l].new(self.length) if options[:l]
49
+ self[:value] = options[:v].new if options[:v]
49
50
  self.type = options[:type] if options[:type]
50
51
  self.value = options[:value] if options[:value]
51
52
  self.length = options[:length] if options[:length]
52
53
  end
53
54
 
55
+ # Populate object from a binary string
56
+ # @param [String] str
57
+ # @return [Fields] self
58
+ def read(str)
59
+ idx = 0
60
+ self[:type].read str[idx, self[:type].sz]
61
+ idx += self[:type].sz
62
+ self[:length].read str[idx, self[:length].sz]
63
+ idx += self[:length].sz
64
+ self[:value].read str[idx, self.length]
65
+ self
66
+ end
67
+
54
68
  # @private
55
69
  alias old_type= type=
56
70
 
@@ -73,13 +87,31 @@ module PacketGen
73
87
  end
74
88
  end
75
89
 
76
- # Set +value+ and +length+ fields
77
- # @param [::String] str
78
- # @return [::String]
79
- def value=(str)
80
- self.length = str.length
81
- self[:value].read str
82
- str
90
+ # Set +value+. May set +length+ if value is a {Types::String}.
91
+ # @param [::String,Integer] val
92
+ # @return [::String,Integer]
93
+ def value=(val)
94
+ if self[:value].respond_to? :from_human
95
+ self[:value].from_human val
96
+ elsif self[:value].is_a? Types::Int
97
+ self[:value].value = val
98
+ else
99
+ self.length = val.length if val.is_a? ::String
100
+ self[:value].read val
101
+ end
102
+ val
103
+ end
104
+
105
+ # Get +value+
106
+ # @return [Object] depend on +value+ type
107
+ def value
108
+ if self[:value].respond_to? :to_human
109
+ self[:value].to_human
110
+ elsif self[:value].is_a? Types::Int
111
+ self[:value].to_i
112
+ else
113
+ self[:value]
114
+ end
83
115
  end
84
116
 
85
117
  # Return human readable type, if TYPES is defined
@@ -53,7 +53,8 @@ module PacketGen
53
53
  timeout = options[:timeout] || 1
54
54
  my_hwaddr = Config.instance.hwaddr(iface)
55
55
  arp_pkt = Packet.gen('Eth', dst: 'ff:ff:ff:ff:ff:ff', src: my_hwaddr)
56
- arp_pkt.add('ARP', sha: @config.hwaddr, spa: @config.ipaddr, tpa: ipaddr)
56
+ arp_pkt.add('ARP', sha: Config.instance.hwaddr, spa: Config.instance.ipaddr,
57
+ tpa: ipaddr)
57
58
 
58
59
  capture = Capture.new(iface: iface, timeout: timeout, max: 1,
59
60
  filter: "arp src #{ipaddr} and ether dst #{my_hwaddr}")
@@ -97,5 +98,54 @@ module PacketGen
97
98
  as.start(target_ip, spoofed_ip, mac: options[:mac])
98
99
  as.wait
99
100
  end
101
+
102
+ # Man in the middle attack. Capture all packets between two peers on
103
+ # same local network.
104
+ # @note This method is provided for test purpose.
105
+ # @param [String] target1 IP address of first peer to attack
106
+ # @param [String] target2 IP address of second peer to attack
107
+ # @param [Hash] options
108
+ # @option options [Float,Integer] :interval number of seconds between 2
109
+ # ARP packets (default: 1.0).
110
+ # @option options [String] :iface interface to use. Default to
111
+ # {PacketGen.default_iface}
112
+ # @return [void]
113
+ # @yieldparam [Packet] pkt captured packets between target1 and target2
114
+ # @yieldreturn [Packet] packet to send to target1 or 2. This may be
115
+ # modified received packet
116
+ # @example Change ID in packets
117
+ # PacketGen::Utils.mitm('192.168.0.1', '192.168.0.45') do |pkt|
118
+ # if pkt.ip.src == '192.168.0.1'
119
+ # # 192.168.0.1 -> 192.168.0.45
120
+ # pkt.ip.id = 1
121
+ # else
122
+ # # 192.168.0.45 -> 192.168.0.1
123
+ # pkt.ip.id = 2
124
+ # end
125
+ # pkt
126
+ # end
127
+ # @since 2.2.0
128
+ def self.mitm(target1, target2, options={})
129
+ options = { iface: PacketGen.default_iface }.merge(options)
130
+
131
+ spoofer = Utils::ARPSpoofer.new(options)
132
+ spoofer.add target1, target2, options
133
+ spoofer.add target2, target1, options
134
+
135
+ my_mac = Config.instance.hwaddr(options[:iface])
136
+ my_ip = Config.instance.ipaddr(options[:iface])
137
+ capture = Capture.new(iface: options[:iface],
138
+ filter: "((ip src #{target1} and not ip dst #{my_ip}) or" +
139
+ " (ip src #{target2} and not ip dst #{my_ip}) or"+
140
+ " (ip dst #{target1} and not ip src #{my_ip}) or"+
141
+ " (ip dst #{target2} and not ip src #{my_ip}))"+
142
+ " and ether dst #{my_mac}")
143
+
144
+ spoofer.start_all
145
+ capture.start do |pkt|
146
+ modified_pkt = yield pkt
147
+ modified_pkt.ip.to_w(options[:iface])
148
+ end
149
+ end
100
150
  end
101
151
  end
@@ -147,7 +147,7 @@ module PacketGen
147
147
  @spoof_thread = Thread.new(@queue, @iface, @timeout, @interval) do |queue, iface, timeout, interval|
148
148
  while timeout.nil? or timeout > 0.0 do
149
149
  packets = queue.pop unless queue.empty?
150
- send_packets_on_wire packets
150
+ send_packets_on_wire(packets) unless packets.empty?
151
151
  timeout -= interval if timeout
152
152
  sleep interval
153
153
  end
@@ -156,7 +156,7 @@ module PacketGen
156
156
 
157
157
  # send packets on wire
158
158
  def send_packets_on_wire(packets)
159
- packets.each { |pkt| pkt.to_w(iface) }
159
+ packets.each { |pkt| pkt.to_w(@iface) }
160
160
  end
161
161
 
162
162
  # Deactivate spoofing for given target
@@ -8,5 +8,5 @@
8
8
  # @author Sylvain Daubert
9
9
  module PacketGen
10
10
  # PacketGen version
11
- VERSION = '2.1.4'
11
+ VERSION = '2.2.0'
12
12
  end
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: 2.1.4
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvain Daubert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-11 00:00:00.000000000 Z
11
+ date: 2017-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pcaprub
@@ -150,7 +150,11 @@ files:
150
150
  - lib/packetgen/header/arp.rb
151
151
  - lib/packetgen/header/asn1_base.rb
152
152
  - lib/packetgen/header/base.rb
153
+ - lib/packetgen/header/bootp.rb
153
154
  - lib/packetgen/header/crypto.rb
155
+ - lib/packetgen/header/dhcp.rb
156
+ - lib/packetgen/header/dhcp/option.rb
157
+ - lib/packetgen/header/dhcp/options.rb
154
158
  - lib/packetgen/header/dns.rb
155
159
  - lib/packetgen/header/dns/name.rb
156
160
  - lib/packetgen/header/dns/opt.rb
@@ -175,6 +179,10 @@ files:
175
179
  - lib/packetgen/header/esp.rb
176
180
  - lib/packetgen/header/eth.rb
177
181
  - lib/packetgen/header/gre.rb
182
+ - lib/packetgen/header/http.rb
183
+ - lib/packetgen/header/http/headers.rb
184
+ - lib/packetgen/header/http/request.rb
185
+ - lib/packetgen/header/http/response.rb
178
186
  - lib/packetgen/header/icmp.rb
179
187
  - lib/packetgen/header/icmpv6.rb
180
188
  - lib/packetgen/header/ike.rb
@@ -211,6 +219,7 @@ files:
211
219
  - lib/packetgen/proto.rb
212
220
  - lib/packetgen/types.rb
213
221
  - lib/packetgen/types/array.rb
222
+ - lib/packetgen/types/cstring.rb
214
223
  - lib/packetgen/types/enum.rb
215
224
  - lib/packetgen/types/fields.rb
216
225
  - lib/packetgen/types/int.rb