Compare commits

..

6 Commits

Author SHA1 Message Date
世界
73c068b96f Update documentation 2023-02-19 17:49:05 +08:00
世界
f516026540 Fix shadowtls in go versiojns below 1.20 2023-02-19 12:02:11 +08:00
dyhkwong
3c5bc842ed Update QUIC v2 version number and initial salt 2023-02-18 23:51:55 +08:00
世界
d8270a66f4 Update release script 2023-02-18 21:14:17 +08:00
世界
123c383eae Fix documentation 2023-02-18 19:33:35 +08:00
世界
67814faf92 Remove TLS min version for shadowtls v3 2023-02-18 19:26:05 +08:00
30 changed files with 49 additions and 953 deletions

View File

@@ -14,5 +14,7 @@ jobs:
- uses: actions/setup-python@v4 - uses: actions/setup-python@v4
with: with:
python-version: 3.x python-version: 3.x
- run: pip install mkdocs-material mkdocs-static-i18n - run: |
- run: mkdocs gh-deploy -m "{sha}" --force --ignore-version --no-history pip install mkdocs-material=="9.*" mkdocs-static-i18n=="0.53"
- run: |
mkdocs gh-deploy -m "{sha}" --force --ignore-version --no-history

View File

@@ -17,7 +17,6 @@ builds:
- with_wireguard - with_wireguard
- with_utls - with_utls
- with_clash_api - with_clash_api
- with_ssm_api
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
targets: targets:
@@ -51,7 +50,6 @@ builds:
- with_wireguard - with_wireguard
- with_utls - with_utls
- with_clash_api - with_clash_api
- with_ssm_api
env: env:
- CGO_ENABLED=1 - CGO_ENABLED=1
overrides: overrides:

View File

@@ -1,6 +1,6 @@
NAME = sing-box NAME = sing-box
COMMIT = $(shell git rev-parse --short HEAD) COMMIT = $(shell git rev-parse --short HEAD)
TAGS ?= with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,with_ssm_api TAGS ?= with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr
PARAMS = -v -trimpath -tags "$(TAGS)" -ldflags "-s -w -buildid=" PARAMS = -v -trimpath -tags "$(TAGS)" -ldflags "-s -w -buildid="
MAIN = ./cmd/sing-box MAIN = ./cmd/sing-box

View File

@@ -48,16 +48,3 @@ type V2RayStatsService interface {
RoutedConnection(inbound string, outbound string, user string, conn net.Conn) net.Conn RoutedConnection(inbound string, outbound string, user string, conn net.Conn) net.Conn
RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn
} }
type SSMServer interface {
Service
RoutedConnection(metadata InboundContext, conn net.Conn) net.Conn
RoutedPacketConnection(metadata InboundContext, conn N.PacketConn) N.PacketConn
}
type ManagedShadowsocksServer interface {
Inbound
Method() string
Password() string
UpdateUsers(users []string, uPSKs []string) error
}

View File

@@ -17,7 +17,6 @@ import (
type Router interface { type Router interface {
Service Service
Inbound(tag string) (Inbound, bool)
Outbounds() []Outbound Outbounds() []Outbound
Outbound(tag string) (Outbound, bool) Outbound(tag string) (Outbound, bool)
DefaultOutbound(network string) Outbound DefaultOutbound(network string) Outbound
@@ -46,9 +45,6 @@ type Router interface {
V2RayServer() V2RayServer V2RayServer() V2RayServer
SetV2RayServer(server V2RayServer) SetV2RayServer(server V2RayServer)
SSMServer() SSMServer
SetSSMServer(server SSMServer)
} }
type routerContextKey struct{} type routerContextKey struct{}

21
box.go
View File

@@ -32,7 +32,6 @@ type Box struct {
logFile *os.File logFile *os.File
clashServer adapter.ClashServer clashServer adapter.ClashServer
v2rayServer adapter.V2RayServer v2rayServer adapter.V2RayServer
ssmServer adapter.SSMServer
done chan struct{} done chan struct{}
} }
@@ -42,7 +41,6 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
var needClashAPI bool var needClashAPI bool
var needV2RayAPI bool var needV2RayAPI bool
var needSSMAPI bool
if options.Experimental != nil { if options.Experimental != nil {
if options.Experimental.ClashAPI != nil && options.Experimental.ClashAPI.ExternalController != "" { if options.Experimental.ClashAPI != nil && options.Experimental.ClashAPI.ExternalController != "" {
needClashAPI = true needClashAPI = true
@@ -50,9 +48,6 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
if options.Experimental.V2RayAPI != nil && options.Experimental.V2RayAPI.Listen != "" { if options.Experimental.V2RayAPI != nil && options.Experimental.V2RayAPI.Listen != "" {
needV2RayAPI = true needV2RayAPI = true
} }
if options.Experimental.SSMAPI != nil && options.Experimental.SSMAPI.Listen != "" {
needSSMAPI = true
}
} }
var logFactory log.Factory var logFactory log.Factory
@@ -161,7 +156,6 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
var clashServer adapter.ClashServer var clashServer adapter.ClashServer
var v2rayServer adapter.V2RayServer var v2rayServer adapter.V2RayServer
var ssmServer adapter.SSMServer
if needClashAPI { if needClashAPI {
clashServer, err = experimental.NewClashServer(router, observableLogFactory, common.PtrValueOrDefault(options.Experimental.ClashAPI)) clashServer, err = experimental.NewClashServer(router, observableLogFactory, common.PtrValueOrDefault(options.Experimental.ClashAPI))
if err != nil { if err != nil {
@@ -176,13 +170,6 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
} }
router.SetV2RayServer(v2rayServer) router.SetV2RayServer(v2rayServer)
} }
if needSSMAPI {
ssmServer, err = experimental.NewSSMServer(router, logFactory.NewLogger("ssm-api"), common.PtrValueOrDefault(options.Experimental.SSMAPI))
if err != nil {
return nil, E.Cause(err, "create ssm api server")
}
router.SetSSMServer(ssmServer)
}
return &Box{ return &Box{
router: router, router: router,
inbounds: inbounds, inbounds: inbounds,
@@ -193,7 +180,6 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
logFile: logFile, logFile: logFile,
clashServer: clashServer, clashServer: clashServer,
v2rayServer: v2rayServer, v2rayServer: v2rayServer,
ssmServer: ssmServer,
done: make(chan struct{}), done: make(chan struct{}),
}, nil }, nil
} }
@@ -258,12 +244,6 @@ func (s *Box) start() error {
return E.Cause(err, "start v2ray api server") return E.Cause(err, "start v2ray api server")
} }
} }
if s.ssmServer != nil {
err = s.ssmServer.Start()
if err != nil {
return E.Cause(err, "start ssm api server")
}
}
s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)") s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
return nil return nil
} }
@@ -286,7 +266,6 @@ func (s *Box) Close() error {
s.logFactory, s.logFactory,
s.clashServer, s.clashServer,
s.v2rayServer, s.v2rayServer,
s.ssmServer,
common.PtrOrNil(s.logFile), common.PtrOrNil(s.logFile),
) )
} }

