V2bX/core/sing/node.go
2023-08-07 12:30:55 +08:00

209 lines
5.4 KiB
Go

package sing
import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/google/uuid"
"net/netip"
"net/url"
"strconv"
"strings"
"github.com/inazumav/sing-box/inbound"
F "github.com/sagernet/sing/common/format"
"github.com/InazumaV/V2bX/api/panel"
"github.com/InazumaV/V2bX/conf"
"github.com/goccy/go-json"
"github.com/inazumav/sing-box/option"
)
type WsNetworkConfig struct {
Path string `json:"path"`
Headers map[string]string `json:"headers"`
}
func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (option.Inbound, error) {
addr, err := netip.ParseAddr(c.ListenIP)
if err != nil {
return option.Inbound{}, fmt.Errorf("the listen ip not vail")
}
listen := option.ListenOptions{
Listen: (*option.ListenAddress)(&addr),
ListenPort: uint16(info.Port),
ProxyProtocol: c.SingOptions.EnableProxyProtocol,
TCPFastOpen: c.SingOptions.TCPFastOpen,
InboundOptions: option.InboundOptions{
SniffEnabled: c.SingOptions.SniffEnabled,
SniffOverrideDestination: c.SingOptions.SniffOverrideDestination,
},
}
tls := option.InboundTLSOptions{
Enabled: info.Tls,
CertificatePath: c.CertConfig.CertFile,
KeyPath: c.CertConfig.KeyFile,
ServerName: info.ServerName,
}
in := option.Inbound{
Tag: tag,
}
switch info.Type {
case "v2ray":
t := option.V2RayTransportOptions{
Type: info.Network,
}
switch info.Network {
case "tcp":
t.Type = ""
case "ws":
network := WsNetworkConfig{}
err := json.Unmarshal(info.NetworkSettings, &network)
if err != nil {
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
}
var u *url.URL
u, err = url.Parse(network.Path)
if err != nil {
return option.Inbound{}, fmt.Errorf("parse path error: %s", err)
}
ed, _ := strconv.Atoi(u.Query().Get("ed"))
h := make(map[string]option.Listable[string], len(network.Headers))
for k, v := range network.Headers {
h[k] = option.Listable[string]{
v,
}
}
t.WebsocketOptions = option.V2RayWebsocketOptions{
Path: u.Path,
EarlyDataHeaderName: "Sec-WebSocket-Protocol",
MaxEarlyData: uint32(ed),
Headers: h,
}
case "grpc":
err := json.Unmarshal(info.NetworkSettings, &t.GRPCOptions)
if err != nil {
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
}
}
if info.ExtraConfig.EnableVless == "true" {
in.Type = "vless"
in.VLESSOptions = option.VLESSInboundOptions{
ListenOptions: listen,
TLS: &tls,
Transport: &t,
}
} else {
in.Type = "vmess"
in.VMessOptions = option.VMessInboundOptions{
ListenOptions: listen,
TLS: &tls,
Transport: &t,
}
}
case "shadowsocks":
in.Type = "shadowsocks"
var keyLength int
switch info.Cipher {
case "2022-blake3-aes-128-gcm":
keyLength = 16
case "2022-blake3-aes-256-gcm":
keyLength = 32
default:
keyLength = 16
}
in.ShadowsocksOptions = option.ShadowsocksInboundOptions{
ListenOptions: listen,
Method: info.Cipher,
}
p := make([]byte, keyLength)
_, _ = rand.Read(p)
randomPasswd := string(p)
if strings.Contains(info.Cipher, "2022") {
in.ShadowsocksOptions.Password = info.ServerKey
randomPasswd = base64.StdEncoding.EncodeToString([]byte(randomPasswd))
}
in.ShadowsocksOptions.Users = []option.ShadowsocksUser{{
Password: randomPasswd,
}}
case "trojan":
in.Type = "trojan"
t := option.V2RayTransportOptions{
Type: info.Network,
}
switch info.Network {
case "tcp":
t.Type = ""
case "grpc":
err := json.Unmarshal(info.NetworkSettings, &t.GRPCOptions)
if err != nil {
return option.Inbound{}, fmt.Errorf("decode NetworkSettings error: %s", err)
}
}
randomPasswd := uuid.New().String()
in.TrojanOptions = option.TrojanInboundOptions{
ListenOptions: listen,
Users: []option.TrojanUser{{
Name: randomPasswd,
Password: randomPasswd,
}},
TLS: &tls,
Transport: &t,
}
if c.SingOptions.FallBackConfigs != nil {
// fallback handling
fallback := c.SingOptions.FallBackConfigs.FallBack
fallbackPort, err := strconv.Atoi(fallback.ServerPort)
if err == nil {
in.TrojanOptions.Fallback = &option.ServerOptions{
Server: fallback.Server,
ServerPort: uint16(fallbackPort),
}
}
fallbackForALPNMap := c.SingOptions.FallBackConfigs.FallBackForALPN
fallbackForALPN := make(map[string]*option.ServerOptions, len(fallbackForALPNMap))
if err := processFallback(c, fallbackForALPN); err == nil {
in.TrojanOptions.FallbackForALPN = fallbackForALPN
}
}
}
return in, nil
}
func (b *Box) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error {
c, err := getInboundOptions(tag, info, config)
if err != nil {
return err
}
in, err := inbound.New(
context.Background(),
b.router,
b.logFactory.NewLogger(F.ToString("inbound/", c.Type, "[", tag, "]")),
c,
nil,
)
b.inbounds[tag] = in
err = in.Start()
if err != nil {
return fmt.Errorf("start inbound error: %s", err)
}
err = b.router.AddInbound(in)
if err != nil {
return fmt.Errorf("add inbound error: %s", err)
}
return nil
}
func (b *Box) DelNode(tag string) error {
err := b.inbounds[tag].Close()
if err != nil {
return fmt.Errorf("close inbound error: %s", err)
}
err = b.router.DelInbound(tag)
if err != nil {
return fmt.Errorf("delete inbound error: %s", err)
}
return nil
}