diff options
author | Alexander Larsson <alexl@redhat.com> | 2010-06-22 16:36:42 +0200 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2010-06-22 17:34:45 +0200 |
commit | 72cf104c2897b4dd124c8a65df02e6895a9f1d88 (patch) | |
tree | a1f4c3db6621d16db6744fc84ee0a22736e5108b | |
parent | 652c13e71b8c203fc701edb6bad365c50e7cf1aa (diff) | |
download | spice-72cf104c2897b4dd124c8a65df02e6895a9f1d88.tar.gz spice-72cf104c2897b4dd124c8a65df02e6895a9f1d88.tar.xz spice-72cf104c2897b4dd124c8a65df02e6895a9f1d88.zip |
client: Support connecting to a major==1 server
-rw-r--r-- | client/red_channel.cpp | 96 | ||||
-rw-r--r-- | client/red_channel.h | 20 | ||||
-rw-r--r-- | client/red_client.cpp | 8 | ||||
-rw-r--r-- | client/red_client.h | 3 | ||||
-rw-r--r-- | client/red_peer.h | 5 |
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; |