diff --git a/ftpd/ftpd.go b/ftpd/ftpd.go index 8e4a35b2..e6925c2c 100644 --- a/ftpd/ftpd.go +++ b/ftpd/ftpd.go @@ -211,6 +211,7 @@ func (c *Configuration) Initialize(configDir string) error { go func(s *Server) { ftpServer := ftpserver.NewFtpServer(s) logger.Info(logSender, "", "starting FTP serving, binding: %v", s.binding.GetAddress()) + utils.CheckTCP4Port(s.binding.Port) exitChannel <- ftpServer.ListenAndServe() }(server) diff --git a/sftpd/server.go b/sftpd/server.go index b04694d9..a188e5b5 100644 --- a/sftpd/server.go +++ b/sftpd/server.go @@ -232,6 +232,7 @@ func (c *Configuration) Initialize(configDir string) error { go func(binding Binding) { addr := binding.GetAddress() + utils.CheckTCP4Port(binding.Port) listener, err := net.Listen("tcp", addr) if err != nil { logger.Warn(logSender, "", "error starting listener on address %v: %v", addr, err) diff --git a/utils/utils.go b/utils/utils.go index e8eced67..eae594c2 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -34,6 +34,7 @@ import ( const ( logSender = "utils" + osWindows = "windows" ) // IsStringInSlice searches a string in a slice and returns true if the string is found @@ -373,7 +374,7 @@ func IsFileInputValid(fileInput string) bool { // the -l flag will be ignored and the -c flag will get the value `C:\ProgramData\SFTPGO" -l sftpgo.log` // since the backslash after SFTPGO escape the double quote. This is definitely a bad user input func CleanDirInput(dirInput string) string { - if runtime.GOOS == "windows" { + if runtime.GOOS == osWindows { for strings.HasSuffix(dirInput, "\"") { dirInput = strings.TrimSuffix(dirInput, "\"") } @@ -414,7 +415,7 @@ func HTTPListenAndServe(srv *http.Server, address string, port int, isTLS bool, var listener net.Listener var err error - if filepath.IsAbs(address) && runtime.GOOS != "windows" { + if filepath.IsAbs(address) && runtime.GOOS != osWindows { if !IsFileInputValid(address) { return fmt.Errorf("invalid socket address %#v", address) } @@ -427,6 +428,7 @@ func HTTPListenAndServe(srv *http.Server, address string, port int, isTLS bool, listener, err = net.Listen("unix", address) } else { + CheckTCP4Port(port) listener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", address, port)) } if err != nil { @@ -462,7 +464,7 @@ func GetTLSCiphersFromNames(cipherNames []string) []uint16 { // This can be verified using openssl x509 -in cert.crt -text -noout func EncodeTLSCertToPem(tlsCert *x509.Certificate) (string, error) { if len(tlsCert.Raw) == 0 { - return "", errors.New("Invalid x509 certificate, no der contents") + return "", errors.New("invalid x509 certificate, no der contents") } publicKeyBlock := pem.Block{ Type: "CERTIFICATE", @@ -470,3 +472,20 @@ func EncodeTLSCertToPem(tlsCert *x509.Certificate) (string, error) { } return string(pem.EncodeToMemory(&publicKeyBlock)), nil } + +// CheckTCP4Port quits the app if bind to the given IPv4 port. +// This is a ugly hack to avoid to bind on an already used port. +// It is required on Windows only. +// https://github.com/golang/go/issues/45150 +func CheckTCP4Port(port int) { + if runtime.GOOS != osWindows { + return + } + listener, err := net.Listen("tcp4", fmt.Sprintf(":%d", port)) + if err != nil { + logger.ErrorToConsole("unable to bind tcp4 address: %v", err) + logger.Error(logSender, "", "unable to bind tcp4 address: %v", err) + os.Exit(1) + } + listener.Close() +} diff --git a/vfs/folder.go b/vfs/folder.go index 113211a4..4ddacb4a 100644 --- a/vfs/folder.go +++ b/vfs/folder.go @@ -141,6 +141,7 @@ type VirtualFolder struct { QuotaFiles int `json:"quota_files"` } +// GetFilesystem returns the filesystem for this folder func (v *VirtualFolder) GetFilesystem(connectionID string) (Fs, error) { switch v.FsConfig.Provider { case S3FilesystemProvider: diff --git a/webdavd/server.go b/webdavd/server.go index cacddb7e..4e03b513 100644 --- a/webdavd/server.go +++ b/webdavd/server.go @@ -80,11 +80,13 @@ func (s *webDavServer) listenAndServe(compressor *middleware.Compressor) error { } } logger.Info(logSender, "", "starting HTTPS serving, binding: %v", s.binding.GetAddress()) + utils.CheckTCP4Port(s.binding.Port) return httpServer.ListenAndServeTLS("", "") } s.binding.EnableHTTPS = false serviceStatus.Bindings = append(serviceStatus.Bindings, s.binding) logger.Info(logSender, "", "starting HTTP serving, binding: %v", s.binding.GetAddress()) + utils.CheckTCP4Port(s.binding.Port) return httpServer.ListenAndServe() }