diff options
Diffstat (limited to 'pkg/api/api0/api0gameserver/nsserver.go')
-rw-r--r-- | pkg/api/api0/api0gameserver/nsserver.go | 84 |
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 +} |