-
Notifications
You must be signed in to change notification settings - Fork 256
/
TCPConn.go
142 lines (130 loc) · 3.88 KB
/
TCPConn.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
/*
* Copyright (c) 2015, Psiphon Inc.
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package psiphon
import (
"errors"
"net"
"sync"
"time"
)
// TCPConn is a customized TCP connection that:
// - can be interrupted while connecting;
// - implements idle read/write timeouts;
// - can be bound to a specific system device (for Android VpnService
// routing compatibility, for example).
// - implements the psiphon.Conn interface
type TCPConn struct {
net.Conn
mutex sync.Mutex
isClosed bool
closedSignal chan struct{}
interruptible interruptibleTCPSocket
readTimeout time.Duration
writeTimeout time.Duration
}
// NewTCPDialer creates a TCPDialer.
func NewTCPDialer(config *DialConfig) Dialer {
return func(network, addr string) (net.Conn, error) {
if network != "tcp" {
return nil, errors.New("unsupported network type in NewTCPDialer")
}
return DialTCP(addr, config)
}
}
// TCPConn creates a new, connected TCPConn.
func DialTCP(addr string, config *DialConfig) (conn *TCPConn, err error) {
conn, err = interruptibleTCPDial(addr, config)
if err != nil {
return nil, ContextError(err)
}
return conn, nil
}
// SetClosedSignal implements psiphon.Conn.SetClosedSignal.
func (conn *TCPConn) SetClosedSignal(closedSignal chan struct{}) bool {
conn.mutex.Lock()
defer conn.mutex.Unlock()
if conn.isClosed {
return false
}
conn.closedSignal = closedSignal
return true
}
// Close terminates a connected (net.Conn) or connecting (socketFd) TCPConn.
// A mutex is required to support psiphon.Conn.SetClosedSignal concurrency semantics.
func (conn *TCPConn) Close() (err error) {
conn.mutex.Lock()
defer conn.mutex.Unlock()
if !conn.isClosed {
if conn.Conn == nil {
err = interruptibleTCPClose(conn.interruptible)
} else {
err = conn.Conn.Close()
}
conn.isClosed = true
select {
case conn.closedSignal <- *new(struct{}):
default:
}
}
return err
}
// Read wraps standard Read to add an idle timeout. The connection
// is explicitly closed on timeout.
func (conn *TCPConn) Read(buffer []byte) (n int, err error) {
// Note: no mutex on the conn.readTimeout access
if conn.readTimeout != 0 {
err = conn.Conn.SetReadDeadline(time.Now().Add(conn.readTimeout))
if err != nil {
return 0, ContextError(err)
}
}
n, err = conn.Conn.Read(buffer)
if err != nil {
conn.Close()
}
return
}
// Write wraps standard Write to add an idle timeout The connection
// is explicitly closed on timeout.
func (conn *TCPConn) Write(buffer []byte) (n int, err error) {
// Note: no mutex on the conn.writeTimeout access
if conn.writeTimeout != 0 {
err = conn.Conn.SetWriteDeadline(time.Now().Add(conn.writeTimeout))
if err != nil {
return 0, ContextError(err)
}
}
n, err = conn.Conn.Write(buffer)
if err != nil {
conn.Close()
}
return
}
// Override implementation of net.Conn.SetDeadline
func (conn *TCPConn) SetDeadline(t time.Time) error {
return errors.New("net.Conn SetDeadline not supported")
}
// Override implementation of net.Conn.SetReadDeadline
func (conn *TCPConn) SetReadDeadline(t time.Time) error {
return errors.New("net.Conn SetReadDeadline not supported")
}
// Override implementation of net.Conn.SetWriteDeadline
func (conn *TCPConn) SetWriteDeadline(t time.Time) error {
return errors.New("net.Conn SetWriteDeadline not supported")
}