Implement FTPS implicit FTP over TLS
This commit is contained in:
		
							parent
							
								
									769512c448
								
							
						
					
					
						commit
						8c9122ed82
					
				@ -6,6 +6,7 @@ go:
 | 
			
		||||
  - 1.8.1
 | 
			
		||||
env:
 | 
			
		||||
  - FTP_SERVER=vsftpd
 | 
			
		||||
  - FTP_SERVER=vsftpd_implicit_tls
 | 
			
		||||
  - FTP_SERVER=proftpd
 | 
			
		||||
before_install:
 | 
			
		||||
- sudo $TRAVIS_BUILD_DIR/.travis/prepare.sh "$FTP_SERVER"
 | 
			
		||||
 | 
			
		||||
@ -1,18 +1,31 @@
 | 
			
		||||
#!/bin/sh -e
 | 
			
		||||
 | 
			
		||||
mkdir --mode 0777 -p /var/ftp/incoming
 | 
			
		||||
 | 
			
		||||
case "$1" in
 | 
			
		||||
  proftpd)
 | 
			
		||||
    mkdir -p /etc/proftpd/conf.d/
 | 
			
		||||
    cp $TRAVIS_BUILD_DIR/.travis/proftpd.conf /etc/proftpd/conf.d/
 | 
			
		||||
    apt-get install -qq "$1"
 | 
			
		||||
    ;;
 | 
			
		||||
  vsftpd)
 | 
			
		||||
    cp $TRAVIS_BUILD_DIR/.travis/vsftpd.conf /etc/vsftpd.conf
 | 
			
		||||
    apt-get install -qq "$1"
 | 
			
		||||
    ;;
 | 
			
		||||
  vsftpd_implicit_tls)
 | 
			
		||||
    openssl req \
 | 
			
		||||
    -new \
 | 
			
		||||
    -newkey rsa:1024 \
 | 
			
		||||
    -days 365 \
 | 
			
		||||
    -nodes \
 | 
			
		||||
    -x509 \
 | 
			
		||||
    -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=localhost" \
 | 
			
		||||
    -keyout /etc/ssl/certs/vsftpd.pem \
 | 
			
		||||
    -out /etc/ssl/certs/vsftpd.pem
 | 
			
		||||
    cp $TRAVIS_BUILD_DIR/.travis/vsftpd_implicit_tls.conf /etc/vsftpd.conf
 | 
			
		||||
    apt-get install -qq vsftpd
 | 
			
		||||
    ;;
 | 
			
		||||
  *)
 | 
			
		||||
    echo "unknown software: $1"
 | 
			
		||||
    exit 1
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
mkdir --mode 0777 -p /var/ftp/incoming
 | 
			
		||||
 | 
			
		||||
apt-get install -qq "$1"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										33
									
								
								.travis/vsftpd_implicit_tls.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								.travis/vsftpd_implicit_tls.conf
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
# Used by Travis CI
 | 
			
		||||
 | 
			
		||||
listen=NO
 | 
			
		||||
listen_ipv6=YES
 | 
			
		||||
 | 
			
		||||
write_enable=YES
 | 
			
		||||
dirmessage_enable=YES
 | 
			
		||||
secure_chroot_dir=/var/run/vsftpd/empty
 | 
			
		||||
 | 
			
		||||
anonymous_enable=YES
 | 
			
		||||
anon_root=/var/ftp
 | 
			
		||||
anon_upload_enable=YES
 | 
			
		||||
anon_mkdir_write_enable=YES
 | 
			
		||||
anon_other_write_enable=YES
 | 
			
		||||
anon_umask=022
 | 
			
		||||
 | 
			
		||||
force_local_logins_ssl=YES
 | 
			
		||||
force_local_data_ssl=YES
 | 
			
		||||
ssl_enable=YES
 | 
			
		||||
implicit_ssl=YES
 | 
			
		||||
listen_port=21
 | 
			
		||||
ssl_tlsv1=YES
 | 
			
		||||
ssl_sslv2=NO
 | 
			
		||||
ssl_sslv3=NO
 | 
			
		||||
rsa_cert_file=/etc/ssl/certs/vsftpd.pem
 | 
			
		||||
rsa_private_key_file=/etc/ssl/certs/vsftpd.pem
 | 
			
		||||
require_ssl_reuse=NO
 | 
			
		||||
ssl_ciphers=HIGH
 | 
			
		||||
xferlog_enable=yes
 | 
			
		||||
log_ftp_protocol=true
 | 
			
		||||
xferlog_file=/var/log/vsftpd.log
 | 
			
		||||
debug_ssl=true
 | 
			
		||||
allow_anon_ssl=true
 | 
			
		||||
