From 237c6e951a8268afeaa288d8ddf0c79da32f6fd1 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Tue, 5 Feb 2013 17:18:31 +0100 Subject: Initial commit. --- CMakeLists.txt | 51 + COPYING | 32 + CPackConfig.cmake | 53 + CTestConfig.cmake | 9 + ConfigureChecks.cmake | 100 + DefineOptions.cmake | 27 + README | 18 + cmake/Modules/COPYING-CMAKE-SCRIPTS | 22 + cmake/Modules/CheckCCompilerFlagSSP.cmake | 26 + cmake/Modules/DefineCMakeDefaults.cmake | 27 + cmake/Modules/DefineCompilerFlags.cmake | 75 + cmake/Modules/DefineInstallationPaths.cmake | 104 + cmake/Modules/DefinePlatformDefaults.cmake | 28 + cmake/Modules/MacroEnsureOutOfSourceBuild.cmake | 17 + config.h.cmake | 58 + src/CMakeLists.txt | 4 + src/py_socket_wrapper.c | 883 ++++++++ src/socket.py | 50 + src/socket_wrapper.c | 2666 +++++++++++++++++++++++ src/socket_wrapper.h | 177 ++ tests/testsuite.c | 105 + 21 files changed, 4532 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 CPackConfig.cmake create mode 100644 CTestConfig.cmake create mode 100644 ConfigureChecks.cmake create mode 100644 DefineOptions.cmake create mode 100644 README create mode 100644 cmake/Modules/COPYING-CMAKE-SCRIPTS create mode 100644 cmake/Modules/CheckCCompilerFlagSSP.cmake create mode 100644 cmake/Modules/DefineCMakeDefaults.cmake create mode 100644 cmake/Modules/DefineCompilerFlags.cmake create mode 100644 cmake/Modules/DefineInstallationPaths.cmake create mode 100644 cmake/Modules/DefinePlatformDefaults.cmake create mode 100644 cmake/Modules/MacroEnsureOutOfSourceBuild.cmake create mode 100644 config.h.cmake create mode 100644 src/CMakeLists.txt create mode 100644 src/py_socket_wrapper.c create mode 100644 src/socket.py create mode 100644 src/socket_wrapper.c create mode 100644 src/socket_wrapper.h create mode 100644 tests/testsuite.c 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) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..e6227e1 --- /dev/null +++ b/COPYING @@ -0,0 +1,32 @@ +Copyright (C) Jelmer Vernooij 2005,2008 +Copyright (C) Stefan Metzmacher 2006-2009 +Copyright (C) Andreas Schneider 2013 + +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) diff --git a/README b/README new file mode 100644 index 0000000..7646883 --- /dev/null +++ b/README @@ -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, +# +# 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() +# MACRO_ENSURE_OUT_OF_SOURCE_BUILD() + +# Copyright (c) 2006, Alexander Neundorf, +# +# 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 +#include +#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 . +# + +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 + * Copyright (C) Stefan Metzmacher 2006-2009 + * Copyright (C) Andreas Schneider 2013 + * + * 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 +#include +#include +#include +#include +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#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 + * Copyright (C) Stefan Metzmacher 2006 + * + * 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 . +*/ + +#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; +} -- cgit