packetgen 2.6.0 → 2.7.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.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +28 -0
  4. data/Rakefile +6 -6
  5. data/bin/pgconsole +6 -6
  6. data/lib/packetgen.rb +4 -3
  7. data/lib/packetgen/capture.rb +5 -7
  8. data/lib/packetgen/config.rb +3 -3
  9. data/lib/packetgen/deprecation.rb +17 -0
  10. data/lib/packetgen/header.rb +40 -38
  11. data/lib/packetgen/header/arp.rb +46 -31
  12. data/lib/packetgen/header/asn1_base.rb +8 -8
  13. data/lib/packetgen/header/base.rb +108 -36
  14. data/lib/packetgen/header/bootp.rb +28 -19
  15. data/lib/packetgen/header/crypto.rb +6 -8
  16. data/lib/packetgen/header/dhcp.rb +4 -5
  17. data/lib/packetgen/header/dhcp/option.rb +46 -34
  18. data/lib/packetgen/header/dhcp/options.rb +0 -1
  19. data/lib/packetgen/header/dhcpv6.rb +10 -10
  20. data/lib/packetgen/header/dhcpv6/duid.rb +2 -3
  21. data/lib/packetgen/header/dhcpv6/option.rb +16 -21
  22. data/lib/packetgen/header/dhcpv6/relay.rb +6 -4
  23. data/lib/packetgen/header/dns.rb +13 -11
  24. data/lib/packetgen/header/dns/name.rb +4 -6
  25. data/lib/packetgen/header/dns/opt.rb +31 -31
  26. data/lib/packetgen/header/dns/option.rb +0 -2
  27. data/lib/packetgen/header/dns/qdsection.rb +1 -2
  28. data/lib/packetgen/header/dns/question.rb +19 -13
  29. data/lib/packetgen/header/dns/rr.rb +11 -14
  30. data/lib/packetgen/header/dns/rrsection.rb +5 -7
  31. data/lib/packetgen/header/dot11.rb +45 -29
  32. data/lib/packetgen/header/dot11/control.rb +3 -5
  33. data/lib/packetgen/header/dot11/data.rb +34 -6
  34. data/lib/packetgen/header/dot11/element.rb +0 -1
  35. data/lib/packetgen/header/dot11/management.rb +9 -5
  36. data/lib/packetgen/header/dot11/sub_mngt.rb +13 -14
  37. data/lib/packetgen/header/dot1q.rb +2 -2
  38. data/lib/packetgen/header/dot1x.rb +3 -4
  39. data/lib/packetgen/header/eap.rb +62 -53
  40. data/lib/packetgen/header/eap/fast.rb +0 -1
  41. data/lib/packetgen/header/eap/md5.rb +1 -2
  42. data/lib/packetgen/header/eap/tls.rb +9 -10
  43. data/lib/packetgen/header/eap/ttls.rb +9 -10
  44. data/lib/packetgen/header/esp.rb +33 -33
  45. data/lib/packetgen/header/eth.rb +11 -8
  46. data/lib/packetgen/header/gre.rb +5 -6
  47. data/lib/packetgen/header/http/headers.rb +11 -14
  48. data/lib/packetgen/header/http/request.rb +20 -20
  49. data/lib/packetgen/header/http/response.rb +16 -18
  50. data/lib/packetgen/header/http/verbs.rb +5 -5
  51. data/lib/packetgen/header/icmp.rb +1 -3
  52. data/lib/packetgen/header/icmpv6.rb +1 -3
  53. data/lib/packetgen/header/igmp.rb +5 -6
  54. data/lib/packetgen/header/igmpv3.rb +5 -9
  55. data/lib/packetgen/header/igmpv3/group_record.rb +4 -5
  56. data/lib/packetgen/header/igmpv3/mq.rb +2 -2
  57. data/lib/packetgen/header/igmpv3/mr.rb +4 -3
  58. data/lib/packetgen/header/ike.rb +33 -8
  59. data/lib/packetgen/header/ike/auth.rb +4 -6
  60. data/lib/packetgen/header/ike/cert.rb +0 -2
  61. data/lib/packetgen/header/ike/certreq.rb +1 -3
  62. data/lib/packetgen/header/ike/id.rb +1 -3
  63. data/lib/packetgen/header/ike/ke.rb +0 -2
  64. data/lib/packetgen/header/ike/nonce.rb +0 -2
  65. data/lib/packetgen/header/ike/notify.rb +22 -24
  66. data/lib/packetgen/header/ike/payload.rb +198 -199
  67. data/lib/packetgen/header/ike/sa.rb +21 -30
  68. data/lib/packetgen/header/ike/sk.rb +16 -17
  69. data/lib/packetgen/header/ike/ts.rb +22 -24
  70. data/lib/packetgen/header/ike/vendor_id.rb +0 -2
  71. data/lib/packetgen/header/ip.rb +30 -23
  72. data/lib/packetgen/header/ip/addr.rb +5 -6
  73. data/lib/packetgen/header/ip/option.rb +11 -15
  74. data/lib/packetgen/header/ip/options.rb +1 -2
  75. data/lib/packetgen/header/ipv6.rb +27 -12
  76. data/lib/packetgen/header/ipv6/addr.rb +2 -2
  77. data/lib/packetgen/header/ipv6/extension.rb +1 -1
  78. data/lib/packetgen/header/ipv6/hop_by_hop.rb +11 -11
  79. data/lib/packetgen/header/llc.rb +4 -3
  80. data/lib/packetgen/header/mdns.rb +11 -5
  81. data/lib/packetgen/header/mld.rb +4 -4
  82. data/lib/packetgen/header/mldv2.rb +4 -3
  83. data/lib/packetgen/header/mldv2/mcast_address_record.rb +3 -4
  84. data/lib/packetgen/header/mldv2/mlq.rb +3 -4
  85. data/lib/packetgen/header/mldv2/mlr.rb +4 -3
  86. data/lib/packetgen/header/netbios.rb +18 -5
  87. data/lib/packetgen/header/ospfv2.rb +6 -7
  88. data/lib/packetgen/header/ospfv2/db_description.rb +3 -4
  89. data/lib/packetgen/header/ospfv2/hello.rb +2 -3
  90. data/lib/packetgen/header/ospfv2/ls_ack.rb +2 -3
  91. data/lib/packetgen/header/ospfv2/ls_request.rb +2 -3
  92. data/lib/packetgen/header/ospfv2/ls_update.rb +5 -6
  93. data/lib/packetgen/header/ospfv2/lsa.rb +13 -14
  94. data/lib/packetgen/header/ospfv2/lsa_header.rb +4 -5
  95. data/lib/packetgen/header/ospfv3.rb +3 -4
  96. data/lib/packetgen/header/ospfv3/db_description.rb +3 -5
  97. data/lib/packetgen/header/ospfv3/hello.rb +2 -3
  98. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +7 -8
  99. data/lib/packetgen/header/ospfv3/ls_ack.rb +2 -3
  100. data/lib/packetgen/header/ospfv3/ls_request.rb +2 -3
  101. data/lib/packetgen/header/ospfv3/ls_update.rb +5 -6
  102. data/lib/packetgen/header/ospfv3/lsa.rb +10 -11
  103. data/lib/packetgen/header/ospfv3/lsa_header.rb +3 -4
  104. data/lib/packetgen/header/snmp.rb +45 -32
  105. data/lib/packetgen/header/tcp.rb +13 -9
  106. data/lib/packetgen/header/tcp/option.rb +16 -11
  107. data/lib/packetgen/header/tcp/options.rb +3 -4
  108. data/lib/packetgen/header/tftp.rb +15 -17
  109. data/lib/packetgen/header/udp.rb +10 -4
  110. data/lib/packetgen/inspect.rb +7 -9
  111. data/lib/packetgen/packet.rb +44 -22
  112. data/lib/packetgen/pcapng.rb +1 -5
  113. data/lib/packetgen/pcapng/block.rb +17 -11
  114. data/lib/packetgen/pcapng/epb.rb +6 -11
  115. data/lib/packetgen/pcapng/file.rb +37 -44
  116. data/lib/packetgen/pcapng/idb.rb +17 -22
  117. data/lib/packetgen/pcapng/shb.rb +7 -10
  118. data/lib/packetgen/pcapng/spb.rb +21 -17
  119. data/lib/packetgen/pcapng/unknown_block.rb +17 -13
  120. data/lib/packetgen/proto.rb +1 -2
  121. data/lib/packetgen/types/array.rb +119 -34
  122. data/lib/packetgen/types/cstring.rb +1 -5
  123. data/lib/packetgen/types/enum.rb +8 -10
  124. data/lib/packetgen/types/fields.rb +34 -28
  125. data/lib/packetgen/types/int.rb +11 -13
  126. data/lib/packetgen/types/int_string.rb +6 -8
  127. data/lib/packetgen/types/oui.rb +3 -6
  128. data/lib/packetgen/types/string.rb +4 -6
  129. data/lib/packetgen/types/tlv.rb +11 -14
  130. data/lib/packetgen/utils.rb +15 -23
  131. data/lib/packetgen/utils/arp_spoofer.rb +12 -18
  132. data/lib/packetgen/version.rb +1 -1
  133. data/packetgen.gemspec +9 -8
  134. metadata +19 -17
