package main import ( "bufio" "context" "errors" "fmt" "github.com/TwiN/go-color" "log" "net" "regexp" "strings" "time" ) // Chunks splits a string into chunks of chunkSize length // by topskip taken from https://stackoverflow.com/questions/18556693/slice-string-into-letters func Chunks(s string, chunkSize int) []string { if len(s) == 0 { return nil } if chunkSize >= len(s) { return []string{s} } var chunks []string = make([]string, 0, (len(s)-1)/chunkSize+1) currentLen := 0 currentStart := 0 for i := range s { if currentLen == chunkSize { chunks = append(chunks, s[currentStart:i]) currentLen = 0 currentStart = i } currentLen++ } chunks = append(chunks, s[currentStart:]) return chunks } // HandleConnection processes incoming connections func handleTCPCmdConnection(conn net.Conn) { defer func() { s.Status &^= StatusTCPCmdActive conn.Close() }() s.Status |= StatusTCPCmdActive // Sending "IAMLIVE" every 60 seconds alivectx, alivecancel := context.WithCancel(context.Background()) go func(conn net.Conn, ctx context.Context) { for ctx.Err() == nil { if s.VARAMode { s.Command.Response.Enqueue("IAMALIVE") } time.Sleep(60 * time.Second) } }(conn, alivectx) defer alivecancel() /* bufferctx, buffercancel := context.WithCancel(context.Background()) go func(conn net.Conn, ctx context.Context) { for ctx.Err() == nil { if s.VARAMode { s.Command.Response.Enqueue(fmt.Sprintf("BUFFER %d", s.Data.Data.GetLen())) } time.Sleep(10 * time.Second) } }(conn, bufferctx) defer buffercancel() */ s.Protocol <- fmt.Sprintf(color.InGreen("TCP Cmd Connection established with %s\n"), conn.RemoteAddr()) s.Status |= StatusTCPCmdActive ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func(ctx context.Context) { for ctx.Err() == nil { msg, err := s.Command.Response.DequeueOrWaitContext(ctx) if err != nil { s.Protocol <- fmt.Sprintf(color.InRed("End of Cmd Data Connection %s\n"), conn.RemoteAddr()) return } else { s.Protocol <- fmt.Sprintf(color.InCyan("Response: %s\n"), msg) conn.Write([]byte(fmt.Sprintf("%s\r", msg))) } } }(ctx) reader := bufio.NewReader(conn) for { // Read incoming message message, err := reader.ReadString('\r') if err != nil { //s.Protocol <- fmt.Sprintf(color.InRed("Connection closed by %s\n"), conn.RemoteAddr()) s.Status &^= StatusTCPCmdActive alivecancel() //buffercancel() cancel() conn.Close() break } message = strings.TrimSpace(message) re := regexp.MustCompile("[[:^ascii:]]") message = re.ReplaceAllLiteralString(message, "") // prevent any non-ASCII from being sent if message == "" { // do not send an empty message to the controller continue } // Process the VARA TNC protocol commands if s.VARAMode { translatedmsg, predefanswer, err := processVARACommand(message) if err != nil { s.Protocol <- color.InRed(fmt.Sprintf("Error translating VARA command %s: %s\n", message, err.Error())) log.Println(fmt.Sprintf("Error translating VARA command %s: %s\n", message, err.Error())) s.Command.Response.Enqueue("WRONG") } else { if translatedmsg != "" { s.Protocol <- fmt.Sprintf(color.InPurple("(V) Got: %s Sending: %s\n"), message, translatedmsg) s.Command.Cmd.Enqueue(translatedmsg) } if predefanswer != "" { s.Command.Response.Enqueue(predefanswer) } else { s.Command.Response.Enqueue("OK") } } } else { // TCP Mode == passthrough s.Protocol <- fmt.Sprintf(color.InPurple("(T) Sending: %s\n"), message) s.Command.Cmd.Enqueue(message) } } } func handleTCPDataConnection(conn net.Conn) { defer func() { s.Status &^= StatusTCPDataActive conn.Close() }() s.Protocol <- fmt.Sprintf(color.InGreen("TCP Data Connection established with %s\n"), conn.RemoteAddr()) s.Status |= StatusTCPDataActive ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func() { for { msg, err := s.Data.Response.DequeueOrWaitContext(ctx, 1) if err != nil { s.Protocol <- fmt.Sprintf(color.InRed("End of TCP Data Connection %s\n"), conn.RemoteAddr()) return } else { //TODO: VARA if !s.DaemonMode { s.FromPactor.Enqueue(msg) } conn.Write(msg) } } }() reader := bufio.NewReader(conn) for { var temp []byte buf := make([]byte, 1024) n, err := reader.Read(buf) if n > 0 { temp = append(temp, buf[:n]...) } if err != nil { s.Status &^= StatusTCPDataActive cancel() conn.Close() return } //TODO: muss das nicht weg? /* if temp == 0x04 { break } */ if !s.DaemonMode { s.ToPactor.Enqueue(temp) } if n > 255 { // we can dump at max 255 bytes into for _, ck := range Chunks(string(temp), 255) { s.Data.Data.Enqueue([]byte(ck)) } } else { s.Data.Data.Enqueue(temp) } } } // Process VARA commands and return appropriate WA8DED command and a pre-defined answer func processVARACommand(command string) (string, string, error) { switch { case strings.HasPrefix(command, "CONNECT"): re, err := regexp.MatchString(`CONNECT \w+ \w+`, command) if err != nil || !re { return "", "", errors.New("Error matching regex") } c := strings.Split(command, " ") return fmt.Sprintf("C %s", c[2]), "", nil case strings.HasPrefix(command, "DISCONNECT"): return "D", "", nil case strings.HasPrefix(command, "ABORT"): return "DD", "", nil // case strings.HasPrefix(command, "VERSION"): // return "", nil case strings.HasPrefix(command, "LISTEN"): // Handle listen if strings.HasSuffix(command, "ON") { return "%L 1", "", nil } if strings.HasSuffix(command, "OFF") { return "%L 0", "", nil } return "", "", errors.New("Neither ON nor OFF after LISTEN") case strings.HasPrefix(command, "MYCALL"): m := "" // Handle MYCALL s := strings.Split(command, " ") if len(s) > 1 { // send own callsign to PACTOR controller m = s[1] } else { return "", "", errors.New("Invalid MYCALL command") } return fmt.Sprintf("%s%s", "I ", m), "", nil case strings.HasPrefix(command, "COMPRESSION"): // Handle COMPRESSION return "", "", nil case strings.HasPrefix(command, "BW"): // Has no meaning for PACTOR return "", "", nil case strings.HasPrefix(command, "CHAT"): // Has no meaning for PACTOR return "", "", nil case strings.HasPrefix(command, "WINLINK SESSION"): // Has no meaning for PACTOR, just return "OK" return "", "", nil case strings.HasPrefix(command, "P2P SESSION"): // Has no meaning for PACTOR, just return "OK" return "", "", nil case strings.HasPrefix(command, "CWID"): // Has no meaning for PACTOR, just return "OK" return "", "", nil case strings.HasPrefix(command, "PUBLIC"): // Has no meaning for PACTOR, just return "OK" return "", "", nil case strings.HasPrefix(command, "VERSION"): return "", "VERSION 1.0.0", nil default: // Handle unrecognized commands return "", "", errors.New("Unknown command") } } func tcpCmdServer(Config *Userconfig) { // Start listening for connections listener, err := net.Listen("tcp", Config.ServerAddress) if err != nil { s.Protocol <- fmt.Sprintf(color.InWhiteOverRed("Error starting server: %v\n"), err) log.Println(err) return } defer listener.Close() s.Protocol <- fmt.Sprintf("TCP Protocol Server listening on %s\n", Config.ServerAddress) for { // Accept incoming connection conn, err := listener.Accept() if err != nil { s.Protocol <- fmt.Sprintf("Error accepting connection: %v\n", err) continue } // Handle connection in a separate goroutine go handleTCPCmdConnection(conn) } } func tcpDataServer(Config *Userconfig) { listener, err := net.Listen("tcp", Config.DataAddress) if err != nil { s.Protocol <- fmt.Sprintf(color.InWhiteOverRed("Error starting server: %v\n"), err) log.Println(err) return } defer listener.Close() s.Protocol <- fmt.Sprintf("TCP Data Server listening on %s\n", Config.DataAddress) for { // Accept incoming connection conn, err := listener.Accept() if err != nil { s.Protocol <- fmt.Sprintf("Error accepting connection: %v\n", err) continue } // Handle connection in a separate goroutine go handleTCPDataConnection(conn) } }