client: Support connecting to a major==1 server

This commit is contained in:
Alexander Larsson 2010-06-22 16:36:42 +02:00
parent 652c13e71b
commit 72cf104c28
5 changed files with 106 additions and 30 deletions

View File

@ -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;
}
RedPeer::close();
}
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();
}
}
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);

View File

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

View File

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

View File

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

View File

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