main
go 186 lines 4.57 KB
Raw
1 package portal
2
3 import (
4 "context"
5 "time"
6
7 "github.com/gosuda/portal-tunnel/v2/portal/acme"
8 "github.com/gosuda/portal-tunnel/v2/portal/auth"
9 "github.com/gosuda/portal-tunnel/v2/portal/transport"
10 "github.com/gosuda/portal-tunnel/v2/types"
11 "github.com/gosuda/portal-tunnel/v2/utils"
12 "github.com/rs/zerolog/log"
13 )
14
15 type leaseRecord struct {
16 types.Identity
17 ExpiresAt time.Time
18 FirstSeenAt time.Time
19 LastSeenAt time.Time
20 ClientIP string
21 ReportedIP string
22 Hostname string
23 HostnameHash string
24 ECHConfigList []byte
25 ECHDNSHostname string
26 Metadata types.LeaseMetadata
27
28 hopToken string
29 hopNextOverlayIPv4 string
30 hopNextToken string
31 registerChallenge *auth.RegisterChallenge
32
33 datagram *transport.RelayDatagram
34 udpPorts *transport.PortAllocator
35 tcpPort *transport.RelayTCPPort
36 tcpPorts *transport.PortAllocator
37 stream *transport.RelayStream
38 }
39
40 func (r *leaseRecord) isPublicEntry() bool {
41 return r != nil && r.hopToken == "" && r.Hostname != ""
42 }
43
44 func (r *leaseRecord) ensGaslessDNSHostname() string {
45 if !r.isPublicEntry() {
46 return ""
47 }
48 if len(r.ECHConfigList) > 0 && r.ECHDNSHostname != "" {
49 return r.ECHDNSHostname
50 }
51 if r.HostnameHash == "" {
52 return r.Hostname
53 }
54 return ""
55 }
56
57 func (r *leaseRecord) hasECHDNSRecord() bool {
58 return r.isPublicEntry() && len(r.ECHConfigList) > 0 && r.ECHDNSHostname != ""
59 }
60
61 func (r *leaseRecord) isHopMiddle() bool {
62 _, _, hasNextHop := r.nextHop()
63 return r != nil && r.Hostname == "" && r.hopToken != "" && hasNextHop
64 }
65
66 func (r *leaseRecord) isHopExit() bool {
67 _, _, hasNextHop := r.nextHop()
68 return r != nil && r.hopToken != "" && !hasNextHop
69 }
70
71 func (r *leaseRecord) routesOverlap(other *leaseRecord) bool {
72 if r == nil || other == nil {
73 return false
74 }
75 if r.Hostname != "" && other.Hostname != "" && r.Hostname == other.Hostname {
76 return true
77 }
78 if r.HostnameHash != "" && other.HostnameHash != "" && r.HostnameHash == other.HostnameHash {
79 return true
80 }
81 if r.Hostname != "" && other.HostnameHash != "" && utils.HostnameHash(r.Hostname) == other.HostnameHash {
82 return true
83 }
84 return other.Hostname != "" && r.HostnameHash != "" && utils.HostnameHash(other.Hostname) == r.HostnameHash
85 }
86
87 func (r *leaseRecord) nextHop() (string, string, bool) {
88 if r == nil {
89 return "", "", false
90 }
91 overlayIPv4 := r.hopNextOverlayIPv4
92 forwardToken := r.hopNextToken
93 return overlayIPv4, forwardToken, overlayIPv4 != "" || forwardToken != ""
94 }
95
96 func (r *leaseRecord) isExpired(now time.Time) bool {
97 return r != nil && !now.IsZero() && !now.Before(r.ExpiresAt)
98 }
99
100 func (r *leaseRecord) Start() error {
101 if r.datagram != nil {
102 if err := r.datagram.Start(context.Background()); err != nil {
103 return err
104 }
105 }
106 if r.tcpPort != nil {
107 return r.tcpPort.Start(context.Background())
108 }
109 return nil
110 }
111
112 func (r *leaseRecord) Close() {
113 if r == nil {
114 return
115 }
116 if r.stream != nil {
117 r.stream.Close()
118 }
119 if r.datagram != nil {
120 port := r.datagram.UDPPort()
121 r.datagram.Close()
122 if port > 0 && r.udpPorts != nil {
123 r.udpPorts.Release(port)
124 }
125 }
126 if r.tcpPort != nil {
127 port := r.tcpPort.TCPPort()
128 r.tcpPort.Close()
129 if port > 0 && r.tcpPorts != nil {
130 r.tcpPorts.Release(port)
131 }
132 }
133 }
134
135 func (r *leaseRecord) syncENSGaslessDNS(ctx context.Context, manager *acme.Manager) error {
136 if r == nil || manager == nil {
137 return nil
138 }
139 if ensHostname := r.ensGaslessDNSHostname(); ensHostname != "" {
140 if err := manager.SyncENSGaslessHostname(ctx, ensHostname, r.Address); err != nil {
141 return err
142 }
143 }
144 return nil
145 }
146
147 func (r *leaseRecord) syncECHDNS(ctx context.Context, manager *acme.Manager, sniPort int) error {
148 if r == nil || manager == nil || !r.hasECHDNSRecord() {
149 return nil
150 }
151 return manager.SyncECHConfig(ctx, r.ECHDNSHostname, r.ECHConfigList, sniPort)
152 }
153
154 func (r *leaseRecord) deleteECHDNS(ctx context.Context, manager *acme.Manager) {
155 if r == nil || manager == nil || !r.hasECHDNSRecord() {
156 return
157 }
158 err := manager.DeleteECHConfig(ctx, r.ECHDNSHostname)
159 if err != nil {
160 log.Warn().
161 Err(err).
162 Str("hostname", r.ECHDNSHostname).
163 Str("route_hostname", r.Hostname).
164 Str("address", r.Address).
165 Msg("delete ech dns record")
166 }
167 }
168
169 func (r *leaseRecord) deleteDNS(ctx context.Context, manager *acme.Manager, includeECH bool) {
170 if r == nil || manager == nil {
171 return
172 }
173 if ensHostname := r.ensGaslessDNSHostname(); ensHostname != "" {
174 err := manager.DeleteENSGaslessHostname(ctx, ensHostname)
175 if err != nil {
176 log.Warn().
177 Err(err).
178 Str("hostname", ensHostname).
179 Str("address", r.Address).
180 Msg("delete ens gasless hostname")
181 }
182 }
183 if includeECH {
184 r.deleteECHDNS(ctx, manager)
185 }
186 }