Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/rfb.c
Show All 33 Lines | |||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
#include <sys/capsicum.h> | #include <sys/capsicum.h> | ||||
#endif | #endif | ||||
#include <sys/endian.h> | #include <sys/endian.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/select.h> | #include <sys/select.h> | ||||
#include <sys/time.h> | #include <sys/time.h> | ||||
#include <arpa/inet.h> | #include <arpa/inet.h> | ||||
#include <stdatomic.h> | |||||
jhb: For new code it might be nice to use C11 atomics instead? | |||||
#include <machine/cpufunc.h> | #include <machine/cpufunc.h> | ||||
#include <machine/specialreg.h> | #include <machine/specialreg.h> | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netdb.h> | #include <netdb.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
#include <capsicum_helpers.h> | #include <capsicum_helpers.h> | ||||
Show All 17 Lines | |||||
#include "console.h" | #include "console.h" | ||||
#include "rfb.h" | #include "rfb.h" | ||||
#include "sockstream.h" | #include "sockstream.h" | ||||
#ifndef NO_OPENSSL | #ifndef NO_OPENSSL | ||||
#include <openssl/des.h> | #include <openssl/des.h> | ||||
#endif | #endif | ||||
/* Delays in microseconds */ | |||||
#define CFD_SEL_DELAY 10000 | |||||
#define SCREEN_REFRESH_DELAY 33300 /* 30Hz */ | |||||
#define SCREEN_POLL_DELAY (SCREEN_REFRESH_DELAY / 2) | |||||
static int rfb_debug = 0; | static int rfb_debug = 0; | ||||
#define DPRINTF(params) if (rfb_debug) PRINTLN params | #define DPRINTF(params) if (rfb_debug) PRINTLN params | ||||
#define WPRINTF(params) PRINTLN params | #define WPRINTF(params) PRINTLN params | ||||
#define VERSION_LENGTH 12 | #define VERSION_LENGTH 12 | ||||
#define AUTH_LENGTH 16 | #define AUTH_LENGTH 16 | ||||
#define PASSWD_LENGTH 8 | #define PASSWD_LENGTH 8 | ||||
/* Protocol versions */ | |||||
#define CVERS_3_3 '3' | |||||
#define CVERS_3_7 '7' | |||||
#define CVERS_3_8 '8' | |||||
/* Client-to-server msg types */ | |||||
#define CS_SET_PIXEL_FORMAT 0 | |||||
#define CS_SET_ENCODINGS 2 | |||||
#define CS_UPDATE_MSG 3 | |||||
#define CS_KEY_EVENT 4 | |||||
#define CS_POINTER_EVENT 5 | |||||
#define CS_CUT_TEXT 6 | |||||
#define SECURITY_TYPE_NONE 1 | #define SECURITY_TYPE_NONE 1 | ||||
#define SECURITY_TYPE_VNC_AUTH 2 | #define SECURITY_TYPE_VNC_AUTH 2 | ||||
#define AUTH_FAILED_UNAUTH 1 | #define AUTH_FAILED_UNAUTH 1 | ||||
#define AUTH_FAILED_ERROR 2 | #define AUTH_FAILED_ERROR 2 | ||||
struct rfb_softc { | struct rfb_softc { | ||||
int sfd; | int sfd; | ||||
pthread_t tid; | pthread_t tid; | ||||
int cfd; | int cfd; | ||||
int width, height; | int width, height; | ||||
char *password; | char *password; | ||||
bool enc_raw_ok; | bool enc_raw_ok; | ||||
bool enc_zlib_ok; | bool enc_zlib_ok; | ||||
bool enc_resize_ok; | bool enc_resize_ok; | ||||
z_stream zstream; | z_stream zstream; | ||||
uint8_t *zbuf; | uint8_t *zbuf; | ||||
int zbuflen; | int zbuflen; | ||||
int conn_wait; | int conn_wait; | ||||
int sending; | int wrcount; | ||||
atomic_bool sending; | |||||
atomic_bool pending; | |||||
atomic_bool update_all; | |||||
atomic_bool input_detected; | |||||
pthread_mutex_t mtx; | pthread_mutex_t mtx; | ||||
pthread_cond_t cond; | pthread_cond_t cond; | ||||
int hw_crc; | int hw_crc; | ||||
uint32_t *crc; /* WxH crc cells */ | uint32_t *crc; /* WxH crc cells */ | ||||
uint32_t *crc_tmp; /* buffer to store single crc row */ | uint32_t *crc_tmp; /* buffer to store single crc row */ | ||||
int crc_width, crc_height; | int crc_width, crc_height; | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | |||||
}; | }; | ||||
struct rfb_cuttext_msg { | struct rfb_cuttext_msg { | ||||
uint8_t type; | uint8_t type; | ||||
uint8_t padding[3]; | uint8_t padding[3]; | ||||
uint32_t length; | uint32_t length; | ||||
}; | }; | ||||
static void | static void | ||||
rfb_send_server_init_msg(int cfd) | rfb_send_server_init_msg(int cfd) | ||||
{ | { | ||||
struct bhyvegc_image *gc_image; | struct bhyvegc_image *gc_image; | ||||
struct rfb_srvr_info sinfo; | struct rfb_srvr_info sinfo; | ||||
gc_image = console_get_image(); | gc_image = console_get_image(); | ||||
sinfo.width = htons(gc_image->width); | sinfo.width = htons(gc_image->width); | ||||
sinfo.height = htons(gc_image->height); | sinfo.height = htons(gc_image->height); | ||||
sinfo.pixfmt.bpp = 32; | sinfo.pixfmt.bpp = 32; | ||||
sinfo.pixfmt.depth = 32; | sinfo.pixfmt.depth = 32; | ||||
sinfo.pixfmt.bigendian = 0; | sinfo.pixfmt.bigendian = 0; | ||||
sinfo.pixfmt.truecolor = 1; | sinfo.pixfmt.truecolor = 1; | ||||
sinfo.pixfmt.red_max = htons(255); | sinfo.pixfmt.red_max = htons(255); | ||||
sinfo.pixfmt.green_max = htons(255); | sinfo.pixfmt.green_max = htons(255); | ||||
sinfo.pixfmt.blue_max = htons(255); | sinfo.pixfmt.blue_max = htons(255); | ||||
sinfo.pixfmt.red_shift = 16; | sinfo.pixfmt.red_shift = 16; | ||||
sinfo.pixfmt.green_shift = 8; | sinfo.pixfmt.green_shift = 8; | ||||
sinfo.pixfmt.blue_shift = 0; | sinfo.pixfmt.blue_shift = 0; | ||||
sinfo.pixfmt.pad[0] = 0; | |||||
sinfo.pixfmt.pad[1] = 0; | |||||
sinfo.pixfmt.pad[2] = 0; | |||||
sinfo.namelen = htonl(strlen("bhyve")); | sinfo.namelen = htonl(strlen("bhyve")); | ||||
(void)stream_write(cfd, &sinfo, sizeof(sinfo)); | (void)stream_write(cfd, &sinfo, sizeof(sinfo)); | ||||
(void)stream_write(cfd, "bhyve", strlen("bhyve")); | (void)stream_write(cfd, "bhyve", strlen("bhyve")); | ||||
} | } | ||||
static void | static void | ||||
rfb_send_resize_update_msg(struct rfb_softc *rc, int cfd) | rfb_send_resize_update_msg(struct rfb_softc *rc, int cfd) | ||||
{ | { | ||||
Show All 26 Lines | |||||
static void | static void | ||||
rfb_recv_set_encodings_msg(struct rfb_softc *rc, int cfd) | rfb_recv_set_encodings_msg(struct rfb_softc *rc, int cfd) | ||||
{ | { | ||||
struct rfb_enc_msg enc_msg; | struct rfb_enc_msg enc_msg; | ||||
int i; | int i; | ||||
uint32_t encoding; | uint32_t encoding; | ||||
assert((sizeof(enc_msg) - 1) == 3); | |||||
(void)stream_read(cfd, ((void *)&enc_msg)+1, sizeof(enc_msg)-1); | (void)stream_read(cfd, ((void *)&enc_msg)+1, sizeof(enc_msg)-1); | ||||
for (i = 0; i < htons(enc_msg.numencs); i++) { | for (i = 0; i < htons(enc_msg.numencs); i++) { | ||||
(void)stream_read(cfd, &encoding, sizeof(encoding)); | (void)stream_read(cfd, &encoding, sizeof(encoding)); | ||||
switch (htonl(encoding)) { | switch (htonl(encoding)) { | ||||
case RFB_ENCODING_RAW: | case RFB_ENCODING_RAW: | ||||
rc->enc_raw_ok = true; | rc->enc_raw_ok = true; | ||||
break; | break; | ||||
Show All 26 Lines | asm volatile ( | ||||
:"0" (crcval), "c" (*p) | :"0" (crcval), "c" (*p) | ||||
); | ); | ||||
p++; | p++; | ||||
} | } | ||||
return (crcval); | return (crcval); | ||||
} | } | ||||
static int | |||||
rfb_send_update_header(struct rfb_softc *rc, int cfd, int numrects) | |||||
{ | |||||
struct rfb_srvr_updt_msg supdt_msg; | |||||
supdt_msg.type = 0; | |||||
supdt_msg.pad = 0; | |||||
supdt_msg.numrects = htons(numrects); | |||||
return stream_write(cfd, &supdt_msg, | |||||
sizeof(struct rfb_srvr_updt_msg)); | |||||
} | |||||
static int | static int | ||||
rfb_send_rect(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc, | rfb_send_rect(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc, | ||||
int x, int y, int w, int h) | int x, int y, int w, int h) | ||||
{ | { | ||||
struct rfb_srvr_updt_msg supdt_msg; | |||||
struct rfb_srvr_rect_hdr srect_hdr; | struct rfb_srvr_rect_hdr srect_hdr; | ||||
unsigned long zlen; | unsigned long zlen; | ||||
ssize_t nwrite, total; | ssize_t nwrite, total; | ||||
int err; | int err; | ||||
uint32_t *p; | uint32_t *p; | ||||
uint8_t *zbufp; | uint8_t *zbufp; | ||||
/* | /* | ||||
* Send a single rectangle of the given x, y, w h dimensions. | * Send a single rectangle of the given x, y, w h dimensions. | ||||
*/ | */ | ||||
/* Number of rectangles: 1 */ | |||||
supdt_msg.type = 0; | |||||
supdt_msg.pad = 0; | |||||
supdt_msg.numrects = htons(1); | |||||
nwrite = stream_write(cfd, &supdt_msg, | |||||
sizeof(struct rfb_srvr_updt_msg)); | |||||
if (nwrite <= 0) | |||||
return (nwrite); | |||||
/* Rectangle header */ | /* Rectangle header */ | ||||
srect_hdr.x = htons(x); | srect_hdr.x = htons(x); | ||||
srect_hdr.y = htons(y); | srect_hdr.y = htons(y); | ||||
srect_hdr.width = htons(w); | srect_hdr.width = htons(w); | ||||
srect_hdr.height = htons(h); | srect_hdr.height = htons(h); | ||||
h = y + h; | h = y + h; | ||||
w *= sizeof(uint32_t); | w *= sizeof(uint32_t); | ||||
▲ Show 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | doraw: | ||||
return (nwrite); | return (nwrite); | ||||
} | } | ||||
#define PIX_PER_CELL 32 | #define PIX_PER_CELL 32 | ||||
#define PIXCELL_SHIFT 5 | #define PIXCELL_SHIFT 5 | ||||
#define PIXCELL_MASK 0x1F | #define PIXCELL_MASK 0x1F | ||||
static int | static int | ||||
rfb_send_screen(struct rfb_softc *rc, int cfd, int all) | rfb_send_screen(struct rfb_softc *rc, int cfd) | ||||
{ | { | ||||
struct bhyvegc_image *gc_image; | struct bhyvegc_image *gc_image; | ||||
ssize_t nwrite; | ssize_t nwrite; | ||||
int x, y; | int x, y; | ||||
int celly, cellwidth; | int celly, cellwidth; | ||||
int xcells, ycells; | int xcells, ycells; | ||||
int w, h; | int w, h; | ||||
uint32_t *p; | uint32_t *p; | ||||
int rem_x, rem_y; /* remainder for resolutions not x32 pixels ratio */ | int rem_x, rem_y; /* remainder for resolutions not x32 pixels ratio */ | ||||
int retval; | int retval; | ||||
uint32_t *crc_p, *orig_crc; | uint32_t *crc_p, *orig_crc; | ||||
int changes; | int changes; | ||||
bool expected; | |||||
/* Return if another thread sending */ | |||||
expected = false; | |||||
if (atomic_compare_exchange_strong(&rc->sending, &expected, true) == false) | |||||
return (1); | |||||
retval = 1; | |||||
/* Updates require a preceding update request */ | |||||
if (atomic_exchange(&rc->pending, false) == false) | |||||
goto done; | |||||
console_refresh(); | console_refresh(); | ||||
gc_image = console_get_image(); | gc_image = console_get_image(); | ||||
pthread_mutex_lock(&rc->mtx); | /* Clear old CRC values when the size changes */ | ||||
if (rc->sending) { | if (rc->crc_width != gc_image->width || | ||||
pthread_mutex_unlock(&rc->mtx); | rc->crc_height != gc_image->height) { | ||||
return (1); | memset(rc->crc, 0, sizeof(uint32_t) * | ||||
howmany(RFB_MAX_WIDTH, PIX_PER_CELL) * | |||||
howmany(RFB_MAX_HEIGHT, PIX_PER_CELL)); | |||||
rc->crc_width = gc_image->width; | |||||
rc->crc_height = gc_image->height; | |||||
} | } | ||||
rc->sending = 1; | |||||
pthread_mutex_unlock(&rc->mtx); | |||||
retval = 0; | /* A size update counts as an update in itself */ | ||||
if (rc->width != gc_image->width || | |||||
rc->height != gc_image->height) { | |||||
rc->width = gc_image->width; | |||||
rc->height = gc_image->height; | |||||
if (rc->enc_resize_ok) { | |||||
rfb_send_resize_update_msg(rc, cfd); | |||||
rc->update_all = true; | |||||
goto done; | |||||
} | |||||
} | |||||
if (all) { | if (atomic_exchange(&rc->update_all, false) == true) { | ||||
retval = rfb_send_all(rc, cfd, gc_image); | retval = rfb_send_all(rc, cfd, gc_image); | ||||
goto done; | goto done; | ||||
} | } | ||||
/* | /* | ||||
* Calculate the checksum for each 32x32 cell. Send each that | * Calculate the checksum for each 32x32 cell. Send each that | ||||
* has changed since the last scan. | * has changed since the last scan. | ||||
*/ | */ | ||||
/* Resolution changed */ | |||||
rc->crc_width = gc_image->width; | |||||
rc->crc_height = gc_image->height; | |||||
w = rc->crc_width; | w = rc->crc_width; | ||||
h = rc->crc_height; | h = rc->crc_height; | ||||
xcells = howmany(rc->crc_width, PIX_PER_CELL); | xcells = howmany(rc->crc_width, PIX_PER_CELL); | ||||
ycells = howmany(rc->crc_height, PIX_PER_CELL); | ycells = howmany(rc->crc_height, PIX_PER_CELL); | ||||
rem_x = w & PIXCELL_MASK; | rem_x = w & PIXCELL_MASK; | ||||
rem_y = h & PIXCELL_MASK; | rem_y = h & PIXCELL_MASK; | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | for (x = 0; x < xcells; x++) { | ||||
changes++; | changes++; | ||||
} else { | } else { | ||||
crc_p[x] = 0; | crc_p[x] = 0; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* | |||||
* We only send the update if there are changes. | |||||
* Restore the pending flag since it was unconditionally cleared | |||||
* above. | |||||
*/ | |||||
if (!changes) { | |||||
rc->pending = true; | |||||
goto done; | |||||
} | |||||
/* If number of changes is > THRESH percent, send the whole screen */ | /* If number of changes is > THRESH percent, send the whole screen */ | ||||
if (((changes * 100) / (xcells * ycells)) >= RFB_SEND_ALL_THRESH) { | if (((changes * 100) / (xcells * ycells)) >= RFB_SEND_ALL_THRESH) { | ||||
retval = rfb_send_all(rc, cfd, gc_image); | retval = rfb_send_all(rc, cfd, gc_image); | ||||
goto done; | goto done; | ||||
} | } | ||||
rfb_send_update_header(rc, cfd, changes); | |||||
/* Go through all cells, and send only changed ones */ | /* Go through all cells, and send only changed ones */ | ||||
crc_p = rc->crc_tmp; | crc_p = rc->crc_tmp; | ||||
for (y = 0; y < h; y += PIX_PER_CELL) { | for (y = 0; y < h; y += PIX_PER_CELL) { | ||||
/* previous cell's row */ | /* previous cell's row */ | ||||
celly = (y >> PIXCELL_SHIFT); | celly = (y >> PIXCELL_SHIFT); | ||||
/* Delta check crc to previous set */ | /* Delta check crc to previous set */ | ||||
for (x = 0; x < xcells; x++) { | for (x = 0; x < xcells; x++) { | ||||
Show All 11 Lines | for (x = 0; x < xcells; x++) { | ||||
cellwidth, | cellwidth, | ||||
y + PIX_PER_CELL >= h ? rem_y : PIX_PER_CELL); | y + PIX_PER_CELL >= h ? rem_y : PIX_PER_CELL); | ||||
if (nwrite <= 0) { | if (nwrite <= 0) { | ||||
retval = nwrite; | retval = nwrite; | ||||
goto done; | goto done; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
retval = 1; | |||||
done: | done: | ||||
pthread_mutex_lock(&rc->mtx); | rc->sending = false; | ||||
rc->sending = 0; | |||||
pthread_mutex_unlock(&rc->mtx); | |||||
return (retval); | return (retval); | ||||
} | } | ||||
static void | static void | ||||
rfb_recv_update_msg(struct rfb_softc *rc, int cfd, int discardonly) | rfb_recv_update_msg(struct rfb_softc *rc, int cfd) | ||||
{ | { | ||||
struct rfb_updt_msg updt_msg; | struct rfb_updt_msg updt_msg; | ||||
struct bhyvegc_image *gc_image; | |||||
(void)stream_read(cfd, ((void *)&updt_msg) + 1 , sizeof(updt_msg) - 1); | (void)stream_read(cfd, ((void *)&updt_msg) + 1 , sizeof(updt_msg) - 1); | ||||
console_refresh(); | rc->pending = true; | ||||
gc_image = console_get_image(); | if (!updt_msg.incremental) | ||||
rc->update_all = true; | |||||
updt_msg.x = htons(updt_msg.x); | |||||
updt_msg.y = htons(updt_msg.y); | |||||
updt_msg.width = htons(updt_msg.width); | |||||
updt_msg.height = htons(updt_msg.height); | |||||
if (updt_msg.width != gc_image->width || | |||||
updt_msg.height != gc_image->height) { | |||||
rc->width = gc_image->width; | |||||
rc->height = gc_image->height; | |||||
if (rc->enc_resize_ok) | |||||
rfb_send_resize_update_msg(rc, cfd); | |||||
} | } | ||||
if (discardonly) | |||||
return; | |||||
rfb_send_screen(rc, cfd, 1); | |||||
} | |||||
static void | static void | ||||
rfb_recv_key_msg(struct rfb_softc *rc, int cfd) | rfb_recv_key_msg(struct rfb_softc *rc, int cfd) | ||||
{ | { | ||||
struct rfb_key_msg key_msg; | struct rfb_key_msg key_msg; | ||||
(void)stream_read(cfd, ((void *)&key_msg) + 1, sizeof(key_msg) - 1); | (void)stream_read(cfd, ((void *)&key_msg) + 1, sizeof(key_msg) - 1); | ||||
console_key_event(key_msg.down, htonl(key_msg.code)); | console_key_event(key_msg.down, htonl(key_msg.code)); | ||||
rc->input_detected = true; | |||||
} | } | ||||
static void | static void | ||||
rfb_recv_ptr_msg(struct rfb_softc *rc, int cfd) | rfb_recv_ptr_msg(struct rfb_softc *rc, int cfd) | ||||
{ | { | ||||
struct rfb_ptr_msg ptr_msg; | struct rfb_ptr_msg ptr_msg; | ||||
(void)stream_read(cfd, ((void *)&ptr_msg) + 1, sizeof(ptr_msg) - 1); | (void)stream_read(cfd, ((void *)&ptr_msg) + 1, sizeof(ptr_msg) - 1); | ||||
console_ptr_event(ptr_msg.button, htons(ptr_msg.x), htons(ptr_msg.y)); | console_ptr_event(ptr_msg.button, htons(ptr_msg.x), htons(ptr_msg.y)); | ||||
rc->input_detected = true; | |||||
} | } | ||||
static void | static void | ||||
rfb_recv_cuttext_msg(struct rfb_softc *rc, int cfd) | rfb_recv_cuttext_msg(struct rfb_softc *rc, int cfd) | ||||
{ | { | ||||
struct rfb_cuttext_msg ct_msg; | struct rfb_cuttext_msg ct_msg; | ||||
unsigned char buf[32]; | unsigned char buf[32]; | ||||
int len; | int len; | ||||
Show All 31 Lines | rfb_wr_thr(void *arg) | ||||
cfd = rc->cfd; | cfd = rc->cfd; | ||||
prev_tv.tv_sec = 0; | prev_tv.tv_sec = 0; | ||||
prev_tv.tv_usec = 0; | prev_tv.tv_usec = 0; | ||||
while (rc->cfd >= 0) { | while (rc->cfd >= 0) { | ||||
FD_ZERO(&rfds); | FD_ZERO(&rfds); | ||||
FD_SET(cfd, &rfds); | FD_SET(cfd, &rfds); | ||||
tv.tv_sec = 0; | tv.tv_sec = 0; | ||||
tv.tv_usec = 10000; | tv.tv_usec = CFD_SEL_DELAY; | ||||
err = select(cfd+1, &rfds, NULL, NULL, &tv); | err = select(cfd+1, &rfds, NULL, NULL, &tv); | ||||
if (err < 0) | if (err < 0) | ||||
return (NULL); | return (NULL); | ||||
/* Determine if its time to push screen; ~24hz */ | /* Determine if its time to push screen; ~24hz */ | ||||
gettimeofday(&tv, NULL); | gettimeofday(&tv, NULL); | ||||
tdiff = timeval_delta(&prev_tv, &tv); | tdiff = timeval_delta(&prev_tv, &tv); | ||||
if (tdiff > 40000) { | if (tdiff >= SCREEN_POLL_DELAY) { | ||||
bool input; | |||||
prev_tv.tv_sec = tv.tv_sec; | prev_tv.tv_sec = tv.tv_sec; | ||||
prev_tv.tv_usec = tv.tv_usec; | prev_tv.tv_usec = tv.tv_usec; | ||||
if (rfb_send_screen(rc, cfd, 0) <= 0) { | input = atomic_exchange(&rc->input_detected, false); | ||||
/* | |||||
* Refresh the screen on every second trip through the loop, | |||||
* or if keyboard/mouse input has been detected. | |||||
*/ | |||||
if ((++rc->wrcount & 1) || input) { | |||||
if (rfb_send_screen(rc, cfd) <= 0) { | |||||
return (NULL); | return (NULL); | ||||
} | } | ||||
} | |||||
} else { | } else { | ||||
/* sleep */ | /* sleep */ | ||||
usleep(40000 - tdiff); | usleep(SCREEN_POLL_DELAY - tdiff); | ||||
} | } | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
void | void | ||||
rfb_handle(struct rfb_softc *rc, int cfd) | rfb_handle(struct rfb_softc *rc, int cfd) | ||||
{ | { | ||||
const char *vbuf = "RFB 003.008\n"; | const char *vbuf = "RFB 003.008\n"; | ||||
unsigned char buf[80]; | unsigned char buf[80]; | ||||
unsigned char *message = NULL; | unsigned char *message = NULL; | ||||
#ifndef NO_OPENSSL | #ifndef NO_OPENSSL | ||||
unsigned char challenge[AUTH_LENGTH]; | unsigned char challenge[AUTH_LENGTH]; | ||||
unsigned char keystr[PASSWD_LENGTH]; | unsigned char keystr[PASSWD_LENGTH]; | ||||
unsigned char crypt_expected[AUTH_LENGTH]; | unsigned char crypt_expected[AUTH_LENGTH]; | ||||
DES_key_schedule ks; | DES_key_schedule ks; | ||||
int i; | int i; | ||||
#endif | #endif | ||||
uint8_t client_ver; | |||||
uint8_t auth_type; | |||||
pthread_t tid; | pthread_t tid; | ||||
uint32_t sres = 0; | uint32_t sres = 0; | ||||
int len; | int len; | ||||
int perror = 1; | int perror = 1; | ||||
rc->cfd = cfd; | rc->cfd = cfd; | ||||
/* 1a. Send server version */ | /* 1a. Send server version */ | ||||
stream_write(cfd, vbuf, strlen(vbuf)); | stream_write(cfd, vbuf, strlen(vbuf)); | ||||
/* 1b. Read client version */ | /* 1b. Read client version */ | ||||
len = stream_read(cfd, buf, VERSION_LENGTH); | len = stream_read(cfd, buf, VERSION_LENGTH); | ||||
if (len == VERSION_LENGTH && !strncmp(vbuf, buf, VERSION_LENGTH - 2)) { | |||||
client_ver = buf[VERSION_LENGTH - 2]; | |||||
} | |||||
if (client_ver != CVERS_3_8 && client_ver != CVERS_3_7) { | |||||
/* only recognize 3.3, 3.7 & 3.8. Others dflt to 3.3 */ | |||||
client_ver = CVERS_3_3; | |||||
} | |||||
/* 2a. Send security type */ | /* 2a. Send security type */ | ||||
buf[0] = 1; | buf[0] = 1; | ||||
/* In versions 3.7 & 3.8, it's 2-way handshake */ | |||||
/* For version 3.3, server says what the authentication type must be */ | |||||
#ifndef NO_OPENSSL | #ifndef NO_OPENSSL | ||||
if (rc->password) | if (rc->password) { | ||||
buf[1] = SECURITY_TYPE_VNC_AUTH; | auth_type = SECURITY_TYPE_VNC_AUTH; | ||||
else | } else { | ||||
buf[1] = SECURITY_TYPE_NONE; | auth_type = SECURITY_TYPE_NONE; | ||||
} | |||||
#else | #else | ||||
buf[1] = SECURITY_TYPE_NONE; | auth_type = SECURITY_TYPE_NONE; | ||||
#endif | #endif | ||||
switch (client_ver) { | |||||
case CVERS_3_7: | |||||
case CVERS_3_8: | |||||
buf[0] = 1; | |||||
buf[1] = auth_type; | |||||
stream_write(cfd, buf, 2); | stream_write(cfd, buf, 2); | ||||
/* 2b. Read agreed security type */ | /* 2b. Read agreed security type */ | ||||
len = stream_read(cfd, buf, 1); | len = stream_read(cfd, buf, 1); | ||||
if (buf[0] != auth_type) { | |||||
/* deny */ | |||||
sres = htonl(1); | |||||
message = "Auth failed: authentication type mismatch"; | |||||
goto report_and_done; | |||||
} | |||||
break; | |||||
case CVERS_3_3: | |||||
default: | |||||
be32enc(buf, auth_type); | |||||
stream_write(cfd, buf, 4); | |||||
break; | |||||
} | |||||
/* 2c. Do VNC authentication */ | /* 2c. Do VNC authentication */ | ||||
switch (buf[0]) { | switch (auth_type) { | ||||
case SECURITY_TYPE_NONE: | case SECURITY_TYPE_NONE: | ||||
sres = 0; | |||||
break; | break; | ||||
case SECURITY_TYPE_VNC_AUTH: | case SECURITY_TYPE_VNC_AUTH: | ||||
/* | /* | ||||
* The client encrypts the challenge with DES, using a password | * The client encrypts the challenge with DES, using a password | ||||
* supplied by the user as the key. | * supplied by the user as the key. | ||||
* To form the key, the password is truncated to | * To form the key, the password is truncated to | ||||
* eight characters, or padded with null bytes on the right. | * eight characters, or padded with null bytes on the right. | ||||
* The client then sends the resulting 16-bytes response. | * The client then sends the resulting 16-bytes response. | ||||
Show All 31 Lines | #ifndef NO_OPENSSL | ||||
DES_ecb_encrypt((const_DES_cblock *)(challenge + PASSWD_LENGTH), | DES_ecb_encrypt((const_DES_cblock *)(challenge + PASSWD_LENGTH), | ||||
(const_DES_cblock *)(crypt_expected + | (const_DES_cblock *)(crypt_expected + | ||||
PASSWD_LENGTH), | PASSWD_LENGTH), | ||||
&ks, DES_ENCRYPT); | &ks, DES_ENCRYPT); | ||||
if (memcmp(crypt_expected, buf, AUTH_LENGTH) != 0) { | if (memcmp(crypt_expected, buf, AUTH_LENGTH) != 0) { | ||||
message = "Auth Failed: Invalid Password."; | message = "Auth Failed: Invalid Password."; | ||||
sres = htonl(1); | sres = htonl(1); | ||||
} else | } else { | ||||
sres = 0; | sres = 0; | ||||
} | |||||
#else | #else | ||||
sres = 0; | sres = htonl(1); | ||||
WPRINTF(("Auth not supported, no OpenSSL in your system")); | WPRINTF(("Auth not supported, no OpenSSL in your system")); | ||||
#endif | #endif | ||||
break; | break; | ||||
} | } | ||||
switch (client_ver) { | |||||
case CVERS_3_7: | |||||
case CVERS_3_8: | |||||
report_and_done: | |||||
/* 2d. Write back a status */ | /* 2d. Write back a status */ | ||||
stream_write(cfd, &sres, 4); | stream_write(cfd, &sres, 4); | ||||
if (sres) { | if (sres) { | ||||
/* 3.7 does not want string explaining cause */ | |||||
if (client_ver == CVERS_3_8) { | |||||
be32enc(buf, strlen(message)); | be32enc(buf, strlen(message)); | ||||
stream_write(cfd, buf, 4); | stream_write(cfd, buf, 4); | ||||
stream_write(cfd, message, strlen(message)); | stream_write(cfd, message, strlen(message)); | ||||
} | |||||
goto done; | goto done; | ||||
} | } | ||||
break; | |||||
case CVERS_3_3: | |||||
default: | |||||
/* for VNC auth case send status */ | |||||
if (auth_type == SECURITY_TYPE_VNC_AUTH) { | |||||
/* 2d. Write back a status */ | |||||
stream_write(cfd, &sres, 4); | |||||
} | |||||
if (sres) { | |||||
goto done; | |||||
} | |||||
break; | |||||
} | |||||
/* 3a. Read client shared-flag byte */ | /* 3a. Read client shared-flag byte */ | ||||
len = stream_read(cfd, buf, 1); | len = stream_read(cfd, buf, 1); | ||||
/* 4a. Write server-init info */ | /* 4a. Write server-init info */ | ||||
rfb_send_server_init_msg(cfd); | rfb_send_server_init_msg(cfd); | ||||
if (!rc->zbuf) { | if (!rc->zbuf) { | ||||
rc->zbuf = malloc(RFB_ZLIB_BUFSZ + 16); | rc->zbuf = malloc(RFB_ZLIB_BUFSZ + 16); | ||||
assert(rc->zbuf != NULL); | assert(rc->zbuf != NULL); | ||||
} | } | ||||
rfb_send_screen(rc, cfd, 1); | |||||
perror = pthread_create(&tid, NULL, rfb_wr_thr, rc); | perror = pthread_create(&tid, NULL, rfb_wr_thr, rc); | ||||
if (perror == 0) | if (perror == 0) | ||||
pthread_set_name_np(tid, "rfbout"); | pthread_set_name_np(tid, "rfbout"); | ||||
/* Now read in client requests. 1st byte identifies type */ | /* Now read in client requests. 1st byte identifies type */ | ||||
for (;;) { | for (;;) { | ||||
len = read(cfd, buf, 1); | len = read(cfd, buf, 1); | ||||
if (len <= 0) { | if (len <= 0) { | ||||
DPRINTF(("rfb client exiting")); | DPRINTF(("rfb client exiting")); | ||||
break; | break; | ||||
} | } | ||||
switch (buf[0]) { | switch (buf[0]) { | ||||
case 0: | case CS_SET_PIXEL_FORMAT: | ||||
rfb_recv_set_pixfmt_msg(rc, cfd); | rfb_recv_set_pixfmt_msg(rc, cfd); | ||||
break; | break; | ||||
case 2: | case CS_SET_ENCODINGS: | ||||
rfb_recv_set_encodings_msg(rc, cfd); | rfb_recv_set_encodings_msg(rc, cfd); | ||||
break; | break; | ||||
case 3: | case CS_UPDATE_MSG: | ||||
rfb_recv_update_msg(rc, cfd, 1); | rfb_recv_update_msg(rc, cfd); | ||||
break; | break; | ||||
case 4: | case CS_KEY_EVENT: | ||||
rfb_recv_key_msg(rc, cfd); | rfb_recv_key_msg(rc, cfd); | ||||
break; | break; | ||||
case 5: | case CS_POINTER_EVENT: | ||||
rfb_recv_ptr_msg(rc, cfd); | rfb_recv_ptr_msg(rc, cfd); | ||||
break; | break; | ||||
case 6: | case CS_CUT_TEXT: | ||||
rfb_recv_cuttext_msg(rc, cfd); | rfb_recv_cuttext_msg(rc, cfd); | ||||
break; | break; | ||||
default: | default: | ||||
WPRINTF(("rfb unknown cli-code %d!", buf[0] & 0xff)); | WPRINTF(("rfb unknown cli-code %d!", buf[0] & 0xff)); | ||||
goto done; | goto done; | ||||
} | } | ||||
} | } | ||||
done: | done: | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | |||||
rfb_init(char *hostname, int port, int wait, char *password) | rfb_init(char *hostname, int port, int wait, char *password) | ||||
{ | { | ||||
int e; | int e; | ||||
char servname[6]; | char servname[6]; | ||||
struct rfb_softc *rc; | struct rfb_softc *rc; | ||||
struct addrinfo *ai = NULL; | struct addrinfo *ai = NULL; | ||||
struct addrinfo hints; | struct addrinfo hints; | ||||
int on = 1; | int on = 1; | ||||
int cnt; | |||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
#endif | #endif | ||||
rc = calloc(1, sizeof(struct rfb_softc)); | rc = calloc(1, sizeof(struct rfb_softc)); | ||||
rc->crc = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32), | cnt = howmany(RFB_MAX_WIDTH, PIX_PER_CELL) * | ||||
sizeof(uint32_t)); | howmany(RFB_MAX_HEIGHT, PIX_PER_CELL); | ||||
rc->crc_tmp = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32), | rc->crc = calloc(cnt, sizeof(uint32_t)); | ||||
sizeof(uint32_t)); | rc->crc_tmp = calloc(cnt, sizeof(uint32_t)); | ||||
rc->crc_width = RFB_MAX_WIDTH; | rc->crc_width = RFB_MAX_WIDTH; | ||||
rc->crc_height = RFB_MAX_HEIGHT; | rc->crc_height = RFB_MAX_HEIGHT; | ||||
rc->sfd = -1; | rc->sfd = -1; | ||||
rc->password = password; | rc->password = password; | ||||
snprintf(servname, sizeof(servname), "%d", port ? port : 5900); | snprintf(servname, sizeof(servname), "%d", port ? port : 5900); | ||||
▲ Show 20 Lines • Show All 73 Lines • Show Last 20 Lines |
For new code it might be nice to use C11 atomics instead?