2 * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3 * Copyright (c) 2015 Leon Dang
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #ifndef WITHOUT_CAPSICUM
33 #include <sys/capsicum.h>
35 #include <sys/socket.h>
36 #include <sys/select.h>
38 #include <arpa/inet.h>
39 #include <machine/cpufunc.h>
40 #include <machine/specialreg.h>
41 #include <netinet/in.h>
47 #include <pthread_np.h>
61 #include "sockstream.h"
64 #include <openssl/des.h>
67 static int rfb_debug = 0;
68 #define DPRINTF(params) if (rfb_debug) printf params
69 #define WPRINTF(params) printf params
71 #define AUTH_LENGTH 16
72 #define PASSWD_LENGTH 8
74 #define SECURITY_TYPE_NONE 1
75 #define SECURITY_TYPE_VNC_AUTH 2
77 #define AUTH_FAILED_UNAUTH 1
78 #define AUTH_FAILED_ERROR 2
104 uint32_t *crc; /* WxH crc cells */
105 uint32_t *crc_tmp; /* buffer to store single crc row */
106 int crc_width, crc_height;
123 struct rfb_srvr_info {
126 struct rfb_pixfmt pixfmt;
130 struct rfb_pixfmt_msg {
133 struct rfb_pixfmt pixfmt;
136 #define RFB_ENCODING_RAW 0
137 #define RFB_ENCODING_ZLIB 6
138 #define RFB_ENCODING_RESIZE -223
140 #define RFB_MAX_WIDTH 2000
141 #define RFB_MAX_HEIGHT 1200
142 #define RFB_ZLIB_BUFSZ RFB_MAX_WIDTH*RFB_MAX_HEIGHT*4
144 /* percentage changes to screen before sending the entire screen */
145 #define RFB_SEND_ALL_THRESH 25
153 struct rfb_updt_msg {
176 struct rfb_srvr_updt_msg {
182 struct rfb_srvr_rect_hdr {
190 struct rfb_cuttext_msg {
198 rfb_send_server_init_msg(int cfd)
200 struct bhyvegc_image *gc_image;
201 struct rfb_srvr_info sinfo;
203 gc_image = console_get_image();
205 sinfo.width = htons(gc_image->width);
206 sinfo.height = htons(gc_image->height);
207 sinfo.pixfmt.bpp = 32;
208 sinfo.pixfmt.depth = 32;
209 sinfo.pixfmt.bigendian = 0;
210 sinfo.pixfmt.truecolor = 1;
211 sinfo.pixfmt.red_max = htons(255);
212 sinfo.pixfmt.green_max = htons(255);
213 sinfo.pixfmt.blue_max = htons(255);
214 sinfo.pixfmt.red_shift = 16;
215 sinfo.pixfmt.green_shift = 8;
216 sinfo.pixfmt.blue_shift = 0;
217 sinfo.namelen = htonl(strlen("bhyve"));
218 (void)stream_write(cfd, &sinfo, sizeof(sinfo));
219 (void)stream_write(cfd, "bhyve", strlen("bhyve"));
223 rfb_send_resize_update_msg(struct rfb_softc *rc, int cfd)
225 struct rfb_srvr_updt_msg supdt_msg;
226 struct rfb_srvr_rect_hdr srect_hdr;
228 /* Number of rectangles: 1 */
231 supdt_msg.numrects = htons(1);
232 stream_write(cfd, &supdt_msg, sizeof(struct rfb_srvr_updt_msg));
234 /* Rectangle header */
235 srect_hdr.x = htons(0);
236 srect_hdr.y = htons(0);
237 srect_hdr.width = htons(rc->width);
238 srect_hdr.height = htons(rc->height);
239 srect_hdr.encoding = htonl(RFB_ENCODING_RESIZE);
240 stream_write(cfd, &srect_hdr, sizeof(struct rfb_srvr_rect_hdr));
244 rfb_recv_set_pixfmt_msg(struct rfb_softc *rc, int cfd)
246 struct rfb_pixfmt_msg pixfmt_msg;
248 (void)stream_read(cfd, ((void *)&pixfmt_msg)+1, sizeof(pixfmt_msg)-1);
253 rfb_recv_set_encodings_msg(struct rfb_softc *rc, int cfd)
255 struct rfb_enc_msg enc_msg;
259 assert((sizeof(enc_msg) - 1) == 3);
260 (void)stream_read(cfd, ((void *)&enc_msg)+1, sizeof(enc_msg)-1);
262 for (i = 0; i < htons(enc_msg.numencs); i++) {
263 (void)stream_read(cfd, &encoding, sizeof(encoding));
264 switch (htonl(encoding)) {
265 case RFB_ENCODING_RAW:
266 rc->enc_raw_ok = true;
268 case RFB_ENCODING_ZLIB:
269 rc->enc_zlib_ok = true;
270 deflateInit(&rc->zstream, Z_BEST_SPEED);
272 case RFB_ENCODING_RESIZE:
273 rc->enc_resize_ok = true;
280 * Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only
282 static __inline uint32_t
283 fast_crc32(void *buf, int len, uint32_t crcval)
285 uint32_t q = len / sizeof(uint32_t);
286 uint32_t *p = (uint32_t *)buf;
290 ".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
292 :"0" (crcval), "c" (*p)
302 rfb_send_rect(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc,
303 int x, int y, int w, int h)
305 struct rfb_srvr_updt_msg supdt_msg;
306 struct rfb_srvr_rect_hdr srect_hdr;
308 ssize_t nwrite, total;
314 * Send a single rectangle of the given x, y, w h dimensions.
317 /* Number of rectangles: 1 */
320 supdt_msg.numrects = htons(1);
321 nwrite = stream_write(cfd, &supdt_msg,
322 sizeof(struct rfb_srvr_updt_msg));
327 /* Rectangle header */
328 srect_hdr.x = htons(x);
329 srect_hdr.y = htons(y);
330 srect_hdr.width = htons(w);
331 srect_hdr.height = htons(h);
334 w *= sizeof(uint32_t);
335 if (rc->enc_zlib_ok) {
337 rc->zstream.total_in = 0;
338 rc->zstream.total_out = 0;
339 for (p = &gc->data[y * gc->width + x]; y < h; y++) {
340 rc->zstream.next_in = (Bytef *)p;
341 rc->zstream.avail_in = w;
342 rc->zstream.next_out = (Bytef *)zbufp;
343 rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16 -
344 rc->zstream.total_out;
345 rc->zstream.data_type = Z_BINARY;
347 /* Compress with zlib */
348 err = deflate(&rc->zstream, Z_SYNC_FLUSH);
350 WPRINTF(("zlib[rect] deflate err: %d\n", err));
351 rc->enc_zlib_ok = false;
352 deflateEnd(&rc->zstream);
355 zbufp = rc->zbuf + rc->zstream.total_out;
358 srect_hdr.encoding = htonl(RFB_ENCODING_ZLIB);
359 nwrite = stream_write(cfd, &srect_hdr,
360 sizeof(struct rfb_srvr_rect_hdr));
364 zlen = htonl(rc->zstream.total_out);
365 nwrite = stream_write(cfd, &zlen, sizeof(uint32_t));
368 return (stream_write(cfd, rc->zbuf, rc->zstream.total_out));
375 for (p = &gc->data[y * gc->width + x]; y < h; y++) {
382 srect_hdr.encoding = htonl(RFB_ENCODING_RAW);
383 nwrite = stream_write(cfd, &srect_hdr,
384 sizeof(struct rfb_srvr_rect_hdr));
388 total = stream_write(cfd, rc->zbuf, total);
394 rfb_send_all(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc)
396 struct rfb_srvr_updt_msg supdt_msg;
397 struct rfb_srvr_rect_hdr srect_hdr;
403 * Send the whole thing
406 /* Number of rectangles: 1 */
409 supdt_msg.numrects = htons(1);
410 nwrite = stream_write(cfd, &supdt_msg,
411 sizeof(struct rfb_srvr_updt_msg));
415 /* Rectangle header */
418 srect_hdr.width = htons(gc->width);
419 srect_hdr.height = htons(gc->height);
420 if (rc->enc_zlib_ok) {
421 rc->zstream.next_in = (Bytef *)gc->data;
422 rc->zstream.avail_in = gc->width * gc->height *
424 rc->zstream.next_out = (Bytef *)rc->zbuf;
425 rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16;
426 rc->zstream.data_type = Z_BINARY;
428 rc->zstream.total_in = 0;
429 rc->zstream.total_out = 0;
431 /* Compress with zlib */
432 err = deflate(&rc->zstream, Z_SYNC_FLUSH);
434 WPRINTF(("zlib deflate err: %d\n", err));
435 rc->enc_zlib_ok = false;
436 deflateEnd(&rc->zstream);
440 srect_hdr.encoding = htonl(RFB_ENCODING_ZLIB);
441 nwrite = stream_write(cfd, &srect_hdr,
442 sizeof(struct rfb_srvr_rect_hdr));
446 zlen = htonl(rc->zstream.total_out);
447 nwrite = stream_write(cfd, &zlen, sizeof(uint32_t));
450 return (stream_write(cfd, rc->zbuf, rc->zstream.total_out));
454 srect_hdr.encoding = htonl(RFB_ENCODING_RAW);
455 nwrite = stream_write(cfd, &srect_hdr,
456 sizeof(struct rfb_srvr_rect_hdr));
460 nwrite = stream_write(cfd, gc->data,
461 gc->width * gc->height * sizeof(uint32_t));
466 #define PIX_PER_CELL 32
467 #define PIXCELL_SHIFT 5
468 #define PIXCELL_MASK 0x1F
471 rfb_send_screen(struct rfb_softc *rc, int cfd, int all)
473 struct bhyvegc_image *gc_image;
476 int celly, cellwidth;
480 int rem_x, rem_y; /* remainder for resolutions not x32 pixels ratio */
482 uint32_t *crc_p, *orig_crc;
486 gc_image = console_get_image();
488 pthread_mutex_lock(&rc->mtx);
490 pthread_mutex_unlock(&rc->mtx);
494 pthread_mutex_unlock(&rc->mtx);
499 retval = rfb_send_all(rc, cfd, gc_image);
504 * Calculate the checksum for each 32x32 cell. Send each that
505 * has changed since the last scan.
508 /* Resolution changed */
510 rc->crc_width = gc_image->width;
511 rc->crc_height = gc_image->height;
515 xcells = howmany(rc->crc_width, PIX_PER_CELL);
516 ycells = howmany(rc->crc_height, PIX_PER_CELL);
518 rem_x = w & PIXCELL_MASK;
520 rem_y = h & PIXCELL_MASK;
522 rem_y = PIX_PER_CELL;
527 * Go through all cells and calculate crc. If significant number
528 * of changes, then send entire screen.
529 * crc_tmp is dual purpose: to store the new crc and to flag as
530 * a cell that has changed.
532 crc_p = rc->crc_tmp - xcells;
533 orig_crc = rc->crc - xcells;
535 memset(rc->crc_tmp, 0, sizeof(uint32_t) * xcells * ycells);
536 for (y = 0; y < h; y++) {
537 if ((y & PIXCELL_MASK) == 0) {
542 for (x = 0; x < xcells; x++) {
544 crc_p[x] = fast_crc32(p,
545 PIX_PER_CELL * sizeof(uint32_t),
548 crc_p[x] = (uint32_t)crc32(crc_p[x],
550 PIX_PER_CELL * sizeof(uint32_t));
554 /* check for crc delta if last row in cell */
555 if ((y & PIXCELL_MASK) == PIXCELL_MASK || y == (h-1)) {
556 if (orig_crc[x] != crc_p[x]) {
557 orig_crc[x] = crc_p[x];
568 crc_p[x] = fast_crc32(p,
569 rem_x * sizeof(uint32_t),
572 crc_p[x] = (uint32_t)crc32(crc_p[x],
574 rem_x * sizeof(uint32_t));
577 if ((y & PIXCELL_MASK) == PIXCELL_MASK || y == (h-1)) {
578 if (orig_crc[x] != crc_p[x]) {
579 orig_crc[x] = crc_p[x];
589 /* If number of changes is > THRESH percent, send the whole screen */
590 if (((changes * 100) / (xcells * ycells)) >= RFB_SEND_ALL_THRESH) {
591 retval = rfb_send_all(rc, cfd, gc_image);
595 /* Go through all cells, and send only changed ones */
597 for (y = 0; y < h; y += PIX_PER_CELL) {
598 /* previous cell's row */
599 celly = (y >> PIXCELL_SHIFT);
601 /* Delta check crc to previous set */
602 for (x = 0; x < xcells; x++) {
606 if (x == (xcells - 1) && rem_x > 0)
609 cellwidth = PIX_PER_CELL;
610 nwrite = rfb_send_rect(rc, cfd,
613 celly * PIX_PER_CELL,
615 y + PIX_PER_CELL >= h ? rem_y : PIX_PER_CELL);
625 pthread_mutex_lock(&rc->mtx);
627 pthread_mutex_unlock(&rc->mtx);
634 rfb_recv_update_msg(struct rfb_softc *rc, int cfd, int discardonly)
636 struct rfb_updt_msg updt_msg;
637 struct bhyvegc_image *gc_image;
639 (void)stream_read(cfd, ((void *)&updt_msg) + 1 , sizeof(updt_msg) - 1);
642 gc_image = console_get_image();
644 updt_msg.x = htons(updt_msg.x);
645 updt_msg.y = htons(updt_msg.y);
646 updt_msg.width = htons(updt_msg.width);
647 updt_msg.height = htons(updt_msg.height);
649 if (updt_msg.width != gc_image->width ||
650 updt_msg.height != gc_image->height) {
651 rc->width = gc_image->width;
652 rc->height = gc_image->height;
653 if (rc->enc_resize_ok)
654 rfb_send_resize_update_msg(rc, cfd);
660 rfb_send_screen(rc, cfd, 1);
664 rfb_recv_key_msg(struct rfb_softc *rc, int cfd)
666 struct rfb_key_msg key_msg;
668 (void)stream_read(cfd, ((void *)&key_msg) + 1, sizeof(key_msg) - 1);
670 console_key_event(key_msg.down, htonl(key_msg.code));
674 rfb_recv_ptr_msg(struct rfb_softc *rc, int cfd)
676 struct rfb_ptr_msg ptr_msg;
678 (void)stream_read(cfd, ((void *)&ptr_msg) + 1, sizeof(ptr_msg) - 1);
680 console_ptr_event(ptr_msg.button, htons(ptr_msg.x), htons(ptr_msg.y));
684 rfb_recv_cuttext_msg(struct rfb_softc *rc, int cfd)
686 struct rfb_cuttext_msg ct_msg;
687 unsigned char buf[32];
690 len = stream_read(cfd, ((void *)&ct_msg) + 1, sizeof(ct_msg) - 1);
691 ct_msg.length = htonl(ct_msg.length);
692 while (ct_msg.length > 0) {
693 len = stream_read(cfd, buf, ct_msg.length > sizeof(buf) ?
694 sizeof(buf) : ct_msg.length);
695 ct_msg.length -= len;
700 timeval_delta(struct timeval *prev, struct timeval *now)
703 n1 = now->tv_sec * 1000000 + now->tv_usec;
704 n2 = prev->tv_sec * 1000000 + prev->tv_usec;
709 rfb_wr_thr(void *arg)
711 struct rfb_softc *rc;
714 struct timeval prev_tv;
724 while (rc->cfd >= 0) {
730 err = select(cfd+1, &rfds, NULL, NULL, &tv);
734 /* Determine if its time to push screen; ~24hz */
735 gettimeofday(&tv, NULL);
736 tdiff = timeval_delta(&prev_tv, &tv);
738 prev_tv.tv_sec = tv.tv_sec;
739 prev_tv.tv_usec = tv.tv_usec;
740 if (rfb_send_screen(rc, cfd, 0) <= 0) {
745 usleep(40000 - tdiff);
753 rfb_handle(struct rfb_softc *rc, int cfd)
755 const char *vbuf = "RFB 003.008\n";
756 unsigned char buf[80];
757 unsigned char *message;
760 unsigned char challenge[AUTH_LENGTH];
761 unsigned char keystr[PASSWD_LENGTH];
762 unsigned char crypt_expected[AUTH_LENGTH];
774 /* 1a. Send server version */
775 stream_write(cfd, vbuf, strlen(vbuf));
777 /* 1b. Read client version */
778 len = read(cfd, buf, sizeof(buf));
780 /* 2a. Send security type */
784 buf[1] = SECURITY_TYPE_VNC_AUTH;
786 buf[1] = SECURITY_TYPE_NONE;
788 buf[1] = SECURITY_TYPE_NONE;
791 stream_write(cfd, buf, 2);
793 /* 2b. Read agreed security type */
794 len = stream_read(cfd, buf, 1);
796 /* 2c. Do VNC authentication */
798 case SECURITY_TYPE_NONE:
801 case SECURITY_TYPE_VNC_AUTH:
803 * The client encrypts the challenge with DES, using a password
804 * supplied by the user as the key.
805 * To form the key, the password is truncated to
806 * eight characters, or padded with null bytes on the right.
807 * The client then sends the resulting 16-bytes response.
810 strncpy(keystr, rc->password, PASSWD_LENGTH);
812 /* VNC clients encrypts the challenge with all the bit fields
813 * in each byte of the password mirrored.
814 * Here we flip each byte of the keystr.
816 for (i = 0; i < PASSWD_LENGTH; i++) {
817 keystr[i] = (keystr[i] & 0xF0) >> 4
818 | (keystr[i] & 0x0F) << 4;
819 keystr[i] = (keystr[i] & 0xCC) >> 2
820 | (keystr[i] & 0x33) << 2;
821 keystr[i] = (keystr[i] & 0xAA) >> 1
822 | (keystr[i] & 0x55) << 1;
825 /* Initialize a 16-byte random challenge */
826 arc4random_buf(challenge, sizeof(challenge));
827 stream_write(cfd, challenge, AUTH_LENGTH);
829 /* Receive the 16-byte challenge response */
830 stream_read(cfd, buf, AUTH_LENGTH);
832 memcpy(crypt_expected, challenge, AUTH_LENGTH);
834 /* Encrypt the Challenge with DES */
835 DES_set_key((const_DES_cblock *)keystr, &ks);
836 DES_ecb_encrypt((const_DES_cblock *)challenge,
837 (const_DES_cblock *)crypt_expected,
839 DES_ecb_encrypt((const_DES_cblock *)(challenge + PASSWD_LENGTH),
840 (const_DES_cblock *)(crypt_expected +
844 if (memcmp(crypt_expected, buf, AUTH_LENGTH) != 0) {
845 message = "Auth Failed: Invalid Password.";
851 WPRINTF(("Auth not supported, no OpenSSL in your system"));
857 /* 2d. Write back a status */
858 stream_write(cfd, &sres, 4);
861 *((uint32_t *) buf) = htonl(strlen(message));
862 stream_write(cfd, buf, 4);
863 stream_write(cfd, message, strlen(message));
867 /* 3a. Read client shared-flag byte */
868 len = stream_read(cfd, buf, 1);
870 /* 4a. Write server-init info */
871 rfb_send_server_init_msg(cfd);
874 rc->zbuf = malloc(RFB_ZLIB_BUFSZ + 16);
875 assert(rc->zbuf != NULL);
878 rfb_send_screen(rc, cfd, 1);
880 pthread_create(&tid, NULL, rfb_wr_thr, rc);
881 pthread_set_name_np(tid, "rfbout");
883 /* Now read in client requests. 1st byte identifies type */
885 len = read(cfd, buf, 1);
887 DPRINTF(("rfb client exiting\r\n"));
893 rfb_recv_set_pixfmt_msg(rc, cfd);
896 rfb_recv_set_encodings_msg(rc, cfd);
899 rfb_recv_update_msg(rc, cfd, 1);
902 rfb_recv_key_msg(rc, cfd);
905 rfb_recv_ptr_msg(rc, cfd);
908 rfb_recv_cuttext_msg(rc, cfd);
911 WPRINTF(("rfb unknown cli-code %d!\n", buf[0] & 0xff));
917 pthread_join(tid, NULL);
919 deflateEnd(&rc->zstream);
925 struct rfb_softc *rc;
933 sigaddset(&set, SIGPIPE);
934 if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
935 perror("pthread_sigmask");
940 rc->enc_raw_ok = false;
941 rc->enc_zlib_ok = false;
942 rc->enc_resize_ok = false;
944 cfd = accept(rc->sfd, NULL, NULL);
946 pthread_mutex_lock(&rc->mtx);
947 pthread_cond_signal(&rc->cond);
948 pthread_mutex_unlock(&rc->mtx);
960 sse42_supported(void)
962 u_int cpu_registers[4], ecx;
964 do_cpuid(1, cpu_registers);
966 ecx = cpu_registers[2];
968 return ((ecx & CPUID2_SSE42) != 0);
972 rfb_init(char *hostname, int port, int wait, char *password)
974 struct rfb_softc *rc;
975 struct sockaddr_in sin;
977 #ifndef WITHOUT_CAPSICUM
981 rc = calloc(1, sizeof(struct rfb_softc));
983 rc->crc = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32),
985 rc->crc_tmp = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32),
987 rc->crc_width = RFB_MAX_WIDTH;
988 rc->crc_height = RFB_MAX_HEIGHT;
990 rc->password = password;
992 rc->sfd = socket(AF_INET, SOCK_STREAM, 0);
998 setsockopt(rc->sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1000 sin.sin_len = sizeof(sin);
1001 sin.sin_family = AF_INET;
1002 sin.sin_port = port ? htons(port) : htons(5900);
1003 if (hostname && strlen(hostname) > 0)
1004 inet_pton(AF_INET, hostname, &(sin.sin_addr));
1006 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1008 if (bind(rc->sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
1013 if (listen(rc->sfd, 1) < 0) {
1018 #ifndef WITHOUT_CAPSICUM
1019 cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE);
1020 if (cap_rights_limit(rc->sfd, &rights) == -1 && errno != ENOSYS)
1021 errx(EX_OSERR, "Unable to apply rights for sandbox");
1024 rc->hw_crc = sse42_supported();
1026 rc->conn_wait = wait;
1028 pthread_mutex_init(&rc->mtx, NULL);
1029 pthread_cond_init(&rc->cond, NULL);
1032 pthread_create(&rc->tid, NULL, rfb_thr, rc);
1033 pthread_set_name_np(rc->tid, "rfb");
1036 DPRINTF(("Waiting for rfb client...\n"));
1037 pthread_mutex_lock(&rc->mtx);
1038 pthread_cond_wait(&rc->cond, &rc->mtx);
1039 pthread_mutex_unlock(&rc->mtx);