packetgen 3.1.1 → 3.1.6

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/bin/pgconsole +1 -0
  3. data/lib/packetgen.rb +33 -4
  4. data/lib/packetgen/capture.rb +51 -28
  5. data/lib/packetgen/config.rb +17 -11
  6. data/lib/packetgen/deprecation.rb +34 -8
  7. data/lib/packetgen/header.rb +2 -9
  8. data/lib/packetgen/header/arp.rb +2 -2
  9. data/lib/packetgen/header/asn1_base.rb +2 -2
  10. data/lib/packetgen/header/base.rb +70 -74
  11. data/lib/packetgen/header/bootp.rb +3 -3
  12. data/lib/packetgen/header/dhcp.rb +2 -2
  13. data/lib/packetgen/header/dhcp/option.rb +2 -2
  14. data/lib/packetgen/header/dhcp/options.rb +2 -2
  15. data/lib/packetgen/header/dhcpv6.rb +12 -12
  16. data/lib/packetgen/header/dhcpv6/duid.rb +11 -5
  17. data/lib/packetgen/header/dhcpv6/option.rb +8 -16
  18. data/lib/packetgen/header/dhcpv6/options.rb +2 -2
  19. data/lib/packetgen/header/dhcpv6/relay.rb +2 -2
  20. data/lib/packetgen/header/dns.rb +9 -9
  21. data/lib/packetgen/header/dns/name.rb +20 -9
  22. data/lib/packetgen/header/dns/opt.rb +2 -2
  23. data/lib/packetgen/header/dns/option.rb +2 -2
  24. data/lib/packetgen/header/dns/qdsection.rb +3 -3
  25. data/lib/packetgen/header/dns/question.rb +37 -35
  26. data/lib/packetgen/header/dns/rr.rb +3 -3
  27. data/lib/packetgen/header/dns/rrsection.rb +2 -2
  28. data/lib/packetgen/header/dot11.rb +30 -51
  29. data/lib/packetgen/header/dot11/control.rb +5 -5
  30. data/lib/packetgen/header/dot11/data.rb +11 -7
  31. data/lib/packetgen/header/dot11/element.rb +16 -16
  32. data/lib/packetgen/header/dot11/management.rb +2 -2
  33. data/lib/packetgen/header/dot11/sub_mngt.rb +2 -12
  34. data/lib/packetgen/header/dot1q.rb +2 -2
  35. data/lib/packetgen/header/dot1x.rb +7 -20
  36. data/lib/packetgen/header/eap.rb +30 -33
  37. data/lib/packetgen/header/eap/fast.rb +2 -2
  38. data/lib/packetgen/header/eap/md5.rb +2 -2
  39. data/lib/packetgen/header/eap/tls.rb +2 -2
  40. data/lib/packetgen/header/eap/ttls.rb +2 -2
  41. data/lib/packetgen/header/eth.rb +13 -11
  42. data/lib/packetgen/header/gre.rb +2 -2
  43. data/lib/packetgen/header/http.rb +2 -0
  44. data/lib/packetgen/header/http/headers.rb +6 -4
  45. data/lib/packetgen/header/http/request.rb +36 -21
  46. data/lib/packetgen/header/http/response.rb +7 -7
  47. data/lib/packetgen/header/http/verbs.rb +3 -3
  48. data/lib/packetgen/header/icmp.rb +2 -2
  49. data/lib/packetgen/header/icmpv6.rb +2 -2
  50. data/lib/packetgen/header/igmp.rb +4 -4
  51. data/lib/packetgen/header/igmpv3.rb +3 -3
  52. data/lib/packetgen/header/igmpv3/group_record.rb +8 -6
  53. data/lib/packetgen/header/igmpv3/mq.rb +2 -2
  54. data/lib/packetgen/header/igmpv3/mr.rb +2 -2
  55. data/lib/packetgen/header/ip.rb +30 -31
  56. data/lib/packetgen/header/ip/addr.rb +10 -3
  57. data/lib/packetgen/header/ip/option.rb +8 -10
  58. data/lib/packetgen/header/ip/options.rb +3 -5
  59. data/lib/packetgen/header/ipv6.rb +2 -2
  60. data/lib/packetgen/header/ipv6/addr.rb +9 -2
  61. data/lib/packetgen/header/ipv6/extension.rb +2 -2
  62. data/lib/packetgen/header/ipv6/hop_by_hop.rb +3 -3
  63. data/lib/packetgen/header/llc.rb +2 -2
  64. data/lib/packetgen/header/mdns.rb +2 -2
  65. data/lib/packetgen/header/mld.rb +2 -2
  66. data/lib/packetgen/header/mldv2.rb +2 -2
  67. data/lib/packetgen/header/mldv2/mcast_address_record.rb +4 -2
  68. data/lib/packetgen/header/mldv2/mlq.rb +2 -2
  69. data/lib/packetgen/header/mldv2/mlr.rb +2 -2
  70. data/lib/packetgen/header/ospfv2.rb +9 -9
  71. data/lib/packetgen/header/ospfv2/db_description.rb +2 -2
  72. data/lib/packetgen/header/ospfv2/hello.rb +2 -2
  73. data/lib/packetgen/header/ospfv2/ls_ack.rb +2 -2
  74. data/lib/packetgen/header/ospfv2/ls_request.rb +4 -2
  75. data/lib/packetgen/header/ospfv2/ls_update.rb +2 -2
  76. data/lib/packetgen/header/ospfv2/lsa.rb +9 -5
  77. data/lib/packetgen/header/ospfv2/lsa_header.rb +8 -7
  78. data/lib/packetgen/header/ospfv3.rb +2 -2
  79. data/lib/packetgen/header/ospfv3/db_description.rb +2 -2
  80. data/lib/packetgen/header/ospfv3/hello.rb +2 -2
  81. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +4 -2
  82. data/lib/packetgen/header/ospfv3/ls_ack.rb +2 -2
  83. data/lib/packetgen/header/ospfv3/ls_request.rb +4 -2
  84. data/lib/packetgen/header/ospfv3/ls_update.rb +2 -2
  85. data/lib/packetgen/header/ospfv3/lsa.rb +5 -5
  86. data/lib/packetgen/header/ospfv3/lsa_header.rb +9 -8
  87. data/lib/packetgen/header/snmp.rb +33 -29
  88. data/lib/packetgen/header/tcp.rb +4 -23
  89. data/lib/packetgen/header/tcp/option.rb +11 -11
  90. data/lib/packetgen/header/tcp/options.rb +2 -2
  91. data/lib/packetgen/header/tftp.rb +6 -6
  92. data/lib/packetgen/header/udp.rb +3 -3
  93. data/lib/packetgen/headerable.rb +5 -4
  94. data/lib/packetgen/inject.rb +23 -0
  95. data/lib/packetgen/inspect.rb +23 -20
  96. data/lib/packetgen/packet.rb +96 -53
  97. data/lib/packetgen/pcap.rb +29 -0
  98. data/lib/packetgen/pcapng.rb +13 -13
  99. data/lib/packetgen/pcapng/block.rb +26 -13
  100. data/lib/packetgen/pcapng/epb.rb +25 -22
  101. data/lib/packetgen/pcapng/file.rb +260 -138
  102. data/lib/packetgen/pcapng/idb.rb +36 -38
  103. data/lib/packetgen/pcapng/shb.rb +51 -53
  104. data/lib/packetgen/pcapng/spb.rb +19 -19
  105. data/lib/packetgen/pcapng/unknown_block.rb +5 -13
  106. data/lib/packetgen/pcaprub_wrapper.rb +81 -0
  107. data/lib/packetgen/proto.rb +2 -2
  108. data/lib/packetgen/types.rb +3 -0
  109. data/lib/packetgen/types/abstract_tlv.rb +28 -8
  110. data/lib/packetgen/types/array.rb +22 -15
  111. data/lib/packetgen/types/cstring.rb +40 -16
  112. data/lib/packetgen/types/enum.rb +8 -3
  113. data/lib/packetgen/types/fieldable.rb +65 -0
  114. data/lib/packetgen/types/fields.rb +182 -117
  115. data/lib/packetgen/types/int.rb +18 -6
  116. data/lib/packetgen/types/int_string.rb +10 -2
  117. data/lib/packetgen/types/length_from.rb +20 -12
  118. data/lib/packetgen/types/oui.rb +4 -2
  119. data/lib/packetgen/types/string.rb +46 -8
  120. data/lib/packetgen/types/tlv.rb +4 -2
  121. data/lib/packetgen/utils.rb +4 -4
  122. data/lib/packetgen/utils/arp_spoofer.rb +2 -2
  123. data/lib/packetgen/version.rb +3 -3
  124. metadata +35 -50
  125. data/.gitignore +0 -13
  126. data/.rubocop.yml +0 -28
  127. data/.travis.yml +0 -17
  128. data/Gemfile +0 -4
  129. data/Rakefile +0 -21
  130. data/packetgen.gemspec +0 -36
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of PacketGen
2
4
  # See https://github.com/sdaubert/packetgen for more informations
