This repository has been archived by the owner on May 22, 2020. It is now read-only.
/
conn_dual.go
118 lines (105 loc) · 3.29 KB
/
conn_dual.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
package tapdance
import (
"context"
"errors"
"net"
"strconv"
)
// DualConn is composed of 2 separate TapdanceFlowConn.
// Allows to achieve substantially higher upload speed
// and slightly higher download speed.
type DualConn struct {
net.Conn
writerConn *TapdanceFlowConn
readerConn *TapdanceFlowConn
sessionId uint64 // constant for logging
}
// returns TapDance connection that utilizes 2 flows underneath: reader and writer
func dialSplitFlow(ctx context.Context, customDialer func(context.Context, string, string) (net.Conn, error),
covert string) (net.Conn, error) {
dualConn := DualConn{sessionId: sessionsTotal.GetAndInc()}
stationPubkey := Assets().GetPubkey()
rawRConn := makeTdRaw(tagHttpGetIncomplete, stationPubkey[:])
if customDialer != nil {
rawRConn.TcpDialer = customDialer
}
rawRConn.sessionId = dualConn.sessionId
rawRConn.strIdSuffix = "R"
var err error
dualConn.readerConn, err = makeTdFlow(flowReadOnly, rawRConn, covert)
if err != nil {
return nil, err
}
err = dualConn.readerConn.DialContext(ctx)
if err != nil {
return nil, err
}
// net.Conn functions that are not explicitly declared will be performed by readerConn
dualConn.Conn = dualConn.readerConn
// TODO: traffic fingerprinting issue
// TODO: fundamental issue of observable dependency between 2 flows
err = dualConn.readerConn.yieldUpload()
if err != nil {
dualConn.readerConn.closeWithErrorOnce(err)
return nil, err
}
rawWConn := makeTdRaw(tagHttpPostIncomplete,
stationPubkey[:])
if customDialer != nil {
rawRConn.TcpDialer = customDialer
}
rawWConn.sessionId = dualConn.sessionId
rawWConn.strIdSuffix = "W"
rawWConn.decoySpec = rawRConn.decoySpec
rawWConn.pinDecoySpec = true
dualConn.writerConn, err = makeTdFlow(flowUpload, rawWConn, covert)
if err != nil {
dualConn.readerConn.closeWithErrorOnce(err)
return nil, err
}
err = dualConn.writerConn.DialContext(ctx)
if err != nil {
dualConn.readerConn.closeWithErrorOnce(err)
return nil, err
}
err = dualConn.writerConn.acquireUpload()
if err != nil {
dualConn.readerConn.closeWithErrorOnce(err)
dualConn.writerConn.closeWithErrorOnce(err)
return nil, err
}
/* // TODO: yield confirmation
writerConn.yieldConfirmed = make(chan struct{})
go func() {
time.Sleep(time.Duration(getRandInt(1234, 5432)) * time.Millisecond)
Logger().Infoln(dualConn.idStr() + " faking yield confirmation!")
writerConn.yieldConfirmed <- struct{}{}
}()
err = writerConn.WaitForYieldConfirmation()
if err != nil {
dualConn.readerConn.Close()
writerConn.Close()
return nil, err
}
*/
go func() {
select {
case <-dualConn.readerConn.closed:
dualConn.writerConn.closeWithErrorOnce(errors.New("in paired readerConn: " +
dualConn.readerConn.closeErr.Error()))
case <-dualConn.writerConn.closed:
dualConn.readerConn.closeWithErrorOnce(errors.New("in paired writerConn: " +
dualConn.writerConn.closeErr.Error()))
}
}()
return &dualConn, nil
}
// Write writes data to the connection.
// Write can be made to time out and return an Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
func (tdConn *DualConn) Write(b []byte) (int, error) {
return tdConn.writerConn.Write(b)
}
func (tdConn *DualConn) idStr() string {
return "[Session " + strconv.FormatUint(tdConn.sessionId, 10) + "]"
}