@@ -7,7 +7,6 @@
7
7
 
8
8
  module PacketGen
9
9
  module Header
10
-
11
10
  # A TCP header consists of:
12
11
  # * a source port ({#sport}, {Types::Int16} type),
13
12
  # * a destination port ({#dport}, +Int16+ type),
@@ -147,8 +146,8 @@ module PacketGen
147
146
  # @!attribute flags
148
147
  # @return [Integer] 9-bit flags from {#u16}
149
148
  define_bit_fields_on :u16, :data_offset, 4, :reserved, 3, :flags, 9
150
- alias :hlen :data_offset
151
- alias :hlen= :data_offset=
149
+ alias hlen data_offset
150
+ alias hlen= data_offset=
152
151
 
153
152
  # @!attribute flag_ns
154
153
  # @return [Boolean] 1-bit NS flag
@@ -205,9 +204,6 @@ module PacketGen
205
204
  self[:data_offset] = 5 + self[:options].sz / 4
206
205
  end
207
206
 
208
- alias hlen data_offset
209
- alias hlen= data_offset=
210
-
211
207
  # @return [String]
212
208
  def inspect
213
209
  str = Inspect.dashed_line(self.class, 2)
@@ -220,7 +216,7 @@ module PacketGen
220
216
  str << shift + Inspect::FMT_ATTR % ['', 'data_offset', doff]
