pcapr-local 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/LICENSE.txt +20 -0
- data/README.md +64 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/bin/pcap2par +49 -0
- data/bin/startpcapr +40 -0
- data/bin/stoppcapr +33 -0
- data/bin/xtractr +5 -0
- data/lib/environment.rb +106 -0
- data/lib/exe/xtractr +0 -0
- data/lib/mu/pcap.rb +110 -0
- data/lib/mu/pcap/ethernet.rb +148 -0
- data/lib/mu/pcap/header.rb +75 -0
- data/lib/mu/pcap/io_pair.rb +67 -0
- data/lib/mu/pcap/io_wrapper.rb +76 -0
- data/lib/mu/pcap/ip.rb +61 -0
- data/lib/mu/pcap/ipv4.rb +257 -0
- data/lib/mu/pcap/ipv6.rb +148 -0
- data/lib/mu/pcap/packet.rb +104 -0
- data/lib/mu/pcap/pkthdr.rb +155 -0
- data/lib/mu/pcap/reader.rb +61 -0
- data/lib/mu/pcap/reader/http_family.rb +170 -0
- data/lib/mu/pcap/sctp.rb +367 -0
- data/lib/mu/pcap/sctp/chunk.rb +123 -0
- data/lib/mu/pcap/sctp/chunk/data.rb +134 -0
- data/lib/mu/pcap/sctp/chunk/init.rb +100 -0
- data/lib/mu/pcap/sctp/chunk/init_ack.rb +68 -0
- data/lib/mu/pcap/sctp/parameter.rb +110 -0
- data/lib/mu/pcap/sctp/parameter/ip_address.rb +48 -0
- data/lib/mu/pcap/stream_packetizer.rb +72 -0
- data/lib/mu/pcap/tcp.rb +505 -0
- data/lib/mu/pcap/udp.rb +69 -0
- data/lib/mu/scenario/pcap.rb +164 -0
- data/lib/mu/scenario/pcap/fields.rb +50 -0
- data/lib/mu/scenario/pcap/rtp.rb +71 -0
- data/lib/pcapr_local.rb +159 -0
- data/lib/pcapr_local/config.rb +336 -0
- data/lib/pcapr_local/db.rb +197 -0
- data/lib/pcapr_local/scanner.rb +250 -0
- data/lib/pcapr_local/server.rb +178 -0
- data/lib/pcapr_local/www/favicon.ico +0 -0
- data/lib/pcapr_local/www/favicon.png +0 -0
- data/lib/pcapr_local/www/home/index.html +138 -0
- data/lib/pcapr_local/www/static/image/16x16/Cancel.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Cancel.png.1 +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Download.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Folder3.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Full Size.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Minus.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Plus.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Search.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/User.png +0 -0
- data/lib/pcapr_local/www/static/image/48x48/Phone.png +0 -0
- data/lib/pcapr_local/www/static/image/48x48/Video.png +0 -0
- data/lib/pcapr_local/www/static/image/bar-orange.gif +0 -0
- data/lib/pcapr_local/www/static/image/beta.png +0 -0
- data/lib/pcapr_local/www/static/image/bg.png +0 -0
- data/lib/pcapr_local/www/static/image/blockquote.png +0 -0
- data/lib/pcapr_local/www/static/image/body-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl5-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl6-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl7-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl8-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-l.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-r.png +0 -0
- data/lib/pcapr_local/www/static/image/btn-search.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-2.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-3.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-4.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-5.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-6.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-7.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl2.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl3.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl4.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-pathway.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-section1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-section2.png +0 -0
- data/lib/pcapr_local/www/static/image/collapsed.gif +0 -0
- data/lib/pcapr_local/www/static/image/crosslink.png +0 -0
- data/lib/pcapr_local/www/static/image/expanded.gif +0 -0
- data/lib/pcapr_local/www/static/image/favicon.ico +0 -0
- data/lib/pcapr_local/www/static/image/favicon.png +0 -0
- data/lib/pcapr_local/www/static/image/icon-author.png +0 -0
- data/lib/pcapr_local/www/static/image/icon-created.png +0 -0
- data/lib/pcapr_local/www/static/image/p-expand.gif +0 -0
- data/lib/pcapr_local/www/static/image/pcapr-logo.png +0 -0
- data/lib/pcapr_local/www/static/image/powered-by.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/status-alert.png +0 -0
- data/lib/pcapr_local/www/static/image/status-download.png +0 -0
- data/lib/pcapr_local/www/static/image/status-info.png +0 -0
- data/lib/pcapr_local/www/static/image/status-note.png +0 -0
- data/lib/pcapr_local/www/static/image/tab-round.png +0 -0
- data/lib/pcapr_local/www/static/image/throbber.gif +0 -0
- data/lib/pcapr_local/www/static/image/user.jpg +0 -0
- data/lib/pcapr_local/www/static/script/closet/async.js +421 -0
- data/lib/pcapr_local/www/static/script/closet/closet.api.js +241 -0
- data/lib/pcapr_local/www/static/script/closet/closet.folders.js +94 -0
- data/lib/pcapr_local/www/static/script/closet/closet.js +187 -0
- data/lib/pcapr_local/www/static/script/closet/closet.mr.js +219 -0
- data/lib/pcapr_local/www/static/script/closet/closet.options.js +359 -0
- data/lib/pcapr_local/www/static/script/closet/closet.quantity.js +73 -0
- data/lib/pcapr_local/www/static/script/closet/closet.render.js +205 -0
- data/lib/pcapr_local/www/static/script/closet/closet.report.js +86 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.http.js +135 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.overview.js +163 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.sip.js +159 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.tcp.js +72 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.visualize.js +263 -0
- data/lib/pcapr_local/www/static/script/closet/closet.util.js +40 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery-1.4.2.min.js +154 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery-ui.js +10921 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.js +2123 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.selection.js +184 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.stack.js +184 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.form.js +643 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.jsonp.min.js +3 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.menu.js +142 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.suggest.js +308 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.core.js +203 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.slider.js +629 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.sortable.js +1055 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.widget.js +236 -0
- data/lib/pcapr_local/www/static/script/json2.js +481 -0
- data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.cache.js +115 -0
- data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.template.js +117 -0
- data/lib/pcapr_local/www/static/script/sammy/sammy.js +1696 -0
- data/lib/pcapr_local/www/static/script/tipsy/jquery.tipsy.js +104 -0
- data/lib/pcapr_local/www/static/style/c3p0.css +116 -0
- data/lib/pcapr_local/www/static/style/jquery.suggest.css +27 -0
- data/lib/pcapr_local/www/static/style/page.css +1113 -0
- data/lib/pcapr_local/www/static/style/tipsy.css +7 -0
- data/lib/pcapr_local/www/templates/browse.services.template +10 -0
- data/lib/pcapr_local/www/templates/browse.template +77 -0
- data/lib/pcapr_local/www/templates/flows.template +38 -0
- data/lib/pcapr_local/www/templates/pcap.template +63 -0
- data/lib/pcapr_local/www/templates/sip.calls.template +35 -0
- data/lib/pcapr_local/www/templates/statistics.template +6 -0
- data/lib/pcapr_local/xtractr.rb +179 -0
- data/lib/pcapr_local/xtractr/instance.rb +172 -0
- data/pcapr-local.gemspec +297 -0
- data/test/mu/pcap/reader/tc_http_family.rb +251 -0
- data/test/mu/pcap/tc_ethernet.rb +71 -0
- data/test/mu/pcap/tc_header.rb +56 -0
- data/test/mu/pcap/tc_ipv4.rb +103 -0
- data/test/mu/pcap/tc_ipv6.rb +83 -0
- data/test/mu/pcap/tc_packet.rb +44 -0
- data/test/mu/pcap/tc_pair.rb +58 -0
- data/test/mu/pcap/tc_pkthdr.rb +33 -0
- data/test/mu/pcap/tc_reader.rb +76 -0
- data/test/mu/pcap/tc_tcp.rb +426 -0
- data/test/mu/pcap/tc_udp.rb +33 -0
- data/test/mu/pcap/tc_wrapper.rb +80 -0
- data/test/mu/scenario/pcap/tc_fields.rb +67 -0
- data/test/mu/scenario/pcap/tc_rtp.rb +135 -0
- data/test/mu/scenario/sip_signalled_call_1.pcap +0 -0
- data/test/mu/scenario/tc_pcap.rb +190 -0
- data/test/mu/scenario/test_data/arp.pcap +0 -0
- data/test/mu/scenario/test_data/dns.pcap +0 -0
- data/test/mu/scenario/test_data/http-v6.pcap +0 -0
- data/test/mu/scenario/test_data/http.pcap +0 -0
- data/test/mu/scenario/test_data/http_chunked.pcap +0 -0
- data/test/mu/scenario/test_data/http_deflate.pcap +0 -0
- data/test/mu/scenario/test_data/httpauth3.pcap +0 -0
- data/test/mu/scenario/test_data/icmp.pcap +0 -0
- data/test/mu/scenario/test_data/sip_signalled_call_1.pcap +0 -0
- data/test/mu/tc_pcap.rb +39 -0
- data/test/mu/testcase.rb +86 -0
- data/test/pcapr_local/arp.pcap +0 -0
- data/test/pcapr_local/data.js +3 -0
- data/test/pcapr_local/http_chunked.pcap +0 -0
- data/test/pcapr_local/tc_api.rb +181 -0
- data/test/pcapr_local/test.tgz +0 -0
- data/test/pcapr_local/test_scanner.rb +241 -0
- data/test/pcapr_local/test_xtractr.rb +219 -0
- data/test/pcapr_local/testcase.rb +107 -0
- data/test/test_export_to_scenario.sh +25 -0
- data/test/test_pcapr_local.rb +29 -0
- metadata +450 -0
@@ -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
|
data/lib/mu/pcap/sctp.rb
ADDED
@@ -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'
|