main
go 132 lines 2.51 KB
Raw
1 package transport
2
3 import (
4 "context"
5 "errors"
6 "net"
7 "sync"
8 "time"
9
10 "github.com/rs/zerolog/log"
11 )
12
13 const defaultTCPPortClaimTimeout = 10 * time.Second
14
15 // RelayTCPPort owns a TCP listener on an allocated port for one lease.
16 type RelayTCPPort struct {
17 identityKey string
18 port int
19 listener net.Listener
20 stream *RelayStream
21 bridge func(net.Conn, net.Conn)
22
23 cancel context.CancelFunc
24 closeOnce sync.Once
25 }
26
27 func NewRelayTCPPort(identityKey string, port int, stream *RelayStream, bridge func(net.Conn, net.Conn)) *RelayTCPPort {
28 return &RelayTCPPort{
29 identityKey: identityKey,
30 port: port,
31 stream: stream,
32 bridge: bridge,
33 }
34 }
35
36 func (t *RelayTCPPort) Start(ctx context.Context) error {
37 if t == nil || t.port <= 0 {
38 return nil
39 }
40
41 addr := &net.TCPAddr{Port: t.port}
42 listener, err := net.ListenTCP("tcp", addr)
43 if err != nil {
44 return err
45 }
46 t.listener = listener
47
48 relayCtx, cancel := context.WithCancel(ctx)
49 t.cancel = cancel
50 go t.acceptLoop(relayCtx)
51
52 log.Info().
53 Str("component", "tcp-port-relay").
54 Str("identity_key", t.identityKey).
55 Int("port", t.port).
56 Msg("tcp port relay started")
57
58 return nil
59 }
60
61 func (t *RelayTCPPort) Close() {
62 if t == nil {
63 return
64 }
65
66 t.closeOnce.Do(func() {
67 if t.cancel != nil {
68 t.cancel()
69 }
70 if t.listener != nil {
71 _ = t.listener.Close()
72 }
73 log.Info().
74 Str("component", "tcp-port-relay").
75 Str("identity_key", t.identityKey).
76 Int("port", t.port).
77 Msg("tcp port relay stopped")
78 })
79 }
80
81 func (t *RelayTCPPort) TCPPort() int {
82 if t == nil {
83 return 0
84 }
85 return t.port
86 }
87
88 func (t *RelayTCPPort) acceptLoop(ctx context.Context) {
89 for {
90 conn, err := t.listener.Accept()
91 if err != nil {
92 if ctx.Err() != nil {
93 return
94 }
95 var netErr net.Error
96 if errors.As(err, &netErr) && netErr.Timeout() {
97 continue
98 }
99 log.Warn().
100 Str("component", "tcp-port-relay").
101 Str("identity_key", t.identityKey).
102 Err(err).
103 Msg("accept loop exiting")
104 return
105 }
106
107 go t.handleConn(ctx, conn)
108 }
109 }
110
111 func (t *RelayTCPPort) handleConn(ctx context.Context, conn net.Conn) {
112 claimCtx, cancel := context.WithTimeout(ctx, defaultTCPPortClaimTimeout)
113 defer cancel()
114
115 session, err := t.stream.claimRaw(claimCtx)
116 if err != nil {
117 _ = conn.Close()
118 log.Warn().
119 Str("component", "tcp-port-relay").
120 Str("identity_key", t.identityKey).
121 Err(err).
122 Msg("failed to claim reverse session for tcp port connection")
123 return
124 }
125
126 if t.bridge == nil {
127 _ = conn.Close()
128 _ = session.Close()
129 return
130 }
131 t.bridge(conn, session)
132 }