Skip to content

Commit ed9f8a4

Browse files
committed
chore: move ip utils
1 parent 78de598 commit ed9f8a4

6 files changed

Lines changed: 138 additions & 126 deletions

File tree

portal/api_server.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -432,17 +432,6 @@ func (s *Server) handleQUICTunnelConn(conn *quic.Conn) {
432432
Msg("quic tunnel connected")
433433
}
434434

435-
func sanitizeReportedIP(raw string) string {
436-
candidate := strings.TrimSpace(raw)
437-
if candidate == "" {
438-
return ""
439-
}
440-
if net.ParseIP(candidate) == nil {
441-
return ""
442-
}
443-
return candidate
444-
}
445-
446435
func (s *Server) registerLease(req types.RegisterRequest, clientIP string) (types.RegisterResponse, error) {
447436
name, err := utils.NormalizeDNSLabel(req.Name)
448437
if err != nil {
@@ -503,7 +492,7 @@ func (s *Server) registerLease(req types.RegisterRequest, clientIP string) (type
503492
FirstSeenAt: now,
504493
LastSeenAt: now,
505494
ClientIP: clientIP,
506-
ReportedIP: sanitizeReportedIP(req.ReportedIP),
495+
ReportedIP: utils.SanitizeReportedIP(req.ReportedIP),
507496
UDPEnabled: req.UDPEnabled,
508497
},
509498
ReverseToken: req.ReverseToken,
@@ -580,7 +569,7 @@ func (s *Server) renewLease(req types.RenewRequest, clientIP string) (types.Rene
580569
if req.TTL > 0 {
581570
ttl = time.Duration(req.TTL) * time.Second
582571
}
583-
record, err := s.registry.Renew(req.LeaseID, req.ReverseToken, ttl, clientIP, sanitizeReportedIP(req.ReportedIP))
572+
record, err := s.registry.Renew(req.LeaseID, req.ReverseToken, ttl, clientIP, utils.SanitizeReportedIP(req.ReportedIP))
584573
if err != nil {
585574
return types.RenewResponse{}, err
586575
}

sdk/api_client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func (a *apiClient) ensureReady(ctx context.Context) error {
147147
a.rawTLSConfig = rawTLSConfig
148148

149149
if a.resolvedPublicIP == "" {
150-
a.resolvedPublicIP = resolvePublicIP(ctx)
150+
a.resolvedPublicIP = utils.ResolvePublicIP(ctx)
151151
}
152152

153153
return nil

sdk/public_ip.go

Lines changed: 0 additions & 60 deletions
This file was deleted.

utils/network.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package utils
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"io"
7+
"net"
8+
"net/http"
9+
"strings"
10+
"time"
11+
12+
"github.com/gosuda/portal/v2/types"
13+
)
14+
15+
// ResolvePublicIP attempts to determine the caller's public IP address
16+
// using well-known external services. Returns empty string on failure.
17+
// Best-effort with a short timeout to avoid blocking registration.
18+
func ResolvePublicIP(ctx context.Context) string {
19+
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
20+
defer cancel()
21+
22+
endpoints := []string{
23+
"https://api.ipify.org",
24+
"https://ifconfig.me/ip",
25+
}
26+
client := &http.Client{Timeout: 3 * time.Second}
27+
28+
for _, endpoint := range endpoints {
29+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
30+
if err != nil {
31+
continue
32+
}
33+
req.Header.Set("User-Agent", "portal-tunnel")
34+
35+
resp, err := client.Do(req)
36+
if err != nil {
37+
continue
38+
}
39+
40+
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 256))
41+
_ = resp.Body.Close()
42+
if resp.StatusCode != http.StatusOK || readErr != nil {
43+
continue
44+
}
45+
46+
if candidate := SanitizeReportedIP(string(body)); candidate != "" {
47+
return candidate
48+
}
49+
}
50+
51+
return ""
52+
}
53+
54+
func SanitizeReportedIP(raw string) string {
55+
candidate := strings.TrimSpace(raw)
56+
if candidate == "" {
57+
return ""
58+
}
59+
if net.ParseIP(candidate) == nil {
60+
return ""
61+
}
62+
return candidate
63+
}
64+
65+
func ResolvePortalRelayURLs(ctx context.Context, explicit []string, includeDefaults bool) ([]string, error) {
66+
explicit, err := NormalizeRelayURLs(explicit...)
67+
if err != nil {
68+
return nil, err
69+
}
70+
if !includeDefaults {
71+
return explicit, nil
72+
}
73+
74+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, types.PortalRelayRegistryURL, nil)
75+
if err != nil {
76+
return explicit, nil
77+
}
78+
79+
client := &http.Client{Timeout: 5 * time.Second}
80+
resp, err := client.Do(req)
81+
if err != nil {
82+
return explicit, nil
83+
}
84+
defer resp.Body.Close()
85+
86+
if resp.StatusCode != http.StatusOK {
87+
return explicit, nil
88+
}
89+
90+
var registry struct {
91+
Relays []string `json:"relays"`
92+
}
93+
if err := json.NewDecoder(resp.Body).Decode(&registry); err != nil {
94+
return explicit, nil
95+
}
96+
97+
defaults, err := NormalizeRelayURLs(registry.Relays...)
98+
if err != nil {
99+
return explicit, nil
100+
}
101+
if len(defaults) == 0 {
102+
return explicit, nil
103+
}
104+
return MergeRelayURLs(defaults, nil, explicit)
105+
}

utils/network_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package utils
2+
3+
import "testing"
4+
5+
func TestSanitizeReportedIP(t *testing.T) {
6+
t.Parallel()
7+
8+
tests := []struct {
9+
name string
10+
raw string
11+
want string
12+
}{
13+
{name: "empty", raw: "", want: ""},
14+
{name: "whitespace", raw: " ", want: ""},
15+
{name: "ipv4", raw: " 203.0.113.10 ", want: "203.0.113.10"},
16+
{name: "ipv6", raw: " 2001:db8::1 ", want: "2001:db8::1"},
17+
{name: "invalid", raw: "not-an-ip", want: ""},
18+
{name: "host port", raw: "203.0.113.10:443", want: ""},
19+
}
20+
21+
for _, tc := range tests {
22+
t.Run(tc.name, func(t *testing.T) {
23+
t.Parallel()
24+
25+
if got := SanitizeReportedIP(tc.raw); got != tc.want {
26+
t.Fatalf("SanitizeReportedIP(%q) = %q, want %q", tc.raw, got, tc.want)
27+
}
28+
})
29+
}
30+
}

utils/registry.go

Lines changed: 0 additions & 52 deletions
This file was deleted.

0 commit comments

Comments
 (0)