int guac_client_init(guac_client* client, int argc, char** argv) { rfbClient* rfb_client; vnc_guac_client_data* guac_client_data; int retries_remaining; /* Set up libvncclient logging */ rfbClientLog = guac_vnc_client_log_info; rfbClientErr = guac_vnc_client_log_error; /*** PARSE ARGUMENTS ***/ if (argc != VNC_ARGS_COUNT) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong argument count received."); return 1; } /* Alloc client data */ guac_client_data = malloc(sizeof(vnc_guac_client_data)); client->data = guac_client_data; guac_client_data->hostname = strdup(argv[IDX_HOSTNAME]); guac_client_data->port = atoi(argv[IDX_PORT]); /* Set remote cursor flag */ guac_client_data->remote_cursor = (strcmp(argv[IDX_CURSOR], "remote") == 0); /* Set red/blue swap flag */ guac_client_data->swap_red_blue = (strcmp(argv[IDX_SWAP_RED_BLUE], "true") == 0); /* Set read-only flag */ guac_client_data->read_only = (strcmp(argv[IDX_READ_ONLY], "true") == 0); /* Freed after use by libvncclient */ guac_client_data->password = strdup(argv[IDX_PASSWORD]); /* Parse color depth */ guac_client_data->color_depth = atoi(argv[IDX_COLOR_DEPTH]); #ifdef ENABLE_VNC_REPEATER /* Set repeater parameters if specified */ if (argv[IDX_DEST_HOST][0] != '\0') guac_client_data->dest_host = strdup(argv[IDX_DEST_HOST]); else guac_client_data->dest_host = NULL; if (argv[IDX_DEST_PORT][0] != '\0') guac_client_data->dest_port = atoi(argv[IDX_DEST_PORT]); #endif /* Set encodings if specified */ if (argv[IDX_ENCODINGS][0] != '\0') guac_client_data->encodings = strdup(argv[IDX_ENCODINGS]); else guac_client_data->encodings = NULL; /* Parse autoretry */ if (argv[IDX_AUTORETRY][0] != '\0') retries_remaining = atoi(argv[IDX_AUTORETRY]); else retries_remaining = 0; #ifdef ENABLE_VNC_LISTEN /* Set reverse-connection flag */ guac_client_data->reverse_connect = (strcmp(argv[IDX_REVERSE_CONNECT], "true") == 0); /* Parse listen timeout */ if (argv[IDX_LISTEN_TIMEOUT][0] != '\0') guac_client_data->listen_timeout = atoi(argv[IDX_LISTEN_TIMEOUT]); else guac_client_data->listen_timeout = 5000; #endif /* Init clipboard */ guac_client_data->clipboard = guac_common_clipboard_alloc(GUAC_VNC_CLIPBOARD_MAX_LENGTH); /* Ensure connection is kept alive during lengthy connects */ guac_socket_require_keep_alive(client->socket); /* Attempt connection */ rfb_client = __guac_vnc_get_client(client); /* If unsuccessful, retry as many times as specified */ while (!rfb_client && retries_remaining > 0) { guac_client_log_info(client, "Connect failed. Waiting %ims before retrying...", GUAC_VNC_CONNECT_INTERVAL); /* Wait for given interval then retry */ usleep(GUAC_VNC_CONNECT_INTERVAL*1000); rfb_client = __guac_vnc_get_client(client); retries_remaining--; } /* If the final connect attempt fails, return error */ if (!rfb_client) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to connect to VNC server."); return 1; } #ifdef ENABLE_PULSE guac_client_data->audio_enabled = (strcmp(argv[IDX_ENABLE_AUDIO], "true") == 0); /* If an encoding is available, load an audio stream */ if (guac_client_data->audio_enabled) { guac_client_data->audio = guac_audio_stream_alloc(client, NULL); /* Load servername if specified */ if (argv[IDX_AUDIO_SERVERNAME][0] != '\0') guac_client_data->pa_servername = strdup(argv[IDX_AUDIO_SERVERNAME]); else guac_client_data->pa_servername = NULL; /* If successful, init audio system */ if (guac_client_data->audio != NULL) { guac_client_log_info(client, "Audio will be encoded as %s", guac_client_data->audio->encoder->mimetype); /* Require threadsafe sockets if audio enabled */ guac_socket_require_threadsafe(client->socket); /* Start audio stream */ guac_pa_start_stream(client); } /* Otherwise, audio loading failed */ else guac_client_log_info(client, "No available audio encoding. Sound disabled."); } /* end if audio enabled */ #endif /* Set remaining client data */ guac_client_data->rfb_client = rfb_client; guac_client_data->copy_rect_used = 0; guac_client_data->cursor = guac_client_alloc_buffer(client); /* Set handlers */ client->handle_messages = vnc_guac_client_handle_messages; client->free_handler = vnc_guac_client_free_handler; /* If not read-only, set input handlers and pointer */ if (guac_client_data->read_only == 0) { /* Only handle mouse/keyboard/clipboard if not read-only */ client->mouse_handler = vnc_guac_client_mouse_handler; client->key_handler = vnc_guac_client_key_handler; client->clipboard_handler = vnc_guac_client_clipboard_handler; client->blob_handler = vnc_guac_client_blob_handler; client->end_handler = vnc_guac_client_end_handler; /* If not read-only but cursor is remote, set a dot cursor */ if (guac_client_data->remote_cursor) guac_common_set_dot_cursor(client); /* Otherwise, set pointer until explicitly requested otherwise */ else guac_common_set_pointer_cursor(client); } /* Send name */ guac_protocol_send_name(client->socket, rfb_client->desktopName); /* Send size */ guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER, rfb_client->width, rfb_client->height); return 0; }
int guac_client_init(guac_client* client, int argc, char** argv) { rdp_guac_client_data* guac_client_data; freerdp* rdp_inst; rdpSettings* settings; char* hostname; int port = RDP_DEFAULT_PORT; boolean bitmap_cache; /** * Selected server-side keymap. Client will be assumed to also use this * keymap. Keys will be sent to server based on client input on a * best-effort basis. */ const guac_rdp_keymap* chosen_keymap; if (argc < RDP_ARGS_COUNT) { guac_protocol_send_error(client->socket, "Wrong argument count received."); guac_socket_flush(client->socket); guac_error = GUAC_STATUS_BAD_ARGUMENT; guac_error_message = "Wrong argument count received"; return 1; } /* If port specified, use it */ if (argv[IDX_PORT][0] != '\0') port = atoi(argv[IDX_PORT]); hostname = argv[IDX_HOSTNAME]; /* Allocate client data */ guac_client_data = malloc(sizeof(rdp_guac_client_data)); /* Init random number generator */ srandom(time(NULL)); /* Init client */ freerdp_channels_global_init(); rdp_inst = freerdp_new(); rdp_inst->PreConnect = rdp_freerdp_pre_connect; rdp_inst->PostConnect = rdp_freerdp_post_connect; rdp_inst->ReceiveChannelData = __guac_receive_channel_data; /* Allocate FreeRDP context */ rdp_inst->context_size = sizeof(rdp_freerdp_context); rdp_inst->ContextNew = (pContextNew) rdp_freerdp_context_new; rdp_inst->ContextFree = (pContextFree) rdp_freerdp_context_free; freerdp_context_new(rdp_inst); /* Set settings */ settings = rdp_inst->settings; /* Console */ settings->console_session = (strcmp(argv[IDX_CONSOLE], "true") == 0); settings->console_audio = (strcmp(argv[IDX_CONSOLE_AUDIO], "true") == 0); /* --no-auth */ settings->authentication = false; /* --sec rdp */ settings->rdp_security = true; settings->tls_security = false; settings->nla_security = false; settings->encryption = true; settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS; settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE; /* Use optimal width unless overridden */ settings->width = client->info.optimal_width; if (argv[IDX_WIDTH][0] != '\0') settings->width = atoi(argv[IDX_WIDTH]); /* Use default width if given width is invalid. */ if (settings->width <= 0) { settings->width = RDP_DEFAULT_WIDTH; guac_client_log_error(client, "Invalid width: \"%s\". Using default of %i.", argv[IDX_WIDTH], settings->width); } /* Round width up to nearest multiple of 4 */ settings->width = (settings->width + 3) & ~0x3; /* Use optimal height unless overridden */ settings->height = client->info.optimal_height; if (argv[IDX_HEIGHT][0] != '\0') settings->height = atoi(argv[IDX_HEIGHT]); /* Use default height if given height is invalid. */ if (settings->height <= 0) { settings->height = RDP_DEFAULT_HEIGHT; guac_client_log_error(client, "Invalid height: \"%s\". Using default of %i.", argv[IDX_WIDTH], settings->height); } /* Set hostname */ settings->hostname = strdup(hostname); settings->port = port; settings->window_title = strdup(hostname); /* Domain */ if (argv[IDX_DOMAIN][0] != '\0') settings->domain = strdup(argv[IDX_DOMAIN]); /* Username */ if (argv[IDX_USERNAME][0] != '\0') settings->username = strdup(argv[IDX_USERNAME]); /* Password */ if (argv[IDX_PASSWORD][0] != '\0') { settings->password = strdup(argv[IDX_PASSWORD]); settings->autologon = 1; } /* Initial program */ if (argv[IDX_INITIAL_PROGRAM][0] != '\0') settings->shell = strdup(argv[IDX_INITIAL_PROGRAM]); /* Session color depth */ settings->color_depth = RDP_DEFAULT_DEPTH; if (argv[IDX_COLOR_DEPTH][0] != '\0') settings->color_depth = atoi(argv[IDX_COLOR_DEPTH]); /* Use default depth if given depth is invalid. */ if (settings->color_depth == 0) { settings->color_depth = RDP_DEFAULT_DEPTH; guac_client_log_error(client, "Invalid color-depth: \"%s\". Using default of %i.", argv[IDX_WIDTH], settings->color_depth); } /* Audio enable/disable */ guac_client_data->audio_enabled = (strcmp(argv[IDX_DISABLE_AUDIO], "true") != 0); /* Printing enable/disable */ guac_client_data->printing_enabled = (strcmp(argv[IDX_ENABLE_PRINTING], "true") == 0); /* Order support */ bitmap_cache = settings->bitmap_cache; settings->os_major_type = OSMAJORTYPE_UNSPECIFIED; settings->os_minor_type = OSMINORTYPE_UNSPECIFIED; settings->order_support[NEG_DSTBLT_INDEX] = true; settings->order_support[NEG_PATBLT_INDEX] = false; /* PATBLT not yet supported */ settings->order_support[NEG_SCRBLT_INDEX] = true; settings->order_support[NEG_OPAQUE_RECT_INDEX] = true; settings->order_support[NEG_DRAWNINEGRID_INDEX] = false; settings->order_support[NEG_MULTIDSTBLT_INDEX] = false; settings->order_support[NEG_MULTIPATBLT_INDEX] = false; settings->order_support[NEG_MULTISCRBLT_INDEX] = false; settings->order_support[NEG_MULTIOPAQUERECT_INDEX] = false; settings->order_support[NEG_MULTI_DRAWNINEGRID_INDEX] = false; settings->order_support[NEG_LINETO_INDEX] = false; settings->order_support[NEG_POLYLINE_INDEX] = false; settings->order_support[NEG_MEMBLT_INDEX] = bitmap_cache; settings->order_support[NEG_MEM3BLT_INDEX] = false; settings->order_support[NEG_MEMBLT_V2_INDEX] = bitmap_cache; settings->order_support[NEG_MEM3BLT_V2_INDEX] = false; settings->order_support[NEG_SAVEBITMAP_INDEX] = false; settings->order_support[NEG_GLYPH_INDEX_INDEX] = true; settings->order_support[NEG_FAST_INDEX_INDEX] = true; settings->order_support[NEG_FAST_GLYPH_INDEX] = true; settings->order_support[NEG_POLYGON_SC_INDEX] = false; settings->order_support[NEG_POLYGON_CB_INDEX] = false; settings->order_support[NEG_ELLIPSE_SC_INDEX] = false; settings->order_support[NEG_ELLIPSE_CB_INDEX] = false; /* Store client data */ guac_client_data->rdp_inst = rdp_inst; guac_client_data->bounded = false; guac_client_data->mouse_button_mask = 0; guac_client_data->current_surface = GUAC_DEFAULT_LAYER; guac_client_data->clipboard = NULL; guac_client_data->audio = NULL; /* Main socket needs to be threadsafe */ guac_socket_require_threadsafe(client->socket); /* Recursive attribute for locks */ pthread_mutexattr_init(&(guac_client_data->attributes)); pthread_mutexattr_settype(&(guac_client_data->attributes), PTHREAD_MUTEX_RECURSIVE); /* Init RDP lock */ pthread_mutex_init(&(guac_client_data->rdp_lock), &(guac_client_data->attributes)); /* Clear keysym state mapping and keymap */ memset(guac_client_data->keysym_state, 0, sizeof(guac_rdp_keysym_state_map)); memset(guac_client_data->keymap, 0, sizeof(guac_rdp_static_keymap)); client->data = guac_client_data; ((rdp_freerdp_context*) rdp_inst->context)->client = client; /* Pick keymap based on argument */ if (argv[IDX_SERVER_LAYOUT][0] != '\0') { /* US English Qwerty */ if (strcmp("en-us-qwerty", argv[IDX_SERVER_LAYOUT]) == 0) chosen_keymap = &guac_rdp_keymap_en_us; /* German Qwertz */ else if (strcmp("de-de-qwertz", argv[IDX_SERVER_LAYOUT]) == 0) chosen_keymap = &guac_rdp_keymap_de_de; /* French Azerty */ else if (strcmp("fr-fr-azerty", argv[IDX_SERVER_LAYOUT]) == 0) chosen_keymap = &guac_rdp_keymap_fr_fr; /* Failsafe (Unicode) keymap */ else if (strcmp("failsafe", argv[IDX_SERVER_LAYOUT]) == 0) chosen_keymap = &guac_rdp_keymap_failsafe; /* If keymap unknown, resort to failsafe */ else { guac_client_log_error(client, "Unknown layout \"%s\". Using the failsafe layout instead.", argv[IDX_SERVER_LAYOUT]); chosen_keymap = &guac_rdp_keymap_failsafe; } } /* If no keymap requested, assume US */ else chosen_keymap = &guac_rdp_keymap_en_us; /* Load keymap into client */ __guac_rdp_client_load_keymap(client, chosen_keymap); /* Set server-side keymap */ settings->kbd_layout = chosen_keymap->freerdp_keyboard_layout; /* Connect to RDP server */ if (!freerdp_connect(rdp_inst)) { guac_protocol_send_error(client->socket, "Error connecting to RDP server"); guac_socket_flush(client->socket); guac_error = GUAC_STATUS_BAD_STATE; guac_error_message = "Error connecting to RDP server"; return 1; } /* Send connection name */ guac_protocol_send_name(client->socket, settings->window_title); /* Send size */ guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER, settings->width, settings->height); /* Create glyph surfaces */ guac_client_data->opaque_glyph_surface = cairo_image_surface_create( CAIRO_FORMAT_RGB24, settings->width, settings->height); guac_client_data->trans_glyph_surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, settings->width, settings->height); /* Set default pointer */ guac_rdp_set_default_pointer(client); /* Success */ return 0; }
int guac_client_init(guac_client* client, int argc, char** argv) { rdp_guac_client_data* guac_client_data; guac_rdp_settings* settings; freerdp* rdp_inst; /* Validate number of arguments received */ if (argc != RDP_ARGS_COUNT) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong argument count received."); return 1; } /* Allocate client data */ guac_client_data = malloc(sizeof(rdp_guac_client_data)); /* Init random number generator */ srandom(time(NULL)); /* Init client */ #ifdef HAVE_FREERDP_CHANNELS_GLOBAL_INIT freerdp_channels_global_init(); #endif rdp_inst = freerdp_new(); rdp_inst->PreConnect = rdp_freerdp_pre_connect; rdp_inst->PostConnect = rdp_freerdp_post_connect; rdp_inst->Authenticate = rdp_freerdp_authenticate; rdp_inst->VerifyCertificate = rdp_freerdp_verify_certificate; rdp_inst->ReceiveChannelData = __guac_receive_channel_data; /* Allocate FreeRDP context */ #ifdef LEGACY_FREERDP rdp_inst->context_size = sizeof(rdp_freerdp_context); #else rdp_inst->ContextSize = sizeof(rdp_freerdp_context); #endif rdp_inst->ContextNew = (pContextNew) rdp_freerdp_context_new; rdp_inst->ContextFree = (pContextFree) rdp_freerdp_context_free; freerdp_context_new(rdp_inst); /* Set settings */ settings = &(guac_client_data->settings); /* Console */ settings->console = (strcmp(argv[IDX_CONSOLE], "true") == 0); settings->console_audio = (strcmp(argv[IDX_CONSOLE_AUDIO], "true") == 0); /* Certificate and auth */ settings->ignore_certificate = (strcmp(argv[IDX_IGNORE_CERT], "true") == 0); settings->disable_authentication = (strcmp(argv[IDX_DISABLE_AUTH], "true") == 0); /* NLA security */ if (strcmp(argv[IDX_SECURITY], "nla") == 0) { guac_client_log(client, GUAC_LOG_INFO, "Security mode: NLA"); settings->security_mode = GUAC_SECURITY_NLA; } /* TLS security */ else if (strcmp(argv[IDX_SECURITY], "tls") == 0) { guac_client_log(client, GUAC_LOG_INFO, "Security mode: TLS"); settings->security_mode = GUAC_SECURITY_TLS; } /* RDP security */ else if (strcmp(argv[IDX_SECURITY], "rdp") == 0) { guac_client_log(client, GUAC_LOG_INFO, "Security mode: RDP"); settings->security_mode = GUAC_SECURITY_RDP; } /* ANY security (allow server to choose) */ else if (strcmp(argv[IDX_SECURITY], "any") == 0) { guac_client_log(client, GUAC_LOG_INFO, "Security mode: ANY"); settings->security_mode = GUAC_SECURITY_ANY; } /* If nothing given, default to RDP */ else { guac_client_log(client, GUAC_LOG_INFO, "No security mode specified. Defaulting to RDP."); settings->security_mode = GUAC_SECURITY_RDP; } /* Set hostname */ settings->hostname = strdup(argv[IDX_HOSTNAME]); /* If port specified, use it */ settings->port = RDP_DEFAULT_PORT; if (argv[IDX_PORT][0] != '\0') settings->port = atoi(argv[IDX_PORT]); guac_client_log(client, GUAC_LOG_DEBUG, "Client resolution is %ix%i at %i DPI", client->info.optimal_width, client->info.optimal_height, client->info.optimal_resolution); /* Use suggested resolution unless overridden */ settings->resolution = guac_rdp_suggest_resolution(client); if (argv[IDX_DPI][0] != '\0') settings->resolution = atoi(argv[IDX_DPI]); /* Use optimal width unless overridden */ settings->width = client->info.optimal_width * settings->resolution / client->info.optimal_resolution; if (argv[IDX_WIDTH][0] != '\0') settings->width = atoi(argv[IDX_WIDTH]); /* Use default width if given width is invalid. */ if (settings->width <= 0) { settings->width = RDP_DEFAULT_WIDTH; guac_client_log(client, GUAC_LOG_ERROR, "Invalid width: \"%s\". Using default of %i.", argv[IDX_WIDTH], settings->width); } /* Round width down to nearest multiple of 4 */ settings->width = settings->width & ~0x3; /* Use optimal height unless overridden */ settings->height = client->info.optimal_height * settings->resolution / client->info.optimal_resolution; if (argv[IDX_HEIGHT][0] != '\0') settings->height = atoi(argv[IDX_HEIGHT]); /* Use default height if given height is invalid. */ if (settings->height <= 0) { settings->height = RDP_DEFAULT_HEIGHT; guac_client_log(client, GUAC_LOG_ERROR, "Invalid height: \"%s\". Using default of %i.", argv[IDX_WIDTH], settings->height); } guac_client_log(client, GUAC_LOG_DEBUG, "Using resolution of %ix%i at %i DPI", settings->width, settings->height, settings->resolution); /* Domain */ settings->domain = NULL; if (argv[IDX_DOMAIN][0] != '\0') settings->domain = strdup(argv[IDX_DOMAIN]); /* Username */ settings->username = NULL; if (argv[IDX_USERNAME][0] != '\0') settings->username = strdup(argv[IDX_USERNAME]); /* Password */ settings->password = NULL; if (argv[IDX_PASSWORD][0] != '\0') settings->password = strdup(argv[IDX_PASSWORD]); /* Client name */ settings->client_name = NULL; if (argv[IDX_CLIENT_NAME][0] != '\0') settings->client_name = strdup(argv[IDX_CLIENT_NAME]); /* Initial program */ settings->initial_program = NULL; if (argv[IDX_INITIAL_PROGRAM][0] != '\0') settings->initial_program = strdup(argv[IDX_INITIAL_PROGRAM]); /* RemoteApp program */ settings->remote_app = NULL; if (argv[IDX_REMOTE_APP][0] != '\0') settings->remote_app = strdup(argv[IDX_REMOTE_APP]); /* RemoteApp working directory */ settings->remote_app_dir = NULL; if (argv[IDX_REMOTE_APP_DIR][0] != '\0') settings->remote_app_dir = strdup(argv[IDX_REMOTE_APP_DIR]); /* RemoteApp arguments */ settings->remote_app_args = NULL; if (argv[IDX_REMOTE_APP_ARGS][0] != '\0') settings->remote_app_args = strdup(argv[IDX_REMOTE_APP_ARGS]); /* Static virtual channels */ settings->svc_names = NULL; if (argv[IDX_STATIC_CHANNELS][0] != '\0') settings->svc_names = guac_split(argv[IDX_STATIC_CHANNELS], ','); /* Performance flags */ settings->wallpaper_enabled = (strcmp(argv[IDX_ENABLE_WALLPAPER], "true") == 0); settings->theming_enabled = (strcmp(argv[IDX_ENABLE_THEMING], "true") == 0); settings->font_smoothing_enabled = (strcmp(argv[IDX_ENABLE_FONT_SMOOTHING], "true") == 0); settings->full_window_drag_enabled = (strcmp(argv[IDX_ENABLE_FULL_WINDOW_DRAG], "true") == 0); settings->desktop_composition_enabled = (strcmp(argv[IDX_ENABLE_DESKTOP_COMPOSITION], "true") == 0); settings->menu_animations_enabled = (strcmp(argv[IDX_ENABLE_MENU_ANIMATIONS], "true") == 0); /* Session color depth */ settings->color_depth = RDP_DEFAULT_DEPTH; if (argv[IDX_COLOR_DEPTH][0] != '\0') settings->color_depth = atoi(argv[IDX_COLOR_DEPTH]); /* Use default depth if given depth is invalid. */ if (settings->color_depth == 0) { settings->color_depth = RDP_DEFAULT_DEPTH; guac_client_log(client, GUAC_LOG_ERROR, "Invalid color-depth: \"%s\". Using default of %i.", argv[IDX_WIDTH], settings->color_depth); } /* Audio enable/disable */ guac_client_data->settings.audio_enabled = (strcmp(argv[IDX_DISABLE_AUDIO], "true") != 0); /* Printing enable/disable */ guac_client_data->settings.printing_enabled = (strcmp(argv[IDX_ENABLE_PRINTING], "true") == 0); /* Drive enable/disable */ guac_client_data->settings.drive_enabled = (strcmp(argv[IDX_ENABLE_DRIVE], "true") == 0); guac_client_data->settings.drive_path = strdup(argv[IDX_DRIVE_PATH]); guac_client_data->settings.create_drive_path = (strcmp(argv[IDX_CREATE_DRIVE_PATH], "true") == 0); /* Store client data */ guac_client_data->rdp_inst = rdp_inst; guac_client_data->mouse_button_mask = 0; guac_client_data->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH); guac_client_data->requested_clipboard_format = CB_FORMAT_TEXT; guac_client_data->audio = NULL; guac_client_data->filesystem = NULL; guac_client_data->available_svc = guac_common_list_alloc(); /* Main socket needs to be threadsafe */ guac_socket_require_threadsafe(client->socket); /* Recursive attribute for locks */ pthread_mutexattr_init(&(guac_client_data->attributes)); pthread_mutexattr_settype(&(guac_client_data->attributes), PTHREAD_MUTEX_RECURSIVE); /* Init RDP lock */ pthread_mutex_init(&(guac_client_data->rdp_lock), &(guac_client_data->attributes)); /* Clear keysym state mapping and keymap */ memset(guac_client_data->keysym_state, 0, sizeof(guac_rdp_keysym_state_map)); memset(guac_client_data->keymap, 0, sizeof(guac_rdp_static_keymap)); client->data = guac_client_data; ((rdp_freerdp_context*) rdp_inst->context)->client = client; /* Pick keymap based on argument */ settings->server_layout = NULL; if (argv[IDX_SERVER_LAYOUT][0] != '\0') settings->server_layout = guac_rdp_keymap_find(argv[IDX_SERVER_LAYOUT]); /* If no keymap requested, use default */ if (settings->server_layout == NULL) settings->server_layout = guac_rdp_keymap_find(GUAC_DEFAULT_KEYMAP); /* Load keymap into client */ __guac_rdp_client_load_keymap(client, settings->server_layout); #ifdef ENABLE_COMMON_SSH guac_common_ssh_init(client); /* Connect via SSH if SFTP is enabled */ if (strcmp(argv[IDX_ENABLE_SFTP], "true") == 0) { guac_client_log(client, GUAC_LOG_DEBUG, "Connecting via SSH for SFTP filesystem access."); /* Parse username - use RDP username by default */ const char* sftp_username = argv[IDX_SFTP_USERNAME]; if (sftp_username[0] == '\0' && settings->username != NULL) sftp_username = settings->username; guac_client_data->sftp_user = guac_common_ssh_create_user(sftp_username); /* Import private key, if given */ if (argv[IDX_SFTP_PRIVATE_KEY][0] != '\0') { guac_client_log(client, GUAC_LOG_DEBUG, "Authenticating with private key."); /* Abort if private key cannot be read */ if (guac_common_ssh_user_import_key(guac_client_data->sftp_user, argv[IDX_SFTP_PRIVATE_KEY], argv[IDX_SFTP_PASSPHRASE])) { guac_common_ssh_destroy_user(guac_client_data->sftp_user); return 1; } } /* Otherwise, use specified password */ else { guac_client_log(client, GUAC_LOG_DEBUG, "Authenticating with password."); /* Parse password - use RDP password by default */ const char* sftp_password = argv[IDX_SFTP_PASSWORD]; if (sftp_password[0] == '\0' && settings->password != NULL) sftp_password = settings->password; guac_common_ssh_user_set_password(guac_client_data->sftp_user, sftp_password); } /* Parse hostname - use RDP hostname by default */ const char* sftp_hostname = argv[IDX_SFTP_HOSTNAME]; if (sftp_hostname[0] == '\0') sftp_hostname = settings->hostname; /* Parse port, defaulting to standard SSH port */ const char* sftp_port = argv[IDX_SFTP_PORT]; if (sftp_port[0] == '\0') sftp_port = "22"; /* Attempt SSH connection */ guac_client_data->sftp_session = guac_common_ssh_create_session(client, sftp_hostname, sftp_port, guac_client_data->sftp_user); /* Fail if SSH connection does not succeed */ if (guac_client_data->sftp_session == NULL) { /* Already aborted within guac_common_ssh_create_session() */ guac_common_ssh_destroy_user(guac_client_data->sftp_user); return 1; } /* Load and expose filesystem */ guac_client_data->sftp_filesystem = guac_common_ssh_create_sftp_filesystem( guac_client_data->sftp_session, "/"); /* Abort if SFTP connection fails */ if (guac_client_data->sftp_filesystem == NULL) { guac_common_ssh_destroy_session(guac_client_data->sftp_session); guac_common_ssh_destroy_user(guac_client_data->sftp_user); return 1; } /* Configure destination for basic uploads, if specified */ if (argv[IDX_SFTP_DIRECTORY][0] != '\0') { client->file_handler = guac_rdp_sftp_file_handler; guac_common_ssh_sftp_set_upload_path( guac_client_data->sftp_filesystem, argv[IDX_SFTP_DIRECTORY]); } /* Otherwise, use SFTP for basic uploads only if drive not enabled */ else if (!settings->drive_enabled) client->file_handler = guac_rdp_sftp_file_handler; guac_client_log(client, GUAC_LOG_DEBUG, "SFTP connection succeeded."); } #endif /* Create default surface */ guac_client_data->default_surface = guac_common_surface_alloc(client->socket, GUAC_DEFAULT_LAYER, settings->width, settings->height); guac_client_data->current_surface = guac_client_data->default_surface; /* Send connection name */ guac_protocol_send_name(client->socket, settings->hostname); /* Set default pointer */ guac_common_set_pointer_cursor(client); /* Push desired settings to FreeRDP */ guac_rdp_push_settings(settings, rdp_inst); /* Connect to RDP server */ if (!freerdp_connect(rdp_inst)) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Error connecting to RDP server"); return 1; } /* Success */ return 0; }
int guac_client_init(guac_client* client, int argc, char** argv) { rfbClient* rfb_client; vnc_guac_client_data* guac_client_data; int retries_remaining; /* Set up libvncclient logging */ rfbClientLog = guac_vnc_client_log_info; rfbClientErr = guac_vnc_client_log_error; /*** PARSE ARGUMENTS ***/ if (argc != VNC_ARGS_COUNT) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong argument count received."); return 1; } /* Alloc client data */ guac_client_data = malloc(sizeof(vnc_guac_client_data)); client->data = guac_client_data; guac_client_data->hostname = strdup(argv[IDX_HOSTNAME]); guac_client_data->port = atoi(argv[IDX_PORT]); guac_client_data->password = strdup(argv[IDX_PASSWORD]); /* NOTE: freed by libvncclient */ guac_client_data->default_surface = NULL; /* Set flags */ guac_client_data->remote_cursor = (strcmp(argv[IDX_CURSOR], "remote") == 0); guac_client_data->swap_red_blue = (strcmp(argv[IDX_SWAP_RED_BLUE], "true") == 0); guac_client_data->read_only = (strcmp(argv[IDX_READ_ONLY], "true") == 0); /* Parse color depth */ guac_client_data->color_depth = atoi(argv[IDX_COLOR_DEPTH]); #ifdef ENABLE_VNC_REPEATER /* Set repeater parameters if specified */ if (argv[IDX_DEST_HOST][0] != '\0') guac_client_data->dest_host = strdup(argv[IDX_DEST_HOST]); else guac_client_data->dest_host = NULL; if (argv[IDX_DEST_PORT][0] != '\0') guac_client_data->dest_port = atoi(argv[IDX_DEST_PORT]); #endif /* Set encodings if specified */ if (argv[IDX_ENCODINGS][0] != '\0') guac_client_data->encodings = strdup(argv[IDX_ENCODINGS]); else guac_client_data->encodings = NULL; /* Parse autoretry */ if (argv[IDX_AUTORETRY][0] != '\0') retries_remaining = atoi(argv[IDX_AUTORETRY]); else retries_remaining = 0; #ifdef ENABLE_VNC_LISTEN /* Set reverse-connection flag */ guac_client_data->reverse_connect = (strcmp(argv[IDX_REVERSE_CONNECT], "true") == 0); /* Parse listen timeout */ if (argv[IDX_LISTEN_TIMEOUT][0] != '\0') guac_client_data->listen_timeout = atoi(argv[IDX_LISTEN_TIMEOUT]); else guac_client_data->listen_timeout = 5000; #endif /* Init clipboard */ guac_client_data->clipboard = guac_common_clipboard_alloc(GUAC_VNC_CLIPBOARD_MAX_LENGTH); /* Configure clipboard encoding */ if (guac_vnc_set_clipboard_encoding(client, argv[IDX_CLIPBOARD_ENCODING])) guac_client_log(client, GUAC_LOG_INFO, "Using non-standard VNC clipboard encoding: '%s'.", argv[IDX_CLIPBOARD_ENCODING]); /* Ensure connection is kept alive during lengthy connects */ guac_socket_require_keep_alive(client->socket); /* Attempt connection */ rfb_client = __guac_vnc_get_client(client); /* If unsuccessful, retry as many times as specified */ while (!rfb_client && retries_remaining > 0) { struct timespec guac_vnc_connect_interval = { .tv_sec = GUAC_VNC_CONNECT_INTERVAL/1000, .tv_nsec = (GUAC_VNC_CONNECT_INTERVAL%1000)*1000000 }; guac_client_log(client, GUAC_LOG_INFO, "Connect failed. Waiting %ims before retrying...", GUAC_VNC_CONNECT_INTERVAL); /* Wait for given interval then retry */ nanosleep(&guac_vnc_connect_interval, NULL); rfb_client = __guac_vnc_get_client(client); retries_remaining--; } /* If the final connect attempt fails, return error */ if (!rfb_client) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to connect to VNC server."); return 1; } #ifdef ENABLE_PULSE guac_client_data->audio_enabled = (strcmp(argv[IDX_ENABLE_AUDIO], "true") == 0); /* If an encoding is available, load an audio stream */ if (guac_client_data->audio_enabled) { guac_client_data->audio = guac_audio_stream_alloc(client, NULL); /* Load servername if specified */ if (argv[IDX_AUDIO_SERVERNAME][0] != '\0') guac_client_data->pa_servername = strdup(argv[IDX_AUDIO_SERVERNAME]); else guac_client_data->pa_servername = NULL; /* If successful, init audio system */ if (guac_client_data->audio != NULL) { guac_client_log(client, GUAC_LOG_INFO, "Audio will be encoded as %s", guac_client_data->audio->encoder->mimetype); /* Require threadsafe sockets if audio enabled */ guac_socket_require_threadsafe(client->socket); /* Start audio stream */ guac_pa_start_stream(client); } /* Otherwise, audio loading failed */ else guac_client_log(client, GUAC_LOG_INFO, "No available audio encoding. Sound disabled."); } /* end if audio enabled */ #endif #ifdef ENABLE_COMMON_SSH guac_common_ssh_init(client); /* Connect via SSH if SFTP is enabled */ if (strcmp(argv[IDX_ENABLE_SFTP], "true") == 0) { guac_client_log(client, GUAC_LOG_DEBUG, "Connecting via SSH for SFTP filesystem access."); guac_client_data->sftp_user = guac_common_ssh_create_user(argv[IDX_SFTP_USERNAME]); /* Import private key, if given */ if (argv[IDX_SFTP_PRIVATE_KEY][0] != '\0') { guac_client_log(client, GUAC_LOG_DEBUG, "Authenticating with private key."); /* Abort if private key cannot be read */ if (guac_common_ssh_user_import_key(guac_client_data->sftp_user, argv[IDX_SFTP_PRIVATE_KEY], argv[IDX_SFTP_PASSPHRASE])) { guac_common_ssh_destroy_user(guac_client_data->sftp_user); return 1; } } /* Otherwise, use specified password */ else { guac_client_log(client, GUAC_LOG_DEBUG, "Authenticating with password."); guac_common_ssh_user_set_password(guac_client_data->sftp_user, argv[IDX_SFTP_PASSWORD]); } /* Parse hostname - use VNC hostname by default */ const char* sftp_hostname = argv[IDX_SFTP_HOSTNAME]; if (sftp_hostname[0] == '\0') sftp_hostname = guac_client_data->hostname; /* Parse port, defaulting to standard SSH port */ const char* sftp_port = argv[IDX_SFTP_PORT]; if (sftp_port[0] == '\0') sftp_port = "22"; /* Attempt SSH connection */ guac_client_data->sftp_session = guac_common_ssh_create_session(client, sftp_hostname, sftp_port, guac_client_data->sftp_user); /* Fail if SSH connection does not succeed */ if (guac_client_data->sftp_session == NULL) { /* Already aborted within guac_common_ssh_create_session() */ guac_common_ssh_destroy_user(guac_client_data->sftp_user); return 1; } /* Load and expose filesystem */ guac_client_data->sftp_filesystem = guac_common_ssh_create_sftp_filesystem( guac_client_data->sftp_session, "/"); /* Abort if SFTP connection fails */ if (guac_client_data->sftp_filesystem == NULL) { guac_common_ssh_destroy_session(guac_client_data->sftp_session); guac_common_ssh_destroy_user(guac_client_data->sftp_user); return 1; } /* Configure destination for basic uploads, if specified */ if (argv[IDX_SFTP_DIRECTORY][0] != '\0') guac_common_ssh_sftp_set_upload_path( guac_client_data->sftp_filesystem, argv[IDX_SFTP_DIRECTORY]); /* Set file handler for basic uploads */ client->file_handler = guac_vnc_sftp_file_handler; guac_client_log(client, GUAC_LOG_DEBUG, "SFTP connection succeeded."); } #endif /* Set remaining client data */ guac_client_data->rfb_client = rfb_client; guac_client_data->copy_rect_used = 0; guac_client_data->cursor = guac_client_alloc_buffer(client); /* Set handlers */ client->handle_messages = vnc_guac_client_handle_messages; client->free_handler = vnc_guac_client_free_handler; /* If not read-only, set input handlers and pointer */ if (guac_client_data->read_only == 0) { /* Only handle mouse/keyboard/clipboard if not read-only */ client->mouse_handler = vnc_guac_client_mouse_handler; client->key_handler = vnc_guac_client_key_handler; client->clipboard_handler = guac_vnc_clipboard_handler; /* If not read-only but cursor is remote, set a dot cursor */ if (guac_client_data->remote_cursor) guac_common_set_dot_cursor(client); /* Otherwise, set pointer until explicitly requested otherwise */ else guac_common_set_pointer_cursor(client); } /* Send name */ guac_protocol_send_name(client->socket, rfb_client->desktopName); /* Create default surface */ guac_client_data->default_surface = guac_common_surface_alloc(client->socket, GUAC_DEFAULT_LAYER, rfb_client->width, rfb_client->height); return 0; }
int guac_client_init(guac_client* client, int argc, char** argv) { rfbClient* rfb_client; vnc_guac_client_data* guac_client_data; int read_only; /* Set up libvncclient logging */ rfbClientLog = guac_vnc_client_log_info; rfbClientErr = guac_vnc_client_log_error; /*** PARSE ARGUMENTS ***/ if (argc != VNC_ARGS_COUNT) { guac_protocol_send_error(client->socket, "Wrong argument count received."); guac_socket_flush(client->socket); return 1; } /* Alloc client data */ guac_client_data = malloc(sizeof(vnc_guac_client_data)); client->data = guac_client_data; /* Set read-only flag */ read_only = (strcmp(argv[IDX_READ_ONLY], "true") == 0); /* Set red/blue swap flag */ guac_client_data->swap_red_blue = (strcmp(argv[IDX_SWAP_RED_BLUE], "true") == 0); /* Freed after use by libvncclient */ guac_client_data->password = strdup(argv[IDX_PASSWORD]); /*** INIT RFB CLIENT ***/ rfb_client = rfbGetClient(8, 3, 4); /* 32-bpp client */ /* Store Guac client in rfb client */ rfbClientSetClientData(rfb_client, __GUAC_CLIENT, client); /* Framebuffer update handler */ rfb_client->GotFrameBufferUpdate = guac_vnc_update; rfb_client->GotCopyRect = guac_vnc_copyrect; /* Do not handle clipboard and local cursor if read-only */ if (read_only == 0) { /* Enable client-side cursor */ rfb_client->GotCursorShape = guac_vnc_cursor; rfb_client->appData.useRemoteCursor = TRUE; /* Clipboard */ rfb_client->GotXCutText = guac_vnc_cut_text; } /* Password */ rfb_client->GetPassword = guac_vnc_get_password; /* Depth */ guac_vnc_set_pixel_format(rfb_client, atoi(argv[IDX_COLOR_DEPTH])); #ifdef ENABLE_PULSE guac_client_data->audio_enabled = (strcmp(argv[IDX_ENABLE_AUDIO], "true") == 0); /* If an encoding is available, load an audio stream */ if (guac_client_data->audio_enabled) { guac_client_data->audio = guac_audio_stream_alloc(client, NULL); /* Load servername if specified */ if (argv[IDX_AUDIO_SERVERNAME][0] != '\0') guac_client_data->pa_servername = strdup(argv[IDX_AUDIO_SERVERNAME]); else guac_client_data->pa_servername = NULL; /* If successful, init audio system */ if (guac_client_data->audio != NULL) { guac_client_log_info(client, "Audio will be encoded as %s", guac_client_data->audio->encoder->mimetype); /* Require threadsafe sockets if audio enabled */ guac_socket_require_threadsafe(client->socket); /* Start audio stream */ guac_pa_start_stream(client); } /* Otherwise, audio loading failed */ else guac_client_log_info(client, "No available audio encoding. Sound disabled."); } /* end if audio enabled */ #endif /* Hook into allocation so we can handle resize. */ guac_client_data->rfb_MallocFrameBuffer = rfb_client->MallocFrameBuffer; rfb_client->MallocFrameBuffer = guac_vnc_malloc_framebuffer; rfb_client->canHandleNewFBSize = 1; /* Set hostname and port */ rfb_client->serverHost = strdup(argv[0]); rfb_client->serverPort = atoi(argv[1]); #ifdef ENABLE_VNC_REPEATER /* Set repeater parameters if specified */ if(argv[IDX_DEST_HOST][0] != '\0') rfb_client->destHost = strdup(argv[IDX_DEST_HOST]); if(argv[IDX_DEST_PORT][0] != '\0') rfb_client->destPort = atoi(argv[IDX_DEST_PORT]); #endif /* Set encodings if specified */ if (argv[IDX_ENCODINGS][0] != '\0') rfb_client->appData.encodingsString = guac_client_data->encodings = strdup(argv[IDX_ENCODINGS]); else guac_client_data->encodings = NULL; /* Connect */ if (!rfbInitClient(rfb_client, NULL, NULL)) { guac_protocol_send_error(client->socket, "Error initializing VNC client"); guac_socket_flush(client->socket); return 1; } /* Set remaining client data */ guac_client_data->rfb_client = rfb_client; guac_client_data->copy_rect_used = 0; guac_client_data->cursor = guac_client_alloc_buffer(client); /* Set handlers */ client->handle_messages = vnc_guac_client_handle_messages; client->free_handler = vnc_guac_client_free_handler; if (read_only == 0) { /* Do not handle mouse/keyboard/clipboard if read-only */ client->mouse_handler = vnc_guac_client_mouse_handler; client->key_handler = vnc_guac_client_key_handler; client->clipboard_handler = vnc_guac_client_clipboard_handler; } /* Send name */ guac_protocol_send_name(client->socket, rfb_client->desktopName); /* Send size */ guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER, rfb_client->width, rfb_client->height); return 0; }
int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket* socket = client->socket; ssh_guac_client_data* client_data = calloc(1, sizeof(ssh_guac_client_data)); /* Init client data */ client->data = client_data; if (argc != SSH_ARGS_COUNT) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong number of arguments"); return -1; } /* Set locale and warn if not UTF-8 */ setlocale(LC_CTYPE, ""); if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0) guac_client_log(client, GUAC_LOG_INFO, "Current locale does not use UTF-8. Some characters may not render correctly."); /* Read parameters */ strcpy(client_data->hostname, argv[IDX_HOSTNAME]); strcpy(client_data->username, argv[IDX_USERNAME]); strcpy(client_data->password, argv[IDX_PASSWORD]); /* Init public key auth information */ strcpy(client_data->key_base64, argv[IDX_PRIVATE_KEY]); strcpy(client_data->key_passphrase, argv[IDX_PASSPHRASE]); /* Read font name */ if (argv[IDX_FONT_NAME][0] != 0) strcpy(client_data->font_name, argv[IDX_FONT_NAME]); else strcpy(client_data->font_name, GUAC_SSH_DEFAULT_FONT_NAME ); /* Read font size */ if (argv[IDX_FONT_SIZE][0] != 0) client_data->font_size = atoi(argv[IDX_FONT_SIZE]); else client_data->font_size = GUAC_SSH_DEFAULT_FONT_SIZE; /* Parse SFTP enable */ client_data->enable_sftp = strcmp(argv[IDX_ENABLE_SFTP], "true") == 0; #ifdef ENABLE_SSH_AGENT client_data->enable_agent = strcmp(argv[IDX_ENABLE_AGENT], "true") == 0; #endif /* Read port */ if (argv[IDX_PORT][0] != 0) strcpy(client_data->port, argv[IDX_PORT]); else strcpy(client_data->port, GUAC_SSH_DEFAULT_PORT); /* Read command, if any */ if (argv[IDX_COMMAND][0] != 0) client_data->command = strdup(argv[IDX_COMMAND]); /* Create terminal */ client_data->term = guac_terminal_create(client, client_data->font_name, client_data->font_size, client->info.optimal_resolution, client->info.optimal_width, client->info.optimal_height, argv[IDX_COLOR_SCHEME]); /* Fail if terminal init failed */ if (client_data->term == NULL) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Terminal initialization failed"); return -1; } /* Set up typescript, if requested */ const char* typescript_path = argv[IDX_TYPESCRIPT_PATH]; if (typescript_path[0] != 0) { /* Default to "typescript" if no name provided */ const char* typescript_name = argv[IDX_TYPESCRIPT_NAME]; if (typescript_name[0] == 0) typescript_name = "typescript"; /* Parse path creation flag */ int create_path = strcmp(argv[IDX_CREATE_TYPESCRIPT_PATH], "true") == 0; /* Create typescript */ guac_terminal_create_typescript(client_data->term, typescript_path, typescript_name, create_path); } /* Ensure main socket is threadsafe */ guac_socket_require_threadsafe(socket); /* Send initial name */ guac_protocol_send_name(socket, client_data->hostname); guac_socket_flush(socket); /* Set basic handlers */ client->handle_messages = ssh_guac_client_handle_messages; client->key_handler = ssh_guac_client_key_handler; client->mouse_handler = ssh_guac_client_mouse_handler; client->size_handler = ssh_guac_client_size_handler; client->free_handler = ssh_guac_client_free_handler; client->clipboard_handler = guac_ssh_clipboard_handler; /* Start client thread */ if (pthread_create(&(client_data->client_thread), NULL, ssh_client_thread, (void*) client)) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start SSH client thread"); return -1; } /* Success */ return 0; }