SagerNet / sing-box

The universal proxy platform
https://sing-box.sagernet.org/
Other
19.35k stars 2.31k forks source link

Feature: High-level boundary management interface #2180

Open beryll1um opened 4 days ago

beryll1um commented 4 days ago

I believe that any server-side management logic requires high-level boundary management interfaces to implement it. Manually restarting the service with a new configuration is not the best choice for a high-loaded environment where many users and protocols are handled.

Such interfaces are already implemented in v2fly/v2ray-core, but I find their implementation generally messy, so I prefer to switch to sing-box. Unfortunately, such functionality is not implemented there yet, as far as I understand... I decided to implement this myself, so check out my contribution. I'm still working on it, but overall it's done and working fine as far as I can verify.

I have some suspicions that this might break something, because some experimental tools like the Clash API can get a pointer to outgoing connections to work with them separately, and during work time these outgoing connections can be removed from the router, but so far I haven't found any problems with this (doesn't means there is no of them).

Please check my pull request: https://github.com/SagerNet/sing-box/pull/2177

beryll1um commented 1 day ago

How it looks like at this time:

package main

import (
    "context"
    "net/netip"
    "time"

    "github.com/sagernet/sing-box"
    "github.com/sagernet/sing-box/option"

    C "github.com/sagernet/sing-box/constant"
)

func main() {
    boxInstance, err := box.New(box.Options{
        Context: context.TODO(),
        Options: option.Options{
            Inbounds: []option.Inbound{
                {
                    Tag:  "shadowsocks",
                    Type: C.TypeShadowsocks,
                    ShadowsocksOptions: option.ShadowsocksInboundOptions{
                        ListenOptions: option.ListenOptions{
                            Listen:     option.NewListenAddress(netip.IPv4Unspecified()),
                            ListenPort: 1024,
                        },
                        Method:   "chacha20-ietf-poly1305",
                        Password: "1234567890",
                    },
                },
            },
            Outbounds: []option.Outbound{
                {
                    Tag:  "direct",
                    Type: C.TypeDirect,
                },
            },
        },
    })
    if err != nil {
        panic(err)
    }
    if err := boxInstance.Start(); err != nil {
        panic(err)
    }
    time.Sleep(10 * time.Second)
    if err := boxInstance.RemoveOutbound("direct"); err != nil {
        panic(err)
    }
    time.Sleep(10 * time.Second)
    if err := boxInstance.RemoveInbound("shadowsocks"); err != nil {
        panic(err)
    }
    time.Sleep(10 * time.Second)
    if err := boxInstance.Close(); err != nil {
        panic(err)
    }
}