221
217
  str << shift + Inspect::FMT_ATTR % ['', 'reserved', reserved]
222
218
  flags = ''.dup
223
- %w(ns cwr ece urg ack psh rst syn fin).each do |fl|
219
+ %w[ns cwr ece urg ack psh rst syn fin].each do |fl|
224
220
  flags << (send("flag_#{fl}?") ? fl[0].upcase : '.')
225
221
  end
226
222
  str << shift + Inspect::FMT_ATTR % ['', 'flags', flags]
@@ -228,11 +224,19 @@ module PacketGen
228
224
  end
229
225
  str
230
226
  end
227
+
228
+ # Invert source and destination port numbers
229
+ # @return [self]
230
+ # @since 2.7.0
231
+ def reply!
232
+ self[:sport], self[:dport] = self[:dport], self[:sport]
233
+ self
234
+ end
231
235
  end
232
236
 
233
237
  self.add_class TCP
234
238
 
235
- IP.bind_header TCP, protocol: TCP::IP_PROTOCOL
236
- IPv6.bind_header TCP, next: TCP::IP_PROTOCOL
239
+ IP.bind TCP, protocol: TCP::IP_PROTOCOL
240
+ IPv6.bind TCP, next: TCP::IP_PROTOCOL
237
241
  end
238
242
  end
@@ -8,11 +8,9 @@
8
8
  module PacketGen
9
9
  module Header
10
10
  class TCP
11
-
12
11
  # Base class to describe a TCP option
13
12
  # @author Sylvain Daubert
14
13
  class Option < Base
15
-
16
14
  # EOL option value
17
15
  EOL_KIND = 0
18
16
  # NOP option value
@@ -53,9 +51,9 @@ module PacketGen
53
51
  case options[:value]
54
52
  when Integer
55
53
  klass = case self[:length].to_i
