packetgen 3.1.4 → 3.1.5

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
  SHA256:
3
- metadata.gz: 2adf2b07c2d2d334e4f0ae6e4e7b49571d4d436788bef51df6cf7c1a5aaba9c8
4
- data.tar.gz: 8997321a31379ea6952ab42a4642d98a717587ed777652ebdb9e25b5928cc8d4
3
+ metadata.gz: 476cbc2bd2b9fb254ea40fe0a07a4d703a0fc36e4accc823de7e83b640d95a2d
4
+ data.tar.gz: 375f31ea90589b2b0c0b257b8a3efaa66715e8b369c542124fe3dcac283d9221
5
5
  SHA512:
6
- metadata.gz: b5af6e0d04f8bb03af01d63cde3879a6a13c8a78477811be2b95fee5c597820f4714f8ac8b69f4aa8f04d3d5b81c5e25b5787815ee2237a8c3329721b5dc7895
7
- data.tar.gz: 6eebb203f8dfde49505987d16778c1ecbc81c463d6ac8b5f005e2c6f486166ba0cceabd848d3b3aeebe867b138993171026e8dcacfb04c84a126bc149ff5c616
6
+ metadata.gz: c518ff72cf70227c8f9ef38cce97ba5036eb3229051d38172ba947898b9051f508670d1e0106bac0f5edf577fe1450d5f71db557aa6968b7e3998945a87177d7
7
+ data.tar.gz: 9382d6f8a7542c8988dec0aaf92940f1518885133bf9e7c835478e787546309410c7e7bcfb4525c13fb7479a9cad9a7ece3e30a3d3c6bbb4f081c1cc48b7f02e
@@ -97,6 +97,12 @@ module PacketGen
97
97
  Interfacez.default
98
98
  end
99
99
 
100
+ # Get loopback network interface
101
+ # @return [String]
102
+ def self.loopback_iface
103
+ Interfacez.loopback
104
+ end
105
+
100
106
  # Shortcut to get a header class
101
107
  # @example builtin class
102
108
  # # same as PacketGen::Header::Dot11:Data.new(id: 0xfedc)
@@ -114,8 +120,9 @@ module PacketGen
114
120
  end
115
121
 
116
122
  require 'packetgen/deprecation'
117
- require 'packetgen/types'
118
123
  require 'packetgen/inspect'
124
+ require 'packetgen/inspectable'
125
+ require 'packetgen/types'
119
126
  require 'packetgen/pcapng'
120
127
  require 'packetgen/pcap'
121
128
  require 'packetgen/packet'
@@ -13,7 +13,7 @@ module PacketGen
13
13
  class Capture
14
14
  private
15
15
 
16
- attr_reader :filter, :cap_thread, :snaplen, :promisc
16
+ attr_reader :filter, :cap_thread, :snaplen, :promisc, :monitor
17
17
 
18
18
  public
19
19
 
@@ -41,14 +41,18 @@ module PacketGen
41
41
  # yielding. Default: +true+
42
42
  # @param [Integer] snaplen maximum number of bytes to capture for
43
43
  # each packet.
44
+ # @param [Boolean] monitor enable or disable monitor mode on interface (if supported by +iface+).
44
45
  # @since 2.0.0 remove old 1.x API
45
46
  # @since 3.0.0 arguments are kwargs and no more a hash
46
- def initialize(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil)
47
- @iface = iface || Interfacez.default || Interfacez.loopback
47
+ # @since 3.1.5 add monitor argument
48
+ # @author Sylvain Daubert
49
+ # @author optix2000 - add monitor argument
50
+ def initialize(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil)
51
+ @iface = iface || PacketGen.default_iface || PacketGen.loopback_iface
48
52
 
49
53
  @packets = []
50
54
  @raw_packets = []
51
- set_options iface, max, timeout, filter, promisc, parse, snaplen
55
+ set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
52
56
  end
53
57
 
54
58
  # Start capture
@@ -56,8 +60,11 @@ module PacketGen
56
60
  # @yieldparam [Packet,String] packet if a block is given, yield each
57
61
  # captured packet (Packet or raw data String, depending on +:parse+ option)
58
62
  # @since 3.0.0 arguments are kwargs and no more a hash
59
- def start(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, &block)
60
- set_options iface, max, timeout, filter, promisc, parse, snaplen
63
+ # @since 3.1.5 add monitor argument
64
+ # @author Sylvain Daubert
65
+ # @author optix2000 - add monitor argument
66
+ def start(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil, &block)
67
+ set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
61
68
 
62
69
  @cap_thread = Thread.new do
63
70
  PCAPRUBWrapper.capture(**capture_args) do |packet_data|
@@ -79,7 +86,7 @@ module PacketGen
79
86
 
80
87
  private
81
88
 
82
- def set_options(iface, max, timeout, filter, promisc, parse, snaplen)
89
+ def set_options(iface, max, timeout, filter, promisc, parse, snaplen, monitor)
83
90
  @max = max if max
