mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-23 01:56:48 +08:00
Compare commits
68 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
179eec707f | ||
![]() |
cedbb3f173 | ||
![]() |
4a4e160a54 | ||
![]() |
2e56fe11e1 | ||
![]() |
7b7084f825 | ||
![]() |
d5a195458b | ||
![]() |
7cf30d5101 | ||
![]() |
598e15aed2 | ||
![]() |
708ce026ca | ||
![]() |
217844cc37 | ||
![]() |
f20c445974 | ||
![]() |
6e902b24ae | ||
![]() |
70b63e21a5 | ||
![]() |
726a722019 | ||
![]() |
f4a048aa0c | ||
![]() |
bf94fb53ca | ||
![]() |
1d13a8da49 | ||
![]() |
90c81e8459 | ||
![]() |
364086c974 | ||
![]() |
f65c21337c | ||
![]() |
4bf8b6d89c | ||
![]() |
7a778d74d0 | ||
![]() |
d3533abe3c | ||
![]() |
95a68a6d73 | ||
![]() |
7f2fad73d4 | ||
![]() |
3ed14c2fcd | ||
![]() |
b63049f404 | ||
![]() |
a9e11075d1 | ||
![]() |
e564d9ef7e | ||
![]() |
6c936e2fd3 | ||
![]() |
b2d8168284 | ||
![]() |
e0910ab4d9 | ||
![]() |
d46af8b5d4 | ||
![]() |
58daa2f788 | ||
![]() |
0470381fe2 | ||
![]() |
b0e7ad9663 | ||
![]() |
8382b29922 | ||
![]() |
41d3f31447 | ||
![]() |
9b93b90fa9 | ||
![]() |
b15fffaac6 | ||
![]() |
8884e948fe | ||
![]() |
4e63c22197 | ||
![]() |
36961ed882 | ||
![]() |
de54d4b08f | ||
![]() |
32713bcc0e | ||
![]() |
9dec65e367 | ||
![]() |
3fe85449a9 | ||
![]() |
e0526c27b3 | ||
![]() |
a0a32ee00d | ||
![]() |
60b06877bf | ||
![]() |
9adce5a6c4 | ||
![]() |
100edc370b | ||
![]() |
819717d278 | ||
![]() |
fcc9d97074 | ||
![]() |
439c91d509 | ||
![]() |
924fe16077 | ||
![]() |
3de5af0611 | ||
![]() |
d7cd71b741 | ||
![]() |
f50eff5ebb | ||
![]() |
db32ce6fd9 | ||
![]() |
ad1807dd99 | ||
![]() |
e1a5392beb | ||
![]() |
24f564b401 | ||
![]() |
54af48a1ae | ||
![]() |
055fb51ed9 | ||
![]() |
6380abca73 | ||
![]() |
1dae2c5636 | ||
![]() |
e9ea658852 |
12
.github/build/friendly-filenames.json
vendored
12
.github/build/friendly-filenames.json
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"android-arm64": { "friendlyName": "android-arm64-v8a"},
|
"android-arm64": { "friendlyName": "android-arm64-v8a" },
|
||||||
"darwin-amd64": { "friendlyName": "macos-64" },
|
"darwin-amd64": { "friendlyName": "macos-64" },
|
||||||
"darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
|
"darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
|
||||||
"dragonfly-amd64": { "friendlyName": "dragonfly-64" },
|
"dragonfly-amd64": { "friendlyName": "dragonfly-64" },
|
||||||
@@ -15,19 +15,19 @@
|
|||||||
"linux-arm7": { "friendlyName": "linux-arm32-v7a" },
|
"linux-arm7": { "friendlyName": "linux-arm32-v7a" },
|
||||||
"linux-mips64le": { "friendlyName": "linux-mips64le" },
|
"linux-mips64le": { "friendlyName": "linux-mips64le" },
|
||||||
"linux-mips64": { "friendlyName": "linux-mips64" },
|
"linux-mips64": { "friendlyName": "linux-mips64" },
|
||||||
"linux-mipsle": { "friendlyName": "linux-mips32le" },
|
|
||||||
"linux-mipslesoftfloat": { "friendlyName": "linux-mips32le-softfloat" },
|
"linux-mipslesoftfloat": { "friendlyName": "linux-mips32le-softfloat" },
|
||||||
"linux-mips": { "friendlyName": "linux-mips32" },
|
"linux-mipsle": { "friendlyName": "linux-mips32le" },
|
||||||
"linux-mipssoftfloat": { "friendlyName": "linux-mips32-softfloat" },
|
"linux-mipssoftfloat": { "friendlyName": "linux-mips32-softfloat" },
|
||||||
"linux-riscv64": { "friendlyName": "linux-riscv64" },
|
"linux-mips": { "friendlyName": "linux-mips32" },
|
||||||
|
"linux-ppc64le": { "friendlyName": "linux-ppc64le" },
|
||||||
"linux-ppc64": { "friendlyName": "linux-ppc64" },
|
"linux-ppc64": { "friendlyName": "linux-ppc64" },
|
||||||
"linux-ppc64le": { "friendlyName": "linux-rppc64le" },
|
"linux-riscv64": { "friendlyName": "linux-riscv64" },
|
||||||
"linux-s390x": { "friendlyName": "linux-s390x" },
|
"linux-s390x": { "friendlyName": "linux-s390x" },
|
||||||
"openbsd-386": { "friendlyName": "openbsd-32" },
|
"openbsd-386": { "friendlyName": "openbsd-32" },
|
||||||
"openbsd-amd64": { "friendlyName": "openbsd-64" },
|
"openbsd-amd64": { "friendlyName": "openbsd-64" },
|
||||||
"openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" },
|
"openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" },
|
||||||
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
|
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
|
||||||
"windows-amd64": { "friendlyName": "windows-64" },
|
|
||||||
"windows-386": { "friendlyName": "windows-32" },
|
"windows-386": { "friendlyName": "windows-32" },
|
||||||
|
"windows-amd64": { "friendlyName": "windows-64" },
|
||||||
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
||||||
}
|
}
|
||||||
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@@ -99,8 +99,8 @@ jobs:
|
|||||||
goarm: 7
|
goarm: 7
|
||||||
# END OPENBSD ARM
|
# END OPENBSD ARM
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
#配置编译环境
|
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
GOOS: ${{ matrix.goos }}
|
GOOS: ${{ matrix.goos }}
|
||||||
GOARCH: ${{ matrix.goarch }}
|
GOARCH: ${{ matrix.goarch }}
|
||||||
@@ -173,16 +173,8 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
pushd build_assets || exit 1
|
pushd build_assets || exit 1
|
||||||
touch -mt 202101010000 *
|
touch -mt $(date +%Y01010000) *
|
||||||
zip -9vr ../Xray-$ASSET_NAME.zip .
|
zip -9vr ../Xray-$ASSET_NAME.zip .
|
||||||
for CORE in $(ls xray*)
|
|
||||||
do
|
|
||||||
COREDGST=$CORE.dgst
|
|
||||||
for METHOD in {"md5","sha1","sha256","sha512"}
|
|
||||||
do
|
|
||||||
openssl dgst -$METHOD $CORE | sed 's/([^)]*)//g' >>$COREDGST
|
|
||||||
done
|
|
||||||
done
|
|
||||||
popd || exit 1
|
popd || exit 1
|
||||||
FILE=./Xray-$ASSET_NAME.zip
|
FILE=./Xray-$ASSET_NAME.zip
|
||||||
DGST=$FILE.dgst
|
DGST=$FILE.dgst
|
||||||
|
@@ -23,8 +23,9 @@
|
|||||||
- [Xray4Magisk](https://github.com/CerteKim/Xray4Magisk)
|
- [Xray4Magisk](https://github.com/CerteKim/Xray4Magisk)
|
||||||
- [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)
|
- [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)
|
||||||
- Homebrew
|
- Homebrew
|
||||||
- [Repository 0](https://github.com/N4FA/homebrew-xray)
|
- `brew install xray`
|
||||||
- [Repository 1](https://github.com/xiruizhao/homebrew-xray)
|
- [(Tap) Repository 0](https://github.com/N4FA/homebrew-xray)
|
||||||
|
- [(Tap) Repository 1](https://github.com/xiruizhao/homebrew-xray)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
"github.com/xtls/xray-core/features/policy"
|
"github.com/xtls/xray-core/features/policy"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
@@ -175,17 +176,28 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
|
|||||||
return inboundLink, outboundLink
|
return inboundLink, outboundLink
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldOverride(result SniffResult, request session.SniffingRequest) bool {
|
func shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
|
||||||
domain := result.Domain()
|
domain := result.Domain()
|
||||||
for _, d := range request.ExcludeForDomain {
|
for _, d := range request.ExcludeForDomain {
|
||||||
if domain == d {
|
if strings.ToLower(domain) == d {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var fakeDNSEngine dns.FakeDNSEngine
|
||||||
protocol := result.Protocol()
|
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
|
||||||
|
fakeDNSEngine = fdns
|
||||||
|
})
|
||||||
|
protocolString := result.Protocol()
|
||||||
|
if resComp, ok := result.(SnifferResultComposite); ok {
|
||||||
|
protocolString = resComp.ProtocolForDomainResult()
|
||||||
|
}
|
||||||
for _, p := range request.OverrideDestinationForProtocol {
|
for _, p := range request.OverrideDestinationForProtocol {
|
||||||
if strings.HasPrefix(protocol, p) {
|
if strings.HasPrefix(protocolString, p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if fakeDNSEngine != nil && protocolString != "bittorrent" && p == "fakedns" &&
|
||||||
|
destination.Address.Family().IsIP() && fakeDNSEngine.GetFakeIPRange().Contains(destination.Address.IP()) {
|
||||||
|
newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,19 +222,33 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
|||||||
ctx = session.ContextWithContent(ctx, content)
|
ctx = session.ContextWithContent(ctx, content)
|
||||||
}
|
}
|
||||||
sniffingRequest := content.SniffingRequest
|
sniffingRequest := content.SniffingRequest
|
||||||
if destination.Network != net.Network_TCP || !sniffingRequest.Enabled {
|
switch {
|
||||||
|
case !sniffingRequest.Enabled:
|
||||||
go d.routedDispatch(ctx, outbound, destination)
|
go d.routedDispatch(ctx, outbound, destination)
|
||||||
} else {
|
case destination.Network != net.Network_TCP:
|
||||||
|
// Only metadata sniff will be used for non tcp connection
|
||||||
|
result, err := sniffer(ctx, nil, true)
|
||||||
|
if err == nil {
|
||||||
|
content.Protocol = result.Protocol()
|
||||||
|
if shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||||
|
domain := result.Domain()
|
||||||
|
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
destination.Address = net.ParseAddress(domain)
|
||||||
|
ob.Target = destination
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go d.routedDispatch(ctx, outbound, destination)
|
||||||
|
default:
|
||||||
go func() {
|
go func() {
|
||||||
cReader := &cachedReader{
|
cReader := &cachedReader{
|
||||||
reader: outbound.Reader.(*pipe.Reader),
|
reader: outbound.Reader.(*pipe.Reader),
|
||||||
}
|
}
|
||||||
outbound.Reader = cReader
|
outbound.Reader = cReader
|
||||||
result, err := sniffer(ctx, cReader)
|
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
content.Protocol = result.Protocol()
|
content.Protocol = result.Protocol()
|
||||||
}
|
}
|
||||||
if err == nil && shouldOverride(result, sniffingRequest) {
|
if err == nil && shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||||
domain := result.Domain()
|
domain := result.Domain()
|
||||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
||||||
destination.Address = net.ParseAddress(domain)
|
destination.Address = net.ParseAddress(domain)
|
||||||
@@ -234,48 +260,59 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
|||||||
return inbound, nil
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sniffer(ctx context.Context, cReader *cachedReader) (SniffResult, error) {
|
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (SniffResult, error) {
|
||||||
payload := buf.New()
|
payload := buf.New()
|
||||||
defer payload.Release()
|
defer payload.Release()
|
||||||
|
|
||||||
sniffer := NewSniffer()
|
sniffer := NewSniffer(ctx)
|
||||||
totalAttempt := 0
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
default:
|
|
||||||
totalAttempt++
|
|
||||||
if totalAttempt > 2 {
|
|
||||||
return nil, errSniffingTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
cReader.Cache(payload)
|
metaresult, metadataErr := sniffer.SniffMetadata(ctx)
|
||||||
if !payload.IsEmpty() {
|
|
||||||
result, err := sniffer.Sniff(payload.Bytes())
|
if metadataOnly {
|
||||||
if err != common.ErrNoClue {
|
return metaresult, metadataErr
|
||||||
return result, err
|
}
|
||||||
|
|
||||||
|
contentResult, contentErr := func() (SniffResult, error) {
|
||||||
|
totalAttempt := 0
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
totalAttempt++
|
||||||
|
if totalAttempt > 2 {
|
||||||
|
return nil, errSniffingTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
cReader.Cache(payload)
|
||||||
|
if !payload.IsEmpty() {
|
||||||
|
result, err := sniffer.Sniff(ctx, payload.Bytes())
|
||||||
|
if err != common.ErrNoClue {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if payload.IsFull() {
|
||||||
|
return nil, errUnknownContent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if payload.IsFull() {
|
|
||||||
return nil, errUnknownContent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
if contentErr != nil && metadataErr == nil {
|
||||||
|
return metaresult, nil
|
||||||
}
|
}
|
||||||
|
if contentErr == nil && metadataErr == nil {
|
||||||
|
return CompositeResult(metaresult, contentResult), nil
|
||||||
|
}
|
||||||
|
return contentResult, contentErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
||||||
var handler outbound.Handler
|
var handler outbound.Handler
|
||||||
|
|
||||||
skipRoutePick := false
|
|
||||||
if content := session.ContentFromContext(ctx); content != nil {
|
|
||||||
skipRoutePick = content.SkipRoutePick
|
|
||||||
}
|
|
||||||
|
|
||||||
routingLink := routing_session.AsRoutingContext(ctx)
|
routingLink := routing_session.AsRoutingContext(ctx)
|
||||||
inTag := routingLink.GetInboundTag()
|
inTag := routingLink.GetInboundTag()
|
||||||
isPickRoute := false
|
isPickRoute := false
|
||||||
if d.router != nil && !skipRoutePick {
|
if d.router != nil {
|
||||||
if route, err := d.router.PickRoute(routingLink); err == nil {
|
if route, err := d.router.PickRoute(routingLink); err == nil {
|
||||||
outTag := route.GetOutboundTag()
|
outTag := route.GetOutboundTag()
|
||||||
isPickRoute = true
|
isPickRoute = true
|
||||||
|
49
app/dispatcher/fakednssniffer.go
Normal file
49
app/dispatcher/fakednssniffer.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newFakeDNSSniffer Create a Fake DNS metadata sniffer
|
||||||
|
func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) {
|
||||||
|
var fakeDNSEngine dns.FakeDNSEngine
|
||||||
|
err := core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
|
||||||
|
fakeDNSEngine = fdns
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return protocolSnifferWithMetadata{}, err
|
||||||
|
}
|
||||||
|
if fakeDNSEngine == nil {
|
||||||
|
errNotInit := newError("FakeDNSEngine is not initialized, but such a sniffer is used").AtError()
|
||||||
|
return protocolSnifferWithMetadata{}, errNotInit
|
||||||
|
}
|
||||||
|
return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
|
||||||
|
Target := session.OutboundFromContext(ctx).Target
|
||||||
|
if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP {
|
||||||
|
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)
|
||||||
|
if domainFromFakeDNS != "" {
|
||||||
|
newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", Target.Address.String()).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, common.ErrNoClue
|
||||||
|
}, metadataSniffer: true}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type fakeDNSSniffResult struct {
|
||||||
|
domainName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fakeDNSSniffResult) Protocol() string {
|
||||||
|
return "fakedns"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fakeDNSSniffResult) Domain() string {
|
||||||
|
return f.domainName
|
||||||
|
}
|
@@ -1,6 +1,8 @@
|
|||||||
package dispatcher
|
package dispatcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/protocol/bittorrent"
|
"github.com/xtls/xray-core/common/protocol/bittorrent"
|
||||||
"github.com/xtls/xray-core/common/protocol/http"
|
"github.com/xtls/xray-core/common/protocol/http"
|
||||||
@@ -12,30 +14,46 @@ type SniffResult interface {
|
|||||||
Domain() string
|
Domain() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type protocolSniffer func([]byte) (SniffResult, error)
|
type protocolSniffer func(context.Context, []byte) (SniffResult, error)
|
||||||
|
|
||||||
type Sniffer struct {
|
type protocolSnifferWithMetadata struct {
|
||||||
sniffer []protocolSniffer
|
protocolSniffer protocolSniffer
|
||||||
|
// A Metadata sniffer will be invoked on connection establishment only, with nil body,
|
||||||
|
// for both TCP and UDP connections
|
||||||
|
// It will not be shown as a traffic type for routing unless there is no other successful sniffing.
|
||||||
|
metadataSniffer bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSniffer() *Sniffer {
|
type Sniffer struct {
|
||||||
return &Sniffer{
|
sniffer []protocolSnifferWithMetadata
|
||||||
sniffer: []protocolSniffer{
|
}
|
||||||
func(b []byte) (SniffResult, error) { return http.SniffHTTP(b) },
|
|
||||||
func(b []byte) (SniffResult, error) { return tls.SniffTLS(b) },
|
func NewSniffer(ctx context.Context) *Sniffer {
|
||||||
func(b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) },
|
ret := &Sniffer{
|
||||||
|
sniffer: []protocolSnifferWithMetadata{
|
||||||
|
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false},
|
||||||
|
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false},
|
||||||
|
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
|
||||||
|
ret.sniffer = append(ret.sniffer, sniffer)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
var errUnknownContent = newError("unknown content")
|
var errUnknownContent = newError("unknown content")
|
||||||
|
|
||||||
func (s *Sniffer) Sniff(payload []byte) (SniffResult, error) {
|
func (s *Sniffer) Sniff(c context.Context, payload []byte) (SniffResult, error) {
|
||||||
var pendingSniffer []protocolSniffer
|
var pendingSniffer []protocolSnifferWithMetadata
|
||||||
for _, s := range s.sniffer {
|
for _, si := range s.sniffer {
|
||||||
result, err := s(payload)
|
s := si.protocolSniffer
|
||||||
|
if si.metadataSniffer {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result, err := s(c, payload)
|
||||||
if err == common.ErrNoClue {
|
if err == common.ErrNoClue {
|
||||||
pendingSniffer = append(pendingSniffer, s)
|
pendingSniffer = append(pendingSniffer, si)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,3 +69,55 @@ func (s *Sniffer) Sniff(payload []byte) (SniffResult, error) {
|
|||||||
|
|
||||||
return nil, errUnknownContent
|
return nil, errUnknownContent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Sniffer) SniffMetadata(c context.Context) (SniffResult, error) {
|
||||||
|
var pendingSniffer []protocolSnifferWithMetadata
|
||||||
|
for _, si := range s.sniffer {
|
||||||
|
s := si.protocolSniffer
|
||||||
|
if !si.metadataSniffer {
|
||||||
|
pendingSniffer = append(pendingSniffer, si)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result, err := s(c, nil)
|
||||||
|
if err == common.ErrNoClue {
|
||||||
|
pendingSniffer = append(pendingSniffer, si)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && result != nil {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pendingSniffer) > 0 {
|
||||||
|
s.sniffer = pendingSniffer
|
||||||
|
return nil, common.ErrNoClue
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errUnknownContent
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompositeResult(domainResult SniffResult, protocolResult SniffResult) SniffResult {
|
||||||
|
return &compositeResult{domainResult: domainResult, protocolResult: protocolResult}
|
||||||
|
}
|
||||||
|
|
||||||
|
type compositeResult struct {
|
||||||
|
domainResult SniffResult
|
||||||
|
protocolResult SniffResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c compositeResult) Protocol() string {
|
||||||
|
return c.protocolResult.Protocol()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c compositeResult) Domain() string {
|
||||||
|
return c.domainResult.Domain()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c compositeResult) ProtocolForDomainResult() string {
|
||||||
|
return c.domainResult.Protocol()
|
||||||
|
}
|
||||||
|
|
||||||
|
type SnifferResultComposite interface {
|
||||||
|
ProtocolForDomainResult() string
|
||||||
|
}
|
||||||
|
63
app/dns/config.go
Normal file
63
app/dns/config.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/strmatcher"
|
||||||
|
"github.com/xtls/xray-core/common/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var typeMap = map[DomainMatchingType]strmatcher.Type{
|
||||||
|
DomainMatchingType_Full: strmatcher.Full,
|
||||||
|
DomainMatchingType_Subdomain: strmatcher.Domain,
|
||||||
|
DomainMatchingType_Keyword: strmatcher.Substr,
|
||||||
|
DomainMatchingType_Regex: strmatcher.Regex,
|
||||||
|
}
|
||||||
|
|
||||||
|
// References:
|
||||||
|
// https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
|
||||||
|
// https://unix.stackexchange.com/questions/92441/whats-the-difference-between-local-home-and-lan
|
||||||
|
var localTLDsAndDotlessDomains = []*NameServer_PriorityDomain{
|
||||||
|
{Type: DomainMatchingType_Regex, Domain: "^[^.]+$"}, // This will only match domains without any dot
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "local"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "localdomain"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "localhost"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "lan"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "home.arpa"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "example"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "invalid"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "test"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var localTLDsAndDotlessDomainsRule = &NameServer_OriginalRule{
|
||||||
|
Rule: "geosite:private",
|
||||||
|
Size: uint32(len(localTLDsAndDotlessDomains)),
|
||||||
|
}
|
||||||
|
|
||||||
|
func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, error) {
|
||||||
|
strMType, f := typeMap[t]
|
||||||
|
if !f {
|
||||||
|
return nil, newError("unknown mapping type", t).AtWarning()
|
||||||
|
}
|
||||||
|
matcher, err := strMType.New(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to create str matcher").Base(err)
|
||||||
|
}
|
||||||
|
return matcher, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toNetIP(addrs []net.Address) ([]net.IP, error) {
|
||||||
|
ips := make([]net.IP, 0, len(addrs))
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if addr.Family().IsIP() {
|
||||||
|
ips = append(ips, addr.IP())
|
||||||
|
} else {
|
||||||
|
return nil, newError("Failed to convert address", addr, "to Net IP.").AtWarning()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRandomTag() string {
|
||||||
|
id := uuid.New()
|
||||||
|
return "xray.system." + id.String()
|
||||||
|
}
|
@@ -1,13 +1,12 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.25.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v3.14.0
|
// protoc v3.15.8
|
||||||
// source: app/dns/config.proto
|
// source: app/dns/config.proto
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
router "github.com/xtls/xray-core/app/router"
|
router "github.com/xtls/xray-core/app/router"
|
||||||
net "github.com/xtls/xray-core/common/net"
|
net "github.com/xtls/xray-core/common/net"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
@@ -23,10 +22,6 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
|
||||||
// of the legacy proto package is being used.
|
|
||||||
const _ = proto.ProtoPackageIsVersion4
|
|
||||||
|
|
||||||
type DomainMatchingType int32
|
type DomainMatchingType int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -79,12 +74,112 @@ func (DomainMatchingType) EnumDescriptor() ([]byte, []int) {
|
|||||||
return file_app_dns_config_proto_rawDescGZIP(), []int{0}
|
return file_app_dns_config_proto_rawDescGZIP(), []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueryStrategy int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
QueryStrategy_USE_IP QueryStrategy = 0
|
||||||
|
QueryStrategy_USE_IP4 QueryStrategy = 1
|
||||||
|
QueryStrategy_USE_IP6 QueryStrategy = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for QueryStrategy.
|
||||||
|
var (
|
||||||
|
QueryStrategy_name = map[int32]string{
|
||||||
|
0: "USE_IP",
|
||||||
|
1: "USE_IP4",
|
||||||
|
2: "USE_IP6",
|
||||||
|
}
|
||||||
|
QueryStrategy_value = map[string]int32{
|
||||||
|
"USE_IP": 0,
|
||||||
|
"USE_IP4": 1,
|
||||||
|
"USE_IP6": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x QueryStrategy) Enum() *QueryStrategy {
|
||||||
|
p := new(QueryStrategy)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x QueryStrategy) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (QueryStrategy) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_app_dns_config_proto_enumTypes[1].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (QueryStrategy) Type() protoreflect.EnumType {
|
||||||
|
return &file_app_dns_config_proto_enumTypes[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x QueryStrategy) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use QueryStrategy.Descriptor instead.
|
||||||
|
func (QueryStrategy) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_app_dns_config_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CacheStrategy int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
CacheStrategy_Cache_ALL CacheStrategy = 0
|
||||||
|
CacheStrategy_Cache_NOERROR CacheStrategy = 1
|
||||||
|
CacheStrategy_Cache_DISABLE CacheStrategy = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for CacheStrategy.
|
||||||
|
var (
|
||||||
|
CacheStrategy_name = map[int32]string{
|
||||||
|
0: "Cache_ALL",
|
||||||
|
1: "Cache_NOERROR",
|
||||||
|
2: "Cache_DISABLE",
|
||||||
|
}
|
||||||
|
CacheStrategy_value = map[string]int32{
|
||||||
|
"Cache_ALL": 0,
|
||||||
|
"Cache_NOERROR": 1,
|
||||||
|
"Cache_DISABLE": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x CacheStrategy) Enum() *CacheStrategy {
|
||||||
|
p := new(CacheStrategy)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x CacheStrategy) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (CacheStrategy) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_app_dns_config_proto_enumTypes[2].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (CacheStrategy) Type() protoreflect.EnumType {
|
||||||
|
return &file_app_dns_config_proto_enumTypes[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x CacheStrategy) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CacheStrategy.Descriptor instead.
|
||||||
|
func (CacheStrategy) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_app_dns_config_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
type NameServer struct {
|
type NameServer struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||||
|
ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
|
||||||
|
SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"`
|
||||||
PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
|
PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
|
||||||
Geoip []*router.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"`
|
Geoip []*router.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"`
|
||||||
OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"`
|
OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"`
|
||||||
@@ -129,6 +224,20 @@ func (x *NameServer) GetAddress() *net.Endpoint {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *NameServer) GetClientIp() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.ClientIp
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *NameServer) GetSkipFallback() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.SkipFallback
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain {
|
func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.PrioritizedDomain
|
return x.PrioritizedDomain
|
||||||
@@ -174,6 +283,10 @@ type Config struct {
|
|||||||
StaticHosts []*Config_HostMapping `protobuf:"bytes,4,rep,name=static_hosts,json=staticHosts,proto3" json:"static_hosts,omitempty"`
|
StaticHosts []*Config_HostMapping `protobuf:"bytes,4,rep,name=static_hosts,json=staticHosts,proto3" json:"static_hosts,omitempty"`
|
||||||
// Tag is the inbound tag of DNS client.
|
// Tag is the inbound tag of DNS client.
|
||||||
Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"`
|
Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||||
|
// DisableCache disables DNS cache
|
||||||
|
CacheStrategy CacheStrategy `protobuf:"varint,8,opt,name=cache_strategy,json=cacheStrategy,proto3,enum=xray.app.dns.CacheStrategy" json:"cache_strategy,omitempty"`
|
||||||
|
QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
|
||||||
|
DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -252,6 +365,27 @@ func (x *Config) GetTag() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetCacheStrategy() CacheStrategy {
|
||||||
|
if x != nil {
|
||||||
|
return x.CacheStrategy
|
||||||
|
}
|
||||||
|
return CacheStrategy_Cache_ALL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetQueryStrategy() QueryStrategy {
|
||||||
|
if x != nil {
|
||||||
|
return x.QueryStrategy
|
||||||
|
}
|
||||||
|
return QueryStrategy_USE_IP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetDisableFallback() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.DisableFallback
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type NameServer_PriorityDomain struct {
|
type NameServer_PriorityDomain struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -371,8 +505,7 @@ type Config_HostMapping struct {
|
|||||||
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
|
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
|
||||||
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
|
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
|
||||||
// ProxiedDomain indicates the mapped domain has the same IP address on this
|
// ProxiedDomain indicates the mapped domain has the same IP address on this
|
||||||
// domain. Xray will use this domain for IP queries. This field is only
|
// domain. Xray will use this domain for IP queries.
|
||||||
// effective if ip is empty.
|
|
||||||
ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"`
|
ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,77 +579,101 @@ var file_app_dns_config_proto_rawDesc = []byte{
|
|||||||
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69,
|
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69,
|
||||||
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70,
|
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70,
|
||||||
0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xad, 0x03, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xee, 0x03, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
|
||||||
0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
|
0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||||
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72, 0x69,
|
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69,
|
||||||
0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
|
0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c,
|
||||||
0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61,
|
||||||
0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
|
0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b,
|
||||||
0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x11,
|
0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72,
|
||||||
0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
0x6e, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||||
0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
|
||||||
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x12,
|
|
||||||
0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65,
|
|
||||||
0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
|
||||||
0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
|
||||||
0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d,
|
|
||||||
0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x1a, 0x5e, 0x0a,
|
|
||||||
0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12,
|
|
||||||
0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e,
|
|
||||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d,
|
|
||||||
0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52,
|
|
||||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
|
|
||||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a,
|
|
||||||
0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a,
|
|
||||||
0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c,
|
|
||||||
0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
|
||||||
0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x9f, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
|
||||||
0x12, 0x3f, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18,
|
|
||||||
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
|
||||||
0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
|
||||||
0x73, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
|
||||||
0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
|
||||||
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||||
0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x05,
|
0x2e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52,
|
||||||
0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72,
|
0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61,
|
||||||
|
0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28,
|
||||||
|
0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75,
|
||||||
|
0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70,
|
||||||
|
0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c,
|
||||||
|
0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
|
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76,
|
||||||
|
0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52,
|
||||||
|
0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x1a, 0x5e,
|
||||||
|
0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
|
0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20,
|
||||||
|
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f,
|
||||||
|
0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65,
|
||||||
|
0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36,
|
||||||
|
0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12,
|
||||||
|
0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75,
|
||||||
|
0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
|
||||||
|
0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xd7, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
|
0x67, 0x12, 0x3f, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73,
|
||||||
|
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
|
||||||
|
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e,
|
||||||
|
0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
||||||
|
0x72, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65,
|
||||||
|
0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
||||||
|
0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
||||||
|
0x72, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x39, 0x0a,
|
||||||
|
0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78,
|
||||||
|
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
|
0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x02, 0x18,
|
||||||
|
0x01, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65,
|
||||||
|
0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69,
|
||||||
|
0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f,
|
||||||
|
0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72,
|
||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x02, 0x18, 0x01,
|
0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73,
|
||||||
0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
|
||||||
0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65,
|
0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x42, 0x0a, 0x0e,
|
||||||
0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x68,
|
0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x08,
|
||||||
0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
|
0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
||||||
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
0x64, 0x6e, 0x73, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67,
|
||||||
0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73, 0x74,
|
0x79, 0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||||
0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67,
|
0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65,
|
||||||
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x1a, 0x55, 0x0a, 0x0a, 0x48,
|
0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76,
|
0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61,
|
||||||
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61,
|
0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46,
|
||||||
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f,
|
0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64,
|
||||||
0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
|
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x1a, 0x55,
|
||||||
0x38, 0x01, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69,
|
0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
|
||||||
0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
|
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31,
|
||||||
0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e,
|
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e,
|
||||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79,
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e,
|
||||||
0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61,
|
0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||||
0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61,
|
||||||
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70,
|
0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20,
|
||||||
0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61,
|
0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64,
|
||||||
0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65,
|
0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e,
|
||||||
0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64,
|
||||||
0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a,
|
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d,
|
||||||
0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f,
|
0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52,
|
||||||
0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72,
|
0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64,
|
||||||
0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x42, 0x46,
|
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f,
|
||||||
0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64,
|
0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08,
|
||||||
0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69,
|
||||||
0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f,
|
0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00,
|
||||||
0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41,
|
0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12,
|
||||||
0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05,
|
||||||
|
0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x35, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79,
|
||||||
|
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f,
|
||||||
|
0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10,
|
||||||
|
0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, 0x2a, 0x44,
|
||||||
|
0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12,
|
||||||
|
0x0d, 0x0a, 0x09, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x11,
|
||||||
|
0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x4e, 0x4f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10,
|
||||||
|
0x01, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42,
|
||||||
|
0x4c, 0x45, 0x10, 0x02, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||||
|
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68,
|
||||||
|
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
||||||
|
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c,
|
||||||
|
0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -531,37 +688,41 @@ func file_app_dns_config_proto_rawDescGZIP() []byte {
|
|||||||
return file_app_dns_config_proto_rawDescData
|
return file_app_dns_config_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||||
var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||||
var file_app_dns_config_proto_goTypes = []interface{}{
|
var file_app_dns_config_proto_goTypes = []interface{}{
|
||||||
(DomainMatchingType)(0), // 0: xray.app.dns.DomainMatchingType
|
(DomainMatchingType)(0), // 0: xray.app.dns.DomainMatchingType
|
||||||
(*NameServer)(nil), // 1: xray.app.dns.NameServer
|
(QueryStrategy)(0), // 1: xray.app.dns.QueryStrategy
|
||||||
(*Config)(nil), // 2: xray.app.dns.Config
|
(CacheStrategy)(0), // 2: xray.app.dns.CacheStrategy
|
||||||
(*NameServer_PriorityDomain)(nil), // 3: xray.app.dns.NameServer.PriorityDomain
|
(*NameServer)(nil), // 3: xray.app.dns.NameServer
|
||||||
(*NameServer_OriginalRule)(nil), // 4: xray.app.dns.NameServer.OriginalRule
|
(*Config)(nil), // 4: xray.app.dns.Config
|
||||||
nil, // 5: xray.app.dns.Config.HostsEntry
|
(*NameServer_PriorityDomain)(nil), // 5: xray.app.dns.NameServer.PriorityDomain
|
||||||
(*Config_HostMapping)(nil), // 6: xray.app.dns.Config.HostMapping
|
(*NameServer_OriginalRule)(nil), // 6: xray.app.dns.NameServer.OriginalRule
|
||||||
(*net.Endpoint)(nil), // 7: xray.common.net.Endpoint
|
nil, // 7: xray.app.dns.Config.HostsEntry
|
||||||
(*router.GeoIP)(nil), // 8: xray.app.router.GeoIP
|
(*Config_HostMapping)(nil), // 8: xray.app.dns.Config.HostMapping
|
||||||
(*net.IPOrDomain)(nil), // 9: xray.common.net.IPOrDomain
|
(*net.Endpoint)(nil), // 9: xray.common.net.Endpoint
|
||||||
|
(*router.GeoIP)(nil), // 10: xray.app.router.GeoIP
|
||||||
|
(*net.IPOrDomain)(nil), // 11: xray.common.net.IPOrDomain
|
||||||
}
|
}
|
||||||
var file_app_dns_config_proto_depIdxs = []int32{
|
var file_app_dns_config_proto_depIdxs = []int32{
|
||||||
7, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
|
9, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
|
||||||
3, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
|
5, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
|
||||||
8, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP
|
10, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP
|
||||||
4, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
|
6, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
|
||||||
7, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint
|
9, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint
|
||||||
1, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
|
3, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
|
||||||
5, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry
|
7, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry
|
||||||
6, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
|
8, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
|
||||||
0, // 8: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
|
2, // 8: xray.app.dns.Config.cache_strategy:type_name -> xray.app.dns.CacheStrategy
|
||||||
9, // 9: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain
|
1, // 9: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
|
||||||
0, // 10: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
|
0, // 10: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
|
||||||
11, // [11:11] is the sub-list for method output_type
|
11, // 11: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain
|
||||||
11, // [11:11] is the sub-list for method input_type
|
0, // 12: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
|
||||||
11, // [11:11] is the sub-list for extension type_name
|
13, // [13:13] is the sub-list for method output_type
|
||||||
11, // [11:11] is the sub-list for extension extendee
|
13, // [13:13] is the sub-list for method input_type
|
||||||
0, // [0:11] is the sub-list for field type_name
|
13, // [13:13] is the sub-list for extension type_name
|
||||||
|
13, // [13:13] is the sub-list for extension extendee
|
||||||
|
0, // [0:13] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_app_dns_config_proto_init() }
|
func init() { file_app_dns_config_proto_init() }
|
||||||
@@ -636,7 +797,7 @@ func file_app_dns_config_proto_init() {
|
|||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_app_dns_config_proto_rawDesc,
|
RawDescriptor: file_app_dns_config_proto_rawDesc,
|
||||||
NumEnums: 1,
|
NumEnums: 3,
|
||||||
NumMessages: 6,
|
NumMessages: 6,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
|
@@ -12,6 +12,8 @@ import "app/router/config.proto";
|
|||||||
|
|
||||||
message NameServer {
|
message NameServer {
|
||||||
xray.common.net.Endpoint address = 1;
|
xray.common.net.Endpoint address = 1;
|
||||||
|
bytes client_ip = 5;
|
||||||
|
bool skipFallback = 6;
|
||||||
|
|
||||||
message PriorityDomain {
|
message PriorityDomain {
|
||||||
DomainMatchingType type = 1;
|
DomainMatchingType type = 1;
|
||||||
@@ -35,6 +37,18 @@ enum DomainMatchingType {
|
|||||||
Regex = 3;
|
Regex = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum QueryStrategy {
|
||||||
|
USE_IP = 0;
|
||||||
|
USE_IP4 = 1;
|
||||||
|
USE_IP6 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CacheStrategy {
|
||||||
|
Cache_ALL = 0;
|
||||||
|
Cache_NOERROR = 1;
|
||||||
|
Cache_DISABLE = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
// Nameservers used by this DNS. Only traditional UDP servers are support at
|
// Nameservers used by this DNS. Only traditional UDP servers are support at
|
||||||
// the moment. A special value 'localhost' as a domain address can be set to
|
// the moment. A special value 'localhost' as a domain address can be set to
|
||||||
@@ -59,8 +73,7 @@ message Config {
|
|||||||
repeated bytes ip = 3;
|
repeated bytes ip = 3;
|
||||||
|
|
||||||
// ProxiedDomain indicates the mapped domain has the same IP address on this
|
// ProxiedDomain indicates the mapped domain has the same IP address on this
|
||||||
// domain. Xray will use this domain for IP queries. This field is only
|
// domain. Xray will use this domain for IP queries.
|
||||||
// effective if ip is empty.
|
|
||||||
string proxied_domain = 4;
|
string proxied_domain = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,4 +81,13 @@ message Config {
|
|||||||
|
|
||||||
// Tag is the inbound tag of DNS client.
|
// Tag is the inbound tag of DNS client.
|
||||||
string tag = 6;
|
string tag = 6;
|
||||||
|
|
||||||
|
reserved 7;
|
||||||
|
|
||||||
|
// DisableCache disables DNS cache
|
||||||
|
CacheStrategy cache_strategy = 8;
|
||||||
|
|
||||||
|
QueryStrategy query_strategy = 9;
|
||||||
|
|
||||||
|
bool disableFallback = 10;
|
||||||
}
|
}
|
||||||
|
309
app/dns/dns.go
309
app/dns/dns.go
@@ -2,3 +2,312 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/app/router"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/strmatcher"
|
||||||
|
"github.com/xtls/xray-core/features"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNS is a DNS rely server.
|
||||||
|
type DNS struct {
|
||||||
|
sync.Mutex
|
||||||
|
tag string
|
||||||
|
cacheStrategy CacheStrategy
|
||||||
|
disableFallback bool
|
||||||
|
ipOption *dns.IPOption
|
||||||
|
hosts *StaticHosts
|
||||||
|
clients []*Client
|
||||||
|
ctx context.Context
|
||||||
|
domainMatcher strmatcher.IndexMatcher
|
||||||
|
matcherInfos []DomainMatcherInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
|
||||||
|
type DomainMatcherInfo struct {
|
||||||
|
clientIdx uint16
|
||||||
|
domainRuleIdx uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new DNS server with given configuration.
|
||||||
|
func New(ctx context.Context, config *Config) (*DNS, error) {
|
||||||
|
var tag string
|
||||||
|
if len(config.Tag) > 0 {
|
||||||
|
tag = config.Tag
|
||||||
|
} else {
|
||||||
|
tag = generateRandomTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
var clientIP net.IP
|
||||||
|
switch len(config.ClientIp) {
|
||||||
|
case 0, net.IPv4len, net.IPv6len:
|
||||||
|
clientIP = net.IP(config.ClientIp)
|
||||||
|
default:
|
||||||
|
return nil, newError("unexpected client IP length ", len(config.ClientIp))
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipOption *dns.IPOption
|
||||||
|
switch config.QueryStrategy {
|
||||||
|
case QueryStrategy_USE_IP:
|
||||||
|
ipOption = &dns.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
FakeEnable: false,
|
||||||
|
}
|
||||||
|
case QueryStrategy_USE_IP4:
|
||||||
|
ipOption = &dns.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: false,
|
||||||
|
FakeEnable: false,
|
||||||
|
}
|
||||||
|
case QueryStrategy_USE_IP6:
|
||||||
|
ipOption = &dns.IPOption{
|
||||||
|
IPv4Enable: false,
|
||||||
|
IPv6Enable: true,
|
||||||
|
FakeEnable: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts, err := NewStaticHosts(config.StaticHosts, config.Hosts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to create hosts").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clients := []*Client{}
|
||||||
|
domainRuleCount := 0
|
||||||
|
for _, ns := range config.NameServer {
|
||||||
|
domainRuleCount += len(ns.PrioritizedDomain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1
|
||||||
|
matcherInfos := make([]DomainMatcherInfo, domainRuleCount+1)
|
||||||
|
domainMatcher := &strmatcher.MatcherGroup{}
|
||||||
|
geoipContainer := router.GeoIPMatcherContainer{}
|
||||||
|
|
||||||
|
for _, endpoint := range config.NameServers {
|
||||||
|
features.PrintDeprecatedFeatureWarning("simple DNS server")
|
||||||
|
client, err := NewSimpleClient(ctx, endpoint, clientIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to create client").Base(err)
|
||||||
|
}
|
||||||
|
clients = append(clients, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ns := range config.NameServer {
|
||||||
|
clientIdx := len(clients)
|
||||||
|
updateDomain := func(domainRule strmatcher.Matcher, originalRuleIdx int, matcherInfos []DomainMatcherInfo) error {
|
||||||
|
midx := domainMatcher.Add(domainRule)
|
||||||
|
matcherInfos[midx] = DomainMatcherInfo{
|
||||||
|
clientIdx: uint16(clientIdx),
|
||||||
|
domainRuleIdx: uint16(originalRuleIdx),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
myClientIP := clientIP
|
||||||
|
switch len(ns.ClientIp) {
|
||||||
|
case net.IPv4len, net.IPv6len:
|
||||||
|
myClientIP = net.IP(ns.ClientIp)
|
||||||
|
}
|
||||||
|
client, err := NewClient(ctx, ns, myClientIP, geoipContainer, &matcherInfos, updateDomain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to create client").Base(err)
|
||||||
|
}
|
||||||
|
clients = append(clients, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no DNS client in config, add a `localhost` DNS client
|
||||||
|
if len(clients) == 0 {
|
||||||
|
clients = append(clients, NewLocalDNSClient())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DNS{
|
||||||
|
tag: tag,
|
||||||
|
hosts: hosts,
|
||||||
|
ipOption: ipOption,
|
||||||
|
clients: clients,
|
||||||
|
ctx: ctx,
|
||||||
|
domainMatcher: domainMatcher,
|
||||||
|
matcherInfos: matcherInfos,
|
||||||
|
cacheStrategy: config.CacheStrategy,
|
||||||
|
disableFallback: config.DisableFallback,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements common.HasType.
|
||||||
|
func (*DNS) Type() interface{} {
|
||||||
|
return dns.ClientType()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start implements common.Runnable.
|
||||||
|
func (s *DNS) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements common.Closable.
|
||||||
|
func (s *DNS) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOwnLink implements proxy.dns.ownLinkVerifier
|
||||||
|
func (s *DNS) IsOwnLink(ctx context.Context) bool {
|
||||||
|
inbound := session.InboundFromContext(ctx)
|
||||||
|
return inbound != nil && inbound.Tag == s.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupIP implements dns.Client.
|
||||||
|
func (s *DNS) LookupIP(domain string) ([]net.IP, error) {
|
||||||
|
return s.lookupIPInternal(domain, s.ipOption.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupOptions implements dns.Client.
|
||||||
|
func (s *DNS) LookupOptions(domain string, opts ...dns.Option) ([]net.IP, error) {
|
||||||
|
opt := s.ipOption.Copy()
|
||||||
|
for _, o := range opts {
|
||||||
|
if o != nil {
|
||||||
|
o(opt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.lookupIPInternal(domain, opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupIPv4 implements dns.IPv4Lookup.
|
||||||
|
func (s *DNS) LookupIPv4(domain string) ([]net.IP, error) {
|
||||||
|
return s.lookupIPInternal(domain, &dns.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupIPv6 implements dns.IPv6Lookup.
|
||||||
|
func (s *DNS) LookupIPv6(domain string) ([]net.IP, error) {
|
||||||
|
return s.lookupIPInternal(domain, &dns.IPOption{
|
||||||
|
IPv6Enable: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DNS) lookupIPInternal(domain string, option *dns.IPOption) ([]net.IP, error) {
|
||||||
|
if domain == "" {
|
||||||
|
return nil, newError("empty domain name")
|
||||||
|
}
|
||||||
|
if isQuery(option) {
|
||||||
|
return nil, newError("empty option: Impossible.").AtWarning()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize the FQDN form query
|
||||||
|
if strings.HasSuffix(domain, ".") {
|
||||||
|
domain = domain[:len(domain)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static host lookup
|
||||||
|
switch addrs := s.hosts.Lookup(domain, option); {
|
||||||
|
case addrs == nil: // Domain not recorded in static host
|
||||||
|
break
|
||||||
|
case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled)
|
||||||
|
return nil, dns.ErrEmptyResponse
|
||||||
|
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Domain replacement
|
||||||
|
newError("domain replaced: ", domain, " -> ", addrs[0].Domain()).WriteToLog()
|
||||||
|
domain = addrs[0].Domain()
|
||||||
|
default:
|
||||||
|
// Successfully found ip records in static host.
|
||||||
|
// Skip hosts mapping result in FakeDNS query.
|
||||||
|
if isIPQuery(option) {
|
||||||
|
newError("returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs).WriteToLog()
|
||||||
|
return toNetIP(addrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name servers lookup
|
||||||
|
errs := []error{}
|
||||||
|
ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag})
|
||||||
|
for _, client := range s.sortClients(domain, option) {
|
||||||
|
ips, err := client.QueryIP(ctx, domain, *option, s.cacheStrategy)
|
||||||
|
if len(ips) > 0 {
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog()
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, newError("returning nil for domain ", domain).Base(errors.Combine(errs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DNS) sortClients(domain string, option *dns.IPOption) []*Client {
|
||||||
|
clients := make([]*Client, 0, len(s.clients))
|
||||||
|
clientUsed := make([]bool, len(s.clients))
|
||||||
|
clientNames := make([]string, 0, len(s.clients))
|
||||||
|
domainRules := []string{}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if len(domainRules) > 0 {
|
||||||
|
newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog()
|
||||||
|
}
|
||||||
|
if len(clientNames) > 0 {
|
||||||
|
newError("domain ", domain, " will use DNS in order: ", clientNames).AtDebug().WriteToLog()
|
||||||
|
}
|
||||||
|
if len(clients) == 0 {
|
||||||
|
clients = append(clients, s.clients[0])
|
||||||
|
clientNames = append(clientNames, s.clients[0].Name())
|
||||||
|
newError("domain ", domain, " will use the first DNS: ", clientNames).AtDebug().WriteToLog()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Priority domain matching
|
||||||
|
for _, match := range s.domainMatcher.Match(domain) {
|
||||||
|
info := s.matcherInfos[match]
|
||||||
|
client := s.clients[info.clientIdx]
|
||||||
|
domainRule := client.domains[info.domainRuleIdx]
|
||||||
|
if !canQueryOnClient(option, client) {
|
||||||
|
newError("skipping the client " + client.Name()).AtDebug().WriteToLog()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx))
|
||||||
|
if clientUsed[info.clientIdx] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
clientUsed[info.clientIdx] = true
|
||||||
|
clients = append(clients, client)
|
||||||
|
clientNames = append(clientNames, client.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.disableFallback {
|
||||||
|
// Default round-robin query
|
||||||
|
for idx, client := range s.clients {
|
||||||
|
if clientUsed[idx] || client.skipFallback {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !canQueryOnClient(option, client) {
|
||||||
|
newError("skipping the client " + client.Name()).AtDebug().WriteToLog()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
clientUsed[idx] = true
|
||||||
|
clients = append(clients, client)
|
||||||
|
clientNames = append(clientNames, client.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clients
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
|
return New(ctx, config.(*Config))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
@@ -101,8 +101,8 @@ func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
|||||||
rr, _ := dns.NewRR("localhost-b. IN A 127.0.0.4")
|
rr, _ := dns.NewRR("localhost-b. IN A 127.0.0.4")
|
||||||
ans.Answer = append(ans.Answer, rr)
|
ans.Answer = append(ans.Answer, rr)
|
||||||
|
|
||||||
case q.Name == "mijia\\ cloud." && q.Qtype == dns.TypeA:
|
case q.Name == "Mijia\\ Cloud." && q.Qtype == dns.TypeA:
|
||||||
rr, _ := dns.NewRR("mijia\\ cloud. IN A 127.0.0.1")
|
rr, _ := dns.NewRR("Mijia\\ Cloud. IN A 127.0.0.1")
|
||||||
ans.Answer = append(ans.Answer, rr)
|
ans.Answer = append(ans.Answer, rr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,7 +391,6 @@ func TestUDPServerIPv6(t *testing.T) {
|
|||||||
|
|
||||||
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
|
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
|
||||||
client6 := client.(feature_dns.IPv6Lookup)
|
client6 := client.(feature_dns.IPv6Lookup)
|
||||||
|
|
||||||
{
|
{
|
||||||
ips, err := client6.LookupIPv6("ipv6.google.com")
|
ips, err := client6.LookupIPv6("ipv6.google.com")
|
||||||
if err != nil {
|
if err != nil {
|
@@ -2,6 +2,7 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
@@ -13,7 +14,7 @@ import (
|
|||||||
|
|
||||||
// Fqdn normalize domain make sure it ends with '.'
|
// Fqdn normalize domain make sure it ends with '.'
|
||||||
func Fqdn(domain string) string {
|
func Fqdn(domain string) string {
|
||||||
if len(domain) > 0 && domain[len(domain)-1] == '.' {
|
if len(domain) > 0 && strings.HasSuffix(domain, ".") {
|
||||||
return domain
|
return domain
|
||||||
}
|
}
|
||||||
return domain + "."
|
return domain + "."
|
||||||
@@ -112,7 +113,7 @@ func genEDNS0Options(clientIP net.IP) *dnsmessage.Resource {
|
|||||||
return opt
|
return opt
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildReqMsgs(domain string, option IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest {
|
func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest {
|
||||||
qA := dnsmessage.Question{
|
qA := dnsmessage.Question{
|
||||||
Name: dnsmessage.MustNewName(domain),
|
Name: dnsmessage.MustNewName(domain),
|
||||||
Type: dnsmessage.TypeA,
|
Type: dnsmessage.TypeA,
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ func Test_buildReqMsgs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
domain string
|
domain string
|
||||||
option IPOption
|
option dns_feature.IPOption
|
||||||
reqOpts *dnsmessage.Resource
|
reqOpts *dnsmessage.Resource
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -100,10 +101,26 @@ func Test_buildReqMsgs(t *testing.T) {
|
|||||||
args args
|
args args
|
||||||
want int
|
want int
|
||||||
}{
|
}{
|
||||||
{"dual stack", args{"test.com", IPOption{true, true}, nil}, 2},
|
{"dual stack", args{"test.com", dns_feature.IPOption{
|
||||||
{"ipv4 only", args{"test.com", IPOption{true, false}, nil}, 1},
|
IPv4Enable: true,
|
||||||
{"ipv6 only", args{"test.com", IPOption{false, true}, nil}, 1},
|
IPv6Enable: true,
|
||||||
{"none/error", args{"test.com", IPOption{false, false}, nil}, 0},
|
FakeEnable: false,
|
||||||
|
}, nil}, 2},
|
||||||
|
{"ipv4 only", args{"test.com", dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: false,
|
||||||
|
FakeEnable: false,
|
||||||
|
}, nil}, 1},
|
||||||
|
{"ipv6 only", args{"test.com", dns_feature.IPOption{
|
||||||
|
IPv4Enable: false,
|
||||||
|
IPv6Enable: true,
|
||||||
|
FakeEnable: false,
|
||||||
|
}, nil}, 1},
|
||||||
|
{"none/error", args{"test.com", dns_feature.IPOption{
|
||||||
|
IPv4Enable: false,
|
||||||
|
IPv6Enable: false,
|
||||||
|
FakeEnable: false,
|
||||||
|
}, nil}, 0},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
9
app/dns/fakedns/errors.generated.go
Normal file
9
app/dns/fakedns/errors.generated.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package fakedns
|
||||||
|
|
||||||
|
import "github.com/xtls/xray-core/common/errors"
|
||||||
|
|
||||||
|
type errPathObjHolder struct{}
|
||||||
|
|
||||||
|
func newError(values ...interface{}) *errors.Error {
|
||||||
|
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||||
|
}
|
134
app/dns/fakedns/fake.go
Normal file
134
app/dns/fakedns/fake.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package fakedns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
gonet "net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/cache"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Holder struct {
|
||||||
|
domainToIP cache.Lru
|
||||||
|
ipRange *gonet.IPNet
|
||||||
|
|
||||||
|
config *FakeDnsPool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Holder) Type() interface{} {
|
||||||
|
return (*dns.FakeDNSEngine)(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fkdns *Holder) Start() error {
|
||||||
|
return fkdns.initializeFromConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fkdns *Holder) Close() error {
|
||||||
|
fkdns.domainToIP = nil
|
||||||
|
fkdns.ipRange = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFakeDNSHolder() (*Holder, error) {
|
||||||
|
var fkdns *Holder
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil {
|
||||||
|
return nil, newError("Unable to create Fake Dns Engine").Base(err).AtError()
|
||||||
|
}
|
||||||
|
err = fkdns.initialize(dns.FakeIPPool, 65535)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return fkdns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) {
|
||||||
|
return &Holder{nil, nil, conf}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fkdns *Holder) initializeFromConfig() error {
|
||||||
|
return fkdns.initialize(fkdns.config.IpPool, int(fkdns.config.LruSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
|
||||||
|
var ipRange *gonet.IPNet
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if _, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil {
|
||||||
|
return newError("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError()
|
||||||
|
}
|
||||||
|
|
||||||
|
ones, bits := ipRange.Mask.Size()
|
||||||
|
rooms := bits - ones
|
||||||
|
if math.Log2(float64(lruSize)) >= float64(rooms) {
|
||||||
|
return newError("LRU size is bigger than subnet size").AtError()
|
||||||
|
}
|
||||||
|
fkdns.domainToIP = cache.NewLru(lruSize)
|
||||||
|
fkdns.ipRange = ipRange
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFakeIPForDomain check and generate a fake IP for a domain name
|
||||||
|
func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address {
|
||||||
|
if v, ok := fkdns.domainToIP.Get(domain); ok {
|
||||||
|
return []net.Address{v.(net.Address)}
|
||||||
|
}
|
||||||
|
var currentTimeMillis = uint64(time.Now().UnixNano() / 1e6)
|
||||||
|
ones, bits := fkdns.ipRange.Mask.Size()
|
||||||
|
rooms := bits - ones
|
||||||
|
if rooms < 64 {
|
||||||
|
currentTimeMillis %= (uint64(1) << rooms)
|
||||||
|
}
|
||||||
|
var bigIntIP = big.NewInt(0).SetBytes(fkdns.ipRange.IP)
|
||||||
|
bigIntIP = bigIntIP.Add(bigIntIP, new(big.Int).SetUint64(currentTimeMillis))
|
||||||
|
var ip net.Address
|
||||||
|
for {
|
||||||
|
ip = net.IPAddress(bigIntIP.Bytes())
|
||||||
|
|
||||||
|
// if we run for a long time, we may go back to beginning and start seeing the IP in use
|
||||||
|
if _, ok := fkdns.domainToIP.PeekKeyFromValue(ip); !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
bigIntIP = bigIntIP.Add(bigIntIP, big.NewInt(1))
|
||||||
|
if !fkdns.ipRange.Contains(bigIntIP.Bytes()) {
|
||||||
|
bigIntIP = big.NewInt(0).SetBytes(fkdns.ipRange.IP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fkdns.domainToIP.Put(domain, ip)
|
||||||
|
return []net.Address{ip}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDomainFromFakeDNS check if an IP is a fake IP and have corresponding domain name
|
||||||
|
func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string {
|
||||||
|
if !ip.Family().IsIP() || !fkdns.ipRange.Contains(ip.IP()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if k, ok := fkdns.domainToIP.GetKeyFromValue(ip); ok {
|
||||||
|
return k.(string)
|
||||||
|
}
|
||||||
|
newError("A fake ip request to ", ip, ", however there is no matching domain name in fake DNS").AtInfo().WriteToLog()
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFakeIPRange return fake IP range from configuration
|
||||||
|
func (fkdns *Holder) GetFakeIPRange() *gonet.IPNet {
|
||||||
|
return fkdns.ipRange
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*FakeDnsPool)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
|
var f *Holder
|
||||||
|
var err error
|
||||||
|
if f, err = NewFakeDNSHolderConfigOnly(config.(*FakeDnsPool)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}))
|
||||||
|
}
|
3
app/dns/fakedns/fakedns.go
Normal file
3
app/dns/fakedns/fakedns.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package fakedns
|
||||||
|
|
||||||
|
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
164
app/dns/fakedns/fakedns.pb.go
Normal file
164
app/dns/fakedns/fakedns.pb.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.25.0
|
||||||
|
// protoc v3.13.0
|
||||||
|
// source: app/dns/fakedns/fakedns.proto
|
||||||
|
|
||||||
|
package fakedns
|
||||||
|
|
||||||
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
|
type FakeDnsPool struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
IpPool string `protobuf:"bytes,1,opt,name=ip_pool,json=ipPool,proto3" json:"ip_pool,omitempty"` //CIDR of IP pool used as fake DNS IP
|
||||||
|
LruSize int64 `protobuf:"varint,2,opt,name=lruSize,proto3" json:"lruSize,omitempty"` //Size of Pool for remembering relationship between domain name and IP address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FakeDnsPool) Reset() {
|
||||||
|
*x = FakeDnsPool{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FakeDnsPool) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FakeDnsPool) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *FakeDnsPool) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use FakeDnsPool.ProtoReflect.Descriptor instead.
|
||||||
|
func (*FakeDnsPool) Descriptor() ([]byte, []int) {
|
||||||
|
return file_app_dns_fakedns_fakedns_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FakeDnsPool) GetIpPool() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.IpPool
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FakeDnsPool) GetLruSize() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.LruSize
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x1d, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
|
||||||
|
0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
||||||
|
0x1a, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
||||||
|
0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x0b, 0x46,
|
||||||
|
0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70,
|
||||||
|
0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x50,
|
||||||
|
0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02,
|
||||||
|
0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x5f, 0x0a,
|
||||||
|
0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
|
||||||
|
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x50,
|
||||||
|
0x01, 0x5a, 0x1e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72,
|
||||||
|
0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
|
||||||
|
0x73, 0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41,
|
||||||
|
0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, 0x46, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x62, 0x06,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_app_dns_fakedns_fakedns_proto_rawDescOnce sync.Once
|
||||||
|
file_app_dns_fakedns_fakedns_proto_rawDescData = file_app_dns_fakedns_fakedns_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte {
|
||||||
|
file_app_dns_fakedns_fakedns_proto_rawDescOnce.Do(func() {
|
||||||
|
file_app_dns_fakedns_fakedns_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_dns_fakedns_fakedns_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_app_dns_fakedns_fakedns_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_app_dns_fakedns_fakedns_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_app_dns_fakedns_fakedns_proto_goTypes = []interface{}{
|
||||||
|
(*FakeDnsPool)(nil), // 0: xray.app.dns.fakedns.FakeDnsPool
|
||||||
|
}
|
||||||
|
var file_app_dns_fakedns_fakedns_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_app_dns_fakedns_fakedns_proto_init() }
|
||||||
|
func file_app_dns_fakedns_fakedns_proto_init() {
|
||||||
|
if File_app_dns_fakedns_fakedns_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_app_dns_fakedns_fakedns_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*FakeDnsPool); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_app_dns_fakedns_fakedns_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_app_dns_fakedns_fakedns_proto_goTypes,
|
||||||
|
DependencyIndexes: file_app_dns_fakedns_fakedns_proto_depIdxs,
|
||||||
|
MessageInfos: file_app_dns_fakedns_fakedns_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_app_dns_fakedns_fakedns_proto = out.File
|
||||||
|
file_app_dns_fakedns_fakedns_proto_rawDesc = nil
|
||||||
|
file_app_dns_fakedns_fakedns_proto_goTypes = nil
|
||||||
|
file_app_dns_fakedns_fakedns_proto_depIdxs = nil
|
||||||
|
}
|
12
app/dns/fakedns/fakedns.proto
Normal file
12
app/dns/fakedns/fakedns.proto
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.app.dns.fakedns;
|
||||||
|
option csharp_namespace = "Xray.App.Dns.Fakedns";
|
||||||
|
option go_package = "github.com/xtls/xray-core/app/dns/fakedns";
|
||||||
|
option java_package = "com.xray.app.dns.fakedns";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message FakeDnsPool{
|
||||||
|
string ip_pool = 1; //CIDR of IP pool used as fake DNS IP
|
||||||
|
int64 lruSize = 2; //Size of Pool for remembering relationship between domain name and IP address
|
||||||
|
}
|
105
app/dns/fakedns/fakedns_test.go
Normal file
105
app/dns/fakedns/fakedns_test.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package fakedns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/uuid"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ipPrefix = "198.18."
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewFakeDnsHolder(_ *testing.T) {
|
||||||
|
_, err := NewFakeDNSHolder()
|
||||||
|
common.Must(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFakeDnsHolderCreateMapping(t *testing.T) {
|
||||||
|
fkdns, err := NewFakeDNSHolder()
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
addr := fkdns.GetFakeIPForDomain("fakednstest.example.com")
|
||||||
|
assert.Equal(t, ipPrefix, addr[0].IP().String()[0:len(ipPrefix)])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFakeDnsHolderCreateMappingMany(t *testing.T) {
|
||||||
|
fkdns, err := NewFakeDNSHolder()
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
addr := fkdns.GetFakeIPForDomain("fakednstest.example.com")
|
||||||
|
assert.Equal(t, ipPrefix, addr[0].IP().String()[0:len(ipPrefix)])
|
||||||
|
|
||||||
|
addr2 := fkdns.GetFakeIPForDomain("fakednstest2.example.com")
|
||||||
|
assert.Equal(t, ipPrefix, addr2[0].IP().String()[0:len(ipPrefix)])
|
||||||
|
assert.NotEqual(t, addr[0].IP().String(), addr2[0].IP().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFakeDnsHolderCreateMappingManyAndResolve(t *testing.T) {
|
||||||
|
fkdns, err := NewFakeDNSHolder()
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
addr := fkdns.GetFakeIPForDomain("fakednstest.example.com")
|
||||||
|
addr2 := fkdns.GetFakeIPForDomain("fakednstest2.example.com")
|
||||||
|
|
||||||
|
{
|
||||||
|
result := fkdns.GetDomainFromFakeDNS(addr[0])
|
||||||
|
assert.Equal(t, "fakednstest.example.com", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
result := fkdns.GetDomainFromFakeDNS(addr2[0])
|
||||||
|
assert.Equal(t, "fakednstest2.example.com", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFakeDnsHolderCreateMappingManySingleDomain(t *testing.T) {
|
||||||
|
fkdns, err := NewFakeDNSHolder()
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
addr := fkdns.GetFakeIPForDomain("fakednstest.example.com")
|
||||||
|
addr2 := fkdns.GetFakeIPForDomain("fakednstest.example.com")
|
||||||
|
assert.Equal(t, addr[0].IP().String(), addr2[0].IP().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
|
||||||
|
fkdns, err := NewFakeDNSHolderConfigOnly(&FakeDnsPool{
|
||||||
|
IpPool: dns.FakeIPPool,
|
||||||
|
LruSize: 256,
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
err = fkdns.Start()
|
||||||
|
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
addr := fkdns.GetFakeIPForDomain("fakednstest.example.com")
|
||||||
|
addr2 := fkdns.GetFakeIPForDomain("fakednstest2.example.com")
|
||||||
|
|
||||||
|
for i := 0; i <= 8192; i++ {
|
||||||
|
{
|
||||||
|
result := fkdns.GetDomainFromFakeDNS(addr[0])
|
||||||
|
assert.Equal(t, "fakednstest.example.com", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
result := fkdns.GetDomainFromFakeDNS(addr2[0])
|
||||||
|
assert.Equal(t, "fakednstest2.example.com", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uuid := uuid.New()
|
||||||
|
domain := uuid.String() + ".fakednstest.example.com"
|
||||||
|
tempAddr := fkdns.GetFakeIPForDomain(domain)
|
||||||
|
rsaddr := tempAddr[0].IP().String()
|
||||||
|
|
||||||
|
result := fkdns.GetDomainFromFakeDNS(net.ParseAddress(rsaddr))
|
||||||
|
assert.Equal(t, domain, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/strmatcher"
|
"github.com/xtls/xray-core/common/strmatcher"
|
||||||
"github.com/xtls/xray-core/features"
|
"github.com/xtls/xray-core/features"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StaticHosts represents static domain-ip mapping in DNS server.
|
// StaticHosts represents static domain-ip mapping in DNS server.
|
||||||
@@ -13,25 +14,6 @@ type StaticHosts struct {
|
|||||||
matchers *strmatcher.MatcherGroup
|
matchers *strmatcher.MatcherGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
var typeMap = map[DomainMatchingType]strmatcher.Type{
|
|
||||||
DomainMatchingType_Full: strmatcher.Full,
|
|
||||||
DomainMatchingType_Subdomain: strmatcher.Domain,
|
|
||||||
DomainMatchingType_Keyword: strmatcher.Substr,
|
|
||||||
DomainMatchingType_Regex: strmatcher.Regex,
|
|
||||||
}
|
|
||||||
|
|
||||||
func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, error) {
|
|
||||||
strMType, f := typeMap[t]
|
|
||||||
if !f {
|
|
||||||
return nil, newError("unknown mapping type", t).AtWarning()
|
|
||||||
}
|
|
||||||
matcher, err := strMType.New(domain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to create str matcher").Base(err)
|
|
||||||
}
|
|
||||||
return matcher, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStaticHosts creates a new StaticHosts instance.
|
// NewStaticHosts creates a new StaticHosts instance.
|
||||||
func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) {
|
func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) {
|
||||||
g := new(strmatcher.MatcherGroup)
|
g := new(strmatcher.MatcherGroup)
|
||||||
@@ -65,6 +47,9 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
|
|||||||
id := g.Add(matcher)
|
id := g.Add(matcher)
|
||||||
ips := make([]net.Address, 0, len(mapping.Ip)+1)
|
ips := make([]net.Address, 0, len(mapping.Ip)+1)
|
||||||
switch {
|
switch {
|
||||||
|
case len(mapping.ProxiedDomain) > 0:
|
||||||
|
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
|
||||||
|
|
||||||
case len(mapping.Ip) > 0:
|
case len(mapping.Ip) > 0:
|
||||||
for _, ip := range mapping.Ip {
|
for _, ip := range mapping.Ip {
|
||||||
addr := net.IPAddress(ip)
|
addr := net.IPAddress(ip)
|
||||||
@@ -74,49 +59,56 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
|
|||||||
ips = append(ips, addr)
|
ips = append(ips, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
case len(mapping.ProxiedDomain) > 0:
|
|
||||||
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning()
|
return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special handling for localhost IPv6. This is a dirty workaround as JSON config supports only single IP mapping.
|
|
||||||
if len(ips) == 1 && ips[0] == net.LocalHostIP {
|
|
||||||
ips = append(ips, net.LocalHostIPv6)
|
|
||||||
}
|
|
||||||
|
|
||||||
sh.ips[id] = ips
|
sh.ips[id] = ips
|
||||||
}
|
}
|
||||||
|
|
||||||
return sh, nil
|
return sh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterIP(ips []net.Address, option IPOption) []net.Address {
|
func filterIP(ips []net.Address, option *dns.IPOption) []net.Address {
|
||||||
filtered := make([]net.Address, 0, len(ips))
|
filtered := make([]net.Address, 0, len(ips))
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) {
|
if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) {
|
||||||
filtered = append(filtered, ip)
|
filtered = append(filtered, ip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(filtered) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return filtered
|
return filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupIP returns IP address for the given domain, if exists in this StaticHosts.
|
func (h *StaticHosts) lookupInternal(domain string) []net.Address {
|
||||||
func (h *StaticHosts) LookupIP(domain string, option IPOption) []net.Address {
|
var ips []net.Address
|
||||||
indices := h.matchers.Match(domain)
|
for _, id := range h.matchers.Match(domain) {
|
||||||
if len(indices) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ips := []net.Address{}
|
|
||||||
for _, id := range indices {
|
|
||||||
ips = append(ips, h.ips[id]...)
|
ips = append(ips, h.ips[id]...)
|
||||||
}
|
}
|
||||||
if len(ips) == 1 && ips[0].Family().IsDomain() {
|
if len(ips) == 1 && ips[0].Family().IsDomain() {
|
||||||
return ips
|
return ips
|
||||||
}
|
}
|
||||||
return filterIP(ips, option)
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *StaticHosts) lookup(domain string, option *dns.IPOption, maxDepth int) []net.Address {
|
||||||
|
switch addrs := h.lookupInternal(domain); {
|
||||||
|
case len(addrs) == 0: // Not recorded in static hosts, return nil
|
||||||
|
return nil
|
||||||
|
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
|
||||||
|
newError("found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it").AtDebug().WriteToLog()
|
||||||
|
if maxDepth > 0 {
|
||||||
|
unwrapped := h.lookup(addrs[0].Domain(), option, maxDepth-1)
|
||||||
|
if unwrapped != nil {
|
||||||
|
return unwrapped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addrs
|
||||||
|
default: // IP record found, return a non-nil IP array
|
||||||
|
return filterIP(addrs, option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
|
||||||
|
func (h *StaticHosts) Lookup(domain string, option *dns.IPOption) []net.Address {
|
||||||
|
return h.lookup(domain, option, 5)
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ import (
|
|||||||
. "github.com/xtls/xray-core/app/dns"
|
. "github.com/xtls/xray-core/app/dns"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStaticHosts(t *testing.T) {
|
func TestStaticHosts(t *testing.T) {
|
||||||
@@ -19,6 +20,20 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
{1, 1, 1, 1},
|
{1, 1, 1, 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Type: DomainMatchingType_Full,
|
||||||
|
Domain: "proxy.example.com",
|
||||||
|
Ip: [][]byte{
|
||||||
|
{1, 2, 3, 4},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
},
|
||||||
|
ProxiedDomain: "another-proxy.example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: DomainMatchingType_Full,
|
||||||
|
Domain: "proxy2.example.com",
|
||||||
|
ProxiedDomain: "proxy.example.com",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Type: DomainMatchingType_Subdomain,
|
Type: DomainMatchingType_Subdomain,
|
||||||
Domain: "example.cn",
|
Domain: "example.cn",
|
||||||
@@ -31,6 +46,7 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
Domain: "baidu.com",
|
Domain: "baidu.com",
|
||||||
Ip: [][]byte{
|
Ip: [][]byte{
|
||||||
{127, 0, 0, 1},
|
{127, 0, 0, 1},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -39,7 +55,7 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
{
|
{
|
||||||
ips := hosts.LookupIP("example.com", IPOption{
|
ips := hosts.Lookup("example.com", &dns.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
})
|
})
|
||||||
@@ -52,7 +68,33 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ips := hosts.LookupIP("www.example.cn", IPOption{
|
domain := hosts.Lookup("proxy.example.com", &dns.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: false,
|
||||||
|
})
|
||||||
|
if len(domain) != 1 {
|
||||||
|
t.Error("expect 1 domain, but got ", len(domain))
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(domain[0].Domain(), "another-proxy.example.com"); diff != "" {
|
||||||
|
t.Error(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
domain := hosts.Lookup("proxy2.example.com", &dns.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: false,
|
||||||
|
})
|
||||||
|
if len(domain) != 1 {
|
||||||
|
t.Error("expect 1 domain, but got ", len(domain))
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(domain[0].Domain(), "another-proxy.example.com"); diff != "" {
|
||||||
|
t.Error(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ips := hosts.Lookup("www.example.cn", &dns.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
})
|
})
|
||||||
@@ -65,7 +107,7 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ips := hosts.LookupIP("baidu.com", IPOption{
|
ips := hosts.Lookup("baidu.com", &dns.IPOption{
|
||||||
IPv4Enable: false,
|
IPv4Enable: false,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
})
|
})
|
||||||
|
@@ -2,53 +2,211 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/app/router"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/features/dns/localdns"
|
"github.com/xtls/xray-core/common/strmatcher"
|
||||||
|
core "github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
"github.com/xtls/xray-core/features/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IPOption is an object for IP query options.
|
// Server is the interface for Name Server.
|
||||||
type IPOption struct {
|
type Server interface {
|
||||||
IPv4Enable bool
|
// Name of the Client.
|
||||||
IPv6Enable bool
|
Name() string
|
||||||
|
// QueryIP sends IP queries to its configured server.
|
||||||
|
QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, cs CacheStrategy) ([]net.IP, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client is the interface for DNS client.
|
// Client is the interface for DNS client.
|
||||||
type Client interface {
|
type Client struct {
|
||||||
// Name of the Client.
|
server Server
|
||||||
Name() string
|
clientIP net.IP
|
||||||
|
skipFallback bool
|
||||||
// QueryIP sends IP queries to its configured server.
|
domains []string
|
||||||
QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error)
|
expectIPs []*router.GeoIPMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocalNameServer struct {
|
var errExpectedIPNonMatch = errors.New("expectIPs not match")
|
||||||
client *localdns.Client
|
|
||||||
|
// NewServer creates a name server object according to the network destination url.
|
||||||
|
func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, error) {
|
||||||
|
if address := dest.Address; address.Family().IsDomain() {
|
||||||
|
u, err := url.Parse(address.Domain())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case strings.EqualFold(u.String(), "localhost"):
|
||||||
|
return NewLocalNameServer(), nil
|
||||||
|
case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
|
||||||
|
return NewDoHNameServer(u, dispatcher)
|
||||||
|
case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
|
||||||
|
return NewDoHLocalNameServer(u), nil
|
||||||
|
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
|
||||||
|
return NewQUICNameServer(u)
|
||||||
|
case strings.EqualFold(u.String(), "fakedns"):
|
||||||
|
return NewFakeDNSServer(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dest.Network == net.Network_Unknown {
|
||||||
|
dest.Network = net.Network_UDP
|
||||||
|
}
|
||||||
|
if dest.Network == net.Network_UDP { // UDP classic DNS mode
|
||||||
|
return NewClassicNameServer(dest, dispatcher), nil
|
||||||
|
}
|
||||||
|
return nil, newError("No available name server could be created from ", dest).AtWarning()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error) {
|
// NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
|
||||||
if option.IPv4Enable && option.IPv6Enable {
|
func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container router.GeoIPMatcherContainer, matcherInfos *[]DomainMatcherInfo, updateDomainRule func(strmatcher.Matcher, int, []DomainMatcherInfo) error) (*Client, error) {
|
||||||
return s.client.LookupIP(domain)
|
client := &Client{}
|
||||||
|
|
||||||
|
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
||||||
|
// Create a new server for each client for now
|
||||||
|
server, err := NewServer(ns.Address.AsDestination(), dispatcher)
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to create nameserver").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priotize local domains with specific TLDs or without any dot to local DNS
|
||||||
|
if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS {
|
||||||
|
ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
|
||||||
|
ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
|
||||||
|
// The following lines is a solution to avoid core panics(rule index out of range) when setting `localhost` DNS client in config.
|
||||||
|
// Because the `localhost` DNS client will apend len(localTLDsAndDotlessDomains) rules into matcherInfos to match `geosite:private` default rule.
|
||||||
|
// But `matcherInfos` has no enough length to add rules, which leads to core panics (rule index out of range).
|
||||||
|
// To avoid this, the length of `matcherInfos` must be equal to the expected, so manually append it with Golang default zero value first for later modification.
|
||||||
|
for i := 0; i < len(localTLDsAndDotlessDomains); i++ {
|
||||||
|
*matcherInfos = append(*matcherInfos, DomainMatcherInfo{
|
||||||
|
clientIdx: uint16(0),
|
||||||
|
domainRuleIdx: uint16(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establish domain rules
|
||||||
|
var rules []string
|
||||||
|
ruleCurr := 0
|
||||||
|
ruleIter := 0
|
||||||
|
for _, domain := range ns.PrioritizedDomain {
|
||||||
|
domainRule, err := toStrMatcher(domain.Type, domain.Domain)
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to create prioritized domain").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
originalRuleIdx := ruleCurr
|
||||||
|
if ruleCurr < len(ns.OriginalRules) {
|
||||||
|
rule := ns.OriginalRules[ruleCurr]
|
||||||
|
if ruleCurr >= len(rules) {
|
||||||
|
rules = append(rules, rule.Rule)
|
||||||
|
}
|
||||||
|
ruleIter++
|
||||||
|
if ruleIter >= int(rule.Size) {
|
||||||
|
ruleIter = 0
|
||||||
|
ruleCurr++
|
||||||
|
}
|
||||||
|
} else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests)
|
||||||
|
rules = append(rules, domainRule.String())
|
||||||
|
ruleCurr++
|
||||||
|
}
|
||||||
|
err = updateDomainRule(domainRule, originalRuleIdx, *matcherInfos)
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to create prioritized domain").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establish expected IPs
|
||||||
|
var matchers []*router.GeoIPMatcher
|
||||||
|
for _, geoip := range ns.Geoip {
|
||||||
|
matcher, err := container.Add(geoip)
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to create ip matcher").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
matchers = append(matchers, matcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(clientIP) > 0 {
|
||||||
|
switch ns.Address.Address.GetAddress().(type) {
|
||||||
|
case *net.IPOrDomain_Domain:
|
||||||
|
newError("DNS: client ", ns.Address.Address.GetDomain(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog()
|
||||||
|
case *net.IPOrDomain_Ip:
|
||||||
|
newError("DNS: client ", ns.Address.Address.GetIp(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.server = server
|
||||||
|
client.clientIP = clientIP
|
||||||
|
client.domains = rules
|
||||||
|
client.expectIPs = matchers
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSimpleClient creates a DNS client with a simple destination.
|
||||||
|
func NewSimpleClient(ctx context.Context, endpoint *net.Endpoint, clientIP net.IP) (*Client, error) {
|
||||||
|
client := &Client{}
|
||||||
|
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
||||||
|
server, err := NewServer(endpoint.AsDestination(), dispatcher)
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to create nameserver").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
client.server = server
|
||||||
|
client.clientIP = clientIP
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(clientIP) > 0 {
|
||||||
|
switch endpoint.Address.GetAddress().(type) {
|
||||||
|
case *net.IPOrDomain_Domain:
|
||||||
|
newError("DNS: client ", endpoint.Address.GetDomain(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog()
|
||||||
|
case *net.IPOrDomain_Ip:
|
||||||
|
newError("DNS: client ", endpoint.Address.GetIp(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if option.IPv4Enable {
|
return client, err
|
||||||
return s.client.LookupIPv4(domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
if option.IPv6Enable {
|
|
||||||
return s.client.LookupIPv6(domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, newError("neither IPv4 nor IPv6 is enabled")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalNameServer) Name() string {
|
// Name returns the server name the client manages.
|
||||||
return "localhost"
|
func (c *Client) Name() string {
|
||||||
|
return c.server.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLocalNameServer() *LocalNameServer {
|
// QueryIP send DNS query to the name server with the client's IP.
|
||||||
newError("DNS: created localhost client").AtInfo().WriteToLog()
|
func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption, cs CacheStrategy) ([]net.IP, error) {
|
||||||
return &LocalNameServer{
|
ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
|
||||||
client: localdns.New(),
|
ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option, cs)
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ips, err
|
||||||
}
|
}
|
||||||
|
return c.MatchExpectedIPs(domain, ips)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones.
|
||||||
|
func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) ([]net.IP, error) {
|
||||||
|
if len(c.expectIPs) == 0 {
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
newIps := []net.IP{}
|
||||||
|
for _, ip := range ips {
|
||||||
|
for _, matcher := range c.expectIPs {
|
||||||
|
if matcher.Match(ip) {
|
||||||
|
newIps = append(newIps, ip)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(newIps) == 0 {
|
||||||
|
return nil, errExpectedIPNonMatch
|
||||||
|
}
|
||||||
|
newError("domain ", domain, " expectIPs ", newIps, " matched at server ", c.Name()).AtDebug().WriteToLog()
|
||||||
|
return newIps, nil
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/log"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/net/cnc"
|
"github.com/xtls/xray-core/common/net/cnc"
|
||||||
"github.com/xtls/xray-core/common/protocol/dns"
|
"github.com/xtls/xray-core/common/protocol/dns"
|
||||||
@@ -41,20 +42,71 @@ type DoHNameServer struct {
|
|||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDoHNameServer creates DOH client object for remote resolving
|
// NewDoHNameServer creates DOH server object for remote resolving
|
||||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net.IP) (*DoHNameServer, error) {
|
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServer, error) {
|
||||||
newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
|
newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
|
||||||
s := baseDOHNameServer(url, "DOH", clientIP)
|
s := baseDOHNameServer(url, "DOH")
|
||||||
|
|
||||||
s.dispatcher = dispatcher
|
s.dispatcher = dispatcher
|
||||||
|
tr := &http.Transport{
|
||||||
|
MaxIdleConns: 30,
|
||||||
|
IdleConnTimeout: 90 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 30 * time.Second,
|
||||||
|
ForceAttemptHTTP2: true,
|
||||||
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
dispatcherCtx := context.Background()
|
||||||
|
|
||||||
|
dest, err := net.ParseDestination(network + ":" + addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatcherCtx = session.ContextWithContent(dispatcherCtx, session.ContentFromContext(ctx))
|
||||||
|
dispatcherCtx = session.ContextWithInbound(dispatcherCtx, session.InboundFromContext(ctx))
|
||||||
|
dispatcherCtx = log.ContextWithAccessMessage(dispatcherCtx, &log.AccessMessage{
|
||||||
|
From: "DoH",
|
||||||
|
To: s.dohURL,
|
||||||
|
Status: log.AccessAccepted,
|
||||||
|
Reason: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
link, err := s.dispatcher.Dispatch(dispatcherCtx, dest)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cc := common.ChainedClosable{}
|
||||||
|
if cw, ok := link.Writer.(common.Closable); ok {
|
||||||
|
cc = append(cc, cw)
|
||||||
|
}
|
||||||
|
if cr, ok := link.Reader.(common.Closable); ok {
|
||||||
|
cc = append(cc, cr)
|
||||||
|
}
|
||||||
|
return cnc.NewConnection(
|
||||||
|
cnc.ConnectionInputMulti(link.Writer),
|
||||||
|
cnc.ConnectionOutputMulti(link.Reader),
|
||||||
|
cnc.ConnectionOnClose(cc),
|
||||||
|
), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s.httpClient = &http.Client{
|
||||||
|
Timeout: time.Second * 180,
|
||||||
|
Transport: tr,
|
||||||
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDoHLocalNameServer creates DOH client object for local resolving
|
// NewDoHLocalNameServer creates DOH client object for local resolving
|
||||||
func NewDoHLocalNameServer(url *url.URL, clientIP net.IP) *DoHNameServer {
|
func NewDoHLocalNameServer(url *url.URL) *DoHNameServer {
|
||||||
url.Scheme = "https"
|
url.Scheme = "https"
|
||||||
s := baseDOHNameServer(url, "DOHL", clientIP)
|
s := baseDOHNameServer(url, "DOHL")
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
IdleConnTimeout: 90 * time.Second,
|
IdleConnTimeout: 90 * time.Second,
|
||||||
ForceAttemptHTTP2: true,
|
ForceAttemptHTTP2: true,
|
||||||
@@ -64,6 +116,12 @@ func NewDoHLocalNameServer(url *url.URL, clientIP net.IP) *DoHNameServer {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conn, err := internet.DialSystem(ctx, dest, nil)
|
conn, err := internet.DialSystem(ctx, dest, nil)
|
||||||
|
log.Record(&log.AccessMessage{
|
||||||
|
From: "DoH",
|
||||||
|
To: s.dohURL,
|
||||||
|
Status: log.AccessAccepted,
|
||||||
|
Detour: "local",
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -78,13 +136,12 @@ func NewDoHLocalNameServer(url *url.URL, clientIP net.IP) *DoHNameServer {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func baseDOHNameServer(url *url.URL, prefix string, clientIP net.IP) *DoHNameServer {
|
func baseDOHNameServer(url *url.URL, prefix string) *DoHNameServer {
|
||||||
s := &DoHNameServer{
|
s := &DoHNameServer{
|
||||||
ips: make(map[string]record),
|
ips: make(map[string]record),
|
||||||
clientIP: clientIP,
|
pub: pubsub.NewService(),
|
||||||
pub: pubsub.NewService(),
|
name: prefix + "//" + url.Host,
|
||||||
name: prefix + "//" + url.Host,
|
dohURL: url.String(),
|
||||||
dohURL: url.String(),
|
|
||||||
}
|
}
|
||||||
s.cleanup = &task.Periodic{
|
s.cleanup = &task.Periodic{
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
@@ -94,7 +151,7 @@ func baseDOHNameServer(url *url.URL, prefix string, clientIP net.IP) *DoHNameSer
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name returns client name
|
// Name implements Server.
|
||||||
func (s *DoHNameServer) Name() string {
|
func (s *DoHNameServer) Name() string {
|
||||||
return s.name
|
return s.name
|
||||||
}
|
}
|
||||||
@@ -177,7 +234,7 @@ func (s *DoHNameServer) newReqID() uint16 {
|
|||||||
return uint16(atomic.AddUint32(&s.reqID, 1))
|
return uint16(atomic.AddUint32(&s.reqID, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPOption) {
|
func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
|
||||||
newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
|
newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
if s.name+"." == "DOH//"+domain {
|
if s.name+"." == "DOH//"+domain {
|
||||||
@@ -185,7 +242,7 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPO
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP))
|
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
|
||||||
|
|
||||||
var deadline time.Time
|
var deadline time.Time
|
||||||
if d, ok := ctx.Deadline(); ok {
|
if d, ok := ctx.Deadline(); ok {
|
||||||
@@ -206,8 +263,8 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPO
|
|||||||
}
|
}
|
||||||
|
|
||||||
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
|
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
|
||||||
Protocol: "https",
|
Protocol: "https",
|
||||||
//SkipRoutePick: true,
|
SkipDNSResolve: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// forced to use mux for DOH
|
// forced to use mux for DOH
|
||||||
@@ -249,41 +306,6 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
|
|||||||
|
|
||||||
hc := s.httpClient
|
hc := s.httpClient
|
||||||
|
|
||||||
// Dispatched connection will be closed (interrupted) after each request
|
|
||||||
// This makes DOH inefficient without a keep-alived connection
|
|
||||||
// See: core/app/proxyman/outbound/handler.go:113
|
|
||||||
// Using mux (https request wrapped in a stream layer) improves the situation.
|
|
||||||
// Recommend to use NewDoHLocalNameServer (DOHL:) if xray instance is running on
|
|
||||||
// a normal network eg. the server side of xray
|
|
||||||
|
|
||||||
if s.dispatcher != nil {
|
|
||||||
tr := &http.Transport{
|
|
||||||
MaxIdleConns: 30,
|
|
||||||
IdleConnTimeout: 90 * time.Second,
|
|
||||||
TLSHandshakeTimeout: 30 * time.Second,
|
|
||||||
ForceAttemptHTTP2: true,
|
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
dest, err := net.ParseDestination(network + ":" + addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
link, err := s.dispatcher.Dispatch(ctx, dest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return cnc.NewConnection(
|
|
||||||
cnc.ConnectionInputMulti(link.Writer),
|
|
||||||
cnc.ConnectionOutputMulti(link.Reader),
|
|
||||||
), nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
hc = &http.Client{
|
|
||||||
Timeout: time.Second * 180,
|
|
||||||
Transport: tr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := hc.Do(req.WithContext(ctx))
|
resp, err := hc.Do(req.WithContext(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -298,7 +320,7 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
|
|||||||
return ioutil.ReadAll(resp.Body)
|
return ioutil.ReadAll(resp.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DoHNameServer) findIPsForDomain(domain string, option IPOption) ([]net.IP, error) {
|
func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
record, found := s.ips[domain]
|
record, found := s.ips[domain]
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
@@ -326,7 +348,7 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option IPOption) ([]net.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(ips) > 0 {
|
if len(ips) > 0 {
|
||||||
return toNetIP(ips), nil
|
return toNetIP(ips)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lastErr != nil {
|
if lastErr != nil {
|
||||||
@@ -340,14 +362,21 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option IPOption) ([]net.
|
|||||||
return nil, errRecordNotFound
|
return nil, errRecordNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryIP is called from dns.Server->queryIPTimeout
|
// QueryIP implements Server.
|
||||||
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error) {
|
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) { // nolint: dupl
|
||||||
fqdn := Fqdn(domain)
|
fqdn := Fqdn(domain)
|
||||||
|
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
if cs == CacheStrategy_Cache_DISABLE {
|
||||||
if err != errRecordNotFound {
|
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
||||||
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
} else {
|
||||||
return ips, err
|
ips, err := s.findIPsForDomain(fqdn, option)
|
||||||
|
if err != errRecordNotFound {
|
||||||
|
if cs == CacheStrategy_Cache_ALL || (cs == CacheStrategy_Cache_NOERROR && err == nil) {
|
||||||
|
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
||||||
|
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Error: err})
|
||||||
|
return ips, err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ipv4 and ipv6 belong to different subscription groups
|
// ipv4 and ipv6 belong to different subscription groups
|
||||||
@@ -376,11 +405,13 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option IPOpt
|
|||||||
}
|
}
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
s.sendQuery(ctx, fqdn, option)
|
s.sendQuery(ctx, fqdn, clientIP, option)
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
ips, err := s.findIPsForDomain(fqdn, option)
|
||||||
if err != errRecordNotFound {
|
if err != errRecordNotFound {
|
||||||
|
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
|
||||||
return ips, err
|
return ips, err
|
||||||
}
|
}
|
||||||
|
|
60
app/dns/nameserver_doh_test.go
Normal file
60
app/dns/nameserver_doh_test.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package dns_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
|
. "github.com/xtls/xray-core/app/dns"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDOHNameServer(t *testing.T) {
|
||||||
|
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
s := NewDoHLocalNameServer(url)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, CacheStrategy_Cache_ALL)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
if len(ips) == 0 {
|
||||||
|
t.Error("expect some ips, but got 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDOHNameServerWithCache(t *testing.T) {
|
||||||
|
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
s := NewDoHLocalNameServer(url)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, CacheStrategy_Cache_ALL)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
if len(ips) == 0 {
|
||||||
|
t.Error("expect some ips, but got 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx2, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||||
|
ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, CacheStrategy_Cache_ALL)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
if r := cmp.Diff(ips2, ips); r != "" {
|
||||||
|
t.Fatal(r)
|
||||||
|
}
|
||||||
|
}
|
43
app/dns/nameserver_fakedns.go
Normal file
43
app/dns/nameserver_fakedns.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FakeDNSServer struct {
|
||||||
|
fakeDNSEngine dns.FakeDNSEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFakeDNSServer() *FakeDNSServer {
|
||||||
|
return &FakeDNSServer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FakeDNSName = "FakeDNS"
|
||||||
|
|
||||||
|
func (FakeDNSServer) Name() string {
|
||||||
|
return FakeDNSName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, _ dns.IPOption, _ CacheStrategy) ([]net.IP, error) {
|
||||||
|
if f.fakeDNSEngine == nil {
|
||||||
|
if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) {
|
||||||
|
f.fakeDNSEngine = fd
|
||||||
|
}); err != nil {
|
||||||
|
return nil, newError("Unable to locate a fake DNS Engine").Base(err).AtError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ips := f.fakeDNSEngine.GetFakeIPForDomain(domain)
|
||||||
|
|
||||||
|
netIP, err := toNetIP(ips)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Unable to convert IP to net ip").Base(err).AtError()
|
||||||
|
}
|
||||||
|
|
||||||
|
newError(f.Name(), " got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
|
||||||
|
|
||||||
|
return netIP, nil
|
||||||
|
}
|
53
app/dns/nameserver_local.go
Normal file
53
app/dns/nameserver_local.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
"github.com/xtls/xray-core/features/dns/localdns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LocalNameServer is an wrapper over local DNS feature.
|
||||||
|
type LocalNameServer struct {
|
||||||
|
client *localdns.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryIP implements Server.
|
||||||
|
func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ CacheStrategy) ([]net.IP, error) {
|
||||||
|
var ips []net.IP
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case option.IPv4Enable && option.IPv6Enable:
|
||||||
|
ips, err = s.client.LookupIP(domain)
|
||||||
|
case option.IPv4Enable:
|
||||||
|
ips, err = s.client.LookupIPv4(domain)
|
||||||
|
case option.IPv6Enable:
|
||||||
|
ips, err = s.client.LookupIPv6(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ips) > 0 {
|
||||||
|
newError("Localhost got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ips, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name implements Server.
|
||||||
|
func (s *LocalNameServer) Name() string {
|
||||||
|
return "localhost"
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLocalNameServer creates localdns server object for directly lookup in system DNS.
|
||||||
|
func NewLocalNameServer() *LocalNameServer {
|
||||||
|
newError("DNS: created localhost client").AtInfo().WriteToLog()
|
||||||
|
return &LocalNameServer{
|
||||||
|
client: localdns.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLocalDNSClient creates localdns client object for directly lookup in system DNS.
|
||||||
|
func NewLocalDNSClient() *Client {
|
||||||
|
return &Client{server: NewLocalNameServer()}
|
||||||
|
}
|
@@ -7,15 +7,17 @@ import (
|
|||||||
|
|
||||||
. "github.com/xtls/xray-core/app/dns"
|
. "github.com/xtls/xray-core/app/dns"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLocalNameServer(t *testing.T) {
|
func TestLocalNameServer(t *testing.T) {
|
||||||
s := NewLocalNameServer()
|
s := NewLocalNameServer()
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
ips, err := s.QueryIP(ctx, "google.com", IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
})
|
}, CacheStrategy_Cache_ALL)
|
||||||
cancel()
|
cancel()
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if len(ips) == 0 {
|
if len(ips) == 0 {
|
394
app/dns/nameserver_quic.go
Normal file
394
app/dns/nameserver_quic.go
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go"
|
||||||
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/buf"
|
||||||
|
"github.com/xtls/xray-core/common/log"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/protocol/dns"
|
||||||
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/signal/pubsub"
|
||||||
|
"github.com/xtls/xray-core/common/task"
|
||||||
|
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NextProtoDQ - During connection establishment, DNS/QUIC support is indicated
|
||||||
|
// by selecting the ALPN token "dq" in the crypto handshake.
|
||||||
|
const NextProtoDQ = "doq-i00"
|
||||||
|
|
||||||
|
// QUICNameServer implemented DNS over QUIC
|
||||||
|
type QUICNameServer struct {
|
||||||
|
sync.RWMutex
|
||||||
|
ips map[string]record
|
||||||
|
pub *pubsub.Service
|
||||||
|
cleanup *task.Periodic
|
||||||
|
reqID uint32
|
||||||
|
name string
|
||||||
|
destination net.Destination
|
||||||
|
session quic.Session
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving
|
||||||
|
func NewQUICNameServer(url *url.URL) (*QUICNameServer, error) {
|
||||||
|
newError("DNS: created Local DNS-over-QUIC client for ", url.String()).AtInfo().WriteToLog()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
port := net.Port(784)
|
||||||
|
if url.Port() != "" {
|
||||||
|
port, err = net.PortFromString(url.Port())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dest := net.UDPDestination(net.DomainAddress(url.Hostname()), port)
|
||||||
|
|
||||||
|
s := &QUICNameServer{
|
||||||
|
ips: make(map[string]record),
|
||||||
|
pub: pubsub.NewService(),
|
||||||
|
name: url.String(),
|
||||||
|
destination: dest,
|
||||||
|
}
|
||||||
|
s.cleanup = &task.Periodic{
|
||||||
|
Interval: time.Minute,
|
||||||
|
Execute: s.Cleanup,
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns client name
|
||||||
|
func (s *QUICNameServer) Name() string {
|
||||||
|
return s.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup clears expired items from cache
|
||||||
|
func (s *QUICNameServer) Cleanup() error {
|
||||||
|
now := time.Now()
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
if len(s.ips) == 0 {
|
||||||
|
return newError("nothing to do. stopping...")
|
||||||
|
}
|
||||||
|
|
||||||
|
for domain, record := range s.ips {
|
||||||
|
if record.A != nil && record.A.Expire.Before(now) {
|
||||||
|
record.A = nil
|
||||||
|
}
|
||||||
|
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
|
||||||
|
record.AAAA = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if record.A == nil && record.AAAA == nil {
|
||||||
|
newError(s.name, " cleanup ", domain).AtDebug().WriteToLog()
|
||||||
|
delete(s.ips, domain)
|
||||||
|
} else {
|
||||||
|
s.ips[domain] = record
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.ips) == 0 {
|
||||||
|
s.ips = make(map[string]record)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QUICNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
|
||||||
|
elapsed := time.Since(req.start)
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
rec := s.ips[req.domain]
|
||||||
|
updated := false
|
||||||
|
|
||||||
|
switch req.reqType {
|
||||||
|
case dnsmessage.TypeA:
|
||||||
|
if isNewer(rec.A, ipRec) {
|
||||||
|
rec.A = ipRec
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
case dnsmessage.TypeAAAA:
|
||||||
|
addr := make([]net.Address, 0)
|
||||||
|
for _, ip := range ipRec.IP {
|
||||||
|
if len(ip.IP()) == net.IPv6len {
|
||||||
|
addr = append(addr, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipRec.IP = addr
|
||||||
|
if isNewer(rec.AAAA, ipRec) {
|
||||||
|
rec.AAAA = ipRec
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog()
|
||||||
|
|
||||||
|
if updated {
|
||||||
|
s.ips[req.domain] = rec
|
||||||
|
}
|
||||||
|
switch req.reqType {
|
||||||
|
case dnsmessage.TypeA:
|
||||||
|
s.pub.Publish(req.domain+"4", nil)
|
||||||
|
case dnsmessage.TypeAAAA:
|
||||||
|
s.pub.Publish(req.domain+"6", nil)
|
||||||
|
}
|
||||||
|
s.Unlock()
|
||||||
|
common.Must(s.cleanup.Start())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QUICNameServer) newReqID() uint16 {
|
||||||
|
return uint16(atomic.AddUint32(&s.reqID, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
|
||||||
|
newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
|
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
|
||||||
|
|
||||||
|
var deadline time.Time
|
||||||
|
if d, ok := ctx.Deadline(); ok {
|
||||||
|
deadline = d
|
||||||
|
} else {
|
||||||
|
deadline = time.Now().Add(time.Second * 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, req := range reqs {
|
||||||
|
go func(r *dnsRequest) {
|
||||||
|
// generate new context for each req, using same context
|
||||||
|
// may cause reqs all aborted if any one encounter an error
|
||||||
|
dnsCtx := context.Background()
|
||||||
|
|
||||||
|
// reserve internal dns server requested Inbound
|
||||||
|
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||||
|
dnsCtx = session.ContextWithInbound(dnsCtx, inbound)
|
||||||
|
}
|
||||||
|
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
|
||||||
|
Protocol: "quic",
|
||||||
|
SkipDNSResolve: true,
|
||||||
|
})
|
||||||
|
dnsCtx = log.ContextWithAccessMessage(dnsCtx, &log.AccessMessage{
|
||||||
|
From: "DoQ",
|
||||||
|
To: s.name,
|
||||||
|
Status: log.AccessAccepted,
|
||||||
|
Reason: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
b, err := dns.PackMessage(r.msg)
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to pack dns query").Base(err).AtError().WriteToLog()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := s.openStream(dnsCtx)
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to open quic session").Base(err).AtError().WriteToLog()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = conn.Write(b.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to send query").Base(err).AtError().WriteToLog()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = conn.Close()
|
||||||
|
|
||||||
|
respBuf := buf.New()
|
||||||
|
defer respBuf.Release()
|
||||||
|
n, err := respBuf.ReadFrom(conn)
|
||||||
|
if err != nil && n == 0 {
|
||||||
|
newError("failed to read response").Base(err).AtError().WriteToLog()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rec, err := parseResponse(respBuf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to handle response").Base(err).AtError().WriteToLog()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.updateIP(r, rec)
|
||||||
|
}(req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
|
||||||
|
s.RLock()
|
||||||
|
record, found := s.ips[domain]
|
||||||
|
s.RUnlock()
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return nil, errRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
var ips []net.Address
|
||||||
|
var lastErr error
|
||||||
|
if option.IPv6Enable && record.AAAA != nil && record.AAAA.RCode == dnsmessage.RCodeSuccess {
|
||||||
|
aaaa, err := record.AAAA.getIPs()
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
ips = append(ips, aaaa...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if option.IPv4Enable && record.A != nil && record.A.RCode == dnsmessage.RCodeSuccess {
|
||||||
|
a, err := record.A.getIPs()
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
ips = append(ips, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ips) > 0 {
|
||||||
|
return toNetIP(ips)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastErr != nil {
|
||||||
|
return nil, lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) {
|
||||||
|
return nil, dns_feature.ErrEmptyResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryIP is called from dns.Server->queryIPTimeout
|
||||||
|
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) {
|
||||||
|
fqdn := Fqdn(domain)
|
||||||
|
|
||||||
|
if cs == CacheStrategy_Cache_DISABLE {
|
||||||
|
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
||||||
|
} else {
|
||||||
|
ips, err := s.findIPsForDomain(fqdn, option)
|
||||||
|
if err != errRecordNotFound {
|
||||||
|
if cs == CacheStrategy_Cache_ALL || (cs == CacheStrategy_Cache_NOERROR && err == nil) {
|
||||||
|
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
||||||
|
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Error: err})
|
||||||
|
return ips, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ipv4 and ipv6 belong to different subscription groups
|
||||||
|
var sub4, sub6 *pubsub.Subscriber
|
||||||
|
if option.IPv4Enable {
|
||||||
|
sub4 = s.pub.Subscribe(fqdn + "4")
|
||||||
|
defer sub4.Close()
|
||||||
|
}
|
||||||
|
if option.IPv6Enable {
|
||||||
|
sub6 = s.pub.Subscribe(fqdn + "6")
|
||||||
|
defer sub6.Close()
|
||||||
|
}
|
||||||
|
done := make(chan interface{})
|
||||||
|
go func() {
|
||||||
|
if sub4 != nil {
|
||||||
|
select {
|
||||||
|
case <-sub4.Wait():
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sub6 != nil {
|
||||||
|
select {
|
||||||
|
case <-sub6.Wait():
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
s.sendQuery(ctx, fqdn, clientIP, option)
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
for {
|
||||||
|
ips, err := s.findIPsForDomain(fqdn, option)
|
||||||
|
if err != errRecordNotFound {
|
||||||
|
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
|
||||||
|
return ips, err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isActive(s quic.Session) bool {
|
||||||
|
select {
|
||||||
|
case <-s.Context().Done():
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QUICNameServer) getSession() (quic.Session, error) {
|
||||||
|
var session quic.Session
|
||||||
|
s.RLock()
|
||||||
|
session = s.session
|
||||||
|
if session != nil && isActive(session) {
|
||||||
|
s.RUnlock()
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
if session != nil {
|
||||||
|
// we're recreating the session, let's create a new one
|
||||||
|
_ = session.CloseWithError(0, "")
|
||||||
|
}
|
||||||
|
s.RUnlock()
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
session, err = s.openSession()
|
||||||
|
if err != nil {
|
||||||
|
// This does not look too nice, but QUIC (or maybe quic-go)
|
||||||
|
// doesn't seem stable enough.
|
||||||
|
// Maybe retransmissions aren't fully implemented in quic-go?
|
||||||
|
// Anyways, the simple solution is to make a second try when
|
||||||
|
// it fails to open the QUIC session.
|
||||||
|
session, err = s.openSession()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.session = session
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QUICNameServer) openSession() (quic.Session, error) {
|
||||||
|
tlsConfig := tls.Config{}
|
||||||
|
quicConfig := &quic.Config{}
|
||||||
|
|
||||||
|
session, err := quic.DialAddrContext(context.Background(), s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto("http/1.1", http2.NextProtoTLS, NextProtoDQ)), quicConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QUICNameServer) openStream(ctx context.Context) (quic.Stream, error) {
|
||||||
|
session, err := s.getSession()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// open a new stream
|
||||||
|
return session.OpenStreamSync(ctx)
|
||||||
|
}
|
60
app/dns/nameserver_quic_test.go
Normal file
60
app/dns/nameserver_quic_test.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package dns_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
|
. "github.com/xtls/xray-core/app/dns"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestQUICNameServer(t *testing.T) {
|
||||||
|
url, err := url.Parse("quic://dns.adguard.com")
|
||||||
|
common.Must(err)
|
||||||
|
s, err := NewQUICNameServer(url)
|
||||||
|
common.Must(err)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, CacheStrategy_Cache_ALL)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
if len(ips) == 0 {
|
||||||
|
t.Error("expect some ips, but got 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQUICNameServerWithCache(t *testing.T) {
|
||||||
|
url, err := url.Parse("quic://dns.adguard.com")
|
||||||
|
common.Must(err)
|
||||||
|
s, err := NewQUICNameServer(url)
|
||||||
|
common.Must(err)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, CacheStrategy_Cache_ALL)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
if len(ips) == 0 {
|
||||||
|
t.Error("expect some ips, but got 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, CacheStrategy_Cache_ALL)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
if r := cmp.Diff(ips2, ips); r != "" {
|
||||||
|
t.Fatal(r)
|
||||||
|
}
|
||||||
|
}
|
@@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/log"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol/dns"
|
"github.com/xtls/xray-core/common/protocol/dns"
|
||||||
udp_proto "github.com/xtls/xray-core/common/protocol/udp"
|
udp_proto "github.com/xtls/xray-core/common/protocol/udp"
|
||||||
@@ -30,10 +31,10 @@ type ClassicNameServer struct {
|
|||||||
udpServer *udp.Dispatcher
|
udpServer *udp.Dispatcher
|
||||||
cleanup *task.Periodic
|
cleanup *task.Periodic
|
||||||
reqID uint32
|
reqID uint32
|
||||||
clientIP net.IP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, clientIP net.IP) *ClassicNameServer {
|
// NewClassicNameServer creates udp server object for remote resolving.
|
||||||
|
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher) *ClassicNameServer {
|
||||||
// default to 53 if unspecific
|
// default to 53 if unspecific
|
||||||
if address.Port == 0 {
|
if address.Port == 0 {
|
||||||
address.Port = net.Port(53)
|
address.Port = net.Port(53)
|
||||||
@@ -43,7 +44,6 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
|
|||||||
address: address,
|
address: address,
|
||||||
ips: make(map[string]record),
|
ips: make(map[string]record),
|
||||||
requests: make(map[uint16]dnsRequest),
|
requests: make(map[uint16]dnsRequest),
|
||||||
clientIP: clientIP,
|
|
||||||
pub: pubsub.NewService(),
|
pub: pubsub.NewService(),
|
||||||
name: strings.ToUpper(address.String()),
|
name: strings.ToUpper(address.String()),
|
||||||
}
|
}
|
||||||
@@ -52,14 +52,16 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
|
|||||||
Execute: s.Cleanup,
|
Execute: s.Cleanup,
|
||||||
}
|
}
|
||||||
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
|
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
|
||||||
newError("DNS: created udp client inited for ", address.NetAddr()).AtInfo().WriteToLog()
|
newError("DNS: created UDP client initialized for ", address.NetAddr()).AtInfo().WriteToLog()
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name implements Server.
|
||||||
func (s *ClassicNameServer) Name() string {
|
func (s *ClassicNameServer) Name() string {
|
||||||
return s.name
|
return s.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cleanup clears expired items from cache
|
||||||
func (s *ClassicNameServer) Cleanup() error {
|
func (s *ClassicNameServer) Cleanup() error {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
s.Lock()
|
s.Lock()
|
||||||
@@ -101,6 +103,7 @@ func (s *ClassicNameServer) Cleanup() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleResponse handles udp response packet from remote DNS server.
|
||||||
func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) {
|
func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) {
|
||||||
ipRec, err := parseResponse(packet.Payload.Bytes())
|
ipRec, err := parseResponse(packet.Payload.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -178,10 +181,10 @@ func (s *ClassicNameServer) addPendingRequest(req *dnsRequest) {
|
|||||||
s.requests[id] = *req
|
s.requests[id] = *req
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, option IPOption) {
|
func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
|
||||||
newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP))
|
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
|
||||||
|
|
||||||
for _, req := range reqs {
|
for _, req := range reqs {
|
||||||
s.addPendingRequest(req)
|
s.addPendingRequest(req)
|
||||||
@@ -193,11 +196,17 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, option
|
|||||||
udpCtx = session.ContextWithContent(udpCtx, &session.Content{
|
udpCtx = session.ContextWithContent(udpCtx, &session.Content{
|
||||||
Protocol: "dns",
|
Protocol: "dns",
|
||||||
})
|
})
|
||||||
|
udpCtx = log.ContextWithAccessMessage(udpCtx, &log.AccessMessage{
|
||||||
|
From: "DNS",
|
||||||
|
To: s.address,
|
||||||
|
Status: log.AccessAccepted,
|
||||||
|
Reason: "",
|
||||||
|
})
|
||||||
s.udpServer.Dispatch(udpCtx, s.address, b)
|
s.udpServer.Dispatch(udpCtx, s.address, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ClassicNameServer) findIPsForDomain(domain string, option IPOption) ([]net.IP, error) {
|
func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
record, found := s.ips[domain]
|
record, found := s.ips[domain]
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
@@ -225,7 +234,7 @@ func (s *ClassicNameServer) findIPsForDomain(domain string, option IPOption) ([]
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(ips) > 0 {
|
if len(ips) > 0 {
|
||||||
return toNetIP(ips), nil
|
return toNetIP(ips)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lastErr != nil {
|
if lastErr != nil {
|
||||||
@@ -235,13 +244,21 @@ func (s *ClassicNameServer) findIPsForDomain(domain string, option IPOption) ([]
|
|||||||
return nil, dns_feature.ErrEmptyResponse
|
return nil, dns_feature.ErrEmptyResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error) {
|
// QueryIP implements Server.
|
||||||
|
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) {
|
||||||
fqdn := Fqdn(domain)
|
fqdn := Fqdn(domain)
|
||||||
|
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
if cs == CacheStrategy_Cache_DISABLE {
|
||||||
if err != errRecordNotFound {
|
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
||||||
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
} else {
|
||||||
return ips, err
|
ips, err := s.findIPsForDomain(fqdn, option)
|
||||||
|
if err != errRecordNotFound {
|
||||||
|
if cs == CacheStrategy_Cache_ALL || (cs == CacheStrategy_Cache_NOERROR && err == nil) {
|
||||||
|
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
||||||
|
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Error: err})
|
||||||
|
return ips, err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ipv4 and ipv6 belong to different subscription groups
|
// ipv4 and ipv6 belong to different subscription groups
|
||||||
@@ -270,11 +287,13 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option I
|
|||||||
}
|
}
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
s.sendQuery(ctx, fqdn, option)
|
s.sendQuery(ctx, fqdn, clientIP, option)
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
ips, err := s.findIPsForDomain(fqdn, option)
|
||||||
if err != errRecordNotFound {
|
if err != errRecordNotFound {
|
||||||
|
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
|
||||||
return ips, err
|
return ips, err
|
||||||
}
|
}
|
||||||
|
|
16
app/dns/options.go
Normal file
16
app/dns/options.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import "github.com/xtls/xray-core/features/dns"
|
||||||
|
|
||||||
|
func isIPQuery(o *dns.IPOption) bool {
|
||||||
|
return o.IPv4Enable || o.IPv6Enable
|
||||||
|
}
|
||||||
|
|
||||||
|
func canQueryOnClient(o *dns.IPOption, c *Client) bool {
|
||||||
|
isIPClient := !(c.Name() == FakeDNSName)
|
||||||
|
return isIPClient && isIPQuery(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isQuery(o *dns.IPOption) bool {
|
||||||
|
return !(o.IPv4Enable || o.IPv6Enable || o.FakeEnable)
|
||||||
|
}
|
@@ -1,448 +0,0 @@
|
|||||||
package dns
|
|
||||||
|
|
||||||
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/router"
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
"github.com/xtls/xray-core/common/session"
|
|
||||||
"github.com/xtls/xray-core/common/strmatcher"
|
|
||||||
"github.com/xtls/xray-core/common/uuid"
|
|
||||||
core "github.com/xtls/xray-core/core"
|
|
||||||
"github.com/xtls/xray-core/features"
|
|
||||||
"github.com/xtls/xray-core/features/dns"
|
|
||||||
"github.com/xtls/xray-core/features/routing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Server is a DNS rely server.
|
|
||||||
type Server struct {
|
|
||||||
sync.Mutex
|
|
||||||
hosts *StaticHosts
|
|
||||||
clientIP net.IP
|
|
||||||
clients []Client // clientIdx -> Client
|
|
||||||
ipIndexMap []*MultiGeoIPMatcher // clientIdx -> *MultiGeoIPMatcher
|
|
||||||
domainRules [][]string // clientIdx -> domainRuleIdx -> DomainRule
|
|
||||||
domainMatcher strmatcher.IndexMatcher
|
|
||||||
matcherInfos []DomainMatcherInfo // matcherIdx -> DomainMatcherInfo
|
|
||||||
tag string
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
|
|
||||||
type DomainMatcherInfo struct {
|
|
||||||
clientIdx uint16
|
|
||||||
domainRuleIdx uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultiGeoIPMatcher for match
|
|
||||||
type MultiGeoIPMatcher struct {
|
|
||||||
matchers []*router.GeoIPMatcher
|
|
||||||
}
|
|
||||||
|
|
||||||
var errExpectedIPNonMatch = errors.New("expectIPs not match")
|
|
||||||
|
|
||||||
// Match check ip match
|
|
||||||
func (c *MultiGeoIPMatcher) Match(ip net.IP) bool {
|
|
||||||
for _, matcher := range c.matchers {
|
|
||||||
if matcher.Match(ip) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasMatcher check has matcher
|
|
||||||
func (c *MultiGeoIPMatcher) HasMatcher() bool {
|
|
||||||
return len(c.matchers) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateRandomTag() string {
|
|
||||||
id := uuid.New()
|
|
||||||
return "xray.system." + id.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new DNS server with given configuration.
|
|
||||||
func New(ctx context.Context, config *Config) (*Server, error) {
|
|
||||||
server := &Server{
|
|
||||||
clients: make([]Client, 0, len(config.NameServers)+len(config.NameServer)),
|
|
||||||
tag: config.Tag,
|
|
||||||
}
|
|
||||||
if server.tag == "" {
|
|
||||||
server.tag = generateRandomTag()
|
|
||||||
}
|
|
||||||
if len(config.ClientIp) > 0 {
|
|
||||||
if len(config.ClientIp) != net.IPv4len && len(config.ClientIp) != net.IPv6len {
|
|
||||||
return nil, newError("unexpected IP length", len(config.ClientIp))
|
|
||||||
}
|
|
||||||
server.clientIP = net.IP(config.ClientIp)
|
|
||||||
}
|
|
||||||
|
|
||||||
hosts, err := NewStaticHosts(config.StaticHosts, config.Hosts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to create hosts").Base(err)
|
|
||||||
}
|
|
||||||
server.hosts = hosts
|
|
||||||
|
|
||||||
addNameServer := func(ns *NameServer) int {
|
|
||||||
endpoint := ns.Address
|
|
||||||
address := endpoint.Address.AsAddress()
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case address.Family().IsDomain() && address.Domain() == "localhost":
|
|
||||||
server.clients = append(server.clients, NewLocalNameServer())
|
|
||||||
// Priotize local domains with specific TLDs or without any dot to local DNS
|
|
||||||
// References:
|
|
||||||
// https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
|
|
||||||
// https://unix.stackexchange.com/questions/92441/whats-the-difference-between-local-home-and-lan
|
|
||||||
localTLDsAndDotlessDomains := []*NameServer_PriorityDomain{
|
|
||||||
{Type: DomainMatchingType_Regex, Domain: "^[^.]+$"}, // This will only match domains without any dot
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "local"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "localdomain"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "localhost"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "lan"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "home.arpa"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "example"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "invalid"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "test"},
|
|
||||||
}
|
|
||||||
ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
|
|
||||||
|
|
||||||
case address.Family().IsDomain() && strings.HasPrefix(address.Domain(), "https+local://"):
|
|
||||||
// URI schemed string treated as domain
|
|
||||||
// DOH Local mode
|
|
||||||
u, err := url.Parse(address.Domain())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(newError("DNS config error").Base(err))
|
|
||||||
}
|
|
||||||
server.clients = append(server.clients, NewDoHLocalNameServer(u, server.clientIP))
|
|
||||||
|
|
||||||
case address.Family().IsDomain() && strings.HasPrefix(address.Domain(), "https://"):
|
|
||||||
// DOH Remote mode
|
|
||||||
u, err := url.Parse(address.Domain())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(newError("DNS config error").Base(err))
|
|
||||||
}
|
|
||||||
idx := len(server.clients)
|
|
||||||
server.clients = append(server.clients, nil)
|
|
||||||
|
|
||||||
// need the core dispatcher, register DOHClient at callback
|
|
||||||
common.Must(core.RequireFeatures(ctx, func(d routing.Dispatcher) {
|
|
||||||
c, err := NewDoHNameServer(u, d, server.clientIP)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(newError("DNS config error").Base(err))
|
|
||||||
}
|
|
||||||
server.clients[idx] = c
|
|
||||||
}))
|
|
||||||
|
|
||||||
default:
|
|
||||||
// UDP classic DNS mode
|
|
||||||
dest := endpoint.AsDestination()
|
|
||||||
if dest.Network == net.Network_Unknown {
|
|
||||||
dest.Network = net.Network_UDP
|
|
||||||
}
|
|
||||||
if dest.Network == net.Network_UDP {
|
|
||||||
idx := len(server.clients)
|
|
||||||
server.clients = append(server.clients, nil)
|
|
||||||
|
|
||||||
common.Must(core.RequireFeatures(ctx, func(d routing.Dispatcher) {
|
|
||||||
server.clients[idx] = NewClassicNameServer(dest, d, server.clientIP)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
server.ipIndexMap = append(server.ipIndexMap, nil)
|
|
||||||
return len(server.clients) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(config.NameServers) > 0 {
|
|
||||||
features.PrintDeprecatedFeatureWarning("simple DNS server")
|
|
||||||
for _, destPB := range config.NameServers {
|
|
||||||
addNameServer(&NameServer{Address: destPB})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(config.NameServer) > 0 {
|
|
||||||
clientIndices := []int{}
|
|
||||||
domainRuleCount := 0
|
|
||||||
for _, ns := range config.NameServer {
|
|
||||||
idx := addNameServer(ns)
|
|
||||||
clientIndices = append(clientIndices, idx)
|
|
||||||
domainRuleCount += len(ns.PrioritizedDomain)
|
|
||||||
}
|
|
||||||
|
|
||||||
domainRules := make([][]string, len(server.clients))
|
|
||||||
domainMatcher := &strmatcher.MatcherGroup{}
|
|
||||||
matcherInfos := make([]DomainMatcherInfo, domainRuleCount+1) // matcher index starts from 1
|
|
||||||
var geoIPMatcherContainer router.GeoIPMatcherContainer
|
|
||||||
for nidx, ns := range config.NameServer {
|
|
||||||
idx := clientIndices[nidx]
|
|
||||||
|
|
||||||
// Establish domain rule matcher
|
|
||||||
rules := []string{}
|
|
||||||
ruleCurr := 0
|
|
||||||
ruleIter := 0
|
|
||||||
for _, domain := range ns.PrioritizedDomain {
|
|
||||||
matcher, err := toStrMatcher(domain.Type, domain.Domain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to create prioritized domain").Base(err).AtWarning()
|
|
||||||
}
|
|
||||||
midx := domainMatcher.Add(matcher)
|
|
||||||
if midx >= uint32(len(matcherInfos)) { // This rarely happens according to current matcher's implementation
|
|
||||||
newError("expanding domain matcher info array to size ", midx, " when adding ", matcher).AtDebug().WriteToLog()
|
|
||||||
matcherInfos = append(matcherInfos, make([]DomainMatcherInfo, midx-uint32(len(matcherInfos))+1)...)
|
|
||||||
}
|
|
||||||
info := &matcherInfos[midx]
|
|
||||||
info.clientIdx = uint16(idx)
|
|
||||||
if ruleCurr < len(ns.OriginalRules) {
|
|
||||||
info.domainRuleIdx = uint16(ruleCurr)
|
|
||||||
rule := ns.OriginalRules[ruleCurr]
|
|
||||||
if ruleCurr >= len(rules) {
|
|
||||||
rules = append(rules, rule.Rule)
|
|
||||||
}
|
|
||||||
ruleIter++
|
|
||||||
if ruleIter >= int(rule.Size) {
|
|
||||||
ruleIter = 0
|
|
||||||
ruleCurr++
|
|
||||||
}
|
|
||||||
} else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests)
|
|
||||||
info.domainRuleIdx = uint16(len(rules))
|
|
||||||
rules = append(rules, matcher.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
domainRules[idx] = rules
|
|
||||||
|
|
||||||
// only add to ipIndexMap if GeoIP is configured
|
|
||||||
if len(ns.Geoip) > 0 {
|
|
||||||
var matchers []*router.GeoIPMatcher
|
|
||||||
for _, geoip := range ns.Geoip {
|
|
||||||
matcher, err := geoIPMatcherContainer.Add(geoip)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to create ip matcher").Base(err).AtWarning()
|
|
||||||
}
|
|
||||||
matchers = append(matchers, matcher)
|
|
||||||
}
|
|
||||||
matcher := &MultiGeoIPMatcher{matchers: matchers}
|
|
||||||
server.ipIndexMap[idx] = matcher
|
|
||||||
}
|
|
||||||
}
|
|
||||||
server.domainRules = domainRules
|
|
||||||
server.domainMatcher = domainMatcher
|
|
||||||
server.matcherInfos = matcherInfos
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(server.clients) == 0 {
|
|
||||||
server.clients = append(server.clients, NewLocalNameServer())
|
|
||||||
server.ipIndexMap = append(server.ipIndexMap, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
return server, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type implements common.HasType.
|
|
||||||
func (*Server) Type() interface{} {
|
|
||||||
return dns.ClientType()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start implements common.Runnable.
|
|
||||||
func (s *Server) Start() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close implements common.Closable.
|
|
||||||
func (s *Server) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) IsOwnLink(ctx context.Context) bool {
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
|
||||||
return inbound != nil && inbound.Tag == s.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match check dns ip match geoip
|
|
||||||
func (s *Server) Match(idx int, client Client, domain string, ips []net.IP) ([]net.IP, error) {
|
|
||||||
var matcher *MultiGeoIPMatcher
|
|
||||||
if idx < len(s.ipIndexMap) {
|
|
||||||
matcher = s.ipIndexMap[idx]
|
|
||||||
}
|
|
||||||
if matcher == nil {
|
|
||||||
return ips, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !matcher.HasMatcher() {
|
|
||||||
newError("domain ", domain, " server has no valid matcher: ", client.Name(), " idx:", idx).AtDebug().WriteToLog()
|
|
||||||
return ips, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
newIps := []net.IP{}
|
|
||||||
for _, ip := range ips {
|
|
||||||
if matcher.Match(ip) {
|
|
||||||
newIps = append(newIps, ip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(newIps) == 0 {
|
|
||||||
return nil, errExpectedIPNonMatch
|
|
||||||
}
|
|
||||||
newError("domain ", domain, " expectIPs ", newIps, " matched at server ", client.Name(), " idx:", idx).AtDebug().WriteToLog()
|
|
||||||
return newIps, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) queryIPTimeout(idx int, client Client, domain string, option IPOption) ([]net.IP, error) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*4)
|
|
||||||
if len(s.tag) > 0 {
|
|
||||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
|
||||||
Tag: s.tag,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ips, err := client.QueryIP(ctx, domain, option)
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return ips, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ips, err = s.Match(idx, client, domain, ips)
|
|
||||||
return ips, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupIP implements dns.Client.
|
|
||||||
func (s *Server) LookupIP(domain string) ([]net.IP, error) {
|
|
||||||
return s.lookupIPInternal(domain, IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupIPv4 implements dns.IPv4Lookup.
|
|
||||||
func (s *Server) LookupIPv4(domain string) ([]net.IP, error) {
|
|
||||||
return s.lookupIPInternal(domain, IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupIPv6 implements dns.IPv6Lookup.
|
|
||||||
func (s *Server) LookupIPv6(domain string) ([]net.IP, error) {
|
|
||||||
return s.lookupIPInternal(domain, IPOption{
|
|
||||||
IPv4Enable: false,
|
|
||||||
IPv6Enable: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) lookupStatic(domain string, option IPOption, depth int32) []net.Address {
|
|
||||||
ips := s.hosts.LookupIP(domain, option)
|
|
||||||
if ips == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if ips[0].Family().IsDomain() && depth < 5 {
|
|
||||||
if newIPs := s.lookupStatic(ips[0].Domain(), option, depth+1); newIPs != nil {
|
|
||||||
return newIPs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ips
|
|
||||||
}
|
|
||||||
|
|
||||||
func toNetIP(ips []net.Address) []net.IP {
|
|
||||||
if len(ips) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
netips := make([]net.IP, 0, len(ips))
|
|
||||||
for _, ip := range ips {
|
|
||||||
netips = append(netips, ip.IP())
|
|
||||||
}
|
|
||||||
return netips
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) lookupIPInternal(domain string, option IPOption) ([]net.IP, error) {
|
|
||||||
if domain == "" {
|
|
||||||
return nil, newError("empty domain name")
|
|
||||||
}
|
|
||||||
domain = strings.ToLower(domain)
|
|
||||||
|
|
||||||
// normalize the FQDN form query
|
|
||||||
if domain[len(domain)-1] == '.' {
|
|
||||||
domain = domain[:len(domain)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
ips := s.lookupStatic(domain, option, 0)
|
|
||||||
if ips != nil && ips[0].Family().IsIP() {
|
|
||||||
newError("returning ", len(ips), " IPs for domain ", domain).WriteToLog()
|
|
||||||
return toNetIP(ips), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if ips != nil && ips[0].Family().IsDomain() {
|
|
||||||
newdomain := ips[0].Domain()
|
|
||||||
newError("domain replaced: ", domain, " -> ", newdomain).WriteToLog()
|
|
||||||
domain = newdomain
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastErr error
|
|
||||||
var matchedClient Client
|
|
||||||
if s.domainMatcher != nil {
|
|
||||||
indices := s.domainMatcher.Match(domain)
|
|
||||||
domainRules := []string{}
|
|
||||||
matchingDNS := []string{}
|
|
||||||
for _, idx := range indices {
|
|
||||||
info := s.matcherInfos[idx]
|
|
||||||
rule := s.domainRules[info.clientIdx][info.domainRuleIdx]
|
|
||||||
domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", rule, info.clientIdx))
|
|
||||||
matchingDNS = append(matchingDNS, s.clients[info.clientIdx].Name())
|
|
||||||
}
|
|
||||||
if len(domainRules) > 0 {
|
|
||||||
newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog()
|
|
||||||
}
|
|
||||||
if len(matchingDNS) > 0 {
|
|
||||||
newError("domain ", domain, " uses following DNS first: ", matchingDNS).AtDebug().WriteToLog()
|
|
||||||
}
|
|
||||||
for _, idx := range indices {
|
|
||||||
clientIdx := int(s.matcherInfos[idx].clientIdx)
|
|
||||||
matchedClient = s.clients[clientIdx]
|
|
||||||
ips, err := s.queryIPTimeout(clientIdx, matchedClient, domain, option)
|
|
||||||
if len(ips) > 0 {
|
|
||||||
return ips, nil
|
|
||||||
}
|
|
||||||
if err == dns.ErrEmptyResponse {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to lookup ip for domain ", domain, " at server ", matchedClient.Name()).Base(err).WriteToLog()
|
|
||||||
lastErr = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx, client := range s.clients {
|
|
||||||
if client == matchedClient {
|
|
||||||
newError("domain ", domain, " at server ", client.Name(), " idx:", idx, " already lookup failed, just ignore").AtDebug().WriteToLog()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ips, err := s.queryIPTimeout(idx, client, domain, option)
|
|
||||||
if len(ips) > 0 {
|
|
||||||
return ips, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog()
|
|
||||||
lastErr = err
|
|
||||||
}
|
|
||||||
if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, newError("returning nil for domain ", domain).Base(lastErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
|
||||||
return New(ctx, config.(*Config))
|
|
||||||
}))
|
|
||||||
}
|
|
@@ -88,6 +88,7 @@ type Config struct {
|
|||||||
ErrorLogPath string `protobuf:"bytes,3,opt,name=error_log_path,json=errorLogPath,proto3" json:"error_log_path,omitempty"`
|
ErrorLogPath string `protobuf:"bytes,3,opt,name=error_log_path,json=errorLogPath,proto3" json:"error_log_path,omitempty"`
|
||||||
AccessLogType LogType `protobuf:"varint,4,opt,name=access_log_type,json=accessLogType,proto3,enum=xray.app.log.LogType" json:"access_log_type,omitempty"`
|
AccessLogType LogType `protobuf:"varint,4,opt,name=access_log_type,json=accessLogType,proto3,enum=xray.app.log.LogType" json:"access_log_type,omitempty"`
|
||||||
AccessLogPath string `protobuf:"bytes,5,opt,name=access_log_path,json=accessLogPath,proto3" json:"access_log_path,omitempty"`
|
AccessLogPath string `protobuf:"bytes,5,opt,name=access_log_path,json=accessLogPath,proto3" json:"access_log_path,omitempty"`
|
||||||
|
EnableDnsLog bool `protobuf:"varint,6,opt,name=enable_dns_log,json=enableDnsLog,proto3" json:"enable_dns_log,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -157,13 +158,20 @@ func (x *Config) GetAccessLogPath() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetEnableDnsLog() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.EnableDnsLog
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
var File_app_log_config_proto protoreflect.FileDescriptor
|
var File_app_log_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_app_log_config_proto_rawDesc = []byte{
|
var file_app_log_config_proto_rawDesc = []byte{
|
||||||
0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||||
0x2e, 0x6c, 0x6f, 0x67, 0x1a, 0x14, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6c, 0x6f, 0x67,
|
0x2e, 0x6c, 0x6f, 0x67, 0x1a, 0x14, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6c, 0x6f, 0x67,
|
||||||
0x2f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x95, 0x02, 0x0a, 0x06, 0x43,
|
0x2f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbb, 0x02, 0x0a, 0x06, 0x43,
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 0x0a, 0x0e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 0x0a, 0x0e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c,
|
||||||
0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e,
|
0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e,
|
||||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x4c, 0x6f, 0x67,
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x4c, 0x6f, 0x67,
|
||||||
@@ -181,15 +189,17 @@ var file_app_log_config_proto_rawDesc = []byte{
|
|||||||
0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x63,
|
0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x63,
|
||||||
0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20,
|
0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61,
|
0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61,
|
||||||
0x74, 0x68, 0x2a, 0x35, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a,
|
0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x6e, 0x73,
|
||||||
0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x73, 0x6f,
|
0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x6e, 0x61, 0x62,
|
||||||
0x6c, 0x65, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x10, 0x02, 0x12, 0x09,
|
0x6c, 0x65, 0x44, 0x6e, 0x73, 0x4c, 0x6f, 0x67, 0x2a, 0x35, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x54,
|
||||||
0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x10, 0x03, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d,
|
0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0b, 0x0a,
|
||||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x50, 0x01, 0x5a,
|
0x07, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x69,
|
||||||
0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73,
|
0x6c, 0x65, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x10, 0x03, 0x42,
|
||||||
0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6c,
|
0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
||||||
0x6f, 0x67, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4c, 0x6f,
|
0x6c, 0x6f, 0x67, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||||
0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
|
||||||
|
0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e,
|
||||||
|
0x41, 0x70, 0x70, 0x2e, 0x4c, 0x6f, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -22,4 +22,5 @@ message Config {
|
|||||||
|
|
||||||
LogType access_log_type = 4;
|
LogType access_log_type = 4;
|
||||||
string access_log_path = 5;
|
string access_log_path = 5;
|
||||||
|
bool enable_dns_log = 6;
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,7 @@ type Instance struct {
|
|||||||
accessLogger log.Handler
|
accessLogger log.Handler
|
||||||
errorLogger log.Handler
|
errorLogger log.Handler
|
||||||
active bool
|
active bool
|
||||||
|
dns bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new log.Instance based on the given config.
|
// New creates a new log.Instance based on the given config.
|
||||||
@@ -24,6 +25,7 @@ func New(ctx context.Context, config *Config) (*Instance, error) {
|
|||||||
g := &Instance{
|
g := &Instance{
|
||||||
config: config,
|
config: config,
|
||||||
active: false,
|
active: false,
|
||||||
|
dns: config.EnableDnsLog,
|
||||||
}
|
}
|
||||||
log.RegisterHandler(g)
|
log.RegisterHandler(g)
|
||||||
|
|
||||||
@@ -103,6 +105,10 @@ func (g *Instance) Handle(msg log.Message) {
|
|||||||
if g.accessLogger != nil {
|
if g.accessLogger != nil {
|
||||||
g.accessLogger.Handle(msg)
|
g.accessLogger.Handle(msg)
|
||||||
}
|
}
|
||||||
|
case *log.DNSLog:
|
||||||
|
if g.dns && g.accessLogger != nil {
|
||||||
|
g.accessLogger.Handle(msg)
|
||||||
|
}
|
||||||
case *log.GeneralMessage:
|
case *log.GeneralMessage:
|
||||||
if g.errorLogger != nil && msg.Severity <= g.config.ErrorLogLevel {
|
if g.errorLogger != nil && msg.Severity <= g.config.ErrorLogLevel {
|
||||||
g.errorLogger.Handle(msg)
|
g.errorLogger.Handle(msg)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.25.0
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.14.0
|
// protoc (unknown)
|
||||||
// source: app/proxyman/config.proto
|
// source: app/proxyman/config.proto
|
||||||
|
|
||||||
package proxyman
|
package proxyman
|
||||||
@@ -239,9 +239,12 @@ type SniffingConfig struct {
|
|||||||
// Whether or not to enable content sniffing on an inbound connection.
|
// Whether or not to enable content sniffing on an inbound connection.
|
||||||
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||||
// Override target destination if sniff'ed protocol is in the given list.
|
// Override target destination if sniff'ed protocol is in the given list.
|
||||||
// Supported values are "http", "tls".
|
// Supported values are "http", "tls", "fakedns".
|
||||||
DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
|
DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
|
||||||
DomainsExcluded []string `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"`
|
DomainsExcluded []string `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"`
|
||||||
|
// Whether should only try to sniff metadata without waiting for client input.
|
||||||
|
// Can be used to support SMTP like protocol where server send the first message.
|
||||||
|
MetadataOnly bool `protobuf:"varint,4,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SniffingConfig) Reset() {
|
func (x *SniffingConfig) Reset() {
|
||||||
@@ -297,6 +300,13 @@ func (x *SniffingConfig) GetDomainsExcluded() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *SniffingConfig) GetMetadataOnly() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.MetadataOnly
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type ReceiverConfig struct {
|
type ReceiverConfig struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -764,7 +774,7 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
|
|||||||
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
|
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||||
0x0a, 0x0a, 0x06, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x52,
|
0x0a, 0x0a, 0x06, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x52,
|
||||||
0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x78, 0x74, 0x65, 0x72,
|
0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x78, 0x74, 0x65, 0x72,
|
||||||
0x6e, 0x61, 0x6c, 0x10, 0x02, 0x22, 0x88, 0x01, 0x0a, 0x0e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69,
|
0x6e, 0x61, 0x6c, 0x10, 0x02, 0x22, 0xad, 0x01, 0x0a, 0x0e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69,
|
||||||
0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62,
|
0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62,
|
||||||
0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
|
0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
|
||||||
0x65, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f,
|
0x65, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f,
|
||||||
@@ -773,86 +783,88 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
|
|||||||
0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
|
0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
|
||||||
0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52,
|
0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||||
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64,
|
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64,
|
||||||
0x22, 0x90, 0x04, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e,
|
0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6f, 0x6e, 0x6c,
|
||||||
0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67,
|
0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
|
0x61, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x90, 0x04, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76,
|
||||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x61,
|
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74,
|
||||||
0x6e, 0x67, 0x65, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x33,
|
0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78,
|
||||||
0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b,
|
0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50,
|
||||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74,
|
0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61,
|
||||||
0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x6c, 0x69, 0x73,
|
0x6e, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 0x20,
|
||||||
0x74, 0x65, 0x6e, 0x12, 0x56, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
|
0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||||
0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78,
|
0x52, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x56, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f,
|
||||||
0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53,
|
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18,
|
||||||
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74,
|
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||||
0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4e, 0x0a, 0x0f, 0x73,
|
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61,
|
||||||
0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04,
|
0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x12, 0x61, 0x6c,
|
||||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53,
|
0x12, 0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
|
||||||
0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74, 0x72,
|
0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||||
0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x72,
|
0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||||
0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f,
|
0x6e, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28,
|
0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
|
||||||
0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e,
|
0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67,
|
||||||
0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a,
|
0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
|
0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f,
|
||||||
0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69,
|
||||||
0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e,
|
0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x76, 0x65,
|
||||||
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x64,
|
0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x78, 0x72,
|
||||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x4e, 0x0a,
|
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e,
|
||||||
0x11, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
|
0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x42, 0x02,
|
||||||
0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
0x18, 0x01, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69,
|
||||||
0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x53, 0x6e, 0x69,
|
0x64, 0x65, 0x12, 0x4e, 0x0a, 0x11, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x5f, 0x73,
|
||||||
0x66, 0x66, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x6e, 0x69,
|
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e,
|
||||||
0x66, 0x66, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x4a, 0x04, 0x08,
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61,
|
||||||
0x06, 0x10, 0x07, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48,
|
0x6e, 0x2e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03,
|
0x52, 0x10, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
|
||||||
0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x4d,
|
0x67, 0x73, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62,
|
||||||
0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
|
0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
|
||||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54,
|
0x74, 0x61, 0x67, 0x12, 0x4d, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f,
|
||||||
0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10, 0x72, 0x65, 0x63,
|
0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20,
|
||||||
0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a,
|
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72,
|
||||||
|
0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||||
|
0x52, 0x10, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
|
||||||
|
0x67, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74,
|
||||||
|
0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
|
||||||
|
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e,
|
||||||
|
0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72,
|
||||||
|
0x6f, 0x78, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f,
|
||||||
|
0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xb0, 0x02,
|
||||||
|
0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d,
|
||||||
|
0x0a, 0x03, 0x76, 0x69, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72,
|
||||||
|
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50,
|
||||||
|
0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x03, 0x76, 0x69, 0x61, 0x12, 0x4e, 0x0a,
|
||||||
|
0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
|
||||||
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72,
|
||||||
|
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||||
|
0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73,
|
||||||
|
0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4b, 0x0a,
|
||||||
0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
|
0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64,
|
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
||||||
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65,
|
0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x70, 0x72, 0x6f,
|
||||||
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75,
|
0x78, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x6d, 0x75,
|
||||||
0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xb0, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x6e,
|
0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
|
||||||
0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69, 0x61,
|
0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
|
0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69,
|
||||||
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d,
|
0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d,
|
||||||
0x61, 0x69, 0x6e, 0x52, 0x03, 0x76, 0x69, 0x61, 0x12, 0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65,
|
0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
|
||||||
0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x22, 0x50, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67,
|
||||||
0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
|
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
||||||
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x65,
|
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||||
0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d,
|
0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18,
|
||||||
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78,
|
0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
|
||||||
0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
0x63, 0x79, 0x2a, 0x23, 0x0a, 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
0x63, 0x6f, 0x6c, 0x73, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07,
|
||||||
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79,
|
0x0a, 0x03, 0x54, 0x4c, 0x53, 0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
||||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x74,
|
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e,
|
||||||
0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c,
|
0x50, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
|
||||||
0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
|
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70,
|
||||||
0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f,
|
0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61,
|
||||||
0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69,
|
0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06,
|
||||||
0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x50, 0x0a, 0x12, 0x4d,
|
|
||||||
0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
|
||||||
0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01,
|
|
||||||
0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63,
|
|
||||||
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
|
|
||||||
0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x2a, 0x23, 0x0a,
|
|
||||||
0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12,
|
|
||||||
0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4c, 0x53,
|
|
||||||
0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
|
||||||
0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26, 0x67,
|
|
||||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78,
|
|
||||||
0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f,
|
|
||||||
0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70,
|
|
||||||
0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -54,9 +54,13 @@ message SniffingConfig {
|
|||||||
bool enabled = 1;
|
bool enabled = 1;
|
||||||
|
|
||||||
// Override target destination if sniff'ed protocol is in the given list.
|
// Override target destination if sniff'ed protocol is in the given list.
|
||||||
// Supported values are "http", "tls".
|
// Supported values are "http", "tls", "fakedns".
|
||||||
repeated string destination_override = 2;
|
repeated string destination_override = 2;
|
||||||
repeated string domains_excluded = 3;
|
repeated string domains_excluded = 3;
|
||||||
|
|
||||||
|
// Whether should only try to sniff metadata without waiting for client input.
|
||||||
|
// Can be used to support SMTP like protocol where server send the first message.
|
||||||
|
bool metadata_only = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ReceiverConfig {
|
message ReceiverConfig {
|
||||||
|
@@ -133,6 +133,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
|
|||||||
address: address,
|
address: address,
|
||||||
port: net.Port(port),
|
port: net.Port(port),
|
||||||
dispatcher: h.mux,
|
dispatcher: h.mux,
|
||||||
|
sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(),
|
||||||
uplinkCounter: uplinkCounter,
|
uplinkCounter: uplinkCounter,
|
||||||
downlinkCounter: downlinkCounter,
|
downlinkCounter: downlinkCounter,
|
||||||
stream: mss,
|
stream: mss,
|
||||||
|
@@ -153,6 +153,7 @@ func (h *DynamicInboundHandler) refresh() error {
|
|||||||
address: address,
|
address: address,
|
||||||
port: port,
|
port: port,
|
||||||
dispatcher: h.mux,
|
dispatcher: h.mux,
|
||||||
|
sniffingConfig: h.receiverConfig.GetEffectiveSniffingSettings(),
|
||||||
uplinkCounter: uplinkCounter,
|
uplinkCounter: uplinkCounter,
|
||||||
downlinkCounter: downlinkCounter,
|
downlinkCounter: downlinkCounter,
|
||||||
stream: h.streamSettings,
|
stream: h.streamSettings,
|
||||||
|
@@ -98,6 +98,7 @@ func (w *tcpWorker) callback(conn internet.Connection) {
|
|||||||
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
|
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
|
||||||
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
||||||
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
|
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
|
||||||
|
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
|
||||||
}
|
}
|
||||||
ctx = session.ContextWithContent(ctx, content)
|
ctx = session.ContextWithContent(ctx, content)
|
||||||
|
|
||||||
@@ -235,6 +236,7 @@ type udpWorker struct {
|
|||||||
tag string
|
tag string
|
||||||
stream *internet.MemoryStreamConfig
|
stream *internet.MemoryStreamConfig
|
||||||
dispatcher routing.Dispatcher
|
dispatcher routing.Dispatcher
|
||||||
|
sniffingConfig *proxyman.SniffingConfig
|
||||||
uplinkCounter stats.Counter
|
uplinkCounter stats.Counter
|
||||||
downlinkCounter stats.Counter
|
downlinkCounter stats.Counter
|
||||||
|
|
||||||
@@ -297,7 +299,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
|||||||
common.Must(w.checker.Start())
|
common.Must(w.checker.Start())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ctx := context.Background()
|
ctx := w.ctx
|
||||||
sid := session.NewID()
|
sid := session.NewID()
|
||||||
ctx = session.ContextWithID(ctx, sid)
|
ctx = session.ContextWithID(ctx, sid)
|
||||||
|
|
||||||
@@ -311,6 +313,13 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
|||||||
Gateway: net.UDPDestination(w.address, w.port),
|
Gateway: net.UDPDestination(w.address, w.port),
|
||||||
Tag: w.tag,
|
Tag: w.tag,
|
||||||
})
|
})
|
||||||
|
content := new(session.Content)
|
||||||
|
if w.sniffingConfig != nil {
|
||||||
|
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
|
||||||
|
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
||||||
|
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
|
||||||
|
}
|
||||||
|
ctx = session.ContextWithContent(ctx, content)
|
||||||
if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil {
|
if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil {
|
||||||
newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
}
|
}
|
||||||
@@ -451,6 +460,7 @@ func (w *dsWorker) callback(conn internet.Connection) {
|
|||||||
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
|
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
|
||||||
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
||||||
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
|
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
|
||||||
|
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
|
||||||
}
|
}
|
||||||
ctx = session.ContextWithContent(ctx, content)
|
ctx = session.ContextWithContent(ctx, content)
|
||||||
|
|
||||||
|
@@ -28,6 +28,12 @@ func (c routingContext) GetTargetPort() net.Port {
|
|||||||
return net.Port(c.RoutingContext.GetTargetPort())
|
return net.Port(c.RoutingContext.GetTargetPort())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSkipDNSResolve is a mock implementation here to match the interface,
|
||||||
|
// SkipDNSResolve is set from dns module, no use if coming from a protobuf object?
|
||||||
|
func (c routingContext) GetSkipDNSResolve() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// AsRoutingContext converts a protobuf RoutingContext into an implementation of routing.Context.
|
// AsRoutingContext converts a protobuf RoutingContext into an implementation of routing.Context.
|
||||||
func AsRoutingContext(r *RoutingContext) routing.Context {
|
func AsRoutingContext(r *RoutingContext) routing.Context {
|
||||||
return routingContext{r}
|
return routingContext{r}
|
||||||
|
@@ -66,6 +66,24 @@ type DomainMatcher struct {
|
|||||||
matchers strmatcher.IndexMatcher
|
matchers strmatcher.IndexMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) {
|
||||||
|
g := strmatcher.NewMphMatcherGroup()
|
||||||
|
for _, d := range domains {
|
||||||
|
matcherType, f := matcherTypeMap[d.Type]
|
||||||
|
if !f {
|
||||||
|
return nil, newError("unsupported domain type", d.Type)
|
||||||
|
}
|
||||||
|
_, err := g.AddPattern(d.Value, matcherType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.Build()
|
||||||
|
return &DomainMatcher{
|
||||||
|
matchers: g,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
|
func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
|
||||||
g := new(strmatcher.MatcherGroup)
|
g := new(strmatcher.MatcherGroup)
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
@@ -82,7 +100,7 @@ func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *DomainMatcher) ApplyDomain(domain string) bool {
|
func (m *DomainMatcher) ApplyDomain(domain string) bool {
|
||||||
return len(m.matchers.Match(domain)) > 0
|
return len(m.matchers.Match(strings.ToLower(domain))) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply implements Condition.
|
// Apply implements Condition.
|
||||||
@@ -91,7 +109,7 @@ func (m *DomainMatcher) Apply(ctx routing.Context) bool {
|
|||||||
if len(domain) == 0 {
|
if len(domain) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return m.ApplyDomain(strings.ToLower(domain))
|
return m.ApplyDomain(domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
type MultiGeoIPMatcher struct {
|
type MultiGeoIPMatcher struct {
|
||||||
|
@@ -359,6 +359,9 @@ func TestChinaSites(t *testing.T) {
|
|||||||
matcher, err := NewDomainMatcher(domains)
|
matcher, err := NewDomainMatcher(domains)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
|
acMatcher, err := NewMphMatcherGroup(domains)
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
type TestCase struct {
|
type TestCase struct {
|
||||||
Domain string
|
Domain string
|
||||||
Output bool
|
Output bool
|
||||||
@@ -387,9 +390,96 @@ func TestChinaSites(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
r := matcher.ApplyDomain(testCase.Domain)
|
r1 := matcher.ApplyDomain(testCase.Domain)
|
||||||
if r != testCase.Output {
|
r2 := acMatcher.ApplyDomain(testCase.Domain)
|
||||||
t.Error("expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
|
if r1 != testCase.Output {
|
||||||
|
t.Error("DomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r1)
|
||||||
|
} else if r2 != testCase.Output {
|
||||||
|
t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMphDomainMatcher(b *testing.B) {
|
||||||
|
domains, err := loadGeoSite("CN")
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
matcher, err := NewMphMatcherGroup(domains)
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
type TestCase struct {
|
||||||
|
Domain string
|
||||||
|
Output bool
|
||||||
|
}
|
||||||
|
testCases := []TestCase{
|
||||||
|
{
|
||||||
|
Domain: "163.com",
|
||||||
|
Output: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domain: "163.com",
|
||||||
|
Output: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domain: "164.com",
|
||||||
|
Output: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domain: "164.com",
|
||||||
|
Output: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 1024; i++ {
|
||||||
|
testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
_ = matcher.ApplyDomain(testCase.Domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDomainMatcher(b *testing.B) {
|
||||||
|
domains, err := loadGeoSite("CN")
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
matcher, err := NewDomainMatcher(domains)
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
type TestCase struct {
|
||||||
|
Domain string
|
||||||
|
Output bool
|
||||||
|
}
|
||||||
|
testCases := []TestCase{
|
||||||
|
{
|
||||||
|
Domain: "163.com",
|
||||||
|
Output: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domain: "163.com",
|
||||||
|
Output: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domain: "164.com",
|
||||||
|
Output: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domain: "164.com",
|
||||||
|
Output: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 1024; i++ {
|
||||||
|
testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
_ = matcher.ApplyDomain(testCase.Domain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -67,11 +67,24 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
|||||||
conds := NewConditionChan()
|
conds := NewConditionChan()
|
||||||
|
|
||||||
if len(rr.Domain) > 0 {
|
if len(rr.Domain) > 0 {
|
||||||
matcher, err := NewDomainMatcher(rr.Domain)
|
switch rr.DomainMatcher {
|
||||||
if err != nil {
|
case "linear":
|
||||||
return nil, newError("failed to build domain condition").Base(err)
|
matcher, err := NewDomainMatcher(rr.Domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to build domain condition").Base(err)
|
||||||
|
}
|
||||||
|
conds.Add(matcher)
|
||||||
|
case "mph", "hybrid":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
matcher, err := NewMphMatcherGroup(rr.Domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to build domain condition with MphDomainMatcher").Base(err)
|
||||||
|
}
|
||||||
|
newError("MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)").AtDebug().WriteToLog()
|
||||||
|
conds.Add(matcher)
|
||||||
}
|
}
|
||||||
conds.Add(matcher)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rr.UserEmail) > 0 {
|
if len(rr.UserEmail) > 0 {
|
||||||
|
@@ -1,13 +1,12 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.25.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v3.14.0
|
// protoc v3.15.8
|
||||||
// source: app/router/config.proto
|
// source: app/router/config.proto
|
||||||
|
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
net "github.com/xtls/xray-core/common/net"
|
net "github.com/xtls/xray-core/common/net"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
@@ -22,10 +21,6 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
|
||||||
// of the legacy proto package is being used.
|
|
||||||
const _ = proto.ProtoPackageIsVersion4
|
|
||||||
|
|
||||||
// Type of domain value.
|
// Type of domain value.
|
||||||
type Domain_Type int32
|
type Domain_Type int32
|
||||||
|
|
||||||
@@ -515,6 +510,7 @@ type RoutingRule struct {
|
|||||||
InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"`
|
InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"`
|
||||||
Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"`
|
Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"`
|
||||||
Attributes string `protobuf:"bytes,15,opt,name=attributes,proto3" json:"attributes,omitempty"`
|
Attributes string `protobuf:"bytes,15,opt,name=attributes,proto3" json:"attributes,omitempty"`
|
||||||
|
DomainMatcher string `protobuf:"bytes,17,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RoutingRule) Reset() {
|
func (x *RoutingRule) Reset() {
|
||||||
@@ -672,6 +668,13 @@ func (x *RoutingRule) GetAttributes() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *RoutingRule) GetDomainMatcher() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.DomainMatcher
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type isRoutingRule_TargetTag interface {
|
type isRoutingRule_TargetTag interface {
|
||||||
isRoutingRule_TargetTag()
|
isRoutingRule_TargetTag()
|
||||||
}
|
}
|
||||||
@@ -946,7 +949,7 @@ var file_app_router_config_proto_rawDesc = []byte{
|
|||||||
0x74, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
|
0x74, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
|
||||||
0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
||||||
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72,
|
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72,
|
||||||
0x79, 0x22, 0x8e, 0x06, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
|
0x79, 0x22, 0xb5, 0x06, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
|
||||||
0x65, 0x12, 0x12, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
|
0x65, 0x12, 0x12, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
|
||||||
0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69,
|
0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69,
|
||||||
0x6e, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c,
|
0x6e, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c,
|
||||||
@@ -994,36 +997,38 @@ var file_app_router_config_proto_rawDesc = []byte{
|
|||||||
0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74,
|
0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
|
0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
|
||||||
0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
|
0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
|
||||||
0x75, 0x74, 0x65, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74,
|
0x75, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6d,
|
||||||
0x61, 0x67, 0x22, 0x4e, 0x0a, 0x0d, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52,
|
0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x6f,
|
||||||
0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x42, 0x0c, 0x0a, 0x0a, 0x74,
|
||||||
0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
|
0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x22, 0x4e, 0x0a, 0x0d, 0x42, 0x61, 0x6c,
|
||||||
0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
|
0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
|
||||||
0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b, 0x0a, 0x11,
|
||||||
0x6f, 0x72, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a,
|
0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f,
|
||||||
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43, 0x6f,
|
||||||
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73,
|
||||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e,
|
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e,
|
||||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30,
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e,
|
||||||
0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78,
|
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72,
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52,
|
0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72,
|
||||||
0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65,
|
0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20,
|
||||||
0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75,
|
0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
|
||||||
0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
|
||||||
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e,
|
0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e,
|
||||||
0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63,
|
0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x22, 0x47, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
|
||||||
0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49,
|
0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52,
|
||||||
0x73, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10,
|
0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x22, 0x47,
|
||||||
0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02,
|
0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||||
0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03,
|
0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x73,
|
||||||
0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f, 0x6e,
|
||||||
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75,
|
0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e, 0x44,
|
||||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
|
0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
||||||
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa,
|
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x50, 0x01,
|
||||||
0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65,
|
0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
|
||||||
0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f,
|
||||||
|
0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70,
|
||||||
|
0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -119,6 +119,8 @@ message RoutingRule {
|
|||||||
repeated string protocol = 9;
|
repeated string protocol = 9;
|
||||||
|
|
||||||
string attributes = 15;
|
string attributes = 15;
|
||||||
|
|
||||||
|
string domain_matcher = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BalancingRule {
|
message BalancingRule {
|
||||||
|
@@ -80,7 +80,13 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
|
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
|
||||||
if r.domainStrategy == Config_IpOnDemand {
|
|
||||||
|
// SkipDNSResolve is set from DNS module.
|
||||||
|
// the DOH remote server maybe a domain name,
|
||||||
|
// this prevents cycle resolving dead loop
|
||||||
|
skipDNSResolve := ctx.GetSkipDNSResolve()
|
||||||
|
|
||||||
|
if r.domainStrategy == Config_IpOnDemand && !skipDNSResolve {
|
||||||
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
|
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +96,7 @@ func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.domainStrategy != Config_IpIfNonMatch || len(ctx.GetTargetDomain()) == 0 {
|
if r.domainStrategy != Config_IpIfNonMatch || len(ctx.GetTargetDomain()) == 0 || skipDNSResolve {
|
||||||
return nil, ctx, common.ErrNoClue
|
return nil, ctx, common.ErrNoClue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,6 +31,22 @@ func New() *Buffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewExisted(b []byte) *Buffer {
|
||||||
|
if cap(b) < Size {
|
||||||
|
panic("Invalid buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
oLen := len(b)
|
||||||
|
if oLen < Size {
|
||||||
|
b = b[:Size]
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Buffer{
|
||||||
|
v: b,
|
||||||
|
end: int32(oLen),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// StackNew creates a new Buffer object on stack.
|
// StackNew creates a new Buffer object on stack.
|
||||||
// This method is for buffers that is released in the same function.
|
// This method is for buffers that is released in the same function.
|
||||||
func StackNew() Buffer {
|
func StackNew() Buffer {
|
||||||
|
85
common/cache/lru.go
vendored
Normal file
85
common/cache/lru.go
vendored
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lru simple, fast lru cache implementation
|
||||||
|
type Lru interface {
|
||||||
|
Get(key interface{}) (value interface{}, ok bool)
|
||||||
|
GetKeyFromValue(value interface{}) (key interface{}, ok bool)
|
||||||
|
PeekKeyFromValue(value interface{}) (key interface{}, ok bool) // Peek means check but NOT bring to top
|
||||||
|
Put(key, value interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type lru struct {
|
||||||
|
capacity int
|
||||||
|
doubleLinkedlist *list.List
|
||||||
|
keyToElement *sync.Map
|
||||||
|
valueToElement *sync.Map
|
||||||
|
mu *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type lruElement struct {
|
||||||
|
key interface{}
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLru init a lru cache
|
||||||
|
func NewLru(cap int) Lru {
|
||||||
|
return lru{
|
||||||
|
capacity: cap,
|
||||||
|
doubleLinkedlist: list.New(),
|
||||||
|
keyToElement: new(sync.Map),
|
||||||
|
valueToElement: new(sync.Map),
|
||||||
|
mu: new(sync.Mutex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l lru) Get(key interface{}) (value interface{}, ok bool) {
|
||||||
|
if v, ok := l.keyToElement.Load(key); ok {
|
||||||
|
element := v.(*list.Element)
|
||||||
|
l.doubleLinkedlist.MoveBefore(element, l.doubleLinkedlist.Front())
|
||||||
|
return element.Value.(lruElement).value, true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l lru) GetKeyFromValue(value interface{}) (key interface{}, ok bool) {
|
||||||
|
if k, ok := l.valueToElement.Load(value); ok {
|
||||||
|
element := k.(*list.Element)
|
||||||
|
l.doubleLinkedlist.MoveBefore(element, l.doubleLinkedlist.Front())
|
||||||
|
return element.Value.(lruElement).key, true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l lru) PeekKeyFromValue(value interface{}) (key interface{}, ok bool) {
|
||||||
|
if k, ok := l.valueToElement.Load(value); ok {
|
||||||
|
element := k.(*list.Element)
|
||||||
|
return element.Value.(lruElement).key, true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l lru) Put(key, value interface{}) {
|
||||||
|
e := lruElement{key, value}
|
||||||
|
if v, ok := l.keyToElement.Load(key); ok {
|
||||||
|
element := v.(*list.Element)
|
||||||
|
element.Value = e
|
||||||
|
l.doubleLinkedlist.MoveBefore(element, l.doubleLinkedlist.Front())
|
||||||
|
} else {
|
||||||
|
l.mu.Lock()
|
||||||
|
element := l.doubleLinkedlist.PushFront(e)
|
||||||
|
l.keyToElement.Store(key, element)
|
||||||
|
l.valueToElement.Store(value, element)
|
||||||
|
if l.doubleLinkedlist.Len() > l.capacity {
|
||||||
|
toBeRemove := l.doubleLinkedlist.Back()
|
||||||
|
l.doubleLinkedlist.Remove(toBeRemove)
|
||||||
|
l.keyToElement.Delete(toBeRemove.Value.(lruElement).key)
|
||||||
|
l.valueToElement.Delete(toBeRemove.Value.(lruElement).value)
|
||||||
|
}
|
||||||
|
l.mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
86
common/cache/lru_test.go
vendored
Normal file
86
common/cache/lru_test.go
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package cache_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/xtls/xray-core/common/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLruReplaceValue(t *testing.T) {
|
||||||
|
lru := NewLru(2)
|
||||||
|
lru.Put(2, 6)
|
||||||
|
lru.Put(1, 5)
|
||||||
|
lru.Put(1, 2)
|
||||||
|
v, _ := lru.Get(1)
|
||||||
|
if v != 2 {
|
||||||
|
t.Error("should get 2", v)
|
||||||
|
}
|
||||||
|
v, _ = lru.Get(2)
|
||||||
|
if v != 6 {
|
||||||
|
t.Error("should get 6", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLruRemoveOld(t *testing.T) {
|
||||||
|
lru := NewLru(2)
|
||||||
|
v, ok := lru.Get(2)
|
||||||
|
if ok {
|
||||||
|
t.Error("should get nil", v)
|
||||||
|
}
|
||||||
|
lru.Put(1, 1)
|
||||||
|
lru.Put(2, 2)
|
||||||
|
v, _ = lru.Get(1)
|
||||||
|
if v != 1 {
|
||||||
|
t.Error("should get 1", v)
|
||||||
|
}
|
||||||
|
lru.Put(3, 3)
|
||||||
|
v, ok = lru.Get(2)
|
||||||
|
if ok {
|
||||||
|
t.Error("should get nil", v)
|
||||||
|
}
|
||||||
|
lru.Put(4, 4)
|
||||||
|
v, ok = lru.Get(1)
|
||||||
|
if ok {
|
||||||
|
t.Error("should get nil", v)
|
||||||
|
}
|
||||||
|
v, _ = lru.Get(3)
|
||||||
|
if v != 3 {
|
||||||
|
t.Error("should get 3", v)
|
||||||
|
}
|
||||||
|
v, _ = lru.Get(4)
|
||||||
|
if v != 4 {
|
||||||
|
t.Error("should get 4", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetKeyFromValue(t *testing.T) {
|
||||||
|
lru := NewLru(2)
|
||||||
|
lru.Put(3, 3)
|
||||||
|
lru.Put(2, 2)
|
||||||
|
lru.GetKeyFromValue(3)
|
||||||
|
lru.Put(1, 1)
|
||||||
|
v, ok := lru.GetKeyFromValue(2)
|
||||||
|
if ok {
|
||||||
|
t.Error("should get nil", v)
|
||||||
|
}
|
||||||
|
v, _ = lru.GetKeyFromValue(3)
|
||||||
|
if v != 3 {
|
||||||
|
t.Error("should get 3", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeekKeyFromValue(t *testing.T) {
|
||||||
|
lru := NewLru(2)
|
||||||
|
lru.Put(3, 3)
|
||||||
|
lru.Put(2, 2)
|
||||||
|
lru.PeekKeyFromValue(3)
|
||||||
|
lru.Put(1, 1)
|
||||||
|
v, ok := lru.PeekKeyFromValue(3)
|
||||||
|
if ok {
|
||||||
|
t.Error("should get nil", v)
|
||||||
|
}
|
||||||
|
v, _ = lru.PeekKeyFromValue(2)
|
||||||
|
if v != 2 {
|
||||||
|
t.Error("should get 2", v)
|
||||||
|
}
|
||||||
|
}
|
@@ -40,10 +40,8 @@ func (err *Error) pkgPath() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
path := reflect.TypeOf(err.pathObj).PkgPath()
|
path := reflect.TypeOf(err.pathObj).PkgPath()
|
||||||
for i := 0; i < len(path); i++ {
|
if len(path) >= trim {
|
||||||
if path[i] == '/' {
|
return path[trim:]
|
||||||
return path[trim:]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
59
common/log/dns.go
Normal file
59
common/log/dns.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DNSLog struct {
|
||||||
|
Server string
|
||||||
|
Domain string
|
||||||
|
Result []net.IP
|
||||||
|
Status dnsStatus
|
||||||
|
Elapsed time.Duration
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *DNSLog) String() string {
|
||||||
|
builder := &strings.Builder{}
|
||||||
|
|
||||||
|
// Server got answer: domain -> [ip1, ip2] 23ms
|
||||||
|
builder.WriteString(l.Server)
|
||||||
|
builder.WriteString(" ")
|
||||||
|
builder.WriteString(string(l.Status))
|
||||||
|
builder.WriteString(" ")
|
||||||
|
builder.WriteString(l.Domain)
|
||||||
|
builder.WriteString(" -> [")
|
||||||
|
builder.WriteString(joinNetIP(l.Result))
|
||||||
|
builder.WriteString("]")
|
||||||
|
|
||||||
|
if l.Elapsed > 0 {
|
||||||
|
builder.WriteString(" ")
|
||||||
|
builder.WriteString(l.Elapsed.String())
|
||||||
|
}
|
||||||
|
if l.Error != nil {
|
||||||
|
builder.WriteString(" <")
|
||||||
|
builder.WriteString(l.Error.Error())
|
||||||
|
builder.WriteString(">")
|
||||||
|
}
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type dnsStatus string
|
||||||
|
|
||||||
|
var (
|
||||||
|
DNSQueried = dnsStatus("got answer:")
|
||||||
|
DNSCacheHit = dnsStatus("cache HIT:")
|
||||||
|
)
|
||||||
|
|
||||||
|
func joinNetIP(ips []net.IP) string {
|
||||||
|
if len(ips) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
sips := make([]string, 0, len(ips))
|
||||||
|
for _, ip := range ips {
|
||||||
|
sips = append(sips, ip.String())
|
||||||
|
}
|
||||||
|
return strings.Join(sips, ", ")
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.25.0
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.14.0
|
// protoc v3.15.4
|
||||||
// source: common/protocol/headers.proto
|
// source: common/protocol/headers.proto
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
@@ -34,6 +34,7 @@ const (
|
|||||||
SecurityType_AES128_GCM SecurityType = 3
|
SecurityType_AES128_GCM SecurityType = 3
|
||||||
SecurityType_CHACHA20_POLY1305 SecurityType = 4
|
SecurityType_CHACHA20_POLY1305 SecurityType = 4
|
||||||
SecurityType_NONE SecurityType = 5
|
SecurityType_NONE SecurityType = 5
|
||||||
|
SecurityType_ZERO SecurityType = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enum value maps for SecurityType.
|
// Enum value maps for SecurityType.
|
||||||
@@ -45,6 +46,7 @@ var (
|
|||||||
3: "AES128_GCM",
|
3: "AES128_GCM",
|
||||||
4: "CHACHA20_POLY1305",
|
4: "CHACHA20_POLY1305",
|
||||||
5: "NONE",
|
5: "NONE",
|
||||||
|
6: "ZERO",
|
||||||
}
|
}
|
||||||
SecurityType_value = map[string]int32{
|
SecurityType_value = map[string]int32{
|
||||||
"UNKNOWN": 0,
|
"UNKNOWN": 0,
|
||||||
@@ -53,6 +55,7 @@ var (
|
|||||||
"AES128_GCM": 3,
|
"AES128_GCM": 3,
|
||||||
"CHACHA20_POLY1305": 4,
|
"CHACHA20_POLY1305": 4,
|
||||||
"NONE": 5,
|
"NONE": 5,
|
||||||
|
"ZERO": 6,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -141,19 +144,20 @@ var file_common_protocol_headers_proto_rawDesc = []byte{
|
|||||||
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63,
|
0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63,
|
||||||
0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a,
|
0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a,
|
||||||
0x62, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12,
|
0x6c, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||||
0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
|
0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
|
||||||
0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x4f,
|
0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x4f,
|
||||||
0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x45, 0x53, 0x31, 0x32, 0x38, 0x5f, 0x47, 0x43, 0x4d,
|
0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x45, 0x53, 0x31, 0x32, 0x38, 0x5f, 0x47, 0x43, 0x4d,
|
||||||
0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x50,
|
0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x50,
|
||||||
0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e,
|
0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e,
|
||||||
0x45, 0x10, 0x05, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x5a, 0x45, 0x52, 0x4f, 0x10, 0x06, 0x42, 0x5e, 0x0a,
|
||||||
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x50,
|
0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||||
0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74,
|
||||||
0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d,
|
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
||||||
0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0xaa, 0x02, 0x14, 0x58,
|
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72,
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f,
|
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f,
|
||||||
0x63, 0x6f, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x62, 0x06, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -13,6 +13,7 @@ enum SecurityType {
|
|||||||
AES128_GCM = 3;
|
AES128_GCM = 3;
|
||||||
CHACHA20_POLY1305 = 4;
|
CHACHA20_POLY1305 = 4;
|
||||||
NONE = 5;
|
NONE = 5;
|
||||||
|
ZERO = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SecurityConfig {
|
message SecurityConfig {
|
||||||
|
@@ -102,7 +102,7 @@ func ReadClientHello(data []byte, h *SniffHeader) error {
|
|||||||
return errNotClientHello
|
return errNotClientHello
|
||||||
}
|
}
|
||||||
if nameType == 0 {
|
if nameType == 0 {
|
||||||
serverName := strings.ToLower(string(d[:nameLen]))
|
serverName := string(d[:nameLen])
|
||||||
// An SNI value may not include a
|
// An SNI value may not include a
|
||||||
// trailing dot. See
|
// trailing dot. See
|
||||||
// https://tools.ietf.org/html/rfc6066#section-3.
|
// https://tools.ietf.org/html/rfc6066#section-3.
|
||||||
|
@@ -63,6 +63,7 @@ type SniffingRequest struct {
|
|||||||
ExcludeForDomain []string
|
ExcludeForDomain []string
|
||||||
OverrideDestinationForProtocol []string
|
OverrideDestinationForProtocol []string
|
||||||
Enabled bool
|
Enabled bool
|
||||||
|
MetadataOnly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Content is the metadata of the connection content.
|
// Content is the metadata of the connection content.
|
||||||
@@ -74,7 +75,7 @@ type Content struct {
|
|||||||
|
|
||||||
Attributes map[string]string
|
Attributes map[string]string
|
||||||
|
|
||||||
SkipRoutePick bool
|
SkipDNSResolve bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sockopt is the settings for socket connection.
|
// Sockopt is the settings for socket connection.
|
||||||
|
243
common/strmatcher/ac_automaton_matcher.go
Normal file
243
common/strmatcher/ac_automaton_matcher.go
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
package strmatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
const validCharCount = 53
|
||||||
|
|
||||||
|
type MatchType struct {
|
||||||
|
matchType Type
|
||||||
|
exist bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TrieEdge bool = true
|
||||||
|
FailEdge bool = false
|
||||||
|
)
|
||||||
|
|
||||||
|
type Edge struct {
|
||||||
|
edgeType bool
|
||||||
|
nextNode int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ACAutomaton struct {
|
||||||
|
trie [][validCharCount]Edge
|
||||||
|
fail []int
|
||||||
|
exists []MatchType
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNode() [validCharCount]Edge {
|
||||||
|
var s [validCharCount]Edge
|
||||||
|
for i := range s {
|
||||||
|
s[i] = Edge{
|
||||||
|
edgeType: FailEdge,
|
||||||
|
nextNode: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var char2Index = []int{
|
||||||
|
'A': 0,
|
||||||
|
'a': 0,
|
||||||
|
'B': 1,
|
||||||
|
'b': 1,
|
||||||
|
'C': 2,
|
||||||
|
'c': 2,
|
||||||
|
'D': 3,
|
||||||
|
'd': 3,
|
||||||
|
'E': 4,
|
||||||
|
'e': 4,
|
||||||
|
'F': 5,
|
||||||
|
'f': 5,
|
||||||
|
'G': 6,
|
||||||
|
'g': 6,
|
||||||
|
'H': 7,
|
||||||
|
'h': 7,
|
||||||
|
'I': 8,
|
||||||
|
'i': 8,
|
||||||
|
'J': 9,
|
||||||
|
'j': 9,
|
||||||
|
'K': 10,
|
||||||
|
'k': 10,
|
||||||
|
'L': 11,
|
||||||
|
'l': 11,
|
||||||
|
'M': 12,
|
||||||
|
'm': 12,
|
||||||
|
'N': 13,
|
||||||
|
'n': 13,
|
||||||
|
'O': 14,
|
||||||
|
'o': 14,
|
||||||
|
'P': 15,
|
||||||
|
'p': 15,
|
||||||
|
'Q': 16,
|
||||||
|
'q': 16,
|
||||||
|
'R': 17,
|
||||||
|
'r': 17,
|
||||||
|
'S': 18,
|
||||||
|
's': 18,
|
||||||
|
'T': 19,
|
||||||
|
't': 19,
|
||||||
|
'U': 20,
|
||||||
|
'u': 20,
|
||||||
|
'V': 21,
|
||||||
|
'v': 21,
|
||||||
|
'W': 22,
|
||||||
|
'w': 22,
|
||||||
|
'X': 23,
|
||||||
|
'x': 23,
|
||||||
|
'Y': 24,
|
||||||
|
'y': 24,
|
||||||
|
'Z': 25,
|
||||||
|
'z': 25,
|
||||||
|
'!': 26,
|
||||||
|
'$': 27,
|
||||||
|
'&': 28,
|
||||||
|
'\'': 29,
|
||||||
|
'(': 30,
|
||||||
|
')': 31,
|
||||||
|
'*': 32,
|
||||||
|
'+': 33,
|
||||||
|
',': 34,
|
||||||
|
';': 35,
|
||||||
|
'=': 36,
|
||||||
|
':': 37,
|
||||||
|
'%': 38,
|
||||||
|
'-': 39,
|
||||||
|
'.': 40,
|
||||||
|
'_': 41,
|
||||||
|
'~': 42,
|
||||||
|
'0': 43,
|
||||||
|
'1': 44,
|
||||||
|
'2': 45,
|
||||||
|
'3': 46,
|
||||||
|
'4': 47,
|
||||||
|
'5': 48,
|
||||||
|
'6': 49,
|
||||||
|
'7': 50,
|
||||||
|
'8': 51,
|
||||||
|
'9': 52,
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewACAutomaton() *ACAutomaton {
|
||||||
|
var ac = new(ACAutomaton)
|
||||||
|
ac.trie = append(ac.trie, newNode())
|
||||||
|
ac.fail = append(ac.fail, 0)
|
||||||
|
ac.exists = append(ac.exists, MatchType{
|
||||||
|
matchType: Full,
|
||||||
|
exist: false,
|
||||||
|
})
|
||||||
|
return ac
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *ACAutomaton) Add(domain string, t Type) {
|
||||||
|
var node = 0
|
||||||
|
for i := len(domain) - 1; i >= 0; i-- {
|
||||||
|
var idx = char2Index[domain[i]]
|
||||||
|
if ac.trie[node][idx].nextNode == 0 {
|
||||||
|
ac.count++
|
||||||
|
if len(ac.trie) < ac.count+1 {
|
||||||
|
ac.trie = append(ac.trie, newNode())
|
||||||
|
ac.fail = append(ac.fail, 0)
|
||||||
|
ac.exists = append(ac.exists, MatchType{
|
||||||
|
matchType: Full,
|
||||||
|
exist: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ac.trie[node][idx] = Edge{
|
||||||
|
edgeType: TrieEdge,
|
||||||
|
nextNode: ac.count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = ac.trie[node][idx].nextNode
|
||||||
|
}
|
||||||
|
ac.exists[node] = MatchType{
|
||||||
|
matchType: t,
|
||||||
|
exist: true,
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case Domain:
|
||||||
|
ac.exists[node] = MatchType{
|
||||||
|
matchType: Full,
|
||||||
|
exist: true,
|
||||||
|
}
|
||||||
|
var idx = char2Index['.']
|
||||||
|
if ac.trie[node][idx].nextNode == 0 {
|
||||||
|
ac.count++
|
||||||
|
if len(ac.trie) < ac.count+1 {
|
||||||
|
ac.trie = append(ac.trie, newNode())
|
||||||
|
ac.fail = append(ac.fail, 0)
|
||||||
|
ac.exists = append(ac.exists, MatchType{
|
||||||
|
matchType: Full,
|
||||||
|
exist: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ac.trie[node][idx] = Edge{
|
||||||
|
edgeType: TrieEdge,
|
||||||
|
nextNode: ac.count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = ac.trie[node][idx].nextNode
|
||||||
|
ac.exists[node] = MatchType{
|
||||||
|
matchType: t,
|
||||||
|
exist: true,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *ACAutomaton) Build() {
|
||||||
|
var queue = list.New()
|
||||||
|
for i := 0; i < validCharCount; i++ {
|
||||||
|
if ac.trie[0][i].nextNode != 0 {
|
||||||
|
queue.PushBack(ac.trie[0][i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
var front = queue.Front()
|
||||||
|
if front == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
var node = front.Value.(Edge).nextNode
|
||||||
|
queue.Remove(front)
|
||||||
|
for i := 0; i < validCharCount; i++ {
|
||||||
|
if ac.trie[node][i].nextNode != 0 {
|
||||||
|
ac.fail[ac.trie[node][i].nextNode] = ac.trie[ac.fail[node]][i].nextNode
|
||||||
|
queue.PushBack(ac.trie[node][i])
|
||||||
|
} else {
|
||||||
|
ac.trie[node][i] = Edge{
|
||||||
|
edgeType: FailEdge,
|
||||||
|
nextNode: ac.trie[ac.fail[node]][i].nextNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *ACAutomaton) Match(s string) bool {
|
||||||
|
var node = 0
|
||||||
|
var fullMatch = true
|
||||||
|
// 1. the match string is all through trie edge. FULL MATCH or DOMAIN
|
||||||
|
// 2. the match string is through a fail edge. NOT FULL MATCH
|
||||||
|
// 2.1 Through a fail edge, but there exists a valid node. SUBSTR
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
var idx = char2Index[s[i]]
|
||||||
|
fullMatch = fullMatch && ac.trie[node][idx].edgeType
|
||||||
|
node = ac.trie[node][idx].nextNode
|
||||||
|
switch ac.exists[node].matchType {
|
||||||
|
case Substr:
|
||||||
|
return true
|
||||||
|
case Domain:
|
||||||
|
if fullMatch {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fullMatch && ac.exists[node].exist
|
||||||
|
}
|
@@ -8,6 +8,19 @@ import (
|
|||||||
. "github.com/xtls/xray-core/common/strmatcher"
|
. "github.com/xtls/xray-core/common/strmatcher"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func BenchmarkACAutomaton(b *testing.B) {
|
||||||
|
ac := NewACAutomaton()
|
||||||
|
for i := 1; i <= 1024; i++ {
|
||||||
|
ac.Add(strconv.Itoa(i)+".v2ray.com", Domain)
|
||||||
|
}
|
||||||
|
ac.Build()
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = ac.Match("0.v2ray.com")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkDomainMatcherGroup(b *testing.B) {
|
func BenchmarkDomainMatcherGroup(b *testing.B) {
|
||||||
g := new(DomainMatcherGroup)
|
g := new(DomainMatcherGroup)
|
||||||
|
|
||||||
|
301
common/strmatcher/mph_matcher.go
Normal file
301
common/strmatcher/mph_matcher.go
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
package strmatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/bits"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrimeRK is the prime base used in Rabin-Karp algorithm.
|
||||||
|
const PrimeRK = 16777619
|
||||||
|
|
||||||
|
// calculate the rolling murmurHash of given string
|
||||||
|
func RollingHash(s string) uint32 {
|
||||||
|
h := uint32(0)
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
h = h*PrimeRK + uint32(s[i])
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// A MphMatcherGroup is divided into three parts:
|
||||||
|
// 1. `full` and `domain` patterns are matched by Rabin-Karp algorithm and minimal perfect hash table;
|
||||||
|
// 2. `substr` patterns are matched by ac automaton;
|
||||||
|
// 3. `regex` patterns are matched with the regex library.
|
||||||
|
type MphMatcherGroup struct {
|
||||||
|
ac *ACAutomaton
|
||||||
|
otherMatchers []matcherEntry
|
||||||
|
rules []string
|
||||||
|
level0 []uint32
|
||||||
|
level0Mask int
|
||||||
|
level1 []uint32
|
||||||
|
level1Mask int
|
||||||
|
count uint32
|
||||||
|
ruleMap *map[string]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *MphMatcherGroup) AddFullOrDomainPattern(pattern string, t Type) {
|
||||||
|
h := RollingHash(pattern)
|
||||||
|
switch t {
|
||||||
|
case Domain:
|
||||||
|
(*g.ruleMap)["."+pattern] = h*PrimeRK + uint32('.')
|
||||||
|
fallthrough
|
||||||
|
case Full:
|
||||||
|
(*g.ruleMap)[pattern] = h
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMphMatcherGroup() *MphMatcherGroup {
|
||||||
|
return &MphMatcherGroup{
|
||||||
|
ac: nil,
|
||||||
|
otherMatchers: nil,
|
||||||
|
rules: nil,
|
||||||
|
level0: nil,
|
||||||
|
level0Mask: 0,
|
||||||
|
level1: nil,
|
||||||
|
level1Mask: 0,
|
||||||
|
count: 1,
|
||||||
|
ruleMap: &map[string]uint32{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPattern adds a pattern to MphMatcherGroup
|
||||||
|
func (g *MphMatcherGroup) AddPattern(pattern string, t Type) (uint32, error) {
|
||||||
|
switch t {
|
||||||
|
case Substr:
|
||||||
|
if g.ac == nil {
|
||||||
|
g.ac = NewACAutomaton()
|
||||||
|
}
|
||||||
|
g.ac.Add(pattern, t)
|
||||||
|
case Full, Domain:
|
||||||
|
pattern = strings.ToLower(pattern)
|
||||||
|
g.AddFullOrDomainPattern(pattern, t)
|
||||||
|
case Regex:
|
||||||
|
r, err := regexp.Compile(pattern)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
g.otherMatchers = append(g.otherMatchers, matcherEntry{
|
||||||
|
m: ®exMatcher{pattern: r},
|
||||||
|
id: g.count,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
panic("Unknown type")
|
||||||
|
}
|
||||||
|
return g.count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build builds a minimal perfect hash table and ac automaton from insert rules
|
||||||
|
func (g *MphMatcherGroup) Build() {
|
||||||
|
if g.ac != nil {
|
||||||
|
g.ac.Build()
|
||||||
|
}
|
||||||
|
keyLen := len(*g.ruleMap)
|
||||||
|
if keyLen == 0 {
|
||||||
|
keyLen = 1
|
||||||
|
(*g.ruleMap)["empty___"] = RollingHash("empty___")
|
||||||
|
}
|
||||||
|
g.level0 = make([]uint32, nextPow2(keyLen/4))
|
||||||
|
g.level0Mask = len(g.level0) - 1
|
||||||
|
g.level1 = make([]uint32, nextPow2(keyLen))
|
||||||
|
g.level1Mask = len(g.level1) - 1
|
||||||
|
var sparseBuckets = make([][]int, len(g.level0))
|
||||||
|
var ruleIdx int
|
||||||
|
for rule, hash := range *g.ruleMap {
|
||||||
|
n := int(hash) & g.level0Mask
|
||||||
|
g.rules = append(g.rules, rule)
|
||||||
|
sparseBuckets[n] = append(sparseBuckets[n], ruleIdx)
|
||||||
|
ruleIdx++
|
||||||
|
}
|
||||||
|
g.ruleMap = nil
|
||||||
|
var buckets []indexBucket
|
||||||
|
for n, vals := range sparseBuckets {
|
||||||
|
if len(vals) > 0 {
|
||||||
|
buckets = append(buckets, indexBucket{n, vals})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(bySize(buckets))
|
||||||
|
|
||||||
|
occ := make([]bool, len(g.level1))
|
||||||
|
var tmpOcc []int
|
||||||
|
for _, bucket := range buckets {
|
||||||
|
var seed = uint32(0)
|
||||||
|
for {
|
||||||
|
findSeed := true
|
||||||
|
tmpOcc = tmpOcc[:0]
|
||||||
|
for _, i := range bucket.vals {
|
||||||
|
n := int(strhashFallback(unsafe.Pointer(&g.rules[i]), uintptr(seed))) & g.level1Mask
|
||||||
|
if occ[n] {
|
||||||
|
for _, n := range tmpOcc {
|
||||||
|
occ[n] = false
|
||||||
|
}
|
||||||
|
seed++
|
||||||
|
findSeed = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
occ[n] = true
|
||||||
|
tmpOcc = append(tmpOcc, n)
|
||||||
|
g.level1[n] = uint32(i)
|
||||||
|
}
|
||||||
|
if findSeed {
|
||||||
|
g.level0[bucket.n] = seed
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextPow2(v int) int {
|
||||||
|
if v <= 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
const MaxUInt = ^uint(0)
|
||||||
|
n := (MaxUInt >> bits.LeadingZeros(uint(v))) + 1
|
||||||
|
return int(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup searches for s in t and returns its index and whether it was found.
|
||||||
|
func (g *MphMatcherGroup) Lookup(h uint32, s string) bool {
|
||||||
|
i0 := int(h) & g.level0Mask
|
||||||
|
seed := g.level0[i0]
|
||||||
|
i1 := int(strhashFallback(unsafe.Pointer(&s), uintptr(seed))) & g.level1Mask
|
||||||
|
n := g.level1[i1]
|
||||||
|
return s == g.rules[int(n)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements IndexMatcher.Match.
|
||||||
|
func (g *MphMatcherGroup) Match(pattern string) []uint32 {
|
||||||
|
result := []uint32{}
|
||||||
|
hash := uint32(0)
|
||||||
|
for i := len(pattern) - 1; i >= 0; i-- {
|
||||||
|
hash = hash*PrimeRK + uint32(pattern[i])
|
||||||
|
if pattern[i] == '.' {
|
||||||
|
if g.Lookup(hash, pattern[i:]) {
|
||||||
|
result = append(result, 1)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if g.Lookup(hash, pattern) {
|
||||||
|
result = append(result, 1)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if g.ac != nil && g.ac.Match(pattern) {
|
||||||
|
result = append(result, 1)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
for _, e := range g.otherMatchers {
|
||||||
|
if e.m.Match(pattern) {
|
||||||
|
result = append(result, e.id)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type indexBucket struct {
|
||||||
|
n int
|
||||||
|
vals []int
|
||||||
|
}
|
||||||
|
|
||||||
|
type bySize []indexBucket
|
||||||
|
|
||||||
|
func (s bySize) Len() int { return len(s) }
|
||||||
|
func (s bySize) Less(i, j int) bool { return len(s[i].vals) > len(s[j].vals) }
|
||||||
|
func (s bySize) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
|
||||||
|
type stringStruct struct {
|
||||||
|
str unsafe.Pointer
|
||||||
|
len int
|
||||||
|
}
|
||||||
|
|
||||||
|
func strhashFallback(a unsafe.Pointer, h uintptr) uintptr {
|
||||||
|
x := (*stringStruct)(a)
|
||||||
|
return memhashFallback(x.str, h, uintptr(x.len))
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Constants for multiplication: four random odd 64-bit numbers.
|
||||||
|
m1 = 16877499708836156737
|
||||||
|
m2 = 2820277070424839065
|
||||||
|
m3 = 9497967016996688599
|
||||||
|
m4 = 15839092249703872147
|
||||||
|
)
|
||||||
|
|
||||||
|
var hashkey = [4]uintptr{1, 1, 1, 1}
|
||||||
|
|
||||||
|
func memhashFallback(p unsafe.Pointer, seed, s uintptr) uintptr {
|
||||||
|
h := uint64(seed + s*hashkey[0])
|
||||||
|
tail:
|
||||||
|
switch {
|
||||||
|
case s == 0:
|
||||||
|
case s < 4:
|
||||||
|
h ^= uint64(*(*byte)(p))
|
||||||
|
h ^= uint64(*(*byte)(add(p, s>>1))) << 8
|
||||||
|
h ^= uint64(*(*byte)(add(p, s-1))) << 16
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
case s <= 8:
|
||||||
|
h ^= uint64(readUnaligned32(p))
|
||||||
|
h ^= uint64(readUnaligned32(add(p, s-4))) << 32
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
case s <= 16:
|
||||||
|
h ^= readUnaligned64(p)
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
h ^= readUnaligned64(add(p, s-8))
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
case s <= 32:
|
||||||
|
h ^= readUnaligned64(p)
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
h ^= readUnaligned64(add(p, 8))
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
h ^= readUnaligned64(add(p, s-16))
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
h ^= readUnaligned64(add(p, s-8))
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
default:
|
||||||
|
v1 := h
|
||||||
|
v2 := uint64(seed * hashkey[1])
|
||||||
|
v3 := uint64(seed * hashkey[2])
|
||||||
|
v4 := uint64(seed * hashkey[3])
|
||||||
|
for s >= 32 {
|
||||||
|
v1 ^= readUnaligned64(p)
|
||||||
|
v1 = rotl31(v1*m1) * m2
|
||||||
|
p = add(p, 8)
|
||||||
|
v2 ^= readUnaligned64(p)
|
||||||
|
v2 = rotl31(v2*m2) * m3
|
||||||
|
p = add(p, 8)
|
||||||
|
v3 ^= readUnaligned64(p)
|
||||||
|
v3 = rotl31(v3*m3) * m4
|
||||||
|
p = add(p, 8)
|
||||||
|
v4 ^= readUnaligned64(p)
|
||||||
|
v4 = rotl31(v4*m4) * m1
|
||||||
|
p = add(p, 8)
|
||||||
|
s -= 32
|
||||||
|
}
|
||||||
|
h = v1 ^ v2 ^ v3 ^ v4
|
||||||
|
goto tail
|
||||||
|
}
|
||||||
|
|
||||||
|
h ^= h >> 29
|
||||||
|
h *= m3
|
||||||
|
h ^= h >> 32
|
||||||
|
return uintptr(h)
|
||||||
|
}
|
||||||
|
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(uintptr(p) + x)
|
||||||
|
}
|
||||||
|
func readUnaligned32(p unsafe.Pointer) uint32 {
|
||||||
|
q := (*[4]byte)(p)
|
||||||
|
return uint32(q[0]) | uint32(q[1])<<8 | uint32(q[2])<<16 | uint32(q[3])<<24
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotl31(x uint64) uint64 {
|
||||||
|
return (x << 31) | (x >> (64 - 31))
|
||||||
|
}
|
||||||
|
func readUnaligned64(p unsafe.Pointer) uint64 {
|
||||||
|
q := (*[8]byte)(p)
|
||||||
|
return uint64(q[0]) | uint64(q[1])<<8 | uint64(q[2])<<16 | uint64(q[3])<<24 | uint64(q[4])<<32 | uint64(q[5])<<40 | uint64(q[6])<<48 | uint64(q[7])<<56
|
||||||
|
}
|
@@ -27,6 +27,7 @@ const (
|
|||||||
|
|
||||||
// New creates a new Matcher based on the given pattern.
|
// New creates a new Matcher based on the given pattern.
|
||||||
func (t Type) New(pattern string) (Matcher, error) {
|
func (t Type) New(pattern string) (Matcher, error) {
|
||||||
|
// 1. regex matching is case-sensitive
|
||||||
switch t {
|
switch t {
|
||||||
case Full:
|
case Full:
|
||||||
return fullMatcher(pattern), nil
|
return fullMatcher(pattern), nil
|
||||||
|
@@ -91,3 +91,172 @@ func TestMatcherGroup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestACAutomaton(t *testing.T) {
|
||||||
|
cases1 := []struct {
|
||||||
|
pattern string
|
||||||
|
mType Type
|
||||||
|
input string
|
||||||
|
output bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pattern: "xtls.github.io",
|
||||||
|
mType: Domain,
|
||||||
|
input: "www.xtls.github.io",
|
||||||
|
output: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "xtls.github.io",
|
||||||
|
mType: Domain,
|
||||||
|
input: "xtls.github.io",
|
||||||
|
output: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "xtls.github.io",
|
||||||
|
mType: Domain,
|
||||||
|
input: "www.xtis.github.io",
|
||||||
|
output: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "xtls.github.io",
|
||||||
|
mType: Domain,
|
||||||
|
input: "tls.github.io",
|
||||||
|
output: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "xtls.github.io",
|
||||||
|
mType: Domain,
|
||||||
|
input: "xxtls.github.io",
|
||||||
|
output: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "xtls.github.io",
|
||||||
|
mType: Full,
|
||||||
|
input: "xtls.github.io",
|
||||||
|
output: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "xtls.github.io",
|
||||||
|
mType: Full,
|
||||||
|
input: "xxtls.github.io",
|
||||||
|
output: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range cases1 {
|
||||||
|
var ac = NewACAutomaton()
|
||||||
|
ac.Add(test.pattern, test.mType)
|
||||||
|
ac.Build()
|
||||||
|
if m := ac.Match(test.input); m != test.output {
|
||||||
|
t.Error("unexpected output: ", m, " for test case ", test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cases2Input := []struct {
|
||||||
|
pattern string
|
||||||
|
mType Type
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pattern: "163.com",
|
||||||
|
mType: Domain,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "m.126.com",
|
||||||
|
mType: Full,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "3.com",
|
||||||
|
mType: Full,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "google.com",
|
||||||
|
mType: Substr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "vgoogle.com",
|
||||||
|
mType: Substr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var ac = NewACAutomaton()
|
||||||
|
for _, test := range cases2Input {
|
||||||
|
ac.Add(test.pattern, test.mType)
|
||||||
|
}
|
||||||
|
ac.Build()
|
||||||
|
cases2Output := []struct {
|
||||||
|
pattern string
|
||||||
|
res bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pattern: "126.com",
|
||||||
|
res: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "m.163.com",
|
||||||
|
res: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "mm163.com",
|
||||||
|
res: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "m.126.com",
|
||||||
|
res: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "163.com",
|
||||||
|
res: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "63.com",
|
||||||
|
res: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "oogle.com",
|
||||||
|
res: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "vvgoogle.com",
|
||||||
|
res: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range cases2Output {
|
||||||
|
if m := ac.Match(test.pattern); m != test.res {
|
||||||
|
t.Error("unexpected output: ", m, " for test case ", test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
cases3Input := []struct {
|
||||||
|
pattern string
|
||||||
|
mType Type
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pattern: "video.google.com",
|
||||||
|
mType: Domain,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "gle.com",
|
||||||
|
mType: Domain,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var ac = NewACAutomaton()
|
||||||
|
for _, test := range cases3Input {
|
||||||
|
ac.Add(test.pattern, test.mType)
|
||||||
|
}
|
||||||
|
ac.Build()
|
||||||
|
cases3Output := []struct {
|
||||||
|
pattern string
|
||||||
|
res bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pattern: "google.com",
|
||||||
|
res: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range cases3Output {
|
||||||
|
if m := ac.Match(test.pattern); m != test.res {
|
||||||
|
t.Error("unexpected output: ", m, " for test case ", test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -80,14 +80,25 @@ func getFormat(filename string) string {
|
|||||||
func LoadConfig(formatName string, input interface{}) (*Config, error) {
|
func LoadConfig(formatName string, input interface{}) (*Config, error) {
|
||||||
switch v := input.(type) {
|
switch v := input.(type) {
|
||||||
case cmdarg.Arg:
|
case cmdarg.Arg:
|
||||||
|
|
||||||
formats := make([]string, len(v))
|
formats := make([]string, len(v))
|
||||||
hasProtobuf := false
|
hasProtobuf := false
|
||||||
for i, file := range v {
|
for i, file := range v {
|
||||||
f := getFormat(file)
|
var f string
|
||||||
if f == "" {
|
|
||||||
|
if formatName == "auto" {
|
||||||
|
if file != "stdin:" {
|
||||||
|
f = getFormat(file)
|
||||||
|
} else {
|
||||||
|
f = "json"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
f = formatName
|
f = formatName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if f == "" {
|
||||||
|
return nil, newError("Failed to get format of ", file).AtWarning()
|
||||||
|
}
|
||||||
|
|
||||||
if f == "protobuf" {
|
if f == "protobuf" {
|
||||||
hasProtobuf = true
|
hasProtobuf = true
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.25.0
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.14.0
|
// protoc v3.15.6
|
||||||
// source: core/config.proto
|
// source: core/config.proto
|
||||||
|
|
||||||
package core
|
package core
|
||||||
@@ -9,7 +9,7 @@ package core
|
|||||||
import (
|
import (
|
||||||
proto "github.com/golang/protobuf/proto"
|
proto "github.com/golang/protobuf/proto"
|
||||||
serial "github.com/xtls/xray-core/common/serial"
|
serial "github.com/xtls/xray-core/common/serial"
|
||||||
transport "github.com/xtls/xray-core/transport"
|
global "github.com/xtls/xray-core/transport/global"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -48,7 +48,7 @@ type Config struct {
|
|||||||
// config. Date to remove: 2020-01-13
|
// config. Date to remove: 2020-01-13
|
||||||
//
|
//
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Do not use.
|
||||||
Transport *transport.Config `protobuf:"bytes,5,opt,name=transport,proto3" json:"transport,omitempty"`
|
Transport *global.Config `protobuf:"bytes,5,opt,name=transport,proto3" json:"transport,omitempty"`
|
||||||
// Configuration for extensions. The config may not work if corresponding
|
// Configuration for extensions. The config may not work if corresponding
|
||||||
// extension is not loaded into Xray. Xray will ignore such config during
|
// extension is not loaded into Xray. Xray will ignore such config during
|
||||||
// initialization.
|
// initialization.
|
||||||
@@ -109,7 +109,7 @@ func (x *Config) GetApp() []*serial.TypedMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Do not use.
|
||||||
func (x *Config) GetTransport() *transport.Config {
|
func (x *Config) GetTransport() *global.Config {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Transport
|
return x.Transport
|
||||||
}
|
}
|
||||||
@@ -283,59 +283,60 @@ var file_core_config_proto_rawDesc = []byte{
|
|||||||
0x6f, 0x74, 0x6f, 0x12, 0x09, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x1a, 0x21,
|
0x6f, 0x74, 0x6f, 0x12, 0x09, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x1a, 0x21,
|
||||||
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79,
|
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79,
|
||||||
0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x6f, 0x1a, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x63, 0x6f, 0x6e,
|
0x6f, 0x1a, 0x1d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x6c, 0x6f,
|
||||||
0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb5, 0x02, 0x0a, 0x06, 0x43, 0x6f,
|
0x62, 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18,
|
0x22, 0xb5, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x07, 0x69,
|
||||||
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72,
|
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78,
|
||||||
0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72,
|
0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
|
||||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12,
|
0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x69,
|
||||||
0x3c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28,
|
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x3c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75,
|
||||||
0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x4f, 0x75,
|
0x6e, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e,
|
0x63, 0x6f, 0x72, 0x65, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e,
|
||||||
0x66, 0x69, 0x67, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x32, 0x0a,
|
0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x62,
|
||||||
0x03, 0x61, 0x70, 0x70, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
|
0x6f, 0x75, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x04, 0x20, 0x03, 0x28,
|
||||||
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e,
|
0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||||
0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x03, 0x61, 0x70,
|
0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73,
|
||||||
0x70, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x05,
|
0x61, 0x67, 0x65, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e,
|
||||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72,
|
||||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01,
|
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x43, 0x6f, 0x6e,
|
||||||
0x52, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x09, 0x65,
|
0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
|
||||||
0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20,
|
0x72, 0x74, 0x12, 0x3e, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18,
|
||||||
|
0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
|
0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64,
|
||||||
|
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
|
||||||
|
0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62,
|
||||||
|
0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
|
0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
|
||||||
|
0x74, 0x61, 0x67, 0x12, 0x4d, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f,
|
||||||
|
0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20,
|
||||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72,
|
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72,
|
||||||
0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||||
0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x03, 0x10,
|
0x52, 0x10, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
|
||||||
0x04, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e,
|
0x67, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74,
|
||||||
0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
|
0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
|
||||||
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x4d, 0x0a, 0x11,
|
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e,
|
||||||
0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72,
|
||||||
0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
|
0x6f, 0x78, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xef, 0x01, 0x0a, 0x15,
|
||||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70,
|
0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43,
|
||||||
0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10, 0x72, 0x65, 0x63, 0x65, 0x69,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70,
|
0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x49, 0x0a, 0x0f, 0x73, 0x65, 0x6e, 0x64, 0x65,
|
||||||
0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20,
|
0x72, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73,
|
||||||
0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65,
|
0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
||||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x74, 0x74,
|
0x67, 0x65, 0x52, 0x0e, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
|
||||||
0x69, 0x6e, 0x67, 0x73, 0x22, 0xef, 0x01, 0x0a, 0x15, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
|
0x67, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74,
|
||||||
0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10,
|
0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
|
||||||
0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67,
|
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e,
|
||||||
0x12, 0x49, 0x0a, 0x0f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
|
0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72,
|
||||||
0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
0x6f, 0x78, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65,
|
||||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54,
|
0x78, 0x70, 0x69, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70,
|
||||||
0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0e, 0x73, 0x65, 0x6e,
|
0x69, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05,
|
||||||
0x64, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x3d, 0x0a,
|
||||||
0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20,
|
0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x50, 0x01,
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
|
||||||
0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65,
|
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x72, 0x65,
|
||||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x74, 0x74,
|
0xaa, 0x02, 0x09, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72,
|
||||||
0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x18, 0x04,
|
0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07,
|
|
||||||
0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63,
|
|
||||||
0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x3d, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
|
||||||
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x50, 0x01, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75,
|
|
||||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
|
|
||||||
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0xaa, 0x02, 0x09, 0x58, 0x72, 0x61, 0x79,
|
|
||||||
0x2e, 0x43, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -356,7 +357,7 @@ var file_core_config_proto_goTypes = []interface{}{
|
|||||||
(*InboundHandlerConfig)(nil), // 1: xray.core.InboundHandlerConfig
|
(*InboundHandlerConfig)(nil), // 1: xray.core.InboundHandlerConfig
|
||||||
(*OutboundHandlerConfig)(nil), // 2: xray.core.OutboundHandlerConfig
|
(*OutboundHandlerConfig)(nil), // 2: xray.core.OutboundHandlerConfig
|
||||||
(*serial.TypedMessage)(nil), // 3: xray.common.serial.TypedMessage
|
(*serial.TypedMessage)(nil), // 3: xray.common.serial.TypedMessage
|
||||||
(*transport.Config)(nil), // 4: xray.transport.Config
|
(*global.Config)(nil), // 4: xray.transport.Config
|
||||||
}
|
}
|
||||||
var file_core_config_proto_depIdxs = []int32{
|
var file_core_config_proto_depIdxs = []int32{
|
||||||
1, // 0: xray.core.Config.inbound:type_name -> xray.core.InboundHandlerConfig
|
1, // 0: xray.core.Config.inbound:type_name -> xray.core.InboundHandlerConfig
|
||||||
|
@@ -7,7 +7,7 @@ option java_package = "com.xray.core";
|
|||||||
option java_multiple_files = true;
|
option java_multiple_files = true;
|
||||||
|
|
||||||
import "common/serial/typed_message.proto";
|
import "common/serial/typed_message.proto";
|
||||||
import "transport/config.proto";
|
import "transport/global/config.proto";
|
||||||
|
|
||||||
// Config is the master config of Xray. Xray takes this config as input and
|
// Config is the master config of Xray. Xray takes this config as input and
|
||||||
// functions accordingly.
|
// functions accordingly.
|
||||||
|
@@ -18,7 +18,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "1.3.1"
|
version = "1.4.2"
|
||||||
build = "Custom"
|
build = "Custom"
|
||||||
codename = "Xray, Penetrates Everything."
|
codename = "Xray, Penetrates Everything."
|
||||||
intro = "A unified platform for anti-censorship."
|
intro = "A unified platform for anti-censorship."
|
||||||
|
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/features/policy"
|
"github.com/xtls/xray-core/features/policy"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"github.com/xtls/xray-core/features/stats"
|
"github.com/xtls/xray-core/features/stats"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server is an instance of Xray. At any time, there must be at most one Server instance running.
|
// Server is an instance of Xray. At any time, there must be at most one Server instance running.
|
||||||
@@ -223,6 +224,14 @@ func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internet.InitSystemDialer(
|
||||||
|
server.GetFeature(dns.ClientType()).(dns.Client),
|
||||||
|
func() outbound.Manager {
|
||||||
|
obm, _ := server.GetFeature(outbound.ManagerType()).(outbound.Manager)
|
||||||
|
return obm
|
||||||
|
}(),
|
||||||
|
)
|
||||||
|
|
||||||
if server.featureResolutions != nil {
|
if server.featureResolutions != nil {
|
||||||
return true, newError("not all dependency are resolved.")
|
return true, newError("not all dependency are resolved.")
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,19 @@ import (
|
|||||||
"github.com/xtls/xray-core/features"
|
"github.com/xtls/xray-core/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IPOption is an object for IP query options.
|
||||||
|
type IPOption struct {
|
||||||
|
IPv4Enable bool
|
||||||
|
IPv6Enable bool
|
||||||
|
FakeEnable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *IPOption) Copy() *IPOption {
|
||||||
|
return &IPOption{p.IPv4Enable, p.IPv6Enable, p.FakeEnable}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(dopt *IPOption) *IPOption
|
||||||
|
|
||||||
// Client is a Xray feature for querying DNS information.
|
// Client is a Xray feature for querying DNS information.
|
||||||
//
|
//
|
||||||
// xray:api:stable
|
// xray:api:stable
|
||||||
@@ -15,6 +28,9 @@ type Client interface {
|
|||||||
|
|
||||||
// LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses.
|
// LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses.
|
||||||
LookupIP(domain string) ([]net.IP, error)
|
LookupIP(domain string) ([]net.IP, error)
|
||||||
|
|
||||||
|
// LookupOptions query IP address for domain with *IPOption.
|
||||||
|
LookupOptions(domain string, opt ...Option) ([]net.IP, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPv4Lookup is an optional feature for querying IPv4 addresses only.
|
// IPv4Lookup is an optional feature for querying IPv4 addresses only.
|
||||||
@@ -57,3 +73,41 @@ func RCodeFromError(err error) uint16 {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
LookupIPv4Only = func(d *IPOption) *IPOption {
|
||||||
|
d.IPv4Enable = true
|
||||||
|
d.IPv6Enable = false
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
LookupIPv6Only = func(d *IPOption) *IPOption {
|
||||||
|
d.IPv4Enable = false
|
||||||
|
d.IPv6Enable = true
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
LookupIP = func(d *IPOption) *IPOption {
|
||||||
|
d.IPv4Enable = true
|
||||||
|
d.IPv6Enable = true
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
LookupFake = func(d *IPOption) *IPOption {
|
||||||
|
d.FakeEnable = true
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
LookupFakeOnly = func(d *IPOption) *IPOption {
|
||||||
|
d.FakeEnable = true
|
||||||
|
d.IPv4Enable = false
|
||||||
|
d.IPv6Enable = false
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
LookupNoFake = func(d *IPOption) *IPOption {
|
||||||
|
d.FakeEnable = false
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
LookupAll = func(d *IPOption) *IPOption {
|
||||||
|
LookupIP(d)
|
||||||
|
LookupFake(d)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
)
|
||||||
|
17
features/dns/fakedns.go
Normal file
17
features/dns/fakedns.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
gonet "net"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/features"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FakeDNSEngine interface {
|
||||||
|
features.Feature
|
||||||
|
GetFakeIPForDomain(domain string) []net.Address
|
||||||
|
GetDomainFromFakeDNS(ip net.Address) string
|
||||||
|
GetFakeIPRange() *gonet.IPNet
|
||||||
|
}
|
||||||
|
|
||||||
|
var FakeIPPool = "198.18.0.0/16"
|
@@ -38,6 +38,11 @@ func (*Client) LookupIP(host string) ([]net.IP, error) {
|
|||||||
return parsedIPs, nil
|
return parsedIPs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LookupOptions implements Client.
|
||||||
|
func (c *Client) LookupOptions(host string, _ ...dns.Option) ([]net.IP, error) {
|
||||||
|
return c.LookupIP(host)
|
||||||
|
}
|
||||||
|
|
||||||
// LookupIPv4 implements IPv4Lookup.
|
// LookupIPv4 implements IPv4Lookup.
|
||||||
func (c *Client) LookupIPv4(host string) ([]net.IP, error) {
|
func (c *Client) LookupIPv4(host string) ([]net.IP, error) {
|
||||||
ips, err := c.LookupIP(host)
|
ips, err := c.LookupIP(host)
|
||||||
@@ -71,6 +76,7 @@ func (c *Client) LookupIPv6(host string) ([]net.IP, error) {
|
|||||||
if len(ipv6) == 0 {
|
if len(ipv6) == 0 {
|
||||||
return nil, dns.ErrEmptyResponse
|
return nil, dns.ErrEmptyResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipv6, nil
|
return ipv6, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
features/dns/localdns/errors.generated.go
Normal file
9
features/dns/localdns/errors.generated.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package localdns
|
||||||
|
|
||||||
|
import "github.com/xtls/xray-core/common/errors"
|
||||||
|
|
||||||
|
type errPathObjHolder struct{}
|
||||||
|
|
||||||
|
func newError(values ...interface{}) *errors.Error {
|
||||||
|
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||||
|
}
|
@@ -37,4 +37,7 @@ type Context interface {
|
|||||||
|
|
||||||
// GetAttributes returns extra attributes from the conneciont content.
|
// GetAttributes returns extra attributes from the conneciont content.
|
||||||
GetAttributes() map[string]string
|
GetAttributes() map[string]string
|
||||||
|
|
||||||
|
// GetSkipDNSResolve returns a flag switch for weather skip dns resolve during route pick.
|
||||||
|
GetSkipDNSResolve() bool
|
||||||
}
|
}
|
||||||
|
@@ -31,7 +31,7 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
|
|||||||
ctx.resolvedIPs = ips
|
ctx.resolvedIPs = ips
|
||||||
return ips
|
return ips
|
||||||
}
|
}
|
||||||
newError("resolve ip for ", domain).Base(err).WriteToLog()
|
newError("failed to resolve ip for ", domain).Base(err).WriteToLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@@ -109,6 +109,14 @@ func (ctx *Context) GetAttributes() map[string]string {
|
|||||||
return ctx.Content.Attributes
|
return ctx.Content.Attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSkipDNSResolve implements routing.Context.
|
||||||
|
func (ctx *Context) GetSkipDNSResolve() bool {
|
||||||
|
if ctx.Content == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ctx.Content.SkipDNSResolve
|
||||||
|
}
|
||||||
|
|
||||||
// AsRoutingContext creates a context from context.context with session info.
|
// AsRoutingContext creates a context from context.context with session info.
|
||||||
func AsRoutingContext(ctx context.Context) routing.Context {
|
func AsRoutingContext(ctx context.Context) routing.Context {
|
||||||
return &Context{
|
return &Context{
|
||||||
|
25
go.mod
25
go.mod
@@ -1,26 +1,27 @@
|
|||||||
module github.com/xtls/xray-core
|
module github.com/xtls/xray-core
|
||||||
|
|
||||||
go 1.15
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
|
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
|
||||||
github.com/golang/mock v1.5.0
|
github.com/golang/mock v1.5.0
|
||||||
github.com/golang/protobuf v1.4.3
|
github.com/golang/protobuf v1.5.2
|
||||||
github.com/google/go-cmp v0.5.4
|
github.com/google/go-cmp v0.5.5
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/lucas-clemente/quic-go v0.19.3
|
github.com/lucas-clemente/quic-go v0.20.0
|
||||||
github.com/miekg/dns v1.1.40
|
github.com/miekg/dns v1.1.41
|
||||||
github.com/pelletier/go-toml v1.8.1
|
github.com/pelletier/go-toml v1.8.1
|
||||||
github.com/pires/go-proxyproto v0.4.2
|
github.com/pires/go-proxyproto v0.5.0
|
||||||
|
github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c
|
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499
|
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499
|
||||||
go.starlark.net v0.0.0-20210223155950-e043a3d3c984
|
go.starlark.net v0.0.0-20210312235212-74c10e2c17dc
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
golang.org/x/net v0.0.0-20210330230544-e57232859fb2
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44
|
||||||
google.golang.org/grpc v1.36.0
|
google.golang.org/grpc v1.36.1
|
||||||
google.golang.org/protobuf v1.25.0
|
google.golang.org/protobuf v1.26.0
|
||||||
h12.io/socks v1.0.2
|
h12.io/socks v1.0.2
|
||||||
)
|
)
|
||||||
|
73
go.sum
73
go.sum
@@ -43,12 +43,9 @@ github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev
|
|||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
||||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||||
@@ -62,8 +59,9 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
|||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
@@ -71,8 +69,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
@@ -98,19 +96,19 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/lucas-clemente/quic-go v0.19.3 h1:eCDQqvGBB+kCTkA0XrAFtNe81FMa0/fn4QSoeAbmiF4=
|
github.com/lucas-clemente/quic-go v0.20.0 h1:FSU3YN5VnLafHR27Ejs1r1CYMS7XMyIVDzRewkDLNBw=
|
||||||
github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8=
|
github.com/lucas-clemente/quic-go v0.20.0/go.mod h1:fZq/HUDIM+mW6X6wtzORjC0E/WDBMKe5Hf9bgjISwLk=
|
||||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||||
github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc=
|
github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A=
|
||||||
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
|
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||||
github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ=
|
github.com/marten-seemann/qtls-go1-16 v0.1.3 h1:XEZ1xGorVy9u+lJq+WXNE+hiqRYLNvJGYmwfwKQN2gU=
|
||||||
github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
github.com/marten-seemann/qtls-go1-16 v0.1.3/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||||
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
|
||||||
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||||
@@ -129,8 +127,8 @@ github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNC
|
|||||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
||||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||||
github.com/pires/go-proxyproto v0.4.2 h1:VRAvsUCTrmiahoU5fqQqkbY0GWcJ1Q0F7b7CkFaipSU=
|
github.com/pires/go-proxyproto v0.5.0 h1:A4Jv4ZCaV3AFJeGh5mGwkz4iuWUYMlQ7IoO/GTuSuLo=
|
||||||
github.com/pires/go-proxyproto v0.4.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
github.com/pires/go-proxyproto v0.5.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
@@ -139,6 +137,8 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:
|
|||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b h1:lzo71oHzQEz0fKMSjR0BpVzuh2hOHvJTxnN3Rnikmtg=
|
||||||
|
github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c h1:pqy40B3MQWYrza7YZXOXgl0Nf0QGFqrOC0BKae1UNAA=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c h1:pqy40B3MQWYrza7YZXOXgl0Nf0QGFqrOC0BKae1UNAA=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||||
@@ -169,7 +169,6 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh
|
|||||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
@@ -180,9 +179,8 @@ github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMI
|
|||||||
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 h1:QHESTXtfgc1ABV+ArlbPVqUx9Ht5I0dDkYhxYoXFxNo=
|
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 h1:QHESTXtfgc1ABV+ArlbPVqUx9Ht5I0dDkYhxYoXFxNo=
|
||||||
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499/go.mod h1:5TB2+k58gx4A4g2Nf5miSHNDF6CuAzHKpWBooLAshTs=
|
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499/go.mod h1:5TB2+k58gx4A4g2Nf5miSHNDF6CuAzHKpWBooLAshTs=
|
||||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.starlark.net v0.0.0-20210312235212-74c10e2c17dc h1:pVkptfeOTFfx+zXZo7HEHN3d5LmhatBFvHdm/f2QnpY=
|
||||||
go.starlark.net v0.0.0-20210223155950-e043a3d3c984 h1:xwwDQW5We85NaTk2APgoN9202w/l0DVGp+GZMfsrh7s=
|
go.starlark.net v0.0.0-20210312235212-74c10e2c17dc/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
|
||||||
go.starlark.net v0.0.0-20210223155950-e043a3d3c984/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
|
|
||||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
@@ -191,14 +189,13 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -211,11 +208,11 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210330230544-e57232859fb2 h1:nGCZOty+lVDsc4H2qPFksI5Se296+V+GhMiL/TzmYNk=
|
||||||
|
golang.org/x/net v0.0.0-20210330230544-e57232859fb2/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -235,22 +232,19 @@ golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b h1:kHlr0tATeLRMEiZJu5CknOw/E8V6h69sXXQFGoPtjcc=
|
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
@@ -267,7 +261,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -285,7 +278,6 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
|
|||||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
@@ -293,12 +285,11 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
|
|||||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As=
|
google.golang.org/grpc v1.36.1 h1:cmUfbeGKnz9+2DD/UYsMQXeqbHZqZDs4eQwW0sFOpBY=
|
||||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@@ -307,8 +298,10 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
|||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -331,7 +324,5 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh
|
|||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
|
||||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||||
|
@@ -11,10 +11,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NameServerConfig struct {
|
type NameServerConfig struct {
|
||||||
Address *Address
|
Address *Address
|
||||||
Port uint16
|
ClientIP *Address
|
||||||
Domains []string
|
Port uint16
|
||||||
ExpectIPs StringList
|
SkipFallback bool
|
||||||
|
Domains []string
|
||||||
|
ExpectIPs StringList
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
||||||
@@ -25,14 +27,18 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var advanced struct {
|
var advanced struct {
|
||||||
Address *Address `json:"address"`
|
Address *Address `json:"address"`
|
||||||
Port uint16 `json:"port"`
|
ClientIP *Address `json:"clientIp"`
|
||||||
Domains []string `json:"domains"`
|
Port uint16 `json:"port"`
|
||||||
ExpectIPs StringList `json:"expectIps"`
|
SkipFallback bool `json:"skipFallback"`
|
||||||
|
Domains []string `json:"domains"`
|
||||||
|
ExpectIPs StringList `json:"expectIps"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(data, &advanced); err == nil {
|
if err := json.Unmarshal(data, &advanced); err == nil {
|
||||||
c.Address = advanced.Address
|
c.Address = advanced.Address
|
||||||
|
c.ClientIP = advanced.ClientIP
|
||||||
c.Port = advanced.Port
|
c.Port = advanced.Port
|
||||||
|
c.SkipFallback = advanced.SkipFallback
|
||||||
c.Domains = advanced.Domains
|
c.Domains = advanced.Domains
|
||||||
c.ExpectIPs = advanced.ExpectIPs
|
c.ExpectIPs = advanced.ExpectIPs
|
||||||
return nil
|
return nil
|
||||||
@@ -87,12 +93,21 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
|
|||||||
return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
|
return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var myClientIP []byte
|
||||||
|
if c.ClientIP != nil {
|
||||||
|
if !c.ClientIP.Family().IsIP() {
|
||||||
|
return nil, newError("not an IP address:", c.ClientIP.String())
|
||||||
|
}
|
||||||
|
myClientIP = []byte(c.ClientIP.IP())
|
||||||
|
}
|
||||||
return &dns.NameServer{
|
return &dns.NameServer{
|
||||||
Address: &net.Endpoint{
|
Address: &net.Endpoint{
|
||||||
Network: net.Network_UDP,
|
Network: net.Network_UDP,
|
||||||
Address: c.Address.Build(),
|
Address: c.Address.Build(),
|
||||||
Port: uint32(c.Port),
|
Port: uint32(c.Port),
|
||||||
},
|
},
|
||||||
|
ClientIp: myClientIP,
|
||||||
|
SkipFallback: c.SkipFallback,
|
||||||
PrioritizedDomain: domains,
|
PrioritizedDomain: domains,
|
||||||
Geoip: geoipList,
|
Geoip: geoipList,
|
||||||
OriginalRules: originalRules,
|
OriginalRules: originalRules,
|
||||||
@@ -108,28 +123,72 @@ var typeMap = map[router.Domain_Type]dns.DomainMatchingType{
|
|||||||
|
|
||||||
// DNSConfig is a JSON serializable object for dns.Config.
|
// DNSConfig is a JSON serializable object for dns.Config.
|
||||||
type DNSConfig struct {
|
type DNSConfig struct {
|
||||||
Servers []*NameServerConfig `json:"servers"`
|
Servers []*NameServerConfig `json:"servers"`
|
||||||
Hosts map[string]*Address `json:"hosts"`
|
Hosts map[string]*HostAddress `json:"hosts"`
|
||||||
ClientIP *Address `json:"clientIp"`
|
ClientIP *Address `json:"clientIp"`
|
||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
|
QueryStrategy string `json:"queryStrategy"`
|
||||||
|
CacheStrategy string `json:"cacheStrategy"`
|
||||||
|
DisableCache bool `json:"disableCache"`
|
||||||
|
DisableFallback bool `json:"disableFallback"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHostMapping(addr *Address) *dns.Config_HostMapping {
|
type HostAddress struct {
|
||||||
if addr.Family().IsIP() {
|
addr *Address
|
||||||
return &dns.Config_HostMapping{
|
addrs []*Address
|
||||||
Ip: [][]byte{[]byte(addr.IP())},
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
|
||||||
|
func (h *HostAddress) UnmarshalJSON(data []byte) error {
|
||||||
|
addr := new(Address)
|
||||||
|
var addrs []*Address
|
||||||
|
switch {
|
||||||
|
case json.Unmarshal(data, &addr) == nil:
|
||||||
|
h.addr = addr
|
||||||
|
case json.Unmarshal(data, &addrs) == nil:
|
||||||
|
h.addrs = addrs
|
||||||
|
default:
|
||||||
|
return newError("invalid address")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHostMapping(ha *HostAddress) *dns.Config_HostMapping {
|
||||||
|
if ha.addr != nil {
|
||||||
|
if ha.addr.Family().IsDomain() {
|
||||||
|
return &dns.Config_HostMapping{
|
||||||
|
ProxiedDomain: ha.addr.Domain(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return &dns.Config_HostMapping{
|
return &dns.Config_HostMapping{
|
||||||
ProxiedDomain: addr.Domain(),
|
Ip: [][]byte{ha.addr.IP()},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ips := make([][]byte, 0, len(ha.addrs))
|
||||||
|
for _, addr := range ha.addrs {
|
||||||
|
if addr.Family().IsDomain() {
|
||||||
|
return &dns.Config_HostMapping{
|
||||||
|
ProxiedDomain: addr.Domain(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ips = append(ips, []byte(addr.IP()))
|
||||||
|
}
|
||||||
|
return &dns.Config_HostMapping{
|
||||||
|
Ip: ips,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable
|
// Build implements Buildable
|
||||||
func (c *DNSConfig) Build() (*dns.Config, error) {
|
func (c *DNSConfig) Build() (*dns.Config, error) {
|
||||||
config := &dns.Config{
|
config := &dns.Config{
|
||||||
Tag: c.Tag,
|
Tag: c.Tag,
|
||||||
|
CacheStrategy: dns.CacheStrategy_Cache_ALL,
|
||||||
|
DisableFallback: c.DisableFallback,
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.DisableCache {
|
||||||
|
config.CacheStrategy = dns.CacheStrategy_Cache_DISABLE
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.ClientIP != nil {
|
if c.ClientIP != nil {
|
||||||
@@ -139,6 +198,25 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
|
|||||||
config.ClientIp = []byte(c.ClientIP.IP())
|
config.ClientIp = []byte(c.ClientIP.IP())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.QueryStrategy = dns.QueryStrategy_USE_IP
|
||||||
|
switch strings.ToLower(c.QueryStrategy) {
|
||||||
|
case "useip", "use_ip", "use-ip":
|
||||||
|
config.QueryStrategy = dns.QueryStrategy_USE_IP
|
||||||
|
case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4":
|
||||||
|
config.QueryStrategy = dns.QueryStrategy_USE_IP4
|
||||||
|
case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6":
|
||||||
|
config.QueryStrategy = dns.QueryStrategy_USE_IP6
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(c.CacheStrategy) {
|
||||||
|
case "noerror":
|
||||||
|
config.CacheStrategy = dns.CacheStrategy_Cache_NOERROR
|
||||||
|
case "all":
|
||||||
|
config.CacheStrategy = dns.CacheStrategy_Cache_ALL
|
||||||
|
case "disable", "none":
|
||||||
|
config.CacheStrategy = dns.CacheStrategy_Cache_DISABLE
|
||||||
|
}
|
||||||
|
|
||||||
for _, server := range c.Servers {
|
for _, server := range c.Servers {
|
||||||
ns, err := server.Build()
|
ns, err := server.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -1,15 +1,18 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/proxy/dns"
|
"github.com/xtls/xray-core/proxy/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DNSOutboundConfig struct {
|
type DNSOutboundConfig struct {
|
||||||
Network Network `json:"network"`
|
Network Network `json:"network"`
|
||||||
Address *Address `json:"address"`
|
Address *Address `json:"address"`
|
||||||
Port uint16 `json:"port"`
|
Port uint16 `json:"port"`
|
||||||
|
DomainStrategy string `json:"domainStrategy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSOutboundConfig) Build() (proto.Message, error) {
|
func (c *DNSOutboundConfig) Build() (proto.Message, error) {
|
||||||
@@ -22,5 +25,12 @@ func (c *DNSOutboundConfig) Build() (proto.Message, error) {
|
|||||||
if c.Address != nil {
|
if c.Address != nil {
|
||||||
config.Server.Address = c.Address.Build()
|
config.Server.Address = c.Address.Build()
|
||||||
}
|
}
|
||||||
|
config.DomainStrategy = dns.Config_USE_ALL
|
||||||
|
switch strings.ToLower(c.DomainStrategy) {
|
||||||
|
case "useip", "use_ip", "use-ip":
|
||||||
|
config.DomainStrategy = dns.Config_USE_IP
|
||||||
|
case "fake", "fakedns":
|
||||||
|
config.DomainStrategy = dns.Config_USE_FAKE
|
||||||
|
}
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})),
|
Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})),
|
||||||
Port: 53,
|
Port: 53,
|
||||||
},
|
},
|
||||||
|
DomainStrategy: dns.Config_USE_ALL,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@@ -67,17 +67,23 @@ func TestDNSConfigParsing(t *testing.T) {
|
|||||||
Input: `{
|
Input: `{
|
||||||
"servers": [{
|
"servers": [{
|
||||||
"address": "8.8.8.8",
|
"address": "8.8.8.8",
|
||||||
|
"clientIp": "10.0.0.1",
|
||||||
"port": 5353,
|
"port": 5353,
|
||||||
|
"skipFallback": true,
|
||||||
"domains": ["domain:example.com"]
|
"domains": ["domain:example.com"]
|
||||||
}],
|
}],
|
||||||
"hosts": {
|
"hosts": {
|
||||||
"example.com": "127.0.0.1",
|
"example.com": "127.0.0.1",
|
||||||
|
"xtls.github.io": ["1.2.3.4", "5.6.7.8"],
|
||||||
"domain:example.com": "google.com",
|
"domain:example.com": "google.com",
|
||||||
"geosite:test": "10.0.0.1",
|
"geosite:test": ["127.0.0.1", "127.0.0.2"],
|
||||||
"keyword:google": "8.8.8.8",
|
"keyword:google": ["8.8.8.8", "8.8.4.4"],
|
||||||
"regexp:.*\\.com": "8.8.4.4"
|
"regexp:.*\\.com": "8.8.4.4"
|
||||||
},
|
},
|
||||||
"clientIp": "10.0.0.1"
|
"clientIp": "10.0.0.1",
|
||||||
|
"queryStrategy": "UseIPv4",
|
||||||
|
"cacheStrategy": "disable",
|
||||||
|
"disableFallback": true
|
||||||
}`,
|
}`,
|
||||||
Parser: parserCreator(),
|
Parser: parserCreator(),
|
||||||
Output: &dns.Config{
|
Output: &dns.Config{
|
||||||
@@ -92,6 +98,8 @@ func TestDNSConfigParsing(t *testing.T) {
|
|||||||
Network: net.Network_UDP,
|
Network: net.Network_UDP,
|
||||||
Port: 5353,
|
Port: 5353,
|
||||||
},
|
},
|
||||||
|
ClientIp: []byte{10, 0, 0, 1},
|
||||||
|
SkipFallback: true,
|
||||||
PrioritizedDomain: []*dns.NameServer_PriorityDomain{
|
PrioritizedDomain: []*dns.NameServer_PriorityDomain{
|
||||||
{
|
{
|
||||||
Type: dns.DomainMatchingType_Subdomain,
|
Type: dns.DomainMatchingType_Subdomain,
|
||||||
@@ -120,20 +128,28 @@ func TestDNSConfigParsing(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Type: dns.DomainMatchingType_Full,
|
Type: dns.DomainMatchingType_Full,
|
||||||
Domain: "example.com",
|
Domain: "example.com",
|
||||||
Ip: [][]byte{{10, 0, 0, 1}},
|
Ip: [][]byte{{127, 0, 0, 1}, {127, 0, 0, 2}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: dns.DomainMatchingType_Keyword,
|
Type: dns.DomainMatchingType_Keyword,
|
||||||
Domain: "google",
|
Domain: "google",
|
||||||
Ip: [][]byte{{8, 8, 8, 8}},
|
Ip: [][]byte{{8, 8, 8, 8}, {8, 8, 4, 4}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: dns.DomainMatchingType_Regex,
|
Type: dns.DomainMatchingType_Regex,
|
||||||
Domain: ".*\\.com",
|
Domain: ".*\\.com",
|
||||||
Ip: [][]byte{{8, 8, 4, 4}},
|
Ip: [][]byte{{8, 8, 4, 4}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Type: dns.DomainMatchingType_Full,
|
||||||
|
Domain: "xtls.github.io",
|
||||||
|
Ip: [][]byte{{1, 2, 3, 4}, {5, 6, 7, 8}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ClientIp: []byte{10, 0, 0, 1},
|
ClientIp: []byte{10, 0, 0, 1},
|
||||||
|
QueryStrategy: dns.QueryStrategy_USE_IP4,
|
||||||
|
CacheStrategy: dns.CacheStrategy_Cache_DISABLE,
|
||||||
|
DisableFallback: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
66
infra/conf/fakedns.go
Normal file
66
infra/conf/fakedns.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/xtls/xray-core/app/dns/fakedns"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FakeDNSConfig struct {
|
||||||
|
IPPool string `json:"ipPool"`
|
||||||
|
LruSize int64 `json:"poolSize"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeDNSConfig) Build() (proto.Message, error) {
|
||||||
|
return &fakedns.FakeDnsPool{
|
||||||
|
IpPool: f.IPPool,
|
||||||
|
LruSize: f.LruSize,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type FakeDNSPostProcessingStage struct{}
|
||||||
|
|
||||||
|
func (FakeDNSPostProcessingStage) Process(conf *Config) error {
|
||||||
|
var fakeDNSInUse bool
|
||||||
|
|
||||||
|
if conf.DNSConfig != nil {
|
||||||
|
for _, v := range conf.DNSConfig.Servers {
|
||||||
|
if v.Address.Family().IsDomain() {
|
||||||
|
if v.Address.Domain() == "fakedns" {
|
||||||
|
fakeDNSInUse = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fakeDNSInUse {
|
||||||
|
if conf.FakeDNS == nil {
|
||||||
|
// Add a Fake DNS Config if there is none
|
||||||
|
conf.FakeDNS = &FakeDNSConfig{
|
||||||
|
IPPool: dns.FakeIPPool,
|
||||||
|
LruSize: 65535,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found := false
|
||||||
|
// Check if there is a Outbound with necessary sniffer on
|
||||||
|
var inbounds []InboundDetourConfig
|
||||||
|
|
||||||
|
if len(conf.InboundConfigs) > 0 {
|
||||||
|
inbounds = append(inbounds, conf.InboundConfigs...)
|
||||||
|
}
|
||||||
|
for _, v := range inbounds {
|
||||||
|
if v.SniffingConfig != nil && v.SniffingConfig.Enabled && v.SniffingConfig.DestOverride != nil {
|
||||||
|
for _, dov := range *v.SniffingConfig.DestOverride {
|
||||||
|
if dov == "fakedns" {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
newError("Defined Fake DNS but haven't enabled fake dns sniffing at any inbound.").AtWarning().WriteToLog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@@ -22,11 +22,11 @@ func (c *FreedomConfig) Build() (proto.Message, error) {
|
|||||||
config := new(freedom.Config)
|
config := new(freedom.Config)
|
||||||
config.DomainStrategy = freedom.Config_AS_IS
|
config.DomainStrategy = freedom.Config_AS_IS
|
||||||
switch strings.ToLower(c.DomainStrategy) {
|
switch strings.ToLower(c.DomainStrategy) {
|
||||||
case "useip", "use_ip":
|
case "useip", "use_ip", "use-ip":
|
||||||
config.DomainStrategy = freedom.Config_USE_IP
|
config.DomainStrategy = freedom.Config_USE_IP
|
||||||
case "useip4", "useipv4", "use_ipv4", "use_ip_v4", "use_ip4":
|
case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4":
|
||||||
config.DomainStrategy = freedom.Config_USE_IP4
|
config.DomainStrategy = freedom.Config_USE_IP4
|
||||||
case "useip6", "useipv6", "use_ipv6", "use_ip_v6", "use_ip6":
|
case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6":
|
||||||
config.DomainStrategy = freedom.Config_USE_IP6
|
config.DomainStrategy = freedom.Config_USE_IP6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
infra/conf/grpc.go
Normal file
16
infra/conf/grpc.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/transport/internet/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GRPCConfig struct {
|
||||||
|
ServiceName string `json:"serviceName"`
|
||||||
|
MultiMode bool `json:"multiMode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GRPCConfig) Build() (proto.Message, error) {
|
||||||
|
return &grpc.Config{ServiceName: g.ServiceName, MultiMode: g.MultiMode}, nil
|
||||||
|
}
|
5
infra/conf/init.go
Normal file
5
infra/conf/init.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterConfigureFilePostProcessingStage("FakeDNS", &FakeDNSPostProcessingStage{})
|
||||||
|
}
|
23
infra/conf/lint.go
Normal file
23
infra/conf/lint.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
type ConfigureFilePostProcessingStage interface {
|
||||||
|
Process(conf *Config) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var configureFilePostProcessingStages map[string]ConfigureFilePostProcessingStage
|
||||||
|
|
||||||
|
func RegisterConfigureFilePostProcessingStage(name string, stage ConfigureFilePostProcessingStage) {
|
||||||
|
if configureFilePostProcessingStages == nil {
|
||||||
|
configureFilePostProcessingStages = make(map[string]ConfigureFilePostProcessingStage)
|
||||||
|
}
|
||||||
|
configureFilePostProcessingStages[name] = stage
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostProcessConfigureFile(conf *Config) error {
|
||||||
|
for k, v := range configureFilePostProcessingStages {
|
||||||
|
if err := v.Process(conf); err != nil {
|
||||||
|
return newError("Rejected by Postprocessing Stage ", k).AtError().Base(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@@ -19,6 +19,7 @@ type LogConfig struct {
|
|||||||
AccessLog string `json:"access"`
|
AccessLog string `json:"access"`
|
||||||
ErrorLog string `json:"error"`
|
ErrorLog string `json:"error"`
|
||||||
LogLevel string `json:"loglevel"`
|
LogLevel string `json:"loglevel"`
|
||||||
|
DNSLog bool `json:"dnsLog"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *LogConfig) Build() *log.Config {
|
func (v *LogConfig) Build() *log.Config {
|
||||||
@@ -28,6 +29,7 @@ func (v *LogConfig) Build() *log.Config {
|
|||||||
config := &log.Config{
|
config := &log.Config{
|
||||||
ErrorLogType: log.LogType_Console,
|
ErrorLogType: log.LogType_Console,
|
||||||
AccessLogType: log.LogType_Console,
|
AccessLogType: log.LogType_Console,
|
||||||
|
EnableDnsLog: v.DNSLog,
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.AccessLog == "none" {
|
if v.AccessLog == "none" {
|
||||||
|
@@ -98,6 +98,8 @@ type RouterRule struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
OutboundTag string `json:"outboundTag"`
|
OutboundTag string `json:"outboundTag"`
|
||||||
BalancerTag string `json:"balancerTag"`
|
BalancerTag string `json:"balancerTag"`
|
||||||
|
|
||||||
|
DomainMatcher string `json:"domainMatcher"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseIP(s string) (*router.CIDR, error) {
|
func ParseIP(s string) (*router.CIDR, error) {
|
||||||
@@ -491,6 +493,10 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
|||||||
return nil, newError("neither outboundTag nor balancerTag is specified in routing rule")
|
return nil, newError("neither outboundTag nor balancerTag is specified in routing rule")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rawFieldRule.DomainMatcher != "" {
|
||||||
|
rule.DomainMatcher = rawFieldRule.DomainMatcher
|
||||||
|
}
|
||||||
|
|
||||||
if rawFieldRule.Domain != nil {
|
if rawFieldRule.Domain != nil {
|
||||||
for _, domain := range *rawFieldRule.Domain {
|
for _, domain := range *rawFieldRule.Domain {
|
||||||
rules, err := parseDomainRule(domain)
|
rules, err := parseDomainRule(domain)
|
||||||
|
@@ -2,7 +2,7 @@ package conf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport/global"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,11 +13,13 @@ type TransportConfig struct {
|
|||||||
HTTPConfig *HTTPConfig `json:"httpSettings"`
|
HTTPConfig *HTTPConfig `json:"httpSettings"`
|
||||||
DSConfig *DomainSocketConfig `json:"dsSettings"`
|
DSConfig *DomainSocketConfig `json:"dsSettings"`
|
||||||
QUICConfig *QUICConfig `json:"quicSettings"`
|
QUICConfig *QUICConfig `json:"quicSettings"`
|
||||||
|
GRPCConfig *GRPCConfig `json:"grpcSettings"`
|
||||||
|
GUNConfig *GRPCConfig `json:"gunSettings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
func (c *TransportConfig) Build() (*transport.Config, error) {
|
func (c *TransportConfig) Build() (*global.Config, error) {
|
||||||
config := new(transport.Config)
|
config := new(global.Config)
|
||||||
|
|
||||||
if c.TCPConfig != nil {
|
if c.TCPConfig != nil {
|
||||||
ts, err := c.TCPConfig.Build()
|
ts, err := c.TCPConfig.Build()
|
||||||
@@ -85,5 +87,19 @@ func (c *TransportConfig) Build() (*transport.Config, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.GRPCConfig == nil {
|
||||||
|
c.GRPCConfig = c.GUNConfig
|
||||||
|
}
|
||||||
|
if c.GRPCConfig != nil {
|
||||||
|
gs, err := c.GRPCConfig.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to build gRPC config.").Base(err)
|
||||||
|
}
|
||||||
|
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
||||||
|
ProtocolName: "grpc",
|
||||||
|
Settings: serial.ToTypedMessage(gs),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,9 @@ package conf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
@@ -154,9 +157,20 @@ func (c *WebSocketConfig) Build() (proto.Message, error) {
|
|||||||
Value: value,
|
Value: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
var ed uint32
|
||||||
|
if u, err := url.Parse(path); err == nil {
|
||||||
|
if q := u.Query(); q.Get("ed") != "" {
|
||||||
|
Ed, _ := strconv.Atoi(q.Get("ed"))
|
||||||
|
ed = uint32(Ed)
|
||||||
|
q.Del("ed")
|
||||||
|
u.RawQuery = q.Encode()
|
||||||
|
path = u.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
config := &websocket.Config{
|
config := &websocket.Config{
|
||||||
Path: path,
|
Path: path,
|
||||||
Header: header,
|
Header: header,
|
||||||
|
Ed: ed,
|
||||||
}
|
}
|
||||||
if c.AcceptProxyProtocol {
|
if c.AcceptProxyProtocol {
|
||||||
config.AcceptProxyProtocol = c.AcceptProxyProtocol
|
config.AcceptProxyProtocol = c.AcceptProxyProtocol
|
||||||
@@ -307,6 +321,7 @@ type TLSConfig struct {
|
|||||||
MaxVersion string `json:"maxVersion"`
|
MaxVersion string `json:"maxVersion"`
|
||||||
CipherSuites string `json:"cipherSuites"`
|
CipherSuites string `json:"cipherSuites"`
|
||||||
PreferServerCipherSuites bool `json:"preferServerCipherSuites"`
|
PreferServerCipherSuites bool `json:"preferServerCipherSuites"`
|
||||||
|
Fingerprint string `json:"fingerprint"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -334,6 +349,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
config.MaxVersion = c.MaxVersion
|
config.MaxVersion = c.MaxVersion
|
||||||
config.CipherSuites = c.CipherSuites
|
config.CipherSuites = c.CipherSuites
|
||||||
config.PreferServerCipherSuites = c.PreferServerCipherSuites
|
config.PreferServerCipherSuites = c.PreferServerCipherSuites
|
||||||
|
config.Fingerprint = strings.ToLower(c.Fingerprint)
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,26 +460,37 @@ func (p TransportProtocol) Build() (string, error) {
|
|||||||
return "domainsocket", nil
|
return "domainsocket", nil
|
||||||
case "quic":
|
case "quic":
|
||||||
return "quic", nil
|
return "quic", nil
|
||||||
|
case "grpc", "gun":
|
||||||
|
return "grpc", nil
|
||||||
default:
|
default:
|
||||||
return "", newError("Config: unknown transport protocol: ", p)
|
return "", newError("Config: unknown transport protocol: ", p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SocketConfig struct {
|
type SocketConfig struct {
|
||||||
Mark int32 `json:"mark"`
|
Mark int32 `json:"mark"`
|
||||||
TFO *bool `json:"tcpFastOpen"`
|
TFO interface{} `json:"tcpFastOpen"`
|
||||||
TProxy string `json:"tproxy"`
|
TProxy string `json:"tproxy"`
|
||||||
AcceptProxyProtocol bool `json:"acceptProxyProtocol"`
|
AcceptProxyProtocol bool `json:"acceptProxyProtocol"`
|
||||||
|
DomainStrategy string `json:"domainStrategy"`
|
||||||
|
DialerProxy string `json:"dialerProxy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
||||||
var tfoSettings internet.SocketConfig_TCPFastOpenState
|
tfo := int32(0) // don't invoke setsockopt() for TFO
|
||||||
if c.TFO != nil {
|
if c.TFO != nil {
|
||||||
if *c.TFO {
|
switch v := c.TFO.(type) {
|
||||||
tfoSettings = internet.SocketConfig_Enable
|
case bool:
|
||||||
} else {
|
if v {
|
||||||
tfoSettings = internet.SocketConfig_Disable
|
tfo = 256
|
||||||
|
} else {
|
||||||
|
tfo = -1 // TFO need to be disabled
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
tfo = int32(math.Min(v, math.MaxInt32))
|
||||||
|
default:
|
||||||
|
return nil, newError("tcpFastOpen: only boolean and integer value is acceptable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var tproxy internet.SocketConfig_TProxyMode
|
var tproxy internet.SocketConfig_TProxyMode
|
||||||
@@ -476,11 +503,23 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
|||||||
tproxy = internet.SocketConfig_Off
|
tproxy = internet.SocketConfig_Off
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dStrategy = internet.DomainStrategy_AS_IS
|
||||||
|
switch strings.ToLower(c.DomainStrategy) {
|
||||||
|
case "useip", "use_ip":
|
||||||
|
dStrategy = internet.DomainStrategy_USE_IP
|
||||||
|
case "useip4", "useipv4", "use_ipv4", "use_ip_v4", "use_ip4":
|
||||||
|
dStrategy = internet.DomainStrategy_USE_IP4
|
||||||
|
case "useip6", "useipv6", "use_ipv6", "use_ip_v6", "use_ip6":
|
||||||
|
dStrategy = internet.DomainStrategy_USE_IP6
|
||||||
|
}
|
||||||
|
|
||||||
return &internet.SocketConfig{
|
return &internet.SocketConfig{
|
||||||
Mark: c.Mark,
|
Mark: c.Mark,
|
||||||
Tfo: tfoSettings,
|
Tfo: tfo,
|
||||||
Tproxy: tproxy,
|
Tproxy: tproxy,
|
||||||
|
DomainStrategy: dStrategy,
|
||||||
AcceptProxyProtocol: c.AcceptProxyProtocol,
|
AcceptProxyProtocol: c.AcceptProxyProtocol,
|
||||||
|
DialerProxy: c.DialerProxy,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,6 +535,8 @@ type StreamConfig struct {
|
|||||||
DSSettings *DomainSocketConfig `json:"dsSettings"`
|
DSSettings *DomainSocketConfig `json:"dsSettings"`
|
||||||
QUICSettings *QUICConfig `json:"quicSettings"`
|
QUICSettings *QUICConfig `json:"quicSettings"`
|
||||||
SocketSettings *SocketConfig `json:"sockopt"`
|
SocketSettings *SocketConfig `json:"sockopt"`
|
||||||
|
GRPCConfig *GRPCConfig `json:"grpcSettings"`
|
||||||
|
GUNConfig *GRPCConfig `json:"gunSettings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -605,6 +646,19 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
|||||||
Settings: serial.ToTypedMessage(qs),
|
Settings: serial.ToTypedMessage(qs),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if c.GRPCConfig == nil {
|
||||||
|
c.GRPCConfig = c.GUNConfig
|
||||||
|
}
|
||||||
|
if c.GRPCConfig != nil {
|
||||||
|
gs, err := c.GRPCConfig.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to build gRPC config.").Base(err)
|
||||||
|
}
|
||||||
|
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
||||||
|
ProtocolName: "grpc",
|
||||||
|
Settings: serial.ToTypedMessage(gs),
|
||||||
|
})
|
||||||
|
}
|
||||||
if c.SocketSettings != nil {
|
if c.SocketSettings != nil {
|
||||||
ss, err := c.SocketSettings.Build()
|
ss, err := c.SocketSettings.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -617,6 +671,9 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
|||||||
|
|
||||||
type ProxyConfig struct {
|
type ProxyConfig struct {
|
||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
|
|
||||||
|
// TransportLayerProxy: For compatibility.
|
||||||
|
TransportLayerProxy bool `json:"transportLayer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -625,6 +682,7 @@ func (v *ProxyConfig) Build() (*internet.ProxyConfig, error) {
|
|||||||
return nil, newError("Proxy tag is not set.")
|
return nil, newError("Proxy tag is not set.")
|
||||||
}
|
}
|
||||||
return &internet.ProxyConfig{
|
return &internet.ProxyConfig{
|
||||||
Tag: v.Tag,
|
Tag: v.Tag,
|
||||||
|
TransportLayerProxy: v.TransportLayerProxy,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@@ -8,8 +8,9 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
. "github.com/xtls/xray-core/infra/conf"
|
. "github.com/xtls/xray-core/infra/conf"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport/global"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/grpc"
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/http"
|
"github.com/xtls/xray-core/transport/internet/headers/http"
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/noop"
|
"github.com/xtls/xray-core/transport/internet/headers/noop"
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/tls"
|
"github.com/xtls/xray-core/transport/internet/headers/tls"
|
||||||
@@ -30,19 +31,134 @@ func TestSocketConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test "tcpFastOpen": true, queue length 256 is expected. other parameters are tested here too
|
||||||
|
expectedOutput := &internet.SocketConfig{
|
||||||
|
Mark: 1,
|
||||||
|
Tfo: 256,
|
||||||
|
DomainStrategy: internet.DomainStrategy_USE_IP,
|
||||||
|
DialerProxy: "tag",
|
||||||
|
}
|
||||||
runMultiTestCase(t, []TestCase{
|
runMultiTestCase(t, []TestCase{
|
||||||
{
|
{
|
||||||
Input: `{
|
Input: `{
|
||||||
"mark": 1,
|
"mark": 1,
|
||||||
"tcpFastOpen": true
|
"tcpFastOpen": true,
|
||||||
|
"domainStrategy": "UseIP",
|
||||||
|
"dialerProxy": "tag"
|
||||||
}`,
|
}`,
|
||||||
Parser: createParser(),
|
Parser: createParser(),
|
||||||
Output: &internet.SocketConfig{
|
Output: expectedOutput,
|
||||||
Mark: 1,
|
|
||||||
Tfo: internet.SocketConfig_Enable,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
if expectedOutput.ParseTFOValue() != 256 {
|
||||||
|
t.Fatalf("unexpected parsed TFO value, which should be 256")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test "tcpFastOpen": false, disabled TFO is expected
|
||||||
|
expectedOutput = &internet.SocketConfig{
|
||||||
|
Mark: 0,
|
||||||
|
Tfo: -1,
|
||||||
|
}
|
||||||
|
runMultiTestCase(t, []TestCase{
|
||||||
|
{
|
||||||
|
Input: `{
|
||||||
|
"tcpFastOpen": false
|
||||||
|
}`,
|
||||||
|
Parser: createParser(),
|
||||||
|
Output: expectedOutput,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if expectedOutput.ParseTFOValue() != 0 {
|
||||||
|
t.Fatalf("unexpected parsed TFO value, which should be 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test "tcpFastOpen": 65535, queue length 65535 is expected
|
||||||
|
expectedOutput = &internet.SocketConfig{
|
||||||
|
Mark: 0,
|
||||||
|
Tfo: 65535,
|
||||||
|
}
|
||||||
|
runMultiTestCase(t, []TestCase{
|
||||||
|
{
|
||||||
|
Input: `{
|
||||||
|
"tcpFastOpen": 65535
|
||||||
|
}`,
|
||||||
|
Parser: createParser(),
|
||||||
|
Output: expectedOutput,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if expectedOutput.ParseTFOValue() != 65535 {
|
||||||
|
t.Fatalf("unexpected parsed TFO value, which should be 65535")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test "tcpFastOpen": -65535, disable TFO is expected
|
||||||
|
expectedOutput = &internet.SocketConfig{
|
||||||
|
Mark: 0,
|
||||||
|
Tfo: -65535,
|
||||||
|
}
|
||||||
|
runMultiTestCase(t, []TestCase{
|
||||||
|
{
|
||||||
|
Input: `{
|
||||||
|
"tcpFastOpen": -65535
|
||||||
|
}`,
|
||||||
|
Parser: createParser(),
|
||||||
|
Output: expectedOutput,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if expectedOutput.ParseTFOValue() != 0 {
|
||||||
|
t.Fatalf("unexpected parsed TFO value, which should be 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test "tcpFastOpen": 0, no operation is expected
|
||||||
|
expectedOutput = &internet.SocketConfig{
|
||||||
|
Mark: 0,
|
||||||
|
Tfo: 0,
|
||||||
|
}
|
||||||
|
runMultiTestCase(t, []TestCase{
|
||||||
|
{
|
||||||
|
Input: `{
|
||||||
|
"tcpFastOpen": 0
|
||||||
|
}`,
|
||||||
|
Parser: createParser(),
|
||||||
|
Output: expectedOutput,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if expectedOutput.ParseTFOValue() != -1 {
|
||||||
|
t.Fatalf("unexpected parsed TFO value, which should be -1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test omit "tcpFastOpen", no operation is expected
|
||||||
|
expectedOutput = &internet.SocketConfig{
|
||||||
|
Mark: 0,
|
||||||
|
Tfo: 0,
|
||||||
|
}
|
||||||
|
runMultiTestCase(t, []TestCase{
|
||||||
|
{
|
||||||
|
Input: `{}`,
|
||||||
|
Parser: createParser(),
|
||||||
|
Output: expectedOutput,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if expectedOutput.ParseTFOValue() != -1 {
|
||||||
|
t.Fatalf("unexpected parsed TFO value, which should be -1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test "tcpFastOpen": null, no operation is expected
|
||||||
|
expectedOutput = &internet.SocketConfig{
|
||||||
|
Mark: 0,
|
||||||
|
Tfo: 0,
|
||||||
|
}
|
||||||
|
runMultiTestCase(t, []TestCase{
|
||||||
|
{
|
||||||
|
Input: `{
|
||||||
|
"tcpFastOpen": null
|
||||||
|
}`,
|
||||||
|
Parser: createParser(),
|
||||||
|
Output: expectedOutput,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if expectedOutput.ParseTFOValue() != -1 {
|
||||||
|
t.Fatalf("unexpected parsed TFO value, which should be -1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTransportConfig(t *testing.T) {
|
func TestTransportConfig(t *testing.T) {
|
||||||
@@ -92,10 +208,14 @@ func TestTransportConfig(t *testing.T) {
|
|||||||
"header": {
|
"header": {
|
||||||
"type": "dtls"
|
"type": "dtls"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"grpcSettings": {
|
||||||
|
"serviceName": "name",
|
||||||
|
"multiMode": true
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
Parser: createParser(),
|
Parser: createParser(),
|
||||||
Output: &transport.Config{
|
Output: &global.Config{
|
||||||
TransportSettings: []*internet.TransportConfig{
|
TransportSettings: []*internet.TransportConfig{
|
||||||
{
|
{
|
||||||
ProtocolName: "tcp",
|
ProtocolName: "tcp",
|
||||||
@@ -162,6 +282,31 @@ func TestTransportConfig(t *testing.T) {
|
|||||||
Header: serial.ToTypedMessage(&tls.PacketConfig{}),
|
Header: serial.ToTypedMessage(&tls.PacketConfig{}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ProtocolName: "grpc",
|
||||||
|
Settings: serial.ToTypedMessage(&grpc.Config{
|
||||||
|
ServiceName: "name",
|
||||||
|
MultiMode: true,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: `{
|
||||||
|
"gunSettings": {
|
||||||
|
"serviceName": "name"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
Parser: createParser(),
|
||||||
|
Output: &global.Config{
|
||||||
|
TransportSettings: []*internet.TransportConfig{
|
||||||
|
{
|
||||||
|
ProtocolName: "grpc",
|
||||||
|
Settings: serial.ToTypedMessage(&grpc.Config{
|
||||||
|
ServiceName: "name",
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -104,7 +104,7 @@ type TrojanUserConfig struct {
|
|||||||
// TrojanServerConfig is Inbound configuration
|
// TrojanServerConfig is Inbound configuration
|
||||||
type TrojanServerConfig struct {
|
type TrojanServerConfig struct {
|
||||||
Clients []*TrojanUserConfig `json:"clients"`
|
Clients []*TrojanUserConfig `json:"clients"`
|
||||||
Fallback json.RawMessage `json:"fallback"`
|
Fallback *TrojanInboundFallback `json:"fallback"`
|
||||||
Fallbacks []*TrojanInboundFallback `json:"fallbacks"`
|
Fallbacks []*TrojanInboundFallback `json:"fallbacks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,7 +29,7 @@ type VLessInboundFallback struct {
|
|||||||
type VLessInboundConfig struct {
|
type VLessInboundConfig struct {
|
||||||
Clients []json.RawMessage `json:"clients"`
|
Clients []json.RawMessage `json:"clients"`
|
||||||
Decryption string `json:"decryption"`
|
Decryption string `json:"decryption"`
|
||||||
Fallback json.RawMessage `json:"fallback"`
|
Fallback *VLessInboundFallback `json:"fallback"`
|
||||||
Fallbacks []*VLessInboundFallback `json:"fallbacks"`
|
Fallbacks []*VLessInboundFallback `json:"fallbacks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,6 +32,8 @@ func (a *VMessAccount) Build() *vmess.Account {
|
|||||||
st = protocol.SecurityType_AUTO
|
st = protocol.SecurityType_AUTO
|
||||||
case "none":
|
case "none":
|
||||||
st = protocol.SecurityType_NONE
|
st = protocol.SecurityType_NONE
|
||||||
|
case "zero":
|
||||||
|
st = protocol.SecurityType_ZERO
|
||||||
default:
|
default:
|
||||||
st = protocol.SecurityType_AUTO
|
st = protocol.SecurityType_AUTO
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package conf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -61,6 +62,7 @@ type SniffingConfig struct {
|
|||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
DestOverride *StringList `json:"destOverride"`
|
DestOverride *StringList `json:"destOverride"`
|
||||||
DomainsExcluded *StringList `json:"domainsExcluded"`
|
DomainsExcluded *StringList `json:"domainsExcluded"`
|
||||||
|
MetadataOnly bool `json:"metadataOnly"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -73,6 +75,8 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
|
|||||||
p = append(p, "http")
|
p = append(p, "http")
|
||||||
case "tls", "https", "ssl":
|
case "tls", "https", "ssl":
|
||||||
p = append(p, "tls")
|
p = append(p, "tls")
|
||||||
|
case "fakedns":
|
||||||
|
p = append(p, "fakedns")
|
||||||
default:
|
default:
|
||||||
return nil, newError("unknown protocol: ", protocol)
|
return nil, newError("unknown protocol: ", protocol)
|
||||||
}
|
}
|
||||||
@@ -90,6 +94,7 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
|
|||||||
Enabled: c.Enabled,
|
Enabled: c.Enabled,
|
||||||
DestinationOverride: p,
|
DestinationOverride: p,
|
||||||
DomainsExcluded: d,
|
DomainsExcluded: d,
|
||||||
|
MetadataOnly: c.MetadataOnly,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,9 +272,22 @@ type OutboundDetourConfig struct {
|
|||||||
MuxSettings *MuxConfig `json:"mux"`
|
MuxSettings *MuxConfig `json:"mux"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *OutboundDetourConfig) checkChainProxyConfig() error {
|
||||||
|
if c.StreamSetting == nil || c.ProxySettings == nil || c.StreamSetting.SocketSettings == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(c.ProxySettings.Tag) > 0 && len(c.StreamSetting.SocketSettings.DialerProxy) > 0 {
|
||||||
|
return newError("proxySettings.tag is conflicted with sockopt.dialerProxy").AtWarning()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
||||||
senderSettings := &proxyman.SenderConfig{}
|
senderSettings := &proxyman.SenderConfig{}
|
||||||
|
if err := c.checkChainProxyConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if c.SendThrough != nil {
|
if c.SendThrough != nil {
|
||||||
address := c.SendThrough
|
address := c.SendThrough
|
||||||
@@ -295,6 +313,18 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newError("invalid outbound detour proxy settings.").Base(err)
|
return nil, newError("invalid outbound detour proxy settings.").Base(err)
|
||||||
}
|
}
|
||||||
|
if ps.TransportLayerProxy {
|
||||||
|
if senderSettings.StreamSettings != nil {
|
||||||
|
if senderSettings.StreamSettings.SocketSettings != nil {
|
||||||
|
senderSettings.StreamSettings.SocketSettings.DialerProxy = ps.Tag
|
||||||
|
} else {
|
||||||
|
senderSettings.StreamSettings.SocketSettings = &internet.SocketConfig{DialerProxy: ps.Tag}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
senderSettings.StreamSettings = &internet.StreamConfig{SocketSettings: &internet.SocketConfig{DialerProxy: ps.Tag}}
|
||||||
|
}
|
||||||
|
ps = nil
|
||||||
|
}
|
||||||
senderSettings.ProxySettings = ps
|
senderSettings.ProxySettings = ps
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,6 +399,7 @@ type Config struct {
|
|||||||
API *APIConfig `json:"api"`
|
API *APIConfig `json:"api"`
|
||||||
Stats *StatsConfig `json:"stats"`
|
Stats *StatsConfig `json:"stats"`
|
||||||
Reverse *ReverseConfig `json:"reverse"`
|
Reverse *ReverseConfig `json:"reverse"`
|
||||||
|
FakeDNS *FakeDNSConfig `json:"fakeDns"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) findInboundTag(tag string) int {
|
func (c *Config) findInboundTag(tag string) int {
|
||||||
@@ -422,6 +453,10 @@ func (c *Config) Override(o *Config, fn string) {
|
|||||||
c.Reverse = o.Reverse
|
c.Reverse = o.Reverse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.FakeDNS != nil {
|
||||||
|
c.FakeDNS = o.FakeDNS
|
||||||
|
}
|
||||||
|
|
||||||
// deprecated attrs... keep them for now
|
// deprecated attrs... keep them for now
|
||||||
if o.InboundConfig != nil {
|
if o.InboundConfig != nil {
|
||||||
c.InboundConfig = o.InboundConfig
|
c.InboundConfig = o.InboundConfig
|
||||||
@@ -493,6 +528,10 @@ func applyTransportConfig(s *StreamConfig, t *TransportConfig) {
|
|||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
func (c *Config) Build() (*core.Config, error) {
|
func (c *Config) Build() (*core.Config, error) {
|
||||||
|
if err := PostProcessConfigureFile(c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
config := &core.Config{
|
config := &core.Config{
|
||||||
App: []*serial.TypedMessage{
|
App: []*serial.TypedMessage{
|
||||||
serial.ToTypedMessage(&dispatcher.Config{}),
|
serial.ToTypedMessage(&dispatcher.Config{}),
|
||||||
@@ -559,6 +598,14 @@ func (c *Config) Build() (*core.Config, error) {
|
|||||||
config.App = append(config.App, serial.ToTypedMessage(r))
|
config.App = append(config.App, serial.ToTypedMessage(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.FakeDNS != nil {
|
||||||
|
r, err := c.FakeDNS.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config.App = append(config.App, serial.ToTypedMessage(r))
|
||||||
|
}
|
||||||
|
|
||||||
var inbounds []InboundDetourConfig
|
var inbounds []InboundDetourConfig
|
||||||
|
|
||||||
if c.InboundConfig != nil {
|
if c.InboundConfig != nil {
|
||||||
|
@@ -26,6 +26,9 @@ Arguments:
|
|||||||
-domain=domain_name
|
-domain=domain_name
|
||||||
The domain name for the certificate.
|
The domain name for the certificate.
|
||||||
|
|
||||||
|
-name=common_name
|
||||||
|
The common name for the certificate.
|
||||||
|
|
||||||
-org=organization
|
-org=organization
|
||||||
The organization name for the certificate.
|
The organization name for the certificate.
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
// Other optional features.
|
// Other optional features.
|
||||||
_ "github.com/xtls/xray-core/app/dns"
|
_ "github.com/xtls/xray-core/app/dns"
|
||||||
|
_ "github.com/xtls/xray-core/app/dns/fakedns"
|
||||||
_ "github.com/xtls/xray-core/app/log"
|
_ "github.com/xtls/xray-core/app/log"
|
||||||
_ "github.com/xtls/xray-core/app/policy"
|
_ "github.com/xtls/xray-core/app/policy"
|
||||||
_ "github.com/xtls/xray-core/app/reverse"
|
_ "github.com/xtls/xray-core/app/reverse"
|
||||||
@@ -39,6 +40,7 @@ import (
|
|||||||
|
|
||||||
// Transports
|
// Transports
|
||||||
_ "github.com/xtls/xray-core/transport/internet/domainsocket"
|
_ "github.com/xtls/xray-core/transport/internet/domainsocket"
|
||||||
|
_ "github.com/xtls/xray-core/transport/internet/grpc"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/http"
|
_ "github.com/xtls/xray-core/transport/internet/http"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/kcp"
|
_ "github.com/xtls/xray-core/transport/internet/kcp"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/quic"
|
_ "github.com/xtls/xray-core/transport/internet/quic"
|
||||||
|
22
main/run.go
22
main/run.go
@@ -11,6 +11,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/cmdarg"
|
"github.com/xtls/xray-core/common/cmdarg"
|
||||||
@@ -31,7 +32,7 @@ Xray. Multiple assign is accepted.
|
|||||||
The -confdir=dir flag sets a dir with multiple json config
|
The -confdir=dir flag sets a dir with multiple json config
|
||||||
|
|
||||||
The -format=json flag sets the format of config files.
|
The -format=json flag sets the format of config files.
|
||||||
Default "json".
|
Default "auto".
|
||||||
|
|
||||||
The -test flag tells Xray to test config files only,
|
The -test flag tells Xray to test config files only,
|
||||||
without launching the server
|
without launching the server
|
||||||
@@ -46,7 +47,7 @@ var (
|
|||||||
configFiles cmdarg.Arg // "Config file for Xray.", the option is customed type, parse in main
|
configFiles cmdarg.Arg // "Config file for Xray.", the option is customed type, parse in main
|
||||||
configDir string
|
configDir string
|
||||||
test = cmdRun.Flag.Bool("test", false, "Test config file only, without launching Xray server.")
|
test = cmdRun.Flag.Bool("test", false, "Test config file only, without launching Xray server.")
|
||||||
format = cmdRun.Flag.String("format", "json", "Format of input file.")
|
format = cmdRun.Flag.String("format", "auto", "Format of input file.")
|
||||||
|
|
||||||
/* We have to do this here because Golang's Test will also need to parse flag, before
|
/* We have to do this here because Golang's Test will also need to parse flag, before
|
||||||
* main func in this file is run.
|
* main func in this file is run.
|
||||||
@@ -111,13 +112,26 @@ func dirExists(file string) bool {
|
|||||||
return err == nil && info.IsDir()
|
return err == nil && info.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRegepxByFormat() string {
|
||||||
|
switch strings.ToLower(*format) {
|
||||||
|
case "json":
|
||||||
|
return `^.+\.json$`
|
||||||
|
case "toml":
|
||||||
|
return `^.+\.toml$`
|
||||||
|
case "yaml", "yml":
|
||||||
|
return `^.+\.(yaml|yml)$`
|
||||||
|
default:
|
||||||
|
return `^.+\.(json|toml|yaml|yml)$`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func readConfDir(dirPath string) {
|
func readConfDir(dirPath string) {
|
||||||
confs, err := ioutil.ReadDir(dirPath)
|
confs, err := ioutil.ReadDir(dirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
for _, f := range confs {
|
for _, f := range confs {
|
||||||
matched, err := regexp.MatchString(`^.+\.(json|toml|yaml|yml)$`, f.Name())
|
matched, err := regexp.MatchString(getRegepxByFormat(), f.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
@@ -160,7 +174,7 @@ func getConfigFilePath() cmdarg.Arg {
|
|||||||
func getConfigFormat() string {
|
func getConfigFormat() string {
|
||||||
f := core.GetFormatByExtension(*format)
|
f := core.GetFormatByExtension(*format)
|
||||||
if f == "" {
|
if f == "" {
|
||||||
f = "json"
|
f = "auto"
|
||||||
}
|
}
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.25.0
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.14.0
|
// protoc v3.15.8
|
||||||
// source: proxy/dns/config.proto
|
// source: proxy/dns/config.proto
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
@@ -26,6 +26,55 @@ const (
|
|||||||
// of the legacy proto package is being used.
|
// of the legacy proto package is being used.
|
||||||
const _ = proto.ProtoPackageIsVersion4
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
|
type Config_DomainStrategy int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Config_USE_ALL Config_DomainStrategy = 0
|
||||||
|
Config_USE_IP Config_DomainStrategy = 1
|
||||||
|
Config_USE_FAKE Config_DomainStrategy = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for Config_DomainStrategy.
|
||||||
|
var (
|
||||||
|
Config_DomainStrategy_name = map[int32]string{
|
||||||
|
0: "USE_ALL",
|
||||||
|
1: "USE_IP",
|
||||||
|
2: "USE_FAKE",
|
||||||
|
}
|
||||||
|
Config_DomainStrategy_value = map[string]int32{
|
||||||
|
"USE_ALL": 0,
|
||||||
|
"USE_IP": 1,
|
||||||
|
"USE_FAKE": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x Config_DomainStrategy) Enum() *Config_DomainStrategy {
|
||||||
|
p := new(Config_DomainStrategy)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Config_DomainStrategy) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Config_DomainStrategy) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_proxy_dns_config_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Config_DomainStrategy) Type() protoreflect.EnumType {
|
||||||
|
return &file_proxy_dns_config_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Config_DomainStrategy) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config_DomainStrategy.Descriptor instead.
|
||||||
|
func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_proxy_dns_config_proto_rawDescGZIP(), []int{0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -33,7 +82,8 @@ type Config struct {
|
|||||||
|
|
||||||
// Server is the DNS server address. If specified, this address overrides the
|
// Server is the DNS server address. If specified, this address overrides the
|
||||||
// original one.
|
// original one.
|
||||||
Server *net.Endpoint `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
|
Server *net.Endpoint `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
|
||||||
|
DomainStrategy Config_DomainStrategy `protobuf:"varint,2,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.proxy.dns.Config_DomainStrategy" json:"domain_strategy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -75,6 +125,13 @@ func (x *Config) GetServer() *net.Endpoint {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetDomainStrategy() Config_DomainStrategy {
|
||||||
|
if x != nil {
|
||||||
|
return x.DomainStrategy
|
||||||
|
}
|
||||||
|
return Config_USE_ALL
|
||||||
|
}
|
||||||
|
|
||||||
var File_proxy_dns_config_proto protoreflect.FileDescriptor
|
var File_proxy_dns_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_proxy_dns_config_proto_rawDesc = []byte{
|
var file_proxy_dns_config_proto_rawDesc = []byte{
|
||||||
@@ -82,16 +139,25 @@ var file_proxy_dns_config_proto_rawDesc = []byte{
|
|||||||
0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70,
|
0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70,
|
||||||
0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||||
0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3b, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc4, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
0x12, 0x31, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
0x67, 0x12, 0x31, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e,
|
0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||||
0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72,
|
0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65,
|
||||||
0x76, 0x65, 0x72, 0x42, 0x4c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
0x72, 0x76, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73,
|
||||||
0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x23, 0x67, 0x69, 0x74,
|
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e,
|
||||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43,
|
||||||
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x64, 0x6e, 0x73,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
|
||||||
0xaa, 0x02, 0x0e, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x44, 0x6e,
|
0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
|
||||||
0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x74, 0x65, 0x67, 0x79, 0x22, 0x37, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74,
|
||||||
|
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x41, 0x4c,
|
||||||
|
0x4c, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12,
|
||||||
|
0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x46, 0x41, 0x4b, 0x45, 0x10, 0x02, 0x42, 0x4c, 0x0a,
|
||||||
|
0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
|
||||||
|
0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||||
|
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
|
||||||
|
0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0e, 0x58, 0x72, 0x61,
|
||||||
|
0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -106,18 +172,21 @@ func file_proxy_dns_config_proto_rawDescGZIP() []byte {
|
|||||||
return file_proxy_dns_config_proto_rawDescData
|
return file_proxy_dns_config_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var file_proxy_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
var file_proxy_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
var file_proxy_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
var file_proxy_dns_config_proto_goTypes = []interface{}{
|
var file_proxy_dns_config_proto_goTypes = []interface{}{
|
||||||
(*Config)(nil), // 0: xray.proxy.dns.Config
|
(Config_DomainStrategy)(0), // 0: xray.proxy.dns.Config.DomainStrategy
|
||||||
(*net.Endpoint)(nil), // 1: xray.common.net.Endpoint
|
(*Config)(nil), // 1: xray.proxy.dns.Config
|
||||||
|
(*net.Endpoint)(nil), // 2: xray.common.net.Endpoint
|
||||||
}
|
}
|
||||||
var file_proxy_dns_config_proto_depIdxs = []int32{
|
var file_proxy_dns_config_proto_depIdxs = []int32{
|
||||||
1, // 0: xray.proxy.dns.Config.server:type_name -> xray.common.net.Endpoint
|
2, // 0: xray.proxy.dns.Config.server:type_name -> xray.common.net.Endpoint
|
||||||
1, // [1:1] is the sub-list for method output_type
|
0, // 1: xray.proxy.dns.Config.domain_strategy:type_name -> xray.proxy.dns.Config.DomainStrategy
|
||||||
1, // [1:1] is the sub-list for method input_type
|
2, // [2:2] is the sub-list for method output_type
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
2, // [2:2] is the sub-list for method input_type
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
0, // [0:1] is the sub-list for field type_name
|
2, // [2:2] is the sub-list for extension extendee
|
||||||
|
0, // [0:2] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_proxy_dns_config_proto_init() }
|
func init() { file_proxy_dns_config_proto_init() }
|
||||||
@@ -144,13 +213,14 @@ func file_proxy_dns_config_proto_init() {
|
|||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_proxy_dns_config_proto_rawDesc,
|
RawDescriptor: file_proxy_dns_config_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 1,
|
||||||
NumMessages: 1,
|
NumMessages: 1,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
GoTypes: file_proxy_dns_config_proto_goTypes,
|
GoTypes: file_proxy_dns_config_proto_goTypes,
|
||||||
DependencyIndexes: file_proxy_dns_config_proto_depIdxs,
|
DependencyIndexes: file_proxy_dns_config_proto_depIdxs,
|
||||||
|
EnumInfos: file_proxy_dns_config_proto_enumTypes,
|
||||||
MessageInfos: file_proxy_dns_config_proto_msgTypes,
|
MessageInfos: file_proxy_dns_config_proto_msgTypes,
|
||||||
}.Build()
|
}.Build()
|
||||||
File_proxy_dns_config_proto = out.File
|
File_proxy_dns_config_proto = out.File
|
||||||
|
@@ -12,4 +12,10 @@ message Config {
|
|||||||
// Server is the DNS server address. If specified, this address overrides the
|
// Server is the DNS server address. If specified, this address overrides the
|
||||||
// original one.
|
// original one.
|
||||||
xray.common.net.Endpoint server = 1;
|
xray.common.net.Endpoint server = 1;
|
||||||
|
enum DomainStrategy {
|
||||||
|
USE_ALL = 0;
|
||||||
|
USE_IP = 1;
|
||||||
|
USE_FAKE = 2;
|
||||||
|
}
|
||||||
|
DomainStrategy domain_strategy = 2;
|
||||||
}
|
}
|
||||||
|
@@ -36,24 +36,14 @@ type ownLinkVerifier interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
ipv4Lookup dns.IPv4Lookup
|
client dns.Client
|
||||||
ipv6Lookup dns.IPv6Lookup
|
|
||||||
ownLinkVerifier ownLinkVerifier
|
ownLinkVerifier ownLinkVerifier
|
||||||
server net.Destination
|
server net.Destination
|
||||||
|
DomainStrategy Config_DomainStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) Init(config *Config, dnsClient dns.Client) error {
|
func (h *Handler) Init(config *Config, dnsClient dns.Client) error {
|
||||||
ipv4lookup, ok := dnsClient.(dns.IPv4Lookup)
|
h.client = dnsClient
|
||||||
if !ok {
|
|
||||||
return newError("dns.Client doesn't implement IPv4Lookup")
|
|
||||||
}
|
|
||||||
h.ipv4Lookup = ipv4lookup
|
|
||||||
|
|
||||||
ipv6lookup, ok := dnsClient.(dns.IPv6Lookup)
|
|
||||||
if !ok {
|
|
||||||
return newError("dns.Client doesn't implement IPv6Lookup")
|
|
||||||
}
|
|
||||||
h.ipv6Lookup = ipv6lookup
|
|
||||||
|
|
||||||
if v, ok := dnsClient.(ownLinkVerifier); ok {
|
if v, ok := dnsClient.(ownLinkVerifier); ok {
|
||||||
h.ownLinkVerifier = v
|
h.ownLinkVerifier = v
|
||||||
@@ -209,13 +199,25 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
|
|||||||
var ips []net.IP
|
var ips []net.IP
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch qType {
|
var ttl uint32 = 600
|
||||||
case dnsmessage.TypeA:
|
var opt []dns.Option
|
||||||
ips, err = h.ipv4Lookup.LookupIPv4(domain)
|
|
||||||
case dnsmessage.TypeAAAA:
|
if h.DomainStrategy == Config_USE_FAKE {
|
||||||
ips, err = h.ipv6Lookup.LookupIPv6(domain)
|
opt = append(opt, dns.LookupFakeOnly)
|
||||||
|
} else {
|
||||||
|
switch qType {
|
||||||
|
case dnsmessage.TypeA:
|
||||||
|
opt = append(opt, dns.LookupIPv4Only)
|
||||||
|
case dnsmessage.TypeAAAA:
|
||||||
|
opt = append(opt, dns.LookupIPv6Only)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.DomainStrategy == Config_USE_ALL {
|
||||||
|
opt = append(opt, dns.LookupFake)
|
||||||
|
}
|
||||||
|
|
||||||
|
ips, err = h.client.LookupOptions(domain, opt...)
|
||||||
rcode := dns.RCodeFromError(err)
|
rcode := dns.RCodeFromError(err)
|
||||||
if rcode == 0 && len(ips) == 0 && err != dns.ErrEmptyResponse {
|
if rcode == 0 && len(ips) == 0 && err != dns.ErrEmptyResponse {
|
||||||
newError("ip query").Base(err).WriteToLog()
|
newError("ip query").Base(err).WriteToLog()
|
||||||
@@ -230,7 +232,6 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
|
|||||||
RecursionAvailable: true,
|
RecursionAvailable: true,
|
||||||
RecursionDesired: true,
|
RecursionDesired: true,
|
||||||
Response: true,
|
Response: true,
|
||||||
Authoritative: true,
|
|
||||||
})
|
})
|
||||||
builder.EnableCompression()
|
builder.EnableCompression()
|
||||||
common.Must(builder.StartQuestions())
|
common.Must(builder.StartQuestions())
|
||||||
@@ -241,7 +242,7 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
|
|||||||
}))
|
}))
|
||||||
common.Must(builder.StartAnswers())
|
common.Must(builder.StartAnswers())
|
||||||
|
|
||||||
rHeader := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: 600}
|
rHeader := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl}
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
if len(ip) == net.IPv4len {
|
if len(ip) == net.IPv4len {
|
||||||
var r dnsmessage.AResource
|
var r dnsmessage.AResource
|
||||||
|
@@ -59,19 +59,14 @@ func (h *Handler) policy() policy.Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address {
|
func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address {
|
||||||
var lookupFunc func(string) ([]net.IP, error) = h.dns.LookupIP
|
var opt dns.Option
|
||||||
|
|
||||||
if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) {
|
if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) {
|
||||||
if lookupIPv4, ok := h.dns.(dns.IPv4Lookup); ok {
|
opt = dns.LookupIPv4Only
|
||||||
lookupFunc = lookupIPv4.LookupIPv4
|
|
||||||
}
|
|
||||||
} else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) {
|
} else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) {
|
||||||
if lookupIPv6, ok := h.dns.(dns.IPv6Lookup); ok {
|
opt = dns.LookupIPv6Only
|
||||||
lookupFunc = lookupIPv6.LookupIPv6
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ips, err := lookupFunc(domain)
|
ips, err := h.dns.LookupOptions(domain, opt, dns.LookupNoFake)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
}
|
}
|
||||||
@@ -125,7 +120,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
Address: ip,
|
Address: ip,
|
||||||
Port: dialDest.Port,
|
Port: dialDest.Port,
|
||||||
}
|
}
|
||||||
newError("dialing to to ", dialDest).WriteToLog(session.ExportIDToError(ctx))
|
newError("dialing to ", dialDest).WriteToLog(session.ExportIDToError(ctx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +164,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
if destination.Network == net.Network_TCP {
|
if destination.Network == net.Network_TCP {
|
||||||
reader = buf.NewReader(conn)
|
reader = buf.NewReader(conn)
|
||||||
} else {
|
} else {
|
||||||
reader = NewPacketReader(conn)
|
reader = NewPacketReader(conn, UDPOverride)
|
||||||
}
|
}
|
||||||
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
|
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
|
||||||
return newError("failed to process response").Base(err)
|
return newError("failed to process response").Base(err)
|
||||||
@@ -185,7 +180,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPacketReader(conn net.Conn) buf.Reader {
|
func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
|
||||||
iConn := conn
|
iConn := conn
|
||||||
statConn, ok := iConn.(*internet.StatCouterConnection)
|
statConn, ok := iConn.(*internet.StatCouterConnection)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -195,7 +190,7 @@ func NewPacketReader(conn net.Conn) buf.Reader {
|
|||||||
if statConn != nil {
|
if statConn != nil {
|
||||||
counter = statConn.ReadCounter
|
counter = statConn.ReadCounter
|
||||||
}
|
}
|
||||||
if c, ok := iConn.(*internet.PacketConnWrapper); ok {
|
if c, ok := iConn.(*internet.PacketConnWrapper); ok && UDPOverride.Address == nil && UDPOverride.Port == 0 {
|
||||||
return &PacketReader{
|
return &PacketReader{
|
||||||
PacketConnWrapper: c,
|
PacketConnWrapper: c,
|
||||||
Counter: counter,
|
Counter: counter,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user