pcapr-local 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,69 @@
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 UDP < Packet
9
+ attr_accessor :src_port, :dst_port
10
+
11
+ def initialize src_port=0, dst_port=0
12
+ super()
13
+ @src_port = src_port
14
+ @dst_port = dst_port
15
+ end
16
+
17
+ def flow_id
18
+ return [:udp, @src_port, @dst_port]
19
+ end
20
+
21
+ FMT_nnnn = 'nnnn'
22
+ def self.from_bytes bytes
23
+ bytes_length = bytes.length
24
+ bytes_length >= 8 or
25
+ raise ParseError, "Truncated UDP header: expected 8 bytes, got #{bytes_length} bytes"
26
+ sport, dport, length, checksum = bytes.unpack(FMT_nnnn)
27
+ bytes_length >= length or
28
+ raise ParseError, "Truncated UDP packet: expected #{length} bytes, got #{bytes_length} bytes"
29
+ udp = UDP.new sport, dport
30
+ udp.payload_raw = bytes[8..-1]
31
+ udp.payload = bytes[8..length]
32
+ return udp
33
+ end
34
+
35
+ def write io, ip
36
+ length = @payload.length
37
+ length_8 = length + 8
38
+ if length_8 > 65535
39
+ Pcap.warning "UDP payload is too large"
40
+ end
41
+ pseudo_header = ip.pseudo_header length_8
42
+ header = [@src_port, @dst_port, length_8, 0] \
43
+ .pack FMT_nnnn
44
+ checksum = IP.checksum(pseudo_header + header + @payload)
45
+ header = [@src_port, @dst_port, length_8, checksum] \
46
+ .pack FMT_nnnn
47
+ io.write header
48
+ io.write @payload
49
+ end
50
+
51
+ def self.udp? packet
52
+ return packet.is_a?(Ethernet) &&
53
+ packet.payload.is_a?(IP) &&
54
+ packet.payload.payload.is_a?(UDP)
55
+ end
56
+
57
+ def to_s
58
+ return "udp(%d, %d, %s)" % [@src_port, @dst_port, @payload.inspect]
59
+ end
60
+
61
+ def == other
62
+ return super &&
63
+ self.src_port == other.src_port &&
64
+ self.dst_port == other.dst_port
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,164 @@
1
+ # http://www.mudynamics.com
2
+ # http://labs.mudynamics.com
3
+ # http://www.pcapr.net
4
+
5
+ require 'tempfile'
6
+ require 'fileutils'
7
+ require 'mu/scenario/pcap/fields'
8
+ require 'mu/pcap'
9
+ require 'json'
10
+
11
+ module Mu
12
+ class Scenario
13
+
14
+ module Pcap
15
+ TSHARK_READ_TIMEOUT = 10.0 # seconds
16
+ TSHARK_LINES_PER_PACKET = 16384
17
+ TSHARK_OPTS = "-n -o tcp.desegment_tcp_streams:false"
18
+ TSHARK_SIZE_OPTS = "-n -o 'column.format: cum_size, \"%B\"'"
19
+ TSHARK_PSML_OPTS = %Q{#{TSHARK_OPTS} -o 'column.format: "Protocol", "%p", "Info", "%i"'}
20
+
21
+ MAX_PCAP_SIZE = 102400 # 100KB
22
+ MAX_RAW_PCAP_SIZE_MB = 25
23
+ MAX_RAW_PCAP_SIZE = MAX_RAW_PCAP_SIZE_MB * 1024 * 1000
24
+ EXCLUDE_FROM_SIZE_CHECK = ['rtp'].freeze
25
+
26
+ class PcapTooLarge < StandardError; end
27
+
28
+ def self.validate_pcap_size(path)
29
+ tshark_filter = EXCLUDE_FROM_SIZE_CHECK.map{ |proto| "not #{proto}" }.join " and "
30
+ io = ::IO.popen "tshark #{TSHARK_SIZE_OPTS} -r #{path} -R '#{tshark_filter}' | tail -1"
31
+ if ::IO.select [ io ], nil, nil, TSHARK_READ_TIMEOUT
32
+ if io.eof?
33
+ size = 0
34
+ else
35
+ last_line = io.readline
36
+ size = last_line.to_i
37
+ end
38
+ end
39
+
40
+ if size.nil? or size == 0
41
+ size = File.size(path)
42
+ end
43
+
44
+ if size > MAX_PCAP_SIZE
45
+ raise PcapTooLarge, "Selected packets have a size of #{size} bytes which " +
46
+ "exceeds the #{MAX_PCAP_SIZE} byte maximum."
47
+ end
48
+
49
+ if size > MAX_RAW_PCAP_SIZE
50
+ raise PcapTooLarge, "Selected packets have a raw size of #{size} bytes which " +
51
+ "exceeds the #{MAX_RAW_PCAP_SIZE_MB}MB maximum."
52
+ end
53
+
54
+ return size
55
+ end
56
+
57
+ PAR_VERSION = 1
58
+ def self.export_to_par pcap_path, opts=nil
59
+ opts ||= {}
60
+
61
+ # Open pcap file
62
+ File.exist?(pcap_path) or raise "Cannot open file '#{pcap_path}'."
63
+ validate_pcap_size pcap_path
64
+ pcap = open pcap_path, 'rb'
65
+
66
+ # Get Mu::Pcap::Packets
67
+ packets = to_pcap_packets pcap, opts[:isolate_l7]
68
+
69
+ # Write normalized packets to tempfile
70
+ tmpdir = Dir.mktmpdir
71
+ norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
72
+ pcap = Mu::Pcap.from_packets packets
73
+ pcap.write norm_pcap
74
+ norm_pcap.close
75
+
76
+ # Get wireshark dissected field values for all packets.
77
+ `tshark -T fields #{TSHARK_OPTS} #{Fields::TSHARK_OPTS} -Eseparator='\xff' -r #{norm_pcap.path} > #{tmpdir}/fields`
78
+ fields = open "#{tmpdir}/fields", 'rb'
79
+
80
+ # Get wireshark dissected field values for all packets.
81
+ fields_array = []
82
+ if fields
83
+ packets.each do |packet|
84
+ fields_array << Fields.next_from_io(fields)
85
+ end
86
+ end
87
+
88
+ # Protocol specific preprocessing, packets may be deleted.
89
+ Rtp.preprocess packets, fields_array
90
+
91
+ File.open "#{tmpdir}/packets.dump", 'wb' do |f|
92
+ Marshal.dump packets, f
93
+ end
94
+
95
+ # Create a second pcap with packets removed.
96
+ norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
97
+ pcap = Mu::Pcap.from_packets packets
98
+ pcap.write norm_pcap
99
+ norm_pcap.close
100
+
101
+ # Dump PSML to file.
102
+ # (The no-op filter sometimes produces slighty more verbose descriptions.)
103
+ `tshark -T psml #{TSHARK_PSML_OPTS} -r #{norm_pcap.path} -R 'rtp or not rtp' > #{tmpdir}/psml`
104
+
105
+ # Dump PDML io file.
106
+ `tshark -T pdml #{TSHARK_OPTS} -r #{norm_pcap.path} > #{tmpdir}/pdml`
107
+ pdml = open "#{tmpdir}/pdml", 'rb'
108
+
109
+ # Create about
110
+ open "#{tmpdir}/about", 'w' do |about|
111
+ about.puts({:par_version => PAR_VERSION}.to_json)
112
+ end
113
+
114
+ # Create zip
115
+ Dir.chdir tmpdir do
116
+ system "zip -q dissected.zip about pdml psml fields packets.dump normalized.pcap"
117
+ return open("dissected.zip")
118
+ end
119
+ ensure
120
+ if tmpdir
121
+ FileUtils.rm_rf tmpdir
122
+ end
123
+ end
124
+
125
+ def self.to_pcap_packets io, isolate_l7=true
126
+ packets = []
127
+
128
+ # Read Pcap packets from Pcap
129
+ Mu::Pcap.each_pkthdr io do |pkthdr|
130
+ if pkthdr.len != pkthdr.caplen
131
+ raise Mu::Pcap::ParseError, "Error: Capture contains truncated packets. " +
132
+ "Try recapturing with an increased snapshot length."
133
+ end
134
+ if not pkthdr.pkt.is_a? Mu::Pcap::Ethernet
135
+ warning 'Unable to parse packet, skipping.'
136
+ end
137
+ packets << pkthdr.pkt
138
+ end
139
+
140
+ if (packets.length == 0)
141
+ raise Mu::Pcap::ParseError, "No valid packets found!"
142
+ end
143
+
144
+ packets = Mu::Pcap::IPv4.reassemble packets
145
+
146
+ if isolate_l7
147
+ packets = Mu::Pcap::Packet.isolate_l7 packets
148
+ end
149
+
150
+ packets = Mu::Pcap::Packet.normalize packets
151
+ packets = Mu::Pcap::TCP.split packets
152
+
153
+ packets
154
+ end
155
+
156
+ def self.warning msg
157
+ $stderr.puts "WARNING: #{msg}"#, caller, $!
158
+ end
159
+ end
160
+
161
+ end
162
+ end
163
+
164
+ require 'mu/scenario/pcap/rtp'
@@ -0,0 +1,50 @@
1
+ # http://www.mudynamics.com
2
+ # http://labs.mudynamics.com
3
+ # http://www.pcapr.net
4
+
5
+ require 'mu/scenario/pcap'
6
+
7
+ module Mu
8
+ class Scenario
9
+ module Pcap
10
+
11
+ class Fields
12
+ FIELDS = [
13
+ :rtp,
14
+ :"rtp.setup-frame"
15
+ ].freeze
16
+ FIELD_COUNT = FIELDS.length
17
+ SEPARATOR = "\xff".freeze
18
+ TSHARK_OPTS = "-Eseparator='#{SEPARATOR}'"
19
+ FIELDS.each do |field|
20
+ TSHARK_OPTS << " -e #{field}"
21
+ end
22
+ TSHARK_OPTS.freeze
23
+
24
+ def self.readline io
25
+ if ::IO.select [ io ], nil, nil, Pcap::TSHARK_READ_TIMEOUT
26
+ return io.readline.chomp
27
+ end
28
+
29
+ raise Errno::ETIMEDOUT, "read timed out"
30
+ end
31
+
32
+ def self.next_from_io io
33
+ if line = readline(io)
34
+ fields = line.split SEPARATOR, FIELD_COUNT
35
+ hash = {}
36
+ FIELDS.each do |key|
37
+ val = fields.shift
38
+ hash[key] = val.empty? ? nil : val
39
+ end
40
+ return hash
41
+ end
42
+ rescue Exception => e
43
+ Pcap.warning e.message
44
+ return nil
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,71 @@
1
+ # http://www.mudynamics.com
2
+ # http://labs.mudynamics.com
3
+ # http://www.pcapr.net
4
+
5
+ module Mu
6
+ class Scenario
7
+ module Pcap
8
+ module Rtp
9
+
10
+ TRUNC_COUNT = 5
11
+
12
+ def self.preprocess packets, fields_per_packet
13
+ signaled_by = []
14
+ prev_signal_frame = {}
15
+ packets.each_with_index do |packet, idx|
16
+ fields = fields_per_packet[idx]
17
+ if fields and fields[:rtp]
18
+ flow_id = packet.flow_id
19
+ if frame = fields[:"rtp.setup-frame"]
20
+ prev_signal_frame[flow_id] = frame
21
+ else
22
+ if frame = prev_signal_frame[flow_id]
23
+ fields[:"rtp.setup-frame"] = frame
24
+ else
25
+ packets[idx] = nil
26
+ fields_per_packet[idx] = nil
27
+ next
28
+ end
29
+ end
30
+ sig_idx = frame.to_i
31
+ signaled_by[idx] = sig_idx
32
+ end
33
+ end
34
+
35
+ flow_to_count = Hash.new 0
36
+ prev_setup_frame = {}
37
+ keep_frames = []
38
+ packets.each_with_index do |packet, idx|
39
+ if setup_frame = signaled_by[idx]
40
+ flow = packet.flow_id
41
+ count = flow_to_count[flow]
42
+ if setup_frame != prev_setup_frame[flow]
43
+ prev_setup_frame[flow] = setup_frame
44
+ count = 1
45
+ else
46
+ count += 1
47
+ end
48
+ if count <= TRUNC_COUNT
49
+ keep_frames << idx + 1
50
+ else
51
+ packets[idx] = nil
52
+ fields_per_packet[idx] = nil
53
+ end
54
+ flow_to_count[flow] = count
55
+ end
56
+ end
57
+
58
+ packets.reject! {|p| not p }
59
+ fields_per_packet.reject! {|p| not p }
60
+
61
+ filter = "not rtp"
62
+ keep_frames.each do |frame|
63
+ filter << " or frame.number == #{frame}"
64
+ end
65
+
66
+ return filter
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,159 @@
1
+ # http://www.mudynamics.com
2
+ # http://labs.mudynamics.com
3
+ # http://www.pcapr.net
4
+
5
+ require 'rubygems'
6
+ require 'environment'
7
+ require 'pcapr_local/config'
8
+ require 'pcapr_local/scanner'
9
+ require 'pcapr_local/server'
10
+ require 'pcapr_local/xtractr'
11
+ require 'logger'
12
+
13
+ module PcaprLocal
14
+ START_SCRIPT = File.join(ROOT, 'bin/pcapr_start.rb')
15
+
16
+ # We share a single Logger across all of pcapr.Local.
17
+ Logger = Logger.new(STDOUT)
18
+
19
+ # Recreate logger using configured log location.
20
+ LOGFILE = "server.log"
21
+ def self.start_logging log_dir
22
+ if log_dir and not log_dir.empty?
23
+ if const_defined? :Logger
24
+ remove_const :Logger
25
+ end
26
+
27
+ logfile = File.join(log_dir, LOGFILE)
28
+ FileUtils.mkdir_p log_dir
29
+ const_set :Logger, Logger.new(logfile, 5)
30
+ end
31
+ end
32
+
33
+ # Start xtractr instance manager.
34
+ def self.start_xtractr config
35
+ xtractr_config = config['xtractr'].merge(
36
+ "index_dir" => config.fetch("index_dir"),
37
+ "pcap_dir" => config.fetch("pcap_dir")
38
+ )
39
+ Xtractr.new xtractr_config
40
+ end
41
+
42
+ # Start file system scanner.
43
+ def self.start_scanner config, db, xtractr
44
+ scanner_config = config.fetch('scanner').merge(
45
+ "index_dir" => config.fetch("index_dir"),
46
+ "pcap_dir" => config.fetch("pcap_dir"),
47
+ "db" => db,
48
+ "xtractr" => xtractr
49
+ )
50
+ PcaprLocal::Scanner.start scanner_config
51
+ end
52
+
53
+ # Start webserver UI/API
54
+ def self.start_app config, db, scanner, xtractr
55
+ app_config = config.fetch "app"
56
+ root = File.expand_path(File.dirname(__FILE__))
57
+ app_file = File.join(root, "pcapr_local/server.rb")
58
+ PcaprLocal::Server.run! \
59
+ :app_file => app_file,
60
+ :dump_errors => true,
61
+ :logging => true,
62
+ :port => app_config.fetch("port"),
63
+ :bind => app_config.fetch("host"),
64
+ :db => db,
65
+ :scanner => scanner,
66
+ :xtractr => xtractr
67
+ end
68
+
69
+ def self.get_db config
70
+ PcaprLocal::DB.get_db config.fetch("couch")
71
+ end
72
+
73
+ def self.start config=nil
74
+ config ||= PcaprLocal::Config.config
75
+
76
+ # Check that server is not already running.
77
+ check_pid_file config['pidfile']
78
+
79
+ # Start logging.
80
+ if config["log_dir"]
81
+ start_logging config['log_dir']
82
+ end
83
+ start_msg = "Starting server at #{config['app']['host']}:#{config['app']['port']}"
84
+ Logger.info start_msg
85
+ puts start_msg
86
+ puts "Log is at #{config['log_dir']}/#{LOGFILE}"
87
+
88
+ # Deamonize
89
+ unless config['debug_mode']
90
+ puts "Moving server process to the background. Run 'stoppcapr' to stop the server."
91
+ Process.daemon
92
+ end
93
+
94
+ # Create pid file that will be deleted when we shutdown.
95
+ create_pid_file config['pidfile']
96
+
97
+ # Get database instance
98
+ db = get_db config
99
+
100
+ # Xtractr manager
101
+ xtractr = start_xtractr config
102
+
103
+ # Start scanner thread
104
+ scanner = start_scanner config, db, xtractr
105
+
106
+ # Start application server
107
+ start_app config, db, scanner, xtractr
108
+ end
109
+
110
+ def self.check_pid_file file
111
+ if File.exist? file
112
+ # If we get Errno::ESRCH then process does not exist and
113
+ # we can safely cleanup the pid file.
114
+ pid = File.read(file).to_i
115
+ begin
116
+ Process.kill(0, pid)
117
+ rescue Errno::ESRCH
118
+ stale_pid = true
119
+ rescue
120
+ end
121
+
122
+ unless stale_pid
123
+ puts "Server is already running (pid=#{pid})"
124
+ exit
125
+ end
126
+ end
127
+ end
128
+
129
+ def self.create_pid_file file
130
+ File.open(file, "w") { |f| f.puts Process.pid }
131
+
132
+ # Remove pid file during shutdown
133
+ at_exit do
134
+ Logger.info "Shutting down." rescue nil
135
+ if File.exist? file
136
+ File.unlink file
137
+ end
138
+ end
139
+ end
140
+
141
+ # Sends SIGTERM to process in pidfile. Server should trap this
142
+ # and shutdown cleanly.
143
+ def self.stop config=nil
144
+ user_config = Config.user_config_path
145
+ if File.exist?(user_config)
146
+ config = PcaprLocal::Config.config user_config
147
+ pid_file = config["pidfile"]
148
+ if pid_file and File.exist? pid_file
149
+ pid = Integer(File.read(pid_file))
150
+ Process.kill -15, -pid
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ if __FILE__ == $0
157
+ PcaprLocal.start
158
+ end
159
+