Skip to content

Commit 1f23662

Browse files
Merge pull request #132 from fclairamb/feature/pasv-port-allocation-fix
Passive port allocation fix
2 parents e2a1fe4 + 10e6831 commit 1f23662

File tree

9 files changed

+60
-45
lines changed

9 files changed

+60
-45
lines changed

.github/workflows/docker-test.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
ftpserver:
1414
image: ftpserver/ftpserver
1515
ports:
16-
- 2121:2121
16+
- 2121-2200:2121-2200
1717

1818
steps:
1919
- name: Run sample
@@ -26,22 +26,21 @@ jobs:
2626
sudo apt-get install netcat curl -y
2727
2828
# Getting a file
29-
curl -o file.bin https://github.com/fclairamb/ftpserver/releases/download/v0.5/ftpserver-linux-amd64
29+
curl https://placekitten.com/2048/2048 -o kitty.jpg
3030
3131
# Waiting for server to be ready
3232
while ! nc -z localhost ${SERVER_PORT} </dev/null; do sleep 1; done
3333
3434
# Upload a file
35-
curl -P - -T file.bin ftp://test:test@localhost:2121/remote.bin
35+
curl -P - -T kitty.jpg ftp://test:test@localhost:${SERVER_PORT}/remote.jpg
3636
3737
# Download a file
38-
curl -P - ftp://test:test@localhost:2121/remote.bin -o remote.bin
38+
curl -P - ftp://test:test@localhost:2121/remote.jpg -o remote.jpg
3939
4040
# Checking that the output file exists
41-
if [ ! -f remote.bin ]; then
41+
if [ ! -f remote.jpg ]; then
4242
exit 1
4343
fi
4444
4545
# Comparing file contents
46-
diff file.bin remote.bin
47-
46+
diff kitty.jpg remote.jpg

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
ftpserver
33
.idea
44
.vscode
5-
settings.toml
65
debug
76
__debug_bin

README.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,11 @@ mkdir -p data
6868
docker run --rm -d -p 2121-2200:2121-2200 -v $(pwd)/data:/data fclairamb/ftpserver
6969

7070
# Download some file
71-
if [ ! -f file.bin ]; then
72-
wget -O file.bin.tmp https://github.com/fclairamb/ftpserver/releases/download/v0.5/ftpserver-linux-amd64 && mv file.bin.tmp file.bin
71+
if [ ! -f kitty.jpg ]; then
72+
curl -o kitty.jpg.tmp https://placekitten.com/2048/2048 && mv kitty.jpg.tmp kitty.jpg
7373
fi
7474

75-
# Connecting to it and uploading a file
76-
ftp ftp://test:test@localhost:2121
77-
put file.bin
78-
quit
79-
ls -lh data/file.bin
75+
curl -v -T kitty.bin ftp://test:test@localhost:2121/
8076
```
8177

8278
## The driver

local-docker-test.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/sh -ex
2+
3+
docker rm -f ftpserver ||:
4+
5+
GOOS=linux GOARCH=amd64 go build
6+
docker build . -t test
7+
if [ ! -f kitty.jpg ]; then
8+
curl -o kitty.jpg.tmp https://placekitten.com/2048/2048 && mv kitty.jpg.tmp kitty.jpg
9+
fi
10+
docker run --name ftpserver -p 2121-2200:2121-2200 test &
11+
while ! nc -z localhost 2121 </dev/null; do sleep 1; done
12+
curl -v -T kitty.jpg ftp://test:test@localhost:2121/

main.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func main() {
4242
// The general idea here is that if you start it without any arg, you're probably doing a local quick&dirty run
4343
// possibly on a windows machine, so we're better of just using a default file name and create the file.
4444
if confFile == "" {
45-
confFile = "settings.toml"
45+
confFile = "settings_test.toml"
4646
autoCreate = true
4747
}
4848

@@ -102,8 +102,6 @@ func signalHandler() {
102102

103103
func confFileContent() []byte {
104104
str := `# ftpserver configuration file
105-
#
106-
# These are all the config parameters with their default values. If not present,
107105
108106
# Max number of control connections to accept
109107
# max_connections = 0
@@ -114,7 +112,7 @@ max_connections = 10
114112
# listen_addr="0.0.0.0:2121"
115113
116114
# Public host to expose in the passive connection
117-
# public_host = ""
115+
public_host = "127.0.0.1"
118116
119117
# Idle timeout time
120118
# idle_timeout = 900

prepare.sh

Lines changed: 0 additions & 13 deletions
This file was deleted.

