Compare commits

...

41 Commits
0.0.0 ... main

Author SHA1 Message Date
57cc17f8ac asd
asd
2025-02-28 20:13:00 -05:00
468bc28482 dummy file 2025-02-28 20:12:12 -05:00
78f964e9c4 add paths-ignore 2025-02-15 06:34:53 +03:00
552b175a48 checks for merge requests
All checks were successful
Build and Package OPKG / Build for "armv5-3.2" (push) Successful in 58s
Build and Package OPKG / Build for "armv7-2.6" (push) Successful in 59s
Build and Package OPKG / Build for "armv7-3.2" (push) Successful in 58s
Build and Package OPKG / Build for "aarch64-3.10" (push) Successful in 58s
Build and Package OPKG / Build for "mips-3.4" (push) Successful in 58s
Build and Package OPKG / Build for "mipsel-3.4" (push) Successful in 58s
2025-02-15 06:29:59 +03:00
4de4f1745f fix ci
All checks were successful
Build and Package OPKG / Build for "armv5-3.2" (push) Successful in 1m1s
Build and Package OPKG / Build for "armv7-2.6" (push) Successful in 1m0s
Build and Package OPKG / Build for "armv7-3.2" (push) Successful in 1m0s
Build and Package OPKG / Build for "armv5-3.2" (release) Successful in 1m21s
Build and Package OPKG / Build for "armv7-2.6" (release) Successful in 1m20s
Build and Package OPKG / Build for "armv7-3.2" (release) Successful in 1m18s
Build and Package OPKG / Build for "aarch64-3.10" (release) Successful in 1m20s
Build and Package OPKG / Build for "mips-3.4" (release) Successful in 1m22s
Build and Package OPKG / Build for "mipsel-3.4" (release) Successful in 1m17s
Build and Package OPKG / Build for "aarch64-3.10" (push) Successful in 59s
Build and Package OPKG / Build for "mipsel-3.4" (push) Successful in 59s
Build and Package OPKG / Build for "mips-3.4" (push) Successful in 58s
2025-02-15 06:25:47 +03:00
87ee5f5f9b Merge pull request 'Исправление CI сборок' (#2) from feature/pr_ci into main
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (release) Successful in 1m10s
Build and Package OPKG / Build for armv5-3.2 (release) Successful in 1m4s
Build and Package OPKG / Build for armv7-2.6 (release) Successful in 1m10s
Build and Package OPKG / Build for armv7-3.2 (release) Successful in 1m10s
Build and Package OPKG / Build for mips-3.4 (release) Successful in 1m10s
Build and Package OPKG / Build for mipsel-3.4 (release) Successful in 1m4s
2025-02-14 17:35:59 -05:00
e8d53872b0 upload only for releases 2025-02-15 01:35:01 +03:00
c7dac46d67 fix arch name 2025-02-15 01:18:24 +03:00
cc8d3ede3c place version in filename
Some checks failed
Build and Package OPKG / Build for aarch64-3.10 (push) Failing after 50s
Build and Package OPKG / Build for armv5-3.2 (push) Failing after 43s
Build and Package OPKG / Build for armv7-2.6 (push) Failing after 44s
Build and Package OPKG / Build for armv7-3.2 (push) Failing after 41s
Build and Package OPKG / Build for mips-3.4 (push) Failing after 49s
Build and Package OPKG / Build for mipsel-3.4 (push) Failing after 46s
2025-02-15 01:10:28 +03:00
867788dc4c ignore tags
Some checks failed
Build and Package OPKG / Build for armv5-3.2 (push) Has been cancelled
Build and Package OPKG / Build for aarch64-3.10 (push) Has been cancelled
Build and Package OPKG / Build for armv7-2.6 (push) Has been cancelled
Build and Package OPKG / Build for armv7-3.2 (push) Has been cancelled
Build and Package OPKG / Build for mips-3.4 (push) Has been cancelled
Build and Package OPKG / Build for mipsel-3.4 (push) Has been cancelled
2025-02-15 01:05:46 +03:00
1ab11fa899 test release
Some checks failed
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 51s
Build and Package OPKG / Build for armv7-2.6 (push) Has been cancelled
Build and Package OPKG / Build for armv7-3.2 (push) Has been cancelled
Build and Package OPKG / Build for armv5-3.2 (push) Has been cancelled
Build and Package OPKG / Build for mips-3.4 (push) Has been cancelled
Build and Package OPKG / Build for mipsel-3.4 (push) Has been cancelled
Build and Package OPKG / Build for aarch64-3.10 (release) Successful in 1m17s
Build and Package OPKG / Build for armv7-2.6 (release) Has been cancelled
Build and Package OPKG / Build for armv7-3.2 (release) Has been cancelled
Build and Package OPKG / Build for mips-3.4 (release) Has been cancelled
Build and Package OPKG / Build for mipsel-3.4 (release) Has been cancelled
Build and Package OPKG / Build for armv5-3.2 (release) Has been cancelled
2025-02-15 00:58:29 +03:00
9926cfa0fb downgrading actions/setup-go
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 57s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 49s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 45s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 45s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 43s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 43s
2025-02-15 00:48:11 +03:00
7833bc3db4 build nightly versions
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 55s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 46s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 44s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 46s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 46s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 48s
2025-02-15 00:40:06 +03:00
965b4e6718 fix tests 2025-02-15 00:03:02 +03:00
c6831f98e0 clear stage
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 50s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 42s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 42s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 42s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 45s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 41s
2025-02-14 20:55:47 +03:00
891f6ee7c2 fix dns routing 2025-02-14 20:55:37 +03:00
bfe6a00589 set executable flag
All checks were successful
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 58s
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 41s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 57s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 57s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 58s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 57s
2025-02-14 17:43:12 +03:00
4c49d2ff10 update readme
Some checks failed
Build and Package OPKG / Build for mips-3.4 (push) Waiting to run
Build and Package OPKG / Build for mipsel-3.4 (push) Waiting to run
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 55s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 43s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 40s
Build and Package OPKG / Build for armv7-3.2 (push) Has been cancelled
2025-02-14 17:40:14 +03:00
1967d4e0dc new naming
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 2m5s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 53s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 55s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 53s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 55s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 55s
2025-02-14 17:16:20 +03:00
6a2c5e6c11 remove mkdir
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 1m2s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 1m1s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 1m1s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 58s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 56s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 57s
2025-02-14 05:57:55 +03:00
61905052d9 fix example
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 59s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 55s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 54s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 55s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 56s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 55s
2025-02-14 04:49:45 +03:00
b16f7fc876 fix typing
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 56s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 42s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 41s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 41s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 41s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 44s
2025-02-14 04:30:02 +03:00
baab8eca40 add logging using table and mark
All checks were successful
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 44s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 47s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 41s
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 57s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 41s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 40s
2025-02-14 03:32:01 +03:00
07c07b6aba config refactoring
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 45s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 41s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 40s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 41s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 41s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 40s
2025-02-14 03:17:43 +03:00
5e909ac18d fix file permission
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 49s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 41s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 40s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 48s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 45s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 39s
2025-02-14 01:36:02 +03:00
a65599eafa disable compression
Some checks failed
Build and Package OPKG / Build for armv7-3.2 (push) Waiting to run
Build and Package OPKG / Build for mips-3.4 (push) Waiting to run
Build and Package OPKG / Build for mipsel-3.4 (push) Waiting to run
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 44s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 47s
Build and Package OPKG / Build for armv7-2.6 (push) Has been cancelled
2025-02-14 01:33:33 +03:00
37a2cac335 fix ci name
Some checks failed
Build and Package OPKG / Build for armv7-2.6 (push) Waiting to run
Build and Package OPKG / Build for armv7-3.2 (push) Waiting to run
Build and Package OPKG / Build for mips-3.4 (push) Waiting to run
Build and Package OPKG / Build for mipsel-3.4 (push) Waiting to run
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 41s
Build and Package OPKG / Build for armv5-3.2 (push) Has been cancelled
2025-02-14 01:32:30 +03:00
2371abc9fd destroy ipset
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 53s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 44s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 42s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 41s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 44s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 43s
2025-02-14 01:24:52 +03:00
fb60f838bc change path 2025-02-14 01:16:18 +03:00
43a13664f2 change section 2025-02-14 01:15:37 +03:00
258fa0a275 service 2025-02-14 01:15:27 +03:00
f50101ac97 fetch tags with another way
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 43s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 44s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 46s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 43s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 43s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 45s
2025-02-14 00:51:31 +03:00
41a25ad095 fetch tags directly
Some checks failed
Build and Package OPKG / Build for armv7-3.2 (push) Waiting to run
Build and Package OPKG / Build for mipsel-3.4 (push) Waiting to run
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 41s
Build and Package OPKG / Build for mips-3.4 (push) Waiting to run
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 49s
Build and Package OPKG / Build for armv7-2.6 (push) Has been cancelled
2025-02-14 00:49:54 +03:00
1063815a10 i hate github actions and their documentation...
Some checks failed
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 45s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 41s
Build and Package OPKG / Build for armv7-3.2 (push) Has been cancelled
Build and Package OPKG / Build for mips-3.4 (push) Has been cancelled
Build and Package OPKG / Build for mipsel-3.4 (push) Has been cancelled
Build and Package OPKG / Build for armv7-2.6 (push) Has been cancelled
2025-02-14 00:46:28 +03:00
e4cecf6022 fix arch
All checks were successful
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 43s
Build and Package OPKG / Build for armv5-3.2 (push) Successful in 41s
Build and Package OPKG / Build for armv7-2.6 (push) Successful in 44s
Build and Package OPKG / Build for armv7-3.2 (push) Successful in 42s
Build and Package OPKG / Build for mips-3.4 (push) Successful in 43s
Build and Package OPKG / Build for mipsel-3.4 (push) Successful in 42s
2025-02-14 00:37:06 +03:00
a9ee952b0f fix arch
Some checks failed
Build and Package OPKG / Build for armv5-3.2 (push) Waiting to run
Build and Package OPKG / Build for armv7 (push) Waiting to run
Build and Package OPKG / Build for armv7-2.6 (push) Waiting to run
Build and Package OPKG / Build for armv7-3.2 (push) Waiting to run
Build and Package OPKG / Build for mips (push) Waiting to run
Build and Package OPKG / Build for mips-3.4 (push) Waiting to run
Build and Package OPKG / Build for mipsel (push) Waiting to run
Build and Package OPKG / Build for mipsel-3.4 (push) Waiting to run
Build and Package OPKG / Build for aarch64 (push) Successful in 52s
Build and Package OPKG / Build for aarch64-3.10 (push) Successful in 42s
Build and Package OPKG / Build for armv5 (push) Has been cancelled
2025-02-14 00:34:58 +03:00
97992a6e5e fix path
All checks were successful
Build and Package OPKG / Build aarch64 (push) Successful in 42s
Build and Package OPKG / Build armv5 (push) Successful in 40s
Build and Package OPKG / Build armv7 (push) Successful in 47s
Build and Package OPKG / Build mips (push) Successful in 44s
Build and Package OPKG / Build mipsel (push) Successful in 44s
2025-02-14 00:26:47 +03:00
3dd0003016 using tar instead of ar
Some checks failed
Build and Package OPKG / Build aarch64 (push) Failing after 48s
Build and Package OPKG / Build armv5 (push) Failing after 43s
Build and Package OPKG / Build armv7 (push) Failing after 44s
Build and Package OPKG / Build mips (push) Failing after 43s
Build and Package OPKG / Build mipsel (push) Failing after 42s
2025-02-14 00:23:51 +03:00
4be873beb3 compress with .tar.gz
All checks were successful
Build and Package OPKG / Build aarch64 (push) Successful in 49s
Build and Package OPKG / Build armv5 (push) Successful in 41s
Build and Package OPKG / Build armv7 (push) Successful in 40s
Build and Package OPKG / Build mips (push) Successful in 40s
Build and Package OPKG / Build mipsel (push) Successful in 41s
2025-02-14 00:15:07 +03:00
d223d7e22e fix make file
All checks were successful
Build and Package OPKG / Build aarch64 (push) Successful in 46s
Build and Package OPKG / Build armv5 (push) Successful in 39s
Build and Package OPKG / Build armv7 (push) Successful in 45s
Build and Package OPKG / Build mips (push) Successful in 44s
Build and Package OPKG / Build mipsel (push) Successful in 42s
2025-02-14 00:07:11 +03:00
1a47e0f817 fetch tags
Some checks failed
Build and Package OPKG / Build aarch64 (push) Successful in 41s
Build and Package OPKG / Build armv7 (push) Has been cancelled
Build and Package OPKG / Build mips (push) Has been cancelled
Build and Package OPKG / Build mipsel (push) Has been cancelled
Build and Package OPKG / Build armv5 (push) Has been cancelled
2025-02-14 00:02:28 +03:00
21 changed files with 724 additions and 371 deletions

90
.github/workflows/build-for-release.yml vendored Normal file
View File

@ -0,0 +1,90 @@
name: Build and Package OPKG
on:
push:
branches:
- develop
- main
paths-ignore:
- '**.md'
- '.github/**'
- '!.github/workflows/build-for-release.yml'
release:
types: [published]
jobs:
build:
name: Build for "${{ matrix.target }}"
runs-on: ubuntu-latest
strategy:
matrix:
include:
- target: mipsel-3.4
goos: linux
goarch: mipsle
gomips: softfloat
- target: mips-3.4
goos: linux
goarch: mips
gomips: softfloat
- target: aarch64-3.10
goos: linux
goarch: arm64
- target: armv7-3.2
goos: linux
goarch: arm
goarm: 7
- target: armv7-2.6
goos: linux
goarch: arm
goarm: 7
- target: armv5-3.2
goos: linux
goarch: arm
goarm: 5
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.21'
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y fakeroot
- name: Build and Package
run: |
TARGET=${{ matrix.target }} GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} GOMIPS=${{ matrix.gomips }} GOARM=${{ matrix.goarm }} make
- name: Fetching file list for artifact
if: ${{ github.event_name != 'release' }}
id: create_file_list
run: |
echo 'file_list<<EOF' >> $GITHUB_OUTPUT
find . -name "magitrickle_*_${{ matrix.target }}.ipk" -print >> $GITHUB_OUTPUT
echo 'EOF' >> $GITHUB_OUTPUT
- name: Upload artifact
if: ${{ github.event_name != 'release' }}
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.target }}
path: ${{ steps.create_file_list.outputs.file_list }}
if-no-files-found: error
- name: Upload asset to release
if: ${{ github.event_name == 'release' }}
uses: https://gitea.com/actions/release-action@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
files: |-
./.build/magitrickle_*_${{ matrix.target }}.ipk
api_key: '${{secrets.RELEASE_TOKEN}}'

