#ifndef _DOMAINCONNECTION_H #define _DOMAINCONNECTION_H /* TCP basics: Connection Establishment • The client sends a SYN message which contains the server's port and the client's Initial Sequence Number (ISN) to the server (active open). • The server sends back its own SYN and ACK (which consists of the client's ISN + 1). • The Client sends an ACK (which consists of the server's ISN + 1). Connection Tear-down (modified three way handshake). • The client sends a FIN (active close). This is a now a half-closed connection. The client no longer sends data, but is still able to receive data from the server. Upon receiving this FIN, the server enters a passive close state. • The server sends an ACK (which is the clients FIN sequence + 1) • The server sends its own FIN. • The client sends an ACK (which is server's FIN sequence + 1). Upon receiving this ACK, the server closes the connection. A half-closed connection can be used to terminate sending data while sill receiving data. Socket applications can call shutdown with the second argument set to 1 to enter this state. Flags ------------ URG - urgent pointer field significant. ACK - acknowledgment field significant. PSH - push function. RST - reset the connection. SYN - synchronize the sequence numbers to initiate a connection. FIN - no more data from sender. NETSTAT State Explanation ------------ SYN_SEND Indicates active open. SYN_RECEIVED Server just received SYN from the client. ESTABLISHED Client received server's SYN and session is established. LISTEN Server is ready to accept connection. FIN_WAIT_1 Indicates active close our side has shutdown, waiting to complete transmission of remaining buffered data TIMED_WAIT Client enters this state after active close. timeout to catch resent junk before entering closed, can only be entered from FIN_WAIT2 or CLOSING. Required because the other end may not have gotten our last ACK causing it to retransmit the data packet (which we ignore) CLOSE_WAIT Indicates passive close. Server just received first FIN from a client. remote side has shutdown and is waiting for us to finish writing our data and to shutdown (we have to close() to move on to LAST_ACK) FIN_WAIT_2 Client just received acknowledgment of its first FIN from the server all buffered data sent, waiting for remote to shutdown LAST_ACK Server is in this state when it sends its own FIN. our side has shutdown after remote has shutdown. There may still be data in our buffer that we have to finish sending CLOSED Server received ACK from client and connection is closed. */ #include "extensions.h" #include "debug.h" //includes winsock2.h or socket.h (communications) //inlcudes windows.h or pthread.h (threads) #include "define_platform.h" #include #include #include #include using namespace std; class DomainConnectionEventSink; class DomainConnection { //in other words an async SOCKET wrapper //created everytime that a DomainConnection cannot be found for a given domain. //class will automatically begin DNS resolving the domain at construction enum mode { waitingForRead, waitingForWrite, waitingForNothing } m_mode; static unsigned int m_count; //instance count (to check not exceeds FD_SETSIZE) bool m_DNSResolved; bool m_connecting; //avoids repeat connection attempts but allows the first one bool m_connected; //see m_connecting bool m_socketopen; //socket is open unsigned int m_consecutiveConnectionFails; //to report failures when connection continually drops unsigned int m_millisecondsBetweenRequests; //to learn about DOS responses and slow down static map m_instances; //reverse lookup from socket to DomainConnection (used for results of select) const char *m_domain; DomainConnectionEventSink *m_listener; //object to inform on events (may move this to a list of event sinks later) pthread_mutex_t m_DCOwn; //to queue listeners wanting to claim() this DC time_t m_lastEvent; static SOCKET m_max; //maximum socket number so far static fd_set m_rfds, m_wfds, m_xfds; //collections of raw sockets static pthread_mutex_t m_hEventLoop_mutex; //MUTEX to avoid event loop internals (e.g. for a report) static pthread_mutex_t m_hDNS_mutex; //MUTEX around gethostbyname() as it returns static data! static pthread_t m_eventLoopThread; //event loop thread handle, starts at first registered FD (FD_SETS are always populated after) static time_t m_lastSelectTime; //time of the last select, see below static unsigned int m_lastInterval; //gap between last 2 successive selects in seconds (too see if we are approaching server timeout) static bool m_eventLoopStarted; //if started /* typedef struct in_addr { union { struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { USHORT s_w1,s_w2; } S_un_w; ULONG S_addr; } S_un; #define s_addr S_un.S_addr // can be used for most tcp & ip code #define s_host S_un.S_un_b.s_b2 // host on imp #define s_net S_un.S_un_b.s_b1 // network #define s_imp S_un.S_un_w.s_w2 // imp #define s_impno S_un.S_un_b.s_b4 // imp # #define s_lh S_un.S_un_b.s_b3 // logical host } IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR; */ struct sockaddr_in m_sin; struct hostent *m_hp; SOCKET m_s; //this ones *current* socket char *m_readbuffer; //stored compiled buffer managed by this class const char *m_writebuffer; //write buffer stored to be sent asap size_t m_readbufsize, m_writebufsize; unsigned int m_keepAlive; //if !0 enables keep-alive mode at socket level unsigned int m_linger; //if !0 enables linger mode at socket level //when to enter into the select static pthread_cond_t m_hasReadWriteFDs_cond; static pthread_mutex_t m_hasReadWriteFDs_mutex; //event loop with select on FD_SETS //triggers a readyFor*() event on the listener via the readyFor*() control func below //update all the flags in the class from all the sockets using a select static const vector waitForEvent(const u_int timeout = DC_TIMEOUT); static void beginEventLoop(); static THREAD_CALLBACK_TYPE eventLoop(LPVOID lpParam); static void staticrunCleanup(LPVOID lpParam); //raw send and recieve functions (used by the DomainConnection::readyFor*() functions below) //register buffers for sending and receiving using the waitFor*(buf) functions below const int send(const char *sBufOut, const size_t sizeofbuf) const; //calculates the size with strlen if sizeofbuf=0 const int read(char sBufIn[], const size_t sizeofbuf) const; const bool closeSocket(); //close this end whatever //extra functions here because they need to check the result from the raw read/send funcs and trigger error events //on the listener if 0 or -1 (SOCKET_ERROR) are returned. These are only run by the event loop const size_t readyForReading(); const size_t readyForWriting(); const size_t readyForException(); const size_t readyForSomething(); static const char *WSAGetLastErrorText(); //simple text lookup for _DEBUG mode friend class Report_Full; public: //anything can create a DC and pass in an interface to the event sink DomainConnection(const char *_domain, DomainConnectionEventSink *_listener = 0, unsigned int _keepAlive = 0, unsigned int _linger = 0); //usually no listener at start ~DomainConnection(); //event sink control: not that claims and releases can overlap because of the overlapping destruction/construction times of Protocols void claim(DomainConnectionEventSink *_listener); void release(DomainConnectionEventSink *listener); //exceptions class DomainConnectionException {}; //abstract class ExceededFDSETMaxSize: public DomainConnectionException {}; //in constructor: bubbles up on Spider thread class NoWriteBuffer: public DomainConnectionException {}; //in waitForWrite() run from Protocol initially on Spider thread and then DC thread class NoReadBuffer: public DomainConnectionException {}; //in waitForRead() run from DC thread class CompleteSocketFailure: public DomainConnectionException {}; //DC eventLoop thread class InvalidSocket: public DomainConnectionException {}; //during connection attempts class DNSFailure: public DomainConnectionException {}; //initial DNS lookup on Spider thread class DCAlreadyClaimed: public DomainConnectionException {}; //When Protocol claims DC for listening on Spider thread class DCNotYours: public DomainConnectionException {}; //When Protocol releases DC for listening on Spider thread //copies of the raw socket flags completed by the static select call bool m_readyForReading; bool m_readyForWriting; bool m_threwException; bool m_somethingReady; //(m_readyForReading||m_readyForWriting||m_threwException||m_timedout) bool m_timedout; //when no activity has happened on the socket for timeout OR a select timeout (on all FDs) //accessors const unsigned int consecutiveConnectionFails() const {return m_consecutiveConnectionFails;} static const unsigned int lastInterval() {return m_lastInterval;} //diagnostics const bool FD_ISSET_IN(fd_set &set) const {return (FD_ISSET(m_s, &set) != 0);} const bool FD_ISSET_rdfs() const {return FD_ISSET_IN(m_rfds);} const bool FD_ISSET_wdfs() const {return FD_ISSET_IN(m_wfds);} const bool FD_ISSET_xdfs() const {return FD_ISSET_IN(m_xfds);} //connection management (asynchronous): m_connecting, m_connected limit successive calls to connection const int DNSLookup(); //synchronous and throws exception if fails const bool closeDisconnectedSocket(); //clears all FD_SETS and status const bool closeConnectedSocket(); //disconnects even if the other end doesn't know. clears all FD_SETS and status const bool closeConnectionWithZeroRead(); //force the other end to close with a zero buffer space read const int connect(); //does not enable waitForConnect(), the listener must do this when ready static void endEventLoop(); //cancels and joins thread //state control waitFor*() sets/clears the FD_SETS and also registers the buffers to use when ready (read/write) to send to the private raw functions //write buffers will be free'd after sending: they must be malloc'd void waitForConnect(const char *_buffer = 0, const size_t _bufsize = 0) {waitForWrite(_buffer, _bufsize);} void waitForRead( char *_buffer, const size_t _bufsize); //the DomainConnection does the reading for you (0 byte reads=connection close) void waitForWrite( const char *_buffer = 0, const size_t _bufsize = 0); //the DomainConnection does the writing for you void waitForNothing(); void ignoreReadData(); //read data from the SOCKET and discard it }; class DomainConnectionEventSink { public: //select response *real events* virtual void readyForReading() {} virtual void readyForWriting() {} virtual void threwException() {} //select response real events when not waiting for them virtual void outOfSyncRead() {} virtual void outOfSyncWrite() {} //select response calculated events virtual void somethingReady() {} // *real event* triggered (does not include timeouts) virtual void timedoutOnRead() {} //select timeout or calculated inactivity waiting for read virtual void timedoutOnWrite() {} //select timeout or calculated inactivity wating for write virtual void timedoutForNowt() {} //select timeout or calculated inactivity, but not waiting for anything //other events virtual void connectionClosedOnRead(const size_t bufspace) {} virtual void connectionClosedOnWrite() {} virtual void finishedRead(const int bytes) {} virtual void finishedWrite(const int bytes) {} virtual bool outOfBufferSpace() {return true;} virtual void completeFailure() {} //select returns general SOCKET_ERROR virtual void customException(DomainConnection::DomainConnectionException &e) {DEBUGERROR0("EventLoop Exception");throw e;} }; #endif