56
- when 3; Types::Int8
57
- when 4; Types::Int16
58
- when 6; Types::Int32
54
+ when 3 then Types::Int8
55
+ when 4 then Types::Int16
56
+ when 6 then Types::Int32
59
57
  else
60
58
  raise ArgumentError, 'impossible length'
61
59
  end
@@ -77,18 +75,25 @@ module PacketGen
77
75
  self[:kind].read str[0, 1]
78
76
  if str[1, 1]
79
77
  self[:length].read str[1, 1]
80
- if str[2, 1] && length > 2
81
- self[:value].read str[2, length - 2]
82
- end
78
+ self[:value].read str[2, length - 2] if str[2, 1] && length > 2
83
79
  end
84
80
  self
85
81
  end
86
82
 
83
+ # Say if given option has a length field.
87
84
  # @return [Boolean]
88
- def has_length?
85
+ # @since 2.7.0
86
+ def length?
89
87
  self[:kind].value && kind >= 2
90
88
  end
91
89
 
90
+ # @deprecated Use {#length?}.
91
+ # @return [Boolean]
92
+ def has_length?
93
+ Deprecation.deprecated(self.class, __method__, 'length?')
94
+ length?
95
+ end
96
+
92
97
  # Getter for value attribute
93
98
  # @return [String, Integer]
94
99
  def value
@@ -113,7 +118,7 @@ module PacketGen
113
118
  # @return [String]
114
119
  def to_human
115
120
  str = self.class == Option ? "unk-#{kind}" : self.class.to_s.sub(/.*::/, '')
116
- if length > 2 and self[:value].to_s.size > 0
121
+ if (length > 2) && !self[:value].to_s.empty?
117
122
  str << ":#{self[:value].to_s.inspect}"
118
123
  end
119
124
  str
@@ -121,7 +126,7 @@ module PacketGen
121
126
 
122
127
  # @return [String]
123
128
  def inspect
124
- str = "#<#{self.class} kind=#{self[:kind].value.inspect} "
129
+ str = +"#<#{self.class} kind=#{self[:kind].value.inspect} "
125
130
  str << "length=#{self[:length].value.inspect} " if self[:length].value
126
131
  str << "value=#{self[:value].inspect}>"
127
132
  end
@@ -8,11 +8,9 @@
8
8
  module PacketGen
9
9
  module Header
10
10
  class TCP
11
-
12
11
  # Container for TCP options in {TCP TCP header}.
13
12
  # @author Sylvain Daubert
14
13
  class Options < Types::Array
15
-
16
14
  # Get {Option} subclasses
17
15
  # @return [Array<Class>]
18
16
  def self.option_classes
@@ -44,7 +42,7 @@ module PacketGen
44
42
  klasses[kind].new
45
43
  end
46
44
  this_option.read str[i, str.size]
47
- unless this_option.has_length?
45
+ unless this_option.length?
48
46
  this_option.length = nil
49
47
  this_option.value = nil
50
48
  end
@@ -61,6 +59,7 @@ module PacketGen
61
59
  # @return [self]
62
60
  # @raise [ArgumentError] unknown option
63
61
  def add(opt, value=nil)
62
+ Deprecation.deprecated(self.class, __method__, 'push')
64
63
  option = record_from_hash(opt: opt, value: value)
65
64
  self << option
66
65
  self
@@ -69,7 +68,7 @@ module PacketGen
69
68
  private
70
69
 
71
70
  def record_from_hash(hsh)
72
- if hsh.has_key? :opt
71
+ if hsh.key? :opt
73
72
  klassname = hsh.delete(:opt)
74
73
  if TCP.const_defined?(klassname)
75
74
  klass = TCP.const_get(klassname)
@@ -7,7 +7,6 @@
7
7
 
8
8
  module PacketGen
9
9
  module Header
10
-
11
10
  # A TFTP (Trivial File Transfer Protocol,
12
11
  # {https://tools.ietf.org/html/rfc1350 RFC 1350}) header consists of:
13
12
  # * a {#opcode} ({Types::Int16Enum}),
@@ -46,8 +45,8 @@ module PacketGen
46
45
  # packets[0].tftp.decode!(packets[1..-1])
47
46
  # packets.map { |pkt| pkt.headers.last.class.to_s }.join(',') # => TFTP::RRQ,TFTP::DATA,UDP,TFTP::ACK
