metasploit_data_models 0.18.1 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/app/models/mdm/host.rb +7 -0
- data/app/models/mdm/service.rb +30 -1
- data/app/models/mdm/tag.rb +10 -0
- data/app/models/metasploit_data_models/ip_address/v4/cidr.rb +14 -0
- data/app/models/metasploit_data_models/ip_address/v4/nmap.rb +14 -0
- data/app/models/metasploit_data_models/ip_address/v4/range.rb +12 -0
- data/app/models/metasploit_data_models/ip_address/v4/segment/nmap/list.rb +126 -0
- data/app/models/metasploit_data_models/ip_address/v4/segment/nmap/range.rb +12 -0
- data/app/models/metasploit_data_models/ip_address/v4/segment/single.rb +123 -0
- data/app/models/metasploit_data_models/ip_address/v4/segmented.rb +200 -0
- data/app/models/metasploit_data_models/ip_address/v4/single.rb +53 -0
- data/app/models/metasploit_data_models/search/operation/ip_address.rb +60 -0
- data/app/models/metasploit_data_models/search/operator/ip_address.rb +33 -0
- data/app/models/metasploit_data_models/search/visitor/attribute.rb +1 -0
- data/app/models/metasploit_data_models/search/visitor/includes.rb +1 -0
- data/app/models/metasploit_data_models/search/visitor/joins.rb +1 -0
- data/app/models/metasploit_data_models/search/visitor/where.rb +51 -0
- data/config/locales/en.yml +35 -4
- data/lib/metasploit_data_models/ip_address.rb +5 -0
- data/lib/metasploit_data_models/ip_address/cidr.rb +174 -0
- data/lib/metasploit_data_models/ip_address/range.rb +181 -0
- data/lib/metasploit_data_models/match/child.rb +48 -0
- data/lib/metasploit_data_models/match/parent.rb +103 -0
- data/lib/metasploit_data_models/version.rb +4 -4
- data/metasploit_data_models.gemspec +2 -1
- data/spec/app/models/mdm/cred_spec.rb +164 -31
- data/spec/app/models/mdm/service_spec.rb +33 -44
- data/spec/app/models/metasploit_data_models/ip_address/v4/cidr_spec.rb +121 -0
- data/spec/app/models/metasploit_data_models/ip_address/v4/nmap_spec.rb +151 -0
- data/spec/app/models/metasploit_data_models/ip_address/v4/range_spec.rb +300 -0
- data/spec/app/models/metasploit_data_models/ip_address/v4/segment/nmap/list_spec.rb +278 -0
- data/spec/app/models/metasploit_data_models/ip_address/v4/segment/nmap/range_spec.rb +304 -0
- data/spec/app/models/metasploit_data_models/ip_address/v4/segment/segmented_spec.rb +29 -0
- data/spec/app/models/metasploit_data_models/ip_address/v4/segment/single_spec.rb +315 -0
- data/spec/app/models/metasploit_data_models/ip_address/v4/single_spec.rb +183 -0
- data/spec/app/models/metasploit_data_models/search/operation/ip_address_spec.rb +182 -0
- data/spec/app/models/metasploit_data_models/search/operator/ip_address_spec.rb +19 -0
- data/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb +229 -36
- data/spec/dummy/config/application.rb +1 -1
- data/spec/dummy/db/structure.sql +3011 -0
- data/spec/factories/mdm/services.rb +3 -1
- data/spec/lib/metasploit_data_models/ip_address/cidr_spec.rb +350 -0
- data/spec/lib/metasploit_data_models/ip_address/range_spec.rb +77 -0
- data/spec/lib/metasploit_data_models/match/child_spec.rb +61 -0
- data/spec/lib/metasploit_data_models/match/parent_spec.rb +155 -0
- data/spec/support/matchers/match_regex_exactly.rb +28 -0
- data/spec/support/shared/contexts/rex/text.rb +15 -0
- data/spec/support/shared/examples/metasploit_data_models/search/operation/ipaddress/match.rb +109 -0
- metadata +58 -9
- data/spec/dummy/db/schema.rb +0 -609
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MTQ0Y2Q5YzA1YWYxNzViOTg0NGVjYTM1M2E5ZTE1NjJkNTJhZTc2Ng==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YTY1NTEwYWNlZTYzOGEyY2Y5NWU3MmMyOTIzMzBlZDQxNjE4NTNiZA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MDhiZjg1M2FhNzg2ZjdiYmUyNzFlOWNhYWQ1YzYzOGM4ZmJiMjY3MzdlMjdk
|
10
|
+
NzE2ODcwM2JjNWM3MzllZTE0MTNiYzc5Y2VhMjE5OWVkMTQ2MGYwZDdkMmIy
|
11
|
+
OGFmZTI4YmY0YjExMzQzMTQxYWU1YzVmZDg0MmU3MTI2ZTBmOWY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
M2ZlZmRmY2E4Y2E4NmQzZjc3MmFmOTZjYzJhMWNjYmM4YWU3ODNhMTNkODI2
|
14
|
+
ZTNhMTdjMGUzYmI1N2EzMDRmYWMzMzg5MjA1MWI1OTZiNjdkZjBjNzdlNmVm
|
15
|
+
YmM1NmYyMzU0YjQwNWQ1ZGRjZjE5OGQ4OGYzZTk3MDA5ZTZjYmI=
|
data/app/models/mdm/host.rb
CHANGED
data/app/models/mdm/service.rb
CHANGED
@@ -6,6 +6,9 @@ class Mdm::Service < ActiveRecord::Base
|
|
6
6
|
# CONSTANTS
|
7
7
|
#
|
8
8
|
|
9
|
+
# Valid values for {#proto}.
|
10
|
+
PROTOS = %w{tcp udp}
|
11
|
+
|
9
12
|
# Valid values for {#state}.
|
10
13
|
STATES = ['open', 'closed', 'filtered', 'unknown']
|
11
14
|
|
@@ -186,8 +189,14 @@ class Mdm::Service < ActiveRecord::Base
|
|
186
189
|
# Search Attributes
|
187
190
|
#
|
188
191
|
|
192
|
+
search_attribute :info,
|
193
|
+
type: :string
|
189
194
|
search_attribute :name,
|
190
195
|
type: :string
|
196
|
+
search_attribute :proto,
|
197
|
+
type: {
|
198
|
+
set: :string
|
199
|
+
}
|
191
200
|
|
192
201
|
#
|
193
202
|
# Search Withs
|
@@ -202,7 +211,27 @@ class Mdm::Service < ActiveRecord::Base
|
|
202
211
|
numericality: {
|
203
212
|
only_integer: true
|
204
213
|
}
|
205
|
-
validates :proto,
|
214
|
+
validates :proto,
|
215
|
+
inclusion: {
|
216
|
+
in: PROTOS
|
217
|
+
}
|
218
|
+
|
219
|
+
#
|
220
|
+
# Class Methods
|
221
|
+
#
|
222
|
+
|
223
|
+
# Set of searchable values for {#proto}.
|
224
|
+
#
|
225
|
+
# @return [Set<String>] {PROTOS} as a `Set`.
|
226
|
+
# @see Metasploit::Model::Search::Operation::Set#membership
|
227
|
+
# @see Metasploit::Model::Search::Operator::Attribute#attribute_set
|
228
|
+
def self.proto_set
|
229
|
+
@proto_set ||= Set.new(PROTOS)
|
230
|
+
end
|
231
|
+
|
232
|
+
#
|
233
|
+
# Instance Methods
|
234
|
+
#
|
206
235
|
|
207
236
|
# {Mdm::Host::OperatingSystemNormalization#normalize_os Normalizes the host operating system} whenever {#info} has
|
208
237
|
# changed.
|
data/app/models/mdm/tag.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
class Mdm::Tag < ActiveRecord::Base
|
2
|
+
include Metasploit::Model::Search
|
3
|
+
|
2
4
|
#
|
3
5
|
# Relations
|
4
6
|
#
|
@@ -26,6 +28,14 @@ class Mdm::Tag < ActiveRecord::Base
|
|
26
28
|
# @return [ActiveRecord::Relation<Mdm::Host>]
|
27
29
|
has_many :hosts, :through => :hosts_tags, :class_name => 'Mdm::Host'
|
28
30
|
|
31
|
+
#
|
32
|
+
# Search
|
33
|
+
#
|
34
|
+
|
35
|
+
search_attribute :desc,
|
36
|
+
type: :string
|
37
|
+
search_attribute :name,
|
38
|
+
type: :string
|
29
39
|
|
30
40
|
#
|
31
41
|
# Validations
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# An IPv4 CIDR (Classless InterDomain Routing) block composed of a
|
2
|
+
# {MetasploitDataModels::IPAddress::V4::Single IPv4} {MetasploitDataModels::IPAddress::CIDR#address address} and
|
3
|
+
# {MetasploitDataModels::IPAddress::CIDR#prefix_length prefix_length} written in the form `'a.b.c.d/prefix_length'`.
|
4
|
+
#
|
5
|
+
# @see https://en.wikipedia.org/wiki/Cidr#IPv6_CIDR_blocks
|
6
|
+
class MetasploitDataModels::IPAddress::V4::CIDR < Metasploit::Model::Base
|
7
|
+
include MetasploitDataModels::IPAddress::CIDR
|
8
|
+
|
9
|
+
#
|
10
|
+
# CIDR
|
11
|
+
#
|
12
|
+
|
13
|
+
cidr address_class: MetasploitDataModels::IPAddress::V4::Single
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Nmap's octet range format composed of segments of comma separated list of segment numbers and segment number ranges.
|
2
|
+
#
|
3
|
+
# @example Nmap octect range format
|
4
|
+
# # equivalent to ['1.5.6.7', '3.5.6.7', '4.5.6.7']
|
5
|
+
# '1,3-4.5.6.7'
|
6
|
+
#
|
7
|
+
# @see http://nmap.org/book/man-target-specification.html
|
8
|
+
class MetasploitDataModels::IPAddress::V4::Nmap < MetasploitDataModels::IPAddress::V4::Segmented
|
9
|
+
#
|
10
|
+
# Segments
|
11
|
+
#
|
12
|
+
|
13
|
+
segment class_name: 'MetasploitDataModels::IPAddress::V4::Segment::Nmap::List'
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# A range of complete IPv4 addresses, separated by a `-`.
|
2
|
+
class MetasploitDataModels::IPAddress::V4::Range < Metasploit::Model::Base
|
3
|
+
extend MetasploitDataModels::Match::Child
|
4
|
+
|
5
|
+
include MetasploitDataModels::IPAddress::Range
|
6
|
+
|
7
|
+
#
|
8
|
+
# Range Extremes
|
9
|
+
#
|
10
|
+
|
11
|
+
extremes class_name: 'MetasploitDataModels::IPAddress::V4::Single'
|
12
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# A comma separated list of {MetasploitDataModels::IPAddress::V4::Segment::Single segment numbers} and
|
2
|
+
# {MetasploitDataModels::IPAddress::V4::Segment::Nmap::Range range of segment numbers} making up one segment of
|
3
|
+
# {MetasploitDataModels::IPAddress::V4::Nmap}.
|
4
|
+
class MetasploitDataModels::IPAddress::V4::Segment::Nmap::List < Metasploit::Model::Base
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
|
7
|
+
include MetasploitDataModels::Match::Parent
|
8
|
+
|
9
|
+
#
|
10
|
+
# CONSTANTS
|
11
|
+
#
|
12
|
+
|
13
|
+
# Either an individual {MetasploitDataModels::IPAddress::V4::Segment::Single segment number} or a
|
14
|
+
# {MetasploitDataModels::IPAddress::V4::Segment::Nmap::Range segment range}.
|
15
|
+
RANGE_OR_NUMBER_REGEXP = %r{
|
16
|
+
(?<range>#{parent::Range.regexp})
|
17
|
+
|
|
18
|
+
# range first because it contains a segment and if the range isn't first only the first part of the range will
|
19
|
+
# match.
|
20
|
+
(?<number>#{MetasploitDataModels::IPAddress::V4::Segment::Single::REGEXP})
|
21
|
+
}x
|
22
|
+
# Separator between number or ranges
|
23
|
+
SEPARATOR = ','
|
24
|
+
# Segment of an NMAP address, composed of comma separated {RANGE_OR_NUMBER_REGEXP segment numbers or ranges}.
|
25
|
+
REGEXP = /#{RANGE_OR_NUMBER_REGEXP}(#{SEPARATOR}#{RANGE_OR_NUMBER_REGEXP})*/
|
26
|
+
|
27
|
+
# Matches exactly an Nmap comma separated list of segment numbers and ranges.
|
28
|
+
MATCH_REGEXP = /\A#{REGEXP}\z/
|
29
|
+
|
30
|
+
#
|
31
|
+
# Attributes
|
32
|
+
#
|
33
|
+
|
34
|
+
# @!attribute value
|
35
|
+
# The NMAP IPv4 octect range.
|
36
|
+
#
|
37
|
+
# @return [Array<MetasploitDataModels::IPAddress::V4::Segment::Number, MetasploitDataModels::IPAddress::V4::Segment::Range>]
|
38
|
+
# number and range in the order they appeared in formatted value.
|
39
|
+
attr_reader :value
|
40
|
+
|
41
|
+
#
|
42
|
+
# Match Children
|
43
|
+
#
|
44
|
+
|
45
|
+
match_children_named %w{
|
46
|
+
MetasploitDataModels::IPAddress::V4::Segment::Single
|
47
|
+
MetasploitDataModels::IPAddress::V4::Segment::Nmap::Range
|
48
|
+
}
|
49
|
+
|
50
|
+
#
|
51
|
+
#
|
52
|
+
# Validations
|
53
|
+
#
|
54
|
+
#
|
55
|
+
|
56
|
+
#
|
57
|
+
# Method Validations
|
58
|
+
#
|
59
|
+
|
60
|
+
validate :value_elements_valid
|
61
|
+
validate :value_is_array
|
62
|
+
|
63
|
+
#
|
64
|
+
# Attribute Validations
|
65
|
+
#
|
66
|
+
|
67
|
+
validates :value,
|
68
|
+
presence: true
|
69
|
+
|
70
|
+
#
|
71
|
+
# Instance Methods
|
72
|
+
#
|
73
|
+
|
74
|
+
# @return [String]
|
75
|
+
def to_s
|
76
|
+
if value.is_a? Array
|
77
|
+
value.map(&:to_s).join(SEPARATOR)
|
78
|
+
else
|
79
|
+
value.to_s
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Set {#value} to an `Array` of segment numbers and ranges.
|
84
|
+
#
|
85
|
+
# @param formatted_value [#to_s]
|
86
|
+
# @return [Array<MetasploitDataModels::IPAddress::V4::Segment::Single, MetasploitDataModels::IPAddress::V4::Segment::Nmap::Range>] a parsed `Array` of segment numbers and ranges.
|
87
|
+
# @return [#to_s] if `formatted_value` does not match {MATCH_REGEXP}.
|
88
|
+
def value=(formatted_value)
|
89
|
+
string = formatted_value.to_s
|
90
|
+
match = MATCH_REGEXP.match(string)
|
91
|
+
|
92
|
+
if match
|
93
|
+
ranges_or_numbers = string.split(SEPARATOR)
|
94
|
+
|
95
|
+
@value = ranges_or_numbers.map { |range_or_number|
|
96
|
+
match_child(range_or_number) || range_or_number
|
97
|
+
}
|
98
|
+
else
|
99
|
+
@value = formatted_value
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# Validates that {#value}'s elements are all valid.
|
106
|
+
#
|
107
|
+
# @return [void]
|
108
|
+
def value_elements_valid
|
109
|
+
if value.is_a? Array
|
110
|
+
value.each_with_index do |element, index|
|
111
|
+
unless element.valid?
|
112
|
+
errors.add(:value, :element, element: element, index: index)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Validates that {#value} is an `Array`.
|
119
|
+
#
|
120
|
+
# @return [void]
|
121
|
+
def value_is_array
|
122
|
+
unless value.is_a? Array
|
123
|
+
errors.add(:value, :array)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# A range of segment number composed of a {#begin} and {#end} segment number, separated by a `-`.
|
2
|
+
class MetasploitDataModels::IPAddress::V4::Segment::Nmap::Range < Metasploit::Model::Base
|
3
|
+
extend MetasploitDataModels::Match::Child
|
4
|
+
|
5
|
+
include MetasploitDataModels::IPAddress::Range
|
6
|
+
|
7
|
+
#
|
8
|
+
# Range Extremes
|
9
|
+
#
|
10
|
+
|
11
|
+
extremes class_name: 'MetasploitDataModels::IPAddress::V4::Segment::Single'
|
12
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# A segment number in an IPv4 address or the
|
2
|
+
# {MetasploitDataModels::IPAddress::V4::Segment::Nmap::Range#begin} or
|
3
|
+
# {MetasploitDataModels::IPAddress::V4::Segment::Nmap::Range#send}.
|
4
|
+
class MetasploitDataModels::IPAddress::V4::Segment::Single < Metasploit::Model::Base
|
5
|
+
extend MetasploitDataModels::Match::Child
|
6
|
+
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
#
|
10
|
+
# CONSTANTS
|
11
|
+
#
|
12
|
+
|
13
|
+
# Number of bits in a IPv4 segment
|
14
|
+
BITS = 8
|
15
|
+
|
16
|
+
# Limit that {#value} can never reach
|
17
|
+
LIMIT = 1 << BITS
|
18
|
+
|
19
|
+
# Maximum segment {#value}
|
20
|
+
MAXIMUM = LIMIT - 1
|
21
|
+
|
22
|
+
# Minimum segment {#value}
|
23
|
+
MINIMUM = 0
|
24
|
+
|
25
|
+
# Regular expression for a segment (octet) of an IPv4 address in decimal dotted notation.
|
26
|
+
#
|
27
|
+
# @see http://stackoverflow.com/a/17871737/470451
|
28
|
+
REGEXP = /(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])/
|
29
|
+
|
30
|
+
#
|
31
|
+
# Attributes
|
32
|
+
#
|
33
|
+
|
34
|
+
# @!attribute value
|
35
|
+
# The segment number.
|
36
|
+
#
|
37
|
+
# @return [Integer, String]
|
38
|
+
attr_reader :value
|
39
|
+
|
40
|
+
#
|
41
|
+
# Validations
|
42
|
+
#
|
43
|
+
|
44
|
+
validates :value,
|
45
|
+
numericality: {
|
46
|
+
greater_than_or_equal_to: MINIMUM,
|
47
|
+
less_than_or_equal_to: MAXIMUM,
|
48
|
+
only_integer: true
|
49
|
+
}
|
50
|
+
|
51
|
+
#
|
52
|
+
# Class Methods
|
53
|
+
#
|
54
|
+
|
55
|
+
# (see BITS)
|
56
|
+
#
|
57
|
+
# @return [Integer] {BITS}
|
58
|
+
def self.bits
|
59
|
+
BITS
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Instance Methods
|
64
|
+
#
|
65
|
+
|
66
|
+
# Compare this segment to `other`.
|
67
|
+
#
|
68
|
+
# @param other [#value] another segent to compare against.
|
69
|
+
# @return [1] if this segment is greater than `other`.
|
70
|
+
# @return [0] if this segment is equal to `other`.
|
71
|
+
# @return [-1] if this segment is less than `other`.
|
72
|
+
def <=>(other)
|
73
|
+
value <=> other.value
|
74
|
+
end
|
75
|
+
|
76
|
+
# Full add (as in [full adder](https://en.wikipedia.org/wiki/Full_adder)) two (this segment and `other`) segments and
|
77
|
+
# a carry from the previous {#add_with_carry}.
|
78
|
+
#
|
79
|
+
# @param other [MetasploitDataModels:IPAddress::V4::Segment::Single] segment to add to this segment.
|
80
|
+
# @param carry [Integer] integer to add to this segment and other segment from a previous call to {#add_with_carry}
|
81
|
+
# for lower segments.
|
82
|
+
# @return [Array<(MetasploitDataModels::IPAddress::V4::Segment::Single, Integer)>] Array containing a proper segment
|
83
|
+
# (where {#value} is less than {LIMIT}) and a carry integer to pass to next call to {#add_with_carry}.
|
84
|
+
# @return (see #half_add)
|
85
|
+
def add_with_carry(other, carry=0)
|
86
|
+
improper_value = self.value + other.value + carry
|
87
|
+
proper_value = improper_value % LIMIT
|
88
|
+
carry = improper_value / LIMIT
|
89
|
+
segment = self.class.new(value: proper_value)
|
90
|
+
|
91
|
+
[segment, carry]
|
92
|
+
end
|
93
|
+
|
94
|
+
# The succeeding segment. Used in `Range`s when walking the `Range`.
|
95
|
+
#
|
96
|
+
# @return [MetasploitDataModels::IPAddress::V4::Segment::Single] if {#value} responds to `#succ`.
|
97
|
+
# @return [nil] otherwise
|
98
|
+
def succ
|
99
|
+
if value.respond_to? :succ
|
100
|
+
self.class.new(value: value.succ)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
delegate :to_s,
|
105
|
+
to: :value
|
106
|
+
|
107
|
+
# Sets {#value} by type casting String to Integer.
|
108
|
+
#
|
109
|
+
# @param formatted_value [#to_s]
|
110
|
+
# @return [Integer] if `formatted_value` contains only an Integer#to_s
|
111
|
+
# @return [#to_s] `formatted_value` if it does not contain an Integer#to_s
|
112
|
+
def value=(formatted_value)
|
113
|
+
@value_before_type_cast = formatted_value
|
114
|
+
|
115
|
+
begin
|
116
|
+
# use Integer() instead of String#to_i as String#to_i will ignore trailing letters (i.e. '1two' -> 1) and turn all
|
117
|
+
# string without an integer in it to 0.
|
118
|
+
@value = Integer(formatted_value.to_s)
|
119
|
+
rescue ArgumentError
|
120
|
+
@value = formatted_value
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
# @note {segment} must be called in subclasses to set the {segment_class_name}.
|
2
|
+
#
|
3
|
+
# An IPv4 address that is composed of {SEGMENT_COUNT 4} {#segments} separated by {SEPARATOR `'.'`}.
|
4
|
+
#
|
5
|
+
# @example Using single segments to make a single IPv4 address class
|
6
|
+
# class MetasploitDataModels::IPAddress::V4::Single < MetasploitDataModels::IPAddress::V4::Segmented
|
7
|
+
# #
|
8
|
+
# # Segments
|
9
|
+
# #
|
10
|
+
#
|
11
|
+
# segment class_name: 'MetasploitDataModels::IPAddress::V4::Segment::Single'
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
class MetasploitDataModels::IPAddress::V4::Segmented < Metasploit::Model::Base
|
15
|
+
extend MetasploitDataModels::Match::Child
|
16
|
+
|
17
|
+
include Comparable
|
18
|
+
|
19
|
+
#
|
20
|
+
# CONSTANTS
|
21
|
+
#
|
22
|
+
|
23
|
+
# The number of {#segments}
|
24
|
+
SEGMENT_COUNT = 4
|
25
|
+
# Separator between segments
|
26
|
+
SEPARATOR = '.'
|
27
|
+
|
28
|
+
#
|
29
|
+
# Attributes
|
30
|
+
#
|
31
|
+
|
32
|
+
# @!attribute value
|
33
|
+
# Segments of IP address from high to low.
|
34
|
+
#
|
35
|
+
# @return [Array<MetasploitDataModels::IPAddress:V4::Segment::Nmap>]
|
36
|
+
attr_reader :value
|
37
|
+
|
38
|
+
#
|
39
|
+
#
|
40
|
+
# Validations
|
41
|
+
#
|
42
|
+
#
|
43
|
+
|
44
|
+
#
|
45
|
+
# Validation Methods
|
46
|
+
#
|
47
|
+
|
48
|
+
validate :segments_valid
|
49
|
+
|
50
|
+
#
|
51
|
+
# Attribute Validations
|
52
|
+
#
|
53
|
+
|
54
|
+
validates :segments,
|
55
|
+
length: {
|
56
|
+
is: SEGMENT_COUNT
|
57
|
+
}
|
58
|
+
|
59
|
+
#
|
60
|
+
# Class methods
|
61
|
+
#
|
62
|
+
|
63
|
+
# @note Call {segment} with the {segment_class_name} before calling this method, as it uses {segment_class} to look
|
64
|
+
# up the `REGEXP` of the {segment_class}.
|
65
|
+
#
|
66
|
+
# Regular expression that matches the part of a string that represents a IPv4 segmented IP address format.
|
67
|
+
#
|
68
|
+
# @return [Regexp]
|
69
|
+
def self.regexp
|
70
|
+
unless @regexp
|
71
|
+
separated_segment_count = SEGMENT_COUNT - 1
|
72
|
+
|
73
|
+
@regexp = %r{
|
74
|
+
(#{segment_class::REGEXP}#{Regexp.escape(SEPARATOR)}){#{separated_segment_count},#{separated_segment_count}}
|
75
|
+
#{segment_class::REGEXP}
|
76
|
+
}x
|
77
|
+
end
|
78
|
+
|
79
|
+
@regexp
|
80
|
+
end
|
81
|
+
|
82
|
+
# Sets up the {segment_class_name} for the subclass.
|
83
|
+
#
|
84
|
+
# @example Using {segment} to set {segment_class_name}
|
85
|
+
# segment class_name: 'MetasploitDataModels::IPAddress::V4::Segment::Single'
|
86
|
+
#
|
87
|
+
# @param options [Hash{Symbol => String}]
|
88
|
+
# @option options [String] :class_name a `Class#name` to use for {segment_class_name}.
|
89
|
+
# @return [void]
|
90
|
+
def self.segment(options={})
|
91
|
+
options.assert_valid_keys(:class_name)
|
92
|
+
|
93
|
+
@segment_class_name = options.fetch(:class_name)
|
94
|
+
end
|
95
|
+
|
96
|
+
# @note Call {segment} to set the {segment_class_name} before calling {segment_class}, which will attempt to
|
97
|
+
# String#constantize` {segment_class_name}.
|
98
|
+
#
|
99
|
+
# The `Class` used to parse each segment of the IPv4 address.
|
100
|
+
#
|
101
|
+
# @return [Class]
|
102
|
+
def self.segment_class
|
103
|
+
@segment_class = segment_class_name.constantize
|
104
|
+
end
|
105
|
+
|
106
|
+
# @note Call {segment} to set {segment_class_name}
|
107
|
+
#
|
108
|
+
# The name of {segment_class}
|
109
|
+
#
|
110
|
+
# @return [String] a `Class#name` for {segment_class}.
|
111
|
+
def self.segment_class_name
|
112
|
+
@segment_class_name
|
113
|
+
end
|
114
|
+
|
115
|
+
# (see SEGMENT_COUNT)
|
116
|
+
#
|
117
|
+
# @return [Integer]
|
118
|
+
def self.segment_count
|
119
|
+
SEGMENT_COUNT
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Instance methods
|
124
|
+
#
|
125
|
+
|
126
|
+
# Compare this segment IPv4 address to `other`.
|
127
|
+
#
|
128
|
+
# @return [1] if {#segments} are greater than {#segments} of `other`.
|
129
|
+
# @return [0] if {#segments} are equal to {#segments} of `other`.
|
130
|
+
# @return [-1] if {#segments} are less than {#segments} of `other`.
|
131
|
+
# @return [nil] if `other` isn't the same `Class`
|
132
|
+
def <=>(other)
|
133
|
+
if other.is_a? self.class
|
134
|
+
segments <=> other.segments
|
135
|
+
else
|
136
|
+
# The interface for <=> requires nil be returned if other is incomparable
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Array of segments.
|
142
|
+
#
|
143
|
+
# @return [Array] if {#value} is an `Array`.
|
144
|
+
# @return [[]] if {#value} is not an `Array`.
|
145
|
+
def segments
|
146
|
+
if value.is_a? Array
|
147
|
+
value
|
148
|
+
else
|
149
|
+
[]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Set {#segments}.
|
154
|
+
#
|
155
|
+
# @param segments [Array] `Array` of {segment_class} instances
|
156
|
+
# @return [Array] `Array` of {segment_class} instances
|
157
|
+
def segments=(segments)
|
158
|
+
@value = segments
|
159
|
+
end
|
160
|
+
|
161
|
+
# Segments joined with {SEPARATOR}.
|
162
|
+
#
|
163
|
+
# @return [String]
|
164
|
+
def to_s
|
165
|
+
segments.map(&:to_s).join(SEPARATOR)
|
166
|
+
end
|
167
|
+
|
168
|
+
# @note Set {#segments} if value is not formatted, but already broken into an `Array` of {segment_class} instances.
|
169
|
+
#
|
170
|
+
# Sets {#value} by parsing its segments.
|
171
|
+
#
|
172
|
+
# @param formatted_value [#to_s]
|
173
|
+
def value=(formatted_value)
|
174
|
+
string = formatted_value.to_s
|
175
|
+
match = self.class.match_regexp.match(string)
|
176
|
+
|
177
|
+
if match
|
178
|
+
segments = string.split(SEPARATOR)
|
179
|
+
|
180
|
+
@value = segments.map { |segment|
|
181
|
+
self.class.segment_class.new(value: segment)
|
182
|
+
}
|
183
|
+
else
|
184
|
+
@value = formatted_value
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
# Validates that all segments in {#segments} are valid.
|
191
|
+
#
|
192
|
+
# @return [void]
|
193
|
+
def segments_valid
|
194
|
+
segments.each_with_index do |segment, index|
|
195
|
+
unless segment.valid?
|
196
|
+
errors.add(:segments, :segment_invalid, index: index, segment: segment)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|