mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-29 00:41:33 +00:00
client: Support connecting to a major==1 server
This commit is contained in:
parent
652c13e71b
commit
72cf104c28
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user