metasploit_data_models 0.18.1 → 0.19.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.
- 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
|