48
47
  # @author Sylvain Daubert
48
+ # @since 2.3.0
49
49
  class TFTP < Base
50
-
51
50
  # Known opcodes
52
51
  OPCODES = {
53
52
  'RRQ' => 1,
@@ -55,22 +54,22 @@ module PacketGen
55
54
  'DATA' => 3,
56
55
  'ACK' => 4,
57
56
  'Error' => 5
58
- }
57
+ }.freeze
59
58
 
60
59
  # @!attribute opcode
61
60
  # 16-bit operation code
62
61
  # @return [Integer]
63
62
  define_field :opcode, Types::Int16Enum, enum: OPCODES
64
-
63
+
65
64
  # @!attribute body
66
65
  # @return [String]
67
66
  define_field :body, Types::String
68
-
67
+
69
68
  def initialize(options={})
70
69
  type = protocol_name.sub(/^.*::/, '')
71
70
  opcode = OPCODES[type]
72
- if self.class != TFTP and !opcode.nil?
73
- super({opcode: opcode}.merge(options))
71
+ if (self.class != TFTP) && !opcode.nil?
72
+ super({ opcode: opcode }.merge(options))
74
73
  else
75
74
  super
76
75
  end
@@ -85,7 +84,7 @@ module PacketGen
85
84
  def read(str)
86
85
  if self.instance_of? TFTP
87
86
  super
88
- if OPCODES.values.include? opcode
87
+ if OPCODES.value? opcode
89
88
  TFTP.const_get(human_opcode).new.read str
90
89
  else
91
90
  self
@@ -94,7 +93,7 @@ module PacketGen
94
93
  old_read str
95
94
  end
96
95
  end
97
-
96
+
98
97
  # Decode subsequent TFTP packets to this one. Packets are modified
99
98
  # in place in +ary+.
100
99
  # @param [Array<Packet>] ary
@@ -104,7 +103,7 @@ module PacketGen
104
103
  server_tid = nil
105
104
  ary.each do |pkt|
106
105
  if server_tid.nil?
107
- next unless pkt.is?('UDP') and pkt.udp.dport == client_tid
106
+ next unless pkt.is?('UDP') && (pkt.udp.dport == client_tid)
108
107
  server_tid = pkt.udp.sport
109
108
  else
110
109
  next unless pkt.is?('UDP')
@@ -120,7 +119,7 @@ module PacketGen
120
119
  end
121
120
  end
122
121
 
123
- # Get human readable opcode
122
+ # Get human readable opcode
124
123
  # @return [String]
125
124
  def human_opcode
126
125
  self[:opcode].to_human
@@ -132,16 +131,15 @@ module PacketGen
132
131
  # @param [Packet] packet
133
132
  # @return [void]
134
133
  def added_to_packet(packet)
135
- unless packet.respond_to? :tftp
136
- packet.instance_eval("def tftp(arg=nil); header(#{self.class}, arg); end")
137
- end
134
+ return if packet.respond_to? :tftp
135
+ packet.instance_eval("def tftp(arg=nil); header(#{self.class}, arg); end")
138
136
  end
139
137
 
140
138
  # TFTP Read Request header
141
139
  class RRQ < TFTP
142
140
  delete_field :body
143
141
  undef body
144
-
142
+
145
143
  # @!attribute filename
146
144
  # Filename to access
147
145
  # @return [String]
@@ -168,7 +166,7 @@ module PacketGen
168
166
  class ACK < TFTP
169
167
  delete_field :body
170
168
  undef body
171
-
169
+
172
170
  # @!attribute block_num
173
171
  # 16-bit block number
174
172
  # @return [Integer]
@@ -192,6 +190,6 @@ module PacketGen
192
190
  alias error_message error_msg
193
191
  end
194
192
  end
195
- UDP.bind_header TFTP, dport: 69
193
+ UDP.bind TFTP, dport: 69
196
194
  end
197
195
  end
@@ -7,7 +7,6 @@
7
7
 
8
8
  module PacketGen
9
9
  module Header
10
-
11
10
  # A UDP header consists of:
12
11
  # * a source port field ({#sport}, {Types::Int16} type),
13
12
  # * a destination port field ({#dport}, +Int16+ type),