sample/sample_driver.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ func (driver *MainDriver) getCertificate() (*tls.Certificate, error) {
200200
// WelcomeUser is called to send the very first welcome message
201201
func (driver *MainDriver) WelcomeUser(cc server.ClientContext) (string, error) {
202202
nbClients := atomic.AddInt32(&driver.nbClients, 1)
203-
if nbClients > driver.config.MaxConnections {
203+
if driver.config.MaxConnections != 0 && nbClients > driver.config.MaxConnections {
204204
return "Cannot accept any additional client", fmt.Errorf(
205205
"too many clients: %d > % d",
206206
driver.nbClients,
@@ -313,8 +313,11 @@ func (driver *ClientDriver) OpenFile(cc server.ClientContext, path string, flag
313313
if (flag & os.O_WRONLY) != 0 {
314314
flag |= os.O_CREATE
315315
if (flag & os.O_APPEND) == 0 {
316-
if err := os.Remove(path); err != nil {
317-
fmt.Println("Problem removing file", path, "err:", err)
316+
_, err := os.Stat(path)
317+
if !os.IsNotExist(err) {
318+
if err := os.Remove(path); err != nil {
319+
fmt.Println("Problem removing file", path, "err:", err)
320+
}
318321
}
319322
}
320323
}
@@ -415,7 +418,7 @@ func (f *virtualFile) Seek(n int64, w int) (int64, error) {
415418
}
416419

417420
func (f *virtualFile) Write(buffer []byte) (int, error) {
418-
return 0, nil
421+
return len(buffer), nil
419422
}
420423

421424
type virtualFileInfo struct {

server/handle_files.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,8 @@ func (c *clientHandler) transferFile(write bool, append bool) {
5454

5555
// Try to seek on it
5656
if c.ctxRest != 0 {
57-
if err == nil {
58-
if _, errSeek := file.Seek(c.ctxRest, 0); errSeek != nil {
59-
err = errSeek
60-
}
57+
if _, errSeek := file.Seek(c.ctxRest, 0); errSeek != nil {
58+
err = errSeek
6159
}
6260

6361
// Whatever happens we should reset the seek position
@@ -85,8 +83,13 @@ func (c *clientHandler) transferFile(write bool, append bool) {
8583
out = tr
8684
}
8785

88-
if _, errCopy := io.Copy(out, in); errCopy != nil && errCopy != io.EOF {
86+
if written, errCopy := io.Copy(out, in); errCopy != nil && errCopy != io.EOF {
8987
err = errCopy
88+
} else {
89+
c.logger.Debug(
90+
logKeyMsg, "Stream copy finished",
91+
"writtenBytes", written,
92+
)
9093
}
9194
}
9295
}

server/transfer_pasv.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,37 @@ func (c *clientHandler) handlePASV() error {
6363
portRange := c.server.settings.PassiveTransferPortRange
6464

6565
if portRange != nil {
66-
for start := portRange.Start; start < portRange.End; start++ {
67-
port := portRange.Start + rand.Intn(portRange.End-portRange.Start)
68-
laddr, err2 := net.ResolveTCPAddr("tcp", "0.0.0.0:"+fmt.Sprintf("%d", port))
66+
nbAttempts := portRange.End - portRange.Start
67+
68+
// Making sure we trying a reasonable amount of ports before giving up
69+
if nbAttempts < 10 {
70+
nbAttempts = 10
71+
} else if nbAttempts > 1000 {
72+
nbAttempts = 1000
73+
}
74+
75+
for i := 0; i < nbAttempts; i++ {
76+
port := portRange.Start + rand.Intn(portRange.End-portRange.Start+1)
77+
laddr, err2 := net.ResolveTCPAddr("tcp", fmt.Sprintf("0.0.0.0:%d", port))
6978

7079
if err2 != nil {
71-
continue
80+
c.logger.Error(logKeyMsg, "Problem resolving local port", "port", port)
81+
return err2
7282
}
7383

7484
tcpListener, err = net.ListenTCP("tcp", laddr)
7585
if err == nil {
7686
break
7787
}
7888
}
89+
if err != nil {
90+
c.logger.Warn(
91+
logKeyMsg, "Could not find any free port",
92+
"nbAttempts", nbAttempts,
93+
"portRangeStart", portRange.Start,
94+
"portRAngeEnd", portRange.End,
95+
)
96+
}
7997
} else {
8098
tcpListener, err = net.ListenTCP("tcp", addr)
8199
}

0 commit comments

Comments
 (0)