Merge remote-tracking branch 'origin/main' into fix_refine_dns_ttl

This commit is contained in:
mzz2017 2023-06-04 14:23:09 +08:00
commit 2d96b3c59c
36 changed files with 527 additions and 259 deletions

View File

@ -8,15 +8,15 @@ labels: kind/bug
Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks!
-->
## What happened:
## What happened
## What you expected to happen:
## What you expected to happen
## How to reproduce it (as minimally and precisely as possible):
## Anything else we need to know
## Environment:
## Environment
- Dae version (use `dae --version`):
- OS (e.g `cat /etc/os-release`):

View File

@ -16,4 +16,4 @@ You may also post your idea on the Discussions or the Dae Telegram channel (http
## What feature you would like us to integrate into the dae project
## Why is this needed:
## Why is this needed

View File

@ -4,8 +4,6 @@ on:
push:
branches:
- main
- fix*
- feat*
paths:
- "**/*.go"
- "**/*.c"

View File

@ -1,17 +1,13 @@
name: Check document
on:
push:
branches: [main]
paths:
- 'README.md'
- 'docs/**'
- 'package.json'
- '.autocorrectrc'
- '.markdownlint-cli2.jsonc'
- '.github/workflows/check-docs.yml'
workflow_dispatch:
workflow_call:
pull_request:
branches: [main]
types:
- opened
- synchronize
paths:
- 'README.md'
- 'docs/**'

110
.github/workflows/sync-docs.yml vendored Normal file
View File

@ -0,0 +1,110 @@
name: Synchronize document
on:
workflow_dispatch:
push:
branches:
- main
- 'ci/**'
- 'release/**'
tags:
- 'v*'
paths:
- "README*.md"
- "docs/**"
- "example.dae"
- "package.json"
- ".autocorrectrc"
- ".markdownlint-cli2.jsonc"
- '.github/workflows/sync-docs.yml'
jobs:
check-docs:
name: Check document
uses: ./.github/workflows/check-docs.yml
replicate-config:
needs: [check-docs]
name: Replicate relevant document
runs-on: ubuntu-latest
outputs:
git_sha_long: ${{ steps.export.outputs.git_sha_long }}
git_sha_short: ${{ steps.export.outputs.git_sha_short }}
git_commit_msg: ${{ steps.export.outputs.git_commit_msg }}
git_run_number: ${{ steps.export.outputs.git_run_number }}
steps:
- uses: actions/checkout@v3
- name: Get metadata from HEAD sha
id: export
run: |
echo "git_sha_long=${{ github.sha }}" >> $GITHUB_OUTPUT
echo "git_sha_short=$(echo ${{ github.sha }} | cut -c1-6)" >> $GITHUB_OUTPUT
echo "git_commit_msg=$(git log --format=%s -n 1 ${{ github.sha }})" >> $GITHUB_OUTPUT
echo "git_run_number=${{ github.run_number }}" >> $GITHUB_OUTPUT
- name: Generate artifacts
run: |
python3 hack/ci/config-doc-generator.py
- name: Export artifacts
uses: actions/upload-artifact@v3
with:
name: pre_flight_artifacts
path: |
./docs/sync/*.md
sync-to-dae-docs:
needs: [replicate-config]
name: Synchronize changes to dae-docs
runs-on: ubuntu-latest
steps:
- name: Checkout to daeuniverse/dae-docs
uses: actions/checkout@v3
with:
repository: daeuniverse/dae-docs
fetch-depth: 0
ref: master
- name: Copy artifacts from build job to local path
uses: actions/download-artifact@v3
with:
name: pre_flight_artifacts
path: docs/
- name: Create commits
run: |
git config user.name 'daebot'
git config user.email 'daebot@users.noreply.github.com'
git ls-files --others --exclude-standard -- "*.md"
git add "*.md"
git commit -m "ci(auto-release): push proposed documentation changes from upstream dae project"
- name: Create Pull Request
id: create_pr
uses: peter-evans/create-pull-request@v5
with:
author: daebot <dae@v2raya.org>
committer: daebot <dae@v2raya.org>
token: ${{ secrets.GH_TOKEN }}
signoff: false
branch: automated-pr-sync
delete-branch: true
title: 'ci(auto-release): sync changes from upstream'
body: |
## Summary
Update documentation - Auto-generated by [${{ github.repository }} #${{ needs.replicate-config.outputs.git_run_number }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) @daebot
Commit message: ${{ needs.replicate-config.outputs.git_commit_msg }}
## Context
🏗 daeuniverse/dae(${{ needs.replicate-config.outputs.git_sha_short }}): Build [#${{ needs.replicate-config.outputs.git_run_number }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) succeeded!
labels: |
automated-pr
assignees: daebot
team-reviewers: |
docs
draft: false

View File

@ -12,9 +12,9 @@ curl --silent "https://api.github.com/repos/daeuniverse/dae/releases" | jq -r '.
## Releases
- [0.1.10rc1 (Pre-release)](#0110rc1-pre-release)
- [0.1.10rc (Pre-release)](#0110rc-pre-release)
- [0.1.9-patch.1 (Current)](#019-patch1-current)
- [0.2.0rc1 (Pre-release)](#020rc1-pre-release)
- [0.1.10 (Current)](#0110-current)
- [0.1.9-patch.1](#019-patch1)
- [0.1.9](#019)
- [0.1.8](#018)
- [0.1.7](#017)
@ -26,29 +26,68 @@ curl --silent "https://api.github.com/repos/daeuniverse/dae/releases" | jq -r '.
- [0.1.1](#011)
- [0.1.0](#010)
### 0.1.10rc1 (Pre-release)
### 0.2.0rc1 (Pre-release)
> Release date: 2023/05/15
> Release date: 2023/06/04
#### 功能变更
- patch(geodata): 修复由 #84 导致的错误的 geodata 搜索路径 `/etc/dae/dae` by @mzz2017 in https://github.com/daeuniverse/dae/pull/90
- feat: 支持 iptables/nftables 的 mangle 表 tproxy by @mzz2017 in https://github.com/daeuniverse/dae/pull/80
- feat: 支持 uTLS by @AkinoKaede in https://github.com/daeuniverse/dae/pull/94
- feat: 支持在 geosite 使用属性标签 `@` 符号 by @mzz2017 in https://github.com/daeuniverse/dae/pull/98
- feat(dns): 支持为特定域名设定固定的 ttl这对 DDNS 场景较为有用 by @mzz2017 in https://github.com/daeuniverse/dae/pull/100
- fix(dns): 修复 DNS 中 qname 匹配规则失效的问题 by @mzz2017 in https://github.com/daeuniverse/dae/pull/99
- fix: 修复启动时网络检查链接列表的随机排布问题 by @mzz2017 in https://github.com/daeuniverse/dae/pull/106
- fix(config_parser): 修复配置文件格式错误时潜在的崩溃问题 by @mzz2017 in https://github.com/daeuniverse/dae/pull/108
- fix(trojan): 修复 trojan 崩溃问题,该问题由 ReadFrom 返回的 n 可能不正确导致 by @mzz2017 in https://github.com/daeuniverse/dae/pull/109
#### 其他变更
- ci: 添加文档格式检查工作流 by @yqlbu in https://github.com/daeuniverse/dae/pull/93
- refactor: 将 insert.sh 移动至 ./hack/test by @yqlbu in https://github.com/daeuniverse/dae/pull/95
- ci(hack): 添加 config-doc-generator by @yqlbu in https://github.com/daeuniverse/dae/pull/101
- fix(test): 修复 domain_matcher/benchmark_test.go in https://github.com/daeuniverse/dae/pull/107
- ci: 添加文档自动同步至 dae-docs 项目 by @yqlbu in https://github.com/daeuniverse/dae/pull/103
- docs(routing.md): 修订 fwmark 一节的文档 by @mzz2017 in https://github.com/daeuniverse/dae/pull/113
#### Changes
- patch(geodata): fix incorrect geodata search path `/etc/dae/dae` caused by #84 by @mzz2017 in https://github.com/daeuniverse/dae/pull/90
- feat: support iptables tproxy by @mzz2017 in https://github.com/daeuniverse/dae/pull/80
- feat: add uTLS support by @AkinoKaede in https://github.com/daeuniverse/dae/pull/94
- feat: support geosite attr by @mzz2017 in https://github.com/daeuniverse/dae/pull/98
- fix(dns): mismatched qname matching rules by @mzz2017 in https://github.com/daeuniverse/dae/pull/99
- feat(dns): support fixed domain ttl by @mzz2017 in https://github.com/daeuniverse/dae/pull/100
- fix: rand seed for network check by @mzz2017 in https://github.com/daeuniverse/dae/pull/106
- fix(config_parser): potential panic due to out of index by @mzz2017 in https://github.com/daeuniverse/dae/pull/108
- fix(trojan): potential panic due to incorrect n returned by ReadFrom by @mzz2017 in https://github.com/daeuniverse/dae/pull/109
**Full Changelog**: https://github.com/daeuniverse/dae/compare/v0.1.10rc...v0.1.10rc1
#### Other Changes
### 0.1.10rc (Pre-release)
- ci: add check-docs workflow by @yqlbu in https://github.com/daeuniverse/dae/pull/93
- refactor: move insert.sh to ./hack/test by @yqlbu in https://github.com/daeuniverse/dae/pull/95
- ci(hack): add config-doc-generator by @yqlbu in https://github.com/daeuniverse/dae/pull/101
- fix(test): domain_matcher/benchmark_test.go in https://github.com/daeuniverse/dae/pull/107
- ci: docs synchronization by @yqlbu in https://github.com/daeuniverse/dae/pull/103
- docs(routing.md): revise fwmark section by @mzz2017 in https://github.com/daeuniverse/dae/pull/113
> Release date: 2023/05/14
#### New Contributors
- @AkinoKaede made their first contribution in https://github.com/daeuniverse/dae/pull/94
**Full Changelog**: https://github.com/daeuniverse/dae/compare/v0.1.10...v0.2.0rc1
**Example Config**: https://github.com/daeuniverse/dae/blob/v0.2.0rc1/example.dae
### 0.1.10 (Current)
> Release date: 2023/06/04
#### 功能变更
- feat: 支持 `tcp_check_http_method` by @mzz2017 in https://github.com/daeuniverse/dae/pull/77
- patch: 现在会优先在配置文件同目录搜索 geodata by @mzz2017 in https://github.com/daeuniverse/dae/pull/84
- fix(dns): 修复 0.1.8 版本中 PR #63 导致的 DNS 缓存不会过期的问题 by @mzz2017 in https://github.com/daeuniverse/dae/pull/87
- patch(geodata): 修复由 #84 导致的错误的 geodata 搜索路径 `/etc/dae/dae` by @mzz2017 in https://github.com/daeuniverse/dae/pull/90
#### 其他变更
@ -61,6 +100,7 @@ curl --silent "https://api.github.com/repos/daeuniverse/dae/releases" | jq -r '.
- feat: support `tcp_check_http_method` by @mzz2017 in https://github.com/daeuniverse/dae/pull/77
- patch: search geodata at same dir with config first by @mzz2017 in https://github.com/daeuniverse/dae/pull/84
- fix(dns): cache would never expire caused by #63 by accident by @mzz2017 in https://github.com/daeuniverse/dae/pull/87
- patch(geodata): fix incorrect geodata search path `/etc/dae/dae` caused by #84 by @mzz2017 in https://github.com/daeuniverse/dae/pull/90
#### Other Changes
@ -68,9 +108,11 @@ curl --silent "https://api.github.com/repos/daeuniverse/dae/releases" | jq -r '.
- chore: add editorconfig by @yqlbu in https://github.com/daeuniverse/dae/pull/85
- chore: add pull_request_template by @yqlbu in https://github.com/daeuniverse/dae/pull/86
**Full Changelog**: https://github.com/daeuniverse/dae/compare/v0.1.9...v0.1.10rc
**Full Changelog**: https://github.com/daeuniverse/dae/compare/v0.1.9...v0.1.10
### 0.1.9-patch.1 (Current)
**Example Config**: https://github.com/daeuniverse/dae/blob/v0.1.10/example.dae
### 0.1.9-patch.1
> Release date: 2023/05/14

View File

@ -23,6 +23,7 @@ else
STRIP_FLAG := -strip=$(STRIP_PATH)
endif
# Do NOT remove the line below. This line is for CI.
#export GOMODCACHE=$(PWD)/go-mod
# Get version from .git.
@ -41,7 +42,7 @@ dae: export GOOS=linux
dae: ebpf
go build -o $(OUTPUT) -trimpath -ldflags "-s -w -X github.com/daeuniverse/dae/cmd.Version=$(VERSION) -X github.com/daeuniverse/dae/common/consts.MaxMatchSetLen_=$(MAX_MATCH_SET_LEN)" .
clean-ebpf:
clean-ebpf:
@rm -f control/bpf_bpf*.go && \
rm -f control/bpf_bpf*.o
fmt:

View File

@ -5,7 +5,7 @@
<p align="left">
<img src="https://custom-icon-badges.herokuapp.com/github/license/daeuniverse/dae?logo=law&color=orange" alt="License"/>
<img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fdaeuniverse%2Fdae&count_bg=%235C3DC8&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false"/>
<img src="https://custom-icon-badges.herokuapp.com/badge/version-v0.1.9-blue.svg?logo=semanticrelease&logoColor=white" alt="version">
<img src="https://custom-icon-badges.herokuapp.com/badge/version-v0.2.0rc1-blue.svg?logo=semanticrelease&logoColor=white" alt="version">
<img src="https://custom-icon-badges.herokuapp.com/github/issues-pr-closed/daeuniverse/dae?color=purple&logo=git-pull-request&logoColor=white"/>
<img src="https://custom-icon-badges.herokuapp.com/github/last-commit/daeuniverse/dae?logo=history&logoColor=white" alt="lastcommit"/>
</p>

View File

@ -1,9 +1,12 @@
package cmd
import (
"context"
"errors"
"fmt"
"github.com/mzz2017/softwind/netproxy"
"github.com/mzz2017/softwind/pkg/fastrand"
"github.com/mzz2017/softwind/protocol/direct"
"net"
"net/http"
"os"
@ -247,6 +250,20 @@ func newControlPlane(log *logrus.Logger, bpf interface{}, dnsCache map[string]*c
if !conf.Global.DisableWaitingNetwork && len(conf.Subscription) > 0 {
epo := 5 * time.Second
client := http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (c net.Conn, err error) {
cd := netproxy.ContextDialer{Dialer: direct.SymmetricDirect}
conn, err := cd.DialContext(ctx, common.MagicNetwork("tcp", conf.Global.SoMarkFromDae), addr)
if err != nil {
return nil, err
}
return &netproxy.FakeNetConn{
Conn: conn,
LAddr: nil,
RAddr: nil,
}, nil
},
},
Timeout: epo,
}
log.Infoln("Waiting for network...")
@ -274,8 +291,25 @@ func newControlPlane(log *logrus.Logger, bpf interface{}, dnsCache map[string]*c
if len(conf.Subscription) > 0 {
log.Infoln("Fetching subscriptions...")
}
client := http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (c net.Conn, err error) {
cd := netproxy.ContextDialer{Dialer: direct.SymmetricDirect}
conn, err := cd.DialContext(ctx, common.MagicNetwork("tcp", conf.Global.SoMarkFromDae), addr)
if err != nil {
return nil, err
}
return &netproxy.FakeNetConn{
Conn: conn,
LAddr: nil,
RAddr: nil,
}, nil
},
},
Timeout: 30 * time.Second,
}
for _, sub := range conf.Subscription {
tag, nodes, err := subscription.ResolveSubscription(log, filepath.Dir(cfgFile), string(sub))
tag, nodes, err := subscription.ResolveSubscription(log, &client, filepath.Dir(cfgFile), string(sub))
if err != nil {
log.Warnf(`failed to resolve subscription "%v": %v`, sub, err)
resolvingfailed = true

View File

@ -146,6 +146,7 @@ var (
const (
TproxyMark uint32 = 0x8000000
Recognize uint16 = 0x2017
LoopbackIfIndex = 1
)

View File

@ -19,7 +19,6 @@ import (
"github.com/mzz2017/softwind/netproxy"
"github.com/mzz2017/softwind/pkg/fastrand"
"github.com/mzz2017/softwind/pool"
"github.com/sirupsen/logrus"
"golang.org/x/net/dns/dnsmessage"
)
@ -91,8 +90,8 @@ func SystemDns() (dns netip.AddrPort, err error) {
return systemDns, nil
}
func ResolveNetip(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host string, typ dnsmessage.Type, tcp bool) (addrs []netip.Addr, err error) {
resources, err := resolve(ctx, d, dns, host, typ, tcp)
func ResolveNetip(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host string, typ dnsmessage.Type, network string) (addrs []netip.Addr, err error) {
resources, err := resolve(ctx, d, dns, host, typ, network)
if err != nil {
return nil, err
}
@ -118,16 +117,14 @@ func ResolveNetip(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, ho
return addrs, nil
}
func ResolveNS(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host string, tcp bool) (records []string, err error) {
func ResolveNS(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host string, network string) (records []string, err error) {
typ := dnsmessage.TypeNS
resources, err := resolve(ctx, d, dns, host, typ, tcp)
resources, err := resolve(ctx, d, dns, host, typ, network)
if err != nil {
return nil, err
}
logrus.Println(host, len(resources))
for _, ans := range resources {
if ans.Header.Type != typ {
logrus.Println(host, ans.Header.Type)
continue
}
ns, ok := ans.Body.(*dnsmessage.NSResource)
@ -139,7 +136,7 @@ func ResolveNS(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host
return records, nil
}
func resolve(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host string, typ dnsmessage.Type, tcp bool) (ans []dnsmessage.Resource, err error) {
func resolve(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host string, typ dnsmessage.Type, network string) (ans []dnsmessage.Resource, err error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
fqdn := host
@ -202,7 +199,11 @@ func resolve(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host st
if err != nil {
return nil, err
}
if tcp {
magicNetwork, err := netproxy.ParseMagicNetwork(network)
if err != nil {
return nil, err
}
if magicNetwork.Network == "tcp" {
// Put DNS request length
buf := pool.Get(2 + len(b))
defer pool.Put(buf)
@ -213,12 +214,7 @@ func resolve(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host st
// Dial and write.
cd := &netproxy.ContextDialer{Dialer: d}
var c netproxy.Conn
if tcp {
c, err = cd.DialTcpContext(ctx, dns.String())
} else {
c, err = cd.DialUdpContext(ctx, dns.String())
}
c, err := cd.DialContext(ctx, network, dns.String())
if err != nil {
return nil, err
}
@ -228,7 +224,7 @@ func resolve(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host st
return nil, err
}
ch := make(chan error, 2)
if !tcp {
if magicNetwork.Network == "udp" {
go func() {
// Resend every 3 seconds for UDP.
for {
@ -249,7 +245,7 @@ func resolve(ctx context.Context, d netproxy.Dialer, dns netip.AddrPort, host st
go func() {
buf := pool.Get(512)
defer pool.Put(buf)
if tcp {
if magicNetwork.Network == "tcp" {
// Read DNS response length
_, err := io.ReadFull(c, buf[:2])
if err != nil {

View File

@ -22,7 +22,7 @@ type Ip46 struct {
Ip6 netip.Addr
}
func ResolveIp46(ctx context.Context, dialer netproxy.Dialer, dns netip.AddrPort, host string, tcp bool, race bool) (ipv46 *Ip46, err error) {
func ResolveIp46(ctx context.Context, dialer netproxy.Dialer, dns netip.AddrPort, host string, network string, race bool) (ipv46 *Ip46, err error) {
var log *logrus.Logger
if _log := ctx.Value("logger"); _log != nil {
log = _log.(*logrus.Logger)
@ -49,7 +49,7 @@ func ResolveIp46(ctx context.Context, dialer netproxy.Dialer, dns netip.AddrPort
}
}()
var e error
addrs4, e = ResolveNetip(ctx4, dialer, dns, host, dnsmessage.TypeA, tcp)
addrs4, e = ResolveNetip(ctx4, dialer, dns, host, dnsmessage.TypeA, network)
if err != nil && !errors.Is(e, context.Canceled) {
err4 = e
return
@ -67,7 +67,7 @@ func ResolveIp46(ctx context.Context, dialer netproxy.Dialer, dns netip.AddrPort
}
}()
var e error
addrs6, e = ResolveNetip(ctx6, dialer, dns, host, dnsmessage.TypeAAAA, tcp)
addrs6, e = ResolveNetip(ctx6, dialer, dns, host, dnsmessage.TypeAAAA, network)
if err != nil && !errors.Is(e, context.Canceled) {
err6 = e
return

View File

@ -137,7 +137,7 @@ func ResolveFile(u *url.URL, configDir string) (b []byte, err error) {
return bytes.TrimSpace(b), err
}
func ResolveSubscription(log *logrus.Logger, configDir string, subscription string) (tag string, nodes []string, err error) {
func ResolveSubscription(log *logrus.Logger, client *http.Client, configDir string, subscription string) (tag string, nodes []string, err error) {
/// Get tag.
tag, subscription = common.GetTagFromLinkLikePlaintext(subscription)
@ -160,7 +160,7 @@ func ResolveSubscription(log *logrus.Logger, configDir string, subscription stri
goto resolve
default:
}
resp, err = http.Get(subscription)
resp, err = client.Get(subscription)
if err != nil {
return "", nil, err
}

View File

@ -12,6 +12,7 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
"github.com/mzz2017/softwind/netproxy"
"net/netip"
"net/url"
"path/filepath"
@ -221,25 +222,25 @@ func FuzzyDecode(to interface{}, val string) bool {
v := reflect.Indirect(reflect.ValueOf(to))
switch v.Kind() {
case reflect.Int:
i, err := strconv.ParseInt(val, 10, strconv.IntSize)
i, err := strconv.ParseInt(val, 0, strconv.IntSize)
if err != nil {
return false
}
v.SetInt(i)
case reflect.Int8:
i, err := strconv.ParseInt(val, 10, 8)
i, err := strconv.ParseInt(val, 0, 8)
if err != nil {
return false
}
v.SetInt(i)
case reflect.Int16:
i, err := strconv.ParseInt(val, 10, 16)
i, err := strconv.ParseInt(val, 0, 16)
if err != nil {
return false
}
v.SetInt(i)
case reflect.Int32:
i, err := strconv.ParseInt(val, 10, 32)
i, err := strconv.ParseInt(val, 0, 32)
if err != nil {
return false
}
@ -253,38 +254,38 @@ func FuzzyDecode(to interface{}, val string) bool {
}
v.Set(reflect.ValueOf(duration))
default:
i, err := strconv.ParseInt(val, 10, 64)
i, err := strconv.ParseInt(val, 0, 64)
if err != nil {
return false
}
v.SetInt(i)
}
case reflect.Uint:
i, err := strconv.ParseUint(val, 10, strconv.IntSize)
i, err := strconv.ParseUint(val, 0, strconv.IntSize)
if err != nil {
return false
}
v.SetUint(i)
case reflect.Uint8:
i, err := strconv.ParseUint(val, 10, 8)
i, err := strconv.ParseUint(val, 0, 8)
if err != nil {
return false
}
v.SetUint(i)
case reflect.Uint16:
i, err := strconv.ParseUint(val, 10, 16)
i, err := strconv.ParseUint(val, 0, 16)
if err != nil {
return false
}
v.SetUint(i)
case reflect.Uint32:
i, err := strconv.ParseUint(val, 10, 32)
i, err := strconv.ParseUint(val, 0, 32)
if err != nil {
return false
}
v.SetUint(i)
case reflect.Uint64:
i, err := strconv.ParseUint(val, 10, 64)
i, err := strconv.ParseUint(val, 0, 64)
if err != nil {
return false
}
@ -458,6 +459,17 @@ nextLink:
return Deduplicate(defaultIfs), nil
}
func MagicNetwork(network string, mark uint32) string {
if mark == 0 {
return network
} else {
return netproxy.MagicNetwork{
Network: network,
Mark: mark,
}.Encode()
}
}
func IsValidHttpMethod(method string) bool {
switch method {
case "GET", "POST", "PUT", "PATCH", "DELETE", "COPY", "HEAD", "OPTIONS", "LINK", "UNLINK", "PURGE", "LOCK", "UNLOCK", "PROPFIND", "CONNECT", "TRACE":

View File

@ -32,9 +32,10 @@ type Dns struct {
}
type NewOption struct {
Logger *logrus.Logger
LocationFinder *assets.LocationFinder
UpstreamReadyCallback func(dnsUpstream *Upstream) (err error)
Logger *logrus.Logger
LocationFinder *assets.LocationFinder
UpstreamReadyCallback func(dnsUpstream *Upstream) (err error)
UpstreamResolverNetwork string
}
func New(dns *config.Dns, opt *NewOption) (s *Dns, err error) {
@ -62,7 +63,8 @@ func New(dns *config.Dns, opt *NewOption) (s *Dns, err error) {
return nil, fmt.Errorf("%w: %v", BadUpstreamFormatError, err)
}
r := &UpstreamResolver{
Raw: u,
Raw: u,
Network: opt.UpstreamResolverNetwork,
FinishInitCallback: func(i int) func(raw *url.URL, upstream *Upstream) (err error) {
return func(raw *url.URL, upstream *Upstream) (err error) {
if opt != nil && opt.UpstreamReadyCallback != nil {
@ -77,6 +79,9 @@ func New(dns *config.Dns, opt *NewOption) (s *Dns, err error) {
return nil
}
}(i),
mu: sync.Mutex{},
upstream: nil,
init: false,
}
upstreamName2Id[tag] = uint8(len(s.upstream))
s.upstream = append(s.upstream, r)

View File

@ -72,7 +72,7 @@ type Upstream struct {
*netutils.Ip46
}
func NewUpstream(ctx context.Context, upstream *url.URL) (up *Upstream, err error) {
func NewUpstream(ctx context.Context, upstream *url.URL, resolverNetwork string) (up *Upstream, err error) {
scheme, hostname, port, err := ParseRawUpstream(upstream)
if err != nil {
return nil, fmt.Errorf("%w: %v", FormatError, err)
@ -88,7 +88,7 @@ func NewUpstream(ctx context.Context, upstream *url.URL) (up *Upstream, err erro
}
}()
ip46, err := netutils.ResolveIp46(ctx, direct.SymmetricDirect, systemDns, hostname, false, false)
ip46, err := netutils.ResolveIp46(ctx, direct.SymmetricDirect, systemDns, hostname, resolverNetwork, false)
if err != nil {
return nil, fmt.Errorf("failed to resolve dns_upstream: %w", err)
}
@ -131,7 +131,8 @@ func (u *Upstream) String() string {
}
type UpstreamResolver struct {
Raw *url.URL
Raw *url.URL
Network string
// FinishInitCallback may be invoked again if err is not nil
FinishInitCallback func(raw *url.URL, upstream *Upstream) (err error)
mu sync.Mutex
@ -154,7 +155,7 @@ func (u *UpstreamResolver) GetUpstream() (_ *Upstream, err error) {
}()
ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second)
defer cancel()
if u.upstream, err = NewUpstream(ctx, u.Raw); err != nil {
if u.upstream, err = NewUpstream(ctx, u.Raw, u.Network); err != nil {
return nil, fmt.Errorf("failed to init dns upstream: %w", err)
}
}

View File

@ -9,6 +9,7 @@ import (
"context"
"errors"
"fmt"
"github.com/daeuniverse/dae/common"
"net"
"net/http"
"net/netip"
@ -121,7 +122,7 @@ type TcpCheckOption struct {
Method string
}
func ParseTcpCheckOption(ctx context.Context, rawURL []string, method string) (opt *TcpCheckOption, err error) {
func ParseTcpCheckOption(ctx context.Context, rawURL []string, method string, resolverNetwork string) (opt *TcpCheckOption, err error) {
if method == "" {
method = http.MethodGet
}
@ -146,7 +147,7 @@ func ParseTcpCheckOption(ctx context.Context, rawURL []string, method string) (o
if len(rawURL) > 1 {
ip46 = parseIp46FromList(rawURL[1:])
} else {
ip46, err = netutils.ResolveIp46(ctx, direct.SymmetricDirect, systemDns, u.Hostname(), false, false)
ip46, err = netutils.ResolveIp46(ctx, direct.SymmetricDirect, systemDns, u.Hostname(), resolverNetwork, false)
if err != nil {
return nil, err
}
@ -164,7 +165,7 @@ type CheckDnsOption struct {
*netutils.Ip46
}
func ParseCheckDnsOption(ctx context.Context, dnsHostPort []string) (opt *CheckDnsOption, err error) {
func ParseCheckDnsOption(ctx context.Context, dnsHostPort []string, resolverNetwork string) (opt *CheckDnsOption, err error) {
systemDns, err := netutils.SystemDns()
if err != nil {
return nil, err
@ -191,7 +192,7 @@ func ParseCheckDnsOption(ctx context.Context, dnsHostPort []string) (opt *CheckD
if len(dnsHostPort) > 1 {
ip46 = parseIp46FromList(dnsHostPort[1:])
} else {
ip46, err = netutils.ResolveIp46(ctx, direct.SymmetricDirect, systemDns, host, false, false)
ip46, err = netutils.ResolveIp46(ctx, direct.SymmetricDirect, systemDns, host, resolverNetwork, false)
if err != nil {
return nil, err
}
@ -204,11 +205,12 @@ func ParseCheckDnsOption(ctx context.Context, dnsHostPort []string) (opt *CheckD
}
type TcpCheckOptionRaw struct {
opt *TcpCheckOption
mu sync.Mutex
Log *logrus.Logger
Raw []string
Method string
opt *TcpCheckOption
mu sync.Mutex
Log *logrus.Logger
Raw []string
ResolverNetwork string
Method string
}
func (c *TcpCheckOptionRaw) Option() (opt *TcpCheckOption, err error) {
@ -218,7 +220,7 @@ func (c *TcpCheckOptionRaw) Option() (opt *TcpCheckOption, err error) {
ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second)
defer cancel()
ctx = context.WithValue(ctx, "logger", c.Log)
tcpCheckOption, err := ParseTcpCheckOption(ctx, c.Raw, c.Method)
tcpCheckOption, err := ParseTcpCheckOption(ctx, c.Raw, c.Method, c.ResolverNetwork)
if err != nil {
return nil, fmt.Errorf("failed to parse tcp_check_url: %w", err)
}
@ -228,9 +230,10 @@ func (c *TcpCheckOptionRaw) Option() (opt *TcpCheckOption, err error) {
}
type CheckDnsOptionRaw struct {
opt *CheckDnsOption
mu sync.Mutex
Raw []string
opt *CheckDnsOption
mu sync.Mutex
Raw []string
ResolverNetwork string
}
func (c *CheckDnsOptionRaw) Option() (opt *CheckDnsOption, err error) {
@ -239,7 +242,7 @@ func (c *CheckDnsOptionRaw) Option() (opt *CheckDnsOption, err error) {
if c.opt == nil {
ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second)
defer cancel()
udpCheckOption, err := ParseCheckDnsOption(ctx, c.Raw)
udpCheckOption, err := ParseCheckDnsOption(ctx, c.Raw, c.ResolverNetwork)
if err != nil {
return nil, fmt.Errorf("failed to parse tcp_check_url: %w", err)
}
@ -266,6 +269,10 @@ func (d *Dialer) ActivateCheck() {
func (d *Dialer) aliveBackground() {
timeout := 10 * time.Second
cycle := d.CheckInterval
var tcpSomark uint32
if network, err := netproxy.ParseMagicNetwork(d.TcpCheckOptionRaw.ResolverNetwork); err == nil {
tcpSomark = network.Mark
}
tcp4CheckOpt := &CheckOption{
networkType: &NetworkType{
L4Proto: consts.L4ProtoStr_TCP,
@ -285,7 +292,7 @@ func (d *Dialer) aliveBackground() {
}).Debugln("Skip check due to no DNS record.")
return false, nil
}
return d.HttpCheck(ctx, opt.Url, opt.Ip4, opt.Method)
return d.HttpCheck(ctx, opt.Url, opt.Ip4, opt.Method, tcpSomark)
},
}
tcp6CheckOpt := &CheckOption{
@ -307,7 +314,7 @@ func (d *Dialer) aliveBackground() {
}).Debugln("Skip check due to no DNS record.")
return false, nil
}
return d.HttpCheck(ctx, opt.Url, opt.Ip6, opt.Method)
return d.HttpCheck(ctx, opt.Url, opt.Ip6, opt.Method, tcpSomark)
},
}
tcp4CheckDnsOpt := &CheckOption{
@ -329,7 +336,7 @@ func (d *Dialer) aliveBackground() {
}).Debugln("Skip check due to no DNS record.")
return false, nil
}
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip4, opt.DnsPort), true)
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip4, opt.DnsPort), d.CheckDnsOptionRaw.ResolverNetwork)
},
}
tcp6CheckDnsOpt := &CheckOption{
@ -351,7 +358,7 @@ func (d *Dialer) aliveBackground() {
}).Debugln("Skip check due to no DNS record.")
return false, nil
}
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip6, opt.DnsPort), true)
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip6, opt.DnsPort), d.CheckDnsOptionRaw.ResolverNetwork)
},
}
udp4CheckDnsOpt := &CheckOption{
@ -372,7 +379,7 @@ func (d *Dialer) aliveBackground() {
}).Debugln("Skip check due to no DNS record.")
return false, nil
}
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip4, opt.DnsPort), false)
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip4, opt.DnsPort), d.CheckDnsOptionRaw.ResolverNetwork)
},
}
udp6CheckDnsOpt := &CheckOption{
@ -393,7 +400,7 @@ func (d *Dialer) aliveBackground() {
}).Debugln("Skip check due to no DNS record.")
return false, nil
}
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip6, opt.DnsPort), false)
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip6, opt.DnsPort), d.CheckDnsOptionRaw.ResolverNetwork)
},
}
var CheckOpts = []*CheckOption{
@ -535,7 +542,7 @@ func (d *Dialer) Check(timeout time.Duration,
return ok, err
}
func (d *Dialer) HttpCheck(ctx context.Context, u *netutils.URL, ip netip.Addr, method string) (ok bool, err error) {
func (d *Dialer) HttpCheck(ctx context.Context, u *netutils.URL, ip netip.Addr, method string, soMark uint32) (ok bool, err error) {
// HTTP(S) check.
if method == "" {
method = http.MethodGet
@ -545,7 +552,7 @@ func (d *Dialer) HttpCheck(ctx context.Context, u *netutils.URL, ip netip.Addr,
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (c net.Conn, err error) {
// Force to dial "ip".
conn, err := cd.DialTcpContext(ctx, net.JoinHostPort(ip.String(), u.Port()))
conn, err := cd.DialContext(ctx, common.MagicNetwork("tcp", soMark), net.JoinHostPort(ip.String(), u.Port()))
if err != nil {
return nil, err
}
@ -584,8 +591,8 @@ func (d *Dialer) HttpCheck(ctx context.Context, u *netutils.URL, ip netip.Addr,
}
}
func (d *Dialer) DnsCheck(ctx context.Context, dns netip.AddrPort, tcp bool) (ok bool, err error) {
addrs, err := netutils.ResolveNetip(ctx, d, dns, consts.UdpCheckLookupHost, dnsmessage.TypeA, tcp)
func (d *Dialer) DnsCheck(ctx context.Context, dns netip.AddrPort, network string) (ok bool, err error) {
addrs, err := netutils.ResolveNetip(ctx, d, dns, consts.UdpCheckLookupHost, dnsmessage.TypeA, network)
if err != nil {
return false, err
}

View File

@ -2,6 +2,7 @@ package trojan
import (
"fmt"
"github.com/daeuniverse/dae/component/outbound/transport/tls"
"net"
"net/url"
"strconv"
@ -9,7 +10,6 @@ import (
"github.com/daeuniverse/dae/common"
"github.com/daeuniverse/dae/component/outbound/dialer"
"github.com/daeuniverse/dae/component/outbound/transport/tls"
"github.com/daeuniverse/dae/component/outbound/transport/ws"
"github.com/mzz2017/softwind/netproxy"
"github.com/mzz2017/softwind/protocol"

View File

@ -63,38 +63,28 @@ func (s *SimpleObfs) Dial(network, addr string) (c netproxy.Conn, err error) {
}
switch magicNetwork.Network {
case "tcp":
return s.DialTcp(addr)
rc, err := s.dialer.Dial(network, s.addr)
if err != nil {
return nil, fmt.Errorf("[simpleobfs]: dial to %s: %w", s.addr, err)
}
host, port, err := net.SplitHostPort(s.addr)
if err != nil {
return nil, err
}
if s.host != "" {
host = s.host
}
switch s.obfstype {
case HTTP:
c = NewHTTPObfs(rc, host, port, s.path)
case TLS:
c = NewTLSObfs(rc, host)
}
return c, err
case "udp":
return s.DialUdp(addr)
return nil, fmt.Errorf("%w: simpleobfs+udp", netproxy.UnsupportedTunnelTypeError)
default:
return nil, fmt.Errorf("%w: %v", netproxy.UnsupportedTunnelTypeError, network)
}
}
func (s *SimpleObfs) DialUdp(addr string) (conn netproxy.PacketConn, err error) {
return nil, fmt.Errorf("%w: simpleobfs+udp", netproxy.UnsupportedTunnelTypeError)
}
// DialTcp connects to the address addr on the network net via the proxy.
func (s *SimpleObfs) DialTcp(addr string) (c netproxy.Conn, err error) {
rc, err := s.dialer.DialTcp(s.addr)
if err != nil {
return nil, fmt.Errorf("[simpleobfs]: dial to %s: %w", s.addr, err)
}
host, port, err := net.SplitHostPort(s.addr)
if err != nil {
return nil, err
}
if s.host != "" {
host = s.host
}
switch s.obfstype {
case HTTP:
c = NewHTTPObfs(rc, host, port, s.path)
case TLS:
c = NewTLSObfs(rc, host)
}
return c, err
}

View File

@ -61,55 +61,47 @@ func (s *Tls) Dial(network, addr string) (c netproxy.Conn, err error) {
}
switch magicNetwork.Network {
case "tcp":
return s.DialTcp(addr)
rc, err := s.dialer.Dial(network, addr)
if err != nil {
return nil, fmt.Errorf("[Tls]: dial to %s: %w", s.addr, err)
}
var tlsConn interface {
netproxy.Conn
Handshake() error
}
switch s.tlsImplentation {
case "tls":
tlsConn = tls.Client(&netproxy.FakeNetConn{
Conn: rc,
LAddr: nil,
RAddr: nil,
}, s.tlsConfig)
case "utls":
clientHelloID, err := nameToUtlsClientHelloID(s.utlsImitate)
if err != nil {
return nil, err
}
tlsConn = utls.UClient(&netproxy.FakeNetConn{
Conn: rc,
LAddr: nil,
RAddr: nil,
}, uTLSConfigFromTLSConfig(s.tlsConfig), *clientHelloID)
default:
return nil, fmt.Errorf("unknown tls implementation: %v", s.tlsImplentation)
}
if err := tlsConn.Handshake(); err != nil {
return nil, err
}
return tlsConn, err
case "udp":
return s.DialUdp(addr)
return nil, fmt.Errorf("%w: tls+udp", netproxy.UnsupportedTunnelTypeError)
default:
return nil, fmt.Errorf("%w: %v", netproxy.UnsupportedTunnelTypeError, network)
}
}
func (s *Tls) DialUdp(addr string) (conn netproxy.PacketConn, err error) {
return nil, fmt.Errorf("%w: tls+udp", netproxy.UnsupportedTunnelTypeError)
}
func (s *Tls) DialTcp(addr string) (conn netproxy.Conn, err error) {
rc, err := s.dialer.DialTcp(addr)
if err != nil {
return nil, fmt.Errorf("[Tls]: dial to %s: %w", s.addr, err)
}
var tlsConn interface {
netproxy.Conn
Handshake() error
}
switch s.tlsImplentation {
case "tls":
tlsConn = tls.Client(&netproxy.FakeNetConn{
Conn: rc,
LAddr: nil,
RAddr: nil,
}, s.tlsConfig)
case "utls":
clientHelloID, err := nameToUtlsClientHelloID(s.utlsImitate)
if err != nil {
return nil, err
}
tlsConn = utls.UClient(&netproxy.FakeNetConn{
Conn: rc,
LAddr: nil,
RAddr: nil,
}, uTLSConfigFromTLSConfig(s.tlsConfig), *clientHelloID)
default:
return nil, fmt.Errorf("unknown tls implementation: %v", s.tlsImplentation)
}
if err := tlsConn.Handshake(); err != nil {
return nil, err
}
return tlsConn, err
}

View File

@ -13,10 +13,10 @@ import (
// Ws is a base Ws struct
type Ws struct {
dialer netproxy.Dialer
wsAddr string
header http.Header
wsDialer *websocket.Dialer
dialer netproxy.Dialer
wsAddr string
header http.Header
tlsClientConfig *tls.Config
}
// NewWs returns a Ws infra.
@ -43,23 +43,9 @@ func NewWs(s string, d netproxy.Dialer) (*Ws, error) {
Host: u.Host,
}
t.wsAddr = wsUrl.String() + u.Path
t.wsDialer = &websocket.Dialer{
NetDial: func(network, addr string) (net.Conn, error) {
c, err := d.DialTcp(addr)
if err != nil {
return nil, err
}
return &netproxy.FakeNetConn{
Conn: c,
LAddr: nil,
RAddr: nil,
}, nil
},
//Subprotocols: []string{"binary"},
}
if u.Scheme == "wss" {
skipVerify, _ := strconv.ParseBool(u.Query().Get("allowInsecure"))
t.wsDialer.TLSClientConfig = &tls.Config{
t.tlsClientConfig = &tls.Config{
ServerName: u.Query().Get("sni"),
InsecureSkipVerify: skipVerify,
}
@ -74,23 +60,28 @@ func (s *Ws) Dial(network, addr string) (c netproxy.Conn, err error) {
}
switch magicNetwork.Network {
case "tcp":
return s.DialTcp(addr)
wsDialer := &websocket.Dialer{
NetDial: func(_, addr string) (net.Conn, error) {
c, err := s.dialer.Dial(network, addr)
if err != nil {
return nil, err
}
return &netproxy.FakeNetConn{
Conn: c,
LAddr: nil,
RAddr: nil,
}, nil
},
//Subprotocols: []string{"binary"},
}
rc, _, err := wsDialer.Dial(s.wsAddr, s.header)
if err != nil {
return nil, fmt.Errorf("[Ws]: dial to %s: %w", s.wsAddr, err)
}
return newConn(rc), err
case "udp":
return s.DialUdp(addr)
return nil, fmt.Errorf("%w: ws+udp", netproxy.UnsupportedTunnelTypeError)
default:
return nil, fmt.Errorf("%w: %v", netproxy.UnsupportedTunnelTypeError, network)
}
}
func (s *Ws) DialUdp(addr string) (netproxy.PacketConn, error) {
return nil, fmt.Errorf("%w: ws+udp", netproxy.UnsupportedTunnelTypeError)
}
// DialTcp connects to the address addr on the network net via the infra.
func (s *Ws) DialTcp(addr string) (netproxy.Conn, error) {
rc, _, err := s.wsDialer.Dial(s.wsAddr, s.header)
if err != nil {
return nil, fmt.Errorf("[Ws]: dial to %s: %w", s.wsAddr, err)
}
return newConn(rc), err
}

View File

@ -14,8 +14,10 @@ import (
)
type Global struct {
TproxyPort uint16 `mapstructure:"tproxy_port" default:"12345"`
LogLevel string `mapstructure:"log_level" default:"info"`
TproxyPort uint16 `mapstructure:"tproxy_port" default:"12345"`
TproxyPortProtect bool `mapstructure:"tproxy_port_protect" default:"true"`
SoMarkFromDae uint32 `mapstructure:"so_mark_from_dae"`
LogLevel string `mapstructure:"log_level" default:"info"`
// We use DirectTcpCheckUrl to check (tcp)*(ipv4/ipv6) connectivity for direct.
//DirectTcpCheckUrl string `mapstructure:"direct_tcp_check_url" default:"http://www.qualcomm.cn/generate_204"`
TcpCheckUrl []string `mapstructure:"tcp_check_url" default:"http://cp.cloudflare.com,1.1.1.1,2606:4700:4700::1111"`

View File

@ -36,6 +36,8 @@ var SectionDescription = map[string]Desc{
var GlobalDesc = Desc{
"tproxy_port": "tproxy port to listen on. It is NOT a HTTP/SOCKS port, and is just used by eBPF program.\nIn normal case, you do not need to use it.",
"tproxy_port_protect": "Set it true to protect tproxy port from unsolicited traffic. Set it false to allow users to use self-managed iptables tproxy rules.",
"so_mark_from_dae": "If not zero, traffic sent from dae will be set SO_MARK. It is useful to avoid traffic loop with iptables tproxy rules.",
"log_level": "Log level: error, warn, info, debug, trace.",
"tcp_check_url": "Node connectivity check.\nHost of URL should have both IPv4 and IPv6 if you have double stack in local.\nConsidering traffic consumption, it is recommended to choose a site with anycast IP and less response.",
"tcp_check_http_method": "The HTTP request method to `tcp_check_url`. Use 'CONNECT' by default because some server implementations bypass accounting for this kind of traffic.",

View File

@ -67,7 +67,9 @@ type ControlPlane struct {
wanInterface []string
lanInterface []string
sniffingTimeout time.Duration
sniffingTimeout time.Duration
tproxyPortProtect bool
soMarkFromDae uint32
}
func NewControlPlane(
@ -226,9 +228,17 @@ func NewControlPlane(
log.Warnln("AllowInsecure is enabled, but it is not recommended. Please make sure you have to turn it on.")
}
option := &dialer.GlobalOption{
Log: log,
TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{Raw: global.TcpCheckUrl, Log: log, Method: global.TcpCheckHttpMethod},
CheckDnsOptionRaw: dialer.CheckDnsOptionRaw{Raw: global.UdpCheckDns},
Log: log,
TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{
Raw: global.TcpCheckUrl,
Log: log,
ResolverNetwork: common.MagicNetwork("udp", global.SoMarkFromDae),
Method: global.TcpCheckHttpMethod,
},
CheckDnsOptionRaw: dialer.CheckDnsOptionRaw{
Raw: global.UdpCheckDns,
ResolverNetwork: common.MagicNetwork("udp", global.SoMarkFromDae),
},
CheckInterval: global.CheckInterval,
CheckTolerance: global.CheckTolerance,
CheckDnsTcp: true,
@ -337,23 +347,25 @@ func NewControlPlane(
ctx, cancel := context.WithCancel(context.Background())
plane := &ControlPlane{
log: log,
core: core,
deferFuncs: deferFuncs,
listenIp: "0.0.0.0",
outbounds: outbounds,
dnsController: nil,
onceNetworkReady: sync.Once{},
dialMode: dialMode,
routingMatcher: routingMatcher,
ctx: ctx,
cancel: cancel,
ready: make(chan struct{}),
muRealDomainSet: sync.Mutex{},
realDomainSet: bloom.NewWithEstimates(2048, 0.001),
lanInterface: global.LanInterface,
wanInterface: global.WanInterface,
sniffingTimeout: sniffingTimeout,
log: log,
core: core,
deferFuncs: deferFuncs,
listenIp: "0.0.0.0",
outbounds: outbounds,
dnsController: nil,
onceNetworkReady: sync.Once{},
dialMode: dialMode,
routingMatcher: routingMatcher,
ctx: ctx,
cancel: cancel,
ready: make(chan struct{}),
muRealDomainSet: sync.Mutex{},
realDomainSet: bloom.NewWithEstimates(2048, 0.001),
lanInterface: global.LanInterface,
wanInterface: global.WanInterface,
sniffingTimeout: sniffingTimeout,
tproxyPortProtect: global.TproxyPortProtect,
soMarkFromDae: global.SoMarkFromDae,
}
defer func() {
if err != nil {
@ -363,9 +375,10 @@ func NewControlPlane(
/// DNS upstream.
dnsUpstream, err := dns.New(dnsConfig, &dns.NewOption{
Logger: log,
LocationFinder: locationFinder,
UpstreamReadyCallback: plane.dnsUpstreamReadyCallback,
Logger: log,
LocationFinder: locationFinder,
UpstreamReadyCallback: plane.dnsUpstreamReadyCallback,
UpstreamResolverNetwork: common.MagicNetwork("udp", global.SoMarkFromDae),
})
if err != nil {
return nil, err
@ -559,7 +572,7 @@ func (c *ControlPlane) ChooseDialTarget(outbound consts.OutboundIndex, dst netip
// TODO: use DNS controller and re-route by control plane.
systemDns, err := netutils.SystemDns()
if err == nil {
if ip46, err := netutils.ResolveIp46(ctx, direct.SymmetricDirect, systemDns, domain, false, true); err == nil && (ip46.Ip4.IsValid() || ip46.Ip6.IsValid()) {
if ip46, err := netutils.ResolveIp46(ctx, direct.SymmetricDirect, systemDns, domain, common.MagicNetwork("udp", c.soMarkFromDae), true); err == nil && (ip46.Ip4.IsValid() || ip46.Ip6.IsValid()) {
// Has A/AAAA records. It is a real domain.
dialMode = consts.DialMode_Domain
// Add it to real-domain set.
@ -717,8 +730,21 @@ func (c *ControlPlane) Serve(readyChan chan<- bool, listener *Listener) (err err
lastErr := err
addrHdr, dataOffset, err := ParseAddrHdr(data)
if err != nil {
c.log.Warnf("No AddrPort presented: %v, %v", lastErr, err)
return
if c.tproxyPortProtect {
c.log.Warnf("No AddrPort presented: %v, %v", lastErr, err)
return
} else {
routingResult = &bpfRoutingResult{
Mark: 0,
Must: 0,
Mac: [6]uint8{},
Outbound: uint8(consts.OutboundControlPlaneRouting),
Pname: [16]uint8{},
Pid: 0,
}
realDst = pktDst
goto destRetrieved
}
}
n := copy(data, data[dataOffset:])
data = data[:n]
@ -731,6 +757,7 @@ func (c *ControlPlane) Serve(readyChan chan<- bool, listener *Listener) (err err
} else {
realDst = pktDst
}
destRetrieved:
if e := c.handlePkt(udpConn, data, common.ConvergeAddrPort(src), common.ConvergeAddrPort(pktDst), common.ConvergeAddrPort(realDst), routingResult); e != nil {
c.log.Warnln("handlePkt:", e)
}
@ -814,6 +841,9 @@ func (c *ControlPlane) chooseBestDnsDialer(
if err != nil {
return nil, err
}
if mark == 0 {
mark = c.soMarkFromDae
}
if int(outboundIndex) >= len(c.outbounds) {
return nil, fmt.Errorf("bad outbound index: %v", outboundIndex)
}

View File

@ -10,6 +10,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"github.com/daeuniverse/dae/common"
"io"
"math"
"net"
@ -645,7 +646,7 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
// TODO: connection pool.
conn, err = dialArgument.bestDialer.Dial(
MagicNetwork("udp", dialArgument.mark),
common.MagicNetwork("udp", dialArgument.mark),
dialArgument.bestTarget.String(),
)
if err != nil {
@ -707,7 +708,7 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
case consts.L4ProtoStr_TCP:
// We can block here because we are in a coroutine.
conn, err = dialArgument.bestDialer.Dial(MagicNetwork("tcp", dialArgument.mark), dialArgument.bestTarget.String())
conn, err = dialArgument.bestDialer.Dial(common.MagicNetwork("tcp", dialArgument.mark), dialArgument.bestTarget.String())
if err != nil {
return fmt.Errorf("failed to dial proxy to tcp: %w", err)
}

View File

@ -64,6 +64,7 @@
#define IS_LAN 1
#define TPROXY_MARK 0x8000000
#define RECOGNIZE 0x2017
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
@ -139,6 +140,7 @@ struct routing_result {
struct dst_routing_result {
__be32 ip[4];
__be16 port;
__u16 recognize;
struct routing_result routing_result;
};
@ -1751,6 +1753,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
__builtin_memset(&new_hdr, 0, sizeof(new_hdr));
__builtin_memcpy(new_hdr.ip, &tuples.dip, IPV6_BYTE_LENGTH);
new_hdr.port = udph.dest;
new_hdr.recognize = RECOGNIZE;
new_hdr.routing_result.outbound = s64_ret;
new_hdr.routing_result.mark = s64_ret >> 8;
new_hdr.routing_result.must = (s64_ret >> 40) & 1;

View File

@ -50,7 +50,19 @@ func (c *ControlPlane) handleConn(lConn net.Conn) (err error) {
Ip: struct{ U6Addr8 [16]uint8 }{U6Addr8: ip6},
Port: common.Htons(src.Port()),
}, &value); e != nil {
return fmt.Errorf("failed to retrieve target info %v: %v, %v", src.String(), err, e)
if c.tproxyPortProtect {
return fmt.Errorf("failed to retrieve target info %v: %v, %v", src.String(), err, e)
} else {
routingResult = &bpfRoutingResult{
Mark: 0,
Must: 0,
Mac: [6]uint8{},
Outbound: uint8(consts.OutboundControlPlaneRouting),
Pname: [16]uint8{},
Pid: 0,
}
goto destRetrieved
}
}
routingResult = &value.RoutingResult
@ -60,6 +72,7 @@ func (c *ControlPlane) handleConn(lConn net.Conn) (err error) {
}
dst = netip.AddrPortFrom(dstAddr, common.Htons(value.Port))
}
destRetrieved:
src = common.ConvergeAddrPort(src)
dst = common.ConvergeAddrPort(dst)
@ -92,6 +105,9 @@ func (c *ControlPlane) handleConn(lConn net.Conn) (err error) {
dialTarget, _ = c.ChooseDialTarget(outboundIndex, dst, domain)
default:
}
if routingResult.Mark == 0 {
routingResult.Mark = c.soMarkFromDae
}
// TODO: Set-up ip to domain mapping and show domain if possible.
if outboundIndex < 0 || int(outboundIndex) >= len(c.outbounds) {
return fmt.Errorf("outbound id from bpf is out of range: %v not in [0, %v]", outboundIndex, len(c.outbounds)-1)
@ -122,7 +138,7 @@ func (c *ControlPlane) handleConn(lConn net.Conn) (err error) {
}
// Dial and relay.
rConn, err := d.Dial(MagicNetwork("tcp", routingResult.Mark), dialTarget)
rConn, err := d.Dial(common.MagicNetwork("tcp", routingResult.Mark), dialTarget)
if err != nil {
return fmt.Errorf("failed to dial %v: %w", dst, err)
}

View File

@ -48,6 +48,9 @@ func ParseAddrHdr(data []byte) (hdr *bpfDstRoutingResult, dataOffset int, err er
return nil, 0, fmt.Errorf("data is too short to parse AddrHdr")
}
_hdr := *(*bpfDstRoutingResult)(unsafe.Pointer(&data[0]))
if _hdr.Recognize != consts.Recognize {
return nil, 0, fmt.Errorf("bad recognize")
}
_hdr.Port = common.Ntohs(_hdr.Port)
return &_hdr, dataOffset, nil
}
@ -173,6 +176,9 @@ func (c *ControlPlane) handlePkt(lConn *net.UDPConn, data []byte, src, pktDst, r
dialTarget, _ = c.ChooseDialTarget(outboundIndex, realDst, domain)
default:
}
if routingResult.Mark == 0 {
routingResult.Mark = c.soMarkFromDae
}
if isDns {
return c.dnsController.Handle_(dnsMessage, &udpRequest{
lanWanFlag: lanWanFlag,
@ -226,7 +232,7 @@ getNew:
},
NatTimeout: natTimeout,
Dialer: dialerForNew,
Network: MagicNetwork("udp", routingResult.Mark),
Network: common.MagicNetwork("udp", routingResult.Mark),
Target: dialTarget,
})
if err != nil {

View File

@ -16,7 +16,6 @@ import (
"github.com/daeuniverse/dae/common"
"github.com/daeuniverse/dae/common/consts"
"github.com/mzz2017/softwind/netproxy"
"golang.org/x/sys/unix"
)
@ -160,17 +159,6 @@ func SetSendRedirects(ifname string, val string) {
_ = setSendRedirects(ifname, consts.IpVersionStr_4, val)
}
func MagicNetwork(network string, mark uint32) string {
if mark == 0 {
return network
} else {
return netproxy.MagicNetwork{
Network: network,
Mark: mark,
}.Encode()
}
}
func ProcessName2String(pname []uint8) string {
return string(bytes.TrimRight(pname[:], string([]byte{0})))
}

View File

@ -90,6 +90,18 @@ sudo systemctl start dae
sudo systemctl enable dae
```
### Gentoo Linux
dae has been released on [gentoo-zh](https://github.com/microcai/gentoo-zh)
use `app-eselect/eselect-repository` to enable this overlay:
```shell
eselect repository enable gentoo-zh
emaint sync -r gentoo-zh
emerge -a net-proxy/dae
```
### macOS
We provide a hacky way to run dae on your macOS. See [run on macOS](run-on-macos.md).

View File

@ -86,6 +86,16 @@ sudo systemctl start dae
sudo systemctl enable dae
```
### Gentoo Linux
dae 已发布于 [gentoo-zh](https://github.com/microcai/gentoo-zh),可以使用 `app-eselect/eselect-repository` 启用此 overlay:
```shell
eselect repository enable gentoo-zh
emaint sync -r gentoo-zh
emerge -a net-proxy/dae
```
### macOS
我们提供了一种比较 hacky 的方式在 macOS 上运行 dae见 [run on macOS](run-on-macos.md)。

View File

@ -84,10 +84,8 @@ domain(geosite:geolocation-!cn) &&
domain(ext:"yourdatfile.dat:yourtag")->direct
dip(ext:"yourdatfile.dat:yourtag")->direct
### Mark for direct/must_direct outbound
# Mark is useful when you want to redirect traffic to specific interface (such as wireguard) or other advanced uses.
# Traffic from LAN will not be forwarded by dae to archive higher performance if lan_nat_direct is off (you can set it
# off only if you are sure dae is on a bridge device).
### Set fwmark
# Mark is useful when you want to redirect traffic to specific interface (such as wireguard) or for other advanced uses.
# An example of redirecting Disney traffic to wg0 is given here.
# You need set ip rule and ip table like this:

View File

@ -5,6 +5,14 @@ global {
# In normal case, you do not need to use it.
tproxy_port: 12345
# Set it true to protect tproxy port from unsolicited traffic. Set it false to allow users to use self-managed
# iptables tproxy rules.
tproxy_port_protect: true
# If not zero, traffic sent from dae will be set SO_MARK. It is useful to avoid traffic loop with iptables tproxy
# rules.
so_mark_from_dae: 0
# Log level: error, warn, info, debug, trace.
log_level: info

2
go.mod
View File

@ -11,7 +11,7 @@ require (
github.com/gorilla/websocket v1.5.0
github.com/json-iterator/go v1.1.12
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/mzz2017/softwind v0.0.0-20230501115403-98d9a7116d72
github.com/mzz2017/softwind v0.0.0-20230530150701-b78490a65b9f
github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd
github.com/safchain/ethtool v0.0.0-20230116090318-67cc41908669
github.com/sirupsen/logrus v1.9.0

4
go.sum
View File

@ -78,8 +78,8 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mzz2017/disk-bloom v1.0.1 h1:rEF9MiXd9qMW3ibRpqcerLXULoTgRlM21yqqJl1B90M=
github.com/mzz2017/disk-bloom v1.0.1/go.mod h1:JLHETtUu44Z6iBmsqzkOtFlRvXSlKnxjwiBRDapizDI=
github.com/mzz2017/softwind v0.0.0-20230501115403-98d9a7116d72 h1:h6xMzLtz5pW24T8E+GSdNJ9lRYh5cDpgL85d5c3/om0=
github.com/mzz2017/softwind v0.0.0-20230501115403-98d9a7116d72/go.mod h1:V8GFOtdpTgzCJtCVXRqjmdDsY+PIhCCx4JpD0zq8Z7I=
github.com/mzz2017/softwind v0.0.0-20230530150701-b78490a65b9f h1:wrOAYscIeug7HZHS7LoOY+hBs4qk48twIxormhOD/Rc=
github.com/mzz2017/softwind v0.0.0-20230530150701-b78490a65b9f/go.mod h1:1hn+ZsP9fLm17yEx0jyqxHkIKm5u2gBXVGIrnKsE1fY=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=

View File

@ -86,10 +86,18 @@ func (w *Walker) parseFunctionPrototype(ctx *dae_config.FunctionPrototypeContext
children := ctx.GetChildren()
not := false
offset := 0
if len(children) == 0 {
w.ReportError(ctx, ErrorType_Unsupported, "bad function prototype expression")
return nil
}
if children[0].(*antlr.TerminalNodeImpl).GetText() == "!" {
offset++
not = true
}
if len(children) <= offset+2 {
w.ReportError(ctx, ErrorType_Unsupported, "bad function prototype expression")
return nil
}
funcName := children[offset+0].(*antlr.TerminalNodeImpl).GetText()
paramList := children[offset+2].(*dae_config.OptParameterListContext)
children = paramList.GetChildren()
@ -151,6 +159,10 @@ func (p *literalExpressionParser) Parse(ctx *dae_config.LiteralExpressionContext
func (w *Walker) parseDeclaration(ctx dae_config.IDeclarationContext) *Param {
children := ctx.GetChildren()
if len(children) < 3 {
w.ReportError(ctx, ErrorType_Unsupported, "bad declaration expression")
return nil
}
key := children[0].(*antlr.TerminalNodeImpl).GetText()
switch valueCtx := children[2].(type) {
case *dae_config.LiteralExpressionContext:
@ -202,6 +214,10 @@ func (w *Walker) parseFunctionPrototypeExpression(ctx dae_config.IFunctionProtot
func (w *Walker) parseRoutingRule(ctx dae_config.IRoutingRuleContext) *RoutingRule {
children := ctx.GetChildren()
if len(children) < 3 {
w.ReportError(ctx, ErrorType_Unsupported, "bad routing rule expression")
return nil
}
//logrus.Debugln(ctx.GetText(), children)
functionList, ok := children[0].(*dae_config.FunctionPrototypeExpressionContext)
if !ok {