├── src ├── signal.h ├── listener.h ├── bufferpump.h ├── log.c ├── mem-allocator.h ├── pump.h ├── log.h ├── pump.c ├── splicepump.h ├── socks5.h ├── signal.c ├── context.h ├── util.h ├── transocks.c ├── listener.c ├── util.c ├── context.c ├── bufferpump.c ├── socks5.c ├── splicepump.c └── list.h ├── .gitignore ├── cmake ├── Findmimalloc.cmake ├── FindLibevent2.cmake ├── MacroPushRequiredVars.cmake └── TrMacros.cmake ├── CMakeLists.txt ├── .travis.yml ├── README.md └── LICENSE.txt /src/signal.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/27/18. 3 | // 4 | 5 | #ifndef TRANSOCKS_WONG_SIGNAL_H 6 | #define TRANSOCKS_WONG_SIGNAL_H 7 | 8 | #include "mem-allocator.h" 9 | 10 | #include 11 | 12 | #include "context.h" 13 | 14 | 15 | int signal_init(transocks_global_env *); 16 | 17 | void signal_deinit(transocks_global_env *); 18 | 19 | #endif //TRANSOCKS_WONG_SIGNAL_H 20 | -------------------------------------------------------------------------------- /src/listener.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/27/18. 3 | // 4 | 5 | #ifndef TRANSOCKS_WONG_LISTENER_H 6 | #define TRANSOCKS_WONG_LISTENER_H 7 | 8 | #include "mem-allocator.h" 9 | 10 | #include 11 | 12 | #include "context.h" 13 | #include "util.h" 14 | #include "log.h" 15 | #include "socks5.h" 16 | 17 | 18 | int listener_init(transocks_global_env *); 19 | void listener_deinit(transocks_global_env *); 20 | 21 | #endif //TRANSOCKS_WONG_LISTENER_H 22 | -------------------------------------------------------------------------------- /src/bufferpump.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/30/18. 3 | // 4 | 5 | #ifndef TRANSOCKS_WONG_BUFFERPUMP_H 6 | #define TRANSOCKS_WONG_BUFFERPUMP_H 7 | 8 | #include "mem-allocator.h" 9 | #include "context.h" 10 | #include "log.h" 11 | #include "util.h" 12 | #include "pump.h" 13 | 14 | // used when relaying data between client and the relay 15 | // via buffer copying 16 | typedef struct transocks_bufferpump_t transocks_bufferpump; 17 | 18 | #endif //TRANSOCKS_WONG_BUFFERPUMP_H 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Jetbrains 2 | .idea/ 3 | *.iml 4 | 5 | # Prerequisites 6 | *.d 7 | 8 | # Compiled Object files 9 | *.slo 10 | *.lo 11 | *.o 12 | *.obj 13 | 14 | # Precompiled Headers 15 | *.gch 16 | *.pch 17 | 18 | # Compiled Dynamic libraries 19 | *.so 20 | *.dylib 21 | *.dll 22 | 23 | # Fortran module files 24 | *.mod 25 | *.smod 26 | 27 | # Compiled Static libraries 28 | *.lai 29 | *.la 30 | *.a 31 | *.lib 32 | 33 | # Config files 34 | *.json 35 | 36 | # Cmake files 37 | CMakeCache.txt 38 | CMakeFiles 39 | CMakeScripts 40 | Testing 41 | Makefile 42 | cmake_install.cmake 43 | install_manifest.txt 44 | compile_commands.json 45 | CTestTestfile.cmake 46 | build/ 47 | cmake-build-*/ 48 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/25/18. 3 | // 4 | 5 | #include "log.h" 6 | 7 | static char *loglevel_str[] = { 8 | [LOG_ERR] = "err", 9 | [LOG_INFO] = "info", 10 | [LOG_DEBUG] = "debug" 11 | }; 12 | 13 | void _log_write(FILE *fd, const char *file, int line, const char *func, 14 | bool do_errno, int priority, const char *fmt, ...) { 15 | va_list ap; 16 | va_start(ap, fmt); 17 | 18 | int saved_errno = errno; 19 | struct timespec tv; 20 | clock_gettime(CLOCK_REALTIME, &tv); 21 | 22 | // header 23 | fprintf(fd, "%ld.%6.6ld %s %s:%d %s() ", tv.tv_sec, tv.tv_nsec / 1000 /* to microseconds */, 24 | loglevel_str[priority], file, line, func); 25 | 26 | // message 27 | vfprintf(fd, fmt, ap); 28 | va_end(ap); 29 | 30 | // appendix 31 | if (do_errno) { 32 | fprintf(fd, ": %s\n", strerror(saved_errno)); 33 | } else { 34 | fprintf(fd, "\n"); 35 | } 36 | } 37 | 38 | #ifdef TRANSOCKS_DEBUG 39 | void dump_data(char *tag, char *text, int len) 40 | { 41 | int i; 42 | printf("%s: ", tag); 43 | for (i = 0; i < len; i++) 44 | printf("0x%02x ", (uint8_t)text[i]); 45 | printf("\n"); 46 | } 47 | #endif 48 | -------------------------------------------------------------------------------- /src/mem-allocator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 9/3/19. 3 | // 4 | 5 | #ifndef TRANSOCKS_WONG_MEM_ALLOCATOR_H 6 | #define TRANSOCKS_WONG_MEM_ALLOCATOR_H 7 | 8 | #if defined(TRANSOCKS_ALLOCATOR_USE_SYSTEM) 9 | #define TR_USED_MEM_ALLOCATOR "system" 10 | 11 | #define tr_malloc(size) malloc((size)) 12 | #define tr_calloc(count, size) calloc((count), (size)) 13 | #define tr_realloc(p, newsize) realloc((p), (newsize)) 14 | #define tr_expand(p, newsize) expand((p), (newsize)) 15 | #define tr_free(p) free((p)) 16 | #define tr_strdup(s) strdup((s)) 17 | #define tr_strndup(s, n) strndup((s), (n)) 18 | #define tr_realpath(fname, resolved_name) realpath((fname), (resolved_name)) 19 | 20 | #elif defined(TRANSOCKS_ALLOCATOR_USE_MIMALLOC) 21 | #define TR_USED_MEM_ALLOCATOR "mimalloc" 22 | #include 23 | 24 | #define tr_malloc(size) mi_malloc((size)) 25 | #define tr_calloc(count, size) mi_calloc((count), (size)) 26 | #define tr_realloc(p, newsize) mi_realloc((p), (newsize)) 27 | #define tr_expand(p, newsize) mi_expand((p), (newsize)) 28 | #define tr_free(p) mi_free((p)) 29 | #define tr_strdup(s) mi_strdup((s)) 30 | #define tr_strndup(s, n) mi_strndup((s), (n)) 31 | #define tr_realpath(fname, resolved_name) mi_realpath((fname), (resolved_name)) 32 | 33 | #else 34 | #error memory allocator not specified 35 | #endif 36 | 37 | #endif //TRANSOCKS_WONG_MEM_ALLOCATOR_H 38 | -------------------------------------------------------------------------------- /src/pump.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/30/18. 3 | // 4 | 5 | #ifndef TRANSOCKS_WONG_PUMP_H 6 | #define TRANSOCKS_WONG_PUMP_H 7 | 8 | #include "mem-allocator.h" 9 | #include "context.h" 10 | #include "util.h" 11 | #include "log.h" 12 | 13 | 14 | #define PUMPMETHOD_SPLICE "splicepump" 15 | #define PUMPMETHOD_BUFFER "bufferpump" 16 | 17 | /* forward declaration */ 18 | typedef struct transocks_pump_t transocks_pump; 19 | 20 | /* functions */ 21 | typedef int (*transocks_pump_start_fn_t)(transocks_client *); 22 | 23 | typedef void (*transocks_pump_free_fn_t)(transocks_client *); 24 | 25 | typedef void (*transocks_pump_dump_info_fn_t)(transocks_client *); 26 | 27 | /* detailed type */ 28 | typedef struct transocks_pump_t { 29 | char *name; 30 | void *user_arg; 31 | transocks_pump_start_fn_t start_pump_fn; 32 | transocks_pump_free_fn_t free_pump_fn; 33 | transocks_pump_dump_info_fn_t dump_info_fn; 34 | } transocks_pump; 35 | 36 | 37 | /* exported functions */ 38 | 39 | int transocks_pump_init(transocks_global_env *); 40 | 41 | int transocks_start_pump(transocks_client *); 42 | 43 | void transocks_pump_free(transocks_client *); 44 | 45 | /* the tagfmt should NOT contain '\n' */ 46 | void transocks_pump_dump_info(transocks_client *, const char *tagfmt, ...); 47 | 48 | #ifdef TRANSOCKS_DEBUG 49 | #define transocks_pump_dump_info_debug transocks_pump_dump_info 50 | #else 51 | #define transocks_pump_dump_info_debug(pclient, tagfmt, ...) 52 | #endif 53 | 54 | #endif //TRANSOCKS_WONG_PUMP_H 55 | -------------------------------------------------------------------------------- /cmake/Findmimalloc.cmake: -------------------------------------------------------------------------------- 1 | include(MacroPushRequiredVars) 2 | MACRO_PUSH_REQUIRED_VARS(CMAKE_FIND_LIBRARY_SUFFIXES) 3 | 4 | FIND_PATH(MIMALLOC_INCLUDE_DIR mimalloc.h) 5 | 6 | IF(MIMALLOC_USE_STATIC_LIB) 7 | SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a") 8 | ENDIF() 9 | 10 | FIND_LIBRARY(MIMALLOC_LIBRARIES NAMES mimalloc) 11 | 12 | IF(MIMALLOC_INCLUDE_DIR) 13 | SET(MIMALLOC_FOUND_INCLUDE TRUE) 14 | ENDIF(MIMALLOC_INCLUDE_DIR) 15 | IF(MIMALLOC_LIBRARIES) 16 | SET(MIMALLOC_FOUND_LIBRARY TRUE) 17 | ENDIF(MIMALLOC_LIBRARIES) 18 | IF(MIMALLOC_FOUND_INCLUDE AND MIMALLOC_FOUND_LIBRARY) 19 | SET(MIMALLOC_FOUND TRUE) 20 | ENDIF(MIMALLOC_FOUND_INCLUDE AND MIMALLOC_FOUND_LIBRARY) 21 | 22 | IF(MIMALLOC_FOUND_INCLUDE) 23 | IF (NOT MIMALLOC_FIND_QUIETLY) 24 | MESSAGE(STATUS "Found mimalloc includes: ${MIMALLOC_INCLUDE_DIR}/mimalloc.h") 25 | ENDIF (NOT MIMALLOC_FIND_QUIETLY) 26 | ELSE(MIMALLOC_FOUND_INCLUDE) 27 | IF (MIMALLOC_FIND_REQUIRED) 28 | MESSAGE(FATAL_ERROR "Could NOT find mimalloc include headers") 29 | ENDIF (MIMALLOC_FIND_REQUIRED) 30 | ENDIF(MIMALLOC_FOUND_INCLUDE) 31 | 32 | IF(MIMALLOC_FOUND_LIBRARY) 33 | IF (NOT MIMALLOC_FIND_QUIETLY) 34 | MESSAGE(STATUS "Found mimalloc library: ${MIMALLOC_LIBRARIES}") 35 | ENDIF (NOT MIMALLOC_FIND_QUIETLY) 36 | ELSE(MIMALLOC_FOUND_LIBRARY) 37 | IF (MIMALLOC_FIND_REQUIRED) 38 | MESSAGE(FATAL_ERROR "Could NOT find mimalloc library") 39 | ENDIF (MIMALLOC_FIND_REQUIRED) 40 | ENDIF(MIMALLOC_FOUND_LIBRARY) 41 | 42 | MACRO_POP_REQUIRED_VARS(CMAKE_FIND_LIBRARY_SUFFIXES) 43 | -------------------------------------------------------------------------------- /cmake/FindLibevent2.cmake: -------------------------------------------------------------------------------- 1 | include(MacroPushRequiredVars) 2 | MACRO_PUSH_REQUIRED_VARS(CMAKE_FIND_LIBRARY_SUFFIXES) 3 | 4 | FIND_PATH(LIBEVENT2_INCLUDE_DIR event2/event.h) 5 | 6 | IF(Libevent2_USE_STATIC_LIB) 7 | SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a") 8 | ENDIF() 9 | 10 | FIND_LIBRARY(LIBEVENT2_LIBRARIES NAMES event_core) 11 | 12 | IF(LIBEVENT2_INCLUDE_DIR) 13 | SET(LIBEVENT2_FOUND_INCLUDE TRUE) 14 | ENDIF(LIBEVENT2_INCLUDE_DIR) 15 | IF(LIBEVENT2_LIBRARIES) 16 | SET(LIBEVENT2_FOUND_LIBRARY TRUE) 17 | ENDIF(LIBEVENT2_LIBRARIES) 18 | IF(LIBEVENT2_FOUND_INCLUDE AND LIBEVENT2_FOUND_LIBRARY) 19 | SET(LIBEVENT2_FOUND TRUE) 20 | ENDIF(LIBEVENT2_FOUND_INCLUDE AND LIBEVENT2_FOUND_LIBRARY) 21 | 22 | IF(LIBEVENT2_FOUND_INCLUDE) 23 | IF (NOT Libevent2_FIND_QUIETLY) 24 | MESSAGE(STATUS "Found libevent2 includes: ${LIBEVENT2_INCLUDE_DIR}/event2/event.h") 25 | ENDIF (NOT Libevent2_FIND_QUIETLY) 26 | ELSE(LIBEVENT2_FOUND_INCLUDE) 27 | IF (Libevent2_FIND_REQUIRED) 28 | MESSAGE(FATAL_ERROR "Could NOT find libevent2 include headers") 29 | ENDIF (Libevent2_FIND_REQUIRED) 30 | ENDIF(LIBEVENT2_FOUND_INCLUDE) 31 | 32 | IF(LIBEVENT2_FOUND_LIBRARY) 33 | IF (NOT Libevent2_FIND_QUIETLY) 34 | MESSAGE(STATUS "Found libevent2 library: ${LIBEVENT2_LIBRARIES}") 35 | ENDIF (NOT Libevent2_FIND_QUIETLY) 36 | ELSE(LIBEVENT2_FOUND_LIBRARY) 37 | IF (Libevent2_FIND_REQUIRED) 38 | MESSAGE(FATAL_ERROR "Could NOT find libevent2 library") 39 | ENDIF (Libevent2_FIND_REQUIRED) 40 | ENDIF(LIBEVENT2_FOUND_LIBRARY) 41 | 42 | MACRO_POP_REQUIRED_VARS(CMAKE_FIND_LIBRARY_SUFFIXES) -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/25/18. 3 | // 4 | 5 | #ifndef TRANSOCKS_WONG_LOG_H 6 | #define TRANSOCKS_WONG_LOG_H 7 | 8 | #include "mem-allocator.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "util.h" 17 | 18 | enum LOGLEVEL { 19 | LOG_ERR, 20 | LOG_INFO, 21 | LOG_DEBUG 22 | }; 23 | 24 | 25 | #ifdef TRANSOCKS_DEBUG 26 | void dump_data(char *tag, char *text, int len); 27 | #else 28 | #define dump_data(tag, text, len) 29 | #endif 30 | 31 | #define log_errno(prio, msg...) _log_write(stderr, __FILE__, __LINE__, __func__, true, prio, ## msg) 32 | #define log_error(prio, msg...) _log_write(stderr, __FILE__, __LINE__, __func__, false, prio, ## msg) 33 | 34 | #ifdef TRANSOCKS_DEBUG 35 | #define LOGD(msg...) log_error(LOG_DEBUG, msg) 36 | #define LOGD_ERRNO(msg...) log_errno(LOG_DEBUG, msg) 37 | #else 38 | #define LOGD(msg...) 39 | #define LOGD_ERRNO(msg...) 40 | #endif 41 | 42 | #define LOGI(msg...) log_error(LOG_INFO, msg) 43 | 44 | #define LOGE(msg...) log_error(LOG_ERR, msg) 45 | #define LOGE_ERRNO(msg...) log_errno(LOG_ERR, msg) 46 | 47 | #define FATAL(msg...) do { LOGE(msg); exit(EXIT_FAILURE); } while(0) 48 | #define FATAL_ERRNO(msg...) do { LOGE_ERRNO(msg); exit(EXIT_FAILURE); } while(0) 49 | #define FATAL_WITH_HELPMSG(msg...) do { LOGE(msg); print_help(); exit(EXIT_FAILURE); } while(0) 50 | #define PRINTHELP_EXIT() do { print_help(); exit(EXIT_FAILURE); } while(0) 51 | 52 | TRANSOCKS_ATTR(format(printf, 7, 8)) 53 | void 54 | _log_write(FILE *fd, const char *file, int line, const char *func, bool do_errno, int priority, const char *fmt, ...); 55 | 56 | #endif //TRANSOCKS_WONG_LOG_H 57 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.2) 2 | project(transocks-wong) 3 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 4 | 5 | include(TrMacros) 6 | 7 | if(NOT CMAKE_BUILD_TYPE) 8 | set(CMAKE_BUILD_TYPE Debug) 9 | endif() 10 | add_definitions(-std=c11 -Wno-unused-parameter -D_GNU_SOURCE) 11 | add_executable(transocks-wong 12 | src/transocks.c src/util.c src/util.h src/socks5.h src/log.c src/log.h src/context.c src/context.h src/listener.c src/listener.h src/signal.c src/signal.h src/socks5.c src/pump.c src/pump.h src/bufferpump.c src/bufferpump.h src/splicepump.c src/splicepump.h src/list.h src/mem-allocator.h) 13 | find_package(Libevent2 REQUIRED) 14 | include_directories(${LIBEVENT2_INCLUDE_DIR}) 15 | target_link_libraries(transocks-wong ${LIBEVENT2_LIBRARIES}) 16 | 17 | find_library(LIBRT rt) 18 | if(LIBRT) 19 | target_link_libraries(transocks-wong ${LIBRT}) 20 | endif() 21 | 22 | option(ENABLE_DEBUG "Build as DEBUG mode" OFF) 23 | if(ENABLE_DEBUG) 24 | message(STATUS "DEBUG mode enabled") 25 | add_definitions(-DTRANSOCKS_DEBUG) 26 | else() 27 | message(STATUS "DEBUG mode disabled") 28 | endif() 29 | 30 | tr_list_option(WITH_MEM_ALLOCATOR "Use specified memory allocator" AUTO system mimalloc) 31 | if (WITH_MEM_ALLOCATOR STREQUAL "mimalloc") 32 | message(STATUS "using memory allocator: mimalloc") 33 | add_definitions(-DTRANSOCKS_ALLOCATOR_USE_MIMALLOC) 34 | find_package(mimalloc REQUIRED) 35 | include_directories(${MIMALLOC_INCLUDE_DIR}) 36 | target_link_libraries(transocks-wong ${MIMALLOC_LIBRARIES}) 37 | elseif(WITH_MEM_ALLOCATOR STREQUAL "AUTO" OR WITH_MEM_ALLOCATOR STREQUAL "system") 38 | message(STATUS "using memory allocator: system") 39 | add_definitions(-DTRANSOCKS_ALLOCATOR_USE_SYSTEM) 40 | endif() 41 | 42 | 43 | include(GNUInstallDirs) 44 | install(TARGETS transocks-wong DESTINATION ${CMAKE_INSTALL_BINDIR}) 45 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | dist: xenial 3 | 4 | addons: 5 | apt: 6 | packages: 7 | - tree 8 | - cmake 9 | env: 10 | matrix: 11 | - TRANSOCKS_OPTIONS="-DENABLE_DEBUG=ON -DLibevent2_USE_STATIC_LIB=ON" 12 | - TRANSOCKS_OPTIONS="-DENABLE_DEBUG=ON -DLibevent2_USE_STATIC_LIB=OFF" 13 | - TRANSOCKS_OPTIONS="-DENABLE_DEBUG=OFF -DLibevent2_USE_STATIC_LIB=ON" 14 | - TRANSOCKS_OPTIONS="-DENABLE_DEBUG=OFF -DLibevent2_USE_STATIC_LIB=OFF" 15 | - TRANSOCKS_OPTIONS="-DENABLE_DEBUG=OFF -DLibevent2_USE_STATIC_LIB=ON -DWITH_MEM_ALLOCATOR=mimalloc" 16 | - TRANSOCKS_OPTIONS="-DENABLE_DEBUG=OFF -DLibevent2_USE_STATIC_LIB=OFF -DWITH_MEM_ALLOCATOR=mimalloc" 17 | before_install: 18 | - echo "build libevent2" 19 | - LIBEVENT_VER=2.1.11-stable 20 | - SRCDIR=$PWD 21 | - echo $SRCDIR 22 | - curl -O -L https://github.com/libevent/libevent/releases/download/release-${LIBEVENT_VER}/libevent-${LIBEVENT_VER}.tar.gz 23 | - tar xf libevent-${LIBEVENT_VER}.tar.gz 24 | - pushd libevent-* 25 | - ./configure --prefix=$SRCDIR/libevent-install && make && make install 26 | - tree $SRCDIR/libevent-install 27 | - popd 28 | - echo "build mimalloc" 29 | - MIMALLOC_VER=1.0.8 30 | - SRCDIR=$PWD 31 | - echo $SRCDIR 32 | - curl -O -L https://github.com/microsoft/mimalloc/archive/v${MIMALLOC_VER}.tar.gz 33 | - tar xf v${MIMALLOC_VER}.tar.gz 34 | - pushd mimalloc-${MIMALLOC_VER} 35 | - mkdir mimalloc-install 36 | - pushd mimalloc-install 37 | - cmake -DCMAKE_INSTALL_PREFIX:PATH="$SRCDIR/mimalloc-${MIMALLOC_VER}/mimalloc-install" .. && make install 38 | - tree $SRCDIR/mimalloc-${MIMALLOC_VER}/mimalloc-install 39 | - popd 40 | - popd 41 | script: 42 | - SRCDIR=$PWD 43 | - echo $SRCDIR 44 | - mkdir build 45 | - pushd build 46 | - cmake -DCMAKE_INSTALL_PREFIX:PATH="$SRCDIR/transocks-wong-bin" -DCMAKE_FIND_ROOT_PATH:PATH=$SRCDIR/libevent-install\;$SRCDIR/mimalloc-${MIMALLOC_VER}/mimalloc-install\;/usr $TRANSOCKS_OPTIONS .. 47 | - make install 48 | - tree $SRCDIR/transocks-wong-bin 49 | - ldd $SRCDIR/transocks-wong-bin/bin/transocks-wong 50 | -------------------------------------------------------------------------------- /cmake/MacroPushRequiredVars.cmake: -------------------------------------------------------------------------------- 1 | # this module defines two macros: 2 | # MACRO_PUSH_REQUIRED_VARS() 3 | # and 4 | # MACRO_POP_REQUIRED_VARS() 5 | # use these if you call cmake macros which use 6 | # any of the CMAKE_REQUIRED_XXX variables 7 | # 8 | # Usage: 9 | # MACRO_PUSH_REQUIRED_VARS() 10 | # SET(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -DSOME_MORE_DEF) 11 | # CHECK_FUNCTION_EXISTS(...) 12 | # MACRO_POP_REQUIRED_VARS() 13 | 14 | # Copyright (c) 2006, Alexander Neundorf, 15 | # 16 | # Redistribution and use is allowed according to the terms of the BSD license. 17 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 18 | 19 | MACRO(MACRO_PUSH_REQUIRED_VARS) 20 | 21 | IF(NOT DEFINED _PUSH_REQUIRED_VARS_COUNTER) 22 | SET(_PUSH_REQUIRED_VARS_COUNTER 0) 23 | ENDIF(NOT DEFINED _PUSH_REQUIRED_VARS_COUNTER) 24 | 25 | MATH(EXPR _PUSH_REQUIRED_VARS_COUNTER "${_PUSH_REQUIRED_VARS_COUNTER}+1") 26 | 27 | SET(_CMAKE_REQUIRED_INCLUDES_SAVE_${_PUSH_REQUIRED_VARS_COUNTER} ${CMAKE_REQUIRED_INCLUDES}) 28 | SET(_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_PUSH_REQUIRED_VARS_COUNTER} ${CMAKE_REQUIRED_DEFINITIONS}) 29 | SET(_CMAKE_REQUIRED_LIBRARIES_SAVE_${_PUSH_REQUIRED_VARS_COUNTER} ${CMAKE_REQUIRED_LIBRARIES}) 30 | SET(_CMAKE_REQUIRED_FLAGS_SAVE_${_PUSH_REQUIRED_VARS_COUNTER} ${CMAKE_REQUIRED_FLAGS}) 31 | ENDMACRO(MACRO_PUSH_REQUIRED_VARS) 32 | 33 | MACRO(MACRO_POP_REQUIRED_VARS) 34 | 35 | # don't pop more than we pushed 36 | IF("${_PUSH_REQUIRED_VARS_COUNTER}" GREATER "0") 37 | 38 | SET(CMAKE_REQUIRED_INCLUDES ${_CMAKE_REQUIRED_INCLUDES_SAVE_${_PUSH_REQUIRED_VARS_COUNTER}}) 39 | SET(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_PUSH_REQUIRED_VARS_COUNTER}}) 40 | SET(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_SAVE_${_PUSH_REQUIRED_VARS_COUNTER}}) 41 | SET(CMAKE_REQUIRED_FLAGS ${_CMAKE_REQUIRED_FLAGS_SAVE_${_PUSH_REQUIRED_VARS_COUNTER}}) 42 | 43 | MATH(EXPR _PUSH_REQUIRED_VARS_COUNTER "${_PUSH_REQUIRED_VARS_COUNTER}-1") 44 | ENDIF("${_PUSH_REQUIRED_VARS_COUNTER}" GREATER "0") 45 | 46 | ENDMACRO(MACRO_POP_REQUIRED_VARS) 47 | -------------------------------------------------------------------------------- /src/pump.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/30/18. 3 | // 4 | 5 | #include "util.h" 6 | #include "pump.h" 7 | 8 | #include "bufferpump.h" 9 | #include "splicepump.h" 10 | 11 | extern transocks_pump transocks_bufferpump_ops; 12 | extern transocks_pump transocks_splicepump_ops; 13 | 14 | static transocks_pump *transocks_pumps[] = { 15 | &transocks_splicepump_ops, 16 | &transocks_bufferpump_ops, 17 | }; 18 | 19 | static transocks_pump *pumpMethodImpl = NULL; 20 | 21 | int transocks_pump_init(transocks_global_env *env) { 22 | transocks_pump **ppump; 23 | TRANSOCKS_FOREACH(ppump, transocks_pumps) { 24 | if (!strcmp(env->pump_method_name, (*ppump)->name)) { 25 | pumpMethodImpl = *ppump; 26 | break; 27 | } 28 | } 29 | // sanity checks 30 | if (pumpMethodImpl == NULL) { 31 | LOGE("cannot find pump impl %s", env->pump_method_name); 32 | return -1; 33 | } 34 | if (pumpMethodImpl->start_pump_fn == NULL) { 35 | LOGE("unregistered start_pump_fn handler"); 36 | return -1; 37 | } 38 | if (pumpMethodImpl->free_pump_fn == NULL) { 39 | LOGE("unregistered free_pump_fn handler"); 40 | return -1; 41 | } 42 | if (pumpMethodImpl->dump_info_fn == NULL) { 43 | LOGE("unregistered dump_info_fn handler"); 44 | return -1; 45 | } 46 | 47 | return 0; 48 | } 49 | 50 | int transocks_start_pump(transocks_client *pclient) { 51 | if (pumpMethodImpl->start_pump_fn(pclient) != 0) { 52 | LOGE("fail to start pump %s", pumpMethodImpl->name); 53 | return -1; 54 | } 55 | pclient->client_state = client_pumping_data; 56 | return 0; 57 | } 58 | 59 | void transocks_pump_free(transocks_client *pclient) { 60 | pumpMethodImpl->free_pump_fn(pclient); 61 | } 62 | 63 | void transocks_pump_dump_info(transocks_client *pclient, const char *tagfmt, ...) { 64 | va_list args; 65 | va_start(args, tagfmt); 66 | fprintf(stdout, "\n----------\n"); 67 | vfprintf(stdout, tagfmt, args); 68 | va_end(args); 69 | fprintf(stdout, ":\n"); 70 | // pump specific dump info function 71 | pumpMethodImpl->dump_info_fn(pclient); 72 | } 73 | -------------------------------------------------------------------------------- /src/splicepump.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/30/18. 3 | // 4 | 5 | #ifndef TRANSOCKS_WONG_SPLICEPUMP_H 6 | #define TRANSOCKS_WONG_SPLICEPUMP_H 7 | 8 | #include "mem-allocator.h" 9 | 10 | #include "util.h" 11 | #include "context.h" 12 | #include "log.h" 13 | #include "pump.h" 14 | 15 | // used when relaying data between client and relay socket 16 | // via splice syscall (zero copy) 17 | typedef struct transocks_splicepipe_t transocks_splicepipe; 18 | typedef struct transocks_splicepump_t transocks_splicepump; 19 | 20 | typedef struct transocks_splicepipe_t { 21 | int pipe_readfd; // pass pointer to read fd, pipe2() reads these two fds 22 | int pipe_writefd; 23 | size_t data_in_pipe; 24 | size_t capacity; 25 | } transocks_splicepipe; 26 | 27 | typedef struct transocks_splicepump_t { 28 | struct event *client_read_ev; 29 | struct event *client_write_ev; 30 | struct event *relay_read_ev; 31 | struct event *relay_write_ev; 32 | transocks_splicepipe *inbound_pipe; // relay -> client 33 | transocks_splicepipe *outbound_pipe; // client -> relay 34 | } transocks_splicepump; 35 | 36 | typedef enum transocks_splicepump_data_direction_e { 37 | inbound, 38 | outbound, 39 | } transocks_splicepump_data_direction; 40 | 41 | typedef enum transocks_splicepump_splice_result_e { 42 | normal_transfer, 43 | can_retry, 44 | fatal_failure, 45 | read_eof, 46 | } transocks_splicepump_splice_result; 47 | 48 | /* 49 | * According to libevent documentation, event_active() is rarely used, we 50 | * use it to let producer controls consumer(from pipe's perspective), thus 51 | * the event being used should NOT be passed to event_add() as producer 52 | * triggers the consumer and producer controls when the data comes to the end 53 | * We use the simple trick to activate event that doesn't. 54 | */ 55 | #define TRANSOCKS_EVENT_ACTIVE(ev, events) \ 56 | do { \ 57 | if (!event_pending((ev), (events), NULL)) { \ 58 | event_active((ev), (events), 0); \ 59 | } \ 60 | } while (0) 61 | 62 | 63 | #endif //TRANSOCKS_WONG_SPLICEPUMP_H 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # transocks-wong 2 | 3 | ## Feature 4 | 5 | - Event-driven non-blocking I/O model 6 | - IPv4 and IPv6 support, IPv6 works in dual stack mode 7 | - SOCKS5 works in noauth mode 8 | - Buffer copy via bufferevent provided by libevent 9 | - Zero copy via splice() syscall provided by modern Linux kernel 10 | 11 | ## Prerequisite 12 | 13 | netfilter_conntrack, iptables NAT/REDIRECT, modern Linux kernel with IPv6 support 14 | 15 | ## Usage 16 | 17 | Run `transocks-wong -h` to check help text. You can send `SIGHUP` to dump all connection we are handling, 18 | and send `SIGUSR1` to close all connection manually, it's equivalent to restart the program. 19 | 20 | As usual, send `SIGTERM` or `SIGINT` to terminate. 21 | 22 | examples: 23 | 24 | ``` 25 | transocks-wong --listener-addr-port=[::]:8123 --socks5-addr-port=[::1]:1081 --pump-method=splicepump 26 | transocks-wong --listener-addr-port=0.0.0.0:8123 --socks5-addr-port=127.0.0.1:1081 27 | ``` 28 | 29 | ## Other Tips 30 | 31 | DNSMasq can be used to add resolved ip address to the appropriate IPset. 32 | ``` 33 | # /etc/dnsmasq.conf 34 | ipset=//setmefree,setmefree6 35 | ``` 36 | 37 | Use two IPset, one for IPv4, the other one for IPv6, to enable us to 38 | redirect IPv4/IPv6 traffic simultaneously based on the matching result. 39 | ``` 40 | ## /etc/firewall.user(OpenWrt) 41 | # This file is interpreted as shell script. 42 | # drop old one 43 | ipset -! destroy setmefree 44 | ipset -! destroy setmefree6 45 | # new ipset syntax, create TCP ipset 46 | ipset -! create setmefree hash:net family inet 47 | ipset -! create setmefree6 hash:net family inet6 48 | # example to add IP range 49 | #telegram IPs 50 | ipset -! add setmefree 91.108.56.0/23 51 | ipset -! add setmefree 91.108.56.0/22 52 | ipset -! add setmefree 91.108.4.0/22 53 | ipset -! add setmefree 149.154.172.0/22 54 | ipset -! add setmefree 149.154.168.0/22 55 | ipset -! add setmefree 149.154.164.0/22 56 | ipset -! add setmefree 109.239.140.0/24 57 | 58 | ipset -! add setmefree6 2001:b28:f23f::/48 59 | ipset -! add setmefree6 2001:b28:f23d::/48 60 | ipset -! add setmefree6 2001:67c:4e8::/48 61 | # TCP redirect to TCP transparent proxy listening port 62 | iptables -t nat -I PREROUTING -p tcp -m set --match-set setmefree dst -j REDIRECT --to-port 8123 63 | # requires ip6tables nat module 64 | ip6tables -t nat -I PREROUTING -p tcp -m set --match-set setmefree6 dst -j REDIRECT --to-port 8123 65 | ``` 66 | 67 | ## Credit to 68 | 69 | - [transocks-ev](http://oss.tiggerswelt.net/transocks_ev/) 70 | - [redsocks](https://github.com/darkk/redsocks) 71 | - [transocks](https://github.com/cybozu-go/transocks) 72 | -------------------------------------------------------------------------------- /src/socks5.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/24/18. 3 | // 4 | 5 | #ifndef TRANSOCKS_WONG_SOCKS5_H 6 | #define TRANSOCKS_WONG_SOCKS5_H 7 | 8 | #include "mem-allocator.h" 9 | 10 | #include 11 | 12 | #include "context.h" 13 | #include "log.h" 14 | #include "util.h" 15 | 16 | //Memory alignment settings(Part 1) 17 | #pragma pack(push) //Push current alignment to stack. 18 | #pragma pack(1) //Set alignment to 1 byte boundary. 19 | 20 | #define SOCKS5_VERSION 0x05 21 | #define SOCKS5_METHOD_NOAUTH 0x00 22 | #define SOCKS5_METHOD_UNACCEPTABLE 0xff 23 | 24 | #define SOCKS5_CMD_CONNECT 0x01 25 | #define SOCKS5_CMD_BIND 0x02 26 | #define SOCKS5_CMD_UDP_ASSOCIATE 0x03 27 | 28 | #define SOCKS5_ATYP_IPV4 0x01 29 | #define SOCKS5_ATYP_DOMAIN 0x03 30 | #define SOCKS5_ATYP_IPV6 0x04 31 | 32 | #define SOCKS5_REP_SUCCEEDED 0x00 33 | #define SOCKS5_REP_GENERAL 0x01 34 | #define SOCKS5_REP_CONN_DISALLOWED 0x02 35 | #define SOCKS5_REP_NETWORK_UNREACHABLE 0x03 36 | #define SOCKS5_REP_HOST_UNREACHABLE 0x04 37 | #define SOCKS5_REP_CONN_REFUSED 0x05 38 | #define SOCKS5_REP_TTL_EXPIRED 0x06 39 | #define SOCKS5_REP_CMD_NOT_SUPPORTED 0x07 40 | #define SOCKS5_REP_ADDRTYPE_NOT_SUPPORTED 0x08 41 | #define SOCKS5_REP_FF_UNASSIGNED 0x09 42 | 43 | struct socks_method_select_request { 44 | unsigned char ver; 45 | unsigned char nmethods; 46 | unsigned char methods[1]; 47 | }; 48 | 49 | struct socks_method_select_response { 50 | unsigned char ver; 51 | unsigned char method; 52 | }; 53 | 54 | struct socks_request_ipv4 { 55 | unsigned char ver; 56 | unsigned char cmd; 57 | unsigned char rsv; 58 | unsigned char atyp; 59 | struct in_addr addr; 60 | uint16_t port; 61 | }; 62 | 63 | struct socks_request_ipv6 { 64 | unsigned char ver; 65 | unsigned char cmd; 66 | unsigned char rsv; 67 | unsigned char atyp; 68 | struct in6_addr addr; 69 | uint16_t port; 70 | }; 71 | 72 | struct socks_response_ipv4 { 73 | unsigned char ver; 74 | unsigned char rep; 75 | unsigned char rsv; 76 | unsigned char atyp; 77 | struct in_addr addr; 78 | uint16_t port; 79 | }; 80 | 81 | struct socks_response_ipv6 { 82 | unsigned char ver; 83 | unsigned char rep; 84 | unsigned char rsv; 85 | unsigned char atyp; 86 | struct in6_addr addr; 87 | uint16_t port; 88 | }; 89 | 90 | struct socks_response_header { 91 | unsigned char ver; 92 | unsigned char rep; 93 | unsigned char rsv; 94 | unsigned char atyp; 95 | }; 96 | 97 | 98 | //Memory alignment settings(Part 2) 99 | #pragma pack(pop) //Restore original alignment from stack. 100 | 101 | 102 | void transocks_start_connect_relay(transocks_client *); 103 | 104 | #endif //TRANSOCKS_WONG_SOCKS5_H 105 | -------------------------------------------------------------------------------- /src/signal.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/27/18. 3 | // 4 | 5 | #include "signal.h" 6 | 7 | static void terminate_signal_cb(evutil_socket_t fd, short events, void *arg); 8 | 9 | static void dump_client_info_signal_cb(evutil_socket_t fd, short events, void *arg); 10 | 11 | static void drop_all_clients_signal_cb(evutil_socket_t fd, short events, void *arg); 12 | 13 | 14 | static void terminate_signal_cb(evutil_socket_t fd, short events, void *arg) { 15 | TRANSOCKS_UNUSED(fd); 16 | TRANSOCKS_UNUSED(events); 17 | transocks_global_env *env = (transocks_global_env *) arg; 18 | if (event_base_loopbreak(env->eventBaseLoop) != 0) 19 | LOGE("fail to event_base_loopbreak"); 20 | } 21 | 22 | static void dump_client_info_signal_cb(evutil_socket_t fd, short events, void *arg) { 23 | TRANSOCKS_UNUSED(fd); 24 | TRANSOCKS_UNUSED(events); 25 | transocks_global_env *env = (transocks_global_env *) arg; 26 | transocks_dump_all_client_info(env); 27 | } 28 | 29 | static void drop_all_clients_signal_cb(evutil_socket_t fd, short events, void *arg) { 30 | TRANSOCKS_UNUSED(fd); 31 | TRANSOCKS_UNUSED(events); 32 | transocks_global_env *env = (transocks_global_env *) arg; 33 | transocks_drop_all_clients(env); 34 | } 35 | 36 | int signal_init(transocks_global_env *env) { 37 | env->sigterm_ev = evsignal_new(env->eventBaseLoop, SIGTERM, terminate_signal_cb, env); 38 | env->sigint_ev = evsignal_new(env->eventBaseLoop, SIGINT, terminate_signal_cb, env); 39 | env->sighup_ev = evsignal_new(env->eventBaseLoop, SIGHUP, dump_client_info_signal_cb, env); 40 | env->sigusr1_ev = evsignal_new(env->eventBaseLoop, SIGUSR1, drop_all_clients_signal_cb, env); 41 | 42 | if (env->sigterm_ev == NULL 43 | || env->sigint_ev == NULL 44 | || env->sighup_ev == NULL 45 | || env->sigusr1_ev == NULL) { 46 | LOGE("fail to allocate evsignal"); 47 | return -1; 48 | } 49 | 50 | if (evsignal_add(env->sigterm_ev, NULL) != 0) { 51 | LOGE("fail to add SIGTERM"); 52 | return -1; 53 | } 54 | if (evsignal_add(env->sigint_ev, NULL) != 0) { 55 | LOGE("fail to add SIGINT"); 56 | return -1; 57 | } 58 | if (evsignal_add(env->sighup_ev, NULL) != 0) { 59 | LOGE("fail to add SIGHUP"); 60 | return -1; 61 | } 62 | if (evsignal_add(env->sigusr1_ev, NULL) != 0) { 63 | LOGE("fail to add SIGUSR1"); 64 | return -1; 65 | } 66 | 67 | return 0; 68 | } 69 | 70 | void signal_deinit(transocks_global_env *env) { 71 | if (env == NULL) return; 72 | if (env->sigint_ev != NULL) { 73 | evsignal_del(env->sigint_ev); 74 | TRANSOCKS_FREE(event_free, env->sigint_ev); 75 | } 76 | if (env->sigterm_ev != NULL) { 77 | evsignal_del(env->sigterm_ev); 78 | TRANSOCKS_FREE(event_free, env->sigterm_ev); 79 | } 80 | if (env->sighup_ev != NULL) { 81 | evsignal_del(env->sighup_ev); 82 | TRANSOCKS_FREE(event_free, env->sighup_ev); 83 | } 84 | if (env->sigusr1_ev != NULL) { 85 | evsignal_del(env->sigusr1_ev); 86 | TRANSOCKS_FREE(event_free, env->sigusr1_ev); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/context.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/25/18. 3 | // 4 | 5 | #ifndef TRANSOCKS_WONG_CONTEXT_H 6 | #define TRANSOCKS_WONG_CONTEXT_H 7 | 8 | #include "mem-allocator.h" 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "util.h" 19 | #include "log.h" 20 | #include "list.h" 21 | 22 | enum transocks_client_state { 23 | client_NEW, 24 | client_relay_connected, 25 | client_socks5_finish_handshake, 26 | client_pumping_data, 27 | client_DESTROYING, 28 | client_DESTROYED 29 | }; 30 | 31 | /* forward declaration */ 32 | 33 | // global configuration and global environment 34 | // free only exit the program 35 | typedef struct transocks_global_env_t transocks_global_env; 36 | 37 | // the client entity carrying essential metadata 38 | typedef struct transocks_client_t transocks_client; 39 | 40 | // the connection listener 41 | typedef struct transocks_listener_t transocks_listener; 42 | 43 | /* detailed declaration */ 44 | 45 | typedef struct transocks_global_env_t { 46 | char *pump_method_name; // pump method name 47 | struct sockaddr_storage *bind_addr; // listener addr 48 | struct sockaddr_storage *relay_addr; // SOCKS5 server addr 49 | socklen_t bind_addr_len; // listener addr socklen 50 | socklen_t relay_addr_len; // SOCKS5 server addr socklen 51 | struct event_base *eventBaseLoop; 52 | transocks_listener *listener; 53 | struct event *sigterm_ev; 54 | struct event *sigint_ev; 55 | struct event *sighup_ev; 56 | struct event *sigusr1_ev; 57 | struct list_head current_clients_dlinklist; // double link list of clients 58 | } transocks_global_env; 59 | 60 | 61 | typedef struct transocks_client_t { 62 | struct list_head single_client_dlinklist_entry; 63 | struct transocks_global_env_t *global_env; 64 | struct sockaddr_storage *client_addr; // accepted client addr 65 | struct sockaddr_storage *dest_addr; // accepted client destination addr (from iptables) 66 | int client_fd; // accepted client fd 67 | int relay_fd; 68 | socklen_t client_addr_len; // accepted client addr socklen 69 | socklen_t dest_addr_len; // accepted client destination addr socklen 70 | struct bufferevent *client_bev; // client output -> relay input 71 | struct bufferevent *relay_bev; // relay output -> client input 72 | struct event *timeout_ev; 73 | void *user_arg; 74 | enum transocks_client_state client_state; 75 | bool client_shutdown_read; 76 | bool client_shutdown_write; 77 | bool relay_shutdown_read; 78 | bool relay_shutdown_write; 79 | } transocks_client; 80 | 81 | typedef struct transocks_listener_t { 82 | int listener_fd; // listener socket fd 83 | struct event *listener_ev; // listener EV_READ 84 | } transocks_listener; 85 | 86 | /* context structures util functions */ 87 | 88 | transocks_global_env *transocks_global_env_new(void); 89 | 90 | void transocks_global_env_free(transocks_global_env *); 91 | 92 | transocks_client *transocks_client_new(transocks_global_env *); 93 | 94 | void transocks_client_free(transocks_client *); 95 | 96 | int transocks_client_set_timeout(transocks_client *, const struct timeval *, event_callback_fn, void *); 97 | 98 | void transocks_drop_all_clients(transocks_global_env *); 99 | 100 | void transocks_dump_all_client_info(transocks_global_env *); 101 | 102 | void transocks_client_dump_info(transocks_client *pclient); 103 | 104 | #endif //TRANSOCKS_WONG_CONTEXT_H 105 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/24/18. 3 | // 4 | 5 | #ifndef TRANSOCKS_WONG_UTIL_H 6 | #define TRANSOCKS_WONG_UTIL_H 7 | 8 | #include "mem-allocator.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include /* inet_pton, inet_ntop */ 23 | #include 24 | #include /* socket(), bind(),listen(), accept(),getsockopt() */ 25 | #include /* For IFNAMSIZ. Silly Travis CI Ubuntu kernel */ 26 | #include /* SO_ORIGINAL_DST */ 27 | #include /* IP6T_SO_ORIGINAL_DST */ 28 | 29 | #include 30 | 31 | 32 | #if defined(__GNUC__) || defined(__clang__) 33 | #define TRANSOCKS_ATTR(s) __attribute__((s)) 34 | #else 35 | #define TRANSOCKS_ATTR(s) 36 | #endif 37 | 38 | #define TRANSOCKS_ALWAYS_INLINE TRANSOCKS_ATTR(always_inline) inline 39 | 40 | 41 | #ifndef SO_ORIGINAL_DST 42 | #warning using custom SO_ORIGINAL_DST 43 | #define SO_ORIGINAL_DST 80 44 | #endif 45 | 46 | #ifndef IP6T_SO_ORIGINAL_DST 47 | #warning using custom IP6T_SO_ORIGINAL_DST 48 | #define IP6T_SO_ORIGINAL_DST 80 49 | #endif 50 | 51 | #define TRANSOCKS_INET_PORTSTRLEN (5 + 1) 52 | /* '[' + INET6_ADDRSTRLEN + ']' + ':' + "65535" + NUL */ 53 | #define TRANSOCKS_INET_ADDRPORTSTRLEN (1 + INET6_ADDRSTRLEN + 1 + 1 + TRANSOCKS_INET_PORTSTRLEN + 1) 54 | 55 | #define TRANSOCKS_SIZEOF_ARRAY(arr) (sizeof(arr) / sizeof(arr[0])) 56 | #define TRANSOCKS_FOREACH(ptr, array) for (ptr = array; ptr < array + TRANSOCKS_SIZEOF_ARRAY(array); ptr++) 57 | #define TRANSOCKS_FOREACH_REVERSE(ptr, array) for (ptr = array + TRANSOCKS_SIZEOF_ARRAY(array) - 1; ptr >= array; ptr--) 58 | #define TRANSOCKS_UNUSED(obj) ((void)(obj)) 59 | 60 | #define TRANSOCKS_CHKBIT(val, flag) (((val) & (flag)) == (flag)) 61 | #define TRANSOCKS_SETBIT(val, flag) ((val) |= (flag)) 62 | #define TRANSOCKS_CLRBIT(val, flag) ((val) &= ~(flag)) 63 | #define TRANSOCKS_TOGGLEBIT(val, flag) ((val) ^= (flag)) 64 | #define TRANSOCKS_BUFSIZE (4096) 65 | #define TRANSOCKS_IS_RETRIABLE(err) ((err) == EAGAIN || (err) == EWOULDBLOCK || (err) == EINTR) 66 | 67 | #define TRANSOCKS_FREE(free_fn, ptr) \ 68 | do { \ 69 | if ((ptr) != NULL) { \ 70 | free_fn(ptr); \ 71 | (ptr) = NULL; \ 72 | } \ 73 | } while (0) 74 | 75 | #define TRANSOCKS_CLOSE(fd) \ 76 | do { \ 77 | if ((fd) >= 0) { \ 78 | close(fd); \ 79 | (fd) = -1; \ 80 | } \ 81 | } while (0) 82 | 83 | #define TRANSOCKS_SHUTDOWN(fd, how) shutdown(fd, how) 84 | 85 | #define TRANSOCKS_IS_INVALID_FD(fd) ((fd) == -1) 86 | 87 | enum { 88 | GETOPT_VAL_LISTENERADDRPORT, 89 | GETOPT_VAL_SOCKS5ADDRPORT, 90 | GETOPT_VAL_PUMPMETHOD, 91 | GETOPT_VAL_HELP 92 | }; 93 | 94 | void generate_sockaddr_port_str(char *, size_t, const struct sockaddr *, socklen_t); 95 | 96 | int apply_tcp_keepalive(int); 97 | 98 | int apply_ipv6only(int, int); 99 | 100 | int apply_tcp_nodelay(int); 101 | 102 | int createpipe(int *readfd, int *writefd); 103 | 104 | int setnonblocking(int, bool); 105 | 106 | int getorigdst(int, struct sockaddr_storage *, socklen_t *); 107 | 108 | bool validateAddrPort(struct sockaddr_storage *); 109 | 110 | int transocks_parse_sockaddr_port(const char *str, struct sockaddr *sa, socklen_t *actualSockAddrLen); 111 | 112 | void print_help(void); 113 | 114 | #endif //TRANSOCKS_WONG_UTIL_H 115 | -------------------------------------------------------------------------------- /src/transocks.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/24/18. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "util.h" 12 | #include "log.h" 13 | #include "context.h" 14 | #include "signal.h" 15 | #include "listener.h" 16 | #include "pump.h" 17 | 18 | 19 | static transocks_global_env *globalEnv = NULL; 20 | 21 | int main(int argc, char **argv) { 22 | int opt; 23 | 24 | char *listenerAddrPort = NULL; 25 | char *socks5AddrPort = NULL; 26 | char *pumpMethod = NULL; 27 | 28 | struct sockaddr_storage listener_ss; 29 | socklen_t listener_ss_size; 30 | struct sockaddr_storage socks5_ss; 31 | socklen_t socks5_ss_size; 32 | 33 | static struct option long_options[] = { 34 | {"listener-addr-port", required_argument, NULL, GETOPT_VAL_LISTENERADDRPORT}, 35 | {"socks5-addr-port", required_argument, NULL, GETOPT_VAL_SOCKS5ADDRPORT}, 36 | {"pump-method", optional_argument, NULL, GETOPT_VAL_PUMPMETHOD}, 37 | {"help", no_argument, NULL, GETOPT_VAL_HELP}, 38 | {NULL, 0, NULL, 0} 39 | }; 40 | 41 | while ((opt = getopt_long(argc, argv, "", long_options, NULL)) != -1) { 42 | switch (opt) { 43 | case GETOPT_VAL_LISTENERADDRPORT: 44 | listenerAddrPort = optarg; 45 | break; 46 | case GETOPT_VAL_SOCKS5ADDRPORT: 47 | socks5AddrPort = optarg; 48 | break; 49 | case GETOPT_VAL_PUMPMETHOD: 50 | pumpMethod = optarg; 51 | break; 52 | case '?': 53 | case 'h': 54 | case GETOPT_VAL_HELP: 55 | PRINTHELP_EXIT(); 56 | default: 57 | PRINTHELP_EXIT(); 58 | } 59 | } 60 | 61 | if (listenerAddrPort == NULL 62 | || socks5AddrPort == NULL) { 63 | PRINTHELP_EXIT(); 64 | } 65 | 66 | if (pumpMethod == NULL) { 67 | pumpMethod = PUMPMETHOD_BUFFER; 68 | } 69 | 70 | // ignore SIGPIPE 71 | signal(SIGPIPE, SIG_IGN); 72 | 73 | if (transocks_parse_sockaddr_port(listenerAddrPort, (struct sockaddr *) &listener_ss, &listener_ss_size) != 0) { 74 | FATAL_WITH_HELPMSG("invalid listener address and port: %s", listenerAddrPort); 75 | } 76 | if (transocks_parse_sockaddr_port(socks5AddrPort, (struct sockaddr *) &socks5_ss, &socks5_ss_size) != 0) { 77 | FATAL_WITH_HELPMSG("invalid socks5 address and port: %s", socks5AddrPort); 78 | } 79 | 80 | // check if port exists 81 | if (!validateAddrPort(&listener_ss)) { 82 | FATAL_WITH_HELPMSG("fail to parse listener address port: %s", listenerAddrPort); 83 | } 84 | if (!validateAddrPort(&socks5_ss)) { 85 | FATAL_WITH_HELPMSG("fail to parse socks5 address port: %s", socks5AddrPort); 86 | } 87 | 88 | 89 | globalEnv = transocks_global_env_new(); 90 | if (globalEnv == NULL) { 91 | goto bareExit; 92 | } 93 | if (signal_init(globalEnv) != 0) { 94 | goto shutdown; 95 | } 96 | memcpy(globalEnv->bind_addr, &listener_ss, sizeof(struct sockaddr_storage)); 97 | globalEnv->bind_addr_len = listener_ss_size; 98 | memcpy(globalEnv->relay_addr, &socks5_ss, sizeof(struct sockaddr_storage)); 99 | globalEnv->relay_addr_len = socks5_ss_size; 100 | 101 | if (listener_init(globalEnv) != 0) { 102 | goto shutdown; 103 | } 104 | globalEnv->pump_method_name = strdup(pumpMethod); 105 | if (globalEnv->pump_method_name == NULL) { 106 | goto shutdown; 107 | } 108 | 109 | if (transocks_pump_init(globalEnv) != 0) { 110 | goto shutdown; 111 | } 112 | 113 | LOGI("transocks-wong started"); 114 | LOGI("using memory allocator: " TR_USED_MEM_ALLOCATOR); 115 | LOGI("using pumpmethod: %s", globalEnv->pump_method_name); 116 | 117 | // start event loop 118 | event_base_dispatch(globalEnv->eventBaseLoop); 119 | 120 | LOGI("exited event loop, shutting down.."); 121 | 122 | shutdown: 123 | 124 | // exit gracefully 125 | transocks_drop_all_clients(globalEnv); 126 | // report intentional event loop break 127 | if (event_base_got_exit(globalEnv->eventBaseLoop) 128 | || event_base_got_break(globalEnv->eventBaseLoop)) { 129 | LOGE("exited event loop intentionally"); 130 | } 131 | // we are done, bye 132 | TRANSOCKS_FREE(transocks_global_env_free, globalEnv); 133 | bareExit: 134 | return 0; 135 | } -------------------------------------------------------------------------------- /src/listener.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/27/18. 3 | // 4 | 5 | #include "listener.h" 6 | 7 | static void listener_cb(evutil_socket_t, short, void *); 8 | 9 | static void listener_cb(evutil_socket_t fd, short events, void *userArg) { 10 | TRANSOCKS_UNUSED(fd); 11 | TRANSOCKS_UNUSED(events); 12 | transocks_global_env *env = (transocks_global_env *) userArg; 13 | int clientFd; 14 | struct sockaddr_storage acceptedSrcSockAddr; 15 | socklen_t acceptedSrcSockLen = sizeof(struct sockaddr_storage); 16 | clientFd = accept(env->listener->listener_fd, 17 | (struct sockaddr *) (&acceptedSrcSockAddr), &acceptedSrcSockLen); 18 | if (clientFd < 0) { 19 | if (TRANSOCKS_IS_RETRIABLE(errno)) { 20 | // wait for next event 21 | return; 22 | } else { 23 | LOGE_ERRNO("accept() err"); 24 | return; 25 | } 26 | } 27 | if (acceptedSrcSockLen == 0) { 28 | goto freeFd; 29 | } 30 | if (setnonblocking(clientFd, true) != 0) { 31 | LOGE("fail to set nonblocking"); 32 | goto freeFd; 33 | } 34 | 35 | if (apply_tcp_keepalive(clientFd) != 0) { 36 | LOGE("fail to set TCP keepalive"); 37 | goto freeFd; 38 | } 39 | if (apply_tcp_nodelay(clientFd) != 0) { 40 | LOGE("fail to set TCP nodelay"); 41 | goto freeFd; 42 | } 43 | 44 | transocks_client *pclient = transocks_client_new(env); 45 | 46 | if (pclient == NULL) { 47 | LOGE("fail to allocate memory"); 48 | goto freeClient; 49 | } 50 | 51 | char srcaddrstr[TRANSOCKS_INET_ADDRPORTSTRLEN]; 52 | char bindaddrstr[TRANSOCKS_INET_ADDRPORTSTRLEN]; 53 | char destaddrstr[TRANSOCKS_INET_ADDRPORTSTRLEN]; 54 | generate_sockaddr_port_str(bindaddrstr, TRANSOCKS_INET_ADDRPORTSTRLEN, 55 | (const struct sockaddr *) env->bind_addr, env->bind_addr_len); 56 | 57 | pclient->client_addr_len = acceptedSrcSockLen; 58 | memcpy((void *) (pclient->client_addr), (void *) (&acceptedSrcSockAddr), acceptedSrcSockLen); 59 | generate_sockaddr_port_str(srcaddrstr, TRANSOCKS_INET_ADDRPORTSTRLEN, 60 | (const struct sockaddr *) (&acceptedSrcSockAddr), acceptedSrcSockLen); 61 | 62 | 63 | if (getorigdst(clientFd, pclient->dest_addr, &pclient->dest_addr_len) != 0) { 64 | LOGE("fail to get origdestaddr, close fd"); 65 | goto freeClient; 66 | } 67 | 68 | generate_sockaddr_port_str(destaddrstr, TRANSOCKS_INET_ADDRPORTSTRLEN, 69 | (const struct sockaddr *) (pclient->dest_addr), pclient->dest_addr_len); 70 | 71 | 72 | LOGI("%s accept a conn %s -> %s", bindaddrstr, srcaddrstr, destaddrstr); 73 | pclient->client_fd = clientFd; 74 | pclient->global_env = env; 75 | 76 | struct bufferevent *client_bev = bufferevent_socket_new(env->eventBaseLoop, 77 | -1, BEV_OPT_CLOSE_ON_FREE); 78 | 79 | if (client_bev == NULL) { 80 | LOGE("bufferevent_socket_new"); 81 | goto freeBev; 82 | } 83 | 84 | // should not read any data from client now, until handshake is finished 85 | if (bufferevent_disable(client_bev, EV_READ) != 0) { 86 | LOGE("bufferevent_disable read"); 87 | goto freeBev; 88 | } 89 | if (bufferevent_setfd(client_bev, clientFd) != 0) { 90 | LOGE("bufferevent_setfd client"); 91 | goto freeBev; 92 | } 93 | 94 | pclient->client_bev = client_bev; 95 | 96 | list_add_tail(&(pclient->single_client_dlinklist_entry), &(env->current_clients_dlinklist)); 97 | 98 | // start connecting SOCKS5 relay 99 | transocks_start_connect_relay(pclient); 100 | return; 101 | 102 | freeBev: 103 | TRANSOCKS_FREE(bufferevent_free, client_bev); 104 | 105 | freeClient: 106 | TRANSOCKS_FREE(transocks_client_free, pclient); 107 | 108 | freeFd: 109 | TRANSOCKS_CLOSE(clientFd); 110 | } 111 | 112 | int listener_init(transocks_global_env *env) { 113 | int on = 1; 114 | int err; 115 | int fd = socket(env->bind_addr->ss_family, SOCK_STREAM, 0); 116 | if (fd < 0) { 117 | LOGE_ERRNO("fail to create socket"); 118 | return -1; 119 | } 120 | err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 121 | 122 | if (err != 0) { 123 | LOGE_ERRNO("fail to set SO_REUSEADDR"); 124 | goto closeFd; 125 | } 126 | err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); 127 | if (err != 0) { 128 | LOGE_ERRNO("fail to set SO_REUSEPORT"); 129 | goto closeFd; 130 | } 131 | if (setnonblocking(fd, true) != 0) { 132 | LOGE("fail to set non-blocking"); 133 | goto closeFd; 134 | } 135 | // ensure we accept both ipv4 and ipv6 136 | if (env->bind_addr->ss_family == AF_INET6) { 137 | if (apply_ipv6only(fd, 0) != 0) { 138 | LOGE("fail to disable IPV6_V6ONLY"); 139 | goto closeFd; 140 | } 141 | } 142 | 143 | err = bind(fd, (struct sockaddr *) (env->bind_addr), env->bind_addr_len); 144 | if (err != 0) { 145 | LOGE_ERRNO("fail to bind"); 146 | goto closeFd; 147 | } 148 | 149 | err = listen(fd, SOMAXCONN); 150 | if (err != 0) { 151 | LOGE_ERRNO("fail to listen"); 152 | goto closeFd; 153 | } 154 | 155 | env->listener = tr_malloc(sizeof(struct transocks_listener_t)); 156 | if (env->listener == NULL) { 157 | LOGE("fail to allocate memory"); 158 | goto freeListener; 159 | } 160 | env->listener->listener_fd = fd; 161 | env->listener->listener_ev = event_new(env->eventBaseLoop, fd, 162 | EV_READ | EV_PERSIST, listener_cb, env); 163 | if (env->listener->listener_ev == NULL) { 164 | LOGE("fail to allocate memory"); 165 | goto freeListener; 166 | } 167 | if (event_add(env->listener->listener_ev, NULL) != 0) { 168 | LOGE("fail to add listener_ev"); 169 | goto freeListener; 170 | } 171 | 172 | return 0; 173 | 174 | freeListener: 175 | listener_deinit(env); 176 | 177 | closeFd: 178 | TRANSOCKS_CLOSE(fd); 179 | 180 | return -1; 181 | } 182 | 183 | void listener_deinit(transocks_global_env *env) { 184 | if (env == NULL) return; 185 | if (env->listener != NULL) { 186 | if (env->listener->listener_ev != NULL) { 187 | event_del(env->listener->listener_ev); 188 | TRANSOCKS_FREE(event_free, env->listener->listener_ev); 189 | } 190 | TRANSOCKS_CLOSE(env->listener->listener_fd); 191 | TRANSOCKS_FREE(tr_free, env->listener); 192 | } 193 | } -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/24/18. 3 | // 4 | 5 | #include "util.h" 6 | #include "log.h" 7 | #include "pump.h" 8 | 9 | void generate_sockaddr_port_str(char *outstrbuf, size_t strbufsize, const struct sockaddr *sa, socklen_t socklen) { 10 | char ipstr[INET6_ADDRSTRLEN]; 11 | uint16_t port; 12 | 13 | if (socklen == sizeof(struct sockaddr_in) || sa->sa_family == AF_INET) { 14 | const struct sockaddr_in *in = (const struct sockaddr_in *) sa; 15 | if (inet_ntop(AF_INET, &(in->sin_addr), ipstr, INET_ADDRSTRLEN) == NULL) { 16 | LOGE_ERRNO("inet_ntop"); 17 | } 18 | port = ntohs(in->sin_port); 19 | snprintf(outstrbuf, strbufsize, "%s:%d", ipstr, port); 20 | } else if (socklen == sizeof(struct sockaddr_in6) || sa->sa_family == AF_INET6) { 21 | const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *) sa; 22 | if (inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN) == NULL) { 23 | LOGE_ERRNO("inet_ntop"); 24 | } 25 | port = ntohs(in6->sin6_port); 26 | snprintf(outstrbuf, strbufsize, "[%s]:%d", ipstr, port); 27 | } else { 28 | LOGE("unknown sa_family %d, socklen %d", sa->sa_family, socklen); 29 | } 30 | } 31 | 32 | int apply_tcp_keepalive(int fd) { 33 | if (fd < 0) return -1; 34 | int keepAlive = 1; 35 | int keepIdle = 40; 36 | int keepInterval = 20; 37 | int keepCount = 5; 38 | #ifdef SO_KEEPALIVE 39 | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *) &keepAlive, sizeof(keepAlive)) < 0) { 40 | LOGE_ERRNO("fail to set SO_KEEPALIVE"); 41 | return -1; 42 | } 43 | #endif 44 | #ifdef TCP_KEEPIDLE 45 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (void *) &keepIdle, sizeof(keepIdle)) < 0) { 46 | LOGE_ERRNO("fail to set TCP_KEEPIDLE"); 47 | return -1; 48 | } 49 | #endif 50 | #ifdef TCP_KEEPINTVL 51 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, (void *) &keepInterval, sizeof(keepInterval)) < 0) { 52 | LOGE_ERRNO("fail to set TCP_KEEPINTVL"); 53 | return -1; 54 | } 55 | #endif 56 | #ifdef TCP_KEEPCNT 57 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, (void *) &keepCount, sizeof(keepCount)) < 0) { 58 | LOGE_ERRNO("fail to set TCP_KEEPCNT"); 59 | return -1; 60 | } 61 | #endif 62 | return 0; 63 | } 64 | 65 | int apply_ipv6only(int fd, int on) { 66 | if (fd < 0) return -1; 67 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &on, sizeof(int)) < 0) { 68 | LOGE_ERRNO("fail to set IPV6_V6ONLY"); 69 | return -1; 70 | } 71 | return 0; 72 | } 73 | 74 | int apply_tcp_nodelay(int fd) { 75 | if (fd < 0) return -1; 76 | int on = 1; 77 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *) &on, sizeof(on)) < 0) { 78 | LOGE_ERRNO("fail to set TCP_NODELAY"); 79 | return -1; 80 | } 81 | return 0; 82 | } 83 | 84 | int setnonblocking(int fd, bool isenable) { 85 | if (fd < 0) return -1; 86 | int flags = fcntl(fd, F_GETFL); 87 | if (flags == -1) { 88 | LOGE_ERRNO("fcntl F_GETFL"); 89 | return -1; 90 | } 91 | if (isenable) { 92 | TRANSOCKS_SETBIT(flags, O_NONBLOCK); 93 | } else { 94 | TRANSOCKS_CLRBIT(flags, O_NONBLOCK); 95 | } 96 | if (fcntl(fd, F_SETFL, flags) != 0) { 97 | LOGE_ERRNO("fcntl F_SETFL %x", flags); 98 | return -1; 99 | } 100 | return 0; 101 | } 102 | 103 | 104 | int createpipe(int *readEndFd, int *writeEndFd) { 105 | int pipefds[2]; 106 | int ret = pipe(pipefds); 107 | if (ret == -1) { 108 | LOGE_ERRNO("pipe"); 109 | return -1; 110 | } 111 | if (setnonblocking(pipefds[0], true) != 0) { 112 | return -1; 113 | } 114 | if (setnonblocking(pipefds[1], true) != 0) { 115 | return -1; 116 | } 117 | *readEndFd = pipefds[0]; 118 | *writeEndFd = pipefds[1]; 119 | return 0; 120 | } 121 | 122 | int getorigdst(int fd, struct sockaddr_storage *destaddr, socklen_t *addrlen) { 123 | socklen_t v6_len = sizeof(struct sockaddr_in6); 124 | socklen_t v4_len = sizeof(struct sockaddr_in); 125 | int err; 126 | err = getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, destaddr, &v6_len); 127 | if (err) { 128 | LOGD_ERRNO("IP6T_SO_ORIGINAL_DST"); 129 | err = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &v4_len); 130 | if (err) { 131 | LOGE_ERRNO("SO_ORIGINAL_DST"); 132 | return -1; 133 | } 134 | *addrlen = v4_len; 135 | return 0; 136 | } 137 | *addrlen = v6_len; 138 | return 0; 139 | } 140 | 141 | bool validateAddrPort(struct sockaddr_storage *ss) { 142 | struct sockaddr_in6 *in6 = NULL; 143 | struct sockaddr_in *in = NULL; 144 | switch (ss->ss_family) { 145 | case AF_INET: 146 | in = (struct sockaddr_in *) ss; 147 | if (in->sin_port == 0) { 148 | return false; 149 | } 150 | break; 151 | case AF_INET6: 152 | in6 = (struct sockaddr_in6 *) ss; 153 | if (in6->sin6_port == 0) { 154 | return false; 155 | } 156 | if (IN6_IS_ADDR_V4MAPPED(&(in6->sin6_addr))) { 157 | LOGE("Please input IPv4 address directly"); 158 | return false; 159 | } 160 | break; 161 | default: 162 | LOGE("unknown sockaddr_storage ss_family\n"); 163 | return false; 164 | } 165 | return true; 166 | } 167 | 168 | int transocks_parse_sockaddr_port(const char *str, struct sockaddr *sa, socklen_t *actualSockAddrLen) { 169 | int v6len = sizeof(struct sockaddr_in6); 170 | int v4len = sizeof(struct sockaddr_in); 171 | 172 | int err; 173 | err = evutil_parse_sockaddr_port(str, sa, &v6len); 174 | if (err != 0) { 175 | // try again as v4 176 | err = evutil_parse_sockaddr_port(str, sa, &v4len); 177 | if (err != 0) { 178 | return -1; 179 | } 180 | *actualSockAddrLen = (socklen_t) v4len; 181 | return 0; 182 | } 183 | *actualSockAddrLen = (socklen_t) v6len; 184 | return 0; 185 | } 186 | 187 | void print_help() { 188 | fprintf(stdout, "transocks-wong help:\n"); 189 | fprintf(stdout, "\t WARNING: data must be NATed to our listener endpoint\n"); 190 | fprintf(stdout, "\t --listener-addr-port what address and port we are listening\n"); 191 | fprintf(stdout, "\t --socks5-addr-port the SOCKS5 server address and port\n"); 192 | fprintf(stdout, "\t --pump-method " PUMPMETHOD_BUFFER "/" PUMPMETHOD_SPLICE "\n"); 193 | fprintf(stdout, "\t --help this message\n"); 194 | fprintf(stdout, "\t Address and port must in this format:\n"); 195 | fprintf(stdout, "\t\t - [IPv6Address]:Port\n"); 196 | fprintf(stdout, "\t\t - IPv4Address:Port\n"); 197 | } 198 | -------------------------------------------------------------------------------- /src/context.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/25/18. 3 | // 4 | 5 | #include "context.h" 6 | #include "listener.h" 7 | #include "signal.h" 8 | #include "pump.h" 9 | 10 | static char *transocks_client_state_str[] = { 11 | [client_NEW] = "client_NEW", 12 | [client_relay_connected] = "client_relay_connected", 13 | [client_socks5_finish_handshake] = "client_socks5_finish_handshake", 14 | [client_pumping_data] = "client_pumping_data", 15 | [client_DESTROYING] = "client_DESTROYING", 16 | [client_DESTROYED] = "client_DESTROYED" 17 | }; 18 | 19 | /* 20 | * context structure utility function strategy 21 | * only init essential member for the current layer 22 | * only free member directly created and call inner layer free function 23 | */ 24 | 25 | transocks_global_env *transocks_global_env_new(void) { 26 | struct transocks_global_env_t *env = 27 | tr_calloc(1, sizeof(struct transocks_global_env_t)); 28 | if (env == NULL) { 29 | LOGE("fail to allocate memory"); 30 | goto fail; 31 | } 32 | env->bind_addr = tr_calloc(1, sizeof(struct sockaddr_storage)); 33 | env->relay_addr = tr_calloc(1, sizeof(struct sockaddr_storage)); 34 | if (env->bind_addr == NULL || env->relay_addr == NULL) { 35 | LOGE("fail to allocate memory"); 36 | goto fail; 37 | } 38 | env->eventBaseLoop = event_base_new(); 39 | if (env->eventBaseLoop == NULL) { 40 | LOGE("fail to allocate event_base"); 41 | goto fail; 42 | } 43 | 44 | INIT_LIST_HEAD(&(env->current_clients_dlinklist)); 45 | 46 | return env; 47 | 48 | fail: 49 | TRANSOCKS_FREE(tr_free, env->bind_addr); 50 | TRANSOCKS_FREE(tr_free, env->relay_addr); 51 | TRANSOCKS_FREE(event_base_free, env->eventBaseLoop); 52 | TRANSOCKS_FREE(tr_free, env); 53 | return NULL; 54 | } 55 | 56 | void transocks_global_env_free(transocks_global_env *pEnv) { 57 | if (pEnv == NULL) return; 58 | 59 | TRANSOCKS_FREE(tr_free, pEnv->pump_method_name); 60 | TRANSOCKS_FREE(tr_free, pEnv->relay_addr); 61 | TRANSOCKS_FREE(tr_free, pEnv->bind_addr); 62 | listener_deinit(pEnv); 63 | signal_deinit(pEnv); 64 | 65 | TRANSOCKS_FREE(event_base_free, pEnv->eventBaseLoop); 66 | TRANSOCKS_FREE(tr_free, pEnv); 67 | } 68 | 69 | transocks_client *transocks_client_new(transocks_global_env *env) { 70 | transocks_client *client = tr_malloc(sizeof(transocks_client)); 71 | if (client == NULL) { 72 | LOGE("fail to allocate memory"); 73 | goto fail; 74 | } 75 | INIT_LIST_HEAD(&(client->single_client_dlinklist_entry)); 76 | client->global_env = NULL; 77 | client->client_addr = NULL; 78 | client->dest_addr = NULL; 79 | client->client_fd = -1; 80 | client->relay_fd = -1; 81 | client->client_bev = NULL; 82 | client->relay_bev = NULL; 83 | client->timeout_ev = NULL; 84 | client->user_arg = NULL; 85 | client->client_shutdown_read = false; 86 | client->client_shutdown_write = false; 87 | client->relay_shutdown_read = false; 88 | client->relay_shutdown_write = false; 89 | client->client_state = client_NEW; 90 | 91 | client->client_addr = tr_malloc(sizeof(struct sockaddr_storage)); 92 | client->dest_addr = tr_malloc(sizeof(struct sockaddr_storage)); 93 | if (client->client_addr == NULL 94 | || client->dest_addr == NULL) { 95 | LOGE("fail to allocate memory"); 96 | goto fail; 97 | } 98 | 99 | client->global_env = env; 100 | 101 | return client; 102 | 103 | fail: 104 | TRANSOCKS_FREE(tr_free, client->client_addr); 105 | TRANSOCKS_FREE(tr_free, client->dest_addr); 106 | TRANSOCKS_FREE(tr_free, client); 107 | return NULL; 108 | } 109 | 110 | void transocks_client_free(transocks_client *pClient) { 111 | if (pClient == NULL) return; 112 | 113 | transocks_pump_dump_info_debug(pClient, "free a conn"); 114 | 115 | if (pClient->client_state == client_DESTROYED || pClient->client_state == client_DESTROYING) { 116 | return; 117 | } 118 | 119 | pClient->client_state = client_DESTROYING; 120 | 121 | if (pClient->client_fd >= 0) { 122 | TRANSOCKS_SHUTDOWN(pClient->client_fd, SHUT_RDWR); 123 | } 124 | if (pClient->relay_fd >= 0) { 125 | TRANSOCKS_SHUTDOWN(pClient->relay_fd, SHUT_RDWR); 126 | } 127 | 128 | TRANSOCKS_CLOSE(pClient->client_fd); 129 | TRANSOCKS_CLOSE(pClient->relay_fd); 130 | 131 | if (pClient->timeout_ev != NULL) { 132 | evtimer_del(pClient->timeout_ev); 133 | } 134 | TRANSOCKS_FREE(event_free, pClient->timeout_ev); 135 | 136 | if (pClient->relay_bev != NULL) { 137 | bufferevent_disable(pClient->relay_bev, EV_READ | EV_WRITE); 138 | } 139 | if (pClient->client_bev != NULL) { 140 | bufferevent_disable(pClient->client_bev, EV_READ | EV_WRITE); 141 | } 142 | 143 | TRANSOCKS_FREE(bufferevent_free, pClient->relay_bev); 144 | TRANSOCKS_FREE(bufferevent_free, pClient->client_bev); 145 | 146 | 147 | TRANSOCKS_FREE(tr_free, pClient->client_addr); 148 | TRANSOCKS_FREE(tr_free, pClient->dest_addr); 149 | 150 | list_del(&(pClient->single_client_dlinklist_entry)); 151 | 152 | pClient->client_state = client_DESTROYED; 153 | 154 | TRANSOCKS_FREE(tr_free, pClient); 155 | } 156 | 157 | int transocks_client_set_timeout(transocks_client *pclient, const struct timeval *timeout, 158 | event_callback_fn ev_fn, void *arg) { 159 | int ret; 160 | if (pclient->timeout_ev == NULL) { 161 | // first time 162 | pclient->timeout_ev = evtimer_new(pclient->global_env->eventBaseLoop, ev_fn, arg); 163 | if (pclient->timeout_ev == NULL) { 164 | LOGE("mem"); 165 | return -1; 166 | } 167 | return evtimer_add(pclient->timeout_ev, timeout); 168 | } else { 169 | // already allocated, replace existing timeout 170 | evtimer_del(pclient->timeout_ev); 171 | ret = evtimer_assign(pclient->timeout_ev, pclient->global_env->eventBaseLoop, ev_fn, arg); 172 | if (ret != 0) return ret; 173 | ret = evtimer_add(pclient->timeout_ev, timeout); 174 | return ret; 175 | } 176 | } 177 | 178 | void transocks_drop_all_clients(transocks_global_env *env) { 179 | transocks_client *pclient = NULL, *tmp = NULL; 180 | 181 | list_for_each_entry_safe(pclient, tmp, &(env->current_clients_dlinklist), single_client_dlinklist_entry) { 182 | transocks_pump_dump_info(pclient, "close connection"); 183 | transocks_pump_free(pclient); 184 | } 185 | } 186 | 187 | void transocks_dump_all_client_info(transocks_global_env *env) { 188 | transocks_client *pclient = NULL; 189 | int i = 0; 190 | fprintf(stdout, "transocks-wong connection info:\n"); 191 | list_for_each_entry(pclient, &(env->current_clients_dlinklist), single_client_dlinklist_entry) { 192 | transocks_pump_dump_info(pclient, "conn #%d", i); 193 | ++i; 194 | } 195 | } 196 | 197 | void transocks_client_dump_info(transocks_client *pclient) { 198 | char srcaddrstr[TRANSOCKS_INET_ADDRPORTSTRLEN]; 199 | char destaddrstr[TRANSOCKS_INET_ADDRPORTSTRLEN]; 200 | generate_sockaddr_port_str(srcaddrstr, TRANSOCKS_INET_ADDRPORTSTRLEN, 201 | (const struct sockaddr *) (pclient->client_addr), pclient->client_addr_len); 202 | generate_sockaddr_port_str(destaddrstr, TRANSOCKS_INET_ADDRPORTSTRLEN, 203 | (const struct sockaddr *) (pclient->dest_addr), pclient->dest_addr_len); 204 | fprintf(stdout, "\n\t%s -> %s", srcaddrstr, destaddrstr); 205 | fprintf(stdout, "\n\tfd: client %d relay %d", 206 | pclient->client_fd, pclient->relay_fd); 207 | fprintf(stdout, "\n\tclient shut R %d W %d", 208 | pclient->client_shutdown_read, pclient->client_shutdown_write); 209 | fprintf(stdout, "\n\trelay shut R %d W %d", 210 | pclient->relay_shutdown_read, pclient->relay_shutdown_write); 211 | fprintf(stdout, "\n\tclient state: %s", transocks_client_state_str[pclient->client_state]); 212 | fprintf(stdout, "\n----------\n"); 213 | } -------------------------------------------------------------------------------- /cmake/TrMacros.cmake: -------------------------------------------------------------------------------- 1 | macro(tr_auto_option_changed NAME ACC VAL FIL STK) 2 | if(NOT ("${VAL}" STREQUAL "AUTO" OR "${VAL}" STREQUAL "ON" OR "${VAL}" STREQUAL "OFF")) 3 | if("${VAL}" STREQUAL "0" OR "${VAL}" STREQUAL "NO" OR "${VAL}" STREQUAL "FALSE" OR "${VAL}" STREQUAL "N") 4 | set_property(CACHE ${NAME} PROPERTY VALUE OFF) 5 | elseif("${VAL}" MATCHES "^[-+]?[0-9]+$" OR "${VAL}" STREQUAL "YES" OR "${VAL}" STREQUAL "TRUE" OR "${VAL}" STREQUAL "Y") 6 | set_property(CACHE ${NAME} PROPERTY VALUE ON) 7 | else() 8 | message(FATAL_ERROR "Option '${NAME}' set to unrecognized value '${VAL}'. Should be boolean or 'AUTO'.") 9 | endif() 10 | endif() 11 | endmacro() 12 | 13 | macro(tr_auto_option NAME DESC VAL) 14 | set(${NAME} "${VAL}" CACHE STRING "${DESC}") 15 | set_property(CACHE ${NAME} PROPERTY STRINGS "AUTO;ON;OFF") 16 | variable_watch(${NAME} tr_auto_option_changed) 17 | endmacro() 18 | 19 | macro(tr_fixup_auto_option NAME ISFOUND ISREQ) 20 | if(${ISFOUND}) 21 | set_property(CACHE ${NAME} PROPERTY VALUE ON) 22 | elseif(NOT (${ISREQ})) 23 | set_property(CACHE ${NAME} PROPERTY VALUE OFF) 24 | endif() 25 | endmacro() 26 | 27 | function(tr_list_option_changed NAME ACC VAL FIL STK) 28 | get_property(VAR_STRINGS CACHE ${NAME} PROPERTY STRINGS) 29 | string(TOUPPER "${VAL}" VAL_UPCASE) 30 | foreach(X ${VAR_STRINGS}) 31 | string(TOUPPER "${X}" X_UPCASE) 32 | if("${VAL_UPCASE}" STREQUAL "${X_UPCASE}") 33 | if(NOT "${VAL}" STREQUAL "${X}") 34 | set_property(CACHE ${NAME} PROPERTY VALUE "${X}") 35 | message(STATUS ">>> (list) ${NAME} -> ${X}") 36 | endif() 37 | return() 38 | endif() 39 | endforeach() 40 | string(REPLACE ";" "', '" VAR_STRINGS "${VAR_STRINGS}") 41 | message(FATAL_ERROR "Option '${NAME}' set to unrecognized value '${VAL}'. Should be one of '${VAR_STRINGS}'.") 42 | endfunction() 43 | 44 | macro(tr_list_option NAME DESC VAL) 45 | set(${NAME} "${VAL}" CACHE STRING "${DESC}") 46 | set_property(CACHE ${NAME} PROPERTY STRINGS "${VAL};${ARGN}") 47 | variable_watch(${NAME} tr_list_option_changed) 48 | endmacro() 49 | 50 | macro(tr_fixup_list_option NAME FVAL ISFOUND RVAL ISREQ) 51 | if(${ISFOUND}) 52 | set_property(CACHE ${NAME} PROPERTY VALUE "${FVAL}") 53 | elseif(NOT (${ISREQ})) 54 | set_property(CACHE ${NAME} PROPERTY VALUE "${RVAL}") 55 | endif() 56 | endmacro() 57 | 58 | macro(tr_get_required_flag IVAR OVAR) 59 | set(${OVAR}) 60 | if (${IVAR} AND NOT ${IVAR} STREQUAL "AUTO") 61 | set(${OVAR} REQUIRED) 62 | endif() 63 | endmacro() 64 | 65 | function(tr_make_id INPUT OVAR) 66 | string(TOUPPER "${INPUT}" ID) 67 | string(REGEX REPLACE "[^A-Z0-9]+" "_" ID "${ID}") 68 | # string(REGEX REPLACE "^_+|_+$" "" ID "${ID}") 69 | set(${OVAR} "${ID}" PARENT_SCOPE) 70 | endfunction() 71 | 72 | macro(tr_add_external_auto_library ID DIRNAME LIBNAME) 73 | if(USE_SYSTEM_${ID}) 74 | tr_get_required_flag(USE_SYSTEM_${ID} SYSTEM_${ID}_IS_REQUIRED) 75 | find_package(${ID} ${${ID}_MINIMUM} ${SYSTEM_${ID}_IS_REQUIRED}) 76 | tr_fixup_auto_option(USE_SYSTEM_${ID} ${ID}_FOUND SYSTEM_${ID}_IS_REQUIRED) 77 | endif() 78 | 79 | if(USE_SYSTEM_${ID}) 80 | unset(${ID}_UPSTREAM_TARGET) 81 | else() 82 | set(${ID}_UPSTREAM_TARGET ${LIBNAME}) 83 | set(${ID}_PREFIX "${CMAKE_BINARY_DIR}/third-party/${${ID}_UPSTREAM_TARGET}") 84 | 85 | set(${ID}_INCLUDE_DIR "${${ID}_PREFIX}/include" CACHE INTERNAL "") 86 | set(${ID}_LIBRARY "${${ID}_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${LIBNAME}${CMAKE_STATIC_LIBRARY_SUFFIX}" CACHE INTERNAL "") 87 | 88 | set(${ID}_INCLUDE_DIRS ${${ID}_INCLUDE_DIR}) 89 | set(${ID}_LIBRARIES ${${ID}_LIBRARY}) 90 | 91 | ExternalProject_Add( 92 | ${${ID}_UPSTREAM_TARGET} 93 | URL "${CMAKE_SOURCE_DIR}/third-party/${DIRNAME}" 94 | ${ARGN} 95 | PREFIX "${${ID}_PREFIX}" 96 | CMAKE_ARGS 97 | -Wno-dev # We don't want to be warned over unused variables 98 | "-DCMAKE_TOOLCHAIN_FILE:PATH=${CMAKE_TOOLCHAIN_FILE}" 99 | "-DCMAKE_USER_MAKE_RULES_OVERRIDE=${CMAKE_USER_MAKE_RULES_OVERRIDE}" 100 | "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}" 101 | "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}" 102 | "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}" 103 | "-DCMAKE_INSTALL_PREFIX:PATH=" 104 | BUILD_BYPRODUCTS "${${ID}_LIBRARY}" 105 | ) 106 | 107 | set_property(TARGET ${${ID}_UPSTREAM_TARGET} PROPERTY FOLDER "ThirdParty") 108 | endif() 109 | endmacro() 110 | 111 | function(tr_append_target_property TGT PROP VAL) 112 | get_target_property(OVAL ${TGT} ${PROP}) 113 | if(OVAL) 114 | set(VAL "${OVAL} ${VAL}") 115 | endif() 116 | set_target_properties(${TGT} PROPERTIES ${PROP} "${VAL}") 117 | endfunction() 118 | 119 | function(tr_win32_app_info OVAR DESCR INTNAME ORIGFNAME) 120 | if(NOT WIN32) 121 | return() 122 | endif() 123 | 124 | set(TR_FILE_DESCRIPTION "${DESCR}") 125 | set(TR_INTERNAL_NAME "${INTNAME}") 126 | set(TR_ORIGINAL_FILENAME "${ORIGFNAME}") 127 | if(ARGN) 128 | set(TR_MAIN_ICON "${ARGN}") 129 | endif() 130 | 131 | configure_file("${CMAKE_SOURCE_DIR}/cmake/Transmission.rc.in" "${INTNAME}-app-info.rc") 132 | 133 | set(${OVAR} "${CMAKE_CURRENT_BINARY_DIR}/${INTNAME}-app-info.rc" PARENT_SCOPE) 134 | endfunction() 135 | 136 | function(tr_select_library LIBNAMES FUNCNAME DIRS OVAR) 137 | set(LIBNAME) 138 | foreach(X ${LIBNAMES}) 139 | set(VAR_NAME "HAVE_${FUNCNAME}_IN_LIB${X}") 140 | string(TOUPPER "${VAR_NAME}" VAR_NAME) 141 | check_library_exists("${X}" "${FUNCNAME}" "${DIRS}" ${VAR_NAME}) 142 | if(${VAR_NAME}) 143 | set(LIBNAME "${X}") 144 | break() 145 | endif() 146 | endforeach() 147 | set(${OVAR} "${LIBNAME}" PARENT_SCOPE) 148 | endfunction() 149 | 150 | function(tr_fixup_bundle_item BUNDLE_DIR BUNDLE_ITEMS DEP_DIRS) 151 | while(BUNDLE_ITEMS) 152 | list(GET BUNDLE_ITEMS 0 ITEM) 153 | list(REMOVE_AT BUNDLE_ITEMS 0) 154 | 155 | set(ITEM_FULL_BUNDLE_PATH "${BUNDLE_DIR}/${ITEM}") 156 | get_filename_component(ITEM_FULL_BUNDLE_DIR "${ITEM_FULL_BUNDLE_PATH}" PATH) 157 | 158 | unset(ITEM_DEPS) 159 | get_prerequisites("${ITEM_FULL_BUNDLE_PATH}" ITEM_DEPS 1 0 "${ITEM_FULL_BUNDLE_PATH}" "${DEP_DIRS}") 160 | 161 | foreach(DEP IN LISTS ITEM_DEPS) 162 | gp_resolve_item("${ITEM_FULL_BUNDLE_PATH}" "${DEP}" "${ITEM_FULL_BUNDLE_DIR}" "${DEP_DIRS}" DEP_FULL_PATH) 163 | 164 | if(DEP_FULL_PATH MATCHES "[.]dylib$") 165 | get_filename_component(DEP_NAME "${DEP_FULL_PATH}" NAME) 166 | file(COPY "${DEP_FULL_PATH}" DESTINATION "${BUNDLE_DIR}/Contents/MacOS/") 167 | set(DEP_BUNDLE_PATH "Contents/MacOS/${DEP_NAME}") 168 | elseif(DEP_FULL_PATH MATCHES "^(.+)/(([^/]+[.]framework)/.+)$") 169 | set(DEP_NAME "${CMAKE_MATCH_2}") 170 | file(COPY "${CMAKE_MATCH_1}/${CMAKE_MATCH_3}" DESTINATION "${BUNDLE_DIR}/Contents/Frameworks/" PATTERN "Headers" EXCLUDE) 171 | set(DEP_BUNDLE_PATH "Contents/Frameworks/${DEP_NAME}") 172 | else() 173 | message(FATAL_ERROR "Don't know how to fixup '${DEP_FULL_PATH}'") 174 | endif() 175 | 176 | execute_process(COMMAND install_name_tool -change "${DEP}" "@rpath/${DEP_NAME}" "${ITEM_FULL_BUNDLE_PATH}") 177 | 178 | set(DEP_FULL_BUNDLE_PATH "${BUNDLE_DIR}/${DEP_BUNDLE_PATH}") 179 | execute_process(COMMAND chmod u+w "${DEP_FULL_BUNDLE_PATH}") 180 | execute_process(COMMAND install_name_tool -id "@rpath/${DEP_NAME}" "${DEP_FULL_BUNDLE_PATH}") 181 | 182 | list(REMOVE_ITEM BUNDLE_ITEMS "${DEP_BUNDLE_PATH}") 183 | list(APPEND BUNDLE_ITEMS "${DEP_BUNDLE_PATH}") 184 | endforeach() 185 | endwhile() 186 | endfunction() -------------------------------------------------------------------------------- /src/bufferpump.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/30/18. 3 | // 4 | 5 | #include "bufferpump.h" 6 | 7 | /* 8 | * read EOF: eventcb & (BEV_EVENT_READING | BEV_EVENT_EOF) 9 | * write EOF: eventcb & (BEV_EVENT_ERROR | BEV_EVENT_WRITING) && errno == ECONNRESET 10 | * strategy: alway enable EV_READ|EV_WRITE, check invariant at the beginning, if error occured 11 | * check shutdown_how and determine what to do next, retry or destroy 12 | */ 13 | 14 | transocks_pump transocks_bufferpump_ops; 15 | 16 | static void transocks_client_readcb(struct bufferevent *bev, void *userArg); 17 | 18 | static void transocks_client_writecb(struct bufferevent *bev, void *userArg); 19 | 20 | static void transocks_relay_readcb(struct bufferevent *bev, void *userArg); 21 | 22 | static void transocks_relay_writecb(struct bufferevent *bev, void *userArg); 23 | 24 | static void transocks_client_eventcb(struct bufferevent *bev, short bevs, void *userArg); 25 | 26 | static void transocks_relay_eventcb(struct bufferevent *bev, short bevs, void *userArg); 27 | 28 | static void transocks_bufferpump_free(transocks_client *pclient); 29 | 30 | static void transocks_bufferpump_dump_info(transocks_client *pclient); 31 | 32 | static void transocks_bufferpump_dump_info(transocks_client *pclient) { 33 | transocks_client_dump_info(pclient); 34 | } 35 | 36 | static void transocks_bufferpump_free(transocks_client *pclient) { 37 | bufferevent_disable(pclient->client_bev, EV_READ | EV_WRITE); 38 | bufferevent_disable(pclient->relay_bev, EV_READ | EV_WRITE); 39 | TRANSOCKS_FREE(transocks_client_free, pclient); 40 | } 41 | 42 | TRANSOCKS_ALWAYS_INLINE 43 | static bool transocks_check_close(transocks_client *pclient) { 44 | return pclient->client_shutdown_read 45 | && pclient->client_shutdown_write 46 | && pclient->relay_shutdown_read 47 | && pclient->relay_shutdown_write; 48 | } 49 | 50 | static void transocks_client_readcb(struct bufferevent *bev, void *userArg) { 51 | TRANSOCKS_UNUSED(bev); 52 | transocks_client *pclient = (transocks_client *) userArg; 53 | struct evbuffer *clientinbuf = bufferevent_get_input(pclient->client_bev); 54 | struct evbuffer *relayoutbuf = bufferevent_get_output(pclient->relay_bev); 55 | size_t srclen = evbuffer_get_length(clientinbuf); 56 | evbuffer_remove_buffer(clientinbuf, relayoutbuf, srclen); 57 | } 58 | 59 | static void transocks_relay_writecb(struct bufferevent *bev, void *userArg) { 60 | TRANSOCKS_UNUSED(bev); 61 | transocks_client *pclient = (transocks_client *) userArg; 62 | // check client close 63 | if (0 == evbuffer_get_length(bufferevent_get_output(pclient->relay_bev)) 64 | && pclient->client_shutdown_read) { 65 | pclient->relay_shutdown_write = true; 66 | if (TRANSOCKS_SHUTDOWN(pclient->relay_fd, SHUT_WR) < 0) { 67 | LOGE_ERRNO("relay shutdown write err"); 68 | } 69 | } 70 | if (transocks_check_close(pclient)) { 71 | LOGD("passed close check"); 72 | TRANSOCKS_FREE(transocks_bufferpump_free, pclient); 73 | return; 74 | } 75 | 76 | } 77 | 78 | static void transocks_relay_readcb(struct bufferevent *bev, void *userArg) { 79 | TRANSOCKS_UNUSED(bev); 80 | transocks_client *pclient = (transocks_client *) userArg; 81 | struct evbuffer *relayinbuf = bufferevent_get_input(pclient->relay_bev); 82 | struct evbuffer *clientoutbuf = bufferevent_get_output(pclient->client_bev); 83 | size_t srclen = evbuffer_get_length(relayinbuf); 84 | evbuffer_remove_buffer(relayinbuf, clientoutbuf, srclen); 85 | } 86 | 87 | static void transocks_client_writecb(struct bufferevent *bev, void *userArg) { 88 | TRANSOCKS_UNUSED(bev); 89 | transocks_client *pclient = (transocks_client *) userArg; 90 | // check relay close 91 | if (0 == evbuffer_get_length(bufferevent_get_output(pclient->client_bev)) 92 | && pclient->relay_shutdown_read) { 93 | pclient->client_shutdown_write = true; 94 | if (TRANSOCKS_SHUTDOWN(pclient->client_fd, SHUT_WR) < 0) { 95 | LOGE_ERRNO("client shutdown write err"); 96 | } 97 | } 98 | if (transocks_check_close(pclient)) { 99 | LOGD("passed close check"); 100 | TRANSOCKS_FREE(transocks_bufferpump_free, pclient); 101 | return; 102 | } 103 | } 104 | 105 | 106 | static void transocks_client_eventcb(struct bufferevent *bev, short bevs, void *userArg) { 107 | TRANSOCKS_UNUSED(bev); 108 | transocks_client *pclient = (transocks_client *) userArg; 109 | if (TRANSOCKS_CHKBIT(bevs, BEV_EVENT_READING) 110 | && TRANSOCKS_CHKBIT(bevs, BEV_EVENT_EOF)) { 111 | // read eof 112 | pclient->client_shutdown_read = true; 113 | bufferevent_disable(pclient->client_bev, EV_READ); 114 | if (TRANSOCKS_SHUTDOWN(pclient->client_fd, SHUT_RD) < 0) { 115 | LOGE_ERRNO("client shutdown read err"); 116 | } 117 | // check if ouput cache is empty 118 | if (0 == evbuffer_get_length(bufferevent_get_output(pclient->relay_bev)) 119 | && pclient->client_shutdown_read) { 120 | pclient->relay_shutdown_write = true; 121 | if (TRANSOCKS_SHUTDOWN(pclient->relay_fd, SHUT_WR) < 0) { 122 | LOGE_ERRNO("relay shutdown write err"); 123 | } 124 | } 125 | goto checkClose; 126 | } 127 | if (TRANSOCKS_CHKBIT(bevs, BEV_EVENT_WRITING) 128 | && ((TRANSOCKS_CHKBIT(bevs, BEV_EVENT_ERROR) && errno == ECONNRESET) 129 | || TRANSOCKS_CHKBIT(bevs, BEV_EVENT_EOF))) { 130 | // write eof 131 | pclient->client_shutdown_write = true; 132 | bufferevent_disable(pclient->client_bev, EV_WRITE); 133 | if (TRANSOCKS_SHUTDOWN(pclient->client_fd, SHUT_WR) < 0) { 134 | LOGE_ERRNO("client shutdown write err"); 135 | } 136 | goto checkClose; 137 | } 138 | if (TRANSOCKS_CHKBIT(bevs, BEV_EVENT_ERROR)) { 139 | int err = EVUTIL_SOCKET_ERROR(); 140 | LOGE("client error %d (%s)", err, evutil_socket_error_to_string(err)); 141 | TRANSOCKS_FREE(transocks_bufferpump_free, pclient); 142 | return; 143 | } 144 | checkClose: 145 | if (transocks_check_close(pclient)) { 146 | LOGD("passed close check"); 147 | TRANSOCKS_FREE(transocks_bufferpump_free, pclient); 148 | return; 149 | } 150 | } 151 | 152 | static void transocks_relay_eventcb(struct bufferevent *bev, short bevs, void *userArg) { 153 | TRANSOCKS_UNUSED(bev); 154 | transocks_client *pclient = (transocks_client *) userArg; 155 | if (TRANSOCKS_CHKBIT(bevs, BEV_EVENT_READING) 156 | && TRANSOCKS_CHKBIT(bevs, BEV_EVENT_EOF)) { 157 | // read eof 158 | pclient->relay_shutdown_read = true; 159 | bufferevent_disable(pclient->relay_bev, EV_READ); 160 | if (TRANSOCKS_SHUTDOWN(pclient->relay_fd, SHUT_RD) < 0) { 161 | LOGE_ERRNO("relay shutdown read err"); 162 | } 163 | // check if ouput cache is empty 164 | if (0 == evbuffer_get_length(bufferevent_get_output(pclient->client_bev)) 165 | && pclient->relay_shutdown_read) { 166 | pclient->client_shutdown_write = true; 167 | if (TRANSOCKS_SHUTDOWN(pclient->client_fd, SHUT_WR) < 0) { 168 | LOGE_ERRNO("client shutdown write err"); 169 | } 170 | } 171 | goto checkClose; 172 | } 173 | if (TRANSOCKS_CHKBIT(bevs, BEV_EVENT_WRITING) 174 | && ((TRANSOCKS_CHKBIT(bevs, BEV_EVENT_ERROR) && errno == ECONNRESET) 175 | || TRANSOCKS_CHKBIT(bevs, BEV_EVENT_EOF))) { 176 | // write eof 177 | pclient->relay_shutdown_write = true; 178 | bufferevent_disable(pclient->relay_bev, EV_WRITE); 179 | if (TRANSOCKS_SHUTDOWN(pclient->relay_fd, SHUT_WR) < 0) { 180 | LOGE_ERRNO("relay shutdown write err"); 181 | } 182 | goto checkClose; 183 | } 184 | if (TRANSOCKS_CHKBIT(bevs, BEV_EVENT_ERROR)) { 185 | int err = EVUTIL_SOCKET_ERROR(); 186 | LOGE("relay error %d (%s)", err, evutil_socket_error_to_string(err)); 187 | TRANSOCKS_FREE(transocks_bufferpump_free, pclient); 188 | return; 189 | } 190 | checkClose: 191 | if (transocks_check_close(pclient)) { 192 | LOGD("passed close check"); 193 | TRANSOCKS_FREE(transocks_bufferpump_free, pclient); 194 | return; 195 | } 196 | } 197 | 198 | 199 | static int transocks_bufferpump_start_pump(transocks_client *pclient) { 200 | int err; 201 | struct bufferevent *client_bev = pclient->client_bev; 202 | struct bufferevent *relay_bev = pclient->relay_bev; 203 | bufferevent_setwatermark(client_bev, EV_READ | EV_WRITE, 0, TRANSOCKS_BUFSIZE); 204 | bufferevent_setwatermark(relay_bev, EV_READ | EV_WRITE, 0, TRANSOCKS_BUFSIZE); 205 | bufferevent_setcb(client_bev, transocks_client_readcb, transocks_client_writecb, 206 | transocks_client_eventcb, pclient); 207 | bufferevent_setcb(relay_bev, transocks_relay_readcb, transocks_relay_writecb, 208 | transocks_relay_eventcb, pclient); 209 | 210 | err = bufferevent_enable(client_bev, EV_READ | EV_WRITE); 211 | if (err) { 212 | LOGE("client bufferevent_enable"); 213 | return -1; 214 | } 215 | err = bufferevent_enable(relay_bev, EV_READ | EV_WRITE); 216 | if (err) { 217 | LOGE("relay bufferevent_enable"); 218 | return -1; 219 | } 220 | 221 | return 0; 222 | } 223 | 224 | transocks_pump transocks_bufferpump_ops = { 225 | .name = PUMPMETHOD_BUFFER, 226 | .start_pump_fn = transocks_bufferpump_start_pump, 227 | .free_pump_fn = transocks_bufferpump_free, 228 | .dump_info_fn = transocks_bufferpump_dump_info 229 | }; -------------------------------------------------------------------------------- /src/socks5.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/28/18. 3 | // 4 | 5 | #include "socks5.h" 6 | #include "pump.h" 7 | 8 | static void socks5_timeout_cb(evutil_socket_t, short, void *); 9 | 10 | static void socks_handshake_stage_errcb(struct bufferevent *, short, void *); 11 | 12 | static void relay_onconnect_eventcb(struct bufferevent *, short, void *); 13 | 14 | static void socks_on_server_connect_reply_readcb(struct bufferevent *, void *); 15 | 16 | static void socks_send_connect_request(transocks_client *); 17 | 18 | static void socks_on_server_selected_method_readcb(struct bufferevent *, void *); 19 | 20 | static void socks_send_method_selection(transocks_client *); 21 | 22 | 23 | static struct timeval socks5_timeout_tv = { 24 | .tv_sec = 60, 25 | .tv_usec = 0 26 | }; 27 | 28 | /* 29 | * We assume socks5 server are listening on the loopback interface 30 | * thus treats any EOF as error 31 | */ 32 | static void socks_handshake_stage_errcb(struct bufferevent *bev, short bevs, void *userArg) { 33 | TRANSOCKS_UNUSED(bev); 34 | transocks_client *pclient = (transocks_client *) userArg; 35 | evtimer_del(pclient->timeout_ev); 36 | if (TRANSOCKS_CHKBIT(bevs, BEV_EVENT_EOF) 37 | || TRANSOCKS_CHKBIT(bevs, BEV_EVENT_ERROR)) { 38 | int err = EVUTIL_SOCKET_ERROR(); 39 | LOGE("socks error %d (%s)", err, evutil_socket_error_to_string(err)); 40 | TRANSOCKS_FREE(transocks_client_free, pclient); 41 | } 42 | } 43 | 44 | static void socks_on_server_connect_reply_readcb(struct bufferevent *bev, void *userArg) { 45 | TRANSOCKS_UNUSED(bev); 46 | LOGD("enter"); 47 | transocks_client *pClient = (transocks_client *) userArg; 48 | evtimer_del(pClient->timeout_ev); 49 | struct bufferevent *relay_bev = pClient->relay_bev; 50 | struct evbuffer *input = bufferevent_get_input(relay_bev); 51 | size_t input_read_size = evbuffer_get_length(input); 52 | if (input_read_size < 10) { 53 | LOGE("no enough data"); 54 | goto freeClient; 55 | } 56 | unsigned char *mem = evbuffer_pullup(input, input_read_size); 57 | struct socks_response_header *preshead = (struct socks_response_header *) mem; 58 | if (preshead->ver != SOCKS5_VERSION) { 59 | LOGE("not a socks5 server"); 60 | goto freeClient; 61 | } 62 | if (preshead->rep != SOCKS5_REP_SUCCEEDED) { 63 | LOGE("socks connect rep %x", preshead->rep); 64 | goto freeClient; 65 | } 66 | 67 | switch (preshead->atyp) { 68 | case SOCKS5_ATYP_IPV4: 69 | evbuffer_drain(input, 10); 70 | break; 71 | case SOCKS5_ATYP_IPV6: 72 | evbuffer_drain(input, 22); 73 | break; 74 | default: 75 | LOGE("atyp domain should not happen"); 76 | goto freeClient; 77 | } 78 | pClient->client_state = client_socks5_finish_handshake; 79 | LOGD("before pump, %d", (int) evbuffer_get_length(input)); 80 | // disable bufferevent until pump enable it again 81 | bufferevent_disable(relay_bev, EV_READ | EV_WRITE); 82 | 83 | if (transocks_start_pump(pClient) != 0) 84 | goto freeClient; 85 | 86 | 87 | return; 88 | 89 | freeClient: 90 | TRANSOCKS_FREE(transocks_client_free, pClient); 91 | } 92 | 93 | static void socks_send_connect_request(transocks_client *pclient) { 94 | LOGD("enter"); 95 | struct bufferevent *relay_bev = pclient->relay_bev; 96 | struct socks_request_ipv4 *req_ip4 = NULL; 97 | struct socks_request_ipv6 *req_ip6 = NULL; 98 | struct sockaddr_in *sa_ip4 = NULL; 99 | struct sockaddr_in6 *sa_ip6 = NULL; 100 | if (pclient->dest_addr->ss_family == AF_INET) { 101 | req_ip4 = tr_malloc(sizeof(struct socks_request_ipv4)); 102 | if (req_ip4 == NULL) goto freeClient; 103 | sa_ip4 = (struct sockaddr_in *) (pclient->dest_addr); 104 | req_ip4->ver = SOCKS5_VERSION; 105 | req_ip4->cmd = SOCKS5_CMD_CONNECT; 106 | req_ip4->rsv = 0x00; 107 | req_ip4->atyp = SOCKS5_ATYP_IPV4; 108 | req_ip4->port = sa_ip4->sin_port; 109 | assert(sizeof(struct socks_request_ipv4) == 6 + 4); 110 | memcpy(&(req_ip4->addr), &(sa_ip4->sin_addr), sizeof(struct in_addr)); 111 | dump_data("socks_send_connect_request_v4", (char *) req_ip4, sizeof(struct socks_request_ipv4)); 112 | bufferevent_write(relay_bev, (const void *) req_ip4, sizeof(struct socks_request_ipv4)); 113 | TRANSOCKS_FREE(tr_free, req_ip4); 114 | } else if (pclient->dest_addr->ss_family == AF_INET6) { 115 | req_ip6 = tr_malloc(sizeof(struct socks_request_ipv6)); 116 | if (req_ip6 == NULL) goto freeClient; 117 | sa_ip6 = (struct sockaddr_in6 *) (pclient->dest_addr); 118 | req_ip6->ver = SOCKS5_VERSION; 119 | req_ip6->cmd = SOCKS5_CMD_CONNECT; 120 | req_ip6->rsv = 0x00; 121 | req_ip6->atyp = SOCKS5_ATYP_IPV6; 122 | req_ip6->port = sa_ip6->sin6_port; 123 | assert(sizeof(struct socks_request_ipv6) == 6 + 16); 124 | memcpy(&(req_ip6->addr), &(sa_ip6->sin6_addr), sizeof(struct in6_addr)); 125 | dump_data("socks_send_connect_request_v6", (char *) req_ip6, sizeof(struct socks_request_ipv6)); 126 | bufferevent_write(relay_bev, (const void *) req_ip6, sizeof(struct socks_request_ipv6)); 127 | TRANSOCKS_FREE(tr_free, req_ip6); 128 | } else { 129 | LOGE("unknown ss_family"); 130 | goto freeClient; 131 | } 132 | 133 | // VER + REP + RSV + ATYP + 4-byte ipv4 ADDR + 2-byte port 134 | // (should not reply with domain, iptables has ip address only) 135 | bufferevent_setwatermark(relay_bev, EV_READ, 10, TRANSOCKS_BUFSIZE); 136 | bufferevent_setcb(relay_bev, socks_on_server_connect_reply_readcb, NULL, socks_handshake_stage_errcb, pclient); 137 | bufferevent_enable(relay_bev, EV_READ); 138 | 139 | transocks_client_set_timeout(pclient, &socks5_timeout_tv, socks5_timeout_cb, pclient); 140 | 141 | return; 142 | 143 | freeClient: 144 | TRANSOCKS_FREE(transocks_client_free, pclient); 145 | } 146 | 147 | static void socks_on_server_selected_method_readcb(struct bufferevent *bev, void *userArg) { 148 | LOGD("enter"); 149 | transocks_client *pclient = (transocks_client *) userArg; 150 | evtimer_del(pclient->timeout_ev); 151 | struct evbuffer *input = bufferevent_get_input(bev); 152 | size_t input_read_size = evbuffer_get_length(input); 153 | if (input_read_size < 2) { 154 | LOGE("not enough data"); 155 | goto freeClient; 156 | } 157 | unsigned char *mem = evbuffer_pullup(input, input_read_size); 158 | struct socks_method_select_response *pres = (struct socks_method_select_response *) mem; 159 | if (pres->ver != SOCKS5_VERSION) { 160 | LOGE("not a socks5 server"); 161 | goto freeClient; 162 | } 163 | if (pres->method != SOCKS5_METHOD_NOAUTH) { 164 | LOGE("unsupported socks5 auth method %d", pres->method); 165 | goto freeClient; 166 | } 167 | if (evbuffer_drain(input, 2) != 0) { 168 | LOGE("fail to drain 2bytes server_selected_method"); 169 | goto freeClient; 170 | } 171 | if (evbuffer_get_length(bufferevent_get_input(bev)) != 0) { 172 | LOGE("still has data in the relay bev input buffer"); 173 | goto freeClient; 174 | } 175 | socks_send_connect_request(pclient); 176 | return; 177 | 178 | freeClient: 179 | TRANSOCKS_FREE(transocks_client_free, pclient); 180 | } 181 | 182 | static void socks_send_method_selection(transocks_client *pclient) { 183 | LOGD("enter"); 184 | //wait for 2 bytes socks server selected method 185 | struct bufferevent *relay_bev = pclient->relay_bev; 186 | struct socks_method_select_request req; 187 | req.ver = SOCKS5_VERSION; 188 | req.nmethods = 0x01; 189 | req.methods[0] = SOCKS5_METHOD_NOAUTH; 190 | bufferevent_setwatermark(relay_bev, EV_READ, 2, TRANSOCKS_BUFSIZE); 191 | bufferevent_write(relay_bev, (const void *) &req, sizeof(struct socks_method_select_request)); 192 | bufferevent_setcb(relay_bev, socks_on_server_selected_method_readcb, NULL, socks_handshake_stage_errcb, pclient); 193 | bufferevent_enable(relay_bev, EV_READ); 194 | 195 | transocks_client_set_timeout(pclient, &socks5_timeout_tv, socks5_timeout_cb, pclient); 196 | } 197 | 198 | static void relay_onconnect_eventcb(struct bufferevent *bev, short bevs, void *userArg) { 199 | TRANSOCKS_UNUSED(bev); 200 | transocks_client *pclient = (transocks_client *) userArg; 201 | evtimer_del(pclient->timeout_ev); 202 | if (TRANSOCKS_CHKBIT(bevs, BEV_EVENT_CONNECTED)) { 203 | /* We're connected */ 204 | pclient->client_state = client_relay_connected; 205 | socks_send_method_selection(pclient); 206 | return; 207 | } 208 | 209 | if (TRANSOCKS_CHKBIT(bevs, BEV_EVENT_ERROR)) { 210 | /* An error occured while connecting. */ 211 | int err = EVUTIL_SOCKET_ERROR(); 212 | LOGE("connect relay %d (%s)", err, evutil_socket_error_to_string(err)); 213 | TRANSOCKS_FREE(transocks_client_free, pclient); 214 | } 215 | } 216 | 217 | static void socks5_timeout_cb(evutil_socket_t fd, short events, void *userArg) { 218 | TRANSOCKS_UNUSED(fd); 219 | TRANSOCKS_UNUSED(events); 220 | transocks_client *pclient = (transocks_client *) userArg; 221 | LOGE("timed out"); 222 | TRANSOCKS_FREE(transocks_client_free, pclient); 223 | } 224 | 225 | void transocks_start_connect_relay(transocks_client *pClient) { 226 | LOGD("enter"); 227 | transocks_global_env *env = pClient->global_env; 228 | int relay_fd = socket(env->relay_addr->ss_family, SOCK_STREAM, 0); 229 | if (relay_fd < 0) { 230 | LOGE_ERRNO("fail to create socket"); 231 | goto freeClient; 232 | } 233 | if (setnonblocking(relay_fd, true) != 0) { 234 | LOGE("fail to set non-blocking"); 235 | goto closeFd; 236 | } 237 | if (apply_tcp_keepalive(relay_fd) != 0) { 238 | LOGE("fail to set TCP keepalive"); 239 | goto closeFd; 240 | } 241 | if (apply_tcp_nodelay(relay_fd) != 0) { 242 | LOGE("fail to set TCP nodelay"); 243 | goto closeFd; 244 | } 245 | 246 | pClient->relay_fd = relay_fd; 247 | 248 | // create relay bev 249 | struct bufferevent *relay_bev = bufferevent_socket_new(env->eventBaseLoop, 250 | relay_fd, BEV_OPT_CLOSE_ON_FREE); 251 | 252 | if (relay_bev == NULL) { 253 | LOGE("bufferevent_socket_new"); 254 | goto closeFd; 255 | } 256 | pClient->relay_bev = relay_bev; 257 | 258 | bufferevent_setcb(pClient->relay_bev, NULL, NULL, relay_onconnect_eventcb, pClient); 259 | bufferevent_enable(pClient->relay_bev, EV_WRITE); 260 | 261 | transocks_client_set_timeout(pClient, &socks5_timeout_tv, socks5_timeout_cb, pClient); 262 | if (bufferevent_socket_connect(pClient->relay_bev, 263 | (const struct sockaddr *) (env->relay_addr), env->relay_addr_len) != 0) { 264 | LOGE("fail to connect to relay"); 265 | goto freeBev; 266 | } 267 | 268 | 269 | return; 270 | 271 | freeBev: 272 | TRANSOCKS_FREE(bufferevent_free, relay_bev); 273 | 274 | closeFd: 275 | TRANSOCKS_CLOSE(relay_fd); 276 | 277 | freeClient: 278 | TRANSOCKS_FREE(transocks_client_free, pClient); 279 | } 280 | -------------------------------------------------------------------------------- /src/splicepump.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wong on 10/30/18. 3 | // 4 | 5 | #include "splicepump.h" 6 | 7 | /* 8 | * source socket => pipe write end => pipe read end => target socket 9 | * read socket EOF write pipe EOF read pipe EOF write socket EOF 10 | */ 11 | 12 | /* 13 | * CONDITION: data_size_in_pipe and socket_can_keep_read_or_write 14 | * read socket EOF: pipe not full && splice_from_socket() == 0 15 | * write pipe EOF: read socket EOF && pipe not full 16 | * if pipe is full, let other side read 17 | * read pipe EOF: splice_from_pipe() == 0 {aka pipe write end closed} 18 | * write socket EOF: read pipe EOF && pipe is empty 19 | * if pipe is not empty, self activate until all data been written to target socket 20 | * strategy: always enable EV_READ, let read callbacks control write callbacks, if error occured 21 | * check shutdown_how and determine what to do next, handle pipe or handle socket 22 | */ 23 | 24 | #define TRANSOCKS_SPLICE_SHOULD_LOG_ERR(err) ((err) != EBADF && (err) != ENOTCONN) 25 | 26 | transocks_pump transocks_splicepump_ops; 27 | 28 | static transocks_splicepump *transocks_splicepump_new(transocks_client *pclient); 29 | 30 | static transocks_splicepump *transocks_get_splicepump(transocks_client *pclient); 31 | 32 | static void transocks_write_to_pipe(transocks_client *pclient, transocks_splicepump_data_direction direction); 33 | 34 | static void transocks_read_from_pipe(transocks_client *pclient, transocks_splicepump_data_direction direction); 35 | 36 | static void transocks_on_fatal_failure(transocks_client *pclient); 37 | 38 | static void 39 | transocks_check_normal_termination(transocks_client *pclient, transocks_splicepump_data_direction direction); 40 | 41 | static transocks_splicepump_splice_result transocks_single_splice(int from_fd, int to_fd, 42 | size_t want_len, 43 | ssize_t *transfered_bytes); 44 | 45 | static int getpipesize(int fd); 46 | 47 | static void transocks_splicepump_dump_info(transocks_client *pclient); 48 | 49 | static void transocks_splicepump_free(transocks_client *pclient); 50 | 51 | static void transocks_splicepump_client_readcb(evutil_socket_t fd, short events, void *arg); 52 | 53 | static void transocks_splicepump_relay_writecb(evutil_socket_t fd, short events, void *arg); 54 | 55 | static void transocks_splicepump_relay_readcb(evutil_socket_t fd, short events, void *arg); 56 | 57 | static void transocks_splicepump_client_writecb(evutil_socket_t fd, short events, void *arg); 58 | 59 | 60 | static int getpipesize(int fd) { 61 | if (fd < 0) return -1; 62 | int ret = fcntl(fd, F_GETPIPE_SZ); 63 | if (ret == -1) { 64 | LOGE_ERRNO("fcntl F_GETPIPE_SZ"); 65 | return -1; 66 | } 67 | return ret; 68 | } 69 | 70 | static void transocks_on_fatal_failure(transocks_client *pclient) { 71 | LOGD("fatal_failure transocks_splicepump_free"); 72 | TRANSOCKS_FREE(transocks_splicepump_free, pclient); 73 | } 74 | 75 | static void 76 | transocks_check_normal_termination(transocks_client *pclient, transocks_splicepump_data_direction direction) { 77 | bool flag = false; 78 | if (TRANSOCKS_IS_INVALID_FD(pclient->client_fd) || TRANSOCKS_IS_INVALID_FD(pclient->relay_fd)) { 79 | // not half close, we can do nothing 80 | flag = true; 81 | } else { 82 | // valid fd 83 | if (direction == inbound) { 84 | if (pclient->relay_shutdown_read && pclient->client_shutdown_write) { 85 | flag = true; 86 | } 87 | } 88 | if (direction == outbound) { 89 | if (pclient->client_shutdown_read && pclient->relay_shutdown_write) { 90 | flag = true; 91 | } 92 | } 93 | } 94 | 95 | if (flag) { 96 | LOGD("normal transocks_splicepump_free"); 97 | TRANSOCKS_FREE(transocks_splicepump_free, pclient); 98 | return; 99 | } 100 | } 101 | 102 | static void transocks_splicepump_client_readcb(evutil_socket_t fd, short events, void *arg) { 103 | TRANSOCKS_UNUSED(fd); 104 | TRANSOCKS_UNUSED(events); 105 | transocks_client *pclient = (transocks_client *) arg; 106 | transocks_write_to_pipe(pclient, outbound); 107 | } 108 | 109 | static void transocks_splicepump_relay_writecb(evutil_socket_t fd, short events, void *arg) { 110 | TRANSOCKS_UNUSED(fd); 111 | TRANSOCKS_UNUSED(events); 112 | transocks_client *pclient = (transocks_client *) arg; 113 | transocks_splicepump *ppump = transocks_get_splicepump(pclient); 114 | transocks_read_from_pipe(pclient, outbound); 115 | } 116 | 117 | static void transocks_splicepump_relay_readcb(evutil_socket_t fd, short events, void *arg) { 118 | TRANSOCKS_UNUSED(fd); 119 | TRANSOCKS_UNUSED(events); 120 | transocks_client *pclient = (transocks_client *) arg; 121 | transocks_write_to_pipe(pclient, inbound); 122 | } 123 | 124 | static void transocks_splicepump_client_writecb(evutil_socket_t fd, short events, void *arg) { 125 | TRANSOCKS_UNUSED(fd); 126 | TRANSOCKS_UNUSED(events); 127 | transocks_client *pclient = (transocks_client *) arg; 128 | transocks_read_from_pipe(pclient, inbound); 129 | } 130 | 131 | static transocks_splicepump_splice_result transocks_single_splice(int from_fd, int to_fd, 132 | size_t want_len, 133 | ssize_t *transfered_bytes) { 134 | ssize_t n = splice(from_fd, NULL, 135 | to_fd, NULL, 136 | want_len, 137 | SPLICE_F_MOVE | SPLICE_F_NONBLOCK); 138 | if (n == -1) { 139 | if (TRANSOCKS_IS_RETRIABLE(errno)) { 140 | return can_retry; 141 | } else { 142 | if (TRANSOCKS_SPLICE_SHOULD_LOG_ERR(errno)) { 143 | LOGE_ERRNO("splice from: %d, to: %d, want_len: %zu, ret %zd", from_fd, to_fd, want_len, n); 144 | } 145 | return fatal_failure; 146 | } 147 | } else if (n == 0) { 148 | return read_eof; 149 | } else { 150 | // data transfer 151 | *transfered_bytes = n; 152 | return normal_transfer; 153 | } 154 | } 155 | 156 | static void transocks_write_to_pipe(transocks_client *pclient, transocks_splicepump_data_direction direction) { 157 | transocks_splicepump *ppump = transocks_get_splicepump(pclient); 158 | transocks_splicepump_splice_result splice_ret; 159 | ssize_t transfer_bytes; 160 | int from_fd = -1, to_fd = -1; 161 | int ultimate_target_fd = -1; 162 | size_t want_len; 163 | transocks_splicepipe *pipe = NULL; 164 | bool *read_eof_flag; 165 | struct event *this_libevent_ev = NULL; 166 | struct event *ultimate_target_libevent_ev = NULL; 167 | 168 | if (direction == inbound) { 169 | from_fd = pclient->relay_fd; 170 | ultimate_target_fd = pclient->client_fd; 171 | pipe = ppump->inbound_pipe; 172 | read_eof_flag = &(pclient->relay_shutdown_read); 173 | this_libevent_ev = ppump->relay_read_ev; 174 | ultimate_target_libevent_ev = ppump->client_write_ev; 175 | } 176 | if (direction == outbound) { 177 | from_fd = pclient->client_fd; 178 | ultimate_target_fd = pclient->relay_fd; 179 | pipe = ppump->outbound_pipe; 180 | read_eof_flag = &(pclient->client_shutdown_read); 181 | this_libevent_ev = ppump->client_read_ev; 182 | ultimate_target_libevent_ev = ppump->relay_write_ev; 183 | } 184 | to_fd = pipe->pipe_writefd; 185 | want_len = pipe->capacity; 186 | if (TRANSOCKS_IS_INVALID_FD(from_fd) || TRANSOCKS_IS_INVALID_FD(to_fd)) { 187 | LOGD("fatal failure: invalid fd"); 188 | transocks_on_fatal_failure(pclient); 189 | return; 190 | } 191 | splice_ret = transocks_single_splice(from_fd, 192 | to_fd, 193 | want_len, 194 | &transfer_bytes); 195 | switch (splice_ret) { 196 | case normal_transfer: 197 | pipe->data_in_pipe += (size_t) transfer_bytes; 198 | break; 199 | case can_retry: 200 | // pipe has data, should clear as soon as possible 201 | if (!TRANSOCKS_IS_INVALID_FD(ultimate_target_fd) && pipe->data_in_pipe > 0) { 202 | // activate events 203 | TRANSOCKS_EVENT_ACTIVE(ultimate_target_libevent_ev, EV_READ); 204 | } 205 | 206 | break; 207 | case fatal_failure: 208 | transocks_on_fatal_failure(pclient); 209 | return; 210 | case read_eof: 211 | *read_eof_flag = true; 212 | TRANSOCKS_SHUTDOWN(from_fd, SHUT_RD); 213 | TRANSOCKS_CLOSE(to_fd); // make sure pipe will get EOF 214 | // no more data from from_fd 215 | event_del(this_libevent_ev); 216 | break; 217 | default: 218 | break; 219 | } 220 | 221 | transocks_check_normal_termination(pclient, direction); 222 | } 223 | 224 | static void transocks_read_from_pipe(transocks_client *pclient, transocks_splicepump_data_direction direction) { 225 | transocks_splicepump *ppump = transocks_get_splicepump(pclient); 226 | transocks_splicepump_splice_result splice_ret; 227 | ssize_t transfer_bytes; 228 | int from_fd = -1, to_fd = -1; 229 | int origin_source_fd = -1; 230 | size_t want_len; 231 | transocks_splicepipe *pipe = NULL; 232 | bool *read_eof_flag; 233 | struct event *this_libevent_ev; 234 | 235 | if (direction == inbound) { 236 | origin_source_fd = pclient->relay_fd; 237 | to_fd = pclient->client_fd; 238 | pipe = ppump->inbound_pipe; 239 | read_eof_flag = &(pclient->client_shutdown_write); 240 | } 241 | if (direction == outbound) { 242 | origin_source_fd = pclient->client_fd; 243 | to_fd = pclient->relay_fd; 244 | pipe = ppump->outbound_pipe; 245 | read_eof_flag = &(pclient->relay_shutdown_write); 246 | } 247 | from_fd = pipe->pipe_readfd; 248 | want_len = pipe->data_in_pipe; 249 | if (TRANSOCKS_IS_INVALID_FD(from_fd) || TRANSOCKS_IS_INVALID_FD(to_fd)) { 250 | LOGD("fatal failure: invalid fd"); 251 | transocks_on_fatal_failure(pclient); 252 | return; 253 | } 254 | splice_ret = transocks_single_splice(from_fd, 255 | to_fd, 256 | want_len, 257 | &transfer_bytes); 258 | switch (splice_ret) { 259 | case normal_transfer: 260 | pipe->data_in_pipe -= (size_t) transfer_bytes; 261 | break; 262 | case can_retry: 263 | LOGE("read_from_pipe splice: unexpected can_retry"); 264 | break; 265 | case fatal_failure: 266 | transocks_on_fatal_failure(pclient); 267 | return; 268 | case read_eof: 269 | // no data from pipe anymore, thus cannot write to other side 270 | *read_eof_flag = true; 271 | TRANSOCKS_SHUTDOWN(to_fd, SHUT_WR); 272 | TRANSOCKS_CLOSE(from_fd); 273 | break; 274 | default: 275 | break; 276 | } 277 | 278 | transocks_check_normal_termination(pclient, direction); 279 | } 280 | 281 | static transocks_splicepump *transocks_splicepump_new(transocks_client *pclient) { 282 | transocks_splicepump *pump = tr_malloc(sizeof(transocks_splicepump)); 283 | if (pump == NULL) { 284 | LOGE("mem"); 285 | return NULL; 286 | } 287 | pump->client_read_ev = NULL; 288 | pump->client_write_ev = NULL; 289 | pump->relay_read_ev = NULL; 290 | pump->relay_write_ev = NULL; 291 | pump->inbound_pipe = NULL; 292 | pump->outbound_pipe = NULL; 293 | 294 | pump->inbound_pipe = tr_malloc(sizeof(transocks_splicepipe)); 295 | if (pump->inbound_pipe == NULL) { 296 | LOGE("mem"); 297 | return NULL; 298 | } 299 | pump->inbound_pipe->pipe_writefd = -1; 300 | pump->inbound_pipe->pipe_readfd = -1; 301 | pump->inbound_pipe->data_in_pipe = 0; 302 | 303 | pump->outbound_pipe = tr_malloc(sizeof(transocks_splicepipe)); 304 | if (pump->outbound_pipe == NULL) { 305 | LOGE("mem"); 306 | return NULL; 307 | } 308 | pump->outbound_pipe->pipe_writefd = -1; 309 | pump->outbound_pipe->pipe_readfd = -1; 310 | pump->outbound_pipe->data_in_pipe = 0; 311 | 312 | // attach pump to client context 313 | pclient->user_arg = pump; 314 | 315 | return pump; 316 | } 317 | 318 | static transocks_splicepump *transocks_get_splicepump(transocks_client *pclient) { 319 | transocks_splicepump *ppump = (transocks_splicepump *) (pclient->user_arg); 320 | return ppump; 321 | } 322 | 323 | static void transocks_splicepump_free(transocks_client *pclient) { 324 | if (pclient == NULL) 325 | return; 326 | transocks_splicepump *ppump = transocks_get_splicepump(pclient); 327 | if (ppump == NULL) 328 | return; 329 | LOGD("enter"); 330 | 331 | TRANSOCKS_CLOSE(ppump->inbound_pipe->pipe_writefd); 332 | TRANSOCKS_CLOSE(ppump->inbound_pipe->pipe_readfd); 333 | TRANSOCKS_FREE(tr_free, ppump->inbound_pipe); 334 | 335 | TRANSOCKS_CLOSE(ppump->outbound_pipe->pipe_writefd); 336 | TRANSOCKS_CLOSE(ppump->outbound_pipe->pipe_readfd); 337 | TRANSOCKS_FREE(tr_free, ppump->outbound_pipe); 338 | 339 | if (ppump->client_read_ev != NULL) { 340 | event_del(ppump->client_read_ev); 341 | } 342 | if (ppump->client_write_ev != NULL) { 343 | event_del(ppump->client_write_ev); 344 | } 345 | if (ppump->relay_read_ev != NULL) { 346 | event_del(ppump->relay_read_ev); 347 | } 348 | if (ppump->relay_write_ev != NULL) { 349 | event_del(ppump->relay_write_ev); 350 | } 351 | TRANSOCKS_FREE(event_free, ppump->client_read_ev); 352 | TRANSOCKS_FREE(event_free, ppump->client_write_ev); 353 | TRANSOCKS_FREE(event_free, ppump->relay_read_ev); 354 | TRANSOCKS_FREE(event_free, ppump->relay_write_ev); 355 | 356 | TRANSOCKS_FREE(tr_free, ppump); 357 | // call outer free func 358 | TRANSOCKS_FREE(transocks_client_free, pclient); 359 | } 360 | 361 | static void transocks_splicepump_dump_info(transocks_client *pclient) { 362 | if (pclient == NULL) 363 | return; 364 | transocks_splicepump *ppump = transocks_get_splicepump(pclient); 365 | if (ppump == NULL) 366 | return; 367 | fprintf(stdout, "\n\tpipe:"); 368 | fprintf(stdout, "\n\t IN: R %d W %d DATALEN %ld CAPACITY %ld", 369 | ppump->inbound_pipe->pipe_readfd, ppump->inbound_pipe->pipe_writefd, 370 | ppump->inbound_pipe->data_in_pipe, ppump->inbound_pipe->capacity); 371 | fprintf(stdout, "\n\t OUT: R %d W %d DATALEN %ld CAPACITY %ld", 372 | ppump->outbound_pipe->pipe_readfd, ppump->outbound_pipe->pipe_writefd, 373 | ppump->outbound_pipe->data_in_pipe, ppump->outbound_pipe->capacity); 374 | // call outer func 375 | transocks_client_dump_info(pclient); 376 | } 377 | 378 | static int transocks_splicepump_start_pump(transocks_client *pclient) { 379 | transocks_global_env *penv = pclient->global_env; 380 | int pipesz; 381 | int error = bufferevent_disable(pclient->client_bev, EV_READ | EV_WRITE); 382 | if (error) { 383 | LOGE("client bev bufferevent_disable"); 384 | goto fail; 385 | } 386 | error = bufferevent_disable(pclient->relay_bev, EV_READ | EV_WRITE); 387 | if (error) { 388 | LOGE("relay bev bufferevent_disable"); 389 | goto fail; 390 | } 391 | // drop fd, we take the responsibility 392 | bufferevent_setfd(pclient->client_bev, -1); 393 | bufferevent_setfd(pclient->relay_bev, -1); 394 | 395 | transocks_splicepump *pump = transocks_splicepump_new(pclient); 396 | if (pump == NULL) { 397 | LOGE("fail to allocate memory"); 398 | goto fail; 399 | } 400 | 401 | // create pipe 402 | if (createpipe(&(pump->inbound_pipe->pipe_readfd), &(pump->inbound_pipe->pipe_writefd)) != 0) { 403 | goto fail; 404 | } 405 | if (createpipe(&(pump->outbound_pipe->pipe_readfd), &(pump->outbound_pipe->pipe_writefd)) != 0) { 406 | goto fail; 407 | } 408 | // set pipe size 409 | if ((pipesz = getpipesize(pump->inbound_pipe->pipe_readfd)) == -1) { 410 | goto fail; 411 | } 412 | pump->inbound_pipe->capacity = (size_t) pipesz; 413 | 414 | if ((pipesz = getpipesize(pump->outbound_pipe->pipe_readfd)) == -1) { 415 | goto fail; 416 | } 417 | pump->outbound_pipe->capacity = (size_t) pipesz; 418 | // create event 419 | pump->client_read_ev = event_new(penv->eventBaseLoop, pclient->client_fd, EV_READ | EV_PERSIST, 420 | transocks_splicepump_client_readcb, pclient); 421 | if (pump->client_read_ev == NULL) { 422 | goto fail; 423 | } 424 | pump->client_write_ev = event_new(penv->eventBaseLoop, pump->inbound_pipe->pipe_readfd, EV_READ | EV_PERSIST, 425 | transocks_splicepump_client_writecb, pclient); 426 | if (pump->client_write_ev == NULL) { 427 | goto fail; 428 | } 429 | pump->relay_read_ev = event_new(penv->eventBaseLoop, pclient->relay_fd, EV_READ | EV_PERSIST, 430 | transocks_splicepump_relay_readcb, pclient); 431 | if (pump->relay_read_ev == NULL) { 432 | goto fail; 433 | } 434 | pump->relay_write_ev = event_new(penv->eventBaseLoop, pump->outbound_pipe->pipe_readfd, EV_READ | EV_PERSIST, 435 | transocks_splicepump_relay_writecb, pclient); 436 | if (pump->relay_write_ev == NULL) { 437 | goto fail; 438 | } 439 | // start both side read 440 | if (event_add(pump->client_read_ev, NULL) != 0) { 441 | goto fail; 442 | } 443 | if (event_add(pump->client_write_ev, NULL) != 0) { 444 | goto fail; 445 | } 446 | if (event_add(pump->relay_read_ev, NULL) != 0) { 447 | goto fail; 448 | } 449 | if (event_add(pump->relay_write_ev, NULL) != 0) { 450 | goto fail; 451 | } 452 | return 0; 453 | 454 | fail: 455 | TRANSOCKS_FREE(transocks_splicepump_free, pclient); 456 | return -1; 457 | } 458 | 459 | 460 | transocks_pump transocks_splicepump_ops = { 461 | .name = PUMPMETHOD_SPLICE, 462 | .start_pump_fn = transocks_splicepump_start_pump, 463 | .free_pump_fn = transocks_splicepump_free, 464 | .dump_info_fn = transocks_splicepump_dump_info 465 | }; 466 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | // 2 | // Taken from Linux kernel 3 | // and modified to fit application's need 4 | // 5 | 6 | #ifndef TRANSOCKS_WONG_LIST_H 7 | #define TRANSOCKS_WONG_LIST_H 8 | 9 | /* SPDX-License-Identifier: GPL-2.0 */ 10 | 11 | #include "mem-allocator.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifndef offsetof 19 | #define offsetof(type, member) ((size_t)( (char *)&(((type *)0)->member) - (char *)0 )) 20 | #endif 21 | 22 | #define container_of(ptr, type, member) ({ \ 23 | const __typeof( ((type *)0)->member ) *__mptr = (ptr); \ 24 | (type *)( (char *)__mptr - offsetof(type,member) );}) 25 | 26 | /* 27 | * Simple doubly linked list implementation. 28 | * 29 | * Some of the internal functions ("__xxx") are useful when 30 | * manipulating whole lists rather than single entries, as 31 | * sometimes we already know the next/prev entries and we can 32 | * generate better code by using them directly rather than 33 | * using the generic single-entry routines. 34 | */ 35 | 36 | struct list_head { 37 | struct list_head *next, *prev; 38 | }; 39 | 40 | struct hlist_head { 41 | struct hlist_node *first; 42 | }; 43 | 44 | struct hlist_node { 45 | struct hlist_node *next, **pprev; 46 | }; 47 | 48 | #if defined(__powerpc64__) 49 | /* arch/powerpc/Kconfig */ 50 | #define CONFIG_ILLEGAL_POINTER_VALUE 0x5deadbeef0000000UL 51 | #elif defined(__aarch64__) || defined(__x86_64__) 52 | /* 53 | * arch/arm64/Kconfig 54 | * arch/x86/Kconfig 55 | */ 56 | #define CONFIG_ILLEGAL_POINTER_VALUE 0xdead000000000000UL 57 | #else 58 | #define CONFIG_ILLEGAL_POINTER_VALUE 0UL 59 | #endif 60 | 61 | /* 62 | * Architectures might want to move the poison pointer offset 63 | * into some well-recognized area such as 0xdead000000000000, 64 | * that is also not mappable by user-space exploits: 65 | */ 66 | #ifdef CONFIG_ILLEGAL_POINTER_VALUE 67 | # define POISON_POINTER_DELTA CONFIG_ILLEGAL_POINTER_VALUE 68 | #else 69 | # define POISON_POINTER_DELTA 0 70 | #endif 71 | 72 | /* 73 | * These are non-NULL pointers that will result in page faults 74 | * under normal circumstances, used to verify that nobody uses 75 | * non-initialized list entries. 76 | */ 77 | #define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA) 78 | #define LIST_POISON2 ((void *) 0x200 + POISON_POINTER_DELTA) 79 | 80 | 81 | #define LIST_HEAD_INIT(name) { &(name), &(name) } 82 | 83 | #define LIST_HEAD(name) \ 84 | struct list_head name = LIST_HEAD_INIT(name) 85 | 86 | static inline void INIT_LIST_HEAD(struct list_head *list) 87 | { 88 | list->next = list; 89 | list->prev = list; 90 | } 91 | 92 | #ifdef CONFIG_DEBUG_LIST 93 | extern bool __list_add_valid(struct list_head *new, 94 | struct list_head *prev, 95 | struct list_head *next); 96 | extern bool __list_del_entry_valid(struct list_head *entry); 97 | #else 98 | static inline bool __list_add_valid(struct list_head *new, 99 | struct list_head *prev, 100 | struct list_head *next) 101 | { 102 | return true; 103 | } 104 | static inline bool __list_del_entry_valid(struct list_head *entry) 105 | { 106 | return true; 107 | } 108 | #endif 109 | 110 | /* 111 | * Insert a new entry between two known consecutive entries. 112 | * 113 | * This is only for internal list manipulation where we know 114 | * the prev/next entries already! 115 | */ 116 | static inline void __list_add(struct list_head *new, 117 | struct list_head *prev, 118 | struct list_head *next) 119 | { 120 | if (!__list_add_valid(new, prev, next)) 121 | return; 122 | 123 | next->prev = new; 124 | new->next = next; 125 | new->prev = prev; 126 | prev->next = new; 127 | } 128 | 129 | /** 130 | * list_add - add a new entry 131 | * @new: new entry to be added 132 | * @head: list head to add it after 133 | * 134 | * Insert a new entry after the specified head. 135 | * This is good for implementing stacks. 136 | */ 137 | static inline void list_add(struct list_head *new, struct list_head *head) 138 | { 139 | __list_add(new, head, head->next); 140 | } 141 | 142 | 143 | /** 144 | * list_add_tail - add a new entry 145 | * @new: new entry to be added 146 | * @head: list head to add it before 147 | * 148 | * Insert a new entry before the specified head. 149 | * This is useful for implementing queues. 150 | */ 151 | static inline void list_add_tail(struct list_head *new, struct list_head *head) 152 | { 153 | __list_add(new, head->prev, head); 154 | } 155 | 156 | /* 157 | * Delete a list entry by making the prev/next entries 158 | * point to each other. 159 | * 160 | * This is only for internal list manipulation where we know 161 | * the prev/next entries already! 162 | */ 163 | static inline void __list_del(struct list_head * prev, struct list_head * next) 164 | { 165 | next->prev = prev; 166 | prev->next = next; 167 | } 168 | 169 | /** 170 | * list_del - deletes entry from list. 171 | * @entry: the element to delete from the list. 172 | * Note: list_empty() on entry does not return true after this, the entry is 173 | * in an undefined state. 174 | */ 175 | static inline void __list_del_entry(struct list_head *entry) 176 | { 177 | if (!__list_del_entry_valid(entry)) 178 | return; 179 | 180 | __list_del(entry->prev, entry->next); 181 | } 182 | 183 | static inline void list_del(struct list_head *entry) 184 | { 185 | __list_del_entry(entry); 186 | entry->next = LIST_POISON1; 187 | entry->prev = LIST_POISON2; 188 | } 189 | 190 | /** 191 | * list_replace - replace old entry by new one 192 | * @old : the element to be replaced 193 | * @new : the new element to insert 194 | * 195 | * If @old was empty, it will be overwritten. 196 | */ 197 | static inline void list_replace(struct list_head *old, 198 | struct list_head *new) 199 | { 200 | new->next = old->next; 201 | new->next->prev = new; 202 | new->prev = old->prev; 203 | new->prev->next = new; 204 | } 205 | 206 | static inline void list_replace_init(struct list_head *old, 207 | struct list_head *new) 208 | { 209 | list_replace(old, new); 210 | INIT_LIST_HEAD(old); 211 | } 212 | 213 | /** 214 | * list_del_init - deletes entry from list and reinitialize it. 215 | * @entry: the element to delete from the list. 216 | */ 217 | static inline void list_del_init(struct list_head *entry) 218 | { 219 | __list_del_entry(entry); 220 | INIT_LIST_HEAD(entry); 221 | } 222 | 223 | /** 224 | * list_move - delete from one list and add as another's head 225 | * @list: the entry to move 226 | * @head: the head that will precede our entry 227 | */ 228 | static inline void list_move(struct list_head *list, struct list_head *head) 229 | { 230 | __list_del_entry(list); 231 | list_add(list, head); 232 | } 233 | 234 | /** 235 | * list_move_tail - delete from one list and add as another's tail 236 | * @list: the entry to move 237 | * @head: the head that will follow our entry 238 | */ 239 | static inline void list_move_tail(struct list_head *list, 240 | struct list_head *head) 241 | { 242 | __list_del_entry(list); 243 | list_add_tail(list, head); 244 | } 245 | 246 | /** 247 | * list_bulk_move_tail - move a subsection of a list to its tail 248 | * @head: the head that will follow our entry 249 | * @first: first entry to move 250 | * @last: last entry to move, can be the same as first 251 | * 252 | * Move all entries between @first and including @last before @head. 253 | * All three entries must belong to the same linked list. 254 | */ 255 | static inline void list_bulk_move_tail(struct list_head *head, 256 | struct list_head *first, 257 | struct list_head *last) 258 | { 259 | first->prev->next = last->next; 260 | last->next->prev = first->prev; 261 | 262 | head->prev->next = first; 263 | first->prev = head->prev; 264 | 265 | last->next = head; 266 | head->prev = last; 267 | } 268 | 269 | /** 270 | * list_is_last - tests whether @list is the last entry in list @head 271 | * @list: the entry to test 272 | * @head: the head of the list 273 | */ 274 | static inline int list_is_last(const struct list_head *list, 275 | const struct list_head *head) 276 | { 277 | return list->next == head; 278 | } 279 | 280 | /** 281 | * list_empty - tests whether a list is empty 282 | * @head: the list to test. 283 | */ 284 | static inline int list_empty(const struct list_head *head) 285 | { 286 | return head->next == head; 287 | } 288 | 289 | /** 290 | * list_empty_careful - tests whether a list is empty and not being modified 291 | * @head: the list to test 292 | * 293 | * Description: 294 | * tests whether a list is empty _and_ checks that no other CPU might be 295 | * in the process of modifying either member (next or prev) 296 | * 297 | * NOTE: using list_empty_careful() without synchronization 298 | * can only be safe if the only activity that can happen 299 | * to the list entry is list_del_init(). Eg. it cannot be used 300 | * if another CPU could re-list_add() it. 301 | */ 302 | static inline int list_empty_careful(const struct list_head *head) 303 | { 304 | struct list_head *next = head->next; 305 | return (next == head) && (next == head->prev); 306 | } 307 | 308 | /** 309 | * list_rotate_left - rotate the list to the left 310 | * @head: the head of the list 311 | */ 312 | static inline void list_rotate_left(struct list_head *head) 313 | { 314 | struct list_head *first; 315 | 316 | if (!list_empty(head)) { 317 | first = head->next; 318 | list_move_tail(first, head); 319 | } 320 | } 321 | 322 | /** 323 | * list_is_singular - tests whether a list has just one entry. 324 | * @head: the list to test. 325 | */ 326 | static inline int list_is_singular(const struct list_head *head) 327 | { 328 | return !list_empty(head) && (head->next == head->prev); 329 | } 330 | 331 | static inline void __list_cut_position(struct list_head *list, 332 | struct list_head *head, struct list_head *entry) 333 | { 334 | struct list_head *new_first = entry->next; 335 | list->next = head->next; 336 | list->next->prev = list; 337 | list->prev = entry; 338 | entry->next = list; 339 | head->next = new_first; 340 | new_first->prev = head; 341 | } 342 | 343 | /** 344 | * list_cut_position - cut a list into two 345 | * @list: a new list to add all removed entries 346 | * @head: a list with entries 347 | * @entry: an entry within head, could be the head itself 348 | * and if so we won't cut the list 349 | * 350 | * This helper moves the initial part of @head, up to and 351 | * including @entry, from @head to @list. You should 352 | * pass on @entry an element you know is on @head. @list 353 | * should be an empty list or a list you do not care about 354 | * losing its data. 355 | * 356 | */ 357 | static inline void list_cut_position(struct list_head *list, 358 | struct list_head *head, struct list_head *entry) 359 | { 360 | if (list_empty(head)) 361 | return; 362 | if (list_is_singular(head) && 363 | (head->next != entry && head != entry)) 364 | return; 365 | if (entry == head) 366 | INIT_LIST_HEAD(list); 367 | else 368 | __list_cut_position(list, head, entry); 369 | } 370 | 371 | /** 372 | * list_cut_before - cut a list into two, before given entry 373 | * @list: a new list to add all removed entries 374 | * @head: a list with entries 375 | * @entry: an entry within head, could be the head itself 376 | * 377 | * This helper moves the initial part of @head, up to but 378 | * excluding @entry, from @head to @list. You should pass 379 | * in @entry an element you know is on @head. @list should 380 | * be an empty list or a list you do not care about losing 381 | * its data. 382 | * If @entry == @head, all entries on @head are moved to 383 | * @list. 384 | */ 385 | static inline void list_cut_before(struct list_head *list, 386 | struct list_head *head, 387 | struct list_head *entry) 388 | { 389 | if (head->next == entry) { 390 | INIT_LIST_HEAD(list); 391 | return; 392 | } 393 | list->next = head->next; 394 | list->next->prev = list; 395 | list->prev = entry->prev; 396 | list->prev->next = list; 397 | head->next = entry; 398 | entry->prev = head; 399 | } 400 | 401 | static inline void __list_splice(const struct list_head *list, 402 | struct list_head *prev, 403 | struct list_head *next) 404 | { 405 | struct list_head *first = list->next; 406 | struct list_head *last = list->prev; 407 | 408 | first->prev = prev; 409 | prev->next = first; 410 | 411 | last->next = next; 412 | next->prev = last; 413 | } 414 | 415 | /** 416 | * list_splice - join two lists, this is designed for stacks 417 | * @list: the new list to add. 418 | * @head: the place to add it in the first list. 419 | */ 420 | static inline void list_splice(const struct list_head *list, 421 | struct list_head *head) 422 | { 423 | if (!list_empty(list)) 424 | __list_splice(list, head, head->next); 425 | } 426 | 427 | /** 428 | * list_splice_tail - join two lists, each list being a queue 429 | * @list: the new list to add. 430 | * @head: the place to add it in the first list. 431 | */ 432 | static inline void list_splice_tail(struct list_head *list, 433 | struct list_head *head) 434 | { 435 | if (!list_empty(list)) 436 | __list_splice(list, head->prev, head); 437 | } 438 | 439 | /** 440 | * list_splice_init - join two lists and reinitialise the emptied list. 441 | * @list: the new list to add. 442 | * @head: the place to add it in the first list. 443 | * 444 | * The list at @list is reinitialised 445 | */ 446 | static inline void list_splice_init(struct list_head *list, 447 | struct list_head *head) 448 | { 449 | if (!list_empty(list)) { 450 | __list_splice(list, head, head->next); 451 | INIT_LIST_HEAD(list); 452 | } 453 | } 454 | 455 | /** 456 | * list_splice_tail_init - join two lists and reinitialise the emptied list 457 | * @list: the new list to add. 458 | * @head: the place to add it in the first list. 459 | * 460 | * Each of the lists is a queue. 461 | * The list at @list is reinitialised 462 | */ 463 | static inline void list_splice_tail_init(struct list_head *list, 464 | struct list_head *head) 465 | { 466 | if (!list_empty(list)) { 467 | __list_splice(list, head->prev, head); 468 | INIT_LIST_HEAD(list); 469 | } 470 | } 471 | 472 | /** 473 | * list_entry - get the struct for this entry 474 | * @ptr: the &struct list_head pointer. 475 | * @type: the type of the struct this is embedded in. 476 | * @member: the name of the list_head within the struct. 477 | */ 478 | #define list_entry(ptr, type, member) \ 479 | container_of(ptr, type, member) 480 | 481 | /** 482 | * list_first_entry - get the first element from a list 483 | * @ptr: the list head to take the element from. 484 | * @type: the type of the struct this is embedded in. 485 | * @member: the name of the list_head within the struct. 486 | * 487 | * Note, that list is expected to be not empty. 488 | */ 489 | #define list_first_entry(ptr, type, member) \ 490 | list_entry((ptr)->next, type, member) 491 | 492 | /** 493 | * list_last_entry - get the last element from a list 494 | * @ptr: the list head to take the element from. 495 | * @type: the type of the struct this is embedded in. 496 | * @member: the name of the list_head within the struct. 497 | * 498 | * Note, that list is expected to be not empty. 499 | */ 500 | #define list_last_entry(ptr, type, member) \ 501 | list_entry((ptr)->prev, type, member) 502 | 503 | /** 504 | * list_first_entry_or_null - get the first element from a list 505 | * @ptr: the list head to take the element from. 506 | * @type: the type of the struct this is embedded in. 507 | * @member: the name of the list_head within the struct. 508 | * 509 | * Note that if the list is empty, it returns NULL. 510 | */ 511 | #define list_first_entry_or_null(ptr, type, member) ({ \ 512 | struct list_head *head__ = (ptr); \ 513 | struct list_head *pos__ = READ_ONCE(head__->next); \ 514 | pos__ != head__ ? list_entry(pos__, type, member) : NULL; \ 515 | }) 516 | 517 | /** 518 | * list_next_entry - get the next element in list 519 | * @pos: the type * to cursor 520 | * @member: the name of the list_head within the struct. 521 | */ 522 | #define list_next_entry(pos, member) \ 523 | list_entry((pos)->member.next, __typeof(*(pos)), member) 524 | 525 | /** 526 | * list_prev_entry - get the prev element in list 527 | * @pos: the type * to cursor 528 | * @member: the name of the list_head within the struct. 529 | */ 530 | #define list_prev_entry(pos, member) \ 531 | list_entry((pos)->member.prev, __typeof(*(pos)), member) 532 | 533 | /** 534 | * list_for_each - iterate over a list 535 | * @pos: the &struct list_head to use as a loop cursor. 536 | * @head: the head for your list. 537 | */ 538 | #define list_for_each(pos, head) \ 539 | for (pos = (head)->next; pos != (head); pos = pos->next) 540 | 541 | /** 542 | * list_for_each_prev - iterate over a list backwards 543 | * @pos: the &struct list_head to use as a loop cursor. 544 | * @head: the head for your list. 545 | */ 546 | #define list_for_each_prev(pos, head) \ 547 | for (pos = (head)->prev; pos != (head); pos = pos->prev) 548 | 549 | /** 550 | * list_for_each_safe - iterate over a list safe against removal of list entry 551 | * @pos: the &struct list_head to use as a loop cursor. 552 | * @n: another &struct list_head to use as temporary storage 553 | * @head: the head for your list. 554 | */ 555 | #define list_for_each_safe(pos, n, head) \ 556 | for (pos = (head)->next, n = pos->next; pos != (head); \ 557 | pos = n, n = pos->next) 558 | 559 | /** 560 | * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry 561 | * @pos: the &struct list_head to use as a loop cursor. 562 | * @n: another &struct list_head to use as temporary storage 563 | * @head: the head for your list. 564 | */ 565 | #define list_for_each_prev_safe(pos, n, head) \ 566 | for (pos = (head)->prev, n = pos->prev; \ 567 | pos != (head); \ 568 | pos = n, n = pos->prev) 569 | 570 | /** 571 | * list_for_each_entry - iterate over list of given type 572 | * @pos: the type * to use as a loop cursor. 573 | * @head: the head for your list. 574 | * @member: the name of the list_head within the struct. 575 | */ 576 | #define list_for_each_entry(pos, head, member) \ 577 | for (pos = list_first_entry(head, __typeof(*pos), member); \ 578 | &pos->member != (head); \ 579 | pos = list_next_entry(pos, member)) 580 | 581 | /** 582 | * list_for_each_entry_reverse - iterate backwards over list of given type. 583 | * @pos: the type * to use as a loop cursor. 584 | * @head: the head for your list. 585 | * @member: the name of the list_head within the struct. 586 | */ 587 | #define list_for_each_entry_reverse(pos, head, member) \ 588 | for (pos = list_last_entry(head, __typeof(*pos), member); \ 589 | &pos->member != (head); \ 590 | pos = list_prev_entry(pos, member)) 591 | 592 | /** 593 | * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() 594 | * @pos: the type * to use as a start point 595 | * @head: the head of the list 596 | * @member: the name of the list_head within the struct. 597 | * 598 | * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). 599 | */ 600 | #define list_prepare_entry(pos, head, member) \ 601 | ((pos) ? : list_entry(head, __typeof(*pos), member)) 602 | 603 | /** 604 | * list_for_each_entry_continue - continue iteration over list of given type 605 | * @pos: the type * to use as a loop cursor. 606 | * @head: the head for your list. 607 | * @member: the name of the list_head within the struct. 608 | * 609 | * Continue to iterate over list of given type, continuing after 610 | * the current position. 611 | */ 612 | #define list_for_each_entry_continue(pos, head, member) \ 613 | for (pos = list_next_entry(pos, member); \ 614 | &pos->member != (head); \ 615 | pos = list_next_entry(pos, member)) 616 | 617 | /** 618 | * list_for_each_entry_continue_reverse - iterate backwards from the given point 619 | * @pos: the type * to use as a loop cursor. 620 | * @head: the head for your list. 621 | * @member: the name of the list_head within the struct. 622 | * 623 | * Start to iterate over list of given type backwards, continuing after 624 | * the current position. 625 | */ 626 | #define list_for_each_entry_continue_reverse(pos, head, member) \ 627 | for (pos = list_prev_entry(pos, member); \ 628 | &pos->member != (head); \ 629 | pos = list_prev_entry(pos, member)) 630 | 631 | /** 632 | * list_for_each_entry_from - iterate over list of given type from the current point 633 | * @pos: the type * to use as a loop cursor. 634 | * @head: the head for your list. 635 | * @member: the name of the list_head within the struct. 636 | * 637 | * Iterate over list of given type, continuing from current position. 638 | */ 639 | #define list_for_each_entry_from(pos, head, member) \ 640 | for (; &pos->member != (head); \ 641 | pos = list_next_entry(pos, member)) 642 | 643 | /** 644 | * list_for_each_entry_from_reverse - iterate backwards over list of given type 645 | * from the current point 646 | * @pos: the type * to use as a loop cursor. 647 | * @head: the head for your list. 648 | * @member: the name of the list_head within the struct. 649 | * 650 | * Iterate backwards over list of given type, continuing from current position. 651 | */ 652 | #define list_for_each_entry_from_reverse(pos, head, member) \ 653 | for (; &pos->member != (head); \ 654 | pos = list_prev_entry(pos, member)) 655 | 656 | /** 657 | * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry 658 | * @pos: the type * to use as a loop cursor. 659 | * @n: another type * to use as temporary storage 660 | * @head: the head for your list. 661 | * @member: the name of the list_head within the struct. 662 | */ 663 | #define list_for_each_entry_safe(pos, n, head, member) \ 664 | for (pos = list_first_entry(head, __typeof(*pos), member), \ 665 | n = list_next_entry(pos, member); \ 666 | &pos->member != (head); \ 667 | pos = n, n = list_next_entry(n, member)) 668 | 669 | /** 670 | * list_for_each_entry_safe_continue - continue list iteration safe against removal 671 | * @pos: the type * to use as a loop cursor. 672 | * @n: another type * to use as temporary storage 673 | * @head: the head for your list. 674 | * @member: the name of the list_head within the struct. 675 | * 676 | * Iterate over list of given type, continuing after current point, 677 | * safe against removal of list entry. 678 | */ 679 | #define list_for_each_entry_safe_continue(pos, n, head, member) \ 680 | for (pos = list_next_entry(pos, member), \ 681 | n = list_next_entry(pos, member); \ 682 | &pos->member != (head); \ 683 | pos = n, n = list_next_entry(n, member)) 684 | 685 | /** 686 | * list_for_each_entry_safe_from - iterate over list from current point safe against removal 687 | * @pos: the type * to use as a loop cursor. 688 | * @n: another type * to use as temporary storage 689 | * @head: the head for your list. 690 | * @member: the name of the list_head within the struct. 691 | * 692 | * Iterate over list of given type from current point, safe against 693 | * removal of list entry. 694 | */ 695 | #define list_for_each_entry_safe_from(pos, n, head, member) \ 696 | for (n = list_next_entry(pos, member); \ 697 | &pos->member != (head); \ 698 | pos = n, n = list_next_entry(n, member)) 699 | 700 | /** 701 | * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal 702 | * @pos: the type * to use as a loop cursor. 703 | * @n: another type * to use as temporary storage 704 | * @head: the head for your list. 705 | * @member: the name of the list_head within the struct. 706 | * 707 | * Iterate backwards over list of given type, safe against removal 708 | * of list entry. 709 | */ 710 | #define list_for_each_entry_safe_reverse(pos, n, head, member) \ 711 | for (pos = list_last_entry(head, __typeof(*pos), member), \ 712 | n = list_prev_entry(pos, member); \ 713 | &pos->member != (head); \ 714 | pos = n, n = list_prev_entry(n, member)) 715 | 716 | /** 717 | * list_safe_reset_next - reset a stale list_for_each_entry_safe loop 718 | * @pos: the loop cursor used in the list_for_each_entry_safe loop 719 | * @n: temporary storage used in list_for_each_entry_safe 720 | * @member: the name of the list_head within the struct. 721 | * 722 | * list_safe_reset_next is not safe to use in general if the list may be 723 | * modified concurrently (eg. the lock is dropped in the loop body). An 724 | * exception to this is if the cursor element (pos) is pinned in the list, 725 | * and list_safe_reset_next is called after re-taking the lock and before 726 | * completing the current iteration of the loop body. 727 | */ 728 | #define list_safe_reset_next(pos, n, member) \ 729 | n = list_next_entry(pos, member) 730 | 731 | /* 732 | * Double linked lists with a single pointer list head. 733 | * Mostly useful for hash tables where the two pointer list head is 734 | * too wasteful. 735 | * You lose the ability to access the tail in O(1). 736 | */ 737 | 738 | #define HLIST_HEAD_INIT { .first = NULL } 739 | #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } 740 | #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) 741 | static inline void INIT_HLIST_NODE(struct hlist_node *h) 742 | { 743 | h->next = NULL; 744 | h->pprev = NULL; 745 | } 746 | 747 | static inline int hlist_unhashed(const struct hlist_node *h) 748 | { 749 | return !h->pprev; 750 | } 751 | 752 | static inline int hlist_empty(const struct hlist_head *h) 753 | { 754 | return !h->first; 755 | } 756 | 757 | static inline void __hlist_del(struct hlist_node *n) 758 | { 759 | struct hlist_node *next = n->next; 760 | struct hlist_node **pprev = n->pprev; 761 | 762 | *pprev = next; 763 | if (next) 764 | next->pprev = pprev; 765 | } 766 | 767 | static inline void hlist_del(struct hlist_node *n) 768 | { 769 | __hlist_del(n); 770 | n->next = LIST_POISON1; 771 | n->pprev = LIST_POISON2; 772 | } 773 | 774 | static inline void hlist_del_init(struct hlist_node *n) 775 | { 776 | if (!hlist_unhashed(n)) { 777 | __hlist_del(n); 778 | INIT_HLIST_NODE(n); 779 | } 780 | } 781 | 782 | static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) 783 | { 784 | struct hlist_node *first = h->first; 785 | n->next = first; 786 | if (first) 787 | first->pprev = &n->next; 788 | h->first = n; 789 | n->pprev = &h->first; 790 | } 791 | 792 | /* next must be != NULL */ 793 | static inline void hlist_add_before(struct hlist_node *n, 794 | struct hlist_node *next) 795 | { 796 | n->pprev = next->pprev; 797 | n->next = next; 798 | next->pprev = &n->next; 799 | *(n->pprev) = n; 800 | } 801 | 802 | static inline void hlist_add_behind(struct hlist_node *n, 803 | struct hlist_node *prev) 804 | { 805 | n->next = prev->next; 806 | prev->next = n; 807 | n->pprev = &prev->next; 808 | 809 | if (n->next) 810 | n->next->pprev = &n->next; 811 | } 812 | 813 | /* after that we'll appear to be on some hlist and hlist_del will work */ 814 | static inline void hlist_add_fake(struct hlist_node *n) 815 | { 816 | n->pprev = &n->next; 817 | } 818 | 819 | static inline bool hlist_fake(struct hlist_node *h) 820 | { 821 | return h->pprev == &h->next; 822 | } 823 | 824 | /* 825 | * Check whether the node is the only node of the head without 826 | * accessing head: 827 | */ 828 | static inline bool 829 | hlist_is_singular_node(struct hlist_node *n, struct hlist_head *h) 830 | { 831 | return !n->next && n->pprev == &h->first; 832 | } 833 | 834 | /* 835 | * Move a list from one list head to another. Fixup the pprev 836 | * reference of the first entry if it exists. 837 | */ 838 | static inline void hlist_move_list(struct hlist_head *old, 839 | struct hlist_head *new) 840 | { 841 | new->first = old->first; 842 | if (new->first) 843 | new->first->pprev = &new->first; 844 | old->first = NULL; 845 | } 846 | 847 | #define hlist_entry(ptr, type, member) container_of(ptr,type,member) 848 | 849 | #define hlist_for_each(pos, head) \ 850 | for (pos = (head)->first; pos ; pos = pos->next) 851 | 852 | #define hlist_for_each_safe(pos, n, head) \ 853 | for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ 854 | pos = n) 855 | 856 | #define hlist_entry_safe(ptr, type, member) \ 857 | ({ __typeof(ptr) ____ptr = (ptr); \ 858 | ____ptr ? hlist_entry(____ptr, type, member) : NULL; \ 859 | }) 860 | 861 | /** 862 | * hlist_for_each_entry - iterate over list of given type 863 | * @pos: the type * to use as a loop cursor. 864 | * @head: the head for your list. 865 | * @member: the name of the hlist_node within the struct. 866 | */ 867 | #define hlist_for_each_entry(pos, head, member) \ 868 | for (pos = hlist_entry_safe((head)->first, __typeof(*(pos)), member);\ 869 | pos; \ 870 | pos = hlist_entry_safe((pos)->member.next, __typeof(*(pos)), member)) 871 | 872 | /** 873 | * hlist_for_each_entry_continue - iterate over a hlist continuing after current point 874 | * @pos: the type * to use as a loop cursor. 875 | * @member: the name of the hlist_node within the struct. 876 | */ 877 | #define hlist_for_each_entry_continue(pos, member) \ 878 | for (pos = hlist_entry_safe((pos)->member.next, __typeof(*(pos)), member);\ 879 | pos; \ 880 | pos = hlist_entry_safe((pos)->member.next, __typeof(*(pos)), member)) 881 | 882 | /** 883 | * hlist_for_each_entry_from - iterate over a hlist continuing from current point 884 | * @pos: the type * to use as a loop cursor. 885 | * @member: the name of the hlist_node within the struct. 886 | */ 887 | #define hlist_for_each_entry_from(pos, member) \ 888 | for (; pos; \ 889 | pos = hlist_entry_safe((pos)->member.next, __typeof(*(pos)), member)) 890 | 891 | /** 892 | * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry 893 | * @pos: the type * to use as a loop cursor. 894 | * @n: another &struct hlist_node to use as temporary storage 895 | * @head: the head for your list. 896 | * @member: the name of the hlist_node within the struct. 897 | */ 898 | #define hlist_for_each_entry_safe(pos, n, head, member) \ 899 | for (pos = hlist_entry_safe((head)->first, __typeof(*pos), member);\ 900 | pos && ({ n = pos->member.next; 1; }); \ 901 | pos = hlist_entry_safe(n, __typeof(*pos), member)) 902 | 903 | #endif //TRANSOCKS_WONG_LIST_H 904 | --------------------------------------------------------------------------------