rex-socket 0.1.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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/README.md +32 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rex/socket.rb +798 -0
- data/lib/rex/socket/comm.rb +120 -0
- data/lib/rex/socket/comm/local.rb +529 -0
- data/lib/rex/socket/ip.rb +132 -0
- data/lib/rex/socket/parameters.rb +372 -0
- data/lib/rex/socket/range_walker.rb +470 -0
- data/lib/rex/socket/ssh_factory.rb +46 -0
- data/lib/rex/socket/ssl_tcp.rb +374 -0
- data/lib/rex/socket/ssl_tcp_server.rb +220 -0
- data/lib/rex/socket/subnet_walker.rb +76 -0
- data/lib/rex/socket/switch_board.rb +289 -0
- data/lib/rex/socket/tcp.rb +79 -0
- data/lib/rex/socket/tcp_server.rb +70 -0
- data/lib/rex/socket/udp.rb +165 -0
- data/lib/rex/socket/version.rb +5 -0
- data/lib/rex/socket/x509_certificate.rb +92 -0
- data/rex-socket.gemspec +30 -0
- metadata +205 -0
- metadata.gz.sig +2 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
require 'rex/socket'
|
3
|
+
|
4
|
+
module Rex
|
5
|
+
module Socket
|
6
|
+
|
7
|
+
###
|
8
|
+
#
|
9
|
+
# This mixin provides the basic interface that a derived class must implement
|
10
|
+
# in order to be a compatible comm class. The base comm class also supports
|
11
|
+
# registering event handlers that can be notified when sockets are being
|
12
|
+
# created and have been created. This allows code to extend sockets on
|
13
|
+
# creation from the single point that they are created.
|
14
|
+
#
|
15
|
+
###
|
16
|
+
module Comm
|
17
|
+
|
18
|
+
###
|
19
|
+
#
|
20
|
+
# This mixin provides stubs for event notification handlers that can be
|
21
|
+
# registered with a Comm factory to be called when various events occur,
|
22
|
+
# such as socket instantiation.
|
23
|
+
#
|
24
|
+
###
|
25
|
+
module Events
|
26
|
+
|
27
|
+
#
|
28
|
+
# This callback is notified when a socket is being created and is passed
|
29
|
+
# the parameters that will be used to create it.
|
30
|
+
#
|
31
|
+
def on_before_socket_create(comm, param)
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# This callback is notified when a new socket is created and the
|
36
|
+
# parameters that were used to create it. This provides the callback
|
37
|
+
# with a chance to extend or otherwise modify the socket before it's
|
38
|
+
# passed on to the actual requestor.
|
39
|
+
#
|
40
|
+
def on_socket_created(comm, sock, param)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Creates a compatible socket based on the supplied uniform parameters.
|
47
|
+
#
|
48
|
+
def self.create(param)
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Indicates whether or not this comm can be chained with other chainable
|
54
|
+
# comms. This is particularly important for things like Proxy Comms that
|
55
|
+
# can be proxied through one another. The semantics of this are currently
|
56
|
+
# undefined and will probably need some more thought.
|
57
|
+
#
|
58
|
+
def chainable?
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Registers an event handler that implements the Rex::Socket::Comm::Event
|
64
|
+
# interface in at least some fashion. Event handlers are notified when
|
65
|
+
# sockets are created through the Comm instance that they register against.
|
66
|
+
#
|
67
|
+
def register_event_handler(handler)
|
68
|
+
if (handlers == nil)
|
69
|
+
self.handlers = []
|
70
|
+
end
|
71
|
+
|
72
|
+
self.handlers << handler
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Deregisters a previously registered event handler.
|
77
|
+
#
|
78
|
+
def deregister_event_handler(handler)
|
79
|
+
if (handlers)
|
80
|
+
handlers.delete(handler)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Enumerates each registered event handler so that they can be notified of
|
86
|
+
# an event.
|
87
|
+
#
|
88
|
+
def each_event_handler(&block)
|
89
|
+
if (handlers)
|
90
|
+
handlers.each(&block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Notifies handlers of the before socket create event.
|
96
|
+
#
|
97
|
+
def notify_before_socket_create(comm, param)
|
98
|
+
each_event_handler() { |handler|
|
99
|
+
handler.on_before_socket_create(comm, param)
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Notifies handlers of the socket created event.
|
105
|
+
#
|
106
|
+
def notify_socket_created(comm, sock, param)
|
107
|
+
each_event_handler() { |handler|
|
108
|
+
handler.on_socket_created(comm, sock, param)
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
|
114
|
+
attr_accessor :handlers # :nodoc:
|
115
|
+
attr_accessor :handlers_rwlock # :nodoc:
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,529 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
require 'singleton'
|
3
|
+
require 'rex/socket'
|
4
|
+
require 'rex/socket/tcp'
|
5
|
+
require 'rex/socket/ssl_tcp'
|
6
|
+
require 'rex/socket/ssl_tcp_server'
|
7
|
+
require 'rex/socket/udp'
|
8
|
+
require 'rex/socket/ip'
|
9
|
+
require 'timeout'
|
10
|
+
|
11
|
+
###
|
12
|
+
#
|
13
|
+
# Local communication class factory.
|
14
|
+
#
|
15
|
+
###
|
16
|
+
class Rex::Socket::Comm::Local
|
17
|
+
|
18
|
+
include Singleton
|
19
|
+
include Rex::Socket::Comm
|
20
|
+
|
21
|
+
#
|
22
|
+
# Creates an instance of a socket using the supplied parameters.
|
23
|
+
#
|
24
|
+
def self.create(param)
|
25
|
+
|
26
|
+
# Work around jRuby socket implementation issues
|
27
|
+
if(RUBY_PLATFORM == 'java')
|
28
|
+
return self.create_jruby(param)
|
29
|
+
end
|
30
|
+
|
31
|
+
case param.proto
|
32
|
+
when 'tcp'
|
33
|
+
return create_by_type(param, ::Socket::SOCK_STREAM, ::Socket::IPPROTO_TCP)
|
34
|
+
when 'udp'
|
35
|
+
return create_by_type(param, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP)
|
36
|
+
when 'ip'
|
37
|
+
return create_ip(param)
|
38
|
+
else
|
39
|
+
raise Rex::UnsupportedProtocol.new(param.proto), caller
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Creates an instance of a socket using the supplied parameters.
|
45
|
+
# Use various hacks to make this work with jRuby
|
46
|
+
#
|
47
|
+
def self.create_jruby(param)
|
48
|
+
sock = nil
|
49
|
+
|
50
|
+
# Notify handlers of the before socket create event.
|
51
|
+
self.instance.notify_before_socket_create(self, param)
|
52
|
+
|
53
|
+
case param.proto
|
54
|
+
when 'tcp'
|
55
|
+
if (param.server?)
|
56
|
+
sock = TCPServer.new(param.localport, param.localhost)
|
57
|
+
klass = Rex::Socket::TcpServer
|
58
|
+
if (param.ssl)
|
59
|
+
klass = Rex::Socket::SslTcpServer
|
60
|
+
end
|
61
|
+
sock.extend(klass)
|
62
|
+
|
63
|
+
else
|
64
|
+
sock = TCPSocket.new(param.peerhost, param.peerport)
|
65
|
+
klass = Rex::Socket::Tcp
|
66
|
+
if (param.ssl)
|
67
|
+
klass = Rex::Socket::SslTcp
|
68
|
+
end
|
69
|
+
sock.extend(klass)
|
70
|
+
end
|
71
|
+
when 'udp'
|
72
|
+
if (param.server?)
|
73
|
+
sock = UDPServer.new(param.localport, param.localhost)
|
74
|
+
klass = Rex::Socket::UdpServer
|
75
|
+
sock.extend(klass)
|
76
|
+
else
|
77
|
+
sock = UDPSocket.new(param.peerhost, param.peerport)
|
78
|
+
klass = Rex::Socket::Udp
|
79
|
+
sock.extend(klass)
|
80
|
+
end
|
81
|
+
else
|
82
|
+
raise Rex::UnsupportedProtocol.new(param.proto), caller
|
83
|
+
end
|
84
|
+
|
85
|
+
sock.initsock(param)
|
86
|
+
self.instance.notify_socket_created(self, sock, param)
|
87
|
+
return sock
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
#
|
92
|
+
# Creates a raw IP socket using the supplied Parameter instance.
|
93
|
+
# Special-cased because of how different it is from UDP/TCP
|
94
|
+
#
|
95
|
+
def self.create_ip(param)
|
96
|
+
self.instance.notify_before_socket_create(self, param)
|
97
|
+
|
98
|
+
sock = ::Socket.open(::Socket::PF_INET, ::Socket::SOCK_RAW, ::Socket::IPPROTO_RAW)
|
99
|
+
sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_HDRINCL, 1)
|
100
|
+
|
101
|
+
# Configure broadcast support
|
102
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BROADCAST, true)
|
103
|
+
|
104
|
+
if (param.bare? == false)
|
105
|
+
sock.extend(::Rex::Socket::Ip)
|
106
|
+
sock.initsock(param)
|
107
|
+
end
|
108
|
+
|
109
|
+
self.instance.notify_socket_created(self, sock, param)
|
110
|
+
|
111
|
+
sock
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
#
|
116
|
+
# Creates a socket using the supplied Parameter instance.
|
117
|
+
#
|
118
|
+
def self.create_by_type(param, type, proto = 0)
|
119
|
+
|
120
|
+
# Whether to use IPv6 addressing
|
121
|
+
usev6 = false
|
122
|
+
|
123
|
+
# Detect IPv6 addresses and enable IPv6 accordingly
|
124
|
+
if ( Rex::Socket.support_ipv6?())
|
125
|
+
|
126
|
+
# Allow the caller to force IPv6
|
127
|
+
if (param.v6)
|
128
|
+
usev6 = true
|
129
|
+
end
|
130
|
+
|
131
|
+
# Force IPv6 mode for non-connected UDP sockets
|
132
|
+
if (type == ::Socket::SOCK_DGRAM and not param.peerhost)
|
133
|
+
# FreeBSD allows IPv6 socket creation, but throws an error on sendto()
|
134
|
+
# Windows 7 SP1 and newer also fail to sendto with IPv6 udp sockets
|
135
|
+
unless Rex::Compat.is_freebsd or Rex::Compat.is_windows
|
136
|
+
usev6 = true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
local = Rex::Socket.resolv_nbo(param.localhost) if param.localhost
|
141
|
+
peer = Rex::Socket.resolv_nbo(param.peerhost) if param.peerhost
|
142
|
+
|
143
|
+
if (local and local.length == 16)
|
144
|
+
usev6 = true
|
145
|
+
end
|
146
|
+
|
147
|
+
if (peer and peer.length == 16)
|
148
|
+
usev6 = true
|
149
|
+
end
|
150
|
+
|
151
|
+
if (usev6)
|
152
|
+
if (local and local.length == 4)
|
153
|
+
if (local == "\x00\x00\x00\x00")
|
154
|
+
param.localhost = '::'
|
155
|
+
elsif (local == "\x7f\x00\x00\x01")
|
156
|
+
param.localhost = '::1'
|
157
|
+
else
|
158
|
+
param.localhost = '::ffff:' + Rex::Socket.getaddress(param.localhost, true)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
if (peer and peer.length == 4)
|
163
|
+
if (peer == "\x00\x00\x00\x00")
|
164
|
+
param.peerhost = '::'
|
165
|
+
elsif (peer == "\x7f\x00\x00\x01")
|
166
|
+
param.peerhost = '::1'
|
167
|
+
else
|
168
|
+
param.peerhost = '::ffff:' + Rex::Socket.getaddress(param.peerhost, true)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
param.v6 = true
|
173
|
+
end
|
174
|
+
else
|
175
|
+
# No IPv6 support
|
176
|
+
param.v6 = false
|
177
|
+
end
|
178
|
+
|
179
|
+
# Notify handlers of the before socket create event.
|
180
|
+
self.instance.notify_before_socket_create(self, param)
|
181
|
+
|
182
|
+
# Create the socket
|
183
|
+
sock = nil
|
184
|
+
if (param.v6)
|
185
|
+
sock = ::Socket.new(::Socket::AF_INET6, type, proto)
|
186
|
+
else
|
187
|
+
sock = ::Socket.new(::Socket::AF_INET, type, proto)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Bind to a given local address and/or port if they are supplied
|
191
|
+
if param.localport or param.localhost
|
192
|
+
begin
|
193
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, true)
|
194
|
+
sock.bind(Rex::Socket.to_sockaddr(param.localhost, param.localport))
|
195
|
+
|
196
|
+
rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE
|
197
|
+
sock.close
|
198
|
+
raise Rex::BindFailed.new(param.localhost, param.localport), caller
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Configure broadcast support for all datagram sockets
|
203
|
+
if (type == ::Socket::SOCK_DGRAM)
|
204
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BROADCAST, true)
|
205
|
+
end
|
206
|
+
|
207
|
+
# If a server TCP instance is being created...
|
208
|
+
if (param.server?)
|
209
|
+
sock.listen(256)
|
210
|
+
|
211
|
+
if (param.bare? == false)
|
212
|
+
klass = Rex::Socket::TcpServer
|
213
|
+
if (param.ssl)
|
214
|
+
klass = Rex::Socket::SslTcpServer
|
215
|
+
end
|
216
|
+
sock.extend(klass)
|
217
|
+
|
218
|
+
sock.initsock(param)
|
219
|
+
end
|
220
|
+
# Otherwise, if we're creating a client...
|
221
|
+
else
|
222
|
+
chain = []
|
223
|
+
|
224
|
+
# If we were supplied with host information
|
225
|
+
if (param.peerhost)
|
226
|
+
|
227
|
+
# A flag that indicates whether we need to try multiple scopes
|
228
|
+
retry_scopes = false
|
229
|
+
|
230
|
+
# Always retry with link-local IPv6 addresses
|
231
|
+
if Rex::Socket.is_ipv6?( param.peerhost ) and param.peerhost =~ /^fe80::/
|
232
|
+
retry_scopes = true
|
233
|
+
end
|
234
|
+
|
235
|
+
# Prepare a list of scope IDs to try when connecting to
|
236
|
+
# link-level addresses. Read from /proc if it is available,
|
237
|
+
# otherwise increment through the first 255 IDs.
|
238
|
+
@@ip6_lla_scopes ||= []
|
239
|
+
|
240
|
+
if @@ip6_lla_scopes.length == 0 and retry_scopes
|
241
|
+
|
242
|
+
# Linux specific interface lookup code
|
243
|
+
if ::File.exist?( "/proc/self/net/igmp6" )
|
244
|
+
::File.open("/proc/self/net/igmp6") do |fd|
|
245
|
+
fd.each_line do |line|
|
246
|
+
line = line.strip
|
247
|
+
tscope, tint, junk = line.split(/\s+/, 3)
|
248
|
+
next if not tint
|
249
|
+
|
250
|
+
# Specifying lo in any connect call results in the socket
|
251
|
+
# being unusable, even if the correct interface is set.
|
252
|
+
next if tint == "lo"
|
253
|
+
|
254
|
+
@@ip6_lla_scopes << tscope
|
255
|
+
end
|
256
|
+
end
|
257
|
+
else
|
258
|
+
# Other Unix-like platforms should support a raw scope ID
|
259
|
+
[*(1 .. 255)].map{ |x| @@ip6_lla_scopes << x.to_s }
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
ip6_scope_idx = 0
|
264
|
+
ip = param.peerhost
|
265
|
+
port = param.peerport
|
266
|
+
|
267
|
+
if param.proxies
|
268
|
+
chain = param.proxies.dup
|
269
|
+
chain.push(['host',param.peerhost,param.peerport])
|
270
|
+
ip = chain[0][1]
|
271
|
+
port = chain[0][2].to_i
|
272
|
+
end
|
273
|
+
|
274
|
+
begin
|
275
|
+
|
276
|
+
begin
|
277
|
+
Timeout.timeout(param.timeout) do
|
278
|
+
sock.connect(Rex::Socket.to_sockaddr(ip, port))
|
279
|
+
end
|
280
|
+
rescue ::Timeout::Error
|
281
|
+
raise ::Errno::ETIMEDOUT
|
282
|
+
end
|
283
|
+
|
284
|
+
rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::ENOPROTOOPT
|
285
|
+
|
286
|
+
# Rescue errors caused by a bad Scope ID for a link-local address
|
287
|
+
if retry_scopes and @@ip6_lla_scopes[ ip6_scope_idx ]
|
288
|
+
ip = param.peerhost + "%" + @@ip6_lla_scopes[ ip6_scope_idx ]
|
289
|
+
ip6_scope_idx += 1
|
290
|
+
retry
|
291
|
+
end
|
292
|
+
|
293
|
+
sock.close
|
294
|
+
raise Rex::HostUnreachable.new(ip, port), caller
|
295
|
+
|
296
|
+
rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE
|
297
|
+
sock.close
|
298
|
+
raise Rex::InvalidDestination.new(ip, port), caller
|
299
|
+
|
300
|
+
rescue Errno::ETIMEDOUT
|
301
|
+
sock.close
|
302
|
+
raise Rex::ConnectionTimeout.new(ip, port), caller
|
303
|
+
|
304
|
+
rescue ::Errno::ECONNRESET,::Errno::ECONNREFUSED,::Errno::ENOTCONN,::Errno::ECONNABORTED
|
305
|
+
sock.close
|
306
|
+
# Report the actual thing we were trying to connect to here, not
|
307
|
+
# param.peerhost, since that's the eventual target at the end of the
|
308
|
+
# proxy chain
|
309
|
+
raise Rex::ConnectionRefused.new(ip, port.to_i), caller
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
if (param.bare? == false)
|
314
|
+
case param.proto
|
315
|
+
when 'tcp'
|
316
|
+
klass = Rex::Socket::Tcp
|
317
|
+
sock.extend(klass)
|
318
|
+
sock.initsock(param)
|
319
|
+
when 'udp'
|
320
|
+
sock.extend(Rex::Socket::Udp)
|
321
|
+
sock.initsock(param)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
if chain.size > 1
|
326
|
+
chain.each_with_index {
|
327
|
+
|proxy, i|
|
328
|
+
next_hop = chain[i + 1]
|
329
|
+
if next_hop
|
330
|
+
proxy(sock, proxy[0], next_hop[1], next_hop[2])
|
331
|
+
end
|
332
|
+
}
|
333
|
+
end
|
334
|
+
|
335
|
+
# Now extend the socket with SSL and perform the handshake
|
336
|
+
if(param.bare? == false and param.ssl)
|
337
|
+
klass = Rex::Socket::SslTcp
|
338
|
+
sock.extend(klass)
|
339
|
+
sock.initsock(param)
|
340
|
+
end
|
341
|
+
|
342
|
+
|
343
|
+
end
|
344
|
+
|
345
|
+
# Notify handlers that a socket has been created.
|
346
|
+
self.instance.notify_socket_created(self, sock, param)
|
347
|
+
|
348
|
+
sock
|
349
|
+
end
|
350
|
+
|
351
|
+
def self.proxy(sock, type, host, port)
|
352
|
+
case type.downcase
|
353
|
+
when 'sapni'
|
354
|
+
packet_type = 'NI_ROUTE'
|
355
|
+
route_info_version = 2
|
356
|
+
ni_version = 39
|
357
|
+
num_of_entries = 2
|
358
|
+
talk_mode = 1 # ref: http://help.sap.com/saphelp_dimp50/helpdata/En/f8/bb960899d743378ccb8372215bb767/content.htm
|
359
|
+
num_rest_nodes = 1
|
360
|
+
|
361
|
+
_af, shost, sport = sock.getpeername_as_array
|
362
|
+
first_route_item = [shost, 0, sport, 0, 0].pack("A*CA*cc")
|
363
|
+
route_data = [first_route_item.length, first_route_item].pack("NA*")
|
364
|
+
route_data << [host, 0, port.to_s, 0, 0].pack("A*CA*cc")
|
365
|
+
|
366
|
+
ni_packet = [
|
367
|
+
packet_type,
|
368
|
+
0,
|
369
|
+
route_info_version,
|
370
|
+
ni_version,
|
371
|
+
num_of_entries,
|
372
|
+
talk_mode,
|
373
|
+
0,
|
374
|
+
0,
|
375
|
+
num_rest_nodes
|
376
|
+
].pack("A8c8")
|
377
|
+
# Add the data block, according to sap documentation:
|
378
|
+
# A 4-byte header precedes each data block. These 4 bytes give the
|
379
|
+
# length of the data block (length without leading 4 bytes)
|
380
|
+
# The data block (the route data)
|
381
|
+
ni_packet << [route_data.length - 4].pack('N') + route_data
|
382
|
+
# Now that we've built the whole packet, prepend its length before writing it to the wire
|
383
|
+
ni_packet = [ni_packet.length].pack('N') + ni_packet
|
384
|
+
|
385
|
+
size = sock.put(ni_packet)
|
386
|
+
|
387
|
+
if size != ni_packet.length
|
388
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
389
|
+
end
|
390
|
+
|
391
|
+
begin
|
392
|
+
ret_len = sock.get_once(4, 30).unpack('N')[0]
|
393
|
+
if ret_len and ret_len != 0
|
394
|
+
ret = sock.get_once(ret_len, 30)
|
395
|
+
end
|
396
|
+
rescue IOError
|
397
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
398
|
+
end
|
399
|
+
|
400
|
+
if ret and ret.length < 4
|
401
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
|
402
|
+
end
|
403
|
+
|
404
|
+
if ret =~ /NI_RTERR/
|
405
|
+
case ret
|
406
|
+
when /timed out/
|
407
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to remote host #{host} timed out")
|
408
|
+
when /refused/
|
409
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to remote port #{port} closed")
|
410
|
+
when /denied/
|
411
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} blocked by ACL")
|
412
|
+
else
|
413
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} failed (Unknown fail)")
|
414
|
+
end
|
415
|
+
elsif ret =~ /NI_PONG/
|
416
|
+
# success case
|
417
|
+
# would like to print this "[*] remote native connection to #{host}:#{port} established\n"
|
418
|
+
else
|
419
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} failed (Unknown fail)")
|
420
|
+
end
|
421
|
+
|
422
|
+
when 'http'
|
423
|
+
setup = "CONNECT #{host}:#{port} HTTP/1.0\r\n\r\n"
|
424
|
+
size = sock.put(setup)
|
425
|
+
if (size != setup.length)
|
426
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
427
|
+
end
|
428
|
+
|
429
|
+
begin
|
430
|
+
ret = sock.get_once(39,30)
|
431
|
+
rescue IOError
|
432
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
433
|
+
end
|
434
|
+
|
435
|
+
if ret.nil?
|
436
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
437
|
+
end
|
438
|
+
|
439
|
+
resp = Rex::Proto::Http::Response.new
|
440
|
+
resp.update_cmd_parts(ret.split(/\r?\n/)[0])
|
441
|
+
|
442
|
+
if resp.code != 200
|
443
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "The proxy returned a non-OK response"), caller
|
444
|
+
end
|
445
|
+
when 'socks4'
|
446
|
+
setup = [4,1,port.to_i].pack('CCn') + Socket.gethostbyname(host)[3] + Rex::Text.rand_text_alpha(rand(8)+1) + "\x00"
|
447
|
+
size = sock.put(setup)
|
448
|
+
if (size != setup.length)
|
449
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
450
|
+
end
|
451
|
+
|
452
|
+
begin
|
453
|
+
ret = sock.get_once(8, 30)
|
454
|
+
rescue IOError
|
455
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
456
|
+
end
|
457
|
+
|
458
|
+
if (ret.nil? or ret.length < 8)
|
459
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
|
460
|
+
end
|
461
|
+
if ret[1,1] != "\x5a"
|
462
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{ret[0,1].unpack("C")[0]}"), caller
|
463
|
+
end
|
464
|
+
when 'socks5'
|
465
|
+
auth_methods = [5,1,0].pack('CCC')
|
466
|
+
size = sock.put(auth_methods)
|
467
|
+
if (size != auth_methods.length)
|
468
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
469
|
+
end
|
470
|
+
ret = sock.get_once(2,30)
|
471
|
+
if (ret[1,1] == "\xff")
|
472
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "The proxy requires authentication"), caller
|
473
|
+
end
|
474
|
+
|
475
|
+
if (Rex::Socket.is_ipv4?(host))
|
476
|
+
addr = Rex::Socket.gethostbyname(host)[3]
|
477
|
+
setup = [5,1,0,1].pack('C4') + addr + [port.to_i].pack('n')
|
478
|
+
elsif (Rex::Socket.support_ipv6? and Rex::Socket.is_ipv6?(host))
|
479
|
+
# IPv6 stuff all untested
|
480
|
+
addr = Rex::Socket.gethostbyname(host)[3]
|
481
|
+
setup = [5,1,0,4].pack('C4') + addr + [port.to_i].pack('n')
|
482
|
+
else
|
483
|
+
# Then it must be a domain name.
|
484
|
+
# Unfortunately, it looks like the host has always been
|
485
|
+
# resolved by the time it gets here, so this code never runs.
|
486
|
+
setup = [5,1,0,3].pack('C4') + [host.length].pack('C') + host + [port.to_i].pack('n')
|
487
|
+
end
|
488
|
+
|
489
|
+
size = sock.put(setup)
|
490
|
+
if (size != setup.length)
|
491
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
492
|
+
end
|
493
|
+
|
494
|
+
begin
|
495
|
+
response = sock.get_once(10, 30)
|
496
|
+
rescue IOError
|
497
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
498
|
+
end
|
499
|
+
|
500
|
+
if (response.nil? or response.length < 10)
|
501
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
|
502
|
+
end
|
503
|
+
if response[1,1] != "\x00"
|
504
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{response[1,1].unpack("C")[0]}"), caller
|
505
|
+
end
|
506
|
+
else
|
507
|
+
raise RuntimeError, "The proxy type specified is not valid", caller
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
##
|
512
|
+
#
|
513
|
+
# Registration
|
514
|
+
#
|
515
|
+
##
|
516
|
+
|
517
|
+
def self.register_event_handler(handler) # :nodoc:
|
518
|
+
self.instance.register_event_handler(handler)
|
519
|
+
end
|
520
|
+
|
521
|
+
def self.deregister_event_handler(handler) # :nodoc:
|
522
|
+
self.instance.deregister_event_handler(handler)
|
523
|
+
end
|
524
|
+
|
525
|
+
def self.each_event_handler(handler) # :nodoc:
|
526
|
+
self.instance.each_event_handler(handler)
|
527
|
+
end
|
528
|
+
|
529
|
+
end
|