View File

@@ -13,13 +13,13 @@ import (
const ( const (
VersionDraft29 = 0xff00001d VersionDraft29 = 0xff00001d
Version1 = 0x1 Version1 = 0x1
Version2 = 0x709a50c4 Version2 = 0x6b3343cf
) )
var ( var (
SaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99} SaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}
SaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a} SaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}
SaltV2 = []byte{0xa7, 0x07, 0xc2, 0x03, 0xa5, 0x9b, 0x47, 0x18, 0x4a, 0x1d, 0x62, 0xca, 0x57, 0x04, 0x06, 0xea, 0x7a, 0xe3, 0xe5, 0xd3} SaltV2 = []byte{0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9}
) )
const ( const (

View File

@@ -1,3 +1,3 @@
package constant package constant
var Version = "1.2-beta2" var Version = "1.2-beta3"

View File

@@ -1,3 +1,8 @@
#### 1.2-beta3
* Update QUIC v2 version number and initial salt
* Fix shadowtls v3 implementation
#### 1.2-beta2 #### 1.2-beta2
* Add [ShadowTLS protocol v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md) * Add [ShadowTLS protocol v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md)

View File

@@ -7,7 +7,7 @@
... // Listen Fields ... // Listen Fields
"version": 2, "version": 3,
"password": "fuck me till the daylight", "password": "fuck me till the daylight",
"handshake": { "handshake": {
"server": "google.com", "server": "google.com",
@@ -32,12 +32,13 @@ ShadowTLS protocol version.
|---------------|-----------------------------------------------------------------------------------------| |---------------|-----------------------------------------------------------------------------------------|
| `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) | | `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) |
| `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) | | `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) |
| `3` | [ShadowTLS v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md) |
#### password #### password
Set password. Set password.
Only available in the ShadowTLS v2 protocol. Only available in the ShadowTLS v2/v3 protocol.
#### handshake #### handshake

View File

@@ -7,7 +7,7 @@
... // 监听字段 ... // 监听字段
"version": 2, "version": 3,
"password": "fuck me till the daylight", "password": "fuck me till the daylight",
"handshake": { "handshake": {
"server": "google.com", "server": "google.com",
@@ -32,12 +32,13 @@ ShadowTLS 协议版本。
|---------------|-----------------------------------------------------------------------------------------| |---------------|-----------------------------------------------------------------------------------------|
| `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) | | `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) |
| `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) | | `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) |
| `3` | [ShadowTLS v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md) |
#### password #### password
设置密码。 设置密码。
仅在 ShadowTLS v2 协议中可用。 仅在 ShadowTLS v2/v3 协议中可用。
#### handshake #### handshake

View File

@@ -7,7 +7,7 @@
"type": "shadowtls", "type": "shadowtls",
"listen": "::", "listen": "::",
"listen_port": 4443, "listen_port": 4443,
"version": 2, "version": 3,
"password": "fuck me till the daylight", "password": "fuck me till the daylight",
"handshake": { "handshake": {
"server": "google.com", "server": "google.com",
@@ -47,7 +47,7 @@
"tag": "shadowtls-out", "tag": "shadowtls-out",
"server": "127.0.0.1", "server": "127.0.0.1",
"server_port": 4443, "server_port": 4443,
"version": 2, "version": 3,
"password": "fuck me till the daylight", "password": "fuck me till the daylight",
"tls": { "tls": {
"enabled": true, "enabled": true,

View File

@@ -1,24 +0,0 @@
package experimental
import (
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
)
type SSMServerConstructor = func(router adapter.Router, logger log.Logger, options option.SSMAPIOptions) (adapter.SSMServer, error)
var ssmServerConstructor SSMServerConstructor
func RegisterSSMServerConstructor(constructor SSMServerConstructor) {
ssmServerConstructor = constructor
}
func NewSSMServer(router adapter.Router, logger log.Logger, options option.SSMAPIOptions) (adapter.SSMServer, error) {
if ssmServerConstructor == nil {
return nil, os.ErrInvalid
}
return ssmServerConstructor(router, logger, options)
}

View File

@@ -1,214 +0,0 @@
package ssmapi
import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
)
func (s *Server) setupRoutes(r chi.Router) {
r.Group(func(r chi.Router) {
r.Get("/", s.getServerInfo)
r.Get("/nodes", s.getNodes)
r.Post("/nodes", s.addNode)
r.Get("/nodes/{id}", s.getNode)
r.Put("/nodes/{id}", s.updateNode)
r.Delete("/nodes/{id}", s.deleteNode)
r.Get("/users", s.listUser)
r.Post("/users", s.addUser)
r.Get("/users/{username}", s.getUser)
r.Put("/users/{username}", s.updateUser)
r.Delete("/users/{username}", s.deleteUser)
r.Get("/stats", s.getStats)
})
}
func (s *Server) getServerInfo(writer http.ResponseWriter, request *http.Request) {
render.JSON(writer, request, render.M{
"server": "sing-box",
"apiVersion": "v1",
"_sing_box_version": C.Version,
})
}
func (s *Server) getNodes(writer http.ResponseWriter, request *http.Request) {
var response struct {
Protocols []string `json:"protocols"`
Shadowsocks []ShadowsocksNodeObject `json:"shadowsocks,omitempty"`
}
for _, node := range s.nodes {
if !common.Contains(response.Protocols, node.Protocol()) {
response.Protocols = append(response.Protocols, node.Protocol())
}
switch node.Protocol() {
case C.TypeShadowsocks:
response.Shadowsocks = append(response.Shadowsocks, node.Shadowsocks())
}
}
render.JSON(writer, request, &response)
}
func (s *Server) addNode(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
}
func (s *Server) getNode(writer http.ResponseWriter, request *http.Request) {
nodeID := chi.URLParam(request, "id")
if nodeID == "" {
writer.WriteHeader(http.StatusBadRequest)
return
}
for _, node := range s.nodes {
if nodeID == node.ID() {
render.JSON(writer, request, render.M{
node.Protocol(): node.Object(),
})
return
}
}
}
func (s *Server) updateNode(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
}
func (s *Server) deleteNode(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotImplemented)
}
type SSMUserObject struct {
UserName string `json:"username"`
Password string `json:"uPSK,omitempty"`
DownlinkBytes int64 `json:"downlinkBytes"`
UplinkBytes int64 `json:"uplinkBytes"`
DownlinkPackets int64 `json:"downlinkPackets"`
UplinkPackets int64 `json:"uplinkPackets"`
TCPSessions int64 `json:"tcpSessions"`
UDPSessions int64 `json:"udpSessions"`
}
func (s *Server) listUser(writer http.ResponseWriter, request *http.Request) {
render.JSON(writer, request, render.M{
"users": s.userManager.List(),
})
}
func (s *Server) addUser(writer http.ResponseWriter, request *http.Request) {
var addRequest struct {
UserName string `json:"username"`
Password string `json:"uPSK"`
}
err := render.DecodeJSON(request.Body, &addRequest)
if err != nil {
render.Status(request, http.StatusBadRequest)
render.PlainText(writer, request, err.Error())
return
}
err = s.userManager.Add(addRequest.UserName, addRequest.Password)
if err != nil {
render.Status(request, http.StatusBadRequest)
render.PlainText(writer, request, err.Error())
return
}
writer.WriteHeader(http.StatusCreated)
}
func (s *Server) getUser(writer http.ResponseWriter, request *http.Request) {
userName := chi.URLParam(request, "username")
if userName == "" {
writer.WriteHeader(http.StatusBadRequest)
return
}
uPSK, loaded := s.userManager.Get(userName)
if !loaded {
writer.WriteHeader(http.StatusNotFound)
return
}
user := SSMUserObject{
UserName: userName,
Password: uPSK,
}
s.trafficManager.ReadUser(&user)
render.JSON(writer, request, user)
}
func (s *Server) updateUser(writer http.ResponseWriter, request *http.Request) {
userName := chi.URLParam(request, "username")
if userName == "" {
writer.WriteHeader(http.StatusBadRequest)
return
}
var updateRequest struct {
Password string `json:"uPSK"`
}
err := render.DecodeJSON(request.Body, &updateRequest)
if err != nil {
render.Status(request, http.StatusBadRequest)
render.PlainText(writer, request, err.Error())
return
}
_, loaded := s.userManager.Get(userName)
if !loaded {
writer.WriteHeader(http.StatusNotFound)
return
}
err = s.userManager.Update(userName, updateRequest.Password)
if err != nil {
render.Status(request, http.StatusBadRequest)
render.PlainText(writer, request, err.Error())
return
}
writer.WriteHeader(http.StatusNoContent)
}
func (s *Server) deleteUser(writer http.ResponseWriter, request *http.Request) {
userName := chi.URLParam(request, "username")
if userName == "" {
writer.WriteHeader(http.StatusBadRequest)
return
}
_, loaded := s.userManager.Get(userName)
if !loaded {
writer.WriteHeader(http.StatusNotFound)
return
}
err := s.userManager.Delete(userName)
if err != nil {
render.Status(request, http.StatusBadRequest)
render.PlainText(writer, request, err.Error())
return
}
writer.WriteHeader(http.StatusNoContent)
}
func (s *Server) getStats(writer http.ResponseWriter, request *http.Request) {
requireClear := chi.URLParam(request, "clear") == "true"
users := s.userManager.List()
s.trafficManager.ReadUsers(users)
for i := range users {
users[i].Password = ""
}
uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.trafficManager.ReadGlobal()
if requireClear {
s.trafficManager.Clear()
}
render.JSON(writer, request, render.M{
"uplinkBytes": uplinkBytes,
"downlinkBytes": downlinkBytes,
"uplinkPackets": uplinkPackets,
"downlinkPackets": downlinkPackets,
"tcpSessions": tcpSessions,
"udpSessions": udpSessions,
"users": users,
})
}

View File

@@ -1,117 +0,0 @@
package ssmapi
import (
"errors"
"net"
"net/http"
"strings"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network"
"github.com/go-chi/chi/v5"
)
func init() {
experimental.RegisterSSMServerConstructor(NewServer)
}
var _ adapter.SSMServer = (*Server)(nil)
type Server struct {
router adapter.Router
logger log.Logger
httpServer *http.Server
tcpListener net.Listener
nodes []Node
userManager *UserManager
trafficManager *TrafficManager
}
type Node interface {
Protocol() string
ID() string
Shadowsocks() ShadowsocksNodeObject
Object() any
Tag() string
UpdateUsers(users []string, uPSKs []string) error
}
func NewServer(router adapter.Router, logger log.Logger, options option.SSMAPIOptions) (adapter.SSMServer, error) {
chiRouter := chi.NewRouter()
server := &Server{
router: router,
logger: logger,
httpServer: &http.Server{
Addr: options.Listen,
Handler: chiRouter,
},
nodes: make([]Node, 0, len(options.Nodes)),
}
for i, nodeOptions := range options.Nodes {
switch nodeOptions.Type {
case C.TypeShadowsocks:
ssOptions := nodeOptions.ShadowsocksOptions
inbound, loaded := router.Inbound(ssOptions.Inbound)
if !loaded {
return nil, E.New("parse SSM node[", i, "]: inbound", ssOptions.Inbound, "not found")
}
ssInbound, isSS := inbound.(adapter.ManagedShadowsocksServer)
if !isSS {
return nil, E.New("parse SSM node[", i, "]: inbound", ssOptions.Inbound, "is not a shadowsocks inbound")
}
node := &ShadowsocksNode{
ssOptions,
ssInbound,
}
server.nodes = append(server.nodes, node)
}
}
server.trafficManager = NewTrafficManager(server.nodes)
server.userManager = NewUserManager(server.nodes, server.trafficManager)
listenPrefix := options.ListenPrefix
if !strings.HasPrefix(listenPrefix, "/") {
listenPrefix = "/" + listenPrefix
}
chiRouter.Route(listenPrefix+"server/v1", server.setupRoutes)
return server, nil
}
func (s *Server) Start() error {
listener, err := net.Listen("tcp", s.httpServer.Addr)
if err != nil {
return err
}
s.logger.Info("ssm-api started at ", listener.Addr())
s.tcpListener = listener
go func() {
err = s.httpServer.Serve(listener)
if err != nil && !errors.Is(err, http.ErrServerClosed) {
s.logger.Error("ssm-api serve error: ", err)
}
}()
return nil
}
func (s *Server) Close() error {
return common.Close(
common.PtrOrNil(s.httpServer),
s.tcpListener,
s.trafficManager,
)
}
func (s *Server) RoutedConnection(metadata adapter.InboundContext, conn net.Conn) net.Conn {
return s.trafficManager.RoutedConnection(metadata, conn)
}
func (s *Server) RoutedPacketConnection(metadata adapter.InboundContext, conn N.PacketConn) N.PacketConn {
return s.trafficManager.RoutedPacketConnection(metadata, conn)
}

View File

@@ -1,54 +0,0 @@
package ssmapi
import (
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
)
var _ Node = (*ShadowsocksNode)(nil)
type ShadowsocksNode struct {
node option.SSMShadowsocksNode
inbound adapter.ManagedShadowsocksServer
}
type ShadowsocksNodeObject struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
Method string `json:"method,omitempty"`
Passwords []string `json:"iPSKs,omitempty"`
Tags []string `json:"tags,omitempty"`
}
func (n *ShadowsocksNode) Protocol() string {
return C.TypeShadowsocks
}
func (n *ShadowsocksNode) ID() string {
return n.node.ID
}
func (n *ShadowsocksNode) Shadowsocks() ShadowsocksNodeObject {
return ShadowsocksNodeObject{
ID: n.node.ID,
Name: n.node.Name,
Endpoint: n.node.Address,
Method: n.inbound.Method(),
Passwords: []string{n.inbound.Password()},
Tags: n.node.Tags,
}
}
func (n *ShadowsocksNode) Object() any {
return n.Shadowsocks()
}
func (n *ShadowsocksNode) Tag() string {
return n.inbound.Tag()
}
func (n *ShadowsocksNode) UpdateUsers(users []string, uPSKs []string) error {
return n.inbound.UpdateUsers(users, uPSKs)
}

View File

@@ -1,227 +0,0 @@
package ssmapi
import (
"net"
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/trackerconn"
N "github.com/sagernet/sing/common/network"
"go.uber.org/atomic"
)
type TrafficManager struct {
nodeTags map[string]bool
nodeUsers map[string]bool
globalUplink *atomic.Int64
globalDownlink *atomic.Int64
globalUplinkPackets *atomic.Int64
globalDownlinkPackets *atomic.Int64
globalTCPSessions *atomic.Int64
globalUDPSessions *atomic.Int64
userAccess sync.Mutex
userUplink map[string]*atomic.Int64
userDownlink map[string]*atomic.Int64
userUplinkPackets map[string]*atomic.Int64
userDownlinkPackets map[string]*atomic.Int64
userTCPSessions map[string]*atomic.Int64
userUDPSessions map[string]*atomic.Int64
}
func NewTrafficManager(nodes []Node) *TrafficManager {
manager := &TrafficManager{
nodeTags: make(map[string]bool),
globalUplink: atomic.NewInt64(0),
globalDownlink: atomic.NewInt64(0),
globalUplinkPackets: atomic.NewInt64(0),
globalDownlinkPackets: atomic.NewInt64(0),
globalTCPSessions: atomic.NewInt64(0),
globalUDPSessions: atomic.NewInt64(0),
userUplink: make(map[string]*atomic.Int64),
userDownlink: make(map[string]*atomic.Int64),
userUplinkPackets: make(map[string]*atomic.Int64),
userDownlinkPackets: make(map[string]*atomic.Int64),
userTCPSessions: make(map[string]*atomic.Int64),
userUDPSessions: make(map[string]*atomic.Int64),
}
for _, node := range nodes {
manager.nodeTags[node.Tag()] = true
}
return manager
}
func (s *TrafficManager) UpdateUsers(users []string) {
nodeUsers := make(map[string]bool)
for _, user := range users {
nodeUsers[user] = true
}
s.nodeUsers = nodeUsers
}
func (s *TrafficManager) userCounter(user string) (*atomic.Int64, *atomic.Int64, *atomic.Int64, *atomic.Int64, *atomic.Int64, *atomic.Int64) {
s.userAccess.Lock()
defer s.userAccess.Unlock()
upCounter, loaded := s.userUplink[user]
if !loaded {
upCounter = atomic.NewInt64(0)
s.userUplink[user] = upCounter
}
downCounter, loaded := s.userDownlink[user]
if !loaded {
downCounter = atomic.NewInt64(0)
s.userDownlink[user] = downCounter
}
upPacketsCounter, loaded := s.userUplinkPackets[user]
if !loaded {
upPacketsCounter = atomic.NewInt64(0)
s.userUplinkPackets[user] = upPacketsCounter
}
downPacketsCounter, loaded := s.userDownlinkPackets[user]
if !loaded {
downPacketsCounter = atomic.NewInt64(0)
s.userDownlinkPackets[user] = downPacketsCounter
}
tcpSessionsCounter, loaded := s.userTCPSessions[user]
if !loaded {
tcpSessionsCounter = atomic.NewInt64(0)
s.userTCPSessions[user] = tcpSessionsCounter
}
udpSessionsCounter, loaded := s.userUDPSessions[user]
if !loaded {
udpSessionsCounter = atomic.NewInt64(0)
s.userUDPSessions[user] = udpSessionsCounter
}
return upCounter, downCounter, upPacketsCounter, downPacketsCounter, tcpSessionsCounter, udpSessionsCounter
}
func createCounter(counterList []*atomic.Int64, packetCounterList []*atomic.Int64) func(n int64) {
return func(n int64) {
for _, counter := range counterList {
counter.Add(n)
}
for _, counter := range packetCounterList {
counter.Inc()
}
}
}
func (s *TrafficManager) RoutedConnection(metadata adapter.InboundContext, conn net.Conn) net.Conn {
s.globalTCPSessions.Inc()
var readCounter []*atomic.Int64
var writeCounter []*atomic.Int64
if s.nodeTags[metadata.Inbound] {
readCounter = append(readCounter, s.globalUplink)
writeCounter = append(writeCounter, s.globalDownlink)
}
if s.nodeUsers[metadata.User] {
upCounter, downCounter, _, _, tcpSessionCounter, _ := s.userCounter(metadata.User)
readCounter = append(readCounter, upCounter)
writeCounter = append(writeCounter, downCounter)
tcpSessionCounter.Inc()
}
if len(readCounter) > 0 {
return trackerconn.New(conn, readCounter, writeCounter)
}
return conn
}
func (s *TrafficManager) RoutedPacketConnection(metadata adapter.InboundContext, conn N.PacketConn) N.PacketConn {
s.globalUDPSessions.Inc()
var readCounter []*atomic.Int64
var readPacketCounter []*atomic.Int64
var writeCounter []*atomic.Int64
var writePacketCounter []*atomic.Int64
if s.nodeTags[metadata.Inbound] {
readCounter = append(readCounter, s.globalUplink)
writeCounter = append(writeCounter, s.globalDownlink)
readPacketCounter = append(readPacketCounter, s.globalUplinkPackets)
writePacketCounter = append(writePacketCounter, s.globalDownlinkPackets)
}
if s.nodeUsers[metadata.User] {
upCounter, downCounter, upPacketsCounter, downPacketsCounter, _, udpSessionCounter := s.userCounter(metadata.User)
readCounter = append(readCounter, upCounter)
writeCounter = append(writeCounter, downCounter)
readPacketCounter = append(readPacketCounter, upPacketsCounter)
writePacketCounter = append(writePacketCounter, downPacketsCounter)
udpSessionCounter.Inc()
}
if len(readCounter) > 0 {
return trackerconn.NewHookPacket(conn, createCounter(readCounter, readPacketCounter), createCounter(writeCounter, writePacketCounter))
}
return conn
}
func (s *TrafficManager) ReadUser(user *SSMUserObject) {
s.userAccess.Lock()
defer s.userAccess.Unlock()
s.readUser(user)
}
func (s *TrafficManager) readUser(user *SSMUserObject) {
if counter, loaded := s.userUplink[user.UserName]; loaded {
user.UplinkBytes = counter.Load()
}
if counter, loaded := s.userDownlink[user.UserName]; loaded {
user.DownlinkBytes = counter.Load()
}
if counter, loaded := s.userUplinkPackets[user.UserName]; loaded {
user.UplinkPackets = counter.Load()
}
if counter, loaded := s.userDownlinkPackets[user.UserName]; loaded {
user.DownlinkPackets = counter.Load()
}
if counter, loaded := s.userTCPSessions[user.UserName]; loaded {
user.TCPSessions = counter.Load()
}
if counter, loaded := s.userUDPSessions[user.UserName]; loaded {
user.UDPSessions = counter.Load()
}
}
func (s *TrafficManager) ReadUsers(users []*SSMUserObject) {
s.userAccess.Lock()
defer s.userAccess.Unlock()
for _, user := range users {
s.readUser(user)
}
return
}
func (s *TrafficManager) ReadGlobal() (
uplinkBytes int64,
downlinkBytes int64,
uplinkPackets int64,
downlinkPackets int64,
tcpSessions int64,
udpSessions int64,
) {
return s.globalUplink.Load(),
s.globalDownlink.Load(),
s.globalUplinkPackets.Load(),
s.globalDownlinkPackets.Load(),
s.globalTCPSessions.Load(),
s.globalUDPSessions.Load()
}
func (s *TrafficManager) Clear() {
s.globalUplink.Store(0)
s.globalDownlink.Store(0)
s.globalUplinkPackets.Store(0)
s.globalDownlinkPackets.Store(0)
s.globalTCPSessions.Store(0)
s.globalUDPSessions.Store(0)
s.userAccess.Lock()
defer s.userAccess.Unlock()
s.userUplink = make(map[string]*atomic.Int64)
s.userDownlink = make(map[string]*atomic.Int64)
s.userUplinkPackets = make(map[string]*atomic.Int64)
s.userDownlinkPackets = make(map[string]*atomic.Int64)
s.userTCPSessions = make(map[string]*atomic.Int64)
s.userUDPSessions = make(map[string]*atomic.Int64)
}

View File

@@ -1,86 +0,0 @@
package ssmapi
import (
"sync"
E "github.com/sagernet/sing/common/exceptions"
)
type UserManager struct {
access sync.Mutex
usersMap map[string]string
nodes []Node
trafficManager *TrafficManager
}
func NewUserManager(nodes []Node, trafficManager *TrafficManager) *UserManager {
return &UserManager{
usersMap: make(map[string]string),
nodes: nodes,
trafficManager: trafficManager,
}
}
func (m *UserManager) postUpdate() error {
users := make([]string, 0, len(m.usersMap))
uPSKs := make([]string, 0, len(m.usersMap))
for username, password := range m.usersMap {
users = append(users, username)
uPSKs = append(uPSKs, password)
}
for _, node := range m.nodes {
err := node.UpdateUsers(users, uPSKs)
if err != nil {
return err
}
}
m.trafficManager.UpdateUsers(users)
return nil
}
func (m *UserManager) List() []*SSMUserObject {
m.access.Lock()
defer m.access.Unlock()
users := make([]*SSMUserObject, 0, len(m.usersMap))
for username, password := range m.usersMap {
users = append(users, &SSMUserObject{
UserName: username,
Password: password,
})
}
return users
}
func (m *UserManager) Add(username string, password string) error {
m.access.Lock()
defer m.access.Unlock()
if _, found := m.usersMap[username]; found {
return E.New("user", username, "already exists")
}
m.usersMap[username] = password
return m.postUpdate()
}
func (m *UserManager) Get(username string) (string, bool) {
m.access.Lock()
defer m.access.Unlock()
if password, found := m.usersMap[username]; found {
return password, true
}
return "", false
}
func (m *UserManager) Update(username string, password string) error {
m.access.Lock()
defer m.access.Unlock()
m.usersMap[username] = password
return m.postUpdate()
}
func (m *UserManager) Delete(username string) error {
m.access.Lock()
defer m.access.Unlock()
delete(m.usersMap, username)
return m.postUpdate()
}

View File

@@ -22,7 +22,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
if len(options.Users) > 0 && len(options.Destinations) > 0 { if len(options.Users) > 0 && len(options.Destinations) > 0 {
return nil, E.New("users and destinations options must not be combined") return nil, E.New("users and destinations options must not be combined")
} }
if len(options.Users) > 0 || options.Managed { if len(options.Users) > 0 {
return newShadowsocksMulti(ctx, router, logger, tag, options) return newShadowsocksMulti(ctx, router, logger, tag, options)
} else if len(options.Destinations) > 0 { } else if len(options.Destinations) > 0 {
return newShadowsocksRelay(ctx, router, logger, tag, options) return newShadowsocksRelay(ctx, router, logger, tag, options)
@@ -32,9 +32,8 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
} }
var ( var (
_ adapter.Inbound = (*Shadowsocks)(nil) _ adapter.Inbound = (*Shadowsocks)(nil)
_ adapter.InjectableInbound = (*Shadowsocks)(nil) _ adapter.InjectableInbound = (*Shadowsocks)(nil)
_ adapter.ManagedShadowsocksServer = (*Shadowsocks)(nil)
) )
type Shadowsocks struct { type Shadowsocks struct {
@@ -77,18 +76,6 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
return inbound, err return inbound, err
} }
func (h *Shadowsocks) Method() string {
return h.service.Name()
}
func (h *Shadowsocks) Password() string {
return h.service.Password()
}
func (h *Shadowsocks) UpdateUsers(names []string, uPSKs []string) error {
return os.ErrInvalid
}
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
} }

View File

@@ -19,9 +19,8 @@ import (
) )
var ( var (
_ adapter.Inbound = (*ShadowsocksMulti)(nil) _ adapter.Inbound = (*ShadowsocksMulti)(nil)
_ adapter.InjectableInbound = (*ShadowsocksMulti)(nil) _ adapter.InjectableInbound = (*ShadowsocksMulti)(nil)
_ adapter.ManagedShadowsocksServer = (*ShadowsocksMulti)(nil)
) )
type ShadowsocksMulti struct { type ShadowsocksMulti struct {
@@ -62,13 +61,11 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(options.Users) > 0 { err = service.UpdateUsersWithPasswords(common.MapIndexed(options.Users, func(index int, user option.ShadowsocksUser) int {
err = service.UpdateUsersWithPasswords(common.MapIndexed(options.Users, func(index int, user option.ShadowsocksUser) int { return index
return index }), common.Map(options.Users, func(user option.ShadowsocksUser) string {
}), common.Map(options.Users, func(user option.ShadowsocksUser) string { return user.Password
return user.Password }))
}))
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -78,29 +75,6 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
return inbound, err return inbound, err
} }
func (h *ShadowsocksMulti) Method() string {
return h.service.Name()
}
func (h *ShadowsocksMulti) Password() string {
return h.service.Password()
}
func (h *ShadowsocksMulti) UpdateUsers(users []string, uPSKs []string) error {
err := h.service.UpdateUsersWithPasswords(common.MapIndexed(users, func(index int, user string) int {
return index
}), uPSKs)
if err != nil {
return err
}
h.users = common.Map(users, func(user string) option.ShadowsocksUser {
return option.ShadowsocksUser{
Name: user,
}
})
return nil
}
func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
} }

