diff --git a/main.go b/main.go index 57431a2..f0f78de 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "canguidev/shelfy/internal/db" "canguidev/shelfy/internal/routes" "canguidev/shelfy/internal/utils" @@ -22,22 +23,48 @@ import ( type mainDriver struct{} +// tcpKeepAliveListener est un wrapper générique qui active le keep-alive +// sur chaque connexion acceptée par un net.Listener. +type tcpKeepAliveListener struct{ net.Listener } + +func (l tcpKeepAliveListener) Accept() (net.Conn, error) { + c, err := l.Listener.Accept() + if err != nil { + return nil, err + } + if tc, ok := c.(*net.TCPConn); ok { + _ = tc.SetKeepAlive(true) + _ = tc.SetKeepAlivePeriod(30 * time.Second) + } + return c, nil +} + +// clientDriver : wrapper sur afero.Fs type clientDriver struct { fs afero.Fs } func (d *mainDriver) GetSettings() (*ftpserver.Settings, error) { + // Listener principal (port 2121) avec TCP keep-alive activé + lc := net.ListenConfig{ + KeepAlive: 30 * time.Second, // garde la connexion de contrôle vivante côté NAT/pare-feu + } + ln, err := lc.Listen(context.Background(), "tcp", ":2121") + if err != nil { + return nil, err + } + return &ftpserver.Settings{ - ListenAddr: ":2121", + // On fournit notre listener plutôt que ListenAddr + Listener: ln, PublicHost: "163.172.68.103", PassiveTransferPortRange: &ftpserver.PortRange{ Start: 30000, End: 30100, }, - Banner: "Bienvenue sur le FTP Go!", - IdleTimeout: 0, - ConnectionTimeout: 0, - + Banner: "Bienvenue sur le FTP Go!", + IdleTimeout: 0, // ne pas couper côté serveur + ConnectionTimeout: 10, // délai pour établir une connexion de transfert }, nil } @@ -45,22 +72,27 @@ func (d *mainDriver) ClientConnected(cc ftpserver.ClientContext) (string, error) log.Printf("[FTP] Nouvelle connexion depuis %s", cc.RemoteAddr()) return "Bienvenue FTP !", nil } + func (d *mainDriver) ClientDisconnected(cc ftpserver.ClientContext) { log.Printf("[FTP] Déconnexion client %s", cc.RemoteAddr()) } + func (d *mainDriver) GetTLSConfig() (*tls.Config, error) { - return nil, nil // Pas de TLS, à gérer si tu veux FTPS + // Pas de TLS ici (FTPS), à ajouter si besoin + return nil, nil } + func (d *mainDriver) AuthUser(cc ftpserver.ClientContext, user, pass string) (ftpserver.ClientDriver, error) { log.Printf("[FTP] Tentative login user='%s' pass='%s'", user, pass) remoteAddr := cc.RemoteAddr().String() host, _, _ := net.SplitHostPort(remoteAddr) + // 1. Compte user/pwd if user == "cangui2089" && pass == "GHT30k7!" { uploadPath, _ := filepath.Abs("upload") fi, err := os.Stat(uploadPath) if err != nil || !fi.IsDir() { - log.Printf("[FTP] Le dossier upload/ est manquant ou non valide : %v", err) + log.Printf("[FTP] Le dossier upload/ est manquant ou non valide : %v", err) return nil, errors.New("le dossier upload/ doit exister") } log.Printf("[FTP] Connexion OK, exposé: %s", uploadPath) @@ -68,15 +100,16 @@ func (d *mainDriver) AuthUser(cc ftpserver.ClientContext, user, pass string) (ft fs: afero.NewBasePathFs(afero.NewOsFs(), uploadPath), }, nil } - // 2. Autoriser anonymous depuis 192.168.1.123 uniquement + + // 2. Autoriser anonymous depuis IP spécifique if user == "anonymous" && host == "82.65.73.115" { base := filepath.Clean("upload") fs := afero.NewBasePathFs(afero.NewOsFs(), base) log.Printf("[FTP] Login ANONYMOUS autorisé pour %s", host) - return &clientDriver{fs: fs}, nil + return &clientDriver{fs: fs}, nil } - // 3. Sinon refuse tout anonyme ou mauvais login + // 3. Refuser anonymous ou mauvais login if user == "" || user == "anonymous" { log.Printf("[FTP] Login anonymous refusé pour %s", host) return nil, errors.New("anonymous login not allowed") @@ -85,20 +118,30 @@ func (d *mainDriver) AuthUser(cc ftpserver.ClientContext, user, pass string) (ft return nil, errors.New("identifiants invalides") } +// ---- Extension: wrapper du listener passif (PASV) pour activer keep-alive ---- + +// WrapPassiveListener est appelée par ftpserverlib si le mainDriver +// implémente l'extension "MainDriverExtensionPassiveWrapper". +// On y met le même wrapper keep-alive que pour le listener principal. +func (d *mainDriver) WrapPassiveListener(l net.Listener) (net.Listener, error) { + log.Printf("[FTP] WrapPassiveListener: activation TCP keep-alive sur PASV") + return tcpKeepAliveListener{l}, nil +} + // ---------- clientDriver = wrapper sur afero.Fs ---------- -func (c *clientDriver) Name() string { return "aferofs" } -func (c *clientDriver) Create(name string) (afero.File, error) { return c.fs.Create(name) } -func (c *clientDriver) Mkdir(name string, perm os.FileMode) error { return c.fs.Mkdir(name, perm) } -func (c *clientDriver) MkdirAll(path string, perm os.FileMode) error { return c.fs.MkdirAll(path, perm) } -func (c *clientDriver) Open(name string) (afero.File, error) { return c.fs.Open(name) } +func (c *clientDriver) Name() string { return "aferofs" } +func (c *clientDriver) Create(name string) (afero.File, error) { return c.fs.Create(name) } +func (c *clientDriver) Mkdir(name string, perm os.FileMode) error { return c.fs.Mkdir(name, perm) } +func (c *clientDriver) MkdirAll(path string, perm os.FileMode) error { return c.fs.MkdirAll(path, perm) } +func (c *clientDriver) Open(name string) (afero.File, error) { return c.fs.Open(name) } func (c *clientDriver) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { - log.Printf("[FTP][DEBUG] Open: %s", name) + log.Printf("[FTP][DEBUG] Open: %s", name) return c.fs.OpenFile(name, flag, perm) } -func (c *clientDriver) Remove(name string) error { return c.fs.Remove(name) } -func (c *clientDriver) RemoveAll(path string) error { return c.fs.RemoveAll(path) } -func (c *clientDriver) Rename(old, new string) error { return c.fs.Rename(old, new) } +func (c *clientDriver) Remove(name string) error { return c.fs.Remove(name) } +func (c *clientDriver) RemoveAll(path string) error { return c.fs.RemoveAll(path) } +func (c *clientDriver) Rename(old, new string) error { return c.fs.Rename(old, new) } func (c *clientDriver) Stat(name string) (os.FileInfo, error) { return c.fs.Stat(name) } @@ -119,7 +162,6 @@ func (c *clientDriver) Chtimes(name string, atime, mtime time.Time) error { return os.Chtimes(fullPath, atime, mtime) } - // ---------- MAIN ---------- func main() { @@ -150,5 +192,5 @@ func main() { }) log.Println("[HTTP] Serveur Gin sur http://localhost:8080") - app.Run(":8080") + _ = app.Run(":8080") }