dorothy2 0.0.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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