mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 18:17:18 +10:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc2b0820a0 | ||
|
|
2c60eebc42 | ||
|
|
0ef1c78c0e | ||
|
|
7a1bc204b2 | ||
|
|
78f494831d | ||
|
|
d07c908e5d | ||
|
|
6126f8712b | ||
|
|
ea5c2446b2 | ||
|
|
c0fcd6afce |
21
.github/workflows/build.yml
vendored
21
.github/workflows/build.yml
vendored
@@ -25,8 +25,7 @@ on:
|
||||
- publish-android
|
||||
push:
|
||||
branches:
|
||||
- main-next
|
||||
- dev-next
|
||||
- oldstable
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}-${{ inputs.build }}
|
||||
@@ -287,7 +286,7 @@ jobs:
|
||||
path: "dist"
|
||||
build_android:
|
||||
name: Build Android
|
||||
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android'
|
||||
if: (github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android') && github.ref != 'refs/heads/oldstable'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- calculate_version
|
||||
@@ -323,12 +322,12 @@ jobs:
|
||||
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
- name: Checkout main branch
|
||||
if: github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
|
||||
if: github.ref == 'refs/heads/stable' && github.event_name != 'workflow_dispatch'
|
||||
run: |-
|
||||
cd clients/android
|
||||
git checkout main
|
||||
- name: Checkout dev branch
|
||||
if: github.ref == 'refs/heads/dev-next'
|
||||
if: github.ref == 'refs/heads/testing'
|
||||
run: |-
|
||||
cd clients/android
|
||||
git checkout dev
|
||||
@@ -367,7 +366,7 @@ jobs:
|
||||
path: 'dist'
|
||||
publish_android:
|
||||
name: Publish Android
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android'
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android' && github.ref != 'refs/heads/oldstable'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- calculate_version
|
||||
@@ -403,12 +402,12 @@ jobs:
|
||||
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
- name: Checkout main branch
|
||||
if: github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
|
||||
if: github.ref == 'refs/heads/stable' && github.event_name != 'workflow_dispatch'
|
||||
run: |-
|
||||
cd clients/android
|
||||
git checkout main
|
||||
- name: Checkout dev branch
|
||||
if: github.ref == 'refs/heads/dev-next'
|
||||
if: github.ref == 'refs/heads/testing'
|
||||
run: |-
|
||||
cd clients/android
|
||||
git checkout dev
|
||||
@@ -487,12 +486,12 @@ jobs:
|
||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
||||
- name: Checkout main branch
|
||||
if: matrix.if && github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
|
||||
if: matrix.if && github.ref == 'refs/heads/stable' && github.event_name != 'workflow_dispatch'
|
||||
run: |-
|
||||
cd clients/apple
|
||||
git checkout main
|
||||
- name: Checkout dev branch
|
||||
if: matrix.if && github.ref == 'refs/heads/dev-next'
|
||||
if: matrix.if && github.ref == 'refs/heads/testing'
|
||||
run: |-
|
||||
cd clients/apple
|
||||
git checkout dev
|
||||
@@ -578,7 +577,7 @@ jobs:
|
||||
-authenticationKeyID $ASC_KEY_ID \
|
||||
-authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
|
||||
- name: Publish to TestFlight
|
||||
if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch' && github.ref =='refs/heads/dev-next'
|
||||
if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch' && github.ref =='refs/heads/testing'
|
||||
run: |-
|
||||
go run -v ./cmd/internal/app_store_connect publish_testflight ${{ matrix.platform }}
|
||||
- name: Build image
|
||||
|
||||
14
.github/workflows/lint.yml
vendored
14
.github/workflows/lint.yml
vendored
@@ -3,18 +3,20 @@ name: Lint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- stable-next
|
||||
- main-next
|
||||
- dev-next
|
||||
- oldstable
|
||||
- stable
|
||||
- testing
|
||||
- unstable
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/lint.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- stable-next
|
||||
- main-next
|
||||
- dev-next
|
||||
- oldstable
|
||||
- stable
|
||||
- testing
|
||||
- unstable
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
Submodule clients/android updated: eb87216961...134402995e
@@ -145,3 +145,7 @@ type ParallelNetworkDialer interface {
|
||||
DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error)
|
||||
ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error)
|
||||
}
|
||||
|
||||
type PacketDialerWithDestination interface {
|
||||
ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error)
|
||||
}
|
||||
|
||||
@@ -240,8 +240,10 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
||||
if responseChecker != nil {
|
||||
var rejected bool
|
||||
// TODO: add accept_any rule and support to check response instead of addresses
|
||||
if response.Rcode != dns.RcodeSuccess || len(response.Answer) == 0 {
|
||||
if response.Rcode != dns.RcodeSuccess && response.Rcode != dns.RcodeNameError {
|
||||
rejected = true
|
||||
} else if len(response.Answer) == 0 {
|
||||
rejected = !responseChecker(nil)
|
||||
} else {
|
||||
rejected = !responseChecker(MessageToAddresses(response))
|
||||
}
|
||||
|
||||
@@ -273,13 +273,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte
|
||||
return action.Response(message), nil
|
||||
}
|
||||
}
|
||||
var responseCheck func(responseAddrs []netip.Addr) bool
|
||||
if rule != nil && rule.WithAddressLimit() {
|
||||
responseCheck = func(responseAddrs []netip.Addr) bool {
|
||||
metadata.DestinationAddresses = responseAddrs
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
}
|
||||
}
|
||||
responseCheck := addressLimitResponseCheck(rule, metadata)
|
||||
if dnsOptions.Strategy == C.DomainStrategyAsIS {
|
||||
dnsOptions.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
@@ -395,13 +389,7 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ
|
||||
goto response
|
||||
}
|
||||
}
|
||||
var responseCheck func(responseAddrs []netip.Addr) bool
|
||||
if rule != nil && rule.WithAddressLimit() {
|
||||
responseCheck = func(responseAddrs []netip.Addr) bool {
|
||||
metadata.DestinationAddresses = responseAddrs
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
}
|
||||
}
|
||||
responseCheck := addressLimitResponseCheck(rule, metadata)
|
||||
if dnsOptions.Strategy == C.DomainStrategyAsIS {
|
||||
dnsOptions.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
@@ -429,6 +417,18 @@ func isAddressQuery(message *mDNS.Msg) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func addressLimitResponseCheck(rule adapter.DNSRule, metadata *adapter.InboundContext) func(responseAddrs []netip.Addr) bool {
|
||||
if rule == nil || !rule.WithAddressLimit() {
|
||||
return nil
|
||||
}
|
||||
responseMetadata := *metadata
|
||||
return func(responseAddrs []netip.Addr) bool {
|
||||
checkMetadata := responseMetadata
|
||||
checkMetadata.DestinationAddresses = responseAddrs
|
||||
return rule.MatchAddressLimit(&checkMetadata)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) ClearCache() {
|
||||
r.client.ClearCache()
|
||||
if r.platformInterface != nil {
|
||||
|
||||
@@ -17,18 +17,43 @@ type Store struct {
|
||||
logger logger.Logger
|
||||
inet4Range netip.Prefix
|
||||
inet6Range netip.Prefix
|
||||
inet4Last netip.Addr
|
||||
inet6Last netip.Addr
|
||||
storage adapter.FakeIPStorage
|
||||
inet4Current netip.Addr
|
||||
inet6Current netip.Addr
|
||||
}
|
||||
|
||||
func NewStore(ctx context.Context, logger logger.Logger, inet4Range netip.Prefix, inet6Range netip.Prefix) *Store {
|
||||
return &Store{
|
||||
store := &Store{
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
inet4Range: inet4Range,
|
||||
inet6Range: inet6Range,
|
||||
}
|
||||
if inet4Range.IsValid() {
|
||||
store.inet4Last = broadcastAddress(inet4Range)
|
||||
}
|
||||
if inet6Range.IsValid() {
|
||||
store.inet6Last = broadcastAddress(inet6Range)
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
func broadcastAddress(prefix netip.Prefix) netip.Addr {
|
||||
addr := prefix.Addr()
|
||||
raw := addr.As16()
|
||||
bits := prefix.Bits()
|
||||
if addr.Is4() {
|
||||
bits += 96
|
||||
}
|
||||
for i := bits; i < 128; i++ {
|
||||
raw[i/8] |= 1 << (7 - i%8)
|
||||
}
|
||||
if addr.Is4() {
|
||||
return netip.AddrFrom4([4]byte(raw[12:]))
|
||||
}
|
||||
return netip.AddrFrom16(raw)
|
||||
}
|
||||
|
||||
func (s *Store) Start() error {
|
||||
@@ -46,10 +71,10 @@ func (s *Store) Start() error {
|
||||
s.inet6Current = metadata.Inet6Current
|
||||
} else {
|
||||
if s.inet4Range.IsValid() {
|
||||
s.inet4Current = s.inet4Range.Addr().Next().Next()
|
||||
s.inet4Current = s.inet4Range.Addr().Next()
|
||||
}
|
||||
if s.inet6Range.IsValid() {
|
||||
s.inet6Current = s.inet6Range.Addr().Next().Next()
|
||||
s.inet6Current = s.inet6Range.Addr().Next()
|
||||
}
|
||||
_ = storage.FakeIPReset()
|
||||
}
|
||||
@@ -83,7 +108,7 @@ func (s *Store) Create(domain string, isIPv6 bool) (netip.Addr, error) {
|
||||
return netip.Addr{}, E.New("missing IPv4 fakeip address range")
|
||||
}
|
||||
nextAddress := s.inet4Current.Next()
|
||||
if !s.inet4Range.Contains(nextAddress) {
|
||||
if nextAddress == s.inet4Last || !s.inet4Range.Contains(nextAddress) {
|
||||
nextAddress = s.inet4Range.Addr().Next().Next()
|
||||
}
|
||||
s.inet4Current = nextAddress
|
||||
@@ -93,7 +118,7 @@ func (s *Store) Create(domain string, isIPv6 bool) (netip.Addr, error) {
|
||||
return netip.Addr{}, E.New("missing IPv6 fakeip address range")
|
||||
}
|
||||
nextAddress := s.inet6Current.Next()
|
||||
if !s.inet6Range.Contains(nextAddress) {
|
||||
if nextAddress == s.inet6Last || !s.inet6Range.Contains(nextAddress) {
|
||||
nextAddress = s.inet6Range.Addr().Next().Next()
|
||||
}
|
||||
s.inet6Current = nextAddress
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
@@ -63,6 +64,9 @@ func dnsReadConfig(ctx context.Context, _ string) *dnsConfig {
|
||||
continue
|
||||
}
|
||||
dnsServerAddr = netip.AddrFrom16(sockaddr.Addr)
|
||||
if sockaddr.ZoneId != 0 {
|
||||
dnsServerAddr = dnsServerAddr.WithZone(strconv.FormatInt(int64(sockaddr.ZoneId), 10))
|
||||
}
|
||||
default:
|
||||
// Unexpected type.
|
||||
continue
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.12.24
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.12.23
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.12.22
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@@ -2,6 +2,7 @@ package clashapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -17,15 +18,15 @@ import (
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
|
||||
func connectionRouter(router adapter.Router, trafficManager *trafficontrol.Manager) http.Handler {
|
||||
func connectionRouter(ctx context.Context, router adapter.Router, trafficManager *trafficontrol.Manager) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getConnections(trafficManager))
|
||||
r.Get("/", getConnections(ctx, trafficManager))
|
||||
r.Delete("/", closeAllConnections(router, trafficManager))
|
||||
r.Delete("/{id}", closeConnection(trafficManager))
|
||||
return r
|
||||
}
|
||||
|
||||
func getConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
|
||||
func getConnections(ctx context.Context, trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Upgrade") != "websocket" {
|
||||
snapshot := trafficManager.Snapshot()
|
||||
@@ -67,7 +68,12 @@ func getConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseW
|
||||
|
||||
tick := time.NewTicker(time.Millisecond * time.Duration(interval))
|
||||
defer tick.Stop()
|
||||
for range tick.C {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-tick.C:
|
||||
}
|
||||
if err = sendSnapshot(); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -115,12 +115,12 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
||||
r.Use(authentication(options.Secret))
|
||||
r.Get("/", hello(options.ExternalUI != ""))
|
||||
r.Get("/logs", getLogs(logFactory))
|
||||
r.Get("/traffic", traffic(trafficManager))
|
||||
r.Get("/traffic", traffic(s.ctx, trafficManager))
|
||||
r.Get("/version", version)
|
||||
r.Mount("/configs", configRouter(s, logFactory))
|
||||
r.Mount("/proxies", proxyRouter(s, s.router))
|
||||
r.Mount("/rules", ruleRouter(s.router))
|
||||
r.Mount("/connections", connectionRouter(s.router, trafficManager))
|
||||
r.Mount("/connections", connectionRouter(s.ctx, s.router, trafficManager))
|
||||
r.Mount("/providers/proxies", proxyProviderRouter())
|
||||
r.Mount("/providers/rules", ruleProviderRouter())
|
||||
r.Mount("/script", scriptRouter())
|
||||
@@ -305,7 +305,7 @@ type Traffic struct {
|
||||
Down int64 `json:"down"`
|
||||
}
|
||||
|
||||
func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
|
||||
func traffic(ctx context.Context, trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var conn net.Conn
|
||||
if r.Header.Get("Upgrade") == "websocket" {
|
||||
@@ -326,7 +326,12 @@ func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter,
|
||||
defer tick.Stop()
|
||||
buf := &bytes.Buffer{}
|
||||
uploadTotal, downloadTotal := trafficManager.Total()
|
||||
for range tick.C {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-tick.C:
|
||||
}
|
||||
buf.Reset()
|
||||
uploadTotalNew, downloadTotalNew := trafficManager.Total()
|
||||
err := json.NewEncoder(buf).Encode(Traffic{
|
||||
|
||||
2
go.mod
2
go.mod
@@ -33,7 +33,7 @@ require (
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
||||
github.com/sagernet/sing-tun v0.7.11
|
||||
github.com/sagernet/sing-tun v0.7.12
|
||||
github.com/sagernet/sing-vmess v0.2.7
|
||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1
|
||||
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2
|
||||
|
||||
4
go.sum
4
go.sum
@@ -177,8 +177,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||
github.com/sagernet/sing-tun v0.7.11 h1:qB7jy8JKqXg73fYBsDkBSy4ulRSbLrFut0e+y+QPhqU=
|
||||
github.com/sagernet/sing-tun v0.7.11/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
|
||||
github.com/sagernet/sing-tun v0.7.12 h1:nmzIpxCpT8pfPqoNiTdXtxE0wbR42zgjkcOyZPppMGM=
|
||||
github.com/sagernet/sing-tun v0.7.12/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
|
||||
github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk=
|
||||
github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
|
||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
|
||||
|
||||
@@ -52,6 +52,8 @@ import (
|
||||
"github.com/sagernet/tailscale/wgengine/filter"
|
||||
)
|
||||
|
||||
var _ dialer.PacketDialerWithDestination = (*Endpoint)(nil)
|
||||
|
||||
func init() {
|
||||
version.SetVersion("sing-box " + C.Version)
|
||||
}
|
||||
@@ -386,19 +388,7 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
t.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
if destination.IsFqdn() {
|
||||
destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packetConn, _, err := N.ListenSerial(ctx, t, destination, destinationAddresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return packetConn, err
|
||||
}
|
||||
func (t *Endpoint) listenPacketWithAddress(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
addr4, addr6 := t.server.TailscaleIPs()
|
||||
bind := tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
@@ -424,6 +414,44 @@ func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
||||
return udpConn, nil
|
||||
}
|
||||
|
||||
func (t *Endpoint) ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error) {
|
||||
t.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
if destination.IsFqdn() {
|
||||
destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
|
||||
if err != nil {
|
||||
return nil, netip.Addr{}, err
|
||||
}
|
||||
var errors []error
|
||||
for _, address := range destinationAddresses {
|
||||
packetConn, packetErr := t.listenPacketWithAddress(ctx, M.SocksaddrFrom(address, destination.Port))
|
||||
if packetErr == nil {
|
||||
return packetConn, address, nil
|
||||
}
|
||||
errors = append(errors, packetErr)
|
||||
}
|
||||
return nil, netip.Addr{}, E.Errors(errors...)
|
||||
}
|
||||
packetConn, err := t.listenPacketWithAddress(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, netip.Addr{}, err
|
||||
}
|
||||
if destination.IsIP() {
|
||||
return packetConn, destination.Addr, nil
|
||||
}
|
||||
return packetConn, netip.Addr{}, nil
|
||||
}
|
||||
|
||||
func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
packetConn, destinationAddress, err := t.ListenPacketWithDestination(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if destinationAddress.IsValid() && destination != M.SocksaddrFrom(destinationAddress, destination.Port) {
|
||||
return bufio.NewNATPacketConn(bufio.NewPacketConn(packetConn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil
|
||||
}
|
||||
return packetConn, nil
|
||||
}
|
||||
|
||||
func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
||||
tsFilter := t.filter.Load()
|
||||
if tsFilter != nil {
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
var _ dialer.PacketDialerWithDestination = (*Endpoint)(nil)
|
||||
|
||||
func RegisterEndpoint(registry *endpoint.Registry) {
|
||||
endpoint.Register[option.WireGuardEndpointOptions](registry, C.TypeWireGuard, NewEndpoint)
|
||||
}
|
||||
@@ -195,18 +197,32 @@ func (w *Endpoint) DialContext(ctx context.Context, network string, destination
|
||||
return w.endpoint.DialContext(ctx, network, destination)
|
||||
}
|
||||
|
||||
func (w *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
func (w *Endpoint) ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error) {
|
||||
w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
if destination.IsFqdn() {
|
||||
destinationAddresses, err := w.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, netip.Addr{}, err
|
||||
}
|
||||
packetConn, _, err := N.ListenSerial(ctx, w.endpoint, destination, destinationAddresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return packetConn, err
|
||||
return N.ListenSerial(ctx, w.endpoint, destination, destinationAddresses)
|
||||
}
|
||||
return w.endpoint.ListenPacket(ctx, destination)
|
||||
packetConn, err := w.endpoint.ListenPacket(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, netip.Addr{}, err
|
||||
}
|
||||
if destination.IsIP() {
|
||||
return packetConn, destination.Addr, nil
|
||||
}
|
||||
return packetConn, netip.Addr{}, nil
|
||||
}
|
||||
|
||||
func (w *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
packetConn, destinationAddress, err := w.ListenPacketWithDestination(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if destinationAddress.IsValid() && destination != M.SocksaddrFrom(destinationAddress, destination.Port) {
|
||||
return bufio.NewNATPacketConn(bufio.NewPacketConn(packetConn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil
|
||||
}
|
||||
return packetConn, nil
|
||||
}
|
||||
|
||||
@@ -155,6 +155,8 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
|
||||
} else {
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
remotePacketConn, destinationAddress, err = dialer.ListenSerialNetworkPacket(ctx, this, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
||||
} else if packetDialer, withDestination := this.(dialer.PacketDialerWithDestination); withDestination {
|
||||
remotePacketConn, destinationAddress, err = packetDialer.ListenPacketWithDestination(ctx, metadata.Destination)
|
||||
} else {
|
||||
remotePacketConn, err = this.ListenPacket(ctx, metadata.Destination)
|
||||
}
|
||||
@@ -185,11 +187,16 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
|
||||
}
|
||||
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
|
||||
natConn.UpdateDestination(destinationAddress)
|
||||
} else if metadata.Destination != M.SocksaddrFrom(destinationAddress, metadata.Destination.Port) {
|
||||
if metadata.UDPDisableDomainUnmapping {
|
||||
remotePacketConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(remotePacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), originDestination)
|
||||
} else {
|
||||
remotePacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(remotePacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), originDestination)
|
||||
} else {
|
||||
destination := M.SocksaddrFrom(destinationAddress, metadata.Destination.Port)
|
||||
if metadata.Destination != destination {
|
||||
if metadata.UDPDisableDomainUnmapping {
|
||||
remotePacketConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(remotePacketConn), destination, originDestination)
|
||||
} else {
|
||||
remotePacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(remotePacketConn), destination, originDestination)
|
||||
}
|
||||
} else if metadata.RouteOriginalDestination.IsValid() && metadata.RouteOriginalDestination != metadata.Destination {
|
||||
remotePacketConn = bufio.NewDestinationNATPacketConn(bufio.NewPacketConn(remotePacketConn), metadata.Destination, metadata.RouteOriginalDestination)
|
||||
}
|
||||
}
|
||||
} else if metadata.RouteOriginalDestination.IsValid() && metadata.RouteOriginalDestination != metadata.Destination {
|
||||
|
||||
Reference in New Issue
Block a user