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.
@@ -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