GUACAMOLE-208: Handle RDP disconnect reason codes.

Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/a78d52e6
Tree: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/a78d52e6
Diff: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/a78d52e6

Branch: refs/heads/master
Commit: a78d52e615429f7a8d3fd52e3bc63be340a42587
Parents: 0210b7d
Author: Michael Jumper <[email protected]>
Authored: Sat Feb 11 11:53:34 2017 -0800
Committer: Michael Jumper <[email protected]>
Committed: Wed Feb 15 21:23:46 2017 -0800

----------------------------------------------------------------------
 src/protocols/rdp/Makefile.am |   2 +
 src/protocols/rdp/error.c     | 146 +++++++++++++++++++++++++++++++++++++
 src/protocols/rdp/error.h     |  36 +++++++++
 src/protocols/rdp/rdp.c       |  17 ++---
 4 files changed, 192 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/a78d52e6/src/protocols/rdp/Makefile.am
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am
index a190003..85b5fb5 100644
--- a/src/protocols/rdp/Makefile.am
+++ b/src/protocols/rdp/Makefile.am
@@ -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                             \

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/a78d52e6/src/protocols/rdp/error.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/error.c b/src/protocols/rdp/error.c
new file mode 100644
index 0000000..e340362
--- /dev/null
+++ b/src/protocols/rdp/error.c
@@ -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);
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/a78d52e6/src/protocols/rdp/error.h
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/error.h b/src/protocols/rdp/error.h
new file mode 100644
index 0000000..469bd4a
--- /dev/null
+++ b/src/protocols/rdp/error.h
@@ -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
+

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/a78d52e6/src/protocols/rdp/rdp.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c
index 389c704..8269348 100644
--- a/src/protocols/rdp/rdp.c
+++ b/src/protocols/rdp/rdp.c
@@ -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;
         }

Reply via email to