ftpd 1.1.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/Changelog.md +15 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +57 -48
- data/README.md +8 -1
- data/VERSION +1 -1
- data/examples/example.rb +12 -0
- data/features/example/step_definitions/example_server.rb +2 -0
- data/features/ftp_server/eprt.feature +1 -0
- data/features/ftp_server/epsv.feature +1 -0
- data/features/ftp_server/get_ipv6.feature +3 -0
- data/features/ftp_server/pasv.feature +10 -3
- data/features/ftp_server/step_definitions/logging.rb +2 -0
- data/features/ftp_server/step_definitions/test_server.rb +6 -0
- data/features/step_definitions/append.rb +2 -0
- data/features/step_definitions/client.rb +3 -0
- data/features/step_definitions/client_and_server_files.rb +2 -0
- data/features/step_definitions/client_files.rb +2 -0
- data/features/step_definitions/command.rb +2 -0
- data/features/step_definitions/connect.rb +2 -0
- data/features/step_definitions/delete.rb +2 -0
- data/features/step_definitions/directory_navigation.rb +2 -0
- data/features/step_definitions/error_replies.rb +2 -0
- data/features/step_definitions/features.rb +2 -0
- data/features/step_definitions/file_structure.rb +2 -0
- data/features/step_definitions/generic_send.rb +2 -0
- data/features/step_definitions/get.rb +2 -0
- data/features/step_definitions/help.rb +2 -0
- data/features/step_definitions/invalid_commands.rb +2 -0
- data/features/step_definitions/ipv6.rb +11 -0
- data/features/step_definitions/line_endings.rb +2 -0
- data/features/step_definitions/list.rb +2 -0
- data/features/step_definitions/login.rb +2 -0
- data/features/step_definitions/mkdir.rb +2 -0
- data/features/step_definitions/mode.rb +2 -0
- data/features/step_definitions/mtime.rb +2 -0
- data/features/step_definitions/noop.rb +2 -0
- data/features/step_definitions/options.rb +2 -0
- data/features/step_definitions/passive.rb +7 -0
- data/features/step_definitions/pending.rb +2 -0
- data/features/step_definitions/port.rb +2 -0
- data/features/step_definitions/put.rb +2 -0
- data/features/step_definitions/quit.rb +2 -0
- data/features/step_definitions/rename.rb +2 -0
- data/features/step_definitions/rmdir.rb +2 -0
- data/features/step_definitions/server_files.rb +2 -0
- data/features/step_definitions/server_title.rb +2 -0
- data/features/step_definitions/size.rb +2 -0
- data/features/step_definitions/status.rb +2 -0
- data/features/step_definitions/success_replies.rb +2 -0
- data/features/step_definitions/system.rb +5 -0
- data/features/step_definitions/timing.rb +2 -0
- data/features/step_definitions/type.rb +2 -0
- data/features/support/env.rb +2 -0
- data/features/support/example_server.rb +3 -1
- data/features/support/test_client.rb +27 -19
- data/features/support/test_file_templates.rb +2 -0
- data/features/support/test_server.rb +31 -20
- data/features/support/test_server_files.rb +2 -0
- data/ftpd.gemspec +17 -11
- data/lib/ftpd.rb +2 -0
- data/lib/ftpd/auth_levels.rb +2 -0
- data/lib/ftpd/cmd_abor.rb +2 -0
- data/lib/ftpd/cmd_allo.rb +2 -0
- data/lib/ftpd/cmd_appe.rb +2 -0
- data/lib/ftpd/cmd_auth.rb +2 -0
- data/lib/ftpd/cmd_cdup.rb +2 -0
- data/lib/ftpd/cmd_cwd.rb +2 -0
- data/lib/ftpd/cmd_dele.rb +2 -0
- data/lib/ftpd/cmd_eprt.rb +2 -0
- data/lib/ftpd/cmd_epsv.rb +4 -2
- data/lib/ftpd/cmd_feat.rb +2 -0
- data/lib/ftpd/cmd_help.rb +2 -0
- data/lib/ftpd/cmd_list.rb +2 -0
- data/lib/ftpd/cmd_login.rb +2 -0
- data/lib/ftpd/cmd_mdtm.rb +2 -0
- data/lib/ftpd/cmd_mkd.rb +2 -0
- data/lib/ftpd/cmd_mode.rb +2 -0
- data/lib/ftpd/cmd_nlst.rb +2 -0
- data/lib/ftpd/cmd_noop.rb +2 -0
- data/lib/ftpd/cmd_opts.rb +2 -0
- data/lib/ftpd/cmd_pasv.rb +5 -3
- data/lib/ftpd/cmd_pbsz.rb +2 -0
- data/lib/ftpd/cmd_port.rb +2 -0
- data/lib/ftpd/cmd_prot.rb +2 -0
- data/lib/ftpd/cmd_pwd.rb +2 -0
- data/lib/ftpd/cmd_quit.rb +2 -0
- data/lib/ftpd/cmd_rein.rb +2 -0
- data/lib/ftpd/cmd_rename.rb +2 -0
- data/lib/ftpd/cmd_rest.rb +2 -0
- data/lib/ftpd/cmd_retr.rb +2 -0
- data/lib/ftpd/cmd_rmd.rb +2 -0
- data/lib/ftpd/cmd_site.rb +2 -0
- data/lib/ftpd/cmd_size.rb +2 -0
- data/lib/ftpd/cmd_smnt.rb +2 -0
- data/lib/ftpd/cmd_stat.rb +2 -0
- data/lib/ftpd/cmd_stor.rb +2 -0
- data/lib/ftpd/cmd_stou.rb +2 -0
- data/lib/ftpd/cmd_stru.rb +2 -0
- data/lib/ftpd/cmd_syst.rb +2 -0
- data/lib/ftpd/cmd_type.rb +2 -0
- data/lib/ftpd/command_handler.rb +3 -0
- data/lib/ftpd/command_handler_factory.rb +2 -0
- data/lib/ftpd/command_handlers.rb +2 -0
- data/lib/ftpd/command_loop.rb +2 -0
- data/lib/ftpd/command_sequence_checker.rb +2 -0
- data/lib/ftpd/config.rb +2 -0
- data/lib/ftpd/connection_throttle.rb +2 -0
- data/lib/ftpd/connection_tracker.rb +2 -0
- data/lib/ftpd/data_connection_helper.rb +2 -0
- data/lib/ftpd/data_server_factory.rb +44 -0
- data/lib/ftpd/data_server_factory/random_ephemeral_port.rb +26 -0
- data/lib/ftpd/data_server_factory/specific_port_range.rb +37 -0
- data/lib/ftpd/disk_file_system.rb +2 -0
- data/lib/ftpd/error.rb +2 -0
- data/lib/ftpd/exception_translator.rb +2 -0
- data/lib/ftpd/exceptions.rb +2 -0
- data/lib/ftpd/file_info.rb +2 -0
- data/lib/ftpd/file_system_helper.rb +2 -0
- data/lib/ftpd/ftp_server.rb +27 -0
- data/lib/ftpd/gets_peer_address.rb +2 -0
- data/lib/ftpd/insecure_certificate.rb +2 -0
- data/lib/ftpd/list_format/eplf.rb +2 -0
- data/lib/ftpd/list_format/ls.rb +2 -0
- data/lib/ftpd/list_path.rb +2 -0
- data/lib/ftpd/null_logger.rb +2 -0
- data/lib/ftpd/protocols.rb +2 -0
- data/lib/ftpd/read_only_disk_file_system.rb +2 -0
- data/lib/ftpd/server.rb +2 -0
- data/lib/ftpd/session.rb +7 -0
- data/lib/ftpd/session_config.rb +26 -2
- data/lib/ftpd/stream.rb +2 -0
- data/lib/ftpd/telnet.rb +3 -2
- data/lib/ftpd/temp_dir.rb +2 -0
- data/lib/ftpd/tls_server.rb +2 -0
- data/lib/ftpd/translate_exceptions.rb +2 -0
- data/spec/command_sequence_checker_spec.rb +2 -0
- data/spec/connection_throttle_spec.rb +2 -0
- data/spec/connection_tracker_spec.rb +2 -0
- data/spec/data_server_factory_spec.rb +104 -0
- data/spec/disk_file_system_spec.rb +2 -0
- data/spec/exception_translator_spec.rb +2 -0
- data/spec/file_info_spec.rb +2 -0
- data/spec/ftp_server_error_spec.rb +2 -0
- data/spec/list_format/eplf_spec.rb +2 -0
- data/spec/list_format/ls_spec.rb +2 -0
- data/spec/list_path_spec.rb +2 -0
- data/spec/null_logger_spec.rb +2 -0
- data/spec/protocols_spec.rb +58 -38
- data/spec/server_spec.rb +2 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/telnet_spec.rb +2 -0
- data/spec/translate_exceptions_spec.rb +2 -0
- data/testlib/network.rb +17 -0
- metadata +13 -7
data/lib/ftpd/command_loop.rb
CHANGED
data/lib/ftpd/config.rb
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'data_server_factory/random_ephemeral_port'
|
|
4
|
+
require_relative 'data_server_factory/specific_port_range'
|
|
5
|
+
|
|
6
|
+
module Ftpd
|
|
7
|
+
|
|
8
|
+
# Factories for creating TCPServer used for passive mode
|
|
9
|
+
# connections.
|
|
10
|
+
module DataServerFactory
|
|
11
|
+
|
|
12
|
+
attr_reader :tcp_server
|
|
13
|
+
|
|
14
|
+
# Create a factory.
|
|
15
|
+
#
|
|
16
|
+
# @param interface [String] The IP address of the interface to
|
|
17
|
+
# bind to (e.g. "127.0.0.1")
|
|
18
|
+
# @param ports [nil, Range] The range of ports to bind to. If nil,
|
|
19
|
+
# then binds to a random ephemeral port.
|
|
20
|
+
def self.make(interface, ports)
|
|
21
|
+
if ports
|
|
22
|
+
SpecificPortRange.new(interface, ports)
|
|
23
|
+
else
|
|
24
|
+
RandomEphemeralPort.new(interface)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @param interface [String] The IP address of the interface to
|
|
29
|
+
# bind to (e.g. "127.0.0.1")
|
|
30
|
+
# @param ports [nil, Range] The range of ports to bind to. If nil,
|
|
31
|
+
# then binds to a random ephemeral port.
|
|
32
|
+
def initialize(interface, ports)
|
|
33
|
+
@interface = interface
|
|
34
|
+
@ports = ports
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [TCPServer]
|
|
38
|
+
def make_tcp_server
|
|
39
|
+
TCPServer.new(@interface, 0)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ftpd
|
|
4
|
+
|
|
5
|
+
module DataServerFactory
|
|
6
|
+
|
|
7
|
+
# Factory for creating TCPServer used for passive mode connections.
|
|
8
|
+
# This factory binds to a random ephemeral port.
|
|
9
|
+
class RandomEphemeralPort
|
|
10
|
+
|
|
11
|
+
# @param interface [String] The IP address of the interface to
|
|
12
|
+
# bind to (e.g. "127.0.0.1")
|
|
13
|
+
def initialize(interface)
|
|
14
|
+
@interface = interface
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @return [TCPServer]
|
|
18
|
+
def make_tcp_server
|
|
19
|
+
TCPServer.new(@interface, 0)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ftpd
|
|
4
|
+
|
|
5
|
+
module DataServerFactory
|
|
6
|
+
|
|
7
|
+
# Factory for creating TCPServer used for passive mode
|
|
8
|
+
# connections. This factory binds to a random port within a
|
|
9
|
+
# specific range of ports.
|
|
10
|
+
class SpecificPortRange
|
|
11
|
+
|
|
12
|
+
# @param interface [String] The IP address of the interface to
|
|
13
|
+
# bind to (e.g. "127.0.0.1")
|
|
14
|
+
# @param ports [nil, Range] The range of ports to bind to.
|
|
15
|
+
def initialize(interface, ports)
|
|
16
|
+
@interface = interface
|
|
17
|
+
@ports = ports
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @return [TCPServer]
|
|
21
|
+
def make_tcp_server
|
|
22
|
+
ports_to_try = @ports.to_a.shuffle
|
|
23
|
+
until ports_to_try.empty?
|
|
24
|
+
port = ports_to_try.shift
|
|
25
|
+
begin
|
|
26
|
+
return TCPServer.new(@interface, port)
|
|
27
|
+
rescue Errno::EADDRINUSE
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
TCPServer.new(@interface, 0)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
data/lib/ftpd/error.rb
CHANGED
data/lib/ftpd/exceptions.rb
CHANGED
data/lib/ftpd/file_info.rb
CHANGED
data/lib/ftpd/ftp_server.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'tls_server'
|
|
2
4
|
|
|
3
5
|
module Ftpd
|
|
@@ -88,6 +90,27 @@ module Ftpd
|
|
|
88
90
|
def_delegator :@connection_throttle, :'max_connections_per_ip'
|
|
89
91
|
def_delegator :@connection_throttle, :'max_connections_per_ip='
|
|
90
92
|
|
|
93
|
+
# The advertised public IP for passive mode connections. This is
|
|
94
|
+
# the IP that the client must use to make a connection back to the
|
|
95
|
+
# server. If nil, the IP of the bound interface is used. When
|
|
96
|
+
# the FTP server is behind a firewall, set this to firewall's
|
|
97
|
+
# public IP and add the appropriate rule to the firewall to
|
|
98
|
+
# forward that IP to the machine that ftpd is running on.
|
|
99
|
+
#
|
|
100
|
+
# Set this before calling #start.
|
|
101
|
+
#
|
|
102
|
+
# @return [nil, String]
|
|
103
|
+
attr_accessor :nat_ip
|
|
104
|
+
|
|
105
|
+
# The range of ports for passive mode connections. If nil, then a
|
|
106
|
+
# random etherial port is used. Otherwise, a random port from
|
|
107
|
+
# this range is used.
|
|
108
|
+
#
|
|
109
|
+
# Set this before calling #start.
|
|
110
|
+
#
|
|
111
|
+
# @return [nil, Range]
|
|
112
|
+
attr_accessor :passive_ports
|
|
113
|
+
|
|
91
114
|
# The number of seconds to delay before replying. This is for
|
|
92
115
|
# testing, when you need to test, for example, client timeouts.
|
|
93
116
|
# Defaults to 0 (no delay).
|
|
@@ -164,6 +187,8 @@ module Ftpd
|
|
|
164
187
|
@server_version = read_version_file
|
|
165
188
|
@allow_low_data_ports = false
|
|
166
189
|
@failed_login_delay = 0
|
|
190
|
+
@nat_ip = nil
|
|
191
|
+
@passive_ports = nil
|
|
167
192
|
self.log = nil
|
|
168
193
|
@connection_tracker = ConnectionTracker.new
|
|
169
194
|
@connection_throttle = ConnectionThrottle.new(@connection_tracker)
|
|
@@ -194,6 +219,8 @@ module Ftpd
|
|
|
194
219
|
config.list_formatter = @list_formatter
|
|
195
220
|
config.log = @log
|
|
196
221
|
config.max_failed_logins = @max_failed_logins
|
|
222
|
+
config.nat_ip = @nat_ip
|
|
223
|
+
config.passive_ports = @passive_ports
|
|
197
224
|
config.response_delay = response_delay
|
|
198
225
|
config.server_name = @server_name
|
|
199
226
|
config.server_version = @server_version
|
data/lib/ftpd/list_format/ls.rb
CHANGED
data/lib/ftpd/list_path.rb
CHANGED
data/lib/ftpd/null_logger.rb
CHANGED
data/lib/ftpd/protocols.rb
CHANGED
data/lib/ftpd/server.rb
CHANGED
data/lib/ftpd/session.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Ftpd
|
|
2
4
|
class Session
|
|
3
5
|
|
|
@@ -7,6 +9,7 @@ module Ftpd
|
|
|
7
9
|
attr_accessor :command_sequence_checker
|
|
8
10
|
attr_accessor :data_channel_protection_level
|
|
9
11
|
attr_accessor :data_server
|
|
12
|
+
attr_accessor :data_server_factory
|
|
10
13
|
attr_accessor :data_type
|
|
11
14
|
attr_accessor :logged_in
|
|
12
15
|
attr_accessor :name_prefix
|
|
@@ -34,6 +37,10 @@ module Ftpd
|
|
|
34
37
|
@protocols = Protocols.new(@socket)
|
|
35
38
|
@command_handlers = CommandHandlers.new
|
|
36
39
|
@command_loop = CommandLoop.new(self)
|
|
40
|
+
@data_server_factory = DataServerFactory.make(
|
|
41
|
+
@socket.addr[3],
|
|
42
|
+
config.passive_ports,
|
|
43
|
+
)
|
|
37
44
|
register_commands
|
|
38
45
|
initialize_session
|
|
39
46
|
end
|
data/lib/ftpd/session_config.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Ftpd
|
|
2
4
|
|
|
3
5
|
# All of the configuration needed by a session
|
|
@@ -23,8 +25,8 @@ module Ftpd
|
|
|
23
25
|
|
|
24
26
|
attr_accessor :auth_level
|
|
25
27
|
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
+
# A driver for the server's dynamic behavior such as
|
|
29
|
+
# authentication and file system access.
|
|
28
30
|
#
|
|
29
31
|
# The driver should expose these public methods:
|
|
30
32
|
# * {Example::Driver#authenticate authenticate}
|
|
@@ -66,6 +68,28 @@ module Ftpd
|
|
|
66
68
|
|
|
67
69
|
attr_accessor :response_delay
|
|
68
70
|
|
|
71
|
+
# The advertised public IP for passive mode connections. This is
|
|
72
|
+
# the IP that the client must use to make a connection back to the
|
|
73
|
+
# server. If nil, the IP of the bound interface is used. When
|
|
74
|
+
# the FTP server is behind a firewall, set this to firewall's
|
|
75
|
+
# public IP and add the appropriate rule to the firewall to
|
|
76
|
+
# forward that IP to the machine that ftpd is running on.
|
|
77
|
+
#
|
|
78
|
+
# Set this before calling #start.
|
|
79
|
+
#
|
|
80
|
+
# @return [nil, String]
|
|
81
|
+
|
|
82
|
+
attr_accessor :nat_ip
|
|
83
|
+
|
|
84
|
+
# The range of ports for passive mode connections. If nil, then a
|
|
85
|
+
# random etherial port is used. Otherwise, a random port from
|
|
86
|
+
# this range is used.
|
|
87
|
+
#
|
|
88
|
+
# Set this before calling #start.
|
|
89
|
+
#
|
|
90
|
+
# @return [nil, Range]
|
|
91
|
+
attr_accessor :passive_ports
|
|
92
|
+
|
|
69
93
|
# The server's name, sent in a STAT reply. Defaults to
|
|
70
94
|
# {Ftpd::FtpServer::DEFAULT_SERVER_NAME}.
|
|
71
95
|
#
|
data/lib/ftpd/stream.rb
CHANGED
data/lib/ftpd/telnet.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# -*- ruby encoding: us-ascii -*-
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module Ftpd
|
|
4
5
|
|
|
@@ -97,8 +98,8 @@ module Ftpd
|
|
|
97
98
|
# Parse the the command. Sets @plain and @reply
|
|
98
99
|
|
|
99
100
|
def parse_command(command)
|
|
100
|
-
@plain = ''
|
|
101
|
-
@reply = ''
|
|
101
|
+
@plain = ''.dup
|
|
102
|
+
@reply = ''.dup
|
|
102
103
|
scanner = StringScanner.new(command)
|
|
103
104
|
while !scanner.eos?
|
|
104
105
|
SEQUENCES.each do |regexp, method|
|
data/lib/ftpd/temp_dir.rb
CHANGED
data/lib/ftpd/tls_server.rb
CHANGED