void guac_rdp_process_printing_notification(guac_client* client, int fd) { char pdf_filename[MAX_PDF_PRINTJOB_NAME_LEN]; char pdf_url[MAX_PDF_PRINTJOB_NAME_LEN]; if (read(fd, pdf_filename, MAX_PDF_PRINTJOB_NAME_LEN) <= 0) { guac_client_log_info(client, "Read 0 bytes from FIFO..."); return; } job_path_to_url(pdf_url, pdf_filename); guac_client_log_info(client, "Sending printing notification to client (%s)", pdf_url); guac_protocol_send_pdf_printjob_notif(client->socket, pdf_url); }
void guac_pa_start_stream(guac_client* client) { vnc_guac_client_data* client_data = (vnc_guac_client_data*) client->data; pa_context* context; guac_client_log_info(client, "Starting audio stream"); guac_audio_stream_begin(client_data->audio, GUAC_VNC_AUDIO_RATE, GUAC_VNC_AUDIO_CHANNELS, GUAC_VNC_AUDIO_BPS); /* Init main loop */ client_data->pa_mainloop = pa_threaded_mainloop_new(); /* Create context */ context = pa_context_new( pa_threaded_mainloop_get_api(client_data->pa_mainloop), "Guacamole Audio"); /* Set up context */ pa_context_set_state_callback(context, __context_state_callback, client); pa_context_connect(context, client_data->pa_servername, PA_CONTEXT_NOAUTOSPAWN, NULL); /* Start loop */ pa_threaded_mainloop_start(client_data->pa_mainloop); }
void __guac_rdp_client_load_keymap(guac_client* client, const guac_rdp_keymap* keymap) { rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; /* Get mapping */ const guac_rdp_keysym_desc* mapping = keymap->mapping; /* If parent exists, load parent first */ if (keymap->parent != NULL) __guac_rdp_client_load_keymap(client, keymap->parent); /* Log load */ guac_client_log_info(client, "Loading keymap \"%s\"", keymap->name); /* Load mapping into keymap */ while (mapping->keysym != 0) { /* Copy mapping */ GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keymap, mapping->keysym) = *mapping; /* Next keysym */ mapping++; } }
void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) { guac_client* client = ((rdp_freerdp_context*) context)->client; const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface; rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; pthread_mutex_lock(&(data->update_lock)); switch (dstblt->bRop) { /* Blackness */ case 0: /* Send black rectangle */ guac_protocol_send_rect(client->socket, current_layer, dstblt->nLeftRect, dstblt->nTopRect, dstblt->nWidth, dstblt->nHeight); guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, current_layer, 0, 0, 0, 255); break; /* Unsupported ROP3 */ default: guac_client_log_info(client, "guac_rdp_gdi_dstblt(rop3=%i)", dstblt->bRop); } pthread_mutex_unlock(&(data->update_lock)); }
void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary) { guac_client* client = ((rdp_freerdp_context*) context)->client; if (primary) ((rdp_guac_client_data*) client->data)->current_surface = GUAC_DEFAULT_LAYER; else { /* Make sure that the recieved bitmap is not NULL before processing */ if (bitmap == NULL) { guac_client_log_info(client, "NULL bitmap found in bitmap_setsurface instruction."); return; } /* If not available as a surface, make available. */ if (((guac_rdp_bitmap*) bitmap)->layer == NULL) guac_rdp_cache_bitmap(context, bitmap); ((rdp_guac_client_data*) client->data)->current_surface = ((guac_rdp_bitmap*) bitmap)->layer; } }
guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client, int rop3) { /* Translate supported ROP3 opcodes into composite modes */ switch (rop3) { /* "DSon" !(src | dest) */ case 0x11: return GUAC_TRANSFER_BINARY_NOR; /* "DSna" !src & dest */ case 0x22: return GUAC_TRANSFER_BINARY_NSRC_AND; /* "Sn" !src */ case 0x33: return GUAC_TRANSFER_BINARY_NSRC; /* "SDna" (src & !dest) */ case 0x44: return GUAC_TRANSFER_BINARY_NDEST_AND; /* "Dn" !dest */ case 0x55: return GUAC_TRANSFER_BINARY_NDEST; /* "SRCINVERT" (src ^ dest) */ case 0x66: return GUAC_TRANSFER_BINARY_XOR; /* "DSan" !(src & dest) */ case 0x77: return GUAC_TRANSFER_BINARY_NAND; /* "SRCAND" (src & dest) */ case 0x88: return GUAC_TRANSFER_BINARY_AND; /* "DSxn" !(src ^ dest) */ case 0x99: return GUAC_TRANSFER_BINARY_XNOR; /* "MERGEPAINT" (!src | dest)*/ case 0xBB: return GUAC_TRANSFER_BINARY_NSRC_OR; /* "SDno" (src | !dest) */ case 0xDD: return GUAC_TRANSFER_BINARY_NDEST_OR; /* "SRCPAINT" (src | dest) */ case 0xEE: return GUAC_TRANSFER_BINARY_OR; /* 0x00 = "BLACKNESS" (0) */ /* 0xAA = "NOP" (dest) */ /* 0xCC = "SRCCOPY" (src) */ /* 0xFF = "WHITENESS" (1) */ } /* Log warning if ROP3 opcode not supported */ guac_client_log_info (client, "guac_rdp_rop3_transfer_function: " "UNSUPPORTED opcode = 0x%02X", rop3); /* Default to BINARY_SRC */ return GUAC_TRANSFER_BINARY_SRC; }
void guac_pa_stop_stream(guac_client* client) { vnc_guac_client_data* client_data = (vnc_guac_client_data*) client->data; /* Stop loop */ pa_threaded_mainloop_stop(client_data->pa_mainloop); guac_client_log_info(client, "Audio stream finished"); }
static void __stream_state_callback(pa_stream* stream, void* data) { guac_client* client = (guac_client*) data; switch (pa_stream_get_state(stream)) { case PA_STREAM_UNCONNECTED: guac_client_log_info(client, "PulseAudio stream currently unconnected"); break; case PA_STREAM_CREATING: guac_client_log_info(client, "PulseAudio stream being created..."); break; case PA_STREAM_READY: guac_client_log_info(client, "PulseAudio stream now ready"); break; case PA_STREAM_FAILED: guac_client_log_info(client, "PulseAudio stream connection failed"); break; case PA_STREAM_TERMINATED: guac_client_log_info(client, "PulseAudio stream terminated"); break; default: guac_client_log_info(client, "Unknown PulseAudio stream state: 0x%x", pa_stream_get_state(stream)); } }
static void __context_state_callback(pa_context* context, void* data) { guac_client* client = (guac_client*) data; switch (pa_context_get_state(context)) { case PA_CONTEXT_UNCONNECTED: guac_client_log_info(client, "PulseAudio reports it is unconnected"); break; case PA_CONTEXT_CONNECTING: guac_client_log_info(client, "Connecting to PulseAudio..."); break; case PA_CONTEXT_AUTHORIZING: guac_client_log_info(client, "Authorizing PulseAudio connection..."); break; case PA_CONTEXT_SETTING_NAME: guac_client_log_info(client, "Sending client name..."); break; case PA_CONTEXT_READY: guac_client_log_info(client, "PulseAudio now ready"); pa_operation_unref(pa_context_get_server_info(context, __context_get_server_info_callback, client)); break; case PA_CONTEXT_FAILED: guac_client_log_info(client, "PulseAudio connection failed"); break; case PA_CONTEXT_TERMINATED: guac_client_log_info(client, "PulseAudio connection terminated"); break; default: guac_client_log_info(client, "Unknown PulseAudio context state: 0x%x", pa_context_get_state(context)); } }
void guac_rdpdr_process_connect(rdpSvcPlugin* plugin) { /* Get RDPDR plugin */ guac_rdpdrPlugin* rdpdr = (guac_rdpdrPlugin*) plugin; /* Get client from plugin */ guac_client* client = (guac_client*) plugin->channel_entry_points.pExtendedData; /* Init plugin */ rdpdr->client = client; /* Log that printing, etc. has been loaded */ guac_client_log_info(client, "guac_rdpdr connected."); }
static void __context_get_server_info_callback(pa_context* context, const pa_server_info* info, void* data) { guac_client* client = (guac_client*) data; /* If no default sink, cannot continue */ if (info->default_sink_name == NULL) { guac_client_log_error(client, "No default sink. Cannot stream audio."); return; } guac_client_log_info(client, "Will use default sink: \"%s\"", info->default_sink_name); /* Wait for default sink information */ pa_operation_unref( pa_context_get_sink_info_by_name(context, info->default_sink_name, __context_get_sink_info_callback, client)); }
void guac_rdpsnd_process_connect(rdpSvcPlugin* plugin) { guac_rdpsndPlugin* rdpsnd = (guac_rdpsndPlugin*) plugin; /* Get audio stream from plugin parameters */ audio_stream* audio = rdpsnd->audio = (audio_stream*) plugin->channel_entry_points.pExtendedData; /* NULL out pExtendedData so we don't lose our audio_stream due to an * automatic free() within libfreerdp */ plugin->channel_entry_points.pExtendedData = NULL; #ifdef RDPSVCPLUGIN_INTERVAL_MS /* Update every 10 ms */ plugin->interval_ms = 10; #endif /* Log that sound has been loaded */ guac_client_log_info(audio->client, "guacsnd connected."); }
void guac_rdp_process_cb_format_list(guac_client* client, RDP_CB_FORMAT_LIST_EVENT* event) { rdpChannels* channels = ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels; /* Received notification of available data */ int i; for (i=0; i<event->num_formats; i++) { /* If plain text available, request it */ if (event->formats[i] == CB_FORMAT_TEXT) { /* Create new data request */ RDP_CB_DATA_REQUEST_EVENT* data_request = (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new( CliprdrChannel_Class, CliprdrChannel_DataRequest, NULL, NULL); /* We want plain text */ data_request->format = CB_FORMAT_TEXT; /* Send request */ freerdp_channels_send_event(channels, (wMessage*) data_request); return; } } /* Otherwise, no supported data available */ guac_client_log_info(client, "Ignoring unsupported clipboard data"); }
static void __context_get_sink_info_callback(pa_context* context, const pa_sink_info* info, int is_last, void* data) { guac_client* client = (guac_client*) data; pa_stream* stream; pa_sample_spec spec; pa_buffer_attr attr; /* Stop if end of list reached */ if (is_last) return; guac_client_log_info(client, "Starting streaming from \"%s\"", info->description); /* Set format */ spec.format = PA_SAMPLE_S16LE; spec.rate = GUAC_VNC_AUDIO_RATE; spec.channels = GUAC_VNC_AUDIO_CHANNELS; attr.maxlength = -1; attr.fragsize = GUAC_VNC_AUDIO_FRAGMENT_SIZE; /* Create stream */ stream = pa_stream_new(context, "Guacamole Audio", &spec, NULL); /* Set stream callbacks */ pa_stream_set_state_callback(stream, __stream_state_callback, client); pa_stream_set_read_callback(stream, __stream_read_callback, client); /* Start stream */ pa_stream_connect_record(stream, info->monitor_source_name, &attr, PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND | PA_STREAM_ADJUST_LATENCY); }
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; }
/** * Allocates a new rfbClient instance given the parameters stored within the * client, returning NULL on failure. */ static rfbClient* __guac_vnc_get_client(guac_client* client) { rfbClient* rfb_client = rfbGetClient(8, 3, 4); /* 32-bpp client */ vnc_guac_client_data* guac_client_data = (vnc_guac_client_data*) client->data; /* 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 (guac_client_data->read_only == 0) { /* Clipboard */ rfb_client->GotXCutText = guac_vnc_cut_text; /* Set remote cursor */ if (guac_client_data->remote_cursor) rfb_client->appData.useRemoteCursor = FALSE; else { /* Enable client-side cursor */ rfb_client->appData.useRemoteCursor = TRUE; rfb_client->GotCursorShape = guac_vnc_cursor; } } /* Password */ rfb_client->GetPassword = guac_vnc_get_password; /* Depth */ guac_vnc_set_pixel_format(rfb_client, guac_client_data->color_depth); /* 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(guac_client_data->hostname); rfb_client->serverPort = guac_client_data->port; #ifdef ENABLE_VNC_REPEATER /* Set repeater parameters if specified */ if (guac_client_data->dest_host) { rfb_client->destHost = strdup(guac_client_data->dest_host); rfb_client->destPort = guac_client_data->dest_port; } #endif #ifdef ENABLE_VNC_LISTEN /* If reverse connection enabled, start listening */ if (guac_client_data->reverse_connect) { guac_client_log_info(client, "Listening for connections on port %i", guac_client_data->port); /* Listen for connection from server */ rfb_client->listenPort = guac_client_data->port; if (listenForIncomingConnectionsNoFork(rfb_client, guac_client_data->listen_timeout*1000) <= 0) return NULL; } #endif /* Set encodings if provided */ if (guac_client_data->encodings) rfb_client->appData.encodingsString = strdup(guac_client_data->encodings); /* Connect */ if (rfbInitClient(rfb_client, NULL, NULL)) return rfb_client; /* If connection fails, return NULL */ return NULL; }
void guac_rdpdr_process_receive(rdpSvcPlugin* plugin, STREAM* input_stream) { guac_rdpdrPlugin* rdpdr = (guac_rdpdrPlugin*) plugin; int component; int packet_id; /* Read header */ stream_read_uint16(input_stream, component); stream_read_uint16(input_stream, packet_id); /* Core component */ if (component == RDPDR_CTYP_CORE) { /* Dispatch handlers based on packet ID */ switch (packet_id) { case PAKID_CORE_SERVER_ANNOUNCE: guac_rdpdr_process_server_announce(rdpdr, input_stream); break; case PAKID_CORE_CLIENTID_CONFIRM: guac_rdpdr_process_clientid_confirm(rdpdr, input_stream); break; case PAKID_CORE_DEVICE_REPLY: guac_rdpdr_process_device_reply(rdpdr, input_stream); break; case PAKID_CORE_DEVICE_IOREQUEST: guac_rdpdr_process_device_iorequest(rdpdr, input_stream); break; case PAKID_CORE_SERVER_CAPABILITY: guac_rdpdr_process_server_capability(rdpdr, input_stream); break; case PAKID_CORE_USER_LOGGEDON: guac_rdpdr_process_user_loggedon(rdpdr, input_stream); break; default: guac_client_log_info(rdpdr->client, "Ignoring RDPDR core packet with unexpected ID: 0x%04x", packet_id); } } /* end if core */ /* Printer component */ else if (component == RDPDR_CTYP_PRN) { /* Dispatch handlers based on packet ID */ switch (packet_id) { case PAKID_PRN_CACHE_DATA: guac_rdpdr_process_prn_cache_data(rdpdr, input_stream); break; case PAKID_PRN_USING_XPS: guac_rdpdr_process_prn_using_xps(rdpdr, input_stream); break; default: guac_client_log_info(rdpdr->client, "Ignoring RDPDR printer packet with unexpected ID: 0x%04x", packet_id); } } /* end if printer */ else guac_client_log_info(rdpdr->client, "Ignoring packet for unknown RDPDR component: 0x%04x", component); }
int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) { rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; freerdp* rdp_inst = guac_client_data->rdp_inst; /* If keysym can be in lookup table */ if (GUAC_RDP_KEYSYM_STORABLE(keysym)) { int pressed_flags; /* Look up scancode mapping */ const guac_rdp_keysym_desc* keysym_desc = &GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keymap, keysym); /* If defined, send event */ if (keysym_desc->scancode != 0) { pthread_mutex_lock(&(guac_client_data->rdp_lock)); /* If defined, send any prerequesite keys that must be set */ if (keysym_desc->set_keysyms != NULL) __guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 1); /* If defined, release any keys that must be cleared */ if (keysym_desc->clear_keysyms != NULL) __guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 0); /* Determine proper event flag for pressed state */ if (pressed) pressed_flags = KBD_FLAGS_DOWN; else pressed_flags = KBD_FLAGS_RELEASE; /* Send actual key */ rdp_inst->input->KeyboardEvent(rdp_inst->input, keysym_desc->flags | pressed_flags, keysym_desc->scancode); /* If defined, release any keys that were originally released */ if (keysym_desc->set_keysyms != NULL) __guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 0); /* If defined, send any keys that were originally set */ if (keysym_desc->clear_keysyms != NULL) __guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 1); pthread_mutex_unlock(&(guac_client_data->rdp_lock)); return 0; } } /* Fall back to unicode events if undefined inside current keymap */ /* Only send when key pressed - Unicode events do not have * DOWN/RELEASE flags */ if (pressed) { /* Translate keysym into codepoint */ int codepoint; if (keysym <= 0xFF) codepoint = keysym; else if (keysym >= 0x1000000) codepoint = keysym & 0xFFFFFF; else { guac_client_log_info(client, "Unmapped keysym has no equivalent unicode " "value: 0x%x", keysym); return 0; } pthread_mutex_lock(&(guac_client_data->rdp_lock)); /* Send Unicode event */ rdp_inst->input->UnicodeKeyboardEvent( rdp_inst->input, 0, codepoint); pthread_mutex_unlock(&(guac_client_data->rdp_lock)); } return 0; }
boolean rdp_freerdp_pre_connect(freerdp* instance) { rdpContext* context = instance->context; guac_client* client = ((rdp_freerdp_context*) context)->client; rdpChannels* channels = context->channels; rdpBitmap* bitmap; rdpGlyph* glyph; rdpPointer* pointer; rdpPrimaryUpdate* primary; CLRCONV* clrconv; int i; rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; /* Load clipboard plugin */ if (freerdp_channels_load_plugin(channels, instance->settings, "cliprdr", NULL)) guac_client_log_error(client, "Failed to load cliprdr plugin."); /* If audio enabled, choose an encoder */ if (guac_client_data->audio_enabled) { /* Choose an encoding */ for (i=0; client->info.audio_mimetypes[i] != NULL; i++) { const char* mimetype = client->info.audio_mimetypes[i]; #ifdef ENABLE_OGG /* If Ogg is supported, done. */ if (strcmp(mimetype, ogg_encoder->mimetype) == 0) { guac_client_log_info(client, "Loading Ogg Vorbis encoder."); guac_client_data->audio = audio_stream_alloc(client, ogg_encoder); break; } #endif /* If wav is supported, done. */ if (strcmp(mimetype, wav_encoder->mimetype) == 0) { guac_client_log_info(client, "Loading wav encoder."); guac_client_data->audio = audio_stream_alloc(client, wav_encoder); break; } } /* If an encoding is available, load the sound plugin */ if (guac_client_data->audio != NULL) { /* Load sound plugin */ if (freerdp_channels_load_plugin(channels, instance->settings, "guac_rdpsnd", guac_client_data->audio)) guac_client_log_error(client, "Failed to load guac_rdpsnd plugin."); } else guac_client_log_info(client, "No available audio encoding. Sound disabled."); } /* end if audio enabled */ /* If printing enabled, load rdpdr */ if (guac_client_data->printing_enabled) { /* Load RDPDR plugin */ if (freerdp_channels_load_plugin(channels, instance->settings, "guac_rdpdr", client)) guac_client_log_error(client, "Failed to load guac_rdpdr plugin."); } /* end if printing enabled */ /* Init color conversion structure */ clrconv = xnew(CLRCONV); clrconv->alpha = 1; clrconv->invert = 0; clrconv->rgb555 = 0; clrconv->palette = xnew(rdpPalette); ((rdp_freerdp_context*) context)->clrconv = clrconv; /* Init FreeRDP cache */ instance->context->cache = cache_new(instance->settings); /* Set up bitmap handling */ bitmap = xnew(rdpBitmap); bitmap->size = sizeof(guac_rdp_bitmap); bitmap->New = guac_rdp_bitmap_new; bitmap->Free = guac_rdp_bitmap_free; bitmap->Paint = guac_rdp_bitmap_paint; bitmap->Decompress = guac_rdp_bitmap_decompress; bitmap->SetSurface = guac_rdp_bitmap_setsurface; graphics_register_bitmap(context->graphics, bitmap); xfree(bitmap); /* Set up glyph handling */ glyph = xnew(rdpGlyph); glyph->size = sizeof(guac_rdp_glyph); glyph->New = guac_rdp_glyph_new; glyph->Free = guac_rdp_glyph_free; glyph->Draw = guac_rdp_glyph_draw; glyph->BeginDraw = guac_rdp_glyph_begindraw; glyph->EndDraw = guac_rdp_glyph_enddraw; graphics_register_glyph(context->graphics, glyph); xfree(glyph); /* Set up pointer handling */ pointer = xnew(rdpPointer); pointer->size = sizeof(guac_rdp_pointer); pointer->New = guac_rdp_pointer_new; pointer->Free = guac_rdp_pointer_free; pointer->Set = guac_rdp_pointer_set; #ifdef HAVE_RDPPOINTER_SETNULL pointer->SetNull = guac_rdp_pointer_set_null; #endif #ifdef HAVE_RDPPOINTER_SETDEFAULT pointer->SetDefault = guac_rdp_pointer_set_default; #endif graphics_register_pointer(context->graphics, pointer); xfree(pointer); /* Set up GDI */ instance->update->EndPaint = guac_rdp_gdi_end_paint; instance->update->Palette = guac_rdp_gdi_palette_update; instance->update->SetBounds = guac_rdp_gdi_set_bounds; primary = instance->update->primary; primary->DstBlt = guac_rdp_gdi_dstblt; primary->PatBlt = guac_rdp_gdi_patblt; primary->ScrBlt = guac_rdp_gdi_scrblt; primary->MemBlt = guac_rdp_gdi_memblt; primary->OpaqueRect = guac_rdp_gdi_opaquerect; pointer_cache_register_callbacks(instance->update); glyph_cache_register_callbacks(instance->update); brush_cache_register_callbacks(instance->update); bitmap_cache_register_callbacks(instance->update); offscreen_cache_register_callbacks(instance->update); palette_cache_register_callbacks(instance->update); /* Init channels (pre-connect) */ if (freerdp_channels_pre_connect(channels, instance)) { guac_protocol_send_error(client->socket, "Error initializing RDP client channel manager"); guac_socket_flush(client->socket); return false; } return true; }
int guac_terminal_csi(guac_terminal* term, char c) { /* CSI function arguments */ static int argc = 0; static int argv[16] = {0}; /* Sequence prefix, if any */ static char private_mode_character = 0; /* Argument building counter and buffer */ static int argv_length = 0; static char argv_buffer[256]; /* Digits get concatenated into argv */ if (c >= '0' && c <= '9') { /* Concatenate digit if there is space in buffer */ if (argv_length < sizeof(argv_buffer)-1) argv_buffer[argv_length++] = c; } /* Specific non-digits stop the parameter, and possibly the sequence */ else if ((c >= 0x40 && c <= 0x7E) || c == ';') { int i, row, col, amount; bool* flag; /* At most 16 parameters */ if (argc < 16) { /* Finish parameter */ argv_buffer[argv_length] = 0; argv[argc++] = atoi(argv_buffer); /* Prepare for next parameter */ argv_length = 0; } /* Handle CSI functions */ switch (c) { /* @: Insert characters (scroll right) */ case '@': amount = argv[0]; if (amount == 0) amount = 1; /* Scroll right by amount */ if (term->cursor_col + amount < term->term_width) guac_terminal_copy_columns(term, term->cursor_row, term->cursor_col, term->term_width - amount - 1, amount); /* Clear left */ guac_terminal_clear_columns(term, term->cursor_row, term->cursor_col, term->cursor_col + amount - 1); break; /* A: Move up */ case 'A': /* Get move amount */ amount = argv[0]; if (amount == 0) amount = 1; /* Move cursor */ term->cursor_row -= amount; if (term->cursor_row < 0) term->cursor_row = 0; break; /* B: Move down */ case 'e': case 'B': /* Get move amount */ amount = argv[0]; if (amount == 0) amount = 1; /* Move cursor */ term->cursor_row += amount; if (term->cursor_row >= term->term_height) term->cursor_row = term->term_height - 1; break; /* C: Move right */ case 'a': case 'C': /* Get move amount */ amount = argv[0]; if (amount == 0) amount = 1; /* Move cursor */ term->cursor_col += amount; if (term->cursor_col >= term->term_width) term->cursor_col = term->term_width - 1; break; /* D: Move left */ case 'D': /* Get move amount */ amount = argv[0]; if (amount == 0) amount = 1; /* Move cursor */ term->cursor_col -= amount; if (term->cursor_col < 0) term->cursor_col = 0; break; /* E: Move cursor down given number rows, column 1 */ case 'E': /* Get move amount */ amount = argv[0]; if (amount == 0) amount = 1; /* Move cursor */ term->cursor_row += amount; if (term->cursor_row >= term->term_height) term->cursor_row = term->term_height - 1; /* Reset to column 1 */ term->cursor_col = 0; break; /* F: Move cursor up given number rows, column 1 */ case 'F': /* Get move amount */ amount = argv[0]; if (amount == 0) amount = 1; /* Move cursor */ term->cursor_row -= amount; if (term->cursor_row < 0) term->cursor_row = 0; /* Reset to column 1 */ term->cursor_col = 0; break; /* G: Move cursor, current row */ case '`': case 'G': col = argv[0]; if (col != 0) col--; term->cursor_col = col; break; /* H: Move cursor */ case 'f': case 'H': row = argv[0]; if (row != 0) row--; col = argv[1]; if (col != 0) col--; term->cursor_row = row; term->cursor_col = col; break; /* J: Erase display */ case 'J': /* Erase from cursor to end of display */ if (argv[0] == 0) guac_terminal_clear_range(term, term->cursor_row, term->cursor_col, term->term_height-1, term->term_width-1); /* Erase from start to cursor */ else if (argv[0] == 1) guac_terminal_clear_range(term, 0, 0, term->cursor_row, term->cursor_col); /* Entire screen */ else if (argv[0] == 2 || argv[0] == 3) guac_terminal_clear_range(term, 0, 0, term->term_height - 1, term->term_width - 1); break; /* K: Erase line */ case 'K': /* Erase from cursor to end of line */ if (argv[0] == 0) guac_terminal_clear_columns(term, term->cursor_row, term->cursor_col, term->term_width - 1); /* Erase from start to cursor */ else if (argv[0] == 1) guac_terminal_clear_columns(term, term->cursor_row, 0, term->cursor_col); /* Erase line */ else if (argv[0] == 2) guac_terminal_clear_columns(term, term->cursor_row, 0, term->term_width - 1); break; /* L: Insert blank lines (scroll down) */ case 'L': amount = argv[0]; if (amount == 0) amount = 1; guac_terminal_scroll_down(term, term->cursor_row, term->scroll_end, amount); break; /* M: Delete lines (scroll up) */ case 'M': amount = argv[0]; if (amount == 0) amount = 1; guac_terminal_scroll_up(term, term->cursor_row, term->scroll_end, amount); break; /* P: Delete characters (scroll left) */ case 'P': amount = argv[0]; if (amount == 0) amount = 1; /* Scroll left by amount */ if (term->cursor_col + amount < term->term_width) guac_terminal_copy_columns(term, term->cursor_row, term->cursor_col + amount, term->term_width - 1, -amount); /* Clear right */ guac_terminal_clear_columns(term, term->cursor_row, term->term_width - amount, term->term_width - 1); break; /* X: Erase characters (no scroll) */ case 'X': amount = argv[0]; if (amount == 0) amount = 1; /* Clear characters */ guac_terminal_clear_columns(term, term->cursor_row, term->cursor_col, term->cursor_col + amount - 1); break; /* ]: Linux Private CSI */ case ']': /* Explicitly ignored */ break; /* c: Identify */ case 'c': if (argv[0] == 0 && private_mode_character == 0) guac_terminal_send_string(term, GUAC_TERMINAL_VT102_ID); break; /* d: Move cursor, current col */ case 'd': row = argv[0]; if (row != 0) row--; term->cursor_row = row; break; /* g: Clear tab */ case 'g': /* Clear tab at current location */ if (argv[0] == 0) guac_terminal_unset_tab(term, term->cursor_col); /* Clear all tabs */ else if (argv[0] == 3) guac_terminal_clear_tabs(term); break; /* h: Set Mode */ case 'h': /* Look up flag and set */ flag = __guac_terminal_get_flag(term, argv[0], private_mode_character); if (flag != NULL) *flag = true; break; /* l: Reset Mode */ case 'l': /* Look up flag and clear */ flag = __guac_terminal_get_flag(term, argv[0], private_mode_character); if (flag != NULL) *flag = false; break; /* m: Set graphics rendition */ case 'm': for (i=0; i<argc; i++) { int value = argv[i]; /* Reset attributes */ if (value == 0) term->current_attributes = term->default_char.attributes; /* Bold */ else if (value == 1) term->current_attributes.bold = true; /* Underscore on */ else if (value == 4) term->current_attributes.underscore = true; /* Reverse video */ else if (value == 7) term->current_attributes.reverse = true; /* Normal intensity (not bold) */ else if (value == 21 || value == 22) term->current_attributes.bold = false; /* Reset underscore */ else if (value == 24) term->current_attributes.underscore = false; /* Reset reverse video */ else if (value == 27) term->current_attributes.reverse = false; /* Foreground */ else if (value >= 30 && value <= 37) term->current_attributes.foreground = value - 30; /* Underscore on, default foreground */ else if (value == 38) { term->current_attributes.underscore = true; term->current_attributes.foreground = term->default_char.attributes.foreground; } /* Underscore off, default foreground */ else if (value == 39) { term->current_attributes.underscore = false; term->current_attributes.foreground = term->default_char.attributes.foreground; } /* Background */ else if (value >= 40 && value <= 47) term->current_attributes.background = value - 40; /* Reset background */ else if (value == 49) term->current_attributes.background = term->default_char.attributes.background; } break; /* n: Status report */ case 'n': /* Device status report */ if (argv[0] == 5 && private_mode_character == 0) guac_terminal_send_string(term, GUAC_TERMINAL_OK); /* Cursor position report */ else if (argv[0] == 6 && private_mode_character == 0) guac_terminal_sendf(term, "\x1B[%i;%iR", term->cursor_row+1, term->cursor_col+1); break; /* q: Set keyboard LEDs */ case 'q': /* Explicitly ignored */ break; /* r: Set scrolling region */ case 'r': /* If parameters given, set region */ if (argc == 2) { term->scroll_start = argv[0]-1; term->scroll_end = argv[1]-1; } /* Otherwise, reset scrolling region */ else { term->scroll_start = 0; term->scroll_end = term->term_height - 1; } break; /* Save Cursor */ case 's': term->saved_cursor_row = term->cursor_row; term->saved_cursor_col = term->cursor_col; break; /* Restore Cursor */ case 'u': term->cursor_row = term->saved_cursor_row; if (term->cursor_row >= term->term_height) term->cursor_row = term->term_height - 1; term->cursor_col = term->saved_cursor_col; if (term->cursor_col >= term->term_width) term->cursor_col = term->term_width - 1; break; /* Warn of unhandled codes */ default: if (c != ';') { guac_client_log_info(term->client, "Unhandled CSI sequence: %c", c); for (i=0; i<argc; i++) guac_client_log_info(term->client, " -> argv[%i] = %i", i, argv[i]); } } /* If not a semicolon, end of CSI sequence */ if (c != ';') { term->char_handler = guac_terminal_echo; /* Reset parameters */ for (i=0; i<argc; i++) argv[i] = 0; /* Reset private mode character */ private_mode_character = 0; /* Reset argument counters */ argc = 0; argv_length = 0; } } /* Set private mode character if given and unset */ else if (c >= 0x3A && c <= 0x3F && private_mode_character == 0) private_mode_character = c; return 0; }
int guac_terminal_escape(guac_terminal* term, char c) { switch (c) { case '(': term->char_handler = guac_terminal_g0_charset; break; case ')': term->char_handler = guac_terminal_g1_charset; break; case ']': term->char_handler = guac_terminal_osc; break; case '[': term->char_handler = guac_terminal_csi; break; case '#': term->char_handler = guac_terminal_ctrl_func; break; /* Save Cursor (DECSC) */ case '7': term->saved_cursor_row = term->cursor_row; term->saved_cursor_col = term->cursor_col; term->char_handler = guac_terminal_echo; break; /* Restore Cursor (DECRC) */ case '8': term->cursor_row = term->saved_cursor_row; if (term->cursor_row >= term->term_height) term->cursor_row = term->term_height - 1; term->cursor_col = term->saved_cursor_col; if (term->cursor_col >= term->term_width) term->cursor_col = term->term_width - 1; term->char_handler = guac_terminal_echo; break; /* Index (IND) */ case 'D': term->cursor_row++; /* Scroll up if necessary */ if (term->cursor_row > term->scroll_end) { term->cursor_row = term->scroll_end; /* Scroll up by one row */ guac_terminal_scroll_up(term, term->scroll_start, term->scroll_end, 1); } term->char_handler = guac_terminal_echo; break; /* Next Line (NEL) */ case 'E': term->cursor_col = 0; term->cursor_row++; /* Scroll up if necessary */ if (term->cursor_row > term->scroll_end) { term->cursor_row = term->scroll_end; /* Scroll up by one row */ guac_terminal_scroll_up(term, term->scroll_start, term->scroll_end, 1); } term->char_handler = guac_terminal_echo; break; /* Set Tab (HTS) */ case 'H': guac_terminal_set_tab(term, term->cursor_col); break; /* Reverse Linefeed */ case 'M': term->cursor_row--; /* Scroll down if necessary */ if (term->cursor_row < term->scroll_start) { term->cursor_row = term->scroll_start; /* Scroll down by one row */ guac_terminal_scroll_down(term, term->scroll_start, term->scroll_end, 1); } term->char_handler = guac_terminal_echo; break; /* DEC Identify */ case 'Z': guac_terminal_send_string(term, GUAC_TERMINAL_VT102_ID); term->char_handler = guac_terminal_echo; break; /* Reset */ case 'c': guac_terminal_reset(term); break; default: guac_client_log_info(term->client, "Unhandled ESC sequence: %c", c); term->char_handler = guac_terminal_echo; } 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; }
void* ssh_client_thread(void* data) { guac_client* client = (guac_client*) data; ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; char name[1024]; guac_socket* socket = client->socket; char buffer[8192]; int bytes_read = -1234; int socket_fd; int stdout_fd = client_data->term->stdout_pipe_fd[1]; pthread_t input_thread; libssh2_init(0); /* Get username */ if (client_data->username[0] == 0) prompt(client, "Login as: ", client_data->username, sizeof(client_data->username), true); /* Send new name */ snprintf(name, sizeof(name)-1, "%s@%s", client_data->username, client_data->hostname); guac_protocol_send_name(socket, name); /* If key specified, import */ if (client_data->key_base64[0] != 0) { /* Attempt to read key without passphrase */ client_data->key = ssh_key_alloc(client_data->key_base64, strlen(client_data->key_base64), ""); /* On failure, attempt with passphrase */ if (client_data->key == NULL) { /* Prompt for passphrase if missing */ if (client_data->key_passphrase[0] == 0) prompt(client, "Key passphrase: ", client_data->key_passphrase, sizeof(client_data->key_passphrase), false); /* Import key with passphrase */ client_data->key = ssh_key_alloc(client_data->key_base64, strlen(client_data->key_base64), client_data->key_passphrase); /* If still failing, give up */ if (client_data->key == NULL) { guac_client_log_error(client, "Auth key import failed."); return NULL; } } /* end decrypt key with passphrase */ /* Success */ guac_client_log_info(client, "Auth key successfully imported."); } /* end if key given */ /* Otherwise, get password if not provided */ else if (client_data->password[0] == 0) prompt(client, "Password: "******"\x1B[H\x1B[J", 6); /* Open SSH session */ client_data->session = __guac_ssh_create_session(client, &socket_fd); if (client_data->session == NULL) { /* Already aborted within __guac_ssh_create_session() */ return NULL; } /* Open channel for terminal */ client_data->term_channel = libssh2_channel_open_session(client_data->session); if (client_data->term_channel == NULL) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to open terminal channel."); return NULL; } #ifdef ENABLE_SSH_AGENT /* Start SSH agent forwarding, if enabled */ if (client_data->enable_agent) { libssh2_session_callback_set(client_data->session, LIBSSH2_CALLBACK_AUTH_AGENT, (void*) ssh_auth_agent_callback); /* Request agent forwarding */ if (libssh2_channel_request_auth_agent(client_data->term_channel)) guac_client_log_error(client, "Agent forwarding request failed"); else guac_client_log_info(client, "Agent forwarding enabled."); } client_data->auth_agent = NULL; #endif /* Start SFTP session as well, if enabled */ if (client_data->enable_sftp) { /* Create SSH session specific for SFTP */ guac_client_log_info(client, "Reconnecting for SFTP..."); client_data->sftp_ssh_session = __guac_ssh_create_session(client, NULL); if (client_data->sftp_ssh_session == NULL) { /* Already aborted within __guac_ssh_create_session() */ return NULL; } /* Request SFTP */ client_data->sftp_session = libssh2_sftp_init(client_data->sftp_ssh_session); if (client_data->sftp_session == NULL) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to start SFTP session."); return NULL; } /* Set file handler */ client->file_handler = guac_sftp_file_handler; guac_client_log_info(client, "SFTP session initialized"); } /* Request PTY */ if (libssh2_channel_request_pty_ex(client_data->term_channel, "linux", sizeof("linux")-1, NULL, 0, client_data->term->term_width, client_data->term->term_height, 0, 0)) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to allocate PTY."); return NULL; } /* Request shell */ if (libssh2_channel_shell(client_data->term_channel)) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to associate shell with PTY."); return NULL; } /* Logged in */ guac_client_log_info(client, "SSH connection successful."); /* Start input thread */ if (pthread_create(&(input_thread), NULL, ssh_input_thread, (void*) client)) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start input thread"); return NULL; } /* Set non-blocking */ libssh2_session_set_blocking(client_data->session, 0); /* While data available, write to terminal */ bytes_read = 0; while (!libssh2_channel_eof(client_data->term_channel)) { /* Track total amount of data read */ int total_read = 0; /* Read terminal data */ bytes_read = libssh2_channel_read(client_data->term_channel, buffer, sizeof(buffer)); /* Attempt to write data received. Exit on failure. */ if (bytes_read > 0) { int written = guac_terminal_write_all(stdout_fd, buffer, bytes_read); if (written < 0) break; total_read += bytes_read; } else if (bytes_read < 0 && bytes_read != LIBSSH2_ERROR_EAGAIN) break; #ifdef ENABLE_SSH_AGENT /* If agent open, handle any agent packets */ if (client_data->auth_agent != NULL) { bytes_read = ssh_auth_agent_read(client_data->auth_agent); if (bytes_read > 0) total_read += bytes_read; else if (bytes_read < 0 && bytes_read != LIBSSH2_ERROR_EAGAIN) client_data->auth_agent = NULL; } #endif /* Wait for more data if reads turn up empty */ if (total_read == 0) { fd_set fds; struct timeval timeout; FD_ZERO(&fds); FD_SET(socket_fd, &fds); /* Wait for one second */ timeout.tv_sec = 1; timeout.tv_usec = 0; if (select(socket_fd+1, &fds, NULL, NULL, &timeout) < 0) break; } } /* Kill client and Wait for input thread to die */ guac_client_stop(client); pthread_join(input_thread, NULL); guac_client_log_info(client, "SSH connection ended."); return NULL; }
void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event) { #ifdef LEGACY_EVENT switch (event->event_type) { #else switch (GetMessageType(event->id)) { #endif case CliprdrChannel_MonitorReady: guac_rdp_process_cb_monitor_ready(client, event); break; case CliprdrChannel_FormatList: guac_rdp_process_cb_format_list(client, (RDP_CB_FORMAT_LIST_EVENT*) event); break; case CliprdrChannel_DataRequest: guac_rdp_process_cb_data_request(client, (RDP_CB_DATA_REQUEST_EVENT*) event); break; case CliprdrChannel_DataResponse: guac_rdp_process_cb_data_response(client, (RDP_CB_DATA_RESPONSE_EVENT*) event); break; default: #ifdef LEGACY_EVENT guac_client_log_info(client, "Unknown cliprdr event type: 0x%x", event->event_type); #else guac_client_log_info(client, "Unknown cliprdr event type: 0x%x", GetMessageType(event->id)); #endif } } void guac_rdp_process_cb_monitor_ready(guac_client* client, wMessage* event) { rdpChannels* channels = ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels; RDP_CB_FORMAT_LIST_EVENT* format_list = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new( CliprdrChannel_Class, CliprdrChannel_FormatList, NULL, NULL); /* Received notification of clipboard support. */ /* Respond with supported format list */ format_list->formats = (UINT32*) malloc(sizeof(UINT32)); format_list->formats[0] = CB_FORMAT_TEXT; format_list->num_formats = 1; freerdp_channels_send_event(channels, (wMessage*) format_list); }
static LIBSSH2_SESSION* __guac_ssh_create_session(guac_client* client, int* socket_fd) { int retval; int fd; struct addrinfo* addresses; struct addrinfo* current_address; char connected_address[1024]; char connected_port[64]; ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP }; /* Get socket */ fd = socket(AF_INET, SOCK_STREAM, 0); /* Get addresses connection */ if ((retval = getaddrinfo(client_data->hostname, client_data->port, &hints, &addresses))) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error parsing given address or port: %s", gai_strerror(retval)); return NULL; } /* Attempt connection to each address until success */ current_address = addresses; while (current_address != NULL) { int retval; /* Resolve hostname */ if ((retval = getnameinfo(current_address->ai_addr, current_address->ai_addrlen, connected_address, sizeof(connected_address), connected_port, sizeof(connected_port), NI_NUMERICHOST | NI_NUMERICSERV))) guac_client_log_info(client, "Unable to resolve host: %s", gai_strerror(retval)); /* Connect */ if (connect(fd, current_address->ai_addr, current_address->ai_addrlen) == 0) { guac_client_log_info(client, "Successfully connected to " "host %s, port %s", connected_address, connected_port); /* Done if successful connect */ break; } /* Otherwise log information regarding bind failure */ else guac_client_log_info(client, "Unable to connect to " "host %s, port %s: %s", connected_address, connected_port, strerror(errno)); current_address = current_address->ai_next; } /* If unable to connect to anything, fail */ if (current_address == NULL) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to connect to any addresses."); return NULL; } /* Free addrinfo */ freeaddrinfo(addresses); /* Open SSH session */ LIBSSH2_SESSION* session = libssh2_session_init_ex(NULL, NULL, NULL, client); if (session == NULL) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Session allocation failed."); return NULL; } /* Perform handshake */ if (libssh2_session_handshake(session, fd)) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "SSH handshake failed."); return NULL; } /* Save file descriptor */ if (socket_fd != NULL) *socket_fd = fd; /* Authenticate with key if available */ if (client_data->key != NULL) { if (!libssh2_userauth_publickey(session, client_data->username, (unsigned char*) client_data->key->public_key, client_data->key->public_key_length, __sign_callback, (void**) client_data->key)) return session; else { char* error_message; libssh2_session_last_error(session, &error_message, NULL, 0); guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, "Public key authentication failed: %s", error_message); return NULL; } } /* Authenticate with password */ if (!libssh2_userauth_password(session, client_data->username, client_data->password)) return session; else { char* error_message; libssh2_session_last_error(session, &error_message, NULL, 0); guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, "Password authentication failed: %s", error_message); return NULL; } }
void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) { guac_client* client = ((rdp_freerdp_context*) context)->client; const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface; int x = dstblt->nLeftRect; int y = dstblt->nTopRect; int w = dstblt->nWidth; int h = dstblt->nHeight; rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; pthread_mutex_lock(&(data->update_lock)); /* Clip operation to bounds */ __guac_rdp_clip_rect(data, &x, &y, &w, &h); switch (dstblt->bRop) { /* Blackness */ case 0: /* Send black rectangle */ guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, current_layer, 0, 0, 0, 255); break; /* DSTINVERT */ case 0x55: /* Invert */ guac_protocol_send_transfer(client->socket, current_layer, x, y, w, h, GUAC_TRANSFER_BINARY_NDEST, current_layer, x, y); break; /* NOP */ case 0xAA: break; /* Whiteness */ case 0xFF: guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, current_layer, 0xFF, 0xFF, 0xFF, 0xFF); break; /* Unsupported ROP3 */ default: guac_client_log_info(client, "guac_rdp_gdi_dstblt(rop3=0x%x)", dstblt->bRop); } pthread_mutex_unlock(&(data->update_lock)); }
void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) { /* * Note that this is not a full implementation of PATBLT. This is a * fallback implementation which only renders a solid block of background * color using the specified ROP3 operation, ignoring whatever brush * was actually specified. * * As libguac-client-rdp explicitly tells the server not to send PATBLT, * well-behaved RDP servers will not use this operation at all, while * others will at least have a fallback. */ /* Get client and current layer */ guac_client* client = ((rdp_freerdp_context*) context)->client; const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface; int x = patblt->nLeftRect; int y = patblt->nTopRect; int w = patblt->nWidth; int h = patblt->nHeight; rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; /* Layer for actual transfer */ guac_layer* buffer; /* * Warn that rendering is a fallback, as the server should not be sending * this order. */ guac_client_log_info(client, "Using fallback PATBLT (server is ignoring " "negotiated client capabilities)"); /* Clip operation to bounds */ __guac_rdp_clip_rect(data, &x, &y, &w, &h); /* Render rectangle based on ROP */ switch (patblt->bRop) { /* If blackness, send black rectangle */ case 0x00: guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, current_layer, 0x00, 0x00, 0x00, 0xFF); break; /* If NOP, do nothing */ case 0xAA: break; /* If operation is just a copy, send foreground only */ case 0xCC: case 0xF0: guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, current_layer, (patblt->foreColor >> 16) & 0xFF, (patblt->foreColor >> 8 ) & 0xFF, (patblt->foreColor ) & 0xFF, 0xFF); break; /* If whiteness, send white rectangle */ case 0xFF: guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, current_layer, 0xFF, 0xFF, 0xFF, 0xFF); break; /* Otherwise, invert entire rect */ default: /* Allocate buffer for transfer */ buffer = guac_client_alloc_buffer(client); /* Send rectangle stroke */ guac_protocol_send_rect(client->socket, buffer, 0, 0, w, h); /* Fill rectangle with fore color only */ guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, buffer, 0xFF, 0xFF, 0xFF, 0xFF); /* Transfer */ guac_protocol_send_transfer(client->socket, /* ... from buffer */ buffer, 0, 0, w, h, /* ... inverting */ GUAC_TRANSFER_BINARY_XOR, /* ... to current layer */ current_layer, x, y); /* Done with buffer */ guac_client_free_buffer(client, buffer); } }
void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) { guac_client* client = ((rdp_freerdp_context*) context)->client; guac_client_log_info(client, "guac_rdp_gdi_patblt()"); }