12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019 |
- // networking.cpp
- //
- // Copyright (C) 2004-2020 MicroNeil Research Corporation.
- //
- // This software is released under the MIT license. See LICENSE.TXT.
-
- #include "networking.hpp"
-
- namespace codedweller {
-
- Networking Network; // Creating _THE_ Network instance.
-
- #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
-
- int Networking::getLastError() { // In windows you get the last error
- return WSAGetLastError(); // from WSAGetLastError();
- }
-
- int Networking::setNonBlocking(hSocket socket) { // Set a winsock to non-blocking
- unsigned long nonblocking = 1; // Create a flag...
- int result = 0;
- if(0 != ioctlsocket(socket, FIONBIO, &nonblocking)) { // Set the state of the socket.
- result = -1; // If that fails then return -1.
- }
- return result; // Show 'em my motto!
- }
-
- int Networking::closeSocket(hSocket socket) { // Close a socket in winsock
- return closesocket(socket); // wraps closesocket().
- }
-
- bool Networking::WouldBlock(int ErrorCode) { // ErrorCode matches [WSA]EWOULDBLOCK.
- return (WSAEWOULDBLOCK == ErrorCode);
- }
-
- bool Networking::InProgress(int ErrorCode) { // ErrorCode matches [WSA]EINPROGRESS.
- return( // [WSA]EALREADY also returns true.
- WSAEINPROGRESS == ErrorCode || // In fact, on Win* platforms we could
- WSAEALREADY == ErrorCode || // get any of these when retesting
- WSAEWOULDBLOCK == ErrorCode || // open() for a connection.
- WSAEINVAL == ErrorCode
- );
- }
-
- bool Networking::IsConnected(int ErrorCode) { // ErrorCode matches [WSA]EISCONN.
- return(WSAEISCONN == ErrorCode);
- }
-
- #else
-
- //// GNU platform
-
- int Networking::getLastError() { // In GNU you get the last error
- return errno; // from errno;
- }
-
- int Networking::setNonBlocking(hSocket socket) { // Set a socket to non-blocking
- int flags, result; // Grab a place to hold the flags.
- flags = fcntl(socket, F_GETFL, 0); // Get the current flags.
- result = fcntl(socket, F_SETFL, flags | O_NONBLOCK); // Set the NONBLOCK flag & return.
- return result; // Return the result.
- }
-
- int Networking::closeSocket(hSocket socket) { // Close a socket in GNU
- return close(socket); // wraps close().
- }
-
- bool Networking::WouldBlock(int ErrorCode) { // ErrorCode matches [WSA]EWOULDBLOCK.
- return (EWOULDBLOCK == ErrorCode);
- }
-
- bool Networking::InProgress(int ErrorCode) { // ErrorCode matches [WSA]EINPROGRESS.
- return( // [WSA]EALREADY also returns true.
- EINPROGRESS == ErrorCode ||
- EALREADY == ErrorCode
- );
- }
-
- bool Networking::IsConnected(int ErrorCode) { // ErrorCode matches [WSA]EISCONN.
- return(EISCONN == ErrorCode);
- }
-
- #endif
-
- // End Platform Specific
- ////////////////////////////////////////////////////////////////////////////////
- // Begin Platform Agnostic
-
- //// class IP4Address //////////////////////////////////////////////////////////
-
- IP4Address::IP4Address():IP(0){} // Blank constructor IP = 0.0.0.0
- IP4Address::IP4Address(const unsigned long int newIP):IP(newIP){} // Constructor given unsigned long
- IP4Address::IP4Address(const IP4Address& newIP):IP(newIP.IP){} // Constructor given an IP4Address
-
- IP4Address::IP4Address(const char* newIP) { (*this) = newIP; } // Construcing with a cstring.
- IP4Address::IP4Address(const std::string& newIP) { (*this) = newIP; } // Constructing with a cppstring.
-
- IP4Address& IP4Address::operator=(const unsigned long int Right) { // Convert from unsigned long int.
- IP = Right;
- return *this;
- }
-
- IP4Address& IP4Address::operator=(const char* Right) { // Convert from c string.
- IP = ntohl(inet_addr(Right));
- return *this;
- }
-
- IP4Address& IP4Address::operator=(const std::string& Right) { // Convert from cpp string.
- IP = ntohl(inet_addr(Right.c_str()));
- return *this;
- }
-
- bool IP4Address::operator<(const IP4Address Right) const { // < Comparison.
- return (IP < Right.IP);
- }
-
- bool IP4Address::operator>(const IP4Address Right) const { // > Comparison.
- return (IP > Right.IP);
- }
-
- bool IP4Address::operator==(const IP4Address Right) const { // == Comparison.
- return (IP == Right.IP);
- }
-
- bool IP4Address::operator!=(const IP4Address Right) const { // != Comparison.
- return (IP != Right.IP);
- }
-
- bool IP4Address::operator<=(const IP4Address Right) const { // <= Comparison.
- return (IP <= Right.IP);
- }
-
- bool IP4Address::operator>=(const IP4Address Right) const { // >= Comparison.
- return (IP >= Right.IP);
- }
-
- //// class SocketAddress ///////////////////////////////////////////////////////
-
- void SocketAddress::clear() {
- memset(&Address, 0, sizeof(Address)); // Zero out the address strcuture
- Address.sin_family = AF_INET; // Internet Address Family ip4
- Address.sin_addr.s_addr = htonl(INADDR_ANY); // Any IP address
- Address.sin_port = 0; // Zero means any port.
- }
-
- SocketAddress::SocketAddress() { // Constructor sets up w/ wildcards
- clear(); // Conveniently, we can use clear() :-)
- }
-
- struct sockaddr_in* SocketAddress::getPtr_sockaddr_in() { // Returns a pointer to sockaddr_in.
- return &Address; // Simply return it's address.
- }
-
- struct sockaddr* SocketAddress::getPtr_sockaddr() { // Returns a pointer to sockaddr.
- return (struct sockaddr*) &Address;
- }
-
-
- socklen_t SocketAddress::getAddressSize() {
- return sizeof(Address); // Return the size of the structure.
- }
-
- void SocketAddress::setAddress(unsigned long ipAddress) { // Set the IP address from an unsigned int
- Address.sin_addr.s_addr = htonl(ipAddress); // Convert to network order and assign.
- }
-
- void SocketAddress::setAddress(char* ipString) { // Set the IP address from a cstring
- Address.sin_addr.s_addr = inet_addr(ipString); // Convert to number and assign.
- }
-
- unsigned long SocketAddress::getAddress() { // Get the IP address as an unsigned int
- return ntohl(Address.sin_addr.s_addr); // Convert to host order and return.
- }
-
- void SocketAddress::setPort(unsigned short port) { // Set the port address from an int
- Address.sin_port = htons(port); // Convert to network order and set.
- }
-
- void SocketAddress::setPort(char* port) { // Set the port address from a cstring
- setPort(atoi(port)); // Convert to int and set.
- }
-
- unsigned short SocketAddress::getPort() { // Get the port address as an unsigned int
- return ntohs(Address.sin_port); // Convert to host order and return.
- }
-
- const char* SocketAddress::getPort(char* str) { // Get the port address into a cstring.
- if(NULL == str) { // If the caller did not provide a
- str = PortStringBuffer; // buffer to use then we will use ours.
- }
- sprintf(str,"%d",getPort()); // Get the port and convert to cstring.
- return str; // Return the string we got.
- }
-
- //// class Socket //////////////////////////////////////////////////////////////
-
- Socket::Socket() : // When starting up we are
- Handle(INVALID_SOCKET), OpenSucceeded(false) { // not yet valid.
- }
-
- Socket::~Socket() { // When shutting down, be sure
- if(INVALID_SOCKET != Handle) { // any open socket is closed without
- Network.closeSocket(Handle); // throwing any exceptions.
- }
- }
-
- void Socket::close() { // When we close,
- if(INVALID_SOCKET != Handle) { // If the handle is open then
- if(Network.closeSocket(Handle)) { // close the handle and check for error.
- LastError = Network.getLastError(); // If there was an error record it.
- if(!Network.WouldBlock(LastError)) { // If the error was not WOULDBLOCK
- throw Networking::ControlError( // then throw a ControlError exception.
- Network.DescriptiveError(
- "Socket::close()", LastError));
- }
- } else { // If there was no error then
- LastError = 0; // reset the LastError value.
- }
- Handle = INVALID_SOCKET; // and reset the handle to INVALID.
- NonBlocking = false; // The default is Blocking.
- OpenSucceeded = false; // After close, forget we opened.
- }
- }
-
- hSocket Socket::getHandle() { // Returns the current Socket handle.
- return Handle;
- }
-
- bool Socket::isNonBlocking() { // Returns true if socket is NonBlocking
- return NonBlocking;
- }
-
- void Socket::makeNonBlocking() { // Sets the socket to NonBlocking mode.
- if(0 > Network.setNonBlocking(Handle)) { // Feed the call through Network.
- LastError = Network.getLastError(); // If it didn't work, go get the error.
- NonBlocking = false; // We are NOT NonBlocking.
- throw Networking::ControlError( // Throw a control error.
- Network.DescriptiveError(
- "Socket::makeNonBlocking()", LastError));
- } else {
- NonBlocking = true; // If we didn't throw, we're ON.
- }
- }
-
- bool Socket::isReuseAddress() { return ReuseAddress; } // True if socket is set SO_REUSEADDR.
- bool Socket::isReuseAddress(bool set) { return (ReuseAddress = set); } // Changes SO_REUSEADDR setting.
-
- bool Socket::isOpen() { // True if the socket is open.
- return(
- INVALID_SOCKET != Handle && // A valid handle and
- true == OpenSucceeded // a successful open operation
- ); // means we're open.
- }
-
- int Socket::getLastError() { // Returns the last error for this socket.
- return LastError;
- }
-
- //// class TCPClient ///////////////////////////////////////////////////////////
-
- TCPClient::TCPClient(TCPListener& L, hSocket H, SocketAddress& A) : // How to create a TCPClient.
- MyListener(L) { // Capture our listener.
- Handle = H; // Capture the new socket handle.
- RemoteAddress = A; // Capture the client address.
- ReadPointer = ReadBuffer; // Set the read position to zero.
- DataLength = 0; // There is no data yet.
- OpenSucceeded = true; // We're getting an open socket.
- }
-
- TCPClient::~TCPClient() { // When destroying a TCPClient
- try{ if(isOpen()) close(); } catch(...) {} // silently close any open connections.
- }
-
- void TCPClient::open() { // We provide open() as unsupported.
- throw Networking::NotSupportedError( // Throw an exception if this is called.
- Network.DescriptiveError(
- "TCPClient::open()", LastError));
- }
-
- bool TCPClient::ReadBufferIsEmpty() { // True if the ReadBuffer is empty.
- return (0 >= DataLength); // We can check that with DataLength.
- }
-
- void TCPClient::fillReadBuffer() { // Fills the buffer from the socket.
- LastError = 0; // Clear the LastError value.
- ReadPointer = ReadBuffer; // Reset the ReadPointer.
- DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), MSG_NOSIGNAL); // Try to read some data.
-
- if(0 >= DataLength) { // If there was an error then
- LastError = Network.getLastError(); // Grab the last error code.
- DataLength = 0; // Correct the DataLength.
- if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then
- return; // simply return - it's ok.
- } else { // If it was a different error
- throw Networking::SocketReadError( // then throw a ReadError.
- Network.DescriptiveError(
- "TCPClient::fillReadBuffer()", LastError));
- }
- } // If we succeeded then our ReadBuffer
- } // assembly is in good shape.
-
- bool TCPClient::isNonBlocking() { // Provided for MessagePort.
- return Socket::isNonBlocking();
- }
-
- unsigned long TCPClient::getRemoteIP() { // Get remote IP as long.
- return RemoteAddress.getAddress();
- }
-
- const char* TCPClient::getRemoteIP(char* str) { // Get IP as string.
- return RemoteAddress.getAddress(str);
- }
-
- unsigned short TCPClient::getRemotePort() { // Get remote Port as unsigned short.
- return RemoteAddress.getPort();
- }
-
- const char* TCPClient::getRemotePort(char* str) { // Get Port as string.
- return RemoteAddress.getPort(str);
- }
-
- //// class TCPHost /////////////////////////////////////////////////////////////
-
- TCPHost::~TCPHost() { // When destroying a TCPHost
- try{ if(isOpen()) close(); } catch(...) {} // silently close any open connection.
- }
-
- bool TCPHost::ReadBufferIsEmpty() { // True if the ReadBuffer is empty.
- return (0 >= DataLength); // We can check that with DataLength.
- }
-
- void TCPHost::fillReadBuffer() { // Fills the buffer from the socket.
- LastError = 0; // Clear the LastError value.
- ReadPointer = ReadBuffer; // Reset the ReadPointer.
- DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), MSG_NOSIGNAL); // Try to read some data.
-
- if(0 >= DataLength) { // If there was an error then
- LastError = Network.getLastError(); // Grab the last error code.
- DataLength = 0; // Correct the DataLength.
- if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then
- return; // simply return - it's ok.
- } else { // If it was a different error
- throw Networking::SocketReadError( // then throw a ReadError.
- Network.DescriptiveError(
- "TCPHost::fillReadBuffer()", LastError));
- }
- } // If we succeeded then our ReadBuffer
- } // assembly is in good shape.
-
- bool TCPHost::isNonBlocking() { // Provided for MessagePort.
- return Socket::isNonBlocking();
- }
-
- //// class TCPListener /////////////////////////////////////////////////////////
-
- TCPListener::~TCPListener() { // When destroying a TCPListener
- try{ close(); } catch(...) {} // silently close if not already done.
- }
-
- //// Platform Specific Stuff ///////////////////////////////////////////////////
-
- #if defined(WIN32) || defined(WIN64)
-
- ////////////////////////////////////////////////////////////////////////////////
- //// Being Windows specific code
-
- WSADATA WSSTartData; // Socket library data structure.
-
- // Error description handling for humans.
-
- string Networking::DescriptiveError(string Msg, int Errno) { // Form a descriptive error w/ errno.
- string s = ""; // Message string.
-
- switch(Errno) { // Assign the appropriate message.
- case WSA_INVALID_HANDLE: s = "WSA_INVALID_HANDLE"; break;
- case WSA_NOT_ENOUGH_MEMORY: s = "WSA_NOT_ENOUGH_MEMORY"; break;
- case WSA_INVALID_PARAMETER: s = "WSA_INVALID_PARAMETER"; break;
- case WSA_OPERATION_ABORTED: s = "WSA_OPERATION_ABORTED"; break;
- case WSA_IO_INCOMPLETE: s = "WSA_IO_INCOMPLETE"; break;
- case WSA_IO_PENDING: s = "WSA_IO_PENDING"; break;
- case WSAEINTR: s = "WSAEINTR"; break;
- case WSAEBADF: s = "WSAEBADF"; break;
- case WSAEACCES: s = "WSAEACCES"; break;
- case WSAEFAULT: s = "WSAEFAULT"; break;
- case WSAEINVAL: s = "WSAEINVAL"; break;
- case WSAEMFILE: s = "WSAEMFILE"; break;
- case WSAEWOULDBLOCK: s = "WSAEWOULDBLOCK"; break;
- case WSAEINPROGRESS: s = "WSAEINPROGRESS"; break;
- case WSAEALREADY: s = "WSAEALREADY"; break;
- case WSAENOTSOCK: s = "WSAENOTSOCK"; break;
- case WSAEDESTADDRREQ: s = "WSAEDESTADDRREQ"; break;
- case WSAEMSGSIZE: s = "WSAEMSGSIZE"; break;
- case WSAEPROTOTYPE: s = "WSAEPROTOTYPE"; break;
- case WSAENOPROTOOPT: s = "WSAENOPROTOOPT"; break;
- case WSAEPROTONOSUPPORT: s = "WSAEPROTONOSUPPORT"; break;
- case WSAESOCKTNOSUPPORT: s = "WSAESOCKTNOSUPPORT"; break;
- case WSAEOPNOTSUPP: s = "WSAEOPNOTSUPP"; break;
- case WSAEPFNOSUPPORT: s = "WSAEPFNOSUPPORT"; break;
- case WSAEAFNOSUPPORT: s = "WSAEAFNOSUPPORT"; break;
- case WSAEADDRINUSE: s = "WSAEADDRINUSE"; break;
- case WSAEADDRNOTAVAIL: s = "WSAEADDRNOTAVAIL"; break;
- case WSAENETDOWN: s = "WSAENETDOWN"; break;
- case WSAENETUNREACH: s = "WSAENETUNREACH"; break;
- case WSAENETRESET: s = "WSAENETRESET"; break;
- case WSAECONNABORTED: s = "WSAECONNABORTED"; break;
- case WSAECONNRESET: s = "WSAECONNRESET"; break;
- case WSAENOBUFS: s = "WSAENOBUFS"; break;
- case WSAEISCONN: s = "WSAEISCONN"; break;
- case WSAENOTCONN: s = "WSAENOTCONN"; break;
- case WSAESHUTDOWN: s = "WSAESHUTDOWN"; break;
- case WSAETOOMANYREFS: s = "WSAETOOMANYREFS"; break;
- case WSAETIMEDOUT: s = "WSAETIMEDOUT"; break;
- case WSAECONNREFUSED: s = "WSAECONNREFUSED"; break;
- case WSAELOOP: s = "WSAELOOP"; break;
- case WSAENAMETOOLONG: s = "WSAENAMETOOLONG"; break;
- case WSAEHOSTDOWN: s = "WSAEHOSTDOWN"; break;
- case WSAEHOSTUNREACH: s = "WSAEHOSTUNREACH"; break;
- case WSAENOTEMPTY: s = "WSAENOTEMPTY"; break;
- case WSAEPROCLIM: s = "WSAEPROCLIM"; break;
- case WSAEUSERS: s = "WSAEUSERS"; break;
- case WSAEDQUOT: s = "WSAEDQUOT"; break;
- case WSAESTALE: s = "WSAESTALE"; break;
- case WSAEREMOTE: s = "WSAEREMOTE"; break;
- case WSASYSNOTREADY: s = "WSASYSNOTREADY"; break;
- case WSAVERNOTSUPPORTED: s = "WSAVERNOTSUPPORTED"; break;
- case WSANOTINITIALISED: s = "WSANOTINITIALISED"; break;
- case WSAEDISCON: s = "WSAEDISCON"; break;
- case WSAENOMORE: s = "WSAENOMORE"; break;
- case WSAECANCELLED: s = "WSAECANCELLED"; break;
- case WSAEINVALIDPROCTABLE: s = "WSAEINVALIDPROCTABLE"; break;
- case WSAEINVALIDPROVIDER: s = "WSAEINVALIDPROVIDER"; break;
- case WSAEPROVIDERFAILEDINIT: s = "WSAEPROVIDERFAILEDINIT"; break;
- case WSASYSCALLFAILURE: s = "WSASYSCALLFAILURE"; break;
- case WSASERVICE_NOT_FOUND: s = "WSASERVICE_NOT_FOUND"; break;
- case WSATYPE_NOT_FOUND: s = "WSATYPE_NOT_FOUND"; break;
- case WSA_E_NO_MORE: s = "WSA_E_NO_MORE"; break;
- case WSA_E_CANCELLED: s = "WSA_E_CANCELLED"; break;
- case WSAEREFUSED: s = "WSAEREFUSED"; break;
- case WSAHOST_NOT_FOUND: s = "WSAHOST_NOT_FOUND"; break;
- case WSATRY_AGAIN: s = "WSATRY_AGAIN"; break;
- case WSANO_RECOVERY: s = "WSANO_RECOVERY"; break;
- case WSANO_DATA: s = "WSANO_DATA"; break;
- case WSA_QOS_RECEIVERS: s = "WSA_QOS_RECEIVERS"; break;
- case WSA_QOS_SENDERS: s = "WSA_QOS_SENDERS"; break;
- case WSA_QOS_NO_SENDERS: s = "WSA_QOS_NO_SENDERS"; break;
- case WSA_QOS_NO_RECEIVERS: s = "WSA_QOS_NO_RECEIVERS"; break;
- case WSA_QOS_REQUEST_CONFIRMED: s = "WSA_QOS_REQUEST_CONFIRMED"; break;
- case WSA_QOS_ADMISSION_FAILURE: s = "WSA_QOS_ADMISSION_FAILURE"; break;
- case WSA_QOS_POLICY_FAILURE: s = "WSA_QOS_POLICY_FAILURE"; break;
- case WSA_QOS_BAD_STYLE: s = "WSA_QOS_BAD_STYLE"; break;
- case WSA_QOS_BAD_OBJECT: s = "WSA_QOS_BAD_OBJECT"; break;
- case WSA_QOS_TRAFFIC_CTRL_ERROR: s = "WSA_QOS_TRAFFIC_CTRL_ERROR"; break;
- case WSA_QOS_GENERIC_ERROR: s = "WSA_QOS_GENERIC_ERROR"; break;
- case WSA_QOS_ESERVICETYPE: s = "WSA_QOS_ESERVICETYPE"; break;
- case WSA_QOS_EFLOWSPEC: s = "WSA_QOS_EFLOWSPEC"; break;
- case WSA_QOS_EPROVSPECBUF: s = "WSA_QOS_EPROVSPECBUF"; break;
- case WSA_QOS_EFILTERSTYLE: s = "WSA_QOS_EFILTERSTYLE"; break;
- case WSA_QOS_EFILTERTYPE: s = "WSA_QOS_EFILTERTYPE"; break;
- case WSA_QOS_EFILTERCOUNT: s = "WSA_QOS_EFILTERCOUNT"; break;
- case WSA_QOS_EOBJLENGTH: s = "WSA_QOS_EOBJLENGTH"; break;
- case WSA_QOS_EFLOWCOUNT: s = "WSA_QOS_EFLOWCOUNT"; break;
- case WSA_QOS_EPOLICYOBJ: s = "WSA_QOS_EPOLICYOBJ"; break;
- case WSA_QOS_EFLOWDESC: s = "WSA_QOS_EFLOWDESC"; break;
- case WSA_QOS_EPSFLOWSPEC: s = "WSA_QOS_EPSFLOWSPEC"; break;
- case WSA_QOS_EPSFILTERSPEC: s = "WSA_QOS_EPSFILTERSPEC"; break;
- case WSA_QOS_ESDMODEOBJ: s = "WSA_QOS_ESDMODEOBJ"; break;
- case WSA_QOS_ESHAPERATEOBJ: s = "WSA_QOS_ESHAPERATEOBJ"; break;
- case WSA_QOS_RESERVED_PETYPE: s = "WSA_QOS_RESERVED_PETYPE"; break;
-
- #ifdef WSA_QOS_EUNKOWNPSOBJ
- case WSA_QOS_EUNKOWNPSOBJ: s = "WSA_QOS_EUNKOWNPSOBJ"; break;
- #endif
- }
-
- Msg.append(" "); // Add a space to the existing message.
- if(0 < s.length()) { // If we know the message for Errno
- Msg.append(s); // then append it.
- }
- else { // If we don't know what Errno means
- std::ostringstream ErrNoMsg; // then say so and pass on Errno as
- ErrNoMsg << " UNKNOWN ErrorNumber = " << Errno; // well so someone can figure it out.
- Msg.append(ErrNoMsg.str());
- }
- return Msg;
- };
-
- // Networking Constructor //////////////////////////////////////////////////////
- // Handles any necessary setup of Network Module resources.
-
- Networking::Networking() { // Upon initialization,
- if(0 != WSAStartup(MAKEWORD (2,0), &WSSTartData)) { // startup the Winsock2.0 DLL.
- throw InitializationError( // If that fails then throw!
- "Networking::Networking() if(0 != WSAStartup(MAKEWORD (2,0), &WSSTartData))"
- );
- }
- }
-
- // Networking Destructor ///////////////////////////////////////////////////////
- // Handles any necessary cleanup of Network Module resources.
-
- Networking::~Networking() { // Upon shutdown,
- WSACleanup(); // shutdown the Winsock DLL.
- }
-
- //// Emd Windows specific code
- ////////////////////////////////////////////////////////////////////////////////
-
- #else
-
- ////////////////////////////////////////////////////////////////////////////////
- //// Begin GNU specific code
-
- // Error description handling for humans.
-
- std::string Networking::DescriptiveError(std::string Msg, int Errno) { // Form a descriptive error w/ errno.
- Msg.append(" "); Msg.append(strerror(Errno));
- return Msg;
- };
-
- // Networking Constructor //////////////////////////////////////////////////////
- // Handles any necessary setup of Network Module resources.
-
- Networking::Networking() { // Upon initialization,
- // Nothing So Far... // nothing special required.
- }
-
- // Networking Destructor ///////////////////////////////////////////////////////
- // Handles any necessary cleanup of Network Module resources.
-
- Networking::~Networking() { // GNU sockets cleanup,
- // Nothing So Far... // nothing specail to required.
- }
-
- //// End GNU specific code
- ////////////////////////////////////////////////////////////////////////////////
-
- #endif
-
- ////////////////////////////////////////////////////////////////////////////////
- //// Platform Agnostic Stuff
-
- //// Useful Internal Bits & Pieces /////////////////////////////////////////////
-
- const int LowestOctetMask = 0x000000FF; // The bits to look at.
- const int OneOctetInBits = 8; // The bits to shift.
-
- void splitIP( // Split an IP into octets.
- unsigned long A, // The address in host format.
- int& a0, // Reference to the first octet.
- int& a1, // Reference to the second octet.
- int& a2, // Reference to the third octet.
- int& a3 // Reference to the forth octet.
- ){
- a3 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the lowest order octet & move.
- a2 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the next lowest octet & move.
- a1 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the next lowest octet & move.
- a0 = A & LowestOctetMask; // Get the highest octet. That's IT!
- }
-
- //// IP4Address methods ////////////////////////////////////////////////////////
-
- IP4Address::operator unsigned long int() const { // Assign to unsigned long int.
- return IP; // Return it.
- }
-
- IP4Address::operator std::string() const { // Assign to a string.
- char stringbfr[IPStringBufferSize]; // Grab a temporary buffer.
- memset(stringbfr, 0, sizeof(stringbfr)); // Null out it's space.
- int a0, a1, a2, a3; // Grab some integers.
- splitIP(IP, a0, a1, a2, a3); // Split the IP in the IP4Address.
- sprintf(stringbfr, "%d.%d.%d.%d", a0, a1, a2, a3); // Format the octets.
- return std::string(stringbfr); // Return a string.
- }
-
- //// SocketAddress methods /////////////////////////////////////////////////////
-
- // getAddress(str, len)
-
- const char* SocketAddress::getAddress(char* str) { // Get the IP address into a cstring.
- if(NULL == str) { // If the caller did not provide a
- str = IPStringBuffer; // buffer to use then we will use ours.
- }
- int a0, a1, a2, a3; // Grab a bunch of handy integers.
- getAddress(a0, a1, a2, a3); // Get the address as octets.
- sprintf(str, "%d.%d.%d.%d", a0, a1, a2, a3); // Format as dotted decimal notation.
- return str; // Return the output buffer.
- }
-
- // getAddress(int& a0, int& a1, int& a2, int& a3)
-
- void SocketAddress::getAddress(int& a0, int& a1, int& a2, int& a3) { // Get the IP address into 4 ints
- unsigned long A = getAddress(); // Get the address.
- splitIP(A, a0, a1, a2, a3); // Split it into octets.
- }
-
- //// TCPListener methods ///////////////////////////////////////////////////////
-
- TCPListener::TCPListener(unsigned short Port) { // Set up localhost on this Port.
- LocalAddress.setPort(Port); // Establish the port.
- LocalAddress.setAddress(LOCALHOST); // Set the address to LOCALHOST.
- MaxPending = DefaultMaxPending; // Use the default inbound queue size.
- ReuseAddress = true; // ReuseAddress on by default.
- OpenStage1Complete = false; // This stage of open() not yet done.
- OpenStage2Complete = false; // This stage of open() not yet done.
-
- // Create a socket...
-
- LastError = 0;
-
- Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket.
- if(INVALID_SOCKET == Handle) { // If that operation failed then
- LastError = Network.getLastError(); // grab the error code and
- throw Networking::SocketCreationError( // throw.
- Network.DescriptiveError(
- "TCPListener::TCPListener().socket()", LastError));
- }
- }
-
- TCPListener::TCPListener(SocketAddress& WhereToBind) { // Set up specific "name" for listening.
- LocalAddress = WhereToBind; // Make my Local address as provided.
- MaxPending = DefaultMaxPending; // Use the default inbound queue size.
- ReuseAddress = true; // ReuseAddress on by default.
- OpenStage1Complete = false; // This stage of open() not yet done.
- OpenStage2Complete = false; // This stage of open() not yet done.
-
- // Create a socket...
-
- LastError = 0;
-
- Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket.
- if(INVALID_SOCKET == Handle) { // If that operation failed then
- LastError = Network.getLastError(); // grab the error code and
- throw Networking::SocketCreationError( // throw.
- Network.DescriptiveError(
- "TCPListener::TCPListener().socket()", LastError));
- }
- }
-
- // open()
-
- void TCPListener::open() { // Open when ready.
-
- if(OpenSucceeded) return; // If open already, we're done.
-
- LastError = 0; // Clear the last error.
- bool SuccessFlag = true; // Start optimistically.
-
- // Set SO_REUSEADDR if turned on
-
- if(!OpenStage1Complete) { // Do this stage only once.
- int ReuseAddress_Flag = (ReuseAddress? 1:0); // Setup an appropriate integer flag.
- int result = // Set SO_REUSEADDR before bind().
- setsockopt(
- Handle,
- SOL_SOCKET,
- SO_REUSEADDR,
- (char*) &ReuseAddress_Flag,
- sizeof(ReuseAddress_Flag));
-
- if(0 > result) { // If there was an error then
- SuccessFlag = false; // we did not succeed.
- LastError = Network.getLastError(); // Capture the error information and
- throw Networking::SocketSetSockOptError( // throw.
- Network.DescriptiveError(
- "TCPListener::open().setsockopt(SO_REUSEADDR)", LastError));
- }
- OpenStage1Complete = true; // Stage 1 complete now.
- } // End of open() stage 1
-
- // Next we bind it...
-
- if(!OpenStage2Complete) { // Do this stage only once.
- int result = // Bind our socket to the LocalAddress.
- bind(
- Handle,
- LocalAddress.getPtr_sockaddr(),
- LocalAddress.getAddressSize());
-
- if(0 > result) { // If there was an error then
- SuccessFlag = false; // we did not succeed.
- LastError = Network.getLastError(); // Capture the error information and
- throw Networking::SocketBindError( // throw.
- Network.DescriptiveError(
- "TCPListener::open().bind()", LastError));
- }
- OpenStage2Complete = true; // Stage 2 complete now.
- } // End of open() stage 2
-
- // Then we put it in a listening state...
-
- int result = listen(Handle, MaxPending); // Listen for up to MaxPending at once.
-
- if(0 > result) { // If an error occurred then
- SuccessFlag = false; // we did not succeed.
- LastError = Network.getLastError(); // Capture the error information and
- throw Networking::SocketListenError( // throw.
- Network.DescriptiveError(
- "TCPListener::open().listen()", LastError));
- }
-
- OpenSucceeded = SuccessFlag; // So, did we succeed?
- }
-
- // acceptClient()
-
- TCPClient* TCPListener::acceptClient() { // Accept a client connection.
-
- LastError = 0; // Clear the last error.
- socklen_t rsize = RemoteAddress.getAddressSize(); // Size as an int for accept().
-
- hSocket NewHandle = // Accept a new connection if available.
- accept(
- Handle, // use our handle, of course,...
- RemoteAddress.getPtr_sockaddr(), // and store the remote hosts
- &rsize); // address for us.
-
- if(INVALID_SOCKET == NewHandle) { // If there was an error then
- LastError = Network.getLastError(); // capture the error value.
- if(!Network.WouldBlock(LastError)) { // If it's not a EWOULDBLOCK error
- throw Networking::SocketAcceptError( // then we need to throw.
- Network.DescriptiveError(
- "TCPListener::acceptClient().accept()", LastError));
- } else { // EWOULDBLOCK errors are normal in
- return NULL; // non blocking mode so we return
- } // NULL when we see them.
- }
-
- // Set SO_NOSIGPIPE if needed
-
- if( // On some systems we may have to
- 0 != SO_NOSIGPIPE && // use SO_NOSIPIPE but if they offer
- 0 == MSG_NOSIGNAL // MSG_NOSIGNAL we prefer that instead.
- ) {
- int TurnedOn = 1; // Prepare to turn this option on.
- int result = // Set SO_NOSIGPIPE.
- setsockopt(
- NewHandle,
- SOL_SOCKET,
- SO_NOSIGPIPE,
- (char*) &TurnedOn,
- sizeof(TurnedOn));
-
- if(0 > result) { // If there was an error then
- LastError = Network.getLastError(); // Capture the error information
- Network.closeSocket(NewHandle); // close the handle (avoid leaks)
- throw Networking::SocketSetSockOptError( // and throw a descriptive exception.
- Network.DescriptiveError(
- "TCPListener::acceptClient().setsockopt(SO_NOSIGPIPE)", LastError));
- }
- }
-
- // If things have gone well we can do what we came for.
-
- return new TCPClient(*this, NewHandle, RemoteAddress); // Create the new TCPClient object.
-
- }
-
- //// TCPClient methods /////////////////////////////////////////////////////////
-
- int TCPClient::transmit(const char* bfr, int size) { // How to send a buffer of data.
- if(0 == size) return 0; // Nothing to send, send nothing.
- if(0 == bfr) // Watch out for null buffers.
- throw Networking::SocketWriteError("TCPClient::transmit() NULL Bfr!");
- if(0 > size) // Watch out for bad sizes.
- throw Networking::SocketWriteError("TCPClient::transmit() 0 > size!");
-
- LastError = 0; // No errors yet.
- int ByteCount = 0; // No bytes sent yet this pass.
- ByteCount = send(Handle, bfr, size, MSG_NOSIGNAL); // Try to send and capture the count.
- LastError = Network.getLastError(); // Grab any error code.
-
- bool AnErrorOccurred = (0 > ByteCount); // How to know if an error occurred.
- const int NoBytesSent = 0; // This is our "Would Block" result.
-
- if(AnErrorOccurred) { // If there was an error check it out.
- if(Network.WouldBlock(LastError)) { // If the error was "Would Block" then
- return NoBytesSent; // return no bytes sent (try again).
- } else { // If this was a different kind of error
- throw Networking::SocketWriteError( // then throw!
- Network.DescriptiveError(
- "TCPClient::transmit().send()", LastError));
- }
- }
-
- return ByteCount; // Usually: return the sent byte count.
- }
-
- int TCPClient::receive(char* bfr, int size) { // How to receive a buffer of data.
- if(ReadBufferIsEmpty()) { // If the read buffer is empty then
- fillReadBuffer(); // fill it first.
- } // Optimize our transfer to the smaller
- if(DataLength < size) { // of what we have or the size of the
- size = DataLength; // provided buffer. This way we ony check
- } // one value in our copy loop ;-)
- int RemainingDataLength = size; // Capture the length of data to xfer.
- while(0 < RemainingDataLength) { // While we have work to do
- *bfr = *ReadPointer; // copy each byte from our ReadBuffer,
- bfr++; ReadPointer++; // move the pointers to the next byte,
- DataLength--; // update our ReadBuffers's DataLength,
- RemainingDataLength--; // and count down the bytes left to xfer.
- }
- return size; // When done, say how much we moved.
- }
-
- int TCPClient::delimited_receive(char* bfr, int size, char delimiter) { // How to receive delimited data.
- if(ReadBufferIsEmpty()) { // If the read buffer is empty then
- fillReadBuffer(); // fill it first.
- } // Optimize our transfer to the smaller
- if(DataLength < size) { // of what we have or the size of the
- size = DataLength; // provided buffer. This way we ony check
- } // one value in our copy loop ;-)
- int Count = 0; // Keep our byte count in scope.
- bool DelimiterNotReached = true; // Watching for our deliimiter.
- while((Count < size) && DelimiterNotReached) { // While there is work to do...
- *bfr = *ReadPointer; // copy each byte from our ReadBuffer,
- DelimiterNotReached = (delimiter != (*bfr)); // check for the delimiter character,
- bfr++; ReadPointer++; // move the pointers to the next byte,
- DataLength--; // update our ReadBuffers's DataLength,
- Count++; // and count up the bytes we have moved.
- }
- return Count; // When done, say how much we moved.
- }
-
- //// TCPHost methods ///////////////////////////////////////////////////////////
-
- // Constructors...
-
- TCPHost::TCPHost(unsigned short Port) { // Will connect to localhost on Port.
- RemoteAddress.setPort(Port); // Connect to Port on
- RemoteAddress.setAddress(LOCALHOST); // Localhost.
- ReadPointer = ReadBuffer; // Set the read position to zero.
- DataLength = 0; // There is no data yet.
- ReuseAddress = false; // ReuseAddress off by default.
- OpenStage1Complete = false; // Stage 1 of open() not done yet.
-
- // Create a socket to use.
-
- LastError = 0; // Clear our last error value.
-
- Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket.
- if(0 > Handle) { // If that operation failed then
- LastError = Network.getLastError(); // grab the error code and
- throw Networking::SocketCreationError( // throw.
- Network.DescriptiveError(
- "TCPHost::TCPHost().socket()", LastError));
- }
- }
-
- TCPHost::TCPHost(SocketAddress& Remote) { // Will connect to Remote address/port.
- RemoteAddress = Remote; // Capture the provided address.
- ReadPointer = ReadBuffer; // Set the read position to zero.
- DataLength = 0; // There is no data yet.
- ReuseAddress = false; // ReuseAddress off by default.
- OpenStage1Complete = false; // Stage 1 of open() not done yet.
-
- // Create a socket to use.
-
- LastError = 0; // Clear our last error value.
-
- Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket.
- if(0 > Handle) { // If that operation failed then
- LastError = Network.getLastError(); // grab the error code and
- throw Networking::SocketCreationError( // throw.
- Network.DescriptiveError(
- "TCPHost::TCPHost().socket()", LastError));
- }
- }
-
- // Methods...
-
- void TCPHost::open() { // We provide open().
-
- if(OpenSucceeded) return; // If open already, we're done.
-
- LastError = 0; // Clear our LastError value.
- bool SuccessFlag = true; // Begin optimistically.
-
- // Set Socket Options
-
- if(!OpenStage1Complete) { // If we haven't done this yet:
-
- // Set SO_REUSEADDR if turned on
-
- int ReuseAddress_Flag = (ReuseAddress? 1:0); // Setup an appropriate integer flag.
- int result = // Set SO_REUSEADDR before bind().
- setsockopt(
- Handle,
- SOL_SOCKET,
- SO_REUSEADDR,
- (char*) &ReuseAddress_Flag,
- sizeof(ReuseAddress_Flag));
-
- if(0 > result) { // If there was an error then
- SuccessFlag = false; // we did not succeed.
- LastError = Network.getLastError(); // Capture the error information and
- throw Networking::SocketSetSockOptError( // throw.
- Network.DescriptiveError(
- "TCPHost::open().setsockopt(SO_REUSEADDR)", LastError));
- }
-
- // Set SO_NOSIGPIPE if needed
-
- if( // On some systems we may have to
- 0 != SO_NOSIGPIPE && // use SO_NOSIPIPE but if they offer
- 0 == MSG_NOSIGNAL // MSG_NOSIGNAL we prefer that instead.
- ) {
- int TurnedOn = 1; // Prepare to turn this option on.
- int result = // Set SO_NOSIGPIPE.
- setsockopt(
- Handle,
- SOL_SOCKET,
- SO_NOSIGPIPE,
- (char*) &TurnedOn,
- sizeof(TurnedOn));
-
- if(0 > result) { // If there was an error then
- SuccessFlag = false; // we did not succeed.
- LastError = Network.getLastError(); // Capture the error information and
- throw Networking::SocketSetSockOptError( // throw.
- Network.DescriptiveError(
- "TCPHost::open().setsockopt(SO_NOSIGPIPE)", LastError));
- }
- }
-
- OpenStage1Complete = true; // Skip this section from now on.
- } // Done with stage 1.
-
- // Connect the socekt to the Host.
-
- int result = // Connect to the remote host
- connect( // using the socket we just
- Handle, // stored in Handle and
- RemoteAddress.getPtr_sockaddr(), // the Remote address.
- RemoteAddress.getAddressSize());
-
- if(0 > result) { // If there was an error then
- SuccessFlag = false; // we did not succeed.
- LastError = Network.getLastError(); // Record the error data.
- if(Network.IsConnected(LastError)) { // If we actually did succeed then
- SuccessFlag = true; // say so. (Silly Winsock!)
- } else // But if that's not the case check
- if( // to see if something bad happened -
- !Network.WouldBlock(LastError) && // not just would-block, or
- !Network.InProgress(LastError) // in progress...
- ) { // If it was something other than
- throw Networking::SocketConnectError( // WouldBlock or InProgress then
- Network.DescriptiveError( // throw!
- "TCPHost::open().connect()", LastError));
- } // If it was WouldBlock then it's
- } // considered to be ok.
-
- OpenSucceeded = SuccessFlag; // So, are we open now?
- }
-
- int TCPHost::transmit(const char* bfr, int size) { // How to send a buffer of data.
- LastError = 0; // No errors yet.
- if(0 == size) return 0; // Nothing to send, send nothing.
- if(0 == bfr) // Watch out for null buffers.
- throw Networking::SocketWriteError("TCPHost::transmit() NULL Bfr!");
- if(0 > size) // Watch out for bad sizes.
- throw Networking::SocketWriteError("TCPHost::transmit() 0 > size!");
-
- int ByteCount = send(Handle, bfr, size, MSG_NOSIGNAL); // Try to send and capture the count.
- if(0 > ByteCount) ByteCount = 0; // Mask error results as 0 bytes sent.
-
- if(size > ByteCount) { // If we didn't send it all check it out.
- LastError = Network.getLastError(); // Grab the error code.
- if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then
- return ByteCount; // it was a partial snd - return.
- } else { // If this was a different kind of error
- throw Networking::SocketWriteError( // then throw!
- Network.DescriptiveError(
- "TCPHost::transmit().send()", LastError));
- }
- }
- return ByteCount; // Ultimately return the byte count.
- }
-
- int TCPHost::receive(char* bfr, int size) { // How to receive a buffer of data.
- if(ReadBufferIsEmpty()) { // If the read buffer is empty then
- fillReadBuffer(); // fill it first.
- } // Optimize our transfer to the smaller
- if(DataLength < size) { // of what we have or the size of the
- size = DataLength; // provided buffer. This way we ony check
- } // one value in our copy loop ;-)
- int RemainingDataLength = size; // Capture the length of data to xfer.
- while(0 < RemainingDataLength) { // While we have work to do
- *bfr = *ReadPointer; // copy each byte from our ReadBuffer,
- bfr++; ReadPointer++; // move the pointers to the next byte,
- DataLength--; // update our ReadBuffers's DataLength,
- RemainingDataLength--; // and count down the bytes left to xfer.
- }
- return size; // When done, say how much we moved.
- }
-
- int TCPHost::delimited_receive(char* bfr, int size, char delimiter) { // How to receive delimited data.
- if(ReadBufferIsEmpty()) { // If the read buffer is empty then
- fillReadBuffer(); // fill it first.
- } // Optimize our transfer to the smaller
- if(DataLength < size) { // of what we have or the size of the
- size = DataLength; // provided buffer. This way we ony check
- } // one value in our copy loop ;-)
- int Count = 0; // Keep our byte count in scope.
- bool DelimiterNotReached = true; // Watching for our deliimiter.
- while((Count < size) && DelimiterNotReached) { // While there is work to do...
- *bfr = *ReadPointer; // copy each byte from our ReadBuffer,
- DelimiterNotReached = (delimiter != (*bfr)); // check for the delimiter character,
- bfr++; ReadPointer++; // move the pointers to the next byte,
- DataLength--; // update our ReadBuffers's DataLength,
- Count++; // and count up the bytes we have moved.
- }
- return Count; // When done, say how much we moved.
- }
-
-
- // End Platform Agnostic Stuff
- ////////////////////////////////////////////////////////////////////////////////
-
- } // End namespace codedweller
|