partially implement tokened sessions

This commit is contained in:
wish
2022-07-16 15:46:38 +10:00
parent f28c9fa636
commit 5f833f93a0
10 changed files with 124 additions and 63 deletions

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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

View File

@@ -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,

View File

@@ -34,6 +34,7 @@ type Session struct {
logKey []byte
sessionStart int64
rights uint32
token string
semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet.

View File

@@ -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(&currentplayers)
err := s.db.QueryRow("SELECT current_players FROM servers WHERE server_id=$1", sid).Scan(&currentplayers)
if err != nil {
panic(err)
}

View File

@@ -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
}
}
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
}

View File

@@ -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)

View File

@@ -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!")

22
Erupe/tokensessions.sql Normal file
View File

@@ -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;