コード例 #1
0
/* coroutine context */
G_GNUC_INTERNAL
void spice_channel_handle_migrate(SpiceChannel *channel, spice_msg_in *in)
{
    spice_msg_out *out;
    spice_msg_in *data = NULL;
    SpiceMsgMigrate *mig = spice_msg_in_parsed(in);
    spice_channel *c = channel->priv;

    SPICE_DEBUG("%s: channel %s flags %u", __FUNCTION__, c->name, mig->flags);
    if (mig->flags & SPICE_MIGRATE_NEED_FLUSH) {
        /* iterate_write is blocking and flushing all pending write */
        SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel);

        out = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_MIGRATE_FLUSH_MARK);
        spice_msg_out_send_internal(out);
        spice_msg_out_unref(out);
        SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel);
    }
    if (mig->flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) {
        spice_channel_recv_msg(channel, get_msg_handler, &data);
        if (!data || data->header.type != SPICE_MSG_MIGRATE_DATA) {
            g_warning("expected SPICE_MSG_MIGRATE_DATA, got %d", data->header.type);
        }
    }

    spice_session_channel_migrate(c->session, channel);

    if (mig->flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) {
        out = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_MIGRATE_DATA);
        spice_marshaller_add(out->marshaller, data->data, data->header.size);
        spice_msg_out_send_internal(out);
        spice_msg_out_unref(out);
    }
}
コード例 #2
0
/* coroutine context */
static void spice_channel_recv_link_msg(SpiceChannel *channel)
{
    spice_channel *c = channel->priv;
    int rc, num_caps, i;

    g_return_if_fail(channel != NULL);

    rc = spice_channel_read(channel, (uint8_t*)c->peer_msg + c->peer_pos,
                            c->peer_hdr.size - c->peer_pos);
    c->peer_pos += rc;
    if (c->peer_pos != c->peer_hdr.size) {
        g_critical("%s: %s: incomplete link reply (%d/%d)",
                  c->name, __FUNCTION__, rc, c->peer_hdr.size);
        emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_ERROR_LINK);
        return;
    }
    switch (c->peer_msg->error) {
    case SPICE_LINK_ERR_OK:
        /* nothing */
        break;
    case SPICE_LINK_ERR_NEED_SECURED:
        c->tls = true;
        SPICE_DEBUG("%s: switching to tls", c->name);
        SPICE_CHANNEL_GET_CLASS(channel)->channel_disconnect(channel);
        spice_channel_connect(channel);
        return;
    default:
        g_warning("%s: %s: unhandled error %d",
                c->name, __FUNCTION__, c->peer_msg->error);
        SPICE_CHANNEL_GET_CLASS(channel)->channel_disconnect(channel);
        emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_ERROR_LINK);
        return;
    }

    num_caps = c->peer_msg->num_channel_caps + c->peer_msg->num_common_caps;
    SPICE_DEBUG("%s: %s: %d caps", c->name, __FUNCTION__, num_caps);

    /* see original spice/client code: */
    /* g_return_if_fail(c->peer_msg + c->peer_msg->caps_offset * sizeof(uint32_t) > c->peer_msg + c->peer_hdr.size); */

    uint32_t *caps = (uint32_t *)((uint8_t *)c->peer_msg + c->peer_msg->caps_offset);

    g_array_set_size(c->remote_common_caps, c->peer_msg->num_common_caps);
    for (i = 0; i < c->peer_msg->num_common_caps; i++, caps++) {
        g_array_index(c->remote_common_caps, uint32_t, i) = *caps;
        SPICE_DEBUG("got caps %u %u", i, *caps);
    }

    g_array_set_size(c->remote_caps, c->peer_msg->num_channel_caps);
    for (i = 0; i < c->peer_msg->num_channel_caps; i++, caps++) {
        g_array_index(c->remote_caps, uint32_t, i) = *caps;
        SPICE_DEBUG("got caps %u %u", i, *caps);
    }

    c->state = SPICE_CHANNEL_STATE_AUTH;
    spice_channel_send_auth(channel);
}
コード例 #3
0
G_GNUC_INTERNAL
void spice_channel_up(SpiceChannel *channel)
{
    spice_channel *c = channel->priv;

    SPICE_DEBUG("%s: channel up, state %d", c->name, c->state);

    if (SPICE_CHANNEL_GET_CLASS(channel)->channel_up)
        SPICE_CHANNEL_GET_CLASS(channel)->channel_up(channel);
}
コード例 #4
0
/* coroutine context */
static gboolean spice_channel_iterate(SpiceChannel *channel)
{
    spice_channel *c = channel->priv;
    GIOCondition ret;

    do {
        while (c->state == SPICE_CHANNEL_STATE_MIGRATING) {
            /* freeze coroutine */
            coroutine_yield(NULL);
            g_return_val_if_fail(c->state != SPICE_CHANNEL_STATE_MIGRATING, FALSE);
        }

        if (c->has_error) {
            SPICE_DEBUG("channel has error, breaking loop");
            return FALSE;
        }

        SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel);
	/*FIXME: this calling will fall in deadloop because coroutine_swap() returns NULL;
	 * So I force reading.
	 * */
        ret = G_IO_IN;//g_io_wait_interruptable(&c->wait, c->sock, G_IO_IN);
#ifdef WIN32
        /* FIXME: windows gsocket is buggy, it doesn't return correct condition... */
        ret = g_socket_condition_check(c->sock, G_IO_IN);
#endif
    } while (ret == 0); /* ret == 0 means no IO condition, but woken up */

    if (ret & (G_IO_ERR|G_IO_HUP)) {
        SPICE_DEBUG("got socket error before read(): %d", ret);
        c->has_error = TRUE;
        return FALSE;
    }

    SPICE_CHANNEL_GET_CLASS(channel)->iterate_read(channel);

    return TRUE;
}
コード例 #5
0
ファイル: channel-base.c プロジェクト: hwc56/spicy
/* coroutine context */
G_GNUC_INTERNAL
void spice_channel_handle_migrate(SpiceChannel *channel, SpiceMsgIn *in)
{
    SpiceMsgOut *out;
    SpiceMsgIn *data = NULL;
    SpiceMsgMigrate *mig = spice_msg_in_parsed(in);
    SpiceChannelPrivate *c = channel->priv;
    g_message("spice_channel_handle_migrate.\n");
    CHANNEL_DEBUG(channel, "%s: flags %u", __FUNCTION__, mig->flags);
    if (mig->flags & SPICE_MIGRATE_NEED_FLUSH) {
        g_message("++++++++++++ SPICE_MIGRATE_NEED_FLUSH.\n");
        /* if peer version > 1: pushing the mark msg before all other messgages and sending it,
         * and only it */
        if (c->peer_hdr.major_version == 1) {
            /* iterate_write is blocking and flushing all pending write */
            SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel);
        }
        out = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_MIGRATE_FLUSH_MARK);
        spice_msg_out_send_internal(out);
    }
    if (mig->flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) {
        g_message("+++++++++++ SPICE_MIGRATE_NEED_DATA_TRANSFER.\n");
        spice_channel_recv_msg(channel, get_msg_handler, &data);
        if (!data) {
            g_critical("expected SPICE_MSG_MIGRATE_DATA, got empty message");
            goto end;
        } else if (spice_header_get_msg_type(data->header, c->use_mini_header) !=
                   SPICE_MSG_MIGRATE_DATA) {
            g_critical("expected SPICE_MSG_MIGRATE_DATA, got %d",
                      spice_header_get_msg_type(data->header, c->use_mini_header));
            goto end;
        }
    }

    /* swapping channels sockets */
    spice_session_channel_migrate(c->session, channel);

    /* pushing the MIGRATE_DATA before all other pending messages */
    if ((mig->flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) && (data != NULL)) {
        out = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_MIGRATE_DATA);
        spice_marshaller_add(out->marshaller, data->data,
                             spice_header_get_msg_size(data->header, c->use_mini_header));
        spice_msg_out_send_internal(out);
    }

