Init
This commit is contained in:
commit
d911248c53
55
client.go
Normal file
55
client.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BRNSystems/go-telnet"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
Client telnet.Client
|
||||||
|
Username string
|
||||||
|
ID int
|
||||||
|
History []string
|
||||||
|
HistoryIndex int
|
||||||
|
context telnet.Context
|
||||||
|
writer telnet.Writer
|
||||||
|
reader telnet.Reader
|
||||||
|
// Add more fields as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(context telnet.Context, writer telnet.Writer, reader telnet.Reader) int {
|
||||||
|
client := Client{}
|
||||||
|
|
||||||
|
clientsMutex.Lock()
|
||||||
|
clients = append(clients, &client)
|
||||||
|
clientID := len(clients) - 1
|
||||||
|
defer clientsMutex.Unlock()
|
||||||
|
|
||||||
|
clients[clientID].ID = clientID
|
||||||
|
clients[clientID].Username = "Client #" + strconv.Itoa(clientID)
|
||||||
|
clients[clientID].context = context
|
||||||
|
clients[clientID].writer = writer
|
||||||
|
clients[clientID].reader = reader
|
||||||
|
return clientID
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveClient(clientID int) {
|
||||||
|
clientsMutex.Lock()
|
||||||
|
defer clientsMutex.Unlock()
|
||||||
|
clients = append(clients[:clientID], clients[clientID+1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client Client) send(message string) {
|
||||||
|
client.writer.Write([]byte(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIDByName(name string) int {
|
||||||
|
clientsMutex.Lock()
|
||||||
|
defer clientsMutex.Unlock()
|
||||||
|
for _, client := range clients {
|
||||||
|
if client.Username == name {
|
||||||
|
return client.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
121
commands.go
Normal file
121
commands.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommandHandlerFunc func(args []string, clientID int) string
|
||||||
|
|
||||||
|
type CommandRegistry struct {
|
||||||
|
commands map[string]CommandHandlerFunc
|
||||||
|
mutex sync.Mutex // Mutex to safely access commands map
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommandRegistry() *CommandRegistry {
|
||||||
|
return &CommandRegistry{
|
||||||
|
commands: make(map[string]CommandHandlerFunc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *CommandRegistry) RegisterCommand(command string, handler CommandHandlerFunc) {
|
||||||
|
cr.mutex.Lock()
|
||||||
|
defer cr.mutex.Unlock()
|
||||||
|
cr.commands[command] = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *CommandRegistry) GetCommandHandler(command string, clientID int) (CommandHandlerFunc, bool, int) {
|
||||||
|
cr.mutex.Lock()
|
||||||
|
defer cr.mutex.Unlock()
|
||||||
|
handler, ok := cr.commands[command]
|
||||||
|
return handler, ok, clientID
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterCommands(registry *CommandRegistry) {
|
||||||
|
registry.RegisterCommand("help", HelpCommand)
|
||||||
|
registry.RegisterCommand("setname", SetNameCommand)
|
||||||
|
registry.RegisterCommand("list", ListClientsCommand)
|
||||||
|
registry.RegisterCommand("tell", TellClientCommand)
|
||||||
|
registry.RegisterCommand("clear", ClearCommand)
|
||||||
|
registry.RegisterCommand("rng", RandomCommand)
|
||||||
|
// Add more commands here...
|
||||||
|
}
|
||||||
|
|
||||||
|
func HelpCommand(args []string, clientID int) string {
|
||||||
|
// Handle help command
|
||||||
|
if len(clients) > 0 {
|
||||||
|
return "Just help yourself, " + clients[clientID].Username
|
||||||
|
}
|
||||||
|
return "No clients available"
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandomCommand(args []string, clientID int) string {
|
||||||
|
// Handle help command
|
||||||
|
if len(args) == 2 {
|
||||||
|
from, err1 := strconv.Atoi(args[0])
|
||||||
|
to, err2 := strconv.Atoi(args[1])
|
||||||
|
if err1 != nil || err2 != nil {
|
||||||
|
return "Invalid argument"
|
||||||
|
}
|
||||||
|
if to-from <= 0 {
|
||||||
|
return "Invalid range"
|
||||||
|
}
|
||||||
|
return "The number is " + strconv.Itoa(rand.Intn(to-from)+from)
|
||||||
|
}
|
||||||
|
return "No range provided"
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearCommand(args []string, clientID int) string {
|
||||||
|
// Handle help command
|
||||||
|
return "\033[H\033[2J\033[K"
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetNameCommand(args []string, clientID int) string {
|
||||||
|
// Handle help command
|
||||||
|
if len(args) == 1 {
|
||||||
|
newName := args[0]
|
||||||
|
clients[clientID].Username = newName
|
||||||
|
return "Name set to " + newName
|
||||||
|
}
|
||||||
|
return "No name provided"
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListClientsCommand(args []string, clientID int) string {
|
||||||
|
message := ""
|
||||||
|
for i, client := range clients {
|
||||||
|
message += strconv.Itoa(i) + ": " + client.Username + "\n"
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func TellClientCommand(args []string, clientID int) string {
|
||||||
|
message := "Message not delivered"
|
||||||
|
if len(args) >= 2 {
|
||||||
|
clientTo := args[0]
|
||||||
|
//if integer assume clientID
|
||||||
|
var toClientID int
|
||||||
|
var toClientName string
|
||||||
|
toClientID, err := strconv.Atoi(clientTo)
|
||||||
|
if err != nil {
|
||||||
|
toClientID = getIDByName(clientTo)
|
||||||
|
toClientName = clientTo
|
||||||
|
}
|
||||||
|
if toClientID < len(clients) && toClientID >= 0 {
|
||||||
|
toClientName = clients[toClientID].Username
|
||||||
|
myUsername := clients[clientID].Username
|
||||||
|
newMessage := ""
|
||||||
|
//all the remaining arguments are a part of the message, join them with space
|
||||||
|
for i, arg := range args {
|
||||||
|
if i > 0 {
|
||||||
|
newMessage += arg + " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clients[toClientID].send("\a\r\n<" + myUsername + "(" + strconv.Itoa(clientID) + ")> " + newMessage + "\r\n")
|
||||||
|
return "Message sent to " + toClientName
|
||||||
|
} else {
|
||||||
|
message = "Invalid recipient"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
8
go.mod
Normal file
8
go.mod
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module telnetroulette
|
||||||
|
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/BRNSystems/go-telnet v0.0.0-20240607215108-c7f613a868ed
|
||||||
|
github.com/reiver/go-oi v1.0.0
|
||||||
|
)
|
6
go.sum
Normal file
6
go.sum
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
github.com/BRNSystems/go-telnet v0.0.0-20240607213850-6f6b77773107 h1:mch1qDcG/Cj3c+RKlQiTSgtd1ijhFHG986HtEuvJCgA=
|
||||||
|
github.com/BRNSystems/go-telnet v0.0.0-20240607213850-6f6b77773107/go.mod h1:xZD4mQdIL/DseYfMKq66/DDBwxmX48EmqjlMY0opBDg=
|
||||||
|
github.com/BRNSystems/go-telnet v0.0.0-20240607215108-c7f613a868ed h1:ogTX00ebS+daoXPZJ869USmnn1WIrYkZg+v944meYio=
|
||||||
|
github.com/BRNSystems/go-telnet v0.0.0-20240607215108-c7f613a868ed/go.mod h1:xZD4mQdIL/DseYfMKq66/DDBwxmX48EmqjlMY0opBDg=
|
||||||
|
github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM=
|
||||||
|
github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI=
|
29
main.go
Normal file
29
main.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BRNSystems/go-telnet"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var clients []*Client
|
||||||
|
var clientsMutex sync.Mutex
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
addr := ":6969"
|
||||||
|
commandRegistry := NewCommandRegistry()
|
||||||
|
termHandler := NewInternalTerminalHandler("# ",
|
||||||
|
` ____ _ _ ____ _ _ _
|
||||||
|
/ ___|| |__ ___ | |_ __ _ _ _ _ __ | _ \ ___ _ _| | ___| |_| |_ ___
|
||||||
|
\___ \| '_ \ / _ \| __/ _`+"`"+` | | | | '_ \ | |_) / _ \| | | | |/ _ \ __| __/ _ \
|
||||||
|
___) | | | | (_) | || (_| | |_| | | | | | _ < (_) | |_| | | __/ |_| || __/
|
||||||
|
|____/|_| |_|\___/ \__\__, |\__,_|_| |_| |_| \_\___/ \__,_|_|\___|\__|\__\___|
|
||||||
|
|___/`,
|
||||||
|
commandRegistry)
|
||||||
|
|
||||||
|
// Register commands
|
||||||
|
RegisterCommands(commandRegistry)
|
||||||
|
|
||||||
|
if err := telnet.ListenAndServe(addr, termHandler); nil != err {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
108
terminal_handler.go
Normal file
108
terminal_handler.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BRNSystems/go-telnet"
|
||||||
|
"github.com/reiver/go-oi"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InternalTerminalHandler struct {
|
||||||
|
ShellCharacter string
|
||||||
|
WelcomeMessage string
|
||||||
|
CommandRegistry *CommandRegistry
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInternalTerminalHandler(shellCharacter, welcomeMessage string, commandRegistry *CommandRegistry) *InternalTerminalHandler {
|
||||||
|
return &InternalTerminalHandler{
|
||||||
|
ShellCharacter: shellCharacter,
|
||||||
|
WelcomeMessage: welcomeMessage,
|
||||||
|
CommandRegistry: commandRegistry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *InternalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet.Writer, r telnet.Reader) {
|
||||||
|
clientID := NewClient(ctx, w, r)
|
||||||
|
|
||||||
|
defer RemoveClient(clientID)
|
||||||
|
w.RawWrite([]byte{0xff, 0xfb, 0x03}) //send char by char
|
||||||
|
w.RawWrite([]byte{0xff, 0xfb, 0x01}) //echo from server
|
||||||
|
w.Write([]byte(strings.ReplaceAll(handler.WelcomeMessage, "\n", "\r\n") + "\r\n"))
|
||||||
|
//replace every \n with \r\n in WelcomeMessage
|
||||||
|
w.Write([]byte(handler.ShellCharacter))
|
||||||
|
|
||||||
|
var lineBuffer []byte
|
||||||
|
|
||||||
|
CSISequence := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
var bt [1]byte
|
||||||
|
n, err := r.Read(bt[:])
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
b := bt[0]
|
||||||
|
|
||||||
|
if b == 0x03 || b == 0x04 {
|
||||||
|
return
|
||||||
|
} else if b == 0x1B { //escape
|
||||||
|
CSISequence = 1
|
||||||
|
} else if CSISequence == 1 && b == 0x5B {
|
||||||
|
CSISequence = 2
|
||||||
|
} else if CSISequence == 2 && b == 'A' {
|
||||||
|
if len(lineBuffer) == 0 && len(clients[clientID].History) > 0 {
|
||||||
|
if clients[clientID].HistoryIndex < len(clients[clientID].History)-1 {
|
||||||
|
clients[clientID].HistoryIndex++
|
||||||
|
}
|
||||||
|
lineBuffer = []byte(clients[clientID].History[clients[clientID].HistoryIndex])
|
||||||
|
w.RawWrite([]byte{0x1B, 0x5B, 0x4B}) //clear line
|
||||||
|
w.Write(lineBuffer)
|
||||||
|
}
|
||||||
|
} else if CSISequence == 2 && b == 'B' {
|
||||||
|
if len(lineBuffer) == 0 && len(clients[clientID].History) > 0 {
|
||||||
|
if clients[clientID].HistoryIndex > 0 {
|
||||||
|
clients[clientID].HistoryIndex--
|
||||||
|
}
|
||||||
|
lineBuffer = []byte(clients[clientID].History[clients[clientID].HistoryIndex])
|
||||||
|
w.RawWrite([]byte{0x1B, 0x5B, 0x4B}) //clear line
|
||||||
|
w.Write(lineBuffer)
|
||||||
|
}
|
||||||
|
} else if b == 0x7F {
|
||||||
|
if len(lineBuffer) > 0 {
|
||||||
|
w.Write([]byte{0x08, 0x20, 0x08})
|
||||||
|
lineBuffer = lineBuffer[:len(lineBuffer)-1]
|
||||||
|
}
|
||||||
|
} else if b != '\r' && b != '\n' && b != 0 {
|
||||||
|
CSISequence = 0
|
||||||
|
lineBuffer = append(lineBuffer, b)
|
||||||
|
w.RawWrite([]byte{b})
|
||||||
|
} else if b == '\r' {
|
||||||
|
w.Write([]byte("\r\n"))
|
||||||
|
command := string(lineBuffer)
|
||||||
|
args := strings.Fields(command)
|
||||||
|
var message string
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
cmd := args[0]
|
||||||
|
if cmd == "exit" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handlerFunc, ok, clientID := handler.CommandRegistry.GetCommandHandler(cmd, clientID)
|
||||||
|
clients[clientID].HistoryIndex = 0
|
||||||
|
if ok {
|
||||||
|
message = handlerFunc(args[1:], clientID)
|
||||||
|
clients[clientID].History = append(clients[clientID].History, command)
|
||||||
|
} else {
|
||||||
|
message = "Command not found"
|
||||||
|
}
|
||||||
|
message += "\n" + handler.ShellCharacter
|
||||||
|
} else {
|
||||||
|
message = handler.ShellCharacter
|
||||||
|
}
|
||||||
|
oi.LongWrite(w, []byte(strings.ReplaceAll(message, "\n", "\r\n")))
|
||||||
|
lineBuffer = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
util.go
Normal file
14
util.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isInt(s string) bool {
|
||||||
|
for _, c := range s {
|
||||||
|
if !unicode.IsDigit(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user