aboutsummaryrefslogtreecommitdiff
path: root/pkg/api/api0/api0gameserver/nsserver.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api/api0/api0gameserver/nsserver.go')
-rw-r--r--pkg/api/api0/api0gameserver/nsserver.go84
1 files changed, 84 insertions, 0 deletions
diff --git a/pkg/api/api0/api0gameserver/nsserver.go b/pkg/api/api0/api0gameserver/nsserver.go
new file mode 100644
index 0000000..5897a9c
--- /dev/null
+++ b/pkg/api/api0/api0gameserver/nsserver.go
@@ -0,0 +1,84 @@
+// Package api0gameserver interacts with game servers using the original master
+// server api.
+package api0gameserver
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "net/http"
+ "net/netip"
+ "net/url"
+ "strconv"
+)
+
+var (
+ ErrInvalidResponse = errors.New("invalid response")
+ ErrAuthFailed = errors.New("authentication failed")
+)
+
+// VerifyText is the expected server response for /verify.
+const VerifyText = "I am a northstar server!"
+
+// Verify checks whether an address is a Northstar auth server. If the HTTP
+// request succeeds but the response is incorrect, err is ErrInvalidResponse.
+func Verify(ctx context.Context, auth netip.AddrPort) error {
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://"+auth.String()+"/verify", nil)
+ if err != nil {
+ return err // shouldn't happen
+ }
+ req.Header.Set("User-Agent", "Atlas")
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ buf, err := io.ReadAll(io.LimitReader(resp.Body, int64(len(VerifyText)*2)))
+ if err != nil {
+ return err
+ }
+
+ if string(bytes.TrimSpace(buf)) != VerifyText {
+ return ErrInvalidResponse
+ }
+ return nil
+}
+
+// AuthenticateIncomingPlayer checks if a player can connect to a game server,
+// registers a one-time connection token, and sends the player's pdata. If the
+// authentication request returns invalid JSON, err is ErrInvalidResponse. If
+// the authentication response .success is false, err is ErrAuthFailed.
+func AuthenticateIncomingPlayer(ctx context.Context, auth netip.AddrPort, uid uint64, username, connToken, serverToken string, pdata []byte) error {
+ u := "http://" + auth.String() + "/authenticate_incoming_player" +
+ "?id=" + strconv.FormatUint(uid, 10) +
+ "&authToken=" + url.QueryEscape(connToken) +
+ "&serverAuthToken=" + url.QueryEscape(serverToken) +
+ "&username=" + url.QueryEscape(username)
+
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, bytes.NewReader(pdata))
+ if err != nil {
+ return err // shouldn't happen
+ }
+ req.Header.Set("User-Agent", "Atlas")
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ var obj struct {
+ Success bool `json:"success"`
+ }
+ if err := json.NewDecoder(resp.Body).Decode(&obj); err != nil {
+ return ErrInvalidResponse
+ }
+ if !obj.Success {
+ return ErrAuthFailed
+ }
+ return nil
+}