84
91
  @filter = filter unless filter.nil?
85
92
  @timeout = timeout unless timeout.nil?
@@ -87,10 +94,11 @@ module PacketGen
87
94
  @snaplen = snaplen unless snaplen.nil?
88
95
  @parse = parse unless parse.nil?
89
96
  @iface = iface unless iface.nil?
97
+ @monitor = monitor unless monitor.nil?
90
98
  end
91
99
 
92
100
  def capture_args
93
- h = { iface: iface, filter: filter }
101
+ h = { iface: iface, filter: filter, monitor: monitor }
94
102
  h[:snaplen] = snaplen unless snaplen.nil?
95
103
  h[:promisc] = promisc unless promisc.nil?
96
104
  h
@@ -22,37 +22,43 @@ module PacketGen
22
22
  attr_reader :default_iface
23
23
 
24
24
  def initialize
25
- @default_iface = Interfacez.default || Interfacez.loopback
25
+ @default_iface = PacketGen.default_iface || PacketGen.loopback_iface
26
26
  @hwaddr = {}
27
27
  @ipaddr = {}
28
28
  @ip6addr = {}
29
29
 
30
- Interfacez.all do |iface_name|
31
- @hwaddr[iface_name] = Interfacez.mac_address_of(iface_name)
32
- @ipaddr[iface_name] = Interfacez.ipv4_address_of(iface_name)
33
- @ip6addr[iface_name] = Interfacez.ipv6_addresses_of(iface_name)
34
- end
30
+ initialize_local_addresses
35
31
  end
36
32
 
37
33
  # Get MAC address for given network interface
38
34
  # @param [String,nil] iface network interface. If +nil+, use default one.
39
35
  # @return [String]
40
36
  def hwaddr(iface=nil)
41
- @hwaddr[iface || @default_iface]
37
+ @hwaddr[iface || default_iface]
42
38
  end
43
39
 
44
40
  # Get IP address for given network interface
45
41
  # @param [String,nil] iface network interface. If +nil+, use default one.
46
42
  # @return [String]
47
43
  def ipaddr(iface=nil)
48
- @ipaddr[iface || @default_iface]
44
+ @ipaddr[iface || default_iface]
49
45
  end
50
46
 
51
47
  # Get IPv6 addresses for given network interface
52
48
  # @param [String,nil] iface network interface. If +nil+, use default one.
53
49
  # @return [Array<String>]
54
50
  def ip6addr(iface=nil)
55
- @ip6addr[iface || @default_iface]
51
+ @ip6addr[iface || default_iface]
52
+ end
53
+
54
+ private
55
+
56
+ def initialize_local_addresses
57
+ Interfacez.all do |iface_name|
58
+ @hwaddr[iface_name] = Interfacez.mac_address_of(iface_name)
59
+ @ipaddr[iface_name] = Interfacez.ipv4_address_of(iface_name)
60
+ @ip6addr[iface_name] = Interfacez.ipv6_addresses_of(iface_name)
61
+ end
56
62
  end
57
63
  end
58
64
  end
@@ -31,7 +31,7 @@ module PacketGen
31
31
  complete_deprecated_method_name = "#{base_name}#{deprecated_method}"
32
32
  complete_new_method_name = "#{base_name}#{new_method}" unless new_method.nil?
33
33
 
34
- file, line = caller(2..2).split(':')[0, 2]
34
+ file, line = caller(2..2).first.split(':')[0, 2]
35
35
  message = +"#{file}:#{line}: #{complete_deprecated_method_name} is deprecated"
36
36
  message << " in favor of #{complete_new_method_name}" unless new_method.nil?
37
37
  message << '. ' << self.removed(remove_version)
@@ -230,17 +230,7 @@ module PacketGen
230
230
 
231
231
  # @!attribute options
232
232
  # @return [RequestedOptions]
233
- define_field :options, RequestedOptions
234
-
235
- # Populate object from +str+
236
- # @param [String] str
237
- # @return [self]
238
- def read(str)
239
- self[:type].read str[0, 2]
240
- self[:length].read str[2, 2]
241
- self[:options].read str[4, self.length]
242
- self
243
- end
233
+ define_field :options, RequestedOptions, builder: ->(h, t) { t.new(length_from: h[:length]) }
244
234
 
245
235
  # Get human-readable data
246
236
  # @return [String]
@@ -35,7 +35,7 @@ module PacketGen
35
35
  while !str.empty? && (self.size < @counter.to_i)
36
36
  question = Question.new(@dns).read(str)
37
37
  str.slice!(0, question.sz)
38
- self.push question
38
+ push question
39
39
  end
40
40
  self
41
41
  end
@@ -28,7 +28,7 @@ module PacketGen
28
28
  define_field :dlt, Types::Int32le
29
29
  # @!attribute ppi_fields