View File

@ -1,58 +0,0 @@
name: Build and Package OPKG
on:
push:
branches:
- main
jobs:
build:
name: Build ${{ matrix.arch }}
runs-on: ubuntu-latest
strategy:
matrix:
arch: [mipsel, mips, aarch64, armv7, armv5]
include:
- arch: mipsel
goos: linux
goarch: mipsle
gomips: softfloat
- arch: mips
goos: linux
goarch: mips
gomips: softfloat
- arch: aarch64
goos: linux
goarch: arm64
- arch: armv7
goos: linux
goarch: arm
goarm: 7
- arch: armv5
goos: linux
goarch: arm
goarm: 5
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: false
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y fakeroot
- name: Build and Package
run: |
ARCH=${{ matrix.arch }} GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} GOMIPS=${{ matrix.gomips }} GOARM=${{ matrix.goarm }} make
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: kvas2_${{ matrix.arch }}.ipk
path: .build/kvas2_${{ matrix.arch }}.ipk

57
.github/workflows/check.yml vendored Normal file
View File

@ -0,0 +1,57 @@
name: Checking
on:
pull_request:
branches:
- develop
- main
paths-ignore:
- '**.md'
- '.github/**'
- '!.github/workflows/check.yml'
jobs:
check:
name: Finding suspicious constructs for ${{ matrix.arch }}
runs-on: ubuntu-latest
strategy:
matrix:
include:
- arch: mipsel-3.4
goos: linux
goarch: mipsle
gomips: softfloat
- arch: mips-3.4
goos: linux
goarch: mips
gomips: softfloat
- arch: aarch64-3.10
goos: linux
goarch: arm64
- arch: armv7-3.2
goos: linux
goarch: arm
goarm: 7
- arch: armv7-2.6
goos: linux
goarch: arm
goarm: 7
- arch: armv5-3.2
goos: linux
goarch: arm
goarm: 5
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.21'
- name: Finding suspicious constructs
run: |
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} GOMIPS=${{ matrix.gomips }} GOARM=${{ matrix.goarm }} go vet ./...

View File