end:
    if (data)
        spice_msg_in_unref(data);
}
コード例 #6
0
/* coroutine context */
static void spice_channel_iterate_read(SpiceChannel *channel)
{
    spice_channel *c = channel->priv;

    /* TODO: get rid of state, and use coroutine state */
    switch (c->state) {
    case SPICE_CHANNEL_STATE_LINK_HDR:
        spice_channel_recv_link_hdr(channel);
        break;
    case SPICE_CHANNEL_STATE_LINK_MSG:
        spice_channel_recv_link_msg(channel);
        break;
    case SPICE_CHANNEL_STATE_AUTH:
        spice_channel_recv_auth(channel);
        break;
    case SPICE_CHANNEL_STATE_READY:
        spice_channel_recv_msg(channel,
            (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL);
        break;
    default:
        g_critical("unknown state %d", c->state);
    }
}
コード例 #7
0
/* coroutine context */
static void spice_channel_recv_link_hdr(SpiceChannel *channel)
{
    spice_channel *c = channel->priv;
    int rc;

    rc = spice_channel_read(channel, &c->peer_hdr, sizeof(c->peer_hdr));
    if (rc != sizeof(c->peer_hdr)) {
        g_critical("incomplete link header (%d/%" G_GSIZE_FORMAT ")",
                   rc, sizeof(c->peer_hdr));
        goto error;
    }
    if (c->peer_hdr.magic != SPICE_MAGIC) {
        g_critical("invalid SPICE_MAGIC!");
        goto error;
    }

    if (c->peer_hdr.major_version != c->link_hdr.major_version) {
        if (c->peer_hdr.major_version == 1) {
            /* enter spice 0.4 mode */
            g_object_set(c->session, "protocol", 1, NULL);
            SPICE_DEBUG("%s: switching to protocol 1 (spice 0.4)", c->name);
            SPICE_CHANNEL_GET_CLASS(channel)->channel_disconnect(channel);
            spice_channel_connect(channel);
            return;
        }
        g_critical("major mismatch (got %d, expected %d)",
                   c->peer_hdr.major_version, c->link_hdr.major_version);
        goto error;
    }

    c->peer_msg = spice_malloc(c->peer_hdr.size);
    c->state = SPICE_CHANNEL_STATE_LINK_MSG;
    return;

error:
    emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_ERROR_LINK);
}
コード例 #8
0
/* coroutine context */
static void *spice_channel_coroutine(void *data)
{
    SpiceChannel *channel = SPICE_CHANNEL(data);
    spice_channel *c = channel->priv;
    int ret;
    guint verify;

    SPICE_DEBUG("Started background coroutine %p", &c->coroutine);

    if (spice_session_get_client_provided_socket(c->session)) {
        if (c->fd < 0) {
            g_critical("fd not provided!");
            goto cleanup;
        }

	if (!(c->sock = g_socket_new_from_fd(c->fd, NULL))) {
		SPICE_DEBUG("Failed to open socket from fd %d", c->fd);
		return FALSE;
	}

	g_socket_set_blocking(c->sock, FALSE);
        goto connected;
    }

reconnect:
    c->sock = spice_session_channel_open_host(c->session, c->tls);
    if (c->sock == NULL) {
        if (!c->tls) {
            SPICE_DEBUG("connection failed, trying with TLS port");
            c->tls = true; /* FIXME: does that really work with provided fd */
            goto reconnect;
        } else {
            SPICE_DEBUG("Connect error");
            emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_ERROR_CONNECT);
            goto cleanup;
        }
    }

    c->has_error = FALSE;

    if (c->tls) {
        int rc;

        c->ctx = SSL_CTX_new(TLSv1_method());
        if (c->ctx == NULL) {
            g_critical("SSL_CTX_new failed");
            goto cleanup;
        }

        verify = spice_session_get_verify(c->session);
        if (verify &
            (SPICE_SESSION_VERIFY_PUBKEY | SPICE_SESSION_VERIFY_HOSTNAME)) {
            gchar *ca_file;

            g_object_get(c->session, "ca-file", &ca_file, NULL);
            if (ca_file) {
                rc = SSL_CTX_load_verify_locations(c->ctx, ca_file, NULL);
                if (rc != 1)
                    g_warning("loading ca certs from %s failed", ca_file);
                g_free(ca_file);

                if (rc != 1) {
                    if (verify & SPICE_SESSION_VERIFY_PUBKEY) {
                        g_warning("only pubkey active");
                        verify = SPICE_SESSION_VERIFY_PUBKEY;
                    } else
                        goto cleanup;
                }
            }
        }

        c->ssl = SSL_new(c->ctx);
        if (c->ssl == NULL) {
            g_critical("SSL_new failed");
            goto cleanup;
        }
        rc = SSL_set_fd(c->ssl, g_socket_get_fd(c->sock));
        if (rc <= 0) {
            g_critical("SSL_set_fd failed");
            goto cleanup;
        }


        {
            gchar *hostname, *subject;
            guint8 *pubkey;
            guint pubkey_len;

            g_object_get(c->session,
                         "host", &hostname,
                         "cert-subject", &subject, NULL);
            spice_session_get_pubkey(c->session, &pubkey, &pubkey_len);
            c->sslverify = spice_openssl_verify_new(c->ssl, verify,
                                                    hostname,
                                                    (char*)pubkey, pubkey_len,
                                                    subject);
            g_free(hostname);
            g_free(subject);
        }

ssl_reconnect:
        rc = SSL_connect(c->ssl);
        if (rc <= 0) {
            rc = SSL_get_error(c->ssl, rc);
            if (rc == SSL_ERROR_WANT_READ || rc == SSL_ERROR_WANT_WRITE) {
                g_io_wait(c->sock, G_IO_OUT|G_IO_ERR|G_IO_HUP);
                goto ssl_reconnect;
            } else {
                g_warning("%s: SSL_connect: %s",
                          c->name, ERR_error_string(rc, NULL));
                emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_ERROR_TLS);
                goto cleanup;
            }
        }
    }

connected:
    c->state = SPICE_CHANNEL_STATE_LINK_HDR;
    spice_channel_send_link(channel);

    while ((ret = spice_channel_iterate(channel)))
        ;

    /* TODO: improve it, this is a bit hairy, c->coroutine will be
       overwritten on (re)connect, so we skip the normal cleanup
       path. Ideally, we shouldn't use the same channel structure? */
    if (c->state == SPICE_CHANNEL_STATE_CONNECTING) {
        g_object_unref(channel);
        goto end;
    }

cleanup:
    SPICE_DEBUG("Coroutine exit");

    SPICE_CHANNEL_GET_CLASS(channel)->channel_disconnect(channel);

    g_idle_add(spice_channel_delayed_unref, data);


end:
    /* Co-routine exits now - the SpiceChannel object may no longer exist,
       so don't do anything else now unless you like SEGVs */

    exit(1);
    return NULL;
}