diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d9b8e0..67a60ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ option(SOCKPP_BUILD_DOCUMENTATION "Create Doxygen reference documentation" OFF) #option(SOCKPP_WITH_OPENSSL "Include support for TLS Secure Sockets with OpenSSL" OFF) #option(SOCKPP_WITH_MBEDTLS "Include support for TLS Secure Sockets with Mbed TLS" OFF) option(SOCKPP_WITH_CAN "Include support for Linux SocketCAN components" OFF) - +option(SOCKPP_WITH_EXCEPTIONS "Use C++ exceptions" ON) # ----- Find any dependencies ----- diff --git a/README.md b/README.md index 99c9cd8..873f28a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # sockpp -[![Build Status](https://travis-ci.org/fpagliughi/sockpp.svg?branch=master)](https://travis-ci.org/fpagliughi/sockpp) - Simple, modern, C++ socket library. This is a fairly low-level C++ wrapper around the Berkeley sockets library using `socket`, `acceptor,` and `connector` classes that are familiar concepts from other languages. @@ -24,7 +22,7 @@ The API is changing (slightly)! The idea of having "stateless" I/O operations introduced in [PR #17](https://github.com/fpagliughi/sockpp/pull/17), which was never fully merged is coming into the API with a `result` class. This will be generic over the return type, though typically use an int for I/O operations, but the error state will be represented by a `std::error_code`. This should help to significantly reduce platform issues for tracking and reporting errors. This may even take over the whole of the API to make _all_ operations stateless by default. -Work has has begun on incorporating Secure Sockets into the library using either OpenSSL or MbedTLS libraries. [PR #17](https://github.com/fpagliughi/sockpp/pull/17), which has been sitting dormant for a few years in being merged and updated, along with new work to do something compatible with OpenSSL. You will be ablt to chose one library or the other when building `sockpp`. +Work has has begun on incorporating Secure Sockets into the library using either OpenSSL or MbedTLS libraries. [PR #17](https://github.com/fpagliughi/sockpp/pull/17), which has been sitting dormant for a few years in being merged and updated, along with new work to do something compatible with OpenSSL. You will be ablt to chose one library or the other when building `sockpp`. This probably won't be in the next release, but the following one. The library is reaching a stable API, and is on track for a 1.0 release in the near future. Until then, there may be a few more breaking changes, but hopefully those will be fewer than we have seen so far. @@ -38,9 +36,15 @@ If you're using this library, tweet at me or send me a message, and let me know ## Unrelased Features in this Branch -- [#72](https://github.com/fpagliughi/sockpp/issues/72) Removed exceptions +- [#72](https://github.com/fpagliughi/sockpp/issues/72) Removed some exceptions and made the others optional by build option. - Added `raw_socket` class. -- [#77](https://github.com/fpagliughi/sockpp/issues/77) Added `result` tempate class for success/error return values using `std::error_code` for errors. +- [#77](https://github.com/fpagliughi/sockpp/issues/77) + - Exceptions can now be removed through a CMake build option, `SOCKPP_WITH_EXCEPTIONS`. + - Added `result` tempate class for success/error return values using `std::error_code` for errors. + - `sockpp::last_error()` now returns a `std::error_code`. This should be slightly more portable, but Windows will likely still be somewhat problematic. + - A new `sockpp::last_errno()` will return the platform-specific integer error code (i.e. what `last_error()` used to return). + - More consistent validity checks for address types with `is_set()` and `operator bool()`. +- The `connector::connect()` with timeout now uses `poll()` for the timeout on non-Windows systems. Hopefully `WSAPoll()` on Windows will be available before the upcoming release as well. ## New in v0.8.1 @@ -112,7 +116,7 @@ $ cmake --build build/ --target install ### Build Options -The library has several build options via CMake to choose between creating a static or shared (dynamic) library - or both. It also allows you to build the example options, and if Doxygen is +The library has several build options via CMake to choose between creating a static or shared (dynamic) library - or both. It also allows you to build the example options, and if Doxygen is installed, it can be used to create documentation. Variable | Default Value | Description ------------ | ------------- | ------------- @@ -121,9 +125,8 @@ SOCKPP_BUILD_STATIC | OFF | Whether to build the static library SOCKPP_BUILD_DOCUMENTATION | OFF | Create and install the HTML based API documentation (requires _Doxygen)_ SOCKPP_BUILD_EXAMPLES | OFF | Build example programs SOCKPP_BUILD_TESTS | OFF | Build the unit tests (requires _Catch2_) -SOCKPP_BUILD_CAN | OFF | Build SocketCAN support. (Linux only) -SOCKPP_WITH_MBEDTLS | OFF | Secure Sockets with MbedTLS -SOCKPP_WITH_OPENSSL | OFF | Secure Sockets with OpenSSL +SOCKPP_WITH_CAN | OFF | Include SocketCAN support. (Linux only) +SOCKPP_WITH_EXCEPTIONS | ON | Whether to use C++ exceptions Set these using the '-D' switch in the CMake configuration command. For example, to build documentation and example apps: diff --git a/cmake/version.h.in b/cmake/version.h.in index e5faf68..4745bc2 100644 --- a/cmake/version.h.in +++ b/cmake/version.h.in @@ -45,6 +45,13 @@ #include +// Flag if sockpp was built with exceptions + +#cmakedefine SOCKPP_WITH_EXCEPTIONS +#if defined(SOCKPP_WITH_EXCEPTIONS) + #define SOCKPP_EXCEPTIONS +#endif + // Define which secure socket library compiled in (if any) #cmakedefine SOCKPP_WITH_OPENSSL diff --git a/include/sockpp/can_address.h b/include/sockpp/can_address.h index 9321551..12bbd65 100644 --- a/include/sockpp/can_address.h +++ b/include/sockpp/can_address.h @@ -128,13 +128,7 @@ class can_address : public sock_address * that it's not all zero. * @return @em true if the address has been set, @em false otherwise. */ - bool is_set() const noexcept { return family() != AF_UNSPEC; } - /** - * Determines if the address is valid for a CAN bus interface. - * @return @em true if the address is valid or a CAN bus interface, - * @false otherwise. - */ - operator bool() const noexcept { return family() == AF_CAN; } + bool is_set() const noexcept override { return addr_.can_family == ADDRESS_FAMILY; } /** * Gets the name of the CAN interface to which this address refers. * @return The name of the CAN interface to which this address refers. diff --git a/include/sockpp/datagram_socket.h b/include/sockpp/datagram_socket.h index cdc4f19..eafda93 100644 --- a/include/sockpp/datagram_socket.h +++ b/include/sockpp/datagram_socket.h @@ -216,6 +216,18 @@ class datagram_socket_tmpl : public datagram_socket datagram_socket_tmpl{std::get<0>(pr).release()}, datagram_socket_tmpl{std::get<1>(pr).release()}); } + /** + * Gets the local address to which the socket is bound. + * @return The local address to which the socket is bound. + * @throw sys_error on error + */ + addr_t address() const { return addr_t(socket::address()); } + /** + * Gets the address of the remote peer, if this socket is connected. + * @return The address of the remote peer, if this socket is connected. + * @throw sys_error on error + */ + addr_t peer_address() const { return addr_t(socket::peer_address()); } /** * Binds the socket to the local address. * Datagram sockets can bind to a local address/adapter to filter which diff --git a/include/sockpp/inet6_address.h b/include/sockpp/inet6_address.h index 27e0201..10ca956 100644 --- a/include/sockpp/inet6_address.h +++ b/include/sockpp/inet6_address.h @@ -13,7 +13,8 @@ // -------------------------------------------------------------------------- // This file is part of the "sockpp" C++ socket library. // -// Copyright (c) 2019 Frank Pagliughi All rights reserved. +// Copyright (c) 2019-2023 Frank Pagliughi +// All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -56,7 +57,6 @@ namespace sockpp { ///////////////////////////////////////////////////////////////////////////// - /** * Class that represents an internet (IPv6) address. This inherits from the * IP-specific form of a socket address, @em sockaddr_in. @@ -139,11 +139,12 @@ class inet6_address : public sock_address } /** * Checks if the address is set to some value. - * This doesn't attempt to determine if the address is valid, simply - * that it's not all zero. - * @return bool + * This doesn't attempt to determine if the address is valid, simply + * that it was at least set to an address of this family. + * @return @em true if the address is set to some value in this family, + * even if not valid, @em false if the address is empty. */ - bool is_set() const; + bool is_set() const noexcept override { return addr_.sin6_family == ADDRESS_FAMILY; } /** * Attempts to resolve the host name into a 32-bit internet address. * @param saddr The string host name. @@ -175,8 +176,6 @@ class inet6_address : public sock_address * Note that the address is normally stored in network byte * order. * @param i The byte to read (0-7) -{ - return addr_ != sockaddr_in6{}; * @return The specified byte in the 128-bit IPv6 Address */ uint8_t operator[](int i) const { diff --git a/include/sockpp/inet_address.h b/include/sockpp/inet_address.h index 638abb0..e64528a 100644 --- a/include/sockpp/inet_address.h +++ b/include/sockpp/inet_address.h @@ -13,7 +13,7 @@ // -------------------------------------------------------------------------- // This file is part of the "sockpp" C++ socket library. // -// Copyright (c) 2014-2019 Frank Pagliughi +// Copyright (c) 2014-2023 Frank Pagliughi // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -129,21 +129,15 @@ class inet_address : public sock_address * @param addr The other address */ inet_address(const inet_address& addr) : addr_{addr.addr_} {} + /** * Checks if the address is set to some value. * This doesn't attempt to determine if the address is valid, simply - * that it's not all zero. - * @return bool - */ - bool is_set() const; - /** - * Determines if the address is set to some value. - * This doesn't attempt to determine if the address is valid, simply - * that the family has been set properly. - * @return @em true if this has been set as some AF_INET address, - * whether or not is is valid. + * that it was at least set to an address of this family. + * @return @em true if the address is set to some value in this family, + * even if not valid, @em false if the address is empty. */ - operator bool() const { return addr_.sin_family == ADDRESS_FAMILY; } + bool is_set() const noexcept override { return addr_.sin_family == ADDRESS_FAMILY; } /** * Attempts to resolve the host name into a 32-bit internet address. * @param saddr The string host name. diff --git a/include/sockpp/sock_address.h b/include/sockpp/sock_address.h index d8a5168..5bb2102 100644 --- a/include/sockpp/sock_address.h +++ b/include/sockpp/sock_address.h @@ -96,6 +96,23 @@ class sock_address auto p = sockaddr_ptr(); return p ? p->sa_family : AF_UNSPEC; } + /** + * Checks if the address is set to some value. + * This doesn't attempt to determine if the address is valid, simply + * that it has set an address family. + * @return @em true if at least the address family has been set, @em + * false otherwise. + */ + virtual bool is_set() const noexcept { return family() != AF_UNSPEC; } + /** + * Determines if the address is set to some value. + * This doesn't attempt to determine if the address is valid, simply + * that the family has been set properly. + * @return @em true if this has been set as some address, whether or not + * is is valid. + */ + virtual operator bool() const noexcept { return is_set(); } + }; ///////////////////////////////////////////////////////////////////////////// @@ -109,20 +126,20 @@ class sock_address */ class sock_address_any : public sock_address { - /** Storage for any kind of socket address */ - sockaddr_storage addr_; - /** Length of the address (in bytes) */ - socklen_t sz_; - /** The maximum size of an address, in bytes */ static constexpr size_t MAX_SZ = sizeof(sockaddr_storage); + /** Storage for any kind of socket address */ + sockaddr_storage addr_{}; + /** Length of the address (in bytes) */ + socklen_t sz_{MAX_SZ}; + public: /** * Constructs an empty address. * The address is initialized to all zeroes. */ - sock_address_any() : addr_{}, sz_{MAX_SZ} {} + sock_address_any() =default; /** * Constructs an address. * @param addr Pointer to a buffer holding the address. @@ -131,8 +148,13 @@ class sock_address_any : public sock_address * an address. */ sock_address_any(const sockaddr* addr, socklen_t n) { - if (size_t(n) > MAX_SZ) - throw std::length_error("Address length out of range"); + if (size_t(n) > MAX_SZ) { + #if defined(SOCKPP_WITH_EXCEPTIONS) + throw std::length_error("Address length out of range"); + #else + return; + #endif + } std::memcpy(&addr_, addr, sz_ = n); } /** @@ -143,8 +165,13 @@ class sock_address_any : public sock_address * an address. */ sock_address_any(const sockaddr_storage& addr, socklen_t n) { - if (size_t(n) > MAX_SZ) - throw std::length_error("Address length out of range"); + if (size_t(n) > MAX_SZ) { + #if defined(SOCKPP_WITH_EXCEPTIONS) + throw std::length_error("Address length out of range"); + #else + return; + #endif + } std::memcpy(&addr_, &addr, sz_ = n); } /** diff --git a/include/sockpp/unix_address.h b/include/sockpp/unix_address.h index cb7418c..4cb71ee 100644 --- a/include/sockpp/unix_address.h +++ b/include/sockpp/unix_address.h @@ -119,14 +119,8 @@ class unix_address : public sock_address * that it's not all zero. * @return @em true if the address has been set, @em false otherwise. */ - bool is_set() const { return addr_.sun_path[0] != '\0'; } - /** - * Determines if the address is valid for a UNIX socket. - * @return @em true if the address is valid for a UNIX socket, - * @false otherwise. - */ - operator bool() const noexcept { - return family() == ADDRESS_FAMILY && is_set(); + bool is_set() const noexcept override { + return addr_.sun_family == ADDRESS_FAMILY && addr_.sun_path[0] != '\0'; } /** * Gets the path to which this address refers. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2286c5d..048c730 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -87,6 +87,10 @@ endif() target_compile_features(sockpp-objs PUBLIC cxx_std_14) +if(SOCKPP_WITH_EXCEPTIONS) + target_compile_options(sockpp-objs PUBLIC -DSOCKPP_WITH_EXCEPTIONS) +endif() + # Position indepence is only necessary for older compilers, but doesn't hurt set_target_properties(sockpp-objs PROPERTIES diff --git a/src/inet6_address.cpp b/src/inet6_address.cpp index be9ed52..1899e15 100644 --- a/src/inet6_address.cpp +++ b/src/inet6_address.cpp @@ -3,7 +3,7 @@ // -------------------------------------------------------------------------- // This file is part of the "sockpp" C++ socket library. // -// Copyright (c) 2019 Frank Pagliughi +// Copyright (c) 2019-2023 Frank Pagliughi // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -43,14 +43,6 @@ namespace sockpp { // -------------------------------------------------------------------------- -bool inet6_address::is_set() const -{ - static const auto EMPTY_ADDR = sockaddr_in6{}; - return std::memcmp(&addr_, &EMPTY_ADDR, SZ) != 0; -} - -// -------------------------------------------------------------------------- - in6_addr inet6_address::resolve_name(const string& saddr) { #if !defined(_WIN32) @@ -66,13 +58,22 @@ in6_addr inet6_address::resolve_name(const string& saddr) int gai_err = ::getaddrinfo(saddr.c_str(), NULL, &hints, &res); #if !defined(_WIN32) - if (gai_err == EAI_SYSTEM) - throw sys_error(); + if (gai_err == EAI_SYSTEM) { + #if defined(SOCKPP_WITH_EXCEPTIONS) + throw sys_error(); + #else + return in6_addr{}; + #endif + } #endif - if (gai_err != 0) - throw getaddrinfo_error(gai_err, saddr); - + if (gai_err != 0) { + #if defined(SOCKPP_WITH_EXCEPTIONS) + throw getaddrinfo_error(gai_err, saddr); + #else + return in6_addr{}; + #endif + } auto ipv6 = reinterpret_cast(res->ai_addr); auto addr = ipv6->sin6_addr; diff --git a/src/inet_address.cpp b/src/inet_address.cpp index 6e04103..82eddbc 100644 --- a/src/inet_address.cpp +++ b/src/inet_address.cpp @@ -3,7 +3,7 @@ // -------------------------------------------------------------------------- // This file is part of the "sockpp" C++ socket library. // -// Copyright (c) 2014-2017 Frank Pagliughi +// Copyright (c) 2014-2023 Frank Pagliughi // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -43,14 +43,6 @@ namespace sockpp { // -------------------------------------------------------------------------- -bool inet_address::is_set() const -{ - static const auto EMPTY_ADDR = sockaddr_in{}; - return std::memcmp(&addr_, &EMPTY_ADDR, SZ) != 0; -} - -// -------------------------------------------------------------------------- - in_addr_t inet_address::resolve_name(const std::string& saddr) { #if !defined(_WIN32) @@ -66,12 +58,22 @@ in_addr_t inet_address::resolve_name(const std::string& saddr) int gai_err = ::getaddrinfo(saddr.c_str(), NULL, &hints, &res); #if !defined(_WIN32) - if (gai_err == EAI_SYSTEM) - throw sys_error(); + if (gai_err == EAI_SYSTEM) { + #if defined(SOCKPP_WITH_EXCEPTIONS) + throw sys_error(); + #else + return in_addr_t{}; + #endif + } #endif - if (gai_err != 0) - throw getaddrinfo_error(gai_err, saddr); + if (gai_err != 0) { + #if defined(SOCKPP_WITH_EXCEPTIONS) + throw getaddrinfo_error(gai_err, saddr); + #else + return in_addr_t{}; + #endif + } auto ipv4 = reinterpret_cast(res->ai_addr); auto addr = ipv4->sin_addr.s_addr; diff --git a/tests/unit/test_inet_address.cpp b/tests/unit/test_inet_address.cpp index e56835d..6c60e51 100644 --- a/tests/unit/test_inet_address.cpp +++ b/tests/unit/test_inet_address.cpp @@ -6,7 +6,7 @@ // -------------------------------------------------------------------------- // This file is part of the "sockpp" C++ socket library. // -// Copyright (c) 2018 Frank Pagliughi +// Copyright (c) 2018-2023 Frank Pagliughi // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -52,6 +52,7 @@ const in_port_t PORT { 12345 }; TEST_CASE("inet_address default constructor", "[address]") { inet_address addr; + REQUIRE(!addr); REQUIRE(!addr.is_set()); REQUIRE(0 == addr.address()); REQUIRE(0 == addr.port()); @@ -60,6 +61,7 @@ TEST_CASE("inet_address default constructor", "[address]") { SECTION("creating address from int32") { addr.create(LOCALHOST_ADDR, PORT); + REQUIRE(addr); REQUIRE(addr.is_set()); REQUIRE(LOCALHOST_ADDR == addr.address()); REQUIRE(PORT == addr.port()); @@ -78,6 +80,7 @@ TEST_CASE("inet_address default constructor", "[address]") { SECTION("creating address from name") { addr.create(LOCALHOST_STR, PORT); + REQUIRE(addr); REQUIRE(addr.is_set()); REQUIRE(LOCALHOST_ADDR == addr.address()); REQUIRE(PORT == addr.port()); @@ -105,6 +108,7 @@ TEST_CASE("inet_address port-only constructor", "[address]") { TEST_CASE("inet_address int32_t constructor", "[address]") { inet_address addr(LOCALHOST_ADDR, PORT); + REQUIRE(addr); REQUIRE(addr.is_set()); REQUIRE(LOCALHOST_ADDR == addr.address()); REQUIRE(PORT == addr.port()); @@ -123,6 +127,7 @@ TEST_CASE("inet_address int32_t constructor", "[address]") { TEST_CASE("inet_address name constructor", "[address]") { inet_address addr(LOCALHOST_STR, PORT); + REQUIRE(addr); REQUIRE(addr.is_set()); REQUIRE(LOCALHOST_ADDR == addr.address()); REQUIRE(PORT == addr.port()); diff --git a/tests/unit/test_unix_address.cpp b/tests/unit/test_unix_address.cpp index aee97f7..7f853f4 100644 --- a/tests/unit/test_unix_address.cpp +++ b/tests/unit/test_unix_address.cpp @@ -6,7 +6,7 @@ // -------------------------------------------------------------------------- // This file is part of the "sockpp" C++ socket library. // -// Copyright (c) 2018 Frank Pagliughi +// Copyright (c) 2018-2023 Frank Pagliughi // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -68,6 +68,7 @@ TEST_CASE("unix_address default constructor", "[address]") { TEST_CASE("unix_address path constructor", "[address]") { unix_address addr(PATH); + REQUIRE(addr); REQUIRE(addr.is_set()); REQUIRE(PATH == addr.path()); REQUIRE(sizeof(sockaddr_un) == addr.size()); @@ -81,6 +82,7 @@ TEST_CASE("unix_address path constructor", "[address]") { SECTION("copy constructor") { unix_address addr2(addr); + REQUIRE(addr2); REQUIRE(addr2.is_set()); REQUIRE(PATH == addr2.path()); REQUIRE(sizeof(sockaddr_un) == addr2.size()); @@ -96,6 +98,7 @@ TEST_CASE("unix_address path constructor", "[address]") { auto sa = addr.sockaddr_ptr(); unix_address addr2(*sa); + REQUIRE(addr2); REQUIRE(addr2.is_set()); REQUIRE(PATH == addr2.path()); REQUIRE(sizeof(sockaddr_un) == addr2.size()); @@ -138,6 +141,7 @@ TEST_CASE("unix_address sockaddr_un constructor", "[address]") { unix_address addr(unaddr); + REQUIRE(addr); REQUIRE(addr.is_set()); REQUIRE(PATH == addr.path()); REQUIRE(sizeof(sockaddr_un) == addr.size());