packetgen 2.1.2 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 59b1fc19fea5c7400e6d70e01a83ca68351e646b
4
- data.tar.gz: b9ef7b691f66d23d763e7cf03f98aab5314924b5
3
+ metadata.gz: e306dbee54d246dd2044db6610ba27e3fb539215
4
+ data.tar.gz: 1103a3a6429fb2b4c8391750cdefa8c7d87ac323
5
5
  SHA512:
6
- metadata.gz: bddf2bf621ae0ddd6e30d4f1ee25f24b575d4b42b994203baa135cc58d0fac3ecce8c1549966faa381a8049c0585cd8517dc358c0e2399dc0afab1778121ef77
7
- data.tar.gz: a02f287f900e5f7d21e845fd26c86b146375a87babc61252f4c73cf63c306ff403f0fef8220594d4b8e0d17a9f5f71bb6f2dbef7e61e49c1cc1472aed2180d63
6
+ metadata.gz: 592a69c18ea009c84374aa79125134ee136f0ed9050aefb8610ff484447f9cddc66685fcc3449733d2f6a1160de403d51a4465689029090815db45cdf292e12f
7
+ data.tar.gz: cf57d3f41ea4eaa30964086ec2a1127a0f2aae14a291b2fc7476819484aaa0a14a5c0681bda1f68ad7ba4aa84edc6196cb7684054ea94c22c91a5626eb2ac533
data/.gitignore CHANGED
@@ -9,4 +9,4 @@
9
9
  /tmp/
10
10
  /vendor/
11
11
  *~
12
-
12
+ tags
data/README.md CHANGED
@@ -41,6 +41,11 @@ PacketGen.gen('Eth', src: '00:00:00:00:00:01', dst: '00:00:00:00:00:02').to_w
41
41
  PacketGen.gen('IP', src: '192.168.1.1', dst: '192.168.1.2').to_w
42
42
  # send forged IP packet over Ethernet
43
43
  PacketGen.gen('Eth', src: '00:00:00:00:00:01', dst: '00:00:00:00:00:02').add('IP').to_w('eth1')
