repository cleanup

This commit is contained in:
wish
2022-07-29 03:25:23 +10:00
parent a0be6c627c
commit 2c0e7a5267
645 changed files with 996 additions and 903 deletions

View File

@@ -0,0 +1,56 @@
package entranceserver
import (
"encoding/binary"
)
var (
_bin8Key = []byte{0x01, 0x23, 0x34, 0x45, 0x56, 0xAB, 0xCD, 0xEF}
_sum32Table0 = []byte{0x35, 0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12}
_sum32Table1 = []byte{0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12, 0xDE, 0xDE, 0x35}
)
// CalcSum32 calculates the custom MHF "sum32" checksum of the given data.
func CalcSum32(data []byte) uint32 {
tableIdx0 := int(len(data) & 0xFF)
tableIdx1 := int(data[len(data)>>1] & 0xFF)
out := make([]byte, 4)
for i := 0; i < len(data); i++ {
tableIdx0++
tableIdx1++
tmp := byte((_sum32Table1[tableIdx1%9] ^ _sum32Table0[tableIdx0%7]) ^ data[i])
out[i&3] = (out[i&3] + tmp) & 0xFF
}
return binary.BigEndian.Uint32(out)
}
// EncryptBin8 encrypts the given data using MHF's "binary8" encryption.
func EncryptBin8(data []byte, key byte) []byte {
curKey := uint32(((54323 * uint(key)) + 1) & 0xFFFFFFFF)
var output []byte
for i := 0; i < len(data); i++ {
tmp := (_bin8Key[i&7] ^ byte((curKey>>13)&0xFF))
output = append(output, data[i]^tmp)
curKey = uint32(((54323 * uint(curKey)) + 1) & 0xFFFFFFFF)
}
return output
}
// DecryptBin8 decrypts the given MHF "binary8" data.
func DecryptBin8(data []byte, key byte) []byte {
curKey := uint32(((54323 * uint(key)) + 1) & 0xFFFFFFFF)
var output []byte
for i := 0; i < len(data); i++ {
tmp := (data[i] ^ byte((curKey>>13)&0xFF))
output = append(output, tmp^_bin8Key[i&7])
curKey = uint32(((54323 * uint(curKey)) + 1) & 0xFFFFFFFF)
}
return output
}

View File

@@ -0,0 +1,73 @@
package entranceserver
import (
"bytes"
"encoding/hex"
"fmt"
"testing"
)
var tests = []struct {
data []byte
sum uint32
encryptedData []byte
encryptionKey byte
}{
{
[]byte{0x4C, 0x6F, 0x72, 0x65, 0x6D, 0x20},
0xAE6CA2C,
[]byte{0x7E, 0x4C, 0x1D, 0x16, 0x9D, 0x46},
0x55,
},
{
[]byte{0x69, 0x70, 0x73, 0x75, 0x6D, 0x20, 0x64, 0x6F, 0x6C, 0x6F, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6D, 0x65, 0x74, 0x2C, 0x20},
0xCE5F1E96,
[]byte{0x41, 0x65, 0xFF, 0x74, 0x64, 0x45, 0xB8, 0xB1, 0x18, 0xB0, 0x94, 0xA3, 0xF8, 0xD, 0xBF, 0x3C, 0xC8, 0x24, 0xE2, 0xEC, 0x3B, 0xCE},
0x7A,
},
{
[]byte{0x63, 0x6F, 0x6E, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, 0x69, 0x6E, 0x67, 0x20, 0x65, 0x6C, 0x69, 0x74, 0x2C, 0x20},
0xF3EECEBB,
[]byte{0xE, 0xBB, 0x19, 0xA5, 0xB9, 0x34, 0xFE, 0x51, 0x0, 0x61, 0x2D, 0x38, 0xB2, 0x98, 0xC2, 0xE0, 0x17, 0xDE, 0x6E, 0xE3, 0x6C, 0x1E, 0x19, 0xB6, 0x8C, 0x57, 0x32, 0x32, 0xD8},
0xF8,
},
}
func TestSum32(t *testing.T) {
for k, test := range tests {
testname := fmt.Sprintf("sum32_test_%d", k)
t.Run(testname, func(t *testing.T) {
gotSum := CalcSum32(test.data)
if gotSum != test.sum {
t.Errorf("got sum32 0x%X, want 0x%X", gotSum, test.sum)
}
})
}
}
func TestEncryptBin8(t *testing.T) {
for k, test := range tests {
testname := fmt.Sprintf("encrypt_bin8_test_%d", k)
t.Run(testname, func(t *testing.T) {
gotEncData := EncryptBin8(test.data, test.encryptionKey)
if !bytes.Equal(gotEncData, test.encryptedData) {
t.Errorf("got\n\t%s\nwant\n\t%s", hex.Dump(gotEncData), hex.Dump(test.encryptedData))
}
})
}
}
func TestDecryptBin8(t *testing.T) {
for k, test := range tests {
testname := fmt.Sprintf("decrypt_bin8_test_%d", k)
t.Run(testname, func(t *testing.T) {
gotDecData := DecryptBin8(test.encryptedData, test.encryptionKey)
if !bytes.Equal(gotDecData, test.data) {
t.Errorf("got\n\t%s\nwant\n\t%s", hex.Dump(gotDecData), hex.Dump(test.data))
}
})
}
}

