mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-10-02 12:06:34 -05:00
Everything up to GPCM getprofile
This commit is contained in:
29
common/config.go
Normal file
29
common/config.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Username string `xml:"username"`
|
||||
Password string `xml:"password"`
|
||||
DatabaseAddress string `xml:"databaseAddress"`
|
||||
DatabaseName string `xml:"databaseName"`
|
||||
Address string `xml:"address"`
|
||||
}
|
||||
|
||||
func GetConfig() Config {
|
||||
data, err := ioutil.ReadFile("config.xml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var config Config
|
||||
err = xml.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
28
common/encoding.go
Normal file
28
common/encoding.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package common
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Base32Encode(value int) string {
|
||||
alpha := "0123456789abcdefghijklmnopqrstuv"
|
||||
|
||||
encoded := ""
|
||||
for value > 0 {
|
||||
encoded += string(alpha[value&0x1f])
|
||||
value >>= 5
|
||||
|
||||
fmt.Sprintf("%0.9s", encoded)
|
||||
}
|
||||
|
||||
encoded = reverse(encoded)
|
||||
|
||||
return encoded
|
||||
}
|
||||
|
||||
func reverse(s string) string {
|
||||
rns := []rune(s)
|
||||
for i, j := 0, len(rns)-1; i < j; i, j = i+1, j-1 {
|
||||
rns[i], rns[j] = rns[j], rns[i]
|
||||
}
|
||||
|
||||
return string(rns)
|
||||
}
|
||||
75
common/gamespy_message.go
Normal file
75
common/gamespy_message.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type GameSpyCommand struct {
|
||||
Command string
|
||||
CommandValue string
|
||||
OtherValues map[string]string
|
||||
}
|
||||
|
||||
var (
|
||||
InvalidGameSpyCommand = errors.New("invalid command received")
|
||||
)
|
||||
|
||||
func ParseGameSpyMessage(msg string) (*GameSpyCommand, error) {
|
||||
g := &GameSpyCommand{
|
||||
OtherValues: map[string]string{},
|
||||
}
|
||||
|
||||
if !strings.Contains(msg, `\\`) || !strings.Contains(msg, `\final\`) {
|
||||
return nil, InvalidGameSpyCommand
|
||||
}
|
||||
|
||||
foundCommand := false
|
||||
for len(msg) > 0 {
|
||||
keyEnd := strings.Index(msg[1:], `\`) + 1
|
||||
key := msg[1:keyEnd]
|
||||
value := ""
|
||||
msg = msg[keyEnd+1:]
|
||||
|
||||
if key == "final" {
|
||||
// We are done.
|
||||
break
|
||||
}
|
||||
|
||||
if strings.Contains(msg, `\`) {
|
||||
if msg[0] != '\\' {
|
||||
valueEnd := strings.Index(msg[1:], `\`)
|
||||
value = msg[:valueEnd+1]
|
||||
msg = msg[valueEnd+1:]
|
||||
}
|
||||
} else {
|
||||
// We have most likely reached the end of the line.
|
||||
// However, we do not want to exit out without parsing the final key.
|
||||
value = msg
|
||||
}
|
||||
|
||||
if !foundCommand {
|
||||
g.Command = key
|
||||
g.CommandValue = value
|
||||
foundCommand = true
|
||||
} else {
|
||||
g.OtherValues[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func CreateGameSpyMessage(command GameSpyCommand) string {
|
||||
query := ""
|
||||
for k, v := range command.OtherValues {
|
||||
query += fmt.Sprintf(`\%s\%s`, strings.Replace(k, `\`, ``, -1), strings.Replace(v, `\`, ``, -1))
|
||||
}
|
||||
|
||||
if command.Command != "" {
|
||||
query = fmt.Sprintf(`\%s\%s%s`, command.Command, command.CommandValue, query)
|
||||
}
|
||||
|
||||
return query + `\final\`
|
||||
}
|
||||
29
common/gamespy_test.go
Normal file
29
common/gamespy_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseGameSpyMessage(t *testing.T) {
|
||||
_, err := ParseGameSpyMessage(`\login\\challenge\rc5lU5V5skphHnc1eSuXh8j2EzyI2TZP\authtoken\NDSLplmebUYep9V3q2CuPf5HRWoz0K3wJNzO1XqJog0QKHTIJczIu89ecAfhwKDlngIEztYsOJoH8c4zPrp\partnerid\11\response\394beecb14fa59feb8d4c3690975e24c\firewall\1\port\0\productid\11059\gamename\mariokartwii\namespaceid\16\sdkrevision\3\quiet\0\id\1\final\`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateGameSpyMessage(t *testing.T) {
|
||||
fmt.Println(CreateGameSpyMessage(GameSpyCommand{
|
||||
Command: "lc",
|
||||
CommandValue: "2",
|
||||
OtherValues: map[string]string{
|
||||
"sesskey": "07187200",
|
||||
"proof": "b0a0e576b28861f2512b943daf374158",
|
||||
"userid": "8467681766588",
|
||||
"profileid": "1",
|
||||
"uniquenick": "7me4ijr5sRMCJ3cf1asa@nds",
|
||||
"lt": "MDEyMzQ1Njc4OTBBQkNERUY=",
|
||||
"id": "1",
|
||||
},
|
||||
}))
|
||||
}
|
||||
18
common/utils.go
Normal file
18
common/utils.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"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)
|
||||
}
|
||||
12
config_example.xml
Normal file
12
config_example.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<Config>
|
||||
<!-- The address which HTTP will bind to -->
|
||||
<address>127.0.0.1:8080</address>
|
||||
|
||||
<!-- Database Credentials -->
|
||||
<username>username</username>
|
||||
<password>password</password>
|
||||
|
||||
<!-- Database information-->
|
||||
<databaseAddress>127.0.0.1</databaseAddress>
|
||||
<databaseName>wwfc</databaseName>
|
||||
</Config>
|
||||
96
database/login.go
Normal file
96
database/login.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"wwfc/common"
|
||||
)
|
||||
|
||||
const (
|
||||
DoesAuthTokenExist = `SELECT EXISTS(SELECT 1 FROM logins WHERE auth_token = $1)`
|
||||
DoesNASUserExist = `SELECT EXISTS(SELECT 1 FROM logins WHERE user_id = $1)`
|
||||
UpdateUserLogin = `UPDATE logins SET auth_token = $1 WHERE user_id = $2`
|
||||
InsertUserLogin = `INSERT INTO logins (auth_token, user_id, gsbrcd) VALUES ($1, $2, $3)`
|
||||
GetNASUserLogin = `SELECT user_id, gsbrcd FROM logins WHERE auth_token = $1 LIMIT 1`
|
||||
)
|
||||
|
||||
func GenerateAuthToken(pool *pgxpool.Pool, ctx context.Context, userId int, gsbrcd string) string {
|
||||
authToken := "NDS" + common.RandomString(80)
|
||||
for {
|
||||
// We must make sure that the auth token doesn't exist before attempting to insert it into the database.
|
||||
var exists bool
|
||||
err := pool.QueryRow(ctx, DoesAuthTokenExist, authToken).Scan(&exists)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
break
|
||||
}
|
||||
|
||||
authToken = "NDS" + common.RandomString(80)
|
||||
}
|
||||
|
||||
var exists bool
|
||||
err := pool.QueryRow(ctx, DoesNASUserExist, userId).Scan(&exists)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
// UPDATE rather than INSERT
|
||||
_, err = pool.Exec(ctx, UpdateUserLogin, authToken, userId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
_, err = pool.Exec(ctx, InsertUserLogin, authToken, userId, gsbrcd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return authToken
|
||||
}
|
||||
|
||||
func GetNASLogin(pool *pgxpool.Pool, ctx context.Context, authToken string) (int, string) {
|
||||
var userId int
|
||||
var gsbrcd string
|
||||
err := pool.QueryRow(ctx, GetNASUserLogin, authToken).Scan(&userId, &gsbrcd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return userId, gsbrcd
|
||||
}
|
||||
|
||||
func LoginUserToGCPM(pool *pgxpool.Pool, ctx context.Context, authToken string) User {
|
||||
// Query login table with the auth token.
|
||||
userId, gsbrcd := GetNASLogin(pool, ctx, authToken)
|
||||
|
||||
var exists bool
|
||||
err := pool.QueryRow(ctx, DoesUserExist, userId, gsbrcd).Scan(&exists)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
uniqueNickname := common.Base32Encode(userId) + gsbrcd
|
||||
|
||||
user := User{
|
||||
UserId: userId,
|
||||
GsbrCode: gsbrcd,
|
||||
Password: "troll",
|
||||
Email: uniqueNickname + "@nds",
|
||||
UniqueNick: uniqueNickname,
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// Create the GPCM account
|
||||
user.CreateUser(pool, ctx)
|
||||
} else {
|
||||
// TODO get the profile ID!!!!!
|
||||
user.ProfileId = 4
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
56
database/user.go
Normal file
56
database/user.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/jackc/pgx/v4"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"wwfc/common"
|
||||
)
|
||||
|
||||
const (
|
||||
InsertUser = `INSERT INTO users (user_id, gsbrcd, password, email, unique_nick) VALUES ($1, $2, $3, $4, $5) RETURNING profile_id`
|
||||
CreateUserSession = `INSERT INTO sessions (session_key, profile_id, login_ticket) VALUES ($1, $2, $3)`
|
||||
DoesUserExist = `SELECT EXISTS(SELECT 1 FROM users WHERE user_id = $1 AND gsbrcd = $2)`
|
||||
DeleteUserSession = `DELETE FROM sessions WHERE profile_id = $1`
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ProfileId int
|
||||
UserId int
|
||||
GsbrCode string
|
||||
Password string
|
||||
Email string
|
||||
UniqueNick string
|
||||
}
|
||||
|
||||
func (user *User) CreateUser(pool *pgxpool.Pool, ctx context.Context) {
|
||||
err := pool.QueryRow(ctx, InsertUser, user.UserId, user.GsbrCode, user.Password, user.Email, user.UniqueNick).Scan(&user.ProfileId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func CreateSession(pool *pgxpool.Pool, ctx context.Context, profileId int, loginTicket string) string {
|
||||
// Delete session first.
|
||||
deleteSession(pool, ctx, profileId)
|
||||
|
||||
sessionKey := common.RandomString(8)
|
||||
_, err := pool.Exec(ctx, CreateUserSession, sessionKey, profileId, loginTicket)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return sessionKey
|
||||
}
|
||||
|
||||
func deleteSession(pool *pgxpool.Pool, ctx context.Context, profileId int) {
|
||||
_, err := pool.Exec(ctx, DeleteUserSession, profileId)
|
||||
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func GetProfile(pool *pgxpool.Pool, ctx context.Context) {
|
||||
|
||||
}
|
||||
14
go.mod
14
go.mod
@@ -7,4 +7,16 @@ require (
|
||||
golang.org/x/net v0.10.0
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.9.0 // indirect
|
||||
require (
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.14.0 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgtype v1.14.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.18.1 // indirect
|
||||
github.com/jackc/puddle v1.3.0 // indirect
|
||||
golang.org/x/crypto v0.6.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
)
|
||||
|
||||
190
go.sum
190
go.sum
@@ -1,6 +1,196 @@
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q=
|
||||
github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
|
||||
github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
|
||||
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0=
|
||||
github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/logrusorgru/aurora/v3 v3.0.0 h1:R6zcoZZbvVcGMvDCKo45A9U/lzYyzl5NfYIvznmDfE4=
|
||||
github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
|
||||
65
gpcm/login.go
Normal file
65
gpcm/login.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package gpcm
|
||||
|
||||
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)
|
||||
|
||||
// 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"],
|
||||
},
|
||||
})
|
||||
}
|
||||
113
gpcm/main.go
Normal file
113
gpcm/main.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package gpcm
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
"wwfc/common"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.Background()
|
||||
pool *pgxpool.Pool
|
||||
)
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
log.Fatalf("GPCM 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:29900")
|
||||
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:29900")
|
||||
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()
|
||||
|
||||
// Set session ID and challenge
|
||||
challenge := common.RandomString(10)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
command, err := common.ParseGameSpyMessage(string(buffer))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("%s: Message received. Command: %s", aurora.Green("[NOTICE]"), aurora.Yellow(command.Command))
|
||||
switch command.Command {
|
||||
case "ka":
|
||||
conn.Write([]byte(`\ka\final\`))
|
||||
break
|
||||
case "login":
|
||||
payload := login(pool, ctx, command, challenge)
|
||||
conn.Write([]byte(payload))
|
||||
break
|
||||
case "getprofile":
|
||||
payload := getProfile(pool, ctx, command)
|
||||
fmt.Println(payload)
|
||||
conn.Write([]byte(payload))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
34
gpcm/profile.go
Normal file
34
gpcm/profile.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package gpcm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"wwfc/common"
|
||||
)
|
||||
|
||||
func getProfile(pool *pgxpool.Pool, ctx context.Context, command *common.GameSpyCommand) string {
|
||||
for k, v := range command.OtherValues {
|
||||
fmt.Println(fmt.Sprintf("%s: %s", k, v))
|
||||
}
|
||||
|
||||
sig := hex.EncodeToString([]byte(common.RandomString(16)))
|
||||
return common.CreateGameSpyMessage(common.GameSpyCommand{
|
||||
Command: "pi",
|
||||
CommandValue: "",
|
||||
OtherValues: map[string]string{
|
||||
"profileid": command.OtherValues["profileid"],
|
||||
"nick": "7me4ijr5sRMCJ23ul711",
|
||||
"userid": "8467681766588",
|
||||
"email": "7me4ijr5sRMCJ23ul711@nds",
|
||||
"sig": sig,
|
||||
"uniquenick": "7me4ijr5sRMCJ23ul711",
|
||||
"pid": "11",
|
||||
"lon": "0.000000",
|
||||
"lat": "0.000000",
|
||||
"loc": "",
|
||||
"id": command.OtherValues["id"],
|
||||
},
|
||||
})
|
||||
}
|
||||
46
gpsp/main.go
Normal file
46
gpsp/main.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package gpsp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
func StartServer() {
|
||||
l, err := net.Listen("tcp", "127.0.0.1:27900")
|
||||
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.
|
||||
fmt.Println("aaaa")
|
||||
go handleRequest(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// Handles incoming requests.
|
||||
func handleRequest(conn net.Conn) {
|
||||
// Make a buffer to hold incoming data.
|
||||
buf := make([]byte, 1024)
|
||||
// Read the incoming connection into the buffer.
|
||||
reqLen, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading:", err.Error())
|
||||
}
|
||||
fmt.Println(reqLen)
|
||||
fmt.Println(string(buf))
|
||||
// Send a response back to person contacting us.
|
||||
conn.Write([]byte(`\ka\\final\`))
|
||||
// Close the connection when you're done with it.
|
||||
conn.Close()
|
||||
}
|
||||
22
main.go
Normal file
22
main.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"wwfc/gpcm"
|
||||
"wwfc/gpsp"
|
||||
"wwfc/nas"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wg := &sync.WaitGroup{}
|
||||
actions := []func(){nas.StartServer, gpcm.StartServer, gpsp.StartServer}
|
||||
wg.Add(3)
|
||||
for _, action := range actions {
|
||||
go func(ac func()) {
|
||||
defer wg.Done()
|
||||
ac()
|
||||
}(action)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
37
nas/login.go
Normal file
37
nas/login.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package nas
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"wwfc/database"
|
||||
)
|
||||
|
||||
// TODO: Generate and store in database!!!
|
||||
const Challenge = "0qUekMb4"
|
||||
|
||||
func login(r *Response) {
|
||||
// Validate the user id. It must be an integer.
|
||||
strUserId, _ := base64.StdEncoding.DecodeString(strings.Replace(r.request.PostForm.Get("userid"), "*", "=", -1))
|
||||
userId, err := strconv.Atoi(string(strUserId))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
gsbrcd, _ := base64.StdEncoding.DecodeString(strings.Replace(r.request.PostForm.Get("gsbrcd"), "*", "=", -1))
|
||||
authToken := database.GenerateAuthToken(pool, ctx, userId, string(gsbrcd))
|
||||
|
||||
param := url.Values{}
|
||||
// param.Set("datetime", strings.Replace(base64.StdEncoding.EncodeToString([]byte("20230911232518")), "=", "*", -1))
|
||||
param.Set("retry", strings.Replace(base64.StdEncoding.EncodeToString([]byte("0")), "=", "*", -1))
|
||||
param.Set("returncd", strings.Replace(base64.StdEncoding.EncodeToString([]byte("001")), "=", "*", -1))
|
||||
param.Set("locator", strings.Replace(base64.StdEncoding.EncodeToString([]byte("gamespy.com")), "=", "*", -1))
|
||||
param.Set("challenge", strings.Replace(base64.StdEncoding.EncodeToString([]byte(Challenge)), "=", "*", -1))
|
||||
param.Set("token", strings.Replace(base64.StdEncoding.EncodeToString([]byte(authToken)), "=", "*", -1))
|
||||
|
||||
// Encode and send off to be written!
|
||||
r.payload = []byte(param.Encode())
|
||||
r.payload = []byte(param.Encode())
|
||||
r.payload = []byte(strings.Replace(string(r.payload), "%2A", "*", -1))
|
||||
}
|
||||
43
nas/main.go
Normal file
43
nas/main.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package nas
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"log"
|
||||
"wwfc/common"
|
||||
"wwfc/nhttp"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.Background()
|
||||
pool *pgxpool.Pool
|
||||
)
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
log.Fatalf("NAS 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)
|
||||
|
||||
// Finally, initialize the HTTP server
|
||||
fmt.Printf("Starting HTTP connection (%s)...\nNot using the usual port for HTTP?\nBe sure to use a proxy, otherwise the Wii can't connect!\n", "[::1]")
|
||||
r := NewRoute()
|
||||
ac := r.HandleGroup("ac")
|
||||
{
|
||||
ac.HandleAction("login", login)
|
||||
}
|
||||
|
||||
log.Fatal(nhttp.ListenAndServe("0.0.0.0:80", r.Handle()))
|
||||
}
|
||||
92
nas/route.go
Normal file
92
nas/route.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package nas
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Route struct {
|
||||
Actions []Action
|
||||
}
|
||||
|
||||
// Action contains information about how a specified action should be handled.
|
||||
type Action struct {
|
||||
ActionName string
|
||||
Callback func(*Response)
|
||||
ServiceType string
|
||||
}
|
||||
|
||||
func NewRoute() Route {
|
||||
return Route{}
|
||||
}
|
||||
|
||||
// RoutingGroup defines a group of actions for a given service type.
|
||||
type RoutingGroup struct {
|
||||
Route *Route
|
||||
ServiceType string
|
||||
}
|
||||
|
||||
// HandleGroup returns a routing group type for the given service type.
|
||||
func (route *Route) HandleGroup(serviceType string) RoutingGroup {
|
||||
return RoutingGroup{
|
||||
Route: route,
|
||||
ServiceType: serviceType,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RoutingGroup) HandleAction(action string, function func(*Response)) {
|
||||
r.Route.Actions = append(r.Route.Actions, Action{
|
||||
ActionName: action,
|
||||
Callback: function,
|
||||
ServiceType: r.ServiceType,
|
||||
})
|
||||
}
|
||||
|
||||
func (route *Route) Handle() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%s %s via %s", aurora.Yellow(r.Method), aurora.Cyan(r.URL), aurora.Cyan(r.Host))
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
log.Printf(aurora.Red("failed to parse form").String())
|
||||
return
|
||||
}
|
||||
|
||||
// While generally a bad idea, we can only have a path with a depth of one.
|
||||
path := strings.Replace(r.URL.Path, "/", "", -1)
|
||||
actionName, _ := base64.StdEncoding.DecodeString(strings.Replace(r.PostForm.Get("action"), "*", "=", -1))
|
||||
|
||||
var action Action
|
||||
for _, _action := range route.Actions {
|
||||
if path == _action.ServiceType && string(actionName) == _action.ActionName {
|
||||
action = _action
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we found an action
|
||||
if action.ActionName == "" && action.ServiceType == "" {
|
||||
log.Printf(aurora.Red("no action found").String())
|
||||
return
|
||||
}
|
||||
|
||||
response := NewResponse(&w, r)
|
||||
action.Callback(response)
|
||||
|
||||
// Our callback function will already have formulated the needed response.
|
||||
// We will write common headers then the data.
|
||||
w.Header().Set("NODE", "wifiappe1")
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(response.payload)))
|
||||
w.Write(response.payload)
|
||||
})
|
||||
}
|
||||
|
||||
func NewResponse(w *http.ResponseWriter, r *http.Request) *Response {
|
||||
return &Response{
|
||||
request: r,
|
||||
writer: w,
|
||||
}
|
||||
}
|
||||
9
nas/structure.go
Normal file
9
nas/structure.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package nas
|
||||
|
||||
import "net/http"
|
||||
|
||||
type Response struct {
|
||||
request *http.Request
|
||||
writer *http.ResponseWriter
|
||||
payload []byte
|
||||
}
|
||||
Reference in New Issue
Block a user