Browse Source

Add protect path dialer option

世界 3 years ago
parent
commit
46f28a9de9
4 changed files with 61 additions and 0 deletions
  1. 1 0
      option/outbound.go
  2. 4 0
      outbound/dialer/default.go
  3. 47 0
      outbound/dialer/protect.go
  4. 9 0
      outbound/dialer/protect_stub.go

+ 1 - 0
option/outbound.go

@@ -67,6 +67,7 @@ func (h *Outbound) UnmarshalJSON(bytes []byte) error {
 type DialerOptions struct {
 	Detour          string                 `json:"detour,omitempty"`
 	BindInterface   string                 `json:"bind_interface,omitempty"`
+	ProtectPath     string                 `json:"protect_path,omitempty"`
 	RoutingMark     int                    `json:"routing_mark,omitempty"`
 	ReuseAddr       bool                   `json:"reuse_addr,omitempty"`
 	ConnectTimeout  int                    `json:"connect_timeout,omitempty"`

+ 4 - 0
outbound/dialer/default.go

@@ -32,6 +32,10 @@ func newDefault(options option.DialerOptions) N.Dialer {
 	if options.ReuseAddr {
 		listener.Control = control.Append(listener.Control, control.ReuseAddr())
 	}
+	if options.ProtectPath != "" {
+		dialer.Control = control.Append(dialer.Control, ProtectPath(options.ProtectPath))
+		listener.Control = control.Append(listener.Control, ProtectPath(options.ProtectPath))
+	}
 	if options.ConnectTimeout != 0 {
 		dialer.Timeout = time.Duration(options.ConnectTimeout) * time.Second
 	}

+ 47 - 0
outbound/dialer/protect.go

@@ -0,0 +1,47 @@
+//go:build android || with_protect
+
+package dialer
+
+import (
+	"syscall"
+
+	"github.com/sagernet/sing/common"
+	"github.com/sagernet/sing/common/control"
+	E "github.com/sagernet/sing/common/exceptions"
+)
+
+func sendAncillaryFileDescriptors(protectPath string, fileDescriptors []int) error {
+	socket, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
+	if err != nil {
+		return E.Cause(err, "open protect socket")
+	}
+	defer syscall.Close(socket)
+	err = syscall.Connect(socket, &syscall.SockaddrUnix{Name: protectPath})
+	if err != nil {
+		return E.Cause(err, "connect protect path")
+	}
+	oob := syscall.UnixRights(fileDescriptors...)
+	dummy := []byte{1}
+	err = syscall.Sendmsg(socket, dummy, oob, nil, 0)
+	if err != nil {
+		return err
+	}
+	n, err := syscall.Read(socket, dummy)
+	if err != nil {
+		return err
+	}
+	if n != 1 {
+		return E.New("failed to protect fd")
+	}
+	return nil
+}
+
+func ProtectPath(protectPath string) control.Func {
+	return func(network, address string, conn syscall.RawConn) error {
+		var innerErr error
+		err := conn.Control(func(fd uintptr) {
+			innerErr = sendAncillaryFileDescriptors(protectPath, []int{int(fd)})
+		})
+		return common.AnyError(innerErr, err)
+	}
+}

+ 9 - 0
outbound/dialer/protect_stub.go

@@ -0,0 +1,9 @@
+//go:build !android && !with_protect
+
+package dialer
+
+import "github.com/sagernet/sing/common/control"
+
+func ProtectPath(protectPath string) control.Func {
+	return nil
+}