flowtag 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ �b:�rT���b>k�nqG��s�������ڿ�܆��dT�(��
2
+ ��&�"<�˧�tL˸;��O++� �؎��Ii���>�aG�yQ�k�~�6�Ů�=s����ө��f!<5ɧ��n�(Zݙę���T��[��͆ME z����I< �"��y���}��-W�+9��/)wk�j� �E�ʍ�v�ԖH�w�����e[��{�a��R���|hhI+��DN3Eb�#���!�(FF�9�h>�!#3X��
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Chris Lee, PhD
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = flowtag
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to flowtag
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 Chris Lee, PhD. See LICENSE.txt for
18
+ further details.
19
+
data/bin/flowtag ADDED
@@ -0,0 +1,181 @@
1
+ #! /opt/local/bin/ruby
2
+ # DESCRIPTION: presents the user with a GUI interface to visualize and explore flows found from a given pcap file.
3
+ # FLOWTAG - parses and visualizes pcap data
4
+ # Copyright (C) 2007 Christopher Lee
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ unless ARGV.length > 0
20
+ puts "Usage: #{$0} [-o <outputdir>] <pcapfile>"
21
+ exit
22
+ end
23
+
24
+ require 'tk' # this takes a long time to load
25
+ require 'tk/labelframe'
26
+ require 'flowtag/flowdb'
27
+ require 'flowtag/flowcanvas'
28
+ require 'flowtag/flowtable'
29
+ require 'tk-double-slider'
30
+
31
+ def select_cb(flows)
32
+ $flowtable.clear
33
+ $flowtable.addflows(flows.sort_by { |fl| fl[FlowDB::ST] })
34
+ $flowview.clear
35
+ $tag_entry.delete('@0','end')
36
+ $currflow = nil
37
+ end
38
+
39
+ def tableselect_cb(idxs, flows)
40
+ return if flows.length < 1
41
+ flow = flows[0]
42
+ idx = idxs[0]
43
+ (st,sip,dip,sp,dp,pkts,bytes) = flow.strip.split(/\s+/)
44
+ $curridx = idx
45
+ $currflow = [sip,dip,sp,dp]
46
+ $flowcanvas.select_flow(sip, dip, sp.to_i, dp.to_i)
47
+ payload = $fdb.getflowdata(sip, dip, sp.to_i, dp.to_i, 5000)
48
+ # replace the gremlins that destroy the performance of the flow vie
49
+ payload = payload.gsub(/[\x00-\x09\x0b-\x1f\x7f-\xff]/) do |c| sprintf("\\x%02x", c[0]) end
50
+ $flowview.clear
51
+ $flowview.insert('end',payload) if payload
52
+ tags = $fdb.getflowtags($currflow)
53
+ $tag_entry.delete('@0','end')
54
+ $tag_entry.insert('end',tags.join(" "))
55
+ end
56
+
57
+ def tag_cb(tags)
58
+ return unless $currflow
59
+ sip,dip,sp,dp = $currflow
60
+ $fdb.tag_flow($currflow,tags.split(/\s+/))
61
+ $flowtable.update_flow($curridx, $fdb.flows[$currflow.join("|")])
62
+ alltags = $fdb.tags
63
+ $tags_list.clear
64
+ alltags.sort.each do |tag|
65
+ $tags_list.insert 'end',tag
66
+ end
67
+ end
68
+
69
+ def tag_select
70
+ tags = []
71
+ indices = $tags_list.curselection
72
+ indices.each do |i|
73
+ tags.push($tags_list.get(i))
74
+ end
75
+ $tags_list.focus
76
+ flows = []
77
+ tags.each do |tag|
78
+ flows += $fdb.flows_taggedwith(tag)
79
+ end
80
+ select_cb(flows)
81
+ end
82
+
83
+ def finish
84
+ $fdb.writetagdb
85
+ $fdb.close
86
+ puts "Thank you for playing. Please send suggestions to scholar.freenode@gmail.com"
87
+ exit
88
+ end
89
+ if ARGV[0] == '-o'
90
+ outputdir = ARGV[1]
91
+ $fdb = FlowDB.new(ARGV[2],outputdir)
92
+ else
93
+ $fdb = FlowDB.new(ARGV[0])
94
+ end
95
+ pkt_min = byte_min = time_min = 2**32
96
+ pkt_max = byte_max = time_max = 0
97
+ $fdb.flows.each do |key,flow|
98
+ pkt_min = flow[FlowDB::PKTS] if flow[FlowDB::PKTS] < pkt_min
99
+ pkt_max = flow[FlowDB::PKTS] if flow[FlowDB::PKTS] > pkt_max
100
+ byte_min = flow[FlowDB::BYTES] if flow[FlowDB::BYTES] < byte_min
101
+ byte_max = flow[FlowDB::BYTES] if flow[FlowDB::BYTES] > byte_max
102
+ time_min = flow[FlowDB::ST] if flow[FlowDB::ST] < time_min
103
+ time_max = flow[FlowDB::ST] if flow[FlowDB::ST] > time_max
104
+ end
105
+ root = TkRoot.new() {
106
+ title "FlowTag v2.0"
107
+ protocol('WM_DELETE_WINDOW', proc{ finish })
108
+ }
109
+ root.bind('Control-q', proc{ finish })
110
+ root.bind('Control-c', proc{ finish })
111
+ trap('SIGINT') { finish }
112
+ TkPalette.setPalette('background','grey20','foreground','white','disabledBackground','grey20','disabledForeground','grey30')
113
+ left_frame = TkFrame.new(root)
114
+ left_top_frame = TkLabelFrame.new(left_frame,:text=>'Flow Table')
115
+ left_mid_frame = TkLabelFrame.new(left_frame,:text=>'Flow Tags')
116
+ left_bot_frame = TkLabelFrame.new(left_frame,:text=>'Payload View')
117
+ right_frame = TkFrame.new(root)
118
+ right_top_frame = TkLabelFrame.new(right_frame,:text=>'Connection Visualization')
119
+ right_bot_frame = TkLabelFrame.new(right_frame,:text=>'Filters')
120
+ tags_frame = TkLabelFrame.new(root, :text=>'Tags List')
121
+
122
+ left_frame.grid(:row=>0,:column=>0,:sticky=>'new')
123
+ left_top_frame.grid(:row=>0,:column=>0,:sticky=>'news')
124
+ left_mid_frame.grid(:row=>1,:column=>0,:sticky=>'news')
125
+ left_bot_frame.grid(:row=>2,:column=>0,:sticky=>'news')
126
+ right_frame.grid(:row=>0,:column=>1,:sticky=>'new')
127
+ right_top_frame.grid(:row=>0,:column=>0,:sticky=>'news')
128
+ right_bot_frame.grid(:row=>1,:column=>0,:sticky=>'news')
129
+ tags_frame.grid(:row=>0,:column=>2,:sticky=>'new')
130
+
131
+ # LEFT FRAME
132
+ $flowtable = FlowTable.new(left_top_frame,$fdb.flows.values.sort_by { |fl| fl[FlowDB::ST] })
133
+ $flowtable.pack(:side=>'top',:expand=>1,:fill=>'both',:anchor=>'n')
134
+ $flowtable.set_select_cb(proc { |idxs,flows| tableselect_cb(idxs,flows) })
135
+
136
+ $tag_entry = TkEntry.new(left_mid_frame) {
137
+ font TkFont.new('Monaco 12')
138
+ }
139
+ $tag_entry.bind('Return', proc { |e| tag_cb($tag_entry.get) } )
140
+ $tag_entry.pack(:side=>'right', :expand=>1, :fill=>'x')
141
+
142
+ flowview_scrollbar = TkScrollbar.new(left_bot_frame)
143
+ $flowview = flowview = TkText.new(left_bot_frame) {
144
+ font TkFont.new('Monaco 12')
145
+ wrap 'char'
146
+ height 30
147
+ width 60
148
+ }
149
+ flowview_scrollbar.pack(:side=>'right', :fill=>'y')
150
+ $flowview.pack(:side=>'bottom',:expand=>1,:fill=>'both')
151
+ $flowview.yscrollbar(flowview_scrollbar);
152
+
153
+ # TAGS FRAME
154
+ $tags_list = TkScrollbox.new(tags_frame) {
155
+ font TkFont.new('Monaco 12')
156
+ height 40
157
+ width 20
158
+ }
159
+ $tags_list.pack(:fill=>'y',:expand=>1)
160
+ $fdb.tags.sort.each do |tag|
161
+ $tags_list.insert('end',tag)
162
+ end
163
+ $tags_list.bind('<ListboxSelect>',proc { tag_select })
164
+
165
+ # RIGHT FRAME
166
+ $flowcanvas = FlowCanvas.new(right_top_frame,$fdb.flows.sort_by { |k,fl| fl[FlowDB::ST] })
167
+ $timeslide = TkDoubleSlider.new(right_bot_frame, :min=>time_min, :max=>time_max, :low=>time_min, :high=>time_max, :snap => 300, :label=>'Time', :valuefmt => proc { |x| Time.at(x).strftime("%H:%M") }, :deltafmt => proc { |x| sprintf("%0.2f hours", (x/3600.0)) })
168
+ $pktslide = TkDoubleSlider.new(right_bot_frame, :min=>1, :max=>pkt_max, :low=>1, :high=>pkt_max, :logbase => true, :snap => 1.0, :label=>'Packets')
169
+ $byteslide = TkDoubleSlider.new(right_bot_frame, :min=>1, :max=>byte_max, :low=>1, :high=>byte_max, :logbase => true, :snap => 1.0, :label=>'Bytes')
170
+ $flowcanvas.pack
171
+ $timeslide.pack(:side=>'top')
172
+ $pktslide.pack
173
+ $byteslide.pack(:side=>'bottom')
174
+ $flowcanvas.set_select_cb(proc { |x| select_cb(x) })
175
+ $timeslide.change_cb = proc { |low,high| $flowcanvas.set_time_range(low,high) };
176
+ $pktslide.change_cb = proc { |low,high| $flowcanvas.set_packet_range(low,high) };
177
+ $byteslide.change_cb = proc { |low,high| $flowcanvas.set_byte_range(low,high) };
178
+ $currflow = nil
179
+
180
+ Tk.mainloop()
181
+
data/bin/ftlistflows ADDED
@@ -0,0 +1,29 @@
1
+ #! /opt/local/bin/ruby
2
+ # DESCRIPTION: is part of the flowtag toolkit and prints the flows from a flowdb
3
+ # FLOWTAG - parses and visualizes pcap data
4
+ # Copyright (C) 2007 Christopher Lee
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require 'flowtag/flowdb'
20
+
21
+ unless ARGV.length > 0
22
+ puts "Usage: $0 <pcapfile>"
23
+ exit
24
+ end
25
+ ARGV.each do |f|
26
+ fdb = FlowDB.new(f)
27
+ fdb.dumpflows
28
+ end
29
+
data/bin/ftpcap2flowdb ADDED
@@ -0,0 +1,26 @@
1
+ #! /opt/local/bin/ruby
2
+ # DESCRIPTION: is part of the flowtag toolkit and generates a flow database from a pcap file
3
+ # Copyright (C) 2007 Christopher Lee
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'flowtag/flowdb'
19
+
20
+ unless ARGV.length > 0
21
+ puts "Usage: $0 <pcapfile>"
22
+ exit
23
+ end
24
+ ARGV.each do |f|
25
+ FlowDB.new(f)
26
+ end
data/bin/ftprintflow ADDED
@@ -0,0 +1,27 @@
1
+ #! /opt/local/bin/ruby
2
+ # DESCRIPTION: is part of the flowtag toolkit and prints the contents of an indicated flow
3
+ # FLOWTAG - parses and visualizes pcap data
4
+ # Copyright (C) 2007 Christopher Lee
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require 'flowtag/flowdb'
20
+
21
+ unless ARGV.length == 5
22
+ puts "Usage: $0 <pcapfile> <sip> <dip> <sp> <dp>"
23
+ exit
24
+ end
25
+ (f,sip,dip,sp,dp) = ARGV
26
+ fdb = FlowDB.new(f)
27
+ puts fdb.getflowdata(sip, dip, sp.to_i, dp.to_i)
data/lib/flowtag.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'flowtag/flowcanvas'
2
+ require 'flowtag/flowdb'
3
+ require 'flowtag/flowtable'
4
+ require 'flowtag/pcapparser'
@@ -0,0 +1,124 @@
1
+ # DESCRIPTION: is part of the flowtag toolkit and provides a Tk widget for visualizing and selecting flows on a canvas
2
+ # FLOWTAG - parses and visualizes pcap data
3
+ # Copyright (C) 2007 Christopher Lee
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'date'
19
+ require 'tk-parallel-coordinates'
20
+
21
+ module FlowTag
22
+ class FlowCanvas
23
+ def cb_select(tuples)
24
+ @selected_flows = []
25
+ tuples.each do |t|
26
+ key = t.join("|")
27
+ @selected_flows += @flow_keys[key]
28
+ end
29
+ @select_cb.call @selected_flows if @select_cb
30
+ end
31
+
32
+ def select_flow(sip,dip,sp,dp)
33
+ @pcp.set_tuple_state(@cflow,TK::ParallelCoordinates::STATE_NORMAL) if @cflow
34
+ key = dp.to_s+"|"+sip
35
+ @cflow = key
36
+ @pcp.set_tuple_state(key,TK::ParallelCoordinates::STATE_CURRENT)
37
+ end
38
+
39
+ def set_select_cb(callback)
40
+ @select_cb = callback
41
+ end
42
+
43
+ def set_time_range(low, high)
44
+ @time_low = low
45
+ @time_high = high
46
+ filter
47
+ end
48
+
49
+ def set_byte_range(low, high)
50
+ @byte_low = low
51
+ @byte_high = high
52
+ filter
53
+ end
54
+
55
+ def set_packet_range(low, high)
56
+ @pkt_low = low
57
+ @pkt_high = high
58
+ filter
59
+ end
60
+
61
+ def filter
62
+ @flow_keys.each do |key,flows|
63
+ flows.each do |fl|
64
+ if fl[FlowDB::PKTS] < @pkt_low or fl[FlowDB::PKTS] > @pkt_high or
65
+ fl[FlowDB::BYTES] < @byte_low or fl[FlowDB::BYTES] > @byte_high or
66
+ fl[FlowDB::ST] < @time_low or fl[FlowDB::ST] > @time_high
67
+ @pcp.set_tuple_state(key,TK::ParallelCoordinates::STATE_FILTERED)
68
+ else
69
+ @pcp.set_tuple_state(key,TK::ParallelCoordinates::STATE_NORMAL)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def pack(*args)
76
+ @pcp.pack(*args)
77
+ end
78
+
79
+ def initialize(parent, flows)
80
+ hostseen = {}
81
+ hosts = []
82
+ @selected_flows = @flows = flows
83
+ flows.each do |k,fl|
84
+ sip = fl[FlowDB::SIP]
85
+ next if hostseen[sip]
86
+ hosts.push(sip)
87
+ hostseen[sip]=1
88
+ end
89
+ model = [
90
+ {
91
+ :name => 'Port',
92
+ :type => 'range',
93
+ :scale => '3rt',
94
+ :min => 0,
95
+ :max => 65535,
96
+ :ofmt => '%d',
97
+ #:items => [1,22,80,137,443,1024,5900,6667,31337,65335]
98
+ },
99
+ {
100
+ :name => 'Host',
101
+ :type => 'list',
102
+ :list => hosts
103
+ }
104
+ ]
105
+ @pcp = TK::ParallelCoordinates.new(parent, 500, 360, model)
106
+ @pcp.set_select_cb( proc { |tuples| cb_select(tuples) } )
107
+ @flow_keys = {}
108
+ @pkt_low = @byte_low = @pkt_high = @byte_high = @time_high = 0
109
+ @time_low = 2**32
110
+ flows.each do |k,fl|
111
+ key = fl[FlowDB::DP].to_s+"|"+fl[FlowDB::SIP]
112
+ skip = (@flow_keys[key]) ? true:false
113
+ @flow_keys[key] = [] unless @flow_keys[key]
114
+ @flow_keys[key].push(fl)
115
+ @pkt_high = fl[FlowDB::PKTS] if fl[FlowDB::PKTS] > @pkt_high
116
+ @byte_high = fl[FlowDB::BYTES] if fl[FlowDB::BYTES] > @byte_high
117
+ @time_low = fl[FlowDB::ST] if fl[FlowDB::ST] < @time_low
118
+ @time_high = fl[FlowDB::ST] if fl[FlowDB::ST] > @time_high
119
+ next if skip
120
+ @pcp.addtuple(key,TK::ParallelCoordinates::STATE_NORMAL,[fl[FlowDB::DP],fl[FlowDB::SIP]])
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,266 @@
1
+ # DESCRIPTION: is part of the flowtag toolkit and wraps a flowdb
2
+ # FLOWTAG - parses and visualizes pcap data
3
+ # Copyright (C) 2007 Christopher Lee
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'flowtag/pcapparser'
19
+ require 'ipaddr'
20
+
21
+ module FlowTag
22
+ class FlowDB
23
+ ST = 0
24
+ SIP = 1
25
+ DIP = 2
26
+ SP = 3
27
+ DP = 4
28
+ PKTS = 5
29
+ BYTES = 6
30
+ FIRST_PKT = 7
31
+ TAGS = 8
32
+
33
+ attr_reader :flows, :tags_flows
34
+ def initialize(pcapfile,basedir=nil)
35
+ @pcapfile = pcapfile
36
+ basedir = File.dirname(pcapfile) unless basedir
37
+ basename = basedir+"/"+File.basename(pcapfile)
38
+ raise "Cannot find pcapfile, #{pcapfile}" unless File.exists?(pcapfile)
39
+ @pcapfh = File.new(pcapfile, 'rb')
40
+ unless File.exists?(basename+".flows") and File.exists?(basename+'.pkts') and File.exists?(basename+'.tags')
41
+ # the flow and packet database are missing, let's generate them
42
+ flowdb = File.new(basename+".flows", 'wb')
43
+ pktdb = File.new(basename+".pkts", 'wb')
44
+ tagdb = File.new(basename+".tags", 'w')
45
+ create_flowdb(basename, flowdb, pktdb)
46
+ flowdb.close
47
+ pktdb.close
48
+ tagdb.close
49
+ @flowdb = File.new(basename+".flows", 'rb')
50
+ @pktdb = File.new(basename+".pkts", 'rb')
51
+ @tagdb = File.new(basename+".tags", 'r')
52
+ @flows = readflows
53
+ @tags_flows = {}
54
+ @flows_tags = {}
55
+ else
56
+ @flowdb = File.new(basename+".flows", 'rb')
57
+ @pktdb = File.new(basename+".pkts", 'rb')
58
+ @tagdb = File.new(basename+".tags", 'r')
59
+ @flows = readflows
60
+ # readtags must be called AFTER readflows
61
+ @tags_flows, @flows_tags = readtags
62
+ end
63
+ @tags_updated = false
64
+ end
65
+
66
+ def close
67
+ @pcapfh.close if @pcapfh
68
+ @pktdb.close if @pktdb
69
+ @flowdb.close if @flowdb
70
+ @tagdb.close if @tagdb
71
+ end
72
+
73
+ def create_flowdb(pcapfile, flowdb, pktdb)
74
+ offset = 24 # offset into the pcap file, starts at 24 to skip header
75
+ pktid = 0 # database id of the pkt, increments for each matching packet
76
+ flows = {} # flow hash to store and check for keys
77
+ pkts = [] # stores the packet database
78
+ pcap = PcapParser.new(File.new(pcapfile,'rb'))
79
+ pcap.each do |pkt|
80
+ unless pkt.tcp?
81
+ offset += 16 + pkt.length
82
+ next
83
+ end
84
+ tuple = [pkt.ip_src, pkt.tcp_sport, pkt.tcp_dport, pkt.ip_dst]
85
+ key = tuple.join "|"
86
+ rkey = tuple.reverse.join "|"
87
+ if flows[rkey]
88
+ key = rkey
89
+ end
90
+ if flows[key]
91
+ last_pkt_id = flows[key][:last_pkt]
92
+ pkts[last_pkt_id][:next_pkt] = pktid
93
+ flows[key][:last_pkt] = pktid
94
+ else
95
+ flows[key] = { :st => pkt.time, :sip => pkt.ip_src, :dip => pkt.ip_dst, :sp => pkt.tcp_sport, :dp => pkt.tcp_dport, :pkts => 0, :bytes => 0 }
96
+ flows[key][:first_pkt] = flows[key][:last_pkt] = pktid
97
+ end
98
+ flows[key][:pkts] += 1
99
+ flows[key][:bytes] += pkt.length
100
+ pkts[pktid] = { :offset => offset, :next_pkt => 0 }
101
+ offset += 16 + pkt.length
102
+ pktid+=1
103
+ end
104
+ pcap.close
105
+
106
+ # write out the flow database and the packet database
107
+ flows.sort_by{|key,flow| flow[:st]}.each do |key,flow|
108
+ flowdb.write( [ flow[:st], flow[:sip], flow[:dip], flow[:sp], flow[:dp], flow[:pkts], flow[:bytes], flow[:first_pkt] ].pack("NNNnnNNn") )
109
+ flows[key] = [flow[:st], [flow[:sip]].pack("N").unpack("C4").join("."), [flow[:dip]].pack("N").unpack("C4").join("."), flow[:sp], flow[:dp], flow[:pkts], flow[:bytes], flow[:first_pkt], []]
110
+ end
111
+ pkts.each do |pkt|
112
+ pktdb.write([pkt[:offset],pkt[:next_pkt]].pack("Nn"))
113
+ end
114
+ return flows
115
+ end
116
+
117
+ def dumpflows
118
+ @flows.sort_by { |k,f| f[ST].to_i }.each do |key,flow|
119
+ puts flow.join(" ")
120
+ end
121
+ end
122
+
123
+ def readflows
124
+ flows = {}
125
+ @flowdb.seek(0)
126
+ while ! @flowdb.eof?
127
+ (st,sip,dip,sp,dp,pkts,bytes,first_pkt) = @flowdb.read(26).unpack("NNNnnNNn")
128
+ sip = IPAddr.ntop([sip].pack("N"))
129
+ dip = IPAddr.ntop([dip].pack("N"))
130
+ key = [sip,dip,sp,dp].join("|")
131
+ flows[key] = [st,sip,dip,sp,dp,pkts,bytes,first_pkt,[]]
132
+ end
133
+ flows
134
+ end
135
+
136
+ def getflows
137
+ return @flows
138
+ end
139
+
140
+ def readtags
141
+ @tagdb.seek 0
142
+ flows_tags = {}
143
+ tags_flows = {}
144
+ @tagdb.each_line do |l|
145
+ (sip,dip,sp,dp,*tags)=l.strip.split(/\|/)
146
+ key = [sip,dip,sp,dp].join("|")
147
+ flows_tags[key] = tags
148
+ @flows[key][TAGS] = tags
149
+ tags.each do |tag|
150
+ tags_flows[tag] = [] unless tags_flows[tag]
151
+ tags_flows[tag].push(key)
152
+ end
153
+ end
154
+ [tags_flows, flows_tags]
155
+ end
156
+
157
+ def writetagdb
158
+ return true unless @tags_updated
159
+ @tagdb.close if @tagdb
160
+ tagdb = File.new(@pcapfile+'.tags', 'w')
161
+ @flows_tags.each do |key,tags|
162
+ tagdb.puts key+"|"+tags.join("|")
163
+ end
164
+ tagdb.close
165
+ @tagdb = File.new(@pcapfile+'.tags', 'r')
166
+ @tags_updated = false
167
+ true
168
+ end
169
+
170
+ def tag_flow(flow, tags)
171
+ @tags_updated = true
172
+ tags.uniq!
173
+ key = flow.join("|")
174
+ if @flows_tags[key]
175
+ currtags = @flows_tags[key]
176
+ currtags.each do |tag|
177
+ @tags_flows[tag].delete(key)
178
+ @tags_flows.delete(tag) if @tags_flows[tag].length == 0
179
+ end
180
+ end
181
+ @flows_tags[key] = tags
182
+ @flows[key][TAGS] = tags
183
+ tags.each do |tag|
184
+ @tags_flows[tag] = [] unless @tags_flows[tag]
185
+ @tags_flows[tag].push(key)
186
+ end
187
+ end
188
+
189
+ def getflowtags(flow)
190
+ key = flow.join("|")
191
+ @flows_tags[key] || []
192
+ end
193
+
194
+ def flows_taggedwith(tag)
195
+ keys = @tags_flows[tag]
196
+ flows = []
197
+ if keys
198
+ keys.each do |key|
199
+ flows.push(@flows[key])
200
+ end
201
+ end
202
+ flows
203
+ end
204
+
205
+ def tags
206
+ @tags_flows.keys
207
+ end
208
+
209
+ def getfirstpktid(sip, dip, sp, dp)
210
+ @flows.each do |key,flow|
211
+ if flow[SIP] == sip and flow[DIP] == dip and flow[SP] == sp and flow[DP] == dp
212
+ return flow[FIRST_PKT]
213
+ end
214
+ end
215
+ -1
216
+ end
217
+
218
+ def getpktrec(pktid)
219
+ offset = 6*pktid
220
+ @pktdb.seek(offset)
221
+ (poff, pktid) = @pktdb.read(6).unpack("Nn")
222
+ [poff, pktid]
223
+ end
224
+
225
+ def getdata(offset)
226
+ @pcapfh.seek(offset)
227
+ # this needs to be endian sensitive...
228
+ if @endian
229
+ (tv_sec, tv_usec, caplen, origlen) = @pcapfh.read(16).unpack(@endian)
230
+ else
231
+ (tv_sec, tv_usec, caplen, origlen) = @pcapfh.read(16).unpack("VVVV")
232
+ @endian = "VVVV"
233
+ if caplen > 5000
234
+ @pcapfh.seek(offset)
235
+ (tv_sec, tv_usec, caplen, origlen) = @pcapfh.read(16).unpack("NNNN")
236
+ @endian = "NNNN"
237
+ end
238
+ end
239
+ pkt = @pcapfh.read(caplen)
240
+ type = (pkt[12,2].unpack("n"))[0]
241
+ return nil unless type == 0x0800
242
+ return nil unless pkt[14] == 0x45
243
+ return nil unless pkt[14+10-1] == 0x06
244
+ tcp_header_len = (pkt[14+20+12]>>4)<<2
245
+ pkt[14+20+tcp_header_len,1000] || ''
246
+ end
247
+
248
+ def getflowdata_frompkt(first_pkt, limit=nil)
249
+ (poff,npkt) = fp_rec = getpktrec(first_pkt)
250
+ payload = getdata(poff)
251
+ while npkt != 0
252
+ (poff,npkt) = getpktrec(npkt)
253
+ data = getdata(poff)
254
+ payload += data if data
255
+ break if limit and payload.length > limit
256
+ end
257
+ payload
258
+ end
259
+
260
+ def getflowdata(sip, dip, sp, dp, limit=nil)
261
+ first_pkt = getfirstpktid(sip, dip, sp, dp)
262
+ return '' if first_pkt == -1
263
+ return getflowdata_frompkt(first_pkt, limit)
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,107 @@
1
+ # DESCRIPTION: is part of the flowtag toolkit and provides a TK widget for listing the flows
2
+ # FLOWTAG - parses and visualizes pcap data
3
+ # Copyright (C) 2007 Christopher Lee
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'flowtag/flowdb'
19
+
20
+ module FlowTag
21
+ class FlowTable
22
+ @@column_lengths = [8,15,15,5,5,6,8,20]
23
+ @@column_names = ['Time', 'Source IP', 'Dest. IP', 'SPort', 'DPort', 'Pkts', 'Bytes', 'Tags']
24
+
25
+ def addflow(flow)
26
+ entry = Time.at(flow[FlowDB::ST]).strftime("%H:%M:%S")+" "
27
+ (1..@@column_lengths.length-2).each do |i|
28
+ entry += flow[i].to_s.rjust(@@column_lengths[i])+" "
29
+ end
30
+ entry += flow[FlowDB::TAGS].join(" ")[0,20]
31
+ @scrollbox.insert 'end', entry
32
+ end
33
+
34
+ def addflows(flows)
35
+ flows.each do |flow|
36
+ addflow(flow)
37
+ end
38
+ end
39
+
40
+ def update_flow(idx, flow)
41
+ entry = Time.at(flow[FlowDB::ST]).strftime("%H:%M:%S")+" "
42
+ (1..@@column_lengths.length-2).each do |i|
43
+ entry += flow[i].to_s.rjust(@@column_lengths[i])+" "
44
+ end
45
+ entry += flow[FlowDB::TAGS].join(" ")[0,20]
46
+ @scrollbox.delete idx
47
+ @scrollbox.insert idx, entry
48
+ end
49
+
50
+
51
+ def clear
52
+ @scrollbox.clear
53
+ end
54
+
55
+ def pack(*args)
56
+ @tableframe.pack(*args)
57
+ end
58
+
59
+ def unpack
60
+ @tableframe.unpack
61
+ end
62
+
63
+ def selected
64
+ items = []
65
+ indices = @scrollbox.curselection
66
+ indices.each do |i|
67
+ items.push(@scrollbox.get(i))
68
+ end
69
+ if @select_cb
70
+ @select_cb.call indices, items
71
+ end
72
+ @scrollbox.focus
73
+ end
74
+
75
+ def set_select_cb(callback)
76
+ @select_cb = callback
77
+ end
78
+
79
+ def initialize(parent, flows)
80
+ @rows = 0
81
+ @tableframe = TkFrame.new(parent)
82
+ header = ''
83
+ (0..@@column_names.length-1).each do |i|
84
+ header += @@column_names[i].center(@@column_lengths[i])+"|"
85
+ end
86
+ @table_header = TkLabel.new(@tableframe) {
87
+ text header
88
+ font TkFont.new('Monaco 12 bold')
89
+ anchor 'sw'
90
+ height 1
91
+ padx 0
92
+ pady 0
93
+ foreground 'lightblue'
94
+ }
95
+ @scrollbox = scrollbox = TkScrollbox.new(@tableframe) {
96
+ setgrid 'yes'
97
+ takefocus 'yes'
98
+ width 59
99
+ font TkFont.new('Monaco 12')
100
+ }
101
+ @table_header.pack(:side=>'top',:fill=>'x')
102
+ scrollbox.pack(:side=>'top',:fill=>'both',:expand=>1)
103
+ scrollbox.bind('<ListboxSelect>', proc { |x| selected() })
104
+ addflows(flows)
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,107 @@
1
+ # DESCRIPTION: is part of the flowtag toolkit and simply parses a pcap file.
2
+ # Copyright (C) 2007 Christopher Lee
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the Lesser GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # Lesser GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the Lesser GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ module FlowTag
17
+ class PcapParser
18
+ LINKTYPE_ETH = 0x0001
19
+ LINKTYPE_SLL = 0x0071
20
+
21
+ def initialize(pcapfh)
22
+ @offset = 0
23
+ @bigendian = nil
24
+ @fh = pcapfh
25
+ @fh.seek 0
26
+ magic = @fh.read(4).unpack("N")[0]
27
+ @bigendian = (magic == 0xa1b2c3d4) ? true : false
28
+ endian = (@bigendian) ? "nnNNNN" : "vvVVVV"
29
+ @version_major, @version_minor, @zone, @significant_figures, @snaplength, @linktype = @fh.read(20).unpack(endian)
30
+ @offset += 24
31
+ if @linktype != LINKTYPE_ETH
32
+ puts "Only ethernet is supported, sorry."
33
+ exit
34
+ end
35
+ end
36
+
37
+ def nextpkt
38
+ endian = (@bigendian) ? "NNNN" : "VVVV"
39
+ pkt = {}
40
+ tv_sec, tv_usec, caplen, origlen = @fh.read(16).unpack(endian)
41
+ time = tv_sec + (tv_usec / 1E6)
42
+ data = @fh.read(caplen)
43
+ @offset += 16+caplen
44
+ return Packet.new(time, data)
45
+ end
46
+
47
+ def each
48
+ while ! @fh.eof?
49
+ yield nextpkt
50
+ end
51
+ end
52
+
53
+ def close
54
+ @fh.close unless @fh.tty?
55
+ end
56
+ end
57
+
58
+ class Packet
59
+ attr_reader :time, :data, :ip_src, :ip_dst, :sport, :dport, :tcp_sport, :tcp_dport, :udp_sport, :udp_dport, :length
60
+ def initialize(time, data)
61
+ @time = time
62
+ @data = data
63
+ @length = data.length
64
+ @ip = @tcp = @udp = false
65
+ @ip_src = @ip_dst = @sport = @dport = @tcp_sport = @tcp_dport = @udp_sport = @udp_dport = nil
66
+ @ip = (data[12,2].unpack("n")[0] == 0x0800) ? true : false
67
+ offset = 14
68
+ if @ip
69
+ @ip_hlen = (data[offset].ord & 0x0f) << 2
70
+ @ip_proto = data[offset+9].ord
71
+ @ip_src, @ip_dst = data[offset+12,8].unpack("NN")
72
+ offset += @ip_hlen
73
+ @tcp = true if @ip_proto == 0x06
74
+ @udp = true if @ip_proto == 0x11
75
+ if @tcp
76
+ @sport, @dport = data[offset,4].unpack("nn")
77
+ @tcp_sport = @sport
78
+ @tcp_dport = @dport
79
+ @tcp_hlen = (data[offset+12]>>4)<<2
80
+ offset += @tcp_hlen
81
+ elsif @udp
82
+ @sport, @dport = data[offset,4].unpack("nn")
83
+ @udp_sport = @sport
84
+ @udp_dport = @dport
85
+ offset += 8
86
+ end
87
+ end
88
+ @data_offset = offset
89
+ end
90
+
91
+ def ip?
92
+ @ip
93
+ end
94
+
95
+ def udp?
96
+ @udp
97
+ end
98
+
99
+ def tcp?
100
+ @tcp
101
+ end
102
+
103
+ def payload
104
+ @data[@data_offset,10000]
105
+ end
106
+ end
107
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'flowtag'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,63 @@
1
+ require 'helper'
2
+ require 'pp'
3
+ class TestFlowtag < Test::Unit::TestCase
4
+ flow = ['192.168.44.100', '72.14.207.99', 50697, 80]
5
+
6
+ should "create a flowdb from the test.pcap and dump the flows" do
7
+ File.unlink('test/test.pcap.flows') if File.exists?('test/test.pcap.flows')
8
+ File.unlink('test/test.pcap.pkts') if File.exists?('test/test.pcap.pkts')
9
+ File.unlink('test/test.pcap.tags') if File.exists?('test/test.pcap.tags')
10
+ fdb = FlowTag::FlowDB.new('test/test.pcap')
11
+ assert(File.exists?('test/test.pcap.flows'))
12
+ assert(File.exists?('test/test.pcap.pkts'))
13
+ assert(File.exists?('test/test.pcap.tags'))
14
+ fdb.dumpflows
15
+ end
16
+
17
+ should "get the first pktid of the test.pcap" do
18
+ fdb = FlowTag::FlowDB.new('test/test.pcap')
19
+ pid = fdb.getfirstpktid(*flow)
20
+ assert_equal(0,pid)
21
+ end
22
+
23
+ should "return no tags for the first flow" do
24
+ fdb = FlowTag::FlowDB.new('test/test.pcap')
25
+ tags = fdb.getflowtags(flow)
26
+ assert_equal(0,tags.length)
27
+ end
28
+
29
+ should "get all flows tagged with test should be empty" do
30
+ fdb = FlowTag::FlowDB.new('test/test.pcap')
31
+ flows = fdb.flows_taggedwith("test")
32
+ assert_equal(0,flows.length)
33
+ end
34
+
35
+ should "tag the first flow with test and retrieve it" do
36
+ fdb = FlowTag::FlowDB.new('test/test.pcap')
37
+ fdb.tag_flow(flow,["test"])
38
+ flows = fdb.flows_taggedwith("test")
39
+ assert_equal(1,flows.length)
40
+ end
41
+
42
+ should "write the tags database and reload" do
43
+ fdb = FlowTag::FlowDB.new('test/test.pcap')
44
+ fdb.tag_flow(flow,["test"])
45
+ fdb.writetagdb
46
+ fdb = FlowTag::FlowDB.new('test/test.pcap')
47
+ flows = fdb.flows_taggedwith("test")
48
+ assert_equal(1, flows.length)
49
+ fdb.tag_flow(flow,[])
50
+ fdb.writetagdb
51
+ fdb = FlowTag::FlowDB.new('test/test.pcap')
52
+ flows = fdb.flows_taggedwith("test")
53
+ assert_equal(0, flows.length)
54
+ end
55
+
56
+ should "list all the tags and receive one, test" do
57
+ fdb = FlowTag::FlowDB.new('test/test.pcap')
58
+ fdb.tag_flow(flow,["test"])
59
+ tags = fdb.tags
60
+ assert_equal(1, tags.length)
61
+ assert_equal("test", tags[0])
62
+ end
63
+ end
metadata ADDED
@@ -0,0 +1,229 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flowtag
3
+ version: !ruby/object:Gem::Version
4
+ hash: 11
5
+ prerelease:
6
+ segments:
7
+ - 2
8
+ - 1
9
+ - 0
10
+ version: 2.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Chris Lee
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain:
17
+ - |
18
+ -----BEGIN CERTIFICATE-----
19
+ MIIDYjCCAkqgAwIBAgIBADANBgkqhkiG9w0BAQUFADBXMREwDwYDVQQDDAhydWJ5
20
+ Z2VtczEYMBYGCgmSJomT8ixkARkWCGNocmlzbGVlMRMwEQYKCZImiZPyLGQBGRYD
21
+ ZGhzMRMwEQYKCZImiZPyLGQBGRYDb3JnMB4XDTExMDIyNzE1MzAxOVoXDTEyMDIy
22
+ NzE1MzAxOVowVzERMA8GA1UEAwwIcnVieWdlbXMxGDAWBgoJkiaJk/IsZAEZFghj
23
+ aHJpc2xlZTETMBEGCgmSJomT8ixkARkWA2RoczETMBEGCgmSJomT8ixkARkWA29y
24
+ ZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALNM1Hjs6q58sf7Jp64A
25
+ vEY2cnRWDdFpD8UWpwaJK5kgSHOVgs+0mtszn+YlYjmx8kpmuYpyU4g9mNMImMQe
26
+ ow8pVsL4QBBK/1Ozgdxrsptk3IiTozMYA+g2I/+WvZSEDu9uHkKe8pvMBEMrg7RJ
27
+ IN7+jWaPnSzg3DbFwxwOdi+QRw33DjK7oFWcOaaBqWTUpI4epdi/c/FE1I6UWULJ
28
+ ZF/Uso0Sc2Pp/YuVhuMHGrUbn7zrWWo76nnK4DTLfXFDbZF5lIXT1w6BtIiN6Ho9
29
+ Rdr/W6663hYUo3WMsUSa3I5+PJXEBKmGHIZ2TNFnoFIRHha2fmm1HC9+BTaKwcO9
30
+ PLcCAwEAAaM5MDcwCQYDVR0TBAIwADAdBgNVHQ4EFgQURzsNkZo2rv86Ftc+hVww
31
+ RNICMrwwCwYDVR0PBAQDAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQBRRw/iNA/PdnvW
32
+ OBoNCSr/IiHOGZqMHgPJwyWs68FhThnLc2EyIkuLTQf98ms1/D3p0XX9JsxazvKT
33
+ W/in8Mm/R2fkVziSdzqChtw/4Z4bW3c+RF7TgX6SP5cKxNAfKmAPuItcs2Y+7bdS
34
+ hr/FktVtT2iAmISRnlEbdaTpfl6N2ZWNT83khV6iOs5xRkX/+0e+GgAv9mE6nqr1
35
+ AkuDXMhposxcnFZUrZ3UtMPEe/JnyP7Vv6pvr3qtZm8FidFZU91+rX/fwdyBU8RP
36
+ /5l8uLWXXNt1wEbtu4N1I66LwTK2iRrQZE8XtlgZGbxYDFUkiurq3OafF2YwRs6W
37
+ 6yhklP75
38
+ -----END CERTIFICATE-----
39
+
40
+ date: 2011-03-08 00:00:00 -05:00
41
+ default_executable:
42
+ dependencies:
43
+ - !ruby/object:Gem::Dependency
44
+ version_requirements: &id001 !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ hash: 27
50
+ segments:
51
+ - 0
52
+ - 1
53
+ - 0
54
+ version: 0.1.0
55
+ requirement: *id001
56
+ prerelease: false
57
+ name: tk-double-slider
58
+ type: :runtime
59
+ - !ruby/object:Gem::Dependency
60
+ version_requirements: &id002 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ hash: 27
66
+ segments:
67
+ - 0
68
+ - 1
69
+ - 0
70
+ version: 0.1.0
71
+ requirement: *id002
72
+ prerelease: false
73
+ name: tk-parallel-coordinates
74
+ type: :runtime
75
+ - !ruby/object:Gem::Dependency
76
+ version_requirements: &id003 !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ requirement: *id003
86
+ prerelease: false
87
+ name: shoulda
88
+ type: :development
89
+ - !ruby/object:Gem::Dependency
90
+ version_requirements: &id004 !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ~>
94
+ - !ruby/object:Gem::Version
95
+ hash: 23
96
+ segments:
97
+ - 1
98
+ - 0
99
+ - 0
100
+ version: 1.0.0
101
+ requirement: *id004
102
+ prerelease: false
103
+ name: bundler
104
+ type: :development
105
+ - !ruby/object:Gem::Dependency
106
+ version_requirements: &id005 !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ~>
110
+ - !ruby/object:Gem::Version
111
+ hash: 7
112
+ segments:
113
+ - 1
114
+ - 5
115
+ - 2
116
+ version: 1.5.2
117
+ requirement: *id005
118
+ prerelease: false
119
+ name: jeweler
120
+ type: :development
121
+ - !ruby/object:Gem::Dependency
122
+ version_requirements: &id006 !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ hash: 3
128
+ segments:
129
+ - 0
130
+ version: "0"
131
+ requirement: *id006
132
+ prerelease: false
133
+ name: rcov
134
+ type: :development
135
+ - !ruby/object:Gem::Dependency
136
+ version_requirements: &id007 !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ hash: 27
142
+ segments:
143
+ - 0
144
+ - 1
145
+ - 0
146
+ version: 0.1.0
147
+ requirement: *id007
148
+ prerelease: false
149
+ name: tk-double-slider
150
+ type: :runtime
151
+ - !ruby/object:Gem::Dependency
152
+ version_requirements: &id008 !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ hash: 27
158
+ segments:
159
+ - 0
160
+ - 1
161
+ - 0
162
+ version: 0.1.0
163
+ requirement: *id008
164
+ prerelease: false
165
+ name: tk-parallel-coordinates
166
+ type: :runtime
167
+ description: presents the user with a GUI interface to visualize and explore flows found from a given pcap file
168
+ email: rubygems@chrislee.dhs.org
169
+ executables:
170
+ - flowtag
171
+ - ftlistflows
172
+ - ftpcap2flowdb
173
+ - ftprintflow
174
+ extensions: []
175
+
176
+ extra_rdoc_files:
177
+ - LICENSE.txt
178
+ - README.rdoc
179
+ files:
180
+ - bin/flowtag
181
+ - bin/ftlistflows
182
+ - bin/ftpcap2flowdb
183
+ - bin/ftprintflow
184
+ - lib/flowtag.rb
185
+ - lib/flowtag/flowcanvas.rb
186
+ - lib/flowtag/flowdb.rb
187
+ - lib/flowtag/flowtable.rb
188
+ - lib/flowtag/pcapparser.rb
189
+ - LICENSE.txt
190
+ - README.rdoc
191
+ - test/helper.rb
192
+ - test/test_flowtag.rb
193
+ has_rdoc: true
194
+ homepage: https://rubygems.org/gems/flowtag
195
+ licenses:
196
+ - MIT
197
+ post_install_message:
198
+ rdoc_options: []
199
+
200
+ require_paths:
201
+ - lib
202
+ required_ruby_version: !ruby/object:Gem::Requirement
203
+ none: false
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ hash: 3
208
+ segments:
209
+ - 0
210
+ version: "0"
211
+ required_rubygems_version: !ruby/object:Gem::Requirement
212
+ none: false
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ hash: 3
217
+ segments:
218
+ - 0
219
+ version: "0"
220
+ requirements: []
221
+
222
+ rubyforge_project:
223
+ rubygems_version: 1.6.1
224
+ signing_key:
225
+ specification_version: 3
226
+ summary: FlowTag visualizes pcap files for forensic analysis
227
+ test_files:
228
+ - test/helper.rb
229
+ - test/test_flowtag.rb
metadata.gz.sig ADDED
@@ -0,0 +1 @@
1
+ 'mnN�V���&���KA� �6#ָ�(�g�6��Zj"�����1�Y��zq1� "�X,k����QK�nY��u�2����Ɏ6�o��fO%Z���ȭC��"lX�_�I���Wbn=ϤO>K�t�o�"�B�n4?��B���g��`aD�Ͽ�P#��픳�2�<䷛��L�}��͟��������O-�/]���$���74a�eCXEq8V�-�`^