You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by vn...@apache.org on 2018/09/22 00:27:25 UTC

[1/8] guacamole-server git commit: GUACAMOLE-622: Do not allow STDIN to be redirected if the terminal is not yet started.

Repository: guacamole-server
Updated Branches:
  refs/heads/master d7cfff324 -> 54fda2136


GUACAMOLE-622: Do not allow STDIN to be redirected if the terminal is not yet started.


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

Branch: refs/heads/master
Commit: 1178b475dad6b8c567041dda8e3fabd34a584f75
Parents: 286cbf3
Author: Michael Jumper <mj...@apache.org>
Authored: Sat Sep 1 21:41:13 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Sun Sep 2 23:04:14 2018 -0700

----------------------------------------------------------------------
 src/terminal/terminal-stdin-stream.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/1178b475/src/terminal/terminal-stdin-stream.c
----------------------------------------------------------------------
diff --git a/src/terminal/terminal-stdin-stream.c b/src/terminal/terminal-stdin-stream.c
index 1f662d6..02f5b94 100644
--- a/src/terminal/terminal-stdin-stream.c
+++ b/src/terminal/terminal-stdin-stream.c
@@ -103,6 +103,22 @@ static int guac_terminal_input_stream_end_handler(guac_user* user,
 static int __guac_terminal_send_stream(guac_terminal* term, guac_user* user,
         guac_stream* stream) {
 
+    /* Deny redirecting STDIN if terminal is not started */
+    if (!term->started) {
+
+        guac_user_log(user, GUAC_LOG_DEBUG, "Attempt to direct the contents "
+                "of an inbound stream to STDIN denied. The terminal is not "
+                "yet ready for input.");
+
+        guac_protocol_send_ack(user->socket, stream,
+                "Terminal not yet started.",
+                GUAC_PROTOCOL_STATUS_RESOURCE_CONFLICT);
+
+        guac_socket_flush(user->socket);
+        return 1;
+
+    }
+
     /* If a stream is already being used for STDIN, deny creation of
      * further streams */
     if (term->input_stream != NULL) {


[6/8] guacamole-server git commit: GUACAMOLE-622: Start terminal for telnet only after login status is known (if login success/failure detection enabled).

Posted by vn...@apache.org.
GUACAMOLE-622: Start terminal for telnet only after login status is known (if login success/failure detection enabled).


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

Branch: refs/heads/master
Commit: 442b1d5cc2a460a9f6d4797e9b1428c181aee2c4
Parents: 1178b47
Author: Michael Jumper <mj...@apache.org>
Authored: Sun Sep 2 14:32:05 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Sep 21 14:29:01 2018 -0700

----------------------------------------------------------------------
 src/protocols/telnet/settings.c |  87 ++++++++++++++++++++++++------
 src/protocols/telnet/settings.h |  24 +++++++++
 src/protocols/telnet/telnet.c   | 102 ++++++++++++++++++++++++++++-------
 3 files changed, 178 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/442b1d5c/src/protocols/telnet/settings.c
----------------------------------------------------------------------
diff --git a/src/protocols/telnet/settings.c b/src/protocols/telnet/settings.c
index bef5cfa..890d5fe 100644
--- a/src/protocols/telnet/settings.c
+++ b/src/protocols/telnet/settings.c
@@ -53,6 +53,8 @@ const char* GUAC_TELNET_CLIENT_ARGS[] = {
     "backspace",
     "terminal-type",
     "scrollback",
+    "login-success-regex",
+    "login-failure-regex",
     NULL
 };
 
@@ -196,12 +198,31 @@ enum TELNET_ARGS_IDX {
      */
     IDX_SCROLLBACK,
 
+    /**
+     * The regular expression to use when searching for whether login was
+     * successful. This parameter is optional. If given, the
+     * "login-failure-regex" parameter must also be specified, and the first
+     * frame of the Guacamole connection will be withheld until login
+     * success/failure has been determined.
+     */
+    IDX_LOGIN_SUCCESS_REGEX,
+
+    /**
+     * The regular expression to use when searching for whether login was
+     * unsuccessful. This parameter is optional. If given, the
+     * "login-success-regex" parameter must also be specified, and the first
+     * frame of the Guacamole connection will be withheld until login
+     * success/failure has been determined.
+     */
+    IDX_LOGIN_FAILURE_REGEX,
+
     TELNET_ARGS_COUNT
 };
 
 /**
- * Compiles the given regular expression, returning NULL if compilation fails.
- * The returned regex_t must be freed with regfree() AND free().
+ * Compiles the given regular expression, returning NULL if compilation fails
+ * or of the given regular expression is NULL. The returned regex_t must be
+ * freed with regfree() AND free(), or with guac_telnet_regex_free().
  *
  * @param user
  *     The user who provided the setting associated with the given regex
@@ -211,10 +232,15 @@ enum TELNET_ARGS_IDX {
  *     The regular expression pattern to compile.
  *
  * @return
- *     The compiled regular expression, or NULL if compilation fails.
+ *     The compiled regular expression, or NULL if compilation fails or NULL
+ *     was originally provided for the pattern.
  */
 static regex_t* guac_telnet_compile_regex(guac_user* user, char* pattern) {
 
+    /* Nothing to compile if no pattern provided */
+    if (pattern == NULL)
+        return NULL;
+
     int compile_result;
     regex_t* regex = malloc(sizeof(regex_t));
 
@@ -233,6 +259,14 @@ static regex_t* guac_telnet_compile_regex(guac_user* user, char* pattern) {
     return regex;
 }
 
+void guac_telnet_regex_free(regex_t** regex) {
+    if (*regex != NULL) {
+        regfree(*regex);
+        free(*regex);
+        *regex = NULL;
+    }
+}
+
 guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
         int argc, const char** argv) {
 
@@ -256,7 +290,7 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
         guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
                 IDX_USERNAME, NULL);
 
-    /* Read username regex only if password is specified */
+    /* Read username regex only if username is specified */
     if (settings->username != NULL) {
         settings->username_regex = guac_telnet_compile_regex(user,
             guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
@@ -275,6 +309,35 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
                     IDX_PASSWORD_REGEX, GUAC_TELNET_DEFAULT_PASSWORD_REGEX));
     }
 
+    /* Read optional login success detection regex */
+    settings->login_success_regex = guac_telnet_compile_regex(user,
+            guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
+                    IDX_LOGIN_SUCCESS_REGEX, NULL));
+
+    /* Read optional login failure detection regex */
+    settings->login_failure_regex = guac_telnet_compile_regex(user,
+            guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
+                    IDX_LOGIN_FAILURE_REGEX, NULL));
+
+    /* Both login success and login failure regexes must be provided if either
+     * is present at all */
+    if (settings->login_success_regex != NULL
+            && settings->login_failure_regex == NULL) {
+        guac_telnet_regex_free(&settings->login_success_regex);
+        guac_user_log(user, GUAC_LOG_WARNING, "Ignoring provided value for "
+                "\"%s\" as \"%s\" must also be provided.",
+                GUAC_TELNET_CLIENT_ARGS[IDX_LOGIN_SUCCESS_REGEX],
+                GUAC_TELNET_CLIENT_ARGS[IDX_LOGIN_FAILURE_REGEX]);
+    }
+    else if (settings->login_failure_regex != NULL
+            && settings->login_success_regex == NULL) {
+        guac_telnet_regex_free(&settings->login_failure_regex);
+        guac_user_log(user, GUAC_LOG_WARNING, "Ignoring provided value for "
+                "\"%s\" as \"%s\" must also be provided.",
+                GUAC_TELNET_CLIENT_ARGS[IDX_LOGIN_FAILURE_REGEX],
+                GUAC_TELNET_CLIENT_ARGS[IDX_LOGIN_SUCCESS_REGEX]);
+    }
+
     /* Read-only mode */
     settings->read_only =
         guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv,