3
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen
9
9
  module Header
10
10
  # TCP header ({https://tools.ietf.org/html/rfc793 RFC 793})
@@ -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]
@@ -235,7 +216,7 @@ module PacketGen
235
216
  doff = Inspect.int_dec_hex(data_offset, 1)
236
217
  str << shift << Inspect::FMT_ATTR % ['', 'data_offset', doff]
237
218
  str << shift << Inspect::FMT_ATTR % ['', 'reserved', reserved]
238
- flags = ''.dup
219
+ flags = +''
239
220
  %w[ns cwr ece urg ack psh rst syn fin].each do |fl|
240
221
  flags << (send("flag_#{fl}?") ? fl[0].upcase : '.')
241
222
  end
@@ -1,16 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of PacketGen
2
4
  # See https://github.com/sdaubert/packetgen for more informations
3
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen
9
9
  module Header
10
10
  class TCP
11
11
  # Base class to describe a TCP option
12
12
  # @author Sylvain Daubert
13
13
  class Option < Types::Fields
14
+ include Types::Fieldable
15
+
14
16
  # EOL option value
15
17
  EOL_KIND = 0
16
18
  # NOP option value
@@ -91,15 +93,15 @@ module PacketGen
91
93
  # Setter for value attribute
92
94
  # @param[String,Integer]
