diff options
| author | Fuwn <[email protected]> | 2021-07-04 02:56:14 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2021-07-04 02:56:14 -0700 |
| commit | 38ddbd97122ca71777c500df101f1274b3f11900 (patch) | |
| tree | 9ff64f8446646413a4832fd666d94454ad01f603 /pkg | |
| download | archived-munch-38ddbd97122ca71777c500df101f1274b3f11900.tar.xz archived-munch-38ddbd97122ca71777c500df101f1274b3f11900.zip | |
feat(munch): :star:
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/config/config.go | 25 | ||||
| -rw-r--r-- | pkg/server/distributor.go | 58 | ||||
| -rw-r--r-- | pkg/server/hub.go | 70 | ||||
| -rw-r--r-- | pkg/server/server.go | 10 | ||||
| -rw-r--r-- | pkg/server/utils/decode.go | 24 | ||||
| -rw-r--r-- | pkg/server/utils/encode.go | 70 | ||||
| -rw-r--r-- | pkg/server/utils/hex.go | 31 |
7 files changed, 288 insertions, 0 deletions
diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000..b3a3314 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,25 @@ +// Copyright (C) 2021-2021 The Whirlsplash Collective +// SPDX-License-Identifier: GPL-3.0-only + +package config + +import ( + "github.com/spf13/viper" + "log" +) + +func Setup() { + viper.SetConfigName("config.yml") + viper.SetConfigType("yaml") + viper.AddConfigPath(".") + + if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + log.Panicln("Cannot read configuration file:", err) + } else { + log.Panicln("Read configuration file but an error occurred anyway:", err) + } + } + + viper.WatchConfig() +} diff --git a/pkg/server/distributor.go b/pkg/server/distributor.go new file mode 100644 index 0000000..d9c6bcf --- /dev/null +++ b/pkg/server/distributor.go @@ -0,0 +1,58 @@ +// Copyright (C) 2021-2021 The Whirlsplash Collective +// SPDX-License-Identifier: GPL-3.0-only + +package server + +import ( + "encoding/binary" + "github.com/Whirlsplash/munch/pkg/server/utils" + "github.com/spf13/viper" + "log" + "net" + "strconv" +) + +func doDistributor() string { + tcpAddr, _ := net.ResolveTCPAddr( + "tcp", + viper.GetString("worlds.server.ip")+":"+viper.GetString("worlds.server.port"), + ) + conn, _ := net.DialTCP("tcp", nil, tcpAddr) + + // PROPREQ + conn.Write([]byte("\x03\xff\x0a")) + reply := make([]byte, 1024) + conn.Read(reply) + log.Println("AutoServer: PROPREQ") + + // SESSINIT + conn.Write(utils.EncodeSessionInitialization( + viper.GetString("worlds.account.username"), + viper.GetString("worlds.account.password"), + utils.AutoServer, + )) + reply = make([]byte, 1024) + conn.Read(reply) + log.Println("AutoServer: SESSINIT") + + // PROPUPD + conn.Write(utils.EncodePropertyUpdate(viper.GetString("worlds.account.avatar"))) + reply = make([]byte, 1024) + conn.Read(reply) + log.Println("AutoServer: PROPUPD") + + // ROOMIDRQ + conn.Write([]byte( + "\x26\x01\x14\x22\x47\x72\x6f\x75\x6e\x64\x5a\x65\x72\x6f\x23\x73" + + "\x74\x61\x69\x72\x63\x61\x73\x65\x31\x3c\x64\x69\x6d\x65\x6e\x73" + + "\x69\x6f\x6e\x2d\x31\x3e", + )) + reply = make([]byte, 1024) + conn.Read(reply) + port := strconv.Itoa(int(binary.BigEndian.Uint16(reply[44:46]))) + log.Println("AutoServer: ROOMIDRQ") + + conn.Close() + + return port +} diff --git a/pkg/server/hub.go b/pkg/server/hub.go new file mode 100644 index 0000000..9e34523 --- /dev/null +++ b/pkg/server/hub.go @@ -0,0 +1,70 @@ +// Copyright (C) 2021-2021 The Whirlsplash Collective +// SPDX-License-Identifier: GPL-3.0-only + +package server + +import ( + "github.com/Whirlsplash/munch/pkg/server/utils" + "github.com/spf13/viper" + "log" + "net" +) + +func doHub(port string) { + tcpAddr, _ := net.ResolveTCPAddr( + "tcp", + (viper.GetString("worlds.server.ip") + ":" + port), + ) + conn, _ := net.DialTCP("tcp", nil, tcpAddr) + + // PROPREQ + conn.Write([]byte("\x03\xff\x0a")) + reply := make([]byte, 1024) + conn.Read(reply) + log.Println("RoomServer: PROPREQ") + + // SESSINIT + conn.Write(utils.EncodeSessionInitialization( + viper.GetString("worlds.account.username"), + viper.GetString("worlds.account.password"), + utils.RoomServer, + )) + reply = make([]byte, 1024) + conn.Read(reply) + log.Println("RoomServer: SESSINIT") + + // SUB-DIST + conn.Write([]byte( + "\x0d\x01\x16\x00\x02\x00\x00\x00\xfa\x00\x00\x0a\xf8" + + "\x0d\x01\x16\x00\x01\x00\x00\x02\xee\x00\x00\x0c\xf9" + + "\x0d\x01\x16\x00\x07\x00\x00\x36\xaf\x00\x00\x09\xbd" + + "\x0d\x01\x16\x00\x06\xf1\x77\x00\x00\x01\x90\x0d\xcf\x0d\x01\x16" + + "\x98\xee\x00\x00\x00\xc8\x00\x28\x0e\x67\x0d\x01\x16\x00\x04\x00" + + "\x00\x00\xfa\x00\x00\x0c\xb7\x0d\x01\x16\x00\x03\x00\x00\x02\xee" + + "\x00\x00\x0c\x30\x0d\x01\x16\x00\x09\x01\xf4\x03\xe8\x01\xf4\x09" + + "\xd5\x0d\x01\x16\x00\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x0f\x01" + + "\x12\x00\x08\x00\x01\x04\xcc\x09\xa1\x00\x00\x01\x25\x07\x01\x18" + + "\x00\x01\x0c\xf9\x07\x01\x18\x00\x03\x0c\x30\x07\x01\x18\x00\x09" + + "\x09\xd5\x07\x01\x18\x00\x07\x09\xbd\x07\x01\x18\x00\x06\x0d\xcf" + + "\x07\x01\x18\x98\xee\x0e\x67\x07\x01\x18\x00\x04\x0c\xb7\x07\x01" + + "\x18\x00\x02\x0a\xf8", + )) + reply = make([]byte, 1024) + conn.Read(reply) + log.Println("RoomServer: SUBSCRIB") + + // Listen for whispers + for { + reply = make([]byte, 1024) + conn.Read(reply) + if reply[2] == 17 { + decodedText := utils.DecodeText(reply) + log.Printf( + "RoomServer: WHISPER: %s: %s\n", + decodedText.Author, decodedText.Message, + ) + } + } + + conn.Close() +} diff --git a/pkg/server/server.go b/pkg/server/server.go new file mode 100644 index 0000000..d85a5ed --- /dev/null +++ b/pkg/server/server.go @@ -0,0 +1,10 @@ +// Copyright (C) 2021-2021 The Whirlsplash Collective +// SPDX-License-Identifier: GPL-3.0-only + +package server + +func Do() { + // The Distributor isn't actually needed for Munch to work, the only reason + // that we use it is because it gives us the correct Hub port to connect to. + doHub(doDistributor()) +} diff --git a/pkg/server/utils/decode.go b/pkg/server/utils/decode.go new file mode 100644 index 0000000..e819b31 --- /dev/null +++ b/pkg/server/utils/decode.go @@ -0,0 +1,24 @@ +// Copyright (C) 2021-2021 The Whirlsplash Collective +// SPDX-License-Identifier: GPL-3.0-only + +package utils + +import "strconv" + +type Text struct { + Author string + Message string +} + +// Decode a TEXT command from hexadecimal to a Text struct +func DecodeText(data []byte) Text { + authorLength, _ := strconv.ParseInt(strconv.Itoa(int(data[4])), 16, 32) + authorLast := authorLength + 5 + messageLength, _ := strconv.ParseInt(strconv.Itoa(int(data[authorLast])), 16, 32) + messageStart := authorLast + 1 + + return Text{ + Author: string(data[5:(authorLast)]), + Message: string(data[messageStart : messageStart+(messageLength)]), + } +} diff --git a/pkg/server/utils/encode.go b/pkg/server/utils/encode.go new file mode 100644 index 0000000..86e5500 --- /dev/null +++ b/pkg/server/utils/encode.go @@ -0,0 +1,70 @@ +// Copyright (C) 2021-2021 The Whirlsplash Collective +// SPDX-License-Identifier: GPL-3.0-only + +package utils + +const ( + AutoServer = iota + RoomServer +) + +func EncodeSessionInitialization(username string, password string, serverType int) []byte { + data := "" + + // Data length + if serverType == AutoServer { + data += "\x2b" + } else { + data += "\x2c" + } + + // Command header and other stuff we don't need to worry about + data += "\x01\x06\x03\x02\x32\x34\x09\x0a\x32\x30\x32\x30\x30\x33\x31" + + "\x32\x30\x30" + + if serverType == RoomServer { + data += "\x07\x02\x32\x34" + } + + data += + // Username + "\x02" + string(rune(len(username))) + ISO88591ToString(username) + + + // Password + "\x06" + string(rune(len(password))) + ISO88591ToString(password) + + if serverType == AutoServer { + data += "\x0c\x01\x31" + } + + return []byte(data) +} + +func EncodeBuddyListUpdate(buddy string) []byte { + // Command header + data := "\x01\x1d" + + // Buddy UTF-8 length and UTF-8 + data += string(rune(len(buddy))) + ISO88591ToString(buddy) + + // Buddy "add" + data += "\x01" + + // Data length + data = (string(rune(len(data) + 1))) + data + + return []byte(data) +} + +func EncodePropertyUpdate(avatar string) []byte { + // Command header and extra stuff we don't need to worry about + data := "\x01\x0f\x00\x05\x40\x01" + + // Avatar UTF-8 length and UTF-8 + data += string(rune(len("avatar:"+avatar))) + ISO88591ToString("avatar:"+avatar) + + // Data length + data = (string(rune(len(data) + 1))) + data + + return []byte(data) +} diff --git a/pkg/server/utils/hex.go b/pkg/server/utils/hex.go new file mode 100644 index 0000000..a5393aa --- /dev/null +++ b/pkg/server/utils/hex.go @@ -0,0 +1,31 @@ +package utils + +import "unicode/utf8" + +// https://stackoverflow.com/a/43461796/14452787 +// +// I cannot explain the joy that flew through me once I found this ISO-8859-1 +// (Latin-1) to UTF-8 converter. I was looking for a valid solution on how to +// insert the username and password into a hex-escaped string sequence for +// EncodeSessionInitialization for hours and this finally solved all of my +// problems. +func ISO88591ToString(iso string) string { + var utf []rune + for i := 0; i < len(iso); i++ { + r := iso[i] + if utf == nil { + if r < utf8.RuneSelf { + continue + } + utf = make([]rune, len(iso)) + for j, r := range iso[:i] { + utf[j] = rune(r) + } + } + utf[i] = rune(r) + } + if utf == nil { + return string(iso) + } + return string(utf) +} |