From 5f833f93a0f5510172b43f7567aa73117ccc7586 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 16 Jul 2022 15:46:38 +1000 Subject: [PATCH] partially implement tokened sessions --- Erupe/main.go | 27 ++++++++++--- Erupe/network/mhfpacket/msg_sys_login.go | 7 ++-- Erupe/server/channelserver/handlers.go | 29 +++++++++----- .../channelserver/sys_channel_server.go | 3 ++ Erupe/server/channelserver/sys_session.go | 1 + Erupe/server/entranceserver/make_resp.go | 40 ++++--------------- Erupe/server/signserver/dbutils.go | 38 +++++++++++++++++- Erupe/server/signserver/dsgn_resp.go | 4 +- Erupe/server/signserver/session.go | 16 +++++--- Erupe/tokensessions.sql | 22 ++++++++++ 10 files changed, 124 insertions(+), 63 deletions(-) create mode 100644 Erupe/tokensessions.sql diff --git a/Erupe/main.go b/Erupe/main.go index 738387a7d..4806b58cf 100644 --- a/Erupe/main.go +++ b/Erupe/main.go @@ -87,6 +87,10 @@ func main() { } logger.Info("Connected to database") + // Clear existing tokens + _ = db.MustExec("DELETE FROM sign_sessions") + _ = db.MustExec("DELETE FROM servers") + // Clean the DB if the option is on. if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB { logger.Info("Cleaning DB") @@ -137,26 +141,37 @@ func main() { logger.Info("Started sign server") var channels []channelserver.Server + channelQuery := "" + si := 0 + ci := 0 count := 1 for _, ee := range erupeConfig.Entrance.Entries { for _, ce := range ee.Channels { + sid := (4096 + si * 256) + (16 + ci) c := *channelserver.NewServer(&channelserver.Config{ - Logger: logger.Named("channel-"+fmt.Sprint(count)), - ErupeConfig: erupeConfig, - DB: db, - DiscordBot: discordBot, + ID: uint16(sid), + Logger: logger.Named("channel-"+fmt.Sprint(count)), + ErupeConfig: erupeConfig, + DB: db, + DiscordBot: discordBot, }) err = c.Start(int(ce.Port)) if err != nil { logger.Fatal("Failed to start channel", zap.Error(err)) } else { + channelQuery += fmt.Sprintf("INSERT INTO servers (server_id, season, current_players) VALUES (%d, 0, 0);", sid) + channels = append(channels, c) logger.Info(fmt.Sprintf("Started channel server %d on port %d", count, ce.Port)) + ci++ + count++ } - channels = append(channels, c) - count++ } + ci = 0 + si++ } + _ = db.MustExec(channelQuery) + // Wait for exit or interrupt with ctrl+C. c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) diff --git a/Erupe/network/mhfpacket/msg_sys_login.go b/Erupe/network/mhfpacket/msg_sys_login.go index db9370afc..fc881b991 100644 --- a/Erupe/network/mhfpacket/msg_sys_login.go +++ b/Erupe/network/mhfpacket/msg_sys_login.go @@ -1,7 +1,7 @@ package mhfpacket -import ( - "errors" +import ( + "errors" "erupe-ce/network/clientctx" "erupe-ce/network" @@ -36,8 +36,7 @@ func (m *MsgSysLogin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContex m.CharID1 = bf.ReadUint32() m.HardcodedZero1 = bf.ReadUint16() m.LoginTokenStringLength = bf.ReadUint16() - m.LoginTokenString = string(bf.ReadBytes(17)) // TODO(Andoryuuta): What encoding is this string? - + m.LoginTokenString = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/Erupe/server/channelserver/handlers.go b/Erupe/server/channelserver/handlers.go index 98d8aeb31..c558c75e6 100644 --- a/Erupe/server/channelserver/handlers.go +++ b/Erupe/server/channelserver/handlers.go @@ -155,15 +155,19 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { s.Name = name s.charID = pkt.CharID0 s.rights = rights + s.token = pkt.LoginTokenString s.Unlock() bf := byteframe.NewByteFrame() bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp - if s.server.erupeConfig.DevModeOptions.ServerName != "" { - _, err := s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_name=$2", uint32(len(s.server.sessions)), s.server.erupeConfig.DevModeOptions.ServerName) - if err != nil { - panic(err) - } + _, err = s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID) + if err != nil { + panic(err) + } + + _, err = s.server.db.Exec("UPDATE sign_sessions SET server_id=$1 WHERE token=$2", s.server.ID, s.token) + if err != nil { + panic(err) } _, err = s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", Time_Current().Unix(), s.charID) @@ -194,11 +198,14 @@ func logoutPlayer(s *Session) { delete(s.server.sessions, s.rawConn) s.rawConn.Close() - if s.server.erupeConfig.DevModeOptions.ServerName != "" { - _, err := s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_name=$2", uint32(len(s.server.sessions)), s.server.erupeConfig.DevModeOptions.ServerName) - if err != nil { - panic(err) - } + _, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL WHERE token=$1", s.token) + if err != nil { + panic(err) + } + + _, err = s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID) + if err != nil { + panic(err) } s.server.Lock() @@ -213,7 +220,7 @@ func logoutPlayer(s *Session) { removeSessionFromStage(s) var timePlayed int - err := s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed) + _ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed) timePlayed = (int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)) + timePlayed diff --git a/Erupe/server/channelserver/sys_channel_server.go b/Erupe/server/channelserver/sys_channel_server.go index a768456b2..ec0a6e54f 100644 --- a/Erupe/server/channelserver/sys_channel_server.go +++ b/Erupe/server/channelserver/sys_channel_server.go @@ -33,6 +33,7 @@ const ( // Config struct allows configuring the server. type Config struct { + ID uint16 Logger *zap.Logger DB *sqlx.DB DiscordBot *discordbot.DiscordBot @@ -50,6 +51,7 @@ type userBinaryPartID struct { // Server is a MHF channel server. type Server struct { sync.Mutex + ID uint16 logger *zap.Logger db *sqlx.DB erupeConfig *config.Config @@ -137,6 +139,7 @@ func NewRaviente() *Raviente { // NewServer creates a new Server type. func NewServer(config *Config) *Server { s := &Server { + ID: config.ID, logger: config.Logger, db: config.DB, erupeConfig: config.ErupeConfig, diff --git a/Erupe/server/channelserver/sys_session.go b/Erupe/server/channelserver/sys_session.go index 447a55753..282930856 100644 --- a/Erupe/server/channelserver/sys_session.go +++ b/Erupe/server/channelserver/sys_session.go @@ -34,6 +34,7 @@ type Session struct { logKey []byte sessionStart int64 rights uint32 + token string semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet. diff --git a/Erupe/server/entranceserver/make_resp.go b/Erupe/server/entranceserver/make_resp.go index c39b99bc4..55517deca 100644 --- a/Erupe/server/entranceserver/make_resp.go +++ b/Erupe/server/entranceserver/make_resp.go @@ -11,17 +11,7 @@ import ( "erupe-ce/server/channelserver" ) -func paddedString(x string, size uint) []byte { - out := make([]byte, size) - copy(out, x) - - // Null terminate it. - out[len(out)-1] = 0 - return out -} - // Server Entries -var name string var season uint8 // Server Channels @@ -31,17 +21,10 @@ func encodeServerInfo(serverInfos []config.EntranceServerInfo, s *Server) []byte bf := byteframe.NewByteFrame() for serverIdx, si := range serverInfos { - err := s.db.QueryRow("SELECT server_name FROM servers WHERE server_name=$1", si.Name).Scan(&name) + sid := (4096 + serverIdx * 256) + 16 + err := s.db.QueryRow("SELECT season FROM servers WHERE server_id=$1", sid).Scan(&season) if err != nil { - _, err := s.db.Exec("INSERT INTO servers (server_name, season, current_players, event_id, event_expiration) VALUES ($1, $2, 0, 0, 0)", si.Name, si.Season) - if err != nil { - panic(err) - } - } else { - err := s.db.QueryRow("SELECT season FROM servers WHERE server_name=$1", si.Name).Scan(&season) - if err != nil { - panic(err) - } + panic(err) } bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4())) bf.WriteUint16(16 + uint16(serverIdx)) @@ -50,25 +33,18 @@ func encodeServerInfo(serverInfos []config.EntranceServerInfo, s *Server) []byte bf.WriteUint8(si.Type) bf.WriteUint8(season) bf.WriteUint8(si.Recommended) - sjisName, err := stringsupport.ConvertUTF8ToShiftJIS(si.Name) - if err != nil { - panic(err) - } - sjisDesc, err := stringsupport.ConvertUTF8ToShiftJIS(si.Description) - if err != nil { - panic(err) - } - combined := append([]byte{0x00}, sjisName...) + combined := append([]byte{0x00}, stringsupport.UTF8ToSJIS(si.Name)...) combined = append(combined, []byte{0x00}...) - combined = append(combined, sjisDesc...) - bf.WriteBytes(paddedString(string(combined), 66)) + combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...) + bf.WriteBytes(stringsupport.PaddedString(string(combined), 66, false)) bf.WriteUint32(si.AllowedClientFlags) for channelIdx, ci := range si.Channels { + sid = (4096 + serverIdx * 256) + (16 + channelIdx) bf.WriteUint16(ci.Port) bf.WriteUint16(16 + uint16(channelIdx)) bf.WriteUint16(ci.MaxPlayers) - err := s.db.QueryRow("SELECT current_players FROM servers WHERE server_name=$1", si.Name).Scan(¤tplayers) + err := s.db.QueryRow("SELECT current_players FROM servers WHERE server_id=$1", sid).Scan(¤tplayers) if err != nil { panic(err) } diff --git a/Erupe/server/signserver/dbutils.go b/Erupe/server/signserver/dbutils.go index 05791258b..79d85ebd5 100644 --- a/Erupe/server/signserver/dbutils.go +++ b/Erupe/server/signserver/dbutils.go @@ -87,9 +87,43 @@ type character struct { func (s *Server) getCharactersForUser(uid int) ([]character, error) { characters := []character{} - err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1", uid) + err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false", uid) if err != nil { return nil, err } return characters, nil -} \ No newline at end of file +} + +func (s *Server) deleteCharacter(cid int, token string) error { + var verify int + err := s.db.QueryRow("SELECT count(*) FROM sign_sessions WHERE token = $1", token).Scan(&verify) + if err != nil { + return err // Invalid token + } + _, err = s.db.Exec("UPDATE characters SET deleted = true WHERE id = $1", cid) + if err != nil { + return err + } + return nil +} + +// Unused +func (s *Server) checkToken(uid int) (bool, error) { + var exists int + err := s.db.QueryRow("SELECT count(*) FROM sign_sessions WHERE user_id = $1", uid).Scan(&exists) + if err != nil { + return false, err + } + if exists > 0 { + return true, nil + } + return false, nil +} + +func (s *Server) registerToken(uid int, token string) error { + _, err := s.db.Exec("INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2)", uid, token) + if err != nil { + return err + } + return nil +} diff --git a/Erupe/server/signserver/dsgn_resp.go b/Erupe/server/signserver/dsgn_resp.go index def8f24ab..4a817702d 100644 --- a/Erupe/server/signserver/dsgn_resp.go +++ b/Erupe/server/signserver/dsgn_resp.go @@ -36,7 +36,7 @@ func (s *Session) makeSignInResp(uid int) []byte { rand.Seed(time.Now().UnixNano()) token := randSeq(16) - // TODO: register token to db, users table + s.server.registerToken(uid, token) bf := byteframe.NewByteFrame() @@ -45,7 +45,7 @@ func (s *Session) makeSignInResp(uid int) []byte { bf.WriteUint8(1) // entrance server count bf.WriteUint8(uint8(len(chars))) // character count bf.WriteUint32(0xFFFFFFFF) // login_token_number - bf.WriteBytes(stringsupport.PaddedString(token, 16, false)) // login_token (16 byte padded string) + bf.WriteBytes([]byte(token)) // login_token bf.WriteUint32(uint32(time.Now().Unix())) // unk timestamp ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.HostIP, s.server.erupeConfig.Entrance.Port), false) diff --git a/Erupe/server/signserver/session.go b/Erupe/server/signserver/session.go index 59fbf9c24..cb4a3619c 100644 --- a/Erupe/server/signserver/session.go +++ b/Erupe/server/signserver/session.go @@ -60,11 +60,9 @@ func (s *Session) handlePacket(pkt []byte) error { } case "DELETE:100": loginTokenString := string(bf.ReadNullTerminatedBytes()) - _ = loginTokenString - characterID := bf.ReadUint32() - - sugar.Infof("Got delete request for character ID: %v\n", characterID) - sugar.Infof("remaining unknown data:\n%s\n", hex.Dump(bf.DataFromCurrent())) + characterID := int(bf.ReadUint32()) + s.server.deleteCharacter(characterID, loginTokenString) + sugar.Infof("Deleted character ID: %v\n", characterID) default: sugar.Infof("Got unknown request type %s, data:\n%s\n", reqType, hex.Dump(bf.DataFromCurrent())) } @@ -92,7 +90,6 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error { newCharaReq = true } - // TODO(Andoryuuta): remove plaintext password storage if this ever becomes more than a toy project. var ( id int password string @@ -138,6 +135,13 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error { break } } + // TODO: Need to auto delete user tokens after inactivity + // exists, err := s.server.checkToken(id) + // if err != nil { + // s.logger.Info("Error checking for live tokens", zap.Error(err)) + // serverRespBytes = makeSignInFailureResp(SIGN_EABORT) + // break + // } serverRespBytes = s.makeSignInResp(id) } else { s.logger.Info("Passwords don't match!") diff --git a/Erupe/tokensessions.sql b/Erupe/tokensessions.sql new file mode 100644 index 000000000..82a6befac --- /dev/null +++ b/Erupe/tokensessions.sql @@ -0,0 +1,22 @@ +BEGIN; + +DROP TABLE IF EXISTS public.sign_sessions; +CREATE TABLE IF NOT EXISTS public.sign_sessions +( + user_id int NOT NULL, + token varchar(16) NOT NULL, + server_id integer +); + +DROP TABLE IF EXISTS public.servers; +CREATE TABLE IF NOT EXISTS public.servers +( + server_id int NOT NULL, + season int NOT NULL, + current_players int NOT NULL +); + +ALTER TABLE IF EXISTS public.characters + ADD COLUMN deleted boolean NOT NULL DEFAULT false; + +END;