@@ -32,7 +31,6 @@ module PacketGen
32
31
  #
33
32
  # @author Sylvain Daubert
34
33
  class UDP < Base
35
-
36
34
  # IP protocol number for UDP
37
35
  IP_PROTOCOL = 17
38
36
 
@@ -84,11 +82,19 @@ module PacketGen
84
82
  def calc_length
85
83
  Base.calculate_and_set_length self
86
84
  end
85
+
86
+ # Invert source and destination port numbers
87
+ # @return [self]
88
+ # @since 2.7.0
89
+ def reply!
90
+ self[:sport], self[:dport] = self[:dport], self[:sport]
91
+ self
92
+ end
87
93
  end
88
94
 
89
95
  self.add_class UDP
90
96
 
91
- IP.bind_header UDP, protocol: UDP::IP_PROTOCOL
92
- IPv6.bind_header UDP, next: UDP::IP_PROTOCOL
97
+ IP.bind UDP, protocol: UDP::IP_PROTOCOL
98
+ IPv6.bind UDP, next: UDP::IP_PROTOCOL
93
99
  end
94
100
  end
@@ -6,12 +6,10 @@
6
6
  # frozen_string_literal: true
7
7
 
8
8
  module PacketGen
9
-
10
9
  # {Inspect} module provides methods to help writing +inspect+
11
10
  # @api private
12
11
  # @author Sylvain Daubert
13
12
  module Inspect
14
-
15
13
  # Maximum number of characters on a line for INSPECT
16
14
  MAX_WIDTH = 70
17
15
 
@@ -53,7 +51,7 @@ module PacketGen
53
51
  str = shift_level(level)
54
52
  val = if value.is_a?(Types::Enum)
55
53
  "%-10s (0x%0#{value.sz * 2}x)" % [value.to_human, value.to_i]
56
- elsif value.is_a?(Types::Int) or value.is_a?(Integer)
54
+ elsif value.is_a?(Types::Int) || value.is_a?(Integer)
57
55
  int_dec_hex(value, value.sz * 2)
58
56
  elsif value.is_a?(String)
59
57
  value.to_s.inspect
@@ -96,16 +94,16 @@ module PacketGen
96
94
  # @param [#to_s] body
97
95
  # @return [String]
98
96
  def self.inspect_body(body, name='Body')
99
- return '' if body.nil? or body.empty?
97
+ return '' if body.nil? || body.empty?
100
98
  str = dashed_line(name, 2)
101
- str << (0..15).to_a.map { |v| " %02d" % v}.join << "\n"
99
+ str << (0..15).to_a.map { |v| ' %02d' % v }.join << "\n"
102
100
  str << '-' * MAX_WIDTH << "\n"
103
- if body.size > 0
101
+ unless body.empty?
104
102
  (body.size / 16 + 1).times do |i|
105
- octets = body.to_s[i*16, 16].unpack('C*')
106
- o_str = octets.map { |v| " %02x" % v}.join
103
+ octets = body.to_s[i * 16, 16].unpack('C*')
104
+ o_str = octets.map { |v| ' %02x' % v }.join
107
105
  str << o_str
108
- str << ' ' * (3*16 - o_str.size) unless o_str.size >= 3*16
106
+ str << ' ' * (3 * 16 - o_str.size) unless o_str.size >= 3 * 16
109
107
  str << ' ' << octets.map { |v| v < 128 && v > 31 ? v.chr : '.' }.join
110
108
  str << "\n"
111
109
  end
@@ -5,10 +5,10 @@
5
5
  # This program is published under MIT license.
6
6
 
7
7
  # frozen_string_literal: true
8
+
8
9
  require 'pcaprub'
9
10
 
10
11
  module PacketGen
11
-
12
12
  # An object of type {Packet} handles a network packet. This packet may contain
13
13
  # multiple protocol headers, starting from MAC layer or from Network (OSI) layer.
14
14
  #
@@ -107,17 +107,15 @@ module PacketGen
107
107
  # @author Sylvain Daubert
108
108
  # @author Kent Gruber
109
109
  def self.read(filename)
