Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow passing in an existing net.Listener and a public ip resolver. #53

Merged
merged 1 commit into from
Dec 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions server/client_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ func (c *clientHandler) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}

// LocalAddr returns the local network address.
func (c *clientHandler) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}

func (c *clientHandler) end() {
c.daddy.driver.UserLeft(c)
c.daddy.clientDeparture(c)
Expand Down
19 changes: 14 additions & 5 deletions server/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ type ClientContext interface {

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

// Servers's address
LocalAddr() net.Addr
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we rename it to ListenAddr to make it a bit more explicit ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im open to a name change, but figured it would make more sense to expose it under the same name as the connection object it is coming from.
Its also not guaranteed that the address is actually the same as the listener address (again from my use case, it will be the private IP of the load balancer that is forwarding the request).

I had originally written this (when prototyping for my own uses) to pass the net.Conn object to the PublicIPResolver, but figured that exposed too much, and instead opted to abstract it behind the ClientContext.

}

// FileStream is a read or write closeable stream
Expand All @@ -90,11 +93,17 @@ type PortRange struct {
End int // Range end
}

// 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 PublicIPResolver func(ClientContext) (string, error)

// Settings defines all the server settings
type Settings struct {
ListenAddr string // Listening address
PublicHost string // Public IP to expose (only an IP address is accepted at this stage)
DataPortRange *PortRange // Port Range for data connections. Random one will be used if not specified
DisableMLSD bool // Disable MLSD support
NonStandardActiveDataPort bool // Allow to use a non-standard active data port
Listener net.Listener // Allow providing an already initialized listener. Mutually exclusive with ListenAddr
ListenAddr string // Listening address
PublicHost string // Public IP to expose (only an IP address is accepted at this stage)
PublicIPResolver PublicIPResolver // Optional function that can perform a public ip lookup for the given CientContext.
DataPortRange *PortRange // Port Range for data connections. Random one will be used if not specified
DisableMLSD bool // Disable MLSD support
NonStandardActiveDataPort bool // Allow to use a non-standard active data port
}
17 changes: 9 additions & 8 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (server *FtpServer) loadSettings() error {
return err
}

if s.ListenAddr == "" {
if s.Listener == nil && s.ListenAddr == "" {
s.ListenAddr = "0.0.0.0:2121"
}

Expand All @@ -112,14 +112,15 @@ func (server *FtpServer) Listen() error {
return fmt.Errorf("could not load settings: %v", err)
}

server.listener, err = net.Listen(
"tcp",
server.settings.ListenAddr,
)
if server.settings.Listener != nil {
server.listener = server.settings.Listener
} else {
server.listener, err = net.Listen("tcp", server.settings.ListenAddr)

if err != nil {
level.Error(server.Logger).Log(logKeyMsg, "Cannot listen", "err", err)
return err
if err != nil {
level.Error(server.Logger).Log(logKeyMsg, "Cannot listen", "err", err)
return err
}
}

level.Info(server.Logger).Log(logKeyMsg, "Listening...", logKeyAction, "ftp.listening", "address", server.listener.Addr())
Expand Down
14 changes: 13 additions & 1 deletion server/transfer_pasv.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,19 @@ func (c *clientHandler) handlePASV() {

// If we don't have an IP address, we can take the one that was used for the current connection
if ip == "" {
ip = strings.Split(c.conn.LocalAddr().String(), ":")[0]
// Defer to the user provided resolver.
if c.daddy.settings.PublicIPResolver != nil {
var err error
ip, err = c.daddy.settings.PublicIPResolver(c)
if err != nil {
// Not sure if there is better desired behavior than this.
// If we can't resolve the public ip to return to the client, is there any actual
// fallback that is better than erroring.
panic(err)
}
} else {
ip = strings.Split(c.conn.LocalAddr().String(), ":")[0]
}
}

quads := strings.Split(ip, ".")
Expand Down