ftpserver

package module
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2020 License: MIT Imports: 14 Imported by: 40

README

Golang FTP Server library

Build Go Report Card GoDoc

This library allows to easily build a simple and fully-featured FTP server using afero as the backend filesystem.

If you're interested in a fully featured FTP server, you should use ftpserver.

Current status of the project

Features
  • Uploading and downloading files
  • Directory listing (LIST + MLST)
  • File and directory deletion and renaming
  • TLS support (AUTH + PROT)
  • File download/upload resume support (REST)
  • Passive socket connections (EPSV and PASV commands)
  • Active socket connections (PORT command)
  • Small memory footprint
  • Clean code: No sync, no sleep, no panic
  • Uses only the standard library except for:
    • afero for generic file systems handling
    • go-kit log (optional) for logging
  • Supported extensions:
    • AUTH - Control session protection
    • AUTH TLS - TLS session
    • PROT - Transfer protection
    • MDTM - File Modification Time
    • SIZE - Size of a file
    • REST - Restart of interrupted transfer
    • MLST - Simple file listing for machine processing
    • MLSD - Directory listing for machine processing

Quick test

We are providing a server so that you can test how the library behaves.

# Get and install the server
go install github.com/fclairamb/ftpserver

# Create a storage dir
mkdir -p data

ftpserver -data data &

# Download some file
if [ ! -f file.bin ]; then
    wget -O file.bin.tmp https://github.com/fclairamb/ftpserver/releases/download/v0.5/ftpserver-linux-amd64 && mv file.bin.tmp file.bin
fi

# Connecting to the server and uploading the file
ftp ftp://test:test@localhost:2121
put file.bin
quit
ls -lh data/file.bin

Quick test with docker

There's also a containerized version of the demo server (15MB, based on alpine).

# Creating a storage dir
mkdir -p data

# Starting the sample FTP server
docker run --rm -d -p 2121-2130:2121-2130 -v $(pwd)/data:/data fclairamb/ftpserver

# Download some file
if [ ! -f kitty.jpg ]; then
    curl -o kitty.jpg.tmp https://placekitten.com/2048/2048 && mv kitty.jpg.tmp kitty.jpg
fi

curl -v -T kitty.bin ftp://test:test@localhost:2121/

The driver

The API

The API is directly based on afero.

// ServerDriver handles the authentication and ClientHandlingDriver selection
type ServerDriver interface {
	// Load some general settings around the server setup
	GetSettings() *Settings

	// WelcomeUser is called to send the very first welcome message
	WelcomeUser(cc ClientContext) (string, error)

	// UserLeft is called when the user disconnects, even if he never authenticated
	UserLeft(cc ClientContext)

	// AuthUser authenticates the user and selects an handling driver
	AuthUser(cc ClientContext, user, pass string) (afero.Fs, error)

	// GetCertificate returns a TLS Certificate to use
	// The certificate could frequently change if we use something like "let's encrypt"
	GetTLSConfig() (*tls.Config, error)
}

// ClientContext is implemented on the server side to provide some access to few data around the client
type ClientContext interface {
	// Get current path
	Path() string

	// SetDebug activates the debugging of this connection commands
	SetDebug(debug bool)

	// Debug returns the current debugging status of this connection commands
	Debug() bool
	
	// Client's ID on the server
	ID() uint32

	// Client's address
	RemoteAddr() net.Addr
}

// Settings define all the server settings
type Settings struct {
	ListenHost     string     // Host to receive connections on
	ListenPort     int        // Port to listen on
	PublicHost     string     // Public IP to expose (only an IP address is accepted at this stage)
	MaxConnections int        // Max number of connections to accept
	DataPortRange  *PortRange // Port Range for data connections. Random one will be used if not specified
}
Sample implementation

Have a look at the sample driver. It shows how you can plug your FTP server to something else, in this case your file system.

Sample run

$ ftp ftp://a:a@localhost:2121
Trying ::1...
Connected to localhost.
220 Welcome on https://github.com/fclairamb/ftpserver
331 OK
230 Password ok, continue
Remote system type is UNIX.
Using binary mode to transfer files.
200 Type set to binary
ftp> put iMX7D_RM_Rev_B.pdf 
local: iMX7D_RM_Rev_B.pdf remote: iMX7D_RM_Rev_B.pdf
229 Entering Extended Passive Mode (|||62362|)
150 Using transfer connection
100% |******************************************************************************************************************************************************************| 44333 KiB  635.92 MiB/s    00:00 ETA
226 OK, received 45397173 bytes
45397173 bytes sent in 00:00 (538.68 MiB/s)
ftp> cd virtual
250 CD worked on /virtual
ftp> ls
229 Entering Extended Passive Mode (|||62369|)
150 Using transfer connection
-rw-rw-rw- 1 ftp ftp         1024 Sep 28 01:44 localpath.txt
-rw-rw-rw- 1 ftp ftp         2048 Sep 28 01:44 file2.txt

