summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/red_channel.cpp96
-rw-r--r--client/red_channel.h20
-rw-r--r--client/red_client.cpp8
-rw-r--r--client/red_client.h3
-rw-r--r--client/red_peer.h5
5 files changed, 104 insertions, 28 deletions
diff --git a/client/red_channel.cpp b/client/red_channel.cpp
index 2e6130ca..d499e8cf 100644
--- a/client/red_channel.cpp
+++ b/client/red_channel.cpp
@@ -42,7 +42,8 @@ RedChannelBase::~RedChannelBase()
{
}
-void RedChannelBase::link(uint32_t connection_id, const std::string& password)
+void RedChannelBase::link(uint32_t connection_id, const std::string& password,
+ int protocol)
{
SpiceLinkHeader header;
SpiceLinkMess link_mess;
@@ -54,11 +55,19 @@ void RedChannelBase::link(uint32_t connection_id, const std::string& password)
int nRSASize;
BIO *bioKey;
RSA *rsa;
+ uint8_t *buffer, *p;
header.magic = SPICE_MAGIC;
header.size = sizeof(link_mess);
- header.major_version = SPICE_VERSION_MAJOR;
- header.minor_version = SPICE_VERSION_MINOR;
+ if (protocol == 1) {
+ /* protocol 1 == major 1, old 0.4 protocol, last active minor */
+ header.major_version = 1;
+ header.minor_version = 3;
+ } else if (protocol == 2) {
+ /* protocol 2 == current */
+ header.major_version = SPICE_VERSION_MAJOR;
+ header.minor_version = SPICE_VERSION_MINOR;
+ }
link_mess.connection_id = connection_id;
link_mess.channel_type = _type;
link_mess.channel_id = _id;
@@ -66,30 +75,44 @@ void RedChannelBase::link(uint32_t connection_id, const std::string& password)
link_mess.num_channel_caps = get_caps().size();
link_mess.caps_offset = sizeof(link_mess);
header.size += (link_mess.num_common_caps + link_mess.num_channel_caps) * sizeof(uint32_t);
- send((uint8_t*)&header, sizeof(header));
- send((uint8_t*)&link_mess, sizeof(link_mess));
+ buffer =
+ new uint8_t[sizeof(header) + sizeof(link_mess) +
+ _common_caps.size() * sizeof(uint32_t) +
+ _caps.size() * sizeof(uint32_t)];
+ p = buffer;
+
+ memcpy(p, (uint8_t*)&header, sizeof(header));
+ p += sizeof(header);
+ memcpy(p, (uint8_t*)&link_mess, sizeof(link_mess));
+ p += sizeof(link_mess);
for (i = 0; i < _common_caps.size(); i++) {
- send((uint8_t*)&_common_caps[i], sizeof(uint32_t));
+ *(uint32_t *)p = _common_caps[i];
+ p += sizeof(uint32_t);
}
for (i = 0; i < _caps.size(); i++) {
- send((uint8_t*)&_caps[i], sizeof(uint32_t));
+ *(uint32_t *)p = _caps[i];
+ p += sizeof(uint32_t);
}
+ send(buffer, p - buffer);
+ delete [] buffer;
+
recive((uint8_t*)&header, sizeof(header));
if (header.magic != SPICE_MAGIC) {
THROW_ERR(SPICEC_ERROR_CODE_CONNECT_FAILED, "bad magic");
}
- if (header.major_version != SPICE_VERSION_MAJOR) {
+ if (header.major_version != protocol) {
THROW_ERR(SPICEC_ERROR_CODE_VERSION_MISMATCH,
"version mismatch: expect %u got %u",
- SPICE_VERSION_MAJOR,
+ protocol,
header.major_version);
}
+ _remote_major = header.major_version;
_remote_minor = header.minor_version;
AutoArray<uint8_t> reply_buf(new uint8_t[header.size]);
@@ -160,21 +183,39 @@ void RedChannelBase::link(uint32_t connection_id, const std::string& password)
void RedChannelBase::connect(const ConnectionOptions& options, uint32_t connection_id,
const char* host, std::string password)
{
- if (options.allow_unsecure()) {
- try {
- RedPeer::connect_unsecure(host, options.unsecure_port);
- link(connection_id, password);
- return;
- } catch (...) {
- if (!options.allow_secure()) {
- throw;
+ int protocol = options.protocol;
+
+ if (protocol == 0) { /* AUTO, try major 2 first */
+ protocol = 2;
+ }
+
+ retry:
+ try {
+ if (options.allow_unsecure()) {
+ try {
+ RedPeer::connect_unsecure(host, options.unsecure_port);
+ link(connection_id, password, protocol);
+ return;
+ } catch (...) {
+ if (!options.allow_secure()) {
+ throw;
+ }
+ RedPeer::close();
}
- RedPeer::close();
}
+ ASSERT(options.allow_secure());
+ RedPeer::connect_secure(options, host);
+ link(connection_id, password, protocol);
+ } catch (Exception& e) {
+ if (protocol == 2 &&
+ options.protocol == 0 &&
+ e.get_error_code() == SPICEC_ERROR_CODE_VERSION_MISMATCH) {
+ RedPeer::cleanup();
+ protocol = 1;
+ goto retry;
+ }
+ throw;
}
- ASSERT(options.allow_secure());
- RedPeer::connect_secure(options, host);
- link(connection_id, password);
}
void RedChannelBase::set_capability(ChannelCaps& caps, uint32_t cap)
@@ -237,6 +278,7 @@ RedChannel::RedChannel(RedClient& client, uint8_t type, uint8_t id,
RedChannel::MessageHandler* handler,
Platform::ThreadPriority worker_priority)
: RedChannelBase(type, id, ChannelCaps(), ChannelCaps())
+ , _marshallers (NULL)
, _client (client)
, _state (PASSIVE_STATE)
, _action (WAIT_ACTION)
@@ -258,7 +300,6 @@ RedChannel::RedChannel(RedClient& client, uint8_t type, uint8_t id,
{
_loop.add_trigger(_send_trigger);
_loop.add_trigger(_abort_trigger);
- _marshallers = spice_message_marshallers_get();
}
RedChannel::~RedChannel()
@@ -398,11 +439,22 @@ void RedChannel::run()
ConnectionOptions con_options(_client.get_connection_options(get_type()),
_client.get_port(),
_client.get_sport(),
+ _client.get_protocol(),
_client.get_host_auth_options(),
_client.get_connection_ciphers());
RedChannelBase::connect(con_options, _client.get_connection_id(),
_client.get_host().c_str(),
_client.get_password().c_str());
+ /* If automatic protocol, remember the first connect protocol type */
+ if (_client.get_protocol() == 0) {
+ _client.set_protocol(get_peer_major());
+ }
+ /* Initialize when we know the remote major version */
+ if (_client.get_peer_major() == 1) {
+ _marshallers = spice_message_marshallers_get1();
+ } else {
+ _marshallers = spice_message_marshallers_get();
+ }
on_connect();
set_state(CONNECTED_STATE);
_loop.add_socket(*this);
diff --git a/client/red_channel.h b/client/red_channel.h
index d973a455..fd01f203 100644
--- a/client/red_channel.h
+++ b/client/red_channel.h
@@ -63,6 +63,7 @@ public:
const ChannelCaps& get_common_caps() { return _common_caps;}
const ChannelCaps& get_caps() {return _caps;}
+ uint32_t get_peer_major() { return _remote_major;}
uint32_t get_peer_minor() { return _remote_minor;}
protected:
@@ -74,7 +75,7 @@ protected:
private:
void set_capability(ChannelCaps& caps, uint32_t cap);
bool test_capability(const ChannelCaps& caps, uint32_t cap);
- void link(uint32_t connection_id, const std::string& password);
+ void link(uint32_t connection_id, const std::string& password, int protocol);
private:
uint8_t _type;
@@ -86,6 +87,7 @@ private:
ChannelCaps _remote_common_caps;
ChannelCaps _remote_caps;
+ uint32_t _remote_major;
uint32_t _remote_minor;
};
@@ -253,8 +255,10 @@ private:
template <class HandlerClass, unsigned int channel_id>
MessageHandlerImp<HandlerClass, channel_id>::MessageHandlerImp(HandlerClass& obj)
: _obj (obj)
+ , _parser (NULL)
{
- _parser = spice_get_server_channel_parser(channel_id, &_max_messages);
+ /* max_messages is always from current as its larger than for backwards compat */
+ spice_get_server_channel_parser(channel_id, &_max_messages);
_handlers = new Handler[_max_messages + 1];
memset(_handlers, 0, sizeof(Handler) * (_max_messages + 1));
}
@@ -270,6 +274,16 @@ void MessageHandlerImp<HandlerClass, channel_id>::handle_message(RedPeer::Compun
size_t parsed_size;
message_destructor_t parsed_free;
+ if (_parser == NULL) {
+ /* We need to do this lazily rather than at constuction because we
+ don't know the major until we've connected */
+ if (_obj.get_peer_major() == 1) {
+ _parser = spice_get_server_channel_parser1(channel_id, NULL);
+ } else {
+ _parser = spice_get_server_channel_parser(channel_id, NULL);
+ }
+ }
+
if (message.sub_list()) {
SpiceSubMessageList *sub_list;
sub_list = (SpiceSubMessageList *)(message.data() + message.sub_list());
@@ -295,12 +309,12 @@ void MessageHandlerImp<HandlerClass, channel_id>::handle_message(RedPeer::Compun
type = message.type();
size = message.size();
parsed = _parser(msg, msg + size, type, _obj.get_peer_minor(), &parsed_size, &parsed_free);
- RedPeer::InMessage main_message(type, parsed_size, parsed);
if (parsed == NULL) {
THROW("failed to parse message channel %d type %d", channel_id, type);
}
+ RedPeer::InMessage main_message(type, parsed_size, parsed);
(_obj.*_handlers[type])(&main_message);
parsed_free(parsed);
}
diff --git a/client/red_client.cpp b/client/red_client.cpp
index f5b52d64..573e4b28 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -171,7 +171,9 @@ void Migrate::run()
DBG(0, "");
try {
conn_type = _client.get_connection_options(SPICE_CHANNEL_MAIN);
- RedPeer::ConnectionOptions con_opt(conn_type, _port, _sport, _auth_options, _con_ciphers);
+ RedPeer::ConnectionOptions con_opt(conn_type, _port, _sport,
+ _client.get_peer_major(),
+ _auth_options, _con_ciphers);
MigChannels::iterator iter = _channels.begin();
connection_id = _client.get_connection_id();
connect_one(**iter, con_opt, connection_id);
@@ -179,6 +181,7 @@ void Migrate::run()
for (++iter; iter != _channels.end(); ++iter) {
conn_type = _client.get_connection_options((*iter)->get_type());
con_opt = RedPeer::ConnectionOptions(conn_type, _port, _sport,
+ _client.get_peer_major(),
_auth_options, _con_ciphers);
connect_one(**iter, con_opt, connection_id);
}
@@ -211,7 +214,7 @@ void Migrate::start(const SpiceMsgMainMigrationBegin* migrate)
{
DBG(0, "");
abort();
- if ((SPICE_VERSION_MAJOR == 1) && (_client.get_peer_minor() < 2)) {
+ if ((_client.get_peer_major() == 1) && (_client.get_peer_minor() < 2)) {
LOG_INFO("server minor version incompatible for destination authentication"
"(missing dest pubkey in SpiceMsgMainMigrationBegin)");
OldRedMigrationBegin* old_migrate = (OldRedMigrationBegin*)migrate;
@@ -304,6 +307,7 @@ RedClient::RedClient(Application& application)
, _application (application)
, _port (-1)
, _sport (-1)
+ , _protocol (0)
, _connection_id (0)
, _mouse_mode (SPICE_MOUSE_MODE_SERVER)
, _notify_disconnect (false)
diff --git a/client/red_client.h b/client/red_client.h
index 1b81328a..52a3456b 100644
--- a/client/red_client.h
+++ b/client/red_client.h
@@ -152,6 +152,8 @@ public:
const std::string& get_host() { return _host;}
int get_port() { return _port;}
int get_sport() { return _sport;}
+ int get_protocol() { return _protocol;}
+ void set_protocol(int protocol) { _protocol = protocol;}
virtual uint32_t get_connection_id() { return _connection_id;}
uint32_t get_mouse_mode() { return _mouse_mode;}
Application& get_application() { return _application;}
@@ -212,6 +214,7 @@ private:
std::string _host;
int _port;
int _sport;
+ int _protocol;
std::string _password;
uint32_t _connection_id;
uint32_t _mouse_mode;
diff --git a/client/red_peer.h b/client/red_peer.h
index 001f9fa9..25a35605 100644
--- a/client/red_peer.h
+++ b/client/red_peer.h
@@ -73,11 +73,13 @@ public:
};
ConnectionOptions(Type in_type, int in_port, int in_sport,
+ int in_protocol,
const HostAuthOptions& in_host_auth,
const std::string& in_ciphers)
: type (in_type)
, unsecure_port (in_port)
, secure_port (in_sport)
+ , protocol (in_protocol)
, host_auth (in_host_auth)
, ciphers (in_ciphers)
{
@@ -99,6 +101,7 @@ public:
Type type;
int unsecure_port;
int secure_port;
+ int protocol; // 0 == auto
HostAuthOptions host_auth; // for secure connection
std::string ciphers;
};
@@ -130,10 +133,10 @@ protected:
static bool verify_subject(X509* cert, const HostAuthOptions::CertFieldValueList& subject);
static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx);
+ void cleanup();
private:
void shutdown();
- void cleanup();
private:
SOCKET _peer;