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,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