93
95
  # @return [String, Integer]
94
- def value=(v)
96
+ def value=(val)
95
97
  case self[:value]
96
98
  when Types::Int
97
99
  self.length = 2 + self[:value].sz
98
- when String
99
- self.length = 2 + Types::String.new.read(v).sz
100
+ when Types::String
101
+ self.length = 2 + Types::String.new.read(val).sz
100
102
  end
101
- self[:value].read v
102
- v
103
+ self[:value].read val
104
+ val
103
105
  end
104
106
 
105
107
  # Get binary string
@@ -112,10 +114,8 @@ module PacketGen
112
114
  # Get option as a human readable string
113
115
  # @return [String]
114
116
  def to_human
115
- str = self.class == Option ? +"unk-#{kind}" : self.class.to_s.sub(/.*::/, '')
116
- if (length > 2) && !self[:value].to_s.empty?
117
- str << ":#{self[:value].to_s.inspect}"
118
- end
117
+ str = self.instance_of?(Option) ? +"unk-#{kind}" : self.class.to_s.sub(/.*::/, '')
118
+ str << ":#{self[:value].to_s.inspect}" if (length > 2) && !self[:value].to_s.empty?
119
119
  str
120
120
  end
121
121
 
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of PacketGen
2
4
  # See https://github.com/sdaubert/packetgen for more informations
3
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  require_relative 'option'
9
9
 
10
10
  module PacketGen
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of PacketGen
2
4
  # See https://github.com/sdaubert/packetgen for more informations
3
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen
9
9
  module Header
10
10
  # A TFTP (Trivial File Transfer Protocol,
@@ -49,10 +49,10 @@ module PacketGen
49
49
  class TFTP < Base
50
50
  # Known opcodes
51
51
  OPCODES = {
52
- 'RRQ' => 1,
53
- 'WRQ' => 2,
54
- 'DATA' => 3,
55
- 'ACK' => 4,
52
+ 'RRQ' => 1,
53
+ 'WRQ' => 2,
54
+ 'DATA' => 3,
55
+ 'ACK' => 4,
56
56
  'Error' => 5
57
57
  }.freeze
