class DoubleBagFTPS
Constants
- EXPLICIT
- IMPLICIT
- IMPLICIT_PORT
Attributes
The OpenSSL::SSL::SSLContext to use for creating all OpenSSL::SSL::SSLSocket objects.
Public Class Methods
# File lib/double_bag_ftps.rb, line 203 def DoubleBagFTPS.create_ssl_context(params = {}) raise 'SSL extension not installed' unless defined?(OpenSSL) context = OpenSSL::SSL::SSLContext.new context.set_params(params) return context end
# File lib/double_bag_ftps.rb, line 20 def initialize(host = nil, user = nil, passwd = nil, acct = nil, ftps_mode = EXPLICIT, ssl_context_params = {}) raise ArgumentError unless valid_ftps_mode?(ftps_mode) @ftps_mode = ftps_mode @ssl_context = DoubleBagFTPS.create_ssl_context(ssl_context_params) super(host, user, passwd, acct) end
# File lib/double_bag_ftps.rb, line 27 def DoubleBagFTPS.open(host, user = nil, passwd = nil, acct = nil, ftps_mode = EXPLICIT, ssl_context_params = {}) if block_given? ftps = new(host, user, passwd, acct, ftps_mode, ssl_context_params) begin yield ftps ensure ftps.close end else new(host, user, passwd, acct, ftps_mode, ssl_context_params) end end
Public Instance Methods
Establishes the command channel. Override parent to record host name for verification, and allow default implicit port.
# File lib/double_bag_ftps.rb, line 57 def connect(host, port = ftps_implicit? ? IMPLICIT_PORT : FTP_PORT) @hostname = host super end
# File lib/double_bag_ftps.rb, line 147 def ftps_explicit?; @ftps_mode == EXPLICIT end
# File lib/double_bag_ftps.rb, line 148 def ftps_implicit?; @ftps_mode == IMPLICIT end
Allow @ftps_mode to be set when @sock is not connected
# File lib/double_bag_ftps.rb, line 43 def ftps_mode=(ftps_mode) # Ruby 1.8.7/1.9.2 compatible check if (defined?(NullSocket) && @sock.kind_of?(NullSocket)) || @sock.nil? || @sock.closed? raise ArgumentError unless valid_ftps_mode?(ftps_mode) @ftps_mode = ftps_mode else raise 'Cannot set ftps_mode while connected' end end
# File lib/double_bag_ftps.rb, line 62 def login(user = 'anonymous', passwd = nil, acct = nil, auth = 'TLS') if ftps_explicit? synchronize do sendcmd('AUTH ' + auth) # Set the security mechanism @sock = ssl_socket(@sock) end end super(user, passwd, acct) voidcmd('PBSZ 0') # The expected value for Protection Buffer Size (PBSZ) is 0 for TLS/SSL voidcmd('PROT P') # Set data channel protection level to Private end
Private Instance Methods
Ruby 2.0's Ftp class closes sockets by first doing a shutdown, setting the read timeout, and doing a read. OpenSSL doesn't have those methods, so fake it.
Ftp calls close in an ensure block, so the socket will still get closed.
# File lib/double_bag_ftps.rb, line 183 def decorate_socket(sock) def sock.shutdown(how) @shutdown = true end def sock.read_timeout=(seconds) end # Skip read after shutdown. Prevents 2.0 from hanging in # Ftp#close def sock.read(*args) return if @shutdown super(*args) end end
Override parent to allow an OpenSSL::SSL::SSLSocket to be returned when using implicit FTPS
# File lib/double_bag_ftps.rb, line 79 def open_socket(host, port, defer_implicit_ssl = false) if defined? SOCKSSocket and ENV["SOCKS_SERVER"] @passive = true sock = SOCKSSocket.open(host, port) else sock = TCPSocket.open(host, port) end return (!defer_implicit_ssl && ftps_implicit?) ? ssl_socket(sock) : sock end
Before ruby-2.2.3, makeport called sendport automatically. After Ruby 2.3.0, makeport does not call sendport automatically, so do it ourselves. This change to Ruby's FTP lib has been backported to Ruby 2.1 since version 2.1.7.
# File lib/double_bag_ftps.rb, line 138 def sendport_needed? @sendport_needed ||= begin Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.3") || Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1.7") && Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.2.0") end end
Returns a connected OpenSSL::SSL::SSLSocket
# File lib/double_bag_ftps.rb, line 158 def ssl_socket(sock) raise 'SSL extension not installed' unless defined?(OpenSSL) sock = OpenSSL::SSL::SSLSocket.new(sock, @ssl_context) if @ssl_session sock.session = @ssl_session end sock.sync_close = true sock.connect print "get: #{sock.peer_cert.to_text}" if @debug_mode unless @ssl_context.verify_mode == OpenSSL::SSL::VERIFY_NONE sock.post_connection_check(@hostname) end @ssl_session = sock.session decorate_socket sock return sock end
Override parent to support ssl sockets
# File lib/double_bag_ftps.rb, line 93 def transfercmd(cmd, rest_offset = nil) if @passive host, port = makepasv if @resume and rest_offset resp = sendcmd('REST ' + rest_offset.to_s) if resp[0] != ?3 raise FTPReplyError, resp end end conn = open_socket(host, port, true) resp = sendcmd(cmd) # skip 2XX for some ftp servers resp = getresp if resp[0] == ?2 if resp[0] != ?1 raise FTPReplyError, resp end conn = ssl_socket(conn) # SSL connection now possible after cmd sent else sock = makeport sendport(sock.addr[3], sock.addr[1]) if sendport_needed? if @resume and rest_offset resp = sendcmd('REST ' + rest_offset.to_s) if resp[0] != ?3 raise FTPReplyError, resp end end resp = sendcmd(cmd) # skip 2XX for some ftp servers resp = getresp if resp[0] == ?2 if resp[0] != ?1 raise FTPReplyError, resp end conn = sock.accept conn = ssl_socket(conn) sock.close end return conn end
# File lib/double_bag_ftps.rb, line 150 def valid_ftps_mode?(mode) mode == EXPLICIT || mode == IMPLICIT end