class SFTPServer::Server
Attributes
dsa_key[RW]
listen_address[RW]
password[RW]
port[RW]
rsa_key[RW]
user_name[RW]
verbose[RW]
Public Class Methods
new(options = {})
click to toggle source
# File lib/sftp_server/server.rb, line 16 def initialize(options = {}) @user_name = options[:user_name] @password = options[:password] @rsa_key = options[:rsa_key] @dsa_key = options[:dsa_key] @port = options[:port] @listen_address = options[:listen_address] @verbose = options[:verbose] @authenticated = false @handles = {} end
Public Instance Methods
authenticate(session)
click to toggle source
# File lib/sftp_server/server.rb, line 126 def authenticate(session) authenticated = false while !authenticated message = SSH::API.ssh_message_get(session) next unless message message_type = SSH::API.ssh_message_type(message) log "message_type: #{message_type}" next unless message_type > -1 case message_type when SSH::API::MessageTypes::SSH_REQUEST_AUTH log "auth" message_subtype = SSH::API.ssh_message_subtype(message) log "auth message_subtype: #{message_subtype}" case message_subtype when SSH::API::MessageAuthTypes::SSH_AUTH_METHOD_PASSWORD request_user_name = SSH::API.ssh_message_auth_user(message) request_password = SSH::API.ssh_message_auth_password(message) log user_name log password if user_name == request_user_name && password == request_password SSH::API.ssh_message_auth_reply_success(message, 0) SSH::API.ssh_message_free(message) authenticated = true break else SSH::API.ssh_message_reply_default(message) next end else respond_auth_required(message) unless @authenticated end else SSH::API.ssh_message_reply_default(message) end end authenticated end
bind_accept(bind, session)
click to toggle source
# File lib/sftp_server/server.rb, line 41 def bind_accept(bind, session) result = SSH::API.ssh_bind_accept(bind, session) fail SSH::API.ssh_get_error(bind) if result < 0 end
bind_listen(bind)
click to toggle source
# File lib/sftp_server/server.rb, line 36 def bind_listen(bind) result = SSH::API.ssh_bind_listen(bind) fail SSH::API.ssh_get_error(bind) if result < 0 end
close_channel(channel)
click to toggle source
# File lib/sftp_server/server.rb, line 401 def close_channel(channel) result = SSH::API.ssh_channel_close(channel) fail SSH::API.ssh_get_error(channel) if result < 0 end
disconnect_session(session)
click to toggle source
# File lib/sftp_server/server.rb, line 411 def disconnect_session(session) result = SSH::API.ssh_disconnect(session) fail SSH::API.ssh_get_error(session) if result < 0 end
free_bind(bind)
click to toggle source
# File lib/sftp_server/server.rb, line 416 def free_bind(bind) result = SSH::API.ssh_bind_free(bind) fail SSH::API.ssh_get_error(bind) if result < 0 end
free_channel(channel)
click to toggle source
# File lib/sftp_server/server.rb, line 406 def free_channel(channel) result = SSH::API.ssh_channel_free(channel) fail SSH::API.ssh_get_error(channel) if result < 0 end
handle_auth(message)
click to toggle source
# File lib/sftp_server/server.rb, line 51 def handle_auth(message) end
handle_key_exchange(session)
click to toggle source
# File lib/sftp_server/server.rb, line 46 def handle_key_exchange(session) result = SSH::API.ssh_handle_key_exchange(session) fail SSH::API.ssh_get_error(session) if result < 0 end
init_sftp_session(sftp_session)
click to toggle source
# File lib/sftp_server/server.rb, line 166 def init_sftp_session(sftp_session) result = SSH::API.sftp_server_init(sftp_session) fail SSH::API.ssh_get_error(sftp_session) unless result == 0 end
log(message)
click to toggle source
# File lib/sftp_server/server.rb, line 96 def log(message) puts(message) if verbose end
open()
click to toggle source
# File lib/sftp_server/server.rb, line 421 def open ssh_bind = SSH::API.ssh_bind_new set_bind_option(ssh_bind, :int, SSH::API::BindOptions::SSH_BIND_OPTIONS_BINDADDR, :string, listen_address) if listen_address set_bind_option(ssh_bind, :int, SSH::API::BindOptions::SSH_BIND_OPTIONS_BINDPORT_STR, :string, port) if port set_bind_option(ssh_bind, :int, SSH::API::BindOptions::SSH_BIND_OPTIONS_RSAKEY, :string, rsa_key) if rsa_key set_bind_option(ssh_bind, :int, SSH::API::BindOptions::SSH_BIND_OPTIONS_DSAKEY, :string, dsa_key) if dsa_key bind_listen(ssh_bind) loop do session = SSH::API.ssh_new bind_accept(ssh_bind, session) handle_key_exchange(session) if authenticate(session) channel = open_channel(session) if channel if sftp_channel_request(session) sftp_session = SSH::API.sftp_server_new(session, channel) init_sftp_session(sftp_session) sftp_message_loop(sftp_session) end end close_channel(channel) free_channel(channel) end end end
open_channel(session)
click to toggle source
# File lib/sftp_server/server.rb, line 100 def open_channel(session) channel = nil while channel.nil? message = SSH::API.ssh_message_get(session) next unless message message_type = SSH::API.ssh_message_type(message) log "channel message_type: #{message_type}" next unless message_type > -1 case message_type when SSH::API::MessageTypes::SSH_REQUEST_CHANNEL_OPEN message_subtype = SSH::API.ssh_message_subtype(message) log "channel message_subtype: #{message_subtype}" if message_subtype == SSH::API::ChannelTypes::SSH_CHANNEL_SESSION channel = SSH::API.ssh_message_channel_request_open_reply_accept(message) break end else SSH::API.ssh_message_reply_default(message) end SSH::API.ssh_message_free(message) end channel end
respond_auth_required(message)
click to toggle source
# File lib/sftp_server/server.rb, line 54 def respond_auth_required(message) SSH::API.ssh_message_auth_set_methods(message, SSH::API::MessageAuthTypes::SSH_AUTH_METHOD_PASSWORD) SSH::API.ssh_message_reply_default(message) end
set_bind_option(sshbind, key_type, key_value, value_type, value_value)
click to toggle source
# File lib/sftp_server/server.rb, line 29 def set_bind_option(sshbind, key_type, key_value, value_type, value_value) result = SSH::API.ssh_bind_options_set( sshbind, key_type, key_value, value_type, value_value ) fail SSH::API.ssh_get_error(sshbind) if result < 0 end
sftp_channel_request(session)
click to toggle source
# File lib/sftp_server/server.rb, line 59 def sftp_channel_request(session) sftp_channel_requested = false while !sftp_channel_requested message = SSH::API.ssh_message_get(session) if message message_type = SSH::API.ssh_message_type(message) log "open sftp session message_type: #{message_type}" if message_type == SSH::API::MessageTypes::SSH_REQUEST_CHANNEL message_subtype = SSH::API.ssh_message_subtype(message) log "open sftp session message_subtype: #{message_subtype}" case message_subtype when SSH::API::ChannelRequestTypes::SSH_CHANNEL_REQUEST_ENV env_name = SSH::API.ssh_message_channel_request_env_name(message) log "request env name: #{env_name}" env_value = SSH::API.ssh_message_channel_request_env_value(message) log "request env value: #{env_value}" when SSH::API::ChannelRequestTypes::SSH_CHANNEL_REQUEST_SUBSYSTEM subsystem_name = SSH::API.ssh_message_channel_request_subsystem(message) log "request subsystem: #{subsystem_name}" if subsystem_name == 'sftp' SSH::API.ssh_message_channel_request_reply_success(message) sftp_channel_requested = true end end end end unless sftp_channel_requested SSH::API.ssh_message_reply_default(message) end SSH::API.ssh_message_free(message) end sftp_channel_requested end
sftp_message_loop(sftp_session)
click to toggle source
# File lib/sftp_server/server.rb, line 171 def sftp_message_loop(sftp_session) while true client_message = SSH::API.sftp_get_client_message(sftp_session) log "client_message: #{client_message}" return if client_message.null? client_message_type = SSH::API.sftp_client_message_get_type(client_message) next unless client_message_type log "client_message_type: #{client_message_type}" case client_message_type when SSH::API::SFTPCommands::SSH_FXP_REALPATH log "realpath" file_name = SSH::API.sftp_client_message_get_filename(client_message) log "file_name: #{file_name}" long_file_name = File.expand_path(file_name) SSH::API.sftp_reply_names_add(client_message, long_file_name, long_file_name, SSH::API::SFTPAttributes.new.to_ptr) SSH::API.sftp_reply_names(client_message) when SSH::API::SFTPCommands::SSH_FXP_OPENDIR log "opendir" dir_name = SSH::API.sftp_client_message_get_filename(client_message) long_dir_name = File.expand_path(dir_name) log "long_dir_name: #{long_dir_name}" @handles[long_dir_name] = :open long_dir_name_pointer = FFI::MemoryPointer.from_string(long_dir_name) handle = SSH::API.sftp_handle_alloc(sftp_session, long_dir_name_pointer) SSH::API.sftp_reply_handle(client_message, handle) when SSH::API::SFTPCommands::SSH_FXP_READDIR log "readdir" client_message_data = SSH::API::SFTPClientMessage.new(client_message) handle = SSH::API.sftp_handle(sftp_session, client_message_data[:handle]) long_dir_name = handle.read_string log "long_dir_name: #{long_dir_name}" if @handles[long_dir_name] == :open Dir.entries(long_dir_name).each do |entry| file_stat = File.lstat(File.join(long_dir_name, entry)) attributes = SSH::API::SFTPAttributes.new attributes[:flags] = 0 attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_SIZE attributes[:size] = file_stat.size attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_UIDGID attributes[:uid] = file_stat.uid attributes[:gid] = file_stat.gid attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_PERMISSIONS attributes[:permissions] = file_stat.mode attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_ACMODTIME attributes[:atime] = file_stat.atime.to_i attributes[:mtime] = file_stat.mtime.to_i SSH::API.sftp_reply_names_add( client_message, entry, entry, attributes.to_ptr ) end @handles[long_dir_name] = :read SSH::API.sftp_reply_names(client_message) else SSH::API.sftp_reply_status(client_message, SSH::API::SFTPStatus::SSH_FX_EOF, 'End-of-file encountered') end when SSH::API::SFTPCommands::SSH_FXP_CLOSE log 'close' client_message_data = SSH::API::SFTPClientMessage.new(client_message) handle = SSH::API.sftp_handle(sftp_session, client_message_data[:handle]) long_dir_name = handle.read_string log "long_dir_name: #{long_dir_name}" entry = @handles[long_dir_name] if entry.respond_to?(:close) entry.close end @handles.delete(long_dir_name) SSH::API.sftp_reply_status(client_message, SSH::API::SFTPStatus::SSH_FX_OK, 'Success') when SSH::API::SFTPCommands::SSH_FXP_LSTAT log 'lstat' file_name = SSH::API.sftp_client_message_get_filename(client_message) log "file_name: #{file_name}" long_file_name = File.expand_path(file_name) file_stat = File.lstat(long_file_name) attributes = SSH::API::SFTPAttributes.new attributes[:flags] = 0 attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_SIZE attributes[:size] = file_stat.size attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_UIDGID attributes[:uid] = file_stat.uid attributes[:gid] = file_stat.gid attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_PERMISSIONS attributes[:permissions] = file_stat.mode attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_ACMODTIME attributes[:atime] = file_stat.atime.to_i attributes[:mtime] = file_stat.mtime.to_i SSH::API.sftp_reply_attr(client_message, attributes.to_ptr) when SSH::API::SFTPCommands::SSH_FXP_STAT log 'stat' file_name = SSH::API.sftp_client_message_get_filename(client_message) log "file_name: #{file_name}" long_file_name = File.expand_path(file_name) file_stat = File.stat(long_file_name) attributes = SSH::API::SFTPAttributes.new attributes[:flags] = 0 attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_SIZE attributes[:size] = file_stat.size attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_UIDGID attributes[:uid] = file_stat.uid attributes[:gid] = file_stat.gid attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_PERMISSIONS attributes[:permissions] = file_stat.mode attributes[:flags] |= SSH::API::Attributes::SSH_FILEXFER_ATTR_ACMODTIME attributes[:atime] = file_stat.atime.to_i attributes[:mtime] = file_stat.mtime.to_i SSH::API.sftp_reply_attr(client_message, attributes.to_ptr) when SSH::API::SFTPCommands::SSH_FXP_OPEN log 'open' file_name = SSH::API.sftp_client_message_get_filename(client_message) long_file_name = File.expand_path(file_name) log "long_file_name: #{long_file_name}" client_message_data = SSH::API::SFTPClientMessage.new(client_message) message_flags = client_message_data[:flags] flags = 0 if (message_flags & SSH::API::Flags::SSH_FXF_READ == SSH::API::Flags::SSH_FXF_READ) && (message_flags & SSH::API::Flags::SSH_FXF_WRITE == SSH::API::Flags::SSH_FXF_WRITE) flags = File::Constants::RDWR elsif (message_flags & SSH::API::Flags::SSH_FXF_READ == SSH::API::Flags::SSH_FXF_READ) flags = File::Constants::RDONLY elsif (message_flags & SSH::API::Flags::SSH_FXF_WRITE == SSH::API::Flags::SSH_FXF_WRITE) flags = File::Constants::WRONLY end if (message_flags & SSH::API::Flags::SSH_FXF_APPEND == SSH::API::Flags::SSH_FXF_APPEND) flags |= File::Constants::APPEND end if (message_flags & SSH::API::Flags::SSH_FXF_CREAT == SSH::API::Flags::SSH_FXF_CREAT) flags |= File::Constants::CREAT end if (message_flags & SSH::API::Flags::SSH_FXF_TRUNC == SSH::API::Flags::SSH_FXF_TRUNC) flags |= File::Constants::TRUNC end if (message_flags & SSH::API::Flags::SSH_FXF_EXCL == SSH::API::Flags::SSH_FXF_EXCL) flags |= File::Constants::EXCL end @handles[long_file_name] = File.open(long_file_name, flags) long_file_name_pointer = FFI::MemoryPointer.from_string(long_file_name) handle = SSH::API.sftp_handle_alloc(sftp_session, long_file_name_pointer) SSH::API.sftp_reply_handle(client_message, handle) when SSH::API::SFTPCommands::SSH_FXP_READ log 'read' client_message_data = SSH::API::SFTPClientMessage.new(client_message) handle = SSH::API.sftp_handle(sftp_session, client_message_data[:handle]) long_file_name = handle.read_string log "long_file_name: #{long_file_name}" file = @handles[long_file_name] if file file.seek(client_message_data[:offset]) data = file.read(client_message_data[:len]) if data buffer = FFI::MemoryPointer.new(:char, data.size) buffer.put_bytes(0, data) SSH::API.sftp_reply_data(client_message, buffer, data.size) else SSH::API.sftp_reply_status(client_message, SSH::API::SFTPStatus::SSH_FX_EOF, 'End-of-file encountered') end end when SSH::API::SFTPCommands::SSH_FXP_WRITE log 'write' client_message_data = SSH::API::SFTPClientMessage.new(client_message) handle = SSH::API.sftp_handle(sftp_session, client_message_data[:handle]) long_file_name = handle.read_string log "long_file_name: #{long_file_name}" file = @handles[long_file_name] if file file.seek(client_message_data[:offset]) buffer = SSH::API.sftp_client_message_get_data(client_message) file.write(buffer.read_string) SSH::API.sftp_reply_status(client_message, SSH::API::SFTPStatus::SSH_FX_OK, 'Success') end when SSH::API::SFTPCommands::SSH_FXP_REMOVE log 'remove' file_name = SSH::API.sftp_client_message_get_filename(client_message) long_file_name = File.expand_path(file_name) log "long_file_name: #{long_file_name}" File.unlink(long_file_name) SSH::API.sftp_reply_status(client_message, SSH::API::SFTPStatus::SSH_FX_OK, 'Success') end SSH::API.sftp_client_message_free(client_message) end end