packetfu 1.0.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 (52) hide show
  1. data/.document +4 -0
  2. data/CHANGES +36 -0
  3. data/INSTALL +40 -0
  4. data/LICENSE +28 -0
  5. data/README +25 -0
  6. data/TODO +25 -0
  7. data/examples/ackscan.rb +38 -0
  8. data/examples/arp.rb +60 -0
  9. data/examples/arphood.rb +56 -0
  10. data/examples/ethernet.rb +10 -0
  11. data/examples/examples.rb +3 -0
  12. data/examples/ids.rb +4 -0
  13. data/examples/idsv2.rb +6 -0
  14. data/examples/oui.txt +84177 -0
  15. data/examples/packetfu-shell.rb +111 -0
  16. data/examples/simple-stats.rb +42 -0
  17. data/examples/slammer.rb +33 -0
  18. data/examples/uniqpcap.rb +15 -0
  19. data/lib/packetfu.rb +108 -0
  20. data/lib/packetfu/arp.rb +239 -0
  21. data/lib/packetfu/capture.rb +169 -0
  22. data/lib/packetfu/config.rb +55 -0
  23. data/lib/packetfu/eth.rb +264 -0
  24. data/lib/packetfu/icmp.rb +153 -0
  25. data/lib/packetfu/inject.rb +65 -0
  26. data/lib/packetfu/invalid.rb +41 -0
  27. data/lib/packetfu/ip.rb +318 -0
  28. data/lib/packetfu/ipv6.rb +230 -0
  29. data/lib/packetfu/packet.rb +492 -0
  30. data/lib/packetfu/pcap.rb +502 -0
  31. data/lib/packetfu/structfu.rb +274 -0
  32. data/lib/packetfu/tcp.rb +1061 -0
  33. data/lib/packetfu/udp.rb +210 -0
  34. data/lib/packetfu/utils.rb +182 -0
  35. data/test/all_tests.rb +37 -0
  36. data/test/ptest.rb +10 -0
  37. data/test/sample.pcap +0 -0
  38. data/test/sample2.pcap +0 -0
  39. data/test/test_arp.rb +135 -0
  40. data/test/test_eth.rb +90 -0
  41. data/test/test_icmp.rb +54 -0
  42. data/test/test_inject.rb +33 -0
  43. data/test/test_invalid.rb +28 -0
  44. data/test/test_ip.rb +69 -0
  45. data/test/test_ip6.rb +68 -0
  46. data/test/test_octets.rb +37 -0
  47. data/test/test_packet.rb +41 -0
  48. data/test/test_pcap.rb +210 -0
  49. data/test/test_structfu.rb +112 -0
  50. data/test/test_tcp.rb +327 -0
  51. data/test/test_udp.rb +73 -0
  52. metadata +144 -0
