Update On Tue Aug 5 20:45:24 CEST 2025

This commit is contained in:
github-action[bot]
2025-08-05 20:45:24 +02:00
parent e00e98bbc5
commit 10e3133b5c
601 changed files with 149082 additions and 504 deletions
+1
View File
@@ -1080,3 +1080,4 @@ Update On Fri Aug 1 20:44:14 CEST 2025
Update On Sat Aug 2 20:40:30 CEST 2025
Update On Sun Aug 3 20:40:33 CEST 2025
Update On Mon Aug 4 20:43:42 CEST 2025
Update On Tue Aug 5 20:45:16 CEST 2025
@@ -16,9 +16,9 @@
"@emotion/styled": "11.14.1",
"@juggle/resize-observer": "3.4.0",
"@material/material-color-utilities": "0.3.0",
"@mui/icons-material": "7.2.0",
"@mui/lab": "7.0.0-beta.14",
"@mui/material": "7.2.0",
"@mui/icons-material": "7.3.0",
"@mui/lab": "7.0.0-beta.15",
"@mui/material": "7.3.0",
"@mui/x-date-pickers": "8.9.2",
"@nyanpasu/interface": "workspace:^",
"@nyanpasu/ui": "workspace:^",
@@ -33,7 +33,7 @@
"dayjs": "1.11.13",
"framer-motion": "12.23.12",
"i18next": "25.3.2",
"jotai": "2.12.5",
"jotai": "2.13.0",
"json-schema": "0.4.0",
"material-react-table": "3.2.1",
"monaco-editor": "0.52.2",
@@ -56,7 +56,7 @@
"@csstools/normalize.css": "12.1.1",
"@emotion/babel-plugin": "11.13.5",
"@emotion/react": "11.14.0",
"@iconify/json": "2.2.365",
"@iconify/json": "2.2.366",
"@monaco-editor/react": "4.7.0",
"@tanstack/react-query": "5.84.1",
"@tanstack/react-router": "1.130.12",
@@ -78,7 +78,7 @@
"@vitejs/plugin-react-swc": "3.11.0",
"change-case": "5.4.4",
"clsx": "2.1.1",
"core-js": "3.44.0",
"core-js": "3.45.0",
"filesize": "11.0.2",
"meta-json-schema": "1.19.12",
"monaco-yaml": "5.4.0",
+3 -3
View File
@@ -12,9 +12,9 @@
},
"dependencies": {
"@material/material-color-utilities": "0.3.0",
"@mui/icons-material": "7.2.0",
"@mui/lab": "7.0.0-beta.14",
"@mui/material": "7.2.0",
"@mui/icons-material": "7.3.0",
"@mui/lab": "7.0.0-beta.15",
"@mui/material": "7.3.0",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-scroll-area": "1.2.9",
"@tauri-apps/api": "2.6.0",
+3 -3
View File
@@ -67,8 +67,8 @@
"@types/fs-extra": "11.0.4",
"@types/lodash-es": "4.17.12",
"@types/node": "22.17.0",
"@typescript-eslint/eslint-plugin": "8.38.0",
"@typescript-eslint/parser": "8.38.0",
"@typescript-eslint/eslint-plugin": "8.39.0",
"@typescript-eslint/parser": "8.39.0",
"autoprefixer": "10.4.21",
"conventional-changelog-conventionalcommits": "9.1.0",
"cross-env": "10.0.0",
@@ -107,7 +107,7 @@
"tailwindcss": "4.1.11",
"tsx": "4.20.3",
"typescript": "5.9.2",
"typescript-eslint": "8.38.0"
"typescript-eslint": "8.39.0"
},
"packageManager": "pnpm@10.14.0",
"engines": {
+263 -332
View File
File diff suppressed because it is too large Load Diff
+73
View File
@@ -0,0 +1,73 @@
#
# Copyright (C) 2020-2024 Tony Ambardar <itugrok@yahoo.com>
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=libbpf
PKG_VERSION:=1.5.0
PKG_RELEASE:=1
PKG_SOURCE_URL:=https://github.com/libbpf/libbpf
PKG_MIRROR_HASH:=ef379723b73175cd1659c5f71cfc5df4e7cb6788d0eb0c8bc5829b3e3997c981
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
PKG_ABI_VERSION:=$(firstword $(subst .,$(space),$(PKG_VERSION)))
PKG_MAINTAINER:=Tony Ambardar <itugrok@yahoo.com>
PKG_CPE_ID:=cpe:/a:libbpf_project:libbpf
PKG_USE_MIPS16:=0
PKG_BUILD_FLAGS:=no-mips16 no-gc-sections no-lto
PKG_BUILD_PARALLEL:=1
PKG_INSTALL:=1
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/nls.mk
define Package/libbpf
SECTION:=libs
CATEGORY:=Libraries
TITLE:=libbpf - eBPF helper library
LICENSE:=LGPL-2.1 OR BSD-2-Clause
ABI_VERSION:=$(PKG_ABI_VERSION)
URL:=http://www.kernel.org
DEPENDS:=+libelf
endef
define Package/libbpf/description
libbpf is a library for loading eBPF programs and reading and manipulating eBPF objects from user-space.
endef
MAKE_FLAGS += \
$(if $(findstring c,$(OPENWRT_VERBOSE)),V=1,V='') \
LIBSUBDIR=lib
MAKE_PATH = src
define Build/InstallDev/libbpf
$(INSTALL_DIR) $(1)/usr/include/bpf
$(CP) $(PKG_INSTALL_DIR)/usr/include/bpf/*.h $(1)/usr/include/bpf/
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libbpf.{a,so*} \
$(1)/usr/lib/
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/libbpf.pc \
$(1)/usr/lib/pkgconfig/
$(SED) 's,/usr/include,$$$${prefix}/include,g' \
$(1)/usr/lib/pkgconfig/libbpf.pc
$(SED) 's,/usr/lib,$$$${exec_prefix}/lib,g' \
$(1)/usr/lib/pkgconfig/libbpf.pc
endef
Build/InstallDev=$(Build/InstallDev/libbpf)
define Package/libbpf/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libbpf.so.* $(1)/usr/lib/
endef
$(eval $(call BuildPackage,libbpf))
@@ -0,0 +1,54 @@
--- a/src/libbpf.h
+++ b/src/libbpf.h
@@ -1291,9 +1291,10 @@ struct bpf_tc_opts {
__u32 prog_id;
__u32 handle;
__u32 priority;
+ __u32 classid;
size_t :0;
};
-#define bpf_tc_opts__last_field priority
+#define bpf_tc_opts__last_field classid
LIBBPF_API int bpf_tc_hook_create(struct bpf_tc_hook *hook);
LIBBPF_API int bpf_tc_hook_destroy(struct bpf_tc_hook *hook);
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -673,6 +673,8 @@ static int __get_tc_info(void *cookie, s
OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID]));
OPTS_SET(info->opts, handle, tc->tcm_handle);
OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16);
+ if (tbb[TCA_BPF_CLASSID])
+ OPTS_SET(info->opts, classid, libbpf_nla_getattr_u32(tbb[TCA_BPF_CLASSID]));
info->processed = true;
return unicast ? NL_NEXT : NL_DONE;
@@ -717,7 +719,7 @@ static int tc_add_fd_and_name(struct lib
int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
{
- __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
+ __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags, classid;
int ret, ifindex, attach_point, prog_fd;
struct bpf_cb_ctx info = {};
struct libbpf_nla_req req;
@@ -737,6 +739,7 @@ int bpf_tc_attach(const struct bpf_tc_ho
prog_fd = OPTS_GET(opts, prog_fd, 0);
prog_id = OPTS_GET(opts, prog_id, 0);
flags = OPTS_GET(opts, flags, 0);
+ classid = OPTS_GET(opts, classid, 0);
if (ifindex <= 0 || !prog_fd || prog_id)
return libbpf_err(-EINVAL);
@@ -776,6 +779,11 @@ int bpf_tc_attach(const struct bpf_tc_ho
ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags));
if (ret < 0)
return libbpf_err(ret);
+ if (classid) {
+ ret = nlattr_add(&req, TCA_BPF_CLASSID, &classid, sizeof(classid));
+ if (ret < 0)
+ return libbpf_err(ret);
+ }
nlattr_end_nested(&req, nla);
info.opts = opts;
@@ -1,5 +1,5 @@
#
# Copyright (C) 2020 Tony Ambardar <itugrok@yahoo.com>
# Copyright (C) 2020-2024 Tony Ambardar <itugrok@yahoo.com>
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
@@ -7,24 +7,27 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=bpftools
PKG_NAME:=bpftool
PKG_VERSION:=7.5.0
PKG_RELEASE:=1
PKG_SOURCE_URL:=https://github.com/libbpf/bpftool
PKG_MIRROR_HASH:=d2d55ff9cc11490b251105c9e376cbfaa9bcd26d224af779fd58cdcaa8f70ccf
PKG_SOURCE_PROTO:=git
PKG_SOURCE_DATE:=2022-03-08
PKG_SOURCE_VERSION:=04c465fd1f561f67796dc68bbfe1aa7cfa956c3c
PKG_MIRROR_HASH:=e22a954cd186f43228a96586bbdc120b11e6c87360ab88ae96ba37afb9c7cb58
PKG_ABI_VERSION:=$(call abi_version_str,$(PKG_SOURCE_DATE))
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
PKG_MAINTAINER:=Tony Ambardar <itugrok@yahoo.com>
PKG_USE_MIPS16:=0
PKG_BUILD_FLAGS:=no-mips16 gc-sections lto
PKG_BUILD_PARALLEL:=1
PKG_INSTALL:=1
HOST_BUILD_PARALLEL:=1
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/nls.mk
include $(INCLUDE_DIR)/host-build.mk
define Package/bpftool/Default
SECTION:=net
@@ -63,75 +66,25 @@ define Package/bpftool-full/description
eBPF programs and jited code.
endef
define Package/libbpf
SECTION:=libs
CATEGORY:=Libraries
TITLE:=libbpf - eBPF helper library
VARIANT:=lib
LICENSE:=LGPL-2.1 OR BSD-2-Clause
ABI_VERSION:=$(PKG_ABI_VERSION)
URL:=http://www.kernel.org
DEPENDS:=+libelf
endef
define Package/libbpf/description
libbpf is a library for loading eBPF programs and reading and manipulating eBPF objects from user-space.
endef
# LTO not compatible with DSO using PIC
ifneq ($(BUILD_VARIANT),lib)
TARGET_CFLAGS += -ffunction-sections -fdata-sections -flto
TARGET_LDFLAGS += -Wl,--gc-sections
endif
ifeq ($(BUILD_VARIANT),full)
full:=1
else
full:=0
endif
MAKE_VARS = \
EXTRA_CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS)" \
LDFLAGS="$(TARGET_LDFLAGS)"
MAKE_FLAGS += \
OUTPUT="$(PKG_BUILD_DIR)/" \
prefix="/usr" \
$(if $(findstring c,$(OPENWRT_VERBOSE)),V=1,V='') \
LIBSUBDIR=lib \
check_feat=0 \
feature-clang-bpf-co-re=0 \
feature-reallocarray=1 \
feature-zlib=1 \
feature-libbfd=$(full) \
feature-llvm=0 \
feature-libcap=0 \
feature-disassembler-four-args=$(full)
feature-disassembler-four-args=1 \
feature-disassembler-init-styled=1
ifeq ($(BUILD_VARIANT),lib)
MAKE_PATH = libbpf/src
else
MAKE_PATH = src
endif
define Build/InstallDev/libbpf
$(INSTALL_DIR) $(1)/usr/include/bpf
$(CP) $(PKG_INSTALL_DIR)/usr/include/bpf/*.h $(1)/usr/include/bpf/
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libbpf.{a,so*} \
$(1)/usr/lib/
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/libbpf.pc \
$(1)/usr/lib/pkgconfig/
$(SED) 's,/usr/include,$$$${prefix}/include,g' \
$(1)/usr/lib/pkgconfig/libbpf.pc
$(SED) 's,/usr/lib,$$$${exec_prefix}/lib,g' \
$(1)/usr/lib/pkgconfig/libbpf.pc
endef
ifeq ($(BUILD_VARIANT),lib)
Build/InstallDev=$(Build/InstallDev/libbpf)
endif
MAKE_PATH = src
define Package/bpftool-$(BUILD_VARIANT)/install
$(INSTALL_DIR) $(1)/usr/libexec
@@ -139,11 +92,30 @@ define Package/bpftool-$(BUILD_VARIANT)/install
$(1)/usr/libexec/bpftool-$(BUILD_VARIANT)
endef
define Package/libbpf/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libbpf.so.* $(1)/usr/lib/
HOST_MAKE_FLAGS += \
OUTPUT="$(HOST_BUILD_DIR)/" \
prefix="/usr" \
$(if $(findstring c,$(OPENWRT_VERBOSE)),V=1,V='') \
check_feat=0 \
feature-clang-bpf-co-re=0 \
feature-libbfd=0 \
feature-llvm=0 \
feature-libcap=0 \
feature-disassembler-four-args=1 \
feature-disassembler-init-styled=1
HOST_MAKE_PATH = src
define Host/Install
$(INSTALL_DIR) $(STAGING_DIR_HOST)/usr/sbin
$(INSTALL_BIN) $(HOST_BUILD_DIR)/bpftool \
$(STAGING_DIR_HOST)/usr/sbin/bpftool
endef
define Host/Clean
rm -f $(STAGING_DIR_HOST)/usr/sbin/bpftool
endef
$(eval $(call BuildPackage,libbpf))
$(eval $(call BuildPackage,bpftool-full))
$(eval $(call BuildPackage,bpftool-minimal))
$(eval $(call HostBuild))
@@ -1,20 +1,9 @@
--- a/libbpf/include/linux/list.h
+++ b/libbpf/include/linux/list.h
@@ -3,6 +3,8 @@
#ifndef __LINUX_LIST_H
#define __LINUX_LIST_H
+#include <linux/types.h>
+
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
--- a/src/Makefile
+++ b/src/Makefile
@@ -73,10 +73,10 @@ CFLAGS += -W -Wall -Wextra -Wno-unused-p
CFLAGS += $(filter-out -Wswitch-enum -Wnested-externs,$(EXTRA_WARNINGS))
CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ \
-I$(if $(OUTPUT),$(OUTPUT),.) \
-I$(or $(OUTPUT),.) \
- -I$(LIBBPF_INCLUDE) \
-I$(srctree)/src/kernel/bpf/ \
-I$(srctree)/include \
@@ -1,10 +0,0 @@
--- a/libbpf/src/Makefile
+++ b/libbpf/src/Makefile
@@ -25,6 +25,7 @@ ALL_CFLAGS := $(INCLUDES)
SHARED_CFLAGS += -fPIC -fvisibility=hidden -DSHARED
+CFLAGS = $(EXTRA_CFLAGS)
CFLAGS ?= -g -O2 -Werror -Wall -std=gnu89
ALL_CFLAGS += $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
ALL_LDFLAGS += $(LDFLAGS)
+1 -1
View File
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=25.8.5
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_CONFIG_DEPENDS:= \
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
@@ -1580,6 +1580,9 @@ local hysteria2_type = map:get("@global_subscribe[0]", "hysteria2_type") or "sin
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
}
}
if ((!queryParam.security || queryParam.security == "") && queryParam.sni && queryParam.sni != "") {
queryParam.security = "tls";
}
if (queryParam.security) {
if (queryParam.security == "tls") {
opt.set(dom_prefix + 'tls', true);
@@ -1751,7 +1751,7 @@ msgid "Fragmentation interval (ms)"
msgstr "分片间隔(ms"
msgid "Split handshake data into multiple TLS records for better censorship evasion. Low overhead. Recommended to enable first."
msgstr 将握手数据拆分为多个 TLS 记录,提升抗封锁能力,几乎不增加延迟,建议优先启用。"
msgstr "将握手数据拆分为多个 TLS 记录,提升抗封锁能力,几乎不增加延迟,建议优先启用。"
msgid "Split handshake into multiple TCP segments. Enhances obfuscation. May increase delay. Use only if needed."
msgstr "将 TLS 握手数据分为多个 TCP 包发送,提高伪装性,可能增加延迟,仅在封锁严重时使用。"
@@ -280,7 +280,7 @@ do
if node.balancing_node then
for k, node in pairs(node.balancing_node) do
currentNodes[#currentNodes + 1] = {
log = false,
log = true,
node = node,
currentNode = node and uci:get_all(appname, node) or nil,
remarks = node,
@@ -328,7 +328,7 @@ do
if node.urltest_node then
for k, node in pairs(node.urltest_node) do
currentNodes[#currentNodes + 1] = {
log = false,
log = true,
node = node,
currentNode = node and uci:get_all(appname, node) or nil,
remarks = node,
@@ -1452,9 +1452,12 @@ local function processData(szType, content, add_mode, add_from)
result.address = host_port
end
result.tls = "0"
if (not params.security or params.security == "") and params.sni and params.sni ~= "" then
params.security = "tls"
end
if params.security == "tls" or params.security == "reality" then
result.tls = "1"
result.tls_serverName = (params.sni and params.sni ~= "") and params.sni or params.host
result.tls_serverName = params.sni
result.alpn = params.alpn
if params.fp and params.fp ~= "" then
result.utls = "1"
@@ -1566,7 +1569,9 @@ local function select_node(nodes, config, parentConfig)
if config.currentNode[".name"] then
for index, node in pairs(nodes) do
if node[".name"] == config.currentNode[".name"] then
log('更新【' .. config.remarks .. '】匹配节点:' .. node.remarks)
if config.log == nil or config.log == true then
log('更新【' .. config.remarks .. '】匹配节点:' .. node.remarks)
end
server = node[".name"]
break
end
@@ -1734,6 +1739,9 @@ local function update_node(manual)
for _, config in pairs(CONFIG) do
if config.currentNodes and #config.currentNodes > 0 then
if config.remarks and config.currentNodes[1].log ~= false then
log('----【' .. config.remarks .. '】----')
end
for kk, vv in pairs(config.currentNodes) do
select_node(nodes, vv, config)
end
+10 -10
View File
@@ -1024,14 +1024,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
name = "etherparse"
version = "0.18.2"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a48e7bdc36cdf86876a6890c0840957029374ea07f6697c96fa15898730375"
checksum = "b119b9796ff800751a220394b8b3613f26dd30c48f254f6837e64c464872d1c7"
dependencies = [
"arrayvec",
]
@@ -1977,7 +1977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
dependencies = [
"cfg-if",
"windows-targets 0.48.5",
"windows-targets 0.53.2",
]
[[package]]
@@ -2741,7 +2741,7 @@ dependencies = [
"once_cell",
"socket2 0.5.10",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -3062,7 +3062,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -3744,7 +3744,7 @@ dependencies = [
"getrandom 0.3.3",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -3876,9 +3876,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.47.0"
version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35"
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [
"backtrace",
"bytes",
@@ -4384,7 +4384,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -177,7 +177,7 @@ flate2 = { version = "1.0", optional = true }
brotli = { version = "8.0", optional = true }
zstd = { version = "0.13", optional = true }
etherparse = { version = "0.18", optional = true }
etherparse = { version = "0.19", optional = true }
smoltcp = { version = "0.12", optional = true, default-features = false, features = [
"std",
"log",
@@ -112,6 +112,8 @@ func buildAndroid() {
args = append(args, debugFlags...)
}
args = append(args, "-ldflags", "-checklinkname=0")
tags := append(sharedTags, memcTags...)
if debugEnabled {
tags = append(tags, debugTags...)
@@ -0,0 +1,19 @@
package libbox
import (
"os"
_ "unsafe"
)
// https://github.com/SagerNet/sing-box/issues/3233
// https://github.com/golang/go/issues/70508
// https://github.com/tailscale/tailscale/issues/13452
//go:linkname checkPidfdOnce os.checkPidfdOnce
var checkPidfdOnce func() error
func init() {
checkPidfdOnce = func() error {
return os.ErrInvalid
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=25.8.5
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_CONFIG_DEPENDS:= \
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
+1 -1
View File
@@ -1751,7 +1751,7 @@ msgid "Fragmentation interval (ms)"
msgstr "分片间隔(ms"
msgid "Split handshake data into multiple TLS records for better censorship evasion. Low overhead. Recommended to enable first."
msgstr 将握手数据拆分为多个 TLS 记录,提升抗封锁能力,几乎不增加延迟,建议优先启用。"
msgstr "将握手数据拆分为多个 TLS 记录,提升抗封锁能力,几乎不增加延迟,建议优先启用。"
msgid "Split handshake into multiple TCP segments. Enhances obfuscation. May increase delay. Use only if needed."
msgstr "将 TLS 握手数据分为多个 TCP 包发送,提高伪装性,可能增加延迟,仅在封锁严重时使用。"
+1 -1
View File
@@ -9,7 +9,7 @@ PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/MetaCubeX/mihomo.git
PKG_SOURCE_VERSION:=v1.19.12
PKG_MIRROR_HASH:=de065d6eb33b84b660d3ecaaee3359c991307ab42345c70e82c5ce65c1ae329e
PKG_MIRROR_HASH:=9ca38753d1b76268892cbf22e7d0f1348f58c0c6cff7305e87e41ea8193aba55
PKG_LICENSE:=GPL3.0+
PKG_MAINTAINER:=Joseph Mory <morytyann@gmail.com>
+12 -15
View File
@@ -1,28 +1,25 @@
# Mixin File
# You can set any mihomo profile's config at here, it will mixin to the profile.
# Mixin file have lower priority than the LuCI mixin options.
#
# Mihomo's Wiki: https://wiki.metacubex.one
#
# For example:
#
# global-client-fingerprint: chrome # set fingerprint for TLS transport
# experimental: # experimental config
# quic-go-disable-gso: false # disable quic-go GSO support
# quic-go-disable-ecn: false # disable quic-go ECN support
# dialer-ip4p-convert: false # IP4P support
# proxies: # overwrite proxies
# listeners: # overwrite listeners
# - name: shadowsocks
# type: shadowsocks
# listen: "::"
# port: 12060
# nikki-proxies: # prepend proxies
# - name: "PROXY"
# type: ss
# server: proxy.example.com
# port: 443
# cipher: chacha20-ietf-poly1305
# password: "password"
# rules: # overwrite rules
# - DOMAIN,google.com,PROXY
# - DOMAIN-SUFFIX,google.com,PROXY
# - DOMAIN-KEYWORD,google,PROXY
# - DOMAIN-REGEX,^google.*com,PROXY
# - GEOSITE,google,PROXY
# - GEOSITE,cn,DIRECT
# - IP-CIDR,8.8.8.8/32,DIRECT,no-resolve
# - GEOIP,telegram,DIRECT
# - GEOIP,cn,DIRECT
# - Match,PROXY
# nikki-rules: # prepend rules
# - DOMAIN,direct.example.com,DIRECT
# - DOMAIN-SUFFIX,proxy.example.com,PROXY
+2 -2
View File
@@ -139,9 +139,9 @@ start_service() {
yq -M -i 'del(.sniffer.sniff)' "$RUN_PROFILE_PATH"
fi
if [ "$mixin_file_content" = 0 ]; then
ucode -S "$MIXIN_UC" | yq -M -p json -o yaml | yq -M -i ea '... comments="" | . as $item ireduce ({}; . * $item ) | .rules = .nikki-rules + .rules | del(.nikki-rules)' "$RUN_PROFILE_PATH" -
ucode -S "$MIXIN_UC" | yq -M -p json -o yaml | yq -M -i ea '... comments="" | . as $item ireduce ({}; . * $item ) | .proxies = .nikki-proxies + .proxies | del(.nikki-proxies) | .rules = .nikki-rules + .rules | del(.nikki-rules)' "$RUN_PROFILE_PATH" -
elif [ "$mixin_file_content" = 1 ]; then
ucode -S "$MIXIN_UC" | yq -M -p json -o yaml | yq -M -i ea '... comments="" | . as $item ireduce ({}; . * $item ) | .rules = .nikki-rules + .rules | del(.nikki-rules)' "$RUN_PROFILE_PATH" "$MIXIN_FILE_PATH" -
ucode -S "$MIXIN_UC" | yq -M -p json -o yaml | yq -M -i ea '... comments="" | . as $item ireduce ({}; . * $item ) | .proxies = .nikki-proxies + .proxies | del(.nikki-proxies) | .rules = .nikki-rules + .rules | del(.nikki-rules)' "$RUN_PROFILE_PATH" "$MIXIN_FILE_PATH" -
fi
fi
# test profile
+2 -2
View File
@@ -12,13 +12,13 @@ PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
include $(INCLUDE_DIR)/package.mk
GEOIP_VER:=202507050144
GEOIP_VER:=202508050201
GEOIP_FILE:=geoip.dat.$(GEOIP_VER)
define Download/geoip
URL:=https://github.com/v2fly/geoip/releases/download/$(GEOIP_VER)/
URL_FILE:=geoip.dat
FILE:=$(GEOIP_FILE)
HASH:=d77289a7465b6e59df39a2d46bd02b30b4fa7dd70939c13d431fd2bd8f448e10
HASH:=54761d8691a5756fdb08d2cd4d0a9c889dbaab786e1cf758592e09fb00377f53
endef
GEOSITE_VER:=20250627153051
@@ -25,6 +25,8 @@ public class CoreHandler
Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.XrayLocalCert, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
// TODO Temporary addition to support proper use of sing-box v1.12
Environment.SetEnvironmentVariable("ENABLE_DEPRECATED_SPECIAL_OUTBOUNDS", "true", EnvironmentVariableTarget.Process);
//Copy the bin folder to the storage location (for init)
if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1")
+7 -7
View File
@@ -31,31 +31,31 @@ jobs:
- name: Install NDK
run: |
echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager \
--channel=3 \
--install "ndk;29.0.13113456"
echo "NDK_HOME=$ANDROID_HOME/ndk/29.0.13113456" >> $GITHUB_ENV
--channel=0 \
--install "ndk;28.2.13676358"
echo "NDK_HOME=$ANDROID_HOME/ndk/28.2.13676358" >> $GITHUB_ENV
sed -i '10i\
\
ndkVersion = "29.0.13113456"' ${{ github.workspace }}/V2rayNG/app/build.gradle.kts
ndkVersion = "28.2.13676358"' ${{ github.workspace }}/V2rayNG/app/build.gradle.kts
- name: Restore cached libtun2socks
id: cache-libtun2socks-restore
uses: actions/cache/restore@v4
with:
path: ${{ github.workspace }}/libs
key: libtun2socks-${{ runner.os }}-${{ env.NDK_HOME }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
key: libtun2socks-${{ runner.os }}-${{ env.NDK_HOME }}-${{ hashFiles('.git/modules/hev-socks5-tunnel/HEAD') }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
- name: Build libtun2socks
if: steps.cache-libtun2socks-restore.outputs.cache-hit != 'true'
run: |
bash compile-tun2socks.sh
- name: Save libtun2socks
if: steps.cache-libtun2socks-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ github.workspace }}/libs
key: libtun2socks-${{ runner.os }}-${{ env.NDK_HOME }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
key: libtun2socks-${{ runner.os }}-${{ env.NDK_HOME }}-${{ hashFiles('.git/modules/hev-socks5-tunnel/HEAD') }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
- name: Copy libtun2socks
run: |
+4
View File
@@ -10,3 +10,7 @@
[submodule "libancillary"]
path = libancillary
url = https://github.com/shadowsocks/libancillary
[submodule "hev-socks5-tunnel"]
path = hev-socks5-tunnel
url = https://github.com/heiher/hev-socks5-tunnel
branch = master
@@ -248,8 +248,8 @@
<string name="title_language">زووݩ</string>
<string name="title_ui_settings">سامووا رابت منتوری</string>
<string name="title_pref_ui_mode_night">سامووا هالت رابت منتوری</string>
<string name="title_pref_use_hev_tunnel">Enable New TUN Feature</string>
<string name="summary_pref_use_hev_tunnel">When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks.</string>
<string name="title_pref_use_hev_tunnel">فعال کردن ویژیی نۊ TUN</string>
<string name="summary_pref_use_hev_tunnel">مجالی ک فعال بۊ، TUN ، hev-socks5-tunnel ن و کار اگره؛ ٱر چینووݩ نبۊ و جاس badvpn-tun2socks و کار گرؽڌه ابۊ.</string>
<string name="title_logcat">داسووا</string>
<string name="logcat_copy">لف گیری</string>
@@ -245,8 +245,8 @@
<string name="title_language">زبان</string>
<string name="title_ui_settings">تنظیمات رابط کاربری</string>
<string name="title_pref_ui_mode_night">تنظیمات حالت رابط کاربری</string>
<string name="title_pref_use_hev_tunnel">Enable New TUN Feature</string>
<string name="summary_pref_use_hev_tunnel">When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks.</string>
<string name="title_pref_use_hev_tunnel">فعالسازی قابلیت TUN جدید</string>
<string name="summary_pref_use_hev_tunnel">در صورت فعال بودن، TUN از hev-socks5-tunnel استفاده می‌کند؛ در غیر این صورت از badvpn-tun2socks.</string>
<string name="title_logcat">گزارشات</string>
<string name="logcat_copy">کپی</string>
@@ -247,8 +247,8 @@
<string name="title_language">Язык</string>
<string name="title_ui_settings">Настройки интерфейса</string>
<string name="title_pref_ui_mode_night">Тема интерфейса</string>
<string name="title_pref_use_hev_tunnel">Enable New TUN Feature</string>
<string name="summary_pref_use_hev_tunnel">When enabled, TUN will use hev-socks5-tunnel; otherwise, it will use badvpn-tun2socks.</string>
<string name="title_pref_use_hev_tunnel">Использовать новую версию TUN</string>
<string name="summary_pref_use_hev_tunnel">Если включено, TUN будет использовать hev-socks5-tunnel; иначе будет использоваться badvpn-tun2socks.</string>
<string name="title_logcat">Журнал</string>
<string name="logcat_copy">Копировать</string>
+26
View File
@@ -30,3 +30,29 @@ $NDK_HOME/ndk-build \
cp -r $TMPDIR/libs $__dir/
popd
rm -rf $TMPDIR
#build hev-socks5-tunnel
HEVTUN_TMP=$(mktemp -d)
trap 'rm -rf "$HEVTUN_TMP"' EXIT
mkdir -p "$HEVTUN_TMP/jni"
pushd "$HEVTUN_TMP"
echo 'include $(call all-subdir-makefiles)' > jni/Android.mk
ln -s "$__dir/hev-socks5-tunnel" jni/hev-socks5-tunnel
"$NDK_HOME/ndk-build" \
NDK_PROJECT_PATH=. \
APP_BUILD_SCRIPT=jni/Android.mk \
"APP_ABI=armeabi-v7a arm64-v8a x86 x86_64" \
APP_PLATFORM=android-21 \
NDK_LIBS_OUT="$HEVTUN_TMP/libs" \
NDK_OUT="$HEVTUN_TMP/obj" \
"APP_CFLAGS=-O3 -DPKGNAME=com/v2ray/ang/service" \
"APP_LDFLAGS=-WI,--build-id=none -WI,--hash-style=gnu" \
cp -r "$HEVTUN_TMP/libs/"* "$__dir/libs/"
popd
rm -rf "$HEVTUN_TMP"
+115
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
...
+11
View File
@@ -0,0 +1,11 @@
/.idea/
/.vscode/
/.git/
/.github/
/README.md
/License
/Dockerfile
/conf/
+402
View File
@@ -0,0 +1,402 @@
name: "Build"
on:
push:
branches:
- master
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@v4
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@v4
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: powerpc64
tool: powerpc64-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@v4
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/20250520/${{ 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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
with:
fetch-depth: 1
- name: Download artifacts
uses: actions/download-artifact@v4
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@v4
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@v4
with:
fetch-depth: 1
submodules: true
- name: Prepare
run: |
wget https://dl.google.com/android/repository/android-ndk-r27b-linux.zip
unzip android-ndk-r27b-linux.zip
ln -sf . jni
- name: Build
run: |
./android-ndk-r27b/ndk-build
llvm:
name: LLVM
runs-on: ubuntu-latest
if: github.event_name != 'release'
steps:
- name: Checkout
uses: actions/checkout@v4
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@v4
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@v4
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@v4
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 }}
@@ -0,0 +1,26 @@
name: "Check code formatting"
on:
push:
branches:
- master
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
+5
View File
@@ -0,0 +1,5 @@
bin
build
HevSocks5Tunnel.xcframework
obj
libs
+12
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
+51
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)
+21
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-21
APP_ABI := armeabi-v7a arm64-v8a
APP_CFLAGS := -O3
APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true
NDK_TOOLCHAIN_VERSION := clang
+42
View File
@@ -0,0 +1,42 @@
FROM alpine:latest AS builder
RUN apk add --update --no-cache \
make \
git \
gcc \
linux-headers \
musl-dev
WORKDIR /src
COPY . /src
RUN 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 \
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"]
+19
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.
+146
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
+360
View File
@@ -0,0 +1,360 @@
# HevSocks5Tunnel
[![status](https://github.com/heiher/hev-socks5-tunnel/actions/workflows/build.yaml/badge.svg?branch=master&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'
# Socks5 handshake using pipeline mode
# pipeline: false
# Socks5 server username
# username: 'username'
# Socks5 server password
# password: 'password'
# Socket mark
# mark: 0
#mapdns:
# Mapped DNS address
# address: 198.18.0.2
# Mapped DNS port
# port: 53
# Mapped IP network base
# network: 240.0.0.0
# Mapped IP network mask
# netmask: 240.0.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
# connect timeout (ms)
# connect-timeout: 5000
# read-write timeout (ms)
# 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
# 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 tun0
route change ::/0 :: if tun0
```
#### Low memory usage
On low-memory systems like iOS, reducing the size of the TCP buffer
and task stack 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
```
#### 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`
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
* **pronebird** - https://github.com/pronebird
* **saeeddev94** - https://github.com/saeeddev94
* **sskaje** - https://github.com/sskaje
* **wankkoree** - https://github.com/wankkoree
* **xz-dev** - https://github.com/xz-dev
* **yiguous** - https://github.com/yiguous
## License
MIT
[^1]: See [protocol specification](https://github.com/heiher/hev-socks5-core/tree/master?tab=readme-ov-file#udp-in-tcp). The [hev-socks5-server](https://github.com/heiher/hev-socks5-server) supports UDP relay over TCP.
+75
View File
@@ -0,0 +1,75 @@
#!/bin/bash
set -e
XCFRAMEWORK_DIR="./apple_xcframework"
# 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
cp ./src/hev-main.h $INCLUDE_DIR
cp ./module.modulemap $INCLUDE_DIR
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
+20
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)\"
+63
View File
@@ -0,0 +1,63 @@
# 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'
# Socks5 handshake using pipeline mode
# pipeline: false
# Socks5 server username
# username: 'username'
# Socks5 server password
# password: 'password'
# Socket mark
# mark: 0
#mapdns:
# Mapped DNS address
# address: 198.18.0.2
# Mapped DNS port
# port: 53
# Mapped IP network base
# network: 240.0.0.0
# Mapped IP network mask
# netmask: 240.0.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
# connect timeout (ms)
# connect-timeout: 5000
# read-write timeout (ms)
# 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
@@ -0,0 +1,84 @@
#!/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}"
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
}
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
@@ -0,0 +1 @@
../src/hev-main.h
@@ -0,0 +1,4 @@
module HevSocks5Tunnel {
umbrella header "hev-main.h"
export *
}
@@ -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
...
@@ -0,0 +1,26 @@
name: "Check code formatting"
on:
push:
branches:
- master
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
@@ -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.
@@ -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));
/*
* HevSocks5Addr addr;
*
* send udp packet:
* hev_socks5_udp_sendto (HEV_SOCKS5_UDP (udp), data, len, &addr);
*
* recv udp packet:
* hev_socks5_udp_recvfrom (HEV_SOCKS5_UDP (udp), data, len, &addr);
*/
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
@@ -0,0 +1 @@
../src/hev-rbtree.h
@@ -0,0 +1 @@
../src/hev-socks5-authenticator.h
@@ -0,0 +1 @@
../src/hev-socks5-client-tcp.h
@@ -0,0 +1 @@
../src/hev-socks5-client-udp.h
@@ -0,0 +1 @@
../src/hev-socks5-client.h
@@ -0,0 +1 @@
../src/hev-socks5-logger.h
@@ -0,0 +1 @@
../src/hev-socks5-misc.h
@@ -0,0 +1 @@
../src/hev-socks5-proto.h
@@ -0,0 +1 @@
../src/hev-socks5-server.h
@@ -0,0 +1 @@
../src/hev-socks5-tcp.h
@@ -0,0 +1 @@
../src/hev-socks5-udp.h
@@ -0,0 +1 @@
../src/hev-socks5-user.h
@@ -0,0 +1 @@
../src/hev-socks5.h
@@ -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__ */
@@ -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);
}
@@ -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__ */
@@ -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;
}
@@ -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__ */
@@ -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;
}
@@ -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__ */
@@ -0,0 +1,212 @@
/*
============================================================================
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;
addr_family = hev_socks5_get_addr_family (HEV_SOCKS5 (self));
res = hev_socks5_addr_into_sockaddr6 (addr, &saddr, &addr_family);
if (res < 0) {
LOG_E ("%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_E ("%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_E ("%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;
}
@@ -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__ */
@@ -0,0 +1,469 @@
/*
============================================================================
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_connect_server (HevSocks5Client *self, const char *addr,
int port)
{
HevSocks5Class *klass;
struct sockaddr_in6 saddr;
struct sockaddr *sap;
int addr_family;
int fd, res;
LOG_D ("%p socks5 client connect server", self);
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_E ("%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_E ("%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_E ("%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_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_E ("%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_E ("%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_E ("%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_E ("%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 <= 0) {
LOG_E ("%p socks5 client read auth", self);
return -1;
}
if (auth.ver != HEV_SOCKS5_VERSION_5) {
LOG_E ("%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 <= 0) {
LOG_E ("%p socks5 client read auth creds", self);
return -1;
}
if (res.ver != HEV_SOCKS5_AUTH_VERSION_1) {
LOG_E ("%p socks5 client auth.res.ver %u", self, res.ver);
return -1;
}
if (res.rep != HEV_SOCKS5_RES_REP_SUCC) {
LOG_E ("%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 <= 0) {
LOG_E ("%p socks5 client read response", self);
return -1;
}
if (res.ver != HEV_SOCKS5_VERSION_5) {
LOG_E ("%p socks5 client res.ver %u", self, res.ver);
return -1;
}
if (res.rep != HEV_SOCKS5_RES_REP_SUCC) {
LOG_E ("%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_E ("%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 <= 0) {
LOG_E ("%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_E ("%p socks5 client set upstream addr", self);
return -1;
}
return 0;
}
int
hev_socks5_client_connect (HevSocks5Client *self, const char *addr, int port)
{
int res;
LOG_D ("%p socks5 client connect [%s]:%d", self, addr, port);
res = hev_socks5_client_connect_server (self, addr, port);
if (res < 0) {
LOG_E ("%p socks5 client connect", self);
return -1;
}
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_E ("%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_E ("%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)
{
if (pipeline)
return hev_socks5_client_handshake_pipeline (self);
return hev_socks5_client_handshake_standard (self);
}
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;
}
@@ -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__ */
@@ -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__ */
@@ -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 (fd >= 0 && level >= req_level)
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 (fd < 0 || level < req_level)
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 */
}
}
@@ -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__ */
@@ -0,0 +1,36 @@
/*
============================================================================
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_task_stack_size (void);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_MISC_PRIV_H__ */
@@ -0,0 +1,332 @@
/*
============================================================================
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 task_stack_size = 8192;
static int udp_recv_buffer_size = 512 * 1024;
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_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;
}
@@ -0,0 +1,43 @@
/*
============================================================================
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_task_stack_size (int stack_size);
void hev_socks5_set_udp_recv_buffer_size (int buffer_size);
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__ */
@@ -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__ */
@@ -0,0 +1,688 @@
/*
============================================================================
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;
}
void
hev_socks5_server_set_connect_timeout (HevSocks5Server *self, int timeout)
{
self->timeout = timeout;
}
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 <= 0) {
LOG_E ("%p socks5 server read auth method", self);
return -1;
}
if (auth.ver != HEV_SOCKS5_VERSION_5) {
LOG_E ("%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 <= 0) {
LOG_E ("%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_E ("%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 <= 0) {
LOG_E ("%p socks5 server read auth user.ver", self);
return -1;
}
if (head[0] != 1) {
LOG_E ("%p socks5 server auth user.ver %u", self, head[0]);
return -1;
}
nlen = head[1];
if (nlen == 0) {
LOG_E ("%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 <= 0) {
LOG_E ("%p socks5 server read auth user.name", self);
return -1;
}
plen = name[nlen];
if (plen == 0) {
LOG_E ("%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 <= 0) {
LOG_E ("%p socks5 server read auth user.pass", self);
return -1;
}
user = hev_socks5_authenticator_get (self->auth, (char *)name, nlen);
if (!user) {
LOG_E ("%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_E ("%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_E ("%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 <= 0) {
LOG_E ("%p socks5 server read request", self);
return -1;
}
if (req.ver != HEV_SOCKS5_VERSION_5) {
*rep = HEV_SOCKS5_RES_REP_FAIL;
LOG_E ("%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_E ("%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 <= 0) {
*rep = HEV_SOCKS5_RES_REP_ADDR;
LOG_E ("%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_E ("%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_E ("%p socks5 server bind", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
timeout = hev_socks5_get_timeout (HEV_SOCKS5 (self));
hev_socks5_set_timeout (HEV_SOCKS5 (self), self->timeout);
res = hev_task_io_socket_connect (fd, (struct sockaddr *)addr,
sizeof (*addr), task_io_yielder, self);
hev_socks5_set_timeout (HEV_SOCKS5 (self), timeout);
if (res < 0) {
LOG_E ("%p socks5 server connect", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
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);
socklen_t alen;
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_E ("%p socks5 server socket reuse", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
res = sskptr->binder (self, fd, (struct sockaddr *)addr);
if (res < 0) {
LOG_E ("%p socks5 server bind", self);
hev_task_del_fd (hev_task_self (), fd);
close (fd);
return -1;
}
alen = sizeof (struct sockaddr_in6);
res = getsockname (fd, (struct sockaddr *)addr, &alen);
if (res < 0) {
LOG_E ("%p socks5 server socket name", 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,
const struct sockaddr *src)
{
struct sockaddr_in6 addr;
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 *)&addr, &alen);
if (res < 0) {
LOG_E ("%p socks5 server socket name", self);
return -1;
}
addr.sin6_port = 0;
res = bind (sock, (struct sockaddr *)&addr, alen);
if (res < 0) {
LOG_E ("%p socks5 server socket bind", self);
return -1;
}
return 0;
}
static int
hev_socks5_server_handshake (HevSocks5Server *self)
{
struct sockaddr_in6 addr;
int cmd;
int rep;
int res;
LOG_D ("%p socks5 server handshake", self);
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)
{
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:
hev_socks5_udp_splice (HEV_SOCKS5_UDP (self), self->fds[0]);
break;
case HEV_SOCKS5_TYPE_UDP_IN_TCP:
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;
self->timeout = -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;
}
@@ -0,0 +1,73 @@
/*
============================================================================
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];
int timeout;
union
{
HevObject *obj;
HevSocks5User *user;
HevSocks5Authenticator *auth;
};
};
struct _HevSocks5ServerClass
{
HevSocks5Class base;
int (*binder) (HevSocks5Server *self, int sock, const struct sockaddr *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);
void hev_socks5_server_set_connect_timeout (HevSocks5Server *self, int timeout);
int hev_socks5_server_run (HevSocks5Server *self);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_SERVER_H__ */
@@ -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;
}
@@ -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__ */
@@ -0,0 +1,420 @@
/*
============================================================================
Name : hev-socks5-udp.c
Author : Heiher <r@hev.cc>
Copyright : Copyright (c) 2021 - 2025 hev
Description : Socks5 UDP
============================================================================
*/
#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-socks5.h"
#include "hev-socks5-misc-priv.h"
#include "hev-socks5-logger-priv.h"
#include "hev-socks5-udp.h"
typedef enum _HevSocks5UDPAlive HevSocks5UDPAlive;
typedef struct _HevSocks5UDPSplice HevSocks5UDPSplice;
enum _HevSocks5UDPAlive
{
HEV_SOCKS5_UDP_ALIVE_F = (1 << 0),
HEV_SOCKS5_UDP_ALIVE_B = (1 << 1),
};
struct _HevSocks5UDPSplice
{
HevSocks5UDP *udp;
HevSocks5UDPAlive alive;
int bind;
int fd;
};
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);
}
int
hev_socks5_udp_sendto (HevSocks5UDP *self, const void *buf, size_t len,
const HevSocks5Addr *addr)
{
HevSocks5UDPHdr udp;
struct iovec iov[3];
struct msghdr mh;
int addrlen;
int res;
LOG_D ("%p socks5 udp sendto", self);
addrlen = hev_socks5_addr_len (addr);
if (addrlen <= 0) {
LOG_D ("%p socks5 udp addr", self);
return -1;
}
switch (HEV_SOCKS5 (self)->type) {
case HEV_SOCKS5_TYPE_UDP_IN_TCP:
udp.datlen = htons (len);
udp.hdrlen = 3 + addrlen;
break;
case HEV_SOCKS5_TYPE_UDP_IN_UDP:
udp.datlen = 0;
udp.hdrlen = 0;
break;
default:
return -1;
}
memset (&mh, 0, sizeof (mh));
mh.msg_iov = iov;
mh.msg_iovlen = 3;
iov[0].iov_base = &udp;
iov[0].iov_len = 3;
iov[1].iov_base = (void *)addr;
iov[1].iov_len = addrlen;
iov[2].iov_base = (void *)buf;
iov[2].iov_len = 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 udp", self);
return res;
}
static int
hev_socks5_udp_recvfrom_tcp (HevSocks5UDP *self, void *buf, size_t len,
HevSocks5Addr *addr)
{
HevSocks5UDPHdr udp;
struct iovec iov[2];
struct msghdr mh;
int res;
int fd;
LOG_D ("%p socks5 udp recvfrom tcp", self);
fd = hev_socks5_udp_get_fd (self);
res = hev_task_io_socket_recv (fd, &udp, 5, MSG_WAITALL, task_io_yielder,
self);
if (res <= 0) {
LOG_D ("%p socks5 udp read udp head", self);
return res;
}
udp.datlen = ntohs (udp.datlen);
if (udp.datlen > len) {
LOG_D ("%p socks5 udp data len", self);
return -1;
}
memset (&mh, 0, sizeof (mh));
mh.msg_iov = iov;
mh.msg_iovlen = 2;
iov[0].iov_base = &addr->domain.addr;
iov[0].iov_len = udp.hdrlen - 5;
iov[1].iov_base = buf;
iov[1].iov_len = udp.datlen;
res = hev_task_io_socket_recvmsg (fd, &mh, MSG_WAITALL, task_io_yielder,
self);
if (res <= 0) {
LOG_D ("%p socks5 udp read udp data", self);
return res;
}
addr->atype = udp.addr.atype;
addr->domain.len = udp.addr.domain.len;
return udp.datlen;
}
static int
hev_socks5_udp_recvfrom_udp (HevSocks5UDP *self, void *buf, size_t len,
HevSocks5Addr *addr)
{
struct sockaddr *saddr = NULL;
struct sockaddr_in6 taddr;
HevSocks5UDPHdr *udp;
uint8_t rbuf[1500];
socklen_t alen = 0;
ssize_t rlen;
int addrlen;
int doff;
int res;
int fd;
LOG_D ("%p socks5 udp recvfrom udp", self);
if (!HEV_SOCKS5 (self)->udp_associated) {
saddr = (struct sockaddr *)&taddr;
alen = sizeof (struct sockaddr_in6);
HEV_SOCKS5 (self)->udp_associated = 1;
}
fd = hev_socks5_udp_get_fd (self);
rlen = hev_task_io_socket_recvfrom (fd, rbuf, sizeof (rbuf), 0, saddr,
&alen, task_io_yielder, self);
if (rlen < 4) {
LOG_D ("%p socks5 udp read", self);
return rlen;
}
if (saddr) {
res = connect (fd, saddr, alen);
if (res < 0)
return -1;
}
udp = (HevSocks5UDPHdr *)rbuf;
addrlen = hev_socks5_addr_len (&udp->addr);
if (addrlen <= 0) {
LOG_D ("%p socks5 udp addr", self);
return -1;
}
doff = 3 + addrlen;
if (doff > rlen) {
LOG_D ("%p socks5 udp data len", self);
return -1;
}
rlen -= doff;
if (len < rlen)
rlen = len;
memcpy (buf, rbuf + doff, rlen);
memcpy (addr, &udp->addr, addrlen);
return rlen;
}
int
hev_socks5_udp_recvfrom (HevSocks5UDP *self, void *buf, size_t len,
HevSocks5Addr *addr)
{
int res;
switch (HEV_SOCKS5 (self)->type) {
case HEV_SOCKS5_TYPE_UDP_IN_TCP:
res = hev_socks5_udp_recvfrom_tcp (self, buf, len, addr);
break;
case HEV_SOCKS5_TYPE_UDP_IN_UDP:
res = hev_socks5_udp_recvfrom_udp (self, buf, len, addr);
break;
default:
return -1;
}
return res;
}
static int
hev_socks5_udp_fwd_f (HevSocks5UDP *self, HevSocks5UDPSplice *splice)
{
struct sockaddr_in6 addr;
struct sockaddr *saddr;
HevSocks5Addr taddr;
uint8_t buf[1500];
int addr_family;
ssize_t res;
int ret;
LOG_D ("%p socks5 udp fwd f", self);
res = hev_socks5_udp_recvfrom (self, buf, sizeof (buf), &taddr);
if (res <= 0) {
if (res < -1) {
splice->alive &= ~HEV_SOCKS5_UDP_ALIVE_F;
if (splice->alive && hev_socks5_get_timeout (HEV_SOCKS5 (self)))
return 0;
}
if (HEV_SOCKS5 (self)->type == HEV_SOCKS5_TYPE_UDP_IN_TCP)
hev_socks5_set_timeout (HEV_SOCKS5 (self), 0);
LOG_D ("%p socks5 udp fwd f recv", self);
return -1;
}
saddr = (struct sockaddr *)&addr;
addr_family = hev_socks5_get_addr_family (HEV_SOCKS5 (self));
ret = hev_socks5_addr_into_sockaddr6 (&taddr, &addr, &addr_family);
if (ret < 0) {
LOG_D ("%p socks5 udp to sockaddr", self);
return -1;
}
if (!splice->bind) {
HevSocks5Class *skptr = HEV_OBJECT_GET_CLASS (self);
ret = skptr->binder (HEV_SOCKS5 (self), splice->fd, saddr);
if (ret < 0) {
LOG_E ("%p socks5 udp bind", self);
return -1;
}
splice->bind = 1;
}
res = sendto (splice->fd, buf, res, 0, saddr, sizeof (addr));
if (res <= 0) {
if ((res < 0) && (errno == EAGAIN))
return 0;
LOG_D ("%p socks5 udp fwd f send", self);
return -1;
}
splice->alive |= HEV_SOCKS5_UDP_ALIVE_F;
return 0;
}
static int
hev_socks5_udp_fwd_b (HevSocks5UDP *self, HevSocks5UDPSplice *splice)
{
struct sockaddr_in6 addr;
socklen_t addrlen;
uint8_t buf[1500];
ssize_t res;
LOG_D ("%p socks5 udp fwd b", self);
addrlen = sizeof (addr);
res = hev_task_io_socket_recvfrom (splice->fd, buf, sizeof (buf), 0,
(struct sockaddr *)&addr, &addrlen,
task_io_yielder, self);
if (res > 0) {
HevSocks5Addr taddr;
hev_socks5_addr_from_sockaddr6 (&taddr, &addr);
res = hev_socks5_udp_sendto (self, buf, res, &taddr);
}
if (res <= 0) {
if (res < -1) {
splice->alive &= ~HEV_SOCKS5_UDP_ALIVE_B;
if (splice->alive && hev_socks5_get_timeout (HEV_SOCKS5 (self)))
return 0;
}
if (HEV_SOCKS5 (self)->type == HEV_SOCKS5_TYPE_UDP_IN_TCP)
hev_socks5_set_timeout (HEV_SOCKS5 (self), 0);
LOG_D ("%p socks5 udp fwd b recv send", self);
return -1;
}
splice->alive |= HEV_SOCKS5_UDP_ALIVE_B;
return 0;
}
static void
splice_task_entry (void *data)
{
HevSocks5UDPSplice *splice = data;
HevSocks5UDP *self = splice->udp;
HevTask *task = hev_task_self ();
int fd;
fd = hev_task_io_dup (hev_socks5_udp_get_fd (self));
if (fd < 0)
return;
if (hev_task_add_fd (task, fd, POLLIN) < 0)
hev_task_mod_fd (task, fd, POLLIN);
for (;;) {
if (hev_socks5_udp_fwd_f (self, splice) < 0)
break;
}
splice->alive &= ~HEV_SOCKS5_UDP_ALIVE_F;
hev_task_del_fd (task, fd);
close (fd);
}
static int
hev_socks5_udp_splicer (HevSocks5UDP *self, int fd)
{
HevTask *task = hev_task_self ();
HevSocks5UDPSplice splice;
int stack_size;
int ufd;
LOG_D ("%p socks5 udp splicer", self);
splice.udp = self;
splice.alive = HEV_SOCKS5_UDP_ALIVE_F | HEV_SOCKS5_UDP_ALIVE_B;
splice.bind = 0;
splice.fd = fd;
if (hev_task_add_fd (task, fd, POLLIN) < 0)
hev_task_mod_fd (task, fd, POLLIN);
ufd = hev_socks5_udp_get_fd (self);
if (hev_task_mod_fd (task, ufd, POLLOUT) < 0)
hev_task_add_fd (task, ufd, POLLOUT);
stack_size = hev_socks5_get_task_stack_size ();
task = hev_task_new (stack_size);
hev_task_ref (task);
hev_task_run (task, splice_task_entry, &splice);
for (;;) {
if (hev_socks5_udp_fwd_b (self, &splice) < 0)
break;
}
splice.alive &= ~HEV_SOCKS5_UDP_ALIVE_B;
hev_task_join (task);
hev_task_unref (task);
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;
}
@@ -0,0 +1,48 @@
/*
============================================================================
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 _HevSocks5UDPIface HevSocks5UDPIface;
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_sendto (HevSocks5UDP *self, const void *buf, size_t len,
const HevSocks5Addr *addr);
int hev_socks5_udp_recvfrom (HevSocks5UDP *self, void *buf, size_t len,
HevSocks5Addr *addr);
int hev_socks5_udp_splice (HevSocks5UDP *self, int fd);
#ifdef __cplusplus
}
#endif
#endif /* __HEV_SOCKS5_UDP_H__ */
@@ -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;
}
@@ -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__ */
@@ -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;
}
@@ -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__ */
@@ -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 (13)
#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__ */
+618
View File
@@ -0,0 +1,618 @@
/*
============================================================================
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 task_stack_size = 86016;
static int tcp_buffer_size = 65536;
static int connect_timeout = 5000;
static int 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 *user = NULL;
const char *pass = NULL;
const char *mark = NULL;
const char *pipe = 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, "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;
}
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 (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);
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;
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, "connect-timeout"))
connect_timeout = strtoul (value, NULL, 10);
else if (0 == strcmp (key, "read-write-timeout"))
read_write_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);
}
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;
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;
min_task_stack_size = TASK_STACK_SIZE + tcp_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_connect_timeout (void)
{
return connect_timeout;
}
int
hev_config_get_misc_read_write_timeout (void)
{
return 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;
}
@@ -0,0 +1,58 @@
/*
============================================================================
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;
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_connect_timeout (void);
int hev_config_get_misc_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__ */
+170
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 */
+13
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__ */
+144
View File
@@ -0,0 +1,144 @@
/*
============================================================================
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-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_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 */

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