diff --git a/README.MD b/README.MD index 69083de..865deda 100644 --- a/README.MD +++ b/README.MD @@ -14,5 +14,5 @@ A Minecraft proxy that can extended with JavaScript. ## Applications -[liter-proxy](./cmds/liter-proxy/README.MD): The minecraft forward proxy which support socks5 -[liter-server](./cmds/liter-server/README.MD): The minecraft reverse proxy with a simple manage dashboard +- [liter-proxy](./cmds/liter-proxy/README.MD): The minecraft forward proxy which support socks5 +- [liter-server](./cmds/liter-server/README.MD): The minecraft reverse proxy with a simple manage dashboard diff --git a/cmds/liter-server/api_v1.go b/cmds/liter-server/api_v1.go index 4e8d6f4..1279ec8 100644 --- a/cmds/liter-server/api_v1.go +++ b/cmds/liter-server/api_v1.go @@ -482,6 +482,7 @@ func registerPlayerAPI(s *Server, g *gin.RouterGroup){ ctx.Next() }) gu.GET("/head", func(ctx *gin.Context){ + ctx.Header("Cache-Control", "no-cache") uid := ctx.Value(uuidKey).(uuid.UUID) profile, err := AuthClient.GetPlayerProfile(uid) if err != nil { @@ -511,9 +512,9 @@ func registerPlayerAPI(s *Server, g *gin.RouterGroup){ func registerStatus(s *Server, g *gin.RouterGroup){ g.GET("/conns", func(ctx *gin.Context){ type resT struct { - Id int `json:"id"` - Addr string `json:"addr"` - When int64 `json:"when"` + Id int `json:"id"` + Addr string `json:"addr"` + When int64 `json:"when"` LocalAddr string `json:"localAddr"` Server string `json:"server"` Player *liter.PlayerInfo `json:"player,omitempty"` @@ -536,6 +537,10 @@ func registerStatus(s *Server, g *gin.RouterGroup){ }) }) ctx.Header("Last-Modified", lm) + if lm2 := ctx.GetHeader("Last-Modified"); len(lm2) != 0 && lm2 == lm { + ctx.Status(http.StatusNotModified) + return + } ctx.JSON(http.StatusOK, gin.H{ "status": "ok", "data": data, diff --git a/cmds/liter-server/handler.go b/cmds/liter-server/handler.go index 33e8079..a3837c3 100644 --- a/cmds/liter-server/handler.go +++ b/cmds/liter-server/handler.go @@ -51,7 +51,7 @@ func (s *Server)handle(c *liter.Conn, cfg *Config){ c0.SetLocalServer(hp.Addr, svr.Id) ploger.Infof("Connected with address [%s:%d], passing to server %q[%s]", hp.Addr, hp.Port, svr.Id, svr.Target) - var lp liter.LoginStartPacket + var lp liter.LoginStartPkt if isPing { if svr.HandlePing { diff --git a/cmds/liter-server/server.go b/cmds/liter-server/server.go index bc70021..b9ac284 100644 --- a/cmds/liter-server/server.go +++ b/cmds/liter-server/server.go @@ -3,6 +3,8 @@ package main import ( "context" + crand "crypto/rand" + "crypto/rsa" "errors" "net" "net/http" @@ -52,6 +54,7 @@ type Server struct{ scripts *script.Manager handler http.Handler users *UserStorage + rsaKey *rsa.PrivateKey inShutdown atomic.Bool mux sync.Mutex @@ -64,8 +67,14 @@ func NewServer(sm *script.Manager)(s *Server){ scripts: sm, users: NewUserStorage(filepath.Join(configDir, "users.json")), } + var err error + + if s.rsaKey, err = rsa.GenerateKey(crand.Reader, 1024); err != nil { + loger.Fatalf("Cannot generate RSA key: %v", err) + } + s.initHandler() - if err := s.users.Load(); errors.Is(err, os.ErrNotExist) { + if err = s.users.Load(); errors.Is(err, os.ErrNotExist) { s.users.Save() } return diff --git a/preset_packets.go b/preset_packets.go index 11ec01e..0c5af52 100644 --- a/preset_packets.go +++ b/preset_packets.go @@ -2,9 +2,11 @@ package liter import ( - "strings" + "crypto/rsa" + "crypto/x509" "fmt" "io" + "strings" ) const ( @@ -61,6 +63,9 @@ func (p *HandshakePkt)Decode(r *PacketReader)(err error){ return } +//////////////////////////// +//// BEGIN PING PACKETS //// +//////////////////////////// type StatusRequestPkt struct{} @@ -160,6 +165,13 @@ func (p *PingResponsePkt)DecodeFrom(r *PacketReader)(err error){ return } +///////////////////////////// +//// BEGIN LOGIN PACKETS //// +///////////////////////////// + +// See: https://wiki.vg/Protocol#Login +// See: https://wiki.vg/Protocol_Encryption + type DisconnectPkt struct { Reason *Chat } @@ -177,8 +189,7 @@ func (p *DisconnectPkt)DecodeFrom(r *PacketReader)(err error){ return } - -type LoginStartPacket struct { +type LoginStartPkt struct { Name string // if protocol >= 1.19 && protocol <= 1.19.2 @@ -191,16 +202,16 @@ type LoginStartPacket struct { Id Optional[UUID] } -var _ Packet = (*LoginStartPacket)(nil) +var _ Packet = (*LoginStartPkt)(nil) -func (p LoginStartPacket)String()(string){ +func (p LoginStartPkt)String()(string){ if p.Id.Ok { - return fmt.Sprintf("", p.Name, p.Id.V) + return fmt.Sprintf("", p.Name, p.Id.V) } - return fmt.Sprintf("", p.Name) + return fmt.Sprintf("", p.Name) } -func (p LoginStartPacket)Encode(b *PacketBuilder){ +func (p LoginStartPkt)Encode(b *PacketBuilder){ protocol := b.Protocol() b.String(p.Name) if protocol >= V1_19 { @@ -224,7 +235,7 @@ func (p LoginStartPacket)Encode(b *PacketBuilder){ } } -func (p *LoginStartPacket)DecodeFrom(r *PacketReader)(err error){ +func (p *LoginStartPkt)DecodeFrom(r *PacketReader)(err error){ protocol := r.Protocol() var ok bool if p.Name, ok = r.String(); !ok { @@ -269,3 +280,134 @@ func (p *LoginStartPacket)DecodeFrom(r *PacketReader)(err error){ } return } + +type LoginEncryptionRequestPkt struct { + ServerID string // Appears to be empty. + PublicKey *rsa.PublicKey // encoded in ASN.1 DER format + /* A sequence of random bytes generated by the server. + * Length of Verify Token is always 4 for Notchian servers. */ + VerifyToken ByteArray +} + +var _ Packet = (*LoginEncryptionRequestPkt)(nil) + +func (p LoginEncryptionRequestPkt)Encode(b *PacketBuilder){ + b.String(p.ServerID) + pubKey, _ := x509.MarshalPKIXPublicKey(p.PublicKey) + b.VarInt((VarInt)(len(pubKey))) + b.ByteArray(pubKey) + b.VarInt((VarInt)(len(p.VerifyToken))) + b.ByteArray(p.VerifyToken) +} + +func (p *LoginEncryptionRequestPkt)DecodeFrom(r *PacketReader)(err error){ + var ok bool + var n VarInt + if p.ServerID, ok = r.String(); !ok { + return io.EOF + } + if n, ok = r.VarInt(); !ok { + return io.EOF + } + pubKey := make(ByteArray, n) + if ok = r.ByteArray(pubKey); !ok { + return io.EOF + } + var key any + if key, err = x509.ParsePKIXPublicKey(pubKey); err != nil { + return + } + if p.PublicKey, ok = key.(*rsa.PublicKey); !ok { + return fmt.Errorf("Unexpected public key type %T when parsing login encryption request", key) + } + if n, ok = r.VarInt(); !ok { + return io.EOF + } + p.VerifyToken = make(ByteArray, n) + if ok = r.ByteArray(p.VerifyToken); !ok { + return io.EOF + } + return +} + +type LoginEncryptionResponsePkt struct { + SharedSecret ByteArray // Shared Secret value, encrypted with the server's public key. + VerifyToken ByteArray // Verify Token value, encrypted with the same public key as the shared secret. +} + +var _ Packet = (*LoginEncryptionResponsePkt)(nil) + +func (p LoginEncryptionResponsePkt)Encode(b *PacketBuilder){ + b.VarInt((VarInt)(len(p.SharedSecret))) + b.ByteArray(p.SharedSecret) + b.VarInt((VarInt)(len(p.VerifyToken))) + b.ByteArray(p.VerifyToken) +} + +func (p *LoginEncryptionResponsePkt)DecodeFrom(r *PacketReader)(err error){ + var ok bool + var n VarInt + if n, ok = r.VarInt(); !ok { + return io.EOF + } + p.SharedSecret = make(ByteArray, n) + if ok = r.ByteArray(p.SharedSecret); !ok { + return io.EOF + } + if n, ok = r.VarInt(); !ok { + return io.EOF + } + p.VerifyToken = make(ByteArray, n) + if ok = r.ByteArray(p.VerifyToken); !ok { + return io.EOF + } + return +} + +type LoginSuccessPkt struct { + UUID UUID + Username string + Properties []*Property +} + +var _ Packet = (*LoginSuccessPkt)(nil) + +func (p *LoginSuccessPkt)Encode(b *PacketBuilder){ + b.UUID(p.UUID) + b.String(p.Username) + b.VarInt((VarInt)(len(p.Properties))) + for _, v := range p.Properties { + v.Encode(b) + } +} + +func (p *LoginSuccessPkt)DecodeFrom(r *PacketReader)(err error){ + var ok bool + if p.UUID, ok = r.UUID(); !ok { + return io.EOF + } + if p.Username, ok = r.String(); !ok { + return io.EOF + } + var n VarInt + if n, ok = r.VarInt(); !ok { + return io.EOF + } + p.Properties = make([]*Property, n) + for i, _ := range p.Properties { + v := new(Property) + if err = v.DecodeFrom(r); err != nil { + return + } + p.Properties[i] = v + } + return +} + +type LoginAcknowledgedPkt struct{} + +var _ Packet = (*LoginAcknowledgedPkt)(nil) + +func (p LoginAcknowledgedPkt)String()(string){ return "" } +func (p LoginAcknowledgedPkt)Encode(b *PacketBuilder){} +func (p *LoginAcknowledgedPkt)DecodeFrom(r *PacketReader)(err error){ return } diff --git a/profile.go b/profile.go index 2924aa9..7a78660 100644 --- a/profile.go +++ b/profile.go @@ -8,6 +8,7 @@ import ( "image" "image/color" "image/png" + "io" "net/http" "sync" ) @@ -15,6 +16,7 @@ import ( type Property struct { Name string `json:"name"` Value string `json:"value"` + Sign string `json:"signature,omitempty"` } func (p *Property)UnmarshalJSONValue(ptr any)(err error){ @@ -25,11 +27,43 @@ func (p *Property)UnmarshalJSONValue(ptr any)(err error){ return json.Unmarshal(buf, ptr) } +func (p *Property)Encode(b *PacketBuilder){ + b.String(p.Name) + b.String(p.Value) + hasSign := len(p.Sign) > 0 + b.Bool(hasSign) + if hasSign { + b.String(p.Sign) + } +} + +func (p *Property)DecodeFrom(r *PacketReader)(err error){ + var ok bool + if p.Name, ok = r.String(); !ok { + return io.EOF + } + if p.Value, ok = r.String(); !ok { + return io.EOF + } + var hasSign bool + if hasSign, ok = r.Bool(); !ok { + return io.EOF + } + if hasSign { + if p.Sign, ok = r.String(); !ok { + return io.EOF + } + }else{ + p.Sign = "" + } + return +} + type PlayerProfile struct { - Id UUID `json:"id"` + Id UUID `json:"id"` Name string `json:"name"` Properties []*Property `json:"properties` - ProfileActions []string `json:"profileActions"` + ProfileActions []string `json:"profileActions"` } func (p *PlayerProfile)GetProp(name string)(t *Property){