Przeglądaj źródła

Add set_system_proxy option for windows

世界 3 lat temu
rodzic
commit
238afda9da

+ 13 - 0
common/wininet/wininet_stub.go

@@ -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 - 0
common/wininet/wininet_windows.go

@@ -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)
+}

+ 18 - 2
inbound/default.go

@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/wininet"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -16,6 +17,7 @@ import (
 	"github.com/sagernet/sing/common"
 	"github.com/sagernet/sing/common/buf"
 	E "github.com/sagernet/sing/common/exceptions"
+	F "github.com/sagernet/sing/common/format"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 
@@ -36,6 +38,10 @@ type myInboundAdapter struct {
 	packetHandler  adapter.PacketHandler
 	packetUpstream any
 
+	// http mixed
+
+	setSystemProxy bool
+
 	// internal
 
 	tcpListener          *net.TCPListener
@@ -88,14 +94,24 @@ func (a *myInboundAdapter) Start() error {
 		go a.loopUDPOut()
 		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
 }
 
 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.udpConn),
-	)
+	))
 }
 
 func (a *myInboundAdapter) upstreamHandler(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapter {

+ 9 - 8
inbound/http.go

@@ -21,16 +21,17 @@ type HTTP struct {
 	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{
 		myInboundAdapter{
-			protocol:      C.TypeHTTP,
-			network:       []string{C.NetworkTCP},
-			ctx:           ctx,
-			router:        router,
-			logger:        logger,
-			tag:           tag,
-			listenOptions: options.ListenOptions,
+			protocol:       C.TypeHTTP,
+			network:        []string{C.NetworkTCP},
+			ctx:            ctx,
+			router:         router,
+			logger:         logger,
+			tag:            tag,
+			listenOptions:  options.ListenOptions,
+			setSystemProxy: options.SetSystemProxy,
 		},
 		auth.NewAuthenticator(options.Users),
 	}

+ 9 - 8
inbound/mixed.go

@@ -27,16 +27,17 @@ type Mixed struct {
 	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{
 		myInboundAdapter{
-			protocol:      C.TypeMixed,
-			network:       []string{C.NetworkTCP},
-			ctx:           ctx,
-			router:        router,
-			logger:        logger,
-			tag:           tag,
-			listenOptions: options.ListenOptions,
+			protocol:       C.TypeMixed,
+			network:        []string{C.NetworkTCP},
+			ctx:            ctx,
+			router:         router,
+			logger:         logger,
+			tag:            tag,
+			listenOptions:  options.ListenOptions,
+			setSystemProxy: options.SetSystemProxy,
 		},
 		auth.NewAuthenticator(options.Users),
 	}

+ 1 - 1
inbound/socks.go

@@ -20,7 +20,7 @@ type Socks struct {
 	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{
 		myInboundAdapter{
 			protocol:      C.TypeSocks,

+ 22 - 18
log/format.go

@@ -42,26 +42,30 @@ func (f Formatter) Format(ctx context.Context, level Level, tag string, message
 		id, hasId = IDFromContext(ctx)
 	}
 	if hasId {
-		var color aurora.Color
-		color = aurora.Color(uint8(id))
-		color %= 215
-		row := uint(color / 36)
-		column := uint(color % 36)
+		if !f.DisableColors {
+			var color aurora.Color
+			color = aurora.Color(uint8(id))
+			color %= 215
+			row := uint(color / 36)
+			column := uint(color % 36)
 
-		var r, g, b float32
-		r = float32(row * 51)
-		g = float32(column / 6 * 51)
-		b = float32((column % 6) * 51)
-		luma := 0.2126*r + 0.7152*g + 0.0722*b
-		if luma < 60 {
-			row = 5 - row
-			column = 35 - column
-			color = aurora.Color(row*36 + column)
+			var r, g, b float32
+			r = float32(row * 51)
+			g = float32(column / 6 * 51)
+			b = float32((column % 6) * 51)
+			luma := 0.2126*r + 0.7152*g + 0.0722*b
+			if luma < 60 {
+				row = 5 - row
+				column = 35 - 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 {
 	case f.DisableTimestamp:

+ 17 - 5
option/inbound.go

@@ -13,9 +13,9 @@ type _Inbound struct {
 	Type               string                    `json:"type"`
 	Tag                string                    `json:"tag,omitempty"`
 	DirectOptions      DirectInboundOptions      `json:"-"`
-	SocksOptions       SimpleInboundOptions      `json:"-"`
-	HTTPOptions        SimpleInboundOptions      `json:"-"`
-	MixedOptions       SimpleInboundOptions      `json:"-"`
+	SocksOptions       SocksInboundOptions       `json:"-"`
+	HTTPOptions        HTTPMixedInboundOptions   `json:"-"`
+	MixedOptions       HTTPMixedInboundOptions   `json:"-"`
 	ShadowsocksOptions ShadowsocksInboundOptions `json:"-"`
 	TunOptions         TunInboundOptions         `json:"-"`
 }
@@ -97,16 +97,28 @@ type ListenOptions struct {
 	InboundOptions
 }
 
-type SimpleInboundOptions struct {
+type SocksInboundOptions struct {
 	ListenOptions
 	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 &&
 		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 {
 	ListenOptions
 	Network         NetworkList `json:"network,omitempty"`

+ 1 - 1
test/go.mod

@@ -5,7 +5,7 @@ go 1.18
 require (
 	github.com/docker/docker v20.10.17+incompatible
 	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/sirupsen/logrus v1.8.1
 	github.com/stretchr/testify v1.8.0

+ 2 - 2
test/go.sum

@@ -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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 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-20220712060558-029ab1ce4f91/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
+github.com/sagernet/sing v0.0.0-20220714062657-6685f65aac21 h1:PaX9VR0gVUagiRHwp9imiVZ9VW169WoEyToJg3UCKBI=
+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/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=

+ 2 - 2
test/shadowsocks_test.go

@@ -93,7 +93,7 @@ func testShadowsocksOutboundWithShadowsocksRust(t *testing.T, method string, pas
 		Inbounds: []option.Inbound{
 			{
 				Type: C.TypeMixed,
-				MixedOptions: option.SimpleInboundOptions{
+				MixedOptions: option.HTTPMixedInboundOptions{
 					ListenOptions: option.ListenOptions{
 						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
 						ListenPort: clientPort,
@@ -131,7 +131,7 @@ func testShadowsocksSelf(t *testing.T, method string, password string) {
 			{
 				Type: C.TypeMixed,
 				Tag:  "mixed-in",
-				MixedOptions: option.SimpleInboundOptions{
+				MixedOptions: option.HTTPMixedInboundOptions{
 					ListenOptions: option.ListenOptions{
 						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
 						ListenPort: clientPort,