ftpd 0.8.0 → 0.9.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 +7 -0
- data/Changelog.md +8 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +34 -4
- data/README.md +64 -40
- data/VERSION +1 -1
- data/doc/rfc-compliance.md +2 -2
- data/examples/example.rb +14 -6
- data/examples/example_spec.rb +93 -0
- data/features/ftp_server/eprt.feature +54 -0
- data/features/ftp_server/epsv.feature +36 -0
- data/features/ftp_server/get_ipv6.feature +43 -0
- data/features/ftp_server/list.feature +9 -0
- data/features/ftp_server/name_list.feature +9 -0
- data/features/ftp_server/pasv.feature +23 -0
- data/features/ftp_server/port.feature +7 -1
- data/features/ftp_server/step_definitions/test_server.rb +4 -0
- data/features/step_definitions/connect.rb +1 -1
- data/features/step_definitions/error_replies.rb +8 -0
- data/features/support/test_server.rb +2 -1
- data/ftpd.gemspec +16 -7
- data/lib/ftpd.rb +2 -0
- data/lib/ftpd/list_path.rb +28 -0
- data/lib/ftpd/protocols.rb +60 -0
- data/lib/ftpd/session.rb +76 -10
- data/lib/ftpd/tls_server.rb +19 -1
- data/spec/list_path_spec.rb +21 -0
- data/spec/protocols_spec.rb +139 -0
- data/spec/spec_helper.rb +1 -0
- metadata +34 -48
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 402da43ab955cc1f7ec47d5080556ae09c85da9d
|
4
|
+
data.tar.gz: 6cde23e0be18f61b46e544809127337623ee55e9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2a434cd8438916a37900e2e6dc37a56c9b87d0628ed979f18e9ed8e1bba4f4a31252c012de9bfaa77b7078440212231ae1b073a848a5d80f5f43819277df0399
|
7
|
+
data.tar.gz: a9a3d3875c56cb3d8f5ba701291e9c2c22668b3d42534cdb34eb79fc81f781e49b00c42eb8cab8b5a13f67e47e2b846ddb93e7f8faf38086b4b14205a10118f1
|
data/Changelog.md
CHANGED
@@ -2,6 +2,14 @@ This is the change log for the main branch of ftpd, which supports
|
|
2
2
|
Ruby 1.9 and greater. For ruby 1.8.7, please use the latest version
|
3
3
|
before 0.8.0.
|
4
4
|
|
5
|
+
### 0.9.0
|
6
|
+
|
7
|
+
Enhancements
|
8
|
+
|
9
|
+
* Added example showing ftp used as a test harness with rspec
|
10
|
+
* Ignore LIST/NLST switches such as "-a"
|
11
|
+
* Support IPV6
|
12
|
+
|
5
13
|
### 0.8.0
|
6
14
|
|
7
15
|
Administration
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
addressable (2.3.5)
|
4
5
|
builder (3.2.2)
|
5
6
|
cucumber (1.3.8)
|
6
7
|
builder (>= 2.1.2)
|
@@ -10,20 +11,49 @@ GEM
|
|
10
11
|
multi_test (>= 0.0.2)
|
11
12
|
diff-lcs (1.2.4)
|
12
13
|
double-bag-ftps (0.1.1)
|
14
|
+
faraday (0.8.8)
|
15
|
+
multipart-post (~> 1.2.0)
|
13
16
|
gherkin (2.12.1)
|
14
17
|
multi_json (~> 1.3)
|
15
|
-
git (1.2.
|
16
|
-
|
18
|
+
git (1.2.6)
|
19
|
+
github_api (0.10.1)
|
20
|
+
addressable
|
21
|
+
faraday (~> 0.8.1)
|
22
|
+
hashie (>= 1.2)
|
23
|
+
multi_json (~> 1.4)
|
24
|
+
nokogiri (~> 1.5.2)
|
25
|
+
oauth2
|
26
|
+
hashie (2.0.5)
|
27
|
+
highline (1.6.19)
|
28
|
+
httpauth (0.2.0)
|
29
|
+
jeweler (1.8.7)
|
30
|
+
builder
|
17
31
|
bundler (~> 1.0)
|
18
32
|
git (>= 1.2.5)
|
33
|
+
github_api (= 0.10.1)
|
34
|
+
highline (>= 1.6.15)
|
35
|
+
nokogiri (= 1.5.10)
|
19
36
|
rake
|
20
37
|
rdoc
|
21
38
|
json (1.8.0)
|
39
|
+
jwt (0.1.8)
|
40
|
+
multi_json (>= 1.5)
|
22
41
|
memoizer (1.0.1)
|
23
42
|
multi_json (1.8.0)
|
24
43
|
multi_test (0.0.2)
|
44
|
+
multi_xml (0.5.5)
|
45
|
+
multipart-post (1.2.0)
|
46
|
+
nokogiri (1.5.10)
|
47
|
+
oauth2 (0.9.2)
|
48
|
+
faraday (~> 0.8)
|
49
|
+
httpauth (~> 0.2)
|
50
|
+
jwt (~> 0.1.4)
|
51
|
+
multi_json (~> 1.0)
|
52
|
+
multi_xml (~> 0.5)
|
53
|
+
rack (~> 1.2)
|
54
|
+
rack (1.5.2)
|
25
55
|
rake (10.1.0)
|
26
|
-
rdoc (4.0.
|
56
|
+
rdoc (4.0.1)
|
27
57
|
json (~> 1.4)
|
28
58
|
redcarpet (3.0.0)
|
29
59
|
rspec (2.14.1)
|
@@ -43,7 +73,7 @@ PLATFORMS
|
|
43
73
|
DEPENDENCIES
|
44
74
|
cucumber
|
45
75
|
double-bag-ftps
|
46
|
-
jeweler
|
76
|
+
jeweler
|
47
77
|
memoizer (~> 1.0.1)
|
48
78
|
rake
|
49
79
|
redcarpet
|
data/README.md
CHANGED
@@ -34,32 +34,35 @@ This is examples/hello_world.rb, a bare minimum FTP server. It allows
|
|
34
34
|
any user/password, and serves files in a temporary directory. It
|
35
35
|
binds to an ephemeral port on the local interface:
|
36
36
|
|
37
|
-
|
38
|
-
|
37
|
+
```ruby
|
38
|
+
require 'ftpd'
|
39
|
+
require 'tmpdir'
|
39
40
|
|
40
|
-
|
41
|
+
class Driver
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
def initialize(temp_dir)
|
44
|
+
@temp_dir = temp_dir
|
45
|
+
end
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
47
|
+
def authenticate(user, password)
|
48
|
+
true
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
def file_system(user)
|
52
|
+
Ftpd::DiskFileSystem.new(@temp_dir)
|
53
|
+
end
|
53
54
|
|
54
|
-
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
57
|
+
Dir.mktmpdir do |temp_dir|
|
58
|
+
driver = Driver.new(temp_dir)
|
59
|
+
server = Ftpd::FtpServer.new(driver)
|
60
|
+
server.interface = '127.0.0.1'
|
61
|
+
server.start
|
62
|
+
puts "Server listening on port #{server.bound_port}"
|
63
|
+
gets
|
64
|
+
end
|
65
|
+
```
|
63
66
|
|
64
67
|
A more full-featured example that allows TLS and takes options is in
|
65
68
|
examples/example.rb
|
@@ -115,15 +118,17 @@ Here are the methods a file system may expose:
|
|
115
118
|
|
116
119
|
Ftpd includes a disk based file system:
|
117
120
|
|
118
|
-
|
121
|
+
```ruby
|
122
|
+
class Driver
|
119
123
|
|
120
|
-
|
124
|
+
...
|
121
125
|
|
122
|
-
|
123
|
-
|
124
|
-
|
126
|
+
def file_system(user)
|
127
|
+
Ftpd::DiskFileSystem.new('/var/lib/ftp')
|
128
|
+
end
|
125
129
|
|
126
|
-
|
130
|
+
end
|
131
|
+
```
|
127
132
|
|
128
133
|
**Warning**: The DiskFileSystem allows file and directory modification
|
129
134
|
including writing, renaming, deleting, etc. If you want a read-only
|
@@ -146,25 +151,30 @@ allows only the operations you want, or which mixes the predefined
|
|
146
151
|
modules with your customizations, as in this silly example that allows
|
147
152
|
uploads but then throws them away.
|
148
153
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
154
|
+
```ruby
|
155
|
+
class BlackHole
|
156
|
+
def write(ftp_path, contents)
|
157
|
+
end
|
158
|
+
end
|
153
159
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
160
|
+
class CustomDiskFileSystem
|
161
|
+
include DiskFileSystem::Base
|
162
|
+
include DiskFileSystem::Read
|
163
|
+
include BlackHole
|
164
|
+
end
|
165
|
+
```
|
159
166
|
|
160
167
|
## Configuration
|
161
168
|
|
162
169
|
Configuration is done via accessors on {Ftpd::FtpServer}. For
|
163
170
|
example, to set the session timeout to 10 minutes:
|
164
171
|
|
165
|
-
|
166
|
-
|
167
|
-
|
172
|
+
```ruby
|
173
|
+
server = Ftpd::FtpServer.new(driver)
|
174
|
+
server.session_timeout = 10 * 60
|
175
|
+
server.interface = '127.0.0.1'
|
176
|
+
server.start
|
177
|
+
```
|
168
178
|
|
169
179
|
You can set any of these attributes before starting the server:
|
170
180
|
|
@@ -233,6 +243,9 @@ To log to a file:
|
|
233
243
|
* Implements [RFC-2389](http://tools.ietf.org/rfc/rfc2389.txt)
|
234
244
|
(Feature negotiation mechanism for the File Transfer Protocol)
|
235
245
|
|
246
|
+
* Implements [RFC-2428](http://tools.ietf.org/rfc/rfc2428.txt) (FTP
|
247
|
+
Extensions for IPv6 and NATs)
|
248
|
+
|
236
249
|
* Implements enough of
|
237
250
|
[RFC-4217](http://tools.ietf.org/rfc/rfc4217.txt) (Securing FTP with
|
238
251
|
TLS) to get by.
|
@@ -244,8 +257,8 @@ See [RFC Compliance](doc/rfc-compliance.md) for details
|
|
244
257
|
The tests pass with these Rubies:
|
245
258
|
|
246
259
|
* ruby-1.8.7-p371
|
247
|
-
* ruby-1.9.3-
|
248
|
-
* ruby-2.0.0-
|
260
|
+
* ruby-1.9.3-p448
|
261
|
+
* ruby-2.0.0-p247
|
249
262
|
|
250
263
|
For Ruby 1.8, use an ftpd version before 0.8. In your Gemfile:
|
251
264
|
|
@@ -312,6 +325,17 @@ Wayne Conrad <wconrad@yagni.com>
|
|
312
325
|
Thanks to Databill, LLC, which supported the creation of this library,
|
313
326
|
and granted permission to donate it to the community.
|
314
327
|
|
328
|
+
### Contributors
|
329
|
+
|
330
|
+
Among those who have improved ftpd are:
|
331
|
+
|
332
|
+
* Alfonso Cora
|
333
|
+
* Bjoern B. Dorra
|
334
|
+
* Larry. W. Cashdollar
|
335
|
+
* Michael de Silva
|
336
|
+
|
337
|
+
Thank you!
|
338
|
+
|
315
339
|
## See also
|
316
340
|
|
317
341
|
* [Changelog](Changelog.md)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.9.0
|
data/doc/rfc-compliance.md
CHANGED
@@ -213,8 +213,8 @@ use over various network protocols, and the new response codes 522 and
|
|
213
213
|
* [link](http://tools.ietf.org/rfc/rfc2428.txt)
|
214
214
|
|
215
215
|
<pre>
|
216
|
-
EPRT
|
217
|
-
EPSV
|
216
|
+
EPRT Yes 0.9.0 Set active data connection over IPv4 or IPv6
|
217
|
+
EPSV Yes 0.9.0 Set passive data connection over IPv4 or IPv6
|
218
218
|
</pre>
|
219
219
|
|
220
220
|
##RFC-2577 - FTP Security Considerations
|
data/examples/example.rb
CHANGED
@@ -5,6 +5,7 @@ unless $:.include?(File.dirname(__FILE__) + '/../lib')
|
|
5
5
|
end
|
6
6
|
|
7
7
|
require 'ftpd'
|
8
|
+
require 'ipaddr'
|
8
9
|
require 'optparse'
|
9
10
|
|
10
11
|
module Example
|
@@ -26,7 +27,7 @@ module Example
|
|
26
27
|
attr_reader :user
|
27
28
|
|
28
29
|
def initialize(argv)
|
29
|
-
@interface = '
|
30
|
+
@interface = '127.0.0.1'
|
30
31
|
@tls = :explicit
|
31
32
|
@port = 0
|
32
33
|
@auth_level = 'password'
|
@@ -185,8 +186,6 @@ module Example
|
|
185
186
|
|
186
187
|
private
|
187
188
|
|
188
|
-
HOST = 'localhost'
|
189
|
-
|
190
189
|
def auth_level
|
191
190
|
Ftpd.const_get("AUTH_#{@args.auth_level.upcase}")
|
192
191
|
end
|
@@ -210,10 +209,10 @@ module Example
|
|
210
209
|
puts "Port: #{@server.bound_port}"
|
211
210
|
puts "User: #{user.inspect}"
|
212
211
|
puts "Pass: #{password.inspect}" if auth_level >= Ftpd::AUTH_PASSWORD
|
213
|
-
puts "
|
212
|
+
puts "Account: #{account.inspect}" if auth_level >= Ftpd::AUTH_ACCOUNT
|
214
213
|
puts "TLS: #{@args.tls}"
|
215
214
|
puts "Directory: #{@data_dir}"
|
216
|
-
puts "URI: ftp://#{
|
215
|
+
puts "URI: ftp://#{connection_host}:#{@server.bound_port}"
|
217
216
|
puts "PID: #{$$}"
|
218
217
|
end
|
219
218
|
|
@@ -221,7 +220,7 @@ module Example
|
|
221
220
|
command_path = '/tmp/connect-to-example-ftp-server.sh'
|
222
221
|
File.open(command_path, 'w') do |file|
|
223
222
|
file.puts "#!/bin/bash"
|
224
|
-
file.puts "ftp $FTP_ARGS #{
|
223
|
+
file.puts "ftp $FTP_ARGS #{connection_host} #{@server.bound_port}"
|
225
224
|
end
|
226
225
|
system("chmod +x #{command_path}")
|
227
226
|
puts "Connection script written to #{command_path}"
|
@@ -253,6 +252,15 @@ module Example
|
|
253
252
|
@args.debug && Logger.new($stdout)
|
254
253
|
end
|
255
254
|
|
255
|
+
def connection_host
|
256
|
+
addr = IPAddr.new(@server.interface)
|
257
|
+
if addr.ipv6?
|
258
|
+
'::1'
|
259
|
+
else
|
260
|
+
'127.0.0.1'
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
256
264
|
end
|
257
265
|
end
|
258
266
|
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Authour: Michael de Silva <michael@mwdesilva.com>
|
4
|
+
# CEO @ http://omakaselabs.com / Mentor @ http://railsphd.com
|
5
|
+
# https://twitter.com/bsodmike / https://github.com/bsodmike
|
6
|
+
|
7
|
+
# This is an example for using Ftpd as a means for spec driving
|
8
|
+
# interaction with a 'dummy' ftp server via RSpec. In this example we
|
9
|
+
# assume the client is implemented via `Fetcher::FTPFetcher`.
|
10
|
+
|
11
|
+
unless $:.include?(File.dirname(__FILE__) + '/../lib')
|
12
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'net/ftp'
|
16
|
+
require 'ftpd'
|
17
|
+
require 'tmpdir'
|
18
|
+
|
19
|
+
# This is an example client spec driven via the use of Ftpd within the
|
20
|
+
# specs. The specs spawn a 'dummy' Ftpd server and ensure this client
|
21
|
+
# operates as expected.
|
22
|
+
|
23
|
+
module Fetcher
|
24
|
+
|
25
|
+
# This is the code under test, a simple fetcher that logs into an
|
26
|
+
# FTP site, changes to a directlry, and gets a list of files.
|
27
|
+
|
28
|
+
class FTPFetcher
|
29
|
+
|
30
|
+
# @param host [String] ftp host to connect to.
|
31
|
+
# @param user [String] username.
|
32
|
+
# @param pwd [String] password.
|
33
|
+
# @param dir [String] remote directory to change to.
|
34
|
+
|
35
|
+
def initialize(host, user, pwd, dir)
|
36
|
+
@host = host
|
37
|
+
@user = user
|
38
|
+
@pwd = pwd
|
39
|
+
@dir = dir
|
40
|
+
@ftp = Net::FTP.new
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param port [Fixnum] port to connect to, 21 by default.
|
44
|
+
# @return [Array] list of files in the current directory.
|
45
|
+
|
46
|
+
def connect_and_list(port = 21)
|
47
|
+
@ftp.debug_mode = true if ENV['DEBUG'] == "true"
|
48
|
+
@ftp.passive = true
|
49
|
+
@ftp.connect @host, port
|
50
|
+
@ftp.login @user, @pwd
|
51
|
+
@ftp.chdir @dir
|
52
|
+
@ftp.nlst
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe Fetcher::FTPFetcher do
|
59
|
+
|
60
|
+
# This `Driver` tells Ftpd how to authenticate and how to interact
|
61
|
+
# with the file systme. In this example, the file system is
|
62
|
+
# read-only and contains a single file.
|
63
|
+
|
64
|
+
class Driver
|
65
|
+
def initialize
|
66
|
+
@data_dir = Dir.mktmpdir
|
67
|
+
at_exit {FileUtils.rm_rf(@data_dir)}
|
68
|
+
FileUtils.touch File.expand_path('report.txt', @data_dir)
|
69
|
+
end
|
70
|
+
def authenticate(user, pwd); true; end
|
71
|
+
def file_system(user); Ftpd::ReadOnlyDiskFileSystem.new(@data_dir); end
|
72
|
+
end
|
73
|
+
|
74
|
+
let(:server) do
|
75
|
+
server = Ftpd::FtpServer.new(Driver.new)
|
76
|
+
server.interface = "127.0.0.1"
|
77
|
+
server.start
|
78
|
+
server
|
79
|
+
end
|
80
|
+
|
81
|
+
let(:subject) do
|
82
|
+
Fetcher::FTPFetcher.new('127.0.0.1', 'user', 'password', '/')
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#connect_and_list" do
|
86
|
+
|
87
|
+
it "should connect to the FTP server and find 'report.txt' in the Array returned" do
|
88
|
+
result = subject.connect_and_list(server.bound_port)
|
89
|
+
expect(result).to include('report.txt')
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
Feature: EPRT
|
2
|
+
|
3
|
+
As a programmer
|
4
|
+
I want good error messages
|
5
|
+
So that I can correct problems
|
6
|
+
|
7
|
+
Background:
|
8
|
+
Given the test server is started
|
9
|
+
|
10
|
+
Scenario: Port 1024
|
11
|
+
Given a successful login
|
12
|
+
Then the client successfully sends "EPRT |1|1.2.3.4|1024|"
|
13
|
+
|
14
|
+
Scenario: Port 1023; low ports disallowed
|
15
|
+
Given the test server disallows low data ports
|
16
|
+
And a successful login
|
17
|
+
When the client sends "EPRT |1|2.3.4.3|255|"
|
18
|
+
Then the server returns an unimplemented parameter error
|
19
|
+
|
20
|
+
Scenario: Port out of range
|
21
|
+
Given a successful login
|
22
|
+
When the client sends "EPRT |1|2.3.4.5|65536|"
|
23
|
+
Then the server returns an unimplemented parameter error
|
24
|
+
|
25
|
+
Scenario: Port 1023; low ports allowed
|
26
|
+
Given the test server allows low data ports
|
27
|
+
And a successful login
|
28
|
+
Then the client successfully sends "EPRT |1|2.3.4.3|255|"
|
29
|
+
|
30
|
+
Scenario: Not logged in
|
31
|
+
Given a successful connection
|
32
|
+
When the client sends "EPRT |1|2.3.4.5|6|"
|
33
|
+
Then the server returns a not logged in error
|
34
|
+
|
35
|
+
Scenario: Too few parts
|
36
|
+
Given a successful login
|
37
|
+
When the client sends "EPRT |1|2.3.4|"
|
38
|
+
Then the server returns a syntax error
|
39
|
+
|
40
|
+
Scenario: Too many parts
|
41
|
+
Given a successful login
|
42
|
+
When the client sends "EPRT |1|2.3.4|5|6|"
|
43
|
+
Then the server returns a syntax error
|
44
|
+
|
45
|
+
Scenario: Unknown network protocol
|
46
|
+
Given a successful login
|
47
|
+
When the client sends "EPRT |3|2.3.4.5|6|"
|
48
|
+
Then the server returns a network protocol not supported error
|
49
|
+
|
50
|
+
Scenario: After "EPSV ALL"
|
51
|
+
Given a successful login
|
52
|
+
Given the client successfully sends "EPSV ALL"
|
53
|
+
When the client sends "EPRT |1|2.3.4.5|6|"
|
54
|
+
Then the server sends a not allowed after epsv all error
|