From a98326eb2954ac1e79f5eac28dbeab3ec15e047f Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Sat, 30 Jun 2018 12:43:08 +0200 Subject: 10.1 --- zen/socket.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100755 zen/socket.h (limited to 'zen/socket.h') diff --git a/zen/socket.h b/zen/socket.h new file mode 100755 index 00000000..c92071e2 --- /dev/null +++ b/zen/socket.h @@ -0,0 +1,84 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef SOCKET_H_23498325972583947678456437 +#define SOCKET_H_23498325972583947678456437 + +#include +#include "sys_error.h" + #include //close + #include + #include //getaddrinfo + + +namespace zen +{ +#define THROW_LAST_SYS_ERROR_WSA(functionName) \ + do { const ErrorCode ecInternal = getLastError(); throw SysError(formatSystemError(functionName, ecInternal)); } while (false) + + +//Winsock needs to be initialized before calling any of these functions! (WSAStartup/WSACleanup) + +class Socket //throw SysError +{ +public: + Socket(const Zstring& server, const Zstring& serviceName) //throw SysError + { + ::addrinfo hints = {}; + hints.ai_socktype = SOCK_STREAM; //we *do* care about this one! + hints.ai_flags = AI_ADDRCONFIG; //save a AAAA lookup on machines that can't use the returned data anyhow + + ::addrinfo* servinfo = nullptr; + ZEN_ON_SCOPE_EXIT(if (servinfo) ::freeaddrinfo(servinfo)); + + const int rcGai = ::getaddrinfo(server.c_str(), serviceName.c_str(), &hints, &servinfo); + if (rcGai != 0) + throw SysError(formatSystemError(L"getaddrinfo", replaceCpy(_("Error Code %x"), L"%x", numberTo(rcGai)), utfTo(::gai_strerror(rcGai)))); + if (!servinfo) + throw SysError(L"getaddrinfo: empty server info"); + + auto getConnectedSocket = [&](const auto& /*::addrinfo*/ ai) + { + SocketType testSocket = ::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol); + if (testSocket == invalidSocket) + THROW_LAST_SYS_ERROR_WSA(L"socket"); + ZEN_ON_SCOPE_FAIL(closeSocket(testSocket)); + + if (::connect(testSocket, ai.ai_addr, static_cast(ai.ai_addrlen)) != 0) + THROW_LAST_SYS_ERROR_WSA(L"connect"); + + return testSocket; + }; + + Opt firstError; + for (const auto* /*::addrinfo*/ si = servinfo; si; si = si->ai_next) + try + { + socket_ = getConnectedSocket(*si); //throw SysError; pass ownership + return; + } + catch (const SysError& e) { if (!firstError) firstError = e; } + + throw* firstError; //list was not empty, so there must have been an error! + } + + ~Socket() { closeSocket(socket_); } + + using SocketType = int; + SocketType get() const { return socket_; } + +private: + Socket (const Socket&) = delete; + Socket& operator=(const Socket&) = delete; + + static const SocketType invalidSocket = -1; + static void closeSocket(SocketType s) { ::close(s); } + + SocketType socket_ = invalidSocket; +}; +} + +#endif //SOCKET_H_23498325972583947678456437 -- cgit