pcapr-local 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (203) hide show
  1. data/.document +5 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +64 -0
  4. data/Rakefile +57 -0
  5. data/VERSION +1 -0
  6. data/bin/pcap2par +49 -0
  7. data/bin/startpcapr +40 -0
  8. data/bin/stoppcapr +33 -0
  9. data/bin/xtractr +5 -0
  10. data/lib/environment.rb +106 -0
  11. data/lib/exe/xtractr +0 -0
  12. data/lib/mu/pcap.rb +110 -0
  13. data/lib/mu/pcap/ethernet.rb +148 -0
  14. data/lib/mu/pcap/header.rb +75 -0
  15. data/lib/mu/pcap/io_pair.rb +67 -0
  16. data/lib/mu/pcap/io_wrapper.rb +76 -0
  17. data/lib/mu/pcap/ip.rb +61 -0
  18. data/lib/mu/pcap/ipv4.rb +257 -0
  19. data/lib/mu/pcap/ipv6.rb +148 -0
  20. data/lib/mu/pcap/packet.rb +104 -0
  21. data/lib/mu/pcap/pkthdr.rb +155 -0
  22. data/lib/mu/pcap/reader.rb +61 -0
  23. data/lib/mu/pcap/reader/http_family.rb +170 -0
  24. data/lib/mu/pcap/sctp.rb +367 -0
  25. data/lib/mu/pcap/sctp/chunk.rb +123 -0
  26. data/lib/mu/pcap/sctp/chunk/data.rb +134 -0
  27. data/lib/mu/pcap/sctp/chunk/init.rb +100 -0
  28. data/lib/mu/pcap/sctp/chunk/init_ack.rb +68 -0
  29. data/lib/mu/pcap/sctp/parameter.rb +110 -0
  30. data/lib/mu/pcap/sctp/parameter/ip_address.rb +48 -0
  31. data/lib/mu/pcap/stream_packetizer.rb +72 -0
  32. data/lib/mu/pcap/tcp.rb +505 -0
  33. data/lib/mu/pcap/udp.rb +69 -0
  34. data/lib/mu/scenario/pcap.rb +164 -0
  35. data/lib/mu/scenario/pcap/fields.rb +50 -0
  36. data/lib/mu/scenario/pcap/rtp.rb +71 -0
  37. data/lib/pcapr_local.rb +159 -0
  38. data/lib/pcapr_local/config.rb +336 -0
  39. data/lib/pcapr_local/db.rb +197 -0
  40. data/lib/pcapr_local/scanner.rb +250 -0
  41. data/lib/pcapr_local/server.rb +178 -0
  42. data/lib/pcapr_local/www/favicon.ico +0 -0
  43. data/lib/pcapr_local/www/favicon.png +0 -0
  44. data/lib/pcapr_local/www/home/index.html +138 -0
  45. data/lib/pcapr_local/www/static/image/16x16/Cancel.png +0 -0
  46. data/lib/pcapr_local/www/static/image/16x16/Cancel.png.1 +0 -0
  47. data/lib/pcapr_local/www/static/image/16x16/Download.png +0 -0
  48. data/lib/pcapr_local/www/static/image/16x16/Folder3.png +0 -0
  49. data/lib/pcapr_local/www/static/image/16x16/Full Size.png +0 -0
  50. data/lib/pcapr_local/www/static/image/16x16/Minus.png +0 -0
  51. data/lib/pcapr_local/www/static/image/16x16/Plus.png +0 -0
  52. data/lib/pcapr_local/www/static/image/16x16/Search.png +0 -0
  53. data/lib/pcapr_local/www/static/image/16x16/User.png +0 -0
  54. data/lib/pcapr_local/www/static/image/48x48/Phone.png +0 -0
  55. data/lib/pcapr_local/www/static/image/48x48/Video.png +0 -0
  56. data/lib/pcapr_local/www/static/image/bar-orange.gif +0 -0
  57. data/lib/pcapr_local/www/static/image/beta.png +0 -0
  58. data/lib/pcapr_local/www/static/image/bg.png +0 -0
  59. data/lib/pcapr_local/www/static/image/blockquote.png +0 -0
  60. data/lib/pcapr_local/www/static/image/body-bg.png +0 -0
  61. data/lib/pcapr_local/www/static/image/body-h3.png +0 -0
  62. data/lib/pcapr_local/www/static/image/body-hl1-bg.png +0 -0
  63. data/lib/pcapr_local/www/static/image/body-hl1-h3.png +0 -0
  64. data/lib/pcapr_local/www/static/image/body-hl1-readmore.png +0 -0
  65. data/lib/pcapr_local/www/static/image/body-hl2-bg.png +0 -0
  66. data/lib/pcapr_local/www/static/image/body-hl2-h3.png +0 -0
  67. data/lib/pcapr_local/www/static/image/body-hl2-readmore.png +0 -0
  68. data/lib/pcapr_local/www/static/image/body-hl3-bg.png +0 -0
  69. data/lib/pcapr_local/www/static/image/body-hl3-h3.png +0 -0
  70. data/lib/pcapr_local/www/static/image/body-hl3-readmore.png +0 -0
  71. data/lib/pcapr_local/www/static/image/body-hl4-bg.png +0 -0
  72. data/lib/pcapr_local/www/static/image/body-hl4-h3.png +0 -0
  73. data/lib/pcapr_local/www/static/image/body-hl4-readmore.png +0 -0
  74. data/lib/pcapr_local/www/static/image/body-hl5-h3.png +0 -0
  75. data/lib/pcapr_local/www/static/image/body-hl6-h3.png +0 -0
  76. data/lib/pcapr_local/www/static/image/body-hl7-h3.png +0 -0
  77. data/lib/pcapr_local/www/static/image/body-hl8-h3.png +0 -0
  78. data/lib/pcapr_local/www/static/image/body-readmore.png +0 -0
  79. data/lib/pcapr_local/www/static/image/bottom-bg.png +0 -0
  80. data/lib/pcapr_local/www/static/image/bottom-l.png +0 -0
  81. data/lib/pcapr_local/www/static/image/bottom-r.png +0 -0
  82. data/lib/pcapr_local/www/static/image/btn-search.png +0 -0
  83. data/lib/pcapr_local/www/static/image/bullet-1.png +0 -0
  84. data/lib/pcapr_local/www/static/image/bullet-2.png +0 -0
  85. data/lib/pcapr_local/www/static/image/bullet-3.png +0 -0
  86. data/lib/pcapr_local/www/static/image/bullet-4.png +0 -0
  87. data/lib/pcapr_local/www/static/image/bullet-5.png +0 -0
  88. data/lib/pcapr_local/www/static/image/bullet-6.png +0 -0
  89. data/lib/pcapr_local/www/static/image/bullet-7.png +0 -0
  90. data/lib/pcapr_local/www/static/image/bullet-hl1.png +0 -0
  91. data/lib/pcapr_local/www/static/image/bullet-hl2.png +0 -0
  92. data/lib/pcapr_local/www/static/image/bullet-hl3.png +0 -0
  93. data/lib/pcapr_local/www/static/image/bullet-hl4.png +0 -0
  94. data/lib/pcapr_local/www/static/image/bullet-pathway.png +0 -0
  95. data/lib/pcapr_local/www/static/image/bullet-section1.png +0 -0
  96. data/lib/pcapr_local/www/static/image/bullet-section2.png +0 -0
  97. data/lib/pcapr_local/www/static/image/collapsed.gif +0 -0
  98. data/lib/pcapr_local/www/static/image/crosslink.png +0 -0
  99. data/lib/pcapr_local/www/static/image/expanded.gif +0 -0
  100. data/lib/pcapr_local/www/static/image/favicon.ico +0 -0
  101. data/lib/pcapr_local/www/static/image/favicon.png +0 -0
  102. data/lib/pcapr_local/www/static/image/icon-author.png +0 -0
  103. data/lib/pcapr_local/www/static/image/icon-created.png +0 -0
  104. data/lib/pcapr_local/www/static/image/p-expand.gif +0 -0
  105. data/lib/pcapr_local/www/static/image/pcapr-logo.png +0 -0
  106. data/lib/pcapr_local/www/static/image/powered-by.png +0 -0
  107. data/lib/pcapr_local/www/static/image/section1-bg.png +0 -0
  108. data/lib/pcapr_local/www/static/image/section1-h3.png +0 -0
  109. data/lib/pcapr_local/www/static/image/section1-readmore.png +0 -0
  110. data/lib/pcapr_local/www/static/image/section2-bg.png +0 -0
  111. data/lib/pcapr_local/www/static/image/section2-h3.png +0 -0
  112. data/lib/pcapr_local/www/static/image/section2-readmore.png +0 -0
  113. data/lib/pcapr_local/www/static/image/status-alert.png +0 -0
  114. data/lib/pcapr_local/www/static/image/status-download.png +0 -0
  115. data/lib/pcapr_local/www/static/image/status-info.png +0 -0
  116. data/lib/pcapr_local/www/static/image/status-note.png +0 -0
  117. data/lib/pcapr_local/www/static/image/tab-round.png +0 -0
  118. data/lib/pcapr_local/www/static/image/throbber.gif +0 -0
  119. data/lib/pcapr_local/www/static/image/user.jpg +0 -0
  120. data/lib/pcapr_local/www/static/script/closet/async.js +421 -0
  121. data/lib/pcapr_local/www/static/script/closet/closet.api.js +241 -0
  122. data/lib/pcapr_local/www/static/script/closet/closet.folders.js +94 -0
  123. data/lib/pcapr_local/www/static/script/closet/closet.js +187 -0
  124. data/lib/pcapr_local/www/static/script/closet/closet.mr.js +219 -0
  125. data/lib/pcapr_local/www/static/script/closet/closet.options.js +359 -0
  126. data/lib/pcapr_local/www/static/script/closet/closet.quantity.js +73 -0
  127. data/lib/pcapr_local/www/static/script/closet/closet.render.js +205 -0
  128. data/lib/pcapr_local/www/static/script/closet/closet.report.js +86 -0
  129. data/lib/pcapr_local/www/static/script/closet/closet.reports.http.js +135 -0
  130. data/lib/pcapr_local/www/static/script/closet/closet.reports.overview.js +163 -0
  131. data/lib/pcapr_local/www/static/script/closet/closet.reports.sip.js +159 -0
  132. data/lib/pcapr_local/www/static/script/closet/closet.reports.tcp.js +72 -0
  133. data/lib/pcapr_local/www/static/script/closet/closet.reports.visualize.js +263 -0
  134. data/lib/pcapr_local/www/static/script/closet/closet.util.js +40 -0
  135. data/lib/pcapr_local/www/static/script/jquery/jquery-1.4.2.min.js +154 -0
  136. data/lib/pcapr_local/www/static/script/jquery/jquery-ui.js +10921 -0
  137. data/lib/pcapr_local/www/static/script/jquery/jquery.flot.js +2123 -0
  138. data/lib/pcapr_local/www/static/script/jquery/jquery.flot.selection.js +184 -0
  139. data/lib/pcapr_local/www/static/script/jquery/jquery.flot.stack.js +184 -0
  140. data/lib/pcapr_local/www/static/script/jquery/jquery.form.js +643 -0
  141. data/lib/pcapr_local/www/static/script/jquery/jquery.jsonp.min.js +3 -0
  142. data/lib/pcapr_local/www/static/script/jquery/jquery.menu.js +142 -0
  143. data/lib/pcapr_local/www/static/script/jquery/jquery.suggest.js +308 -0
  144. data/lib/pcapr_local/www/static/script/jquery/jquery.ui.core.js +203 -0
  145. data/lib/pcapr_local/www/static/script/jquery/jquery.ui.slider.js +629 -0
  146. data/lib/pcapr_local/www/static/script/jquery/jquery.ui.sortable.js +1055 -0
  147. data/lib/pcapr_local/www/static/script/jquery/jquery.ui.widget.js +236 -0
  148. data/lib/pcapr_local/www/static/script/json2.js +481 -0
  149. data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.cache.js +115 -0
  150. data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.template.js +117 -0
  151. data/lib/pcapr_local/www/static/script/sammy/sammy.js +1696 -0
  152. data/lib/pcapr_local/www/static/script/tipsy/jquery.tipsy.js +104 -0
  153. data/lib/pcapr_local/www/static/style/c3p0.css +116 -0
  154. data/lib/pcapr_local/www/static/style/jquery.suggest.css +27 -0
  155. data/lib/pcapr_local/www/static/style/page.css +1113 -0
  156. data/lib/pcapr_local/www/static/style/tipsy.css +7 -0
  157. data/lib/pcapr_local/www/templates/browse.services.template +10 -0
  158. data/lib/pcapr_local/www/templates/browse.template +77 -0
  159. data/lib/pcapr_local/www/templates/flows.template +38 -0
  160. data/lib/pcapr_local/www/templates/pcap.template +63 -0
  161. data/lib/pcapr_local/www/templates/sip.calls.template +35 -0
  162. data/lib/pcapr_local/www/templates/statistics.template +6 -0
  163. data/lib/pcapr_local/xtractr.rb +179 -0
  164. data/lib/pcapr_local/xtractr/instance.rb +172 -0
  165. data/pcapr-local.gemspec +297 -0
  166. data/test/mu/pcap/reader/tc_http_family.rb +251 -0
  167. data/test/mu/pcap/tc_ethernet.rb +71 -0
  168. data/test/mu/pcap/tc_header.rb +56 -0
  169. data/test/mu/pcap/tc_ipv4.rb +103 -0
  170. data/test/mu/pcap/tc_ipv6.rb +83 -0
  171. data/test/mu/pcap/tc_packet.rb +44 -0
  172. data/test/mu/pcap/tc_pair.rb +58 -0
  173. data/test/mu/pcap/tc_pkthdr.rb +33 -0
  174. data/test/mu/pcap/tc_reader.rb +76 -0
  175. data/test/mu/pcap/tc_tcp.rb +426 -0
  176. data/test/mu/pcap/tc_udp.rb +33 -0
  177. data/test/mu/pcap/tc_wrapper.rb +80 -0
  178. data/test/mu/scenario/pcap/tc_fields.rb +67 -0
  179. data/test/mu/scenario/pcap/tc_rtp.rb +135 -0
  180. data/test/mu/scenario/sip_signalled_call_1.pcap +0 -0
  181. data/test/mu/scenario/tc_pcap.rb +190 -0
  182. data/test/mu/scenario/test_data/arp.pcap +0 -0
  183. data/test/mu/scenario/test_data/dns.pcap +0 -0
  184. data/test/mu/scenario/test_data/http-v6.pcap +0 -0
  185. data/test/mu/scenario/test_data/http.pcap +0 -0
  186. data/test/mu/scenario/test_data/http_chunked.pcap +0 -0
  187. data/test/mu/scenario/test_data/http_deflate.pcap +0 -0
  188. data/test/mu/scenario/test_data/httpauth3.pcap +0 -0
  189. data/test/mu/scenario/test_data/icmp.pcap +0 -0
  190. data/test/mu/scenario/test_data/sip_signalled_call_1.pcap +0 -0
  191. data/test/mu/tc_pcap.rb +39 -0
  192. data/test/mu/testcase.rb +86 -0
  193. data/test/pcapr_local/arp.pcap +0 -0
  194. data/test/pcapr_local/data.js +3 -0
  195. data/test/pcapr_local/http_chunked.pcap +0 -0
  196. data/test/pcapr_local/tc_api.rb +181 -0
  197. data/test/pcapr_local/test.tgz +0 -0
  198. data/test/pcapr_local/test_scanner.rb +241 -0
  199. data/test/pcapr_local/test_xtractr.rb +219 -0
  200. data/test/pcapr_local/testcase.rb +107 -0
  201. data/test/test_export_to_scenario.sh +25 -0
  202. data/test/test_pcapr_local.rb +29 -0
  203. 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
@@ -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'