View File

@@ -18,9 +18,8 @@ import (
) )
var ( var (
_ adapter.Inbound = (*ShadowsocksRelay)(nil) _ adapter.Inbound = (*ShadowsocksRelay)(nil)
_ adapter.InjectableInbound = (*ShadowsocksRelay)(nil) _ adapter.InjectableInbound = (*ShadowsocksRelay)(nil)
_ adapter.ManagedShadowsocksServer = (*ShadowsocksRelay)(nil)
) )
type ShadowsocksRelay struct { type ShadowsocksRelay struct {
@@ -72,18 +71,6 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
return inbound, err return inbound, err
} }
func (h *ShadowsocksRelay) Method() string {
return h.service.Name()
}
func (h *ShadowsocksRelay) Password() string {
return h.service.Password()
}
func (h *ShadowsocksRelay) UpdateUsers(users []string, uPSKs []string) error {
return os.ErrInvalid
}
func (h *ShadowsocksRelay) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *ShadowsocksRelay) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
} }

View File

@@ -1,5 +0,0 @@
//go:build with_ssm_api
package include
import _ "github.com/sagernet/sing-box/experimental/ssmapi"

View File

@@ -1,17 +0,0 @@
//go:build !with_ssm_api
package include
import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
)
func init() {
experimental.RegisterSSMServerConstructor(func(router adapter.Router, logger log.Logger, options option.SSMAPIOptions) (adapter.SSMServer, error) {
return nil, E.New(`SSM api is not included in this build, rebuild with -tags with_ssm_api`)
})
}

