From da6f5b66d83889fdd09a673421957d4132035cd0 Mon Sep 17 00:00:00 2001 From: layou233 Date: Fri, 19 Jul 2024 16:27:45 +0800 Subject: [PATCH] fix: Minecraft login packet version difference --- common/bufio/cached.go | 4 +++ protocol/minecraft/outbound.go | 30 +++++++++----------- protocol/minecraft/sniff.go | 52 ++++++++++++++++++++++++++++++---- protocol/sniff.go | 10 +++---- route/router.go | 6 ++-- 5 files changed, 71 insertions(+), 31 deletions(-) diff --git a/common/bufio/cached.go b/common/bufio/cached.go index dde7b4c..fdf8710 100644 --- a/common/bufio/cached.go +++ b/common/bufio/cached.go @@ -27,6 +27,10 @@ func NewCachedConn(c net.Conn) *CachedConn { } } +func (c *CachedConn) Cache() *buf.Buffer { + return c.cache +} + func (c *CachedConn) Read(p []byte) (n int, err error) { if c.cache != nil && !c.cache.IsEmpty() { return c.cache.Read(p) diff --git a/protocol/minecraft/outbound.go b/protocol/minecraft/outbound.go index dd5a083..bef5b05 100644 --- a/protocol/minecraft/outbound.go +++ b/protocol/minecraft/outbound.go @@ -201,9 +201,15 @@ func (o *Outbound) InjectConnection(ctx context.Context, conn *bufio.CachedConn, } switch metadata.Minecraft.NextState { case mcprotocol.NextStateStatus: + // skip Status Request packet + _, err := conn.Peek(2) + if err != nil { + return common.Cause("skip status request: ", err) + } if o.config.Minecraft.MotdFavicon == "" && o.config.Minecraft.MotdDescription == "" { // directly proxy MOTD from server - remoteConn, err := o.connectServer(ctx, metadata) + var remoteConn net.Conn + remoteConn, err = o.connectServer(ctx, metadata) if err != nil { return common.Cause("request remote MOTD: ", err) } @@ -261,7 +267,7 @@ func (o *Outbound) InjectConnection(ctx context.Context, conn *bufio.CachedConn, Writer: common.UnwrapWriter(conn), // unwrap to make writev syscall possible Conn: conn, } - err := clientMC.WriteVectorizedPacket(buffer, motd) + err = clientMC.WriteVectorizedPacket(buffer, motd) if err != nil { return common.Cause("respond MOTD: ", err) } @@ -366,26 +372,16 @@ func (o *Outbound) InjectConnection(ctx context.Context, conn *bufio.CachedConn, binary.BigEndian.PutUint16(buffer.Extend(2), port) buffer.WriteByte(mcprotocol.NextStateLogin) mcprotocol.AppendPacketLength(buffer, buffer.Len()) - // construct login packet - loginPacketSize := 2 + len(metadata.Minecraft.PlayerName) // should not exceed 127 - hasUUID := false - if metadata.Minecraft.UUID != [16]byte{} { // is not empty - loginPacketSize += 16 - hasUUID = true - } - buffer.WriteByte(byte(loginPacketSize)) - buffer.WriteByte(0) // Server bound : Login Start - buffer.WriteByte(byte(len(metadata.Minecraft.PlayerName))) - buffer.WriteString(metadata.Minecraft.PlayerName) - if hasUUID { - buffer.Write(metadata.Minecraft.UUID[:]) - } - _, err = serverConn.Write(buffer.Bytes()) + // write handshake and login packet + cache := conn.Cache() + vector := net.Buffers{buffer.Bytes(), cache.Bytes()} + _, err = vector.WriteTo(serverConn) buffer.Release() if err != nil { serverConn.Close() return common.Cause("server handshake: ", err) } + cache.Advance(cache.Len()) // all written o.logger.Info().Str("id", metadata.ConnectionID).Str("outbound", o.config.Name). Str("player", metadata.Minecraft.PlayerName).Msg("Created Minecraft connection") o.onlineCount.Add(1) diff --git a/protocol/minecraft/sniff.go b/protocol/minecraft/sniff.go index a32f923..3326597 100644 --- a/protocol/minecraft/sniff.go +++ b/protocol/minecraft/sniff.go @@ -2,6 +2,7 @@ package minecraft import ( "errors" + "io" "time" "github.com/layou233/zbproxy/v3/adapter" @@ -85,6 +86,7 @@ func SniffClientHandshake(conn bufio.PeekConn, metadata *adapter.Metadata) error } metadata.Minecraft.NextState = int8(nextState) + metadata.Minecraft.SniffPosition = conn.CurrentPosition() if nextState == mcprotocol.NextStateStatus { // status packet conn.SetReadDeadline(time.Now().Add(10 * time.Second)) @@ -99,9 +101,6 @@ func SniffClientHandshake(conn bufio.PeekConn, metadata *adapter.Metadata) error if err != nil { return common.Cause("read packet size: ", err) } - if packetSize > 33 { // maximum possible size of this kind of packet - return ErrBadPacket - } conn.SetReadDeadline(time.Now().Add(10 * time.Second)) packetContent, err = conn.Peek(int(packetSize)) if err != nil { @@ -120,11 +119,52 @@ func SniffClientHandshake(conn bufio.PeekConn, metadata *adapter.Metadata) error if err != nil { return common.Cause("read player name: ", err) } - if buffer.Len() == 16 { // UUID exists - copy(metadata.Minecraft.UUID[:], buffer.Bytes()) + if metadata.Minecraft.ProtocolVersion >= 764 { // 1.20.2 + if buffer.Len() == 16 { // UUID exists + copy(metadata.Minecraft.UUID[:], buffer.Bytes()) + } + } else if metadata.Minecraft.ProtocolVersion >= 761 { // 1.19.3 + var hasUUID byte + hasUUID, err = buffer.ReadByte() + if err != nil { + return common.Cause("read has UUID: ", err) + } + if hasUUID == mcprotocol.BooleanTrue { + copy(metadata.Minecraft.UUID[:], buffer.Bytes()) + } + } else if metadata.Minecraft.ProtocolVersion >= 759 { // 1.19 + var hasSigData byte + hasSigData, err = buffer.ReadByte() + if err != nil { + return common.Cause("read has sig data: ", err) + } + if hasSigData == mcprotocol.BooleanTrue { + // skip timestamp + buffer.Advance(8) // size of Long + var length int32 + // skip public key + length, _, err = mcprotocol.ReadVarIntFrom(buffer) + if err != nil { + return common.Cause("read public key length: ", err) + } + buffer.Advance(int(length)) + // skip signature + length, _, err = mcprotocol.ReadVarIntFrom(buffer) + if err != nil { + return common.Cause("read signature length: ", err) + } + buffer.Advance(int(length)) + } + var hasUUID byte + hasUUID, err = buffer.ReadByte() + if err != nil && err != io.EOF { + return common.Cause("read has UUID: ", err) + } + if hasUUID == mcprotocol.BooleanTrue { + copy(metadata.Minecraft.UUID[:], buffer.Bytes()) + } } } - metadata.Minecraft.SniffPosition = conn.CurrentPosition() return nil } diff --git a/protocol/sniff.go b/protocol/sniff.go index 9704ec8..43cc7de 100644 --- a/protocol/sniff.go +++ b/protocol/sniff.go @@ -33,7 +33,7 @@ func Sniff(logger *log.Logger, conn bufio.PeekConn, metadata *adapter.Metadata, if metadata.Minecraft == nil { err = minecraft.SniffClientHandshake(conn, metadata) if err != nil { - logger.Trace().Str("protocol", protocol).Err(err).Msg("sniff error") + logger.Trace().Str("protocol", protocol).Err(err).Msg("Sniff error") } } if !sniffAll { @@ -48,7 +48,7 @@ func Sniff(logger *log.Logger, conn bufio.PeekConn, metadata *adapter.Metadata, for _, snifferFunc := range registry { err = snifferFunc(logger, conn, metadata) if err != nil { - logger.Trace().Str("protocol", protocol).Err(err).Msg("sniff error") + logger.Trace().Str("protocol", protocol).Err(err).Msg("Sniff error") } } return @@ -56,13 +56,13 @@ func Sniff(logger *log.Logger, conn bufio.PeekConn, metadata *adapter.Metadata, if snifferFunc := registry[protocol]; snifferFunc != nil { err = snifferFunc(logger, conn, metadata) if err != nil { - logger.Trace().Str("protocol", protocol).Err(err).Msg("sniff error") + logger.Trace().Str("protocol", protocol).Err(err).Msg("Sniff error") } } else { - logger.Fatal().Str("protocol", protocol).Msg("unsupported protocol") + logger.Fatal().Str("protocol", protocol).Msg("Unsupported protocol") } } else { - logger.Fatal().Str("protocol", protocol).Msg("unsupported protocol") + logger.Fatal().Str("protocol", protocol).Msg("Unsupported protocol") } } conn.Rewind(startPosition) diff --git a/route/router.go b/route/router.go index 1b4f3aa..98a7752 100644 --- a/route/router.go +++ b/route/router.go @@ -142,11 +142,11 @@ func (r *Router) HandleConnection(conn net.Conn, metadata *adapter.Metadata) { } r.access.RUnlock() err = bufio.CopyConn(destinationConn, cachedConn) - logger := r.logger.Warn().Str("id", metadata.ConnectionID).Str("outbound", outbound.Name()) if err != nil { - logger = logger.Err(err) + r.logger.Warn().Str("id", metadata.ConnectionID).Str("outbound", outbound.Name()).Err(err).Msg("Handled connection") + } else { + r.logger.Info().Str("id", metadata.ConnectionID).Str("outbound", outbound.Name()).Msg("Handled connection") } - logger.Msg("Handled connection") cachedConn.Close() return }