forked from Psiphon-Labs/psiphon-tunnel-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
listener.go
146 lines (127 loc) · 4.74 KB
/
listener.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
/*
* Copyright (c) 2020, 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 server
import (
"net"
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/fragmentor"
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
)
// TacticsListener wraps a net.Listener and applies server-side implementation
// of certain tactics parameters to accepted connections. Tactics filtering is
// limited to GeoIP attributes as the client has not yet sent API paramaters.
type TacticsListener struct {
net.Listener
support *SupportServices
tunnelProtocol string
geoIPLookup func(IPaddress string) GeoIPData
}
// NewTacticsListener creates a new TacticsListener.
func NewTacticsListener(
support *SupportServices,
listener net.Listener,
tunnelProtocol string,
geoIPLookup func(IPaddress string) GeoIPData) *TacticsListener {
return &TacticsListener{
Listener: listener,
support: support,
tunnelProtocol: tunnelProtocol,
geoIPLookup: geoIPLookup,
}
}
// Accept calls the underlying listener's Accept, and then checks if tactics
// for the connection set LimitTunnelProtocols.
//
// If LimitTunnelProtocols is set and does not include the tunnel protocol the
// listener is running, the accepted connection is immediately closed and the
// underlying Accept is called again.
//
// For retained connections, fragmentation is applied when specified by
// tactics.
func (listener *TacticsListener) Accept() (net.Conn, error) {
conn, err := listener.Listener.Accept()
if err != nil {
// Don't modify error from net.Listener
return nil, err
}
geoIPData := listener.geoIPLookup(
common.IPAddressFromAddr(conn.RemoteAddr()))
p, err := listener.support.ServerTacticsParametersCache.Get(geoIPData)
if err != nil {
return nil, errors.Trace(err)
}
if p.IsNil() {
// No tactics are configured; use the accepted conn without customization.
return conn, nil
}
// Server-side fragmentation may be synchronized with client-side in two ways.
//
// In the OSSH case, replay is always activated and it is seeded using the
// content of the client's OSSH seed message, which is fully delivered before
// the server sends any bytes. SetReplay is deferred until after the seed
// message is read by obfuscator.NewServerObfuscatedSSHConn. doReplay is set
// to true so no seed is set at this time.
//
// SSH lacks the initial obfuscation message, and meek and other protocols
// transmit downstream data before the initial obfuscation message arrives.
// For these protocols, server-side fragmentation will happen, initially,
// with an uncoordinated coin flip, based on server-side tactics
// configuration. For protocols with multiple underlying TCP connections,
// such as meek, the coin flip is performed independently once per
// TCP connection.
//
// The server-side replay mechanism is used to replay successful server-side
// fragmentation for uncoordinated protocols, subject to replay configuration
// parameters. In this case, the replay seed returned by GetReplayFragmentor
// below is applied.
replaySeed, doReplay := listener.support.ReplayCache.GetReplayFragmentor(
listener.tunnelProtocol, geoIPData)
if listener.tunnelProtocol == protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH {
replaySeed = nil
doReplay = true
}
var newSeed *prng.Seed
if !doReplay {
var err error
newSeed, err = prng.NewSeed()
if err != nil {
log.WithTraceFields(
LogFields{"error": err}).Warning("failed to seed fragmentor PRNG")
return conn, nil
}
}
fragmentorConfig := fragmentor.NewDownstreamConfig(
p, listener.tunnelProtocol, newSeed)
if fragmentorConfig.MayFragment() {
conn = fragmentor.NewConn(
fragmentorConfig,
func(message string) {
log.WithTraceFields(
LogFields{"message": message}).Debug("Fragmentor")
},
conn)
if doReplay && replaySeed != nil {
conn.(common.FragmentorReplayAccessor).SetReplay(
prng.NewPRNGWithSeed(replaySeed))
}
}
return conn, nil
}