Initial commit of SSH Proxy VPN Android app.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
kykaevv 2026-09-01 13:31:24 +03:00
commit 46fcf18bed
628 changed files with 156577 additions and 0 deletions

37
.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
# Gradle / Android
.gradle/
**/build/
**/.cxx/
local.properties
*.apk
*.aab
*.ap_
*.dex
captures/
.externalNativeBuild/
# IDE
.idea/
*.iml
.vscode/
# Native duplicate (used copy lives in app/src/main/cpp)
/hev-socks5-tunnel/
# Logs / crash dumps
*.log
hs_err_pid*.log
replay_pid*.log
# OS
.DS_Store
Thumbs.db
# Secrets / keys
*.jks
*.keystore
id_rsa
id_ed25519
*.pem
.env
.env.*

104
README.md Normal file
View File

@ -0,0 +1,104 @@
# SSH Proxy VPN (Android)
Приложение поднимает **SSH-туннель по ключу**, внутри него — **SOCKS5** (`ssh -D`), и через **VpnService** направляет **весь трафик Android** в локальный SOCKS.
## Схема
```
Приложения Android
VpnService (TUN 10.8.0.2)
tun2socks (native)
SOCKS5 127.0.0.1:10808 ← SSH dynamic forward (-D)
SSH-сервер
Интернет
```
## Сборка
1. Откройте папку `D:\SshProxyVpn` в **Android Studio** (Hedgehog+).
2. Подключите native-модуль **hev-socks5-tunnel** (обязательно для реальной маршрутизации):
```powershell
cd D:\SshProxyVpn
.\setup-native.ps1
```
Или вручную:
```bash
cd D:/SshProxyVpn/app/src/main/cpp
git clone --recursive https://github.com/heiher/hev-socks5-tunnel.git
cd ../../..
powershell -ExecutionPolicy Bypass -File fix-symlinks.ps1
```
> **Windows:** Git часто не создаёт symlink-заголовки (`../src/...`). Скрипт `fix-symlinks.ps1` заменяет их на реальные `.h` файлы. Без этого сборка падает с ошибкой `hev-memory-allocator-api.h`.
3. Соберите проект: **Build → Make Project**.
4. Установите APK на телефон (minSdk 26).
> Без `hev-socks5-tunnel` SSH подключится, но VPN не пропустит трафик — приложение покажет ошибку native-модуля.
## Настройка
| Поле | Пример |
|------|--------|
| SSH хост | `203.0.113.10` |
| Порт | `22` |
| Пользователь | `ubuntu` |
| Ключ | OpenSSH private key (`id_ed25519`, `id_rsa`) |
| Passphrase | если ключ защищён |
| SOCKS порт | `10808` (локальный) |
На сервере в `~/.ssh/authorized_keys` должен быть ваш **публичный** ключ.
## Как работает код
| Компонент | Файл |
|-----------|------|
| UI и сохранение настроек | `MainActivity.kt` |
| SSH + SOCKS (локальный SOCKS5 через SSH direct-tcpip) | `ssh/SshTunnelManager.kt`, `ssh/LocalSocks5Proxy.kt` |
| Foreground-сервис оркестрации | `service/TunnelService.kt` |
| VPN-интерфейс | `vpn/ProxyVpnService.kt` |
| TUN → SOCKS | `vpn/Tun2Socks.kt` + `cpp/tun2socks_jni.cpp` |
**Важно:** перед включением VPN вызывается `VpnService.protect()` для SSH-сокета, иначе SSH уйдёт в собственный туннель и соединение оборвётся.
## Разрешения
- `INTERNET` — SSH
- `FOREGROUND_SERVICE` — фоновый туннель
- **VPN** — системный запрос при первом подключении
## Ограничения
- Нужен **NDK** для сборки native-части.
- Производительность ниже, чем у WireGuard/OpenVPN.
- Некоторые приложения с certificate pinning могут не работать через прокси.
- Для production добавьте проверку **host key** SSH (сейчас есть опция «принимать неизвестный ключ»).
## Тест SSH без VPN
На ПК с тем же ключом:
```bash
ssh -i id_ed25519 -D 10808 user@server
curl --socks5 127.0.0.1:10808 https://ifconfig.me
```
На Android SOCKS доступен только внутри приложения на `127.0.0.1:10808` после подключения SSH.
## Структура проекта
```
SshProxyVpn/
app/src/main/java/com/sshproxy/vpn/
app/src/main/cpp/
tun2socks_jni.cpp
hev-socks5-tunnel/ ← клонировать вручную
```

75
app/build.gradle.kts Normal file
View File

@ -0,0 +1,75 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.parcelize")
}
android {
namespace = "com.sshproxy.vpn"
compileSdk = 34
defaultConfig {
applicationId = "com.sshproxy.vpn"
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.0.0"
ndk {
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
}
externalNativeBuild {
cmake {
cppFlags += "-std=c++17"
}
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
viewBinding = true
}
packaging {
jniLibs {
useLegacyPackaging = true
}
}
externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
}
}
}
dependencies {
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4")
implementation("androidx.lifecycle:lifecycle-service:2.8.4")
implementation("androidx.recyclerview:recyclerview:1.3.2")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")
implementation("com.github.mwiede:jsch:0.2.20")
}

0
app/proguard-rules.pro vendored Normal file
View File

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent>
</queries>
<application
android:name=".TunnelApplication"
android:allowBackup="false"
android:extractNativeLibs="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.SshProxyVpn">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".AppExclusionActivity"
android:exported="false"
android:parentActivityName=".MainActivity" />
<activity
android:name=".TunnelSettingsActivity"
android:exported="false"
android:parentActivityName=".MainActivity" />
<service
android:name=".service.TunnelService"
android:exported="false"
android:stopWithTask="false"
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="SSH VPN tunnel" />
</service>
<service
android:name=".vpn.ProxyVpnService"
android:exported="false"
android:stopWithTask="false"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
</application>
</manifest>

View File

@ -0,0 +1,92 @@
cmake_minimum_required(VERSION 3.22.1)
project(tun2socks_jni)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 11)
enable_language(ASM)
set(HEV_DIR ${CMAKE_CURRENT_SOURCE_DIR}/hev-socks5-tunnel)
find_library(log-lib log)
if(EXISTS ${HEV_DIR}/src/hev-main.c)
message(STATUS "Building with hev-socks5-tunnel")
# --- yaml ---
file(GLOB YAML_SOURCES ${HEV_DIR}/third-part/yaml/src/*.c)
add_library(hev_yaml STATIC ${YAML_SOURCES})
target_include_directories(hev_yaml PUBLIC ${HEV_DIR}/third-part/yaml/src)
target_compile_definitions(hev_yaml PRIVATE
YAML_VERSION_MAJOR=0
YAML_VERSION_MINOR=2
YAML_VERSION_PATCH=5
YAML_VERSION_STRING=\"0.2.5\"
)
# --- lwip ---
file(GLOB_RECURSE LWIP_SOURCES ${HEV_DIR}/third-part/lwip/src/*.c)
add_library(hev_lwip STATIC ${LWIP_SOURCES})
target_include_directories(hev_lwip PUBLIC
${HEV_DIR}/third-part/lwip/src/include
${HEV_DIR}/third-part/lwip/src/ports/include
)
target_compile_definitions(hev_lwip PRIVATE FD_SET_DEFINED SOCKLEN_T_DEFINED)
# --- hev-task-system ---
file(GLOB_RECURSE TASK_SOURCES ${HEV_DIR}/third-part/hev-task-system/src/*.c)
set(TASK_ASM ${HEV_DIR}/third-part/hev-task-system/src/kern/task/hev-task-execute.S)
add_library(hev_task STATIC ${TASK_SOURCES} ${TASK_ASM})
target_include_directories(hev_task PUBLIC
${HEV_DIR}/third-part/hev-task-system/include
${HEV_DIR}/third-part/hev-task-system/src
)
set_source_files_properties(${TASK_ASM} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp")
target_compile_definitions(hev_task PRIVATE
ENABLE_STACK_OVERFLOW_DETECTION=1
ENABLE_MEMALLOC_SLICE=1
ENABLE_IO_SPLICE_SYSCALL=1
CONFIG_STACK_BACKEND=STACK_MMAP
CONFIG_STACK_OVERFLOW_DETECTION=1
CONFIG_MEMALLOC_SLICE_ALIGN=16
CONFIG_MEMALLOC_SLICE_MAX_SIZE=4096
CONFIG_MEMALLOC_SLICE_MAX_COUNT=1000
CONFIG_SCHED_CLOCK=CLOCK_NONE
)
# --- hev-socks5-tunnel (без hev-jni.c свой JNI) ---
file(GLOB HEV_MAIN_SOURCES ${HEV_DIR}/src/*.c)
file(GLOB_RECURSE HEV_CORE_SOURCES ${HEV_DIR}/src/core/src/*.c)
file(GLOB HEV_MISC_SOURCES ${HEV_DIR}/src/misc/*.c)
list(FILTER HEV_MAIN_SOURCES EXCLUDE REGEX ".*/hev-jni\\.c$")
add_library(hev_socks5 STATIC
${HEV_MAIN_SOURCES}
${HEV_CORE_SOURCES}
${HEV_MISC_SOURCES}
)
target_include_directories(hev_socks5 PUBLIC
${HEV_DIR}/src
${HEV_DIR}/src/core/include
${HEV_DIR}/src/misc
${HEV_DIR}/third-part/yaml/include
${HEV_DIR}/third-part/lwip/src/include
${HEV_DIR}/third-part/lwip/src/ports/include
${HEV_DIR}/third-part/hev-task-system/include
)
target_compile_definitions(hev_socks5 PRIVATE
ENABLE_LIBRARY=1
FD_SET_DEFINED=1
SOCKLEN_T_DEFINED=1
COMMIT_ID=\"embedded\"
)
target_link_libraries(hev_socks5 PUBLIC hev_yaml hev_lwip hev_task)
add_library(tun2socks_jni SHARED tun2socks_jni.cpp)
target_compile_definitions(tun2socks_jni PRIVATE USE_HEV_TUN2SOCKS=1)
target_include_directories(tun2socks_jni PRIVATE ${HEV_DIR}/src)
target_link_libraries(tun2socks_jni hev_socks5 ${log-lib} atomic)
else()
message(WARNING "hev-socks5-tunnel not found — clone into app/src/main/cpp/hev-socks5-tunnel")
add_library(tun2socks_jni SHARED tun2socks_jni.cpp)
target_link_libraries(tun2socks_jni ${log-lib})
endif()

View File

@ -0,0 +1,115 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: TopLevel
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '.*'
Priority: 1
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: Always
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 4
UseTab: Never
...

View File

@ -0,0 +1,11 @@
/.idea/
/.vscode/
/.git/
/.github/
/README.md
/License
/Dockerfile
/conf/

View File

@ -0,0 +1,408 @@
name: "Build"
on:
push:
branches:
- main
pull_request:
release:
types:
- published
workflow_dispatch:
jobs:
source:
name: Source
runs-on: ubuntu-latest
env:
BRANCH_NAME: ${{ github.base_ref || github.ref_name }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
submodules: true
- name: Gen Source
run: |
REV_ID=$(git rev-parse --short HEAD)
mkdir -p hev-socks5-tunnel-${{ env.BRANCH_NAME }}
git ls-files --recurse-submodules | tar c -O -T- | tar x -C hev-socks5-tunnel-${{ env.BRANCH_NAME }}
echo ${REV_ID} > hev-socks5-tunnel-${{ env.BRANCH_NAME }}/.rev-id
tar cJf hev-socks5-tunnel-${{ env.BRANCH_NAME }}.tar.xz hev-socks5-tunnel-${{ env.BRANCH_NAME }}
- name: Upload source
uses: actions/upload-artifact@v7
with:
name: hev-socks5-tunnel-${{ env.BRANCH_NAME }}.tar.xz
path: hev-socks5-tunnel-${{ env.BRANCH_NAME }}.tar.xz
if-no-files-found: error
retention-days: 1
linux:
name: Linux
runs-on: ubuntu-latest
strategy:
matrix:
include:
- name: arm64
tool: aarch64-unknown-linux-musl
- name: arm32
tool: arm-unknown-linux-musleabi
- name: arm32hf
tool: arm-unknown-linux-musleabihf
- name: arm32v7
tool: armv7-unknown-linux-musleabi
- name: arm32v7hf
tool: armv7-unknown-linux-musleabihf
- name: i586
tool: i586-unknown-linux-musl
- name: i686
tool: i686-unknown-linux-musl
- name: loong64
tool: loongarch64-unknown-linux-musl
- name: m68k
tool: m68k-unknown-linux-musl
- name: microblazeel
tool: microblazeel-xilinx-linux-musl
- name: microblaze
tool: microblaze-xilinx-linux-musl
- name: mips64el
tool: mips64el-unknown-linux-musl
- name: mips64
tool: mips64-unknown-linux-musl
- name: mips32el
tool: mipsel-unknown-linux-musl
- name: mips32elsf
tool: mipsel-unknown-linux-muslsf
- name: mips32
tool: mips-unknown-linux-musl
- name: mips32sf
tool: mips-unknown-linux-muslsf
- name: or1k
tool: or1k-unknown-linux-musl
- name: powerpc64le
tool: powerpc64le-unknown-linux-musl
- name: powerpc64
tool: powerpc64-unknown-linux-musl
- name: powerpcle
tool: powerpcle-unknown-linux-musl
- name: powerpc
tool: powerpc-unknown-linux-musl
- name: riscv32
tool: riscv32-unknown-linux-musl
- name: riscv64
tool: riscv64-unknown-linux-musl
- name: s390x
tool: s390x-ibm-linux-musl
- name: sh4
tool: sh4-multilib-linux-musl
- name: x86_64
tool: x86_64-unknown-linux-musl
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
submodules: true
- name: Build ${{ matrix.name }}
run: |
sudo mkdir -p /opt/x-tools
wget https://github.com/cross-tools/musl-cross/releases/download/20260515/${{ matrix.tool }}.tar.xz
sudo tar xf ${{ matrix.tool }}.tar.xz -C /opt/x-tools
make CROSS_PREFIX=/opt/x-tools/${{ matrix.tool }}/bin/${{ matrix.tool }}- CFLAGS=${{ matrix.env.CFLAGS }} ENABLE_STATIC=1 -j`nproc`
cp bin/hev-socks5-tunnel hev-socks5-tunnel-linux-${{ matrix.name }}
- name: Upload ${{ matrix.name }}
uses: actions/upload-artifact@v7
with:
name: hev-socks5-tunnel-linux-${{ matrix.name }}
path: hev-socks5-tunnel-linux-${{ matrix.name }}
if-no-files-found: error
retention-days: 1
macos:
name: macOS
runs-on: macos-latest
strategy:
matrix:
include:
- name: arm64
flags: -arch arm64 -mmacosx-version-min=11.0
- name: x86_64
flags: -arch x86_64 -mmacosx-version-min=10.6
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
submodules: true
- name: Build ${{ matrix.name }}
run: |
make CC=clang CFLAGS="${{ matrix.flags }}" LFLAGS="${{ matrix.flags }}" -j $(sysctl -n hw.logicalcpu)
cp bin/hev-socks5-tunnel hev-socks5-tunnel-darwin-${{ matrix.name }}
- name: Upload ${{ matrix.name }}
uses: actions/upload-artifact@v7
with:
name: hev-socks5-tunnel-darwin-${{ matrix.name }}
path: hev-socks5-tunnel-darwin-${{ matrix.name }}
if-no-files-found: error
retention-days: 1
freebsd:
name: FreeBSD
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
submodules: true
- name: Build
uses: vmactions/freebsd-vm@v1
with:
usesh: true
prepare: |
pkg install -y gmake gcc
run: |
gmake
cp bin/hev-socks5-tunnel hev-socks5-tunnel-freebsd-x86_64
- name: Upload
uses: actions/upload-artifact@v7
with:
name: hev-socks5-tunnel-freebsd-x86_64
path: hev-socks5-tunnel-freebsd-x86_64
if-no-files-found: error
retention-days: 1
windows:
name: Windows
runs-on: windows-latest
defaults:
run:
shell: cmd
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
submodules: true
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: MSYS
location: D:\msys2
update: true
install: >-
gcc
git
make
wget
zip
- name: Build
shell: msys2 {0}
run: |
export MSYS=winsymlinks:native
git clone --depth=1 --recursive file://`pwd` work; cd work
make -j`nproc`
mkdir hev-socks5-tunnel
cp bin/hev-socks5-tunnel* hev-socks5-tunnel
cp third-part/wintun/bin/wintun.dll hev-socks5-tunnel
wget -P hev-socks5-tunnel https://github.com/heiher/msys2/releases/latest/download/msys-2.0.dll
zip -r ../hev-socks5-tunnel-win64.zip hev-socks5-tunnel
- name: Upload ${{ matrix.name }}
uses: actions/upload-artifact@v7
with:
name: hev-socks5-tunnel-win64.zip
path: hev-socks5-tunnel-win64.zip
if-no-files-found: error
retention-days: 1
release:
name: Release
runs-on: ubuntu-latest
needs:
- source
- linux
- macos
- freebsd
- windows
if: github.event_name == 'release'
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Download artifacts
uses: actions/download-artifact@v8
with:
path: release
pattern: "hev-*"
merge-multiple: true
- name: Upload artifacts
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
for i in release/hev-*; do
gh release upload ${{ github.event.release.tag_name }} $i
done
apple:
name: Apple
runs-on: macos-latest
if: github.event_name != 'release'
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
submodules: true
- name: Build
run: |
./build-apple.sh
android:
name: Android
runs-on: ubuntu-latest
if: github.event_name != 'release'
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
submodules: true
- name: Prepare
run: |
wget https://dl.google.com/android/repository/android-ndk-r27d-linux.zip
unzip android-ndk-r27d-linux.zip
ln -sf . jni
- name: Build
run: |
./android-ndk-r27d/ndk-build
llvm:
name: LLVM
runs-on: ubuntu-latest
if: github.event_name != 'release'
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
submodules: true
- name: Prepare
run: |
sudo apt install -y clang
- name: Build
run: |
make CC=clang ENABLE_STATIC=1 -j`nproc`
docker:
name: Build Docker Image
runs-on: ubuntu-latest
strategy:
matrix:
platform:
- linux/amd64
- linux/386
- linux/arm64/v8
- linux/arm/v7
- linux/arm/v6
- linux/riscv64
permissions:
packages: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
submodules: true
- name: Prepare QEMU
uses: docker/setup-qemu-action@v3
- name: Prepare Buildx
uses: docker/setup-buildx-action@v3
- name: Prepare Repo Name
id: repo
run: |
echo "repository=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT
- name: Prepare Digest
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Login GitHub Packages Docker Image Repository
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push Docker Image
id: build
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: ${{ matrix.platform }}
provenance: false
outputs: type=image,name=ghcr.io/${{ steps.repo.outputs.repository }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }}
cache-from: type=gha,scope=${{ matrix.platform }}
cache-to: type=gha,mode=max,scope=${{ matrix.platform }}
- name: Export Digest
if: github.event_name != 'pull_request'
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload Digest
uses: actions/upload-artifact@v7
if: github.event_name != 'pull_request'
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
docker-merge:
name: Merge Docker Image Tags
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
needs:
- docker
permissions:
packages: write
contents: read
steps:
- name: Download Digests
uses: actions/download-artifact@v8
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Prepare Buildx
uses: docker/setup-buildx-action@v3
- name: Prepare Repo Name
id: repo
run: |
echo "repository=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT
- name: Login GitHub Packages Docker Image Repository
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Format Docker Image Meta
uses: docker/metadata-action@v5
id: docker_meta
with:
images: ghcr.io/${{ steps.repo.outputs.repository }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=nightly,enable={{is_default_branch}}
- name: Create Manifest List and Push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/${{ steps.repo.outputs.repository }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ghcr.io/${{ steps.repo.outputs.repository }}:${{ steps.docker_meta.outputs.version }}

View File

@ -0,0 +1,26 @@
name: "Check code formatting"
on:
push:
branches:
- main
pull_request:
jobs:
checker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Prepare
run: |
sudo apt install -y clang-format
- name: Format
run: |
find src -iname "*.[h,c]" -exec clang-format -i {} \;
- name: Check
run: |
git status
git diff --quiet

View File

@ -0,0 +1,5 @@
bin
build
HevSocks5Tunnel.xcframework
obj
libs

View File

@ -0,0 +1,12 @@
[submodule "third-part/hev-task-system"]
path = third-part/hev-task-system
url = https://github.com/heiher/hev-task-system
[submodule "third-part/yaml"]
path = third-part/yaml
url = https://github.com/heiher/yaml
[submodule "third-part/lwip"]
path = third-part/lwip
url = https://github.com/heiher/lwip
[submodule "src/core"]
path = src/core
url = https://github.com/heiher/hev-socks5-core

View File

@ -0,0 +1,51 @@
# Copyright (C) 2021 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
TOP_PATH := $(call my-dir)
ifeq ($(filter $(modules-get-list),yaml),)
include $(TOP_PATH)/third-part/yaml/Android.mk
endif
ifeq ($(filter $(modules-get-list),lwip),)
include $(TOP_PATH)/third-part/lwip/Android.mk
endif
ifeq ($(filter $(modules-get-list),hev-task-system),)
include $(TOP_PATH)/third-part/hev-task-system/Android.mk
endif
LOCAL_PATH = $(TOP_PATH)
SRCDIR := $(LOCAL_PATH)/src
include $(CLEAR_VARS)
include $(LOCAL_PATH)/build.mk
LOCAL_MODULE := hev-socks5-tunnel
LOCAL_SRC_FILES := $(patsubst $(SRCDIR)/%,src/%,$(SRCFILES))
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/src \
$(LOCAL_PATH)/src/misc \
$(LOCAL_PATH)/src/core/include \
$(LOCAL_PATH)/third-part/yaml/include \
$(LOCAL_PATH)/third-part/lwip/src/include \
$(LOCAL_PATH)/third-part/lwip/src/ports/include \
$(LOCAL_PATH)/third-part/hev-task-system/include
LOCAL_CFLAGS += -DFD_SET_DEFINED -DSOCKLEN_T_DEFINED -DENABLE_LIBRARY
LOCAL_CFLAGS += $(VERSION_CFLAGS)
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_CFLAGS += -mfpu=neon
endif
LOCAL_STATIC_LIBRARIES := yaml lwip hev-task-system
LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384
LOCAL_LDFLAGS += -Wl,-z,common-page-size=16384
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,21 @@
# Copyright (C) 2013 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
APP_OPTIM := release
APP_PLATFORM := android-29
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
APP_CFLAGS := -O3
APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true
NDK_TOOLCHAIN_VERSION := clang

View File

@ -0,0 +1,43 @@
FROM alpine:latest AS builder
RUN apk add --update --no-cache \
make \
git \
gcc \
linux-headers \
musl-dev
WORKDIR /src
COPY . /src
RUN make clean && make
FROM alpine:latest
LABEL org.opencontainers.image.source="https://github.com/heiher/hev-socks5-tunnel"
RUN apk add --update --no-cache \
iproute2
ENV TUN=tun0 \
MTU=8500 \
IPV4=198.18.0.1 \
IPV6='' \
TABLE=20 \
MARK=438 \
SOCKS5_ADDR=172.17.0.1 \
SOCKS5_PORT=1080 \
SOCKS5_USERNAME='' \
SOCKS5_PASSWORD='' \
SOCKS5_UDP_MODE=udp \
SOCKS5_UDP_ADDR='' \
CONFIG_ROUTES=1 \
IPV4_INCLUDED_ROUTES=0.0.0.0/0 \
IPV4_EXCLUDED_ROUTES='' \
LOG_LEVEL=warn
HEALTHCHECK --start-period=5s --interval=5s --timeout=2s --retries=3 CMD ["test", "-f", "/success"]
COPY --chmod=755 docker/entrypoint.sh /entrypoint.sh
COPY --from=builder /src/bin/hev-socks5-tunnel /usr/bin/hev-socks5-tunnel
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -0,0 +1,19 @@
Copyright (c) 2022 hev
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,146 @@
# Makefile for hev-socks5-tunnel
PROJECT=hev-socks5-tunnel
CROSS_PREFIX :=
PP=$(CROSS_PREFIX)cpp
CC=$(CROSS_PREFIX)gcc
AR=$(CROSS_PREFIX)ar
STRIP=$(CROSS_PREFIX)strip
CCFLAGS=-O3 -pipe -Wall -Werror $(CFLAGS) \
-I$(SRCDIR) \
-I$(SRCDIR)/misc \
-I$(SRCDIR)/core/include \
-I$(THIRDPARTDIR)/yaml/include \
-I$(THIRDPARTDIR)/wintun/include \
-I$(THIRDPARTDIR)/lwip/src/include \
-I$(THIRDPARTDIR)/lwip/src/ports/include \
-I$(THIRDPARTDIR)/hev-task-system/include
LDFLAGS=-L$(THIRDPARTDIR)/yaml/bin -lyaml \
-L$(THIRDPARTDIR)/lwip/bin -llwip \
-L$(THIRDPARTDIR)/hev-task-system/bin -lhev-task-system \
-lpthread $(LFLAGS)
SRCDIR=src
BINDIR=bin
CONFDIR=conf
BUILDDIR=build
INSTDIR=/usr/local
THIRDPARTDIR=third-part
CONFIG=$(CONFDIR)/main.yml
EXEC_TARGET=$(BINDIR)/hev-socks5-tunnel
STATIC_TARGET=$(BINDIR)/lib$(PROJECT).a
SHARED_TARGET=$(BINDIR)/lib$(PROJECT).so
THIRDPARTS=$(THIRDPARTDIR)/yaml \
$(THIRDPARTDIR)/lwip \
$(THIRDPARTDIR)/hev-task-system
$(STATIC_TARGET) : CCFLAGS+=-DENABLE_LIBRARY
$(SHARED_TARGET) : CCFLAGS+=-DENABLE_LIBRARY -fPIC
$(SHARED_TARGET) : LDFLAGS+=-shared -pthread
-include build.mk
CCFLAGS+=$(VERSION_CFLAGS)
CCSRCS=$(filter %.c,$(SRCFILES))
ASSRCS=$(filter %.S,$(SRCFILES))
LDOBJS=$(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(CCSRCS)) \
$(patsubst $(SRCDIR)/%.S,$(BUILDDIR)/%.o,$(ASSRCS))
DEPEND=$(LDOBJS:.o=.dep)
BUILDMSG="\e[1;31mBUILD\e[0m %s\n"
LINKMSG="\e[1;34mLINK\e[0m \e[1;32m%s\e[0m\n"
STRIPMSG="\e[1;34mSTRIP\e[0m \e[1;32m%s\e[0m\n"
CLEANMSG="\e[1;34mCLEAN\e[0m %s\n"
INSTMSG="\e[1;34mINST\e[0m %s -> %s\n"
UNINSMSG="\e[1;34mUNINS\e[0m %s\n"
ENABLE_DEBUG :=
ifeq ($(ENABLE_DEBUG),1)
CCFLAGS+=-g -O0 -DENABLE_DEBUG
STRIP=true
endif
ENABLE_STATIC :=
ifeq ($(ENABLE_STATIC),1)
CCFLAGS+=-static
endif
ifeq ($(MSYSTEM),MSYS)
LDFLAGS+=-lmsys-2.0 -lws2_32 -lIphlpapi
endif
V :=
ECHO_PREFIX := @
ifeq ($(V),1)
undefine ECHO_PREFIX
endif
.PHONY: exec static shared clean install uninstall tp-static tp-shared tp-clean
exec : $(EXEC_TARGET)
static : $(STATIC_TARGET)
shared : $(SHARED_TARGET)
tp-static : $(THIRDPARTS)
@$(foreach dir,$^,$(MAKE) --no-print-directory -C $(dir) static;)
tp-shared : $(THIRDPARTS)
@$(foreach dir,$^,$(MAKE) --no-print-directory -C $(dir) shared;)
tp-clean : $(THIRDPARTS)
@$(foreach dir,$^,$(MAKE) --no-print-directory -C $(dir) clean;)
clean : tp-clean
$(ECHO_PREFIX) $(RM) -rf $(BINDIR) $(BUILDDIR)
@printf $(CLEANMSG) $(PROJECT)
install : $(INSTDIR)/bin/$(PROJECT) $(INSTDIR)/etc/$(PROJECT).yml
uninstall :
$(ECHO_PREFIX) $(RM) -rf $(INSTDIR)/bin/$(PROJECT)
@printf $(UNINSMSG) $(INSTDIR)/bin/$(PROJECT)
$(ECHO_PREFIX) $(RM) -rf $(INSTDIR)/etc/$(PROJECT).yml
@printf $(UNINSMSG) $(INSTDIR)/etc/$(PROJECT).yml
$(INSTDIR)/bin/$(PROJECT) : $(EXEC_TARGET)
$(ECHO_PREFIX) install -d -m 0755 $(dir $@)
$(ECHO_PREFIX) install -m 0755 $< $@
@printf $(INSTMSG) $< $@
$(INSTDIR)/etc/$(PROJECT).yml : $(CONFIG)
$(ECHO_PREFIX) install -d -m 0755 $(dir $@)
$(ECHO_PREFIX) install -m 0644 $< $@
@printf $(INSTMSG) $< $@
$(EXEC_TARGET) : $(LDOBJS) tp-static
$(ECHO_PREFIX) mkdir -p $(dir $@)
$(ECHO_PREFIX) $(CC) $(CCFLAGS) -o $@ $(LDOBJS) $(LDFLAGS)
@printf $(LINKMSG) $@
$(ECHO_PREFIX) $(STRIP) $@
@printf $(STRIPMSG) $@
$(STATIC_TARGET) : $(LDOBJS) tp-static
$(ECHO_PREFIX) mkdir -p $(dir $@)
$(ECHO_PREFIX) $(AR) csq $@ $(LDOBJS)
@printf $(LINKMSG) $@
$(SHARED_TARGET) : $(LDOBJS) tp-shared
$(ECHO_PREFIX) mkdir -p $(dir $@)
$(ECHO_PREFIX) $(CC) $(CCFLAGS) -o $@ $(LDOBJS) $(LDFLAGS)
@printf $(LINKMSG) $@
$(BUILDDIR)/%.dep : $(SRCDIR)/%.c
$(ECHO_PREFIX) mkdir -p $(dir $@)
$(ECHO_PREFIX) $(PP) $(CCFLAGS) -MM -MT$(@:.dep=.o) -MF$@ $< 2>/dev/null
$(BUILDDIR)/%.o : $(SRCDIR)/%.c
$(ECHO_PREFIX) mkdir -p $(dir $@)
$(ECHO_PREFIX) $(CC) $(CCFLAGS) -c -o $@ $<
@printf $(BUILDMSG) $<
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPEND)
endif

View File

@ -0,0 +1,398 @@
# HevSocks5Tunnel
[![status](https://github.com/heiher/hev-socks5-tunnel/actions/workflows/build.yaml/badge.svg?branch=main&event=push)](https://github.com/heiher/hev-socks5-tunnel)
A simple, lightweight tunnel over Socks5 proxy (tun2socks).
## Features
* IPv4/IPv6. (dual stack)
* Redirect TCP connections.
* Redirect UDP packets. (Fullcone NAT, UDP-in-UDP and UDP-in-TCP [^1])
* Linux/Android/FreeBSD/macOS/iOS/Windows.
## Benchmarks
See [here](https://github.com/heiher/hev-socks5-tunnel/wiki/Benchmarks) for more details.
### Speed
![](https://github.com/heiher/hev-socks5-tunnel/wiki/res/upload-speed.png)
![](https://github.com/heiher/hev-socks5-tunnel/wiki/res/download-speed.png)
### CPU usage
![](https://github.com/heiher/hev-socks5-tunnel/wiki/res/upload-cpu.png)
![](https://github.com/heiher/hev-socks5-tunnel/wiki/res/download-cpu.png)
### Memory usage
![](https://github.com/heiher/hev-socks5-tunnel/wiki/res/upload-mem.png)
![](https://github.com/heiher/hev-socks5-tunnel/wiki/res/download-mem.png)
## How to Build
### Unix
```bash
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
make
```
### Android
```bash
mkdir hev-socks5-tunnel
cd hev-socks5-tunnel
git clone --recursive https://github.com/heiher/hev-socks5-tunnel jni
ndk-build
```
### iOS and macOS
```bash
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
# will generate HevSocks5Tunnel.xcframework
./build-apple.sh
```
### Windows (MSYS2)
```bash
export MSYS=winsymlinks:native
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
make
```
### Library
```bash
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
# Static library
make static
# Shared library
make shared
```
## How to Use
### Config
```yaml
tunnel:
# Interface name
name: tun0
# Interface MTU
mtu: 8500
# Multi-queue
multi-queue: false
# IPv4 address
ipv4: 198.18.0.1
# IPv6 address
ipv6: 'fc00::1'
# Post up script
# post-up-script: up.sh
# Pre down script
# pre-down-script: down.sh
socks5:
# Socks5 server port
port: 1080
# Socks5 server address (ipv4/ipv6)
address: 127.0.0.1
# Socks5 UDP relay mode (tcp|udp)
udp: 'udp'
# Override the UDP address provided by the Socks5 server (ipv4/ipv6)
# udp-address: ''
# Socks5 handshake using pipeline mode
# pipeline: false
# Socks5 server username
# username: 'username'
# Socks5 server password
# password: 'password'
# Socket mark
# mark: 0
# TCP fastopen
# tcp-fastopen: false
#mapdns:
# Mapped DNS address
# address: 198.18.0.2
# Mapped DNS port
# port: 53
# Mapped IP network base
# network: 100.64.0.0
# Mapped IP network mask
# netmask: 255.192.0.0
# Mapped DNS cache size
# cache-size: 10000
#misc:
# task stack size (bytes)
# task-stack-size: 86016
# tcp buffer size (bytes)
# tcp-buffer-size: 65536
# udp socket recv buffer (SO_RCVBUF) size (bytes)
# udp-recv-buffer-size: 524288
# number of udp buffers in splice, 1500 bytes per buffer.
# udp-copy-buffer-nums: 10
# maximum session count (0: unlimited)
# max-session-count: 0
# connect timeout (ms)
# connect-timeout: 10000
# TCP read-write timeout (ms)
# tcp-read-write-timeout: 300000
# UDP read-write timeout (ms)
# udp-read-write-timeout: 60000
# stdout, stderr or file-path
# log-file: stderr
# debug, info, warn or error
# log-level: warn
# If present, run as a daemon with this pid file
# pid-file: /run/hev-socks5-tunnel.pid
# If present, set rlimit nofile; else use default value
# limit-nofile: 65535
```
### Run
#### Linux
```bash
# Set socks5.mark = 438
bin/hev-socks5-tunnel conf/main.yml
# Disable reverse path filter
sudo sysctl -w net.ipv4.conf.all.rp_filter=0
sudo sysctl -w net.ipv4.conf.tun0.rp_filter=0
# Bypass upstream socks5 server
sudo ip rule add fwmark 438 lookup main pref 10
sudo ip -6 rule add fwmark 438 lookup main pref 10
# Route others
sudo ip route add default dev tun0 table 20
sudo ip rule add lookup 20 pref 20
sudo ip -6 route add default dev tun0 table 20
sudo ip -6 rule add lookup 20 pref 20
```
#### FreeBSD/macOS
```zsh
# Bypass upstream socks5 server
# 10.0.0.1: socks5 server
# 10.0.2.2: default gateway
sudo route add -net 10.0.0.1/32 10.0.2.2
# Route others
sudo route change -inet default -interface tun0
sudo route change -inet6 default -interface tun0
```
#### Windows
```zsh
# Bypass upstream socks5 server
# 10.0.0.1: socks5 server
# 10.0.2.2: default gateway
route add 10.0.0.1/32 10.0.2.2
# Route others
route change 0.0.0.0/0 0.0.0.0 if tun-index
route change ::/0 :: if tun-index
```
#### OpenWrt 24.10+
Repo: https://github.com/openwrt/packages/tree/master/net/hev-socks5-tunnel
```sh
# Install package
opkg install hev-socks5-tunnel
# Edit /etc/config/hev-socks5-tunnel
# Restart service
/etc/init.d/hev-socks5-tunnel restart
```
#### Low memory usage
On low-memory systems like iOS, reducing the size of the TCP buffer and
task stack, as well as limiting the maximum session count, can help prevent
out-of-memory issues.
```yaml
misc:
# task stack size (bytes)
task-stack-size: 24576 # 20480 + tcp-buffer-size
# tcp buffer size (bytes)
tcp-buffer-size: 4096
# maximum session count
max-session-count: 1200
```
#### Docker Compose
```yaml
version: "3.9"
services:
client:
image: alpine:latest # just for network testing
tty: true # you can test network in terminal
depends_on:
tun:
condition: service_healthy
network_mode: "service:tun"
tun:
image: ghcr.io/heiher/hev-socks5-tunnel:latest # `latest` for the latest published version; `nightly` for the latest source build; `vX.Y.Z` for the specific version
cap_add:
- NET_ADMIN # needed
devices:
- /dev/net/tun:/dev/net/tun # needed
environment:
TUN: tun0 # optional, tun interface name, default `tun0`
MTU: 8500 # optional, MTU is MTU, default `8500`
IPV4: 198.18.0.1 # optional, tun interface ip, default `198.18.0.1`
TABLE: 20 # optional, ip route table id, default `20`
MARK: 438 # optional, ip route rule mark, dec or hex format, default `438`
SOCKS5_ADDR: a.b.c.d # socks5 proxy server address
SOCKS5_PORT: 1080 # socks5 proxy server port
SOCKS5_USERNAME: user # optional, socks5 proxy username, only set when need to auth
SOCKS5_PASSWORD: pass # optional, socks5 proxy password, only set when need to auth
SOCKS5_UDP_MODE: udp # optional, UDP relay mode, default `udp`, other option `tcp`
SOCKS5_UDP_ADDR: a.b.c.d # optional, override the UDP address provided by the Socks5 server
CONFIG_ROUTES: 1 # optional, set 0 to ignore TABLE, IPV4_INCLUDED_ROUTES and IPV4_EXCLUDED_ROUTES, with MARK defaults to 0
IPV4_INCLUDED_ROUTES: 0.0.0.0/0 # optional, demo means proxy all traffic. for multiple network segments, join with `,` or `\n`
IPV4_EXCLUDED_ROUTES: a.b.c.d # optional, demo means exclude traffic from the proxy itself. for multiple network segments, join with `,` or `\n`
LOG_LEVEL: warn # optional, default `warn`, other option `debug`/`info`/`error`
dns:
- 8.8.8.8
```
You can also set the route rules with multiple network segments like:
```yaml
environment:
IPV4_INCLUDED_ROUTES: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
IPV4_EXCLUDED_ROUTES: |-
a.b.c.d/24
a.b.c.f/24
```
## API
```c
/**
* hev_socks5_tunnel_main:
* @config_path: config file path
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Alias of hev_socks5_tunnel_main_from_file
*
* Returns: returns zero on successful, otherwise returns -1.
*
* Since: 2.4.6
*/
int hev_socks5_tunnel_main (const char *config_path, int tun_fd);
/**
* hev_socks5_tunnel_main_from_file:
* @config_path: config file path
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Returns: returns zero on successful, otherwise returns -1.
*
* Since: 2.6.7
*/
int hev_socks5_tunnel_main_from_file (const char *config_path, int tun_fd);
/**
* hev_socks5_tunnel_main_from_str:
* @config_str: string config
* @config_len: the byte length of string config
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Returns: returns zero on successful, otherwise returns -1.
*
* Since: 2.6.7
*/
int hev_socks5_tunnel_main_from_str (const unsigned char *config_str,
unsigned int config_len, int tun_fd);
/**
* hev_socks5_tunnel_quit:
*
* Stop the socks5 tunnel.
*
* Since: 2.4.6
*/
void hev_socks5_tunnel_quit (void);
/**
* hev_socks5_tunnel_stats:
* @tx_packets (out): transmitted packets
* @tx_bytes (out): transmitted bytes
* @rx_packets (out): received packets
* @rx_bytes (out): received bytes
*
* Retrieve tunnel interface traffic statistics.
*
* Since: 2.6.5
*/
void hev_socks5_tunnel_stats (size_t *tx_packets, size_t *tx_bytes,
size_t *rx_packets, size_t *rx_bytes);
```
## Use Cases
### Android VPN
* [SocksTun](https://github.com/heiher/sockstun)
### iOS
* [Tun2SocksKit](https://github.com/EbrahimTahernejad/Tun2SocksKit)
## Contributors
* **arror** - https://github.com/arror
* **bazuchan** - https://github.com/bazuchan
* **codewithtamim** - https://github.com/codewithtamim
* **dovecoteescapee** - https://github.com/dovecoteescapee
* **ebrahimtahernejad** - https://github.com/ebrahimtahernejad
* **heiby** - https://github.com/heiby
* **hev** - https://hev.cc
* **katana** - https://github.com/officialkatana
* **pronebird** - https://github.com/pronebird
* **saeeddev94** - https://github.com/saeeddev94
* **sskaje** - https://github.com/sskaje
* **wankkoree** - https://github.com/wankkoree
* **xiguagua** - https://github.com/xiguagua
* **xz-dev** - https://github.com/xz-dev
* **yiguous** - https://github.com/yiguous
* **yujinpan** - https://github.com/yujinpan
* **zheshinicheng** - https://github.com/zheshinicheng
## License
MIT
[^1]: See [protocol specification](https://github.com/heiher/hev-socks5-core/tree/main?tab=readme-ov-file#udp-in-tcp). The [hev-socks5-server](https://github.com/heiher/hev-socks5-server) supports UDP relay over TCP.

View File

@ -0,0 +1,76 @@
#!/bin/bash
set -e
XCFRAMEWORK_DIR="./apple_xcframework"
LIB_NAME="HevSocks5Tunnel"
# buildStatic iphoneos -mios-version-min=15.0 arm64
buildStatic()
{
echo "build for $1, $2, min version $3"
local MIN_VERSION="-m$1-version-min=$3"
make PP="xcrun --sdk $1 --toolchain $1 clang" \
CC="xcrun --sdk $1 --toolchain $1 clang" \
CFLAGS="-arch $2 $MIN_VERSION" \
LFLAGS="-arch $2 $MIN_VERSION -Wl,-Bsymbolic-functions" static
local OUTPUT_DIR="$XCFRAMEWORK_DIR/$1-$2"
mkdir -p $OUTPUT_DIR
local OUTPUT_ARCH_FILE="$OUTPUT_DIR/libhev-socks5-tunnel.a"
libtool -static -o $OUTPUT_ARCH_FILE \
bin/libhev-socks5-tunnel.a \
third-part/lwip/bin/liblwip.a \
third-part/yaml/bin/libyaml.a \
third-part/hev-task-system/bin/libhev-task-system.a
make clean
}
mergeStatic()
{
echo "merge for $1, $2, $3"
local FIRST_LIB_FILE="$XCFRAMEWORK_DIR/$1-$2/libhev-socks5-tunnel.a"
local SECOND_LIB_FILE="$XCFRAMEWORK_DIR/$1-$3/libhev-socks5-tunnel.a"
local OUTPUT_DIR="$XCFRAMEWORK_DIR/$1-$2-$3"
mkdir -p $OUTPUT_DIR
local OUTPUT_ARCH_FILE="$OUTPUT_DIR/libhev-socks5-tunnel.a"
lipo -create \
-arch $2 $FIRST_LIB_FILE \
-arch $3 $SECOND_LIB_FILE \
-output $OUTPUT_ARCH_FILE
}
rm -rf $XCFRAMEWORK_DIR
rm -rf HevSocks5Tunnel.xcframework
mkdir $XCFRAMEWORK_DIR
buildStatic iphoneos arm64 15.0
buildStatic iphonesimulator x86_64 15.0
buildStatic iphonesimulator arm64 15.0
mergeStatic iphonesimulator x86_64 arm64
# keep same with flutter
buildStatic macosx x86_64 10.14
buildStatic macosx arm64 10.14
mergeStatic macosx x86_64 arm64
buildStatic appletvos arm64 17.0
buildStatic appletvsimulator x86_64 17.0
buildStatic appletvsimulator arm64 17.0
mergeStatic appletvsimulator x86_64 arm64
INCLUDE_DIR="$XCFRAMEWORK_DIR/include"
mkdir -p $INCLUDE_DIR/$LIB_NAME
cp ./src/hev-main.h $INCLUDE_DIR/$LIB_NAME
cp ./module.modulemap $INCLUDE_DIR/$LIB_NAME
xcodebuild -create-xcframework \
-library ./apple_xcframework/iphoneos-arm64/libhev-socks5-tunnel.a -headers $INCLUDE_DIR \
-library ./apple_xcframework/iphonesimulator-x86_64-arm64/libhev-socks5-tunnel.a -headers $INCLUDE_DIR \
-library ./apple_xcframework/macosx-x86_64-arm64/libhev-socks5-tunnel.a -headers $INCLUDE_DIR \
-library ./apple_xcframework/appletvos-arm64/libhev-socks5-tunnel.a -headers $INCLUDE_DIR \
-library ./apple_xcframework/appletvsimulator-x86_64-arm64/libhev-socks5-tunnel.a -headers $INCLUDE_DIR \
-output ./HevSocks5Tunnel.xcframework
rm -rf ./apple_xcframework

View File

@ -0,0 +1,20 @@
# Build
rwildcard=$(foreach d,$(wildcard $1*), \
$(call rwildcard,$d/,$2) \
$(filter $(subst *,%,$2),$d))
SRCFILES=$(call rwildcard,$(SRCDIR)/,*.c *.S)
ifeq ($(REV_ID),)
ifneq (,$(wildcard .rev-id))
REV_ID=$(shell cat .rev-id)
endif
ifeq ($(REV_ID),)
REV_ID=$(shell git -C $(SRCDIR) rev-parse --short HEAD)
endif
ifeq ($(REV_ID),)
REV_ID=unknown
endif
endif
VERSION_CFLAGS=-DCOMMIT_ID=\"$(REV_ID)\"

View File

@ -0,0 +1,75 @@
# Main configuration for hev-socks5-tunnel
tunnel:
# Interface name
name: tun0
# Interface MTU
mtu: 8500
# Multi-queue
multi-queue: false
# IPv4 address
ipv4: 198.18.0.1
# IPv6 address
ipv6: 'fc00::1'
# Post up script
# post-up-script: up.sh
# Pre down script
# pre-down-script: down.sh
socks5:
# Socks5 server port
port: 1080
# Socks5 server address (ipv4/ipv6)
address: 127.0.0.1
# Socks5 UDP relay mode (tcp|udp)
udp: 'udp'
# Override the UDP address provided by the Socks5 server (ipv4/ipv6)
# udp-address: ''
# Socks5 handshake using pipeline mode
# pipeline: false
# Socks5 server username
# username: 'username'
# Socks5 server password
# password: 'password'
# Socket mark
# mark: 0
# TCP fastopen
# tcp-fastopen: false
#mapdns:
# Mapped DNS address
# address: 198.18.0.2
# Mapped DNS port
# port: 53
# Mapped IP network base
# network: 100.64.0.0
# Mapped IP network mask
# netmask: 255.192.0.0
# Mapped DNS cache size
# cache-size: 10000
#misc:
# task stack size (bytes)
# task-stack-size: 86016
# tcp buffer size (bytes)
# tcp-buffer-size: 65536
# udp socket recv buffer (SO_RCVBUF) size (bytes)
# udp-recv-buffer-size: 524288
# number of udp buffers in splice, 1500 bytes per buffer.
# udp-copy-buffer-nums: 10
# maximum session count (0: unlimited)
# max-session-count: 0
# connect timeout (ms)
# connect-timeout: 10000
# TCP read-write timeout (ms)
# tcp-read-write-timeout: 300000
# UDP read-write timeout (ms)
# udp-read-write-timeout: 60000
# stdout, stderr or file-path
# log-file: stderr
# debug, info, warn or error
# log-level: warn
# If present, run as a daemon with this pid file
# pid-file: /run/hev-socks5-tunnel.pid
# If present, set rlimit nofile; else use default value
# limit-nofile: 65535

View File

@ -0,0 +1,89 @@
#!/bin/sh
TUN="${TUN:-tun0}"
MTU="${MTU:-8500}"
IPV4="${IPV4:-198.18.0.1}"
IPV6="${IPV6:-}"
CONFIG_ROUTES="${CONFIG_ROUTES:-1}"
TABLE="${TABLE:-20}"
if [ "${CONFIG_ROUTES}" == "0" ]; then
MARK="${MARK:-0}"
else
MARK="${MARK:-438}"
fi
SOCKS5_ADDR="${SOCKS5_ADDR:-172.17.0.1}"
SOCKS5_PORT="${SOCKS5_PORT:-1080}"
SOCKS5_USERNAME="${SOCKS5_USERNAME:-}"
SOCKS5_PASSWORD="${SOCKS5_PASSWORD:-}"
SOCKS5_UDP_MODE="${SOCKS5_UDP_MODE:-udp}"
SOCKS5_UDP_ADDR="${SOCKS5_UDP_ADDR:-}"
IPV4_INCLUDED_ROUTES="${IPV4_INCLUDED_ROUTES:-0.0.0.0/0}"
IPV4_EXCLUDED_ROUTES="${IPV4_EXCLUDED_ROUTES:-}"
LOG_LEVEL="${LOG_LEVEL:-warn}"
config_file() {
cat > /hs5t.yml << EOF
misc:
log-level: '${LOG_LEVEL}'
tunnel:
name: '${TUN}'
mtu: ${MTU}
ipv4: '${IPV4}'
ipv6: '${IPV6}'
post-up-script: '/route.sh'
socks5:
address: '${SOCKS5_ADDR}'
port: ${SOCKS5_PORT}
udp: '${SOCKS5_UDP_MODE}'
mark: ${MARK}
EOF
if [ -n "${SOCKS5_USERNAME}" ]; then
echo " username: '${SOCKS5_USERNAME}'" >> /hs5t.yml
fi
if [ -n "${SOCKS5_PASSWORD}" ]; then
echo " password: '${SOCKS5_PASSWORD}'" >> /hs5t.yml
fi
if [ -n "${SOCKS5_UDP_ADDR}" ]; then
echo " udp-address: '${SOCKS5_UDP_ADDR}'" >> /hs5t.yml
fi
}
config_route() {
echo "#!/bin/sh" > /route.sh
chmod +x /route.sh
if [ "${CONFIG_ROUTES}" == "0" ]; then
return
fi
echo "ip route add default dev ${TUN} table ${TABLE}" >> /route.sh
for addr in $(echo ${IPV4_INCLUDED_ROUTES} | tr ',' '\n'); do
echo "ip rule add to ${addr} table ${TABLE}" >> /route.sh
done
echo "ip rule add to $(ip -o -f inet address show eth0 | awk '/scope global/ {print $4}') table main" >> /route.sh
for addr in $(echo ${IPV4_EXCLUDED_ROUTES} | tr ',' '\n'); do
echo "ip rule add to ${addr} table main" >> /route.sh
done
echo "ip rule add fwmark ${MARK} table main pref 1" >> /route.sh
}
run() {
config_file
config_route
echo "echo 1 > /success" >> /route.sh
hev-socks5-tunnel /hs5t.yml
}
run || exit 1

View File

@ -0,0 +1,90 @@
/*
============================================================================
Name : hev-main.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2023 hev
Description : Main
============================================================================
*/
#ifndef __HEV_MAIN_H__
#define __HEV_MAIN_H__
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* hev_socks5_tunnel_main:
* @config_path: config file path
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Returns: returns zero on successful, otherwise returns -1.
*
* Since: 2.4.6
*/
int hev_socks5_tunnel_main (const char *config_path, int tun_fd);
/**
* hev_socks5_tunnel_main_from_file:
* @config_path: config file path
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Returns: returns zero on successful, otherwise returns -1.
*
* Since: 2.6.7
*/
int hev_socks5_tunnel_main_from_file (const char *config_path, int tun_fd);
/**
* hev_socks5_tunnel_main_from_str:
* @config_str: string config
* @config_len: the byte length of string config
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Returns: returns zero on successful, otherwise returns -1.
*
* Since: 2.6.7
*/
int hev_socks5_tunnel_main_from_str (const unsigned char *config_str,
unsigned int config_len, int tun_fd);
/**
* hev_socks5_tunnel_quit:
*
* Stop the socks5 tunnel.
*
* Since: 2.4.6
*/
void hev_socks5_tunnel_quit (void);
/**
* hev_socks5_tunnel_stats:
* @tx_packets (out): transmitted packets
* @tx_bytes (out): transmitted bytes
* @rx_packets (out): received packets
* @rx_bytes (out): received bytes
*
* Retrieve tunnel interface traffic statistics.
*
* Since: 2.6.5
*/
void hev_socks5_tunnel_stats (size_t *tx_packets, size_t *tx_bytes,
size_t *rx_packets, size_t *rx_bytes);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_MAIN_H__ */

View File

@ -0,0 +1,4 @@
module HevSocks5Tunnel {
umbrella header "hev-main.h"
export *
}

View File

@ -0,0 +1,115 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: TopLevel
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '.*'
Priority: 1
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: Always
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 4
UseTab: Never
...

View File

@ -0,0 +1,26 @@
name: "Check code formatting"
on:
push:
branches:
- main
pull_request:
jobs:
checker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Prepare
run: |
sudo apt install -y clang-format
- name: Format
run: |
find src -iname "*.[h,c]" -exec clang-format -i {} \;
- name: Check
run: |
git status
git diff --quiet

View File

@ -0,0 +1,19 @@
Copyright (c) 2022 hev
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,204 @@
# HevSocks5Core
HevSocks5Core is a simple, lightweight socks5 library.
**Features**
* IPv4/IPv6. (dual stack)
* Standard `CONNECT` command.
* Standard `UDP ASSOCIATE` command.
* Extended `FWD UDP` command. (UDP in TCP)
* Multiple username/password authentication.
**Dependencies**
* HevTaskSystem - https://github.com/heiher/hev-task-system
## Examples
### Server
```c
#include <unistd.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-task-io-socket.h>
#include <hev-task-dns.h>
#include <hev-task-system.h>
#include <hev-socks5-server.h>
static void
server_entry (void *data)
{
HevSocks5Server *server = data;
hev_socks5_server_run (server);
hev_object_unref (HEV_OBJECT (server));
}
static void
listener_entry (void *data)
{
struct addrinfo hints = { 0 };
struct addrinfo *result;
int fd;
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hev_task_dns_getaddrinfo (NULL, "1080", &hints, &result);
fd = hev_task_io_socket_socket (AF_INET6, SOCK_STREAM, 0);
bind (fd, result->ai_addr, result->ai_addrlen);
freeaddrinfo (result);
listen (fd, 5);
hev_task_add_fd (hev_task_self (), fd, POLLIN);
for (;;) {
HevSocks5Server *server;
HevTask *task;
int nfd;
nfd = hev_task_io_socket_accept (fd, NULL, NULL, NULL, NULL);
task = hev_task_new (-1);
server = hev_socks5_server_new (nfd);
hev_task_run (task, server_entry, server);
}
close (fd);
}
int
main (int argc, char *argv[])
{
HevTask *task;
hev_task_system_init ();
task = hev_task_new (-1);
hev_task_run (task, listener_entry, NULL);
hev_task_system_run ();
hev_task_system_fini ();
return 0;
}
```
### Client
```c
#include <stddef.h>
#include <hev-task.h>
#include <hev-task-system.h>
#include <hev-socks5-client-tcp.h>
#include <hev-socks5-client-udp.h>
static void
tcp_client_entry (void *data)
{
HevSocks5ClientTCP *tcp;
tcp = hev_socks5_client_tcp_new_name ("www.google.com", 443);
hev_socks5_client_connect (HEV_SOCKS5_CLIENT (tcp), "127.0.0.1", 1080);
hev_socks5_client_handshake (HEV_SOCKS5_CLIENT (tcp));
/*
* splice data to/from a socket fd:
* hev_socks5_tcp_splice (HEV_SOCKS5_TCP (tcp), fd);
*/
hev_object_unref (HEV_OBJECT (tcp));
}
static void
udp_client_entry (void *data)
{
HevSocks5ClientUDP *udp;
udp = hev_socks5_client_udp_new (HEV_SOCKS5_TYPE_UDP_IN_TCP);
hev_socks5_client_connect (HEV_SOCKS5_CLIENT (udp), "127.0.0.1", 1080);
hev_socks5_client_handshake (HEV_SOCKS5_CLIENT (udp));
/*
* HevSocks5UDPMsg msgv[num];
*
* send udp packets:
* hev_socks5_udp_sendmmsg (HEV_SOCKS5_UDP (udp), msgv, num);
*
* recv udp packets:
* hev_socks5_udp_recvmmsg (HEV_SOCKS5_UDP (udp), msgv, num, 0);
*/
hev_object_unref (HEV_OBJECT (udp));
}
int
main (int argc, char *argv[])
{
HevTask *task;
hev_task_system_init ();
task = hev_task_new (-1);
hev_task_run (task, tcp_client_entry, NULL);
task = hev_task_new (-1);
hev_task_run (task, udp_client_entry, NULL);
hev_task_system_run ();
hev_task_system_fini ();
return 0;
}
```
## UDP in TCP
UDP-in-TCP mode is a proprietary extension based on RFC 1928, designed to
forward UDP packets within the primary SOCKS5 TCP stream. The protocol is
defined as follows:
### SOCKS5 Requests
```
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
```
* CMD
* UDP IN TCP: X'05'
### UDP Relays
```
+--------+--------+------+----------+----------+----------+
| MSGLEN | HDRLEN | ATYP | DST.ADDR | DST.PORT | DATA |
+--------+--------+------+----------+----------+----------+
| 2 | 1 | 1 | Variable | 2 | Variable |
+--------+--------+------+----------+----------+----------+
```
- MSGLEN: The total length of the UDP relay message. `[MSGLEN, DATA]`
- HDRLEN: The header length of the UDP relay message. `[MSGLEN, DST.PORT]`
- ATYPE/DST.ADDR/DST.PORT: Fields follow the definitions specified in RFC 1928.
## Users
* **HevSocks5Server** - https://github.com/heiher/hev-socks5-server
* **HevSocks5TProxy** - https://github.com/heiher/hev-socks5-tproxy
* **HevSocks5Tunnel** - https://github.com/heiher/hev-socks5-tunnel
## Contributors
* **hev** - https://hev.cc
* **spider84** - https://github.com/spider84
## License
MIT

View File

@ -0,0 +1,68 @@
/*
============================================================================
Name : hev-rbtree.h
Authors : Andrea Arcangeli <andrea@suse.de>
David Woodhouse <dwmw2@infradead.org>
Michel Lespinasse <walken@google.com>
Heiher <r@hev.cc>
Copyright : Copyright (c) 2018 everyone.
Description : RedBlack Tree
============================================================================
*/
#ifndef __HEV_RBTREE_H__
#define __HEV_RBTREE_H__
#include <stddef.h>
typedef struct _HevRBTree HevRBTree;
typedef struct _HevRBTreeNode HevRBTreeNode;
struct _HevRBTree
{
HevRBTreeNode *root;
};
struct _HevRBTreeNode
{
unsigned long __parent_color;
HevRBTreeNode *right;
HevRBTreeNode *left;
} __attribute__ ((aligned (sizeof (long))));
static inline int
hev_rbtree_node_empty (HevRBTreeNode *node)
{
return node->__parent_color == (unsigned long)node;
}
static inline HevRBTreeNode *
hev_rbtree_node_parent (HevRBTreeNode *node)
{
return (HevRBTreeNode *)(node->__parent_color & ~3);
}
static inline void
hev_rbtree_node_link (HevRBTreeNode *node, HevRBTreeNode *parent,
HevRBTreeNode **link)
{
node->__parent_color = (unsigned long)parent;
node->left = node->right = NULL;
*link = node;
}
HevRBTreeNode *hev_rbtree_node_prev (HevRBTreeNode *node);
HevRBTreeNode *hev_rbtree_node_next (HevRBTreeNode *node);
HevRBTreeNode *hev_rbtree_first (HevRBTree *self);
HevRBTreeNode *hev_rbtree_last (HevRBTree *self);
void hev_rbtree_insert_color (HevRBTree *self, HevRBTreeNode *node);
void hev_rbtree_replace (HevRBTree *self, HevRBTreeNode *victim,
HevRBTreeNode *new);
void hev_rbtree_erase (HevRBTree *self, HevRBTreeNode *node);
#endif /* __HEV_RBTREE_H__ */

View File

@ -0,0 +1,64 @@
/*
============================================================================
Name : hev-socks5-authenticator.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2023 hev
Description : Socks5 Authenticator
============================================================================
*/
#ifndef __HEV_SOCKS5_AUTHENTICATOR_H__
#define __HEV_SOCKS5_AUTHENTICATOR_H__
#include <hev-object-atomic.h>
#include "hev-rbtree.h"
#include "hev-socks5-user.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_AUTHENTICATOR(p) ((HevSocks5Authenticator *)p)
#define HEV_SOCKS5_AUTHENTICATOR_CLASS(p) ((HevSocks5AuthenticatorClass *)p)
#define HEV_SOCKS5_AUTHENTICATOR_TYPE (hev_socks5_authenticator_class ())
typedef struct _HevSocks5Authenticator HevSocks5Authenticator;
typedef struct _HevSocks5AuthenticatorClass HevSocks5AuthenticatorClass;
typedef enum _HevSocks5AuthenticatorType HevSocks5AuthenticatorType;
struct _HevSocks5Authenticator
{
HevObjectAtomic base;
HevRBTree tree;
};
struct _HevSocks5AuthenticatorClass
{
HevObjectAtomicClass base;
};
HevObjectClass *hev_socks5_authenticator_class (void);
int hev_socks5_authenticator_construct (HevSocks5Authenticator *self);
HevSocks5Authenticator *hev_socks5_authenticator_new (void);
int hev_socks5_authenticator_add (HevSocks5Authenticator *self,
HevSocks5User *user);
int hev_socks5_authenticator_del (HevSocks5Authenticator *self,
const char *name, unsigned int name_len);
HevSocks5User *hev_socks5_authenticator_get (HevSocks5Authenticator *self,
const char *name,
unsigned int name_len);
void hev_socks5_authenticator_clear (HevSocks5Authenticator *self);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_AUTHENTICATOR_H__ */

View File

@ -0,0 +1,56 @@
/*
============================================================================
Name : hev-socks5-client-tcp.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 Client TCP
============================================================================
*/
#ifndef __HEV_SOCKS5_CLIENT_TCP_H__
#define __HEV_SOCKS5_CLIENT_TCP_H__
#include "hev-socks5-tcp.h"
#include "hev-socks5-proto.h"
#include "hev-socks5-client.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_CLIENT_TCP(p) ((HevSocks5ClientTCP *)p)
#define HEV_SOCKS5_CLIENT_TCP_CLASS(p) ((HevSocks5ClientTCPClass *)p)
#define HEV_SOCKS5_CLIENT_TCP_TYPE (hev_socks5_client_tcp_class ())
typedef struct _HevSocks5ClientTCP HevSocks5ClientTCP;
typedef struct _HevSocks5ClientTCPClass HevSocks5ClientTCPClass;
struct _HevSocks5ClientTCP
{
HevSocks5Client base;
HevSocks5Addr *addr;
};
struct _HevSocks5ClientTCPClass
{
HevSocks5ClientClass base;
HevSocks5TCPIface tcp;
};
HevObjectClass *hev_socks5_client_tcp_class (void);
int hev_socks5_client_tcp_construct (HevSocks5ClientTCP *self,
const HevSocks5Addr *addr);
HevSocks5ClientTCP *hev_socks5_client_tcp_new_name (const char *name, int port);
HevSocks5ClientTCP *hev_socks5_client_tcp_new_ipv4 (const void *ipv4, int port);
HevSocks5ClientTCP *hev_socks5_client_tcp_new_ipv6 (const void *ipv6, int port);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_CLIENT_TCP_H__ */

View File

@ -0,0 +1,53 @@
/*
============================================================================
Name : hev-socks5-client-udp.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2023 hev
Description : Socks5 Client UDP
============================================================================
*/
#ifndef __HEV_SOCKS5_CLIENT_UDP_H__
#define __HEV_SOCKS5_CLIENT_UDP_H__
#include "hev-socks5-udp.h"
#include "hev-socks5-client.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_CLIENT_UDP(p) ((HevSocks5ClientUDP *)p)
#define HEV_SOCKS5_CLIENT_UDP_CLASS(p) ((HevSocks5ClientUDPClass *)p)
#define HEV_SOCKS5_CLIENT_UDP_TYPE (hev_socks5_client_udp_class ())
typedef struct _HevSocks5ClientUDP HevSocks5ClientUDP;
typedef struct _HevSocks5ClientUDPClass HevSocks5ClientUDPClass;
struct _HevSocks5ClientUDP
{
HevSocks5Client base;
int fd;
};
struct _HevSocks5ClientUDPClass
{
HevSocks5ClientClass base;
HevSocks5UDPIface udp;
};
HevObjectClass *hev_socks5_client_udp_class (void);
int hev_socks5_client_udp_construct (HevSocks5ClientUDP *self,
HevSocks5Type type);
HevSocks5ClientUDP *hev_socks5_client_udp_new (HevSocks5Type type);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_CLIENT_UDP_H__ */

View File

@ -0,0 +1,62 @@
/*
============================================================================
Name : hev-socks5-client.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2024 hev
Description : Socks5 Client
============================================================================
*/
#ifndef __HEV_SOCKS5_CLIENT_H__
#define __HEV_SOCKS5_CLIENT_H__
#include "hev-socks5.h"
#include "hev-socks5-proto.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_CLIENT(p) ((HevSocks5Client *)p)
#define HEV_SOCKS5_CLIENT_CLASS(p) ((HevSocks5ClientClass *)p)
#define HEV_SOCKS5_CLIENT_TYPE (hev_socks5_client_class ())
typedef struct _HevSocks5Client HevSocks5Client;
typedef struct _HevSocks5ClientClass HevSocks5ClientClass;
struct _HevSocks5Client
{
HevSocks5 base;
struct
{
const char *user;
const char *pass;
} auth;
};
struct _HevSocks5ClientClass
{
HevSocks5Class base;
HevSocks5Addr *(*get_upstream_addr) (HevSocks5Client *self);
int (*set_upstream_addr) (HevSocks5Client *self, HevSocks5Addr *addr);
};
HevObjectClass *hev_socks5_client_class (void);
int hev_socks5_client_construct (HevSocks5Client *self, HevSocks5Type type);
int hev_socks5_client_connect (HevSocks5Client *self, const char *addr,
int port);
int hev_socks5_client_handshake (HevSocks5Client *self, int pipeline);
void hev_socks5_client_set_auth (HevSocks5Client *self, const char *user,
const char *pass);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_CLIENT_H__ */

View File

@ -0,0 +1,35 @@
/*
============================================================================
Name : hev-socks5-logger.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 hev
Description : Socks5 Logger
============================================================================
*/
#ifndef __HEV_SOCKS5_LOGGER_H__
#define __HEV_SOCKS5_LOGGER_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef enum _HevSocks5LoggerLevel HevSocks5LoggerLevel;
enum _HevSocks5LoggerLevel
{
HEV_SOCKS5_LOGGER_DEBUG,
HEV_SOCKS5_LOGGER_INFO,
HEV_SOCKS5_LOGGER_WARN,
HEV_SOCKS5_LOGGER_ERROR,
HEV_SOCKS5_LOGGER_UNSET,
};
int hev_socks5_logger_init (HevSocks5LoggerLevel level, const char *path);
void hev_socks5_logger_fini (void);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_LOGGER_H__ */

View File

@ -0,0 +1,48 @@
/*
============================================================================
Name : hev-socks5-misc.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 Misc
============================================================================
*/
#ifndef __HEV_SOCKS5_MISC_H__
#define __HEV_SOCKS5_MISC_H__
#include <netinet/in.h>
#include <hev-task.h>
#include "hev-socks5-proto.h"
#ifdef __cplusplus
extern "C" {
#endif
int hev_socks5_task_io_yielder (HevTaskYieldType type, void *data);
void hev_socks5_set_connect_timeout (int timeout);
void hev_socks5_set_tcp_timeout (int timeout);
void hev_socks5_set_udp_timeout (int timeout);
void hev_socks5_set_task_stack_size (int stack_size);
void hev_socks5_set_udp_recv_buffer_size (int buffer_size);
void hev_socks5_set_udp_copy_buffer_nums (int nums);
int hev_socks5_addr_len (const HevSocks5Addr *addr);
int hev_socks5_addr_from_name (HevSocks5Addr *addr, const char *name, int port);
int hev_socks5_addr_from_ipv4 (HevSocks5Addr *addr, const void *ipv4, int port);
int hev_socks5_addr_from_ipv6 (HevSocks5Addr *addr, const void *ipv6, int port);
int hev_socks5_addr_from_sockaddr6 (HevSocks5Addr *addr,
struct sockaddr_in6 *saddr);
int hev_socks5_addr_into_sockaddr6 (const HevSocks5Addr *addr,
struct sockaddr_in6 *saddr, int *family);
int hev_socks5_name_into_sockaddr6 (const char *host, int port,
struct sockaddr_in6 *saddr, int *family);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_MISC_H__ */

View File

@ -0,0 +1,135 @@
/*
============================================================================
Name : hev-socks5-proto.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2023 hev
Description : Socks5 Proto
============================================================================
*/
#ifndef __HEV_SOCKS5_PROTO_H__
#define __HEV_SOCKS5_PROTO_H__
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum _HevSocks5Version HevSocks5Version;
typedef enum _HevSocks5AuthMethod HevSocks5AuthMethod;
typedef enum _HevSocks5AuthVersion HevSocks5AuthVersion;
typedef enum _HevSocks5ReqCmd HevSocks5ReqCmd;
typedef enum _HevSocks5ResRep HevSocks5ResRep;
typedef enum _HevSocks5AddrType HevSocks5AddrType;
typedef struct _HevSocks5Auth HevSocks5Auth;
typedef struct _HevSocks5Addr HevSocks5Addr;
typedef struct _HevSocks5ReqRes HevSocks5ReqRes;
typedef struct _HevSocks5UDPHdr HevSocks5UDPHdr;
enum _HevSocks5Version
{
HEV_SOCKS5_VERSION_5 = 5,
};
enum _HevSocks5AuthMethod
{
HEV_SOCKS5_AUTH_METHOD_NONE = 0,
HEV_SOCKS5_AUTH_METHOD_USER = 2,
HEV_SOCKS5_AUTH_METHOD_DENY = 255,
};
enum _HevSocks5AuthVersion
{
HEV_SOCKS5_AUTH_VERSION_1 = 1,
};
enum _HevSocks5ReqCmd
{
HEV_SOCKS5_REQ_CMD_CONNECT = 1,
HEV_SOCKS5_REQ_CMD_UDP_ASC = 3,
HEV_SOCKS5_REQ_CMD_FWD_UDP = 5,
};
enum _HevSocks5ResRep
{
HEV_SOCKS5_RES_REP_SUCC = 0,
HEV_SOCKS5_RES_REP_FAIL = 1,
HEV_SOCKS5_RES_REP_HOST = 4,
HEV_SOCKS5_RES_REP_IMPL = 7,
HEV_SOCKS5_RES_REP_ADDR = 8,
};
enum _HevSocks5AddrType
{
HEV_SOCKS5_ADDR_TYPE_IPV4 = 1,
HEV_SOCKS5_ADDR_TYPE_IPV6 = 4,
HEV_SOCKS5_ADDR_TYPE_NAME = 3,
};
struct _HevSocks5Auth
{
uint8_t ver;
union
{
uint8_t method;
uint8_t method_len;
};
uint8_t methods[256];
} __attribute__ ((packed));
struct _HevSocks5Addr
{
uint8_t atype;
union
{
struct
{
uint8_t addr[4];
uint16_t port;
} ipv4 __attribute__ ((packed));
struct
{
uint8_t addr[16];
uint16_t port;
} ipv6 __attribute__ ((packed));
struct
{
uint8_t len;
uint8_t addr[256 + 2];
} domain;
};
} __attribute__ ((packed));
struct _HevSocks5ReqRes
{
uint8_t ver;
union
{
uint8_t cmd;
uint8_t rep;
};
uint8_t rsv;
HevSocks5Addr addr;
} __attribute__ ((packed));
struct _HevSocks5UDPHdr
{
union
{
uint8_t rsv[3];
struct
{
uint16_t datlen;
uint8_t hdrlen;
} __attribute__ ((packed));
};
HevSocks5Addr addr;
} __attribute__ ((packed));
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_PROTO_H__ */

View File

@ -0,0 +1,71 @@
/*
============================================================================
Name : hev-socks5-server.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2023 hev
Description : Socks5 Server
============================================================================
*/
#ifndef __HEV_SOCKS5_SERVER_H__
#define __HEV_SOCKS5_SERVER_H__
#include <hev-object.h>
#include "hev-socks5.h"
#include "hev-socks5-tcp.h"
#include "hev-socks5-udp.h"
#include "hev-socks5-user.h"
#include "hev-socks5-authenticator.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_SERVER(p) ((HevSocks5Server *)p)
#define HEV_SOCKS5_SERVER_CLASS(p) ((HevSocks5ServerClass *)p)
#define HEV_SOCKS5_SERVER_TYPE (hev_socks5_server_class ())
typedef struct _HevSocks5Server HevSocks5Server;
typedef struct _HevSocks5ServerClass HevSocks5ServerClass;
struct _HevSocks5Server
{
HevSocks5 base;
int fds[2];
union
{
HevObject *obj;
HevSocks5User *user;
HevSocks5Authenticator *auth;
};
};
struct _HevSocks5ServerClass
{
HevSocks5Class base;
int (*binder) (HevSocks5Server *self, int sock, struct sockaddr_in6 *src);
HevSocks5TCPIface tcp;
HevSocks5UDPIface udp;
};
HevObjectClass *hev_socks5_server_class (void);
int hev_socks5_server_construct (HevSocks5Server *self, int fd);
HevSocks5Server *hev_socks5_server_new (int fd);
void hev_socks5_server_set_auth (HevSocks5Server *self,
HevSocks5Authenticator *auth);
int hev_socks5_server_run (HevSocks5Server *self);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_SERVER_H__ */

View File

@ -0,0 +1,37 @@
/*
============================================================================
Name : hev-socks5-tcp.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 hev
Description : Socks5 TCP
============================================================================
*/
#ifndef __HEV_SOCKS5_TCP_H__
#define __HEV_SOCKS5_TCP_H__
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_TCP(p) ((HevSocks5TCP *)p)
#define HEV_SOCKS5_TCP_IFACE(p) ((HevSocks5TCPIface *)p)
#define HEV_SOCKS5_TCP_TYPE (hev_socks5_tcp_iface ())
typedef void HevSocks5TCP;
typedef struct _HevSocks5TCPIface HevSocks5TCPIface;
struct _HevSocks5TCPIface
{
int (*splicer) (HevSocks5TCP *self, int fd);
};
void *hev_socks5_tcp_iface (void);
int hev_socks5_tcp_splice (HevSocks5TCP *self, int fd);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_TCP_H__ */

View File

@ -0,0 +1,55 @@
/*
============================================================================
Name : hev-socks5-udp.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 UDP
============================================================================
*/
#ifndef __HEV_SOCKS5_UDP_H__
#define __HEV_SOCKS5_UDP_H__
#include "hev-socks5-proto.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_UDP(p) ((HevSocks5UDP *)p)
#define HEV_SOCKS5_UDP_IFACE(p) ((HevSocks5UDPIface *)p)
#define HEV_SOCKS5_UDP_TYPE (hev_socks5_udp_iface ())
typedef void HevSocks5UDP;
typedef struct _HevSocks5UDPMsg HevSocks5UDPMsg;
typedef struct _HevSocks5UDPIface HevSocks5UDPIface;
struct _HevSocks5UDPMsg
{
HevSocks5Addr *addr;
void *buf;
size_t len;
};
struct _HevSocks5UDPIface
{
int (*get_fd) (HevSocks5UDP *self);
int (*splicer) (HevSocks5UDP *self, int fd);
};
void *hev_socks5_udp_iface (void);
int hev_socks5_udp_get_fd (HevSocks5UDP *self);
int hev_socks5_udp_sendmmsg (HevSocks5UDP *self, HevSocks5UDPMsg *msgv,
unsigned int num);
int hev_socks5_udp_recvmmsg (HevSocks5UDP *self, HevSocks5UDPMsg *msgv,
unsigned int num, int nonblock);
int hev_socks5_udp_splice (HevSocks5UDP *self, int fd);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_UDP_H__ */

View File

@ -0,0 +1,64 @@
/*
============================================================================
Name : hev-socks5-user.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2023 hev
Description : Socks5 User
============================================================================
*/
#ifndef __HEV_SOCKS5_USER_H__
#define __HEV_SOCKS5_USER_H__
#include <hev-object-atomic.h>
#include "hev-rbtree.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_USER(p) ((HevSocks5User *)p)
#define HEV_SOCKS5_USER_CLASS(p) ((HevSocks5UserClass *)p)
#define HEV_SOCKS5_USER_TYPE (hev_socks5_user_class ())
typedef struct _HevSocks5User HevSocks5User;
typedef struct _HevSocks5UserClass HevSocks5UserClass;
struct _HevSocks5User
{
HevObjectAtomic base;
HevRBTreeNode node;
char *name;
char *pass;
unsigned int name_len;
unsigned int pass_len;
};
struct _HevSocks5UserClass
{
HevObjectAtomicClass base;
int (*checker) (HevSocks5User *self, const char *pass,
unsigned int pass_len);
};
HevObjectClass *hev_socks5_user_class (void);
int hev_socks5_user_construct (HevSocks5User *self, const char *name,
unsigned int name_len, const char *pass,
unsigned int pass_len);
HevSocks5User *hev_socks5_user_new (const char *name, unsigned int name_len,
const char *pass, unsigned int pass_len);
int hev_socks5_user_check (HevSocks5User *self, const char *pass,
unsigned int pass_len);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_USER_H__ */

View File

@ -0,0 +1,78 @@
/*
============================================================================
Name : hev-socks5.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2023 hev
Description : Socks5
============================================================================
*/
#ifndef __HEV_SOCKS5_H__
#define __HEV_SOCKS5_H__
#include <netinet/in.h>
#include <sys/socket.h>
#include <hev-object.h>
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5(p) ((HevSocks5 *)p)
#define HEV_SOCKS5_CLASS(p) ((HevSocks5Class *)p)
#define HEV_SOCKS5_TYPE (hev_socks5_class ())
typedef struct _HevSocks5 HevSocks5;
typedef struct _HevSocks5Class HevSocks5Class;
typedef enum _HevSocks5Type HevSocks5Type;
typedef enum _HevSocks5AddrFamily HevSocks5AddrFamily;
enum _HevSocks5Type
{
HEV_SOCKS5_TYPE_NONE,
HEV_SOCKS5_TYPE_TCP,
HEV_SOCKS5_TYPE_UDP_IN_TCP,
HEV_SOCKS5_TYPE_UDP_IN_UDP,
};
enum _HevSocks5AddrFamily
{
HEV_SOCKS5_ADDR_FAMILY_IPV4 = AF_INET,
HEV_SOCKS5_ADDR_FAMILY_IPV6 = AF_INET6,
HEV_SOCKS5_ADDR_FAMILY_UNSPEC = AF_UNSPEC,
};
struct _HevSocks5
{
HevObject base;
int fd;
int timeout;
int udp_associated;
HevSocks5Type type;
HevSocks5AddrFamily addr_family;
};
struct _HevSocks5Class
{
HevObjectClass base;
int (*binder) (HevSocks5 *self, int sock, const struct sockaddr *dest);
};
HevObjectClass *hev_socks5_class (void);
int hev_socks5_construct (HevSocks5 *self, HevSocks5Type type);
int hev_socks5_get_timeout (HevSocks5 *self);
void hev_socks5_set_timeout (HevSocks5 *self, int timeout);
HevSocks5AddrFamily hev_socks5_get_addr_family (HevSocks5 *self);
void hev_socks5_set_addr_family (HevSocks5 *self, HevSocks5AddrFamily family);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_H__ */

View File

@ -0,0 +1,106 @@
/*
============================================================================
Name : hev-compiler.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2019 everyone.
Description : Compiler
============================================================================
*/
#ifndef __HEV_COMPILER_H__
#define __HEV_COMPILER_H__
#ifdef __cplusplus
extern "C" {
#endif
#define ALIGN_UP(addr, align) \
((addr + (typeof (addr))align - 1) & ~((typeof (addr))align - 1))
#define ALIGN_DOWN(addr, align) ((addr) & ~((typeof (addr))align - 1))
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0]))
#endif
#ifndef EXPORT_SYMBOL
#define EXPORT_SYMBOL __attribute__ ((visibility ("default")))
#endif
#define barrier() __asm__ __volatile__ ("" : : : "memory")
static inline void
__read_once_size (void *dst, const volatile void *src, int size)
{
switch (size) {
case sizeof (char):
*(char *)dst = *(volatile char *)src;
break;
case sizeof (short):
*(short *)dst = *(volatile short *)src;
break;
case sizeof (int):
*(int *)dst = *(volatile int *)src;
break;
default:
barrier ();
__builtin_memcpy ((void *)dst, (const void *)src, size);
barrier ();
}
}
static inline void
__write_once_size (volatile void *dst, const void *src, int size)
{
switch (size) {
case sizeof (char):
*(volatile char *)dst = *(char *)src;
break;
case sizeof (short):
*(volatile short *)dst = *(short *)src;
break;
case sizeof (int):
*(volatile int *)dst = *(int *)src;
break;
default:
barrier ();
__builtin_memcpy ((void *)dst, (const void *)src, size);
barrier ();
}
}
#define READ_ONCE(x) \
({ \
union \
{ \
typeof (x) __val; \
char __c[1]; \
} __u; \
__read_once_size (__u.__c, &(x), sizeof (x)); \
__u.__val; \
})
#define WRITE_ONCE(x, val) \
({ \
union \
{ \
typeof (x) __val; \
char __c[1]; \
} __u = { .__val = (typeof (x))(val) }; \
__write_once_size (&(x), __u.__c, sizeof (x)); \
__u.__val; \
})
#ifndef container_of
#define container_of(ptr, type, member) \
({ \
void *__mptr = (void *)(ptr); \
((type *)(__mptr - offsetof (type, member))); \
})
#endif
#ifdef __cplusplus
}
#endif
#endif /* __HEV_COMPILER_H__ */

View File

@ -0,0 +1,635 @@
/*
============================================================================
Name : hev-rbtree.c
Authors : Andrea Arcangeli <andrea@suse.de>
David Woodhouse <dwmw2@infradead.org>
Michel Lespinasse <walken@google.com>
Heiher <r@hev.cc>
Copyright : Copyright (c) 2018 everyone.
Description : RedBlack Tree
============================================================================
*/
#include "hev-rbtree.h"
typedef enum _HevRBTreeColor HevRBTreeColor;
enum _HevRBTreeColor
{
HEV_RBTREE_RED = 0,
HEV_RBTREE_BLACK,
};
static inline int
hev_rbtree_node_color (HevRBTreeNode *node)
{
return node->__parent_color & 1;
}
static inline int
hev_rbtree_node_is_red (HevRBTreeNode *node)
{
return !(hev_rbtree_node_color (node) & HEV_RBTREE_BLACK);
}
static inline int
hev_rbtree_node_is_black (HevRBTreeNode *node)
{
return hev_rbtree_node_color (node) & HEV_RBTREE_BLACK;
}
static inline int
_hev_rbtree_node_is_black (unsigned long pc)
{
return pc & HEV_RBTREE_BLACK;
}
static inline HevRBTreeNode *
_hev_rbtree_node_parent (unsigned long pc)
{
return ((HevRBTreeNode *)(pc & ~3));
}
static inline HevRBTreeNode *
hev_rbtree_node_red_parent (HevRBTreeNode *node)
{
return (HevRBTreeNode *)node->__parent_color;
}
static inline void
hev_rbtree_node_set_black (HevRBTreeNode *node)
{
node->__parent_color |= HEV_RBTREE_BLACK;
}
static inline void
hev_rbtree_node_set_parent (HevRBTreeNode *node, HevRBTreeNode *parent)
{
node->__parent_color = hev_rbtree_node_color (node) | (unsigned long)parent;
}
static inline void
hev_rbtree_node_set_parent_color (HevRBTreeNode *node, HevRBTreeNode *parent,
int color)
{
node->__parent_color = (unsigned long)parent | color;
}
static inline void
hev_rbtree_change_child (HevRBTree *self, HevRBTreeNode *old,
HevRBTreeNode *new, HevRBTreeNode *parent)
{
if (parent) {
if (parent->left == old)
parent->left = new;
else
parent->right = new;
} else {
self->root = new;
}
}
/*
* Helper function for rotations:
* - old's parent and color get assigned to new
* - old gets assigned new as a parent and 'color' as a color.
*/
static inline void
hev_rbtree_rotate_set_parents (HevRBTree *self, HevRBTreeNode *old,
HevRBTreeNode *new, int color)
{
HevRBTreeNode *parent = hev_rbtree_node_parent (old);
new->__parent_color = old->__parent_color;
hev_rbtree_node_set_parent_color (old, new, color);
hev_rbtree_change_child (self, old, new, parent);
}
HevRBTreeNode *
hev_rbtree_node_prev (HevRBTreeNode *node)
{
HevRBTreeNode *parent;
if (hev_rbtree_node_empty (node))
return NULL;
/*
* If we have a left-hand child, go down and then right as far
* as we can.
*/
if (node->left) {
node = node->left;
while (node->right)
node = node->right;
return (HevRBTreeNode *)node;
}
/*
* No left-hand children. Go up till we find an ancestor which
* is a right-hand child of its parent.
*/
while ((parent = hev_rbtree_node_parent (node)) && node == parent->left)
node = parent;
return parent;
}
HevRBTreeNode *
hev_rbtree_node_next (HevRBTreeNode *node)
{
HevRBTreeNode *parent;
if (hev_rbtree_node_empty (node))
return NULL;
/*
* If we have a right-hand child, go down and then left as far
* as we can.
*/
if (node->right) {
node = node->right;
while (node->left)
node = node->left;
return (HevRBTreeNode *)node;
}
/*
* No right-hand children. Everything down and left is smaller than us,
* so any 'next' node must be in the general direction of our parent.
* Go up the tree; any time the ancestor is a right-hand child of its
* parent, keep going up. First time it's a left-hand child of its
* parent, said parent is our 'next' node.
*/
while ((parent = hev_rbtree_node_parent (node)) && node == parent->right)
node = parent;
return parent;
}
HevRBTreeNode *
hev_rbtree_first (HevRBTree *self)
{
HevRBTreeNode *n;
n = self->root;
if (!n)
return NULL;
while (n->left)
n = n->left;
return n;
}
HevRBTreeNode *
hev_rbtree_last (HevRBTree *self)
{
HevRBTreeNode *n;
n = self->root;
if (!n)
return NULL;
while (n->right)
n = n->right;
return n;
}
void
hev_rbtree_insert_color (HevRBTree *self, HevRBTreeNode *node)
{
HevRBTreeNode *parent = hev_rbtree_node_red_parent (node), *gparent, *tmp;
while (1) {
/*
* Loop invariant: node is red.
*/
if (!parent) {
/*
* The inserted node is root. Either this is the
* first node, or we recursed at Case 1 below and
* are no longer violating 4).
*/
hev_rbtree_node_set_parent_color (node, NULL, HEV_RBTREE_BLACK);
break;
}
/*
* If there is a black parent, we are done.
* Otherwise, take some corrective action as,
* per 4), we don't want a red root or two
* consecutive red nodes.
*/
if (hev_rbtree_node_is_black (parent))
break;
gparent = hev_rbtree_node_red_parent (parent);
tmp = gparent->right;
if (parent != tmp) { /* parent == gparent->left */
if (tmp && hev_rbtree_node_is_red (tmp)) {
/*
* Case 1 - node's uncle is red (color flips).
*
* G g
* / \ / \
* p u --> P U
* / /
* n n
*
* However, since g's parent might be red, and
* 4) does not allow this, we need to recurse
* at g.
*/
hev_rbtree_node_set_parent_color (tmp, gparent,
HEV_RBTREE_BLACK);
hev_rbtree_node_set_parent_color (parent, gparent,
HEV_RBTREE_BLACK);
node = gparent;
parent = hev_rbtree_node_parent (node);
hev_rbtree_node_set_parent_color (node, parent, HEV_RBTREE_RED);
continue;
}
tmp = parent->right;
if (node == tmp) {
/*
* Case 2 - node's uncle is black and node is
* the parent's right child (left rotate at parent).
*
* G G
* / \ / \
* p U --> n U
* \ /
* n p
*
* This still leaves us in violation of 4), the
* continuation into Case 3 will fix that.
*/
tmp = node->left;
parent->right = tmp;
node->left = parent;
if (tmp)
hev_rbtree_node_set_parent_color (tmp, parent,
HEV_RBTREE_BLACK);
hev_rbtree_node_set_parent_color (parent, node, HEV_RBTREE_RED);
parent = node;
tmp = node->right;
}
/*
* Case 3 - node's uncle is black and node is
* the parent's left child (right rotate at gparent).
*
* G P
* / \ / \
* p U --> n g
* / \
* n U
*/
gparent->left = tmp; /* == parent->right */
parent->right = gparent;
if (tmp)
hev_rbtree_node_set_parent_color (tmp, gparent,
HEV_RBTREE_BLACK);
hev_rbtree_rotate_set_parents (self, gparent, parent,
HEV_RBTREE_RED);
break;
} else {
tmp = gparent->left;
if (tmp && hev_rbtree_node_is_red (tmp)) {
/* Case 1 - color flips */
hev_rbtree_node_set_parent_color (tmp, gparent,
HEV_RBTREE_BLACK);
hev_rbtree_node_set_parent_color (parent, gparent,
HEV_RBTREE_BLACK);
node = gparent;
parent = hev_rbtree_node_parent (node);
hev_rbtree_node_set_parent_color (node, parent, HEV_RBTREE_RED);
continue;
}
tmp = parent->left;
if (node == tmp) {
/* Case 2 - right rotate at parent */
tmp = node->right;
parent->left = tmp;
node->right = parent;
if (tmp)
hev_rbtree_node_set_parent_color (tmp, parent,
HEV_RBTREE_BLACK);
hev_rbtree_node_set_parent_color (parent, node, HEV_RBTREE_RED);
parent = node;
tmp = node->left;
}
/* Case 3 - left rotate at gparent */
gparent->right = tmp; /* == parent->left */
parent->left = gparent;
if (tmp)
hev_rbtree_node_set_parent_color (tmp, gparent,
HEV_RBTREE_BLACK);
hev_rbtree_rotate_set_parents (self, gparent, parent,
HEV_RBTREE_RED);
break;
}
}
}
void
hev_rbtree_replace (HevRBTree *self, HevRBTreeNode *victim, HevRBTreeNode *new)
{
HevRBTreeNode *parent = hev_rbtree_node_parent (victim);
/* Copy the pointers/colour from the victim to the replacement */
*new = *victim;
/* Set the surrounding nodes to point to the replacement */
if (victim->left)
hev_rbtree_node_set_parent (victim->left, new);
if (victim->right)
hev_rbtree_node_set_parent (victim->right, new);
hev_rbtree_change_child (self, victim, new, parent);
}
static inline HevRBTreeNode *
_hev_rbtree_erase (HevRBTree *self, HevRBTreeNode *node)
{
HevRBTreeNode *child = node->right;
HevRBTreeNode *tmp = node->left;
HevRBTreeNode *parent, *rebalance;
unsigned long pc;
if (!tmp) {
/*
* Case 1: node to erase has no more than 1 child (easy!)
*
* Note that if there is one child it must be red due to 5)
* and node must be black due to 4). We adjust colors locally
* so as to bypass _hev_rbtree_erase_color() later on.
*/
pc = node->__parent_color;
parent = _hev_rbtree_node_parent (pc);
hev_rbtree_change_child (self, node, child, parent);
if (child) {
child->__parent_color = pc;
rebalance = NULL;
} else
rebalance = _hev_rbtree_node_is_black (pc) ? parent : NULL;
tmp = parent;
} else if (!child) {
/* Still case 1, but this time the child is node->left */
tmp->__parent_color = pc = node->__parent_color;
parent = _hev_rbtree_node_parent (pc);
hev_rbtree_change_child (self, node, tmp, parent);
rebalance = NULL;
tmp = parent;
} else {
HevRBTreeNode *successor = child, *child2;
tmp = child->left;
if (!tmp) {
/*
* Case 2: node's successor is its right child
*
* (n) (s)
* / \ / \
* (x) (s) -> (x) (c)
* \
* (c)
*/
parent = successor;
child2 = successor->right;
} else {
/*
* Case 3: node's successor is leftmost under
* node's right child subtree
*
* (n) (s)
* / \ / \
* (x) (y) -> (x) (y)
* / /
* (p) (p)
* / /
* (s) (c)
* \
* (c)
*/
do {
parent = successor;
successor = tmp;
tmp = tmp->left;
} while (tmp);
child2 = successor->right;
parent->left = child2;
successor->right = child;
hev_rbtree_node_set_parent (child, successor);
}
tmp = node->left;
successor->left = tmp;
hev_rbtree_node_set_parent (tmp, successor);
pc = node->__parent_color;
tmp = _hev_rbtree_node_parent (pc);
hev_rbtree_change_child (self, node, successor, tmp);
if (child2) {
hev_rbtree_node_set_parent_color (child2, parent, HEV_RBTREE_BLACK);
rebalance = NULL;
} else {
rebalance = hev_rbtree_node_is_black (successor) ? parent : NULL;
}
successor->__parent_color = pc;
tmp = successor;
}
return rebalance;
}
/*
* Inline version for hev_rbtree_erase() use - we want to be able to inline
* and eliminate the dummy_rotate callback there
*/
static inline void
_hev_rbtree_erase_color (HevRBTree *self, HevRBTreeNode *parent)
{
HevRBTreeNode *node = NULL, *sibling, *tmp1, *tmp2;
while (1) {
/*
* Loop invariants:
* - node is black (or NULL on first iteration)
* - node is not the root (parent is not NULL)
* - All leaf paths going through parent and node have a
* black node count that is 1 lower than other leaf paths.
*/
sibling = parent->right;
if (node != sibling) { /* node == parent->left */
if (hev_rbtree_node_is_red (sibling)) {
/*
* Case 1 - left rotate at parent
*
* P S
* / \ / \
* N s --> p Sr
* / \ / \
* Sl Sr N Sl
*/
tmp1 = sibling->left;
parent->right = tmp1;
sibling->left = parent;
hev_rbtree_node_set_parent_color (tmp1, parent,
HEV_RBTREE_BLACK);
hev_rbtree_rotate_set_parents (self, parent, sibling,
HEV_RBTREE_RED);
sibling = tmp1;
}
tmp1 = sibling->right;
if (!tmp1 || hev_rbtree_node_is_black (tmp1)) {
tmp2 = sibling->left;
if (!tmp2 || hev_rbtree_node_is_black (tmp2)) {
/*
* Case 2 - sibling color flip
* (p could be either color here)
*
* (p) (p)
* / \ / \
* N S --> N s
* / \ / \
* Sl Sr Sl Sr
*
* This leaves us violating 5) which
* can be fixed by flipping p to black
* if it was red, or by recursing at p.
* p is red when coming from Case 1.
*/
hev_rbtree_node_set_parent_color (sibling, parent,
HEV_RBTREE_RED);
if (hev_rbtree_node_is_red (parent))
hev_rbtree_node_set_black (parent);
else {
node = parent;
parent = hev_rbtree_node_parent (node);
if (parent)
continue;
}
break;
}
/*
* Case 3 - right rotate at sibling
* (p could be either color here)
*
* (p) (p)
* / \ / \
* N S --> N sl
* / \ \
* sl Sr S
* \
* Sr
*
* Note: p might be red, and then both
* p and sl are red after rotation(which
* breaks property 4). This is fixed in
* Case 4 (in hev_rbtree_rotate_set_parents()
* which set sl the color of p
* and set p HEV_RBTREE_BLACK)
*
* (p) (sl)
* / \ / \
* N sl --> P S
* \ / \
* S N Sr
* \
* Sr
*/
tmp1 = tmp2->right;
sibling->left = tmp1;
tmp2->right = sibling;
parent->right = tmp2;
if (tmp1)
hev_rbtree_node_set_parent_color (tmp1, sibling,
HEV_RBTREE_BLACK);
tmp1 = sibling;
sibling = tmp2;
}
/*
* Case 4 - left rotate at parent + color flips
* (p and sl could be either color here.
* After rotation, p becomes black, s acquires
* p's color, and sl keeps its color)
*
* (p) (s)
* / \ / \
* N S --> P Sr
* / \ / \
* (sl) sr N (sl)
*/
tmp2 = sibling->left;
parent->right = tmp2;
sibling->left = parent;
hev_rbtree_node_set_parent_color (tmp1, sibling, HEV_RBTREE_BLACK);
if (tmp2)
hev_rbtree_node_set_parent (tmp2, parent);
hev_rbtree_rotate_set_parents (self, parent, sibling,
HEV_RBTREE_BLACK);
break;
} else {
sibling = parent->left;
if (hev_rbtree_node_is_red (sibling)) {
/* Case 1 - right rotate at parent */
tmp1 = sibling->right;
parent->left = tmp1;
sibling->right = parent;
hev_rbtree_node_set_parent_color (tmp1, parent,
HEV_RBTREE_BLACK);
hev_rbtree_rotate_set_parents (self, parent, sibling,
HEV_RBTREE_RED);
sibling = tmp1;
}
tmp1 = sibling->left;
if (!tmp1 || hev_rbtree_node_is_black (tmp1)) {
tmp2 = sibling->right;
if (!tmp2 || hev_rbtree_node_is_black (tmp2)) {
/* Case 2 - sibling color flip */
hev_rbtree_node_set_parent_color (sibling, parent,
HEV_RBTREE_RED);
if (hev_rbtree_node_is_red (parent))
hev_rbtree_node_set_black (parent);
else {
node = parent;
parent = hev_rbtree_node_parent (node);
if (parent)
continue;
}
break;
}
/* Case 3 - left rotate at sibling */
tmp1 = tmp2->left;
sibling->right = tmp1;
tmp2->left = sibling;
parent->left = tmp2;
if (tmp1)
hev_rbtree_node_set_parent_color (tmp1, sibling,
HEV_RBTREE_BLACK);
tmp1 = sibling;
sibling = tmp2;
}
/* Case 4 - right rotate at parent + color flips */
tmp2 = sibling->right;
parent->left = tmp2;
sibling->right = parent;
hev_rbtree_node_set_parent_color (tmp1, sibling, HEV_RBTREE_BLACK);
if (tmp2)
hev_rbtree_node_set_parent (tmp2, parent);
hev_rbtree_rotate_set_parents (self, parent, sibling,
HEV_RBTREE_BLACK);
break;
}
}
}
void
hev_rbtree_erase (HevRBTree *self, HevRBTreeNode *node)
{
HevRBTreeNode *rebalance;
rebalance = _hev_rbtree_erase (self, node);
if (rebalance)
_hev_rbtree_erase_color (self, rebalance);
}

View File

@ -0,0 +1,68 @@
/*
============================================================================
Name : hev-rbtree.h
Authors : Andrea Arcangeli <andrea@suse.de>
David Woodhouse <dwmw2@infradead.org>
Michel Lespinasse <walken@google.com>
Heiher <r@hev.cc>
Copyright : Copyright (c) 2018 everyone.
Description : RedBlack Tree
============================================================================
*/
#ifndef __HEV_RBTREE_H__
#define __HEV_RBTREE_H__
#include <stddef.h>
typedef struct _HevRBTree HevRBTree;
typedef struct _HevRBTreeNode HevRBTreeNode;
struct _HevRBTree
{
HevRBTreeNode *root;
};
struct _HevRBTreeNode
{
unsigned long __parent_color;
HevRBTreeNode *right;
HevRBTreeNode *left;
} __attribute__ ((aligned (sizeof (long))));
static inline int
hev_rbtree_node_empty (HevRBTreeNode *node)
{
return node->__parent_color == (unsigned long)node;
}
static inline HevRBTreeNode *
hev_rbtree_node_parent (HevRBTreeNode *node)
{
return (HevRBTreeNode *)(node->__parent_color & ~3);
}
static inline void
hev_rbtree_node_link (HevRBTreeNode *node, HevRBTreeNode *parent,
HevRBTreeNode **link)
{
node->__parent_color = (unsigned long)parent;
node->left = node->right = NULL;
*link = node;
}
HevRBTreeNode *hev_rbtree_node_prev (HevRBTreeNode *node);
HevRBTreeNode *hev_rbtree_node_next (HevRBTreeNode *node);
HevRBTreeNode *hev_rbtree_first (HevRBTree *self);
HevRBTreeNode *hev_rbtree_last (HevRBTree *self);
void hev_rbtree_insert_color (HevRBTree *self, HevRBTreeNode *node);
void hev_rbtree_replace (HevRBTree *self, HevRBTreeNode *victim,
HevRBTreeNode *new);
void hev_rbtree_erase (HevRBTree *self, HevRBTreeNode *node);
#endif /* __HEV_RBTREE_H__ */

View File

@ -0,0 +1,191 @@
/*
============================================================================
Name : hev-socks5-authenticator.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2023 hev
Description : Socks5 Authenticator
============================================================================
*/
#include <stdlib.h>
#include <string.h>
#include "hev-compiler.h"
#include "hev-socks5-logger-priv.h"
#include "hev-socks5-authenticator.h"
HevSocks5Authenticator *
hev_socks5_authenticator_new (void)
{
HevSocks5Authenticator *self;
int res;
self = calloc (1, sizeof (HevSocks5Authenticator));
if (!self)
return NULL;
res = hev_socks5_authenticator_construct (self);
if (res < 0) {
free (self);
return NULL;
}
LOG_D ("%p socks5 authenticator new", self);
return self;
}
int
hev_socks5_authenticator_add (HevSocks5Authenticator *self, HevSocks5User *user)
{
HevRBTreeNode **new = &self->tree.root, *parent = NULL;
while (*new) {
HevSocks5User *this;
int res;
this = container_of (*new, HevSocks5User, node);
if (this->name_len < user->name_len)
res = -1;
else if (this->name_len > user->name_len)
res = 1;
else
res = memcmp (this->name, user->name, this->name_len);
parent = *new;
if (res < 0)
new = &((*new)->left);
else if (res > 0)
new = &((*new)->right);
else
return -1;
}
hev_rbtree_node_link (&user->node, parent, new);
hev_rbtree_insert_color (&self->tree, &user->node);
return 0;
}
int
hev_socks5_authenticator_del (HevSocks5Authenticator *self, const char *name,
unsigned int name_len)
{
HevRBTreeNode *node = self->tree.root;
while (node) {
HevSocks5User *this;
int res;
this = container_of (node, HevSocks5User, node);
if (this->name_len < name_len)
res = -1;
else if (this->name_len > name_len)
res = 1;
else
res = memcmp (this->name, name, name_len);
if (res < 0) {
node = node->left;
} else if (res > 0) {
node = node->right;
} else {
hev_rbtree_erase (&self->tree, node);
hev_object_unref (HEV_OBJECT (this));
return 0;
}
}
return -1;
}
HevSocks5User *
hev_socks5_authenticator_get (HevSocks5Authenticator *self, const char *name,
unsigned int name_len)
{
HevRBTreeNode *node = self->tree.root;
while (node) {
HevSocks5User *this;
int res;
this = container_of (node, HevSocks5User, node);
if (this->name_len < name_len)
res = -1;
else if (this->name_len > name_len)
res = 1;
else
res = memcmp (this->name, name, name_len);
if (res < 0)
node = node->left;
else if (res > 0)
node = node->right;
else
return this;
}
return NULL;
}
void
hev_socks5_authenticator_clear (HevSocks5Authenticator *self)
{
HevRBTreeNode *n;
while ((n = hev_rbtree_first (&self->tree))) {
HevSocks5User *t;
t = container_of (n, HevSocks5User, node);
hev_rbtree_erase (&self->tree, n);
hev_object_unref (HEV_OBJECT (t));
}
}
int
hev_socks5_authenticator_construct (HevSocks5Authenticator *self)
{
int res;
res = hev_object_atomic_construct (&self->base);
if (res < 0)
return res;
LOG_D ("%p socks5 authenticator construct", self);
HEV_OBJECT (self)->klass = HEV_SOCKS5_AUTHENTICATOR_TYPE;
return 0;
}
static void
hev_socks5_authenticator_destruct (HevObject *base)
{
HevSocks5Authenticator *self = HEV_SOCKS5_AUTHENTICATOR (base);
LOG_D ("%p socks5 authenticator destruct", self);
hev_socks5_authenticator_clear (self);
HEV_OBJECT_ATOMIC_TYPE->destruct (base);
free (base);
}
HevObjectClass *
hev_socks5_authenticator_class (void)
{
static HevSocks5AuthenticatorClass klass;
HevSocks5AuthenticatorClass *kptr = &klass;
HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr);
if (!okptr->name) {
memcpy (kptr, HEV_OBJECT_ATOMIC_TYPE, sizeof (HevObjectAtomicClass));
okptr->name = "HevSocks5Authenticator";
okptr->destruct = hev_socks5_authenticator_destruct;
}
return okptr;
}

View File

@ -0,0 +1,64 @@
/*
============================================================================
Name : hev-socks5-authenticator.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2023 hev
Description : Socks5 Authenticator
============================================================================
*/
#ifndef __HEV_SOCKS5_AUTHENTICATOR_H__
#define __HEV_SOCKS5_AUTHENTICATOR_H__
#include <hev-object-atomic.h>
#include "hev-rbtree.h"
#include "hev-socks5-user.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_AUTHENTICATOR(p) ((HevSocks5Authenticator *)p)
#define HEV_SOCKS5_AUTHENTICATOR_CLASS(p) ((HevSocks5AuthenticatorClass *)p)
#define HEV_SOCKS5_AUTHENTICATOR_TYPE (hev_socks5_authenticator_class ())
typedef struct _HevSocks5Authenticator HevSocks5Authenticator;
typedef struct _HevSocks5AuthenticatorClass HevSocks5AuthenticatorClass;
typedef enum _HevSocks5AuthenticatorType HevSocks5AuthenticatorType;
struct _HevSocks5Authenticator
{
HevObjectAtomic base;
HevRBTree tree;
};
struct _HevSocks5AuthenticatorClass
{
HevObjectAtomicClass base;
};
HevObjectClass *hev_socks5_authenticator_class (void);
int hev_socks5_authenticator_construct (HevSocks5Authenticator *self);
HevSocks5Authenticator *hev_socks5_authenticator_new (void);
int hev_socks5_authenticator_add (HevSocks5Authenticator *self,
HevSocks5User *user);
int hev_socks5_authenticator_del (HevSocks5Authenticator *self,
const char *name, unsigned int name_len);
HevSocks5User *hev_socks5_authenticator_get (HevSocks5Authenticator *self,
const char *name,
unsigned int name_len);
void hev_socks5_authenticator_clear (HevSocks5Authenticator *self);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_AUTHENTICATOR_H__ */

View File

@ -0,0 +1,185 @@
/*
============================================================================
Name : hev-socks5-client-tcp.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 Client TCP
============================================================================
*/
#include <string.h>
#include <hev-memory-allocator.h>
#include "hev-socks5-misc-priv.h"
#include "hev-socks5-logger-priv.h"
#include "hev-socks5-client-tcp.h"
HevSocks5ClientTCP *
hev_socks5_client_tcp_new_name (const char *name, int port)
{
HevSocks5ClientTCP *self;
HevSocks5Addr addr;
int res;
self = hev_malloc0 (sizeof (HevSocks5ClientTCP));
if (!self)
return NULL;
hev_socks5_addr_from_name (&addr, name, port);
res = hev_socks5_client_tcp_construct (self, &addr);
if (res < 0) {
hev_free (self);
return NULL;
}
LOG_D ("%p socks5 client tcp new name", self);
return self;
}
HevSocks5ClientTCP *
hev_socks5_client_tcp_new_ipv4 (const void *ipv4, int port)
{
HevSocks5ClientTCP *self;
HevSocks5Addr addr;
int res;
self = hev_malloc0 (sizeof (HevSocks5ClientTCP));
if (!self)
return NULL;
hev_socks5_addr_from_ipv4 (&addr, ipv4, port);
res = hev_socks5_client_tcp_construct (self, &addr);
if (res < 0) {
hev_free (self);
return NULL;
}
LOG_D ("%p socks5 client tcp new ipv4", self);
return self;
}
HevSocks5ClientTCP *
hev_socks5_client_tcp_new_ipv6 (const void *ipv6, int port)
{
HevSocks5ClientTCP *self;
HevSocks5Addr addr;
int res;
self = hev_malloc0 (sizeof (HevSocks5ClientTCP));
if (!self)
return NULL;
hev_socks5_addr_from_ipv6 (&addr, ipv6, port);
res = hev_socks5_client_tcp_construct (self, &addr);
if (res < 0) {
hev_free (self);
return NULL;
}
LOG_D ("%p socks5 client tcp new ipv6", self);
return self;
}
static HevSocks5Addr *
hev_socks5_client_tcp_get_upstream_addr (HevSocks5Client *base)
{
HevSocks5ClientTCP *self = HEV_SOCKS5_CLIENT_TCP (base);
HevSocks5Addr *addr;
addr = self->addr;
self->addr = NULL;
return addr;
}
static int
hev_socks5_client_tcp_set_upstream_addr (HevSocks5Client *base,
HevSocks5Addr *addr)
{
return 0;
}
int
hev_socks5_client_tcp_construct (HevSocks5ClientTCP *self,
const HevSocks5Addr *addr)
{
int res;
res = hev_socks5_client_construct (&self->base, HEV_SOCKS5_TYPE_TCP);
if (res < 0)
return res;
LOG_D ("%p socks5 client tcp construct", self);
HEV_OBJECT (self)->klass = HEV_SOCKS5_CLIENT_TCP_TYPE;
res = hev_socks5_addr_len (addr);
self->addr = hev_malloc (res);
if (!self->addr)
return -1;
memcpy (self->addr, addr, res);
if (LOG_ON ()) {
const char *str;
char buf[272];
str = hev_socks5_addr_into_str (self->addr, buf, sizeof (buf));
LOG_I ("%p socks5 client tcp -> %s", self, str);
}
return 0;
}
static void
hev_socks5_client_tcp_destruct (HevObject *base)
{
HevSocks5ClientTCP *self = HEV_SOCKS5_CLIENT_TCP (base);
LOG_D ("%p socks5 client tcp destruct", self);
if (self->addr)
hev_free (self->addr);
HEV_SOCKS5_CLIENT_TYPE->destruct (base);
}
static void *
hev_socks5_client_tcp_iface (HevObject *base, void *type)
{
HevSocks5ClientTCPClass *klass = HEV_OBJECT_GET_CLASS (base);
return &klass->tcp;
}
HevObjectClass *
hev_socks5_client_tcp_class (void)
{
static HevSocks5ClientTCPClass klass;
HevSocks5ClientTCPClass *kptr = &klass;
HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr);
if (!okptr->name) {
HevSocks5ClientClass *ckptr;
HevSocks5TCPIface *tiptr;
memcpy (kptr, HEV_SOCKS5_CLIENT_TYPE, sizeof (HevSocks5ClientClass));
okptr->name = "HevSocks5ClientTCP";
okptr->destruct = hev_socks5_client_tcp_destruct;
okptr->iface = hev_socks5_client_tcp_iface;
ckptr = HEV_SOCKS5_CLIENT_CLASS (kptr);
ckptr->get_upstream_addr = hev_socks5_client_tcp_get_upstream_addr;
ckptr->set_upstream_addr = hev_socks5_client_tcp_set_upstream_addr;
tiptr = &kptr->tcp;
memcpy (tiptr, HEV_SOCKS5_TCP_TYPE, sizeof (HevSocks5TCPIface));
}
return okptr;
}

View File

@ -0,0 +1,56 @@
/*
============================================================================
Name : hev-socks5-client-tcp.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 Client TCP
============================================================================
*/
#ifndef __HEV_SOCKS5_CLIENT_TCP_H__
#define __HEV_SOCKS5_CLIENT_TCP_H__
#include "hev-socks5-tcp.h"
#include "hev-socks5-proto.h"
#include "hev-socks5-client.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_CLIENT_TCP(p) ((HevSocks5ClientTCP *)p)
#define HEV_SOCKS5_CLIENT_TCP_CLASS(p) ((HevSocks5ClientTCPClass *)p)
#define HEV_SOCKS5_CLIENT_TCP_TYPE (hev_socks5_client_tcp_class ())
typedef struct _HevSocks5ClientTCP HevSocks5ClientTCP;
typedef struct _HevSocks5ClientTCPClass HevSocks5ClientTCPClass;
struct _HevSocks5ClientTCP
{
HevSocks5Client base;
HevSocks5Addr *addr;
};
struct _HevSocks5ClientTCPClass
{
HevSocks5ClientClass base;
HevSocks5TCPIface tcp;
};
HevObjectClass *hev_socks5_client_tcp_class (void);
int hev_socks5_client_tcp_construct (HevSocks5ClientTCP *self,
const HevSocks5Addr *addr);
HevSocks5ClientTCP *hev_socks5_client_tcp_new_name (const char *name, int port);
HevSocks5ClientTCP *hev_socks5_client_tcp_new_ipv4 (const void *ipv4, int port);
HevSocks5ClientTCP *hev_socks5_client_tcp_new_ipv6 (const void *ipv6, int port);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_CLIENT_TCP_H__ */

View File

@ -0,0 +1,213 @@
/*
============================================================================
Name : hev-socks5-client-udp.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 Client UDP
============================================================================
*/
#include <string.h>
#include <unistd.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-task-io-socket.h>
#include <hev-memory-allocator.h>
#include "hev-socks5-misc-priv.h"
#include "hev-socks5-logger-priv.h"
#include "hev-socks5-client-udp.h"
#define task_io_yielder hev_socks5_task_io_yielder
HevSocks5ClientUDP *
hev_socks5_client_udp_new (HevSocks5Type type)
{
HevSocks5ClientUDP *self;
int res;
self = hev_malloc0 (sizeof (HevSocks5ClientUDP));
if (!self)
return NULL;
res = hev_socks5_client_udp_construct (self, type);
if (res < 0) {
hev_free (self);
return NULL;
}
LOG_D ("%p socks5 client udp new", self);
return self;
}
static HevSocks5Addr *
hev_socks5_client_udp_get_upstream_addr (HevSocks5Client *base)
{
HevSocks5AddrFamily family;
HevSocks5Addr *addr;
family = hev_socks5_get_addr_family (HEV_SOCKS5 (base));
switch (family) {
case HEV_SOCKS5_ADDR_FAMILY_IPV4:
addr = hev_malloc0 (7);
if (addr)
addr->atype = HEV_SOCKS5_ADDR_TYPE_IPV4;
break;
case HEV_SOCKS5_ADDR_FAMILY_IPV6:
addr = hev_malloc0 (19);
if (addr)
addr->atype = HEV_SOCKS5_ADDR_TYPE_IPV6;
break;
default:
addr = NULL;
}
return addr;
}
static int
hev_socks5_client_udp_set_upstream_addr (HevSocks5Client *base,
HevSocks5Addr *addr)
{
HevSocks5ClientUDP *self = HEV_SOCKS5_CLIENT_UDP (base);
struct sockaddr_in6 saddr;
struct sockaddr *sadp;
HevSocks5Class *klass;
int addr_family;
int res;
int fd;
if (HEV_SOCKS5 (base)->type != HEV_SOCKS5_TYPE_UDP_IN_UDP)
return 0;
memset (&saddr, 0, sizeof (saddr));
addr_family = hev_socks5_get_addr_family (HEV_SOCKS5 (self));
res = hev_socks5_addr_into_sockaddr6 (addr, &saddr, &addr_family);
if (res < 0) {
LOG_W ("%p socks5 client udp addr", self);
return -1;
}
fd = hev_socks5_socket (SOCK_DGRAM);
if (fd < 0) {
LOG_E ("%p socks5 client udp socket", self);
return -1;
}
sadp = (struct sockaddr *)&saddr;
klass = HEV_OBJECT_GET_CLASS (self);
res = klass->binder (HEV_SOCKS5 (self), fd, sadp);
if (res < 0) {
LOG_W ("%p socks5 client udp bind", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
res = hev_task_io_socket_connect (fd, sadp, sizeof (saddr), task_io_yielder,
self);
if (res < 0) {
LOG_I ("%p socks5 client udp connect", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
HEV_SOCKS5 (self)->udp_associated = 1;
self->fd = fd;
return 0;
}
static int
hev_socks5_client_udp_get_fd (HevSocks5UDP *self)
{
int fd;
switch (HEV_SOCKS5 (self)->type) {
case HEV_SOCKS5_TYPE_UDP_IN_TCP:
fd = HEV_SOCKS5 (self)->fd;
break;
case HEV_SOCKS5_TYPE_UDP_IN_UDP:
fd = HEV_SOCKS5_CLIENT_UDP (self)->fd;
break;
default:
return -1;
}
return fd;
}
int
hev_socks5_client_udp_construct (HevSocks5ClientUDP *self, HevSocks5Type type)
{
int res;
res = hev_socks5_client_construct (&self->base, type);
if (res < 0)
return res;
LOG_I ("%p socks5 client udp construct", self);
HEV_OBJECT (self)->klass = HEV_SOCKS5_CLIENT_UDP_TYPE;
self->fd = -1;
return 0;
}
static void
hev_socks5_client_udp_destruct (HevObject *base)
{
HevSocks5ClientUDP *self = HEV_SOCKS5_CLIENT_UDP (base);
LOG_D ("%p socks5 client udp destruct", self);
if (self->fd >= 0) {
hev_task_del_fd (hev_task_self (), self->fd);
close (self->fd);
}
HEV_SOCKS5_CLIENT_TYPE->destruct (base);
}
static void *
hev_socks5_client_udp_iface (HevObject *base, void *type)
{
HevSocks5ClientUDPClass *klass = HEV_OBJECT_GET_CLASS (base);
return &klass->udp;
}
HevObjectClass *
hev_socks5_client_udp_class (void)
{
static HevSocks5ClientUDPClass klass;
HevSocks5ClientUDPClass *kptr = &klass;
HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr);
if (!okptr->name) {
HevSocks5ClientClass *ckptr;
HevSocks5UDPIface *uiptr;
memcpy (kptr, HEV_SOCKS5_CLIENT_TYPE, sizeof (HevSocks5ClientClass));
okptr->name = "HevSocks5ClientUDP";
okptr->destruct = hev_socks5_client_udp_destruct;
okptr->iface = hev_socks5_client_udp_iface;
ckptr = HEV_SOCKS5_CLIENT_CLASS (kptr);
ckptr->get_upstream_addr = hev_socks5_client_udp_get_upstream_addr;
ckptr->set_upstream_addr = hev_socks5_client_udp_set_upstream_addr;
uiptr = &kptr->udp;
memcpy (uiptr, HEV_SOCKS5_UDP_TYPE, sizeof (HevSocks5UDPIface));
uiptr->get_fd = hev_socks5_client_udp_get_fd;
}
return okptr;
}

View File

@ -0,0 +1,53 @@
/*
============================================================================
Name : hev-socks5-client-udp.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2023 hev
Description : Socks5 Client UDP
============================================================================
*/
#ifndef __HEV_SOCKS5_CLIENT_UDP_H__
#define __HEV_SOCKS5_CLIENT_UDP_H__
#include "hev-socks5-udp.h"
#include "hev-socks5-client.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_CLIENT_UDP(p) ((HevSocks5ClientUDP *)p)
#define HEV_SOCKS5_CLIENT_UDP_CLASS(p) ((HevSocks5ClientUDPClass *)p)
#define HEV_SOCKS5_CLIENT_UDP_TYPE (hev_socks5_client_udp_class ())
typedef struct _HevSocks5ClientUDP HevSocks5ClientUDP;
typedef struct _HevSocks5ClientUDPClass HevSocks5ClientUDPClass;
struct _HevSocks5ClientUDP
{
HevSocks5Client base;
int fd;
};
struct _HevSocks5ClientUDPClass
{
HevSocks5ClientClass base;
HevSocks5UDPIface udp;
};
HevObjectClass *hev_socks5_client_udp_class (void);
int hev_socks5_client_udp_construct (HevSocks5ClientUDP *self,
HevSocks5Type type);
HevSocks5ClientUDP *hev_socks5_client_udp_new (HevSocks5Type type);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_CLIENT_UDP_H__ */

View File

@ -0,0 +1,475 @@
/*
============================================================================
Name : hev-socks5-client.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 Client
============================================================================
*/
#include <string.h>
#include <unistd.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-task-io-socket.h>
#include <hev-memory-allocator.h>
#include "hev-socks5-misc-priv.h"
#include "hev-socks5-logger-priv.h"
#include "hev-socks5-client.h"
#define task_io_yielder hev_socks5_task_io_yielder
static int
hev_socks5_client_write_auth_methods (HevSocks5Client *self)
{
HevSocks5Auth auth;
int res;
LOG_D ("%p socks5 client write auth methods", self);
auth.ver = HEV_SOCKS5_VERSION_5;
auth.method_len = 1;
if (!self->auth.user || !self->auth.pass)
auth.methods[0] = HEV_SOCKS5_AUTH_METHOD_NONE;
else
auth.methods[0] = HEV_SOCKS5_AUTH_METHOD_USER;
res = hev_task_io_socket_send (HEV_SOCKS5 (self)->fd, &auth, 3, MSG_WAITALL,
task_io_yielder, self);
if (res <= 0) {
LOG_I ("%p socks5 client write auth methods", self);
return -1;
}
return 0;
}
static int
hev_socks5_client_write_auth_creds (HevSocks5Client *self)
{
struct msghdr mh = { 0 };
struct iovec iov[4];
unsigned char ub[3];
int res;
LOG_D ("%p socks5 client write auth creds", self);
if (!self->auth.user || !self->auth.pass)
return 0;
ub[0] = HEV_SOCKS5_AUTH_VERSION_1;
ub[1] = strlen (self->auth.user);
ub[2] = strlen (self->auth.pass);
iov[0].iov_base = &ub[0];
iov[0].iov_len = 2;
iov[1].iov_base = (void *)self->auth.user;
iov[1].iov_len = ub[1];
iov[2].iov_base = &ub[2];
iov[2].iov_len = 1;
iov[3].iov_base = (void *)self->auth.pass;
iov[3].iov_len = ub[2];
mh.msg_iov = iov;
mh.msg_iovlen = 4;
res = hev_task_io_socket_sendmsg (HEV_SOCKS5 (self)->fd, &mh, MSG_WAITALL,
task_io_yielder, self);
if (res <= 0) {
LOG_I ("%p socks5 client write auth creds", self);
return -1;
}
return 0;
}
static int
hev_socks5_client_write_request (HevSocks5Client *self)
{
HevSocks5ClientClass *klass;
struct msghdr mh = { 0 };
struct iovec iov[2];
HevSocks5Addr *addr;
HevSocks5ReqRes req;
int addrlen;
int ret;
LOG_D ("%p socks5 client write request", self);
req.ver = HEV_SOCKS5_VERSION_5;
req.rsv = 0;
switch (HEV_SOCKS5 (self)->type) {
case HEV_SOCKS5_TYPE_TCP:
req.cmd = HEV_SOCKS5_REQ_CMD_CONNECT;
break;
case HEV_SOCKS5_TYPE_UDP_IN_TCP:
req.cmd = HEV_SOCKS5_REQ_CMD_FWD_UDP;
break;
case HEV_SOCKS5_TYPE_UDP_IN_UDP:
req.cmd = HEV_SOCKS5_REQ_CMD_UDP_ASC;
break;
default:
return -1;
}
iov[0].iov_base = &req;
iov[0].iov_len = 3;
klass = HEV_OBJECT_GET_CLASS (self);
addr = klass->get_upstream_addr (self);
switch (addr->atype) {
case HEV_SOCKS5_ADDR_TYPE_IPV4:
addrlen = 7;
break;
case HEV_SOCKS5_ADDR_TYPE_IPV6:
addrlen = 19;
break;
case HEV_SOCKS5_ADDR_TYPE_NAME:
addrlen = 4 + addr->domain.len;
break;
default:
LOG_I ("%p socks5 client req.atype %u", self, addr->atype);
return -1;
}
iov[1].iov_base = addr;
iov[1].iov_len = addrlen;
mh.msg_iov = iov;
mh.msg_iovlen = 2;
ret = hev_task_io_socket_sendmsg (HEV_SOCKS5 (self)->fd, &mh, MSG_WAITALL,
task_io_yielder, self);
if (ret <= 0) {
LOG_I ("%p socks5 client write request", self);
return -1;
}
hev_free (addr);
return 0;
}
static int
hev_socks5_client_read_auth_method (HevSocks5Client *self)
{
HevSocks5Auth auth;
int res;
LOG_D ("%p socks5 client read auth method", self);
res = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, &auth, 2, MSG_WAITALL,
task_io_yielder, self);
if (res != 2) {
LOG_I ("%p socks5 client read auth", self);
return -1;
}
if (auth.ver != HEV_SOCKS5_VERSION_5) {
LOG_I ("%p socks5 client auth.ver %u", self, auth.ver);
return -1;
}
return auth.method;
}
static int
hev_socks5_client_read_auth_creds (HevSocks5Client *self)
{
HevSocks5ReqRes res;
int ret;
LOG_D ("%p socks5 client read auth creds", self);
ret = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, &res, 2, MSG_WAITALL,
task_io_yielder, self);
if (ret != 2) {
LOG_I ("%p socks5 client read auth creds", self);
return -1;
}
if (res.ver != HEV_SOCKS5_AUTH_VERSION_1) {
LOG_I ("%p socks5 client auth.res.ver %u", self, res.ver);
return -1;
}
if (res.rep != HEV_SOCKS5_RES_REP_SUCC) {
LOG_I ("%p socks5 client auth.res.rep %u", self, res.rep);
return -1;
}
LOG_D ("%p socks5 client auth done", self);
return 0;
}
static int
hev_socks5_client_read_response (HevSocks5Client *self)
{
HevSocks5ClientClass *klass;
HevSocks5ReqRes res;
int addrlen;
int ret;
LOG_D ("%p socks5 client read response", self);
ret = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, &res, 4, MSG_WAITALL,
task_io_yielder, self);
if (ret != 4) {
LOG_I ("%p socks5 client read response", self);
return -1;
}
if (res.ver != HEV_SOCKS5_VERSION_5) {
LOG_I ("%p socks5 client res.ver %u", self, res.ver);
return -1;
}
if (res.rep != HEV_SOCKS5_RES_REP_SUCC) {
LOG_I ("%p socks5 client res.rep %u", self, res.rep);
return -1;
}
switch (res.addr.atype) {
case HEV_SOCKS5_ADDR_TYPE_IPV4:
addrlen = 6;
break;
case HEV_SOCKS5_ADDR_TYPE_IPV6:
addrlen = 18;
break;
default:
LOG_I ("%p socks5 client res.atype %u", self, res.addr.atype);
return -1;
}
ret = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, &res.addr.ipv4,
addrlen, MSG_WAITALL, task_io_yielder, self);
if (ret != addrlen) {
LOG_I ("%p socks5 client read addr", self);
return -1;
}
klass = HEV_OBJECT_GET_CLASS (self);
ret = klass->set_upstream_addr (self, &res.addr);
if (ret < 0) {
LOG_W ("%p socks5 client set upstream addr", self);
return -1;
}
return 0;
}
int
hev_socks5_client_connect (HevSocks5Client *self, const char *addr, int port)
{
HevSocks5Class *klass;
struct sockaddr_in6 saddr;
struct sockaddr *sap;
int addr_family;
int timeout;
int fd, res;
LOG_D ("%p socks5 client connect [%s]:%d", self, addr, port);
timeout = hev_socks5_get_connect_timeout ();
hev_socks5_set_timeout (HEV_SOCKS5 (self), timeout);
memset (&saddr, 0, sizeof (saddr));
addr_family = hev_socks5_get_addr_family (HEV_SOCKS5 (self));
res = hev_socks5_name_into_sockaddr6 (addr, port, &saddr, &addr_family);
if (res < 0) {
LOG_I ("%p socks5 client resolve [%s]:%d", self, addr, port);
return -1;
}
fd = hev_socks5_socket (SOCK_STREAM);
if (fd < 0) {
LOG_E ("%p socks5 client socket", self);
return -1;
}
sap = (struct sockaddr *)&saddr;
klass = HEV_OBJECT_GET_CLASS (self);
res = klass->binder (HEV_SOCKS5 (self), fd, sap);
if (res < 0) {
LOG_W ("%p socks5 client bind", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
res = hev_task_io_socket_connect (fd, sap, sizeof (saddr), task_io_yielder,
self);
if (res < 0) {
LOG_I ("%p socks5 client connect", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
HEV_SOCKS5 (self)->fd = fd;
hev_socks5_set_addr_family (HEV_SOCKS5 (self), addr_family);
LOG_D ("%p socks5 client connect server fd %d", self, fd);
return 0;
}
static int
hev_socks5_client_handshake_standard (HevSocks5Client *self)
{
int res;
LOG_D ("%p socks5 client handshake standard", self);
res = hev_socks5_client_write_auth_methods (self);
if (res < 0)
return -1;
res = hev_socks5_client_read_auth_method (self);
if (res < 0)
return -1;
if (res == HEV_SOCKS5_AUTH_METHOD_USER) {
res = hev_socks5_client_write_auth_creds (self);
if (res < 0)
return -1;
res = hev_socks5_client_read_auth_creds (self);
if (res < 0)
return -1;
} else if (res != HEV_SOCKS5_AUTH_METHOD_NONE) {
LOG_I ("%p socks5 client auth method %d", self, res);
return -1;
}
res = hev_socks5_client_write_request (self);
if (res < 0)
return -1;
res = hev_socks5_client_read_response (self);
if (res < 0)
return -1;
return 0;
}
static int
hev_socks5_client_handshake_pipeline (HevSocks5Client *self)
{
int res;
LOG_D ("%p socks5 client handshake pipeline", self);
res = hev_socks5_client_write_auth_methods (self);
if (res < 0)
return -1;
res = hev_socks5_client_write_auth_creds (self);
if (res < 0)
return -1;
res = hev_socks5_client_write_request (self);
if (res < 0)
return -1;
res = hev_socks5_client_read_auth_method (self);
if (res < 0)
return -1;
if (res == HEV_SOCKS5_AUTH_METHOD_USER) {
res = hev_socks5_client_read_auth_creds (self);
if (res < 0)
return -1;
} else if (res != HEV_SOCKS5_AUTH_METHOD_NONE) {
LOG_I ("%p socks5 client auth method %d", self, res);
return -1;
}
res = hev_socks5_client_read_response (self);
if (res < 0)
return -1;
return 0;
}
int
hev_socks5_client_handshake (HevSocks5Client *self, int pipeline)
{
int timeout;
int res;
timeout = hev_socks5_get_tcp_timeout ();
hev_socks5_set_timeout (HEV_SOCKS5 (self), timeout);
if (pipeline)
res = hev_socks5_client_handshake_pipeline (self);
else
res = hev_socks5_client_handshake_standard (self);
switch (HEV_SOCKS5 (self)->type) {
case HEV_SOCKS5_TYPE_UDP_IN_TCP:
case HEV_SOCKS5_TYPE_UDP_IN_UDP:
timeout = hev_socks5_get_udp_timeout ();
hev_socks5_set_timeout (HEV_SOCKS5 (self), timeout);
break;
default:
break;
}
return res;
}
void
hev_socks5_client_set_auth (HevSocks5Client *self, const char *user,
const char *pass)
{
LOG_D ("%p socks5 client set auth", self);
self->auth.user = user;
self->auth.pass = pass;
}
int
hev_socks5_client_construct (HevSocks5Client *self, HevSocks5Type type)
{
int res;
res = hev_socks5_construct (&self->base, type);
if (res < 0)
return res;
LOG_D ("%p socks5 client construct", self);
HEV_OBJECT (self)->klass = HEV_SOCKS5_CLIENT_TYPE;
return 0;
}
static void
hev_socks5_client_destruct (HevObject *base)
{
HevSocks5Client *self = HEV_SOCKS5_CLIENT (base);
LOG_D ("%p socks5 client destruct", self);
HEV_SOCKS5_TYPE->destruct (base);
}
HevObjectClass *
hev_socks5_client_class (void)
{
static HevSocks5ClientClass klass;
HevSocks5ClientClass *kptr = &klass;
HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr);
if (!okptr->name) {
memcpy (kptr, HEV_SOCKS5_TYPE, sizeof (HevSocks5Class));
okptr->name = "HevSocks5Client";
okptr->destruct = hev_socks5_client_destruct;
}
return okptr;
}

View File

@ -0,0 +1,62 @@
/*
============================================================================
Name : hev-socks5-client.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2024 hev
Description : Socks5 Client
============================================================================
*/
#ifndef __HEV_SOCKS5_CLIENT_H__
#define __HEV_SOCKS5_CLIENT_H__
#include "hev-socks5.h"
#include "hev-socks5-proto.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_CLIENT(p) ((HevSocks5Client *)p)
#define HEV_SOCKS5_CLIENT_CLASS(p) ((HevSocks5ClientClass *)p)
#define HEV_SOCKS5_CLIENT_TYPE (hev_socks5_client_class ())
typedef struct _HevSocks5Client HevSocks5Client;
typedef struct _HevSocks5ClientClass HevSocks5ClientClass;
struct _HevSocks5Client
{
HevSocks5 base;
struct
{
const char *user;
const char *pass;
} auth;
};
struct _HevSocks5ClientClass
{
HevSocks5Class base;
HevSocks5Addr *(*get_upstream_addr) (HevSocks5Client *self);
int (*set_upstream_addr) (HevSocks5Client *self, HevSocks5Addr *addr);
};
HevObjectClass *hev_socks5_client_class (void);
int hev_socks5_client_construct (HevSocks5Client *self, HevSocks5Type type);
int hev_socks5_client_connect (HevSocks5Client *self, const char *addr,
int port);
int hev_socks5_client_handshake (HevSocks5Client *self, int pipeline);
void hev_socks5_client_set_auth (HevSocks5Client *self, const char *user,
const char *pass);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_CLIENT_H__ */

View File

@ -0,0 +1,37 @@
/*
============================================================================
Name : hev-socks5-logger-priv.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 hev
Description : Socks5 Logger Private
============================================================================
*/
#ifndef __HEV_SOCKS5_LOGGER_PRIV_H__
#define __HEV_SOCKS5_LOGGER_PRIV_H__
#include "hev-socks5-logger.h"
#ifdef __cplusplus
extern "C" {
#endif
#define LOG_D(fmt...) hev_socks5_logger_log (HEV_SOCKS5_LOGGER_DEBUG, fmt)
#define LOG_I(fmt...) hev_socks5_logger_log (HEV_SOCKS5_LOGGER_INFO, fmt)
#define LOG_W(fmt...) hev_socks5_logger_log (HEV_SOCKS5_LOGGER_WARN, fmt)
#define LOG_E(fmt...) hev_socks5_logger_log (HEV_SOCKS5_LOGGER_ERROR, fmt)
#define LOG_ON() hev_socks5_logger_enabled (HEV_SOCKS5_LOGGER_UNSET)
#define LOG_ON_D() hev_socks5_logger_enabled (HEV_SOCKS5_LOGGER_DEBUG)
#define LOG_ON_I() hev_socks5_logger_enabled (HEV_SOCKS5_LOGGER_INFO)
#define LOG_ON_W() hev_socks5_logger_enabled (HEV_SOCKS5_LOGGER_WARN)
#define LOG_ON_E() hev_socks5_logger_enabled (HEV_SOCKS5_LOGGER_ERROR)
int hev_socks5_logger_enabled (HevSocks5LoggerLevel level);
void hev_socks5_logger_log (HevSocks5LoggerLevel level, const char *fmt, ...);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_LOGGER_PRIV_H__ */

View File

@ -0,0 +1,113 @@
/*
============================================================================
Name : hev-socks5-logger.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 hev
Description : Socks5 Logger
============================================================================
*/
#include <time.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include "hev-socks5-logger.h"
#include "hev-socks5-logger-priv.h"
static int fd = -1;
static HevSocks5LoggerLevel req_level;
int
hev_socks5_logger_init (HevSocks5LoggerLevel level, const char *path)
{
req_level = level;
if (0 == strcmp (path, "stdout"))
fd = dup (1);
else if (0 == strcmp (path, "stderr"))
fd = dup (2);
else
fd = open (path, O_WRONLY | O_APPEND | O_CREAT, 0640);
if (fd < 0)
return -1;
return 0;
}
void
hev_socks5_logger_fini (void)
{
close (fd);
}
int
hev_socks5_logger_enabled (HevSocks5LoggerLevel level)
{
if (level >= req_level && fd >= 0)
return 1;
return 0;
}
void
hev_socks5_logger_log (HevSocks5LoggerLevel level, const char *fmt, ...)
{
struct iovec iov[4];
const char *ts_fmt;
char msg[1024];
struct tm *ti;
char ts[32];
time_t now;
va_list ap;
int len;
if (level < req_level || fd < 0)
return;
time (&now);
ti = localtime (&now);
ts_fmt = "[%04u-%02u-%02u %02u:%02u:%02u] ";
len = snprintf (ts, sizeof (ts), ts_fmt, 1900 + ti->tm_year, 1 + ti->tm_mon,
ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec);
iov[0].iov_base = ts;
iov[0].iov_len = len;
switch (level) {
case HEV_SOCKS5_LOGGER_DEBUG:
iov[1].iov_base = "[D] ";
break;
case HEV_SOCKS5_LOGGER_INFO:
iov[1].iov_base = "[I] ";
break;
case HEV_SOCKS5_LOGGER_WARN:
iov[1].iov_base = "[W] ";
break;
case HEV_SOCKS5_LOGGER_ERROR:
iov[1].iov_base = "[E] ";
break;
case HEV_SOCKS5_LOGGER_UNSET:
iov[1].iov_base = "[?] ";
break;
}
iov[1].iov_len = 4;
va_start (ap, fmt);
iov[2].iov_base = msg;
iov[2].iov_len = vsnprintf (msg, 1024, fmt, ap);
va_end (ap);
iov[3].iov_base = "\n";
iov[3].iov_len = 1;
if (writev (fd, iov, 4)) {
/* ignore return value */
}
}

View File

@ -0,0 +1,35 @@
/*
============================================================================
Name : hev-socks5-logger.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 hev
Description : Socks5 Logger
============================================================================
*/
#ifndef __HEV_SOCKS5_LOGGER_H__
#define __HEV_SOCKS5_LOGGER_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef enum _HevSocks5LoggerLevel HevSocks5LoggerLevel;
enum _HevSocks5LoggerLevel
{
HEV_SOCKS5_LOGGER_DEBUG,
HEV_SOCKS5_LOGGER_INFO,
HEV_SOCKS5_LOGGER_WARN,
HEV_SOCKS5_LOGGER_ERROR,
HEV_SOCKS5_LOGGER_UNSET,
};
int hev_socks5_logger_init (HevSocks5LoggerLevel level, const char *path);
void hev_socks5_logger_fini (void);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_LOGGER_H__ */

View File

@ -0,0 +1,41 @@
/*
============================================================================
Name : hev-socks5-misc-priv.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 Misc Private
============================================================================
*/
#ifndef __HEV_SOCKS5_MISC_PRIV_H__
#define __HEV_SOCKS5_MISC_PRIV_H__
#include <netinet/in.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include "hev-socks5-misc.h"
#include "hev-socks5-proto.h"
#ifdef __cplusplus
extern "C" {
#endif
int hev_socks5_socket (int type);
const char *hev_socks5_addr_into_str (const HevSocks5Addr *addr, char *buf,
int len);
int hev_socks5_get_connect_timeout (void);
int hev_socks5_get_tcp_timeout (void);
int hev_socks5_get_udp_timeout (void);
int hev_socks5_get_task_stack_size (void);
int hev_socks5_get_udp_copy_buffer_nums (void);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_MISC_PRIV_H__ */

View File

@ -0,0 +1,385 @@
/*
============================================================================
Name : hev-socks5-misc.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 Misc
============================================================================
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-task-io-socket.h>
#include <hev-task-dns.h>
#include <hev-memory-allocator.h>
#include "hev-socks5.h"
#include "hev-socks5-logger-priv.h"
#include "hev-socks5-misc.h"
#include "hev-socks5-misc-priv.h"
static int connect_timeout = 10000;
static int tcp_timeout = 300000;
static int udp_timeout = 60000;
static int task_stack_size = 8192;
static int udp_recv_buffer_size = 512 * 1024;
static int udp_copy_buffer_nums = 10;
int
hev_socks5_task_io_yielder (HevTaskYieldType type, void *data)
{
HevSocks5 *self = data;
if (type == HEV_TASK_YIELD) {
hev_task_yield (HEV_TASK_YIELD);
return 0;
}
if (self->timeout < 0) {
hev_task_yield (HEV_TASK_WAITIO);
} else {
int timeout = self->timeout;
timeout = hev_task_sleep (timeout);
if (timeout <= 0) {
LOG_I ("%p io timeout", self);
return -1;
}
}
return 0;
}
int
hev_socks5_socket (int type)
{
HevTask *task = hev_task_self ();
int fd, res, zero = 0;
fd = hev_task_io_socket_socket (AF_INET6, type, 0);
if (fd < 0)
return -1;
res = setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof (zero));
if (res < 0) {
close (fd);
return -1;
}
res = hev_task_add_fd (task, fd, POLLIN | POLLOUT);
if (res < 0)
hev_task_mod_fd (task, fd, POLLIN | POLLOUT);
if (type == SOCK_DGRAM) {
res = udp_recv_buffer_size;
setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &res, sizeof (res));
}
return fd;
}
const char *
hev_socks5_addr_into_str (const HevSocks5Addr *addr, char *buf, int len)
{
const char *res = buf;
uint16_t port;
char sa[256];
switch (addr->atype) {
case HEV_SOCKS5_ADDR_TYPE_IPV4:
port = ntohs (addr->ipv4.port);
inet_ntop (AF_INET, addr->ipv4.addr, sa, sizeof (sa));
break;
case HEV_SOCKS5_ADDR_TYPE_IPV6:
port = ntohs (addr->ipv6.port);
inet_ntop (AF_INET6, addr->ipv6.addr, sa, sizeof (sa));
break;
case HEV_SOCKS5_ADDR_TYPE_NAME:
memcpy (sa, addr->domain.addr, addr->domain.len);
sa[addr->domain.len] = '\0';
memcpy (&port, addr->domain.addr + addr->domain.len, 2);
port = ntohs (port);
break;
default:
return NULL;
}
snprintf (buf, len, "[%s]:%u", sa, port);
return res;
}
int
hev_socks5_addr_len (const HevSocks5Addr *addr)
{
switch (addr->atype) {
case HEV_SOCKS5_ADDR_TYPE_IPV4:
return 7;
case HEV_SOCKS5_ADDR_TYPE_IPV6:
return 19;
case HEV_SOCKS5_ADDR_TYPE_NAME:
return 4 + addr->domain.len;
default:
return -1;
}
}
int
hev_socks5_addr_from_name (HevSocks5Addr *addr, const char *name, int _port)
{
uint16_t port = _port;
addr->atype = HEV_SOCKS5_ADDR_TYPE_NAME;
strncpy ((char *)addr->domain.addr, name, 256);
addr->domain.len = strlen ((char *)addr->domain.addr);
memcpy ((char *)addr->domain.addr + addr->domain.len, &port, 2);
return 4 + addr->domain.len;
}
int
hev_socks5_addr_from_ipv4 (HevSocks5Addr *addr, const void *ipv4, int port)
{
addr->atype = HEV_SOCKS5_ADDR_TYPE_IPV4;
memcpy (addr->ipv4.addr, ipv4, sizeof (addr->ipv4.addr));
addr->ipv4.port = port;
return 7;
}
int
hev_socks5_addr_from_ipv6 (HevSocks5Addr *addr, const void *ipv6, int port)
{
addr->atype = HEV_SOCKS5_ADDR_TYPE_IPV6;
memcpy (addr->ipv6.addr, ipv6, sizeof (addr->ipv6.addr));
addr->ipv6.port = port;
return 19;
}
int
hev_socks5_addr_from_sockaddr6 (HevSocks5Addr *addr, struct sockaddr_in6 *saddr)
{
if (IN6_IS_ADDR_V4MAPPED (&saddr->sin6_addr)) {
addr->atype = HEV_SOCKS5_ADDR_TYPE_IPV4;
addr->ipv4.port = saddr->sin6_port;
memcpy (addr->ipv4.addr, &saddr->sin6_addr.s6_addr[12], 4);
return 7;
}
addr->atype = HEV_SOCKS5_ADDR_TYPE_IPV6;
addr->ipv6.port = saddr->sin6_port;
memcpy (addr->ipv6.addr, &saddr->sin6_addr, 16);
return 19;
}
static void
hev_socks5_ipv4_into_sockaddr6 (const HevSocks5Addr *addr,
struct sockaddr_in6 *saddr)
{
saddr->sin6_family = AF_INET6;
saddr->sin6_port = addr->ipv4.port;
memset (&saddr->sin6_addr, 0, 10);
saddr->sin6_addr.s6_addr[10] = 0xff;
saddr->sin6_addr.s6_addr[11] = 0xff;
memcpy (&saddr->sin6_addr.s6_addr[12], addr->ipv4.addr, 4);
}
static void
hev_socks5_ipv6_into_sockaddr6 (const HevSocks5Addr *addr,
struct sockaddr_in6 *saddr)
{
saddr->sin6_family = AF_INET6;
saddr->sin6_port = addr->ipv6.port;
memcpy (&saddr->sin6_addr, addr->ipv6.addr, 16);
}
int
hev_socks5_addr_into_sockaddr6 (const HevSocks5Addr *addr,
struct sockaddr_in6 *saddr, int *family)
{
switch (addr->atype) {
case HEV_SOCKS5_ADDR_TYPE_IPV4: {
hev_socks5_ipv4_into_sockaddr6 (addr, saddr);
*family = HEV_SOCKS5_ADDR_FAMILY_IPV4;
break;
}
case HEV_SOCKS5_ADDR_TYPE_IPV6: {
hev_socks5_ipv6_into_sockaddr6 (addr, saddr);
*family = HEV_SOCKS5_ADDR_FAMILY_IPV6;
break;
}
case HEV_SOCKS5_ADDR_TYPE_NAME: {
char name[256];
uint16_t port;
memcpy (name, addr->domain.addr, addr->domain.len);
name[addr->domain.len] = '\0';
memcpy (&port, addr->domain.addr + addr->domain.len, 2);
return hev_socks5_name_into_sockaddr6 (name, ntohs (port), saddr,
family);
}
default:
return -1;
}
return 0;
}
static int
hev_socks5_name_resolve_ipv4 (const char *name, struct sockaddr_in6 *saddr)
{
int res;
memset (&saddr->sin6_addr, 0, 10);
res = inet_pton (AF_INET, name, &saddr->sin6_addr.s6_addr[12]);
if (res == 0)
return -1;
saddr->sin6_addr.s6_addr[10] = 0xff;
saddr->sin6_addr.s6_addr[11] = 0xff;
return 0;
}
static int
hev_socks5_name_resolve_ipv6 (const char *name, struct sockaddr_in6 *saddr)
{
int res;
res = inet_pton (AF_INET6, name, &saddr->sin6_addr);
if (res == 0)
return -1;
return 0;
}
static int
hev_socks5_name_resolve_name (const char *name, struct sockaddr_in6 *saddr,
int *family)
{
struct addrinfo *result = NULL;
struct addrinfo hints = { 0 };
int res = 0;
hints.ai_family = *family;
hints.ai_socktype = SOCK_STREAM;
hev_task_dns_getaddrinfo (name, NULL, &hints, &result);
if (!result)
return -1;
switch (result->ai_family) {
case AF_INET: {
struct sockaddr_in *sa = (struct sockaddr_in *)result->ai_addr;
memset (&saddr->sin6_addr, 0, 10);
saddr->sin6_addr.s6_addr[10] = 0xff;
saddr->sin6_addr.s6_addr[11] = 0xff;
memcpy (&saddr->sin6_addr.s6_addr[12], &sa->sin_addr, 4);
*family = HEV_SOCKS5_ADDR_FAMILY_IPV4;
break;
}
case AF_INET6: {
struct sockaddr_in6 *sa = (struct sockaddr_in6 *)result->ai_addr;
memcpy (&saddr->sin6_addr, &sa->sin6_addr, 16);
*family = HEV_SOCKS5_ADDR_FAMILY_IPV6;
break;
}
default:
res = -1;
}
freeaddrinfo (result);
return res;
}
int
hev_socks5_name_into_sockaddr6 (const char *name, int port,
struct sockaddr_in6 *saddr, int *family)
{
int res;
saddr->sin6_family = AF_INET6;
saddr->sin6_port = htons (port);
res = hev_socks5_name_resolve_ipv4 (name, saddr);
if (res == 0) {
*family = HEV_SOCKS5_ADDR_FAMILY_IPV4;
return 0;
}
res = hev_socks5_name_resolve_ipv6 (name, saddr);
if (res == 0) {
*family = HEV_SOCKS5_ADDR_FAMILY_IPV6;
return 0;
}
res = hev_socks5_name_resolve_name (name, saddr, family);
return res;
}
void
hev_socks5_set_connect_timeout (int timeout)
{
connect_timeout = timeout;
}
int
hev_socks5_get_connect_timeout (void)
{
return connect_timeout;
}
void
hev_socks5_set_tcp_timeout (int timeout)
{
tcp_timeout = timeout;
}
int
hev_socks5_get_tcp_timeout (void)
{
return tcp_timeout;
}
void
hev_socks5_set_udp_timeout (int timeout)
{
udp_timeout = timeout;
}
int
hev_socks5_get_udp_timeout (void)
{
return udp_timeout;
}
void
hev_socks5_set_task_stack_size (int stack_size)
{
task_stack_size = stack_size;
}
int
hev_socks5_get_task_stack_size (void)
{
return task_stack_size;
}
void
hev_socks5_set_udp_recv_buffer_size (int buffer_size)
{
udp_recv_buffer_size = buffer_size;
}
void
hev_socks5_set_udp_copy_buffer_nums (int nums)
{
udp_copy_buffer_nums = nums;
}
int
hev_socks5_get_udp_copy_buffer_nums (void)
{
return udp_copy_buffer_nums;
}

View File

@ -0,0 +1,48 @@
/*
============================================================================
Name : hev-socks5-misc.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 Misc
============================================================================
*/
#ifndef __HEV_SOCKS5_MISC_H__
#define __HEV_SOCKS5_MISC_H__
#include <netinet/in.h>
#include <hev-task.h>
#include "hev-socks5-proto.h"
#ifdef __cplusplus
extern "C" {
#endif
int hev_socks5_task_io_yielder (HevTaskYieldType type, void *data);
void hev_socks5_set_connect_timeout (int timeout);
void hev_socks5_set_tcp_timeout (int timeout);
void hev_socks5_set_udp_timeout (int timeout);
void hev_socks5_set_task_stack_size (int stack_size);
void hev_socks5_set_udp_recv_buffer_size (int buffer_size);
void hev_socks5_set_udp_copy_buffer_nums (int nums);
int hev_socks5_addr_len (const HevSocks5Addr *addr);
int hev_socks5_addr_from_name (HevSocks5Addr *addr, const char *name, int port);
int hev_socks5_addr_from_ipv4 (HevSocks5Addr *addr, const void *ipv4, int port);
int hev_socks5_addr_from_ipv6 (HevSocks5Addr *addr, const void *ipv6, int port);
int hev_socks5_addr_from_sockaddr6 (HevSocks5Addr *addr,
struct sockaddr_in6 *saddr);
int hev_socks5_addr_into_sockaddr6 (const HevSocks5Addr *addr,
struct sockaddr_in6 *saddr, int *family);
int hev_socks5_name_into_sockaddr6 (const char *host, int port,
struct sockaddr_in6 *saddr, int *family);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_MISC_H__ */

View File

@ -0,0 +1,135 @@
/*
============================================================================
Name : hev-socks5-proto.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2023 hev
Description : Socks5 Proto
============================================================================
*/
#ifndef __HEV_SOCKS5_PROTO_H__
#define __HEV_SOCKS5_PROTO_H__
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum _HevSocks5Version HevSocks5Version;
typedef enum _HevSocks5AuthMethod HevSocks5AuthMethod;
typedef enum _HevSocks5AuthVersion HevSocks5AuthVersion;
typedef enum _HevSocks5ReqCmd HevSocks5ReqCmd;
typedef enum _HevSocks5ResRep HevSocks5ResRep;
typedef enum _HevSocks5AddrType HevSocks5AddrType;
typedef struct _HevSocks5Auth HevSocks5Auth;
typedef struct _HevSocks5Addr HevSocks5Addr;
typedef struct _HevSocks5ReqRes HevSocks5ReqRes;
typedef struct _HevSocks5UDPHdr HevSocks5UDPHdr;
enum _HevSocks5Version
{
HEV_SOCKS5_VERSION_5 = 5,
};
enum _HevSocks5AuthMethod
{
HEV_SOCKS5_AUTH_METHOD_NONE = 0,
HEV_SOCKS5_AUTH_METHOD_USER = 2,
HEV_SOCKS5_AUTH_METHOD_DENY = 255,
};
enum _HevSocks5AuthVersion
{
HEV_SOCKS5_AUTH_VERSION_1 = 1,
};
enum _HevSocks5ReqCmd
{
HEV_SOCKS5_REQ_CMD_CONNECT = 1,
HEV_SOCKS5_REQ_CMD_UDP_ASC = 3,
HEV_SOCKS5_REQ_CMD_FWD_UDP = 5,
};
enum _HevSocks5ResRep
{
HEV_SOCKS5_RES_REP_SUCC = 0,
HEV_SOCKS5_RES_REP_FAIL = 1,
HEV_SOCKS5_RES_REP_HOST = 4,
HEV_SOCKS5_RES_REP_IMPL = 7,
HEV_SOCKS5_RES_REP_ADDR = 8,
};
enum _HevSocks5AddrType
{
HEV_SOCKS5_ADDR_TYPE_IPV4 = 1,
HEV_SOCKS5_ADDR_TYPE_IPV6 = 4,
HEV_SOCKS5_ADDR_TYPE_NAME = 3,
};
struct _HevSocks5Auth
{
uint8_t ver;
union
{
uint8_t method;
uint8_t method_len;
};
uint8_t methods[256];
} __attribute__ ((packed));
struct _HevSocks5Addr
{
uint8_t atype;
union
{
struct
{
uint8_t addr[4];
uint16_t port;
} ipv4 __attribute__ ((packed));
struct
{
uint8_t addr[16];
uint16_t port;
} ipv6 __attribute__ ((packed));
struct
{
uint8_t len;
uint8_t addr[256 + 2];
} domain;
};
} __attribute__ ((packed));
struct _HevSocks5ReqRes
{
uint8_t ver;
union
{
uint8_t cmd;
uint8_t rep;
};
uint8_t rsv;
HevSocks5Addr addr;
} __attribute__ ((packed));
struct _HevSocks5UDPHdr
{
union
{
uint8_t rsv[3];
struct
{
uint16_t datlen;
uint8_t hdrlen;
} __attribute__ ((packed));
};
HevSocks5Addr addr;
} __attribute__ ((packed));
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_PROTO_H__ */

View File

@ -0,0 +1,684 @@
/*
============================================================================
Name : hev-socks5-server.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 Server
============================================================================
*/
#include <string.h>
#include <unistd.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-task-io-socket.h>
#include <hev-memory-allocator.h>
#include "hev-socks5-proto.h"
#include "hev-socks5-misc-priv.h"
#include "hev-socks5-logger-priv.h"
#include "hev-socks5-server.h"
#define task_io_yielder hev_socks5_task_io_yielder
HevSocks5Server *
hev_socks5_server_new (int fd)
{
HevSocks5Server *self;
int res;
self = hev_malloc0 (sizeof (HevSocks5Server));
if (!self)
return NULL;
res = hev_socks5_server_construct (self, fd);
if (res < 0) {
hev_free (self);
return NULL;
}
LOG_D ("%p socks5 server new", self);
return self;
}
void
hev_socks5_server_set_auth (HevSocks5Server *self, HevSocks5Authenticator *auth)
{
if (self->auth)
hev_object_unref (HEV_OBJECT (self->auth));
hev_object_ref (HEV_OBJECT (auth));
self->auth = auth;
}
static int
hev_socks5_server_read_auth_method (HevSocks5Server *self)
{
HevSocks5Auth auth;
HevSocks5AuthMethod method;
int res;
int i;
LOG_D ("%p socks5 server read auth method", self);
res = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, &auth, 2, MSG_WAITALL,
task_io_yielder, self);
if (res != 2) {
LOG_I ("%p socks5 server read auth method", self);
return -1;
}
if (auth.ver != HEV_SOCKS5_VERSION_5) {
LOG_I ("%p socks5 server auth.ver %u", self, auth.ver);
return -1;
}
res = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, &auth.methods,
auth.method_len, MSG_WAITALL,
task_io_yielder, self);
if (res != auth.method_len) {
LOG_I ("%p socks5 server read auth methods", self);
return -1;
}
if (self->auth)
method = HEV_SOCKS5_AUTH_METHOD_USER;
else
method = HEV_SOCKS5_AUTH_METHOD_NONE;
res = -1;
for (i = 0; i < auth.method_len; i++) {
if (auth.methods[i] == method) {
res = method;
break;
}
}
return res;
}
static int
hev_socks5_server_write_auth_method (HevSocks5Server *self, int auth_method)
{
HevSocks5Auth auth;
int res;
LOG_D ("%p socks5 server write auth method", self);
auth.ver = HEV_SOCKS5_VERSION_5;
auth.method = auth_method;
res = hev_task_io_socket_send (HEV_SOCKS5 (self)->fd, &auth, 2, MSG_WAITALL,
task_io_yielder, self);
if (res <= 0) {
LOG_I ("%p socks5 server write auth method", self);
return -1;
}
return 0;
}
static int
hev_socks5_server_read_auth_user (HevSocks5Server *self)
{
HevSocks5User *user;
uint8_t nlen, plen;
uint8_t name[257];
uint8_t pass[257];
uint8_t head[2];
int res;
LOG_D ("%p socks5 server read auth user", self);
res = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, head, 2, MSG_WAITALL,
task_io_yielder, self);
if (res != 2) {
LOG_I ("%p socks5 server read auth user.ver", self);
return -1;
}
if (head[0] != 1) {
LOG_I ("%p socks5 server auth user.ver %u", self, head[0]);
return -1;
}
nlen = head[1];
if (nlen == 0) {
LOG_I ("%p socks5 server auth user.nlen %u", self, nlen);
return -1;
}
res = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, name, nlen + 1,
MSG_WAITALL, task_io_yielder, self);
if (res != (nlen + 1)) {
LOG_I ("%p socks5 server read auth user.name", self);
return -1;
}
plen = name[nlen];
if (plen == 0) {
LOG_I ("%p socks5 server auth user.plen %u", self, plen);
return -1;
}
res = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, pass, plen,
MSG_WAITALL, task_io_yielder, self);
if (res != plen) {
LOG_I ("%p socks5 server read auth user.pass", self);
return -1;
}
user = hev_socks5_authenticator_get (self->auth, (char *)name, nlen);
if (!user) {
LOG_I ("%p socks5 server auth user: %s pass: %s", self, name, pass);
return -1;
}
res = hev_socks5_user_check (user, (char *)pass, plen);
if (res < 0) {
LOG_I ("%p socks5 server auth user: %s pass: %s", self, name, pass);
return -1;
}
hev_object_ref (HEV_OBJECT (user));
hev_object_unref (HEV_OBJECT (self->auth));
self->user = user;
return 0;
}
static int
hev_socks5_server_write_auth_user (HevSocks5Server *self, int auth_res)
{
uint8_t buf[2];
int res;
LOG_D ("%p socks5 server write auth user", self);
buf[0] = 1;
buf[1] = auth_res;
res = hev_task_io_socket_send (HEV_SOCKS5 (self)->fd, buf, 2, MSG_WAITALL,
task_io_yielder, self);
if (res <= 0) {
LOG_I ("%p socks5 server write auth user", self);
return -1;
}
return 0;
}
static int
hev_socks5_server_auth (HevSocks5Server *self)
{
int method;
int res;
method = hev_socks5_server_read_auth_method (self);
res = hev_socks5_server_write_auth_method (self, method);
if (res < 0)
return -1;
switch (method) {
case HEV_SOCKS5_AUTH_METHOD_NONE:
break;
case HEV_SOCKS5_AUTH_METHOD_USER:
res = hev_socks5_server_read_auth_user (self);
res |= hev_socks5_server_write_auth_user (self, res);
if (res < 0)
return -1;
break;
default:
return -1;
}
return 0;
}
static int
hev_socks5_server_read_request (HevSocks5Server *self, int *cmd, int *rep,
struct sockaddr_in6 *addr)
{
HevSocks5ReqRes req;
int addr_family;
int addrlen;
int res;
LOG_D ("%p socks5 server read request", self);
res = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, &req, 5, MSG_WAITALL,
task_io_yielder, self);
if (res != 5) {
LOG_I ("%p socks5 server read request", self);
return -1;
}
if (req.ver != HEV_SOCKS5_VERSION_5) {
*rep = HEV_SOCKS5_RES_REP_FAIL;
LOG_I ("%p socks5 server req.ver %u", self, req.ver);
return 0;
}
switch (req.addr.atype) {
case HEV_SOCKS5_ADDR_TYPE_IPV4:
addrlen = 5;
break;
case HEV_SOCKS5_ADDR_TYPE_IPV6:
addrlen = 17;
break;
case HEV_SOCKS5_ADDR_TYPE_NAME:
addrlen = 2 + req.addr.domain.len;
break;
default:
*rep = HEV_SOCKS5_RES_REP_ADDR;
LOG_I ("%p socks5 server req.atype %u", self, req.addr.atype);
return 0;
}
res = hev_task_io_socket_recv (HEV_SOCKS5 (self)->fd, req.addr.domain.addr,
addrlen, MSG_WAITALL, task_io_yielder, self);
if (res != addrlen) {
*rep = HEV_SOCKS5_RES_REP_ADDR;
LOG_I ("%p socks5 server read addr", self);
return 0;
}
addr_family = hev_socks5_get_addr_family (HEV_SOCKS5 (self));
res = hev_socks5_addr_into_sockaddr6 (&req.addr, addr, &addr_family);
if (res < 0) {
*rep = HEV_SOCKS5_RES_REP_ADDR;
LOG_I ("%p socks5 server resolve addr", self);
return 0;
}
hev_socks5_set_addr_family (HEV_SOCKS5 (self), addr_family);
if (LOG_ON ()) {
const char *type;
const char *str;
char buf[272];
switch (req.cmd) {
case HEV_SOCKS5_REQ_CMD_CONNECT:
type = "tcp";
break;
case HEV_SOCKS5_REQ_CMD_UDP_ASC:
case HEV_SOCKS5_REQ_CMD_FWD_UDP:
type = "udp";
break;
default:
type = "unknown";
break;
}
str = hev_socks5_addr_into_str (&req.addr, buf, sizeof (buf));
LOG_I ("%p socks5 server %s %s", self, type, str);
}
*cmd = req.cmd;
return 0;
}
static int
hev_socks5_server_write_response (HevSocks5Server *self, int rep,
struct sockaddr_in6 *addr)
{
HevSocks5ReqRes res;
int ret;
LOG_D ("%p socks5 server write response", self);
res.ver = HEV_SOCKS5_VERSION_5;
res.rep = rep;
res.rsv = 0;
ret = hev_socks5_addr_from_sockaddr6 (&res.addr, addr);
ret = hev_task_io_socket_send (HEV_SOCKS5 (self)->fd, &res, 3 + ret,
MSG_WAITALL, task_io_yielder, self);
if (ret <= 0) {
LOG_I ("%p socks5 server write response", self);
return -1;
}
return 0;
}
static int
hev_socks5_server_connect (HevSocks5Server *self, struct sockaddr_in6 *addr)
{
HevSocks5Class *klass;
int timeout;
int res;
int fd;
LOG_D ("%p socks5 server connect", self);
fd = hev_socks5_socket (SOCK_STREAM);
if (fd < 0) {
LOG_E ("%p socks5 server socket stream", self);
return -1;
}
klass = HEV_OBJECT_GET_CLASS (self);
res = klass->binder (HEV_SOCKS5 (self), fd, (struct sockaddr *)addr);
if (res < 0) {
LOG_W ("%p socks5 server bind", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
timeout = hev_socks5_get_connect_timeout ();
hev_socks5_set_timeout (HEV_SOCKS5 (self), timeout);
res = hev_task_io_socket_connect (fd, (struct sockaddr *)addr,
sizeof (*addr), task_io_yielder, self);
if (res < 0) {
LOG_I ("%p socks5 server connect", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
timeout = hev_socks5_get_tcp_timeout ();
hev_socks5_set_timeout (HEV_SOCKS5 (self), timeout);
self->fds[0] = fd;
return 0;
}
static int
hev_socks5_server_bind (HevSocks5Server *self, struct sockaddr_in6 *addr)
{
HevSocks5ServerClass *sskptr = HEV_OBJECT_GET_CLASS (self);
int one = 1;
int res;
int fd;
LOG_D ("%p socks5 server bind", self);
fd = hev_socks5_socket (SOCK_DGRAM);
if (fd < 0) {
LOG_E ("%p socks5 server socket dgram", self);
return -1;
}
self->fds[0] = fd;
if (!addr)
return 0;
fd = hev_socks5_socket (SOCK_DGRAM);
if (fd < 0) {
LOG_E ("%p socks5 server socket dgram", self);
return -1;
}
res = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one));
if (res < 0) {
LOG_W ("%p socks5 server socket reuse", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
res = sskptr->binder (self, fd, addr);
if (res < 0) {
LOG_W ("%p socks5 server bind", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
self->fds[1] = fd;
return 0;
}
static int
hev_socks5_server_udp_bind (HevSocks5Server *self, int sock,
struct sockaddr_in6 *src)
{
socklen_t alen;
int res;
LOG_D ("%p socks5 server udp bind", self);
alen = sizeof (struct sockaddr_in6);
res = getsockname (HEV_SOCKS5 (self)->fd, (struct sockaddr *)src, &alen);
if (res < 0) {
LOG_W ("%p socks5 server tcp socket name", self);
return -1;
}
src->sin6_port = 0;
res = bind (sock, (struct sockaddr *)src, alen);
if (res < 0) {
LOG_W ("%p socks5 server socket bind", self);
return -1;
}
res = getsockname (sock, (struct sockaddr *)src, &alen);
if (res < 0) {
LOG_W ("%p socks5 server udp socket name", self);
return -1;
}
return 0;
}
static int
hev_socks5_server_handshake (HevSocks5Server *self)
{
struct sockaddr_in6 addr;
int timeout;
int cmd;
int rep;
int res;
LOG_D ("%p socks5 server handshake", self);
timeout = hev_socks5_get_tcp_timeout ();
hev_socks5_set_timeout (HEV_SOCKS5 (self), timeout);
res = hev_socks5_server_auth (self);
if (res < 0)
return -1;
rep = HEV_SOCKS5_RES_REP_SUCC;
res = hev_socks5_server_read_request (self, &cmd, &rep, &addr);
if (res < 0)
return -1;
if (rep == HEV_SOCKS5_RES_REP_SUCC) {
switch (cmd) {
case HEV_SOCKS5_REQ_CMD_CONNECT:
res = hev_socks5_server_connect (self, &addr);
if (res < 0)
rep = HEV_SOCKS5_RES_REP_HOST;
HEV_SOCKS5 (self)->type = HEV_SOCKS5_TYPE_TCP;
break;
case HEV_SOCKS5_REQ_CMD_UDP_ASC:
res = hev_socks5_server_bind (self, &addr);
if (res < 0)
rep = HEV_SOCKS5_RES_REP_FAIL;
HEV_SOCKS5 (self)->type = HEV_SOCKS5_TYPE_UDP_IN_UDP;
break;
case HEV_SOCKS5_REQ_CMD_FWD_UDP:
res = hev_socks5_server_bind (self, NULL);
if (res < 0)
rep = HEV_SOCKS5_RES_REP_FAIL;
HEV_SOCKS5 (self)->type = HEV_SOCKS5_TYPE_UDP_IN_TCP;
break;
default:
rep = HEV_SOCKS5_RES_REP_IMPL;
break;
}
}
res = hev_socks5_server_write_response (self, rep, &addr);
if ((res < 0) || (rep != HEV_SOCKS5_RES_REP_SUCC))
return -1;
return 0;
}
static int
hev_socks5_server_service (HevSocks5Server *self)
{
int timeout;
LOG_D ("%p socks5 server service", self);
switch (HEV_SOCKS5 (self)->type) {
case HEV_SOCKS5_TYPE_TCP:
hev_socks5_tcp_splice (HEV_SOCKS5_TCP (self), self->fds[0]);
break;
case HEV_SOCKS5_TYPE_UDP_IN_UDP:
case HEV_SOCKS5_TYPE_UDP_IN_TCP:
timeout = hev_socks5_get_udp_timeout ();
hev_socks5_set_timeout (HEV_SOCKS5 (self), timeout);
hev_socks5_udp_splice (HEV_SOCKS5_UDP (self), self->fds[0]);
break;
default:
return -1;
}
return 0;
}
int
hev_socks5_server_run (HevSocks5Server *self)
{
HevTask *task = hev_task_self ();
int res;
int fd;
LOG_D ("%p socks5 server run", self);
fd = HEV_SOCKS5 (self)->fd;
res = hev_task_add_fd (task, fd, POLLIN | POLLOUT);
if (res < 0)
hev_task_mod_fd (task, fd, POLLIN | POLLOUT);
res = hev_socks5_server_handshake (self);
if (res < 0)
return -1;
res = hev_socks5_server_service (self);
if (res < 0)
return -1;
return 0;
}
static int
hev_socks5_server_get_fd (HevSocks5UDP *self)
{
int fd;
switch (HEV_SOCKS5 (self)->type) {
case HEV_SOCKS5_TYPE_UDP_IN_TCP:
fd = HEV_SOCKS5 (self)->fd;
break;
case HEV_SOCKS5_TYPE_UDP_IN_UDP:
fd = HEV_SOCKS5_SERVER (self)->fds[1];
break;
default:
return -1;
}
return fd;
}
int
hev_socks5_server_construct (HevSocks5Server *self, int fd)
{
int res;
res = hev_socks5_construct (&self->base, HEV_SOCKS5_TYPE_NONE);
if (res < 0)
return res;
LOG_D ("%p socks5 server construct", self);
HEV_OBJECT (self)->klass = HEV_SOCKS5_SERVER_TYPE;
HEV_SOCKS5 (self)->fd = fd;
self->fds[0] = -1;
self->fds[1] = -1;
return 0;
}
static void
hev_socks5_server_destruct (HevObject *base)
{
HevSocks5Server *self = HEV_SOCKS5_SERVER (base);
HevTask *task = hev_task_self ();
LOG_D ("%p socks5 server destruct", self);
if (self->fds[0] >= 0) {
hev_task_del_fd (task, self->fds[0]);
close (self->fds[0]);
}
if (self->fds[1] >= 0) {
hev_task_del_fd (task, self->fds[1]);
close (self->fds[1]);
}
if (self->obj)
hev_object_unref (self->obj);
HEV_SOCKS5_TYPE->destruct (base);
}
static void *
hev_socks5_server_iface (HevObject *base, void *type)
{
HevSocks5ServerClass *klass = HEV_OBJECT_GET_CLASS (base);
if (type == HEV_SOCKS5_TCP_TYPE)
return &klass->tcp;
if (type == HEV_SOCKS5_UDP_TYPE)
return &klass->udp;
return NULL;
}
HevObjectClass *
hev_socks5_server_class (void)
{
static HevSocks5ServerClass klass;
HevSocks5ServerClass *kptr = &klass;
HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr);
if (!okptr->name) {
HevSocks5TCPIface *tiptr;
HevSocks5UDPIface *uiptr;
memcpy (kptr, HEV_SOCKS5_TYPE, sizeof (HevSocks5Class));
okptr->name = "HevSocks5Server";
okptr->destruct = hev_socks5_server_destruct;
okptr->iface = hev_socks5_server_iface;
kptr->binder = hev_socks5_server_udp_bind;
tiptr = &kptr->tcp;
memcpy (tiptr, HEV_SOCKS5_TCP_TYPE, sizeof (HevSocks5TCPIface));
uiptr = &kptr->udp;
memcpy (uiptr, HEV_SOCKS5_UDP_TYPE, sizeof (HevSocks5UDPIface));
uiptr->get_fd = hev_socks5_server_get_fd;
}
return okptr;
}

View File

@ -0,0 +1,71 @@
/*
============================================================================
Name : hev-socks5-server.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2023 hev
Description : Socks5 Server
============================================================================
*/
#ifndef __HEV_SOCKS5_SERVER_H__
#define __HEV_SOCKS5_SERVER_H__
#include <hev-object.h>
#include "hev-socks5.h"
#include "hev-socks5-tcp.h"
#include "hev-socks5-udp.h"
#include "hev-socks5-user.h"
#include "hev-socks5-authenticator.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_SERVER(p) ((HevSocks5Server *)p)
#define HEV_SOCKS5_SERVER_CLASS(p) ((HevSocks5ServerClass *)p)
#define HEV_SOCKS5_SERVER_TYPE (hev_socks5_server_class ())
typedef struct _HevSocks5Server HevSocks5Server;
typedef struct _HevSocks5ServerClass HevSocks5ServerClass;
struct _HevSocks5Server
{
HevSocks5 base;
int fds[2];
union
{
HevObject *obj;
HevSocks5User *user;
HevSocks5Authenticator *auth;
};
};
struct _HevSocks5ServerClass
{
HevSocks5Class base;
int (*binder) (HevSocks5Server *self, int sock, struct sockaddr_in6 *src);
HevSocks5TCPIface tcp;
HevSocks5UDPIface udp;
};
HevObjectClass *hev_socks5_server_class (void);
int hev_socks5_server_construct (HevSocks5Server *self, int fd);
HevSocks5Server *hev_socks5_server_new (int fd);
void hev_socks5_server_set_auth (HevSocks5Server *self,
HevSocks5Authenticator *auth);
int hev_socks5_server_run (HevSocks5Server *self);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_SERVER_H__ */

View File

@ -0,0 +1,57 @@
/*
============================================================================
Name : hev-socks5-tcp.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 hev
Description : Socks5 TCP
============================================================================
*/
#include "hev-socks5.h"
#include "hev-socks5-misc-priv.h"
#include "hev-socks5-logger-priv.h"
#include "hev-socks5-tcp.h"
#define task_io_yielder hev_socks5_task_io_yielder
static int
hev_socks5_tcp_splicer (HevSocks5TCP *self, int fd)
{
HevTask *task = hev_task_self ();
int cfd;
int res;
LOG_D ("%p socks5 tcp splicer", self);
cfd = HEV_SOCKS5 (self)->fd;
if (cfd < 0)
return -1;
res = hev_task_add_fd (task, fd, POLLIN | POLLOUT);
if (res < 0)
hev_task_mod_fd (task, fd, POLLIN | POLLOUT);
hev_task_io_splice (cfd, cfd, fd, fd, 8192, task_io_yielder, self);
return 0;
}
int
hev_socks5_tcp_splice (HevSocks5TCP *self, int fd)
{
HevSocks5TCPIface *iface;
iface = HEV_OBJECT_GET_IFACE (self, HEV_SOCKS5_TCP_TYPE);
return iface->splicer (self, fd);
}
void *
hev_socks5_tcp_iface (void)
{
static HevSocks5TCPIface type = {
.splicer = hev_socks5_tcp_splicer,
};
return &type;
}

View File

@ -0,0 +1,37 @@
/*
============================================================================
Name : hev-socks5-tcp.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 hev
Description : Socks5 TCP
============================================================================
*/
#ifndef __HEV_SOCKS5_TCP_H__
#define __HEV_SOCKS5_TCP_H__
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_TCP(p) ((HevSocks5TCP *)p)
#define HEV_SOCKS5_TCP_IFACE(p) ((HevSocks5TCPIface *)p)
#define HEV_SOCKS5_TCP_TYPE (hev_socks5_tcp_iface ())
typedef void HevSocks5TCP;
typedef struct _HevSocks5TCPIface HevSocks5TCPIface;
struct _HevSocks5TCPIface
{
int (*splicer) (HevSocks5TCP *self, int fd);
};
void *hev_socks5_tcp_iface (void);
int hev_socks5_tcp_splice (HevSocks5TCP *self, int fd);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_TCP_H__ */

View File

@ -0,0 +1,508 @@
/*
============================================================================
Name : hev-socks5-udp.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 UDP
============================================================================
*/
#define _GNU_SOURCE
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-task-io-socket.h>
#include <hev-memory-allocator.h>
#include "hev-socks5.h"
#include "hev-socks5-misc-priv.h"
#include "hev-socks5-logger-priv.h"
#include "hev-socks5-udp.h"
#define UDP_BUF_SIZE 1500
static int
task_io_yielder (HevTaskYieldType type, void *data)
{
HevSocks5 *self = data;
if (self->type == HEV_SOCKS5_TYPE_UDP_IN_UDP) {
ssize_t res;
char buf;
res = recv (self->fd, &buf, sizeof (buf), 0);
if ((res == 0) || ((res < 0) && (errno != EAGAIN))) {
hev_socks5_set_timeout (self, 0);
return -1;
}
}
return hev_socks5_task_io_yielder (type, data);
}
int
hev_socks5_udp_get_fd (HevSocks5UDP *self)
{
HevSocks5UDPIface *iface;
iface = HEV_OBJECT_GET_IFACE (self, HEV_SOCKS5_UDP_TYPE);
return iface->get_fd (self);
}
static int
hev_socks5_udp_sendmmsg_tcp (HevSocks5UDP *self, HevSocks5UDPMsg *msgv,
unsigned int num)
{
struct iovec iov[num * 3];
HevSocks5UDPHdr udp[num];
struct msghdr mh;
int i, res;
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_control = NULL;
mh.msg_controllen = 0;
mh.msg_iov = iov;
mh.msg_iovlen = num * 3;
for (i = 0; i < num; i++) {
int addrlen;
addrlen = hev_socks5_addr_len (msgv[i].addr);
if (addrlen <= 0) {
LOG_D ("%p socks5 udp addr", self);
return -1;
}
udp[i].datlen = htons (msgv[i].len);
udp[i].hdrlen = 3 + addrlen;
iov[i * 3].iov_base = &udp[i];
iov[i * 3].iov_len = 3;
iov[i * 3 + 1].iov_base = msgv[i].addr;
iov[i * 3 + 1].iov_len = addrlen;
iov[i * 3 + 2].iov_base = msgv[i].buf;
iov[i * 3 + 2].iov_len = msgv[i].len;
}
res = hev_task_io_socket_sendmsg (hev_socks5_udp_get_fd (self), &mh,
MSG_WAITALL, task_io_yielder, self);
if (res <= 0) {
LOG_D ("%p socks5 udp write tcp", self);
return -1;
}
return num;
}
static int
hev_socks5_udp_sendmmsg_udp (HevSocks5UDP *self, HevSocks5UDPMsg *msgv,
unsigned int num)
{
struct iovec iov[num * 3];
struct mmsghdr mvec[num];
HevSocks5UDPHdr udp[num];
int i, res;
for (i = 0; i < num; i++) {
int addrlen;
addrlen = hev_socks5_addr_len (msgv[i].addr);
if (addrlen <= 0) {
LOG_D ("%p socks5 udp addr", self);
return -1;
}
udp[i].datlen = 0;
udp[i].hdrlen = 0;
iov[i * 3].iov_base = &udp[i];
iov[i * 3].iov_len = 3;
iov[i * 3 + 1].iov_base = msgv[i].addr;
iov[i * 3 + 1].iov_len = addrlen;
iov[i * 3 + 2].iov_base = msgv[i].buf;
iov[i * 3 + 2].iov_len = msgv[i].len;
mvec[i].msg_hdr.msg_name = NULL;
mvec[i].msg_hdr.msg_namelen = 0;
mvec[i].msg_hdr.msg_control = NULL;
mvec[i].msg_hdr.msg_controllen = 0;
mvec[i].msg_hdr.msg_iov = &iov[i * 3];
mvec[i].msg_hdr.msg_iovlen = 3;
}
res = hev_task_io_socket_sendmmsg (hev_socks5_udp_get_fd (self), mvec, num,
MSG_WAITALL, task_io_yielder, self);
if (res <= 0)
LOG_D ("%p socks5 udp write udp", self);
return res;
}
int
hev_socks5_udp_sendmmsg (HevSocks5UDP *self, HevSocks5UDPMsg *msgv,
unsigned int num)
{
switch (HEV_SOCKS5 (self)->type) {
case HEV_SOCKS5_TYPE_UDP_IN_TCP:
return hev_socks5_udp_sendmmsg_tcp (self, msgv, num);
case HEV_SOCKS5_TYPE_UDP_IN_UDP:
return hev_socks5_udp_sendmmsg_udp (self, msgv, num);
default:
return -1;
}
}
static int
hev_socks5_udp_recvmmsg_tcp (HevSocks5UDP *self, HevSocks5UDPMsg *msgv,
unsigned int num, int nonblock)
{
int i, fd, rlen = 0;
fd = hev_socks5_udp_get_fd (self);
if (nonblock)
nonblock = MSG_DONTWAIT;
for (i = 0; i < num; i++) {
HevSocks5UDPHdr udp;
struct iovec iov[2];
struct msghdr mh;
int addrlen;
int res;
res = hev_task_io_socket_recv (fd, &udp, 5, nonblock, task_io_yielder,
self);
if (res > 0 && res < 5)
res += hev_task_io_socket_recv (fd, (void *)&udp + res, 5 - res,
MSG_WAITALL, task_io_yielder, self);
if (res != 5) {
if (rlen > 0)
break;
if (res != -1 || errno != EAGAIN)
LOG_D ("%p socks5 udp read udp head", self);
return res;
}
if (udp.hdrlen < 5) {
LOG_D ("%p socks5 udp head len", self);
return -1;
}
addrlen = udp.hdrlen - 3;
udp.datlen = ntohs (udp.datlen);
if (udp.datlen > (msgv[i].len - addrlen)) {
LOG_D ("%p socks5 udp data len", self);
return -1;
}
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_control = NULL;
mh.msg_controllen = 0;
mh.msg_iov = iov;
mh.msg_iovlen = 2;
iov[0].iov_base = msgv[i].buf + 2;
iov[0].iov_len = addrlen - 2;
iov[1].iov_base = msgv[i].buf + addrlen;
iov[1].iov_len = udp.datlen;
res = hev_task_io_socket_recvmsg (fd, &mh, MSG_WAITALL, task_io_yielder,
self);
if (res != (addrlen - 2 + udp.datlen)) {
LOG_D ("%p socks5 udp read udp data", self);
return res;
}
msgv[i].addr = msgv[i].buf;
msgv[i].buf = iov[1].iov_base;
msgv[i].len = udp.datlen;
msgv[i].addr->atype = udp.addr.atype;
msgv[i].addr->domain.len = udp.addr.domain.len;
rlen++;
}
return rlen;
}
static int
hev_socks5_udp_recvmmsg_udp (HevSocks5UDP *self, HevSocks5UDPMsg *msgv,
unsigned int num, int nonblock)
{
struct sockaddr_in6 taddr;
struct mmsghdr mvec[num];
struct iovec iov[num];
int i, fd, res;
fd = hev_socks5_udp_get_fd (self);
if (nonblock)
nonblock = MSG_DONTWAIT;
for (i = 0; i < num; i++) {
mvec[i].msg_hdr.msg_name = NULL;
mvec[i].msg_hdr.msg_namelen = 0;
mvec[i].msg_hdr.msg_control = NULL;
mvec[i].msg_hdr.msg_controllen = 0;
mvec[i].msg_hdr.msg_iov = &iov[i];
mvec[i].msg_hdr.msg_iovlen = 1;
iov[i].iov_base = msgv[i].buf;
iov[i].iov_len = msgv[i].len;
}
if (!HEV_SOCKS5 (self)->udp_associated) {
mvec[0].msg_hdr.msg_name = &taddr;
mvec[0].msg_hdr.msg_namelen = sizeof (taddr);
}
res = hev_task_io_socket_recvmmsg (fd, mvec, num, nonblock, task_io_yielder,
self);
if (res <= 0) {
if (res != -1 || errno != EAGAIN)
LOG_D ("%p socks5 udp read udp", self);
return res;
}
if (!HEV_SOCKS5 (self)->udp_associated) {
struct sockaddr *saddr = mvec[0].msg_hdr.msg_name;
socklen_t alen = mvec[0].msg_hdr.msg_namelen;
if (connect (fd, saddr, alen) < 0)
return -1;
HEV_SOCKS5 (self)->udp_associated = 1;
}
for (i = 0; i < res; i++) {
HevSocks5UDPHdr *udp = msgv[i].buf;
int addrlen = hev_socks5_addr_len (&udp->addr);
int doff;
msgv[i].len = mvec[i].msg_len;
if (msgv[i].len < 4) {
msgv[i].addr = NULL;
msgv[i].len = 0;
continue;
}
if (addrlen <= 0) {
LOG_D ("%p socks5 udp addr", self);
return -1;
}
doff = 3 + addrlen;
if (doff > msgv[i].len) {
LOG_D ("%p socks5 udp data len", self);
return -1;
}
msgv[i].addr = &udp->addr;
msgv[i].buf += doff;
msgv[i].len -= doff;
}
return res;
}
int
hev_socks5_udp_recvmmsg (HevSocks5UDP *self, HevSocks5UDPMsg *msgv,
unsigned int num, int nonblock)
{
switch (HEV_SOCKS5 (self)->type) {
case HEV_SOCKS5_TYPE_UDP_IN_TCP:
return hev_socks5_udp_recvmmsg_tcp (self, msgv, num, nonblock);
case HEV_SOCKS5_TYPE_UDP_IN_UDP:
return hev_socks5_udp_recvmmsg_udp (self, msgv, num, nonblock);
default:
return -1;
}
}
static int
hev_socks5_udp_fwd_f (HevSocks5UDP *self, int fd, void *buf, unsigned int num,
int *bind)
{
HevSocks5UDPMsg svec[num];
int i, res;
for (i = 0; i < num; i++) {
svec[i].buf = buf + UDP_BUF_SIZE * i;
svec[i].len = UDP_BUF_SIZE;
}
res = hev_socks5_udp_recvmmsg (self, svec, num, 1);
if (res > 0) {
struct sockaddr_in6 addr[res];
struct mmsghdr dvec[res];
struct iovec iov[res];
int ret;
for (i = 0; i < res; i++) {
int family;
if (!svec[i].len || !svec[i].addr) {
LOG_D ("%p socks5 udp invalid", self);
return -1;
}
family = hev_socks5_get_addr_family (HEV_SOCKS5 (self));
ret = hev_socks5_addr_into_sockaddr6 (svec[i].addr, &addr[i],
&family);
if (ret < 0) {
LOG_D ("%p socks5 udp sockaddr", self);
return -1;
}
dvec[i].msg_hdr.msg_name = (struct sockaddr *)&addr[i];
dvec[i].msg_hdr.msg_namelen = sizeof (struct sockaddr_in6);
dvec[i].msg_hdr.msg_control = NULL;
dvec[i].msg_hdr.msg_controllen = 0;
dvec[i].msg_hdr.msg_iov = &iov[i];
dvec[i].msg_hdr.msg_iovlen = 1;
iov[i].iov_base = svec[i].buf;
iov[i].iov_len = svec[i].len;
}
if (!*bind) {
HevSocks5Class *skptr = HEV_OBJECT_GET_CLASS (self);
struct sockaddr *addr = dvec[0].msg_hdr.msg_name;
ret = skptr->binder (HEV_SOCKS5 (self), fd, addr);
if (ret < 0) {
LOG_W ("%p socks5 udp bind", self);
return -1;
}
*bind = 1;
}
res = hev_task_io_socket_sendmmsg (fd, dvec, res, MSG_WAITALL,
task_io_yielder, self);
}
if (res <= 0) {
if (res == -1 && errno == EAGAIN)
return 0;
LOG_D ("%p socks5 udp fwd f recv send", self);
return -1;
}
return 1;
}
static int
hev_socks5_udp_fwd_b (HevSocks5UDP *self, int fd, struct mmsghdr *svec,
unsigned int num)
{
int i, res;
res = hev_task_io_socket_recvmmsg (fd, svec, num, MSG_DONTWAIT,
task_io_yielder, self);
if (res > 0) {
HevSocks5UDPMsg dvec[res];
char saddr[res][19];
for (i = 0; i < res; i++) {
dvec[i].buf = svec[i].msg_hdr.msg_iov->iov_base;
dvec[i].len = svec[i].msg_len;
dvec[i].addr = (HevSocks5Addr *)&saddr[i];
hev_socks5_addr_from_sockaddr6 (dvec[i].addr,
svec[i].msg_hdr.msg_name);
}
res = hev_socks5_udp_sendmmsg (self, dvec, res);
}
if (res <= 0) {
if (res == -1 && errno == EAGAIN)
return 0;
LOG_D ("%p socks5 udp fwd b recv send", self);
return -1;
}
return 1;
}
static int
hev_socks5_udp_splicer (HevSocks5UDP *self, int fd_b)
{
HevTask *task = hev_task_self ();
int res_f = 1, res_b = 1;
int bind = 0;
void *buf;
int fd_a;
int num;
LOG_D ("%p socks5 udp splicer", self);
num = hev_socks5_get_udp_copy_buffer_nums ();
buf = hev_malloc (UDP_BUF_SIZE * num * 2);
if (!buf)
return -1;
fd_a = hev_socks5_udp_get_fd (self);
if (hev_task_mod_fd (task, fd_a, POLLIN | POLLOUT) < 0)
hev_task_add_fd (task, fd_a, POLLIN | POLLOUT);
if (hev_task_add_fd (task, fd_b, POLLIN | POLLOUT) < 0)
hev_task_mod_fd (task, fd_b, POLLIN | POLLOUT);
{
struct mmsghdr vec[num];
struct sockaddr_in6 addr[num];
struct iovec iov[num];
int i;
for (i = 0; i < num; i++) {
vec[i].msg_hdr.msg_name = (struct sockaddr *)&addr[i];
vec[i].msg_hdr.msg_namelen = sizeof (struct sockaddr_in6);
vec[i].msg_hdr.msg_control = NULL;
vec[i].msg_hdr.msg_controllen = 0;
vec[i].msg_hdr.msg_iov = &iov[i];
vec[i].msg_hdr.msg_iovlen = 1;
iov[i].iov_base = buf + UDP_BUF_SIZE * (num + i);
iov[i].iov_len = UDP_BUF_SIZE;
}
for (;;) {
HevTaskYieldType type;
if (res_f >= 0)
res_f = hev_socks5_udp_fwd_f (self, fd_b, buf, num, &bind);
if (res_b >= 0)
res_b = hev_socks5_udp_fwd_b (self, fd_b, vec, num);
if (res_f > 0 || res_b > 0)
type = HEV_TASK_YIELD;
else if ((res_f & res_b) == 0)
type = HEV_TASK_WAITIO;
else
break;
if (task_io_yielder (type, self))
break;
}
}
hev_free (buf);
return 0;
}
int
hev_socks5_udp_splice (HevSocks5UDP *self, int fd)
{
HevSocks5UDPIface *iface;
iface = HEV_OBJECT_GET_IFACE (self, HEV_SOCKS5_UDP_TYPE);
return iface->splicer (self, fd);
}
void *
hev_socks5_udp_iface (void)
{
static HevSocks5UDPIface type = {
.splicer = hev_socks5_udp_splicer,
};
return &type;
}

View File

@ -0,0 +1,55 @@
/*
============================================================================
Name : hev-socks5-udp.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 UDP
============================================================================
*/
#ifndef __HEV_SOCKS5_UDP_H__
#define __HEV_SOCKS5_UDP_H__
#include "hev-socks5-proto.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_UDP(p) ((HevSocks5UDP *)p)
#define HEV_SOCKS5_UDP_IFACE(p) ((HevSocks5UDPIface *)p)
#define HEV_SOCKS5_UDP_TYPE (hev_socks5_udp_iface ())
typedef void HevSocks5UDP;
typedef struct _HevSocks5UDPMsg HevSocks5UDPMsg;
typedef struct _HevSocks5UDPIface HevSocks5UDPIface;
struct _HevSocks5UDPMsg
{
HevSocks5Addr *addr;
void *buf;
size_t len;
};
struct _HevSocks5UDPIface
{
int (*get_fd) (HevSocks5UDP *self);
int (*splicer) (HevSocks5UDP *self, int fd);
};
void *hev_socks5_udp_iface (void);
int hev_socks5_udp_get_fd (HevSocks5UDP *self);
int hev_socks5_udp_sendmmsg (HevSocks5UDP *self, HevSocks5UDPMsg *msgv,
unsigned int num);
int hev_socks5_udp_recvmmsg (HevSocks5UDP *self, HevSocks5UDPMsg *msgv,
unsigned int num, int nonblock);
int hev_socks5_udp_splice (HevSocks5UDP *self, int fd);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_UDP_H__ */

View File

@ -0,0 +1,120 @@
/*
============================================================================
Name : hev-socks5-user.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2023 hev
Description : Socks5 User
============================================================================
*/
#include <string.h>
#include <stdlib.h>
#include "hev-socks5-logger-priv.h"
#include "hev-socks5-user.h"
HevSocks5User *
hev_socks5_user_new (const char *name, unsigned int name_len, const char *pass,
unsigned int pass_len)
{
HevSocks5User *self;
int res;
self = calloc (1, sizeof (HevSocks5User));
if (!self)
return NULL;
res = hev_socks5_user_construct (self, name, name_len, pass, pass_len);
if (res < 0) {
free (self);
return NULL;
}
LOG_D ("%p socks5 user new", self);
return self;
}
int
hev_socks5_user_check (HevSocks5User *self, const char *pass,
unsigned int pass_len)
{
HevSocks5UserClass *klass = HEV_OBJECT_GET_CLASS (self);
return klass->checker (self, pass, pass_len);
}
static int
hev_socks5_user_checker (HevSocks5User *self, const char *pass,
unsigned int pass_len)
{
LOG_D ("%p socks5 user checker", self);
if (self->pass_len != pass_len)
return -1;
if (memcmp (self->pass, pass, pass_len) != 0)
return -1;
return 0;
}
int
hev_socks5_user_construct (HevSocks5User *self, const char *name,
unsigned int name_len, const char *pass,
unsigned int pass_len)
{
int res;
res = hev_object_atomic_construct (&self->base);
if (res < 0)
return res;
LOG_D ("%p socks5 user construct", self);
HEV_OBJECT (self)->klass = HEV_SOCKS5_USER_TYPE;
self->name = malloc (name_len);
self->name_len = name_len;
memcpy (self->name, name, name_len);
self->pass = malloc (pass_len);
self->pass_len = pass_len;
memcpy (self->pass, pass, pass_len);
return 0;
}
static void
hev_socks5_user_destruct (HevObject *base)
{
HevSocks5User *self = HEV_SOCKS5_USER (base);
LOG_D ("%p socks5 user destruct", self);
free (self->name);
free (self->pass);
HEV_OBJECT_ATOMIC_TYPE->destruct (base);
free (base);
}
HevObjectClass *
hev_socks5_user_class (void)
{
static HevSocks5UserClass klass;
HevSocks5UserClass *kptr = &klass;
HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr);
if (!okptr->name) {
memcpy (kptr, HEV_OBJECT_ATOMIC_TYPE, sizeof (HevObjectAtomicClass));
okptr->name = "HevSocks5User";
okptr->destruct = hev_socks5_user_destruct;
kptr->checker = hev_socks5_user_checker;
}
return okptr;
}

View File

@ -0,0 +1,64 @@
/*
============================================================================
Name : hev-socks5-user.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2023 hev
Description : Socks5 User
============================================================================
*/
#ifndef __HEV_SOCKS5_USER_H__
#define __HEV_SOCKS5_USER_H__
#include <hev-object-atomic.h>
#include "hev-rbtree.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5_USER(p) ((HevSocks5User *)p)
#define HEV_SOCKS5_USER_CLASS(p) ((HevSocks5UserClass *)p)
#define HEV_SOCKS5_USER_TYPE (hev_socks5_user_class ())
typedef struct _HevSocks5User HevSocks5User;
typedef struct _HevSocks5UserClass HevSocks5UserClass;
struct _HevSocks5User
{
HevObjectAtomic base;
HevRBTreeNode node;
char *name;
char *pass;
unsigned int name_len;
unsigned int pass_len;
};
struct _HevSocks5UserClass
{
HevObjectAtomicClass base;
int (*checker) (HevSocks5User *self, const char *pass,
unsigned int pass_len);
};
HevObjectClass *hev_socks5_user_class (void);
int hev_socks5_user_construct (HevSocks5User *self, const char *name,
unsigned int name_len, const char *pass,
unsigned int pass_len);
HevSocks5User *hev_socks5_user_new (const char *name, unsigned int name_len,
const char *pass, unsigned int pass_len);
int hev_socks5_user_check (HevSocks5User *self, const char *pass,
unsigned int pass_len);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_USER_H__ */

View File

@ -0,0 +1,109 @@
/*
============================================================================
Name : hev-socks5.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2023 hev
Description : Socks5
============================================================================
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-task-io-socket.h>
#include <hev-task-dns.h>
#include <hev-memory-allocator.h>
#include "hev-socks5-logger-priv.h"
#include "hev-socks5.h"
int
hev_socks5_get_timeout (HevSocks5 *self)
{
return self->timeout;
}
void
hev_socks5_set_timeout (HevSocks5 *self, int timeout)
{
self->timeout = timeout;
}
HevSocks5AddrFamily
hev_socks5_get_addr_family (HevSocks5 *self)
{
return self->addr_family;
}
void
hev_socks5_set_addr_family (HevSocks5 *self, HevSocks5AddrFamily family)
{
self->addr_family = family;
}
static int
hev_socks5_bind (HevSocks5 *self, int sock, const struct sockaddr *dest)
{
return 0;
}
int
hev_socks5_construct (HevSocks5 *self, HevSocks5Type type)
{
int res;
res = hev_object_construct (&self->base);
if (res < 0)
return res;
LOG_D ("%p socks5 construct", self);
HEV_OBJECT (self)->klass = HEV_SOCKS5_TYPE;
self->fd = -1;
self->timeout = -1;
self->type = type;
self->addr_family = HEV_SOCKS5_ADDR_FAMILY_UNSPEC;
return 0;
}
static void
hev_socks5_destruct (HevObject *base)
{
HevSocks5 *self = HEV_SOCKS5 (base);
LOG_D ("%p socks5 destruct", self);
if (self->fd >= 0) {
hev_task_del_fd (hev_task_self (), self->fd);
close (self->fd);
}
HEV_OBJECT_TYPE->destruct (base);
hev_free (base);
}
HevObjectClass *
hev_socks5_class (void)
{
static HevSocks5Class klass;
HevSocks5Class *kptr = &klass;
HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr);
if (!okptr->name) {
memcpy (kptr, HEV_OBJECT_TYPE, sizeof (HevObjectClass));
okptr->name = "HevSocks5";
okptr->destruct = hev_socks5_destruct;
kptr->binder = hev_socks5_bind;
}
return okptr;
}

View File

@ -0,0 +1,78 @@
/*
============================================================================
Name : hev-socks5.h
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2023 hev
Description : Socks5
============================================================================
*/
#ifndef __HEV_SOCKS5_H__
#define __HEV_SOCKS5_H__
#include <netinet/in.h>
#include <sys/socket.h>
#include <hev-object.h>
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_SOCKS5(p) ((HevSocks5 *)p)
#define HEV_SOCKS5_CLASS(p) ((HevSocks5Class *)p)
#define HEV_SOCKS5_TYPE (hev_socks5_class ())
typedef struct _HevSocks5 HevSocks5;
typedef struct _HevSocks5Class HevSocks5Class;
typedef enum _HevSocks5Type HevSocks5Type;
typedef enum _HevSocks5AddrFamily HevSocks5AddrFamily;
enum _HevSocks5Type
{
HEV_SOCKS5_TYPE_NONE,
HEV_SOCKS5_TYPE_TCP,
HEV_SOCKS5_TYPE_UDP_IN_TCP,
HEV_SOCKS5_TYPE_UDP_IN_UDP,
};
enum _HevSocks5AddrFamily
{
HEV_SOCKS5_ADDR_FAMILY_IPV4 = AF_INET,
HEV_SOCKS5_ADDR_FAMILY_IPV6 = AF_INET6,
HEV_SOCKS5_ADDR_FAMILY_UNSPEC = AF_UNSPEC,
};
struct _HevSocks5
{
HevObject base;
int fd;
int timeout;
int udp_associated;
HevSocks5Type type;
HevSocks5AddrFamily addr_family;
};
struct _HevSocks5Class
{
HevObjectClass base;
int (*binder) (HevSocks5 *self, int sock, const struct sockaddr *dest);
};
HevObjectClass *hev_socks5_class (void);
int hev_socks5_construct (HevSocks5 *self, HevSocks5Type type);
int hev_socks5_get_timeout (HevSocks5 *self);
void hev_socks5_set_timeout (HevSocks5 *self, int timeout);
HevSocks5AddrFamily hev_socks5_get_addr_family (HevSocks5 *self);
void hev_socks5_set_addr_family (HevSocks5 *self, HevSocks5AddrFamily family);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_H__ */

View File

@ -0,0 +1,21 @@
/*
============================================================================
Name : hev-config-const.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2024 hev
Description : Config Constants
============================================================================
*/
#ifndef __HEV_CONFIG_CONST_H__
#define __HEV_CONFIG_CONST_H__
#define MAJOR_VERSION (2)
#define MINOR_VERSION (15)
#define MICRO_VERSION (0)
static const int UDP_BUF_SIZE = 1500;
static const int UDP_POOL_SIZE = 512;
static const int TASK_STACK_SIZE = 20480;
#endif /* __HEV_CONFIG_CONST_H__ */

View File

@ -0,0 +1,688 @@
/*
============================================================================
Name : hev-config.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2024 hev
Description : Config
============================================================================
*/
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <lwip/tcp.h>
#include <yaml.h>
#include "hev-logger.h"
#include "hev-config.h"
#include "hev-config-const.h"
static char tun_name[64];
static unsigned int tun_mtu = 8500;
static int multi_queue;
static char tun_ipv4_address[16];
static char tun_ipv6_address[64];
static char tun_post_up_script[1024];
static char tun_pre_down_script[1024];
static HevConfigServer srv;
static int mapdns_address;
static int mapdns_port;
static int mapdns_network;
static int mapdns_netmask;
static int mapdns_cache_size;
static char log_file[1024];
static char pid_file[1024];
static int max_session_count;
static int task_stack_size = 86016;
static int tcp_buffer_size = 65536;
static int udp_recv_buffer_size = 524288;
static int udp_copy_buffer_nums = 10;
static int connect_timeout = 10000;
static int tcp_read_write_timeout = 300000;
static int udp_read_write_timeout = 60000;
static int limit_nofile = 65535;
static int log_level = HEV_LOGGER_WARN;
static int
hev_config_parse_tunnel_ipv4 (yaml_document_t *doc, yaml_node_t *base)
{
yaml_node_pair_t *pair;
if (!base || YAML_MAPPING_NODE != base->type)
return -1;
for (pair = base->data.mapping.pairs.start;
pair < base->data.mapping.pairs.top; pair++) {
yaml_node_t *node;
const char *key, *value;
if (!pair->key || !pair->value)
break;
node = yaml_document_get_node (doc, pair->key);
if (!node || YAML_SCALAR_NODE != node->type)
break;
key = (const char *)node->data.scalar.value;
node = yaml_document_get_node (doc, pair->value);
if (!node || YAML_SCALAR_NODE != node->type)
break;
value = (const char *)node->data.scalar.value;
if (0 == strcmp (key, "address"))
strncpy (tun_ipv4_address, value, 16 - 1);
}
return 0;
}
static int
hev_config_parse_tunnel_ipv6 (yaml_document_t *doc, yaml_node_t *base)
{
yaml_node_pair_t *pair;
if (!base || YAML_MAPPING_NODE != base->type)
return -1;
for (pair = base->data.mapping.pairs.start;
pair < base->data.mapping.pairs.top; pair++) {
yaml_node_t *node;
const char *key, *value;
if (!pair->key || !pair->value)
break;
node = yaml_document_get_node (doc, pair->key);
if (!node || YAML_SCALAR_NODE != node->type)
break;
key = (const char *)node->data.scalar.value;
node = yaml_document_get_node (doc, pair->value);
if (!node || YAML_SCALAR_NODE != node->type)
break;
value = (const char *)node->data.scalar.value;
if (0 == strcmp (key, "address"))
strncpy (tun_ipv6_address, value, 64 - 1);
}
return 0;
}
static int
hev_config_parse_tunnel (yaml_document_t *doc, yaml_node_t *base)
{
yaml_node_pair_t *pair;
if (!base || YAML_MAPPING_NODE != base->type)
return -1;
for (pair = base->data.mapping.pairs.start;
pair < base->data.mapping.pairs.top; pair++) {
yaml_node_t *node;
const char *key;
if (!pair->key || !pair->value)
break;
node = yaml_document_get_node (doc, pair->key);
if (!node || YAML_SCALAR_NODE != node->type)
break;
key = (const char *)node->data.scalar.value;
node = yaml_document_get_node (doc, pair->value);
if (!node)
break;
if (YAML_SCALAR_NODE == node->type) {
const char *value = (const char *)node->data.scalar.value;
if (0 == strcmp (key, "name"))
strncpy (tun_name, value, 64 - 1);
else if (0 == strcmp (key, "mtu"))
tun_mtu = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "multi-queue"))
multi_queue = strcasecmp (value, "false");
else if (0 == strcmp (key, "ipv4"))
strncpy (tun_ipv4_address, value, 16 - 1);
else if (0 == strcmp (key, "ipv6"))
strncpy (tun_ipv6_address, value, 64 - 1);
else if (0 == strcmp (key, "post-up-script"))
strncpy (tun_post_up_script, value, 64 - 1);
else if (0 == strcmp (key, "pre-down-script"))
strncpy (tun_pre_down_script, value, 64 - 1);
} else {
if (0 == strcmp (key, "ipv4"))
hev_config_parse_tunnel_ipv4 (doc, node);
else if (0 == strcmp (key, "ipv6"))
hev_config_parse_tunnel_ipv6 (doc, node);
}
}
return 0;
}
static int
hev_config_parse_socks5 (yaml_document_t *doc, yaml_node_t *base)
{
yaml_node_pair_t *pair;
static char _user[256];
static char _pass[256];
const char *addr = NULL;
const char *port = NULL;
const char *udpm = NULL;
const char *udpa = NULL;
const char *user = NULL;
const char *pass = NULL;
const char *mark = NULL;
const char *pipe = NULL;
const char *tfso = NULL;
if (!base || YAML_MAPPING_NODE != base->type)
return -1;
for (pair = base->data.mapping.pairs.start;
pair < base->data.mapping.pairs.top; pair++) {
yaml_node_t *node;
const char *key, *value;
if (!pair->key || !pair->value)
break;
node = yaml_document_get_node (doc, pair->key);
if (!node || YAML_SCALAR_NODE != node->type)
break;
key = (const char *)node->data.scalar.value;
node = yaml_document_get_node (doc, pair->value);
if (!node || YAML_SCALAR_NODE != node->type)
break;
value = (const char *)node->data.scalar.value;
if (0 == strcmp (key, "port"))
port = value;
else if (0 == strcmp (key, "address"))
addr = value;
else if (0 == strcmp (key, "udp"))
udpm = value;
else if (0 == strcmp (key, "udp-address"))
udpa = value;
else if (0 == strcmp (key, "pipeline"))
pipe = value;
else if (0 == strcmp (key, "username"))
user = value;
else if (0 == strcmp (key, "password"))
pass = value;
else if (0 == strcmp (key, "mark"))
mark = value;
else if (0 == strcmp (key, "tcp-fastopen"))
tfso = value;
}
if (!port) {
fprintf (stderr, "Can't found socks5.port!\n");
return -1;
}
if (!addr) {
fprintf (stderr, "Can't found socks5.address!\n");
return -1;
}
if ((user && !pass) || (!user && pass)) {
fprintf (stderr, "Must be set both socks5 username and password!\n");
return -1;
}
strncpy (srv.addr, addr, 256 - 1);
srv.port = strtoul (port, NULL, 10);
if (pipe && (strcasecmp (pipe, "true") == 0))
srv.pipeline = 1;
if (udpm && (strcasecmp (udpm, "udp") == 0))
srv.udp_in_udp = 1;
if (udpa)
strncpy (srv.udp_addr, udpa, 256 - 1);
if (user && pass) {
strncpy (_user, user, 256 - 1);
strncpy (_pass, pass, 256 - 1);
srv.user = _user;
srv.pass = _pass;
}
if (mark)
srv.mark = strtoul (mark, NULL, 0);
if (tfso)
srv.fastopen = (0 == strcasecmp (tfso, "true")) ? 1 : 0;
return 0;
}
static int
hev_config_parse_mapdns (yaml_document_t *doc, yaml_node_t *base)
{
yaml_node_pair_t *pair;
if (!base || YAML_MAPPING_NODE != base->type)
return -1;
for (pair = base->data.mapping.pairs.start;
pair < base->data.mapping.pairs.top; pair++) {
yaml_node_t *node;
const char *key, *value;
if (!pair->key || !pair->value)
break;
node = yaml_document_get_node (doc, pair->key);
if (!node || YAML_SCALAR_NODE != node->type)
break;
key = (const char *)node->data.scalar.value;
node = yaml_document_get_node (doc, pair->value);
if (!node || YAML_SCALAR_NODE != node->type)
break;
value = (const char *)node->data.scalar.value;
if (0 == strcmp (key, "address"))
inet_pton (AF_INET, value, &mapdns_address);
else if (0 == strcmp (key, "port"))
mapdns_port = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "network"))
inet_pton (AF_INET, value, &mapdns_network);
else if (0 == strcmp (key, "netmask"))
inet_pton (AF_INET, value, &mapdns_netmask);
else if (0 == strcmp (key, "cache-size"))
mapdns_cache_size = strtoul (value, NULL, 10);
}
mapdns_network = ntohl (mapdns_network);
mapdns_netmask = ntohl (mapdns_netmask);
return 0;
}
static int
hev_config_parse_log_level (const char *value)
{
if (0 == strcmp (value, "debug"))
return HEV_LOGGER_DEBUG;
else if (0 == strcmp (value, "info"))
return HEV_LOGGER_INFO;
else if (0 == strcmp (value, "error"))
return HEV_LOGGER_ERROR;
return HEV_LOGGER_WARN;
}
static int
hev_config_parse_misc (yaml_document_t *doc, yaml_node_t *base)
{
yaml_node_pair_t *pair;
int tcp_rw_timeout = -1;
int udp_rw_timeout = -1;
int rw_timeout = -1;
if (!base || YAML_MAPPING_NODE != base->type)
return -1;
for (pair = base->data.mapping.pairs.start;
pair < base->data.mapping.pairs.top; pair++) {
yaml_node_t *node;
const char *key, *value;
if (!pair->key || !pair->value)
break;
node = yaml_document_get_node (doc, pair->key);
if (!node || YAML_SCALAR_NODE != node->type)
break;
key = (const char *)node->data.scalar.value;
node = yaml_document_get_node (doc, pair->value);
if (!node || YAML_SCALAR_NODE != node->type)
break;
value = (const char *)node->data.scalar.value;
if (0 == strcmp (key, "task-stack-size"))
task_stack_size = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "tcp-buffer-size"))
tcp_buffer_size = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "udp-recv-buffer-size"))
udp_recv_buffer_size = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "udp-copy-buffer-nums"))
udp_copy_buffer_nums = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "max-session-count"))
max_session_count = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "connect-timeout"))
connect_timeout = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "read-write-timeout"))
rw_timeout = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "tcp-read-write-timeout"))
tcp_rw_timeout = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "udp-read-write-timeout"))
udp_rw_timeout = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "pid-file"))
strncpy (pid_file, value, 1024 - 1);
else if (0 == strcmp (key, "log-file"))
strncpy (log_file, value, 1024 - 1);
else if (0 == strcmp (key, "log-level"))
log_level = hev_config_parse_log_level (value);
else if (0 == strcmp (key, "limit-nofile"))
limit_nofile = strtol (value, NULL, 10);
}
if (tcp_rw_timeout <= 0)
tcp_rw_timeout = rw_timeout;
if (udp_rw_timeout <= 0)
udp_rw_timeout = rw_timeout;
if (tcp_rw_timeout > 0)
tcp_read_write_timeout = tcp_rw_timeout;
if (udp_rw_timeout > 0)
udp_read_write_timeout = udp_rw_timeout;
return 0;
}
static int
hev_config_parse_doc (yaml_document_t *doc)
{
yaml_node_t *root;
yaml_node_pair_t *pair;
int min_task_stack_size;
int udp_buffer_size;
root = yaml_document_get_root_node (doc);
if (!root || YAML_MAPPING_NODE != root->type)
return -1;
for (pair = root->data.mapping.pairs.start;
pair < root->data.mapping.pairs.top; pair++) {
yaml_node_t *node;
const char *key;
int res = 0;
if (!pair->key || !pair->value)
break;
node = yaml_document_get_node (doc, pair->key);
if (!node || YAML_SCALAR_NODE != node->type)
break;
key = (const char *)node->data.scalar.value;
node = yaml_document_get_node (doc, pair->value);
if (0 == strcmp (key, "tunnel"))
res = hev_config_parse_tunnel (doc, node);
else if (0 == strcmp (key, "socks5"))
res = hev_config_parse_socks5 (doc, node);
else if (0 == strcmp (key, "mapdns"))
res = hev_config_parse_mapdns (doc, node);
else if (0 == strcmp (key, "misc"))
res = hev_config_parse_misc (doc, node);
if (res < 0)
return -1;
}
if (tcp_buffer_size > TCP_SND_BUF)
tcp_buffer_size = TCP_SND_BUF;
udp_buffer_size = UDP_BUF_SIZE * udp_copy_buffer_nums;
if (tcp_buffer_size > udp_buffer_size)
min_task_stack_size = TASK_STACK_SIZE + tcp_buffer_size;
else
min_task_stack_size = TASK_STACK_SIZE + udp_buffer_size;
if (task_stack_size < min_task_stack_size)
task_stack_size = min_task_stack_size;
return 0;
}
int
hev_config_init_from_file (const char *config_path)
{
yaml_parser_t parser;
yaml_document_t doc;
FILE *fp;
int res = -1;
if (!yaml_parser_initialize (&parser))
goto exit;
fp = fopen (config_path, "r");
if (!fp) {
fprintf (stderr, "Open %s failed!\n", config_path);
goto exit_free_parser;
}
yaml_parser_set_input_file (&parser, fp);
if (!yaml_parser_load (&parser, &doc)) {
fprintf (stderr, "Parse %s failed!\n", config_path);
goto exit_close_fp;
}
res = hev_config_parse_doc (&doc);
yaml_document_delete (&doc);
exit_close_fp:
fclose (fp);
exit_free_parser:
yaml_parser_delete (&parser);
exit:
return res;
}
int
hev_config_init_from_str (const unsigned char *config_str,
unsigned int config_len)
{
yaml_parser_t parser;
yaml_document_t doc;
int res = -1;
if (!yaml_parser_initialize (&parser))
goto exit;
yaml_parser_set_input_string (&parser, config_str, config_len);
if (!yaml_parser_load (&parser, &doc)) {
fprintf (stderr, "Failed to parse config.");
goto exit_free_parser;
}
res = hev_config_parse_doc (&doc);
yaml_document_delete (&doc);
exit_free_parser:
yaml_parser_delete (&parser);
exit:
return res;
}
void
hev_config_fini (void)
{
}
const char *
hev_config_get_tunnel_name (void)
{
if (!tun_name[0])
return NULL;
return tun_name;
}
unsigned int
hev_config_get_tunnel_mtu (void)
{
return tun_mtu;
}
int
hev_config_get_tunnel_multi_queue (void)
{
return multi_queue;
}
const char *
hev_config_get_tunnel_ipv4_address (void)
{
if (!tun_ipv4_address[0])
return NULL;
return tun_ipv4_address;
}
const char *
hev_config_get_tunnel_ipv6_address (void)
{
if (!tun_ipv6_address[0])
return NULL;
return tun_ipv6_address;
}
const char *
hev_config_get_tunnel_post_up_script (void)
{
if (!tun_post_up_script[0])
return NULL;
return tun_post_up_script;
}
const char *
hev_config_get_tunnel_pre_down_script (void)
{
if (!tun_pre_down_script[0])
return NULL;
return tun_pre_down_script;
}
HevConfigServer *
hev_config_get_socks5_server (void)
{
return &srv;
}
int
hev_config_get_mapdns_address (void)
{
return mapdns_address;
}
int
hev_config_get_mapdns_port (void)
{
return mapdns_port;
}
int
hev_config_get_mapdns_network (void)
{
return mapdns_network;
}
int
hev_config_get_mapdns_netmask (void)
{
return mapdns_netmask;
}
int
hev_config_get_mapdns_cache_size (void)
{
return mapdns_cache_size;
}
int
hev_config_get_misc_task_stack_size (void)
{
return task_stack_size;
}
int
hev_config_get_misc_tcp_buffer_size (void)
{
return tcp_buffer_size;
}
int
hev_config_get_misc_udp_recv_buffer_size (void)
{
return udp_recv_buffer_size;
}
int
hev_config_get_misc_udp_copy_buffer_nums (void)
{
return udp_copy_buffer_nums;
}
int
hev_config_get_misc_max_session_count (void)
{
return max_session_count;
}
int
hev_config_get_misc_connect_timeout (void)
{
return connect_timeout;
}
int
hev_config_get_misc_tcp_read_write_timeout (void)
{
return tcp_read_write_timeout;
}
int
hev_config_get_misc_udp_read_write_timeout (void)
{
return udp_read_write_timeout;
}
int
hev_config_get_misc_limit_nofile (void)
{
return limit_nofile;
}
const char *
hev_config_get_misc_pid_file (void)
{
if (!pid_file[0])
return NULL;
return pid_file;
}
const char *
hev_config_get_misc_log_file (void)
{
if (!log_file[0])
return "stderr";
return log_file;
}
int
hev_config_get_misc_log_level (void)
{
return log_level;
}

View File

@ -0,0 +1,64 @@
/*
============================================================================
Name : hev-config.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2023 hev
Description : Config
============================================================================
*/
#ifndef __HEV_CONFIG_H__
#define __HEV_CONFIG_H__
typedef struct _HevConfigServer HevConfigServer;
struct _HevConfigServer
{
const char *user;
const char *pass;
unsigned int mark;
short udp_in_udp;
unsigned short port;
unsigned char pipeline;
unsigned char fastopen;
char udp_addr[256];
char addr[256];
};
int hev_config_init_from_file (const char *config_path);
int hev_config_init_from_str (const unsigned char *config_str,
unsigned int config_len);
void hev_config_fini (void);
const char *hev_config_get_tunnel_name (void);
unsigned int hev_config_get_tunnel_mtu (void);
int hev_config_get_tunnel_multi_queue (void);
const char *hev_config_get_tunnel_ipv4_address (void);
const char *hev_config_get_tunnel_ipv6_address (void);
const char *hev_config_get_tunnel_post_up_script (void);
const char *hev_config_get_tunnel_pre_down_script (void);
HevConfigServer *hev_config_get_socks5_server (void);
int hev_config_get_mapdns_address (void);
int hev_config_get_mapdns_port (void);
int hev_config_get_mapdns_network (void);
int hev_config_get_mapdns_netmask (void);
int hev_config_get_mapdns_cache_size (void);
int hev_config_get_misc_task_stack_size (void);
int hev_config_get_misc_tcp_buffer_size (void);
int hev_config_get_misc_udp_recv_buffer_size (void);
int hev_config_get_misc_udp_copy_buffer_nums (void);
int hev_config_get_misc_max_session_count (void);
int hev_config_get_misc_connect_timeout (void);
int hev_config_get_misc_tcp_read_write_timeout (void);
int hev_config_get_misc_udp_read_write_timeout (void);
int hev_config_get_misc_limit_nofile (void);
const char *hev_config_get_misc_pid_file (void);
const char *hev_config_get_misc_log_file (void);
int hev_config_get_misc_log_level (void);
#endif /* __HEV_CONFIG_H__ */

View File

@ -0,0 +1,170 @@
/*
============================================================================
Name : hev-jni.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2023 hev
Description : Jave Native Interface
============================================================================
*/
#ifdef ANDROID
#include <jni.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include "hev-main.h"
#include "hev-jni.h"
/* clang-format off */
#ifndef PKGNAME
#define PKGNAME hev/htproxy
#endif
#ifndef CLSNAME
#define CLSNAME TProxyService
#endif
/* clang-format on */
#define STR(s) STR_ARG (s)
#define STR_ARG(c) #c
#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
typedef struct _ThreadData ThreadData;
struct _ThreadData
{
char *path;
int fd;
};
static int is_working;
static JavaVM *java_vm;
static pthread_t work_thread;
static pthread_mutex_t mutex;
static pthread_key_t current_jni_env;
static void native_start_service (JNIEnv *env, jobject thiz, jstring conig_path,
jint fd);
static void native_stop_service (JNIEnv *env, jobject thiz);
static jlongArray native_get_stats (JNIEnv *env, jobject thiz);
static JNINativeMethod native_methods[] = {
{ "TProxyStartService", "(Ljava/lang/String;I)V",
(void *)native_start_service },
{ "TProxyStopService", "()V", (void *)native_stop_service },
{ "TProxyGetStats", "()[J", (void *)native_get_stats },
};
static void
detach_current_thread (void *env)
{
(*java_vm)->DetachCurrentThread (java_vm);
}
jint
JNI_OnLoad (JavaVM *vm, void *reserved)
{
JNIEnv *env = NULL;
jclass klass;
java_vm = vm;
if (JNI_OK != (*vm)->GetEnv (vm, (void **)&env, JNI_VERSION_1_4)) {
return 0;
}
klass = (*env)->FindClass (env, STR (PKGNAME) "/" STR (CLSNAME));
(*env)->RegisterNatives (env, klass, native_methods,
N_ELEMENTS (native_methods));
(*env)->DeleteLocalRef (env, klass);
pthread_key_create (&current_jni_env, detach_current_thread);
pthread_mutex_init (&mutex, NULL);
return JNI_VERSION_1_4;
}
static void *
thread_handler (void *data)
{
ThreadData *tdata = data;
hev_socks5_tunnel_main (tdata->path, tdata->fd);
free (tdata->path);
free (tdata);
return NULL;
}
static void
native_start_service (JNIEnv *env, jobject thiz, jstring config_path, jint fd)
{
const jbyte *bytes;
ThreadData *tdata;
int res;
pthread_mutex_lock (&mutex);
if (is_working)
goto exit;
tdata = malloc (sizeof (ThreadData));
tdata->fd = fd;
bytes = (const jbyte *)(*env)->GetStringUTFChars (env, config_path, NULL);
tdata->path = strdup ((const char *)bytes);
(*env)->ReleaseStringUTFChars (env, config_path, (const char *)bytes);
res = pthread_create (&work_thread, NULL, thread_handler, tdata);
if (res != 0) {
free (tdata->path);
free (tdata);
goto exit;
}
is_working = 1;
exit:
pthread_mutex_unlock (&mutex);
}
static void
native_stop_service (JNIEnv *env, jobject thiz)
{
pthread_mutex_lock (&mutex);
if (!is_working)
goto exit;
hev_socks5_tunnel_quit ();
pthread_join (work_thread, NULL);
is_working = 0;
exit:
pthread_mutex_unlock (&mutex);
}
static jlongArray
native_get_stats (JNIEnv *env, jobject thiz)
{
size_t tx_packets, rx_packets, tx_bytes, rx_bytes;
jlongArray res;
jlong array[4];
hev_socks5_tunnel_stats (&tx_packets, &tx_bytes, &rx_packets, &rx_bytes);
array[0] = tx_packets;
array[1] = tx_bytes;
array[2] = rx_packets;
array[3] = rx_bytes;
res = (*env)->NewLongArray (env, 4);
(*env)->SetLongArrayRegion (env, res, 0, 4, array);
return res;
}
#endif /* ANDROID */

View File

@ -0,0 +1,13 @@
/*
============================================================================
Name : hev-jni.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2023 hev
Description : Java Native Interface
============================================================================
*/
#ifndef __HEV_JNI_H__
#define __HEV_JNI_H__
#endif /* __HEV_JNI_H__ */

View File

@ -0,0 +1,155 @@
/*
============================================================================
Name : hev-main.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2023 hev
Description : Main
============================================================================
*/
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <lwip/init.h>
#include <hev-task.h>
#include <hev-task-system.h>
#include <hev-socks5-misc.h>
#include "hev-utils.h"
#include "hev-config.h"
#include "hev-config-const.h"
#include "hev-logger.h"
#include "hev-socks5-logger.h"
#include "hev-socks5-tunnel.h"
#include "hev-main.h"
static int
hev_socks5_tunnel_main_inner (int tun_fd)
{
const char *pid_file;
const char *log_file;
int log_level;
int nofile;
int res;
log_file = hev_config_get_misc_log_file ();
log_level = hev_config_get_misc_log_level ();
res = hev_config_get_misc_connect_timeout ();
hev_socks5_set_connect_timeout (res);
res = hev_config_get_misc_tcp_read_write_timeout ();
hev_socks5_set_tcp_timeout (res);
res = hev_config_get_misc_udp_read_write_timeout ();
hev_socks5_set_udp_timeout (res);
res = hev_config_get_misc_udp_recv_buffer_size ();
hev_socks5_set_udp_recv_buffer_size (res);
res = hev_logger_init (log_level, log_file);
if (res < 0)
return -2;
res = hev_socks5_logger_init (log_level, log_file);
if (res < 0)
return -3;
nofile = hev_config_get_misc_limit_nofile ();
res = set_limit_nofile (nofile);
if (res < 0)
LOG_I ("set limit nofile");
pid_file = hev_config_get_misc_pid_file ();
if (pid_file)
run_as_daemon (pid_file);
res = hev_task_system_init ();
if (res < 0)
return -4;
lwip_init ();
res = hev_socks5_tunnel_init (tun_fd);
if (res < 0)
return -5;
hev_socks5_tunnel_run ();
hev_socks5_tunnel_fini ();
hev_socks5_logger_fini ();
hev_logger_fini ();
hev_config_fini ();
hev_task_system_fini ();
return 0;
}
int
hev_socks5_tunnel_main_from_file (const char *config_path, int tun_fd)
{
int res = hev_config_init_from_file (config_path);
if (res < 0)
return -1;
return hev_socks5_tunnel_main_inner (tun_fd);
}
int
hev_socks5_tunnel_main_from_str (const unsigned char *config_str,
unsigned int config_len, int tun_fd)
{
int res = hev_config_init_from_str (config_str, config_len);
if (res < 0)
return -1;
return hev_socks5_tunnel_main_inner (tun_fd);
}
int
hev_socks5_tunnel_main (const char *config_path, int tun_fd)
{
return hev_socks5_tunnel_main_from_file (config_path, tun_fd);
}
void
hev_socks5_tunnel_quit (void)
{
hev_socks5_tunnel_stop ();
}
#ifndef ENABLE_LIBRARY
static void
show_help (const char *self_path)
{
printf ("%s CONFIG_PATH\n", self_path);
printf ("Version: %u.%u.%u %s\n", MAJOR_VERSION, MINOR_VERSION,
MICRO_VERSION, COMMIT_ID);
}
static void
sigint_handler (int signum)
{
hev_socks5_tunnel_stop ();
}
int
main (int argc, char *argv[])
{
int res;
if (argc < 2 || strcmp (argv[1], "--version") == 0) {
show_help (argv[0]);
return -1;
}
signal (SIGINT, sigint_handler);
res = hev_socks5_tunnel_main (argv[1], -1);
if (res < 0)
return -2;
return 0;
}
#endif /* ENABLE_LIBRARY */

View File

@ -0,0 +1,90 @@
/*
============================================================================
Name : hev-main.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2023 hev
Description : Main
============================================================================
*/
#ifndef __HEV_MAIN_H__
#define __HEV_MAIN_H__
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* hev_socks5_tunnel_main:
* @config_path: config file path
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Returns: returns zero on successful, otherwise returns -1.
*
* Since: 2.4.6
*/
int hev_socks5_tunnel_main (const char *config_path, int tun_fd);
/**
* hev_socks5_tunnel_main_from_file:
* @config_path: config file path
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Returns: returns zero on successful, otherwise returns -1.
*
* Since: 2.6.7
*/
int hev_socks5_tunnel_main_from_file (const char *config_path, int tun_fd);
/**
* hev_socks5_tunnel_main_from_str:
* @config_str: string config
* @config_len: the byte length of string config
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Returns: returns zero on successful, otherwise returns -1.
*
* Since: 2.6.7
*/
int hev_socks5_tunnel_main_from_str (const unsigned char *config_str,
unsigned int config_len, int tun_fd);
/**
* hev_socks5_tunnel_quit:
*
* Stop the socks5 tunnel.
*
* Since: 2.4.6
*/
void hev_socks5_tunnel_quit (void);
/**
* hev_socks5_tunnel_stats:
* @tx_packets (out): transmitted packets
* @tx_bytes (out): transmitted bytes
* @rx_packets (out): received packets
* @rx_bytes (out): received bytes
*
* Retrieve tunnel interface traffic statistics.
*
* Since: 2.6.5
*/
void hev_socks5_tunnel_stats (size_t *tx_packets, size_t *tx_bytes,
size_t *rx_packets, size_t *rx_bytes);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_MAIN_H__ */

View File

@ -0,0 +1,332 @@
/*
============================================================================
Name : hev-mapped-dns.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2025 hev
Description : Mapped DNS
============================================================================
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <hev-compiler.h>
#include <hev-memory-allocator.h>
#include "hev-logger.h"
#include "hev-mapped-dns.h"
static HevMappedDNS *singleton;
typedef struct _DNSHdr DNSHdr;
struct _DNSHdr
{
uint16_t id;
uint16_t fl;
uint16_t qd;
uint16_t an;
uint16_t ns;
uint16_t ar;
};
struct _HevMappedDNSNode
{
HevRBTreeNode tree;
HevListNode list;
char *name;
int idx;
};
HevMappedDNS *
hev_mapped_dns_new (int net, int mask, int max)
{
HevMappedDNS *self;
int res;
self = hev_malloc0 (sizeof (HevMappedDNS) + sizeof (void *) * max);
if (!self)
return NULL;
res = hev_mapped_dns_construct (self, net, mask, max);
if (res < 0) {
hev_free (self);
return NULL;
}
LOG_D ("%p mapped dns new", self);
return self;
}
HevMappedDNS *
hev_mapped_dns_get (void)
{
return singleton;
}
void
hev_mapped_dns_put (HevMappedDNS *self)
{
singleton = self;
}
static HevMappedDNSNode *
hev_mapped_dns_node_alloc (const char *name)
{
HevMappedDNSNode *node;
node = hev_malloc0 (sizeof (HevMappedDNSNode));
if (!node)
return NULL;
node->name = strdup (name);
return node;
}
static void
hev_mapped_dns_node_free (HevMappedDNSNode *node)
{
free (node->name);
hev_free (node);
}
static int
hev_mapped_dns_find (HevMappedDNS *self, const char *name)
{
HevRBTreeNode **new = &self->tree.root, *parent = NULL;
HevMappedDNSNode *node;
int idx = self->use;
while (*new) {
int res;
node = container_of (*new, HevMappedDNSNode, tree);
res = strcmp (node->name, name);
parent = *new;
if (res < 0) {
new = &((*new)->left);
} else if (res > 0) {
new = &((*new)->right);
} else {
hev_list_del (&self->list, &node->list);
hev_list_add_tail (&self->list, &node->list);
return node->idx;
}
}
node = hev_mapped_dns_node_alloc (name);
if (!node)
return -1;
hev_rbtree_node_link (&node->tree, parent, new);
hev_rbtree_insert_color (&self->tree, &node->tree);
hev_list_add_tail (&self->list, &node->list);
if (self->use < self->max) {
self->use++;
} else {
HevMappedDNSNode *nf;
HevListNode *nl;
nl = hev_list_first (&self->list);
nf = container_of (nl, HevMappedDNSNode, list);
idx = nf->idx;
hev_rbtree_erase (&self->tree, &nf->tree);
hev_list_del (&self->list, &nf->list);
hev_mapped_dns_node_free (nf);
}
self->records[idx] = node;
node->idx = idx;
return node->idx;
}
static inline uint16_t
read_u16 (const uint8_t *p)
{
return ((uint16_t)p[0] << 8) | p[1];
}
static inline void
write_u16 (uint8_t *p, uint16_t v)
{
p[0] = v >> 8;
p[1] = v;
}
static inline void
write_u32 (uint8_t *p, uint32_t v)
{
p[0] = v >> 24;
p[1] = v >> 16;
p[2] = v >> 8;
p[3] = v;
}
int
hev_mapped_dns_handle (HevMappedDNS *self, void *req, int qlen, void *res,
int slen)
{
DNSHdr *qhdr = req;
DNSHdr *shdr = res;
uint8_t *rb = req;
uint8_t *sb = res;
int ips[32];
int ipo[32];
int ipn = 0;
int off;
int i;
if (slen < qlen)
return -1;
memcpy (res, req, qlen);
qhdr->qd = ntohs (qhdr->qd);
shdr->fl = ntohs (shdr->fl);
shdr->ns = 0;
shdr->an = 0;
shdr->ar = 0;
if (qhdr->qd > 32)
return -1;
off = sizeof (DNSHdr);
for (i = 0; i < qhdr->qd; i++) {
ipo[ipn] = off;
while (rb[off]) {
int poff = off;
off += 1 + rb[off];
if (off >= qlen)
return -1;
rb[poff] = '.';
}
off++;
if ((off + 3) >= qlen)
return -1;
if ((read_u16 (&rb[off + 0]) == 1) && (read_u16 (&rb[off + 2]) == 1)) {
int idx;
idx = hev_mapped_dns_find (self, (char *)&rb[ipo[ipn] + 1]);
if (idx >= 0) {
ips[ipn] = self->net | idx;
ipn++;
}
}
off += 4;
}
for (i = 0; i < ipn; i++) {
if ((off + 15) >= slen)
return -1;
sb[off + 0] = 0xc0;
sb[off + 1] = ipo[i];
write_u16 (&sb[off + 2], 1);
write_u16 (&sb[off + 4], 1);
write_u32 (&sb[off + 6], 1);
write_u16 (&sb[off + 10], 4);
write_u32 (&sb[off + 12], ips[i]);
off += 16;
}
shdr->fl = htons (shdr->fl | 0x8000 | ((shdr->fl & 0x100) >> 1));
shdr->an = htons (ipn);
return off;
}
const char *
hev_mapped_dns_lookup (HevMappedDNS *self, int ip)
{
HevMappedDNSNode *node;
int idx;
idx = ip & ~self->mask;
if (idx >= self->max)
return NULL;
node = self->records[idx];
if (!node)
return NULL;
hev_list_del (&self->list, &node->list);
hev_list_add_tail (&self->list, &node->list);
return node->name;
}
int
hev_mapped_dns_construct (HevMappedDNS *self, int net, int mask, int max)
{
int res;
res = hev_object_construct (&self->base);
if (res < 0)
return res;
LOG_D ("%p mapped dns construct", self);
HEV_OBJECT (self)->klass = HEV_MAPPED_DNS_TYPE;
if (max > ~mask)
return -1;
self->max = max;
self->net = net;
self->mask = mask;
return 0;
}
static void
hev_mapped_dns_destruct (HevObject *base)
{
HevMappedDNS *self = HEV_MAPPED_DNS (base);
HevListNode *n;
LOG_D ("%p mapped dns destruct", self);
n = hev_list_first (&self->list);
while (n) {
HevMappedDNSNode *t;
t = container_of (n, HevMappedDNSNode, list);
n = hev_list_node_next (n);
hev_mapped_dns_node_free (t);
}
HEV_OBJECT_TYPE->destruct (base);
hev_free (base);
}
HevObjectClass *
hev_mapped_dns_class (void)
{
static HevMappedDNSClass klass;
HevMappedDNSClass *kptr = &klass;
HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr);
if (!okptr->name) {
memcpy (kptr, HEV_OBJECT_TYPE, sizeof (HevObjectClass));
okptr->name = "HevMappedDNS";
okptr->destruct = hev_mapped_dns_destruct;
}
return okptr;
}

View File

@ -0,0 +1,65 @@
/*
============================================================================
Name : hev-mapped-dns.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2025 hev
Description : Mapped DNS
============================================================================
*/
#ifndef __HEV_MAPPED_DNS_H__
#define __HEV_MAPPED_DNS_H__
#include <hev-list.h>
#include <hev-rbtree.h>
#include <hev-object.h>
#ifdef __cplusplus
extern "C" {
#endif
#define HEV_MAPPED_DNS(p) ((HevMappedDNS *)p)
#define HEV_MAPPED_DNS_CLASS(p) ((HevMappedDNSClass *)p)
#define HEV_MAPPED_DNS_TYPE (hev_mapped_dns_class ())
typedef struct _HevMappedDNS HevMappedDNS;
typedef struct _HevMappedDNSClass HevMappedDNSClass;
typedef struct _HevMappedDNSNode HevMappedDNSNode;
struct _HevMappedDNS
{
HevObject base;
int use;
int max;
int net;
int mask;
HevList list;
HevRBTree tree;
HevMappedDNSNode *records[0];
};
struct _HevMappedDNSClass
{
HevObjectClass base;
};
HevObjectClass *hev_mapped_dns_class (void);
int hev_mapped_dns_construct (HevMappedDNS *self, int net, int mask, int max);
HevMappedDNS *hev_mapped_dns_new (int net, int mask, int max);
HevMappedDNS *hev_mapped_dns_get (void);
void hev_mapped_dns_put (HevMappedDNS *self);
int hev_mapped_dns_handle (HevMappedDNS *self, void *req, int qlen, void *res,
int slen);
const char *hev_mapped_dns_lookup (HevMappedDNS *self, int ip);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_MAPPED_DNS_H__ */

View File

@ -0,0 +1,359 @@
/*
============================================================================
Name : hev-socks5-session-tcp.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2017 - 2023 hev
Description : Socks5 Session TCP
============================================================================
*/
#include <errno.h>
#include <string.h>
#include <lwip/tcp.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-task-io-socket.h>
#include <hev-task-mutex.h>
#include <hev-memory-allocator.h>
#include <hev-socks5-misc.h>
#include "hev-utils.h"
#include "hev-config.h"
#include "hev-logger.h"
#include "hev-config-const.h"
#include "hev-socks5-tunnel.h"
#include "hev-socks5-session-tcp.h"
static int
task_io_yielder (HevTaskYieldType type, void *data)
{
HevSocks5Session *self = data;
HevListNode *node;
int res;
res = hev_socks5_task_io_yielder (type, data);
node = hev_socks5_session_get_node (self);
hev_socks5_tunnel_update_session (node);
return res;
}
static int
tcp_splice_f (HevSocks5SessionTCP *self)
{
struct iovec iov[64];
struct pbuf *p;
int iovc = 0;
int res = 1;
if (self->queue) {
for (p = self->queue; p && (iovc < 64); p = p->next, iovc++) {
iov[iovc].iov_base = p->payload;
iov[iovc].iov_len = p->len;
}
} else if (self->pcb_eof) {
res = -1;
} else {
res = 0;
}
if (iovc) {
ssize_t s = writev (HEV_SOCKS5 (self)->fd, iov, iovc);
if (0 >= s) {
if ((0 > s) && (EAGAIN == errno))
res = 0;
else
res = -1;
} else {
hev_task_mutex_lock (self->mutex);
self->queue = pbuf_free_header (self->queue, s);
if (self->pcb)
tcp_recved (self->pcb, s);
hev_task_mutex_unlock (self->mutex);
res = 1;
}
} else if (res < 0) {
shutdown (HEV_SOCKS5 (self)->fd, SHUT_WR);
}
return res;
}
static int
tcp_splice_b (HevSocks5SessionTCP *self)
{
struct iovec iov[2];
err_t err = ERR_OK;
int res = 1, iovc;
iovc = hev_ring_buffer_writing (self->buffer, iov);
if (iovc) {
ssize_t s = readv (HEV_SOCKS5 (self)->fd, iov, iovc);
if (0 >= s) {
if ((0 > s) && (EAGAIN == errno))
res = 0;
else
res = -1;
} else {
hev_ring_buffer_write_finish (self->buffer, s);
}
} else {
res = 0;
}
hev_task_mutex_lock (self->mutex);
if (self->pcb) {
iovc = hev_ring_buffer_reading (self->buffer, iov);
if (iovc) {
ssize_t s = 0;
int i;
for (i = 0; i < iovc; i++) {
void *ptr = iov[i].iov_base;
size_t len = iov[i].iov_len;
err |= tcp_write (self->pcb, ptr, len, 0);
s += len;
}
hev_ring_buffer_read_finish (self->buffer, s);
err |= tcp_output (self->pcb);
res = 1;
} else if (res < 0) {
tcp_shutdown (self->pcb, 0, 1);
}
}
hev_task_mutex_unlock (self->mutex);
if (!self->pcb || (err != ERR_OK))
res = -1;
return res;
}
static err_t
tcp_recv_handler (void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
HevSocks5SessionTCP *self = arg;
if (p) {
if (!self->queue) {
self->queue = p;
} else {
if (self->queue->tot_len > TCP_WND_MAX (pcb))
return ERR_WOULDBLOCK;
pbuf_cat (self->queue, p);
}
} else {
self->pcb_eof = 1;
}
hev_task_wakeup (self->data.task);
return ERR_OK;
}
static err_t
tcp_sent_handler (void *arg, struct tcp_pcb *pcb, u16_t len)
{
HevSocks5SessionTCP *self = arg;
hev_ring_buffer_read_release (self->buffer, len);
hev_task_wakeup (self->data.task);
return ERR_OK;
}
static void
tcp_err_handler (void *arg, err_t err)
{
HevSocks5SessionTCP *self = arg;
self->pcb = NULL;
hev_socks5_session_terminate (HEV_SOCKS5_SESSION (self));
}
HevSocks5SessionTCP *
hev_socks5_session_tcp_new (struct tcp_pcb *pcb, HevTaskMutex *mutex)
{
HevSocks5SessionTCP *self;
int res;
self = hev_malloc0 (sizeof (HevSocks5SessionTCP));
if (!self)
return NULL;
res = hev_socks5_session_tcp_construct (self, pcb, mutex);
if (res < 0) {
hev_free (self);
return NULL;
}
LOG_D ("%p socks5 session tcp new", self);
return self;
}
static void
hev_socks5_session_tcp_splice (HevSocks5Session *base)
{
HevSocks5SessionTCP *self = HEV_SOCKS5_SESSION_TCP (base);
int tcp_buffer_size;
int res_f = 1;
int res_b = 1;
LOG_D ("%p socks5 session tcp splice", self);
if (!self->pcb)
return;
tcp_buffer_size = hev_config_get_misc_tcp_buffer_size ();
self->buffer = hev_ring_buffer_alloca (tcp_buffer_size);
if (!self->buffer)
return;
for (;;) {
HevTaskYieldType type;
if (res_f >= 0)
res_f = tcp_splice_f (self);
if (res_b >= 0)
res_b = tcp_splice_b (self);
if (res_f > 0 || res_b > 0)
type = HEV_TASK_YIELD;
else if ((res_f & res_b) == 0)
type = HEV_TASK_WAITIO;
else
break;
if (task_io_yielder (type, base) < 0)
break;
}
while (self->pcb) {
if (hev_ring_buffer_get_use_size (self->buffer) == 0)
break;
if (task_io_yielder (HEV_TASK_WAITIO, base) < 0)
break;
}
}
static HevTask *
hev_socks5_session_tcp_get_task (HevSocks5Session *base)
{
HevSocks5SessionTCP *self = HEV_SOCKS5_SESSION_TCP (base);
return self->data.task;
}
static void
hev_socks5_session_tcp_set_task (HevSocks5Session *base, HevTask *task)
{
HevSocks5SessionTCP *self = HEV_SOCKS5_SESSION_TCP (base);
self->data.task = task;
}
static HevListNode *
hev_socks5_session_tcp_get_node (HevSocks5Session *base)
{
HevSocks5SessionTCP *self = HEV_SOCKS5_SESSION_TCP (base);
return &self->data.node;
}
int
hev_socks5_session_tcp_construct (HevSocks5SessionTCP *self,
struct tcp_pcb *pcb, HevTaskMutex *mutex)
{
HevSocks5Addr addr;
int res;
res = hev_socks5_addr_from_lwip (&addr, &pcb->local_ip, pcb->local_port);
if (res < 0)
return -1;
res = hev_socks5_client_tcp_construct (&self->base, &addr);
if (res < 0)
return -1;
LOG_D ("%p socks5 session tcp construct", self);
HEV_OBJECT (self)->klass = HEV_SOCKS5_SESSION_TCP_TYPE;
tcp_arg (pcb, self);
tcp_recv (pcb, tcp_recv_handler);
tcp_sent (pcb, tcp_sent_handler);
tcp_err (pcb, tcp_err_handler);
self->pcb = pcb;
self->mutex = mutex;
self->data.self = self;
return 0;
}
void
hev_socks5_session_tcp_destruct (HevObject *base)
{
HevSocks5SessionTCP *self = HEV_SOCKS5_SESSION_TCP (base);
LOG_D ("%p socks5 session tcp destruct", self);
hev_task_mutex_lock (self->mutex);
if (self->pcb) {
tcp_recv (self->pcb, NULL);
tcp_sent (self->pcb, NULL);
tcp_err (self->pcb, NULL);
tcp_abort (self->pcb);
}
if (self->queue)
pbuf_free (self->queue);
hev_task_mutex_unlock (self->mutex);
HEV_SOCKS5_CLIENT_TCP_TYPE->destruct (base);
}
static void *
hev_socks5_session_tcp_iface (HevObject *base, void *type)
{
if (type == HEV_SOCKS5_SESSION_TYPE) {
HevSocks5SessionTCPClass *klass = HEV_OBJECT_GET_CLASS (base);
return &klass->session;
}
return HEV_SOCKS5_CLIENT_TCP_TYPE->iface (base, type);
}
HevObjectClass *
hev_socks5_session_tcp_class (void)
{
static HevSocks5SessionTCPClass klass;
HevSocks5SessionTCPClass *kptr = &klass;
HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr);
if (!okptr->name) {
HevSocks5Class *skptr;
HevSocks5SessionIface *siptr;
void *ptr;
ptr = HEV_SOCKS5_CLIENT_TCP_TYPE;
memcpy (kptr, ptr, sizeof (HevSocks5ClientTCPClass));
okptr->name = "HevSocks5SessionTCP";
okptr->destruct = hev_socks5_session_tcp_destruct;
okptr->iface = hev_socks5_session_tcp_iface;
skptr = HEV_SOCKS5_CLASS (kptr);
skptr->binder = hev_socks5_session_bind;
siptr = &kptr->session;
siptr->splicer = hev_socks5_session_tcp_splice;
siptr->get_task = hev_socks5_session_tcp_get_task;
siptr->set_task = hev_socks5_session_tcp_set_task;
siptr->get_node = hev_socks5_session_tcp_get_node;
}
return okptr;
}

View File

@ -0,0 +1,53 @@
/*
============================================================================
Name : hev-socks5-session-tcp.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2017 - 2023 hev
Description : Socks5 Session TCP
============================================================================
*/
#ifndef __HEV_SOCKS5_SESSION_TCP_H__
#define __HEV_SOCKS5_SESSION_TCP_H__
#include <hev-ring-buffer.h>
#include <hev-socks5-client-tcp.h>
#include "hev-socks5-session.h"
#define HEV_SOCKS5_SESSION_TCP(p) ((HevSocks5SessionTCP *)p)
#define HEV_SOCKS5_SESSION_TCP_CLASS(p) ((HevSocks5SessionTCPClass *)p)
#define HEV_SOCKS5_SESSION_TCP_TYPE (hev_socks5_session_tcp_class ())
typedef struct _HevSocks5SessionTCP HevSocks5SessionTCP;
typedef struct _HevSocks5SessionTCPClass HevSocks5SessionTCPClass;
struct _HevSocks5SessionTCP
{
HevSocks5ClientTCP base;
HevSocks5SessionData data;
struct pbuf *queue;
struct tcp_pcb *pcb;
HevTaskMutex *mutex;
HevRingBuffer *buffer;
int pcb_eof;
};
struct _HevSocks5SessionTCPClass
{
HevSocks5ClientTCPClass base;
HevSocks5SessionIface session;
};
HevObjectClass *hev_socks5_session_tcp_class (void);
int hev_socks5_session_tcp_construct (HevSocks5SessionTCP *self,
struct tcp_pcb *pcb, HevTaskMutex *mutex);
HevSocks5SessionTCP *hev_socks5_session_tcp_new (struct tcp_pcb *pcb,
HevTaskMutex *mutex);
#endif /* __HEV_SOCKS5_SESSION_TCP_H__ */

View File

@ -0,0 +1,425 @@
/*
============================================================================
Name : hev-socks5-session-udp.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2017 - 2023 hev
Description : Socks5 Session UDP
============================================================================
*/
#include <errno.h>
#include <string.h>
#include <lwip/udp.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-task-io-socket.h>
#include <hev-task-mutex.h>
#include <hev-memory-allocator.h>
#include <hev-socks5-udp.h>
#include <hev-socks5-misc.h>
#include "hev-utils.h"
#include "hev-config.h"
#include "hev-logger.h"
#include "hev-compiler.h"
#include "hev-config-const.h"
#include "hev-socks5-tunnel.h"
#include "hev-socks5-session-udp.h"
typedef struct _HevSocks5UDPFrame HevSocks5UDPFrame;
struct _HevSocks5UDPFrame
{
HevListNode node;
HevSocks5Addr addr;
struct pbuf *data;
};
static int
task_io_yielder (HevTaskYieldType type, void *data)
{
HevSocks5 *self = data;
HevListNode *node;
int res;
if (self->type == HEV_SOCKS5_TYPE_UDP_IN_UDP) {
ssize_t res;
char buf;
res = recv (self->fd, &buf, sizeof (buf), 0);
if ((res == 0) || ((res < 0) && (errno != EAGAIN))) {
hev_socks5_set_timeout (self, 0);
return -1;
}
}
res = hev_socks5_task_io_yielder (type, data);
node = hev_socks5_session_get_node (HEV_SOCKS5_SESSION (self));
hev_socks5_tunnel_update_session (node);
return res;
}
static int
hev_socks5_session_udp_fwd_f (HevSocks5SessionUDP *self, unsigned int num)
{
HevSocks5UDPMsg msgv[num];
HevSocks5UDPFrame *frame;
HevListNode *node;
struct pbuf *buf;
int i, res;
res = self->frames;
if (res <= 0)
return 0;
res = (res > num) ? num : res;
node = hev_list_first (&self->frame_list);
for (i = 0; i < res; i++) {
frame = container_of (node, HevSocks5UDPFrame, node);
node = hev_list_node_next (node);
buf = frame->data;
msgv[i].buf = buf->payload;
msgv[i].len = buf->len;
msgv[i].addr = &frame->addr;
}
res = hev_socks5_udp_sendmmsg (HEV_SOCKS5_UDP (self), msgv, res);
if (res <= 0) {
LOG_D ("%p socks5 session udp fwd f send", self);
return -1;
}
for (i = 0; i < res; i++) {
node = hev_list_first (&self->frame_list);
frame = container_of (node, HevSocks5UDPFrame, node);
buf = frame->data;
hev_list_del (&self->frame_list, node);
hev_free (frame);
pbuf_free (buf);
self->frames--;
}
return 1;
}
static int
hev_socks5_session_udp_fwd_b (HevSocks5SessionUDP *self, unsigned int num)
{
char buf[UDP_BUF_SIZE * num];
HevSocks5UDPMsg msgv[num];
int i, res;
for (i = 0; i < num; i++) {
msgv[i].buf = buf + UDP_BUF_SIZE * i;
msgv[i].len = UDP_BUF_SIZE;
}
res = hev_socks5_udp_recvmmsg (HEV_SOCKS5_UDP (self), msgv, num, 1);
if (res <= 0) {
if (res == -1 && errno == EAGAIN)
return 0;
LOG_D ("%p socks5 session udp fwd b recv", self);
return -1;
}
for (i = 0; i < res; i++) {
ip_addr_t saddr;
struct pbuf *b;
uint16_t port;
err_t err;
int ret;
if (self->addr && self->port) {
ip_2_ip4 (&saddr)->addr = self->addr;
port = self->port;
} else {
ret = hev_socks5_addr_into_lwip (msgv[i].addr, &saddr, &port);
if (ret < 0) {
LOG_D ("%p socks5 session udp fwd b addr", self);
return -1;
}
}
b = pbuf_alloc_reference (msgv[i].buf, msgv[i].len, PBUF_REF);
if (!b) {
LOG_D ("%p socks5 session udp fwd b buf", self);
return -1;
}
hev_task_mutex_lock (self->mutex);
err = udp_sendfrom (self->pcb, b, &saddr, port);
hev_task_mutex_unlock (self->mutex);
pbuf_free (b);
if (err != ERR_OK) {
LOG_D ("%p socks5 session udp fwd b send", self);
return -1;
}
}
return 1;
}
static void
udp_recv_handler (void *arg, struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *addr, u16_t port)
{
HevSocks5SessionUDP *self = arg;
HevSocks5UDPFrame *frame;
if (!p) {
hev_socks5_session_terminate (HEV_SOCKS5_SESSION (self));
return;
}
if (self->frames > UDP_POOL_SIZE) {
pbuf_free (p);
return;
}
frame = hev_malloc (sizeof (HevSocks5UDPFrame));
if (!frame) {
pbuf_free (p);
return;
}
frame->data = p;
memset (&frame->node, 0, sizeof (frame->node));
hev_socks5_addr_from_lwip (&frame->addr, &pcb->local_ip, pcb->local_port);
if (frame->addr.atype == HEV_SOCKS5_ADDR_TYPE_NAME) {
self->addr = ip_2_ip4 (&pcb->local_ip)->addr;
self->port = pcb->local_port;
}
self->frames++;
hev_list_add_tail (&self->frame_list, &frame->node);
hev_task_wakeup (self->data.task);
}
HevSocks5SessionUDP *
hev_socks5_session_udp_new (struct udp_pcb *pcb, HevTaskMutex *mutex)
{
HevSocks5SessionUDP *self;
int res;
self = hev_malloc0 (sizeof (HevSocks5SessionUDP));
if (!self)
return NULL;
res = hev_socks5_session_udp_construct (self, pcb, mutex);
if (res < 0) {
hev_free (self);
return NULL;
}
LOG_D ("%p socks5 session udp new", self);
return self;
}
static uint16_t
hev_socks5_addr_get_port (const HevSocks5Addr *addr)
{
uint16_t port = 0;
switch (addr->atype) {
case HEV_SOCKS5_ADDR_TYPE_IPV4:
port = addr->ipv4.port;
break;
case HEV_SOCKS5_ADDR_TYPE_IPV6:
port = addr->ipv6.port;
break;
case HEV_SOCKS5_ADDR_TYPE_NAME:
memcpy (&port, addr->domain.addr + addr->domain.len, 2);
}
return port;
}
static int
hev_socks5_session_udp_set_upstream_addr (HevSocks5Client *base,
HevSocks5Addr *addr)
{
HevConfigServer *srv = hev_config_get_socks5_server ();
HevSocks5ClientClass *ckptr;
if (srv->udp_in_udp && srv->udp_addr[0]) {
uint16_t port = hev_socks5_addr_get_port (addr);
hev_socks5_addr_from_name (addr, srv->udp_addr, port);
}
ckptr = HEV_SOCKS5_CLIENT_CLASS (HEV_SOCKS5_CLIENT_UDP_TYPE);
return ckptr->set_upstream_addr (base, addr);
}
static void
hev_socks5_session_udp_splice (HevSocks5Session *base)
{
HevSocks5SessionUDP *self = HEV_SOCKS5_SESSION_UDP (base);
HevTask *task = hev_task_self ();
int res_f = 1, res_b = 1;
int num;
int fd;
LOG_D ("%p socks5 session udp splice", self);
num = hev_config_get_misc_udp_copy_buffer_nums ();
fd = hev_socks5_udp_get_fd (HEV_SOCKS5_UDP (self));
if (hev_task_mod_fd (task, fd, POLLIN | POLLOUT) < 0)
hev_task_add_fd (task, fd, POLLIN | POLLOUT);
for (;;) {
HevTaskYieldType type;
if (res_f >= 0)
res_f = hev_socks5_session_udp_fwd_f (self, num);
if (res_b >= 0)
res_b = hev_socks5_session_udp_fwd_b (self, num);
if (res_f > 0 || res_b > 0)
type = HEV_TASK_YIELD;
else if ((res_f & res_b) == 0)
type = HEV_TASK_WAITIO;
else
break;
if (task_io_yielder (type, self))
break;
}
}
static HevTask *
hev_socks5_session_udp_get_task (HevSocks5Session *base)
{
HevSocks5SessionUDP *self = HEV_SOCKS5_SESSION_UDP (base);
return self->data.task;
}
static void
hev_socks5_session_udp_set_task (HevSocks5Session *base, HevTask *task)
{
HevSocks5SessionUDP *self = HEV_SOCKS5_SESSION_UDP (base);
self->data.task = task;
}
static HevListNode *
hev_socks5_session_udp_get_node (HevSocks5Session *base)
{
HevSocks5SessionUDP *self = HEV_SOCKS5_SESSION_UDP (base);
return &self->data.node;
}
int
hev_socks5_session_udp_construct (HevSocks5SessionUDP *self,
struct udp_pcb *pcb, HevTaskMutex *mutex)
{
HevConfigServer *srv = hev_config_get_socks5_server ();
int type;
int res;
if (srv->udp_in_udp)
type = HEV_SOCKS5_TYPE_UDP_IN_UDP;
else
type = HEV_SOCKS5_TYPE_UDP_IN_TCP;
res = hev_socks5_client_udp_construct (&self->base, type);
if (res < 0)
return -1;
LOG_D ("%p socks5 session udp construct", self);
HEV_OBJECT (self)->klass = HEV_SOCKS5_SESSION_UDP_TYPE;
udp_recv (pcb, udp_recv_handler, self);
self->pcb = pcb;
self->mutex = mutex;
self->data.self = self;
return 0;
}
void
hev_socks5_session_udp_destruct (HevObject *base)
{
HevSocks5SessionUDP *self = HEV_SOCKS5_SESSION_UDP (base);
HevListNode *node;
LOG_D ("%p socks5 session udp destruct", self);
node = hev_list_first (&self->frame_list);
while (node) {
HevSocks5UDPFrame *frame;
frame = container_of (node, HevSocks5UDPFrame, node);
node = hev_list_node_next (node);
pbuf_free (frame->data);
hev_free (frame);
}
hev_task_mutex_lock (self->mutex);
if (self->pcb) {
udp_recv (self->pcb, NULL, NULL);
udp_remove (self->pcb);
}
hev_task_mutex_unlock (self->mutex);
HEV_SOCKS5_CLIENT_UDP_TYPE->destruct (base);
}
static void *
hev_socks5_session_udp_iface (HevObject *base, void *type)
{
if (type == HEV_SOCKS5_SESSION_TYPE) {
HevSocks5SessionUDPClass *klass = HEV_OBJECT_GET_CLASS (base);
return &klass->session;
}
return HEV_SOCKS5_CLIENT_UDP_TYPE->iface (base, type);
}
HevObjectClass *
hev_socks5_session_udp_class (void)
{
static HevSocks5SessionUDPClass klass;
HevSocks5SessionUDPClass *kptr = &klass;
HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr);
if (!okptr->name) {
HevSocks5Class *skptr;
HevSocks5ClientClass *ckptr;
HevSocks5SessionIface *siptr;
void *ptr;
ptr = HEV_SOCKS5_CLIENT_UDP_TYPE;
memcpy (kptr, ptr, sizeof (HevSocks5ClientUDPClass));
okptr->name = "HevSocks5SessionUDP";
okptr->destruct = hev_socks5_session_udp_destruct;
okptr->iface = hev_socks5_session_udp_iface;
skptr = HEV_SOCKS5_CLASS (kptr);
skptr->binder = hev_socks5_session_bind;
ckptr = HEV_SOCKS5_CLIENT_CLASS (kptr);
ckptr->set_upstream_addr = hev_socks5_session_udp_set_upstream_addr;
siptr = &kptr->session;
siptr->splicer = hev_socks5_session_udp_splice;
siptr->get_task = hev_socks5_session_udp_get_task;
siptr->set_task = hev_socks5_session_udp_set_task;
siptr->get_node = hev_socks5_session_udp_get_node;
}
return okptr;
}

View File

@ -0,0 +1,53 @@
/*
============================================================================
Name : hev-socks5-session-udp.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2017 - 2023 hev
Description : Socks5 Session UDP
============================================================================
*/
#ifndef __HEV_SOCKS5_SESSION_UDP_H__
#define __HEV_SOCKS5_SESSION_UDP_H__
#include <hev-socks5-client-udp.h>
#include "hev-socks5-session.h"
#define HEV_SOCKS5_SESSION_UDP(p) ((HevSocks5SessionUDP *)p)
#define HEV_SOCKS5_SESSION_UDP_CLASS(p) ((HevSocks5SessionUDPClass *)p)
#define HEV_SOCKS5_SESSION_UDP_TYPE (hev_socks5_session_udp_class ())
typedef struct _HevSocks5SessionUDP HevSocks5SessionUDP;
typedef struct _HevSocks5SessionUDPClass HevSocks5SessionUDPClass;
struct _HevSocks5SessionUDP
{
HevSocks5ClientUDP base;
HevSocks5SessionData data;
HevList frame_list;
struct udp_pcb *pcb;
HevTaskMutex *mutex;
int frames;
int addr;
int port;
};
struct _HevSocks5SessionUDPClass
{
HevSocks5ClientUDPClass base;
HevSocks5SessionIface session;
};
HevObjectClass *hev_socks5_session_udp_class (void);
int hev_socks5_session_udp_construct (HevSocks5SessionUDP *self,
struct udp_pcb *pcb, HevTaskMutex *mutex);
HevSocks5SessionUDP *hev_socks5_session_udp_new (struct udp_pcb *pcb,
HevTaskMutex *mutex);
#endif /* __HEV_SOCKS5_SESSION_UDP_H__ */

View File

@ -0,0 +1,113 @@
/*
============================================================================
Name : hev-socks5-session.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2017 - 2023 hev
Description : Socks5 Session
============================================================================
*/
#include <string.h>
#include "hev-utils.h"
#include "hev-logger.h"
#include "hev-config.h"
#include "hev-socks5-client.h"
#include "hev-socks5-session.h"
void
hev_socks5_session_run (HevSocks5Session *self)
{
HevSocks5SessionIface *iface;
HevConfigServer *srv;
int res;
LOG_D ("%p socks5 session run", self);
srv = hev_config_get_socks5_server ();
res = hev_socks5_client_connect (HEV_SOCKS5_CLIENT (self), srv->addr,
srv->port);
if (res < 0) {
LOG_I ("%p socks5 session connect", self);
return;
}
if (srv->user && srv->pass) {
hev_socks5_client_set_auth (HEV_SOCKS5_CLIENT (self), srv->user,
srv->pass);
LOG_D ("%p socks5 client auth %s:%s", self, srv->user, srv->pass);
}
res = hev_socks5_client_handshake (HEV_SOCKS5_CLIENT (self), srv->pipeline);
if (res < 0) {
LOG_I ("%p socks5 session handshake", self);
return;
}
iface = HEV_OBJECT_GET_IFACE (self, HEV_SOCKS5_SESSION_TYPE);
iface->splicer (self);
}
void
hev_socks5_session_terminate (HevSocks5Session *self)
{
HevSocks5SessionIface *iface;
LOG_D ("%p socks5 session terminate", self);
iface = HEV_OBJECT_GET_IFACE (self, HEV_SOCKS5_SESSION_TYPE);
hev_socks5_set_timeout (HEV_SOCKS5 (self), 0);
hev_task_wakeup (iface->get_task (self));
}
void
hev_socks5_session_set_task (HevSocks5Session *self, HevTask *task)
{
HevSocks5SessionIface *iface;
iface = HEV_OBJECT_GET_IFACE (self, HEV_SOCKS5_SESSION_TYPE);
iface->set_task (self, task);
}
HevListNode *
hev_socks5_session_get_node (HevSocks5Session *self)
{
HevSocks5SessionIface *iface;
iface = HEV_OBJECT_GET_IFACE (self, HEV_SOCKS5_SESSION_TYPE);
return iface->get_node (self);
}
int
hev_socks5_session_bind (HevSocks5 *self, int fd, const struct sockaddr *dest)
{
HevConfigServer *srv;
unsigned int mark;
LOG_D ("%p socks5 session bind", self);
srv = hev_config_get_socks5_server ();
mark = srv->mark;
if (mark) {
int res;
res = set_sock_mark (fd, mark);
if (res < 0)
return -1;
}
set_sock_tcp_fastopen (fd, srv->fastopen);
return 0;
}
void *
hev_socks5_session_iface (void)
{
static char type;
return &type;
}

View File

@ -0,0 +1,51 @@
/*
============================================================================
Name : hev-socks5-session.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2017 - 2023 hev
Description : Socks5 Session
============================================================================
*/
#ifndef __HEV_SOCKS5_SESSION_H__
#define __HEV_SOCKS5_SESSION_H__
#include <hev-task.h>
#include "hev-list.h"
#define HEV_SOCKS5_SESSION(p) ((HevSocks5Session *)p)
#define HEV_SOCKS5_SESSION_IFACE(p) ((HevSocks5SessionIface *)p)
#define HEV_SOCKS5_SESSION_TYPE (hev_socks5_session_iface ())
typedef void HevSocks5Session;
typedef struct _HevSocks5SessionData HevSocks5SessionData;
typedef struct _HevSocks5SessionIface HevSocks5SessionIface;
struct _HevSocks5SessionData
{
HevListNode node;
HevTask *task;
HevSocks5Session *self;
};
struct _HevSocks5SessionIface
{
void (*splicer) (HevSocks5Session *self);
HevTask *(*get_task) (HevSocks5Session *self);
void (*set_task) (HevSocks5Session *self, HevTask *task);
HevListNode *(*get_node) (HevSocks5Session *self);
};
void *hev_socks5_session_iface (void);
void hev_socks5_session_run (HevSocks5Session *self);
void hev_socks5_session_terminate (HevSocks5Session *self);
void hev_socks5_session_set_task (HevSocks5Session *self, HevTask *task);
HevListNode *hev_socks5_session_get_node (HevSocks5Session *self);
int hev_socks5_session_bind (HevSocks5 *self, int fd,
const struct sockaddr *dest);
#endif /* __HEV_SOCKS5_SESSION_H__ */

View File

@ -0,0 +1,727 @@
/*
============================================================================
Name : hev-socks5-tunnel.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2025 hev
Description : Socks5 Tunnel
============================================================================
*/
#include <errno.h>
#include <assert.h>
#include <signal.h>
#include <string.h>
#include <sys/ioctl.h>
#include <lwip/tcp.h>
#include <lwip/udp.h>
#include <lwip/nd6.h>
#include <lwip/netif.h>
#include <lwip/ip4_frag.h>
#include <lwip/ip6_frag.h>
#include <lwip/priv/tcp_priv.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-task-mutex.h>
#include <hev-task-system.h>
#include <hev-memory-allocator.h>
#include "hev-exec.h"
#include "hev-config.h"
#include "hev-logger.h"
#include "hev-tunnel.h"
#include "hev-compiler.h"
#include "hev-mapped-dns.h"
#include "hev-config-const.h"
#include "hev-socks5-session-tcp.h"
#include "hev-socks5-session-udp.h"
#include "hev-socks5-tunnel.h"
static int run;
static int tun_fd = -1;
static int tun_fd_local;
static int session_count;
static int event_fds[2] = { -1, -1 };
static size_t stat_tx_packets;
static size_t stat_rx_packets;
static size_t stat_tx_bytes;
static size_t stat_rx_bytes;
static struct netif netif;
static struct tcp_pcb *tcp;
static struct udp_pcb *udp;
static HevTaskMutex mutex;
static HevTask *task_event;
static HevTask *task_lwip_io;
static HevTask *task_lwip_timer;
static HevList session_set;
static int
task_io_yielder (HevTaskYieldType type, void *data)
{
hev_task_yield (type);
return run ? 0 : -1;
}
static err_t
netif_output_handler (struct netif *netif, struct pbuf *p)
{
ssize_t s;
s = hev_tunnel_write (tun_fd, p);
if (s <= 0) {
if (errno == EAGAIN)
return ERR_WOULDBLOCK;
LOG_W ("socks5 tunnel write");
return ERR_IF;
}
stat_rx_packets++;
stat_rx_bytes += s;
return ERR_OK;
}
static err_t
netif_output_v4_handler (struct netif *netif, struct pbuf *p,
const ip4_addr_t *ipaddr)
{
return netif_output_handler (netif, p);
}
static err_t
netif_output_v6_handler (struct netif *netif, struct pbuf *p,
const ip6_addr_t *ipaddr)
{
return netif_output_handler (netif, p);
}
static err_t
netif_init_handler (struct netif *netif)
{
netif->output = netif_output_v4_handler;
netif->output_ip6 = netif_output_v6_handler;
return ERR_OK;
}
static void
hev_socks5_tunnel_insert_session (HevListNode *node)
{
HevSocks5SessionData *sd;
int max_session_count;
hev_list_add_tail (&session_set, node);
session_count++;
max_session_count = hev_config_get_misc_max_session_count ();
if (!max_session_count || session_count < max_session_count)
return;
node = hev_list_first (&session_set);
sd = container_of (node, HevSocks5SessionData, node);
hev_socks5_session_terminate (sd->self);
}
static void
hev_socks5_tunnel_delete_session (HevListNode *node)
{
hev_list_del (&session_set, node);
session_count--;
}
void
hev_socks5_tunnel_update_session (HevListNode *node)
{
int max_session_count;
max_session_count = hev_config_get_misc_max_session_count ();
if (!max_session_count)
return;
hev_list_del (&session_set, node);
hev_list_add_tail (&session_set, node);
}
static void
hev_socks5_session_task_entry (void *data)
{
HevSocks5Session *s = data;
hev_socks5_session_run (s);
hev_socks5_tunnel_delete_session (hev_socks5_session_get_node (s));
hev_object_unref (HEV_OBJECT (s));
}
static err_t
tcp_accept_handler (void *arg, struct tcp_pcb *pcb, err_t err)
{
HevSocks5SessionTCP *tcp;
HevListNode *node;
int stack_size;
HevTask *task;
if (err != ERR_OK)
return err;
if (!run)
return ERR_RST;
tcp = hev_socks5_session_tcp_new (pcb, &mutex);
if (!tcp)
return ERR_MEM;
stack_size = hev_config_get_misc_task_stack_size ();
task = hev_task_new (stack_size);
if (!task) {
hev_object_unref (HEV_OBJECT (tcp));
return ERR_MEM;
}
hev_socks5_session_set_task (HEV_SOCKS5_SESSION (tcp), task);
node = hev_socks5_session_get_node (HEV_SOCKS5_SESSION (tcp));
hev_socks5_tunnel_insert_session (node);
hev_task_run (task, hev_socks5_session_task_entry, tcp);
hev_task_wakeup (task_lwip_timer);
return ERR_OK;
}
static void
dns_recv_handler (void *arg, struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *addr, u16_t port)
{
HevMappedDNS *dns = arg;
struct pbuf *b;
int res;
LOG_D ("%p mapped dns handle", dns);
b = pbuf_alloc (PBUF_TRANSPORT, UDP_BUF_SIZE, PBUF_RAM);
if (!b)
goto exit;
res = hev_mapped_dns_handle (dns, p->payload, p->len, b->payload, b->len);
if (res < 0)
goto free;
b->len = res;
b->tot_len = res;
udp_sendfrom (pcb, b, &pcb->local_ip, pcb->local_port);
free:
pbuf_free (b);
exit:
pbuf_free (p);
udp_recv (pcb, NULL, NULL);
udp_remove (pcb);
}
static void
udp_recv_handler (void *arg, struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *addr, u16_t port)
{
HevSocks5SessionUDP *udp;
HevListNode *node;
HevMappedDNS *dns;
int stack_size;
HevTask *task;
if (!run) {
udp_remove (pcb);
return;
}
dns = hev_mapped_dns_get ();
if (dns && addr->type == IPADDR_TYPE_V4) {
int faddr = hev_config_get_mapdns_address ();
int fport = hev_config_get_mapdns_port ();
if (fport == port && faddr == ip_2_ip4 (addr)->addr) {
udp_recv (pcb, dns_recv_handler, dns);
return;
}
}
udp = hev_socks5_session_udp_new (pcb, &mutex);
if (!udp) {
udp_remove (pcb);
return;
}
stack_size = hev_config_get_misc_task_stack_size ();
task = hev_task_new (stack_size);
if (!task) {
hev_object_unref (HEV_OBJECT (udp));
return;
}
hev_socks5_session_set_task (HEV_SOCKS5_SESSION (udp), task);
node = hev_socks5_session_get_node (HEV_SOCKS5_SESSION (udp));
hev_socks5_tunnel_insert_session (node);
hev_task_run (task, hev_socks5_session_task_entry, udp);
hev_task_wakeup (task_lwip_timer);
}
static void
event_task_entry (void *data)
{
HevListNode *node;
int val;
LOG_D ("socks5 tunnel event task run");
hev_task_add_fd (task_event, event_fds[0], POLLIN);
hev_task_io_read (event_fds[0], &val, sizeof (val), NULL, NULL);
run = 0;
node = hev_list_first (&session_set);
for (; node; node = hev_list_node_next (node)) {
HevSocks5SessionData *sd;
sd = container_of (node, HevSocks5SessionData, node);
hev_socks5_session_terminate (sd->self);
}
hev_task_join (task_lwip_io);
hev_task_join (task_lwip_timer);
hev_task_del_fd (task_event, event_fds[0]);
}
static void
lwip_io_task_entry (void *data)
{
const unsigned int mtu = hev_config_get_tunnel_mtu ();
LOG_D ("socks5 tunnel lwip task run");
hev_tunnel_add_task (tun_fd, task_lwip_io);
for (; run;) {
struct pbuf *buf;
buf = hev_tunnel_read (tun_fd, mtu, task_io_yielder, NULL);
if (!buf)
continue;
stat_tx_packets++;
stat_tx_bytes += buf->tot_len;
hev_task_mutex_lock (&mutex);
if (netif.input (buf, &netif) != ERR_OK)
pbuf_free (buf);
hev_task_mutex_unlock (&mutex);
}
hev_tunnel_del_task (tun_fd, task_lwip_io);
}
static void
lwip_timer_task_entry (void *data)
{
unsigned int i;
LOG_D ("socks5 tunnel timer task run");
for (i = 1; run; i++) {
hev_task_mutex_lock (&mutex);
tcp_tmr ();
if ((i & 3) == 0) {
#if IP_REASSEMBLY
ip_reass_tmr ();
#endif
#if LWIP_IPV6
nd6_tmr ();
#if LWIP_IPV6_REASS
ip6_reass_tmr ();
#endif
#endif
}
hev_task_mutex_unlock (&mutex);
if (hev_list_first (&session_set))
hev_task_sleep (TCP_TMR_INTERVAL);
else
hev_task_yield (HEV_TASK_WAITIO);
}
}
static int
tunnel_init (int extern_tun_fd)
{
const char *script_path, *name, *ipv4, *ipv6;
int multi_queue, res;
unsigned int mtu;
if (extern_tun_fd >= 0) {
int nonblock = 1;
res = ioctl (extern_tun_fd, FIONBIO, (char *)&nonblock);
if (res < 0) {
LOG_E ("socks5 tunnel non-blocking");
return -1;
}
tun_fd = extern_tun_fd;
return 0;
}
name = hev_config_get_tunnel_name ();
multi_queue = hev_config_get_tunnel_multi_queue ();
tun_fd = hev_tunnel_open (name, multi_queue);
if (tun_fd < 0) {
LOG_E ("socks5 tunnel open (%s)", strerror (errno));
return -1;
}
mtu = hev_config_get_tunnel_mtu ();
res = hev_tunnel_set_mtu (mtu);
if (res < 0) {
LOG_E ("socks5 tunnel mtu");
return -1;
}
ipv4 = hev_config_get_tunnel_ipv4_address ();
if (ipv4) {
res = hev_tunnel_set_ipv4 (ipv4, 32);
if (res < 0) {
LOG_E ("socks5 tunnel ipv4");
return -1;
}
}
ipv6 = hev_config_get_tunnel_ipv6_address ();
if (ipv6) {
res = hev_tunnel_set_ipv6 (ipv6, 128);
if (res < 0) {
LOG_E ("socks5 tunnel ipv6");
return -1;
}
}
res = hev_tunnel_set_state (1);
if (res < 0) {
LOG_E ("socks5 tunnel state");
return -1;
}
script_path = hev_config_get_tunnel_post_up_script ();
if (script_path)
hev_exec_run (script_path, hev_tunnel_get_name (),
hev_tunnel_get_index (), 0);
tun_fd_local = 1;
return 0;
}
static void
tunnel_fini (void)
{
const char *script_path;
if (!tun_fd_local)
return;
script_path = hev_config_get_tunnel_pre_down_script ();
if (script_path)
hev_exec_run (script_path, hev_tunnel_get_name (),
hev_tunnel_get_index (), 1);
hev_tunnel_close (tun_fd);
tun_fd_local = 0;
tun_fd = -1;
}
static int
gateway_init (void)
{
ip4_addr_t addr4, mask, gw;
ip6_addr_t addr6;
netif_add_noaddr (&netif, NULL, netif_init_handler, ip_input);
ip4_addr_set_loopback (&addr4);
ip4_addr_set_any (&mask);
ip4_addr_set_any (&gw);
netif_set_addr (&netif, &addr4, &mask, &gw);
ip6_addr_set_loopback (&addr6);
netif_add_ip6_address (&netif, &addr6, NULL);
netif_set_up (&netif);
netif_set_link_up (&netif);
netif_set_default (&netif);
netif_set_flags (&netif, NETIF_FLAG_PRETEND_TCP);
tcp = tcp_new_ip_type (IPADDR_TYPE_ANY);
tcp_bind_netif (tcp, &netif);
tcp_bind (tcp, NULL, 0);
tcp = tcp_listen (tcp);
tcp_accept (tcp, tcp_accept_handler);
udp = udp_new_ip_type (IPADDR_TYPE_ANY);
udp_bind_netif (udp, &netif);
udp_bind (udp, NULL, 0);
udp_recv (udp, udp_recv_handler, NULL);
return 0;
}
static void
gateway_fini (void)
{
udp_remove (udp);
tcp_close (tcp);
netif_remove (&netif);
}
static int
event_task_init (void)
{
int nonblock = 1;
int res;
res = socketpair (PF_LOCAL, SOCK_STREAM, 0, event_fds);
if (res < 0) {
LOG_E ("socks5 tunnel event");
return -1;
}
res = ioctl (event_fds[0], FIONBIO, (char *)&nonblock);
if (res < 0) {
LOG_E ("socks5 tunnel event nonblock");
return -1;
}
task_event = hev_task_new (-1);
if (!task_event) {
LOG_E ("socks5 tunnel task event");
return -1;
}
return 0;
}
static void
event_task_fini (void)
{
if (task_event) {
hev_task_unref (task_event);
task_event = NULL;
}
if (event_fds[0] >= 0) {
close (event_fds[0]);
event_fds[0] = -1;
}
if (event_fds[1] >= 0) {
close (event_fds[1]);
event_fds[1] = -1;
}
}
static int
lwip_io_task_init (void)
{
task_lwip_io = hev_task_new (-1);
if (!task_lwip_io) {
LOG_E ("socks5 tunnel task lwip");
return -1;
}
hev_task_set_priority (task_lwip_io, 1);
return 0;
}
static void
lwip_io_task_fini (void)
{
if (task_lwip_io) {
hev_task_unref (task_lwip_io);
task_lwip_io = NULL;
}
}
static int
lwip_timer_task_init (void)
{
task_lwip_timer = hev_task_new (-1);
if (!task_lwip_timer) {
LOG_E ("socks5 tunnel task timer");
return -1;
}
hev_task_set_priority (task_lwip_timer, 1);
return 0;
}
static void
lwip_timer_task_fini (void)
{
if (task_lwip_timer) {
hev_task_unref (task_lwip_timer);
task_lwip_timer = NULL;
}
}
static int
mapped_dns_init (void)
{
HevMappedDNS *dns;
int cache_size;
int network;
int netmask;
network = hev_config_get_mapdns_network ();
netmask = hev_config_get_mapdns_netmask ();
cache_size = hev_config_get_mapdns_cache_size ();
if (!cache_size)
return 0;
dns = hev_mapped_dns_new (network, netmask, cache_size);
if (!dns)
return -1;
hev_mapped_dns_put (dns);
return 0;
}
static void
mapped_dns_fini (void)
{
HevMappedDNS *dns;
dns = hev_mapped_dns_get ();
if (dns) {
hev_object_unref (HEV_OBJECT (dns));
hev_mapped_dns_put (NULL);
}
}
int
hev_socks5_tunnel_init (int tun_fd)
{
int res;
LOG_D ("socks5 tunnel init");
res = tunnel_init (tun_fd);
if (res < 0)
goto exit;
res = gateway_init ();
if (res < 0)
goto exit;
res = event_task_init ();
if (res < 0)
goto exit;
res = lwip_io_task_init ();
if (res < 0)
goto exit;
res = lwip_timer_task_init ();
if (res < 0)
goto exit;
res = mapped_dns_init ();
if (res < 0)
goto exit;
signal (SIGPIPE, SIG_IGN);
hev_task_mutex_init (&mutex);
return 0;
exit:
hev_socks5_tunnel_fini ();
return -1;
}
void
hev_socks5_tunnel_fini (void)
{
LOG_D ("socks5 tunnel fini");
mapped_dns_fini ();
lwip_timer_task_fini ();
lwip_io_task_fini ();
event_task_fini ();
gateway_fini ();
tunnel_fini ();
stat_tx_packets = 0;
stat_rx_packets = 0;
stat_tx_bytes = 0;
stat_rx_bytes = 0;
}
int
hev_socks5_tunnel_run (void)
{
LOG_D ("socks5 tunnel run");
task_event = hev_task_ref (task_event);
hev_task_run (task_event, event_task_entry, NULL);
task_lwip_io = hev_task_ref (task_lwip_io);
hev_task_run (task_lwip_io, lwip_io_task_entry, NULL);
task_lwip_timer = hev_task_ref (task_lwip_timer);
hev_task_run (task_lwip_timer, lwip_timer_task_entry, NULL);
run = 1;
hev_task_system_run ();
return 0;
}
void
hev_socks5_tunnel_stop (void)
{
int res = 0;
int fd;
LOG_D ("socks5 tunnel stop");
for (;;) {
fd = READ_ONCE (event_fds[1]);
if (fd >= 0)
break;
/* Wait for async initialization */
usleep (100 * 1000);
}
res = write (fd, &res, 1);
assert (res > 0 && "socks5 tunnel write event");
}
void
hev_socks5_tunnel_stats (size_t *tx_packets, size_t *tx_bytes,
size_t *rx_packets, size_t *rx_bytes)
{
LOG_D ("socks5 tunnel stats");
if (tx_packets)
*tx_packets = stat_tx_packets;
if (tx_bytes)
*tx_bytes = stat_tx_bytes;
if (rx_packets)
*rx_packets = stat_rx_packets;
if (rx_bytes)
*rx_bytes = stat_rx_bytes;
}

View File

@ -0,0 +1,26 @@
/*
============================================================================
Name : hev-socks5-tunnel.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2023 hev
Description : Socks5 Tunnel
============================================================================
*/
#ifndef __HEV_SOCKS5_TUNNEL_H__
#define __HEV_SOCKS5_TUNNEL_H__
#include "hev-list.h"
int hev_socks5_tunnel_init (int tun_fd);
void hev_socks5_tunnel_fini (void);
int hev_socks5_tunnel_run (void);
void hev_socks5_tunnel_stop (void);
void hev_socks5_tunnel_stats (size_t *tx_packets, size_t *tx_bytes,
size_t *rx_packets, size_t *rx_bytes);
void hev_socks5_tunnel_update_session (HevListNode *node);
#endif /* __HEV_SOCKS5_TUNNEL_H__ */

View File

@ -0,0 +1,242 @@
/*
============================================================================
Name : hev-tunnel-freebsd.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2023 hev
Description : Tunnel on FreeBSD
============================================================================
*/
#if defined(__FreeBSD__)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if_tun.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet6/nd6.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include "hev-tunnel.h"
static char tun_name[IFNAMSIZ];
int
hev_tunnel_open (const char *name, int multi_queue)
{
struct ifreq ifr;
char buf[256];
int res;
int sfd;
int tfd;
snprintf (buf, sizeof (buf), "/dev/%s", name);
tfd = hev_task_io_open (buf, O_RDWR);
if (tfd >= 0)
goto succ;
tfd = hev_task_io_open ("/dev/tun", O_RDWR);
if (tfd < 0)
goto fail;
res = ioctl (tfd, TUNGIFNAME, (void *)&ifr);
if (res < 0)
goto fail_close;
sfd = socket (AF_INET, SOCK_DGRAM, 0);
if (sfd < 0)
goto fail_close;
strncpy (buf, name, sizeof (buf) - 1);
ifr.ifr_data = buf;
res = ioctl (sfd, SIOCSIFNAME, (void *)&ifr);
close (sfd);
if (res < 0)
goto fail_close;
succ:
strncpy (tun_name, name, IFNAMSIZ - 1);
return tfd;
fail_close:
close (tfd);
fail:
return -1;
}
void
hev_tunnel_close (int fd)
{
struct ifreq ifr = { 0 };
close (fd);
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
return;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
ioctl (fd, SIOCIFDESTROY, (void *)&ifr);
close (fd);
}
int
hev_tunnel_set_mtu (int mtu)
{
int fd, res = -1;
struct ifreq ifr = { .ifr_mtu = mtu };
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
goto exit;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
res = ioctl (fd, SIOCSIFMTU, (void *)&ifr);
close (fd);
exit:
return res;
}
int
hev_tunnel_set_state (int state)
{
struct ifreq ifr = { 0 };
int res = -1;
int fd;
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
goto exit;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
res = ioctl (fd, SIOCGIFFLAGS, (void *)&ifr);
if (res < 0)
goto exit_close;
if (state)
ifr.ifr_flags |= IFF_UP;
else
ifr.ifr_flags &= ~IFF_UP;
res = ioctl (fd, SIOCSIFFLAGS, (void *)&ifr);
exit_close:
close (fd);
exit:
return res;
}
int
hev_tunnel_set_ipv4 (const char *addr, unsigned int prefix)
{
struct ifaliasreq ifra = { 0 };
struct sockaddr_in *pa;
int res = -1;
int fd;
fd = socket (AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
goto exit;
strcpy (ifra.ifra_name, tun_name);
pa = (struct sockaddr_in *)&ifra.ifra_addr;
pa->sin_len = sizeof (ifra.ifra_addr);
pa->sin_family = AF_INET;
res = inet_pton (AF_INET, addr, &pa->sin_addr);
if (!res)
goto exit_close;
memcpy (&ifra.ifra_broadaddr, &ifra.ifra_addr, sizeof (ifra.ifra_addr));
pa = (struct sockaddr_in *)&ifra.ifra_mask;
pa->sin_len = sizeof (ifra.ifra_addr);
pa->sin_family = AF_INET;
pa->sin_addr.s_addr = htonl (((unsigned int)(-1)) << (32 - prefix));
res = ioctl (fd, SIOCAIFADDR, &ifra);
exit_close:
close (fd);
exit:
return res;
}
int
hev_tunnel_set_ipv6 (const char *addr, unsigned int prefix)
{
struct in6_aliasreq ifra = { .ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME,
ND6_INFINITE_LIFETIME } };
uint8_t *bytes;
int res = -1;
int fd;
int i;
fd = socket (AF_INET6, SOCK_DGRAM, 0);
if (fd < 0)
goto exit;
strcpy (ifra.ifra_name, tun_name);
ifra.ifra_addr.sin6_len = sizeof (ifra.ifra_addr);
ifra.ifra_addr.sin6_family = AF_INET6;
res = inet_pton (AF_INET6, addr, &ifra.ifra_addr.sin6_addr);
if (!res)
goto exit_close;
ifra.ifra_prefixmask.sin6_len = sizeof (ifra.ifra_prefixmask);
ifra.ifra_prefixmask.sin6_family = AF_INET6;
bytes = (uint8_t *)&ifra.ifra_prefixmask.sin6_addr;
memset (bytes, 0xFF, 16);
bytes[prefix / 8] <<= prefix % 8;
prefix += prefix % 8;
for (i = prefix / 8; i < 16; i++)
bytes[i] = 0;
res = ioctl (fd, SIOCAIFADDR_IN6, &ifra);
exit_close:
close (fd);
exit:
return res;
}
const char *
hev_tunnel_get_name (void)
{
return tun_name;
}
const char *
hev_tunnel_get_index (void)
{
static char tun_index[16];
unsigned int index;
index = if_nametoindex (tun_name);
snprintf (tun_index, sizeof (tun_index) - 1, "%d", index);
return tun_index;
}
int
hev_tunnel_add_task (int fd, HevTask *task)
{
return hev_task_add_fd (task, fd, POLLIN);
}
void
hev_tunnel_del_task (int fd, HevTask *task)
{
hev_task_del_fd (task, fd);
}
#endif /* __FreeBSD__ */

View File

@ -0,0 +1,13 @@
/*
============================================================================
Name : hev-tunnel-freebsd.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2023 - 2025 hev
Description : Tunnel on FreeBSD
============================================================================
*/
#ifndef __HEV_TUNNEL_FREEBSD_H__
#define __HEV_TUNNEL_FREEBSD_H__
#endif /* __HEV_TUNNEL_FREEBSD_H__ */

View File

@ -0,0 +1,213 @@
/*
============================================================================
Name : hev-tunnel-linux.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2023 hev
Description : Tunnel on Linux
============================================================================
*/
#if defined(__linux__)
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <linux/ipv6.h>
#include <linux/if_tun.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include "hev-tunnel.h"
static char tun_name[IFNAMSIZ];
int
hev_tunnel_open (const char *name, int multi_queue)
{
struct ifreq ifr = { 0 };
int res = -1;
int fd;
fd = hev_task_io_open ("/dev/net/tun", O_RDWR);
if (fd < 0)
goto exit;
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
if (multi_queue)
ifr.ifr_flags |= IFF_MULTI_QUEUE;
if (name)
strncpy (ifr.ifr_name, name, IFNAMSIZ - 1);
res = ioctl (fd, TUNSETIFF, (void *)&ifr);
if (res < 0)
goto exit_close;
memcpy (tun_name, ifr.ifr_name, IFNAMSIZ);
return fd;
exit_close:
close (fd);
exit:
return res;
}
void
hev_tunnel_close (int fd)
{
close (fd);
}
int
hev_tunnel_set_mtu (int mtu)
{
int fd, res = -1;
struct ifreq ifr = { .ifr_mtu = mtu };
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
goto exit;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
res = ioctl (fd, SIOCSIFMTU, (void *)&ifr);
close (fd);
exit:
return res;
}
int
hev_tunnel_set_state (int state)
{
struct ifreq ifr = { 0 };
int res = -1;
int fd;
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
goto exit;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
res = ioctl (fd, SIOCGIFFLAGS, (void *)&ifr);
if (res < 0)
goto exit_close;
if (state)
ifr.ifr_flags |= IFF_UP;
else
ifr.ifr_flags &= ~IFF_UP;
res = ioctl (fd, SIOCSIFFLAGS, (void *)&ifr);
exit_close:
close (fd);
exit:
return res;
}
int
hev_tunnel_set_ipv4 (const char *addr, unsigned int prefix)
{
struct ifreq ifr = { 0 };
struct sockaddr_in *pa;
int res = -1;
int fd;
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
goto exit;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
pa = (struct sockaddr_in *)&ifr.ifr_addr;
pa->sin_family = AF_INET;
res = inet_pton (AF_INET, addr, &pa->sin_addr);
if (!res)
goto exit_close;
res = ioctl (fd, SIOCSIFADDR, (void *)&ifr);
if (res < 0)
goto exit_close;
pa = (struct sockaddr_in *)&ifr.ifr_netmask;
pa->sin_family = AF_INET;
pa->sin_addr.s_addr = htonl (((unsigned int)(-1)) << (32 - prefix));
res = ioctl (fd, SIOCSIFNETMASK, (void *)&ifr);
if ((res < 0) && (errno == EEXIST))
res = 0;
exit_close:
close (fd);
exit:
return res;
}
int
hev_tunnel_set_ipv6 (const char *addr, unsigned int prefix)
{
struct in6_ifreq ifr6 = { 0 };
struct ifreq ifr = { 0 };
int res = -1;
int fd;
fd = socket (AF_INET6, SOCK_STREAM, 0);
if (fd < 0)
goto exit;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
res = ioctl (fd, SIOCGIFINDEX, (void *)&ifr);
if (res < 0)
goto exit_close;
ifr6.ifr6_prefixlen = prefix;
ifr6.ifr6_ifindex = ifr.ifr_ifindex;
res = inet_pton (AF_INET6, addr, &ifr6.ifr6_addr);
if (!res)
goto exit_close;
res = ioctl (fd, SIOCSIFADDR, (void *)&ifr6);
if ((res < 0) && (errno == EEXIST))
res = 0;
exit_close:
close (fd);
exit:
return res;
}
const char *
hev_tunnel_get_name (void)
{
return tun_name;
}
const char *
hev_tunnel_get_index (void)
{
static char tun_index[16];
unsigned int index;
index = if_nametoindex (tun_name);
snprintf (tun_index, sizeof (tun_index) - 1, "%d", index);
return tun_index;
}
int
hev_tunnel_add_task (int fd, HevTask *task)
{
return hev_task_add_fd (task, fd, POLLIN);
}
void
hev_tunnel_del_task (int fd, HevTask *task)
{
hev_task_del_fd (task, fd);
}
#endif /* __linux__ */

View File

@ -0,0 +1,13 @@
/*
============================================================================
Name : hev-tunnel-linux.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2023 - 2025 hev
Description : Tunnel on Linux
============================================================================
*/
#ifndef __HEV_TUNNEL_LINUX_H__
#define __HEV_TUNNEL_LINUX_H__
#endif /* __HEV_TUNNEL_LINUX_H__ */

View File

@ -0,0 +1,259 @@
/*
============================================================================
Name : hev-tunnel-macos.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2023 hev
Description : Tunnel on MacOS
============================================================================
*/
#if defined(__APPLE__) || defined(__MACH__)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <TargetConditionals.h>
#if TARGET_OS_OSX
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet6/nd6.h>
#include <net/if_utun.h>
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#endif
#include <hev-task.h>
#include <hev-task-io.h>
#include "hev-tunnel.h"
static char tun_name[IFNAMSIZ];
int
hev_tunnel_open (const char *name, int multi_queue)
{
#if TARGET_OS_OSX
socklen_t len = IFNAMSIZ;
struct sockaddr_ctl sc;
struct ctl_info ci;
int nonblock = 1;
int res = -1;
int fd;
memset (&ci, 0, sizeof (ci));
strncpy (ci.ctl_name, UTUN_CONTROL_NAME, sizeof (ci.ctl_name));
fd = socket (PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
if (fd < 0)
goto exit;
res = ioctl (fd, CTLIOCGINFO, &ci);
if (res < 0)
goto exit_close;
sc.sc_id = ci.ctl_id;
sc.sc_len = sizeof (sc);
sc.sc_family = AF_SYSTEM;
sc.ss_sysaddr = AF_SYS_CONTROL;
sc.sc_unit = 0;
res = sscanf (name, "utun%u", &sc.sc_unit);
if (res > 0)
sc.sc_unit += 1;
res = connect (fd, (struct sockaddr *)&sc, sizeof (sc));
if (res < 0)
goto exit_close;
res = ioctl (fd, FIONBIO, (char *)&nonblock);
if (res < 0)
goto exit_close;
res = getsockopt (fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, tun_name, &len);
if (res < 0)
goto exit_close;
return fd;
exit_close:
close (fd);
exit:
return res;
#else
return 0;
#endif
}
void
hev_tunnel_close (int fd)
{
close (fd);
}
int
hev_tunnel_set_mtu (int mtu)
{
struct ifreq ifr = { .ifr_mtu = mtu };
int res = -1;
int fd;
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
goto exit;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
res = ioctl (fd, SIOCSIFMTU, (void *)&ifr);
close (fd);
exit:
return res;
}
int
hev_tunnel_set_state (int state)
{
struct ifreq ifr = { 0 };
int res = -1;
int fd;
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
goto exit;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
res = ioctl (fd, SIOCGIFFLAGS, (void *)&ifr);
if (res < 0)
goto exit_close;
if (state)
ifr.ifr_flags |= IFF_UP;
else
ifr.ifr_flags &= ~IFF_UP;
res = ioctl (fd, SIOCSIFFLAGS, (void *)&ifr);
exit_close:
close (fd);
exit:
return res;
}
int
hev_tunnel_set_ipv4 (const char *addr, unsigned int prefix)
{
#if TARGET_OS_OSX
struct ifaliasreq ifra = { 0 };
struct sockaddr_in *pa;
int res = -1;
int fd;
fd = socket (AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
goto exit;
strcpy (ifra.ifra_name, tun_name);
pa = (struct sockaddr_in *)&ifra.ifra_addr;
pa->sin_len = sizeof (ifra.ifra_addr);
pa->sin_family = AF_INET;
res = inet_pton (AF_INET, addr, &pa->sin_addr);
if (!res)
goto exit_close;
memcpy (&ifra.ifra_broadaddr, &ifra.ifra_addr, sizeof (ifra.ifra_addr));
pa = (struct sockaddr_in *)&ifra.ifra_mask;
pa->sin_len = sizeof (ifra.ifra_addr);
pa->sin_family = AF_INET;
pa->sin_addr.s_addr = htonl (((unsigned int)(-1)) << (32 - prefix));
res = ioctl (fd, SIOCAIFADDR, &ifra);
exit_close:
close (fd);
exit:
return res;
#else
return 0;
#endif
}
int
hev_tunnel_set_ipv6 (const char *addr, unsigned int prefix)
{
#if TARGET_OS_OSX
struct in6_aliasreq ifra = { .ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME,
ND6_INFINITE_LIFETIME } };
uint8_t *bytes;
int res = -1;
int fd;
int i;
fd = socket (AF_INET6, SOCK_DGRAM, 0);
if (fd < 0)
goto exit;
strcpy (ifra.ifra_name, tun_name);
ifra.ifra_addr.sin6_len = sizeof (ifra.ifra_addr);
ifra.ifra_addr.sin6_family = AF_INET6;
res = inet_pton (AF_INET6, addr, &ifra.ifra_addr.sin6_addr);
if (!res)
goto exit_close;
ifra.ifra_prefixmask.sin6_len = sizeof (ifra.ifra_prefixmask);
ifra.ifra_prefixmask.sin6_family = AF_INET6;
bytes = (uint8_t *)&ifra.ifra_prefixmask.sin6_addr;
memset (bytes, 0xFF, 16);
bytes[prefix / 8] <<= prefix % 8;
prefix += prefix % 8;
for (i = prefix / 8; i < 16; i++)
bytes[i] = 0;
res = ioctl (fd, SIOCAIFADDR_IN6, &ifra);
exit_close:
close (fd);
exit:
return res;
#else
return 0;
#endif
}
const char *
hev_tunnel_get_name (void)
{
return tun_name;
}
const char *
hev_tunnel_get_index (void)
{
static char tun_index[16];
unsigned int index;
index = if_nametoindex (tun_name);
snprintf (tun_index, sizeof (tun_index) - 1, "%d", index);
return tun_index;
}
int
hev_tunnel_add_task (int fd, HevTask *task)
{
return hev_task_add_fd (task, fd, POLLIN);
}
void
hev_tunnel_del_task (int fd, HevTask *task)
{
hev_task_del_fd (task, fd);
}
#endif /* __APPLE__ || __MACH__ */

View File

@ -0,0 +1,76 @@
/*
============================================================================
Name : hev-tunnel-macos.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2023 - 2025 hev
Description : Tunnel on MacOS
============================================================================
*/
#ifndef __HEV_TUNNEL_MACOS_H__
#define __HEV_TUNNEL_MACOS_H__
#include <sys/socket.h>
static inline struct pbuf *
hev_tunnel_read (int fd, int mtu, HevTaskIOYielder yielder, void *yielder_data)
{
struct iovec iov[2];
struct pbuf *buf;
uint32_t type;
ssize_t s;
buf = pbuf_alloc (PBUF_RAW, mtu, PBUF_RAM);
if (!buf)
return NULL;
iov[0].iov_base = &type;
iov[0].iov_len = sizeof (type);
iov[1].iov_base = buf->payload;
iov[1].iov_len = buf->len;
s = hev_task_io_readv (fd, iov, 2, yielder, yielder_data);
if (s <= (ssize_t)sizeof (type)) {
pbuf_free (buf);
return NULL;
}
buf->tot_len = s - sizeof (type);
buf->len = s - sizeof (type);
return buf;
}
static inline ssize_t
hev_tunnel_write (int fd, struct pbuf *buf)
{
struct iovec iov[512];
struct pbuf *p = buf;
uint32_t type = 0;
ssize_t res;
int i;
iov[0].iov_base = &type;
iov[0].iov_len = sizeof (type);
for (i = 1; p && (i < 512); p = p->next) {
iov[i].iov_base = p->payload;
iov[i].iov_len = p->len;
i++;
if (!type && p->len) {
if (((*(uint8_t *)p->payload >> 4) & 0xF) == 4)
type = htonl (AF_INET);
else
type = htonl (AF_INET6);
}
}
res = writev (fd, iov, i);
if (res <= (ssize_t)sizeof (type))
return -1;
return res;
}
#endif /* __HEV_TUNNEL_MACOS_H__ */

View File

@ -0,0 +1,225 @@
/*
============================================================================
Name : hev-tunnel-netbsd.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2025 hev
Description : Tunnel on NetBSD
============================================================================
*/
#if defined(__NetBSD__)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if_tun.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet6/nd6.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include "hev-tunnel.h"
static char tun_name[IFNAMSIZ];
int
hev_tunnel_open (const char *name, int multi_queue)
{
char buf[256];
int one = 1;
int res;
int fd;
snprintf (buf, sizeof (buf), "/dev/%s", name);
fd = hev_task_io_open (buf, O_RDWR);
if (fd < 0)
goto fail;
res = ioctl (fd, FIONBIO, (char *)&one);
if (res < 0)
goto fail_close;
strncpy (tun_name, name, IFNAMSIZ - 1);
return fd;
fail_close:
close (fd);
fail:
return -1;
}
void
hev_tunnel_close (int fd)
{
struct ifreq ifr = { 0 };
close (fd);
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
return;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
ioctl (fd, SIOCIFDESTROY, (void *)&ifr);
close (fd);
}
int
hev_tunnel_set_mtu (int mtu)
{
int fd, res = -1;
struct ifreq ifr = { .ifr_mtu = mtu };
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
goto exit;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
res = ioctl (fd, SIOCSIFMTU, (void *)&ifr);
close (fd);
exit:
return res;
}
int
hev_tunnel_set_state (int state)
{
struct ifreq ifr = { 0 };
int res = -1;
int fd;
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
goto exit;
memcpy (ifr.ifr_name, tun_name, IFNAMSIZ);
res = ioctl (fd, SIOCGIFFLAGS, (void *)&ifr);
if (res < 0)
goto exit_close;
if (state)
ifr.ifr_flags |= IFF_UP;
else
ifr.ifr_flags &= ~IFF_UP;
res = ioctl (fd, SIOCSIFFLAGS, (void *)&ifr);
exit_close:
close (fd);
exit:
return res;
}
int
hev_tunnel_set_ipv4 (const char *addr, unsigned int prefix)
{
struct ifaliasreq ifra = { 0 };
struct sockaddr_in *pa;
int res = -1;
int fd;
fd = socket (AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
goto exit;
strcpy (ifra.ifra_name, tun_name);
pa = (struct sockaddr_in *)&ifra.ifra_addr;
pa->sin_len = sizeof (ifra.ifra_addr);
pa->sin_family = AF_INET;
res = inet_pton (AF_INET, addr, &pa->sin_addr);
if (!res)
goto exit_close;
memcpy (&ifra.ifra_broadaddr, &ifra.ifra_addr, sizeof (ifra.ifra_addr));
pa = (struct sockaddr_in *)&ifra.ifra_mask;
pa->sin_len = sizeof (ifra.ifra_addr);
pa->sin_family = AF_INET;
pa->sin_addr.s_addr = htonl (((unsigned int)(-1)) << (32 - prefix));
res = ioctl (fd, SIOCAIFADDR, &ifra);
exit_close:
close (fd);
exit:
return res;
}
int
hev_tunnel_set_ipv6 (const char *addr, unsigned int prefix)
{
struct in6_aliasreq ifra = { .ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME,
ND6_INFINITE_LIFETIME } };
uint8_t *bytes;
int res = -1;
int fd;
int i;
fd = socket (AF_INET6, SOCK_DGRAM, 0);
if (fd < 0)
goto exit;
strcpy (ifra.ifra_name, tun_name);
ifra.ifra_addr.sin6_len = sizeof (ifra.ifra_addr);
ifra.ifra_addr.sin6_family = AF_INET6;
res = inet_pton (AF_INET6, addr, &ifra.ifra_addr.sin6_addr);
if (!res)
goto exit_close;
ifra.ifra_prefixmask.sin6_len = sizeof (ifra.ifra_prefixmask);
ifra.ifra_prefixmask.sin6_family = AF_INET6;
bytes = (uint8_t *)&ifra.ifra_prefixmask.sin6_addr;
memset (bytes, 0xFF, 16);
bytes[prefix / 8] <<= prefix % 8;
prefix += prefix % 8;
for (i = prefix / 8; i < 16; i++)
bytes[i] = 0;
res = ioctl (fd, SIOCAIFADDR_IN6, &ifra);
exit_close:
close (fd);
exit:
return res;
}
const char *
hev_tunnel_get_name (void)
{
return tun_name;
}
const char *
hev_tunnel_get_index (void)
{
static char tun_index[16];
unsigned int index;
index = if_nametoindex (tun_name);
snprintf (tun_index, sizeof (tun_index) - 1, "%d", index);
return tun_index;
}
int
hev_tunnel_add_task (int fd, HevTask *task)
{
return hev_task_add_fd (task, fd, POLLIN);
}
void
hev_tunnel_del_task (int fd, HevTask *task)
{
hev_task_del_fd (task, fd);
}
#endif /* __NetBSD__ */

View File

@ -0,0 +1,13 @@
/*
============================================================================
Name : hev-tunnel-netbsd.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2025 hev
Description : Tunnel on NetBSD
============================================================================
*/
#ifndef __HEV_TUNNEL_NETBSD_H__
#define __HEV_TUNNEL_NETBSD_H__
#endif /* __HEV_TUNNEL_NETBSD_H__ */

View File

@ -0,0 +1,214 @@
/*
============================================================================
Name : hev-tunnel-windows.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2025 hev
Description : Tunnel on Windows
============================================================================
*/
#if defined(__MSYS__)
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <hev-task.h>
#include <hev-task-io.h>
#include <hev-memory-allocator.h>
#include "hev-wintun.h"
#include "hev-tunnel.h"
typedef struct _HevPBuf HevPBuf;
struct _HevPBuf
{
struct pbuf_custom base;
void *mem;
};
static HevWinTun *wintun;
static HevWinTunAdapter *adapter;
static HevWinTunSession *session;
static char tun_name[IFNAMSIZ];
static void
hev_pbuf_free (struct pbuf *p)
{
HevPBuf *buf = (HevPBuf *)p;
hev_wintun_session_release (session, buf->mem);
hev_free (buf);
}
int
hev_tunnel_open (const char *name, int multi_queue)
{
wintun = hev_wintun_open ();
if (!wintun)
return -1;
adapter = hev_wintun_adapter_create (name);
if (!adapter)
goto free;
strncpy (tun_name, name, IFNAMSIZ - 1);
return 0;
free:
hev_wintun_close (wintun);
return -1;
}
void
hev_tunnel_close (int fd)
{
if (session)
hev_wintun_session_stop (session);
hev_wintun_adapter_close (adapter);
hev_wintun_close (wintun);
}
int
hev_tunnel_set_mtu (int mtu)
{
return hev_wintun_adapter_set_mtu (adapter, mtu);
}
int
hev_tunnel_set_state (int state)
{
if (state) {
session = hev_wintun_session_start (adapter);
if (!session)
return -1;
} else {
if (session) {
hev_wintun_session_stop (session);
session = NULL;
}
}
return 0;
}
int
hev_tunnel_set_ipv4 (const char *addr, unsigned int prefix)
{
char ipv4[4];
int res;
res = inet_pton (AF_INET, addr, ipv4);
if (!res)
return -1;
return hev_wintun_adapter_set_ipv4 (adapter, ipv4, prefix);
}
int
hev_tunnel_set_ipv6 (const char *addr, unsigned int prefix)
{
char ipv6[16];
int res;
res = inet_pton (AF_INET6, addr, ipv6);
if (!res)
return -1;
return hev_wintun_adapter_set_ipv6 (adapter, ipv6, prefix);
}
const char *
hev_tunnel_get_name (void)
{
return tun_name;
}
const char *
hev_tunnel_get_index (void)
{
static char tun_index[16];
int index;
index = hev_wintun_adapter_get_index (adapter);
if (index < 0)
return NULL;
snprintf (tun_index, sizeof (tun_index) - 1, "%d", index);
return tun_index;
}
int
hev_tunnel_add_task (int fd, HevTask *task)
{
void *handle = hev_wintun_session_get_read_wait_event (session);
return hev_task_add_whandle (task, handle);
}
void
hev_tunnel_del_task (int fd, HevTask *task)
{
void *handle = hev_wintun_session_get_read_wait_event (session);
hev_task_del_whandle (task, handle);
}
struct pbuf *
hev_tunnel_read (int fd, int mtu, HevTaskIOYielder yielder, void *yielder_data)
{
HevPBuf *buf;
void *packet;
int spin = 0;
int size;
retry:
packet = hev_wintun_session_receive (session, &size);
if (!packet) {
if (hev_wintun_get_last_error () == HEV_WINTUN_EAGAIN) {
if (spin++ < 1000) {
hev_task_yield (HEV_TASK_YIELD);
} else if (yielder) {
if (yielder (HEV_TASK_WAITIO, yielder_data))
return NULL;
} else {
hev_task_yield (HEV_TASK_WAITIO);
}
goto retry;
} else {
return NULL;
}
}
buf = hev_malloc (sizeof (HevPBuf));
if (!buf)
return NULL;
buf->mem = packet;
buf->base.custom_free_function = hev_pbuf_free;
pbuf_alloced_custom (PBUF_RAW, size, PBUF_RAM, &buf->base, packet, size);
return &buf->base.pbuf;
}
ssize_t
hev_tunnel_write (int fd, struct pbuf *buf)
{
struct pbuf *p = buf;
void *packet;
size_t size;
packet = hev_wintun_session_allocate (session, p->tot_len);
if (!packet)
return -1;
for (size = 0; p; p = p->next) {
memcpy (packet + size, p->payload, p->len);
size += p->len;
}
hev_wintun_session_send (session, packet);
return size;
}
#endif /* __MSYS__ */

View File

@ -0,0 +1,17 @@
/*
============================================================================
Name : hev-tunnel-windows.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2025 hev
Description : Tunnel on Windows
============================================================================
*/
#ifndef __HEV_TUNNEL_WINDOWS_H__
#define __HEV_TUNNEL_WINDOWS_H__
struct pbuf *hev_tunnel_read (int fd, int mtu, HevTaskIOYielder yielder,
void *yielder_data);
ssize_t hev_tunnel_write (int fd, struct pbuf *buf);
#endif /* __HEV_TUNNEL_WINDOWS_H__ */

View File

@ -0,0 +1,93 @@
/*
============================================================================
Name : hev-tunnel.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2023 - 2025 hev
Description : Tunnel
============================================================================
*/
#ifndef __HEV_TUNNEL_H__
#define __HEV_TUNNEL_H__
#include <lwip/pbuf.h>
#if defined(__linux__)
#include "hev-tunnel-linux.h"
#endif /* __linux__ */
#if defined(__FreeBSD__)
#include "hev-tunnel-freebsd.h"
#endif /* __FreeBSD__ */
#if defined(__NetBSD__)
#include "hev-tunnel-netbsd.h"
#endif /* __NetBSD__ */
#if defined(__APPLE__) || defined(__MACH__)
#include "hev-tunnel-macos.h"
#endif /* __APPLE__ || __MACH__ */
#if defined(__MSYS__)
#include "hev-tunnel-windows.h"
#endif /* __MSYS__ */
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
static inline struct pbuf *
hev_tunnel_read (int fd, int mtu, HevTaskIOYielder yielder, void *yielder_data)
{
struct pbuf *buf;
ssize_t s;
buf = pbuf_alloc (PBUF_RAW, mtu, PBUF_RAM);
if (!buf)
return NULL;
s = hev_task_io_read (fd, buf->payload, buf->len, yielder, yielder_data);
if (s <= 0) {
pbuf_free (buf);
return NULL;
}
buf->tot_len = s;
buf->len = s;
return buf;
}
static inline ssize_t
hev_tunnel_write (int fd, struct pbuf *buf)
{
struct iovec iov[512];
struct pbuf *p = buf;
int i;
if (!p->next)
return write (fd, p->payload, p->len);
for (i = 0; p && (i < 512); p = p->next) {
iov[i].iov_base = p->payload;
iov[i].iov_len = p->len;
i++;
}
return writev (fd, iov, i);
}
#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) */
int hev_tunnel_open (const char *name, int multi_queue);
void hev_tunnel_close (int fd);
int hev_tunnel_set_mtu (int mtu);
int hev_tunnel_set_state (int state);
int hev_tunnel_set_ipv4 (const char *addr, unsigned int prefix);
int hev_tunnel_set_ipv6 (const char *addr, unsigned int prefix);
const char *hev_tunnel_get_name (void);
const char *hev_tunnel_get_index (void);
int hev_tunnel_add_task (int fd, HevTask *task);
void hev_tunnel_del_task (int fd, HevTask *task);
#endif /* __HEV_TUNNEL_H__ */

View File

@ -0,0 +1,106 @@
/*
============================================================================
Name : hev-compiler.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2019 - 2023 hev
Description : Compiler
============================================================================
*/
#ifndef __HEV_COMPILER_H__
#define __HEV_COMPILER_H__
#ifdef __cplusplus
extern "C" {
#endif
#define ALIGN_UP(addr, align) \
((addr + (typeof (addr))align - 1) & ~((typeof (addr))align - 1))
#define ALIGN_DOWN(addr, align) ((addr) & ~((typeof (addr))align - 1))
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0]))
#endif
#ifndef EXPORT_SYMBOL
#define EXPORT_SYMBOL __attribute__ ((visibility ("default")))
#endif
#define barrier() __asm__ __volatile__ ("" : : : "memory")
static inline void
__read_once_size (void *dst, const volatile void *src, int size)
{
switch (size) {
case sizeof (char):
*(char *)dst = *(volatile char *)src;
break;
case sizeof (short):
*(short *)dst = *(volatile short *)src;
break;
case sizeof (int):
*(int *)dst = *(volatile int *)src;
break;
default:
barrier ();
__builtin_memcpy ((void *)dst, (const void *)src, size);
barrier ();
}
}
static inline void
__write_once_size (volatile void *dst, const void *src, int size)
{
switch (size) {
case sizeof (char):
*(volatile char *)dst = *(char *)src;
break;
case sizeof (short):
*(volatile short *)dst = *(short *)src;
break;
case sizeof (int):
*(volatile int *)dst = *(int *)src;
break;
default:
barrier ();
__builtin_memcpy ((void *)dst, (const void *)src, size);
barrier ();
}
}
#define READ_ONCE(x) \
({ \
union \
{ \
typeof (x) __val; \
char __c[1]; \
} __u; \
__read_once_size (__u.__c, &(x), sizeof (x)); \
__u.__val; \
})
#define WRITE_ONCE(x, val) \
({ \
union \
{ \
typeof (x) __val; \
char __c[1]; \
} __u = { .__val = (typeof (x))(val) }; \
__write_once_size (&(x), __u.__c, sizeof (x)); \
__u.__val; \
})
#ifndef container_of
#define container_of(ptr, type, member) \
({ \
void *__mptr = (void *)(ptr); \
((type *)(__mptr - offsetof (type, member))); \
})
#endif
#ifdef __cplusplus
}
#endif
#endif /* __HEV_COMPILER_H__ */

View File

@ -0,0 +1,61 @@
/*
============================================================================
Name : hev-exec.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2023 hev
Description : Exec
============================================================================
*/
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#if defined(__APPLE__)
#include <Availability.h>
#include <AvailabilityMacros.h>
#include <TargetConditionals.h>
#endif
#include "hev-logger.h"
#include "hev-exec.h"
#if TARGET_OS_TV
void
hev_exec_run (const char *script_path, const char *tun_name,
const char *tun_index, int wait)
{
}
#else
static void
signal_handler (int signum)
{
waitpid (-1, NULL, WNOHANG);
}
void
hev_exec_run (const char *script_path, const char *tun_name,
const char *tun_index, int wait)
{
pid_t pid;
signal (SIGCHLD, signal_handler);
pid = fork ();
if (pid < 0) {
LOG_E ("exec fork");
return;
} else if (pid != 0) {
if (wait)
waitpid (pid, NULL, 0);
return;
}
execl (script_path, script_path, tun_name, tun_index, NULL);
LOG_E ("exec %s %s %s", script_path, tun_name, tun_index);
exit (-1);
}
#endif

View File

@ -0,0 +1,16 @@
/*
============================================================================
Name : hev-exec.h
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2023 - 2025 hev
Description : Exec
============================================================================
*/
#ifndef __HEV_EXEC_H__
#define __HEV_EXEC_H__
void hev_exec_run (const char *script_path, const char *tun_name,
const char *tun_index, int wait);
#endif /* __HEV_EXEC_H__ */

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