110
- begin
111
- PcapNG::File.new.read_packets filename
112
- rescue => e
113
- raise ArgumentError, e unless File.extname(filename.downcase) == '.pcap'
114
- packets = []
115
- PCAPRUB::Pcap.open_offline(filename).each_packet do |packet|
116
- next unless packet = PacketGen.parse(packet.to_s)
117
- packets << packet
118
- end
119
- packets
110
+ PcapNG::File.new.read_packets filename
111
+ rescue StandardError => e
112
+ raise ArgumentError, e unless File.extname(filename.downcase) == '.pcap'
113
+ packets = []
114
+ PCAPRUB::Pcap.open_offline(filename).each_packet do |packet|
115
+ next unless (packet = PacketGen.parse(packet.to_s))
116
+ packets << packet
120
117
  end
118
+ packets
121
119
  end
122
120
 
123
121
  # Write packets to +filename+
@@ -225,7 +223,7 @@ module PacketGen
225
223
  def to_f(filename)
226
224
  PcapNG::File.new.array_to_file(filename: filename, array: [self])
227
225
  end
228
- alias :write :to_f
226
+ alias write to_f
229
227
 
230
228
  # send packet on wire. Use first header +#to_w+ method.
231
229
  # @param [String] iface interface name. Default to first non-loopback interface
@@ -277,9 +275,9 @@ module PacketGen
277
275
  raise FormatError, 'header not in packet!' if idx.nil?
278
276
 
279
277
  prev_header = idx > 0 ? @headers[idx - 1] : nil
280
- next_header = (idx+1) < @headers.size ? @headers[idx + 1] : nil
278
+ next_header = (idx + 1) < @headers.size ? @headers[idx + 1] : nil
281
279
  @headers.delete_at(idx)
282
- if prev_header and next_header
280
+ if prev_header && next_header
283
281
  add_header(next_header, previous_header: prev_header)
284
282
  end
285
283
  end
@@ -325,12 +323,32 @@ module PacketGen
325
323
  to_s == other.to_s
326
324
  end
327
325
 
326
+ # Invert all possible fields in packet to create a reply.
327
+ # @return [self]
328
+ def reply!
329
+ @headers.each do |header|
330
+ header.reply! if header.respond_to?(:reply!)
331
+ end
332
+ self
333
+ end
334
+
335
+ # Forge a new packet from current one with all possible fields
336
+ # inverted. The new packet may be a reply to current one.
337
+ # @return [Packet]
338
+ def reply
339
+ pkt = dup
340
+ pkt.reply!
341
+ end
342
+
328
343
  private
329
344
 
330
345
  # Dup +@headers+ instance variable. Internally used by +#dup+ and +#clone+
331
346
  # @return [void]
332
- def initialize_copy(other)
333
- @headers = @headers.dup
347
+ def initialize_copy(_other)
348
+ @headers = @headers.map(&:dup)
349
+ @headers.each do |header|
350
+ add_magic_header_method header
351
+ end
334
352
  end
335
353
 
336
354
  # @overload header(klass, layer=1)
@@ -386,15 +404,19 @@ module PacketGen
386
404
  raise ArgumentError, msg
387
405
  end
388
406
  end
389
- bindings.set(prev_header) if !bindings.empty? and !parsing
407
+ bindings.set(prev_header) if !bindings.empty? && !parsing
390
408
  prev_header[:body] = header
391
409
  end
392
410
  header.packet = self
393
411
  @headers << header unless previous_header
394
- unless respond_to? header.method_name
395
- self.instance_eval "def #{header.method_name}(arg=nil);" \
396
- "header(#{header.class}, arg); end"
397
- end
412
+
413
+ return if respond_to? header.method_name
414
+ add_magic_header_method header
415
+ end
416
+
417
+ def add_magic_header_method(header)
418
+ self.instance_eval "def #{header.method_name}(arg=nil);" \
419
+ "header(#{header.class}, arg); end"
398
420
  end
399
421
 
400
422
  def guess_first_header(binary_str)
@@ -418,7 +440,7 @@ module PacketGen
418
440
 
419
441
  def decode_bottom_up
420
442
  decode_packet_bottom_up = true
421
- while decode_packet_bottom_up do
443
+ while decode_packet_bottom_up
422
444
  last_known_hdr = @headers.last
423
445
  break unless last_known_hdr.respond_to? :body
424
446
  break if last_known_hdr.body.empty?