// networking.hpp // // Copyright (C) 2004-2020 MicroNeil Research Corporation. // // This software is released under the MIT license. See LICENSE.TXT. // The networking module abstracts network communications and provides a set // of objects for handling most tasks on both win* and *nix. #pragma once #include #include #include #include #include #include #include #include //// Platform specific includes... #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) //// Windows headers... #include namespace codedweller { typedef int socklen_t; // Posix uses socklen_t so we mimic it. typedef SOCKET hSocket; // Winx handles Socket is opaque. } // End namespace codedweller #else //// GNU Headers... #include #include #include #include #include #include #include namespace codedweller { typedef int hSocket; // *nix uses int to handle a Socket. const hSocket INVALID_SOCKET = -1; // -1 is the invalid Socket. } // End namespace codedweller #endif namespace codedweller { //// Handling SIGPIPE ////////////////////////////////////////////////////////// #ifndef MSG_NOSIGNAL const int MSG_NOSIGNAL = 0; // Fake this if it isn't defined. #endif #ifndef SO_NOSIGPIPE const int SO_NOSIGPIPE = 0; // Fake this if it isn't defined. #endif //// Tuning and Constants ////////////////////////////////////////////////////// const unsigned long LOCALHOST = 0x7F000001; // 127.0.0.1 as an integer. const int DefaultMaxPending = 5; // Default connection queue size. const int TCPClientBufferSize = 4096; // TCP Client buffer size. const int TCPHostBufferSize = 4096; // TCP Host buffer size. const int NOFLAGS = 0; // Magic number for no flags. //////////////////////////////////////////////////////////////////////////////// // IP4address class // // The IP4address class makes it easy to manipulate IPs. class IP4Address { // IP4Address manipulator. private: unsigned long int IP; // The actual data. public: IP4Address(); // Blank constructor IP = 0.0.0.0 IP4Address(const unsigned long int newIP); // Constructor given unsigned long IP4Address(const IP4Address&); // Constructor given an IP4Address IP4Address(const char* newIP); // Construcing with a cstring. IP4Address(const std::string& newIP); // Constructing with a cppstring. IP4Address& operator=(const unsigned long int Right); // Convert from unsigned long int. IP4Address& operator=(const char* Right); // Convert from c string. IP4Address& operator=(const std::string& Right); // Convert from cpp string. operator unsigned long int() const; operator std::string() const; bool operator<(const IP4Address Right) const; // < Comparison. bool operator>(const IP4Address Right) const; // > Comparison. bool operator==(const IP4Address Right) const; // == Comparison. bool operator!=(const IP4Address Right) const; // != Comparison. bool operator<=(const IP4Address Right) const; // <= Comparison. bool operator>=(const IP4Address Right) const; // >= Comparison. }; /* static unsigned long int& operator=(unsigned long int& Out, const IP4Address& In); // Assign to unsigned long static string& operator=(string& Out, const IP4Address& In); // Assign to cpp string */ //////////////////////////////////////////////////////////////////////////////// // Network Core class // // The Networking class acts as a central point for setup, cleanup, and access // to network services. For example, when using WinSock, the DLL initialization // must occur once when the program starts up and the shutdown must occur once // as the program shuts down. The constructor and destructor of the "Network" // instances of this class handles that work. There should only be one instance // of this class anywhere in the program and that instance is created when this // module is included. DON'T MAKE MORE INSTANCES OF THIS :-) // // Part of the reason for this class is to handle all of the cross-platform // weirdness involved in handling sockets and conversions. This way all of the // ifdef switched code can be consolidated into this utility class and the // code for the remaining classes can remain nice and clean by using this // class to handle those tasks. class Networking { private: public: class NotSupportedError : public std::runtime_error { // Thrown when something can't be done. public: NotSupportedError(const std::string& w):runtime_error(w) {} }; class InitializationError : public std::runtime_error { // Thrown if initialization fails. public: InitializationError(const std::string& w):runtime_error(w) {} }; class ControlError : public std::runtime_error { // Thrown if control functions fail. public: ControlError(const std::string& w):runtime_error(w) {} }; class SocketCreationError : public std::runtime_error { // Thrown if a call to socket() fails. public: SocketCreationError(const std::string& w):runtime_error(w) {} }; class SocketSetSockOptError : public std::runtime_error { // Thrown if a call to setsockopt() fails. public: SocketSetSockOptError(const std::string& w):runtime_error(w) {} }; class SocketBindError : public std::runtime_error { // Thrown if a call to bind() fails. public: SocketBindError(const std::string& w):runtime_error(w) {} }; class SocketListenError : public std::runtime_error { // Thrown if a call to listen() fails. public: SocketListenError(const std::string& w):runtime_error(w) {} }; class SocketConnectError : public std::runtime_error { // Thrown if a call to connect() fails. public: SocketConnectError(const std::string& w):runtime_error(w) {} }; class SocketAcceptError : public std::runtime_error { // Thrown if a call to accept() fails. public: SocketAcceptError(const std::string& w):runtime_error(w) {} }; class SocketReadError : public std::runtime_error { // Thrown if a socket read call fails. public: SocketReadError(const std::string& w):runtime_error(w) {} }; class SocketWriteError : public std::runtime_error { // Thrown if a socket write call fails. public: SocketWriteError(const std::string& w):runtime_error(w) {} }; static std::string DescriptiveError(std::string Msg, int Errno); // Form a descriptive error w/ errno. Networking(); ~Networking(); int getLastError(); // WSAGetLastError or errno int setNonBlocking(hSocket socket); // Set socket to non-blocking. int closeSocket(hSocket socket); // closesocket() or close() bool WouldBlock(int ErrorCode); // ErrorCode matches [WSA]EWOULDBLOCK bool InProgress(int ErrorCode); // ErrorCode matches [WSA]EINPROGRESS bool IsConnected(int ErrorCode); // ErrorCode matches [WSA]EISCONN }; extern Networking Network; // There is ONE Network object ;-) // End of Network Core Class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // SocketName class // This class represents a communications end-point on a TCP/IP network. All // conversions from/to strings and for byte orders are handled in this class // as well as lookups for ports/services and IPaddresses/host-names. // // Note that the cstring conversions expect the buffer to be large enough. const int IPStringBufferSize = 40; // Safe size for IP as text conversion. const int PortStringBufferSize = 20; // Safe size for Port as text conversion. class SocketAddress { private: struct sockaddr_in Address; // Socket address structure. char IPStringBuffer[IPStringBufferSize]; // Handy conversion buffer. char PortStringBuffer[PortStringBufferSize]; // Handy conversion buffer. public: SocketAddress(); // Constructor sets ANY address. struct sockaddr_in* getPtr_sockaddr_in(); // Returns a pointer to sockaddr_in. struct sockaddr* getPtr_sockaddr(); // Returns a pointer to sockaddr. socklen_t getAddressSize(); // How big is that structure anyway? void setAddress(unsigned long ipAddress); // Set the IP address from an unsigned int void setAddress(char* ipString); // Set the IP address from a cstring unsigned long getAddress(); // Get the IP address as an unsigned int const char* getAddress(char* str); // Get the IP address into a cstring void getAddress(int& a0, int& a1, int& a2, int& a3); // Get the IP address into 4 ints void setPort(unsigned short port); // Set the port address from an int void setPort(char* port); // Set the port address from a cstring unsigned short getPort(); // Get the port address as an unsigned int const char* getPort(char* str); // Get the port address into a cstring void clear(); // Initialize the address. }; // End of SocketName class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Socket class // This class abstracts the underlying socket and adds some functionality // for this module. The derived class is expected to setup the socket before // it can be opened. In fact, the derivative class must provide the open() // function :-) Open is expected to call socket, bind it, and set the socket // into the appropriate mode for it's use in the derived object. class Socket { protected: hSocket Handle; // Our socket handle. bool NonBlocking; // True if the socket is NonBlocking. bool ReuseAddress; // True if SO_REUSEADDR should be used. bool OpenSucceeded; // Successful open occurred. int LastError; // Last error result for this socket. SocketAddress LocalAddress; // Our local address data. SocketAddress RemoteAddress; // Our remote address data. public: Socket(); // Constructor sets initial state. virtual ~Socket(); // Destructor closes Socket if open. hSocket getHandle(); // Returns the current SocketId. bool isNonBlocking(); // Returns true if socket is NonBlocking void makeNonBlocking(); // Sets the socket to NonBlocking mode. bool isReuseAddress(); // True if socket is set SO_REUSEADDR. bool isReuseAddress(bool set); // Changes SO_REUSEADDR setting. bool isOpen(); // True if the socket is open. int getLastError(); // Returns the last error for this socket. virtual void open() = 0; // Derived class specifies open(); void close(); // Close politely. }; // End of Socket class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // MessagePort class // Interface that Sends and Receives messages - possibly a bit at a time. This // interface standardizes things so that multiple technologies can go beneith // such as UNIX domain pipes or named pipes or sockets etc. There is also a // special function to improve the efficiency of delimited transfers (such as // email). The function checks for the delimited byte inside an optimized loop // so that the port doesn't have to be read one byte at a time by the caller. // In the case of non-blocking ports, these methods may return before all of // the data has been transferred. In these cases the caller is expected to know // if it's got the complete message and is expected to repeat it's call until // it does. class MessagePort { public: virtual bool isNonBlocking() = 0; // True if we should expect partial xfrs. virtual int transmit(const char* bfr, int size) = 0; // How to send a buffer of data. virtual int receive(char* bfr, int size) = 0; // How to receive a buffer of data. virtual int delimited_receive(char* bfr, int size, char delimiter) = 0; // How to receive delimited data. }; //////////////////////////////////////////////////////////////////////////////// // Message class // This is a base class for representing messages that are sent to or received // from MessagePorts. The basic Message has 3 modes. Unfixed width, fixed width, // or delimeted. More complex messaging schemes can be built up from these // basics. A message must know how to send and recieve itself using the // MessagePort API and must be able to indicate if the latest transfer request // is complete or needs to be continued. The MessagePort may be blocking or // non-blocking. If it is blocking then a writeTo() or readFrom() operation // should not return until the transfer is completed. If the MessagePort is in // a non-blocking mode then writeTo() and readFrom() will do as much as they // can before returning but if the transfer was not completed then the app // lication may need to transferMore(). class Message { char* Data; // Pointer to message data. int DataBufferSize; // Size of buffer to hold data. int DataSize; // Size of Data. char* RWPointer; // RW position in buffer. bool TransferInProgress; // True if read or write is not complete. bool Delimited; // Delimited Message Flag. char Delimiter; // Delimiter character. public: /** All of this is yet to be built! **/ Message(const Message& M); // Copy constructor. Message(int Size); // Construct empty of Size. Message(int Size, char Delimiter); // Construct empty with delimiter. Message(char* NewData, int Size); // Construct non-delimited message. Message(char* NewData, int Size, char Delimiter); // Construct delimited message. void writeTo(MessagePort &P); // Initiate an outbound transfer. void readFrom(MessagePort &P); // Initiate an inbound transfer. bool isBusy(); // True if the transfer isn't complete. void transferMore(); // Do more of the transfer. void abortTransfer(); // Forget about the transfer. bool isDelimited(); // True if the message is delimited. char getDelimiter(); // Read the delimiter cahracter. char* getData(); // Access the data buffer. int getDataSize(); // How much data is there. }; // End of Message class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // TCPListener class // This class represents a local socket used to listen for new connections. The // application can poll this object for new inbound connections which are then // delivered as TCPClient objects. class TCPClient; // Hint about the coming client class. class TCPListener : public Socket { private: bool OpenStage1Complete; // First stage of open() complete. bool OpenStage2Complete; // Second stage of open() complete. public: TCPListener(unsigned short Port); // Set up localhost on this Port. TCPListener(SocketAddress& WhereToBind); // Set up specific "name" for listening. ~TCPListener(); // Close when destructing. int MaxPending; // Maximum inbound connection queue. virtual void open(); // Open when ready. TCPClient* acceptClient(); // Accept a client connection. }; // End of TCPListener class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // TCPClient class // This class represents a TCP network client connection. It is created by // a TCPListener object if/when a TCP connection is made to the Listener and // accepted by the application. class TCPClient : public Socket, public MessagePort { private: TCPListener& MyListener; char ReadBuffer[TCPClientBufferSize]; // Buffer for delimited reading. char* ReadPointer; // Read position. int DataLength; // Length of data in buffer. bool ReadBufferIsEmpty(); // True if DataLength is zero. void fillReadBuffer(); // Fill the ReadBuffer from the socket. public: TCPClient(TCPListener& L, hSocket H, SocketAddress& A); // How to create a TCPClient. ~TCPClient(); // Destructor for cleanup. TCPListener& getMyListener(); // Where did I come from? bool isNonBlocking(); // Provided for MessagePort. virtual int transmit(const char* bfr, int size); // How to send a buffer of data. virtual int receive(char* bfr, int size); // How to receive a buffer of data. virtual int delimited_receive(char* bfr, int size, char delimiter); // How to receive delimited data. virtual void open(); // We provide open() as unsupported. unsigned long getRemoteIP(); // Get remote IP as long. const char* getRemoteIP(char* str); // Get IP as string. unsigned short getRemotePort(); // Get remote Port as unsigned short. const char* getRemotePort(char* str); // Get Port as string. }; // End of TCPClient class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // TCPHost class // This class represents a TCP network server connection. A client application // creates this object when it wants to connect to a given TCP service. class TCPHost : public Socket, public MessagePort { private: char ReadBuffer[TCPHostBufferSize]; // Buffer for delimited reading. char* ReadPointer; // Read position. int DataLength; // Length of data in buffer. bool ReadBufferIsEmpty(); // True if DataLength is zero. void fillReadBuffer(); // Fill the ReadBuffer from the socket. bool OpenStage1Complete; // Skip stage 1 of open() after done. public: TCPHost(unsigned short Port); // Will connect to localhost on Port. TCPHost(SocketAddress& Remote); // Will connect to Remote address/port. // TCPHost(SocketAddress& Local, SocketAddress& Remote); // Will connect to Remote from Local. ~TCPHost(); // Clean up when we go away. bool isNonBlocking(); // Provided for MessagePort. virtual int transmit(const char* bfr, int size); // How to send a buffer of data. virtual int receive(char* bfr, int size); // How to receive a buffer of data. virtual int delimited_receive(char* bfr, int size, char delimiter); // How to receive delimited data. virtual void open(); // We provide open(). }; // End of TCPHost class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDPListener class // This class represents a local UPD port set up to listen for UDP requests. In // this case, each UDP packet that arrives is assumed to be a single request so // for each a UDPRequest object is created that links back to this Listener. // The application can then use that UDPRequest to .respond() with a Message. // the response is sent back to the original requester and the UDPRequest is // considered satisfied. class UDPListener : public Socket { }; // End of UDPListener class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDPRequest class // This class is created by a UDPListener when a packet is received. The object // contains all of the necessary information about the source for the request // so that the application can .respond() to them through this object. The // response UDP packtes are sent through the UDPListener socket. class UDPRequest : public MessagePort { }; // End of UDPRequest class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDPHost class // This class represents a server/host on the network that uses the UDP // protocol. The application can use this object to send a .request() Message // and getReply(). Each request becomes a UDP packet. Each received UDP packet // from the specified UDPHost becomes a reply Message. (Connected UDP socket). class UDPHost : public Socket, public MessagePort { }; // End of UDPHost class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDPReceiver class // This class is used to receive UDP packets on a particular port, but does not // create UDPRequest objects from them - they are considered to be simply // Messages. A UDPReceiver is most likely to be used in ad-hoc networking and // to receive advertisements and/or broadcasts from other peers. class UDPReceiver : public Socket, public MessagePort { }; // End of UDPReceiver class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDPBroadcaster class // This class is used to advertise / broadcast Messages using UDP. class UDPBroadcaster : public Socket, public MessagePort { }; // End of UDPBroadcaster class //////////////////////////////////////////////////////////////////////////////// } // End namespace codedweller