make transmitted vars smaller where possible; new relay identifying

scheme where cookie is used only to connect, and is replaced for
reconnects by a relay-generated name that's supposed to be unique
across all games on all relays and includes a hostname read in from
config file; relay assign non-servers' hostIDs.
This commit is contained in:
ehouse 2005-10-01 16:33:45 +00:00
parent c954361d3b
commit e098e6f7b2
14 changed files with 407 additions and 233 deletions

View file

@ -24,6 +24,7 @@ SRC = xwrelay.cpp \
timermgr.cpp \
configs.cpp \
crefmgr.cpp \
permid.cpp \
OBJ = $(patsubst %.cpp,%.o,$(SRC))
LDFLAGS += -lpthread -g

View file

@ -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<HostID,HostRec>::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<HostID,HostRec>(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<HostID,HostRec>::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 );

View file

@ -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<HostID,HostRec> 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<CRefEvent> m_eventQueue;
static CookieID ms_nextConnectionID;
HostID m_nextHostID;
}; /* CookieRef */
#endif

View file

@ -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<CookieID, CookieRef*>(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;

View file

@ -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;
};

View file

@ -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 <num>\n"
"%s cookie name <name>\n"
"%s cookie name <connName>\n"
"%s cookie id <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 );

View file

@ -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;
}

View file

@ -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

View file

@ -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 },

View file

@ -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 */

View file

@ -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

View file

@ -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 ) {

View file

@ -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: <cookieLen>; hostID:
2. connectionID: 2. */
flags: 1; cookieLen: 1; cookie: <cookieLen>; 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<connNameLen>*/
, 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: <connNameLen>; */
, 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 <len-headerLen> */
/* Sent from relay to device. Format: cookieID: 2; src_hostID: 1;
dest_hostID: 1; data <len-headerLen> */
, 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

View file

@ -5,7 +5,7 @@
#include <time.h>
typedef unsigned short HostID;
typedef unsigned char HostID;
void logf( const char* format, ... );