server no longer asserts when a client connects with a name that's

already in use by another client.  also added reporting of errors
from the server to clients so clients can report meaningful
messages to users.
This commit is contained in:
crs 2002-05-23 14:56:03 +00:00
parent 995771eec1
commit 13eee14232
8 changed files with 159 additions and 57 deletions

View File

@ -269,6 +269,18 @@ void CClient::runSession(void*)
log((CLOG_DEBUG1 "recv close")); log((CLOG_DEBUG1 "recv close"));
break; break;
} }
else if (memcmp(code, kMsgEIncompatible, 4) == 0) {
onErrorIncompatible();
break;
}
else if (memcmp(code, kMsgEBusy, 4) == 0) {
onErrorBusy();
break;
}
else if (memcmp(code, kMsgEBad, 4) == 0) {
onErrorBad();
break;
}
else { else {
// unknown message // unknown message
log((CLOG_ERR "unknown message from server")); log((CLOG_ERR "unknown message from server"));
@ -543,3 +555,21 @@ void CClient::onMouseWheel()
log((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); log((CLOG_DEBUG2 "recv mouse wheel %+d", delta));
m_screen->mouseWheel(delta); m_screen->mouseWheel(delta);
} }
void CClient::onErrorIncompatible()
{
SInt32 major, minor;
CLock lock(&m_mutex);
CProtocolUtil::readf(m_input, kMsgEIncompatible + 4, &major, &minor);
log((CLOG_ERR "server has incompatible version %d.%d", major, minor));
}
void CClient::onErrorBusy()
{
log((CLOG_ERR "server already has a connected client with name \"%s\"", m_name.c_str()));
}
void CClient::onErrorBad()
{
log((CLOG_ERR "server disconnected due to a protocol error"));
}

View File

@ -48,6 +48,9 @@ private:
void onMouseUp(); void onMouseUp();
void onMouseMove(); void onMouseMove();
void onMouseWheel(); void onMouseWheel();
void onErrorIncompatible();
void onErrorBusy();
void onErrorBad();
private: private:
CMutex m_mutex; CMutex m_mutex;

View File

@ -838,69 +838,81 @@ void CServer::handshakeClient(void* vsocket)
std::auto_ptr<IServerProtocol> protocol; std::auto_ptr<IServerProtocol> protocol;
std::auto_ptr<CConnectionNote> connectedNote; std::auto_ptr<CConnectionNote> connectedNote;
{ try {
// give the client a limited time to complete the handshake {
CTimerThread timer(30.0); // give the client a limited time to complete the handshake
CTimerThread timer(30.0);
// limit the maximum length of the hello // limit the maximum length of the hello
static const UInt32 maxHelloLen = 1024; static const UInt32 maxHelloLen = 1024;
// say hello // say hello
log((CLOG_DEBUG1 "saying hello")); log((CLOG_DEBUG1 "saying hello"));
CProtocolUtil::writef(output.get(), "Synergy%2i%2i", CProtocolUtil::writef(output.get(), "Synergy%2i%2i",
kMajorVersion, kMinorVersion); kMajorVersion, kMinorVersion);
output->flush(); output->flush();
// wait for the reply // wait for the reply
log((CLOG_DEBUG1 "waiting for hello reply")); log((CLOG_DEBUG1 "waiting for hello reply"));
UInt32 n = input->getSize(); UInt32 n = input->getSize();
if (n > maxHelloLen) { if (n > maxHelloLen) {
throw XBadClient(); throw XBadClient();
} }
// get and parse the reply to hello // get and parse the reply to hello
SInt16 major, minor; SInt16 major, minor;
try { try {
log((CLOG_DEBUG1 "parsing hello reply")); log((CLOG_DEBUG1 "parsing hello reply"));
CProtocolUtil::readf(input.get(), "Synergy%2i%2i%s", CProtocolUtil::readf(input.get(), "Synergy%2i%2i%s",
&major, &minor, &name); &major, &minor, &name);
} }
catch (XIO&) { catch (XIO&) {
throw XBadClient(); throw XBadClient();
} }
if (major < 0 || minor < 0) {
throw XBadClient();
}
// create a protocol interpreter for the version // create a protocol interpreter for the version
log((CLOG_DEBUG1 "creating interpreter for client %s version %d.%d", name.c_str(), major, minor)); log((CLOG_DEBUG1 "creating interpreter for client \"%s\" version %d.%d", name.c_str(), major, minor));
assign(protocol, CServerProtocol::create(major, minor, assign(protocol, CServerProtocol::create(major, minor,
this, name, input.get(), output.get()), this, name, input.get(), output.get()),
IServerProtocol); IServerProtocol);
// client is now pending // client is now pending
assign(connectedNote, new CConnectionNote(this, assign(connectedNote, new CConnectionNote(this,
name, protocol.get()), CConnectionNote); name, protocol.get()), CConnectionNote);
// ask and wait for the client's info // ask and wait for the client's info
log((CLOG_DEBUG1 "waiting for info for client %s", name.c_str())); log((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str()));
protocol->queryInfo(); protocol->queryInfo();
// now connected; client no longer subject to timeout.
}
// handle messages from client. returns when the client
// disconnects.
log((CLOG_NOTE "client \"%s\" has connected", name.c_str()));
protocol->run();
}
catch (XDuplicateClient& e) {
// client has duplicate name
log((CLOG_WARN "a client with name \"%s\" is already connected)", e.getName().c_str()));
CProtocolUtil::writef(output.get(), kMsgEBusy);
}
catch (XIncompatibleClient& e) {
// client is incompatible
// FIXME -- could print network address if socket had suitable method
log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor()));
CProtocolUtil::writef(output.get(), kMsgEIncompatible,
kMajorVersion, kMinorVersion);
}
catch (XBadClient&) {
// client not behaving
// FIXME -- could print network address if socket had suitable method
log((CLOG_WARN "protocol error from client \"%s\"", name.c_str()));
CProtocolUtil::writef(output.get(), kMsgEBad);
} }
// handle messages from client. returns when the client // flush any pending output
// disconnects. output.get()->flush();
log((CLOG_NOTE "client %s is connected", name.c_str()));
protocol->run();
}
catch (XIncompatibleClient& e) {
// client is incompatible
log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor()));
// FIXME -- could print network address if socket had suitable method
}
catch (XBadClient&) {
// client not behaving
log((CLOG_WARN "protocol error from client \"%s\"", name.c_str()));
// FIXME -- could print network address if socket had suitable method
} }
catch (XBase& e) { catch (XBase& e) {
// misc error // misc error
@ -1087,10 +1099,18 @@ CServer::CScreenInfo* CServer::addConnection(
const CString& name, IServerProtocol* protocol) const CString& name, IServerProtocol* protocol)
{ {
log((CLOG_DEBUG "adding connection \"%s\"", name.c_str())); log((CLOG_DEBUG "adding connection \"%s\"", name.c_str()));
CLock lock(&m_mutex); CLock lock(&m_mutex);
assert(m_screens.count(name) == 0);
// can only have one screen with a given name at any given time
if (m_screens.count(name) != 0) {
throw XDuplicateClient(name);
}
// save screen info
CScreenInfo* newScreen = new CScreenInfo(name, protocol); CScreenInfo* newScreen = new CScreenInfo(name, protocol);
m_screens.insert(std::make_pair(name, newScreen)); m_screens.insert(std::make_pair(name, newScreen));
return newScreen; return newScreen;
} }

View File

@ -50,18 +50,19 @@ IServerProtocol* CServerProtocol::create(SInt32 major, SInt32 minor,
CServer* server, const CString& client, CServer* server, const CString& client,
IInputStream* input, IOutputStream* output) IInputStream* input, IOutputStream* output)
{ {
// disallow invalid version numbers
if (major < 0 || minor < 0) {
throw XIncompatibleClient(major, minor);
}
// disallow connection from test versions to release versions // disallow connection from test versions to release versions
if (major == 0 && kMajorVersion != 0) { if (major == 0 && kMajorVersion != 0) {
output->write(kMsgEIncompatible, sizeof(kMsgEIncompatible) - 1);
output->flush();
throw XIncompatibleClient(major, minor); throw XIncompatibleClient(major, minor);
} }
// hangup (with error) if version isn't supported // hangup (with error) if version isn't supported
if (major > kMajorVersion || if (major > kMajorVersion ||
(major == kMajorVersion && minor > kMinorVersion)) { (major == kMajorVersion && minor > kMinorVersion)) {
output->write(kMsgEIncompatible, sizeof(kMsgEIncompatible) - 1);
output->flush();
throw XIncompatibleClient(major, minor); throw XIncompatibleClient(major, minor);
} }

View File

@ -58,7 +58,7 @@ void CServerProtocol1_0::run()
} }
// FIXME -- more message here // FIXME -- more message here
else { else {
log((CLOG_ERR "unknown message from client \"%s\"", getClient().c_str())); log((CLOG_ERR "invalid message from client \"%s\"", getClient().c_str()));
// unknown message // unknown message
throw XBadClient(); throw XBadClient();

View File

@ -109,7 +109,16 @@ static const char kMsgQInfo[] = "QINF";
// error codes // error codes
// //
static const char kMsgEIncompatible[] = "EICV"; // incompatible versions: primary -> secondary
// $1 = major version of primary, $2 = minor version of primary.
static const char kMsgEIncompatible[] = "EICV%2i%2i";
// name provided when connecting is already in use: primary -> secondary
static const char kMsgEBusy[] = "EBSY";
// protocol violation: primary -> secondary
// primary should disconnect after sending this message.
static const char kMsgEBad[] = "EBAD";
#endif #endif

View File

@ -9,8 +9,9 @@ CString XBadClient::getWhat() const throw()
return "XBadClient"; return "XBadClient";
} }
// //
// // XIncompatibleClient
// //
XIncompatibleClient::XIncompatibleClient(int major, int minor) : XIncompatibleClient::XIncompatibleClient(int major, int minor) :
@ -35,3 +36,22 @@ CString XIncompatibleClient::getWhat() const throw()
return "XIncompatibleClient"; return "XIncompatibleClient";
} }
//
// XDuplicateClient
//
XDuplicateClient::XDuplicateClient(const CString& name) : m_name(name)
{
// do nothing
}
const CString& XDuplicateClient::getName() const throw()
{
return m_name;
}
CString XDuplicateClient::getWhat() const throw()
{
return "XDuplicateClient";
}

View File

@ -31,4 +31,23 @@ private:
int m_minor; int m_minor;
}; };
// client has duplicate name (i.e. client with name is already connected)
class XDuplicateClient : public XSynergy {
public:
XDuplicateClient(const CString& name);
// manipulators
// accessors
virtual const CString&
getName() const throw();
protected:
virtual CString getWhat() const throw();
private:
CString m_name;
};
#endif #endif