Prechádzať zdrojové kódy

Windows: manually check if we can bind on the configured port/ports

Windows allows the coexistence of three types of sockets on the same
transport-layer service port, for example, 127.0.0.1:8080, [::1]:8080
and [::ffff:0.0.0.0]:8080

Go don't properly handles this, so we use a ugly hack

Fixes #350
Nicola Murino 4 rokov pred
rodič
commit
54c0c1b80d
5 zmenil súbory, kde vykonal 27 pridanie a 3 odobranie
  1. 1 0
      ftpd/ftpd.go
  2. 1 0
      sftpd/server.go
  3. 22 3
      utils/utils.go
  4. 1 0
      vfs/folder.go
  5. 2 0
      webdavd/server.go

+ 1 - 0
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)
 

+ 1 - 0
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)

+ 22 - 3
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()
+}

+ 1 - 0
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:

+ 2 - 0
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()
 }