mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-16 17:05:03 +01:00
partially implement tokened sessions
This commit is contained in:
@@ -87,6 +87,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
logger.Info("Connected to database")
|
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.
|
// Clean the DB if the option is on.
|
||||||
if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB {
|
if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB {
|
||||||
logger.Info("Cleaning DB")
|
logger.Info("Cleaning DB")
|
||||||
@@ -137,26 +141,37 @@ func main() {
|
|||||||
logger.Info("Started sign server")
|
logger.Info("Started sign server")
|
||||||
|
|
||||||
var channels []channelserver.Server
|
var channels []channelserver.Server
|
||||||
|
channelQuery := ""
|
||||||
|
si := 0
|
||||||
|
ci := 0
|
||||||
count := 1
|
count := 1
|
||||||
for _, ee := range erupeConfig.Entrance.Entries {
|
for _, ee := range erupeConfig.Entrance.Entries {
|
||||||
for _, ce := range ee.Channels {
|
for _, ce := range ee.Channels {
|
||||||
|
sid := (4096 + si * 256) + (16 + ci)
|
||||||
c := *channelserver.NewServer(&channelserver.Config{
|
c := *channelserver.NewServer(&channelserver.Config{
|
||||||
Logger: logger.Named("channel-"+fmt.Sprint(count)),
|
ID: uint16(sid),
|
||||||
ErupeConfig: erupeConfig,
|
Logger: logger.Named("channel-"+fmt.Sprint(count)),
|
||||||
DB: db,
|
ErupeConfig: erupeConfig,
|
||||||
DiscordBot: discordBot,
|
DB: db,
|
||||||
|
DiscordBot: discordBot,
|
||||||
})
|
})
|
||||||
err = c.Start(int(ce.Port))
|
err = c.Start(int(ce.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("Failed to start channel", zap.Error(err))
|
logger.Fatal("Failed to start channel", zap.Error(err))
|
||||||
} else {
|
} 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))
|
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.
|
// Wait for exit or interrupt with ctrl+C.
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ func (m *MsgSysLogin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContex
|
|||||||
m.CharID1 = bf.ReadUint32()
|
m.CharID1 = bf.ReadUint32()
|
||||||
m.HardcodedZero1 = bf.ReadUint16()
|
m.HardcodedZero1 = bf.ReadUint16()
|
||||||
m.LoginTokenStringLength = 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -155,15 +155,19 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
s.Name = name
|
s.Name = name
|
||||||
s.charID = pkt.CharID0
|
s.charID = pkt.CharID0
|
||||||
s.rights = rights
|
s.rights = rights
|
||||||
|
s.token = pkt.LoginTokenString
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp
|
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_id=$2", len(s.server.sessions), s.server.ID)
|
||||||
_, 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 {
|
||||||
if err != nil {
|
panic(err)
|
||||||
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)
|
_, 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)
|
delete(s.server.sessions, s.rawConn)
|
||||||
s.rawConn.Close()
|
s.rawConn.Close()
|
||||||
|
|
||||||
if s.server.erupeConfig.DevModeOptions.ServerName != "" {
|
_, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL WHERE token=$1", s.token)
|
||||||
_, 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 {
|
||||||
if err != nil {
|
panic(err)
|
||||||
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()
|
s.server.Lock()
|
||||||
@@ -213,7 +220,7 @@ func logoutPlayer(s *Session) {
|
|||||||
removeSessionFromStage(s)
|
removeSessionFromStage(s)
|
||||||
|
|
||||||
var timePlayed int
|
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
|
timePlayed = (int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)) + timePlayed
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const (
|
|||||||
|
|
||||||
// Config struct allows configuring the server.
|
// Config struct allows configuring the server.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
ID uint16
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
DB *sqlx.DB
|
DB *sqlx.DB
|
||||||
DiscordBot *discordbot.DiscordBot
|
DiscordBot *discordbot.DiscordBot
|
||||||
@@ -50,6 +51,7 @@ type userBinaryPartID struct {
|
|||||||
// Server is a MHF channel server.
|
// Server is a MHF channel server.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
ID uint16
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
erupeConfig *config.Config
|
erupeConfig *config.Config
|
||||||
@@ -137,6 +139,7 @@ func NewRaviente() *Raviente {
|
|||||||
// NewServer creates a new Server type.
|
// NewServer creates a new Server type.
|
||||||
func NewServer(config *Config) *Server {
|
func NewServer(config *Config) *Server {
|
||||||
s := &Server {
|
s := &Server {
|
||||||
|
ID: config.ID,
|
||||||
logger: config.Logger,
|
logger: config.Logger,
|
||||||
db: config.DB,
|
db: config.DB,
|
||||||
erupeConfig: config.ErupeConfig,
|
erupeConfig: config.ErupeConfig,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type Session struct {
|
|||||||
logKey []byte
|
logKey []byte
|
||||||
sessionStart int64
|
sessionStart int64
|
||||||
rights uint32
|
rights uint32
|
||||||
|
token string
|
||||||
|
|
||||||
semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet.
|
semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet.
|
||||||
|
|
||||||
|
|||||||
@@ -11,17 +11,7 @@ import (
|
|||||||
"erupe-ce/server/channelserver"
|
"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
|
// Server Entries
|
||||||
var name string
|
|
||||||
var season uint8
|
var season uint8
|
||||||
|
|
||||||
// Server Channels
|
// Server Channels
|
||||||
@@ -31,17 +21,10 @@ func encodeServerInfo(serverInfos []config.EntranceServerInfo, s *Server) []byte
|
|||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
|
|
||||||
for serverIdx, si := range serverInfos {
|
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 {
|
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)
|
panic(err)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4()))
|
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4()))
|
||||||
bf.WriteUint16(16 + uint16(serverIdx))
|
bf.WriteUint16(16 + uint16(serverIdx))
|
||||||
@@ -50,25 +33,18 @@ func encodeServerInfo(serverInfos []config.EntranceServerInfo, s *Server) []byte
|
|||||||
bf.WriteUint8(si.Type)
|
bf.WriteUint8(si.Type)
|
||||||
bf.WriteUint8(season)
|
bf.WriteUint8(season)
|
||||||
bf.WriteUint8(si.Recommended)
|
bf.WriteUint8(si.Recommended)
|
||||||
sjisName, err := stringsupport.ConvertUTF8ToShiftJIS(si.Name)
|
combined := append([]byte{0x00}, stringsupport.UTF8ToSJIS(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(combined, []byte{0x00}...)
|
combined = append(combined, []byte{0x00}...)
|
||||||
combined = append(combined, sjisDesc...)
|
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
|
||||||
bf.WriteBytes(paddedString(string(combined), 66))
|
bf.WriteBytes(stringsupport.PaddedString(string(combined), 66, false))
|
||||||
bf.WriteUint32(si.AllowedClientFlags)
|
bf.WriteUint32(si.AllowedClientFlags)
|
||||||
|
|
||||||
for channelIdx, ci := range si.Channels {
|
for channelIdx, ci := range si.Channels {
|
||||||
|
sid = (4096 + serverIdx * 256) + (16 + channelIdx)
|
||||||
bf.WriteUint16(ci.Port)
|
bf.WriteUint16(ci.Port)
|
||||||
bf.WriteUint16(16 + uint16(channelIdx))
|
bf.WriteUint16(16 + uint16(channelIdx))
|
||||||
bf.WriteUint16(ci.MaxPlayers)
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,9 +87,43 @@ type character struct {
|
|||||||
|
|
||||||
func (s *Server) getCharactersForUser(uid int) ([]character, error) {
|
func (s *Server) getCharactersForUser(uid int) ([]character, error) {
|
||||||
characters := []character{}
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return characters, nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
|||||||
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
token := randSeq(16)
|
token := randSeq(16)
|
||||||
// TODO: register token to db, users table
|
s.server.registerToken(uid, token)
|
||||||
|
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
|||||||
bf.WriteUint8(1) // entrance server count
|
bf.WriteUint8(1) // entrance server count
|
||||||
bf.WriteUint8(uint8(len(chars))) // character count
|
bf.WriteUint8(uint8(len(chars))) // character count
|
||||||
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
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
|
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)
|
ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.HostIP, s.server.erupeConfig.Entrance.Port), false)
|
||||||
|
|
||||||
|
|||||||
@@ -60,11 +60,9 @@ func (s *Session) handlePacket(pkt []byte) error {
|
|||||||
}
|
}
|
||||||
case "DELETE:100":
|
case "DELETE:100":
|
||||||
loginTokenString := string(bf.ReadNullTerminatedBytes())
|
loginTokenString := string(bf.ReadNullTerminatedBytes())
|
||||||
_ = loginTokenString
|
characterID := int(bf.ReadUint32())
|
||||||
characterID := bf.ReadUint32()
|
s.server.deleteCharacter(characterID, loginTokenString)
|
||||||
|
sugar.Infof("Deleted character ID: %v\n", characterID)
|
||||||
sugar.Infof("Got delete request for character ID: %v\n", characterID)
|
|
||||||
sugar.Infof("remaining unknown data:\n%s\n", hex.Dump(bf.DataFromCurrent()))
|
|
||||||
default:
|
default:
|
||||||
sugar.Infof("Got unknown request type %s, data:\n%s\n", reqType, hex.Dump(bf.DataFromCurrent()))
|
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
|
newCharaReq = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Andoryuuta): remove plaintext password storage if this ever becomes more than a toy project.
|
|
||||||
var (
|
var (
|
||||||
id int
|
id int
|
||||||
password string
|
password string
|
||||||
@@ -138,6 +135,13 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
|
|||||||
break
|
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)
|
serverRespBytes = s.makeSignInResp(id)
|
||||||
} else {
|
} else {
|
||||||
s.logger.Info("Passwords don't match!")
|
s.logger.Info("Passwords don't match!")
|
||||||
|
|||||||
22
Erupe/tokensessions.sql
Normal file
22
Erupe/tokensessions.sql
Normal 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;
|
||||||
Reference in New Issue
Block a user