Small SOCKSv4-implementation - comments / feedback appreciated

Discussion in 'Ruby' started by Jørgen P. Tjernø, Oct 5, 2006.

  1. After having struggeled with libsock/ruby-sockets+socks, etc, and not
    getting any way, I decided to try another approach. The whole
    ruby-builting-socks / libsock idea is to make it as transparent / global
    as possible, seemingly never meant to be controlled directly by the
    application.

    So, I implemented my own little TCPSocket-"wrapper"-class, which just
    speaks the one command from SOCKSv4 and which only understands the one
    reply. After that, it lets go of the socket. (SOCKS protocol information
    found on http://en.wikipedia.org/wiki/SOCKS)

    Then I just overrode Net::FTP#open_socket, so if ENV['SOCKS_SERVER'] is
    set, it uses this my little class to set up a socks-connection. This can
    of course be used for anything, not just Net::FTP - any other
    Net::-module, or a custom creation of yours. Net::FTP just happens to be
    what I wanted it for. ;-)

    Now, I'm curious what you think of the code as a whole (coding style,
    language use, approach, how to "hook into" Net::FTP, etc), and if you
    have any suggestions. I'm thinking I should release this as public
    domain, so anyone else in my situation won't have to bark up all the
    wrong trees for as long as I did. ;-)

    Thanks in advance!

    Kindest regards, Jørgen P. Tjernø.

    * code follows:

    require 'socket'
    require 'resolv'

    require 'net/ftp'

    class SocksProxying
    REQUEST_GRANTED = 0x5A
    REQUEST_FAILED = 0x5B
    REQUEST_FAILED_NOIDENT = 0x5C
    REQUEST_FAILED_IDENTINVALID = 0x5D

    SOCKS_VERSION_4 = 0x04
    SOCKS_CMD_CONNECTION = 0x01
    SOCKS_CMD_BINDING = 0x02

    SOCKS_REPLY_LENGTH = 8

    def initialize
    @socket = nil
    end

    def connect_via(proxy, proxy_port = 1080)
    @socket = TCPSocket.open(proxy, proxy_port)
    end

    def connect(host, port, user = ENV['USER'])
    if @socket.nil?
    raise "Error: Must connect_via first, then connect."
    end

    ip = Resolv.getaddress(host).split(/\./).collect {|part| part.to_i}
    port = port.to_i

    data = [SOCKS_VERSION_4, SOCKS_CMD_CONNECTION, port, ip,
    user].flatten.pack('CCnC4a' + (user.length + 1).to_s)
    @socket.write data
    dud, statuscode, port, ip =
    @socket.recv(SOCKS_REPLY_LENGTH).unpack('CCnC4')
    if statuscode != REQUEST_GRANTED
    if statuscode == REQUEST_FAILED
    raise "Error: SOCKS-access not granted by SOCKS-server."
    elsif statuscode == REQUEST_FAILED_NOIDENT
    raise "Error: SOCKS-connection failed; server could not
    reach our ident server."
    elsif statuscode == REQUEST_FAILED_IDENTINVALID
    raise "Error: SOCKS-connection failed; server could not
    match our ident reply to the one sent."
    else
    raise "Error: SOCKS-connection failed; unknown error-code."
    end
    end

    @socket
    end
    end

    # Here is where we override Net::FTP's internal socket creation.
    class Net::FTP
    def open_socket(host, port)
    if ENV["SOCKS_SERVER"]
    @passive = true
    proxy_host, proxy_port = ENV["SOCKS_SERVER"].split /:/
    sp = SocksProxying.new
    sp.connect_via(proxy_host, proxy_port)
    return sp.connect(host, port)
    else
    return TCPSocket.open(host, port)
    end
    end
    end
     
    Jørgen P. Tjernø, Oct 5, 2006
    #1
    1. Advertisements

  2. I should of course mention that I plan on implementing SOCKSv4A (it's a
    simple and useful addition), but not SOCKSv5 - someone else is of course
    free to do so if they please. :)

    Kindest regards, Jørgen P. Tjernø.
     
    Jørgen P. Tjernø, Oct 5, 2006
    #2
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.