/
server.go
226 lines (202 loc) · 5.65 KB
/
server.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
package server
import (
"container/list"
"fmt"
"github.com/evgeniy-krivenko/outline-ss-server/service"
"github.com/evgeniy-krivenko/outline-ss-server/service/metrics"
ss "github.com/evgeniy-krivenko/outline-ss-server/shadowsocks"
"github.com/op/go-logging"
"gopkg.in/yaml.v2"
"io/ioutil"
"net"
"time"
)
//var logger *logging.Logger
// 59 seconds is most common timeout for servers that do not respond to invalid requests
const tcpReadTimeout = 59 * time.Second
type SsPort struct {
tcpService service.TCPService
udpService service.UDPService
cipherList service.CipherList
}
type SSServer struct {
natTimeout time.Duration
m metrics.ShadowsocksMetrics
replayCache service.ReplayCache
ports map[int]*SsPort
logger *logging.Logger
}
func NewSSServer(cnf *SSConfig) *SSServer {
return &SSServer{
cnf.NatTimeout,
cnf.Metrics,
service.NewReplayCache(cnf.ReplayHistory),
cnf.Ports,
cnf.Logger,
}
}
type SSConfig struct {
NatTimeout time.Duration
Metrics metrics.ShadowsocksMetrics
ReplayHistory int
Ports map[int]*SsPort
Logger *logging.Logger
}
func (s *SSServer) startPort(portNum int) error {
listener, err := net.ListenTCP("tcp", &net.TCPAddr{Port: portNum})
if err != nil {
return fmt.Errorf("Failed to start TCP on port %v: %v", portNum, err)
}
packetConn, err := net.ListenUDP("udp", &net.UDPAddr{Port: portNum})
if err != nil {
return fmt.Errorf("Failed to start UDP on port %v: %v", portNum, err)
}
s.logger.Infof("Listening TCP and UDP on port %v", portNum)
port := &SsPort{cipherList: service.NewCipherList()}
// TODO: Register initial data metrics at zero.
port.tcpService = service.NewTCPService(port.cipherList, &s.replayCache, s.m, tcpReadTimeout)
port.udpService = service.NewUDPService(s.natTimeout, port.cipherList, s.m)
s.ports[portNum] = port
go port.tcpService.Serve(listener)
go port.udpService.Serve(packetConn)
return nil
}
func (s *SSServer) removePort(portNum int) error {
port, ok := s.ports[portNum]
if !ok {
return fmt.Errorf("Port %v doesn't exist", portNum)
}
tcpErr := port.tcpService.Stop()
udpErr := port.udpService.Stop()
delete(s.ports, portNum)
if tcpErr != nil {
return fmt.Errorf("Failed to close listener on %v: %v", portNum, tcpErr)
}
if udpErr != nil {
return fmt.Errorf("Failed to close packetConn on %v: %v", portNum, udpErr)
}
s.logger.Infof("Stopped TCP and UDP on port %v", portNum)
return nil
}
func (s *SSServer) LoadConfig(filename string) error {
config, err := readConfig(filename)
if err != nil {
return fmt.Errorf("Failed to read config file %v: %v", filename, err)
}
portChanges := make(map[int]int)
portCiphers := make(map[int]*list.List) // Values are *List of *CipherEntry.
for _, keyConfig := range config.Keys {
portChanges[keyConfig.Port] = 1
cipherList, ok := portCiphers[keyConfig.Port]
if !ok {
cipherList = list.New()
portCiphers[keyConfig.Port] = cipherList
}
cipher, err := ss.NewCipher(keyConfig.Cipher, keyConfig.Secret)
if err != nil {
return fmt.Errorf("Failed to create cipher for key %v: %v", keyConfig.ID, err)
}
entry := service.MakeCipherEntry(keyConfig.ID, cipher, keyConfig.Secret)
cipherList.PushBack(&entry)
}
for port := range s.ports {
portChanges[port] = portChanges[port] - 1
}
for portNum, count := range portChanges {
if count == -1 {
if err := s.removePort(portNum); err != nil {
return fmt.Errorf("Failed to remove port %v: %v", portNum, err)
}
} else if count == +1 {
if err := s.startPort(portNum); err != nil {
return fmt.Errorf("Failed to start port %v: %v", portNum, err)
}
}
}
for portNum, cipherList := range portCiphers {
s.ports[portNum].cipherList.Update(cipherList)
}
s.logger.Infof("Loaded %v access keys", len(config.Keys))
s.m.SetNumAccessKeys(len(config.Keys), len(portCiphers))
return nil
}
type CipherStruct struct {
ID string
Port int
Cipher string
Secret string
}
func (s *SSServer) AddCipher(cs CipherStruct) (int, error) {
var isPortInit bool
for port := range s.ports {
if port == cs.Port {
isPortInit = true
}
}
if !isPortInit {
var port = cs.Port
// iter while starting port
// TODO Remove iter and create method to check free port
for err := s.startPort(port); err != nil; {
s.logger.Errorf("error for starting port: %d", port)
port++
}
cs.Port = port
}
cipher, err := ss.NewCipher(cs.Cipher, cs.Secret)
if err != nil {
return 0, fmt.Errorf("failed to create cipher for key %v: %v", cs.ID, err)
}
entry := service.MakeCipherEntry(cs.ID, cipher, cs.Secret)
s.ports[cs.Port].cipherList.AddCipher(&entry)
s.logger.Infof("add cipher with client id %s and port %d", cs.ID, cs.Port)
return cs.Port, nil
}
func (s *SSServer) RemoveCipher(cs CipherStruct) error {
ssP, ok := s.ports[cs.Port]
if !ok {
return fmt.Errorf("port for remove does not exists in server: %d", cs.Port)
}
ssP.cipherList.RemoveCipher(cs.ID)
l := ssP.cipherList.GetList()
if l.Len() <= 0 {
err := s.removePort(cs.Port)
if err != nil {
return err
}
}
return nil
}
func (s *SSServer) IsCipherExists(cs CipherStruct) bool {
ssP, ok := s.ports[cs.Port]
if !ok {
return false
}
return ssP.cipherList.IsCipherExists(cs.ID)
}
// Stop serving on all ports.
func (s *SSServer) Stop() error {
for portNum := range s.ports {
if err := s.removePort(portNum); err != nil {
return err
}
}
return nil
}
type Config struct {
Keys []struct {
ID string
Port int
Cipher string
Secret string
}
}
func readConfig(filename string) (*Config, error) {
config := Config{}
configData, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(configData, &config)
return &config, err
}