package main import ( "bufio" "net" "os" "strings" "sync" "time" log "github.com/sirupsen/logrus" ) var ( err error ) type TCPServer struct { Address string Port string Clients map[net.Conn]bool Mutex *sync.Mutex LogWriter *bufio.Writer Reader *bufio.Reader Writer *bufio.Writer Conn net.Conn Listener net.Listener MsgChan chan string CmdChan chan string Log *log.Logger Config *Config MessageSent int } func NewTCPServer(address string, port string) *TCPServer { return &TCPServer{ Address: address, Port: port, Clients: make(map[net.Conn]bool), MsgChan: make(chan string, 100), CmdChan: make(chan string), Mutex: new(sync.Mutex), MessageSent: 0, } } func (s *TCPServer) StartServer() { s.LogWriter = bufio.NewWriter(os.Stdout) s.Listener, err = net.Listen("tcp", Cfg.TelnetServer.Host+":"+Cfg.TelnetServer.Port) if err != nil { Log.Info("Could not create telnet server") } defer s.Listener.Close() Log.Infof("Telnet server listening on %s:%s", Cfg.TelnetServer.Host, Cfg.TelnetServer.Port) go func() { for message := range s.MsgChan { s.broadcastMessage(message) } }() for { s.Conn, err = s.Listener.Accept() Log.Info("Client connected: ", s.Conn.RemoteAddr().String()) if err != nil { Log.Error("Could not accept connections to telnet server") continue } s.Mutex.Lock() s.Clients[s.Conn] = true s.Mutex.Unlock() go s.handleConnection() } } func (s *TCPServer) handleConnection() { // Store the connection locally to avoid race conditions conn := s.Conn conn.Write([]byte("Welcome to the FlexDXCluster telnet server! Type 'bye' to exit.\n")) reader := bufio.NewReader(conn) defer func() { s.Mutex.Lock() delete(s.Clients, conn) s.Mutex.Unlock() conn.Close() Log.Infof("Client %s disconnected", conn.RemoteAddr().String()) }() for { message, err := reader.ReadString('\n') if err != nil { Log.Debugf("Error reading from client %s: %v", conn.RemoteAddr().String(), err) return } message = strings.TrimSpace(message) // if message is bye then disconnect if message == "bye" { Log.Infof("Client %s sent bye command", conn.RemoteAddr().String()) return } if strings.Contains(message, "DX") || strings.Contains(message, "SH/DX") || strings.Contains(message, "set") || strings.Contains(message, "SET") { // send DX spot to the client select { case s.CmdChan <- message: Log.Debugf("Command from client %s: %s", conn.RemoteAddr().String(), message) default: Log.Warn("Command channel is full, dropping command") } } } } func (s *TCPServer) Write(message string) (n int, err error) { _, err = s.Writer.Write([]byte(message)) if err == nil { err = s.Writer.Flush() } return } func (s *TCPServer) broadcastMessage(message string) { s.Mutex.Lock() defer s.Mutex.Unlock() if len(s.Clients) == 0 { return } if s.MessageSent == 0 { time.Sleep(3 * time.Second) s.MessageSent = 1 } // Collect failed clients var failedClients []net.Conn for client := range s.Clients { _, err := client.Write([]byte(message + "\r\n")) s.MessageSent++ if err != nil { Log.Warnf("Error sending to client %s: %v", client.RemoteAddr(), err) failedClients = append(failedClients, client) } } // Remove failed clients for _, client := range failedClients { delete(s.Clients, client) client.Close() } }