packetfu 1.1.5 → 1.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.
- data/.document +5 -2
- data/.gitignore +1 -0
- data/LICENSE.txt +1 -1
- data/bench/after-2012-07-28.txt +25 -0
- data/bench/before-2012-07-28.txt +25 -0
- data/bench/benchit.rb +68 -0
- data/bench/calc_delta.rb +17 -0
- data/bench/octets.rb +22 -0
- data/bench/octets_after.txt +8 -0
- data/bench/octets_after_refactor.txt +8 -0
- data/bench/octets_before.txt +8 -0
- data/lib/packetfu.rb +8 -3
- data/lib/packetfu/packet.rb +2 -2
- data/lib/packetfu/pcap.rb +20 -4
- data/lib/packetfu/protos/arp.rb +7 -160
- data/lib/packetfu/protos/arp/header.rb +160 -0
- data/lib/packetfu/protos/arp/mixin.rb +38 -0
- data/lib/packetfu/protos/eth.rb +5 -247
- data/lib/packetfu/protos/eth/header.rb +247 -0
- data/lib/packetfu/protos/eth/mixin.rb +20 -0
- data/lib/packetfu/protos/hsrp.rb +13 -123
- data/lib/packetfu/protos/hsrp/header.rb +120 -0
- data/lib/packetfu/protos/hsrp/mixin.rb +31 -0
- data/lib/packetfu/protos/icmp.rb +10 -97
- data/lib/packetfu/protos/icmp/header.rb +93 -0
- data/lib/packetfu/protos/icmp/mixin.rb +17 -0
- data/lib/packetfu/protos/ip.rb +7 -295
- data/lib/packetfu/protos/ip/header.rb +335 -0
- data/lib/packetfu/protos/ip/mixin.rb +43 -0
- data/lib/packetfu/protos/ipv6.rb +7 -191
- data/lib/packetfu/protos/ipv6/header.rb +190 -0
- data/lib/packetfu/protos/ipv6/mixin.rb +31 -0
- data/lib/packetfu/protos/tcp.rb +13 -939
- data/lib/packetfu/protos/tcp/ecn.rb +42 -0
- data/lib/packetfu/protos/tcp/flags.rb +83 -0
- data/lib/packetfu/protos/tcp/header.rb +307 -0
- data/lib/packetfu/protos/tcp/hlen.rb +40 -0
- data/lib/packetfu/protos/tcp/mixin.rb +48 -0
- data/lib/packetfu/protos/tcp/option.rb +323 -0
- data/lib/packetfu/protos/tcp/options.rb +106 -0
- data/lib/packetfu/protos/tcp/reserved.rb +42 -0
- data/lib/packetfu/protos/udp.rb +12 -110
- data/lib/packetfu/protos/udp/header.rb +107 -0
- data/lib/packetfu/protos/udp/mixin.rb +23 -0
- data/lib/packetfu/utils.rb +24 -24
- data/lib/packetfu/version.rb +1 -1
- data/packetfu.gemspec +2 -2
- data/test/test_ip.rb +0 -19
- data/test/test_octets.rb +18 -21
- data/test/test_tcp.rb +10 -0
- data/test/test_udp.rb +17 -0
- metadata +79 -50
@@ -0,0 +1,190 @@
|
|
1
|
+
module PacketFu
|
2
|
+
# AddrIpv6 handles addressing for IPv6Header
|
3
|
+
#
|
4
|
+
# ==== Header Definition
|
5
|
+
#
|
6
|
+
# Int32 :a1
|
7
|
+
# Int32 :a2
|
8
|
+
# Int32 :a3
|
9
|
+
# Int32 :a4
|
10
|
+
class AddrIpv6 < Struct.new(:a1, :a2, :a3, :a4)
|
11
|
+
|
12
|
+
include StructFu
|
13
|
+
|
14
|
+
def initialize(args={})
|
15
|
+
super(
|
16
|
+
Int32.new(args[:a1]),
|
17
|
+
Int32.new(args[:a2]),
|
18
|
+
Int32.new(args[:a3]),
|
19
|
+
Int32.new(args[:a4]))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the address in string format.
|
23
|
+
def to_s
|
24
|
+
self.to_a.map {|x| x.to_s}.join
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the address as a fairly ginormous integer.
|
28
|
+
def to_i
|
29
|
+
(a1.to_i << 96) + (a2.to_i << 64) + (a3.to_i << 32) + a4.to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the address as a colon-delimited hex string.
|
33
|
+
def to_x
|
34
|
+
IPAddr.new(self.to_i, Socket::AF_INET6).to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
# Reads in a string and casts it as an IPv6 address
|
38
|
+
def read(str)
|
39
|
+
force_binary(str)
|
40
|
+
return self if str.nil?
|
41
|
+
self[:a1].read str[0,4]
|
42
|
+
self[:a2].read str[4,4]
|
43
|
+
self[:a3].read str[8,4]
|
44
|
+
self[:a4].read str[12,4]
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# Reads in a colon-delimited hex string and casts it as an IPv6 address.
|
49
|
+
def read_x(str)
|
50
|
+
addr = IPAddr.new(str).to_i
|
51
|
+
self[:a1]=Int32.new(addr >> 96)
|
52
|
+
self[:a2]=Int32.new((addr & 0x00000000ffffffff0000000000000000) >> 64)
|
53
|
+
self[:a3]=Int32.new((addr & 0x0000000000000000ffffffff00000000) >> 32)
|
54
|
+
self[:a4]=Int32.new(addr & 0x000000000000000000000000ffffffff)
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
# IPv6Header is complete IPv6 struct, used in IPv6Packet.
|
61
|
+
#
|
62
|
+
# ==== Header Definition
|
63
|
+
#
|
64
|
+
# Fixnum (4 bits) :ipv6_v Default: 6 # Versiom
|
65
|
+
# Fixnum (8 bits) :ipv6_class Defualt: 0 # Class
|
66
|
+
# Fixnum (20 bits) :ipv6_label Defualt: 0 # Label
|
67
|
+
# Int16 :ipv6_len Default: calc # Payload length
|
68
|
+
# Int8 :ipv6_next # Next Header
|
69
|
+
# Int8 :ipv6_hop Default: 0xff # Hop limit
|
70
|
+
# AddrIpv6 :ipv6_src
|
71
|
+
# AddrIpv6 :ipv6_dst
|
72
|
+
# String :body
|
73
|
+
class IPv6Header < Struct.new(:ipv6_v, :ipv6_class, :ipv6_label,
|
74
|
+
:ipv6_len, :ipv6_next, :ipv6_hop,
|
75
|
+
:ipv6_src, :ipv6_dst, :body)
|
76
|
+
include StructFu
|
77
|
+
|
78
|
+
def initialize(args={})
|
79
|
+
super(
|
80
|
+
(args[:ipv6_v] || 6),
|
81
|
+
(args[:ipv6_class] || 0),
|
82
|
+
(args[:ipv6_label] || 0),
|
83
|
+
Int16.new(args[:ipv6_len]),
|
84
|
+
Int8.new(args[:ipv6_next]),
|
85
|
+
Int8.new(args[:ipv6_hop] || 0xff),
|
86
|
+
AddrIpv6.new.read(args[:ipv6_src] || ("\x00" * 16)),
|
87
|
+
AddrIpv6.new.read(args[:ipv6_dst] || ("\x00" * 16)),
|
88
|
+
StructFu::String.new.read(args[:body])
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the object in string form.
|
93
|
+
def to_s
|
94
|
+
bytes_v_class_label = [(self.ipv6_v << 28) +
|
95
|
+
(self.ipv6_class << 20) +
|
96
|
+
self.ipv6_label].pack("N")
|
97
|
+
bytes_v_class_label + (self.to_a[3,6].map {|x| x.to_s}.join)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Reads a string to populate the object.
|
101
|
+
def read(str)
|
102
|
+
force_binary(str)
|
103
|
+
return self if str.nil?
|
104
|
+
self[:ipv6_v] = str[0,1].unpack("C").first >> 4
|
105
|
+
self[:ipv6_class] = (str[0,2].unpack("n").first & 0x0ff0) >> 4
|
106
|
+
self[:ipv6_label] = str[0,4].unpack("N").first & 0x000fffff
|
107
|
+
self[:ipv6_len].read(str[4,2])
|
108
|
+
self[:ipv6_next].read(str[6,1])
|
109
|
+
self[:ipv6_hop].read(str[7,1])
|
110
|
+
self[:ipv6_src].read(str[8,16])
|
111
|
+
self[:ipv6_dst].read(str[24,16])
|
112
|
+
self[:body].read(str[40,str.size]) if str.size > 40
|
113
|
+
self
|
114
|
+
end
|
115
|
+
|
116
|
+
# Setter for the version (usually, 6).
|
117
|
+
def ipv6_v=(i); self[:ip_v] = i.to_i; end
|
118
|
+
# Getter for the version (usually, 6).
|
119
|
+
def ipv6_v; self[:ipv6_v].to_i; end
|
120
|
+
# Setter for the traffic class.
|
121
|
+
def ipv6_class=(i); self[:ip_class] = i.to_i; end
|
122
|
+
# Getter for the traffic class.
|
123
|
+
def ipv6_class; self[:ipv6_class].to_i; end
|
124
|
+
# Setter for the flow label.
|
125
|
+
def ipv6_label=(i); self[:ip_label] = i.to_i; end
|
126
|
+
# Getter for the flow label.
|
127
|
+
def ipv6_label; self[:ipv6_label].to_i; end
|
128
|
+
# Setter for the payload length.
|
129
|
+
def ipv6_len=(i); typecast i; end
|
130
|
+
# Getter for the payload length.
|
131
|
+
def ipv6_len; self[:ipv6_len].to_i; end
|
132
|
+
# Setter for the next protocol header.
|
133
|
+
def ipv6_next=(i); typecast i; end
|
134
|
+
# Getter for the next protocol header.
|
135
|
+
def ipv6_next; self[:ipv6_next].to_i; end
|
136
|
+
# Setter for the hop limit.
|
137
|
+
def ipv6_hop=(i); typecast i; end
|
138
|
+
# Getter for the hop limit.
|
139
|
+
def ipv6_hop; self[:ipv6_hop].to_i; end
|
140
|
+
# Setter for the source address.
|
141
|
+
def ipv6_src=(i); typecast i; end
|
142
|
+
# Getter for the source address.
|
143
|
+
def ipv6_src; self[:ipv6_src].to_i; end
|
144
|
+
# Setter for the destination address.
|
145
|
+
def ipv6_dst=(i); typecast i; end
|
146
|
+
# Getter for the destination address.
|
147
|
+
def ipv6_dst; self[:ipv6_dst].to_i; end
|
148
|
+
|
149
|
+
# Calculates the payload length.
|
150
|
+
def ipv6_calc_len
|
151
|
+
self[:ipv6_len] = body.to_s.length
|
152
|
+
end
|
153
|
+
|
154
|
+
# Recalculates the calculatable fields for this object.
|
155
|
+
def ipv6_recalc(arg=:all)
|
156
|
+
case arg
|
157
|
+
when :ipv6_len
|
158
|
+
ipv6_calc_len
|
159
|
+
when :all
|
160
|
+
ipv6_recalc(:len)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Get the source address in a more readable form.
|
165
|
+
def ipv6_saddr
|
166
|
+
self[:ipv6_src].to_x
|
167
|
+
end
|
168
|
+
|
169
|
+
# Set the source address in a more readable form.
|
170
|
+
def ipv6_saddr=(str)
|
171
|
+
self[:ipv6_src].read_x(str)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Get the destination address in a more readable form.
|
175
|
+
def ipv6_daddr
|
176
|
+
self[:ipv6_dst].to_x
|
177
|
+
end
|
178
|
+
|
179
|
+
# Set the destination address in a more readable form.
|
180
|
+
def ipv6_daddr=(str)
|
181
|
+
self[:ipv6_dst].read_x(str)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Readability aliases
|
185
|
+
|
186
|
+
alias :ipv6_src_readable :ipv6_saddr
|
187
|
+
alias :ipv6_dst_readable :ipv6_daddr
|
188
|
+
|
189
|
+
end # class IPv6Header
|
190
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module PacketFu
|
2
|
+
# This Mixin simplifies access to the IPv6Headers. Mix this in with your
|
3
|
+
# packet interface, and it will add methods that essentially delegate to
|
4
|
+
# the 'ipv6_header' method (assuming that it is a IPv6Header object)
|
5
|
+
module IPv6HeaderMixin
|
6
|
+
def ipv6_v=(v); self.ipv6_header.ipv6_v= v; end
|
7
|
+
def ipv6_v; self.ipv6_header.ipv6_v; end
|
8
|
+
def ipv6_class=(v); self.ipv6_header.ipv6_class= v; end
|
9
|
+
def ipv6_class; self.ipv6_header.ipv6_class; end
|
10
|
+
def ipv6_label=(v); self.ipv6_header.ipv6_label= v; end
|
11
|
+
def ipv6_label; self.ipv6_header.ipv6_label; end
|
12
|
+
def ipv6_len=(v); self.ipv6_header.ipv6_len= v; end
|
13
|
+
def ipv6_len; self.ipv6_header.ipv6_len; end
|
14
|
+
def ipv6_next=(v); self.ipv6_header.ipv6_next= v; end
|
15
|
+
def ipv6_next; self.ipv6_header.ipv6_next; end
|
16
|
+
def ipv6_hop=(v); self.ipv6_header.ipv6_hop= v; end
|
17
|
+
def ipv6_hop; self.ipv6_header.ipv6_hop; end
|
18
|
+
def ipv6_src=(v); self.ipv6_header.ipv6_src= v; end
|
19
|
+
def ipv6_src; self.ipv6_header.ipv6_src; end
|
20
|
+
def ipv6_dst=(v); self.ipv6_header.ipv6_dst= v; end
|
21
|
+
def ipv6_dst; self.ipv6_header.ipv6_dst; end
|
22
|
+
def ipv6_calc_len; self.ipv6_header.ipv6_calc_len; end
|
23
|
+
def ipv6_recalc(*v); self.ipv6_header.ipv6_recalc(*v); end
|
24
|
+
def ipv6_saddr; self.ipv6_header.ipv6_saddr; end
|
25
|
+
def ipv6_saddr=(v); self.ipv6_header.ipv6_saddr= v; end
|
26
|
+
def ipv6_daddr; self.ipv6_header.ipv6_daddr; end
|
27
|
+
def ipv6_daddr=(v); self.ipv6_header.ipv6_daddr= v; end
|
28
|
+
def ipv6_src_readable; self.ipv6_header.ipv6_src_readable; end
|
29
|
+
def ipv6_dst_readable; self.ipv6_header.ipv6_dst_readable; end
|
30
|
+
end
|
31
|
+
end
|
data/lib/packetfu/protos/tcp.rb
CHANGED
@@ -1,939 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Implements the Explict Congestion Notification for TCPHeader.
|
4
|
-
#
|
5
|
-
# ==== Header Definition
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# Fixnum (1 bit) :n
|
9
|
-
# Fixnum (1 bit) :c
|
10
|
-
# Fixnum (1 bit) :e
|
11
|
-
class TcpEcn < Struct.new(:n, :c, :e)
|
12
|
-
|
13
|
-
include StructFu
|
14
|
-
|
15
|
-
def initialize(args={})
|
16
|
-
super(args[:n], args[:c], args[:e]) if args
|
17
|
-
end
|
18
|
-
|
19
|
-
# Returns the TcpEcn field as an integer... even though it's going
|
20
|
-
# to be split across a byte boundary.
|
21
|
-
def to_i
|
22
|
-
(n.to_i << 2) + (c.to_i << 1) + e.to_i
|
23
|
-
end
|
24
|
-
|
25
|
-
# Reads a string to populate the object.
|
26
|
-
def read(str)
|
27
|
-
force_binary(str)
|
28
|
-
return self if str.nil? || str.size < 2
|
29
|
-
if 1.respond_to? :ord
|
30
|
-
byte1 = str[0].ord
|
31
|
-
byte2 = str[1].ord
|
32
|
-
else
|
33
|
-
byte1 = str[0]
|
34
|
-
byte2 = str[1]
|
35
|
-
end
|
36
|
-
self[:n] = byte1 & 0b00000001 == 0b00000001 ? 1 : 0
|
37
|
-
self[:c] = byte2 & 0b10000000 == 0b10000000 ? 1 : 0
|
38
|
-
self[:e] = byte2 & 0b01000000 == 0b01000000 ? 1 : 0
|
39
|
-
self
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
# Implements the Header Length for TCPHeader.
|
45
|
-
#
|
46
|
-
# ==== Header Definition
|
47
|
-
#
|
48
|
-
# Fixnum (4 bits) :hlen
|
49
|
-
class TcpHlen < Struct.new(:hlen)
|
50
|
-
|
51
|
-
include StructFu
|
52
|
-
|
53
|
-
def initialize(args={})
|
54
|
-
super(args[:hlen])
|
55
|
-
end
|
56
|
-
|
57
|
-
# Returns the TcpHlen field as an integer. Note these will become the high
|
58
|
-
# bits at the TCP header's offset, even though the lower 4 bits
|
59
|
-
# will be further chopped up.
|
60
|
-
def to_i
|
61
|
-
hlen.to_i & 0b1111
|
62
|
-
end
|
63
|
-
|
64
|
-
# Reads a string to populate the object.
|
65
|
-
def read(str)
|
66
|
-
force_binary(str)
|
67
|
-
return self if str.nil? || str.size.zero?
|
68
|
-
if 1.respond_to? :ord
|
69
|
-
self[:hlen] = (str[0].ord & 0b11110000) >> 4
|
70
|
-
else
|
71
|
-
self[:hlen] = (str[0] & 0b11110000) >> 4
|
72
|
-
end
|
73
|
-
self
|
74
|
-
end
|
75
|
-
|
76
|
-
# Returns the object in string form.
|
77
|
-
def to_s
|
78
|
-
[self.to_i].pack("C")
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
# Implements the Reserved bits for TCPHeader.
|
84
|
-
#
|
85
|
-
# ==== Header Definition
|
86
|
-
#
|
87
|
-
#
|
88
|
-
# Fixnum (1 bit) :r1
|
89
|
-
# Fixnum (1 bit) :r2
|
90
|
-
# Fixnum (1 bit) :r3
|
91
|
-
class TcpReserved < Struct.new(:r1, :r2, :r3)
|
92
|
-
|
93
|
-
include StructFu
|
94
|
-
|
95
|
-
def initialize(args={})
|
96
|
-
super(
|
97
|
-
args[:r1] || 0,
|
98
|
-
args[:r2] || 0,
|
99
|
-
args[:r3] || 0) if args.kind_of? Hash
|
100
|
-
end
|
101
|
-
|
102
|
-
# Returns the Reserved field as an integer.
|
103
|
-
def to_i
|
104
|
-
(r1.to_i << 2) + (r2.to_i << 1) + r3.to_i
|
105
|
-
end
|
106
|
-
|
107
|
-
# Reads a string to populate the object.
|
108
|
-
def read(str)
|
109
|
-
force_binary(str)
|
110
|
-
return self if str.nil? || str.size.zero?
|
111
|
-
if 1.respond_to? :ord
|
112
|
-
byte = str[0].ord
|
113
|
-
else
|
114
|
-
byte = str[0]
|
115
|
-
end
|
116
|
-
self[:r1] = byte & 0b00000100 == 0b00000100 ? 1 : 0
|
117
|
-
self[:r2] = byte & 0b00000010 == 0b00000010 ? 1 : 0
|
118
|
-
self[:r3] = byte & 0b00000001 == 0b00000001 ? 1 : 0
|
119
|
-
self
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
# Implements flags for TCPHeader.
|
125
|
-
#
|
126
|
-
# ==== Header Definition
|
127
|
-
#
|
128
|
-
# Fixnum (1 bit) :urg
|
129
|
-
# Fixnum (1 bit) :ack
|
130
|
-
# Fixnum (1 bit) :psh
|
131
|
-
# Fixnum (1 bit) :rst
|
132
|
-
# Fixnum (1 bit) :syn
|
133
|
-
# Fixnum (1 bit) :fin
|
134
|
-
#
|
135
|
-
# Flags can typically be set by setting them either to 1 or 0, or to true or false.
|
136
|
-
class TcpFlags < Struct.new(:urg, :ack, :psh, :rst, :syn, :fin)
|
137
|
-
|
138
|
-
include StructFu
|
139
|
-
|
140
|
-
def initialize(args={})
|
141
|
-
# This technique attemts to ensure that flags are always 0 (off)
|
142
|
-
# or 1 (on). Statements like nil and false shouldn't be lurking in here.
|
143
|
-
if args.nil? || args.size.zero?
|
144
|
-
super( 0, 0, 0, 0, 0, 0)
|
145
|
-
else
|
146
|
-
super(
|
147
|
-
(args[:urg] ? 1 : 0),
|
148
|
-
(args[:ack] ? 1 : 0),
|
149
|
-
(args[:psh] ? 1 : 0),
|
150
|
-
(args[:rst] ? 1 : 0),
|
151
|
-
(args[:syn] ? 1 : 0),
|
152
|
-
(args[:fin] ? 1 : 0)
|
153
|
-
)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
# Returns the TcpFlags as an integer.
|
158
|
-
# Also not a great candidate for to_s due to the short bitspace.
|
159
|
-
def to_i
|
160
|
-
(urg.to_i << 5) + (ack.to_i << 4) + (psh.to_i << 3) +
|
161
|
-
(rst.to_i << 2) + (syn.to_i << 1) + fin.to_i
|
162
|
-
end
|
163
|
-
|
164
|
-
# Helper to determine if this flag is a 1 or a 0.
|
165
|
-
def zero_or_one(i=0)
|
166
|
-
if i == 0 || i == false || i == nil
|
167
|
-
0
|
168
|
-
else
|
169
|
-
1
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# Setter for the Urgent flag.
|
174
|
-
def urg=(i); self[:urg] = zero_or_one(i); end
|
175
|
-
# Setter for the Acknowlege flag.
|
176
|
-
def ack=(i); self[:ack] = zero_or_one(i); end
|
177
|
-
# Setter for the Push flag.
|
178
|
-
def psh=(i); self[:psh] = zero_or_one(i); end
|
179
|
-
# Setter for the Reset flag.
|
180
|
-
def rst=(i); self[:rst] = zero_or_one(i); end
|
181
|
-
# Setter for the Synchronize flag.
|
182
|
-
def syn=(i); self[:syn] = zero_or_one(i); end
|
183
|
-
# Setter for the Finish flag.
|
184
|
-
def fin=(i); self[:fin] = zero_or_one(i); end
|
185
|
-
|
186
|
-
# Reads a string to populate the object.
|
187
|
-
def read(str)
|
188
|
-
force_binary(str)
|
189
|
-
return self if str.nil?
|
190
|
-
if 1.respond_to? :ord
|
191
|
-
byte = str[0].ord
|
192
|
-
else
|
193
|
-
byte = str[0]
|
194
|
-
end
|
195
|
-
self[:urg] = byte & 0b00100000 == 0b00100000 ? 1 : 0
|
196
|
-
self[:ack] = byte & 0b00010000 == 0b00010000 ? 1 : 0
|
197
|
-
self[:psh] = byte & 0b00001000 == 0b00001000 ? 1 : 0
|
198
|
-
self[:rst] = byte & 0b00000100 == 0b00000100 ? 1 : 0
|
199
|
-
self[:syn] = byte & 0b00000010 == 0b00000010 ? 1 : 0
|
200
|
-
self[:fin] = byte & 0b00000001 == 0b00000001 ? 1 : 0
|
201
|
-
self
|
202
|
-
end
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
end
|
207
|
-
|
208
|
-
module PacketFu
|
209
|
-
|
210
|
-
# TcpOption is the base class for all TCP options. Note that TcpOption#len
|
211
|
-
# returns the size of the entire option, while TcpOption#optlen is the struct
|
212
|
-
# for the TCP Option Length field.
|
213
|
-
#
|
214
|
-
# Subclassed options should set the correct TcpOption#kind by redefining
|
215
|
-
# initialize. They should also deal with various value types there by setting
|
216
|
-
# them explicitly with an accompanying StructFu#typecast for the setter.
|
217
|
-
#
|
218
|
-
# By default, values are presumed to be strings, unless they are Numeric, in
|
219
|
-
# which case a guess is made to the width of the Numeric based on the given
|
220
|
-
# optlen.
|
221
|
-
#
|
222
|
-
# Note that normally, optlen is /not/ enforced for directly setting values,
|
223
|
-
# so the user is perfectly capable of setting incorrect lengths.
|
224
|
-
class TcpOption < Struct.new(:kind, :optlen, :value)
|
225
|
-
|
226
|
-
include StructFu
|
227
|
-
|
228
|
-
def initialize(args={})
|
229
|
-
super(
|
230
|
-
Int8.new(args[:kind]),
|
231
|
-
Int8.new(args[:optlen])
|
232
|
-
)
|
233
|
-
if args[:value].kind_of? Numeric
|
234
|
-
self[:value] = case args[:optlen]
|
235
|
-
when 3; Int8.new(args[:value])
|
236
|
-
when 4; Int16.new(args[:value])
|
237
|
-
when 6; Int32.new(args[:value])
|
238
|
-
else; StructFu::String.new.read(args[:value])
|
239
|
-
end
|
240
|
-
else
|
241
|
-
self[:value] = StructFu::String.new.read(args[:value])
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
# Returns the object in string form.
|
246
|
-
def to_s
|
247
|
-
self[:kind].to_s +
|
248
|
-
(self[:optlen].value.nil? ? nil : self[:optlen]).to_s +
|
249
|
-
(self[:value].nil? ? nil : self[:value]).to_s
|
250
|
-
end
|
251
|
-
|
252
|
-
# Reads a string to populate the object.
|
253
|
-
def read(str)
|
254
|
-
force_binary(str)
|
255
|
-
return self if str.nil?
|
256
|
-
self[:kind].read(str[0,1])
|
257
|
-
if str[1,1]
|
258
|
-
self[:optlen].read(str[1,1])
|
259
|
-
if str[2,1] && optlen.value > 2
|
260
|
-
self[:value].read(str[2,optlen.value-2])
|
261
|
-
end
|
262
|
-
end
|
263
|
-
self
|
264
|
-
end
|
265
|
-
|
266
|
-
# The default decode for an unknown option. Known options should redefine this.
|
267
|
-
def decode
|
268
|
-
unk = "unk-#{self.kind.to_i}"
|
269
|
-
(self[:optlen].to_i > 2 && self[:value].to_s.size > 1) ? [unk,self[:value]].join(":") : unk
|
270
|
-
end
|
271
|
-
|
272
|
-
# Setter for the "kind" byte of this option.
|
273
|
-
def kind=(i); typecast i; end
|
274
|
-
# Setter for the "option length" byte for this option.
|
275
|
-
def optlen=(i); typecast i; end
|
276
|
-
|
277
|
-
# Setter for the value of this option.
|
278
|
-
def value=(i)
|
279
|
-
if i.kind_of? Numeric
|
280
|
-
typecast i
|
281
|
-
elsif i.respond_to? :to_s
|
282
|
-
self[:value] = i
|
283
|
-
else
|
284
|
-
self[:value] = ''
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
# Generally, encoding a value is going to be just a read. Some
|
289
|
-
# options will treat things a little differently; TS for example,
|
290
|
-
# takes two values and concatenates them.
|
291
|
-
def encode(str)
|
292
|
-
self[:value] = self.class.new(:value => str).value
|
293
|
-
end
|
294
|
-
|
295
|
-
# Returns true if this option has an optlen. Some don't.
|
296
|
-
def has_optlen?
|
297
|
-
(kind.value && kind.value < 2) ? false : true
|
298
|
-
end
|
299
|
-
|
300
|
-
# Returns true if this option has a value. Some don't.
|
301
|
-
def has_value?
|
302
|
-
(value.respond_to? :to_s && value.to_s.size > 0) ? false : true
|
303
|
-
end
|
304
|
-
|
305
|
-
# End of Line option. Usually used to terminate a string of options.
|
306
|
-
#
|
307
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option000.htm
|
308
|
-
class EOL < TcpOption
|
309
|
-
def initialize(args={})
|
310
|
-
super(
|
311
|
-
args.merge(:kind => 0)
|
312
|
-
)
|
313
|
-
end
|
314
|
-
|
315
|
-
def decode
|
316
|
-
"EOL"
|
317
|
-
end
|
318
|
-
|
319
|
-
end
|
320
|
-
|
321
|
-
# No Operation option. Usually used to pad out options to fit a 4-byte alignment.
|
322
|
-
#
|
323
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option001.htm
|
324
|
-
class NOP < TcpOption
|
325
|
-
def initialize(args={})
|
326
|
-
super(
|
327
|
-
args.merge(:kind => 1)
|
328
|
-
)
|
329
|
-
end
|
330
|
-
|
331
|
-
def decode
|
332
|
-
"NOP"
|
333
|
-
end
|
334
|
-
|
335
|
-
end
|
336
|
-
|
337
|
-
# Maximum Segment Size option.
|
338
|
-
#
|
339
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option002.htm
|
340
|
-
class MSS < TcpOption
|
341
|
-
def initialize(args={})
|
342
|
-
super(
|
343
|
-
args.merge(:kind => 2,
|
344
|
-
:optlen => 4
|
345
|
-
)
|
346
|
-
)
|
347
|
-
self[:value] = Int16.new(args[:value])
|
348
|
-
end
|
349
|
-
|
350
|
-
def value=(i); typecast i; end
|
351
|
-
|
352
|
-
# MSS options with lengths other than 4 are malformed.
|
353
|
-
def decode
|
354
|
-
if self[:optlen].to_i == 4
|
355
|
-
"MSS:#{self[:value].to_i}"
|
356
|
-
else
|
357
|
-
"MSS-bad:#{self[:value]}"
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
end
|
362
|
-
|
363
|
-
# Window Size option.
|
364
|
-
#
|
365
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option003.htm
|
366
|
-
class WS < TcpOption
|
367
|
-
def initialize(args={})
|
368
|
-
super(
|
369
|
-
args.merge(:kind => 3,
|
370
|
-
:optlen => 3
|
371
|
-
)
|
372
|
-
)
|
373
|
-
self[:value] = Int8.new(args[:value])
|
374
|
-
end
|
375
|
-
|
376
|
-
def value=(i); typecast i; end
|
377
|
-
|
378
|
-
# WS options with lengths other than 3 are malformed.
|
379
|
-
def decode
|
380
|
-
if self[:optlen].to_i == 3
|
381
|
-
"WS:#{self[:value].to_i}"
|
382
|
-
else
|
383
|
-
"WS-bad:#{self[:value]}"
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
end
|
388
|
-
|
389
|
-
# Selective Acknowlegment OK option.
|
390
|
-
#
|
391
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option004.htm
|
392
|
-
class SACKOK < TcpOption
|
393
|
-
def initialize(args={})
|
394
|
-
super(
|
395
|
-
args.merge(:kind => 4,
|
396
|
-
:optlen => 2)
|
397
|
-
)
|
398
|
-
end
|
399
|
-
|
400
|
-
# SACKOK options with sizes other than 2 are malformed.
|
401
|
-
def decode
|
402
|
-
if self[:optlen].to_i == 2
|
403
|
-
"SACKOK"
|
404
|
-
else
|
405
|
-
"SACKOK-bad:#{self[:value]}"
|
406
|
-
end
|
407
|
-
end
|
1
|
+
require 'packetfu/protos/eth/header'
|
2
|
+
require 'packetfu/protos/eth/mixin'
|
408
3
|
|
409
|
-
|
410
|
-
|
411
|
-
# Selective Acknowledgement option.
|
412
|
-
#
|
413
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option004.htm
|
414
|
-
#
|
415
|
-
# Note that SACK always takes its optlen from the size of the string.
|
416
|
-
class SACK < TcpOption
|
417
|
-
def initialize(args={})
|
418
|
-
super(
|
419
|
-
args.merge(:kind => 5,
|
420
|
-
:optlen => ((args[:value] || "").size + 2)
|
421
|
-
)
|
422
|
-
)
|
423
|
-
end
|
424
|
-
|
425
|
-
def optlen=(i); typecast i; end
|
426
|
-
|
427
|
-
def value=(i)
|
428
|
-
self[:optlen] = Int8.new(i.to_s.size + 2)
|
429
|
-
self[:value] = StructFu::String.new(i)
|
430
|
-
end
|
431
|
-
|
432
|
-
def decode
|
433
|
-
"SACK:#{self[:value]}"
|
434
|
-
end
|
435
|
-
|
436
|
-
def encode(str)
|
437
|
-
temp_obj = self.class.new(:value => str)
|
438
|
-
self[:value] = temp_obj.value
|
439
|
-
self[:optlen] = temp_obj.optlen.value
|
440
|
-
self
|
441
|
-
end
|
442
|
-
|
443
|
-
end
|
444
|
-
|
445
|
-
# Echo option.
|
446
|
-
#
|
447
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option006.htm
|
448
|
-
class ECHO < TcpOption
|
449
|
-
def initialize(args={})
|
450
|
-
super(
|
451
|
-
args.merge(:kind => 6,
|
452
|
-
:optlen => 6
|
453
|
-
)
|
454
|
-
)
|
455
|
-
end
|
456
|
-
|
457
|
-
# ECHO options with lengths other than 6 are malformed.
|
458
|
-
def decode
|
459
|
-
if self[:optlen].to_i == 6
|
460
|
-
"ECHO:#{self[:value]}"
|
461
|
-
else
|
462
|
-
"ECHO-bad:#{self[:value]}"
|
463
|
-
end
|
464
|
-
end
|
465
|
-
|
466
|
-
end
|
467
|
-
|
468
|
-
# Echo Reply option.
|
469
|
-
#
|
470
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option007.htm
|
471
|
-
class ECHOREPLY < TcpOption
|
472
|
-
def initialize(args={})
|
473
|
-
super(
|
474
|
-
args.merge(:kind => 7,
|
475
|
-
:optlen => 6
|
476
|
-
)
|
477
|
-
)
|
478
|
-
end
|
479
|
-
|
480
|
-
# ECHOREPLY options with lengths other than 6 are malformed.
|
481
|
-
def decode
|
482
|
-
if self[:optlen].to_i == 6
|
483
|
-
"ECHOREPLY:#{self[:value]}"
|
484
|
-
else
|
485
|
-
"ECHOREPLY-bad:#{self[:value]}"
|
486
|
-
end
|
487
|
-
end
|
488
|
-
|
489
|
-
end
|
490
|
-
|
491
|
-
# Timestamp option
|
492
|
-
#
|
493
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option008.htm
|
494
|
-
class TS < TcpOption
|
495
|
-
def initialize(args={})
|
496
|
-
super(
|
497
|
-
args.merge(:kind => 8,
|
498
|
-
:optlen => 10
|
499
|
-
)
|
500
|
-
)
|
501
|
-
self[:value] = StructFu::String.new.read(args[:value] || "\x00" * 8)
|
502
|
-
end
|
503
|
-
|
504
|
-
# TS options with lengths other than 10 are malformed.
|
505
|
-
def decode
|
506
|
-
if self[:optlen].to_i == 10
|
507
|
-
val1,val2 = self[:value].unpack("NN")
|
508
|
-
"TS:#{val1};#{val2}"
|
509
|
-
else
|
510
|
-
"TS-bad:#{self[:value]}"
|
511
|
-
end
|
512
|
-
end
|
513
|
-
|
514
|
-
# TS options are in the format of "TS:[timestamp value];[timestamp secret]" Both
|
515
|
-
# should be written as decimal numbers.
|
516
|
-
def encode(str)
|
517
|
-
if str =~ /^([0-9]+);([0-9]+)$/
|
518
|
-
tsval,tsecr = str.split(";").map {|x| x.to_i}
|
519
|
-
if tsval <= 0xffffffff && tsecr <= 0xffffffff
|
520
|
-
self[:value] = StructFu::String.new([tsval,tsecr].pack("NN"))
|
521
|
-
else
|
522
|
-
self[:value] = StructFu::String.new(str)
|
523
|
-
end
|
524
|
-
else
|
525
|
-
self[:value] = StructFu::String.new(str)
|
526
|
-
end
|
527
|
-
end
|
528
|
-
|
529
|
-
end
|
530
|
-
|
531
|
-
end
|
532
|
-
|
533
|
-
class TcpOptions < Array
|
534
|
-
|
535
|
-
include StructFu
|
536
|
-
|
537
|
-
# If args[:pad] is set, the options line is automatically padded out
|
538
|
-
# with NOPs.
|
539
|
-
def to_s(args={})
|
540
|
-
opts = self.map {|x| x.to_s}.join
|
541
|
-
if args[:pad]
|
542
|
-
unless (opts.size % 4).zero?
|
543
|
-
(4 - (opts.size % 4)).times { opts << "\x01" }
|
544
|
-
end
|
545
|
-
end
|
546
|
-
opts
|
547
|
-
end
|
548
|
-
|
549
|
-
# Reads a string to populate the object.
|
550
|
-
def read(str)
|
551
|
-
self.clear
|
552
|
-
PacketFu.force_binary(str)
|
553
|
-
return self if(!str.respond_to? :to_s || str.nil?)
|
554
|
-
i = 0
|
555
|
-
while i < str.to_s.size
|
556
|
-
this_opt = case str[i,1].unpack("C").first
|
557
|
-
when 0; TcpOption::EOL.new
|
558
|
-
when 1; TcpOption::NOP.new
|
559
|
-
when 2; TcpOption::MSS.new
|
560
|
-
when 3; TcpOption::WS.new
|
561
|
-
when 4; TcpOption::SACKOK.new
|
562
|
-
when 5; TcpOption::SACK.new
|
563
|
-
when 6; TcpOption::ECHO.new
|
564
|
-
when 7; TcpOption::ECHOREPLY.new
|
565
|
-
when 8; TcpOption::TS.new
|
566
|
-
else; TcpOption.new
|
567
|
-
end
|
568
|
-
this_opt.read str[i,str.size]
|
569
|
-
unless this_opt.has_optlen?
|
570
|
-
this_opt.value = nil
|
571
|
-
this_opt.optlen = nil
|
572
|
-
end
|
573
|
-
self << this_opt
|
574
|
-
i += this_opt.sz
|
575
|
-
end
|
576
|
-
self
|
577
|
-
end
|
578
|
-
|
579
|
-
# Decode parses the TcpOptions object's member options, and produces a
|
580
|
-
# human-readable string by iterating over each element's decode() function.
|
581
|
-
# If TcpOptions elements were not initially created as TcpOptions, an
|
582
|
-
# attempt will be made to convert them.
|
583
|
-
#
|
584
|
-
# The output of decode is suitable as input for TcpOptions#encode.
|
585
|
-
def decode
|
586
|
-
decoded = self.map do |x|
|
587
|
-
if x.kind_of? TcpOption
|
588
|
-
x.decode
|
589
|
-
else
|
590
|
-
x = TcpOptions.new.read(x).decode
|
591
|
-
end
|
592
|
-
end
|
593
|
-
decoded.join(",")
|
594
|
-
end
|
595
|
-
|
596
|
-
# Encode takes a human-readable string and appends the corresponding
|
597
|
-
# binary options to the TcpOptions object. To completely replace the contents
|
598
|
-
# of the object, use TcpOptions#encode! instead.
|
599
|
-
#
|
600
|
-
# Options are comma-delimited, and are identical to the output of the
|
601
|
-
# TcpOptions#decode function. Note that the syntax can be unforgiving, so
|
602
|
-
# it may be easier to create the subclassed TcpOptions themselves directly,
|
603
|
-
# but this method can be less typing if you know what you're doing.
|
604
|
-
#
|
605
|
-
# Note that by using TcpOptions#encode, strings supplied as values which
|
606
|
-
# can be converted to numbers will be converted first.
|
607
|
-
#
|
608
|
-
# === Example
|
609
|
-
#
|
610
|
-
# t = TcpOptions.new
|
611
|
-
# t.encode("MS:1460,WS:6")
|
612
|
-
# t.to_s # => "\002\004\005\264\002\003\006"
|
613
|
-
# t.encode("NOP")
|
614
|
-
# t.to_s # => "\002\004\005\264\002\003\006\001"
|
615
|
-
def encode(str)
|
616
|
-
opts = str.split(/[\s]*,[\s]*/)
|
617
|
-
opts.each do |o|
|
618
|
-
kind,value = o.split(/[\s]*:[\s]*/)
|
619
|
-
klass = TcpOption.const_get(kind.upcase)
|
620
|
-
value = value.to_i if value =~ /^[0-9]+$/
|
621
|
-
this_opt = klass.new
|
622
|
-
this_opt.encode(value)
|
623
|
-
self << this_opt
|
624
|
-
end
|
625
|
-
self
|
626
|
-
end
|
4
|
+
require 'packetfu/protos/tcp/header'
|
5
|
+
require 'packetfu/protos/tcp/mixin'
|
627
6
|
|
628
|
-
|
629
|
-
|
630
|
-
self.clear if self.size > 0
|
631
|
-
encode(str)
|
632
|
-
end
|
633
|
-
|
634
|
-
end
|
635
|
-
|
636
|
-
end
|
7
|
+
require 'packetfu/protos/ip/header'
|
8
|
+
require 'packetfu/protos/ip/mixin'
|
637
9
|
|
638
10
|
module PacketFu
|
639
|
-
|
640
|
-
# TCPHeader is a complete TCP struct, used in TCPPacket. Most IP traffic is TCP-based, by
|
641
|
-
# volume.
|
642
|
-
#
|
643
|
-
# For more on TCP packets, see http://www.networksorcery.com/enp/protocol/tcp.htm
|
644
|
-
#
|
645
|
-
# ==== Header Definition
|
646
|
-
#
|
647
|
-
# Int16 :tcp_src Default: random
|
648
|
-
# Int16 :tcp_dst
|
649
|
-
# Int32 :tcp_seq Default: random
|
650
|
-
# Int32 :tcp_ack
|
651
|
-
# TcpHlen :tcp_hlen Default: 5 # Must recalc as options are set.
|
652
|
-
# TcpReserved :tcp_reserved Default: 0
|
653
|
-
# TcpEcn :tcp_ecn
|
654
|
-
# TcpFlags :tcp_flags
|
655
|
-
# Int16 :tcp_win, Default: 0 # WinXP's default syn packet
|
656
|
-
# Int16 :tcp_sum, Default: calculated # Must set this upon generation.
|
657
|
-
# Int16 :tcp_urg
|
658
|
-
# TcpOptions :tcp_opts
|
659
|
-
# String :body
|
660
|
-
#
|
661
|
-
# See also TcpHlen, TcpReserved, TcpEcn, TcpFlags, TcpOpts
|
662
|
-
class TCPHeader < Struct.new(:tcp_src, :tcp_dst,
|
663
|
-
:tcp_seq,
|
664
|
-
:tcp_ack,
|
665
|
-
:tcp_hlen, :tcp_reserved, :tcp_ecn, :tcp_flags, :tcp_win,
|
666
|
-
:tcp_sum, :tcp_urg,
|
667
|
-
:tcp_opts, :body)
|
668
|
-
include StructFu
|
669
|
-
|
670
|
-
def initialize(args={})
|
671
|
-
@random_seq = rand(0xffffffff)
|
672
|
-
@random_src = rand_port
|
673
|
-
super(
|
674
|
-
Int16.new(args[:tcp_src] || tcp_calc_src),
|
675
|
-
Int16.new(args[:tcp_dst]),
|
676
|
-
Int32.new(args[:tcp_seq] || tcp_calc_seq),
|
677
|
-
Int32.new(args[:tcp_ack]),
|
678
|
-
TcpHlen.new(:hlen => (args[:tcp_hlen] || 5)),
|
679
|
-
TcpReserved.new(args[:tcp_reserved] || 0),
|
680
|
-
TcpEcn.new(args[:tcp_ecn]),
|
681
|
-
TcpFlags.new(args[:tcp_flags]),
|
682
|
-
Int16.new(args[:tcp_win] || 0x4000),
|
683
|
-
Int16.new(args[:tcp_sum] || 0),
|
684
|
-
Int16.new(args[:tcp_urg]),
|
685
|
-
TcpOptions.new.read(args[:tcp_opts]),
|
686
|
-
StructFu::String.new.read(args[:body])
|
687
|
-
)
|
688
|
-
end
|
689
|
-
|
690
|
-
attr_accessor :flavor
|
691
|
-
|
692
|
-
# Helper function to create the string for Hlen, Reserved, ECN, and Flags.
|
693
|
-
def bits_to_s
|
694
|
-
bytes = []
|
695
|
-
bytes[0] = (self[:tcp_hlen].to_i << 4) +
|
696
|
-
(self[:tcp_reserved].to_i << 1) +
|
697
|
-
self[:tcp_ecn].n.to_i
|
698
|
-
bytes[1] = (self[:tcp_ecn].c.to_i << 7) +
|
699
|
-
(self[:tcp_ecn].e.to_i << 6) +
|
700
|
-
self[:tcp_flags].to_i
|
701
|
-
bytes.pack("CC")
|
702
|
-
end
|
703
|
-
|
704
|
-
# Returns the object in string form.
|
705
|
-
def to_s
|
706
|
-
hdr = self.to_a.map do |x|
|
707
|
-
if x.kind_of? TcpHlen
|
708
|
-
bits_to_s
|
709
|
-
elsif x.kind_of? TcpReserved
|
710
|
-
next
|
711
|
-
elsif x.kind_of? TcpEcn
|
712
|
-
next
|
713
|
-
elsif x.kind_of? TcpFlags
|
714
|
-
next
|
715
|
-
else
|
716
|
-
x.to_s
|
717
|
-
end
|
718
|
-
end
|
719
|
-
hdr.flatten.join
|
720
|
-
end
|
721
|
-
|
722
|
-
# Reads a string to populate the object.
|
723
|
-
def read(str)
|
724
|
-
force_binary(str)
|
725
|
-
return self if str.nil?
|
726
|
-
self[:tcp_src].read(str[0,2])
|
727
|
-
self[:tcp_dst].read(str[2,2])
|
728
|
-
self[:tcp_seq].read(str[4,4])
|
729
|
-
self[:tcp_ack].read(str[8,4])
|
730
|
-
self[:tcp_hlen].read(str[12,1])
|
731
|
-
self[:tcp_reserved].read(str[12,1])
|
732
|
-
self[:tcp_ecn].read(str[12,2])
|
733
|
-
self[:tcp_flags].read(str[13,1])
|
734
|
-
self[:tcp_win].read(str[14,2])
|
735
|
-
self[:tcp_sum].read(str[16,2])
|
736
|
-
self[:tcp_urg].read(str[18,2])
|
737
|
-
self[:tcp_opts].read(str[20,((self[:tcp_hlen].to_i * 4) - 20)])
|
738
|
-
self[:body].read(str[(self[:tcp_hlen].to_i * 4),str.size])
|
739
|
-
self
|
740
|
-
end
|
741
|
-
|
742
|
-
# Setter for the TCP source port.
|
743
|
-
def tcp_src=(i); typecast i; end
|
744
|
-
# Getter for the TCP source port.
|
745
|
-
def tcp_src; self[:tcp_src].to_i; end
|
746
|
-
# Setter for the TCP destination port.
|
747
|
-
def tcp_dst=(i); typecast i; end
|
748
|
-
# Getter for the TCP destination port.
|
749
|
-
def tcp_dst; self[:tcp_dst].to_i; end
|
750
|
-
# Setter for the TCP sequence number.
|
751
|
-
def tcp_seq=(i); typecast i; end
|
752
|
-
# Getter for the TCP sequence number.
|
753
|
-
def tcp_seq; self[:tcp_seq].to_i; end
|
754
|
-
# Setter for the TCP ackowlegement number.
|
755
|
-
def tcp_ack=(i); typecast i; end
|
756
|
-
# Getter for the TCP ackowlegement number.
|
757
|
-
def tcp_ack; self[:tcp_ack].to_i; end
|
758
|
-
# Setter for the TCP window size number.
|
759
|
-
def tcp_win=(i); typecast i; end
|
760
|
-
# Getter for the TCP window size number.
|
761
|
-
def tcp_win; self[:tcp_win].to_i; end
|
762
|
-
# Setter for the TCP checksum.
|
763
|
-
def tcp_sum=(i); typecast i; end
|
764
|
-
# Getter for the TCP checksum.
|
765
|
-
def tcp_sum; self[:tcp_sum].to_i; end
|
766
|
-
# Setter for the TCP urgent field.
|
767
|
-
def tcp_urg=(i); typecast i; end
|
768
|
-
# Getter for the TCP urgent field.
|
769
|
-
def tcp_urg; self[:tcp_urg].to_i; end
|
770
|
-
|
771
|
-
# Getter for the TCP Header Length value.
|
772
|
-
def tcp_hlen; self[:tcp_hlen].to_i; end
|
773
|
-
# Setter for the TCP Header Length value. Can take
|
774
|
-
# either a string or an integer. Note that if it's
|
775
|
-
# a string, the top four bits are used.
|
776
|
-
def tcp_hlen=(i)
|
777
|
-
case i
|
778
|
-
when PacketFu::TcpHlen
|
779
|
-
self[:tcp_hlen] = i
|
780
|
-
when Numeric
|
781
|
-
self[:tcp_hlen] = TcpHlen.new(:hlen => i.to_i)
|
782
|
-
else
|
783
|
-
self[:tcp_hlen].read(i)
|
784
|
-
end
|
785
|
-
end
|
786
|
-
|
787
|
-
# Getter for the TCP Reserved field.
|
788
|
-
def tcp_reserved; self[:tcp_reserved].to_i; end
|
789
|
-
# Setter for the TCP Reserved field.
|
790
|
-
def tcp_reserved=(i)
|
791
|
-
case i
|
792
|
-
when PacketFu::TcpReserved
|
793
|
-
self[:tcp_reserved]=i
|
794
|
-
when Numeric
|
795
|
-
args = {}
|
796
|
-
args[:r1] = (i & 0b100) >> 2
|
797
|
-
args[:r2] = (i & 0b010) >> 1
|
798
|
-
args[:r3] = (i & 0b001)
|
799
|
-
self[:tcp_reserved] = TcpReserved.new(args)
|
800
|
-
else
|
801
|
-
self[:tcp_reserved].read(i)
|
802
|
-
end
|
803
|
-
end
|
804
|
-
|
805
|
-
# Getter for the ECN bits.
|
806
|
-
def tcp_ecn; self[:tcp_ecn].to_i; end
|
807
|
-
# Setter for the ECN bits.
|
808
|
-
def tcp_ecn=(i)
|
809
|
-
case i
|
810
|
-
when PacketFu::TcpEcn
|
811
|
-
self[:tcp_ecn]=i
|
812
|
-
when Numeric
|
813
|
-
args = {}
|
814
|
-
args[:n] = (i & 0b100) >> 2
|
815
|
-
args[:c] = (i & 0b010) >> 1
|
816
|
-
args[:e] = (i & 0b001)
|
817
|
-
self[:tcp_ecn] = TcpEcn.new(args)
|
818
|
-
else
|
819
|
-
self[:tcp_ecn].read(i)
|
820
|
-
end
|
821
|
-
end
|
822
|
-
|
823
|
-
# Getter for TCP Options.
|
824
|
-
def tcp_opts; self[:tcp_opts].to_s; end
|
825
|
-
# Setter for TCP Options.
|
826
|
-
def tcp_opts=(i)
|
827
|
-
case i
|
828
|
-
when PacketFu::TcpOptions
|
829
|
-
self[:tcp_opts]=i
|
830
|
-
else
|
831
|
-
self[:tcp_opts].read(i)
|
832
|
-
end
|
833
|
-
end
|
834
|
-
|
835
|
-
# Resets the sequence number to a new random number.
|
836
|
-
def tcp_calc_seq; @random_seq; end
|
837
|
-
# Resets the source port to a new random number.
|
838
|
-
def tcp_calc_src; @random_src; end
|
839
|
-
|
840
|
-
# Returns the actual length of the TCP options.
|
841
|
-
def tcp_opts_len
|
842
|
-
self[:tcp_opts].to_s.size
|
843
|
-
end
|
844
|
-
|
845
|
-
# Sets and returns the true length of the TCP Header.
|
846
|
-
# TODO: Think about making all the option stuff safer.
|
847
|
-
def tcp_calc_hlen
|
848
|
-
self[:tcp_hlen] = TcpHlen.new(:hlen => ((20 + tcp_opts_len) / 4))
|
849
|
-
end
|
850
|
-
|
851
|
-
# Generates a random high port. This is affected by packet flavor.
|
852
|
-
def rand_port
|
853
|
-
rand(0xffff - 1025) + 1025
|
854
|
-
end
|
855
|
-
|
856
|
-
# Gets a more readable option list.
|
857
|
-
def tcp_options
|
858
|
-
self[:tcp_opts].decode
|
859
|
-
end
|
860
|
-
|
861
|
-
# Gets a more readable flags list
|
862
|
-
def tcp_flags_dotmap
|
863
|
-
dotmap = tcp_flags.members.map do |flag|
|
864
|
-
status = self.tcp_flags.send flag
|
865
|
-
status == 0 ? "." : flag.to_s.upcase[0].chr
|
866
|
-
end
|
867
|
-
dotmap.join
|
868
|
-
end
|
869
|
-
|
870
|
-
# Sets a more readable option list.
|
871
|
-
def tcp_options=(arg)
|
872
|
-
self[:tcp_opts].encode arg
|
873
|
-
end
|
874
|
-
|
875
|
-
# Equivalent to tcp_src.
|
876
|
-
def tcp_sport
|
877
|
-
self.tcp_src.to_i
|
878
|
-
end
|
879
|
-
|
880
|
-
# Equivalent to tcp_src=.
|
881
|
-
def tcp_sport=(arg)
|
882
|
-
self.tcp_src=(arg)
|
883
|
-
end
|
884
|
-
|
885
|
-
# Equivalent to tcp_dst.
|
886
|
-
def tcp_dport
|
887
|
-
self.tcp_dst.to_i
|
888
|
-
end
|
889
|
-
|
890
|
-
# Equivalent to tcp_dst=.
|
891
|
-
def tcp_dport=(arg)
|
892
|
-
self.tcp_dst=(arg)
|
893
|
-
end
|
894
|
-
|
895
|
-
# Recalculates calculated fields for TCP (except checksum which is at the Packet level).
|
896
|
-
def tcp_recalc(arg=:all)
|
897
|
-
case arg
|
898
|
-
when :tcp_hlen
|
899
|
-
tcp_calc_hlen
|
900
|
-
when :tcp_src
|
901
|
-
@random_tcp_src = rand_port
|
902
|
-
when :tcp_sport
|
903
|
-
@random_tcp_src = rand_port
|
904
|
-
when :tcp_seq
|
905
|
-
@random_tcp_seq = rand(0xffffffff)
|
906
|
-
when :all
|
907
|
-
tcp_calc_hlen
|
908
|
-
@random_tcp_src = rand_port
|
909
|
-
@random_tcp_seq = rand(0xffffffff)
|
910
|
-
else
|
911
|
-
raise ArgumentError, "No such field `#{arg}'"
|
912
|
-
end
|
913
|
-
end
|
914
|
-
|
915
|
-
# Readability aliases
|
916
|
-
|
917
|
-
alias :tcp_flags_readable :tcp_flags_dotmap
|
918
|
-
|
919
|
-
def tcp_ack_readable
|
920
|
-
"0x%08x" % tcp_ack
|
921
|
-
end
|
922
|
-
|
923
|
-
def tcp_seq_readable
|
924
|
-
"0x%08x" % tcp_seq
|
925
|
-
end
|
926
|
-
|
927
|
-
def tcp_sum_readable
|
928
|
-
"0x%04x" % tcp_sum
|
929
|
-
end
|
930
|
-
|
931
|
-
def tcp_opts_readable
|
932
|
-
tcp_options
|
933
|
-
end
|
934
|
-
|
935
|
-
end
|
936
|
-
|
937
11
|
# TCPPacket is used to construct TCP packets. They contain an EthHeader, an IPHeader, and a TCPHeader.
|
938
12
|
#
|
939
13
|
# == Example
|
@@ -964,6 +38,9 @@ module PacketFu
|
|
964
38
|
# :config
|
965
39
|
# A hash of return address details, often the output of Utils.whoami?
|
966
40
|
class TCPPacket < Packet
|
41
|
+
include ::PacketFu::EthHeaderMixin
|
42
|
+
include ::PacketFu::IPHeaderMixin
|
43
|
+
include ::PacketFu::TCPHeaderMixin
|
967
44
|
|
968
45
|
attr_accessor :eth_header, :ip_header, :tcp_header
|
969
46
|
|
@@ -978,15 +55,12 @@ module PacketFu
|
|
978
55
|
def read(str=nil, args={})
|
979
56
|
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
980
57
|
@eth_header.read(str)
|
981
|
-
|
982
|
-
|
58
|
+
|
59
|
+
# Strip off any extra data, if we are asked to do so.
|
983
60
|
if args[:strip]
|
984
|
-
|
985
|
-
@tcp_header.read(
|
986
|
-
else
|
987
|
-
@tcp_header.read(str[14+(@ip_header.ip_hlen),str.size])
|
61
|
+
tcp_body_len = self.ip_len - self.ip_hlen - (self.tcp_hlen * 4)
|
62
|
+
@tcp_header.body.read(@tcp_header.body.to_s[0,tcp_body_len])
|
988
63
|
end
|
989
|
-
@ip_header.body = @tcp_header
|
990
64
|
super(args)
|
991
65
|
self
|
992
66
|
end
|