1bf21cd93STycho Nightingale /*-
2*32640292SAndy Fiddaman * SPDX-License-Identifier: BSD-2-Clause
34c87aefeSPatrick Mooney *
4bf21cd93STycho Nightingale * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
54c87aefeSPatrick Mooney * Copyright (c) 2015 Leon Dang
626624470SAndy Fiddaman * Copyright 2020 Joyent, Inc.
7bf21cd93STycho Nightingale * All rights reserved.
8bf21cd93STycho Nightingale *
9bf21cd93STycho Nightingale * Redistribution and use in source and binary forms, with or without
10bf21cd93STycho Nightingale * modification, are permitted provided that the following conditions
11bf21cd93STycho Nightingale * are met:
12bf21cd93STycho Nightingale * 1. Redistributions of source code must retain the above copyright
13bf21cd93STycho Nightingale * notice, this list of conditions and the following disclaimer.
14bf21cd93STycho Nightingale * 2. Redistributions in binary form must reproduce the above copyright
15bf21cd93STycho Nightingale * notice, this list of conditions and the following disclaimer in the
16bf21cd93STycho Nightingale * documentation and/or other materials provided with the distribution.
17bf21cd93STycho Nightingale *
18bf21cd93STycho Nightingale * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
19bf21cd93STycho Nightingale * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20bf21cd93STycho Nightingale * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21bf21cd93STycho Nightingale * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22bf21cd93STycho Nightingale * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23bf21cd93STycho Nightingale * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24bf21cd93STycho Nightingale * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25bf21cd93STycho Nightingale * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26bf21cd93STycho Nightingale * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27bf21cd93STycho Nightingale * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28bf21cd93STycho Nightingale * SUCH DAMAGE.
29bf21cd93STycho Nightingale */
301aa1f41fSAndy Fiddaman /*
311aa1f41fSAndy Fiddaman * This file and its contents are supplied under the terms of the
321aa1f41fSAndy Fiddaman * Common Development and Distribution License ("CDDL"), version 1.0.
331aa1f41fSAndy Fiddaman * You may only use this file in accordance with the terms of version
341aa1f41fSAndy Fiddaman * 1.0 of the CDDL.
351aa1f41fSAndy Fiddaman *
361aa1f41fSAndy Fiddaman * A full copy of the text of the CDDL should have accompanied this
371aa1f41fSAndy Fiddaman * source. A copy of the CDDL is also available via the Internet at
381aa1f41fSAndy Fiddaman * http://www.illumos.org/license/CDDL.
391aa1f41fSAndy Fiddaman *
401aa1f41fSAndy Fiddaman * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
411aa1f41fSAndy Fiddaman */
42bf21cd93STycho Nightingale
431aa1f41fSAndy Fiddaman /*
441aa1f41fSAndy Fiddaman * References to the RFB protocol specification refer to:
451aa1f41fSAndy Fiddaman * - [1] https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst
461aa1f41fSAndy Fiddaman */
47bf21cd93STycho Nightingale
484c87aefeSPatrick Mooney #include <err.h>
494c87aefeSPatrick Mooney #include <errno.h>
501aa1f41fSAndy Fiddaman #include <libidspace.h>
511aa1f41fSAndy Fiddaman #include <netdb.h>
52bf21cd93STycho Nightingale #include <pthread.h>
534c87aefeSPatrick Mooney #include <pthread_np.h>
54bf21cd93STycho Nightingale #include <signal.h>
551aa1f41fSAndy Fiddaman #include <stdatomic.h>
56bf21cd93STycho Nightingale #include <stdbool.h>
57bf21cd93STycho Nightingale #include <stdio.h>
581aa1f41fSAndy Fiddaman #include <stdlib.h>
59bf21cd93STycho Nightingale #include <string.h>
60bf21cd93STycho Nightingale #include <unistd.h>
614c87aefeSPatrick Mooney #include <zlib.h>
621aa1f41fSAndy Fiddaman #include <machine/cpufunc.h>
631aa1f41fSAndy Fiddaman #include <machine/specialreg.h>
641aa1f41fSAndy Fiddaman #include <netinet/in.h>
654c87aefeSPatrick Mooney #ifndef NO_OPENSSL
664c87aefeSPatrick Mooney #include <openssl/des.h>
674c87aefeSPatrick Mooney #endif
681aa1f41fSAndy Fiddaman #include <sys/debug.h>
691aa1f41fSAndy Fiddaman #include <sys/endian.h>
701aa1f41fSAndy Fiddaman #include <sys/list.h>
711aa1f41fSAndy Fiddaman #include <sys/socket.h>
721aa1f41fSAndy Fiddaman #include <sys/types.h>
731aa1f41fSAndy Fiddaman #ifndef WITHOUT_CAPSICUM
741aa1f41fSAndy Fiddaman #include <sysexits.h>
751aa1f41fSAndy Fiddaman #include <sys/capsicum.h>
761aa1f41fSAndy Fiddaman #include <capsicum_helpers.h>
7737fc8a1fSAndy Fiddaman #endif
78bf21cd93STycho Nightingale
791aa1f41fSAndy Fiddaman #include "bhyvegc.h"
801aa1f41fSAndy Fiddaman #include "config.h"
811aa1f41fSAndy Fiddaman #include "debug.h"
821aa1f41fSAndy Fiddaman #include "console.h"
831aa1f41fSAndy Fiddaman #include "rfb.h"
841aa1f41fSAndy Fiddaman #include "rfb_impl.h"
851aa1f41fSAndy Fiddaman #include "sockstream.h"
86bf21cd93STycho Nightingale
871aa1f41fSAndy Fiddaman static uint_t rfb_debug = 0;
881aa1f41fSAndy Fiddaman static list_t rfb_list;
891aa1f41fSAndy Fiddaman static id_space_t *rfb_idspace;
90bf21cd93STycho Nightingale
911aa1f41fSAndy Fiddaman static bool rfb_sse42;
921aa1f41fSAndy Fiddaman static pthread_once_t rfb_once = PTHREAD_ONCE_INIT;
93bf21cd93STycho Nightingale
941aa1f41fSAndy Fiddaman extern int raw_stdio;
95b0de25cbSAndy Fiddaman
961aa1f41fSAndy Fiddaman static void rfb_send_extended_keyevent_update_msg(rfb_client_t *);
974c87aefeSPatrick Mooney
98bf21cd93STycho Nightingale static void
rfb_printf(rfb_client_t * c,rfb_loglevel_t level,const char * fmt,...)991aa1f41fSAndy Fiddaman rfb_printf(rfb_client_t *c, rfb_loglevel_t level, const char *fmt, ...)
100bf21cd93STycho Nightingale {
1011aa1f41fSAndy Fiddaman FILE *fp = stdout;
1021aa1f41fSAndy Fiddaman va_list ap;
103bf21cd93STycho Nightingale
1041aa1f41fSAndy Fiddaman switch (level) {
1051aa1f41fSAndy Fiddaman case RFB_LOGDEBUG:
1061aa1f41fSAndy Fiddaman if (rfb_debug == 0)
1071aa1f41fSAndy Fiddaman return;
1081aa1f41fSAndy Fiddaman /* FALLTHROUGH */
1091aa1f41fSAndy Fiddaman case RFB_LOGERR:
1101aa1f41fSAndy Fiddaman fp = stderr;
1111aa1f41fSAndy Fiddaman /* FALLTHROUGH */
1121aa1f41fSAndy Fiddaman case RFB_LOGWARN:
1131aa1f41fSAndy Fiddaman if (c != NULL)
1141aa1f41fSAndy Fiddaman (void) fprintf(fp, "rfb%u: ", c->rc_instance);
1151aa1f41fSAndy Fiddaman else
1161aa1f41fSAndy Fiddaman (void) fprintf(fp, "rfb: ");
1171aa1f41fSAndy Fiddaman va_start(ap, fmt);
1181aa1f41fSAndy Fiddaman (void) vfprintf(fp, fmt, ap);
1191aa1f41fSAndy Fiddaman va_end(ap);
1201aa1f41fSAndy Fiddaman if (raw_stdio)
1211aa1f41fSAndy Fiddaman (void) fprintf(fp, "\r\n");
1221aa1f41fSAndy Fiddaman else
1231aa1f41fSAndy Fiddaman (void) fprintf(fp, "\n");
1241aa1f41fSAndy Fiddaman (void) fflush(fp);
1251aa1f41fSAndy Fiddaman }
126bf21cd93STycho Nightingale }
127bf21cd93STycho Nightingale
128bf21cd93STycho Nightingale static void
rfb_init_once(void)1291aa1f41fSAndy Fiddaman rfb_init_once(void)
130bf21cd93STycho Nightingale {
1311aa1f41fSAndy Fiddaman uint_t cpu_registers[4], ecx;
132bf21cd93STycho Nightingale
1331aa1f41fSAndy Fiddaman do_cpuid(1, cpu_registers);
1341aa1f41fSAndy Fiddaman ecx = cpu_registers[2];
1351aa1f41fSAndy Fiddaman rfb_sse42 = (ecx & CPUID2_SSE42) != 0;
136bf21cd93STycho Nightingale
1371aa1f41fSAndy Fiddaman if (rfb_sse42)
1381aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGDEBUG, "enabled fast crc32");
1391aa1f41fSAndy Fiddaman else
1401aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGWARN, "no support for fast crc32");
1411aa1f41fSAndy Fiddaman
1421aa1f41fSAndy Fiddaman if (get_config_bool_default("rfb.debug", false))
1431aa1f41fSAndy Fiddaman rfb_debug = 1;
1441aa1f41fSAndy Fiddaman
1451aa1f41fSAndy Fiddaman list_create(&rfb_list, sizeof (rfb_server_t),
1461aa1f41fSAndy Fiddaman offsetof(rfb_server_t, rs_node));
1471aa1f41fSAndy Fiddaman
1481aa1f41fSAndy Fiddaman rfb_idspace = id_space_create("rfb", 0, INT32_MAX);
149bf21cd93STycho Nightingale }
150bf21cd93STycho Nightingale
151bf21cd93STycho Nightingale static void
rfb_free_client(rfb_client_t * c)1521aa1f41fSAndy Fiddaman rfb_free_client(rfb_client_t *c)
153b0de25cbSAndy Fiddaman {
1541aa1f41fSAndy Fiddaman free(c->rc_crc);
1551aa1f41fSAndy Fiddaman free(c->rc_crc_tmp);
1561aa1f41fSAndy Fiddaman free(c->rc_zbuf);
1571aa1f41fSAndy Fiddaman free(c->rc_gci.data);
158b0de25cbSAndy Fiddaman
1591aa1f41fSAndy Fiddaman if (c->rc_encodings & RFB_ENCODING_ZLIB)
1601aa1f41fSAndy Fiddaman (void) deflateEnd(&c->rc_zstream);
161b0de25cbSAndy Fiddaman
1621aa1f41fSAndy Fiddaman if (c->rc_fd != -1)
1631aa1f41fSAndy Fiddaman (void) close(c->rc_fd);
164b0de25cbSAndy Fiddaman
1651aa1f41fSAndy Fiddaman free(c);
166bf21cd93STycho Nightingale }
167bf21cd93STycho Nightingale
1684c87aefeSPatrick Mooney /*
1694c87aefeSPatrick Mooney * Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only
1704c87aefeSPatrick Mooney */
1711aa1f41fSAndy Fiddaman static inline uint32_t
fast_crc32(void * buf,int len,uint32_t crcval)1724c87aefeSPatrick Mooney fast_crc32(void *buf, int len, uint32_t crcval)
1734c87aefeSPatrick Mooney {
1744c87aefeSPatrick Mooney uint32_t q = len / sizeof (uint32_t);
1754c87aefeSPatrick Mooney uint32_t *p = (uint32_t *)buf;
1764c87aefeSPatrick Mooney
1774c87aefeSPatrick Mooney while (q--) {
1781aa1f41fSAndy Fiddaman /* BEGIN CSTYLED */
1794c87aefeSPatrick Mooney asm volatile (
1801aa1f41fSAndy Fiddaman /* crc32l %ecx,%esi */
1814c87aefeSPatrick Mooney ".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
1824c87aefeSPatrick Mooney :"=S" (crcval)
1834c87aefeSPatrick Mooney :"0" (crcval), "c" (*p)
1844c87aefeSPatrick Mooney );
1851aa1f41fSAndy Fiddaman /* END CSTYLED */
1864c87aefeSPatrick Mooney p++;
1874c87aefeSPatrick Mooney }
1884c87aefeSPatrick Mooney
1894c87aefeSPatrick Mooney return (crcval);
1904c87aefeSPatrick Mooney }
1914c87aefeSPatrick Mooney
1921aa1f41fSAndy Fiddaman static void
rfb_send_client_status(rfb_client_t * c,uint32_t status,const char * msg)1931aa1f41fSAndy Fiddaman rfb_send_client_status(rfb_client_t *c, uint32_t status, const char *msg)
194dc8050e8SMarko Kiiskila {
19538864087SAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "sending client status %u (%s)",
19638864087SAndy Fiddaman status, msg ? msg : "NULL");
197dc8050e8SMarko Kiiskila
19838864087SAndy Fiddaman status = htonl(status);
1991aa1f41fSAndy Fiddaman (void) stream_write(c->rc_fd, &status, sizeof (status));
200dc8050e8SMarko Kiiskila
2011aa1f41fSAndy Fiddaman if (msg != NULL && status != 0 && c->rc_cver == RFB_CVER_3_8) {
2021aa1f41fSAndy Fiddaman char buf[4];
2031aa1f41fSAndy Fiddaman
2041aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN, msg);
2051aa1f41fSAndy Fiddaman
2061aa1f41fSAndy Fiddaman be32enc(buf, strlen((char *)msg));
2071aa1f41fSAndy Fiddaman (void) stream_write(c->rc_fd, buf, 4);
2081aa1f41fSAndy Fiddaman (void) stream_write(c->rc_fd, msg, strlen((char *)msg));
2091aa1f41fSAndy Fiddaman }
210dc8050e8SMarko Kiiskila }
2114c87aefeSPatrick Mooney
2121aa1f41fSAndy Fiddaman static bool
rfb_handshake_version(rfb_client_t * c)2131aa1f41fSAndy Fiddaman rfb_handshake_version(rfb_client_t *c)
2141aa1f41fSAndy Fiddaman {
2151aa1f41fSAndy Fiddaman unsigned char buf[RFB_VERSION_LEN];
2161aa1f41fSAndy Fiddaman ssize_t l;
2171aa1f41fSAndy Fiddaman
2181aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "handshake version");
2191aa1f41fSAndy Fiddaman
2201aa1f41fSAndy Fiddaman if (stream_write(c->rc_fd, RFB_VERSION, RFB_VERSION_LEN) !=
2211aa1f41fSAndy Fiddaman RFB_VERSION_LEN) {
2221aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "could not send server version.");
2231aa1f41fSAndy Fiddaman return (false);
2241aa1f41fSAndy Fiddaman }
2251aa1f41fSAndy Fiddaman
2261aa1f41fSAndy Fiddaman l = stream_read(c->rc_fd, buf, sizeof (buf));
2271aa1f41fSAndy Fiddaman if (l <= 0) {
2281aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "client version not read");
2291aa1f41fSAndy Fiddaman return (false);
2301aa1f41fSAndy Fiddaman } else if (l != RFB_VERSION_LEN) {
2311aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "client sent short version - '%.*s'",
2321aa1f41fSAndy Fiddaman l, buf);
2331aa1f41fSAndy Fiddaman return (false);
2341aa1f41fSAndy Fiddaman }
2351aa1f41fSAndy Fiddaman
2361aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "version handshake, client ver '%.*s'",
2371aa1f41fSAndy Fiddaman l - 1, buf);
2381aa1f41fSAndy Fiddaman
2391aa1f41fSAndy Fiddaman if (strncmp(RFB_VERSION, (char *)buf, RFB_VERSION_LEN - 2) != 0) {
2401aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGERR, "bad client version '%.*s'", l, buf);
2411aa1f41fSAndy Fiddaman return (false);
2421aa1f41fSAndy Fiddaman }
2431aa1f41fSAndy Fiddaman
2441aa1f41fSAndy Fiddaman switch (buf[RFB_VERSION_LEN - 2]) {
2451aa1f41fSAndy Fiddaman case '8':
2461aa1f41fSAndy Fiddaman c->rc_cver = RFB_CVER_3_8;
2471aa1f41fSAndy Fiddaman break;
2481aa1f41fSAndy Fiddaman case '7':
2491aa1f41fSAndy Fiddaman c->rc_cver = RFB_CVER_3_7;
2501aa1f41fSAndy Fiddaman break;
2511aa1f41fSAndy Fiddaman case '5':
2521aa1f41fSAndy Fiddaman /*
2531aa1f41fSAndy Fiddaman * From the RFB specification[1], section 7.1.1:
2541aa1f41fSAndy Fiddaman * "version 3.5 was wrongly reported by some clients, but this
2551aa1f41fSAndy Fiddaman * should be interpreted by all servers as 3.3."
2561aa1f41fSAndy Fiddaman */
2571aa1f41fSAndy Fiddaman case '3':
2581aa1f41fSAndy Fiddaman c->rc_cver = RFB_CVER_3_3;
2591aa1f41fSAndy Fiddaman break;
2601aa1f41fSAndy Fiddaman default:
2611aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGERR, "unsupported client version '%.*s'",
2621aa1f41fSAndy Fiddaman l - 1, buf);
2631aa1f41fSAndy Fiddaman return (false);
2641aa1f41fSAndy Fiddaman }
2651aa1f41fSAndy Fiddaman
2661aa1f41fSAndy Fiddaman return (true);
2671aa1f41fSAndy Fiddaman }
2681aa1f41fSAndy Fiddaman
2691aa1f41fSAndy Fiddaman static bool
rfb_handshake_auth(rfb_client_t * c)2701aa1f41fSAndy Fiddaman rfb_handshake_auth(rfb_client_t *c)
2711aa1f41fSAndy Fiddaman {
2721aa1f41fSAndy Fiddaman unsigned char buf[RFBP_SECURITY_VNC_AUTH_LEN];
2731aa1f41fSAndy Fiddaman int auth_type;
2741aa1f41fSAndy Fiddaman
2751aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "handshake auth");
2761aa1f41fSAndy Fiddaman
2771aa1f41fSAndy Fiddaman auth_type = RFBP_SECURITY_NONE;
2781aa1f41fSAndy Fiddaman #ifndef NO_OPENSSL
2791aa1f41fSAndy Fiddaman if (c->rc_s->rs_password != NULL)
2801aa1f41fSAndy Fiddaman auth_type = RFBP_SECURITY_VNC_AUTH;
2811aa1f41fSAndy Fiddaman #endif
2821aa1f41fSAndy Fiddaman
2831aa1f41fSAndy Fiddaman switch (c->rc_cver) {
2841aa1f41fSAndy Fiddaman case RFB_CVER_3_3:
2851aa1f41fSAndy Fiddaman /*
2861aa1f41fSAndy Fiddaman * RFB specification[1] section 7.1.2:
2871aa1f41fSAndy Fiddaman * The server decides the security type and sends a single word.
2881aa1f41fSAndy Fiddaman */
2891aa1f41fSAndy Fiddaman be32enc(buf, auth_type);
2901aa1f41fSAndy Fiddaman (void) stream_write(c->rc_fd, buf, 4);
2911aa1f41fSAndy Fiddaman
2921aa1f41fSAndy Fiddaman break;
2931aa1f41fSAndy Fiddaman
2941aa1f41fSAndy Fiddaman case RFB_CVER_3_7:
2951aa1f41fSAndy Fiddaman case RFB_CVER_3_8:
2961aa1f41fSAndy Fiddaman /* Send list of supported types. */
2971aa1f41fSAndy Fiddaman buf[0] = 1; /* list length */
2981aa1f41fSAndy Fiddaman buf[1] = auth_type;
2991aa1f41fSAndy Fiddaman (void) stream_write(c->rc_fd, buf, 2);
3001aa1f41fSAndy Fiddaman
3011aa1f41fSAndy Fiddaman /* Read agreed security type. */
3021aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, buf, 1) != 1) {
3031aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN,
3041aa1f41fSAndy Fiddaman "auth fail, no type from client");
3051aa1f41fSAndy Fiddaman return (false);
3061aa1f41fSAndy Fiddaman }
3071aa1f41fSAndy Fiddaman
3081aa1f41fSAndy Fiddaman if (buf[0] != auth_type) {
3091aa1f41fSAndy Fiddaman rfb_send_client_status(c, 1,
3101aa1f41fSAndy Fiddaman "Auth failed: authentication type mismatch");
3111aa1f41fSAndy Fiddaman return (false);
3121aa1f41fSAndy Fiddaman }
3131aa1f41fSAndy Fiddaman
3141aa1f41fSAndy Fiddaman break;
3151aa1f41fSAndy Fiddaman }
3161aa1f41fSAndy Fiddaman
3171aa1f41fSAndy Fiddaman if (auth_type == RFBP_SECURITY_NONE) {
3181aa1f41fSAndy Fiddaman /*
3191aa1f41fSAndy Fiddaman * According to the RFB specification[1], section 7.2.1, for a
3201aa1f41fSAndy Fiddaman * security type of 'None', client versions 3.3 and 3.7 expect
3211aa1f41fSAndy Fiddaman * to move straight to the ClientInit phase, without the server
3221aa1f41fSAndy Fiddaman * sending a response. For version 3.8, a SecurityResult word
3231aa1f41fSAndy Fiddaman * needs to be sent indicating success.
3241aa1f41fSAndy Fiddaman */
3251aa1f41fSAndy Fiddaman switch (c->rc_cver) {
3261aa1f41fSAndy Fiddaman case RFB_CVER_3_3:
3271aa1f41fSAndy Fiddaman case RFB_CVER_3_7:
3281aa1f41fSAndy Fiddaman break;
3291aa1f41fSAndy Fiddaman case RFB_CVER_3_8:
3301aa1f41fSAndy Fiddaman rfb_send_client_status(c, 0, NULL);
3311aa1f41fSAndy Fiddaman break;
3321aa1f41fSAndy Fiddaman }
3331aa1f41fSAndy Fiddaman return (true);
3341aa1f41fSAndy Fiddaman }
3351aa1f41fSAndy Fiddaman
3361aa1f41fSAndy Fiddaman /* Perform VNC authentication. */
3371aa1f41fSAndy Fiddaman
3381aa1f41fSAndy Fiddaman #ifdef NO_OPENSSL
3391aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGERR,
3401aa1f41fSAndy Fiddaman "Auth not supported, no OpenSSL in your system");
3411aa1f41fSAndy Fiddaman rfb_send_client_status(c, 1, "Auth failed.");
3421aa1f41fSAndy Fiddaman return (false);
3431aa1f41fSAndy Fiddaman #else
3441aa1f41fSAndy Fiddaman unsigned char challenge[RFBP_SECURITY_VNC_AUTH_LEN];
3451aa1f41fSAndy Fiddaman unsigned char keystr[RFBP_SECURITY_VNC_PASSWD_LEN];
3461aa1f41fSAndy Fiddaman unsigned char crypt_expected[RFBP_SECURITY_VNC_AUTH_LEN];
3471aa1f41fSAndy Fiddaman DES_key_schedule ks;
3481aa1f41fSAndy Fiddaman
3491aa1f41fSAndy Fiddaman /*
3501aa1f41fSAndy Fiddaman * The client encrypts the challenge with DES, using a password
3511aa1f41fSAndy Fiddaman * supplied by the user as the key.
3521aa1f41fSAndy Fiddaman * To form the key, the password is truncated to eight characters, or
3531aa1f41fSAndy Fiddaman * padded with null bytes on the right.
3541aa1f41fSAndy Fiddaman * The client then sends the resulting 16-bytes response.
3551aa1f41fSAndy Fiddaman */
3561aa1f41fSAndy Fiddaman (void) strncpy((char *)keystr, c->rc_s->rs_password,
3571aa1f41fSAndy Fiddaman RFBP_SECURITY_VNC_PASSWD_LEN);
3581aa1f41fSAndy Fiddaman
3591aa1f41fSAndy Fiddaman /*
3601aa1f41fSAndy Fiddaman * VNC clients encrypt the challenge with all the bit fields in each
3611aa1f41fSAndy Fiddaman * byte of the password mirrored.
3621aa1f41fSAndy Fiddaman * Here we flip each byte of the keystr.
3631aa1f41fSAndy Fiddaman */
3641aa1f41fSAndy Fiddaman for (uint_t i = 0; i < RFBP_SECURITY_VNC_PASSWD_LEN; i++) {
3651aa1f41fSAndy Fiddaman keystr[i] = (keystr[i] & 0xf0) >> 4 | (keystr[i] & 0x0f) << 4;
3661aa1f41fSAndy Fiddaman keystr[i] = (keystr[i] & 0xcc) >> 2 | (keystr[i] & 0x33) << 2;
3671aa1f41fSAndy Fiddaman keystr[i] = (keystr[i] & 0xaa) >> 1 | (keystr[i] & 0x55) << 1;
3681aa1f41fSAndy Fiddaman }
3691aa1f41fSAndy Fiddaman
3701aa1f41fSAndy Fiddaman /* Initialize a 16-byte random challenge. */
3711aa1f41fSAndy Fiddaman arc4random_buf(challenge, sizeof (challenge));
37238864087SAndy Fiddaman
37338864087SAndy Fiddaman /* Send the challenge to the client. */
37438864087SAndy Fiddaman if (stream_write(c->rc_fd, challenge, RFBP_SECURITY_VNC_AUTH_LEN)
37538864087SAndy Fiddaman != RFBP_SECURITY_VNC_AUTH_LEN) {
37638864087SAndy Fiddaman rfb_printf(c, RFB_LOGERR,
37738864087SAndy Fiddaman "failed to send challenge to client");
37838864087SAndy Fiddaman return (false);
37938864087SAndy Fiddaman }
3801aa1f41fSAndy Fiddaman
3811aa1f41fSAndy Fiddaman /* Receive the 16-byte challenge response. */
3821aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, buf, RFBP_SECURITY_VNC_AUTH_LEN)
3831aa1f41fSAndy Fiddaman != RFBP_SECURITY_VNC_AUTH_LEN) {
38438864087SAndy Fiddaman rfb_send_client_status(c, 1, "Challenge response read failed");
3851aa1f41fSAndy Fiddaman return (false);
3861aa1f41fSAndy Fiddaman }
3871aa1f41fSAndy Fiddaman
3881aa1f41fSAndy Fiddaman memcpy(crypt_expected, challenge, RFBP_SECURITY_VNC_AUTH_LEN);
3891aa1f41fSAndy Fiddaman
3901aa1f41fSAndy Fiddaman /* Encrypt the Challenge with DES. */
3911aa1f41fSAndy Fiddaman DES_set_key_unchecked((const_DES_cblock *)keystr, &ks);
3921aa1f41fSAndy Fiddaman DES_ecb_encrypt((const_DES_cblock *)challenge,
3931aa1f41fSAndy Fiddaman (const_DES_cblock *)crypt_expected, &ks, DES_ENCRYPT);
3941aa1f41fSAndy Fiddaman DES_ecb_encrypt(
3951aa1f41fSAndy Fiddaman (const_DES_cblock *)(challenge + RFBP_SECURITY_VNC_PASSWD_LEN),
3961aa1f41fSAndy Fiddaman (const_DES_cblock *)(crypt_expected + RFBP_SECURITY_VNC_PASSWD_LEN),
3971aa1f41fSAndy Fiddaman &ks, DES_ENCRYPT);
3981aa1f41fSAndy Fiddaman
3991aa1f41fSAndy Fiddaman if (memcmp(crypt_expected, buf, RFBP_SECURITY_VNC_AUTH_LEN) != 0) {
4001aa1f41fSAndy Fiddaman rfb_send_client_status(c, 1, "Auth failed: Invalid password.");
4011aa1f41fSAndy Fiddaman return (false);
4021aa1f41fSAndy Fiddaman }
4031aa1f41fSAndy Fiddaman
4041aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "authentication succeeded");
4051aa1f41fSAndy Fiddaman rfb_send_client_status(c, 0, NULL);
4061aa1f41fSAndy Fiddaman #endif
4071aa1f41fSAndy Fiddaman
4081aa1f41fSAndy Fiddaman return (true);
4091aa1f41fSAndy Fiddaman }
4101aa1f41fSAndy Fiddaman
4111aa1f41fSAndy Fiddaman static bool
rfb_handshake_init_message(rfb_client_t * c)4121aa1f41fSAndy Fiddaman rfb_handshake_init_message(rfb_client_t *c)
4131aa1f41fSAndy Fiddaman {
4141aa1f41fSAndy Fiddaman struct bhyvegc_image *gci;
4151aa1f41fSAndy Fiddaman char buf[1];
4161aa1f41fSAndy Fiddaman char *name;
4171aa1f41fSAndy Fiddaman
4181aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "handshake server init");
4191aa1f41fSAndy Fiddaman
4201aa1f41fSAndy Fiddaman /* Read the client init message. */
4211aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, buf, 1) != 1) {
4221aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "client did not send init");
4231aa1f41fSAndy Fiddaman return (false);
4241aa1f41fSAndy Fiddaman }
4251aa1f41fSAndy Fiddaman
4261aa1f41fSAndy Fiddaman if (buf[0] == 0) {
4271aa1f41fSAndy Fiddaman rfb_client_t *oc;
4281aa1f41fSAndy Fiddaman
4291aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG,
4301aa1f41fSAndy Fiddaman "client requested exclusive access");
4311aa1f41fSAndy Fiddaman
4321aa1f41fSAndy Fiddaman pthread_mutex_lock(&c->rc_s->rs_clientlock);
4331aa1f41fSAndy Fiddaman c->rc_s->rs_exclusive = true;
4341aa1f41fSAndy Fiddaman /* Disconnect all other clients. */
4351aa1f41fSAndy Fiddaman for (oc = list_head(&c->rc_s->rs_clients); oc != NULL;
4361aa1f41fSAndy Fiddaman oc = list_next(&c->rc_s->rs_clients, oc)) {
4371aa1f41fSAndy Fiddaman if (oc != c)
4381aa1f41fSAndy Fiddaman oc->rc_closing = true;
4391aa1f41fSAndy Fiddaman }
4401aa1f41fSAndy Fiddaman pthread_mutex_unlock(&c->rc_s->rs_clientlock);
4411aa1f41fSAndy Fiddaman } else {
4421aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "client requested shared access");
4431aa1f41fSAndy Fiddaman
4441aa1f41fSAndy Fiddaman pthread_mutex_lock(&c->rc_s->rs_clientlock);
4451aa1f41fSAndy Fiddaman if (c->rc_s->rs_exclusive) {
4461aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN,
4471aa1f41fSAndy Fiddaman "deny due to existing exclusive session");
4481aa1f41fSAndy Fiddaman pthread_mutex_unlock(&c->rc_s->rs_clientlock);
4491aa1f41fSAndy Fiddaman return (false);
4501aa1f41fSAndy Fiddaman }
4511aa1f41fSAndy Fiddaman pthread_mutex_unlock(&c->rc_s->rs_clientlock);
4521aa1f41fSAndy Fiddaman }
4531aa1f41fSAndy Fiddaman
4541aa1f41fSAndy Fiddaman gci = console_get_image();
4551aa1f41fSAndy Fiddaman
4561aa1f41fSAndy Fiddaman c->rc_sinfo.rsi_width = htons(gci->width);
4571aa1f41fSAndy Fiddaman c->rc_sinfo.rsi_height = htons(gci->height);
4581aa1f41fSAndy Fiddaman c->rc_width = gci->width;
4591aa1f41fSAndy Fiddaman c->rc_height = gci->height;
4601aa1f41fSAndy Fiddaman
4611aa1f41fSAndy Fiddaman if (c->rc_s->rs_name != NULL)
4621aa1f41fSAndy Fiddaman name = (char *)c->rc_s->rs_name;
4631aa1f41fSAndy Fiddaman else
4641aa1f41fSAndy Fiddaman name = "bhyve";
4651aa1f41fSAndy Fiddaman
4661aa1f41fSAndy Fiddaman c->rc_sinfo.rsi_namelen = htonl(strlen(name));
4671aa1f41fSAndy Fiddaman (void) stream_write(c->rc_fd, &c->rc_sinfo, sizeof (c->rc_sinfo));
4681aa1f41fSAndy Fiddaman (void) stream_write(c->rc_fd, name, strlen(name));
4691aa1f41fSAndy Fiddaman
4701aa1f41fSAndy Fiddaman return (true);
4711aa1f41fSAndy Fiddaman }
4721aa1f41fSAndy Fiddaman
4731aa1f41fSAndy Fiddaman static bool
rfb_handshake(rfb_client_t * c)4741aa1f41fSAndy Fiddaman rfb_handshake(rfb_client_t *c)
4751aa1f41fSAndy Fiddaman {
4761aa1f41fSAndy Fiddaman if (!rfb_handshake_version(c))
4771aa1f41fSAndy Fiddaman return (false);
4781aa1f41fSAndy Fiddaman
4791aa1f41fSAndy Fiddaman if (!rfb_handshake_auth(c))
4801aa1f41fSAndy Fiddaman return (false);
4811aa1f41fSAndy Fiddaman
4821aa1f41fSAndy Fiddaman if (!rfb_handshake_init_message(c))
4831aa1f41fSAndy Fiddaman return (false);
4841aa1f41fSAndy Fiddaman
4851aa1f41fSAndy Fiddaman return (true);
4861aa1f41fSAndy Fiddaman }
4871aa1f41fSAndy Fiddaman
4881aa1f41fSAndy Fiddaman static void
rfb_print_pixfmt(rfb_client_t * c,rfb_pixfmt_t * px,rfb_loglevel_t level)4891aa1f41fSAndy Fiddaman rfb_print_pixfmt(rfb_client_t *c, rfb_pixfmt_t *px, rfb_loglevel_t level)
4901aa1f41fSAndy Fiddaman {
4911aa1f41fSAndy Fiddaman rfb_printf(c, level, "%20s: %u", "bpp", px->rp_bpp);
4921aa1f41fSAndy Fiddaman rfb_printf(c, level, "%20s: %u", "depth", px->rp_depth);
4931aa1f41fSAndy Fiddaman rfb_printf(c, level, "%20s: %u", "bigendian", px->rp_bigendian);
4941aa1f41fSAndy Fiddaman rfb_printf(c, level, "%20s: %u", "truecolour", px->rp_truecolour);
4951aa1f41fSAndy Fiddaman rfb_printf(c, level, "%20s: %u", "r_max", ntohs(px->rp_r_max));
4961aa1f41fSAndy Fiddaman rfb_printf(c, level, "%20s: %u", "g_max", ntohs(px->rp_g_max));
4971aa1f41fSAndy Fiddaman rfb_printf(c, level, "%20s: %u", "b_max", ntohs(px->rp_b_max));
4981aa1f41fSAndy Fiddaman rfb_printf(c, level, "%20s: %u", "r_shift", px->rp_r_shift);
4991aa1f41fSAndy Fiddaman rfb_printf(c, level, "%20s: %u", "g_shift", px->rp_g_shift);
5001aa1f41fSAndy Fiddaman rfb_printf(c, level, "%20s: %u", "b_shift", px->rp_b_shift);
5011aa1f41fSAndy Fiddaman }
5021aa1f41fSAndy Fiddaman
5031aa1f41fSAndy Fiddaman static bool
rfb_recv_set_pixel_format(rfb_client_t * c)5041aa1f41fSAndy Fiddaman rfb_recv_set_pixel_format(rfb_client_t *c)
5051aa1f41fSAndy Fiddaman {
5061aa1f41fSAndy Fiddaman rfb_cs_pixfmt_msg_t msg;
5071aa1f41fSAndy Fiddaman rfb_pixfmt_t *newpx = &msg.rp_pixfmt;
5081aa1f41fSAndy Fiddaman rfb_pixfmt_t *oldpx = &c->rc_sinfo.rsi_pixfmt;
5091aa1f41fSAndy Fiddaman rfb_pixfmt_t *spx = &c->rc_s->rs_pixfmt;
5101aa1f41fSAndy Fiddaman
5111aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "received pixel format");
5121aa1f41fSAndy Fiddaman
5131aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
5141aa1f41fSAndy Fiddaman return (false);
5151aa1f41fSAndy Fiddaman
5161aa1f41fSAndy Fiddaman /*
5171aa1f41fSAndy Fiddaman * The client has sent its desired pixel format. The protocol does not
5181aa1f41fSAndy Fiddaman * have a mechanism to reject this, we are supposed to just start using
5191aa1f41fSAndy Fiddaman * the requested format from the next update.
5201aa1f41fSAndy Fiddaman *
5211aa1f41fSAndy Fiddaman * At present, we can only support alternative rgb-shift values and
5221aa1f41fSAndy Fiddaman * will accept (and ignore) a new depth value.
5231aa1f41fSAndy Fiddaman */
5241aa1f41fSAndy Fiddaman
5251aa1f41fSAndy Fiddaman if (oldpx->rp_bpp != newpx->rp_bpp ||
5261aa1f41fSAndy Fiddaman oldpx->rp_bigendian != newpx->rp_bigendian ||
5271aa1f41fSAndy Fiddaman oldpx->rp_truecolour != newpx->rp_truecolour ||
5281aa1f41fSAndy Fiddaman oldpx->rp_r_max != newpx->rp_r_max ||
5291aa1f41fSAndy Fiddaman oldpx->rp_g_max != newpx->rp_g_max ||
5301aa1f41fSAndy Fiddaman oldpx->rp_b_max != newpx->rp_b_max) {
5311aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "unsupported pixfmt from client");
5321aa1f41fSAndy Fiddaman rfb_print_pixfmt(c, newpx, RFB_LOGWARN);
5331aa1f41fSAndy Fiddaman return (false);
5341aa1f41fSAndy Fiddaman }
5351aa1f41fSAndy Fiddaman
5361aa1f41fSAndy Fiddaman rfb_print_pixfmt(c, newpx, RFB_LOGDEBUG);
5371aa1f41fSAndy Fiddaman
5381aa1f41fSAndy Fiddaman /* Check if the new shifts match the server's native values. */
5391aa1f41fSAndy Fiddaman if (newpx->rp_r_shift != spx->rp_r_shift ||
5401aa1f41fSAndy Fiddaman newpx->rp_g_shift != spx->rp_g_shift ||
5411aa1f41fSAndy Fiddaman newpx->rp_b_shift != spx->rp_b_shift) {
5421aa1f41fSAndy Fiddaman c->rc_custom_pixfmt = true;
5431aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "Using custom pixfmt");
5441aa1f41fSAndy Fiddaman } else {
5451aa1f41fSAndy Fiddaman c->rc_custom_pixfmt = false;
5461aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "Using native pixfmt");
5471aa1f41fSAndy Fiddaman }
5481aa1f41fSAndy Fiddaman
5491aa1f41fSAndy Fiddaman c->rc_sinfo.rsi_pixfmt = msg.rp_pixfmt;
5501aa1f41fSAndy Fiddaman c->rc_crc_reset = true;
5511aa1f41fSAndy Fiddaman
5521aa1f41fSAndy Fiddaman return (true);
5531aa1f41fSAndy Fiddaman }
5541aa1f41fSAndy Fiddaman
5551aa1f41fSAndy Fiddaman static bool
rfb_recv_set_encodings(rfb_client_t * c)5561aa1f41fSAndy Fiddaman rfb_recv_set_encodings(rfb_client_t *c)
5571aa1f41fSAndy Fiddaman {
5581aa1f41fSAndy Fiddaman rfb_cs_encodings_msg_t msg;
5591aa1f41fSAndy Fiddaman
5601aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "received encodings");
5611aa1f41fSAndy Fiddaman
5621aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
5631aa1f41fSAndy Fiddaman return (false);
5641aa1f41fSAndy Fiddaman
5651aa1f41fSAndy Fiddaman msg.re_numencs = htons(msg.re_numencs);
5661aa1f41fSAndy Fiddaman
5671aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "%d values", msg.re_numencs);
5681aa1f41fSAndy Fiddaman
5691aa1f41fSAndy Fiddaman for (uint_t i = 0; i < msg.re_numencs; i++) {
5701aa1f41fSAndy Fiddaman uint32_t enc;
5711aa1f41fSAndy Fiddaman
5721aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, &enc, sizeof (enc)) != sizeof (enc))
5731aa1f41fSAndy Fiddaman return (false);
5741aa1f41fSAndy Fiddaman
5751aa1f41fSAndy Fiddaman enc = htonl(enc);
5761aa1f41fSAndy Fiddaman
5771aa1f41fSAndy Fiddaman switch (enc) {
5781aa1f41fSAndy Fiddaman case RFBP_ENCODING_RAW:
5791aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG,
5801aa1f41fSAndy Fiddaman "client supports raw encoding");
5811aa1f41fSAndy Fiddaman c->rc_encodings |= RFB_ENCODING_RAW;
5821aa1f41fSAndy Fiddaman break;
5831aa1f41fSAndy Fiddaman case RFBP_ENCODING_ZLIB:
5841aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG,
5851aa1f41fSAndy Fiddaman "client supports zlib encoding");
5861aa1f41fSAndy Fiddaman if (!(c->rc_encodings & RFB_ENCODING_ZLIB)) {
5871aa1f41fSAndy Fiddaman if (deflateInit(&c->rc_zstream, Z_BEST_SPEED)
5881aa1f41fSAndy Fiddaman != Z_OK) {
5891aa1f41fSAndy Fiddaman return (false);
5901aa1f41fSAndy Fiddaman }
5911aa1f41fSAndy Fiddaman c->rc_encodings |= RFB_ENCODING_ZLIB;
5921aa1f41fSAndy Fiddaman }
5931aa1f41fSAndy Fiddaman break;
5941aa1f41fSAndy Fiddaman case RFBP_ENCODING_RESIZE:
5951aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "client supports resize");
5961aa1f41fSAndy Fiddaman c->rc_encodings |= RFB_ENCODING_RESIZE;
5971aa1f41fSAndy Fiddaman break;
5981aa1f41fSAndy Fiddaman case RFBP_ENCODING_EXT_KEVENT:
5991aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG,
6001aa1f41fSAndy Fiddaman "client supports ext key event");
6011aa1f41fSAndy Fiddaman c->rc_encodings |= RFB_ENCODING_EXT_KEVENT;
6021aa1f41fSAndy Fiddaman break;
6031aa1f41fSAndy Fiddaman case RFBP_ENCODING_DESKTOP_NAME:
6041aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG,
6051aa1f41fSAndy Fiddaman "client supports desktop name");
6061aa1f41fSAndy Fiddaman c->rc_encodings |= RFB_ENCODING_DESKTOP_NAME;
6071aa1f41fSAndy Fiddaman break;
6081aa1f41fSAndy Fiddaman default:
6091aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG,
6101aa1f41fSAndy Fiddaman "client supports encoding %d", (int32_t)enc);
6111aa1f41fSAndy Fiddaman }
6121aa1f41fSAndy Fiddaman }
6131aa1f41fSAndy Fiddaman
6141aa1f41fSAndy Fiddaman return (true);
6151aa1f41fSAndy Fiddaman }
6161aa1f41fSAndy Fiddaman
6171aa1f41fSAndy Fiddaman static bool
rfb_recv_update(rfb_client_t * c)6181aa1f41fSAndy Fiddaman rfb_recv_update(rfb_client_t *c)
6191aa1f41fSAndy Fiddaman {
6201aa1f41fSAndy Fiddaman rfb_cs_update_msg_t msg;
6211aa1f41fSAndy Fiddaman
6221aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
6231aa1f41fSAndy Fiddaman return (false);
6241aa1f41fSAndy Fiddaman
6251aa1f41fSAndy Fiddaman if (!c->rc_keyevent_sent &&
6261aa1f41fSAndy Fiddaman (c->rc_encodings & RFB_ENCODING_EXT_KEVENT)) {
6271aa1f41fSAndy Fiddaman /*
6281aa1f41fSAndy Fiddaman * Take this opportunity to tell the client that we
6291aa1f41fSAndy Fiddaman * accept QEMU Extended Key Event Pseudo-encoding.
6301aa1f41fSAndy Fiddaman */
6311aa1f41fSAndy Fiddaman c->rc_keyevent_sent = true;
6321aa1f41fSAndy Fiddaman rfb_send_extended_keyevent_update_msg(c);
6331aa1f41fSAndy Fiddaman }
6341aa1f41fSAndy Fiddaman
6351aa1f41fSAndy Fiddaman c->rc_pending = true;
6361aa1f41fSAndy Fiddaman if (msg.rum_incremental == 0) {
6371aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG,
6381aa1f41fSAndy Fiddaman "client requested full screen update");
6391aa1f41fSAndy Fiddaman c->rc_send_fullscreen = true;
6401aa1f41fSAndy Fiddaman }
6411aa1f41fSAndy Fiddaman
6421aa1f41fSAndy Fiddaman return (true);
6431aa1f41fSAndy Fiddaman }
6441aa1f41fSAndy Fiddaman
6451aa1f41fSAndy Fiddaman static bool
rfb_recv_key_event(rfb_client_t * c)6461aa1f41fSAndy Fiddaman rfb_recv_key_event(rfb_client_t *c)
6471aa1f41fSAndy Fiddaman {
6481aa1f41fSAndy Fiddaman rfb_cs_key_event_msg_t msg;
6491aa1f41fSAndy Fiddaman
6501aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
6511aa1f41fSAndy Fiddaman return (false);
6521aa1f41fSAndy Fiddaman
6531aa1f41fSAndy Fiddaman msg.rke_sym = htonl(msg.rke_sym);
6541aa1f41fSAndy Fiddaman
6551aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "received key %s %x",
6561aa1f41fSAndy Fiddaman msg.rke_down == 0 ? "up" : "down", msg.rke_sym);
6571aa1f41fSAndy Fiddaman
6581aa1f41fSAndy Fiddaman console_key_event(msg.rke_down, msg.rke_sym, htonl(0));
6591aa1f41fSAndy Fiddaman c->rc_input_detected = true;
6601aa1f41fSAndy Fiddaman
6611aa1f41fSAndy Fiddaman return (true);
6621aa1f41fSAndy Fiddaman }
6631aa1f41fSAndy Fiddaman
6641aa1f41fSAndy Fiddaman static bool
rfb_recv_pointer_event(rfb_client_t * c)6651aa1f41fSAndy Fiddaman rfb_recv_pointer_event(rfb_client_t *c)
6661aa1f41fSAndy Fiddaman {
6671aa1f41fSAndy Fiddaman rfb_cs_pointer_event_msg_t msg;
6681aa1f41fSAndy Fiddaman
6691aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
6701aa1f41fSAndy Fiddaman return (false);
6711aa1f41fSAndy Fiddaman
6721aa1f41fSAndy Fiddaman msg.rpe_x = htons(msg.rpe_x);
6731aa1f41fSAndy Fiddaman msg.rpe_y = htons(msg.rpe_y);
6741aa1f41fSAndy Fiddaman
6751aa1f41fSAndy Fiddaman if (rfb_debug > 1) {
6761aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "received pointer event @ %dx%d",
6771aa1f41fSAndy Fiddaman msg.rpe_x, msg.rpe_y);
6781aa1f41fSAndy Fiddaman }
6791aa1f41fSAndy Fiddaman
6801aa1f41fSAndy Fiddaman console_ptr_event(msg.rpe_button, msg.rpe_x, msg.rpe_y);
6811aa1f41fSAndy Fiddaman c->rc_input_detected = true;
6821aa1f41fSAndy Fiddaman
6831aa1f41fSAndy Fiddaman return (true);
6841aa1f41fSAndy Fiddaman }
6851aa1f41fSAndy Fiddaman
6861aa1f41fSAndy Fiddaman static bool
rfb_recv_cut_text(rfb_client_t * c)6871aa1f41fSAndy Fiddaman rfb_recv_cut_text(rfb_client_t *c)
6881aa1f41fSAndy Fiddaman {
6891aa1f41fSAndy Fiddaman rfb_cs_cut_text_msg_t msg;
6901aa1f41fSAndy Fiddaman unsigned char buf[32];
6911aa1f41fSAndy Fiddaman
6921aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "received cut text event");
6931aa1f41fSAndy Fiddaman
6941aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
6951aa1f41fSAndy Fiddaman return (false);
6961aa1f41fSAndy Fiddaman
6971aa1f41fSAndy Fiddaman msg.rct_length = htonl(msg.rct_length);
6981aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "%u bytes in buffer", msg.rct_length);
6991aa1f41fSAndy Fiddaman /* Consume the buffer */
7001aa1f41fSAndy Fiddaman while (msg.rct_length > 0) {
7011aa1f41fSAndy Fiddaman ssize_t l;
7021aa1f41fSAndy Fiddaman
7031aa1f41fSAndy Fiddaman l = stream_read(c->rc_fd, buf,
7041aa1f41fSAndy Fiddaman MIN(sizeof (buf), msg.rct_length));
7051aa1f41fSAndy Fiddaman if (l <= 0)
7061aa1f41fSAndy Fiddaman return (false);
7071aa1f41fSAndy Fiddaman msg.rct_length -= l;
7081aa1f41fSAndy Fiddaman }
7091aa1f41fSAndy Fiddaman
7101aa1f41fSAndy Fiddaman return (true);
7111aa1f41fSAndy Fiddaman }
7121aa1f41fSAndy Fiddaman
7131aa1f41fSAndy Fiddaman static bool
rfb_recv_qemu(rfb_client_t * c)7141aa1f41fSAndy Fiddaman rfb_recv_qemu(rfb_client_t *c)
7151aa1f41fSAndy Fiddaman {
7161aa1f41fSAndy Fiddaman rfb_cs_qemu_msg_t msg;
7171aa1f41fSAndy Fiddaman
7181aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "received QEMU event");
7191aa1f41fSAndy Fiddaman
7201aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
7211aa1f41fSAndy Fiddaman return (false);
7221aa1f41fSAndy Fiddaman
7231aa1f41fSAndy Fiddaman switch (msg.rq_subtype) {
7241aa1f41fSAndy Fiddaman case RFBP_CS_QEMU_KEVENT: {
7251aa1f41fSAndy Fiddaman rfb_cs_qemu_extended_key_msg_t keymsg;
7261aa1f41fSAndy Fiddaman
7271aa1f41fSAndy Fiddaman if (stream_read(c->rc_fd, &keymsg, sizeof (keymsg)) !=
7281aa1f41fSAndy Fiddaman sizeof (keymsg)) {
7291aa1f41fSAndy Fiddaman return (false);
7301aa1f41fSAndy Fiddaman }
7311aa1f41fSAndy Fiddaman
7321aa1f41fSAndy Fiddaman keymsg.rqek_sym = htonl(keymsg.rqek_sym);
7331aa1f41fSAndy Fiddaman keymsg.rqek_code = htonl(keymsg.rqek_code);
7341aa1f41fSAndy Fiddaman
7351aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "QEMU key %s %x / %x",
7361aa1f41fSAndy Fiddaman keymsg.rqek_down == 0 ? "up" : "down",
7371aa1f41fSAndy Fiddaman keymsg.rqek_sym, keymsg.rqek_code);
7381aa1f41fSAndy Fiddaman
7391aa1f41fSAndy Fiddaman console_key_event((int)keymsg.rqek_down, keymsg.rqek_sym,
7401aa1f41fSAndy Fiddaman keymsg.rqek_code);
7411aa1f41fSAndy Fiddaman c->rc_input_detected = true;
7421aa1f41fSAndy Fiddaman break;
7431aa1f41fSAndy Fiddaman }
7441aa1f41fSAndy Fiddaman default:
7451aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "Unknown QEMU event subtype: %d\n",
7461aa1f41fSAndy Fiddaman msg.rq_subtype);
7471aa1f41fSAndy Fiddaman return (false);
7481aa1f41fSAndy Fiddaman }
7491aa1f41fSAndy Fiddaman
7501aa1f41fSAndy Fiddaman return (true);
7511aa1f41fSAndy Fiddaman }
7521aa1f41fSAndy Fiddaman
7531aa1f41fSAndy Fiddaman static bool
rfb_send_update_header(rfb_client_t * c,int numrects)7541aa1f41fSAndy Fiddaman rfb_send_update_header(rfb_client_t *c, int numrects)
7551aa1f41fSAndy Fiddaman {
7561aa1f41fSAndy Fiddaman rfb_server_update_msg_t msg;
7571aa1f41fSAndy Fiddaman
7581aa1f41fSAndy Fiddaman msg.rss_type = RFBP_SC_UPDATE;
7591aa1f41fSAndy Fiddaman msg.rss_pad = 0;
7601aa1f41fSAndy Fiddaman msg.rss_numrects = htons(numrects);
7611aa1f41fSAndy Fiddaman
7621aa1f41fSAndy Fiddaman return (stream_write(c->rc_fd, &msg, sizeof (msg)) == sizeof (msg));
7631aa1f41fSAndy Fiddaman }
7641aa1f41fSAndy Fiddaman
7651aa1f41fSAndy Fiddaman static void
rfb_send_resize_update_msg(rfb_client_t * c)7661aa1f41fSAndy Fiddaman rfb_send_resize_update_msg(rfb_client_t *c)
7671aa1f41fSAndy Fiddaman {
7681aa1f41fSAndy Fiddaman rfb_rect_hdr_t rect;
7691aa1f41fSAndy Fiddaman
7701aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "sending screen resize %dx%d",
7711aa1f41fSAndy Fiddaman c->rc_width, c->rc_height);
7721aa1f41fSAndy Fiddaman
7731aa1f41fSAndy Fiddaman (void) rfb_send_update_header(c, 1);
7741aa1f41fSAndy Fiddaman
7751aa1f41fSAndy Fiddaman rect.rr_x = htons(0);
7761aa1f41fSAndy Fiddaman rect.rr_y = htons(0);
7771aa1f41fSAndy Fiddaman rect.rr_width = htons(c->rc_width);
7781aa1f41fSAndy Fiddaman rect.rr_height = htons(c->rc_height);
7791aa1f41fSAndy Fiddaman rect.rr_encoding = htonl(RFBP_ENCODING_RESIZE);
7801aa1f41fSAndy Fiddaman
7811aa1f41fSAndy Fiddaman (void) stream_write(c->rc_fd, &rect, sizeof (rect));
7821aa1f41fSAndy Fiddaman }
7831aa1f41fSAndy Fiddaman
7841aa1f41fSAndy Fiddaman static void
rfb_send_extended_keyevent_update_msg(rfb_client_t * c)7851aa1f41fSAndy Fiddaman rfb_send_extended_keyevent_update_msg(rfb_client_t *c)
7861aa1f41fSAndy Fiddaman {
7871aa1f41fSAndy Fiddaman rfb_rect_hdr_t rect;
7881aa1f41fSAndy Fiddaman
7891aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "sending extended keyevent update message");
7901aa1f41fSAndy Fiddaman
7911aa1f41fSAndy Fiddaman (void) rfb_send_update_header(c, 1);
7921aa1f41fSAndy Fiddaman
7931aa1f41fSAndy Fiddaman rect.rr_x = htons(0);
7941aa1f41fSAndy Fiddaman rect.rr_y = htons(0);
7951aa1f41fSAndy Fiddaman rect.rr_width = htons(c->rc_width);
7961aa1f41fSAndy Fiddaman rect.rr_height = htons(c->rc_height);
7971aa1f41fSAndy Fiddaman rect.rr_encoding = htonl(RFBP_ENCODING_EXT_KEVENT);
7981aa1f41fSAndy Fiddaman
7991aa1f41fSAndy Fiddaman (void) stream_write(c->rc_fd, &rect, sizeof (rect));
8001aa1f41fSAndy Fiddaman }
8011aa1f41fSAndy Fiddaman
8021aa1f41fSAndy Fiddaman static void
translate_pixels(rfb_client_t * c,struct bhyvegc_image * gci,int x1,int y1,int x2,int y2)8031aa1f41fSAndy Fiddaman translate_pixels(rfb_client_t *c, struct bhyvegc_image *gci,
8041aa1f41fSAndy Fiddaman int x1, int y1, int x2, int y2)
8051aa1f41fSAndy Fiddaman {
8061aa1f41fSAndy Fiddaman rfb_pixfmt_t *px = &c->rc_sinfo.rsi_pixfmt;
8071aa1f41fSAndy Fiddaman rfb_pixfmt_t *spx = &c->rc_s->rs_pixfmt;
8081aa1f41fSAndy Fiddaman int w, h;
8091aa1f41fSAndy Fiddaman
8101aa1f41fSAndy Fiddaman w = gci->width;
8111aa1f41fSAndy Fiddaman h = gci->height;
8121aa1f41fSAndy Fiddaman VERIFY3S(gci->width, ==, c->rc_gci.width);
8131aa1f41fSAndy Fiddaman VERIFY3S(gci->height, ==, c->rc_gci.height);
8141aa1f41fSAndy Fiddaman
8151aa1f41fSAndy Fiddaman for (uint_t y = y1; y < h && y < y2; y++) {
8161aa1f41fSAndy Fiddaman for (uint_t x = x1; x < w && x < x2; x++) {
8171aa1f41fSAndy Fiddaman uint32_t p;
8181aa1f41fSAndy Fiddaman
8191aa1f41fSAndy Fiddaman p = gci->data[y * w + x];
8201aa1f41fSAndy Fiddaman c->rc_gci.data[y * w + x] =
8211aa1f41fSAndy Fiddaman 0xff000000 |
8221aa1f41fSAndy Fiddaman ((p >> spx->rp_r_shift) & 0xff) << px->rp_r_shift |
8231aa1f41fSAndy Fiddaman ((p >> spx->rp_g_shift) & 0xff) << px->rp_g_shift |
8241aa1f41fSAndy Fiddaman ((p >> spx->rp_b_shift) & 0xff) << px->rp_b_shift;
8251aa1f41fSAndy Fiddaman }
8261aa1f41fSAndy Fiddaman }
8271aa1f41fSAndy Fiddaman }
8281aa1f41fSAndy Fiddaman
8291aa1f41fSAndy Fiddaman static bool
rfb_send_rect(rfb_client_t * c,struct bhyvegc_image * gci,int x,int y,int w,int h)8301aa1f41fSAndy Fiddaman rfb_send_rect(rfb_client_t *c, struct bhyvegc_image *gci,
8314c87aefeSPatrick Mooney int x, int y, int w, int h)
832bf21cd93STycho Nightingale {
8331aa1f41fSAndy Fiddaman rfb_rect_hdr_t rect;
8344c87aefeSPatrick Mooney unsigned long zlen;
8354c87aefeSPatrick Mooney ssize_t nwrite, total;
8364c87aefeSPatrick Mooney int err;
8374c87aefeSPatrick Mooney uint32_t *p;
8384c87aefeSPatrick Mooney uint8_t *zbufp;
8394c87aefeSPatrick Mooney
8401aa1f41fSAndy Fiddaman if (rfb_debug > 1) {
8411aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "send rect %dx%d %dx%d",
8421aa1f41fSAndy Fiddaman x, y, w, h);
8431aa1f41fSAndy Fiddaman }
844bf21cd93STycho Nightingale
8451aa1f41fSAndy Fiddaman /* Rectangle header. */
8461aa1f41fSAndy Fiddaman rect.rr_x = htons(x);
8471aa1f41fSAndy Fiddaman rect.rr_y = htons(y);
8481aa1f41fSAndy Fiddaman rect.rr_width = htons(w);
8491aa1f41fSAndy Fiddaman rect.rr_height = htons(h);
8501aa1f41fSAndy Fiddaman
8511aa1f41fSAndy Fiddaman uint32_t *data = gci->data;
8521aa1f41fSAndy Fiddaman if (c->rc_custom_pixfmt) {
8531aa1f41fSAndy Fiddaman translate_pixels(c, gci, x, y, x + w, y + h);
8541aa1f41fSAndy Fiddaman data = c->rc_gci.data;
8551aa1f41fSAndy Fiddaman }
8564c87aefeSPatrick Mooney
8574c87aefeSPatrick Mooney h = y + h;
8584c87aefeSPatrick Mooney w *= sizeof (uint32_t);
8594c87aefeSPatrick Mooney
8601aa1f41fSAndy Fiddaman if (c->rc_encodings & RFB_ENCODING_ZLIB) {
8611aa1f41fSAndy Fiddaman zbufp = c->rc_zbuf;
8621aa1f41fSAndy Fiddaman c->rc_zstream.total_in = 0;
8631aa1f41fSAndy Fiddaman c->rc_zstream.total_out = 0;
8641aa1f41fSAndy Fiddaman for (p = &data[y * gci->width + x]; y < h; y++) {
8651aa1f41fSAndy Fiddaman c->rc_zstream.next_in = (Bytef *)p;
8661aa1f41fSAndy Fiddaman c->rc_zstream.avail_in = w;
8671aa1f41fSAndy Fiddaman c->rc_zstream.next_out = (Bytef *)zbufp;
8681aa1f41fSAndy Fiddaman c->rc_zstream.avail_out = RFB_ZLIB_BUFSZ + 16 -
8691aa1f41fSAndy Fiddaman c->rc_zstream.total_out;
8701aa1f41fSAndy Fiddaman c->rc_zstream.data_type = Z_BINARY;
8711aa1f41fSAndy Fiddaman
8721aa1f41fSAndy Fiddaman /* Compress with zlib. */
8731aa1f41fSAndy Fiddaman err = deflate(&c->rc_zstream, Z_SYNC_FLUSH);
8744c87aefeSPatrick Mooney if (err != Z_OK) {
8751aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN,
8761aa1f41fSAndy Fiddaman "zlib[rect] deflate err: %d", err);
8774c87aefeSPatrick Mooney goto doraw;
8784c87aefeSPatrick Mooney }
8791aa1f41fSAndy Fiddaman zbufp = c->rc_zbuf + c->rc_zstream.total_out;
8801aa1f41fSAndy Fiddaman p += gci->width;
8814c87aefeSPatrick Mooney }
8821aa1f41fSAndy Fiddaman rect.rr_encoding = htonl(RFBP_ENCODING_ZLIB);
8831aa1f41fSAndy Fiddaman nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
8844c87aefeSPatrick Mooney if (nwrite <= 0)
8851aa1f41fSAndy Fiddaman return (false);
8864c87aefeSPatrick Mooney
8871aa1f41fSAndy Fiddaman zlen = htonl(c->rc_zstream.total_out);
8881aa1f41fSAndy Fiddaman nwrite = stream_write(c->rc_fd, &zlen, sizeof (uint32_t));
8894c87aefeSPatrick Mooney if (nwrite <= 0)
8901aa1f41fSAndy Fiddaman return (false);
8911aa1f41fSAndy Fiddaman return (stream_write(c->rc_fd, c->rc_zbuf,
8921aa1f41fSAndy Fiddaman c->rc_zstream.total_out) == c->rc_zstream.total_out);
893bf21cd93STycho Nightingale }
894bf21cd93STycho Nightingale
8954c87aefeSPatrick Mooney doraw:
8964c87aefeSPatrick Mooney
8974c87aefeSPatrick Mooney total = 0;
8981aa1f41fSAndy Fiddaman zbufp = c->rc_zbuf;
8991aa1f41fSAndy Fiddaman for (p = &data[y * gci->width + x]; y < h; y++) {
9004c87aefeSPatrick Mooney memcpy(zbufp, p, w);
9014c87aefeSPatrick Mooney zbufp += w;
9024c87aefeSPatrick Mooney total += w;
9031aa1f41fSAndy Fiddaman p += gci->width;
9044c87aefeSPatrick Mooney }
9054c87aefeSPatrick Mooney
9061aa1f41fSAndy Fiddaman rect.rr_encoding = htonl(RFBP_ENCODING_RAW);
9071aa1f41fSAndy Fiddaman nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
9084c87aefeSPatrick Mooney if (nwrite <= 0)
9091aa1f41fSAndy Fiddaman return (false);
9104c87aefeSPatrick Mooney
9111aa1f41fSAndy Fiddaman return (stream_write(c->rc_fd, c->rc_zbuf, total) == total);
9124c87aefeSPatrick Mooney }
9134c87aefeSPatrick Mooney
9141aa1f41fSAndy Fiddaman
9151aa1f41fSAndy Fiddaman static bool
rfb_send_all(rfb_client_t * c,struct bhyvegc_image * gci)9161aa1f41fSAndy Fiddaman rfb_send_all(rfb_client_t *c, struct bhyvegc_image *gci)
917bf21cd93STycho Nightingale {
9181aa1f41fSAndy Fiddaman rfb_rect_hdr_t rect;
9194c87aefeSPatrick Mooney ssize_t nwrite;
9204c87aefeSPatrick Mooney unsigned long zlen;
9214c87aefeSPatrick Mooney int err;
922bf21cd93STycho Nightingale
9231aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "send entire screen");
9244c87aefeSPatrick Mooney
9251aa1f41fSAndy Fiddaman /* Just the one (big) rect. */
9261aa1f41fSAndy Fiddaman if (!rfb_send_update_header(c, 1))
9271aa1f41fSAndy Fiddaman return (false);
928bf21cd93STycho Nightingale
9291aa1f41fSAndy Fiddaman rect.rr_x = 0;
9301aa1f41fSAndy Fiddaman rect.rr_y = 0;
9311aa1f41fSAndy Fiddaman rect.rr_width = htons(gci->width);
9321aa1f41fSAndy Fiddaman rect.rr_height = htons(gci->height);
9331aa1f41fSAndy Fiddaman
9341aa1f41fSAndy Fiddaman uint32_t *data = gci->data;
9351aa1f41fSAndy Fiddaman if (c->rc_custom_pixfmt) {
9361aa1f41fSAndy Fiddaman translate_pixels(c, gci, 0, 0, gci->width, gci->height);
9371aa1f41fSAndy Fiddaman data = c->rc_gci.data;
9381aa1f41fSAndy Fiddaman }
9391aa1f41fSAndy Fiddaman
9401aa1f41fSAndy Fiddaman if (c->rc_encodings & RFB_ENCODING_ZLIB) {
9411aa1f41fSAndy Fiddaman c->rc_zstream.next_in = (Bytef *)data;
9421aa1f41fSAndy Fiddaman c->rc_zstream.avail_in = gci->width * gci->height *
9434c87aefeSPatrick Mooney sizeof (uint32_t);
9441aa1f41fSAndy Fiddaman c->rc_zstream.next_out = (Bytef *)c->rc_zbuf;
9451aa1f41fSAndy Fiddaman c->rc_zstream.avail_out = RFB_ZLIB_BUFSZ + 16;
9461aa1f41fSAndy Fiddaman c->rc_zstream.data_type = Z_BINARY;
947bf21cd93STycho Nightingale
9481aa1f41fSAndy Fiddaman c->rc_zstream.total_in = 0;
9491aa1f41fSAndy Fiddaman c->rc_zstream.total_out = 0;
9504c87aefeSPatrick Mooney
9511aa1f41fSAndy Fiddaman /* Compress with zlib. */
9521aa1f41fSAndy Fiddaman err = deflate(&c->rc_zstream, Z_SYNC_FLUSH);
9534c87aefeSPatrick Mooney if (err != Z_OK) {
9541aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "zlib deflate err: %d", err);
9554c87aefeSPatrick Mooney goto doraw;
9564c87aefeSPatrick Mooney }
9574c87aefeSPatrick Mooney
9581aa1f41fSAndy Fiddaman rect.rr_encoding = htonl(RFBP_ENCODING_ZLIB);
9591aa1f41fSAndy Fiddaman nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
9604c87aefeSPatrick Mooney if (nwrite <= 0)
9611aa1f41fSAndy Fiddaman return (false);
9624c87aefeSPatrick Mooney
9631aa1f41fSAndy Fiddaman zlen = htonl(c->rc_zstream.total_out);
9641aa1f41fSAndy Fiddaman nwrite = stream_write(c->rc_fd, &zlen, sizeof (uint32_t));
9654c87aefeSPatrick Mooney if (nwrite <= 0)
9661aa1f41fSAndy Fiddaman return (false);
9671aa1f41fSAndy Fiddaman return (stream_write(c->rc_fd, c->rc_zbuf,
9681aa1f41fSAndy Fiddaman c->rc_zstream.total_out) == c->rc_zstream.total_out);
9694c87aefeSPatrick Mooney }
9704c87aefeSPatrick Mooney
9714c87aefeSPatrick Mooney doraw:
9721aa1f41fSAndy Fiddaman rect.rr_encoding = htonl(RFBP_ENCODING_RAW);
9731aa1f41fSAndy Fiddaman nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
9744c87aefeSPatrick Mooney if (nwrite <= 0)
9751aa1f41fSAndy Fiddaman return (false);
9764c87aefeSPatrick Mooney
9771aa1f41fSAndy Fiddaman nwrite = gci->width * gci->height * sizeof (uint32_t);
9781aa1f41fSAndy Fiddaman return (stream_write(c->rc_fd, data, nwrite) == nwrite);
9794c87aefeSPatrick Mooney }
9804c87aefeSPatrick Mooney
9811aa1f41fSAndy Fiddaman static bool
rfb_send_screen(rfb_client_t * c)9821aa1f41fSAndy Fiddaman rfb_send_screen(rfb_client_t *c)
9834c87aefeSPatrick Mooney {
9841aa1f41fSAndy Fiddaman struct bhyvegc_image *gci;
9851aa1f41fSAndy Fiddaman bool retval = true;
9861aa1f41fSAndy Fiddaman bool sendall = false;
9874c87aefeSPatrick Mooney int xcells, ycells;
9881aa1f41fSAndy Fiddaman int rem_x, rem_y;
9891aa1f41fSAndy Fiddaman uint32_t *p, *ncrc, *ocrc;
9901aa1f41fSAndy Fiddaman uint_t changes, perc, x, y;
991dc8050e8SMarko Kiiskila
9921aa1f41fSAndy Fiddaman /* Updates require a preceding client update request. */
9931aa1f41fSAndy Fiddaman if (atomic_exchange(&c->rc_pending, false) == false)
9941aa1f41fSAndy Fiddaman return (true);
9954c87aefeSPatrick Mooney
9964c87aefeSPatrick Mooney console_refresh();
9971aa1f41fSAndy Fiddaman gci = console_get_image();
9984c87aefeSPatrick Mooney
9991aa1f41fSAndy Fiddaman /*
10001aa1f41fSAndy Fiddaman * It's helpful if the image size or data address does not change
10011aa1f41fSAndy Fiddaman * underneath us.
10021aa1f41fSAndy Fiddaman */
10031aa1f41fSAndy Fiddaman pthread_mutex_lock(&gci->mtx);
10044c87aefeSPatrick Mooney
10051aa1f41fSAndy Fiddaman /* Check for screen resolution changes. */
10061aa1f41fSAndy Fiddaman if (c->rc_width != gci->width ||
10071aa1f41fSAndy Fiddaman c->rc_height != gci->height) {
10081aa1f41fSAndy Fiddaman c->rc_width = gci->width;
10091aa1f41fSAndy Fiddaman c->rc_height = gci->height;
10101aa1f41fSAndy Fiddaman c->rc_crc_reset = true;
10111aa1f41fSAndy Fiddaman c->rc_send_fullscreen = true;
10121aa1f41fSAndy Fiddaman
10131aa1f41fSAndy Fiddaman /* If the client supports it, send a resize event. */
10141aa1f41fSAndy Fiddaman if (c->rc_encodings & RFB_ENCODING_RESIZE) {
10151aa1f41fSAndy Fiddaman rfb_send_resize_update_msg(c);
10161aa1f41fSAndy Fiddaman /*
10171aa1f41fSAndy Fiddaman * A resize message counts as an update in response to
10181aa1f41fSAndy Fiddaman * the client's preceding request so rc->pending does
10191aa1f41fSAndy Fiddaman * not need to be reset here.
10201aa1f41fSAndy Fiddaman */
1021dc8050e8SMarko Kiiskila goto done;
1022dc8050e8SMarko Kiiskila }
1023dc8050e8SMarko Kiiskila }
10244c87aefeSPatrick Mooney
10251aa1f41fSAndy Fiddaman /* Clear old CRC values. */
10261aa1f41fSAndy Fiddaman if (atomic_exchange(&c->rc_crc_reset, false))
10271aa1f41fSAndy Fiddaman memset(c->rc_crc, '\0', c->rc_cells * sizeof (uint32_t));
10281aa1f41fSAndy Fiddaman
10291aa1f41fSAndy Fiddaman if (c->rc_custom_pixfmt && (c->rc_gci.data == NULL ||
10301aa1f41fSAndy Fiddaman c->rc_gci.width != c->rc_width ||
10311aa1f41fSAndy Fiddaman c->rc_gci.height != c->rc_height)) {
10321aa1f41fSAndy Fiddaman c->rc_gci.data = reallocarray(c->rc_gci.data,
10331aa1f41fSAndy Fiddaman c->rc_width * c->rc_height, sizeof (uint32_t));
10341aa1f41fSAndy Fiddaman if (c->rc_gci.data == NULL) {
10351aa1f41fSAndy Fiddaman retval = false;
10364c87aefeSPatrick Mooney goto done;
10374c87aefeSPatrick Mooney }
10381aa1f41fSAndy Fiddaman c->rc_gci.width = c->rc_width;
10391aa1f41fSAndy Fiddaman c->rc_gci.height = c->rc_height;
10401aa1f41fSAndy Fiddaman } else if (!c->rc_custom_pixfmt && c->rc_gci.data != NULL) {
10411aa1f41fSAndy Fiddaman free(c->rc_gci.data);
10421aa1f41fSAndy Fiddaman c->rc_gci.data = NULL;
10431aa1f41fSAndy Fiddaman }
10441aa1f41fSAndy Fiddaman
10451aa1f41fSAndy Fiddaman sendall = atomic_exchange(&c->rc_send_fullscreen, false);
10464c87aefeSPatrick Mooney
10474c87aefeSPatrick Mooney /*
10481aa1f41fSAndy Fiddaman * Calculate a checksum for each 32x32 cell. Send all that have
10491aa1f41fSAndy Fiddaman * changed since the last scan.
10504c87aefeSPatrick Mooney */
10514c87aefeSPatrick Mooney
10521aa1f41fSAndy Fiddaman xcells = howmany(gci->width, RFB_PIX_PER_CELL);
10531aa1f41fSAndy Fiddaman ycells = howmany(gci->height, RFB_PIX_PER_CELL);
10541aa1f41fSAndy Fiddaman rem_x = gci->width & RFB_PIXCELL_MASK;
10551aa1f41fSAndy Fiddaman rem_y = gci->height & RFB_PIXCELL_MASK;
10561aa1f41fSAndy Fiddaman if (rem_y == 0)
10571aa1f41fSAndy Fiddaman rem_y = RFB_PIX_PER_CELL;
10584c87aefeSPatrick Mooney
10591aa1f41fSAndy Fiddaman p = gci->data;
10604c87aefeSPatrick Mooney
10611aa1f41fSAndy Fiddaman ncrc = c->rc_crc_tmp - xcells;
10621aa1f41fSAndy Fiddaman ocrc = c->rc_crc - xcells;
10634c87aefeSPatrick Mooney changes = 0;
10641aa1f41fSAndy Fiddaman memset(c->rc_crc_tmp, '\0', sizeof (uint32_t) * xcells * ycells);
10651aa1f41fSAndy Fiddaman for (y = 0; y < gci->height; y++) {
10661aa1f41fSAndy Fiddaman if ((y & RFB_PIXCELL_MASK) == 0) {
10671aa1f41fSAndy Fiddaman ncrc += xcells;
10681aa1f41fSAndy Fiddaman ocrc += xcells;
10694c87aefeSPatrick Mooney }
10704c87aefeSPatrick Mooney
10714c87aefeSPatrick Mooney for (x = 0; x < xcells; x++) {
10721aa1f41fSAndy Fiddaman uint_t cellwidth;
10731aa1f41fSAndy Fiddaman
10741aa1f41fSAndy Fiddaman if (x == xcells - 1 && rem_x > 0)
10754c87aefeSPatrick Mooney cellwidth = rem_x;
10764c87aefeSPatrick Mooney else
10771aa1f41fSAndy Fiddaman cellwidth = RFB_PIX_PER_CELL;
10784c87aefeSPatrick Mooney
10791aa1f41fSAndy Fiddaman if (rfb_sse42) {
10801aa1f41fSAndy Fiddaman ncrc[x] = fast_crc32(p,
10811aa1f41fSAndy Fiddaman cellwidth * sizeof (uint32_t), ncrc[x]);
10821aa1f41fSAndy Fiddaman } else {
10831aa1f41fSAndy Fiddaman ncrc[x] = (uint32_t)crc32(ncrc[x],
10841aa1f41fSAndy Fiddaman (Bytef *)p, cellwidth * sizeof (uint32_t));
10851aa1f41fSAndy Fiddaman }
10864c87aefeSPatrick Mooney
10874c87aefeSPatrick Mooney p += cellwidth;
10884c87aefeSPatrick Mooney
10891aa1f41fSAndy Fiddaman /* check for crc delta if last row in cell. */
10901aa1f41fSAndy Fiddaman if ((y & RFB_PIXCELL_MASK) == RFB_PIXCELL_MASK ||
10911aa1f41fSAndy Fiddaman y == gci->height - 1) {
10921aa1f41fSAndy Fiddaman if (ocrc[x] != ncrc[x]) {
10931aa1f41fSAndy Fiddaman ocrc[x] = ncrc[x];
10941aa1f41fSAndy Fiddaman ncrc[x] = 1;
10954c87aefeSPatrick Mooney changes++;
10964c87aefeSPatrick Mooney } else {
10971aa1f41fSAndy Fiddaman ncrc[x] = 0;
10984c87aefeSPatrick Mooney }
10994c87aefeSPatrick Mooney }
11004c87aefeSPatrick Mooney }
11014c87aefeSPatrick Mooney }
11024c87aefeSPatrick Mooney
11031aa1f41fSAndy Fiddaman perc = (changes * 100) / (xcells * ycells);
11041aa1f41fSAndy Fiddaman if (rfb_debug > 1 && changes > 0) {
11051aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG,
11061aa1f41fSAndy Fiddaman "scanned and found %u changed cell(s) - %u%%",
11071aa1f41fSAndy Fiddaman changes, perc);
11081aa1f41fSAndy Fiddaman }
11091aa1f41fSAndy Fiddaman
1110dc8050e8SMarko Kiiskila /*
11111aa1f41fSAndy Fiddaman * If there are no changes, don't send an update. Restore the pending
11121aa1f41fSAndy Fiddaman * flag since we still owe the client an update.
1113dc8050e8SMarko Kiiskila */
11141aa1f41fSAndy Fiddaman if (!sendall && !changes) {
11151aa1f41fSAndy Fiddaman c->rc_pending = true;
1116dc8050e8SMarko Kiiskila goto done;
1117dc8050e8SMarko Kiiskila }
1118dc8050e8SMarko Kiiskila
11191aa1f41fSAndy Fiddaman /* If there are a lot of changes, send the whole screen. */
11201aa1f41fSAndy Fiddaman if (perc >= RFB_SENDALL_THRESH)
11211aa1f41fSAndy Fiddaman sendall = true;
11221aa1f41fSAndy Fiddaman
11231aa1f41fSAndy Fiddaman if (sendall) {
11241aa1f41fSAndy Fiddaman retval = rfb_send_all(c, gci);
11254c87aefeSPatrick Mooney goto done;
11264c87aefeSPatrick Mooney }
11274c87aefeSPatrick Mooney
11281aa1f41fSAndy Fiddaman if (!rfb_send_update_header(c, changes)) {
11291aa1f41fSAndy Fiddaman retval = false;
11301aa1f41fSAndy Fiddaman goto done;
11311aa1f41fSAndy Fiddaman }
1132dc8050e8SMarko Kiiskila
11331aa1f41fSAndy Fiddaman /* Send the changed cells as separate rects. */
11341aa1f41fSAndy Fiddaman ncrc = c->rc_crc_tmp;
11351aa1f41fSAndy Fiddaman for (y = 0; y < gci->height; y += RFB_PIX_PER_CELL) {
11361aa1f41fSAndy Fiddaman /* Previous cell's row. */
11371aa1f41fSAndy Fiddaman int celly = (y >> RFB_PIXCELL_SHIFT);
11384c87aefeSPatrick Mooney
11391aa1f41fSAndy Fiddaman /* Delta check crc to previous set. */
11404c87aefeSPatrick Mooney for (x = 0; x < xcells; x++) {
11411aa1f41fSAndy Fiddaman uint_t cellwidth;
11421aa1f41fSAndy Fiddaman
11431aa1f41fSAndy Fiddaman if (*ncrc++ == 0)
11444c87aefeSPatrick Mooney continue;
11454c87aefeSPatrick Mooney
11461aa1f41fSAndy Fiddaman if (x == xcells - 1 && rem_x > 0)
11474c87aefeSPatrick Mooney cellwidth = rem_x;
11484c87aefeSPatrick Mooney else
11491aa1f41fSAndy Fiddaman cellwidth = RFB_PIX_PER_CELL;
11501aa1f41fSAndy Fiddaman
11511aa1f41fSAndy Fiddaman if (!rfb_send_rect(c, gci,
11521aa1f41fSAndy Fiddaman x * RFB_PIX_PER_CELL, celly * RFB_PIX_PER_CELL,
11531aa1f41fSAndy Fiddaman cellwidth, y + RFB_PIX_PER_CELL >= gci->height ?
11541aa1f41fSAndy Fiddaman rem_y : RFB_PIX_PER_CELL)) {
11551aa1f41fSAndy Fiddaman retval = false;
11564c87aefeSPatrick Mooney goto done;
11574c87aefeSPatrick Mooney }
11584c87aefeSPatrick Mooney }
11594c87aefeSPatrick Mooney }
11604c87aefeSPatrick Mooney
11614c87aefeSPatrick Mooney done:
11621aa1f41fSAndy Fiddaman pthread_mutex_unlock(&gci->mtx);
11634c87aefeSPatrick Mooney
11644c87aefeSPatrick Mooney return (retval);
11654c87aefeSPatrick Mooney }
11664c87aefeSPatrick Mooney
11671aa1f41fSAndy Fiddaman static void *
rfb_client_rx_thread(void * arg)11681aa1f41fSAndy Fiddaman rfb_client_rx_thread(void *arg)
11694c87aefeSPatrick Mooney {
11701aa1f41fSAndy Fiddaman rfb_client_t *c = arg;
11711aa1f41fSAndy Fiddaman unsigned char cmd;
11721aa1f41fSAndy Fiddaman bool ret = true;
11734c87aefeSPatrick Mooney
11741aa1f41fSAndy Fiddaman while (ret && !c->rc_closing && (read(c->rc_fd, &cmd, 1) == 1)) {
11751aa1f41fSAndy Fiddaman switch (cmd) {
11761aa1f41fSAndy Fiddaman case RFBP_CS_SET_PIXEL_FORMAT:
11771aa1f41fSAndy Fiddaman ret = rfb_recv_set_pixel_format(c);
11781aa1f41fSAndy Fiddaman break;
11791aa1f41fSAndy Fiddaman case RFBP_CS_SET_ENCODINGS:
11801aa1f41fSAndy Fiddaman ret = rfb_recv_set_encodings(c);
11811aa1f41fSAndy Fiddaman break;
11821aa1f41fSAndy Fiddaman case RFBP_CS_UPDATE_REQUEST:
11831aa1f41fSAndy Fiddaman ret = rfb_recv_update(c);
11841aa1f41fSAndy Fiddaman break;
11851aa1f41fSAndy Fiddaman case RFBP_CS_KEY_EVENT:
11861aa1f41fSAndy Fiddaman ret = rfb_recv_key_event(c);
11871aa1f41fSAndy Fiddaman break;
11881aa1f41fSAndy Fiddaman case RFBP_CS_POINTER_EVENT:
11891aa1f41fSAndy Fiddaman ret = rfb_recv_pointer_event(c);
11901aa1f41fSAndy Fiddaman break;
11911aa1f41fSAndy Fiddaman case RFBP_CS_CUT_TEXT:
11921aa1f41fSAndy Fiddaman ret = rfb_recv_cut_text(c);
11931aa1f41fSAndy Fiddaman break;
11941aa1f41fSAndy Fiddaman case RFBP_CS_QEMU:
11951aa1f41fSAndy Fiddaman ret = rfb_recv_qemu(c);
11961aa1f41fSAndy Fiddaman break;
11971aa1f41fSAndy Fiddaman default:
11981aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "unknown cs code %d",
11991aa1f41fSAndy Fiddaman cmd & 0xff);
12001aa1f41fSAndy Fiddaman ret = false;
1201b0de25cbSAndy Fiddaman }
1202b0de25cbSAndy Fiddaman }
1203b0de25cbSAndy Fiddaman
12041aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "client rx thread exiting");
12051aa1f41fSAndy Fiddaman c->rc_closing = true;
12064c87aefeSPatrick Mooney
12071aa1f41fSAndy Fiddaman return (NULL);
12084c87aefeSPatrick Mooney }
12094c87aefeSPatrick Mooney
12104c87aefeSPatrick Mooney static void *
rfb_client_tx_thread(void * arg)12111aa1f41fSAndy Fiddaman rfb_client_tx_thread(void *arg)
12124c87aefeSPatrick Mooney {
12131aa1f41fSAndy Fiddaman rfb_client_t *c = arg;
12141aa1f41fSAndy Fiddaman rfb_server_t *s = c->rc_s;
12151aa1f41fSAndy Fiddaman char tname[MAXCOMLEN + 1];
12161aa1f41fSAndy Fiddaman uint_t counter = 0;
12171aa1f41fSAndy Fiddaman hrtime_t tprev;
12181aa1f41fSAndy Fiddaman void *status;
12194c87aefeSPatrick Mooney int err;
12204c87aefeSPatrick Mooney
12211aa1f41fSAndy Fiddaman (void) snprintf(tname, sizeof (tname), "rfb%u tx", c->rc_instance);
12221aa1f41fSAndy Fiddaman (void) pthread_set_name_np(c->rc_tx_tid, tname);
12234c87aefeSPatrick Mooney
12241aa1f41fSAndy Fiddaman c->rc_sinfo.rsi_pixfmt = c->rc_s->rs_pixfmt;
12251aa1f41fSAndy Fiddaman c->rc_encodings = RFB_ENCODING_RAW;
12261aa1f41fSAndy Fiddaman
122738864087SAndy Fiddaman if (!rfb_handshake(c)) {
122838864087SAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "handshake failure");
12291aa1f41fSAndy Fiddaman goto out;
123038864087SAndy Fiddaman }
12311aa1f41fSAndy Fiddaman
12321aa1f41fSAndy Fiddaman c->rc_cells = howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, RFB_PIX_PER_CELL);
12331aa1f41fSAndy Fiddaman if ((c->rc_crc = calloc(c->rc_cells, sizeof (uint32_t))) == NULL ||
12341aa1f41fSAndy Fiddaman (c->rc_crc_tmp = calloc(c->rc_cells, sizeof (uint32_t))) == NULL) {
12351aa1f41fSAndy Fiddaman perror("calloc crc");
12361aa1f41fSAndy Fiddaman goto out;
12371aa1f41fSAndy Fiddaman }
12381aa1f41fSAndy Fiddaman
12391aa1f41fSAndy Fiddaman err = pthread_create(&c->rc_rx_tid, NULL, rfb_client_rx_thread, c);
12401aa1f41fSAndy Fiddaman if (err != 0) {
12411aa1f41fSAndy Fiddaman perror("pthread_create client rx thread");
12421aa1f41fSAndy Fiddaman goto out;
12431aa1f41fSAndy Fiddaman }
12441aa1f41fSAndy Fiddaman
12451aa1f41fSAndy Fiddaman (void) snprintf(tname, sizeof (tname), "rfb%u rx", c->rc_instance);
12461aa1f41fSAndy Fiddaman (void) pthread_set_name_np(c->rc_rx_tid, tname);
12471aa1f41fSAndy Fiddaman
12481aa1f41fSAndy Fiddaman tprev = gethrtime();
12491aa1f41fSAndy Fiddaman
12501aa1f41fSAndy Fiddaman while (!c->rc_closing) {
12511aa1f41fSAndy Fiddaman struct timeval tv;
12521aa1f41fSAndy Fiddaman hrtime_t tnow;
12531aa1f41fSAndy Fiddaman int64_t tdiff;
12541aa1f41fSAndy Fiddaman fd_set rfds;
12551aa1f41fSAndy Fiddaman int err;
12561aa1f41fSAndy Fiddaman
12574c87aefeSPatrick Mooney FD_ZERO(&rfds);
12581aa1f41fSAndy Fiddaman FD_SET(c->rc_fd, &rfds);
12594c87aefeSPatrick Mooney tv.tv_sec = 0;
12601aa1f41fSAndy Fiddaman tv.tv_usec = RFB_SEL_DELAY_US;
12614c87aefeSPatrick Mooney
12621aa1f41fSAndy Fiddaman err = select(c->rc_fd + 1, &rfds, NULL, NULL, &tv);
12634c87aefeSPatrick Mooney if (err < 0)
12641aa1f41fSAndy Fiddaman break;
12654c87aefeSPatrick Mooney
12661aa1f41fSAndy Fiddaman /* Determine if its time to push the screen; ~24hz. */
12671aa1f41fSAndy Fiddaman tnow = gethrtime();
12681aa1f41fSAndy Fiddaman tdiff = NSEC2USEC(tnow - tprev);
12691aa1f41fSAndy Fiddaman if (tdiff >= RFB_SCREEN_POLL_DELAY) {
1270dc8050e8SMarko Kiiskila bool input;
12711aa1f41fSAndy Fiddaman
12721aa1f41fSAndy Fiddaman tprev = tnow;
12731aa1f41fSAndy Fiddaman
12741aa1f41fSAndy Fiddaman input = atomic_exchange(&c->rc_input_detected, false);
1275dc8050e8SMarko Kiiskila /*
12761aa1f41fSAndy Fiddaman * Refresh the screen on every second trip through the
12771aa1f41fSAndy Fiddaman * loop, or if keyboard/mouse input has been detected.
1278dc8050e8SMarko Kiiskila */
12791aa1f41fSAndy Fiddaman if ((++counter & 1) != 0 || input) {
12801aa1f41fSAndy Fiddaman if (!rfb_send_screen(c))
12811aa1f41fSAndy Fiddaman break;
1282dc8050e8SMarko Kiiskila }
12834c87aefeSPatrick Mooney } else {
12841aa1f41fSAndy Fiddaman (void) usleep(RFB_SCREEN_POLL_DELAY - tdiff);
12854c87aefeSPatrick Mooney }
12864c87aefeSPatrick Mooney }
12874c87aefeSPatrick Mooney
12881aa1f41fSAndy Fiddaman out:
12891aa1f41fSAndy Fiddaman
129038864087SAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "disconnected");
129138864087SAndy Fiddaman
12921aa1f41fSAndy Fiddaman (void) pthread_join(c->rc_rx_tid, &status);
12931aa1f41fSAndy Fiddaman pthread_mutex_lock(&s->rs_clientlock);
12941aa1f41fSAndy Fiddaman s->rs_clientcount--;
12951aa1f41fSAndy Fiddaman list_remove(&s->rs_clients, c);
12961aa1f41fSAndy Fiddaman if (s->rs_exclusive && s->rs_clientcount == 0)
12971aa1f41fSAndy Fiddaman s->rs_exclusive = false;
12981aa1f41fSAndy Fiddaman id_free(rfb_idspace, c->rc_instance);
12991aa1f41fSAndy Fiddaman pthread_mutex_unlock(&s->rs_clientlock);
13001aa1f41fSAndy Fiddaman
13011aa1f41fSAndy Fiddaman rfb_free_client(c);
13024c87aefeSPatrick Mooney return (NULL);
1303bf21cd93STycho Nightingale }
1304bf21cd93STycho Nightingale
13051aa1f41fSAndy Fiddaman static void
rfb_accept(int sfd,enum ev_type event,void * arg)13061aa1f41fSAndy Fiddaman rfb_accept(int sfd, enum ev_type event, void *arg)
1307bf21cd93STycho Nightingale {
13081aa1f41fSAndy Fiddaman rfb_server_t *s = arg;
13091aa1f41fSAndy Fiddaman rfb_client_t *c = NULL;
13101aa1f41fSAndy Fiddaman struct sockaddr_storage cliaddr;
13111aa1f41fSAndy Fiddaman socklen_t len;
13121aa1f41fSAndy Fiddaman char host[NI_MAXHOST], port[NI_MAXSERV];
13131aa1f41fSAndy Fiddaman int cfd, err;
13141aa1f41fSAndy Fiddaman uint_t cc;
13154c87aefeSPatrick Mooney
13161aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGDEBUG, "incoming connection");
13174c87aefeSPatrick Mooney
13181aa1f41fSAndy Fiddaman len = sizeof (cliaddr);
13191aa1f41fSAndy Fiddaman cfd = accept(sfd, (struct sockaddr *)&cliaddr, &len);
13201aa1f41fSAndy Fiddaman if (cfd == -1) {
13211aa1f41fSAndy Fiddaman perror("client accept");
13221aa1f41fSAndy Fiddaman return;
1323dc8050e8SMarko Kiiskila }
1324bf21cd93STycho Nightingale
13251aa1f41fSAndy Fiddaman *host = *port = '\0';
13261aa1f41fSAndy Fiddaman if (cliaddr.ss_family == AF_UNIX) {
13271aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGDEBUG, "connection on UNIX socket");
13281aa1f41fSAndy Fiddaman (void) strlcpy(host, "<UNIX>", sizeof (host));
1329dc8050e8SMarko Kiiskila } else {
13301aa1f41fSAndy Fiddaman err = getnameinfo((struct sockaddr *)&cliaddr, len,
13311aa1f41fSAndy Fiddaman host, sizeof (host), port, sizeof (port),
13321aa1f41fSAndy Fiddaman NI_NUMERICHOST | NI_NUMERICSERV);
13331aa1f41fSAndy Fiddaman if (err != 0) {
13341aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGERR, "getnameinfo: %s",
13351aa1f41fSAndy Fiddaman gai_strerror(err));
13361aa1f41fSAndy Fiddaman *host = *port = '\0';
1337dc8050e8SMarko Kiiskila } else {
13381aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGDEBUG, "connection from %s:%s",
13391aa1f41fSAndy Fiddaman host, port);
1340bf21cd93STycho Nightingale }
1341bf21cd93STycho Nightingale }
13421aa1f41fSAndy Fiddaman
13431aa1f41fSAndy Fiddaman pthread_mutex_lock(&s->rs_clientlock);
13441aa1f41fSAndy Fiddaman cc = s->rs_clientcount;
13451aa1f41fSAndy Fiddaman pthread_mutex_unlock(&s->rs_clientlock);
13461aa1f41fSAndy Fiddaman if (cc >= RFB_MAX_CLIENTS) {
13471aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGERR,
13481aa1f41fSAndy Fiddaman "too many clients, closing connection.");
13491aa1f41fSAndy Fiddaman goto fail;
1350bf21cd93STycho Nightingale }
1351bf21cd93STycho Nightingale
13521aa1f41fSAndy Fiddaman if ((c = calloc(1, sizeof (rfb_client_t))) == NULL) {
13531aa1f41fSAndy Fiddaman perror("calloc client");
13541aa1f41fSAndy Fiddaman goto fail;
1355bf21cd93STycho Nightingale }
1356bf21cd93STycho Nightingale
13571aa1f41fSAndy Fiddaman c->rc_fd = cfd;
13581aa1f41fSAndy Fiddaman c->rc_s = s;
13591aa1f41fSAndy Fiddaman c->rc_zbuf = malloc(RFB_ZLIB_BUFSZ + 16);
13601aa1f41fSAndy Fiddaman if (c->rc_zbuf == NULL)
13611aa1f41fSAndy Fiddaman goto fail;
1362b0de25cbSAndy Fiddaman
13631aa1f41fSAndy Fiddaman pthread_mutex_lock(&s->rs_clientlock);
13644c87aefeSPatrick Mooney
13651aa1f41fSAndy Fiddaman err = pthread_create(&c->rc_tx_tid, NULL, rfb_client_tx_thread, c);
13661aa1f41fSAndy Fiddaman if (err != 0) {
13671aa1f41fSAndy Fiddaman perror("pthread_create client tx thread");
13681aa1f41fSAndy Fiddaman pthread_mutex_unlock(&s->rs_clientlock);
13691aa1f41fSAndy Fiddaman goto fail;
1370bf21cd93STycho Nightingale }
1371bf21cd93STycho Nightingale
13721aa1f41fSAndy Fiddaman s->rs_clientcount++;
13731aa1f41fSAndy Fiddaman list_insert_tail(&s->rs_clients, c);
13741aa1f41fSAndy Fiddaman c->rc_instance = id_allocff(rfb_idspace);
13751aa1f41fSAndy Fiddaman pthread_mutex_unlock(&s->rs_clientlock);
1376bf21cd93STycho Nightingale
13771aa1f41fSAndy Fiddaman (void) pthread_detach(c->rc_tx_tid);
13784c87aefeSPatrick Mooney
13791aa1f41fSAndy Fiddaman rfb_printf(c, RFB_LOGWARN, "connection from %s", host);
13804c87aefeSPatrick Mooney
13811aa1f41fSAndy Fiddaman return;
13824c87aefeSPatrick Mooney
13831aa1f41fSAndy Fiddaman fail:
13841aa1f41fSAndy Fiddaman (void) close(cfd);
13851aa1f41fSAndy Fiddaman free(c);
13864c87aefeSPatrick Mooney }
13874c87aefeSPatrick Mooney
13884c87aefeSPatrick Mooney int
rfb_init(char * hostname,int port,int wait,const char * password,const char * name)13891aa1f41fSAndy Fiddaman rfb_init(char *hostname, int port, int wait, const char *password,
13901aa1f41fSAndy Fiddaman const char *name)
13914c87aefeSPatrick Mooney {
13921aa1f41fSAndy Fiddaman rfb_server_t *s;
13934c87aefeSPatrick Mooney #ifndef WITHOUT_CAPSICUM
13944c87aefeSPatrick Mooney cap_rights_t rights;
13954c87aefeSPatrick Mooney #endif
1396bf21cd93STycho Nightingale
13971aa1f41fSAndy Fiddaman (void) pthread_once(&rfb_once, rfb_init_once);
1398bf21cd93STycho Nightingale
13991aa1f41fSAndy Fiddaman if (rfb_idspace == NULL) {
14001aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGERR,
14011aa1f41fSAndy Fiddaman "rfb_idspace could not be allocated");
140284659b24SMichael Zeller return (-1);
1403bf21cd93STycho Nightingale }
14044c87aefeSPatrick Mooney
14051aa1f41fSAndy Fiddaman if ((s = calloc(1, sizeof (rfb_server_t))) == NULL) {
14061aa1f41fSAndy Fiddaman perror("calloc");
14071aa1f41fSAndy Fiddaman return (-1);
14081aa1f41fSAndy Fiddaman }
14091aa1f41fSAndy Fiddaman s->rs_fd = -1;
14101aa1f41fSAndy Fiddaman s->rs_name = name;
14111aa1f41fSAndy Fiddaman
14121aa1f41fSAndy Fiddaman if (password != NULL && strlen(password) > 0)
14131aa1f41fSAndy Fiddaman s->rs_password = password;
14141aa1f41fSAndy Fiddaman
14151aa1f41fSAndy Fiddaman if (pthread_mutex_init(&s->rs_clientlock, NULL) != 0) {
14161aa1f41fSAndy Fiddaman perror("pthread_mutex_init");
14171aa1f41fSAndy Fiddaman free(s);
14181aa1f41fSAndy Fiddaman return (-1);
14191aa1f41fSAndy Fiddaman }
14201aa1f41fSAndy Fiddaman
14211aa1f41fSAndy Fiddaman list_create(&s->rs_clients, sizeof (rfb_client_t),
14221aa1f41fSAndy Fiddaman offsetof(rfb_client_t, rc_node));
14231aa1f41fSAndy Fiddaman
14241aa1f41fSAndy Fiddaman /* Server pixel format. */
14251aa1f41fSAndy Fiddaman s->rs_pixfmt.rp_bpp = RFB_PIX_BPP;
14261aa1f41fSAndy Fiddaman s->rs_pixfmt.rp_depth = RFB_PIX_DEPTH;
14271aa1f41fSAndy Fiddaman s->rs_pixfmt.rp_bigendian = 0;
14281aa1f41fSAndy Fiddaman s->rs_pixfmt.rp_truecolour = 1;
14291aa1f41fSAndy Fiddaman s->rs_pixfmt.rp_r_max = htons(RFB_PIX_RMAX);
14301aa1f41fSAndy Fiddaman s->rs_pixfmt.rp_g_max = htons(RFB_PIX_GMAX);
14311aa1f41fSAndy Fiddaman s->rs_pixfmt.rp_b_max = htons(RFB_PIX_BMAX);
14321aa1f41fSAndy Fiddaman s->rs_pixfmt.rp_r_shift = RFB_PIX_RSHIFT;
14331aa1f41fSAndy Fiddaman s->rs_pixfmt.rp_g_shift = RFB_PIX_GSHIFT;
14341aa1f41fSAndy Fiddaman s->rs_pixfmt.rp_b_shift = RFB_PIX_BSHIFT;
14351aa1f41fSAndy Fiddaman
14361aa1f41fSAndy Fiddaman /* UNIX socket. */
14371aa1f41fSAndy Fiddaman if (port == -1 && hostname != NULL && *hostname == '/') {
14384c87aefeSPatrick Mooney struct sockaddr_un sock;
14394c87aefeSPatrick Mooney
14401aa1f41fSAndy Fiddaman s->rs_fd = socket(PF_UNIX, SOCK_STREAM, 0);
14411aa1f41fSAndy Fiddaman if (s->rs_fd < 0) {
14424c87aefeSPatrick Mooney perror("socket");
14434c87aefeSPatrick Mooney goto fail;
14444c87aefeSPatrick Mooney }
14454c87aefeSPatrick Mooney
14464c87aefeSPatrick Mooney sock.sun_family = AF_UNIX;
14471aa1f41fSAndy Fiddaman if (strlcpy(sock.sun_path, hostname, sizeof (sock.sun_path)) >=
14484c87aefeSPatrick Mooney sizeof (sock.sun_path)) {
14491aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGERR,
14501aa1f41fSAndy Fiddaman "socket path '%s' too long\n", hostname);
14514c87aefeSPatrick Mooney goto fail;
14524c87aefeSPatrick Mooney }
14534c87aefeSPatrick Mooney
14541aa1f41fSAndy Fiddaman (void) unlink(hostname);
14551aa1f41fSAndy Fiddaman if (bind(s->rs_fd, (struct sockaddr *)&sock,
14561aa1f41fSAndy Fiddaman sizeof (sock)) < 0) {
14574c87aefeSPatrick Mooney perror("bind");
14584c87aefeSPatrick Mooney goto fail;
14594c87aefeSPatrick Mooney }
14601aa1f41fSAndy Fiddaman } else {
14611aa1f41fSAndy Fiddaman struct addrinfo hints, *ai = NULL;
14621aa1f41fSAndy Fiddaman char servname[6];
14631aa1f41fSAndy Fiddaman int e;
14644c87aefeSPatrick Mooney
14651aa1f41fSAndy Fiddaman (void) snprintf(servname, sizeof (servname), "%d",
14661aa1f41fSAndy Fiddaman port ? port : RFB_DEFAULT_PORT);
14671aa1f41fSAndy Fiddaman
14681aa1f41fSAndy Fiddaman if (hostname == NULL || strlen(hostname) == 0) {
14691aa1f41fSAndy Fiddaman #if defined(INET)
14701aa1f41fSAndy Fiddaman hostname = "127.0.0.1";
14711aa1f41fSAndy Fiddaman #elif defined(INET6)
14721aa1f41fSAndy Fiddaman hostname = "[::1]";
14731aa1f41fSAndy Fiddaman #endif
14741aa1f41fSAndy Fiddaman }
14751aa1f41fSAndy Fiddaman
14761aa1f41fSAndy Fiddaman memset(&hints, '\0', sizeof (hints));
14771aa1f41fSAndy Fiddaman hints.ai_family = AF_UNSPEC;
14781aa1f41fSAndy Fiddaman hints.ai_socktype = SOCK_STREAM;
14791aa1f41fSAndy Fiddaman hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
14801aa1f41fSAndy Fiddaman
14811aa1f41fSAndy Fiddaman if ((e = getaddrinfo(hostname, servname, &hints, &ai)) != 0) {
14821aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGERR, "getaddrinfo: %s",
14831aa1f41fSAndy Fiddaman gai_strerror(e));
14841aa1f41fSAndy Fiddaman goto fail;
14851aa1f41fSAndy Fiddaman }
14861aa1f41fSAndy Fiddaman
14871aa1f41fSAndy Fiddaman s->rs_fd = socket(ai->ai_family, ai->ai_socktype, 0);
14881aa1f41fSAndy Fiddaman if (s->rs_fd < 0) {
14891aa1f41fSAndy Fiddaman perror("socket");
14901aa1f41fSAndy Fiddaman freeaddrinfo(ai);
14911aa1f41fSAndy Fiddaman goto fail;
14921aa1f41fSAndy Fiddaman }
14931aa1f41fSAndy Fiddaman
14941aa1f41fSAndy Fiddaman e = 1;
14951aa1f41fSAndy Fiddaman (void) setsockopt(s->rs_fd, SOL_SOCKET, SO_REUSEADDR,
14961aa1f41fSAndy Fiddaman &e, sizeof (e));
14971aa1f41fSAndy Fiddaman
14981aa1f41fSAndy Fiddaman if (bind(s->rs_fd, ai->ai_addr, ai->ai_addrlen) < 0) {
14991aa1f41fSAndy Fiddaman perror("bind");
15001aa1f41fSAndy Fiddaman freeaddrinfo(ai);
15011aa1f41fSAndy Fiddaman goto fail;
15021aa1f41fSAndy Fiddaman }
15031aa1f41fSAndy Fiddaman freeaddrinfo(ai);
15041aa1f41fSAndy Fiddaman }
15051aa1f41fSAndy Fiddaman
15061aa1f41fSAndy Fiddaman if (listen(s->rs_fd, 5) < 0) {
15074c87aefeSPatrick Mooney perror("listen");
15084c87aefeSPatrick Mooney goto fail;
15094c87aefeSPatrick Mooney }
15104c87aefeSPatrick Mooney
15111aa1f41fSAndy Fiddaman #ifndef WITHOUT_CAPSICUM
15121aa1f41fSAndy Fiddaman cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE);
15131aa1f41fSAndy Fiddaman if (caph_rights_limit(s->rs_fd, &rights) == -1)
15141aa1f41fSAndy Fiddaman errx(EX_OSERR, "Unable to apply rights for sandbox");
15151aa1f41fSAndy Fiddaman #endif
15164c87aefeSPatrick Mooney
15171aa1f41fSAndy Fiddaman s->rs_connevent = mevent_add(s->rs_fd, EVF_READ, rfb_accept, s);
15181aa1f41fSAndy Fiddaman if (s->rs_connevent == NULL) {
15191aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGERR,
15201aa1f41fSAndy Fiddaman "Failed to set up rfb connection mevent");
15211aa1f41fSAndy Fiddaman goto fail;
15224c87aefeSPatrick Mooney }
15234c87aefeSPatrick Mooney
15241aa1f41fSAndy Fiddaman list_insert_tail(&rfb_list, s);
15254c87aefeSPatrick Mooney
15261aa1f41fSAndy Fiddaman /*
15271aa1f41fSAndy Fiddaman * Wait for first connection. Since the mevent thread is
15281aa1f41fSAndy Fiddaman * not yet running, we can't rely on normal incoming connection
15291aa1f41fSAndy Fiddaman * handling.
15301aa1f41fSAndy Fiddaman */
15311aa1f41fSAndy Fiddaman if (wait != 0) {
15321aa1f41fSAndy Fiddaman fd_set rfds;
15331aa1f41fSAndy Fiddaman int e;
15341aa1f41fSAndy Fiddaman
15351aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGWARN,
15361aa1f41fSAndy Fiddaman "holding boot until first client connection");
15371aa1f41fSAndy Fiddaman
15381aa1f41fSAndy Fiddaman for (;;) {
15391aa1f41fSAndy Fiddaman FD_ZERO(&rfds);
15401aa1f41fSAndy Fiddaman FD_SET(s->rs_fd, &rfds);
15411aa1f41fSAndy Fiddaman
15421aa1f41fSAndy Fiddaman e = select(s->rs_fd + 1, &rfds, NULL, NULL, NULL);
15431aa1f41fSAndy Fiddaman if (e < 0 && errno == EINTR)
15441aa1f41fSAndy Fiddaman continue;
15451aa1f41fSAndy Fiddaman if (e < 0 || FD_ISSET(s->rs_fd, &rfds))
15461aa1f41fSAndy Fiddaman break;
15471aa1f41fSAndy Fiddaman }
15481aa1f41fSAndy Fiddaman rfb_printf(NULL, RFB_LOGWARN, "continuing boot");
15494c87aefeSPatrick Mooney }
15504c87aefeSPatrick Mooney
15514c87aefeSPatrick Mooney return (0);
15524c87aefeSPatrick Mooney
15534c87aefeSPatrick Mooney fail:
15541aa1f41fSAndy Fiddaman if (s->rs_fd != -1)
15551aa1f41fSAndy Fiddaman VERIFY3S(close(s->rs_fd), ==, 0);
15561aa1f41fSAndy Fiddaman (void) pthread_mutex_destroy(&s->rs_clientlock);
15571aa1f41fSAndy Fiddaman list_destroy(&s->rs_clients);
15581aa1f41fSAndy Fiddaman free(s);
15594c87aefeSPatrick Mooney return (-1);
15604c87aefeSPatrick Mooney }
1561