@ -1,5 +1,5 @@
# Contributors # Коллабораторы
## Consultants ## Консультация
- **nesteroff561** - [GitHub](https://github.com/nesteroff561) - "Help with understanding `iptables`" - **nesteroff561** ([GitHub](https://github.com/nesteroff561)) - Помощь с `iptables`

View File

@ -1,41 +1,55 @@
APP_NAME = kvas2 APP_NAME = magitrickle
APP_DESCRIPTION = DNS-based routing application APP_DESCRIPTION = DNS-based routing application
APP_MAINTAINER = Vladimir Avtsenov <vladimir.lsk.cool@gmail.com> APP_MAINTAINER = Vladimir Avtsenov <vladimir.lsk.cool@gmail.com>
TAG = $(shell git describe --tags --abbrev=0)
COMMIT = $(shell git rev-parse --short HEAD) COMMIT = $(shell git rev-parse --short HEAD)
COMMITS_SINCE_TAG = $(shell git rev-list ${TAG}..HEAD --count || echo "0") UPSTREAM_VERSION = $(shell git describe --tags --abbrev=0 2> /dev/null || echo "0.0.0")
VERSION ?= $(TAG) PKG_REVISION ?= 1
ARCH ?= mipsel TAG = $(shell git describe --tags --abbrev=0 2> /dev/null)
COMMITS_SINCE_TAG = $(shell git rev-list ${TAG}..HEAD --count 2>/dev/null)
PRERELEASE_POSTFIX =
PRERELEASE_DATE = $(shell date +%Y%m%d)
ifneq ($(TAG),)
ifneq ($(COMMITS_SINCE_TAG), 0)
PRERELEASE_POSTFIX = ~git$(PRERELEASE_DATE).$(COMMIT)
endif
else
PRERELEASE_POSTFIX = ~git$(PRERELEASE_DATE).$(COMMIT)
endif
TARGET ?= mipsel-3.4
GOOS ?= linux GOOS ?= linux
GOARCH ?= mipsle GOARCH ?= mipsle
GOMIPS ?= softfloat GOMIPS ?= softfloat
GOARM ?= GOARM ?=
BUILD_DIR = ./.build BUILD_DIR = ./.build
PKG_DIR = $(BUILD_DIR)/$(ARCH) PKG_DIR = $(BUILD_DIR)/$(TARGET)
BIN_DIR = $(PKG_DIR)/data/opt/usr/bin BIN_DIR = $(PKG_DIR)/data/opt/bin
PARAMS = -v -a -trimpath -ldflags="-X 'kvas2/constant.Version=$(VERSION)' -X 'kvas2/constant.Commit=$(COMMIT)' -w -s" PARAMS = -v -a -trimpath -ldflags="-X 'magitrickle/constant.Version=$(UPSTREAM_VERSION)$(PRERELEASE_POSTFIX)' -X 'magitrickle/constant.Commit=$(COMMIT)' -w -s"
all: build_daemon package all: clear build_daemon package
clear:
echo $(shell git rev-parse --abbrev-ref HEAD)
rm -rf $(PKG_DIR)
build_daemon: build_daemon:
GOOS=$(GOOS) GOARCH=$(GOARCH) GOMIPS=$(GOMIPS) GOARM=$(GOARM) go build $(PARAMS) -o $(BIN_DIR)/kvas2d ./cmd/kvas2d GOOS=$(GOOS) GOARCH=$(GOARCH) GOMIPS=$(GOMIPS) GOARM=$(GOARM) go build $(PARAMS) -o $(BIN_DIR)/magitrickled ./cmd/magitrickled
package: package:
@mkdir -p $(PKG_DIR)/control @mkdir -p $(PKG_DIR)/control
@echo '2.0' > $(PKG_DIR)/debian-binary @echo '2.0' > $(PKG_DIR)/debian-binary
@echo 'Package: $(APP_NAME)' > $(PKG_DIR)/control/control @echo 'Package: $(APP_NAME)' > $(PKG_DIR)/control/control
@echo 'Version: $(VERSION)-$(COMMITS_SINCE_TAG)' >> $(PKG_DIR)/control/control @echo 'Version: $(UPSTREAM_VERSION)$(PRERELEASE_POSTFIX)-$(PKG_REVISION)' >> $(PKG_DIR)/control/control
@echo 'Architecture: $(ARCH)' >> $(PKG_DIR)/control/control @echo 'Architecture: $(TARGET)' >> $(PKG_DIR)/control/control
@echo 'Maintainer: $(APP_MAINTAINER)' >> $(PKG_DIR)/control/control @echo 'Maintainer: $(APP_MAINTAINER)' >> $(PKG_DIR)/control/control
@echo 'Description: $(APP_DESCRIPTION)' >> $(PKG_DIR)/control/control @echo 'Description: $(APP_DESCRIPTION)' >> $(PKG_DIR)/control/control
@echo 'Section: base' >> $(PKG_DIR)/control/control @echo 'Section: net' >> $(PKG_DIR)/control/control
@echo 'Priority: optional' >> $(PKG_DIR)/control/control @echo 'Priority: optional' >> $(PKG_DIR)/control/control
@echo 'Depends: libc, iptables, socat' >> $(PKG_DIR)/control/control @echo 'Depends: libc, iptables, socat' >> $(PKG_DIR)/control/control
@mkdir -p $(PKG_DIR)/data/opt/usr/bin
@cp -r ./opt $(PKG_DIR)/data/ @cp -r ./opt $(PKG_DIR)/data/
@fakeroot sh -c "tar -C $(PKG_DIR)/control -cvf $(PKG_DIR)/control.tar ." @fakeroot sh -c "tar -C $(PKG_DIR)/control -czvf $(PKG_DIR)/control.tar.gz ."
@fakeroot sh -c "tar -C $(PKG_DIR)/data -cvf $(PKG_DIR)/data.tar ." @fakeroot sh -c "tar -C $(PKG_DIR)/data -czvf $(PKG_DIR)/data.tar.gz ."
@ar r $(BUILD_DIR)/$(APP_NAME)_$(ARCH).ipk $(PKG_DIR)/debian-binary $(PKG_DIR)/control.tar $(PKG_DIR)/data.tar @tar -C $(PKG_DIR) -czvf $(BUILD_DIR)/$(APP_NAME)_$(UPSTREAM_VERSION)$(PRERELEASE_POSTFIX)-$(PKG_REVISION)_$(TARGET).ipk ./debian-binary ./control.tar.gz ./data.tar.gz

131
README.md
View File

@ -1,3 +1,130 @@
# kvas2 # MagiTrickle
Better implementation of [KVAS](https://github.com/qzeleza/kvas) MagiTrickle - Маршрутизация трафика на основе DNS запросов для роутеров Keenetic (под управлением [Entware](https://github.com/The-BB/Entware-Keenetic)).
*(Продукт в данный момент находится в состоянии разработки)*
Данное программное обеспечение реализует маршрутизацию трафика на основе проксирования через себя DNS запросов. Можно указать список доменных имён, которые нужно маршрутизировать на тот, или иной интерфейс, вместо бесконечного накопления IP адресов.
### Особенности, в сравнении с другим ПО:
1. Не требует отключения встроенного в Keenetic DNS сервера - всё работает методом перенаправления портов.
2. Работает с любыми туннелями, которые умеют поднимать UNIX интерфейс.
3. Несколько типов правил - domain, namespace, wildcard и regex.
4. Не тянет за собой огромное количество сторонних пакетов пакетов. Вся конфигурация находится в одном месте (в одном файле).
5. Возможность создавать несколько групп на разные сети.
6. Моментальное бесшовное включение/выключение сервиса.
### Roadmap:
1. CLI интерфейс для добавления/удаления записей в режиме реального времени. (Уже заложен функционал обработки записей в реальном времени, необходимо заняться CLI интерфейсом)
2. Дружелюбный к пользователю Web-GUI для конфигурации записей.
3. Поддержка подсетей и диапазона IP адресов.
4. Поддержка автообновляемых "подпискок" на список доменных имён (готовые списки подключаемые несколькими кликами мышки).
### Установка:
Т.к. в данный момент нету никакого дружелюбного к пользователю интерфейсов - данное руководство рассчитано на тех, кому просто нужна маршрутизация на требуемые для него домены без отключения встроенного в Keenetic DNS сервера.
Программа не была досканально протестирована, возможны очень редкие "вылеты". Максимально возможный риск заключается в том, что придётся перезапускать роутер, но шанс этого маловероятен.
1. Устанавливаем пакет:
```bash
opkg install magitrickle_<version>_<arch>.ipk
```
2. Копируем конфиг:
```bash
cp /opt/var/lib/magitrickle/config.yaml.example /opt/var/lib/magitrickle/config.yaml
```
3. Настраиваем конфиг (если не понимаете что делаете - не трогайте группу "app"!):
```yaml
configVersion: 0.1.0
app: # Настройки программы - не трогайте, если не знаете что к чему
dnsProxy:
host:
address: '[::]' # Адрес, который будет слушать программа для приёма DNS запросов
port: 3553 # Порт
upstream:
address: 127.0.0.1 # Адрес, используемый для отправки DNS запросов
port: 53 # Порт
disableRemap53: false # Флаг отключения перепривязки 53 порта
disableFakePTR: false # Флаг отключения подделки PTR записи (без неё есть проблемы, может быть будет исправлено в будущем)
disableDropAAAA: false # Флаг отключения откидывания AAAA записей
netfilter:
iptables:
chainPrefix: MT_ # Префикс для названий цепочек IPTables
ipset:
tablePrefix: mt_ # Префикс для названий таблиц IPSet
additionalTTL: 3600 # Дополнительный TTL (если от DNS пришел TTL 300, то к этому числу прибавится указанный TTL)
link: # Список адресов где будет подменяться DNS
- br0
- br1
logLevel: info # Уровень логов (trace, debug, info, warn, error)
groups: # Список групп
- id: d663876a # Уникальный ID группы (8 символов в диапозоне "0123456789abcdef")
name: Routing 1 # Человеко-читаемое имя (для будущего CLI и Web-GUI)
interface: nwg0 # Интерфейс, на который будет выполняться маршрутизация
fixProtect: false # Подключение интерфейса в список для выхода в интернет (для неподдерживаемых Keenetic туннелей)
rules: # Список правил
- id: 6f34ee91 # Уникальный ID правила (8 символов в диапозоне "0123456789abcdef")
name: Wildcard Example # Человеко-читаемое имя (для будущего CLI и Web-GUI)
type: wildcard # Тип правила
rule: '*.example.com' # Правило
enable: true # Флаг активации
- id: 00ae5f7c
name: RegEx Example
type: regex
rule: '^.*.regex.example.com$'
enable: true
- id: d663876b
name: Routing 2
interface: nwg1
fixProtect: false
rules:
- id: 6120dc8a
name: Domain Example
type: domain
rule: 'domain.example.com'
enable: true
```
Примеры правил:
* Domain (один домен без поддоменов)
```yaml
- id: 6120dc8a
name: Domain Example
type: domain
rule: 'example.com'
enable: true
```
* Namespace (домен и все его поддомены)
```yaml
- id: b9751782
name: Namespace Example
type: namespace
rule: 'example.com'
enable: true
```
* Wildcard
```yaml
- id: 6f34ee91
name: Wildcard Example
type: wildcard
rule: '*.example.com'
enable: true
```
* RegEx
```yaml
- id: 00ae5f7c
name: RegEx Example
type: regex
rule: '^.*.regex.example.com$'
enable: true
```
4. Запускаем сервис:
```bash
/opt/etc/init.d/S99magitrickle start
```
### Отладка
Если вам нужна отладка, то останавливаем сервис и запускаем "демона" руками:
```bash
/opt/etc/init.d/S99magitrickle stop
magitrickled
```

1
asd Normal file
View File

@ -0,0 +1 @@
sad

7
cmd/magitrickle/main.go Normal file
View File

@ -0,0 +1,7 @@
package magitrickle
import "fmt"
func main() {
fmt.Println("dummy file")
}

View File

@ -10,18 +10,18 @@ import (
"sync" "sync"
"syscall" "syscall"
"kvas2" "magitrickle"
"kvas2/constant" "magitrickle/constant"
"kvas2/models" "magitrickle/models"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const cfgFolderLocation = "/opt/var/lib/kvas2" const cfgFolderLocation = "/opt/var/lib/magitrickle"
const cfgFileLocation = cfgFolderLocation + "/config.yaml" const cfgFileLocation = cfgFolderLocation + "/config.yaml"
const pidFileLocation = "/opt/var/run/kvas2.pid" const pidFileLocation = "/opt/var/run/magitrickle.pid"
func checkPIDFile() error { func checkPIDFile() error {
data, err := os.ReadFile(pidFileLocation) data, err := os.ReadFile(pidFileLocation)
@ -59,10 +59,10 @@ func main() {
log.Info(). log.Info().
Str("version", constant.Version). Str("version", constant.Version).
Str("commit", constant.Commit). Str("commit", constant.Commit).
Msg("starting kvas2 daemon") Msg("starting MagiTrickle daemon")
if err := checkPIDFile(); err != nil { if err := checkPIDFile(); err != nil {
log.Fatal().Err(err).Msg("failed to start kvas2 daemon") log.Fatal().Err(err).Msg("failed to start MagiTrickle daemon")
} }
if err := createPIDFile(); err != nil { if err := createPIDFile(); err != nil {
@ -70,30 +70,15 @@ func main() {
} }
defer removePIDFile() defer removePIDFile()
cfg := models.ConfigFile{} cfg := models.Config{}
cfgFile, err := os.ReadFile(cfgFileLocation) cfgFile, err := os.ReadFile(cfgFileLocation)
if err == nil { if err != nil {
err = yaml.Unmarshal(cfgFile, &cfg)
if err != nil {
log.Fatal().Err(err).Msg("failed to parse config.yaml")
}
} else {
if !errors.Is(err, os.ErrNotExist) { if !errors.Is(err, os.ErrNotExist) {
log.Fatal().Err(err).Msg("failed to read config.yaml") log.Fatal().Err(err).Msg("failed to read config.yaml")
} }
cfg = models.ConfigFile{ cfg = models.Config{
AppConfig: models.AppConfig{ ConfigVersion: "0.1.0",
LogLevel: "info", App: magitrickle.DefaultAppConfig,
AdditionalTTL: 216000,
ChainPrefix: "KVAS2_",
IPSetPrefix: "kvas2_",
LinkName: "br0",
TargetDNSServerAddress: "127.0.0.1",
TargetDNSServerPort: 53,
ListenDNSPort: 3553,
},
} }
out, err := yaml.Marshal(cfg) out, err := yaml.Marshal(cfg)
if err != nil { if err != nil {
@ -107,9 +92,14 @@ func main() {
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("failed to save config.yaml") log.Fatal().Err(err).Msg("failed to save config.yaml")
} }
} else {
err = yaml.Unmarshal(cfgFile, &cfg)
if err != nil {
log.Fatal().Err(err).Msg("failed to parse config.yaml")
}
} }
switch cfg.AppConfig.LogLevel { switch cfg.App.LogLevel {
case "trace": case "trace":
zerolog.SetGlobalLevel(zerolog.TraceLevel) zerolog.SetGlobalLevel(zerolog.TraceLevel)
case "debug": case "debug":
@ -132,18 +122,18 @@ func main() {
zerolog.SetGlobalLevel(zerolog.InfoLevel) zerolog.SetGlobalLevel(zerolog.InfoLevel)
} }
app, err := kvas2.New(cfg) app := magitrickle.New()
err = app.ImportConfig(cfg)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("failed to initialize application") log.Fatal().Err(err).Msg("failed to import config")
} }
ctx, cancel := context.WithCancel(context.Background())
log.Info().Msg("starting service") log.Info().Msg("starting service")
/* /*
Starting app with graceful shutdown Starting app with graceful shutdown
*/ */
ctx, cancel := context.WithCancel(context.Background())
appResult := make(chan error) appResult := make(chan error)
go func() { go func() {
appResult <- app.Start(ctx) appResult <- app.Start(ctx)

View File

@ -12,33 +12,33 @@ import (
) )
type DNSMITMProxy struct { type DNSMITMProxy struct {
TargetDNSServerAddress string UpstreamDNSAddress string
TargetDNSServerPort uint16 UpstreamDNSPort uint16
RequestHook func(net.Addr, dns.Msg, string) (*dns.Msg, *dns.Msg, error) RequestHook func(net.Addr, dns.Msg, string) (*dns.Msg, *dns.Msg, error)
ResponseHook func(net.Addr, dns.Msg, dns.Msg, string) (*dns.Msg, error) ResponseHook func(net.Addr, dns.Msg, dns.Msg, string) (*dns.Msg, error)
} }
func (p DNSMITMProxy) requestDNS(req []byte, network string) ([]byte, error) { func (p DNSMITMProxy) requestDNS(req []byte, network string) ([]byte, error) {
serverConn, err := net.Dial(network, fmt.Sprintf("%s:%d", p.TargetDNSServerAddress, p.TargetDNSServerPort)) upstreamConn, err := net.Dial(network, fmt.Sprintf("%s:%d", p.UpstreamDNSAddress, p.UpstreamDNSPort))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to dial DNS server: %w", err) return nil, fmt.Errorf("failed to dial DNS upstream: %w", err)
} }
defer func() { _ = serverConn.Close() }() defer func() { _ = upstreamConn.Close() }()
err = serverConn.SetDeadline(time.Now().Add(time.Second * 5)) err = upstreamConn.SetDeadline(time.Now().Add(time.Second * 5))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to set deadline: %w", err) return nil, fmt.Errorf("failed to set deadline: %w", err)
} }
if network == "tcp" { if network == "tcp" {
err = binary.Write(serverConn, binary.BigEndian, uint16(len(req))) err = binary.Write(upstreamConn, binary.BigEndian, uint16(len(req)))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to write length: %w", err) return nil, fmt.Errorf("failed to write length: %w", err)
} }
} }
n, err := serverConn.Write(req) n, err := upstreamConn.Write(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to write request: %w", err) return nil, fmt.Errorf("failed to write request: %w", err)
} }
@ -46,7 +46,7 @@ func (p DNSMITMProxy) requestDNS(req []byte, network string) ([]byte, error) {
var resp []byte var resp []byte
if network == "tcp" { if network == "tcp" {
var respLen uint16 var respLen uint16
err = binary.Read(serverConn, binary.BigEndian, &respLen) err = binary.Read(upstreamConn, binary.BigEndian, &respLen)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read length: %w", err) return nil, fmt.Errorf("failed to read length: %w", err)
} }
@ -55,7 +55,7 @@ func (p DNSMITMProxy) requestDNS(req []byte, network string) ([]byte, error) {
resp = make([]byte, 512) resp = make([]byte, 512)
} }
n, err = serverConn.Read(resp) n, err = upstreamConn.Read(resp)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err) return nil, fmt.Errorf("failed to read response: %w", err)
} }
@ -213,9 +213,3 @@ func (p DNSMITMProxy) ListenUDP(ctx context.Context, addr *net.UDPAddr) error {
}(conn, clientAddr) }(conn, clientAddr)
} }
} }
func New() *DNSMITMProxy {
return &DNSMITMProxy{
TargetDNSServerPort: 53,
}
}