30
30
  # @return [Type::String] concatenation of PPI fields
31
- define_field :ppi_fields, Types::String
31
+ define_field :ppi_fields, Types::String, builder: ->(h, t) { t.new(length_from: -> { h.length - 8 }) }
32
32
  # @!attribute body
33
33
  # @return [Type::String]
34
34
  define_field :body, Types::String
@@ -36,21 +36,6 @@ module PacketGen
36
36
  # @return [Boolean] align flag from {#flags} attribute
37
37
  define_bit_fields_on :flags, :reserved, 7, :align
38
38
 
39
- # @param [String] str
40
- # @return [PPI] self
41
- def read(str)
42
- return self if str.nil?
43
-
44
- force_binary str
45
- self[:version].read str[0, 1]
46
- self[:flags].read str[1, 1]
47
- self[:length].read str[2, 2]
48
- self[:dlt].read str[4, 4]
49
- self[:ppi_fields].read str[8, length - 8]
50
- self[:body].read str[length, str.size]
51
- self
52
- end
53
-
54
39
  # Check version field
55
40
  # @see [Base#parse?]
56
41
  def parse?
@@ -91,26 +76,11 @@ module PacketGen
91
76
  define_field :present_flags, Types::Int32le
92
77
  # @!attribute radio_fields
93
78
  # @return [Type::String] concatenation of RadioTap fields
94
- define_field :radio_fields, Types::String
79
+ define_field :radio_fields, Types::String, builder: ->(h, t) { t.new(length_from: -> { h.length - 8 }) }
95
80
  # @!attribute body
96
81
  # @return [Type::String]
97
82
  define_field :body, Types::String
98
83
 
99
- # @param [String] str
100
- # @return [RadioTap] self
101
- def read(str)
102
- return self if str.nil?
103
-
104
- force_binary str
105
- self[:version].read str[0, 1]
106
- self[:pad].read str[1, 1]
107
- self[:length].read str[2, 2]
108
- self[:present_flags].read str[4, 4]
109
- self[:radio_fields].read str[8, length - 8]
110
- self[:body].read str[length, str.size]
111
- self
112
- end
113
-
114
84
  # Check version field
115
85
  # @see [Base#parse?]
116
86
  def parse?
@@ -45,20 +45,7 @@ module PacketGen
45
45
  define_field :length, Types::Int16
46
46
  # @!attribute body
47
47
  # @return [Types::String,Header::Base]
48
- define_field :body, Types::String
49
-
50
- # Populate object from string
51
- # @param [String] str
52
- # @return [self]
53
- def read(str)
54
- return self if str.nil?
55
-
56
- self[:version].read(str[0, 1])
57
- self[:type].read(str[1, 1])
58
- self[:length].read(str[2, 2])
59
- self[:body].read(str[4, self.length])
60
- self
61
- end
48
+ define_field :body, Types::String, builder: ->(h, t) { t.new(length_from: h[:length]) }
62
49
 
63
50
  # Get human readable type
64
51
  # @return [String]
@@ -132,21 +132,17 @@ module PacketGen
132
132
  def read(str)
133
133
  super str
134
134
  return self unless self.class == EAP
135
-
136
- if type?
137
- obj = case self.type
138
- when 4
139
- EAP::MD5.new.read(str)
140
- when 13
141
- EAP::TLS.new.read(str)
142
- when 21
143
- EAP::TTLS.new.read(str)
144
- when 43
145
- EAP::FAST.new.read(str)
146
- else
147
- self
148
- end
149
- obj
135
+ return self unless type?
136
+
137
+ case self.type
138
+ when 4
139
+ EAP::MD5.new.read(str)
140
+ when 13
141
+ EAP::TLS.new.read(str)
142
+ when 21
143
+ EAP::TTLS.new.read(str)
144
+ when 43
145
+ EAP::FAST.new.read(str)
150
146
  else
151
147
  self
152
148
  end
@@ -33,6 +33,8 @@ module PacketGen
33
33
  # Ethernet MAC address, as a group of 6 bytes
34
34
  # @author Sylvain Daubert
35
35
  class MacAddr < Types::Fields
36
+ include Inspectable
37
+
36
38
  # @!attribute a0
37
39
  # @return [Integer] first byte from MacAddr
38
40
  define_field :a0, Types::Int8
@@ -75,6 +77,7 @@ module PacketGen
75
77
  def to_human
76
78
  fields.map { |m| '%02x' % self[m] }.join(':')
77
79
  end
80
+ alias format_inspect to_human
78
81
 
79
82
  def ==(other)
80
83
  other.is_a?(self.class) &&
@@ -12,6 +12,8 @@ module PacketGen
12
12
  # @abstract Base class for HTTP headers.
13
13
  # @author Kent 'picat' Gruber
14
14
  class Headers
15
+ include Inspectable
16
+
15
17
  # Underlying Headers data (or nil).