@ -2,8 +2,10 @@ package ftp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/textproto"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
@ -14,20 +16,39 @@ const (
 | 
			
		||||
	testDir  = "mydir"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func isTLSServer() bool {
 | 
			
		||||
	return os.Getenv("FTP_SERVER") == "vsftpd_implicit_tls"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getConnection() (*ServerConn, error) {
 | 
			
		||||
	if isTLSServer() {
 | 
			
		||||
		return DialImplicitTLS("localhost:21", &tls.Config{InsecureSkipVerify: true})
 | 
			
		||||
	} else {
 | 
			
		||||
		return DialTimeout("localhost:21", 5*time.Second)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestConnPASV(t *testing.T) {
 | 
			
		||||
	testConn(t, true)
 | 
			
		||||
	testConn(t, true, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestConnEPSV(t *testing.T) {
 | 
			
		||||
	testConn(t, false)
 | 
			
		||||
	testConn(t, false, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testConn(t *testing.T, disableEPSV bool) {
 | 
			
		||||
func TestConnTLS(t *testing.T) {
 | 
			
		||||
	if !isTLSServer() {
 | 
			
		||||
		t.Skip("skipping test in non TLS server env.")
 | 
			
		||||
	}
 | 
			
		||||
	testConn(t, false, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testConn(t *testing.T, disableEPSV bool, implicitTLS bool) {
 | 
			
		||||
	if testing.Short() {
 | 
			
		||||
		t.Skip("skipping test in short mode.")
 | 
			
		||||
	}
 | 
			
		||||
	c, err := getConnection()
 | 
			
		||||
 | 
			
		||||
	c, err := DialTimeout("localhost:21", 5*time.Second)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
@ -207,6 +228,9 @@ func TestConnIPv6(t *testing.T) {
 | 
			
		||||
	if testing.Short() {
 | 
			
		||||
		t.Skip("skipping test in short mode.")
 | 
			
		||||
	}
 | 
			
		||||
	if isTLSServer() {
 | 
			
		||||
		t.Skip("skipping test in TLS mode.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c, err := DialTimeout("[::1]:21", 5*time.Second)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -228,7 +252,7 @@ func TestConnIPv6(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
// TestConnect tests the legacy Connect function
 | 
			
		||||
func TestConnect(t *testing.T) {
 | 
			
		||||
	if testing.Short() {
 | 
			
		||||
	if testing.Short() || isTLSServer() {
 | 
			
		||||
		t.Skip("skipping test in short mode.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -244,6 +268,9 @@ func TestTimeout(t *testing.T) {
 | 
			
		||||
	if testing.Short() {
 | 
			
		||||
		t.Skip("skipping test in short mode.")
 | 
			
		||||
	}
 | 
			
		||||
	if isTLSServer() {
 | 
			
		||||
		t.Skip("skipping test in TLS mode.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c, err := DialTimeout("localhost:2121", 1*time.Second)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
@ -251,13 +278,12 @@ func TestTimeout(t *testing.T) {
 | 
			
		||||
		c.Quit()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestWrongLogin(t *testing.T) {
 | 
			
		||||
	if testing.Short() {
 | 
			
		||||
		t.Skip("skipping test in short mode.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c, err := DialTimeout("localhost:21", 5*time.Second)
 | 
			
		||||
	c, err := getConnection()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
@ -273,7 +299,7 @@ func TestDeleteDirRecur(t *testing.T) {
 | 
			
		||||
	if testing.Short() {
 | 
			
		||||
		t.Skip("skipping test in short mode.")
 | 
			
		||||
	}
 | 
			
		||||
	c, err := DialTimeout("localhost:21", 5*time.Second)
 | 
			
		||||
	c, err := getConnection()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
@ -355,7 +381,7 @@ func TestFileDeleteDirRecur(t *testing.T) {
 | 
			
		||||
		t.Skip("skipping test in short mode.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c, err := DialTimeout("localhost:21", 5*time.Second)
 | 
			
		||||
	c, err := getConnection()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
@ -414,7 +440,7 @@ func TestMissingFolderDeleteDirRecur(t *testing.T) {
 | 
			
		||||
		t.Skip("skipping test in short mode.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c, err := DialTimeout("localhost:21", 5*time.Second)
 | 
			
		||||
	c, err := getConnection()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								ftp.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								ftp.go
									
									
									
									
									
								
							@ -5,6 +5,7 @@ package ftp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
@ -62,6 +63,15 @@ func Dial(addr string) (*ServerConn, error) {
 | 
			
		||||
	return DialTimeout(addr, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial a ftps server with implicit TLS
 | 
			
		||||
func DialImplicitTLS(addr string, config *tls.Config) (*ServerConn, error) {
 | 
			
		||||
	tconn, err := tls.Dial("tcp", addr, config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dialServer(tconn, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DialTimeout initializes the connection to the specified ftp server address.
 | 
			
		||||
//
 | 
			
		||||
// It is generally followed by a call to Login() as most FTP commands require
 | 
			
		||||
@ -71,7 +81,10 @@ func DialTimeout(addr string, timeout time.Duration) (*ServerConn, error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dialServer(tconn, timeout)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func dialServer(tconn net.Conn, timeout time.Duration) (*ServerConn, error) {
 | 
			
		||||
	// Use the resolved IP address in case addr contains a domain name
 | 
			
		||||
	// If we use the domain name, we might not resolve to the same IP.
 | 
			
		||||
	remoteAddr := tconn.RemoteAddr().String()
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user