226 Closing data connection, sent some bytes
ftp> get localpath.txt
local: localpath.txt remote: localpath.txt
229 Entering Extended Passive Mode (|||62371|)
150 Using transfer connection
    67      241.43 KiB/s 
226 OK, sent 67 bytes
67 bytes received in 00:00 (160.36 KiB/s)
ftp> ^D
221 Goodbye
$ more localpath.txt 
/var/folders/vk/vgsfkf9975xfrc4_fk102g200000gn/T/ftpserver020090599
$ shasum /var/folders/vk/vgsfkf9975xfrc4_fk102g200000gn/T/ftpserver020090599/iMX7D_RM_Rev_B.pdf 
03b3686b31867fb14d3f3a61e20d28a029883a32  /var/folders/vk/vgsfkf9975xfrc4_fk102g200000gn/T/ftpserver020090599/iMX7D_RM_Rev_B.pdf
$ more localpath.txt 
$ shasum iMX7D_RM_Rev_B.pdf 
03b3686b31867fb14d3f3a61e20d28a029883a32  iMX7D_RM_Rev_B.pdf

History of the project

I wanted to make a system which would accept files through FTP and redirect them to something else. Go seemed like the obvious choice and it seemed there was a lot of libraries available but it turns out none of them were in a useable state.

That's why I forked from this last one.

Documentation

Overview

Package ftpserver provides all the tools to build your own FTP server: The core library and the driver.

Package ftpserver provides all the tools to build your own FTP server: The core library and the driver.

Package ftpserver provides all the tools to build your own FTP server: The core library and the driver.

Package ftpserver provides all the tools to build your own FTP server: The core library and the driver.

Package ftpserver provides all the tools to build your own FTP server: The core library and the driver.

Package ftpserver provides all the tools to build your own FTP server: The core library and the driver.

Package ftpserver provides all the tools to build your own FTP server: The core library and the driver.

Package ftpserver provides all the tools to build your own FTP server: The core library and the driver.

Package ftpserver provides all the tools to build your own FTP server: The core library and the driver.

Package ftpserver provides all the tools to build your own FTP server: The core library and the driver.

Index

Constants

View Source
const (
	// 100 Series - The requested action is being initiated, expect another reply before
	// proceeding with a new command.
	StatusFileStatusOK = 150 // RFC 959, 4.2.1

	// 200 Series - The requested action has been successfully completed.
	StatusOK                 = 200 // RFC 959, 4.2.1
	StatusNotImplemented     = 202 // RFC 959, 4.2.1
	StatusSystemStatus       = 211 // RFC 959, 4.2.1
	StatusDirectoryStatus    = 212 // RFC 959, 4.2.1
	StatusFileStatus         = 213 // RFC 959, 4.2.1
	StatusHelpMessage        = 214 // RFC 959, 4.2.1
	StatusSystemType         = 215 // RFC 959, 4.2.1
	StatusServiceReady       = 220 // RFC 959, 4.2.1
	StatusClosingControlConn = 221 // RFC 959, 4.2.1
	StatusClosingDataConn    = 226 // RFC 959, 4.2.1
	StatusEnteringPASV       = 227 // RFC 959, 4.2.1
	StatusEnteringEPSV       = 229 // RFC 2428, 3
	StatusUserLoggedIn       = 230 // RFC 959, 4.2.1
	StatusAuthAccepted       = 234 // RFC 2228, 3
	StatusFileOK             = 250 // RFC 959, 4.2.1
	StatusPathCreated        = 257 // RFC 959, 4.2.1

	// 300 Series - The command has been accepted, but the requested action is on hold,
	// pending receipt of further information.
	StatusUserOK            = 331 // RFC 959, 4.2.1
	StatusFileActionPending = 350 // RFC 959, 4.2.1

	// 400 Series - The command was not accepted and the requested action did not take place,
	// but the error condition is temporary and the action may be requested again.
	StatusServiceNotAvailable = 421 // RFC 959, 4.2.1
	StatusFileActionNotTaken  = 450 // RFC 959, 4.2.1

	// 500 Series - Syntax error, command unrecognized and the requested action did not take
	// place. This may include errors such as command line too long.
	StatusSyntaxErrorNotRecognised = 500 // RFC 959, 4.2.1
	StatusSyntaxErrorParameters    = 501 // RFC 959, 4.2.1
	StatusCommandNotImplemented    = 502 // RFC 959, 4.2.1
	StatusNotLoggedIn              = 530 // RFC 959, 4.2.1
	StatusActionNotTaken           = 550 // RFC 959, 4.2.1
)

