xref: /illumos-gate/usr/src/cmd/bhyve/rfb.c (revision 32640292)
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>
651aa1f41fSAndy Fiddaman #ifndef NO_OPENSSL
661aa1f41fSAndy Fiddaman #include <openssl/des.h>
671aa1f41fSAndy Fiddaman #endif
684c87aefeSPatrick Mooney #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>
774c87aefeSPatrick Mooney #endif
784c87aefeSPatrick Mooney 
79bf21cd93STycho Nightingale #include "bhyvegc.h"
801aa1f41fSAndy Fiddaman #include "config.h"
81154972afSPatrick Mooney #include "debug.h"
82bf21cd93STycho Nightingale #include "console.h"
83bf21cd93STycho Nightingale #include "rfb.h"
841aa1f41fSAndy Fiddaman #include "rfb_impl.h"
854c87aefeSPatrick Mooney #include "sockstream.h"
864c87aefeSPatrick Mooney 
871aa1f41fSAndy Fiddaman static uint_t rfb_debug = 0;
881aa1f41fSAndy Fiddaman static list_t rfb_list;
891aa1f41fSAndy Fiddaman static id_space_t *rfb_idspace;
901aa1f41fSAndy Fiddaman 
911aa1f41fSAndy Fiddaman static bool rfb_sse42;
921aa1f41fSAndy Fiddaman static pthread_once_t rfb_once = PTHREAD_ONCE_INIT;
931aa1f41fSAndy Fiddaman 
941aa1f41fSAndy Fiddaman extern int raw_stdio;
951aa1f41fSAndy Fiddaman 
961aa1f41fSAndy Fiddaman static void rfb_send_extended_keyevent_update_msg(rfb_client_t *);
971aa1f41fSAndy Fiddaman 
981aa1f41fSAndy Fiddaman 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, ...)
1001aa1f41fSAndy Fiddaman {
1011aa1f41fSAndy Fiddaman 	FILE *fp = stdout;
1021aa1f41fSAndy Fiddaman 	va_list ap;
1031aa1f41fSAndy Fiddaman 
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 	}
1261aa1f41fSAndy Fiddaman }
1271aa1f41fSAndy Fiddaman 
1281aa1f41fSAndy Fiddaman static void
rfb_init_once(void)1291aa1f41fSAndy Fiddaman rfb_init_once(void)
1301aa1f41fSAndy Fiddaman {
1311aa1f41fSAndy Fiddaman 	uint_t cpu_registers[4], ecx;
1321aa1f41fSAndy Fiddaman 
1331aa1f41fSAndy Fiddaman 	do_cpuid(1, cpu_registers);
1341aa1f41fSAndy Fiddaman 	ecx = cpu_registers[2];
1351aa1f41fSAndy Fiddaman 	rfb_sse42 = (ecx & CPUID2_SSE42) != 0;
1361aa1f41fSAndy Fiddaman 
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);
1491aa1f41fSAndy Fiddaman }
1501aa1f41fSAndy Fiddaman 
1511aa1f41fSAndy Fiddaman static void
rfb_free_client(rfb_client_t * c)1521aa1f41fSAndy Fiddaman rfb_free_client(rfb_client_t *c)
1531aa1f41fSAndy 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);
1581aa1f41fSAndy Fiddaman 
1591aa1f41fSAndy Fiddaman 	if (c->rc_encodings & RFB_ENCODING_ZLIB)
1601aa1f41fSAndy Fiddaman 		(void) deflateEnd(&c->rc_zstream);
1611aa1f41fSAndy Fiddaman 
1621aa1f41fSAndy Fiddaman 	if (c->rc_fd != -1)
1631aa1f41fSAndy Fiddaman 		(void) close(c->rc_fd);
1641aa1f41fSAndy Fiddaman 
1651aa1f41fSAndy Fiddaman 	free(c);
1661aa1f41fSAndy Fiddaman }
1671aa1f41fSAndy Fiddaman 
1681aa1f41fSAndy Fiddaman /*
1691aa1f41fSAndy Fiddaman  * Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only
1701aa1f41fSAndy Fiddaman  */
1711aa1f41fSAndy Fiddaman static inline uint32_t
fast_crc32(void * buf,int len,uint32_t crcval)1721aa1f41fSAndy Fiddaman fast_crc32(void *buf, int len, uint32_t crcval)
1731aa1f41fSAndy Fiddaman {
1741aa1f41fSAndy Fiddaman 	uint32_t q = len / sizeof (uint32_t);
1751aa1f41fSAndy Fiddaman 	uint32_t *p = (uint32_t *)buf;
1764c87aefeSPatrick Mooney 
1771aa1f41fSAndy Fiddaman 	while (q--) {
1781aa1f41fSAndy Fiddaman 		/* BEGIN CSTYLED */
1791aa1f41fSAndy Fiddaman 		asm volatile (
1801aa1f41fSAndy Fiddaman 		    /* crc32l %ecx,%esi */
1811aa1f41fSAndy Fiddaman 		    ".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
1821aa1f41fSAndy Fiddaman 		    :"=S" (crcval)
1831aa1f41fSAndy Fiddaman 		    :"0" (crcval), "c" (*p)
1841aa1f41fSAndy Fiddaman 		);
1851aa1f41fSAndy Fiddaman 		/* END CSTYLED */
1861aa1f41fSAndy Fiddaman 		p++;
1871aa1f41fSAndy Fiddaman 	}
188dc8050e8SMarko Kiiskila 
1891aa1f41fSAndy Fiddaman 	return (crcval);
1901aa1f41fSAndy Fiddaman }
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)
1941aa1f41fSAndy Fiddaman {
19538864087SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "sending client status %u (%s)",
19638864087SAndy Fiddaman 	    status, msg ? msg : "NULL");
1974c87aefeSPatrick Mooney 
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];
203dc8050e8SMarko Kiiskila 
2041aa1f41fSAndy Fiddaman 		rfb_printf(c, RFB_LOGWARN, msg);
2054c87aefeSPatrick Mooney 
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 	}
2101aa1f41fSAndy Fiddaman }
211bf21cd93STycho Nightingale 
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;
217bf21cd93STycho Nightingale 
21838864087SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "handshake version");
2194c87aefeSPatrick Mooney 
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 	}
225bf21cd93STycho Nightingale 
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 	}
2354c87aefeSPatrick Mooney 
2361aa1f41fSAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "version handshake, client ver '%.*s'",
2371aa1f41fSAndy Fiddaman 	    l - 1, buf);
238b0de25cbSAndy 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 	}
2434c87aefeSPatrick Mooney 
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 	}
2654c87aefeSPatrick Mooney 
2661aa1f41fSAndy Fiddaman 	return (true);
2671aa1f41fSAndy Fiddaman }
268dc8050e8SMarko Kiiskila 
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;
274dc8050e8SMarko Kiiskila 
2751aa1f41fSAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "handshake auth");
2764c87aefeSPatrick Mooney 
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;
28137fc8a1fSAndy Fiddaman #endif
2824c87aefeSPatrick Mooney 
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);
34337fc8a1fSAndy 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);
40637fc8a1fSAndy 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)
413bf21cd93STycho Nightingale {
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);
471bf21cd93STycho Nightingale }
472bf21cd93STycho Nightingale 
4731aa1f41fSAndy Fiddaman static bool
rfb_handshake(rfb_client_t * c)4741aa1f41fSAndy Fiddaman rfb_handshake(rfb_client_t *c)
475bf21cd93STycho Nightingale {
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);
486bf21cd93STycho Nightingale }
487bf21cd93STycho Nightingale 
488b0de25cbSAndy 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)
490b0de25cbSAndy 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);
501b0de25cbSAndy Fiddaman }
502b0de25cbSAndy 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)
505bf21cd93STycho Nightingale {
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;
510bf21cd93STycho Nightingale 
5111aa1f41fSAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "received pixel format");
512bf21cd93STycho Nightingale 
5131aa1f41fSAndy Fiddaman 	if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
5141aa1f41fSAndy Fiddaman 		return (false);
515bf21cd93STycho Nightingale 
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)
557bf21cd93STycho Nightingale {
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);
568bf21cd93STycho Nightingale 
5691aa1f41fSAndy Fiddaman 	for (uint_t i = 0; i < msg.re_numencs; i++) {
5701aa1f41fSAndy Fiddaman 		uint32_t enc;
571bf21cd93STycho Nightingale 
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;
582bf21cd93STycho Nightingale 			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;
59284659b24SMichael Zeller 			}
5934c87aefeSPatrick Mooney 			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;
597bf21cd93STycho Nightingale 			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;
602b0de25cbSAndy 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);
611bf21cd93STycho Nightingale 		}
612bf21cd93STycho Nightingale 	}
6131aa1f41fSAndy Fiddaman 
6141aa1f41fSAndy Fiddaman 	return (true);
615bf21cd93STycho Nightingale }
616bf21cd93STycho Nightingale 
6171aa1f41fSAndy Fiddaman static bool
rfb_recv_update(rfb_client_t * c)6181aa1f41fSAndy Fiddaman rfb_recv_update(rfb_client_t *c)
6194c87aefeSPatrick Mooney {
6201aa1f41fSAndy Fiddaman 	rfb_cs_update_msg_t msg;
6214c87aefeSPatrick Mooney 
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);
6334c87aefeSPatrick Mooney 	}
6344c87aefeSPatrick Mooney 
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);
6434c87aefeSPatrick Mooney }
6444c87aefeSPatrick Mooney 
6451aa1f41fSAndy Fiddaman static bool
rfb_recv_key_event(rfb_client_t * c)6461aa1f41fSAndy Fiddaman rfb_recv_key_event(rfb_client_t *c)
647dc8050e8SMarko Kiiskila {
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);
652dc8050e8SMarko Kiiskila 
6531aa1f41fSAndy Fiddaman 	msg.rke_sym = htonl(msg.rke_sym);
654dc8050e8SMarko Kiiskila 
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);
662dc8050e8SMarko Kiiskila }
6634c87aefeSPatrick Mooney 
6641aa1f41fSAndy Fiddaman static bool
rfb_recv_pointer_event(rfb_client_t * c)6651aa1f41fSAndy Fiddaman rfb_recv_pointer_event(rfb_client_t *c)
6664c87aefeSPatrick Mooney {
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,
8311aa1f41fSAndy Fiddaman     int x, int y, int w, int h)
8321aa1f41fSAndy Fiddaman {
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 	}
8444c87aefeSPatrick Mooney 
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;
8581aa1f41fSAndy Fiddaman 	w *= sizeof (uint32_t);
8591aa1f41fSAndy Fiddaman 
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);
8934c87aefeSPatrick Mooney 	}
8944c87aefeSPatrick Mooney 
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;
9224c87aefeSPatrick Mooney 
9231aa1f41fSAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "send entire screen");
924bf21cd93STycho Nightingale 
9251aa1f41fSAndy Fiddaman 	/* Just the one (big) rect. */
9261aa1f41fSAndy Fiddaman 	if (!rfb_send_update_header(c, 1))
9271aa1f41fSAndy Fiddaman 		return (false);
9281aa1f41fSAndy Fiddaman 
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 *
9431aa1f41fSAndy Fiddaman 		    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;
9471aa1f41fSAndy Fiddaman 
9481aa1f41fSAndy Fiddaman 		c->rc_zstream.total_in = 0;
9491aa1f41fSAndy Fiddaman 		c->rc_zstream.total_out = 0;
9501aa1f41fSAndy Fiddaman 
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);
979bf21cd93STycho Nightingale }
980bf21cd93STycho Nightingale 
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);
10041aa1f41fSAndy Fiddaman 
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;
10361aa1f41fSAndy Fiddaman 			goto done;
10371aa1f41fSAndy Fiddaman 		}
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;
10434c87aefeSPatrick Mooney 	}
10444c87aefeSPatrick Mooney 
10451aa1f41fSAndy Fiddaman 	sendall = atomic_exchange(&c->rc_send_fullscreen, false);
10461aa1f41fSAndy Fiddaman 
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;
10734c87aefeSPatrick Mooney 
10741aa1f41fSAndy Fiddaman 			if (x == xcells - 1 && rem_x > 0)
10751aa1f41fSAndy Fiddaman 				cellwidth = rem_x;
10764c87aefeSPatrick Mooney 			else
10771aa1f41fSAndy Fiddaman 				cellwidth = RFB_PIX_PER_CELL;
10781aa1f41fSAndy Fiddaman 
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 
11101aa1f41fSAndy Fiddaman 	/*
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.
11131aa1f41fSAndy Fiddaman 	 */
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 	}
1127dc8050e8SMarko Kiiskila 
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);
1163dc8050e8SMarko Kiiskila 
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)
1169bf21cd93STycho Nightingale {
11701aa1f41fSAndy Fiddaman 	rfb_client_t *c = arg;
11711aa1f41fSAndy Fiddaman 	unsigned char cmd;
11721aa1f41fSAndy Fiddaman 	bool ret = true;
11731aa1f41fSAndy Fiddaman 
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;
12011aa1f41fSAndy Fiddaman 		}
1202b0de25cbSAndy Fiddaman 	}
1203b0de25cbSAndy Fiddaman 
12041aa1f41fSAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "client rx thread exiting");
12051aa1f41fSAndy Fiddaman 	c->rc_closing = true;
1206bf21cd93STycho Nightingale 
12071aa1f41fSAndy Fiddaman 	return (NULL);
1208bf21cd93STycho Nightingale }
1209bf21cd93STycho Nightingale 
12101aa1f41fSAndy Fiddaman static void *
rfb_client_tx_thread(void * arg)12111aa1f41fSAndy Fiddaman rfb_client_tx_thread(void *arg)
1212b0de25cbSAndy Fiddaman {
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;
12191aa1f41fSAndy Fiddaman 	int err;
1220b0de25cbSAndy Fiddaman 
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);
1223b0de25cbSAndy Fiddaman 
12241aa1f41fSAndy Fiddaman 	c->rc_sinfo.rsi_pixfmt = c->rc_s->rs_pixfmt;
12251aa1f41fSAndy Fiddaman 	c->rc_encodings = RFB_ENCODING_RAW;
12264c87aefeSPatrick Mooney 
122738864087SAndy Fiddaman 	if (!rfb_handshake(c)) {
122838864087SAndy Fiddaman 		rfb_printf(c, RFB_LOGWARN, "handshake failure");
12291aa1f41fSAndy Fiddaman 		goto out;
123038864087SAndy Fiddaman 	}
12314c87aefeSPatrick Mooney 
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 	}
12384c87aefeSPatrick Mooney 
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;
12434c87aefeSPatrick Mooney 	}
1244bf21cd93STycho Nightingale 
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);
12474c87aefeSPatrick Mooney 
12481aa1f41fSAndy Fiddaman 	tprev = gethrtime();
12494c87aefeSPatrick Mooney 
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;
12564c87aefeSPatrick Mooney 
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;
12824c87aefeSPatrick Mooney 			}
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;
13151aa1f41fSAndy Fiddaman 
13161aa1f41fSAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "incoming connection");
13171aa1f41fSAndy Fiddaman 
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);
1340dc8050e8SMarko Kiiskila 		}
13414c87aefeSPatrick Mooney 	}
13424c87aefeSPatrick Mooney 
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 
1388bf21cd93STycho Nightingale 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)
1391bf21cd93STycho Nightingale {
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);
13984c87aefeSPatrick Mooney 
13991aa1f41fSAndy Fiddaman 	if (rfb_idspace == NULL) {
14001aa1f41fSAndy Fiddaman 		rfb_printf(NULL, RFB_LOGERR,
14011aa1f41fSAndy Fiddaman 		    "rfb_idspace could not be allocated");
14021aa1f41fSAndy Fiddaman 		return (-1);
14034c87aefeSPatrick Mooney 	}
14044c87aefeSPatrick Mooney 
14051aa1f41fSAndy Fiddaman 	if ((s = calloc(1, sizeof (rfb_server_t))) == NULL) {
14061aa1f41fSAndy Fiddaman 		perror("calloc");
14071aa1f41fSAndy Fiddaman 		return (-1);
1408bf21cd93STycho Nightingale 	}
14091aa1f41fSAndy Fiddaman 	s->rs_fd = -1;
14101aa1f41fSAndy Fiddaman 	s->rs_name = name;
1411bf21cd93STycho Nightingale 
14121aa1f41fSAndy Fiddaman 	if (password != NULL && strlen(password) > 0)
14131aa1f41fSAndy Fiddaman 		s->rs_password = password;
1414bf21cd93STycho Nightingale 
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);
1419bf21cd93STycho Nightingale 	}
1420bf21cd93STycho Nightingale 
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 == '/') {
14381aa1f41fSAndy Fiddaman 		struct sockaddr_un sock;
14391aa1f41fSAndy Fiddaman 
14401aa1f41fSAndy Fiddaman 		s->rs_fd = socket(PF_UNIX, SOCK_STREAM, 0);
14411aa1f41fSAndy Fiddaman 		if (s->rs_fd < 0) {
14421aa1f41fSAndy Fiddaman 			perror("socket");
14431aa1f41fSAndy Fiddaman 			goto fail;
14441aa1f41fSAndy Fiddaman 		}
14454c87aefeSPatrick Mooney 
14461aa1f41fSAndy Fiddaman 		sock.sun_family = AF_UNIX;
14471aa1f41fSAndy Fiddaman 		if (strlcpy(sock.sun_path, hostname, sizeof (sock.sun_path)) >=
14481aa1f41fSAndy Fiddaman 		    sizeof (sock.sun_path)) {
14491aa1f41fSAndy Fiddaman 			rfb_printf(NULL, RFB_LOGERR,
14501aa1f41fSAndy Fiddaman 			    "socket path '%s' too long\n", hostname);
14511aa1f41fSAndy Fiddaman 			goto fail;
14521aa1f41fSAndy Fiddaman 		}
14534c87aefeSPatrick Mooney 
14541aa1f41fSAndy Fiddaman 		(void) unlink(hostname);
14551aa1f41fSAndy Fiddaman 		if (bind(s->rs_fd, (struct sockaddr *)&sock,
14561aa1f41fSAndy Fiddaman 		    sizeof (sock)) < 0) {
14571aa1f41fSAndy Fiddaman 			perror("bind");
14581aa1f41fSAndy Fiddaman 			goto fail;
14591aa1f41fSAndy Fiddaman 		}
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);
14674c87aefeSPatrick Mooney 
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 		}
1475bf21cd93STycho Nightingale 
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;
148084659b24SMichael Zeller 
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 		}
14864c87aefeSPatrick Mooney 
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 		}
14934c87aefeSPatrick Mooney 
14941aa1f41fSAndy Fiddaman 		e = 1;
14951aa1f41fSAndy Fiddaman 		(void) setsockopt(s->rs_fd, SOL_SOCKET, SO_REUSEADDR,
14961aa1f41fSAndy Fiddaman 		    &e, sizeof (e));
14974c87aefeSPatrick Mooney 
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);
15044c87aefeSPatrick Mooney 	}
15054c87aefeSPatrick Mooney 
15061aa1f41fSAndy Fiddaman 	if (listen(s->rs_fd, 5) < 0) {
15071aa1f41fSAndy Fiddaman 		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");
15214c87aefeSPatrick Mooney 		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;
15344c87aefeSPatrick Mooney 
15351aa1f41fSAndy Fiddaman 		rfb_printf(NULL, RFB_LOGWARN,
15361aa1f41fSAndy Fiddaman 		    "holding boot until first client connection");
15374c87aefeSPatrick Mooney 
15381aa1f41fSAndy Fiddaman 		for (;;) {
15391aa1f41fSAndy Fiddaman 			FD_ZERO(&rfds);
15401aa1f41fSAndy Fiddaman 			FD_SET(s->rs_fd, &rfds);
15414c87aefeSPatrick Mooney 
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