@@ -380,17 +443,11 @@ void guac_telnet_settings_free(guac_telnet_settings* settings) {
     free(settings->username);
     free(settings->password);
 
-    /* Free username regex (if allocated) */
-    if (settings->username_regex != NULL) {
-        regfree(settings->username_regex);
-        free(settings->username_regex);
-    }
-
-    /* Free password regex (if allocated) */
-    if (settings->password_regex != NULL) {
-        regfree(settings->password_regex);
-        free(settings->password_regex);
-    }
+    /* Free various regexes */
+    guac_telnet_regex_free(&settings->username_regex);
+    guac_telnet_regex_free(&settings->password_regex);
+    guac_telnet_regex_free(&settings->login_success_regex);
+    guac_telnet_regex_free(&settings->login_failure_regex);
 
     /* Free display preferences */
     free(settings->font_name);

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/442b1d5c/src/protocols/telnet/settings.h
----------------------------------------------------------------------
diff --git a/src/protocols/telnet/settings.h b/src/protocols/telnet/settings.h
index 83ec431..86302b7 100644
--- a/src/protocols/telnet/settings.h
+++ b/src/protocols/telnet/settings.h
@@ -118,6 +118,20 @@ typedef struct guac_telnet_settings {
     regex_t* password_regex;
 
     /**
+     * The regular expression to use when searching for whether login was
+     * successful. If no such regex is specified, or if no login failure regex
+     * was specified, this will be NULL.
+     */
+    regex_t* login_success_regex;
+
+    /**
+     * The regular expression to use when searching for whether login failed.
+     * If no such regex is specified, or if no login success regex was
+     * specified, this will be NULL.
+     */
+    regex_t* login_failure_regex;
+
+    /**
      * Whether this connection is read-only, and user input should be dropped.
      */
     bool read_only;
@@ -254,6 +268,16 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
         int argc, const char** argv);
 
 /**
+ * Frees the regex pointed to by the given pointer, assigning the value NULL to
+ * that pointer once the regex is freed. If the pointer already contains NULL,
+ * this function has no effect.
+ *
+ * @param regex
+ *     The address of the pointer to the regex that should be freed.
+ */
+void guac_telnet_regex_free(regex_t** regex);
+
+/**
  * Frees the given guac_telnet_settings object, having been previously
  * allocated via guac_telnet_parse_args().
  *

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/442b1d5c/src/protocols/telnet/telnet.c
----------------------------------------------------------------------
diff --git a/src/protocols/telnet/telnet.c b/src/protocols/telnet/telnet.c
index c9a636c..36e4cac 100644
--- a/src/protocols/telnet/telnet.c
+++ b/src/protocols/telnet/telnet.c
@@ -31,6 +31,7 @@
 #include <netinet/in.h>
 #include <poll.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -84,10 +85,32 @@ static int __guac_telnet_write_all(int fd, const char* buffer, int size) {
 /**
  * Searches for a line matching the stored password regex, appending the given
  * buffer to the internal pattern matching buffer. The internal pattern match
- * buffer is cleared whenever a newline is read. Returns TRUE if a match is found and the
- * value is sent.
+ * buffer is cleared whenever a newline is read. Returns true if a match is
+ * found and the value is sent. An enter keypress is automatically sent after
+ * the value is sent.
+ *
+ * @param client
+ *     The guac_client associated with the telnet session.
+ *
+ * @param regex
+ *     The regex to search for within the output of the telnet session
+ *     associated with the given client.
+ *
+ * @param value
+ *     The string value to send once a match is found, or NULL if no value
+ *     should be sent.
+ *
+ * @param buffer
+ *     The buffer of received data to search through.
+ *
+ * @param size
+ *     The size of the given buffer, in bytes.
+ *
+ * @return
+ *     true if a match is found, false otherwise.
  */
-static bool __guac_telnet_regex_search(guac_client* client, regex_t* regex, char* value, const char* buffer, int size) {
+static bool __guac_telnet_regex_search(guac_client* client, regex_t* regex,
+        char* value, const char* buffer, int size) {
 
     static char line_buffer[1024] = {0};
     static int length = 0;
@@ -123,16 +146,17 @@ static bool __guac_telnet_regex_search(guac_client* client, regex_t* regex, char
     if (regexec(regex, line_buffer, 0, NULL, 0) == 0) {
 
         /* Send value */
-        guac_terminal_send_string(telnet_client->term, value);
-        guac_terminal_send_key(telnet_client->term, 0xFF0D, 1);
-        guac_terminal_send_key(telnet_client->term, 0xFF0D, 0);
+        if (value != NULL) {
+            guac_terminal_send_string(telnet_client->term, value);
+            guac_terminal_send_string(telnet_client->term, "\x0D");
+        }
 
         /* Stop searching for prompt */
-        return TRUE;
+        return true;
 
     }
 
-    return FALSE;
+    return false;
 }
 
 /**
@@ -158,9 +182,7 @@ static void __guac_telnet_event_handler(telnet_t* telnet, telnet_event_t* event,
                             settings->username_regex, settings->username,
                             event->data.buffer, event->data.size)) {
                     guac_client_log(client, GUAC_LOG_DEBUG, "Username sent");
-                    regfree(settings->username_regex);
-                    free(settings->username_regex);
-                    settings->username_regex = NULL;
+                    guac_telnet_regex_free(&settings->username_regex);
                 }
             }
 
@@ -172,18 +194,52 @@ static void __guac_telnet_event_handler(telnet_t* telnet, telnet_event_t* event,
 
                     guac_client_log(client, GUAC_LOG_DEBUG, "Password sent");
 
-                    /* Do not continue searching for username once password is sent */
-                    if (settings->username_regex != NULL) {
-                        regfree(settings->username_regex);
-                        free(settings->username_regex);
-                        settings->username_regex = NULL;
-                    }
+                    /* Do not continue searching for username/password once password is sent */
+                    guac_telnet_regex_free(&settings->username_regex);
+                    guac_telnet_regex_free(&settings->password_regex);
 
-                    regfree(settings->password_regex);
-                    free(settings->password_regex);
-                    settings->password_regex = NULL;
                 }
             }
+
+            /* Continue search for login success */
+            if (settings->login_success_regex != NULL) {
+                if (__guac_telnet_regex_search(client,
+                            settings->login_success_regex, NULL,
+                            event->data.buffer, event->data.size)) {
+
+                    /* Allow terminal to render now that login has been deemed successful */
+                    guac_client_log(client, GUAC_LOG_DEBUG, "Login successful");
+                    guac_terminal_start(telnet_client->term);
+
+                    /* Stop all searches */
+                    guac_telnet_regex_free(&settings->username_regex);
+                    guac_telnet_regex_free(&settings->password_regex);
+                    guac_telnet_regex_free(&settings->login_success_regex);
+                    guac_telnet_regex_free(&settings->login_failure_regex);
+
+                }
+            }
+
+            /* Continue search for login failure */
+            if (settings->login_failure_regex != NULL) {
+                if (__guac_telnet_regex_search(client,
+                            settings->login_failure_regex, NULL,
+                            event->data.buffer, event->data.size)) {
+
+                    /* Advise that login has failed and connection should be closed */
+                    guac_client_abort(client,
+                            GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                            "Login failed");
+
+                    /* Stop all searches */
+                    guac_telnet_regex_free(&settings->username_regex);
+                    guac_telnet_regex_free(&settings->password_regex);
+                    guac_telnet_regex_free(&settings->login_success_regex);
+                    guac_telnet_regex_free(&settings->login_failure_regex);
+
+                }
+            }
+
             break;
 
         /* Data destined for remote end */
@@ -508,6 +564,12 @@ void* guac_telnet_client_thread(void* data) {
     /* Logged in */
     guac_client_log(client, GUAC_LOG_INFO, "Telnet connection successful.");
 
+    /* Allow terminal to render if login success/failure detection is not
+     * enabled */
+    if (settings->login_success_regex == NULL
+            && settings->login_failure_regex == NULL)
+        guac_terminal_start(telnet_client->term);
+
     /* Start input thread */
     if (pthread_create(&(input_thread), NULL, __guac_telnet_input_thread, (void*) client)) {
         guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start input thread");


[7/8] guacamole-server git commit: GUACAMOLE-622: Match each line against all regexes.

Posted by vn...@apache.org.
GUACAMOLE-622: Match each line against all regexes.


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

Branch: refs/heads/master
Commit: 462d494ed865a0fd47ef6cb22d23089d12f2b1ac
Parents: 442b1d5
Author: Michael Jumper <mj...@apache.org>
Authored: Sun Sep 2 22:59:18 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Sep 21 14:29:01 2018 -0700

----------------------------------------------------------------------
 src/protocols/telnet/telnet.c | 240 +++++++++++++++++++++----------------
 1 file changed, 137 insertions(+), 103 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/462d494e/src/protocols/telnet/telnet.c
----------------------------------------------------------------------
diff --git a/src/protocols/telnet/telnet.c b/src/protocols/telnet/telnet.c
index 36e4cac..17abd68 100644
--- a/src/protocols/telnet/telnet.c
+++ b/src/protocols/telnet/telnet.c
@@ -83,65 +83,31 @@ static int __guac_telnet_write_all(int fd, const char* buffer, int size) {
 }
 
 /**
- * Searches for a line matching the stored password regex, appending the given
- * buffer to the internal pattern matching buffer. The internal pattern match
- * buffer is cleared whenever a newline is read. Returns true if a match is
- * found and the value is sent. An enter keypress is automatically sent after
- * the value is sent.
+ * Matches the given line against the given regex, returning true and sending
+ * the given value if a match is found. An enter keypress is automatically
+ * sent after the value is sent.
  *
  * @param client
  *     The guac_client associated with the telnet session.
  *
  * @param regex
- *     The regex to search for within the output of the telnet session
- *     associated with the given client.
+ *     The regex to search for within the given line buffer.
  *
  * @param value
- *     The string value to send once a match is found, or NULL if no value
- *     should be sent.
+ *     The string value to send through STDIN of the telnet session if a
+ *     match is found, or NULL if no value should be sent.
  *
- * @param buffer
- *     The buffer of received data to search through.
- *
- * @param size
- *     The size of the given buffer, in bytes.
+ * @param line_buffer
+ *     The line of character data to test.
  *
  * @return
  *     true if a match is found, false otherwise.
  */
-static bool __guac_telnet_regex_search(guac_client* client, regex_t* regex,
-        char* value, const char* buffer, int size) {
-
-    static char line_buffer[1024] = {0};
-    static int length = 0;
+static bool guac_telnet_regex_exec(guac_client* client, regex_t* regex,
+        const char* value, const char* line_buffer) {
 
     guac_telnet_client* telnet_client = (guac_telnet_client*) client->data;
 
-    int i;
-    const char* current;
-
-    /* Ensure line buffer contains only the most recent line */
-    current = buffer;
-    for (i = 0; i < size; i++) {
-
-        /* Reset line buffer and shift input buffer for each newline */
-        if (*(current++) == '\n') {
-            length = 0;
-            buffer += i;
-            size -= i;
-            i = 0;
-        }
-    }
-
-    /* Truncate if necessary */
-    if (size + length + 1 > sizeof(line_buffer))
-        size = sizeof(line_buffer) - length - 1;
-
-    /* Append to line */
-    memcpy(&(line_buffer[length]), buffer, size);
-    length += size;
-    line_buffer[length] = '\0';
-
     /* Send value upon match */
     if (regexec(regex, line_buffer, 0, NULL, 0) == 0) {
 
@@ -157,89 +123,157 @@ static bool __guac_telnet_regex_search(guac_client* client, regex_t* regex,
     }
 
     return false;
+
 }
 
 /**
- * Event handler, as defined by libtelnet. This function is passed to
- * telnet_init() and will be called for every event fired by libtelnet,
- * including feature enable/disable and receipt/transmission of data.
+ * Matches the given line against the various stored regexes, automatically
+ * sending the configured username, password, or reporting login
+ * success/failure depending on context. If no search is in progress, either
+ * because no regexes have been defined or because all applicable searches have
+ * completed, this function has no effect.
+ *
+ * @param client
+ *     The guac_client associated with the telnet session.
+ *
+ * @param line_buffer
+ *     The line of character data to test.
  */
-static void __guac_telnet_event_handler(telnet_t* telnet, telnet_event_t* event, void* data) {
+static void guac_telnet_search_line(guac_client* client, const char* line_buffer) {
 
-    guac_client* client = (guac_client*) data;
     guac_telnet_client* telnet_client = (guac_telnet_client*) client->data;
     guac_telnet_settings* settings = telnet_client->settings;
 
-    switch (event->type) {
+    /* Continue search for username prompt */
+    if (settings->username_regex != NULL) {
+        if (guac_telnet_regex_exec(client, settings->username_regex,
+                    settings->username, line_buffer)) {
+            guac_client_log(client, GUAC_LOG_DEBUG, "Username sent");
+            guac_telnet_regex_free(&settings->username_regex);
+        }
+    }
 
-        /* Terminal output received */
-        case TELNET_EV_DATA:
-            guac_terminal_write(telnet_client->term, event->data.buffer, event->data.size);
+    /* Continue search for password prompt */
+    if (settings->password_regex != NULL) {
+        if (guac_telnet_regex_exec(client, settings->password_regex,
+                    settings->password, line_buffer)) {
 
-            /* Continue search for username prompt */
-            if (settings->username_regex != NULL) {
-                if (__guac_telnet_regex_search(client,
-                            settings->username_regex, settings->username,
-                            event->data.buffer, event->data.size)) {
-                    guac_client_log(client, GUAC_LOG_DEBUG, "Username sent");
-                    guac_telnet_regex_free(&settings->username_regex);
-                }
-            }
+            guac_client_log(client, GUAC_LOG_DEBUG, "Password sent");
 
-            /* Continue search for password prompt */
-            if (settings->password_regex != NULL) {
-                if (__guac_telnet_regex_search(client,
-                            settings->password_regex, settings->password,
-                            event->data.buffer, event->data.size)) {
+            /* Do not continue searching for username/password once password is sent */
+            guac_telnet_regex_free(&settings->username_regex);
+            guac_telnet_regex_free(&settings->password_regex);
 
-                    guac_client_log(client, GUAC_LOG_DEBUG, "Password sent");
+        }
+    }
 
-                    /* Do not continue searching for username/password once password is sent */
-                    guac_telnet_regex_free(&settings->username_regex);
-                    guac_telnet_regex_free(&settings->password_regex);
+    /* Continue search for login success */
+    if (settings->login_success_regex != NULL) {
+        if (guac_telnet_regex_exec(client, settings->login_success_regex,
+                    NULL, line_buffer)) {
 
-                }
-            }
+            /* Allow terminal to render now that login has been deemed successful */
+            guac_client_log(client, GUAC_LOG_DEBUG, "Login successful");
+            guac_terminal_start(telnet_client->term);
+
+            /* Stop all searches */
+            guac_telnet_regex_free(&settings->username_regex);
+            guac_telnet_regex_free(&settings->password_regex);
+            guac_telnet_regex_free(&settings->login_success_regex);
+            guac_telnet_regex_free(&settings->login_failure_regex);
 
-            /* Continue search for login success */
-            if (settings->login_success_regex != NULL) {
-                if (__guac_telnet_regex_search(client,
-                            settings->login_success_regex, NULL,
-                            event->data.buffer, event->data.size)) {
+        }
+    }
 
-                    /* Allow terminal to render now that login has been deemed successful */
-                    guac_client_log(client, GUAC_LOG_DEBUG, "Login successful");
-                    guac_terminal_start(telnet_client->term);
+    /* Continue search for login failure */
+    if (settings->login_failure_regex != NULL) {
+        if (guac_telnet_regex_exec(client, settings->login_failure_regex,
+                    NULL, line_buffer)) {
 
-                    /* Stop all searches */
-                    guac_telnet_regex_free(&settings->username_regex);
-                    guac_telnet_regex_free(&settings->password_regex);
-                    guac_telnet_regex_free(&settings->login_success_regex);
-                    guac_telnet_regex_free(&settings->login_failure_regex);
+            /* Advise that login has failed and connection should be closed */
+            guac_client_abort(client,
+                    GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                    "Login failed");
 
-                }
-            }
+            /* Stop all searches */
+            guac_telnet_regex_free(&settings->username_regex);
+            guac_telnet_regex_free(&settings->password_regex);
+            guac_telnet_regex_free(&settings->login_success_regex);
+            guac_telnet_regex_free(&settings->login_failure_regex);
+
+        }
+    }
 
-            /* Continue search for login failure */
-            if (settings->login_failure_regex != NULL) {
-                if (__guac_telnet_regex_search(client,
-                            settings->login_failure_regex, NULL,
-                            event->data.buffer, event->data.size)) {
+}
 
-                    /* Advise that login has failed and connection should be closed */
-                    guac_client_abort(client,
-                            GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
-                            "Login failed");
+/**
+ * Searches for a line matching the various stored regexes, automatically
+ * sending the configured username, password, or reporting login
+ * success/failure depending on context. If no search is in progress, either
+ * because no regexes have been defined or because all applicable searches
+ * have completed, this function has no effect.
+ *
+ * @param client
+ *     The guac_client associated with the telnet session.
+ *
+ * @param buffer
+ *     The buffer of received data to search through.
+ *
+ * @param size
+ *     The size of the given buffer, in bytes.
+ */
+static void guac_telnet_search(guac_client* client, const char* buffer, int size) {
+
+    static char line_buffer[1024] = {0};
+    static int length = 0;
 
-                    /* Stop all searches */
-                    guac_telnet_regex_free(&settings->username_regex);
-                    guac_telnet_regex_free(&settings->password_regex);
-                    guac_telnet_regex_free(&settings->login_success_regex);
-                    guac_telnet_regex_free(&settings->login_failure_regex);
+    /* Append all characters in buffer to current line */
+    const char* current = buffer;
+    for (int i = 0; i < size; i++) {
 
-                }
+        char c = *(current++);
+
+        /* Attempt pattern match and clear buffer upon reading newline */
+        if (c == '\n') {
+            if (length > 0) {
+                line_buffer[length] = '\0';
+                guac_telnet_search_line(client, line_buffer);
+                length = 0;
             }
+        }
+
+        /* Append all non-newline characters to line buffer as long as space
+         * remains */
+        else if (length < sizeof(line_buffer) - 1)
+            line_buffer[length++] = c;
+
+    }
 
+    /* Attempt pattern match if an unfinished line remains (may be a prompt) */
+    if (length > 0) {
+        line_buffer[length] = '\0';
+        guac_telnet_search_line(client, line_buffer);
+    }
+
+}
+
+/**
+ * Event handler, as defined by libtelnet. This function is passed to
+ * telnet_init() and will be called for every event fired by libtelnet,
+ * including feature enable/disable and receipt/transmission of data.
+ */
+static void __guac_telnet_event_handler(telnet_t* telnet, telnet_event_t* event, void* data) {
+
+    guac_client* client = (guac_client*) data;
+    guac_telnet_client* telnet_client = (guac_telnet_client*) client->data;
+    guac_telnet_settings* settings = telnet_client->settings;
+
+    switch (event->type) {
+
+        /* Terminal output received */
+        case TELNET_EV_DATA:
+            guac_terminal_write(telnet_client->term, event->data.buffer, event->data.size);
+            guac_telnet_search(client, event->data.buffer, event->data.size);
             break;
 
         /* Data destined for remote end */


[8/8] guacamole-server git commit: GUACAMOLE-622: Merge withold first terminal frame until connection is verified.

Posted by vn...@apache.org.
GUACAMOLE-622: Merge withold first terminal frame until connection is verified.


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

Branch: refs/heads/master
Commit: 54fda213666fce23db474b899512341358915fcf
Parents: d7cfff3 462d494
Author: Nick Couchman <vn...@apache.org>
Authored: Fri Sep 21 20:26:47 2018 -0400
Committer: Nick Couchman <vn...@apache.org>
Committed: Fri Sep 21 20:26:47 2018 -0400

----------------------------------------------------------------------
 src/protocols/ssh/ssh.c              |   4 +
 src/protocols/telnet/settings.c      |  87 +++++++++--
 src/protocols/telnet/settings.h      |  24 ++++
 src/protocols/telnet/telnet.c        | 230 +++++++++++++++++++++---------
 src/terminal/terminal-stdin-stream.c |  16 +++
 src/terminal/terminal.c              |  32 ++++-
 src/terminal/terminal/terminal.h     |  33 ++++-
 7 files changed, 339 insertions(+), 87 deletions(-)
----------------------------------------------------------------------



[3/8] guacamole-server git commit: GUACAMOLE-622: Implicitly invoke guac_terminal_start() if prompting is required.

Posted by vn...@apache.org.
GUACAMOLE-622: Implicitly invoke guac_terminal_start() if prompting is required.


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

Branch: refs/heads/master
Commit: 0b39b0fc5fa9846ff350cc443b8f432efb4678ee
Parents: 61a51df
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Aug 30 10:06:20 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Sun Sep 2 23:04:14 2018 -0700

----------------------------------------------------------------------
 src/terminal/terminal.c          | 3 +++
 src/terminal/terminal/terminal.h | 4 +++-
 2 files changed, 6 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/0b39b0fc/src/terminal/terminal.c
----------------------------------------------------------------------
diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c
index 0427ae4..95e945d 100644
--- a/src/terminal/terminal.c
+++ b/src/terminal/terminal.c
@@ -943,6 +943,9 @@ char* guac_terminal_prompt(guac_terminal* terminal, const char* title,
     int pos;
     char in_byte;
 
+    /* Prompting implicitly requires user input */
+    guac_terminal_start(terminal);
+
     /* Print title */
     guac_terminal_printf(terminal, "%s", title);
 

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/0b39b0fc/src/terminal/terminal/terminal.h
----------------------------------------------------------------------
diff --git a/src/terminal/terminal/terminal.h b/src/terminal/terminal/terminal.h
index 9a0145f..08094ba 100644
--- a/src/terminal/terminal/terminal.h
+++ b/src/terminal/terminal/terminal.h
@@ -652,7 +652,9 @@ void guac_terminal_notify(guac_terminal* terminal);
 /**
  * Reads a single line from this terminal's STDIN, storing the result in a
  * newly-allocated string. Input is retrieved in the same manner as
- * guac_terminal_read_stdin() and the same restrictions apply.
+ * guac_terminal_read_stdin() and the same restrictions apply. As reading input
+ * naturally requires user interaction, this function will implicitly invoke
+ * guac_terminal_start().
  *
  * @param terminal
  *     The terminal to which the provided title should be output, and from


[2/8] guacamole-server git commit: GUACAMOLE-622: Start terminal for SSH only after SSH connection succeeds.

Posted by vn...@apache.org.
GUACAMOLE-622: Start terminal for SSH only after SSH connection succeeds.


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

Branch: refs/heads/master
Commit: 4606607309761cca9f69dbd8e15be2376950dac3
Parents: 0b39b0f
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Aug 30 10:08:30 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Sun Sep 2 23:04:14 2018 -0700

----------------------------------------------------------------------
 src/protocols/ssh/ssh.c | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/46066073/src/protocols/ssh/ssh.c
----------------------------------------------------------------------
diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c
index 59554fb..0760f29 100644
--- a/src/protocols/ssh/ssh.c
+++ b/src/protocols/ssh/ssh.c
@@ -335,6 +335,7 @@ void* ssh_client_thread(void* data) {
 
     /* Logged in */
     guac_client_log(client, GUAC_LOG_INFO, "SSH connection successful.");
+    guac_terminal_start(ssh_client->term);
 
     /* Start input thread */
     if (pthread_create(&(input_thread), NULL, ssh_input_thread, (void*) client)) {


[4/8] guacamole-server git commit: GUACAMOLE-622: Ensure connection to guacd is kept alive even if the SSH daemon is taking its time responding. Lengthy connect times due to DNS verification, PAM, etc. are not uncommon.

Posted by vn...@apache.org.
GUACAMOLE-622: Ensure connection to guacd is kept alive even if the SSH daemon is taking its time responding. Lengthy connect times due to DNS verification, PAM, etc. are not uncommon.


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

Branch: refs/heads/master
Commit: 286cbf32a7a178cd6483a52983a33d486edc9129
Parents: 4606607
Author: Michael Jumper <mj...@apache.org>
Authored: Sat Sep 1 21:26:37 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Sun Sep 2 23:04:14 2018 -0700

----------------------------------------------------------------------
 src/protocols/ssh/ssh.c | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/286cbf32/src/protocols/ssh/ssh.c
----------------------------------------------------------------------
diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c
index 0760f29..cddb2fe 100644
--- a/src/protocols/ssh/ssh.c
+++ b/src/protocols/ssh/ssh.c
@@ -233,6 +233,9 @@ void* ssh_client_thread(void* data) {
         return NULL;
     }
 
+    /* Ensure connection is kept alive during lengthy connects */
+    guac_socket_require_keep_alive(client->socket);
+
     /* Open SSH session */
     ssh_client->session = guac_common_ssh_create_session(client,
             settings->hostname, settings->port, ssh_client->user, settings->server_alive_interval,


[5/8] guacamole-server git commit: GUACAMOLE-622: Require guac_terminal_start() to be invoked before the terminal will render frames or accept user input.

Posted by vn...@apache.org.
GUACAMOLE-622: Require guac_terminal_start() to be invoked before the terminal will render frames or accept user input.


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

Branch: refs/heads/master
Commit: 61a51df1b29695f6464193b18dd4f24809ef07df
Parents: 332e187
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Aug 30 10:01:41 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Sun Sep 2 23:04:14 2018 -0700

----------------------------------------------------------------------
 src/terminal/terminal.c          | 29 ++++++++++++++++++++++++++---
 src/terminal/terminal/terminal.h | 29 ++++++++++++++++++++++++++++-
 2 files changed, 54 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/61a51df1/src/terminal/terminal.c
----------------------------------------------------------------------
diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c
index 760e128..0427ae4 100644
--- a/src/terminal/terminal.c
+++ b/src/terminal/terminal.c
@@ -596,6 +596,7 @@ guac_terminal* guac_terminal_create(guac_client* client,
         available_width = 0;
 
     guac_terminal* term = malloc(sizeof(guac_terminal));
+    term->started = false;
     term->client = client;
     term->upload_path_handler = NULL;
     term->file_download_handler = NULL;
@@ -723,6 +724,11 @@ guac_terminal* guac_terminal_create(guac_client* client,
 
 }
 
+void guac_terminal_start(guac_terminal* term) {
+    term->started = true;
+    guac_terminal_notify(term);
+}
+
 void guac_terminal_stop(guac_terminal* term) {
 
     /* Close input pipe and set fds to invalid */
@@ -851,11 +857,13 @@ wait_complete:
 
 int guac_terminal_render_frame(guac_terminal* terminal) {
 
+    guac_client* client = terminal->client;
+
     int wait_result;
 
     /* Wait for data to be available */
     wait_result = guac_terminal_wait(terminal, 1000);
-    if (wait_result) {
+    if (wait_result || !terminal->started) {
 
         guac_timestamp frame_start = guac_timestamp_current();
 
@@ -867,13 +875,14 @@ int guac_terminal_render_frame(guac_terminal* terminal) {
                                 - frame_end;
 
             /* Wait again if frame remaining */
-            if (frame_remaining > 0)
+            if (frame_remaining > 0 || !terminal->started)
                 wait_result = guac_terminal_wait(terminal,
                         GUAC_TERMINAL_FRAME_TIMEOUT);
             else
                 break;
 
-        } while (wait_result > 0);
+        } while (client->state == GUAC_CLIENT_RUNNING
+                && (wait_result > 0 || !terminal->started));
 
         /* Flush terminal */
         guac_terminal_lock(terminal);
@@ -1672,6 +1681,13 @@ int guac_terminal_send_string(guac_terminal* term, const char* data) {
 
 static int __guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
 
+    /* Ignore user input if terminal is not started */
+    if (!term->started) {
+        guac_client_log(term->client, GUAC_LOG_DEBUG, "Ignoring user input "
+                "while terminal has not yet started.");
+        return 0;
+    }
+
     /* Hide mouse cursor if not already hidden */
     if (term->current_cursor != GUAC_TERMINAL_CURSOR_BLANK) {
         term->current_cursor = GUAC_TERMINAL_CURSOR_BLANK;
@@ -1845,6 +1861,13 @@ int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
 static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
         int x, int y, int mask) {
 
+    /* Ignore user input if terminal is not started */
+    if (!term->started) {
+        guac_client_log(term->client, GUAC_LOG_DEBUG, "Ignoring user input "
+                "while terminal has not yet started.");
+        return 0;
+    }
+
     /* Determine which buttons were just released and pressed */
     int released_mask =  term->mouse_mask & ~mask;
     int pressed_mask  = ~term->mouse_mask &  mask;

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/61a51df1/src/terminal/terminal/terminal.h
----------------------------------------------------------------------
diff --git a/src/terminal/terminal/terminal.h b/src/terminal/terminal/terminal.h
index bddf8e5..9a0145f 100644
--- a/src/terminal/terminal/terminal.h
+++ b/src/terminal/terminal/terminal.h
@@ -172,6 +172,16 @@ struct guac_terminal {
     guac_client* client;
 
     /**
+     * Whether user input should be handled and this terminal should render
+     * frames. Initially, this will be false, user input will be ignored, and
+     * rendering of frames will be withheld until guac_terminal_start() has
+     * been invoked. The data within frames will still be rendered, and text
+     * data received will still be handled, however actual frame boundaries
+     * will not be sent.
+     */
+    bool started;
+
+    /**
      * The terminal render thread.
      */
     pthread_t thread;
@@ -526,7 +536,13 @@ struct guac_terminal {
 
 /**
  * Creates a new guac_terminal, having the given width and height, and
- * rendering to the given client.
+ * rendering to the given client. As failover mechanisms and the Guacamole
+ * client implementation typically use the receipt of a "sync" message to
+ * denote successful connection, rendering of frames (sending of "sync") will
+ * be withheld until guac_terminal_start() is called, and user input will be
+ * ignored. The guac_terminal_start() function should be invoked only after
+ * either the underlying connection has truly succeeded, or until visible
+ * terminal output or user input is required.
  *
  * @param client
  *     The client to which the terminal will be rendered.
@@ -605,6 +621,17 @@ int guac_terminal_render_frame(guac_terminal* terminal);
 int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size);
 
 /**
+ * Notifies the terminal that rendering should begin and that user input should
+ * now be accepted. This function must be invoked following terminal creation
+ * for the end of frames to be signalled with "sync" messages. Until this
+ * function is invoked, "sync" messages will be withheld.
+ *
+ * @param term
+ *     The terminal to start.
+ */
+void guac_terminal_start(guac_terminal* term);
+
+/**
  * Manually stop the terminal to forcibly unblock any pending reads/writes,
  * e.g. forcing guac_terminal_read_stdin() to return and cease all terminal I/O.
  *