Skip to content

Instantly share code, notes, and snippets.

@cetteup
Last active September 4, 2024 20:53
Show Gist options
  • Save cetteup/cfdf7c1659410a3d4b29dade3cefacae to your computer and use it in GitHub Desktop.
Save cetteup/cfdf7c1659410a3d4b29dade3cefacae to your computer and use it in GitHub Desktop.
GameSpy login flow
package main
import (
"fmt"
"net"
"os"
"time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/dogclan/dumbspy/pkg/gamespy"
)
const (
network = "tcp4"
timeout = time.Duration(5) * time.Second
)
func main() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout})
raddr, err := net.ResolveTCPAddr(network, net.JoinHostPort("gpcm.bf2hub.com", "29900"))
if err != nil {
log.Fatal().
Err(err).
Msg("Failed to resolve address")
}
conn, err := net.DialTCP(raddr.Network(), nil, raddr)
if err != nil {
log.Fatal().
Err(err).
Msg("Failed to connect")
}
defer func(conn net.Conn) {
err2 := conn.Close()
if err2 != nil {
log.Error().
Err(err2).
Msg("Failed to close connection")
}
}(conn)
prompt, err := read(conn, timeout)
if err != nil {
log.Fatal().
Err(err).
Msg("Failed to read login challenge prompt")
}
nick := "your-nick"
password := "your-password"
challenge := gamespy.RandString(32)
login := new(gamespy.Packet)
login.Set("login", "")
login.Set("challenge", challenge)
login.Set("uniquenick", nick)
login.Set("response", gamespy.GenerateProof(
nick,
gamespy.ComputeMD5(password),
challenge,
prompt.Get("challenge"),
))
login.Set("port", "-15095")
login.Set("productid", "10493")
login.Set("gamename", "battlefield2")
login.Set("namespaceid", "12")
login.Set("sdkrevision", "3")
login.Set("id", "1")
err = write(conn, login, timeout)
if err != nil {
log.Fatal().
Err(err).
Msg("Failed to write login request")
}
response, err := read(conn, timeout)
if err != nil {
log.Fatal().
Err(err).
Msg("Failed to read login response")
}
if _, ok := response.Lookup("sesskey"); !ok {
log.Fatal().Msg("Failed to log in")
}
log.Info().Msg("Logged in successfully")
lookup := new(gamespy.Packet)
lookup.Set("getprofile", "")
lookup.Set("sesskey", response.Get("sesskey"))
lookup.Set("profileid", response.Get("profileid"))
lookup.Set("id", "2")
err = write(conn, lookup, timeout)
if err != nil {
log.Fatal().
Err(err).
Msg("Failed to write profile lookup request")
}
_, err = read(conn, timeout)
if err != nil {
log.Fatal().
Err(err).
Msg("Failed to read profile lookup response")
}
}
func read(conn net.Conn, timeout time.Duration) (*gamespy.Packet, error) {
if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
return nil, fmt.Errorf("failed to set read deadline: %w", err)
}
log.Debug().Msg("Reading packet")
buffer := make([]byte, 512)
n, err := conn.Read(buffer)
if err != nil {
return nil, fmt.Errorf("failed to read packet: %w", err)
}
log.Debug().Msg("Parsing packet")
packet, err := gamespy.NewPacketFromBytes(buffer[:n])
if err != nil {
return nil, fmt.Errorf("failed to parse packet: %w", err)
}
log.Debug().
Bytes("data", packet.Bytes()).
Msg("Received packet")
return packet, nil
}
func write(conn net.Conn, packet *gamespy.Packet, timeout time.Duration) error {
if err := conn.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
return fmt.Errorf("failed to set write deadline: %w", err)
}
log.Debug().
Bytes("data", packet.Bytes()).
Msg("Sending packet")
if _, err := conn.Write(packet.Bytes()); err != nil {
return fmt.Errorf("failed to send packet: %w", err)
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment