From 6deff7f6ea24c0ceda6d8bfb988fcb28ee3af990 Mon Sep 17 00:00:00 2001 From: adman Date: Sun, 18 Jan 2026 17:10:46 +0100 Subject: [PATCH] yes a lot of stuff --- client/modules/config_ignorer.go | 2 +- client/modules/login.go | 2 +- client/modules/status.go | 2 +- error_utils/error_utils.go | 8 -- event/event_bus.go | 18 +++- proto/connection_test.go | 10 +- proto_base/base_types.go | 6 +- server/server.go | 16 +++- server/server_modules/kicker.go | 39 ++++++++ server/server_modules/login_server.go | 99 +++++++++++++++++--- server/server_modules/status.go | 17 ++-- tools/uncodeable_packet_hunter_proxy/main.go | 34 ++++--- 12 files changed, 193 insertions(+), 60 deletions(-) delete mode 100644 error_utils/error_utils.go create mode 100644 server/server_modules/kicker.go diff --git a/client/modules/config_ignorer.go b/client/modules/config_ignorer.go index 1c3ecfc..744575c 100644 --- a/client/modules/config_ignorer.go +++ b/client/modules/config_ignorer.go @@ -29,7 +29,7 @@ func (c *ConfigIgnorer) OnFinish(_ v1_21_8.ConfigurationToClientPacketFinishConf return } c.SetState(proto_base.Play) - err = event.ErrHandlerDone + err = event.HandlerDoneErr{} return } diff --git a/client/modules/login.go b/client/modules/login.go index 0a2fead..54f2085 100644 --- a/client/modules/login.go +++ b/client/modules/login.go @@ -141,7 +141,7 @@ func (s *LoginClient) OnSuccess(success *v1_21_8.LoginToClientPacketSuccess) (er return } s.SetState(proto_base.Configuration) - err = event.ErrHandlerDone + err = event.HandlerDoneErr{} return } diff --git a/client/modules/status.go b/client/modules/status.go index b8439a2..70b6598 100644 --- a/client/modules/status.go +++ b/client/modules/status.go @@ -49,6 +49,6 @@ func (s *StatusClient) OnStart(_ event.OnStart) (err error) { func (s *StatusClient) OnStatus(p v1_21_8.StatusToClientPacketServerInfo) (err error) { s.Status = p.Response - err = event.ErrHandlerDone + err = event.HandlerDoneErr{} return } diff --git a/error_utils/error_utils.go b/error_utils/error_utils.go deleted file mode 100644 index e4359cd..0000000 --- a/error_utils/error_utils.go +++ /dev/null @@ -1,8 +0,0 @@ -package error_utils - -func Unwrap[T any](v T, err error) T { - if err != nil { - panic(err) - } - return v -} diff --git a/event/event_bus.go b/event/event_bus.go index 881f00d..e8ff696 100644 --- a/event/event_bus.go +++ b/event/event_bus.go @@ -27,8 +27,18 @@ type ( Tick struct{} ) -// ErrHandlerDone will stop the event loop. -var ErrHandlerDone = errors.New("handler done") +// HandlerDoneErr will stop the event loop and return the error if provided. +type HandlerDoneErr struct { + Return error +} + +func (e HandlerDoneErr) Error() string { + return "HandlerDoneErr" +} + +func (e HandlerDoneErr) Unwrap() error { + return e.Return +} // ErrDontForward will stop the event from being forwarded to other handlers, including anything handlers. var ErrDontForward = errors.New("dont forward") @@ -101,8 +111,8 @@ func (l *Loop) Start() (err error) { break } } - if errors.Is(err, ErrHandlerDone) { - err = nil + if errors.Is(err, HandlerDoneErr{}) { + err = errors.Unwrap(err) } return } diff --git a/proto/connection_test.go b/proto/connection_test.go index 58754f1..afdcdab 100644 --- a/proto/connection_test.go +++ b/proto/connection_test.go @@ -62,7 +62,7 @@ func NewStateFuzzer(state proto_base.State, actor proto_base.Actor) func(t *test } func FuzzConnHandshakingToServer(f *testing.F) { - f.Fuzz(NewStateFuzzer(proto_base.Handshaking, proto_base.Server)) + f.Fuzz(NewStateFuzzer(proto_base.Handshaking, proto_base.Servee)) } func FuzzConnHandshakingToClient(f *testing.F) { @@ -70,7 +70,7 @@ func FuzzConnHandshakingToClient(f *testing.F) { } func FuzzConnStatusToServer(f *testing.F) { - f.Fuzz(NewStateFuzzer(proto_base.Status, proto_base.Server)) + f.Fuzz(NewStateFuzzer(proto_base.Status, proto_base.Servee)) } func FuzzConnStatusToClient(f *testing.F) { @@ -78,7 +78,7 @@ func FuzzConnStatusToClient(f *testing.F) { } func FuzzConnLoginToServer(f *testing.F) { - f.Fuzz(NewStateFuzzer(proto_base.Login, proto_base.Server)) + f.Fuzz(NewStateFuzzer(proto_base.Login, proto_base.Servee)) } func FuzzConnLoginToClient(f *testing.F) { @@ -86,7 +86,7 @@ func FuzzConnLoginToClient(f *testing.F) { } func FuzzConnConfigToServer(f *testing.F) { - f.Fuzz(NewStateFuzzer(proto_base.Configuration, proto_base.Server)) + f.Fuzz(NewStateFuzzer(proto_base.Configuration, proto_base.Servee)) } func FuzzConnConfigToClient(f *testing.F) { @@ -94,7 +94,7 @@ func FuzzConnConfigToClient(f *testing.F) { } func FuzzConnPlayToServer(f *testing.F) { - f.Fuzz(NewStateFuzzer(proto_base.Play, proto_base.Server)) + f.Fuzz(NewStateFuzzer(proto_base.Play, proto_base.Servee)) } func FuzzConnPlayToClient(f *testing.F) { diff --git a/proto_base/base_types.go b/proto_base/base_types.go index 4c7201c..0d6292f 100644 --- a/proto_base/base_types.go +++ b/proto_base/base_types.go @@ -41,13 +41,13 @@ const ( type Actor int const ( - Server Actor = iota + Servee Actor = iota // Servee is the one being served Client ) func (a Actor) SendDirection() Direction { switch a { - case Server: + case Servee: return ToClient case Client: return ToServer @@ -58,7 +58,7 @@ func (a Actor) SendDirection() Direction { func (a Actor) ReceiveDirection() Direction { switch a { - case Server: + case Servee: return ToServer case Client: return ToClient diff --git a/server/server.go b/server/server.go index b2b78f8..a560d14 100644 --- a/server/server.go +++ b/server/server.go @@ -1,11 +1,13 @@ package server import ( - "log" + "log/slog" "net" "github.com/admin-else/strom/proto" "github.com/admin-else/strom/proto_base" + "github.com/admin-else/strom/server/server_modules" + "github.com/admin-else/strom/text" ) func Servee(c net.Conn) (ret *proto.Conn) { @@ -13,7 +15,7 @@ func Servee(c net.Conn) (ret *proto.Conn) { _ = ret.SetVersion("1.21.8") // cant fail ret.SetState(proto_base.Handshaking) ret.CompressionThreshold = -1 - ret.Actor = proto_base.Server + ret.Actor = proto_base.Servee ret.Conn = c ret.R = c ret.W = c @@ -57,9 +59,15 @@ func StartServerWithOnConn(listenAddr string, onConn func(c *proto.Conn) (err er return } go func() { - connErr := onConn(Servee(cNet)) + c := Servee(cNet) + defer c.Close() + connErr := onConn(c) if connErr != nil { - log.Println(connErr) + slog.Error("Error while handling client", "error", connErr, "client", c.Conn.RemoteAddr()) + connErr = server_modules.Kick(c, text.Pretty(connErr.Error())) + if connErr != nil { + slog.Error("Error while kicking client", "error", connErr, "client", c.Conn.RemoteAddr()) + } } }() } diff --git a/server/server_modules/kicker.go b/server/server_modules/kicker.go new file mode 100644 index 0000000..bab6781 --- /dev/null +++ b/server/server_modules/kicker.go @@ -0,0 +1,39 @@ +package server_modules + +import ( + "errors" + + "github.com/admin-else/strom/nbt" + "github.com/admin-else/strom/proto" + "github.com/admin-else/strom/proto_base" + "github.com/admin-else/strom/proto_generated/v1_21_8" + "github.com/admin-else/strom/text" +) + +var ( + NotClientErr = errors.New("not a servee") + CantKickInStateErr = errors.New("cant kick in this state") +) + +func Kick(c *proto.Conn, reason *text.Component) (err error) { + if c.Actor != proto_base.Servee { + err = NotClientErr + return + } + switch c.State() { + case proto_base.Handshaking, proto_base.Status: + err = CantKickInStateErr + case proto_base.Login: + var b []byte + b, err = reason.MarshalJSON() + if err != nil { + return + } + err = c.Send(&v1_21_8.LoginToClientPacketDisconnect{Reason: string(b)}) + case proto_base.Configuration: + err = c.Send(&v1_21_8.ConfigurationToClientPacketDisconnect{Reason: nbt.Anon{Value: reason.ToNBT()}}) + case proto_base.Play: + err = c.Send(&v1_21_8.PlayToClientPacketKickDisconnect{Reason: nbt.Anon{Value: reason.ToNBT()}}) + } + return +} diff --git a/server/server_modules/login_server.go b/server/server_modules/login_server.go index 476cf5f..21d59c8 100644 --- a/server/server_modules/login_server.go +++ b/server/server_modules/login_server.go @@ -1,7 +1,11 @@ package server_modules import ( + "encoding/json" + "errors" "fmt" + "log/slog" + "slices" "github.com/admin-else/strom/api" "github.com/admin-else/strom/event" @@ -11,6 +15,8 @@ import ( "github.com/google/uuid" ) +var StatusServedError = errors.New("status served") + var UnexpectedStatusRequest = UnexpectedNextStateError{proto_base.Status} type NameAndUUID struct { @@ -26,25 +32,51 @@ func (u UnexpectedNextStateError) Error() string { return fmt.Sprintf("unexpected next state: %v", u.NextState) } +var IncompatibleProtocolVersionError = fmt.Errorf("incompatible protocol version") + type LoginServer struct { *proto.Conn //OnlineMode bool FIXME: implement this ServerHost string + ServerPort uint16 Requested NameAndUUID Given *NameAndUUID CompressionThreshold int32 + Status []byte + CompatibleVersions []int32 } func (l *LoginServer) OnHandshake(packet *v1_21_8.HandshakingToServerPacketSetProtocol) (err error) { l.SetState(proto_base.State(packet.NextState)) l.ServerHost = packet.ServerHost - if packet.NextState != int32(proto_base.Login) { + l.ServerPort = packet.ServerPort + + if len(l.CompatibleVersions) != 0 { + if !slices.Contains(l.CompatibleVersions, packet.ProtocolVersion) { + err = IncompatibleProtocolVersionError + return + } + } + + if packet.NextState != int32(proto_base.Login) && !(l.Status != nil && packet.NextState == int32(proto_base.Status)) { err = UnexpectedNextStateError{proto_base.State(packet.NextState)} - return } return } +func (l *LoginServer) OnStatusRequest(_ *v1_21_8.StatusToServerPacketPingStart) (err error) { + err = l.Send(&v1_21_8.StatusToClientPacketServerInfo{Response: string(l.Status)}) + return +} + +func (l *LoginServer) OnStatusPing(packet *v1_21_8.StatusToServerPacketPing) (err error) { + err = l.Send(&v1_21_8.StatusToClientPacketPing{Time: packet.Time}) + if err != nil { + return + } + return event.HandlerDoneErr{Return: StatusServedError} +} + func (l *LoginServer) SetCompressionThreshold(threshold int32) (err error) { err = l.Send(&v1_21_8.LoginToClientPacketCompress{Threshold: threshold}) if err != nil { @@ -73,7 +105,7 @@ func (l *LoginServer) OnLoginStart(packet *v1_21_8.LoginToServerPacketLoginStart func (l *LoginServer) OnLoginAcknowledged(_ *v1_21_8.LoginToServerPacketLoginAcknowledged) (err error) { l.SetState(proto_base.Configuration) - err = event.ErrHandlerDone + err = event.HandlerDoneErr{} return } @@ -91,18 +123,59 @@ func (l *LoginServer) OnCycle(_ event.Tick) (err error) { return } -func ServeLogin(c *proto.Conn) (ret *LoginServer, err error) { - ret = &LoginServer{Conn: c} - ret.CompressionThreshold = 256 - err = ret.Start(ret) - return +type LoginServerSetting func(*LoginServer) + +func WithOtherAccount(a *api.Account) LoginServerSetting { + return func(s *LoginServer) { + if s.Given == nil { + return + } + s.Given = &NameAndUUID{ + Name: a.Name, + UUID: a.Uuid, + } + } } -func ServeLoginWithOtherAccount(c *proto.Conn, a *api.Account) (ret *LoginServer, err error) { - ret = &LoginServer{Conn: c, Given: &NameAndUUID{ - Name: a.Name, - UUID: a.Uuid, - }} +func WithRawStatus(status []byte) LoginServerSetting { + return func(s *LoginServer) { + s.Status = status + } +} + +func WithStatus(response StatusResponse) LoginServerSetting { + var status []byte + status, err := json.Marshal(response) + if err != nil { + panic(err) + } + slog.Debug("With status", "status", status) + return WithRawStatus(status) +} + +func WithCompatibleVersion(versions int32) LoginServerSetting { + return WithCompatibleVersions([]int32{versions}) +} + +func WithCompatibleVersionsRange(start, end int32) LoginServerSetting { + var versions []int32 + for i := start; i <= end; i++ { + versions = append(versions, i) + } + return WithCompatibleVersions(versions) +} + +func WithCompatibleVersions(versions []int32) LoginServerSetting { + return func(s *LoginServer) { + s.CompatibleVersions = versions + } +} + +func ServeLogin(c *proto.Conn, settings ...LoginServerSetting) (ret *LoginServer, err error) { + ret = &LoginServer{Conn: c} + for _, s := range settings { + s(ret) + } ret.CompressionThreshold = 256 err = ret.Start(ret) return diff --git a/server/server_modules/status.go b/server/server_modules/status.go index b2d0ada..21e56e5 100644 --- a/server/server_modules/status.go +++ b/server/server_modules/status.go @@ -5,27 +5,26 @@ import ( "github.com/admin-else/strom/proto" "github.com/admin-else/strom/proto_generated/v1_21_8" + "github.com/admin-else/strom/text" ) // StatusResponse here is the structure I used: https://minecraft.wiki/w/Java_Edition_protocol/Server_List_Ping#Status_Response type StatusResponse struct { Version struct { - Name string `json:"name"` - Protocol int `json:"protocol"` + Name *text.Component `json:"name"` + Protocol int `json:"protocol"` } `json:"version"` Players struct { Max int `json:"max"` Online int `json:"online"` Sample []struct { - Name string `json:"name"` - ID string `json:"id"` + Name *text.Component `json:"name"` + ID string `json:"id"` } `json:"sample"` } `json:"players"` - Description struct { - Text string `json:"text"` - } `json:"description"` - Favicon string `json:"favicon"` - EnforcesSecureChat bool `json:"enforcesSecureChat"` + Description *text.Component `json:"description"` + Favicon string `json:"favicon"` + EnforcesSecureChat bool `json:"enforcesSecureChat"` } type StatusServer struct { diff --git a/tools/uncodeable_packet_hunter_proxy/main.go b/tools/uncodeable_packet_hunter_proxy/main.go index e99eeeb..ac3e0aa 100644 --- a/tools/uncodeable_packet_hunter_proxy/main.go +++ b/tools/uncodeable_packet_hunter_proxy/main.go @@ -1,8 +1,8 @@ +// THIS THING IS BROKEN package main import ( "crypto/sha256" - "errors" "fmt" "os" @@ -14,6 +14,7 @@ import ( "github.com/admin-else/strom/proto_generated/v1_21_8" "github.com/admin-else/strom/server" "github.com/admin-else/strom/server/server_modules" + "github.com/admin-else/strom/text" ) type ProxyClient struct { @@ -123,22 +124,33 @@ func SaveUnCodeAbleAsTest(d proto_base.Direction, packet proto.UnCodablePacket) fmt.Println("Error", packet.Err, "saved to", f.Name(), "data:", packet.Data) } -var StatusResponse = server_modules.StatusResponse{Description: struct { - Text string `json:"text"` -}{Text: "Un-code-able packet hunter proxy"}, Version: struct { - Name string `json:"name"` - Protocol int `json:"protocol"` -}{Name: "STROM", Protocol: 772}} +var StatusResponse = server_modules.StatusResponse{ + Version: struct { + Name *text.Component `json:"name"` + Protocol int `json:"protocol"` + }{ + Name: text.Pretty("STROM"), + Protocol: 772, + }, + Players: struct { + Max int `json:"max"` + Online int `json:"online"` + Sample []struct { + Name *text.Component `json:"name"` + ID string `json:"id"` + } `json:"sample"` + }{}, + Description: text.Pretty("Hunt the uncodeable packets"), + Favicon: "", + EnforcesSecureChat: false, +} func main() { err := server.StartServerWithOnConn(":25565", func(serveeConn *proto.Conn) (err error) { p := &Proxy{Conn: serveeConn, Client: nil} acc := api.NewOfflineAccount("sigma") - _, err = server_modules.ServeLoginWithOtherAccount(serveeConn, acc) + _, err = server_modules.ServeLogin(serveeConn, server_modules.WithOtherAccount(acc), server_modules.WithStatus(StatusResponse)) if err != nil { - if errors.Is(err, server_modules.UnexpectedStatusRequest) { - err = server_modules.ServeStatus(serveeConn, StatusResponse) - } return } c, err := modules.ConnectAndLogin("127.0.0.1:25566", acc)