DIY-pcap 0.2.5 → 0.2.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/bin/pcap +2 -62
- data/bin/rpcap +2 -63
- data/lib/diy/command.rb +80 -0
- data/lib/diy/device_finder.rb +1 -1
- data/lib/diy/dig.rb +3 -1
- data/lib/diy/live.rb +5 -0
- data/lib/diy/parser/mu/fixnum_ext.rb +7 -0
- data/lib/diy/parser/mu/pcap/ethernet.rb +148 -0
- data/lib/diy/parser/mu/pcap/header.rb +75 -0
- data/lib/diy/parser/mu/pcap/io_pair.rb +67 -0
- data/lib/diy/parser/mu/pcap/io_wrapper.rb +76 -0
- data/lib/diy/parser/mu/pcap/ip.rb +61 -0
- data/lib/diy/parser/mu/pcap/ipv4.rb +257 -0
- data/lib/diy/parser/mu/pcap/ipv6.rb +148 -0
- data/lib/diy/parser/mu/pcap/packet.rb +104 -0
- data/lib/diy/parser/mu/pcap/pkthdr.rb +155 -0
- data/lib/diy/parser/mu/pcap/reader.rb +61 -0
- data/lib/diy/parser/mu/pcap/reader/http_family.rb +170 -0
- data/lib/diy/parser/mu/pcap/sctp.rb +367 -0
- data/lib/diy/parser/mu/pcap/sctp/chunk.rb +123 -0
- data/lib/diy/parser/mu/pcap/sctp/chunk/data.rb +134 -0
- data/lib/diy/parser/mu/pcap/sctp/chunk/init.rb +100 -0
- data/lib/diy/parser/mu/pcap/sctp/chunk/init_ack.rb +68 -0
- data/lib/diy/parser/mu/pcap/sctp/parameter.rb +110 -0
- data/lib/diy/parser/mu/pcap/sctp/parameter/ip_address.rb +48 -0
- data/lib/diy/parser/mu/pcap/stream_packetizer.rb +72 -0
- data/lib/diy/parser/mu/pcap/tcp.rb +505 -0
- data/lib/diy/parser/mu/pcap/udp.rb +69 -0
- data/lib/diy/parser/mu/scenario/pcap.rb +172 -0
- data/lib/diy/parser/mu/scenario/pcap/fields.rb +50 -0
- data/lib/diy/parser/mu/scenario/pcap/rtp.rb +71 -0
- data/lib/diy/parser/pcap.rb +113 -0
- data/lib/diy/parser/readme.md +72 -0
- data/lib/diy/utils.rb +9 -1
- data/lib/diy/version.rb +1 -1
- data/lib/diy/worker.rb +3 -2
- data/lib/diy/worker_keeper.rb +6 -0
- data/spec/helper/tcp.dat +0 -0
- data/spec/live_spec.rb +9 -0
- data/spec/mu_parser_spec.rb +12 -0
- data/spec/utils_spec.rb +1 -1
- metadata +34 -3
@@ -0,0 +1,170 @@
|
|
1
|
+
# http://www.mudynamics.com
|
2
|
+
# http://labs.mudynamics.com
|
3
|
+
# http://www.pcapr.net
|
4
|
+
|
5
|
+
require 'mu/pcap/reader'
|
6
|
+
require 'stringio'
|
7
|
+
require 'zlib'
|
8
|
+
|
9
|
+
module Mu
|
10
|
+
class Pcap
|
11
|
+
class Reader
|
12
|
+
|
13
|
+
# Reader for HTTP family of protocols (HTTP/SIP/RTSP).
|
14
|
+
# Handles message boundaries and decompressing/dechunking payloads.
|
15
|
+
class HttpFamily < Reader
|
16
|
+
FAMILY = :http
|
17
|
+
FAMILY_TO_READER[FAMILY] = self
|
18
|
+
CRLF = "\r\n"
|
19
|
+
def family
|
20
|
+
FAMILY
|
21
|
+
end
|
22
|
+
|
23
|
+
def do_record_write bytes, state=nil
|
24
|
+
return if not state
|
25
|
+
if bytes =~ RE_REQUEST_LINE
|
26
|
+
method = $1
|
27
|
+
requests = state[:requests] ||= []
|
28
|
+
requests << method
|
29
|
+
end
|
30
|
+
end
|
31
|
+
private :do_record_write
|
32
|
+
|
33
|
+
RE_CONTENT_ENCODING = /^content-encoding:\s*(gzip|deflate)/i
|
34
|
+
RE_CHUNKED = /Transfer-Encoding:\s*chunked/i
|
35
|
+
RE_HEADERS_COMPLETE = /.*?\r\n\r\n/m
|
36
|
+
# Request line e.g. GET /index.html HTTP/1.1
|
37
|
+
RE_REQUEST_LINE = /\A([^ \t\r\n]+)[ \t]+([^ \t\r\n]+)[ \t]+(HTTP|SIP|RTSP)\/[\d.]+.*\r\n/
|
38
|
+
# Status line e.g. SIP/2.0 404 Authorization required
|
39
|
+
RE_STATUS_LINE = /\A((HTTP|SIP|RTSP)\/[\d.]+[ \t]+(\d+))\b.*\r\n/
|
40
|
+
|
41
|
+
RE_CONTENT_LENGTH = /^(Content-Length)(:\s*)(\d+)\r\n/i
|
42
|
+
RE_CONTENT_LENGTH_SIP = /^(Content-Length|l)(:\s*)(\d+)\r\n/i
|
43
|
+
|
44
|
+
|
45
|
+
def do_read_message! bytes, state=nil
|
46
|
+
case bytes
|
47
|
+
when RE_REQUEST_LINE
|
48
|
+
proto = $3
|
49
|
+
when RE_STATUS_LINE
|
50
|
+
proto = $2
|
51
|
+
status = $3.to_i
|
52
|
+
if state
|
53
|
+
requests = state[:requests] ||= []
|
54
|
+
if requests[0] == "HEAD"
|
55
|
+
reply_to_head = true
|
56
|
+
end
|
57
|
+
if status > 199
|
58
|
+
# We have a final response. Forget about request.
|
59
|
+
requests.shift
|
60
|
+
end
|
61
|
+
end
|
62
|
+
else
|
63
|
+
return nil # Not http family.
|
64
|
+
end
|
65
|
+
|
66
|
+
# Read headers
|
67
|
+
if bytes =~ RE_HEADERS_COMPLETE
|
68
|
+
headers = $&
|
69
|
+
rest = $'
|
70
|
+
else
|
71
|
+
return nil
|
72
|
+
end
|
73
|
+
message = [headers]
|
74
|
+
|
75
|
+
# Read payload.
|
76
|
+
if proto == 'SIP'
|
77
|
+
re_content_length = RE_CONTENT_LENGTH_SIP
|
78
|
+
else
|
79
|
+
re_content_length = RE_CONTENT_LENGTH
|
80
|
+
end
|
81
|
+
if reply_to_head
|
82
|
+
length = 0
|
83
|
+
elsif headers =~ RE_CHUNKED
|
84
|
+
# Read chunks, dechunking in runtime case.
|
85
|
+
raw, dechunked = get_chunks(rest)
|
86
|
+
if raw
|
87
|
+
length = raw.length
|
88
|
+
payload = raw
|
89
|
+
else
|
90
|
+
return nil # Last chunk not received.
|
91
|
+
end
|
92
|
+
elsif headers =~ re_content_length
|
93
|
+
length = $3.to_i
|
94
|
+
if rest.length >= length
|
95
|
+
payload = rest.slice(0,length)
|
96
|
+
else
|
97
|
+
return nil # Not enough bytes.
|
98
|
+
end
|
99
|
+
else
|
100
|
+
# XXX. When there is a payload and no content-length
|
101
|
+
# header HTTP RFC says to read until connection close.
|
102
|
+
length = 0
|
103
|
+
end
|
104
|
+
|
105
|
+
message << payload
|
106
|
+
|
107
|
+
# Consume message from input bytes.
|
108
|
+
message_len = headers.length + length
|
109
|
+
if bytes.length >= message_len
|
110
|
+
bytes.slice!(0, message_len)
|
111
|
+
return message.join
|
112
|
+
else
|
113
|
+
return nil # Not enough bytes.
|
114
|
+
end
|
115
|
+
end
|
116
|
+
private :do_read_message!
|
117
|
+
|
118
|
+
# Returns array containing raw and dechunked payload. Returns nil
|
119
|
+
# if payload cannot be completely read.
|
120
|
+
RE_CHUNK_SIZE_LINE = /\A([[:xdigit:]]+)\r\n?/
|
121
|
+
def get_chunks bytes
|
122
|
+
raw = []
|
123
|
+
dechunked = []
|
124
|
+
io = StringIO.new bytes
|
125
|
+
until io.eof?
|
126
|
+
# Read size line
|
127
|
+
size_line = io.readline
|
128
|
+
raw << size_line
|
129
|
+
if size_line =~ RE_CHUNK_SIZE_LINE
|
130
|
+
chunk_size = $1.to_i(16)
|
131
|
+
else
|
132
|
+
# Malformed chunk size line
|
133
|
+
$stderr.puts "malformed size line : #{size_line.inspect}"
|
134
|
+
return nil
|
135
|
+
end
|
136
|
+
|
137
|
+
# Read chunk data
|
138
|
+
chunk = io.read(chunk_size)
|
139
|
+
if chunk.size < chunk_size
|
140
|
+
# malformed/incomplete
|
141
|
+
$stderr.puts "malformed/incomplete #{chunk_size}"
|
142
|
+
return nil
|
143
|
+
end
|
144
|
+
raw << chunk
|
145
|
+
dechunked << chunk
|
146
|
+
# Get end-of-chunk CRLF
|
147
|
+
crlf = io.read(2)
|
148
|
+
if crlf == CRLF
|
149
|
+
raw << crlf
|
150
|
+
else
|
151
|
+
# CRLF has not arrived or, if this is the last chunk,
|
152
|
+
# we might be looking at the first two bytes of a trailer
|
153
|
+
# and we don't support trailers (see rfc 2616 sec3.6.1).
|
154
|
+
return nil
|
155
|
+
end
|
156
|
+
|
157
|
+
if chunk_size == 0
|
158
|
+
# Done. Return raw and dechunked payloads.
|
159
|
+
return raw.join, dechunked.join
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# EOF w/out reaching last chunk.
|
164
|
+
return nil
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,367 @@
|
|
1
|
+
# http://www.mudynamics.com
|
2
|
+
# http://labs.mudynamics.com
|
3
|
+
# http://www.pcapr.net
|
4
|
+
|
5
|
+
module Mu
|
6
|
+
class Pcap
|
7
|
+
|
8
|
+
class SCTP < Packet
|
9
|
+
attr_accessor :src_port, :dst_port, :verify_tag, :checksum
|
10
|
+
|
11
|
+
# SCTP chunk types
|
12
|
+
CHUNK_DATA = 0x00
|
13
|
+
CHUNK_INIT = 0x01
|
14
|
+
CHUNK_INIT_ACK = 0x02
|
15
|
+
CHUNK_SACK = 0x03
|
16
|
+
CHUNK_HEARTBEAT = 0x04
|
17
|
+
CHUNK_HEARTBEAT_ACK = 0x05
|
18
|
+
CHUNK_ABORT = 0x06
|
19
|
+
CHUNK_SHUTDOWN = 0x07
|
20
|
+
CHUNK_SHUTDOWN_ACK = 0x08
|
21
|
+
CHUNK_ERROR = 0x09
|
22
|
+
CHUNK_COOKIE_ECHO = 0x0A
|
23
|
+
CHUNK_COOKIE_ACK = 0x0B
|
24
|
+
CHUNK_ECNE = 0x0C
|
25
|
+
CHUNK_CWR = 0x0D
|
26
|
+
CHUNK_SHUTDOWN_COMPLETE = 0x0E
|
27
|
+
CHUNK_AUTH = 0x0F
|
28
|
+
CHUNK_ASCONF_ACK = 0x80
|
29
|
+
CHUNK_PADDING = 0x84
|
30
|
+
CHUNK_FORWARD_TSN = 0xC0
|
31
|
+
CHUNK_ASCONF = 0xC1
|
32
|
+
|
33
|
+
# SCTP parameter types
|
34
|
+
PARAM_IPV4 = 0x0005
|
35
|
+
PARAM_IPV6 = 0x0006
|
36
|
+
PARAM_STATE_COOKIE = 0x0007
|
37
|
+
PARAM_COOKIE_PRESERVATIVE = 0x0009
|
38
|
+
PARAM_HOST_NAME_ADDR = 0x000B
|
39
|
+
PARAM_SUPPORTED_ADDR_TYPES = 0x000C
|
40
|
+
PARAM_ECN = 0x8000
|
41
|
+
PARAM_RANDOM = 0x8002
|
42
|
+
PARAM_CHUNK_LIST = 0x8003
|
43
|
+
PARAM_HMAC_ALGORITHM = 0x8004
|
44
|
+
PARAM_PADDING = 0x8005
|
45
|
+
PARAM_SUPPORTED_EXTENSIONS = 0x8006
|
46
|
+
PARAM_FORWARD_TSN = 0xC000
|
47
|
+
PARAM_SET_PRIMARY_ADDR = 0xC004
|
48
|
+
PARAM_ADAPTATION_LAYER_INDICATION = 0xC006
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
super
|
52
|
+
|
53
|
+
@src_port = 0
|
54
|
+
@dst_port = 0
|
55
|
+
@verify_tag = 0
|
56
|
+
@checksum = 0
|
57
|
+
@payload = []
|
58
|
+
end
|
59
|
+
|
60
|
+
def flow_id
|
61
|
+
return [:sctp, @src_port, @dst_port, @verify_tag]
|
62
|
+
end
|
63
|
+
|
64
|
+
def reverse_flow_id
|
65
|
+
return [:sctp, @dst_port, @src_port, @checksum]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Creates SCTP packet from the payload
|
69
|
+
def self.from_bytes bytes
|
70
|
+
# Basic packet validation
|
71
|
+
Pcap.assert(bytes.length >= 12,
|
72
|
+
"Truncated SCTP header: 12 > #{bytes.length}")
|
73
|
+
Pcap.assert(bytes.length >= 16,
|
74
|
+
"Truncated SCTP packet: got only #{bytes.length} bytes")
|
75
|
+
|
76
|
+
# Read SCTP header
|
77
|
+
sport, dport, vtag, cksum = bytes.unpack('nnNN')
|
78
|
+
|
79
|
+
# Create SCTP packet and populate SCTP header fields
|
80
|
+
sctp = SCTP.new
|
81
|
+
sctp.src_port = sport
|
82
|
+
sctp.dst_port = dport
|
83
|
+
sctp.verify_tag = vtag
|
84
|
+
sctp.checksum = cksum
|
85
|
+
|
86
|
+
# Initialize the counter
|
87
|
+
length = 12
|
88
|
+
|
89
|
+
# Collect the chunks
|
90
|
+
while length < bytes.length
|
91
|
+
# Parse new chunk from the bytes
|
92
|
+
chunk = Chunk.from_bytes(bytes[length..-1])
|
93
|
+
|
94
|
+
# Get chunk size with padding
|
95
|
+
length += chunk.padded_size
|
96
|
+
|
97
|
+
# Add chunk to the list
|
98
|
+
sctp << chunk
|
99
|
+
end
|
100
|
+
|
101
|
+
# Sync the payload
|
102
|
+
sctp.sync_payload
|
103
|
+
|
104
|
+
# Return the result
|
105
|
+
return sctp
|
106
|
+
end
|
107
|
+
|
108
|
+
class ReorderError < StandardError ; end
|
109
|
+
|
110
|
+
# Reorders SCTP packets, if necessary
|
111
|
+
def self.reorder packets
|
112
|
+
# Initialize
|
113
|
+
tsns = {}
|
114
|
+
init_packets = {}
|
115
|
+
init_ack_packets = {}
|
116
|
+
reordered_packets = []
|
117
|
+
|
118
|
+
# Iterate over each packet
|
119
|
+
while not packets.empty?
|
120
|
+
# Get next packet
|
121
|
+
packet = packets.shift
|
122
|
+
|
123
|
+
# Do not reorder non-SCTP packets
|
124
|
+
if not sctp?(packet)
|
125
|
+
reordered_packets << packet
|
126
|
+
else
|
127
|
+
# Get SCTP portion
|
128
|
+
sctp = packet.payload.payload
|
129
|
+
|
130
|
+
# Sanity checks and packet filtering/preprocessing
|
131
|
+
if 0 == sctp.verify_tag and not sctp.init?
|
132
|
+
# Non-Init packet with 0 verify tag
|
133
|
+
raise ReorderError, "Non-Init packet with zero verify tag"
|
134
|
+
elsif sctp.init_or_ack? and 1 < sctp.chunk_count
|
135
|
+
# Init/InitAck packet with more with one chunk
|
136
|
+
raise ReorderError, "Init/Ack packet with more than 1 chunk"
|
137
|
+
elsif sctp.init?
|
138
|
+
# Use checksum to save reverse verify tag in the Init packet
|
139
|
+
sctp.checksum = sctp[0].init_tag
|
140
|
+
|
141
|
+
# Save orphaned Init packets until we find the Ack
|
142
|
+
init_packets[sctp.reverse_flow_id] = sctp
|
143
|
+
|
144
|
+
# Add packet for further processing
|
145
|
+
reordered_packets << packet
|
146
|
+
elsif sctp.init_ack?
|
147
|
+
# Lookup Init packet and construct it's flow it
|
148
|
+
init_packet = init_packets.delete(sctp.flow_id)
|
149
|
+
|
150
|
+
# Did we find anything?
|
151
|
+
if init_packet
|
152
|
+
# Set verify tag in the Init packet
|
153
|
+
init_packet.verify_tag = sctp[0].init_tag
|
154
|
+
|
155
|
+
# Set reverse verify tag in the InitAck packet
|
156
|
+
sctp.checksum = init_packet.verify_tag
|
157
|
+
|
158
|
+
# Re-insert INIT packet keyed by its flow id
|
159
|
+
init_packets[init_packet.flow_id] = init_packet
|
160
|
+
else
|
161
|
+
Pcap.warning("Orphaned SCTP INIT_ACK packet")
|
162
|
+
end
|
163
|
+
|
164
|
+
# Save InitAck packet
|
165
|
+
init_ack_packets[sctp.flow_id] = sctp
|
166
|
+
|
167
|
+
# Add packet for further processing
|
168
|
+
reordered_packets << packet
|
169
|
+
elsif sctp.has_data?
|
170
|
+
# SCTP packet with user data; lookup Init or InitAck packet
|
171
|
+
init_packet = init_packets[sctp.flow_id]
|
172
|
+
init_ack_packet = init_ack_packets[sctp.flow_id]
|
173
|
+
|
174
|
+
# It should belong to either one flow id or the other
|
175
|
+
if init_packet
|
176
|
+
# Set reverse verify tag from Init packet
|
177
|
+
sctp.checksum = init_packet.checksum
|
178
|
+
elsif init_ack_packet
|
179
|
+
# Set reverse flow id from InitAck packet
|
180
|
+
sctp.checksum = init_ack_packet.checksum
|
181
|
+
else
|
182
|
+
# Orphaned SCTP packet -- not very good
|
183
|
+
Pcap.warning("Orphaned SCTP DATA packet detected")
|
184
|
+
end
|
185
|
+
|
186
|
+
# If we have just one chunk we are done
|
187
|
+
if 1 == sctp.chunk_count and not tsns.member?(sctp[0].tsn)
|
188
|
+
# Save TSN
|
189
|
+
tsns[sctp[0].tsn] = sctp[0]
|
190
|
+
|
191
|
+
# sync the payload
|
192
|
+
sctp.sync_payload
|
193
|
+
|
194
|
+
# Add packet for further processing
|
195
|
+
reordered_packets << packet
|
196
|
+
else
|
197
|
+
# Split each data chunk in a separate SCTP packet
|
198
|
+
sctp.chunk_count.times do
|
199
|
+
# Get next chunk
|
200
|
+
chunk = sctp.shift
|
201
|
+
|
202
|
+
# Is it data?
|
203
|
+
if CHUNK_DATA == chunk.type
|
204
|
+
# Yes, check for duplicate TSNs
|
205
|
+
if not tsns.member?(chunk.tsn)
|
206
|
+
# Not a duplicate; create new SCTP packet
|
207
|
+
packet_new = packet.deepdup
|
208
|
+
|
209
|
+
# Create new SCTP payload
|
210
|
+
sctp_new = SCTP.new
|
211
|
+
sctp_new.src_port = sctp.src_port
|
212
|
+
sctp_new.dst_port = sctp.dst_port
|
213
|
+
sctp_new.verify_tag = sctp.verify_tag
|
214
|
+
sctp_new.checksum = sctp.checksum
|
215
|
+
|
216
|
+
# Add the chunk
|
217
|
+
sctp_new << chunk
|
218
|
+
|
219
|
+
# Add SCTP payload to the new packet
|
220
|
+
packet_new.payload.payload = sctp_new
|
221
|
+
|
222
|
+
# Save TSN
|
223
|
+
tsns[chunk.tsn] = chunk
|
224
|
+
|
225
|
+
# Sync the payload
|
226
|
+
sctp_new.sync_payload
|
227
|
+
|
228
|
+
# Add packet for further processing
|
229
|
+
reordered_packets << packet_new
|
230
|
+
else
|
231
|
+
Pcap.warning("Duplicate chunk: #{chunk.tsn}")
|
232
|
+
end
|
233
|
+
else
|
234
|
+
Pcap.warning("Non-data chunk: #{chunk.type}")
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
else
|
239
|
+
# Other SCTP packet; we are not interested at this time
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Return the result
|
245
|
+
return reordered_packets
|
246
|
+
end
|
247
|
+
|
248
|
+
def write io, ip
|
249
|
+
# Give a warning if packet size exceeds maximum allowed
|
250
|
+
if @payload_raw and @payload_raw.length + 20 > 65535
|
251
|
+
Pcap.warning("SCTP payload is too large")
|
252
|
+
end
|
253
|
+
|
254
|
+
# Calculate CRC32 checksum on the packet; temporarily removed due to a
|
255
|
+
# hack that uses checksum to link forward and reverse SCTP flow IDs.
|
256
|
+
#header = [@src_port, @dst_port, @verify_tag, 0].pack('nnNN')
|
257
|
+
#checksum = SCTP.crc32(header + @payload_raw)
|
258
|
+
header = [@src_port, @dst_port, @verify_tag, @checksum].pack('nnNN')
|
259
|
+
|
260
|
+
# Write SCTP header followed by each chunk
|
261
|
+
io.write(header)
|
262
|
+
|
263
|
+
# Write each chunks' data
|
264
|
+
@payload.each do |chunk|
|
265
|
+
chunk.write(io, ip)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def sync_payload
|
270
|
+
# Reset raw bytes
|
271
|
+
@payload_raw = ''
|
272
|
+
|
273
|
+
# Iterate over each chunk
|
274
|
+
@payload.each do |chunk|
|
275
|
+
@payload_raw << chunk.payload_raw
|
276
|
+
end
|
277
|
+
|
278
|
+
# Reset raw payload if it's empty
|
279
|
+
@payload_raw = nil if @payload_raw == ''
|
280
|
+
end
|
281
|
+
|
282
|
+
def self.crc32 bytes
|
283
|
+
r = 0xFFFFFFFF
|
284
|
+
|
285
|
+
bytes.each_byte do |b|
|
286
|
+
r ^= b
|
287
|
+
|
288
|
+
8.times do
|
289
|
+
r = (r >> 1) ^ (0xEDB88320 * (r & 1))
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
return r ^ 0xFFFFFFFF
|
294
|
+
end
|
295
|
+
|
296
|
+
def self.sctp? packet
|
297
|
+
return packet.is_a?(Ethernet) &&
|
298
|
+
packet.payload.is_a?(IP) &&
|
299
|
+
packet.payload.payload.is_a?(SCTP)
|
300
|
+
end
|
301
|
+
|
302
|
+
def << chunk
|
303
|
+
@payload << chunk
|
304
|
+
end
|
305
|
+
|
306
|
+
def shift
|
307
|
+
return @payload.shift
|
308
|
+
end
|
309
|
+
|
310
|
+
def [] index
|
311
|
+
return @payload[index]
|
312
|
+
end
|
313
|
+
|
314
|
+
def chunk_count
|
315
|
+
return @payload.size
|
316
|
+
end
|
317
|
+
|
318
|
+
def has_data?
|
319
|
+
return @payload.any? do |chunk|
|
320
|
+
CHUNK_DATA == chunk.type
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def to_s
|
325
|
+
return "sctp(%d, %d, %d, %s)" % [@src_port,
|
326
|
+
@dst_port,
|
327
|
+
@verify_tag,
|
328
|
+
@payload.join(", ")]
|
329
|
+
end
|
330
|
+
|
331
|
+
def == other
|
332
|
+
return super &&
|
333
|
+
self.src_port == other.src_port &&
|
334
|
+
self.dst_port == other.dst_port &&
|
335
|
+
self.verify_tag == other.verify_tag &&
|
336
|
+
self.payload.size == other.payload.size
|
337
|
+
end
|
338
|
+
|
339
|
+
def init?
|
340
|
+
if CHUNK_INIT == @payload[0].type
|
341
|
+
return true
|
342
|
+
else
|
343
|
+
return false
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def init_ack?
|
348
|
+
if CHUNK_INIT_ACK == @payload[0].type
|
349
|
+
return true
|
350
|
+
else
|
351
|
+
return false
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def init_or_ack?
|
356
|
+
if CHUNK_INIT == @payload[0].type or CHUNK_INIT_ACK == @payload[0].type
|
357
|
+
return true
|
358
|
+
else
|
359
|
+
return false
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end # class SCTP
|
363
|
+
|
364
|
+
end # class Pcap
|
365
|
+
end # module Mu
|
366
|
+
|
367
|
+
require 'mu/pcap/sctp/chunk'
|