Class: FtpProxy::Session
- Inherits:
-
Object
- Object
- FtpProxy::Session
- Defined in:
- lib/ftpproxy.rb
Instance Method Summary collapse
- #cmd_pasv(params) ⇒ Object
- #cmd_port(params) ⇒ Object
- #cmd_user(user) ⇒ Object
-
#initialize(socket, params = {}) ⇒ Session
constructor
A new instance of Session.
- #make_relay(clientdata, mode) ⇒ Object
- #proxy(line) ⇒ Object
- #read_client ⇒ Object
-
#read_server ⇒ Object
handle multiline responses.
-
#relay_cmd ⇒ Object
relay command channel.
- #start ⇒ Object
- #write_client(str) ⇒ Object
- #write_server(str) ⇒ Object
Constructor Details
#initialize(socket, params = {}) ⇒ Session
Returns a new instance of Session.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/ftpproxy.rb', line 51 def initialize socket, params={} @client = socket @server = nil @relay = nil @log = params[:log] @mode = params[:mode] if @proxy = params[:proxy] raise ArgumentError, 'malformed proxy' unless @proxy =~ /^(.*)(?::(\d+))?$/ @proxy = [$1, $2] end rescue @log.error $! raise end |
Instance Method Details
#cmd_pasv(params) ⇒ Object
179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/ftpproxy.rb', line 179 def cmd_pasv params socket = TCPServer.open @server.addr[3], 0 host = socket.addr[3] port = socket.addr[1] @log.debug "Waiting for passive connection from client on #{host}:#{port}" write_client "227 Entering Passive Mode (#{(host.split('.') + port.divmod(256)).join ','})" make_relay socket.accept, :passive rescue @log.error $! write_client '425 Data connection failed' end |
#cmd_port(params) ⇒ Object
165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/ftpproxy.rb', line 165 def cmd_port params nums = params.split(',') raise 'invalid parameters' unless nums.length == 6 host = nums[0, 4].join('.') port = nums[4].to_i * 256 + nums[5].to_i @log.debug "Opening active connection to client on #{host}:#{port}" socket = TCPSocket.open(host, port) write_client "200 Connection established (#{port})" make_relay socket, :active rescue @log.error $! write_client '425 Data connection failed' end |
#cmd_user(user) ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/ftpproxy.rb', line 146 def cmd_user user raise 'malformed user string' unless user =~ /(.+)@(.+?)(?::(\d+))?$/ user, host, port = $1, $2, $3 # this is to double-proxy through an upstream proxy if @proxy user, pass = "#{user}@#{host}#{':' + port.to_s if port}", pass host, port = @proxy end @log.debug "Connecting to #{host}:#{port || 21}" @server = TCPSocket.open host, (port || '21').to_i raise 'invalid server response' unless read_server[0] == ?2 write_server "USER #{user}" rescue @log.error $! write_client '530 Not logged in.' else relay_cmd end |
#make_relay(clientdata, mode) ⇒ Object
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/ftpproxy.rb', line 191 def make_relay clientdata, mode @relay.close if @relay case mode = @mode || mode when :active socket = TCPServer.open @server.addr[3], 0 host = socket.addr[3] port = socket.addr[1] @log.debug "Waiting for active connection from server on #{host}:#{port}" write_server "PORT #{(host.split('.') + port.divmod(256)).join ','}" raise 'invalid server response' unless read_server[0] == ?2 @relay = ActiveRelay.new socket, clientdata when :passive write_server 'PASV' raise 'invalid server response' unless nums = read_server[/^227[^(]*\(([^)]*)\)/, 1] nums = nums.split(',') raise 'invalid server response' unless nums.length == 6 host = nums[0, 4].join('.') port = nums[4].to_i * 256 + nums[5].to_i @log.debug "Opening passive connection to server on #{host}:#{port}" @relay = Relay.new TCPSocket.new(host, port), clientdata else raise 'unhandled server data channel mode - %p' % mode end end |
#proxy(line) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/ftpproxy.rb', line 124 def proxy line write_server line if %w[125 150].include? relay_cmd[0, 3] begin # relay data channel @relay.relay rescue @log.error $! write_client '425 Data connection failed' else relay_cmd end end end |
#read_client ⇒ Object
80 81 82 83 84 |
# File 'lib/ftpproxy.rb', line 80 def read_client line = @client.gets @log.debug "@CLIENT>> #{line.inspect}" line end |
#read_server ⇒ Object
handle multiline responses
67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/ftpproxy.rb', line 67 def read_server data = @server.readline if data[3] == ?- code = data[0, 3] begin line = @server.readline data << line end until line[0, 3] == code and line[3] != ?- end @log.debug "@SERVER>> #{data.inspect}" data.gsub(/\r\n|\r/, "\n") end |
#relay_cmd ⇒ Object
relay command channel
140 141 142 143 144 |
# File 'lib/ftpproxy.rb', line 140 def relay_cmd resp = read_server resp.each { |line| write_client line.chomp } resp end |
#start ⇒ Object
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 |
# File 'lib/ftpproxy.rb', line 96 def start write_client "220 #{Socket.gethostname} (#{File.basename $0}) #{Time.now.strftime '%a, %d %b %Y %H:%M:%S'}" while line = read_client line.chomp! cmd, params = line.split ' ', 2 cmd = cmd.downcase params = nil if params == '' msg = "cmd_#{cmd}" if @server and WHITELIST.include?(cmd) proxy line elsif cmd == 'user' or @server && respond_to?(msg) send msg, params elsif WHITELIST.include?(cmd) or respond_to?(msg) write_client '530 Not logged in.' else write_client '500 Syntax error, command unrecognized.' end end rescue Errno::EINVAL # common, when client just aborts rescue @log.warn $! ensure @client.close if !@client.closed? @server.close if @server and !@server.closed? @relay.close if @relay end |
#write_client(str) ⇒ Object
91 92 93 94 |
# File 'lib/ftpproxy.rb', line 91 def write_client str @log.debug "@CLIENT<< #{str.inspect}" @client.write str + "\r\n" end |
#write_server(str) ⇒ Object
86 87 88 89 |
# File 'lib/ftpproxy.rb', line 86 def write_server str @log.debug "@SERVER<< #{str.inspect}" @server.write str + "\r\n" end |