16
18
  # @return [Hash, nil]
17
19
  attr_reader :data
@@ -56,6 +58,7 @@ module PacketGen
56
58
  def to_human
57
59
  @data
58
60
  end
61
+ alias format_inspect to_human
59
62
 
60
63
  # Read human-readable data to populate header data.
61
64
  # @param [String, Hash] data
@@ -11,6 +11,8 @@ module PacketGen
11
11
  # IP address, as a group of 4 bytes
12
12
  # @author Sylvain Daubert
13
13
  class Addr < Types::Fields
14
+ include Inspectable
15
+
14
16
  # @!attribute a1
15
17
  # @return [Integer] IP address first byte
16
18
  define_field :a1, Types::Int8
@@ -47,6 +49,7 @@ module PacketGen
47
49
  def to_human
48
50
  fields.map { |f| self[f].to_i.to_s }.join('.')
49
51
  end
52
+ alias format_inspect to_human
50
53
 
51
54
  # Addr as an integer
52
55
  # @return [Integer]
@@ -14,6 +14,8 @@ module PacketGen
14
14
  # IPv6 address, as a group of 8 2-byte words
15
15
  # @author Sylvain Daubert
16
16
  class Addr < Types::Fields
17
+ include Inspectable
18
+
17
19
  # @!attribute a1
18
20
  # 1st 2-byte word of IPv6 address
19
21
  # @return [Integer]
@@ -73,6 +75,7 @@ module PacketGen
73
75
  def to_human
74
76
  IPAddr.new(to_a.map { |a| a.to_i.to_s(16) }.join(':')).to_s
75
77
  end
78
+ alias format_inspect to_human
76
79
 
77
80
  # Return an array of address 16-bit words
78
81
  # @return [Array<Integer>]
@@ -121,7 +121,7 @@ module PacketGen
121
121
  # @!attribute options
122
122
  # TCP options
123
123
  # @return [Options]
124
- define_field :options, TCP::Options
124
+ define_field :options, TCP::Options, builder: ->(h, t) { t.new(length_from: -> { h.data_offset > 5 ? (h.data_offset - 5) * 4 : 0 }) }
125
125
  # @!attribute body
126
126
  # @return [Types::String,Header::Base]
127
127
  define_field :body, Types::String
@@ -189,25 +189,6 @@ module PacketGen
189
189
  # @return [Boolean] 1-bit FIN flag
190
190
  define_bit_fields_on :u16, :_, 7, :flag_ns, :flag_cwr, :flag_ece, :flag_urg,
191
191
  :flag_ack, :flag_psh, :flag_rst, :flag_syn, :flag_fin
192
- # Read a TCP header from a string
193
- # @param [String] str binary string
194
- # @return [self]
195
- def read(str)
196
- return self if str.nil?
197
-
198
- force_binary str
199
- self[:sport].read str[0, 2]
200
- self[:dport].read str[2, 2]
201
- self[:seqnum].read str[4, 4]
202
- self[:acknum].read str[8, 4]
203
- self[:u16].read str[12, 2]
204
- self[:window].read str[14, 2]
205
- self[:checksum].read str[16, 2]
206
- self[:urg_pointer].read str[18, 2]
207
- self[:options].read str[20, (self.data_offset - 5) * 4] if self.data_offset > 5
208
- self[:body].read str[self.data_offset * 4..-1]
209
- self
210
- end
211
192
 
212
193
  # Compute checksum and set +checksum+ field
213
194
  # @return [Integer]
@@ -68,23 +68,7 @@ module PacketGen
68
68
  # @return [String]
69
69
  def self.inspect_attribute(attr, value, level=1)
70
70
  type = value.class.to_s.sub(/.*::/, '')
71
- val = case value
72
- when Types::Enum
73
- enum_human_hex(value.to_human, value.to_i, value.sz * 2)
74
- when Types::Int
75
- int_dec_hex(value, value.sz * 2)
76
- when Integer
77
- int_dec_hex(value, value.sz * 2)
78
- when String
79
- value.to_s.inspect
80
- else
81
- if value.respond_to? :to_human
82
- value.to_human
83
- else
84
- value.to_s.inspect
85
- end
86
- end
87
- self.format(type, attr, val, level)
71
+ self.format(type, attr, value.format_inspect, level)
88
72
  end
89
73
 
90
74
  # Format a ASN.1 attribute for +#inspect+.
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of PacketGen
4
+ # See https://github.com/sdaubert/packetgen for more informations
5
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # This program is published under MIT license.
7
+
8
+ module PacketGen
9
+ # Module to add common methods to format types when inspecting packets/headers.
10
+ # @author Sylvain Daubert
11
+ # @since 3.1.5
12
+ module Inspectable
13
+ # Format attribute for inspecting
14
+ # @abstract should be overriden by types.
15
+ # @return [String]
16
+ def format_inspect
17
+ to_s.inspect
18
+ end
19
+ end
20
+ end
@@ -23,9 +23,19 @@ module PacketGen
23
23
  # @param [String] iface interface name
