Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
af889dc1ad |
110
conn_test.go
110
conn_test.go
@ -2,7 +2,7 @@ package ftp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"crypto/tls"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
@ -14,44 +14,74 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
certPem = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
|
||||||
|
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
|
||||||
|
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
|
||||||
|
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
|
||||||
|
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
|
||||||
|
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
|
||||||
|
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
|
||||||
|
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
|
||||||
|
6MF9+Yw1Yy0t
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
keyPem = `-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
|
||||||
|
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
|
||||||
|
-----END EC PRIVATE KEY-----`
|
||||||
|
)
|
||||||
|
|
||||||
type ftpMock struct {
|
type ftpMock struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
address string
|
address string
|
||||||
modtime string // no-time, std-time, vsftpd
|
modtime string // no-time, std-time, vsftpd
|
||||||
listener *net.TCPListener
|
listener net.Listener
|
||||||
proto *textproto.Conn
|
proto *textproto.Conn
|
||||||
commands []string // list of received commands
|
commands []string // list of received commands
|
||||||
lastFull string // full last command
|
lastFull string // full last command
|
||||||
rest int
|
rest int
|
||||||
fileCont *bytes.Buffer
|
fileCont *bytes.Buffer
|
||||||
dataConn *mockDataConn
|
dataConn *mockDataConn
|
||||||
|
tlsConfig *tls.Config
|
||||||
sync.WaitGroup
|
sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFtpMock returns a mock implementation of a FTP server
|
// newFtpMock returns a mock implementation of a FTP server
|
||||||
// For simplication, a mock instance only accepts a signle connection and terminates afer
|
// For simplication, a mock instance only accepts a signle connection and terminates afer
|
||||||
func newFtpMock(t *testing.T, address string) (*ftpMock, error) {
|
func newFtpMock(t *testing.T, address string) (*ftpMock, error) {
|
||||||
return newFtpMockExt(t, address, "no-time")
|
return newFtpMockExt(t, address, false, "no-time")
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFtpMockExt(t *testing.T, address, modtime string) (*ftpMock, error) {
|
func newFtpMockExt(t *testing.T, address string, ssl bool, modtime string) (*ftpMock, error) {
|
||||||
var err error
|
|
||||||
mock := &ftpMock{
|
mock := &ftpMock{
|
||||||
t: t,
|
t: t,
|
||||||
address: address,
|
address: address,
|
||||||
modtime: modtime,
|
modtime: modtime,
|
||||||
}
|
}
|
||||||
|
|
||||||
l, err := net.Listen("tcp", address+":0")
|
if ssl {
|
||||||
|
cert, err := tls.X509KeyPair([]byte(certPem), []byte(keyPem))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tcpListener, ok := l.(*net.TCPListener)
|
mock.tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||||
if !ok {
|
l, err := tls.Listen("tcp", address+":0", mock.tlsConfig)
|
||||||
return nil, errors.New("listener is not a net.TCPListener")
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.listener = l
|
||||||
|
} else {
|
||||||
|
l, err := net.Listen("tcp", address+":0")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mock.listener = l
|
||||||
|
|
||||||
}
|
}
|
||||||
mock.listener = tcpListener
|
|
||||||
|
|
||||||
go mock.listen()
|
go mock.listen()
|
||||||
|
|
||||||
@ -260,6 +290,10 @@ func (mock *ftpMock) listen() {
|
|||||||
case "QUIT":
|
case "QUIT":
|
||||||
mock.printfLine("221 Goodbye.")
|
mock.printfLine("221 Goodbye.")
|
||||||
return
|
return
|
||||||
|
case "PBSZ":
|
||||||
|
mock.printfLine("200 PBSZ ok.")
|
||||||
|
case "PROT":
|
||||||
|
mock.printfLine("200 PROT ok.")
|
||||||
default:
|
default:
|
||||||
mock.printfLine("500 Unknown command %s.", cmdParts[0])
|
mock.printfLine("500 Unknown command %s.", cmdParts[0])
|
||||||
}
|
}
|
||||||
@ -283,7 +317,7 @@ func (mock *ftpMock) closeDataConn() {
|
|||||||
|
|
||||||
type mockDataConn struct {
|
type mockDataConn struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
listener *net.TCPListener
|
listener net.Listener
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
// WaitGroup is done when conn is accepted and stored
|
// WaitGroup is done when conn is accepted and stored
|
||||||
sync.WaitGroup
|
sync.WaitGroup
|
||||||
@ -313,16 +347,7 @@ func (mock *ftpMock) listenDataConn() (int64, error) {
|
|||||||
mock.closeDataConn()
|
mock.closeDataConn()
|
||||||
|
|
||||||
l, err := net.Listen("tcp", mock.address+":0")
|
l, err := net.Listen("tcp", mock.address+":0")
|
||||||
if err != nil {
|
addr := l.Addr().String()
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tcpListener, ok := l.(*net.TCPListener)
|
|
||||||
if !ok {
|
|
||||||
return 0, errors.New("listener is not a net.TCPListener")
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := tcpListener.Addr().String()
|
|
||||||
|
|
||||||
_, port, err := net.SplitHostPort(addr)
|
_, port, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -336,7 +361,7 @@ func (mock *ftpMock) listenDataConn() (int64, error) {
|
|||||||
|
|
||||||
dataConn := &mockDataConn{
|
dataConn := &mockDataConn{
|
||||||
t: mock.t,
|
t: mock.t,
|
||||||
listener: tcpListener,
|
listener: l,
|
||||||
}
|
}
|
||||||
dataConn.Add(1)
|
dataConn.Add(1)
|
||||||
|
|
||||||
@ -345,9 +370,20 @@ func (mock *ftpMock) listenDataConn() (int64, error) {
|
|||||||
conn, err := dataConn.listener.Accept()
|
conn, err := dataConn.listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// mock.t.Fatalf("can not accept data conn: %s", err)
|
// mock.t.Fatalf("can not accept data conn: %s", err)
|
||||||
|
dataConn.Done()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mock.tlsConfig != nil {
|
||||||
|
tlsConn := tls.Server(conn, mock.tlsConfig)
|
||||||
|
if err := tlsConn.Handshake(); err != nil {
|
||||||
|
dataConn.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = tlsConn
|
||||||
|
}
|
||||||
|
|
||||||
dataConn.conn = conn
|
dataConn.conn = conn
|
||||||
dataConn.Done()
|
dataConn.Done()
|
||||||
}()
|
}()
|
||||||
@ -385,7 +421,7 @@ func openConn(t *testing.T, addr string, options ...DialOption) (*ftpMock, *Serv
|
|||||||
}
|
}
|
||||||
|
|
||||||
func openConnExt(t *testing.T, addr, modtime string, options ...DialOption) (*ftpMock, *ServerConn) {
|
func openConnExt(t *testing.T, addr, modtime string, options ...DialOption) (*ftpMock, *ServerConn) {
|
||||||
mock, err := newFtpMockExt(t, addr, modtime)
|
mock, err := newFtpMockExt(t, addr, false, modtime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -431,3 +467,31 @@ func TestConn6(t *testing.T) {
|
|||||||
mock, c := openConn(t, "[::1]")
|
mock, c := openConn(t, "[::1]")
|
||||||
closeConn(t, mock, c, nil)
|
closeConn(t, mock, c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConnTLS(t *testing.T) {
|
||||||
|
mock, err := newFtpMockExt(t, "127.0.0.1", true, "std-time")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConf := &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := Dial(mock.Addr(), DialWithTLS(tlsConf), DialWithTimeout(1*time.Second))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.Login("anonymous", "anonymous")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.List(".")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
closeConn(t, mock, c, []string{"PBSZ", "PROT", "EPSV", "LIST"})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user