dorothy2 0.0.3 → 1.0.0

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.
@@ -0,0 +1,178 @@
1
+ # "THE BEER-WARE LICENSE" (Revision 42):
2
+ # Mu[http://www.mudynamics.com] wrote this file. As long as you retain this
3
+ # notice you can do whatever you want with this stuff. If we meet some day,
4
+ # and you think this stuff is worth it, you can buy us a beer in return.
5
+ #
6
+ # All about pcapr
7
+ # * http://www.pcapr.net
8
+ # * http://groups.google.com/group/pcapr-forum
9
+ # * http://twitter.com/pcapr
10
+ #
11
+ # Mu Dynamics
12
+ # * http://www.mudynamics.com
13
+ # * http://labs.mudynamics.com
14
+
15
+ module Mu
16
+ class Xtractr
17
+ # = Field
18
+ # Field represents a named packet field in the <b>xtractr</b> index. Each field
19
+ # contains tokenized terms along with the frequency at which they occur. A
20
+ # field is a queryable object and can be used to iterate over flows or packets
21
+ # that match the Field::Value. See Flow or Packet for more information on the
22
+ # fields that are stored in the index.
23
+ #
24
+ # xtractr.field('http.request.uri').each_packet { |packet ... }
25
+ #
26
+ # The fields are useful for quick analysis on top trending terms across the
27
+ # entire index. Here are the top HTTP fields that have NOP slides in them:
28
+ #
29
+ # xtractr.fields(/^http/).select do |field|
30
+ # not field.terms(/AAAAAA/i).empty?
31
+ # end
32
+ class Field
33
+ include Enumerable
34
+
35
+ attr_reader :xtractr # :nodoc:
36
+
37
+ # The name of the field.
38
+ attr_reader :name
39
+
40
+ # = Field::Value
41
+ # Field::Value represents an instance of a field with a concrete value that
42
+ # can further used for fine grained searches.
43
+ class Value
44
+ attr_reader :xtractr # :nodoc:
45
+
46
+ # Return the field object.
47
+ attr_reader :field
48
+
49
+ # Return the value of the field object.
50
+ attr_reader :value
51
+
52
+ def initialize xtractr, json # :nodoc:
53
+ @xtractr = xtractr
54
+ @field = Field.new(xtractr, json['key'])
55
+ @value = json['value']
56
+ end
57
+
58
+ def q # :nodoc:
59
+ "#{field.name}:\"#{value}\""
60
+ end
61
+
62
+ # Fetch the list of packets that contain this Field::Value. If the
63
+ # optional query is given, it's AND'd to the query that matches this
64
+ # Field::Value.
65
+ # value.packets.each { |pkt| ... }
66
+ # value.packets('dns.qry.name:apple').each { |pkt| ... }
67
+ def packets _q=nil
68
+ q2 = q
69
+ q2 << " #{_q}" if _q
70
+ Packets.new xtractr, :q => q2
71
+ end
72
+
73
+ # Iterate over each packet that contains this field value. This is
74
+ # a convenience function used primiarily in method chaining.
75
+ def each_packet(_q=nil, &blk) # :yields: packet
76
+ packets(_q).each(&blk)
77
+ return self
78
+ end
79
+
80
+ # Count the unique values of the specified field amongst all the packets
81
+ # that matched the query.
82
+ # value.count('http.request.method')
83
+ def count _field
84
+ which = field.name =~ /^flow\./ ? 'flows' : 'packets'
85
+ Views.count xtractr, _field, "/api/#{which}/report", :q => q
86
+ end
87
+
88
+ # Sum the unique numeric values of vfield, keyed by the unique values of
89
+ # kfield.
90
+ # value.sum('flow.src', 'flow.bytes')
91
+ def sum kfield, vfield
92
+ which = field.name =~ /^flow\./ ? 'flows' : 'packets'
93
+ Views.sum xtractr, kfield, vfield, "/api/#{which}/report", :q => q
94
+ end
95
+
96
+ def inspect # :nodoc:
97
+ "#<value:#{field.name} #{value}>"
98
+ end
99
+ end
100
+
101
+ def initialize xtractr, name # :nodoc:
102
+ @xtractr = xtractr
103
+ @name = name
104
+ end
105
+
106
+ # Fetch the terms and their packet frequencies (in packets) for this field.
107
+ # If the optional start term is given, then the term enumeration starts
108
+ # from the specified term.
109
+ # field.each { |term| ... }
110
+ # field.each('mozilla') { |term| ... }
111
+ def each_term(start='') # :yields: term
112
+ opts = {}
113
+ opts[:start] = start
114
+ opts[:limit] = 101
115
+
116
+ while true
117
+ result = xtractr.json "api/field/#{name}/terms", opts
118
+ rows = result['rows']
119
+ break if rows.empty?
120
+
121
+ rows[0, 100].each do |row|
122
+ term = Term.new self, row
123
+ yield term
124
+ end
125
+
126
+ break if rows.size < 101
127
+ opts[:start] = rows[100]['key']
128
+ end
129
+
130
+ return self
131
+ end
132
+
133
+ # Fetch the list of <em>all</em> the unique terms for this field, sorted by the
134
+ # frequency of occurence in the packets. This can be used for some quick
135
+ # trend analysis to see which term of a given field appears most amongst
136
+ # all packets in the index. Here's an example to print out the top 10 terms
137
+ # of <em>http.request.uri</em>.
138
+ # p xtractr.field('http.request.uri').terms[0..10]
139
+ def terms regex=nil
140
+ regex = Regexp.new(regex, Regexp::IGNORECASE) if regex.is_a? String
141
+ t = regex ? entries.select { |name| name =~ regex } : entries
142
+ t.sort { |a, b| b.frequency <=> a.frequency }
143
+ end
144
+
145
+ # Find the term for this field which has the name and the packet frequency.
146
+ # field.term 'mozilla'
147
+ def [] which
148
+ result = xtractr.json "api/field/#{name}/terms", :start => which, :limit => 1
149
+ rows = result['rows']
150
+ if rows.empty? || rows[0]['key'] != which
151
+ raise ArgumentError, "Unknown term #{which} for field #{name}"
152
+ end
153
+ return Term.new(self, rows[0])
154
+ end
155
+
156
+ # Find out all the unique values of this field with an optional query.
157
+ # xtractr.field('http.user.agent').count('flow.src:192.168.1.1')
158
+ def count q='*'
159
+ Views.count xtractr, self, "api/flows/report", :q => q
160
+ end
161
+
162
+ # Return a list of Field::Value objects for this field, sorted by their
163
+ # frequency. This is a convenience method to use the resulting Field::Value
164
+ # objects in method chaining.
165
+ # xtractr.field('http.user.agent').values.first.packets.slice('foo.pcap')
166
+ def values q='*'
167
+ count(q).map { |c| c.object }
168
+ end
169
+
170
+ def inspect # :nodoc:
171
+ "#<field:#{name}>"
172
+ end
173
+
174
+ alias_method :each, :each_term
175
+ alias_method :term, :[]
176
+ end
177
+ end # Xtractr
178
+ end # Mu
@@ -0,0 +1,162 @@
1
+ # "THE BEER-WARE LICENSE" (Revision 42):
2
+ # Mu[http://www.mudynamics.com] wrote this file. As long as you retain this
3
+ # notice you can do whatever you want with this stuff. If we meet some day,
4
+ # and you think this stuff is worth it, you can buy us a beer in return.
5
+ #
6
+ # All about pcapr
7
+ # * http://www.pcapr.net
8
+ # * http://groups.google.com/group/pcapr-forum
9
+ # * http://twitter.com/pcapr
10
+ #
11
+ # Mu Dynamics
12
+ # * http://www.mudynamics.com
13
+ # * http://labs.mudynamics.com
14
+
15
+ module Mu
16
+ class Xtractr
17
+ # = Flow
18
+ # Flow represents a single conversation between two hosts. Depending on whether
19
+ # it's IP, UDP or TCP, xtractr uses different information from the IP header
20
+ # of the packets (src/dst addresses and ports) to logically group them
21
+ # together. Each flow also has the duration (difference in the timestamp between
22
+ # the first and last packet in the conversation), the total bytes that were
23
+ # exchanged as well as the logical messages that were exchanged. For example:
24
+ #
25
+ # <b>Identify hosts that performed TCP port scans</b>
26
+ #
27
+ # xtractr.flows('flow.proto:6 flow.cmsgs:0 flow.smsgs:0').count('flow.src')
28
+ #
29
+ # <b>Identify DNS queries with no response (possibly timed out)</b>
30
+ #
31
+ # xtractr.flows('flow.service:DNS flow.smsgs:0').count('dns.qry.name')
32
+ class Flow
33
+ include Enumerable
34
+
35
+ attr_reader :xtractr # :nodoc:
36
+
37
+ # The unique ID of the flow.
38
+ attr_reader :id
39
+
40
+ # The timestamp of the flow, determined by the first packet in the flow.
41
+ attr_reader :time
42
+
43
+ # The duration of the flow, determined by the first and last packet in the flow.
44
+ attr_reader :duration
45
+
46
+ # The source host of the flow.
47
+ attr_reader :src
48
+
49
+ # The destination host of the flow.
50
+ attr_reader :dst
51
+
52
+ # The IP protocol of the flow.
53
+ attr_reader :proto
54
+
55
+ # The source port of the flow (if applicable).
56
+ attr_reader :sport
57
+
58
+ # The destination port of the flow (if applicable).
59
+ attr_reader :dport
60
+
61
+ # The service of the flow (like DNS or HTTP).
62
+ attr_reader :service
63
+
64
+ # The title of the flow.
65
+ attr_reader :title
66
+
67
+ # The total ##packets in the flow.
68
+ attr_reader :packets
69
+
70
+ # The total ##bytes (request and response) in the flow.
71
+ attr_reader :bytes
72
+
73
+ # The logical client messages (payloads) in the flow.
74
+ attr_reader :cmsgs
75
+
76
+ # The logical server messages (payloads) in the flow.
77
+ attr_reader :smsgs
78
+
79
+ def initialize xtractr, json # :nodoc:
80
+ @xtractr = xtractr
81
+ @id = json['id']
82
+ @time = json['time']
83
+ @duration = json['duration']
84
+ @src = Host.new xtractr, json['src']
85
+ @dst = Host.new xtractr, json['dst']
86
+ @proto = json['proto']
87
+ @sport = json['sport']
88
+ @dport = json['dport']
89
+ @service = Service.new xtractr, json['service']
90
+ @title = json['title']
91
+ @packets = json['packets']
92
+ @bytes = json['bytes']
93
+ @cmsgs = json['cmsgs']
94
+ @smsgs = json['smsgs']
95
+ @first_id = json['first']
96
+ @last_id = json['last']
97
+ @iterator = Packets.new(xtractr, :q => "pkt.flow:#{id}")
98
+ end
99
+
100
+ # Return the first packet for this flow. Together the first and last
101
+ # packets make up the span of the flow. Read this
102
+ # blog[http://labs.mudynamics.com/2010/09/30/visualizing-application-flows-with-xtractr/]
103
+ # to see how these spans enable flow visualization.
104
+ # xtractr.flow(1).first.bytes
105
+ def first
106
+ @first ||= xtractr.packet @first_id
107
+ end
108
+
109
+ # Return the last packet for this flow. Together the first and last
110
+ # packets make up the span of the flow. Read this
111
+ # blog[http://labs.mudynamics.com/2010/09/30/visualizing-application-flows-with-xtractr/]
112
+ # to see how these spans enable flow visualization.
113
+ # xtractr.flow(2).last.bytes
114
+ def last
115
+ @last ||= xtractr.packet @last_id
116
+ end
117
+
118
+ # Iterate over each packet in this flow.
119
+ # flow.each { |pkt| ... }
120
+ def each_packet(&blk) # :yields: packet
121
+ @iterator.each(&blk)
122
+ return self
123
+ end
124
+
125
+ # Reassemble the TCP stream for this flow (assuming it's a TCP flow) and
126
+ # return the stream. This is the basis for doing content extraction from
127
+ # packets even if the packets span multiple pcaps.
128
+ # xtractr.service('HTTP').flows.first.stream
129
+ def stream
130
+ result = xtractr.json "api/flow/#{id}/stream"
131
+ return Stream.new(xtractr, self, result)
132
+ end
133
+
134
+ # A convenience method to fetch the stream for this flow, extract the
135
+ # content and then return an array of contents.
136
+ # xtractr.flows('flow.service:HTTP favicon.ico').each do |flow|
137
+ # flow.contents.each { |c| c.save }
138
+ # end
139
+ def contents
140
+ stream.contents
141
+ end
142
+
143
+ # Stich together a pcap made up of all packets containing this flow and
144
+ # save it to the filename. It's possible for the packets to span multiple
145
+ # pcaps, but xtractr makes it seamless.
146
+ # flow.save("foo.pcap")
147
+ def save filename
148
+ open(filename, "w") do |ios|
149
+ pcap = xtractr.get "api/packets/slice", :q => "pkt.flow:#{id}"
150
+ ios.write pcap
151
+ end
152
+ return self
153
+ end
154
+
155
+ def inspect # :nodoc:
156
+ "#<flow:#{id} #{service.name} #{src.address}:#{sport} > #{dst.address}:#{dport} #{title}"
157
+ end
158
+
159
+ alias_method :each, :each_packet
160
+ end
161
+ end # Xtractr
162
+ end # Mu
@@ -0,0 +1,118 @@
1
+ # "THE BEER-WARE LICENSE" (Revision 42):
2
+ # Mu[http://www.mudynamics.com] wrote this file. As long as you retain this
3
+ # notice you can do whatever you want with this stuff. If we meet some day,
4
+ # and you think this stuff is worth it, you can buy us a beer in return.
5
+ #
6
+ # All about pcapr
7
+ # * http://www.pcapr.net
8
+ # * http://groups.google.com/group/pcapr-forum
9
+ # * http://twitter.com/pcapr
10
+ #
11
+ # Mu Dynamics
12
+ # * http://www.mudynamics.com
13
+ # * http://labs.mudynamics.com
14
+
15
+ module Mu
16
+ class Xtractr
17
+ # Flows is an iterator for the flows in the index, based on a given query.
18
+ # The default query for this iterator is '*', implying that it will iterate
19
+ # over <em>all</em> the flows in the index.
20
+ class Flows
21
+ include Enumerable
22
+
23
+ attr_reader :xtractr # :nodoc:
24
+
25
+ MAX_PAGE_SIZE = 100 # :nodoc:
26
+
27
+ def initialize xtractr, opts # :nodoc:
28
+ @xtractr = xtractr
29
+ @opts = opts.dup
30
+ @opts[:q] ||= '*'
31
+ end
32
+
33
+ def q # :nodoc:
34
+ @opts[:q]
35
+ end
36
+
37
+ # Iterate over each flow that matches the search criteria. It's always
38
+ # better to use this with a fine-grained query instead of flows.to_a
39
+ # because it's going to try and load *all* flows from the index.
40
+ # xtractr.flows("flow.src:192.168.1.1").each { |flow| ... }
41
+ def each_flow() # :yields: flow
42
+ _opts = @opts.dup
43
+ _opts[:start] ||= 1
44
+ _opts[:limit] ||= MAX_PAGE_SIZE
45
+
46
+ while true
47
+ result = xtractr.json "api/flows", _opts
48
+ rows = result['rows']
49
+ break if rows.empty?
50
+
51
+ rows[0, MAX_PAGE_SIZE-1].each do |row|
52
+ flow = Flow.new xtractr, row
53
+ yield flow
54
+ end
55
+
56
+ break if rows.size < MAX_PAGE_SIZE
57
+ _opts[:start] = rows[MAX_PAGE_SIZE-1]['id']
58
+ end
59
+ return self
60
+ end
61
+
62
+ # Fetch the first flow that matched the query. This is mainly used for
63
+ # unit testing, but useful within IRB to experiment with method chaining.
64
+ # flows.first.save("1.pcap")
65
+ def first
66
+ result = xtractr.json "api/flows", :start => 1, :limit => 1, :q => q
67
+ rows = result['rows']
68
+ rows.empty? ? nil : Flow.new(xtractr, rows[0])
69
+ end
70
+
71
+ # Count the unique values of the specified field amongst all the flows
72
+ # that matched the query.
73
+ # xtractr.flows('index.html').count('http.request.uri')
74
+ def count field
75
+ Views.count xtractr, field, '/api/flows/report', @opts
76
+ end
77
+
78
+ # Return a list of Field::Value objects for the specified field, sorted
79
+ # by their frequency. This is a convenience method used in method chaining.
80
+ # xtractr.flows('index.html').values('http.request.uri')
81
+ def values field
82
+ count(field).map { |c| c.object }
83
+ end
84
+
85
+ # Sum the numeric values of vfield, keyed by the unique values of
86
+ # kfield.
87
+ # xtractr.flows('index.html').sum('http.request.uri', 'flow.bytes')
88
+ def sum kfield, vfield
89
+ Views.sum xtractr, kfield, vfield, '/api/flows/report', @opts
90
+ end
91
+
92
+ # Save all the packets for this collection of flows into a pcap. It's
93
+ # possible that the packets for the flows might span multiple indexed
94
+ # pcaps.
95
+ # xtractr.flows('flow.service:DNS AAAA').save('dns.pcap')
96
+ def save filename
97
+ flow_ids = []
98
+ each_flow do |flow|
99
+ flow_ids << flow.id.to_s
100
+ break if flow_ids.size >= 1024
101
+ end
102
+
103
+ _q = "pkt.flow:(" << flow_ids.join('||') << ')'
104
+ open(filename, "w") do |ios|
105
+ pcap = xtractr.get "api/packets/slice", :q => _q
106
+ ios.write pcap
107
+ end
108
+ return self
109
+ end
110
+
111
+ def inspect # :nodoc:
112
+ "#<flows:#{@opts[:q]}>"
113
+ end
114
+
115
+ alias_method :each, :each_flow
116
+ end
117
+ end # Xtractr
118
+ end # Mu
@@ -0,0 +1,87 @@
1
+ # "THE BEER-WARE LICENSE" (Revision 42):
2
+ # Mu[http://www.mudynamics.com] wrote this file. As long as you retain this
3
+ # notice you can do whatever you want with this stuff. If we meet some day,
4
+ # and you think this stuff is worth it, you can buy us a beer in return.
5
+ #
6
+ # All about pcapr
7
+ # * http://www.pcapr.net
8
+ # * http://groups.google.com/group/pcapr-forum
9
+ # * http://twitter.com/pcapr
10
+ #
11
+ # Mu Dynamics
12
+ # * http://www.mudynamics.com
13
+ # * http://labs.mudynamics.com
14
+
15
+ module Mu
16
+ class Xtractr
17
+ # = Host
18
+ # Host is a generic entity representing MAC, IPv4 and IPv6 addresses. You can
19
+ # get a list of all the unique hosts in the index using the root xtractr instance.
20
+ # xtractr.hosts
21
+ class Host
22
+ attr_reader :xtractr # :nodoc:
23
+
24
+ # Returns the address of this host.
25
+ attr_reader :address
26
+
27
+ def initialize xtractr, address # :nodoc:
28
+ @xtractr = xtractr
29
+ @address = address
30
+ end
31
+
32
+ # Use the host as a server and get a unique list of its clients.
33
+ # xtractr.hosts(/192.168/).first.clients
34
+ def clients q=nil
35
+ _q = role2q :server, 'flow', q
36
+ Flows.new(xtractr, :q => _q).count('flow.src')
37
+ end
38
+
39
+ # Use the host as a client and get a unique list its servers.
40
+ # xtractr.hosts(/192.168/).first.servers
41
+ def servers q=nil
42
+ _q = role2q :client, 'flow', q
43
+ Flows.new(xtractr, :q => _q).count('flow.dst')
44
+ end
45
+
46
+ # Get a unique list of the host's services. <em>role</em> can be one of
47
+ # :any, :client or :server to specify the role.
48
+ # host.services :server
49
+ def services role =:any, q=nil
50
+ _q = role2q role, 'flow', q
51
+ Flows.new(xtractr, :q => _q).count('flow.service')
52
+ end
53
+
54
+ # Return a flow iterator to iterate over the various flows that contain
55
+ # this host in the specified role.
56
+ # host.flows :client
57
+ def flows role =:any, q=nil
58
+ _q = role2q role, 'flow', q
59
+ Flows.new(xtractr, :q => _q)
60
+ end
61
+
62
+ # Return a packet iterator to iterate over the various packets that contain
63
+ # this host in the specified role.
64
+ # host.packets :server
65
+ def packets role =:any, q=nil
66
+ _q = role2q role, 'pkt', q
67
+ Packets.new(xtractr, :q => _q)
68
+ end
69
+
70
+ def inspect # :nodoc:
71
+ "#<host:#{address}>"
72
+ end
73
+
74
+ private
75
+ def role2q role, forp, q=nil # :nodoc:
76
+ _q = case role
77
+ when :any: "#{forp}.src|#{forp}.dst:\"#{address}\""
78
+ when :client: "#{forp}.src:\"#{address}\""
79
+ when :server: "#{forp}.dst:\"#{address}\""
80
+ else raise ArgumentError, "Unknown role #{role}"
81
+ end
82
+ _q << " #{q}" if q
83
+ return _q
84
+ end
85
+ end
86
+ end # Xtractr
87
+ end # Mu
@@ -0,0 +1,138 @@
1
+ # "THE BEER-WARE LICENSE" (Revision 42):
2
+ # Mu[http://www.mudynamics.com] wrote this file. As long as you retain this
3
+ # notice you can do whatever you want with this stuff. If we meet some day,
4
+ # and you think this stuff is worth it, you can buy us a beer in return.
5
+ #
6
+ # All about pcapr
7
+ # * http://www.pcapr.net
8
+ # * http://groups.google.com/group/pcapr-forum
9
+ # * http://twitter.com/pcapr
10
+ #
11
+ # Mu Dynamics
12
+ # * http://www.mudynamics.com
13
+ # * http://labs.mudynamics.com
14
+
15
+ module Mu
16
+ class Xtractr
17
+ # = Packet
18
+ # A class that represents a single packet in the xtractr index. You can
19
+ # iterate over all the packets in the index like this:
20
+ # xtractr.packets('blah').each { |pkt| ... }
21
+ class Packet
22
+ include Enumerable
23
+
24
+ attr_reader :xtractr # :nodoc:
25
+
26
+ # The unique ID of the packet.
27
+ attr_reader :id
28
+
29
+ # The file offset of the packet within the pcap.
30
+ attr_reader :offset
31
+
32
+ # The length of the packet (the entire frame).
33
+ attr_reader :length
34
+
35
+ # The relative timestamp of the packet.
36
+ attr_reader :time
37
+
38
+ # The direction of the packet (if it belongs to a flow).
39
+ attr_reader :dir
40
+
41
+ # The source host of the packet.
42
+ attr_reader :src
43
+
44
+ # The destination host of the packet.
45
+ attr_reader :dst
46
+
47
+ # The service of the packet.
48
+ attr_reader :service
49
+
50
+ # The title of the packet.
51
+ attr_reader :title
52
+
53
+ def initialize xtractr, json # :nodoc:
54
+ @xtractr = xtractr
55
+ @id = json['id']
56
+ @offset = json['offset']
57
+ @length = json['length']
58
+ @pcap_id = json['pcap']
59
+ @flow_id = json['flow']
60
+ @time = json['time']
61
+ @dir = json['dir']
62
+ @src = Host.new xtractr, json['src']
63
+ @dst = Host.new xtractr, json['dst']
64
+ @service = Service.new xtractr, json['service']
65
+ @title = json['title']
66
+ end
67
+
68
+ # Returns the flow (if any) that this packet belongs to.
69
+ # xtractr.packets('index.html').first.flow
70
+ def flow
71
+ return nil if @flow_id.zero?
72
+ @flow ||= xtractr.flow @flow_id
73
+ end
74
+
75
+ # Fetch the actual packet data from the index. The return value is a
76
+ # String (that might contain null characters).
77
+ # xtractr.packets('index.html').first.bytes
78
+ def bytes
79
+ result = xtractr.json "/api/packet/#{id}/bytes"
80
+ result['bytes'].map { |b| b.chr }.join('')
81
+ end
82
+
83
+ # For UDP/TCP (both IPv4 and IPv6) packets, fetch just the layer4 payload.
84
+ # Returns an empty string for all other types of packet.
85
+ # xtractr.packets('http.request.method:GET').each do |pkt|
86
+ # puts pkt.payload
87
+ # end
88
+ def payload
89
+ result = xtractr.json "/api/packet/#{id}/bytes"
90
+ bytes = result['bytes']
91
+ l4size = result['l4size'] || 0
92
+ bytes[-l4size, l4size].map { |b| b.chr }.join('')
93
+ end
94
+
95
+ # Iterate over each Field::Value in the packet. The various packet fields
96
+ # are only available if the indexing was done with <em>--mode forensics</em>.
97
+ # packet.each('ip.ttl') { |fv| ... }
98
+ def each_field(regex=nil) # :yields: value
99
+ regex = Regexp.new(regex) if regex.is_a? String
100
+ result = xtractr.json "/api/packet/#{id}/fields"
101
+ rows = result['rows']
102
+ rows = rows.select { |row| row['key'] =~ regex } if regex
103
+ rows.each do |row|
104
+ value = Field::Value.new(xtractr, row)
105
+ yield value
106
+ end
107
+ end
108
+
109
+ # Fetch the values of the specified field for this packet. Even if there's
110
+ # only a single value for the field, it's returned as an array of 1 element
111
+ # packet.field('ip.ttl').each { |ttl| ... }
112
+ def [] name
113
+ result = xtractr.json "/api/packet/#{id}/field/#{name}"
114
+ return result['rows']
115
+ end
116
+
117
+ # Extract just this packet and save it to the specified file as a pcap.
118
+ # You can also save a collection of packets using Packets#save or a
119
+ # collection of flows using Flows#save.
120
+ # packet.save("foo.pcap")
121
+ def save filename
122
+ open(filename, "w") do |ios|
123
+ pcap = xtractr.get "api/packet/#{id}/pcap"
124
+ ios.write pcap
125
+ end
126
+ return self
127
+ end
128
+
129
+ def inspect # :nodoc:
130
+ "#<pkt:#{id} #{src.address} > #{dst.address} #{service.name} #{title}"
131
+ end
132
+
133
+ alias_method :each, :each_field
134
+ alias_method :fields, :entries
135
+ alias_method :field, :[]
136
+ end
137
+ end # Xtractr
138
+ end # Mu