@@ -0,0 +1,230 @@
1
+ module PacketFu
2
+
3
+ # AddrIpv6 handles addressing for IPv6Header
4
+ #
5
+ # ==== Header Definition
6
+ #
7
+ # Int32 :a1
8
+ # Int32 :a2
9
+ # Int32 :a3
10
+ # Int32 :a4
11
+ class AddrIpv6 < Struct.new(:a1, :a2, :a3, :a4)
12
+
13
+ include StructFu
14
+
15
+ def initialize(args={})
16
+ super(
17
+ Int32.new(args[:a1]),
18
+ Int32.new(args[:a2]),
19
+ Int32.new(args[:a3]),
20
+ Int32.new(args[:a4]))
21
+ end
22
+
23
+ # Returns the address in string format.
24
+ def to_s
25
+ self.to_a.map {|x| x.to_s}.join
26
+ end
27
+
28
+ # Returns the address as a fairly ginormous integer.
29
+ def to_i
30
+ (a1.to_i << 96) + (a2.to_i << 64) + (a3.to_i << 32) + a4.to_i
31
+ end
32
+
33
+ # Returns the address as a colon-delimited hex string.
34
+ def to_x
35
+ IPAddr.new(self.to_i, Socket::AF_INET6).to_s
36
+ end
37
+
38
+ # Reads in a string and casts it as an IPv6 address
39
+ def read(str)
40
+ force_binary(str)
41
+ return self if str.nil?
42
+ self[:a1].read str[0,4]
43
+ self[:a2].read str[4,4]
44
+ self[:a3].read str[8,4]
45
+ self[:a4].read str[12,4]
46
+ self
47
+ end
48
+
49
+ # Reads in a colon-delimited hex string and casts it as an IPv6 address.
50
+ def read_x(str)
51
+ addr = IPAddr.new(str).to_i
52
+ self[:a1]=Int32.new(addr >> 96)
53
+ self[:a2]=Int32.new((addr & 0x00000000ffffffff0000000000000000) >> 64)
54
+ self[:a3]=Int32.new((addr & 0x0000000000000000ffffffff00000000) >> 32)
55
+ self[:a4]=Int32.new(addr & 0x000000000000000000000000ffffffff)
56
+ self
57
+ end
58
+
59
+ end
60
+
61
+ # IPv6Header is complete IPv6 struct, used in IPv6Packet.
62
+ #
63
+ # ==== Header Definition
64
+ #
65
+ # Fixnum (4 bits) :ipv6_v Default: 6 # Versiom
66
+ # Fixnum (8 bits) :ipv6_class Defualt: 0 # Class
67
+ # Fixnum (20 bits) :ipv6_label Defualt: 0 # Label
68
+ # Int16 :ipv6_len Default: calc # Payload length
69
+ # Int8 :ipv6_next # Next Header
70
+ # Int8 :ipv6_hop Default: 0xff # Hop limit
71
+ # AddrIpv6 :ipv6_src
72
+ # AddrIpv6 :ipv6_dst
73
+ # String :body
74
+ class IPv6Header < Struct.new(:ipv6_v, :ipv6_class, :ipv6_label,
75
+ :ipv6_len, :ipv6_next, :ipv6_hop,
76
+ :ipv6_src, :ipv6_dst, :body)
77
+ include StructFu
78
+
79
+ def initialize(args={})
80
+ super(
81
+ (args[:ipv6_v] || 6),
82
+ (args[:ipv6_class] || 0),
83
+ (args[:ipv6_label] || 0),
84
+ Int16.new(args[:ipv6_len]),
85
+ Int8.new(args[:ipv6_next]),
86
+ Int8.new(args[:ipv6_hop] || 0xff),
87
+ AddrIpv6.new.read(args[:ipv6_src] || ("\x00" * 16)),
88
+ AddrIpv6.new.read(args[:ipv6_dst] || ("\x00" * 16)),
89
+ StructFu::String.new.read(args[:body])
90
+ )
91
+ end
92
+
93
+ # Returns the object in string form.
94
+ def to_s
95
+ bytes_v_class_label = [(self.ipv6_v << 28) +
96
+ (self.ipv6_class << 20) +
97
+ self.ipv6_label].pack("N")
98
+ bytes_v_class_label + (self.to_a[3,6].map {|x| x.to_s}.join)
99
+ end
100
+
101
+ # Reads a string to populate the object.
102
+ def read(str)
103
+ force_binary(str)
104
+ return self if str.nil?
105
+ self[:ipv6_v] = str[0,1].unpack("C").first >> 4
106
+ self[:ipv6_class] = (str[0,2].unpack("n").first & 0x0ff0) >> 4
107
+ self[:ipv6_label] = str[0,4].unpack("N").first & 0x000fffff
108
+ self[:ipv6_len].read(str[4,2])
109
+ self[:ipv6_next].read(str[6,1])
110
+ self[:ipv6_hop].read(str[7,1])
111
+ self[:ipv6_src].read(str[8,16])
112
+ self[:ipv6_dst].read(str[24,16])
113
+ self[:body].read(str[40,str.size]) if str.size > 40
114
+ self
115
+ end
116
+
117
+ # Setter for the version (usually, 6).
118
+ def ipv6_v=(i); self[:ip_v] = i.to_i; end
119
+ # Getter for the version (usually, 6).
120
+ def ipv6_v; self[:ipv6_v].to_i; end
121
+ # Setter for the traffic class.
122
+ def ipv6_class=(i); self[:ip_class] = i.to_i; end
123
+ # Getter for the traffic class.
124
+ def ipv6_class; self[:ipv6_class].to_i; end
125
+ # Setter for the flow label.
126
+ def ipv6_label=(i); self[:ip_label] = i.to_i; end
127
+ # Getter for the flow label.
128
+ def ipv6_label; self[:ipv6_label].to_i; end
129
+ # Setter for the payload length.
130
+ def ipv6_len=(i); typecast i; end
131
+ # Getter for the payload length.
132
+ def ipv6_len; self[:ipv6_len].to_i; end
133
+ # Setter for the next protocol header.
134
+ def ipv6_next=(i); typecast i; end
135
+ # Getter for the next protocol header.
136
+ def ipv6_next; self[:ipv6_next].to_i; end
137
+ # Setter for the hop limit.
138
+ def ipv6_hop=(i); typecast i; end
139
+ # Getter for the hop limit.
140
+ def ipv6_hop; self[:ipv6_hop].to_i; end
141
+ # Setter for the source address.
142
+ def ipv6_src=(i); typecast i; end
143
+ # Getter for the source address.
144
+ def ipv6_src; self[:ipv6_src].to_i; end
145
+ # Setter for the destination address.
146
+ def ipv6_dst=(i); typecast i; end
147
+ # Getter for the destination address.
148
+ def ipv6_dst; self[:ipv6_dst].to_i; end
149
+
150
+ # Calculates the payload length.
151
+ def ipv6_calc_len
152
+ self[:ipv6_len] = body.to_s.length
153
+ end
154
+
155
+ # Recalculates the calculatable fields for this object.
156
+ def ipv6_recalc(arg=:all)
157
+ case arg
158
+ when :ipv6_len
159
+ ipv6_calc_len
160
+ when :all
161
+ ipv6_recalc(:len)
162
+ end
163
+ end
164
+
165
+ # Get the source address in a more readable form.
166
+ def ipv6_saddr
167
+ self[:ipv6_src].to_x
168
+ end
169
+
170
+ # Set the source address in a more readable form.
171
+ def ipv6_saddr=(str)
172
+ self[:ipv6_src].read_x(str)
173
+ end
174
+
175
+ # Get the destination address in a more readable form.
176
+ def ipv6_daddr
177
+ self[:ipv6_dst].to_x
178
+ end
179
+
180
+ # Set the destination address in a more readable form.
181
+ def ipv6_daddr=(str)
182
+ self[:ipv6_dst].read_x(str)
183
+ end
184
+
185
+ end # class IPv6Header
186
+
187
+ # IPv6Packet is used to construct IPv6 Packets. They contain an EthHeader and an IPv6Header, and in
188
+ # the distant, unknowable future, will take interesting IPv6ish payloads.
189
+ #
190
+ # This mostly complete, but not very useful. It's intended primarily as an example protocol.
191
+ #
192
+ # == Parameters
193
+ #
194
+ # :eth
195
+ # A pre-generated EthHeader object.
196
+ # :ip
197
+ # A pre-generated IPHeader object.
198
+ # :flavor
199
+ # TODO: Sets the "flavor" of the IPv6 packet. No idea what this will look like, haven't done much IPv6 fingerprinting.
200
+ # :config
201
+ # A hash of return address details, often the output of Utils.whoami?
202
+ class IPv6Packet < Packet
203
+
204
+ attr_accessor :eth_header, :ipv6_header
205
+
206
+ def initialize(args={})
207
+ @eth_header = (args[:eth] || EthHeader.new)
208
+ @ipv6_header = (args[:ipv6] || IPv6Header.new)
209
+ @eth_header.eth_proto = 0x86dd
210
+ @eth_header.body=@ipv6_header
211
+
212
+ @headers = [@eth_header, @ipv6_header]
213
+ super
214
+ end
215
+
216
+ # Peek provides summary data on packet contents.
217
+ def peek(args={})
218
+ peek_data = ["6 "]
219
+ peek_data << "%-5d" % self.to_s.size
220
+ peek_data << "%-31s" % self.ipv6_saddr
221
+ peek_data << "-> "
222
+ peek_data << "%-31s" % self.ipv6_daddr
223
+ peek_data << " N:"
224
+ peek_data << self.ipv6_next.to_s(16)
225
+ peek_data.join
226
+ end
227
+
228
+ end
229
+
230
+ end
@@ -0,0 +1,492 @@
1
+ module PacketFu
2
+
3
+ # Packet is the parent class of EthPacket, IPPacket, UDPPacket, TCPPacket, and all
4
+ # other packets.
5
+ class Packet
6
+ attr_reader :flavor # Packet Headers are responsible for their own specific flavor methods.
7
+ attr_accessor :headers # All packets have a header collection, useful for determining protocol trees.
8
+ attr_accessor :iface # Default inferface to send packets to
9
+
10
+ # Force strings into binary.
11
+ def self.force_binary(str)
12
+ str.force_encoding "binary" if str.respond_to? :force_encoding
13
+ end
14
+
15
+ # Parse() creates the correct packet type based on the data, and returns the apporpiate
16
+ # Packet subclass.
17
+ #
18
+ # There is an assumption here that all incoming packets are either EthPacket
19
+ # or InvalidPacket types.
20
+ #
21
+ # New packet types should get an entry here.
22
+ def self.parse(packet,args={})
23
+ force_binary(packet)
24
+ if packet.size >= 14 # Min size for Ethernet. No check for max size, yet.
25
+ case packet[12,2] # Check the Eth protocol field.
26
+ when "\x08\x00" # It's IP.
27
+ if 1.respond_to? :ord
28
+ ipv = packet[14,1][0].ord >> 4
29
+ else
30
+ ipv = packet[14,1][0] >> 4
31
+ end
32
+ case ipv # Check the IP version field.
33
+ when 4; # It's IPv4.
34
+ case packet[23,1] # Check the IP protocol field.
35
+ when "\x06"; p = TCPPacket.new # Returns a TCPPacket.
36
+ when "\x11"; p = UDPPacket.new # Returns a UDPPacket.
37
+ when "\x01"; p = ICMPPacket.new # Returns an ICMPPacket.
38
+ else; p = IPPacket.new # Returns an IPPacket since we can't tell the transport layer.
39
+ end
40
+ else; p = IPPacket.new # Returns an EthPacket since we don't know any other IP version.
41
+ end
42
+ when "\x08\x06" # It's arp
43
+ if packet.size >= 28 # Min size for complete arp
44
+ p = ARPPacket.new
45
+ else; p = EthPacket.new # Returns an EthPacket since we can't deal with tiny arps.
46
+ end
47
+ when "\x86\xdd" # It's IPv6
48
+ if packet.size >= 54 # Min size for a complete IPv6 packet.
49
+ p = IPv6Packet.new
50
+ else; p = EthPacket.new # Returns an EthPacket since we can't deal with tiny Ipv6.
51
+ end
52
+ else; p = EthPacket.new # Returns an EthPacket since we can't tell the network layer.
53
+ end
54
+ else
55
+ p = InvalidPacket.new # Not the right size for Ethernet (jumbo frames are okay)
56
+ end
57
+ parsed_packet = p.read(packet,args)
58
+ return parsed_packet
59
+ end
60
+
61
+ #method_missing() delegates protocol-specific field actions to the apporpraite
62
+ #class variable (which contains the associated packet type)
63
+ #This register-of-protocols style switch will work for the
64
+ #forseeable future (there aren't /that/ many packet types), and it's a handy
65
+ #way to know at a glance what packet types are supported.
66
+ def method_missing(sym, *args)
67
+ case sym.to_s
68
+ when /^invalid_/
69
+ @invalid_header.send(sym,*args)
70
+ when /^eth_/
71
+ @eth_header.send(sym,*args)
72
+ when /^arp_/
73
+ @arp_header.send(sym,*args)
74
+ when /^ip_/
75
+ @ip_header.send(sym,*args)
76
+ when /^icmp_/
77
+ @icmp_header.send(sym,*args)
78
+ when /^udp_/
79
+ @udp_header.send(sym,*args)
80
+ when /^tcp_/
81
+ @tcp_header.send(sym,*args)
82
+ when /^ipv6_/
83
+ @ipv6_header.send(sym,*args)
84
+ else
85
+ raise NoMethodError, "Unknown method `#{sym}' for this packet object."
86
+ end
87
+ end
88
+
89
+ # Get the binary string of the entire packet.
90
+ def to_s
91
+ @headers[0].to_s
92
+ end
93
+
94
+ # In the event of no proper decoding, at least send it to the inner-most header.
95
+ def write(io)
96
+ @headers[0].write(io)
97
+ end
98
+
99
+ # Get the outermost payload (body) of the packet; this is why all packet headers
100
+ # should have a body type.
101
+ def payload
102
+ @headers.last.body
103
+ end
104
+
105
+ # Set the outermost payload (body) of the packet.
106
+ def payload=(args)
107
+ @headers.last.body=(args)
108
+ end
109
+
110
+ # Converts a packet to libpcap format. Bit of a hack?
111
+ def to_pcap(args={})
112
+ p = PcapPacket.new(:endian => args[:endian],
113
+ :timestamp => Timestamp.new.to_s,
114
+ :incl_len => self.to_s.size,
115
+ :orig_len => self.to_s.size,
116
+ :data => self)
117
+ end
118
+
119
+ # Put the entire packet into a libpcap file. XXX: this is a
120
+ # hack for now just to confirm that packets are getting created
121
+ # correctly. Now with append! XXX: Document this!
122
+ def to_f(filename=nil,mode='w')
123
+ filename ||= 'out.pcap'
124
+ mode = mode.to_s[0,1] + "b"
125
+ raise ArgumentError, "Unknown mode: #{mode.to_s}" unless mode =~ /^[wa]/
126
+ if(mode == 'w' || !(File.exists?(filename)))
127
+ data = [PcapHeader.new, self.to_pcap].map {|x| x.to_s}.join
128
+ else
129
+ data = self.to_pcap
130
+ end
131
+ File.open(filename, mode) {|f| f.write data}
132
+ return [filename, 1, data.size]
133
+ end
134
+
135
+ # Put the entire packet on the wire by creating a temporary PacketFu::Inject object.
136
+ # TODO: Do something with auto-checksumming?
137
+ def to_w(iface=nil)
138
+ iface = iface || self.iface || PacketFu::Config.new.config[:iface]
139
+ inj = PacketFu::Inject.new(:iface => iface)
140
+ inj.array = [@headers[0].to_s]
141
+ inj.inject
142
+ end
143
+
144
+ # Recalculates all the calcuated fields for all headers in the packet.
145
+ # This is important since read() wipes out all the calculated fields
146
+ # such as length and checksum and what all.
147
+ def recalc(arg=:all)
148
+ case arg
149
+ when :ip
150
+ ip_recalc(:all)
151
+ when :icmp
152
+ icmp_recalc(:all)
153
+ when :udp
154
+ udp_recalc(:all)
155
+ when :tcp
156
+ tcp_recalc(:all)
157
+ when :all
158
+ ip_recalc(:all) if @ip_header
159
+ icmp_recalc(:all) if @icmp_header
160
+ udp_recalc(:all) if @udp_header
161
+ tcp_recalc(:all) if @tcp_header
162
+ else
163
+ raise ArgumentError, "Recalculating `#{arg}' unsupported. Try :all"
164
+ end
165
+ @headers[0]
166
+ end
167
+
168
+ # Read() takes (and trusts) the io input and shoves it all into a well-formed Packet.
169
+ # Note that read is a destructive process, so any existing data will be lost.
170
+ #
171
+ # TODO: This giant if tree is a mess, and worse, is decieving. You need to define
172
+ # actions both here and in parse(). All read() does is make a (good) guess as to
173
+ # what @headers to expect, and reads data to them.
174
+ #
175
+ # To take strings and turn them into packets without knowing ahead of time what kind of
176
+ # packet it is, use Packet.parse instead; parse() handles the figuring-out part.
177
+ #
178
+ # A note on the :strip => true argument: If :strip is set, defined lengths of data will
179
+ # be believed, and any trailers (such as frame check sequences) will be chopped off. This
180
+ # helps to ensure well-formed packets, at the cost of losing perhaps important FCS data.
181
+ #
182
+ # If :strip is false, header lengths are /not/ believed, and all data will be piped in.
183
+ # When capturing from the wire, this is usually fine, but recalculating the length before
184
+ # saving or re-transmitting will absolutely change the data payload; FCS data will become
185
+ # part of the TCP data as far as tcp_len is concerned. Some effort has been made to preserve
186
+ # the "real" payload for the purposes of checksums, but currently, it's impossible to seperate
187
+ # new payload data from old trailers, so things like pkt.payload += "some data" will not work
188
+ # correctly.
189
+ #
190
+ # So, to summarize; if you intend to alter the data, use :strip. If you don't, don't. Also,
191
+ # this is a horrid XXX hack. Stripping is useful (and fun!), but the default behavior really
192
+ # should be to create payloads correctly, and /not/ treat extra FCS data as a payload.
193
+ #
194
+ # Update: This scheme is so lame. Need to fix. Seriously.
195
+ # Update: still sucks. Really.
196
+ def read(io,args={})
197
+ begin
198
+ if io.size >= 14
199
+ @eth_header.read(io)
200
+ eth_proto_num = io[12,2].unpack("n")[0]
201
+ if eth_proto_num == 0x0800 # It's IP.
202
+ if 1.respond_to? :ord
203
+ ipv = io[14].ord
204
+ else
205
+ ipv = io[14]
206
+ end
207
+ ip_hlen=(ipv & 0x0f) * 4
208
+ ip_ver=(ipv >> 4) # It's IPv4. Other versions, all bets are off!
209
+ if ip_ver == 4
210
+ ip_proto_num = io[23,1].unpack("C")[0]
211
+ @ip_header.read(io[14,ip_hlen])
212
+ if ip_proto_num == 0x06 # It's TCP.
213
+ tcp_len = io[16,2].unpack("n")[0] - 20
214
+ if args[:strip] # Drops trailers like frame check sequence (FCS). Often desired for cleaner packets.
215
+ tcp_all = io[ip_hlen+14,tcp_len] # Believe the tcp_len value; chop off anything that's not in range.
216
+ else
217
+ tcp_all = io[ip_hlen+14,0xffff] # Don't believe the tcp_len value; suck everything up.
218
+ end
219
+ tcp_hlen = ((tcp_all[12,1].unpack("C")[0]) >> 4) * 4
220
+ if tcp_hlen.to_i >= 20
221
+ @tcp_header.read(tcp_all)
222
+ @ip_header.body = @tcp_header
223
+ else # It's a TCP packet with an impossibly small hlen, so it can't be real TCP. Abort! Abort!
224
+ @ip_header.body = io[16,io.size-16]
225
+ end
226
+ elsif ip_proto_num == 0x11 # It's UDP.
227
+ udp_len = io[16,2].unpack("n")[0] - 20
228
+ if args[:strip] # Same deal as with TCP. We might have stuff at the end of the packet that's not part of the payload.
229
+ @udp_header.read(io[ip_hlen+14,udp_len])
230
+ else # ... Suck it all up. BTW, this will change the lengths if they are ever recalc'ed. Bummer.
231
+ @udp_header.read(io[ip_hlen+14,0xffff])
232
+ end
233
+ @ip_header.body = @udp_header
234
+ elsif ip_proto_num == 1 # It's ICMP
235
+ @icmp_header.read(io[ip_hlen+14,0xffff])
236
+ @ip_header.body = @icmp_header
237
+ else # It's an IP packet for a protocol we don't have a decoder for.
238
+ @ip_header.body = io[16,io.size-16]
239
+ end
240
+ else # It's not IPv4, so no idea what should come next. Just dump it all into an ip_header and ip payload.
241
+ @ip_header.read(io[14,ip_hlen])
242
+ @ip_header.body = io[16,io.size-16]
243
+ end
244
+ @eth_header.body = @ip_header
245
+ elsif eth_proto_num == 0x0806 # It's ARP
246
+ @arp_header.read(io[14,0xffff]) # You'll nearly have a trailer and you'll never know what size.
247
+ @eth_header.body=@arp_header
248
+ @eth_header.body
249
+ elsif eth_proto_num == 0x86dd # It's IPv6
250
+ @ipv6_header.read(io[14,0xffff])
251
+ @eth_header.body=@ipv6_header
252
+ else # It's an Ethernet packet for a protocol we don't have a decoder for
253
+ @eth_header.body = io[14,io.size-14]
254
+ end
255
+ if (args[:fix] || args[:recalc])
256
+ # Unfortunately, we cannot simply recalc with abandon, since
257
+ # we may have unaccounted trailers that will sneak into the checksum.
258
+ # The better way to handle this is to put trailers in their own
259
+ # StructFu field, but I'm not a-gonna right now. :/
260
+ ip_recalc(:ip_sum) if respond_to? :ip_header
261
+ recalc(:tcp) if respond_to? :tcp_header
262
+ recalc(:udp) if respond_to? :udp_header
263
+ end
264
+ else # You're not big enough for Ethernet.
265
+ @invalid_header.read(io)
266
+ end
267
+ # @headers[0]
268
+ self
269
+ rescue ::Exception => e
270
+ # remove last header
271
+ # nested_types = self.headers.collect {|header| header.class}
272
+ # nested_types.pop # whatever this packet type is, we weren't able to parse it
273
+ self.headers.pop
274
+ return_header_type = self.headers[self.headers.length-1].class.to_s
275
+ retklass = PacketFu::InvalidPacket
276
+ seekpos = 0
277
+ target_header = @invalid_header
278
+ case return_header_type.to_s
279
+ when "PacketFu::EthHeader"
280
+ retklass = PacketFu::EthPacket
281
+ seekpos = 0x0e
282
+ target_header = @eth_header
283
+ when "PacketFu::IPHeader"
284
+ retklass = PacketFu::IPPacket
285
+ seekpos = 0x0e + @ip_header.ip_hl * 4
286
+ target_header = @ip_header
287
+ when "PacketFu::TCPHeader"
288
+ retklass = PacketFu::TCPPacket
289
+ seekpos = 0x0e + @ip_header.ip_hl * 4 + @tcpheader.tcp_hlen
290
+ target_header = @tcp_header
291
+ when "PacketFu::UDPHeader"
292
+ retklass = PacketFu::UDPPacket
293
+ when "PacketFu::ARPHeader"
294
+ retklass = PacketFu::ARPPacket
295
+ when "PacketFu::ICMPHeader"
296
+ retklass = PacketFu::ICMPPacket
297
+ when "PacketFu::IPv6Header"
298
+ retklass = PacketFu::IPv6Packet
299
+ else
300
+ end
301
+
302
+ io = io[seekpos,io.length - seekpos]
303
+ target_header.body = io
304
+ p = retklass.new
305
+ p.headers = self.headers
306
+ p
307
+ raise e if $debug
308
+ end
309
+ end
310
+
311
+ # Peek provides summary data on packet contents.
312
+ # Each packet type should provide its own peek method, and shouldn't exceed 80 characters wide (for
313
+ # easy reading in normal irb shells). If they don't, this default summary will step in.
314
+ def peek(args={})
315
+ peek_data = ["? "]
316
+ peek_data << "%-5d" % self.to_s.size
317
+ peek_data << "%68s" % self.to_s[0,34].unpack("H*")[0]
318
+ peek_data.join
319
+ end
320
+
321
+ # Hexify provides a neatly-formatted dump of binary data, familar to hex readers.
322
+ def hexify(str)
323
+ hexascii_lines = str.to_s.unpack("H*")[0].scan(/.{1,32}/)
324
+ chars = str.to_s.gsub(/[\x00-\x1f\x7f-\xff]/,'.')
325
+ chars_lines = chars.scan(/.{1,16}/)
326
+ ret = []
327
+ hexascii_lines.size.times {|i| ret << "%-48s %s" % [hexascii_lines[i].gsub(/(.{2})/,"\\1 "),chars_lines[i]]}
328
+ ret.join("\n")
329
+ end
330
+
331
+ # Returns a hex-formatted representation of the packet.
332
+ #
333
+ # ==== Arguments
334
+ #
335
+ # 0..9 : If a number is given only the layer in @header[arg] will be displayed. Note that this will include all @headers included in that header.
336
+ # :layers : If :layers is specified, the dump will return an array of headers by layer level.
337
+ # :all : An alias for arg=0.
338
+ #
339
+ # ==== Examples
340
+ #
341
+ # irb(main):003:0> pkt = TCPPacket.new
342
+ # irb(main):003:0> puts pkt.inspect_hex(:layers)
343
+ # 00 1a c5 00 00 00 00 1a c5 00 00 00 08 00 45 00 ..............E.
344
+ # 00 28 83 ce 00 00 ff 06 38 02 00 00 00 00 00 00 .(......8.......
345
+ # 00 00 a6 0f 00 00 ac 89 7b 26 00 00 00 00 50 00 ........{&....P.
346
+ # 40 00 a2 25 00 00 @..%..
347
+ # 45 00 00 28 83 ce 00 00 ff 06 38 02 00 00 00 00 E..(......8.....
348
+ # 00 00 00 00 a6 0f 00 00 ac 89 7b 26 00 00 00 00 ..........{&....
349
+ # 50 00 40 00 a2 25 00 00 P.@..%..
350
+ # a6 0f 00 00 ac 89 7b 26 00 00 00 00 50 00 40 00 ......{&....P.@.
351
+ # a2 25 00 00 .%..
352
+ # => nil
353
+ # irb(main):004:0> puts pkt.inspect_hex(:layers)[2]
354
+ # a6 0f 00 00 ac 89 7b 26 00 00 00 00 50 00 40 00 ......{&....P.@.
355
+ # a2 25 00 00 .%..
356
+ # => nil
357
+ #
358
+ # TODO: Colorize this! Everyone loves colorized irb output.
359
+ def inspect_hex(arg=0)
360
+ case arg
361
+ when :layers
362
+ ret = []
363
+ @headers.size.times do |i|
364
+ ret << hexify(@headers[i])
365
+ end
366
+ ret
367
+ when (0..9)
368
+ if @headers[arg]
369
+ hexify(@headers[arg])
370
+ else
371
+ nil
372
+ end
373
+ when :all
374
+ inspect_hex(0)
375
+ end
376
+ end
377
+
378
+ # For packets, inspect is overloaded as inspect_hex(0).
379
+ # Not sure if this is a great idea yet, but it sure makes
380
+ # the irb output more sane.
381
+ #
382
+ # If you hate this, you can run PacketFu.toggle_inspect to return
383
+ # to the typical (and often unreadable) Object#inspect format.
384
+ def inspect
385
+ self.proto.join("|") + "\n" + self.inspect_hex
386
+ end
387
+
388
+ # Returns the size of the packet (as a binary string)
389
+ def size
390
+ self.to_s.size
391
+ end
392
+
393
+ # Returns an array of protocols contained in this packet. For example:
394
+ #
395
+ # t = PacketFu::TCPPacket.new
396
+ # => 00 1a c5 00 00 00 00 1a c5 00 00 00 08 00 45 00 ..............E.
397
+ # 00 28 3c ab 00 00 ff 06 7f 25 00 00 00 00 00 00 .(<......%......
398
+ # 00 00 93 5e 00 00 ad 4f e4 a4 00 00 00 00 50 00 ...^...O......P.
399
+ # 40 00 4a 92 00 00 @.J...
400
+ # t.proto
401
+ # => ["Eth", "IP", "TCP"]
402
+ #
403
+ def proto
404
+ type_array = []
405
+ self.headers.each {|header| type_array << header.class.to_s.split('::').last.gsub(/Header$/,'')}
406
+ type_array
407
+ end
408
+
409
+ alias_method :protocol, :proto
410
+
411
+ # Returns true if this is an Invalid packet. Else, false.
412
+ def is_invalid? ; self.proto.include? "Invalid"; end
413
+ # Returns true if this is an Ethernet packet. Else, false.
414
+ def is_ethernet? ; self.proto.include? "Eth"; end
415
+ alias_method :is_eth?, :is_ethernet?
416
+ # Returns true if this is an IP packet. Else, false.
417
+ def is_ip? ; self.proto.include? "IP"; end
418
+ # Returns true if this is an TCP packet. Else, false.
419
+ def is_tcp? ; self.proto.include? "TCP"; end
420
+ # Returns true if this is an UDP packet. Else, false.
421
+ def is_udp? ; self.proto.include? "UDP"; end
422
+ # Returns true if this is an ARP packet. Else, false.
423
+ def is_arp? ; self.proto.include? "ARP"; end
424
+ # Returns true if this is an IPv6 packet. Else, false.
425
+ def is_ipv6? ; self.proto.include? "IPv6" ; end
426
+ # Returns true if this is an ICMP packet. Else, false.
427
+ def is_icmp? ; self.proto.include? "ICMP" ; end
428
+ # Returns true if this is an IPv6 packet. Else, false.
429
+ def is_ipv6? ; self.proto.include? "IPv6" ; end
430
+ # Returns true if the outermost layer has data. Else, false.
431
+ def has_data? ; self.payload.size.zero? ? false : true ; end
432
+
433
+ alias_method :length, :size
434
+
435
+ def initialize(args={})
436
+ if args[:config]
437
+ args[:config].each_pair do |k,v|
438
+ case k
439
+ when :eth_daddr; @eth_header.eth_daddr=v if @eth_header
440
+ when :eth_saddr; @eth_header.eth_saddr=v if @eth_header
441
+ when :ip_saddr; @ip_header.ip_saddr=v if @ip_header
442
+ when :iface; @iface = v
443
+ end
444
+ end
445
+ end
446
+ end
447
+
448
+ end # class Packet
449
+
450
+ @@inspect_style = :pretty
451
+
452
+ # If @@inspect_style is :ugly, set the inspect method to the usual inspect.
453
+ # By default, @@inspect_style is :pretty. This default may change if people
454
+ # hate it.
455
+ # Since PacketFu is designed with irb in mind, the normal inspect is way too
456
+ # verbose when new packets are created, and it ruins the aesthetics of the
457
+ # PacketFu console or quick hping-like exercises in irb.
458
+ #
459
+ # However, there are cases where knowing things like object id numbers, the complete
460
+ # @header array, etc. is useful (especially in debugging). So, toggle_inspect
461
+ # provides a means for a script to declar which style of inspect to use.
462
+ #
463
+ # This method may be an even worse idea than the original monkeypatch to Packet.inspect,
464
+ # since it would almost certainly be better to redefine inspect just in the PacketFu console.
465
+ # We'll see what happens.
466
+ #
467
+ # == Example
468
+ #
469
+ # irb(main):001:0> p = PacketFu::TCPPacket.new
470
+ # => Eth|IP|TCP
471
+ # 00 1a c5 00 00 00 00 1a c5 00 00 00 08 00 45 00 ..............E.
472
+ # 00 28 ea d7 00 00 ff 06 d0 f8 00 00 00 00 00 00 .(..............
473
+ # 00 00 a9 76 00 00 f9 28 7e 95 00 00 00 00 50 00 ...v...(~.....P.
474
+ # 40 00 4e b0 00 00 @.N...
475
+ # irb(main):002:0> PacketFu.toggle_inspect
476
+ # => :ugly
477
+ # irb(main):003:0> p = PacketFu::TCPPacket.new
478
+ # => #<PacketFu::TCPPacket:0xb7aaf96c @ip_header=#<struct PacketFu::IPHeader ip_v=4, ip_hl=5, ip_tos=#<struct StructFu::Int8 value=nil, endian=nil, width=1, default=0>, ip_len=#<struct StructFu::Int16 value=20, endian=:big, width=2, default=0>, ip_id=#<struct StructFu::Int16 value=58458, endian=:big, width=2, default=0>, ip_frag=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, ip_ttl=#<struct StructFu::Int8 value=32, endian=nil, width=1, default=0>, ip_proto=#<struct StructFu::Int8 value=6, endian=nil, width=1, default=0>, ip_sum=#<struct StructFu::Int16 value=65535, endian=:big, width=2, default=0>, ip_src=#<struct PacketFu::Octets o1=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o2=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o3=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o4=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>>, ip_dst=#<struct PacketFu::Octets o1=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o2=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o3=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o4=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>>, body=#<struct PacketFu::TCPHeader tcp_src=#<struct StructFu::Int16 value=17222, endian=:big, width=2, default=0>, tcp_dst=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_seq=#<struct StructFu::Int32 value=1528113240, endian=:big, width=4, default=0>, tcp_ack=#<struct StructFu::Int32 value=nil, endian=:big, width=4, default=0>, tcp_hlen=#<struct PacketFu::TcpHlen hlen=5>, tcp_reserved=#<struct PacketFu::TcpReserved r1=0, r2=0, r3=0>, tcp_ecn=#<struct PacketFu::TcpEcn n=nil, c=nil, e=nil>, tcp_flags=#<struct PacketFu::TcpFlags urg=0, ack=0, psh=0, rst=0, syn=0, fin=0>, tcp_win=#<struct StructFu::Int16 value=16384, endian=:big, width=2, default=0>, tcp_sum=#<struct StructFu::Int16 value=43333, endian=:big, width=2, default=0>, tcp_urg=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_opts=[], body="">>, @tcp_header=#<struct PacketFu::TCPHeader tcp_src=#<struct StructFu::Int16 value=17222, endian=:big, width=2, default=0>, tcp_dst=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_seq=#<struct StructFu::Int32 value=1528113240, endian=:big, width=4, default=0>, tcp_ack=#<struct StructFu::Int32 value=nil, endian=:big, width=4, default=0>, tcp_hlen=#<struct PacketFu::TcpHlen hlen=5>, tcp_reserved=#<struct PacketFu::TcpReserved r1=0, r2=0, r3=0>, tcp_ecn=#<struct PacketFu::TcpEcn n=nil, c=nil, e=nil>, tcp_flags=#<struct PacketFu::TcpFlags urg=0, ack=0, psh=0, rst=0, syn=0, fin=0>, tcp_win=#<struct StructFu::Int16 value=16384, endian=:big, width=2, default=0>, tcp_sum=#<struct StructFu::Int16 value=43333, endian=:big, width=2, default=0>, tcp_urg=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_opts=[], body="">, @eth_header=#<struct PacketFu::EthHeader eth_dst=#<struct PacketFu::EthMac oui=#<struct PacketFu::EthOui b0=nil, b1=nil, b2=nil, b3=nil, b4=nil, b5=nil, local=0, multicast=nil, oui=428>, nic=#<struct PacketFu::EthNic n0=nil, n1=nil, n2=nil>>, eth_src=#<struct PacketFu::EthMac oui=#<struct PacketFu::EthOui b0=nil, b1=nil, b2=nil, b3=nil, b4=nil, b5=nil, local=0, multicast=nil, oui=428>, nic=#<struct PacketFu::EthNic n0=nil, n1=nil, n2=nil>>, eth_proto=#<struct StructFu::Int16 value=2048, endian=:big, width=2, default=0>, body=#<struct PacketFu::IPHeader ip_v=4, ip_hl=5, ip_tos=#<struct StructFu::Int8 value=nil, endian=nil, width=1, default=0>, ip_len=#<struct StructFu::Int16 value=20, endian=:big, width=2, default=0>, ip_id=#<struct StructFu::Int16 value=58458, endian=:big, width=2, default=0>, ip_frag=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, ip_ttl=#<struct StructFu::Int8 value=32, endian=nil, width=1, default=0>, ip_proto=#<struct StructFu::Int8 value=6, endian=nil, width=1, default=0>, ip_sum=#<struct StructFu::Int16 value=65535, endian=:big, width=2, default=0>, ip_src=#<struct PacketFu::Octets o1=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o2=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o3=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o4=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>>, ip_dst=#<struct PacketFu::Octets o1=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o2=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o3=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o4=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>>, body=#<struct PacketFu::TCPHeader tcp_src=#<struct StructFu::Int16 value=17222, endian=:big, width=2, default=0>, tcp_dst=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_seq=#<struct StructFu::Int32 value=1528113240, endian=:big, width=4, default=0>, tcp_ack=#<struct StructFu::Int32 value=nil, endian=:big, width=4, default=0>, tcp_hlen=#<struct PacketFu::TcpHlen hlen=5>, tcp_reserved=#<struct PacketFu::TcpReserved r1=0, r2=0, r3=0>, tcp_ecn=#<struct PacketFu::TcpEcn n=nil, c=nil, e=nil>, tcp_flags=#<struct PacketFu::TcpFlags urg=0, ack=0, psh=0, rst=0, syn=0, fin=0>, tcp_win=#<struct StructFu::Int16 value=16384, endian=:big, width=2, default=0>, tcp_sum=#<struct StructFu::Int16 value=43333, endian=:big, width=2, default=0>, tcp_urg=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_opts=[], body="">>>, @headers=[#<struct PacketFu::EthHeader eth_dst=#<struct PacketFu::EthMac oui=#<struct PacketFu::EthOui b0=nil, b1=nil, b2=nil, b3=nil, b4=nil, b5=nil, local=0, multicast=nil, oui=428>, nic=#<struct PacketFu::EthNic n0=nil, n1=nil, n2=nil>>, eth_src=#<struct PacketFu::EthMac oui=#<struct PacketFu::EthOui b0=nil, b1=nil, b2=nil, b3=nil, b4=nil, b5=nil, local=0, multicast=nil, oui=428>, nic=#<struct PacketFu::EthNic n0=nil, n1=nil, n2=nil>>, eth_proto=#<struct StructFu::Int16 value=2048, endian=:big, width=2, default=0>, body=#<struct PacketFu::IPHeader ip_v=4, ip_hl=5, ip_tos=#<struct StructFu::Int8 value=nil, endian=nil, width=1, default=0>, ip_len=#<struct StructFu::Int16 value=20, endian=:big, width=2, default=0>, ip_id=#<struct StructFu::Int16 value=58458, endian=:big, width=2, default=0>, ip_frag=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, ip_ttl=#<struct StructFu::Int8 value=32, endian=nil, width=1, default=0>, ip_proto=#<struct StructFu::Int8 value=6, endian=nil, width=1, default=0>, ip_sum=#<struct StructFu::Int16 value=65535, endian=:big, width=2, default=0>, ip_src=#<struct PacketFu::Octets o1=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o2=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o3=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o4=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>>, ip_dst=#<struct PacketFu::Octets o1=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o2=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o3=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o4=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>>, body=#<struct PacketFu::TCPHeader tcp_src=#<struct StructFu::Int16 value=17222, endian=:big, width=2, default=0>, tcp_dst=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_seq=#<struct StructFu::Int32 value=1528113240, endian=:big, width=4, default=0>, tcp_ack=#<struct StructFu::Int32 value=nil, endian=:big, width=4, default=0>, tcp_hlen=#<struct PacketFu::TcpHlen hlen=5>, tcp_reserved=#<struct PacketFu::TcpReserved r1=0, r2=0, r3=0>, tcp_ecn=#<struct PacketFu::TcpEcn n=nil, c=nil, e=nil>, tcp_flags=#<struct PacketFu::TcpFlags urg=0, ack=0, psh=0, rst=0, syn=0, fin=0>, tcp_win=#<struct StructFu::Int16 value=16384, endian=:big, width=2, default=0>, tcp_sum=#<struct StructFu::Int16 value=43333, endian=:big, width=2, default=0>, tcp_urg=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_opts=[], body="">>>, #<struct PacketFu::IPHeader ip_v=4, ip_hl=5, ip_tos=#<struct StructFu::Int8 value=nil, endian=nil, width=1, default=0>, ip_len=#<struct StructFu::Int16 value=20, endian=:big, width=2, default=0>, ip_id=#<struct StructFu::Int16 value=58458, endian=:big, width=2, default=0>, ip_frag=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, ip_ttl=#<struct StructFu::Int8 value=32, endian=nil, width=1, default=0>, ip_proto=#<struct StructFu::Int8 value=6, endian=nil, width=1, default=0>, ip_sum=#<struct StructFu::Int16 value=65535, endian=:big, width=2, default=0>, ip_src=#<struct PacketFu::Octets o1=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o2=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o3=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o4=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>>, ip_dst=#<struct PacketFu::Octets o1=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o2=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o3=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>, o4=#<struct StructFu::Int8 value=0, endian=nil, width=1, default=0>>, body=#<struct PacketFu::TCPHeader tcp_src=#<struct StructFu::Int16 value=17222, endian=:big, width=2, default=0>, tcp_dst=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_seq=#<struct StructFu::Int32 value=1528113240, endian=:big, width=4, default=0>, tcp_ack=#<struct StructFu::Int32 value=nil, endian=:big, width=4, default=0>, tcp_hlen=#<struct PacketFu::TcpHlen hlen=5>, tcp_reserved=#<struct PacketFu::TcpReserved r1=0, r2=0, r3=0>, tcp_ecn=#<struct PacketFu::TcpEcn n=nil, c=nil, e=nil>, tcp_flags=#<struct PacketFu::TcpFlags urg=0, ack=0, psh=0, rst=0, syn=0, fin=0>, tcp_win=#<struct StructFu::Int16 value=16384, endian=:big, width=2, default=0>, tcp_sum=#<struct StructFu::Int16 value=43333, endian=:big, width=2, default=0>, tcp_urg=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_opts=[], body="">>, #<struct PacketFu::TCPHeader tcp_src=#<struct StructFu::Int16 value=17222, endian=:big, width=2, default=0>, tcp_dst=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_seq=#<struct StructFu::Int32 value=1528113240, endian=:big, width=4, default=0>, tcp_ack=#<struct StructFu::Int32 value=nil, endian=:big, width=4, default=0>, tcp_hlen=#<struct PacketFu::TcpHlen hlen=5>, tcp_reserved=#<struct PacketFu::TcpReserved r1=0, r2=0, r3=0>, tcp_ecn=#<struct PacketFu::TcpEcn n=nil, c=nil, e=nil>, tcp_flags=#<struct PacketFu::TcpFlags urg=0, ack=0, psh=0, rst=0, syn=0, fin=0>, tcp_win=#<struct StructFu::Int16 value=16384, endian=:big, width=2, default=0>, tcp_sum=#<struct StructFu::Int16 value=43333, endian=:big, width=2, default=0>, tcp_urg=#<struct StructFu::Int16 value=nil, endian=:big, width=2, default=0>, tcp_opts=[], body="">]>
479
+ # irb(main):004:0>
480
+ def toggle_inspect
481
+ if @@inspect_style == :pretty
482
+ eval("class Packet; def inspect; super; end; end")
483
+ @@inspect_style = :ugly
484
+ else
485
+ eval("class Packet; def inspect; self.proto.join('|') + \"\n\" + self.inspect_hex; end; end")
486
+ @@inspect_style = :pretty
487
+ end
488
+ end
489
+
490
+ end
491
+
492
+ # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby