jsocketpp 1.0
A cross-platform C++20 socket library.
Loading...
Searching...
No Matches
common.hpp
Go to the documentation of this file.
1
5
6#pragma once
7
8#include "SocketException.hpp"
9
10#include <cstddef> // std::size_t
11#include <source_location>
12#include <span>
13#include <string>
14#include <utility> // std::pair
15
16#ifdef __GNUC__
17#define QUOTE(s) #s
18#define DIAGNOSTIC_PUSH() _Pragma("GCC diagnostic push")
19#define DIAGNOSTIC_IGNORE(warning) _Pragma(QUOTE(GCC diagnostic ignored warning))
20#define DIAGNOSTIC_POP() _Pragma("GCC diagnostic pop")
21#else
22#define DIAGNOSTIC_PUSH()
23#define DIAGNOSTIC_IGNORE(warning)
24#define DIAGNOSTIC_POP()
25#endif
26
27#include <cstring> // Use std::memset()
28#include <exception>
29#include <iostream>
30#include <limits>
31#include <memory>
32#include <string>
33#include <string_view>
34#include <utility>
35#include <vector>
36
37#ifdef _WIN32
38
39// Do not reorder includes here, because Windows headers have specific order requirements.
40// clang-format off
41#include <winsock2.h> // Must come first: socket, bind, listen, accept, etc.
42#include <ws2tcpip.h> // TCP/IP functions: getaddrinfo, getnameinfo, inet_ntop, inet_pton
43#include <windows.h> // General Windows headers (e.g., FormatMessageA)
44#include <iphlpapi.h> // GetAdaptersInfo, network adapter info
45// clang-format on
46
47// https://msdn.microsoft.com/en-us/library/6sehtctf.aspx
48#if !defined(WINVER) || (WINVER < _WIN32_WINNT_WINXP)
49#error WINVER must be defined to something equal or above to 0x0501 // Win XP
50#endif // WINVER
51#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < _WIN32_WINNT_WINXP)
52#error _WIN32_WINNT must be defined to something equal or above to 0x0501 // Win XP
53#endif // _WIN32_WINNT
54
55/* If compiled with Visual C++
56 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms737629%28v=vs.85%29.aspx
57 * http://stackoverflow.com/questions/3484434/what-does-pragma-comment-mean
58 * http://stackoverflow.com/questions/70013/how-to-detect-if-im-compiling-code-with-visual-studio-2008
59 */
60#ifdef _MSC_VER
61#pragma comment(lib, "Ws2_32.lib") // Winsock library
62#pragma comment(lib, "iphlpapi.lib") // GetAdaptersInfo
63#endif
64
65#else
66
67// Assuming Linux
68#include <arpa/inet.h> //inet_ntoa
69#include <cerrno> //errno
70#include <cstring> //strerror
71#include <fcntl.h> //fcntl
72#include <ifaddrs.h> //getifaddrs
73#include <net/if.h> //if_nametoindex
74#include <netdb.h> //addrinfo
75#include <netinet/in.h> //sockaddr_in, sockaddr_in6
76#include <netinet/tcp.h> //TCP_NODELAY, TCP_MAXSEG
77#include <poll.h> //poll
78#include <sys/ioctl.h> //ioctl
79#include <sys/select.h> //select
80#include <sys/socket.h> //socket
81#include <sys/time.h> //timeval
82#include <sys/types.h> //socket
83#include <sys/uio.h> //writev,readv,iovec
84#include <sys/un.h> //for Unix domain sockets
85#include <unistd.h> //close
86
87#endif
88
106
127
138
144
150
156
162
181
197
229namespace jsocketpp
230{
231#ifdef _WIN32
232
233typedef long ssize_t;
234
235inline int InitSockets()
236{
237 WSADATA WSAData;
238 return WSAStartup(MAKEWORD(2, 2), &WSAData);
239}
240
241inline int CleanupSockets()
242{
243 return WSACleanup();
244}
245
246inline int GetSocketError()
247{
248 return WSAGetLastError();
249}
250
251// NOLINTNEXTLINE(misc-const-correctness) - changes socket state
252inline int CloseSocket(SOCKET fd)
253{
254 return closesocket(fd);
255}
256
257const char* inet_ntop_aux(int af, const void* src, char* dst, socklen_t size);
258
259#define JSOCKETPP_TIMEOUT_CODE WSAETIMEDOUT
260
261#else
262
263typedef int SOCKET;
264constexpr SOCKET INVALID_SOCKET = -1;
265constexpr SOCKET SOCKET_ERROR = -1;
266typedef sockaddr_un SOCKADDR_UN;
267
268#define JSOCKETPP_TIMEOUT_CODE ETIMEDOUT
269
270constexpr int InitSockets()
271{
272 return 0;
273}
274constexpr int CleanupSockets()
275{
276 return 0;
277}
278inline int GetSocketError()
279{
280 return errno;
281}
282inline int CloseSocket(const SOCKET fd)
283{
284 return close(fd);
285}
286
287inline int ioctlsocket(const SOCKET fd, const long cmd, u_long* argp)
288{
289 return ioctl(fd, static_cast<unsigned long>(cmd), argp);
290}
291
292#endif
293
299std::vector<std::string> getHostAddr();
300
361std::string SocketErrorMessage(int error, bool gaiStrerror = false);
362
368enum class ShutdownMode
369{
373};
374
392using Port = std::uint16_t;
393
434inline constexpr std::size_t DefaultBufferSize = 4096;
435
485inline constexpr std::size_t DefaultDatagramReceiveSize = 8192;
486
513inline constexpr std::size_t MaxDatagramPayloadSafe = 65507;
514
525inline constexpr std::size_t MaxUdpPayloadIPv4 = 65507;
526
542inline constexpr std::size_t MaxUdpPayloadIPv6 = 65527;
543
560inline bool isIPv4MappedIPv6(const sockaddr_in6* addr6)
561{
562 return IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr);
563}
564
579inline sockaddr_in convertIPv4MappedIPv6ToIPv4(const sockaddr_in6& addr6)
580{
581 sockaddr_in addr4{};
582 addr4.sin_family = AF_INET;
583 addr4.sin_port = addr6.sin6_port;
584 std::memcpy(&addr4.sin_addr.s_addr, addr6.sin6_addr.s6_addr + 12, sizeof(addr4.sin_addr.s_addr));
585 return addr4;
586}
587
601std::string ipFromSockaddr(const sockaddr* addr, bool convertIPv4Mapped = true);
602
614Port portFromSockaddr(const sockaddr* addr);
615
646std::string addressToString(const sockaddr_storage& addr);
647
680void stringToAddress(const std::string& str, sockaddr_storage& addr);
681
682} // namespace jsocketpp
683
722namespace jsocketpp::net
723{
724
728inline uint16_t toNetwork(const uint16_t val)
729{
730 return htons(val);
731}
732
736inline uint32_t toNetwork(const uint32_t val)
737{
738 return htonl(val);
739}
740
744inline uint16_t fromNetwork(const uint16_t val)
745{
746 return ntohs(val);
747}
748
752inline uint32_t fromNetwork(const uint32_t val)
753{
754 return ntohl(val);
755}
756
757} // namespace jsocketpp::net
758
759namespace jsocketpp::internal
760{
761
784{
789 void operator()(addrinfo* p) const noexcept
790 {
791 if (p)
792 freeaddrinfo(p);
793 }
794};
795
819using AddrinfoPtr = std::unique_ptr<addrinfo, AddrinfoDeleter>;
820
946[[nodiscard]] inline AddrinfoPtr resolveAddress(const std::string_view host, const Port port, const int family,
947 const int socktype, const int protocol, const int flags = 0)
948{
949 addrinfo hints{};
950 hints.ai_family = family;
951 hints.ai_socktype = socktype;
952 hints.ai_protocol = protocol;
953 hints.ai_flags = flags;
954
955 const std::string portStr = std::to_string(port);
956
957 // Ensure NUL-terminated host for getaddrinfo
958 const std::string hostStr = host.empty() ? std::string{} : std::string(host);
959 const char* hostC = hostStr.empty() ? nullptr : hostStr.c_str();
960
961 addrinfo* raw = nullptr;
962 if (const int ret = ::getaddrinfo(hostC, portStr.c_str(), &hints, &raw); ret != 0)
963 {
964 throw SocketException(ret, SocketErrorMessage(ret, true));
965 }
966
967 return AddrinfoPtr{raw};
968}
969
1021[[nodiscard]] std::string getBoundLocalIp(SOCKET sockFd);
1022
1056[[nodiscard]] bool ipAddressesEqual(const std::string& ip1, const std::string& ip2);
1057
1103void sendExact(SOCKET fd, const void* data, std::size_t size);
1104
1160void sendExactTo(SOCKET fd, const void* data, std::size_t size, const sockaddr* addr, socklen_t addrLen,
1161 void (*afterSuccess)(void* ctx), void* ctx);
1162
1198inline bool tryCloseNoexcept(const SOCKET fd) noexcept
1199{
1200 if (fd == INVALID_SOCKET)
1201 return true;
1202 if (CloseSocket(fd) != 0)
1203 {
1204 // Optional: hook for logging, left silent per policy
1205 return false;
1206 }
1207 return true;
1208}
1209
1244inline void closeOrThrow(const SOCKET fd)
1245{
1246 if (fd == INVALID_SOCKET)
1247 return;
1248 if (CloseSocket(fd) != 0)
1249 {
1250 const int error = GetSocketError();
1251 throw SocketException(error, SocketErrorMessage(error));
1252 }
1253}
1254
1280inline std::size_t nextDatagramSize(SOCKET fd) noexcept
1281{
1282#if defined(_WIN32)
1283 u_long pending = 0;
1284 return (ioctlsocket(fd, FIONREAD, &pending) == 0 && pending > 0) ? static_cast<std::size_t>(pending) : 0;
1285#else
1286 int pending = 0;
1287 if (ioctl(fd, FIONREAD, &pending) == 0 && pending > 0)
1288 return static_cast<std::size_t>(pending);
1289
1290 // POSIX: MSG_PEEK|MSG_TRUNC returns exact datagram length without consuming it.
1291 sockaddr_storage tmp{};
1292 socklen_t tmpLen = static_cast<socklen_t>(sizeof(tmp));
1293 const ssize_t peekN = ::recvfrom(fd, nullptr, 0, MSG_PEEK | MSG_TRUNC, reinterpret_cast<sockaddr*>(&tmp), &tmpLen);
1294 return (peekN > 0) ? static_cast<std::size_t>(peekN) : 0;
1295#endif
1296}
1297
1318inline ssize_t recvInto(const SOCKET fd, std::span<std::byte> dst, const int flags, sockaddr_storage* src,
1319 socklen_t* srcLen)
1320{
1321 if (src && srcLen)
1322 {
1323 return ::recvfrom(fd, reinterpret_cast<char*>(dst.data()),
1324#if defined(_WIN32)
1325 static_cast<int>(dst.size()),
1326#else
1327 dst.size(),
1328#endif
1329 flags, reinterpret_cast<sockaddr*>(src), srcLen);
1330 }
1331
1332 return ::recv(fd, reinterpret_cast<char*>(dst.data()),
1333#if defined(_WIN32)
1334 static_cast<int>(dst.size()),
1335#else
1336 dst.size(),
1337#endif
1338 flags);
1339}
1340
1378[[noreturn]] inline void throwLastSockError(const std::source_location& loc = std::source_location::current())
1379{
1380 const int err = GetSocketError();
1381#if JSOCKETPP_INCLUDE_ERROR_CONTEXT
1382 std::string msg = SocketErrorMessage(err);
1383 msg.append(" [at ")
1384 .append(loc.file_name())
1385 .append(":")
1386 .append(std::to_string(loc.line()))
1387 .append(" ")
1388 .append(loc.function_name())
1389 .append("]");
1390 throw SocketException(err, std::move(msg));
1391#else
1392 (void) loc;
1393 throw SocketException(err, SocketErrorMessage(err));
1394#endif
1395}
1396
1418inline void resolveNumericHostPort(const sockaddr* sa, const socklen_t len, std::string& host, Port& port)
1419{
1420 char hostBuf[NI_MAXHOST]{};
1421 char servBuf[NI_MAXSERV]{};
1422
1423 int flags = NI_NUMERICHOST | NI_NUMERICSERV;
1424#ifdef NI_NUMERICSCOPE
1425 flags |= NI_NUMERICSCOPE; // ensure scope id stays numeric on IPv6 link-local
1426#endif
1427
1428 if (const int ret = ::getnameinfo(sa, len, hostBuf, sizeof(hostBuf), servBuf, sizeof(servBuf), flags); ret != 0)
1429 {
1430 throw SocketException(ret, SocketErrorMessage(ret, true));
1431 }
1432
1433 // service is numeric due to NI_NUMERICSERV; parse without locale.
1434 const auto raw = std::strtoul(servBuf, nullptr, 10);
1435 if (raw > static_cast<unsigned long>((std::numeric_limits<Port>::max)()))
1436 throw SocketException("Port out of range for Port type.");
1437
1438 host.assign(hostBuf);
1439 port = static_cast<Port>(raw);
1440}
1441
1442} // namespace jsocketpp::internal
Exception class for socket-related errors in jsocketpp.
Represents socket-related errors in the jsocketpp library.
Definition SocketException.hpp:63
constexpr std::size_t MaxUdpPayloadIPv6
Theoretical maximum UDP payload size (in bytes) over IPv6.
Definition common.hpp:542
std::uint16_t Port
Type alias representing a TCP or UDP port number (1–65535).
Definition common.hpp:392
constexpr std::size_t MaxDatagramPayloadSafe
Maximum UDP payload size (in bytes) that is safely valid across common stacks.
Definition common.hpp:513
constexpr std::size_t DefaultDatagramReceiveSize
Fallback receive size (in bytes) for UDP datagrams when the exact size is unknown.
Definition common.hpp:485
sockaddr_in convertIPv4MappedIPv6ToIPv4(const sockaddr_in6 &addr6)
Converts an IPv4-mapped IPv6 address to a pure IPv4 sockaddr_in.
Definition common.hpp:579
constexpr std::size_t MaxUdpPayloadIPv4
Maximum UDP payload size (in bytes) over IPv4.
Definition common.hpp:525
bool isIPv4MappedIPv6(const sockaddr_in6 *addr6)
Checks if a given sockaddr_in6 represents an IPv4-mapped IPv6 address.
Definition common.hpp:560
void resolveNumericHostPort(const sockaddr *sa, const socklen_t len, std::string &host, Port &port)
Resolve numeric host and port from a socket address.
Definition common.hpp:1418
constexpr std::size_t DefaultBufferSize
Default internal buffer size (in bytes) for socket read operations.
Definition common.hpp:434
std::string getBoundLocalIp(SOCKET sockFd)
Retrieves the local IP address to which the socket is currently bound.
Definition common.cpp:370
bool tryCloseNoexcept(const SOCKET fd) noexcept
Attempts to close a socket descriptor without throwing exceptions.
Definition common.hpp:1198
std::unique_ptr< addrinfo, AddrinfoDeleter > AddrinfoPtr
Smart pointer that manages addrinfo* resources using AddrinfoDeleter.
Definition common.hpp:819
void sendExactTo(SOCKET fd, const void *data, std::size_t size, const sockaddr *addr, socklen_t addrLen, void(*afterSuccess)(void *ctx), void *ctx)
Sends an entire datagram to a specific destination using sendto().
Definition common.cpp:505
void sendExact(SOCKET fd, const void *data, std::size_t size)
Sends an entire datagram to a connected peer using send().
Definition common.cpp:477
void closeOrThrow(const SOCKET fd)
Closes a socket descriptor and throws on failure.
Definition common.hpp:1244
AddrinfoPtr resolveAddress(const std::string_view host, const Port port, const int family, const int socktype, const int protocol, const int flags=0)
Resolves a hostname and port into a list of usable socket address structures.
Definition common.hpp:946
void stringToAddress(const std::string &str, sockaddr_storage &addr)
Parses an "IP:port" string into a sockaddr_storage structure.
Definition common.cpp:460
std::string addressToString(const sockaddr_storage &addr)
Converts a socket address to a human-readable "IP:port" string.
Definition common.cpp:423
Implementation-only utilities and platform abstractions for jsocketpp.
Definition BufferView.hpp:52
std::size_t nextDatagramSize(SOCKET fd) noexcept
Query the exact size of the next UDP datagram, if the platform can provide it.
Definition common.hpp:1280
ssize_t recvInto(const SOCKET fd, std::span< std::byte > dst, const int flags, sockaddr_storage *src, socklen_t *srcLen)
Receive into a caller-provided span, routing to recv() or recvfrom() as needed.
Definition common.hpp:1318
void throwLastSockError(const std::source_location &loc=std::source_location::current())
Throw a SocketException for the last socket error, with optional source-location context.
Definition common.hpp:1378
bool ipAddressesEqual(const std::string &ip1, const std::string &ip2)
Compares two IP addresses for logical equality, accounting for IPv4-mapped IPv6 forms.
Definition common.cpp:392
Endianness utilities for network byte order conversion.
uint16_t toNetwork(const uint16_t val)
Converts a 16-bit unsigned integer from host to network byte order.
Definition common.hpp:728
uint16_t fromNetwork(const uint16_t val)
Converts a 16-bit unsigned integer from network to host byte order.
Definition common.hpp:744
A C++ socket library providing Java-style networking interfaces.
Definition BufferView.hpp:16
int CleanupSockets()
Definition common.hpp:241
std::vector< std::string > getHostAddr()
Get all local network interface addresses as strings.
Definition common.cpp:170
ShutdownMode
Enum for socket shutdown modes.
Definition common.hpp:369
@ Write
Shutdown write operations (SHUT_WR or SD_SEND).
Definition common.hpp:371
@ Both
Shutdown both read and write operations (SHUT_RDWR or SD_BOTH).
Definition common.hpp:372
@ Read
Shutdown read operations (SHUT_RD or SD_RECEIVE).
Definition common.hpp:370
int CloseSocket(SOCKET fd)
Definition common.hpp:252
std::string ipFromSockaddr(const sockaddr *addr, bool convertIPv4Mapped=true)
Extracts a human-readable IP address from a socket address structure.
Definition common.cpp:317
std::string SocketErrorMessage(int error, bool gaiStrerror=false)
Convert a socket-related error code to a human-readable message.
Definition common.cpp:5
constexpr SOCKET INVALID_SOCKET
Definition common.hpp:264
const char * inet_ntop_aux(int af, const void *src, char *dst, socklen_t size)
Definition common.cpp:126
Port portFromSockaddr(const sockaddr *addr)
Extracts the port number from a socket address structure.
Definition common.cpp:355
int ioctlsocket(const SOCKET fd, const long cmd, u_long *argp)
Definition common.hpp:287
sockaddr_un SOCKADDR_UN
Definition common.hpp:266
int GetSocketError()
Definition common.hpp:246
int SOCKET
Definition common.hpp:263
constexpr SOCKET SOCKET_ERROR
Definition common.hpp:265
int InitSockets()
Definition common.hpp:235
long ssize_t
Definition common.hpp:233
Custom deleter for addrinfo* pointers to support RAII-style cleanup.
Definition common.hpp:784
void operator()(addrinfo *p) const noexcept
Deletes an addrinfo* by calling freeaddrinfo().
Definition common.hpp:789