58
58
 
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of PacketGen
2
4
  # See https://github.com/sdaubert/packetgen for more informations
3
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen
9
9
  module Header
10
10
  # UDP header ({https://tools.ietf.org/html/rfc768 RFC 768})
@@ -71,7 +71,7 @@ module PacketGen
71
71
  # option is set.
72
72
  def initialize(options={})
73
73
  super
74
- self.length += self[:body].sz if self[:body].sz > 0
74
+ self.length += self[:body].sz if self[:body].sz.positive?
75
75
  end
76
76
 
77
77
  # Compute checksum and set +checksum+ field
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of PacketGen
2
4
  # See https://github.com/sdaubert/packetgen for more informations
3
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen
9
9
  # This mixin module defines minimal API for a class to act as a header
10
10
  # in {Packet}.
@@ -85,9 +85,10 @@ module PacketGen
85
85
  # @return [self]
86
86
  # @raise [NotImplementedError]
87
87
  def read(str)
88
+ # Do not call super and rescue NoMethodError: too slow
89
+ raise NotImplementedError, "#{self.class} should implement #read" if method(:read).super_method.nil?
90
+
88
91
  super
89
- rescue NoMethodError
90
- raise NotImplementedError, "#{self.class} should implement #read"
91
92
  end
92
93
  end
93
94
  end
@@ -0,0 +1,23 @@
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
+ require_relative 'pcaprub_wrapper'
8
+
9
+ module PacketGen
10
+ # Module to inject packets on wire
11
+ # @author Sylvain Daubert
12
+ # @api private
13
+ # @since 3.1.4
14
+ module Inject
15
+ # Inject given data onto wire
16
+ # @param [String] iface interface name
17
+ # @param [String,Packet,Header::Base] data to inject
18
+ # @return [void]
19
+ def self.inject(iface:, data:)
20
+ PCAPRUBWrapper.inject(iface: iface, data: data.to_s)
21
+ end
22
+ end
23
+ end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of PacketGen
2
4
  # See https://github.com/sdaubert/packetgen for more informations
3
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen
9
9
  # {Inspect} module provides methods to help writing +inspect+
10
10
  # @api private
@@ -37,6 +37,25 @@ module PacketGen
37
37
  "%-16s (0x%0#{hexsize}x)" % [value.to_i, value.to_i]
38
38
  end
39
39
 
40
+ # @param [String] str
41
+ # @param [Integer] int
42
+ # @param [Integer] hexsize
43
+ # @return [String]
44
+ def self.enum_human_hex(str, int, hexsize)
45
+ "%-16s (0x%0#{hexsize}x)" % [str, int]
46
+ end
47
+
48
+ # Simple formatter to inspect an attribute
49
+ # @param [String] type attribute type
50
+ # @param [String] attr attribute name
51
+ # @param [String] value
52
+ # @param [Integer] level
53
+ # @return [String]
54
+ def self.format(type, attr, value, level=1)
55
+ str = Inspect.shift_level(level)
56
+ str << Inspect::FMT_ATTR % [type, attr, value]
57
+ end
58
+
40
59
  # Format an attribute for +#inspect+.
41
60
  # 3 cases are handled:
42
61
  # * attribute value is a {Types::Int}: show value as integer and in
@@ -48,24 +67,8 @@ module PacketGen
48
67
  # @param [Integer] level
49
68
  # @return [String]
50
69
  def self.inspect_attribute(attr, value, level=1)
51
- str = shift_level(level)
52
- val = case value
53
- when Types::Enum
54
- "%-16s (0x%0#{value.sz * 2}x)" % [value.to_human, value.to_i]
55
- when Types::Int
56
- int_dec_hex(value, value.sz * 2)
57
- when Integer
58
- int_dec_hex(value, value.sz * 2)
59
- when String
60
- value.to_s.inspect
61
- else
62
- if value.respond_to? :to_human
63
- value.to_human
64
- else
65
- value.to_s.inspect
66
- end
67
- end
68
- str << FMT_ATTR % [value.class.to_s.sub(/.*::/, ''), attr, val]
70
+ type = value.class.to_s.sub(/.*::/, '')
71
+ self.format(type, attr, value.format_inspect, level)
69
72
  end
