xref: /illumos-gate/usr/src/cmd/bhyve/common/rfb.c (revision 5c4a5fe16715fb423db76577a6883b5bbecdbe45)
1*5c4a5fe1SAndy Fiddaman /*-
2*5c4a5fe1SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
3*5c4a5fe1SAndy Fiddaman  *
4*5c4a5fe1SAndy Fiddaman  * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5*5c4a5fe1SAndy Fiddaman  * Copyright (c) 2015 Leon Dang
6*5c4a5fe1SAndy Fiddaman  * Copyright 2020 Joyent, Inc.
7*5c4a5fe1SAndy Fiddaman  * All rights reserved.
8*5c4a5fe1SAndy Fiddaman  *
9*5c4a5fe1SAndy Fiddaman  * Redistribution and use in source and binary forms, with or without
10*5c4a5fe1SAndy Fiddaman  * modification, are permitted provided that the following conditions
11*5c4a5fe1SAndy Fiddaman  * are met:
12*5c4a5fe1SAndy Fiddaman  * 1. Redistributions of source code must retain the above copyright
13*5c4a5fe1SAndy Fiddaman  *    notice, this list of conditions and the following disclaimer.
14*5c4a5fe1SAndy Fiddaman  * 2. Redistributions in binary form must reproduce the above copyright
15*5c4a5fe1SAndy Fiddaman  *    notice, this list of conditions and the following disclaimer in the
16*5c4a5fe1SAndy Fiddaman  *    documentation and/or other materials provided with the distribution.
17*5c4a5fe1SAndy Fiddaman  *
18*5c4a5fe1SAndy Fiddaman  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
19*5c4a5fe1SAndy Fiddaman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*5c4a5fe1SAndy Fiddaman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*5c4a5fe1SAndy Fiddaman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22*5c4a5fe1SAndy Fiddaman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*5c4a5fe1SAndy Fiddaman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*5c4a5fe1SAndy Fiddaman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*5c4a5fe1SAndy Fiddaman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*5c4a5fe1SAndy Fiddaman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*5c4a5fe1SAndy Fiddaman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*5c4a5fe1SAndy Fiddaman  * SUCH DAMAGE.
29*5c4a5fe1SAndy Fiddaman  */
30*5c4a5fe1SAndy Fiddaman /*
31*5c4a5fe1SAndy Fiddaman  * This file and its contents are supplied under the terms of the
32*5c4a5fe1SAndy Fiddaman  * Common Development and Distribution License ("CDDL"), version 1.0.
33*5c4a5fe1SAndy Fiddaman  * You may only use this file in accordance with the terms of version
34*5c4a5fe1SAndy Fiddaman  * 1.0 of the CDDL.
35*5c4a5fe1SAndy Fiddaman  *
36*5c4a5fe1SAndy Fiddaman  * A full copy of the text of the CDDL should have accompanied this
37*5c4a5fe1SAndy Fiddaman  * source.  A copy of the CDDL is also available via the Internet at
38*5c4a5fe1SAndy Fiddaman  * http://www.illumos.org/license/CDDL.
39*5c4a5fe1SAndy Fiddaman  *
40*5c4a5fe1SAndy Fiddaman  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
41*5c4a5fe1SAndy Fiddaman  */
42*5c4a5fe1SAndy Fiddaman 
43*5c4a5fe1SAndy Fiddaman /*
44*5c4a5fe1SAndy Fiddaman  * References to the RFB protocol specification refer to:
45*5c4a5fe1SAndy Fiddaman  * - [1] https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst
46*5c4a5fe1SAndy Fiddaman  */
47*5c4a5fe1SAndy Fiddaman 
48*5c4a5fe1SAndy Fiddaman #include <err.h>
49*5c4a5fe1SAndy Fiddaman #include <errno.h>
50*5c4a5fe1SAndy Fiddaman #include <libidspace.h>
51*5c4a5fe1SAndy Fiddaman #include <netdb.h>
52*5c4a5fe1SAndy Fiddaman #include <pthread.h>
53*5c4a5fe1SAndy Fiddaman #include <pthread_np.h>
54*5c4a5fe1SAndy Fiddaman #include <signal.h>
55*5c4a5fe1SAndy Fiddaman #include <stdatomic.h>
56*5c4a5fe1SAndy Fiddaman #include <stdbool.h>
57*5c4a5fe1SAndy Fiddaman #include <stdio.h>
58*5c4a5fe1SAndy Fiddaman #include <stdlib.h>
59*5c4a5fe1SAndy Fiddaman #include <string.h>
60*5c4a5fe1SAndy Fiddaman #include <unistd.h>
61*5c4a5fe1SAndy Fiddaman #include <zlib.h>
62*5c4a5fe1SAndy Fiddaman #include <machine/cpufunc.h>
63*5c4a5fe1SAndy Fiddaman #include <machine/specialreg.h>
64*5c4a5fe1SAndy Fiddaman #include <netinet/in.h>
65*5c4a5fe1SAndy Fiddaman #ifndef NO_OPENSSL
66*5c4a5fe1SAndy Fiddaman #include <openssl/des.h>
67*5c4a5fe1SAndy Fiddaman #endif
68*5c4a5fe1SAndy Fiddaman #include <sys/debug.h>
69*5c4a5fe1SAndy Fiddaman #include <sys/endian.h>
70*5c4a5fe1SAndy Fiddaman #include <sys/list.h>
71*5c4a5fe1SAndy Fiddaman #include <sys/socket.h>
72*5c4a5fe1SAndy Fiddaman #include <sys/types.h>
73*5c4a5fe1SAndy Fiddaman #ifndef WITHOUT_CAPSICUM
74*5c4a5fe1SAndy Fiddaman #include <sysexits.h>
75*5c4a5fe1SAndy Fiddaman #include <sys/capsicum.h>
76*5c4a5fe1SAndy Fiddaman #include <capsicum_helpers.h>
77*5c4a5fe1SAndy Fiddaman #endif
78*5c4a5fe1SAndy Fiddaman 
79*5c4a5fe1SAndy Fiddaman #include "bhyvegc.h"
80*5c4a5fe1SAndy Fiddaman #include "config.h"
81*5c4a5fe1SAndy Fiddaman #include "debug.h"
82*5c4a5fe1SAndy Fiddaman #include "console.h"
83*5c4a5fe1SAndy Fiddaman #include "rfb.h"
84*5c4a5fe1SAndy Fiddaman #include "rfb_impl.h"
85*5c4a5fe1SAndy Fiddaman #include "sockstream.h"
86*5c4a5fe1SAndy Fiddaman 
87*5c4a5fe1SAndy Fiddaman static uint_t rfb_debug = 0;
88*5c4a5fe1SAndy Fiddaman static list_t rfb_list;
89*5c4a5fe1SAndy Fiddaman static id_space_t *rfb_idspace;
90*5c4a5fe1SAndy Fiddaman 
91*5c4a5fe1SAndy Fiddaman static bool rfb_sse42;
92*5c4a5fe1SAndy Fiddaman static pthread_once_t rfb_once = PTHREAD_ONCE_INIT;
93*5c4a5fe1SAndy Fiddaman 
94*5c4a5fe1SAndy Fiddaman extern int raw_stdio;
95*5c4a5fe1SAndy Fiddaman 
96*5c4a5fe1SAndy Fiddaman static void rfb_send_extended_keyevent_update_msg(rfb_client_t *);
97*5c4a5fe1SAndy Fiddaman 
98*5c4a5fe1SAndy Fiddaman static void
rfb_printf(rfb_client_t * c,rfb_loglevel_t level,const char * fmt,...)99*5c4a5fe1SAndy Fiddaman rfb_printf(rfb_client_t *c, rfb_loglevel_t level, const char *fmt, ...)
100*5c4a5fe1SAndy Fiddaman {
101*5c4a5fe1SAndy Fiddaman 	FILE *fp = stdout;
102*5c4a5fe1SAndy Fiddaman 	va_list ap;
103*5c4a5fe1SAndy Fiddaman 
104*5c4a5fe1SAndy Fiddaman 	switch (level) {
105*5c4a5fe1SAndy Fiddaman 	case RFB_LOGDEBUG:
106*5c4a5fe1SAndy Fiddaman 		if (rfb_debug == 0)
107*5c4a5fe1SAndy Fiddaman 			return;
108*5c4a5fe1SAndy Fiddaman 		/* FALLTHROUGH */
109*5c4a5fe1SAndy Fiddaman 	case RFB_LOGERR:
110*5c4a5fe1SAndy Fiddaman 		fp = stderr;
111*5c4a5fe1SAndy Fiddaman 		/* FALLTHROUGH */
112*5c4a5fe1SAndy Fiddaman 	case RFB_LOGWARN:
113*5c4a5fe1SAndy Fiddaman 		if (c != NULL)
114*5c4a5fe1SAndy Fiddaman 			(void) fprintf(fp, "rfb%u: ", c->rc_instance);
115*5c4a5fe1SAndy Fiddaman 		else
116*5c4a5fe1SAndy Fiddaman 			(void) fprintf(fp, "rfb: ");
117*5c4a5fe1SAndy Fiddaman 		va_start(ap, fmt);
118*5c4a5fe1SAndy Fiddaman 		(void) vfprintf(fp, fmt, ap);
119*5c4a5fe1SAndy Fiddaman 		va_end(ap);
120*5c4a5fe1SAndy Fiddaman 		if (raw_stdio)
121*5c4a5fe1SAndy Fiddaman 			(void) fprintf(fp, "\r\n");
122*5c4a5fe1SAndy Fiddaman 		else
123*5c4a5fe1SAndy Fiddaman 			(void) fprintf(fp, "\n");
124*5c4a5fe1SAndy Fiddaman 		(void) fflush(fp);
125*5c4a5fe1SAndy Fiddaman 	}
126*5c4a5fe1SAndy Fiddaman }
127*5c4a5fe1SAndy Fiddaman 
128*5c4a5fe1SAndy Fiddaman static void
rfb_init_once(void)129*5c4a5fe1SAndy Fiddaman rfb_init_once(void)
130*5c4a5fe1SAndy Fiddaman {
131*5c4a5fe1SAndy Fiddaman 	uint_t cpu_registers[4], ecx;
132*5c4a5fe1SAndy Fiddaman 
133*5c4a5fe1SAndy Fiddaman 	do_cpuid(1, cpu_registers);
134*5c4a5fe1SAndy Fiddaman 	ecx = cpu_registers[2];
135*5c4a5fe1SAndy Fiddaman 	rfb_sse42 = (ecx & CPUID2_SSE42) != 0;
136*5c4a5fe1SAndy Fiddaman 
137*5c4a5fe1SAndy Fiddaman 	if (rfb_sse42)
138*5c4a5fe1SAndy Fiddaman 		rfb_printf(NULL, RFB_LOGDEBUG, "enabled fast crc32");
139*5c4a5fe1SAndy Fiddaman 	else
140*5c4a5fe1SAndy Fiddaman 		rfb_printf(NULL, RFB_LOGWARN, "no support for fast crc32");
141*5c4a5fe1SAndy Fiddaman 
142*5c4a5fe1SAndy Fiddaman 	if (get_config_bool_default("rfb.debug", false))
143*5c4a5fe1SAndy Fiddaman 		rfb_debug = 1;
144*5c4a5fe1SAndy Fiddaman 
145*5c4a5fe1SAndy Fiddaman 	list_create(&rfb_list, sizeof (rfb_server_t),
146*5c4a5fe1SAndy Fiddaman 	    offsetof(rfb_server_t, rs_node));
147*5c4a5fe1SAndy Fiddaman 
148*5c4a5fe1SAndy Fiddaman 	rfb_idspace = id_space_create("rfb", 0, INT32_MAX);
149*5c4a5fe1SAndy Fiddaman }
150*5c4a5fe1SAndy Fiddaman 
151*5c4a5fe1SAndy Fiddaman static void
rfb_free_client(rfb_client_t * c)152*5c4a5fe1SAndy Fiddaman rfb_free_client(rfb_client_t *c)
153*5c4a5fe1SAndy Fiddaman {
154*5c4a5fe1SAndy Fiddaman 	free(c->rc_crc);
155*5c4a5fe1SAndy Fiddaman 	free(c->rc_crc_tmp);
156*5c4a5fe1SAndy Fiddaman 	free(c->rc_zbuf);
157*5c4a5fe1SAndy Fiddaman 	free(c->rc_gci.data);
158*5c4a5fe1SAndy Fiddaman 
159*5c4a5fe1SAndy Fiddaman 	if (c->rc_encodings & RFB_ENCODING_ZLIB)
160*5c4a5fe1SAndy Fiddaman 		(void) deflateEnd(&c->rc_zstream);
161*5c4a5fe1SAndy Fiddaman 
162*5c4a5fe1SAndy Fiddaman 	if (c->rc_fd != -1)
163*5c4a5fe1SAndy Fiddaman 		(void) close(c->rc_fd);
164*5c4a5fe1SAndy Fiddaman 
165*5c4a5fe1SAndy Fiddaman 	free(c);
166*5c4a5fe1SAndy Fiddaman }
167*5c4a5fe1SAndy Fiddaman 
168*5c4a5fe1SAndy Fiddaman /*
169*5c4a5fe1SAndy Fiddaman  * Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only
170*5c4a5fe1SAndy Fiddaman  */
171*5c4a5fe1SAndy Fiddaman static inline uint32_t
fast_crc32(void * buf,int len,uint32_t crcval)172*5c4a5fe1SAndy Fiddaman fast_crc32(void *buf, int len, uint32_t crcval)
173*5c4a5fe1SAndy Fiddaman {
174*5c4a5fe1SAndy Fiddaman 	uint32_t q = len / sizeof (uint32_t);
175*5c4a5fe1SAndy Fiddaman 	uint32_t *p = (uint32_t *)buf;
176*5c4a5fe1SAndy Fiddaman 
177*5c4a5fe1SAndy Fiddaman 	while (q--) {
178*5c4a5fe1SAndy Fiddaman 		/* BEGIN CSTYLED */
179*5c4a5fe1SAndy Fiddaman 		asm volatile (
180*5c4a5fe1SAndy Fiddaman 		    /* crc32l %ecx,%esi */
181*5c4a5fe1SAndy Fiddaman 		    ".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
182*5c4a5fe1SAndy Fiddaman 		    :"=S" (crcval)
183*5c4a5fe1SAndy Fiddaman 		    :"0" (crcval), "c" (*p)
184*5c4a5fe1SAndy Fiddaman 		);
185*5c4a5fe1SAndy Fiddaman 		/* END CSTYLED */
186*5c4a5fe1SAndy Fiddaman 		p++;
187*5c4a5fe1SAndy Fiddaman 	}
188*5c4a5fe1SAndy Fiddaman 
189*5c4a5fe1SAndy Fiddaman 	return (crcval);
190*5c4a5fe1SAndy Fiddaman }
191*5c4a5fe1SAndy Fiddaman 
192*5c4a5fe1SAndy Fiddaman static void
rfb_send_client_status(rfb_client_t * c,uint32_t status,const char * msg)193*5c4a5fe1SAndy Fiddaman rfb_send_client_status(rfb_client_t *c, uint32_t status, const char *msg)
194*5c4a5fe1SAndy Fiddaman {
195*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "sending client status %u (%s)",
196*5c4a5fe1SAndy Fiddaman 	    status, msg ? msg : "NULL");
197*5c4a5fe1SAndy Fiddaman 
198*5c4a5fe1SAndy Fiddaman 	status = htonl(status);
199*5c4a5fe1SAndy Fiddaman 	(void) stream_write(c->rc_fd, &status, sizeof (status));
200*5c4a5fe1SAndy Fiddaman 
201*5c4a5fe1SAndy Fiddaman 	if (msg != NULL && status != 0 && c->rc_cver == RFB_CVER_3_8) {
202*5c4a5fe1SAndy Fiddaman 		char buf[4];
203*5c4a5fe1SAndy Fiddaman 
204*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGWARN, msg);
205*5c4a5fe1SAndy Fiddaman 
206*5c4a5fe1SAndy Fiddaman 		be32enc(buf, strlen((char *)msg));
207*5c4a5fe1SAndy Fiddaman 		(void) stream_write(c->rc_fd, buf, 4);
208*5c4a5fe1SAndy Fiddaman 		(void) stream_write(c->rc_fd, msg, strlen((char *)msg));
209*5c4a5fe1SAndy Fiddaman 	}
210*5c4a5fe1SAndy Fiddaman }
211*5c4a5fe1SAndy Fiddaman 
212*5c4a5fe1SAndy Fiddaman static bool
rfb_handshake_version(rfb_client_t * c)213*5c4a5fe1SAndy Fiddaman rfb_handshake_version(rfb_client_t *c)
214*5c4a5fe1SAndy Fiddaman {
215*5c4a5fe1SAndy Fiddaman 	unsigned char buf[RFB_VERSION_LEN];
216*5c4a5fe1SAndy Fiddaman 	ssize_t l;
217*5c4a5fe1SAndy Fiddaman 
218*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "handshake version");
219*5c4a5fe1SAndy Fiddaman 
220*5c4a5fe1SAndy Fiddaman 	if (stream_write(c->rc_fd, RFB_VERSION, RFB_VERSION_LEN) !=
221*5c4a5fe1SAndy Fiddaman 	    RFB_VERSION_LEN) {
222*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGWARN, "could not send server version.");
223*5c4a5fe1SAndy Fiddaman 		return (false);
224*5c4a5fe1SAndy Fiddaman 	}
225*5c4a5fe1SAndy Fiddaman 
226*5c4a5fe1SAndy Fiddaman 	l = stream_read(c->rc_fd, buf, sizeof (buf));
227*5c4a5fe1SAndy Fiddaman 	if (l <= 0) {
228*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGWARN, "client version not read");
229*5c4a5fe1SAndy Fiddaman 		return (false);
230*5c4a5fe1SAndy Fiddaman 	} else if (l != RFB_VERSION_LEN) {
231*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGWARN, "client sent short version - '%.*s'",
232*5c4a5fe1SAndy Fiddaman 		    l, buf);
233*5c4a5fe1SAndy Fiddaman 		return (false);
234*5c4a5fe1SAndy Fiddaman 	}
235*5c4a5fe1SAndy Fiddaman 
236*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "version handshake, client ver '%.*s'",
237*5c4a5fe1SAndy Fiddaman 	    l - 1, buf);
238*5c4a5fe1SAndy Fiddaman 
239*5c4a5fe1SAndy Fiddaman 	if (strncmp(RFB_VERSION, (char *)buf, RFB_VERSION_LEN - 2) != 0) {
240*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGERR, "bad client version '%.*s'", l, buf);
241*5c4a5fe1SAndy Fiddaman 		return (false);
242*5c4a5fe1SAndy Fiddaman 	}
243*5c4a5fe1SAndy Fiddaman 
244*5c4a5fe1SAndy Fiddaman 	switch (buf[RFB_VERSION_LEN - 2]) {
245*5c4a5fe1SAndy Fiddaman 	case '8':
246*5c4a5fe1SAndy Fiddaman 		c->rc_cver = RFB_CVER_3_8;
247*5c4a5fe1SAndy Fiddaman 		break;
248*5c4a5fe1SAndy Fiddaman 	case '7':
249*5c4a5fe1SAndy Fiddaman 		c->rc_cver = RFB_CVER_3_7;
250*5c4a5fe1SAndy Fiddaman 		break;
251*5c4a5fe1SAndy Fiddaman 	case '5':
252*5c4a5fe1SAndy Fiddaman 		/*
253*5c4a5fe1SAndy Fiddaman 		 * From the RFB specification[1], section 7.1.1:
254*5c4a5fe1SAndy Fiddaman 		 * "version 3.5 was wrongly reported by some clients, but this
255*5c4a5fe1SAndy Fiddaman 		 *  should be interpreted by all servers as 3.3."
256*5c4a5fe1SAndy Fiddaman 		 */
257*5c4a5fe1SAndy Fiddaman 	case '3':
258*5c4a5fe1SAndy Fiddaman 		c->rc_cver = RFB_CVER_3_3;
259*5c4a5fe1SAndy Fiddaman 		break;
260*5c4a5fe1SAndy Fiddaman 	default:
261*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGERR, "unsupported client version '%.*s'",
262*5c4a5fe1SAndy Fiddaman 		    l - 1, buf);
263*5c4a5fe1SAndy Fiddaman 		return (false);
264*5c4a5fe1SAndy Fiddaman 	}
265*5c4a5fe1SAndy Fiddaman 
266*5c4a5fe1SAndy Fiddaman 	return (true);
267*5c4a5fe1SAndy Fiddaman }
268*5c4a5fe1SAndy Fiddaman 
269*5c4a5fe1SAndy Fiddaman static bool
rfb_handshake_auth(rfb_client_t * c)270*5c4a5fe1SAndy Fiddaman rfb_handshake_auth(rfb_client_t *c)
271*5c4a5fe1SAndy Fiddaman {
272*5c4a5fe1SAndy Fiddaman 	unsigned char buf[RFBP_SECURITY_VNC_AUTH_LEN];
273*5c4a5fe1SAndy Fiddaman 	int auth_type;
274*5c4a5fe1SAndy Fiddaman 
275*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "handshake auth");
276*5c4a5fe1SAndy Fiddaman 
277*5c4a5fe1SAndy Fiddaman 	auth_type = RFBP_SECURITY_NONE;
278*5c4a5fe1SAndy Fiddaman #ifndef NO_OPENSSL
279*5c4a5fe1SAndy Fiddaman 	if (c->rc_s->rs_password != NULL)
280*5c4a5fe1SAndy Fiddaman 		auth_type = RFBP_SECURITY_VNC_AUTH;
281*5c4a5fe1SAndy Fiddaman #endif
282*5c4a5fe1SAndy Fiddaman 
283*5c4a5fe1SAndy Fiddaman 	switch (c->rc_cver) {
284*5c4a5fe1SAndy Fiddaman 	case RFB_CVER_3_3:
285*5c4a5fe1SAndy Fiddaman 		/*
286*5c4a5fe1SAndy Fiddaman 		 * RFB specification[1] section 7.1.2:
287*5c4a5fe1SAndy Fiddaman 		 * The server decides the security type and sends a single word.
288*5c4a5fe1SAndy Fiddaman 		 */
289*5c4a5fe1SAndy Fiddaman 		be32enc(buf, auth_type);
290*5c4a5fe1SAndy Fiddaman 		(void) stream_write(c->rc_fd, buf, 4);
291*5c4a5fe1SAndy Fiddaman 
292*5c4a5fe1SAndy Fiddaman 		break;
293*5c4a5fe1SAndy Fiddaman 
294*5c4a5fe1SAndy Fiddaman 	case RFB_CVER_3_7:
295*5c4a5fe1SAndy Fiddaman 	case RFB_CVER_3_8:
296*5c4a5fe1SAndy Fiddaman 		/* Send list of supported types. */
297*5c4a5fe1SAndy Fiddaman 		buf[0] = 1;	/* list length */
298*5c4a5fe1SAndy Fiddaman 		buf[1] = auth_type;
299*5c4a5fe1SAndy Fiddaman 		(void) stream_write(c->rc_fd, buf, 2);
300*5c4a5fe1SAndy Fiddaman 
301*5c4a5fe1SAndy Fiddaman 		/* Read agreed security type. */
302*5c4a5fe1SAndy Fiddaman 		if (stream_read(c->rc_fd, buf, 1) != 1) {
303*5c4a5fe1SAndy Fiddaman 			rfb_printf(c, RFB_LOGWARN,
304*5c4a5fe1SAndy Fiddaman 			    "auth fail, no type from client");
305*5c4a5fe1SAndy Fiddaman 			return (false);
306*5c4a5fe1SAndy Fiddaman 		}
307*5c4a5fe1SAndy Fiddaman 
308*5c4a5fe1SAndy Fiddaman 		if (buf[0] != auth_type) {
309*5c4a5fe1SAndy Fiddaman 			rfb_send_client_status(c, 1,
310*5c4a5fe1SAndy Fiddaman 			    "Auth failed: authentication type mismatch");
311*5c4a5fe1SAndy Fiddaman 			return (false);
312*5c4a5fe1SAndy Fiddaman 		}
313*5c4a5fe1SAndy Fiddaman 
314*5c4a5fe1SAndy Fiddaman 		break;
315*5c4a5fe1SAndy Fiddaman 	}
316*5c4a5fe1SAndy Fiddaman 
317*5c4a5fe1SAndy Fiddaman 	if (auth_type == RFBP_SECURITY_NONE) {
318*5c4a5fe1SAndy Fiddaman 		/*
319*5c4a5fe1SAndy Fiddaman 		 * According to the RFB specification[1], section 7.2.1, for a
320*5c4a5fe1SAndy Fiddaman 		 * security type of 'None', client versions 3.3 and 3.7 expect
321*5c4a5fe1SAndy Fiddaman 		 * to move straight to the ClientInit phase, without the server
322*5c4a5fe1SAndy Fiddaman 		 * sending a response. For version 3.8, a SecurityResult word
323*5c4a5fe1SAndy Fiddaman 		 * needs to be sent indicating success.
324*5c4a5fe1SAndy Fiddaman 		 */
325*5c4a5fe1SAndy Fiddaman 		switch (c->rc_cver) {
326*5c4a5fe1SAndy Fiddaman 		case RFB_CVER_3_3:
327*5c4a5fe1SAndy Fiddaman 		case RFB_CVER_3_7:
328*5c4a5fe1SAndy Fiddaman 			break;
329*5c4a5fe1SAndy Fiddaman 		case RFB_CVER_3_8:
330*5c4a5fe1SAndy Fiddaman 			rfb_send_client_status(c, 0, NULL);
331*5c4a5fe1SAndy Fiddaman 			break;
332*5c4a5fe1SAndy Fiddaman 		}
333*5c4a5fe1SAndy Fiddaman 		return (true);
334*5c4a5fe1SAndy Fiddaman 	}
335*5c4a5fe1SAndy Fiddaman 
336*5c4a5fe1SAndy Fiddaman 	/* Perform VNC authentication. */
337*5c4a5fe1SAndy Fiddaman 
338*5c4a5fe1SAndy Fiddaman #ifdef NO_OPENSSL
339*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGERR,
340*5c4a5fe1SAndy Fiddaman 	    "Auth not supported, no OpenSSL in your system");
341*5c4a5fe1SAndy Fiddaman 	rfb_send_client_status(c, 1, "Auth failed.");
342*5c4a5fe1SAndy Fiddaman 	return (false);
343*5c4a5fe1SAndy Fiddaman #else
344*5c4a5fe1SAndy Fiddaman 	unsigned char challenge[RFBP_SECURITY_VNC_AUTH_LEN];
345*5c4a5fe1SAndy Fiddaman 	unsigned char keystr[RFBP_SECURITY_VNC_PASSWD_LEN];
346*5c4a5fe1SAndy Fiddaman 	unsigned char crypt_expected[RFBP_SECURITY_VNC_AUTH_LEN];
347*5c4a5fe1SAndy Fiddaman 	DES_key_schedule ks;
348*5c4a5fe1SAndy Fiddaman 
349*5c4a5fe1SAndy Fiddaman 	/*
350*5c4a5fe1SAndy Fiddaman 	 * The client encrypts the challenge with DES, using a password
351*5c4a5fe1SAndy Fiddaman 	 * supplied by the user as the key.
352*5c4a5fe1SAndy Fiddaman 	 * To form the key, the password is truncated to eight characters, or
353*5c4a5fe1SAndy Fiddaman 	 * padded with null bytes on the right.
354*5c4a5fe1SAndy Fiddaman 	 * The client then sends the resulting 16-bytes response.
355*5c4a5fe1SAndy Fiddaman 	 */
356*5c4a5fe1SAndy Fiddaman 	(void) strncpy((char *)keystr, c->rc_s->rs_password,
357*5c4a5fe1SAndy Fiddaman 	    RFBP_SECURITY_VNC_PASSWD_LEN);
358*5c4a5fe1SAndy Fiddaman 
359*5c4a5fe1SAndy Fiddaman 	/*
360*5c4a5fe1SAndy Fiddaman 	 * VNC clients encrypt the challenge with all the bit fields in each
361*5c4a5fe1SAndy Fiddaman 	 * byte of the password mirrored.
362*5c4a5fe1SAndy Fiddaman 	 * Here we flip each byte of the keystr.
363*5c4a5fe1SAndy Fiddaman 	 */
364*5c4a5fe1SAndy Fiddaman 	for (uint_t i = 0; i < RFBP_SECURITY_VNC_PASSWD_LEN; i++) {
365*5c4a5fe1SAndy Fiddaman 		keystr[i] = (keystr[i] & 0xf0) >> 4 | (keystr[i] & 0x0f) << 4;
366*5c4a5fe1SAndy Fiddaman 		keystr[i] = (keystr[i] & 0xcc) >> 2 | (keystr[i] & 0x33) << 2;
367*5c4a5fe1SAndy Fiddaman 		keystr[i] = (keystr[i] & 0xaa) >> 1 | (keystr[i] & 0x55) << 1;
368*5c4a5fe1SAndy Fiddaman 	}
369*5c4a5fe1SAndy Fiddaman 
370*5c4a5fe1SAndy Fiddaman 	/* Initialize a 16-byte random challenge. */
371*5c4a5fe1SAndy Fiddaman 	arc4random_buf(challenge, sizeof (challenge));
372*5c4a5fe1SAndy Fiddaman 
373*5c4a5fe1SAndy Fiddaman 	/* Send the challenge to the client. */
374*5c4a5fe1SAndy Fiddaman 	if (stream_write(c->rc_fd, challenge, RFBP_SECURITY_VNC_AUTH_LEN)
375*5c4a5fe1SAndy Fiddaman 	    != RFBP_SECURITY_VNC_AUTH_LEN) {
376*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGERR,
377*5c4a5fe1SAndy Fiddaman 		    "failed to send challenge to client");
378*5c4a5fe1SAndy Fiddaman 		return (false);
379*5c4a5fe1SAndy Fiddaman 	}
380*5c4a5fe1SAndy Fiddaman 
381*5c4a5fe1SAndy Fiddaman 	/* Receive the 16-byte challenge response. */
382*5c4a5fe1SAndy Fiddaman 	if (stream_read(c->rc_fd, buf, RFBP_SECURITY_VNC_AUTH_LEN)
383*5c4a5fe1SAndy Fiddaman 	    != RFBP_SECURITY_VNC_AUTH_LEN) {
384*5c4a5fe1SAndy Fiddaman 		rfb_send_client_status(c, 1, "Challenge response read failed");
385*5c4a5fe1SAndy Fiddaman 		return (false);
386*5c4a5fe1SAndy Fiddaman 	}
387*5c4a5fe1SAndy Fiddaman 
388*5c4a5fe1SAndy Fiddaman 	memcpy(crypt_expected, challenge, RFBP_SECURITY_VNC_AUTH_LEN);
389*5c4a5fe1SAndy Fiddaman 
390*5c4a5fe1SAndy Fiddaman 	/* Encrypt the Challenge with DES. */
391*5c4a5fe1SAndy Fiddaman 	DES_set_key_unchecked((const_DES_cblock *)keystr, &ks);
392*5c4a5fe1SAndy Fiddaman 	DES_ecb_encrypt((const_DES_cblock *)challenge,
393*5c4a5fe1SAndy Fiddaman 	    (const_DES_cblock *)crypt_expected, &ks, DES_ENCRYPT);
394*5c4a5fe1SAndy Fiddaman 	DES_ecb_encrypt(
395*5c4a5fe1SAndy Fiddaman 	    (const_DES_cblock *)(challenge + RFBP_SECURITY_VNC_PASSWD_LEN),
396*5c4a5fe1SAndy Fiddaman 	    (const_DES_cblock *)(crypt_expected + RFBP_SECURITY_VNC_PASSWD_LEN),
397*5c4a5fe1SAndy Fiddaman 	    &ks, DES_ENCRYPT);
398*5c4a5fe1SAndy Fiddaman 
399*5c4a5fe1SAndy Fiddaman 	if (memcmp(crypt_expected, buf, RFBP_SECURITY_VNC_AUTH_LEN) != 0) {
400*5c4a5fe1SAndy Fiddaman 		rfb_send_client_status(c, 1, "Auth failed: Invalid password.");
401*5c4a5fe1SAndy Fiddaman 		return (false);
402*5c4a5fe1SAndy Fiddaman 	}
403*5c4a5fe1SAndy Fiddaman 
404*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "authentication succeeded");
405*5c4a5fe1SAndy Fiddaman 	rfb_send_client_status(c, 0, NULL);
406*5c4a5fe1SAndy Fiddaman #endif
407*5c4a5fe1SAndy Fiddaman 
408*5c4a5fe1SAndy Fiddaman 	return (true);
409*5c4a5fe1SAndy Fiddaman }
410*5c4a5fe1SAndy Fiddaman 
411*5c4a5fe1SAndy Fiddaman static bool
rfb_handshake_init_message(rfb_client_t * c)412*5c4a5fe1SAndy Fiddaman rfb_handshake_init_message(rfb_client_t *c)
413*5c4a5fe1SAndy Fiddaman {
414*5c4a5fe1SAndy Fiddaman 	struct bhyvegc_image *gci;
415*5c4a5fe1SAndy Fiddaman 	char buf[1];
416*5c4a5fe1SAndy Fiddaman 	char *name;
417*5c4a5fe1SAndy Fiddaman 
418*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "handshake server init");
419*5c4a5fe1SAndy Fiddaman 
420*5c4a5fe1SAndy Fiddaman 	/* Read the client init message. */
421*5c4a5fe1SAndy Fiddaman 	if (stream_read(c->rc_fd, buf, 1) != 1) {
422*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGWARN, "client did not send init");
423*5c4a5fe1SAndy Fiddaman 		return (false);
424*5c4a5fe1SAndy Fiddaman 	}
425*5c4a5fe1SAndy Fiddaman 
426*5c4a5fe1SAndy Fiddaman 	if (buf[0] == 0) {
427*5c4a5fe1SAndy Fiddaman 		rfb_client_t *oc;
428*5c4a5fe1SAndy Fiddaman 
429*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGDEBUG,
430*5c4a5fe1SAndy Fiddaman 		    "client requested exclusive access");
431*5c4a5fe1SAndy Fiddaman 
432*5c4a5fe1SAndy Fiddaman 		pthread_mutex_lock(&c->rc_s->rs_clientlock);
433*5c4a5fe1SAndy Fiddaman 		c->rc_s->rs_exclusive = true;
434*5c4a5fe1SAndy Fiddaman 		/* Disconnect all other clients. */
435*5c4a5fe1SAndy Fiddaman 		for (oc = list_head(&c->rc_s->rs_clients); oc != NULL;
436*5c4a5fe1SAndy Fiddaman 		    oc = list_next(&c->rc_s->rs_clients, oc)) {
437*5c4a5fe1SAndy Fiddaman 			if (oc != c)
438*5c4a5fe1SAndy Fiddaman 				oc->rc_closing = true;
439*5c4a5fe1SAndy Fiddaman 		}
440*5c4a5fe1SAndy Fiddaman 		pthread_mutex_unlock(&c->rc_s->rs_clientlock);
441*5c4a5fe1SAndy Fiddaman 	} else {
442*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGDEBUG, "client requested shared access");
443*5c4a5fe1SAndy Fiddaman 
444*5c4a5fe1SAndy Fiddaman 		pthread_mutex_lock(&c->rc_s->rs_clientlock);
445*5c4a5fe1SAndy Fiddaman 		if (c->rc_s->rs_exclusive) {
446*5c4a5fe1SAndy Fiddaman 			rfb_printf(c, RFB_LOGWARN,
447*5c4a5fe1SAndy Fiddaman 			    "deny due to existing exclusive session");
448*5c4a5fe1SAndy Fiddaman 			pthread_mutex_unlock(&c->rc_s->rs_clientlock);
449*5c4a5fe1SAndy Fiddaman 			return (false);
450*5c4a5fe1SAndy Fiddaman 		}
451*5c4a5fe1SAndy Fiddaman 		pthread_mutex_unlock(&c->rc_s->rs_clientlock);
452*5c4a5fe1SAndy Fiddaman 	}
453*5c4a5fe1SAndy Fiddaman 
454*5c4a5fe1SAndy Fiddaman 	gci = console_get_image();
455*5c4a5fe1SAndy Fiddaman 
456*5c4a5fe1SAndy Fiddaman 	c->rc_sinfo.rsi_width = htons(gci->width);
457*5c4a5fe1SAndy Fiddaman 	c->rc_sinfo.rsi_height = htons(gci->height);
458*5c4a5fe1SAndy Fiddaman 	c->rc_width = gci->width;
459*5c4a5fe1SAndy Fiddaman 	c->rc_height = gci->height;
460*5c4a5fe1SAndy Fiddaman 
461*5c4a5fe1SAndy Fiddaman 	if (c->rc_s->rs_name != NULL)
462*5c4a5fe1SAndy Fiddaman 		name = (char *)c->rc_s->rs_name;
463*5c4a5fe1SAndy Fiddaman 	else
464*5c4a5fe1SAndy Fiddaman 		name = "bhyve";
465*5c4a5fe1SAndy Fiddaman 
466*5c4a5fe1SAndy Fiddaman 	c->rc_sinfo.rsi_namelen = htonl(strlen(name));
467*5c4a5fe1SAndy Fiddaman 	(void) stream_write(c->rc_fd, &c->rc_sinfo, sizeof (c->rc_sinfo));
468*5c4a5fe1SAndy Fiddaman 	(void) stream_write(c->rc_fd, name, strlen(name));
469*5c4a5fe1SAndy Fiddaman 
470*5c4a5fe1SAndy Fiddaman 	return (true);
471*5c4a5fe1SAndy Fiddaman }
472*5c4a5fe1SAndy Fiddaman 
473*5c4a5fe1SAndy Fiddaman static bool
rfb_handshake(rfb_client_t * c)474*5c4a5fe1SAndy Fiddaman rfb_handshake(rfb_client_t *c)
475*5c4a5fe1SAndy Fiddaman {
476*5c4a5fe1SAndy Fiddaman 	if (!rfb_handshake_version(c))
477*5c4a5fe1SAndy Fiddaman 		return (false);
478*5c4a5fe1SAndy Fiddaman 
479*5c4a5fe1SAndy Fiddaman 	if (!rfb_handshake_auth(c))
480*5c4a5fe1SAndy Fiddaman 		return (false);
481*5c4a5fe1SAndy Fiddaman 
482*5c4a5fe1SAndy Fiddaman 	if (!rfb_handshake_init_message(c))
483*5c4a5fe1SAndy Fiddaman 		return (false);
484*5c4a5fe1SAndy Fiddaman 
485*5c4a5fe1SAndy Fiddaman 	return (true);
486*5c4a5fe1SAndy Fiddaman }
487*5c4a5fe1SAndy Fiddaman 
488*5c4a5fe1SAndy Fiddaman static void
rfb_print_pixfmt(rfb_client_t * c,rfb_pixfmt_t * px,rfb_loglevel_t level)489*5c4a5fe1SAndy Fiddaman rfb_print_pixfmt(rfb_client_t *c, rfb_pixfmt_t *px, rfb_loglevel_t level)
490*5c4a5fe1SAndy Fiddaman {
491*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, level, "%20s: %u", "bpp", px->rp_bpp);
492*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, level, "%20s: %u", "depth", px->rp_depth);
493*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, level, "%20s: %u", "bigendian", px->rp_bigendian);
494*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, level, "%20s: %u", "truecolour", px->rp_truecolour);
495*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, level, "%20s: %u", "r_max", ntohs(px->rp_r_max));
496*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, level, "%20s: %u", "g_max", ntohs(px->rp_g_max));
497*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, level, "%20s: %u", "b_max", ntohs(px->rp_b_max));
498*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, level, "%20s: %u", "r_shift", px->rp_r_shift);
499*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, level, "%20s: %u", "g_shift", px->rp_g_shift);
500*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, level, "%20s: %u", "b_shift", px->rp_b_shift);
501*5c4a5fe1SAndy Fiddaman }
502*5c4a5fe1SAndy Fiddaman 
503*5c4a5fe1SAndy Fiddaman static bool
rfb_recv_set_pixel_format(rfb_client_t * c)504*5c4a5fe1SAndy Fiddaman rfb_recv_set_pixel_format(rfb_client_t *c)
505*5c4a5fe1SAndy Fiddaman {
506*5c4a5fe1SAndy Fiddaman 	rfb_cs_pixfmt_msg_t msg;
507*5c4a5fe1SAndy Fiddaman 	rfb_pixfmt_t *newpx = &msg.rp_pixfmt;
508*5c4a5fe1SAndy Fiddaman 	rfb_pixfmt_t *oldpx = &c->rc_sinfo.rsi_pixfmt;
509*5c4a5fe1SAndy Fiddaman 	rfb_pixfmt_t *spx = &c->rc_s->rs_pixfmt;
510*5c4a5fe1SAndy Fiddaman 
511*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "received pixel format");
512*5c4a5fe1SAndy Fiddaman 
513*5c4a5fe1SAndy Fiddaman 	if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
514*5c4a5fe1SAndy Fiddaman 		return (false);
515*5c4a5fe1SAndy Fiddaman 
516*5c4a5fe1SAndy Fiddaman 	/*
517*5c4a5fe1SAndy Fiddaman 	 * The client has sent its desired pixel format. The protocol does not
518*5c4a5fe1SAndy Fiddaman 	 * have a mechanism to reject this, we are supposed to just start using
519*5c4a5fe1SAndy Fiddaman 	 * the requested format from the next update.
520*5c4a5fe1SAndy Fiddaman 	 *
521*5c4a5fe1SAndy Fiddaman 	 * At present, we can only support alternative rgb-shift values and
522*5c4a5fe1SAndy Fiddaman 	 * will accept (and ignore) a new depth value.
523*5c4a5fe1SAndy Fiddaman 	 */
524*5c4a5fe1SAndy Fiddaman 
525*5c4a5fe1SAndy Fiddaman 	if (oldpx->rp_bpp != newpx->rp_bpp ||
526*5c4a5fe1SAndy Fiddaman 	    oldpx->rp_bigendian != newpx->rp_bigendian ||
527*5c4a5fe1SAndy Fiddaman 	    oldpx->rp_truecolour != newpx->rp_truecolour ||
528*5c4a5fe1SAndy Fiddaman 	    oldpx->rp_r_max != newpx->rp_r_max ||
529*5c4a5fe1SAndy Fiddaman 	    oldpx->rp_g_max != newpx->rp_g_max ||
530*5c4a5fe1SAndy Fiddaman 	    oldpx->rp_b_max != newpx->rp_b_max) {
531*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGWARN, "unsupported pixfmt from client");
532*5c4a5fe1SAndy Fiddaman 		rfb_print_pixfmt(c, newpx, RFB_LOGWARN);
533*5c4a5fe1SAndy Fiddaman 		return (false);
534*5c4a5fe1SAndy Fiddaman 	}
535*5c4a5fe1SAndy Fiddaman 
536*5c4a5fe1SAndy Fiddaman 	rfb_print_pixfmt(c, newpx, RFB_LOGDEBUG);
537*5c4a5fe1SAndy Fiddaman 
538*5c4a5fe1SAndy Fiddaman 	/* Check if the new shifts match the server's native values. */
539*5c4a5fe1SAndy Fiddaman 	if (newpx->rp_r_shift != spx->rp_r_shift ||
540*5c4a5fe1SAndy Fiddaman 	    newpx->rp_g_shift != spx->rp_g_shift ||
541*5c4a5fe1SAndy Fiddaman 	    newpx->rp_b_shift != spx->rp_b_shift) {
542*5c4a5fe1SAndy Fiddaman 		c->rc_custom_pixfmt = true;
543*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGDEBUG, "Using custom pixfmt");
544*5c4a5fe1SAndy Fiddaman 	} else {
545*5c4a5fe1SAndy Fiddaman 		c->rc_custom_pixfmt = false;
546*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGDEBUG, "Using native pixfmt");
547*5c4a5fe1SAndy Fiddaman 	}
548*5c4a5fe1SAndy Fiddaman 
549*5c4a5fe1SAndy Fiddaman 	c->rc_sinfo.rsi_pixfmt = msg.rp_pixfmt;
550*5c4a5fe1SAndy Fiddaman 	c->rc_crc_reset = true;
551*5c4a5fe1SAndy Fiddaman 
552*5c4a5fe1SAndy Fiddaman 	return (true);
553*5c4a5fe1SAndy Fiddaman }
554*5c4a5fe1SAndy Fiddaman 
555*5c4a5fe1SAndy Fiddaman static bool
rfb_recv_set_encodings(rfb_client_t * c)556*5c4a5fe1SAndy Fiddaman rfb_recv_set_encodings(rfb_client_t *c)
557*5c4a5fe1SAndy Fiddaman {
558*5c4a5fe1SAndy Fiddaman 	rfb_cs_encodings_msg_t msg;
559*5c4a5fe1SAndy Fiddaman 
560*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "received encodings");
561*5c4a5fe1SAndy Fiddaman 
562*5c4a5fe1SAndy Fiddaman 	if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
563*5c4a5fe1SAndy Fiddaman 		return (false);
564*5c4a5fe1SAndy Fiddaman 
565*5c4a5fe1SAndy Fiddaman 	msg.re_numencs = htons(msg.re_numencs);
566*5c4a5fe1SAndy Fiddaman 
567*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "%d values", msg.re_numencs);
568*5c4a5fe1SAndy Fiddaman 
569*5c4a5fe1SAndy Fiddaman 	for (uint_t i = 0; i < msg.re_numencs; i++) {
570*5c4a5fe1SAndy Fiddaman 		uint32_t enc;
571*5c4a5fe1SAndy Fiddaman 
572*5c4a5fe1SAndy Fiddaman 		if (stream_read(c->rc_fd, &enc, sizeof (enc)) != sizeof (enc))
573*5c4a5fe1SAndy Fiddaman 			return (false);
574*5c4a5fe1SAndy Fiddaman 
575*5c4a5fe1SAndy Fiddaman 		enc = htonl(enc);
576*5c4a5fe1SAndy Fiddaman 
577*5c4a5fe1SAndy Fiddaman 		switch (enc) {
578*5c4a5fe1SAndy Fiddaman 		case RFBP_ENCODING_RAW:
579*5c4a5fe1SAndy Fiddaman 			rfb_printf(c, RFB_LOGDEBUG,
580*5c4a5fe1SAndy Fiddaman 			    "client supports raw encoding");
581*5c4a5fe1SAndy Fiddaman 			c->rc_encodings |= RFB_ENCODING_RAW;
582*5c4a5fe1SAndy Fiddaman 			break;
583*5c4a5fe1SAndy Fiddaman 		case RFBP_ENCODING_ZLIB:
584*5c4a5fe1SAndy Fiddaman 			rfb_printf(c, RFB_LOGDEBUG,
585*5c4a5fe1SAndy Fiddaman 			    "client supports zlib encoding");
586*5c4a5fe1SAndy Fiddaman 			if (!(c->rc_encodings & RFB_ENCODING_ZLIB)) {
587*5c4a5fe1SAndy Fiddaman 				if (deflateInit(&c->rc_zstream, Z_BEST_SPEED)
588*5c4a5fe1SAndy Fiddaman 				    != Z_OK) {
589*5c4a5fe1SAndy Fiddaman 					return (false);
590*5c4a5fe1SAndy Fiddaman 				}
591*5c4a5fe1SAndy Fiddaman 				c->rc_encodings |= RFB_ENCODING_ZLIB;
592*5c4a5fe1SAndy Fiddaman 			}
593*5c4a5fe1SAndy Fiddaman 			break;
594*5c4a5fe1SAndy Fiddaman 		case RFBP_ENCODING_RESIZE:
595*5c4a5fe1SAndy Fiddaman 			rfb_printf(c, RFB_LOGDEBUG, "client supports resize");
596*5c4a5fe1SAndy Fiddaman 			c->rc_encodings |= RFB_ENCODING_RESIZE;
597*5c4a5fe1SAndy Fiddaman 			break;
598*5c4a5fe1SAndy Fiddaman 		case RFBP_ENCODING_EXT_KEVENT:
599*5c4a5fe1SAndy Fiddaman 			rfb_printf(c, RFB_LOGDEBUG,
600*5c4a5fe1SAndy Fiddaman 			    "client supports ext key event");
601*5c4a5fe1SAndy Fiddaman 			c->rc_encodings |= RFB_ENCODING_EXT_KEVENT;
602*5c4a5fe1SAndy Fiddaman 			break;
603*5c4a5fe1SAndy Fiddaman 		case RFBP_ENCODING_DESKTOP_NAME:
604*5c4a5fe1SAndy Fiddaman 			rfb_printf(c, RFB_LOGDEBUG,
605*5c4a5fe1SAndy Fiddaman 			    "client supports desktop name");
606*5c4a5fe1SAndy Fiddaman 			c->rc_encodings |= RFB_ENCODING_DESKTOP_NAME;
607*5c4a5fe1SAndy Fiddaman 			break;
608*5c4a5fe1SAndy Fiddaman 		default:
609*5c4a5fe1SAndy Fiddaman 			rfb_printf(c, RFB_LOGDEBUG,
610*5c4a5fe1SAndy Fiddaman 			    "client supports encoding %d", (int32_t)enc);
611*5c4a5fe1SAndy Fiddaman 		}
612*5c4a5fe1SAndy Fiddaman 	}
613*5c4a5fe1SAndy Fiddaman 
614*5c4a5fe1SAndy Fiddaman 	return (true);
615*5c4a5fe1SAndy Fiddaman }
616*5c4a5fe1SAndy Fiddaman 
617*5c4a5fe1SAndy Fiddaman static bool
rfb_recv_update(rfb_client_t * c)618*5c4a5fe1SAndy Fiddaman rfb_recv_update(rfb_client_t *c)
619*5c4a5fe1SAndy Fiddaman {
620*5c4a5fe1SAndy Fiddaman 	rfb_cs_update_msg_t msg;
621*5c4a5fe1SAndy Fiddaman 
622*5c4a5fe1SAndy Fiddaman 	if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
623*5c4a5fe1SAndy Fiddaman 		return (false);
624*5c4a5fe1SAndy Fiddaman 
625*5c4a5fe1SAndy Fiddaman 	if (!c->rc_keyevent_sent &&
626*5c4a5fe1SAndy Fiddaman 	    (c->rc_encodings & RFB_ENCODING_EXT_KEVENT)) {
627*5c4a5fe1SAndy Fiddaman 		/*
628*5c4a5fe1SAndy Fiddaman 		 * Take this opportunity to tell the client that we
629*5c4a5fe1SAndy Fiddaman 		 * accept QEMU Extended Key Event Pseudo-encoding.
630*5c4a5fe1SAndy Fiddaman 		 */
631*5c4a5fe1SAndy Fiddaman 		c->rc_keyevent_sent = true;
632*5c4a5fe1SAndy Fiddaman 		rfb_send_extended_keyevent_update_msg(c);
633*5c4a5fe1SAndy Fiddaman 	}
634*5c4a5fe1SAndy Fiddaman 
635*5c4a5fe1SAndy Fiddaman 	c->rc_pending = true;
636*5c4a5fe1SAndy Fiddaman 	if (msg.rum_incremental == 0) {
637*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGDEBUG,
638*5c4a5fe1SAndy Fiddaman 		    "client requested full screen update");
639*5c4a5fe1SAndy Fiddaman 		c->rc_send_fullscreen = true;
640*5c4a5fe1SAndy Fiddaman 	}
641*5c4a5fe1SAndy Fiddaman 
642*5c4a5fe1SAndy Fiddaman 	return (true);
643*5c4a5fe1SAndy Fiddaman }
644*5c4a5fe1SAndy Fiddaman 
645*5c4a5fe1SAndy Fiddaman static bool
rfb_recv_key_event(rfb_client_t * c)646*5c4a5fe1SAndy Fiddaman rfb_recv_key_event(rfb_client_t *c)
647*5c4a5fe1SAndy Fiddaman {
648*5c4a5fe1SAndy Fiddaman 	rfb_cs_key_event_msg_t msg;
649*5c4a5fe1SAndy Fiddaman 
650*5c4a5fe1SAndy Fiddaman 	if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
651*5c4a5fe1SAndy Fiddaman 		return (false);
652*5c4a5fe1SAndy Fiddaman 
653*5c4a5fe1SAndy Fiddaman 	msg.rke_sym = htonl(msg.rke_sym);
654*5c4a5fe1SAndy Fiddaman 
655*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "received key %s %x",
656*5c4a5fe1SAndy Fiddaman 	    msg.rke_down == 0 ? "up" : "down", msg.rke_sym);
657*5c4a5fe1SAndy Fiddaman 
658*5c4a5fe1SAndy Fiddaman 	console_key_event(msg.rke_down, msg.rke_sym, htonl(0));
659*5c4a5fe1SAndy Fiddaman 	c->rc_input_detected = true;
660*5c4a5fe1SAndy Fiddaman 
661*5c4a5fe1SAndy Fiddaman 	return (true);
662*5c4a5fe1SAndy Fiddaman }
663*5c4a5fe1SAndy Fiddaman 
664*5c4a5fe1SAndy Fiddaman static bool
rfb_recv_pointer_event(rfb_client_t * c)665*5c4a5fe1SAndy Fiddaman rfb_recv_pointer_event(rfb_client_t *c)
666*5c4a5fe1SAndy Fiddaman {
667*5c4a5fe1SAndy Fiddaman 	rfb_cs_pointer_event_msg_t msg;
668*5c4a5fe1SAndy Fiddaman 
669*5c4a5fe1SAndy Fiddaman 	if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
670*5c4a5fe1SAndy Fiddaman 		return (false);
671*5c4a5fe1SAndy Fiddaman 
672*5c4a5fe1SAndy Fiddaman 	msg.rpe_x = htons(msg.rpe_x);
673*5c4a5fe1SAndy Fiddaman 	msg.rpe_y = htons(msg.rpe_y);
674*5c4a5fe1SAndy Fiddaman 
675*5c4a5fe1SAndy Fiddaman 	if (rfb_debug > 1) {
676*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGDEBUG, "received pointer event @ %dx%d",
677*5c4a5fe1SAndy Fiddaman 		    msg.rpe_x, msg.rpe_y);
678*5c4a5fe1SAndy Fiddaman 	}
679*5c4a5fe1SAndy Fiddaman 
680*5c4a5fe1SAndy Fiddaman 	console_ptr_event(msg.rpe_button, msg.rpe_x, msg.rpe_y);
681*5c4a5fe1SAndy Fiddaman 	c->rc_input_detected = true;
682*5c4a5fe1SAndy Fiddaman 
683*5c4a5fe1SAndy Fiddaman 	return (true);
684*5c4a5fe1SAndy Fiddaman }
685*5c4a5fe1SAndy Fiddaman 
686*5c4a5fe1SAndy Fiddaman static bool
rfb_recv_cut_text(rfb_client_t * c)687*5c4a5fe1SAndy Fiddaman rfb_recv_cut_text(rfb_client_t *c)
688*5c4a5fe1SAndy Fiddaman {
689*5c4a5fe1SAndy Fiddaman 	rfb_cs_cut_text_msg_t msg;
690*5c4a5fe1SAndy Fiddaman 	unsigned char buf[32];
691*5c4a5fe1SAndy Fiddaman 
692*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "received cut text event");
693*5c4a5fe1SAndy Fiddaman 
694*5c4a5fe1SAndy Fiddaman 	if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
695*5c4a5fe1SAndy Fiddaman 		return (false);
696*5c4a5fe1SAndy Fiddaman 
697*5c4a5fe1SAndy Fiddaman 	msg.rct_length = htonl(msg.rct_length);
698*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "%u bytes in buffer", msg.rct_length);
699*5c4a5fe1SAndy Fiddaman 	/* Consume the buffer */
700*5c4a5fe1SAndy Fiddaman 	while (msg.rct_length > 0) {
701*5c4a5fe1SAndy Fiddaman 		ssize_t l;
702*5c4a5fe1SAndy Fiddaman 
703*5c4a5fe1SAndy Fiddaman 		l = stream_read(c->rc_fd, buf,
704*5c4a5fe1SAndy Fiddaman 		    MIN(sizeof (buf), msg.rct_length));
705*5c4a5fe1SAndy Fiddaman 		if (l <= 0)
706*5c4a5fe1SAndy Fiddaman 			return (false);
707*5c4a5fe1SAndy Fiddaman 		msg.rct_length -= l;
708*5c4a5fe1SAndy Fiddaman 	}
709*5c4a5fe1SAndy Fiddaman 
710*5c4a5fe1SAndy Fiddaman 	return (true);
711*5c4a5fe1SAndy Fiddaman }
712*5c4a5fe1SAndy Fiddaman 
713*5c4a5fe1SAndy Fiddaman static bool
rfb_recv_qemu(rfb_client_t * c)714*5c4a5fe1SAndy Fiddaman rfb_recv_qemu(rfb_client_t *c)
715*5c4a5fe1SAndy Fiddaman {
716*5c4a5fe1SAndy Fiddaman 	rfb_cs_qemu_msg_t msg;
717*5c4a5fe1SAndy Fiddaman 
718*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "received QEMU event");
719*5c4a5fe1SAndy Fiddaman 
720*5c4a5fe1SAndy Fiddaman 	if (stream_read(c->rc_fd, &msg, sizeof (msg)) != sizeof (msg))
721*5c4a5fe1SAndy Fiddaman 		return (false);
722*5c4a5fe1SAndy Fiddaman 
723*5c4a5fe1SAndy Fiddaman 	switch (msg.rq_subtype) {
724*5c4a5fe1SAndy Fiddaman 	case RFBP_CS_QEMU_KEVENT: {
725*5c4a5fe1SAndy Fiddaman 		rfb_cs_qemu_extended_key_msg_t keymsg;
726*5c4a5fe1SAndy Fiddaman 
727*5c4a5fe1SAndy Fiddaman 		if (stream_read(c->rc_fd, &keymsg, sizeof (keymsg)) !=
728*5c4a5fe1SAndy Fiddaman 		    sizeof (keymsg)) {
729*5c4a5fe1SAndy Fiddaman 			return (false);
730*5c4a5fe1SAndy Fiddaman 		}
731*5c4a5fe1SAndy Fiddaman 
732*5c4a5fe1SAndy Fiddaman 		keymsg.rqek_sym = htonl(keymsg.rqek_sym);
733*5c4a5fe1SAndy Fiddaman 		keymsg.rqek_code = htonl(keymsg.rqek_code);
734*5c4a5fe1SAndy Fiddaman 
735*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGDEBUG, "QEMU key %s %x / %x",
736*5c4a5fe1SAndy Fiddaman 		    keymsg.rqek_down == 0 ? "up" : "down",
737*5c4a5fe1SAndy Fiddaman 		    keymsg.rqek_sym, keymsg.rqek_code);
738*5c4a5fe1SAndy Fiddaman 
739*5c4a5fe1SAndy Fiddaman 		console_key_event((int)keymsg.rqek_down, keymsg.rqek_sym,
740*5c4a5fe1SAndy Fiddaman 		    keymsg.rqek_code);
741*5c4a5fe1SAndy Fiddaman 		c->rc_input_detected = true;
742*5c4a5fe1SAndy Fiddaman 		break;
743*5c4a5fe1SAndy Fiddaman 	}
744*5c4a5fe1SAndy Fiddaman 	default:
745*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGWARN, "Unknown QEMU event subtype: %d\n",
746*5c4a5fe1SAndy Fiddaman 		    msg.rq_subtype);
747*5c4a5fe1SAndy Fiddaman 		return (false);
748*5c4a5fe1SAndy Fiddaman 	}
749*5c4a5fe1SAndy Fiddaman 
750*5c4a5fe1SAndy Fiddaman 	return (true);
751*5c4a5fe1SAndy Fiddaman }
752*5c4a5fe1SAndy Fiddaman 
753*5c4a5fe1SAndy Fiddaman static bool
rfb_send_update_header(rfb_client_t * c,int numrects)754*5c4a5fe1SAndy Fiddaman rfb_send_update_header(rfb_client_t *c, int numrects)
755*5c4a5fe1SAndy Fiddaman {
756*5c4a5fe1SAndy Fiddaman 	rfb_server_update_msg_t msg;
757*5c4a5fe1SAndy Fiddaman 
758*5c4a5fe1SAndy Fiddaman 	msg.rss_type = RFBP_SC_UPDATE;
759*5c4a5fe1SAndy Fiddaman 	msg.rss_pad = 0;
760*5c4a5fe1SAndy Fiddaman 	msg.rss_numrects = htons(numrects);
761*5c4a5fe1SAndy Fiddaman 
762*5c4a5fe1SAndy Fiddaman 	return (stream_write(c->rc_fd, &msg, sizeof (msg)) == sizeof (msg));
763*5c4a5fe1SAndy Fiddaman }
764*5c4a5fe1SAndy Fiddaman 
765*5c4a5fe1SAndy Fiddaman static void
rfb_send_resize_update_msg(rfb_client_t * c)766*5c4a5fe1SAndy Fiddaman rfb_send_resize_update_msg(rfb_client_t *c)
767*5c4a5fe1SAndy Fiddaman {
768*5c4a5fe1SAndy Fiddaman 	rfb_rect_hdr_t rect;
769*5c4a5fe1SAndy Fiddaman 
770*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "sending screen resize %dx%d",
771*5c4a5fe1SAndy Fiddaman 	    c->rc_width, c->rc_height);
772*5c4a5fe1SAndy Fiddaman 
773*5c4a5fe1SAndy Fiddaman 	(void) rfb_send_update_header(c, 1);
774*5c4a5fe1SAndy Fiddaman 
775*5c4a5fe1SAndy Fiddaman 	rect.rr_x = htons(0);
776*5c4a5fe1SAndy Fiddaman 	rect.rr_y = htons(0);
777*5c4a5fe1SAndy Fiddaman 	rect.rr_width = htons(c->rc_width);
778*5c4a5fe1SAndy Fiddaman 	rect.rr_height = htons(c->rc_height);
779*5c4a5fe1SAndy Fiddaman 	rect.rr_encoding = htonl(RFBP_ENCODING_RESIZE);
780*5c4a5fe1SAndy Fiddaman 
781*5c4a5fe1SAndy Fiddaman 	(void) stream_write(c->rc_fd, &rect, sizeof (rect));
782*5c4a5fe1SAndy Fiddaman }
783*5c4a5fe1SAndy Fiddaman 
784*5c4a5fe1SAndy Fiddaman static void
rfb_send_extended_keyevent_update_msg(rfb_client_t * c)785*5c4a5fe1SAndy Fiddaman rfb_send_extended_keyevent_update_msg(rfb_client_t *c)
786*5c4a5fe1SAndy Fiddaman {
787*5c4a5fe1SAndy Fiddaman 	rfb_rect_hdr_t rect;
788*5c4a5fe1SAndy Fiddaman 
789*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "sending extended keyevent update message");
790*5c4a5fe1SAndy Fiddaman 
791*5c4a5fe1SAndy Fiddaman 	(void) rfb_send_update_header(c, 1);
792*5c4a5fe1SAndy Fiddaman 
793*5c4a5fe1SAndy Fiddaman 	rect.rr_x = htons(0);
794*5c4a5fe1SAndy Fiddaman 	rect.rr_y = htons(0);
795*5c4a5fe1SAndy Fiddaman 	rect.rr_width = htons(c->rc_width);
796*5c4a5fe1SAndy Fiddaman 	rect.rr_height = htons(c->rc_height);
797*5c4a5fe1SAndy Fiddaman 	rect.rr_encoding = htonl(RFBP_ENCODING_EXT_KEVENT);
798*5c4a5fe1SAndy Fiddaman 
799*5c4a5fe1SAndy Fiddaman 	(void) stream_write(c->rc_fd, &rect, sizeof (rect));
800*5c4a5fe1SAndy Fiddaman }
801*5c4a5fe1SAndy Fiddaman 
802*5c4a5fe1SAndy Fiddaman static void
translate_pixels(rfb_client_t * c,struct bhyvegc_image * gci,int x1,int y1,int x2,int y2)803*5c4a5fe1SAndy Fiddaman translate_pixels(rfb_client_t *c, struct bhyvegc_image *gci,
804*5c4a5fe1SAndy Fiddaman     int x1, int y1, int x2, int y2)
805*5c4a5fe1SAndy Fiddaman {
806*5c4a5fe1SAndy Fiddaman 	rfb_pixfmt_t *px = &c->rc_sinfo.rsi_pixfmt;
807*5c4a5fe1SAndy Fiddaman 	rfb_pixfmt_t *spx = &c->rc_s->rs_pixfmt;
808*5c4a5fe1SAndy Fiddaman 	int w, h;
809*5c4a5fe1SAndy Fiddaman 
810*5c4a5fe1SAndy Fiddaman 	w = gci->width;
811*5c4a5fe1SAndy Fiddaman 	h = gci->height;
812*5c4a5fe1SAndy Fiddaman 	VERIFY3S(gci->width, ==, c->rc_gci.width);
813*5c4a5fe1SAndy Fiddaman 	VERIFY3S(gci->height, ==, c->rc_gci.height);
814*5c4a5fe1SAndy Fiddaman 
815*5c4a5fe1SAndy Fiddaman 	for (uint_t y = y1; y < h && y < y2; y++) {
816*5c4a5fe1SAndy Fiddaman 		for (uint_t x = x1; x < w && x < x2; x++) {
817*5c4a5fe1SAndy Fiddaman 			uint32_t p;
818*5c4a5fe1SAndy Fiddaman 
819*5c4a5fe1SAndy Fiddaman 			p = gci->data[y * w + x];
820*5c4a5fe1SAndy Fiddaman 			c->rc_gci.data[y * w + x] =
821*5c4a5fe1SAndy Fiddaman 			    0xff000000 |
822*5c4a5fe1SAndy Fiddaman 			    ((p >> spx->rp_r_shift) & 0xff) << px->rp_r_shift |
823*5c4a5fe1SAndy Fiddaman 			    ((p >> spx->rp_g_shift) & 0xff) << px->rp_g_shift |
824*5c4a5fe1SAndy Fiddaman 			    ((p >> spx->rp_b_shift) & 0xff) << px->rp_b_shift;
825*5c4a5fe1SAndy Fiddaman 		}
826*5c4a5fe1SAndy Fiddaman 	}
827*5c4a5fe1SAndy Fiddaman }
828*5c4a5fe1SAndy Fiddaman 
829*5c4a5fe1SAndy Fiddaman static bool
rfb_send_rect(rfb_client_t * c,struct bhyvegc_image * gci,int x,int y,int w,int h)830*5c4a5fe1SAndy Fiddaman rfb_send_rect(rfb_client_t *c, struct bhyvegc_image *gci,
831*5c4a5fe1SAndy Fiddaman     int x, int y, int w, int h)
832*5c4a5fe1SAndy Fiddaman {
833*5c4a5fe1SAndy Fiddaman 	rfb_rect_hdr_t rect;
834*5c4a5fe1SAndy Fiddaman 	unsigned long zlen;
835*5c4a5fe1SAndy Fiddaman 	ssize_t nwrite, total;
836*5c4a5fe1SAndy Fiddaman 	int err;
837*5c4a5fe1SAndy Fiddaman 	uint32_t *p;
838*5c4a5fe1SAndy Fiddaman 	uint8_t *zbufp;
839*5c4a5fe1SAndy Fiddaman 
840*5c4a5fe1SAndy Fiddaman 	if (rfb_debug > 1) {
841*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGDEBUG, "send rect %dx%d %dx%d",
842*5c4a5fe1SAndy Fiddaman 		    x, y, w, h);
843*5c4a5fe1SAndy Fiddaman 	}
844*5c4a5fe1SAndy Fiddaman 
845*5c4a5fe1SAndy Fiddaman 	/* Rectangle header. */
846*5c4a5fe1SAndy Fiddaman 	rect.rr_x = htons(x);
847*5c4a5fe1SAndy Fiddaman 	rect.rr_y = htons(y);
848*5c4a5fe1SAndy Fiddaman 	rect.rr_width = htons(w);
849*5c4a5fe1SAndy Fiddaman 	rect.rr_height = htons(h);
850*5c4a5fe1SAndy Fiddaman 
851*5c4a5fe1SAndy Fiddaman 	uint32_t *data = gci->data;
852*5c4a5fe1SAndy Fiddaman 	if (c->rc_custom_pixfmt) {
853*5c4a5fe1SAndy Fiddaman 		translate_pixels(c, gci, x, y, x + w, y + h);
854*5c4a5fe1SAndy Fiddaman 		data = c->rc_gci.data;
855*5c4a5fe1SAndy Fiddaman 	}
856*5c4a5fe1SAndy Fiddaman 
857*5c4a5fe1SAndy Fiddaman 	h = y + h;
858*5c4a5fe1SAndy Fiddaman 	w *= sizeof (uint32_t);
859*5c4a5fe1SAndy Fiddaman 
860*5c4a5fe1SAndy Fiddaman 	if (c->rc_encodings & RFB_ENCODING_ZLIB) {
861*5c4a5fe1SAndy Fiddaman 		zbufp = c->rc_zbuf;
862*5c4a5fe1SAndy Fiddaman 		c->rc_zstream.total_in = 0;
863*5c4a5fe1SAndy Fiddaman 		c->rc_zstream.total_out = 0;
864*5c4a5fe1SAndy Fiddaman 		for (p = &data[y * gci->width + x]; y < h; y++) {
865*5c4a5fe1SAndy Fiddaman 			c->rc_zstream.next_in = (Bytef *)p;
866*5c4a5fe1SAndy Fiddaman 			c->rc_zstream.avail_in = w;
867*5c4a5fe1SAndy Fiddaman 			c->rc_zstream.next_out = (Bytef *)zbufp;
868*5c4a5fe1SAndy Fiddaman 			c->rc_zstream.avail_out = RFB_ZLIB_BUFSZ + 16 -
869*5c4a5fe1SAndy Fiddaman 			    c->rc_zstream.total_out;
870*5c4a5fe1SAndy Fiddaman 			c->rc_zstream.data_type = Z_BINARY;
871*5c4a5fe1SAndy Fiddaman 
872*5c4a5fe1SAndy Fiddaman 			/* Compress with zlib. */
873*5c4a5fe1SAndy Fiddaman 			err = deflate(&c->rc_zstream, Z_SYNC_FLUSH);
874*5c4a5fe1SAndy Fiddaman 			if (err != Z_OK) {
875*5c4a5fe1SAndy Fiddaman 				rfb_printf(c, RFB_LOGWARN,
876*5c4a5fe1SAndy Fiddaman 				    "zlib[rect] deflate err: %d", err);
877*5c4a5fe1SAndy Fiddaman 				goto doraw;
878*5c4a5fe1SAndy Fiddaman 			}
879*5c4a5fe1SAndy Fiddaman 			zbufp = c->rc_zbuf + c->rc_zstream.total_out;
880*5c4a5fe1SAndy Fiddaman 			p += gci->width;
881*5c4a5fe1SAndy Fiddaman 		}
882*5c4a5fe1SAndy Fiddaman 		rect.rr_encoding = htonl(RFBP_ENCODING_ZLIB);
883*5c4a5fe1SAndy Fiddaman 		nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
884*5c4a5fe1SAndy Fiddaman 		if (nwrite <= 0)
885*5c4a5fe1SAndy Fiddaman 			return (false);
886*5c4a5fe1SAndy Fiddaman 
887*5c4a5fe1SAndy Fiddaman 		zlen = htonl(c->rc_zstream.total_out);
888*5c4a5fe1SAndy Fiddaman 		nwrite = stream_write(c->rc_fd, &zlen, sizeof (uint32_t));
889*5c4a5fe1SAndy Fiddaman 		if (nwrite <= 0)
890*5c4a5fe1SAndy Fiddaman 			return (false);
891*5c4a5fe1SAndy Fiddaman 		return (stream_write(c->rc_fd, c->rc_zbuf,
892*5c4a5fe1SAndy Fiddaman 		    c->rc_zstream.total_out) == c->rc_zstream.total_out);
893*5c4a5fe1SAndy Fiddaman 	}
894*5c4a5fe1SAndy Fiddaman 
895*5c4a5fe1SAndy Fiddaman doraw:
896*5c4a5fe1SAndy Fiddaman 
897*5c4a5fe1SAndy Fiddaman 	total = 0;
898*5c4a5fe1SAndy Fiddaman 	zbufp = c->rc_zbuf;
899*5c4a5fe1SAndy Fiddaman 	for (p = &data[y * gci->width + x]; y < h; y++) {
900*5c4a5fe1SAndy Fiddaman 		memcpy(zbufp, p, w);
901*5c4a5fe1SAndy Fiddaman 		zbufp += w;
902*5c4a5fe1SAndy Fiddaman 		total += w;
903*5c4a5fe1SAndy Fiddaman 		p += gci->width;
904*5c4a5fe1SAndy Fiddaman 	}
905*5c4a5fe1SAndy Fiddaman 
906*5c4a5fe1SAndy Fiddaman 	rect.rr_encoding = htonl(RFBP_ENCODING_RAW);
907*5c4a5fe1SAndy Fiddaman 	nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
908*5c4a5fe1SAndy Fiddaman 	if (nwrite <= 0)
909*5c4a5fe1SAndy Fiddaman 		return (false);
910*5c4a5fe1SAndy Fiddaman 
911*5c4a5fe1SAndy Fiddaman 	return (stream_write(c->rc_fd, c->rc_zbuf, total) == total);
912*5c4a5fe1SAndy Fiddaman }
913*5c4a5fe1SAndy Fiddaman 
914*5c4a5fe1SAndy Fiddaman 
915*5c4a5fe1SAndy Fiddaman static bool
rfb_send_all(rfb_client_t * c,struct bhyvegc_image * gci)916*5c4a5fe1SAndy Fiddaman rfb_send_all(rfb_client_t *c, struct bhyvegc_image *gci)
917*5c4a5fe1SAndy Fiddaman {
918*5c4a5fe1SAndy Fiddaman 	rfb_rect_hdr_t rect;
919*5c4a5fe1SAndy Fiddaman 	ssize_t nwrite;
920*5c4a5fe1SAndy Fiddaman 	unsigned long zlen;
921*5c4a5fe1SAndy Fiddaman 	int err;
922*5c4a5fe1SAndy Fiddaman 
923*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "send entire screen");
924*5c4a5fe1SAndy Fiddaman 
925*5c4a5fe1SAndy Fiddaman 	/* Just the one (big) rect. */
926*5c4a5fe1SAndy Fiddaman 	if (!rfb_send_update_header(c, 1))
927*5c4a5fe1SAndy Fiddaman 		return (false);
928*5c4a5fe1SAndy Fiddaman 
929*5c4a5fe1SAndy Fiddaman 	rect.rr_x = 0;
930*5c4a5fe1SAndy Fiddaman 	rect.rr_y = 0;
931*5c4a5fe1SAndy Fiddaman 	rect.rr_width = htons(gci->width);
932*5c4a5fe1SAndy Fiddaman 	rect.rr_height = htons(gci->height);
933*5c4a5fe1SAndy Fiddaman 
934*5c4a5fe1SAndy Fiddaman 	uint32_t *data = gci->data;
935*5c4a5fe1SAndy Fiddaman 	if (c->rc_custom_pixfmt) {
936*5c4a5fe1SAndy Fiddaman 		translate_pixels(c, gci, 0, 0, gci->width, gci->height);
937*5c4a5fe1SAndy Fiddaman 		data = c->rc_gci.data;
938*5c4a5fe1SAndy Fiddaman 	}
939*5c4a5fe1SAndy Fiddaman 
940*5c4a5fe1SAndy Fiddaman 	if (c->rc_encodings & RFB_ENCODING_ZLIB) {
941*5c4a5fe1SAndy Fiddaman 		c->rc_zstream.next_in = (Bytef *)data;
942*5c4a5fe1SAndy Fiddaman 		c->rc_zstream.avail_in = gci->width * gci->height *
943*5c4a5fe1SAndy Fiddaman 		    sizeof (uint32_t);
944*5c4a5fe1SAndy Fiddaman 		c->rc_zstream.next_out = (Bytef *)c->rc_zbuf;
945*5c4a5fe1SAndy Fiddaman 		c->rc_zstream.avail_out = RFB_ZLIB_BUFSZ + 16;
946*5c4a5fe1SAndy Fiddaman 		c->rc_zstream.data_type = Z_BINARY;
947*5c4a5fe1SAndy Fiddaman 
948*5c4a5fe1SAndy Fiddaman 		c->rc_zstream.total_in = 0;
949*5c4a5fe1SAndy Fiddaman 		c->rc_zstream.total_out = 0;
950*5c4a5fe1SAndy Fiddaman 
951*5c4a5fe1SAndy Fiddaman 		/* Compress with zlib. */
952*5c4a5fe1SAndy Fiddaman 		err = deflate(&c->rc_zstream, Z_SYNC_FLUSH);
953*5c4a5fe1SAndy Fiddaman 		if (err != Z_OK) {
954*5c4a5fe1SAndy Fiddaman 			rfb_printf(c, RFB_LOGWARN, "zlib deflate err: %d", err);
955*5c4a5fe1SAndy Fiddaman 			goto doraw;
956*5c4a5fe1SAndy Fiddaman 		}
957*5c4a5fe1SAndy Fiddaman 
958*5c4a5fe1SAndy Fiddaman 		rect.rr_encoding = htonl(RFBP_ENCODING_ZLIB);
959*5c4a5fe1SAndy Fiddaman 		nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
960*5c4a5fe1SAndy Fiddaman 		if (nwrite <= 0)
961*5c4a5fe1SAndy Fiddaman 			return (false);
962*5c4a5fe1SAndy Fiddaman 
963*5c4a5fe1SAndy Fiddaman 		zlen = htonl(c->rc_zstream.total_out);
964*5c4a5fe1SAndy Fiddaman 		nwrite = stream_write(c->rc_fd, &zlen, sizeof (uint32_t));
965*5c4a5fe1SAndy Fiddaman 		if (nwrite <= 0)
966*5c4a5fe1SAndy Fiddaman 			return (false);
967*5c4a5fe1SAndy Fiddaman 		return (stream_write(c->rc_fd, c->rc_zbuf,
968*5c4a5fe1SAndy Fiddaman 		    c->rc_zstream.total_out) == c->rc_zstream.total_out);
969*5c4a5fe1SAndy Fiddaman 	}
970*5c4a5fe1SAndy Fiddaman 
971*5c4a5fe1SAndy Fiddaman doraw:
972*5c4a5fe1SAndy Fiddaman 	rect.rr_encoding = htonl(RFBP_ENCODING_RAW);
973*5c4a5fe1SAndy Fiddaman 	nwrite = stream_write(c->rc_fd, &rect, sizeof (rect));
974*5c4a5fe1SAndy Fiddaman 	if (nwrite <= 0)
975*5c4a5fe1SAndy Fiddaman 		return (false);
976*5c4a5fe1SAndy Fiddaman 
977*5c4a5fe1SAndy Fiddaman 	nwrite = gci->width * gci->height * sizeof (uint32_t);
978*5c4a5fe1SAndy Fiddaman 	return (stream_write(c->rc_fd, data, nwrite) == nwrite);
979*5c4a5fe1SAndy Fiddaman }
980*5c4a5fe1SAndy Fiddaman 
981*5c4a5fe1SAndy Fiddaman static bool
rfb_send_screen(rfb_client_t * c)982*5c4a5fe1SAndy Fiddaman rfb_send_screen(rfb_client_t *c)
983*5c4a5fe1SAndy Fiddaman {
984*5c4a5fe1SAndy Fiddaman 	struct bhyvegc_image *gci;
985*5c4a5fe1SAndy Fiddaman 	bool retval = true;
986*5c4a5fe1SAndy Fiddaman 	bool sendall = false;
987*5c4a5fe1SAndy Fiddaman 	int xcells, ycells;
988*5c4a5fe1SAndy Fiddaman 	int rem_x, rem_y;
989*5c4a5fe1SAndy Fiddaman 	uint32_t *p, *ncrc, *ocrc;
990*5c4a5fe1SAndy Fiddaman 	uint_t changes, perc, x, y;
991*5c4a5fe1SAndy Fiddaman 
992*5c4a5fe1SAndy Fiddaman 	/* Updates require a preceding client update request. */
993*5c4a5fe1SAndy Fiddaman 	if (atomic_exchange(&c->rc_pending, false) == false)
994*5c4a5fe1SAndy Fiddaman 		return (true);
995*5c4a5fe1SAndy Fiddaman 
996*5c4a5fe1SAndy Fiddaman 	console_refresh();
997*5c4a5fe1SAndy Fiddaman 	gci = console_get_image();
998*5c4a5fe1SAndy Fiddaman 
999*5c4a5fe1SAndy Fiddaman 	/*
1000*5c4a5fe1SAndy Fiddaman 	 * It's helpful if the image size or data address does not change
1001*5c4a5fe1SAndy Fiddaman 	 * underneath us.
1002*5c4a5fe1SAndy Fiddaman 	 */
1003*5c4a5fe1SAndy Fiddaman 	pthread_mutex_lock(&gci->mtx);
1004*5c4a5fe1SAndy Fiddaman 
1005*5c4a5fe1SAndy Fiddaman 	/* Check for screen resolution changes. */
1006*5c4a5fe1SAndy Fiddaman 	if (c->rc_width != gci->width ||
1007*5c4a5fe1SAndy Fiddaman 	    c->rc_height != gci->height) {
1008*5c4a5fe1SAndy Fiddaman 		c->rc_width = gci->width;
1009*5c4a5fe1SAndy Fiddaman 		c->rc_height = gci->height;
1010*5c4a5fe1SAndy Fiddaman 		c->rc_crc_reset = true;
1011*5c4a5fe1SAndy Fiddaman 		c->rc_send_fullscreen = true;
1012*5c4a5fe1SAndy Fiddaman 
1013*5c4a5fe1SAndy Fiddaman 		/* If the client supports it, send a resize event. */
1014*5c4a5fe1SAndy Fiddaman 		if (c->rc_encodings & RFB_ENCODING_RESIZE) {
1015*5c4a5fe1SAndy Fiddaman 			rfb_send_resize_update_msg(c);
1016*5c4a5fe1SAndy Fiddaman 			/*
1017*5c4a5fe1SAndy Fiddaman 			 * A resize message counts as an update in response to
1018*5c4a5fe1SAndy Fiddaman 			 * the client's preceding request so rc->pending does
1019*5c4a5fe1SAndy Fiddaman 			 * not need to be reset here.
1020*5c4a5fe1SAndy Fiddaman 			 */
1021*5c4a5fe1SAndy Fiddaman 			goto done;
1022*5c4a5fe1SAndy Fiddaman 		}
1023*5c4a5fe1SAndy Fiddaman 	}
1024*5c4a5fe1SAndy Fiddaman 
1025*5c4a5fe1SAndy Fiddaman 	/* Clear old CRC values. */
1026*5c4a5fe1SAndy Fiddaman 	if (atomic_exchange(&c->rc_crc_reset, false))
1027*5c4a5fe1SAndy Fiddaman 		memset(c->rc_crc, '\0', c->rc_cells * sizeof (uint32_t));
1028*5c4a5fe1SAndy Fiddaman 
1029*5c4a5fe1SAndy Fiddaman 	if (c->rc_custom_pixfmt && (c->rc_gci.data == NULL ||
1030*5c4a5fe1SAndy Fiddaman 	    c->rc_gci.width != c->rc_width ||
1031*5c4a5fe1SAndy Fiddaman 	    c->rc_gci.height != c->rc_height)) {
1032*5c4a5fe1SAndy Fiddaman 		c->rc_gci.data = reallocarray(c->rc_gci.data,
1033*5c4a5fe1SAndy Fiddaman 		    c->rc_width * c->rc_height, sizeof (uint32_t));
1034*5c4a5fe1SAndy Fiddaman 		if (c->rc_gci.data == NULL) {
1035*5c4a5fe1SAndy Fiddaman 			retval = false;
1036*5c4a5fe1SAndy Fiddaman 			goto done;
1037*5c4a5fe1SAndy Fiddaman 		}
1038*5c4a5fe1SAndy Fiddaman 		c->rc_gci.width = c->rc_width;
1039*5c4a5fe1SAndy Fiddaman 		c->rc_gci.height = c->rc_height;
1040*5c4a5fe1SAndy Fiddaman 	} else if (!c->rc_custom_pixfmt && c->rc_gci.data != NULL) {
1041*5c4a5fe1SAndy Fiddaman 		free(c->rc_gci.data);
1042*5c4a5fe1SAndy Fiddaman 		c->rc_gci.data = NULL;
1043*5c4a5fe1SAndy Fiddaman 	}
1044*5c4a5fe1SAndy Fiddaman 
1045*5c4a5fe1SAndy Fiddaman 	sendall = atomic_exchange(&c->rc_send_fullscreen, false);
1046*5c4a5fe1SAndy Fiddaman 
1047*5c4a5fe1SAndy Fiddaman 	/*
1048*5c4a5fe1SAndy Fiddaman 	 * Calculate a checksum for each 32x32 cell. Send all that have
1049*5c4a5fe1SAndy Fiddaman 	 * changed since the last scan.
1050*5c4a5fe1SAndy Fiddaman 	 */
1051*5c4a5fe1SAndy Fiddaman 
1052*5c4a5fe1SAndy Fiddaman 	xcells = howmany(gci->width, RFB_PIX_PER_CELL);
1053*5c4a5fe1SAndy Fiddaman 	ycells = howmany(gci->height, RFB_PIX_PER_CELL);
1054*5c4a5fe1SAndy Fiddaman 	rem_x = gci->width & RFB_PIXCELL_MASK;
1055*5c4a5fe1SAndy Fiddaman 	rem_y = gci->height & RFB_PIXCELL_MASK;
1056*5c4a5fe1SAndy Fiddaman 	if (rem_y == 0)
1057*5c4a5fe1SAndy Fiddaman 		rem_y = RFB_PIX_PER_CELL;
1058*5c4a5fe1SAndy Fiddaman 
1059*5c4a5fe1SAndy Fiddaman 	p = gci->data;
1060*5c4a5fe1SAndy Fiddaman 
1061*5c4a5fe1SAndy Fiddaman 	ncrc = c->rc_crc_tmp - xcells;
1062*5c4a5fe1SAndy Fiddaman 	ocrc = c->rc_crc - xcells;
1063*5c4a5fe1SAndy Fiddaman 	changes = 0;
1064*5c4a5fe1SAndy Fiddaman 	memset(c->rc_crc_tmp, '\0', sizeof (uint32_t) * xcells * ycells);
1065*5c4a5fe1SAndy Fiddaman 	for (y = 0; y < gci->height; y++) {
1066*5c4a5fe1SAndy Fiddaman 		if ((y & RFB_PIXCELL_MASK) == 0) {
1067*5c4a5fe1SAndy Fiddaman 			ncrc += xcells;
1068*5c4a5fe1SAndy Fiddaman 			ocrc += xcells;
1069*5c4a5fe1SAndy Fiddaman 		}
1070*5c4a5fe1SAndy Fiddaman 
1071*5c4a5fe1SAndy Fiddaman 		for (x = 0; x < xcells; x++) {
1072*5c4a5fe1SAndy Fiddaman 			uint_t cellwidth;
1073*5c4a5fe1SAndy Fiddaman 
1074*5c4a5fe1SAndy Fiddaman 			if (x == xcells - 1 && rem_x > 0)
1075*5c4a5fe1SAndy Fiddaman 				cellwidth = rem_x;
1076*5c4a5fe1SAndy Fiddaman 			else
1077*5c4a5fe1SAndy Fiddaman 				cellwidth = RFB_PIX_PER_CELL;
1078*5c4a5fe1SAndy Fiddaman 
1079*5c4a5fe1SAndy Fiddaman 			if (rfb_sse42) {
1080*5c4a5fe1SAndy Fiddaman 				ncrc[x] = fast_crc32(p,
1081*5c4a5fe1SAndy Fiddaman 				    cellwidth * sizeof (uint32_t), ncrc[x]);
1082*5c4a5fe1SAndy Fiddaman 			} else {
1083*5c4a5fe1SAndy Fiddaman 				ncrc[x] = (uint32_t)crc32(ncrc[x],
1084*5c4a5fe1SAndy Fiddaman 				    (Bytef *)p, cellwidth * sizeof (uint32_t));
1085*5c4a5fe1SAndy Fiddaman 			}
1086*5c4a5fe1SAndy Fiddaman 
1087*5c4a5fe1SAndy Fiddaman 			p += cellwidth;
1088*5c4a5fe1SAndy Fiddaman 
1089*5c4a5fe1SAndy Fiddaman 			/* check for crc delta if last row in cell. */
1090*5c4a5fe1SAndy Fiddaman 			if ((y & RFB_PIXCELL_MASK) == RFB_PIXCELL_MASK ||
1091*5c4a5fe1SAndy Fiddaman 			    y == gci->height - 1) {
1092*5c4a5fe1SAndy Fiddaman 				if (ocrc[x] != ncrc[x]) {
1093*5c4a5fe1SAndy Fiddaman 					ocrc[x] = ncrc[x];
1094*5c4a5fe1SAndy Fiddaman 					ncrc[x] = 1;
1095*5c4a5fe1SAndy Fiddaman 					changes++;
1096*5c4a5fe1SAndy Fiddaman 				} else {
1097*5c4a5fe1SAndy Fiddaman 					ncrc[x] = 0;
1098*5c4a5fe1SAndy Fiddaman 				}
1099*5c4a5fe1SAndy Fiddaman 			}
1100*5c4a5fe1SAndy Fiddaman 		}
1101*5c4a5fe1SAndy Fiddaman 	}
1102*5c4a5fe1SAndy Fiddaman 
1103*5c4a5fe1SAndy Fiddaman 	perc = (changes * 100) / (xcells * ycells);
1104*5c4a5fe1SAndy Fiddaman 	if (rfb_debug > 1 && changes > 0) {
1105*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGDEBUG,
1106*5c4a5fe1SAndy Fiddaman 		    "scanned and found %u changed cell(s) - %u%%",
1107*5c4a5fe1SAndy Fiddaman 		    changes, perc);
1108*5c4a5fe1SAndy Fiddaman 	}
1109*5c4a5fe1SAndy Fiddaman 
1110*5c4a5fe1SAndy Fiddaman 	/*
1111*5c4a5fe1SAndy Fiddaman 	 * If there are no changes, don't send an update. Restore the pending
1112*5c4a5fe1SAndy Fiddaman 	 * flag since we still owe the client an update.
1113*5c4a5fe1SAndy Fiddaman 	 */
1114*5c4a5fe1SAndy Fiddaman 	if (!sendall && !changes) {
1115*5c4a5fe1SAndy Fiddaman 		c->rc_pending = true;
1116*5c4a5fe1SAndy Fiddaman 		goto done;
1117*5c4a5fe1SAndy Fiddaman 	}
1118*5c4a5fe1SAndy Fiddaman 
1119*5c4a5fe1SAndy Fiddaman 	/* If there are a lot of changes, send the whole screen. */
1120*5c4a5fe1SAndy Fiddaman 	if (perc >= RFB_SENDALL_THRESH)
1121*5c4a5fe1SAndy Fiddaman 		sendall = true;
1122*5c4a5fe1SAndy Fiddaman 
1123*5c4a5fe1SAndy Fiddaman 	if (sendall) {
1124*5c4a5fe1SAndy Fiddaman 		retval = rfb_send_all(c, gci);
1125*5c4a5fe1SAndy Fiddaman 		goto done;
1126*5c4a5fe1SAndy Fiddaman 	}
1127*5c4a5fe1SAndy Fiddaman 
1128*5c4a5fe1SAndy Fiddaman 	if (!rfb_send_update_header(c, changes)) {
1129*5c4a5fe1SAndy Fiddaman 		retval = false;
1130*5c4a5fe1SAndy Fiddaman 		goto done;
1131*5c4a5fe1SAndy Fiddaman 	}
1132*5c4a5fe1SAndy Fiddaman 
1133*5c4a5fe1SAndy Fiddaman 	/* Send the changed cells as separate rects. */
1134*5c4a5fe1SAndy Fiddaman 	ncrc = c->rc_crc_tmp;
1135*5c4a5fe1SAndy Fiddaman 	for (y = 0; y < gci->height; y += RFB_PIX_PER_CELL) {
1136*5c4a5fe1SAndy Fiddaman 		/* Previous cell's row. */
1137*5c4a5fe1SAndy Fiddaman 		int celly = (y >> RFB_PIXCELL_SHIFT);
1138*5c4a5fe1SAndy Fiddaman 
1139*5c4a5fe1SAndy Fiddaman 		/* Delta check crc to previous set. */
1140*5c4a5fe1SAndy Fiddaman 		for (x = 0; x < xcells; x++) {
1141*5c4a5fe1SAndy Fiddaman 			uint_t cellwidth;
1142*5c4a5fe1SAndy Fiddaman 
1143*5c4a5fe1SAndy Fiddaman 			if (*ncrc++ == 0)
1144*5c4a5fe1SAndy Fiddaman 				continue;
1145*5c4a5fe1SAndy Fiddaman 
1146*5c4a5fe1SAndy Fiddaman 			if (x == xcells - 1 && rem_x > 0)
1147*5c4a5fe1SAndy Fiddaman 				cellwidth = rem_x;
1148*5c4a5fe1SAndy Fiddaman 			else
1149*5c4a5fe1SAndy Fiddaman 				cellwidth = RFB_PIX_PER_CELL;
1150*5c4a5fe1SAndy Fiddaman 
1151*5c4a5fe1SAndy Fiddaman 			if (!rfb_send_rect(c, gci,
1152*5c4a5fe1SAndy Fiddaman 			    x * RFB_PIX_PER_CELL, celly * RFB_PIX_PER_CELL,
1153*5c4a5fe1SAndy Fiddaman 			    cellwidth, y + RFB_PIX_PER_CELL >= gci->height ?
1154*5c4a5fe1SAndy Fiddaman 			    rem_y : RFB_PIX_PER_CELL)) {
1155*5c4a5fe1SAndy Fiddaman 				retval = false;
1156*5c4a5fe1SAndy Fiddaman 				goto done;
1157*5c4a5fe1SAndy Fiddaman 			}
1158*5c4a5fe1SAndy Fiddaman 		}
1159*5c4a5fe1SAndy Fiddaman 	}
1160*5c4a5fe1SAndy Fiddaman 
1161*5c4a5fe1SAndy Fiddaman done:
1162*5c4a5fe1SAndy Fiddaman 	pthread_mutex_unlock(&gci->mtx);
1163*5c4a5fe1SAndy Fiddaman 
1164*5c4a5fe1SAndy Fiddaman 	return (retval);
1165*5c4a5fe1SAndy Fiddaman }
1166*5c4a5fe1SAndy Fiddaman 
1167*5c4a5fe1SAndy Fiddaman static void *
rfb_client_rx_thread(void * arg)1168*5c4a5fe1SAndy Fiddaman rfb_client_rx_thread(void *arg)
1169*5c4a5fe1SAndy Fiddaman {
1170*5c4a5fe1SAndy Fiddaman 	rfb_client_t *c = arg;
1171*5c4a5fe1SAndy Fiddaman 	unsigned char cmd;
1172*5c4a5fe1SAndy Fiddaman 	bool ret = true;
1173*5c4a5fe1SAndy Fiddaman 
1174*5c4a5fe1SAndy Fiddaman 	while (ret && !c->rc_closing && (read(c->rc_fd, &cmd, 1) == 1)) {
1175*5c4a5fe1SAndy Fiddaman 		switch (cmd) {
1176*5c4a5fe1SAndy Fiddaman 		case RFBP_CS_SET_PIXEL_FORMAT:
1177*5c4a5fe1SAndy Fiddaman 			ret = rfb_recv_set_pixel_format(c);
1178*5c4a5fe1SAndy Fiddaman 			break;
1179*5c4a5fe1SAndy Fiddaman 		case RFBP_CS_SET_ENCODINGS:
1180*5c4a5fe1SAndy Fiddaman 			ret = rfb_recv_set_encodings(c);
1181*5c4a5fe1SAndy Fiddaman 			break;
1182*5c4a5fe1SAndy Fiddaman 		case RFBP_CS_UPDATE_REQUEST:
1183*5c4a5fe1SAndy Fiddaman 			ret = rfb_recv_update(c);
1184*5c4a5fe1SAndy Fiddaman 			break;
1185*5c4a5fe1SAndy Fiddaman 		case RFBP_CS_KEY_EVENT:
1186*5c4a5fe1SAndy Fiddaman 			ret = rfb_recv_key_event(c);
1187*5c4a5fe1SAndy Fiddaman 			break;
1188*5c4a5fe1SAndy Fiddaman 		case RFBP_CS_POINTER_EVENT:
1189*5c4a5fe1SAndy Fiddaman 			ret = rfb_recv_pointer_event(c);
1190*5c4a5fe1SAndy Fiddaman 			break;
1191*5c4a5fe1SAndy Fiddaman 		case RFBP_CS_CUT_TEXT:
1192*5c4a5fe1SAndy Fiddaman 			ret = rfb_recv_cut_text(c);
1193*5c4a5fe1SAndy Fiddaman 			break;
1194*5c4a5fe1SAndy Fiddaman 		case RFBP_CS_QEMU:
1195*5c4a5fe1SAndy Fiddaman 			ret = rfb_recv_qemu(c);
1196*5c4a5fe1SAndy Fiddaman 			break;
1197*5c4a5fe1SAndy Fiddaman 		default:
1198*5c4a5fe1SAndy Fiddaman 			rfb_printf(c, RFB_LOGWARN, "unknown cs code %d",
1199*5c4a5fe1SAndy Fiddaman 			    cmd & 0xff);
1200*5c4a5fe1SAndy Fiddaman 			ret = false;
1201*5c4a5fe1SAndy Fiddaman 		}
1202*5c4a5fe1SAndy Fiddaman 	}
1203*5c4a5fe1SAndy Fiddaman 
1204*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "client rx thread exiting");
1205*5c4a5fe1SAndy Fiddaman 	c->rc_closing = true;
1206*5c4a5fe1SAndy Fiddaman 
1207*5c4a5fe1SAndy Fiddaman 	return (NULL);
1208*5c4a5fe1SAndy Fiddaman }
1209*5c4a5fe1SAndy Fiddaman 
1210*5c4a5fe1SAndy Fiddaman static void *
rfb_client_tx_thread(void * arg)1211*5c4a5fe1SAndy Fiddaman rfb_client_tx_thread(void *arg)
1212*5c4a5fe1SAndy Fiddaman {
1213*5c4a5fe1SAndy Fiddaman 	rfb_client_t *c = arg;
1214*5c4a5fe1SAndy Fiddaman 	rfb_server_t *s = c->rc_s;
1215*5c4a5fe1SAndy Fiddaman 	char tname[MAXCOMLEN + 1];
1216*5c4a5fe1SAndy Fiddaman 	uint_t counter = 0;
1217*5c4a5fe1SAndy Fiddaman 	hrtime_t tprev;
1218*5c4a5fe1SAndy Fiddaman 	void *status;
1219*5c4a5fe1SAndy Fiddaman 	int err;
1220*5c4a5fe1SAndy Fiddaman 
1221*5c4a5fe1SAndy Fiddaman 	(void) snprintf(tname, sizeof (tname), "rfb%u tx", c->rc_instance);
1222*5c4a5fe1SAndy Fiddaman 	(void) pthread_set_name_np(c->rc_tx_tid, tname);
1223*5c4a5fe1SAndy Fiddaman 
1224*5c4a5fe1SAndy Fiddaman 	c->rc_sinfo.rsi_pixfmt = c->rc_s->rs_pixfmt;
1225*5c4a5fe1SAndy Fiddaman 	c->rc_encodings = RFB_ENCODING_RAW;
1226*5c4a5fe1SAndy Fiddaman 
1227*5c4a5fe1SAndy Fiddaman 	if (!rfb_handshake(c)) {
1228*5c4a5fe1SAndy Fiddaman 		rfb_printf(c, RFB_LOGWARN, "handshake failure");
1229*5c4a5fe1SAndy Fiddaman 		goto out;
1230*5c4a5fe1SAndy Fiddaman 	}
1231*5c4a5fe1SAndy Fiddaman 
1232*5c4a5fe1SAndy Fiddaman 	c->rc_cells = howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, RFB_PIX_PER_CELL);
1233*5c4a5fe1SAndy Fiddaman 	if ((c->rc_crc = calloc(c->rc_cells, sizeof (uint32_t))) == NULL ||
1234*5c4a5fe1SAndy Fiddaman 	    (c->rc_crc_tmp = calloc(c->rc_cells, sizeof (uint32_t))) == NULL) {
1235*5c4a5fe1SAndy Fiddaman 		perror("calloc crc");
1236*5c4a5fe1SAndy Fiddaman 		goto out;
1237*5c4a5fe1SAndy Fiddaman 	}
1238*5c4a5fe1SAndy Fiddaman 
1239*5c4a5fe1SAndy Fiddaman 	err = pthread_create(&c->rc_rx_tid, NULL, rfb_client_rx_thread, c);
1240*5c4a5fe1SAndy Fiddaman 	if (err != 0) {
1241*5c4a5fe1SAndy Fiddaman 		perror("pthread_create client rx thread");
1242*5c4a5fe1SAndy Fiddaman 		goto out;
1243*5c4a5fe1SAndy Fiddaman 	}
1244*5c4a5fe1SAndy Fiddaman 
1245*5c4a5fe1SAndy Fiddaman 	(void) snprintf(tname, sizeof (tname), "rfb%u rx", c->rc_instance);
1246*5c4a5fe1SAndy Fiddaman 	(void) pthread_set_name_np(c->rc_rx_tid, tname);
1247*5c4a5fe1SAndy Fiddaman 
1248*5c4a5fe1SAndy Fiddaman 	tprev = gethrtime();
1249*5c4a5fe1SAndy Fiddaman 
1250*5c4a5fe1SAndy Fiddaman 	while (!c->rc_closing) {
1251*5c4a5fe1SAndy Fiddaman 		struct timeval tv;
1252*5c4a5fe1SAndy Fiddaman 		hrtime_t tnow;
1253*5c4a5fe1SAndy Fiddaman 		int64_t tdiff;
1254*5c4a5fe1SAndy Fiddaman 		fd_set rfds;
1255*5c4a5fe1SAndy Fiddaman 		int err;
1256*5c4a5fe1SAndy Fiddaman 
1257*5c4a5fe1SAndy Fiddaman 		FD_ZERO(&rfds);
1258*5c4a5fe1SAndy Fiddaman 		FD_SET(c->rc_fd, &rfds);
1259*5c4a5fe1SAndy Fiddaman 		tv.tv_sec = 0;
1260*5c4a5fe1SAndy Fiddaman 		tv.tv_usec = RFB_SEL_DELAY_US;
1261*5c4a5fe1SAndy Fiddaman 
1262*5c4a5fe1SAndy Fiddaman 		err = select(c->rc_fd + 1, &rfds, NULL, NULL, &tv);
1263*5c4a5fe1SAndy Fiddaman 		if (err < 0)
1264*5c4a5fe1SAndy Fiddaman 			break;
1265*5c4a5fe1SAndy Fiddaman 
1266*5c4a5fe1SAndy Fiddaman 		/* Determine if its time to push the screen; ~24hz. */
1267*5c4a5fe1SAndy Fiddaman 		tnow = gethrtime();
1268*5c4a5fe1SAndy Fiddaman 		tdiff = NSEC2USEC(tnow - tprev);
1269*5c4a5fe1SAndy Fiddaman 		if (tdiff >= RFB_SCREEN_POLL_DELAY) {
1270*5c4a5fe1SAndy Fiddaman 			bool input;
1271*5c4a5fe1SAndy Fiddaman 
1272*5c4a5fe1SAndy Fiddaman 			tprev = tnow;
1273*5c4a5fe1SAndy Fiddaman 
1274*5c4a5fe1SAndy Fiddaman 			input = atomic_exchange(&c->rc_input_detected, false);
1275*5c4a5fe1SAndy Fiddaman 			/*
1276*5c4a5fe1SAndy Fiddaman 			 * Refresh the screen on every second trip through the
1277*5c4a5fe1SAndy Fiddaman 			 * loop, or if keyboard/mouse input has been detected.
1278*5c4a5fe1SAndy Fiddaman 			 */
1279*5c4a5fe1SAndy Fiddaman 			if ((++counter & 1) != 0 || input) {
1280*5c4a5fe1SAndy Fiddaman 				if (!rfb_send_screen(c))
1281*5c4a5fe1SAndy Fiddaman 					break;
1282*5c4a5fe1SAndy Fiddaman 			}
1283*5c4a5fe1SAndy Fiddaman 		} else {
1284*5c4a5fe1SAndy Fiddaman 			(void) usleep(RFB_SCREEN_POLL_DELAY - tdiff);
1285*5c4a5fe1SAndy Fiddaman 		}
1286*5c4a5fe1SAndy Fiddaman 	}
1287*5c4a5fe1SAndy Fiddaman 
1288*5c4a5fe1SAndy Fiddaman out:
1289*5c4a5fe1SAndy Fiddaman 
1290*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGWARN, "disconnected");
1291*5c4a5fe1SAndy Fiddaman 
1292*5c4a5fe1SAndy Fiddaman 	(void) pthread_join(c->rc_rx_tid, &status);
1293*5c4a5fe1SAndy Fiddaman 	pthread_mutex_lock(&s->rs_clientlock);
1294*5c4a5fe1SAndy Fiddaman 	s->rs_clientcount--;
1295*5c4a5fe1SAndy Fiddaman 	list_remove(&s->rs_clients, c);
1296*5c4a5fe1SAndy Fiddaman 	if (s->rs_exclusive && s->rs_clientcount == 0)
1297*5c4a5fe1SAndy Fiddaman 		s->rs_exclusive = false;
1298*5c4a5fe1SAndy Fiddaman 	id_free(rfb_idspace, c->rc_instance);
1299*5c4a5fe1SAndy Fiddaman 	pthread_mutex_unlock(&s->rs_clientlock);
1300*5c4a5fe1SAndy Fiddaman 
1301*5c4a5fe1SAndy Fiddaman 	rfb_free_client(c);
1302*5c4a5fe1SAndy Fiddaman 	return (NULL);
1303*5c4a5fe1SAndy Fiddaman }
1304*5c4a5fe1SAndy Fiddaman 
1305*5c4a5fe1SAndy Fiddaman static void
rfb_accept(int sfd,enum ev_type event,void * arg)1306*5c4a5fe1SAndy Fiddaman rfb_accept(int sfd, enum ev_type event, void *arg)
1307*5c4a5fe1SAndy Fiddaman {
1308*5c4a5fe1SAndy Fiddaman 	rfb_server_t *s = arg;
1309*5c4a5fe1SAndy Fiddaman 	rfb_client_t *c = NULL;
1310*5c4a5fe1SAndy Fiddaman 	struct sockaddr_storage cliaddr;
1311*5c4a5fe1SAndy Fiddaman 	socklen_t len;
1312*5c4a5fe1SAndy Fiddaman 	char host[NI_MAXHOST], port[NI_MAXSERV];
1313*5c4a5fe1SAndy Fiddaman 	int cfd, err;
1314*5c4a5fe1SAndy Fiddaman 	uint_t cc;
1315*5c4a5fe1SAndy Fiddaman 
1316*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGDEBUG, "incoming connection");
1317*5c4a5fe1SAndy Fiddaman 
1318*5c4a5fe1SAndy Fiddaman 	len = sizeof (cliaddr);
1319*5c4a5fe1SAndy Fiddaman 	cfd = accept(sfd, (struct sockaddr *)&cliaddr, &len);
1320*5c4a5fe1SAndy Fiddaman 	if (cfd == -1) {
1321*5c4a5fe1SAndy Fiddaman 		perror("client accept");
1322*5c4a5fe1SAndy Fiddaman 		return;
1323*5c4a5fe1SAndy Fiddaman 	}
1324*5c4a5fe1SAndy Fiddaman 
1325*5c4a5fe1SAndy Fiddaman 	*host = *port = '\0';
1326*5c4a5fe1SAndy Fiddaman 	if (cliaddr.ss_family == AF_UNIX) {
1327*5c4a5fe1SAndy Fiddaman 		rfb_printf(NULL, RFB_LOGDEBUG, "connection on UNIX socket");
1328*5c4a5fe1SAndy Fiddaman 		(void) strlcpy(host, "<UNIX>", sizeof (host));
1329*5c4a5fe1SAndy Fiddaman 	} else {
1330*5c4a5fe1SAndy Fiddaman 		err = getnameinfo((struct sockaddr *)&cliaddr, len,
1331*5c4a5fe1SAndy Fiddaman 		    host, sizeof (host), port, sizeof (port),
1332*5c4a5fe1SAndy Fiddaman 		    NI_NUMERICHOST | NI_NUMERICSERV);
1333*5c4a5fe1SAndy Fiddaman 		if (err != 0) {
1334*5c4a5fe1SAndy Fiddaman 			rfb_printf(NULL, RFB_LOGERR, "getnameinfo: %s",
1335*5c4a5fe1SAndy Fiddaman 			    gai_strerror(err));
1336*5c4a5fe1SAndy Fiddaman 			*host = *port = '\0';
1337*5c4a5fe1SAndy Fiddaman 		} else {
1338*5c4a5fe1SAndy Fiddaman 			rfb_printf(NULL, RFB_LOGDEBUG, "connection from %s:%s",
1339*5c4a5fe1SAndy Fiddaman 			    host, port);
1340*5c4a5fe1SAndy Fiddaman 		}
1341*5c4a5fe1SAndy Fiddaman 	}
1342*5c4a5fe1SAndy Fiddaman 
1343*5c4a5fe1SAndy Fiddaman 	pthread_mutex_lock(&s->rs_clientlock);
1344*5c4a5fe1SAndy Fiddaman 	cc = s->rs_clientcount;
1345*5c4a5fe1SAndy Fiddaman 	pthread_mutex_unlock(&s->rs_clientlock);
1346*5c4a5fe1SAndy Fiddaman 	if (cc >= RFB_MAX_CLIENTS) {
1347*5c4a5fe1SAndy Fiddaman 		rfb_printf(NULL, RFB_LOGERR,
1348*5c4a5fe1SAndy Fiddaman 		    "too many clients, closing connection.");
1349*5c4a5fe1SAndy Fiddaman 		goto fail;
1350*5c4a5fe1SAndy Fiddaman 	}
1351*5c4a5fe1SAndy Fiddaman 
1352*5c4a5fe1SAndy Fiddaman 	if ((c = calloc(1, sizeof (rfb_client_t))) == NULL) {
1353*5c4a5fe1SAndy Fiddaman 		perror("calloc client");
1354*5c4a5fe1SAndy Fiddaman 		goto fail;
1355*5c4a5fe1SAndy Fiddaman 	}
1356*5c4a5fe1SAndy Fiddaman 
1357*5c4a5fe1SAndy Fiddaman 	c->rc_fd = cfd;
1358*5c4a5fe1SAndy Fiddaman 	c->rc_s = s;
1359*5c4a5fe1SAndy Fiddaman 	c->rc_zbuf = malloc(RFB_ZLIB_BUFSZ + 16);
1360*5c4a5fe1SAndy Fiddaman 	if (c->rc_zbuf == NULL)
1361*5c4a5fe1SAndy Fiddaman 		goto fail;
1362*5c4a5fe1SAndy Fiddaman 
1363*5c4a5fe1SAndy Fiddaman 	pthread_mutex_lock(&s->rs_clientlock);
1364*5c4a5fe1SAndy Fiddaman 
1365*5c4a5fe1SAndy Fiddaman 	err = pthread_create(&c->rc_tx_tid, NULL, rfb_client_tx_thread, c);
1366*5c4a5fe1SAndy Fiddaman 	if (err != 0) {
1367*5c4a5fe1SAndy Fiddaman 		perror("pthread_create client tx thread");
1368*5c4a5fe1SAndy Fiddaman 		pthread_mutex_unlock(&s->rs_clientlock);
1369*5c4a5fe1SAndy Fiddaman 		goto fail;
1370*5c4a5fe1SAndy Fiddaman 	}
1371*5c4a5fe1SAndy Fiddaman 
1372*5c4a5fe1SAndy Fiddaman 	s->rs_clientcount++;
1373*5c4a5fe1SAndy Fiddaman 	list_insert_tail(&s->rs_clients, c);
1374*5c4a5fe1SAndy Fiddaman 	c->rc_instance = id_allocff(rfb_idspace);
1375*5c4a5fe1SAndy Fiddaman 	pthread_mutex_unlock(&s->rs_clientlock);
1376*5c4a5fe1SAndy Fiddaman 
1377*5c4a5fe1SAndy Fiddaman 	(void) pthread_detach(c->rc_tx_tid);
1378*5c4a5fe1SAndy Fiddaman 
1379*5c4a5fe1SAndy Fiddaman 	rfb_printf(c, RFB_LOGWARN, "connection from %s", host);
1380*5c4a5fe1SAndy Fiddaman 
1381*5c4a5fe1SAndy Fiddaman 	return;
1382*5c4a5fe1SAndy Fiddaman 
1383*5c4a5fe1SAndy Fiddaman fail:
1384*5c4a5fe1SAndy Fiddaman 	(void) close(cfd);
1385*5c4a5fe1SAndy Fiddaman 	free(c);
1386*5c4a5fe1SAndy Fiddaman }
1387*5c4a5fe1SAndy Fiddaman 
1388*5c4a5fe1SAndy Fiddaman int
rfb_init(char * hostname,int port,int wait,const char * password,const char * name)1389*5c4a5fe1SAndy Fiddaman rfb_init(char *hostname, int port, int wait, const char *password,
1390*5c4a5fe1SAndy Fiddaman     const char *name)
1391*5c4a5fe1SAndy Fiddaman {
1392*5c4a5fe1SAndy Fiddaman 	rfb_server_t *s;
1393*5c4a5fe1SAndy Fiddaman #ifndef WITHOUT_CAPSICUM
1394*5c4a5fe1SAndy Fiddaman 	cap_rights_t rights;
1395*5c4a5fe1SAndy Fiddaman #endif
1396*5c4a5fe1SAndy Fiddaman 
1397*5c4a5fe1SAndy Fiddaman 	(void) pthread_once(&rfb_once, rfb_init_once);
1398*5c4a5fe1SAndy Fiddaman 
1399*5c4a5fe1SAndy Fiddaman 	if (rfb_idspace == NULL) {
1400*5c4a5fe1SAndy Fiddaman 		rfb_printf(NULL, RFB_LOGERR,
1401*5c4a5fe1SAndy Fiddaman 		    "rfb_idspace could not be allocated");
1402*5c4a5fe1SAndy Fiddaman 		return (-1);
1403*5c4a5fe1SAndy Fiddaman 	}
1404*5c4a5fe1SAndy Fiddaman 
1405*5c4a5fe1SAndy Fiddaman 	if ((s = calloc(1, sizeof (rfb_server_t))) == NULL) {
1406*5c4a5fe1SAndy Fiddaman 		perror("calloc");
1407*5c4a5fe1SAndy Fiddaman 		return (-1);
1408*5c4a5fe1SAndy Fiddaman 	}
1409*5c4a5fe1SAndy Fiddaman 	s->rs_fd = -1;
1410*5c4a5fe1SAndy Fiddaman 	s->rs_name = name;
1411*5c4a5fe1SAndy Fiddaman 
1412*5c4a5fe1SAndy Fiddaman 	if (password != NULL && strlen(password) > 0)
1413*5c4a5fe1SAndy Fiddaman 		s->rs_password = password;
1414*5c4a5fe1SAndy Fiddaman 
1415*5c4a5fe1SAndy Fiddaman 	if (pthread_mutex_init(&s->rs_clientlock, NULL) != 0) {
1416*5c4a5fe1SAndy Fiddaman 		perror("pthread_mutex_init");
1417*5c4a5fe1SAndy Fiddaman 		free(s);
1418*5c4a5fe1SAndy Fiddaman 		return (-1);
1419*5c4a5fe1SAndy Fiddaman 	}
1420*5c4a5fe1SAndy Fiddaman 
1421*5c4a5fe1SAndy Fiddaman 	list_create(&s->rs_clients, sizeof (rfb_client_t),
1422*5c4a5fe1SAndy Fiddaman 	    offsetof(rfb_client_t, rc_node));
1423*5c4a5fe1SAndy Fiddaman 
1424*5c4a5fe1SAndy Fiddaman 	/* Server pixel format. */
1425*5c4a5fe1SAndy Fiddaman 	s->rs_pixfmt.rp_bpp = RFB_PIX_BPP;
1426*5c4a5fe1SAndy Fiddaman 	s->rs_pixfmt.rp_depth = RFB_PIX_DEPTH;
1427*5c4a5fe1SAndy Fiddaman 	s->rs_pixfmt.rp_bigendian = 0;
1428*5c4a5fe1SAndy Fiddaman 	s->rs_pixfmt.rp_truecolour = 1;
1429*5c4a5fe1SAndy Fiddaman 	s->rs_pixfmt.rp_r_max = htons(RFB_PIX_RMAX);
1430*5c4a5fe1SAndy Fiddaman 	s->rs_pixfmt.rp_g_max = htons(RFB_PIX_GMAX);
1431*5c4a5fe1SAndy Fiddaman 	s->rs_pixfmt.rp_b_max = htons(RFB_PIX_BMAX);
1432*5c4a5fe1SAndy Fiddaman 	s->rs_pixfmt.rp_r_shift = RFB_PIX_RSHIFT;
1433*5c4a5fe1SAndy Fiddaman 	s->rs_pixfmt.rp_g_shift = RFB_PIX_GSHIFT;
1434*5c4a5fe1SAndy Fiddaman 	s->rs_pixfmt.rp_b_shift = RFB_PIX_BSHIFT;
1435*5c4a5fe1SAndy Fiddaman 
1436*5c4a5fe1SAndy Fiddaman 	/* UNIX socket. */
1437*5c4a5fe1SAndy Fiddaman 	if (port == -1 && hostname != NULL && *hostname == '/') {
1438*5c4a5fe1SAndy Fiddaman 		struct sockaddr_un sock;
1439*5c4a5fe1SAndy Fiddaman 
1440*5c4a5fe1SAndy Fiddaman 		s->rs_fd = socket(PF_UNIX, SOCK_STREAM, 0);
1441*5c4a5fe1SAndy Fiddaman 		if (s->rs_fd < 0) {
1442*5c4a5fe1SAndy Fiddaman 			perror("socket");
1443*5c4a5fe1SAndy Fiddaman 			goto fail;
1444*5c4a5fe1SAndy Fiddaman 		}
1445*5c4a5fe1SAndy Fiddaman 
1446*5c4a5fe1SAndy Fiddaman 		sock.sun_family = AF_UNIX;
1447*5c4a5fe1SAndy Fiddaman 		if (strlcpy(sock.sun_path, hostname, sizeof (sock.sun_path)) >=
1448*5c4a5fe1SAndy Fiddaman 		    sizeof (sock.sun_path)) {
1449*5c4a5fe1SAndy Fiddaman 			rfb_printf(NULL, RFB_LOGERR,
1450*5c4a5fe1SAndy Fiddaman 			    "socket path '%s' too long\n", hostname);
1451*5c4a5fe1SAndy Fiddaman 			goto fail;
1452*5c4a5fe1SAndy Fiddaman 		}
1453*5c4a5fe1SAndy Fiddaman 
1454*5c4a5fe1SAndy Fiddaman 		(void) unlink(hostname);
1455*5c4a5fe1SAndy Fiddaman 		if (bind(s->rs_fd, (struct sockaddr *)&sock,
1456*5c4a5fe1SAndy Fiddaman 		    sizeof (sock)) < 0) {
1457*5c4a5fe1SAndy Fiddaman 			perror("bind");
1458*5c4a5fe1SAndy Fiddaman 			goto fail;
1459*5c4a5fe1SAndy Fiddaman 		}
1460*5c4a5fe1SAndy Fiddaman 	} else {
1461*5c4a5fe1SAndy Fiddaman 		struct addrinfo hints, *ai = NULL;
1462*5c4a5fe1SAndy Fiddaman 		char servname[6];
1463*5c4a5fe1SAndy Fiddaman 		int e;
1464*5c4a5fe1SAndy Fiddaman 
1465*5c4a5fe1SAndy Fiddaman 		(void) snprintf(servname, sizeof (servname), "%d",
1466*5c4a5fe1SAndy Fiddaman 		    port ? port : RFB_DEFAULT_PORT);
1467*5c4a5fe1SAndy Fiddaman 
1468*5c4a5fe1SAndy Fiddaman 		if (hostname == NULL || strlen(hostname) == 0) {
1469*5c4a5fe1SAndy Fiddaman #if defined(INET)
1470*5c4a5fe1SAndy Fiddaman 			hostname = "127.0.0.1";
1471*5c4a5fe1SAndy Fiddaman #elif defined(INET6)
1472*5c4a5fe1SAndy Fiddaman 			hostname = "[::1]";
1473*5c4a5fe1SAndy Fiddaman #endif
1474*5c4a5fe1SAndy Fiddaman 		}
1475*5c4a5fe1SAndy Fiddaman 
1476*5c4a5fe1SAndy Fiddaman 		memset(&hints, '\0', sizeof (hints));
1477*5c4a5fe1SAndy Fiddaman 		hints.ai_family = AF_UNSPEC;
1478*5c4a5fe1SAndy Fiddaman 		hints.ai_socktype = SOCK_STREAM;
1479*5c4a5fe1SAndy Fiddaman 		hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
1480*5c4a5fe1SAndy Fiddaman 
1481*5c4a5fe1SAndy Fiddaman 		if ((e = getaddrinfo(hostname, servname, &hints, &ai)) != 0) {
1482*5c4a5fe1SAndy Fiddaman 			rfb_printf(NULL, RFB_LOGERR, "getaddrinfo: %s",
1483*5c4a5fe1SAndy Fiddaman 			    gai_strerror(e));
1484*5c4a5fe1SAndy Fiddaman 			goto fail;
1485*5c4a5fe1SAndy Fiddaman 		}
1486*5c4a5fe1SAndy Fiddaman 
1487*5c4a5fe1SAndy Fiddaman 		s->rs_fd = socket(ai->ai_family, ai->ai_socktype, 0);
1488*5c4a5fe1SAndy Fiddaman 		if (s->rs_fd < 0) {
1489*5c4a5fe1SAndy Fiddaman 			perror("socket");
1490*5c4a5fe1SAndy Fiddaman 			freeaddrinfo(ai);
1491*5c4a5fe1SAndy Fiddaman 			goto fail;
1492*5c4a5fe1SAndy Fiddaman 		}
1493*5c4a5fe1SAndy Fiddaman 
1494*5c4a5fe1SAndy Fiddaman 		e = 1;
1495*5c4a5fe1SAndy Fiddaman 		(void) setsockopt(s->rs_fd, SOL_SOCKET, SO_REUSEADDR,
1496*5c4a5fe1SAndy Fiddaman 		    &e, sizeof (e));
1497*5c4a5fe1SAndy Fiddaman 
1498*5c4a5fe1SAndy Fiddaman 		if (bind(s->rs_fd, ai->ai_addr, ai->ai_addrlen) < 0) {
1499*5c4a5fe1SAndy Fiddaman 			perror("bind");
1500*5c4a5fe1SAndy Fiddaman 			freeaddrinfo(ai);
1501*5c4a5fe1SAndy Fiddaman 			goto fail;
1502*5c4a5fe1SAndy Fiddaman 		}
1503*5c4a5fe1SAndy Fiddaman 		freeaddrinfo(ai);
1504*5c4a5fe1SAndy Fiddaman 	}
1505*5c4a5fe1SAndy Fiddaman 
1506*5c4a5fe1SAndy Fiddaman 	if (listen(s->rs_fd, 5) < 0) {
1507*5c4a5fe1SAndy Fiddaman 		perror("listen");
1508*5c4a5fe1SAndy Fiddaman 		goto fail;
1509*5c4a5fe1SAndy Fiddaman 	}
1510*5c4a5fe1SAndy Fiddaman 
1511*5c4a5fe1SAndy Fiddaman #ifndef WITHOUT_CAPSICUM
1512*5c4a5fe1SAndy Fiddaman 	cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE);
1513*5c4a5fe1SAndy Fiddaman 	if (caph_rights_limit(s->rs_fd, &rights) == -1)
1514*5c4a5fe1SAndy Fiddaman 		errx(EX_OSERR, "Unable to apply rights for sandbox");
1515*5c4a5fe1SAndy Fiddaman #endif
1516*5c4a5fe1SAndy Fiddaman 
1517*5c4a5fe1SAndy Fiddaman 	s->rs_connevent = mevent_add(s->rs_fd, EVF_READ, rfb_accept, s);
1518*5c4a5fe1SAndy Fiddaman 	if (s->rs_connevent == NULL) {
1519*5c4a5fe1SAndy Fiddaman 		rfb_printf(NULL, RFB_LOGERR,
1520*5c4a5fe1SAndy Fiddaman 		    "Failed to set up rfb connection mevent");
1521*5c4a5fe1SAndy Fiddaman 		goto fail;
1522*5c4a5fe1SAndy Fiddaman 	}
1523*5c4a5fe1SAndy Fiddaman 
1524*5c4a5fe1SAndy Fiddaman 	list_insert_tail(&rfb_list, s);
1525*5c4a5fe1SAndy Fiddaman 
1526*5c4a5fe1SAndy Fiddaman 	/*
1527*5c4a5fe1SAndy Fiddaman 	 * Wait for first connection. Since the mevent thread is
1528*5c4a5fe1SAndy Fiddaman 	 * not yet running, we can't rely on normal incoming connection
1529*5c4a5fe1SAndy Fiddaman 	 * handling.
1530*5c4a5fe1SAndy Fiddaman 	 */
1531*5c4a5fe1SAndy Fiddaman 	if (wait != 0) {
1532*5c4a5fe1SAndy Fiddaman 		fd_set rfds;
1533*5c4a5fe1SAndy Fiddaman 		int e;
1534*5c4a5fe1SAndy Fiddaman 
1535*5c4a5fe1SAndy Fiddaman 		rfb_printf(NULL, RFB_LOGWARN,
1536*5c4a5fe1SAndy Fiddaman 		    "holding boot until first client connection");
1537*5c4a5fe1SAndy Fiddaman 
1538*5c4a5fe1SAndy Fiddaman 		for (;;) {
1539*5c4a5fe1SAndy Fiddaman 			FD_ZERO(&rfds);
1540*5c4a5fe1SAndy Fiddaman 			FD_SET(s->rs_fd, &rfds);
1541*5c4a5fe1SAndy Fiddaman 
1542*5c4a5fe1SAndy Fiddaman 			e = select(s->rs_fd + 1, &rfds, NULL, NULL, NULL);
1543*5c4a5fe1SAndy Fiddaman 			if (e < 0 && errno == EINTR)
1544*5c4a5fe1SAndy Fiddaman 				continue;
1545*5c4a5fe1SAndy Fiddaman 			if (e < 0 || FD_ISSET(s->rs_fd, &rfds))
1546*5c4a5fe1SAndy Fiddaman 				break;
1547*5c4a5fe1SAndy Fiddaman 		}
1548*5c4a5fe1SAndy Fiddaman 		rfb_printf(NULL, RFB_LOGWARN, "continuing boot");
1549*5c4a5fe1SAndy Fiddaman 	}
1550*5c4a5fe1SAndy Fiddaman 
1551*5c4a5fe1SAndy Fiddaman 	return (0);
1552*5c4a5fe1SAndy Fiddaman 
1553*5c4a5fe1SAndy Fiddaman fail:
1554*5c4a5fe1SAndy Fiddaman 	if (s->rs_fd != -1)
1555*5c4a5fe1SAndy Fiddaman 		VERIFY3S(close(s->rs_fd), ==, 0);
1556*5c4a5fe1SAndy Fiddaman 	(void) pthread_mutex_destroy(&s->rs_clientlock);
1557*5c4a5fe1SAndy Fiddaman 	list_destroy(&s->rs_clients);
1558*5c4a5fe1SAndy Fiddaman 	free(s);
1559*5c4a5fe1SAndy Fiddaman 	return (-1);
1560*5c4a5fe1SAndy Fiddaman }
1561