Class: Rex::Proto::Proxy::Socks4a::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/proto/proxy/socks4a.rb

Overview

A client connected to the Socks4a server.

Defined Under Namespace

Modules: Relay Classes: Packet

Constant Summary collapse

REQUEST_VERSION =
4
REPLY_VERSION =
0
COMMAND_CONNECT =
1
COMMAND_BIND =
2
REQUEST_GRANTED =
90
REQUEST_REJECT_FAILED =
91
REQUEST_REJECT_CONNECT =
92
REQUEST_REJECT_USERID =
93
HOST =
1
PORT =
2

Instance Method Summary collapse

Constructor Details

#initialize(server, sock) ⇒ Client

Create a new client connected to the server.



222
223
224
225
226
227
228
# File 'lib/rex/proto/proxy/socks4a.rb', line 222

def initialize( server, sock )
  @server        = server
  @lsock         = sock
  @rsock         = nil
  @client_thread = nil
  @mutex         = ::Mutex.new
end

Instance Method Details

#startObject

Start handling the client connection.



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/rex/proto/proxy/socks4a.rb', line 233

def start
  # create a thread to handle this client request so as to not block the socks4a server
  @client_thread = Rex::ThreadFactory.spawn("SOCKS4AProxyClient", false) do
    begin
      @server.add_client( self )
      # get the initial client request packet
      request = Packet.recv( @lsock )
      raise "Invalid Socks4 request packet received." if not request
      # handle the request
      begin
        # handle socks4a connect requests
        if( request.is_connect? )
          # perform the connection request
          params = {
            'PeerHost' => request.dest_ip,
            'PeerPort' => request.dest_port,
          }
          params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')

          @rsock = Rex::Socket::Tcp.create( params )
          # and send back success to the client
          response         = Packet.new
          response.version = REPLY_VERSION
          response.command = REQUEST_GRANTED
          @lsock.put( response.to_r )
        # handle socks4a bind requests
        elsif( request.is_bind? )
          # create a server socket for this request
          params = {
            'LocalHost' => '0.0.0.0',
            'LocalPort' => 0,
          }
          params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')
          bsock = Rex::Socket::TcpServer.create( params )
          # send back the bind success to the client
          response           = Packet.new
          response.version   = REPLY_VERSION
          response.command   = REQUEST_GRANTED
          response.dest_ip   = '0.0.0.0'
          response.dest_port = bsock.getlocalname()[PORT]
          @lsock.put( response.to_r )
          # accept a client connection (2 minute timeout as per spec)
          begin
            ::Timeout.timeout( 120 ) do
              @rsock = bsock.accept
            end
          rescue ::Timeout::Error
            raise "Timeout reached on accept request."
          end
          # close the listening socket
          bsock.close
          # verify the connection is from the dest_ip originally specified by the client
          rpeer = @rsock.getpeername_as_array
          raise "Got connection from an invalid peer." if( rpeer[HOST] != request.dest_ip )
          # send back the client connect success to the client
          #
          # sf: according to the spec we send this response back to the client, however
          #     I have seen some clients who bawk if they get this second response.
          #
          response           = Packet.new
          response.version   = REPLY_VERSION
          response.command   = REQUEST_GRANTED
          response.dest_ip   = rpeer[HOST]
          response.dest_port = rpeer[PORT]
          @lsock.put( response.to_r )
        else
          raise "Unknown request command received #{request.command} received."
        end
      rescue
        # send back failure to the client
        response         = Packet.new
        response.version = REPLY_VERSION
        response.command = REQUEST_REJECT_FAILED
        @lsock.put( response.to_r )
        # raise an exception to close this client connection
        raise "Failed to handle the clients request."
      end
      # setup the two way relay for full duplex io
      @lsock.extend( Relay )
      @rsock.extend( Relay )
      # start the socket relays...
      @lsock.relay( self, @rsock )
      @rsock.relay( self, @lsock )
    rescue
      wlog( "Client.start - #{$!}" )
      self.stop
    end
  end
end

#stopObject

Stop handling the client connection.



326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/rex/proto/proxy/socks4a.rb', line 326

def stop
  @mutex.synchronize do
    if( not @closed )

      begin
        @lsock.close if @lsock
      rescue
      end

      begin
        @rsock.close if @rsock
      rescue
      end

      @client_thread.kill if( @client_thread and @client_thread.alive? )

      @server.remove_client( self )

      @closed = true
    end
  end
end