diff options
author | Dan McDonald <danmcd@joyent.com> | 2022-04-25 08:23:27 -0400 |
---|---|---|
committer | Dan McDonald <danmcd@joyent.com> | 2022-04-25 08:23:27 -0400 |
commit | b7cc77ad34467677e66c1a06f3d717c4ea632bc5 (patch) | |
tree | 10934aa0fdb2ea01ada9896e382658d81634eb07 | |
parent | 309baf317c94c37e7e9110980b39367afa97ec65 (diff) | |
parent | 1e6b83029f8d7ea1ade06314dc14e2fbd0cd2bcb (diff) | |
download | illumos-joyent-b7cc77ad34467677e66c1a06f3d717c4ea632bc5.tar.gz |
[illumos-gate merge]
commit 1e6b83029f8d7ea1ade06314dc14e2fbd0cd2bcb
14633 lib9p: unlinkat() does not work on 9p share
commit 104fd2955714fa4ea6ed431b6b5cf2eb0766ecad
14623 bhyve/xhci: Connecting device on port 1 failed
commit 77570342d4bf7e1e439bf1b8008312de7f031a0a
14653 loader: zfs: handle holes at the tail end correctly
commit 1aa1f41fe10c6220a7fbef328fac1b72a8355a01
14495 bhyve VNC server should support alternate pixel formats
14494 bhyve VNC server could support multiple connections
14505 libumem's umem_update thread could be named
14506 libidspace does not expose id_allocff()
14640 bhyve VNC authentication fails with openssl3
Conflicts:
usr/src/lib/libidspace/common/mapfile-vers
usr/src/cmd/bhyve/Makefile
-rw-r--r-- | exception_lists/copyright | 1 | ||||
-rw-r--r-- | exception_lists/cstyle | 1 | ||||
-rw-r--r-- | exception_lists/hdrchk | 1 | ||||
-rw-r--r-- | exception_lists/wscheck | 3 | ||||
-rw-r--r-- | usr/src/boot/Makefile.version | 2 | ||||
-rw-r--r-- | usr/src/boot/libsa/zfs/zfsimpl.c | 13 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/Makefile | 11 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/bhyvegc.c | 20 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/bhyvegc.h | 7 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/bhyverun.c | 2 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/pci_fbuf.c | 6 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/pci_xhci.c | 10 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/rfb.c | 2301 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/rfb.h | 12 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/rfb_impl.h | 252 | ||||
-rw-r--r-- | usr/src/lib/lib9p/common/backend/fs.c | 10 | ||||
-rw-r--r-- | usr/src/lib/lib9p/common/request.c | 6 | ||||
-rw-r--r-- | usr/src/lib/libidspace/common/libidspace.h | 2 | ||||
-rw-r--r-- | usr/src/lib/libidspace/common/mapfile-vers | 4 | ||||
-rw-r--r-- | usr/src/lib/libumem/common/umem_update_thread.c | 1 | ||||
-rw-r--r-- | usr/src/man/man5/bhyve_config.5 | 12 |
21 files changed, 1583 insertions, 1094 deletions
diff --git a/exception_lists/copyright b/exception_lists/copyright index 12e0280517..408cdcf7e8 100644 --- a/exception_lists/copyright +++ b/exception_lists/copyright @@ -549,7 +549,6 @@ usr/src/cmd/bhyve/pmtmr.c usr/src/cmd/bhyve/post.c usr/src/cmd/bhyve/ps2kbd.[ch] usr/src/cmd/bhyve/ps2mouse.[ch] -usr/src/cmd/bhyve/rfb.[ch] usr/src/cmd/bhyve/rtc.[ch] usr/src/cmd/bhyve/smbiostbl.[ch] usr/src/cmd/bhyve/sockstream.[ch] diff --git a/exception_lists/cstyle b/exception_lists/cstyle index 21cd3dd186..3e3f817bce 100644 --- a/exception_lists/cstyle +++ b/exception_lists/cstyle @@ -1403,7 +1403,6 @@ usr/src/cmd/bhyve/pm.c usr/src/cmd/bhyve/post.c usr/src/cmd/bhyve/ps2kbd.[ch] usr/src/cmd/bhyve/ps2mouse.[ch] -usr/src/cmd/bhyve/rfb.[ch] usr/src/cmd/bhyve/rtc.[ch] usr/src/cmd/bhyve/smbiostbl.[ch] usr/src/cmd/bhyve/sockstream.[ch] diff --git a/exception_lists/hdrchk b/exception_lists/hdrchk index bf33421d86..7c0313395e 100644 --- a/exception_lists/hdrchk +++ b/exception_lists/hdrchk @@ -419,7 +419,6 @@ usr/src/cmd/bhyve/pci_lpc.h usr/src/cmd/bhyve/pctestdev.h usr/src/cmd/bhyve/ps2kbd.h usr/src/cmd/bhyve/ps2mouse.h -usr/src/cmd/bhyve/rfb.h usr/src/cmd/bhyve/rtc.h usr/src/cmd/bhyve/smbiostbl.h usr/src/cmd/bhyve/sockstream.h diff --git a/exception_lists/wscheck b/exception_lists/wscheck index 90e43bba23..deb2697cdc 100644 --- a/exception_lists/wscheck +++ b/exception_lists/wscheck @@ -10,7 +10,7 @@ # # Copyright 2019 Joyent, Inc. # Copyright 2020 Oxide Computer Company -# Copyright 2021 OmniOS Community Edition (OmniOSce) Association. +# Copyright 2022 OmniOS Community Edition (OmniOSce) Association. # syntax: glob @@ -171,7 +171,6 @@ usr/src/cmd/bhyve/pmtmr.c usr/src/cmd/bhyve/post.c usr/src/cmd/bhyve/ps2kbd.[ch] usr/src/cmd/bhyve/ps2mouse.[ch] -usr/src/cmd/bhyve/rfb.[ch] usr/src/cmd/bhyve/rtc.[ch] usr/src/cmd/bhyve/smbiostbl.[ch] usr/src/cmd/bhyve/sockstream.[ch] diff --git a/usr/src/boot/Makefile.version b/usr/src/boot/Makefile.version index 6e0fa00d86..f0ea00a876 100644 --- a/usr/src/boot/Makefile.version +++ b/usr/src/boot/Makefile.version @@ -34,4 +34,4 @@ LOADER_VERSION = 1.1 # Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes. # The version is processed from left to right, the version number can only # be increased. -BOOT_VERSION = $(LOADER_VERSION)-2022.01.25.1 +BOOT_VERSION = $(LOADER_VERSION)-2022.04.21.1 diff --git a/usr/src/boot/libsa/zfs/zfsimpl.c b/usr/src/boot/libsa/zfs/zfsimpl.c index e83a8a3983..f2c5ca82d6 100644 --- a/usr/src/boot/libsa/zfs/zfsimpl.c +++ b/usr/src/boot/libsa/zfs/zfsimpl.c @@ -2348,6 +2348,19 @@ dnode_read(const spa_t *spa, const dnode_phys_t *dnode, off_t offset, } /* + * Handle odd block sizes, mirrors dmu_read_impl(). Data can't exist + * past the first block, so we'll clip the read to the portion of the + * buffer within bsize and zero out the remainder. + */ + if (dnode->dn_maxblkid == 0) { + size_t newbuflen; + + newbuflen = offset > bsize ? 0 : MIN(buflen, bsize - offset); + bzero((char *)buf + newbuflen, buflen - newbuflen); + buflen = newbuflen; + } + + /* * Note: bsize may not be a power of two here so we need to do an * actual divide rather than a bitshift. */ diff --git a/usr/src/cmd/bhyve/Makefile b/usr/src/cmd/bhyve/Makefile index e006bc3434..6de5407015 100644 --- a/usr/src/cmd/bhyve/Makefile +++ b/usr/src/cmd/bhyve/Makefile @@ -146,18 +146,23 @@ pci_xhci.o := CERRWARN += -_gcc11=-Wno-address-of-packed-member SMOFF += all_func_returns,leaks,no_if_block +rfb.o := SMOFF= + # Force c99 for everything CSTD= $(CSTD_GNU99) $(PROG) := LDLIBS += \ -l9p \ - -lsocket \ - -lnsl \ - -ldlpi \ + -lcmdutils \ -ldladm \ + -ldlpi \ + -lidspace \ -lmd \ + -lnsl \ -lnvpair \ + -lsocket \ -lsunw_crypto \ + -lumem \ -luuid \ -lvmmapi \ -lz diff --git a/usr/src/cmd/bhyve/bhyvegc.c b/usr/src/cmd/bhyve/bhyvegc.c index 4bd49ded79..3aa3bac210 100644 --- a/usr/src/cmd/bhyve/bhyvegc.c +++ b/usr/src/cmd/bhyve/bhyvegc.c @@ -35,6 +35,10 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <string.h> +#ifndef __FreeBSD__ +#include <pthread.h> +#endif + #include "bhyvegc.h" struct bhyvegc { @@ -63,16 +67,26 @@ bhyvegc_init(int width, int height, void *fbaddr) gc->gc_image = gc_image; +#ifndef __FreeBSD__ + pthread_mutex_init(&gc_image->mtx, NULL); +#endif + return (gc); } void bhyvegc_set_fbaddr(struct bhyvegc *gc, void *fbaddr) { +#ifndef __FreeBSD__ + pthread_mutex_lock(&gc->gc_image->mtx); +#endif gc->raw = 1; if (gc->gc_image->data && gc->gc_image->data != fbaddr) free(gc->gc_image->data); gc->gc_image->data = fbaddr; +#ifndef __FreeBSD__ + pthread_mutex_unlock(&gc->gc_image->mtx); +#endif } void @@ -80,6 +94,9 @@ bhyvegc_resize(struct bhyvegc *gc, int width, int height) { struct bhyvegc_image *gc_image; +#ifndef __FreeBSD__ + pthread_mutex_lock(&gc->gc_image->mtx); +#endif gc_image = gc->gc_image; gc_image->width = width; @@ -91,6 +108,9 @@ bhyvegc_resize(struct bhyvegc *gc, int width, int height) memset(gc_image->data, 0, width * height * sizeof (uint32_t)); } +#ifndef __FreeBSD__ + pthread_mutex_unlock(&gc->gc_image->mtx); +#endif } struct bhyvegc_image * diff --git a/usr/src/cmd/bhyve/bhyvegc.h b/usr/src/cmd/bhyve/bhyvegc.h index 11323586df..0b68bfad9d 100644 --- a/usr/src/cmd/bhyve/bhyvegc.h +++ b/usr/src/cmd/bhyve/bhyvegc.h @@ -31,6 +31,10 @@ #ifndef _BHYVEGC_H_ #define _BHYVEGC_H_ +#ifndef __FreeBSD__ +#include <pthread.h> +#endif + struct bhyvegc; struct bhyvegc_image { @@ -38,6 +42,9 @@ struct bhyvegc_image { int width; int height; uint32_t *data; +#ifndef __FreeBSD__ + pthread_mutex_t mtx; +#endif }; struct bhyvegc *bhyvegc_init(int width, int height, void *fbaddr); diff --git a/usr/src/cmd/bhyve/bhyverun.c b/usr/src/cmd/bhyve/bhyverun.c index a6819efb97..3b026f22a0 100644 --- a/usr/src/cmd/bhyve/bhyverun.c +++ b/usr/src/cmd/bhyve/bhyverun.c @@ -1692,8 +1692,6 @@ main(int argc, char *argv[]) } #endif - vga_init(1); - if (lpc_bootrom()) { #ifdef __FreeBSD__ if (vm_set_capability(ctx, BSP, VM_CAP_UNRESTRICTED_GUEST, 1)) { diff --git a/usr/src/cmd/bhyve/pci_fbuf.c b/usr/src/cmd/bhyve/pci_fbuf.c index fad064ebf8..5a17b1e618 100644 --- a/usr/src/cmd/bhyve/pci_fbuf.c +++ b/usr/src/cmd/bhyve/pci_fbuf.c @@ -434,6 +434,10 @@ pci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) /* XXX until VGA rendering is enabled */ if (sc->vga_full != 0) { EPRINTLN("pci_fbuf: VGA rendering not enabled"); +#ifndef __FreeBSD__ + errno = ENOTSUP; + error = -1; +#endif goto done; } @@ -459,7 +463,7 @@ pci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) (void) asprintf(&name, "%s (bhyve)", get_config_value("name")); if (sc->rfb_unix != NULL) { - error = rfb_init_unix(sc->rfb_unix, sc->rfb_wait, + error = rfb_init((char *)sc->rfb_unix, -1, sc->rfb_wait, sc->rfb_password, name != NULL ? name : "bhyve"); } else { error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait, diff --git a/usr/src/cmd/bhyve/pci_xhci.c b/usr/src/cmd/bhyve/pci_xhci.c index 97ce582772..0b9fde3208 100644 --- a/usr/src/cmd/bhyve/pci_xhci.c +++ b/usr/src/cmd/bhyve/pci_xhci.c @@ -483,8 +483,13 @@ pci_xhci_portregs_write(struct pci_xhci_softc *sc, uint64_t offset, oldpls = XHCI_PS_PLS_GET(p->portsc); newpls = XHCI_PS_PLS_GET(value); +#ifndef __FreeBSD__ + p->portsc &= XHCI_PS_PED | XHCI_PS_PP | XHCI_PS_PLS_MASK | + XHCI_PS_SPEED_MASK | XHCI_PS_PIC_MASK; +#else p->portsc &= XHCI_PS_PED | XHCI_PS_PLS_MASK | XHCI_PS_SPEED_MASK | XHCI_PS_PIC_MASK; +#endif if (XHCI_DEVINST_PTR(sc, port)) p->portsc |= XHCI_PS_CCS; @@ -2852,6 +2857,11 @@ pci_xhci_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) struct pci_xhci_softc *sc; int error; +#ifndef __FreeBSD__ + if (get_config_bool_default("xhci.debug", false)) + xhci_debug = 1; +#endif + if (xhci_in_use) { WPRINTF(("pci_xhci controller already defined")); return (-1); diff --git a/usr/src/cmd/bhyve/rfb.c b/usr/src/cmd/bhyve/rfb.c index 86b0d6d456..ab209aae36 100644 --- a/usr/src/cmd/bhyve/rfb.c +++ b/usr/src/cmd/bhyve/rfb.c @@ -4,7 +4,6 @@ * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> * Copyright (c) 2015 Leon Dang * Copyright 2020 Joyent, Inc. - * Copyright 2021 OmniOS Community Edition (OmniOSce) Association. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,1353 +27,1523 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. + */ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#ifndef WITHOUT_CAPSICUM -#include <sys/capsicum.h> -#endif -#include <sys/endian.h> -#include <sys/socket.h> -#include <sys/select.h> -#include <sys/time.h> -#include <arpa/inet.h> -#include <stdatomic.h> -#include <machine/cpufunc.h> -#include <machine/specialreg.h> -#include <netinet/in.h> -#include <netdb.h> +/* + * References to the RFB protocol specification refer to: + * - [1] https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst + */ -#include <assert.h> -#ifndef WITHOUT_CAPSICUM -#include <capsicum_helpers.h> -#endif #include <err.h> #include <errno.h> +#include <libidspace.h> +#include <netdb.h> #include <pthread.h> #include <pthread_np.h> #include <signal.h> +#include <stdatomic.h> #include <stdbool.h> -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <sysexits.h> #include <unistd.h> - #include <zlib.h> - -#ifndef __FreeBSD__ +#include <machine/cpufunc.h> +#include <machine/specialreg.h> +#include <netinet/in.h> +#ifndef NO_OPENSSL +#include <openssl/des.h> +#endif #include <sys/debug.h> +#include <sys/endian.h> +#include <sys/list.h> +#include <sys/socket.h> +#include <sys/types.h> +#ifndef WITHOUT_CAPSICUM +#include <sysexits.h> +#include <sys/capsicum.h> +#include <capsicum_helpers.h> #endif #include "bhyvegc.h" +#include "config.h" #include "debug.h" #include "console.h" #include "rfb.h" +#include "rfb_impl.h" #include "sockstream.h" -#ifndef NO_OPENSSL -#include <openssl/des.h> -#endif +static uint_t rfb_debug = 0; +static list_t rfb_list; +static id_space_t *rfb_idspace; + +static bool rfb_sse42; +static pthread_once_t rfb_once = PTHREAD_ONCE_INIT; + +extern int raw_stdio; + +static void rfb_send_extended_keyevent_update_msg(rfb_client_t *); + +static void +rfb_printf(rfb_client_t *c, rfb_loglevel_t level, const char *fmt, ...) +{ + FILE *fp = stdout; + va_list ap; + + switch (level) { + case RFB_LOGDEBUG: + if (rfb_debug == 0) + return; + /* FALLTHROUGH */ + case RFB_LOGERR: + fp = stderr; + /* FALLTHROUGH */ + case RFB_LOGWARN: + if (c != NULL) + (void) fprintf(fp, "rfb%u: ", c->rc_instance); + else + (void) fprintf(fp, "rfb: "); + va_start(ap, fmt); + (void) vfprintf(fp, fmt, ap); + va_end(ap); + if (raw_stdio) + (void) fprintf(fp, "\r\n"); + else + (void) fprintf(fp, "\n"); + (void) fflush(fp); + } +} + +static void +rfb_init_once(void) +{ + uint_t cpu_registers[4], ecx; + + do_cpuid(1, cpu_registers); + ecx = cpu_registers[2]; + rfb_sse42 = (ecx & CPUID2_SSE42) != 0; + + if (rfb_sse42) + rfb_printf(NULL, RFB_LOGDEBUG, "enabled fast crc32"); + else + rfb_printf(NULL, RFB_LOGWARN, "no support for fast crc32"); + + if (get_config_bool_default("rfb.debug", false)) + rfb_debug = 1; + + list_create(&rfb_list, sizeof (rfb_server_t), + offsetof(rfb_server_t, rs_node)); + + rfb_idspace = id_space_create("rfb", 0, INT32_MAX); +} + +static void +rfb_free_client(rfb_client_t *c) +{ + free(c->rc_crc); + free(c->rc_crc_tmp); + free(c->rc_zbuf); + free(c->rc_gci.data); + + if (c->rc_encodings & RFB_ENCODING_ZLIB) + (void) deflateEnd(&c->rc_zstream); + + if (c->rc_fd != -1) + (void) close(c->rc_fd); + + free(c); +} -/* Delays in microseconds */ -#define CFD_SEL_DELAY 10000 -#define SCREEN_REFRESH_DELAY 33300 /* 30Hz */ -#define SCREEN_POLL_DELAY (SCREEN_REFRESH_DELAY / 2) +/* + * Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only + */ +static inline uint32_t +fast_crc32(void *buf, int len, uint32_t crcval) +{ + uint32_t q = len / sizeof (uint32_t); + uint32_t *p = (uint32_t *)buf; -static int rfb_debug = 0; -#define DPRINTF(params) if (rfb_debug) PRINTLN params -#define WPRINTF(params) PRINTLN params + while (q--) { + /* BEGIN CSTYLED */ + asm volatile ( + /* crc32l %ecx,%esi */ + ".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;" + :"=S" (crcval) + :"0" (crcval), "c" (*p) + ); + /* END CSTYLED */ + p++; + } + + return (crcval); +} -#define VERSION_LENGTH 12 -#define AUTH_LENGTH 16 -#define PASSWD_LENGTH 8 +static void +rfb_send_client_status(rfb_client_t *c, uint32_t status, const char *msg) +{ + status = htonl(status); -/* Protocol versions */ -#define CVERS_3_3 '3' -#define CVERS_3_7 '7' -#define CVERS_3_8 '8' + (void) stream_write(c->rc_fd, &status, sizeof (status)); -/* Client-to-server msg types */ -#define CS_SET_PIXEL_FORMAT 0 -#define CS_SET_ENCODINGS 2 -#define CS_UPDATE_MSG 3 -#define CS_KEY_EVENT 4 -#define CS_POINTER_EVENT 5 -#define CS_CUT_TEXT 6 -#define CS_MSG_CLIENT_QEMU 255 + if (msg != NULL && status != 0 && c->rc_cver == RFB_CVER_3_8) { + char buf[4]; -#define SECURITY_TYPE_NONE 1 -#define SECURITY_TYPE_VNC_AUTH 2 + rfb_printf(c, RFB_LOGWARN, msg); -#define AUTH_FAILED_UNAUTH 1 -#define AUTH_FAILED_ERROR 2 + be32enc(buf, strlen((char *)msg)); + (void) stream_write(c->rc_fd, buf, 4); + (void) stream_write(c->rc_fd, msg, strlen((char *)msg)); + } +} -struct rfb_softc { - int sfd; - pthread_t tid; +static bool +rfb_handshake_version(rfb_client_t *c) +{ + unsigned char buf[RFB_VERSION_LEN]; + ssize_t l; - int cfd; + rfb_printf(c, RFB_LOGDEBUG, "handshake version"); - int width, height; + if (stream_write(c->rc_fd, RFB_VERSION, RFB_VERSION_LEN) != + RFB_VERSION_LEN) { + rfb_printf(c, RFB_LOGWARN, "could not send server version."); + return (false); + } - char *password; + l = stream_read(c->rc_fd, buf, sizeof (buf)); + if (l <= 0) { + rfb_printf(c, RFB_LOGWARN, "client version not read"); + return (false); + } else if (l != RFB_VERSION_LEN) { + rfb_printf(c, RFB_LOGWARN, "client sent short version - '%.*s'", + l, buf); + return (false); + } - bool enc_raw_ok; - bool enc_zlib_ok; - bool enc_resize_ok; - bool enc_extkeyevent_ok; + rfb_printf(c, RFB_LOGDEBUG, "version handshake, client ver '%.*s'", + l - 1, buf); - bool enc_extkeyevent_send; + if (strncmp(RFB_VERSION, (char *)buf, RFB_VERSION_LEN - 2) != 0) { + rfb_printf(c, RFB_LOGERR, "bad client version '%.*s'", l, buf); + return (false); + } - z_stream zstream; - uint8_t *zbuf; - int zbuflen; + switch (buf[RFB_VERSION_LEN - 2]) { + case '8': + c->rc_cver = RFB_CVER_3_8; + break; + case '7': + c->rc_cver = RFB_CVER_3_7; + break; + case '5': + /* + * From the RFB specification[1], section 7.1.1: + * "version 3.5 was wrongly reported by some clients, but this + * should be interpreted by all servers as 3.3." + */ + case '3': + c->rc_cver = RFB_CVER_3_3; + break; + default: + rfb_printf(c, RFB_LOGERR, "unsupported client version '%.*s'", + l - 1, buf); + return (false); + } - int conn_wait; - int wrcount; + return (true); +} - atomic_bool sending; - atomic_bool pending; - atomic_bool update_all; - atomic_bool input_detected; +static bool +rfb_handshake_auth(rfb_client_t *c) +{ + unsigned char buf[RFBP_SECURITY_VNC_AUTH_LEN]; + int auth_type; - pthread_mutex_t mtx; - pthread_cond_t cond; + rfb_printf(c, RFB_LOGDEBUG, "handshake auth"); - int hw_crc; - uint32_t *crc; /* WxH crc cells */ - uint32_t *crc_tmp; /* buffer to store single crc row */ - int crc_width, crc_height; -#ifndef __FreeBSD__ - const char *name; + auth_type = RFBP_SECURITY_NONE; +#ifndef NO_OPENSSL + if (c->rc_s->rs_password != NULL) + auth_type = RFBP_SECURITY_VNC_AUTH; #endif -}; - -struct rfb_pixfmt { - uint8_t bpp; - uint8_t depth; - uint8_t bigendian; - uint8_t truecolor; - uint16_t red_max; - uint16_t green_max; - uint16_t blue_max; - uint8_t red_shift; - uint8_t green_shift; - uint8_t blue_shift; - uint8_t pad[3]; -}; - -struct rfb_srvr_info { - uint16_t width; - uint16_t height; - struct rfb_pixfmt pixfmt; - uint32_t namelen; -}; - -struct rfb_pixfmt_msg { - uint8_t type; - uint8_t pad[3]; - struct rfb_pixfmt pixfmt; -}; - -#define RFB_ENCODING_RAW 0 -#define RFB_ENCODING_ZLIB 6 -#define RFB_ENCODING_RESIZE -223 -#define RFB_ENCODING_EXT_KEYEVENT -258 - -#define RFB_CLIENTMSG_EXT_KEYEVENT 0 - -#define RFB_MAX_WIDTH 2000 -#define RFB_MAX_HEIGHT 1200 -#define RFB_ZLIB_BUFSZ RFB_MAX_WIDTH*RFB_MAX_HEIGHT*4 - -/* percentage changes to screen before sending the entire screen */ -#define RFB_SEND_ALL_THRESH 25 - -struct rfb_enc_msg { - uint8_t type; - uint8_t pad; - uint16_t numencs; -}; - -struct rfb_updt_msg { - uint8_t type; - uint8_t incremental; - uint16_t x; - uint16_t y; - uint16_t width; - uint16_t height; -}; - -struct rfb_key_msg { - uint8_t type; - uint8_t down; - uint16_t pad; - uint32_t sym; -}; - -struct rfb_client_msg { - uint8_t type; - uint8_t subtype; -}; - -struct rfb_extended_key_msg { - uint8_t type; - uint8_t subtype; - uint16_t down; - uint32_t sym; - uint32_t code; -}; - -struct rfb_ptr_msg { - uint8_t type; - uint8_t button; - uint16_t x; - uint16_t y; -}; - -struct rfb_srvr_updt_msg { - uint8_t type; - uint8_t pad; - uint16_t numrects; -}; - -struct rfb_srvr_rect_hdr { - uint16_t x; - uint16_t y; - uint16_t width; - uint16_t height; - uint32_t encoding; -}; - -struct rfb_cuttext_msg { - uint8_t type; - uint8_t padding[3]; - uint32_t length; -}; -static void -#ifndef __FreeBSD__ -rfb_send_server_init_msg(int cfd, struct rfb_softc *rc) + switch (c->rc_cver) { + case RFB_CVER_3_3: + /* + * RFB specification[1] section 7.1.2: + * The server decides the security type and sends a single word. + */ + be32enc(buf, auth_type); + (void) stream_write(c->rc_fd, buf, 4); + + break; + + case RFB_CVER_3_7: + case RFB_CVER_3_8: + /* Send list of supported types. */ + buf[0] = 1; /* list length */ + buf[1] = auth_type; + (void) stream_write(c->rc_fd, buf, 2); + + /* Read agreed security type. */ + if (stream_read(c->rc_fd, buf, 1) != 1) { + rfb_printf(c, RFB_LOGWARN, + "auth fail, no type from client"); + return (false); + } + + if (buf[0] != auth_type) { + rfb_send_client_status(c, 1, + "Auth failed: authentication type mismatch"); + return (false); + } + + break; + } + + if (auth_type == RFBP_SECURITY_NONE) { + /* + * According to the RFB specification[1], section 7.2.1, for a + * security type of 'None', client versions 3.3 and 3.7 expect + * to move straight to the ClientInit phase, without the server + * sending a response. For version 3.8, a SecurityResult word + * needs to be sent indicating success. + */ + switch (c->rc_cver) { + case RFB_CVER_3_3: + case RFB_CVER_3_7: + break; + case RFB_CVER_3_8: + rfb_send_client_status(c, 0, NULL); + break; + } + return (true); + } + + /* Perform VNC authentication. */ + +#ifdef NO_OPENSSL + rfb_printf(c, RFB_LOGERR, + "Auth not supported, no OpenSSL in your system"); + rfb_send_client_status(c, 1, "Auth failed."); + return (false); #else -rfb_send_server_init_msg(int cfd) + unsigned char challenge[RFBP_SECURITY_VNC_AUTH_LEN]; + unsigned char keystr[RFBP_SECURITY_VNC_PASSWD_LEN]; + unsigned char crypt_expected[RFBP_SECURITY_VNC_AUTH_LEN]; + DES_key_schedule ks; + + /* + * The client encrypts the challenge with DES, using a password + * supplied by the user as the key. + * To form the key, the password is truncated to eight characters, or + * padded with null bytes on the right. + * The client then sends the resulting 16-bytes response. + */ + (void) strncpy((char *)keystr, c->rc_s->rs_password, + RFBP_SECURITY_VNC_PASSWD_LEN); + + /* + * VNC clients encrypt the challenge with all the bit fields in each + * byte of the password mirrored. + * Here we flip each byte of the keystr. + */ + for (uint_t i = 0; i < RFBP_SECURITY_VNC_PASSWD_LEN; i++) { + keystr[i] = (keystr[i] & 0xf0) >> 4 | (keystr[i] & 0x0f) << 4; + keystr[i] = (keystr[i] & 0xcc) >> 2 | (keystr[i] & 0x33) << 2; + keystr[i] = (keystr[i] & 0xaa) >> 1 | (keystr[i] & 0x55) << 1; + } + + /* Initialize a 16-byte random challenge. */ + arc4random_buf(challenge, sizeof (challenge)); + (void) stream_write(c->rc_fd, challenge, RFBP_SECURITY_VNC_AUTH_LEN); + + /* Receive the 16-byte challenge response. */ + if (stream_read(c->rc_fd, buf, RFBP_SECURITY_VNC_AUTH_LEN) + != RFBP_SECURITY_VNC_AUTH_LEN) { + rfb_send_client_status(c, 1, "response read failed"); + return (false); + } + + memcpy(crypt_expected, challenge, RFBP_SECURITY_VNC_AUTH_LEN); + + /* Encrypt the Challenge with DES. */ + DES_set_key_unchecked((const_DES_cblock *)keystr, &ks); + DES_ecb_encrypt((const_DES_cblock *)challenge, + (const_DES_cblock *)crypt_expected, &ks, DES_ENCRYPT); + DES_ecb_encrypt( + (const_DES_cblock *)(challenge + RFBP_SECURITY_VNC_PASSWD_LEN), + (const_DES_cblock *)(crypt_expected + RFBP_SECURITY_VNC_PASSWD_LEN), + &ks, DES_ENCRYPT); + + if (memcmp(crypt_expected, buf, RFBP_SECURITY_VNC_AUTH_LEN) != 0) { + rfb_send_client_status(c, 1, "Auth failed: Invalid password."); + return (false); + } + + rfb_printf(c, RFB_LOGDEBUG, "authentication succeeded"); + rfb_send_client_status(c, 0, NULL); #endif + + return (true); +} + +static bool +rfb_handshake_init_message(rfb_client_t *c) { - struct bhyvegc_image *gc_image; - struct rfb_srvr_info sinfo; - - gc_image = console_get_image(); - - sinfo.width = htons(gc_image->width); - sinfo.height = htons(gc_image->height); - sinfo.pixfmt.bpp = 32; - sinfo.pixfmt.depth = 32; - sinfo.pixfmt.bigendian = 0; - sinfo.pixfmt.truecolor = 1; - sinfo.pixfmt.red_max = htons(255); - sinfo.pixfmt.green_max = htons(255); - sinfo.pixfmt.blue_max = htons(255); - sinfo.pixfmt.red_shift = 16; - sinfo.pixfmt.green_shift = 8; - sinfo.pixfmt.blue_shift = 0; - sinfo.pixfmt.pad[0] = 0; - sinfo.pixfmt.pad[1] = 0; - sinfo.pixfmt.pad[2] = 0; - -#ifndef __FreeBSD__ - const char *name = rc->name != NULL ? rc->name : "bhyve"; - - sinfo.namelen = htonl(strlen(name)); - (void)stream_write(cfd, &sinfo, sizeof(sinfo)); - (void)stream_write(cfd, name, strlen(name)); -#else - sinfo.namelen = htonl(strlen("bhyve")); - (void)stream_write(cfd, &sinfo, sizeof(sinfo)); - (void)stream_write(cfd, "bhyve", strlen("bhyve")); -#endif + struct bhyvegc_image *gci; + char buf[1]; + char *name; + + rfb_printf(c, RFB_LOGDEBUG, "handshake server init"); + + /* Read the client init message. */ + if (stream_read(c->rc_fd, buf, 1) != 1) { + rfb_printf(c, RFB_LOGWARN, "client did not send init"); + return (false); + } + + if (buf[0] == 0) { + rfb_client_t *oc; + + rfb_printf(c, RFB_LOGDEBUG, + "client requested exclusive access"); + + pthread_mutex_lock(&c->rc_s->rs_clientlock); + c->rc_s->rs_exclusive = true; + /* Disconnect all other clients. */ + for (oc = list_head(&c->rc_s->rs_clients); oc != NULL; + oc = list_next(&c->rc_s->rs_clients, oc)) { + if (oc != c) + oc->rc_closing = true; + } + pthread_mutex_unlock(&c->rc_s->rs_clientlock); + } else { + rfb_printf(c, RFB_LOGDEBUG, "client requested shared access"); + + pthread_mutex_lock(&c->rc_s->rs_clientlock); + if (c->rc_s->rs_exclusive) { + rfb_printf(c, RFB_LOGWARN, + "deny due to existing exclusive session"); + pthread_mutex_unlock(&c->rc_s->rs_clientlock); + return (false); + } + pthread_mutex_unlock(&c->rc_s->rs_clientlock); + } + + gci = console_get_image(); + + c->rc_sinfo.rsi_width = htons(gci->width); + c->rc_sinfo.rsi_height = htons(gci->height); + c->rc_width = gci->width; + c->rc_height = gci->height; + + if (c->rc_s->rs_name != NULL) + name = (char *)c->rc_s->rs_name; + else + name = "bhyve"; + + c->rc_sinfo.rsi_namelen = htonl(strlen(name)); + (void) stream_write(c->rc_fd, &c->rc_sinfo, sizeof (c->rc_sinfo)); + (void) stream_write(c->rc_fd, name, strlen(name)); + + return (true); } -static void -rfb_send_resize_update_msg(struct rfb_softc *rc, int cfd) +static bool +rfb_handshake(rfb_client_t *c) { - struct rfb_srvr_updt_msg supdt_msg; - struct rfb_srvr_rect_hdr srect_hdr; - - /* Number of rectangles: 1 */ - supdt_msg.type = 0; - supdt_msg.pad = 0; - supdt_msg.numrects = htons(1); - stream_write(cfd, &supdt_msg, sizeof(struct rfb_srvr_updt_msg)); - - /* Rectangle header */ - srect_hdr.x = htons(0); - srect_hdr.y = htons(0); - srect_hdr.width = htons(rc->width); - srect_hdr.height = htons(rc->height); - srect_hdr.encoding = htonl(RFB_ENCODING_RESIZE); - stream_write(cfd, &srect_hdr, sizeof(struct rfb_srvr_rect_hdr)); + if (!rfb_handshake_version(c)) + return (false); + + if (!rfb_handshake_auth(c)) + return (false); + + if (!rfb_handshake_init_message(c)) + return (false); + + return (true); } static void -rfb_send_extended_keyevent_update_msg(struct rfb_softc *rc, int cfd) +rfb_print_pixfmt(rfb_client_t *c, rfb_pixfmt_t *px, rfb_loglevel_t level) { - struct rfb_srvr_updt_msg supdt_msg; - struct rfb_srvr_rect_hdr srect_hdr; - - /* Number of rectangles: 1 */ - supdt_msg.type = 0; - supdt_msg.pad = 0; - supdt_msg.numrects = htons(1); - stream_write(cfd, &supdt_msg, sizeof(struct rfb_srvr_updt_msg)); - - /* Rectangle header */ - srect_hdr.x = htons(0); - srect_hdr.y = htons(0); - srect_hdr.width = htons(rc->width); - srect_hdr.height = htons(rc->height); - srect_hdr.encoding = htonl(RFB_ENCODING_EXT_KEYEVENT); - stream_write(cfd, &srect_hdr, sizeof(struct rfb_srvr_rect_hdr)); + rfb_printf(c, level, "%20s: %u", "bpp", px->rp_bpp); + rfb_printf(c, level, "%20s: %u", "depth", px->rp_depth); + rfb_printf(c, level, "%20s: %u", "bigendian", px->rp_bigendian); + rfb_printf(c, level, "%20s: %u", "truecolour", px->rp_truecolour); + rfb_printf(c, level, "%20s: %u", "r_max", ntohs(px->rp_r_max)); + rfb_printf(c, level, "%20s: %u", "g_max", ntohs(px->rp_g_max)); + rfb_printf(c, level, "%20s: %u", "b_max", ntohs(px->rp_b_max)); + rfb_printf(c, level, "%20s: %u", "r_shift", px->rp_r_shift); + rfb_printf(c, level, "%20s: %u", "g_shift", px->rp_g_shift); + rfb_printf(c, level, "%20s: %u", "b_shift", px->rp_b_shift); } -static void -rfb_recv_set_pixfmt_msg(struct rfb_softc *rc, int cfd) +static bool +rfb_recv_set_pixel_format(rfb_client_t *c) { - struct rfb_pixfmt_msg pixfmt_msg; + rfb_cs_pixfmt_msg_t msg; + rfb_pixfmt_t *newpx = &msg.rp_pixfmt; + rfb_pixfmt_t *oldpx = &c->rc_sinfo.rsi_pixfmt; + rfb_pixfmt_t *spx = &c->rc_s->rs_pixfmt; - (void)stream_read(cfd, ((void *)&pixfmt_msg)+1, sizeof(pixfmt_msg)-1); -} + rfb_printf(c, RFB_LOGDEBUG, "received pixel format"); + if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg)) + return (false); -static void -rfb_recv_set_encodings_msg(struct rfb_softc *rc, int cfd) + /* + * The client has sent its desired pixel format. The protocol does not + * have a mechanism to reject this, we are supposed to just start using + * the requested format from the next update. + * + * At present, we can only support alternative rgb-shift values and + * will accept (and ignore) a new depth value. + */ + + if (oldpx->rp_bpp != newpx->rp_bpp || + oldpx->rp_bigendian != newpx->rp_bigendian || + oldpx->rp_truecolour != newpx->rp_truecolour || + oldpx->rp_r_max != newpx->rp_r_max || + oldpx->rp_g_max != newpx->rp_g_max || + oldpx->rp_b_max != newpx->rp_b_max) { + rfb_printf(c, RFB_LOGWARN, "unsupported pixfmt from client"); + rfb_print_pixfmt(c, newpx, RFB_LOGWARN); + return (false); + } + + rfb_print_pixfmt(c, newpx, RFB_LOGDEBUG); + + /* Check if the new shifts match the server's native values. */ + if (newpx->rp_r_shift != spx->rp_r_shift || + newpx->rp_g_shift != spx->rp_g_shift || + newpx->rp_b_shift != spx->rp_b_shift) { + c->rc_custom_pixfmt = true; + rfb_printf(c, RFB_LOGDEBUG, "Using custom pixfmt"); + } else { + c->rc_custom_pixfmt = false; + rfb_printf(c, RFB_LOGDEBUG, "Using native pixfmt"); + } + + c->rc_sinfo.rsi_pixfmt = msg.rp_pixfmt; + c->rc_crc_reset = true; + + return (true); +} + +static bool +rfb_recv_set_encodings(rfb_client_t *c) { - struct rfb_enc_msg enc_msg; - int i; - uint32_t encoding; + rfb_cs_encodings_msg_t msg; + + rfb_printf(c, RFB_LOGDEBUG, "received encodings"); + + if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg)) + return (false); + + msg.re_numencs = htons(msg.re_numencs); + + rfb_printf(c, RFB_LOGDEBUG, "%d values", msg.re_numencs); + + for (uint_t i = 0; i < msg.re_numencs; i++) { + uint32_t enc; - (void)stream_read(cfd, ((void *)&enc_msg)+1, sizeof(enc_msg)-1); + if (stream_read(c->rc_fd, &enc, sizeof (enc)) != sizeof (enc)) + return (false); - for (i = 0; i < htons(enc_msg.numencs); i++) { - (void)stream_read(cfd, &encoding, sizeof(encoding)); - switch (htonl(encoding)) { - case RFB_ENCODING_RAW: - rc->enc_raw_ok = true; + enc = htonl(enc); + + switch (enc) { + case RFBP_ENCODING_RAW: + rfb_printf(c, RFB_LOGDEBUG, + "client supports raw encoding"); + c->rc_encodings |= RFB_ENCODING_RAW; break; - case RFB_ENCODING_ZLIB: - if (!rc->enc_zlib_ok) { - deflateInit(&rc->zstream, Z_BEST_SPEED); - rc->enc_zlib_ok = true; + case RFBP_ENCODING_ZLIB: + rfb_printf(c, RFB_LOGDEBUG, + "client supports zlib encoding"); + if (!(c->rc_encodings & RFB_ENCODING_ZLIB)) { + if (deflateInit(&c->rc_zstream, Z_BEST_SPEED) + != Z_OK) { + return (false); + } + c->rc_encodings |= RFB_ENCODING_ZLIB; } break; - case RFB_ENCODING_RESIZE: - rc->enc_resize_ok = true; + case RFBP_ENCODING_RESIZE: + rfb_printf(c, RFB_LOGDEBUG, "client supports resize"); + c->rc_encodings |= RFB_ENCODING_RESIZE; break; - case RFB_ENCODING_EXT_KEYEVENT: - rc->enc_extkeyevent_ok = true; + case RFBP_ENCODING_EXT_KEVENT: + rfb_printf(c, RFB_LOGDEBUG, + "client supports ext key event"); + c->rc_encodings |= RFB_ENCODING_EXT_KEVENT; break; + case RFBP_ENCODING_DESKTOP_NAME: + rfb_printf(c, RFB_LOGDEBUG, + "client supports desktop name"); + c->rc_encodings |= RFB_ENCODING_DESKTOP_NAME; + break; + default: + rfb_printf(c, RFB_LOGDEBUG, + "client supports encoding %d", (int32_t)enc); } } + + return (true); } -/* - * Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only - */ -static __inline uint32_t -fast_crc32(void *buf, int len, uint32_t crcval) +static bool +rfb_recv_update(rfb_client_t *c) { - uint32_t q = len / sizeof(uint32_t); - uint32_t *p = (uint32_t *)buf; + rfb_cs_update_msg_t msg; - while (q--) { - asm volatile ( - ".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;" - :"=S" (crcval) - :"0" (crcval), "c" (*p) - ); - p++; + if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg)) + return (false); + + if (!c->rc_keyevent_sent && + (c->rc_encodings & RFB_ENCODING_EXT_KEVENT)) { + /* + * Take this opportunity to tell the client that we + * accept QEMU Extended Key Event Pseudo-encoding. + */ + c->rc_keyevent_sent = true; + rfb_send_extended_keyevent_update_msg(c); } - return (crcval); + c->rc_pending = true; + if (msg.rum_incremental == 0) { + rfb_printf(c, RFB_LOGDEBUG, + "client requested full screen update"); + c->rc_send_fullscreen = true; + } + + return (true); +} + +static bool +rfb_recv_key_event(rfb_client_t *c) +{ + rfb_cs_key_event_msg_t msg; + + if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg)) + return (false); + + msg.rke_sym = htonl(msg.rke_sym); + + rfb_printf(c, RFB_LOGDEBUG, "received key %s %x", + msg.rke_down == 0 ? "up" : "down", msg.rke_sym); + + console_key_event(msg.rke_down, msg.rke_sym, htonl(0)); + c->rc_input_detected = true; + + return (true); +} + +static bool +rfb_recv_pointer_event(rfb_client_t *c) +{ + rfb_cs_pointer_event_msg_t msg; + + if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg)) + return (false); + + msg.rpe_x = htons(msg.rpe_x); + msg.rpe_y = htons(msg.rpe_y); + + if (rfb_debug > 1) { + rfb_printf(c, RFB_LOGDEBUG, "received pointer event @ %dx%d", + msg.rpe_x, msg.rpe_y); + } + + console_ptr_event(msg.rpe_button, msg.rpe_x, msg.rpe_y); + c->rc_input_detected = true; + + return (true); } -static int -rfb_send_update_header(struct rfb_softc *rc, int cfd, int numrects) +static bool +rfb_recv_cut_text(rfb_client_t *c) { - struct rfb_srvr_updt_msg supdt_msg; + rfb_cs_cut_text_msg_t msg; + unsigned char buf[32]; - supdt_msg.type = 0; - supdt_msg.pad = 0; - supdt_msg.numrects = htons(numrects); + rfb_printf(c, RFB_LOGDEBUG, "received cut text event"); + + if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg)) + return (false); + + msg.rct_length = htonl(msg.rct_length); + rfb_printf(c, RFB_LOGDEBUG, "%u bytes in buffer", msg.rct_length); + /* Consume the buffer */ + while (msg.rct_length > 0) { + ssize_t l; + + l = stream_read(c->rc_fd, buf, + MIN(sizeof (buf), msg.rct_length)); + if (l <= 0) + return (false); + msg.rct_length -= l; + } - return stream_write(cfd, &supdt_msg, - sizeof(struct rfb_srvr_updt_msg)); + return (true); } -static int -rfb_send_rect(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc, - int x, int y, int w, int h) +static bool +rfb_recv_qemu(rfb_client_t *c) { - struct rfb_srvr_rect_hdr srect_hdr; + rfb_cs_qemu_msg_t msg; + + rfb_printf(c, RFB_LOGDEBUG, "received QEMU event"); + + if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg)) + return (false); + + switch (msg.rq_subtype) { + case RFBP_CS_QEMU_KEVENT: { + rfb_cs_qemu_extended_key_msg_t keymsg; + + if (stream_read(c->rc_fd, &keymsg, sizeof (keymsg)) != + sizeof (keymsg)) { + return (false); + } + + keymsg.rqek_sym = htonl(keymsg.rqek_sym); + keymsg.rqek_code = htonl(keymsg.rqek_code); + + rfb_printf(c, RFB_LOGDEBUG, "QEMU key %s %x / %x", + keymsg.rqek_down == 0 ? "up" : "down", + keymsg.rqek_sym, keymsg.rqek_code); + + console_key_event((int)keymsg.rqek_down, keymsg.rqek_sym, + keymsg.rqek_code); + c->rc_input_detected = true; + break; + } + default: + rfb_printf(c, RFB_LOGWARN, "Unknown QEMU event subtype: %d\n", + msg.rq_subtype); + return (false); + } + + return (true); +} + +static bool +rfb_send_update_header(rfb_client_t *c, int numrects) +{ + rfb_server_update_msg_t msg; + + msg.rss_type = RFBP_SC_UPDATE; + msg.rss_pad = 0; + msg.rss_numrects = htons(numrects); + + return (stream_write(c->rc_fd, &msg, sizeof (msg)) == sizeof (msg)); +} + +static void +rfb_send_resize_update_msg(rfb_client_t *c) +{ + rfb_rect_hdr_t rect; + + rfb_printf(c, RFB_LOGDEBUG, "sending screen resize %dx%d", + c->rc_width, c->rc_height); + + (void) rfb_send_update_header(c, 1); + + rect.rr_x = htons(0); + rect.rr_y = htons(0); + rect.rr_width = htons(c->rc_width); + rect.rr_height = htons(c->rc_height); + rect.rr_encoding = htonl(RFBP_ENCODING_RESIZE); + + (void) stream_write(c->rc_fd, &rect, sizeof (rect)); +} + +static void +rfb_send_extended_keyevent_update_msg(rfb_client_t *c) +{ + rfb_rect_hdr_t rect; + + rfb_printf(c, RFB_LOGDEBUG, "sending extended keyevent update message"); + + (void) rfb_send_update_header(c, 1); + + rect.rr_x = htons(0); + rect.rr_y = htons(0); + rect.rr_width = htons(c->rc_width); + rect.rr_height = htons(c->rc_height); + rect.rr_encoding = htonl(RFBP_ENCODING_EXT_KEVENT); + + (void) stream_write(c->rc_fd, &rect, sizeof (rect)); +} + +static void +translate_pixels(rfb_client_t *c, struct bhyvegc_image *gci, + int x1, int y1, int x2, int y2) +{ + rfb_pixfmt_t *px = &c->rc_sinfo.rsi_pixfmt; + rfb_pixfmt_t *spx = &c->rc_s->rs_pixfmt; + int w, h; + + w = gci->width; + h = gci->height; + VERIFY3S(gci->width, ==, c->rc_gci.width); + VERIFY3S(gci->height, ==, c->rc_gci.height); + + for (uint_t y = y1; y < h && y < y2; y++) { + for (uint_t x = x1; x < w && x < x2; x++) { + uint32_t p; + + p = gci->data[y * w + x]; + c->rc_gci.data[y * w + x] = + 0xff000000 | + ((p >> spx->rp_r_shift) & 0xff) << px->rp_r_shift | + ((p >> spx->rp_g_shift) & 0xff) << px->rp_g_shift | + ((p >> spx->rp_b_shift) & 0xff) << px->rp_b_shift; + } + } +} + +static bool +rfb_send_rect(rfb_client_t *c, struct bhyvegc_image *gci, + int x, int y, int w, int h) +{ + rfb_rect_hdr_t rect; unsigned long zlen; ssize_t nwrite, total; int err; uint32_t *p; uint8_t *zbufp; - /* - * Send a single rectangle of the given x, y, w h dimensions. - */ + if (rfb_debug > 1) { + rfb_printf(c, RFB_LOGDEBUG, "send rect %dx%d %dx%d", + x, y, w, h); + } - /* Rectangle header */ - srect_hdr.x = htons(x); - srect_hdr.y = htons(y); - srect_hdr.width = htons(w); - srect_hdr.height = htons(h); + /* Rectangle header. */ + rect.rr_x = htons(x); + rect.rr_y = htons(y); + rect.rr_width = htons(w); + rect.rr_height = htons(h); + + uint32_t *data = gci->data; + if (c->rc_custom_pixfmt) { + translate_pixels(c, gci, x, y, x + w, y + h); + data = c->rc_gci.data; + } h = y + h; - w *= sizeof(uint32_t); - if (rc->enc_zlib_ok) { - zbufp = rc->zbuf; - rc->zstream.total_in = 0; - rc->zstream.total_out = 0; - for (p = &gc->data[y * gc->width + x]; y < h; y++) { - rc->zstream.next_in = (Bytef *)p; - rc->zstream.avail_in = w; - rc->zstream.next_out = (Bytef *)zbufp; - rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16 - - rc->zstream.total_out; - rc->zstream.data_type = Z_BINARY; - - /* Compress with zlib */ - err = deflate(&rc->zstream, Z_SYNC_FLUSH); + w *= sizeof (uint32_t); + + if (c->rc_encodings & RFB_ENCODING_ZLIB) { + zbufp = c->rc_zbuf; + c->rc_zstream.total_in = 0; + c->rc_zstream.total_out = 0; + for (p = &data[y * gci->width + x]; y < h; y++) { + c->rc_zstream.next_in = (Bytef *)p; + c->rc_zstream.avail_in = w; + c->rc_zstream.next_out = (Bytef *)zbufp; + c->rc_zstream.avail_out = RFB_ZLIB_BUFSZ + 16 - + c->rc_zstream.total_out; + c->rc_zstream.data_type = Z_BINARY; + + /* Compress with zlib. */ + err = deflate(&c->rc_zstream, Z_SYNC_FLUSH); if (err != Z_OK) { - WPRINTF(("zlib[rect] deflate err: %d", err)); - rc->enc_zlib_ok = false; - deflateEnd(&rc->zstream); + rfb_printf(c, RFB_LOGWARN, + "zlib[rect] deflate err: %d", err); goto doraw; } - zbufp = rc->zbuf + rc->zstream.total_out; - p += gc->width; + zbufp = c->rc_zbuf + c->rc_zstream.total_out; + p += gci->width; } - srect_hdr.encoding = htonl(RFB_ENCODING_ZLIB); - nwrite = stream_write(cfd, &srect_hdr, - sizeof(struct rfb_srvr_rect_hdr)); + rect.rr_encoding = htonl(RFBP_ENCODING_ZLIB); + nwrite = stream_write(c->rc_fd, &rect, sizeof (rect)); if (nwrite <= 0) - return (nwrite); + return (false); - zlen = htonl(rc->zstream.total_out); - nwrite = stream_write(cfd, &zlen, sizeof(uint32_t)); + zlen = htonl(c->rc_zstream.total_out); + nwrite = stream_write(c->rc_fd, &zlen, sizeof (uint32_t)); if (nwrite <= 0) - return (nwrite); - return (stream_write(cfd, rc->zbuf, rc->zstream.total_out)); + return (false); + return (stream_write(c->rc_fd, c->rc_zbuf, + c->rc_zstream.total_out) == c->rc_zstream.total_out); } doraw: total = 0; - zbufp = rc->zbuf; - for (p = &gc->data[y * gc->width + x]; y < h; y++) { + zbufp = c->rc_zbuf; + for (p = &data[y * gci->width + x]; y < h; y++) { memcpy(zbufp, p, w); zbufp += w; total += w; - p += gc->width; + p += gci->width; } - srect_hdr.encoding = htonl(RFB_ENCODING_RAW); - nwrite = stream_write(cfd, &srect_hdr, - sizeof(struct rfb_srvr_rect_hdr)); + rect.rr_encoding = htonl(RFBP_ENCODING_RAW); + nwrite = stream_write(c->rc_fd, &rect, sizeof (rect)); if (nwrite <= 0) - return (nwrite); + return (false); - total = stream_write(cfd, rc->zbuf, total); - - return (total); + return (stream_write(c->rc_fd, c->rc_zbuf, total) == total); } -static int -rfb_send_all(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc) + +static bool +rfb_send_all(rfb_client_t *c, struct bhyvegc_image *gci) { - struct rfb_srvr_updt_msg supdt_msg; - struct rfb_srvr_rect_hdr srect_hdr; + rfb_rect_hdr_t rect; ssize_t nwrite; unsigned long zlen; int err; - /* - * Send the whole thing - */ + rfb_printf(c, RFB_LOGDEBUG, "send entire screen"); - /* Number of rectangles: 1 */ - supdt_msg.type = 0; - supdt_msg.pad = 0; - supdt_msg.numrects = htons(1); - nwrite = stream_write(cfd, &supdt_msg, - sizeof(struct rfb_srvr_updt_msg)); - if (nwrite <= 0) - return (nwrite); - - /* Rectangle header */ - srect_hdr.x = 0; - srect_hdr.y = 0; - srect_hdr.width = htons(gc->width); - srect_hdr.height = htons(gc->height); - if (rc->enc_zlib_ok) { - rc->zstream.next_in = (Bytef *)gc->data; - rc->zstream.avail_in = gc->width * gc->height * - sizeof(uint32_t); - rc->zstream.next_out = (Bytef *)rc->zbuf; - rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16; - rc->zstream.data_type = Z_BINARY; - - rc->zstream.total_in = 0; - rc->zstream.total_out = 0; - - /* Compress with zlib */ - err = deflate(&rc->zstream, Z_SYNC_FLUSH); + /* Just the one (big) rect. */ + if (!rfb_send_update_header(c, 1)) + return (false); + + rect.rr_x = 0; + rect.rr_y = 0; + rect.rr_width = htons(gci->width); + rect.rr_height = htons(gci->height); + + uint32_t *data = gci->data; + if (c->rc_custom_pixfmt) { + translate_pixels(c, gci, 0, 0, gci->width, gci->height); + data = c->rc_gci.data; + } + + if (c->rc_encodings & RFB_ENCODING_ZLIB) { + c->rc_zstream.next_in = (Bytef *)data; + c->rc_zstream.avail_in = gci->width * gci->height * + sizeof (uint32_t); + c->rc_zstream.next_out = (Bytef *)c->rc_zbuf; + c->rc_zstream.avail_out = RFB_ZLIB_BUFSZ + 16; + c->rc_zstream.data_type = Z_BINARY; + + c->rc_zstream.total_in = 0; + c->rc_zstream.total_out = 0; + + /* Compress with zlib. */ + err = deflate(&c->rc_zstream, Z_SYNC_FLUSH); if (err != Z_OK) { - WPRINTF(("zlib deflate err: %d", err)); - rc->enc_zlib_ok = false; - deflateEnd(&rc->zstream); + rfb_printf(c, RFB_LOGWARN, "zlib deflate err: %d", err); goto doraw; } - srect_hdr.encoding = htonl(RFB_ENCODING_ZLIB); - nwrite = stream_write(cfd, &srect_hdr, - sizeof(struct rfb_srvr_rect_hdr)); + rect.rr_encoding = htonl(RFBP_ENCODING_ZLIB); + nwrite = stream_write(c->rc_fd, &rect, sizeof (rect)); if (nwrite <= 0) - return (nwrite); + return (false); - zlen = htonl(rc->zstream.total_out); - nwrite = stream_write(cfd, &zlen, sizeof(uint32_t)); + zlen = htonl(c->rc_zstream.total_out); + nwrite = stream_write(c->rc_fd, &zlen, sizeof (uint32_t)); if (nwrite <= 0) - return (nwrite); - return (stream_write(cfd, rc->zbuf, rc->zstream.total_out)); + return (false); + return (stream_write(c->rc_fd, c->rc_zbuf, + c->rc_zstream.total_out) == c->rc_zstream.total_out); } doraw: - srect_hdr.encoding = htonl(RFB_ENCODING_RAW); - nwrite = stream_write(cfd, &srect_hdr, - sizeof(struct rfb_srvr_rect_hdr)); + rect.rr_encoding = htonl(RFBP_ENCODING_RAW); + nwrite = stream_write(c->rc_fd, &rect, sizeof (rect)); if (nwrite <= 0) - return (nwrite); + return (false); - nwrite = stream_write(cfd, gc->data, - gc->width * gc->height * sizeof(uint32_t)); - - return (nwrite); + nwrite = gci->width * gci->height * sizeof (uint32_t); + return (stream_write(c->rc_fd, data, nwrite) == nwrite); } -#define PIX_PER_CELL 32 -#define PIXCELL_SHIFT 5 -#define PIXCELL_MASK 0x1F - -static int -rfb_send_screen(struct rfb_softc *rc, int cfd) +static bool +rfb_send_screen(rfb_client_t *c) { - struct bhyvegc_image *gc_image; - ssize_t nwrite; - int x, y; - int celly, cellwidth; + struct bhyvegc_image *gci; + bool retval = true; + bool sendall = false; int xcells, ycells; - int w, h; - uint32_t *p; - int rem_x, rem_y; /* remainder for resolutions not x32 pixels ratio */ - int retval; - uint32_t *crc_p, *orig_crc; - int changes; - bool expected; - - /* Return if another thread sending */ - expected = false; - if (atomic_compare_exchange_strong(&rc->sending, &expected, true) == false) - return (1); + int rem_x, rem_y; + uint32_t *p, *ncrc, *ocrc; + uint_t changes, perc, x, y; - retval = 1; - - /* Updates require a preceding update request */ - if (atomic_exchange(&rc->pending, false) == false) - goto done; + /* Updates require a preceding client update request. */ + if (atomic_exchange(&c->rc_pending, false) == false) + return (true); console_refresh(); - gc_image = console_get_image(); - - /* Clear old CRC values when the size changes */ - if (rc->crc_width != gc_image->width || - rc->crc_height != gc_image->height) { - memset(rc->crc, 0, sizeof(uint32_t) * - howmany(RFB_MAX_WIDTH, PIX_PER_CELL) * - howmany(RFB_MAX_HEIGHT, PIX_PER_CELL)); - rc->crc_width = gc_image->width; - rc->crc_height = gc_image->height; - } + gci = console_get_image(); - /* A size update counts as an update in itself */ - if (rc->width != gc_image->width || - rc->height != gc_image->height) { - rc->width = gc_image->width; - rc->height = gc_image->height; - if (rc->enc_resize_ok) { - rfb_send_resize_update_msg(rc, cfd); - rc->update_all = true; + /* + * It's helpful if the image size or data address does not change + * underneath us. + */ + pthread_mutex_lock(&gci->mtx); + + /* Check for screen resolution changes. */ + if (c->rc_width != gci->width || + c->rc_height != gci->height) { + c->rc_width = gci->width; + c->rc_height = gci->height; + c->rc_crc_reset = true; + c->rc_send_fullscreen = true; + + /* If the client supports it, send a resize event. */ + if (c->rc_encodings & RFB_ENCODING_RESIZE) { + rfb_send_resize_update_msg(c); + /* + * A resize message counts as an update in response to + * the client's preceding request so rc->pending does + * not need to be reset here. + */ goto done; } } - if (atomic_exchange(&rc->update_all, false) == true) { - retval = rfb_send_all(rc, cfd, gc_image); - goto done; + /* Clear old CRC values. */ + if (atomic_exchange(&c->rc_crc_reset, false)) + memset(c->rc_crc, '\0', c->rc_cells * sizeof (uint32_t)); + + if (c->rc_custom_pixfmt && (c->rc_gci.data == NULL || + c->rc_gci.width != c->rc_width || + c->rc_gci.height != c->rc_height)) { + c->rc_gci.data = reallocarray(c->rc_gci.data, + c->rc_width * c->rc_height, sizeof (uint32_t)); + if (c->rc_gci.data == NULL) { + retval = false; + goto done; + } + c->rc_gci.width = c->rc_width; + c->rc_gci.height = c->rc_height; + } else if (!c->rc_custom_pixfmt && c->rc_gci.data != NULL) { + free(c->rc_gci.data); + c->rc_gci.data = NULL; } + sendall = atomic_exchange(&c->rc_send_fullscreen, false); + /* - * Calculate the checksum for each 32x32 cell. Send each that - * has changed since the last scan. + * Calculate a checksum for each 32x32 cell. Send all that have + * changed since the last scan. */ - w = rc->crc_width; - h = rc->crc_height; - xcells = howmany(rc->crc_width, PIX_PER_CELL); - ycells = howmany(rc->crc_height, PIX_PER_CELL); - - rem_x = w & PIXCELL_MASK; - - rem_y = h & PIXCELL_MASK; - if (!rem_y) - rem_y = PIX_PER_CELL; + xcells = howmany(gci->width, RFB_PIX_PER_CELL); + ycells = howmany(gci->height, RFB_PIX_PER_CELL); + rem_x = gci->width & RFB_PIXCELL_MASK; + rem_y = gci->height & RFB_PIXCELL_MASK; + if (rem_y == 0) + rem_y = RFB_PIX_PER_CELL; - p = gc_image->data; + p = gci->data; - /* - * Go through all cells and calculate crc. If significant number - * of changes, then send entire screen. - * crc_tmp is dual purpose: to store the new crc and to flag as - * a cell that has changed. - */ - crc_p = rc->crc_tmp - xcells; - orig_crc = rc->crc - xcells; + ncrc = c->rc_crc_tmp - xcells; + ocrc = c->rc_crc - xcells; changes = 0; - memset(rc->crc_tmp, 0, sizeof(uint32_t) * xcells * ycells); - for (y = 0; y < h; y++) { - if ((y & PIXCELL_MASK) == 0) { - crc_p += xcells; - orig_crc += xcells; + memset(c->rc_crc_tmp, '\0', sizeof (uint32_t) * xcells * ycells); + for (y = 0; y < gci->height; y++) { + if ((y & RFB_PIXCELL_MASK) == 0) { + ncrc += xcells; + ocrc += xcells; } for (x = 0; x < xcells; x++) { - if (x == (xcells - 1) && rem_x > 0) - cellwidth = rem_x; - else - cellwidth = PIX_PER_CELL; + uint_t cellwidth; - if (rc->hw_crc) - crc_p[x] = fast_crc32(p, - cellwidth * sizeof(uint32_t), - crc_p[x]); + if (x == xcells - 1 && rem_x > 0) + cellwidth = rem_x; else - crc_p[x] = (uint32_t)crc32(crc_p[x], - (Bytef *)p, - cellwidth * sizeof(uint32_t)); + cellwidth = RFB_PIX_PER_CELL; + + if (rfb_sse42) { + ncrc[x] = fast_crc32(p, + cellwidth * sizeof (uint32_t), ncrc[x]); + } else { + ncrc[x] = (uint32_t)crc32(ncrc[x], + (Bytef *)p, cellwidth * sizeof (uint32_t)); + } p += cellwidth; - /* check for crc delta if last row in cell */ - if ((y & PIXCELL_MASK) == PIXCELL_MASK || y == (h-1)) { - if (orig_crc[x] != crc_p[x]) { - orig_crc[x] = crc_p[x]; - crc_p[x] = 1; + /* check for crc delta if last row in cell. */ + if ((y & RFB_PIXCELL_MASK) == RFB_PIXCELL_MASK || + y == gci->height - 1) { + if (ocrc[x] != ncrc[x]) { + ocrc[x] = ncrc[x]; + ncrc[x] = 1; changes++; } else { - crc_p[x] = 0; + ncrc[x] = 0; } } } } - /* - * We only send the update if there are changes. - * Restore the pending flag since it was unconditionally cleared - * above. - */ - if (!changes) { - rc->pending = true; + perc = (changes * 100) / (xcells * ycells); + if (rfb_debug > 1 && changes > 0) { + rfb_printf(c, RFB_LOGDEBUG, + "scanned and found %u changed cell(s) - %u%%", + changes, perc); + } + + /* + * If there are no changes, don't send an update. Restore the pending + * flag since we still owe the client an update. + */ + if (!sendall && !changes) { + c->rc_pending = true; goto done; } - /* If number of changes is > THRESH percent, send the whole screen */ - if (((changes * 100) / (xcells * ycells)) >= RFB_SEND_ALL_THRESH) { - retval = rfb_send_all(rc, cfd, gc_image); + /* If there are a lot of changes, send the whole screen. */ + if (perc >= RFB_SENDALL_THRESH) + sendall = true; + + if (sendall) { + retval = rfb_send_all(c, gci); goto done; } - rfb_send_update_header(rc, cfd, changes); + if (!rfb_send_update_header(c, changes)) { + retval = false; + goto done; + } - /* Go through all cells, and send only changed ones */ - crc_p = rc->crc_tmp; - for (y = 0; y < h; y += PIX_PER_CELL) { - /* previous cell's row */ - celly = (y >> PIXCELL_SHIFT); + /* Send the changed cells as separate rects. */ + ncrc = c->rc_crc_tmp; + for (y = 0; y < gci->height; y += RFB_PIX_PER_CELL) { + /* Previous cell's row. */ + int celly = (y >> RFB_PIXCELL_SHIFT); - /* Delta check crc to previous set */ + /* Delta check crc to previous set. */ for (x = 0; x < xcells; x++) { - if (*crc_p++ == 0) + uint_t cellwidth; + + if (*ncrc++ == 0) continue; - if (x == (xcells - 1) && rem_x > 0) + if (x == xcells - 1 && rem_x > 0) cellwidth = rem_x; else - cellwidth = PIX_PER_CELL; - nwrite = rfb_send_rect(rc, cfd, - gc_image, - x * PIX_PER_CELL, - celly * PIX_PER_CELL, - cellwidth, - y + PIX_PER_CELL >= h ? rem_y : PIX_PER_CELL); - if (nwrite <= 0) { - retval = nwrite; + cellwidth = RFB_PIX_PER_CELL; + + if (!rfb_send_rect(c, gci, + x * RFB_PIX_PER_CELL, celly * RFB_PIX_PER_CELL, + cellwidth, y + RFB_PIX_PER_CELL >= gci->height ? + rem_y : RFB_PIX_PER_CELL)) { + retval = false; goto done; } } } done: - rc->sending = false; + pthread_mutex_unlock(&gci->mtx); return (retval); } - -static void -rfb_recv_update_msg(struct rfb_softc *rc, int cfd) +static void * +rfb_client_rx_thread(void *arg) { - struct rfb_updt_msg updt_msg; - - (void)stream_read(cfd, ((void *)&updt_msg) + 1 , sizeof(updt_msg) - 1); - - if (rc->enc_extkeyevent_ok && (!rc->enc_extkeyevent_send)) { - rfb_send_extended_keyevent_update_msg(rc, cfd); - rc->enc_extkeyevent_send = true; + rfb_client_t *c = arg; + unsigned char cmd; + bool ret = true; + + while (ret && !c->rc_closing && (read(c->rc_fd, &cmd, 1) == 1)) { + switch (cmd) { + case RFBP_CS_SET_PIXEL_FORMAT: + ret = rfb_recv_set_pixel_format(c); + break; + case RFBP_CS_SET_ENCODINGS: + ret = rfb_recv_set_encodings(c); + break; + case RFBP_CS_UPDATE_REQUEST: + ret = rfb_recv_update(c); + break; + case RFBP_CS_KEY_EVENT: + ret = rfb_recv_key_event(c); + break; + case RFBP_CS_POINTER_EVENT: + ret = rfb_recv_pointer_event(c); + break; + case RFBP_CS_CUT_TEXT: + ret = rfb_recv_cut_text(c); + break; + case RFBP_CS_QEMU: + ret = rfb_recv_qemu(c); + break; + default: + rfb_printf(c, RFB_LOGWARN, "unknown cs code %d", + cmd & 0xff); + ret = false; + } } - rc->pending = true; - if (!updt_msg.incremental) - rc->update_all = true; -} - -static void -rfb_recv_key_msg(struct rfb_softc *rc, int cfd) -{ - struct rfb_key_msg key_msg; + rfb_printf(c, RFB_LOGDEBUG, "client rx thread exiting"); + c->rc_closing = true; - (void)stream_read(cfd, ((void *)&key_msg) + 1, sizeof(key_msg) - 1); - - console_key_event(key_msg.down, htonl(key_msg.sym), htonl(0)); - rc->input_detected = true; + return (NULL); } -static void -rfb_recv_client_msg(struct rfb_softc *rc, int cfd) +static void * +rfb_client_tx_thread(void *arg) { - struct rfb_client_msg client_msg; - struct rfb_extended_key_msg extkey_msg; - - (void)stream_read(cfd, ((void *)&client_msg) + 1, sizeof(client_msg) - 1); + rfb_client_t *c = arg; + rfb_server_t *s = c->rc_s; + char tname[MAXCOMLEN + 1]; + uint_t counter = 0; + hrtime_t tprev; + void *status; + int err; - if (client_msg.subtype == RFB_CLIENTMSG_EXT_KEYEVENT ) { - (void)stream_read(cfd, ((void *)&extkey_msg) + 2, sizeof(extkey_msg) - 2); - console_key_event((int)extkey_msg.down, htonl(extkey_msg.sym), htonl(extkey_msg.code)); - rc->input_detected = true; - } -} + (void) snprintf(tname, sizeof (tname), "rfb%u tx", c->rc_instance); + (void) pthread_set_name_np(c->rc_tx_tid, tname); -static void -rfb_recv_ptr_msg(struct rfb_softc *rc, int cfd) -{ - struct rfb_ptr_msg ptr_msg; + c->rc_sinfo.rsi_pixfmt = c->rc_s->rs_pixfmt; + c->rc_encodings = RFB_ENCODING_RAW; - (void)stream_read(cfd, ((void *)&ptr_msg) + 1, sizeof(ptr_msg) - 1); + if (!rfb_handshake(c)) + goto out; - console_ptr_event(ptr_msg.button, htons(ptr_msg.x), htons(ptr_msg.y)); - rc->input_detected = true; -} + c->rc_cells = howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, RFB_PIX_PER_CELL); + if ((c->rc_crc = calloc(c->rc_cells, sizeof (uint32_t))) == NULL || + (c->rc_crc_tmp = calloc(c->rc_cells, sizeof (uint32_t))) == NULL) { + perror("calloc crc"); + goto out; + } -static void -rfb_recv_cuttext_msg(struct rfb_softc *rc, int cfd) -{ - struct rfb_cuttext_msg ct_msg; - unsigned char buf[32]; - int len; - - len = stream_read(cfd, ((void *)&ct_msg) + 1, sizeof(ct_msg) - 1); - ct_msg.length = htonl(ct_msg.length); - while (ct_msg.length > 0) { - len = stream_read(cfd, buf, ct_msg.length > sizeof(buf) ? - sizeof(buf) : ct_msg.length); - ct_msg.length -= len; + err = pthread_create(&c->rc_rx_tid, NULL, rfb_client_rx_thread, c); + if (err != 0) { + perror("pthread_create client rx thread"); + goto out; } -} -static int64_t -timeval_delta(struct timeval *prev, struct timeval *now) -{ - int64_t n1, n2; - n1 = now->tv_sec * 1000000 + now->tv_usec; - n2 = prev->tv_sec * 1000000 + prev->tv_usec; - return (n1 - n2); -} + (void) snprintf(tname, sizeof (tname), "rfb%u rx", c->rc_instance); + (void) pthread_set_name_np(c->rc_rx_tid, tname); -static void * -rfb_wr_thr(void *arg) -{ - struct rfb_softc *rc; - fd_set rfds; - struct timeval tv; - struct timeval prev_tv; - int64_t tdiff; - int cfd; - int err; + tprev = gethrtime(); - rc = arg; - cfd = rc->cfd; + while (!c->rc_closing) { + struct timeval tv; + hrtime_t tnow; + int64_t tdiff; + fd_set rfds; + int err; - prev_tv.tv_sec = 0; - prev_tv.tv_usec = 0; - while (rc->cfd >= 0) { FD_ZERO(&rfds); - FD_SET(cfd, &rfds); + FD_SET(c->rc_fd, &rfds); tv.tv_sec = 0; - tv.tv_usec = CFD_SEL_DELAY; + tv.tv_usec = RFB_SEL_DELAY_US; - err = select(cfd+1, &rfds, NULL, NULL, &tv); + err = select(c->rc_fd + 1, &rfds, NULL, NULL, &tv); if (err < 0) - return (NULL); + break; - /* Determine if its time to push screen; ~24hz */ - gettimeofday(&tv, NULL); - tdiff = timeval_delta(&prev_tv, &tv); - if (tdiff >= SCREEN_POLL_DELAY) { + /* Determine if its time to push the screen; ~24hz. */ + tnow = gethrtime(); + tdiff = NSEC2USEC(tnow - tprev); + if (tdiff >= RFB_SCREEN_POLL_DELAY) { bool input; - prev_tv.tv_sec = tv.tv_sec; - prev_tv.tv_usec = tv.tv_usec; - input = atomic_exchange(&rc->input_detected, false); + + tprev = tnow; + + input = atomic_exchange(&c->rc_input_detected, false); /* - * Refresh the screen on every second trip through the loop, - * or if keyboard/mouse input has been detected. + * Refresh the screen on every second trip through the + * loop, or if keyboard/mouse input has been detected. */ - if ((++rc->wrcount & 1) || input) { - if (rfb_send_screen(rc, cfd) <= 0) { - return (NULL); - } + if ((++counter & 1) != 0 || input) { + if (!rfb_send_screen(c)) + break; } } else { - /* sleep */ - usleep(SCREEN_POLL_DELAY - tdiff); + (void) usleep(RFB_SCREEN_POLL_DELAY - tdiff); } } + rfb_printf(c, RFB_LOGWARN, "disconnected"); + +out: + + (void) pthread_join(c->rc_rx_tid, &status); + pthread_mutex_lock(&s->rs_clientlock); + s->rs_clientcount--; + list_remove(&s->rs_clients, c); + if (s->rs_exclusive && s->rs_clientcount == 0) + s->rs_exclusive = false; + id_free(rfb_idspace, c->rc_instance); + pthread_mutex_unlock(&s->rs_clientlock); + + rfb_free_client(c); return (NULL); } -void -rfb_handle(struct rfb_softc *rc, int cfd) +static void +rfb_accept(int sfd, enum ev_type event, void *arg) { - const char *vbuf = "RFB 003.008\n"; - unsigned char buf[80]; - unsigned char *message = NULL; - -#ifndef NO_OPENSSL - unsigned char challenge[AUTH_LENGTH]; - unsigned char keystr[PASSWD_LENGTH]; - unsigned char crypt_expected[AUTH_LENGTH]; - - DES_key_schedule ks; - int i; -#endif - uint8_t client_ver; - uint8_t auth_type; - pthread_t tid; - uint32_t sres = 0; - int len; - int perror = 1; - - rc->cfd = cfd; - - /* 1a. Send server version */ - stream_write(cfd, vbuf, strlen(vbuf)); - - /* 1b. Read client version */ - len = stream_read(cfd, buf, VERSION_LENGTH); -#ifdef __FreeBSD__ - if (len == VERSION_LENGTH && !strncmp(vbuf, buf, VERSION_LENGTH - 2)) { - client_ver = buf[VERSION_LENGTH - 2]; - } -#else - /* Work around gcc7 maybe-uninitialized warning */ - client_ver = CVERS_3_3; - if (len == VERSION_LENGTH && !strncmp(vbuf, (char *)buf, - VERSION_LENGTH - 2)) { - client_ver = buf[VERSION_LENGTH - 2]; - } -#endif - if (client_ver != CVERS_3_8 && client_ver != CVERS_3_7) { - /* only recognize 3.3, 3.7 & 3.8. Others dflt to 3.3 */ - client_ver = CVERS_3_3; + rfb_server_t *s = arg; + rfb_client_t *c = NULL; + struct sockaddr_storage cliaddr; + socklen_t len; + char host[NI_MAXHOST], port[NI_MAXSERV]; + int cfd, err; + uint_t cc; + + rfb_printf(c, RFB_LOGDEBUG, "incoming connection"); + + len = sizeof (cliaddr); + cfd = accept(sfd, (struct sockaddr *)&cliaddr, &len); + if (cfd == -1) { + perror("client accept"); + return; } - /* 2a. Send security type */ - buf[0] = 1; - - /* In versions 3.7 & 3.8, it's 2-way handshake */ - /* For version 3.3, server says what the authentication type must be */ -#ifndef NO_OPENSSL - if (rc->password) { - auth_type = SECURITY_TYPE_VNC_AUTH; + *host = *port = '\0'; + if (cliaddr.ss_family == AF_UNIX) { + rfb_printf(NULL, RFB_LOGDEBUG, "connection on UNIX socket"); + (void) strlcpy(host, "<UNIX>", sizeof (host)); } else { - auth_type = SECURITY_TYPE_NONE; - } -#else - auth_type = SECURITY_TYPE_NONE; -#endif - - switch (client_ver) { - case CVERS_3_7: - case CVERS_3_8: - buf[0] = 1; - buf[1] = auth_type; - stream_write(cfd, buf, 2); - - /* 2b. Read agreed security type */ - len = stream_read(cfd, buf, 1); - if (buf[0] != auth_type) { - /* deny */ - sres = htonl(1); -#ifdef __FreeBSD__ - message = "Auth failed: authentication type mismatch"; -#else - message = (unsigned char *) - "Auth failed: authentication type mismatch"; -#endif - goto report_and_done; - } - break; - case CVERS_3_3: - default: - be32enc(buf, auth_type); - stream_write(cfd, buf, 4); - break; - } - - /* 2c. Do VNC authentication */ - switch (auth_type) { - case SECURITY_TYPE_NONE: - break; - case SECURITY_TYPE_VNC_AUTH: - /* - * The client encrypts the challenge with DES, using a password - * supplied by the user as the key. - * To form the key, the password is truncated to - * eight characters, or padded with null bytes on the right. - * The client then sends the resulting 16-bytes response. - */ -#ifndef NO_OPENSSL -#ifdef __FreeBSD__ - strncpy(keystr, rc->password, PASSWD_LENGTH); -#else - strncpy((char *)keystr, rc->password, PASSWD_LENGTH); -#endif - - /* VNC clients encrypts the challenge with all the bit fields - * in each byte of the password mirrored. - * Here we flip each byte of the keystr. - */ - for (i = 0; i < PASSWD_LENGTH; i++) { - keystr[i] = (keystr[i] & 0xF0) >> 4 - | (keystr[i] & 0x0F) << 4; - keystr[i] = (keystr[i] & 0xCC) >> 2 - | (keystr[i] & 0x33) << 2; - keystr[i] = (keystr[i] & 0xAA) >> 1 - | (keystr[i] & 0x55) << 1; - } - - /* Initialize a 16-byte random challenge */ - arc4random_buf(challenge, sizeof(challenge)); - stream_write(cfd, challenge, AUTH_LENGTH); - - /* Receive the 16-byte challenge response */ - stream_read(cfd, buf, AUTH_LENGTH); - - memcpy(crypt_expected, challenge, AUTH_LENGTH); - - /* Encrypt the Challenge with DES */ - DES_set_key((const_DES_cblock *)keystr, &ks); - DES_ecb_encrypt((const_DES_cblock *)challenge, - (const_DES_cblock *)crypt_expected, - &ks, DES_ENCRYPT); - DES_ecb_encrypt((const_DES_cblock *)(challenge + PASSWD_LENGTH), - (const_DES_cblock *)(crypt_expected + - PASSWD_LENGTH), - &ks, DES_ENCRYPT); - - if (memcmp(crypt_expected, buf, AUTH_LENGTH) != 0) { -#ifdef __FreeBSD__ - message = "Auth Failed: Invalid Password."; -#else - message = - (unsigned char *)"Auth Failed: Invalid Password."; -#endif - sres = htonl(1); + err = getnameinfo((struct sockaddr *)&cliaddr, len, + host, sizeof (host), port, sizeof (port), + NI_NUMERICHOST | NI_NUMERICSERV); + if (err != 0) { + rfb_printf(NULL, RFB_LOGERR, "getnameinfo: %s", + gai_strerror(err)); + *host = *port = '\0'; } else { - sres = 0; - } -#else - sres = htonl(1); - WPRINTF(("Auth not supported, no OpenSSL in your system")); -#endif - - break; - } - - switch (client_ver) { - case CVERS_3_7: - case CVERS_3_8: -report_and_done: - /* 2d. Write back a status */ - stream_write(cfd, &sres, 4); - - if (sres) { - /* 3.7 does not want string explaining cause */ - if (client_ver == CVERS_3_8) { -#ifdef __FreeBSD__ - be32enc(buf, strlen(message)); - stream_write(cfd, buf, 4); - stream_write(cfd, message, strlen(message)); -#else - be32enc(buf, strlen((char *)message)); - stream_write(cfd, buf, 4); - stream_write(cfd, message, - strlen((char *)message)); -#endif - } - goto done; - } - break; - case CVERS_3_3: - default: - /* for VNC auth case send status */ - if (auth_type == SECURITY_TYPE_VNC_AUTH) { - /* 2d. Write back a status */ - stream_write(cfd, &sres, 4); - } - if (sres) { - goto done; + rfb_printf(NULL, RFB_LOGDEBUG, "connection from %s:%s", + host, port); } - break; - } - /* 3a. Read client shared-flag byte */ - len = stream_read(cfd, buf, 1); - - /* 4a. Write server-init info */ -#ifndef __FreeBSD__ - rfb_send_server_init_msg(cfd, rc); -#else - rfb_send_server_init_msg(cfd); -#endif - - if (!rc->zbuf) { - rc->zbuf = malloc(RFB_ZLIB_BUFSZ + 16); - assert(rc->zbuf != NULL); } - perror = pthread_create(&tid, NULL, rfb_wr_thr, rc); - if (perror == 0) - pthread_set_name_np(tid, "rfbout"); - - /* Now read in client requests. 1st byte identifies type */ - for (;;) { - len = read(cfd, buf, 1); - if (len <= 0) { - DPRINTF(("rfb client exiting")); - break; - } - - switch (buf[0]) { - case CS_SET_PIXEL_FORMAT: - rfb_recv_set_pixfmt_msg(rc, cfd); - break; - case CS_SET_ENCODINGS: - rfb_recv_set_encodings_msg(rc, cfd); - break; - case CS_UPDATE_MSG: - rfb_recv_update_msg(rc, cfd); - break; - case CS_KEY_EVENT: - rfb_recv_key_msg(rc, cfd); - break; - case CS_POINTER_EVENT: - rfb_recv_ptr_msg(rc, cfd); - break; - case CS_CUT_TEXT: - rfb_recv_cuttext_msg(rc, cfd); - break; - case CS_MSG_CLIENT_QEMU: - rfb_recv_client_msg(rc, cfd); - break; - default: - WPRINTF(("rfb unknown cli-code %d!", buf[0] & 0xff)); - goto done; - } + pthread_mutex_lock(&s->rs_clientlock); + cc = s->rs_clientcount; + pthread_mutex_unlock(&s->rs_clientlock); + if (cc >= RFB_MAX_CLIENTS) { + rfb_printf(NULL, RFB_LOGERR, + "too many clients, closing connection."); + goto fail; } -done: - rc->cfd = -1; - if (perror == 0) - pthread_join(tid, NULL); - if (rc->enc_zlib_ok) - deflateEnd(&rc->zstream); -} -static void * -rfb_thr(void *arg) -{ - struct rfb_softc *rc; - sigset_t set; - - int cfd; - - rc = arg; - - sigemptyset(&set); - sigaddset(&set, SIGPIPE); - if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) { - perror("pthread_sigmask"); - return (NULL); + if ((c = calloc(1, sizeof (rfb_client_t))) == NULL) { + perror("calloc client"); + goto fail; } - for (;;) { - rc->enc_raw_ok = false; - rc->enc_zlib_ok = false; - rc->enc_resize_ok = false; - rc->enc_extkeyevent_ok = false; + c->rc_fd = cfd; + c->rc_s = s; + c->rc_zbuf = malloc(RFB_ZLIB_BUFSZ + 16); + if (c->rc_zbuf == NULL) + goto fail; - rc->enc_extkeyevent_send = false; + pthread_mutex_lock(&s->rs_clientlock); - cfd = accept(rc->sfd, NULL, NULL); - if (rc->conn_wait) { - pthread_mutex_lock(&rc->mtx); - pthread_cond_signal(&rc->cond); - pthread_mutex_unlock(&rc->mtx); - rc->conn_wait = 0; - } - rfb_handle(rc, cfd); - close(cfd); + err = pthread_create(&c->rc_tx_tid, NULL, rfb_client_tx_thread, c); + if (err != 0) { + perror("pthread_create client tx thread"); + pthread_mutex_unlock(&s->rs_clientlock); + goto fail; } - /* NOTREACHED */ - return (NULL); -} + s->rs_clientcount++; + list_insert_tail(&s->rs_clients, c); + c->rc_instance = id_allocff(rfb_idspace); + pthread_mutex_unlock(&s->rs_clientlock); -static int -sse42_supported(void) -{ - u_int cpu_registers[4], ecx; + (void) pthread_detach(c->rc_tx_tid); - do_cpuid(1, cpu_registers); + rfb_printf(c, RFB_LOGWARN, "connection from %s", host); - ecx = cpu_registers[2]; + return; - return ((ecx & CPUID2_SSE42) != 0); +fail: + (void) close(cfd); + free(c); } int -#ifndef __FreeBSD__ -rfb_init(char *hostname, int port, int wait, char *password, const char *name) -#else -rfb_init(char *hostname, int port, int wait, char *password) -#endif +rfb_init(char *hostname, int port, int wait, const char *password, + const char *name) { - int e; - char servname[6]; - struct rfb_softc *rc; - struct addrinfo *ai = NULL; - struct addrinfo hints; - int on = 1; - int cnt; + rfb_server_t *s; #ifndef WITHOUT_CAPSICUM cap_rights_t rights; #endif - rc = calloc(1, sizeof(struct rfb_softc)); - - cnt = howmany(RFB_MAX_WIDTH, PIX_PER_CELL) * - howmany(RFB_MAX_HEIGHT, PIX_PER_CELL); - rc->crc = calloc(cnt, sizeof(uint32_t)); - rc->crc_tmp = calloc(cnt, sizeof(uint32_t)); - rc->crc_width = RFB_MAX_WIDTH; - rc->crc_height = RFB_MAX_HEIGHT; - rc->sfd = -1; + (void) pthread_once(&rfb_once, rfb_init_once); - rc->password = password; - -#ifndef __FreeBSD__ - rc->name = name; -#endif - - snprintf(servname, sizeof(servname), "%d", port ? port : 5900); - - if (!hostname || strlen(hostname) == 0) -#if defined(INET) - hostname = "127.0.0.1"; -#elif defined(INET6) - hostname = "[::1]"; -#endif - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE; - - if ((e = getaddrinfo(hostname, servname, &hints, &ai)) != 0) { - EPRINTLN("getaddrinfo: %s", gai_strerror(e)); - goto error; + if (rfb_idspace == NULL) { + rfb_printf(NULL, RFB_LOGERR, + "rfb_idspace could not be allocated"); + return (-1); } - rc->sfd = socket(ai->ai_family, ai->ai_socktype, 0); - if (rc->sfd < 0) { - perror("socket"); - goto error; + if ((s = calloc(1, sizeof (rfb_server_t))) == NULL) { + perror("calloc"); + return (-1); } + s->rs_fd = -1; + s->rs_name = name; - setsockopt(rc->sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - if (bind(rc->sfd, ai->ai_addr, ai->ai_addrlen) < 0) { - perror("bind"); - goto error; - } + if (password != NULL && strlen(password) > 0) + s->rs_password = password; - if (listen(rc->sfd, 1) < 0) { - perror("listen"); - goto error; + if (pthread_mutex_init(&s->rs_clientlock, NULL) != 0) { + perror("pthread_mutex_init"); + free(s); + return (-1); } -#ifndef WITHOUT_CAPSICUM - cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE); - if (caph_rights_limit(rc->sfd, &rights) == -1) - errx(EX_OSERR, "Unable to apply rights for sandbox"); -#endif + list_create(&s->rs_clients, sizeof (rfb_client_t), + offsetof(rfb_client_t, rc_node)); + + /* Server pixel format. */ + s->rs_pixfmt.rp_bpp = RFB_PIX_BPP; + s->rs_pixfmt.rp_depth = RFB_PIX_DEPTH; + s->rs_pixfmt.rp_bigendian = 0; + s->rs_pixfmt.rp_truecolour = 1; + s->rs_pixfmt.rp_r_max = htons(RFB_PIX_RMAX); + s->rs_pixfmt.rp_g_max = htons(RFB_PIX_GMAX); + s->rs_pixfmt.rp_b_max = htons(RFB_PIX_BMAX); + s->rs_pixfmt.rp_r_shift = RFB_PIX_RSHIFT; + s->rs_pixfmt.rp_g_shift = RFB_PIX_GSHIFT; + s->rs_pixfmt.rp_b_shift = RFB_PIX_BSHIFT; + + /* UNIX socket. */ + if (port == -1 && hostname != NULL && *hostname == '/') { + struct sockaddr_un sock; + + s->rs_fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (s->rs_fd < 0) { + perror("socket"); + goto fail; + } - rc->hw_crc = sse42_supported(); + sock.sun_family = AF_UNIX; + if (strlcpy(sock.sun_path, hostname, sizeof (sock.sun_path)) >= + sizeof (sock.sun_path)) { + rfb_printf(NULL, RFB_LOGERR, + "socket path '%s' too long\n", hostname); + goto fail; + } - rc->conn_wait = wait; - if (wait) { - pthread_mutex_init(&rc->mtx, NULL); - pthread_cond_init(&rc->cond, NULL); - } + (void) unlink(hostname); + if (bind(s->rs_fd, (struct sockaddr *)&sock, + sizeof (sock)) < 0) { + perror("bind"); + goto fail; + } + } else { + struct addrinfo hints, *ai = NULL; + char servname[6]; + int e; - pthread_create(&rc->tid, NULL, rfb_thr, rc); - pthread_set_name_np(rc->tid, "rfb"); + (void) snprintf(servname, sizeof (servname), "%d", + port ? port : RFB_DEFAULT_PORT); - if (wait) { - DPRINTF(("Waiting for rfb client...")); - pthread_mutex_lock(&rc->mtx); - pthread_cond_wait(&rc->cond, &rc->mtx); - pthread_mutex_unlock(&rc->mtx); - DPRINTF(("rfb client connected")); - } + if (hostname == NULL || strlen(hostname) == 0) { +#if defined(INET) + hostname = "127.0.0.1"; +#elif defined(INET6) + hostname = "[::1]"; +#endif + } - freeaddrinfo(ai); - return (0); + memset(&hints, '\0', sizeof (hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE; - error: - if (ai != NULL) - freeaddrinfo(ai); - if (rc->sfd != -1) - close(rc->sfd); - free(rc->crc); - free(rc->crc_tmp); - free(rc); - return (-1); -} + if ((e = getaddrinfo(hostname, servname, &hints, &ai)) != 0) { + rfb_printf(NULL, RFB_LOGERR, "getaddrinfo: %s", + gai_strerror(e)); + goto fail; + } -#ifndef __FreeBSD__ -int -rfb_init_unix(const char *path, int wait, char *password, const char *name) -{ - struct rfb_softc *rc; - struct sockaddr_un sock; + s->rs_fd = socket(ai->ai_family, ai->ai_socktype, 0); + if (s->rs_fd < 0) { + perror("socket"); + freeaddrinfo(ai); + goto fail; + } - if ((rc = calloc(1, sizeof (struct rfb_softc))) == NULL) { - perror("calloc"); - return (-1); - } - rc->sfd = -1; + e = 1; + (void) setsockopt(s->rs_fd, SOL_SOCKET, SO_REUSEADDR, + &e, sizeof (e)); - if ((rc->crc = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32), - sizeof (uint32_t))) == NULL) { - perror("calloc"); - goto fail; - } - if ((rc->crc_tmp = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32), - sizeof (uint32_t))) == NULL) { - perror("calloc"); - goto fail; + if (bind(s->rs_fd, ai->ai_addr, ai->ai_addrlen) < 0) { + perror("bind"); + freeaddrinfo(ai); + goto fail; + } + freeaddrinfo(ai); } - rc->crc_width = RFB_MAX_WIDTH; - rc->crc_height = RFB_MAX_HEIGHT; - rc->password = password; - rc->name = name; - - rc->sfd = socket(PF_UNIX, SOCK_STREAM, 0); - if (rc->sfd < 0) { - perror("socket"); + if (listen(s->rs_fd, 5) < 0) { + perror("listen"); goto fail; } - sock.sun_family = AF_UNIX; - if (strlcpy(sock.sun_path, path, sizeof (sock.sun_path)) >= - sizeof (sock.sun_path)) { - (void) fprintf(stderr, "socket path '%s' too long\n", path); - goto fail; - } +#ifndef WITHOUT_CAPSICUM + cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE); + if (caph_rights_limit(s->rs_fd, &rights) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); +#endif - (void) unlink(path); - if (bind(rc->sfd, (struct sockaddr *)&sock, sizeof (sock)) < 0) { - perror("bind"); + s->rs_connevent = mevent_add(s->rs_fd, EVF_READ, rfb_accept, s); + if (s->rs_connevent == NULL) { + rfb_printf(NULL, RFB_LOGERR, + "Failed to set up rfb connection mevent"); goto fail; } - if (listen(rc->sfd, 1) < 0) { - perror("listen"); - goto fail; - } + list_insert_tail(&rfb_list, s); - rc->hw_crc = sse42_supported(); + /* + * Wait for first connection. Since the mevent thread is + * not yet running, we can't rely on normal incoming connection + * handling. + */ + if (wait != 0) { + fd_set rfds; + int e; - rc->conn_wait = wait; - if (wait) { - VERIFY3S(pthread_mutex_init(&rc->mtx, NULL), ==, 0); - VERIFY3S(pthread_cond_init(&rc->cond, NULL), ==, 0); - } + rfb_printf(NULL, RFB_LOGWARN, + "holding boot until first client connection"); - VERIFY3S(pthread_create(&rc->tid, NULL, rfb_thr, rc), ==, 0); - pthread_set_name_np(rc->tid, "rfb"); + for (;;) { + FD_ZERO(&rfds); + FD_SET(s->rs_fd, &rfds); - if (wait) { - DPRINTF(("Waiting for rfb client...\n")); - pthread_mutex_lock(&rc->mtx); - VERIFY3S(pthread_cond_wait(&rc->cond, &rc->mtx), ==, 0); - pthread_mutex_unlock(&rc->mtx); + e = select(s->rs_fd + 1, &rfds, NULL, NULL, NULL); + if (e < 0 && errno == EINTR) + continue; + if (e < 0 || FD_ISSET(s->rs_fd, &rfds)) + break; + } + rfb_printf(NULL, RFB_LOGWARN, "continuing boot"); } return (0); fail: - if (rc->sfd != -1) { - VERIFY3S(close(rc->sfd), ==, 0); - } - free(rc->crc); - free(rc->crc_tmp); - free(rc); + if (s->rs_fd != -1) + VERIFY3S(close(s->rs_fd), ==, 0); + (void) pthread_mutex_destroy(&s->rs_clientlock); + list_destroy(&s->rs_clients); + free(s); return (-1); } -#endif diff --git a/usr/src/cmd/bhyve/rfb.h b/usr/src/cmd/bhyve/rfb.h index f779b0a5df..b0667f5994 100644 --- a/usr/src/cmd/bhyve/rfb.h +++ b/usr/src/cmd/bhyve/rfb.h @@ -3,7 +3,7 @@ * * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> * Copyright 2018 Joyent, Inc. - * Copyright 2021 OmniOS Community Edition (OmniOSce) Association. + * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,15 +33,9 @@ #ifndef _RFB_H_ #define _RFB_H_ -#define RFB_PORT 5900 +#define RFB_DEFAULT_PORT 5900 -#ifndef __FreeBSD__ -int rfb_init(char *hostname, int port, int wait, char *password, +int rfb_init(char *hostname, int port, int wait, const char *password, const char *name); -int rfb_init_unix(const char *path, int wait, char *password, - const char *name); -#else -int rfb_init(char *hostname, int port, int wait, char *password); -#endif #endif /* _RFB_H_ */ diff --git a/usr/src/cmd/bhyve/rfb_impl.h b/usr/src/cmd/bhyve/rfb_impl.h new file mode 100644 index 0000000000..883c9d7a0d --- /dev/null +++ b/usr/src/cmd/bhyve/rfb_impl.h @@ -0,0 +1,252 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. + */ + +#ifndef _RFB_IMPL_H +#define _RFB_IMPL_H + +#include <stdatomic.h> +#include <stdbool.h> +#include <zlib.h> +#include <sys/types.h> +#include <sys/list.h> + +#include "mevent.h" + +/* + * The ProtocolVersion message consists of 12 bytes interpreted as a string of + * ASCII characters in the format "RFB xxx.yyy\n" where xxx and yyy are the + * major and minor version numbers, padded with zeros. + */ +#define RFB_VERSION "RFB 003.008\n" +#define RFB_VERSION_LEN (sizeof (RFB_VERSION) - 1) + +_Static_assert(RFB_VERSION_LEN == 12, "RFB_VERSION length incorrect"); + +/* Keep synchronised with pci_fbuf.c */ +#define RFB_MAX_WIDTH 1920 +#define RFB_MAX_HEIGHT 1200 + +#define RFB_MAX_CLIENTS 10 + +/* Framebuffer pixel format */ +#define RFB_PIX_BPP 32 +#define RFB_PIX_DEPTH 24 +#define RFB_PIX_RSHIFT 16 +#define RFB_PIX_GSHIFT 8 +#define RFB_PIX_BSHIFT 0 +#define RFB_PIX_RMAX 255 +#define RFB_PIX_GMAX 255 +#define RFB_PIX_BMAX 255 + +#define RFB_ZLIB_BUFSZ (RFB_MAX_WIDTH * RFB_MAX_HEIGHT * 4) + +#define RFB_PIX_PER_CELL 32 +#define RFB_PIXCELL_SHIFT 5 +#define RFB_PIXCELL_MASK 0x1f +#define RFB_SENDALL_THRESH 25 + +#define RFB_SEL_DELAY_US 10000 +#define RFB_SCREEN_REFRESH_DELAY 33300 /* 30 Hz */ +#define RFB_SCREEN_POLL_DELAY (RFB_SCREEN_REFRESH_DELAY / 2) + +/* Client-to-server message types */ +#define RFBP_CS_SET_PIXEL_FORMAT 0 +#define RFBP_CS_SET_ENCODINGS 2 +#define RFBP_CS_UPDATE_REQUEST 3 +#define RFBP_CS_KEY_EVENT 4 +#define RFBP_CS_POINTER_EVENT 5 +#define RFBP_CS_CUT_TEXT 6 +#define RFBP_CS_QEMU 255 +#define RFBP_CS_QEMU_KEVENT 0 + +/* Server-to-client message types */ +#define RFBP_SC_UPDATE 0 +#define RFBP_SC_SET_COLOURMAP_ENTRIES 1 +#define RFBP_SC_BELL 2 +#define RFBP_SC_CUT_TEXT 3 + +/* Encodings */ +#define RFBP_ENCODING_RAW 0 +#define RFBP_ENCODING_ZLIB 6 +/* Pseudo-encodings */ +#define RFBP_ENCODING_RESIZE -223 +#define RFBP_ENCODING_EXT_KEVENT -258 /* QEMU ext. key event */ +#define RFBP_ENCODING_DESKTOP_NAME -307 + +/* Security types */ +#define RFBP_SECURITY_INVALID 0 +#define RFBP_SECURITY_NONE 1 +#define RFBP_SECURITY_VNC_AUTH 2 + +#define RFBP_SECURITY_VNC_AUTH_LEN 16 +#define RFBP_SECURITY_VNC_PASSWD_LEN 8 + +typedef enum rfb_loglevel { + RFB_LOGDEBUG, + RFB_LOGWARN, + RFB_LOGERR +} rfb_loglevel_t; + +typedef enum rfb_encodings { + RFB_ENCODING_RAW = (1ULL << 0), + RFB_ENCODING_ZLIB = (1ULL << 1), + RFB_ENCODING_RESIZE = (1ULL << 2), + RFB_ENCODING_EXT_KEVENT = (1ULL << 3), + RFB_ENCODING_DESKTOP_NAME = (1ULL << 4) +} rfb_encodings_t; + +typedef enum rfb_cver { + RFB_CVER_3_3, + RFB_CVER_3_7, + RFB_CVER_3_8 +} rfb_cver_t; + +typedef struct rfb_pixfmt { + uint8_t rp_bpp; + uint8_t rp_depth; + uint8_t rp_bigendian; + uint8_t rp_truecolour; + uint16_t rp_r_max; + uint16_t rp_g_max; + uint16_t rp_b_max; + uint8_t rp_r_shift; + uint8_t rp_g_shift; + uint8_t rp_b_shift; + uint8_t rp_pad[3]; +} __packed rfb_pixfmt_t; + +/* Server-to-client message formats */ + +typedef struct rfb_server_info { + uint16_t rsi_width; + uint16_t rsi_height; + rfb_pixfmt_t rsi_pixfmt; + uint32_t rsi_namelen; +} __packed rfb_server_info_t; + +typedef struct rfb_server_update_msg { + uint8_t rss_type; + uint8_t rss_pad; + uint16_t rss_numrects; +} __packed rfb_server_update_msg_t; + +typedef struct rfb_rect_hdr { + uint16_t rr_x; + uint16_t rr_y; + uint16_t rr_width; + uint16_t rr_height; + uint32_t rr_encoding; +} __packed rfb_rect_hdr_t; + +/* Client-to-server message formats */ + +typedef struct rfb_cs_pixfmt_msg { + uint8_t rp_pad[3]; + rfb_pixfmt_t rp_pixfmt; +} __packed rfb_cs_pixfmt_msg_t; + +typedef struct rfb_cs_update_msg { + uint8_t rum_incremental; + uint16_t rum_x; + uint16_t rum_y; + uint16_t rum_width; + uint16_t rum_height; +} __packed rfb_cs_update_msg_t; + +typedef struct rfb_cs_encodings_msg { + uint8_t re_pad; + uint16_t re_numencs; +} __packed rfb_cs_encodings_msg_t; + +typedef struct rfb_cs_key_event_msg { + uint8_t rke_down; + uint16_t rke_pad; + uint32_t rke_sym; +} __packed rfb_cs_key_event_msg_t; + +typedef struct rfb_cs_pointer_event_msg { + uint8_t rpe_button; + uint16_t rpe_x; + uint16_t rpe_y; +} __packed rfb_cs_pointer_event_msg_t; + +typedef struct rfb_cs_cut_text_msg { + uint8_t rct_padding[3]; + uint32_t rct_length; +} __packed rfb_cs_cut_text_msg_t; + +typedef struct rfb_cs_qemu_msg { + uint8_t rq_subtype; +} __packed rfb_cs_qemu_msg_t; + +typedef struct rfb_cs_qemu_extended_key_msg { + uint16_t rqek_down; + uint32_t rqek_sym; + uint32_t rqek_code; +} __packed rfb_cs_qemu_extended_key_msg_t; + +/* Client/server data structures */ + +typedef struct rfb_server { + list_node_t rs_node; + int rs_fd; + const char *rs_name; + const char *rs_password; + + struct mevent *rs_connevent; + + uint_t rs_clientcount; + pthread_mutex_t rs_clientlock; + list_t rs_clients; + + bool rs_exclusive; + + rfb_pixfmt_t rs_pixfmt; +} rfb_server_t; + +typedef struct rfb_client { + list_node_t rc_node; + uint_t rc_instance; + + int rc_fd; + rfb_server_t *rc_s; + rfb_server_info_t rc_sinfo; + pthread_t rc_rx_tid; + pthread_t rc_tx_tid; + + int rc_width; + int rc_height; + size_t rc_cells; + uint32_t *rc_crc; + uint32_t *rc_crc_tmp; + z_stream rc_zstream; + uint8_t *rc_zbuf; + + struct bhyvegc_image rc_gci; + + rfb_cver_t rc_cver; + rfb_encodings_t rc_encodings; + + atomic_bool rc_closing; + atomic_bool rc_pending; + atomic_bool rc_input_detected; + atomic_bool rc_crc_reset; + atomic_bool rc_send_fullscreen; + + bool rc_custom_pixfmt; + bool rc_keyevent_sent; +} rfb_client_t; + +#endif /* _RFB_IMPL_H */ diff --git a/usr/src/lib/lib9p/common/backend/fs.c b/usr/src/lib/lib9p/common/backend/fs.c index 4b7764cd86..792e17ebd2 100644 --- a/usr/src/lib/lib9p/common/backend/fs.c +++ b/usr/src/lib/lib9p/common/backend/fs.c @@ -1838,8 +1838,11 @@ fs_remove(void *softc, struct l9p_fid *fid) return (error); if (unlinkat(file->ff_dirfd, file->ff_name, - S_ISDIR(cst.st_mode) ? AT_REMOVEDIR : 0) != 0) + S_ISDIR(cst.st_mode) ? AT_REMOVEDIR : 0) != 0) { error = errno; + if (error == EEXIST && S_ISDIR(cst.st_mode)) + error = ENOTEMPTY; + } return (error); } @@ -3038,8 +3041,11 @@ fs_unlinkat(void *softc, struct l9p_request *req) return (error); if (req->lr_req.tunlinkat.flags & L9PL_AT_REMOVEDIR) { - if (unlinkat(dirff->ff_dirfd, newname, AT_REMOVEDIR) != 0) + if (unlinkat(dirff->ff_dirfd, newname, AT_REMOVEDIR) != 0) { error = errno; + if (error == EEXIST) + error = ENOTEMPTY; + } } else { if (unlinkat(dirff->ff_dirfd, newname, 0) != 0) error = errno; diff --git a/usr/src/lib/lib9p/common/request.c b/usr/src/lib/lib9p/common/request.c index 99885690af..644524e12d 100644 --- a/usr/src/lib/lib9p/common/request.c +++ b/usr/src/lib/lib9p/common/request.c @@ -1410,12 +1410,12 @@ l9p_dispatch_trenameat(struct l9p_request *req) int error; error = fid_lookup(conn, req->lr_req.hdr.fid, ENOENT, - F_REQUIRE_DIR | F_FORBID_OPEN, &req->lr_fid); + F_REQUIRE_DIR, &req->lr_fid); if (error) return (error); error = fid_lookup(conn, req->lr_req.trenameat.newdirfid, ENOENT, - F_REQUIRE_DIR | F_FORBID_OPEN, &req->lr_fid2); + F_REQUIRE_DIR, &req->lr_fid2); if (error) return (error); @@ -1434,7 +1434,7 @@ l9p_dispatch_tunlinkat(struct l9p_request *req) int error; error = fid_lookup(conn, req->lr_req.hdr.fid, ENOENT, - F_REQUIRE_DIR | F_FORBID_OPEN, &req->lr_fid); + F_REQUIRE_DIR, &req->lr_fid); if (error) return (error); diff --git a/usr/src/lib/libidspace/common/libidspace.h b/usr/src/lib/libidspace/common/libidspace.h index bb8690f19c..f78a3eb1fd 100644 --- a/usr/src/lib/libidspace/common/libidspace.h +++ b/usr/src/lib/libidspace/common/libidspace.h @@ -11,6 +11,7 @@ /* * Copyright (c) 2014 Joyent, Inc. All rights reserved. + * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. */ #ifndef _LIBIDSPACE_H @@ -32,6 +33,7 @@ extern id_space_t *id_space_create(const char *, id_t, id_t); extern void id_space_destroy(id_space_t *); extern void id_space_extend(id_space_t *, id_t, id_t); extern id_t id_alloc(id_space_t *); +extern id_t id_allocff(id_space_t *); extern id_t id_alloc_specific(id_space_t *, id_t); extern void id_free(id_space_t *, id_t); diff --git a/usr/src/lib/libidspace/common/mapfile-vers b/usr/src/lib/libidspace/common/mapfile-vers index 61ae855ee0..248c75b2e3 100644 --- a/usr/src/lib/libidspace/common/mapfile-vers +++ b/usr/src/lib/libidspace/common/mapfile-vers @@ -11,6 +11,7 @@ # # Copyright (c) 2014 Joyent, Inc. All rights reserved. +# Copyright 2022 OmniOS Community Edition (OmniOSce) Association. # # @@ -31,7 +32,8 @@ $mapfile_version 2 SYMBOL_VERSION ILLUMOS_1.0 { # first release of libidspace global: - id_alloc; + id_alloc; + id_allocff; id_alloc_specific; id_free; id_space_create; diff --git a/usr/src/lib/libumem/common/umem_update_thread.c b/usr/src/lib/libumem/common/umem_update_thread.c index eb044b7641..fc64f9320b 100644 --- a/usr/src/lib/libumem/common/umem_update_thread.c +++ b/usr/src/lib/libumem/common/umem_update_thread.c @@ -142,6 +142,7 @@ umem_create_update_thread(void) THR_BOUND | THR_DAEMON | THR_DETACHED | THR_SUSPENDED, &newthread) == 0) { (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); + (void) thr_setname(newthread, "umem_update"); (void) mutex_lock(&umem_update_lock); /* diff --git a/usr/src/man/man5/bhyve_config.5 b/usr/src/man/man5/bhyve_config.5 index 5e9caa9857..17788deaf8 100644 --- a/usr/src/man/man5/bhyve_config.5 +++ b/usr/src/man/man5/bhyve_config.5 @@ -25,7 +25,7 @@ .\" .\" Portions Copyright 2022 OmniOS Community Edition (OmniOSce) Association. .\" -.Dd February 26, 2022 +.Dd April 6, 2022 .Dt BHYVE_CONFIG 5 .Os .Sh NAME @@ -159,6 +159,16 @@ No VM will be started. Enable debug messages relating to privilege management. These messages are sent to .Dv stdout . +.It Va rfb.debug Ta bool Ta false Ta +Enable debug messages relating to the RFB +.Pq VNC +server. +.It Va xhci.debug Ta bool Ta false Ta +Enable debug messages relating to the emulated XHCI +.Pq USB +controller. +These messages are sent to +.Dv stderr . .El .Ss x86-Specific Settings .Bl -column "x86.vmexit_on_pause" "integer" "Default" |