rex-socket 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a2cd6273cbf1dc4a60a2af6dc31601368bc7931d
4
+ data.tar.gz: 3c8cbbe8f77806d8c391e79964b30dc0c43a3afd
5
+ SHA512:
6
+ metadata.gz: baba065cc14de4e9b4ecab38bed825e2fd4eca61566ff743c394b0eee694a65b0f81bfcfaa4b2f81802a21ea3f0cb920417fea4f0a9f602a1f8eb9922c81a5e0
7
+ data.tar.gz: 6381df08052531dab6b6382f85883c20962a971584e1f24e416d80d5a1fb2624f0847f7cb133659b81ad04dd2edea3e3dcb74f7172361c5d81907a91a677230b
Binary file
Binary file
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.5
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at DMaloney@rapid7.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rex-socket.gemspec
4
+ gemspec
@@ -0,0 +1,32 @@
1
+ # Rex::Socket
2
+
3
+ The Ruby Exploitation (Rex) Socket Abstraction Library. This library includes all of the code needed to turn sockets into
4
+ Rex::Sockets with the functionality for things like L3 pivoting used by Metasploit.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'rex-socket'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install rex-socket
21
+
22
+
23
+ ## Development
24
+
25
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
26
+
27
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rex-socket. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
32
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rex/socket"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,798 @@
1
+ # -*- coding: binary -*-
2
+ require 'rex/socket/version'
3
+ require 'socket'
4
+ require 'thread'
5
+ require 'resolv'
6
+ require 'rex/exceptions'
7
+
8
+ module Rex
9
+
10
+ ###
11
+ #
12
+ # Base class for all sockets.
13
+ #
14
+ ###
15
+ module Socket
16
+
17
+ module Comm
18
+ end
19
+
20
+ require 'rex/socket/x509_certificate'
21
+ require 'rex/socket/parameters'
22
+ require 'rex/socket/tcp'
23
+ require 'rex/socket/tcp_server'
24
+
25
+ require 'rex/socket/comm'
26
+ require 'rex/socket/comm/local'
27
+
28
+ require 'rex/socket/switch_board'
29
+ require 'rex/socket/subnet_walker'
30
+ require 'rex/socket/range_walker'
31
+
32
+ ##
33
+ #
34
+ # Factory methods
35
+ #
36
+ ##
37
+
38
+ #
39
+ # Create a socket instance using the supplied parameter hash.
40
+ #
41
+ def self.create(opts = {})
42
+ return create_param(Rex::Socket::Parameters.from_hash(opts))
43
+ end
44
+
45
+ #
46
+ # Create a socket using the supplied Rex::Socket::Parameter instance.
47
+ #
48
+ def self.create_param(param)
49
+ return param.comm.create(param)
50
+ end
51
+
52
+ #
53
+ # Create a TCP socket using the supplied parameter hash.
54
+ #
55
+ def self.create_tcp(opts = {})
56
+ return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'tcp')))
57
+ end
58
+
59
+ #
60
+ # Create a TCP server socket using the supplied parameter hash.
61
+ #
62
+ def self.create_tcp_server(opts = {})
63
+ return create_tcp(opts.merge('Server' => true))
64
+ end
65
+
66
+ #
67
+ # Create a UDP socket using the supplied parameter hash.
68
+ #
69
+ def self.create_udp(opts = {})
70
+ return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'udp')))
71
+ end
72
+
73
+ #
74
+ # Create a IP socket using the supplied parameter hash.
75
+ #
76
+ def self.create_ip(opts = {})
77
+ return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'ip')))
78
+ end
79
+
80
+
81
+ #
82
+ # Common Regular Expressions
83
+ #
84
+
85
+ MATCH_IPV6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
86
+
87
+ MATCH_IPV4 = /^\s*(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))\s*$/
88
+
89
+ MATCH_IPV4_PRIVATE = /^\s*(?:10\.|192\.168|172.(?:1[6-9]|2[0-9]|3[01])\.|169\.254)/
90
+
91
+ ##
92
+ #
93
+ # Serialization
94
+ #
95
+ ##
96
+
97
+
98
+ # Cache our IPv6 support flag
99
+ @@support_ipv6 = nil
100
+
101
+ #
102
+ # Determine whether we support IPv6
103
+ #
104
+ def self.support_ipv6?
105
+ return @@support_ipv6 if not @@support_ipv6.nil?
106
+
107
+ @@support_ipv6 = false
108
+
109
+ if (::Socket.const_defined?('AF_INET6'))
110
+ begin
111
+ s = ::Socket.new(::Socket::AF_INET6, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP)
112
+ s.close
113
+ @@support_ipv6 = true
114
+ rescue
115
+ end
116
+ end
117
+
118
+ return @@support_ipv6
119
+ end
120
+
121
+ #
122
+ # Determine whether this is an IPv4 address
123
+ #
124
+ def self.is_ipv4?(addr)
125
+ ( addr =~ MATCH_IPV4 ) ? true : false
126
+ end
127
+
128
+ #
129
+ # Determine whether this is an IPv6 address
130
+ #
131
+ def self.is_ipv6?(addr)
132
+ ( addr =~ MATCH_IPV6 ) ? true : false
133
+ end
134
+
135
+ #
136
+ # Checks to see if the supplied address is in "dotted" form
137
+ #
138
+ def self.dotted_ip?(addr)
139
+ # Match IPv6
140
+ return true if (support_ipv6? and addr =~ MATCH_IPV6)
141
+
142
+ # Match IPv4
143
+ return true if (addr =~ MATCH_IPV4)
144
+
145
+ false
146
+ end
147
+
148
+ #
149
+ # Return true if +addr+ is within the ranges specified in RFC1918, or
150
+ # RFC5735/RFC3927
151
+ #
152
+ def self.is_internal?(addr)
153
+ if self.dotted_ip?(addr)
154
+ addr =~ MATCH_IPV4_PRIVATE
155
+ else
156
+ false
157
+ end
158
+ end
159
+
160
+ # Get the first address returned by a DNS lookup for +hostname+.
161
+ #
162
+ # @see .getaddresses
163
+ #
164
+ # @param (see .getaddresses)
165
+ # @return [String] ASCII IP address
166
+ def self.getaddress(hostname, accept_ipv6 = true)
167
+ getaddresses(hostname, accept_ipv6).first
168
+ end
169
+
170
+ #
171
+ # Wrapper for +::Socket.gethostbyname+ that takes special care to see if the
172
+ # supplied address is already an ASCII IP address. This is necessary to
173
+ # prevent blocking while waiting on a DNS reverse lookup when we already
174
+ # have what we need.
175
+ #
176
+ # @param hostname [String] A hostname or ASCII IP address
177
+ # @return [Array<String>]
178
+ def self.getaddresses(hostname, accept_ipv6 = true)
179
+ if hostname =~ MATCH_IPV4 or (accept_ipv6 and hostname =~ MATCH_IPV6)
180
+ return [hostname]
181
+ end
182
+
183
+ res = ::Socket.gethostbyname(hostname)
184
+ return [] if not res
185
+
186
+ # Shift the first three elements out, leaving just the list of
187
+ # addresses
188
+ res.shift # name
189
+ res.shift # alias hostnames
190
+ res.shift # address_family
191
+
192
+ # Rubinius has a bug where gethostbyname returns dotted quads instead of
193
+ # NBO, but that's what we want anyway, so just short-circuit here.
194
+ if res[0] =~ MATCH_IPV4 || res[0] =~ MATCH_IPV6
195
+ unless accept_ipv6
196
+ res.reject!{ |ascii| ascii =~ MATCH_IPV6 }
197
+ end
198
+ else
199
+ unless accept_ipv6
200
+ res.reject!{ |nbo| nbo.length != 4 }
201
+ end
202
+ res.map!{ |nbo| self.addr_ntoa(nbo) }
203
+ end
204
+
205
+ res
206
+ end
207
+
208
+ #
209
+ # Wrapper for Socket.gethostbyname which takes into account whether or not
210
+ # an IP address is supplied. If it is, then reverse DNS resolution does
211
+ # not occur. This is done in order to prevent delays, such as would occur
212
+ # on Windows.
213
+ #
214
+ def self.gethostbyname(host)
215
+ if (is_ipv4?(host))
216
+ return [ host, [], 2, host.split('.').map{ |c| c.to_i }.pack("C4") ]
217
+ end
218
+
219
+ if is_ipv6?(host)
220
+ # pop off the scopeid since gethostbyname isn't smart enough to
221
+ # deal with it.
222
+ host, _ = host.split('%', 2)
223
+ end
224
+
225
+ ::Socket.gethostbyname(host)
226
+ end
227
+
228
+ #
229
+ # Create a sockaddr structure using the supplied IP address, port, and
230
+ # address family
231
+ #
232
+ def self.to_sockaddr(ip, port)
233
+
234
+ if (ip == '::ffff:0.0.0.0')
235
+ ip = support_ipv6?() ? '::' : '0.0.0.0'
236
+ end
237
+
238
+ return ::Socket.pack_sockaddr_in(port, ip)
239
+ end
240
+
241
+ #
242
+ # Returns the address family, host, and port of the supplied sockaddr as
243
+ # [ af, host, port ]
244
+ #
245
+ def self.from_sockaddr(saddr)
246
+ port, host = ::Socket::unpack_sockaddr_in(saddr)
247
+ af = ::Socket::AF_INET
248
+ if (support_ipv6?() and is_ipv6?(host))
249
+ af = ::Socket::AF_INET6
250
+ end
251
+ return [ af, host, port ]
252
+ end
253
+
254
+ #
255
+ # Resolves a host to raw network-byte order.
256
+ #
257
+ def self.resolv_nbo(host)
258
+ self.gethostbyname( Rex::Socket.getaddress(host, true) )[3]
259
+ end
260
+
261
+ #
262
+ # Resolves a host to raw network-byte order.
263
+ #
264
+ def self.resolv_nbo_list(host)
265
+ Rex::Socket.getaddresses(host).map{|addr| self.gethostbyname(addr)[3] }
266
+ end
267
+
268
+ #
269
+ # Resolves a host to a network-byte order ruby integer.
270
+ #
271
+ def self.resolv_nbo_i(host)
272
+ addr_ntoi(resolv_nbo(host))
273
+ end
274
+
275
+ #
276
+ # Resolves a host to a list of network-byte order ruby integers.
277
+ #
278
+ def self.resolv_nbo_i_list(host)
279
+ resolv_nbo_list(host).map{|addr| addr_ntoi(addr) }
280
+ end
281
+
282
+ #
283
+ # Converts an ASCII IP address to a CIDR mask. Returns
284
+ # nil if it's not convertable.
285
+ #
286
+ def self.addr_atoc(mask)
287
+ mask_i = resolv_nbo_i(mask)
288
+ cidr = nil
289
+ 0.upto(32) do |i|
290
+ if ((1 << i)-1) << (32-i) == mask_i
291
+ cidr = i
292
+ break
293
+ end
294
+ end
295
+ return cidr
296
+ end
297
+
298
+ #
299
+ # Resolves a CIDR bitmask into a dotted-quad. Returns
300
+ # nil if it's not convertable.
301
+ #
302
+ def self.addr_ctoa(cidr)
303
+ return nil unless (0..32) === cidr.to_i
304
+ addr_itoa(((1 << cidr)-1) << 32-cidr)
305
+ end
306
+
307
+ #
308
+ # Resolves a host to a dotted address.
309
+ #
310
+ def self.resolv_to_dotted(host)
311
+ addr_ntoa(addr_aton(host))
312
+ end
313
+
314
+ #
315
+ # Converts a ascii address into an integer
316
+ #
317
+ def self.addr_atoi(addr)
318
+ resolv_nbo_i(addr)
319
+ end
320
+
321
+ #
322
+ # Converts a ascii address into a list of addresses
323
+ #
324
+ def self.addr_atoi_list(addr)
325
+ resolv_nbo_i_list(addr)
326
+ end
327
+
328
+ #
329
+ # Converts an integer address into ascii
330
+ #
331
+ # @param (see #addr_iton)
332
+ # @return (see #addr_ntoa)
333
+ def self.addr_itoa(addr, v6=false)
334
+ nboa = addr_iton(addr, v6)
335
+
336
+ addr_ntoa(nboa)
337
+ end
338
+
339
+ #
340
+ # Converts a ascii address to network byte order
341
+ #
342
+ def self.addr_aton(addr)
343
+ resolv_nbo(addr)
344
+ end
345
+
346
+ #
347
+ # Converts a network byte order address to ascii
348
+ #
349
+ # @param addr [String] Packed network-byte-order address
350
+ # @return [String] Human readable IP address.
351
+ def self.addr_ntoa(addr)
352
+ # IPv4
353
+ if (addr.length == 4)
354
+ return addr.unpack('C4').join('.')
355
+ end
356
+
357
+ # IPv6
358
+ if (addr.length == 16)
359
+ return compress_address(addr.unpack('n8').map{ |c| "%x" % c }.join(":"))
360
+ end
361
+
362
+ raise RuntimeError, "Invalid address format"
363
+ end
364
+
365
+ #
366
+ # Implement zero compression for IPv6 addresses.
367
+ # Uses the compression method from Marco Ceresa's IPAddress GEM
368
+ #
369
+ # @see https://github.com/bluemonk/ipaddress/blob/master/lib/ipaddress/ipv6.rb
370
+ #
371
+ # @param addr [String] Human readable IPv6 address
372
+ # @return [String] Human readable IPv6 address with runs of 0s removed
373
+ def self.compress_address(addr)
374
+ return addr unless is_ipv6?(addr)
375
+ addr = addr.dup
376
+ while true
377
+ break if addr.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
378
+ break if addr.sub!(/\b0:0:0:0:0:0:0\b/, ':')
379
+ break if addr.sub!(/\b0:0:0:0:0:0\b/, ':')
380
+ break if addr.sub!(/\b0:0:0:0:0\b/, ':')
381
+ break if addr.sub!(/\b0:0:0:0\b/, ':')
382
+ break if addr.sub!(/\b0:0:0\b/, ':')
383
+ break if addr.sub!(/\b0:0\b/, ':')
384
+ break
385
+ end
386
+ addr.sub(/:{3,}/, '::')
387
+ end
388
+
389
+ #
390
+ # Converts a network byte order address to an integer
391
+ #
392
+ def self.addr_ntoi(addr)
393
+
394
+ bits = addr.unpack("N*")
395
+
396
+ if (bits.length == 1)
397
+ return bits[0]
398
+ end
399
+
400
+ if (bits.length == 4)
401
+ val = 0
402
+ bits.each_index { |i| val += ( bits[i] << (96 - (i * 32)) ) }
403
+ return val
404
+ end
405
+
406
+ raise RuntimeError, "Invalid address format"
407
+ end
408
+
409
+ #
410
+ # Converts an integer into a network byte order address
411
+ #
412
+ # @param addr [Numeric] The address as a number
413
+ # @param v6 [Boolean] Whether +addr+ is IPv6
414
+ def self.addr_iton(addr, v6=false)
415
+ if(addr < 0x100000000 && !v6)
416
+ return [addr].pack('N')
417
+ else
418
+ w = []
419
+ w[0] = (addr >> 96) & 0xffffffff
420
+ w[1] = (addr >> 64) & 0xffffffff
421
+ w[2] = (addr >> 32) & 0xffffffff
422
+ w[3] = addr & 0xffffffff
423
+ return w.pack('N4')
424
+ end
425
+ end
426
+
427
+ #
428
+ # Converts a colon-delimited MAC address into a 6-byte binary string
429
+ #
430
+ def self.eth_aton(mac)
431
+ mac.split(":").map{|c| c.to_i(16) }.pack("C*")
432
+ end
433
+
434
+ #
435
+ # Converts a 6-byte binary string into a colon-delimited MAC address
436
+ #
437
+ def self.eth_ntoa(bin)
438
+ bin.unpack("C6").map{|x| "%.2x" % x }.join(":").upcase
439
+ end
440
+
441
+ #
442
+ # Converts a CIDR subnet into an array (base, bcast)
443
+ #
444
+ def self.cidr_crack(cidr, v6=false)
445
+ tmp = cidr.split('/')
446
+
447
+ tst,scope = tmp[0].split("%",2)
448
+ scope = "%" + scope if scope
449
+ scope ||= ""
450
+
451
+ addr = addr_atoi(tst)
452
+
453
+ bits = 32
454
+ mask = 0
455
+ use6 = false
456
+
457
+ if (addr > 0xffffffff or v6 or cidr =~ /:/)
458
+ use6 = true
459
+ bits = 128
460
+ end
461
+
462
+ mask = (2 ** bits) - (2 ** (bits - tmp[1].to_i))
463
+ base = addr & mask
464
+
465
+ stop = base + (2 ** (bits - tmp[1].to_i)) - 1
466
+ return [self.addr_itoa(base, use6) + scope, self.addr_itoa(stop, use6) + scope]
467
+ end
468
+
469
+ #
470
+ # Converts a netmask (255.255.255.240) into a bitmask (28). This is the
471
+ # lame kid way of doing it.
472
+ #
473
+ def self.net2bitmask(netmask)
474
+
475
+ nmask = resolv_nbo(netmask)
476
+ imask = addr_ntoi(nmask)
477
+ bits = 32
478
+
479
+ if (imask > 0xffffffff)
480
+ bits = 128
481
+ end
482
+
483
+ 0.upto(bits-1) do |bit|
484
+ p = 2 ** bit
485
+ return (bits - bit) if ((imask & p) == p)
486
+ end
487
+
488
+ 0
489
+ end
490
+
491
+ #
492
+ # Converts a bitmask (28) into a netmask (255.255.255.240)
493
+ #
494
+ def self.bit2netmask(bitmask, ipv6=false)
495
+ if bitmask > 32 or ipv6
496
+ i = ((~((2 ** (128 - bitmask)) - 1)) & (2**128-1))
497
+ n = Rex::Socket.addr_iton(i, true)
498
+ return Rex::Socket.addr_ntoa(n)
499
+ else
500
+ [ (~((2 ** (32 - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
501
+ end
502
+ end
503
+
504
+
505
+ def self.portspec_crack(pspec)
506
+ portspec_to_portlist(pspec)
507
+ end
508
+
509
+ #
510
+ # Converts a port specification like "80,21-25,!24,443" into a sorted,
511
+ # unique array of valid port numbers like [21,22,23,25,80,443]
512
+ #
513
+ def self.portspec_to_portlist(pspec)
514
+ ports = []
515
+ remove = []
516
+
517
+ # Build ports array from port specification
518
+ pspec.split(/,/).each do |item|
519
+ target = ports
520
+
521
+ item.strip!
522
+
523
+ if item.start_with? '!'
524
+ item.delete! '!'
525
+ target = remove
526
+ end
527
+
528
+ start, stop = item.split(/-/).map { |p| p.to_i }
529
+
530
+ start ||= 0
531
+ stop ||= item.match(/-/) ? 65535 : start
532
+
533
+ start, stop = stop, start if stop < start
534
+
535
+ start.upto(stop) { |p| target << p }
536
+ end
537
+
538
+ if ports.empty? and not remove.empty? then
539
+ ports = 1.upto 65535
540
+ end
541
+
542
+ # Sort, and remove dups and invalid ports
543
+ ports.sort.uniq.delete_if { |p| p < 1 or p > 65535 or remove.include? p }
544
+ end
545
+
546
+ #
547
+ # Converts a port list like [1,2,3,4,5,100] into a
548
+ # range specification like "1-5,100"
549
+ #
550
+ def self.portlist_to_portspec(parr)
551
+ ranges = []
552
+ range = []
553
+ lastp = nil
554
+
555
+ parr.uniq.sort{|a,b| a<=>b}.map{|a| a.to_i}.each do |n|
556
+ next if (n < 1 or n > 65535)
557
+ if not lastp
558
+ range = [n]
559
+ lastp = n
560
+ next
561
+ end
562
+
563
+ if lastp == n - 1
564
+ range << n
565
+ else
566
+ ranges << range
567
+ range = [n]
568
+ end
569
+ lastp = n
570
+ end
571
+
572
+ ranges << range
573
+ ranges.delete(nil)
574
+ ranges.uniq.map{|x| x.length == 1 ? "#{x[0]}" : "#{x[0]}-#{x[-1]}"}.join(",")
575
+ end
576
+
577
+ ##
578
+ #
579
+ # Utility class methods
580
+ #
581
+ ##
582
+
583
+ #
584
+ # This method does NOT send any traffic to the destination, instead, it uses a
585
+ # "bound" UDP socket to determine what source address we would use to
586
+ # communicate with the specified destination. The destination defaults to
587
+ # Google's DNS server to make the standard behavior determine which IP
588
+ # we would use to communicate with the internet.
589
+ #
590
+ def self.source_address(dest='8.8.8.8', comm = ::Rex::Socket::Comm::Local)
591
+ begin
592
+ s = self.create_udp(
593
+ 'PeerHost' => dest,
594
+ 'PeerPort' => 31337,
595
+ 'Comm' => comm
596
+ )
597
+ r = s.getsockname[1]
598
+ s.close
599
+
600
+ # Trim off the trailing interface ID for link-local IPv6
601
+ return r.split('%').first
602
+ rescue ::Exception
603
+ return '127.0.0.1'
604
+ end
605
+ end
606
+
607
+ #
608
+ # Identifies the link-local address of a given interface (if IPv6 is enabled)
609
+ #
610
+ def self.ipv6_link_address(intf)
611
+ r = source_address("FF02::1%#{intf}")
612
+ return nil if r.nil? || r !~ /^fe80/i
613
+ r
614
+ end
615
+
616
+ #
617
+ # Identifies the mac address of a given interface (if IPv6 is enabled)
618
+ #
619
+ def self.ipv6_mac(intf)
620
+ r = ipv6_link_address(intf)
621
+ return if not r
622
+ raw = addr_aton(r)[-8, 8]
623
+ (raw[0,3] + raw[5,3]).unpack("C*").map{|c| "%.2x" % c}.join(":")
624
+ end
625
+
626
+ #
627
+ # Create a TCP socket pair.
628
+ #
629
+ # sf: This create a socket pair using native ruby sockets and will work
630
+ # on Windows where ::Socket.pair is not implemented.
631
+ # Note: OpenSSL requires native ruby sockets for its io.
632
+ #
633
+ # Note: Even though sub-threads are smashing the parent threads local, there
634
+ # is no concurrent use of the same locals and this is safe.
635
+ def self.tcp_socket_pair
636
+ lsock = nil
637
+ rsock = nil
638
+ laddr = '127.0.0.1'
639
+ lport = 0
640
+ threads = []
641
+ mutex = ::Mutex.new
642
+
643
+ threads << Rex::ThreadFactory.spawn('TcpSocketPair', false) {
644
+ server = nil
645
+ mutex.synchronize {
646
+ threads << Rex::ThreadFactory.spawn('TcpSocketPairClient', false) {
647
+ mutex.synchronize {
648
+ rsock = ::TCPSocket.new( laddr, lport )
649
+ }
650
+ }
651
+ server = ::TCPServer.new(laddr, 0)
652
+ if (server.getsockname =~ /127\.0\.0\.1:/)
653
+ # JRuby ridiculousness
654
+ caddr, lport = server.getsockname.split(":")
655
+ caddr = caddr[1,caddr.length]
656
+ lport = lport.to_i
657
+ else
658
+ # Sane implementations where Socket#getsockname returns a
659
+ # sockaddr
660
+ lport, caddr = ::Socket.unpack_sockaddr_in( server.getsockname )
661
+ end
662
+ }
663
+ lsock, _ = server.accept
664
+ server.close
665
+ }
666
+
667
+ threads.each { |t| t.join }
668
+
669
+ return [lsock, rsock]
670
+ end
671
+
672
+ #
673
+ # Create a UDP socket pair using native ruby UDP sockets.
674
+ #
675
+ def self.udp_socket_pair
676
+ laddr = '127.0.0.1'
677
+
678
+ lsock = ::UDPSocket.new
679
+ lsock.bind( laddr, 0 )
680
+
681
+ rsock = ::UDPSocket.new
682
+ rsock.bind( laddr, 0 )
683
+
684
+ rsock.connect( *lsock.addr.values_at(3,1) )
685
+
686
+ lsock.connect( *rsock.addr.values_at(3,1) )
687
+
688
+ return [lsock, rsock]
689
+ end
690
+
691
+
692
+ ##
693
+ #
694
+ # Class initialization
695
+ #
696
+ ##
697
+
698
+ #
699
+ # Initialize general socket parameters.
700
+ #
701
+ def initsock(params = nil)
702
+ if (params)
703
+ self.peerhost = params.peerhost
704
+ self.peerport = params.peerport
705
+ self.localhost = params.localhost
706
+ self.localport = params.localport
707
+ self.context = params.context || {}
708
+ self.ipv = params.v6 ? 6 : 4
709
+ end
710
+ end
711
+
712
+ #
713
+ # By default, all sockets are themselves selectable file descriptors.
714
+ #
715
+ def fd
716
+ self
717
+ end
718
+
719
+ #
720
+ # Returns local connection information.
721
+ #
722
+ def getsockname
723
+ Socket.from_sockaddr(super)
724
+ end
725
+
726
+ #
727
+ # Wrapper around getsockname
728
+ #
729
+ def getlocalname
730
+ getsockname
731
+ end
732
+
733
+ #
734
+ # Return peer connection information.
735
+ #
736
+ def getpeername_as_array
737
+ peer_name = nil
738
+ begin
739
+ peer_name = Socket.from_sockaddr(self.getpeername)
740
+ rescue ::Errno::EINVAL => e
741
+ # Ruby's getpeername method may call rb_sys_fail("getpeername(2)")
742
+ elog("#{e.message} (#{e.class})#{e.backtrace * "\n"}\n", 'core', LEV_3)
743
+ end
744
+
745
+ return peer_name
746
+ end
747
+
748
+ #
749
+ # Returns a string that indicates the type of the socket, such as 'tcp'.
750
+ #
751
+ def type?
752
+ raise NotImplementedError, "Socket type is not supported."
753
+ end
754
+
755
+ #
756
+ # The peer host of the connected socket.
757
+ #
758
+ attr_reader :peerhost
759
+ #
760
+ # The peer port of the connected socket.
761
+ #
762
+ attr_reader :peerport
763
+ #
764
+ # The local host of the connected socket.
765
+ #
766
+ attr_reader :localhost
767
+ #
768
+ # The local port of the connected socket.
769
+ #
770
+ attr_reader :localport
771
+ #
772
+ # The IP version of the socket
773
+ #
774
+ attr_reader :ipv
775
+ #
776
+ # Contextual information that describes the source and other
777
+ # instance-specific attributes. This comes from the param.context
778
+ # attribute.
779
+ #
780
+ attr_reader :context
781
+
782
+ protected
783
+
784
+ attr_writer :peerhost, :peerport, :localhost, :localport # :nodoc:
785
+ attr_writer :context # :nodoc:
786
+ attr_writer :ipv # :nodoc:
787
+
788
+ end
789
+
790
+ end
791
+
792
+ #
793
+ # Globalized socket constants
794
+ #
795
+ SHUT_RDWR = ::Socket::SHUT_RDWR
796
+ SHUT_RD = ::Socket::SHUT_RD
797
+ SHUT_WR = ::Socket::SHUT_WR
798
+