mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-08-23 00:54:11 -05:00
Everything up until creating a session
This commit is contained in:
174
common/encryption.go
Normal file
174
common/encryption.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func EncryptTypeX(key []byte, challenge []byte, data []byte) []byte {
|
||||
returnData := make([]byte, 20)
|
||||
returnData = append(returnData, data...)
|
||||
|
||||
keyLen := len(key)
|
||||
challengeLen := len(challenge)
|
||||
rnd := time.Now().Unix()
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
rnd = (rnd * 0x343FD) + 0x269EC3
|
||||
returnData[i] = byte(rnd ^ int64(key[i%keyLen]) ^ int64(challenge[i%challengeLen]))
|
||||
}
|
||||
|
||||
headerLen := 7
|
||||
returnData[0] = byte((headerLen - 2) ^ 0xec)
|
||||
returnData[1] = 0x00
|
||||
returnData[2] = 0x00
|
||||
returnData[headerLen-1] = byte((20 - headerLen) ^ 0xea)
|
||||
|
||||
header := returnData[:20]
|
||||
encxkey := make([]byte, 261)
|
||||
returnData = initEncrypt(encxkey, key, challenge, returnData)
|
||||
func6e(encxkey, returnData)
|
||||
|
||||
return append(header, returnData...)
|
||||
}
|
||||
|
||||
func initEncrypt(encxkey, key, validate, data []byte) []byte {
|
||||
// TODO: Bounds
|
||||
headerLen := (data[0] ^ 0xec) + 2
|
||||
dataStart := (data[headerLen-1] ^ 0xea)
|
||||
|
||||
data = enctypexFuncX(encxkey, key, validate, data[headerLen:])
|
||||
return data[dataStart:]
|
||||
}
|
||||
|
||||
func enctypexFuncX(encxkey, key, challenge, data []byte) []byte {
|
||||
keyLen := len(key)
|
||||
for i := 0; i < len(data); i++ {
|
||||
challenge[(key[i%keyLen]*byte(i))&7] ^= challenge[i&7] ^ data[i]
|
||||
}
|
||||
|
||||
func4(encxkey, challenge, 8)
|
||||
return data
|
||||
}
|
||||
|
||||
func func4(encxkey, challenge []byte, challengeLen int) {
|
||||
for i := 0; i < 256; i++ {
|
||||
encxkey[i] = byte(i)
|
||||
}
|
||||
|
||||
n1 := 0
|
||||
n2 := 0
|
||||
t1 := 0
|
||||
for i := 255; i != -1; i-- {
|
||||
t1, n1, n2 = func5(encxkey, i, challenge, challengeLen, n1, n2)
|
||||
t2 := encxkey[i]
|
||||
encxkey[i] = encxkey[t1]
|
||||
encxkey[t1] = t2
|
||||
}
|
||||
|
||||
encxkey[256] = encxkey[1]
|
||||
encxkey[257] = encxkey[3]
|
||||
encxkey[258] = encxkey[5]
|
||||
encxkey[259] = encxkey[7]
|
||||
encxkey[260] = encxkey[n1&0xff]
|
||||
}
|
||||
|
||||
func func5(encxkey []byte, cnt int, id []byte, idLen, n1, n2 int) (int, int, int) {
|
||||
if cnt == 0 {
|
||||
return 0, n1, n2
|
||||
}
|
||||
|
||||
mask := 1
|
||||
doLoop := true
|
||||
if cnt > 1 {
|
||||
for doLoop {
|
||||
mask = (mask << 1) + 1
|
||||
doLoop = mask < cnt
|
||||
}
|
||||
}
|
||||
|
||||
i := 0
|
||||
tmp := 0
|
||||
doLoop = true
|
||||
for doLoop {
|
||||
n1 = int(encxkey[n1&0xff] + id[n2])
|
||||
n2 += 1
|
||||
|
||||
if n2 >= idLen {
|
||||
n2 = 0
|
||||
n1 += idLen
|
||||
}
|
||||
|
||||
tmp = n1 & mask
|
||||
|
||||
i += 1
|
||||
if i > 11 {
|
||||
tmp %= cnt
|
||||
}
|
||||
|
||||
doLoop = tmp > cnt
|
||||
}
|
||||
|
||||
return tmp, n1, n2
|
||||
}
|
||||
|
||||
func func6e(encxkey []byte, data []byte) []byte {
|
||||
for i := 0; i < len(data); i++ {
|
||||
data[i] = func7e(encxkey, data[i])
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func func7e(encxkey []byte, d byte) byte {
|
||||
a := encxkey[256]
|
||||
b := encxkey[257]
|
||||
c := encxkey[a]
|
||||
encxkey[256] = (a + 1) & 0xff
|
||||
encxkey[257] = (b + c) & 0xff
|
||||
|
||||
a = encxkey[260]
|
||||
b = encxkey[257]
|
||||
b = encxkey[b]
|
||||
c = encxkey[a]
|
||||
encxkey[a] = b
|
||||
|
||||
a = encxkey[259]
|
||||
b = encxkey[257]
|
||||
a = encxkey[a]
|
||||
encxkey[b] = a
|
||||
|
||||
a = encxkey[256]
|
||||
b = encxkey[259]
|
||||
a = encxkey[a]
|
||||
encxkey[b] = a
|
||||
|
||||
a = encxkey[256]
|
||||
encxkey[a] = c
|
||||
|
||||
b = encxkey[258]
|
||||
a = encxkey[c]
|
||||
c = encxkey[259]
|
||||
b = (a + b) & 0xff
|
||||
encxkey[258] = b
|
||||
|
||||
a = b
|
||||
c = encxkey[c]
|
||||
b = encxkey[257]
|
||||
b = encxkey[b]
|
||||
a = encxkey[a]
|
||||
c = (b + c) & 0xff
|
||||
b = encxkey[260]
|
||||
b = encxkey[b]
|
||||
c = (b + c) & 0xff
|
||||
b = encxkey[c]
|
||||
c = encxkey[256]
|
||||
c = encxkey[c]
|
||||
a = (a + c) & 0xff
|
||||
c = encxkey[b]
|
||||
b = encxkey[a]
|
||||
c ^= b ^ d
|
||||
encxkey[260] = c
|
||||
encxkey[259] = d
|
||||
|
||||
return c
|
||||
}
|
||||
36
common/strings.go
Normal file
36
common/strings.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
var letterRunes = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
|
||||
func RandomString(n int) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = letterRunes[rand.Intn(len(letterRunes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
var hexRunes = []rune("0123456789abcdefabcdef")
|
||||
|
||||
func RandomHexString(n int) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = letterRunes[rand.Intn(len(hexRunes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func GetString(buf []byte) string {
|
||||
nullTerminator := bytes.IndexByte(buf, 0)
|
||||
return string(buf[:nullTerminator])
|
||||
}
|
||||
@@ -2,9 +2,8 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"wwfc/common"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"wwfc/common"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -3,10 +3,9 @@ package database
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"wwfc/common"
|
||||
|
||||
"github.com/jackc/pgx/v4"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"wwfc/common"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
66
gcsp/login.go
Normal file
66
gcsp/login.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package gcsp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"wwfc/common"
|
||||
"wwfc/database"
|
||||
)
|
||||
|
||||
func generateResponse(gpcmChallenge, nasChallenge, authToken, clientChallenge string) string {
|
||||
hasher := md5.New()
|
||||
hasher.Write([]byte(nasChallenge))
|
||||
str := hex.EncodeToString(hasher.Sum(nil))
|
||||
str += " "
|
||||
str += authToken
|
||||
str += clientChallenge
|
||||
str += gpcmChallenge
|
||||
str += hex.EncodeToString(hasher.Sum(nil))
|
||||
|
||||
_hasher := md5.New()
|
||||
_hasher.Write([]byte(str))
|
||||
return hex.EncodeToString(_hasher.Sum(nil))
|
||||
}
|
||||
|
||||
func generateProof(gpcmChallenge, nasChallenge, authToken, clientChallenge string) string {
|
||||
return generateResponse(clientChallenge, nasChallenge, authToken, gpcmChallenge)
|
||||
}
|
||||
|
||||
func login(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand, challenge string) string {
|
||||
// TODO: Validate login token with one in database
|
||||
authToken := command.OtherValues["authtoken"]
|
||||
response := generateResponse(challenge, "0qUekMb4", authToken, command.OtherValues["challenge"])
|
||||
if response != command.OtherValues["response"] {
|
||||
log.Fatalf("i hate my life")
|
||||
}
|
||||
|
||||
proof := generateProof(challenge, "0qUekMb4", command.OtherValues["authtoken"], command.OtherValues["challenge"])
|
||||
|
||||
// Perform the login with the database.
|
||||
user := database.LoginUserToGCPM(pool, ctx, authToken)
|
||||
loginTicket := strings.Replace(base64.StdEncoding.EncodeToString([]byte(common.RandomString(16))), "=", "_", -1)
|
||||
// TODO: REMOVE!!!!!
|
||||
userId = user.UserId
|
||||
// Now initiate the session
|
||||
_ = database.CreateSession(pool, ctx, user.ProfileId, loginTicket)
|
||||
|
||||
return common.CreateGameSpyMessage(common.GameSpyCommand{
|
||||
Command: "lc",
|
||||
CommandValue: "2",
|
||||
OtherValues: map[string]string{
|
||||
"sesskey": "199714190",
|
||||
"proof": proof,
|
||||
"userid": strconv.Itoa(user.UserId),
|
||||
"profileid": strconv.Itoa(user.ProfileId),
|
||||
"uniquenick": user.UniqueNick,
|
||||
"lt": loginTicket,
|
||||
"id": command.OtherValues["id"],
|
||||
},
|
||||
})
|
||||
}
|
||||
117
gcsp/main.go
Normal file
117
gcsp/main.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package gcsp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
"wwfc/common"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.Background()
|
||||
pool *pgxpool.Pool
|
||||
userId int
|
||||
)
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
log.Fatalf("GCSP server has encountered a fatal error! Reason: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func StartServer() {
|
||||
// Get config
|
||||
config := common.GetConfig()
|
||||
|
||||
// Start SQL
|
||||
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
|
||||
dbConf, err := pgxpool.ParseConfig(dbString)
|
||||
checkError(err)
|
||||
pool, err = pgxpool.ConnectConfig(ctx, dbConf)
|
||||
checkError(err)
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:29901")
|
||||
if err != nil {
|
||||
fmt.Println("Error listening:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
// Close the listener when the application closes.
|
||||
defer l.Close()
|
||||
fmt.Println("Listening on " + "127.0.0.1:29901")
|
||||
for {
|
||||
// Listen for an incoming connection.
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
fmt.Println("Error accepting: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Handle connections in a new goroutine.
|
||||
go handleRequest(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// Handles incoming requests.
|
||||
func handleRequest(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
err := conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to set keepalive - %s", err)
|
||||
}
|
||||
|
||||
err = conn.(*net.TCPConn).SetKeepAlivePeriod(time.Hour * 1000)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to set keepalive - %s", err)
|
||||
}
|
||||
|
||||
// log.Printf("%s: Connection established from %s. Sending challenge.", aurora.Green("[NOTICE]"), aurora.Yellow(conn.RemoteAddr()))
|
||||
// conn.Write([]byte(fmt.Sprintf(`\lc\1\challenge\%s\id\1\final\`, challenge)))
|
||||
|
||||
// Here we go into the listening loop
|
||||
for {
|
||||
buffer := make([]byte, 1024)
|
||||
_, err := bufio.NewReader(conn).Read(buffer)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
// Client closed connection, terminate.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
commands, err := common.ParseGameSpyMessage(string(buffer))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
logging.Notice("GCSP", "Message received. Command:", aurora.Yellow(command.Command).String())
|
||||
switch command.Command {
|
||||
case "ka":
|
||||
conn.Write([]byte(`\ka\\final\`))
|
||||
break
|
||||
case "otherslist":
|
||||
payload := common.CreateGameSpyMessage(common.GameSpyCommand{
|
||||
Command: "otherslist",
|
||||
CommandValue: "",
|
||||
OtherValues: map[string]string{
|
||||
"o": "0",
|
||||
"uniquenick": "7me4ijr5sRMCJ3d9uhvh",
|
||||
"oldone": "",
|
||||
},
|
||||
})
|
||||
conn.Write([]byte(payload))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
gcsp/profile.go
Normal file
62
gcsp/profile.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package gcsp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"strconv"
|
||||
"wwfc/common"
|
||||
"wwfc/database"
|
||||
)
|
||||
|
||||
func getProfile(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) string {
|
||||
strProfileId := command.OtherValues["profileid"]
|
||||
profileId, _ := strconv.Atoi(strProfileId)
|
||||
|
||||
user := database.GetProfile(pool, ctx, profileId)
|
||||
|
||||
_ = common.RandomHexString(32)
|
||||
return common.CreateGameSpyMessage(common.GameSpyCommand{
|
||||
Command: "pi",
|
||||
CommandValue: "",
|
||||
OtherValues: map[string]string{
|
||||
"profileid": command.OtherValues["profileid"],
|
||||
"nick": user.UniqueNick,
|
||||
"userid": strconv.Itoa(user.UserId),
|
||||
"email": user.Email,
|
||||
"sig": "b126556e5ee62d4da9629dfad0f6b2a8",
|
||||
"uniquenick": user.UniqueNick,
|
||||
"firstname": user.FirstName,
|
||||
"lastname": user.LastName,
|
||||
"pid": "11",
|
||||
"lon": "0.000000",
|
||||
"lat": "0.000000",
|
||||
"loc": "",
|
||||
"id": command.OtherValues["id"],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func updateProfile(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) {
|
||||
var firstName string
|
||||
var lastName string
|
||||
if v, ok := command.OtherValues["firstname"]; ok {
|
||||
firstName = v
|
||||
}
|
||||
|
||||
if v, ok := command.OtherValues["lastname"]; ok {
|
||||
lastName = v
|
||||
}
|
||||
|
||||
database.UpdateUser(pool, ctx, firstName, lastName, userId)
|
||||
}
|
||||
|
||||
func createStatus() string {
|
||||
return common.CreateGameSpyMessage(common.GameSpyCommand{
|
||||
Command: "bm",
|
||||
CommandValue: "100",
|
||||
OtherValues: map[string]string{
|
||||
"f": "5",
|
||||
"msg": "|s|0|ss|Offline",
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -5,13 +5,12 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"wwfc/common"
|
||||
"wwfc/database"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
)
|
||||
|
||||
func generateResponse(gpcmChallenge, nasChallenge, authToken, clientChallenge string) string {
|
||||
@@ -46,7 +45,8 @@ func login(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyComman
|
||||
// Perform the login with the database.
|
||||
user := database.LoginUserToGCPM(pool, ctx, authToken)
|
||||
loginTicket := strings.Replace(base64.StdEncoding.EncodeToString([]byte(common.RandomString(16))), "=", "_", -1)
|
||||
|
||||
// TODO: Remove in favour of proper thread safe holding
|
||||
userId = user.UserId
|
||||
// Now initiate the session
|
||||
_ = database.CreateSession(pool, ctx, user.ProfileId, loginTicket)
|
||||
|
||||
|
||||
@@ -5,15 +5,14 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
"wwfc/common"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -121,7 +120,6 @@ func handleRequest(conn net.Conn) {
|
||||
break
|
||||
case "getprofile":
|
||||
payload := getProfile(pool, ctx, command)
|
||||
fmt.Println(payload)
|
||||
conn.Write([]byte(payload))
|
||||
break
|
||||
}
|
||||
|
||||
16
logging/log.go
Normal file
16
logging/log.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"log"
|
||||
)
|
||||
|
||||
func Notice(module string, arguments ...string) {
|
||||
var finalStr string
|
||||
for _, argument := range arguments {
|
||||
finalStr += argument
|
||||
finalStr += " "
|
||||
}
|
||||
|
||||
log.Printf("[%s]: %s", aurora.Green(module), finalStr)
|
||||
}
|
||||
8
main.go
8
main.go
@@ -2,15 +2,17 @@ package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"wwfc/gcsp"
|
||||
"wwfc/gpcm"
|
||||
"wwfc/gpsp"
|
||||
"wwfc/master"
|
||||
"wwfc/matchmaking"
|
||||
"wwfc/nas"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wg := &sync.WaitGroup{}
|
||||
actions := []func(){nas.StartServer, gpcm.StartServer, gpsp.StartServer}
|
||||
wg.Add(3)
|
||||
actions := []func(){nas.StartServer, gpcm.StartServer, master.StartServer, gcsp.StartServer, matchmaking.StartServer}
|
||||
wg.Add(4)
|
||||
for _, action := range actions {
|
||||
go func(ac func()) {
|
||||
defer wg.Done()
|
||||
|
||||
5
master/challenge.go
Normal file
5
master/challenge.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package master
|
||||
|
||||
func challenge() {
|
||||
|
||||
}
|
||||
57
master/heartbeat.go
Normal file
57
master/heartbeat.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package master
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"wwfc/common"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
func heartbeat(conn net.PacketConn, addr net.Addr, buffer []byte) {
|
||||
sessionId := binary.BigEndian.Uint32(buffer[1:5])
|
||||
logging.Notice("AVAILABLE", "Received heartbeat from", addr.String())
|
||||
values := strings.Split(string(buffer[5:]), "\u0000")
|
||||
|
||||
payload := map[string]string{}
|
||||
for i := 0; i < len(values); i += 2 {
|
||||
if values[i] == "" {
|
||||
break
|
||||
}
|
||||
|
||||
payload[values[i]] = values[i+1]
|
||||
}
|
||||
|
||||
// Generate challenge and send to server
|
||||
var hexIP string
|
||||
for _, i := range strings.Split(payload["localip0"], ".") {
|
||||
val, err := strconv.ParseUint(i, 10, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
hexIP += fmt.Sprintf("%02X", val)
|
||||
}
|
||||
|
||||
port, err := strconv.ParseUint(payload["localport"], 10, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
hexPort := fmt.Sprintf("%04X", port)
|
||||
|
||||
challenge := common.RandomString(6) + "00" + hexIP + hexPort
|
||||
mutex.Lock()
|
||||
session := sessions[sessionId]
|
||||
session.Challenge = challenge
|
||||
mutex.Unlock()
|
||||
|
||||
response := []byte{0xfe, 0xfd, 0x01}
|
||||
response = binary.BigEndian.AppendUint32(response, sessionId)
|
||||
response = append(response, []byte(challenge)...)
|
||||
response = append(response, 0)
|
||||
|
||||
conn.WriteTo(response, addr)
|
||||
}
|
||||
57
master/main.go
Normal file
57
master/main.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package master
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// I would use a sync.Map instead of the map mutex combo, but this performs better.
|
||||
sessions = map[uint32]*Session{}
|
||||
mutex = sync.RWMutex{}
|
||||
)
|
||||
|
||||
func StartServer() {
|
||||
conn, err := net.ListenPacket("udp", ":27900")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
buf := make([]byte, 1024)
|
||||
_, addr, err := conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
go handleConnection(conn, addr, buf)
|
||||
}
|
||||
}
|
||||
|
||||
func handleConnection(conn net.PacketConn, addr net.Addr, buffer []byte) {
|
||||
if buffer[0] != 9 {
|
||||
addSession(addr, buffer)
|
||||
}
|
||||
|
||||
switch buffer[0] {
|
||||
case 1:
|
||||
// Challenge
|
||||
sessionId := binary.BigEndian.Uint32(buffer[1:5])
|
||||
response := []byte{0xfe, 0xfd, 0x0a}
|
||||
response = binary.BigEndian.AppendUint32(response, sessionId)
|
||||
conn.WriteTo(response, addr)
|
||||
break
|
||||
case 3:
|
||||
heartbeat(conn, addr, buffer)
|
||||
break
|
||||
case 9:
|
||||
conn.WriteTo([]byte{0xfe, 0xfd, 0x09, 0x00, 0x00, 0x00, 0x00}, addr)
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
29
master/session.go
Normal file
29
master/session.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package master
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
SessionID uint32
|
||||
Challenge string
|
||||
SecretKey string
|
||||
IsConnected bool
|
||||
}
|
||||
|
||||
func addSession(addr net.Addr, buffer []byte) {
|
||||
sessionId := binary.BigEndian.Uint32(buffer[1:5])
|
||||
|
||||
mutex.Lock()
|
||||
if _, ok := sessions[sessionId]; !ok {
|
||||
sessions[sessionId] = &Session{
|
||||
SessionID: sessionId,
|
||||
Challenge: "",
|
||||
// TODO: This is hardcoded for Mario Kart Wii
|
||||
SecretKey: "9r3Rmy",
|
||||
IsConnected: true,
|
||||
}
|
||||
}
|
||||
mutex.Unlock()
|
||||
}
|
||||
102
matchmaking/main.go
Normal file
102
matchmaking/main.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package matchmaking
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
"wwfc/common"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.Background()
|
||||
pool *pgxpool.Pool
|
||||
userId int
|
||||
)
|
||||
|
||||
const (
|
||||
ServerList = iota
|
||||
ModuleName = "MATCHMAKING"
|
||||
)
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
log.Fatalf("GCSP server has encountered a fatal error! Reason: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func StartServer() {
|
||||
// Get config
|
||||
config := common.GetConfig()
|
||||
|
||||
// Start SQL
|
||||
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
|
||||
dbConf, err := pgxpool.ParseConfig(dbString)
|
||||
checkError(err)
|
||||
pool, err = pgxpool.ConnectConfig(ctx, dbConf)
|
||||
checkError(err)
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:28910")
|
||||
if err != nil {
|
||||
fmt.Println("Error listening:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
// Close the listener when the application closes.
|
||||
defer l.Close()
|
||||
fmt.Println("Listening on " + "127.0.0.1:28910")
|
||||
for {
|
||||
// Listen for an incoming connection.
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
fmt.Println("Error accepting: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Handle connections in a new goroutine.
|
||||
go handleRequest(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// Handles incoming requests.
|
||||
func handleRequest(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
err := conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to set keepalive - %s", err)
|
||||
}
|
||||
|
||||
err = conn.(*net.TCPConn).SetKeepAlivePeriod(time.Hour * 1000)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to set keepalive - %s", err)
|
||||
}
|
||||
|
||||
// log.Printf("%s: Connection established from %s. Sending challenge.", aurora.Green("[NOTICE]"), aurora.Yellow(conn.RemoteAddr()))
|
||||
// conn.Write([]byte(fmt.Sprintf(`\lc\1\challenge\%s\id\1\final\`, challenge)))
|
||||
|
||||
// Here we go into the listening loop
|
||||
for {
|
||||
buffer := make([]byte, 1024)
|
||||
_, err := bufio.NewReader(conn).Read(buffer)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
// Client closed connection, terminate.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
logging.Notice(ModuleName, "Help me please")
|
||||
switch buffer[2] {
|
||||
case ServerList:
|
||||
serverList(conn, buffer)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
43
matchmaking/server.go
Normal file
43
matchmaking/server.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package matchmaking
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"strings"
|
||||
"wwfc/common"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
func serverList(conn net.Conn, buffer []byte) {
|
||||
logging.Notice(ModuleName, "Received server list command")
|
||||
// TODO: Make a custom decoder for this? Go's binary decoder does not support strings as they are not a fixed width.
|
||||
//listVersion := buffer[3]
|
||||
//encodingVersion := buffer[4]
|
||||
//gameVersion := binary.BigEndian.Uint32(buffer[5:])
|
||||
|
||||
index := 9
|
||||
queryGame := common.GetString(buffer[index:])
|
||||
index += len(queryGame) + 1
|
||||
gameName := common.GetString(buffer[index:])
|
||||
index += len(gameName) + 1
|
||||
|
||||
challenge := buffer[index : index+8]
|
||||
index += 8
|
||||
|
||||
filter := common.GetString(buffer[index:])
|
||||
index += len(filter) + 1
|
||||
fields := common.GetString(buffer[index:])
|
||||
index += len(fields) + 1
|
||||
|
||||
options := binary.BigEndian.Uint32(buffer[index:])
|
||||
index += 4
|
||||
|
||||
logging.Notice(ModuleName, "Values", queryGame, gameName, string(challenge), string(options))
|
||||
|
||||
// TODO: Find a game if possible, but there is nobody to do that with yet!
|
||||
output := []byte(strings.Replace(conn.RemoteAddr().String(), ".", "", -1))
|
||||
output = binary.BigEndian.AppendUint16(output, 6500)
|
||||
|
||||
// encrypted := common.EncryptTypeX([]byte("9r3Rmy"), challenge, output)
|
||||
conn.Write(output)
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package nas
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -60,7 +59,6 @@ func (route *Route) Handle() http.Handler {
|
||||
path := strings.Replace(r.URL.Path, "/", "", -1)
|
||||
actionName, _ := base64.StdEncoding.DecodeString(strings.Replace(r.PostForm.Get("action"), "*", "=", -1))
|
||||
|
||||
fmt.Println(string(actionName))
|
||||
var action Action
|
||||
for _, _action := range route.Actions {
|
||||
if path == _action.ServiceType && string(actionName) == _action.ActionName {
|
||||
|
||||
Reference in New Issue
Block a user