diff --git a/relay/Makefile b/relay/Makefile index 7b4684198..25bb11314 100644 --- a/relay/Makefile +++ b/relay/Makefile @@ -24,6 +24,7 @@ SRC = xwrelay.cpp \ timermgr.cpp \ configs.cpp \ crefmgr.cpp \ + permid.cpp \ OBJ = $(patsubst %.cpp,%.o,$(SRC)) LDFLAGS += -lpthread -g diff --git a/relay/cref.cpp b/relay/cref.cpp index 6b98a68d4..cd4a40843 100644 --- a/relay/cref.cpp +++ b/relay/cref.cpp @@ -35,9 +35,6 @@ using namespace std; -pthread_mutex_t g_IdsMutex = PTHREAD_MUTEX_INITIALIZER; -CookieID CookieRef::ms_nextConnectionID = 1000; - /***************************************************************************** * SocketsIterator class *****************************************************************************/ @@ -71,21 +68,17 @@ SocketsIterator::Next() * CookieRef class *****************************************************************************/ -CookieRef::CookieRef( const char* s, CookieID id ) +CookieRef::CookieRef( const char* cookie, const char* connName, CookieID id ) : m_heatbeat(RelayConfigs::GetConfigs()->GetHeartbeatInterval()) - , m_name(s) + , m_name(cookie==NULL?"":cookie) + , m_connName(connName) + , m_cookieID(id) , m_totalSent(0) , m_curState(XW_ST_INITED) + , m_nextState(XW_ST_INITED) + , m_eventQueue() + , m_nextHostID(HOST_ID_SERVER) { -/* pthread_rwlock_init( &m_sockets_rwlock, NULL ); */ -/* pthread_mutex_init( &m_EventsMutex, NULL ); */ - - if ( id == 0 ) { - MutexLock ml( &g_IdsMutex ); - m_connectionID = ms_nextConnectionID++; /* needs a mutex!!! */ - } else { - m_connectionID = id; - } } CookieRef::~CookieRef() @@ -96,7 +89,6 @@ CookieRef::~CookieRef() XWThreadPool* tPool = XWThreadPool::GetTPool(); for ( ; ; ) { -/* RWWriteLock rwl( &m_sockets_rwlock ); */ map::iterator iter = m_sockets.begin(); if ( iter == m_sockets.end() ) { @@ -108,18 +100,18 @@ CookieRef::~CookieRef() m_sockets.erase( iter ); } -/* pthread_rwlock_destroy( &m_sockets_rwlock ); */ - logf( "CookieRef for %d being deleted", m_connectionID ); - -/* pthread_mutex_destroy( &m_EventsMutex ); */ -/* pthread_rwlock_destroy( &m_sockets_rwlock ); */ + logf( "CookieRef for %d being deleted; sent %d bytes", + m_cookieID, m_totalSent ); } /* ~CookieRef */ void CookieRef::_Connect( int socket, HostID srcID ) { CRefMgr::Get()->Associate( socket, this ); -/* MutexLock ml( &m_EventsMutex ); */ + if ( srcID == HOST_ID_NONE ) { + srcID = nextHostID(); + logf( "assigned host id: %x", srcID ); + } pushConnectEvent( socket, srcID ); handleEvents(); } @@ -176,6 +168,14 @@ CookieRef::NeverFullyConnected() && m_curState != XW_ST_MISSING; } +int +CookieRef::AcceptingConnections() +{ + return m_curState == XW_ST_INITED + || m_curState == XW_ST_CONNECTING + || m_curState == XW_ST_MISSING; +} + void CookieRef::notifyDisconn( const CRefEvent* evt ) { @@ -400,19 +400,21 @@ CookieRef::handleEvents() if ( getFromTable( m_curState, evt.type, &takeAction, &m_nextState ) ) { logf( "cid %d: moving from state %s to state %s for event %s", - m_connectionID, stateString(m_curState), + m_cookieID, stateString(m_curState), stateString(m_nextState), eventString(evt.type) ); switch( takeAction ) { case XW_ACT_SEND_1ST_RSP: + case XW_ACT_SEND_1ST_RERSP: setAllConnectedTimer(); - sendResponse( &evt ); + sendResponse( &evt, takeAction == XW_ACT_SEND_1ST_RSP ); break; - case XW_ACT_SENDRSP: + case XW_ACT_SEND_RSP: + case XW_ACT_SEND_RERSP: notifyOthers( evt.u.con.socket, XWRELAY_OTHERCONNECT, XWRELAY_ERROR_NONE ); - sendResponse( &evt ); + sendResponse( &evt, takeAction == XW_ACT_SEND_RSP ); break; case XW_ACT_FWD: @@ -493,15 +495,6 @@ putNetShort( unsigned char** bufpp, unsigned short s ) *bufpp += sizeof(s); } -static void -putNetLong( unsigned char** bufpp, unsigned long s ) -{ - s = htonl( s ); - memcpy( *bufpp, &s, sizeof(s) ); - *bufpp += sizeof(s); - assert( sizeof(s) == 4 ); /* otherwise need to hardcode */ -} - void CookieRef::setAllConnectedTimer() { @@ -518,26 +511,39 @@ CookieRef::cancelAllConnectedTimer() } void -CookieRef::sendResponse( const CRefEvent* evt ) +CookieRef::sendResponse( const CRefEvent* evt, int initial ) { int socket = evt->u.con.socket; HostID id = evt->u.con.srcID; assert( id != HOST_ID_NONE ); logf( "remembering pair: hostid=%x, socket=%d", id, socket ); -/* RWWriteLock ml( &m_sockets_rwlock ); */ HostRec hr(socket); m_sockets.insert( pair(id,hr) ); /* Now send the response */ - unsigned char buf[7]; + unsigned char buf[1 + /* cmd */ + sizeof(short) + /* heartbeat */ + sizeof(CookieID) + + 1 + /* hostID */ + MAX_CONNNAME_LEN+1]; unsigned char* bufp = buf; - *bufp++ = XWRELAY_CONNECTRESP; + *bufp++ = initial ? XWRELAY_CONNECT_RESP : XWRELAY_RECONNECT_RESP; putNetShort( &bufp, GetHeartbeat() ); - putNetLong( &bufp, GetCookieID() ); + putNetShort( &bufp, GetCookieID() ); + logf( "writing hostID of %d into mgs", id ); + *bufp++ = (char)id; + if ( initial ) { + const char* connName = ConnName(); + int len = strlen( connName ); + assert( len < MAX_CONNNAME_LEN ); + *bufp++ = (char)len; + memcpy( bufp, connName, len ); + bufp += len; + } - send_with_length( socket, buf, sizeof(buf) ); + send_with_length( socket, buf, bufp - buf ); logf( "sent XWRELAY_CONNECTRESP" ); } /* sendResponse */ @@ -655,7 +661,7 @@ CookieRef::noteHeartbeat( const CRefEvent* evt ) map::iterator iter = m_sockets.find(id); if ( iter == m_sockets.end() ) { - logf( "no socket for HostID %d", id ); + logf( "no socket for HostID %x", id ); } else { /* PENDING If the message came on an unexpected socket, kill the @@ -675,9 +681,7 @@ CookieRef::s_checkAllConnected( void* closure ) /* Need to ensure */ CookieRef* self = (CookieRef*)closure; SafeCref scr(self); - if ( scr.IsValid() ) { - scr.CheckAllConnected(); - } + scr.CheckAllConnected(); } void @@ -698,9 +702,12 @@ CookieRef::_PrintCookieInfo( string& out ) out += Name(); out += "\n"; out += "ID: "; - char buf[64]; + char buf[MAX_CONNNAME_LEN+MAX_COOKIE_LEN]; - snprintf( buf, sizeof(buf), "%d\n", GetCookieID() ); + snprintf( buf, sizeof(buf), "%s\n", Name() ); + out += buf; + + snprintf( buf, sizeof(buf), "%s\n", ConnName() ); out += buf; snprintf( buf, sizeof(buf), "Bytes sent: %d\n", m_totalSent ); diff --git a/relay/cref.h b/relay/cref.h index 460f60873..c582e4a84 100644 --- a/relay/cref.h +++ b/relay/cref.h @@ -52,21 +52,24 @@ class CookieRef { friend class SafeCref; friend class CookieMapIterator; - CookieRef( const char* s, CookieID id ); + CookieRef( const char* cookie, const char* connName, CookieID id ); ~CookieRef(); /* Within this cookie, remember that this hostID and socket go together. If the hostID is HOST_ID_SERVER, it's the server. */ - CookieID GetCookieID() { return m_connectionID; } + CookieID GetCookieID() { return m_cookieID; } + int HostKnown( HostID host ) { return -1 != SocketForHost( host ); } int CountSockets() { return m_sockets.size(); } int HasSocket( int socket ); - string Name() { return m_name; } + const char* Name() { return m_name.c_str(); } + const char* ConnName() { return m_connName.c_str(); } short GetHeartbeat() { return m_heatbeat; } int SocketForHost( HostID dest ); int NeverFullyConnected(); + int AcceptingConnections(); /* for console */ void _PrintCookieInfo( string& out ); @@ -149,7 +152,7 @@ class CookieRef { void handleEvents(); - void sendResponse( const CRefEvent* evt ); + void sendResponse( const CRefEvent* evt, int initial ); void setAllConnectedTimer(); void cancelAllConnectedTimer(); @@ -163,16 +166,17 @@ class CookieRef { void notifyDisconn(const CRefEvent* evt); void removeSocket( int socket ); - + HostID nextHostID() { return ++m_nextHostID; } /* timer callback */ static void s_checkAllConnected( void* closure ); map m_sockets; /* pthread_rwlock_t m_sockets_rwlock; */ - CookieID m_connectionID; short m_heatbeat; /* might change per carrier or something. */ - string m_name; + string m_name; /* cookie used for initial connections */ + string m_connName; /* globally unique name */ + CookieID m_cookieID; /* Unique among current games on this server */ int m_totalSent; /* Guard the event queue. Only one thread at a time can post to the @@ -185,7 +189,7 @@ class CookieRef { XW_RELAY_STATE m_nextState; deque m_eventQueue; - static CookieID ms_nextConnectionID; + HostID m_nextHostID; }; /* CookieRef */ #endif diff --git a/relay/crefmgr.cpp b/relay/crefmgr.cpp index f59945046..241b89a50 100644 --- a/relay/crefmgr.cpp +++ b/relay/crefmgr.cpp @@ -24,6 +24,7 @@ #include "crefmgr.h" #include "cref.h" #include "mlock.h" +#include "permid.h" class SocketStuff { public: @@ -48,6 +49,7 @@ CRefMgr::Get() } /* Get */ CRefMgr::CRefMgr() + : m_nextCID(0) { /* should be using pthread_once() here */ pthread_mutex_init( &m_guard, NULL ); @@ -59,21 +61,93 @@ CRefMgr::~CRefMgr() } CookieRef* -CRefMgr::getMakeCookieRef_locked( const char* cookie, CookieID connID ) +CRefMgr::FindOpenGameFor( const char* cORn, int isCookie ) { + CookieRef* cref = NULL; + RWReadLock rwl( &m_cookieMapRWLock ); + + CookieMap::iterator iter = m_cookieMap.begin(); + while ( iter != m_cookieMap.end() ) { + cref = iter->second; + if ( isCookie ) { + if ( cref->NeverFullyConnected() ) { + if ( 0 == strcmp( cref->Name(), cORn ) ) { + break; + } + } + } else { + if ( cref->AcceptingConnections() ) { + if ( 0 == strcmp( cref->ConnName(), cORn ) ) { + break; + } + } + } + ++iter; + } + + return (iter == m_cookieMap.end()) ? NULL : cref; +} /* FindOpenGameFor */ + +CookieID +CRefMgr::nextCID( const char* connName ) +{ + /* Later may want to guarantee that wrap-around doesn't cause an overlap. + But that's really only a theoretical possibility. */ + return ++m_nextCID; +} /* nextCID */ + +CookieID +CRefMgr::cookieIDForConnName( const char* connName ) +{ + CookieID cid = 0; + /* for now, just walk the existing data structure and see if the thing's + in use. If it isn't, return a new id. */ + + RWReadLock rwl( &m_cookieMapRWLock ); + + CookieMap::iterator iter = m_cookieMap.begin(); + while ( iter != m_cookieMap.end() ) { + CookieRef* cref = iter->second; + if ( 0 == strcmp( cref->ConnName(), connName ) ) { + cid = iter->first; + break; + } + ++iter; + } + + return cid; +} /* cookieIDForConnName */ + +CookieRef* +CRefMgr::getMakeCookieRef_locked( const char* cORn, int isCookie, HostID hid ) +{ + CookieRef* cref; + pthread_mutex_lock( &m_guard ); - CookieRef* cref = connID == 0 ? NULL: getCookieRef_impl( connID ); + /* We have a cookie from a new connection. This may be the first time + it's been seen, or there may be a game currently in the + XW_ST_CONNECTING state. So we need to look up the cookie first, then + generate new connName and cookieIDs if it's not found. */ - if ( cref == NULL ) { /* need to keep looking? */ - - CookieID newId = CookieIdForName( cookie ); - - if ( newId == 0 ) { /* not in the system */ - cref = AddNew( cookie, connID ); + cref = FindOpenGameFor( cORn, isCookie ); + if ( cref == NULL ) { + const char* connName; + const char* cookie = NULL; + if ( isCookie ) { + cookie = cORn; + string s = PermID::GetNextUniqueID(); + connName = s.c_str(); } else { - cref = getCookieRef_impl( newId ); + connName = cORn; } + + CookieID cid = cookieIDForConnName( connName ); + if ( cid == 0 ) { + cid = nextCID( connName ); + } + + cref = AddNew( cookie, connName, cid ); } if ( cref == NULL ) { @@ -83,25 +157,6 @@ CRefMgr::getMakeCookieRef_locked( const char* cookie, CookieID connID ) return cref; } /* getMakeCookieRef_locked */ -CookieID -CRefMgr::CookieIdForName( const char* name ) -{ - CookieRef* cref = NULL; - string s(name); - - RWReadLock rwl( &m_cookieMapRWLock ); - - CookieMap::iterator iter = m_cookieMap.begin(); - while ( iter != m_cookieMap.end() ) { - cref = iter->second; - if ( cref->Name() == s && cref->NeverFullyConnected() ) { - return cref->GetCookieID(); - } - ++iter; - } - return 0; -} /* CookieIdForName */ - void CRefMgr::Associate( int socket, CookieRef* cref ) { @@ -147,22 +202,21 @@ void CRefMgr::RemoveSocketRefs( int socket ) { SafeCref scr( socket ); - if ( scr.IsValid() ) { - scr.Remove( socket ); - } + scr.Remove( socket ); } void CRefMgr::PrintSocketInfo( int socket, string& out ) { SafeCref scr( socket ); - if ( scr.IsValid() ) { + const char* name = scr.Name(); + if ( name != NULL && name[0] != '\0' ) { char buf[64]; snprintf( buf, sizeof(buf), "* socket: %d\n", socket ); out += buf; - snprintf( buf, sizeof(buf), " in cookie: %s\n", scr.Name().c_str() ); + snprintf( buf, sizeof(buf), " in cookie: %s\n", name ); out += buf; } } @@ -256,13 +310,14 @@ CRefMgr::UnlockCref( CookieRef* cref ) } CookieRef* -CRefMgr::AddNew( const char* s, CookieID id ) +CRefMgr::AddNew( const char* cookie, const char* connName, CookieID id ) { RWWriteLock rwl( &m_cookieMapRWLock ); - logf( "making new cref" ); - CookieRef* ref = new CookieRef( s, id ); + logf( "making new cref: %d", id ); + CookieRef* ref = new CookieRef( cookie, connName, id ); m_cookieMap.insert( pair(ref->GetCookieID(), ref ) ); - logf( "paired cookie %s with id %d", s, ref->GetCookieID() ); + logf( "paired cookie %s/connName %s with id %d", + (cookie?cookie:"NULL"), connName, ref->GetCookieID() ); return ref; } /* AddNew */ @@ -304,9 +359,9 @@ CRefMgr::Delete( CookieID id ) } /* Delete */ void -CRefMgr::Delete( const char* name ) +CRefMgr::Delete( const char* connName ) { - CookieID id = CookieIdForName( name ); + CookieID id = cookieIDForConnName( connName ); Delete( id ); } /* Delete */ @@ -345,9 +400,7 @@ CRefMgr::CheckHeartbeats( time_t now ) unsigned int i; for ( i = 0; i < crefs.size(); ++i ) { SafeCref scr( crefs[i] ); - if ( scr.IsValid() ) { - scr.CheckHeartbeats( now ); - } + scr.CheckHeartbeats( now ); } } /* CheckHeartbeats */ @@ -380,11 +433,13 @@ CookieMapIterator::Next() // SafeCref ////////////////////////////////////////////////////////////////////////////// -SafeCref::SafeCref( const char* cookie, CookieID connID ) +SafeCref::SafeCref( const char* cORn, int isCookie, HostID hid ) : m_cref( NULL ) , m_mgr( CRefMgr::Get() ) { - CookieRef* cref = m_mgr->getMakeCookieRef_locked( cookie, connID ); + CookieRef* cref; + + cref = m_mgr->getMakeCookieRef_locked( cORn, isCookie, hid ); if ( cref != NULL ) { if ( m_mgr->LockCref( cref ) ) { m_cref = cref; diff --git a/relay/crefmgr.h b/relay/crefmgr.h index 13e8704ad..f2af7979a 100644 --- a/relay/crefmgr.h +++ b/relay/crefmgr.h @@ -60,7 +60,7 @@ class CRefMgr { void CheckHeartbeats( time_t now ); void Delete( CookieID id ); void Delete( CookieRef* cref ); - void Delete( const char* name ); + void Delete( const char* connName ); CookieID CookieIdForName( const char* name ); /* For use from ctrl only!!!! */ @@ -77,12 +77,18 @@ class CRefMgr { private: friend class SafeCref; - CookieRef* getMakeCookieRef_locked( const char* cookie, CookieID cid ); + CookieRef* getMakeCookieRef_locked( const char* cORn, int isCookie, HostID hid ); CookieRef* getCookieRef_locked( CookieID cookieID ); CookieRef* getCookieRef_locked( int socket ); int checkCookieRef_locked( CookieRef* cref ); CookieRef* getCookieRef_impl( CookieID cookieID ); - CookieRef* AddNew( const char* s, CookieID id ); + CookieRef* AddNew( const char* cookie, const char* connName, CookieID id ); + CookieRef* FindOpenGameFor( const char* cORn, int isCookie ); + + CookieID cookieIDForConnName( const char* connName ); + CookieID nextCID( const char* connName ); + + CookieID m_nextCID; int LockCref( CookieRef* cref ); void UnlockCref( CookieRef* cref ); @@ -106,45 +112,81 @@ class SafeCref { CookieRef instance at a time. */ public: - SafeCref( const char* cookie, CookieID cid ); + SafeCref( const char* cookieOrConnName, int cookie, HostID hid ); SafeCref( CookieID cid ); SafeCref( int socket ); SafeCref( CookieRef* cref ); ~SafeCref(); - int IsValid() { return m_cref != NULL; } - - void Forward( HostID src, HostID dest, unsigned char* buf, int buflen ) { - m_cref->_Forward( src, dest, buf, buflen ); + int Forward( HostID src, HostID dest, unsigned char* buf, int buflen ) { + if ( IsValid() ) { + m_cref->_Forward( src, dest, buf, buflen ); + return 1; + } else { + return 0; + } } - void Connect( int socket, HostID srcID ) { - m_cref->_Connect( socket, srcID ); + int Connect( int socket, HostID srcID ) { + if ( IsValid() ) { + m_cref->_Connect( socket, srcID ); + return 1; + } else { + return 0; + } } - void Reconnect( int socket, HostID srcID ) { - m_cref->_Reconnect( socket, srcID ); + int Reconnect( int socket, HostID srcID ) { + if ( IsValid() ) { + m_cref->_Reconnect( socket, srcID ); + return 1; + } else { + return 0; + } } void Disconnect(int socket, HostID hostID ) { - m_cref->_Disconnect( socket, hostID ); + if ( IsValid() ) { + m_cref->_Disconnect( socket, hostID ); + } } void Remove( int socket ) { - m_cref->_Remove( socket ); + if ( IsValid() ) { + m_cref->_Remove( socket ); + } } - void HandleHeartbeat( HostID id, int socket ) { - m_cref->_HandleHeartbeat( id, socket ); + int HandleHeartbeat( HostID id, int socket ) { + if ( IsValid() ) { + m_cref->_HandleHeartbeat( id, socket ); + return 1; + } else { + return 0; + } } void CheckHeartbeats( time_t now ) { - m_cref->_CheckHeartbeats( now ); + if ( IsValid() ) { + m_cref->_CheckHeartbeats( now ); + } } void PrintCookieInfo( string& out ) { - m_cref->_PrintCookieInfo( out ); + if ( IsValid() ) { + m_cref->_PrintCookieInfo( out ); + } } void CheckAllConnected() { - m_cref->_CheckAllConnected(); + if ( IsValid() ) { + m_cref->_CheckAllConnected(); + } + } + const char* Name() { + if ( IsValid() ) { + return m_cref->Name(); + } else { + return ""; /* so don't crash.... */ + } } - string Name() { return m_cref->Name(); } private: + int IsValid() { return m_cref != NULL; } + CookieRef* m_cref; CRefMgr* m_mgr; }; diff --git a/relay/ctrl.cpp b/relay/ctrl.cpp index e2ae3eb1d..fab99d797 100644 --- a/relay/ctrl.cpp +++ b/relay/ctrl.cpp @@ -123,12 +123,10 @@ print_cookies( int socket, CookieID theID ) for ( id = iter.Next(); id != 0; id = iter.Next() ) { if ( theID == 0 || theID == id ) { SafeCref scr( id ); - if ( scr.IsValid() ) { - string s; - scr.PrintCookieInfo( s ); + string s; + scr.PrintCookieInfo( s ); - print_to_sock( socket, s.c_str() ); - } + print_to_sock( socket, s.c_str() ); } } } @@ -173,7 +171,7 @@ cmd_kill( int socket, const char** args ) if ( !found ) { char* msg = "%s socket \n" - "%s cookie name \n" + "%s cookie name \n" "%s cookie id \n" ; print_to_sock( socket, msg, args[0], args[0], args[0] ); @@ -207,7 +205,7 @@ print_cookies( int socket, const char* name ) for ( id = iter.Next(); id != 0; id = iter.Next() ) { SafeCref scr( id ); - if ( scr.IsValid() && scr.Name() == name ) { + if ( scr.Name() == name ) { string s; scr.PrintCookieInfo( s ); diff --git a/relay/permid.cpp b/relay/permid.cpp index 41005e04a..8225efd25 100644 --- a/relay/permid.cpp +++ b/relay/permid.cpp @@ -26,11 +26,15 @@ using namespace std; pthread_mutex_t PermID::s_guard = PTHREAD_MUTEX_INITIALIZER; +string PermID::s_serverName; string PermID::GetNextUniqueID() { MutexLock ml( &s_guard ); + + string s = s_serverName; + s += ":"; char buf[32]; /* should last for a while :-) */ @@ -40,6 +44,7 @@ PermID::GetNextUniqueID() rewind( f ); } else { f = fopen( "/etc/xwrelay/xwrelay_id.txt", "w" ); + assert ( f != NULL ); buf[0] = '0'; buf[1] = '\0'; } @@ -50,8 +55,13 @@ PermID::GetNextUniqueID() fprintf( f, "%s\n", buf ); fclose( f ); - string s(buf); - logf( "new id will be %s", s.c_str() ); + s += buf; return s; } + +/* static */ void +PermID::SetServerName( const char* name ) +{ + s_serverName = name; +} diff --git a/relay/permid.h b/relay/permid.h index e78983499..a7c2b5e1c 100644 --- a/relay/permid.h +++ b/relay/permid.h @@ -32,9 +32,15 @@ */ class PermID { public: - static std::string GetNextUniqueID(); + static void SetServerName( const char* name ); + static std::string GetNextUniqueID(); private: - static pthread_mutex_t s_guard; /* guard access to the whole process */ + static pthread_mutex_t s_guard; /* guard access to the whole + process */ + static std::string s_serverName; /* All ID's generated start with + this, which is supposed to be + unique to this relay + instance. */ }; #endif diff --git a/relay/states.cpp b/relay/states.cpp index 43eb92ad7..ba06e5c9c 100644 --- a/relay/states.cpp +++ b/relay/states.cpp @@ -40,31 +40,31 @@ typedef struct StateTable { window where new devices can sign in using a given cookie is fairly small. Perhaps a better algorithm is needed for determining when the game is - closed. It's not when the first XW_EVT_FORWARDMSG comes along, since - that can happen before all hosts have arrived (e.g. if a client signs up - before the server.) The goal has been to avoid having the relay know about - the xwords protocol. But it already knows about hostID 0 (I think). So - maybe when that host sends a message for forwarding we know we're set? Or - maybe we add a message the server can send saying "All are present now; - lock the game!" It'd suck if that were spoofed though. + closed. It's not when the first XW_EVT_FORWARDMSG comes along, since that + can happen before all hosts have arrived (e.g. if a client signs up before + the server.) The goal has been to avoid having the relay know about the + xwords protocol. But it already knows about hostID 1. So maybe when that + host sends a message for forwarding we know we're set? Yes: the server + sends only after all clients have reported in, at least now. The problem we want to avoid is bogus connections. Two are playing using cookie FOO. Somebody looks over a shoulder and sees FOO, and tries to connect. The relay must not start forwarding packets from the interloper into the game associated with FOO. - - */ StateTable g_stateTable[] = { /* Initial msg comes in. Managing object created in init state, sends response */ { XW_ST_INITED, XW_EVT_CONNECTMSG, XW_ACT_SEND_1ST_RSP, XW_ST_CONNECTING }, - { XW_ST_INITED, XW_EVT_RECONNECTMSG, XW_ACT_SENDRSP, XW_ST_CONNECTING }, + { XW_ST_INITED, XW_EVT_RECONNECTMSG, XW_ACT_SEND_1ST_RERSP,XW_ST_CONNECTING }, /* Another connect msg comes in */ - { XW_ST_CONNECTING, XW_EVT_CONNECTMSG, XW_ACT_SENDRSP, XW_ST_CONNECTING }, - { XW_ST_CONNECTING, XW_EVT_RECONNECTMSG, XW_ACT_SENDRSP, XW_ST_CONNECTING }, + { XW_ST_CONNECTING, XW_EVT_CONNECTMSG, XW_ACT_SEND_RSP, XW_ST_CONNECTING }, + { XW_ST_CONNECTING, XW_EVT_RECONNECTMSG, XW_ACT_SEND_RERSP, XW_ST_CONNECTING }, + + { XW_ST_MISSING, XW_EVT_CONNECTMSG, XW_ACT_SEND_RSP, XW_ST_CONNECTING }, + { XW_ST_MISSING, XW_EVT_RECONNECTMSG, XW_ACT_SEND_RERSP, XW_ST_CONNECTING }, /* Disconnect. */ { XW_ST_ALLCONNECTED, XW_EVT_DISCONNECTMSG, XW_ACT_DISCONNECT, XW_ST_MISSING }, diff --git a/relay/states.h b/relay/states.h index 62fefa907..911e6ce43 100644 --- a/relay/states.h +++ b/relay/states.h @@ -105,8 +105,10 @@ typedef enum { XW_ACT_NONE ,XW_ACT_SEND_1ST_RSP + ,XW_ACT_SEND_1ST_RERSP - ,XW_ACT_SENDRSP /* Send a connection response */ + ,XW_ACT_SEND_RSP /* Send a connection response */ + ,XW_ACT_SEND_RERSP ,XW_ACT_FWD /* Forward a message */ diff --git a/relay/xwrelay.conf b/relay/xwrelay.conf index 50cd03955..fe59452f0 100644 --- a/relay/xwrelay.conf +++ b/relay/xwrelay.conf @@ -4,12 +4,12 @@ # Format: var=value. No spaces between var and value at this point. # Heartbeat timer. Sent to clients. -HEARTBEAT=60 +HEARTBEAT=6000 # How long after the first connection on a cookie until we need to # have heard from all players in the game? After this long passes we # kill the connection after notifying all that have connected. -ALLCONN=120 +ALLCONN=12000 # How many worker threads in the thread pool? Default is five. NTHREADS=5 @@ -20,3 +20,6 @@ PORT=10999 # And the control port is? CTLPORT=11000 +# Need a unique name for every instance of the relay so they can +# create game ids guaranteed to be unique +#SERVERNAME=eehouse.org diff --git a/relay/xwrelay.cpp b/relay/xwrelay.cpp index c02c6e0cf..1fcc814aa 100644 --- a/relay/xwrelay.cpp +++ b/relay/xwrelay.cpp @@ -57,6 +57,7 @@ #include "tpool.h" #include "configs.h" #include "timermgr.h" +#include "permid.h" #define N_WORKER_THREADS 5 #define MILLIS 1000 @@ -83,54 +84,63 @@ logf( const char* format, ... ) fprintf( where, "\n" ); } /* logf */ -static unsigned short -getNetShort( unsigned char** bufpp ) +static int +getNetShort( unsigned char** bufpp, unsigned char* end, unsigned short* out ) { - unsigned short tmp; - memcpy( &tmp, *bufpp, 2 ); - *bufpp += 2; - return ntohs( tmp ); + int ok = *bufpp + 2 <= end; + if ( ok ) { + unsigned short tmp; + memcpy( &tmp, *bufpp, 2 ); + *bufpp += 2; + *out = ntohs( tmp ); + } + return ok; } /* getNetShort */ -static unsigned short -getNetLong( unsigned char** bufpp ) +static int +getNetByte( unsigned char** bufpp, unsigned char* end, unsigned char* out ) { - unsigned long tmp; - memcpy( &tmp, *bufpp, sizeof(tmp) ); - *bufpp += sizeof(tmp); - assert( sizeof(tmp) == 4 ); - return ntohl( tmp ); -} /* getNetLong */ + int ok = *bufpp < end; + if ( ok ) { + *out = **bufpp; + ++*bufpp; + } + return ok; +} /* getNetByte */ static void processHeartbeat( unsigned char* buf, int bufLen, int socket ) { - CookieID cookieID = getNetLong( &buf ); - HostID hostID = getNetShort( &buf ); - logf( "processHeartbeat: cookieID 0x%lx, hostID 0x%x", cookieID, hostID ); + unsigned char* end = buf + bufLen; + CookieID cookieID; + HostID hostID; - SafeCref scr( cookieID ); - if ( scr.IsValid() ) { - scr.HandleHeartbeat( hostID, socket ); - } else { - killSocket( socket, "no cref for socket" ); + if ( getNetShort( &buf, end, &cookieID ) + && getNetByte( &buf, end, &hostID ) ) { + logf( "processHeartbeat: cookieID 0x%lx, hostID 0x%x", + cookieID, hostID ); + + SafeCref scr( cookieID ); + if ( !scr.HandleHeartbeat( hostID, socket ) ) { + killSocket( socket, "no cref for socket" ); + } } } /* processHeartbeat */ static int -readCookie( unsigned char** bufp, const unsigned char* end, - char* outBuf ) +readStr( unsigned char** bufp, const unsigned char* end, + char* outBuf, int bufLen ) { unsigned char clen = **bufp; ++*bufp; - if ( *bufp < end && clen > 0 && clen < MAX_COOKIE_LEN ) { + if ( ((*bufp + clen) <= end) && (clen < bufLen) ) { memcpy( outBuf, *bufp, clen ); outBuf[clen] = '\0'; *bufp += clen; return 1; } return 0; -} /* readCookie */ +} /* readStr */ static int flagsOK( unsigned char flags ) @@ -180,10 +190,11 @@ send_with_length_unsafe( int socket, unsigned char* buf, int bufLen ) * game? */ static void -processConnect( unsigned char* bufp, int bufLen, int socket, int recon ) +processConnect( unsigned char* bufp, int bufLen, int socket ) { char cookie[MAX_COOKIE_LEN+1]; unsigned char* end = bufp + bufLen; + int success = 0; logf( "processConnect" ); @@ -191,45 +202,63 @@ processConnect( unsigned char* bufp, int bufLen, int socket, int recon ) unsigned char flags = *bufp++; if ( flagsOK( flags ) ) { - if ( recon || readCookie( &bufp, end, cookie ) ) { + HostID srcID; + if ( readStr( &bufp, end, cookie, sizeof(cookie) ) + && getNetByte( &bufp, end, &srcID ) ) { - HostID srcID; - CookieID cookieID; - - if ( bufp + sizeof(srcID) + sizeof(cookieID) == end ) { - srcID = getNetShort( &bufp ); - cookieID = getNetLong( &bufp ); - - SafeCref scr( cookie, cookieID ); - if ( scr.IsValid() ) { - if ( recon ) { - scr.Reconnect( socket, srcID ); - } else { - scr.Connect( socket, srcID ); - } - } - } + SafeCref scr( cookie, 1, srcID ); + success = scr.Connect( socket, srcID ); } - } else { + } + + if ( !success ) { denyConnection( socket ); } } /* processConnect */ +static void +processReconnect( unsigned char* bufp, int bufLen, int socket ) +{ + char connName[MAX_CONNNAME_LEN+1]; + unsigned char* end = bufp + bufLen; + int success = 0; + + logf( "processReconnect" ); + + connName[0] = '\0'; + + unsigned char flags = *bufp++; + if ( flagsOK( flags ) ) { + HostID srcID; + if ( getNetByte( &bufp, end, &srcID ) + && readStr( &bufp, end, connName, sizeof(connName) ) ) { + + SafeCref scr( connName, 0, srcID ); + success = scr.Connect( socket, srcID ); + } + } + + if ( !success ) { + denyConnection( socket ); + } +} + static void processDisconnect( unsigned char* bufp, int bufLen, int socket ) { - if ( bufLen == 6 ) { - CookieID cookieID = getNetLong( &bufp ); - HostID hostID = getNetShort( &bufp ); + unsigned char* end = bufp + bufLen; + CookieID cookieID; + HostID hostID; + + if ( getNetShort( &bufp, end, &cookieID ) + && getNetByte( &bufp, end, &hostID ) ) { SafeCref scr( cookieID ); - if ( scr.IsValid() ) { - scr.Disconnect( socket, hostID ); - } + scr.Disconnect( socket, hostID ); } else { logf( "dropping XWRELAY_GAME_DISCONNECT; wrong length" ); } -} +} /* processDisconnect */ void killSocket( int socket, char* why ) @@ -255,17 +284,18 @@ forwardMessage( unsigned char* buf, int buflen, int srcSocket ) { int success = 0; unsigned char* bufp = buf + 1; /* skip cmd */ - CookieID cookieID = getNetLong( &bufp ); - logf( "cookieID = %d", cookieID ); + unsigned char* end = buf + buflen; + CookieID cookieID; + HostID src; + HostID dest; + if ( getNetShort( &bufp, end, &cookieID ) + && getNetByte( &bufp, end, &src ) + && getNetByte( &bufp, end, &dest ) ) { + logf( "cookieID = %d", cookieID ); - SafeCref scr( cookieID ); - if ( scr.IsValid() ) { - HostID src = getNetShort( &bufp ); - HostID dest = getNetShort( &bufp ); - - scr.Forward( src, dest, buf, buflen ); - success = 1; + SafeCref scr( cookieID ); + success = scr.Forward( src, dest, buf, buflen ); } return success; } /* forwardMessage */ @@ -277,21 +307,15 @@ processMessage( unsigned char* buf, int bufLen, int socket ) switch( cmd ) { case XWRELAY_GAME_CONNECT: logf( "processMessage got XWRELAY_CONNECT" ); - processConnect( buf+1, bufLen-1, socket, 0 ); + processConnect( buf+1, bufLen-1, socket ); break; case XWRELAY_GAME_RECONNECT: logf( "processMessage got XWRELAY_RECONNECT" ); - processConnect( buf+1, bufLen-1, socket, 1 ); + processReconnect( buf+1, bufLen-1, socket ); break; case XWRELAY_GAME_DISCONNECT: processDisconnect( buf+1, bufLen-1, socket ); break; - case XWRELAY_CONNECTRESP: - logf( "bad: processMessage got XWRELAY_CONNECTRESP" ); - break; - case XWRELAY_MSG_FROMRELAY: - logf( "bad: processMessage got XWRELAY_MSG_FROMRELAY" ); - break; case XWRELAY_HEARTBEAT: logf( "processMessage got XWRELAY_HEARTBEAT" ); processHeartbeat( buf + 1, bufLen - 1, socket ); @@ -303,7 +327,9 @@ processMessage( unsigned char* buf, int bufLen, int socket ) } break; default: - assert(0); + logf( "processMessage bad: %d", cmd ); + break; + /* just drop it */ } } /* processMessage */ @@ -344,6 +370,7 @@ enum { FLAG_HELP ,FLAG_PORT ,FLAG_CPORT ,FLAG_NTHREADS + ,FLAG_NAME }; struct option longopts[] = { @@ -365,6 +392,12 @@ struct option longopts[] = { NULL, FLAG_PORT } + ,{ + "name", + 1, + NULL, + FLAG_NAME + } ,{ "ctrlport", 1, @@ -401,9 +434,10 @@ int main( int argc, char** argv ) int ctrlport = 0; int nWorkerThreads = 0; char* conffile = NULL; + const char* serverName = NULL; /* Verify sizes here... */ - assert( sizeof(CookieID) == 4 ); + assert( sizeof(CookieID) == 2 ); /* Read options. Options trump config file values when they conflict, but @@ -411,7 +445,7 @@ int main( int argc, char** argv ) first. */ for ( ; ; ) { - int opt = getopt_long(argc, argv, "hc:p:l:",longopts, NULL); + int opt = getopt_long(argc, argv, "hc:p:l:n:",longopts, NULL); if ( opt == -1 ) { break; @@ -427,6 +461,9 @@ int main( int argc, char** argv ) case FLAG_PORT: port = atoi( optarg ); break; + case FLAG_NAME: + serverName = optarg; + break; case FLAG_CPORT: ctrlport = atoi( optarg ); break; @@ -451,7 +488,11 @@ int main( int argc, char** argv ) if ( nWorkerThreads == 0 ) { nWorkerThreads = cfg->GetNWorkerThreads(); } + if ( serverName == NULL ) { + serverName = cfg->GetServerName(); + } + PermID::SetServerName( serverName ); int listener = make_socket( INADDR_ANY, port ); if ( listener == -1 ) { diff --git a/relay/xwrelay.h b/relay/xwrelay.h index 33371be45..bc163e859 100644 --- a/relay/xwrelay.h +++ b/relay/xwrelay.h @@ -31,23 +31,27 @@ enum { XWRELAY_NONE /* 0 is an illegal value */ , XWRELAY_GAME_CONNECT /* Sent from device to relay to establish connection to relay. Format: - flags: 1; cookieLen: 1; cookie: ; hostID: - 2. connectionID: 2. */ + flags: 1; cookieLen: 1; cookie: ; hostID: 1. */ , XWRELAY_GAME_RECONNECT - /* Connect using ID rather than cookie. Used by a device that's lost - its connection to a game in progress. Once a game is locked this is - the only way a host can get (back) in. */ + /* Connect using connName rather than cookie. Used by a device that's + lost its connection to a game in progress. Once a game is locked + this is the only way a host can get (back) in. Format: flags: 1; + hostID: 1; connNameLen: 1; connName*/ , XWRELAY_GAME_DISCONNECT /* Tell the relay that we're gone for this game. After this message is sent, the host can reconnect on the same socket for a new game. - Format: cookieID: 4; srcID: 2 */ + Format: cookieID: 2; srcID: 1 */ - , XWRELAY_CONNECTRESP - /* Sent from relay to device in response to XWRELAY_CONNECT or - XWRELAY_RECONNECT. Format: heartbeat_seconds: 2; connectionID: - 2; */ + , XWRELAY_CONNECT_RESP + /* Sent from relay to device in response to XWRELAY_CONNECT. Format: + heartbeat_seconds: 2; connectionID: 2; assignedHostID: 1; + connNameLen: 1; connName: ; */ + + , XWRELAY_RECONNECT_RESP + /* Sent from relay to device in response to XWRELAY_RECONNECT. Format: + heartbeat_seconds: 2; connectionID: 2; */ , XWRELAY_OTHERCONNECT /* Another device has [re]joined your game. Format: ??? */ @@ -58,21 +62,21 @@ enum { XWRELAY_NONE /* 0 is an illegal value */ , XWRELAY_DISCONNECT_OTHER /* Another device has left the game. - Format: errorCode: 1; lostHostId: 2 */ + Format: errorCode: 1; lostHostId: 1 */ , XWRELAY_CONNECTDENIED /* The relay says go away. Format: reason code: 1 */ , XWRELAY_HEARTBEAT - /* Sent in either direction. Format: cookieID: 4; srcID: 2 */ + /* Sent in either direction. Format: cookieID: 2; srcID: 1 */ , XWRELAY_MSG_FROMRELAY - /* Sent from relay to device. Format: cookieID: 4; src_hostID: 2; - dest_hostID: 2; data */ + /* Sent from relay to device. Format: cookieID: 2; src_hostID: 1; + dest_hostID: 1; data */ , XWRELAY_MSG_TORELAY /* Sent from device to relay. Format: connectionID: 2; src_hostID: - 2; dest_hostID: 2 */ + 1; dest_hostID: 1 */ } XWRelayMsg; #ifndef CANT_DO_TYPEDEF @@ -84,6 +88,7 @@ typedef unsigned char XWRELAY_Cmd; #define MAX_COOKIE_LEN 15 #define MAX_MSG_LEN 256 /* 100 is more like it */ +#define MAX_CONNNAME_LEN 35 /* host id plus a small integer, typically */ #define XWRELAY_PROTO_VERSION 0x01 @@ -102,7 +107,7 @@ typedef enum { } XWREASON; -typedef unsigned int CookieID; +typedef unsigned short CookieID; #define COOKIE_ID_NONE 0L #endif diff --git a/relay/xwrelay_priv.h b/relay/xwrelay_priv.h index 7e245f195..0bbebe9b1 100644 --- a/relay/xwrelay_priv.h +++ b/relay/xwrelay_priv.h @@ -5,7 +5,7 @@ #include -typedef unsigned short HostID; +typedef unsigned char HostID; void logf( const char* format, ... );