main
go 138 lines 2.64 KB
Raw
1 package transport
2
3 import (
4 "context"
5 "crypto/tls"
6 "errors"
7 "fmt"
8 "io"
9 "net"
10 "time"
11
12 "github.com/gosuda/portal-tunnel/v2/types"
13 )
14
15 type ClientStream struct {
16 accepted chan net.Conn
17 handshakeTimeout time.Duration
18 }
19
20 func NewClientStream(readyTarget int, handshakeTimeout time.Duration) *ClientStream {
21 return &ClientStream{
22 accepted: make(chan net.Conn, max(readyTarget*2, 1)),
23 handshakeTimeout: handshakeTimeout,
24 }
25 }
26
27 func (s *ClientStream) Accept(done <-chan struct{}) (net.Conn, error) {
28 if s == nil {
29 return nil, net.ErrClosed
30 }
31 select {
32 case <-done:
33 return nil, net.ErrClosed
34 case conn := <-s.accepted:
35 if conn == nil {
36 return nil, net.ErrClosed
37 }
38 return conn, nil
39 }
40 }
41
42 func (s *ClientStream) RunSession(
43 ctx context.Context,
44 conn net.Conn,
45 tlsConfig *tls.Config,
46 ) (bool, error) {
47 if s == nil {
48 return false, net.ErrClosed
49 }
50 return s.runSession(ctx, conn, tlsConfig)
51 }
52
53 func (s *ClientStream) Drain() {
54 if s == nil {
55 return
56 }
57 for {
58 select {
59 case conn := <-s.accepted:
60 if conn != nil {
61 _ = conn.Close()
62 }
63 default:
64 return
65 }
66 }
67 }
68
69 func (s *ClientStream) runSession(
70 ctx context.Context,
71 conn net.Conn,
72 tlsConfig *tls.Config,
73 ) (bool, error) {
74 if conn == nil {
75 return false, net.ErrClosed
76 }
77
78 var marker [1]byte
79 for {
80 _ = conn.SetReadDeadline(time.Now().Add(2 * s.handshakeTimeout))
81 if _, err := io.ReadFull(conn, marker[:]); err != nil {
82 _ = conn.Close()
83 return false, err
84 }
85 _ = conn.SetReadDeadline(time.Time{})
86
87 switch marker[0] {
88 case types.MarkerKeepalive:
89 continue
90 case types.MarkerTLSStart:
91 if err := s.activate(ctx, conn, tlsConfig); err != nil {
92 _ = conn.Close()
93 return true, err
94 }
95 return true, nil
96 case types.MarkerRawStart:
97 if err := s.activateRaw(ctx, conn); err != nil {
98 _ = conn.Close()
99 return true, err
100 }
101 return true, nil
102 default:
103 _ = conn.Close()
104 return false, fmt.Errorf("unexpected reverse marker: 0x%02x", marker[0])
105 }
106 }
107 }
108
109 func (s *ClientStream) activate(ctx context.Context, conn net.Conn, tlsConfig *tls.Config) error {
110 if tlsConfig == nil {
111 return errors.New("tls config is unavailable")
112 }
113
114 tlsConn := tls.Server(conn, tlsConfig)
115 handshakeCtx, cancel := context.WithTimeout(ctx, s.handshakeTimeout)
116 defer cancel()
117 if err := tlsConn.HandshakeContext(handshakeCtx); err != nil {
118 return err
119 }
120
121 select {
122 case <-ctx.Done():
123 _ = tlsConn.Close()
124 return ctx.Err()
125 case s.accepted <- tlsConn:
126 return nil
127 }
128 }
129
130 func (s *ClientStream) activateRaw(ctx context.Context, conn net.Conn) error {
131 select {
132 case <-ctx.Done():
133 _ = conn.Close()
134 return ctx.Err()
135 case s.accepted <- conn:
136 return nil
137 }
138 }