Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package neatvnc for openSUSE:Factory checked in at 2025-02-24 15:48:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/neatvnc (Old) and /work/SRC/openSUSE:Factory/.neatvnc.new.1873 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "neatvnc" Mon Feb 24 15:48:42 2025 rev:16 rq:1247934 version:0.9.3 Changes: -------- --- /work/SRC/openSUSE:Factory/neatvnc/neatvnc.changes 2025-01-07 20:55:08.317179062 +0100 +++ /work/SRC/openSUSE:Factory/.neatvnc.new.1873/neatvnc.changes 2025-02-24 15:48:58.403772366 +0100 @@ -1,0 +2,14 @@ +Sun Feb 23 18:23:21 UTC 2025 - Michael Vetter <[email protected]> + +- Update to 0.9.3: + * Fix some instances of use-after-free that can be reached before + authentication takes place. Those should be viewed as potential + vulnerabilities, so it would be prudent to upgrade ASAP if you're + running Neat VNC on the internet. + * Fix a few issues with WebSockets. One of those bugs will allow + an unauthenticated client to put the server into an endless + loop when parsing HTTP headers. There were also problems with + ping message handling and the way some legacy clients/browsers + were being dealt with that he fixed. + +------------------------------------------------------------------- Old: ---- neatvnc-0.9.2.tar.xz New: ---- neatvnc-0.9.3.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ neatvnc.spec ++++++ --- /var/tmp/diff_new_pack.sDwuBk/_old 2025-02-24 15:48:59.671825371 +0100 +++ /var/tmp/diff_new_pack.sDwuBk/_new 2025-02-24 15:48:59.671825371 +0100 @@ -19,7 +19,7 @@ %define libsoname libneatvnc0 Name: neatvnc -Version: 0.9.2 +Version: 0.9.3 Release: 0 Summary: A VNC server library License: ISC ++++++ _service ++++++ --- /var/tmp/diff_new_pack.sDwuBk/_old 2025-02-24 15:48:59.699826541 +0100 +++ /var/tmp/diff_new_pack.sDwuBk/_new 2025-02-24 15:48:59.699826541 +0100 @@ -3,8 +3,8 @@ <service name="obs_scm" mode="manual"> <param name="scm">git</param> <param name="url">https://github.com/any1/neatvnc.git</param> - <param name="revision">4c37ae9349f16a255cd3e95ed7931c71e6abf8fc</param> - <param name="versionformat">0.9.2</param> + <param name="revision">af5811b75e63f53d1d1f1f3f337387553a96786a</param> + <param name="versionformat">0.9.3</param> </service> <service name="tar" mode="manual"/> <service name="recompress" mode="manual"> ++++++ neatvnc-0.9.2.tar.xz -> neatvnc-0.9.3.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/include/common.h new/neatvnc-0.9.3/include/common.h --- old/neatvnc-0.9.2/include/common.h 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/include/common.h 2025-02-23 10:52:50.000000000 +0100 @@ -43,7 +43,6 @@ #define MAX_SECURITY_TYPES 32 enum nvnc_client_state { - VNC_CLIENT_STATE_ERROR = -1, VNC_CLIENT_STATE_WAITING_FOR_VERSION = 0, VNC_CLIENT_STATE_WAITING_FOR_SECURITY, #ifdef ENABLE_TLS @@ -82,7 +81,7 @@ size_t length; size_t index; bool is_zlib; - bool is_provide; + bool is_text_provide; }; struct nvnc_client { @@ -134,6 +133,7 @@ int32_t min_rtt; struct bwe* bwe; int32_t inflight_bytes; + struct aml_idle* close_task; #ifdef HAVE_CRYPTO struct crypto_key* apple_dh_secret; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/include/stream/stream.h new/neatvnc-0.9.3/include/stream/stream.h --- old/neatvnc-0.9.2/include/stream/stream.h 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/include/stream/stream.h 2025-02-23 10:52:50.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 Andri Yngvason + * Copyright (c) 2020 - 2025 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -78,6 +78,7 @@ struct stream { struct stream_impl *impl; + int ref; enum stream_state state; @@ -102,6 +103,8 @@ #endif struct stream* stream_new(int fd, stream_event_fn on_event, void* userdata); +void stream_init(struct stream* self); +void stream_ref(struct stream* self); int stream_close(struct stream* self); void stream_destroy(struct stream* self); ssize_t stream_read(struct stream* self, void* dst, size_t size); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/meson.build new/neatvnc-0.9.3/meson.build --- old/neatvnc-0.9.2/meson.build 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/meson.build 2025-02-23 10:52:50.000000000 +0100 @@ -1,7 +1,7 @@ project( 'neatvnc', 'c', - version: '0.9.2', + version: '0.9.3', license: 'ISC', default_options: [ 'c_std=gnu11', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/auth/apple-dh.c new/neatvnc-0.9.3/src/auth/apple-dh.c --- old/neatvnc-0.9.2/src/auth/apple-dh.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/auth/apple-dh.c 2025-02-23 10:52:50.000000000 +0100 @@ -98,13 +98,14 @@ update_min_rtt(client); - if (server->auth_fn(username, password, server->auth_ud)) { - security_handshake_ok(client, username); - client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT; - } else { + if (!server->auth_fn(username, password, server->auth_ud)) { security_handshake_failed(client, username, "Invalid username or password"); + return -1; } + security_handshake_ok(client, username); + client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT; + return sizeof(*msg) + key_len; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/auth/common.c new/neatvnc-0.9.3/src/auth/common.c --- old/neatvnc-0.9.2/src/auth/common.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/auth/common.c 2025-02-23 10:52:50.000000000 +0100 @@ -32,8 +32,6 @@ char buffer[256]; - client->state = VNC_CLIENT_STATE_ERROR; - uint32_t* result = (uint32_t*)buffer; struct rfb_error_reason* reason = @@ -45,8 +43,11 @@ size_t len = sizeof(*result) + sizeof(*reason) + strlen(reason_string); stream_write(client->net_stream, buffer, len, close_after_write, - client); + client->net_stream); + + stream_ref(client->net_stream); + nvnc_client_close(client); return 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/auth/rsa-aes.c new/neatvnc-0.9.3/src/auth/rsa-aes.c --- old/neatvnc-0.9.2/src/auth/rsa-aes.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/auth/rsa-aes.c 2025-02-23 10:52:50.000000000 +0100 @@ -134,9 +134,8 @@ client->rsa.challenge_len, 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; + return -1; } // ClientSessionKey = the first 16 bytes of SHA1(ServerRandom || ClientRandom) @@ -202,7 +201,7 @@ client_rsa_aes_hash_len(client), NULL, NULL); client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CLIENT_HASH; -done: + return sizeof(*msg) + length; } @@ -253,7 +252,7 @@ if (memcmp(msg, client_hash, client_rsa_aes_hash_len(client)) != 0) { nvnc_log(NVNC_LOG_INFO, "Client hash mismatch"); nvnc_client_close(client); - return 0; + return -1; } update_min_rtt(client); @@ -294,14 +293,15 @@ update_min_rtt(client); - if (server->auth_fn(username, password, server->auth_ud)) { - security_handshake_ok(client, username); - client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT; - } else { + if (!server->auth_fn(username, password, server->auth_ud)) { security_handshake_failed(client, username, "Invalid username or password"); + return -1; } + security_handshake_ok(client, username); + client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT; + return 2 + username_len + password_len; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/auth/vencrypt.c new/neatvnc-0.9.3/src/auth/vencrypt.c --- old/neatvnc-0.9.2/src/auth/vencrypt.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/auth/vencrypt.c 2025-02-23 10:52:50.000000000 +0100 @@ -28,8 +28,9 @@ static int send_byte_and_close(struct nvnc_client* client, uint8_t value) { + stream_ref(client->net_stream); return stream_write(client->net_stream, &value, 1, close_after_write, - client); + client->net_stream); } int vencrypt_send_version(struct nvnc_client* client) @@ -53,7 +54,7 @@ if (msg->major != 0 || msg->minor != 2) { security_handshake_failed(client, NULL, "Unsupported VeNCrypt version"); - return sizeof(*msg); + return -1; } send_byte(client, 0); @@ -80,7 +81,6 @@ enum rfb_vencrypt_subtype subtype = ntohl(*msg); if (subtype != RFB_VENCRYPT_X509_PLAIN) { - client->state = VNC_CLIENT_STATE_ERROR; send_byte_and_close(client, 0); return sizeof(*msg); } @@ -90,9 +90,8 @@ send_byte(client, 1); if (stream_upgrade_to_tls(client->net_stream, client->server->tls_creds) < 0) { - client->state = VNC_CLIENT_STATE_ERROR; nvnc_client_close(client); - return sizeof(*msg); + return -1; } client->state = VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_PLAIN_AUTH; @@ -127,14 +126,15 @@ update_min_rtt(client); - if (server->auth_fn(username, password, server->auth_ud)) { - security_handshake_ok(client, username); - client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT; - } else { + if (!server->auth_fn(username, password, server->auth_ud)) { security_handshake_failed(client, username, "Invalid username or password"); + return -1; } + security_handshake_ok(client, username); + client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT; + return sizeof(*msg) + ulen + plen; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/enc/tight.c new/neatvnc-0.9.3/src/enc/tight.c --- old/neatvnc-0.9.2/src/enc/tight.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/enc/tight.c 2025-02-23 10:52:50.000000000 +0100 @@ -374,7 +374,7 @@ int32_t xoff = x * bpp; uint8_t* img = addr + xoff + y * byte_stride; - enum TJSAMP subsampling = (quality == 9) ? TJSAMP_444 : TJSAMP_420; + enum TJSAMP subsampling = (self->quality == 9) ? TJSAMP_444 : TJSAMP_420; int rc = -1; rc = tjCompress2(handle, img, width, byte_stride, height, tjfmt, &buffer, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/server.c new/neatvnc-0.9.3/src/server.c --- old/neatvnc-0.9.2/src/server.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/server.c 2025-02-23 10:52:50.000000000 +0100 @@ -102,6 +102,7 @@ #endif extern const unsigned short code_map_qnum_to_linux[]; +extern const unsigned int code_map_qnum_to_linux_len; static uint64_t nvnc__htonll(uint64_t x) { @@ -163,6 +164,13 @@ static void client_close(struct nvnc_client* client) { + if (client->close_task) { + struct aml_idle* task = client->close_task; + client->close_task = NULL; + aml_stop(aml_get_default(), task); + aml_unref(task); + } + nvnc_log(NVNC_LOG_INFO, "Closing client connection %p", client); stream_close(client->net_stream); @@ -204,7 +212,9 @@ static void do_deferred_client_close(void *obj) { - client_close(obj); + struct nvnc_client* client = obj; + if (client->close_task) + client_close(client); } static void stop_self(void* obj) @@ -214,31 +224,23 @@ static void defer_client_close(struct nvnc_client* client) { - struct aml_idle* idle = aml_idle_new(stop_self, client, + if (client->close_task) + return; + client->close_task = aml_idle_new(stop_self, client, do_deferred_client_close); - aml_start(aml_get_default(), idle); - aml_unref(idle); + aml_start(aml_get_default(), client->close_task); } void close_after_write(void* userdata, enum stream_req_status status) { - struct nvnc_client* client = userdata; - nvnc_log(NVNC_LOG_DEBUG, "close_after_write(%p)", client); - 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 - */ - defer_client_close(client); + struct stream* stream = userdata; + stream_destroy(stream); } static int handle_unsupported_version(struct nvnc_client* client) { char buffer[256]; - client->state = VNC_CLIENT_STATE_ERROR; - struct rfb_error_reason* reason = (struct rfb_error_reason*)(buffer + 1); static const char reason_string[] = "Unsupported version\n"; @@ -249,9 +251,13 @@ size_t len = 1 + sizeof(*reason) + strlen(reason_string); stream_write(client->net_stream, buffer, len, close_after_write, - client); + client->net_stream); - return 0; + // Keep stream alive until the result has been sent to the client + stream_ref(client->net_stream); + + client_close(client); + return -1; } static void init_security_types(struct nvnc* server) @@ -417,7 +423,7 @@ } -static void send_server_init_message(struct nvnc_client* client) +static int send_server_init_message(struct nvnc_client* client) { struct nvnc* server = client->server; struct nvnc_display* display = server->display; @@ -471,10 +477,11 @@ client->known_width = width; client->known_height = height; - return; + return 0; close: nvnc_client_close(client); + return -1; } static int on_init_message(struct nvnc_client* client) @@ -488,7 +495,8 @@ update_min_rtt(client); - send_server_init_message(client); + if (send_server_init_message(client) == -1) + return -1; nvnc_client_fn fn = client->server->new_client_fn; if (fn) @@ -1043,7 +1051,14 @@ int down_flag = msg->down_flag; uint32_t xt_keycode = ntohl(msg->keycode); - uint32_t evdev_keycode = code_map_qnum_to_linux[xt_keycode]; + + uint32_t evdev_keycode = 0; + if (xt_keycode < code_map_qnum_to_linux_len) { + evdev_keycode = code_map_qnum_to_linux[xt_keycode]; + } else { + nvnc_log(NVNC_LOG_WARNING, "Received too large key code from client: %" PRIu32, + xt_keycode); + } if (!evdev_keycode) evdev_keycode = xt_keycode; @@ -1070,7 +1085,7 @@ nvnc_log(NVNC_LOG_WARNING, "Got uninterpretable qemu message from client: %p", client); nvnc_client_close(client); - return 0; + return -1; } static int on_client_pointer_event(struct nvnc_client* client) @@ -1175,7 +1190,7 @@ continue; } - if ((in_len == 0) || (*(in + 1) != '\n')) + if ((in_len == 1) || (*(in + 1) != '\n')) *out++ = '\n'; in++; @@ -1291,12 +1306,12 @@ nvnc_log(NVNC_LOG_ERROR, "Extended clipboard payload length (%d) is greater than max supported length (%d)", length, max_length); nvnc_client_close(client); - return 0; + return -1; } size_t msg_size = sizeof(*msg) + length; - /* this is expected to be a provide message. if not, tell + /* this is expected to be a text provide message. if not, tell * process_big_cut_text to ignore it, to avoid unnecessarily attempting * to inflate garbage */ if (msg_size > left_to_process) { @@ -1305,7 +1320,7 @@ if (!client->cut_text.buffer) { nvnc_log(NVNC_LOG_ERROR, "OOM: %m"); nvnc_client_close(client); - return 0; + return -1; } size_t partial_size = left_to_process - sizeof(*msg); @@ -1316,7 +1331,8 @@ client->cut_text.length = length; client->cut_text.index = partial_size; - client->cut_text.is_provide = (flags & RFB_EXT_CLIPBOARD_ACTION_PROVIDE && + client->cut_text.is_text_provide = (flags & RFB_EXT_CLIPBOARD_ACTION_PROVIDE && + flags & RFB_EXT_CLIPBOARD_FORMAT_TEXT && !(flags & RFB_EXT_CLIPBOARD_CAPS)); return left_to_process; @@ -1366,7 +1382,7 @@ nvnc_log(NVNC_LOG_ERROR, "Copied text length (%d) is greater than max supported length (%d)", length, max_length); nvnc_client_close(client); - return 0; + return -1; } size_t msg_size = sizeof(*msg) + length; @@ -1385,7 +1401,7 @@ if (!client->cut_text.buffer) { nvnc_log(NVNC_LOG_ERROR, "OOM: %m"); nvnc_client_close(client); - return 0; + return -1; } size_t partial_size = left_to_process - sizeof(*msg); @@ -1393,7 +1409,7 @@ memcpy(client->cut_text.buffer, msg->text, partial_size); client->cut_text.is_zlib = false; - client->cut_text.is_provide = false; + client->cut_text.is_text_provide = false; client->cut_text.length = length; client->cut_text.index = partial_size; @@ -1479,7 +1495,7 @@ return; if (client->cut_text.is_zlib) { - if (client->cut_text.is_provide) + if (client->cut_text.is_text_provide) process_client_ext_clipboard_provide(client, (unsigned char*)client->cut_text.buffer, client->cut_text.length); @@ -1522,6 +1538,7 @@ server->ext_clipboard_provide_msg.buffer = malloc(length); if (!server->ext_clipboard_provide_msg.buffer) { nvnc_log(NVNC_LOG_ERROR, "OOM: %m"); + free(provide_msg_buf); return; } @@ -1846,7 +1863,7 @@ nvnc_log(NVNC_LOG_WARNING, "Client sent too long fence message. Closing."); nvnc_client_close(client); - return 0; + return -1; } enum rfb_fence_flags flags = ntohl(msg->flags); @@ -1897,14 +1914,15 @@ nvnc_log(NVNC_LOG_WARNING, "Got uninterpretable message from client: %p", client); nvnc_client_close(client); - return 0; + return -1; } static int try_read_client_message(struct nvnc_client* client) { + if (client->net_stream->state == STREAM_STATE_CLOSED) + return -1; + switch (client->state) { - case VNC_CLIENT_STATE_ERROR: - return client->buffer_len - client->buffer_index; case VNC_CLIENT_STATE_WAITING_FOR_VERSION: return on_version_message(client); case VNC_CLIENT_STATE_WAITING_FOR_SECURITY: @@ -1981,6 +1999,9 @@ if (rc == 0) break; + if (rc == -1) + return; + client->buffer_index += rc; } @@ -2097,6 +2118,8 @@ nvnc_log(NVNC_LOG_DEBUG, "Don't know how to convert sa_family %d to string", addr->sa_family); + if (sz > 0) + *dst = 0; break; } } @@ -2431,6 +2454,7 @@ if (!client_supports_resizing(client)) { nvnc_log(NVNC_LOG_ERROR, "Display has been resized but client does not support resizing. Closing."); client_close(client); + return; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/stream/common.c new/neatvnc-0.9.3/src/stream/common.c --- old/neatvnc-0.9.2/src/stream/common.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/stream/common.c 2025-02-23 10:52:50.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 Andri Yngvason + * Copyright (c) 2020 - 2025 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,6 +19,11 @@ #include <stdlib.h> +void stream_init(struct stream* self) +{ + self->ref = 1; +} + void stream_req__finish(struct stream_req* req, enum stream_req_status status) { if (req->on_done) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/stream/gnutls.c new/neatvnc-0.9.3/src/stream/gnutls.c --- old/neatvnc-0.9.2/src/stream/gnutls.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/stream/gnutls.c 2025-02-23 10:52:50.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 Andri Yngvason + * Copyright (c) 2020 - 2025 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -54,6 +54,8 @@ self->base.state = STREAM_STATE_CLOSED; + stream_ref(&self->base); + while (!TAILQ_EMPTY(&self->base.send_queue)) { struct stream_req* req = TAILQ_FIRST(&self->base.send_queue); TAILQ_REMOVE(&self->base.send_queue, req, link); @@ -68,6 +70,9 @@ close(self->base.fd); self->base.fd = -1; + // unref + stream_destroy(&self->base); + return 0; } @@ -81,26 +86,31 @@ static int stream_gnutls__flush(struct stream* base) { struct stream_gnutls* self = (struct stream_gnutls*)base; + + stream_ref(base); + int rc = -1; + while (!TAILQ_EMPTY(&self->base.send_queue)) { assert(self->base.state != STREAM_STATE_CLOSED); struct stream_req* req = TAILQ_FIRST(&self->base.send_queue); - ssize_t rc = gnutls_record_send(self->session, + ssize_t n_sent = gnutls_record_send(self->session, req->payload->payload, req->payload->size); - if (rc < 0) { - if (gnutls_error_is_fatal(rc)) { + if (n_sent < 0) { + if (gnutls_error_is_fatal(n_sent)) { stream_close(base); - return -1; + goto done; } stream__poll_rw(base); - return 0; + rc = 0; + goto done; } - self->base.bytes_sent += rc; + self->base.bytes_sent += n_sent; - ssize_t remaining = req->payload->size - rc; + ssize_t remaining = req->payload->size - n_sent; if (remaining > 0) { char* p = req->payload->payload; @@ -108,7 +118,8 @@ memmove(p, p + s - remaining, remaining); req->payload->size = remaining; stream__poll_rw(base); - return 1; + rc = 1; + goto done; } assert(remaining == 0); @@ -120,7 +131,11 @@ if (TAILQ_EMPTY(&base->send_queue) && base->state != STREAM_STATE_CLOSED) stream__poll_r(base); - return 1; + rc = 1; +done: + // unref + stream_destroy(base); + return rc; } static void stream_gnutls__on_readable(struct stream* self) @@ -161,22 +176,26 @@ struct stream* self = aml_get_userdata(obj); uint32_t events = aml_get_revents(obj); + stream_ref(self); + if (events & AML_EVENT_READ) stream_gnutls__on_readable(self); if (events & AML_EVENT_WRITE) stream_gnutls__on_writable(self); + + stream_destroy(self); } static int stream_gnutls_send(struct stream* self, struct rcbuf* payload, stream_req_fn on_done, void* userdata) { if (self->state == STREAM_STATE_CLOSED) - return -1; + goto failure; struct stream_req* req = calloc(1, sizeof(*req)); if (!req) - return -1; + goto failure; req->payload = payload; req->on_done = on_done; @@ -185,6 +204,10 @@ TAILQ_INSERT_TAIL(&self->send_queue, req, link); return stream_gnutls__flush(self); + +failure: + rcbuf_unref(payload); + return -1; } static ssize_t stream_gnutls_read(struct stream* base, void* dst, size_t size) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/stream/interface.c new/neatvnc-0.9.3/src/stream/interface.c --- old/neatvnc-0.9.2/src/stream/interface.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/stream/interface.c 2025-02-23 10:52:50.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Andri Yngvason + * Copyright (c) 2023 - 2025 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,6 +18,11 @@ #include <assert.h> +void stream_ref(struct stream* self) +{ + self->ref++; +} + int stream_close(struct stream* self) { assert(self->impl && self->impl->close); @@ -27,7 +32,8 @@ void stream_destroy(struct stream* self) { assert(self->impl && self->impl->destroy); - return self->impl->destroy(self); + if (--self->ref == 0) + return self->impl->destroy(self); } int stream_send(struct stream* self, struct rcbuf* payload, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/stream/tcp.c new/neatvnc-0.9.3/src/stream/tcp.c --- old/neatvnc-0.9.2/src/stream/tcp.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/stream/tcp.c 2025-02-23 10:52:50.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 Andri Yngvason + * Copyright (c) 2020 - 2025 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -45,6 +45,8 @@ self->state = STREAM_STATE_CLOSED; self->cork = true; + stream_ref(self); + while (!TAILQ_EMPTY(&self->send_queue)) { struct stream_req* req = TAILQ_FIRST(&self->send_queue); TAILQ_REMOVE(&self->send_queue, req, link); @@ -55,6 +57,9 @@ close(self->fd); self->fd = -1; + // unref + stream_destroy(self); + return 0; } @@ -120,6 +125,8 @@ // Don't flush while flushing self->cork = true; + stream_ref(self); + struct stream_req* tmp; TAILQ_FOREACH_SAFE(req, &self->send_queue, link, tmp) { bytes_left -= req->payload->size; @@ -151,6 +158,9 @@ assert(bytes_left <= 0); + // unref + stream_destroy(self); + return bytes_sent; } @@ -186,11 +196,18 @@ struct stream* self = aml_get_userdata(obj); uint32_t events = aml_get_revents(obj); + // We hold a reference here in case the stream gets destroyed inside + // callback. + stream_ref(self); + if (events & AML_EVENT_READ) stream_tcp__on_readable(self); if (events & AML_EVENT_WRITE) stream_tcp__on_writable(self); + + // unref + stream_destroy(self); } ssize_t stream_tcp_read(struct stream* self, void* dst, size_t size) @@ -218,11 +235,11 @@ stream_req_fn on_done, void* userdata) { if (self->state == STREAM_STATE_CLOSED) - return -1; + goto failure; struct stream_req* req = calloc(1, sizeof(*req)); if (!req) - return -1; + goto failure; req->payload = payload; req->on_done = on_done; @@ -231,21 +248,29 @@ TAILQ_INSERT_TAIL(&self->send_queue, req, link); return stream_tcp__flush(self); + +failure: + rcbuf_unref(payload); + return -1; } int stream_tcp_send_first(struct stream* self, struct rcbuf* payload) { if (self->state == STREAM_STATE_CLOSED) - return -1; + goto failure; struct stream_req* req = calloc(1, sizeof(*req)); if (!req) - return -1; + goto failure; req->payload = payload; TAILQ_INSERT_HEAD(&self->send_queue, req, link); return stream_tcp__flush(self); + +failure: + rcbuf_unref(payload); + return -1; } void stream_tcp_exec_and_send(struct stream* self, @@ -309,11 +334,12 @@ if (!self) return NULL; + stream_init(self); + if (stream_tcp_init(self, fd, on_event, userdata) < 0) { free(self); return NULL; } return self; - } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/stream/ws/handshake.c new/neatvnc-0.9.3/src/stream/ws/handshake.c --- old/neatvnc-0.9.2/src/stream/ws/handshake.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/stream/ws/handshake.c 2025-02-23 10:52:50.000000000 +0100 @@ -29,9 +29,11 @@ static void tolower_and_remove_ws(char* dst, const char* src) { - while (*src) + while (*src) { if (!isspace(*src)) - *dst++ = tolower(*src++); + *dst++ = tolower(*src); + src++; + } *dst = '\0'; } @@ -70,7 +72,7 @@ bool have_protocols = strlen(protocols) != 1; bool have_versions = strlen(versions) != 1; - if (have_protocols && !strstr(protocols, ",chat,")) + if (have_protocols && !strstr(protocols, ",binary,")) goto failure; if (have_versions && !strstr(versions, ",13,")) @@ -95,7 +97,7 @@ "%s%s" "\r\n", response, - have_protocols ? "Sec-WebSocket-Protocol: char\r\n" : "", + have_protocols ? "Sec-WebSocket-Protocol: binary\r\n" : "", have_versions ? "Sec-WebSocket-Version: 13\r\n" : ""); ssize_t header_len = req.header_length; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/neatvnc-0.9.2/src/stream/ws/ws.c new/neatvnc-0.9.3/src/stream/ws/ws.c --- old/neatvnc-0.9.2/src/stream/ws/ws.c 2024-12-07 21:42:23.000000000 +0100 +++ new/neatvnc-0.9.3/src/stream/ws/ws.c 2025-02-23 10:52:50.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Andri Yngvason + * Copyright (c) 2023 - 2025 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -95,11 +95,14 @@ NULL, NULL); } - int payload_len = MIN(ws->read_index, ws->header.payload_length); + int payload_len = MIN(ws->read_index - offset, ws->header.payload_length); - // Feed back the payload: - stream_tcp_send(&ws->base, rcbuf_from_mem(ws->read_buffer + offset, - payload_len), NULL, NULL); + // Feed back the (unmasked) payload: + struct rcbuf* rcbuf = rcbuf_new(malloc(payload_len), payload_len); + assert(rcbuf && rcbuf->payload); + ws_copy_payload(&ws->header, rcbuf->payload, ws->read_buffer + offset, + payload_len); + stream_tcp_send(&ws->base, rcbuf, NULL, NULL); stream_ws_advance_read_buffer(ws, payload_len, offset); return 0; @@ -304,6 +307,8 @@ if (!self) return NULL; + stream_init(&self->base); + stream_tcp_init(&self->base, fd, on_event, userdata); self->base.impl = &impl; ++++++ neatvnc.obsinfo ++++++ --- /var/tmp/diff_new_pack.sDwuBk/_old 2025-02-24 15:48:59.879834066 +0100 +++ /var/tmp/diff_new_pack.sDwuBk/_new 2025-02-24 15:48:59.883834232 +0100 @@ -1,5 +1,5 @@ name: neatvnc -version: 0.9.2 -mtime: 1733604143 -commit: 4c37ae9349f16a255cd3e95ed7931c71e6abf8fc +version: 0.9.3 +mtime: 1740304370 +commit: af5811b75e63f53d1d1f1f3f337387553a96786a