View File

@@ -0,0 +1,122 @@
package entranceserver
import (
"encoding/hex"
"fmt"
"io"
"net"
"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) {
// 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
}
s.logger.Debug("Got entrance server command:\n", zap.String("raw", hex.Dump(pkt)))
data := makeSv2Resp(s.erupeConfig, s)
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.
conn.Close()
}

View File

@@ -0,0 +1,115 @@
package entranceserver
import (
"encoding/binary"
"net"
"erupe-ce/common/stringsupport"
"erupe-ce/common/byteframe"
"erupe-ce/config"
"erupe-ce/server/channelserver"
)
// Server Entries
var season uint8
// Server Channels
var currentplayers uint16
func encodeServerInfo(config *config.Config, s *Server) []byte {
serverInfos := config.Entrance.Entries
bf := byteframe.NewByteFrame()
for serverIdx, si := range serverInfos {
sid := (4096 + serverIdx*256) + 16
err := s.db.QueryRow("SELECT season FROM servers WHERE server_id=$1", sid).Scan(&season)
if err != nil {
panic(err)
}
if si.IP == "" {
si.IP = config.HostIP
}
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4()))
bf.WriteUint16(16 + uint16(serverIdx))
bf.WriteUint16(0x0000)
bf.WriteUint16(uint16(len(si.Channels)))
bf.WriteUint8(si.Type)
bf.WriteUint8(season)
bf.WriteUint8(si.Recommended)
bf.WriteUint8(0) // Prevents malformed server name
combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...)
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, 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_id=$1", sid).Scan(&currentplayers)
if err != nil {
panic(err)
}
bf.WriteUint16(currentplayers)
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint16(319) // Unk
bf.WriteUint16(252) // Unk
bf.WriteUint16(248) // Unk
bf.WriteUint16(0x3039)
}
}
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Unix()))
bf.WriteUint32(0x0000003C)
return bf.Data()
}
func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byte {
bf := byteframe.NewByteFrame()
bf.WriteBytes([]byte(respType))
bf.WriteUint16(entryCount)
bf.WriteUint16(uint16(len(data)))
if len(data) > 0 {
bf.WriteUint32(CalcSum32(data))
bf.WriteBytes(data)
}
dataToEncrypt := bf.Data()
bf = byteframe.NewByteFrame()
bf.WriteUint8(key)
bf.WriteBytes(EncryptBin8(dataToEncrypt, key))
return bf.Data()
}
func makeSv2Resp(config *config.Config, s *Server) []byte {
serverInfos := config.Entrance.Entries
rawServerData := encodeServerInfo(config, s)
bf := byteframe.NewByteFrame()
bf.WriteBytes(makeHeader(rawServerData, "SV2", uint16(len(serverInfos)), 0x00))
return bf.Data()
}
func makeUsrResp(pkt []byte, s *Server) []byte {
bf := byteframe.NewByteFrameFromBytes(pkt)
_ = bf.ReadUint32() // ALL+
_ = bf.ReadUint8() // 0x00
userEntries := bf.ReadUint16()
resp := byteframe.NewByteFrame()
for i := 0; i < int(userEntries); i++ {
cid := bf.ReadUint32()
var sid uint16
err := s.db.QueryRow("SELECT(SELECT server_id FROM sign_sessions WHERE char_id=$1) AS _", cid).Scan(&sid)
if err != nil {
resp.WriteBytes(make([]byte, 4))
continue
} else {
resp.WriteUint16(sid)
resp.WriteUint16(0)
}
}
return makeHeader(resp.Data(), "USR", userEntries, 0x00)
}