├── conf ├── auth.txt └── main.yml ├── .gitignore ├── module.modulemap ├── .gitmodules ├── src ├── hev-jni.h ├── hev-config-const.h ├── hev-socks5-proxy.h ├── hev-socket-factory.h ├── misc │ ├── hev-misc.h │ ├── hev-list.c │ ├── hev-logger.h │ ├── hev-list.h │ ├── hev-logger.c │ ├── hev-compiler.h │ └── hev-misc.c ├── hev-socks5-worker.h ├── hev-socks5-session.h ├── hev-main.h ├── hev-socks5-user-mark.h ├── hev-config.h ├── hev-socks5-user-mark.c ├── hev-socket-factory.c ├── hev-jni.c ├── hev-main.c ├── hev-socks5-session.c ├── hev-socks5-worker.c ├── hev-socks5-proxy.c └── hev-config.c ├── docker ├── README.md ├── entrypoint.sh └── Dockerfile ├── .github └── workflows │ ├── code-format.yaml │ └── build.yaml ├── Application.mk ├── LICENSE ├── Android.mk ├── .clang-format ├── Makefile └── README.md /conf/auth.txt: -------------------------------------------------------------------------------- 1 | tom pass 2 | jerry pass 1a 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | build 3 | HevSocks5Server.xcframework 4 | -------------------------------------------------------------------------------- /module.modulemap: -------------------------------------------------------------------------------- 1 | module HevSocks5Server { 2 | umbrella header "hev-main.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third-part/hev-task-system"] 2 | path = third-part/hev-task-system 3 | url = https://github.com/heiher/hev-task-system 4 | [submodule "third-part/yaml"] 5 | path = third-part/yaml 6 | url = https://github.com/heiher/yaml 7 | [submodule "src/core"] 8 | path = src/core 9 | url = https://github.com/heiher/hev-socks5-core 10 | -------------------------------------------------------------------------------- /src/hev-jni.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-jni.h 4 | Author : hev 5 | Copyright : Copyright (c) 2019 - 2024 hev 6 | Description : Java Native Interface 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_JNI_H__ 11 | #define __HEV_JNI_H__ 12 | 13 | #endif /* __HEV_JNI_H__ */ 14 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # hev-socks5-server for docker 2 | 3 | ## Usage 4 | 5 | Available environment variables: 6 | ```bash 7 | PORT 8 | LISTEN_ADDRESS 9 | AUTH 10 | ``` 11 | 12 | ```bash 13 | cd hev-socks5-server 14 | docker build -t hev-socks5-server -f docker/Dockerfile . 15 | docker run -d \ 16 | --name hev-socks5-server \ 17 | --restart always \ 18 | --net host \ 19 | -e PORT=1081 \ 20 | -e AUTH="user:pass" \ 21 | hev-socks5-server 22 | ``` 23 | -------------------------------------------------------------------------------- /src/hev-config-const.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-config-const.h 4 | Author : Heiher 5 | Copyright : Copyright (c) 2017 - 2023 hev 6 | Description : Config Constants 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_CONFIG_CONST_H__ 11 | #define __HEV_CONFIG_CONST_H__ 12 | 13 | #define MAJOR_VERSION (2) 14 | #define MINOR_VERSION (11) 15 | #define MICRO_VERSION (1) 16 | 17 | #endif /* __HEV_CONFIG_CONST_H__ */ 18 | -------------------------------------------------------------------------------- /src/hev-socks5-proxy.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-socks5-proxy.h 4 | Author : Heiher 5 | Copyright : Copyright (c) 2017 - 2021 hev 6 | Description : Socks5 Proxy 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_SOCKS5_PROXY_H__ 11 | #define __HEV_SOCKS5_PROXY_H__ 12 | 13 | int hev_socks5_proxy_init (void); 14 | void hev_socks5_proxy_fini (void); 15 | 16 | void hev_socks5_proxy_run (void); 17 | void hev_socks5_proxy_stop (void); 18 | 19 | #endif /* __HEV_SOCKS5_PROXY_H__ */ 20 | -------------------------------------------------------------------------------- /.github/workflows/code-format.yaml: -------------------------------------------------------------------------------- 1 | name: "Check code formatting" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | checker: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 1 17 | - name: Prepare 18 | run: | 19 | sudo apt install -y clang-format 20 | - name: Format 21 | run: | 22 | find src -iname "*.[h,c]" -exec clang-format -i {} \; 23 | - name: Check 24 | run: | 25 | git status 26 | git diff --quiet 27 | -------------------------------------------------------------------------------- /docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [[ -n "$PORT" && "$PORT" != 1080 ]]; then 4 | sed -i "s/ port: 1080/ port: $PORT/" /app/etc/hev-socks5-server.yml 5 | fi 6 | 7 | if [[ -n "$LISTEN_ADDRESS" && "$LISTEN_ADDRESS" != '::' ]]; then 8 | sed -i "s/ listen-address: '::'/ listen-address: $LISTEN_ADDRESS/" /app/etc/hev-socks5-server.yml 9 | fi 10 | 11 | if [[ -n "$AUTH" ]]; then 12 | sed -i "s/#auth:/auth:/" /app/etc/hev-socks5-server.yml 13 | sed -i "s/# username:/ username: $(echo $AUTH | cut -d ':' -f 1)/" /app/etc/hev-socks5-server.yml 14 | sed -i "s/# password:/ password: $(echo $AUTH | cut -d ':' -f 2)/" /app/etc/hev-socks5-server.yml 15 | fi 16 | 17 | exec /app/bin/hev-socks5-server /app/etc/hev-socks5-server.yml 18 | -------------------------------------------------------------------------------- /src/hev-socket-factory.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-socket-factory.h 4 | Author : Heiher 5 | Copyright : Copyright (c) 2022 hev 6 | Description : Socket Factory 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_SOCKET_FACTORY_H__ 11 | #define __HEV_SOCKET_FACTORY_H__ 12 | 13 | typedef struct _HevSocketFactory HevSocketFactory; 14 | 15 | HevSocketFactory *hev_socket_factory_new (const char *addr, const char *port, 16 | int ipv6_only); 17 | void hev_socket_factory_destroy (HevSocketFactory *self); 18 | 19 | int hev_socket_factory_get (HevSocketFactory *self); 20 | 21 | #endif /* __HEV_SOCKET_FACTORY_H__ */ 22 | -------------------------------------------------------------------------------- /src/misc/hev-misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-misc.h 4 | Authors : Heiher 5 | Copyright : Copyright (c) 2022 - 2024 everyone. 6 | Description : Misc 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_MISC_H__ 11 | #define __HEV_MISC_H__ 12 | 13 | #include 14 | 15 | int hev_netaddr_resolve (struct sockaddr_in6 *daddr, const char *addr, 16 | const char *port); 17 | int hev_netaddr_is_any (struct sockaddr_in6 *addr); 18 | 19 | void run_as_daemon (const char *pid_file); 20 | int set_limit_nofile (int limit_nofile); 21 | 22 | int set_sock_bind (int fd, const char *iface); 23 | int set_sock_mark (int fd, unsigned int mark); 24 | 25 | #endif /* __HEV_MISC_H__ */ 26 | -------------------------------------------------------------------------------- /Application.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Android Open Source Project 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | APP_OPTIM := release 17 | APP_PLATFORM := android-29 18 | APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 19 | APP_CFLAGS := -O3 20 | APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true 21 | NDK_TOOLCHAIN_VERSION := clang 22 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Dockerfile for hev-socks5-server 3 | # 4 | 5 | # 6 | # Build stage 7 | # 8 | FROM alpine AS builder 9 | 10 | COPY . /tmp/hev-socks5-server 11 | 12 | # build 13 | RUN apk update \ 14 | && apk add --no-cache --virtual .build-deps build-base \ 15 | && cd /tmp/hev-socks5-server \ 16 | && make -j$(nproc) \ 17 | && make install INSTDIR="/app" \ 18 | && make clean \ 19 | && cd / \ 20 | && rm -r /tmp/hev-socks5-server \ 21 | && apk del .build-deps \ 22 | && rm -rf /var/cache/apk/* 23 | 24 | # 25 | # Runtime stage 26 | # 27 | FROM alpine 28 | 29 | ENV TZ=Asia/Taipei 30 | 31 | # add depends 32 | RUN apk update \ 33 | && apk add --no-cache tzdata \ 34 | && rm -rf /var/cache/apk/* 35 | 36 | # copy files 37 | COPY --from=builder /app /app/ 38 | RUN chown nobody:nobody /app/etc 39 | COPY docker/entrypoint.sh /app/entrypoint.sh 40 | 41 | USER nobody 42 | ENTRYPOINT ["/app/entrypoint.sh"] 43 | -------------------------------------------------------------------------------- /src/misc/hev-list.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-list.c 4 | Authors : Heiher 5 | Copyright : Copyright (c) 2019 everyone. 6 | Description : Double-linked List 7 | ============================================================================ 8 | */ 9 | 10 | #include 11 | 12 | #include "hev-list.h" 13 | 14 | void 15 | hev_list_add_tail (HevList *self, HevListNode *new_) 16 | { 17 | new_->prev = self->tail; 18 | new_->next = NULL; 19 | 20 | if (self->tail) 21 | self->tail->next = new_; 22 | else 23 | self->head = new_; 24 | self->tail = new_; 25 | } 26 | 27 | void 28 | hev_list_del (HevList *self, HevListNode *node) 29 | { 30 | if (node->prev) 31 | node->prev->next = node->next; 32 | else 33 | self->head = node->next; 34 | 35 | if (node->next) 36 | node->next->prev = node->prev; 37 | else 38 | self->tail = node->prev; 39 | } 40 | -------------------------------------------------------------------------------- /src/hev-socks5-worker.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-socks5-worker.h 4 | Author : Heiher 5 | Copyright : Copyright (c) 2017 - 2021 hev 6 | Description : Socks5 Worker 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_SOCKS5_WORKER_H__ 11 | #define __HEV_SOCKS5_WORKER_H__ 12 | 13 | #include 14 | 15 | typedef struct _HevSocks5Worker HevSocks5Worker; 16 | 17 | HevSocks5Worker *hev_socks5_worker_new (void); 18 | void hev_socks5_worker_destroy (HevSocks5Worker *self); 19 | int hev_socks5_worker_init (HevSocks5Worker *self, int fd); 20 | 21 | void hev_socks5_worker_start (HevSocks5Worker *self); 22 | void hev_socks5_worker_stop (HevSocks5Worker *self); 23 | void hev_socks5_worker_reload (HevSocks5Worker *self); 24 | 25 | void hev_socks5_worker_set_auth (HevSocks5Worker *self, 26 | HevSocks5Authenticator *auth); 27 | 28 | #endif /* __HEV_SOCKS5_WORKER_H__ */ 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 hev 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/misc/hev-logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-logger.h 4 | Author : Heiher 5 | Copyright : Copyright (c) 2019 - 2021 hev 6 | Description : Logger 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_LOGGER_H__ 11 | #define __HEV_LOGGER_H__ 12 | 13 | #define LOG_D(fmt...) hev_logger_log (HEV_LOGGER_DEBUG, fmt) 14 | #define LOG_I(fmt...) hev_logger_log (HEV_LOGGER_INFO, fmt) 15 | #define LOG_W(fmt...) hev_logger_log (HEV_LOGGER_WARN, fmt) 16 | #define LOG_E(fmt...) hev_logger_log (HEV_LOGGER_ERROR, fmt) 17 | 18 | #define LOG_ON() hev_logger_enabled (HEV_LOGGER_UNSET) 19 | #define LOG_ON_D() hev_logger_enabled (HEV_LOGGER_DEBUG) 20 | #define LOG_ON_I() hev_logger_enabled (HEV_LOGGER_INFO) 21 | #define LOG_ON_W() hev_logger_enabled (HEV_LOGGER_WARN) 22 | #define LOG_ON_E() hev_logger_enabled (HEV_LOGGER_ERROR) 23 | 24 | typedef enum 25 | { 26 | HEV_LOGGER_DEBUG, 27 | HEV_LOGGER_INFO, 28 | HEV_LOGGER_WARN, 29 | HEV_LOGGER_ERROR, 30 | HEV_LOGGER_UNSET, 31 | } HevLoggerLevel; 32 | 33 | int hev_logger_init (HevLoggerLevel level, const char *path); 34 | void hev_logger_fini (void); 35 | 36 | int hev_logger_enabled (HevLoggerLevel level); 37 | void hev_logger_log (HevLoggerLevel level, const char *fmt, ...); 38 | 39 | #endif /* __HEV_LOGGER_H__ */ 40 | -------------------------------------------------------------------------------- /src/misc/hev-list.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-list.h 4 | Authors : Heiher 5 | Copyright : Copyright (c) 2019 everyone. 6 | Description : Double-linked List 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_LIST_H__ 11 | #define __HEV_LIST_H__ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef struct _HevList HevList; 18 | typedef struct _HevListNode HevListNode; 19 | 20 | struct _HevList 21 | { 22 | HevListNode *head; 23 | HevListNode *tail; 24 | }; 25 | 26 | struct _HevListNode 27 | { 28 | HevListNode *next; 29 | HevListNode *prev; 30 | }; 31 | 32 | static inline HevListNode * 33 | hev_list_node_prev (HevListNode *node) 34 | { 35 | return node->prev; 36 | } 37 | 38 | static inline HevListNode * 39 | hev_list_node_next (HevListNode *node) 40 | { 41 | return node->next; 42 | } 43 | 44 | static inline HevListNode * 45 | hev_list_first (HevList *self) 46 | { 47 | return self->head; 48 | } 49 | 50 | static inline HevListNode * 51 | hev_list_last (HevList *self) 52 | { 53 | return self->tail; 54 | } 55 | 56 | void hev_list_add_tail (HevList *self, HevListNode *new_); 57 | void hev_list_del (HevList *self, HevListNode *node); 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | 63 | #endif /* __HEV_LIST_H__ */ 64 | -------------------------------------------------------------------------------- /src/hev-socks5-session.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-socks5-session.h 4 | Author : Heiher 5 | Copyright : Copyright (c) 2017 - 2021 hev 6 | Description : Socks5 Session 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_SOCKS5_SESSION_H__ 11 | #define __HEV_SOCKS5_SESSION_H__ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "hev-list.h" 18 | 19 | #define HEV_SOCKS5_SESSION(p) ((HevSocks5Session *)p) 20 | #define HEV_SOCKS5_SESSION_CLASS(p) ((HevSocks5SessionClass *)p) 21 | #define HEV_SOCKS5_SESSION_TYPE (hev_socks5_session_class ()) 22 | 23 | typedef struct _HevSocks5Session HevSocks5Session; 24 | typedef struct _HevSocks5SessionClass HevSocks5SessionClass; 25 | 26 | struct _HevSocks5Session 27 | { 28 | HevSocks5Server base; 29 | 30 | HevListNode node; 31 | HevTask *task; 32 | void *data; 33 | }; 34 | 35 | struct _HevSocks5SessionClass 36 | { 37 | HevSocks5ServerClass base; 38 | }; 39 | 40 | HevObjectClass *hev_socks5_session_class (void); 41 | 42 | int hev_socks5_session_construct (HevSocks5Session *self, int fd); 43 | 44 | HevSocks5Session *hev_socks5_session_new (int fd); 45 | 46 | void hev_socks5_session_terminate (HevSocks5Session *self); 47 | 48 | #endif /* __HEV_SOCKS5_SESSION_H__ */ 49 | -------------------------------------------------------------------------------- /src/hev-main.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-main.h 4 | Author : hev 5 | Copyright : Copyright (c) 2017 - 2024 hev 6 | Description : Main 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_MAIN_H__ 11 | #define __HEV_MAIN_H__ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /** 18 | * hev_socks5_server_main_from_file: 19 | * @config_path: config file path 20 | * 21 | * Start and run the socks5 server, this function will blocks until the 22 | * hev_socks5_server_quit is called or an error occurs. 23 | * 24 | * Returns: returns zero on successful, otherwise returns -1. 25 | * 26 | * Since: 2.6.7 27 | */ 28 | int hev_socks5_server_main_from_file (const char *config_path); 29 | 30 | /** 31 | * hev_socks5_server_main_from_str: 32 | * @config_str: string config 33 | * @config_len: the byte length of string config 34 | * 35 | * Start and run the socks5 server, this function will blocks until the 36 | * hev_socks5_server_quit is called or an error occurs. 37 | * 38 | * Returns: returns zero on successful, otherwise returns -1. 39 | * 40 | * Since: 2.6.7 41 | */ 42 | int hev_socks5_server_main_from_str (const unsigned char *config_str, 43 | unsigned int config_len); 44 | 45 | /** 46 | * hev_socks5_server_quit: 47 | * 48 | * Stop the socks5 server. 49 | * 50 | * Since: 2.6.7 51 | */ 52 | void hev_socks5_server_quit (void); 53 | 54 | #ifdef __cplusplus 55 | } 56 | #endif 57 | 58 | #endif /* __HEV_MAIN_H__ */ 59 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Android Open Source Project 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | TOP_PATH := $(call my-dir) 17 | 18 | ifeq ($(filter $(modules-get-list),yaml),) 19 | include $(TOP_PATH)/third-part/yaml/Android.mk 20 | endif 21 | ifeq ($(filter $(modules-get-list),hev-task-system),) 22 | include $(TOP_PATH)/third-part/hev-task-system/Android.mk 23 | endif 24 | 25 | LOCAL_PATH = $(TOP_PATH) 26 | SRCDIR := $(LOCAL_PATH)/src 27 | 28 | include $(CLEAR_VARS) 29 | include $(LOCAL_PATH)/build.mk 30 | LOCAL_MODULE := hev-socks5-server 31 | LOCAL_SRC_FILES := $(patsubst $(SRCDIR)/%,src/%,$(SRCFILES)) 32 | LOCAL_C_INCLUDES := \ 33 | $(LOCAL_PATH)/src/misc \ 34 | $(LOCAL_PATH)/src/core/include \ 35 | $(LOCAL_PATH)/third-part/yaml/include \ 36 | $(LOCAL_PATH)/third-part/hev-task-system/include 37 | LOCAL_CFLAGS += $(VERSION_CFLAGS) 38 | ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) 39 | LOCAL_CFLAGS += -mfpu=neon 40 | endif 41 | LOCAL_STATIC_LIBRARIES := yaml hev-task-system 42 | LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384 43 | LOCAL_LDFLAGS += -Wl,-z,common-page-size=16384 44 | include $(BUILD_SHARED_LIBRARY) 45 | -------------------------------------------------------------------------------- /src/hev-socks5-user-mark.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-socks5-user-mark.h 4 | Author : Heiher 5 | Copyright : Copyright (c) 2023 hev 6 | Description : Socks5 User with mark 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_SOCKS5_USER_MARK_H__ 11 | #define __HEV_SOCKS5_USER_MARK_H__ 12 | 13 | #include "hev-socks5-user.h" 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | #define HEV_SOCKS5_USER_MARK(p) ((HevSocks5UserMark *)p) 20 | #define HEV_SOCKS5_USER_MARK_CLASS(p) ((HevSocks5UserMarkClass *)p) 21 | #define HEV_SOCKS5_USER_MARK_TYPE (hev_socks5_user_mark_class ()) 22 | 23 | typedef struct _HevSocks5UserMark HevSocks5UserMark; 24 | typedef struct _HevSocks5UserMarkClass HevSocks5UserMarkClass; 25 | 26 | struct _HevSocks5UserMark 27 | { 28 | HevSocks5User base; 29 | 30 | unsigned int mark; 31 | }; 32 | 33 | struct _HevSocks5UserMarkClass 34 | { 35 | HevSocks5UserClass base; 36 | }; 37 | 38 | HevObjectClass *hev_socks5_user_mark_class (void); 39 | 40 | int hev_socks5_user_mark_construct (HevSocks5UserMark *self, const char *name, 41 | unsigned int name_len, const char *pass, 42 | unsigned int pass_len, unsigned int mark); 43 | 44 | HevSocks5UserMark *hev_socks5_user_mark_new (const char *name, 45 | unsigned int name_len, 46 | const char *pass, 47 | unsigned int pass_len, 48 | unsigned int mark); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif /* __HEV_SOCKS5_USER_MARK_H__ */ 55 | -------------------------------------------------------------------------------- /conf/main.yml: -------------------------------------------------------------------------------- 1 | # Main configuration for hev-socks5-server 2 | 3 | main: 4 | # Worker threads 5 | workers: 4 6 | # Listen port 7 | port: 1080 8 | # Listen address (ipv4|ipv6) 9 | listen-address: '::' 10 | # UDP listen port (0: random, a-b: range) 11 | # udp-port: 0 12 | # UDP listen address (ipv4|ipv6) 13 | # udp-listen-address: '::1' 14 | # UDP public address (ipv4) 15 | # udp-public-address-v4: '' 16 | # UDP public address (ipv6) 17 | # udp-public-address-v6: '' 18 | # Listen ipv6 only 19 | listen-ipv6-only: false 20 | # Bind source address (ipv4|ipv6) 21 | # It is overridden by bind-address-v{4,6} if specified 22 | bind-address: '' 23 | # Bind source address (ipv4) 24 | bind-address-v4: '' 25 | # Bind source address (ipv6) 26 | bind-address-v6: '' 27 | # Bind source network interface 28 | bind-interface: '' 29 | # Domain address type (ipv4|ipv6|unspec) 30 | domain-address-type: unspec 31 | # Socket mark (hex: 0x1, dec: 1, oct: 01) 32 | mark: 0 33 | 34 | #auth: 35 | # file: conf/auth.txt 36 | # username: 37 | # password: 38 | 39 | #misc: 40 | # task stack size (bytes) 41 | # task-stack-size: 8192 42 | # udp socket recv buffer (SO_RCVBUF) size (bytes) 43 | # udp-recv-buffer-size: 524288 44 | # number of udp buffers in splice, 1500 bytes per buffer. 45 | # udp-copy-buffer-nums: 10 46 | # TCP connect timeout (ms) 47 | # connect-timeout: 10000 48 | # TCP read-write timeout (ms) 49 | # tcp-read-write-timeout: 300000 50 | # UDP read-write timeout (ms) 51 | # udp-read-write-timeout: 60000 52 | # stdout, stderr or file-path 53 | # log-file: stderr 54 | # debug, info, warn or error 55 | # log-level: warn 56 | # If present, run as a daemon with this pid file 57 | # pid-file: /run/hev-socks5-server.pid 58 | # If present, set rlimit nofile; else use default value 59 | # limit-nofile: 65535 60 | -------------------------------------------------------------------------------- /src/hev-config.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-config.h 4 | Author : hev 5 | Copyright : Copyright (c) 2017 - 2024 hev 6 | Description : Config 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_CONFIG_H__ 11 | #define __HEV_CONFIG_H__ 12 | 13 | int hev_config_init_from_file (const char *config_path); 14 | int hev_config_init_from_str (const unsigned char *config_str, 15 | unsigned int config_len); 16 | void hev_config_fini (void); 17 | 18 | unsigned int hev_config_get_workers (void); 19 | 20 | const char *hev_config_get_listen_address (void); 21 | const char *hev_config_get_listen_port (void); 22 | const char *hev_config_get_udp_listen_address (void); 23 | int hev_config_get_udp_listen_port (void); 24 | const char *hev_config_get_udp_public_address (int family); 25 | int hev_config_get_listen_ipv6_only (void); 26 | 27 | const char *hev_config_get_bind_address (int family); 28 | const char *hev_config_get_bind_interface (void); 29 | 30 | int hev_config_get_address_family (void); 31 | unsigned int hev_config_get_socket_mark (void); 32 | 33 | const char *hev_config_get_auth_file (void); 34 | const char *hev_config_get_auth_username (void); 35 | const char *hev_config_get_auth_password (void); 36 | 37 | int hev_config_get_misc_task_stack_size (void); 38 | int hev_config_get_misc_udp_recv_buffer_size (void); 39 | int hev_config_get_misc_udp_copy_buffer_nums (void); 40 | int hev_config_get_misc_connect_timeout (void); 41 | int hev_config_get_misc_tcp_read_write_timeout (void); 42 | int hev_config_get_misc_udp_read_write_timeout (void); 43 | int hev_config_get_misc_limit_nofile (void); 44 | const char *hev_config_get_misc_pid_file (void); 45 | const char *hev_config_get_misc_log_file (void); 46 | int hev_config_get_misc_log_level (void); 47 | 48 | #endif /* __HEV_CONFIG_H__ */ 49 | -------------------------------------------------------------------------------- /src/hev-socks5-user-mark.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-socks5-user-mark.c 4 | Author : Heiher 5 | Copyright : Copyright (c) 2023 hev 6 | Description : Socks5 User with mark 7 | ============================================================================ 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "hev-logger.h" 14 | 15 | #include "hev-socks5-user-mark.h" 16 | 17 | HevSocks5UserMark * 18 | hev_socks5_user_mark_new (const char *name, unsigned int name_len, 19 | const char *pass, unsigned int pass_len, 20 | unsigned int mark) 21 | { 22 | HevSocks5UserMark *self; 23 | int res; 24 | 25 | self = calloc (1, sizeof (HevSocks5UserMark)); 26 | if (!self) 27 | return NULL; 28 | 29 | res = hev_socks5_user_mark_construct (self, name, name_len, pass, pass_len, 30 | mark); 31 | if (res < 0) { 32 | free (self); 33 | return NULL; 34 | } 35 | 36 | LOG_D ("%p socks5 user mark new", self); 37 | 38 | return self; 39 | } 40 | 41 | int 42 | hev_socks5_user_mark_construct (HevSocks5UserMark *self, const char *name, 43 | unsigned int name_len, const char *pass, 44 | unsigned int pass_len, unsigned int mark) 45 | { 46 | int res; 47 | 48 | res = 49 | hev_socks5_user_construct (&self->base, name, name_len, pass, pass_len); 50 | if (res < 0) 51 | return res; 52 | 53 | LOG_D ("%p socks5 user mark construct", self); 54 | 55 | HEV_OBJECT (self)->klass = HEV_SOCKS5_USER_MARK_TYPE; 56 | 57 | self->mark = mark; 58 | 59 | return 0; 60 | } 61 | 62 | static void 63 | hev_socks5_user_mark_destruct (HevObject *base) 64 | { 65 | HevSocks5UserMark *self = HEV_SOCKS5_USER_MARK (base); 66 | 67 | LOG_D ("%p socks5 user mark destruct", self); 68 | 69 | HEV_SOCKS5_USER_TYPE->destruct (base); 70 | } 71 | 72 | HevObjectClass * 73 | hev_socks5_user_mark_class (void) 74 | { 75 | static HevSocks5UserMarkClass klass; 76 | HevSocks5UserMarkClass *kptr = &klass; 77 | HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr); 78 | 79 | if (!okptr->name) { 80 | memcpy (kptr, HEV_SOCKS5_USER_TYPE, sizeof (HevSocks5UserClass)); 81 | 82 | okptr->name = "HevSocks5UserMark"; 83 | okptr->destruct = hev_socks5_user_mark_destruct; 84 | } 85 | 86 | return okptr; 87 | } 88 | -------------------------------------------------------------------------------- /src/misc/hev-logger.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-logger.c 4 | Author : Heiher 5 | Copyright : Copyright (c) 2019 - 2021 hev 6 | Description : Logger 7 | ============================================================================ 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "hev-logger.h" 20 | 21 | static int fd = -1; 22 | static HevLoggerLevel req_level; 23 | 24 | int 25 | hev_logger_init (HevLoggerLevel level, const char *path) 26 | { 27 | req_level = level; 28 | 29 | if (0 == strcmp (path, "stdout")) 30 | fd = dup (1); 31 | else if (0 == strcmp (path, "stderr")) 32 | fd = dup (2); 33 | else 34 | fd = open (path, O_WRONLY | O_APPEND | O_CREAT, 0640); 35 | 36 | if (fd < 0) 37 | return -1; 38 | 39 | return 0; 40 | } 41 | 42 | void 43 | hev_logger_fini (void) 44 | { 45 | close (fd); 46 | } 47 | 48 | int 49 | hev_logger_enabled (HevLoggerLevel level) 50 | { 51 | if (level >= req_level && fd >= 0) 52 | return 1; 53 | 54 | return 0; 55 | } 56 | 57 | void 58 | hev_logger_log (HevLoggerLevel level, const char *fmt, ...) 59 | { 60 | struct iovec iov[4]; 61 | const char *ts_fmt; 62 | char msg[1024]; 63 | struct tm *ti; 64 | char ts[32]; 65 | time_t now; 66 | va_list ap; 67 | int len; 68 | 69 | if (level < req_level || fd < 0) 70 | return; 71 | 72 | time (&now); 73 | ti = localtime (&now); 74 | 75 | ts_fmt = "[%04u-%02u-%02u %02u:%02u:%02u] "; 76 | len = snprintf (ts, sizeof (ts), ts_fmt, 1900 + ti->tm_year, 1 + ti->tm_mon, 77 | ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec); 78 | 79 | iov[0].iov_base = ts; 80 | iov[0].iov_len = len; 81 | 82 | switch (level) { 83 | case HEV_LOGGER_DEBUG: 84 | iov[1].iov_base = "[D] "; 85 | break; 86 | case HEV_LOGGER_INFO: 87 | iov[1].iov_base = "[I] "; 88 | break; 89 | case HEV_LOGGER_WARN: 90 | iov[1].iov_base = "[W] "; 91 | break; 92 | case HEV_LOGGER_ERROR: 93 | iov[1].iov_base = "[E] "; 94 | break; 95 | case HEV_LOGGER_UNSET: 96 | iov[1].iov_base = "[?] "; 97 | break; 98 | } 99 | iov[1].iov_len = 4; 100 | 101 | va_start (ap, fmt); 102 | iov[2].iov_base = msg; 103 | iov[2].iov_len = vsnprintf (msg, 1024, fmt, ap); 104 | va_end (ap); 105 | 106 | iov[3].iov_base = "\n"; 107 | iov[3].iov_len = 1; 108 | 109 | if (writev (fd, iov, 4)) { 110 | /* ignore return value */ 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/hev-socket-factory.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-socket-factory.c 4 | Author : Heiher 5 | Copyright : Copyright (c) 2022 hev 6 | Description : Socket Factory 7 | ============================================================================ 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "hev-misc.h" 20 | #include "hev-logger.h" 21 | 22 | #include "hev-socket-factory.h" 23 | 24 | struct _HevSocketFactory 25 | { 26 | struct sockaddr_in6 addr; 27 | int ipv6_only; 28 | int fd; 29 | }; 30 | 31 | HevSocketFactory * 32 | hev_socket_factory_new (const char *addr, const char *port, int ipv6_only) 33 | { 34 | HevSocketFactory *self; 35 | int res; 36 | 37 | LOG_D ("socket factory new"); 38 | 39 | self = hev_malloc0 (sizeof (HevSocketFactory)); 40 | if (!self) { 41 | LOG_E ("socket factory alloc"); 42 | return NULL; 43 | } 44 | 45 | res = hev_netaddr_resolve (&self->addr, addr, port); 46 | if (res < 0) { 47 | LOG_E ("socket factory resolve"); 48 | hev_free (self); 49 | return NULL; 50 | } 51 | 52 | self->ipv6_only = ipv6_only; 53 | self->fd = -1; 54 | 55 | return self; 56 | } 57 | 58 | void 59 | hev_socket_factory_destroy (HevSocketFactory *self) 60 | { 61 | LOG_D ("socket factory destroy"); 62 | 63 | if (self->fd >= 0) 64 | close (self->fd); 65 | hev_free (self); 66 | } 67 | 68 | int 69 | hev_socket_factory_get (HevSocketFactory *self) 70 | { 71 | int one = 1; 72 | int res; 73 | int fd; 74 | 75 | LOG_D ("socket factory get"); 76 | 77 | if (self->fd >= 0) 78 | return dup (self->fd); 79 | 80 | fd = hev_task_io_socket_socket (AF_INET6, SOCK_STREAM, 0); 81 | if (fd < 0) { 82 | LOG_E ("socket factory socket"); 83 | goto exit; 84 | } 85 | 86 | res = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one)); 87 | if (res < 0) { 88 | LOG_E ("socket factory reuse"); 89 | goto exit_close; 90 | } 91 | 92 | res = -1; 93 | #ifdef SO_REUSEPORT 94 | res = setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof (one)); 95 | #endif 96 | if (res < 0 && self->fd < 0) 97 | self->fd = dup (fd); 98 | 99 | res = setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &self->ipv6_only, 100 | sizeof (self->ipv6_only)); 101 | if (res < 0) { 102 | LOG_E ("socket factory ipv6 only"); 103 | goto exit_close; 104 | } 105 | 106 | res = bind (fd, (struct sockaddr *)&self->addr, sizeof (self->addr)); 107 | if (res < 0) { 108 | LOG_E ("socket factory bind"); 109 | goto exit_close; 110 | } 111 | 112 | res = listen (fd, 100); 113 | if (res < 0) { 114 | LOG_E ("socket factory listen"); 115 | goto exit_close; 116 | } 117 | 118 | return fd; 119 | 120 | exit_close: 121 | close (fd); 122 | exit: 123 | return -1; 124 | } 125 | -------------------------------------------------------------------------------- /src/misc/hev-compiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-compiler.h 4 | Author : Heiher 5 | Copyright : Copyright (c) 2019 everyone. 6 | Description : Compiler 7 | ============================================================================ 8 | */ 9 | 10 | #ifndef __HEV_COMPILER_H__ 11 | #define __HEV_COMPILER_H__ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #define ALIGN_UP(addr, align) \ 18 | ((addr + (typeof (addr))align - 1) & ~((typeof (addr))align - 1)) 19 | 20 | #define ALIGN_DOWN(addr, align) ((addr) & ~((typeof (addr))align - 1)) 21 | 22 | #ifndef ARRAY_SIZE 23 | #define ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0])) 24 | #endif 25 | 26 | #ifndef EXPORT_SYMBOL 27 | #define EXPORT_SYMBOL __attribute__ ((visibility ("default"))) 28 | #endif 29 | 30 | #define barrier() __asm__ __volatile__ ("" : : : "memory") 31 | 32 | static inline void 33 | __read_once_size (void *dst, const volatile void *src, int size) 34 | { 35 | switch (size) { 36 | case sizeof (char): 37 | *(char *)dst = *(volatile char *)src; 38 | break; 39 | case sizeof (short): 40 | *(short *)dst = *(volatile short *)src; 41 | break; 42 | case sizeof (int): 43 | *(int *)dst = *(volatile int *)src; 44 | break; 45 | default: 46 | barrier (); 47 | __builtin_memcpy ((void *)dst, (const void *)src, size); 48 | barrier (); 49 | } 50 | } 51 | 52 | static inline void 53 | __write_once_size (volatile void *dst, const void *src, int size) 54 | { 55 | switch (size) { 56 | case sizeof (char): 57 | *(volatile char *)dst = *(char *)src; 58 | break; 59 | case sizeof (short): 60 | *(volatile short *)dst = *(short *)src; 61 | break; 62 | case sizeof (int): 63 | *(volatile int *)dst = *(int *)src; 64 | break; 65 | default: 66 | barrier (); 67 | __builtin_memcpy ((void *)dst, (const void *)src, size); 68 | barrier (); 69 | } 70 | } 71 | 72 | #define READ_ONCE(x) \ 73 | ({ \ 74 | union \ 75 | { \ 76 | typeof (x) __val; \ 77 | char __c[1]; \ 78 | } __u; \ 79 | __read_once_size (__u.__c, &(x), sizeof (x)); \ 80 | __u.__val; \ 81 | }) 82 | 83 | #define WRITE_ONCE(x, val) \ 84 | ({ \ 85 | union \ 86 | { \ 87 | typeof (x) __val; \ 88 | char __c[1]; \ 89 | } __u = { .__val = (typeof (x))(val) }; \ 90 | __write_once_size (&(x), __u.__c, sizeof (x)); \ 91 | __u.__val; \ 92 | }) 93 | 94 | #ifndef container_of 95 | #define container_of(ptr, type, member) \ 96 | ({ \ 97 | void *__mptr = (void *)(ptr); \ 98 | ((type *)(__mptr - offsetof (type, member))); \ 99 | }) 100 | #endif 101 | 102 | #ifdef __cplusplus 103 | } 104 | #endif 105 | 106 | #endif /* __HEV_COMPILER_H__ */ 107 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlines: Left 8 | AlignOperands: true 9 | AlignTrailingComments: false 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: None 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: TopLevel 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: MultiLine 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BraceWrapping: 23 | AfterClass: true 24 | AfterControlStatement: false 25 | AfterEnum: true 26 | AfterFunction: true 27 | AfterNamespace: true 28 | AfterObjCDeclaration: false 29 | AfterStruct: true 30 | AfterUnion: true 31 | AfterExternBlock: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | SplitEmptyFunction: true 36 | SplitEmptyRecord: true 37 | SplitEmptyNamespace: true 38 | BreakBeforeBinaryOperators: None 39 | BreakBeforeBraces: Custom 40 | BreakBeforeInheritanceComma: false 41 | BreakInheritanceList: BeforeColon 42 | BreakBeforeTernaryOperators: false 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakAfterJavaFieldAnnotations: false 45 | BreakStringLiterals: false 46 | ColumnLimit: 80 47 | CommentPragmas: '^ IWYU pragma:' 48 | CompactNamespaces: false 49 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 50 | ConstructorInitializerIndentWidth: 4 51 | ContinuationIndentWidth: 4 52 | Cpp11BracedListStyle: false 53 | DerivePointerAlignment: false 54 | DisableFormat: false 55 | ExperimentalAutoDetectBinPacking: false 56 | FixNamespaceComments: false 57 | ForEachMacros: 58 | - foreach 59 | - Q_FOREACH 60 | - BOOST_FOREACH 61 | IncludeBlocks: Preserve 62 | IncludeCategories: 63 | - Regex: '.*' 64 | Priority: 1 65 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 66 | Priority: 3 67 | - Regex: '.*' 68 | Priority: 1 69 | IncludeIsMainRegex: '(Test)?$' 70 | IndentCaseLabels: false 71 | IndentPPDirectives: None 72 | IndentWidth: 4 73 | IndentWrappedFunctionNames: false 74 | JavaScriptQuotes: Leave 75 | JavaScriptWrapImports: true 76 | KeepEmptyLinesAtTheStartOfBlocks: false 77 | MacroBlockBegin: '' 78 | MacroBlockEnd: '' 79 | MaxEmptyLinesToKeep: 1 80 | NamespaceIndentation: Inner 81 | ObjCBinPackProtocolList: Auto 82 | ObjCBlockIndentWidth: 4 83 | ObjCSpaceAfterProperty: true 84 | ObjCSpaceBeforeProtocolList: true 85 | PenaltyBreakAssignment: 10 86 | PenaltyBreakBeforeFirstCallParameter: 30 87 | PenaltyBreakComment: 10 88 | PenaltyBreakFirstLessLess: 0 89 | PenaltyBreakString: 10 90 | PenaltyBreakTemplateDeclaration: 10 91 | PenaltyExcessCharacter: 100 92 | PenaltyReturnTypeOnItsOwnLine: 60 93 | PointerAlignment: Right 94 | ReflowComments: false 95 | SortIncludes: false 96 | SortUsingDeclarations: false 97 | SpaceAfterCStyleCast: false 98 | SpaceAfterTemplateKeyword: true 99 | SpaceBeforeAssignmentOperators: true 100 | SpaceBeforeCpp11BracedList: false 101 | SpaceBeforeCtorInitializerColon: true 102 | SpaceBeforeInheritanceColon: true 103 | SpaceBeforeParens: Always 104 | SpaceBeforeRangeBasedForLoopColon: true 105 | SpaceInEmptyParentheses: false 106 | SpacesBeforeTrailingComments: 1 107 | SpacesInAngles: false 108 | SpacesInContainerLiterals: false 109 | SpacesInCStyleCastParentheses: false 110 | SpacesInParentheses: false 111 | SpacesInSquareBrackets: false 112 | Standard: Cpp03 113 | TabWidth: 4 114 | UseTab: Never 115 | ... 116 | -------------------------------------------------------------------------------- /src/hev-jni.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-jni.c 4 | Author : hev 5 | Copyright : Copyright (c) 2019 - 2024 hev 6 | Description : Jave Native Interface 7 | ============================================================================ 8 | */ 9 | 10 | #ifdef ANDROID 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "hev-main.h" 21 | 22 | #include "hev-jni.h" 23 | 24 | /* clang-format off */ 25 | #ifndef PKGNAME 26 | #define PKGNAME hev/socks5 27 | #endif 28 | #ifndef CLSNAME 29 | #define CLSNAME Socks5Service 30 | #endif 31 | /* clang-format on */ 32 | 33 | #define STR(s) STR_ARG (s) 34 | #define STR_ARG(c) #c 35 | #define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) 36 | 37 | typedef struct _ThreadData ThreadData; 38 | 39 | struct _ThreadData 40 | { 41 | char *path; 42 | }; 43 | 44 | static int is_working; 45 | static JavaVM *java_vm; 46 | static pthread_t work_thread; 47 | static pthread_mutex_t mutex; 48 | static pthread_key_t current_jni_env; 49 | 50 | static void native_start_service (JNIEnv *env, jobject thiz, 51 | jstring config_path); 52 | static void native_stop_service (JNIEnv *env, jobject thiz); 53 | 54 | static JNINativeMethod native_methods[] = { 55 | { "Socks5StartService", "(Ljava/lang/String;)V", 56 | (void *)native_start_service }, 57 | { "Socks5StopService", "()V", (void *)native_stop_service }, 58 | }; 59 | 60 | static void 61 | detach_current_thread (void *env) 62 | { 63 | (*java_vm)->DetachCurrentThread (java_vm); 64 | } 65 | 66 | jint 67 | JNI_OnLoad (JavaVM *vm, void *reserved) 68 | { 69 | JNIEnv *env = NULL; 70 | jclass klass; 71 | 72 | java_vm = vm; 73 | if (JNI_OK != (*vm)->GetEnv (vm, (void **)&env, JNI_VERSION_1_4)) { 74 | return 0; 75 | } 76 | 77 | klass = (*env)->FindClass (env, STR (PKGNAME) "/" STR (CLSNAME)); 78 | (*env)->RegisterNatives (env, klass, native_methods, 79 | N_ELEMENTS (native_methods)); 80 | (*env)->DeleteLocalRef (env, klass); 81 | 82 | pthread_key_create (¤t_jni_env, detach_current_thread); 83 | pthread_mutex_init (&mutex, NULL); 84 | 85 | return JNI_VERSION_1_4; 86 | } 87 | 88 | static void * 89 | thread_handler (void *data) 90 | { 91 | ThreadData *tdata = data; 92 | 93 | hev_socks5_server_main_from_file (tdata->path); 94 | 95 | free (tdata->path); 96 | free (tdata); 97 | 98 | return NULL; 99 | } 100 | 101 | static void 102 | native_start_service (JNIEnv *env, jobject thiz, jstring config_path) 103 | { 104 | const jbyte *bytes; 105 | ThreadData *tdata; 106 | int res; 107 | 108 | pthread_mutex_lock (&mutex); 109 | 110 | if (is_working) 111 | goto exit; 112 | 113 | tdata = malloc (sizeof (ThreadData)); 114 | bytes = (const jbyte *)(*env)->GetStringUTFChars (env, config_path, NULL); 115 | tdata->path = strdup ((const char *)bytes); 116 | (*env)->ReleaseStringUTFChars (env, config_path, (const char *)bytes); 117 | 118 | res = pthread_create (&work_thread, NULL, thread_handler, tdata); 119 | if (res < 0) { 120 | free (tdata->path); 121 | free (tdata); 122 | goto exit; 123 | } 124 | 125 | is_working = 1; 126 | exit: 127 | pthread_mutex_unlock (&mutex); 128 | } 129 | 130 | static void 131 | native_stop_service (JNIEnv *env, jobject thiz) 132 | { 133 | pthread_mutex_lock (&mutex); 134 | 135 | if (!is_working) 136 | goto exit; 137 | 138 | hev_socks5_server_quit (); 139 | pthread_join (work_thread, NULL); 140 | 141 | is_working = 0; 142 | exit: 143 | pthread_mutex_unlock (&mutex); 144 | } 145 | 146 | #endif /* ANDROID */ 147 | -------------------------------------------------------------------------------- /src/hev-main.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-main.c 4 | Author : hev 5 | Copyright : Copyright (c) 2017 - 2024 hev 6 | Description : Main 7 | ============================================================================ 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "hev-misc.h" 19 | #include "hev-config.h" 20 | #include "hev-config-const.h" 21 | #include "hev-logger.h" 22 | #include "hev-socks5-proxy.h" 23 | 24 | #include "hev-main.h" 25 | 26 | #ifdef __MSYS__ 27 | #define WEAK 28 | #else 29 | #define WEAK __attribute__ ((weak)) 30 | #endif 31 | 32 | static void 33 | show_help (const char *self_path) 34 | { 35 | printf ("%s CONFIG_PATH\n", self_path); 36 | printf ("Version: %u.%u.%u %s\n", MAJOR_VERSION, MINOR_VERSION, 37 | MICRO_VERSION, COMMIT_ID); 38 | } 39 | 40 | static void 41 | sigint_handler (int signum) 42 | { 43 | hev_socks5_proxy_stop (); 44 | } 45 | 46 | static int 47 | hev_socks5_server_main_inner (void) 48 | { 49 | const char *pid_file; 50 | const char *log_file; 51 | int log_level; 52 | int timeout; 53 | int nofile; 54 | int res; 55 | 56 | log_file = hev_config_get_misc_log_file (); 57 | log_level = hev_config_get_misc_log_level (); 58 | 59 | timeout = hev_config_get_misc_connect_timeout (); 60 | hev_socks5_set_connect_timeout (timeout); 61 | timeout = hev_config_get_misc_tcp_read_write_timeout (); 62 | hev_socks5_set_tcp_timeout (timeout); 63 | timeout = hev_config_get_misc_udp_read_write_timeout (); 64 | hev_socks5_set_udp_timeout (timeout); 65 | 66 | res = hev_config_get_misc_task_stack_size (); 67 | hev_socks5_set_task_stack_size (res); 68 | 69 | res = hev_config_get_misc_udp_recv_buffer_size (); 70 | hev_socks5_set_udp_recv_buffer_size (res); 71 | 72 | res = hev_config_get_misc_udp_copy_buffer_nums (); 73 | hev_socks5_set_udp_copy_buffer_nums (res); 74 | 75 | res = hev_logger_init (log_level, log_file); 76 | if (res < 0) 77 | goto exit1; 78 | 79 | res = hev_socks5_logger_init (log_level, log_file); 80 | if (res < 0) 81 | goto exit2; 82 | 83 | nofile = hev_config_get_misc_limit_nofile (); 84 | res = set_limit_nofile (nofile); 85 | if (res < 0) 86 | LOG_W ("set limit nofile"); 87 | 88 | pid_file = hev_config_get_misc_pid_file (); 89 | if (pid_file) 90 | run_as_daemon (pid_file); 91 | 92 | res = hev_socks5_proxy_init (); 93 | if (res < 0) 94 | goto exit3; 95 | 96 | hev_socks5_proxy_run (); 97 | 98 | hev_socks5_proxy_fini (); 99 | exit3: 100 | hev_socks5_logger_fini (); 101 | exit2: 102 | hev_logger_fini (); 103 | exit1: 104 | hev_config_fini (); 105 | return res; 106 | } 107 | 108 | int 109 | hev_socks5_server_main_from_file (const char *config_path) 110 | { 111 | int res = hev_config_init_from_file (config_path); 112 | if (res < 0) 113 | return -1; 114 | 115 | return hev_socks5_server_main_inner (); 116 | } 117 | 118 | int 119 | hev_socks5_server_main_from_str (const unsigned char *config_str, 120 | unsigned int config_len) 121 | { 122 | int res = hev_config_init_from_str (config_str, config_len); 123 | if (res < 0) 124 | return -1; 125 | 126 | return hev_socks5_server_main_inner (); 127 | } 128 | 129 | void 130 | hev_socks5_server_quit (void) 131 | { 132 | hev_socks5_proxy_stop (); 133 | } 134 | 135 | WEAK int 136 | main (int argc, char *argv[]) 137 | { 138 | int res; 139 | 140 | if (argc < 2 || strcmp (argv[1], "--version") == 0) { 141 | show_help (argv[0]); 142 | return -1; 143 | } 144 | 145 | signal (SIGINT, sigint_handler); 146 | 147 | res = hev_socks5_server_main_from_file (argv[1]); 148 | if (res < 0) 149 | return -2; 150 | 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /src/misc/hev-misc.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-misc.c 4 | Authors : Heiher 5 | Copyright : Copyright (c) 2022 - 2024 everyone. 6 | Description : Misc 7 | ============================================================================ 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #if defined(__APPLE__) 18 | #include 19 | #include 20 | #include 21 | #endif 22 | 23 | #include 24 | 25 | #include "hev-logger.h" 26 | 27 | #include "hev-misc.h" 28 | 29 | int 30 | hev_netaddr_resolve (struct sockaddr_in6 *daddr, const char *addr, 31 | const char *port) 32 | { 33 | struct addrinfo hints = { 0 }; 34 | struct addrinfo *result; 35 | int res; 36 | 37 | hints.ai_family = AF_UNSPEC; 38 | hints.ai_socktype = SOCK_STREAM; 39 | hints.ai_flags = AI_PASSIVE; 40 | 41 | res = hev_task_dns_getaddrinfo (addr, port, &hints, &result); 42 | if (res < 0) 43 | return -1; 44 | 45 | if (result->ai_family == AF_INET) { 46 | struct sockaddr_in *adp; 47 | 48 | adp = (struct sockaddr_in *)result->ai_addr; 49 | daddr->sin6_family = AF_INET6; 50 | daddr->sin6_port = adp->sin_port; 51 | memset (&daddr->sin6_addr, 0, 10); 52 | daddr->sin6_addr.s6_addr[10] = 0xff; 53 | daddr->sin6_addr.s6_addr[11] = 0xff; 54 | memcpy (&daddr->sin6_addr.s6_addr[12], &adp->sin_addr, 4); 55 | } else if (result->ai_family == AF_INET6) { 56 | memcpy (daddr, result->ai_addr, sizeof (struct sockaddr_in6)); 57 | } 58 | 59 | freeaddrinfo (result); 60 | 61 | return 0; 62 | } 63 | 64 | int 65 | hev_netaddr_is_any (struct sockaddr_in6 *addr) 66 | { 67 | if (!IN6_IS_ADDR_V4MAPPED (&addr->sin6_addr)) 68 | return !memcmp (&addr->sin6_addr, &in6addr_any, 16); 69 | 70 | return !memcmp (&addr->sin6_addr.s6_addr[12], &in6addr_any, 4); 71 | } 72 | 73 | void 74 | run_as_daemon (const char *pid_file) 75 | { 76 | FILE *fp; 77 | 78 | fp = fopen (pid_file, "w+"); 79 | if (!fp) { 80 | LOG_E ("open pid file %s", pid_file); 81 | return; 82 | } 83 | 84 | #ifndef TARGET_OS_TV 85 | #pragma GCC diagnostic push 86 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 87 | if (daemon (0, 0)) { 88 | /* ignore return value */ 89 | } 90 | #pragma GCC diagnostic pop 91 | #endif 92 | 93 | fprintf (fp, "%u\n", getpid ()); 94 | fclose (fp); 95 | } 96 | 97 | int 98 | set_limit_nofile (int limit_nofile) 99 | { 100 | struct rlimit limit; 101 | int res; 102 | 103 | res = getrlimit (RLIMIT_NOFILE, &limit); 104 | if (res < 0) 105 | return -1; 106 | 107 | limit.rlim_cur = limit.rlim_max; 108 | res = setrlimit (RLIMIT_NOFILE, &limit); 109 | if (res < 0) 110 | return -1; 111 | 112 | limit.rlim_cur = limit_nofile; 113 | limit.rlim_max = limit_nofile; 114 | return setrlimit (RLIMIT_NOFILE, &limit); 115 | } 116 | 117 | int 118 | set_sock_bind (int fd, const char *iface) 119 | { 120 | int res = 0; 121 | 122 | #if defined(__linux__) 123 | struct ifreq ifr = { 0 }; 124 | 125 | strncpy (ifr.ifr_name, iface, sizeof (ifr.ifr_name) - 1); 126 | res = setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof (ifr)); 127 | #elif defined(__APPLE__) || defined(__MACH__) 128 | int i; 129 | 130 | i = if_nametoindex (iface); 131 | if (i == 0) { 132 | return -1; 133 | } 134 | 135 | res = setsockopt (fd, IPPROTO_IPV6, IPV6_BOUND_IF, &i, sizeof (i)); 136 | #endif 137 | 138 | return res; 139 | } 140 | 141 | int 142 | set_sock_mark (int fd, unsigned int mark) 143 | { 144 | #if defined(__linux__) 145 | return setsockopt (fd, SOL_SOCKET, SO_MARK, &mark, sizeof (mark)); 146 | #elif defined(__FreeBSD__) 147 | return setsockopt (fd, SOL_SOCKET, SO_USER_COOKIE, &mark, sizeof (mark)); 148 | #endif 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for hev-socks5-server 2 | 3 | PROJECT=hev-socks5-server 4 | 5 | CROSS_PREFIX := 6 | PP=$(CROSS_PREFIX)cpp 7 | CC=$(CROSS_PREFIX)gcc 8 | STRIP=$(CROSS_PREFIX)strip 9 | CCFLAGS=-O3 -pipe -Wall -Werror $(CFLAGS) \ 10 | -I$(SRCDIR)/misc \ 11 | -I$(SRCDIR)/core/include \ 12 | -I$(THIRDPARTDIR)/yaml/src \ 13 | -I$(THIRDPARTDIR)/hev-task-system/include 14 | LDFLAGS=-L$(THIRDPARTDIR)/yaml/bin -lyaml \ 15 | -L$(THIRDPARTDIR)/hev-task-system/bin -lhev-task-system 16 | 17 | SRCDIR=src 18 | BINDIR=bin 19 | CONFDIR=conf 20 | BUILDDIR=build 21 | INSTDIR=/usr/local 22 | THIRDPARTDIR=third-part 23 | 24 | CONFIG=$(CONFDIR)/main.yml 25 | EXEC_TARGET=$(BINDIR)/hev-socks5-server 26 | STATIC_TARGET=$(BINDIR)/lib$(PROJECT).a 27 | SHARED_TARGET=$(BINDIR)/lib$(PROJECT).so 28 | THIRDPARTS=$(THIRDPARTDIR)/yaml $(THIRDPARTDIR)/hev-task-system 29 | 30 | $(SHARED_TARGET) : CCFLAGS+=-fPIC 31 | $(SHARED_TARGET) : LDFLAGS+=-shared -pthread 32 | 33 | -include build.mk 34 | CCFLAGS+=$(VERSION_CFLAGS) 35 | CCSRCS=$(filter %.c,$(SRCFILES)) 36 | ASSRCS=$(filter %.S,$(SRCFILES)) 37 | LDOBJS=$(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(CCSRCS)) \ 38 | $(patsubst $(SRCDIR)/%.S,$(BUILDDIR)/%.o,$(ASSRCS)) 39 | DEPEND=$(LDOBJS:.o=.dep) 40 | 41 | BUILDMSG="\e[1;31mBUILD\e[0m %s\n" 42 | LINKMSG="\e[1;34mLINK\e[0m \e[1;32m%s\e[0m\n" 43 | STRIPMSG="\e[1;34mSTRIP\e[0m \e[1;32m%s\e[0m\n" 44 | CLEANMSG="\e[1;34mCLEAN\e[0m %s\n" 45 | INSTMSG="\e[1;34mINST\e[0m %s -> %s\n" 46 | UNINSMSG="\e[1;34mUNINS\e[0m %s\n" 47 | 48 | ifeq ($(MSYSTEM),MSYS) 49 | LDFLAGS+=-lmsys-2.0 -lws2_32 50 | endif 51 | 52 | ENABLE_DEBUG := 53 | ifeq ($(ENABLE_DEBUG),1) 54 | CCFLAGS+=-g -O0 -DENABLE_DEBUG 55 | STRIP=true 56 | endif 57 | 58 | ENABLE_STATIC := 59 | ifeq ($(ENABLE_STATIC),1) 60 | CCFLAGS+=-static 61 | endif 62 | 63 | LDFLAGS+=-lpthread $(LFLAGS) 64 | 65 | V := 66 | ECHO_PREFIX := @ 67 | ifeq ($(V),1) 68 | undefine ECHO_PREFIX 69 | endif 70 | 71 | .PHONY: exec static shared clean install uninstall tp-static tp-shared tp-clean 72 | 73 | exec : $(EXEC_TARGET) 74 | 75 | static : $(STATIC_TARGET) 76 | 77 | shared : $(SHARED_TARGET) 78 | 79 | tp-static : $(THIRDPARTS) 80 | @$(foreach dir,$^,$(MAKE) --no-print-directory -C $(dir) static;) 81 | 82 | tp-shared : $(THIRDPARTS) 83 | @$(foreach dir,$^,$(MAKE) --no-print-directory -C $(dir) shared;) 84 | 85 | tp-clean : $(THIRDPARTS) 86 | @$(foreach dir,$^,$(MAKE) --no-print-directory -C $(dir) clean;) 87 | 88 | clean : tp-clean 89 | $(ECHO_PREFIX) $(RM) -rf $(BINDIR) $(BUILDDIR) 90 | @printf $(CLEANMSG) $(PROJECT) 91 | 92 | install : $(INSTDIR)/bin/$(PROJECT) $(INSTDIR)/etc/$(PROJECT).yml 93 | 94 | uninstall : 95 | $(ECHO_PREFIX) $(RM) -rf $(INSTDIR)/bin/$(PROJECT) 96 | @printf $(UNINSMSG) $(INSTDIR)/bin/$(PROJECT) 97 | $(ECHO_PREFIX) $(RM) -rf $(INSTDIR)/etc/$(PROJECT).yml 98 | @printf $(UNINSMSG) $(INSTDIR)/etc/$(PROJECT).yml 99 | 100 | $(INSTDIR)/bin/$(PROJECT) : $(EXEC_TARGET) 101 | $(ECHO_PREFIX) install -d -m 0755 $(dir $@) 102 | $(ECHO_PREFIX) install -m 0755 $< $@ 103 | @printf $(INSTMSG) $< $@ 104 | 105 | $(INSTDIR)/etc/$(PROJECT).yml : $(CONFIG) 106 | $(ECHO_PREFIX) install -d -m 0755 $(dir $@) 107 | $(ECHO_PREFIX) install -m 0644 $< $@ 108 | @printf $(INSTMSG) $< $@ 109 | 110 | $(EXEC_TARGET) : $(LDOBJS) tp-static 111 | $(ECHO_PREFIX) mkdir -p $(dir $@) 112 | $(ECHO_PREFIX) $(CC) $(CCFLAGS) -o $@ $(LDOBJS) $(LDFLAGS) 113 | @printf $(LINKMSG) $@ 114 | $(ECHO_PREFIX) $(STRIP) $@ 115 | @printf $(STRIPMSG) $@ 116 | 117 | $(STATIC_TARGET) : $(LDOBJS) tp-static 118 | $(ECHO_PREFIX) mkdir -p $(dir $@) 119 | $(ECHO_PREFIX) $(AR) csq $@ $(LDOBJS) 120 | @printf $(LINKMSG) $@ 121 | 122 | $(SHARED_TARGET) : $(LDOBJS) tp-shared 123 | $(ECHO_PREFIX) mkdir -p $(dir $@) 124 | $(ECHO_PREFIX) $(CC) $(CCFLAGS) -o $@ $(LDOBJS) $(LDFLAGS) 125 | @printf $(LINKMSG) $@ 126 | 127 | $(BUILDDIR)/%.dep : $(SRCDIR)/%.c 128 | $(ECHO_PREFIX) mkdir -p $(dir $@) 129 | $(ECHO_PREFIX) $(PP) $(CCFLAGS) -MM -MT$(@:.dep=.o) -MF$@ $< 2>/dev/null 130 | 131 | $(BUILDDIR)/%.o : $(SRCDIR)/%.c 132 | $(ECHO_PREFIX) mkdir -p $(dir $@) 133 | $(ECHO_PREFIX) $(CC) $(CCFLAGS) -c -o $@ $< 134 | @printf $(BUILDMSG) $< 135 | 136 | ifneq ($(MAKECMDGOALS),clean) 137 | -include $(DEPEND) 138 | endif 139 | -------------------------------------------------------------------------------- /src/hev-socks5-session.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-socks5-session.c 4 | Author : Heiher 5 | Copyright : Copyright (c) 2017 - 2024 hev 6 | Description : Socks5 Session 7 | ============================================================================ 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "hev-misc.h" 16 | #include "hev-logger.h" 17 | #include "hev-config.h" 18 | #include "hev-socks5-user-mark.h" 19 | 20 | #include "hev-socks5-session.h" 21 | 22 | HevSocks5Session * 23 | hev_socks5_session_new (int fd) 24 | { 25 | HevSocks5Session *self; 26 | int res; 27 | 28 | self = hev_malloc0 (sizeof (HevSocks5Session)); 29 | if (!self) 30 | return NULL; 31 | 32 | res = hev_socks5_session_construct (self, fd); 33 | if (res < 0) { 34 | hev_free (self); 35 | return NULL; 36 | } 37 | 38 | LOG_D ("%p socks5 session new", self); 39 | 40 | return self; 41 | } 42 | 43 | void 44 | hev_socks5_session_terminate (HevSocks5Session *self) 45 | { 46 | LOG_D ("%p socks5 session terminate", self); 47 | 48 | hev_socks5_set_timeout (HEV_SOCKS5 (self), 0); 49 | hev_task_wakeup (self->task); 50 | } 51 | 52 | static int 53 | hev_socks5_session_bind (HevSocks5 *self, int fd, const struct sockaddr *dest) 54 | { 55 | HevSocks5Server *srv = HEV_SOCKS5_SERVER (self); 56 | const char *saddr; 57 | const char *iface; 58 | int mark = 0; 59 | int family; 60 | int res; 61 | 62 | LOG_D ("%p socks5 session bind", self); 63 | 64 | if (IN6_IS_ADDR_V4MAPPED (&((struct sockaddr_in6 *)dest)->sin6_addr)) 65 | family = AF_INET; 66 | else 67 | family = AF_INET6; 68 | 69 | saddr = hev_config_get_bind_address (family); 70 | iface = hev_config_get_bind_interface (); 71 | 72 | if (saddr) { 73 | struct sockaddr_in6 addr; 74 | 75 | res = hev_netaddr_resolve (&addr, saddr, NULL); 76 | if (res < 0) 77 | return -1; 78 | 79 | res = bind (fd, (struct sockaddr *)&addr, sizeof (addr)); 80 | if (res < 0) 81 | return -1; 82 | } 83 | 84 | if (iface) { 85 | res = set_sock_bind (fd, iface); 86 | if (res < 0) 87 | return -1; 88 | } 89 | 90 | if (srv->user) { 91 | HevSocks5UserMark *user = HEV_SOCKS5_USER_MARK (srv->user); 92 | mark = user->mark; 93 | } 94 | 95 | if (!mark) 96 | mark = hev_config_get_socket_mark (); 97 | 98 | if (mark) { 99 | res = set_sock_mark (fd, mark); 100 | if (res < 0) 101 | return -1; 102 | } 103 | 104 | return 0; 105 | } 106 | 107 | static int 108 | hev_socks5_session_udp_bind (HevSocks5Server *self, int sock, 109 | struct sockaddr_in6 *src) 110 | { 111 | struct sockaddr_in6 *dst = src; 112 | struct sockaddr_in6 addr; 113 | const char *saddr; 114 | socklen_t alen; 115 | int ipv6_only; 116 | int one = 1; 117 | int family; 118 | int sport; 119 | int res; 120 | int fd; 121 | 122 | LOG_D ("%p socks5 session udp bind", self); 123 | 124 | fd = HEV_SOCKS5 (self)->fd; 125 | saddr = hev_config_get_udp_listen_address (); 126 | sport = hev_config_get_udp_listen_port (); 127 | ipv6_only = hev_config_get_listen_ipv6_only (); 128 | 129 | #ifdef SO_REUSEPORT 130 | res = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT, &one, sizeof (one)); 131 | if (res < 0) 132 | return -1; 133 | #endif 134 | 135 | if (ipv6_only) { 136 | res = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof (one)); 137 | if (res < 0) 138 | return -1; 139 | } 140 | 141 | alen = sizeof (struct sockaddr_in6); 142 | if (saddr) 143 | res = hev_netaddr_resolve (&addr, saddr, NULL); 144 | else 145 | res = getsockname (fd, (struct sockaddr *)&addr, &alen); 146 | if (res < 0) 147 | return -1; 148 | 149 | addr.sin6_port = htons (sport); 150 | res = bind (sock, (struct sockaddr *)&addr, sizeof (struct sockaddr_in6)); 151 | if (res < 0) 152 | return -1; 153 | 154 | if (hev_netaddr_is_any (dst)) { 155 | alen = sizeof (struct sockaddr_in6); 156 | res = getpeername (fd, (struct sockaddr *)&addr, &alen); 157 | if (res < 0) 158 | return -1; 159 | 160 | addr.sin6_port = dst->sin6_port; 161 | dst = &addr; 162 | } 163 | 164 | res = connect (sock, (struct sockaddr *)dst, sizeof (struct sockaddr_in6)); 165 | if (res < 0) 166 | return -1; 167 | 168 | HEV_SOCKS5 (self)->udp_associated = !!dst->sin6_port; 169 | 170 | alen = sizeof (struct sockaddr_in6); 171 | res = getsockname (sock, (struct sockaddr *)src, &alen); 172 | if (res < 0) 173 | return -1; 174 | 175 | if (IN6_IS_ADDR_V4MAPPED (&src->sin6_addr)) 176 | family = AF_INET; 177 | else 178 | family = AF_INET6; 179 | 180 | saddr = hev_config_get_udp_public_address (family); 181 | if (saddr) { 182 | sport = src->sin6_port; 183 | res = hev_netaddr_resolve (src, saddr, NULL); 184 | src->sin6_port = sport; 185 | if (res < 0) 186 | return -1; 187 | } 188 | 189 | return 0; 190 | } 191 | 192 | int 193 | hev_socks5_session_construct (HevSocks5Session *self, int fd) 194 | { 195 | int addr_family; 196 | int res; 197 | 198 | res = hev_socks5_server_construct (&self->base, fd); 199 | if (res < 0) 200 | return -1; 201 | 202 | LOG_D ("%p socks5 session construct", self); 203 | 204 | HEV_OBJECT (self)->klass = HEV_SOCKS5_SESSION_TYPE; 205 | 206 | addr_family = hev_config_get_address_family (); 207 | hev_socks5_set_addr_family (HEV_SOCKS5 (self), addr_family); 208 | 209 | return 0; 210 | } 211 | 212 | static void 213 | hev_socks5_session_destruct (HevObject *base) 214 | { 215 | HevSocks5Session *self = HEV_SOCKS5_SESSION (base); 216 | 217 | LOG_D ("%p socks5 session destruct", self); 218 | 219 | HEV_SOCKS5_SERVER_TYPE->destruct (base); 220 | } 221 | 222 | HevObjectClass * 223 | hev_socks5_session_class (void) 224 | { 225 | static HevSocks5SessionClass klass; 226 | HevSocks5SessionClass *kptr = &klass; 227 | HevObjectClass *okptr = HEV_OBJECT_CLASS (kptr); 228 | 229 | if (!okptr->name) { 230 | HevSocks5ServerClass *sskptr; 231 | HevSocks5Class *skptr; 232 | 233 | memcpy (kptr, HEV_SOCKS5_SERVER_TYPE, sizeof (HevSocks5ServerClass)); 234 | 235 | okptr->name = "HevSocks5Session"; 236 | okptr->destruct = hev_socks5_session_destruct; 237 | 238 | skptr = HEV_SOCKS5_CLASS (kptr); 239 | skptr->binder = hev_socks5_session_bind; 240 | 241 | sskptr = HEV_SOCKS5_SERVER_CLASS (kptr); 242 | sskptr->binder = hev_socks5_session_udp_bind; 243 | } 244 | 245 | return okptr; 246 | } 247 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HevSocks5Server 2 | 3 | [![status](https://github.com/heiher/hev-socks5-server/actions/workflows/build.yaml/badge.svg?branch=main&event=push)](https://github.com/heiher/hev-socks5-server) 4 | 5 | HevSocks5Server is a simple, lightweight socks5 server. 6 | 7 | ## Features 8 | 9 | * IPv4/IPv6. (dual stack) 10 | * Standard `CONNECT` command. 11 | * Standard `UDP ASSOCIATE` command. [^1] 12 | * Extended `FWD UDP` command. (UDP in TCP) [^2] 13 | * Multiple username/password authentication. 14 | 15 | ## Benchmarks 16 | 17 | See [here](https://github.com/heiher/hev-socks5-server/wiki/Benchmarks) for more details. 18 | 19 | ### Speed 20 | 21 | ![](https://github.com/heiher/hev-socks5-server/wiki/res/upload-speed.png) 22 | ![](https://github.com/heiher/hev-socks5-server/wiki/res/download-speed.png) 23 | 24 | ### CPU usage 25 | 26 | ![](https://github.com/heiher/hev-socks5-server/wiki/res/upload-cpu.png) 27 | ![](https://github.com/heiher/hev-socks5-server/wiki/res/download-cpu.png) 28 | 29 | ### Memory usage 30 | 31 | ![](https://github.com/heiher/hev-socks5-server/wiki/res/upload-mem.png) 32 | ![](https://github.com/heiher/hev-socks5-server/wiki/res/download-mem.png) 33 | 34 | ## How to Build 35 | 36 | ### Unix 37 | 38 | ```bash 39 | git clone --recursive https://github.com/heiher/hev-socks5-server 40 | cd hev-socks5-server 41 | make 42 | 43 | # statically link 44 | make ENABLE_STATIC=1 45 | ``` 46 | 47 | ### Android 48 | 49 | ```bash 50 | mkdir hev-socks5-server 51 | cd hev-socks5-server 52 | git clone --recursive https://github.com/heiher/hev-socks5-server jni 53 | cd jni 54 | ndk-build 55 | ``` 56 | 57 | ### iOS and MacOS 58 | 59 | ```bash 60 | git clone --recursive https://github.com/heiher/hev-socks5-server 61 | cd hev-socks5-server 62 | # will generate HevSocks5Server.xcframework 63 | ./build-apple.sh 64 | ``` 65 | 66 | ### Windows (MSYS2) 67 | 68 | ```bash 69 | export MSYS=winsymlinks:native 70 | git clone --recursive https://github.com/heiher/hev-socks5-server 71 | cd hev-socks5-server 72 | make 73 | ``` 74 | 75 | ## How to Use 76 | 77 | ### Config 78 | 79 | ```yaml 80 | main: 81 | # Worker threads 82 | workers: 4 83 | # Listen port 84 | port: 1080 85 | # Listen address (ipv4|ipv6) 86 | listen-address: '::' 87 | # UDP listen port (0: random, a-b: range) 88 | # udp-port: 0 89 | # UDP listen address (ipv4|ipv6) 90 | # udp-listen-address: '::1' 91 | # UDP public address (ipv4) 92 | # udp-public-address-v4: '' 93 | # UDP public address (ipv6) 94 | # udp-public-address-v6: '' 95 | # Listen ipv6 only 96 | listen-ipv6-only: false 97 | # Bind source address (ipv4|ipv6) 98 | # It is overridden by bind-address-v{4,6} if specified 99 | bind-address: '' 100 | # Bind source address (ipv4) 101 | bind-address-v4: '' 102 | # Bind source address (ipv6) 103 | bind-address-v6: '' 104 | # Bind source network interface 105 | bind-interface: '' 106 | # Domain address type (ipv4|ipv6|unspec) 107 | domain-address-type: unspec 108 | # Socket mark (hex: 0x1, dec: 1, oct: 01) 109 | mark: 0 110 | 111 | #auth: 112 | # file: conf/auth.txt 113 | # username: 114 | # password: 115 | 116 | #misc: 117 | # task stack size (bytes) 118 | # task-stack-size: 8192 119 | # udp socket recv buffer (SO_RCVBUF) size (bytes) 120 | # udp-recv-buffer-size: 524288 121 | # number of udp buffers in splice, 1500 bytes per buffer. 122 | # udp-copy-buffer-nums: 10 123 | # TCP connect timeout (ms) 124 | # connect-timeout: 10000 125 | # TCP read-write timeout (ms) 126 | # tcp-read-write-timeout: 300000 127 | # UDP read-write timeout (ms) 128 | # udp-read-write-timeout: 60000 129 | # stdout, stderr or file-path 130 | # log-file: stderr 131 | # debug, info, warn or error 132 | # log-level: warn 133 | # If present, run as a daemon with this pid file 134 | # pid-file: /run/hev-socks5-server.pid 135 | # If present, set rlimit nofile; else use default value 136 | # limit-nofile: 65535 137 | ``` 138 | 139 | ### Authentication file 140 | 141 | ``` 142 | 143 | ``` 144 | 145 | - USERNAME: A string of up to 255 characters 146 | - PASSWORD: A string of up to 255 characters 147 | - MARK: Hexadecimal 148 | 149 | ### Run 150 | 151 | ```bash 152 | bin/hev-socks5-server conf/main.yml 153 | ``` 154 | 155 | ### Live updating authentication file 156 | 157 | Send signal `SIGUSR1` to socks5 server process after the authentication file is updated. 158 | 159 | ```bash 160 | killall -SIGUSR1 hev-socks5-server 161 | ``` 162 | 163 | ### Limit number of connections 164 | 165 | For example, limit the number of connections for `jerry` up to `2`: 166 | 167 | #### Config 168 | 169 | ```yaml 170 | auth: 171 | file: conf/auth.txt 172 | ``` 173 | 174 | #### Auth file 175 | 176 | ``` 177 | jerry pass 1a 178 | ``` 179 | 180 | #### IPtables 181 | 182 | ```bash 183 | iptables -A OUTPUT -p tcp --syn -m mark --mark 0x1a -m connlimit --connlimit-above 2 -j REJECT 184 | ``` 185 | 186 | #### OpenWrt 23.05+ 187 | 188 | Repo: https://github.com/openwrt/packages/tree/master/net/hev-socks5-server 189 | 190 | ```sh 191 | # Install package 192 | opkg install hev-socks5-server 193 | 194 | # Edit /etc/config/hev-socks5-server 195 | 196 | # Restart service 197 | /etc/init.d/hev-socks5-server restart 198 | ``` 199 | 200 | ## API 201 | 202 | ```c 203 | /** 204 | * hev_socks5_server_main_from_file: 205 | * @config_path: config file path 206 | * 207 | * Start and run the socks5 server, this function will blocks until the 208 | * hev_socks5_server_quit is called or an error occurs. 209 | * 210 | * Returns: returns zero on successful, otherwise returns -1. 211 | * 212 | * Since: 2.6.7 213 | */ 214 | int hev_socks5_server_main_from_file (const char *config_path); 215 | 216 | /** 217 | * hev_socks5_server_main_from_str: 218 | * @config_str: string config 219 | * @config_len: the byte length of string config 220 | * 221 | * Start and run the socks5 server, this function will blocks until the 222 | * hev_socks5_server_quit is called or an error occurs. 223 | * 224 | * Returns: returns zero on successful, otherwise returns -1. 225 | * 226 | * Since: 2.6.7 227 | */ 228 | int hev_socks5_server_main_from_str (const unsigned char *config_str, 229 | unsigned int config_len); 230 | 231 | /** 232 | * hev_socks5_server_quit: 233 | * 234 | * Stop the socks5 server. 235 | * 236 | * Since: 2.6.7 237 | */ 238 | void hev_socks5_server_quit (void); 239 | ``` 240 | 241 | ## Use Cases 242 | 243 | ### Android App 244 | 245 | * [Socks5](https://github.com/heiher/socks5) 246 | 247 | ### iOS App 248 | 249 | * [Socks5](https://github.com/heiher/socks5-ios) 250 | 251 | ## Contributors 252 | 253 | * **ammar faizi** - https://github.com/ammarfaizi2 254 | * **hev** - https://hev.cc 255 | * **pexcn** - 256 | 257 | ## License 258 | 259 | MIT 260 | 261 | [^1]: Windows is not supported at this time. 262 | [^2]: See [protocol specification](https://github.com/heiher/hev-socks5-core/tree/main?tab=readme-ov-file#udp-in-tcp). The [hev-socks5-tunnel](https://github.com/heiher/hev-socks5-tunnel) and [hev-socks5-tproxy](https://github.com/heiher/hev-socks5-tproxy) clients support UDP relay over TCP. 263 | -------------------------------------------------------------------------------- /src/hev-socks5-worker.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-socks5-worker.c 4 | Author : Heiher 5 | Copyright : Copyright (c) 2017 - 2024 hev 6 | Description : Socks5 Worker 7 | ============================================================================ 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "hev-config.h" 21 | #include "hev-logger.h" 22 | #include "hev-compiler.h" 23 | #include "hev-socks5-session.h" 24 | 25 | #include "hev-socks5-worker.h" 26 | 27 | struct _HevSocks5Worker 28 | { 29 | int fd; 30 | int quit; 31 | int event_fds[2]; 32 | 33 | HevTask *task_event; 34 | HevTask *task_worker; 35 | HevList session_set; 36 | HevSocks5Authenticator *auth_curr; 37 | HevSocks5Authenticator *auth_next; 38 | }; 39 | 40 | static int 41 | task_io_yielder (HevTaskYieldType type, void *data) 42 | { 43 | HevSocks5Worker *self = data; 44 | 45 | hev_task_yield (type); 46 | 47 | return self->quit ? -1 : 0; 48 | } 49 | 50 | static void 51 | hev_socks5_worker_load (HevSocks5Worker *self) 52 | { 53 | atomic_intptr_t *ptr; 54 | intptr_t prev; 55 | 56 | LOG_D ("%p works worker load", self); 57 | 58 | ptr = (atomic_intptr_t *)&self->auth_next; 59 | prev = atomic_exchange_explicit (ptr, 0, memory_order_relaxed); 60 | if (!prev) 61 | return; 62 | 63 | if (self->auth_curr) 64 | hev_object_unref (HEV_OBJECT (self->auth_curr)); 65 | 66 | self->auth_curr = HEV_SOCKS5_AUTHENTICATOR (prev); 67 | } 68 | 69 | static void 70 | hev_socks5_session_task_entry (void *data) 71 | { 72 | HevSocks5Session *s = data; 73 | HevSocks5Worker *self = s->data; 74 | 75 | hev_socks5_server_run (HEV_SOCKS5_SERVER (s)); 76 | 77 | hev_list_del (&self->session_set, &s->node); 78 | hev_object_unref (HEV_OBJECT (s)); 79 | } 80 | 81 | static void 82 | hev_socks5_worker_task_entry (void *data) 83 | { 84 | HevTask *task = hev_task_self (); 85 | HevSocks5Worker *self = data; 86 | HevListNode *node; 87 | int stack_size; 88 | int fd; 89 | 90 | LOG_D ("socks5 worker task run"); 91 | 92 | fd = self->fd; 93 | hev_task_add_fd (task, fd, POLLIN); 94 | stack_size = hev_config_get_misc_task_stack_size (); 95 | 96 | for (;;) { 97 | HevSocks5Session *s; 98 | HevTask *task; 99 | int nfd; 100 | 101 | nfd = hev_task_io_socket_accept (fd, NULL, NULL, task_io_yielder, self); 102 | if (nfd == -1) { 103 | LOG_E ("socks5 proxy accept"); 104 | continue; 105 | } else if (nfd < 0) { 106 | break; 107 | } 108 | 109 | s = hev_socks5_session_new (nfd); 110 | if (!s) { 111 | close (nfd); 112 | continue; 113 | } 114 | 115 | task = hev_task_new (stack_size); 116 | if (!task) { 117 | hev_object_unref (HEV_OBJECT (s)); 118 | continue; 119 | } 120 | 121 | if (self->auth_curr) 122 | hev_socks5_server_set_auth (HEV_SOCKS5_SERVER (s), self->auth_curr); 123 | 124 | s->task = task; 125 | s->data = self; 126 | hev_list_add_tail (&self->session_set, &s->node); 127 | hev_task_run (task, hev_socks5_session_task_entry, s); 128 | } 129 | 130 | node = hev_list_first (&self->session_set); 131 | for (; node; node = hev_list_node_next (node)) { 132 | HevSocks5Session *s; 133 | 134 | s = container_of (node, HevSocks5Session, node); 135 | hev_socks5_session_terminate (s); 136 | } 137 | 138 | hev_task_del_fd (task, fd); 139 | } 140 | 141 | static void 142 | hev_socks5_event_task_entry (void *data) 143 | { 144 | HevTask *task = hev_task_self (); 145 | HevSocks5Worker *self = data; 146 | int res; 147 | 148 | LOG_D ("socks5 event task run"); 149 | 150 | res = hev_task_io_pipe_pipe (self->event_fds); 151 | if (res < 0) { 152 | LOG_E ("socks5 proxy pipe"); 153 | return; 154 | } 155 | 156 | hev_task_add_fd (task, self->event_fds[0], POLLIN); 157 | 158 | for (;;) { 159 | char val; 160 | 161 | res = hev_task_io_read (self->event_fds[0], &val, sizeof (val), NULL, 162 | NULL); 163 | if (res < sizeof (val)) 164 | continue; 165 | 166 | if (val == 'r') 167 | hev_socks5_worker_load (self); 168 | else 169 | break; 170 | } 171 | 172 | self->quit = 1; 173 | hev_task_wakeup (self->task_worker); 174 | 175 | hev_task_del_fd (task, self->event_fds[0]); 176 | close (self->event_fds[0]); 177 | close (self->event_fds[1]); 178 | } 179 | 180 | HevSocks5Worker * 181 | hev_socks5_worker_new (void) 182 | { 183 | HevSocks5Worker *self; 184 | 185 | self = calloc (1, sizeof (HevSocks5Worker)); 186 | if (!self) 187 | return NULL; 188 | 189 | LOG_D ("%p socks5 worker new", self); 190 | 191 | self->fd = -1; 192 | self->event_fds[0] = -1; 193 | self->event_fds[1] = -1; 194 | 195 | return self; 196 | } 197 | 198 | void 199 | hev_socks5_worker_destroy (HevSocks5Worker *self) 200 | { 201 | LOG_D ("%p works worker destroy", self); 202 | 203 | if (self->auth_curr) 204 | hev_object_unref (HEV_OBJECT (self->auth_curr)); 205 | if (self->auth_next) 206 | hev_object_unref (HEV_OBJECT (self->auth_next)); 207 | 208 | if (self->fd >= 0) 209 | close (self->fd); 210 | 211 | free (self); 212 | } 213 | 214 | int 215 | hev_socks5_worker_init (HevSocks5Worker *self, int fd) 216 | { 217 | LOG_D ("%p works worker init", self); 218 | 219 | self->task_worker = hev_task_new (-1); 220 | if (!self->task_worker) { 221 | LOG_E ("socks5 worker task worker"); 222 | return -1; 223 | } 224 | 225 | self->task_event = hev_task_new (-1); 226 | if (!self->task_event) { 227 | LOG_E ("socks5 worker task event"); 228 | hev_task_unref (self->task_worker); 229 | return -1; 230 | } 231 | 232 | self->fd = fd; 233 | 234 | return 0; 235 | } 236 | 237 | void 238 | hev_socks5_worker_start (HevSocks5Worker *self) 239 | { 240 | LOG_D ("%p works worker start", self); 241 | 242 | hev_task_run (self->task_event, hev_socks5_event_task_entry, self); 243 | hev_task_run (self->task_worker, hev_socks5_worker_task_entry, self); 244 | } 245 | 246 | void 247 | hev_socks5_worker_stop (HevSocks5Worker *self) 248 | { 249 | char val = 's'; 250 | 251 | LOG_D ("%p works worker stop", self); 252 | 253 | if (self->event_fds[1] < 0) 254 | return; 255 | 256 | val = write (self->event_fds[1], &val, sizeof (val)); 257 | if (val < 0) 258 | LOG_E ("socks5 proxy write event"); 259 | } 260 | 261 | void 262 | hev_socks5_worker_reload (HevSocks5Worker *self) 263 | { 264 | char val = 'r'; 265 | 266 | LOG_D ("%p works worker reload", self); 267 | 268 | if (self->event_fds[1] < 0) 269 | return; 270 | 271 | val = write (self->event_fds[1], &val, sizeof (val)); 272 | if (val < 0) 273 | LOG_E ("socks5 proxy write event"); 274 | } 275 | 276 | void 277 | hev_socks5_worker_set_auth (HevSocks5Worker *self, HevSocks5Authenticator *auth) 278 | { 279 | atomic_intptr_t *ptr; 280 | intptr_t prev; 281 | 282 | LOG_D ("%p works worker set auth", self); 283 | 284 | hev_object_ref (HEV_OBJECT (auth)); 285 | 286 | if (self->auth_curr) 287 | ptr = (atomic_intptr_t *)&self->auth_next; 288 | else 289 | ptr = (atomic_intptr_t *)&self->auth_curr; 290 | 291 | prev = atomic_exchange (ptr, (intptr_t)auth); 292 | if (prev) 293 | hev_object_unref (HEV_OBJECT (prev)); 294 | } 295 | -------------------------------------------------------------------------------- /src/hev-socks5-proxy.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-socks5-proxy.c 4 | Author : Heiher 5 | Copyright : Copyright (c) 2017 - 2024 hev 6 | Description : Socks5 Proxy 7 | ============================================================================ 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "hev-config.h" 23 | #include "hev-logger.h" 24 | #include "hev-socks5-worker.h" 25 | #include "hev-socket-factory.h" 26 | #include "hev-socks5-user-mark.h" 27 | 28 | #include "hev-socks5-proxy.h" 29 | 30 | static int listen_fd = -1; 31 | static unsigned int workers; 32 | 33 | static HevTask *task; 34 | static pthread_t *work_threads; 35 | static HevSocketFactory *factory; 36 | static HevSocks5Worker **worker_list; 37 | 38 | static void 39 | hev_socks5_proxy_load_file (HevSocks5Authenticator *auth, const char *file) 40 | { 41 | char *line = NULL; 42 | size_t len = 0; 43 | ssize_t nread; 44 | FILE *fp; 45 | 46 | fp = fopen (file, "r"); 47 | if (!fp) { 48 | hev_object_unref (HEV_OBJECT (auth)); 49 | return; 50 | } 51 | 52 | while ((nread = getline (&line, &len, fp)) != -1) { 53 | HevSocks5UserMark *user; 54 | unsigned int nlen; 55 | unsigned int plen; 56 | char name[256]; 57 | char pass[256]; 58 | long mark = 0; 59 | int res; 60 | 61 | res = sscanf (line, "%255s %255s %lx\n", name, pass, &mark); 62 | if (res < 2) { 63 | LOG_E ("socks5 proxy user/pass format"); 64 | continue; 65 | } 66 | 67 | nlen = strlen (name); 68 | plen = strlen (pass); 69 | user = hev_socks5_user_mark_new (name, nlen, pass, plen, mark); 70 | if (!user) { 71 | LOG_E ("socks5 proxy user new"); 72 | continue; 73 | } 74 | res = hev_socks5_authenticator_add (auth, HEV_SOCKS5_USER (user)); 75 | if (res < 0) { 76 | LOG_E ("socks5 proxy user conflict"); 77 | hev_object_unref (HEV_OBJECT (user)); 78 | } 79 | } 80 | 81 | free (line); 82 | fclose (fp); 83 | } 84 | 85 | static void 86 | hev_socks5_proxy_load (void) 87 | { 88 | HevSocks5Authenticator *auth; 89 | const char *file, *name, *pass; 90 | int i; 91 | 92 | LOG_D ("socks5 proxy load"); 93 | 94 | file = hev_config_get_auth_file (); 95 | name = hev_config_get_auth_username (); 96 | pass = hev_config_get_auth_password (); 97 | 98 | if (!file && !name && !pass) 99 | return; 100 | 101 | auth = hev_socks5_authenticator_new (); 102 | if (!auth) 103 | return; 104 | 105 | if (file) { 106 | hev_socks5_proxy_load_file (auth, file); 107 | } else { 108 | HevSocks5UserMark *user; 109 | 110 | user = hev_socks5_user_mark_new (name, strlen (name), pass, 111 | strlen (pass), 0); 112 | if (user) 113 | hev_socks5_authenticator_add (auth, HEV_SOCKS5_USER (user)); 114 | } 115 | 116 | for (i = 0; i < workers; i++) { 117 | HevSocks5Worker *worker; 118 | 119 | worker = worker_list[i]; 120 | hev_socks5_worker_set_auth (worker, auth); 121 | hev_socks5_worker_reload (worker); 122 | } 123 | 124 | hev_object_unref (HEV_OBJECT (auth)); 125 | } 126 | 127 | static void 128 | sigint_handler (int signum) 129 | { 130 | hev_socks5_proxy_load (); 131 | } 132 | 133 | int 134 | hev_socks5_proxy_init (void) 135 | { 136 | LOG_D ("socks5 proxy init"); 137 | 138 | if (hev_task_system_init () < 0) { 139 | LOG_E ("socks5 proxy task system"); 140 | goto exit; 141 | } 142 | 143 | task = hev_task_new (-1); 144 | if (!task) { 145 | LOG_E ("socks5 proxy task"); 146 | goto exit; 147 | } 148 | 149 | workers = hev_config_get_workers (); 150 | work_threads = hev_malloc0 (sizeof (pthread_t) * workers); 151 | if (!work_threads) { 152 | LOG_E ("socks5 proxy work threads"); 153 | goto exit; 154 | } 155 | 156 | worker_list = hev_malloc0 (sizeof (HevSocks5Worker *) * workers); 157 | if (!worker_list) { 158 | LOG_E ("socks5 proxy worker list"); 159 | goto exit; 160 | } 161 | 162 | factory = hev_socket_factory_new (hev_config_get_listen_address (), 163 | hev_config_get_listen_port (), 164 | hev_config_get_listen_ipv6_only ()); 165 | if (!factory) { 166 | LOG_E ("socks5 proxy socket factory"); 167 | goto exit; 168 | } 169 | 170 | signal (SIGPIPE, SIG_IGN); 171 | signal (SIGUSR1, sigint_handler); 172 | 173 | return 0; 174 | 175 | exit: 176 | hev_socks5_proxy_fini (); 177 | return -1; 178 | } 179 | 180 | void 181 | hev_socks5_proxy_fini (void) 182 | { 183 | LOG_D ("socks5 proxy fini"); 184 | 185 | if (task) 186 | hev_task_unref (task); 187 | if (work_threads) 188 | hev_free (work_threads); 189 | if (worker_list) 190 | hev_free (worker_list); 191 | if (factory) 192 | hev_socket_factory_destroy (factory); 193 | hev_task_system_fini (); 194 | } 195 | 196 | static void * 197 | work_thread_handler (void *data) 198 | { 199 | HevSocks5Worker **worker = data; 200 | int res; 201 | int fd; 202 | 203 | if (hev_task_system_init () < 0) { 204 | LOG_E ("socks5 proxy worker task system"); 205 | goto exit; 206 | } 207 | 208 | fd = hev_socket_factory_get (factory); 209 | if (fd < 0) { 210 | LOG_E ("socks5 proxy worker socket"); 211 | goto free; 212 | } 213 | 214 | res = hev_socks5_worker_init (*worker, fd); 215 | if (res < 0) { 216 | LOG_E ("socks5 proxy worker init"); 217 | goto free; 218 | } 219 | 220 | hev_socks5_worker_start (*worker); 221 | 222 | hev_task_system_run (); 223 | 224 | hev_socks5_worker_destroy (*worker); 225 | *worker = NULL; 226 | 227 | free: 228 | if (fd >= 0) 229 | close (fd); 230 | hev_task_system_fini (); 231 | exit: 232 | return NULL; 233 | } 234 | 235 | static void 236 | hev_socks5_proxy_task_entry (void *data) 237 | { 238 | int res; 239 | int i; 240 | 241 | LOG_D ("socks5 proxy task run"); 242 | 243 | listen_fd = hev_socket_factory_get (factory); 244 | if (listen_fd < 0) 245 | return; 246 | 247 | worker_list[0] = hev_socks5_worker_new (); 248 | if (!worker_list[0]) { 249 | LOG_E ("socks5 proxy worker"); 250 | return; 251 | } 252 | 253 | res = hev_socks5_worker_init (worker_list[0], listen_fd); 254 | if (res < 0) { 255 | LOG_E ("socks5 proxy worker init"); 256 | return; 257 | } 258 | 259 | hev_socks5_worker_start (worker_list[0]); 260 | 261 | for (i = 1; i < workers; i++) { 262 | worker_list[i] = hev_socks5_worker_new (); 263 | if (!worker_list[i]) { 264 | LOG_E ("socks5 proxy worker"); 265 | return; 266 | } 267 | 268 | pthread_create (&work_threads[i], NULL, work_thread_handler, 269 | &worker_list[i]); 270 | } 271 | 272 | hev_socks5_proxy_load (); 273 | 274 | task = NULL; 275 | } 276 | 277 | void 278 | hev_socks5_proxy_run (void) 279 | { 280 | LOG_D ("socks5 proxy run"); 281 | 282 | hev_task_run (task, hev_socks5_proxy_task_entry, NULL); 283 | 284 | hev_task_system_run (); 285 | 286 | if (listen_fd >= 0) 287 | close (listen_fd); 288 | 289 | if (worker_list[0]) { 290 | int i; 291 | 292 | for (i = 1; i < workers; i++) 293 | pthread_join (work_threads[i], NULL); 294 | 295 | hev_socks5_worker_destroy (worker_list[0]); 296 | worker_list[0] = NULL; 297 | } 298 | } 299 | 300 | void 301 | hev_socks5_proxy_stop (void) 302 | { 303 | int i; 304 | 305 | for (i = 0; i < workers; i++) { 306 | HevSocks5Worker *worker; 307 | 308 | worker = worker_list[i]; 309 | if (!worker) 310 | continue; 311 | 312 | hev_socks5_worker_stop (worker); 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | release: 9 | types: 10 | - published 11 | workflow_dispatch: 12 | 13 | jobs: 14 | source: 15 | name: Source 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 1 22 | submodules: true 23 | - name: Gen Source 24 | run: | 25 | REV_ID=$(git rev-parse --short HEAD) 26 | mkdir -p hev-socks5-server-${{ github.ref_name }} 27 | git ls-files --recurse-submodules | tar c -O -T- | tar x -C hev-socks5-server-${{ github.ref_name }} 28 | echo ${REV_ID} > hev-socks5-server-${{ github.ref_name }}/.rev-id 29 | tar cJf hev-socks5-server-${{ github.ref_name }}.tar.xz hev-socks5-server-${{ github.ref_name }} 30 | - name: Upload source 31 | uses: actions/upload-artifact@v4 32 | with: 33 | name: hev-socks5-server-${{ github.ref_name }}.tar.xz 34 | path: hev-socks5-server-${{ github.ref_name }}.tar.xz 35 | if-no-files-found: error 36 | retention-days: 1 37 | 38 | linux: 39 | name: Linux 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | include: 44 | - name: arm64 45 | tool: aarch64-unknown-linux-musl 46 | - name: arm32 47 | tool: arm-unknown-linux-musleabi 48 | - name: arm32hf 49 | tool: arm-unknown-linux-musleabihf 50 | - name: arm32v7 51 | tool: armv7-unknown-linux-musleabi 52 | - name: arm32v7hf 53 | tool: armv7-unknown-linux-musleabihf 54 | - name: i586 55 | tool: i586-unknown-linux-musl 56 | - name: i686 57 | tool: i686-unknown-linux-musl 58 | - name: loong64 59 | tool: loongarch64-unknown-linux-musl 60 | - name: m68k 61 | tool: m68k-unknown-linux-musl 62 | - name: microblazeel 63 | tool: microblazeel-xilinx-linux-musl 64 | - name: microblaze 65 | tool: microblaze-xilinx-linux-musl 66 | - name: mips64el 67 | tool: mips64el-unknown-linux-musl 68 | - name: mips64 69 | tool: mips64-unknown-linux-musl 70 | - name: mips32el 71 | tool: mipsel-unknown-linux-musl 72 | - name: mips32elsf 73 | tool: mipsel-unknown-linux-muslsf 74 | - name: mips32 75 | tool: mips-unknown-linux-musl 76 | - name: mips32sf 77 | tool: mips-unknown-linux-muslsf 78 | - name: or1k 79 | tool: or1k-unknown-linux-musl 80 | - name: powerpc64le 81 | tool: powerpc64le-unknown-linux-musl 82 | - name: powerpc64 83 | tool: powerpc64-unknown-linux-musl 84 | - name: powerpcle 85 | tool: powerpcle-unknown-linux-musl 86 | - name: powerpc 87 | tool: powerpc-unknown-linux-musl 88 | - name: riscv32 89 | tool: riscv32-unknown-linux-musl 90 | - name: riscv64 91 | tool: riscv64-unknown-linux-musl 92 | - name: s390x 93 | tool: s390x-ibm-linux-musl 94 | - name: sh4 95 | tool: sh4-multilib-linux-musl 96 | - name: x86_64 97 | tool: x86_64-unknown-linux-musl 98 | steps: 99 | - name: Checkout 100 | uses: actions/checkout@v4 101 | with: 102 | fetch-depth: 1 103 | submodules: true 104 | - name: Build ${{ matrix.name }} 105 | run: | 106 | sudo mkdir -p /opt/x-tools 107 | wget https://github.com/cross-tools/musl-cross/releases/download/20250929/${{ matrix.tool }}.tar.xz 108 | sudo tar xf ${{ matrix.tool }}.tar.xz -C /opt/x-tools 109 | make CROSS_PREFIX=/opt/x-tools/${{ matrix.tool }}/bin/${{ matrix.tool }}- CFLAGS=${{ matrix.env.CFLAGS }} ENABLE_STATIC=1 -j`nproc` 110 | cp bin/hev-socks5-server hev-socks5-server-linux-${{ matrix.name }} 111 | - name: Upload ${{ matrix.name }} 112 | uses: actions/upload-artifact@v4 113 | with: 114 | name: hev-socks5-server-linux-${{ matrix.name }} 115 | path: hev-socks5-server-linux-${{ matrix.name }} 116 | if-no-files-found: error 117 | retention-days: 1 118 | 119 | windows: 120 | name: Windows 121 | runs-on: windows-latest 122 | defaults: 123 | run: 124 | shell: cmd 125 | steps: 126 | - name: Checkout 127 | uses: actions/checkout@v4 128 | with: 129 | fetch-depth: 1 130 | submodules: true 131 | - name: Setup MSYS2 132 | uses: msys2/setup-msys2@v2 133 | with: 134 | msystem: MSYS 135 | location: D:\msys2 136 | update: true 137 | install: >- 138 | gcc 139 | git 140 | make 141 | wget 142 | zip 143 | openssl-devel 144 | - name: Build 145 | shell: msys2 {0} 146 | run: | 147 | export MSYS=winsymlinks:native 148 | git clone --depth=1 --recursive file://`pwd` work; cd work 149 | make -j`nproc` 150 | mkdir hev-socks5-server 151 | cp bin/hev-socks5-server* hev-socks5-server 152 | wget -P hev-socks5-server https://github.com/heiher/msys2/releases/latest/download/msys-2.0.dll 153 | zip -r ../hev-socks5-server-win64.zip hev-socks5-server 154 | - name: Upload ${{ matrix.name }} 155 | uses: actions/upload-artifact@v4 156 | with: 157 | name: hev-socks5-server-win64.zip 158 | path: hev-socks5-server-win64.zip 159 | if-no-files-found: error 160 | retention-days: 1 161 | 162 | macos: 163 | name: macOS 164 | runs-on: macos-latest 165 | strategy: 166 | matrix: 167 | include: 168 | - name: arm64 169 | flags: -arch arm64 -mmacosx-version-min=11.0 170 | - name: x86_64 171 | flags: -arch x86_64 -mmacosx-version-min=10.6 172 | steps: 173 | - name: Checkout 174 | uses: actions/checkout@v4 175 | with: 176 | fetch-depth: 1 177 | submodules: true 178 | - name: Build ${{ matrix.name }} 179 | run: | 180 | make CC=clang CFLAGS="${{ matrix.flags }}" LFLAGS="${{ matrix.flags }}" -j $(sysctl -n hw.logicalcpu) 181 | cp bin/hev-socks5-server hev-socks5-server-darwin-${{ matrix.name }} 182 | - name: Upload ${{ matrix.name }} 183 | uses: actions/upload-artifact@v4 184 | with: 185 | name: hev-socks5-server-darwin-${{ matrix.name }} 186 | path: hev-socks5-server-darwin-${{ matrix.name }} 187 | if-no-files-found: error 188 | retention-days: 1 189 | 190 | freebsd: 191 | name: FreeBSD 192 | runs-on: ubuntu-latest 193 | steps: 194 | - name: Checkout 195 | uses: actions/checkout@v4 196 | with: 197 | fetch-depth: 1 198 | submodules: true 199 | - name: Build 200 | uses: vmactions/freebsd-vm@v1 201 | with: 202 | usesh: true 203 | prepare: | 204 | pkg install -y gmake gcc 205 | run: | 206 | gmake 207 | cp bin/hev-socks5-server hev-socks5-server-freebsd-x86_64 208 | - name: Upload 209 | uses: actions/upload-artifact@v4 210 | with: 211 | name: hev-socks5-server-freebsd-x86_64 212 | path: hev-socks5-server-freebsd-x86_64 213 | if-no-files-found: error 214 | retention-days: 1 215 | 216 | release: 217 | name: Release 218 | runs-on: ubuntu-latest 219 | needs: 220 | - source 221 | - linux 222 | - macos 223 | - freebsd 224 | - windows 225 | if: github.event_name == 'release' 226 | steps: 227 | - name: Checkout 228 | uses: actions/checkout@v4 229 | with: 230 | fetch-depth: 1 231 | - name: Download artifacts 232 | uses: actions/download-artifact@v4 233 | with: 234 | path: release 235 | pattern: "hev-*" 236 | merge-multiple: true 237 | - name: Upload artifacts 238 | env: 239 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 240 | run: | 241 | for i in release/hev-*; do 242 | gh release upload ${{ github.event.release.tag_name }} $i 243 | done 244 | 245 | apple: 246 | name: Apple 247 | runs-on: macos-latest 248 | if: github.event_name != 'release' 249 | steps: 250 | - name: Checkout 251 | uses: actions/checkout@v4 252 | with: 253 | fetch-depth: 1 254 | submodules: true 255 | - name: Build 256 | run: | 257 | ./build-apple.sh 258 | 259 | android: 260 | name: Android 261 | runs-on: ubuntu-latest 262 | if: github.event_name != 'release' 263 | steps: 264 | - name: Checkout 265 | uses: actions/checkout@v4 266 | with: 267 | fetch-depth: 1 268 | submodules: true 269 | - name: Prepare 270 | run: | 271 | wget https://dl.google.com/android/repository/android-ndk-r27d-linux.zip 272 | unzip android-ndk-r27d-linux.zip 273 | ln -sf . jni 274 | - name: Build 275 | run: | 276 | ./android-ndk-r27d/ndk-build 277 | 278 | llvm: 279 | name: LLVM 280 | runs-on: ubuntu-latest 281 | if: github.event_name != 'release' 282 | steps: 283 | - name: Checkout 284 | uses: actions/checkout@v4 285 | with: 286 | fetch-depth: 1 287 | submodules: true 288 | - name: Prepare 289 | run: | 290 | sudo apt install -y clang 291 | - name: Build 292 | run: | 293 | make CC=clang ENABLE_STATIC=1 -j`nproc` 294 | -------------------------------------------------------------------------------- /src/hev-config.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : hev-config.c 4 | Author : hev 5 | Copyright : Copyright (c) 2017 - 2024 hev 6 | Description : Config 7 | ============================================================================ 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "hev-logger.h" 19 | #include "hev-config.h" 20 | 21 | static unsigned int workers; 22 | static int listen_ipv6_only; 23 | static char listen_address[256]; 24 | static char listen_port[8]; 25 | static char udp_listen_address[256]; 26 | static char udp_public_address[2][256]; 27 | static char bind_address[2][256]; 28 | static char bind_interface[256]; 29 | static char auth_file[1024]; 30 | static char username[256]; 31 | static char password[256]; 32 | static char log_file[1024]; 33 | static char pid_file[1024]; 34 | static int udp_listen_port_beg; 35 | static int udp_listen_port_mod; 36 | static int task_stack_size = 8192; 37 | static int udp_recv_buffer_size = 524288; 38 | static int udp_copy_buffer_nums = 10; 39 | static int connect_timeout = 10000; 40 | static int tcp_read_write_timeout = 300000; 41 | static int udp_read_write_timeout = 60000; 42 | static int limit_nofile = 65535; 43 | static int log_level = HEV_LOGGER_WARN; 44 | static int addr_family = HEV_SOCKS5_ADDR_FAMILY_UNSPEC; 45 | static unsigned int socket_mark; 46 | 47 | static int 48 | hev_config_parse_main (yaml_document_t *doc, yaml_node_t *base) 49 | { 50 | yaml_node_pair_t *pair; 51 | const char *addr = NULL; 52 | const char *port = NULL; 53 | const char *mark = NULL; 54 | const char *udp_addr = NULL; 55 | const char *udp_addr4 = NULL; 56 | const char *udp_addr6 = NULL; 57 | const char *udp_port = NULL; 58 | const char *bind_saddr = NULL; 59 | const char *bind_saddr4 = NULL; 60 | const char *bind_saddr6 = NULL; 61 | const char *bind_iface = NULL; 62 | const char *addr_type = NULL; 63 | 64 | if (!base || YAML_MAPPING_NODE != base->type) 65 | return -1; 66 | 67 | for (pair = base->data.mapping.pairs.start; 68 | pair < base->data.mapping.pairs.top; pair++) { 69 | yaml_node_t *node; 70 | const char *key, *value; 71 | 72 | if (!pair->key || !pair->value) 73 | break; 74 | 75 | node = yaml_document_get_node (doc, pair->key); 76 | if (!node || YAML_SCALAR_NODE != node->type) 77 | break; 78 | key = (const char *)node->data.scalar.value; 79 | 80 | node = yaml_document_get_node (doc, pair->value); 81 | if (!node || YAML_SCALAR_NODE != node->type) 82 | break; 83 | value = (const char *)node->data.scalar.value; 84 | 85 | if (0 == strcmp (key, "workers")) 86 | workers = strtoul (value, NULL, 10); 87 | else if (0 == strcmp (key, "port")) 88 | port = value; 89 | else if (0 == strcmp (key, "listen-address")) 90 | addr = value; 91 | else if (0 == strcmp (key, "udp-port")) 92 | udp_port = value; 93 | else if (0 == strcmp (key, "udp-listen-address")) 94 | udp_addr = value; 95 | else if (0 == strcmp (key, "udp-public-address-v4")) 96 | udp_addr4 = value; 97 | else if (0 == strcmp (key, "udp-public-address-v6")) 98 | udp_addr6 = value; 99 | else if (0 == strcmp (key, "listen-ipv6-only")) 100 | listen_ipv6_only = (0 == strcasecmp (value, "true")) ? 1 : 0; 101 | else if (0 == strcmp (key, "bind-address")) 102 | bind_saddr = value; 103 | else if (0 == strcmp (key, "bind-address-v4")) 104 | bind_saddr4 = value; 105 | else if (0 == strcmp (key, "bind-address-v6")) 106 | bind_saddr6 = value; 107 | else if (0 == strcmp (key, "bind-interface")) 108 | bind_iface = value; 109 | else if (0 == strcmp (key, "domain-address-type")) 110 | addr_type = value; 111 | else if (0 == strcmp (key, "mark")) 112 | mark = value; 113 | } 114 | 115 | if (!workers) { 116 | fprintf (stderr, "Can't found main.workers!\n"); 117 | return -1; 118 | } 119 | 120 | if (!port) { 121 | fprintf (stderr, "Can't found main.port!\n"); 122 | return -1; 123 | } 124 | 125 | if (!addr) { 126 | fprintf (stderr, "Can't found main.listen-address!\n"); 127 | return -1; 128 | } 129 | 130 | #ifdef __MSYS__ 131 | if (workers > 1) { 132 | fprintf (stderr, "Only supports one worker on Windows.\n"); 133 | workers = 1; 134 | } 135 | #endif 136 | 137 | strncpy (listen_port, port, 8 - 1); 138 | strncpy (listen_address, addr, 256 - 1); 139 | 140 | if (udp_port) { 141 | unsigned int beg = 0, end = 0; 142 | 143 | sscanf (udp_port, "%u-%u", &beg, &end); 144 | if (end && beg > end) { 145 | fprintf (stderr, "Invalid main.udp-port!\n"); 146 | return -1; 147 | } 148 | if (end == 0) 149 | end = beg; 150 | 151 | udp_listen_port_beg = beg; 152 | udp_listen_port_mod = end - beg + 1; 153 | } 154 | 155 | if (udp_addr) 156 | strncpy (udp_listen_address, udp_addr, 256 - 1); 157 | if (udp_addr4) 158 | strncpy (udp_public_address[0], udp_addr4, 256 - 1); 159 | if (udp_addr6) 160 | strncpy (udp_public_address[1], udp_addr6, 256 - 1); 161 | 162 | if (bind_saddr4 && bind_saddr4[0] != '\0') 163 | strncpy (bind_address[0], bind_saddr4, 256 - 1); 164 | else if (bind_saddr && bind_saddr[0] != '\0') 165 | strncpy (bind_address[0], bind_saddr, 256 - 1); 166 | if (bind_saddr6 && bind_saddr6[0] != '\0') 167 | strncpy (bind_address[1], bind_saddr6, 256 - 1); 168 | else if (bind_saddr && bind_saddr[0] != '\0') 169 | strncpy (bind_address[1], bind_saddr, 256 - 1); 170 | 171 | if (bind_iface) 172 | strncpy (bind_interface, bind_iface, 256 - 1); 173 | 174 | if (addr_type) { 175 | if (0 == strcmp (addr_type, "ipv4")) 176 | addr_family = HEV_SOCKS5_ADDR_FAMILY_IPV4; 177 | else if (0 == strcmp (addr_type, "ipv6")) 178 | addr_family = HEV_SOCKS5_ADDR_FAMILY_IPV6; 179 | } 180 | 181 | if (mark) 182 | socket_mark = strtoul (mark, NULL, 0); 183 | 184 | return 0; 185 | } 186 | 187 | static int 188 | hev_config_parse_auth (yaml_document_t *doc, yaml_node_t *base) 189 | { 190 | yaml_node_pair_t *pair; 191 | const char *user = NULL, *pass = NULL, *file = NULL; 192 | 193 | if (!base || YAML_MAPPING_NODE != base->type) 194 | return -1; 195 | 196 | for (pair = base->data.mapping.pairs.start; 197 | pair < base->data.mapping.pairs.top; pair++) { 198 | yaml_node_t *node; 199 | const char *key, *value; 200 | 201 | if (!pair->key || !pair->value) 202 | break; 203 | 204 | node = yaml_document_get_node (doc, pair->key); 205 | if (!node || YAML_SCALAR_NODE != node->type) 206 | break; 207 | key = (const char *)node->data.scalar.value; 208 | 209 | node = yaml_document_get_node (doc, pair->value); 210 | if (!node || YAML_SCALAR_NODE != node->type) 211 | break; 212 | value = (const char *)node->data.scalar.value; 213 | 214 | if (0 == strcmp (key, "username")) 215 | user = value; 216 | else if (0 == strcmp (key, "password")) 217 | pass = value; 218 | else if (0 == strcmp (key, "file")) 219 | file = value; 220 | } 221 | 222 | if (file) { 223 | strncpy (auth_file, file, 1023); 224 | } else if (user && pass) { 225 | strncpy (username, user, 255); 226 | strncpy (password, pass, 255); 227 | } 228 | 229 | return 0; 230 | } 231 | 232 | static int 233 | hev_config_parse_log_level (const char *value) 234 | { 235 | if (0 == strcmp (value, "debug")) 236 | return HEV_LOGGER_DEBUG; 237 | else if (0 == strcmp (value, "info")) 238 | return HEV_LOGGER_INFO; 239 | else if (0 == strcmp (value, "error")) 240 | return HEV_LOGGER_ERROR; 241 | 242 | return HEV_LOGGER_WARN; 243 | } 244 | 245 | static int 246 | hev_config_parse_misc (yaml_document_t *doc, yaml_node_t *base) 247 | { 248 | yaml_node_pair_t *pair; 249 | int tcp_rw_timeout = -1; 250 | int udp_rw_timeout = -1; 251 | int rw_timeout = -1; 252 | 253 | if (!base || YAML_MAPPING_NODE != base->type) 254 | return -1; 255 | 256 | for (pair = base->data.mapping.pairs.start; 257 | pair < base->data.mapping.pairs.top; pair++) { 258 | yaml_node_t *node; 259 | const char *key, *value; 260 | 261 | if (!pair->key || !pair->value) 262 | break; 263 | 264 | node = yaml_document_get_node (doc, pair->key); 265 | if (!node || YAML_SCALAR_NODE != node->type) 266 | break; 267 | key = (const char *)node->data.scalar.value; 268 | 269 | node = yaml_document_get_node (doc, pair->value); 270 | if (!node || YAML_SCALAR_NODE != node->type) 271 | break; 272 | value = (const char *)node->data.scalar.value; 273 | 274 | if (0 == strcmp (key, "task-stack-size")) 275 | task_stack_size = strtoul (value, NULL, 10); 276 | else if (0 == strcmp (key, "udp-recv-buffer-size")) 277 | udp_recv_buffer_size = strtoul (value, NULL, 10); 278 | else if (0 == strcmp (key, "udp-copy-buffer-nums")) 279 | udp_copy_buffer_nums = strtoul (value, NULL, 10); 280 | else if (0 == strcmp (key, "connect-timeout")) 281 | connect_timeout = strtoul (value, NULL, 10); 282 | else if (0 == strcmp (key, "read-write-timeout")) 283 | rw_timeout = strtoul (value, NULL, 10); 284 | else if (0 == strcmp (key, "tcp-read-write-timeout")) 285 | tcp_rw_timeout = strtoul (value, NULL, 10); 286 | else if (0 == strcmp (key, "udp-read-write-timeout")) 287 | udp_rw_timeout = strtoul (value, NULL, 10); 288 | else if (0 == strcmp (key, "pid-file")) 289 | strncpy (pid_file, value, 1024 - 1); 290 | else if (0 == strcmp (key, "log-file")) 291 | strncpy (log_file, value, 1024 - 1); 292 | else if (0 == strcmp (key, "log-level")) 293 | log_level = hev_config_parse_log_level (value); 294 | else if (0 == strcmp (key, "limit-nofile")) 295 | limit_nofile = strtol (value, NULL, 10); 296 | } 297 | 298 | if (tcp_rw_timeout <= 0) 299 | tcp_rw_timeout = rw_timeout; 300 | if (udp_rw_timeout <= 0) 301 | udp_rw_timeout = rw_timeout; 302 | 303 | if (tcp_rw_timeout > 0) 304 | tcp_read_write_timeout = tcp_rw_timeout; 305 | if (udp_rw_timeout > 0) 306 | udp_read_write_timeout = udp_rw_timeout; 307 | 308 | return 0; 309 | } 310 | 311 | static int 312 | hev_config_parse_doc (yaml_document_t *doc) 313 | { 314 | yaml_node_t *root; 315 | yaml_node_pair_t *pair; 316 | 317 | root = yaml_document_get_root_node (doc); 318 | if (!root || YAML_MAPPING_NODE != root->type) 319 | return -1; 320 | 321 | for (pair = root->data.mapping.pairs.start; 322 | pair < root->data.mapping.pairs.top; pair++) { 323 | yaml_node_t *node; 324 | const char *key; 325 | int res = 0; 326 | 327 | if (!pair->key || !pair->value) 328 | break; 329 | 330 | node = yaml_document_get_node (doc, pair->key); 331 | if (!node || YAML_SCALAR_NODE != node->type) 332 | break; 333 | 334 | key = (const char *)node->data.scalar.value; 335 | node = yaml_document_get_node (doc, pair->value); 336 | 337 | if (0 == strcmp (key, "main")) 338 | res = hev_config_parse_main (doc, node); 339 | else if (0 == strcmp (key, "auth")) 340 | res = hev_config_parse_auth (doc, node); 341 | else if (0 == strcmp (key, "misc")) 342 | res = hev_config_parse_misc (doc, node); 343 | 344 | if (res < 0) 345 | return -1; 346 | } 347 | 348 | return 0; 349 | } 350 | 351 | int 352 | hev_config_init_from_file (const char *path) 353 | { 354 | yaml_parser_t parser; 355 | yaml_document_t doc; 356 | FILE *fp; 357 | int res = -1; 358 | 359 | if (!yaml_parser_initialize (&parser)) 360 | goto exit; 361 | 362 | fp = fopen (path, "r"); 363 | if (!fp) { 364 | fprintf (stderr, "Open %s failed!\n", path); 365 | goto exit_free_parser; 366 | } 367 | 368 | yaml_parser_set_input_file (&parser, fp); 369 | if (!yaml_parser_load (&parser, &doc)) { 370 | fprintf (stderr, "Parse %s failed!\n", path); 371 | goto exit_close_fp; 372 | } 373 | 374 | res = hev_config_parse_doc (&doc); 375 | yaml_document_delete (&doc); 376 | 377 | exit_close_fp: 378 | fclose (fp); 379 | exit_free_parser: 380 | yaml_parser_delete (&parser); 381 | exit: 382 | return res; 383 | } 384 | 385 | int 386 | hev_config_init_from_str (const unsigned char *config_str, 387 | unsigned int config_len) 388 | { 389 | yaml_parser_t parser; 390 | yaml_document_t doc; 391 | int res = -1; 392 | 393 | if (!yaml_parser_initialize (&parser)) 394 | goto exit; 395 | 396 | yaml_parser_set_input_string (&parser, config_str, config_len); 397 | if (!yaml_parser_load (&parser, &doc)) { 398 | fprintf (stderr, "Failed to parse config."); 399 | goto exit_free_parser; 400 | } 401 | 402 | res = hev_config_parse_doc (&doc); 403 | yaml_document_delete (&doc); 404 | 405 | exit_free_parser: 406 | yaml_parser_delete (&parser); 407 | exit: 408 | return res; 409 | } 410 | 411 | void 412 | hev_config_fini (void) 413 | { 414 | } 415 | 416 | unsigned int 417 | hev_config_get_workers (void) 418 | { 419 | return workers; 420 | } 421 | 422 | const char * 423 | hev_config_get_listen_address (void) 424 | { 425 | return listen_address; 426 | } 427 | 428 | const char * 429 | hev_config_get_listen_port (void) 430 | { 431 | return listen_port; 432 | } 433 | 434 | const char * 435 | hev_config_get_udp_listen_address (void) 436 | { 437 | if ('\0' == udp_listen_address[0]) 438 | return NULL; 439 | 440 | return udp_listen_address; 441 | } 442 | 443 | int 444 | hev_config_get_udp_listen_port (void) 445 | { 446 | static atomic_uint inc; 447 | unsigned int cur; 448 | 449 | if (udp_listen_port_mod <= 1) 450 | return udp_listen_port_beg; 451 | 452 | cur = atomic_fetch_add_explicit (&inc, 1, memory_order_relaxed); 453 | return udp_listen_port_beg + (cur % udp_listen_port_mod); 454 | } 455 | 456 | const char * 457 | hev_config_get_udp_public_address (int family) 458 | { 459 | int idx = family == AF_INET6; 460 | 461 | if ('\0' == udp_public_address[idx][0]) 462 | return NULL; 463 | 464 | return udp_public_address[idx]; 465 | } 466 | 467 | int 468 | hev_config_get_listen_ipv6_only (void) 469 | { 470 | return listen_ipv6_only; 471 | } 472 | 473 | const char * 474 | hev_config_get_bind_address (int family) 475 | { 476 | int idx = family == AF_INET6; 477 | 478 | if ('\0' == bind_address[idx][0]) 479 | return NULL; 480 | 481 | return bind_address[idx]; 482 | } 483 | 484 | const char * 485 | hev_config_get_bind_interface (void) 486 | { 487 | if ('\0' == bind_interface[0]) 488 | return NULL; 489 | 490 | return bind_interface; 491 | } 492 | 493 | int 494 | hev_config_get_address_family (void) 495 | { 496 | return addr_family; 497 | } 498 | 499 | unsigned int 500 | hev_config_get_socket_mark (void) 501 | { 502 | return socket_mark; 503 | } 504 | 505 | const char * 506 | hev_config_get_auth_file (void) 507 | { 508 | if ('\0' == auth_file[0]) 509 | return NULL; 510 | 511 | return auth_file; 512 | } 513 | 514 | const char * 515 | hev_config_get_auth_username (void) 516 | { 517 | if ('\0' == username[0]) 518 | return NULL; 519 | 520 | return username; 521 | } 522 | 523 | const char * 524 | hev_config_get_auth_password (void) 525 | { 526 | if ('\0' == password[0]) 527 | return NULL; 528 | 529 | return password; 530 | } 531 | 532 | int 533 | hev_config_get_misc_task_stack_size (void) 534 | { 535 | return task_stack_size; 536 | } 537 | 538 | int 539 | hev_config_get_misc_udp_recv_buffer_size (void) 540 | { 541 | return udp_recv_buffer_size; 542 | } 543 | 544 | int 545 | hev_config_get_misc_udp_copy_buffer_nums (void) 546 | { 547 | return udp_copy_buffer_nums; 548 | } 549 | 550 | int 551 | hev_config_get_misc_connect_timeout (void) 552 | { 553 | return connect_timeout; 554 | } 555 | 556 | int 557 | hev_config_get_misc_tcp_read_write_timeout (void) 558 | { 559 | return tcp_read_write_timeout; 560 | } 561 | 562 | int 563 | hev_config_get_misc_udp_read_write_timeout (void) 564 | { 565 | return udp_read_write_timeout; 566 | } 567 | 568 | int 569 | hev_config_get_misc_limit_nofile (void) 570 | { 571 | return limit_nofile; 572 | } 573 | 574 | const char * 575 | hev_config_get_misc_pid_file (void) 576 | { 577 | if ('\0' == pid_file[0]) 578 | return NULL; 579 | 580 | return pid_file; 581 | } 582 | 583 | const char * 584 | hev_config_get_misc_log_file (void) 585 | { 586 | if ('\0' == log_file[0]) 587 | return "stderr"; 588 | 589 | return log_file; 590 | } 591 | 592 | int 593 | hev_config_get_misc_log_level (void) 594 | { 595 | return log_level; 596 | } 597 | --------------------------------------------------------------------------------