Skip to content

Commit 5ac0f46

Browse files
committed
Fix how we deal with legacy services.
1 parent b8c5ab8 commit 5ac0f46

File tree

3 files changed

+211
-171
lines changed

3 files changed

+211
-171
lines changed

cmd/outline-ss-server/main.go

Lines changed: 37 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,16 @@ package main
1616

1717
import (
1818
"container/list"
19-
"context"
2019
"flag"
2120
"fmt"
22-
"net"
2321
"net/http"
2422
"os"
2523
"os/signal"
2624
"strings"
2725
"syscall"
2826
"time"
2927

30-
"github.com/Jigsaw-Code/outline-sdk/transport"
3128
"github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks"
32-
3329
"github.com/Jigsaw-Code/outline-ss-server/ipinfo"
3430
"github.com/Jigsaw-Code/outline-ss-server/service"
3531
"github.com/op/go-logging"
@@ -60,159 +56,11 @@ func init() {
6056
logger = logging.MustGetLogger("")
6157
}
6258

63-
// The implementations of listeners for different network types are not
64-
// interchangeable. The type of listener depends on the network type.
65-
// TODO(sbruens): Create a custom `Listener` type so we can share serving logic,
66-
// dispatching to the handlers based on connection type instead of on the
67-
// listener type.
68-
type Listener = any
69-
70-
type Service struct {
71-
natTimeout time.Duration
72-
m *outlineMetrics
73-
replayCache *service.ReplayCache
74-
Listeners []Listener
75-
Ciphers *list.List // Values are *List of *CipherEntry.
76-
}
77-
78-
func (s *Service) Serve(addr NetworkAddr, listener Listener, cipherList service.CipherList) error {
79-
switch ln := listener.(type) {
80-
case net.Listener:
81-
authFunc := service.NewShadowsocksStreamAuthenticator(cipherList, s.replayCache, s.m)
82-
// TODO: Register initial data metrics at zero.
83-
tcpHandler := service.NewTCPHandler(addr.Key(), authFunc, s.m, tcpReadTimeout)
84-
accept := func() (transport.StreamConn, error) {
85-
c, err := ln.Accept()
86-
if err == nil {
87-
return c.(transport.StreamConn), err
88-
}
89-
return nil, err
90-
}
91-
go service.StreamServe(accept, tcpHandler.Handle)
92-
case net.PacketConn:
93-
packetHandler := service.NewPacketHandler(s.natTimeout, cipherList, s.m)
94-
go packetHandler.Handle(ln)
95-
default:
96-
return fmt.Errorf("unknown listener type: %v", ln)
97-
}
98-
return nil
99-
}
100-
101-
func (s *Service) Stop() error {
102-
for _, listener := range s.Listeners {
103-
switch ln := listener.(type) {
104-
case net.Listener:
105-
if err := ln.Close(); err != nil {
106-
//lint:ignore ST1005 Shadowsocks is capitalized.
107-
return fmt.Errorf("Shadowsocks %s service on address %s failed to stop: %w", ln.Addr().Network(), ln.Addr().String(), err)
108-
}
109-
case net.PacketConn:
110-
if err := ln.Close(); err != nil {
111-
//lint:ignore ST1005 Shadowsocks is capitalized.
112-
return fmt.Errorf("Shadowsocks %s service on address %s failed to stop: %w", ln.LocalAddr().Network(), ln.LocalAddr().String(), err)
113-
}
114-
default:
115-
return fmt.Errorf("unknown listener type: %v", ln)
116-
}
117-
}
118-
return nil
119-
}
120-
121-
// AddListener adds a new listener to the service.
122-
func (s *Service) AddListener(addr NetworkAddr) error {
123-
// Create new listeners based on the configured network addresses.
124-
cipherList := service.NewCipherList()
125-
cipherList.Update(s.Ciphers)
126-
127-
listener, err := addr.Listen(context.TODO(), net.ListenConfig{KeepAlive: 0})
128-
if err != nil {
129-
//lint:ignore ST1005 Shadowsocks is capitalized.
130-
return fmt.Errorf("Shadowsocks %s service failed to start on address %s: %w", addr.Network(), addr.String(), err)
131-
}
132-
s.Listeners = append(s.Listeners, listener)
133-
logger.Infof("Shadowsocks %s service listening on %s", addr.Network(), addr.String())
134-
if err = s.Serve(addr, listener, cipherList); err != nil {
135-
return fmt.Errorf("failed to serve on %s listener on address %s: %w", addr.Network(), addr.String(), err)
136-
}
137-
return nil
138-
}
139-
140-
// NewService creates a new Service.
141-
func NewService(config ServiceConfig, natTimeout time.Duration, m *outlineMetrics, replayCache *service.ReplayCache) (*Service, error) {
142-
s := Service{
143-
natTimeout: natTimeout,
144-
m: m,
145-
replayCache: replayCache,
146-
Ciphers: list.New(),
147-
}
148-
149-
type cipherKey struct {
150-
cipher string
151-
secret string
152-
}
153-
existingCiphers := make(map[cipherKey]bool)
154-
for _, keyConfig := range config.Keys {
155-
key := cipherKey{keyConfig.Cipher, keyConfig.Secret}
156-
if _, exists := existingCiphers[key]; exists {
157-
logger.Debugf("encryption key already exists for ID=`%v`. Skipping.", keyConfig.ID)
158-
continue
159-
}
160-
cryptoKey, err := shadowsocks.NewEncryptionKey(keyConfig.Cipher, keyConfig.Secret)
161-
if err != nil {
162-
return nil, fmt.Errorf("failed to create encyption key for key %v: %w", keyConfig.ID, err)
163-
}
164-
entry := service.MakeCipherEntry(keyConfig.ID, cryptoKey, keyConfig.Secret)
165-
s.Ciphers.PushBack(&entry)
166-
existingCiphers[key] = true
167-
}
168-
169-
for _, listener := range config.Listeners {
170-
addr, err := ParseNetworkAddr(listener.Address)
171-
if err != nil {
172-
return nil, fmt.Errorf("error parsing listener address `%s`: %v", listener.Address, err)
173-
}
174-
if err := s.AddListener(addr); err != nil {
175-
return nil, err
176-
}
177-
}
178-
179-
return &s, nil
180-
}
181-
182-
func NewLegacyKeyService(config LegacyKeyServiceConfig, natTimeout time.Duration, m *outlineMetrics, replayCache *service.ReplayCache) (*Service, error) {
183-
s := Service{
184-
natTimeout: natTimeout,
185-
m: m,
186-
replayCache: replayCache,
187-
Ciphers: list.New(),
188-
}
189-
190-
cryptoKey, err := shadowsocks.NewEncryptionKey(config.Cipher, config.Secret)
191-
if err != nil {
192-
return nil, fmt.Errorf("failed to create encyption key for key %v: %w", config.ID, err)
193-
}
194-
entry := service.MakeCipherEntry(config.ID, cryptoKey, config.Secret)
195-
s.Ciphers.PushBack(&entry)
196-
197-
for _, network := range []string{"tcp", "udp"} {
198-
addr := NetworkAddr{
199-
network: network,
200-
Host: "::",
201-
Port: uint(config.Port),
202-
}
203-
if err := s.AddListener(addr); err != nil {
204-
return nil, err
205-
}
206-
}
207-
208-
return &s, nil
209-
}
210-
21159
type SSServer struct {
21260
natTimeout time.Duration
21361
m *outlineMetrics
21462
replayCache service.ReplayCache
215-
services []Service
63+
services []*Service
21664
}
21765

21866
func (s *SSServer) loadConfig(filename string) error {
@@ -231,39 +79,64 @@ func (s *SSServer) loadConfig(filename string) error {
23179
// We hot swap the services by having them both live at the same time. This
23280
// means we create services for the new config first, and then take down the
23381
// services from the old config.
234-
newServices := make([]Service, 0)
82+
newServices := make([]*Service, 0)
83+
84+
legacyPortService := make(map[int]*Service) // Values are *List of *CipherEntry.
23585
for _, legacyKeyServiceConfig := range config.Keys {
236-
service, err := NewLegacyKeyService(legacyKeyServiceConfig, s.natTimeout, s.m, &s.replayCache)
86+
legacyService, ok := legacyPortService[legacyKeyServiceConfig.Port]
87+
if !ok {
88+
legacyService = &Service{
89+
natTimeout: s.natTimeout,
90+
m: s.m,
91+
replayCache: &s.replayCache,
92+
ciphers: list.New(),
93+
}
94+
for _, network := range []string{"tcp", "udp"} {
95+
addr := NetworkAddr{
96+
network: network,
97+
Host: "::",
98+
Port: uint(legacyKeyServiceConfig.Port),
99+
}
100+
if err := legacyService.AddListener(addr); err != nil {
101+
return err
102+
}
103+
}
104+
newServices = append(newServices, legacyService)
105+
legacyPortService[legacyKeyServiceConfig.Port] = legacyService
106+
}
107+
cryptoKey, err := shadowsocks.NewEncryptionKey(legacyKeyServiceConfig.Cipher, legacyKeyServiceConfig.Secret)
237108
if err != nil {
238-
return fmt.Errorf("Failed to create new service: %v", err)
109+
return fmt.Errorf("failed to create encyption key for key %v: %w", legacyKeyServiceConfig.ID, err)
239110
}
240-
newServices = append(newServices, *service)
111+
entry := service.MakeCipherEntry(legacyKeyServiceConfig.ID, cryptoKey, legacyKeyServiceConfig.Secret)
112+
legacyService.AddCipher(&entry)
241113
}
114+
242115
for _, serviceConfig := range config.Services {
243116
service, err := NewService(serviceConfig, s.natTimeout, s.m, &s.replayCache)
244117
if err != nil {
245118
return fmt.Errorf("Failed to create new service: %v", err)
246119
}
247-
newServices = append(newServices, *service)
120+
newServices = append(newServices, service)
248121
}
122+
logger.Infof("Loaded %d new services", len(newServices))
249123

250124
// Take down the old services now that the new ones are created and serving.
251125
if err := s.Stop(); err != nil {
252126
logger.Errorf("Failed to stop old services: %w", err)
253127
}
254128
s.services = newServices
255129

256-
// Gather some basic stats for logging.
257130
var (
258131
listenerCount int
259132
cipherCount int
260133
)
261134
for _, service := range s.services {
262-
listenerCount += len(service.Listeners)
263-
cipherCount += service.Ciphers.Len()
135+
listenerCount += service.NumListeners()
136+
cipherCount += service.NumCiphers()
264137
}
265-
logger.Infof("Loaded %d services with %d access keys over %d listeners", len(s.services), cipherCount, listenerCount)
266-
s.m.SetNumAccessKeys(cipherCount, len(s.services))
138+
logger.Infof("%d services active: %d access keys over %d listeners", len(s.services), cipherCount, listenerCount)
139+
s.m.SetNumAccessKeys(cipherCount, listenerCount)
267140
return nil
268141
}
269142

@@ -272,13 +145,13 @@ func (s *SSServer) Stop() error {
272145
if len(s.services) == 0 {
273146
return nil
274147
}
275-
276148
for _, service := range s.services {
277149
if err := service.Stop(); err != nil {
278150
return err
279151
}
280152
}
281153
logger.Infof("Stopped %d old services", len(s.services))
154+
s.services = nil
282155
return nil
283156
}
284157

cmd/outline-ss-server/metrics.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type outlineMetrics struct {
3838

3939
buildInfo *prometheus.GaugeVec
4040
accessKeys prometheus.Gauge
41-
ports prometheus.Gauge
41+
listeners prometheus.Gauge
4242
dataBytes *prometheus.CounterVec
4343
dataBytesPerLocation *prometheus.CounterVec
4444
timeToCipherMs *prometheus.HistogramVec
@@ -183,10 +183,10 @@ func newPrometheusOutlineMetrics(ip2info ipinfo.IPInfoMap, registerer prometheus
183183
Name: "keys",
184184
Help: "Count of access keys",
185185
}),
186-
ports: prometheus.NewGauge(prometheus.GaugeOpts{
186+
listeners: prometheus.NewGauge(prometheus.GaugeOpts{
187187
Namespace: namespace,
188-
Name: "ports",
189-
Help: "Count of open Shadowsocks ports",
188+
Name: "listeners",
189+
Help: "Count of open Shadowsocks listeners",
190190
}),
191191
tcpProbes: prometheus.NewHistogramVec(prometheus.HistogramOpts{
192192
Namespace: namespace,
@@ -265,7 +265,7 @@ func newPrometheusOutlineMetrics(ip2info ipinfo.IPInfoMap, registerer prometheus
265265
m.tunnelTimeCollector = newTunnelTimeCollector(ip2info, registerer)
266266

267267
// TODO: Is it possible to pass where to register the collectors?
268-
registerer.MustRegister(m.buildInfo, m.accessKeys, m.ports, m.tcpProbes, m.tcpOpenConnections, m.tcpClosedConnections, m.tcpConnectionDurationMs,
268+
registerer.MustRegister(m.buildInfo, m.accessKeys, m.listeners, m.tcpProbes, m.tcpOpenConnections, m.tcpClosedConnections, m.tcpConnectionDurationMs,
269269
m.dataBytes, m.dataBytesPerLocation, m.timeToCipherMs, m.udpPacketsFromClientPerLocation, m.udpAddedNatEntries, m.udpRemovedNatEntries,
270270
m.tunnelTimeCollector)
271271
return m
@@ -275,9 +275,9 @@ func (m *outlineMetrics) SetBuildInfo(version string) {
275275
m.buildInfo.WithLabelValues(version).Set(1)
276276
}
277277

278-
func (m *outlineMetrics) SetNumAccessKeys(numKeys int, ports int) {
278+
func (m *outlineMetrics) SetNumAccessKeys(numKeys int, listeners int) {
279279
m.accessKeys.Set(float64(numKeys))
280-
m.ports.Set(float64(ports))
280+
m.listeners.Set(float64(listeners))
281281
}
282282

283283
func (m *outlineMetrics) AddOpenTCPConnection(clientInfo ipinfo.IPInfo) {

0 commit comments

Comments
 (0)