diff --git a/TestsVS/Tests/Tests.vcxproj b/TestsVS/Tests/Tests.vcxproj index cebfb3c..1e194a6 100644 --- a/TestsVS/Tests/Tests.vcxproj +++ b/TestsVS/Tests/Tests.vcxproj @@ -101,10 +101,12 @@ Level3 Disabled _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + D:\VoidNet\include Console true + D:\VoidNet\VoidNetVS\x64\Debug\VoidNetVS.lib;%(AdditionalDependencies) diff --git a/VoidNetVS/.vs/VoidNetVS/v15/.suo b/VoidNetVS/.vs/VoidNetVS/v15/.suo index 2cb1999..b4a6ceb 100644 Binary files a/VoidNetVS/.vs/VoidNetVS/v15/.suo and b/VoidNetVS/.vs/VoidNetVS/v15/.suo differ diff --git a/VoidNetVS/.vs/VoidNetVS/v15/Solution.VC.db-shm b/VoidNetVS/.vs/VoidNetVS/v15/Solution.VC.db-shm index 4cd5c74..12f3be4 100644 Binary files a/VoidNetVS/.vs/VoidNetVS/v15/Solution.VC.db-shm and b/VoidNetVS/.vs/VoidNetVS/v15/Solution.VC.db-shm differ diff --git a/VoidNetVS/.vs/VoidNetVS/v15/Solution.VC.db-wal b/VoidNetVS/.vs/VoidNetVS/v15/Solution.VC.db-wal index 3531d39..8345d96 100644 Binary files a/VoidNetVS/.vs/VoidNetVS/v15/Solution.VC.db-wal and b/VoidNetVS/.vs/VoidNetVS/v15/Solution.VC.db-wal differ diff --git a/VoidNetVS/VoidNetVS/VoidNetVS.vcxproj b/VoidNetVS/VoidNetVS/VoidNetVS.vcxproj index c6ef6a3..8caf10b 100644 --- a/VoidNetVS/VoidNetVS/VoidNetVS.vcxproj +++ b/VoidNetVS/VoidNetVS/VoidNetVS.vcxproj @@ -18,44 +18,47 @@ x64 - - - - - - - - - - - - - - - - - - - - - - - true - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {5172321E-CCB0-4A77-9F3D-FAAF0084F434} VoidNetVS @@ -76,7 +79,7 @@ MultiByte - Application + StaticLibrary true v141 MultiByte @@ -120,7 +123,9 @@ Level3 Disabled true - ../include + ../../include + stdcpp17 + _WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) diff --git a/VoidNetVS/VoidNetVS/VoidNetVS.vcxproj.filters b/VoidNetVS/VoidNetVS/VoidNetVS.vcxproj.filters index 46c2a5a..a22a698 100644 --- a/VoidNetVS/VoidNetVS/VoidNetVS.vcxproj.filters +++ b/VoidNetVS/VoidNetVS/VoidNetVS.vcxproj.filters @@ -7,97 +7,118 @@ {4c99f44e-3ff9-4d5c-a3a1-27d1f02b0b7f} - - - - include - - - include - - - include - - - include - - - include - - - include - - - include - - - include - - - include - - - include - - - include - - - include - - - include - - - include - - - include - - - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - + + {b40bea23-e721-4605-9fb2-1a893c226525} + + + {d78dfc6c-3983-4cd8-9cb1-d101a0162e77} + + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include\Http + + + include\Http + + + include\Http + + + include\Http + + + include\Http + + + include\Http + + + include\Http + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src\Http + + + src\Http + + + src\Http + + + src\Http + + + src\Http + + + src\Http + + \ No newline at end of file diff --git a/include/Config.hpp b/include/Config.hpp deleted file mode 100644 index cde1296..0000000 --- a/include/Config.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CONFIG_HPP -#define CONFIG_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "Utility.hpp" - -struct Config -{ - static void Initialize(); - - static void SetUsingConsole(bool value); - static bool GetUsingConsole(); - - static void SetLogToFile(bool value); - static bool GetLogToFile(); - - static Utility::ConfigReader Configuration; - -private: - static bool using_console; - static bool log_to_file; -}; - -#endif \ No newline at end of file diff --git a/include/Cookies.hpp b/include/Cookies.hpp new file mode 100644 index 0000000..0f61249 --- /dev/null +++ b/include/Cookies.hpp @@ -0,0 +1,100 @@ +// https://github.com/mfichman/http + +#pragma once + +#include + +namespace std::net +{ + class Cookie + { + public: + Cookie(const std::string& text); + + Cookie() : m_httpOnly(false), m_secure(false) + { + } + + const std::string& GetName() const + { + return m_name; + } + + const std::string& GetValue() const + { + return m_value; + } + + const std::string& GetPath() const + { + return m_path; + } + + bool IsHttpOnly() const + { + return m_httpOnly; + } + + bool IsSecure() const + { + return m_secure; + } + + void SetName(const std::string& name) + { + m_name = name; + } + + void SetValue(const std::string& value) + { + m_value = value; + } + + void SetPath(const std::string& path) + { + m_path = path; + } + + void SetHttpOnly(bool httpOnly) + { + m_httpOnly = httpOnly; + } + + void SetSecure(bool secure) + { + m_secure = secure; + } + + private: + std::string m_name; + std::string m_value; + std::string m_path; + bool m_httpOnly; + bool m_secure; + }; + + class Cookies + { + public: + const Cookie operator[](const std::string &name) const; + + std::map::const_iterator begin() const + { + return m_cookie.begin(); + } + + std::map::const_iterator end() const + { + return m_cookie.end(); + } + + void SetCookie(Cookie const& cookie); + + static const std::string HOST; + static const std::string CONTENT_LENGTH; + static const std::string ACCEPT_ENCODING; + static const std::string CONNECTION; + private: + std::map m_cookie; + }; +} \ No newline at end of file diff --git a/include/Defs.hpp b/include/Defs.hpp deleted file mode 100644 index cca3cb3..0000000 --- a/include/Defs.hpp +++ /dev/null @@ -1,256 +0,0 @@ -#ifndef DEFS_HPP -#define DEFS_HPP - -#ifdef _MSC_VER -#pragma once -#define WIN32_LEAN_AND_MEAN -#define _WINSOCKAPI_ -#define _CRT_SECURE_NO_DEPRECATE -#pragma comment(lib, "ws2_32.lib") -#include -#include -#include - -#undef GetBinaryType -#undef GetShortPathName -#undef GetLongPathName -#undef GetEnvironmentStrings -#undef SetEnvironmentStrings -#undef FreeEnvironmentStrings -#undef FormatMessage -#undef EncryptFile -#undef DecryptFile -#undef CreateMutex -#undef OpenMutex -#undef CreateEvent -#undef OpenEvent -#undef CreateSemaphore -#undef OpenSemaphore -#undef LoadLibrary -#undef GetModuleFileName -#undef CreateProcess -#undef GetCommandLine -#undef GetEnvironmentVariable -#undef SetEnvironmentVariable -#undef ExpandEnvironmentStrings -#undef OutputDebugString -#undef FindResource -#undef UpdateResource -#undef FindAtom -#undef AddAtom -#undef GetSystemDirectory -#undef GetTempPath -#undef GetTempFileName -#undef SetCurrentDirectory -#undef GetCurrentDirectory -#undef CreateDirectory -#undef RemoveDirectory -#undef CreateFile -#undef DeleteFile -#undef SearchPath -#undef CopyFile -#undef MoveFile -#undef ReplaceFile -#undef GetComputerName -#undef SetComputerName -#undef GetUserName -#undef LogonUser -#undef GetVersion -#undef GetObject -#undef SendMessage - -typedef signed char int8, sbyte; -typedef unsigned char uint8, byte; -typedef signed short int16; -typedef unsigned short uint16; -typedef signed int int32; -typedef unsigned int uint32; -typedef signed __int64 int64; -typedef unsigned __int64 uint64; - -#ifdef _WIN64 -typedef signed __int64 int_ptr; -typedef unsigned __int64 uint_ptr; -#else -typedef signed long int_ptr; -typedef unsigned long uint_ptr; -#endif // win64 - -#elif defined(__GNUC__) || defined(__clang__) -typedef signed char int8, sbyte; -typedef unsigned char uint8, byte; -typedef signed short int16; -typedef unsigned short uint16; -typedef signed int int32; -typedef unsigned int uint32; - -#ifdef _WIN64 -typedef signed long long int_ptr, int64; -typedef unsigned long long uint_ptr, uint64; -#else -typedef signed long int_ptr; -typedef unsigned long uint_ptr; - -#ifdef __LP64__ -typedef signed long int64; -typedef unsigned long uint64; -#else -typedef signed long long int64; -typedef unsigned long long uint64; -#endif // __LP64__ - -#endif // win64 - -#elif defined(__DECCXX) - -typedef signed char int8, sbyte; -typedef unsigned char uint8, byte; -typedef signed short int16; -typedef unsigned short uint16; -typedef signed int int32; -typedef unsigned int uint32; -typedef signed __int64 int64; -typedef unsigned __int64 uint64; - -#ifdef __VMS -#ifdef __32BITS -typedef signed long int_ptr; -typedef unsigned long uint_ptr; -#else -typedef signed __int64 int_ptr; -typedef unsigned __int64 uint_ptr; -#endif // __32BITS -#else -typedef signed long int_ptr; -typedef unsigned long uint_ptr -#endif // __VMS - -#elif defined(__HP_aCC) - -typedef signed char int8, sbyte; -typedef unsigned char uint8, byte; -typedef signed short int16; -typedef unsigned short uint16; -typedef signed int int32; -typedef unsigned int uint32; -typedef signed long int_ptr; -typedef unsigned long uint_ptr; - -#ifdef __LP64__ -typedef signed long int64; -typedef unsigned long uint64; -#else -typedef signed long long int64; -typedef unsigned long long uint64; -#endif // __LP64__ - -#elif defined(__SUNPRO_CC) - -typedef signed char int8, sbyte; -typedef unsigned char uint8, byte; -typedef signed short int16; -typedef unsigned short uint16; -typedef signed int int32; -typedef unsigned int uint32; -typedef signed long int_ptr; -typedef unsigned long uint_otr - -#ifdef __sparcv9 -typedef signed long int64; -typedef unsigned long uint64; -#else -typedef signed long long int64; -typedef unsigned long long uint64; -#endif // __sparcv9 - -#elif defined(__IBMCPP__) - -typedef signed char int8, sbyte; -typedef unsigned char uint8, byte; -typedef signed short int16; -typedef unsigned short uint16; -typedef signed int int32; -typedef unsigned int uint32; -typedef signed long int_ptr; -typedef unsigned long uint_ptr; - -#ifdef __64BIT__ -typedef signed long int64; -typedef unsigned long uint64; -#else -typedef signed long long int64; -typedef unsigned long long uint64; -#endif // __64BIT__ - -#elif defined(__sgi) - -typedef signed char int8, sbyte; -typedef unsigned char uint8, byte; -typedef signed short int16; -typedef unsigned short uint16; -typedef signed int int32; -typedef unsigned int uint32; -typedef signed long int_ptr; -typedef unsigned long uint_ptr; - -#if _MIPS_SZLONG == 64 -typedef signed long int64; -typedef unsigned long uint64; -#else -typedef signed long long int64; -typedef unsigned long long uint64; -#endif // _MIPS_SZLONG - -#elif defined(_DIAB_TOOL) - -typedef signed char int8, sbyte; -typedef unsigned char uint8, byte; -typedef signed short int16; -typedef unsigned short uint16; -typedef signed int in32; -typedef unsigned int uint32; -typedef signed long int_ptr; -typedef unsigned long uint_ptr; -typedef signed long long int64; -typedef unsigned long long uint64; - -#endif // compiler data type defenitions - -typedef long long longlong; - -const uint16 default_client_port = 60250; -const uint16 default_server_port = 61250; - -enum DistributionType -{ - All = 1, // Others and Server - AllAndMe, // Other, Server and the user sending the message - Server, - Others, - ID, -}; - -enum ConnectionCode -{ - Accept, - Reject, - Close -}; - -enum InternalTags -{ - ConnectTag = 254, - DisconnectTag = 255, -}; - -#define IS_HANDSHAKE(name) name.subject == 1 || (name.tag == DisconnectTag || name.tag == ConnectTag || name.tag == Accept || name.tag == Close || name.tag == Reject) - -#ifdef __linux__ -#include -#include -#include -#include -void closesocket(int socket) { close(socket); } -#endif - -#endif // DEFS_HPP \ No newline at end of file diff --git a/include/Enums.hpp b/include/Enums.hpp new file mode 100644 index 0000000..ef9b48d --- /dev/null +++ b/include/Enums.hpp @@ -0,0 +1,171 @@ +#pragma once + +#undef DELETE + +namespace std::net +{ + enum class SocketParam + { + CanRead, + CanWrite, + HasError, + }; + + enum class SocketReturn + { + Yes, + No, + EncounteredError, + }; + + enum class SocketErrors + { + SE_SOCKET_ERROR = -1, + SE_NO_ERROR, + SE_EINTR, + SE_EBADF, + SE_EACCES, + SE_EFAULT, + SE_EINVAL, + SE_EMFILE, + SE_EWOULDBLOCK, + SE_EINPROGRESS, + SE_EALREADY, + SE_ENOTSOCK, + SE_EDESTADDRREQ, + SE_EMSGSIZE, + SE_EPROTOTYPE, + SE_ENOPROTOOPT, + SE_EPROTONOSUPPORT, + SE_ESOCKTNOSUPPORT, + SE_EOPNOTSUPP, + SE_EPFNOSUPPORT, + SE_EAFNOSUPPORT, + SE_EADDRINUSE, + SE_EADDRNOTAVAIL, + SE_ENETDOWN, + SE_ENETUNREACH, + SE_ENETRESET, + SE_ECONNABORTED, + SE_ECONNRESET, + SE_ENOBUFS, + SE_EISCONN, + SE_ENOTCONN, + SE_ESHUTDOWN, + SE_ETOOMANYREFS, + SE_ETIMEDOUT, + SE_ECONNREFUSED, + SE_ELOOP, + SE_ENAMETOOLONG, + SE_EHOSTDOWN, + SE_EHOSTUNREACH, + SE_ENOTEMPTY, + SE_EPROCLIM, + SE_EUSERS, + SE_EDQUOT, + SE_ESTALE, + SE_EREMOTE, + SE_EDISCON, + SE_SYSNOTREADY, + SE_VERNOTSUPPORTED, + SE_NOTINITIALISED, + SE_HOST_NOT_FOUND, + SE_TRY_AGAIN, + SE_NO_RECOVERY, + SE_NO_DATA, + SE_UDP_ERR_PORT_UNREACH, + SE_ADDRFAMILY, + SE_SYSTEM, + SE_NODEV, + SE_GET_LAST_ERROR_CODE, + }; + + enum class SocketType + { + Unknown = -1, + Datagram = 2, + Streaming = 1, + }; + + enum class SocketProtocol + { + IPv4 = 2, // AF_INET + IPv6 = 23 // AF_INET6 + }; + + enum class SocketReceiveFlags + { + None = 0, + Peek = 2, + WaitAll = 0x100, + }; + + enum class SocketWaitConditions + { + WaitForRead, + WaitForWrite, + WaitForReadOrWrite, + }; + + enum class SocketConnectionState + { + NotConnected, + Connected, + ConnectionError, + }; + + enum class HttpStatus + { + INVALID_CODE = 0, + CONTINUE = 100, + SWITCHING_PROTOCOLS = 101, + OK = 200, + CREATED = 201, + ACCEPTED = 202, + NON_AUTHORITATIVE_INFO = 203, + NO_CONTENT = 204, + RESET_CONTENT = 205, + PARTIAL_CONTENT = 206, + MULTIPLE_CHOICES = 300, + MOVED_PERMANENTLY = 301, + FOUND = 302, + SEE_OTHER = 303, + NOT_MODIFIED = 304, + USE_PROXY = 305, + TEMPORARY_REDIRECT = 307, + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + PAYMENT_REQUIRED = 402, + FORBIDDEN = 403, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + NOT_ACCEPTABLE = 406, + PROXY_AUTHENTICATION_REQUIRED = 407, + REQUEST_TIMEOUT = 408, + CONFLICT = 409, + GONE = 410, + LENGTH_REQUIRED = 411, + PRECONDITION_FAILED = 412, + REQUEST_ENTITY_TOO_LARGE = 413, + UNSUPPORTED_MEDIA_TYPE = 415, + REQUESTED_RANGE_NOT_SATISFIABLE = 416, + EXPECTATION_FAILED = 417, + INTERNAL_SERVER_ERROR = 500, + NOT_IMPLEMENTED = 501, + BAD_GATEWAY = 502, + SERVICE_UNAVAILABLE = 503, + GATEWAY_TIMEOUT = 504, + VERSION_NOT_SUPPORTED = 505, + }; + + enum class Method + { + GET, + HEAD, + POST, + PUT, + DELETE, + TRACE, + CONNECT, + }; +} \ No newline at end of file diff --git a/include/Handshake.hpp b/include/Handshake.hpp deleted file mode 100644 index 14125e6..0000000 --- a/include/Handshake.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef HANDSHAKE_HPP -#define HANDSHAKE_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "Defs.hpp" -#include "NetworkMessage.hpp" - -#include - -struct Handshake -{ - Handshake(); - Handshake(uint16 id, byte con_code, byte distribution_mode); - ~Handshake(); - - static const std::vector &EncodeHandshake(const Handshake &handshake); - static Handshake &DecodeHandshake(const std::vector &bytes); - static const NetworkMessage &HandshakeToNetworkMessage(const Handshake &handshake); - static const Handshake &NetworkMessageToHandshake(const NetworkMessage &message); - - uint16 id; - byte con_code; - byte distribution_mode; -}; - -#endif diff --git a/include/Headers.hpp b/include/Headers.hpp new file mode 100644 index 0000000..1400b6a --- /dev/null +++ b/include/Headers.hpp @@ -0,0 +1,33 @@ +// https://github.com/mfichman/http + +#pragma once + +#include + +namespace std::net +{ + class Headers + { + public: + const std::string operator[](const std::string &name) const; + + std::multimap::const_iterator begin() const + { + return m_header.begin(); + } + + std::multimap::const_iterator end() const + { + return m_header.end(); + } + + void AddHeader(std::string const& name, std::string const& value); + + static std::string const HOST; + static std::string const CONTENT_LENGTH; + static std::string const ACCEPT_ENCODING; + static std::string const CONNECTION; + private: + std::multimap m_header; +}; +} \ No newline at end of file diff --git a/include/Http.hpp b/include/Http.hpp new file mode 100644 index 0000000..e983549 --- /dev/null +++ b/include/Http.hpp @@ -0,0 +1,20 @@ +// https://github.com/mfichman/http + +#pragma once + +#include "Response.hpp" +#include "Request.hpp" + +namespace std::net +{ + class Http + { + public: + static Response Get(std::string const& path, std::string const& data = ""); + static Response Post(std::string const& path, std::string const& data = ""); + + private: + static Response Send(Request const& request); + static std::string Str(Request const& request); + }; +} \ No newline at end of file diff --git a/include/IPAddress.hpp b/include/IPAddress.hpp new file mode 100644 index 0000000..dcb1a03 --- /dev/null +++ b/include/IPAddress.hpp @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include + +#include "Net.hpp" + +namespace std::net +{ + class IPAddress + { + public: + inline IPAddress(const IPAddress &addr, uint16_t port) + : m_address(addr.ToInteger()) + , m_valid(true) + , m_port(port) + { + } + + inline IPAddress() + : m_address(0) + , m_valid(false) + , m_port(DEFAULT_SERVER_PORT) + { + } + + inline IPAddress(const std::string& address, uint16_t port = DEFAULT_SERVER_PORT) + : m_address(0) + , m_valid(false) + , m_port(port) + { + Resolve(address); + } + + inline IPAddress(const char* address, uint16_t port = DEFAULT_SERVER_PORT) + : m_address(0) + , m_valid(false) + , m_port(port) + { + Resolve(address); + } + + inline IPAddress(uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t port = DEFAULT_SERVER_PORT) + : m_address(htonl((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3)) + , m_valid(true) + , m_port(port) + { + } + + inline explicit IPAddress(uint32_t address, uint16_t port = DEFAULT_SERVER_PORT) + : m_address(htonl(address)) + , m_valid(true) + , m_port(port) + { + } + + std::string ToString() const; + inline uint32_t ToInteger() const { return ntohl(m_address); } + inline uint16_t GetPort() const { return m_port; } + + static const IPAddress None; + static const IPAddress Any; + static const IPAddress LocalHost; + static const IPAddress Broadcast; + + inline sockaddr_in ToCAddr() const + { + sockaddr_in addr; + std::memset(&addr, 0, sizeof(addr)); + addr.sin_addr.s_addr = htonl(ToInteger()); + addr.sin_family = AF_INET; + addr.sin_port = htons(GetPort()); + + return addr; + } + + private: + + friend bool operator <(const IPAddress& left, const IPAddress& right); + + void Resolve(const std::string& address); + + private: + uint32_t m_address; + bool m_valid; + uint16_t m_port; + }; + + inline bool operator ==(const IPAddress& left, const IPAddress& right) { return !(left < right) && !(right < left); } + inline bool operator !=(const IPAddress& left, const IPAddress& right) { return !(left == right); } + inline bool operator <(const IPAddress& left, const IPAddress& right) { return std::make_pair(left.m_valid, left.m_address) < std::make_pair(right.m_valid, right.m_address); } + inline bool operator >(const IPAddress& left, const IPAddress& right) { return right < left; } + inline bool operator <=(const IPAddress& left, const IPAddress& right) { return !(right < left); } + inline bool operator >=(const IPAddress& left, const IPAddress& right) { return !(left < right); } + + inline std::istream& operator >>(std::istream& stream, IPAddress& address) + { + std::string str; + stream >> str; + address = IPAddress(str); + + return stream; + } + + inline std::ostream& operator <<(std::ostream& stream, const IPAddress& address) { return stream << address.ToString(); } +} \ No newline at end of file diff --git a/include/ISocket.hpp b/include/ISocket.hpp new file mode 100644 index 0000000..5dc44fc --- /dev/null +++ b/include/ISocket.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +#include "Net.hpp" +#include "IPAddress.hpp" +#include "Enums.hpp" + +namespace std::net +{ + class Socket; + + class ISocket + { + public: + inline ISocket() + : m_socketType(SocketType::Unknown) + , m_protocol(SocketProtocol::IPv4) + { + } + + inline ISocket(SocketType InSocketType, SocketProtocol protocol = SocketProtocol::IPv4) + : m_socketType(InSocketType) + , m_protocol(protocol) + { + } + + inline virtual ~ISocket() + { + } + + virtual bool Close() = 0; + virtual bool Bind(const IPAddress &addr) = 0; + virtual bool Connect(const IPAddress& addr) = 0; + virtual bool Listen() = 0; + virtual bool WaitForPendingConnection(bool& hasPendingConnection, std::chrono::milliseconds t) = 0; + virtual bool HasPendingData(uint32_t& pendingDataSize) = 0; + virtual std::unique_ptr Accept() = 0; + virtual bool SendTo(const uint8_t* data, int32_t count, int32_t& sent, const IPAddress& addrDest) = 0; + virtual bool Send(const uint8_t* data, int32_t count, int32_t& sent) = 0; + virtual bool RecvFrom(uint8_t* data, int32_t size, int32_t& read, IPAddress& srcAddr, SocketReceiveFlags flags = SocketReceiveFlags::None) = 0; + virtual bool Recv(uint8_t* data, int32_t size, int32_t& read, SocketReceiveFlags flags = SocketReceiveFlags::None) = 0; + virtual bool Wait(SocketWaitConditions cond, std::chrono::milliseconds t) = 0; + virtual SocketConnectionState GetConnectionState() = 0; + virtual void GetAddress(IPAddress& outAddr) = 0; + virtual bool GetPeerAddress(IPAddress& outAddr) = 0; + virtual bool SetNonBlocking(bool isNonBlocking = true) = 0; + virtual bool JoinMulticastGroup(const IPAddress& addrStr) = 0; + virtual bool LeaveMulticastGroup(const IPAddress& addrStr) = 0; + virtual bool SetMulticastLoopback(bool loopback) = 0; + virtual bool SetMulticastTtl(uint8_t timeToLive) = 0; + virtual bool SetReuseAddr(bool allowReuse = true) = 0; + virtual bool SetLinger(bool shouldLinger = true, int32_t t = 0) = 0; + virtual bool SetSendBufferSize(int32_t size, int32_t& newSize) = 0; + virtual bool SetReceiveBufferSize(int32_t size, int32_t& newSize) = 0; + virtual uint32_t GetPort() = 0; + + inline SocketType GetSocketType() const + { + return m_socketType; + } + + inline SocketProtocol GetSocketProtocol() const + { + return m_protocol; + } + + private: + const SocketType m_socketType; + const SocketProtocol m_protocol; + }; +} \ No newline at end of file diff --git a/include/Init.hpp b/include/Init.hpp index af477b4..0d5eb43 100644 --- a/include/Init.hpp +++ b/include/Init.hpp @@ -1,25 +1,40 @@ -#ifndef INIT_HPP -#define INIT_HPP +#pragma once #ifdef _MSC_VER - #pragma once -#endif -#include "Defs.hpp" + #include + #include + namespace std::net + { + namespace priv + { + static WSADATA WsaData; + static bool Initialized; + } -struct Initialization -{ - static bool Initialize(); + inline static bool Initialize() + { + if (priv::Initialized) return true; + return (priv::Initialized = WSAStartup(MAKEWORD(2, 2), &priv::WsaData)) == 0; + } -#ifdef _MSC_VER - const WSADATA &GetData(); -#endif + inline static void Cleanup() + { + if (priv::Initialized) + { + WSACleanup(); + priv::Initialized = false; + } + } + } -private: -#ifdef _MSC_VER - static WSADATA wsa_data; -#endif -}; +#else + + namespace std::net + { + inline static bool Initialize() { return true; } + inline static void Cleanup() { } + } #endif \ No newline at end of file diff --git a/include/Net.hpp b/include/Net.hpp new file mode 100644 index 0000000..d06f6dd --- /dev/null +++ b/include/Net.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#ifdef _MSC_VER + #include + #include + #undef SendMessage + #undef SetPort + +#define poll WSAPoll +#define ioctl ioctlsocket + + +#else + #include + #include + #include + #include + #include + #include + #include + #include + + #define SOCKET_ERROR -1 + #define NO_ERROR 0 + #define INVALID_SOCKET NO_ERROR + + #define SOCKET int + #define closesocket close + #define ioctlsocket ioctl + + int closesocket(SOCKET soc) + { + return close(soc); + } + +#endif + +#define DEFAULT_SERVER_PORT 61250 diff --git a/include/NetworkBuffer.hpp b/include/NetworkBuffer.hpp deleted file mode 100644 index 1df0170..0000000 --- a/include/NetworkBuffer.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef NETWORK_BUFFER_HPP -#define NETWORK_BUFFER_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "Defs.hpp" -#include "Utility.hpp" - -#include - -struct NetworkBuffer -{ - NetworkBuffer(); - ~NetworkBuffer(); - - std::vector header; // size must always be sizeof(int32) - std::vector body; - - bool valid = false; -}; - -#endif \ No newline at end of file diff --git a/include/NetworkMessage.hpp b/include/NetworkMessage.hpp deleted file mode 100644 index 7b6cc27..0000000 --- a/include/NetworkMessage.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef NETWORK_MESSAGE_HPP -#define NETWORK_MESSAGE_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "Defs.hpp" -#include "NetworkBuffer.hpp" - -struct NetworkMessage -{ - NetworkMessage(); - NetworkMessage(const NetworkBuffer &buffer); - NetworkMessage(uint16 sender, byte distribution_mode, uint16 destination_id, byte tag, byte subject); - ~NetworkMessage(); - - static const NetworkBuffer &EncodeMessage(const NetworkMessage &message); - - static const NetworkMessage &DecodeMessage(const NetworkBuffer &buffer); - - uint16 sender = -2; - byte distribution_mode; - uint16 destination_id; - byte tag; - byte subject; - void *data; - - bool valid = false; -}; - -#endif diff --git a/include/Parse.hpp b/include/Parse.hpp new file mode 100644 index 0000000..a223772 --- /dev/null +++ b/include/Parse.hpp @@ -0,0 +1,67 @@ +// https://github.com/mfichman/http + +#pragma once + +#include + +namespace std::net +{ + template + class ParseResult + { + public: + T value; + char const* ch; + }; + + template + static ParseResult ParseUntil(char const* str, F func) + { + ParseResult result{}; + char const* ch = str; + for (; *ch && !func(*ch); ++ch) + { + } + + result.value = std::string(str, ch - str); + result.ch = ch; + return result; + } + + template + static inline ParseResult ParseWhile(char const* str, F func) + { + ParseResult result{}; + char const* ch = str; + for (; *ch && func(*ch); ++ch) + { + } + + result.value = std::string(str, ch - str); + result.ch = ch; + return result; + } + + static inline ParseResult ParseToken(char const* str) + { + auto token = ParseUntil(str, isspace); + token.ch = ParseWhile(token.ch, isspace).ch; + return token; + } + + static inline ParseResult parseCrLf(char const* str) + { + auto cr = ParseUntil(str, [](char ch) { return ch == '\r'; }); + if (*cr.ch == '\r') + cr.ch++; + return ParseWhile(cr.ch, [](char ch) + { + return isspace(ch) && ch != '\r'; + }); + } + + static inline ParseResult ParseWhitespace(char const* str) + { + return ParseWhile(str, isspace); + } +} \ No newline at end of file diff --git a/include/PluginManager.hpp b/include/PluginManager.hpp deleted file mode 100644 index 73cc20b..0000000 --- a/include/PluginManager.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef PLUGIN_MANAGER_HPP -#define PLUGIN_MANAGER_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -struct PluginManager -{ - -}; - -#endif diff --git a/include/Request.hpp b/include/Request.hpp new file mode 100644 index 0000000..842df07 --- /dev/null +++ b/include/Request.hpp @@ -0,0 +1,52 @@ +// https://github.com/mfichman/http + +#pragma once + +#include "Uri.hpp" +#include "Headers.hpp" +#include "Enums.hpp" + +namespace std::net +{ + class Request + { + public: + Method GetMethod() const + { + return m_method; + } + + const Uri& GetUri() const + { + return m_uri; + } + + const std::string& GetPath() const + { + return m_uri.GetPath(); + } + + const std::string& GetData() const + { + return m_data; + } + + const std::string GetHeaderElement(const std::string& name) const; + + const Headers& GetHeaders() const + { + return m_headers; + } + + void SetMethod(Method method); + void SetUri(const Uri& path); + void SetData(const std::string& data); + void AddHeader(const std::string& name, const std::string& value); + + private: + Method m_method = Method::GET; + Uri m_uri; + std::string m_data; + Headers m_headers; + }; +} \ No newline at end of file diff --git a/include/Response.hpp b/include/Response.hpp new file mode 100644 index 0000000..966e5e0 --- /dev/null +++ b/include/Response.hpp @@ -0,0 +1,43 @@ +// https://github.com/mfichman/http + +#pragma once + +#include "Headers.hpp" +#include "Cookies.hpp" +#include "Enums.hpp" + +#include + +namespace std::net +{ + class Response + { + public: + Response(const std::string& text); + Response() {}; + + HttpStatus GetStatus() const + { + return m_status; + } + + const std::string& daGetDatata() const + { + return m_data; + } + + const std::string GetHeader(const std::string& name) const; + const Cookie GetCookie(const std::string& name) const; + + void SetStatus(HttpStatus status); + void SetData(const std::string& data); + void SetHeader(const std::string& name, const std::string& value); + void SetCookie(const Cookie& cookie); + + private: + HttpStatus m_status = HttpStatus::INVALID_CODE; + std::string m_data; + Headers m_headers; + Cookies m_cookies; + }; +} \ No newline at end of file diff --git a/include/SecureSocket.hpp b/include/SecureSocket.hpp new file mode 100644 index 0000000..58cfd52 --- /dev/null +++ b/include/SecureSocket.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include "Socket.hpp" + +//#include +//#include +//#include + +namespace std::net +{ + class SecureSocket + { + public: + SecureSocket(); + + bool Connect(const IPAddress& addr); + bool Close() const; + bool HasPendingData(uint32_t& pendingDataSize) const; + bool Send(uint8_t* data, int32_t count, int32_t &sent, int flags = 0); // Execute 1 write() syscall + bool Recv(uint8_t* data, int32_t count, int32_t &read, int flags = 0); // Execte 1 read() syscall + bool Wait(SocketWaitConditions cond, std::chrono::milliseconds t) const; + SocketConnectionState GetConnectionState() const; + void GetAddress(IPAddress& outAddr) const; + int32_t GetPort() const; + + void UseCertificateFile(std::string const& path); + void UsePrivateKeyFile(std::string const& path); + + private: + bool SendRaw(uint8_t* buf, size_t len, int flags = 0); + bool SendFromBio(int flags = 0); + bool RecvToBio(int flags = 0); + void HandleReturn(size_t ret); + + std::unique_ptr m_socket; + + //SSL_CTX* m_context; + //SSL* m_conn; + //BIO* m_in; + //BIO* m_out; + bool m_eof; + }; +} \ No newline at end of file diff --git a/include/Serializer.hpp b/include/Serializer.hpp deleted file mode 100644 index 8bec1af..0000000 --- a/include/Serializer.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef SERIALIZER_HPP -#define SERIALIZER_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "Defs.hpp" -#include - -struct Serializer -{ - template static const std::vector &to_bytes(const T &object); - template static const T& from_bytes(const std::vector &bytes, T& object); -}; - -#endif \ No newline at end of file diff --git a/include/Socket.hpp b/include/Socket.hpp new file mode 100644 index 0000000..34c98b4 --- /dev/null +++ b/include/Socket.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "ISocket.hpp" +#include "Enums.hpp" + +namespace std::net +{ + class Socket : public ISocket + { + public: + inline Socket(SocketType socketType, SocketProtocol protocol = SocketProtocol::IPv4) + : ISocket(socketType, protocol) + { + init(); + } + + inline Socket(SOCKET newSocket, SocketType socketType, SocketProtocol protocol = SocketProtocol::IPv4) + : ISocket(socketType, protocol) + , m_socket(newSocket) + { + init(); + } + + virtual ~Socket() { Close(); } + + public: + + // ISocket overrides + + virtual bool Close() override; + virtual bool Bind(const IPAddress &addr) override; + virtual bool Connect(const IPAddress& addr) override; + inline virtual bool Listen() override { return listen(m_socket, SOMAXCONN) == 0; } + virtual bool WaitForPendingConnection(bool& hasPendingConnection, std::chrono::milliseconds t) override; + virtual bool HasPendingData(uint32_t& pendingDataSize) override; + virtual std::unique_ptr Accept() override; + virtual bool SendTo(const uint8_t* data, int32_t count, int32_t& sent, const IPAddress& addrDest) override; + virtual bool Send(const uint8_t* data, int32_t count, int32_t& sent) override; + virtual bool RecvFrom(uint8_t* data, int32_t size, int32_t& read, IPAddress& srcAddr, SocketReceiveFlags flags = SocketReceiveFlags::None) override; + virtual bool Recv(uint8_t* data, int32_t size, int32_t& read, SocketReceiveFlags flags = SocketReceiveFlags::None) override; + virtual bool Wait(SocketWaitConditions cond, std::chrono::milliseconds t) override; + virtual SocketConnectionState GetConnectionState() override; + virtual void GetAddress(IPAddress& outAddr) override; + virtual bool GetPeerAddress(IPAddress& outAddr) override; + virtual bool SetNonBlocking(bool isNonBlocking = true) override; + + virtual bool JoinMulticastGroup(const IPAddress& addrStr) override; + virtual bool LeaveMulticastGroup(const IPAddress& addrStr) override; + inline virtual bool SetMulticastLoopback(bool loopback) override { return (setsockopt(m_socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char*)&loopback, sizeof(loopback)) == 0); } + inline virtual bool SetMulticastTtl(uint8_t timeToLive) override { return (setsockopt(m_socket, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&timeToLive, sizeof(timeToLive)) == 0); } + inline virtual bool SetReuseAddr(bool allowReuse = true) override + { + int param = allowReuse ? 1 : 0; + return setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (char*)¶m, sizeof(param)) == 0; + } + + virtual bool SetLinger(bool shouldLinger = true, int32_t t = 0) override; + virtual bool SetSendBufferSize(int32_t size, int32_t& newSize) override; + virtual bool SetReceiveBufferSize(int32_t size, int32_t& newSize) override; + virtual uint32_t GetPort() override; + + SOCKET GetNativeSocket() + { + return m_socket; + } + + private: + void init(); + + virtual SocketReturn HasState(SocketParam state, std::chrono::milliseconds t = std::chrono::milliseconds(0)); + virtual SocketErrors TranslateErrorCode(int32_t code); + virtual int TranslateFlags(SocketReceiveFlags flags); + + inline void UpdateActivity() + { + m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); + } + + private: + SOCKET m_socket = INVALID_SOCKET; + + long long m_lastActivityTime = 0; + }; +} \ No newline at end of file diff --git a/include/TcpClient.hpp b/include/TcpClient.hpp index 785d454..873888c 100644 --- a/include/TcpClient.hpp +++ b/include/TcpClient.hpp @@ -1,79 +1,33 @@ -#ifndef TCP_CLIENT_HPP -#define TCP_CLIENT_HPP - -#include "Defs.hpp" -#include "NetworkMessage.hpp" - -#include -#include -#include -#include - -#ifdef _MSC_VER #pragma once -#endif -struct TcpClient +#include "Socket.hpp" + +namespace std::net { - TcpClient(const SOCKET &socket); - TcpClient(const std::string &ip); - TcpClient(const std::string &ip, uint16 port = default_client_port); - ~TcpClient(); + class TcpConnectionHandler; +} - void Shutdown(); +namespace std::net +{ + class TcpClient + { + friend class std::net::TcpConnectionHandler; - const std::string &GetIP(); - void SetIP(const std::string &ip); + public: + TcpClient(Socket *soc); + TcpClient(SocketProtocol protocol = SocketProtocol::IPv4); - uint16 GetPort(); - void SetPort(uint16 port); + bool Connect(const IPAddress& addrStr); + bool Close() const; + bool HasPendingData(uint32_t& pendingDataSize) const; + bool Send(const uint8_t* data, int32_t count, int32_t& sent) const; + bool Recv(uint8_t* data, int32_t size, int32_t& read, SocketReceiveFlags flags = SocketReceiveFlags::None) const; + bool Wait(SocketWaitConditions cond, std::chrono::milliseconds t) const; + SocketConnectionState GetConnectionState() const; + void GetAddress(IPAddress& outAddr) const; + int32_t GetPort() const; - uint16 GetID(); - void SetID(uint16 id); - - bool Connect(); - - bool DataAvailable(int32 &size); - - //this method will receive the messages automaticaly and use the callback methods - void ReceiveMessages(); - - //this is a more manual method with no callbacks - const NetworkMessage &ReceiveMessage(); - - std::future SendMessage(const NetworkMessage &message); - bool SendBytes(const std::vector &bytes); - bool SendBytes(byte *bytes, uint32 lenght); - - void SetOnDisconnectCallback(std::function func); - void SetOnConnectCallback(std::function func); - void SetOnMessageCallback(std::function func); - - static const TcpClient &DefaultTcpClient(); - -private: - TcpClient(); - - const NetworkBuffer &receive_data_array(); - static void receive_data(TcpClient *client); - static bool send_network_message(const NetworkMessage &message, TcpClient *client); - static void close_connection(TcpClient *client); - - bool initialize(const std::string &ip, uint16 port = default_client_port); - - uint16 id = -2; - std::string ip; - uint16 port = 0; - bool initialized = false; - bool receive = false; - - std::function OnDisconnect; - std::function OnConnect; - std::function OnMessage; - - SOCKET tcp_socket = INVALID_SOCKET; - struct addrinfo *result = nullptr; - struct addrinfo hints; -}; - -#endif \ No newline at end of file + private: + std::unique_ptr m_socket; + }; +} \ No newline at end of file diff --git a/include/TcpListener.hpp b/include/TcpListener.hpp new file mode 100644 index 0000000..40fc198 --- /dev/null +++ b/include/TcpListener.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include "Socket.hpp" + +namespace std::net +{ + class TcpConnectionHandler; +} + +namespace std::net +{ + class TcpClient; + + class TcpListener + { + friend class std::net::TcpConnectionHandler; + + public: + TcpListener(uint16_t port, std::chrono::milliseconds inSleepTime = std::chrono::milliseconds(1)); + TcpListener(Socket *InSocket, std::chrono::milliseconds inSleepTime = std::chrono::milliseconds(1)); + + TcpClient *AcceptClient(); + + private: + std::chrono::milliseconds m_sleepTime; + std::unique_ptr m_socket; + uint16_t m_port = 0; + }; +} \ No newline at end of file diff --git a/include/TcpServer.hpp b/include/TcpServer.hpp deleted file mode 100644 index 16f877e..0000000 --- a/include/TcpServer.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef TCP_SERVER_HPP -#define TCP_SERVER_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "Defs.hpp" -#include "TcpClient.hpp" -#include "Handshake.hpp" - -#include - -struct TcpServer -{ - TcpServer(); - TcpServer(uint16 port = default_server_port); - ~TcpServer(); - - void Shutdown(); - - bool StartServer(bool accept_connections); // if accept_connections is false the user must call the funcion AcceptConnections() - void AcceptConnections(); - - bool SendMessage(const NetworkMessage &message); - - void RejectConnection(TcpClient &client); - void AcceptConnection(uint16 client); - - void CloseSocket(TcpClient &client); - void CloseSocket(uint16 id); - - uint16 GetMaxConnections(); - void SetMaxConnections(uint16 value); - - const TcpClient &GetClientByID(uint16 id); - - std::vector> OnMessageFunctions; // this is going to be used for plugins - -private: - static void process_client_messages(TcpServer *server, TcpClient &client); - static void accept_connections(TcpServer *server); - - void add_to_clients_list(TcpClient &client); - - uint16 allocate_id(); - - void shutdown_internal(); - - bool initialize(uint16 port = default_server_port); - - bool initialized = false; - bool running = false; - - uint16 max_connections = 0; - - std::vector clients; - - SOCKET server_tcp_socket = INVALID_SOCKET; - struct addrinfo *result = nullptr; - struct addrinfo hints; -}; - -#endif diff --git a/include/TcpSocketBuilder.hpp b/include/TcpSocketBuilder.hpp new file mode 100644 index 0000000..52aae6a --- /dev/null +++ b/include/TcpSocketBuilder.hpp @@ -0,0 +1,112 @@ +#pragma once + +#include "Enums.hpp" +#include "IPAddress.hpp" + +namespace std::net +{ + class Socket; + class TcpClient; + class TcpListener; + + class TcpSocketBuilder + { + public: + inline TcpSocketBuilder() + : m_blocking(false) + , m_bound(false) + , m_boundAddr(IPAddress::Any, 0) + , m_linger(false) + , m_lingerTimeout(0) + , m_listen(false) + , m_receiveBufferSize(0) + , m_reusable(false) + , m_sendBufferSize(0) + , m_socketProtocol(SocketProtocol::IPv4) + { + } + + inline TcpSocketBuilder AsBlocking() + { + m_blocking = true; + + return *this; + } + + inline TcpSocketBuilder AsNonBlocking() + { + m_blocking = false; + + return *this; + } + + inline TcpSocketBuilder AsReusable() + { + m_reusable = true; + + return *this; + } + + inline TcpSocketBuilder Bind(const IPAddress &addr) + { + m_boundAddr = addr; + m_bound = true; + + return *this; + } + + inline TcpSocketBuilder Lingering(int32_t Timeout) + { + m_linger = true; + m_lingerTimeout = Timeout; + + return *this; + } + + inline TcpSocketBuilder Listening() + { + m_listen = true; + + return *this; + } + + inline TcpSocketBuilder WithReceiveBufferSize(int32_t SizeInBytes) + { + m_receiveBufferSize = SizeInBytes; + + return *this; + } + + inline TcpSocketBuilder WithSendBufferSize(int32_t SizeInBytes) + { + m_sendBufferSize = SizeInBytes; + + return *this; + } + + inline TcpSocketBuilder Protocol(SocketProtocol prot) + { + m_socketProtocol = prot; + + return *this; + } + + public: + std::unique_ptr Build() const; + std::unique_ptr BuildClient() const; + std::unique_ptr BuildListener() const; + + private: + bool m_blocking; + bool m_bound; + IPAddress m_boundAddr; + bool m_linger; + int32_t m_lingerTimeout; + bool m_listen; + int32_t m_receiveBufferSize; + bool m_reusable; + int32_t m_sendBufferSize; + + SocketProtocol m_socketProtocol; + }; +} \ No newline at end of file diff --git a/include/UdpClient.hpp b/include/UdpClient.hpp deleted file mode 100644 index ea75fe4..0000000 --- a/include/UdpClient.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef UDP_CLIENT_HPP -#define UDP_CLIENT_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "Defs.hpp" -#include "NetworkMessage.hpp" - -#include -#include -#include -#include - -struct UdpClient -{ - UdpClient(const SOCKET &socket); - UdpClient(const std::string &ip); - UdpClient(const std::string &ip, uint16 = default_client_port); - ~UdpClient(); - - void Shutdown(); - - const std::string &GetIP(); - void SetIP(const std::string &ip); - - uint16 GetPort(); - void SetPort(uint16 port); - - uint16 GetID(); - void SetID(uint16 id); - - void ReceiveMessages(); - - const NetworkMessage &ReceiveMessage(); - - std::future SendMessage(const NetworkMessage &message); - bool SendBytes(const std::vector &bytes); - bool SendBytes(byte *bytes, uint32 lenght); - -private: - const NetworkBuffer &receive_data_array(); - static void receive_data(UdpClient *client); - bool initialize(const std::string &ip, uint16 port = default_client_port); - - static bool send_network_message(const NetworkMessage &message, UdpClient *client); - - uint16 id = -2; - - std::string ip; - uint16 port = 0; - bool initialized = false; - bool receive = false; - - std::function OnDisconnect; - std::function OnConnect; - std::function OnMessage; - - SOCKET udp_socket = INVALID_SOCKET; -}; - -#endif diff --git a/include/UdpServer.hpp b/include/UdpServer.hpp deleted file mode 100644 index 7ab44b2..0000000 --- a/include/UdpServer.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef UDP_SERVER_HPP -#define UDP_SERVER_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "Defs.hpp" -#include "UdpClient.hpp" - -#include -#include - -struct UdpServer -{ -private: - bool initialize(uint16 port = default_server_port); - - uint16 allocate_id(); - - void shutdown_internal(); - - bool initialized = false; - bool running = false; - - uint16 max_connections = 0; - - std::vector clients; - - SOCKET server_udp_socket = INVALID_SOCKET; - struct sockaddr_in server; -}; - -#endif \ No newline at end of file diff --git a/include/UdpSocket.hpp b/include/UdpSocket.hpp new file mode 100644 index 0000000..58a6a76 --- /dev/null +++ b/include/UdpSocket.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "Socket.hpp" + +#include + +namespace std::net +{ + class UdpSocket + { + public: + UdpSocket(Socket *soc); + UdpSocket(SocketProtocol protocol = SocketProtocol::IPv4); + + bool Bind(const IPAddress &addr); + bool SendTo(const uint8_t* data, int32_t count, int32_t& sent, const IPAddress& addrDest); + bool RecvFrom(uint8_t* data, int32_t size, int32_t& read, IPAddress& srcAddr, SocketReceiveFlags flags = SocketReceiveFlags::None); + bool GetPeerAddress(IPAddress& outAddr); + bool JoinMulticastGroup(const IPAddress& addrStr); + bool LeaveMulticastGroup(const IPAddress& addrStr); + bool SetMulticastLoopback(bool loopback); + bool SetMulticastTtl(uint8_t timeToLive); + uint32_t GetPort(); + bool SetReuseAddr(bool allowReuse = true); + + private: + std::unique_ptr m_socket; + }; +} \ No newline at end of file diff --git a/include/UdpSocketBuilder.hpp b/include/UdpSocketBuilder.hpp new file mode 100644 index 0000000..513daed --- /dev/null +++ b/include/UdpSocketBuilder.hpp @@ -0,0 +1,152 @@ +#pragma once + +#include "UdpSocket.hpp" +#include "IPAddress.hpp" + +#include + +namespace std::net +{ + class UdpSocketBuilder + { + public: + UdpSocketBuilder() + : m_blocking(false) + , m_bound(false) + , m_boundEndpoint(IPAddress::Any, 0) + , m_multicastLoopback(false) + , m_multicastTtl(1) + , m_receiveBufferSize(0) + , m_reusable(false) + , m_sendBufferSize(0) + { } + + public: + UdpSocketBuilder AsBlocking() + { + m_blocking = true; + + return *this; + } + + UdpSocketBuilder AsNonBlocking() + { + m_blocking = false; + + return *this; + } + + UdpSocketBuilder AsReusable() + { + m_reusable = true; + + return *this; + } + + UdpSocketBuilder BoundToAddress(const IPAddress& addr) + { + m_boundEndpoint = IPAddress(addr); + m_bound = true; + + return *this; + } + + UdpSocketBuilder BoundToPort(uint16_t port) + { + m_boundEndpoint = IPAddress(m_boundEndpoint.ToInteger(), port); + m_bound = true; + + return *this; + } + + UdpSocketBuilder JoinedToGroup(const IPAddress& group_addr) + { + m_joinedGroups.emplace_back(group_addr); + + return *this; + } + + UdpSocketBuilder WithMulticastLoopback() + { + m_multicastLoopback = true; + + return *this; + } + + UdpSocketBuilder WithMulticastTtl(uint8_t time_to_live) + { + m_multicastTtl = time_to_live; + + return *this; + } + + UdpSocketBuilder WithReceiveBufferSize(uint32_t size) + { + m_receiveBufferSize = size; + + return *this; + } + + UdpSocketBuilder WithSendBufferSize(uint32_t size) + { + m_sendBufferSize = size; + + return *this; + } + + public: + std::unique_ptr Build() const + { + std::unique_ptr soc = std::make_unique(SocketType::Datagram); + + if (soc) + { + bool Error = + !soc->SetNonBlocking(!m_blocking) || + !soc->SetReuseAddr(m_reusable); + + if (!Error) + Error = m_bound && !soc->Bind(m_boundEndpoint); + if (!Error) + Error = !soc->SetMulticastLoopback(m_multicastLoopback) || !soc->SetMulticastTtl(m_multicastTtl); + + if (!Error) + { + for (const auto& Group : m_joinedGroups) + { + if (!soc->JoinMulticastGroup(IPAddress(Group, 0))) + { + Error = true; + break; + } + } + } + + if (!Error) + { + int32_t out_new_size; + if (m_receiveBufferSize > 0) + soc->SetReceiveBufferSize(m_receiveBufferSize, out_new_size); + if (m_sendBufferSize > 0) + soc->SetSendBufferSize(m_sendBufferSize, out_new_size); + } + + if (Error) + throw inl::RuntimeException("Couldnt create socket"); // make parameter a string depending on the error + return std::make_unique(); + } + return std::unique_ptr(nullptr); + } + + private: + bool m_blocking; + bool m_bound; + IPAddress m_boundEndpoint; + std::vector m_joinedGroups; + bool m_multicastLoopback; + uint8_t m_multicastTtl; + uint32_t m_receiveBufferSize; + bool m_reusable; + uint32_t m_sendBufferSize; + }; +} \ No newline at end of file diff --git a/include/Uri.hpp b/include/Uri.hpp new file mode 100644 index 0000000..90a7126 --- /dev/null +++ b/include/Uri.hpp @@ -0,0 +1,78 @@ +// https://github.com/mfichman/http + +#pragma once + +#include + +namespace std::net +{ + class Authority + { + public: + Authority(const std::string& user, const std::string& host, uint16_t port); + Authority(); + + const std::string& GetUser() const + { + return m_user; + } + + const std::string& GetHost() const + { + return m_host; + } + + uint16_t GetPort() const + { + return m_port; + } + + void SetUser(const std::string& user); + void SetHost(const std::string& host); + void SetPort(uint16_t port); + private: + std::string m_user; + std::string m_host; + uint16_t m_port; + }; + + class Uri { + public: + Uri(const char* value); + Uri(const std::string& value); + Uri(); + + const std::string& GetScheme() const + { + return m_scheme; + } + + const Authority& GetAuthority() const + { + return m_authority; + } + + const std::string& GetPath() const + { + return m_path; + } + + const std::string& GetHost() const + { + return m_authority.GetHost(); + } + + uint16_t GetPort() const + { + return m_authority.GetPort(); + } + + void SetScheme(const std::string& scheme); + void SetAuthority(const Authority& authority); + void SetPath(const std::string& path); + private: + std::string m_scheme; + Authority m_authority; + std::string m_path; + }; +} \ No newline at end of file diff --git a/include/Util.hpp b/include/Util.hpp new file mode 100644 index 0000000..df27cd5 --- /dev/null +++ b/include/Util.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include "Net.hpp" + +namespace std::net +{ + inline static std::vector Split(const std::string &str, const std::string &delimiter) + { + std::vector splited; + if (str.empty() && delimiter.empty()) + return std::vector(); + std::string::size_type lastPos = str.find_first_not_of(delimiter, 0); + std::string::size_type pos = str.find_first_of(delimiter, lastPos); + + while (std::string::npos != pos || std::string::npos != lastPos) + { + splited.push_back(str.substr(lastPos, pos - lastPos)); + lastPos = str.find_first_not_of(delimiter, pos); + pos = str.find_first_of(delimiter, lastPos); + } + return splited; + } + + sockaddr_in CreateAddress(uint32_t address, uint16_t port) + { + sockaddr_in addr; + std::memset(&addr, 0, sizeof(addr)); + addr.sin_addr.s_addr = htonl(address); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + + return addr; + } +} \ No newline at end of file diff --git a/include/Utility.hpp b/include/Utility.hpp deleted file mode 100644 index 13759cd..0000000 --- a/include/Utility.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef UTILITY_HPP -#define UTILITY_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "Defs.hpp" - -#include -#include -#include - -struct Utility -{ - static void Delete(void *pointer); - static void DeleteArray(void *pointer); - - struct BitConverter - { - static const std::vector &ToBytes(uint8 number); - static uint8 ToUint8(const std::vector &bytes, uint16 start_index = 0); - - static const std::vector &ToBytes(uint16 number); - static uint16 ToUint16(const std::vector &bytes, uint16 start_index = 0); - - static const std::vector &ToBytes(uint32 number); - static uint32 ToUint32(const std::vector &bytes, uint16 start_index = 0); - - static const std::vector &ToBytes(uint64 number); - static uint64 ToUint64(const std::vector &bytes, uint16 start_index = 0); - - static const std::vector &ToBytes(int8 number); - static int8 ToInt8(const std::vector &bytes, uint16 start_index = 0); - - static const std::vector &ToBytes(int16 number); - static int16 ToInt16(const std::vector &bytes, uint16 start_index = 0); - - static const std::vector &ToBytes(int32 number); - static int32 ToInt32(const std::vector &bytes, uint16 start_index = 0); - - static const std::vector &ToBytes(int64 number); - static int64 ToInt64(const std::vector &bytes, uint16 start_index = 0); - }; - - struct StringConverter - { - static const std::string &ToString(bool value); - static const std::string &ToString(uint8 value); - static const std::string &ToString(uint16 value); - static const std::string &ToString(uint32 value); - static const std::string &ToString(uint64 value); - static const std::string &ToString(int8 value); - static const std::string &ToString(int16 value); - static const std::string &ToString(int32 value); - static const std::string &ToString(int64 value); - static const std::string &ToString(const std::vector &bytes); - - static uint8 ToUint8(const std::string &str); - static uint16 ToUint16(const std::string &str); - static uint32 ToUint32(const std::string &str); - static uint64 ToUint64(const std::string &str); - static int8 ToInt8(const std::string &str); - static int16 ToInt16(const std::string &str); - static int32 ToInt32(const std::string &str); - static int64 ToInt64(const std::string &str); - - static const std::vector &ToBytes(const std::string &str); - - static const std::string &ToString(const std::vector &bytes, uint16 start_index = 0, uint16 lenght = 0); - - static const std::string &Trim(std::string &str, char ch); - static std::vector Split(const std::string &str, const std::string &delimiter); - }; - - struct IPUtil - { - static bool ValidIPV4(const std::string &ip); - }; - - struct ConfigReader - { - void ReadConfig(const std::string &file_name); - void ReadNodes(); - - const std::string &operator[](const std::string &key); - - private: - std::map nodes; - std::string file_content; - }; -}; - -#endif \ No newline at end of file diff --git a/include/VoidNetClient.hpp b/include/VoidNetClient.hpp deleted file mode 100644 index 712fd8b..0000000 --- a/include/VoidNetClient.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef VOID_NET_HPP -#define VOID_NET_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "Defs.hpp" -#include "Init.hpp" -#include "TcpClient.hpp" -#include "NetworkBuffer.hpp" -#include "NetworkMessage.hpp" - -#include -#include - -struct VoidNetClientAPI -{ - static bool Connect(const std::string &ip, uint16 port = default_client_port); - static void Disconnect(); - - static void SendMessageToServer(byte tag, byte subject, void *data); - static void SendMessageToID(uint16 id, byte tag, byte subject, void *data); - static void SendMessageToOthers(byte tag, byte subject, void *data); - static void SendMessageToAll(byte tag, byte subject, void *data); - static void SendMessageToAllAndMe(byte tag, byte subject, void *data); - - static void SendMessage(byte distribution_mode, uint16 destination_id, byte tag, byte subject, void *data); - - static void Receive(); - -private: - static void receive_data(); - - static TcpClient tcp_client; - static uint16 id; -}; - -#endif diff --git a/include/VoidNetServer.hpp b/include/VoidNetServer.hpp deleted file mode 100644 index 8bd5b68..0000000 --- a/include/VoidNetServer.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef VOID_NET_SERVER_HPP -#define VOID_NET_SERVER_HPP - -#ifdef _MSC_VER -#pragma once -#endif - -#include "TcpServer.hpp" - -struct VoidNetServer -{ - void StartServer(uint16 port); - void StopServer(); -private: - TcpServer server; -}; - -#endif \ No newline at end of file diff --git a/src/Config.cpp b/src/Config.cpp deleted file mode 100644 index 960e3fe..0000000 --- a/src/Config.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "Config.hpp" - -#include - -void Config::Initialize() -{ - Configuration.ReadConfig("config.dat"); - Configuration.ReadNodes(); -} - -inline void Config::SetUsingConsole(bool value) -{ - using_console = value; -} - -inline bool Config::GetUsingConsole() -{ - return using_console; -} - -inline void Config::SetLogToFile(bool value) -{ - log_to_file = value; -} - -inline bool Config::GetLogToFile() -{ - return log_to_file; -} diff --git a/src/Cookies.cpp b/src/Cookies.cpp new file mode 100644 index 0000000..fdf9241 --- /dev/null +++ b/src/Cookies.cpp @@ -0,0 +1,84 @@ +// https://github.com/mfichman/http + +#include "Cookies.hpp" +#include "Parse.hpp" + +#include + +namespace std::net +{ + ParseResult ParseName(const char* str) + { + return ParseUntil(str, [](char ch) + { + return isspace(ch) || ch == '='; + }); + } + + ParseResult ParseValue(const char* str) + { + return ParseUntil(str, [](char ch) + { + return ch == ';' || ch == '='; + }); + } + + ParseResult ParseSeparator(const char* str) + { + if (*str) + { + assert(*str == ';' || *str == '='); + return ParseWhitespace(str + 1); + } + else + { + auto result = ParseResult{}; + result.ch = str; + return result; + } + } + + Cookie ParseCookie(const char* str) + { + auto name = ParseName(str); + auto ch = ParseSeparator(name.ch).ch; + auto value = ParseValue(ch); + ch = ParseSeparator(value.ch).ch; + + auto cookie = Cookie(); + cookie.SetName(name.value); + cookie.SetValue(value.value); + while (*ch) + { + auto flag = ParseValue(ch); + if (flag.value == "Path") + { + ch = ParseSeparator(flag.ch).ch; + flag = ParseValue(ch); + cookie.SetPath(flag.value); + } + else if (flag.value == "HttpOnly") + cookie.SetHttpOnly(true); + else if (flag.value == "Secure") + cookie.SetSecure(true); + ch = ParseSeparator(flag.ch).ch; + } + return cookie; + } + + Cookie::Cookie(const std::string& text) + { + *this = ParseCookie(text.c_str()); + } + + const Cookie Cookies::operator[](const std::string & name) const + { + auto i = m_cookie.find(name); + return (i == m_cookie.end()) ? Cookie() : i->second; + } + + void Cookies::SetCookie(const Cookie& cookie) + { + m_cookie[cookie.GetName()] = cookie; + } +} \ No newline at end of file diff --git a/src/Handshake.cpp b/src/Handshake.cpp deleted file mode 100644 index 9c3698d..0000000 --- a/src/Handshake.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "Handshake.hpp" -#include "Utility.hpp" - -Handshake::Handshake() -{ - id = -2; -} - -Handshake::Handshake(uint16 id, byte con_code, byte distribution_mode) -{ - this->id = id; - this->con_code = con_code; - this->distribution_mode = distribution_mode; -} - -Handshake::~Handshake() -{ -} - -const std::vector& Handshake::EncodeHandshake(const Handshake & handshake) -{ - std::vector handshake_bytes; - - std::vector id = Utility::BitConverter::ToBytes(handshake.id); - std::vector con_mode = Utility::BitConverter::ToBytes(handshake.con_code); - std::vector type = Utility::BitConverter::ToBytes(static_cast(1)); - - handshake_bytes.insert(handshake_bytes.begin(), type.begin(), type.end()); - handshake_bytes.insert(handshake_bytes.begin(), id.begin(), id.end()); - handshake_bytes.insert(handshake_bytes.begin(), con_mode.begin(), con_mode.end()); - - return handshake_bytes; -} - -Handshake & Handshake::DecodeHandshake(const std::vector& bytes) -{ - Handshake handshake; - - handshake.id = Utility::BitConverter::ToUint16(bytes, 1); - handshake.con_code = Utility::BitConverter::ToUint8(bytes, 3); - - return handshake; -} - -const NetworkMessage & Handshake::HandshakeToNetworkMessage(const Handshake & handshake) -{ - NetworkMessage message; - message.sender = handshake.id; - message.tag = handshake.con_code; - message.subject = 1; - return message; -} - -const Handshake & Handshake::NetworkMessageToHandshake(const NetworkMessage & message) -{ - Handshake handshake; - handshake.id = message.sender; - handshake.con_code = message.tag; - return handshake; -} diff --git a/src/Headers.cpp b/src/Headers.cpp new file mode 100644 index 0000000..01ba18f --- /dev/null +++ b/src/Headers.cpp @@ -0,0 +1,22 @@ +// https://github.com/mfichman/http + +#include "Headers.hpp" + +namespace std::net +{ + std::string const Headers::HOST("Host"); + std::string const Headers::CONTENT_LENGTH("Content-Length"); + std::string const Headers::ACCEPT_ENCODING("Accept-Encoding"); + std::string const Headers::CONNECTION("Connection"); + + const std::string Headers::operator[](const std::string & name) const + { + auto i = m_header.find(name); + return (i == m_header.end()) ? "" : i->second; + } + + void Headers::AddHeader(std::string const& name, std::string const& value) + { + m_header.emplace(name, value); + } +} \ No newline at end of file diff --git a/src/Http.cpp b/src/Http.cpp new file mode 100644 index 0000000..47c0f38 --- /dev/null +++ b/src/Http.cpp @@ -0,0 +1,120 @@ +// https://github.com/mfichman/http + +#include "Http.hpp" + +#include +#include +#include + +#include "Socket.hpp" +#include "SecureSocket.hpp" + +#undef DELETE + +namespace std::net +{ + Response Http::Send(Request const& request) + { + // Send an HTTP request. Auto-fill the content-length headers. + std::string string = Str(request); + + uint16_t port = 0; + std::unique_ptr socket; + std::unique_ptr secure_socket; + bool secure = false; + if (request.GetUri().GetScheme() == "https") + { + secure_socket.reset(new SecureSocket()); + port = 443; + secure = true; + } + else if (request.GetUri().GetScheme() == "http") + { + socket.reset(new Socket(SocketType::Streaming)); + port = 80; + } + else + assert(!"unknown http scheme"); + + if (request.GetUri().GetPort()) + port = request.GetUri().GetPort(); + + secure ? secure_socket->Connect(IPAddress(request.GetUri().GetHost(), port)) : socket->Connect(IPAddress(request.GetUri().GetHost(), port)); + int32_t sent; + secure ? secure_socket->Recv((uint8_t*)string.c_str(), string.size(), sent) : socket->Send((uint8_t*)string.c_str(), string.size(), sent); + + std::vector buffer(16384); // 16 KiB + std::stringstream ss; + + int32_t read; + do + { + secure ? secure_socket->Recv((uint8_t*)&buffer[0], buffer.size(), read) : socket->Recv((uint8_t*)&buffer[0], buffer.size(), read); + ss.write(&buffer[0], read); + } + while (read > 0); + secure ? secure_socket->Close() : socket->Close(); + + return Response(ss.str()); + } + + Response Http::Get(std::string const& path, std::string const& data) + { + // Shortcut for simple GET requests + Request request; + request.SetMethod(Method::GET); + request.SetUri(Uri(path)); + request.SetData(data); + return Send(request); + } + + Response Http::Post(std::string const& path, std::string const& data) + { + // Shortcut for simple POST requests + Request request; + request.SetMethod(Method::POST); + request.SetUri(Uri(path)); + request.SetData(data); + return Send(request); + } + + static std::string str_impl(Method method) { + switch (method) + { + case Method::GET: + return "GET"; + case Method::HEAD: + return "HEAD"; + case Method::POST: + return "POST"; + case Method::PUT: + return "PUT"; + case Method::DELETE: + return "DELETE"; + case Method::TRACE: + return "TRACE"; + case Method::CONNECT: + return "CONNECT"; + default: + assert(!"unknown request method"); + } + return ""; + } + + std::string Http::Str(Request const& request) + { + // Serialize a request to a string + std::stringstream ss; + auto path = request.GetPath().empty() ? "/" : request.GetPath(); + ss << str_impl(request.GetMethod()) << ' ' << path << " HTTP/1.1\n"; + ss << Headers::HOST << ": " << request.GetUri().GetHost() << "\n"; + ss << Headers::CONTENT_LENGTH << ": " << request.GetData().size() << "\n"; + ss << Headers::CONNECTION << ": close\n"; + ss << Headers::ACCEPT_ENCODING << ": identity\n"; + for (auto header : request.GetHeaders()) + ss << header.first << ": " << header.second << "\n"; + ss << "\n"; + ss << request.GetData(); + return ss.str(); + } +} \ No newline at end of file diff --git a/src/IPAddress.cpp b/src/IPAddress.cpp new file mode 100644 index 0000000..4273ad7 --- /dev/null +++ b/src/IPAddress.cpp @@ -0,0 +1,62 @@ +#include "IPAddress.hpp" + +namespace std::net +{ + const IPAddress IPAddress::None; + const IPAddress IPAddress::Any(0, 0, 0, 0); + const IPAddress IPAddress::LocalHost(127, 0, 0, 1); + const IPAddress IPAddress::Broadcast(255, 255, 255, 255); + + std::string IPAddress::ToString() const + { + in_addr address; + address.s_addr = m_address; + + return inet_ntoa(address); + } + + void IPAddress::Resolve(const std::string& address) + { + m_address = 0; + m_valid = false; + + if (address == "255.255.255.255") + { + // The broadcast address needs to be handled explicitly, + // because it is also the value returned by inet_addr on error + m_address = INADDR_BROADCAST; + m_valid = true; + } + else if (address == "0.0.0.0") + { + m_address = INADDR_ANY; + m_valid = true; + } + else + { + uint32_t ip = inet_addr(address.c_str()); + if (ip != INADDR_NONE) + { + m_address = ip; + m_valid = true; + } + else + { + addrinfo hints; + std::memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + addrinfo* result = NULL; + if (getaddrinfo(address.c_str(), NULL, &hints, &result) == 0) + { + if (result) + { + ip = reinterpret_cast(result->ai_addr)->sin_addr.s_addr; + freeaddrinfo(result); + m_address = ip; + m_valid = true; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Init.cpp b/src/Init.cpp deleted file mode 100644 index d8dedda..0000000 --- a/src/Init.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "Init.hpp" -#include "Config.hpp" - -#include - -#ifdef _MSC_VER -bool Initialization::Initialize() -{ - uint16 code = WSAStartup(MAKEWORD(2, 2), &wsa_data); - if (code != 0) - { - if (Config::GetUsingConsole()) - std::cerr << code << std::endl; // display some more information too - return false; - } - return true; -} - -const WSADATA &Initialization::GetData() -{ - return wsa_data; -} -#endif \ No newline at end of file diff --git a/src/NetworkBuffer.cpp b/src/NetworkBuffer.cpp deleted file mode 100644 index ecf53a3..0000000 --- a/src/NetworkBuffer.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "NetworkBuffer.hpp" - -NetworkBuffer::NetworkBuffer() -{ -} - -NetworkBuffer::~NetworkBuffer() -{ -} \ No newline at end of file diff --git a/src/NetworkMessage.cpp b/src/NetworkMessage.cpp deleted file mode 100644 index 49a8965..0000000 --- a/src/NetworkMessage.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "NetworkMessage.hpp" -#include "Utility.hpp" -#include "Serializer.hpp" -#include "Handshake.hpp" - -#include - -NetworkMessage::NetworkMessage() -{ -} - -NetworkMessage::NetworkMessage(uint16 sender, byte distribution_mode, uint16 destination_id, byte tag, byte subject) : - sender(sender), distribution_mode(distribution_mode), destination_id(destination_id), tag(tag), subject(subject) -{ -} - -NetworkMessage::NetworkMessage(const NetworkBuffer &buffer) -{ - *this = DecodeMessage(buffer); -} - -NetworkMessage::~NetworkMessage() -{ -} - -const NetworkBuffer &NetworkMessage::EncodeMessage(const NetworkMessage &message) -{ - if (message.valid) - { - NetworkBuffer net_buffer; - - if (!IS_HANDSHAKE(message)) - { - std::vector sender = Utility::BitConverter::ToBytes(message.sender); - std::vector distribution_mode = Utility::BitConverter::ToBytes(message.distribution_mode); - std::vector destination_id = Utility::BitConverter::ToBytes(message.destination_id); - std::vector tag = Utility::BitConverter::ToBytes(message.tag); - std::vector subject = Utility::BitConverter::ToBytes(message.subject); - std::vector data; - if (message.data != nullptr) - data = Serializer::to_bytes(message.data); - std::vector type = Utility::BitConverter::ToBytes(0); - - net_buffer.body.insert(net_buffer.body.begin(), type.begin(), type.end()); - net_buffer.body.insert(net_buffer.body.begin(), sender.begin(), sender.end()); - net_buffer.body.insert(net_buffer.body.begin(), distribution_mode.begin(), distribution_mode.end()); - net_buffer.body.insert(net_buffer.body.begin(), destination_id.begin(), destination_id.end()); - net_buffer.body.insert(net_buffer.body.begin(), tag.begin(), tag.end()); - net_buffer.body.insert(net_buffer.body.begin(), subject.begin(), subject.end()); - if (message.data != nullptr && data.size() > 0) - net_buffer.body.insert(net_buffer.body.begin(), data.begin(), data.end()); - net_buffer.header = Utility::BitConverter::ToBytes(sender.size() + distribution_mode.size() + destination_id.size() + - tag.size() + subject.size() + data.size()); - net_buffer.valid = true; - } - else - { - std::vector handshake_bytes = Handshake::EncodeHandshake(Handshake::NetworkMessageToHandshake(message)); - std::vector type = Utility::BitConverter::ToBytes(static_cast(1)); - handshake_bytes.insert(handshake_bytes.begin(), type.begin(), type.end()); - net_buffer.header = Utility::BitConverter::ToBytes(handshake_bytes.size()); - net_buffer.body = handshake_bytes; - } - return net_buffer; - } - return NetworkBuffer(); -} - -const NetworkMessage &NetworkMessage::DecodeMessage(const NetworkBuffer &buffer) -{ - if (buffer.valid) - { - byte type = buffer.body[0]; - switch (type) - { - case 0: - { - NetworkMessage message; - message.sender = Utility::BitConverter::ToUint16(buffer.body, 1); - message.distribution_mode = buffer.body[3]; - message.destination_id = Utility::BitConverter::ToUint16(buffer.body, 4); - message.tag = buffer.body[6]; - message.subject = Utility::BitConverter::ToUint8(buffer.body, 7); - message.valid = message.sender != -2 && message.tag != ConnectTag && message.tag != DisconnectTag; - if (Utility::BitConverter::ToInt32(buffer.header) < 9) - return message; - void *object; - object = Serializer::from_bytes(buffer.body, object); - return message; - } - case 1: - { - NetworkMessage message; - message.sender = Utility::BitConverter::ToUint16(buffer.body, 1); - message.tag = Utility::BitConverter::ToUint8(buffer.body, 3); - message.subject = 1; - message.valid = message.sender != -2 && IS_HANDSHAKE(message); - return message; - break; - } - default: - { - //type not supported - throw std::runtime_error("NetworkMessage - Decoding version not supported"); - } - } - - } - return NetworkMessage(); -} \ No newline at end of file diff --git a/src/Parse.cpp b/src/Parse.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/Request.cpp b/src/Request.cpp new file mode 100644 index 0000000..88e7f91 --- /dev/null +++ b/src/Request.cpp @@ -0,0 +1,31 @@ +// https://github.com/mfichman/http + +#include "Request.hpp" + +namespace std::net +{ + const std::string Request::GetHeaderElement(const std::string& name) const + { + return m_headers[name]; + } + + void Request::SetMethod(Method method) + { + m_method = method; + } + + void Request::SetUri(const Uri& uri) + { + m_uri = uri; + } + + void Request::SetData(const std::string& data) + { + m_data = data; + } + + void Request::AddHeader(const std::string& name, const std::string& value) + { + m_headers.AddHeader(name, value); + } +} \ No newline at end of file diff --git a/src/Response.cpp b/src/Response.cpp new file mode 100644 index 0000000..d274012 --- /dev/null +++ b/src/Response.cpp @@ -0,0 +1,97 @@ +// https://github.com/mfichman/http + +#include "Response.hpp" +#include "Parse.hpp" + +namespace std::net +{ + static ParseResult ParseStatus(const char* str) + { + ParseResult result + { + }; + + auto code = ParseToken(str); + + result.value = (HttpStatus)std::atoi(code.value.c_str()); + result.ch = code.ch; + return result; + } + + Response ParseResponse(const char* str) + { + // Parse an HTTP response + auto version = ParseToken(str); + auto code = ParseStatus(version.ch); + auto message = ParseUntil(code.ch, [](char ch) + { + return ch == '\r'; + }); + + auto response = Response(); + if (version.value != "HTTP/1.1") + throw std::runtime_error("bad HTTP version"); + + auto ch = parseCrLf(message.ch).ch; + while (*ch != '\0' && *ch != '\r') + { + auto name = ParseUntil(ch, [](char ch) + { + return ch == ':'; + }); + + if (*name.ch) + name.ch++; // For ":" + auto ws = ParseWhile(name.ch, isspace); + auto value = ParseUntil(ws.ch, [](char ch) + { + return ch == '\r'; + }); + + response.SetHeader(name.value, value.value); + if (name.value == "Set-Cookie") + response.SetCookie(Cookie(value.value)); + ch = parseCrLf(value.ch).ch; + } + ch = parseCrLf(ch).ch; + + response.SetStatus(code.value); + response.SetData(ch); + return response; + } + + Response::Response(const std::string& response) + { + *this = ParseResponse(response.c_str()); + } + + const std::string Response::GetHeader(const std::string& name) const + { + return m_headers[name]; + } + + const Cookie Response::GetCookie(const std::string& name) const + { + return m_cookies[name]; + } + + void Response::SetStatus(HttpStatus status) + { + m_status = status; + } + + void Response::SetData(const std::string& data) + { + m_data = data; + } + + void Response::SetHeader(const std::string& name, const std::string& value) + { + m_headers.AddHeader(name, value); + } + + void Response::SetCookie(const Cookie& cookie) + { + m_cookies.SetCookie(cookie); + } +} \ No newline at end of file diff --git a/src/SecureSocket.cpp b/src/SecureSocket.cpp new file mode 100644 index 0000000..39a2e6a --- /dev/null +++ b/src/SecureSocket.cpp @@ -0,0 +1,163 @@ +#include "SecureSocket.hpp" + +#include + +namespace inl::net::sockets +{ + /*SecureSocket::SecureSocket() + : m_context(0), m_conn(0), m_eof(false) + { + m_socket = std::make_unique(SocketType::Streaming); + + // Intitialize the SSL client-side socket + SSL_library_init(); + SSL_load_error_strings(); + ERR_load_BIO_strings(); + } + + bool SecureSocket::Connect(const IPAddress & addrStr) + { + if (!m_context) + { + m_context = SSL_CTX_new(SSLv23_client_method()); + assert(m_context); + } + if (!m_conn) + { + m_conn = SSL_new(m_context); + assert(m_conn); + + m_in = BIO_new(BIO_s_mem()); + m_out = BIO_new(BIO_s_mem()); + SSL_set_bio(m_conn, m_in, m_out); + SSL_set_connect_state(m_conn); + } + + return m_socket->Connect(addrStr); + } + + bool SecureSocket::Close() const + { + return m_socket->Close(); + } + + bool SecureSocket::HasPendingData(uint32_t & pendingDataSize) const + { + return m_socket->HasPendingData(pendingDataSize); + } + + bool SecureSocket::Send(uint8_t* data, int32_t count, int32_t &sent, int flags) + { + sent = SSL_write(m_conn, data, count); + SendFromBio(); // Write data if available + if (sent < 0) + { + HandleReturn(sent); + return true; + } + return sent > 0; + } + + bool SecureSocket::SendRaw(uint8_t * buf, size_t len, int flags) + { + int32_t sent; + return m_socket->Send(buf, len, sent) && sent == len; + + } + + bool SecureSocket::SendFromBio(int flags) + { + uint8_t buf[4096]; + size_t pending = BIO_ctrl_pending(m_out); + if (!pending) + return true; + size_t bytes = BIO_read(m_out, buf, sizeof(buf)); + if (bytes > 0) + return SendRaw(buf, bytes, flags); + else if (bytes == -1 || bytes == 0) + return true; + return false; + } + + bool SecureSocket::RecvToBio(int flags) + { + uint8_t buf[4096]; + size_t bytes = m_socket->Recv(buf, sizeof(buf), flags); + if (bytes > 0) + { + size_t written = BIO_write(m_in, buf, int(bytes)); + assert(bytes == written); + return true; + } + else if (bytes == 0) + { + // No data + m_eof = true; + return true; + } + return false; + } + + void SecureSocket::HandleReturn(size_t ret) + { + int32_t err = SSL_get_error(m_conn, ret); + if (SSL_ERROR_WANT_WRITE == err) + SendFromBio(); + else if (SSL_ERROR_WANT_READ == err) + RecvToBio(); + else if (SSL_ERROR_SSL == err) + throw inl::RuntimeException(); + else + assert(!"unexpected error"); + } + + bool SecureSocket::Recv(uint8_t* data, int32_t count, int32_t &read, int flags) + { + read = SSL_read(m_conn, data, count); + if (read < 0) + { + HandleReturn(read); + if (m_eof) + return false; + } + return read > 0; + } + + bool SecureSocket::Wait(SocketWaitConditions cond, std::chrono::milliseconds t) const + { + return m_socket->Wait(cond, t); + } + + SocketConnectionState SecureSocket::GetConnectionState() const + { + return m_socket->GetConnectionState(); + } + + void SecureSocket::GetAddress(IPAddress & outAddr) const + { + m_socket->GetAddress(outAddr); + } + + int32_t SecureSocket::GetPort() const + { + return m_socket->GetPort(); + } + + void SecureSocket::UseCertificateFile(std::string const & path) + { + if (!m_context) + assert(!"not initialized yet"); + if (SSL_CTX_use_certificate_file(m_context, path.c_str(), SSL_FILETYPE_PEM) <= 0) + throw inl::RuntimeException(); + } + + void SecureSocket::UsePrivateKeyFile(std::string const & path) + { + if (!m_context) + assert(!"not initialized yet"); + if (SSL_CTX_use_PrivateKey_file(m_context, path.c_str(), SSL_FILETYPE_PEM) <= 0) + throw inl::RuntimeException(); + if (!SSL_CTX_check_private_key(m_context)) + throw inl::RuntimeException(); + }*/ +} \ No newline at end of file diff --git a/src/Serializer.cpp b/src/Serializer.cpp deleted file mode 100644 index 01d1fca..0000000 --- a/src/Serializer.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "Serializer.hpp" - -template const std::vector &Serializer::to_bytes(const T& object) -{ - std::vector bytes; - - const byte *begin = reinterpret_cast(std::addressof(object)); - const byte *end = begin + sizeof(T); - std::copy(begin, end, bytes.begin()); - - return bytes; -} - -template const T& Serializer::from_bytes(const std::vector &bytes, T& object) -{ - static_assert(std::is_trivially_copyable::value, "not a TriviallyCopyable type"); - std::copy(bytes.begin(), bytes.end(), reinterpret_cast(std::addressof(object))); - - return object; -} \ No newline at end of file diff --git a/src/Socket.cpp b/src/Socket.cpp new file mode 100644 index 0000000..838ad30 --- /dev/null +++ b/src/Socket.cpp @@ -0,0 +1,511 @@ +#include "Socket.hpp" +#include "IPAddress.hpp" + +namespace std::net +{ + void Socket::init() + { + if (GetSocketType() == SocketType::Unknown) + throw std::invalid_argument("Unknown socket type"); + + if (m_socket == INVALID_SOCKET) + { + m_socket = socket(AF_INET, (int)GetSocketType(), 0); + + if (m_socket == INVALID_SOCKET) + throw std::runtime_error("Couldnt create socket"); + } + + if (GetSocketType() == SocketType::Streaming) + { + int yes = 1; + // Disable the Nagle algorithm (i.e. removes buffering of TCP packets) + setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), sizeof(yes)); + + // On Mac OS X, disable the SIGPIPE signal on disconnection +#if defined(__APPLE__) && defined(__MACH__) + setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast(&yes), sizeof(yes)); +#endif + } + else + { + // Enable broadcast by default for UDP sockets + int yes = 1; + setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast(&yes), sizeof(yes)); + } + } + + bool Socket::Close() + { + if (m_socket != INVALID_SOCKET) + { + int32_t error = closesocket(m_socket); + m_socket = INVALID_SOCKET; + return error == 0; + } + return false; + } + + bool Socket::Bind(const IPAddress &addr) + { + sockaddr_in addr_in = addr.ToCAddr(); + return bind(m_socket, (sockaddr*)&addr_in, sizeof(sockaddr_in)) == 0; + } + + bool Socket::Connect(const IPAddress& addr) + { + sockaddr_in addr_in = addr.ToCAddr(); + int32_t Return = connect(m_socket, (sockaddr*)&addr_in, sizeof(sockaddr_in)); + SocketErrors Error = TranslateErrorCode(Return); + + // "would block" is not an error + return ((Error == SocketErrors::SE_NO_ERROR) || (Error == SocketErrors::SE_EWOULDBLOCK)); + } + + bool Socket::WaitForPendingConnection(bool& hasPendingConnection, std::chrono::milliseconds t) + { + bool hasSucceeded = false; + hasPendingConnection = false; + + if (HasState(SocketParam::HasError) == SocketReturn::No) + { + SocketReturn state = HasState(SocketParam::CanRead, t); + + hasSucceeded = state != SocketReturn::EncounteredError; + hasPendingConnection = state == SocketReturn::Yes; + } + + return hasSucceeded; + } + + bool Socket::HasPendingData(uint32_t& pendingDataSize) + { + pendingDataSize = 0; + + if (HasState(SocketParam::CanRead) == SocketReturn::Yes) + { + if (ioctl(m_socket, FIONREAD, + #if defined(_WIN32) + (u_long*) + #endif + &pendingDataSize) == 0) + return (pendingDataSize > 0); + } + + return false; + } + + std::unique_ptr Socket::Accept() + { + SOCKET newSocket = accept(m_socket, nullptr, nullptr); + + if (newSocket != INVALID_SOCKET) + { + return std::make_unique(newSocket, GetSocketType()); + } + + return nullptr; + } + + bool Socket::SendTo(const uint8_t* data, int32_t count, int32_t& sent, const IPAddress& addrDest) + { + sockaddr_in addr = addrDest.ToCAddr(); + sent = sendto(m_socket, (const char*)data, count, 0, (sockaddr*)&addr, sizeof(sockaddr_in)); + + bool result = sent >= 0; + if (result) + m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); + + return result; + } + + bool Socket::Send(const uint8_t* data, int32_t count, int32_t& sent) + { + sent = send(m_socket, (const char*)data, count, 0); + + bool result = sent != SOCKET_ERROR; + if (result) + m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); + + return result; + } + + bool Socket::RecvFrom(uint8_t* data, int32_t size, int32_t& read, IPAddress& srcAddr, SocketReceiveFlags flags) + { + socklen_t len = sizeof(sockaddr_in); + sockaddr_in addr = srcAddr.ToCAddr(); + const int translatedFlags = TranslateFlags(flags); + + read = recvfrom(m_socket, (char*)data, size, translatedFlags, (sockaddr*)&addr, &len); + + if (read < 0 && TranslateErrorCode(read) == SocketErrors::SE_EWOULDBLOCK) + read = 0; + else if (read <= 0) // 0 means gracefully closed + { + read = 0; + return false; + } + + m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); + + return true; + } + + bool Socket::Recv(uint8_t* data, int32_t size, int32_t& read, SocketReceiveFlags flags) + { + const int translatedFlags = TranslateFlags(flags); + read = recv(m_socket, (char*)data, size, translatedFlags); + + if (read < 0 && TranslateErrorCode(read) == SocketErrors::SE_EWOULDBLOCK) + read = 0; + else if (read <= 0) // 0 means gracefully closed + { + read = 0; + return false; + } + + m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); + + return true; + } + + bool Socket::Wait(SocketWaitConditions cond, std::chrono::milliseconds t) + { + if ((cond == SocketWaitConditions::WaitForRead) || (cond == SocketWaitConditions::WaitForReadOrWrite)) + { + if (HasState(SocketParam::CanRead, t) == SocketReturn::Yes) + return true; + } + + if ((cond == SocketWaitConditions::WaitForWrite) || (cond == SocketWaitConditions::WaitForReadOrWrite)) + { + if (HasState(SocketParam::CanWrite, t) == SocketReturn::Yes) + return true; + } + + return false; + } + + SocketConnectionState Socket::GetConnectionState() + { + SocketConnectionState currentState = SocketConnectionState::ConnectionError; + + if (HasState(SocketParam::HasError) == SocketReturn::No) + { + if (std::chrono::system_clock::now().time_since_epoch().count() - m_lastActivityTime > std::chrono::milliseconds(5).count()) + { + SocketReturn writeState = HasState(SocketParam::CanWrite, std::chrono::milliseconds(1)); + SocketReturn readState = HasState(SocketParam::CanRead, std::chrono::milliseconds(1)); + + if (writeState == SocketReturn::Yes || readState == SocketReturn::Yes) + { + currentState = SocketConnectionState::Connected; + m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count(); + } + else if (writeState == SocketReturn::No && readState == SocketReturn::No) + currentState = SocketConnectionState::NotConnected; + } + else + currentState = SocketConnectionState::Connected; + } + + return currentState; + } + + void Socket::GetAddress(IPAddress& outAddr) + { + struct sockaddr_in addr; + socklen_t Size = sizeof(sockaddr_in); + + if (getsockname(m_socket, (sockaddr*)&addr, &Size) != 0) + return; + + outAddr = IPAddress(inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + } + + bool Socket::GetPeerAddress(IPAddress& outAddr) + { + struct sockaddr_in addr; + socklen_t size = sizeof(sockaddr_in); + + int result = getpeername(m_socket, (sockaddr*)&addr, &size); + if (result != 0) + return false; + + outAddr = IPAddress(inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + + return result == 0; + } + + bool Socket::SetNonBlocking(bool isNonBlocking) + { +#if PLATFORM_HTML5 // if we have more platforms later (html5, android, ios) later we need to do some changes to networking + throw std::exception("Can't have blocking sockets on HTML5"); + return false; +#else + +#if _WIN32 + return ioctl(m_socket, FIONBIO, (u_long*)&isNonBlocking) == 0; +#else + int flags = fcntl(m_socket, F_GETFL, 0); + flags = isNonBlocking ? flags | O_NONBLOCK : flags ^ (flags & O_NONBLOCK); + int err = fcntl(m_socket, F_SETFL, flags); + return (err == 0 ? true : false); +#endif +#endif + } + + bool Socket::JoinMulticastGroup(const IPAddress& addrStr) + { + sockaddr_in addr = addrStr.ToCAddr(); + + ip_mreq imr; + imr.imr_interface.s_addr = INADDR_ANY; + imr.imr_multiaddr = addr.sin_addr; + + return (setsockopt(m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&imr, sizeof(imr)) == 0); + } + + bool Socket::LeaveMulticastGroup(const IPAddress& addrStr) + { + sockaddr_in addr = addrStr.ToCAddr(); + + ip_mreq imr; + imr.imr_interface.s_addr = INADDR_ANY; + imr.imr_multiaddr = addr.sin_addr; + + return (setsockopt(m_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&imr, sizeof(imr)) == 0); + } + + bool Socket::SetLinger(bool shouldLinger, int32_t t) + { + linger ling; + ling.l_onoff = shouldLinger; + ling.l_linger = t; + + return setsockopt(m_socket, SOL_SOCKET, SO_LINGER, (char*)&ling, sizeof(ling)) == 0; + } + + bool Socket::SetSendBufferSize(int32_t size, int32_t& newSize) + { + socklen_t len = sizeof(int32_t); + bool success = setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char*)&size, sizeof(int32_t)) == 0; + + getsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char*)&newSize, &len); + + return success; + } + + bool Socket::SetReceiveBufferSize(int32_t size, int32_t& newSize) + { + socklen_t len = sizeof(int32_t); + bool success = setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)&size, sizeof(int32_t)) == 0; + + getsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)&newSize, &len); + + return success; + } + + uint32_t Socket::GetPort() + { + sockaddr_in addr; + socklen_t size = sizeof(sockaddr_in); + if (getsockname(m_socket, (sockaddr*)&addr, &size) != 0) + return 0; // invalid port + return ntohs(addr.sin_port); + } + + SocketReturn Socket::HasState(SocketParam state, std::chrono::milliseconds t) + { + timeval time; + time.tv_sec = t.count(); + time.tv_usec = t.count() * 1000 + t.count(); + + fd_set socketSet; + + FD_ZERO(&socketSet); + FD_SET(m_socket, &socketSet); + + int32_t SelectStatus = 0; + switch (state) + { + case SocketParam::CanRead: + SelectStatus = select(m_socket + 1, &socketSet, nullptr, nullptr, &time); + break; + + case SocketParam::CanWrite: + SelectStatus = select(m_socket + 1, nullptr, &socketSet, nullptr, &time); + break; + + case SocketParam::HasError: + SelectStatus = select(m_socket + 1, nullptr, nullptr, &socketSet, &time); + break; + } + + return SelectStatus > 0 ? SocketReturn::Yes : + SelectStatus == 0 ? SocketReturn::No : + SocketReturn::EncounteredError; + } + + SocketErrors Socket::TranslateErrorCode(int32_t code) + { +#if !_WIN32 + if (code == SOCKET_ERROR) + { + return SE_SOCKET_ERROR; + } + + switch (code) + { + case 0: return SocketErrors::SE_NO_ERROR; + case EINTR: return SocketErrors::SE_EINTR; + case EBADF: return SocketErrors::SE_EBADF; + case EACCES: return SocketErrors::SE_EACCES; + case EFAULT: return SocketErrors::SE_EFAULT; + case EINVAL: return SocketErrors::SE_EINVAL; + case EMFILE: return SocketErrors::SE_EMFILE; + case EWOULDBLOCK: return SocketErrors::SE_EWOULDBLOCK; + case EINPROGRESS: return SocketErrors::SE_EINPROGRESS; + case EALREADY: return SocketErrors::SE_EALREADY; + case ENOTSOCK: return SocketErrors::SE_ENOTSOCK; + case EDESTADDRREQ: return SocketErrors::SE_EDESTADDRREQ; + case EMSGSIZE: return SocketErrors::SE_EMSGSIZE; + case EPROTOTYPE: return SocketErrors::SE_EPROTOTYPE; + case ENOPROTOOPT: return SocketErrors::SE_ENOPROTOOPT; + case EPROTONOSUPPORT: return SocketErrors::SE_EPROTONOSUPPORT; + case ESOCKTNOSUPPORT: return SocketErrors::SE_ESOCKTNOSUPPORT; + case EOPNOTSUPP: return SocketErrors::SE_EOPNOTSUPP; + case EPFNOSUPPORT: return SocketErrors::SE_EPFNOSUPPORT; + case EAFNOSUPPORT: return SocketErrors::SE_EAFNOSUPPORT; + case EADDRINUSE: return SocketErrors::SE_EADDRINUSE; + case EADDRNOTAVAIL: return SocketErrors::SE_EADDRNOTAVAIL; + case ENETDOWN: return SocketErrors::SE_ENETDOWN; + case ENETUNREACH: return SocketErrors::SE_ENETUNREACH; + case ENETRESET: return SocketErrors::SE_ENETRESET; + case ECONNABORTED: return SocketErrors::SE_ECONNABORTED; + case ECONNRESET: return SocketErrors::SE_ECONNRESET; + case ENOBUFS: return SocketErrors::SE_ENOBUFS; + case EISCONN: return SocketErrors::SE_EISCONN; + case ENOTCONN: return SocketErrors::SE_ENOTCONN; + case ESHUTDOWN: return SocketErrors::SE_ESHUTDOWN; + case ETOOMANYREFS: return SocketErrors::SE_ETOOMANYREFS; + case ETIMEDOUT: return SocketErrors::SE_ETIMEDOUT; + case ECONNREFUSED: return SocketErrors::SE_ECONNREFUSED; + case ELOOP: return SocketErrors::SE_ELOOP; + case ENAMETOOLONG: return SocketErrors::SE_ENAMETOOLONG; + case EHOSTDOWN: return SocketErrors::SE_EHOSTDOWN; + case EHOSTUNREACH: return SocketErrors::SE_EHOSTUNREACH; + case ENOTEMPTY: return SocketErrors::SE_ENOTEMPTY; + case EUSERS: return SocketErrors::SE_EUSERS; + case EDQUOT: return SocketErrors::SE_EDQUOT; + case ESTALE: return SocketErrors::SE_ESTALE; + case EREMOTE: return SocketErrors::SE_EREMOTE; + case ENODEV: return SocketErrors::SE_NODEV; +#if !PLATFORM_HAS_NO_EPROCLIM + case EPROCLIM: return SocketErrors::SE_EPROCLIM; +#endif + // case EDISCON: return SE_EDISCON; + // case SYSNOTREADY: return SE_SYSNOTREADY; + // case VERNOTSUPPORTED: return SE_VERNOTSUPPORTED; + // case NOTINITIALISED: return SE_NOTINITIALISED; + +#if PLATFORM_HAS_BSD_SOCKET_FEATURE_GETHOSTNAME + case HOST_NOT_FOUND: return SocketErrors::SE_HOST_NOT_FOUND; + case TRY_AGAIN: return SocketErrors::SE_TRY_AGAIN; + case NO_RECOVERY: return SocketErrors::SE_NO_RECOVERY; +#endif + + // case NO_DATA: return SE_NO_DATA; + // case : return SE_UDP_ERR_PORT_UNREACH; //@TODO Find it's replacement + } + + return SocketErrors::SE_EINVAL; +#else + // handle the generic -1 error + if (code == SOCKET_ERROR) + { + return SocketErrors::SE_SOCKET_ERROR; + } + + switch (code) + { + case 0: return SocketErrors::SE_NO_ERROR; + case ERROR_INVALID_HANDLE: return SocketErrors::SE_ECONNRESET; // invalid socket handle catch + case WSAEINTR: return SocketErrors::SE_EINTR; + case WSAEBADF: return SocketErrors::SE_EBADF; + case WSAEACCES: return SocketErrors::SE_EACCES; + case WSAEFAULT: return SocketErrors::SE_EFAULT; + case WSAEINVAL: return SocketErrors::SE_EINVAL; + case WSAEMFILE: return SocketErrors::SE_EMFILE; + case WSAEWOULDBLOCK: return SocketErrors::SE_EWOULDBLOCK; + case WSAEINPROGRESS: return SocketErrors::SE_EINPROGRESS; + case WSAEALREADY: return SocketErrors::SE_EALREADY; + case WSAENOTSOCK: return SocketErrors::SE_ENOTSOCK; + case WSAEDESTADDRREQ: return SocketErrors::SE_EDESTADDRREQ; + case WSAEMSGSIZE: return SocketErrors::SE_EMSGSIZE; + case WSAEPROTOTYPE: return SocketErrors::SE_EPROTOTYPE; + case WSAENOPROTOOPT: return SocketErrors::SE_ENOPROTOOPT; + case WSAEPROTONOSUPPORT: return SocketErrors::SE_EPROTONOSUPPORT; + case WSAESOCKTNOSUPPORT: return SocketErrors::SE_ESOCKTNOSUPPORT; + case WSAEOPNOTSUPP: return SocketErrors::SE_EOPNOTSUPP; + case WSAEPFNOSUPPORT: return SocketErrors::SE_EPFNOSUPPORT; + case WSAEAFNOSUPPORT: return SocketErrors::SE_EAFNOSUPPORT; + case WSAEADDRINUSE: return SocketErrors::SE_EADDRINUSE; + case WSAEADDRNOTAVAIL: return SocketErrors::SE_EADDRNOTAVAIL; + case WSAENETDOWN: return SocketErrors::SE_ENETDOWN; + case WSAENETUNREACH: return SocketErrors::SE_ENETUNREACH; + case WSAENETRESET: return SocketErrors::SE_ENETRESET; + case WSAECONNABORTED: return SocketErrors::SE_ECONNABORTED; + case WSAECONNRESET: return SocketErrors::SE_ECONNRESET; + case WSAENOBUFS: return SocketErrors::SE_ENOBUFS; + case WSAEISCONN: return SocketErrors::SE_EISCONN; + case WSAENOTCONN: return SocketErrors::SE_ENOTCONN; + case WSAESHUTDOWN: return SocketErrors::SE_ESHUTDOWN; + case WSAETOOMANYREFS: return SocketErrors::SE_ETOOMANYREFS; + case WSAETIMEDOUT: return SocketErrors::SE_ETIMEDOUT; + case WSAECONNREFUSED: return SocketErrors::SE_ECONNREFUSED; + case WSAELOOP: return SocketErrors::SE_ELOOP; + case WSAENAMETOOLONG: return SocketErrors::SE_ENAMETOOLONG; + case WSAEHOSTDOWN: return SocketErrors::SE_EHOSTDOWN; + case WSAEHOSTUNREACH: return SocketErrors::SE_EHOSTUNREACH; + case WSAENOTEMPTY: return SocketErrors::SE_ENOTEMPTY; + case WSAEPROCLIM: return SocketErrors::SE_EPROCLIM; + case WSAEUSERS: return SocketErrors::SE_EUSERS; + case WSAEDQUOT: return SocketErrors::SE_EDQUOT; + case WSAESTALE: return SocketErrors::SE_ESTALE; + case WSAEREMOTE: return SocketErrors::SE_EREMOTE; + case WSAEDISCON: return SocketErrors::SE_EDISCON; + case WSASYSNOTREADY: return SocketErrors::SE_SYSNOTREADY; + case WSAVERNOTSUPPORTED: return SocketErrors::SE_VERNOTSUPPORTED; + case WSANOTINITIALISED: return SocketErrors::SE_NOTINITIALISED; + case WSAHOST_NOT_FOUND: return SocketErrors::SE_HOST_NOT_FOUND; + case WSATRY_AGAIN: return SocketErrors::SE_TRY_AGAIN; + case WSANO_RECOVERY: return SocketErrors::SE_NO_RECOVERY; + case WSANO_DATA: return SocketErrors::SE_NO_DATA; + // case : return SE_UDP_ERR_PORT_UNREACH; + } + + return SocketErrors::SE_NO_ERROR; +#endif + } + + int Socket::TranslateFlags(SocketReceiveFlags flags) + { + int translatedFlags = 0; + + if ((int)flags & (int)SocketReceiveFlags::Peek) + { + translatedFlags |= MSG_PEEK; +#if !_WIN32 + translatedFlags |= MSG_DONTWAIT; +#endif + } + + if ((int)flags & (int)SocketReceiveFlags::WaitAll) + { + translatedFlags |= MSG_WAITALL; + } + + return translatedFlags; + } +} \ No newline at end of file diff --git a/src/TcpClient.cpp b/src/TcpClient.cpp index 904074b..1b39843 100644 --- a/src/TcpClient.cpp +++ b/src/TcpClient.cpp @@ -1,255 +1,60 @@ #include "TcpClient.hpp" -#include "NetworkBuffer.hpp" -#include "Utility.hpp" -#include "Config.hpp" -#include "Handshake.hpp" +#include "TcpSocketBuilder.hpp" -#include -#include -#include - -TcpClient::TcpClient(const std::string &ip) : port(default_client_port) +namespace std::net { - initialize(ip); -} - -TcpClient::TcpClient(const std::string &ip, uint16 port) : - ip(ip), port(port) -{ - initialize(ip, port); -} - -const std::string &TcpClient::GetIP() -{ - return ip; -} - -uint16 TcpClient::GetPort() -{ - return port; -} - -void TcpClient::SetIP(const std::string & ip) -{ - this->ip = ip; -} - -void TcpClient::SetPort(uint16 port) -{ - this->port = port; -} - -uint16 TcpClient::GetID() -{ - return id; -} - -void TcpClient::SetID(uint16 id) -{ - this->id = id; -} - -void TcpClient::receive_data(TcpClient *client) -{ - while (client->receive) + TcpClient::TcpClient(Socket *soc) { - NetworkMessage message(client->ReceiveMessage()); - if (message.valid) - { - if (IS_HANDSHAKE(message)) - { - if (message.tag == ConnectTag) // some user has connected - not us, never - std::async(std::launch::async, client->OnConnect, message.sender); - else if (message.tag == DisconnectTag) // some user has disconnected, it can be us - std::async(std::launch::async, client->OnDisconnect, message.sender); - else if (message.tag == Close) - { - std::async(std::launch::async, client->OnDisconnect, message.sender); - close_connection(client); - } - } - else - std::async(std::launch::async, client->OnMessage, message.sender, message.tag, message.subject, message.data); // we received data - } - } -} - -void TcpClient::ReceiveMessages() -{ - std::async(std::launch::async, &receive_data, this); -} - -const NetworkMessage & TcpClient::ReceiveMessage() -{ - return receive_data_array(); -} - -std::future TcpClient::SendMessage(const NetworkMessage &message) -{ - return std::async(std::launch::async, &send_network_message, message, this); -} - -void TcpClient::SetOnDisconnectCallback(std::function func) -{ - OnDisconnect = func; -} - -void TcpClient::SetOnConnectCallback(std::function func) -{ - OnConnect = func; -} - -void TcpClient::SetOnMessageCallback(std::function func) -{ - OnMessage = func; -} - -const TcpClient & TcpClient::DefaultTcpClient() -{ - return TcpClient(); -} - -TcpClient::TcpClient(const SOCKET & socket) -{ - tcp_socket = socket; -} - -bool TcpClient::initialize(const std::string &ip, uint16 port) -{ - if (Utility::IPUtil::ValidIPV4(ip) || port == 0) - return false; - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - uint16 code = getaddrinfo(ip.c_str(), std::to_string(port).c_str(), &hints, &result); - if (code != 0) - { - if (Config::GetUsingConsole()) - std::cerr << code << std::endl; // display more info - WSACleanup(); - return false; + m_socket = std::unique_ptr(soc); // will this work } - tcp_socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); - - if (tcp_socket == INVALID_SOCKET) + TcpClient::TcpClient(SocketProtocol protocol) { - if (Config::GetUsingConsole()) - std::cerr << WSAGetLastError() << std::endl; // display more info - freeaddrinfo(result); - WSACleanup(); - return false; + m_socket = TcpSocketBuilder().AsNonBlocking().AsReusable().Protocol(protocol).Build(); } - return initialized = true; -} - -TcpClient::~TcpClient() -{ - freeaddrinfo(result); - WSACleanup(); - Utility::Delete(result); -} - -void TcpClient::Shutdown() -{ - Handshake handshake(id, Close, Server); - SendMessage(Handshake::HandshakeToNetworkMessage(handshake)); - uint16 code = closesocket(tcp_socket); - if (code == SOCKET_ERROR) - { - if (Config::GetUsingConsole()) - std::cerr << WSAGetLastError() << std::endl; // display more info + bool TcpClient::Connect(const IPAddress& addrStr) + { + return m_socket->Connect(addrStr); } - WSACleanup(); -} - -bool TcpClient::Connect() -{ - if (!initialized) - { - if (ip.size() == 0 || std::count(ip.begin(), ip.end(), '.') != 4 && port == 0 && !initialize(ip, port)) - return false; + bool TcpClient::Close() const + { + return m_socket->Close(); } - else return false; - uint16 connect_code = connect(tcp_socket, result->ai_addr, result->ai_addrlen); - if (connect_code == SOCKET_ERROR) - return false; - - NetworkMessage message(receive_data_array()); - if (IS_HANDSHAKE(message)) - { - if (message.tag == Accept) - { - receive = true; - OnConnect(message.sender); - return true; - } + bool TcpClient::HasPendingData(uint32_t& pendingDataSize) const + { + return m_socket->HasPendingData(pendingDataSize); } - return false; -} -bool TcpClient::DataAvailable(int32 &size) -{ - return ioctlsocket(tcp_socket, FIONREAD, reinterpret_cast(size)) != NO_ERROR && size > 0; -} - -const NetworkBuffer &TcpClient::receive_data_array() -{ - NetworkBuffer buffer; - - int32 temp; - if (DataAvailable(temp) && temp > sizeof(int32)) - { - byte *header = new byte[sizeof(int32)](); - if (recv(tcp_socket, reinterpret_cast(header), sizeof(int32), 0) != sizeof(int32)) - return NetworkBuffer(); - buffer.header = std::vector(header, header + sizeof(int32)); + bool TcpClient::Send(const uint8_t* data, int32_t count, int32_t& sent) const + { + return m_socket->Send(data, count, sent); } - else - return NetworkBuffer(); - int32 body_size = Utility::BitConverter::ToInt32(buffer.header); - byte *body = new byte[body_size](); - int16 received_bytes = recv(tcp_socket, reinterpret_cast(body), body_size, 0); - if (received_bytes == SOCKET_ERROR || received_bytes != body_size || WSAGetLastError() != 0) - return NetworkBuffer(); - - buffer.body = std::vector(body, body + body_size); - buffer.valid = true; - - return buffer; -} - -bool TcpClient::send_network_message(const NetworkMessage &message, TcpClient *client) -{ - NetworkBuffer buffer = NetworkMessage::EncodeMessage(message); - int32 lenght = Utility::BitConverter::ToInt32(buffer.header); - int32 bytes_sent = send(client->tcp_socket, reinterpret_cast(buffer.body.data()), lenght, 0); - return bytes_sent == SOCKET_ERROR || bytes_sent != lenght || WSAGetLastError() != 0; -} - -void TcpClient::close_connection(TcpClient * client) -{ - shutdown(client->tcp_socket, SD_BOTH); - closesocket(client->tcp_socket); -} - -bool TcpClient::SendBytes(const std::vector& bytes) -{ - int32 bytes_sent = send(tcp_socket, reinterpret_cast(bytes.data()), bytes.size(), 0); - if (bytes_sent == SOCKET_ERROR || bytes_sent != bytes.size() || WSAGetLastError() != 0) - { - //something went wrong couldnt send anything/some data + bool TcpClient::Recv(uint8_t* data, int32_t size, int32_t& read, SocketReceiveFlags flags) const + { + return m_socket->Recv(data, size, read, flags); } -} -bool TcpClient::SendBytes(byte * bytes, uint32 size) -{ - int32 bytes_sent = send(tcp_socket, reinterpret_cast(bytes), size, 0); - return bytes_sent == SOCKET_ERROR || bytes_sent != size || WSAGetLastError() != 0; + bool TcpClient::Wait(SocketWaitConditions cond, std::chrono::milliseconds t) const + { + return m_socket->Wait(cond, t); + } + + SocketConnectionState TcpClient::GetConnectionState() const + { + return m_socket->GetConnectionState(); + } + + void TcpClient::GetAddress(IPAddress& outAddr) const + { + return m_socket->GetAddress(outAddr); + } + + int32_t TcpClient::GetPort() const + { + return m_socket->GetPort(); + } } \ No newline at end of file diff --git a/src/TcpListener.cpp b/src/TcpListener.cpp new file mode 100644 index 0000000..869ad0a --- /dev/null +++ b/src/TcpListener.cpp @@ -0,0 +1,54 @@ +#include "TcpListener.hpp" +#include "TcpSocketBuilder.hpp" +#include "Socket.hpp" +#include "TcpClient.hpp" + +namespace std::net +{ + TcpListener::TcpListener(uint16_t port, std::chrono::milliseconds inSleepTime) + : m_port(port) + , m_sleepTime(inSleepTime) + { + m_socket = TcpSocketBuilder().AsNonBlocking().AsReusable().Bind(IPAddress(0, 0, 0, 0, port)).Listening().Build(); + } + + TcpListener::TcpListener(Socket *InSocket, std::chrono::milliseconds inSleepTime) + : m_sleepTime(inSleepTime) + { + m_socket = std::unique_ptr(InSocket); + } + + TcpClient *TcpListener::AcceptClient() + { + if (m_socket == nullptr) + m_socket = TcpSocketBuilder().AsReusable().Bind(IPAddress(0, 0, 0, 0, m_port)).Listening().Build(); + + if (m_socket == nullptr) + return nullptr; + + std::string remoteAddress; + + const bool hasZeroSleepTime = (m_sleepTime == std::chrono::milliseconds(0)); + + bool pending = false; + + if (m_socket->WaitForPendingConnection(pending, m_sleepTime)) + { + if (pending) + { + std::unique_ptr connectionSocket = m_socket->Accept(); + + if (connectionSocket != nullptr) + { + return new TcpClient(connectionSocket.release()); + } + } + else if (hasZeroSleepTime) + std::this_thread::sleep_for(std::chrono::milliseconds(0)); + } + else + std::this_thread::sleep_for(std::chrono::milliseconds(m_sleepTime)); + + return nullptr; + } +} \ No newline at end of file diff --git a/src/TcpServer.cpp b/src/TcpServer.cpp deleted file mode 100644 index 07a8163..0000000 --- a/src/TcpServer.cpp +++ /dev/null @@ -1,290 +0,0 @@ -#include "TcpServer.hpp" -#include "Config.hpp" -#include "Handshake.hpp" -#include "Utility.hpp" - -#include -#include -#include - -TcpServer::TcpServer() -{ - initialize(); // initialize with the default port - clients.reserve(max_connections); -} - -TcpServer::TcpServer(uint16 port) -{ - initialize(port); - clients.reserve(max_connections); -} - -TcpServer::~TcpServer() -{ -} - -void TcpServer::Shutdown() -{ - running = false; - - for (std::vector::iterator it = clients.begin(); it != clients.end(); ++it) - { - (*it).Shutdown(); - clients.erase(it); - } - - shutdown_internal(); -} - -void TcpServer::AcceptConnections() -{ - if (!running) - { - running = true; - std::async(std::launch::async, &accept_connections, this); - } -} - -void TcpServer::process_client_messages(TcpServer *server, TcpClient & client) -{ - while (server->running) - { - NetworkMessage message(client.ReceiveMessage()); - if (message.valid) - server->SendMessage(message); - } -} - -bool TcpServer::SendMessage(const NetworkMessage & message) -{ - switch (message.distribution_mode) - { - case All: // this will send the message to all except the user that sent it - { - for (std::vector::iterator it = clients.begin(); it != clients.end(); ++it) - { - TcpClient client = *it; - if (message.sender != client.GetID()) - client.SendMessage(message); - } - for (uint16 i = 0; i < OnMessageFunctions.size(); i++) - OnMessageFunctions[i](message); - } - case AllAndMe: // this will send the message to EVERYONE including the user that sent it - { - for (std::vector::iterator it = clients.begin(); it != clients.end(); ++it) - { - TcpClient client = *it; - client.SendMessage(message); - } - for (uint16 i = 0; i < OnMessageFunctions.size(); i++) - OnMessageFunctions[i](message); - } - case Server: // this will only send the message to the server - { - if (message.tag == DisconnectTag) - CloseSocket(message.sender); - for (uint16 i = 0; i < OnMessageFunctions.size(); i++) - OnMessageFunctions[i](message); - } - case Others: // this will send the message to others, excluding server and the user that sent it - { - for (std::vector::iterator it = clients.begin(); it != clients.end(); ++it) - { - TcpClient client = *it; - if (message.sender != client.GetID()) - client.SendMessage(message); - } - } - case ID: // this will send the message to a specific id - { - for (std::vector::iterator it = clients.begin(); it != clients.end(); ++it) - { - TcpClient client = *it; - if (message.sender == client.GetID()) - client.SendMessage(message); - } - return false; - } - } -} - -uint16 TcpServer::allocate_id() // this function is only used in the AddToClientsList function -{ - for (uint16 i = 1; i < max_connections; ++i) - { - bool flag = true; - for (std::vector::iterator it = clients.begin(); it != clients.end(); ++it) - { - TcpClient client = *it; - if (client.GetID() == i) - { - flag = false; - break; - } - } - - if (flag) - return i; - } - return 0; -} - -void TcpServer::add_to_clients_list(TcpClient & client_socket) -{ - uint16 id = allocate_id(); - if (id > 0) - { - client_socket.SetID(id); - clients.emplace_back(client_socket); - AcceptConnection(client_socket.GetID()); - } - else - { - if (Config::GetUsingConsole()) - std::cout << "No available ID's" << std::endl; - RejectConnection(client_socket); - } -} - -void TcpServer::RejectConnection(TcpClient &client) -{ - Handshake handshake(client.GetID(), Reject, ID); - SendMessage(Handshake::HandshakeToNetworkMessage(handshake)); -} - -void TcpServer::AcceptConnection(uint16 id) -{ - Handshake handshake(id, Accept, AllAndMe); - SendMessage(Handshake::HandshakeToNetworkMessage(handshake)); -} - -void TcpServer::CloseSocket(TcpClient & client) -{ - for (std::vector::iterator it = clients.begin(); it != clients.end(); ++it) - { - TcpClient it_client = *it; - if (client.GetID() == it_client.GetID()) - { - it_client.Shutdown(); - clients.erase(it); - } - } -} - -void TcpServer::CloseSocket(uint16 id) -{ - TcpClient client = GetClientByID(id); - if (client.GetID() != -2) - CloseSocket(client); -} - -const TcpClient & TcpServer::GetClientByID(uint16 id) -{ - for (std::vector::iterator it = clients.begin(); it != clients.end(); ++it) - { - TcpClient client = *it; - if (client.GetID() == id) - return client; - } - return TcpClient::DefaultTcpClient(); -} - -void TcpServer::SetMaxConnections(uint16 value) -{ - max_connections = value; -} - -uint16 TcpServer::GetMaxConnections() -{ - return max_connections; -} - -bool TcpServer::initialize(uint16 port) -{ - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - - uint16 code = getaddrinfo(0, std::to_string(port).c_str(), &hints, &result); - if (code != 0) - { - if (Config::GetUsingConsole()) - std::cerr << WSAGetLastError() << std::endl; // display more info - WSACleanup(); - return false; - } - - server_tcp_socket = ::socket(result->ai_family, result->ai_socktype, result->ai_protocol); - - if (server_tcp_socket == INVALID_SOCKET) - { - if (Config::GetUsingConsole()) - std::cerr << WSAGetLastError() << std::endl; // display more info - freeaddrinfo(result); - WSACleanup(); - return false; - } - - code = bind(server_tcp_socket, result->ai_addr, result->ai_addrlen); - if (code == SOCKET_ERROR) - { - if (Config::GetUsingConsole()) - std::cerr << WSAGetLastError() << std::endl; // display more info - freeaddrinfo(result); - closesocket(server_tcp_socket); - WSACleanup(); - return false; - } - - freeaddrinfo(result); - return initialized = true; -} - -bool TcpServer::StartServer(bool accept_connections) -{ - if (listen(server_tcp_socket, SOMAXCONN) == SOCKET_ERROR) - { - if (Config::GetUsingConsole()) - std::cerr << WSAGetLastError() << std::endl; - closesocket(server_tcp_socket); - WSACleanup(); - return false; - } - - if (accept_connections) - AcceptConnections(); - - return true; -} - -void TcpServer::accept_connections(TcpServer *server) -{ - while (server->running) - { - SOCKET client_socket = accept(server->server_tcp_socket, 0, 0); - if (client_socket == INVALID_SOCKET) - { - if (Config::GetUsingConsole()) - std::cerr << WSAGetLastError() << std::endl; - closesocket(server->server_tcp_socket); - WSACleanup(); - server->running = false; // if we cant accept a connection idk if we should stop the server or not mh - break; - } - - TcpClient client(client_socket); - server->add_to_clients_list(client); - - std::async(std::launch::async, &process_client_messages, server, client); - } -} - -void TcpServer::shutdown_internal() -{ - freeaddrinfo(result); - WSACleanup(); - Utility::Delete(result); -} \ No newline at end of file diff --git a/src/TcpSocketBuilder.cpp b/src/TcpSocketBuilder.cpp new file mode 100644 index 0000000..f1d9718 --- /dev/null +++ b/src/TcpSocketBuilder.cpp @@ -0,0 +1,52 @@ +#include "TcpSocketBuilder.hpp" + +#include "Socket.hpp" +#include "TcpClient.hpp" +#include "TcpListener.hpp" + +namespace std::net +{ + std::unique_ptr TcpSocketBuilder::Build() const + { + std::unique_ptr socket = std::make_unique(SocketType::Streaming, m_socketProtocol); + + if (socket != nullptr) + { + bool Error = !socket->SetReuseAddr(m_reusable) || + !socket->SetLinger(m_linger, m_lingerTimeout); + + if (!Error) + Error = m_bound && !socket->Bind(m_boundAddr); + if (!Error) + Error = m_listen && !socket->Listen(); + if (!Error) + Error = !socket->SetNonBlocking(!m_blocking); + + if (!Error) + { + int32_t out_new_size; + if (m_receiveBufferSize > 0) + socket->SetReceiveBufferSize(m_receiveBufferSize, out_new_size); + if (m_sendBufferSize > 0) + socket->SetSendBufferSize(m_sendBufferSize, out_new_size); + } + + if (Error) + throw std::runtime_error("Couldnt create socket"); // make parameter a string depending on the error + } + + return socket; + } + + std::unique_ptr TcpSocketBuilder::BuildClient() const + { + std::unique_ptr socket = Build(); + return std::make_unique(socket.release()); + } + + std::unique_ptr TcpSocketBuilder::BuildListener() const + { + std::unique_ptr socket = Build(); + return std::make_unique(socket.release()); + } +} \ No newline at end of file diff --git a/src/UdpClient.cpp b/src/UdpClient.cpp deleted file mode 100644 index 3fec257..0000000 --- a/src/UdpClient.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "UdpClient.hpp" -#include "Handshake.hpp" -#include "Config.hpp" - -#include - -#include - -UdpClient::UdpClient(const std::string & ip) -{ - initialize(ip); -} - -UdpClient::UdpClient(const std::string & ip, uint16) -{ - initialize(ip, port); -} - -uint16 UdpClient::GetPort() -{ - return port; -} - -void UdpClient::SetPort(uint16 port) -{ - this->port = port; -} - -uint16 UdpClient::GetID() -{ - return id; -} - -void UdpClient::SetID(uint16 id) -{ - this->id = id; -} - -void UdpClient::ReceiveMessages() -{ -} - -const NetworkMessage & UdpClient::ReceiveMessage() -{ - // TODO: insert return statement here -} - -std::future UdpClient::SendMessage(const NetworkMessage & message) -{ - return std::async(std::launch::async, &send_network_message, message, this); -} - -const std::string &UdpClient::GetIP() -{ - return ip; -} - -void UdpClient::SetIP(const std::string & ip) -{ - this->ip = ip; -} - -UdpClient::UdpClient(const SOCKET & socket) -{ - udp_socket = socket; -} - -const NetworkBuffer & UdpClient::receive_data_array() -{ - // TODO: insert return statement here -} - -void UdpClient::receive_data(UdpClient * client) -{ -} - -bool UdpClient::initialize(const std::string &ip, uint16 port) -{ - if (Utility::IPUtil::ValidIPV4(ip) || port == 0) - return false; - - udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - if (udp_socket == INVALID_SOCKET) - { - WSACleanup(); - return false; - } -} - -UdpClient::~UdpClient() -{ - WSACleanup(); -} - -void UdpClient::Shutdown() -{ - Handshake handshake(id, Close, Server); - SendMessage(Handshake::HandshakeToNetworkMessage(handshake)); - uint16 code = closesocket(udp_socket); - if (code == SOCKET_ERROR) - { - if (Config::GetUsingConsole()) - std::cerr << WSAGetLastError() << std::endl; // display more info - } - - WSACleanup(); -} - -bool UdpClient::SendBytes(const std::vector &bytes) -{ - -} - -bool UdpClient::SendBytes(byte *bytes, uint32 lenght) -{ - -} - -bool UdpClient::send_network_message(const NetworkMessage &message, UdpClient *client) -{ - -} \ No newline at end of file diff --git a/src/UdpServer.cpp b/src/UdpServer.cpp deleted file mode 100644 index c513de6..0000000 --- a/src/UdpServer.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "UdpServer.hpp" -#include "Utility.hpp" - -uint16 UdpServer::allocate_id() -{ - for (uint16 i = 1; i < max_connections; ++i) - { - bool flag = true; - for (std::vector::iterator it = clients.begin(); it != clients.end(); ++it) - { - UdpClient client = *it; - if (client.GetID() == i) - { - flag = false; - break; - } - } - - if (flag) - return i; - } - return 0; -} - -bool UdpServer::initialize(uint16 port) -{ - server_udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - if (server_udp_socket == INVALID_SOCKET) - { - WSACleanup(); - return false; - } - - memset(&server, '\0', sizeof(struct sockaddr_in)); - - server.sin_family = AF_INET; - server.sin_port = htons(port); - - server.sin_addr.S_un.S_un_b.s_b1 = 0; - server.sin_addr.S_un.S_un_b.s_b2 = 0; - server.sin_addr.S_un.S_un_b.s_b3 = 0; - server.sin_addr.S_un.S_un_b.s_b4 = 0; - - if (bind(server_udp_socket, reinterpret_cast(&server), sizeof(struct sockaddr_in)) == -1) - { - closesocket(server_udp_socket); - WSACleanup(); - return false; - } - return initialized = true; -} - -void UdpServer::shutdown_internal() -{ - WSACleanup(); -} \ No newline at end of file diff --git a/src/UdpSocket.cpp b/src/UdpSocket.cpp new file mode 100644 index 0000000..811d67a --- /dev/null +++ b/src/UdpSocket.cpp @@ -0,0 +1,64 @@ +#include "UdpSocket.hpp" + +namespace std::net +{ + UdpSocket::UdpSocket(Socket * soc) + { + m_socket = std::unique_ptr(soc); // will this work + } + + UdpSocket::UdpSocket(SocketProtocol protocol) + { + m_socket = std::make_unique(SocketType::Datagram, protocol); + } + + bool UdpSocket::Bind(const IPAddress & addr) + { + return m_socket->Bind(addr); + } + + bool UdpSocket::SendTo(const uint8_t * data, int32_t count, int32_t & sent, const IPAddress & addrDest) + { + return m_socket->SendTo(data, count, sent, addrDest); + } + + bool UdpSocket::RecvFrom(uint8_t * data, int32_t size, int32_t & read, IPAddress & srcAddr, SocketReceiveFlags flags) + { + return m_socket->RecvFrom(data, size, read, srcAddr, flags); + } + + bool UdpSocket::GetPeerAddress(IPAddress & outAddr) + { + return m_socket->GetPeerAddress(outAddr); + } + + bool UdpSocket::JoinMulticastGroup(const IPAddress & addrStr) + { + return m_socket->JoinMulticastGroup(addrStr); + } + + bool UdpSocket::LeaveMulticastGroup(const IPAddress & addrStr) + { + return m_socket->LeaveMulticastGroup(addrStr); + } + + bool UdpSocket::SetMulticastLoopback(bool loopback) + { + return m_socket->SetMulticastLoopback(loopback); + } + + bool UdpSocket::SetMulticastTtl(uint8_t timeToLive) + { + return m_socket->SetMulticastTtl(timeToLive); + } + + uint32_t UdpSocket::GetPort() + { + return m_socket->GetPort(); + } + + bool UdpSocket::SetReuseAddr(bool allowReuse) + { + return m_socket->SetReuseAddr(allowReuse); + } +} diff --git a/src/Uri.cpp b/src/Uri.cpp new file mode 100644 index 0000000..df19bf7 --- /dev/null +++ b/src/Uri.cpp @@ -0,0 +1,188 @@ +// https://github.com/mfichman/http + +#include "Uri.hpp" +#include "Parse.hpp" + +namespace std::net +{ + static bool IsReserved(char ch) + { + switch (ch) + { + case '/': + return true; + case '#': + return true; + case '?': + return true; + default: + return false; + } + } + + static ParseResult ParseScheme(char const* str) + { + auto result = ParseWhile(str, [](char ch) + { + return ch != ':' && !IsReserved(ch); + }); + + result.ch = (result.ch[0] == ':') ? (result.ch + 1) : (result.ch); + return result; + } + + static ParseResult ParseUser(char const* str) + { + auto result = ParseWhile(str, [](char ch) + { + return ch != '@' && !IsReserved(ch); + }); + + if (result.ch[0] == '@') + result.ch = result.ch + 1; + else { + result.ch = str; + result.value = ""; + } + return result; + } + + static ParseResult ParseHost(char const* str) + { + return ParseWhile(str, [](char ch) + { + return ch != ':' && !IsReserved(ch); + }); + } + + static ParseResult ParsePort(char const* str) + { + ParseResult result; + if (str[0] != ':') + { + result.value = 0; + result.ch = str; + return result; + } + + auto tmp = ParseWhile(str + 1, [](char ch) + { + return !IsReserved(ch); + }); + + result.value = uint16_t(strtol(tmp.value.c_str(), 0, 10)); + result.ch = tmp.ch; + return result; + } + + static ParseResult parseAuthority(char const* str) + { + ParseResult result + { + }; + + if (str[0] == '\0' || str[0] != '/' || str[1] != '/') + { + result.ch = str; + return result; + } + + auto user = ParseUser(str + 2); // For "//" + auto host = ParseHost(user.ch); + auto port = ParsePort(host.ch); + + result.value.SetUser(user.value); + result.value.SetHost(host.value); + result.value.SetPort(port.value); + result.ch = port.ch; + + return result; + } + + static ParseResult parsePath(char const* str) + { + // Return query/frag as part of path for now + ParseResult result = ParseWhile(str, [](char ch) + { + return true; + }); + /* + ParseResult result = parseWhile(str, [](char ch) { + return ch != '/' && !isReserved(ch); + }); + result.ch = (result.ch[0] == '?') ? (result.ch+1) : (result.ch); + */ + return result; + + } + + static Uri parseUri(char const* str) + { + Uri uri; + + auto scheme = ParseScheme(str); + auto authority = parseAuthority(scheme.ch); + auto path = parsePath(authority.ch); + + uri.SetScheme(scheme.value); + uri.SetAuthority(authority.value); + uri.SetPath(path.value); + return uri; + } + + + Authority::Authority(std::string const& user, std::string const& host, uint16_t port) + { + m_user = user; + m_host = host; + m_port = port; + } + + Authority::Authority() + { + m_port = 0; + } + + void Authority::SetUser(std::string const& user) + { + m_user = user; + } + + void Authority::SetHost(std::string const& host) + { + m_host = host; + } + + void Authority::SetPort(uint16_t port) + { + m_port = port; + } + + Uri::Uri(const char* value) + { + *this = parseUri(value); + } + + Uri::Uri(const std::string& value) + { + *this = parseUri(value.c_str()); + } + + Uri::Uri() { + } + + void Uri::SetScheme(const std::string& scheme) + { + m_scheme = scheme; + } + + void Uri::SetAuthority(const Authority& authority) + { + m_authority = authority; + } + + void Uri::SetPath(const std::string& path) + { + m_path = path; + } +} \ No newline at end of file diff --git a/src/Utility.cpp b/src/Utility.cpp deleted file mode 100644 index 588724c..0000000 --- a/src/Utility.cpp +++ /dev/null @@ -1,280 +0,0 @@ -#include "Utility.hpp" - -#include -#include -#include - -void Utility::Delete(void *pointer) -{ - if (pointer == nullptr) - return; - delete pointer; - pointer = nullptr; -} - -void Utility::DeleteArray(void *pointer) -{ - if (pointer == nullptr) - return; - delete[] pointer; - pointer = nullptr; -} - -std::vector Utility::StringConverter::Split(const std::string & str, const std::string & delimiter) -{ - std::vector splited; - if (str.empty() && delimiter.empty()) - return std::vector(); - std::stringstream ss(str); - std::string token; - while (std::getline(ss, token, delimiter[0])) - splited.push_back(token); - return splited; -} - -const std::vector &Utility::BitConverter::ToBytes(uint8 number) -{ - return std::vector(); -} - -uint8 Utility::BitConverter::ToUint8(const std::vector &bytes, uint16 start_index) -{ - return uint8(); -} - -const std::vector &Utility::BitConverter::ToBytes(uint16 number) -{ - return std::vector(); -} - -uint16 Utility::BitConverter::ToUint16(const std::vector &bytes, uint16 start_index) -{ - return uint16(); -} - -const std::vector & Utility::BitConverter::ToBytes(uint32 number) -{ - return std::vector(); -} - -uint32 Utility::BitConverter::ToUint32(const std::vector & bytes, uint16 start_index) -{ - return uint32(); -} - -const std::vector & Utility::BitConverter::ToBytes(uint64 number) -{ - return std::vector(); -} - -uint64 Utility::BitConverter::ToUint64(const std::vector & bytes, uint16 start_index) -{ - return uint64(); -} - -const std::vector & Utility::BitConverter::ToBytes(int8 number) -{ - return std::vector(); -} - -int8 Utility::BitConverter::ToInt8(const std::vector & bytes, uint16 start_index) -{ - return int8(); -} - -const std::vector & Utility::BitConverter::ToBytes(int16 number) -{ - return std::vector(); -} - -int16 Utility::BitConverter::ToInt16(const std::vector & bytes, uint16 start_index) -{ - return int16(); -} - -const std::vector & Utility::BitConverter::ToBytes(int32 number) -{ - return std::vector(); -} - -int32 Utility::BitConverter::ToInt32(const std::vector & bytes, uint16 start_index) -{ - return int32(); -} - -const std::vector & Utility::BitConverter::ToBytes(int64 number) -{ - return std::vector(); -} - -int64 Utility::BitConverter::ToInt64(const std::vector & bytes, uint16 start_index) -{ - return int64(); -} - -const std::string & Utility::StringConverter::ToString(bool value) -{ - return std::string(); -} - -const std::string & Utility::StringConverter::ToString(uint8 value) -{ - return std::string(); -} - -const std::string & Utility::StringConverter::ToString(uint16 value) -{ - return std::string(); -} - -const std::string & Utility::StringConverter::ToString(uint32 value) -{ - return std::string(); -} - -const std::string & Utility::StringConverter::ToString(uint64 value) -{ - return std::string(); -} - -const std::string & Utility::StringConverter::ToString(int8 value) -{ - return std::string(); -} - -const std::string & Utility::StringConverter::ToString(int16 value) -{ - return std::string(); -} - -const std::string & Utility::StringConverter::ToString(int32 value) -{ - return std::string(); -} - -const std::string & Utility::StringConverter::ToString(int64 value) -{ - return std::string(); -} - -const std::string & Utility::StringConverter::ToString(const std::vector& bytes) -{ - return std::string(); -} - -uint8 Utility::StringConverter::ToUint8(const std::string & str) -{ - return uint8(); -} - -uint16 Utility::StringConverter::ToUint16(const std::string & str) -{ - return uint16(); -} - -uint32 Utility::StringConverter::ToUint32(const std::string & str) -{ - return uint32(); -} - -uint64 Utility::StringConverter::ToUint64(const std::string & str) -{ - return uint64(); -} - -int8 Utility::StringConverter::ToInt8(const std::string & str) -{ - return int8(); -} - -int16 Utility::StringConverter::ToInt16(const std::string & str) -{ - return int16(); -} - -int32 Utility::StringConverter::ToInt32(const std::string & str) -{ - return int32(); -} - -int64 Utility::StringConverter::ToInt64(const std::string & str) -{ - return int64(); -} - -const std::vector& Utility::StringConverter::ToBytes(const std::string & str) -{ - return std::vector(); -} - -const std::string & Utility::StringConverter::ToString(const std::vector & bytes, uint16 start_index, uint16 lenght) -{ - return std::string(); -} - -const std::string & Utility::StringConverter::Trim(std::string & str, char ch) -{ - if (str.empty() && ch == 0) - return std::string(); - for (std::string::iterator it = str.begin(); it != str.end(); ++it) - { - if (*it == ch) - str.erase(it); - } - return str; -} - -void Utility::ConfigReader::ReadConfig(const std::string & file_name) -{ - if (file_name.empty()) - return; - std::fstream file; - file.open(file_name); - if (file.is_open()) - { - longlong file_lenght = file.gcount(); - char *content = new char[static_cast(file_lenght)](); - file.read(content, file_lenght); - file_content = std::string(content); - } -} - -void Utility::ConfigReader::ReadNodes() -{ - if (file_content.empty()) - return; - std::stringstream ss(file_content); - std::string temp; - std::vector nodes_lines; - while (std::getline(ss, temp, '\n')) - { - if (temp.substr(0, 2) != "//") - nodes_lines.emplace_back(temp); - } - - for (std::vector::iterator it = nodes_lines.begin(); it != nodes_lines.end(); ++it) - { - std::string node_str = Utility::StringConverter::Trim(*it, ' '); - std::vector node = Utility::StringConverter::Split(node_str, "="); - nodes.insert(std::pair(node.at(0), node.at(1))); - } -} - -const std::string & Utility::ConfigReader::operator[](const std::string &key) -{ - return nodes.at(key); -} - -bool Utility::IPUtil::ValidIPV4(const std::string & ip) -{ - std::vector splitted_address = Utility::StringConverter::Split(ip, "."); - if (splitted_address.size() != 4) - return false; - uint8 a1 = Utility::StringConverter::ToUint8(splitted_address[0]); - uint8 a2 = Utility::StringConverter::ToUint8(splitted_address[1]); - uint8 a3 = Utility::StringConverter::ToUint8(splitted_address[2]); - uint8 a4 = Utility::StringConverter::ToUint8(splitted_address[3]); - - return a1 != 0 && a2 != 0 && a3 != 0 && a4 != 0 && - a1 != 255 && a2 != 255 && a3 != 255 && a4 != 255; -} diff --git a/src/VoidNetClient.cpp b/src/VoidNetClient.cpp deleted file mode 100644 index 44f5963..0000000 --- a/src/VoidNetClient.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "VoidNetClient.hpp" -#include "Utility.hpp" - -#include - -bool VoidNetClientAPI::Connect(const std::string &ip, uint16 port) -{ - tcp_client.SetIP(ip); - tcp_client.SetPort(port); - return tcp_client.Connect(); -} - -void VoidNetClientAPI::SendMessageToServer(byte tag, byte subject, void *data) -{ - SendMessage(Server, 0, tag, subject, data); -} - -void VoidNetClientAPI::SendMessageToID(uint16 destination_id, byte tag, byte subject, void *data) -{ - SendMessage(ID, destination_id, tag, subject, data); -} - -void VoidNetClientAPI::SendMessageToOthers(byte tag, byte subject, void *data) -{ - SendMessage(Others, 0, tag, subject, data); -} - -void VoidNetClientAPI::SendMessageToAll(byte tag, byte subject, void *data) -{ - SendMessage(All, 0, tag, subject, data); -} - -void VoidNetClientAPI::SendMessageToAllAndMe(byte tag, byte subject, void *data) -{ - SendMessage(AllAndMe, 0, tag, subject, data); -} - -void VoidNetClientAPI::SendMessage(byte distribution_mode, uint16 destination_id, byte tag, byte subject, void *data) -{ - NetworkMessage message; - message.tag = tag; - message.subject = subject; - message.data = data; - message.distribution_mode = distribution_mode; - message.sender = id; - message.destination_id = destination_id; - if (!IS_HANDSHAKE(message)) - tcp_client.SendMessage(message); -} - -void VoidNetClientAPI::Receive() -{ - std::async(std::launch::async, &receive_data); -} - -void VoidNetClientAPI::receive_data() -{ - tcp_client.ReceiveMessages(); -} - -void VoidNetClientAPI::Disconnect() -{ - tcp_client.Shutdown(); -}