Class: Zokor::ProxyConnection

Inherits:
Object
  • Object
show all
Defined in:
lib/zokor/proxy_connection.rb

Constant Summary collapse

BlockSize =
1024 * 4
BUILTIN_CA_FILE =
File.join(File.dirname(__FILE__), '..', '..',
'ca-certs-small.crt')

Instance Method Summary collapse

Constructor Details

#initialize(local_socket, remote_host, remote_port, opts = {}) ⇒ ProxyConnection

Create a new connection object to wrap a local client connection and ferry packets through the proxies.

Parameters:

  • local_socket (TCPSocket)

    The local inbound connection.

  • remote_host (String)
  • remote_port (Integer)
  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :proxy_url (String)

    Intermediate proxy URL to connect through.

  • :use_ssl (Boolean)

    Whether to use SSL/TLS for the external proxy connection

  • :ssl_opts (Hash)

    A hash of SSL options to pass to #create_ssl_socket. Supports some custom options like :key_file and :cert_file (override :key and :cert). Pass :ca_file => :builtin to use the CA bundle that ships with this library.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/zokor/proxy_connection.rb', line 30

def initialize(local_socket, remote_host, remote_port, opts={})
  @local_socket = local_socket
  @remote_host = remote_host
  @remote_port = remote_port

  @proxy_url = opts[:proxy_url]
  @use_ssl = opts[:use_ssl]
  @ssl_opts = opts.fetch(:ssl_opts, {})

  # process ssl_opts
  if @ssl_opts[:ca_file] == :builtin || @ssl_opts[:ca_file] == ':builtin'
    log.debug('Using built-in CA file')
    @ssl_opts[:ca_file] = BUILTIN_CA_FILE
  end

  key_file = @ssl_opts.delete(:key_file)
  if key_file
    # TODO: support other keys besides RSA
    @ssl_opts[:key] = OpenSSL::PKey::RSA.new(File.open(key_file))
  end

  cert_file = @ssl_opts.delete(:cert_file)
  if cert_file
    @ssl_opts[:cert] = OpenSSL::X509::Certificate.new(File.open(cert_file))
  end

  log.info('new local connection')
end

Instance Method Details

#connectObject

Connect to the proxies and begin ferrying packets. This method will loop indefinitely until the connection is closed by client or server.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/zokor/proxy_connection.rb', line 61

def connect

  local = @local_socket

  # open connection to remote server
  remote = create_outbound_tcp_socket

  # SSL remote main loop
  loop do
    log.debug('IO.select()')
    read_set = [local, remote]
    if remote.is_a?(OpenSSL::SSL::SSLSocket)
      # TODO: determine whether this is needed
      read_set << remote.io
    end

    rd_ready, _, _ = IO.select(read_set, nil, nil, 2)

    if rd_ready.nil?
      log.chunder('select TIMEOUT')
      next
    end

    log.chunder {'read ready: ' + rd_ready.inspect}

    if rd_ready.include?(local)
      data = local.recv(BlockSize)
      if data.empty?
        log.info('Local end closed connection')
        return
      end
      log.debug("=> #{data.length} bytes to remote")
      socket_write(remote, data)
      log.chunder('writen')
    end
    if rd_ready.include?(remote)
      while true
        data = socket_read(remote, BlockSize)
        if data.empty?
          log.info('Remote end closed connection')
          return
        end
        log.debug("<= #{data.length} bytes from remote")
        local.write(data)
        log.chunder('written')

        if data.length < BlockSize
          log.chunder("data.length < blocksize, done")
          break
        else
          log.chunder("data.length >= blocksize, continuing")
        end
      end
    end
  end

rescue Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EPIPE, EOFError => err
  log.warn(err.inspect)

ensure
  local.close if local && !local.closed?
  remote.close if remote && !remote.closed?

  log.info('Connection closed')
end

#to_sObject



127
128
129
# File 'lib/zokor/proxy_connection.rb', line 127

def to_s
  "<#{self.class.name} to #{label}>"
end