View File

@@ -3,5 +3,4 @@ package option
type ExperimentalOptions struct { type ExperimentalOptions struct {
ClashAPI *ClashAPIOptions `json:"clash_api,omitempty"` ClashAPI *ClashAPIOptions `json:"clash_api,omitempty"`
V2RayAPI *V2RayAPIOptions `json:"v2ray_api,omitempty"` V2RayAPI *V2RayAPIOptions `json:"v2ray_api,omitempty"`
SSMAPI *SSMAPIOptions `json:"ssm_api,omitempty"`
} }

View File

@@ -7,7 +7,6 @@ type ShadowsocksInboundOptions struct {
Password string `json:"password"` Password string `json:"password"`
Users []ShadowsocksUser `json:"users,omitempty"` Users []ShadowsocksUser `json:"users,omitempty"`
Destinations []ShadowsocksDestination `json:"destinations,omitempty"` Destinations []ShadowsocksDestination `json:"destinations,omitempty"`
Managed bool `json:"managed,omitempty"`
} }
type ShadowsocksUser struct { type ShadowsocksUser struct {

View File

@@ -1,54 +0,0 @@
package option
import (
"github.com/sagernet/sing-box/common/json"
C "github.com/sagernet/sing-box/constant"
E "github.com/sagernet/sing/common/exceptions"
)
type SSMAPIOptions struct {
Listen string `json:"listen,omitempty"`
ListenPrefix string `json:"listen_prefix,omitempty"`
Nodes []SSMNode `json:"nodes,omitempty"`
}
type _SSMNode struct {
Type string `json:"type,omitempty"`
ShadowsocksOptions SSMShadowsocksNode `json:"-"`
}
type SSMNode _SSMNode
func (h SSMNode) MarshalJSON() ([]byte, error) {
var v any
switch h.Type {
case C.TypeShadowsocks:
v = h.ShadowsocksOptions
default:
return nil, E.New("unknown ssm node type: ", h.Type)
}
return MarshallObjects((_SSMNode)(h), v)
}
func (h *SSMNode) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_SSMNode)(h))
if err != nil {
return err
}
var v any
switch h.Type {
case C.TypeShadowsocks:
v = &h.ShadowsocksOptions
default:
return E.New("unknown ssm node type: ", h.Type)
}
return UnmarshallExcluded(data, (*_SSMNode)(h), v)
}
type SSMShadowsocksNode struct {
ID string `json:"id"`
Name string `json:"name"`
Address string `json:"address"`
Tags []string `json:"tags"`
Inbound string `json:"inbound"`
}