70
73
 
71
74
  # Format a ASN.1 attribute for +#inspect+.
@@ -1,12 +1,12 @@
1
1
  # coding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  # This file is part of PacketGen
3
5
  # See https://github.com/sdaubert/packetgen for more informations
4
6
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
5
7
  # This program is published under MIT license.
6
8
 
7
- # frozen_string_literal: true
8
-
9
- require 'pcaprub'
9
+ # rubocop:disable Metrics/ClassLength
10
10
 
11
11
  module PacketGen
12
12
  # An object of type {Packet} handles a network packet. This packet may contain
@@ -79,36 +79,31 @@ module PacketGen
79
79
  # @yieldparam [Packet,String] packet if a block is given, yield each
80
80
  # captured packet (Packet or raw data String, depending on +:parse+ option)
81
81
  # @return [Array<Packet>] captured packet
82
- def self.capture(**kwargs)
83
- capture = Capture.new(kwargs)
82
+ def self.capture(**kwargs, &block)
83
+ capture = Capture.new(**kwargs)
84
84
  if block_given?
85
- capture.start { |packet| yield packet }
85
+ capture.start(&block)
86
86
  else
87
87
  capture.start
88
88
  end
89
89
  capture.packets
90
90
  end
91
91
 
92
- # Read packets from +filename+. Mays read Pcap and Pcap-NG formats.
92
+ # Read packets from +filename+. May read Pcap and Pcap-NG formats.
93
93
  #
94
- # For more control, see {PcapNG::File} or +PCAPRUB::Pcap+.
94
+ # For more control (on Pcap-ng only), see {PcapNG::File}.
95
95
  # @param [String] filename PcapNG or Pcap file.
96
96
  # @return [Array<Packet>]
97
+ # @raise [ArgumentError] unknown file format
97
98
  # @author Sylvain Daubert
98
99
  # @author Kent Gruber - Pcap format
99
100
  # @since 2.0.0 Also read Pcap format.
100
101
  def self.read(filename)
101
- PcapNG::File.new.read_packets filename
102
+ PcapNG::File.new.read_packets(filename)
102
103
  rescue StandardError => e
103
104
  raise ArgumentError, e unless File.extname(filename.downcase) == '.pcap'
104
105
 
105
- packets = []
106
- PCAPRUB::Pcap.open_offline(filename).each_packet do |packet|
107
- next unless (packet = PacketGen.parse(packet.to_s))
108
-
109
- packets << packet
110
- end
111
- packets
106
+ Pcap.read(filename)
112
107
  end
113
108
 
114
109
  # Write packets to +filename+
@@ -132,11 +127,13 @@ module PacketGen
132
127
  # @param [String] protocol
133
128
  # @param [Hash] options protocol specific options
134
129
  # @return [self]
135
- # @raise [ArgumentError] unknown protocol
130
+ # @raise [BindingError] unknown protocol
136
131
  def add(protocol, options={})
137
132
  klass = check_protocol(protocol)
138
133
 
139
- header = klass.new(options.merge!(packet: self))
134
+ # options[:packet]= self is speedier than options.merge(packet: self)
135
+ options[:packet] = self
136
+ header = klass.new(options)
140
137
  add_header header
141
138
  self
142
139
  end
@@ -146,12 +143,14 @@ module PacketGen
146
143
  # @param [String] protocol protocol to insert
147
144
  # @param [Hash] options protocol specific options
148
145
  # @return [self]
149
- # @raise [ArgumentError] unknown protocol
146
+ # @raise [BindingError] unknown protocol
150
147
  def insert(prev, protocol, options={})
151
148
  klass = check_protocol(protocol)
152
149
 
153
150
  nxt = prev.body
154
- header = klass.new(options.merge!(packet: self))
151
+ # options[:packet]= self is speedier than options.merge(packet: self)
152
+ options[:packet] = self
153
+ header = klass.new(options)
155
154
  add_header header, previous_header: prev
156
155
  idx = headers.index(prev) + 1
