Compare commits

...
Author SHA1 Message Date
patternihaandClaude Opus 5.5 9638715047 TUN inbound's autoSystemWfpBlockLeak: A CIDR in front of any value instead of "ipv4:" and "ipv6:"; Ports alone for TCP and UDP
A CIDR in front of any value limits it to its addresses, an IPv6 one in
brackets, e.g. "1.1.1.0/24:53", "[2001:db8::/32]:tcp/853" or
"[::/0]:all": "0.0.0.0/0:" and "[::/0]:" take the place of "ipv4:" and
"ipv6:". Like autoSystemRoutingTable, it takes CIDRs only, a single
address as "8.8.8.8/32", and no GeoIP.

"<port>" or "<low>-<high>" alone stands for TCP and UDP, so "dns" is
"53", "853" and "windnsclient" now.

The filters follow:
- One filter per layer permits what goes through the TUN, instead of one
  for each value.
- "all" and "misconfigtun" outweigh the local network that "allexceptlan"
  leaves open, so with it, "192.168.1.5/32:all" still blocks that address.
- The DNS Client service's mDNS and LLMNR stay allowed on the local network
  only, and only while no other value blocks them.
- The warning about blocked DNS servers only names those a value blocks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:21:31 +03:30
patternihaandClaude Opus 5.5 f5cc086d6c TUN inbound: README.md says "ipv4:allexceptlan" includes "ipv4:dns" only
Add "dns" to block DNS over IPv6 as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 18:10:11 +03:30
patternihaandClaude Opus 5.5 6b45eadf45 TUN inbound: Recommend at least "dns" for autoSystemWfpBlockLeak in README.md, and shorten it
README.md now recommends at least "dns" to block DNS leaks, and lists the
values of autoSystemWfpBlockLeak more briefly, "dns" first. It also lists
FreeBSD among the systems, and mentions that on Linux, as on Android, the
TUN can be a file descriptor from xray.tun.fd (XRAY_TUN_FD).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 17:53:27 +03:30
patternihaandClaude Opus 5.5 c684201cd8 TUN inbound's autoSystemWfpBlockLeak: "ipv4:" or "ipv6:" in front of any value instead of "tcpv4" and the like
"ipv4:" or "ipv6:" in front of any value limits it to that IP version,
e.g. "ipv4:udp/53", "ipv6:all", "ipv4:dns" or "ipv6:misconfigtun".
"tcpv4", "udpv6", "icmpv4" and the like are gone. Values that only differ
in the IP version become one: "ipv4:udp/53" with "ipv6:udp/53" is
"udp/53".

"all" overrides "allexceptlan" per IP version, and "all" over one IP
version needs dns, as port 53 over one does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 17:52:23 +03:30
patternihaandClaude Opus 5.5 2218fec173 TUN inbound's autoSystemWfpBlockLeak: Add "tcpv4", "tcpv6", "udpv4", "udpv6", "icmpv4" and "icmpv6"
"v4" or "v6" after the protocol limits a value to one IP version, ports
written the same way, e.g. "tcpv4/853" or "udpv6/10-20"; "icmpv4" is ICMP
and "icmpv6" is ICMPv6. Their filters go to the ALE layers of that version
only. "tcp", "udp" and "icmp" keep covering both.

Anything covering TCP or UDP port 53 in either IP version, like
"udpv4/53", still needs dns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:09:55 +03:30
patternihaandClaude Opus 5.5 9ffa0f0552 TUN inbound's autoSystemWfpBlockLeak: Drop "/0"; Ports have to be 1-65535
"tcp/0" and "udp/0" no longer stand for all ports, which "tcp" and "udp"
already say: no syntactic sugar. Port 0 is rejected like any port outside
1-65535. Ranges keep being checked strictly: both ends within 1-65535, the
left one not greater than the right one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 12:49:56 +03:30
patternihaandClaude Opus 5.5 2933b0c8d5 TUN inbound: Don't warn that loopback DNS servers are blocked
With autoSystemWfpBlockLeak, loopback now always stays allowed, so a DNS
server in dns like 127.0.0.1, a local resolver, stays reachable although
it lies in neither gateway nor autoSystemRoutingTable. Leave loopback
servers out of the warning that queries to them cannot go through the TUN
and are blocked, and say so in the README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 12:27:26 +03:30
patternihaandClaude Opus 5.5 bb4cd4b6d3 TUN inbound's autoSystemWfpBlockLeak: Add "allexceptlan"
"allexceptlan" is "all" but for the local network: traffic to and from
private (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7), link-local
(169.254.0.0/16, fe80::/10), multicast (224.0.0.0/4, ff00::/8) and broadcast
(255.255.255.255) addresses stays allowed, so that printers, the router's
settings page, or casting to a TV keep working while nothing else leaves
outside the TUN.

It includes "dns": a resolver on the local network, like the router's,
would forward the queries outside the TUN, so DNS stays blocked there. The
LAN permit sits below the other values, like the permit through the TUN, so
they still apply on the local network, e.g. "tcp/445" blocks Windows file
sharing with it. The DNS Client service keeps mDNS and LLMNR, to the local
network's addresses only. "all" overrides it.

The addresses are matched with FWP_V4_ADDR_MASK and FWP_V6_ADDR_MASK
conditions on the remote address, which match when any of them does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 11:50:18 +03:30
patternihaandClaude Opus 5.5 468f745fc2 TUN inbound's autoSystemWfpBlockLeak: Add "all", "tcp", "udp", "icmp" and port ranges; Always allow loopback, DHCP and IPv6 neighbor discovery
autoSystemWfpBlockLeak now also takes:

- "all": everything outside the TUN, IPv4 and IPv6, in both directions, so
  nothing but Xray leaves except through the TUN.
- "tcp", "udp": all ports of that protocol, also written "tcp/0", "udp/0".
- "tcp/<low>-<high>", "udp/<low>-<high>": a range of ports, matched with
  FWP_MATCH_RANGE, as two conditions on the same field would match when
  either does.
- "icmp": ICMP and ICMPv6, e.g. ping. It takes no port: in ICMP filters the
  local port holds the type, and "icmp/0" would read as the echo reply.

Values are normalized: "tcp/0" and "tcp/1-65535" become "tcp", and
"tcp/53-53" becomes "tcp/53".

Whatever is blocked, loopback, which cannot leave the system, and what
Windows itself needs on the local link now always stay allowed: DHCP, DHCPv6,
and IPv6 neighbor and multicast listener discovery. These used to be allowed
only along with "misconfigtun", so "udp" or "icmp" would otherwise have cut
DHCP or IPv6 neighbor discovery, and "tcp" any local proxy, Xray's own
inbounds included. Loopback being allowed means that with "dns", a local
resolver on 127.0.0.1:53 now works for other programs (its own queries to
port 53 or 853 outside the TUN stay blocked), and the DNS Client service
may use it.

"all" reuses the filters of "misconfigtun", which now also let everything
through the TUN, for both IP versions whatever the routes.