Status codes as documented by: https://tools.ietf.org/html/rfc959 https://tools.ietf.org/html/rfc2428 https://tools.ietf.org/html/rfc2228

Variables

View Source
var (
	// ErrNotListening is returned when we are performing an action that is only valid while listening
	ErrNotListening = errors.New("we aren't listening")
)

Functions

This section is empty.

Types

type ClientContext

type ClientContext interface {
	// Path provides the path of the current connection
	Path() string

	// SetDebug activates the debugging of this connection commands
	SetDebug(debug bool)

	// Debug returns the current debugging status of this connection commands
	Debug() bool

	// Client's ID on the server
	ID() uint32

	// Client's address
	RemoteAddr() net.Addr

	// Servers's address
	LocalAddr() net.Addr
}

ClientContext is implemented on the server side to provide some access to few data around the client

type ClientDriver

type ClientDriver interface {
	afero.Fs
}

ClientDriver is the base FS implementation that allows to manipulate files

type ClientDriverExtensionAllocate

type ClientDriverExtensionAllocate interface {

	// AllocateSpace reserves the space necessary to upload files
	AllocateSpace(size int) error
}

ClientDriverExtensionAllocate is an extension to allow to support the allocation command

type ClientDriverExtensionChown

type ClientDriverExtensionChown interface {

	// Chown changes the owner of a file
	Chown(name string, user string, group string) error
}

ClientDriverExtensionChown is an extension to allow to support the chown command

type CommandDescription

type CommandDescription struct {
	Open bool                       // Open to clients without auth
	Fn   func(*clientHandler) error // Function to handle it
}

CommandDescription defines which function should be used and if it should be open to anyone or only logged in users

type FtpServer

type FtpServer struct {
	Logger log.Logger // Go-Kit logger
	// contains filtered or unexported fields
}

FtpServer is where everything is stored We want to keep it as simple as possible

func NewFtpServer

func NewFtpServer(driver MainDriver) *FtpServer

NewFtpServer creates a new FtpServer instance

func (*FtpServer) Addr

func (server *FtpServer) Addr() string

Addr shows the listening address

func (*FtpServer) Listen

func (server *FtpServer) Listen() error

Listen starts the listening It's not a blocking call

func (*FtpServer) ListenAndServe

func (server *FtpServer) ListenAndServe() error

ListenAndServe simply chains the Listen and Serve method calls

func (*FtpServer) Serve

func (server *FtpServer) Serve() error

Serve accepts and processes any new incoming client

func (*FtpServer) Stop

func (server *FtpServer) Stop() error

Stop closes the listener

type MainDriver

type MainDriver interface {
	// GetSettings returns some general settings around the server setup
	GetSettings() (*Settings, error)

	// ClientConnected is called to send the very first welcome message
	ClientConnected(cc ClientContext) (string, error)

	// ClientDisconnected is called when the user disconnects, even if he never authenticated
	ClientDisconnected(cc ClientContext)

	// AuthUser authenticates the user and selects an handling driver
	AuthUser(cc ClientContext, user, pass string) (ClientDriver, error)

	// GetTLSConfig returns a TLS Certificate to use
	// The certificate could frequently change if we use something like "let's encrypt"
	GetTLSConfig() (*tls.Config, error)
}

MainDriver handles the authentication and ClientHandlingDriver selection

type PortRange

type PortRange struct {
	Start int // Range start
	End   int // Range end
}

PortRange is a range of ports

type PublicIPResolver

type PublicIPResolver func(ClientContext) (string, error)

PublicIPResolver takes a ClientContext for a connection and returns the public IP to use in the response to the PASV command, or an error if a public IP cannot be determined.

type Settings

type Settings struct {
	Listener                 net.Listener     // (Optional) To provide an already initialized listener
	ListenAddr               string           // Listening address
	PublicHost               string           // Public IP to expose (only an IP address is accepted at this stage)
	PublicIPResolver         PublicIPResolver // (Optional) To fetch a public IP lookup
	PassiveTransferPortRange *PortRange       // (Optional) Port Range for data connections. Random if not specified
	ActiveTransferPortNon20  bool             // Do not impose the port 20 for active data transfer (#88, RFC 1579)
	IdleTimeout              int              // Maximum inactivity time before disconnecting (#58)
	ConnectionTimeout        int              // Maximum time to establish passive or active transfer connections
	DisableMLSD              bool             // Disable MLSD support
	DisableMLST              bool             // Disable MLST support
	DisableMFMT              bool             // Disable MFMT support (modify file mtime)
}

Settings defines all the server settings nolint: maligned

Directories

Path Synopsis
log
Package log provides a simple interface to handle logging
Package log provides a simple interface to handle logging
gokit
Package gokit provides go-kit Logger implementation
Package gokit provides go-kit Logger implementation

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL