/
servers.go
213 lines (181 loc) · 4.71 KB
/
servers.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
// Copyright (c) 2023 RethinkDNS and its authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package rnet
import (
"errors"
"fmt"
"sync"
x "github.com/celzero/firestack/intra/backend"
"github.com/celzero/firestack/intra/ipn"
"github.com/celzero/firestack/intra/log"
"github.com/celzero/firestack/intra/protect"
)
const (
// type of services
SVCSOCKS5 = "svcsocks5" // SOCKS5
SVCHTTP = "svchttp" // HTTP
PXSOCKS5 = "pxsocks5" // SOCKS5 with forwarding proxy
PXHTTP = "pxhttp" // HTTP with forwarding proxy
// status of proxies
SUP = 0 // svc UP
SOK = 1 // svc OK
SKO = -1 // svc not OK
END = -2 // svc stopped
)
var (
errNoServer = errors.New("no such server")
errSvcRunning = errors.New("service is running")
errNotUdp = errors.New("not udp conn")
errNotTcp = errors.New("not tcp conn")
errNoAddr = errors.New("no address")
errServerEnd = errors.New("server stopped")
errProxyEnd = errors.New("proxy stopped")
errNotProxy = errors.New("not a proxy")
errBlocked = errors.New("blocked")
udptimeoutsec = 5 * 60 // 5m
tcptimeoutsec = (2 * 60 * 60) + (40 * 60) // 2h40m
)
// todo: github.com/txthinking/brook/blob/master/pac.go
type Server interface {
// Sets the proxy as the next hop.
Hop(p x.Proxy) error
// ID returns the ID of the server.
ID() string
// Start starts the server.
Start() error
// Type returns the type of the server.
Type() string
// Addr returns the address of the server.
GetAddr() string
// Status returns the status of the server.
Status() int
// Stop stops the server.
Stop() error
// Refresh re-registers the server.
Refresh() error
}
type Services interface {
// Add adds a server.
AddServer(id, url string) (Server, error)
// Bridge bridges or unbridges server with proxy.
Bridge(serverid, proxyid string) error
// Remove removes a server.
RemoveServer(id string) (ok bool)
// RemoveAll removes all servers, returns the number removed.
RemoveAll() (rm int)
// Get returns a Server.
GetServer(id string) (Server, error)
// Stop stops all services, returns the number stopped.
StopServers() (n int)
// Refresh re-registers servces and returns a csv of active ones.
RefreshServers() (active string)
}
var _ Server = (*socks5)(nil)
type services struct {
sync.RWMutex
servers map[string]Server
proxies ipn.Proxies
listener ServerListener
ctl protect.Controller
}
func NewServices(proxies ipn.Proxies, ctl protect.Controller, listener ServerListener) Services {
if listener == nil || ctl == nil {
return nil
}
return &services{
servers: make(map[string]Server),
ctl: ctl,
proxies: proxies,
listener: listener,
}
}
func (s *services) AddServer(id, url string) (svc Server, err error) {
s.RemoveServer(id)
switch id {
case SVCSOCKS5, PXSOCKS5:
svc, err = newSocks5Server(id, url, s.ctl, s.listener)
case SVCHTTP, PXHTTP:
svc, err = newHttpServer(id, url, s.ctl, s.listener)
default:
return nil, errors.ErrUnsupported
}
if err != nil {
return nil, err
}
s.Lock()
s.servers[id] = svc
s.Unlock()
// if the server has a namesake proxy, bridge them
go s.Bridge(id, id)
return svc, nil
}
func (s *services) Bridge(serverid, proxyid string) error {
svc, err := s.GetServer(serverid)
if err != nil {
return err
}
// remove existing bridge, if any
if len(proxyid) <= 0 {
return svc.Hop(nil)
}
px, err := s.proxies.ProxyFor(proxyid)
if err != nil {
return err
}
svcstr := fmt.Sprintf("%s/%s [%d] at %s", serverid, svc.Type(), svc.Status(), svc.GetAddr())
pxstr := fmt.Sprintf("%s/%s [%d] at %s", proxyid, px.Type(), px.Status(), px.GetAddr())
log.I("svc: bridge: %s with %s", svcstr, pxstr)
return svc.Hop(px)
}
func (s *services) RemoveServer(id string) bool {
if svc, err := s.GetServer(id); err == nil {
go svc.Stop()
delete(s.servers, id)
return true
}
return false
}
func (s *services) GetServer(id string) (Server, error) {
s.RLock()
defer s.RUnlock()
if svc, ok := s.servers[id]; ok {
return svc, nil
}
return nil, errNoServer
}
func (s *services) StopServers() int {
s.Lock()
defer s.Unlock()
for _, svc := range s.servers {
go svc.Stop()
}
return len(s.servers)
}
func (s *services) RefreshServers() string {
s.Lock()
defer s.Unlock()
var csv string
for _, svc := range s.servers {
sid := svc.ID()
if err := svc.Refresh(); err != nil {
log.W("svc: refresh %s; err: %v", sid, err)
continue
}
if csv == "" {
csv = sid
} else {
csv += "," + sid
}
}
return csv
}
func (s *services) RemoveAll() int {
n := s.StopServers()
s.Lock()
clear(s.servers)
s.Unlock()
return n
}