157
156
  headers[idx, 0] = header
@@ -217,7 +216,7 @@ module PacketGen
217
216
  # @return [Array] see return from {PcapNG::File#to_file}
218
217
  # @see File
219
218
  def to_f(filename)
220
- PcapNG::File.new.array_to_file(filename: filename, array: [self])
219
+ PcapNG::File.new.array_to_file(file: filename, array: [self])
221
220
  end
222
221
  alias write to_f
223
222
 
@@ -251,10 +250,11 @@ module PacketGen
251
250
  # from binding with first other's one. Use only when current header field
252
251
  # has its value set accordingly.
253
252
  # @return [self] +self+ with new headers from +other+
253
+ # @raise [BindingError] do not known how to encapsulate
254
254
  # @since 1.1.0
255
255
  def encapsulate(other, parsing: false)
256
256
  other.headers.each_with_index do |h, i|
257
- add_header h, parsing: (i > 0) || parsing
257
+ add_header h, parsing: i.positive? || parsing
258
258
  end
259
259
  end
260
260
 
@@ -262,36 +262,32 @@ module PacketGen
262
262
  # @param [Array<Header>] hdrs
263
263
  # @return [self] +self+ with some headers removed
264
264
  # @raise [FormatError] any headers not in +self+
265
- # @raise [FormatError] removed headers result in an unknown binding
265
+ # @raise [BindingError] removed headers result in an unknown binding
266
266
  # @since 1.1.0
267
267
  def decapsulate(*hdrs)
268
268
  hdrs.each do |hdr|
269
- idx = headers.index(hdr)
270
- raise FormatError, 'header not in packet!' if idx.nil?
271
-
272
- prev_hdr = idx > 0 ? headers[idx - 1] : nil
273
- next_hdr = (idx + 1) < headers.size ? headers[idx + 1] : nil
274
- headers.delete_at(idx)
269
+ prev_hdr = previous_header(hdr)
270
+ next_hdr = next_header(hdr)
271
+ headers.delete(hdr)
275
272
  add_header(next_hdr, previous_header: prev_hdr) if prev_hdr && next_hdr
276
273
  end
277
- rescue ArgumentError => ex
278
- raise FormatError, ex.message
274
+ rescue ArgumentError => e
275
+ raise FormatError, e.message
279
276
  end
280
277
 
281
278
  # Parse a binary string and populate Packet from it.
282
279
  # @param [String] binary_str
283
280
  # @param [String,nil] first_header First protocol header. +nil+ means discover it!
284
281
  # @return [Packet] self
285
- # @raise [ArgumentError] +first_header+ is an unknown header
282
+ # @raise [ParseError] +first_header+ is an unknown header
283
+ # @raise [BindingError] unknwon binding between some headers
286
284
  def parse(binary_str, first_header: nil)
287
285
  headers.clear
288
286
 
289
287
  if first_header.nil?
290
288
  # No decoding forced for first header. Have to guess it!
291
289
  first_header = guess_first_header(binary_str)
292
- if first_header.nil?
293
- raise ParseError, 'cannot identify first header in string'
294
- end
290
+ raise ParseError, 'cannot identify first header in string' if first_header.nil?
295
291
  end
296
292
 
297
293
  add first_header
@@ -318,6 +314,20 @@ module PacketGen
318
314
  to_s == other.to_s
319
315
  end
320
316
 
317
+ # @param [Packet] other
318
+ # @return [Boolean]
319
+ # @since 3.1.2
320
+ def ===(other)
321
+ case other
322
+ when PacketGen::Packet
323
+ self == other
324
+ when String
325
+ is? other
326
+ else
327
+ false
328
+ end
329
+ end
330
+
321
331
  # Invert all possible fields in packet to create a reply.
322
332
  # @return [self]
323
333
  # @since 2.7.0
@@ -360,6 +370,35 @@ module PacketGen
360
370
  headers.last
361
371
  end
362
372
 
