refactor: update Go dependencies and refactor relay client structures
- Updated golang.org/x/crypto from v0.41.0 to v0.43.0 - Updated google.golang.org/protobuf from v1.36.6 to v1.36.10 - Updated golang.org/x/sys to v0.37.0 and cleaned up indirect requires - Renamed IncommingConn.id to leaseID and added LocalID/RemoteID methods - Removed context parameter from GetRelayInfo and changed RequestConnection return type - Added localID/remoteID fields to SecureConnection and updated randpool import
lemon-mint committed
Oct 27, 2025 at 15:55 UTC
2e45701e9dcab8d2a79c6143ca75f889d8309745
7 files changed
+234
-37
go.mod
+3
-6
@@ -7,11 +7,8 @@ require (
7
github.com/hashicorp/yamux v0.1.2
8
github.com/planetscale/vtprotobuf v0.6.0
9
github.com/valyala/bytebufferpool v1.0.0
10
- golang.org/x/crypto v0.41.0
11
- google.golang.org/protobuf v1.36.6
10
+ golang.org/x/crypto v0.43.0
11
+ google.golang.org/protobuf v1.36.10
12
)
13
14
-require (
15
- github.com/google/go-cmp v0.7.0 // indirect
16
- golang.org/x/sys v0.35.0 // indirect
17
-)
14
+require golang.org/x/sys v0.37.0 // indirect
go.sum
+6
-6
@@ -8,9 +8,9 @@ github.com/planetscale/vtprotobuf v0.6.0 h1:nBeETjudeJ5ZgBHUz1fVHvbqUKnYOXNhsIEa
8
github.com/planetscale/vtprotobuf v0.6.0/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
9
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
10
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
11
-golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
12
-golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
13
-golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
14
-golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
15
-google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
16
-google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
11
+golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
12
+golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
13
+golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
14
+golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
15
+google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
16
+google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
relaydns/client.go
+18
-11
@@ -1,7 +1,6 @@
1
package relaydns
2
3
import (
4
- "context"
4
"crypto/rand"
5
"errors"
6
"io"
@@ -21,11 +20,19 @@ var (
20
21
type IncommingConn struct {
22
*cryptoops.SecureConnection
24
- id string
23
+ leaseID string
24
}
25
27
-func (i *IncommingConn) ID() string {
28
- return i.id
26
+func (i *IncommingConn) LeaseID() string {
27
+ return i.leaseID
28
+}
29
+
30
+func (i *IncommingConn) LocalID() string {
31
+ return i.LocalID()
32
+}
33
+
34
+func (i *IncommingConn) RemoteID() string {
35
+ return i.RemoteID()
36
}
37
38
// RelayClient는 RelayServer에 연결하여 서비스를 요청하는 클라이언트입니다.
@@ -176,12 +183,12 @@ func (g *RelayClient) handleConnectionRequestStream(stream *yamux.Stream) {
183
184
g.incommingConnCh <- &IncommingConn{
185
SecureConnection: secConn,
179
- id: req.LeaseId,
186
+ leaseID: req.LeaseId,
187
}
188
}
189
190
// GetRelayInfo는 서버의 릴레이 정보를 요청합니다.
184
-func (g *RelayClient) GetRelayInfo(ctx context.Context) (*rdverb.RelayInfo, error) {
191
+func (g *RelayClient) GetRelayInfo() (*rdverb.RelayInfo, error) {
192
// 새 스트림 열기
193
stream, err := g.sess.OpenStream()
194
if err != nil {
@@ -357,7 +364,7 @@ func (g *RelayClient) deleteLease(cred *cryptoops.Credential, identity *rdsec.Id
364
}
365
366
// requestConnection은 다른 클라이언트로의 연결을 요청합니다.
360
-func (g *RelayClient) RequestConnection(leaseID string, alpn string, clientCred *cryptoops.Credential) (rdverb.ResponseCode, io.ReadWriteCloser, error) {
367
+func (g *RelayClient) RequestConnection(leaseID string, alpn string, clientCred *cryptoops.Credential) (rdverb.ResponseCode, *cryptoops.SecureConnection, error) {
368
// 새 스트림 열기
369
stream, err := g.sess.OpenStream()
370
if err != nil {
@@ -463,15 +470,15 @@ func (g *RelayClient) DeregisterLease(cred *cryptoops.Credential) error {
470
PublicKey: cred.PublicKey(),
471
}
472
473
+ g.leasesMu.Lock()
474
+ delete(g.leases, identity.Id)
475
+ g.leasesMu.Unlock()
476
+
477
resp, err := g.deleteLease(cred, identity)
478
if err != nil || resp != rdverb.ResponseCode_RESPONSE_CODE_ACCEPTED {
479
return err
480
}
481
471
- g.leasesMu.Lock()
472
- delete(g.leases, identity.Id)
473
- g.leasesMu.Unlock()
474
-
482
return nil
483
}
484
relaydns/core/cryptoops/handshaker.go
+19
-5
@@ -19,7 +19,7 @@ import (
19
"google.golang.org/protobuf/proto"
20
21
"github.com/gosuda/relaydns/relaydns/core/proto/rdsec"
22
- "github.com/gosuda/relaydns/relaydns/internal/randpool"
22
+ "github.com/gosuda/relaydns/relaydns/utils/randpool"
23
"github.com/valyala/bytebufferpool"
24
)
25
@@ -100,13 +100,25 @@ func NewHandshaker(credential *Credential) *Handshaker {
100
101
// SecureConnection represents a secured connection with encryption capabilities
102
type SecureConnection struct {
103
- conn io.ReadWriteCloser
103
+ conn io.ReadWriteCloser
104
+
105
+ localID string
106
+ remoteID string
107
+
108
encryptor cipher.AEAD
109
decryptor cipher.AEAD
110
111
readBuffer *bytebufferpool.ByteBuffer
112
}
113
114
+func (sc *SecureConnection) LocalID() string {
115
+ return sc.localID
116
+}
117
+
118
+func (sc *SecureConnection) RemoteID() string {
119
+ return sc.remoteID
120
+}
121
+
122
// Write encrypts and writes data to the underlying connection
123
func (sc *SecureConnection) Write(p []byte) (int, error) {
124
const fragSize = maxRawPacketSize / 2
@@ -301,7 +313,7 @@ func (h *Handshaker) ClientHandshake(conn io.ReadWriteCloser, alpn string) (*Sec
313
wipeMemory(ephemeralPriv)
314
315
// Create secure connection
304
- return h.createSecureConnection(conn, clientEncryptKey, clientDecryptKey)
316
+ return h.createSecureConnection(conn, clientEncryptKey, clientDecryptKey, serverInitPayload.GetIdentity().GetId())
317
}
318
319
// ServerHandshake performs the server-side of the handshake
@@ -391,7 +403,7 @@ func (h *Handshaker) ServerHandshake(conn io.ReadWriteCloser, alpns []string) (*
403
}
404
405
// Create secure connection
394
- return h.createSecureConnection(conn, serverEncryptKey, serverDecryptKey)
406
+ return h.createSecureConnection(conn, serverEncryptKey, serverDecryptKey, clientInitPayload.GetIdentity().GetId())
407
}
408
409
// validateClientInit validates the client init message
@@ -498,7 +510,7 @@ func (h *Handshaker) deriveServerSessionKeys(serverPriv, clientPub, clientNonce,
510
}
511
512
// createSecureConnection creates a new SecureConnection with the given keys and nonces
501
-func (h *Handshaker) createSecureConnection(conn io.ReadWriteCloser, encryptKey, decryptKey []byte) (*SecureConnection, error) {
513
+func (h *Handshaker) createSecureConnection(conn io.ReadWriteCloser, encryptKey, decryptKey []byte, remoteID string) (*SecureConnection, error) {
514
// Create AEAD instances
515
encryptor, err := chacha20poly1305.New(encryptKey)
516
if err != nil {
@@ -515,6 +527,8 @@ func (h *Handshaker) createSecureConnection(conn io.ReadWriteCloser, encryptKey,
527
528
secureConn := &SecureConnection{
529
conn: conn,
530
+ localID: h.credential.id,
531
+ remoteID: remoteID,
532
encryptor: encryptor,
533
decryptor: decryptor,
534
readBuffer: readBuffer,
relaydns/utils/randpool/randpool.go
renamed
relaydns/utils/wsstream/wsstream.go
renamed
+9
-9
@@ -1,4 +1,4 @@
1
-package relaydns
1
+package wsstream
2
3
import (
4
"io"
@@ -6,14 +6,14 @@ import (
6
"github.com/gorilla/websocket"
7
)
8
9
-type wsStream struct {
10
- c *websocket.Conn
9
+type WsStream struct {
10
+ Conn *websocket.Conn
11
currentReader io.Reader
12
}
13
14
-func (g *wsStream) Read(p []byte) (n int, err error) {
14
+func (g *WsStream) Read(p []byte) (n int, err error) {
15
if g.currentReader == nil {
16
- _, reader, err := g.c.NextReader()
16
+ _, reader, err := g.Conn.NextReader()
17
if err != nil {
18
return 0, err
19
}
@@ -29,14 +29,14 @@ func (g *wsStream) Read(p []byte) (n int, err error) {
29
return n, err
30
}
31
32
-func (g *wsStream) Write(p []byte) (n int, err error) {
33
- err = g.c.WriteMessage(websocket.BinaryMessage, p)
32
+func (g *WsStream) Write(p []byte) (n int, err error) {
33
+ err = g.Conn.WriteMessage(websocket.BinaryMessage, p)
34
if err != nil {
35
return 0, err
36
}
37
return len(p), nil
38
}
39
40
-func (g *wsStream) Close() error {
41
- return g.c.Close()
40
+func (g *WsStream) Close() error {
41
+ return g.Conn.Close()
42
}
sdk/sdk.go
new
+179
@@ -0,0 +1,179 @@
1
+package sdk
2
+
3
+import (
4
+ "context"
5
+ "errors"
6
+ "io"
7
+ "slices"
8
+ "sync"
9
+
10
+ "github.com/gorilla/websocket"
11
+ "github.com/gosuda/relaydns/relaydns"
12
+ "github.com/gosuda/relaydns/relaydns/core/cryptoops"
13
+ "github.com/gosuda/relaydns/relaydns/core/proto/rdverb"
14
+ "github.com/gosuda/relaydns/relaydns/utils/wsstream"
15
+)
16
+
17
+func NewCredential() (*cryptoops.Credential, error) {
18
+ return cryptoops.NewCredential()
19
+}
20
+
21
+func webSocketDialer() func(context.Context, string) (io.ReadWriteCloser, error) {
22
+ return func(ctx context.Context, url string) (io.ReadWriteCloser, error) {
23
+ wsConn, _, err := websocket.DefaultDialer.Dial(url, nil)
24
+ if err != nil {
25
+ return nil, err
26
+ }
27
+ return &wsstream.WsStream{Conn: wsConn}, nil
28
+ }
29
+}
30
+
31
+type RDClientConfig struct {
32
+ BootstrapServers []string
33
+ Dialer func(context.Context, string) (io.ReadWriteCloser, error)
34
+}
35
+
36
+type Option func(*RDClientConfig)
37
+
38
+type rdRelay struct {
39
+ addr string
40
+ client *relaydns.RelayClient
41
+ stop chan struct{}
42
+}
43
+
44
+type RDConnection struct {
45
+ via *rdRelay
46
+ localAddr string
47
+ remoteAddr string
48
+ conn io.ReadWriteCloser
49
+}
50
+
51
+type RDListener struct {
52
+ mu sync.Mutex
53
+
54
+ cred *cryptoops.Credential
55
+ conns map[*RDConnection]struct{}
56
+
57
+ connCh chan *RDConnection
58
+}
59
+
60
+type RDClient struct {
61
+ mu sync.Mutex
62
+
63
+ relays map[string]*rdRelay
64
+ listeners map[string]*RDListener
65
+
66
+ stopch chan struct{}
67
+}
68
+
69
+var (
70
+ ErrNoAvailableRelay = errors.New("no available relay")
71
+)
72
+
73
+func NewClient(opt ...Option) (*RDClient, error) {
74
+ return &RDClient{}, nil
75
+}
76
+
77
+func (g *RDClient) Dial(cred *cryptoops.Credential, leaseID string, alpn string) (*RDConnection, error) {
78
+ var relays []*rdRelay
79
+
80
+ g.mu.Lock()
81
+ for _, server := range g.relays {
82
+ relays = append(relays, server)
83
+ }
84
+ g.mu.Unlock()
85
+
86
+ var wg sync.WaitGroup
87
+ var availableRelaysMu sync.Mutex
88
+ var availableRelays []*rdRelay
89
+
90
+ for _, relay := range relays {
91
+ wg.Add(1)
92
+ go func(relay *rdRelay) {
93
+ defer wg.Done()
94
+ info, err := relay.client.GetRelayInfo()
95
+ if err != nil {
96
+ return
97
+ }
98
+
99
+ if slices.Contains(info.Leases, leaseID) {
100
+ availableRelaysMu.Lock()
101
+ availableRelays = append(availableRelays, relay)
102
+ availableRelaysMu.Unlock()
103
+ }
104
+ }(relay)
105
+ }
106
+ wg.Wait()
107
+
108
+ if len(availableRelays) == 0 {
109
+ return nil, ErrNoAvailableRelay
110
+ }
111
+
112
+ for _, relay := range availableRelays {
113
+ code, conn, err := relay.client.RequestConnection(leaseID, alpn, cred)
114
+ if err != nil || code != rdverb.ResponseCode_RESPONSE_CODE_ACCEPTED {
115
+ continue
116
+ }
117
+ return &RDConnection{via: relay, conn: conn, localAddr: conn.LocalID(), remoteAddr: conn.RemoteID()}, nil
118
+ }
119
+
120
+ return nil, ErrNoAvailableRelay
121
+}
122
+
123
+func (g *RDClient) Listen(cred *cryptoops.Credential, name string, alpns []string) (*RDListener, error) {
124
+
125
+}
126
+
127
+func (g *RDClient) listenerWorker(server *rdRelay) {
128
+ for {
129
+ select {
130
+ case <-server.stop:
131
+ return
132
+ case conn := <-server.client.IncommingConnection():
133
+ lease := conn.LeaseID()
134
+
135
+ g.mu.Lock()
136
+ listener, ok := g.listeners[lease]
137
+ g.mu.Unlock()
138
+
139
+ if !ok {
140
+ continue
141
+ }
142
+
143
+ rdConn := &RDConnection{via: server, conn: conn, localAddr: conn.LocalID(), remoteAddr: conn.RemoteID()}
144
+
145
+ listener.mu.Lock()
146
+ listener.conns[rdConn] = struct{}{}
147
+ listener.mu.Unlock()
148
+
149
+ listener.connCh <- rdConn
150
+ }
151
+ }
152
+}
153
+
154
+func (g *RDClient) Close() error {
155
+ var errs []error
156
+
157
+ close(g.stopch)
158
+
159
+ g.mu.Lock()
160
+ for _, listener := range g.listeners {
161
+ if err := listener.Close(); err != nil {
162
+ errs = append(errs, err)
163
+ }
164
+ }
165
+ g.mu.Unlock()
166
+
167
+ g.mu.Lock()
168
+ for _, server := range g.relays {
169
+ if err := server.client.Close(); err != nil {
170
+ errs = append(errs, err)
171
+ }
172
+ }
173
+ g.mu.Unlock()
174
+
175
+ if len(errs) > 0 {
176
+ return errs[0]
177
+ }
178
+ return nil
179
+}