245 lines · 5.2 KB · Go
RawDownload
// Copyright 2018 The goftp Authors. All rights reserved.// Use of this source code is governed by a MIT-style// license that can be found in the LICENSE file. package server import (	"crypto/tls"	"io"	"net"	"os"	"runtime"	"strconv"	"strings"	"sync"	"syscall") // DataSocket describes a data socket is used to send non-control data between the client and// server.type DataSocket interface {	Host() string 	Port() int 	// the standard io.Reader interface	Read(p []byte) (n int, err error) 	// the standard io.ReaderFrom interface	ReadFrom(r io.Reader) (int64, error) 	// the standard io.Writer interface	Write(p []byte) (n int, err error) 	// the standard io.Closer interface	Close() error} type ftpActiveSocket struct {	conn   *net.TCPConn	host   string	port   int	logger Logger} func newActiveSocket(remote string, port int, logger Logger, sessionID string) (DataSocket, error) {	connectTo := net.JoinHostPort(remote, strconv.Itoa(port)) 	logger.Print(sessionID, "Opening active data connection to "+connectTo) 	raddr, err := net.ResolveTCPAddr("tcp", connectTo) 	if err != nil {		logger.Print(sessionID, err)		return nil, err	} 	tcpConn, err := net.DialTCP("tcp", nil, raddr) 	if err != nil {		logger.Print(sessionID, err)		return nil, err	} 	socket := new(ftpActiveSocket)	socket.conn = tcpConn	socket.host = remote	socket.port = port	socket.logger = logger 	return socket, nil} func (socket *ftpActiveSocket) Host() string {	return socket.host} func (socket *ftpActiveSocket) Port() int {	return socket.port} func (socket *ftpActiveSocket) Read(p []byte) (n int, err error) {	return socket.conn.Read(p)} func (socket *ftpActiveSocket) ReadFrom(r io.Reader) (int64, error) {	return socket.conn.ReadFrom(r)} func (socket *ftpActiveSocket) Write(p []byte) (n int, err error) {	return socket.conn.Write(p)} func (socket *ftpActiveSocket) Close() error {	return socket.conn.Close()} type ftpPassiveSocket struct {	conn      net.Conn	port      int	host      string	ingress   chan []byte	egress    chan []byte	logger    Logger	lock      sync.Mutex // protects conn and err	err       error	tlsConfig *tls.Config} // Detect if an error is "bind: address already in use"//// Originally from https://stackoverflow.com/a/52152912/164234func isErrorAddressAlreadyInUse(err error) bool {	errOpError, ok := err.(*net.OpError)	if !ok {		return false	}	errSyscallError, ok := errOpError.Err.(*os.SyscallError)	if !ok {		return false	}	errErrno, ok := errSyscallError.Err.(syscall.Errno)	if !ok {		return false	}	if errErrno == syscall.EADDRINUSE {		return true	}	const WSAEADDRINUSE = 10048	if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE {		return true	}	return false} func newPassiveSocket(host string, port func() int, logger Logger, sessionID string, tlsConfig *tls.Config) (DataSocket, error) {	socket := new(ftpPassiveSocket)	socket.ingress = make(chan []byte)	socket.egress = make(chan []byte)	socket.logger = logger	socket.host = host	socket.tlsConfig = tlsConfig	const retries = 10	var err error	for i := 1; i <= retries; i++ {		socket.port = port()		err = socket.GoListenAndServe(sessionID)		if err != nil && socket.port != 0 && isErrorAddressAlreadyInUse(err) {			// choose a different port on error already in use			continue		}		break	}	return socket, err} func (socket *ftpPassiveSocket) Host() string {	return socket.host} func (socket *ftpPassiveSocket) Port() int {	return socket.port} func (socket *ftpPassiveSocket) Read(p []byte) (n int, err error) {	socket.lock.Lock()	defer socket.lock.Unlock()	if socket.err != nil {		return 0, socket.err	}	return socket.conn.Read(p)} func (socket *ftpPassiveSocket) ReadFrom(r io.Reader) (int64, error) {	socket.lock.Lock()	defer socket.lock.Unlock()	if socket.err != nil {		return 0, socket.err	} 	// For normal TCPConn, this will use sendfile syscall; if not,	// it will just downgrade to normal read/write procedure	return io.Copy(socket.conn, r)} func (socket *ftpPassiveSocket) Write(p []byte) (n int, err error) {	socket.lock.Lock()	defer socket.lock.Unlock()	if socket.err != nil {		return 0, socket.err	}	return socket.conn.Write(p)} func (socket *ftpPassiveSocket) Close() error {	socket.lock.Lock()	defer socket.lock.Unlock()	if socket.conn != nil {		return socket.conn.Close()	}	return nil} func (socket *ftpPassiveSocket) GoListenAndServe(sessionID string) (err error) {	laddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort("", strconv.Itoa(socket.port)))	if err != nil {		socket.logger.Print(sessionID, err)		return	} 	var listener net.Listener	listener, err = net.ListenTCP("tcp", laddr)	if err != nil {		socket.logger.Print(sessionID, err)		return	} 	add := listener.Addr()	parts := strings.Split(add.String(), ":")	port, err := strconv.Atoi(parts[len(parts)-1])	if err != nil {		socket.logger.Print(sessionID, err)		return	} 	socket.port = port	if socket.tlsConfig != nil {		listener = tls.NewListener(listener, socket.tlsConfig)	} 	socket.lock.Lock()	go func() {		defer socket.lock.Unlock() 		conn, err := listener.Accept()		if err != nil {			socket.err = err			return		}		socket.err = nil		socket.conn = conn		_ = listener.Close()	}()	return nil}