mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 15:43:49 +01:00
Add explicit error discards (_ =) for Close() calls on network connections, SQL rows, and file handles across 28 files. Also add .golangci.yml with standard linter defaults to match CI configuration.
128 lines
2.8 KiB
Go
128 lines
2.8 KiB
Go
package entranceserver
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
|
|
"erupe-ce/config"
|
|
"erupe-ce/network"
|
|
"github.com/jmoiron/sqlx"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// Server is a MHF entrance server.
|
|
type Server struct {
|
|
sync.Mutex
|
|
logger *zap.Logger
|
|
erupeConfig *_config.Config
|
|
db *sqlx.DB
|
|
listener net.Listener
|
|
isShuttingDown bool
|
|
}
|
|
|
|
// Config struct allows configuring the server.
|
|
type Config struct {
|
|
Logger *zap.Logger
|
|
DB *sqlx.DB
|
|
ErupeConfig *_config.Config
|
|
}
|
|
|
|
// NewServer creates a new Server type.
|
|
func NewServer(config *Config) *Server {
|
|
s := &Server{
|
|
logger: config.Logger,
|
|
erupeConfig: config.ErupeConfig,
|
|
db: config.DB,
|
|
}
|
|
return s
|
|
}
|
|
|
|
// Start starts the server in a new goroutine.
|
|
func (s *Server) Start() error {
|
|
|
|
l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.erupeConfig.Entrance.Port))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s.listener = l
|
|
|
|
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()
|
|
}
|
|
|
|
// acceptClients handles accepting new clients in a loop.
|
|
func (s *Server) acceptClients() {
|
|
for {
|
|
conn, err := s.listener.Accept()
|
|
if err != nil {
|
|
// Check if we are shutting down and exit gracefully if so.
|
|
s.Lock()
|
|
shutdown := s.isShuttingDown
|
|
s.Unlock()
|
|
|
|
if shutdown {
|
|
break
|
|
} else {
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Start a new goroutine for the connection so that we don't block other incoming connections.
|
|
go s.handleEntranceServerConnection(conn)
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleEntranceServerConnection(conn net.Conn) {
|
|
defer func() { _ = conn.Close() }()
|
|
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
|
|
nullInit := make([]byte, 8)
|
|
n, err := io.ReadFull(conn, nullInit)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to read 8 NULL init", zap.Error(err))
|
|
return
|
|
} else if n != len(nullInit) {
|
|
s.logger.Warn("io.ReadFull couldn't read the full 8 byte init.")
|
|
return
|
|
}
|
|
|
|
// Create a new encrypted connection handler and read a packet from it.
|
|
cc := network.NewCryptConn(conn)
|
|
pkt, err := cc.ReadPacket()
|
|
if err != nil {
|
|
s.logger.Warn("Error reading packet", zap.Error(err))
|
|
return
|
|
}
|
|
|
|
if s.erupeConfig.DebugOptions.LogInboundMessages {
|
|
fmt.Printf("[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
|
|
}
|
|
|
|
local := strings.Split(conn.RemoteAddr().String(), ":")[0] == "127.0.0.1"
|
|
|
|
data := makeSv2Resp(s.erupeConfig, s, local)
|
|
if len(pkt) > 5 {
|
|
data = append(data, makeUsrResp(pkt, s)...)
|
|
}
|
|
_ = cc.SendPacket(data)
|
|
// Close because we only need to send the response once.
|
|
// Any further requests from the client will come from a new connection.
|
|
}
|