mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 18:11:28 +00:00
Add set_system_proxy option for windows
This commit is contained in:
parent
e7d557fd9e
commit
238afda9da
13
common/wininet/wininet_stub.go
Normal file
13
common/wininet/wininet_stub.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package wininet
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func ClearSystemProxy() error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetSystemProxy(proxy string, bypass string) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
109
common/wininet/wininet_windows.go
Normal file
109
common/wininet/wininet_windows.go
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
package wininet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
modwininet = windows.NewLazySystemDLL("wininet.dll")
|
||||||
|
procInternetSetOptionW = modwininet.NewProc("InternetSetOptionW")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
internetOptionPerConnectionOption = 75
|
||||||
|
internetOptionSettingsChanged = 39
|
||||||
|
internetOptionRefresh = 37
|
||||||
|
internetOptionProxySettingsChanged = 95
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
internetPerConnFlags = 1
|
||||||
|
internetPerConnProxyServer = 2
|
||||||
|
internetPerConnProxyBypass = 3
|
||||||
|
internetPerConnAutoconfigUrl = 4
|
||||||
|
internetPerConnAutodiscoveryFlags = 5
|
||||||
|
internetPerConnAutoconfigSecondaryUrl = 6
|
||||||
|
internetPerConnAutoconfigReloadDelayMins = 7
|
||||||
|
internetPerConnAutoconfigLastDetectTime = 8
|
||||||
|
internetPerConnAutoconfigLastDetectUrl = 9
|
||||||
|
internetPerConnFlagsUi = 10
|
||||||
|
internetOptionProxyUsername = 43
|
||||||
|
internetOptionProxyPassword = 44
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
proxyTypeDirect = 1
|
||||||
|
proxyTypeProxy = 2
|
||||||
|
proxyTypeAutoProxyUrl = 4
|
||||||
|
proxyTypeAutoDetect = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
type internetPerConnOptionList struct {
|
||||||
|
dwSize uint32
|
||||||
|
pszConnection uintptr
|
||||||
|
dwOptionCount uint32
|
||||||
|
dwOptionError uint32
|
||||||
|
pOptions uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type internetPerConnOption struct {
|
||||||
|
dwOption uint32
|
||||||
|
value [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func internetSetOption(option uintptr, lpBuffer uintptr, dwBufferSize uintptr) error {
|
||||||
|
r0, _, err := syscall.SyscallN(procInternetSetOptionW.Addr(), 0, option, lpBuffer, dwBufferSize)
|
||||||
|
if r0 != 1 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setOptions(options ...internetPerConnOption) error {
|
||||||
|
var optionList internetPerConnOptionList
|
||||||
|
optionList.dwSize = uint32(unsafe.Sizeof(optionList))
|
||||||
|
optionList.dwOptionCount = uint32(len(options))
|
||||||
|
optionList.dwOptionError = 0
|
||||||
|
optionList.pOptions = uintptr(unsafe.Pointer(&options[0]))
|
||||||
|
err := internetSetOption(internetOptionPerConnectionOption, uintptr(unsafe.Pointer(&optionList)), uintptr(optionList.dwSize))
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("InternetSetOption(Direct)", err)
|
||||||
|
}
|
||||||
|
err = internetSetOption(internetOptionSettingsChanged, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("InternetSetOption(SettingsChanged)", err)
|
||||||
|
}
|
||||||
|
err = internetSetOption(internetOptionProxySettingsChanged, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("InternetSetOption(ProxySettingsChanged)", err)
|
||||||
|
}
|
||||||
|
err = internetSetOption(internetOptionRefresh, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("InternetSetOption(Refresh)", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearSystemProxy() error {
|
||||||
|
var flagsOption internetPerConnOption
|
||||||
|
flagsOption.dwOption = internetPerConnFlags
|
||||||
|
*((*uint32)(unsafe.Pointer(&flagsOption.value))) = proxyTypeDirect | proxyTypeAutoDetect
|
||||||
|
return setOptions(flagsOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetSystemProxy(proxy string, bypass string) error {
|
||||||
|
var flagsOption internetPerConnOption
|
||||||
|
flagsOption.dwOption = internetPerConnFlags
|
||||||
|
*((*uint32)(unsafe.Pointer(&flagsOption.value))) = proxyTypeProxy | proxyTypeDirect
|
||||||
|
var proxyOption internetPerConnOption
|
||||||
|
proxyOption.dwOption = internetPerConnProxyServer
|
||||||
|
*((*uintptr)(unsafe.Pointer(&proxyOption.value))) = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(proxy)))
|
||||||
|
var bypassOption internetPerConnOption
|
||||||
|
bypassOption.dwOption = internetPerConnProxyBypass
|
||||||
|
*((*uintptr)(unsafe.Pointer(&bypassOption.value))) = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(bypass)))
|
||||||
|
return setOptions(flagsOption, proxyOption, bypassOption)
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/wininet"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
@ -16,6 +17,7 @@ import (
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
|
@ -36,6 +38,10 @@ type myInboundAdapter struct {
|
||||||
packetHandler adapter.PacketHandler
|
packetHandler adapter.PacketHandler
|
||||||
packetUpstream any
|
packetUpstream any
|
||||||
|
|
||||||
|
// http mixed
|
||||||
|
|
||||||
|
setSystemProxy bool
|
||||||
|
|
||||||
// internal
|
// internal
|
||||||
|
|
||||||
tcpListener *net.TCPListener
|
tcpListener *net.TCPListener
|
||||||
|
@ -88,14 +94,24 @@ func (a *myInboundAdapter) Start() error {
|
||||||
go a.loopUDPOut()
|
go a.loopUDPOut()
|
||||||
a.logger.Info("udp server started at ", udpConn.LocalAddr())
|
a.logger.Info("udp server started at ", udpConn.LocalAddr())
|
||||||
}
|
}
|
||||||
|
if a.setSystemProxy {
|
||||||
|
err := wininet.SetSystemProxy(F.ToString("http://127.0.0.1:", M.SocksaddrFromNet(a.tcpListener.Addr()).Port), "local")
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "set system proxy")
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *myInboundAdapter) Close() error {
|
func (a *myInboundAdapter) Close() error {
|
||||||
return common.Close(
|
var err error
|
||||||
|
if a.setSystemProxy {
|
||||||
|
err = wininet.ClearSystemProxy()
|
||||||
|
}
|
||||||
|
return E.Errors(err, common.Close(
|
||||||
common.PtrOrNil(a.tcpListener),
|
common.PtrOrNil(a.tcpListener),
|
||||||
common.PtrOrNil(a.udpConn),
|
common.PtrOrNil(a.udpConn),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *myInboundAdapter) upstreamHandler(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapter {
|
func (a *myInboundAdapter) upstreamHandler(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapter {
|
||||||
|
|
|
@ -21,16 +21,17 @@ type HTTP struct {
|
||||||
authenticator auth.Authenticator
|
authenticator auth.Authenticator
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SimpleInboundOptions) *HTTP {
|
func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) *HTTP {
|
||||||
inbound := &HTTP{
|
inbound := &HTTP{
|
||||||
myInboundAdapter{
|
myInboundAdapter{
|
||||||
protocol: C.TypeHTTP,
|
protocol: C.TypeHTTP,
|
||||||
network: []string{C.NetworkTCP},
|
network: []string{C.NetworkTCP},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
listenOptions: options.ListenOptions,
|
listenOptions: options.ListenOptions,
|
||||||
|
setSystemProxy: options.SetSystemProxy,
|
||||||
},
|
},
|
||||||
auth.NewAuthenticator(options.Users),
|
auth.NewAuthenticator(options.Users),
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,16 +27,17 @@ type Mixed struct {
|
||||||
authenticator auth.Authenticator
|
authenticator auth.Authenticator
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SimpleInboundOptions) *Mixed {
|
func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) *Mixed {
|
||||||
inbound := &Mixed{
|
inbound := &Mixed{
|
||||||
myInboundAdapter{
|
myInboundAdapter{
|
||||||
protocol: C.TypeMixed,
|
protocol: C.TypeMixed,
|
||||||
network: []string{C.NetworkTCP},
|
network: []string{C.NetworkTCP},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
listenOptions: options.ListenOptions,
|
listenOptions: options.ListenOptions,
|
||||||
|
setSystemProxy: options.SetSystemProxy,
|
||||||
},
|
},
|
||||||
auth.NewAuthenticator(options.Users),
|
auth.NewAuthenticator(options.Users),
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ type Socks struct {
|
||||||
authenticator auth.Authenticator
|
authenticator auth.Authenticator
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SimpleInboundOptions) *Socks {
|
func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) *Socks {
|
||||||
inbound := &Socks{
|
inbound := &Socks{
|
||||||
myInboundAdapter{
|
myInboundAdapter{
|
||||||
protocol: C.TypeSocks,
|
protocol: C.TypeSocks,
|
||||||
|
|
|
@ -42,26 +42,30 @@ func (f Formatter) Format(ctx context.Context, level Level, tag string, message
|
||||||
id, hasId = IDFromContext(ctx)
|
id, hasId = IDFromContext(ctx)
|
||||||
}
|
}
|
||||||
if hasId {
|
if hasId {
|
||||||
var color aurora.Color
|
if !f.DisableColors {
|
||||||
color = aurora.Color(uint8(id))
|
var color aurora.Color
|
||||||
color %= 215
|
color = aurora.Color(uint8(id))
|
||||||
row := uint(color / 36)
|
color %= 215
|
||||||
column := uint(color % 36)
|
row := uint(color / 36)
|
||||||
|
column := uint(color % 36)
|
||||||
|
|
||||||
var r, g, b float32
|
var r, g, b float32
|
||||||
r = float32(row * 51)
|
r = float32(row * 51)
|
||||||
g = float32(column / 6 * 51)
|
g = float32(column / 6 * 51)
|
||||||
b = float32((column % 6) * 51)
|
b = float32((column % 6) * 51)
|
||||||
luma := 0.2126*r + 0.7152*g + 0.0722*b
|
luma := 0.2126*r + 0.7152*g + 0.0722*b
|
||||||
if luma < 60 {
|
if luma < 60 {
|
||||||
row = 5 - row
|
row = 5 - row
|
||||||
column = 35 - column
|
column = 35 - column
|
||||||
color = aurora.Color(row*36 + column)
|
color = aurora.Color(row*36 + column)
|
||||||
|
}
|
||||||
|
color += 16
|
||||||
|
color = color << 16
|
||||||
|
color |= 1 << 14
|
||||||
|
message = F.ToString("[", aurora.Colorize(id, color).String(), "] ", message)
|
||||||
|
} else {
|
||||||
|
message = F.ToString("[", id, "] ", message)
|
||||||
}
|
}
|
||||||
color += 16
|
|
||||||
color = color << 16
|
|
||||||
color |= 1 << 14
|
|
||||||
message = F.ToString("[", aurora.Colorize(id, color).String(), "] ", message)
|
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case f.DisableTimestamp:
|
case f.DisableTimestamp:
|
||||||
|
|
|
@ -13,9 +13,9 @@ type _Inbound struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Tag string `json:"tag,omitempty"`
|
Tag string `json:"tag,omitempty"`
|
||||||
DirectOptions DirectInboundOptions `json:"-"`
|
DirectOptions DirectInboundOptions `json:"-"`
|
||||||
SocksOptions SimpleInboundOptions `json:"-"`
|
SocksOptions SocksInboundOptions `json:"-"`
|
||||||
HTTPOptions SimpleInboundOptions `json:"-"`
|
HTTPOptions HTTPMixedInboundOptions `json:"-"`
|
||||||
MixedOptions SimpleInboundOptions `json:"-"`
|
MixedOptions HTTPMixedInboundOptions `json:"-"`
|
||||||
ShadowsocksOptions ShadowsocksInboundOptions `json:"-"`
|
ShadowsocksOptions ShadowsocksInboundOptions `json:"-"`
|
||||||
TunOptions TunInboundOptions `json:"-"`
|
TunOptions TunInboundOptions `json:"-"`
|
||||||
}
|
}
|
||||||
|
@ -97,16 +97,28 @@ type ListenOptions struct {
|
||||||
InboundOptions
|
InboundOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
type SimpleInboundOptions struct {
|
type SocksInboundOptions struct {
|
||||||
ListenOptions
|
ListenOptions
|
||||||
Users []auth.User `json:"users,omitempty"`
|
Users []auth.User `json:"users,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o SimpleInboundOptions) Equals(other SimpleInboundOptions) bool {
|
func (o SocksInboundOptions) Equals(other SocksInboundOptions) bool {
|
||||||
return o.ListenOptions == other.ListenOptions &&
|
return o.ListenOptions == other.ListenOptions &&
|
||||||
common.ComparableSliceEquals(o.Users, other.Users)
|
common.ComparableSliceEquals(o.Users, other.Users)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HTTPMixedInboundOptions struct {
|
||||||
|
ListenOptions
|
||||||
|
Users []auth.User `json:"users,omitempty"`
|
||||||
|
SetSystemProxy bool `json:"set_system_proxy,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o HTTPMixedInboundOptions) Equals(other HTTPMixedInboundOptions) bool {
|
||||||
|
return o.ListenOptions == other.ListenOptions &&
|
||||||
|
common.ComparableSliceEquals(o.Users, other.Users) &&
|
||||||
|
o.SetSystemProxy == other.SetSystemProxy
|
||||||
|
}
|
||||||
|
|
||||||
type DirectInboundOptions struct {
|
type DirectInboundOptions struct {
|
||||||
ListenOptions
|
ListenOptions
|
||||||
Network NetworkList `json:"network,omitempty"`
|
Network NetworkList `json:"network,omitempty"`
|
||||||
|
|
|
@ -5,7 +5,7 @@ go 1.18
|
||||||
require (
|
require (
|
||||||
github.com/docker/docker v20.10.17+incompatible
|
github.com/docker/docker v20.10.17+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91
|
github.com/sagernet/sing v0.0.0-20220714062657-6685f65aac21
|
||||||
github.com/sagernet/sing-box v0.0.0
|
github.com/sagernet/sing-box v0.0.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.0
|
||||||
|
|
|
@ -52,8 +52,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91 h1:fYsRChEViZHDvrOLp7fbswYCH3txaVyAl1zB0cnSNlc=
|
github.com/sagernet/sing v0.0.0-20220714062657-6685f65aac21 h1:PaX9VR0gVUagiRHwp9imiVZ9VW169WoEyToJg3UCKBI=
|
||||||
github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
|
github.com/sagernet/sing v0.0.0-20220714062657-6685f65aac21/go.mod h1:wbwi++q4pI7qbFYMbteUOakZUBdc4NmL+OQ08C3hTqc=
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 h1:oHbOmq1WS0XaZmXp6WpxzyB2xeyRIA1/L8EJKuNntfY=
|
github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 h1:oHbOmq1WS0XaZmXp6WpxzyB2xeyRIA1/L8EJKuNntfY=
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk=
|
github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
|
||||||
|
|
|
@ -93,7 +93,7 @@ func testShadowsocksOutboundWithShadowsocksRust(t *testing.T, method string, pas
|
||||||
Inbounds: []option.Inbound{
|
Inbounds: []option.Inbound{
|
||||||
{
|
{
|
||||||
Type: C.TypeMixed,
|
Type: C.TypeMixed,
|
||||||
MixedOptions: option.SimpleInboundOptions{
|
MixedOptions: option.HTTPMixedInboundOptions{
|
||||||
ListenOptions: option.ListenOptions{
|
ListenOptions: option.ListenOptions{
|
||||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
ListenPort: clientPort,
|
ListenPort: clientPort,
|
||||||
|
@ -131,7 +131,7 @@ func testShadowsocksSelf(t *testing.T, method string, password string) {
|
||||||
{
|
{
|
||||||
Type: C.TypeMixed,
|
Type: C.TypeMixed,
|
||||||
Tag: "mixed-in",
|
Tag: "mixed-in",
|
||||||
MixedOptions: option.SimpleInboundOptions{
|
MixedOptions: option.HTTPMixedInboundOptions{
|
||||||
ListenOptions: option.ListenOptions{
|
ListenOptions: option.ListenOptions{
|
||||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
ListenPort: clientPort,
|
ListenPort: clientPort,
|
||||||
|
|
Loading…
Reference in a new issue