From a3109ce30f0de2fec0f5f130761939233cbeee77 Mon Sep 17 00:00:00 2001 From: pg9182 <96569817+pg9182@users.noreply.github.com> Date: Mon, 6 Mar 2023 00:16:04 -0500 Subject: pkg/api/api0: Allow game servers to reject connections with a reason This is backwards-compatible with old clients, since it implements it as a new error type. --- pkg/api/api0/api0gameserver/nsserver.go | 22 +++++++++++++++++++++- pkg/api/api0/client.go | 4 ++++ pkg/api/api0/errors.go | 3 +++ pkg/api/api0/metrics.go | 2 ++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/pkg/api/api0/api0gameserver/nsserver.go b/pkg/api/api0/api0gameserver/nsserver.go index 5897a9c..38fa0ba 100644 --- a/pkg/api/api0/api0gameserver/nsserver.go +++ b/pkg/api/api0/api0gameserver/nsserver.go @@ -19,6 +19,22 @@ var ( ErrAuthFailed = errors.New("authentication failed") ) +type ConnectionRejectedError string + +func (c ConnectionRejectedError) Reason() string { + if c == "" { + return "unknown" + } + return string(c) +} + +func (c ConnectionRejectedError) Error() string { + if c == "" { + return "connection rejected" + } + return "connection rejected: " + string(c) +} + // VerifyText is the expected server response for /verify. const VerifyText = "I am a northstar server!" @@ -72,11 +88,15 @@ func AuthenticateIncomingPlayer(ctx context.Context, auth netip.AddrPort, uid ui defer resp.Body.Close() var obj struct { - Success bool `json:"success"` + Success bool `json:"success"` + Reject string `json:"reject"` } if err := json.NewDecoder(resp.Body).Decode(&obj); err != nil { return ErrInvalidResponse } + if obj.Reject != "" { + return ConnectionRejectedError(obj.Reject) + } if !obj.Success { return ErrAuthFailed } diff --git a/pkg/api/api0/client.go b/pkg/api/api0/client.go index 6e02603..2dbad76 100644 --- a/pkg/api/api0/client.go +++ b/pkg/api/api0/client.go @@ -577,7 +577,11 @@ func (h *Handler) handleClientAuthWithServer(w http.ResponseWriter, r *http.Requ if errors.Is(err, context.DeadlineExceeded) { err = fmt.Errorf("request timed out") } + var rej api0gameserver.ConnectionRejectedError switch { + case errors.As(err, &rej): + h.m().client_authwithserver_requests_total.reject_gameserver.Inc() + respFail(w, r, http.StatusForbidden, ErrorCode_CONNECTION_REJECTED.MessageObjf("%s", rej.Reason())) case errors.Is(err, api0gameserver.ErrAuthFailed): h.m().client_authwithserver_requests_total.reject_gameserverauth.Inc() respFail(w, r, http.StatusInternalServerError, ErrorCode_JSON_PARSE_ERROR.MessageObj()) // this is kind of misleading... but it's what the original master server did diff --git a/pkg/api/api0/errors.go b/pkg/api/api0/errors.go index e407be6..244200b 100644 --- a/pkg/api/api0/errors.go +++ b/pkg/api/api0/errors.go @@ -21,6 +21,7 @@ const ( ErrorCode_JSON_PARSE_ERROR ErrorCode = "JSON_PARSE_ERROR" // Error parsing json response ErrorCode_UNSUPPORTED_VERSION ErrorCode = "UNSUPPORTED_VERSION" // The version you are using is no longer supported ErrorCode_DUPLICATE_SERVER ErrorCode = "DUPLICATE_SERVER" // A server with this port already exists for your IP address + ErrorCode_CONNECTION_REJECTED ErrorCode = "CONNECTION_REJECTED" // Connection rejected ) const ( @@ -88,6 +89,8 @@ func (n ErrorCode) Message() string { return "Internal server error" case ErrorCode_BAD_REQUEST: return "Bad request" + case ErrorCode_CONNECTION_REJECTED: + return "Connection rejected" default: return string(n) } diff --git a/pkg/api/api0/metrics.go b/pkg/api/api0/metrics.go index 46c8615..271b61b 100644 --- a/pkg/api/api0/metrics.go +++ b/pkg/api/api0/metrics.go @@ -94,6 +94,7 @@ type apiMetrics struct { reject_masterserver_token *metrics.Counter reject_password *metrics.Counter reject_gameserverauth *metrics.Counter + reject_gameserver *metrics.Counter fail_gameserverauth *metrics.Counter fail_storage_error_account *metrics.Counter fail_storage_error_pdata *metrics.Counter @@ -265,6 +266,7 @@ func (h *Handler) m() *apiMetrics { mo.client_authwithserver_requests_total.reject_masterserver_token = mo.set.NewCounter(`atlas_api0_client_authwithserver_requests_total{result="reject_masterserver_token"}`) mo.client_authwithserver_requests_total.reject_password = mo.set.NewCounter(`atlas_api0_client_authwithserver_requests_total{result="reject_password"}`) mo.client_authwithserver_requests_total.reject_gameserverauth = mo.set.NewCounter(`atlas_api0_client_authwithserver_requests_total{result="reject_gameserverauth"}`) + mo.client_authwithserver_requests_total.reject_gameserver = mo.set.NewCounter(`atlas_api0_client_authwithserver_requests_total{result="reject_gameserver"}`) mo.client_authwithserver_requests_total.fail_gameserverauth = mo.set.NewCounter(`atlas_api0_client_authwithserver_requests_total{result="fail_gameserverauth"}`) mo.client_authwithserver_requests_total.fail_storage_error_account = mo.set.NewCounter(`atlas_api0_client_authwithserver_requests_total{result="fail_storage_error_account"}`) mo.client_authwithserver_requests_total.fail_storage_error_pdata = mo.set.NewCounter(`atlas_api0_client_authwithserver_requests_total{result="fail_storage_error_pdata"}`) -- cgit v1.2.3