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