View File

@@ -56,8 +56,6 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
options.TLS.MaxVersion = "1.2" options.TLS.MaxVersion = "1.2"
case 2: case 2:
case 3: case 3:
options.TLS.MinVersion = "1.3"
options.TLS.MaxVersion = "1.3"
default: default:
return nil, E.New("unknown shadowtls protocol version: ", options.Version) return nil, E.New("unknown shadowtls protocol version: ", options.Version)
} }

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e -o pipefail set -e -o pipefail
curl -Lo go.tar.gz https://go.dev/dl/go1.20.linux-amd64.tar.gz curl -Lo go.tar.gz https://go.dev/dl/go1.20.1.linux-amd64.tar.gz
sudo rm -rf /usr/local/go sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go.tar.gz sudo tar -C /usr/local -xzf go.tar.gz
rm go.tar.gz rm go.tar.gz

View File

@@ -97,7 +97,6 @@ type Router struct {
processSearcher process.Searcher processSearcher process.Searcher
clashServer adapter.ClashServer clashServer adapter.ClashServer
v2rayServer adapter.V2RayServer v2rayServer adapter.V2RayServer
ssmServer adapter.SSMServer
} }
func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions, inbounds []option.Inbound) (*Router, error) { func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions, inbounds []option.Inbound) (*Router, error) {
@@ -381,28 +380,10 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb
return nil return nil
} }
func (r *Router) Inbound(tag string) (adapter.Inbound, bool) {
inbound, loaded := r.inboundByTag[tag]
return inbound, loaded
}
func (r *Router) Outbounds() []adapter.Outbound { func (r *Router) Outbounds() []adapter.Outbound {
return r.outbounds return r.outbounds
} }
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
outbound, loaded := r.outboundByTag[tag]
return outbound, loaded
}
func (r *Router) DefaultOutbound(network string) adapter.Outbound {
if network == N.NetworkTCP {
return r.defaultOutboundForConnection
} else {
return r.defaultOutboundForPacketConnection
}
}
func (r *Router) Start() error { func (r *Router) Start() error {
if r.needGeoIPDatabase { if r.needGeoIPDatabase {
err := r.prepareGeoIPDatabase() err := r.prepareGeoIPDatabase()
@@ -523,6 +504,19 @@ func (r *Router) LoadGeosite(code string) (adapter.Rule, error) {
return rule, nil return rule, nil
} }
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
outbound, loaded := r.outboundByTag[tag]
return outbound, loaded
}
func (r *Router) DefaultOutbound(network string) adapter.Outbound {
if network == N.NetworkTCP {
return r.defaultOutboundForConnection
} else {
return r.defaultOutboundForPacketConnection
}
}
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if metadata.InboundDetour != "" { if metadata.InboundDetour != "" {
if metadata.LastInbound == metadata.InboundDetour { if metadata.LastInbound == metadata.InboundDetour {
@@ -609,9 +603,6 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
conn = statsService.RoutedConnection(metadata.Inbound, detour.Tag(), metadata.User, conn) conn = statsService.RoutedConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
} }
} }
if r.ssmServer != nil {
conn = r.ssmServer.RoutedConnection(metadata, conn)
}
return detour.NewConnection(ctx, conn, metadata) return detour.NewConnection(ctx, conn, metadata)
} }
@@ -690,9 +681,6 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), metadata.User, conn) conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
} }
} }
if r.ssmServer != nil {
conn = r.ssmServer.RoutedPacketConnection(metadata, conn)
}
return detour.NewPacketConnection(ctx, conn, metadata) return detour.NewPacketConnection(ctx, conn, metadata)
} }
@@ -789,14 +777,6 @@ func (r *Router) SetV2RayServer(server adapter.V2RayServer) {
r.v2rayServer = server r.v2rayServer = server
} }
func (r *Router) SSMServer() adapter.SSMServer {
return r.ssmServer
}
func (r *Router) SetSSMServer(server adapter.SSMServer) {
r.ssmServer = server
}
func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool { func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool {
for _, rule := range rules { for _, rule := range rules {
switch rule.Type { switch rule.Type {

View File

@@ -145,6 +145,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) {
if err := config.SessionIDGenerator(hello.marshal(), hello.sessionId); err != nil { if err := config.SessionIDGenerator(hello.marshal(), hello.sessionId); err != nil {
return nil, nil, errors.New("tls: generate session id failed: " + err.Error()) return nil, nil, errors.New("tls: generate session id failed: " + err.Error())
} }
hello.raw = nil
} else { } else {
if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil { if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil {
return nil, nil, errors.New("tls: short read from Rand: " + err.Error()) return nil, nil, errors.New("tls: short read from Rand: " + err.Error())