Skip to content

Instantly share code, notes, and snippets.

@shurikk
Created April 26, 2014 03:22

Revisions

  1. shurikk created this gist Apr 26, 2014.
    80 changes: 80 additions & 0 deletions tcp_socket_proxy_monkey_patch.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,80 @@
    # Usage:
    #
    # TCPSocket.socks_server = "socks-proxy.example.net"
    # TCPSocket.socks_port = 8888

    require 'socket'

    class TCPSocket

    alias :direct_to :initialize

    class << self
    attr_accessor :socks_server, :socks_port
    end

    def initialize(host, port, local_host = nil, local_port = nil)
    socks_server = self.class.socks_server
    socks_port = self.class.socks_port

    if socks_server && socks_port
    direct socks_server, socks_port
    authenticate
    proxy_to(host, port)
    else
    direct_to host, port, local_host, local_port
    end
    end

    def authenticate
    # no authentication
    write "\005\001\000"
    recv(2)
    end

    def proxy_to(host, port)
    ["\005", "\001", "\000"].map {|b| write b }

    if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
    write "\001"
    write [$1.to_i, $2.to_i, $3.to_i, $4.to_i].pack("CCCC")
    elsif host =~ /^[:0-9a-f]+$/
    raise "TCP/IPv6 is not supported via SOCKS"
    else
    write "\003" + [host.size].pack("C") + host
    end

    write [port].pack('n')

    receive_via_proxy
    end

    def receive_via_proxy
    reply = recv(4)

    raise "SOCKS version #{reply[0..0]} is not supported" if reply[0..0] != "\005"
    raise "SOCKS error: #{reply.bytes.to_a.inspect}" if reply[1..1] != "\000"

    bind_addr_len = case reply[3..3]
    when "\001"
    4
    when "\003"
    recv(1).bytes.first
    when "\004"
    16
    else
    raise "SOCKS error: #{reply.bytes.to_a.inspect}"
    end

    bind_addr_s = recv(bind_addr_len)

    bind_addr = case reply[3..3]
    when "\001"
    bind_addr_s.bytes.to_a.join(".")
    when "\003"
    bind_addr_s
    end

    [bind_addr, recv(bind_addr_len + 2).unpack("n")]
    end
    end