44
+ # send a IEEE 802.11 frame
45
+ PacketGen.gen('RadioTap').
46
+ add('Dot11::Management', mac1: client, mac2: bssid, mac3: bssid).
47
+ add('Dot11::DeAuth', reason: 7).
48
+ to_w('wlan0')
44
49
  ```
45
50
 
46
51
  ### Parse packets from binary data
@@ -148,9 +153,11 @@ classes. A special `config` object gives local network configuration:
148
153
 
149
154
  If `pry` gem is installed, it is used as backend for `pgconsole`, else IRB is used.
150
155
 
151
- ## API documentation
156
+ ## See also
152
157
 
153
- see http://www.rubydoc.info/gems/packetgen
158
+ Wiki: https://github.com/sdaubert/packetgen/wiki
159
+
160
+ API documentation: http://www.rubydoc.info/gems/packetgen
154
161
 
155
162
  ## Pull requests?
156
163
 
@@ -165,4 +172,4 @@ Copyright © 2016 Sylvain Daubert
165
172
  All original code maintains its copyright from its original authors and licensing.
166
173
 
167
174
  This is mainly for PcapNG (originally copied from [PacketFu](https://github.com/packetfu/packetfu),
168
- but i am the original author).
175
+ but i am the original author.
data/bin/pgconsole CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'packetgen'
4
4
  require 'packetgen/config'
5
+ require 'packetgen/utils'
5
6
 
6
7
  use_pry = false
7
8
  begin
@@ -13,28 +14,25 @@ rescue LoadError
13
14
  require 'irb/completion'
14
15
  end
15
16
 
16
- include PacketGen
17
+ class PgConsole
18
+ include PacketGen
19
+ include Utils
17
20
 
18
- def parse(binary_str, first_header: nil)
19
- Packet.parse binary_str, first_header: first_header
20
- end
21
-
22
- def capture(options={})
23
- Packet.capture(options) { |packet| yield packet if block_given? }
24
- end
25
-
26
- def read(filename)
27
- Packet.read filename
28
- end
29
-
30
- def write(filename, packets)
31
- Packet.write filename, packets
32
- end
21
+ def config
22
+ PacketGen::Config.instance
23
+ end
33
24
 
34
- @config = PacketGen::Config.new
25
+ PacketGen.singleton_methods.each do |m|
26
+ define_method m, PacketGen.method(m).to_proc
27
+ end
35
28
 
36
- def config
37
- @config
29
+ Utils.singleton_methods.each do |m|
30
+ define_method m, Utils.method(m).to_proc
31
+ end
32
+
33
+ def get_binding
34
+ binding
35
+ end
38
36
  end
39
37
 
40
38
  def start_message(use_pry)
@@ -47,21 +45,21 @@ if use_pry
47
45
  Pry.config.prompt = [
48
46
  proc { |target_self, nest_level, pry|
49
47
  "#{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}> "
50
- },
51
- proc { |target_self, nest_level, pry|
52
- "#{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}* "
48
+ },
49
+ proc { |target_self, nest_level, pry|
50
+ "#{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}* "
53
51
  }
54
52
  ]
55
53
  Pry.config.prompt_name = 'pg'
56
- Pry.start
54
+ PgConsole.new.get_binding.pry
57
55
  else
58
56
  IRB.setup nil
59
- irb = IRB::Irb.new(IRB::WorkSpace.new(binding))
57
+ irb = IRB::Irb.new(IRB::WorkSpace.new(PgConsole.new.get_binding))
60
58
  IRB.conf[:MAIN_CONTEXT] = irb.context
61
59
  irb.context.auto_indent_mode = true
62
- irb.context.prompt_i = "pg(%m)>"
63
- irb.context.prompt_s = "pg(%m)*"
64
- irb.context.prompt_c = "pg(%m)*"
60
+ irb.context.prompt_i = "pg> "
61
+ irb.context.prompt_s = "pg* "
62
+ irb.context.prompt_c = "pg* "
65
63
 
66
64
  trap("SIGINT") do
67
65
  irb.signal_handle
@@ -75,4 +73,3 @@ else
75
73
  IRB.irb_at_exit
76
74
  end
77
75
  end
78
-
@@ -3,52 +3,68 @@
3
3
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
4
  # This program is published under MIT license.
5
5
 
6
- require 'network_interface'
7
6
  require 'socket'
7
+ require 'singleton'
8
+ require 'network_interface'
8
9
 
9
10
  module PacketGen
10
11
 
11
12
  # Config class to provide +config+ object to pgconsole
12
13
  # @author Sylvain Daubert
13
14
  # @author Kent 'picat' Gruber
15
+ # @since 2.1.3 Config is singleton
14
16
  class Config
17
+ include Singleton
15
18
 
16
19
  # Default network interface
17
20
  # @return [String]
18
- attr_reader :iface
19
- # MAC address of default interface
20
- # @return [String]
21
- attr_reader :hwaddr
22
- # IP address of default interface
23
- # @return [String]
24
- attr_reader :ipaddr
25
- # IPv6 address of default interface
26
- # @return [String]
27
- attr_reader :ip6addr
21
+ attr_reader :default_iface
22
+
23
+ def initialize
24
+ begin
25
+ default_iface = Pcap.lookupdev
26
+ rescue PCAPRUB::BindingError
27
+ default_iface = NetworkInterface.interfaces.select { |i| i =~ /lo/ }.first
28
+ end
29
+ @default_iface = default_iface
30
+ @hwaddr = {}
31
+ @ipaddr = {}
32
+ @ip6addr = {}
28
33
 
29
- # Create a configuration object. If +iface+ is not set,
30
- # attempt to find it automatically or default to the
31
- # first available loopback interface.
32
- # @param [String,nil] iface
33
- def initialize(iface=nil)
34
- if iface.nil?
35
- begin
36
- iface = Pcap.lookupdev
37
- rescue PCAPRUB::BindingError
38
- iface = NetworkInterface.interfaces.select { |iface| iface =~ /lo/ }.first
34
+ NetworkInterface.interfaces.each do |iface|
35
+ addresses = NetworkInterface.addresses(iface)
36
+ @hwaddr[iface] = case RbConfig::CONFIG['target_os']
37
+ when /darwin/
38
+ addresses[Socket::AF_LINK][0]['addr'] if addresses[Socket::AF_LINK]
39
+ else
40
+ addresses[Socket::AF_PACKET][0]['addr'] if addresses[Socket::AF_PACKET]
41
+ end
42
+ @ipaddr[iface] = addresses[Socket::AF_INET][0]['addr'] if addresses[Socket::AF_INET]
43
+ if addresses[Socket::AF_INET6]
44
+ @ip6addr[iface] = addresses[Socket::AF_INET6].map { |hsh| hsh['addr'] }
39
45
  end
40
46
  end
41
- @iface = iface
47
+ end
42
48
 
43
- addresses = NetworkInterface.addresses(iface)
44
- @hwaddr = case RbConfig::CONFIG['target_os']
45
- when /darwin/
46
- addresses[Socket::AF_LINK][0]['addr'] if addresses[Socket::AF_LINK]
47
- else
48
- addresses[Socket::AF_PACKET][0]['addr'] if addresses[Socket::AF_PACKET]
49
- end
50
- @ipaddr = addresses[Socket::AF_INET][0]['addr'] if addresses[Socket::AF_INET]
51
- @ip6addr = addresses[Socket::AF_INET6][0]['addr'] if addresses[Socket::AF_INET6]
49
+ # Get MAC address for given network interface
50
+ # @param [String,nil] iface network interface. Il +nil+, use default one.
51
+ # @return [String]
52
+ def hwaddr(iface=nil)
53
+ @hwaddr[iface || @default_iface]
54
+ end
55
+
56
+ # Get IP address for given network interface
57
+ # @param [String,nil] iface network interface. Il +nil+, use default one.
58
+ # @return [String]
59
+ def ipaddr(iface=nil)
60
+ @ipaddr[iface || @default_iface]
61
+ end
62
+
63
+ # Get IPv6 addresses for given network interface
64
+ # @param [String,nil] iface network interface. Il +nil+, use default one.
65
+ # @return [Array<String>]
66
+ def ip6addr(iface=nil)
67
+ @ip6addr[iface || @default_iface]
52
68
  end
53
69
  end
54
70
  end
@@ -48,7 +48,7 @@ module PacketGen
48
48
  # @!attribute op
49
49
  # 16-bit operation code
50
50
  # # @return [Integer]
51
- define_field :op, Types::Int16, default: 1
51
+ define_field :op, Types::Int16Enum, enum: { 'request' => 1, 'reply' => 2 }
52
52
  # @!attribute sha
53
53
  # source hardware address
54
54
  # @return [Eth::MacAddr]
@@ -119,4 +119,3 @@ module PacketGen
119
119
  Dot1q.bind_header ARP, ethertype: 0x806
120
120
  end
121
121
  end
122
-
@@ -8,6 +8,8 @@ module PacketGen
8
8
  module Header
9
9
 
10
10
  # @abstract Base class for ASN.1 header types.
11
+ # This class implement minimal {Base} API to mimic a {Base} object.
12
+ #
11
13
  # Subclasses may define magic methods:
12
14
  # * {#parse?}.
13
15
  # @author Sylvain Daubert
@@ -56,6 +58,8 @@ module PacketGen
56
58
  @method_name = protocol_name.downcase.sub(/::/, '_')
57
59
  end
58
60
 
61
+ # Called by {Packet#parse} when guessing first header to check if header
62
+ # is correct
59
63
  # @return [true]
60
64
  def parse?
61
65
  true
@@ -6,19 +6,6 @@ module PacketGen
6
6
  # @author Sylvain Daubert
7
7
  class Question < Base
8
8
 
9
- # @!attribute name
10
- # Question domain name
11
- # @return [String]
12
- define_field :name, Name, default: '.'
13
- # @!attribute type
14
- # 16-bit question type
15
- # @return [Integer]
16
- define_field :type, Types::Int16, default: 1
17
- # @!attribute rrclass
18
- # 16-bit question class
19
- # @return [Integer]
20
- define_field :rrclass, Types::Int16, default: 1
21
-
22
9
  # Ressource Record types
23
10
  TYPES = {
24
11
  'A' => 1,
@@ -49,7 +36,7 @@ module PacketGen
49
36
  'TKEY' => 249,
50
37
  'TSIG' => 250,
51
38
  '*' => 255
52
- }
39
+ }.freeze
53
40
 
54
41
  # Ressource Record classes
55
42
  CLASSES = {
@@ -58,7 +45,20 @@ module PacketGen
58
45
  'HS' => 4,
59
46
  'NONE' => 254,
60
47
  '*' => 255
61
- }
48
+ }.freeze
49
+
50
+ # @!attribute name
51
+ # Question domain name
52
+ # @return [String]
53
+ define_field :name, Name, default: '.'
54
+ # @!attribute type
55
+ # 16-bit question type
56
+ # @return [Integer]
57
+ define_field :type, Types::Int16Enum, default: 1, enum: TYPES
58
+ # @!attribute rrclass
59
+ # 16-bit question class
60
+ # @return [Integer]
61
+ define_field :rrclass, Types::Int16Enum, default: 1, enum: CLASSES
62
62
 
63
63
  # @param [DNS] dns
64
64
  # @param [Hash] options
@@ -72,20 +72,6 @@ module PacketGen
72
72
  self.rrclass = options[:rrclass] if options[:rrclass]
73
73
  end
74
74
 
75
- # Setter for type
76
- # @param [Integer] val
77
- # @return [Integer,String]
78
- def type=(val)
79
- v = case val
80
- when String
81
- self.class::TYPES[val.upcase]
82
- else
83
- val
84
- end
85
- raise ArgumentError, "unknown type #{val.inspect}" unless v
86
- self[:type].read v
87
- end
88
-
89
75
  # Setter for class
90
76
  # @param [Integer] val
91
77
  # @return [Integer,String]
@@ -109,7 +95,7 @@ module PacketGen
109
95
 
110
96
  # Get human readable type
111
97
  # @return [String]
112
- def human_type
98
+ def human_type
113
99
  self.class::TYPES.key(type) || "0x%04x" % type
114
100
  end
115
101
 
@@ -50,7 +50,14 @@ module PacketGen
50
50
  # Check version field
51
51
  # @see [Base#parse?]
52
52
  def parse?
53
- version == 0
53
+ version == 0 and length >= 8
54
+ end
55
+
56
+ # Calculate length field
57
+ # @return [Integer] calculated length
58
+ # @since 2.1.3
59
+ def calc_length
60
+ self[:length].value = self.sz - self[:body].sz
54
61
  end
55
62
 
56
63
  # send PPI packet on wire. Dot11 FCS trailer should be set.
@@ -64,7 +71,7 @@ module PacketGen
64
71
  end
65
72
  self.add_class PPI
66
73
 
67
- # Radiotap header
74
+ # Radiotap header (see http://www.radiotap.org/)
68
75
  # @author Sylvain Daubert
69
76
  class RadioTap < Base
70
77
  # @!attribute version
@@ -75,7 +82,7 @@ module PacketGen
75
82
  define_field :pad, Types::Int8, default: 0
76
83
  # @!attribute length
77
84
  # @return [Integer] 16-bit RadioTap header length
78
- define_field :length, Types::Int16le
85
+ define_field :length, Types::Int16le, default: 8
79
86
  # @!attribute present_flags
80
87
  # @return [Integer] 32-bit integer
81
88
  define_field :present_flags, Types::Int32le
@@ -103,7 +110,14 @@ module PacketGen
103
110
  # Check version field
104
111
  # @see [Base#parse?]
105
112
  def parse?
106
- version == 0
113
+ version == 0 and length >= 8
114
+ end
115
+
116
+ # Calculate length field
117
+ # @return [Integer] calculated length
118
+ # @since 2.1.3
119
+ def calc_length
120
+ self[:length].value = self.sz - self[:body].sz
107
121
  end
108
122
 
109
123
  # send RadioTap packet on wire. Dot11 FCS trailer should be set.
@@ -120,7 +134,7 @@ module PacketGen
120
134
  # IEEE 802.11 header
121
135
  # @abstract This is a base class to demultiplex different IEEE 802.11 frames when
122
136
  # parsing.
123
- # A IEEE 802.11 header may consists of at least:
137
+ # A IEEE 802.11 header may consist of at least:
124
138
  # * a {#frame_ctrl} ({Types::Int16}),
125
139
  # * a {#id}/duration ({Types::Int16le}),
126
140
  # * and a {#mac1} ({Eth::MacAddr}).
@@ -134,10 +148,52 @@ module PacketGen
134
148
  # * a {#body} (a {Types::String} or another {Base} class),
135
149
  # * a Frame check sequence ({#fcs}, of type {Types::Int32le})
136
150
  #
137
- # == header accessors
151
+ # == Header accessors
138
152
  # As Dot11 header types are defined under Dot11 namespace, Dot11 header accessors
139
153
  # have a specific name. By example, to access to a {Dot11::Beacon} header,
140
154
  # accessor is +#dot11_beacon+.
155
+ #
156
+ # == Create Dot11 packets
157
+ # As {Dot11} is an abstract class, you have to use one of its subclasses to
158
+ # instanciate a IEEE802.11 header.
159
+ #
160
+ # === IEEE802.11 control frames
161
+ # Control frames may be created this way:
162
+ # pkt = PacketGen.gen('Dot11::Control', subtype: 13) # Ack control frame
163
+ # pkt.dot11_control # => PacketGen::Header::Dot11::Control
164
+ #
165
+ # === IEEE802.11 management frames
166
+ # Management frames may be created this way:
167
+ # pkt = PacketGen.gen('Dot11::Management')
168
+ # pkt.dot11_management # => PacketGen::Header::Dot11::Management
169
+ # Management frames are usually specialized, AssociationRequest by example:
170
+ # pkt.add('Dot11::AssoReq')
171
+ # pkt.dot11_assoreq # => PacketGen::Header::Dot11::AssoReq
172
+ # Management frames also may contain some elements (see IEEE 802.11 standard):
173
+ # pkt.dot11_assoreq.add_elements(type: 'SSID', value: "My SSID")
174
+ # pkt.dot11_assoreq.add_elements(type: 'Rates', value: supported_rates)
175
+ #
176
+ # === IEEE802.11 data frames
177
+ # Data frames may be created this way:
178
+ # pkt = PacketGen.gen('Dot11::Data')
179
+ # pkt.dot11_data # => PacketGen::Header::Dot11::Data
180
+ #
181
+ # == Parse Dot11 packets
182
+ # When parsing a Dot11 packet, Dot11 subclass is created from +type+ value.
183
+ # Dot11 header should then be accessed through +Packet#dot11_management+,
184
+ # +Packet#dot11_control+ or +Packet#dot11_data+.
185
+ #
186
+ # A +Packet#dot11+ may exist for unknown types.
187
+ #
188
+ # == Send Dot11 packets
189
+ # To send a Dot11 packet, a RadioTap header is needed:
190
+ # pkt = PacketGen.gen('RadioTap')
191
+ # pkt.add('Dot11::Management', mac1: client, mac2: bssid, mac3: bssid)
192
+ # pkt.add('Dot11::Beacon')
193
+ # pkt.dot11_beacon.add_element(type: 'SSID', value: 'My SSID')
194
+ # pkt.dot11_beacon.add_element(type: 'Rates', value: "\x85\x0c")
195
+ # pkt.calc
196
+ # pkt.to_w('wlan0')
141
197
  # @author Sylvain Daubert
142
198
  class Dot11 < Base
143
199
 
@@ -211,6 +267,15 @@ module PacketGen
211
267
  define_bit_fields_on :frame_ctrl, :subtype, 4, :type, 2, :proto_version, 2,
212
268
  :order, :wep, :md, :pwmngt, :retry, :mf, :from_ds, :to_ds
213
269
 
270
+
271
+ # @!attribute sequence_number (12-bit field from {#sequence_ctrl})
272
+ # @return [Integer]
273
+ # @since 2.1.3
274
+ # @!attribute fragment_number (4-bit field from {#sequence_ctrl})
275
+ # @return [Integer]
276
+ # @since 2.1.3
277
+ define_bit_fields_on :sequence_ctrl, :sequence_number, 12, :fragment_number, 4
278
+
214
279
  alias duration id
215
280
  # @private
216
281
  alias old_fields fields
@@ -302,7 +367,7 @@ module PacketGen
302
367
  pcap = PCAPRUB::Pcap.open_live(iface, PCAP_SNAPLEN, PCAP_PROMISC,
303
368
  PCAP_TIMEOUT)
304
369
  str = self.to_s
305
- pcap.inject str << [crc32].pack('V')
370
+ pcap.inject str
306
371
  end
307
372
 
308
373
  private
@@ -317,7 +382,7 @@ module PacketGen
317
382
  unless @applicable_fields.include? :ht_ctrl
318
383
  idx = @applicable_fields.index(:body)
319
384
  @applicable_fields[idx, 0] = :ht_ctrl
320
- end
385
+ end
321
386
  else
322
387
  @applicable_fields -= %i(ht_ctrl)
323
388
  end