2
go.mod
View File

@ -1,4 +1,4 @@
module kvas2 module magitrickle
go 1.21 go 1.21

View File

@ -5,9 +5,9 @@ import (
"net" "net"
"time" "time"
"kvas2/models" "magitrickle/models"
"kvas2/netfilter-helper" "magitrickle/netfilter-helper"
"kvas2/records" "magitrickle/records"
"github.com/coreos/go-iptables/iptables" "github.com/coreos/go-iptables/iptables"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -15,7 +15,7 @@ import (
) )
type Group struct { type Group struct {
*models.Group models.Group
enabled bool enabled bool
iptables *iptables.IPTables iptables *iptables.IPTables
@ -86,6 +86,15 @@ func (g *Group) Disable() []error {
return errs return errs
} }
func (g *Group) Destroy() []error {
errs := g.Disable()
err := g.ipset.Destroy()
if err != nil {
errs = append(errs, err)
}
return errs
}
func (g *Group) Sync(records *records.Records) error { func (g *Group) Sync(records *records.Records) error {
now := time.Now() now := time.Now()
@ -117,9 +126,14 @@ func (g *Group) Sync(records *records.Records) error {
} }
for addr, ttl := range addresses { for addr, ttl := range addresses {
// TODO: Check TTL
if _, exists := currentAddresses[addr]; exists { if _, exists := currentAddresses[addr]; exists {
continue if currentAddresses[addr] == nil {
continue
} else {
if ttl < *currentAddresses[addr] {
continue
}
}
} }
ip := net.IP(addr) ip := net.IP(addr)
err = g.AddIP(ip, ttl) err = g.AddIP(ip, ttl)
@ -173,7 +187,7 @@ func (g *Group) LinkUpdateHook(event netlink.LinkUpdate) error {
return g.ipsetToLink.LinkUpdateHook(event) return g.ipsetToLink.LinkUpdateHook(event)
} }
func NewGroup(group *models.Group, nh4 *netfilterHelper.NetfilterHelper, chainPrefix, ipsetNamePrefix string) (*Group, error) { func NewGroup(group models.Group, nh4 *netfilterHelper.NetfilterHelper, chainPrefix, ipsetNamePrefix string) (*Group, error) {
ipsetName := fmt.Sprintf("%s%8x", ipsetNamePrefix, group.ID) ipsetName := fmt.Sprintf("%s%8x", ipsetNamePrefix, group.ID)
ipset, err := nh4.IPSet(ipsetName) ipset, err := nh4.IPSet(ipsetName)
if err != nil { if err != nil {

View File

@ -1,22 +1,19 @@
package kvas2 package magitrickle
import ( import (
"context" "context"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"math/rand"
"net" "net"
"os" "os"
"strconv"
"strings" "strings"
"time" "time"
"kvas2/dns-mitm-proxy" "magitrickle/dns-mitm-proxy"
"kvas2/group" "magitrickle/group"
"kvas2/models" "magitrickle/models"
"kvas2/netfilter-helper" "magitrickle/netfilter-helper"
"kvas2/records" "magitrickle/records"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -25,37 +22,42 @@ import (
) )
var ( var (
ErrAlreadyRunning = errors.New("already running") ErrAlreadyRunning = errors.New("already running")
ErrGroupIDConflict = errors.New("group id conflict") ErrGroupIDConflict = errors.New("group id conflict")
ErrRuleIDConflict = errors.New("rule id conflict") ErrRuleIDConflict = errors.New("rule id conflict")
ErrConfigUnsupportedVersion = errors.New("config unsupported version")
) )
func randomId() [4]byte { var DefaultAppConfig = models.App{
id := make([]byte, 4) DNSProxy: models.DNSProxy{
binary.BigEndian.PutUint32(id, rand.Uint32()) Host: models.DNSProxyServer{Address: "[::]", Port: 3553},
return [4]byte(id) Upstream: models.DNSProxyServer{Address: "127.0.0.1", Port: 53},
} DisableRemap53: false,
DisableFakePTR: false,
type Config struct { DisableDropAAAA: false,
AdditionalTTL uint32 },
ChainPrefix string Netfilter: models.Netfilter{
IPSetPrefix string IPTables: models.IPTables{
LinkName string ChainPrefix: "MT_",
TargetDNSServerAddress string },
TargetDNSServerPort uint16 IPSet: models.IPSet{
ListenDNSPort uint16 TablePrefix: "mt_",
AdditionalTTL: 3600,
},
},
Link: []string{"br0"},
LogLevel: "info",
} }
type App struct { type App struct {
Config Config config models.App
unprocessedGroups []models.Group
DNSMITM *dnsMitmProxy.DNSMITMProxy dnsMITM *dnsMitmProxy.DNSMITMProxy
NetfilterHelper4 *netfilterHelper.NetfilterHelper nfHelper4 *netfilterHelper.NetfilterHelper
NetfilterHelper6 *netfilterHelper.NetfilterHelper nfHelper6 *netfilterHelper.NetfilterHelper
Records *records.Records records *records.Records
Groups []*group.Group groups []*group.Group
Link netlink.Link
isRunning bool isRunning bool
dnsOverrider4 *netfilterHelper.PortRemap dnsOverrider4 *netfilterHelper.PortRemap
@ -63,7 +65,6 @@ type App struct {
} }
func (a *App) handleLink(event netlink.LinkUpdate) { func (a *App) handleLink(event netlink.LinkUpdate) {
switch event.Change { switch event.Change {
case 0x00000001: case 0x00000001:
log.Trace(). log.Trace().
@ -71,7 +72,7 @@ func (a *App) handleLink(event netlink.LinkUpdate) {
Int("change", int(event.Change)). Int("change", int(event.Change)).
Msg("interface event") Msg("interface event")
ifaceName := event.Link.Attrs().Name ifaceName := event.Link.Attrs().Name
for _, group := range a.Groups { for _, group := range a.groups {
if group.Interface != ifaceName { if group.Interface != ifaceName {
continue continue
} }
@ -98,6 +99,72 @@ func (a *App) handleLink(event netlink.LinkUpdate) {
} }
func (a *App) start(ctx context.Context) (err error) { func (a *App) start(ctx context.Context) (err error) {
a.dnsMITM = &dnsMitmProxy.DNSMITMProxy{
UpstreamDNSAddress: a.config.DNSProxy.Upstream.Address,
UpstreamDNSPort: a.config.DNSProxy.Upstream.Port,
RequestHook: func(clientAddr net.Addr, reqMsg dns.Msg, network string) (*dns.Msg, *dns.Msg, error) {
if a.config.DNSProxy.DisableFakePTR {
return nil, nil, nil
}
// TODO: Проверить на интерфейс
if len(reqMsg.Question) == 1 && reqMsg.Question[0].Qtype == dns.TypePTR {
respMsg := &dns.Msg{
MsgHdr: dns.MsgHdr{
Id: reqMsg.Id,
Response: true,
RecursionAvailable: true,
Rcode: dns.RcodeNameError,
},
Question: reqMsg.Question,
}
return nil, respMsg, nil
}
return nil, nil, nil
},
ResponseHook: func(clientAddr net.Addr, reqMsg dns.Msg, respMsg dns.Msg, network string) (*dns.Msg, error) {
defer a.handleMessage(respMsg, clientAddr, &network)
if a.config.DNSProxy.DisableDropAAAA {
return nil, nil
}
var idx int
for _, answer := range respMsg.Answer {
if answer.Header().Rrtype == dns.TypeAAAA {
continue
}
respMsg.Answer[idx] = answer
idx++
}
respMsg.Answer = respMsg.Answer[:idx]
return &respMsg, nil
},
}
a.records = records.New()
nh4, err := netfilterHelper.New(false)
if err != nil {
return fmt.Errorf("netfilter helper init fail: %w", err)
}
err = nh4.CleanIPTables(a.config.Netfilter.IPTables.ChainPrefix)
if err != nil {
return fmt.Errorf("failed to clear iptables: %w", err)
}
a.nfHelper4 = nh4
nh6, err := netfilterHelper.New(true)
if err != nil {
return fmt.Errorf("netfilter helper init fail: %w", err)
}
err = nh6.CleanIPTables(a.config.Netfilter.IPTables.ChainPrefix)
if err != nil {
return fmt.Errorf("failed to clear iptables: %w", err)
}
a.nfHelper6 = nh6
newCtx, cancel := context.WithCancel(ctx) newCtx, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
@ -108,12 +175,12 @@ func (a *App) start(ctx context.Context) (err error) {
*/ */
go func() { go func() {
addr, err := net.ResolveUDPAddr("udp", "[::]:"+strconv.Itoa(int(a.Config.ListenDNSPort))) addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", a.config.DNSProxy.Host.Address, a.config.DNSProxy.Host.Port))
if err != nil { if err != nil {
errChan <- fmt.Errorf("failed to resolve udp address: %v", err) errChan <- fmt.Errorf("failed to resolve udp address: %v", err)
return return
} }
err = a.DNSMITM.ListenUDP(newCtx, addr) err = a.dnsMITM.ListenUDP(newCtx, addr)
if err != nil { if err != nil {
errChan <- fmt.Errorf("failed to serve DNS UDP proxy: %v", err) errChan <- fmt.Errorf("failed to serve DNS UDP proxy: %v", err)
return return
@ -121,57 +188,73 @@ func (a *App) start(ctx context.Context) (err error) {
}() }()
go func() { go func() {
addr, err := net.ResolveTCPAddr("tcp", "[::]:"+strconv.Itoa(int(a.Config.ListenDNSPort))) addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", a.config.DNSProxy.Host.Address, a.config.DNSProxy.Host.Port))
if err != nil { if err != nil {
errChan <- fmt.Errorf("failed to resolve tcp address: %v", err) errChan <- fmt.Errorf("failed to resolve tcp address: %v", err)
return return
} }
err = a.DNSMITM.ListenTCP(newCtx, addr) err = a.dnsMITM.ListenTCP(newCtx, addr)
if err != nil { if err != nil {
errChan <- fmt.Errorf("failed to serve DNS TCP proxy: %v", err) errChan <- fmt.Errorf("failed to serve DNS TCP proxy: %v", err)
return return
} }
}() }()
addrList, err := netlink.AddrList(a.Link, nl.FAMILY_ALL) var addrList []netlink.Addr
if err != nil { for _, linkName := range a.config.Link {
return fmt.Errorf("failed to list address of interface: %w", err) link, err := netlink.LinkByName(linkName)
if err != nil {
return fmt.Errorf("failed to find link %s: %w", linkName, err)
}
linkAddrList, err := netlink.AddrList(link, nl.FAMILY_ALL)
if err != nil {
return fmt.Errorf("failed to list address of interface: %w", err)
}
addrList = append(addrList, linkAddrList...)
} }
a.dnsOverrider4 = a.NetfilterHelper4.PortRemap(fmt.Sprintf("%sDNSOR", a.Config.ChainPrefix), 53, a.Config.ListenDNSPort, addrList) if !a.config.DNSProxy.DisableRemap53 {
err = a.dnsOverrider4.Enable() a.dnsOverrider4 = a.nfHelper4.PortRemap(fmt.Sprintf("%sDNSOR", a.config.Netfilter.IPTables.ChainPrefix), 53, a.config.DNSProxy.Host.Port, addrList)
if err != nil { err = a.dnsOverrider4.Enable()
return fmt.Errorf("failed to override DNS (IPv4): %v", err) if err != nil {
} return fmt.Errorf("failed to override DNS (IPv4): %v", err)
defer func() { _ = a.dnsOverrider4.Disable() }() }
defer func() { _ = a.dnsOverrider4.Disable() }()
a.dnsOverrider6 = a.NetfilterHelper6.PortRemap(fmt.Sprintf("%sDNSOR", a.Config.ChainPrefix), 53, a.Config.ListenDNSPort, addrList) a.dnsOverrider6 = a.nfHelper6.PortRemap(fmt.Sprintf("%sDNSOR", a.config.Netfilter.IPTables.ChainPrefix), 53, a.config.DNSProxy.Host.Port, addrList)
err = a.dnsOverrider6.Enable() err = a.dnsOverrider6.Enable()
if err != nil { if err != nil {
return fmt.Errorf("failed to override DNS (IPv6): %v", err) return fmt.Errorf("failed to override DNS (IPv6): %v", err)
}
defer func() { _ = a.dnsOverrider6.Disable() }()
} }
defer func() { _ = a.dnsOverrider6.Disable() }()
/* /*
Groups Groups
*/ */
for _, group := range a.Groups { for _, group := range a.unprocessedGroups {
err := a.AddGroup(group)
if err != nil {
return err
}
}
for _, group := range a.groups {
err = group.Enable() err = group.Enable()
if err != nil { if err != nil {
return fmt.Errorf("failed to enable group: %w", err) return fmt.Errorf("failed to enable group: %w", err)
} }
} }
defer func() { defer func() {
for _, group := range a.Groups { for _, group := range a.groups {
_ = group.Disable() _ = group.Destroy()
} }
}() }()
/* /*
Socket (for netfilter.d events) Socket (for netfilter.d events)
*/ */
socketPath := "/opt/var/run/kvas2.sock" socketPath := "/opt/var/run/magitrickle.sock"
err = os.Remove(socketPath) err = os.Remove(socketPath)
if err != nil && !errors.Is(err, os.ErrNotExist) { if err != nil && !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("failed to remove existed UNIX socket: %w", err) return fmt.Errorf("failed to remove existed UNIX socket: %w", err)
@ -219,7 +302,7 @@ func (a *App) start(ctx context.Context) (err error) {
if err != nil { if err != nil {
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d") log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
} }
for _, group := range a.Groups { for _, group := range a.groups {
err := group.NetfilterDHook(args[2]) err := group.NetfilterDHook(args[2])
if err != nil { if err != nil {
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d") log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
@ -281,8 +364,8 @@ func (a *App) Start(ctx context.Context) (err error) {
return err return err
} }
func (a *App) AddGroup(groupModel *models.Group) error { func (a *App) AddGroup(groupModel models.Group) error {
for _, group := range a.Groups { for _, group := range a.groups {
if groupModel.ID == group.ID { if groupModel.ID == group.ID {
return ErrGroupIDConflict return ErrGroupIDConflict
} }
@ -295,15 +378,18 @@ func (a *App) AddGroup(groupModel *models.Group) error {
dup[rule.ID] = struct{}{} dup[rule.ID] = struct{}{}
} }
grp, err := group.NewGroup(groupModel, a.NetfilterHelper4, a.Config.ChainPrefix, a.Config.IPSetPrefix) grp, err := group.NewGroup(groupModel, a.nfHelper4, a.config.Netfilter.IPTables.ChainPrefix, a.config.Netfilter.IPSet.TablePrefix)
if err != nil { if err != nil {
return fmt.Errorf("failed to create group: %w", err) return fmt.Errorf("failed to create group: %w", err)
} }
a.Groups = append(a.Groups, grp) a.groups = append(a.groups, grp)
log.Debug().Str("id", grp.ID.String()).Str("name", grp.Name).Msg("added group") log.Debug().Str("id", grp.ID.String()).Str("name", grp.Name).Msg("added group")
return grp.Sync(a.Records) if a.isRunning {
return grp.Sync(a.records)
}
return nil
} }
func (a *App) ListInterfaces() ([]net.Interface, error) { func (a *App) ListInterfaces() ([]net.Interface, error) {
@ -341,12 +427,12 @@ func (a *App) processARecord(aRecord dns.A, clientAddr net.Addr, network *string
Str("network", networkStr). Str("network", networkStr).
Msg("processing a record") Msg("processing a record")
ttlDuration := aRecord.Hdr.Ttl + a.Config.AdditionalTTL ttlDuration := aRecord.Hdr.Ttl + a.config.Netfilter.IPSet.AdditionalTTL
a.Records.AddARecord(aRecord.Hdr.Name[:len(aRecord.Hdr.Name)-1], aRecord.A, ttlDuration) a.records.AddARecord(aRecord.Hdr.Name[:len(aRecord.Hdr.Name)-1], aRecord.A, ttlDuration)
names := a.Records.GetAliases(aRecord.Hdr.Name[:len(aRecord.Hdr.Name)-1]) names := a.records.GetAliases(aRecord.Hdr.Name[:len(aRecord.Hdr.Name)-1])
for _, group := range a.Groups { for _, group := range a.groups {
Rule: Rule:
for _, domain := range group.Rules { for _, domain := range group.Rules {
if !domain.IsEnabled() { if !domain.IsEnabled() {
@ -392,15 +478,15 @@ func (a *App) processCNameRecord(cNameRecord dns.CNAME, clientAddr net.Addr, net
Str("network", networkStr). Str("network", networkStr).
Msg("processing cname record") Msg("processing cname record")
ttlDuration := cNameRecord.Hdr.Ttl + a.Config.AdditionalTTL ttlDuration := cNameRecord.Hdr.Ttl + a.config.Netfilter.IPSet.AdditionalTTL
a.Records.AddCNameRecord(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1], cNameRecord.Target[:len(cNameRecord.Target)-1], ttlDuration) a.records.AddCNameRecord(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1], cNameRecord.Target[:len(cNameRecord.Target)-1], ttlDuration)
// TODO: Optimization // TODO: Optimization
now := time.Now() now := time.Now()
aRecords := a.Records.GetARecords(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1]) aRecords := a.records.GetARecords(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1])
names := a.Records.GetAliases(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1]) names := a.records.GetAliases(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1])
for _, group := range a.Groups { for _, group := range a.groups {
Rule: Rule:
for _, domain := range group.Rules { for _, domain := range group.Rules {
if !domain.IsEnabled() { if !domain.IsEnabled() {
@ -446,124 +532,51 @@ func (a *App) handleMessage(msg dns.Msg, clientAddr net.Addr, network *string) {
} }
} }
func (a *App) ImportConfig(cfg models.ConfigFile) error { func (a *App) ImportConfig(cfg models.Config) error {
a.Config = Config{ if !strings.HasPrefix(cfg.ConfigVersion, "0.1.") {
AdditionalTTL: cfg.AppConfig.AdditionalTTL, return ErrConfigUnsupportedVersion
ChainPrefix: cfg.AppConfig.ChainPrefix,
IPSetPrefix: cfg.AppConfig.IPSetPrefix,
LinkName: cfg.AppConfig.LinkName,
TargetDNSServerAddress: cfg.AppConfig.TargetDNSServerAddress,
TargetDNSServerPort: cfg.AppConfig.TargetDNSServerPort,
ListenDNSPort: cfg.AppConfig.ListenDNSPort,
} }
if cfg.App.DNSProxy.Upstream.Address != "" {
a.config.DNSProxy.Upstream.Address = cfg.App.DNSProxy.Upstream.Address
}
if cfg.App.DNSProxy.Upstream.Port != 0 {
a.config.DNSProxy.Upstream.Port = cfg.App.DNSProxy.Upstream.Port
}
if cfg.App.DNSProxy.Host.Address != "" {
a.config.DNSProxy.Host.Address = cfg.App.DNSProxy.Host.Address
}
if cfg.App.DNSProxy.Host.Port != 0 {
a.config.DNSProxy.Host.Port = cfg.App.DNSProxy.Host.Port
}
a.config.DNSProxy.DisableRemap53 = cfg.App.DNSProxy.DisableRemap53
a.config.DNSProxy.DisableFakePTR = cfg.App.DNSProxy.DisableFakePTR
a.config.DNSProxy.DisableDropAAAA = cfg.App.DNSProxy.DisableDropAAAA
if cfg.App.Netfilter.IPTables.ChainPrefix != "" {
a.config.Netfilter.IPTables.ChainPrefix = cfg.App.Netfilter.IPTables.ChainPrefix
}
if cfg.App.Netfilter.IPSet.TablePrefix != "" {
a.config.Netfilter.IPSet.TablePrefix = cfg.App.Netfilter.IPSet.TablePrefix
}
a.config.Netfilter.IPSet.AdditionalTTL = cfg.App.Netfilter.IPSet.AdditionalTTL
a.unprocessedGroups = cfg.Groups
return nil return nil
} }
func (a *App) ExportConfig() models.ConfigFile { func (a *App) ExportConfig() models.Config {
groups := make([]models.Group, len(a.Groups)) groups := make([]models.Group, len(a.groups))
for idx, group := range a.Groups { for idx, group := range a.groups {
groups[idx] = *group.Group groups[idx] = group.Group
} }
return models.ConfigFile{ return models.Config{
AppConfig: models.AppConfig{ ConfigVersion: "0.1.0",
AdditionalTTL: a.Config.AdditionalTTL, App: a.config,
ChainPrefix: a.Config.ChainPrefix, Groups: groups,
IPSetPrefix: a.Config.IPSetPrefix,
LinkName: a.Config.LinkName,
TargetDNSServerAddress: a.Config.TargetDNSServerAddress,
TargetDNSServerPort: a.Config.TargetDNSServerPort,
ListenDNSPort: a.Config.ListenDNSPort,
},
Groups: groups,
} }
} }
func New(config models.ConfigFile) (*App, error) { func New() *App {
var err error return &App{config: DefaultAppConfig}
app := &App{}
app.Config = Config{
AdditionalTTL: config.AppConfig.AdditionalTTL,
ChainPrefix: config.AppConfig.ChainPrefix,
IPSetPrefix: config.AppConfig.IPSetPrefix,
LinkName: config.AppConfig.LinkName,
TargetDNSServerAddress: config.AppConfig.TargetDNSServerAddress,
TargetDNSServerPort: config.AppConfig.TargetDNSServerPort,
ListenDNSPort: config.AppConfig.ListenDNSPort,
}
app.DNSMITM = dnsMitmProxy.New()
app.DNSMITM.TargetDNSServerAddress = app.Config.TargetDNSServerAddress
app.DNSMITM.TargetDNSServerPort = app.Config.TargetDNSServerPort
app.DNSMITM.RequestHook = func(clientAddr net.Addr, reqMsg dns.Msg, network string) (*dns.Msg, *dns.Msg, error) {
// TODO: Need to understand why it not works in proxy mode
if len(reqMsg.Question) == 1 && reqMsg.Question[0].Qtype == dns.TypePTR {
respMsg := &dns.Msg{
MsgHdr: dns.MsgHdr{
Id: reqMsg.Id,
Response: true,
RecursionAvailable: true,
Rcode: dns.RcodeNameError,
},
Question: reqMsg.Question,
}
return nil, respMsg, nil
}
return nil, nil, nil
}
app.DNSMITM.ResponseHook = func(clientAddr net.Addr, reqMsg dns.Msg, respMsg dns.Msg, network string) (*dns.Msg, error) {
// TODO: Make it optional
var idx int
for _, a := range respMsg.Answer {
if a.Header().Rrtype == dns.TypeAAAA {
continue
}
respMsg.Answer[idx] = a
idx++
}
respMsg.Answer = respMsg.Answer[:idx]
app.handleMessage(respMsg, clientAddr, &network)
return &respMsg, nil
}
app.Records = records.New()
link, err := netlink.LinkByName(app.Config.LinkName)
if err != nil {
return nil, fmt.Errorf("failed to find link %s: %w", app.Config.LinkName, err)
}
app.Link = link
nh4, err := netfilterHelper.New(false)
if err != nil {
return nil, fmt.Errorf("netfilter helper init fail: %w", err)
}
app.NetfilterHelper4 = nh4
err = app.NetfilterHelper4.CleanIPTables(app.Config.ChainPrefix)
if err != nil {
return nil, fmt.Errorf("failed to clear iptables: %w", err)
}
nh6, err := netfilterHelper.New(true)
if err != nil {
return nil, fmt.Errorf("netfilter helper init fail: %w", err)
}
app.NetfilterHelper6 = nh6
err = app.NetfilterHelper6.CleanIPTables(app.Config.ChainPrefix)
if err != nil {
return nil, fmt.Errorf("failed to clear iptables: %w", err)
}
for _, group := range config.Groups {
err = app.AddGroup(&group)
if err != nil {
return nil, err
}
}
return app, nil
} }

View File

@ -1,17 +1,41 @@
package models package models
type ConfigFile struct { type Config struct {
AppConfig AppConfig `yaml:"appConfig"` ConfigVersion string `yaml:"configVersion"`
Groups []Group `yaml:"groups"` App App `yaml:"app"`
Groups []Group `yaml:"groups"`
} }
type AppConfig struct { type App struct {
LogLevel string `yaml:"logLevel"` DNSProxy DNSProxy `yaml:"dnsProxy"`
AdditionalTTL uint32 `yaml:"additionalTTL"` Netfilter Netfilter `yaml:"netfilter"`
ChainPrefix string `yaml:"chainPrefix"` Link []string `yaml:"link"`
IPSetPrefix string `yaml:"ipsetPrefix"` LogLevel string `yaml:"logLevel"`
LinkName string `yaml:"linkName"` }
TargetDNSServerAddress string `yaml:"targetDNSServerAddress"`
TargetDNSServerPort uint16 `yaml:"targetDNSServerPort"` type DNSProxy struct {
ListenDNSPort uint16 `yaml:"listenDNSPort"` Host DNSProxyServer `yaml:"host"`
Upstream DNSProxyServer `yaml:"upstream"`
DisableRemap53 bool `yaml:"disableRemap53"`
DisableFakePTR bool `yaml:"disableFakePTR"`
DisableDropAAAA bool `yaml:"disableDropAAAA"`
}
type DNSProxyServer struct {
Address string `yaml:"address"`
Port uint16 `yaml:"port"`
}
type Netfilter struct {
IPTables IPTables `yaml:"iptables"`
IPSet IPSet `yaml:"ipset"`
}
type IPTables struct {
ChainPrefix string `yaml:"chainPrefix"`
}
type IPSet struct {
TablePrefix string `yaml:"tablePrefix"`
AdditionalTTL uint32 `yaml:"additionalTTL"`
} }

View File

@ -2,16 +2,16 @@ package models
import "testing" import "testing"
func TestDomain_IsMatch_Plaintext(t *testing.T) { func TestDomain_IsMatch_Domain(t *testing.T) {
rule := &Rule{ rule := &Rule{
Type: "plaintext", Type: "domain",
Rule: "example.com", Rule: "example.com",
} }
if !rule.IsMatch("example.com") { if !rule.IsMatch("example.com") {
t.Fatal("&Rule{Type: \"plaintext\", Rule: \"example.com\"}.IsMatch(\"example.com\") returns false") t.Fatal("&Rule{Type: \"domain\", Rule: \"example.com\"}.IsMatch(\"example.com\") returns false")
} }
if rule.IsMatch("noexample.com") { if rule.IsMatch("noexample.com") {
t.Fatal("&Rule{Type: \"plaintext\", Rule: \"example.com\"}.IsMatch(\"noexample.com\") returns true") t.Fatal("&Rule{Type: \"domain\", Rule: \"example.com\"}.IsMatch(\"noexample.com\") returns true")
} }
} }

View File

@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"github.com/coreos/go-iptables/iptables" "github.com/coreos/go-iptables/iptables"
"github.com/rs/zerolog/log"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
) )
@ -36,6 +37,7 @@ func (r *IPSetToLink) insertIPTablesRules(table string) error {
} }
for _, iptablesArgs := range [][]string{ for _, iptablesArgs := range [][]string{
{"-j", "CONNMARK", "--restore-mark"},
{"-j", "MARK", "--set-mark", strconv.Itoa(int(r.mark))}, {"-j", "MARK", "--set-mark", strconv.Itoa(int(r.mark))},
{"-j", "CONNMARK", "--save-mark"}, {"-j", "CONNMARK", "--save-mark"},
} { } {
@ -111,6 +113,8 @@ func (r *IPSetToLink) insertIPRule() error {
} }
r.ipRule = rule r.ipRule = rule
log.Trace().Int("table", r.table).Int("mark", int(r.mark)).Msg("using ip table and mark")
return nil return nil
} }
@ -133,7 +137,7 @@ func (r *IPSetToLink) insertIPRoute() error {
if err != nil { if err != nil {
// TODO: Нормально отлавливать ошибку // TODO: Нормально отлавливать ошибку
if err.Error() == "Link not found" { if err.Error() == "Link not found" {
// TODO: Логи log.Debug().Str("iface", r.IfaceName).Msg("interface not found (waiting for it to exist)")
return nil return nil
} }
return fmt.Errorf("error while getting interface: %w", err) return fmt.Errorf("error while getting interface: %w", err)

View File

@ -21,7 +21,8 @@ type PortRemap struct {
func (r *PortRemap) insertIPTablesRules(table string) error { func (r *PortRemap) insertIPTablesRules(table string) error {
if table == "" || table == "nat" { if table == "" || table == "nat" {
err := r.IPTables.NewChain("nat", r.ChainName) preroutingChain := r.ChainName + "_PRR"
err := r.IPTables.NewChain("nat", preroutingChain)
if err != nil { if err != nil {
// If not "AlreadyExists" // If not "AlreadyExists"
if eerr, eok := err.(*iptables.Error); !(eok && eerr.ExitStatus() == 1) { if eerr, eok := err.(*iptables.Error); !(eok && eerr.ExitStatus() == 1) {
@ -34,18 +35,62 @@ func (r *PortRemap) insertIPTablesRules(table string) error {
continue continue
} }
for _, iptablesArgs := range [][]string{ if r.IPTables.Proto() != iptables.ProtocolIPv6 {
{"-p", "tcp", "-d", addr.IP.String(), "--dport", strconv.Itoa(int(r.From)), "-j", "DNAT", "--to-destination", fmt.Sprintf(":%d", r.To)}, for _, iptablesArgs := range [][]string{
{"-p", "udp", "-d", addr.IP.String(), "--dport", strconv.Itoa(int(r.From)), "-j", "DNAT", "--to-destination", fmt.Sprintf(":%d", r.To)}, {"-p", "tcp", "-d", addr.IP.String(), "--dport", fmt.Sprintf("%d", r.From), "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", r.To)},
} { {"-p", "udp", "-d", addr.IP.String(), "--dport", fmt.Sprintf("%d", r.From), "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", r.To)},
err = r.IPTables.AppendUnique("nat", r.ChainName, iptablesArgs...) } {
if err != nil { err = r.IPTables.AppendUnique("nat", preroutingChain, iptablesArgs...)
return fmt.Errorf("failed to append rule: %w", err) if err != nil {
return fmt.Errorf("failed to append rule: %w", err)
}
}
} else {
for _, iptablesArgs := range [][]string{
{"-p", "tcp", "-d", addr.IP.String(), "--dport", strconv.Itoa(int(r.From)), "-j", "DNAT", "--to-destination", fmt.Sprintf(":%d", r.To)},
{"-p", "udp", "-d", addr.IP.String(), "--dport", strconv.Itoa(int(r.From)), "-j", "DNAT", "--to-destination", fmt.Sprintf(":%d", r.To)},
} {
err = r.IPTables.AppendUnique("nat", preroutingChain, iptablesArgs...)
if err != nil {
return fmt.Errorf("failed to append rule: %w", err)
}
} }
} }
} }
err = r.IPTables.InsertUnique("nat", "PREROUTING", 1, "-j", r.ChainName) err = r.IPTables.InsertUnique("nat", "PREROUTING", 1, "-j", preroutingChain)
if err != nil {
return fmt.Errorf("failed to linking chain: %w", err)
}
postroutingChain := r.ChainName + "_POR"
err = r.IPTables.NewChain("nat", postroutingChain)
if err != nil {
// If not "AlreadyExists"
if eerr, eok := err.(*iptables.Error); !(eok && eerr.ExitStatus() == 1) {
return fmt.Errorf("failed to create chain: %w", err)
}
}
for _, addr := range r.Addresses {
if !((r.IPTables.Proto() == iptables.ProtocolIPv4 && len(addr.IP) == net.IPv4len) || (r.IPTables.Proto() == iptables.ProtocolIPv6 && len(addr.IP) == net.IPv6len)) {
continue
}
if r.IPTables.Proto() == iptables.ProtocolIPv4 {
for _, iptablesArgs := range [][]string{
{"-p", "tcp", "-d", addr.IP.String(), "--sport", strconv.Itoa(int(r.To)), "-j", "SNAT", "--to-source", addr.IP.String()},
{"-p", "udp", "-d", addr.IP.String(), "--sport", strconv.Itoa(int(r.To)), "-j", "SNAT", "--to-source", addr.IP.String()},
} {
err = r.IPTables.AppendUnique("nat", postroutingChain, iptablesArgs...)
if err != nil {
return fmt.Errorf("failed to append rule: %w", err)
}
}
}
}
err = r.IPTables.InsertUnique("nat", "POSTROUTING", 1, "-j", postroutingChain)
if err != nil { if err != nil {
return fmt.Errorf("failed to linking chain: %w", err) return fmt.Errorf("failed to linking chain: %w", err)
} }
@ -57,7 +102,14 @@ func (r *PortRemap) insertIPTablesRules(table string) error {
func (r *PortRemap) deleteIPTablesRules() []error { func (r *PortRemap) deleteIPTablesRules() []error {
var errs []error var errs []error
err := r.IPTables.DeleteIfExists("nat", "PREROUTING", "-j", r.ChainName) preroutingChain := r.ChainName + "_PRR"
err := r.IPTables.DeleteIfExists("nat", "PREROUTING", "-j", preroutingChain)
if err != nil {
errs = append(errs, fmt.Errorf("failed to unlinking chain: %w", err))
}
postroutingChain := r.ChainName + "_POR"
err = r.IPTables.DeleteIfExists("nat", "POSTROUTING", "-j", postroutingChain)
if err != nil { if err != nil {
errs = append(errs, fmt.Errorf("failed to unlinking chain: %w", err)) errs = append(errs, fmt.Errorf("failed to unlinking chain: %w", err))
} }

10
opt/etc/init.d/S99magitrickle Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
ENABLED=yes
PROCS=magitrickled
ARGS=""
PREARGS=""
DESC=$PROCS
PATH=/opt/sbin:/opt/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
. /opt/etc/init.d/rc.func

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
SOCKET_PATH="/opt/var/run/kvas2.sock" SOCKET_PATH="/opt/var/run/magitrickle.sock"
if [ ! -S "$SOCKET_PATH" ]; then if [ ! -S "$SOCKET_PATH" ]; then
exit exit
fi fi

View File

@ -1,11 +1,24 @@
appConfig: configVersion: 0.1.0
additionalTTL: 216000 app:
chainPrefix: KVAS2_ dnsProxy:
ipsetPrefix: kvas2_ host:
linkName: br0 address: '[::]'
targetDNSServerAddress: 127.0.0.1 port: 3553
targetDNSServerPort: 53 upstream:
listenDNSPort: 3553 address: 127.0.0.1
port: 53
disableRemap53: false
disableFakePTR: false
disableDropAAAA: false
netfilter:
iptables:
chainPrefix: MT_
ipset:
tablePrefix: mt_
additionalTTL: 3600
link:
- br0
logLevel: info
groups: groups:
- id: d663876a - id: d663876a
name: Example name: Example

View File

@ -9,8 +9,8 @@ import (
func TestLoop(t *testing.T) { func TestLoop(t *testing.T) {
r := New() r := New()
r.AddCNameRecord("1", "2", time.Minute) r.AddCNameRecord("1", "2", 60)
r.AddCNameRecord("2", "1", time.Minute) r.AddCNameRecord("2", "1", 60)
if r.GetARecords("1") != nil { if r.GetARecords("1") != nil {
t.Fatal("loop detected") t.Fatal("loop detected")
} }
@ -21,8 +21,8 @@ func TestLoop(t *testing.T) {
func TestCName(t *testing.T) { func TestCName(t *testing.T) {
r := New() r := New()
r.AddARecord("example.com", []byte{1, 2, 3, 4}, time.Minute) r.AddARecord("example.com", []byte{1, 2, 3, 4}, 60)
r.AddCNameRecord("gateway.example.com", "example.com", time.Minute) r.AddCNameRecord("gateway.example.com", "example.com", 60)
records := r.GetARecords("gateway.example.com") records := r.GetARecords("gateway.example.com")
if records == nil { if records == nil {
t.Fatal("no records") t.Fatal("no records")
@ -34,7 +34,7 @@ func TestCName(t *testing.T) {
func TestA(t *testing.T) { func TestA(t *testing.T) {
r := New() r := New()
r.AddARecord("example.com", []byte{1, 2, 3, 4}, time.Minute) r.AddARecord("example.com", []byte{1, 2, 3, 4}, 60)
records := r.GetARecords("example.com") records := r.GetARecords("example.com")
if records == nil { if records == nil {
t.Fatal("no records") t.Fatal("no records")
@ -46,7 +46,8 @@ func TestA(t *testing.T) {
func TestDeprecated(t *testing.T) { func TestDeprecated(t *testing.T) {
r := New() r := New()
r.AddARecord("example.com", []byte{1, 2, 3, 4}, -time.Minute) r.AddARecord("example.com", []byte{1, 2, 3, 4}, 0)
time.Sleep(time.Second)
records := r.GetARecords("example.com") records := r.GetARecords("example.com")
if records != nil { if records != nil {
t.Fatal("deprecated records") t.Fatal("deprecated records")
@ -63,7 +64,7 @@ func TestNotExistedA(t *testing.T) {
func TestNotExistedCNameAlias(t *testing.T) { func TestNotExistedCNameAlias(t *testing.T) {
r := New() r := New()
r.AddCNameRecord("gateway.example.com", "example.com", time.Minute) r.AddCNameRecord("gateway.example.com", "example.com", 60)
records := r.GetARecords("gateway.example.com") records := r.GetARecords("gateway.example.com")
if records != nil { if records != nil {
t.Fatal("not existed records") t.Fatal("not existed records")
@ -72,8 +73,8 @@ func TestNotExistedCNameAlias(t *testing.T) {
func TestReplacing(t *testing.T) { func TestReplacing(t *testing.T) {
r := New() r := New()
r.AddCNameRecord("gateway.example.com", "example.com", time.Minute) r.AddCNameRecord("gateway.example.com", "example.com", 60)
r.AddARecord("gateway.example.com", []byte{1, 2, 3, 4}, time.Minute) r.AddARecord("gateway.example.com", []byte{1, 2, 3, 4}, 60)
records := r.GetARecords("gateway.example.com") records := r.GetARecords("gateway.example.com")
if bytes.Compare(records[0].Address, []byte{1, 2, 3, 4}) != 0 { if bytes.Compare(records[0].Address, []byte{1, 2, 3, 4}) != 0 {
t.Fatal("mismatch") t.Fatal("mismatch")
@ -82,11 +83,11 @@ func TestReplacing(t *testing.T) {
func TestAliases(t *testing.T) { func TestAliases(t *testing.T) {
r := New() r := New()
r.AddARecord("1", []byte{1, 2, 3, 4}, time.Minute) r.AddARecord("1", []byte{1, 2, 3, 4}, 60)
r.AddCNameRecord("2", "1", time.Minute) r.AddCNameRecord("2", "1", 60)
r.AddCNameRecord("3", "2", time.Minute) r.AddCNameRecord("3", "2", 60)
r.AddCNameRecord("4", "2", time.Minute) r.AddCNameRecord("4", "2", 60)
r.AddCNameRecord("5", "1", time.Minute) r.AddCNameRecord("5", "1", 60)
aliases := r.GetAliases("1") aliases := r.GetAliases("1")
if aliases == nil { if aliases == nil {
t.Fatal("no aliases") t.Fatal("no aliases")