25
"github.com/gosuda/portal/v2/utils"
26
)
27
28
+type apiError struct {
29
+ code string
30
+ msg string
31
+ status int
32
+}
33
+
34
+func (e *apiError) Error() string { return e.msg }
35
+
36
var (
29
- errFeatureUnavailable = errors.New(types.APIErrorCodeFeatureUnavailable)
30
- errHostnameConflict = errors.New(types.APIErrorCodeHostnameConflict)
31
- errIPBanned = errors.New(types.APIErrorCodeIPBanned)
32
- errLeaseNotFound = errors.New(types.APIErrorCodeLeaseNotFound)
33
- errLeaseRejected = errors.New(types.APIErrorCodeLeaseRejected)
34
- errTransportMismatch = errors.New(types.APIErrorCodeTransportMismatch)
35
- errUnauthorized = errors.New(types.APIErrorCodeUnauthorized)
36
- errUDPDisabled = errors.New(types.APIErrorCodeUDPDisabled)
37
- errUDPCapacityExceeded = errors.New(types.APIErrorCodeUDPCapacityExceeded)
38
- errTCPPortDisabled = errors.New(types.APIErrorCodeTCPPortDisabled)
39
- errTCPPortCapacityExceeded = errors.New(types.APIErrorCodeTCPPortCapacityExceeded)
40
- errTCPPortExhausted = errors.New("no tcp ports available")
37
+ errFeatureUnavailable = &apiError{types.APIErrorCodeFeatureUnavailable, "feature unavailable", http.StatusServiceUnavailable}
38
+ errHostnameConflict = &apiError{types.APIErrorCodeHostnameConflict, "hostname conflict", http.StatusConflict}
39
+ errIPBanned = &apiError{types.APIErrorCodeIPBanned, "request denied because source IP is banned", http.StatusForbidden}
40
+ errLeaseNotFound = &apiError{types.APIErrorCodeLeaseNotFound, "lease not found", http.StatusNotFound}
41
+ errLeaseRejected = &apiError{types.APIErrorCodeLeaseRejected, "lease is not approved for routing", http.StatusForbidden}
42
+ errTransportMismatch = &apiError{types.APIErrorCodeTransportMismatch, "transport mismatch", http.StatusConflict}
43
+ errUnauthorized = &apiError{types.APIErrorCodeUnauthorized, "unauthorized", http.StatusForbidden}
44
+ errUDPDisabled = &apiError{types.APIErrorCodeUDPDisabled, "udp disabled", http.StatusForbidden}
45
+ errUDPCapacityExceeded = &apiError{types.APIErrorCodeUDPCapacityExceeded, "udp capacity exceeded", http.StatusServiceUnavailable}
46
+ errTCPPortDisabled = &apiError{types.APIErrorCodeTCPPortDisabled, "tcp port disabled", http.StatusForbidden}
47
+ errTCPPortCapacityExceeded = &apiError{types.APIErrorCodeTCPPortCapacityExceeded, "tcp port capacity exceeded", http.StatusServiceUnavailable}
48
+ errTCPPortExhausted = &apiError{types.APIErrorCodeTCPPortExhausted, "no tcp ports available", http.StatusServiceUnavailable}
49
)
50
51
+func writeAPIErrorResponse(w http.ResponseWriter, err error) {
52
+ var ae *apiError
53
+ if errors.As(err, &ae) {
54
+ utils.WriteAPIError(w, ae.status, ae.code, ae.msg)
55
+ return
56
+ }
57
+ utils.InvalidRequestError(err).Write(w)
58
+}
59
+
60
func (s *Server) newAPIServer(listener net.Listener, apiMux *http.ServeMux, apiTLS keyless.TLSMaterialConfig) (net.Listener, *http.Server, io.Closer, error) {
61
var keylessSignerHandler http.Handler
62
if len(apiTLS.KeyPEM) > 0 {
141
return "", false
142
}
143
127
-func leaseLookupError(err error) utils.APIErrorResponse {
128
- if errors.Is(err, errLeaseNotFound) {
129
- return utils.APIErrorResponse{
130
- Status: http.StatusNotFound,
131
- Code: types.APIErrorCodeLeaseNotFound,
132
- Message: err.Error(),
133
- }
134
- }
135
- return utils.InvalidRequestError(err)
136
-}
137
-
144
func (s *Server) handleRelayDiscovery(w http.ResponseWriter, r *http.Request) {
145
if !utils.RequireMethod(w, r, http.MethodGet) {
146
return
221
222
resp, err := s.registerLease(challenge.Request, clientIP, req.ReportedIP)
223
if err != nil {
218
- switch {
219
- case errors.Is(err, errFeatureUnavailable):
220
- utils.WriteAPIError(w, http.StatusServiceUnavailable, types.APIErrorCodeFeatureUnavailable, err.Error())
221
- case errors.Is(err, errHostnameConflict):
222
- utils.WriteAPIError(w, http.StatusConflict, types.APIErrorCodeHostnameConflict, err.Error())
223
- case errors.Is(err, errIPBanned):
224
- utils.WriteAPIError(w, http.StatusForbidden, types.APIErrorCodeIPBanned, err.Error())
225
- case errors.Is(err, errTCPPortExhausted):
226
- utils.WriteAPIError(w, http.StatusServiceUnavailable, types.APIErrorCodeTCPPortExhausted, err.Error())
227
- case errors.Is(err, transport.ErrPortExhausted):
224
+ if errors.Is(err, transport.ErrPortExhausted) {
225
utils.WriteAPIError(w, http.StatusServiceUnavailable, types.APIErrorCodeUDPPortExhausted, err.Error())
229
- case errors.Is(err, errUDPDisabled):
230
- utils.WriteAPIError(w, http.StatusForbidden, types.APIErrorCodeUDPDisabled, err.Error())
231
- case errors.Is(err, errUDPCapacityExceeded):
232
- utils.WriteAPIError(w, http.StatusServiceUnavailable, types.APIErrorCodeUDPCapacityExceeded, err.Error())
233
- case errors.Is(err, errTCPPortDisabled):
234
- utils.WriteAPIError(w, http.StatusForbidden, types.APIErrorCodeTCPPortDisabled, err.Error())
235
- case errors.Is(err, errTCPPortCapacityExceeded):
236
- utils.WriteAPIError(w, http.StatusServiceUnavailable, types.APIErrorCodeTCPPortCapacityExceeded, err.Error())
237
- default:
238
- utils.InvalidRequestError(err).Write(w)
226
+ } else {
227
+ writeAPIErrorResponse(w, err)
228
}
229
return
230
}
271
272
resp, err := s.registry.issueRegisterChallenge(req, domain, registerURI)
273
if err != nil {
285
- switch {
286
- case errors.Is(err, errFeatureUnavailable):
287
- utils.WriteAPIError(w, http.StatusServiceUnavailable, types.APIErrorCodeFeatureUnavailable, err.Error())
288
- case errors.Is(err, errUDPDisabled):
289
- utils.WriteAPIError(w, http.StatusForbidden, types.APIErrorCodeUDPDisabled, err.Error())
290
- case errors.Is(err, errUDPCapacityExceeded):
291
- utils.WriteAPIError(w, http.StatusServiceUnavailable, types.APIErrorCodeUDPCapacityExceeded, err.Error())
292
- default:
293
- utils.InvalidRequestError(err).Write(w)
294
- }
274
+ writeAPIErrorResponse(w, err)
275
return
276
}
277
305
}
306
record, err := s.registry.Renew(claims.Identity, ttl, clientIP, utils.SanitizeReportedIP(req.ReportedIP))
307
if err != nil {
328
- leaseLookupError(err).Write(w)
308
+ writeAPIErrorResponse(w, err)
309
return
310
}
311
nextAccessToken, _, err := auth.IssueLeaseAccessToken(s.identity.PrivateKey, s.identity.Address, s.cfg.PortalURL, record.Copy(), ttl)
337
338
record, err := s.registry.Unregister(claims.Identity)
339
if err != nil {
360
- leaseLookupError(err).Write(w)
340
+ writeAPIErrorResponse(w, err)
341
return
342
}
343
deleteCtx, cancel := context.WithTimeout(context.Background(), defaultClaimTimeout)
353
record.Close()
354
}
355
376
- utils.WriteAPIEmpty(w, http.StatusOK)
356
+ utils.WriteAPIData(w, http.StatusOK, map[string]any{})
357
}
358
359
func (s *Server) handleConnect(w http.ResponseWriter, r *http.Request) {
373
374
lease, err := s.admitLeaseByToken(token, false)
375
if err != nil {
396
- switch {
397
- case errors.Is(err, errLeaseNotFound):
398
- utils.WriteAPIError(w, http.StatusNotFound, types.APIErrorCodeLeaseNotFound, err.Error())
399
- case errors.Is(err, errLeaseRejected):
400
- utils.WriteAPIError(w, http.StatusForbidden, types.APIErrorCodeLeaseRejected, "lease is not approved for routing")
401
- case errors.Is(err, errUnauthorized):
402
- utils.WriteAPIError(w, http.StatusForbidden, types.APIErrorCodeUnauthorized, err.Error())
403
- case errors.Is(err, errTransportMismatch):
404
- utils.WriteAPIError(w, http.StatusConflict, types.APIErrorCodeTransportMismatch, "lease does not support stream transport")
405
- default:
406
- utils.InvalidRequestError(err).Write(w)
407
- }
376
+ writeAPIErrorResponse(w, err)
377
return
378
}
379