GUACAMOLE-208: Handle RDP disconnect reason codes.

This commit is contained in:
Michael Jumper 2017-02-11 11:53:34 -08:00
parent 0210b7dc6b
commit a78d52e615
4 changed files with 192 additions and 9 deletions

View File

@ -29,6 +29,7 @@ libguac_client_rdp_la_SOURCES = \
audio_input.c \
client.c \
dvc.c \
error.c \
input.c \
keyboard.c \
ptr_string.c \
@ -96,6 +97,7 @@ noinst_HEADERS = \
audio_input.h \
client.h \
dvc.h \
error.h \
input.h \
keyboard.h \
ptr_string.h \

146
src/protocols/rdp/error.c Normal file
View File

@ -0,0 +1,146 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "config.h"
#include "error.h"
#include "rdp.h"
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
void guac_rdp_client_abort(guac_client* client) {
/*
* NOTE: The RDP status codes translated here are documented within
* [MS-RDPBCGR], section 2.2.5.1.1: "Set Error Info PDU Data", in the
* description of the "errorInfo" field.
*
* https://msdn.microsoft.com/en-us/library/cc240544.aspx
*/
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
freerdp* rdp_inst = rdp_client->rdp_inst;
guac_protocol_status status;
const char* message;
/* Read disconnect reason code from connection */
int error_info = freerdp_error_info(rdp_inst);
/* Translate reason code into Guacamole protocol status */
switch (error_info) {
/* Normal disconnect */
case 0x0: /* ERRINFO_SUCCESS */
status = GUAC_PROTOCOL_STATUS_SUCCESS;
message = "Disconnected.";
break;
/* Forced disconnect (possibly by admin) */
case 0x1: /* ERRINFO_RPC_INITIATED_DISCONNECT */
status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED;
message = "Forcibly disconnected.";
break;
/* The user was logged off (possibly by admin) */
case 0x2: /* ERRINFO_RPC_INITIATED_LOGOFF */
status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED;
message = "Logged off.";
break;
/* The user was idle long enough that the RDP server disconnected */
case 0x3: /* ERRINFO_IDLE_TIMEOUT */
status = GUAC_PROTOCOL_STATUS_SESSION_TIMEOUT;
message = "Idle session time limit exceeded.";
break;
/* The user's session has been active for too long */
case 0x4: /* ERRINFO_LOGON_TIMEOUT */
status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED;
message = "Active session time limit exceeded.";
break;
/* Another user logged on, disconnecting this user */
case 0x5: /* ERRINFO_DISCONNECTED_BY_OTHER_CONNECTION */
status = GUAC_PROTOCOL_STATUS_SESSION_CONFLICT;
message = "Disconnected by other connection.";
break;
/* The RDP server is refusing to service the connection */
case 0x6: /* ERRINFO_OUT_OF_MEMORY */
case 0x7: /* ERRINFO_SERVER_DENIED_CONNECTION */
status = GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE;
message = "Server refused connection.";
break;
/* The user does not have permission to connect */
case 0x9: /* ERRINFO_SERVER_INSUFFICIENT_PRIVILEGES */
status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN;
message = "Insufficient privileges.";
break;
/* The user's credentials have expired */
case 0xA: /* ERRINFO_SERVER_FRESH_CREDENTIALS_REQUIRED */
status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN;
message = "Credentials expired.";
break;
/* The user manually disconnected using an administrative tool within
* the session */
case 0xB: /* ERRINFO_RPC_INITIATED_DISCONNECT_BYUSER */
status = GUAC_PROTOCOL_STATUS_SUCCESS;
message = "Manually disconnected.";
break;
/* The user manually logged off */
case 0xC: /* ERRINFO_LOGOFF_BY_USER */
status = GUAC_PROTOCOL_STATUS_SUCCESS;
message = "Manually logged off.";
break;
/* Unimplemented/unknown disconnect reason code */
default:
status = GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR;
message = "Upstream error.";
}
/* Send error code if an error occurred */
if (status != GUAC_PROTOCOL_STATUS_SUCCESS) {
guac_protocol_send_error(client->socket, message, status);
guac_socket_flush(client->socket);
}
/* Log human-readable description of disconnect at info level */
guac_client_log(client, GUAC_LOG_INFO, "RDP server closed connection: %s",
message);
/* Log internal disconnect reason code at debug level */
if (error_info)
guac_client_log(client, GUAC_LOG_DEBUG, "Disconnect reason "
"code: 0x%X.", error_info);
/* Abort connection */
guac_client_stop(client);
}

36
src/protocols/rdp/error.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef GUAC_RDP_ERROR_H
#define GUAC_RDP_ERROR_H
#include <guacamole/client.h>
/**
* Stops the current connection due to the RDP server disconnecting. If the RDP
* server provided a reason for disconnecting, that reason will be logged, and
* an appropriate error code will be sent to the Guacamole client.
*
* @param client
* The Guacamole client to disconnect.
*/
void guac_rdp_client_abort(guac_client* client);
#endif

View File

@ -25,6 +25,7 @@
#include "common/display.h"
#include "common/recording.h"
#include "dvc.h"
#include "error.h"
#include "keyboard.h"
#include "rdp.h"
#include "rdp_bitmap.h"
@ -626,7 +627,7 @@ static int rdp_guac_client_wait_for_messages(guac_client* client,
return 0;
/* Otherwise, return as error */
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
"Error waiting for file descriptor.");
return -1;
@ -721,7 +722,7 @@ static int guac_rdp_handle_connection(guac_client* client) {
/* Connect to RDP server */
if (!freerdp_connect(rdp_inst)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND,
"Error connecting to RDP server");
return 1;
}
@ -763,7 +764,7 @@ static int guac_rdp_handle_connection(guac_client* client) {
/* Check the libfreerdp fds */
if (!freerdp_check_fds(rdp_inst)) {
guac_client_abort(client,
GUAC_PROTOCOL_STATUS_SERVER_ERROR,
GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
"Error handling RDP file descriptors");
pthread_mutex_unlock(&(rdp_client->rdp_lock));
return 1;
@ -772,7 +773,7 @@ static int guac_rdp_handle_connection(guac_client* client) {
/* Check channel fds */
if (!freerdp_channels_check_fds(channels, rdp_inst)) {
guac_client_abort(client,
GUAC_PROTOCOL_STATUS_SERVER_ERROR,
GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
"Error handling RDP channel file descriptors");
pthread_mutex_unlock(&(rdp_client->rdp_lock));
return 1;
@ -801,9 +802,7 @@ static int guac_rdp_handle_connection(guac_client* client) {
/* Handle RDP disconnect */
if (freerdp_shall_disconnect(rdp_inst)) {
guac_client_stop(client);
guac_client_log(client, GUAC_LOG_INFO,
"RDP server closed connection");
guac_rdp_client_abort(client);
pthread_mutex_unlock(&(rdp_client->rdp_lock));
return 1;
}
@ -843,7 +842,7 @@ static int guac_rdp_handle_connection(guac_client* client) {
/* If an error occurred, fail */
if (wait_result < 0)
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
"Connection closed.");
/* Flush frame */
@ -995,7 +994,7 @@ void* guac_rdp_client_thread(void* data) {
if (rdp_client->sftp_filesystem == NULL) {
guac_common_ssh_destroy_session(rdp_client->sftp_session);
guac_common_ssh_destroy_user(rdp_client->sftp_user);
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
"SFTP connection failed.");
return NULL;
}