diff options
author | Andreas Schneider <asn@cryptomilk.org> | 2013-02-05 17:18:31 +0100 |
---|---|---|
committer | Andreas Schneider <asn@cryptomilk.org> | 2013-02-07 16:58:18 +0100 |
commit | 237c6e951a8268afeaa288d8ddf0c79da32f6fd1 (patch) | |
tree | 682e9be946c28709c4cfb95fc72af7626f898786 | |
download | socket_wrapper-237c6e951a8268afeaa288d8ddf0c79da32f6fd1.tar.gz socket_wrapper-237c6e951a8268afeaa288d8ddf0c79da32f6fd1.tar.xz socket_wrapper-237c6e951a8268afeaa288d8ddf0c79da32f6fd1.zip |
Initial commit.
-rw-r--r-- | CMakeLists.txt | 51 | ||||
-rw-r--r-- | COPYING | 32 | ||||
-rw-r--r-- | CPackConfig.cmake | 53 | ||||
-rw-r--r-- | CTestConfig.cmake | 9 | ||||
-rw-r--r-- | ConfigureChecks.cmake | 100 | ||||
-rw-r--r-- | DefineOptions.cmake | 27 | ||||
-rw-r--r-- | README | 18 | ||||
-rw-r--r-- | cmake/Modules/COPYING-CMAKE-SCRIPTS | 22 | ||||
-rw-r--r-- | cmake/Modules/CheckCCompilerFlagSSP.cmake | 26 | ||||
-rw-r--r-- | cmake/Modules/DefineCMakeDefaults.cmake | 27 | ||||
-rw-r--r-- | cmake/Modules/DefineCompilerFlags.cmake | 75 | ||||
-rw-r--r-- | cmake/Modules/DefineInstallationPaths.cmake | 104 | ||||
-rw-r--r-- | cmake/Modules/DefinePlatformDefaults.cmake | 28 | ||||
-rw-r--r-- | cmake/Modules/MacroEnsureOutOfSourceBuild.cmake | 17 | ||||
-rw-r--r-- | config.h.cmake | 58 | ||||
-rw-r--r-- | src/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/py_socket_wrapper.c | 883 | ||||
-rw-r--r-- | src/socket.py | 50 | ||||
-rw-r--r-- | src/socket_wrapper.c | 2666 | ||||
-rw-r--r-- | src/socket_wrapper.h | 177 | ||||
-rw-r--r-- | tests/testsuite.c | 105 |
21 files changed, 4532 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c047947 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +project(socket_wrapper C) + +# Required cmake version +cmake_minimum_required(VERSION 2.8.0) + +# global needed variables +set(APPLICATION_NAME ${PROJECT_NAME}) + +set(APPLICATION_VERSION_MAJOR "0") +set(APPLICATION_VERSION_MINOR "1") +set(APPLICATION_VERSION_PATCH "0") + +set(APPLICATION_VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}") + +# SOVERSION scheme: CURRENT.AGE.REVISION +# If there was an incompatible interface change: +# Increment CURRENT. Set AGE and REVISION to 0 +# If there was a compatible interface change: +# Increment AGE. Set REVISION to 0 +# If the source code was changed, but there were no interface changes: +# Increment REVISION. +set(LIBRARY_VERSION "0.0.1") +set(LIBRARY_SOVERSION "0") + +# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked +set(CMAKE_MODULE_PATH + ${CMAKE_SOURCE_DIR}/cmake/Modules +) + +# add definitions +include(DefineCMakeDefaults) +include(DefinePlatformDefaults) +include(DefineCompilerFlags) +include(DefineInstallationPaths) +include(DefineOptions.cmake) +include(CPackConfig.cmake) + +# disallow in-source build +include(MacroEnsureOutOfSourceBuild) +macro_ensure_out_of_source_build("${PROJECT_NAME} requires an out of source build. Please create a separate build directory and run 'cmake /path/to/${PROJECT_NAME} [options]' there.") + +# Find out if we have threading available +set(CMAKE_THREAD_PREFER_PTHREADS ON) +find_package(Threads) + +# config.h checks +include(ConfigureChecks.cmake) +configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +# check subdirectories +add_subdirectory(src) @@ -0,0 +1,32 @@ +Copyright (C) Jelmer Vernooij 2005,2008 <jelmer@samba.org> +Copyright (C) Stefan Metzmacher 2006-2009 <metze@samba.org> +Copyright (C) Andreas Schneider 2013 <asn@samba.org> + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/CPackConfig.cmake b/CPackConfig.cmake new file mode 100644 index 0000000..2e4c9fe --- /dev/null +++ b/CPackConfig.cmake @@ -0,0 +1,53 @@ +# For help take a look at: +# http://www.cmake.org/Wiki/CMake:CPackConfiguration + +### general settings +set(CPACK_PACKAGE_NAME ${APPLICATION_NAME}) +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The SSH library") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README") +set(CPACK_PACKAGE_VENDOR "The SSH Library Development Team") +set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING") + + +### versions +set(CPACK_PACKAGE_VERSION_MAJOR "0") +set(CPACK_PACKAGE_VERSION_MINOR "5") +set(CPACK_PACKAGE_VERSION_PATCH "90") +set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") + + +### source generator +set(CPACK_SOURCE_GENERATOR "TGZ") +set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]svn/;/[.]git/;.gitignore;/build/;tags;cscope.*") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") + +if (WIN32) + set(CPACK_GENERATOR "ZIP") + + ### nsis generator + find_package(NSIS) + if (NSIS_MAKE) + set(CPACK_GENERATOR "${CPACK_GENERATOR};NSIS") + set(CPACK_NSIS_DISPLAY_NAME "The SSH Library") + set(CPACK_NSIS_COMPRESSOR "/SOLID zlib") + set(CPACK_NSIS_MENU_LINKS "http://www.libssh.org/" "libssh homepage") + endif (NSIS_MAKE) +endif (WIN32) + +set(CPACK_PACKAGE_INSTALL_DIRECTORY "libssh") + +set(CPACK_PACKAGE_FILE_NAME ${APPLICATION_NAME}-${CPACK_PACKAGE_VERSION}) + +set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries") +set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C/C++ Headers") +set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION + "Libraries used to build programs which use libssh") +set(CPACK_COMPONENT_HEADERS_DESCRIPTION + "C/C++ header files for use with libssh") +set(CPACK_COMPONENT_HEADERS_DEPENDS libraries) +#set(CPACK_COMPONENT_APPLICATIONS_GROUP "Runtime") +set(CPACK_COMPONENT_LIBRARIES_GROUP "Development") +set(CPACK_COMPONENT_HEADERS_GROUP "Development") + +include(CPack) diff --git a/CTestConfig.cmake b/CTestConfig.cmake new file mode 100644 index 0000000..d8a4183 --- /dev/null +++ b/CTestConfig.cmake @@ -0,0 +1,9 @@ +set(UPDATE_TYPE "true") + +set(CTEST_PROJECT_NAME "libssh") +set(CTEST_NIGHTLY_START_TIME "01:00:00 CET") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "test.libssh.org") +set(CTEST_DROP_LOCATION "/submit.php?project=libssh") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake new file mode 100644 index 0000000..6cb45d0 --- /dev/null +++ b/ConfigureChecks.cmake @@ -0,0 +1,100 @@ +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckFunctionExists) +include(CheckLibraryExists) +include(CheckTypeSize) +include(CheckStructHasMember) +include(CheckPrototypeDefinition) +include(TestBigEndian) + +set(PACKAGE ${APPLICATION_NAME}) +set(VERSION ${APPLICATION_VERSION}) +set(DATADIR ${DATA_INSTALL_DIR}) +set(LIBDIR ${LIB_INSTALL_DIR}) +set(PLUGINDIR "${PLUGIN_INSTALL_DIR}-${LIBRARY_SOVERSION}") +set(SYSCONFDIR ${SYSCONF_INSTALL_DIR}) + +set(BINARYDIR ${CMAKE_BINARY_DIR}) +set(SOURCEDIR ${CMAKE_SOURCE_DIR}) + +function(COMPILER_DUMPVERSION _OUTPUT_VERSION) + # Remove whitespaces from the argument. + # This is needed for CC="ccache gcc" cmake .. + string(REPLACE " " "" _C_COMPILER_ARG "${CMAKE_C_COMPILER_ARG1}") + + execute_process( + COMMAND + ${CMAKE_C_COMPILER} ${_C_COMPILER_ARG} -dumpversion + OUTPUT_VARIABLE _COMPILER_VERSION + ) + + string(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" + _COMPILER_VERSION "${_COMPILER_VERSION}") + + set(${_OUTPUT_VERSION} ${_COMPILER_VERSION} PARENT_SCOPE) +endfunction() + +if(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) + compiler_dumpversion(GNUCC_VERSION) + if (NOT GNUCC_VERSION EQUAL 34) + set(CMAKE_REQUIRED_FLAGS "-fvisibility=hidden") + check_c_source_compiles( +"void __attribute__((visibility(\"default\"))) test() {} +int main(void){ return 0; } +" WITH_VISIBILITY_HIDDEN) + set(CMAKE_REQUIRED_FLAGS "") + endif (NOT GNUCC_VERSION EQUAL 34) +endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) + +# HEADERS +check_include_file(sys/filio.h HAVE_SYS_FILIO_H) + +# FUNCTIONS +check_function_exists(strncpy HAVE_STRNCPY) +check_function_exists(vsnprintf HAVE_VSNPRINTF) +check_function_exists(snprintf HAVE_SNPRINTF) + +if (WIN32) + check_function_exists(_vsnprintf_s HAVE__VSNPRINTF_S) + check_function_exists(_vsnprintf HAVE__VSNPRINTF) + check_function_exists(_snprintf HAVE__SNPRINTF) + check_function_exists(_snprintf_s HAVE__SNPRINTF_S) +endif (WIN32) + +if (UNIX) + if (NOT LINUX) + # libsocket (Solaris) + check_library_exists(socket getaddrinfo "" HAVE_LIBSOCKET) + if (HAVE_LIBSOCKET) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} socket) + endif (HAVE_LIBSOCKET) + + # libnsl/inet_pton (Solaris) + check_library_exists(nsl inet_pton "" HAVE_LIBNSL) + if (HAVE_LIBNSL) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} nsl) + endif (HAVE_LIBNSL) + endif (NOT LINUX) + + check_function_exists(getaddrinfo HAVE_GETADDRINFO) +endif (UNIX) + +set(LIBSSH_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} CACHE INTERNAL "libssh required system libraries") + +# STRUCT MEMBERS +check_struct_has_member("struct sockaddr" sa_len "sys/socket.h netinet/in.h" HAVE_STRUCT_SOCKADDR_SA_LEN) + +# PROTOTYPES +check_prototype_definition(gettimeofday + "int gettimeofday(struct timeval *tv, struct timezone *tz)" + "-1" + "sys/time.h" + HAVE_GETTIMEOFDAY_TZ) + +# TODO +set(HAVE_IPV6 TRUE) + +# ENDIAN +if (NOT WIN32) + test_big_endian(WORDS_BIGENDIAN) +endif (NOT WIN32) diff --git a/DefineOptions.cmake b/DefineOptions.cmake new file mode 100644 index 0000000..ea8265c --- /dev/null +++ b/DefineOptions.cmake @@ -0,0 +1,27 @@ +option(WITH_ZLIB "Build with ZLIB support" ON) +option(WITH_SSH1 "Build with SSH1 support" OFF) +option(WITH_SFTP "Build with SFTP support" ON) +option(WITH_SERVER "Build with SSH server support" ON) +option(WITH_STATIC_LIB "Build with a static library" OFF) +option(WITH_DEBUG_CRYPTO "Build with cryto debug output" OFF) +option(WITH_DEBUG_CALLTRACE "Build with calltrace debug output" ON) +option(WITH_GCRYPT "Compile against libgcrypt" OFF) +option(WITH_PCAP "Compile with Pcap generation support" ON) +option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF) +option(WITH_TESTING "Build with unit tests" OFF) +option(WITH_CLIENT_TESTING "Build with client tests; requires a running sshd" OFF) +option(WITH_BENCHMARKS "Build benchmarks tools" OFF) + +if (WITH_ZLIB) + set(WITH_LIBZ ON) +else (WITH_ZLIB) + set(WITH_LIBZ OFF) +endif (WITH_ZLIB) + +if(WITH_BENCHMARKS) + set(WITH_TESTING ON) +endif(WITH_BENCHMARKS) + +if (WITH_TESTING) + set(WITH_STATIC_LIB ON) +endif (WITH_TESTING) @@ -0,0 +1,18 @@ +Socket wrapper library +======================= + +This library passes all socket communication over unix domain sockets if the +environment variable SOCKET_WRAPPER_DIR is set. + +It need to be preloaded with LD_PRELOAD. + +Environment variables which could be set: + +SOCKET_WRAPPER_DIR + The directory to place all unix sockets. + +SOCKET_WRAPPER_DEFAULT_IFACE + +SOCKET_WRAPPER_PCAP_FILE + If set then all traffic will be written to the + specified pcap file. diff --git a/cmake/Modules/COPYING-CMAKE-SCRIPTS b/cmake/Modules/COPYING-CMAKE-SCRIPTS new file mode 100644 index 0000000..4b41776 --- /dev/null +++ b/cmake/Modules/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cmake/Modules/CheckCCompilerFlagSSP.cmake b/cmake/Modules/CheckCCompilerFlagSSP.cmake new file mode 100644 index 0000000..2fe4395 --- /dev/null +++ b/cmake/Modules/CheckCCompilerFlagSSP.cmake @@ -0,0 +1,26 @@ +# - Check whether the C compiler supports a given flag in the +# context of a stack checking compiler option. + +# CHECK_C_COMPILER_FLAG_SSP(FLAG VARIABLE) +# +# FLAG - the compiler flag +# VARIABLE - variable to store the result +# +# This actually calls check_c_source_compiles. +# See help for CheckCSourceCompiles for a listing of variables +# that can modify the build. + +# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org> +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +include(CheckCSourceCompiles) + +function(CHECK_C_COMPILER_FLAG_SSP _FLAG _RESULT) + set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") + set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") + check_c_source_compiles("int main(int argc, char **argv) { char buffer[256]; return buffer[argc]=0;}" ${_RESULT}) + set(CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") +endfunction(CHECK_C_COMPILER_FLAG_SSP) diff --git a/cmake/Modules/DefineCMakeDefaults.cmake b/cmake/Modules/DefineCMakeDefaults.cmake new file mode 100644 index 0000000..72893c3 --- /dev/null +++ b/cmake/Modules/DefineCMakeDefaults.cmake @@ -0,0 +1,27 @@ +# Always include srcdir and builddir in include path +# This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY} in +# about every subdir +# since cmake 2.4.0 +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +# Put the include dirs which are in the source or build tree +# before all other include dirs, so the headers in the sources +# are prefered over the already installed ones +# since cmake 2.4.1 +set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) + +# Use colored output +# since cmake 2.4.0 +set(CMAKE_COLOR_MAKEFILE ON) + +# Define the generic version of the libraries here +set(GENERIC_LIB_VERSION "0.1.0") +set(GENERIC_LIB_SOVERSION "0") + +# Set the default build type to release with debug info +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RelWithDebInfo + CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." + ) +endif (NOT CMAKE_BUILD_TYPE) diff --git a/cmake/Modules/DefineCompilerFlags.cmake b/cmake/Modules/DefineCompilerFlags.cmake new file mode 100644 index 0000000..0283b12 --- /dev/null +++ b/cmake/Modules/DefineCompilerFlags.cmake @@ -0,0 +1,75 @@ +# define system dependent compiler flags + +include(CheckCCompilerFlag) +include(CheckCCompilerFlagSSP) + +if (UNIX AND NOT WIN32) + # + # Define GNUCC compiler flags + # + if (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)") + + # add -Wconversion ? + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -pedantic -pedantic-errors") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wmissing-prototypes -Wdeclaration-after-statement") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused -Wfloat-equal -Wpointer-arith -Wwrite-strings -Wformat-security") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-format-attribute") + + # with -fPIC + check_c_compiler_flag("-fPIC" WITH_FPIC) + if (WITH_FPIC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + endif (WITH_FPIC) + + check_c_compiler_flag_ssp("-fstack-protector" WITH_STACK_PROTECTOR) + if (WITH_STACK_PROTECTOR) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector") + endif (WITH_STACK_PROTECTOR) + + string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) + if (NOT CMAKE_BUILD_TYPE_LOWER MATCHES debug) + check_c_compiler_flag("-D_FORTIFY_SOURCE=2" WITH_FORTIFY_SOURCE) + if (WITH_FORTIFY_SOURCE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2") + endif (WITH_FORTIFY_SOURCE) + endif() + endif (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)") + + # + # Check for large filesystem support + # + if (CMAKE_SIZEOF_VOID_P MATCHES "8") + # with large file support + execute_process( + COMMAND + getconf LFS64_CFLAGS + OUTPUT_VARIABLE + _lfs_CFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + else (CMAKE_SIZEOF_VOID_P MATCHES "8") + # with large file support + execute_process( + COMMAND + getconf LFS_CFLAGS + OUTPUT_VARIABLE + _lfs_CFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif (CMAKE_SIZEOF_VOID_P MATCHES "8") + if (_lfs_CFLAGS) + string(REGEX REPLACE "[\r\n]" " " "${_lfs_CFLAGS}" "${${_lfs_CFLAGS}}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_lfs_CFLAGS}") + endif (_lfs_CFLAGS) + +endif (UNIX AND NOT WIN32) + +if (MSVC) + # Use secure functions by defaualt and suppress warnings about + #"deprecated" functions + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") +endif (MSVC) diff --git a/cmake/Modules/DefineInstallationPaths.cmake b/cmake/Modules/DefineInstallationPaths.cmake new file mode 100644 index 0000000..d857871 --- /dev/null +++ b/cmake/Modules/DefineInstallationPaths.cmake @@ -0,0 +1,104 @@ +if (WIN32) + # Same same + set(BIN_INSTALL_DIR "bin" CACHE PATH "-") + set(SBIN_INSTALL_DIR "." CACHE PATH "-") + set(LIB_INSTALL_DIR "lib" CACHE PATH "-") + set(INCLUDE_INSTALL_DIR "include" CACHE PATH "-") + set(PLUGIN_INSTALL_DIR "plugins" CACHE PATH "-") + set(HTML_INSTALL_DIR "doc/HTML" CACHE PATH "-") + set(ICON_INSTALL_DIR "." CACHE PATH "-") + set(SOUND_INSTALL_DIR "." CACHE PATH "-") + set(LOCALE_INSTALL_DIR "lang" CACHE PATH "-") +elseif (UNIX OR OS2) + IF (NOT APPLICATION_NAME) + MESSAGE(STATUS "${PROJECT_NAME} is used as APPLICATION_NAME") + SET(APPLICATION_NAME ${PROJECT_NAME}) + ENDIF (NOT APPLICATION_NAME) + + # Suffix for Linux + SET(LIB_SUFFIX + CACHE STRING "Define suffix of directory name (32/64)" + ) + + SET(EXEC_INSTALL_PREFIX + "${CMAKE_INSTALL_PREFIX}" + CACHE PATH "Base directory for executables and libraries" + ) + SET(SHARE_INSTALL_PREFIX + "${CMAKE_INSTALL_PREFIX}/share" + CACHE PATH "Base directory for files which go to share/" + ) + SET(DATA_INSTALL_PREFIX + "${SHARE_INSTALL_PREFIX}/${APPLICATION_NAME}" + CACHE PATH "The parent directory where applications can install their data") + + # The following are directories where stuff will be installed to + SET(BIN_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/bin" + CACHE PATH "The ${APPLICATION_NAME} binary install dir (default prefix/bin)" + ) + SET(SBIN_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/sbin" + CACHE PATH "The ${APPLICATION_NAME} sbin install dir (default prefix/sbin)" + ) + SET(LIB_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" + CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is prefix/lib)" + ) + SET(LIBEXEC_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/libexec" + CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is prefix/libexec)" + ) + SET(PLUGIN_INSTALL_DIR + "${LIB_INSTALL_DIR}/${APPLICATION_NAME}" + CACHE PATH "The subdirectory relative to the install prefix where plugins will be installed (default is prefix/lib/${APPLICATION_NAME})" + ) + SET(INCLUDE_INSTALL_DIR + "${CMAKE_INSTALL_PREFIX}/include" + CACHE PATH "The subdirectory to the header prefix (default prefix/include)" + ) + + SET(DATA_INSTALL_DIR + "${DATA_INSTALL_PREFIX}" + CACHE PATH "The parent directory where applications can install their data (default prefix/share/${APPLICATION_NAME})" + ) + SET(HTML_INSTALL_DIR + "${DATA_INSTALL_PREFIX}/doc/HTML" + CACHE PATH "The HTML install dir for documentation (default data/doc/html)" + ) + SET(ICON_INSTALL_DIR + "${DATA_INSTALL_PREFIX}/icons" + CACHE PATH "The icon install dir (default data/icons/)" + ) + SET(SOUND_INSTALL_DIR + "${DATA_INSTALL_PREFIX}/sounds" + CACHE PATH "The install dir for sound files (default data/sounds)" + ) + + SET(LOCALE_INSTALL_DIR + "${SHARE_INSTALL_PREFIX}/locale" + CACHE PATH "The install dir for translations (default prefix/share/locale)" + ) + + SET(XDG_APPS_DIR + "${SHARE_INSTALL_PREFIX}/applications/" + CACHE PATH "The XDG apps dir" + ) + SET(XDG_DIRECTORY_DIR + "${SHARE_INSTALL_PREFIX}/desktop-directories" + CACHE PATH "The XDG directory" + ) + + SET(SYSCONF_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/etc" + CACHE PATH "The ${APPLICATION_NAME} sysconfig install dir (default prefix/etc)" + ) + SET(MAN_INSTALL_DIR + "${SHARE_INSTALL_PREFIX}/man" + CACHE PATH "The ${APPLICATION_NAME} man install dir (default prefix/man)" + ) + SET(INFO_INSTALL_DIR + "${SHARE_INSTALL_PREFIX}/info" + CACHE PATH "The ${APPLICATION_NAME} info install dir (default prefix/info)" + ) +endif () diff --git a/cmake/Modules/DefinePlatformDefaults.cmake b/cmake/Modules/DefinePlatformDefaults.cmake new file mode 100644 index 0000000..502d936 --- /dev/null +++ b/cmake/Modules/DefinePlatformDefaults.cmake @@ -0,0 +1,28 @@ +# Set system vars + +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + set(LINUX TRUE) +endif(CMAKE_SYSTEM_NAME MATCHES "Linux") + +if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + set(FREEBSD TRUE) + set(BSD TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + +if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + set(OPENBSD TRUE) + set(BSD TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + +if (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + set(NETBSD TRUE) + set(BSD TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + +if (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + set(SOLARIS TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + +if (CMAKE_SYSTEM_NAME MATCHES "OS2") + set(OS2 TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "OS2") diff --git a/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake b/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake new file mode 100644 index 0000000..a2e9480 --- /dev/null +++ b/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake @@ -0,0 +1,17 @@ +# - MACRO_ENSURE_OUT_OF_SOURCE_BUILD(<errorMessage>) +# MACRO_ENSURE_OUT_OF_SOURCE_BUILD(<errorMessage>) + +# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org> +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +macro (MACRO_ENSURE_OUT_OF_SOURCE_BUILD _errorMessage) + + string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" _insource) + if (_insource) + message(SEND_ERROR "${_errorMessage}") + message(FATAL_ERROR "Remove the file CMakeCache.txt in ${CMAKE_SOURCE_DIR} first.") + endif (_insource) + +endmacro (MACRO_ENSURE_OUT_OF_SOURCE_BUILD) diff --git a/config.h.cmake b/config.h.cmake new file mode 100644 index 0000000..6c9633d --- /dev/null +++ b/config.h.cmake @@ -0,0 +1,58 @@ +/* Name of package */ +#cmakedefine PACKAGE "${APPLICATION_NAME}" + +/* Version number of package */ +#cmakedefine VERSION "${APPLICATION_VERSION}" + +#cmakedefine LOCALEDIR "${LOCALE_INSTALL_DIR}" +#cmakedefine DATADIR "${DATADIR}" +#cmakedefine LIBDIR "${LIBDIR}" +#cmakedefine PLUGINDIR "${PLUGINDIR}" +#cmakedefine SYSCONFDIR "${SYSCONFDIR}" +#cmakedefine BINARYDIR "${BINARYDIR}" +#cmakedefine SOURCEDIR "${SOURCEDIR}" + +/************************** HEADER FILES *************************/ + +#cmakedefine HAVE_SYS_FILIO_H 1 + + +/*************************** FUNCTIONS ***************************/ + +/* Define to 1 if you have the `getaddrinfo' function. */ +#cmakedefine HAVE_GETADDRINFO 1 + +/*************************** LIBRARIES ***************************/ + +#cmakedefine HAVE_GETTIMEOFDAY_TZ 1 + +/**************************** OPTIONS ****************************/ + +/* Define to 1 if you want to enable ZLIB */ +#cmakedefine WITH_ZLIB 1 + +/* Define to 1 if you want to enable SFTP */ +#cmakedefine WITH_SFTP 1 + +/* Define to 1 if you want to enable SSH1 */ +#cmakedefine WITH_SSH1 1 + +/* Define to 1 if you want to enable server support */ +#cmakedefine WITH_SERVER 1 + +/* Define to 1 if you want to enable debug output for crypto functions */ +#cmakedefine DEBUG_CRYPTO 1 + +/* Define to 1 if you want to enable pcap output support (experimental) */ +#cmakedefine WITH_PCAP 1 + +/* Define to 1 if you want to enable calltrace debug output */ +#cmakedefine DEBUG_CALLTRACE 1 + +#cmakedefine HAVE_IPV6 1 + +/*************************** ENDIAN *****************************/ + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#cmakedefine WORDS_BIGENDIAN 1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..5c4aa29 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,4 @@ +project(libsocket_wrapper C) + +include_directories(${CMAKE_BINARY_DIR}) +add_library(socket_wrapper SHARED socket_wrapper.c) diff --git a/src/py_socket_wrapper.c b/src/py_socket_wrapper.c new file mode 100644 index 0000000..f5742d3 --- /dev/null +++ b/src/py_socket_wrapper.c @@ -0,0 +1,883 @@ +/* + * Copyright (C) Amitay Isaacs 2011 + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + Python interface to socket wrapper library. + + Passes all socket communication over unix domain sockets if the environment + variable SOCKET_WRAPPER_DIR is set. +*/ + +#include <Python.h> +#include <pytalloc.h> +#include "replace/replace.h" +#include "system/network.h" +#include "socket_wrapper.h" + +/* There's no Py_ssize_t in 2.4, apparently */ +#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5 +typedef int Py_ssize_t; +typedef inquiry lenfunc; +typedef intargfunc ssizeargfunc; +#endif + +#ifndef Py_RETURN_NONE +#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#endif + +#ifndef Py_TYPE /* Py_TYPE is only available on Python > 2.6 */ +#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) +#endif + +#ifndef PY_CHECK_TYPE +#define PY_CHECK_TYPE(type, var, fail) \ + if (!PyObject_TypeCheck(var, type)) {\ + PyErr_Format(PyExc_TypeError, __location__ ": Expected type '%s' for '%s' of type '%s'", (type)->tp_name, #var, Py_TYPE(var)->tp_name); \ + fail; \ + } +#endif + +staticforward PyTypeObject PySocket; + +static PyObject *py_socket_error; + +void initsocket_wrapper(void); + +static PyObject *py_socket_addr_to_tuple(struct sockaddr *addr, socklen_t len) +{ + char host[256]; + char service[8]; + int status; + PyObject *pyaddr; + + status = getnameinfo(addr, len, host, 255, service, 7, NI_NUMERICHOST|NI_NUMERICSERV); + if (status < 0) { + PyErr_SetString(py_socket_error, gai_strerror(status)); + return NULL; + } + + pyaddr = PyTuple_New(2); + if (pyaddr == NULL) { + return PyErr_NoMemory(); + } + + PyTuple_SetItem(pyaddr, 0, PyString_FromString(host)); + PyTuple_SetItem(pyaddr, 1, PyInt_FromLong(atoi(service))); + + return pyaddr; +} + +static bool py_socket_tuple_to_addr(PyObject *pyaddr, struct sockaddr *addr, socklen_t *len) +{ + const char *host; + char *service; + in_port_t port; + struct addrinfo *ainfo; + int status; + + if (!PyTuple_Check(pyaddr)) { + PyErr_SetString(PyExc_TypeError, "Expected a tuple"); + return false; + } + + if (!PyArg_ParseTuple(pyaddr, "sH", &host, &port)) { + return false; + } + + service = talloc_asprintf(NULL, "%d", port); + if (service == NULL) { + PyErr_NoMemory(); + return false; + } + + status = getaddrinfo(host, service, NULL, &ainfo); + if (status < 0) { + talloc_free(service); + PyErr_SetString(py_socket_error, gai_strerror(status)); + return false; + } + + talloc_free(service); + + memcpy(addr, ainfo->ai_addr, sizeof(struct sockaddr)); + *len = ainfo->ai_addrlen; + + freeaddrinfo(ainfo); + return true; +} + + +static PyObject *py_socket_accept(pytalloc_Object *self, PyObject *args) +{ + int *sock, *new_sock; + struct sockaddr addr; + socklen_t addrlen; + PyObject *pysocket; + PyObject *pyaddr; + PyObject *pyret; + + sock = pytalloc_get_ptr(self); + + new_sock = talloc_zero(NULL, int); + if (new_sock == NULL) { + return PyErr_NoMemory(); + } + + *new_sock = swrap_accept(*sock, &addr, &addrlen); + if (*new_sock < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + if ((pysocket = pytalloc_steal(&PySocket, new_sock)) == NULL) { + return PyErr_NoMemory(); + } + + pyret = PyTuple_New(2); + if (pyret == NULL) { + Py_DECREF(pysocket); + return PyErr_NoMemory(); + } + + pyaddr = py_socket_addr_to_tuple(&addr, addrlen); + if (pyaddr == NULL) { + Py_DECREF(pysocket); + Py_DECREF(pysocket); + return NULL; + } + + PyTuple_SetItem(pyret, 0, pysocket); + PyTuple_SetItem(pyret, 1, pyaddr); + return pyret; +} + +static PyObject *py_socket_bind(pytalloc_Object *self, PyObject *args) +{ + PyObject *pyaddr; + int *sock; + int status; + struct sockaddr addr; + socklen_t addrlen; + + if (!PyArg_ParseTuple(args, "O:bind", &pyaddr)) { + return NULL; + } + + if (!py_socket_tuple_to_addr(pyaddr, &addr, &addrlen)) { + return NULL; + } + + sock = pytalloc_get_ptr(self); + + status = swrap_bind(*sock, &addr, addrlen); + if (status < 0) { + PyErr_SetString(py_socket_error, "Unable to bind"); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject *py_socket_close(pytalloc_Object *self, PyObject *args) +{ + int *sock; + int status; + + sock = pytalloc_get_ptr(self); + + status = swrap_close(*sock); + if (status < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + Py_RETURN_NONE; +} + +static PyObject *py_socket_connect(pytalloc_Object *self, PyObject *args) +{ + int *sock; + PyObject *pyaddr; + struct sockaddr addr; + socklen_t addrlen; + int status; + + if (!PyArg_ParseTuple(args, "O:connect", &pyaddr)) { + return NULL; + } + + if (!py_socket_tuple_to_addr(pyaddr, &addr, &addrlen)) { + return NULL; + } + + sock = pytalloc_get_ptr(self); + + status = swrap_connect(*sock, &addr, addrlen); + if (status < 0) { + PyErr_SetFromErrno(py_socket_error); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject *py_socket_connect_ex(pytalloc_Object *self, PyObject *args) +{ + int *sock; + PyObject *pyaddr; + struct sockaddr addr; + socklen_t addrlen; + int status; + + if (!PyArg_ParseTuple(args, "O:connect", &pyaddr)) { + return NULL; + } + + if (!py_socket_tuple_to_addr(pyaddr, &addr, &addrlen)) { + return NULL; + } + + sock = pytalloc_get_ptr(self); + + status = swrap_connect(*sock, &addr, addrlen); + if (status < 0) { + return Py_BuildValue("%d", errno); + } + + return Py_BuildValue("%d", 0); +} + +static PyObject *py_socket_dup(pytalloc_Object *self, PyObject *args) +{ + int *sock, *new_sock; + PyObject *pysocket; + + sock = pytalloc_get_ptr(self); + + new_sock = talloc_zero(NULL, int); + if (new_sock == NULL) { + return PyErr_NoMemory(); + } + + *new_sock = swrap_dup(*sock); + if (*new_sock < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + pysocket = pytalloc_steal(&PySocket, new_sock); + if (pysocket == NULL) { + return PyErr_NoMemory(); + } + + return pysocket; +} + +static PyObject *py_socket_dup2(pytalloc_Object *self, PyObject *args) +{ + int *sock, *new_sock; + PyObject *pysocket; + int status; + + if (!PyArg_ParseTuple(args, "O", &pysocket)) { + return NULL; + } + + PY_CHECK_TYPE(&PySocket, pysocket, return NULL); + + sock = pytalloc_get_ptr(self); + new_sock = pytalloc_get_ptr(pysocket); + + status = swrap_dup2(*sock, *new_sock); + if (status < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + Py_RETURN_NONE; +} + +static PyObject *py_socket_fileno(pytalloc_Object *self, PyObject *args) +{ + PyErr_SetString(py_socket_error, "Not Supported"); + return NULL; +} + +static PyObject *py_socket_getpeername(pytalloc_Object *self, PyObject *args) +{ + int *sock; + struct sockaddr addr; + socklen_t addrlen; + int status; + PyObject *pyaddr; + + sock = pytalloc_get_ptr(self); + + status = swrap_getpeername(*sock, &addr, &addrlen); + if (status < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + pyaddr = py_socket_addr_to_tuple(&addr, addrlen); + + return pyaddr; +} + +static PyObject *py_socket_getsockname(pytalloc_Object *self, PyObject *args) +{ + int *sock; + struct sockaddr addr; + socklen_t addrlen; + int status; + PyObject *pyaddr; + + sock = pytalloc_get_ptr(self); + + status = swrap_getsockname(*sock, &addr, &addrlen); + if (status < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + pyaddr = py_socket_addr_to_tuple(&addr, addrlen); + + return pyaddr; +} + +static PyObject *py_socket_getsockopt(pytalloc_Object *self, PyObject *args) +{ + int level, optname; + int *sock; + socklen_t optlen = 0, newlen; + int optval; + bool is_integer = false; + char *buffer; + PyObject *pyret; + int status; + + if (!PyArg_ParseTuple(args, "ii|i:getsockopt", &level, &optname, &optlen)) { + return NULL; + } + + if (optlen == 0) { + optlen = sizeof(int); + is_integer = true; + } + + buffer = talloc_zero_array(NULL, char, optlen); + if (buffer == NULL) { + return PyErr_NoMemory(); + } + + sock = pytalloc_get_ptr(self); + + status = swrap_getsockopt(*sock, level, optname, (void *)buffer, &newlen); + if (status < 0) { + talloc_free(buffer); + return PyErr_SetFromErrno(py_socket_error); + } + + if (is_integer) { + optval = *(int *)buffer; + pyret = PyInt_FromLong(optval); + } else { + pyret = PyString_FromStringAndSize(buffer, optlen); + } + + talloc_free(buffer); + + return pyret; +} + +static PyObject *py_socket_gettimeout(pytalloc_Object *self, PyObject *args) +{ + PyErr_SetString(py_socket_error, "Not Supported"); + return NULL; +} + +static PyObject *py_socket_listen(pytalloc_Object *self, PyObject *args) +{ + int backlog; + int *sock; + int status; + + if (!PyArg_ParseTuple(args, "i:listen", &backlog)) { + return NULL; + } + + sock = pytalloc_get_ptr(self); + + status = swrap_listen(*sock, backlog); + if (status < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + Py_RETURN_NONE; +} + +static PyObject *py_socket_makefile(pytalloc_Object *self, PyObject *args) +{ + PyErr_SetString(py_socket_error, "Not Supported"); + return NULL; +} + +static PyObject *py_socket_read(pytalloc_Object *self, PyObject *args) +{ + int bufsize, len; + int *sock; + char *buffer; + PyObject *pyret; + + if (!PyArg_ParseTuple(args, "i:read", &bufsize)) { + return NULL; + } + + buffer = talloc_zero_array(NULL, char, bufsize); + if (buffer == NULL) { + return PyErr_NoMemory(); + } + + sock = pytalloc_get_ptr(self); + + len = swrap_read(*sock, buffer, bufsize); + if (len < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + pyret = PyString_FromStringAndSize(buffer, len); + + talloc_free(buffer); + + return pyret; +} + +static PyObject *py_socket_recv(pytalloc_Object *self, PyObject *args) +{ + int bufsize, flags, len; + int *sock; + char *buffer; + PyObject *pyret; + + if (!PyArg_ParseTuple(args, "ii:recv", &bufsize, &flags)) { + return NULL; + } + + buffer = talloc_zero_array(NULL, char, bufsize); + if (buffer == NULL) { + return PyErr_NoMemory(); + } + + sock = pytalloc_get_ptr(self); + + len = swrap_recv(*sock, buffer, bufsize, flags); + if (len < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + pyret = PyString_FromStringAndSize(buffer, len); + + talloc_free(buffer); + + return pyret; +} + +static PyObject *py_socket_recvfrom(pytalloc_Object *self, PyObject *args) +{ + int bufsize, flags, len; + int *sock; + char *buffer; + struct sockaddr from; + socklen_t fromlen; + PyObject *pybuf, *pyaddr, *pyret; + + if (!PyArg_ParseTuple(args, "ii:recvfrom", &bufsize, &flags)) { + return NULL; + } + + buffer = talloc_zero_array(NULL, char, bufsize); + if (buffer == NULL) { + return PyErr_NoMemory(); + } + + sock = pytalloc_get_ptr(self); + + fromlen = sizeof(struct sockaddr); + + len = swrap_recvfrom(*sock, buffer, bufsize, flags, &from, &fromlen); + if (len < 0) { + talloc_free(buffer); + return PyErr_SetFromErrno(py_socket_error); + } + + pybuf = PyString_FromStringAndSize(buffer, len); + if (pybuf == NULL) { + talloc_free(buffer); + return PyErr_NoMemory(); + } + + talloc_free(buffer); + + pyaddr = py_socket_addr_to_tuple(&from, fromlen); + if (pyaddr == NULL) { + Py_DECREF(pybuf); + return NULL; + } + + pyret = PyTuple_New(2); + if (pyret == NULL) { + Py_DECREF(pybuf); + Py_DECREF(pyaddr); + return PyErr_NoMemory(); + } + + PyTuple_SetItem(pyret, 0, pybuf); + PyTuple_SetItem(pyret, 1, pyaddr); + + return pyret; +} + +static PyObject *py_socket_send(pytalloc_Object *self, PyObject *args) +{ + char *buffer; + int len, flags; + int *sock; + int status; + + if (!PyArg_ParseTuple(args, "s#i:sendto", &buffer, &len, &flags)) { + return NULL; + } + + sock = pytalloc_get_ptr(self); + + status = swrap_send(*sock, buffer, len, flags); + if (status < 0) { + PyErr_SetFromErrno(py_socket_error); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject *py_socket_sendall(pytalloc_Object *self, PyObject *args) +{ + char *buffer; + int len, flags; + int *sock; + int status; + + if (!PyArg_ParseTuple(args, "s#i:sendall", &buffer, &len, &flags)) { + return NULL; + } + + sock = pytalloc_get_ptr(self); + + status = swrap_send(*sock, buffer, len, flags); + if (status < 0) { + PyErr_SetFromErrno(py_socket_error); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject *py_socket_sendto(pytalloc_Object *self, PyObject *args) +{ + PyObject *pyaddr; + char *buffer; + int len, flags; + int *sock; + struct sockaddr addr; + socklen_t addrlen; + int status; + + if (!PyArg_ParseTuple(args, "s#iO:sendto", &buffer, &len, &flags, &pyaddr)) { + return NULL; + } + + if (!py_socket_tuple_to_addr(pyaddr, &addr, &addrlen)) { + return NULL; + } + + sock = pytalloc_get_ptr(self); + + status = swrap_sendto(*sock, buffer, len, flags, &addr, addrlen); + if (status < 0) { + PyErr_SetFromErrno(py_socket_error); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject *py_socket_setblocking(pytalloc_Object *self, PyObject *args) +{ + PyErr_SetString(py_socket_error, "Not Supported"); + return NULL; +} + +static PyObject *py_socket_setsockopt(pytalloc_Object *self, PyObject *args) +{ + int level, optname; + int *sock; + PyObject *pyval; + int optval; + Py_ssize_t optlen; + char *buffer; + int status; + + if (!PyArg_ParseTuple(args, "iiO:getsockopt", &level, &optname, &pyval)) { + return NULL; + } + + if (PyInt_Check(pyval)) { + optval = PyInt_AsLong(pyval); + buffer = (char *)&optval; + optlen = sizeof(int); + } else { + PyString_AsStringAndSize(pyval, &buffer, &optlen); + } + + sock = pytalloc_get_ptr(self); + + status = swrap_setsockopt(*sock, level, optname, (void *)buffer, optlen); + if (status < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + Py_RETURN_NONE; +} + +static PyObject *py_socket_settimeout(pytalloc_Object *self, PyObject *args) +{ + PyErr_SetString(py_socket_error, "Not Supported"); + return NULL; +} + +static PyObject *py_socket_shutdown(pytalloc_Object *self, PyObject *args) +{ + PyErr_SetString(py_socket_error, "Not Supported"); + return NULL; +} + +static PyObject *py_socket_write(pytalloc_Object *self, PyObject *args) +{ + char *buffer; + int len; + int *sock; + int status; + + if (!PyArg_ParseTuple(args, "s#:write", &buffer, &len)) { + return NULL; + } + + sock = pytalloc_get_ptr(self); + + status = swrap_send(*sock, buffer, len, 0); + if (status < 0) { + PyErr_SetFromErrno(py_socket_error); + return NULL; + } + + Py_RETURN_NONE; +} + + +static PyMethodDef py_socket_methods[] = { + { "accept", (PyCFunction)py_socket_accept, METH_NOARGS, + "accept() -> (socket object, address info)\n\n \ + Wait for an incoming connection." }, + { "bind", (PyCFunction)py_socket_bind, METH_VARARGS, + "bind(address)\n\n \ + Bind the socket to a local address." }, + { "close", (PyCFunction)py_socket_close, METH_NOARGS, + "close()\n\n \ + Close the socket." }, + { "connect", (PyCFunction)py_socket_connect, METH_VARARGS, + "connect(address)\n\n \ + Connect the socket to a remote address." }, + { "connect_ex", (PyCFunction)py_socket_connect_ex, METH_VARARGS, + "connect_ex(address)\n\n \ + Connect the socket to a remote address." }, + { "dup", (PyCFunction)py_socket_dup, METH_VARARGS, + "dup() -> socket object\n\n \ + Return a new socket object connected to the same system resource." }, + { "dup2", (PyCFunction)py_socket_dup2, METH_VARARGS, + "dup2(socket object) -> socket object\n\n \ + Return a new socket object connected to teh same system resource." }, + { "fileno", (PyCFunction)py_socket_fileno, METH_NOARGS, + "fileno() -> file descriptor\n\n \ + Return socket's file descriptor." }, + { "getpeername", (PyCFunction)py_socket_getpeername, METH_NOARGS, + "getpeername() -> address info\n\n \ + Return the address of the remote endpoint." }, + { "getsockname", (PyCFunction)py_socket_getsockname, METH_NOARGS, + "getsockname() -> address info\n\n \ + Return the address of the local endpoing." }, + { "getsockopt", (PyCFunction)py_socket_getsockopt, METH_VARARGS, + "getsockopt(level, option[, buffersize]) -> value\n\n \ + Get a socket option." }, + { "gettimeout", (PyCFunction)py_socket_gettimeout, METH_NOARGS, + "gettimeout() -> value\n\n \ + Return the timeout in seconds associated with socket operations." }, + { "listen", (PyCFunction)py_socket_listen, METH_VARARGS, + "listen(backlog)\n\n \ + Enable a server to accept connections." }, + { "makefile", (PyCFunction)py_socket_makefile, METH_NOARGS, + "makefile() -> file object\n\n \ + Return a file object associated with the socket." }, + { "read", (PyCFunction)py_socket_read, METH_VARARGS, + "read(buflen) -> data\n\n \ + Receive data." }, + { "recv", (PyCFunction)py_socket_recv, METH_VARARGS, + "recv(buflen, flags) -> data\n\n \ + Receive data." }, + { "recvfrom", (PyCFunction)py_socket_recvfrom, METH_VARARGS, + "recvfrom(buflen, flags) -> (data, sender address)\n\n \ + Receive data and sender's address." }, + { "send", (PyCFunction)py_socket_send, METH_VARARGS, + "send(data, flags)\n\n \ + Send data." }, + { "sendall", (PyCFunction)py_socket_sendall, METH_VARARGS, + "sendall(data, flags)\n\n \ + Send data." }, + { "sendto", (PyCFunction)py_socket_sendto, METH_VARARGS, + "sendto(data, flags, addr)\n\n \ + Send data to a given address." }, + { "setblocking", (PyCFunction)py_socket_setblocking, METH_VARARGS, + "setblocking(flag)\n\n \ + Set blocking or non-blocking mode of the socket." }, + { "setsockopt", (PyCFunction)py_socket_setsockopt, METH_VARARGS, + "setsockopt(level, option, value)\n\n \ + Set a socket option." }, + { "settimeout", (PyCFunction)py_socket_settimeout, METH_VARARGS, + "settimeout(value)\n\n \ + Set a timeout on socket blocking operations." }, + { "shutdown", (PyCFunction)py_socket_shutdown, METH_VARARGS, + "shutdown(how)\n\n \ + Shut down one or both halves of the connection." }, + { "write", (PyCFunction)py_socket_write, METH_VARARGS, + "write(data)\n\n \ + Send data." }, + { NULL }, +}; + + +static PyObject *py_socket_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + int family, sock_type, protocol; + int *sock; + PyObject *pysocket; + + if (!PyArg_ParseTuple(args, "iii:socket", &family, &sock_type, &protocol)) { + return NULL; + } + + sock = talloc_zero(NULL, int); + if (sock == NULL) { + return PyErr_NoMemory(); + } + + *sock = swrap_socket(family, sock_type, protocol); + if (*sock < 0) { + return PyErr_SetFromErrno(py_socket_error); + } + + if ((pysocket = pytalloc_steal(type, sock)) == NULL) { + return PyErr_NoMemory(); + } + + return pysocket; +} + + +static PyTypeObject PySocket = { + .tp_name = "socket_wrapper.socket", + .tp_basicsize = sizeof(pytalloc_Object), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_methods = py_socket_methods, + .tp_new = py_socket_new, + .tp_doc = "socket(family, type, proto) -> socket object\n\n Open a socket of the give type.", +}; + +static PyObject *py_socket_wrapper_dir(PyObject *self) +{ + const char *dir; + + dir = socket_wrapper_dir(); + + return PyString_FromString(dir); +} + +static PyObject *py_socket_wrapper_default_interface(PyObject *self) +{ + unsigned int id; + + id = socket_wrapper_default_iface(); + + return PyInt_FromLong(id); +} + + +static PyMethodDef py_socket_wrapper_methods[] = { + { "dir", (PyCFunction)py_socket_wrapper_dir, METH_NOARGS, + "dir() -> path\n\n \ + Return socket_wrapper directory." }, + { "default_iface", (PyCFunction)py_socket_wrapper_default_interface, METH_NOARGS, + "default_iface() -> id\n\n \ + Return default interface id." }, + { NULL }, +}; + +void initsocket_wrapper(void) +{ + PyObject *m; + char exception_name[] = "socket_wrapper.error"; + + PyTypeObject *talloc_type = pytalloc_GetObjectType(); + if (talloc_type == NULL) { + return; + } + + PySocket.tp_base = talloc_type; + if (PyType_Ready(&PySocket) < 0) { + return; + } + + m = Py_InitModule3("socket_wrapper", py_socket_wrapper_methods, "Socket wrapper"); + if (m == NULL) { + return; + } + + py_socket_error = PyErr_NewException(exception_name, NULL, NULL); + Py_INCREF(py_socket_error); + PyModule_AddObject(m, "error", py_socket_error); + + Py_INCREF(&PySocket); + PyModule_AddObject(m, "socket", (PyObject *)&PySocket); +} diff --git a/src/socket.py b/src/socket.py new file mode 100644 index 0000000..ccbb820 --- /dev/null +++ b/src/socket.py @@ -0,0 +1,50 @@ +# Wrapper for socket wrapper (based on python socket wrapper) +# Copyright (C) Amitay Isaacs 2011 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import _socket +from _socket import * + +from samba.socket_wrapper import socket + + +def getfqdn(name=''): + """Get fully qualified domain name from name. + + An empty argument is interpreted as meaning the local host. + + First the hostname returned by gethostbyaddr() is checked, then + possibly existing aliases. In case no FQDN is available, hostname + from gethostname() is returned. + """ + name = name.strip() + if not name or name == '0.0.0.0': + name = gethostname() + try: + hostname, aliases, ipaddrs = gethostbyaddr(name) + except error: + pass + else: + aliases.insert(0, hostname) + for name in aliases: + if '.' in name: + break + else: + name = hostname + return name + + +_GLOBAL_DEFAULT_TIMEOUT = object() diff --git a/src/socket_wrapper.c b/src/socket_wrapper.c new file mode 100644 index 0000000..7cf09a8 --- /dev/null +++ b/src/socket_wrapper.c @@ -0,0 +1,2666 @@ +/* + * Copyright (C) Jelmer Vernooij 2005,2008 <jelmer@samba.org> + * Copyright (C) Stefan Metzmacher 2006-2009 <metze@samba.org> + * Copyright (C) Andreas Schneider 2013 <asn@samba.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + Socket wrapper library. Passes all socket communication over + unix domain sockets if the environment variable SOCKET_WRAPPER_DIR + is set. +*/ + +#include "config.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#ifdef HAVE_SYS_FILIO_H +#include <sys/filio.h> +#endif +#include <errno.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdint.h> + +#ifndef _PUBLIC_ +#define _PUBLIC_ +#endif + +#define SWRAP_DLIST_ADD(list,item) do { \ + if (!(list)) { \ + (item)->prev = NULL; \ + (item)->next = NULL; \ + (list) = (item); \ + } else { \ + (item)->prev = NULL; \ + (item)->next = (list); \ + (list)->prev = (item); \ + (list) = (item); \ + } \ +} while (0) + +#define SWRAP_DLIST_REMOVE(list,item) do { \ + if ((list) == (item)) { \ + (list) = (item)->next; \ + if (list) { \ + (list)->prev = NULL; \ + } \ + } else { \ + if ((item)->prev) { \ + (item)->prev->next = (item)->next; \ + } \ + if ((item)->next) { \ + (item)->next->prev = (item)->prev; \ + } \ + } \ + (item)->prev = NULL; \ + (item)->next = NULL; \ +} while (0) + +#ifdef HAVE_GETTIMEOFDAY_TZ +#define swrapGetTimeOfDay(tval) gettimeofday(tval,NULL) +#else +#define swrapGetTimeOfDay(tval) gettimeofday(tval) +#endif + +/* we need to use a very terse format here as IRIX 6.4 silently + truncates names to 16 chars, so if we use a longer name then we + can't tell which port a packet came from with recvfrom() + + with this format we have 8 chars left for the directory name +*/ +#define SOCKET_FORMAT "%c%02X%04X" +#define SOCKET_TYPE_CHAR_TCP 'T' +#define SOCKET_TYPE_CHAR_UDP 'U' +#define SOCKET_TYPE_CHAR_TCP_V6 'X' +#define SOCKET_TYPE_CHAR_UDP_V6 'Y' + +/* This limit is to avoid broadcast sendto() needing to stat too many + * files. It may be raised (with a performance cost) to up to 254 + * without changing the format above */ +#define MAX_WRAPPED_INTERFACES 40 + +#include <dlfcn.h> + +#define LIBC_NAME "libc.so.6" + +static void *libc_hnd; + +static int libc_dlopen(void) +{ + if (libc_hnd != NULL) { + return 0; + } + libc_hnd = dlopen(LIBC_NAME, RTLD_LAZY); + if (libc_hnd == NULL) { + printf("Failed to dlopen %s: %s\n", LIBC_NAME, dlerror()); + exit(-1); + } + + return 0; +} + +static void *libc_dlsym(const char *name) +{ + void *func; + + libc_dlopen(); + + func = dlsym(libc_hnd, name); + + if (func == NULL) { + printf("Failed to find %s in %s: %s\n", + name, LIBC_NAME, dlerror()); + exit(-1); + } + + return func; +} + +static int (*libc_accept)(int sockfd, struct sockaddr *addr, socklen_t *addrlen); + +static int real_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + if (libc_accept == NULL) { + *(void **)(&libc_accept) = libc_dlsym("accept"); + } + + return libc_accept(sockfd, addr, addrlen); +} + +#ifdef HAVE_IPV6 +/* + * FD00::5357:5FXX + */ +static const struct in6_addr *swrap_ipv6(void) +{ + static struct in6_addr v; + static int initialized; + int ret; + + if (initialized) { + return &v; + } + initialized = 1; + + ret = inet_pton(AF_INET6, "FD00::5357:5F00", &v); + if (ret <= 0) { + abort(); + } + + return &v; +} +#endif + +static struct sockaddr *sockaddr_dup(const void *data, socklen_t len) +{ + struct sockaddr *ret = (struct sockaddr *)malloc(len); + memcpy(ret, data, len); + return ret; +} + +static void set_port(int family, int prt, struct sockaddr *addr) +{ + switch (family) { + case AF_INET: + ((struct sockaddr_in *)addr)->sin_port = htons(prt); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + ((struct sockaddr_in6 *)addr)->sin6_port = htons(prt); + break; +#endif + } +} + +static size_t socket_length(int family) +{ + switch (family) { + case AF_INET: + return sizeof(struct sockaddr_in); +#ifdef HAVE_IPV6 + case AF_INET6: + return sizeof(struct sockaddr_in6); +#endif + } + return 0; +} + +struct socket_info_fd { + struct socket_info_fd *prev, *next; + int fd; +}; + +struct socket_info +{ + struct socket_info_fd *fds; + + int family; + int type; + int protocol; + int bound; + int bcast; + int is_server; + int connected; + int defer_connect; + + char *tmp_path; + + struct sockaddr *myname; + socklen_t myname_len; + + struct sockaddr *peername; + socklen_t peername_len; + + struct { + unsigned long pck_snd; + unsigned long pck_rcv; + } io; + + struct socket_info *prev, *next; +}; + +static struct socket_info *sockets; + +const char *socket_wrapper_dir(void) +{ + const char *s = getenv("SOCKET_WRAPPER_DIR"); + if (s == NULL) { + return NULL; + } + if (strncmp(s, "./", 2) == 0) { + s += 2; + } + return s; +} + +unsigned int socket_wrapper_default_iface(void) +{ + const char *s = getenv("SOCKET_WRAPPER_DEFAULT_IFACE"); + if (s) { + unsigned int iface; + if (sscanf(s, "%u", &iface) == 1) { + if (iface >= 1 && iface <= MAX_WRAPPED_INTERFACES) { + return iface; + } + } + } + + return 1;/* 127.0.0.1 */ +} + +static int convert_un_in(const struct sockaddr_un *un, struct sockaddr *in, socklen_t *len) +{ + unsigned int iface; + unsigned int prt; + const char *p; + char type; + + p = strrchr(un->sun_path, '/'); + if (p) p++; else p = un->sun_path; + + if (sscanf(p, SOCKET_FORMAT, &type, &iface, &prt) != 3) { + errno = EINVAL; + return -1; + } + + if (iface == 0 || iface > MAX_WRAPPED_INTERFACES) { + errno = EINVAL; + return -1; + } + + if (prt > 0xFFFF) { + errno = EINVAL; + return -1; + } + + switch(type) { + case SOCKET_TYPE_CHAR_TCP: + case SOCKET_TYPE_CHAR_UDP: { + struct sockaddr_in *in2 = (struct sockaddr_in *)(void *)in; + + if ((*len) < sizeof(*in2)) { + errno = EINVAL; + return -1; + } + + memset(in2, 0, sizeof(*in2)); + in2->sin_family = AF_INET; + in2->sin_addr.s_addr = htonl((127<<24) | iface); + in2->sin_port = htons(prt); + + *len = sizeof(*in2); + break; + } +#ifdef HAVE_IPV6 + case SOCKET_TYPE_CHAR_TCP_V6: + case SOCKET_TYPE_CHAR_UDP_V6: { + struct sockaddr_in6 *in2 = (struct sockaddr_in6 *)(void *)in; + + if ((*len) < sizeof(*in2)) { + errno = EINVAL; + return -1; + } + + memset(in2, 0, sizeof(*in2)); + in2->sin6_family = AF_INET6; + in2->sin6_addr = *swrap_ipv6(); + in2->sin6_addr.s6_addr[15] = iface; + in2->sin6_port = htons(prt); + + *len = sizeof(*in2); + break; + } +#endif + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *inaddr, struct sockaddr_un *un, + int *bcast) +{ + char type = '\0'; + unsigned int prt; + unsigned int iface; + int is_bcast = 0; + + if (bcast) *bcast = 0; + + switch (inaddr->sa_family) { + case AF_INET: { + const struct sockaddr_in *in = + (const struct sockaddr_in *)(const void *)inaddr; + unsigned int addr = ntohl(in->sin_addr.s_addr); + char u_type = '\0'; + char b_type = '\0'; + char a_type = '\0'; + + switch (si->type) { + case SOCK_STREAM: + u_type = SOCKET_TYPE_CHAR_TCP; + break; + case SOCK_DGRAM: + u_type = SOCKET_TYPE_CHAR_UDP; + a_type = SOCKET_TYPE_CHAR_UDP; + b_type = SOCKET_TYPE_CHAR_UDP; + break; + } + + prt = ntohs(in->sin_port); + if (a_type && addr == 0xFFFFFFFF) { + /* 255.255.255.255 only udp */ + is_bcast = 2; + type = a_type; + iface = socket_wrapper_default_iface(); + } else if (b_type && addr == 0x7FFFFFFF) { + /* 127.255.255.255 only udp */ + is_bcast = 1; + type = b_type; + iface = socket_wrapper_default_iface(); + } else if ((addr & 0xFFFFFF00) == 0x7F000000) { + /* 127.0.0.X */ + is_bcast = 0; + type = u_type; + iface = (addr & 0x000000FF); + } else { + errno = ENETUNREACH; + return -1; + } + if (bcast) *bcast = is_bcast; + break; + } +#ifdef HAVE_IPV6 + case AF_INET6: { + const struct sockaddr_in6 *in = + (const struct sockaddr_in6 *)(const void *)inaddr; + struct in6_addr cmp1, cmp2; + + switch (si->type) { + case SOCK_STREAM: + type = SOCKET_TYPE_CHAR_TCP_V6; + break; + case SOCK_DGRAM: + type = SOCKET_TYPE_CHAR_UDP_V6; + break; + } + + /* XXX no multicast/broadcast */ + + prt = ntohs(in->sin6_port); + + cmp1 = *swrap_ipv6(); + cmp2 = in->sin6_addr; + cmp2.s6_addr[15] = 0; + if (IN6_ARE_ADDR_EQUAL(&cmp1, &cmp2)) { + iface = in->sin6_addr.s6_addr[15]; + } else { + errno = ENETUNREACH; + return -1; + } + + break; + } +#endif + default: + errno = ENETUNREACH; + return -1; + } + + if (prt == 0) { + errno = EINVAL; + return -1; + } + + if (is_bcast) { + snprintf(un->sun_path, sizeof(un->sun_path), "%s/EINVAL", + socket_wrapper_dir()); + /* the caller need to do more processing */ + return 0; + } + + snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, + socket_wrapper_dir(), type, iface, prt); + + return 0; +} + +static int convert_in_un_alloc(struct socket_info *si, const struct sockaddr *inaddr, struct sockaddr_un *un, + int *bcast) +{ + char type = '\0'; + unsigned int prt; + unsigned int iface; + struct stat st; + int is_bcast = 0; + + if (bcast) *bcast = 0; + + switch (si->family) { + case AF_INET: { + const struct sockaddr_in *in = + (const struct sockaddr_in *)(const void *)inaddr; + unsigned int addr = ntohl(in->sin_addr.s_addr); + char u_type = '\0'; + char d_type = '\0'; + char b_type = '\0'; + char a_type = '\0'; + + prt = ntohs(in->sin_port); + + switch (si->type) { + case SOCK_STREAM: + u_type = SOCKET_TYPE_CHAR_TCP; + d_type = SOCKET_TYPE_CHAR_TCP; + break; + case SOCK_DGRAM: + u_type = SOCKET_TYPE_CHAR_UDP; + d_type = SOCKET_TYPE_CHAR_UDP; + a_type = SOCKET_TYPE_CHAR_UDP; + b_type = SOCKET_TYPE_CHAR_UDP; + break; + } + + if (addr == 0) { + /* 0.0.0.0 */ + is_bcast = 0; + type = d_type; + iface = socket_wrapper_default_iface(); + } else if (a_type && addr == 0xFFFFFFFF) { + /* 255.255.255.255 only udp */ + is_bcast = 2; + type = a_type; + iface = socket_wrapper_default_iface(); + } else if (b_type && addr == 0x7FFFFFFF) { + /* 127.255.255.255 only udp */ + is_bcast = 1; + type = b_type; + iface = socket_wrapper_default_iface(); + } else if ((addr & 0xFFFFFF00) == 0x7F000000) { + /* 127.0.0.X */ + is_bcast = 0; + type = u_type; + iface = (addr & 0x000000FF); + } else { + errno = EADDRNOTAVAIL; + return -1; + } + break; + } +#ifdef HAVE_IPV6 + case AF_INET6: { + const struct sockaddr_in6 *in = + (const struct sockaddr_in6 *)(const void *)inaddr; + struct in6_addr cmp1, cmp2; + + switch (si->type) { + case SOCK_STREAM: + type = SOCKET_TYPE_CHAR_TCP_V6; + break; + case SOCK_DGRAM: + type = SOCKET_TYPE_CHAR_UDP_V6; + break; + } + + /* XXX no multicast/broadcast */ + + prt = ntohs(in->sin6_port); + + cmp1 = *swrap_ipv6(); + cmp2 = in->sin6_addr; + cmp2.s6_addr[15] = 0; + if (IN6_IS_ADDR_UNSPECIFIED(&in->sin6_addr)) { + iface = socket_wrapper_default_iface(); + } else if (IN6_ARE_ADDR_EQUAL(&cmp1, &cmp2)) { + iface = in->sin6_addr.s6_addr[15]; + } else { + errno = EADDRNOTAVAIL; + return -1; + } + + break; + } +#endif + default: + errno = EADDRNOTAVAIL; + return -1; + } + + + if (bcast) *bcast = is_bcast; + + if (iface == 0 || iface > MAX_WRAPPED_INTERFACES) { + errno = EINVAL; + return -1; + } + + if (prt == 0) { + /* handle auto-allocation of ephemeral ports */ + for (prt = 5001; prt < 10000; prt++) { + snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, + socket_wrapper_dir(), type, iface, prt); + if (stat(un->sun_path, &st) == 0) continue; + + set_port(si->family, prt, si->myname); + break; + } + if (prt == 10000) { + errno = ENFILE; + return -1; + } + } + + snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, + socket_wrapper_dir(), type, iface, prt); + return 0; +} + +static struct socket_info *find_socket_info(int fd) +{ + struct socket_info *i; + for (i = sockets; i; i = i->next) { + struct socket_info_fd *f; + for (f = i->fds; f; f = f->next) { + if (f->fd == fd) { + return i; + } + } + } + + return NULL; +} + +static int sockaddr_convert_to_un(struct socket_info *si, const struct sockaddr *in_addr, socklen_t in_len, + struct sockaddr_un *out_addr, int alloc_sock, int *bcast) +{ + struct sockaddr *out = (struct sockaddr *)(void *)out_addr; + if (!out_addr) + return 0; + + out->sa_family = AF_UNIX; +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + out->sa_len = sizeof(*out_addr); +#endif + + switch (in_addr->sa_family) { + case AF_INET: +#ifdef HAVE_IPV6 + case AF_INET6: +#endif + switch (si->type) { + case SOCK_STREAM: + case SOCK_DGRAM: + break; + default: + errno = ESOCKTNOSUPPORT; + return -1; + } + if (alloc_sock) { + return convert_in_un_alloc(si, in_addr, out_addr, bcast); + } else { + return convert_in_un_remote(si, in_addr, out_addr, bcast); + } + default: + break; + } + + errno = EAFNOSUPPORT; + return -1; +} + +static int sockaddr_convert_from_un(const struct socket_info *si, + const struct sockaddr_un *in_addr, + socklen_t un_addrlen, + int family, + struct sockaddr *out_addr, + socklen_t *out_addrlen) +{ + int ret; + + if (out_addr == NULL || out_addrlen == NULL) + return 0; + + if (un_addrlen == 0) { + *out_addrlen = 0; + return 0; + } + + switch (family) { + case AF_INET: +#ifdef HAVE_IPV6 + case AF_INET6: +#endif + switch (si->type) { + case SOCK_STREAM: + case SOCK_DGRAM: + break; + default: + errno = ESOCKTNOSUPPORT; + return -1; + } + ret = convert_un_in(in_addr, out_addr, out_addrlen); +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + out_addr->sa_len = *out_addrlen; +#endif + return ret; + default: + break; + } + + errno = EAFNOSUPPORT; + return -1; +} + +enum swrap_packet_type { + SWRAP_CONNECT_SEND, + SWRAP_CONNECT_UNREACH, + SWRAP_CONNECT_RECV, + SWRAP_CONNECT_ACK, + SWRAP_ACCEPT_SEND, + SWRAP_ACCEPT_RECV, + SWRAP_ACCEPT_ACK, + SWRAP_RECVFROM, + SWRAP_SENDTO, + SWRAP_SENDTO_UNREACH, + SWRAP_PENDING_RST, + SWRAP_RECV, + SWRAP_RECV_RST, + SWRAP_SEND, + SWRAP_SEND_RST, + SWRAP_CLOSE_SEND, + SWRAP_CLOSE_RECV, + SWRAP_CLOSE_ACK, +}; + +struct swrap_file_hdr { + uint32_t magic; + uint16_t version_major; + uint16_t version_minor; + int32_t timezone; + uint32_t sigfigs; + uint32_t frame_max_len; +#define SWRAP_FRAME_LENGTH_MAX 0xFFFF + uint32_t link_type; +}; +#define SWRAP_FILE_HDR_SIZE 24 + +struct swrap_packet_frame { + uint32_t seconds; + uint32_t micro_seconds; + uint32_t recorded_length; + uint32_t full_length; +}; +#define SWRAP_PACKET_FRAME_SIZE 16 + +union swrap_packet_ip { + struct { + uint8_t ver_hdrlen; + uint8_t tos; + uint16_t packet_length; + uint16_t identification; + uint8_t flags; + uint8_t fragment; + uint8_t ttl; + uint8_t protocol; + uint16_t hdr_checksum; + uint32_t src_addr; + uint32_t dest_addr; + } v4; +#define SWRAP_PACKET_IP_V4_SIZE 20 + struct { + uint8_t ver_prio; + uint8_t flow_label_high; + uint16_t flow_label_low; + uint16_t payload_length; + uint8_t next_header; + uint8_t hop_limit; + uint8_t src_addr[16]; + uint8_t dest_addr[16]; + } v6; +#define SWRAP_PACKET_IP_V6_SIZE 40 +}; +#define SWRAP_PACKET_IP_SIZE 40 + +union swrap_packet_payload { + struct { + uint16_t source_port; + uint16_t dest_port; + uint32_t seq_num; + uint32_t ack_num; + uint8_t hdr_length; + uint8_t control; + uint16_t window; + uint16_t checksum; + uint16_t urg; + } tcp; +#define SWRAP_PACKET_PAYLOAD_TCP_SIZE 20 + struct { + uint16_t source_port; + uint16_t dest_port; + uint16_t length; + uint16_t checksum; + } udp; +#define SWRAP_PACKET_PAYLOAD_UDP_SIZE 8 + struct { + uint8_t type; + uint8_t code; + uint16_t checksum; + uint32_t unused; + } icmp4; +#define SWRAP_PACKET_PAYLOAD_ICMP4_SIZE 8 + struct { + uint8_t type; + uint8_t code; + uint16_t checksum; + uint32_t unused; + } icmp6; +#define SWRAP_PACKET_PAYLOAD_ICMP6_SIZE 8 +}; +#define SWRAP_PACKET_PAYLOAD_SIZE 20 + +#define SWRAP_PACKET_MIN_ALLOC \ + (SWRAP_PACKET_FRAME_SIZE + \ + SWRAP_PACKET_IP_SIZE + \ + SWRAP_PACKET_PAYLOAD_SIZE) + +static const char *socket_wrapper_pcap_file(void) +{ + static int initialized = 0; + static const char *s = NULL; + static const struct swrap_file_hdr h; + static const struct swrap_packet_frame f; + static const union swrap_packet_ip i; + static const union swrap_packet_payload p; + + if (initialized == 1) { + return s; + } + initialized = 1; + + /* + * TODO: don't use the structs use plain buffer offsets + * and PUSH_U8(), PUSH_U16() and PUSH_U32() + * + * for now make sure we disable PCAP support + * if the struct has alignment! + */ + if (sizeof(h) != SWRAP_FILE_HDR_SIZE) { + return NULL; + } + if (sizeof(f) != SWRAP_PACKET_FRAME_SIZE) { + return NULL; + } + if (sizeof(i) != SWRAP_PACKET_IP_SIZE) { + return NULL; + } + if (sizeof(i.v4) != SWRAP_PACKET_IP_V4_SIZE) { + return NULL; + } + if (sizeof(i.v6) != SWRAP_PACKET_IP_V6_SIZE) { + return NULL; + } + if (sizeof(p) != SWRAP_PACKET_PAYLOAD_SIZE) { + return NULL; + } + if (sizeof(p.tcp) != SWRAP_PACKET_PAYLOAD_TCP_SIZE) { + return NULL; + } + if (sizeof(p.udp) != SWRAP_PACKET_PAYLOAD_UDP_SIZE) { + return NULL; + } + if (sizeof(p.icmp4) != SWRAP_PACKET_PAYLOAD_ICMP4_SIZE) { + return NULL; + } + if (sizeof(p.icmp6) != SWRAP_PACKET_PAYLOAD_ICMP6_SIZE) { + return NULL; + } + + s = getenv("SOCKET_WRAPPER_PCAP_FILE"); + if (s == NULL) { + return NULL; + } + if (strncmp(s, "./", 2) == 0) { + s += 2; + } + return s; +} + +static uint8_t *swrap_packet_init(struct timeval *tval, + const struct sockaddr *src, + const struct sockaddr *dest, + int socket_type, + const uint8_t *payload, + size_t payload_len, + unsigned long tcp_seqno, + unsigned long tcp_ack, + unsigned char tcp_ctl, + int unreachable, + size_t *_packet_len) +{ + uint8_t *base; + uint8_t *buf; + struct swrap_packet_frame *frame; + union swrap_packet_ip *ip; + union swrap_packet_payload *pay; + size_t packet_len; + size_t alloc_len; + size_t nonwire_len = sizeof(*frame); + size_t wire_hdr_len = 0; + size_t wire_len = 0; + size_t ip_hdr_len = 0; + size_t icmp_hdr_len = 0; + size_t icmp_truncate_len = 0; + uint8_t protocol = 0, icmp_protocol = 0; + const struct sockaddr_in *src_in = NULL; + const struct sockaddr_in *dest_in = NULL; +#ifdef HAVE_IPV6 + const struct sockaddr_in6 *src_in6 = NULL; + const struct sockaddr_in6 *dest_in6 = NULL; +#endif + uint16_t src_port; + uint16_t dest_port; + + switch (src->sa_family) { + case AF_INET: + src_in = (const struct sockaddr_in *)src; + dest_in = (const struct sockaddr_in *)dest; + src_port = src_in->sin_port; + dest_port = dest_in->sin_port; + ip_hdr_len = sizeof(ip->v4); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + src_in6 = (const struct sockaddr_in6 *)src; + dest_in6 = (const struct sockaddr_in6 *)dest; + src_port = src_in6->sin6_port; + dest_port = dest_in6->sin6_port; + ip_hdr_len = sizeof(ip->v6); + break; +#endif + default: + return NULL; + } + + switch (socket_type) { + case SOCK_STREAM: + protocol = 0x06; /* TCP */ + wire_hdr_len = ip_hdr_len + sizeof(pay->tcp); + wire_len = wire_hdr_len + payload_len; + break; + + case SOCK_DGRAM: + protocol = 0x11; /* UDP */ + wire_hdr_len = ip_hdr_len + sizeof(pay->udp); + wire_len = wire_hdr_len + payload_len; + break; + + default: + return NULL; + } + + if (unreachable) { + icmp_protocol = protocol; + switch (src->sa_family) { + case AF_INET: + protocol = 0x01; /* ICMPv4 */ + icmp_hdr_len = ip_hdr_len + sizeof(pay->icmp4); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + protocol = 0x3A; /* ICMPv6 */ + icmp_hdr_len = ip_hdr_len + sizeof(pay->icmp6); + break; +#endif + } + if (wire_len > 64 ) { + icmp_truncate_len = wire_len - 64; + } + wire_hdr_len += icmp_hdr_len; + wire_len += icmp_hdr_len; + } + + packet_len = nonwire_len + wire_len; + alloc_len = packet_len; + if (alloc_len < SWRAP_PACKET_MIN_ALLOC) { + alloc_len = SWRAP_PACKET_MIN_ALLOC; + } + + base = (uint8_t *)malloc(alloc_len); + if (!base) return NULL; + + buf = base; + + frame = (struct swrap_packet_frame *)buf; + frame->seconds = tval->tv_sec; + frame->micro_seconds = tval->tv_usec; + frame->recorded_length = wire_len - icmp_truncate_len; + frame->full_length = wire_len - icmp_truncate_len; + buf += SWRAP_PACKET_FRAME_SIZE; + + ip = (union swrap_packet_ip *)buf; + switch (src->sa_family) { + case AF_INET: + ip->v4.ver_hdrlen = 0x45; /* version 4 and 5 * 32 bit words */ + ip->v4.tos = 0x00; + ip->v4.packet_length = htons(wire_len - icmp_truncate_len); + ip->v4.identification = htons(0xFFFF); + ip->v4.flags = 0x40; /* BIT 1 set - means don't fraqment */ + ip->v4.fragment = htons(0x0000); + ip->v4.ttl = 0xFF; + ip->v4.protocol = protocol; + ip->v4.hdr_checksum = htons(0x0000); + ip->v4.src_addr = src_in->sin_addr.s_addr; + ip->v4.dest_addr = dest_in->sin_addr.s_addr; + buf += SWRAP_PACKET_IP_V4_SIZE; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + ip->v6.ver_prio = 0x60; /* version 4 and 5 * 32 bit words */ + ip->v6.flow_label_high = 0x00; + ip->v6.flow_label_low = 0x0000; + ip->v6.payload_length = htons(wire_len - icmp_truncate_len); /* TODO */ + ip->v6.next_header = protocol; + memcpy(ip->v6.src_addr, src_in6->sin6_addr.s6_addr, 16); + memcpy(ip->v6.dest_addr, dest_in6->sin6_addr.s6_addr, 16); + buf += SWRAP_PACKET_IP_V6_SIZE; + break; +#endif + } + + if (unreachable) { + pay = (union swrap_packet_payload *)buf; + switch (src->sa_family) { + case AF_INET: + pay->icmp4.type = 0x03; /* destination unreachable */ + pay->icmp4.code = 0x01; /* host unreachable */ + pay->icmp4.checksum = htons(0x0000); + pay->icmp4.unused = htonl(0x00000000); + buf += SWRAP_PACKET_PAYLOAD_ICMP4_SIZE; + + /* set the ip header in the ICMP payload */ + ip = (union swrap_packet_ip *)buf; + ip->v4.ver_hdrlen = 0x45; /* version 4 and 5 * 32 bit words */ + ip->v4.tos = 0x00; + ip->v4.packet_length = htons(wire_len - icmp_hdr_len); + ip->v4.identification = htons(0xFFFF); + ip->v4.flags = 0x40; /* BIT 1 set - means don't fraqment */ + ip->v4.fragment = htons(0x0000); + ip->v4.ttl = 0xFF; + ip->v4.protocol = icmp_protocol; + ip->v4.hdr_checksum = htons(0x0000); + ip->v4.src_addr = dest_in->sin_addr.s_addr; + ip->v4.dest_addr = src_in->sin_addr.s_addr; + buf += SWRAP_PACKET_IP_V4_SIZE; + + src_port = dest_in->sin_port; + dest_port = src_in->sin_port; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + pay->icmp6.type = 0x01; /* destination unreachable */ + pay->icmp6.code = 0x03; /* address unreachable */ + pay->icmp6.checksum = htons(0x0000); + pay->icmp6.unused = htonl(0x00000000); + buf += SWRAP_PACKET_PAYLOAD_ICMP6_SIZE; + + /* set the ip header in the ICMP payload */ + ip = (union swrap_packet_ip *)buf; + ip->v6.ver_prio = 0x60; /* version 4 and 5 * 32 bit words */ + ip->v6.flow_label_high = 0x00; + ip->v6.flow_label_low = 0x0000; + ip->v6.payload_length = htons(wire_len - icmp_truncate_len); /* TODO */ + ip->v6.next_header = protocol; + memcpy(ip->v6.src_addr, dest_in6->sin6_addr.s6_addr, 16); + memcpy(ip->v6.dest_addr, src_in6->sin6_addr.s6_addr, 16); + buf += SWRAP_PACKET_IP_V6_SIZE; + + src_port = dest_in6->sin6_port; + dest_port = src_in6->sin6_port; + break; +#endif + } + } + + pay = (union swrap_packet_payload *)buf; + + switch (socket_type) { + case SOCK_STREAM: + pay->tcp.source_port = src_port; + pay->tcp.dest_port = dest_port; + pay->tcp.seq_num = htonl(tcp_seqno); + pay->tcp.ack_num = htonl(tcp_ack); + pay->tcp.hdr_length = 0x50; /* 5 * 32 bit words */ + pay->tcp.control = tcp_ctl; + pay->tcp.window = htons(0x7FFF); + pay->tcp.checksum = htons(0x0000); + pay->tcp.urg = htons(0x0000); + buf += SWRAP_PACKET_PAYLOAD_TCP_SIZE; + + break; + + case SOCK_DGRAM: + pay->udp.source_port = src_port; + pay->udp.dest_port = dest_port; + pay->udp.length = htons(8 + payload_len); + pay->udp.checksum = htons(0x0000); + buf += SWRAP_PACKET_PAYLOAD_UDP_SIZE; + + break; + } + + if (payload && payload_len > 0) { + memcpy(buf, payload, payload_len); + } + + *_packet_len = packet_len - icmp_truncate_len; + return base; +} + +static int swrap_get_pcap_fd(const char *fname) +{ + static int fd = -1; + + if (fd != -1) return fd; + + fd = open(fname, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0644); + if (fd != -1) { + struct swrap_file_hdr file_hdr; + file_hdr.magic = 0xA1B2C3D4; + file_hdr.version_major = 0x0002; + file_hdr.version_minor = 0x0004; + file_hdr.timezone = 0x00000000; + file_hdr.sigfigs = 0x00000000; + file_hdr.frame_max_len = SWRAP_FRAME_LENGTH_MAX; + file_hdr.link_type = 0x0065; /* 101 RAW IP */ + + if (write(fd, &file_hdr, sizeof(file_hdr)) != sizeof(file_hdr)) { + close(fd); + fd = -1; + } + return fd; + } + + fd = open(fname, O_WRONLY|O_APPEND, 0644); + + return fd; +} + +static uint8_t *swrap_marshall_packet(struct socket_info *si, + const struct sockaddr *addr, + enum swrap_packet_type type, + const void *buf, size_t len, + size_t *packet_len) +{ + const struct sockaddr *src_addr; + const struct sockaddr *dest_addr; + unsigned long tcp_seqno = 0; + unsigned long tcp_ack = 0; + unsigned char tcp_ctl = 0; + int unreachable = 0; + + struct timeval tv; + + switch (si->family) { + case AF_INET: + break; +#ifdef HAVE_IPV6 + case AF_INET6: + break; +#endif + default: + return NULL; + } + + switch (type) { + case SWRAP_CONNECT_SEND: + if (si->type != SOCK_STREAM) return NULL; + + src_addr = si->myname; + dest_addr = addr; + + tcp_seqno = si->io.pck_snd; + tcp_ack = si->io.pck_rcv; + tcp_ctl = 0x02; /* SYN */ + + si->io.pck_snd += 1; + + break; + + case SWRAP_CONNECT_RECV: + if (si->type != SOCK_STREAM) return NULL; + + dest_addr = si->myname; + src_addr = addr; + + tcp_seqno = si->io.pck_rcv; + tcp_ack = si->io.pck_snd; + tcp_ctl = 0x12; /** SYN,ACK */ + + si->io.pck_rcv += 1; + + break; + + case SWRAP_CONNECT_UNREACH: + if (si->type != SOCK_STREAM) return NULL; + + dest_addr = si->myname; + src_addr = addr; + + /* Unreachable: resend the data of SWRAP_CONNECT_SEND */ + tcp_seqno = si->io.pck_snd - 1; + tcp_ack = si->io.pck_rcv; + tcp_ctl = 0x02; /* SYN */ + unreachable = 1; + + break; + + case SWRAP_CONNECT_ACK: + if (si->type != SOCK_STREAM) return NULL; + + src_addr = si->myname; + dest_addr = addr; + + tcp_seqno = si->io.pck_snd; + tcp_ack = si->io.pck_rcv; + tcp_ctl = 0x10; /* ACK */ + + break; + + case SWRAP_ACCEPT_SEND: + if (si->type != SOCK_STREAM) return NULL; + + dest_addr = si->myname; + src_addr = addr; + + tcp_seqno = si->io.pck_rcv; + tcp_ack = si->io.pck_snd; + tcp_ctl = 0x02; /* SYN */ + + si->io.pck_rcv += 1; + + break; + + case SWRAP_ACCEPT_RECV: + if (si->type != SOCK_STREAM) return NULL; + + src_addr = si->myname; + dest_addr = addr; + + tcp_seqno = si->io.pck_snd; + tcp_ack = si->io.pck_rcv; + tcp_ctl = 0x12; /* SYN,ACK */ + + si->io.pck_snd += 1; + + break; + + case SWRAP_ACCEPT_ACK: + if (si->type != SOCK_STREAM) return NULL; + + dest_addr = si->myname; + src_addr = addr; + + tcp_seqno = si->io.pck_rcv; + tcp_ack = si->io.pck_snd; + tcp_ctl = 0x10; /* ACK */ + + break; + + case SWRAP_SEND: + src_addr = si->myname; + dest_addr = si->peername; + + tcp_seqno = si->io.pck_snd; + tcp_ack = si->io.pck_rcv; + tcp_ctl = 0x18; /* PSH,ACK */ + + si->io.pck_snd += len; + + break; + + case SWRAP_SEND_RST: + dest_addr = si->myname; + src_addr = si->peername; + + if (si->type == SOCK_DGRAM) { + return swrap_marshall_packet(si, si->peername, + SWRAP_SENDTO_UNREACH, + buf, len, packet_len); + } + + tcp_seqno = si->io.pck_rcv; + tcp_ack = si->io.pck_snd; + tcp_ctl = 0x14; /** RST,ACK */ + + break; + + case SWRAP_PENDING_RST: + dest_addr = si->myname; + src_addr = si->peername; + + if (si->type == SOCK_DGRAM) { + return NULL; + } + + tcp_seqno = si->io.pck_rcv; + tcp_ack = si->io.pck_snd; + tcp_ctl = 0x14; /* RST,ACK */ + + break; + + case SWRAP_RECV: + dest_addr = si->myname; + src_addr = si->peername; + + tcp_seqno = si->io.pck_rcv; + tcp_ack = si->io.pck_snd; + tcp_ctl = 0x18; /* PSH,ACK */ + + si->io.pck_rcv += len; + + break; + + case SWRAP_RECV_RST: + dest_addr = si->myname; + src_addr = si->peername; + + if (si->type == SOCK_DGRAM) { + return NULL; + } + + tcp_seqno = si->io.pck_rcv; + tcp_ack = si->io.pck_snd; + tcp_ctl = 0x14; /* RST,ACK */ + + break; + + case SWRAP_SENDTO: + src_addr = si->myname; + dest_addr = addr; + + si->io.pck_snd += len; + + break; + + case SWRAP_SENDTO_UNREACH: + dest_addr = si->myname; + src_addr = addr; + + unreachable = 1; + + break; + + case SWRAP_RECVFROM: + dest_addr = si->myname; + src_addr = addr; + + si->io.pck_rcv += len; + + break; + + case SWRAP_CLOSE_SEND: + if (si->type != SOCK_STREAM) return NULL; + + src_addr = si->myname; + dest_addr = si->peername; + + tcp_seqno = si->io.pck_snd; + tcp_ack = si->io.pck_rcv; + tcp_ctl = 0x11; /* FIN, ACK */ + + si->io.pck_snd += 1; + + break; + + case SWRAP_CLOSE_RECV: + if (si->type != SOCK_STREAM) return NULL; + + dest_addr = si->myname; + src_addr = si->peername; + + tcp_seqno = si->io.pck_rcv; + tcp_ack = si->io.pck_snd; + tcp_ctl = 0x11; /* FIN,ACK */ + + si->io.pck_rcv += 1; + + break; + + case SWRAP_CLOSE_ACK: + if (si->type != SOCK_STREAM) return NULL; + + src_addr = si->myname; + dest_addr = si->peername; + + tcp_seqno = si->io.pck_snd; + tcp_ack = si->io.pck_rcv; + tcp_ctl = 0x10; /* ACK */ + + break; + default: + return NULL; + } + + swrapGetTimeOfDay(&tv); + + return swrap_packet_init(&tv, src_addr, dest_addr, si->type, + (const uint8_t *)buf, len, + tcp_seqno, tcp_ack, tcp_ctl, unreachable, + packet_len); +} + +static void swrap_dump_packet(struct socket_info *si, + const struct sockaddr *addr, + enum swrap_packet_type type, + const void *buf, size_t len) +{ + const char *file_name; + uint8_t *packet; + size_t packet_len = 0; + int fd; + + file_name = socket_wrapper_pcap_file(); + if (!file_name) { + return; + } + + packet = swrap_marshall_packet(si, addr, type, buf, len, &packet_len); + if (!packet) { + return; + } + + fd = swrap_get_pcap_fd(file_name); + if (fd != -1) { + if (write(fd, packet, packet_len) != packet_len) { + free(packet); + return; + } + } + + free(packet); +} + +_PUBLIC_ int swrap_socket(int family, int type, int protocol) +{ + struct socket_info *si; + struct socket_info_fd *fi; + int fd; + int real_type = type; +#ifdef SOCK_CLOEXEC + real_type &= ~SOCK_CLOEXEC; +#endif +#ifdef SOCK_NONBLOCK + real_type &= ~SOCK_NONBLOCK; +#endif + + if (!socket_wrapper_dir()) { + return real_socket(family, type, protocol); + } + + switch (family) { + case AF_INET: +#ifdef HAVE_IPV6 + case AF_INET6: +#endif + break; + case AF_UNIX: + return real_socket(family, type, protocol); + default: + errno = EAFNOSUPPORT; + return -1; + } + + switch (real_type) { + case SOCK_STREAM: + break; + case SOCK_DGRAM: + break; + default: + errno = EPROTONOSUPPORT; + return -1; + } + + switch (protocol) { + case 0: + break; + case 6: + if (real_type == SOCK_STREAM) { + break; + } + /*fall through*/ + case 17: + if (real_type == SOCK_DGRAM) { + break; + } + /*fall through*/ + default: + errno = EPROTONOSUPPORT; + return -1; + } + + /* We must call real_socket with type, from the caller, not the version we removed + SOCK_CLOEXEC and SOCK_NONBLOCK from */ + fd = real_socket(AF_UNIX, type, 0); + + if (fd == -1) return -1; + + si = (struct socket_info *)calloc(1, sizeof(struct socket_info)); + if (si == NULL) { + errno = ENOMEM; + return -1; + } + + si->family = family; + + /* however, the rest of the socket_wrapper code expects just + * the type, not the flags */ + si->type = real_type; + si->protocol = protocol; + + fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd)); + if (fi == NULL) { + free(si); + errno = ENOMEM; + return -1; + } + + fi->fd = fd; + + SWRAP_DLIST_ADD(si->fds, fi); + SWRAP_DLIST_ADD(sockets, si); + + return fd; +} + +_PUBLIC_ int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + struct socket_info *parent_si, *child_si; + struct socket_info_fd *child_fi; + int fd; + struct sockaddr_un un_addr; + socklen_t un_addrlen = sizeof(un_addr); + struct sockaddr_un un_my_addr; + socklen_t un_my_addrlen = sizeof(un_my_addr); + struct sockaddr *my_addr; + socklen_t my_addrlen, len; + int ret; + + parent_si = find_socket_info(s); + if (!parent_si) { + return real_accept(s, addr, addrlen); + } + + /* + * assume out sockaddr have the same size as the in parent + * socket family + */ + my_addrlen = socket_length(parent_si->family); + if (my_addrlen <= 0) { + errno = EINVAL; + return -1; + } + + my_addr = (struct sockaddr *)malloc(my_addrlen); + if (my_addr == NULL) { + return -1; + } + + memset(&un_addr, 0, sizeof(un_addr)); + memset(&un_my_addr, 0, sizeof(un_my_addr)); + + ret = real_accept(s, (struct sockaddr *)(void *)&un_addr, &un_addrlen); + if (ret == -1) { + free(my_addr); + return ret; + } + + fd = ret; + + len = my_addrlen; + ret = sockaddr_convert_from_un(parent_si, &un_addr, un_addrlen, + parent_si->family, my_addr, &len); + if (ret == -1) { + free(my_addr); + close(fd); + return ret; + } + + child_si = (struct socket_info *)malloc(sizeof(struct socket_info)); + memset(child_si, 0, sizeof(*child_si)); + + child_fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd)); + if (child_fi == NULL) { + free(child_si); + free(my_addr); + close(fd); + errno = ENOMEM; + return -1; + } + + child_fi->fd = fd; + + SWRAP_DLIST_ADD(child_si->fds, child_fi); + + child_si->family = parent_si->family; + child_si->type = parent_si->type; + child_si->protocol = parent_si->protocol; + child_si->bound = 1; + child_si->is_server = 1; + child_si->connected = 1; + + child_si->peername_len = len; + child_si->peername = sockaddr_dup(my_addr, len); + + if (addr != NULL && addrlen != NULL) { + size_t copy_len = MIN(*addrlen, len); + if (copy_len > 0) { + memcpy(addr, my_addr, copy_len); + } + *addrlen = len; + } + + ret = real_getsockname(fd, (struct sockaddr *)(void *)&un_my_addr, + &un_my_addrlen); + if (ret == -1) { + free(child_fi); + free(child_si); + close(fd); + return ret; + } + + len = my_addrlen; + ret = sockaddr_convert_from_un(child_si, &un_my_addr, un_my_addrlen, + child_si->family, my_addr, &len); + if (ret == -1) { + free(child_fi); + free(child_si); + free(my_addr); + close(fd); + return ret; + } + + child_si->myname_len = len; + child_si->myname = sockaddr_dup(my_addr, len); + free(my_addr); + + SWRAP_DLIST_ADD(sockets, child_si); + + swrap_dump_packet(child_si, addr, SWRAP_ACCEPT_SEND, NULL, 0); + swrap_dump_packet(child_si, addr, SWRAP_ACCEPT_RECV, NULL, 0); + swrap_dump_packet(child_si, addr, SWRAP_ACCEPT_ACK, NULL, 0); + + return fd; +} + +static int autobind_start_init; +static int autobind_start; + +/* using sendto() or connect() on an unbound socket would give the + recipient no way to reply, as unlike UDP and TCP, a unix domain + socket can't auto-assign emphemeral port numbers, so we need to + assign it here. + Note: this might change the family from ipv6 to ipv4 +*/ +static int swrap_auto_bind(int fd, struct socket_info *si, int family) +{ + struct sockaddr_un un_addr; + int i; + char type; + int ret; + int port; + struct stat st; + + if (autobind_start_init != 1) { + autobind_start_init = 1; + autobind_start = getpid(); + autobind_start %= 50000; + autobind_start += 10000; + } + + un_addr.sun_family = AF_UNIX; + + switch (family) { + case AF_INET: { + struct sockaddr_in in; + + switch (si->type) { + case SOCK_STREAM: + type = SOCKET_TYPE_CHAR_TCP; + break; + case SOCK_DGRAM: + type = SOCKET_TYPE_CHAR_UDP; + break; + default: + errno = ESOCKTNOSUPPORT; + return -1; + } + + memset(&in, 0, sizeof(in)); + in.sin_family = AF_INET; + in.sin_addr.s_addr = htonl(127<<24 | + socket_wrapper_default_iface()); + + si->myname_len = sizeof(in); + si->myname = sockaddr_dup(&in, si->myname_len); + break; + } +#ifdef HAVE_IPV6 + case AF_INET6: { + struct sockaddr_in6 in6; + + if (si->family != family) { + errno = ENETUNREACH; + return -1; + } + + switch (si->type) { + case SOCK_STREAM: + type = SOCKET_TYPE_CHAR_TCP_V6; + break; + case SOCK_DGRAM: + type = SOCKET_TYPE_CHAR_UDP_V6; + break; + default: + errno = ESOCKTNOSUPPORT; + return -1; + } + + memset(&in6, 0, sizeof(in6)); + in6.sin6_family = AF_INET6; + in6.sin6_addr = *swrap_ipv6(); + in6.sin6_addr.s6_addr[15] = socket_wrapper_default_iface(); + si->myname_len = sizeof(in6); + si->myname = sockaddr_dup(&in6, si->myname_len); + break; + } +#endif + default: + errno = ESOCKTNOSUPPORT; + return -1; + } + + if (autobind_start > 60000) { + autobind_start = 10000; + } + + for (i=0;i<1000;i++) { + port = autobind_start + i; + snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), + "%s/"SOCKET_FORMAT, socket_wrapper_dir(), + type, socket_wrapper_default_iface(), port); + if (stat(un_addr.sun_path, &st) == 0) continue; + + ret = real_bind(fd, (struct sockaddr *)(void *)&un_addr, + sizeof(un_addr)); + if (ret == -1) return ret; + + si->tmp_path = strdup(un_addr.sun_path); + si->bound = 1; + autobind_start = port + 1; + break; + } + if (i == 1000) { + errno = ENFILE; + return -1; + } + + si->family = family; + set_port(si->family, port, si->myname); + + return 0; +} + + +_PUBLIC_ int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen) +{ + int ret; + struct sockaddr_un un_addr; + struct socket_info *si = find_socket_info(s); + int bcast = 0; + + if (!si) { + return real_connect(s, serv_addr, addrlen); + } + + if (si->bound == 0) { + ret = swrap_auto_bind(s, si, serv_addr->sa_family); + if (ret == -1) return -1; + } + + if (si->family != serv_addr->sa_family) { + errno = EINVAL; + return -1; + } + + ret = sockaddr_convert_to_un(si, serv_addr, + addrlen, &un_addr, 0, &bcast); + if (ret == -1) return -1; + + if (bcast) { + errno = ENETUNREACH; + return -1; + } + + if (si->type == SOCK_DGRAM) { + si->defer_connect = 1; + ret = 0; + } else { + swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_SEND, NULL, 0); + + ret = real_connect(s, (struct sockaddr *)(void *)&un_addr, + sizeof(struct sockaddr_un)); + } + + /* to give better errors */ + if (ret == -1 && errno == ENOENT) { + errno = EHOSTUNREACH; + } + + if (ret == 0) { + si->peername_len = addrlen; + si->peername = sockaddr_dup(serv_addr, addrlen); + si->connected = 1; + + swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_RECV, NULL, 0); + swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_ACK, NULL, 0); + } else { + swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_UNREACH, NULL, 0); + } + + return ret; +} + +_PUBLIC_ int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen) +{ + int ret; + struct sockaddr_un un_addr; + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_bind(s, myaddr, addrlen); + } + + si->myname_len = addrlen; + si->myname = sockaddr_dup(myaddr, addrlen); + + ret = sockaddr_convert_to_un(si, myaddr, addrlen, &un_addr, 1, &si->bcast); + if (ret == -1) return -1; + + unlink(un_addr.sun_path); + + ret = real_bind(s, (struct sockaddr *)(void *)&un_addr, + sizeof(struct sockaddr_un)); + + if (ret == 0) { + si->bound = 1; + } + + return ret; +} + +_PUBLIC_ int swrap_listen(int s, int backlog) +{ + int ret; + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_listen(s, backlog); + } + + ret = real_listen(s, backlog); + + return ret; +} + +_PUBLIC_ int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen) +{ + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_getpeername(s, name, addrlen); + } + + if (!si->peername) + { + errno = ENOTCONN; + return -1; + } + + memcpy(name, si->peername, si->peername_len); + *addrlen = si->peername_len; + + return 0; +} + +_PUBLIC_ int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen) +{ + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_getsockname(s, name, addrlen); + } + + memcpy(name, si->myname, si->myname_len); + *addrlen = si->myname_len; + + return 0; +} + +_PUBLIC_ int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_getsockopt(s, level, optname, optval, optlen); + } + + if (level == SOL_SOCKET) { + return real_getsockopt(s, level, optname, optval, optlen); + } + + errno = ENOPROTOOPT; + return -1; +} + +_PUBLIC_ int swrap_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_setsockopt(s, level, optname, optval, optlen); + } + + if (level == SOL_SOCKET) { + return real_setsockopt(s, level, optname, optval, optlen); + } + + switch (si->family) { + case AF_INET: + return 0; +#ifdef HAVE_IPV6 + case AF_INET6: + return 0; +#endif + default: + errno = ENOPROTOOPT; + return -1; + } +} + +_PUBLIC_ int swrap_ioctl(int s, int r, void *p) +{ + int ret; + struct socket_info *si = find_socket_info(s); + int value; + + if (!si) { + return real_ioctl(s, r, p); + } + + ret = real_ioctl(s, r, p); + + switch (r) { + case FIONREAD: + value = *((int *)p); + if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) { + swrap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0); + } else if (value == 0) { /* END OF FILE */ + swrap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0); + } + break; + } + + return ret; +} + +static ssize_t swrap_sendmsg_before(int fd, + struct socket_info *si, + struct msghdr *msg, + struct iovec *tmp_iov, + struct sockaddr_un *tmp_un, + const struct sockaddr_un **to_un, + const struct sockaddr **to, + int *bcast) +{ + size_t i, len = 0; + ssize_t ret; + + if (to_un) { + *to_un = NULL; + } + if (to) { + *to = NULL; + } + if (bcast) { + *bcast = 0; + } + + switch (si->type) { + case SOCK_STREAM: + if (!si->connected) { + errno = ENOTCONN; + return -1; + } + + if (msg->msg_iovlen == 0) { + break; + } + + /* + * cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) + */ + + for (i=0; i < msg->msg_iovlen; i++) { + size_t nlen; + nlen = len + msg->msg_iov[i].iov_len; + if (nlen > 1500) { + break; + } + } + msg->msg_iovlen = i; + if (msg->msg_iovlen == 0) { + *tmp_iov = msg->msg_iov[0]; + tmp_iov->iov_len = MIN(tmp_iov->iov_len, 1500); + msg->msg_iov = tmp_iov; + msg->msg_iovlen = 1; + } + break; + + case SOCK_DGRAM: + if (si->connected) { + if (msg->msg_name) { + errno = EISCONN; + return -1; + } + } else { + const struct sockaddr *msg_name; + msg_name = (const struct sockaddr *)msg->msg_name; + + if (msg_name == NULL) { + errno = ENOTCONN; + return -1; + } + + + ret = sockaddr_convert_to_un(si, msg_name, msg->msg_namelen, + tmp_un, 0, bcast); + if (ret == -1) return -1; + + if (to_un) { + *to_un = tmp_un; + } + if (to) { + *to = msg_name; + } + msg->msg_name = tmp_un; + msg->msg_namelen = sizeof(*tmp_un); + } + + if (si->bound == 0) { + ret = swrap_auto_bind(fd, si, si->family); + if (ret == -1) return -1; + } + + if (!si->defer_connect) { + break; + } + + ret = sockaddr_convert_to_un(si, si->peername, si->peername_len, + tmp_un, 0, NULL); + if (ret == -1) return -1; + + ret = real_connect(fd, (struct sockaddr *)(void *)tmp_un, + sizeof(*tmp_un)); + + /* to give better errors */ + if (ret == -1 && errno == ENOENT) { + errno = EHOSTUNREACH; + } + + if (ret == -1) { + return ret; + } + + si->defer_connect = 0; + break; + default: + errno = EHOSTUNREACH; + return -1; + } + + return 0; +} + +static void swrap_sendmsg_after(struct socket_info *si, + struct msghdr *msg, + const struct sockaddr *to, + ssize_t ret) +{ + int saved_errno = errno; + size_t i, len = 0; + uint8_t *buf; + off_t ofs = 0; + size_t avail = 0; + size_t remain; + + /* to give better errors */ + if (ret == -1 && saved_errno == ENOENT) { + saved_errno = EHOSTUNREACH; + } + + for (i=0; i < msg->msg_iovlen; i++) { + avail += msg->msg_iov[i].iov_len; + } + + if (ret == -1) { + remain = MIN(80, avail); + } else { + remain = ret; + } + + /* we capture it as one single packet */ + buf = (uint8_t *)malloc(remain); + if (!buf) { + /* we just not capture the packet */ + errno = saved_errno; + return; + } + + for (i=0; i < msg->msg_iovlen; i++) { + size_t this_time = MIN(remain, msg->msg_iov[i].iov_len); + memcpy(buf + ofs, + msg->msg_iov[i].iov_base, + this_time); + ofs += this_time; + remain -= this_time; + } + len = ofs; + + switch (si->type) { + case SOCK_STREAM: + if (ret == -1) { + swrap_dump_packet(si, NULL, SWRAP_SEND, buf, len); + swrap_dump_packet(si, NULL, SWRAP_SEND_RST, NULL, 0); + } else { + swrap_dump_packet(si, NULL, SWRAP_SEND, buf, len); + } + break; + + case SOCK_DGRAM: + if (si->connected) { + to = si->peername; + } + if (ret == -1) { + swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len); + swrap_dump_packet(si, to, SWRAP_SENDTO_UNREACH, buf, len); + } else { + swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len); + } + break; + } + + free(buf); + errno = saved_errno; +} + +_PUBLIC_ ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) +{ + struct sockaddr_un un_addr; + socklen_t un_addrlen = sizeof(un_addr); + int ret; + struct socket_info *si = find_socket_info(s); + struct sockaddr_storage ss; + socklen_t ss_len = sizeof(ss); + + if (!si) { + return real_recvfrom(s, buf, len, flags, from, fromlen); + } + + if (!from) { + from = (struct sockaddr *)(void *)&ss; + fromlen = &ss_len; + } + + if (si->type == SOCK_STREAM) { + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ + len = MIN(len, 1500); + } + + /* irix 6.4 forgets to null terminate the sun_path string :-( */ + memset(&un_addr, 0, sizeof(un_addr)); + ret = real_recvfrom(s, buf, len, flags, + (struct sockaddr *)(void *)&un_addr, &un_addrlen); + if (ret == -1) + return ret; + + if (sockaddr_convert_from_un(si, &un_addr, un_addrlen, + si->family, from, fromlen) == -1) { + return -1; + } + + swrap_dump_packet(si, from, SWRAP_RECVFROM, buf, ret); + + return ret; +} + + +_PUBLIC_ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) +{ + struct msghdr msg; + struct iovec tmp; + struct sockaddr_un un_addr; + const struct sockaddr_un *to_un = NULL; + ssize_t ret; + struct socket_info *si = find_socket_info(s); + int bcast = 0; + + if (!si) { + return real_sendto(s, buf, len, flags, to, tolen); + } + + tmp.iov_base = discard_const_p(char, buf); + tmp.iov_len = len; + + ZERO_STRUCT(msg); + msg.msg_name = discard_const_p(struct sockaddr, to); /* optional address */ + msg.msg_namelen = tolen; /* size of address */ + msg.msg_iov = &tmp; /* scatter/gather array */ + msg.msg_iovlen = 1; /* # elements in msg_iov */ +#if 0 /* not available on solaris */ + msg.msg_control = NULL; /* ancillary data, see below */ + msg.msg_controllen = 0; /* ancillary data buffer len */ + msg.msg_flags = 0; /* flags on received message */ +#endif + + ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, &to_un, &to, &bcast); + if (ret == -1) return -1; + + buf = msg.msg_iov[0].iov_base; + len = msg.msg_iov[0].iov_len; + + if (bcast) { + struct stat st; + unsigned int iface; + unsigned int prt = ntohs(((const struct sockaddr_in *)to)->sin_port); + char type; + + type = SOCKET_TYPE_CHAR_UDP; + + for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) { + snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT, + socket_wrapper_dir(), type, iface, prt); + if (stat(un_addr.sun_path, &st) != 0) continue; + + /* ignore the any errors in broadcast sends */ + real_sendto(s, buf, len, flags, + (struct sockaddr *)(void *)&un_addr, + sizeof(un_addr)); + } + + swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len); + + return len; + } + + ret = real_sendto(s, buf, len, flags, (struct sockaddr *)msg.msg_name, + msg.msg_namelen); + + swrap_sendmsg_after(si, &msg, to, ret); + + return ret; +} + +_PUBLIC_ ssize_t swrap_recv(int s, void *buf, size_t len, int flags) +{ + int ret; + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_recv(s, buf, len, flags); + } + + if (si->type == SOCK_STREAM) { + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ + len = MIN(len, 1500); + } + + ret = real_recv(s, buf, len, flags); + if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) { + swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0); + } else if (ret == 0) { /* END OF FILE */ + swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0); + } else if (ret > 0) { + swrap_dump_packet(si, NULL, SWRAP_RECV, buf, ret); + } + + return ret; +} + +_PUBLIC_ ssize_t swrap_read(int s, void *buf, size_t len) +{ + int ret; + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_read(s, buf, len); + } + + if (si->type == SOCK_STREAM) { + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ + len = MIN(len, 1500); + } + + ret = real_read(s, buf, len); + if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) { + swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0); + } else if (ret == 0) { /* END OF FILE */ + swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0); + } else if (ret > 0) { + swrap_dump_packet(si, NULL, SWRAP_RECV, buf, ret); + } + + return ret; +} + + +_PUBLIC_ ssize_t swrap_send(int s, const void *buf, size_t len, int flags) +{ + struct msghdr msg; + struct iovec tmp; + struct sockaddr_un un_addr; + ssize_t ret; + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_send(s, buf, len, flags); + } + + tmp.iov_base = discard_const_p(char, buf); + tmp.iov_len = len; + + ZERO_STRUCT(msg); + msg.msg_name = NULL; /* optional address */ + msg.msg_namelen = 0; /* size of address */ + msg.msg_iov = &tmp; /* scatter/gather array */ + msg.msg_iovlen = 1; /* # elements in msg_iov */ +#if 0 /* not available on solaris */ + msg.msg_control = NULL; /* ancillary data, see below */ + msg.msg_controllen = 0; /* ancillary data buffer len */ + msg.msg_flags = 0; /* flags on received message */ +#endif + + ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, NULL, NULL, NULL); + if (ret == -1) return -1; + + buf = msg.msg_iov[0].iov_base; + len = msg.msg_iov[0].iov_len; + + ret = real_send(s, buf, len, flags); + + swrap_sendmsg_after(si, &msg, NULL, ret); + + return ret; +} + +_PUBLIC_ ssize_t swrap_sendmsg(int s, const struct msghdr *omsg, int flags) +{ + struct msghdr msg; + struct iovec tmp; + struct sockaddr_un un_addr; + const struct sockaddr_un *to_un = NULL; + const struct sockaddr *to = NULL; + ssize_t ret; + struct socket_info *si = find_socket_info(s); + int bcast = 0; + + if (!si) { + return real_sendmsg(s, omsg, flags); + } + + tmp.iov_base = NULL; + tmp.iov_len = 0; + + msg = *omsg; +#if 0 + msg.msg_name = omsg->msg_name; /* optional address */ + msg.msg_namelen = omsg->msg_namelen; /* size of address */ + msg.msg_iov = omsg->msg_iov; /* scatter/gather array */ + msg.msg_iovlen = omsg->msg_iovlen; /* # elements in msg_iov */ + /* the following is not available on solaris */ + msg.msg_control = omsg->msg_control; /* ancillary data, see below */ + msg.msg_controllen = omsg->msg_controllen; /* ancillary data buffer len */ + msg.msg_flags = omsg->msg_flags; /* flags on received message */ +#endif + + ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, &to_un, &to, &bcast); + if (ret == -1) return -1; + + if (bcast) { + struct stat st; + unsigned int iface; + unsigned int prt = ntohs(((const struct sockaddr_in *)to)->sin_port); + char type; + size_t i, len = 0; + uint8_t *buf; + off_t ofs = 0; + size_t avail = 0; + size_t remain; + + for (i=0; i < msg.msg_iovlen; i++) { + avail += msg.msg_iov[i].iov_len; + } + + len = avail; + remain = avail; + + /* we capture it as one single packet */ + buf = (uint8_t *)malloc(remain); + if (!buf) { + return -1; + } + + for (i=0; i < msg.msg_iovlen; i++) { + size_t this_time = MIN(remain, msg.msg_iov[i].iov_len); + memcpy(buf + ofs, + msg.msg_iov[i].iov_base, + this_time); + ofs += this_time; + remain -= this_time; + } + + type = SOCKET_TYPE_CHAR_UDP; + + for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) { + snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT, + socket_wrapper_dir(), type, iface, prt); + if (stat(un_addr.sun_path, &st) != 0) continue; + + msg.msg_name = &un_addr; /* optional address */ + msg.msg_namelen = sizeof(un_addr); /* size of address */ + + /* ignore the any errors in broadcast sends */ + real_sendmsg(s, &msg, flags); + } + + swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len); + free(buf); + + return len; + } + + ret = real_sendmsg(s, &msg, flags); + + swrap_sendmsg_after(si, &msg, to, ret); + + return ret; +} + +int swrap_readv(int s, const struct iovec *vector, size_t count) +{ + int ret; + struct socket_info *si = find_socket_info(s); + struct iovec v; + + if (!si) { + return real_readv(s, vector, count); + } + + if (!si->connected) { + errno = ENOTCONN; + return -1; + } + + if (si->type == SOCK_STREAM && count > 0) { + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ + size_t i, len = 0; + + for (i=0; i < count; i++) { + size_t nlen; + nlen = len + vector[i].iov_len; + if (nlen > 1500) { + break; + } + } + count = i; + if (count == 0) { + v = vector[0]; + v.iov_len = MIN(v.iov_len, 1500); + vector = &v; + count = 1; + } + } + + ret = real_readv(s, vector, count); + if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) { + swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0); + } else if (ret == 0) { /* END OF FILE */ + swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0); + } else if (ret > 0) { + uint8_t *buf; + off_t ofs = 0; + size_t i; + size_t remain = ret; + + /* we capture it as one single packet */ + buf = (uint8_t *)malloc(ret); + if (!buf) { + /* we just not capture the packet */ + errno = 0; + return ret; + } + + for (i=0; i < count; i++) { + size_t this_time = MIN(remain, vector[i].iov_len); + memcpy(buf + ofs, + vector[i].iov_base, + this_time); + ofs += this_time; + remain -= this_time; + } + + swrap_dump_packet(si, NULL, SWRAP_RECV, buf, ret); + free(buf); + } + + return ret; +} + +ssize_t swrap_writev(int s, const struct iovec *vector, size_t count) +{ + struct msghdr msg; + struct iovec tmp; + struct sockaddr_un un_addr; + ssize_t ret; + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_writev(s, vector, count); + } + + tmp.iov_base = NULL; + tmp.iov_len = 0; + + ZERO_STRUCT(msg); + msg.msg_name = NULL; /* optional address */ + msg.msg_namelen = 0; /* size of address */ + msg.msg_iov = discard_const_p(struct iovec, vector); /* scatter/gather array */ + msg.msg_iovlen = count; /* # elements in msg_iov */ +#if 0 /* not available on solaris */ + msg.msg_control = NULL; /* ancillary data, see below */ + msg.msg_controllen = 0; /* ancillary data buffer len */ + msg.msg_flags = 0; /* flags on received message */ +#endif + + ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, NULL, NULL, NULL); + if (ret == -1) return -1; + + ret = real_writev(s, msg.msg_iov, msg.msg_iovlen); + + swrap_sendmsg_after(si, &msg, NULL, ret); + + return ret; +} + +_PUBLIC_ int swrap_close(int fd) +{ + struct socket_info *si = find_socket_info(fd); + struct socket_info_fd *fi; + int ret; + + if (!si) { + return real_close(fd); + } + + for (fi = si->fds; fi; fi = fi->next) { + if (fi->fd == fd) { + SWRAP_DLIST_REMOVE(si->fds, fi); + free(fi); + break; + } + } + + if (si->fds) { + /* there are still references left */ + return real_close(fd); + } + + SWRAP_DLIST_REMOVE(sockets, si); + + if (si->myname && si->peername) { + swrap_dump_packet(si, NULL, SWRAP_CLOSE_SEND, NULL, 0); + } + + ret = real_close(fd); + + if (si->myname && si->peername) { + swrap_dump_packet(si, NULL, SWRAP_CLOSE_RECV, NULL, 0); + swrap_dump_packet(si, NULL, SWRAP_CLOSE_ACK, NULL, 0); + } + + if (si->myname) free(si->myname); + if (si->peername) free(si->peername); + if (si->tmp_path) { + unlink(si->tmp_path); + free(si->tmp_path); + } + free(si); + + return ret; +} + +_PUBLIC_ int swrap_dup(int fd) +{ + struct socket_info *si; + struct socket_info_fd *fi; + + si = find_socket_info(fd); + + if (!si) { + return real_dup(fd); + } + + fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd)); + if (fi == NULL) { + errno = ENOMEM; + return -1; + } + + fi->fd = real_dup(fd); + if (fi->fd == -1) { + int saved_errno = errno; + free(fi); + errno = saved_errno; + return -1; + } + + SWRAP_DLIST_ADD(si->fds, fi); + return fi->fd; +} + +_PUBLIC_ int swrap_dup2(int fd, int newfd) +{ + struct socket_info *si; + struct socket_info_fd *fi; + + si = find_socket_info(fd); + + if (!si) { + return real_dup2(fd, newfd); + } + + if (find_socket_info(newfd)) { + /* dup2() does an implicit close of newfd, which we + * need to emulate */ + swrap_close(newfd); + } + + fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd)); + if (fi == NULL) { + errno = ENOMEM; + return -1; + } + + fi->fd = real_dup2(fd, newfd); + if (fi->fd == -1) { + int saved_errno = errno; + free(fi); + errno = saved_errno; + return -1; + } + + SWRAP_DLIST_ADD(si->fds, fi); + return fi->fd; +} diff --git a/src/socket_wrapper.h b/src/socket_wrapper.h new file mode 100644 index 0000000..0b5a6f5 --- /dev/null +++ b/src/socket_wrapper.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) Jelmer Vernooij 2005 <jelmer@samba.org> + * Copyright (C) Stefan Metzmacher 2006 <metze@samba.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef __SOCKET_WRAPPER_H__ +#define __SOCKET_WRAPPER_H__ + +const char *socket_wrapper_dir(void); +unsigned int socket_wrapper_default_iface(void); +int swrap_socket(int family, int type, int protocol); +int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen); +int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen); +int swrap_listen(int s, int backlog); +int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen); +int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen); +int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen); +int swrap_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen); +ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); +ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen); +ssize_t swrap_sendmsg(int s, const struct msghdr *msg, int flags); +ssize_t swrap_recvmsg(int s, struct msghdr *msg, int flags); +int swrap_ioctl(int s, int req, void *ptr); +ssize_t swrap_recv(int s, void *buf, size_t len, int flags); +ssize_t swrap_read(int s, void *buf, size_t len); +ssize_t swrap_send(int s, const void *buf, size_t len, int flags); +int swrap_readv(int s, const struct iovec *vector, size_t count); +ssize_t swrap_writev(int s, const struct iovec *vector, size_t count); +int swrap_close(int); +int swrap_dup(int oldfd); +int swrap_dup2(int oldfd, int newfd); + +#ifdef SOCKET_WRAPPER_REPLACE + +#ifdef accept +#undef accept +#endif +#define accept(s,addr,addrlen) swrap_accept(s,addr,addrlen) + +#ifdef connect +#undef connect +#endif +#define connect(s,serv_addr,addrlen) swrap_connect(s,serv_addr,addrlen) + +#ifdef bind +#undef bind +#endif +#define bind(s,myaddr,addrlen) swrap_bind(s,myaddr,addrlen) + +#ifdef listen +#undef listen +#endif +#define listen(s,blog) swrap_listen(s,blog) + +#ifdef getpeername +#undef getpeername +#endif +#define getpeername(s,name,addrlen) swrap_getpeername(s,name,addrlen) + +#ifdef getsockname +#undef getsockname +#endif +#define getsockname(s,name,addrlen) swrap_getsockname(s,name,addrlen) + +#ifdef getsockopt +#undef getsockopt +#endif +#define getsockopt(s,level,optname,optval,optlen) swrap_getsockopt(s,level,optname,optval,optlen) + +#ifdef setsockopt +#undef setsockopt +#endif +#define setsockopt(s,level,optname,optval,optlen) swrap_setsockopt(s,level,optname,optval,optlen) + +#ifdef recvfrom +#undef recvfrom +#endif +#define recvfrom(s,buf,len,flags,from,fromlen) swrap_recvfrom(s,buf,len,flags,from,fromlen) + +#ifdef sendto +#undef sendto +#endif +#define sendto(s,buf,len,flags,to,tolen) swrap_sendto(s,buf,len,flags,to,tolen) + +#ifdef sendmsg +#undef sendmsg +#endif +#define sendmsg(s,msg,flags) swrap_sendmsg(s,msg,flags) + +#ifdef recvmsg +#undef recvmsg +#endif +#define recvmsg(s,msg,flags) swrap_recvmsg(s,msg,flags) + +#ifdef ioctl +#undef ioctl +#endif +#define ioctl(s,req,ptr) swrap_ioctl(s,req,ptr) + +#ifdef recv +#undef recv +#endif +#define recv(s,buf,len,flags) swrap_recv(s,buf,len,flags) + +#ifdef read +#undef read +#endif +#define read(s,buf,len) swrap_read(s,buf,len) + +#ifdef send +#undef send +#endif +#define send(s,buf,len,flags) swrap_send(s,buf,len,flags) + +#ifdef readv +#undef readv +#endif +#define readv(s, vector, count) swrap_readv(s,vector, count) + +#ifdef writev +#undef writev +#endif +#define writev(s, vector, count) swrap_writev(s,vector, count) + +#ifdef socket +#undef socket +#endif +#define socket(domain,type,protocol) swrap_socket(domain,type,protocol) + +#ifdef close +#undef close +#endif +#define close(s) swrap_close(s) + +#ifdef dup +#undef dup +#endif +#define dup(s) swrap_dup(s) + +#ifdef dup2 +#undef dup2 +#endif +#define dup2(s, s2) swrap_dup2(s, s2) + +#endif /* SOCKET_WRAPPER_REPLACE */ +#endif /* __SOCKET_WRAPPER_H__ */ diff --git a/tests/testsuite.c b/tests/testsuite.c new file mode 100644 index 0000000..9274e7f --- /dev/null +++ b/tests/testsuite.c @@ -0,0 +1,105 @@ +/* + Unix SMB/CIFS implementation. + + local testing of the socket wrapper + + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/network.h" +#include "../socket_wrapper/socket_wrapper.h" +#include "torture/torture.h" + +static char *old_dir = NULL; +static char *old_iface = NULL; + +static void backup_env(void) +{ + old_dir = getenv("SOCKET_WRAPPER_DIR"); + old_iface = getenv("SOCKET_WRAPPER_DEFAULT_IFACE"); +} + +static void restore_env(void) +{ + if (old_dir == NULL) + unsetenv("SOCKET_WRAPPER_DIR"); + else + setenv("SOCKET_WRAPPER_DIR", old_dir, 1); + if (old_iface == NULL) + unsetenv("SOCKET_WRAPPER_DEFAULT_IFACE"); + else + setenv("SOCKET_WRAPPER_DEFAULT_IFACE", old_iface, 1); +} + +static bool test_socket_wrapper_dir(struct torture_context *tctx) +{ + backup_env(); + + setenv("SOCKET_WRAPPER_DIR", "foo", 1); + torture_assert_str_equal(tctx, socket_wrapper_dir(), "foo", "setting failed"); + setenv("SOCKET_WRAPPER_DIR", "./foo", 1); + torture_assert_str_equal(tctx, socket_wrapper_dir(), "foo", "setting failed"); + unsetenv("SOCKET_WRAPPER_DIR"); + torture_assert_str_equal(tctx, socket_wrapper_dir(), NULL, "resetting failed"); + + restore_env(); + + return true; +} + +static bool test_swrap_socket(struct torture_context *tctx) +{ + backup_env(); + setenv("SOCKET_WRAPPER_DIR", "foo", 1); + + torture_assert_int_equal(tctx, swrap_socket(1337, 1337, 0), -1, "unknown address family fails"); + torture_assert_int_equal(tctx, errno, EAFNOSUPPORT, "correct errno set"); + torture_assert_int_equal(tctx, swrap_socket(AF_INET, 1337, 0), -1, "unknown type fails"); + torture_assert_int_equal(tctx, errno, EPROTONOSUPPORT, "correct errno set"); + torture_assert_int_equal(tctx, swrap_socket(AF_INET, SOCK_DGRAM, 10), -1, "unknown protocol fails"); + torture_assert_int_equal(tctx, errno, EPROTONOSUPPORT, "correct errno set"); + + restore_env(); + + return true; +} + +unsigned int socket_wrapper_default_iface(void); +static bool test_socket_wrapper_default_iface(struct torture_context *tctx) +{ + backup_env(); + unsetenv("SOCKET_WRAPPER_DEFAULT_IFACE"); + torture_assert_int_equal(tctx, socket_wrapper_default_iface(), 1, "unset"); + setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "2", 1); + torture_assert_int_equal(tctx, socket_wrapper_default_iface(), 2, "unset"); + setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "bla", 1); + torture_assert_int_equal(tctx, socket_wrapper_default_iface(), 1, "unset"); + restore_env(); + return true; +} + +struct torture_suite *torture_local_socket_wrapper(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, + "socket-wrapper"); + + torture_suite_add_simple_test(suite, "socket_wrapper_dir", test_socket_wrapper_dir); + torture_suite_add_simple_test(suite, "socket", test_swrap_socket); + torture_suite_add_simple_test(suite, "socket_wrapper_default_iface", test_socket_wrapper_default_iface); + + return suite; +} |