graval

package module
v0.0.0-...-aba28f8 Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2020 License: MIT Imports: 21 Imported by: 10

README

graval

Go FTP server framework. By providing a simple driver class that responds to a handful of methods you can have a complete FTP server.

Some sample use cases include persisting data to:

  • an Amazon S3 bucket
  • a relational database
  • redis
  • memory

Full documentation for the package is available on godoc

Installation

go get github.com/koofr/graval

Example

go run example/example.go

Usage

To boot an FTP server you will need to provide a driver that speaks to your persistence layer - the required driver contract is listed below.

Do not forget to seed math random in your main program. Otherwise passive ports will be predictable.

func main() {
  rand.Seed(time.Now().UTC().UnixNano())
  ...
}
The Driver Contract

Your driver MUST implement a number of simple methods. You can view the required contract in the package docs on godoc

Contributors

Warning

FTP is an incredibly insecure protocol. Be careful about forcing users to authenticate with a username or password that are important.

License

This library is distributed under the terms of the MIT License. See the included file for more detail.

Contributing

All suggestions and patches welcome, preferably via a git repository I can pull from. If this library proves useful to you, please let me know.

Further Reading

There are a range of RFCs that together specify the FTP protocol. In chronological order, the more useful ones are:

For an english summary that's somewhat more legible than the RFCs, and provides some commentary on what features are actually useful or relevant 24 years after RFC959 was published:

For a history lesson, check out Appendix III of RCF959. It lists the preceding (obsolete) RFC documents that relate to file transfers, including the ye old RFC114 from 1971, "A File Transfer Protocol"

This library is heavily based on em-ftpd, an FTPd framework with similar design goals within the ruby and EventMachine ecosystems. It worked well enough, but you know, callbacks and event loops make me something something.

TLS implementation and some other commands are based on pyftpdlib.

Documentation

Overview

An experimental FTP server framework. By providing a simple driver class that responds to a handful of methods you can have a complete FTP server.

Some sample use cases include persisting data to an Amazon S3 bucket, a relational database, redis or memory.

There is a sample in-memory driver available - see the documentation for the graval-mem package or the graval READEME for more details.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewDirItem

func NewDirItem(name string) os.FileInfo

NewDirItem creates a new os.FileInfo that represents a single diretory. Use this function to build the response to DirContents() in your FTPDriver implementation.

func NewFileItem

func NewFileItem(name string, bytes int64, modtime time.Time) os.FileInfo

NewFileItem creates a new os.FileInfo that represents a single file. Use this function to build the response to DirContents() in your FTPDriver implementation.

Types

type BoundCommand

type BoundCommand struct {
	CmdObj ftpCommand
	Param  string
}

type CryptoConfig

type CryptoConfig struct {
	Implicit  bool
	Force     bool
	TlsConfig *tls.Config
}

type FTPDriver

type FTPDriver interface {
	// params  - username, password
	// returns - true if the provided details are valid
	Authenticate(username string, password string) bool

	// params  - a file path
	// returns - an int with the number of bytes in the file or -1 if the file
	//           doesn't exist
	Bytes(path string) int64

	// params  - a file path
	// returns - a time indicating when the requested path was last modified
	//         - an ok flag if the file doesn't exist or the user lacks
	//           permissions
	ModifiedTime(path string) (time.Time, bool)

	// params  - path
	// returns - true if the current user is permitted to change to the
	//           requested path
	ChangeDir(path string) bool

	// params  - path
	// returns - a collection of items describing the contents of the requested
	//           path
	DirContents(path string) ([]os.FileInfo, bool)

	// params  - path
	// returns - true if the directory was deleted
	DeleteDir(path string) bool

	// params  - path
	// returns - true if the file was deleted
	DeleteFile(path string) bool

	// params  - fromPath, toPath
	// returns - true if the file was renamed
	Rename(fromPath string, toPath string) bool

	// params  - path
	// returns - true if the new directory was created
	MakeDir(path string) bool

	// params  - path, position
	// returns - a string containing the file data to send to the client
	GetFile(path string, position int64) (io.ReadCloser, bool)

	// params  - desination path, an io.Reader containing the file data
	// returns - true if the data was successfully persisted
	PutFile(path string, reader io.Reader) bool
}

You will create an implementation of this interface that speaks to your chosen persistence layer. graval will create a new instance of your driver for each client that connects and delegate to it as required.

type FTPDriverCustomAuthenticate

type FTPDriverCustomAuthenticate interface {
	CustomAuthenticate(username string, password string) (code int, message string, ok bool)
}

FTPDriverAuthenticateWithError is an optional interface customized auth

type FTPDriverFactory

type FTPDriverFactory interface {
	NewDriver() (FTPDriver, error)
}

For each client that connects to the server, a new FTPDriver is required. Create an implementation if this interface and provide it to FTPServer.

type FTPDriverSetRemoteAddress

type FTPDriverSetRemoteAddress interface {
	SetRemoteAddress(remoteAddress net.Addr)
}

FTPDriverSetRemoteAddress is an optional interface for setting remote address

type FTPServer

type FTPServer struct {
	// contains filtered or unexported fields
}

FTPServer is the root of your FTP application. You should instantiate one of these and call ListenAndServe() to start accepting client connections.

Always use the NewFTPServer() method to create a new FTPServer.

func NewFTPServer

func NewFTPServer(opts *FTPServerOpts) *FTPServer

NewFTPServer initialises a new FTP server. Configuration options are provided via an instance of FTPServerOpts. Calling this function in your code will probably look something like this:

factory := &MyDriverFactory{}
server  := graval.NewFTPServer(&graval.FTPServerOpts{ Factory: factory })

or:

factory := &MyDriverFactory{}
opts    := &graval.FTPServerOpts{
  Factory: factory,
  Port: 2000,
  Hostname: "127.0.0.1",
}
server  := graval.NewFTPServer(opts)

func (*FTPServer) Close

func (ftpServer *FTPServer) Close() error

func (*FTPServer) ListenAndServe

func (ftpServer *FTPServer) ListenAndServe() error

ListenAndServe asks a new FTPServer to begin accepting client connections. It accepts no arguments - all configuration is provided via the NewFTPServer function.

If the server fails to start for any reason, an error will be returned. Common errors are trying to bind to a privileged port or something else is already listening on the same port.

type FTPServerOpts

type FTPServerOpts struct {
	// Server name will be used for welcome message
	ServerName string

	// The factory that will be used to create a new FTPDriver instance for
	// each client connection. This is a mandatory option.
	Factory FTPDriverFactory

	// The hostname that the FTP server should listen on. Optional, defaults to
	// "::", which means all hostnames on ipv4 and ipv6.
	Hostname string

	// The port that the FTP should listen on. Optional, defaults to 3000. In
	// a production environment you will probably want to change this to 21.
	Port int

	// Options for passive data connections
	PassiveOpts *PassiveOpts

	// Options for FTPS and FTPES
	CryptoConfig *CryptoConfig

	// Disable logging (useful for tests)
	Quiet bool
}

serverOpts contains parameters for graval.NewFTPServer()

type PassiveOpts

type PassiveOpts struct {
	ListenAddress string
	NatAddress    string
	PassivePorts  *PassivePorts
}

type PassivePorts

type PassivePorts struct {
	Low  int
	High int
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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