Implement FTPS implicit FTP over TLS
This commit is contained in:
		
							parent
							
								
									769512c448
								
							
						
					
					
						commit
						8c9122ed82
					
				@ -6,6 +6,7 @@ go:
 | 
				
			|||||||
  - 1.8.1
 | 
					  - 1.8.1
 | 
				
			||||||
env:
 | 
					env:
 | 
				
			||||||
  - FTP_SERVER=vsftpd
 | 
					  - FTP_SERVER=vsftpd
 | 
				
			||||||
 | 
					  - FTP_SERVER=vsftpd_implicit_tls
 | 
				
			||||||
  - FTP_SERVER=proftpd
 | 
					  - FTP_SERVER=proftpd
 | 
				
			||||||
before_install:
 | 
					before_install:
 | 
				
			||||||
- sudo $TRAVIS_BUILD_DIR/.travis/prepare.sh "$FTP_SERVER"
 | 
					- sudo $TRAVIS_BUILD_DIR/.travis/prepare.sh "$FTP_SERVER"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,18 +1,31 @@
 | 
				
			|||||||
#!/bin/sh -e
 | 
					#!/bin/sh -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mkdir --mode 0777 -p /var/ftp/incoming
 | 
				
			||||||
 | 
					
 | 
				
			||||||
case "$1" in
 | 
					case "$1" in
 | 
				
			||||||
  proftpd)
 | 
					  proftpd)
 | 
				
			||||||
    mkdir -p /etc/proftpd/conf.d/
 | 
					    mkdir -p /etc/proftpd/conf.d/
 | 
				
			||||||
    cp $TRAVIS_BUILD_DIR/.travis/proftpd.conf /etc/proftpd/conf.d/
 | 
					    cp $TRAVIS_BUILD_DIR/.travis/proftpd.conf /etc/proftpd/conf.d/
 | 
				
			||||||
 | 
					    apt-get install -qq "$1"
 | 
				
			||||||
    ;;
 | 
					    ;;
 | 
				
			||||||
  vsftpd)
 | 
					  vsftpd)
 | 
				
			||||||
    cp $TRAVIS_BUILD_DIR/.travis/vsftpd.conf /etc/vsftpd.conf
 | 
					    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"
 | 
					    echo "unknown software: $1"
 | 
				
			||||||
    exit 1
 | 
					    exit 1
 | 
				
			||||||
esac
 | 
					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 (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"crypto/tls"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/textproto"
 | 
						"net/textproto"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@ -14,20 +16,39 @@ const (
 | 
				
			|||||||
	testDir  = "mydir"
 | 
						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) {
 | 
					func TestConnPASV(t *testing.T) {
 | 
				
			||||||
	testConn(t, true)
 | 
						testConn(t, true, false)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestConnEPSV(t *testing.T) {
 | 
					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() {
 | 
						if testing.Short() {
 | 
				
			||||||
		t.Skip("skipping test in short mode.")
 | 
							t.Skip("skipping test in short mode.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						c, err := getConnection()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := DialTimeout("localhost:21", 5*time.Second)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -207,6 +228,9 @@ func TestConnIPv6(t *testing.T) {
 | 
				
			|||||||
	if testing.Short() {
 | 
						if testing.Short() {
 | 
				
			||||||
		t.Skip("skipping test in short mode.")
 | 
							t.Skip("skipping test in short mode.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if isTLSServer() {
 | 
				
			||||||
 | 
							t.Skip("skipping test in TLS mode.")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := DialTimeout("[::1]:21", 5*time.Second)
 | 
						c, err := DialTimeout("[::1]:21", 5*time.Second)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -228,7 +252,7 @@ func TestConnIPv6(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// TestConnect tests the legacy Connect function
 | 
					// TestConnect tests the legacy Connect function
 | 
				
			||||||
func TestConnect(t *testing.T) {
 | 
					func TestConnect(t *testing.T) {
 | 
				
			||||||
	if testing.Short() {
 | 
						if testing.Short() || isTLSServer() {
 | 
				
			||||||
		t.Skip("skipping test in short mode.")
 | 
							t.Skip("skipping test in short mode.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -244,6 +268,9 @@ func TestTimeout(t *testing.T) {
 | 
				
			|||||||
	if testing.Short() {
 | 
						if testing.Short() {
 | 
				
			||||||
		t.Skip("skipping test in short mode.")
 | 
							t.Skip("skipping test in short mode.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if isTLSServer() {
 | 
				
			||||||
 | 
							t.Skip("skipping test in TLS mode.")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := DialTimeout("localhost:2121", 1*time.Second)
 | 
						c, err := DialTimeout("localhost:2121", 1*time.Second)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
@ -251,13 +278,12 @@ func TestTimeout(t *testing.T) {
 | 
				
			|||||||
		c.Quit()
 | 
							c.Quit()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestWrongLogin(t *testing.T) {
 | 
					func TestWrongLogin(t *testing.T) {
 | 
				
			||||||
	if testing.Short() {
 | 
						if testing.Short() {
 | 
				
			||||||
		t.Skip("skipping test in short mode.")
 | 
							t.Skip("skipping test in short mode.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := DialTimeout("localhost:21", 5*time.Second)
 | 
						c, err := getConnection()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -273,7 +299,7 @@ func TestDeleteDirRecur(t *testing.T) {
 | 
				
			|||||||
	if testing.Short() {
 | 
						if testing.Short() {
 | 
				
			||||||
		t.Skip("skipping test in short mode.")
 | 
							t.Skip("skipping test in short mode.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	c, err := DialTimeout("localhost:21", 5*time.Second)
 | 
						c, err := getConnection()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -355,7 +381,7 @@ func TestFileDeleteDirRecur(t *testing.T) {
 | 
				
			|||||||
		t.Skip("skipping test in short mode.")
 | 
							t.Skip("skipping test in short mode.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := DialTimeout("localhost:21", 5*time.Second)
 | 
						c, err := getConnection()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -414,7 +440,7 @@ func TestMissingFolderDeleteDirRecur(t *testing.T) {
 | 
				
			|||||||
		t.Skip("skipping test in short mode.")
 | 
							t.Skip("skipping test in short mode.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := DialTimeout("localhost:21", 5*time.Second)
 | 
						c, err := getConnection()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								ftp.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								ftp.go
									
									
									
									
									
								
							@ -5,6 +5,7 @@ package ftp
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bufio"
 | 
						"bufio"
 | 
				
			||||||
 | 
						"crypto/tls"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
@ -62,6 +63,15 @@ func Dial(addr string) (*ServerConn, error) {
 | 
				
			|||||||
	return DialTimeout(addr, 0)
 | 
						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.
 | 
					// DialTimeout initializes the connection to the specified ftp server address.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// It is generally followed by a call to Login() as most FTP commands require
 | 
					// 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 {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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
 | 
						// 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.
 | 
						// If we use the domain name, we might not resolve to the same IP.
 | 
				
			||||||
	remoteAddr := tconn.RemoteAddr().String()
 | 
						remoteAddr := tconn.RemoteAddr().String()
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user