Refactor servers

This commit is contained in:
Andrew Gutekanst
2020-01-13 17:19:29 -05:00
parent 0922ff4f9c
commit e5257eb6ed
16 changed files with 749 additions and 221 deletions

View File

@@ -27,7 +27,7 @@ func makeSignInFailureResp(respID RespID) []byte {
return bf.Data()
}
func makeSignInResp(username string) []byte {
func (session *Session) makeSignInResp(username string) []byte {
bf := byteframe.NewByteFrame()
// delete me:

View File

@@ -1,6 +1,7 @@
package signserver
//revive:disable
type RespID uint16
//go:generate stringer -type=RespID

View File

@@ -3,104 +3,115 @@ package signserver
import (
"database/sql"
"encoding/hex"
"fmt"
"net"
"sync"
"github.com/Andoryuuta/Erupe/network"
"github.com/Andoryuuta/byteframe"
"go.uber.org/zap"
)
// Session holds state for the sign server connection.
type Session struct {
sync.Mutex
logger *zap.Logger
sid int
server *Server
rawConn *net.Conn
cryptConn *network.CryptConn
}
func (session *Session) fail() {
session.server.Lock()
delete(session.server.sessions, session.sid)
session.server.Unlock()
func (s *Session) fail() {
s.server.Lock()
delete(s.server.sessions, s.sid)
s.server.Unlock()
}
func (session *Session) work() {
func (s *Session) work() {
for {
pkt, err := session.cryptConn.ReadPacket()
pkt, err := s.cryptConn.ReadPacket()
if err != nil {
session.fail()
s.fail()
return
}
err = session.handlePacket(pkt)
err = s.handlePacket(pkt)
if err != nil {
session.fail()
s.fail()
return
}
}
}
func (session *Session) handlePacket(pkt []byte) error {
func (s *Session) handlePacket(pkt []byte) error {
sugar := s.logger.Sugar()
bf := byteframe.NewByteFrameFromBytes(pkt)
reqType := string(bf.ReadNullTerminatedBytes())
switch reqType {
case "DLTSKEYSIGN:100":
fallthrough
case "DSGN:100":
session.handleDSGNRequest(bf)
break
err := s.handleDSGNRequest(bf)
if err != nil {
return nil
}
case "DELETE:100":
loginTokenString := string(bf.ReadNullTerminatedBytes())
_ = loginTokenString
characterID := bf.ReadUint32()
fmt.Printf("Got delete request for character ID: %v\n", characterID)
fmt.Printf("remaining unknown data:\n%s\n", hex.Dump(bf.DataFromCurrent()))
sugar.Infof("Got delete request for character ID: %v\n", characterID)
sugar.Infof("remaining unknown data:\n%s\n", hex.Dump(bf.DataFromCurrent()))
default:
fmt.Printf("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()))
}
return nil
}
func (session *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
reqUsername := string(bf.ReadNullTerminatedBytes())
reqPassword := string(bf.ReadNullTerminatedBytes())
reqUnk := string(bf.ReadNullTerminatedBytes())
fmt.Printf("Got sign in request:\n\tUsername: %s\n\tPassword %s\n\tUnk: %s\n", reqUsername, reqPassword, reqUnk)
s.server.logger.Info(
"Got sign in request",
zap.String("reqUsername", reqUsername),
zap.String("reqPassword", reqPassword),
zap.String("reqUnk", reqUnk),
)
// TODO(Andoryuuta): remove plaintext password storage if this ever becomes more than a toy project.
var (
id int
password string
)
err := session.server.db.QueryRow("SELECT id, password FROM users WHERE username = $1", reqUsername).Scan(&id, &password)
err := s.server.db.QueryRow("SELECT id, password FROM users WHERE username = $1", reqUsername).Scan(&id, &password)
var serverRespBytes []byte
switch {
case err == sql.ErrNoRows:
fmt.Printf("No rows for username %s\n", reqUsername)
s.logger.Info("Account not found", zap.String("reqUsername", reqUsername))
serverRespBytes = makeSignInFailureResp(SIGN_EAUTH)
break
case err != nil:
serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
fmt.Println("Got error on SQL query!")
fmt.Println(err)
s.logger.Warn("Got error on SQL query", zap.Error(err))
break
default:
if reqPassword == password {
fmt.Println("Passwords match!")
serverRespBytes = makeSignInResp(reqUsername)
s.logger.Info("Passwords match!")
serverRespBytes = s.makeSignInResp(reqUsername)
} else {
fmt.Println("Passwords don't match!")
s.logger.Info("Passwords don't match!")
serverRespBytes = makeSignInFailureResp(SIGN_EPASS)
}
}
err = session.cryptConn.SendPacket(serverRespBytes)
err = s.cryptConn.SendPacket(serverRespBytes)
if err != nil {
return err
}

View File

@@ -7,55 +7,81 @@ import (
"net"
"sync"
"github.com/Andoryuuta/Erupe/config"
"github.com/Andoryuuta/Erupe/network"
"go.uber.org/zap"
)
// Config struct allows configuring the server.
type Config struct {
DB *sql.DB
ListenAddr string
Logger *zap.Logger
DB *sql.DB
ErupeConfig *config.Config
}
// Server is a MHF sign server.
type Server struct {
sync.Mutex
sid int
sessions map[int]*Session
db *sql.DB
listenAddr string
listener net.Listener
logger *zap.Logger
erupeConfig *config.Config
sid int
sessions map[int]*Session
db *sql.DB
listener net.Listener
isShuttingDown bool
}
// NewServer creates a new Server type.
func NewServer(config *Config) *Server {
s := &Server{
sid: 0,
sessions: make(map[int]*Session),
db: config.DB,
listenAddr: config.ListenAddr,
logger: config.Logger,
erupeConfig: config.ErupeConfig,
sid: 0,
sessions: make(map[int]*Session),
db: config.DB,
}
return s
}
// Start starts the server in a new goroutine.
func (s *Server) Start() error {
l, err := net.Listen("tcp", s.listenAddr)
l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.erupeConfig.Sign.Port))
if err != nil {
return err
}
s.listener = l
//defer l.Close()
go s.acceptClients()
return nil
}
// Shutdown exits the server gracefully.
func (s *Server) Shutdown() {
s.logger.Debug("Shutting down")
s.Lock()
s.isShuttingDown = true
s.Unlock()
// This will cause the acceptor goroutine to error and exit gracefully.
s.listener.Close()
}
func (s *Server) acceptClients() {
for {
conn, err := s.listener.Accept()
if err != nil {
panic(err)
// Check if we are shutting down and exit gracefully if so.
s.Lock()
shutdown := s.isShuttingDown
s.Unlock()
if shutdown {
break
} else {
panic(err)
}
}
go s.handleConnection(s.sid, conn)
@@ -64,7 +90,7 @@ func (s *Server) acceptClients() {
}
func (s *Server) handleConnection(sid int, conn net.Conn) {
fmt.Println("Got connection to sign server")
s.logger.Info("Got connection to sign server", zap.String("remoteaddr", conn.RemoteAddr().String()))
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
nullInit := make([]byte, 8)
@@ -75,15 +101,19 @@ func (s *Server) handleConnection(sid int, conn net.Conn) {
return
}
// Create a new session.
session := &Session{
logger: s.logger,
server: s,
rawConn: &conn,
cryptConn: network.NewCryptConn(conn),
}
// Add the session to the server's sessions map.
s.Lock()
s.sessions[sid] = session
s.Unlock()
// Do the session's work.
session.work()
}