24
24
  # @param [Integer] snaplen
25
25
  # @param [Boolean] promisc
26
+ # @param [Boolean] monitor
26
27
  # @return [PCAPRUB::Pcap]
27
- def self.open_iface(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC)
28
- PCAPRUB::Pcap.open_live(iface, snaplen, promisc, TIMEOUT)
28
+ # @author Sylvain Daubert
29
+ # @author optix2000 - add support for setting monitor mode
30
+ # @since 3.1.5 add monitor argument
31
+ def self.open_iface(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, monitor: nil)
32
+ pcap = PCAPRUB::Pcap.create(iface)
33
+ pcap.setsnaplen(snaplen)
34
+ pcap.setpromisc(promisc)
35
+ pcap.settimeout(TIMEOUT)
36
+ # Monitor MUST be set before pcap is activated
37
+ pcap.setmonitor monitor unless monitor.nil?
38
+ pcap.activate
29
39
  end
30
40
 
31
41
  # Capture packets from a network interface
@@ -33,10 +43,14 @@ module PacketGen
33
43
  # @param [Integer] snaplen
34
44
  # @param [Boolean] promisc
35
45
  # @param [String] filter BPF filter
46
+ # @param [Boolean] monitor
36
47
  # @yieldparam [String] packet_data binary packet data
37
48
  # @return [void]
38
- def self.capture(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, filter: nil)
39
- pcap = self.open_iface(iface: iface, snaplen: snaplen, promisc: promisc)
49
+ # @author Sylvain Daubert
50
+ # @author optix2000 - add support for setting monitor mode
51
+ # @since 3.1.5 add monitor argument
52
+ def self.capture(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, filter: nil, monitor: nil)
53
+ pcap = self.open_iface(iface: iface, snaplen: snaplen, promisc: promisc, monitor: monitor)
40
54
  pcap.setfilter filter unless filter.nil?
41
55
  pcap.each do |packet_data|
42
56
  yield packet_data
@@ -58,6 +58,8 @@ module PacketGen
58
58
  # @since 3.1.0
59
59
  # @since 3.1.1 add +:aliases+ keyword to {#initialize}
60
60
  class AbstractTLV < Types::Fields
61
+ include Inspectable
62
+
61
63
  class <<self
62
64
  # @return [Hash]
63
65
  attr_accessor :aliases
@@ -169,8 +171,9 @@ module PacketGen
169
171
  # @return [String]
170
172
  def to_human
171
173
  my_value = self[:value].is_a?(String) ? self[:value].inspect : self[:value].to_human
172
- "type:%s,length:%u,value:#{my_value}" % [human_type, length]
174
+ "type:%s,length:%u,value:%s" % [human_type, length, my_value]
173
175
  end
176
+ alias format_inspect to_human
174
177
 
175
178
  private
176
179
 
@@ -27,6 +27,9 @@ module PacketGen
27
27
  # @author Sylvain Daubert
28
28
  class Array
29
29
  extend Forwardable
30
+ include Inspectable
31
+ include Enumerable
32
+ include LengthFrom
30
33
 
31
34
  # @!method [](index)
32
35
  # Return the element at +index+.
@@ -54,9 +57,6 @@ module PacketGen
54
57
  def_delegators :@array, :[], :clear, :each, :empty?, :first, :last, :size
55
58
  alias length size
56
59
 
57
- include Enumerable
58
- include LengthFrom
59
-
60
60
  # Separator used in {#to_human}.
61
61
  # May be ovverriden by subclasses
62
62
  HUMAN_SEPARATOR = ','
@@ -198,6 +198,7 @@ module PacketGen
198
198
  def to_human
199
199
  @array.map(&:to_human).join(self.class::HUMAN_SEPARATOR)
200
200
  end
201
+ alias format_inspect to_human
201
202
 
202
203
  private
203
204
 
@@ -11,6 +11,8 @@ module PacketGen
11
11
  # This class handles null-terminated strings (aka C strings).
12
12
  # @author Sylvain Daubert
13
13
  class CString < ::String
14
+ include Inspectable
15
+
14
16
  # @param [Hash] options
15
17
  # @option options [Integer] :static_length set a static length for this string
16
18
  def initialize(options={})
@@ -66,6 +68,7 @@ module PacketGen
66
68
  idx = self.index(+"\x00".encode(self.encoding)) || self.sz
67
69
  self[0, idx]
68
70
  end
71
+ alias format_inspect to_human
69
72
  end
70
73
  end
71
74
  end
@@ -71,6 +71,10 @@ module PacketGen
71
71
  def to_human
72
72
  @enum.key(to_i) || "<unknown:#{@value}>"
73
73
  end
