-
Notifications
You must be signed in to change notification settings - Fork 0
/
qsock.go
385 lines (346 loc) · 9.22 KB
/
qsock.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
package qsocket
import (
"bytes"
"crypto/sha256"
"crypto/tls"
"encoding/hex"
"errors"
"fmt"
"net"
"time"
stream "github.com/qsocket/encrypted-stream"
"golang.org/x/net/proxy"
)
const (
// Tag ID for representing server mode connections.
Server SocketType = iota // 00000000 => Server
// Tag ID for representing client mode connections.
Client
// =====================================================================
)
var (
Version = "?"
ErrUntrustedCert = errors.New("Certificate fingerprint mismatch!")
ErrUninitializedSocket = errors.New("Socket not initiated,")
ErrQSocketSessionEnd = errors.New("QSocket session has ended.")
ErrUnexpectedSocket = errors.New("Unexpected socket type.")
ErrInvalidIdTag = errors.New("Invalid peer ID tag.")
ErrNoTlsConnection = errors.New("TLS socket is nil.")
ErrSocketNotConnected = errors.New("Socket is not connected.")
ErrSrpFailed = errors.New("SRP auth failed.")
ErrSocketInUse = errors.New("Socket already dialed.")
ErrInvalidCertFingerprint = errors.New("Invalid TLS certificate fingerprint.")
//
TOR_MODE = false
)
type SocketType byte
// A QSocket structure contains required values
// for performing a knock sequence with the QSRN gate.
//
// `Secret` value can be considered as the password for the QSocket connection,
// It will be used for generating a 128bit unique identifier (UID) for the connection.
//
// `*tag` values are used internally for QoS purposes.
// It specifies the operating system, architecture and the type of connection initiated by the peers,
// the relay server uses these values for optimizing the connection performance.
type QSocket struct {
secret string
certHash []byte
e2e bool
socketType SocketType
conn net.Conn
tlsConn *tls.Conn
encConn *stream.EncryptedStream
proxyDialer proxy.Dialer
}
// NewSocket creates a new QSocket structure with the given secret.
// `certVerify` value is used for enabling the certificate validation on TLS connections
func NewSocket(sType SocketType, secret string) *QSocket {
switch sType {
case Client, Server:
default:
panic("Invalid socket type!")
}
return &QSocket{
secret: secret,
socketType: sType,
e2e: true,
conn: nil,
tlsConn: nil,
encConn: nil,
proxyDialer: nil,
}
}
// AddIdTag adds a peer identification tag to the QSocket.
func (qs *QSocket) SetE2E(v bool) error {
if !qs.IsClosed() {
return ErrSocketInUse
}
qs.e2e = v
return nil
}
// AddIdTag adds a peer identification tag to the QSocket.
func (qs *QSocket) SetCertFingerprint(h string) error {
if !qs.IsClosed() {
return ErrSocketInUse
}
hash, err := hex.DecodeString(h)
if err != nil {
return err
}
if len(hash) != 32 {
return ErrInvalidCertFingerprint
}
qs.certHash = hash
return nil
}
// AddIdTag adds a peer identification tag to the QSocket.
func (qs *QSocket) SetProxy(proxyAddr string) error {
if !qs.IsClosed() {
return ErrSocketInUse
}
if proxyAddr == "127.0.0.1:9050" {
TOR_MODE = true
}
dialer, err := proxy.SOCKS5("tcp", proxyAddr, nil,
&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 10 * time.Second,
},
)
if err != nil {
return err
}
qs.proxyDialer = dialer
return nil
}
// Dial creates a TLS connection to the `QSRN_GATE` on `QSRN_GATE_TLS_PORT`.
// Based on the `VerifyCert` parameter, certificate fingerprint validation (a.k.a. SSL pinning)
// will be performed after establishing the TLS connection.
func (qs *QSocket) Dial(useTls bool) error {
port := QSRN_GATE_PORT
if useTls {
port = QSRN_GATE_TLS_PORT
}
if qs.proxyDialer != nil {
gate := QSRN_GATE
if TOR_MODE {
gate = QSRN_TOR_GATE
}
pConn, err := qs.proxyDialer.Dial("tcp", fmt.Sprintf("%s:%d", gate, port))
if err != nil {
return err
}
qs.conn = pConn
} else {
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", QSRN_GATE, port))
if err != nil {
return err
}
qs.conn = conn
}
if useTls {
qs.tlsConn = tls.Client(
qs.conn,
&tls.Config{
InsecureSkipVerify: true,
ServerName: QSRN_GATE,
},
)
err := qs.VerifyTlsCertificate()
if err != nil {
return err
}
}
return qs.InitiateKnockSequence()
}
func (qs *QSocket) VerifyTlsCertificate() error {
if qs.IsClosed() {
return ErrSocketNotConnected
}
if qs.tlsConn == nil {
return ErrNoTlsConnection
}
if qs.certHash == nil {
return nil
}
connState := qs.tlsConn.ConnectionState()
for _, peerCert := range connState.PeerCertificates {
hash := sha256.Sum256(peerCert.Raw)
if !bytes.Equal(hash[0:], qs.certHash) {
return ErrUntrustedCert
}
}
return nil
}
// IsClient checks if the QSocket connection is initiated as a client or a server.
func (qs *QSocket) IsClient() bool {
return qs.socketType == Client
}
// IsClient checks if the QSocket connection is initiated as a client or a server.
func (qs *QSocket) IsServer() bool {
return !qs.IsClient()
}
// IsClosed checks if the QSocket connection to the `QSRN_GATE` is ended.
func (qs *QSocket) IsClosed() bool {
return qs.conn == nil && qs.tlsConn == nil && qs.encConn == nil
}
// IsTLS checks if the underlying connection is TLS or not.
func (qs *QSocket) IsTLS() bool {
return qs.tlsConn != nil
}
// IsE2E checks if the underlying connection is E2E encrypted or not.
func (qs *QSocket) IsE2E() bool {
return qs.encConn != nil && qs.e2e
}
// SetReadDeadline sets the read deadline on the underlying connection.
// A zero value for t means Read will not time out.
func (qs *QSocket) SetReadDeadline(t time.Time) error {
if qs.IsTLS() {
return qs.tlsConn.SetReadDeadline(t)
}
if qs.conn != nil {
return qs.conn.SetReadDeadline(t)
}
return nil
}
// SetWriteDeadline sets the write deadline on the underlying connection.
// A zero value for t means Write will not time out.
// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
// Even if write times out, it may return n > 0, indicating that some of the data was successfully written. A zero value for t means Write will not time out.
func (qs *QSocket) SetWriteDeadline(t time.Time) error {
if qs.IsTLS() {
return qs.tlsConn.SetWriteDeadline(t)
}
if qs.conn != nil {
return qs.conn.SetWriteDeadline(t)
}
return nil
}
// RemoteAddr returns the remote network address.
func (qs *QSocket) RemoteAddr() net.Addr {
if qs.IsTLS() {
return qs.tlsConn.RemoteAddr()
}
if qs.conn != nil {
return qs.conn.RemoteAddr()
}
return nil
}
// LocalAddr returns the local network address.
func (qs *QSocket) LocalAddr() net.Addr {
if qs.IsTLS() {
return qs.tlsConn.LocalAddr()
}
if qs.conn != nil {
return qs.conn.LocalAddr()
}
return nil
}
// Read reads data from the connection.
//
// As Read calls Handshake, in order to prevent indefinite blocking a deadline must be set for both Read and Write before Read is called when the handshake has not yet completed.
// See SetDeadline, SetReadDeadline, and SetWriteDeadline.
func (qs *QSocket) Read(b []byte) (int, error) {
if qs.IsE2E() {
return qs.encConn.Read(b)
}
if qs.IsTLS() {
return qs.tlsConn.Read(b)
}
if qs.conn != nil {
return qs.conn.Read(b)
}
return 0, ErrUninitializedSocket
}
// Write writes data to the connection.
//
// As Write calls Handshake, in order to prevent indefinite blocking a deadline must be set for both Read and Write before Write is called when the handshake has not yet completed.
// See SetDeadline, SetReadDeadline, and SetWriteDeadline.
func (qs *QSocket) Write(b []byte) (int, error) {
if qs.IsE2E() {
return qs.encConn.Write(b)
}
if qs.tlsConn != nil {
return qs.tlsConn.Write(b)
}
if qs.conn != nil {
return qs.conn.Write(b)
}
return 0, ErrUninitializedSocket
}
// Close closes the QSocket connection and underlying TCP/TLS connections.
func (qs *QSocket) Close() {
if qs.encConn != nil {
qs.encConn.Close()
}
if qs.tlsConn != nil {
qs.tlsConn.Close()
}
if qs.conn != nil {
qs.conn.Close()
}
qs.conn = nil
qs.tlsConn = nil
qs.encConn = nil
}
// chanFromConn creates a channel from a Conn object, and sends everything it
//
// Read()s from the socket to the channel.
func CreateSocketChan(sock *QSocket) chan []byte {
c := make(chan []byte)
go func() {
b := make([]byte, 1024)
for {
if sock.IsClosed() {
c <- nil
return
}
sock.SetReadDeadline(time.Time{})
n, err := sock.Read(b)
if n > 0 {
res := make([]byte, n)
// Copy the buffer so it doesn't get changed while read by the recipient.
copy(res, b[:n])
c <- res
}
if err != nil || sock.IsClosed() {
// if err.Error() != "EOF" {
// logrus.Errorf("%s -read-err-> %s", sock.RemoteAddr(), err)
// }
c <- nil
break
}
}
}()
return c
}
// BindSockets is used for creating a full duplex channel between `con1` and `con2` sockets,
// effectively binding two sockets.
func BindSockets(con1, con2 *QSocket) error {
defer con1.Close()
defer con2.Close()
chan1 := CreateSocketChan(con1)
chan2 := CreateSocketChan(con2)
var err error
for {
select {
case b1 := <-chan1:
if b1 != nil {
_, err = con2.Write(b1)
} else {
err = ErrQSocketSessionEnd
}
case b2 := <-chan2:
if b2 != nil {
_, err = con1.Write(b2)
} else {
err = ErrQSocketSessionEnd
}
}
if err != nil {
break
}
}
return err
}