373
+ # Give header index in packet
374
+ # @param [Headerable] hdr
375
+ # @return [Integer]
376
+ # @raise [FormatError] +hdr+ not in packet
377
+ def header_index(hdr)
378
+ idx = headers.index(hdr)
379
+ raise FormatError, 'header not in packet!' if idx.nil?
380
+
381
+ idx
382
+ end
383
+
384
+ # Give header previous +hdr+ in packet
385
+ # @param [Headerable] hdr
386
+ # @return [Headerable,nil] May return +nil+ if +hdr+ is the first header in packet
387
+ # @raise [FormatError] +hdr+ not in packet
388
+ def previous_header(hdr)
389
+ idx = header_index(hdr)
390
+ idx.positive? ? headers[idx - 1] : nil
391
+ end
392
+
393
+ # Give header next +hdr+ in packet
394
+ # @param [Headerable] hdr
395
+ # @return [Headerable,nil] May return +nil+ if +hdr+ is the last header in packet
396
+ # @raise [FormatError] +hdr+ not in packet
397
+ def next_header(hdr)
398
+ idx = header_index(hdr)
399
+ (idx + 1) < headers.size ? headers[idx + 1] : nil
400
+ end
401
+
363
402
  # @overload header(klass, layer=1)
364
403
  # @param [Class] klass
365
404
  # @param [Integer] layer
@@ -374,9 +413,8 @@ module PacketGen
374
413
  return header unless arg.is_a? Hash
375
414
 
376
415
  arg.each do |key, value|
377
- unless header.respond_to? "#{key}="
378
- raise ArgumentError, "unknown #{key} attribute for #{klass}"
379
- end
416
+ raise ArgumentError, "unknown #{key} attribute for #{klass}" unless header.respond_to? "#{key}="
417
+
380
418
  header.send "#{key}=", value
381
419
  end
382
420
 
@@ -394,26 +432,15 @@ module PacketGen
394
432
  end
395
433
 
396
434
  # Add a header to packet
397
- # @param [Header::Base] header
398
- # @param [Header::Base] previous_header
435
+ # @param [Headerable] header
436
+ # @param [Headerable] previous_header
399
437
  # @param [Boolean] parsing
400
438
  # @return [void]
439
+ # @raise [BindingError]
401
440
  def add_header(header, previous_header: nil, parsing: false)
402
441
  prev_header = previous_header || last_header
403
- if prev_header
404
- bindings = prev_header.class.known_headers[header.class]
405
- bindings = prev_header.class.known_headers[header.class.superclass] if bindings.nil?
406
- if bindings.nil?
407
- msg = "#{prev_header.class} knowns no layer association with #{header.protocol_name}. ".dup
408
- msg << "Try #{prev_header.class}.bind_layer(#{header.class}, "
409
- msg << "#{prev_header.method_name}_proto_field: "
410
- msg << "value_for_#{header.method_name})"
411
- raise ArgumentError, msg
412
- end
442
+ add_to_previous_header(prev_header, header, parsing) if prev_header
413
443
 
414
- bindings.set(prev_header) if !bindings.empty? && !parsing
415
- prev_header[:body] = header
416
- end
417
444
  header.packet = self
418
445
  headers << header unless previous_header
419
446
 
@@ -422,6 +449,20 @@ module PacketGen
422
449
  add_magic_header_method header
423
450
  end
424
451
 
452
+ # Bind +header+ to +prev_header+.
453
+ # @param [Headerable] prev_header
454
+ # @param [Headerable] header
455
+ # @param [Boolean] parsing
456
+ # @return [void]
457
+ # @raise [BindingError]
458
+ def add_to_previous_header(prev_header, header, parsing)
459
+ bindings = prev_header.class.known_headers[header.class] || prev_header.class.known_headers[header.class.superclass]
460
+ raise BindingError.new(prev_header, header) if bindings.nil?
461
+
462
+ bindings.set(prev_header) if !bindings.empty? && !parsing
463
+ prev_header[:body] = header
464
+ end
465
+
425
466
  # Add method to access +header+
426
467
  # @param [Header::Base] header
427
468
  # @return [void]
@@ -483,5 +524,7 @@ module PacketGen
483
524
  end
484
525
  end
485
526
 
527
+ # rubocop:enable Metrics/ClassLength
528
+
486
529
  require_relative 'headerable'
487
530
  require_relative 'header'