mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-06-29 04:01:23 +00:00
85 lines
2.5 KiB
Go
85 lines
2.5 KiB
Go
package daemon
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"net"
|
|
"net/url"
|
|
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
"google.golang.org/grpc/metadata"
|
|
)
|
|
|
|
type RemoteClientOptions struct {
|
|
ServerURL string
|
|
Secret string
|
|
}
|
|
|
|
func (o RemoteClientOptions) ServerTarget() (string, credentials.TransportCredentials, error) {
|
|
if o.ServerURL == "" {
|
|
return "", nil, E.New("missing server URL")
|
|
}
|
|
serverURL, err := url.Parse(o.ServerURL)
|
|
if err != nil {
|
|
return "", nil, E.Cause(err, "invalid server URL: ", o.ServerURL)
|
|
}
|
|
var enableTLS bool
|
|
switch serverURL.Scheme {
|
|
case "http":
|
|
case "https":
|
|
enableTLS = true
|
|
default:
|
|
return "", nil, E.New("invalid server URL scheme: ", serverURL.Scheme, ", expected http or https")
|
|
}
|
|
host := serverURL.Hostname()
|
|
if host == "" {
|
|
return "", nil, E.New("missing host in server URL: ", o.ServerURL)
|
|
}
|
|
port := serverURL.Port()
|
|
if port == "" {
|
|
if enableTLS {
|
|
port = "443"
|
|
} else {
|
|
port = "80"
|
|
}
|
|
}
|
|
transportCredentials := insecure.NewCredentials()
|
|
if enableTLS {
|
|
transportCredentials = credentials.NewTLS(&tls.Config{ServerName: host})
|
|
}
|
|
return net.JoinHostPort(host, port), transportCredentials, nil
|
|
}
|
|
|
|
func NewRemoteClient(options RemoteClientOptions) (*grpc.ClientConn, error) {
|
|
target, transportCredentials, err := options.ServerTarget()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return grpc.NewClient(target,
|
|
grpc.WithTransportCredentials(transportCredentials),
|
|
grpc.WithUnaryInterceptor(NewClientAuthUnaryInterceptor(options.Secret)),
|
|
grpc.WithStreamInterceptor(NewClientAuthStreamInterceptor(options.Secret)),
|
|
)
|
|
}
|
|
|
|
func NewClientAuthUnaryInterceptor(secret string) grpc.UnaryClientInterceptor {
|
|
return func(ctx context.Context, method string, request, reply any, clientConn *grpc.ClientConn, invoker grpc.UnaryInvoker, options ...grpc.CallOption) error {
|
|
if secret != "" {
|
|
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+secret)
|
|
}
|
|
return invoker(ctx, method, request, reply, clientConn, options...)
|
|
}
|
|
}
|
|
|
|
func NewClientAuthStreamInterceptor(secret string) grpc.StreamClientInterceptor {
|
|
return func(ctx context.Context, desc *grpc.StreamDesc, clientConn *grpc.ClientConn, method string, streamer grpc.Streamer, options ...grpc.CallOption) (grpc.ClientStream, error) {
|
|
if secret != "" {
|
|
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+secret)
|
|
}
|
|
return streamer(ctx, desc, clientConn, method, options...)
|
|
}
|
|
}
|