2019-09-07 16:51:07 +00:00
|
|
|
/*
|
2022-02-19 23:06:15 +00:00
|
|
|
* Copyright (c) 2019 - 2022 Andri Yngvason
|
2019-09-07 16:51:07 +00:00
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
|
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
#include "rfb-proto.h"
|
2019-08-27 21:49:28 +00:00
|
|
|
#include "vec.h"
|
2019-08-31 23:16:55 +00:00
|
|
|
#include "type-macros.h"
|
2019-10-07 17:39:54 +00:00
|
|
|
#include "fb.h"
|
2022-11-05 12:54:58 +00:00
|
|
|
#include "desktop-layout.h"
|
2020-04-12 18:16:19 +00:00
|
|
|
#include "display.h"
|
2019-08-28 22:46:47 +00:00
|
|
|
#include "neatvnc.h"
|
2019-12-30 17:22:19 +00:00
|
|
|
#include "common.h"
|
2019-12-30 20:13:40 +00:00
|
|
|
#include "pixels.h"
|
2020-01-25 15:35:14 +00:00
|
|
|
#include "stream.h"
|
2019-12-31 10:13:21 +00:00
|
|
|
#include "config.h"
|
2020-04-01 22:53:22 +00:00
|
|
|
#include "usdt.h"
|
2021-12-09 22:48:31 +00:00
|
|
|
#include "encoder.h"
|
2021-12-12 16:05:29 +00:00
|
|
|
#include "enc-util.h"
|
2022-02-06 15:43:45 +00:00
|
|
|
#include "cursor.h"
|
2022-07-09 17:15:35 +00:00
|
|
|
#include "logging.h"
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2022-06-25 15:38:38 +00:00
|
|
|
#include <stdio.h>
|
2019-08-12 20:49:23 +00:00
|
|
|
#include <stdlib.h>
|
2023-04-07 21:24:22 +00:00
|
|
|
#include <inttypes.h>
|
2019-08-12 20:49:23 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/queue.h>
|
2020-01-19 19:00:18 +00:00
|
|
|
#include <sys/param.h>
|
2019-08-12 20:49:23 +00:00
|
|
|
#include <assert.h>
|
2020-03-16 20:09:22 +00:00
|
|
|
#include <aml.h>
|
2019-08-12 20:49:23 +00:00
|
|
|
#include <libdrm/drm_fourcc.h>
|
2019-08-25 19:10:35 +00:00
|
|
|
#include <pixman.h>
|
2019-09-15 21:51:07 +00:00
|
|
|
#include <pthread.h>
|
2020-03-16 20:09:22 +00:00
|
|
|
#include <errno.h>
|
2020-04-04 22:23:04 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
2021-03-17 07:37:06 +00:00
|
|
|
#include <sys/un.h>
|
2020-04-04 22:23:04 +00:00
|
|
|
#include <netdb.h>
|
2022-07-09 19:20:03 +00:00
|
|
|
#include <netinet/in.h>
|
2022-07-09 16:29:18 +00:00
|
|
|
#include <netinet/tcp.h>
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
#ifdef ENABLE_TLS
|
|
|
|
#include <gnutls/gnutls.h>
|
|
|
|
#endif
|
|
|
|
|
2023-08-13 12:15:32 +00:00
|
|
|
#ifdef HAVE_CRYPTO
|
|
|
|
#include "crypto.h"
|
|
|
|
#endif
|
|
|
|
|
2019-09-10 18:17:45 +00:00
|
|
|
#ifndef DRM_FORMAT_INVALID
|
|
|
|
#define DRM_FORMAT_INVALID 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef DRM_FORMAT_MOD_LINEAR
|
|
|
|
#define DRM_FORMAT_MOD_LINEAR DRM_FORMAT_MOD_NONE
|
|
|
|
#endif
|
|
|
|
|
2019-08-29 21:47:02 +00:00
|
|
|
#define DEFAULT_NAME "Neat VNC"
|
2023-08-13 12:15:32 +00:00
|
|
|
#define SECURITY_TYPES_MAX 3
|
2023-09-05 08:41:25 +00:00
|
|
|
#define RSA_AES_SERVER_KEY_LENGTH 256
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2023-08-24 10:13:52 +00:00
|
|
|
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
|
|
|
|
|
2019-09-19 18:14:24 +00:00
|
|
|
#define EXPORT __attribute__((visibility("default")))
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2020-09-26 21:40:11 +00:00
|
|
|
static int send_desktop_resize(struct nvnc_client* client, struct nvnc_fb* fb);
|
2020-11-29 18:17:30 +00:00
|
|
|
static int send_qemu_key_ext_frame(struct nvnc_client* client);
|
2021-12-24 16:29:37 +00:00
|
|
|
static enum rfb_encodings choose_frame_encoding(struct nvnc_client* client,
|
|
|
|
struct nvnc_fb*);
|
2022-04-14 16:56:41 +00:00
|
|
|
static void on_encode_frame_done(struct encoder*, struct rcbuf*, uint64_t pts);
|
2020-11-29 18:17:30 +00:00
|
|
|
static bool client_has_encoding(const struct nvnc_client* client,
|
|
|
|
enum rfb_encodings encoding);
|
2021-12-24 16:29:37 +00:00
|
|
|
static void process_fb_update_requests(struct nvnc_client* client);
|
2019-10-07 20:12:59 +00:00
|
|
|
|
2020-07-26 11:43:00 +00:00
|
|
|
#if defined(GIT_VERSION)
|
|
|
|
EXPORT const char nvnc_version[] = GIT_VERSION;
|
|
|
|
#elif defined(PROJECT_VERSION)
|
|
|
|
EXPORT const char nvnc_version[] = PROJECT_VERSION;
|
|
|
|
#else
|
|
|
|
EXPORT const char nvnc_version[] = "UNKNOWN";
|
|
|
|
#endif
|
|
|
|
|
2020-11-29 20:48:31 +00:00
|
|
|
extern const unsigned short code_map_qnum_to_linux[];
|
|
|
|
|
2022-04-14 16:56:41 +00:00
|
|
|
static uint64_t nvnc__htonll(uint64_t x)
|
|
|
|
{
|
|
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
2022-04-24 17:24:45 +00:00
|
|
|
return __builtin_bswap64(x);
|
2022-04-14 16:56:41 +00:00
|
|
|
#else
|
|
|
|
return x;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-04-07 21:24:22 +00:00
|
|
|
static uint64_t gettime_us(clockid_t clock)
|
|
|
|
{
|
|
|
|
struct timespec ts = { 0 };
|
|
|
|
clock_gettime(clock, &ts);
|
|
|
|
return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL;
|
|
|
|
}
|
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
static void client_close(struct nvnc_client* client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_INFO, "Closing client connection %p: ref %d", client,
|
|
|
|
client->ref);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2021-09-04 17:05:30 +00:00
|
|
|
nvnc_cleanup_fn cleanup = client->common.cleanup_fn;
|
|
|
|
if (cleanup)
|
|
|
|
cleanup(client->common.userdata);
|
|
|
|
|
2019-08-31 23:30:08 +00:00
|
|
|
nvnc_client_fn fn = client->cleanup_fn;
|
|
|
|
if (fn)
|
|
|
|
fn(client);
|
|
|
|
|
2021-08-29 14:41:57 +00:00
|
|
|
if (client->current_fb) {
|
|
|
|
nvnc_fb_release(client->current_fb);
|
|
|
|
nvnc_fb_unref(client->current_fb);
|
|
|
|
}
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
LIST_REMOVE(client, link);
|
2020-01-25 15:35:14 +00:00
|
|
|
stream_destroy(client->net_stream);
|
2021-12-25 15:46:58 +00:00
|
|
|
if (client->encoder) {
|
|
|
|
client->server->n_damage_clients -=
|
|
|
|
!(client->encoder->impl->flags &
|
|
|
|
ENCODER_IMPL_FLAG_IGNORES_DAMAGE);
|
2022-10-30 11:10:03 +00:00
|
|
|
client->encoder->on_done = NULL;
|
2021-12-25 15:46:58 +00:00
|
|
|
}
|
2022-10-30 10:41:20 +00:00
|
|
|
encoder_unref(client->encoder);
|
2019-10-07 20:12:59 +00:00
|
|
|
pixman_region_fini(&client->damage);
|
2020-09-23 02:37:42 +00:00
|
|
|
free(client->cut_text.buffer);
|
2019-08-12 20:49:23 +00:00
|
|
|
free(client);
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static inline void client_unref(struct nvnc_client* client)
|
2019-09-18 21:28:48 +00:00
|
|
|
{
|
2020-01-25 15:35:14 +00:00
|
|
|
assert(client->ref > 0);
|
|
|
|
|
2019-09-18 21:28:48 +00:00
|
|
|
if (--client->ref == 0)
|
|
|
|
client_close(client);
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static inline void client_ref(struct nvnc_client* client)
|
2019-09-18 21:28:48 +00:00
|
|
|
{
|
|
|
|
++client->ref;
|
|
|
|
}
|
|
|
|
|
2023-07-04 23:40:29 +00:00
|
|
|
static void deferred_client_close(void *obj)
|
|
|
|
{
|
|
|
|
client_unref(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void stop_self(void* obj)
|
|
|
|
{
|
|
|
|
aml_stop(aml_get_default(), obj);
|
|
|
|
}
|
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
static void close_after_write(void* userdata, enum stream_req_status status)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
2020-01-25 15:35:14 +00:00
|
|
|
struct nvnc_client* client = userdata;
|
2022-06-25 15:38:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_DEBUG, "close_after_write(%p): ref %d", client,
|
|
|
|
client->ref);
|
2023-07-04 23:40:29 +00:00
|
|
|
stream_close(client->net_stream);
|
|
|
|
|
|
|
|
/* This is a rather hacky way of making sure that the client object
|
|
|
|
* stays alive while the stream is processing its queue.
|
|
|
|
* TODO: Figure out some better resource management for clients
|
|
|
|
*/
|
|
|
|
struct aml_idle* idle = aml_idle_new(stop_self, client,
|
|
|
|
deferred_client_close);
|
|
|
|
aml_start(aml_get_default(), idle);
|
|
|
|
aml_unref(idle);
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int handle_unsupported_version(struct nvnc_client* client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
char buffer[256];
|
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_ERROR;
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
struct rfb_error_reason* reason = (struct rfb_error_reason*)(buffer + 1);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
static const char reason_string[] = "Unsupported version\n";
|
|
|
|
|
|
|
|
buffer[0] = 0; /* Number of security types is 0 on error */
|
|
|
|
reason->length = htonl(strlen(reason_string));
|
2020-05-29 20:18:05 +00:00
|
|
|
strcpy(reason->message, reason_string);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-01-25 15:52:44 +00:00
|
|
|
size_t len = 1 + sizeof(*reason) + strlen(reason_string);
|
|
|
|
stream_write(client->net_stream, buffer, len, close_after_write,
|
|
|
|
client);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int on_version_message(struct nvnc_client* client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
2023-08-13 12:15:32 +00:00
|
|
|
struct nvnc* server = client->server;
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
if (client->buffer_len - client->buffer_index < 12)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
char version_string[13];
|
|
|
|
memcpy(version_string, client->msg_buffer + client->buffer_index, 12);
|
|
|
|
version_string[12] = '\0';
|
|
|
|
|
|
|
|
if (strcmp(RFB_VERSION_MESSAGE, version_string) != 0)
|
|
|
|
return handle_unsupported_version(client);
|
|
|
|
|
2023-08-13 12:15:32 +00:00
|
|
|
uint8_t buf[sizeof(struct rfb_security_types_msg) +
|
|
|
|
SECURITY_TYPES_MAX] = {};
|
|
|
|
struct rfb_security_types_msg* security =
|
|
|
|
(struct rfb_security_types_msg*)buf;
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2023-08-13 12:15:32 +00:00
|
|
|
security->n = 0;
|
|
|
|
if (client->server->auth_fn) {
|
2020-01-25 15:35:14 +00:00
|
|
|
#ifdef ENABLE_TLS
|
2023-08-13 12:15:32 +00:00
|
|
|
if (server->tls_creds) {
|
|
|
|
security->types[security->n++] = RFB_SECURITY_TYPE_VENCRYPT;
|
|
|
|
}
|
2020-01-25 15:35:14 +00:00
|
|
|
#endif
|
|
|
|
|
2023-08-13 12:15:32 +00:00
|
|
|
#ifdef HAVE_CRYPTO
|
2023-08-24 10:13:52 +00:00
|
|
|
security->types[security->n++] = RFB_SECURITY_TYPE_RSA_AES;
|
2023-08-13 12:15:32 +00:00
|
|
|
security->types[security->n++] = RFB_SECURITY_TYPE_APPLE_DH;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if (security->n == 0) {
|
|
|
|
security->n = 1;
|
|
|
|
security->types[0] = RFB_SECURITY_TYPE_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream_write(client->net_stream, security, sizeof(*security) +
|
|
|
|
security->n, NULL, NULL);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_SECURITY;
|
|
|
|
return 12;
|
|
|
|
}
|
|
|
|
|
2020-01-25 15:58:43 +00:00
|
|
|
static int security_handshake_failed(struct nvnc_client* client,
|
|
|
|
const char* reason_string)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
char buffer[256];
|
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_ERROR;
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
uint8_t* result = (uint8_t*)buffer;
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
struct rfb_error_reason* reason =
|
|
|
|
(struct rfb_error_reason*)(buffer + sizeof(*result));
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
*result = htonl(RFB_SECURITY_HANDSHAKE_FAILED);
|
|
|
|
reason->length = htonl(strlen(reason_string));
|
2023-07-04 22:56:52 +00:00
|
|
|
strcpy(reason->message, reason_string);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-01-25 15:52:44 +00:00
|
|
|
size_t len = sizeof(*result) + sizeof(*reason) + strlen(reason_string);
|
|
|
|
stream_write(client->net_stream, buffer, len, close_after_write,
|
|
|
|
client);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
static int security_handshake_ok(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
uint32_t result = htonl(RFB_SECURITY_HANDSHAKE_OK);
|
2020-01-25 15:52:44 +00:00
|
|
|
return stream_write(client->net_stream, &result, sizeof(result), NULL,
|
|
|
|
NULL);
|
2020-01-25 15:35:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-22 20:50:09 +00:00
|
|
|
#ifdef ENABLE_TLS
|
2020-01-25 15:35:14 +00:00
|
|
|
static int send_byte(struct nvnc_client* client, uint8_t value)
|
|
|
|
{
|
2020-01-25 15:52:44 +00:00
|
|
|
return stream_write(client->net_stream, &value, 1, NULL, NULL);
|
2020-01-25 15:35:14 +00:00
|
|
|
}
|
|
|
|
|
2020-01-25 16:04:04 +00:00
|
|
|
static int send_byte_and_close(struct nvnc_client* client, uint8_t value)
|
|
|
|
{
|
|
|
|
return stream_write(client->net_stream, &value, 1, close_after_write,
|
|
|
|
client);
|
|
|
|
}
|
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
static int vencrypt_send_version(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct rfb_vencrypt_version_msg msg = {
|
|
|
|
.major = 0,
|
|
|
|
.minor = 2,
|
|
|
|
};
|
|
|
|
|
2020-01-25 15:52:44 +00:00
|
|
|
return stream_write(client->net_stream, &msg, sizeof(msg), NULL, NULL);
|
2020-01-25 15:35:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int on_vencrypt_version_message(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct rfb_vencrypt_version_msg* msg =
|
|
|
|
(struct rfb_vencrypt_version_msg*)&client->msg_buffer[client->buffer_index];
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (msg->major != 0 || msg->minor != 2) {
|
2020-01-25 15:58:43 +00:00
|
|
|
security_handshake_failed(client, "Unsupported VeNCrypt version");
|
2020-01-25 15:35:14 +00:00
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
send_byte(client, 0);
|
|
|
|
|
|
|
|
struct rfb_vencrypt_subtypes_msg result = { .n = 1, };
|
|
|
|
result.types[0] = htonl(RFB_VENCRYPT_X509_PLAIN);
|
|
|
|
|
2020-01-25 15:52:44 +00:00
|
|
|
stream_write(client->net_stream, &result, sizeof(result), NULL, NULL);
|
2020-01-25 15:35:14 +00:00
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_SUBTYPE;
|
|
|
|
|
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_vencrypt_subtype_message(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
uint32_t* msg = (uint32_t*)&client->msg_buffer[client->buffer_index];
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
enum rfb_vencrypt_subtype subtype = ntohl(*msg);
|
|
|
|
|
|
|
|
if (subtype != RFB_VENCRYPT_X509_PLAIN) {
|
2020-01-25 16:04:04 +00:00
|
|
|
client->state = VNC_CLIENT_STATE_ERROR;
|
|
|
|
send_byte_and_close(client, 0);
|
2020-01-25 15:35:14 +00:00
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
send_byte(client, 1);
|
|
|
|
|
|
|
|
if (stream_upgrade_to_tls(client->net_stream, client->server->tls_creds) < 0) {
|
2020-01-25 16:04:04 +00:00
|
|
|
client->state = VNC_CLIENT_STATE_ERROR;
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2020-01-25 15:35:14 +00:00
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_PLAIN_AUTH;
|
|
|
|
|
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_vencrypt_plain_auth_message(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct nvnc* server = client->server;
|
|
|
|
|
|
|
|
struct rfb_vencrypt_plain_auth_msg* msg =
|
|
|
|
(void*)(client->msg_buffer + client->buffer_index);
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint32_t ulen = ntohl(msg->username_len);
|
|
|
|
uint32_t plen = ntohl(msg->password_len);
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg) + ulen + plen)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
char username[256];
|
|
|
|
char password[256];
|
|
|
|
|
|
|
|
memcpy(username, msg->text, MIN(ulen, sizeof(username) - 1));
|
|
|
|
memcpy(password, msg->text + ulen, MIN(plen, sizeof(password) - 1));
|
|
|
|
|
|
|
|
username[MIN(ulen, sizeof(username) - 1)] = '\0';
|
|
|
|
password[MIN(plen, sizeof(password) - 1)] = '\0';
|
|
|
|
|
2022-10-31 16:08:23 +00:00
|
|
|
strncpy(client->username, username, sizeof(client->username));
|
|
|
|
client->username[sizeof(client->username) - 1] = '\0';
|
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
if (server->auth_fn(username, password, server->auth_ud)) {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_INFO, "User \"%s\" authenticated", username);
|
2020-01-25 15:35:14 +00:00
|
|
|
security_handshake_ok(client);
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT;
|
|
|
|
} else {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_INFO, "User \"%s\" rejected", username);
|
2020-01-25 15:58:43 +00:00
|
|
|
security_handshake_failed(client, "Invalid username or password");
|
2020-01-25 15:35:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return sizeof(*msg) + ulen + plen;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-08-13 12:15:32 +00:00
|
|
|
#ifdef HAVE_CRYPTO
|
|
|
|
static int apple_dh_send_public_key(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
client->apple_dh_secret = crypto_keygen();
|
|
|
|
assert(client->apple_dh_secret);
|
|
|
|
|
|
|
|
struct crypto_key* pub =
|
|
|
|
crypto_derive_public_key(client->apple_dh_secret);
|
|
|
|
assert(pub);
|
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
uint8_t mod[RSA_AES_SERVER_KEY_LENGTH] = {};
|
2023-08-13 12:15:32 +00:00
|
|
|
int mod_len = crypto_key_p(pub, mod, sizeof(mod));
|
|
|
|
assert(mod_len == sizeof(mod));
|
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
uint8_t q[RSA_AES_SERVER_KEY_LENGTH] = {};
|
2023-08-13 12:15:32 +00:00
|
|
|
int q_len = crypto_key_q(pub, q, sizeof(q));
|
|
|
|
assert(q_len == sizeof(q));
|
|
|
|
|
|
|
|
struct rfb_apple_dh_server_msg msg = {
|
|
|
|
.generator = htons(crypto_key_g(client->apple_dh_secret)),
|
|
|
|
.key_size = htons(q_len),
|
|
|
|
};
|
|
|
|
|
|
|
|
stream_write(client->net_stream, &msg, sizeof(msg), NULL, NULL);
|
|
|
|
stream_write(client->net_stream, mod, mod_len, NULL, NULL);
|
|
|
|
stream_write(client->net_stream, q, q_len, NULL, NULL);
|
|
|
|
|
|
|
|
crypto_key_del(pub);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_apple_dh_response(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct nvnc* server = client->server;
|
|
|
|
|
|
|
|
struct rfb_apple_dh_client_msg* msg =
|
|
|
|
(void*)(client->msg_buffer + client->buffer_index);
|
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
uint8_t p[RSA_AES_SERVER_KEY_LENGTH];
|
2023-08-13 12:15:32 +00:00
|
|
|
int key_len = crypto_key_p(client->apple_dh_secret, p, sizeof(p));
|
|
|
|
assert(key_len == sizeof(p));
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg) + key_len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int g = crypto_key_g(client->apple_dh_secret);
|
|
|
|
|
|
|
|
struct crypto_key* remote_key = crypto_key_new(g, p, key_len,
|
|
|
|
msg->public_key, key_len);
|
|
|
|
assert(remote_key);
|
|
|
|
|
|
|
|
struct crypto_key* shared_secret =
|
|
|
|
crypto_derive_shared_secret(client->apple_dh_secret, remote_key);
|
|
|
|
assert(shared_secret);
|
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
uint8_t shared_buf[RSA_AES_SERVER_KEY_LENGTH];
|
2023-08-13 12:15:32 +00:00
|
|
|
crypto_key_q(shared_secret, shared_buf, sizeof(shared_buf));
|
|
|
|
crypto_key_del(shared_secret);
|
|
|
|
|
|
|
|
uint8_t hash[16] = {};
|
2023-09-04 21:41:05 +00:00
|
|
|
crypto_hash_one(hash, sizeof(hash), CRYPTO_HASH_MD5, shared_buf,
|
|
|
|
sizeof(shared_buf));
|
2023-08-13 12:15:32 +00:00
|
|
|
|
|
|
|
struct crypto_cipher* cipher;
|
2023-08-15 21:41:11 +00:00
|
|
|
cipher = crypto_cipher_new(NULL, hash, CRYPTO_CIPHER_AES128_ECB);
|
2023-08-13 12:15:32 +00:00
|
|
|
assert(cipher);
|
|
|
|
|
|
|
|
char username[128] = {};
|
|
|
|
char* password = username + 64;
|
|
|
|
|
2023-09-03 22:30:10 +00:00
|
|
|
crypto_cipher_decrypt(cipher, (uint8_t*)username, NULL,
|
|
|
|
msg->encrypted_credentials, sizeof(username), NULL, 0);
|
2023-08-13 12:15:32 +00:00
|
|
|
username[63] = '\0';
|
|
|
|
username[127] = '\0';
|
|
|
|
crypto_cipher_del(cipher);
|
|
|
|
|
|
|
|
if (server->auth_fn(username, password, server->auth_ud)) {
|
|
|
|
nvnc_log(NVNC_LOG_INFO, "User \"%s\" authenticated", username);
|
|
|
|
security_handshake_ok(client);
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT;
|
|
|
|
} else {
|
|
|
|
nvnc_log(NVNC_LOG_INFO, "User \"%s\" rejected", username);
|
|
|
|
security_handshake_failed(client, "Invalid username or password");
|
|
|
|
crypto_cipher_del(cipher);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sizeof(*msg) + key_len;
|
|
|
|
}
|
2023-08-24 10:13:52 +00:00
|
|
|
|
|
|
|
static int rsa_aes_send_public_key(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct nvnc* server = client->server;
|
|
|
|
|
|
|
|
// TODO: The key should be loaded if it exists; otherwise generated and
|
|
|
|
// saved.
|
|
|
|
if (!server->rsa_priv) {
|
|
|
|
assert(!server->rsa_pub);
|
|
|
|
|
|
|
|
server->rsa_priv = crypto_rsa_priv_key_new();
|
|
|
|
server->rsa_pub = crypto_rsa_pub_key_new();
|
|
|
|
|
|
|
|
crypto_rsa_keygen(server->rsa_pub, server->rsa_priv);
|
|
|
|
}
|
|
|
|
assert(server->rsa_pub && server->rsa_priv);
|
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
char buffer[sizeof(struct rfb_rsa_aes_pub_key_msg) +
|
|
|
|
RSA_AES_SERVER_KEY_LENGTH * 2] = {};
|
2023-08-24 10:13:52 +00:00
|
|
|
struct rfb_rsa_aes_pub_key_msg* msg =
|
|
|
|
(struct rfb_rsa_aes_pub_key_msg*)buffer;
|
|
|
|
|
|
|
|
uint8_t* modulus = msg->modulus_and_exponent;
|
2023-09-05 08:41:25 +00:00
|
|
|
uint8_t* exponent = msg->modulus_and_exponent +
|
|
|
|
RSA_AES_SERVER_KEY_LENGTH;
|
2023-08-24 10:13:52 +00:00
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
msg->length = htonl(RSA_AES_SERVER_KEY_LENGTH * 8);
|
|
|
|
crypto_rsa_pub_key_modulus(server->rsa_pub, modulus,
|
|
|
|
RSA_AES_SERVER_KEY_LENGTH);
|
|
|
|
crypto_rsa_pub_key_exponent(server->rsa_pub, exponent,
|
|
|
|
RSA_AES_SERVER_KEY_LENGTH);
|
2023-08-24 10:13:52 +00:00
|
|
|
|
|
|
|
stream_write(client->net_stream, buffer, sizeof(buffer), NULL, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rsa_aes_send_challenge(struct nvnc_client* client,
|
|
|
|
struct crypto_rsa_pub_key* pub)
|
|
|
|
{
|
|
|
|
crypto_random(client->rsa.challenge, sizeof(client->rsa.challenge));
|
|
|
|
|
|
|
|
uint8_t buffer[1024];
|
|
|
|
struct rfb_rsa_aes_challenge_msg *msg =
|
|
|
|
(struct rfb_rsa_aes_challenge_msg*)buffer;
|
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
ssize_t len = crypto_rsa_encrypt(pub, msg->challenge,
|
|
|
|
RSA_AES_SERVER_KEY_LENGTH, client->rsa.challenge,
|
|
|
|
sizeof(client->rsa.challenge));
|
2023-08-24 10:13:52 +00:00
|
|
|
msg->length = htons(len);
|
|
|
|
|
|
|
|
nvnc_trace("Challenge length is %zd", len);
|
|
|
|
|
|
|
|
stream_write(client->net_stream, buffer, sizeof(*msg) + len, NULL, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_rsa_aes_public_key(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct rfb_rsa_aes_pub_key_msg* msg =
|
|
|
|
(void*)(client->msg_buffer + client->buffer_index);
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint32_t bit_length = ntohl(msg->length);
|
|
|
|
size_t byte_length = UDIV_UP(bit_length, 8);
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index <
|
|
|
|
sizeof(*msg) + byte_length * 2)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nvnc_trace("Got public key with bit size %d", bit_length);
|
|
|
|
|
|
|
|
const uint8_t* modulus = msg->modulus_and_exponent;
|
|
|
|
const uint8_t* exponent = msg->modulus_and_exponent + byte_length;
|
|
|
|
|
|
|
|
crypto_dump_base16("Got public key modulus", modulus, byte_length);
|
|
|
|
crypto_dump_base16("Got public key exponent", exponent, byte_length);
|
|
|
|
|
|
|
|
client->rsa.pub =
|
|
|
|
crypto_rsa_pub_key_import(modulus, exponent, byte_length);
|
|
|
|
assert(client->rsa.pub);
|
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CHALLENGE;
|
|
|
|
rsa_aes_send_challenge(client, client->rsa.pub);
|
|
|
|
|
|
|
|
return sizeof(*msg) + byte_length * 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_rsa_aes_challenge(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct rfb_rsa_aes_challenge_msg* msg =
|
|
|
|
(void*)(client->msg_buffer + client->buffer_index);
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint16_t length = ntohs(msg->length);
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg) + length)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
crypto_dump_base16("client buffer", client->msg_buffer +
|
|
|
|
client->buffer_index, client->buffer_len -
|
|
|
|
client->buffer_index);
|
|
|
|
|
|
|
|
struct nvnc* server = client->server;
|
|
|
|
|
|
|
|
nvnc_trace("Encrypted challenge has length: %d", length);
|
|
|
|
|
|
|
|
uint8_t client_random[16] = {};
|
|
|
|
ssize_t len = crypto_rsa_decrypt(server->rsa_priv, client_random,
|
|
|
|
sizeof(client_random), msg->challenge, length);
|
|
|
|
if (len < 0) {
|
|
|
|
nvnc_log(NVNC_LOG_ERROR, "Failed to decrypt client's challenge");
|
|
|
|
client->state = VNC_CLIENT_STATE_ERROR;
|
|
|
|
nvnc_client_close(client);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
nvnc_trace("Decrypted challenge has length: %zd", len);
|
|
|
|
crypto_dump_base16("Got challenge", client_random, 16);
|
|
|
|
|
|
|
|
// ClientSessionKey = the first 16 bytes of SHA1(ServerRandom || ClientRandom)
|
2023-09-04 21:41:05 +00:00
|
|
|
uint8_t client_session_key[16];
|
|
|
|
crypto_hash_many(client_session_key, sizeof(client_session_key),
|
|
|
|
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) {
|
|
|
|
{ client->rsa.challenge, sizeof(client->rsa.challenge) },
|
|
|
|
{ client_random, sizeof(client_random) },
|
|
|
|
{}
|
|
|
|
});
|
2023-08-24 10:13:52 +00:00
|
|
|
|
|
|
|
// ServerSessionKey = the first 16 bytes of SHA1(ClientRandom || ServerRandom)
|
2023-09-04 21:41:05 +00:00
|
|
|
uint8_t server_session_key[16];
|
|
|
|
crypto_hash_many(server_session_key, sizeof(server_session_key),
|
|
|
|
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) {
|
|
|
|
{ client_random, sizeof(client_random) },
|
|
|
|
{ client->rsa.challenge, sizeof(client->rsa.challenge) },
|
|
|
|
{}
|
|
|
|
});
|
2023-08-24 10:13:52 +00:00
|
|
|
|
|
|
|
crypto_dump_base64("Client session key", client_session_key,
|
|
|
|
sizeof(client_session_key));
|
|
|
|
crypto_dump_base64("Server session key", server_session_key,
|
|
|
|
sizeof(server_session_key));
|
|
|
|
|
2023-09-03 22:30:10 +00:00
|
|
|
stream_upgrade_to_rsa_eas(client->net_stream, server_session_key,
|
|
|
|
client_session_key);
|
2023-08-24 10:13:52 +00:00
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
uint8_t server_modulus[RSA_AES_SERVER_KEY_LENGTH];
|
|
|
|
uint8_t server_exponent[RSA_AES_SERVER_KEY_LENGTH];
|
|
|
|
crypto_rsa_pub_key_modulus(server->rsa_pub, server_modulus,
|
|
|
|
RSA_AES_SERVER_KEY_LENGTH);
|
|
|
|
crypto_rsa_pub_key_exponent(server->rsa_pub, server_exponent,
|
|
|
|
RSA_AES_SERVER_KEY_LENGTH);
|
2023-08-24 10:13:52 +00:00
|
|
|
|
|
|
|
size_t client_key_len = crypto_rsa_pub_key_length(client->rsa.pub);
|
|
|
|
uint8_t* client_modulus = malloc(client_key_len * 2);
|
|
|
|
uint8_t* client_exponent = client_modulus + client_key_len;
|
|
|
|
|
|
|
|
crypto_rsa_pub_key_modulus(client->rsa.pub, client_modulus,
|
|
|
|
client_key_len);
|
|
|
|
crypto_rsa_pub_key_exponent(client->rsa.pub, client_exponent,
|
|
|
|
client_key_len);
|
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
uint32_t server_key_len_be = htonl(RSA_AES_SERVER_KEY_LENGTH * 8);
|
2023-08-24 10:13:52 +00:00
|
|
|
uint32_t client_key_len_be = htonl(client_key_len * 8);
|
|
|
|
|
|
|
|
uint8_t server_hash[20] = {};
|
2023-09-04 21:41:05 +00:00
|
|
|
crypto_hash_many(server_hash, sizeof(server_hash),
|
|
|
|
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) {
|
|
|
|
{ (uint8_t*)&server_key_len_be, 4 },
|
2023-09-05 08:41:25 +00:00
|
|
|
{ server_modulus, RSA_AES_SERVER_KEY_LENGTH },
|
|
|
|
{ server_exponent, RSA_AES_SERVER_KEY_LENGTH },
|
2023-09-04 21:41:05 +00:00
|
|
|
{ (uint8_t*)&client_key_len_be, 4 },
|
|
|
|
{ client_modulus, client_key_len },
|
|
|
|
{ client_exponent, client_key_len },
|
|
|
|
{}
|
|
|
|
});
|
2023-08-24 10:13:52 +00:00
|
|
|
|
|
|
|
free(client_modulus);
|
|
|
|
|
|
|
|
stream_write(client->net_stream, server_hash, 20, NULL, NULL);
|
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CLIENT_HASH;
|
|
|
|
done:
|
|
|
|
return sizeof(*msg) + length;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_rsa_aes_client_hash(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
const char* msg = (void*)(client->msg_buffer + client->buffer_index);
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < 20)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
struct nvnc* server = client->server;
|
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
uint8_t server_modulus[RSA_AES_SERVER_KEY_LENGTH];
|
|
|
|
uint8_t server_exponent[RSA_AES_SERVER_KEY_LENGTH];
|
|
|
|
crypto_rsa_pub_key_modulus(server->rsa_pub, server_modulus,
|
|
|
|
RSA_AES_SERVER_KEY_LENGTH);
|
|
|
|
crypto_rsa_pub_key_exponent(server->rsa_pub, server_exponent,
|
|
|
|
RSA_AES_SERVER_KEY_LENGTH);
|
2023-08-24 10:13:52 +00:00
|
|
|
|
|
|
|
size_t client_key_len = crypto_rsa_pub_key_length(client->rsa.pub);
|
|
|
|
uint8_t* client_modulus = malloc(client_key_len * 2);
|
|
|
|
uint8_t* client_exponent = client_modulus + client_key_len;
|
|
|
|
|
|
|
|
crypto_rsa_pub_key_modulus(client->rsa.pub, client_modulus,
|
|
|
|
client_key_len);
|
|
|
|
crypto_rsa_pub_key_exponent(client->rsa.pub, client_exponent,
|
|
|
|
client_key_len);
|
|
|
|
|
2023-09-05 08:41:25 +00:00
|
|
|
uint32_t server_key_len_be = htonl(RSA_AES_SERVER_KEY_LENGTH * 8);
|
2023-08-24 10:13:52 +00:00
|
|
|
uint32_t client_key_len_be = htonl(client_key_len * 8);
|
|
|
|
|
|
|
|
uint8_t client_hash[20] = {};
|
2023-09-04 21:41:05 +00:00
|
|
|
crypto_hash_many(client_hash, sizeof(client_hash),
|
|
|
|
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) {
|
|
|
|
{ (uint8_t*)&client_key_len_be, 4 },
|
|
|
|
{ client_modulus, client_key_len },
|
|
|
|
{ client_exponent, client_key_len },
|
|
|
|
{ (uint8_t*)&server_key_len_be, 4 },
|
2023-09-05 08:41:25 +00:00
|
|
|
{ server_modulus, RSA_AES_SERVER_KEY_LENGTH },
|
|
|
|
{ server_exponent, RSA_AES_SERVER_KEY_LENGTH },
|
2023-09-04 21:41:05 +00:00
|
|
|
{}
|
|
|
|
});
|
2023-08-24 10:13:52 +00:00
|
|
|
|
|
|
|
free(client_modulus);
|
|
|
|
|
|
|
|
crypto_dump_base16("Client hash", client_hash, 20);
|
|
|
|
|
|
|
|
if (memcmp(msg, client_hash, 20) != 0) {
|
|
|
|
nvnc_log(NVNC_LOG_INFO, "Client hash mismatch");
|
|
|
|
// TODO: Close the connection or something
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Read this from config
|
|
|
|
uint8_t subtype = RFB_RSA_AES_CRED_SUBTYPE_USER_AND_PASS;
|
|
|
|
stream_write(client->net_stream, &subtype, 1, NULL, NULL);
|
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CREDENTIALS;
|
|
|
|
return 20;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_rsa_aes_credentials(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
const uint8_t* msg = (void*)(client->msg_buffer + client->buffer_index);
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < 2)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
size_t username_len = msg[0];
|
|
|
|
if (client->buffer_len - client->buffer_index < 2 + username_len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
size_t password_len = msg[1 + username_len];
|
|
|
|
if (client->buffer_len - client->buffer_index < 2 + username_len +
|
|
|
|
password_len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
struct nvnc* server = client->server;
|
|
|
|
|
|
|
|
char username[256];
|
|
|
|
char password[256];
|
|
|
|
|
|
|
|
strlcpy(username, (const char*)(msg + 1), username_len + 1);
|
|
|
|
strlcpy(password, (const char*)(msg + 2 + username_len),
|
|
|
|
password_len + 1);
|
|
|
|
|
|
|
|
if (server->auth_fn(username, password, server->auth_ud)) {
|
|
|
|
nvnc_log(NVNC_LOG_INFO, "User \"%s\" authenticated", username);
|
|
|
|
security_handshake_ok(client);
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT;
|
|
|
|
} else {
|
|
|
|
nvnc_log(NVNC_LOG_INFO, "User \"%s\" rejected", username);
|
|
|
|
security_handshake_failed(client, "Invalid username or password");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 2 + username_len + password_len;
|
|
|
|
}
|
|
|
|
|
2023-08-13 12:15:32 +00:00
|
|
|
#endif // HAVE_CRYPTO
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int on_security_message(struct nvnc_client* client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
if (client->buffer_len - client->buffer_index < 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint8_t type = client->msg_buffer[client->buffer_index];
|
2023-08-24 10:13:52 +00:00
|
|
|
nvnc_log(NVNC_LOG_DEBUG, "Client chose security type: %d", type);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
switch (type) {
|
|
|
|
case RFB_SECURITY_TYPE_NONE:
|
|
|
|
security_handshake_ok(client);
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT;
|
|
|
|
break;
|
|
|
|
#ifdef ENABLE_TLS
|
|
|
|
case RFB_SECURITY_TYPE_VENCRYPT:
|
|
|
|
vencrypt_send_version(client);
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_VERSION;
|
|
|
|
break;
|
2023-08-13 12:15:32 +00:00
|
|
|
#endif
|
|
|
|
#ifdef HAVE_CRYPTO
|
|
|
|
case RFB_SECURITY_TYPE_APPLE_DH:
|
|
|
|
apple_dh_send_public_key(client);
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_APPLE_DH_RESPONSE;
|
|
|
|
break;
|
2023-08-24 10:13:52 +00:00
|
|
|
case RFB_SECURITY_TYPE_RSA_AES:
|
|
|
|
rsa_aes_send_public_key(client);
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_PUBLIC_KEY;
|
|
|
|
break;
|
2020-01-25 15:35:14 +00:00
|
|
|
#endif
|
|
|
|
default:
|
2020-01-25 15:58:43 +00:00
|
|
|
security_handshake_failed(client, "Unsupported security type");
|
2020-01-25 15:35:14 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
return sizeof(type);
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static void disconnect_all_other_clients(struct nvnc_client* client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
struct nvnc_client* node;
|
2020-01-25 15:35:14 +00:00
|
|
|
struct nvnc_client* tmp;
|
|
|
|
|
|
|
|
LIST_FOREACH_SAFE (node, &client->server->clients, link, tmp)
|
|
|
|
if (node != client) {
|
2022-06-25 15:38:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_DEBUG,
|
|
|
|
"disconnect other client %p (ref %d)",
|
|
|
|
node, node->ref);
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(node);
|
2020-01-25 15:35:14 +00:00
|
|
|
}
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static void send_server_init_message(struct nvnc_client* client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
struct nvnc* server = client->server;
|
2020-04-12 18:16:19 +00:00
|
|
|
struct nvnc_display* display = server->display;
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-04-12 16:08:33 +00:00
|
|
|
size_t name_len = strlen(server->name);
|
2019-08-12 20:49:23 +00:00
|
|
|
size_t size = sizeof(struct rfb_server_init_msg) + name_len;
|
|
|
|
|
2020-04-12 18:16:19 +00:00
|
|
|
if (!display) {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_WARNING, "Tried to send init message, but no display has been added");
|
2020-04-12 16:08:33 +00:00
|
|
|
goto close;
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
|
2020-04-12 18:16:19 +00:00
|
|
|
if (!display->buffer) {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_WARNING, "Tried to send init message, but no framebuffers have been set");
|
2020-04-12 18:16:19 +00:00
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t width = nvnc_fb_get_width(display->buffer);
|
|
|
|
uint16_t height = nvnc_fb_get_height(display->buffer);
|
|
|
|
uint32_t fourcc = nvnc_fb_get_fourcc_format(display->buffer);
|
2019-08-25 19:10:35 +00:00
|
|
|
|
2020-04-12 16:08:33 +00:00
|
|
|
struct rfb_server_init_msg* msg = calloc(1, size);
|
|
|
|
if (!msg)
|
|
|
|
goto close;
|
|
|
|
|
|
|
|
msg->width = htons(width);
|
|
|
|
msg->height = htons(height);
|
|
|
|
msg->name_length = htonl(name_len);
|
|
|
|
memcpy(msg->name_string, server->name, name_len);
|
|
|
|
|
|
|
|
int rc = rfb_pixfmt_from_fourcc(&msg->pixel_format, fourcc);
|
|
|
|
if (rc < 0)
|
|
|
|
goto pixfmt_failure;
|
2019-09-08 16:48:05 +00:00
|
|
|
|
2019-08-25 19:10:35 +00:00
|
|
|
msg->pixel_format.red_max = htons(msg->pixel_format.red_max);
|
|
|
|
msg->pixel_format.green_max = htons(msg->pixel_format.green_max);
|
|
|
|
msg->pixel_format.blue_max = htons(msg->pixel_format.blue_max);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
struct rcbuf* payload = rcbuf_new(msg, size);
|
2020-01-25 15:52:44 +00:00
|
|
|
stream_send(client->net_stream, payload, NULL, NULL);
|
2020-04-12 16:08:33 +00:00
|
|
|
|
2020-07-19 11:54:39 +00:00
|
|
|
client->known_width = width;
|
|
|
|
client->known_height = height;
|
2020-04-12 16:08:33 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
pixfmt_failure:
|
|
|
|
free(msg);
|
|
|
|
close:
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int on_init_message(struct nvnc_client* client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
if (client->buffer_len - client->buffer_index < 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint8_t shared_flag = client->msg_buffer[client->buffer_index];
|
2019-09-06 20:07:09 +00:00
|
|
|
if (!shared_flag)
|
2019-08-12 20:49:23 +00:00
|
|
|
disconnect_all_other_clients(client);
|
|
|
|
|
|
|
|
send_server_init_message(client);
|
|
|
|
|
2019-08-31 23:30:08 +00:00
|
|
|
nvnc_client_fn fn = client->server->new_client_fn;
|
|
|
|
if (fn)
|
|
|
|
fn(client);
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
client->state = VNC_CLIENT_STATE_READY;
|
|
|
|
return sizeof(shared_flag);
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int on_client_set_pixel_format(struct nvnc_client* client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
if (client->buffer_len - client->buffer_index <
|
|
|
|
4 + sizeof(struct rfb_pixel_format))
|
2019-08-12 20:49:23 +00:00
|
|
|
return 0;
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
struct rfb_pixel_format* fmt =
|
|
|
|
(struct rfb_pixel_format*)(client->msg_buffer +
|
|
|
|
client->buffer_index + 4);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
if (!fmt->true_colour_flag) {
|
|
|
|
/* We don't really know what to do with color maps right now */
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-25 19:10:35 +00:00
|
|
|
fmt->red_max = ntohs(fmt->red_max);
|
|
|
|
fmt->green_max = ntohs(fmt->green_max);
|
|
|
|
fmt->blue_max = ntohs(fmt->blue_max);
|
|
|
|
|
|
|
|
memcpy(&client->pixfmt, fmt, sizeof(client->pixfmt));
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-01-24 22:28:37 +00:00
|
|
|
client->has_pixfmt = true;
|
2019-08-25 19:10:35 +00:00
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
return 4 + sizeof(struct rfb_pixel_format);
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int on_client_set_encodings(struct nvnc_client* client)
|
2019-08-12 23:33:06 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
struct rfb_client_set_encodings_msg* msg =
|
|
|
|
(struct rfb_client_set_encodings_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2020-01-19 19:16:27 +00:00
|
|
|
size_t n_encodings = MIN(MAX_ENCODINGS, ntohs(msg->n_encodings));
|
|
|
|
size_t n = 0;
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2019-12-23 09:33:29 +00:00
|
|
|
if (client->buffer_len - client->buffer_index <
|
|
|
|
sizeof(*msg) + n_encodings * 4)
|
|
|
|
return 0;
|
|
|
|
|
2022-07-10 12:41:18 +00:00
|
|
|
client->quality = 10;
|
|
|
|
|
2020-01-19 19:16:27 +00:00
|
|
|
for (size_t i = 0; i < n_encodings; ++i) {
|
2019-10-08 18:12:57 +00:00
|
|
|
enum rfb_encodings encoding = htonl(msg->encodings[i]);
|
|
|
|
|
|
|
|
switch (encoding) {
|
2019-09-19 18:14:24 +00:00
|
|
|
case RFB_ENCODING_RAW:
|
|
|
|
case RFB_ENCODING_COPYRECT:
|
|
|
|
case RFB_ENCODING_RRE:
|
|
|
|
case RFB_ENCODING_HEXTILE:
|
2019-12-30 09:59:51 +00:00
|
|
|
case RFB_ENCODING_TIGHT:
|
2019-09-19 18:14:24 +00:00
|
|
|
case RFB_ENCODING_TRLE:
|
|
|
|
case RFB_ENCODING_ZRLE:
|
2021-12-24 16:29:37 +00:00
|
|
|
case RFB_ENCODING_OPEN_H264:
|
2019-09-19 18:14:24 +00:00
|
|
|
case RFB_ENCODING_CURSOR:
|
|
|
|
case RFB_ENCODING_DESKTOPSIZE:
|
2022-11-05 12:54:58 +00:00
|
|
|
case RFB_ENCODING_EXTENDEDDESKTOPSIZE:
|
2020-11-29 17:35:06 +00:00
|
|
|
case RFB_ENCODING_QEMU_EXT_KEY_EVENT:
|
2022-04-14 16:56:41 +00:00
|
|
|
case RFB_ENCODING_PTS:
|
2023-04-07 21:24:22 +00:00
|
|
|
case RFB_ENCODING_NTP:
|
2019-10-08 18:12:57 +00:00
|
|
|
client->encodings[n++] = encoding;
|
2019-08-12 23:33:06 +00:00
|
|
|
}
|
2022-07-10 12:41:18 +00:00
|
|
|
|
|
|
|
if (RFB_ENCODING_JPEG_LOWQ <= encoding &&
|
|
|
|
encoding <= RFB_ENCODING_JPEG_HIGHQ)
|
|
|
|
client->quality = encoding - RFB_ENCODING_JPEG_LOWQ;
|
2019-10-08 18:12:57 +00:00
|
|
|
}
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2019-09-19 18:14:24 +00:00
|
|
|
client->n_encodings = n;
|
2019-08-12 23:33:06 +00:00
|
|
|
|
|
|
|
return sizeof(*msg) + 4 * n_encodings;
|
|
|
|
}
|
|
|
|
|
2022-02-06 15:43:45 +00:00
|
|
|
static void send_cursor_update(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct nvnc* server = client->server;
|
|
|
|
|
|
|
|
struct vec payload;
|
|
|
|
vec_init(&payload, 4096);
|
|
|
|
|
|
|
|
struct rfb_server_fb_update_msg head = {
|
|
|
|
.type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE,
|
|
|
|
.n_rects = htons(1),
|
|
|
|
};
|
|
|
|
|
|
|
|
vec_append(&payload, &head, sizeof(head));
|
|
|
|
|
|
|
|
int rc = cursor_encode(&payload, &client->pixfmt, server->cursor.buffer,
|
2022-02-19 23:06:15 +00:00
|
|
|
server->cursor.width, server->cursor.height,
|
|
|
|
server->cursor.hotspot_x, server->cursor.hotspot_y);
|
2022-02-06 15:43:45 +00:00
|
|
|
if (rc < 0) {
|
2022-06-25 15:38:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_ERROR, "Failed to send cursor to client");
|
2022-02-06 15:43:45 +00:00
|
|
|
vec_destroy(&payload);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
client->cursor_seq = server->cursor_seq;
|
|
|
|
|
|
|
|
stream_send(client->net_stream, rcbuf_new(payload.data, payload.len),
|
|
|
|
NULL, NULL);
|
|
|
|
}
|
|
|
|
|
2022-04-14 16:56:41 +00:00
|
|
|
static bool will_send_pts(const struct nvnc_client* client, uint64_t pts)
|
|
|
|
{
|
|
|
|
return pts != NVNC_NO_PTS && client_has_encoding(client, RFB_ENCODING_PTS);
|
|
|
|
}
|
|
|
|
|
2022-11-05 13:20:41 +00:00
|
|
|
static int send_pts_rect(struct nvnc_client* client, uint64_t pts)
|
2022-04-14 16:56:41 +00:00
|
|
|
{
|
|
|
|
if (!will_send_pts(client, pts))
|
2022-11-05 13:20:41 +00:00
|
|
|
return 0;
|
2022-04-14 16:56:41 +00:00
|
|
|
|
|
|
|
uint8_t buf[sizeof(struct rfb_server_fb_rect) + 8] = { 0 };
|
|
|
|
struct rfb_server_fb_rect* head = (struct rfb_server_fb_rect*)buf;
|
|
|
|
head->encoding = htonl(RFB_ENCODING_PTS);
|
|
|
|
uint64_t* msg_pts = (uint64_t*)&buf[sizeof(struct rfb_server_fb_rect)];
|
|
|
|
*msg_pts = nvnc__htonll(pts);
|
|
|
|
|
2022-11-05 13:20:41 +00:00
|
|
|
return stream_write(client->net_stream, buf, sizeof(buf), NULL, NULL);
|
2022-04-14 16:56:41 +00:00
|
|
|
}
|
|
|
|
|
2022-07-09 16:28:09 +00:00
|
|
|
static const char* encoding_to_string(enum rfb_encodings encoding)
|
|
|
|
{
|
|
|
|
switch (encoding) {
|
|
|
|
case RFB_ENCODING_RAW: return "raw";
|
|
|
|
case RFB_ENCODING_TIGHT: return "tight";
|
|
|
|
case RFB_ENCODING_ZRLE: return "zrle";
|
|
|
|
case RFB_ENCODING_OPEN_H264: return "open-h264";
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static void process_fb_update_requests(struct nvnc_client* client)
|
2019-10-07 20:12:59 +00:00
|
|
|
{
|
2020-09-26 21:40:11 +00:00
|
|
|
struct nvnc* server = client->server;
|
|
|
|
|
|
|
|
if (!server->display || !server->display->buffer)
|
2019-10-07 20:12:59 +00:00
|
|
|
return;
|
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
if (client->net_stream->state == STREAM_STATE_CLOSED)
|
2019-10-07 20:12:59 +00:00
|
|
|
return;
|
|
|
|
|
2019-10-12 17:29:08 +00:00
|
|
|
if (client->is_updating || client->n_pending_requests == 0)
|
2019-10-07 20:12:59 +00:00
|
|
|
return;
|
|
|
|
|
2020-09-26 21:40:11 +00:00
|
|
|
struct nvnc_fb* fb = client->server->display->buffer;
|
|
|
|
assert(fb);
|
|
|
|
|
2020-09-26 22:17:02 +00:00
|
|
|
if (!client->has_pixfmt) {
|
|
|
|
rfb_pixfmt_from_fourcc(&client->pixfmt, fb->fourcc_format);
|
|
|
|
client->has_pixfmt = true;
|
|
|
|
}
|
|
|
|
|
2020-09-26 21:40:11 +00:00
|
|
|
if (fb->width != client->known_width
|
2020-11-29 18:29:18 +00:00
|
|
|
|| fb->height != client->known_height) {
|
2020-09-26 21:40:11 +00:00
|
|
|
send_desktop_resize(client, fb);
|
|
|
|
|
2020-11-29 18:29:18 +00:00
|
|
|
if (--client->n_pending_requests <= 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-29 18:17:30 +00:00
|
|
|
if (server->key_code_fn && !client->is_qemu_key_ext_notified
|
|
|
|
&& client_has_encoding(client, RFB_ENCODING_QEMU_EXT_KEY_EVENT)) {
|
|
|
|
send_qemu_key_ext_frame(client);
|
|
|
|
client->is_qemu_key_ext_notified = true;
|
2020-11-29 18:29:18 +00:00
|
|
|
|
|
|
|
if (--client->n_pending_requests <= 0)
|
|
|
|
return;
|
2020-11-29 18:17:30 +00:00
|
|
|
}
|
|
|
|
|
2022-02-06 15:43:45 +00:00
|
|
|
if (server->cursor_seq != client->cursor_seq
|
|
|
|
&& client_has_encoding(client, RFB_ENCODING_CURSOR)) {
|
|
|
|
send_cursor_update(client);
|
|
|
|
|
|
|
|
if (--client->n_pending_requests <= 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pixman_region_not_empty(&client->damage))
|
|
|
|
return;
|
|
|
|
|
2020-09-26 21:40:11 +00:00
|
|
|
DTRACE_PROBE1(neatvnc, update_fb_start, client);
|
|
|
|
|
2021-12-24 16:29:37 +00:00
|
|
|
enum rfb_encodings encoding = choose_frame_encoding(client, fb);
|
2021-12-09 22:48:31 +00:00
|
|
|
if (!client->encoder || encoding != encoder_get_type(client->encoder)) {
|
|
|
|
int width = server->display->buffer->width;
|
|
|
|
int height = server->display->buffer->height;
|
2021-12-25 15:46:58 +00:00
|
|
|
if (client->encoder) {
|
|
|
|
server->n_damage_clients -=
|
|
|
|
!(client->encoder->impl->flags &
|
|
|
|
ENCODER_IMPL_FLAG_IGNORES_DAMAGE);
|
2022-10-30 11:10:03 +00:00
|
|
|
client->encoder->on_done = NULL;
|
2021-12-25 15:46:58 +00:00
|
|
|
}
|
2022-10-30 10:41:20 +00:00
|
|
|
encoder_unref(client->encoder);
|
2021-12-09 22:48:31 +00:00
|
|
|
client->encoder = encoder_new(encoding, width, height);
|
|
|
|
if (!client->encoder) {
|
2022-06-25 15:38:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_ERROR, "Failed to allocate new encoder");
|
2021-12-24 16:29:37 +00:00
|
|
|
return;
|
2021-12-09 22:48:31 +00:00
|
|
|
}
|
2020-09-27 12:37:00 +00:00
|
|
|
|
2021-12-25 15:46:58 +00:00
|
|
|
server->n_damage_clients +=
|
|
|
|
!(client->encoder->impl->flags &
|
|
|
|
ENCODER_IMPL_FLAG_IGNORES_DAMAGE);
|
2022-07-09 16:28:09 +00:00
|
|
|
|
|
|
|
nvnc_log(NVNC_LOG_INFO, "Choosing %s encoding for client %p",
|
|
|
|
encoding_to_string(encoding), client);
|
2021-12-24 16:29:37 +00:00
|
|
|
}
|
2020-09-27 12:37:00 +00:00
|
|
|
|
2022-10-30 17:28:13 +00:00
|
|
|
/* The client's damage is exchanged for an empty one */
|
|
|
|
struct pixman_region16 damage = client->damage;
|
|
|
|
pixman_region_init(&client->damage);
|
2020-09-27 12:37:00 +00:00
|
|
|
|
2022-10-30 17:28:13 +00:00
|
|
|
client->is_updating = true;
|
|
|
|
client->current_fb = fb;
|
|
|
|
nvnc_fb_hold(fb);
|
|
|
|
nvnc_fb_ref(fb);
|
2022-06-26 13:54:15 +00:00
|
|
|
|
2022-10-30 17:28:13 +00:00
|
|
|
client_ref(client);
|
2022-04-14 16:56:41 +00:00
|
|
|
|
2022-10-30 17:28:13 +00:00
|
|
|
encoder_set_quality(client->encoder, client->quality);
|
|
|
|
encoder_set_output_format(client->encoder, &client->pixfmt);
|
2021-12-24 16:29:37 +00:00
|
|
|
|
2022-10-30 17:28:13 +00:00
|
|
|
client->encoder->on_done = on_encode_frame_done;
|
|
|
|
client->encoder->userdata = client;
|
2022-04-14 16:56:41 +00:00
|
|
|
|
2022-10-30 17:28:13 +00:00
|
|
|
DTRACE_PROBE2(neatvnc, process_fb_update_requests__encode,
|
|
|
|
client, fb->pts);
|
2020-09-27 12:37:00 +00:00
|
|
|
|
2022-10-30 17:28:13 +00:00
|
|
|
if (encoder_encode(client->encoder, fb, &damage) >= 0) {
|
|
|
|
--client->n_pending_requests;
|
2021-12-24 16:29:37 +00:00
|
|
|
} else {
|
2022-10-30 17:28:13 +00:00
|
|
|
nvnc_log(NVNC_LOG_ERROR, "Failed to encode current frame");
|
|
|
|
client_unref(client);
|
|
|
|
client->is_updating = false;
|
|
|
|
assert(client->current_fb);
|
|
|
|
nvnc_fb_release(client->current_fb);
|
|
|
|
nvnc_fb_unref(client->current_fb);
|
|
|
|
client->current_fb = NULL;
|
2020-09-27 12:37:00 +00:00
|
|
|
}
|
2022-10-30 17:28:13 +00:00
|
|
|
|
|
|
|
pixman_region_fini(&damage);
|
2019-10-07 20:12:59 +00:00
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int on_client_fb_update_request(struct nvnc_client* client)
|
2019-08-12 23:33:06 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
struct nvnc* server = client->server;
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
struct rfb_client_fb_update_req_msg* msg =
|
|
|
|
(struct rfb_client_fb_update_req_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2019-12-23 09:33:29 +00:00
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg))
|
|
|
|
return 0;
|
|
|
|
|
2019-08-12 23:33:06 +00:00
|
|
|
int incremental = msg->incremental;
|
|
|
|
int x = ntohs(msg->x);
|
|
|
|
int y = ntohs(msg->y);
|
|
|
|
int width = ntohs(msg->width);
|
|
|
|
int height = ntohs(msg->height);
|
|
|
|
|
2019-10-07 20:12:59 +00:00
|
|
|
client->n_pending_requests++;
|
|
|
|
|
|
|
|
/* Note: The region sent from the client is ignored for incremental
|
|
|
|
* updates. This avoids superfluous complexity.
|
|
|
|
*/
|
2021-12-24 16:29:37 +00:00
|
|
|
if (!incremental) {
|
2019-10-20 22:13:51 +00:00
|
|
|
pixman_region_union_rect(&client->damage, &client->damage, x, y,
|
|
|
|
width, height);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2021-12-24 16:29:37 +00:00
|
|
|
if (client->encoder)
|
|
|
|
encoder_request_key_frame(client->encoder);
|
|
|
|
}
|
|
|
|
|
2020-04-01 22:53:22 +00:00
|
|
|
DTRACE_PROBE1(neatvnc, update_fb_request, client);
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
nvnc_fb_req_fn fn = server->fb_req_fn;
|
|
|
|
if (fn)
|
2019-08-31 23:16:55 +00:00
|
|
|
fn(client, incremental, x, y, width, height);
|
2019-08-27 21:49:28 +00:00
|
|
|
|
2022-11-05 12:54:58 +00:00
|
|
|
if (!incremental &&
|
|
|
|
client_has_encoding(client, RFB_ENCODING_EXTENDEDDESKTOPSIZE)) {
|
|
|
|
client->known_width = 0;
|
|
|
|
client->known_height = 0;
|
|
|
|
}
|
|
|
|
|
2021-09-04 16:46:48 +00:00
|
|
|
process_fb_update_requests(client);
|
|
|
|
|
2019-08-12 23:33:06 +00:00
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int on_client_key_event(struct nvnc_client* client)
|
2019-08-12 23:33:06 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
struct nvnc* server = client->server;
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
struct rfb_client_key_event_msg* msg =
|
|
|
|
(struct rfb_client_key_event_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2019-12-23 09:33:29 +00:00
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg))
|
|
|
|
return 0;
|
|
|
|
|
2019-08-12 23:33:06 +00:00
|
|
|
int down_flag = msg->down_flag;
|
2019-08-28 22:46:47 +00:00
|
|
|
uint32_t keysym = ntohl(msg->key);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
nvnc_key_fn fn = server->key_fn;
|
|
|
|
if (fn)
|
2019-08-31 23:16:55 +00:00
|
|
|
fn(client, keysym, !!down_flag);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
2020-11-29 17:35:06 +00:00
|
|
|
static int on_client_qemu_key_event(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct nvnc* server = client->server;
|
|
|
|
|
|
|
|
struct rfb_client_qemu_key_event_msg* msg =
|
|
|
|
(struct rfb_client_qemu_key_event_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int down_flag = msg->down_flag;
|
2020-11-29 20:48:31 +00:00
|
|
|
uint32_t xt_keycode = ntohl(msg->keycode);
|
|
|
|
|
|
|
|
uint32_t evdev_keycode = code_map_qnum_to_linux[xt_keycode];
|
|
|
|
if (!evdev_keycode)
|
|
|
|
evdev_keycode = xt_keycode;
|
2020-11-29 17:35:06 +00:00
|
|
|
|
|
|
|
nvnc_key_fn fn = server->key_code_fn;
|
|
|
|
if (fn)
|
2020-11-29 20:48:31 +00:00
|
|
|
fn(client, evdev_keycode, !!down_flag);
|
2020-11-29 17:35:06 +00:00
|
|
|
|
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_client_qemu_event(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
if (client->buffer_len - client->buffer_index < 2)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
enum rfb_client_to_server_qemu_msg_type subtype =
|
|
|
|
client->msg_buffer[client->buffer_index + 1];
|
|
|
|
|
|
|
|
switch (subtype) {
|
|
|
|
case RFB_CLIENT_TO_SERVER_QEMU_KEY_EVENT:
|
|
|
|
return on_client_qemu_key_event(client);
|
|
|
|
}
|
|
|
|
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_WARNING, "Got uninterpretable qemu message from client: %p (ref %d)",
|
2020-11-29 17:35:06 +00:00
|
|
|
client, client->ref);
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2020-11-29 17:35:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int on_client_pointer_event(struct nvnc_client* client)
|
2019-08-12 23:33:06 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
struct nvnc* server = client->server;
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
struct rfb_client_pointer_event_msg* msg =
|
|
|
|
(struct rfb_client_pointer_event_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2019-12-23 09:33:29 +00:00
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg))
|
|
|
|
return 0;
|
|
|
|
|
2019-08-12 23:33:06 +00:00
|
|
|
int button_mask = msg->button_mask;
|
|
|
|
uint16_t x = ntohs(msg->x);
|
|
|
|
uint16_t y = ntohs(msg->y);
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
nvnc_pointer_fn fn = server->pointer_fn;
|
|
|
|
if (fn)
|
2019-08-31 23:16:55 +00:00
|
|
|
fn(client, x, y, button_mask);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
2020-09-16 22:10:15 +00:00
|
|
|
EXPORT
|
|
|
|
void nvnc_send_cut_text(struct nvnc* server, const char* text, uint32_t len)
|
|
|
|
{
|
|
|
|
struct rfb_cut_text_msg msg;
|
|
|
|
|
|
|
|
msg.type = RFB_SERVER_TO_CLIENT_SERVER_CUT_TEXT;
|
|
|
|
msg.length = htonl(len);
|
|
|
|
|
|
|
|
struct nvnc_client* client;
|
|
|
|
LIST_FOREACH (client, &server->clients, link) {
|
|
|
|
stream_write(client->net_stream, &msg, sizeof(msg), NULL, NULL);
|
|
|
|
stream_write(client->net_stream, text, len, NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int on_client_cut_text(struct nvnc_client* client)
|
2019-08-12 23:39:37 +00:00
|
|
|
{
|
2020-09-16 22:10:15 +00:00
|
|
|
struct rfb_cut_text_msg* msg =
|
|
|
|
(struct rfb_cut_text_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
2019-08-12 23:39:37 +00:00
|
|
|
|
2020-09-23 02:37:42 +00:00
|
|
|
size_t left_to_process = client->buffer_len - client->buffer_index;
|
|
|
|
|
|
|
|
if (left_to_process < sizeof(*msg))
|
2019-12-23 09:33:29 +00:00
|
|
|
return 0;
|
|
|
|
|
2019-08-12 23:39:37 +00:00
|
|
|
uint32_t length = ntohl(msg->length);
|
2020-09-23 02:37:42 +00:00
|
|
|
uint32_t max_length = MAX_CUT_TEXT_SIZE;
|
2019-08-12 23:39:37 +00:00
|
|
|
|
2020-09-16 22:10:15 +00:00
|
|
|
/* Messages greater than this size are unsupported */
|
2020-09-23 02:37:42 +00:00
|
|
|
if (length > max_length) {
|
2022-06-25 15:38:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_ERROR, "Copied text length (%d) is greater than max supported length (%d)",
|
2020-09-23 02:37:42 +00:00
|
|
|
length, max_length);
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2020-09-16 22:10:15 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-23 02:37:42 +00:00
|
|
|
size_t msg_size = sizeof(*msg) + length;
|
|
|
|
|
|
|
|
if (msg_size <= left_to_process) {
|
2022-11-20 22:42:01 +00:00
|
|
|
nvnc_cut_text_fn fn = client->server->cut_text_fn;
|
2020-09-23 02:37:42 +00:00
|
|
|
if (fn)
|
2022-11-20 22:42:01 +00:00
|
|
|
fn(client, msg->text, length);
|
2020-09-23 02:37:42 +00:00
|
|
|
|
|
|
|
return msg_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(!client->cut_text.buffer);
|
|
|
|
|
|
|
|
client->cut_text.buffer = malloc(length);
|
|
|
|
if (!client->cut_text.buffer) {
|
2022-06-25 15:38:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_ERROR, "OOM: %m");
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2020-09-18 23:15:57 +00:00
|
|
|
return 0;
|
2020-09-23 02:37:42 +00:00
|
|
|
}
|
2020-09-18 23:15:57 +00:00
|
|
|
|
2020-09-23 02:37:42 +00:00
|
|
|
size_t partial_size = left_to_process - sizeof(*msg);
|
|
|
|
|
|
|
|
memcpy(client->cut_text.buffer, msg->text, partial_size);
|
|
|
|
|
|
|
|
client->cut_text.length = length;
|
|
|
|
client->cut_text.index = partial_size;
|
|
|
|
|
|
|
|
return left_to_process;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void process_big_cut_text(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
assert(client->cut_text.length > client->cut_text.index);
|
|
|
|
|
|
|
|
void* start = client->cut_text.buffer + client->cut_text.index;
|
|
|
|
size_t space = client->cut_text.length - client->cut_text.index;
|
|
|
|
|
|
|
|
space = MIN(space, MSG_BUFFER_SIZE);
|
|
|
|
|
|
|
|
ssize_t n_read = stream_read(client->net_stream, start, space);
|
|
|
|
|
|
|
|
if (n_read == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (n_read < 0) {
|
|
|
|
if (errno != EAGAIN) {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_INFO, "Client connection error: %p (ref %d)",
|
2020-09-23 02:37:42 +00:00
|
|
|
client, client->ref);
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2020-09-23 02:37:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2020-09-16 22:10:15 +00:00
|
|
|
}
|
2019-08-12 23:39:37 +00:00
|
|
|
|
2020-09-23 02:37:42 +00:00
|
|
|
client->cut_text.index += n_read;
|
|
|
|
|
|
|
|
if (client->cut_text.index != client->cut_text.length)
|
|
|
|
return;
|
|
|
|
|
2022-11-20 22:42:01 +00:00
|
|
|
nvnc_cut_text_fn fn = client->server->cut_text_fn;
|
2020-09-23 02:37:42 +00:00
|
|
|
if (fn)
|
2022-11-20 22:42:01 +00:00
|
|
|
fn(client, client->cut_text.buffer, client->cut_text.length);
|
2020-09-23 02:37:42 +00:00
|
|
|
|
|
|
|
free(client->cut_text.buffer);
|
|
|
|
client->cut_text.buffer = NULL;
|
2019-08-12 23:39:37 +00:00
|
|
|
}
|
|
|
|
|
2022-11-05 12:54:58 +00:00
|
|
|
static enum rfb_resize_status check_desktop_layout(struct nvnc_client* client,
|
|
|
|
uint16_t width, uint16_t height, uint8_t n_screens,
|
|
|
|
struct rfb_screen* screens)
|
|
|
|
{
|
|
|
|
struct nvnc* server = client->server;
|
|
|
|
struct nvnc_desktop_layout* layout;
|
|
|
|
enum rfb_resize_status status = RFB_RESIZE_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
layout = malloc(sizeof(*layout) +
|
|
|
|
n_screens * sizeof(*layout->display_layouts));
|
|
|
|
if (!layout)
|
|
|
|
return RFB_RESIZE_STATUS_OUT_OF_RESOURCES;
|
|
|
|
|
|
|
|
layout->width = width;
|
|
|
|
layout->height = height;
|
|
|
|
layout->n_display_layouts = n_screens;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < n_screens; ++i) {
|
|
|
|
struct nvnc_display_layout* display;
|
|
|
|
struct rfb_screen* screen;
|
|
|
|
|
|
|
|
display = &layout->display_layouts[i];
|
|
|
|
screen = &screens[i];
|
|
|
|
|
|
|
|
nvnc_display_layout_init(display, screen);
|
|
|
|
|
|
|
|
if (screen->id == 0)
|
|
|
|
display->display = server->display;
|
|
|
|
|
|
|
|
if (display->x_pos + display->width > width ||
|
|
|
|
display->y_pos + display->height > height) {
|
|
|
|
status = RFB_RESIZE_STATUS_INVALID_LAYOUT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!server->desktop_layout_fn ||
|
|
|
|
!server->desktop_layout_fn(client, layout))
|
|
|
|
status = RFB_RESIZE_STATUS_PROHIBITED;
|
|
|
|
out:
|
|
|
|
free(layout);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void send_extended_desktop_size(struct nvnc_client* client,
|
|
|
|
enum rfb_resize_initiator initiator,
|
|
|
|
enum rfb_resize_status status)
|
|
|
|
{
|
|
|
|
struct rfb_server_fb_update_msg head = {
|
|
|
|
.type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE,
|
|
|
|
.n_rects = htons(1),
|
|
|
|
};
|
|
|
|
|
|
|
|
struct rfb_server_fb_rect rect = {
|
|
|
|
.encoding = htonl(RFB_ENCODING_EXTENDEDDESKTOPSIZE),
|
|
|
|
.x = htons(initiator),
|
|
|
|
.y = htons(status),
|
|
|
|
.width = htons(client->known_width),
|
|
|
|
.height = htons(client->known_height),
|
|
|
|
};
|
|
|
|
|
|
|
|
uint8_t number_of_screens = 1;
|
|
|
|
uint8_t buf[4] = { number_of_screens };
|
|
|
|
|
|
|
|
struct rfb_screen screen = {
|
|
|
|
.width = htons(client->known_width),
|
|
|
|
.height = htons(client->known_height),
|
|
|
|
};
|
|
|
|
|
|
|
|
stream_write(client->net_stream, &head, sizeof(head), NULL, NULL);
|
|
|
|
stream_write(client->net_stream, &rect, sizeof(rect), NULL, NULL);
|
|
|
|
stream_write(client->net_stream, &buf, sizeof(buf), NULL, NULL);
|
|
|
|
stream_write(client->net_stream, &screen, sizeof(screen), NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_client_set_desktop_size_event(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct rfb_client_set_desktop_size_event_msg* msg;
|
|
|
|
enum rfb_resize_status status;
|
|
|
|
uint16_t width, height;
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(*msg))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
msg = (struct rfb_client_set_desktop_size_event_msg*)
|
|
|
|
(client->msg_buffer + client->buffer_index);
|
|
|
|
|
|
|
|
width = ntohs(msg->width);
|
|
|
|
height = ntohs(msg->height);
|
|
|
|
|
|
|
|
status = check_desktop_layout(client, width, height,
|
|
|
|
msg->number_of_screens, msg->screens);
|
|
|
|
|
|
|
|
send_extended_desktop_size(client, RFB_RESIZE_INITIATOR_THIS_CLIENT,
|
|
|
|
status);
|
|
|
|
|
|
|
|
return sizeof(*msg) + msg->number_of_screens * sizeof(struct rfb_screen);
|
|
|
|
}
|
|
|
|
|
2023-04-08 13:06:57 +00:00
|
|
|
static void update_ntp_stats(struct nvnc_client* client,
|
|
|
|
const struct rfb_ntp_msg *msg)
|
2023-04-07 21:24:22 +00:00
|
|
|
{
|
2023-04-08 13:06:57 +00:00
|
|
|
uint32_t t0 = ntohl(msg->t0);
|
|
|
|
uint32_t t1 = ntohl(msg->t1);
|
|
|
|
uint32_t t2 = ntohl(msg->t2);
|
|
|
|
uint32_t t3 = ntohl(msg->t3);
|
2023-04-07 21:24:22 +00:00
|
|
|
|
2023-04-08 13:06:57 +00:00
|
|
|
double delta = (int32_t)(t3 - t0) - (int32_t)(t2 - t1);
|
|
|
|
double theta = ((int32_t)(t1 - t0) + (int32_t)(t2 - t3)) / 2;
|
2023-04-07 21:24:22 +00:00
|
|
|
|
2023-04-08 13:06:57 +00:00
|
|
|
nvnc_log(NVNC_LOG_DEBUG, "NTP: delta: %.2f ms, theta: %.2f ms",
|
|
|
|
delta / 1e3, theta / 1e3);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct rcbuf* on_ntp_msg_send(struct stream* tcp_stream,
|
|
|
|
void* userdata)
|
|
|
|
{
|
|
|
|
struct rfb_ntp_msg* msg = userdata;
|
|
|
|
msg->t2 = htonl(gettime_us(CLOCK_MONOTONIC));
|
|
|
|
return rcbuf_from_mem(msg, sizeof(*msg));
|
2023-04-07 21:24:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int on_client_ntp(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct rfb_ntp_msg msg;
|
|
|
|
|
|
|
|
if (client->buffer_len - client->buffer_index < sizeof(msg))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
memcpy(&msg, client->msg_buffer + client->buffer_index, sizeof(msg));
|
|
|
|
|
|
|
|
if (msg.t3 != 0) {
|
2023-04-08 13:06:57 +00:00
|
|
|
update_ntp_stats(client, &msg);
|
2023-04-07 21:24:22 +00:00
|
|
|
return sizeof(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.t1 = htonl(gettime_us(CLOCK_MONOTONIC));
|
|
|
|
|
2023-04-08 13:06:57 +00:00
|
|
|
struct rfb_ntp_msg* out_msg = malloc(sizeof(*out_msg));
|
|
|
|
assert(out_msg);
|
|
|
|
memcpy(out_msg, &msg, sizeof(*out_msg));
|
|
|
|
|
|
|
|
// The callback gets executed as the message is leaving the send queue
|
|
|
|
// so that we can set t2 as late as possible.
|
|
|
|
stream_exec_and_send(client->net_stream, on_ntp_msg_send, out_msg);
|
|
|
|
|
2023-04-07 21:24:22 +00:00
|
|
|
return sizeof(msg);
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int on_client_message(struct nvnc_client* client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
if (client->buffer_len - client->buffer_index < 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
enum rfb_client_to_server_msg_type type =
|
2019-10-20 22:13:51 +00:00
|
|
|
client->msg_buffer[client->buffer_index];
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case RFB_CLIENT_TO_SERVER_SET_PIXEL_FORMAT:
|
2019-08-12 22:11:41 +00:00
|
|
|
return on_client_set_pixel_format(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_SET_ENCODINGS:
|
2019-08-12 23:33:06 +00:00
|
|
|
return on_client_set_encodings(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_FRAMEBUFFER_UPDATE_REQUEST:
|
2019-08-12 23:33:06 +00:00
|
|
|
return on_client_fb_update_request(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_KEY_EVENT:
|
2019-08-12 23:33:06 +00:00
|
|
|
return on_client_key_event(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_POINTER_EVENT:
|
2019-08-12 23:33:06 +00:00
|
|
|
return on_client_pointer_event(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_CLIENT_CUT_TEXT:
|
2019-08-12 23:39:37 +00:00
|
|
|
return on_client_cut_text(client);
|
2020-11-29 17:35:06 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_QEMU:
|
|
|
|
return on_client_qemu_event(client);
|
2022-11-05 12:54:58 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_SET_DESKTOP_SIZE:
|
|
|
|
return on_client_set_desktop_size_event(client);
|
2023-04-07 21:24:22 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_NTP:
|
|
|
|
return on_client_ntp(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_WARNING, "Got uninterpretable message from client: %p (ref %d)",
|
2020-01-25 15:35:14 +00:00
|
|
|
client, client->ref);
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
static int try_read_client_message(struct nvnc_client* client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
switch (client->state) {
|
|
|
|
case VNC_CLIENT_STATE_ERROR:
|
2020-01-25 15:35:14 +00:00
|
|
|
return client->buffer_len - client->buffer_index;
|
2019-08-12 20:49:23 +00:00
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_VERSION:
|
|
|
|
return on_version_message(client);
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_SECURITY:
|
|
|
|
return on_security_message(client);
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_INIT:
|
|
|
|
return on_init_message(client);
|
2020-01-25 15:35:14 +00:00
|
|
|
#ifdef ENABLE_TLS
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_VERSION:
|
|
|
|
return on_vencrypt_version_message(client);
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_SUBTYPE:
|
|
|
|
return on_vencrypt_subtype_message(client);
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_PLAIN_AUTH:
|
|
|
|
return on_vencrypt_plain_auth_message(client);
|
2023-08-13 12:15:32 +00:00
|
|
|
#endif
|
|
|
|
#ifdef HAVE_CRYPTO
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_APPLE_DH_RESPONSE:
|
|
|
|
return on_apple_dh_response(client);
|
2023-08-24 10:13:52 +00:00
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_PUBLIC_KEY:
|
|
|
|
return on_rsa_aes_public_key(client);
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CHALLENGE:
|
|
|
|
return on_rsa_aes_challenge(client);
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CLIENT_HASH:
|
|
|
|
return on_rsa_aes_client_hash(client);
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CREDENTIALS:
|
|
|
|
return on_rsa_aes_credentials(client);
|
2020-01-25 15:35:14 +00:00
|
|
|
#endif
|
2019-08-12 20:49:23 +00:00
|
|
|
case VNC_CLIENT_STATE_READY:
|
|
|
|
return on_client_message(client);
|
|
|
|
}
|
|
|
|
|
2022-10-24 09:16:59 +00:00
|
|
|
nvnc_log(NVNC_LOG_PANIC, "Invalid client state");
|
2019-08-12 20:49:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
static void on_client_event(struct stream* stream, enum stream_event event)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
2020-01-25 15:35:14 +00:00
|
|
|
struct nvnc_client* client = stream->userdata;
|
|
|
|
|
2020-07-16 18:14:52 +00:00
|
|
|
assert(client->net_stream && client->net_stream == stream);
|
2020-01-25 15:35:14 +00:00
|
|
|
|
|
|
|
if (event == STREAM_EVENT_REMOTE_CLOSED) {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_INFO, "Client %p (%d) hung up", client, client->ref);
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2020-01-25 15:35:14 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-23 02:37:42 +00:00
|
|
|
if (client->cut_text.buffer) {
|
|
|
|
process_big_cut_text(client);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-25 16:41:23 +00:00
|
|
|
assert(client->buffer_index == 0);
|
|
|
|
|
|
|
|
void* start = client->msg_buffer + client->buffer_len;
|
|
|
|
size_t space = MSG_BUFFER_SIZE - client->buffer_len;
|
|
|
|
ssize_t n_read = stream_read(stream, start, space);
|
2019-09-18 21:28:48 +00:00
|
|
|
|
2019-12-27 13:57:24 +00:00
|
|
|
if (n_read == 0)
|
2020-01-25 16:41:23 +00:00
|
|
|
return;
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2019-12-27 13:57:24 +00:00
|
|
|
if (n_read < 0) {
|
2020-01-25 15:35:14 +00:00
|
|
|
if (errno != EAGAIN) {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_INFO, "Client connection error: %p (ref %d)",
|
2020-01-25 15:35:14 +00:00
|
|
|
client, client->ref);
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2020-01-25 15:35:14 +00:00
|
|
|
}
|
|
|
|
|
2020-01-25 16:41:23 +00:00
|
|
|
return;
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
client->buffer_len += n_read;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
int rc = try_read_client_message(client);
|
|
|
|
if (rc == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
client->buffer_index += rc;
|
2020-01-25 15:35:14 +00:00
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
assert(client->buffer_index <= client->buffer_len);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
client->buffer_len -= client->buffer_index;
|
2020-09-18 23:15:57 +00:00
|
|
|
memmove(client->msg_buffer, client->msg_buffer + client->buffer_index,
|
|
|
|
client->buffer_len);
|
2019-08-12 20:49:23 +00:00
|
|
|
client->buffer_index = 0;
|
|
|
|
}
|
|
|
|
|
2022-10-31 16:02:43 +00:00
|
|
|
static void record_peer_hostname(int fd, struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct sockaddr_storage storage;
|
|
|
|
struct sockaddr* peer = (struct sockaddr*)&storage;
|
|
|
|
socklen_t peerlen = sizeof(storage);
|
|
|
|
if (getpeername(fd, peer, &peerlen) == 0) {
|
|
|
|
if (peer->sa_family == AF_UNIX) {
|
|
|
|
snprintf(client->hostname, sizeof(client->hostname),
|
|
|
|
"unix domain socket");
|
|
|
|
} else {
|
|
|
|
getnameinfo(peer, peerlen,
|
|
|
|
client->hostname, sizeof(client->hostname),
|
|
|
|
NULL, 0, // no need for port
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-16 20:09:22 +00:00
|
|
|
static void on_connection(void* obj)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
2020-03-16 20:09:22 +00:00
|
|
|
struct nvnc* server = aml_get_userdata(obj);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
struct nvnc_client* client = calloc(1, sizeof(*client));
|
2019-08-12 20:49:23 +00:00
|
|
|
if (!client)
|
|
|
|
return;
|
|
|
|
|
2019-09-18 21:28:48 +00:00
|
|
|
client->ref = 1;
|
2019-08-12 20:49:23 +00:00
|
|
|
client->server = server;
|
2022-07-10 12:41:18 +00:00
|
|
|
client->quality = 10; /* default to lossless */
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
int fd = accept(server->fd, NULL, 0);
|
2020-07-18 14:04:12 +00:00
|
|
|
if (fd < 0) {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_WARNING, "Failed to accept a connection");
|
2020-01-25 15:35:14 +00:00
|
|
|
goto accept_failure;
|
2020-07-18 14:04:12 +00:00
|
|
|
}
|
2020-01-25 15:35:14 +00:00
|
|
|
|
2022-07-09 16:29:18 +00:00
|
|
|
int one = 1;
|
|
|
|
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
|
|
|
|
|
2022-10-31 16:02:43 +00:00
|
|
|
record_peer_hostname(fd, client);
|
|
|
|
|
2023-04-06 21:02:01 +00:00
|
|
|
#ifdef ENABLE_WEBSOCKET
|
|
|
|
if (server->socket_type == NVNC__SOCKET_WEBSOCKET)
|
|
|
|
{
|
|
|
|
client->net_stream = stream_ws_new(fd, on_client_event, client);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
client->net_stream = stream_new(fd, on_client_event, client);
|
|
|
|
}
|
2020-07-18 14:04:12 +00:00
|
|
|
if (!client->net_stream) {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_WARNING, "OOM");
|
2020-01-25 15:35:14 +00:00
|
|
|
goto stream_failure;
|
2020-07-18 14:04:12 +00:00
|
|
|
}
|
2020-01-25 15:35:14 +00:00
|
|
|
|
2020-07-18 14:04:12 +00:00
|
|
|
if (!server->display->buffer) {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_WARNING, "No display buffer has been set");
|
2020-07-18 14:04:12 +00:00
|
|
|
goto buffer_failure;
|
|
|
|
}
|
2019-09-01 18:40:11 +00:00
|
|
|
|
2019-10-07 20:12:59 +00:00
|
|
|
pixman_region_init(&client->damage);
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
struct rcbuf* payload = rcbuf_from_string(RFB_VERSION_MESSAGE);
|
2020-07-18 14:04:12 +00:00
|
|
|
if (!payload) {
|
2022-06-25 16:32:05 +00:00
|
|
|
nvnc_log(NVNC_LOG_WARNING, "OOM");
|
2020-01-25 15:35:14 +00:00
|
|
|
goto payload_failure;
|
2020-07-18 14:04:12 +00:00
|
|
|
}
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-01-25 15:52:44 +00:00
|
|
|
stream_send(client->net_stream, payload, NULL, NULL);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
LIST_INSERT_HEAD(&server->clients, client, link);
|
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_VERSION;
|
|
|
|
|
2022-10-31 16:02:43 +00:00
|
|
|
nvnc_log(NVNC_LOG_INFO, "New client connection from %s: %p (ref %d)", client->hostname, client, client->ref);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
return;
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
payload_failure:
|
2020-04-02 21:52:04 +00:00
|
|
|
pixman_region_fini(&client->damage);
|
2020-07-18 14:04:12 +00:00
|
|
|
buffer_failure:
|
2020-01-25 15:35:14 +00:00
|
|
|
stream_destroy(client->net_stream);
|
|
|
|
stream_failure:
|
|
|
|
close(fd);
|
|
|
|
accept_failure:
|
|
|
|
free(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
2019-08-12 22:11:41 +00:00
|
|
|
|
2022-10-24 20:55:38 +00:00
|
|
|
static void sockaddr_to_string(char* dst, size_t sz, const struct sockaddr* addr)
|
|
|
|
{
|
|
|
|
struct sockaddr_in *sa_in = (struct sockaddr_in*)addr;
|
|
|
|
struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6*)addr;
|
|
|
|
|
|
|
|
switch (addr->sa_family) {
|
|
|
|
case AF_INET:
|
|
|
|
inet_ntop(addr->sa_family, &sa_in->sin_addr, dst, sz);
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
inet_ntop(addr->sa_family, &sa_in6->sin6_addr, dst, sz);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-17 07:37:06 +00:00
|
|
|
static int bind_address_tcp(const char* name, int port)
|
2020-04-04 22:23:04 +00:00
|
|
|
{
|
|
|
|
struct addrinfo hints = {
|
|
|
|
.ai_socktype = SOCK_STREAM,
|
|
|
|
.ai_flags = AI_PASSIVE,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct addrinfo* result;
|
|
|
|
|
|
|
|
char service[256];
|
|
|
|
snprintf(service, sizeof(service), "%d", port);
|
|
|
|
|
|
|
|
int rc = getaddrinfo(name, service, &hints, &result);
|
2022-10-24 20:55:38 +00:00
|
|
|
if (rc != 0) {
|
|
|
|
nvnc_log(NVNC_LOG_ERROR, "Failed to get address info: %s",
|
|
|
|
gai_strerror(rc));
|
2020-04-04 22:23:04 +00:00
|
|
|
return -1;
|
2022-10-24 20:55:38 +00:00
|
|
|
}
|
2020-04-04 22:23:04 +00:00
|
|
|
|
|
|
|
int fd = -1;
|
|
|
|
|
|
|
|
for (struct addrinfo* p = result; p != NULL; p = p->ai_next) {
|
2022-10-24 20:55:38 +00:00
|
|
|
char ai_str[256] = { 0 };
|
|
|
|
sockaddr_to_string(ai_str, sizeof(ai_str), p->ai_addr);
|
|
|
|
nvnc_log(NVNC_LOG_DEBUG, "Trying address: %s", ai_str);
|
|
|
|
|
2020-04-04 22:23:04 +00:00
|
|
|
fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
|
2022-10-24 20:55:38 +00:00
|
|
|
if (fd < 0) {
|
|
|
|
nvnc_log(NVNC_LOG_DEBUG, "Failed to create socket: %m");
|
2020-04-04 22:23:04 +00:00
|
|
|
continue;
|
2022-10-24 20:55:38 +00:00
|
|
|
}
|
2020-04-04 22:23:04 +00:00
|
|
|
|
2020-04-06 18:29:17 +00:00
|
|
|
int one = 1;
|
2022-10-24 20:55:38 +00:00
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0) {
|
2023-04-08 13:11:51 +00:00
|
|
|
nvnc_log(NVNC_LOG_DEBUG, "Failed to set SO_REUSEADDR: %m");
|
2020-04-06 18:29:17 +00:00
|
|
|
goto failure;
|
2022-10-24 20:55:38 +00:00
|
|
|
}
|
2020-04-06 18:29:17 +00:00
|
|
|
|
2023-04-11 20:37:20 +00:00
|
|
|
int sndbuf = 65536;
|
2023-04-08 13:11:51 +00:00
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) < 0) {
|
|
|
|
nvnc_log(NVNC_LOG_DEBUG, "Failed to set SO_SNDBUF: %m");
|
|
|
|
}
|
|
|
|
|
2022-10-24 20:55:38 +00:00
|
|
|
if (bind(fd, p->ai_addr, p->ai_addrlen) == 0) {
|
|
|
|
nvnc_log(NVNC_LOG_DEBUG, "Successfully bound to address");
|
2020-04-04 22:23:04 +00:00
|
|
|
break;
|
2022-10-24 20:55:38 +00:00
|
|
|
}
|
2020-04-04 22:23:04 +00:00
|
|
|
|
2022-10-24 20:55:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_DEBUG, "Failed to bind to address: %m");
|
2020-04-06 18:29:17 +00:00
|
|
|
failure:
|
2020-04-04 22:23:04 +00:00
|
|
|
close(fd);
|
|
|
|
fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(result);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2021-03-17 07:37:06 +00:00
|
|
|
static int bind_address_unix(const char* name)
|
|
|
|
{
|
|
|
|
struct sockaddr_un addr = {
|
|
|
|
.sun_family = AF_UNIX,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (strlen(name) >= sizeof(addr.sun_path)) {
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strcpy(addr.sun_path, name);
|
|
|
|
|
|
|
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
|
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2023-04-06 21:02:01 +00:00
|
|
|
static int bind_address(const char* name, uint16_t port,
|
|
|
|
enum nvnc__socket_type type)
|
2021-03-17 07:37:06 +00:00
|
|
|
{
|
|
|
|
switch (type) {
|
2023-04-06 21:02:01 +00:00
|
|
|
case NVNC__SOCKET_TCP:
|
|
|
|
case NVNC__SOCKET_WEBSOCKET:
|
2021-03-17 07:37:06 +00:00
|
|
|
return bind_address_tcp(name, port);
|
2023-04-06 21:02:01 +00:00
|
|
|
case NVNC__SOCKET_UNIX:
|
2021-03-17 07:37:06 +00:00
|
|
|
return bind_address_unix(name);
|
|
|
|
}
|
|
|
|
|
2022-10-24 09:16:59 +00:00
|
|
|
nvnc_log(NVNC_LOG_PANIC, "Unknown socket address type");
|
|
|
|
return -1;
|
2021-03-17 07:37:06 +00:00
|
|
|
}
|
|
|
|
|
2023-04-06 21:02:01 +00:00
|
|
|
static struct nvnc* open_common(const char* address, uint16_t port,
|
|
|
|
enum nvnc__socket_type type)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
2022-07-09 17:15:35 +00:00
|
|
|
nvnc__log_init();
|
|
|
|
|
2020-03-21 17:27:30 +00:00
|
|
|
aml_require_workers(aml_get_default(), -1);
|
2020-03-16 20:09:22 +00:00
|
|
|
|
2019-10-20 22:13:51 +00:00
|
|
|
struct nvnc* self = calloc(1, sizeof(*self));
|
2019-08-28 22:46:47 +00:00
|
|
|
if (!self)
|
|
|
|
return NULL;
|
|
|
|
|
2023-04-06 21:02:01 +00:00
|
|
|
self->socket_type = type;
|
|
|
|
|
2020-04-12 16:08:33 +00:00
|
|
|
strcpy(self->name, DEFAULT_NAME);
|
2019-08-29 21:47:02 +00:00
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
LIST_INIT(&self->clients);
|
|
|
|
|
2021-03-17 07:37:06 +00:00
|
|
|
self->fd = bind_address(address, port, type);
|
2020-01-25 15:35:14 +00:00
|
|
|
if (self->fd < 0)
|
2021-03-17 07:37:06 +00:00
|
|
|
goto bind_failure;
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
if (listen(self->fd, 16) < 0)
|
2021-03-17 07:37:06 +00:00
|
|
|
goto listen_failure;
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2020-03-16 20:09:22 +00:00
|
|
|
self->poll_handle = aml_handler_new(self->fd, on_connection, self, NULL);
|
|
|
|
if (!self->poll_handle)
|
2021-03-17 07:37:06 +00:00
|
|
|
goto handle_failure;
|
2020-03-16 20:09:22 +00:00
|
|
|
|
|
|
|
if (aml_start(aml_get_default(), self->poll_handle) < 0)
|
2020-04-07 22:21:11 +00:00
|
|
|
goto poll_start_failure;
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
return self;
|
2020-01-25 15:35:14 +00:00
|
|
|
|
2020-04-07 22:21:11 +00:00
|
|
|
poll_start_failure:
|
2020-03-16 20:09:22 +00:00
|
|
|
aml_unref(self->poll_handle);
|
2021-03-17 07:37:06 +00:00
|
|
|
handle_failure:
|
|
|
|
listen_failure:
|
2020-01-25 15:35:14 +00:00
|
|
|
close(self->fd);
|
2023-04-06 21:02:01 +00:00
|
|
|
if (type == NVNC__SOCKET_UNIX) {
|
2021-03-17 07:37:06 +00:00
|
|
|
unlink(address);
|
|
|
|
}
|
|
|
|
bind_failure:
|
|
|
|
free(self);
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-03-17 07:37:06 +00:00
|
|
|
EXPORT
|
|
|
|
struct nvnc* nvnc_open(const char* address, uint16_t port)
|
|
|
|
{
|
2023-04-06 21:02:01 +00:00
|
|
|
return open_common(address, port, NVNC__SOCKET_TCP);
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
|
|
|
struct nvnc* nvnc_open_websocket(const char *address, uint16_t port)
|
|
|
|
{
|
|
|
|
#ifdef ENABLE_WEBSOCKET
|
|
|
|
return open_common(address, port, NVNC__SOCKET_WEBSOCKET);
|
|
|
|
#else
|
|
|
|
return NULL;
|
|
|
|
#endif
|
2021-03-17 07:37:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
|
|
|
struct nvnc* nvnc_open_unix(const char* address)
|
|
|
|
{
|
2023-04-06 21:02:01 +00:00
|
|
|
return open_common(address, 0, NVNC__SOCKET_UNIX);
|
2021-03-17 07:37:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void unlink_fd_path(int fd)
|
|
|
|
{
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
socklen_t addr_len = sizeof(addr);
|
|
|
|
|
|
|
|
if (getsockname(fd, (struct sockaddr*)&addr, &addr_len) == 0) {
|
|
|
|
if (addr.sun_family == AF_UNIX) {
|
|
|
|
unlink(addr.sun_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
EXPORT
|
2019-10-20 22:13:51 +00:00
|
|
|
void nvnc_close(struct nvnc* self)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
struct nvnc_client* client;
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2021-09-04 17:05:30 +00:00
|
|
|
nvnc_cleanup_fn cleanup = self->common.cleanup_fn;
|
|
|
|
if (cleanup)
|
|
|
|
cleanup(self->common.userdata);
|
|
|
|
|
2020-04-12 18:16:19 +00:00
|
|
|
if (self->display)
|
|
|
|
nvnc_display_unref(self->display);
|
2019-10-07 20:12:59 +00:00
|
|
|
|
2022-10-30 11:02:11 +00:00
|
|
|
if (self->cursor.buffer)
|
|
|
|
nvnc_fb_unref(self->cursor.buffer);
|
|
|
|
|
2020-01-25 15:35:14 +00:00
|
|
|
struct nvnc_client* tmp;
|
|
|
|
LIST_FOREACH_SAFE (client, &self->clients, link, tmp)
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2020-03-16 20:09:22 +00:00
|
|
|
aml_stop(aml_get_default(), self->poll_handle);
|
2021-03-17 07:37:06 +00:00
|
|
|
unlink_fd_path(self->fd);
|
2020-01-25 15:35:14 +00:00
|
|
|
close(self->fd);
|
|
|
|
|
|
|
|
#ifdef ENABLE_TLS
|
|
|
|
if (self->tls_creds) {
|
|
|
|
gnutls_certificate_free_credentials(self->tls_creds);
|
|
|
|
gnutls_global_deinit();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-03-16 20:09:22 +00:00
|
|
|
aml_unref(self->poll_handle);
|
2019-08-28 22:46:47 +00:00
|
|
|
free(self);
|
|
|
|
}
|
|
|
|
|
2022-11-05 13:20:41 +00:00
|
|
|
static void complete_fb_update(struct nvnc_client* client)
|
2019-09-06 19:26:18 +00:00
|
|
|
{
|
2019-12-23 12:10:58 +00:00
|
|
|
client->is_updating = false;
|
2021-08-29 14:41:57 +00:00
|
|
|
assert(client->current_fb);
|
|
|
|
nvnc_fb_release(client->current_fb);
|
|
|
|
nvnc_fb_unref(client->current_fb);
|
|
|
|
client->current_fb = NULL;
|
2021-08-29 18:22:25 +00:00
|
|
|
process_fb_update_requests(client);
|
2020-01-25 15:35:14 +00:00
|
|
|
client_unref(client);
|
2022-11-05 13:20:41 +00:00
|
|
|
DTRACE_PROBE2(neatvnc, update_fb_done, client, pts);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void on_write_frame_done(void* userdata, enum stream_req_status status)
|
|
|
|
{
|
|
|
|
struct nvnc_client* client = userdata;
|
|
|
|
complete_fb_update(client);
|
2019-09-06 19:26:18 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 16:29:37 +00:00
|
|
|
static enum rfb_encodings choose_frame_encoding(struct nvnc_client* client,
|
|
|
|
struct nvnc_fb* fb)
|
2019-09-19 18:14:24 +00:00
|
|
|
{
|
2020-01-19 19:16:27 +00:00
|
|
|
for (size_t i = 0; i < client->n_encodings; ++i)
|
2019-09-19 18:14:24 +00:00
|
|
|
switch (client->encodings[i]) {
|
|
|
|
case RFB_ENCODING_RAW:
|
2019-12-30 09:59:51 +00:00
|
|
|
case RFB_ENCODING_TIGHT:
|
2019-09-19 18:14:24 +00:00
|
|
|
case RFB_ENCODING_ZRLE:
|
2022-08-23 22:32:49 +00:00
|
|
|
return client->encodings[i];
|
2021-12-24 16:29:37 +00:00
|
|
|
#ifdef ENABLE_OPEN_H264
|
|
|
|
case RFB_ENCODING_OPEN_H264:
|
|
|
|
// h264 is useless for sw frames
|
|
|
|
if (fb->type != NVNC_FB_GBM_BO)
|
|
|
|
break;
|
2019-09-19 18:14:24 +00:00
|
|
|
return client->encodings[i];
|
2022-08-23 22:32:49 +00:00
|
|
|
#endif
|
2019-09-19 18:14:24 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-05-31 22:56:27 +00:00
|
|
|
return RFB_ENCODING_RAW;
|
2019-09-19 18:14:24 +00:00
|
|
|
}
|
|
|
|
|
2020-07-19 11:54:39 +00:00
|
|
|
static bool client_has_encoding(const struct nvnc_client* client,
|
|
|
|
enum rfb_encodings encoding)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < client->n_encodings; ++i)
|
|
|
|
if (client->encodings[i] == encoding)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-12-12 16:05:29 +00:00
|
|
|
static void finish_fb_update(struct nvnc_client* client, struct rcbuf* payload,
|
2022-04-14 16:56:41 +00:00
|
|
|
int n_rects, uint64_t pts)
|
2019-09-15 21:51:07 +00:00
|
|
|
{
|
2020-04-04 12:33:18 +00:00
|
|
|
client_ref(client);
|
2020-01-25 15:35:14 +00:00
|
|
|
|
2022-11-05 13:20:41 +00:00
|
|
|
if (client->net_stream->state == STREAM_STATE_CLOSED)
|
|
|
|
goto complete;
|
2019-09-15 21:51:07 +00:00
|
|
|
|
2022-11-05 13:20:41 +00:00
|
|
|
DTRACE_PROBE2(neatvnc, send_fb_start, client, pts);
|
|
|
|
n_rects += will_send_pts(client, pts) ? 1 : 0;
|
|
|
|
struct rfb_server_fb_update_msg update_msg = {
|
|
|
|
.type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE,
|
|
|
|
.n_rects = htons(n_rects),
|
|
|
|
};
|
|
|
|
if (stream_write(client->net_stream, &update_msg,
|
|
|
|
sizeof(update_msg), NULL, NULL) < 0)
|
|
|
|
goto complete;
|
|
|
|
|
|
|
|
if (send_pts_rect(client, pts) < 0)
|
|
|
|
goto complete;
|
|
|
|
|
|
|
|
rcbuf_ref(payload);
|
|
|
|
if (stream_send(client->net_stream, payload,
|
|
|
|
on_write_frame_done, client) < 0)
|
|
|
|
goto complete;
|
|
|
|
|
|
|
|
DTRACE_PROBE2(neatvnc, send_fb_done, client, pts);
|
|
|
|
return;
|
|
|
|
|
|
|
|
complete:
|
|
|
|
complete_fb_update(client);
|
2020-09-26 22:29:02 +00:00
|
|
|
}
|
|
|
|
|
2022-04-14 16:56:41 +00:00
|
|
|
static void on_encode_frame_done(struct encoder* encoder, struct rcbuf* result,
|
|
|
|
uint64_t pts)
|
2020-09-27 12:37:00 +00:00
|
|
|
{
|
2021-12-09 22:48:31 +00:00
|
|
|
struct nvnc_client* client = encoder->userdata;
|
2022-06-29 11:36:05 +00:00
|
|
|
client->encoder->on_done = NULL;
|
|
|
|
client->encoder->userdata = NULL;
|
2022-04-14 16:56:41 +00:00
|
|
|
finish_fb_update(client, result, encoder->n_rects, pts);
|
2020-01-25 15:35:14 +00:00
|
|
|
client_unref(client);
|
2019-09-15 21:51:07 +00:00
|
|
|
}
|
|
|
|
|
2020-07-19 11:54:39 +00:00
|
|
|
static int send_desktop_resize(struct nvnc_client* client, struct nvnc_fb* fb)
|
|
|
|
{
|
2022-11-05 12:54:58 +00:00
|
|
|
if (!client_has_encoding(client, RFB_ENCODING_DESKTOPSIZE) &&
|
|
|
|
!client_has_encoding(client, RFB_ENCODING_EXTENDEDDESKTOPSIZE)) {
|
2022-06-25 15:38:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_ERROR, "Client does not support desktop resizing. Closing connection...");
|
2022-11-24 07:39:50 +00:00
|
|
|
nvnc_client_close(client);
|
2020-07-19 11:54:39 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
client->known_width = fb->width;
|
|
|
|
client->known_height = fb->height;
|
|
|
|
|
2022-09-20 11:10:59 +00:00
|
|
|
if (client->encoder)
|
|
|
|
encoder_resize(client->encoder, fb->width, fb->height);
|
2020-07-19 16:58:55 +00:00
|
|
|
|
2020-07-19 11:54:39 +00:00
|
|
|
pixman_region_union_rect(&client->damage, &client->damage, 0, 0,
|
|
|
|
fb->width, fb->height);
|
|
|
|
|
2022-11-05 12:54:58 +00:00
|
|
|
if (client_has_encoding(client, RFB_ENCODING_EXTENDEDDESKTOPSIZE)) {
|
|
|
|
send_extended_desktop_size(client,
|
|
|
|
RFB_RESIZE_INITIATOR_SERVER,
|
|
|
|
RFB_RESIZE_STATUS_SUCCESS);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-19 11:54:39 +00:00
|
|
|
struct rfb_server_fb_update_msg head = {
|
|
|
|
.type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE,
|
|
|
|
.n_rects = htons(1),
|
|
|
|
};
|
|
|
|
|
|
|
|
struct rfb_server_fb_rect rect = {
|
|
|
|
.encoding = htonl(RFB_ENCODING_DESKTOPSIZE),
|
|
|
|
.width = htons(fb->width),
|
|
|
|
.height = htons(fb->height),
|
|
|
|
};
|
|
|
|
|
|
|
|
stream_write(client->net_stream, &head, sizeof(head), NULL, NULL);
|
|
|
|
stream_write(client->net_stream, &rect, sizeof(rect), NULL, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-29 18:17:30 +00:00
|
|
|
static int send_qemu_key_ext_frame(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
struct rfb_server_fb_update_msg head = {
|
|
|
|
.type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE,
|
|
|
|
.n_rects = htons(1),
|
|
|
|
};
|
|
|
|
|
|
|
|
struct rfb_server_fb_rect rect = {
|
|
|
|
.encoding = htonl(RFB_ENCODING_QEMU_EXT_KEY_EVENT),
|
|
|
|
};
|
|
|
|
|
|
|
|
stream_write(client->net_stream, &head, sizeof(head), NULL, NULL);
|
|
|
|
stream_write(client->net_stream, &rect, sizeof(rect), NULL, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-12 18:16:19 +00:00
|
|
|
void nvnc__damage_region(struct nvnc* self, const struct pixman_region16* damage)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
struct nvnc_client* client;
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2020-04-07 22:21:11 +00:00
|
|
|
LIST_FOREACH(client, &self->clients, link)
|
|
|
|
if (client->net_stream->state != STREAM_STATE_CLOSED)
|
|
|
|
pixman_region_union(&client->damage, &client->damage,
|
|
|
|
(struct pixman_region16*)damage);
|
2021-08-29 18:22:25 +00:00
|
|
|
|
|
|
|
LIST_FOREACH(client, &self->clients, link)
|
|
|
|
process_fb_update_requests(client);
|
2020-04-07 22:21:11 +00:00
|
|
|
}
|
2019-08-28 22:46:47 +00:00
|
|
|
|
|
|
|
EXPORT
|
2021-09-04 17:05:30 +00:00
|
|
|
void nvnc_set_userdata(void* self, void* userdata, nvnc_cleanup_fn cleanup_fn)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
struct nvnc_common* common = self;
|
2019-08-31 23:16:55 +00:00
|
|
|
common->userdata = userdata;
|
2021-09-04 17:05:30 +00:00
|
|
|
common->cleanup_fn = cleanup_fn;
|
2019-08-28 22:46:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
2019-10-20 22:13:51 +00:00
|
|
|
void* nvnc_get_userdata(const void* self)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
2019-10-20 22:13:51 +00:00
|
|
|
const struct nvnc_common* common = self;
|
2019-08-31 23:16:55 +00:00
|
|
|
return common->userdata;
|
2019-08-28 22:46:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
2019-10-20 22:13:51 +00:00
|
|
|
void nvnc_set_key_fn(struct nvnc* self, nvnc_key_fn fn)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
|
|
|
self->key_fn = fn;
|
|
|
|
}
|
|
|
|
|
2020-11-29 17:35:06 +00:00
|
|
|
EXPORT
|
|
|
|
void nvnc_set_key_code_fn(struct nvnc* self, nvnc_key_fn fn)
|
|
|
|
{
|
|
|
|
self->key_code_fn = fn;
|
|
|
|
}
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
EXPORT
|
2019-10-20 22:13:51 +00:00
|
|
|
void nvnc_set_pointer_fn(struct nvnc* self, nvnc_pointer_fn fn)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
|
|
|
self->pointer_fn = fn;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
2019-10-20 22:13:51 +00:00
|
|
|
void nvnc_set_fb_req_fn(struct nvnc* self, nvnc_fb_req_fn fn)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
|
|
|
self->fb_req_fn = fn;
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:30:08 +00:00
|
|
|
EXPORT
|
2019-10-20 22:13:51 +00:00
|
|
|
void nvnc_set_new_client_fn(struct nvnc* self, nvnc_client_fn fn)
|
2019-08-31 23:30:08 +00:00
|
|
|
{
|
|
|
|
self->new_client_fn = fn;
|
|
|
|
}
|
|
|
|
|
2020-04-07 22:21:11 +00:00
|
|
|
EXPORT
|
2020-04-12 18:16:19 +00:00
|
|
|
void nvnc_set_client_cleanup_fn(struct nvnc_client* self, nvnc_client_fn fn)
|
2020-04-07 22:21:11 +00:00
|
|
|
{
|
2020-04-12 18:16:19 +00:00
|
|
|
self->cleanup_fn = fn;
|
2020-04-07 22:21:11 +00:00
|
|
|
}
|
|
|
|
|
2020-09-16 22:10:15 +00:00
|
|
|
EXPORT
|
2022-11-20 22:42:01 +00:00
|
|
|
void nvnc_set_cut_text_fn(struct nvnc* self, nvnc_cut_text_fn fn)
|
2020-09-16 22:10:15 +00:00
|
|
|
{
|
|
|
|
self->cut_text_fn = fn;
|
|
|
|
}
|
|
|
|
|
2022-11-05 12:54:58 +00:00
|
|
|
EXPORT
|
|
|
|
void nvnc_set_desktop_layout_fn(struct nvnc* self, nvnc_desktop_layout_fn fn)
|
|
|
|
{
|
|
|
|
self->desktop_layout_fn = fn;
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:30:08 +00:00
|
|
|
EXPORT
|
2020-04-12 18:16:19 +00:00
|
|
|
void nvnc_add_display(struct nvnc* self, struct nvnc_display* display)
|
2019-08-31 23:30:08 +00:00
|
|
|
{
|
2020-04-12 18:16:19 +00:00
|
|
|
if (self->display) {
|
2022-10-24 09:16:59 +00:00
|
|
|
nvnc_log(NVNC_LOG_PANIC, "Multiple displays are not implemented. Aborting!");
|
2020-04-12 18:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
display->server = self;
|
|
|
|
self->display = display;
|
|
|
|
nvnc_display_ref(display);
|
2019-08-31 23:30:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-07 22:21:11 +00:00
|
|
|
EXPORT
|
2020-04-12 18:16:19 +00:00
|
|
|
void nvnc_remove_display(struct nvnc* self, struct nvnc_display* display)
|
2020-04-07 22:21:11 +00:00
|
|
|
{
|
2020-04-12 18:16:19 +00:00
|
|
|
if (self->display != display)
|
|
|
|
return;
|
2020-04-07 22:21:11 +00:00
|
|
|
|
2020-04-12 18:16:19 +00:00
|
|
|
nvnc_display_unref(display);
|
|
|
|
self->display = NULL;
|
2020-04-07 22:21:11 +00:00
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
EXPORT
|
2020-04-12 18:16:19 +00:00
|
|
|
struct nvnc* nvnc_client_get_server(const struct nvnc_client* client)
|
2019-08-31 23:16:55 +00:00
|
|
|
{
|
|
|
|
return client->server;
|
|
|
|
}
|
|
|
|
|
2022-10-31 16:02:43 +00:00
|
|
|
EXPORT
|
|
|
|
const char* nvnc_client_get_hostname(const struct nvnc_client* client) {
|
|
|
|
if (client->hostname[0] == '\0')
|
|
|
|
return NULL;
|
|
|
|
return client->hostname;
|
|
|
|
}
|
|
|
|
|
2022-10-31 16:08:23 +00:00
|
|
|
EXPORT
|
|
|
|
const char* nvnc_client_get_auth_username(const struct nvnc_client* client) {
|
|
|
|
if (client->username[0] == '\0')
|
|
|
|
return NULL;
|
|
|
|
return client->username;
|
|
|
|
}
|
|
|
|
|
2022-11-20 12:03:18 +00:00
|
|
|
EXPORT
|
|
|
|
struct nvnc_client* nvnc_client_first(struct nvnc* self)
|
|
|
|
{
|
|
|
|
return LIST_FIRST(&self->clients);
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
|
|
|
struct nvnc_client* nvnc_client_next(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
assert(client);
|
|
|
|
return LIST_NEXT(client, link);
|
|
|
|
}
|
|
|
|
|
2022-11-24 07:39:50 +00:00
|
|
|
EXPORT
|
|
|
|
void nvnc_client_close(struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
stream_close(client->net_stream);
|
|
|
|
client_unref(client);
|
|
|
|
}
|
|
|
|
|
2022-07-28 10:34:24 +00:00
|
|
|
EXPORT
|
|
|
|
bool nvnc_client_supports_cursor(const struct nvnc_client* client)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < client->n_encodings; ++i) {
|
|
|
|
if (client->encodings[i] == RFB_ENCODING_CURSOR)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-29 21:47:02 +00:00
|
|
|
EXPORT
|
2019-10-20 22:13:51 +00:00
|
|
|
void nvnc_set_name(struct nvnc* self, const char* name)
|
2019-08-29 21:47:02 +00:00
|
|
|
{
|
2020-04-12 16:08:33 +00:00
|
|
|
strncpy(self->name, name, sizeof(self->name));
|
|
|
|
self->name[sizeof(self->name) - 1] = '\0';
|
2019-08-12 22:11:41 +00:00
|
|
|
}
|
2020-01-25 15:35:14 +00:00
|
|
|
|
|
|
|
EXPORT
|
|
|
|
bool nvnc_has_auth(void)
|
|
|
|
{
|
|
|
|
#ifdef ENABLE_TLS
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
|
|
|
int nvnc_enable_auth(struct nvnc* self, const char* privkey_path,
|
|
|
|
const char* cert_path, nvnc_auth_fn auth_fn,
|
|
|
|
void* userdata)
|
|
|
|
{
|
|
|
|
#ifdef ENABLE_TLS
|
|
|
|
if (self->tls_creds)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Note: This is globally reference counted, so we don't need to worry
|
|
|
|
* about messing with other libraries.
|
|
|
|
*/
|
|
|
|
int rc = gnutls_global_init();
|
|
|
|
if (rc != GNUTLS_E_SUCCESS) {
|
2022-06-25 15:38:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_ERROR, "GnuTLS: Failed to initialise: %s",
|
2020-01-25 15:35:14 +00:00
|
|
|
gnutls_strerror(rc));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = gnutls_certificate_allocate_credentials(&self->tls_creds);
|
|
|
|
if (rc != GNUTLS_E_SUCCESS) {
|
2022-06-25 15:38:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_ERROR, "GnuTLS: Failed to allocate credentials: %s",
|
2020-01-25 15:35:14 +00:00
|
|
|
gnutls_strerror(rc));
|
|
|
|
goto cert_alloc_failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = gnutls_certificate_set_x509_key_file(
|
|
|
|
self->tls_creds, cert_path, privkey_path, GNUTLS_X509_FMT_PEM);
|
|
|
|
if (rc != GNUTLS_E_SUCCESS) {
|
2022-06-25 15:38:38 +00:00
|
|
|
nvnc_log(NVNC_LOG_ERROR, "GnuTLS: Failed to load credentials: %s",
|
2020-01-25 15:35:14 +00:00
|
|
|
gnutls_strerror(rc));
|
|
|
|
goto cert_set_failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->auth_fn = auth_fn;
|
|
|
|
self->auth_ud = userdata;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cert_set_failure:
|
|
|
|
gnutls_certificate_free_credentials(self->tls_creds);
|
|
|
|
self->tls_creds = NULL;
|
|
|
|
cert_alloc_failure:
|
|
|
|
gnutls_global_deinit();
|
|
|
|
#endif
|
|
|
|
return -1;
|
|
|
|
}
|
2022-02-06 15:43:45 +00:00
|
|
|
|
2023-08-24 10:14:37 +00:00
|
|
|
EXPORT
|
|
|
|
int nvnc_enable_auth2(struct nvnc* self, nvnc_auth_fn auth_fn, void* userdata)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_CRYPTO
|
|
|
|
self->auth_fn = auth_fn;
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-02-06 15:43:45 +00:00
|
|
|
EXPORT
|
2022-02-19 23:06:15 +00:00
|
|
|
void nvnc_set_cursor(struct nvnc* self, struct nvnc_fb* fb, uint16_t width,
|
2022-02-21 21:38:44 +00:00
|
|
|
uint16_t height, uint16_t hotspot_x, uint16_t hotspot_y,
|
|
|
|
bool is_damaged)
|
2022-02-06 15:43:45 +00:00
|
|
|
{
|
|
|
|
if (self->cursor.buffer) {
|
|
|
|
nvnc_fb_release(self->cursor.buffer);
|
|
|
|
nvnc_fb_unref(self->cursor.buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
self->cursor.buffer = fb;
|
2022-02-21 21:38:44 +00:00
|
|
|
|
2022-02-20 14:48:24 +00:00
|
|
|
if (fb) {
|
|
|
|
// TODO: Hash cursors to check if they actually changed?
|
|
|
|
nvnc_fb_ref(fb);
|
|
|
|
nvnc_fb_hold(fb);
|
|
|
|
|
|
|
|
self->cursor.width = width;
|
|
|
|
self->cursor.height = height;
|
|
|
|
self->cursor.hotspot_x = hotspot_x;
|
|
|
|
self->cursor.hotspot_y = hotspot_y;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
self->cursor.width = width;
|
|
|
|
self->cursor.height = height;
|
2022-02-19 23:06:15 +00:00
|
|
|
self->cursor.hotspot_x = 0;
|
|
|
|
self->cursor.hotspot_y = 0;
|
2022-02-06 15:43:45 +00:00
|
|
|
}
|
|
|
|
|
2022-02-21 21:38:44 +00:00
|
|
|
if (!is_damaged)
|
|
|
|
return;
|
|
|
|
|
2022-02-19 23:06:15 +00:00
|
|
|
self->cursor_seq++;
|
2022-02-06 15:43:45 +00:00
|
|
|
|
2022-02-19 23:06:15 +00:00
|
|
|
struct nvnc_client* client;
|
|
|
|
LIST_FOREACH(client, &self->clients, link)
|
|
|
|
process_fb_update_requests(client);
|
2022-02-06 15:43:45 +00:00
|
|
|
}
|