74
+
75
+ def format_inspect
76
+ format_str % [to_human, to_i]
77
+ end
74
78
  end
75
79
 
76
80
  # Enumeration on one byte. See {Enum}.
@@ -119,6 +119,10 @@ module PacketGen
119
119
  # @return [Hash]
120
120
  # @since 3.1.0
121
121
  attr_reader :field_defs
122
+ # Get bit fields defintions for this class
123
+ # @return [Hash]
124
+ # @since 3.1.5
125
+ attr_reader :bit_fields
122
126
 
123
127
  # On inheritage, create +@field_defs+ class variable
124
128
  # @param [Class] klass
@@ -129,7 +133,7 @@ module PacketGen
129
133
  field_defs[k] = v.clone
130
134
  end
131
135
  ordered = @ordered_fields.clone
132
- bf = @bit_fields.clone
136
+ bf = bit_fields.clone
133
137
 
134
138
  klass.class_eval do
135
139
  @ordered_fields = ordered
@@ -172,37 +176,15 @@ module PacketGen
172
176
  # Define enumeration: hash's keys are +String+, and values are +Integer+.
173
177
  # @return [void]
174
178
  def define_field(name, type, options={})
175
- define = []
176
- if type < Types::Enum
177
- define << "def #{name}; self[:#{name}].to_i; end"
178
- define << "def #{name}=(val) self[:#{name}].value = val; end"
179
- else
180
- define << "def #{name}\n" \
181
- " if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
182
- " self[:#{name}].to_human\n" \
183
- " else\n" \
184
- " self[:#{name}]\n" \
185
- " end\n" \
186
- 'end'
187
- define << "def #{name}=(val)\n" \
188
- " if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
189
- " self[:#{name}].from_human val\n" \
190
- " else\n" \
191
- " self[:#{name}].read val\n" \
192
- " end\n" \
193
- 'end'
194
- end
195
-
196
- define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
197
- define.delete_at(0) if instance_methods.include? name
198
- class_eval define.join("\n")
179
+ fields << name
199
180
  field_defs[name] = FieldDef.new(type,
200
181
  options.delete(:default),
201
182
  options.delete(:builder),
202
183
  options.delete(:optional),
203
184
  options.delete(:enum),
204
185
  options)
205
- fields << name
186
+
187
+ add_methods(name, type)
206
188
  end
207
189
 
208
190
  # Define a field, before another one
@@ -300,8 +282,8 @@ module PacketGen
300
282
  total_size = type.new.width * 8
301
283
  idx = total_size - 1
302
284
 
303
- field = args.shift
304
- while field
285
+ until args.empty?
286
+ field = args.shift
305
287
  next unless field.is_a? Symbol
306
288
 
307
289
  size = if args.first.is_a? Integer
@@ -309,41 +291,13 @@ module PacketGen
309
291
  else
310
292
  1
311
293
  end
294
+
312
295
  unless field == :_
