X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/blobdiff_plain/d1cd666473e5d9097b34bad3388c8c0595612089..5c14e4c916d76ce35ee2b07bf832d5f3b6118260:/hotline/server.go diff --git a/hotline/server.go b/hotline/server.go index 5ff8842..f164df1 100644 --- a/hotline/server.go +++ b/hotline/server.go @@ -236,9 +236,15 @@ func NewServer(configDir, netInterface string, netPort int, logger *zap.SugaredL *server.NextGuestID = 1 if server.Config.EnableTrackerRegistration { + server.Logger.Infow( + "Tracker registration enabled", + "frequency", fmt.Sprintf("%vs", trackerUpdateFrequency), + "trackers", server.Config.Trackers, + ) + go func() { for { - tr := TrackerRegistration{ + tr := &TrackerRegistration{ Port: []byte{0x15, 0x7c}, UserCount: server.userCount(), PassID: server.TrackerPassID, @@ -246,11 +252,10 @@ func NewServer(configDir, netInterface string, netPort int, logger *zap.SugaredL Description: server.Config.Description, } for _, t := range server.Config.Trackers { - server.Logger.Infof("Registering with tracker %v", t) - if err := register(t, tr); err != nil { server.Logger.Errorw("unable to register with tracker %v", "error", err) } + server.Logger.Infow("Sent Tracker registration", "data", tr) } time.Sleep(trackerUpdateFrequency * time.Second) @@ -478,17 +483,29 @@ const ( minTransactionLen = 22 // minimum length of any transaction ) +// dontPanic recovers and logs panics instead of crashing +// TODO: remove this after known issues are fixed +func dontPanic(logger *zap.SugaredLogger) { + if r := recover(); r != nil { + fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) + logger.Errorw("PANIC", "err", r, "trace", string(debug.Stack())) + } +} + // handleNewConnection takes a new net.Conn and performs the initial login sequence func (s *Server) handleNewConnection(conn net.Conn) error { - handshakeBuf := make([]byte, 12) // handshakes are always 12 bytes in length - if _, err := conn.Read(handshakeBuf); err != nil { + defer dontPanic(s.Logger) + + handshakeBuf := make([]byte, 12) + if _, err := io.ReadFull(conn, handshakeBuf); err != nil { return err } - if err := Handshake(conn, handshakeBuf[:12]); err != nil { + if err := Handshake(conn, handshakeBuf); err != nil { return err } buf := make([]byte, 1024) + // TODO: fix potential short read with io.ReadFull readLen, err := conn.Read(buf) if readLen < minTransactionLen { return err @@ -504,13 +521,6 @@ func (s *Server) handleNewConnection(conn net.Conn) error { c := s.NewClientConn(conn) defer c.Disconnect() - defer func() { - if r := recover(); r != nil { - fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) - c.Server.Logger.Errorw("PANIC", "err", r, "trace", string(debug.Stack())) - c.Disconnect() - } - }() encodedLogin := clientLogin.GetField(fieldUserLogin).Data encodedPassword := clientLogin.GetField(fieldUserPassword).Data @@ -646,28 +656,37 @@ const dlFldrActionNextFile = 3 // handleFileTransfer receives a client net.Conn from the file transfer server, performs the requested transfer type, then closes the connection func (s *Server) handleFileTransfer(conn io.ReadWriteCloser) error { defer func() { + if err := conn.Close(); err != nil { s.Logger.Errorw("error closing connection", "error", err) } }() + defer dontPanic(s.Logger) + txBuf := make([]byte, 16) - _, err := conn.Read(txBuf) - if err != nil { + if _, err := io.ReadFull(conn, txBuf); err != nil { return err } var t transfer - _, err = t.Write(txBuf) - if err != nil { + if _, err := t.Write(txBuf); err != nil { return err } transferRefNum := binary.BigEndian.Uint32(t.ReferenceNumber[:]) - fileTransfer := s.FileTransfers[transferRefNum] + defer func() { + s.mux.Lock() + delete(s.FileTransfers, transferRefNum) + s.mux.Unlock() + }() - // delete single use transferRefNum - delete(s.FileTransfers, transferRefNum) + s.mux.Lock() + fileTransfer, ok := s.FileTransfers[transferRefNum] + s.mux.Unlock() + if !ok { + return errors.New("invalid transaction ID") + } switch fileTransfer.Type { case FileDownload: @@ -723,11 +742,27 @@ func (s *Server) handleFileTransfer(conn io.ReadWriteCloser) error { } case FileUpload: destinationFile := s.Config.FileRoot + ReadFilePath(fileTransfer.FilePath) + "/" + string(fileTransfer.FileName) - tmpFile := destinationFile + ".incomplete" - file, err := effectiveFile(destinationFile) + var file *os.File + + // A file upload has three possible cases: + // 1) Upload a new file + // 2) Resume a partially transferred file + // 3) Replace a fully uploaded file + // Unfortunately we have to infer which case applies by inspecting what is already on the file system + + // 1) Check for existing file: + _, err := os.Stat(destinationFile) + if err == nil { + // If found, that means this upload is intended to replace the file + if err = os.Remove(destinationFile); err != nil { + return err + } + file, err = os.Create(destinationFile + incompleteFileSuffix) + } if errors.Is(err, fs.ErrNotExist) { - file, err = FS.Create(tmpFile) + // If not found, open or create a new incomplete file + file, err = os.OpenFile(destinationFile+incompleteFileSuffix, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return err } @@ -785,7 +820,7 @@ func (s *Server) handleFileTransfer(conn io.ReadWriteCloser) error { s.Logger.Infow("Start folder download", "path", fullFilePath, "ReferenceNumber", fileTransfer.ReferenceNumber) nextAction := make([]byte, 2) - if _, err := conn.Read(nextAction); err != nil { + if _, err := io.ReadFull(conn, nextAction); err != nil { return err } @@ -811,7 +846,7 @@ func (s *Server) handleFileTransfer(conn io.ReadWriteCloser) error { } // Read the client's Next Action request - if _, err := conn.Read(nextAction); err != nil { + if _, err := io.ReadFull(conn, nextAction); err != nil { return err } @@ -824,13 +859,13 @@ func (s *Server) handleFileTransfer(conn io.ReadWriteCloser) error { // client asked to resume this file var frd FileResumeData // get size of resumeData - if _, err := conn.Read(nextAction); err != nil { + if _, err := io.ReadFull(conn, nextAction); err != nil { return err } resumeDataLen := binary.BigEndian.Uint16(nextAction) resumeDataBytes := make([]byte, resumeDataLen) - if _, err := conn.Read(resumeDataBytes); err != nil { + if _, err := io.ReadFull(conn, resumeDataBytes); err != nil { return err } @@ -907,7 +942,7 @@ func (s *Server) handleFileTransfer(conn io.ReadWriteCloser) error { // TODO: optionally send resource fork header and resource fork data // Read the client's Next Action request. This is always 3, I think? - if _, err := conn.Read(nextAction); err != nil { + if _, err := io.ReadFull(conn, nextAction); err != nil { return err } @@ -943,7 +978,7 @@ func (s *Server) handleFileTransfer(conn io.ReadWriteCloser) error { readBuffer := make([]byte, 1024) for i := 0; i < fileTransfer.ItemCount(); i++ { - + // TODO: fix potential short read with io.ReadFull _, err := conn.Read(readBuffer) if err != nil { return err @@ -1021,7 +1056,7 @@ func (s *Server) handleFileTransfer(conn io.ReadWriteCloser) error { return err } - if _, err := conn.Read(fileSize); err != nil { + if _, err := io.ReadFull(conn, fileSize); err != nil { return err }