From d60638d55518a68c9f3607b4bf742dfd18a812f0 Mon Sep 17 00:00:00 2001 From: CPunch Date: Fri, 10 Mar 2023 20:30:16 -0600 Subject: [PATCH] loginServer/peer: peer no longer uses panic/recover for error recovery --- server/login.go | 62 ++++++++++++++++++++--------------- server/loginserver.go | 76 ++++++++++++++++++++++--------------------- server/peer.go | 34 +++++++++++-------- 3 files changed, 94 insertions(+), 78 deletions(-) diff --git a/server/login.go b/server/login.go index 16d44a3..7ace2f3 100644 --- a/server/login.go +++ b/server/login.go @@ -23,7 +23,7 @@ const ( LOGIN_UPDATED_EUALA_REQUIRED = 9 ) -func (server *LoginServer) AcceptLogin(peer *Peer, SzID string, IClientVerC int32, ISlotNum int8, data []protocol.SP_LS2CL_REP_CHAR_INFO) { +func (server *LoginServer) AcceptLogin(peer *Peer, SzID string, IClientVerC int32, ISlotNum int8, data []protocol.SP_LS2CL_REP_CHAR_INFO) error { peer.SzID = SzID resp := &protocol.SP_LS2CL_REP_LOGIN_SUCC{ @@ -35,8 +35,11 @@ func (server *LoginServer) AcceptLogin(peer *Peer, SzID string, IClientVerC int3 UiSvrTime: uint64(time.Now().Unix()), } + if err := peer.Send(resp, protocol.P_LS2CL_REP_LOGIN_SUCC); err != nil { + return err + } + // swap keys - peer.Send(resp, protocol.P_LS2CL_REP_LOGIN_SUCC) peer.E_key = protocol.CreateNewKey( resp.UiSvrTime, uint64(resp.ICharCount+1), @@ -50,11 +53,15 @@ func (server *LoginServer) AcceptLogin(peer *Peer, SzID string, IClientVerC int3 // send characters (if any) for i := 0; i < len(data); i++ { - peer.Send(&data[i], protocol.P_LS2CL_REP_CHAR_INFO) + if err := peer.Send(&data[i], protocol.P_LS2CL_REP_CHAR_INFO); err != nil { + return err + } } + + return nil } -func (server *LoginServer) Login(peer *Peer, pkt protocol.Packet) { +func (server *LoginServer) Login(peer *Peer, pkt protocol.Packet) error { var loginPkt protocol.SP_CL2LS_REQ_LOGIN pkt.Decode(&loginPkt) @@ -68,7 +75,7 @@ func (server *LoginServer) Login(peer *Peer, pkt protocol.Packet) { // client is resending a login packet?? if peer.AccountID != -1 { SendError(LOGIN_ERROR) - panic(fmt.Errorf("Out of order P_CL2LS_REQ_LOGIN!")) + return fmt.Errorf("Out of order P_CL2LS_REQ_LOGIN!") } // attempt login @@ -79,15 +86,15 @@ func (server *LoginServer) Login(peer *Peer, pkt protocol.Packet) { if err != nil { // respond with a dummy login error packet so the client doesn't get softlocked SendError(LOGIN_DATABASE_ERROR) - panic(err) + return err } } else if err == db.LoginErrorInvalidPassword { // respond with invalid password SendError(LOGIN_ID_AND_PASSWORD_DO_NOT_MATCH) - return + return nil } else if err != nil { // wtf? SendError(LOGIN_DATABASE_ERROR) - panic(err) + return err } // grab player data @@ -95,7 +102,7 @@ func (server *LoginServer) Login(peer *Peer, pkt protocol.Packet) { plrs, err := db.DefaultDB.GetPlayers(account.AccountID) if err != nil { SendError(LOGIN_DATABASE_ERROR) - panic(err) + return err } // build character list @@ -115,44 +122,44 @@ func (server *LoginServer) Login(peer *Peer, pkt protocol.Packet) { AEquip, err := db.DefaultDB.GetPlayerInventorySlots(plr.PlayerID, 0, config.AEQUIP_COUNT-1) if err != nil { SendError(LOGIN_DATABASE_ERROR) - panic(err) + return err } copy(info.AEquip[:], AEquip) charInfo = append(charInfo, info) } - server.AcceptLogin(peer, loginPkt.SzID, loginPkt.IClientVerC, 1, charInfo) + return server.AcceptLogin(peer, loginPkt.SzID, loginPkt.IClientVerC, 1, charInfo) } -func (server *LoginServer) CheckCharacterName(peer *Peer, pkt protocol.Packet) { +func (server *LoginServer) CheckCharacterName(peer *Peer, pkt protocol.Packet) error { var charPkt protocol.SP_CL2LS_REQ_CHECK_CHAR_NAME pkt.Decode(&charPkt) // just auto accept, client resends this data later - peer.Send(&protocol.SP_LS2CL_REP_CHECK_CHAR_NAME_SUCC{ + return peer.Send(&protocol.SP_LS2CL_REP_CHECK_CHAR_NAME_SUCC{ SzFirstName: charPkt.SzFirstName, SzLastName: charPkt.SzLastName, }, protocol.P_LS2CL_REP_CHECK_CHAR_NAME_SUCC) } -func (server *LoginServer) SaveCharacterName(peer *Peer, pkt protocol.Packet) { +func (server *LoginServer) SaveCharacterName(peer *Peer, pkt protocol.Packet) error { var charPkt protocol.SP_CL2LS_REQ_SAVE_CHAR_NAME pkt.Decode(&charPkt) if peer.AccountID == -1 { peer.Send(&protocol.SP_LS2CL_REP_SAVE_CHAR_NAME_FAIL{}, protocol.P_LS2CL_REP_SAVE_CHAR_NAME_FAIL) - panic(fmt.Errorf("Out of order P_LS2CL_REP_SAVE_CHAR_NAME_FAIL!")) + return fmt.Errorf("Out of order P_LS2CL_REP_SAVE_CHAR_NAME_FAIL!") } // TODO: sanity check SzFirstName && SzLastName PlayerID, err := db.DefaultDB.NewPlayer(peer.AccountID, charPkt.SzFirstName, charPkt.SzLastName, int(charPkt.ISlotNum)) if err != nil { peer.Send(&protocol.SP_LS2CL_REP_SAVE_CHAR_NAME_FAIL{}, protocol.P_LS2CL_REP_SAVE_CHAR_NAME_FAIL) - panic(err) + return err } - peer.Send(&protocol.SP_LS2CL_REP_SAVE_CHAR_NAME_SUCC{ + return peer.Send(&protocol.SP_LS2CL_REP_SAVE_CHAR_NAME_SUCC{ IPC_UID: int64(PlayerID), ISlotNum: charPkt.ISlotNum, IGender: charPkt.IGender, @@ -187,28 +194,28 @@ func validateCharacterCreation(character *protocol.SP_CL2LS_REQ_CHAR_CREATE) boo return true } -func (server *LoginServer) CharacterCreate(peer *Peer, pkt protocol.Packet) { +func (server *LoginServer) CharacterCreate(peer *Peer, pkt protocol.Packet) error { var charPkt protocol.SP_CL2LS_REQ_CHAR_CREATE pkt.Decode(&charPkt) if !validateCharacterCreation(&charPkt) { peer.Send(&protocol.SP_LS2CL_REP_SHARD_SELECT_FAIL{IErrorCode: 2}, protocol.P_LS2CL_REP_SHARD_SELECT_FAIL) - panic(fmt.Errorf("invalid SP_CL2LS_REQ_CHAR_CREATE!")) + return fmt.Errorf("invalid SP_CL2LS_REQ_CHAR_CREATE!") } if err := db.DefaultDB.FinishPlayer(&charPkt, peer.AccountID); err != nil { peer.Send(&protocol.SP_LS2CL_REP_SHARD_SELECT_FAIL{IErrorCode: 2}, protocol.P_LS2CL_REP_SHARD_SELECT_FAIL) - panic(err) + return err } plr, err := db.DefaultDB.GetPlayer(int(charPkt.PCStyle.IPC_UID)) if err != nil { peer.Send(&protocol.SP_LS2CL_REP_SHARD_SELECT_FAIL{IErrorCode: 2}, protocol.P_LS2CL_REP_SHARD_SELECT_FAIL) - panic(err) + return err } PCStyle, PCStyle2 := util.Player2PCStyle(plr) - peer.Send(&protocol.SP_LS2CL_REP_CHAR_CREATE_SUCC{ + return peer.Send(&protocol.SP_LS2CL_REP_CHAR_CREATE_SUCC{ ILevel: int16(plr.Level), SPC_Style: PCStyle, SPC_Style2: PCStyle2, @@ -216,29 +223,30 @@ func (server *LoginServer) CharacterCreate(peer *Peer, pkt protocol.Packet) { }, protocol.P_LS2CL_REP_CHAR_CREATE_SUCC) } -func (server *LoginServer) CharacterDelete(peer *Peer, pkt protocol.Packet) { +func (server *LoginServer) CharacterDelete(peer *Peer, pkt protocol.Packet) error { var charPkt protocol.SP_CL2LS_REQ_CHAR_DELETE pkt.Decode(&charPkt) slot, err := db.DefaultDB.DeletePlayer(int(charPkt.IPC_UID), peer.AccountID) if err != nil { peer.Send(&protocol.SP_LS2CL_REP_SHARD_SELECT_FAIL{IErrorCode: 2}, protocol.P_LS2CL_REP_SHARD_SELECT_FAIL) - panic(err) + return err } - peer.Send(&protocol.SP_LS2CL_REP_CHAR_DELETE_SUCC{ + return peer.Send(&protocol.SP_LS2CL_REP_CHAR_DELETE_SUCC{ ISlotNum: int8(slot), }, protocol.P_LS2CL_REP_CHAR_DELETE_SUCC) } -func (server *LoginServer) FinishTutorial(peer *Peer, pkt protocol.Packet) { +func (server *LoginServer) FinishTutorial(peer *Peer, pkt protocol.Packet) error { var charPkt protocol.SP_CL2LS_REQ_SAVE_CHAR_TUTOR pkt.Decode(&charPkt) if err := db.DefaultDB.FinishTutorial(int(charPkt.IPC_UID), peer.AccountID); err != nil { peer.Send(&protocol.SP_LS2CL_REP_SHARD_SELECT_FAIL{IErrorCode: 2}, protocol.P_LS2CL_REP_SHARD_SELECT_FAIL) - panic(err) + return err } // no response + return nil } diff --git a/server/loginserver.go b/server/loginserver.go index 9ff41c7..f72ba2c 100644 --- a/server/loginserver.go +++ b/server/loginserver.go @@ -8,10 +8,13 @@ import ( "github.com/CPunch/gopenfusion/protocol" ) +type PacketHandler func(peer *Peer, pkt protocol.Packet) error + type LoginServer struct { - listener net.Listener - peers map[*Peer]bool - peersLock sync.Mutex + listener net.Listener + packetHandlers map[uint32]PacketHandler + peers map[*Peer]bool + peersLock sync.Mutex } func NewLoginServer() *LoginServer { @@ -20,10 +23,32 @@ func NewLoginServer() *LoginServer { log.Fatal(err) } - return &LoginServer{ - listener: listener, - peers: make(map[*Peer]bool), + loginServer := &LoginServer{ + listener: listener, + packetHandlers: make(map[uint32]PacketHandler), + peers: make(map[*Peer]bool), } + + loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_LOGIN, loginServer.Login) + loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_CHECK_CHAR_NAME, loginServer.CheckCharacterName) + loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_SAVE_CHAR_NAME, loginServer.SaveCharacterName) + loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_CHAR_CREATE, loginServer.CharacterCreate) + // loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_CHAR_SELECT, func(_ *Peer, _ protocol.Packet) error { /* stubbed */ return nil }) + loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_CHAR_DELETE, loginServer.CharacterDelete) + // loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_SHARD_SELECT, func(_ *Peer, _ protocol.Packet) error { /* stubbed */ return nil }) + // loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_SHARD_LIST_INFO, func(_ *Peer, _ protocol.Packet) error { /* stubbed */ return nil }) + // loginServer.RegisterPacketHandler(protocol.P_CL2LS_CHECK_NAME_LIST, func(_ *Peer, _ protocol.Packet) error { /* stubbed */ return nil }) + loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_SAVE_CHAR_TUTOR, loginServer.FinishTutorial) + // loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_PC_EXIT_DUPLICATE, func(_ *Peer, _ protocol.Packet) error { /* stubbed */ return nil }) + // loginServer.RegisterPacketHandler(protocol.P_CL2LS_REP_LIVE_CHECK, func(_ *Peer, _ protocol.Packet) error { /* stubbed */ return nil }) + // loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_CHANGE_CHAR_NAME, func(_ *Peer, _ protocol.Packet) error { /* stubbed */ return nil }) + // loginServer.RegisterPacketHandler(protocol.P_CL2LS_REQ_SERVER_SELECT, func(_ *Peer, _ protocol.Packet) error { /* stubbed */ return nil }) + + return loginServer +} + +func (server *LoginServer) RegisterPacketHandler(typeID uint32, hndlr PacketHandler) { + server.packetHandlers[typeID] = hndlr } func (server *LoginServer) Start() { @@ -42,39 +67,16 @@ func (server *LoginServer) Start() { } } -func (server *LoginServer) HandlePacket(peer *Peer, typeID uint32, pkt protocol.Packet) { - switch typeID { - case protocol.P_CL2LS_REQ_LOGIN: - server.Login(peer, pkt) - case protocol.P_CL2LS_REQ_CHECK_CHAR_NAME: - server.CheckCharacterName(peer, pkt) - case protocol.P_CL2LS_REQ_SAVE_CHAR_NAME: - server.SaveCharacterName(peer, pkt) - case protocol.P_CL2LS_REQ_CHAR_CREATE: - server.CharacterCreate(peer, pkt) - case protocol.P_CL2LS_REQ_CHAR_SELECT: - /* stubbed */ - case protocol.P_CL2LS_REQ_CHAR_DELETE: - server.CharacterDelete(peer, pkt) - case protocol.P_CL2LS_REQ_SHARD_SELECT: - /* stubbed */ - case protocol.P_CL2LS_REQ_SHARD_LIST_INFO: - /* stubbed */ - case protocol.P_CL2LS_CHECK_NAME_LIST: - /* stubbed */ - case protocol.P_CL2LS_REQ_SAVE_CHAR_TUTOR: - server.FinishTutorial(peer, pkt) - case protocol.P_CL2LS_REQ_PC_EXIT_DUPLICATE: - /* stubbed */ - case protocol.P_CL2LS_REP_LIVE_CHECK: - /* stubbed */ - case protocol.P_CL2LS_REQ_CHANGE_CHAR_NAME: - /* stubbed */ - case protocol.P_CL2LS_REQ_SERVER_SELECT: - /* stubbed */ - default: +func (server *LoginServer) HandlePacket(peer *Peer, typeID uint32, pkt protocol.Packet) error { + if hndlr, ok := server.packetHandlers[typeID]; ok { + if err := hndlr(peer, pkt); err != nil { + return err + } + } else { log.Printf("[WARN] unsupported packet ID: %x\n", typeID) } + + return nil } func (server *LoginServer) Disconnect(peer *Peer) { diff --git a/server/peer.go b/server/peer.go index b122257..248c994 100644 --- a/server/peer.go +++ b/server/peer.go @@ -18,7 +18,7 @@ const ( ) type PeerHandler interface { - HandlePacket(client *Peer, typeID uint32, pkt protocol.Packet) + HandlePacket(client *Peer, typeID uint32, pkt protocol.Packet) error Connect(client *Peer) Disconnect(client *Peer) } @@ -49,7 +49,7 @@ func NewPeer(handler PeerHandler, conn net.Conn) *Peer { } } -func (client *Peer) Send(data interface{}, typeID uint32) { +func (client *Peer) Send(data interface{}, typeID uint32) error { buf := pool.Get() defer func() { // always return the buffer to the pool pool.Put(buf) @@ -76,8 +76,10 @@ func (client *Peer) Send(data interface{}, typeID uint32) { // write packet type && packet body log.Printf("Sending %#v, sizeof: %d", data, buf.Len()) if _, err := client.conn.Write(buf.Bytes()); err != nil { - panic(fmt.Errorf("[FATAL] failed to write packet body! %v", err)) + return fmt.Errorf("[FATAL] failed to write packet body! %v", err) } + + return nil } func (client *Peer) Kill() { @@ -91,29 +93,27 @@ func (client *Peer) Kill() { } func (client *Peer) ClientHandler() { - defer func() { - if err := recover(); err != nil { - log.Printf("Client %p panic'd! %v", client, err) - } - client.Kill() - }() + defer client.Kill() for { // read packet size var sz uint32 if err := binary.Read(client.conn, binary.LittleEndian, &sz); err != nil { - panic(fmt.Errorf("[FATAL] failed to read packet size! %v", err)) + log.Printf("[FATAL] failed to read packet size! %v\n", err) + return } // client should never send a packet size outside of this range if sz > protocol.CN_PACKET_BUFFER_SIZE || sz < 4 { - panic(fmt.Errorf("[FATAL] malicious packet size received! %d", sz)) + log.Printf("[FATAL] malicious packet size received! %d", sz) + return } // read packet body buf := pool.Get() if _, err := buf.ReadFrom(io.LimitReader(client.conn, int64(sz))); err != nil { - panic(fmt.Errorf("[FATAL] failed to read packet body! %v", err)) + log.Printf("[FATAL] failed to read packet body! %v", err) + return } // decrypt @@ -122,11 +122,17 @@ func (client *Peer) ClientHandler() { // create packet && read typeID var typeID uint32 pkt := protocol.NewPacket(buf) - pkt.Decode(&typeID) + if err := pkt.Decode(&typeID); err != nil { + log.Printf("[FATAL] failed to read packet type! %v", err) + return + } // dispatch packet log.Printf("Got packet ID: %x, with a sizeof: %d\n", typeID, sz) - client.handler.HandlePacket(client, typeID, pkt) + if err := client.handler.HandlePacket(client, typeID, pkt); err != nil { + log.Printf("[FATAL] %v", err) + return + } // restore buffer to pool pool.Put(buf)