sftpd: refactor connection closing

we have not known bugs with the previous implementation anyway this one
is cleaner: the underlying network connection is directly related with
SFTP/SCP connections.
This should better protect us against buggy clients and edge cases
This commit is contained in:
Nicola Murino
2019-10-10 09:04:17 +02:00
parent 4b5ce3913e
commit 871e2ccbbf
4 changed files with 25 additions and 10 deletions

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net"
"os" "os"
"runtime" "runtime"
"testing" "testing"
@@ -446,8 +447,12 @@ func TestSCPCommandHandleErrors(t *testing.T) {
ReadError: readErr, ReadError: readErr,
WriteError: writeErr, WriteError: writeErr,
} }
server, client := net.Pipe()
defer server.Close()
defer client.Close()
connection := Connection{ connection := Connection{
channel: &mockSSHChannel, channel: &mockSSHChannel,
netConn: client,
} }
scpCommand := scpCommand{ scpCommand := scpCommand{
connection: connection, connection: connection,
@@ -475,8 +480,12 @@ func TestSCPRecursiveDownloadErrors(t *testing.T) {
ReadError: readErr, ReadError: readErr,
WriteError: writeErr, WriteError: writeErr,
} }
server, client := net.Pipe()
defer server.Close()
defer client.Close()
connection := Connection{ connection := Connection{
channel: &mockSSHChannel, channel: &mockSSHChannel,
netConn: client,
} }
scpCommand := scpCommand{ scpCommand := scpCommand{
connection: connection, connection: connection,

View File

@@ -39,8 +39,8 @@ type scpCommand struct {
func (c *scpCommand) handle() error { func (c *scpCommand) handle() error {
var err error var err error
addConnection(c.connection.ID, c.connection) addConnection(c.connection)
defer removeConnection(c.connection.ID) defer removeConnection(c.connection)
destPath := c.getDestPath() destPath := c.getDestPath()
commandType := c.getCommandType() commandType := c.getCommandType()
c.connection.Log(logger.LevelDebug, logSenderSCP, "handle scp command, args: %v user: %v command type: %v, dest path: %#v", c.connection.Log(logger.LevelDebug, logSenderSCP, "handle scp command, args: %v user: %v command type: %v, dest path: %#v",

View File

@@ -299,7 +299,8 @@ func (c Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.Server
} }
func (c Configuration) handleSftpConnection(channel ssh.Channel, connection Connection) { func (c Configuration) handleSftpConnection(channel ssh.Channel, connection Connection) {
addConnection(connection.ID, connection) addConnection(connection)
defer removeConnection(connection)
// Create a new handler for the currently logged in user's server. // Create a new handler for the currently logged in user's server.
handler := c.createHandler(connection) handler := c.createHandler(connection)
@@ -312,8 +313,6 @@ func (c Configuration) handleSftpConnection(channel ssh.Channel, connection Conn
} else if err != nil { } else if err != nil {
connection.Log(logger.LevelWarn, logSender, "connection closed with error: %v", err) connection.Log(logger.LevelWarn, logSender, "connection closed with error: %v", err)
} }
removeConnection(connection.ID)
} }
func (c Configuration) createHandler(connection Connection) sftp.Handlers { func (c Configuration) createHandler(connection Connection) sftp.Handlers {

View File

@@ -310,20 +310,27 @@ func CheckIdleConnections() {
logger.Debug(logSender, "", "check idle connections ended") logger.Debug(logSender, "", "check idle connections ended")
} }
func addConnection(id string, c Connection) { func addConnection(c Connection) {
mutex.Lock() mutex.Lock()
defer mutex.Unlock() defer mutex.Unlock()
openConnections[id] = c openConnections[c.ID] = c
metrics.UpdateActiveConnectionsSize(len(openConnections)) metrics.UpdateActiveConnectionsSize(len(openConnections))
c.Log(logger.LevelDebug, logSender, "connection added, num open connections: %v", len(openConnections)) c.Log(logger.LevelDebug, logSender, "connection added, num open connections: %v", len(openConnections))
} }
func removeConnection(id string) { func removeConnection(c Connection) {
mutex.Lock() mutex.Lock()
defer mutex.Unlock() defer mutex.Unlock()
c := openConnections[id] delete(openConnections, c.ID)
delete(openConnections, id)
metrics.UpdateActiveConnectionsSize(len(openConnections)) metrics.UpdateActiveConnectionsSize(len(openConnections))
// we have finished to send data here and most of the time the underlying network connection
// is already closed. Sometime a client can still be reading, the last sended data, from the
// connection so we set a deadline instead of directly closing the network connection.
// Setting a deadline on an already closed connection has no effect.
// We only need to ensure that a connection will not remain undefinitely open and so the
// underlying file descriptor is not released.
// This should protect us against buggy clients and edge cases.
c.netConn.SetDeadline(time.Now().Add(2 * time.Minute))
c.Log(logger.LevelDebug, logSender, "connection removed, num open connections: %v", len(openConnections)) c.Log(logger.LevelDebug, logSender, "connection removed, num open connections: %v", len(openConnections))
} }