On other TUN options: keeping the system's DNS inside the TUN, with "all",
"dns", "windnsclient", or anything covering TCP or UDP port 53 ("udp",
"tcp/50-60", ...), needs dns, as Windows can no longer reach the DNS
servers of the other interfaces; the config is rejected otherwise. Like the
other values, the new ones need autoSystemRoutingTable. The filters do not
need autoOutboundsInterface, as Xray is allowed by its app ID; it still
keeps Xray's own connections out of the TUN's routes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 11:13:10 +03:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
7c8dbcb4bb Bump google.golang.org/grpc from 1.84.0 to 1.85.0 (#7311)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.84.0 to 1.85.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.84.0...v1.85.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.85.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-02 02:03:49 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8ab161a2b0 Bump golang.org/x/net from 0.61.0 to 0.62.0 (#7312)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.61.0 to 0.62.0.
- [Commits](https://github.com/golang/net/compare/v0.61.0...v0.62.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.62.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-02 02:03:35 +00:00
RPRXandGitHub a4a3dff5b2 Xray-core v26.10.31
Sponsor & Donation & NFTs: https://github.com/XTLS/Xray-core/issues/3668
Project X Channel: https://t.me/projectXtls

Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

VLESS Post-Quantum Encryption: https://github.com/XTLS/Xray-core/pull/5067
VLESS NFT: https://opensea.io/collection/vless

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-10-31 13:20:19 +00:00
6cdd323ad8 TUN inbound's autoSystemWfpBlockLeak: Support "tcp/port" and "udp/port"; "dns" covers "tcp/853" and "udp/853" as well (#7288)
https://github.com/XTLS/Xray-core/pull/6853#issuecomment-6492838227
https://github.com/XTLS/Xray-core/pull/7288#issuecomment-6507229727
https://github.com/XTLS/Xray-core/pull/7288#issuecomment-6516762634

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 13:16:31 +00:00
316e07a538 XHTTP server: Add h2Params (#7295)
https://github.com/XTLS/Xray-core/pull/7282#issuecomment-6523157944
https://github.com/XTLS/Xray-core/pull/7295#issuecomment-6543501751

Closes https://github.com/XTLS/Xray-core/issues/6268

---------

Co-authored-by: pqhaz3925 <195911022+pqhaz3925@users.noreply.github.com>
2026-10-31 12:52:14 +00:00
MeowandRPRX 0c3402d778 Geodata: Support streaming gzip decompression when downloading assets (#7294)
https://github.com/XTLS/Xray-core/pull/7294#issuecomment-6542074150
2026-10-31 11:56:59 +00:00
bc7c895278 HTTP inbound: Forward short 100 Continue responses promptly (#7298)
https://github.com/XTLS/Xray-core/pull/7298#issuecomment-6542585254

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-10-31 11:29:02 +00:00
konstpicandRPRX e7a69fa85a XHTTP server: Accept OPTIONS as uplinkHTTPMethod (#7290)
https://github.com/XTLS/Xray-core/pull/7290#issuecomment-6505845186
2026-10-31 08:43:57 +00:00
9b8190f52c QUIC sniffer: Don't accept ClientHello with gaps in CRYPTO data (#7292)
https://github.com/XTLS/Xray-core/pull/7292#pullrequestreview-5712194488

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-10-31 08:31:30 +00:00
6ca6d8a693 Commands: Fix xray api bi balancer1 balancer2 (#7299)
https://github.com/XTLS/Xray-core/pull/7279#issuecomment-6468250523

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-10-31 07:21:32 +00:00
风扇滑翔翼andGitHub d1140de273 Chore: Regenerate *.pb.go files (#7287)
https://github.com/XTLS/Xray-core/pull/7263#issuecomment-6464669635
2026-10-31 07:13:55 +00:00
风扇滑翔翼andGitHub 6ea0ab5d3a DefaultListener: Disable MPTCP by default (#7291)
Fixes https://github.com/XTLS/Xray-core/pull/7291#issuecomment-6540309985
2026-10-31 07:05:32 +00:00
111 changed files with 1336 additions and 439 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/commander/config.proto
package commander
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/dispatcher/config.proto
package dispatcher
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/dns/config.proto
package dns
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/dns/fakedns/fakedns.proto
package fakedns
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/geodata/config.proto
package geodata
+18 -1
View File
@@ -1,6 +1,8 @@
package geodata
import (
"bufio"
"compress/gzip"
"context"
"crypto/tls"
go_errors "errors"
@@ -197,7 +199,22 @@ func (d *downloader) fetch(rawURL string, writer io.Writer) error {
return errors.New("geodata download exceeds 256 MiB")
}
n, err := io.Copy(writer, io.LimitReader(resp.Body, maxDownloadSize+1))
br := bufio.NewReader(resp.Body)
header, err := br.Peek(2)
if err != nil && err != io.EOF {
return err
}
var body io.Reader = br
if len(header) == 2 && header[0] == 0x1f && header[1] == 0x8b {
zr, err := gzip.NewReader(br)
if err != nil {
return err
}
defer zr.Close()
body = zr
}
n, err := io.Copy(writer, io.LimitReader(body, maxDownloadSize+1))
if err != nil {
return err
}
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/log/command/config.proto
package command
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.5
// - protoc-gen-go-grpc v1.6.2
// - protoc (unknown)
// source: app/log/command/config.proto
package command
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/log/config.proto
package log
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/metrics/config.proto
package metrics
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/observatory/burst/config.proto
package burst
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/observatory/command/command.proto
package command
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.5
// - protoc-gen-go-grpc v1.6.2
// - protoc (unknown)
// source: app/observatory/command/command.proto
package command
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/observatory/config.proto
package observatory
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/policy/config.proto
package policy
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/proxyman/command/command.proto
package command
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.5
// - protoc-gen-go-grpc v1.6.2
// - protoc (unknown)
// source: app/proxyman/command/command.proto
package command
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/proxyman/config.proto
package proxyman
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/reverse/config.proto
package reverse
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/router/command/command.proto
package command
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.5
// - protoc-gen-go-grpc v1.6.2
// - protoc (unknown)
// source: app/router/command/command.proto
package command
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/router/config.proto
package router
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/stats/command/command.proto
package command
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.5
// - protoc-gen-go-grpc v1.6.2
// - protoc (unknown)
// source: app/stats/command/command.proto
package command
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/stats/config.proto
package stats
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: app/version/config.proto
package version
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: common/geodata/geodat.proto
package geodata
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: common/log/log.proto
package log
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: common/net/address.proto
package net
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: common/net/destination.proto
package net
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: common/net/network.proto
package net
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: common/net/port.proto
package net
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: common/protocol/headers.proto
package protocol
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: common/protocol/server_spec.proto
package protocol
+4
View File
@@ -101,6 +101,10 @@ func ReadClientHello(data []byte, h *SniffHeader) error {
return errNotClientHello
}
if nameType == 0 {
// RFC 6066 requires a non-empty HostName.
if nameLen == 0 {
return errNotClientHello
}
// QUIC separated across packets
// May cause the serverName to be incomplete
b := byte(0)
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: common/protocol/user.proto
package protocol
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: common/serial/typed_message.proto
package serial
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: core/config.proto
package core
+1 -1
View File
@@ -20,7 +20,7 @@ import (
var (
Version_x byte = 26
Version_y byte = 10
Version_z byte = 29
Version_z byte = 31
)
var (
+5 -5
View File
@@ -26,14 +26,14 @@ require (
golang.org/x/crypto v0.58.0
golang.org/x/crypto/x509roots/fallback v0.0.0-20261020153306-9f2b28d4331a
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
golang.org/x/net v0.61.0
golang.org/x/net v0.62.0
golang.org/x/sync v0.24.0
golang.org/x/sys v0.49.0
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
golang.zx2c4.com/wireguard/windows v1.1.1
google.golang.org/grpc v1.84.0
google.golang.org/protobuf v1.36.11
google.golang.org/grpc v1.85.0
google.golang.org/protobuf v1.36.12
h12.io/socks v1.0.3
layeh.com/gopher-luar v1.0.11
lukechampine.com/blake3 v1.4.1
@@ -58,8 +58,8 @@ require (
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/text v0.43.0 // indirect
golang.org/x/tools v0.51.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0 // indirect
)
+8 -10
View File
@@ -109,8 +109,8 @@ golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw=
golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
golang.org/x/net v0.62.0 h1:18ELalfM5+XNRwzM2nYqXsZQ9ByS1uhj2O/U7+5jqRY=
golang.org/x/net v0.62.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
@@ -150,14 +150,12 @@ golang.zx2c4.com/wireguard/windows v1.1.1 h1:8/H97U1v1PNDNcBsMZgU3KFuND9MQdTsU2N
golang.zx2c4.com/wireguard/windows v1.1.1/go.mod h1:+fbT3FFdX4zzYDLwJh5+HPEcNN/3HyNdzhNSVsQM+zs=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 h1:qEHAMpSaUhtD0p3NbEEI83HwNGFxEwaSJ1G9PLnCBZE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0=
google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.0 h1:6Al3kEFFP9VJhRz3DID6quisgPnTeZVr4lep9kkxdPA=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.0/go.mod h1:QLvsjh0OIR0TYBeiu2bkWGTJBUNQ64st52iWj/yA93I=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 h1:utmqiUzZAEINbLmRyAt1QjVkMyLQ+lvIwCpLaB1rZfw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.85.0 h1:BPBCfuVRQ1qvBhKDOq9uvQllMvO59rKsYhXpp7Dd8vw=
google.golang.org/grpc v1.85.0/go.mod h1:0rQrOD58yjal/0EaVbGBANindixsCZNQPDV3VKtaKuQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 h1:rgSNvqscFZ1JgV/4wH5GOsZFSFkR2Eua9As3KIr2LlM=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2/go.mod h1:iMEtFwDlAhjDU9L5mY6U1XLwlIId/G3h+QcBHDIvrJ8=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+2
View File
@@ -293,6 +293,7 @@ type SplitHTTPConfig struct {
ServerMaxHeaderBytes int32 `json:"serverMaxHeaderBytes"`
Xmux XmuxConfig `json:"xmux"`
DownloadSettings *StreamConfig `json:"downloadSettings"`
H2Params json.RawMessage `json:"h2Params"`
Extra json.RawMessage `json:"extra"`
}
@@ -495,6 +496,7 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
ServerMaxHeaderBytes: c.ServerMaxHeaderBytes,
SessionIDTable: c.SessionIDTable,
SessionIDLength: newRangeConfig(c.SessionIDLength),
H2Params: c.H2Params,
Xmux: &splithttp.XmuxConfig{
MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency),
MaxConnections: newRangeConfig(c.Xmux.MaxConnections),
+10 -15
View File
@@ -6,9 +6,7 @@ import (
"math/big"
"net"
"runtime"
"slices"
"strconv"
"strings"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/proxy/tun"
@@ -39,25 +37,22 @@ func (v *TunConfig) Build() (proto.Message, error) {
AutoSystemRoutingTable: v.AutoSystemRoutingTable,
AutoSystemDnsToGateway: v.AutoSystemDnsToGateway,
}
for _, leak := range v.AutoSystemWfpBlockLeak {
switch leak := strings.ToLower(leak); leak {
case "dns", "misconfigtun":
config.AutoSystemWfpBlockLeak = append(config.AutoSystemWfpBlockLeak, leak)
default:
return nil, errors.New("unknown autoSystemWfpBlockLeak value: ", leak)
}
leaks, err := tun.ExpandWfpBlockLeak(v.AutoSystemWfpBlockLeak)
if err != nil {
return nil, err
}
config.AutoSystemWfpBlockLeak = leaks
// Each option needs other settings on the system it takes effect on: the
// filters go along with the routes of autoSystemRoutingTable, "dns" lets
// DNS through the TUN only, and autoSystemDnsToGateway points the system
// DNS at the gateway.
// filters go along with the routes of autoSystemRoutingTable, keeping the
// system's DNS inside the TUN needs DNS servers on it, and
// autoSystemDnsToGateway points the system DNS at the gateway.
switch runtime.GOOS {
case "windows":
if len(config.AutoSystemWfpBlockLeak) > 0 && len(v.AutoSystemRoutingTable) == 0 {
if len(leaks) > 0 && len(v.AutoSystemRoutingTable) == 0 {
return nil, errors.New("autoSystemWfpBlockLeak needs autoSystemRoutingTable to be set")
}
if slices.Contains(config.AutoSystemWfpBlockLeak, "dns") && len(v.DNS) == 0 {
return nil, errors.New(`autoSystemWfpBlockLeak "dns" needs dns to be set`)
if tun.BlocksSystemDNS(leaks) && len(v.DNS) == 0 {
return nil, errors.New(`autoSystemWfpBlockLeak "all", "allexceptlan", "dns", "windnsclient", or TCP or UDP port 53, needs dns to be set`)
}
case "linux", "freebsd":
if v.AutoSystemDnsToGateway && len(v.Gateway) == 0 {
+60 -6
View File
@@ -29,12 +29,37 @@ func TestTunConfigAutoSystem(t *testing.T) {
{
Input: `{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["dns", "misconfigtun"]}`,
Parser: loadJSON(creator),
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, DNS: []string{"1.1.1.1"}, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"dns", "misconfigtun"}},
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, DNS: []string{"1.1.1.1"}, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"53", "853", "windnsclient", "misconfigtun"}},
},
{
Input: `{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["DNS"]}`,
Parser: loadJSON(creator),
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, DNS: []string{"1.1.1.1"}, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"dns"}},
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, DNS: []string{"1.1.1.1"}, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"53", "853", "windnsclient"}},
},
{
Input: `{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["TCP/853", "udp/853", "tcp/0853"]}`,
Parser: loadJSON(creator),
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"tcp/853", "udp/853"}},
},
{
Input: `{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["All", "UDP", "tcp/8000-9000", "icmp"]}`,
Parser: loadJSON(creator),
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, DNS: []string{"1.1.1.1"}, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"all", "udp", "tcp/8000-9000", "icmp"}},
},
{
Input: `{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["AllExceptLAN"]}`,
Parser: loadJSON(creator),
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, DNS: []string{"1.1.1.1"}, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"allexceptlan", "53", "853", "windnsclient"}},
},
{
Input: `{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["1.1.1.1/24:TCP/853", "[2001:DB8::/32]:udp/10-20", "[::/0]:ICMP", "853"]}`,
Parser: loadJSON(creator),
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"1.1.1.0/24:tcp/853", "[2001:db8::/32]:udp/10-20", "[::/0]:icmp", "853"}},
},
{
Input: `{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["0.0.0.0/0:dns", "[::/0]:All", "53"]}`,
Parser: loadJSON(creator),
Output: &tun.Config{Name: "xray0", Desc: "Wintun", MTU: 1500, DNS: []string{"1.1.1.1"}, AutoSystemRoutingTable: []string{"0.0.0.0/0"}, AutoOutboundsInterface: "auto", AutoSystemWfpBlockLeak: []string{"0.0.0.0/0:53", "0.0.0.0/0:853", "0.0.0.0/0:windnsclient", "[::/0]:all", "53"}},
},
})
}
@@ -49,6 +74,24 @@ func TestTunConfigAutoSystemNeeds(t *testing.T) {
{`{"name": "xray0", "autoSystemWfpBlockLeak": ["misconfigtun"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["misconfigtun"]}`, nil},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["dns"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["udp/53"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["windnsclient"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["tcp/853", "udp/853"]}`, nil},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["all"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["udp"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["tcp/50-60"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["tcp/54-60", "icmp"]}`, nil},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["53"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["50-60"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["[::/0]:udp/53"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["1.1.1.1/32:dns"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["[2001:db8::/32]:all"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["0.0.0.0/0:windnsclient"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["0.0.0.0/0:tcp/853", "1.1.1.0/24:icmp", "[::/0]:misconfigtun", "54-60"]}`, nil},
{`{"name": "xray0", "autoSystemWfpBlockLeak": ["[::/0]:misconfigtun"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemWfpBlockLeak": ["all"]}`, []string{"windows"}},
{`{"name": "xray0", "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["allexceptlan"]}`, []string{"windows"}},
{`{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["allexceptlan"]}`, nil},
{`{"name": "xray0", "autoSystemDnsToGateway": true}`, []string{"linux", "freebsd"}},
} {
config := new(TunConfig)
@@ -62,11 +105,22 @@ func TestTunConfigAutoSystemNeeds(t *testing.T) {
}
func TestTunConfigAutoSystemWfpBlockLeakUnknown(t *testing.T) {
config := new(TunConfig)
if err := json.Unmarshal([]byte(`{"name": "xray0", "autoSystemWfpBlockLeak": ["dns", "ip"]}`), config); err != nil {
// With the settings the values need on Windows, so that only an unknown
// one fails.
build := func(value string) error {
config := new(TunConfig)
if err := json.Unmarshal([]byte(`{"name": "xray0", "dns": ["1.1.1.1"], "autoSystemRoutingTable": ["0.0.0.0/0"], "autoSystemWfpBlockLeak": ["dns", "`+value+`"]}`), config); err != nil {
t.Fatal(err)
}
_, err := config.Build()
return err
}
if err := build("1.1.1.0/24:53"); err != nil {
t.Fatal(err)
}
if _, err := config.Build(); err == nil {
t.Error("an unknown autoSystemWfpBlockLeak value was accepted")
for _, value := range []string{"ip", "tcp/0", "tcp/0-10", "icmp/1", "udp/20-10", "0", "20-10", "53/tcp", "tcpv4", "ipv4:udp", "1.1.1.1:53", "2001:db8::/32:53", "[1.1.1.0/24]:53", "[::ffff:1.1.1.0/120]:53", "1.1.1.0/24"} {
if build(value) == nil {
t.Errorf("autoSystemWfpBlockLeak value %q was accepted", value)
}
}
}
+17 -19
View File
@@ -117,7 +117,7 @@ func main() {
}
var protoFiles []string
var pbCount int
var existingPbFiles []string
err := filepath.WalkDir(pwd, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return err
@@ -127,7 +127,7 @@ func main() {
protoFiles = append(protoFiles, filepath.ToSlash(rel))
}
if strings.HasSuffix(d.Name(), ".pb.go") {
pbCount++
existingPbFiles = append(existingPbFiles, path)
}
return nil
})
@@ -135,13 +135,6 @@ func main() {
fmt.Println("Walk error:", err)
os.Exit(1)
}
if len(protoFiles) == 0 {
if isCheck && pbCount > 0 {
fmt.Fprintf(os.Stderr, "Found %d .pb.go files without any .proto files. Remove these orphaned files.\n", pbCount)
os.Exit(1)
}
return
}
compiler := protocompile.Compiler{
Resolver: protocompile.WithStandardImports(&protocompile.SourceResolver{
@@ -163,14 +156,8 @@ func main() {
req := &pluginpb.CodeGeneratorRequest{
FileToGenerate: protoFiles,
Parameter: proto.String("paths=source_relative"),
Parameter: new("paths=source_relative"),
ProtoFile: topoSort(fdList),
// TODO: replace with unknown in the future
CompilerVersion: &pluginpb.Version{
Major: proto.Int32(6),
Minor: proto.Int32(33),
Patch: proto.Int32(5),
},
}
var diffCount int
@@ -223,11 +210,22 @@ func main() {
}
}
if isCheck {
if pbCount != len(generatedFiles) {
fmt.Printf("Protobuf file count mismatch: found %d, expected %d. Remove orphaned files and regenerate.\n", pbCount, len(generatedFiles))
for _, path := range existingPbFiles {
if _, ok := generatedFiles[path]; !ok {
diffCount++
if isCheck {
fmt.Printf("Orphaned Protobuf file: %s\n", path)
} else {
fmt.Printf("Removing orphaned Protobuf file: %s\n", path)
if err := os.Remove(path); err != nil {
fmt.Fprintf(os.Stderr, "Error removing %s: %v\n", path, err)
os.Exit(1)
}
}
}
}
if isCheck {
if diffCount > 0 {
fmt.Println("Protobuf generated file problem(s) found.")
fmt.Println("Please run 'go run ./infra/vprotogen' to regenerate them.")
+11 -10
View File
@@ -47,18 +47,19 @@ func executeBalancerInfo(cmd *base.Command, args []string) {
conn, ctx, close := dialAPIServer()
defer close()
client := routerService.NewRoutingServiceClient(conn)
r := &routerService.GetBalancerInfoRequest{Tag: unnamedArgs[0]}
resp, err := client.GetBalancerInfo(ctx, r)
if err != nil {
base.Fatalf("failed to get health information: %s", err)
}
for _, tag := range unnamedArgs {
r := &routerService.GetBalancerInfoRequest{Tag: tag}
resp, err := client.GetBalancerInfo(ctx, r)
if err != nil {
base.Fatalf("failed to get health information: %s", err)
}
if apiJSON {
showJSONResponse(resp)
return
if apiJSON {
showJSONResponse(resp)
} else {
showBalancerInfo(resp.Balancer)
}
}
showBalancerInfo(resp.Balancer)
}
func showBalancerInfo(b *routerService.BalancerMsg) {
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/blackhole/config.proto
package blackhole
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/dns/config.proto
package dns
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/dokodemo/config.proto
package dokodemo
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/freedom/config.proto
package freedom
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/http/config.proto
package http
+3 -2
View File
@@ -312,8 +312,9 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri
// Sometimes, server might send 1xx response to client
// it should not be processed by http proxy handler, just forward it to client
func readResponseAndHandle100Continue(r *bufio.Reader, req *http.Request, writer io.Writer) (*http.Response, error) {
// have a little look of response
peekBytes, err := r.Peek(56)
// Wait for the HTTP/1.x status prefix before inspecting the buffered response.
r.Peek(12)
peekBytes, err := r.Peek(min(56, r.Buffered()))
if err == nil || err == bufio.ErrBufferFull {
str := string(peekBytes)
ResponseLine := strings.Split(str, "\r\n")[0]
+2 -1
View File
@@ -7,6 +7,7 @@ import (
"net/http"
"strings"
"testing"
"testing/iotest"
)
// A malformed upstream response containing a bare '\n' before the real
@@ -29,7 +30,7 @@ func TestReadResponseAndHandle100ContinueDoesNotPanicOnEarlyNewline(t *testing.T
func TestReadResponseAndHandle100ContinueForwardsAndParsesFinalResponse(t *testing.T) {
payload := "HTTP/1.1 100 Continue\r\n\r\n" +
"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello"
r := bufio.NewReader(bytes.NewReader([]byte(payload)))
r := bufio.NewReader(iotest.OneByteReader(bytes.NewReader([]byte(payload))))
req, err := http.NewRequest("GET", "http://example.com/", nil)
if err != nil {
t.Fatal(err)
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/hysteria/account/config.proto
package account
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/hysteria/config.proto
package hysteria
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/loopback/config.proto
package loopback
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/masque/config.proto
package masque
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/shadowsocks/config.proto
package shadowsocks
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/shadowsocks_2022/config.proto
package shadowsocks_2022
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/socks/config.proto
package socks
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/trojan/config.proto
package trojan
+27 -11
View File
@@ -4,7 +4,7 @@ TUN interface support bridges the gap between network layer 3 and layer 7, intro
This functionality is targeted to assist applications/end devices that don't have proxy support, or can't run external applications (like Smart TV's). Making it possible to run Xray proxy right on network edge devices (routers) with support to route raw network traffic. \
Primary targets are Linux based router devices. Like most popular OpenWRT option. \
Support for Windows, macOS, Android and iOS is also implemented (see below).
Support for Windows, macOS, FreeBSD, Android and iOS is also implemented (see below).
## PLEASE READ FOLLOWING CAREFULLY
@@ -23,6 +23,12 @@ For more advanced routing policies or rules, OS level configuration can still ma
This keeps complex system level routing and rules in a single place of responsibility - the OS itself. \
Examples of how to achieve this on a simple Linux system (Ubuntu with systemd-networkd) can be found at the end of this README.
Packets are handled by Xray-core's own lightweight TCP/IP stack, which replaced gVisor (XTLS/Xray-core#7130).
On Linux, as on Android, the TUN can also be a file descriptor handed over through the environment variable `xray.tun.fd` (or `XRAY_TUN_FD`), e.g. for lightweight or unprivileged setups.
With `autoSystemRoutingTable`, Xray's own connections are kept out of the TUN through `autoOutboundsInterface` (then `"auto"` by default): each of their sockets is bound to another interface, the one named there, or with `"auto"` the one of the default route (Android needs the name). Linux and Android bind with `SO_BINDTODEVICE`, Windows with `IP_UNICAST_IF` and `IPV6_UNICAST_IF`, macOS and iOS with `IP_BOUND_IF` and `IPV6_BOUND_IF`, and FreeBSD switches them to another routing table (see [FreeBSD SUPPORT](#freebsd-support)). Connections to loopback addresses, TCP and UDP, stay unbound. If a socket cannot be bound, the connection fails rather than leave unbound, where it could loop back into the TUN (XTLS/Xray-core#7173).
### SYSTEM DNS ON LINUX AND FREEBSD (`autoSystemDnsToGateway`)
On Linux and FreeBSD, setting `autoSystemDnsToGateway` to `true` lets the inbound point the system resolver at the tun interface, so name lookups resolve through Xray instead of going out over the physical link. It is off by default, and it exists only on those two systems. This section describes Linux; the FreeBSD differences follow in [SYSTEM DNS ON FREEBSD](#system-dns-on-freebsd).
@@ -215,24 +221,33 @@ All in all systemd-networkd and its derivatives (like netplan or NetworkManager)
Windows version of the same functionality is implemented through Wintun library. \
To make it start, wintun.dll specific for your Windows/arch must be present next to Xray.exe binary.
After the start network adapter with the name you chose in the config will be created in the system, and exist while Xray is running.
After the start network adapter with the name you chose in the config will be created in the system, and exist while Xray is running. A Wintun adapter that already has that name is reused.
When `dns` is set, those servers are applied to the adapter. Windows is kept from registering the TUN's addresses in DNS, and its DNS cache is flushed when the TUN starts and stops.
With IPv6 disabled system-wide (the `DisabledComponents` registry value), the adapter has no IPv6, and the TUN starts with IPv4 only, unless `gateway` or `autoSystemRoutingTable` has IPv6 entries, which then keep it from starting.
With `autoSystemWfpBlockLeak`, which needs `autoSystemRoutingTable` (the config is rejected otherwise), Xray also adds Windows Filtering Platform filters that keep two kinds of traffic of every program but Xray itself from leaving outside the TUN, each chosen by a value in the list, e.g. `"autoSystemWfpBlockLeak": ["dns", "misconfigtun"]`:
- `"dns"` (needs `dns`, the config is rejected otherwise): DNS (port 53) only goes through the TUN. Windows keeps sending name queries to the DNS servers of the other interfaces as well, out through those interfaces whatever the routes say, and other programs reach a resolver on the local network (e.g. `192.168.1.1` handed out by DHCP) through its more specific LAN route instead of the TUN. On Windows 11 and Server 2022 and later, where those queries may also go over HTTPS or TLS, Windows' DNS Client service cannot connect outside the TUN at all, except for name resolution on the local network (LLMNR, mDNS). The `dns` servers therefore have to lie within `gateway` or `autoSystemRoutingTable` (a warning is logged otherwise), and DNS servers that should be reached directly belong in Xray's own `dns` settings.
- `"misconfigtun"`: an IP version without routes in `autoSystemRoutingTable`, IPv4 or IPv6, is blocked entirely, in both directions, as it would bypass the TUN. Only loopback and what Windows itself needs on the local link (DHCP, and for IPv6 neighbor and multicast listener discovery) remain allowed. An address of that version in `gateway` is not needed: without one, Windows gives the TUN link-local addresses itself, an IPv6 one at once and an IPv4 one from `169.254.0.0/16` after some seconds (until then, IPv4 routed to the TUN is unreachable), and what is routed to the TUN goes through it with those.
When `dns` is set, those servers are applied to the adapter. Windows is kept from registering the TUN's addresses in DNS, and its DNS cache is flushed when the TUN starts and stops. Xray's own lookups through the system's DNS servers, like those of the `localhost` DNS server with `autoOutboundsInterface`, skip them, unless another interface uses them as well, as they would lead back into the TUN.
With the filters in place, Xray's own connections out also get past Windows Firewall's block rules (other firewalls may still block them), while connections to Xray's inbounds stay subject to them.
Setting `autoSystemWfpBlockLeak` to at least `["dns"]` is recommended, to block DNS leaks. It needs `autoSystemRoutingTable` (the config is rejected otherwise), and adds Windows Filtering Platform filters that let traffic of every program but Xray itself leave only through the TUN, each kind chosen by a value:
- `"dns"`: stands for `"53"`, `"853"` (DNS over TLS and QUIC) and `"windnsclient"`. Windows also queries the DNS servers of the other interfaces, through those interfaces whatever the routes say, and other programs reach a resolver on the local network (e.g. `192.168.1.1` from DHCP) through its more specific route.
- `"windnsclient"`: Windows' DNS Client service, except for name resolution on the local network (LLMNR, mDNS). Since Windows 11 and Server 2022, it may query over HTTPS or TLS, to any port; earlier versions only query port 53, so there it does nothing.
- `"tcp"`, `"udp"`: that protocol, to all ports, or with `/<port>` or `/<low>-<high>` (1 to 65535) to some, e.g. `"tcp/853"` or `"udp/10-20"`. The ports alone stand for both, e.g. `"53"` or `"10-20"`.
- `"icmp"`: ICMP and ICMPv6, e.g. `ping`.
- `"misconfigtun"`: an IP version without routes in `autoSystemRoutingTable`, which would bypass the TUN.
- `"all"`: everything, in both directions. Access to the local network then breaks unless it is routed into the TUN as well, and so do incoming connections to other programs, other VPNs, and virtual machines whose NAT runs on the host.
- `"allexceptlan"`: like `"all"`, but traffic to and from the local network stays allowed, e.g. for printers or casting to a TV: private, link-local, multicast and broadcast addresses (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, `224.0.0.0/4`, `255.255.255.255`, `fc00::/7`, `fe80::/10`, `ff00::/8`), not global IPv6 ones. It includes `"dns"`, and the other values apply there as well. `"all"` overrides it.
Names that Xray resolves through the system resolver, such as an outbound's server address given as a domain with the default `AsIs` domain strategy, would be looked up by Windows on Xray's behalf, and those queries would then go into the TUN too. While DNS is restricted this way and `autoOutboundsInterface` is in use (the default with `autoSystemRoutingTable`), Xray therefore resolves them itself, with its own queries to the DNS servers of the other interfaces. That bypasses Windows' DNS cache, and its name resolution on the local network (LLMNR, mDNS): a server address given as a domain is looked up again for every connection, and a DNS server that does not answer delays each lookup. Having Xray's own `dns` resolve it, through the outbound's `sockopt.domainStrategy`, avoids that. The `localhost` DNS server queries the same servers whenever `autoOutboundsInterface` is in use. Both skip the TUN's own DNS servers, unless another interface uses them as well: queried from Xray itself, they would lead back into it, or nowhere.
A CIDR in front limits a value to its addresses, an IPv6 one in brackets, e.g. `"8.8.8.8/32:all"`, `"1.1.1.0/24:53"` or `"[::/0]:all"` (IPv6 only). It limits what the value stands for or includes as well: `"0.0.0.0/0:allexceptlan"`, for instance, leaves DNS over IPv6 open.
If the filters cannot be added, Xray does not start. They are removed when Xray exits. Not covered is name resolution on the local network (LLMNR, mDNS, NetBIOS), except over an IP version that is blocked.
Loopback, DHCP, DHCPv6 and IPv6 neighbor discovery always stay allowed. Xray's own connections out also get past Windows Firewall's block rules, while connections to its inbounds stay subject to them. If the filters cannot be added, Xray does not start; they are removed when it exits. `"dns"` leaves name resolution on the local network (LLMNR, mDNS, NetBIOS) alone; `"all"` or `"udp"`, for instance, block it as well.
`autoSystemWfpBlockLeak` (Windows only) is empty by default, as the filters break some setups: with `"dns"`, a local DNS resolver other programs use (e.g. on `127.0.0.1:53`), the DNS of another VPN on its own interface, virtual machines whose NAT resolves names on the host, or signing in to a captive portal; with `"misconfigtun"`, IPv4 or IPv6 on the local network while no route of that version leads to the TUN. Without the filters, DNS may leak as described above. To keep an IP version out of the TUN on purpose while still blocking DNS leaks, use only `["dns"]`.
Keeping the system's DNS inside the TUN, with `"all"`, `"allexceptlan"`, `"dns"`, `"windnsclient"`, or anything covering TCP or UDP port 53, even to some addresses only, needs `dns` (the config is rejected otherwise), whose servers have to lie within `gateway` or `autoSystemRoutingTable`, or be loopback addresses like `127.0.0.1` (a warning is logged otherwise); DNS servers to be reached directly belong in Xray's own `dns` settings. With `autoOutboundsInterface`, names that Xray would look up through the system, e.g. an outbound's server address with the default `AsIs` domain strategy, are then resolved by Xray itself, from the DNS servers of the other interfaces, as Windows would send those queries into the TUN. That bypasses Windows' DNS cache, so such a name is looked up for every connection; resolving it with Xray's own `dns`, through the outbound's `sockopt.domainStrategy`, avoids that.
`autoSystemWfpBlockLeak` is empty by default, as the filters break some setups: with `"dns"`, a local DNS resolver that queries through another interface, the DNS of another VPN, virtual machines whose NAT resolves names on the host, and captive portals; with `"misconfigtun"`, IPv4 or IPv6 on the local network while not routed into the TUN.
`autoOutboundsInterface` (the default with `autoSystemRoutingTable`) keeps Xray's own connections out of the TUN by binding them to another interface, which Windows only honors while that interface has weak host send and forwarding off for the IP versions routed to the TUN. Otherwise, Windows sends them into the TUN, from that interface's address, and they stall. While the TUN runs, Xray therefore turns weak host send off on that interface, and on again when it stops or another interface takes over. Forwarding cannot be turned off this way, as Mobile Hotspot and Internet Connection Sharing need it, so a warning is logged while it is on. Having the hotspot share the TUN instead of that interface (Settings, Mobile hotspot, Share my internet connection from) moves forwarding to the TUN, where it does no harm, and sends the hotspot's devices through Xray as well.
An outbound interface with only IPv4 or only IPv6 works as well: a UDP socket opened for both IP versions, which such an interface cannot be bound to, is opened again for the destination's version only, and IPv6 TCP is bound for IPv6 alone, so connections of the missing version fail rather than leave through another interface (XTLS/Xray-core#7214).
You can give the adapter ip address manually, you can live Windows to give it autogenerated ip address (which take few seconds), it doesn't matter, the traffic going _through_ the interface will be forwarded into the app for proxying. \
Minimal configuration that will work for local machine is routing passing the traffic on-link through the interface.
You will need the interface id for that, unfortunately it is going to change with every Xray start due to implementation ambiguity between Xray and wintun driver.
@@ -307,6 +322,7 @@ sudo route add -inet6 -host 2606:4700:4700::1111 -iface utun10
sudo route add -inet6 -host 2606:4700:4700::1001 -iface utun10
```
Alternatively, configure `autoSystemRoutingTable` and Xray will add and remove those system routes while it is running.
Xray's own connections are then kept out of the TUN through `autoOutboundsInterface`: an IPv4 socket is bound with `IP_BOUND_IF`, an IPv6 or dual-stack one with `IPV6_BOUND_IF`, which covers the IPv4 traffic of a dual-stack socket as well, since macOS rejects `IP_BOUND_IF` on IPv6 sockets (XTLS/Xray-core#7230).
Important to remember that everything written above about Linux routing concept, also apply to Mac OS X. If you simply route default route through utun interface, that will result network loop and immediate network failure.
## ANDROID SUPPORT
@@ -338,7 +354,7 @@ let tunFd = ((0 ... 1024).first { (_ fd: Int32) -> Bool in var len = socklen_t(b
}!
```
Set the environment variable `xray.tun.fd` (or `XRAY_TUN_FD`) to the fd number before starting Xray. This can be done from Swift/Objective-C or by exposing a Go function via gomobile bindings.
Set the environment variable `xray.tun.fd` (or `XRAY_TUN_FD`) to the fd number before starting Xray. This can be done from Swift/Objective-C or by exposing a Go function via gomobile bindings. Xray works on a duplicate of the fd and closes only that, so the fd itself stays NetworkExtension's (XTLS/Xray-core#6883).
Build using gomobile for iOS framework integration:
```
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/tun/config.proto
package tun
+38 -35
View File
@@ -247,42 +247,45 @@ startOver:
}
// With autoSystemWfpBlockLeak, once the system routes lead to the TUN,
// keep DNS ("dns", if dns is set), and an IP version no route of which
// leads to the TUN ("misconfigtun"), from leaving through the other
// interfaces. Addresses do not matter: without one of a version in
// gateway, Windows gives the TUN a link-local one.
leaks := t.options.AutoSystemWfpBlockLeak
blockDNS := slices.Contains(leaks, "dns") && len(dns) > 0
blockIPv4 := slices.Contains(leaks, "misconfigtun") && !route4
blockIPv6 := slices.Contains(leaks, "misconfigtun") && !route6
if (route4 || route6) && (blockDNS || blockIPv4 || blockIPv6) {
if t.wfp, err = blockLeaks(t.luid, blockDNS, blockIPv4, blockIPv6); err != nil {
var blocked []string
for _, b := range []struct {
on bool
what string
}{{blockDNS, "DNS"}, {blockIPv4, "IPv4"}, {blockIPv6, "IPv6"}} {
if b.on {
blocked = append(blocked, b.what)
}
}
// Rather no TUN than a leaking one.
return errors.New("unable to block ", strings.Join(blocked, " and "), " outside the TUN (remove autoSystemWfpBlockLeak to run without)").Base(err)
// keep the protocols and ports given ("dns" stands for those of DNS),
// Windows' DNS Client service, an IP version no route of which leads to
// the TUN ("misconfigtun"), or everything ("all", or "allexceptlan" but
// for the local network), to the addresses of a CIDR or to all, from
// leaving through the other interfaces. The TUN's own addresses do not
// matter: without one of a version in gateway, Windows gives it a
// link-local one.
leaks, err := ExpandWfpBlockLeak(t.options.AutoSystemWfpBlockLeak)
if err != nil {
return err
}
block := newLeakFilters(leaks, route4, route6)
if route4 || route6 {
dnsClient := func(l leak) bool { return l.name == "windnsclient" }
if slices.ContainsFunc(block, dnsClient) && !dnsClientUsesAnyPort() {
block = slices.DeleteFunc(block, dnsClient)
errors.LogInfo(context.Background(), "[tun] windnsclient skipped: before Windows 11 and Server 2022, the DNS Client service only queries port 53")
}
errors.LogInfo(context.Background(), "[tun] outside the TUN, blocked DNS: ", blockDNS, ", blocked IPv4: ", blockIPv4, ", blocked IPv6: ", blockIPv6)
if blockDNS {
covered := slices.Clone(addresses)
for _, route := range routesData {
covered = append(covered, route.Destination)
}
for _, server := range dnsOutsideTUN(dns, covered) {
errors.LogWarning(context.Background(), "[tun] DNS server ", server, " is in neither gateway nor autoSystemRoutingTable, so queries to it cannot go through the TUN and are blocked")
}
// With updater, the dialer controllers bind Xray's own sockets
// to the physical interface.
if updater != nil {
t.resolver = resolveOnOwn()
}
if block.systemDNS() && len(dns) == 0 {
return errors.New("keeping the system's DNS inside the TUN needs dns to be set")
}
}
if blocked := block.String(); (route4 || route6) && blocked != "" {
if t.wfp, err = blockLeaks(t.luid, block); err != nil {
// Rather no TUN than a leaking one.
return errors.New("unable to block ", blocked, " outside the TUN (remove autoSystemWfpBlockLeak to run without)").Base(err)
}
errors.LogInfo(context.Background(), "[tun] blocked outside the TUN: ", blocked)
covered := slices.Clone(addresses)
for _, route := range routesData {
covered = append(covered, route.Destination)
}
for _, server := range dnsOutsideTUN(dns, covered, block) {
errors.LogWarning(context.Background(), "[tun] DNS server ", server, " is in neither gateway nor autoSystemRoutingTable, so queries to it cannot go through the TUN and are blocked")
}
// With updater, the dialer controllers bind Xray's own sockets to the
// physical interface.
if block.systemDNS() && updater != nil {
t.resolver = resolveOnOwn()
}
}
if len(dns) > 0 || route4 || route6 {
+366 -134
View File
@@ -3,10 +3,12 @@
package tun
import (
"encoding/binary"
"net/netip"
"os"
"runtime"
"slices"
"strings"
"unsafe"
"github.com/xtls/xray-core/common/errors"
@@ -36,15 +38,19 @@ const (
fwpmSessionFlagDynamic = 1 // FWPM_SESSION_FLAG_DYNAMIC
fwpmFilterFlagClearActionRight = 8 // FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT
fwpUint8 = 1 // FWP_UINT8
fwpUint16 = 2 // FWP_UINT16
fwpUint32 = 3 // FWP_UINT32
fwpUint64 = 4 // FWP_UINT64
fwpByteArray16Type = 11 // FWP_BYTE_ARRAY16_TYPE
fwpByteBlobType = 12 // FWP_BYTE_BLOB_TYPE
fwpSecurityDescriptorType = 14 // FWP_SECURITY_DESCRIPTOR_TYPE
fwpUint8 = 1 // FWP_UINT8
fwpUint16 = 2 // FWP_UINT16
fwpUint32 = 3 // FWP_UINT32
fwpUint64 = 4 // FWP_UINT64
fwpByteArray16Type = 11 // FWP_BYTE_ARRAY16_TYPE
fwpByteBlobType = 12 // FWP_BYTE_BLOB_TYPE
fwpSecurityDescriptorType = 14 // FWP_SECURITY_DESCRIPTOR_TYPE
fwpV4AddrMask = 0x100 // FWP_V4_ADDR_MASK
fwpV6AddrMask = 0x101 // FWP_V6_ADDR_MASK
fwpRangeType = 0x102 // FWP_RANGE_TYPE
fwpMatchEqual = 0 // FWP_MATCH_EQUAL
fwpMatchRange = 5 // FWP_MATCH_RANGE
fwpMatchFlagsAllSet = 6 // FWP_MATCH_FLAGS_ALL_SET
fwpConditionFlagIsLoopback = 1 // FWP_CONDITION_FLAG_IS_LOOPBACK
@@ -93,6 +99,37 @@ type fwpValue0 struct {
value uintptr
}
// fwpRange0 is FWP_RANGE0, which an FWP_RANGE_TYPE condition points to.
type fwpRange0 struct {
low, high fwpValue0
}
// fwpV4AddrAndMask is FWP_V4_ADDR_AND_MASK, in host byte order.
type fwpV4AddrAndMask struct {
addr, mask uint32
}
// fwpV6AddrAndMask is FWP_V6_ADDR_AND_MASK.
type fwpV6AddrAndMask struct {
addr [16]byte
prefixLength uint8
}
// lanPrefixes are what "allexceptlan" leaves to the local network: private
// and link-local addresses, and multicast and broadcast, which discovery on
// it uses.
var lanPrefixes = []netip.Prefix{
netip.MustParsePrefix("10.0.0.0/8"),
netip.MustParsePrefix("172.16.0.0/12"),
netip.MustParsePrefix("192.168.0.0/16"),
netip.MustParsePrefix("169.254.0.0/16"),
netip.MustParsePrefix("224.0.0.0/4"),
netip.MustParsePrefix("255.255.255.255/32"),
netip.MustParsePrefix("fc00::/7"),
netip.MustParsePrefix("fe80::/10"),
netip.MustParsePrefix("ff00::/8"),
}
type fwpmDisplayData0 struct {
name *uint16
description *uint16
@@ -170,33 +207,165 @@ func condition(field *windows.GUID, typ uint32, value uintptr) fwpmFilterConditi
}
}
// leakFilters are the values of autoSystemWfpBlockLeak that blockLeaks keeps
// from leaving outside the TUN.
type leakFilters []leak
// newLeakFilters turns expanded autoSystemWfpBlockLeak values into the filters
// for a TUN that the routes of route4 (IPv4) and route6 (IPv6) lead to, which
// leave "misconfigtun" over the IP versions without such routes only.
func newLeakFilters(leaks []string, route4, route6 bool) leakFilters {
var block leakFilters
for _, value := range leaks {
l, ok := parseLeak(value)
if !ok {
continue
}
if l.name == "misconfigtun" {
unrouted4, unrouted6 := !route4, !route6
if l.prefix.IsValid() {
unrouted4, unrouted6 = unrouted4 && l.prefix.Addr().Is4(), unrouted6 && l.prefix.Addr().Is6()
}
switch {
case !unrouted4 && !unrouted6:
continue
case !l.prefix.IsValid() && !unrouted4:
l.prefix = allAddresses(true)
case !l.prefix.IsValid() && !unrouted6:
l.prefix = allAddresses(false)
}
}
if !slices.Contains(block, l) {
block = append(block, l)
}
}
return block
}
// String lists what block keeps inside the TUN, or is empty for nothing.
func (block leakFilters) String() string {
values := make([]string, len(block))
for i, l := range block {
values[i] = l.String()
}
return strings.Join(values, ", ")
}
// systemDNS tells whether block keeps the system's DNS inside the TUN, to at
// least some addresses: port 53, the DNS Client service, or everything ("all"),
// though not everything but the local network ("allexceptlan"), where a
// resolver like the router's may be.
func (block leakFilters) systemDNS() bool {
return slices.ContainsFunc(block, leak.blocksDNS)
}
// prefixes returns the CIDRs of the values of block named name over an IP
// version.
func (block leakFilters) prefixes(name string, ipv6 bool) []netip.Prefix {
var prefixes []netip.Prefix
for _, l := range block {
if prefix, ok := l.over(ipv6); ok && l.name == name {
prefixes = append(prefixes, prefix)
}
}
return prefixes
}
// over returns the CIDR of l over an IP version, all its addresses for l
// without one, or false for l over the other IP version only.
func (l leak) over(ipv6 bool) (netip.Prefix, bool) {
if !l.prefix.IsValid() {
return allAddresses(ipv6), true
}
return l.prefix, l.prefix.Addr().Is6() == ipv6
}
// allAddresses is the CIDR of all addresses of an IP version.
func allAddresses(ipv6 bool) netip.Prefix {
if ipv6 {
return netip.PrefixFrom(netip.IPv6Unspecified(), 0)
}
return netip.PrefixFrom(netip.IPv4Unspecified(), 0)
}
// lanWithin returns the parts of lanPrefixes that lie in any of prefixes: two
// prefixes either share no address, or one holds the other.
func lanWithin(prefixes ...netip.Prefix) []netip.Prefix {
var parts []netip.Prefix
for _, lan := range lanPrefixes {
for _, prefix := range prefixes {
if !prefix.Overlaps(lan) {
continue
}
part := lan
if prefix.Bits() > lan.Bits() {
part = prefix
}
if !slices.Contains(parts, part) {
parts = append(parts, part)
}
}
}
return parts
}
// dnsClientUsesAnyPort tells whether Windows' DNS Client service may send its
// queries over HTTPS or TLS, and so to any port, which it does since Windows 11
// and Server 2022 (build 20348). Earlier versions only query port 53, and may
// run the service in one process with others, which its filters would catch
// as well.
func dnsClientUsesAnyPort() bool {
_, _, build := windows.RtlGetNtVersionNumbers()
return build >= 20348
}
// The weights of the filters of blockLeaks, the heaviest matching one of which
// decides.
const (
weightAllExceptLAN uint8 = iota // block "allexceptlan"
weightLAN // permit its local network
weightDNSClient // block "windnsclient"
weightLocalLink // permit its mDNS and LLMNR on the local network
weightBlock // block the other values
weightPermit // permit what always stays allowed, and the TUN
weightXray // permit Xray
)
// blockLeaks keeps traffic from leaving through interfaces other than tun,
// for every program but Xray itself, whose outbounds (DNS included) use the
// other interfaces on purpose:
// other interfaces on purpose, as the values of block say, each to the
// addresses of its CIDR, or to all:
//
// - dns: DNS (port 53) may only go through the TUN. Windows sends a name
// query to the DNS servers of all interfaces, not only to those of the TUN:
// to the first server of each interface, then to all of them when no answer
// arrives within a second or two. It sends the queries for the servers of
// an interface out through that interface, whatever the routes say, and
// other programs reach an on-link resolver, like 192.168.1.1 from DHCP,
// through its LAN route, which is more specific than the TUN's default
// route. Since Windows 11 and Server 2022, Windows may also send its
// queries over HTTPS or TLS, so there its DNS Client service may not
// connect outside the TUN at all, except for name resolution on the local
// link (mDNS, LLMNR).
// - ipv4, ipv6: no IPv4, or no IPv6, at all, in either direction, for a TUN
// that no route of it leads to, except loopback and what Windows itself
// needs on the local link (DHCP, and for IPv6 neighbor and multicast
// listener discovery), none of which can leave it. The TUN carries what
// is routed to it even without an address of that IP version in gateway:
// Windows gives it link-local ones itself, an IPv6 one at once, an IPv4
// one from 169.254.0.0/16 after some seconds (until then, IPv4 routed to
// the TUN is unreachable).
// - Protocols and ports: connections of these, TCP or UDP to some or all
// ports, or ICMP, may only go through the TUN, those of DNS (53), DNS
// over TLS (TCP 853) and DNS over QUIC (UDP 853) with "dns". Windows
// sends a name query to the DNS servers of all interfaces, not only to
// those of the TUN: to the first server of each interface, then to all
// of them when no answer arrives within a second or two. It sends the
// queries for the servers of an interface out through that interface,
// whatever the routes say, and other programs reach an on-link resolver,
// like 192.168.1.1 from DHCP, through its LAN route, which is more
// specific than the TUN's default route.
// - "windnsclient": since Windows 11 and Server 2022, Windows may also send
// its queries over HTTPS or TLS, to any port, so its DNS Client service
// may not connect outside the TUN at all, except for name resolution on
// the local network (mDNS, LLMNR).
// - "all", and "misconfigtun" over an IP version no route of which leads to
// the TUN: nothing at all outside the TUN, in either direction. The TUN
// carries what is routed to it even without an address of that IP
// version in gateway: Windows gives it link-local ones itself, an IPv6
// one at once, an IPv4 one from 169.254.0.0/16 after some seconds (until
// then, IPv4 routed to the TUN is unreachable).
// - "allexceptlan": the same, but to and from the local network.
//
// The other values still apply to what "allexceptlan" and "windnsclient" leave
// open on the local network. Whatever is blocked, loopback, which cannot leave
// the system, and what Windows itself needs on the local link stay allowed:
// DHCP, and for IPv6 neighbor and multicast listener discovery.
//
// The filters live in a dynamic WFP session: closing the returned engine handle
// with closeWFPEngine deletes them, and so does Windows when the process dies.
func blockLeaks(tun winipcfg.LUID, dns, ipv4, ipv6 bool) (windows.Handle, error) {
func blockLeaks(tun winipcfg.LUID, block leakFilters) (windows.Handle, error) {
engine, err := openWFPEngine()
if err != nil {
return 0, err
@@ -205,7 +374,7 @@ func blockLeaks(tun winipcfg.LUID, dns, ipv4, ipv6 bool) (windows.Handle, error)
closeWFPEngine(engine)
return 0, errors.New("FwpmTransactionBegin0 failed").Base(err)
}
err = addLeakFilters(engine, tun, dns, ipv4, ipv6)
err = addLeakFilters(engine, tun, block)
if err == nil {
if err = fwpmResult(procFwpmTransactionCommit0.Call(uintptr(engine))); err != nil {
err = errors.New("FwpmTransactionCommit0 failed").Base(err)
@@ -242,7 +411,7 @@ func closeWFPEngine(engine windows.Handle) {
// addLeakFilters adds the filters of blockLeaks in a sublayer of their own.
// blockLeaks runs it in a transaction, so that they take effect all at once.
func addLeakFilters(engine windows.Handle, tun winipcfg.LUID, dns, ipv4, ipv6 bool) error {
func addLeakFilters(engine windows.Handle, tun winipcfg.LUID, block leakFilters) error {
exe, err := os.Executable()
if err != nil {
return err
@@ -285,51 +454,170 @@ func addLeakFilters(engine windows.Handle, tun winipcfg.LUID, dns, ipv4, ipv6 bo
// hard permit. Connections from outside to Xray get an ordinary one, so
// that firewalls keep guarding its inbounds.
self := condition(&fwpmConditionALEAppID, fwpByteBlobType, uintptr(unsafe.Pointer(appID)))
dns53 := condition(&fwpmConditionIPRemotePort, fwpUint16, 53)
// DNS goes through the TUN when its local address is the TUN's, and it
// also leaves, or arrives, through the TUN. The local address alone
// A connection goes through the TUN when its local address is the TUN's,
// and it also leaves, or arrives, through the TUN. The local address alone
// decides by default, but with weak host sending or receiving enabled,
// packets of the TUN's address can use other interfaces. (The next hop,
// the interface replies would leave by, is not known for arriving ones.)
onTUN := func(field *windows.GUID) fwpmFilterCondition0 {
return condition(field, fwpUint64, uintptr(unsafe.Pointer(tunLUID)))
}
out := []fwpmFilterCondition0{dns53, onTUN(&fwpmConditionIPLocalInterface), onTUN(&fwpmConditionIPNexthopInterface)}
in := []fwpmFilterCondition0{dns53, onTUN(&fwpmConditionIPLocalInterface), onTUN(&fwpmConditionIPArrivalInterface)}
// remote matches the remote addresses in any of prefixes, all of one IP
// version: conditions on the same field match when any of them does. With
// all addresses among them, no condition is needed.
remote := func(prefixes ...netip.Prefix) []fwpmFilterCondition0 {
if slices.ContainsFunc(prefixes, func(prefix netip.Prefix) bool { return prefix.Bits() == 0 }) {
return nil
}
var conditions []fwpmFilterCondition0
for _, prefix := range prefixes {
var value uintptr
typ := uint32(fwpV4AddrMask)
if prefix.Addr().Is6() {
addr := &fwpV6AddrAndMask{addr: prefix.Addr().As16(), prefixLength: uint8(prefix.Bits())}
pinner.Pin(addr) // the condition only holds it as uintptr
value, typ = uintptr(unsafe.Pointer(addr)), fwpV6AddrMask
} else {
a := prefix.Addr().As4()
addr := &fwpV4AddrAndMask{addr: binary.BigEndian.Uint32(a[:]), mask: ^uint32(0) << (32 - prefix.Bits())}
pinner.Pin(addr) // the condition only holds it as uintptr
value = uintptr(unsafe.Pointer(addr))
}
conditions = append(conditions, condition(&fwpmConditionIPRemoteAddress, typ, value))
}
return conditions
}
// toPorts matches the ports of traffic, if not all of them.
toPorts := func(traffic leak) []fwpmFilterCondition0 {
switch {
case traffic.low == 0:
return nil
case traffic.low == traffic.high:
return []fwpmFilterCondition0{condition(&fwpmConditionIPRemotePort, fwpUint16, uintptr(traffic.low))}
}
ports := &fwpRange0{
low: fwpValue0{typ: fwpUint16, value: uintptr(traffic.low)},
high: fwpValue0{typ: fwpUint16, value: uintptr(traffic.high)},
}
pinner.Pin(ports) // the condition only holds it as uintptr
return []fwpmFilterCondition0{{
fieldKey: fwpmConditionIPRemotePort,
matchType: fwpMatchRange,
conditionValue: fwpValue0{typ: fwpRangeType, value: uintptr(unsafe.Pointer(ports))},
}}
}
// What stays allowed whatever is blocked, in both directions: replies to
// a connection accepted from outside would leave through the physical
// link as well.
type permit struct {
name string
conditions []fwpmFilterCondition0
}
loopback := permit{"loopback", []fwpmFilterCondition0{{
fieldKey: fwpmConditionFlags,
matchType: fwpMatchFlagsAllSet,
conditionValue: fwpValue0{typ: fwpUint32, value: fwpConditionFlagIsLoopback},
}}}
// DHCP keeps the addresses of the other interfaces, which Xray's own
// connections use.
dhcp := permit{"DHCP", []fwpmFilterCondition0{
condition(&fwpmConditionIPProtocol, fwpUint8, windows.IPPROTO_UDP),
condition(&fwpmConditionIPLocalPort, fwpUint16, 68),
condition(&fwpmConditionIPRemotePort, fwpUint16, 67),
}}
// Neighbor and multicast listener discovery, ICMPv6 130-137 and 143,
// whose type and code sit where the local and remote port are.
discovery := permit{"IPv6 neighbor and multicast listener discovery", []fwpmFilterCondition0{condition(&fwpmConditionIPProtocol, fwpUint8, windows.IPPROTO_ICMPV6)}}
for _, typ := range []uintptr{130, 131, 132, 133, 134, 135, 136, 137, 143} {
discovery.conditions = append(discovery.conditions, condition(&fwpmConditionIPLocalPort, fwpUint16, typ))
}
discovery.conditions = append(discovery.conditions, condition(&fwpmConditionIPRemotePort, fwpUint16, 0))
dhcpv6 := []fwpmFilterCondition0{
condition(&fwpmConditionIPProtocol, fwpUint8, windows.IPPROTO_UDP),
condition(&fwpmConditionIPLocalPort, fwpUint16, 546),
condition(&fwpmConditionIPRemotePort, fwpUint16, 547),
}
// The DHCPv6 client sends to the servers' multicast address, and they
// answer from their own.
dhcpv6Out := permit{"DHCPv6", slices.Concat(dhcpv6, []fwpmFilterCondition0{condition(&fwpmConditionIPRemoteAddress, fwpByteArray16Type, uintptr(unsafe.Pointer(&ipv6AllDHCPv6Servers)))})}
dhcpv6In := permit{"DHCPv6", dhcpv6}
for _, layer := range []struct {
key *windows.GUID
selfFlags uint32
throughTUN []fwpmFilterCondition0
key *windows.GUID
selfFlags uint32
via *windows.GUID // the interface that has to be the TUN as well
ipv6 bool
permits []permit
}{
{&fwpmLayerALEAuthConnectV4, fwpmFilterFlagClearActionRight, out},
{&fwpmLayerALEAuthRecvAcceptV4, 0, in},
{&fwpmLayerALEAuthConnectV6, fwpmFilterFlagClearActionRight, out},
{&fwpmLayerALEAuthRecvAcceptV6, 0, in},
{&fwpmLayerALEAuthConnectV4, fwpmFilterFlagClearActionRight, &fwpmConditionIPNexthopInterface, false, []permit{loopback, dhcp}},
{&fwpmLayerALEAuthRecvAcceptV4, 0, &fwpmConditionIPArrivalInterface, false, []permit{loopback, dhcp}},
{&fwpmLayerALEAuthConnectV6, fwpmFilterFlagClearActionRight, &fwpmConditionIPNexthopInterface, true, []permit{loopback, discovery, dhcpv6Out}},
{&fwpmLayerALEAuthRecvAcceptV6, 0, &fwpmConditionIPArrivalInterface, true, []permit{loopback, discovery, dhcpv6In}},
} {
if err := add(layer.key, "permit Xray", layer.selfFlags, fwpActionPermit, 4, self); err != nil {
throughTUN := permit{"through the TUN", []fwpmFilterCondition0{onTUN(&fwpmConditionIPLocalInterface), onTUN(layer.via)}}
if err := add(layer.key, "permit Xray", layer.selfFlags, fwpActionPermit, weightXray, self); err != nil {
return err
}
if dns {
if err := add(layer.key, "permit DNS through the TUN", 0, fwpActionPermit, 3, layer.throughTUN...); err != nil {
for _, p := range append(layer.permits, throughTUN) {
if err := add(layer.key, "permit "+p.name, 0, fwpActionPermit, weightPermit, p.conditions...); err != nil {
return err
}
if err := add(layer.key, "block DNS", 0, fwpActionBlock, 2, dns53); err != nil {
}
for _, l := range block {
prefix, ok := l.over(layer.ipv6)
if !ok {
continue // for the other IP version only
}
var protocols []uintptr
switch l.name {
case "tcp":
protocols = []uintptr{windows.IPPROTO_TCP}
case "udp":
protocols = []uintptr{windows.IPPROTO_UDP}
case "":
protocols = []uintptr{windows.IPPROTO_TCP, windows.IPPROTO_UDP}
case "icmp":
protocols = []uintptr{windows.IPPROTO_ICMP}
if layer.ipv6 {
protocols = []uintptr{windows.IPPROTO_ICMPV6}
}
case "all", "misconfigtun":
// every protocol
default:
continue // "allexceptlan" and "windnsclient" have filters of their own
}
var matches []fwpmFilterCondition0
for _, protocol := range protocols {
matches = append(matches, condition(&fwpmConditionIPProtocol, fwpUint8, protocol))
}
matches = slices.Concat(matches, toPorts(l), remote(prefix))
if err := add(layer.key, "block "+l.String(), 0, fwpActionBlock, weightBlock, matches...); err != nil {
return err
}
}
if allExceptLAN := block.prefixes("allexceptlan", layer.ipv6); len(allExceptLAN) > 0 {
// Lighter than the other values, which so still apply on the
// local network, DNS with "allexceptlan" among them.
if lan := lanWithin(allExceptLAN...); len(lan) > 0 {
if err := add(layer.key, "permit the local network", 0, fwpActionPermit, weightLAN, remote(lan...)...); err != nil {
return err
}
}
if err := add(layer.key, "block allexceptlan", 0, fwpActionBlock, weightAllExceptLAN, remote(allExceptLAN...)...); err != nil {
return err
}
}
}
// Since Windows 11 and Server 2022 (build 20348), the DNS Client service
// may also send the queries for an interface's servers over HTTPS or TLS,
// out through that interface and to any port. So there it may only
// connect through the TUN, except for mDNS and LLMNR, which stay on the
// local link (over an IP version only while it is not blocked altogether).
// Earlier versions only query port 53, and may run the service in one
// process with others, which the filters would catch as well. Like
// Windows Firewall's rules for it, they recognize the service by its SID,
// Since Windows 11 and Server 2022, the DNS Client service may also send
// the queries for an interface's servers over HTTPS or TLS, out through
// that interface and to any port. So there it may only connect through
// the TUN, except for mDNS and LLMNR on the local network. Like Windows
// Firewall's rules for it, the filters recognize the service by its SID,
// which Windows puts in the token of its process: the security descriptor
// grants that SID the right to match (FWP_ACTRL_MATCH_FILTER, CC in SDDL).
if _, _, build := windows.RtlGetNtVersionNumbers(); dns && build >= 20348 {
if slices.ContainsFunc(block, func(l leak) bool { return l.name == "windnsclient" }) {
sd, err := windows.SecurityDescriptorFromString("O:SYG:SYD:(A;;CCRC;;;" + dnsClientSID + ")")
if err != nil {
return err
@@ -340,85 +628,20 @@ func addLeakFilters(engine windows.Handle, tun winipcfg.LUID, dns, ipv4, ipv6 bo
// Conditions on the same field match when any of them does.
mdnsLLMNR := []fwpmFilterCondition0{dnsClient, condition(&fwpmConditionIPRemotePort, fwpUint16, 5353), condition(&fwpmConditionIPRemotePort, fwpUint16, 5355)}
for _, layer := range []struct {
key *windows.GUID
localLink bool
key *windows.GUID
ipv6 bool
}{
{&fwpmLayerALEAuthConnectV4, !ipv4},
{&fwpmLayerALEAuthConnectV6, !ipv6},
{&fwpmLayerALEAuthConnectV4, false},
{&fwpmLayerALEAuthConnectV6, true},
} {
if err := add(layer.key, "permit the DNS Client service through the TUN", 0, fwpActionPermit, 3, dnsClient, onTUN(&fwpmConditionIPLocalInterface), onTUN(&fwpmConditionIPNexthopInterface)); err != nil {
prefixes := block.prefixes("windnsclient", layer.ipv6)
if len(prefixes) == 0 {
continue
}
if err := add(layer.key, "permit the DNS Client service's mDNS and LLMNR", 0, fwpActionPermit, weightLocalLink, slices.Concat(mdnsLLMNR, remote(lanWithin(allAddresses(layer.ipv6))...))...); err != nil {
return err
}
if layer.localLink {
if err := add(layer.key, "permit the DNS Client service's mDNS and LLMNR", 0, fwpActionPermit, 3, mdnsLLMNR...); err != nil {
return err
}
}
if err := add(layer.key, "block the DNS Client service", 0, fwpActionBlock, 2, dnsClient); err != nil {
return err
}
}
}
// Both directions: replies to a connection accepted from outside would
// leave through the physical link as well.
loopback := fwpmFilterCondition0{
fieldKey: fwpmConditionFlags,
matchType: fwpMatchFlagsAllSet,
conditionValue: fwpValue0{typ: fwpUint32, value: fwpConditionFlagIsLoopback},
}
if ipv4 {
// DHCP keeps the addresses of the other interfaces, which Xray's own
// connections use.
dhcp := []fwpmFilterCondition0{
condition(&fwpmConditionIPProtocol, fwpUint8, windows.IPPROTO_UDP),
condition(&fwpmConditionIPLocalPort, fwpUint16, 68),
condition(&fwpmConditionIPRemotePort, fwpUint16, 67),
}
for _, layer := range []*windows.GUID{&fwpmLayerALEAuthConnectV4, &fwpmLayerALEAuthRecvAcceptV4} {
if err := add(layer, "permit IPv4 loopback", 0, fwpActionPermit, 1, loopback); err != nil {
return err
}
if err := add(layer, "permit DHCP", 0, fwpActionPermit, 1, dhcp...); err != nil {
return err
}
if err := add(layer, "block IPv4", 0, fwpActionBlock, 0); err != nil {
return err
}
}
}
if ipv6 {
// Neighbor and multicast listener discovery, ICMPv6 130-137 and 143,
// whose type and code sit where the local and remote port are.
discovery := []fwpmFilterCondition0{condition(&fwpmConditionIPProtocol, fwpUint8, windows.IPPROTO_ICMPV6)}
for _, typ := range []uintptr{130, 131, 132, 133, 134, 135, 136, 137, 143} {
discovery = append(discovery, condition(&fwpmConditionIPLocalPort, fwpUint16, typ))
}
discovery = append(discovery, condition(&fwpmConditionIPRemotePort, fwpUint16, 0))
dhcpv6 := []fwpmFilterCondition0{
condition(&fwpmConditionIPProtocol, fwpUint8, windows.IPPROTO_UDP),
condition(&fwpmConditionIPLocalPort, fwpUint16, 546),
condition(&fwpmConditionIPRemotePort, fwpUint16, 547),
}
for _, direction := range []struct {
layer *windows.GUID
dhcpv6 []fwpmFilterCondition0
}{
// The client sends to the servers' multicast address, and they
// answer from their own.
{&fwpmLayerALEAuthConnectV6, slices.Concat(dhcpv6, []fwpmFilterCondition0{condition(&fwpmConditionIPRemoteAddress, fwpByteArray16Type, uintptr(unsafe.Pointer(&ipv6AllDHCPv6Servers)))})},
{&fwpmLayerALEAuthRecvAcceptV6, dhcpv6},
} {
if err := add(direction.layer, "permit IPv6 loopback", 0, fwpActionPermit, 1, loopback); err != nil {
return err
}
if err := add(direction.layer, "permit IPv6 neighbor and multicast listener discovery", 0, fwpActionPermit, 1, discovery...); err != nil {
return err
}
if err := add(direction.layer, "permit DHCPv6", 0, fwpActionPermit, 1, direction.dhcpv6...); err != nil {
return err
}
if err := add(direction.layer, "block IPv6", 0, fwpActionBlock, 0); err != nil {
if err := add(layer.key, "block the DNS Client service", 0, fwpActionBlock, weightDNSClient, slices.Concat([]fwpmFilterCondition0{dnsClient}, remote(prefixes...))...); err != nil {
return err
}
}
@@ -445,13 +668,22 @@ func addFilter(engine windows.Handle, sublayer, layer *windows.GUID, name string
return nil
}
// dnsOutsideTUN returns the servers outside all of prefixes, the TUN's own
// subnets and routes: queries to them cannot go through the TUN.
func dnsOutsideTUN(servers []netip.Addr, prefixes []netip.Prefix) []netip.Addr {
// dnsOutsideTUN returns the servers whose queries block blocks: those outside
// all of prefixes, the TUN's own subnets and routes, as queries to them cannot
// go through the TUN, and inside the CIDR of a value that keeps DNS inside the
// TUN, or of "misconfigtun". Loopback ones, local resolvers, are not among
// them, as loopback stays allowed.
func dnsOutsideTUN(servers []netip.Addr, prefixes []netip.Prefix, block leakFilters) []netip.Addr {
var outside []netip.Addr
for _, server := range servers {
server = server.Unmap()
if !slices.ContainsFunc(prefixes, func(p netip.Prefix) bool { return p.Contains(server) }) {
if server.IsLoopback() || slices.ContainsFunc(prefixes, func(p netip.Prefix) bool { return p.Contains(server) }) {
continue
}
if slices.ContainsFunc(block, func(l leak) bool {
prefix, ok := l.over(server.Is6())
return ok && prefix.Contains(server) && (l.blocksDNS() || l.name == "misconfigtun")
}) {
outside = append(outside, server)
}
}
+141 -5
View File
@@ -40,6 +40,21 @@ func TestWFPStructLayout(t *testing.T) {
[]uintptr{unsafe.Sizeof(value), unsafe.Offsetof(value.value)},
[]uintptr{16, 8}, []uintptr{8, 4})
var ports fwpRange0
check("FWP_RANGE0",
[]uintptr{unsafe.Sizeof(ports), unsafe.Offsetof(ports.high)},
[]uintptr{32, 16}, []uintptr{16, 8})
var v4 fwpV4AddrAndMask
check("FWP_V4_ADDR_AND_MASK",
[]uintptr{unsafe.Sizeof(v4), unsafe.Offsetof(v4.mask)},
[]uintptr{8, 4}, []uintptr{8, 4})
var v6 fwpV6AddrAndMask
check("FWP_V6_ADDR_AND_MASK",
[]uintptr{unsafe.Sizeof(v6), unsafe.Offsetof(v6.prefixLength)},
[]uintptr{17, 16}, []uintptr{17, 16})
var display fwpmDisplayData0
check("FWPM_DISPLAY_DATA0",
[]uintptr{unsafe.Sizeof(display), unsafe.Offsetof(display.description)},
@@ -114,8 +129,101 @@ func TestLeakFiltersAccepted(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if err := addLeakFilters(engine, loopback, true, true, true); err != nil {
skipUnlessElevated(err)
// Each with a sublayer of its own.
for _, c := range []struct {
values []string
route4, route6 bool
}{
{[]string{"dns", "misconfigtun", "all", "tcp/443", "udp/1000-2000", "tcp", "icmp", "10-20", "1-65535"}, false, false},
// CIDRs, and the local network's addresses.
{[]string{"1.1.1.0/24:tcp/80", "[2001:db8::/32]:udp/5000-6000", "[::/0]:icmp", "8.8.8.8/32:all", "[2000::/3]:misconfigtun", "9.9.9.9/32:dns"}, true, false},
{[]string{"allexceptlan", "udp/1000-2000", "icmp", "192.168.1.5/32:all"}, true, false},
{[]string{"0.0.0.0/0:allexceptlan", "[::/0]:all", "[::/0]:windnsclient", "1.0.0.0/8:windnsclient"}, true, true},
{[]string{"[::/0]:allexceptlan", "0.0.0.0/0:misconfigtun", "10.0.0.0/8:allexceptlan", "1.0.0.0/8:allexceptlan"}, false, true},
} {
leaks, err := ExpandWfpBlockLeak(c.values)
if err != nil {
t.Fatal(err)
}
if err := addLeakFilters(engine, loopback, newLeakFilters(leaks, c.route4, c.route6)); err != nil {
skipUnlessElevated(err)
}
}
}
func TestNewLeakFilters(t *testing.T) {
for _, c := range []struct {
values []string
route4, route6 bool
blocked string
systemDNS bool
}{
{[]string{"dns", "misconfigtun"}, true, false, "53, 853, windnsclient, [::/0]:misconfigtun", true},
{[]string{"tcp/853", "udp/853", "misconfigtun"}, true, true, "tcp/853, udp/853", false},
{[]string{"all"}, true, true, "all", true},
{[]string{"all", "misconfigtun"}, true, true, "all", true},
{[]string{"misconfigtun"}, true, false, "[::/0]:misconfigtun", false},
{[]string{"misconfigtun"}, false, true, "0.0.0.0/0:misconfigtun", false},
{[]string{"misconfigtun"}, false, false, "misconfigtun", false},
{[]string{"tcp/10-20", "udp", "icmp"}, true, true, "tcp/10-20, udp, icmp", true},
{[]string{"tcp/54-60", "icmp", "54-60"}, true, true, "tcp/54-60, icmp, 54-60", false},
{[]string{"allexceptlan"}, true, true, "allexceptlan, 53, 853, windnsclient", true},
{[]string{"allexceptlan", "all"}, true, true, "allexceptlan, 53, 853, windnsclient, all", true},
{[]string{"misconfigtun", "allexceptlan"}, true, false, "[::/0]:misconfigtun, allexceptlan, 53, 853, windnsclient", true},
// Limited to the addresses of a CIDR.
{[]string{"0.0.0.0/0:TCP/10-20", "[::/0]:udp", "0.0.0.0/0:icmp"}, true, true, "0.0.0.0/0:tcp/10-20, [::/0]:udp, 0.0.0.0/0:icmp", true},
{[]string{"[::/0]:tcp/54-60", "[::/0]:icmp"}, true, true, "[::/0]:tcp/54-60, [::/0]:icmp", false},
{[]string{"0.0.0.0/0:windnsclient"}, true, true, "0.0.0.0/0:windnsclient", true},
{[]string{"1.1.1.0/24:53"}, true, true, "1.1.1.0/24:53", true},
{[]string{"[::/0]:all"}, true, false, "[::/0]:all", true},
{[]string{"0.0.0.0/0:allexceptlan", "[::/0]:all"}, true, true, "0.0.0.0/0:allexceptlan, 0.0.0.0/0:53, 0.0.0.0/0:853, 0.0.0.0/0:windnsclient, [::/0]:all", true},
{[]string{"0.0.0.0/0:misconfigtun"}, true, false, "", false},
{[]string{"[::/0]:misconfigtun"}, true, false, "[::/0]:misconfigtun", false},
{[]string{"[2000::/3]:misconfigtun", "1.0.0.0/8:misconfigtun"}, true, false, "[2000::/3]:misconfigtun", false},
{[]string{"misconfigtun", "[::/0]:misconfigtun"}, true, false, "[::/0]:misconfigtun", false},
{[]string{"0.0.0.0/0:misconfigtun", "[::/0]:allexceptlan"}, false, true, "0.0.0.0/0:misconfigtun, [::/0]:allexceptlan, [::/0]:53, [::/0]:853, [::/0]:windnsclient", true},
} {
leaks, err := ExpandWfpBlockLeak(c.values)
if err != nil {
t.Fatal(err)
}
block := newLeakFilters(leaks, c.route4, c.route6)
if got := block.String(); got != c.blocked {
t.Errorf("%v: String() = %q, want %q", c.values, got, c.blocked)
}
if got := block.systemDNS(); got != c.systemDNS {
t.Errorf("%v: systemDNS() = %v", c.values, got)
}
}
// Without the port 53 values "allexceptlan" brings along, a resolver on
// the local network, like the router's, stays reachable.
if (leakFilters{{name: "allexceptlan"}}).systemDNS() {
t.Error("systemDNS() = true with the local network open")
}
}
func TestLanWithin(t *testing.T) {
prefixes := func(cidrs ...string) []netip.Prefix {
var prefixes []netip.Prefix
for _, cidr := range cidrs {
prefixes = append(prefixes, netip.MustParsePrefix(cidr))
}
return prefixes
}
for _, c := range []struct {
within, lan []netip.Prefix
}{
{nil, nil},
{prefixes("0.0.0.0/0"), prefixes("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16", "224.0.0.0/4", "255.255.255.255/32")},
{prefixes("::/0"), prefixes("fc00::/7", "fe80::/10", "ff00::/8")},
{prefixes("1.0.0.0/8", "10.1.0.0/16"), prefixes("10.1.0.0/16")},
{prefixes("192.0.0.0/2"), prefixes("192.168.0.0/16", "224.0.0.0/4", "255.255.255.255/32")},
{prefixes("2000::/3", "fe80::/64", "fe80::/10"), prefixes("fe80::/64", "fe80::/10")},
{prefixes("8.8.8.8/32", "2001:db8::/32"), nil},
} {
if got := lanWithin(c.within...); !slices.Equal(got, c.lan) {
t.Errorf("%v: got %v, want %v", c.within, got, c.lan)
}
}
}
@@ -140,10 +248,38 @@ func TestDNSOutsideTUN(t *testing.T) {
netip.MustParseAddr("::ffff:203.0.113.54"),
netip.MustParseAddr("8.8.8.8"),
netip.MustParseAddr("2001:db8::53"),
// Local resolvers, reachable over loopback whatever is blocked.
netip.MustParseAddr("127.0.0.1"),
netip.MustParseAddr("::ffff:127.0.0.53"),
netip.MustParseAddr("::1"),
}
want := []netip.Addr{netip.MustParseAddr("8.8.8.8"), netip.MustParseAddr("2001:db8::53")}
if got := dnsOutsideTUN(servers, prefixes); !slices.Equal(got, want) {
t.Errorf("got %v, want %v", got, want)
// Only to the addresses of their CIDRs, if any, and "misconfigtun" over
// an IP version without routes to the TUN blocks DNS as well.
for _, c := range []struct {
values []string
route4, route6 bool
want []string
}{
{[]string{"dns"}, true, true, []string{"8.8.8.8", "2001:db8::53"}},
{[]string{"0.0.0.0/0:53"}, true, true, []string{"8.8.8.8"}},
{[]string{"8.8.8.0/24:windnsclient", "[2001:db8::/32]:tcp/853"}, true, true, []string{"8.8.8.8"}},
{[]string{"[2001:db8::53/128]:all", "9.9.9.9/32:53"}, true, true, []string{"2001:db8::53"}},
{[]string{"misconfigtun"}, true, false, []string{"2001:db8::53"}},
{[]string{"misconfigtun"}, true, true, nil},
{[]string{"tcp/853", "udp/5353", "icmp", "10.0.0.0/8:all"}, true, true, nil},
{[]string{"[::/0]:allexceptlan"}, true, true, []string{"2001:db8::53"}},
} {
leaks, err := ExpandWfpBlockLeak(c.values)
if err != nil {
t.Fatal(err)
}
var want []netip.Addr
for _, server := range c.want {
want = append(want, netip.MustParseAddr(server))
}
if got := dnsOutsideTUN(servers, prefixes, newLeakFilters(leaks, c.route4, c.route6)); !slices.Equal(got, want) {
t.Errorf("%v: got %v, want %v", c.values, got, want)
}
}
}
+160
View File
@@ -0,0 +1,160 @@
package tun
import (
"net/netip"
"slices"
"strconv"
"strings"
"github.com/xtls/xray-core/common/errors"
)
// dnsLeaks is what "dns" in autoSystemWfpBlockLeak stands for: DNS over TCP
// and UDP, DNS over TLS and QUIC, and Windows' DNS Client service, which may
// also use other protocols and ports.
var dnsLeaks = []string{"53", "853", "windnsclient"}
// leak is a value of autoSystemWfpBlockLeak: "dns", "windnsclient",
// "misconfigtun", "all", "allexceptlan", "icmp", or "tcp" or "udp", the latter
// two with "/<port>" or "/<low>-<high>" for some ports only, such as "tcp/853"
// or "udp/10-20", or such ports alone for TCP and UDP, such as "53". A CIDR in
// front limits any of them to its addresses, an IPv6 one in brackets, such as
// "1.1.1.0/24:53" or "[::/0]:all".
type leak struct {
prefix netip.Prefix // all addresses of both IP versions if not valid
name string // empty for TCP and UDP to some ports
low, high uint16 // the ports of TCP and UDP, both 0 for all
}
func (l leak) String() string {
s := l.name
if l.low != 0 {
if s != "" {
s += "/"
}
s += strconv.Itoa(int(l.low))
if l.high != l.low {
s += "-" + strconv.Itoa(int(l.high))
}
}
switch {
case !l.prefix.IsValid():
return s
case l.prefix.Addr().Is6():
return "[" + l.prefix.String() + "]:" + s
default:
return l.prefix.String() + ":" + s
}
}
// covers tells whether l includes connections of protocol, "tcp" or "udp", to
// port, to at least some addresses.
func (l leak) covers(protocol string, port uint16) bool {
switch l.name {
case "all":
return true
case "", protocol:
return l.low == 0 || l.low <= port && port <= l.high
}
return false
}
// blocksDNS tells whether l keeps the system's DNS, TCP or UDP port 53 or
// Windows' DNS Client service, inside the TUN, to at least some addresses.
func (l leak) blocksDNS() bool {
return l.name == "windnsclient" || l.covers("udp", 53) || l.covers("tcp", 53)
}
// parseLeak parses a lower case value of autoSystemWfpBlockLeak. Ports go from
// 1 to 65535, low not above high, and "tcp/1-65535" stands for all of them, as
// does "tcp".
func parseLeak(value string) (leak, bool) {
var l leak
cidr, rest, hasCIDR := strings.Cut(value, ":")
ipv6 := strings.HasPrefix(value, "[")
if ipv6 {
cidr, rest, hasCIDR = strings.Cut(value[1:], "]:")
}
if hasCIDR {
// IPv4-mapped IPv6 ones would not match: Windows checks such
// connections as IPv4.
prefix, err := netip.ParsePrefix(cidr)
if err != nil || prefix.Addr().Is6() != ipv6 || prefix.Addr().Is4In6() {
return leak{}, false
}
l.prefix, value = prefix.Masked(), rest
}
name, ports, hasPorts := strings.Cut(value, "/")
switch name {
case "tcp", "udp":
l.name = name
if !hasPorts {
return l, true
}
case "dns", "windnsclient", "misconfigtun", "all", "allexceptlan", "icmp":
l.name = name
return l, !hasPorts
default:
if hasPorts {
return leak{}, false
}
ports = name // for TCP and UDP
}
lowText, highText, isRange := strings.Cut(ports, "-")
low, err := strconv.ParseUint(lowText, 10, 16)
if err != nil || low == 0 {
return leak{}, false
}
high := low
if isRange {
if high, err = strconv.ParseUint(highText, 10, 16); err != nil || high < low {
return leak{}, false
}
}
if l.name == "" || low != 1 || high != 65535 {
l.low, l.high = uint16(low), uint16(high)
}
return l, true
}
// ExpandWfpBlockLeak checks the values of autoSystemWfpBlockLeak and returns
// them in lower case, in order and each once, with "dns" replaced by what it
// stands for, "allexceptlan" followed by that, CIDRs masked, and ports written
// the same way, "tcp/1-65535" as "tcp" say.
func ExpandWfpBlockLeak(values []string) ([]string, error) {
var expanded []string
add := func(l leak) {
if value := l.String(); !slices.Contains(expanded, value) {
expanded = append(expanded, value)
}
}
for _, value := range values {
l, ok := parseLeak(strings.ToLower(value))
if !ok {
return nil, errors.New("unknown autoSystemWfpBlockLeak value: ", value)
}
if l.name != "dns" {
add(l)
}
// The local network is no way out for DNS with "allexceptlan", as a
// resolver there, like the router, would forward outside the TUN.
if l.name == "dns" || l.name == "allexceptlan" {
for _, dnsLeak := range dnsLeaks {
dns, _ := parseLeak(dnsLeak)
dns.prefix = l.prefix
add(dns)
}
}
}
return expanded, nil
}
// BlocksSystemDNS tells whether expanded autoSystemWfpBlockLeak values keep the
// system's DNS (TCP or UDP port 53, or Windows' DNS Client service) inside the
// TUN, to at least some addresses, which then needs DNS servers of its own.
func BlocksSystemDNS(leaks []string) bool {
return slices.ContainsFunc(leaks, func(value string) bool {
l, _ := parseLeak(value)
return l.blocksDNS()
})
}
+122
View File
@@ -0,0 +1,122 @@
package tun
import (
"slices"
"testing"
)
func TestExpandWfpBlockLeak(t *testing.T) {
for _, c := range []struct {
values []string
leaks []string
systemDNS bool
}{
{nil, nil, false},
{[]string{"dns"}, []string{"53", "853", "windnsclient"}, true},
{[]string{"DNS", "MisconfigTUN"}, []string{"53", "853", "windnsclient", "misconfigtun"}, true},
{[]string{"tcp/853", "dns", "TCP/0853"}, []string{"tcp/853", "53", "853", "windnsclient"}, true},
{[]string{"tcp/853", "udp/853", "misconfigtun"}, []string{"tcp/853", "udp/853", "misconfigtun"}, false},
{[]string{"udp/65535", "tcp/1"}, []string{"udp/65535", "tcp/1"}, false},
{[]string{"tcp/53"}, []string{"tcp/53"}, true},
{[]string{"windnsclient"}, []string{"windnsclient"}, true},
{[]string{"ALL"}, []string{"all"}, true},
{[]string{"tcp", "TCP", "UDP"}, []string{"tcp", "udp"}, true},
{[]string{"tcp/1-65535", "udp/1-65535"}, []string{"tcp", "udp"}, true},
{[]string{"udp/10-20", "udp/53-53", "tcp/50-60"}, []string{"udp/10-20", "udp/53", "tcp/50-60"}, true},
{[]string{"tcp/54-60", "udp/1-52"}, []string{"tcp/54-60", "udp/1-52"}, false},
{[]string{"ICMP", "icmp"}, []string{"icmp"}, false},
{[]string{"AllExceptLAN"}, []string{"allexceptlan", "53", "853", "windnsclient"}, true},
{[]string{"dns", "allexceptlan", "all"}, []string{"53", "853", "windnsclient", "allexceptlan", "all"}, true},
// Ports alone, for TCP and UDP.
{[]string{"53"}, []string{"53"}, true},
{[]string{"10-20", "0053", "853-853", "53"}, []string{"10-20", "53", "853"}, true},
{[]string{"54-60", "1-52", "65535"}, []string{"54-60", "1-52", "65535"}, false},
{[]string{"1-65535"}, []string{"1-65535"}, true},
{[]string{"853", "tcp/853"}, []string{"853", "tcp/853"}, false},
// Limited to the addresses of a CIDR.
{[]string{"1.1.1.0/24:53"}, []string{"1.1.1.0/24:53"}, true},
{[]string{"1.1.1.1/24:TCP/853", "[2001:DB8::1/32]:udp/10-20", "[::/0]:ICMP"}, []string{"1.1.1.0/24:tcp/853", "[2001:db8::/32]:udp/10-20", "[::/0]:icmp"}, false},
{[]string{"0.0.0.0/0:dns"}, []string{"0.0.0.0/0:53", "0.0.0.0/0:853", "0.0.0.0/0:windnsclient"}, true},
{[]string{"[::/0]:all"}, []string{"[::/0]:all"}, true},
{[]string{"10.0.0.1/32:windnsclient"}, []string{"10.0.0.1/32:windnsclient"}, true},
{[]string{"[2000::/3]:misconfigtun"}, []string{"[2000::/3]:misconfigtun"}, false},
{[]string{"1.1.1.0/24:tcp/1-65535", "1.1.1.0/24:udp/53-53"}, []string{"1.1.1.0/24:tcp", "1.1.1.0/24:udp/53"}, true},
{[]string{"1.1.1.0/24:20-30", "[::/0]:10-10"}, []string{"1.1.1.0/24:20-30", "[::/0]:10"}, false},
{[]string{"0.0.0.0/0:allexceptlan", "[::/0]:all"}, []string{"0.0.0.0/0:allexceptlan", "0.0.0.0/0:53", "0.0.0.0/0:853", "0.0.0.0/0:windnsclient", "[::/0]:all"}, true},
{[]string{"1.1.1.1/32:53", "1.1.1.1/32:0053", "1.1.1.1/24:53", "1.1.1.0/24:53", "53"}, []string{"1.1.1.1/32:53", "1.1.1.0/24:53", "53"}, true},
{[]string{"1.2.3.4/0:icmp", "[::1/0]:icmp", "icmp"}, []string{"0.0.0.0/0:icmp", "[::/0]:icmp", "icmp"}, false},
} {
leaks, err := ExpandWfpBlockLeak(c.values)
if err != nil {
t.Errorf("%v: %v", c.values, err)
continue
}
if !slices.Equal(leaks, c.leaks) {
t.Errorf("%v: got %v, want %v", c.values, leaks, c.leaks)
}
if got := BlocksSystemDNS(leaks); got != c.systemDNS {
t.Errorf("%v: BlocksSystemDNS() = %v", c.values, got)
}
}
for _, value := range []string{
"", "ip", "tcp/", "udp/65536", "tcp/-1", "tcp/+53", "tcp/53/1", "udp/abc", "icmp/0", "icmp/8", "tcp:53", " dns",
"tcp/0-10", "tcp/20-10", "tcp/5-", "tcp/-5", "tcp/1-2-3", "tcp/1-65536", "udp/ 53", "all/0", "dns/53", "windnsclient/53",
"tcp/0", "udp/0", "tcp/00", "udp/0-0", "udp/65535-1",
// Ports alone.
"0", "00", "65536", "0-10", "20-10", "5-", "-5", "-", "1-2-3", "1-65536", "+53", " 53", "53 ", "53/1", "53/tcp", "0x35",
// IP versions are CIDRs now.
"ipv4", "ipv6", "ipv4:udp", "ipv6:all", "ipv4:dns", "tcpv4", "udpv6", "icmpv4", "tcp4", "dnsv4",
// CIDRs, IPv6 ones in brackets, but neither single addresses nor
// CIDRs on their own.
"1.1.1.0/24", "[2001:db8::/32]", "1.1.1.0/24:", "[::/0]:", ":udp", "[]:udp", "[:udp", "[::/0]udp",
"1.1.1.1:53", "[::1]:53", "[2001:db8::1]:tcp/53", "2001:db8::/32:53", "::/0:all", "[1.1.1.0/24]:53",
"[2001:db8::]/32:53", "1.1.1.0/33:53", "[::/129]:53", "1.1.1.0/24:1.1.1.0/24:53", "[::/0]:[::/0]:53",
"1.1.1.0/24 :53", "1.1.1.0/24: 53", " 1.1.1.0/24:53", "1.1.1.0/24::53", "[fe80::%1/64]:53", "geoip:cn:53",
"1.1.1.0/24:udp/0", "[::/0]:icmp/8", "1.1.1.0/24:dns/53", "[::/0]:20-10", "1.1.1.0/24:ipv4:53", "1.1.1.0/24:geoip",
"[::ffff:1.1.1.0/120]:53", "[::ffff:0:0/96]:all",
} {
if _, err := ExpandWfpBlockLeak([]string{value}); err == nil {
t.Errorf("%q was accepted", value)
}
}
}
func TestLeakCovers(t *testing.T) {
for _, c := range []struct {
value string
protocol string
port uint16
covers bool
}{
{"tcp", "tcp", 1, true},
{"tcp", "udp", 53, false},
{"udp/53", "udp", 53, true},
{"udp/53", "udp", 54, false},
{"tcp/10-20", "tcp", 10, true},
{"tcp/10-20", "tcp", 20, true},
{"tcp/10-20", "tcp", 21, false},
{"tcp/10-20", "tcp", 9, false},
{"53", "udp", 53, true},
{"53", "tcp", 53, true},
{"10-20", "tcp", 15, true},
{"10-20", "udp", 21, false},
{"1.1.1.0/24:udp/53", "udp", 53, true},
{"[::/0]:tcp", "tcp", 443, true},
{"[2001:db8::/32]:53", "tcp", 53, true},
{"0.0.0.0/0:icmp", "udp", 53, false},
{"all", "udp", 53, true},
{"1.1.1.1/32:all", "tcp", 1, true},
{"allexceptlan", "udp", 53, false},
{"misconfigtun", "udp", 53, false},
{"windnsclient", "udp", 53, false},
} {
l, ok := parseLeak(c.value)
if !ok {
t.Errorf("%q was not parsed", c.value)
continue
}
if got := l.covers(c.protocol, c.port); got != c.covers {
t.Errorf("%q covers %s/%d = %v", c.value, c.protocol, c.port, got)
}
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/vless/account.proto
package vless
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/vless/encoding/addons.proto
package encoding
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/vless/inbound/config.proto
package inbound
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/vless/outbound/config.proto
package outbound
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/vmess/account.proto
package vmess
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/vmess/inbound/config.proto
package inbound
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/vmess/outbound/config.proto
package outbound
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: proxy/wireguard/config.proto
package wireguard
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/config.proto
package internet
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/filter/config.proto
package filter
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/fragment/config.proto
package fragment
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/header/custom/config.proto
package custom
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/mkcp/aes128gcm/config.proto
package aes128gcm
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/mkcp/header/config.proto
package header
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/mkcp/original/config.proto
package original
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/noise/config.proto
package noise
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/realm/config.proto
package realm
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/salamander/config.proto
package salamander
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/sudoku/config.proto
package sudoku
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/udphop/config.proto
package udphop
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/xdns/config.proto
package xdns
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/xicmp/config.proto
package xicmp
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/finalmask/xmc/config.proto
package xmc
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/grpc/config.proto
package grpc
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/grpc/encoding/stream.proto
package encoding
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.5
// - protoc-gen-go-grpc v1.6.2
// - protoc (unknown)
// source: transport/internet/grpc/encoding/stream.proto
package encoding
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/headers/http/config.proto
package http
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/headers/noop/config.proto
package noop
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/httpupgrade/config.proto
package httpupgrade
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/hysteria/config.proto
package hysteria
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/kcp/config.proto
package kcp
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// protoc-gen-go v1.36.12
// protoc (unknown)
// source: transport/internet/masque/config.proto
package masque

Some files were not shown because too many files have changed in this diff Show More