Files
2026-07-13 15:08:24 +08:00

39 lines
1.4 KiB
Go

package main
import (
"net"
"strings"
E "github.com/sagernet/sing/common/exceptions"
"github.com/tailscale/go-winio"
)
// libuv (Node net.connect) opens the client end with GENERIC_READ|GENERIC_WRITE and
// falls back to read-only/write-only opens on ERROR_ACCESS_DENIED (src/win/pipe.c,
// open_named_pipe), so the client principal needs the full GRGW grant.
const pipeSecurityDescriptor = `D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;AU)`
// winio's PipeConfig defaults to zero-quota pipe instances, where NPFS pends every
// WriteFile until the peer posts a consuming ReadFile. Node's http2 client never
// posts one when it opens a pipe immediately after destroying a previous pipe
// socket (the renderer host's deadline-abort-then-retry pattern), which deadlocks
// the gRPC handshake on the server's initial SETTINGS write and surfaces in the
// app as a connect timeout. Nonzero quotas let the handshake complete into the
// pipe buffer.
const (
pipeBufferSize = 65536
daemonPipePath = `\\.\pipe\ProtectedPrefix\Administrators\sing-box`
)
func listenEndpoint() (net.Listener, error) {
if socketPath != "" && !strings.EqualFold(socketPath, daemonPipePath) {
return nil, E.New("custom Windows daemon pipe paths are not supported")
}
return winio.ListenPipe(daemonPipePath, &winio.PipeConfig{
SecurityDescriptor: pipeSecurityDescriptor,
InputBufferSize: pipeBufferSize,
OutputBufferSize: pipeBufferSize,
})
}