313
- shift = idx - (size - 1)
314
- field_mask = (2**size - 1) << shift
315
- clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
316
-
317
- if size == 1
318
- class_eval <<-METHODS
319
- def #{field}?
320
- val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
321
- val != 0
322
- end
323
- def #{field}=(v)
324
- val = v ? 1 : 0
325
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
326
- self[:#{attr}].value |= val << #{shift}
327
- end
328
- METHODS
329
- else
330
- class_eval <<-METHODS
331
- def #{field}
332
- (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
333
- end
334
- def #{field}=(v)
335
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
336
- self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
337
- end
338
- METHODS
339
- end
340
-
341
- @bit_fields[attr] = {} if @bit_fields[attr].nil?
342
- @bit_fields[attr][field] = size
296
+ add_bit_methods(attr, field, size, total_size, idx)
297
+ register_bit_field_size(attr, field, size)
343
298
  end
344
299
 
345
300
  idx -= size
346
- field = args.shift
347
301
  end
348
302
  end
349
303
 
@@ -352,7 +306,7 @@ module PacketGen
352
306
  # @return [void]
353
307
  # @since 2.8.4
354
308
  def remove_bit_fields_on(attr)
355
- fields = @bit_fields.delete(attr)
309
+ fields = bit_fields.delete(attr)
356
310
  return if fields.nil?
357
311
 
358
312
  fields.each do |field, size|
@@ -360,6 +314,62 @@ module PacketGen
360
314
  undef_method(size == 1 ? "#{field}?" : field)
361
315
  end
362
316
  end
317
+
318
+ private
319
+
320
+ def add_methods(name, type)
321
+ define = []
322
+ if type < Types::Enum
323
+ define << "def #{name}; self[:#{name}].to_i; end"
324
+ define << "def #{name}=(val) self[:#{name}].value = val; end"
325
+ else
326
+ define << "def #{name}\n" \
327
+ " to_and_from_human?(:#{name}) ? self[:#{name}].to_human : self[:#{name}]\n" \
328
+ 'end'
329
+ define << "def #{name}=(val)\n" \
330
+ " to_and_from_human?(:#{name}) ? self[:#{name}].from_human(val) : self[:#{name}].read(val)\n" \
331
+ 'end'
332
+ end
333
+
334
+ define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
335
+ define.delete_at(0) if instance_methods.include? name
336
+ class_eval define.join("\n")
337
+ end
338
+
339
+ def add_bit_methods(attr, name, size, total_size, idx)
340
+ shift = idx - (size - 1)
341
+ field_mask = (2**size - 1) << shift
342
+ clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
343
+
344
+ if size == 1
345
+ class_eval <<-METHODS
346
+ def #{name}?
347
+ val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
348
+ val != 0
349
+ end
350
+ def #{name}=(v)
351
+ val = v ? 1 : 0
352
+ self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
353
+ self[:#{attr}].value |= val << #{shift}
354
+ end
355
+ METHODS
356
+ else
357
+ class_eval <<-METHODS
358
+ def #{name}
359
+ (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
360
+ end
361
+ def #{name}=(v)
362
+ self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
363
+ self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
364
+ end
365
+ METHODS
366
+ end
367
+ end
368
+
369
+ def register_bit_field_size(attr, field, size)
370
+ bit_fields[attr] = {} if bit_fields[attr].nil?
371
+ bit_fields[attr][field] = size
372
+ end
363
373
  end
364
374
 
365
375
  # Create a new fields object
@@ -369,36 +379,13 @@ module PacketGen
369
379
  @fields = {}
370
380
  @optional_fields = {}
371
381
 
372
- field_defs = self.class.field_defs
373
382
  self.class.fields.each do |field|
374
- type = field_defs[field].type
375
- default = field_defs[field].default
376
- default = default.to_proc.call(self) if default.is_a?(Proc)
377
- builder = field_defs[field].builder
378
- optional = field_defs[field].optional
379
- enum = field_defs[field].enum
380
- field_options = field_defs[field].options
381
-
382
- @fields[field] = if builder
383
- builder.call(self, type)
384
- elsif enum
385
- type.new(enum)
386
- elsif !field_options.empty?
387
- type.new(field_options)
388
- else
389
- type.new
390
- end
391
-
392
- value = options[field] || default
393
- if value.class <= type
394
- @fields[field] = value
395
- elsif @fields[field].respond_to? :from_human
396
- @fields[field].from_human(value)
397
- end
398
-
399
- @optional_fields[field] = optional if optional
383
+ build_field field
384
+ initialize_value field, options[field]
385
+ initialize_optional field
400
386
  end
401
- self.class.class_eval { @bit_fields }.each do |_, hsh|
387
+
388
+ self.class.bit_fields.each do |_, hsh|
402
389
  hsh.each_key do |bit_field|
403
390
  self.send "#{bit_field}=", options[bit_field] if options[bit_field]
404
391
  end
@@ -528,7 +515,7 @@ module PacketGen
528
515
  # @return [Hash,nil] keys: bit fields, values: their size in bits
529
516
  # @since 2.8.3
530
517
  def bits_on(field)
531
- self.class.class_eval { @bit_fields }[field]
518
+ self.class.bit_fields[field]
532
519
  end
533
520
 
534
521
  private
@@ -547,6 +534,51 @@ module PacketGen
547
534
  def force_binary(str)
548
535
  PacketGen.force_binary(str)
549
536
  end
537
+
538
+ # @param [Symbol] attr attribute
539
+ # @return [Boolean] +tru+e if #from_human and #to_human are both defined for given attribute
540
+ def to_and_from_human?(attr)
541
+ self[attr].respond_to?(:to_human) && self[attr].respond_to?(:from_human)
542
+ end
543
+
544
+ def field_defs
545
+ self.class.field_defs
546
+ end
547
+
548
+ def build_field(field)
549
+ type = field_defs[field].type
550
+ builder = field_defs[field].builder
551
+ enum = field_defs[field].enum
552
+ field_options = field_defs[field].options
553
+
554
+ @fields[field] = if builder
555
+ builder.call(self, type)
556
+ elsif enum
557
+ type.new(enum)
558
+ elsif !field_options.empty?
559
+ type.new(field_options)
560
+ else
561
+ type.new
562
+ end
563
+ end
564
+
565
+ def initialize_value(field, val)
566
+ type = field_defs[field].type
567
+ default = field_defs[field].default
568
+ default = default.to_proc.call(self) if default.is_a?(Proc)
569
+
570
+ value = val || default
571
+ if value.class <= type
572
+ @fields[field] = value
573
+ elsif @fields[field].respond_to? :from_human
574
+ @fields[field].from_human(value)
575
+ end
576
+ end
577
+
578
+ def initialize_optional(field)
579
+ optional = field_defs[field].optional
580
+ @optional_fields[field] = optional if optional
581
+ end
550
582
  end
551
583
  end
552
584
  end
@@ -12,6 +12,8 @@ module PacketGen
12
12
  # @abstract
13
13
  # @author Sylvain Daubert
14
14
  class Int
15
+ include Inspectable
16
+
15
17
  # Integer value
16
18
  # @return [Integer]
17
19
  attr_accessor :value
@@ -80,6 +82,18 @@ module PacketGen
80
82
  def sz
81
83
  width
82
84
  end
85
+
86
+ # Format Int type when inspecting header or packet
87
+ # @return [String]
88
+ def format_inspect
89
+ format_str % [to_i.to_s, to_i]
90
+ end
91
+
92
+ private
93
+
94
+ def format_str
95
+ "%-16s (0x%0#{width * 2}x)"
96
+ end
83
97
  end
84
98
 
85
99
  # One byte unsigned integer
@@ -12,6 +12,8 @@ module PacketGen
12
12
  # By default, a null string will have one byte length (length byte set to 0).
13
13
  # @author Sylvain Daubert
14
14
  class IntString
15
+ include Inspectable
16
+
15
17
  # internal string
16
18
  # @return [String]
17
19
  attr_reader :string
@@ -76,6 +78,7 @@ module PacketGen
76
78
  def to_human
77
79
  @string
78
80
  end
81
+ alias format_inspect to_human
79
82
 
80
83
  # Set length from internal string length
81
84
  # @return [Integer]
@@ -14,6 +14,8 @@ module PacketGen
14
14
  # oui.to_human # => "00:01:02"
15
15
  # @author Sylvain Daubert
16
16
  class OUI < Types::Fields
17
+ include Inspectable
18
+
17
19
  # @attribute b2
18
20
  # @return [Integer] left-most byte
19
21
  define_field :b2, Types::Int8
@@ -44,6 +46,7 @@ module PacketGen
44
46
  def to_human
45
47
  fields.map { |m| '%02x' % self[m] }.join(':')
46
48
  end
49
+ alias format_inspect to_human
47
50
  end
48
51
  end
49
52
  end
@@ -14,6 +14,7 @@ module PacketGen
14
14
  # @author Sylvain Daubert
15
15
  class String < ::String
16
16
  include LengthFrom
17
+ include Inspectable
17
18
 
18
19
  # @return [Integer]
19
20
  attr_reader :static_length
@@ -31,6 +31,8 @@ module PacketGen
31
31
  # @deprecated Use {AbstractTLV} instead.
32
32
  # @since 3.1.0 deprecated
33
33
  class TLV < Fields
34
+ include Inspectable
35
+
34
36
  # @!attribute type
35
37
  # @return [Integer]
36
38
  define_field :type, Int8
@@ -139,6 +141,7 @@ module PacketGen
139
141
  "#{name} type:#{@typestr} length:#{@lenstr} value:#{value.inspect}" % [human_type,
140
142
  length]
141
143
  end
144
+ alias format_inspect to_human
142
145
 
143
146
  private
144
147
 
@@ -53,7 +53,7 @@ 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.instance.hwaddr, spa: Config.instance.ipaddr,
56
+ arp_pkt.add('ARP', sha: Config.instance.hwaddr(iface), spa: Config.instance.ipaddr(iface),
57
57
  tpa: ipaddr)
58
58
 
59
59
  capture = Capture.new(iface: iface, timeout: timeout, max: 1,
@@ -10,5 +10,5 @@
10
10
  # @author Sylvain Daubert
11
11
  module PacketGen
12
12
  # PacketGen version
13
- VERSION = '3.1.4'
13
+ VERSION = '3.1.5'
14
14
  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: 3.1.4
4
+ version: 3.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvain Daubert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-22 00:00:00.000000000 Z
11
+ date: 2020-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: interfacez
@@ -208,6 +208,7 @@ files:
208
208
  - lib/packetgen/headerable.rb
209
209
  - lib/packetgen/inject.rb
210
210
  - lib/packetgen/inspect.rb
211
+ - lib/packetgen/inspectable.rb
211
212
  - lib/packetgen/packet.rb
212
213
  - lib/packetgen/pcap.rb
213
214
  - lib/packetgen/pcapng.rb
@@ -235,10 +236,11 @@ files:
235
236
  - lib/packetgen/utils.rb
236
237
  - lib/packetgen/utils/arp_spoofer.rb
237
238
  - lib/packetgen/version.rb
238
- homepage: https://github.com/sdaubert/packetgen
239
+ homepage:
239
240
  licenses:
240
241
  - MIT
241
242
  metadata:
243
+ homepage_uri: https://github.com/sdaubert/packetgen
242
244
  bug_tracker_uri: https://github.com/sdaubert/packetgen/issues
243
245
  documentation_uri: https://www.rubydoc.info/gems/packetgen
244
246
  source_code_uri: https://github.com/sdaubert/packetgen