Skip to content

Commit

Permalink
Fix STOR of zero length files to ProFTPd with TLS
Browse files Browse the repository at this point in the history
Before this change, uploading a zero length file via TLS meant that
the tls.Handshake function was never called.

ProFTPd quite properly refuses to accept an empty TCP connection with
no TLS handshake as a zero length upload and gives the error

    425 Unable to build data connection: Operation not

See: https://forum.rclone.org/t/rclone-ftps-explicit-rclone-touch-empty-files-proftpd-unable-to-build-data-connection-operation-not-permitted/22522
  • Loading branch information
ncw committed Mar 1, 2021
1 parent 9aae4d1 commit a4e9650
Showing 1 changed file with 23 additions and 2 deletions.
25 changes: 23 additions & 2 deletions ftp.go
Original file line number Diff line number Diff line change
Expand Up @@ -681,9 +681,30 @@ func (c *ServerConn) StorFrom(path string, r io.Reader, offset uint64) error {
// the response and we cannot use the connection to send other commands.
// So we don't check io.Copy error and we return the error from
// ReadResponse so the user can see the real error
_, err = io.Copy(conn, r)
conn.Close()
var n int64
n, err = io.Copy(conn, r)

// If we wrote no bytes but got no error, make sure we call
// tls.Handshake on the connection as it won't get called
// unless Write() is called.
//
// ProFTP doesn't like this and returns "Unable to build data
// connection: Operation not permitted" when trying to upload
// an empty file without this.
if n == 0 && err == nil {
if do, ok := conn.(interface{ Handshake() error }); ok {
err = do.Handshake()
}
}

// Use io.Copy or Handshake error in preference to this one
closeErr := conn.Close()
if err == nil {
err = closeErr
}

// Read the response and use this error in preference to
// previous errors
_, _, respErr := c.conn.ReadResponse(StatusClosingDataConnection)
if respErr != nil {
err = respErr
Expand Down

0 comments on commit a4e9650

Please sign in to comment.