Unverified Commit 84e9b300 authored by Joseph Noir's avatar Joseph Noir Committed by GitHub

Merge pull request #3

Add scaffold for libcaf_net and socket API
parents 79626224 1cdd23c3
......@@ -332,30 +332,35 @@ include_directories("${CAF_INCLUDE_DIRS}")
macro(add_caf_lib name header_only)
string(TOUPPER ${name} upper_name)
set(full_name libcaf_${name})
set(full_name "libcaf_${name}")
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${full_name}")
message(STATUS "skip ${full_name} (not found)")
else()
add_subdirectory(${full_name})
# if (NOT header_only)
# set(shared_target ${full_name}_shared)
# set(static_target ${full_name}_static)
# set(lib_varname CAF_LIBRARY_${upper_name})
# set(lib_varname_static ${lib_varname}_STATIC)
# if(NOT CAF_BUILD_STATIC_ONLY)
# set(${lib_varname} ${shared_target})
# set(CAF_LIBRARIES ${CAF_LIBRARIES} ${shared_target})
# else()
# set(${lib_varname} ${static_target})
# set(CAF_LIBRARIES ${CAF_LIBRARIES} ${static_target})
# endif()
# if(CAF_BUILD_STATIC_ONLY OR CAF_BUILD_STATIC)
# set(${lib_varname_static} ${static_target})
# endif()
# endif()
if(NOT ${header_only})
set(shared_target ${full_name}_shared)
set(static_target ${full_name}_static)
set(lib_varname CAF_LIBRARY_${upper_name})
set(lib_varname_static ${lib_varname}_STATIC)
if(NOT CAF_BUILD_STATIC_ONLY)
set(${lib_varname} ${shared_target})
set(CAF_LIBRARIES ${CAF_LIBRARIES} ${shared_target})
else()
set(${lib_varname} ${static_target})
set(CAF_LIBRARIES ${CAF_LIBRARIES} ${static_target})
endif()
if(CAF_BUILD_STATIC_ONLY OR CAF_BUILD_STATIC)
set(${lib_varname_static} ${static_target})
endif()
endif()
add_unit_tests("${full_name}/test/*.cpp")
# add headers to include directories so other subprojects can use them
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/libcaf_${name}")
endif()
endmacro()
add_caf_lib(bb yes)
add_caf_lib(net no)
# -- unit tests setup ----------------------------------------------------------
......
cmake_minimum_required(VERSION 2.8.12)
project(caf_net C CXX)
# get header files; only needed by CMake generators,
# e.g., for creating proper Xcode projects
file(GLOB_RECURSE LIBCAF_NET_HDRS "caf/*.hpp")
# list cpp files excluding platform-dependent files
set(LIBCAF_NET_SRCS
src/host.cpp
src/network_socket.cpp
src/pipe_socket.cpp
src/socket.cpp
)
add_custom_target(libcaf_net)
# build shared library if not compiling static only
if (NOT CAF_BUILD_STATIC_ONLY)
add_library(libcaf_net_shared SHARED ${LIBCAF_NET_SRCS} ${LIBCAF_NET_HDRS})
target_link_libraries(libcaf_net_shared ${CAF_EXTRA_LDFLAGS} ${CAF_LIBRARY_CORE})
target_include_directories(libcaf_net_shared PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>
)
set_target_properties(libcaf_net_shared
PROPERTIES
SOVERSION ${CAF_VERSION}
VERSION ${CAF_VERSION}
OUTPUT_NAME caf_net
)
install(TARGETS libcaf_net_shared
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
)
add_dependencies(libcaf_net_shared libcaf_net)
endif ()
# build static library only if --build-static or --build-static-only was set
if (CAF_BUILD_STATIC_ONLY OR CAF_BUILD_STATIC)
add_library(libcaf_net_static STATIC ${LIBCAF_NET_HDRS} ${LIBCAF_NET_SRCS})
target_link_libraries(libcaf_net_static ${CAF_EXTRA_LDFLAGS} ${CAF_LIBRARY_CORE_STATIC})
target_include_directories(libcaf_net_static PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>
)
set_target_properties(libcaf_net_static PROPERTIES OUTPUT_NAME caf_net_static)
install(TARGETS libcaf_net_static ARCHIVE DESTINATION lib)
add_dependencies(libcaf_net_static libcaf_net)
endif ()
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/caf"
DESTINATION include
FILES_MATCHING PATTERN "*.hpp")
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstdio>
#include <cstdlib>
#include "caf/error.hpp"
#include "caf/sec.hpp"
/// Calls a C functions and returns an error if `var op rhs` returns `true`.
#define CAF_NET_SYSCALL(funname, var, op, rhs, expr) \
auto var = expr; \
if (var op rhs) \
return make_error(sec::network_syscall_failed, funname, \
last_socket_error_as_string())
/// Calls a C functions and calls exit() if `var op rhs` returns `true`.
#define CAF_NET_CRITICAL_SYSCALL(funname, var, op, rhs, expr) \
auto var = expr; \
if (var op rhs) { \
fprintf(stderr, "[FATAL] %s:%u: syscall failed: %s returned %s\n", \
__FILE__, __LINE__, funname, \
last_socket_error_as_string().c_str()); \
abort(); \
} \
static_cast<void>(0)
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
// This convenience header pulls in platform-specific headers for the C socket
// API. Do *not* include this header in other headers.
#pragma once
#include "caf/config.hpp"
#ifdef CAF_WINDOWS
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif // CAF_WINDOWS
# ifndef NOMINMAX
# define NOMINMAX
# endif // NOMINMAX
# ifdef CAF_MINGW
# undef _WIN32_WINNT
# undef WINVER
# define _WIN32_WINNT WindowsVista
# define WINVER WindowsVista
# include <w32api.h>
# endif // CAF_MINGW
# include <windows.h>
# include <winsock2.h>
# include <ws2ipdef.h>
# include <ws2tcpip.h>
#else // CAF_WINDOWS
# include <arpa/inet.h>
# include <cerrno>
# include <fcntl.h>
# include <netinet/in.h>
# include <netinet/ip.h>
# include <netinet/tcp.h>
# include <sys/socket.h>
# include <unistd.h>
#endif
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstddef>
#include <limits>
#include "caf/net/socket_id.hpp"
namespace caf {
namespace net {
template <class Derived>
struct abstract_socket {
socket_id id;
constexpr abstract_socket() : id(invalid_socket_id) {
// nop
}
constexpr abstract_socket(socket_id id) : id(id) {
// nop
}
constexpr abstract_socket(const Derived& other) : id(other.id) {
// nop
}
abstract_socket& operator=(const Derived& other) {
id = other.id;
return *this;
}
template <class Inspector>
friend typename Inspector::result_type inspect(Inspector& f, Derived& x) {
return f(x.id);
}
friend constexpr bool operator==(Derived x, Derived y) {
return x.id == y.id;
}
friend constexpr bool operator!=(Derived x, Derived y) {
return x.id != y.id;
}
friend constexpr bool operator<(Derived x, Derived y) {
return x.id < y.id;
}
};
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/fwd.hpp"
namespace caf {
namespace net {
struct this_host {
/// Initializes the network subsystem.
static error startup();
/// Release any resources of the network subsystem.
static void cleanup();
};
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstdint>
#include <string>
#include <system_error>
#include <type_traits>
#include "caf/config.hpp"
#include "caf/fwd.hpp"
#include "caf/net/abstract_socket.hpp"
#include "caf/net/socket.hpp"
#include "caf/net/socket_id.hpp"
namespace caf {
namespace net {
struct network_socket : abstract_socket<network_socket> {
using super = abstract_socket<network_socket>;
using super::super;
constexpr operator socket() const noexcept {
return socket{id};
}
};
/// Identifies the invalid socket.
/// @relates network_socket
constexpr auto invalid_network_socket = network_socket{invalid_socket_id};
/// Enables or disables keepalive on `x`.
/// @relates network_socket
error keepalive(network_socket x, bool new_value);
/// Enables or disables Nagle's algorithm on `x`.
/// @relates network_socket
error tcp_nodelay(network_socket x, bool new_value);
/// Enables or disables `SIGPIPE` events from `x`.
/// @relates network_socket
error allow_sigpipe(network_socket x, bool new_value);
/// Enables or disables `SIO_UDP_CONNRESET`error on `x`.
/// @relates network_socket
error allow_udp_connreset(network_socket x, bool new_value);
/// Get the socket buffer size for `x`.
/// @pre `x != invalid_network_socket`
/// @relates network_socket
expected<int> send_buffer_size(network_socket x);
/// Set the socket buffer size for `x`.
/// @relates network_socket
error send_buffer_size(network_socket x, int new_value);
/// Returns the locally assigned port of `x`.
/// @relates network_socket
expected<uint16_t> local_port(network_socket x);
/// Returns the locally assigned address of `x`.
/// @relates network_socket
expected<std::string> local_addr(network_socket x);
/// Returns the port used by the remote host of `x`.
/// @relates network_socket
expected<uint16_t> remote_port(network_socket x);
/// Returns the remote host address of `x`.
/// @relates network_socket
expected<std::string> remote_addr(network_socket x);
/// Closes the read channel for a socket.
/// @relates network_socket
void shutdown_read(network_socket x);
/// Closes the write channel for a socket.
/// @relates network_socket
void shutdown_write(network_socket x);
/// Closes the both read and write channel for a socket.
/// @relates network_socket
void shutdown(network_socket x);
/// Transmits data from `x` to its peer.
/// @param x Connected endpoint.
/// @param buf Points to the message to send.
/// @param buf_size Specifies the size of the buffer in bytes.
/// @returns The number of written bytes on success, otherwise an error code.
/// @relates pipe_socket
variant<size_t, std::errc> write(network_socket x, const void* buf,
size_t buf_size);
/// Receives data from `x`.
/// @param x Connected endpoint.
/// @param buf Points to destination buffer.
/// @param buf_size Specifies the maximum size of the buffer in bytes.
/// @returns The number of received bytes on success, otherwise an error code.
/// @relates pipe_socket
variant<size_t, std::errc> read(network_socket x, void* buf, size_t buf_size);
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstddef>
#include <system_error>
#include <utility>
#include "caf/fwd.hpp"
#include "caf/net/abstract_socket.hpp"
#include "caf/net/socket.hpp"
#include "caf/net/socket_id.hpp"
namespace caf {
namespace net {
struct pipe_socket : abstract_socket<pipe_socket> {
using super = abstract_socket<pipe_socket>;
using super::super;
constexpr operator socket() const noexcept {
return socket{id};
}
};
/// Creates two connected sockets. The first socket is the read handle and the
/// second socket is the write handle.
/// @relates pipe_socket
expected<std::pair<pipe_socket, pipe_socket>> make_pipe();
/// Transmits data from `x` to its peer.
/// @param x Connected endpoint.
/// @param buf Points to the message to send.
/// @param buf_size Specifies the size of the buffer in bytes.
/// @returns The number of written bytes on success, otherwise an error code.
/// @relates pipe_socket
variant<size_t, std::errc> write(pipe_socket x, const void* buf,
size_t buf_size);
/// Receives data from `x`.
/// @param x Connected endpoint.
/// @param buf Points to destination buffer.
/// @param buf_size Specifies the maximum size of the buffer in bytes.
/// @returns The number of received bytes on success, otherwise an error code.
/// @relates pipe_socket
variant<size_t, std::errc> read(pipe_socket x, void* buf, size_t buf_size);
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <string>
#include <type_traits>
#include "caf/config.hpp"
#include "caf/fwd.hpp"
#include "caf/net/abstract_socket.hpp"
#include "caf/net/socket_id.hpp"
namespace caf {
namespace net {
/// An internal endpoint for sending or receiving data. Can be either a
/// ::network_socket or a ::pipe_socket.
struct socket : abstract_socket<socket> {
using super = abstract_socket<socket>;
using super::super;
};
/// Denotes the invalid socket.
constexpr auto invalid_socket = socket{invalid_socket_id};
/// Close socket `x`.
/// @relates socket
void close(socket x);
/// Returns the last socket error in this thread as an integer.
/// @relates socket
int last_socket_error();
/// Returns the last socket error as human-readable string.
/// @relates socket
std::string last_socket_error_as_string();
/// Returns a human-readable string for a given socket error.
/// @relates socket
std::string socket_error_as_string(int errcode);
/// Returns whether `errcode` indicates that an operation would block or return
/// nothing at the moment and can be tried again at a later point.
/// @relates socket
bool would_block_or_temporarily_unavailable(int errcode);
/// Sets x to be inherited by child processes if `new_value == true`
/// or not if `new_value == false`. Not implemented on Windows.
/// @relates socket
error child_process_inherit(socket x, bool new_value);
/// Enables or disables nonblocking I/O on `x`.
/// @relates socket
error nonblocking(socket x, bool new_value);
/// Convenience functions for checking the result of `recv` or `send`.
/// @relates socket
bool is_error(std::make_signed<size_t>::type res, bool is_nonblock);
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstddef>
#include <limits>
#include "caf/config.hpp"
namespace caf {
namespace net {
#ifdef CAF_WINDOWS
/// Platform-specific representation of a socket.
/// @relates socket
using socket_id = size_t;
/// Identifies the invalid socket.
constexpr socket_id invalid_socket_id = std::numeric_limits<socket_id>::max();
#else // CAF_WINDOWS
/// Platform-specific representation of a socket.
/// @relates socket
using socket_id = int;
/// Identifies the invalid socket.
constexpr socket_id invalid_socket_id = -1;
#endif // CAF_WINDOWS
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/net/host.hpp"
#include "caf/config.hpp"
#include "caf/detail/net_syscall.hpp"
#include "caf/detail/socket_sys_includes.hpp"
#include "caf/error.hpp"
#include "caf/net/socket.hpp"
#include "caf/none.hpp"
namespace caf {
namespace net {
#ifdef CAF_WINDOWS
error this_host::startup() {
WSADATA WinsockData;
CAF_NET_SYSCALL("WSAStartup", result, !=, 0,
WSAStartup(MAKEWORD(2, 2), &WinsockData));
return none;
}
void this_host::cleanup() {
WSACleanup();
}
#else // CAF_WINDOWS
error this_host::startup() {
return none;
}
void this_host::cleanup() {
// nop
}
#endif // CAF_WINDOWS
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/net/network_socket.hpp"
#include <cstdint>
#include "caf/config.hpp"
#include "caf/detail/net_syscall.hpp"
#include "caf/detail/socket_sys_includes.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
#include "caf/io/network/protocol.hpp"
#include "caf/logger.hpp"
#include "caf/sec.hpp"
#include "caf/variant.hpp"
namespace {
uint16_t port_of(sockaddr_in& what) {
return what.sin_port;
}
uint16_t port_of(sockaddr_in6& what) {
return what.sin6_port;
}
uint16_t port_of(sockaddr& what) {
switch (what.sa_family) {
case AF_INET:
return port_of(reinterpret_cast<sockaddr_in&>(what));
case AF_INET6:
return port_of(reinterpret_cast<sockaddr_in6&>(what));
default:
break;
}
CAF_CRITICAL("invalid protocol family");
}
} // namespace
namespace caf {
namespace net {
#if defined(CAF_MACOS) || defined(CAF_IOS) || defined(CAF_BSD)
# define CAF_HAS_NOSIGPIPE_SOCKET_FLAG
const int no_sigpipe_io_flag = 0;
#elif defined(CAF_WINDOWS)
const int no_sigpipe_io_flag = 0;
#else
// Use flags to recv/send on Linux/Android but no socket option.
const int no_sigpipe_io_flag = MSG_NOSIGNAL;
#endif
#ifdef CAF_WINDOWS
using setsockopt_ptr = const char*;
using getsockopt_ptr = char*;
using socket_send_ptr = const char*;
using socket_recv_ptr = char*;
using socket_size_type = int;
error keepalive(network_socket x, bool new_value) {
CAF_LOG_TRACE(CAF_ARG(x) << CAF_ARG(new_value));
char value = new_value ? 1 : 0;
CAF_NET_SYSCALL("setsockopt", res, !=, 0,
setsockopt(x.id, SOL_SOCKET, SO_KEEPALIVE, &value,
static_cast<int>(sizeof(value))));
return none;
}
error allow_sigpipe(network_socket x, bool) {
if (x == invalid_network_socket)
return make_error(sec::network_syscall_failed, "setsockopt",
"invalid socket");
return none;
}
error allow_udp_connreset(network_socket x, bool new_value) {
DWORD bytes_returned = 0;
CAF_NET_SYSCALL("WSAIoctl", res, !=, 0,
WSAIoctl(x.id, _WSAIOW(IOC_VENDOR, 12), &new_value,
sizeof(new_value), NULL, 0, &bytes_returned, NULL,
NULL));
return none;
}
#else // CAF_WINDOWS
using setsockopt_ptr = const void*;
using getsockopt_ptr = void*;
using socket_send_ptr = const void*;
using socket_recv_ptr = void*;
using socket_size_type = unsigned;
error keepalive(network_socket x, bool new_value) {
CAF_LOG_TRACE(CAF_ARG(x) << CAF_ARG(new_value));
int value = new_value ? 1 : 0;
CAF_NET_SYSCALL("setsockopt", res, !=, 0,
setsockopt(x.id, SOL_SOCKET, SO_KEEPALIVE, &value,
static_cast<unsigned>(sizeof(value))));
return none;
}
error allow_sigpipe(network_socket x, bool new_value) {
# ifdef CAF_HAS_NOSIGPIPE_SOCKET_FLAG
int value = new_value ? 0 : 1;
CAF_NET_SYSCALL("setsockopt", res, !=, 0,
setsockopt(x.id, SOL_SOCKET, SO_NOSIGPIPE, &value,
static_cast<unsigned>(sizeof(value))));
# else // CAF_HAS_NO_SIGPIPE_SOCKET_FLAG
if (x == invalid_network_socket)
return make_error(sec::network_syscall_failed, "setsockopt",
"invalid socket");
# endif // CAF_HAS_NO_SIGPIPE_SOCKET_FLAG
return none;
}
error allow_udp_connreset(network_socket x, bool) {
// SIO_UDP_CONNRESET only exists on Windows
if (x == invalid_network_socket)
return make_error(sec::network_syscall_failed, "WSAIoctl",
"invalid socket");
return none;
}
#endif // CAF_WINDOWS
expected<int> send_buffer_size(network_socket x) {
int size = 0;
socket_size_type ret_size = sizeof(size);
CAF_NET_SYSCALL("getsockopt", res, !=, 0,
getsockopt(x.id, SOL_SOCKET, SO_SNDBUF,
reinterpret_cast<getsockopt_ptr>(&size),
&ret_size));
return size;
}
error send_buffer_size(network_socket x, int new_value) {
CAF_NET_SYSCALL("setsockopt", res, !=, 0,
setsockopt(x.id, SOL_SOCKET, SO_SNDBUF,
reinterpret_cast<setsockopt_ptr>(&new_value),
static_cast<socket_size_type>(sizeof(int))));
return none;
}
error tcp_nodelay(network_socket x, bool new_value) {
CAF_LOG_TRACE(CAF_ARG(x) << CAF_ARG(new_value));
int flag = new_value ? 1 : 0;
CAF_NET_SYSCALL("setsockopt", res, !=, 0,
setsockopt(x.id, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<setsockopt_ptr>(&flag),
static_cast<socket_size_type>(sizeof(flag))));
return none;
}
expected<std::string> local_addr(network_socket x) {
sockaddr_storage st;
socket_size_type st_len = sizeof(st);
sockaddr* sa = reinterpret_cast<sockaddr*>(&st);
CAF_NET_SYSCALL("getsockname", tmp1, !=, 0, getsockname(x.id, sa, &st_len));
char addr[INET6_ADDRSTRLEN]{0};
switch (sa->sa_family) {
case AF_INET:
return inet_ntop(AF_INET, &reinterpret_cast<sockaddr_in*>(sa)->sin_addr,
addr, sizeof(addr));
case AF_INET6:
return inet_ntop(AF_INET6,
&reinterpret_cast<sockaddr_in6*>(sa)->sin6_addr, addr,
sizeof(addr));
default:
break;
}
return make_error(sec::invalid_protocol_family, "local_addr", sa->sa_family);
}
expected<uint16_t> local_port(network_socket x) {
sockaddr_storage st;
auto st_len = static_cast<socket_size_type>(sizeof(st));
CAF_NET_SYSCALL("getsockname", tmp, !=, 0,
getsockname(x.id, reinterpret_cast<sockaddr*>(&st), &st_len));
return ntohs(port_of(reinterpret_cast<sockaddr&>(st)));
}
expected<std::string> remote_addr(network_socket x) {
sockaddr_storage st;
socket_size_type st_len = sizeof(st);
sockaddr* sa = reinterpret_cast<sockaddr*>(&st);
CAF_NET_SYSCALL("getpeername", tmp, !=, 0, getpeername(x.id, sa, &st_len));
char addr[INET6_ADDRSTRLEN]{0};
switch (sa->sa_family) {
case AF_INET:
return inet_ntop(AF_INET, &reinterpret_cast<sockaddr_in*>(sa)->sin_addr,
addr, sizeof(addr));
case AF_INET6:
return inet_ntop(AF_INET6,
&reinterpret_cast<sockaddr_in6*>(sa)->sin6_addr, addr,
sizeof(addr));
default:
break;
}
return make_error(sec::invalid_protocol_family, "remote_addr", sa->sa_family);
}
expected<uint16_t> remote_port(network_socket x) {
sockaddr_storage st;
socket_size_type st_len = sizeof(st);
CAF_NET_SYSCALL("getpeername", tmp, !=, 0,
getpeername(x.id, reinterpret_cast<sockaddr*>(&st), &st_len));
return ntohs(port_of(reinterpret_cast<sockaddr&>(st)));
}
void shutdown_read(network_socket x) {
::shutdown(x.id, 0);
}
void shutdown_write(network_socket x) {
::shutdown(x.id, 1);
}
void shutdown(network_socket x) {
::shutdown(x.id, 2);
}
variant<size_t, std::errc> write(network_socket x, const void* buf,
size_t buf_size) {
auto res = ::send(x.id, reinterpret_cast<socket_send_ptr>(buf), buf_size,
no_sigpipe_io_flag);
if (res < 0)
return static_cast<std::errc>(errno);
return static_cast<size_t>(res);
}
variant<size_t, std::errc> read(network_socket x, void* buf, size_t buf_size) {
auto res = ::recv(x.id, reinterpret_cast<socket_recv_ptr>(buf), buf_size,
no_sigpipe_io_flag);
if (res < 0)
return static_cast<std::errc>(errno);
return static_cast<size_t>(res);
}
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/net/pipe_socket.hpp"
#include <cstdio>
#include <cstdlib>
#include "caf/config.hpp"
#include "caf/detail/net_syscall.hpp"
#include "caf/detail/socket_sys_includes.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
#include "caf/net/network_socket.hpp"
#include "caf/sec.hpp"
#include "caf/variant.hpp"
namespace caf {
namespace net {
#ifdef CAF_WINDOWS
/**************************************************************************\
* Based on work of others; *
* original header: *
* *
* Copyright 2007, 2010 by Nathan C. Myers <ncm@cantrip.org> *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* Redistributions of source code must retain the above copyright notice, *
* this list of conditions and the following disclaimer. *
* *
* 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. *
* *
* The name of the author must not be used to endorse or promote products *
* derived from this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT *
* HOLDER 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. *
\**************************************************************************/
expected<std::pair<pipe_socket, pipe_socket>> make_pipe() {
auto addrlen = static_cast<int>(sizeof(sockaddr_in));
socket_id socks[2] = {invalid_socket_id, invalid_socket_id};
CAF_NET_SYSCALL("socket", listener, ==, invalid_socket_id,
::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
union {
sockaddr_in inaddr;
sockaddr addr;
} a;
memset(&a, 0, sizeof(a));
a.inaddr.sin_family = AF_INET;
a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
a.inaddr.sin_port = 0;
// makes sure all sockets are closed in case of an error
auto guard = detail::make_scope_guard([&] {
auto e = WSAGetLastError();
close(socket{listener});
close(socket{socks[0]});
close(socket{socks[1]});
WSASetLastError(e);
});
// bind listener to a local port
int reuse = 1;
CAF_NET_SYSCALL("setsockopt", tmp1, !=, 0,
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<char*>(&reuse),
static_cast<int>(sizeof(reuse))));
CAF_NET_SYSCALL("bind", tmp2, !=, 0,
bind(listener, &a.addr, static_cast<int>(sizeof(a.inaddr))));
// Read the port in use: win32 getsockname may only set the port number
// (http://msdn.microsoft.com/library/ms738543.aspx).
memset(&a, 0, sizeof(a));
CAF_NET_SYSCALL("getsockname", tmp3, !=, 0,
getsockname(listener, &a.addr, &addrlen));
a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
a.inaddr.sin_family = AF_INET;
// set listener to listen mode
CAF_NET_SYSCALL("listen", tmp5, !=, 0, listen(listener, 1));
// create read-only end of the pipe
DWORD flags = 0;
CAF_NET_SYSCALL("WSASocketW", read_fd, ==, invalid_socket_id,
WSASocketW(AF_INET, SOCK_STREAM, 0, nullptr, 0, flags));
CAF_NET_SYSCALL("connect", tmp6, !=, 0,
connect(read_fd, &a.addr,
static_cast<int>(sizeof(a.inaddr))));
// get write-only end of the pipe
CAF_NET_SYSCALL("accept", write_fd, ==, invalid_socket_id,
accept(listener, nullptr, nullptr));
close(socket{listener});
guard.disable();
return std::make_pair(read_fd, write_fd);
}
variant<size_t, std::errc> write(pipe_socket x, const void* buf,
size_t buf_size) {
// On Windows, a pipe consists of two stream sockets.
return write(network_socket{x.id}, buf, buf_size);
}
variant<size_t, std::errc> read(pipe_socket x, void* buf, size_t buf_size) {
// On Windows, a pipe consists of two stream sockets.
return read(network_socket{x.id}, buf, buf_size);
}
#else // CAF_WINDOWS
expected<std::pair<pipe_socket, pipe_socket>> make_pipe() {
socket_id pipefds[2];
if (pipe(pipefds) != 0)
return make_error(sec::network_syscall_failed, "pipe",
last_socket_error_as_string());
auto guard = detail::make_scope_guard([&] {
close(socket{pipefds[0]});
close(socket{pipefds[1]});
});
// Note: for pipe2 it is better to avoid races by setting CLOEXEC (but not on
// POSIX).
if (auto err = child_process_inherit(socket{pipefds[0]}, false))
return err;
if (auto err = child_process_inherit(socket{pipefds[1]}, false))
return err;
guard.disable();
return std::make_pair(pipe_socket{pipefds[0]}, pipe_socket{pipefds[1]});
}
variant<size_t, std::errc> write(pipe_socket x, const void* buf,
size_t buf_size) {
auto res = ::write(x.id, buf, buf_size);
if (res < 0)
return static_cast<std::errc>(errno);
return static_cast<size_t>(res);
}
variant<size_t, std::errc> read(pipe_socket x, void* buf, size_t buf_size) {
auto res = ::read(x.id, buf, buf_size);
if (res < 0)
return static_cast<std::errc>(errno);
return static_cast<size_t>(res);
}
#endif // CAF_WINDOWS
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/net/socket.hpp"
#include "caf/config.hpp"
#include "caf/detail/net_syscall.hpp"
#include "caf/detail/socket_sys_includes.hpp"
#include "caf/logger.hpp"
namespace caf {
namespace net {
#ifdef CAF_WINDOWS
void close(socket fd) {
closesocket(fd.id);
}
int last_socket_error() {
return WSAGetLastError();
}
std::string socket_error_as_string(int errcode) {
LPTSTR errorText = NULL;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &errorText, 0, nullptr);
std::string result;
if (errorText != nullptr) {
result = errorText;
// Release memory allocated by FormatMessage().
LocalFree(errorText);
}
return result;
}
std::string last_socket_error_as_string() {
return socket_error_as_string(last_socket_error());
}
bool would_block_or_temporarily_unavailable(int errcode) {
return errcode == WSAEWOULDBLOCK || errcode == WSATRY_AGAIN;
}
error child_process_inherit(socket x, bool) {
// TODO: possible to implement via SetHandleInformation?
if (x == invalid_socket)
return make_error(sec::network_syscall_failed, "ioctlsocket",
"invalid socket");
return none;
}
error nonblocking(socket x, bool new_value) {
u_long mode = new_value ? 1 : 0;
CAF_NET_SYSCALL("ioctlsocket", res, !=, 0, ioctlsocket(x.id, FIONBIO, &mode));
return none;
}
#else // CAF_WINDOWS
void close(socket fd) {
::close(fd.id);
}
int last_socket_error() {
return errno;
}
std::string socket_error_as_string(int error_code) {
return strerror(error_code);
}
std::string last_socket_error_as_string() {
return strerror(errno);
}
bool would_block_or_temporarily_unavailable(int errcode) {
return errcode == EAGAIN || errcode == EWOULDBLOCK;
}
error child_process_inherit(socket x, bool new_value) {
CAF_LOG_TRACE(CAF_ARG(x) << CAF_ARG(new_value));
// read flags for x
CAF_NET_SYSCALL("fcntl", rf, ==, -1, fcntl(x.id, F_GETFD));
// calculate and set new flags
auto wf = !new_value ? rf | FD_CLOEXEC : rf & ~(FD_CLOEXEC);
CAF_NET_SYSCALL("fcntl", set_res, ==, -1, fcntl(x.id, F_SETFD, wf));
return none;
}
error nonblocking(socket x, bool new_value) {
CAF_LOG_TRACE(CAF_ARG(x) << CAF_ARG(new_value));
// read flags for x
CAF_NET_SYSCALL("fcntl", rf, ==, -1, fcntl(x.id, F_GETFL, 0));
// calculate and set new flags
auto wf = new_value ? (rf | O_NONBLOCK) : (rf & (~(O_NONBLOCK)));
CAF_NET_SYSCALL("fcntl", set_res, ==, -1, fcntl(x.id, F_SETFL, wf));
return none;
}
#endif // CAF_WINDOWS
bool is_error(std::make_signed<size_t>::type res, bool is_nonblock) {
if (res < 0) {
auto err = last_socket_error();
return !is_nonblock || !would_block_or_temporarily_unavailable(err);
}
return false;
}
} // namespace net
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <stdexcept>
#include "caf/error.hpp"
#include "caf/net/host.hpp"
namespace {
struct host_fixture {
host_fixture() {
if (auto err = caf::net::this_host::startup())
throw std::logic_error("this_host::startup failed");
}
~host_fixture() {
caf::net::this_host::cleanup();
}
};
} // namespace
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE network_socket
#include "caf/net/network_socket.hpp"
#include "caf/test/dsl.hpp"
#include "host_fixture.hpp"
using namespace caf;
using namespace caf::net;
CAF_TEST_FIXTURE_SCOPE(network_socket_tests, host_fixture)
CAF_TEST(invalid socket) {
auto x = invalid_network_socket;
CAF_CHECK_EQUAL(keepalive(x, true), sec::network_syscall_failed);
CAF_CHECK_EQUAL(tcp_nodelay(x, true), sec::network_syscall_failed);
CAF_CHECK_EQUAL(allow_sigpipe(x, true), sec::network_syscall_failed);
CAF_CHECK_EQUAL(allow_udp_connreset(x, true), sec::network_syscall_failed);
CAF_CHECK_EQUAL(send_buffer_size(x), sec::network_syscall_failed);
CAF_CHECK_EQUAL(local_port(x), sec::network_syscall_failed);
CAF_CHECK_EQUAL(local_addr(x), sec::network_syscall_failed);
CAF_CHECK_EQUAL(remote_port(x), sec::network_syscall_failed);
CAF_CHECK_EQUAL(remote_addr(x), sec::network_syscall_failed);
}
CAF_TEST_FIXTURE_SCOPE_END()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE pipe_socket
#include "caf/net/pipe_socket.hpp"
#include "caf/test/dsl.hpp"
#include <vector>
#include "host_fixture.hpp"
using namespace caf;
using namespace caf::net;
CAF_TEST_FIXTURE_SCOPE(pipe_socket_tests, host_fixture)
CAF_TEST(send and receive) {
std::vector<char> send_buf{1, 2, 3, 4, 5, 6, 7, 8};
std::vector<char> receive_buf;
receive_buf.resize(100);
pipe_socket rd_sock;
pipe_socket wr_sock;
std::tie(rd_sock, wr_sock) = unbox(make_pipe());
CAF_CHECK_EQUAL(write(wr_sock, send_buf.data(), send_buf.size()),
send_buf.size());
CAF_CHECK_EQUAL(read(rd_sock, receive_buf.data(), receive_buf.size()),
send_buf.size());
CAF_CHECK(std::equal(send_buf.begin(), send_buf.end(), receive_buf.begin()));
}
CAF_TEST_FIXTURE_SCOPE_END()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE socket
#include "caf/net/socket.hpp"
#include "caf/test/dsl.hpp"
#include "host_fixture.hpp"
using namespace caf;
using namespace caf::net;
CAF_TEST_FIXTURE_SCOPE(socket_tests, host_fixture)
CAF_TEST(invalid socket) {
auto x = invalid_socket;
CAF_CHECK_EQUAL(x.id, invalid_socket_id);
CAF_CHECK_EQUAL(child_process_inherit(x, true), sec::network_syscall_failed);
CAF_CHECK_EQUAL(nonblocking(x, true), sec::network_syscall_failed);
}
CAF_TEST_FIXTURE_SCOPE_END()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment