guacamole-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jmuehl...@apache.org
Subject [9/9] incubator-guacamole-server git commit: Merge 0.9.12-incubating changes back to master.
Date Tue, 28 Feb 2017 18:40:45 GMT
Merge 0.9.12-incubating changes back to master.


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/c4903a8e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/c4903a8e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/c4903a8e

Branch: refs/heads/master
Commit: c4903a8e36eabbaec20ee83a422f599b9db77358
Parents: eb5ef5a 3ff8323
Author: James Muehlner <james.muehlner@guac-dev.org>
Authored: Tue Feb 28 10:32:49 2017 -0800
Committer: James Muehlner <james.muehlner@guac-dev.org>
Committed: Tue Feb 28 10:32:49 2017 -0800

----------------------------------------------------------------------
 configure.ac                              |   2 +-
 doc/Doxyfile                              |   2 +-
 src/common-ssh/Makefile.am                |  24 +-
 src/common-ssh/buffer.c                   | 135 ++++
 src/common-ssh/common-ssh/buffer.h        | 134 ++++
 src/common-ssh/common-ssh/key.h           | 170 +++++
 src/common-ssh/common-ssh/sftp.h          | 242 +++++++
 src/common-ssh/common-ssh/ssh.h           | 114 ++++
 src/common-ssh/common-ssh/user.h          | 108 ++++
 src/common-ssh/guac_sftp.c                | 794 -----------------------
 src/common-ssh/guac_sftp.h                | 242 -------
 src/common-ssh/guac_ssh.c                 | 544 ----------------
 src/common-ssh/guac_ssh.h                 | 114 ----
 src/common-ssh/guac_ssh_buffer.c          | 135 ----
 src/common-ssh/guac_ssh_buffer.h          | 134 ----
 src/common-ssh/guac_ssh_key.c             | 217 -------
 src/common-ssh/guac_ssh_key.h             | 170 -----
 src/common-ssh/guac_ssh_user.c            |  82 ---
 src/common-ssh/guac_ssh_user.h            | 108 ----
 src/common-ssh/key.c                      | 217 +++++++
 src/common-ssh/sftp.c                     | 794 +++++++++++++++++++++++
 src/common-ssh/ssh.c                      | 544 ++++++++++++++++
 src/common-ssh/user.c                     |  82 +++
 src/libguac/Makefile.am                   |   2 +-
 src/protocols/rdp/client.c                |   6 +-
 src/protocols/rdp/rdp.c                   |   6 +-
 src/protocols/rdp/rdp.h                   |   6 +-
 src/protocols/rdp/sftp.c                  |   2 +-
 src/protocols/ssh/client.c                |   4 +-
 src/protocols/ssh/clipboard.c             |   2 +-
 src/protocols/ssh/input.c                 |   2 +-
 src/protocols/ssh/sftp.c                  |   2 +-
 src/protocols/ssh/ssh.c                   |   6 +-
 src/protocols/ssh/ssh.h                   |   8 +-
 src/protocols/telnet/client.c             |   2 +-
 src/protocols/telnet/client.h             |   2 +-
 src/protocols/telnet/clipboard.c          |   2 +-
 src/protocols/telnet/input.c              |   2 +-
 src/protocols/telnet/telnet.c             |   2 +-
 src/protocols/telnet/telnet.h             |   2 +-
 src/protocols/telnet/user.c               |   2 +-
 src/protocols/vnc/client.c                |   6 +-
 src/protocols/vnc/sftp.c                  |   2 +-
 src/protocols/vnc/vnc.c                   |   4 +-
 src/protocols/vnc/vnc.h                   |   6 +-
 src/terminal/Makefile.am                  |  20 +-
 src/terminal/buffer.c                     |   4 +-
 src/terminal/buffer.h                     | 129 ----
 src/terminal/char_mappings.h              |  48 --
 src/terminal/common.c                     |   2 +-
 src/terminal/common.h                     |  53 --
 src/terminal/display.c                    |   6 +-
 src/terminal/display.h                    | 363 -----------
 src/terminal/scrollbar.c                  |   2 +-
 src/terminal/scrollbar.h                  | 357 -----------
 src/terminal/terminal.c                   |  14 +-
 src/terminal/terminal.h                   | 839 -------------------------
 src/terminal/terminal/buffer.h            | 129 ++++
 src/terminal/terminal/char_mappings.h     |  48 ++
 src/terminal/terminal/common.h            |  53 ++
 src/terminal/terminal/display.h           | 363 +++++++++++
 src/terminal/terminal/scrollbar.h         | 357 +++++++++++
 src/terminal/terminal/terminal.h          | 839 +++++++++++++++++++++++++
 src/terminal/terminal/terminal_handlers.h | 165 +++++
 src/terminal/terminal/types.h             | 124 ++++
 src/terminal/terminal/typescript.h        | 184 ++++++
 src/terminal/terminal_handlers.c          |   8 +-
 src/terminal/terminal_handlers.h          | 165 -----
 src/terminal/types.h                      | 124 ----
 src/terminal/typescript.c                 |   2 +-
 src/terminal/typescript.h                 | 184 ------
 71 files changed, 4884 insertions(+), 4884 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/c4903a8e/src/common-ssh/sftp.c
----------------------------------------------------------------------
diff --cc src/common-ssh/sftp.c
index 0000000,79db35e..4be1568
mode 000000,100644..100644
--- a/src/common-ssh/sftp.c
+++ b/src/common-ssh/sftp.c
@@@ -1,0 -1,783 +1,794 @@@
+ /*
+  * 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 "common-ssh/sftp.h"
+ #include "common-ssh/ssh.h"
+ 
+ #include <guacamole/client.h>
+ #include <guacamole/object.h>
+ #include <guacamole/protocol.h>
+ #include <guacamole/socket.h>
+ #include <guacamole/user.h>
+ #include <libssh2.h>
+ 
+ #include <fcntl.h>
+ #include <libgen.h>
+ #include <stdlib.h>
+ #include <string.h>
+ 
+ /**
+  * Translates the last error message received by the SFTP layer of an SSH
+  * session into a Guacamole protocol status code.
+  *
+  * @param filesystem
+  *     The object (not guac_object) defining the filesystem associated with the
+  *     SFTP and SSH sessions.
+  *
+  * @return
+  *     The Guacamole protocol status code corresponding to the last reported
+  *     error of the SFTP layer, if nay, or GUAC_PROTOCOL_STATUS_SUCCESS if no
+  *     error has occurred.
+  */
+ static guac_protocol_status guac_sftp_get_status(
+         guac_common_ssh_sftp_filesystem* filesystem) {
+ 
+     /* Get libssh2 objects */
+     LIBSSH2_SFTP*    sftp    = filesystem->sftp_session;
+     LIBSSH2_SESSION* session = filesystem->ssh_session->session;
+ 
+     /* Return success code if no error occurred */
+     if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_SFTP_PROTOCOL)
+         return GUAC_PROTOCOL_STATUS_SUCCESS;
+ 
+     /* Translate SFTP error codes defined by
+      * https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02 (the most
+      * commonly-implemented standard) */
+     switch (libssh2_sftp_last_error(sftp)) {
+ 
+         /* SSH_FX_OK (not an error) */
+         case 0:
+             return GUAC_PROTOCOL_STATUS_SUCCESS;
+ 
+         /* SSH_FX_EOF (technically not an error) */
+         case 1:
+             return GUAC_PROTOCOL_STATUS_SUCCESS;
+ 
+         /* SSH_FX_NO_SUCH_FILE */
+         case 2:
+             return GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND;
+ 
+         /* SSH_FX_PERMISSION_DENIED */
+         case 3:
+             return GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN;
+ 
+         /* SSH_FX_FAILURE */
+         case 4:
+             return GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR;
+ 
+         /* SSH_FX_BAD_MESSAGE */
+         case 5:
+             return GUAC_PROTOCOL_STATUS_SERVER_ERROR;
+ 
+         /* SSH_FX_NO_CONNECTION / SSH_FX_CONNECTION_LOST */
+         case 6:
+         case 7:
+             return GUAC_PROTOCOL_STATUS_UPSTREAM_TIMEOUT;
+ 
+         /* SSH_FX_OP_UNSUPPORTED */
+         case 8:
+             return GUAC_PROTOCOL_STATUS_UNSUPPORTED;
+ 
+         /* Return generic error if cause unknown */
+         default:
+             return GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR;
+ 
+     }
+ 
+ }
+ 
+ /**
+  * Concatenates the given filename with the given path, separating the two
+  * with a single forward slash. The full result must be no more than
+  * GUAC_COMMON_SSH_SFTP_MAX_PATH bytes long, counting null terminator.
+  *
+  * @param fullpath
+  *     The buffer to store the result within. This buffer must be at least
+  *     GUAC_COMMON_SSH_SFTP_MAX_PATH bytes long.
+  *
+  * @param path
+  *     The path to append the filename to.
+  *
+  * @param filename
+  *     The filename to append to the path.
+  *
+  * @return
+  *     Non-zero if the filename is valid and was successfully appended to the
+  *     path, zero otherwise.
+  */
+ static int guac_ssh_append_filename(char* fullpath, const char* path,
+         const char* filename) {
+ 
+     int i;
+ 
+     /* Disallow "." as a filename */
+     if (strcmp(filename, ".") == 0)
+         return 0;
+ 
+     /* Disallow ".." as a filename */
+     if (strcmp(filename, "..") == 0)
+         return 0;
+ 
+     /* Copy path, append trailing slash */
+     for (i=0; i<GUAC_COMMON_SSH_SFTP_MAX_PATH; i++) {
+ 
+         /*
+          * Append trailing slash only if:
+          *  1) Trailing slash is not already present
+          *  2) Path is non-empty
+          */
+ 
+         char c = path[i];
+         if (c == '\0') {
+             if (i > 0 && path[i-1] != '/')
+                 fullpath[i++] = '/';
+             break;
+         }
+ 
+         /* Copy character if not end of string */
+         fullpath[i] = c;
+ 
+     }
+ 
+     /* Append filename */
+     for (; i<GUAC_COMMON_SSH_SFTP_MAX_PATH; i++) {
+ 
+         char c = *(filename++);
+         if (c == '\0')
+             break;
+ 
+         /* Filenames may not contain slashes */
+         if (c == '\\' || c == '/')
+             return 0;
+ 
+         /* Append each character within filename */
+         fullpath[i] = c;
+ 
+     }
+ 
+     /* Verify path length is within maximum */
+     if (i == GUAC_COMMON_SSH_SFTP_MAX_PATH)
+         return 0;
+ 
+     /* Terminate path string */
+     fullpath[i] = '\0';
+ 
+     /* Append was successful */
+     return 1;
+ 
+ }
+ 
+ /**
+  * Handler for blob messages which continue an inbound SFTP data transfer
+  * (upload). The data associated with the given stream is expected to be a
+  * pointer to an open LIBSSH2_SFTP_HANDLE for the file to which the data
+  * should be written.
+  *
+  * @param user
+  *     The user receiving the blob message.
+  *
+  * @param stream
+  *     The Guacamole protocol stream associated with the received blob message.
+  *
+  * @param data
+  *     The data received within the blob.
+  *
+  * @param length
+  *     The length of the received data, in bytes.
+  *
+  * @return
+  *     Zero if the blob is handled successfully, or non-zero on error.
+  */
+ static int guac_common_ssh_sftp_blob_handler(guac_user* user,
+         guac_stream* stream, void* data, int length) {
+ 
+     /* Pull file from stream */
+     LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data;
+ 
+     /* Attempt write */
+     if (libssh2_sftp_write(file, data, length) == length) {
+         guac_user_log(user, GUAC_LOG_DEBUG, "%i bytes written", length);
+         guac_protocol_send_ack(user->socket, stream, "SFTP: OK",
+                 GUAC_PROTOCOL_STATUS_SUCCESS);
+         guac_socket_flush(user->socket);
+     }
+ 
+     /* Inform of any errors */
+     else {
+         guac_user_log(user, GUAC_LOG_INFO, "Unable to write to file");
+         guac_protocol_send_ack(user->socket, stream, "SFTP: Write failed",
+                 GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+         guac_socket_flush(user->socket);
+     }
+ 
+     return 0;
+ 
+ }
+ 
+ /**
+  * Handler for end messages which terminate an inbound SFTP data transfer
+  * (upload). The data associated with the given stream is expected to be a
+  * pointer to an open LIBSSH2_SFTP_HANDLE for the file to which the data
+  * has been written and which should now be closed.
+  *
+  * @param user
+  *     The user receiving the end message.
+  *
+  * @param stream
+  *     The Guacamole protocol stream associated with the received end message.
+  *
+  * @return
+  *     Zero if the file is closed successfully, or non-zero on error.
+  */
+ static int guac_common_ssh_sftp_end_handler(guac_user* user,
+         guac_stream* stream) {
+ 
+     /* Pull file from stream */
+     LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data;
+ 
+     /* Attempt to close file */
+     if (libssh2_sftp_close(file) == 0) {
+         guac_user_log(user, GUAC_LOG_DEBUG, "File closed");
+         guac_protocol_send_ack(user->socket, stream, "SFTP: OK",
+                 GUAC_PROTOCOL_STATUS_SUCCESS);
+         guac_socket_flush(user->socket);
+     }
+     else {
+         guac_user_log(user, GUAC_LOG_INFO, "Unable to close file");
+         guac_protocol_send_ack(user->socket, stream, "SFTP: Close failed",
+                 GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+         guac_socket_flush(user->socket);
+     }
+ 
+     return 0;
+ 
+ }
+ 
+ int guac_common_ssh_sftp_handle_file_stream(
+         guac_common_ssh_sftp_filesystem* filesystem, guac_user* user,
+         guac_stream* stream, char* mimetype, char* filename) {
+ 
+     char fullpath[GUAC_COMMON_SSH_SFTP_MAX_PATH];
+     LIBSSH2_SFTP_HANDLE* file;
+ 
+     /* Concatenate filename with path */
+     if (!guac_ssh_append_filename(fullpath, filesystem->upload_path,
+                 filename)) {
+ 
+         guac_user_log(user, GUAC_LOG_DEBUG,
+                 "Filename \"%s\" is invalid or resulting path is too long",
+                 filename);
+ 
+         /* Abort transfer - invalid filename */
+         guac_protocol_send_ack(user->socket, stream, 
+                 "SFTP: Illegal filename",
+                 GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
+ 
+         guac_socket_flush(user->socket);
+         return 0;
+     }
+ 
+     /* Open file via SFTP */
+     file = libssh2_sftp_open(filesystem->sftp_session, fullpath,
+             LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC,
+             S_IRUSR | S_IWUSR);
+ 
+     /* Inform of status */
+     if (file != NULL) {
+ 
+         guac_user_log(user, GUAC_LOG_DEBUG,
+                 "File \"%s\" opened",
+                 fullpath);
+ 
+         guac_protocol_send_ack(user->socket, stream, "SFTP: File opened",
+                 GUAC_PROTOCOL_STATUS_SUCCESS);
+         guac_socket_flush(user->socket);
+     }
+     else {
+         guac_user_log(user, GUAC_LOG_INFO,
+                 "Unable to open file \"%s\"", fullpath);
+         guac_protocol_send_ack(user->socket, stream, "SFTP: Open failed",
+                 guac_sftp_get_status(filesystem));
+         guac_socket_flush(user->socket);
+     }
+ 
+     /* Set handlers for file stream */
+     stream->blob_handler = guac_common_ssh_sftp_blob_handler;
+     stream->end_handler = guac_common_ssh_sftp_end_handler;
+ 
+     /* Store file within stream */
+     stream->data = file;
+     return 0;
+ 
+ }
+ 
+ /**
+  * Handler for ack messages which continue an outbound SFTP data transfer
+  * (download), signalling the current status and requesting additional data.
+  * The data associated with the given stream is expected to be a pointer to an
+  * open LIBSSH2_SFTP_HANDLE for the file from which the data is to be read.
+  *
+  * @param user
+  *     The user receiving the ack message.
+  *
+  * @param stream
+  *     The Guacamole protocol stream associated with the received ack message.
+  *
+  * @param message
+  *     An arbitrary human-readable message describing the nature of the
+  *     success or failure denoted by the ack message.
+  *
+  * @param status
+  *     The status code associated with the ack message, which may indicate
+  *     success or an error.
+  *
+  * @return
+  *     Zero if the file is read from successfully, or non-zero on error.
+  */
+ static int guac_common_ssh_sftp_ack_handler(guac_user* user,
+         guac_stream* stream, char* message, guac_protocol_status status) {
+ 
+     /* Pull file from stream */
+     LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data;
+ 
+     /* If successful, read data */
+     if (status == GUAC_PROTOCOL_STATUS_SUCCESS) {
+ 
+         /* Attempt read into buffer */
+         char buffer[4096];
+         int bytes_read = libssh2_sftp_read(file, buffer, sizeof(buffer)); 
+ 
+         /* If bytes read, send as blob */
+         if (bytes_read > 0) {
+             guac_protocol_send_blob(user->socket, stream,
+                     buffer, bytes_read);
+ 
+             guac_user_log(user, GUAC_LOG_DEBUG, "%i bytes sent to user",
+                     bytes_read);
+ 
+         }
+ 
 -        /* If EOF, send end */
 -        else if (bytes_read == 0) {
 -            guac_user_log(user, GUAC_LOG_DEBUG, "File sent");
 -            guac_protocol_send_end(user->socket, stream);
 -            guac_user_free_stream(user, stream);
 -        }
 -
 -        /* Otherwise, fail stream */
++        /* If bytes could not be read, handle EOF or error condition */
+         else {
 -            guac_user_log(user, GUAC_LOG_INFO, "Error reading file");
 -            guac_protocol_send_end(user->socket, stream);
 -            guac_user_free_stream(user, stream);
++
++            /* If EOF, send end */
++            if (bytes_read == 0) {
++                guac_user_log(user, GUAC_LOG_DEBUG, "File sent");
++                guac_protocol_send_end(user->socket, stream);
++                guac_user_free_stream(user, stream);
++            }
++
++            /* Otherwise, fail stream */
++            else {
++                guac_user_log(user, GUAC_LOG_INFO, "Error reading file");
++                guac_protocol_send_end(user->socket, stream);
++                guac_user_free_stream(user, stream);
++            }
++
++            /* Close file */
++            if (libssh2_sftp_close(file) == 0)
++                guac_user_log(user, GUAC_LOG_DEBUG, "File closed");
++            else
++                guac_user_log(user, GUAC_LOG_INFO, "Unable to close file");
++
+         }
+ 
+         guac_socket_flush(user->socket);
+ 
+     }
+ 
+     /* Otherwise, return stream to user */
+     else
+         guac_user_free_stream(user, stream);
+ 
+     return 0;
+ }
+ 
+ guac_stream* guac_common_ssh_sftp_download_file(
+         guac_common_ssh_sftp_filesystem* filesystem, guac_user* user,
+         char* filename) {
+ 
+     guac_stream* stream;
+     LIBSSH2_SFTP_HANDLE* file;
+ 
+     /* Attempt to open file for reading */
+     file = libssh2_sftp_open(filesystem->sftp_session, filename,
+             LIBSSH2_FXF_READ, 0);
+     if (file == NULL) {
+         guac_user_log(user, GUAC_LOG_INFO, 
+                 "Unable to read file \"%s\"", filename);
+         return NULL;
+     }
+ 
+     /* Allocate stream */
+     stream = guac_user_alloc_stream(user);
+     stream->ack_handler = guac_common_ssh_sftp_ack_handler;
+     stream->data = file;
+ 
+     /* Send stream start, strip name */
+     filename = basename(filename);
+     guac_protocol_send_file(user->socket, stream,
+             "application/octet-stream", filename);
+     guac_socket_flush(user->socket);
+ 
+     guac_user_log(user, GUAC_LOG_DEBUG, "Sending file \"%s\"", filename);
+     return stream;
+ 
+ }
+ 
+ void guac_common_ssh_sftp_set_upload_path(
+         guac_common_ssh_sftp_filesystem* filesystem, const char* path) {
+ 
+     guac_client* client = filesystem->ssh_session->client;
+ 
+     /* Ignore requests which exceed maximum-allowed path */
+     int length = strnlen(path, GUAC_COMMON_SSH_SFTP_MAX_PATH)+1;
+     if (length > GUAC_COMMON_SSH_SFTP_MAX_PATH) {
+         guac_client_log(client, GUAC_LOG_ERROR,
+                 "Submitted path exceeds limit of %i bytes",
+                 GUAC_COMMON_SSH_SFTP_MAX_PATH);
+         return;
+     }
+ 
+     /* Copy path */
+     memcpy(filesystem->upload_path, path, length);
+     guac_client_log(client, GUAC_LOG_DEBUG, "Upload path set to \"%s\"", path);
+ 
+ }
+ 
+ /**
+  * Handler for ack messages received due to receipt of a "body" or "blob"
+  * instruction associated with a SFTP directory list operation.
+  *
+  * @param user
+  *     The user receiving the ack message.
+  *
+  * @param stream
+  *     The Guacamole protocol stream associated with the received ack message.
+  *
+  * @param message
+  *     An arbitrary human-readable message describing the nature of the
+  *     success or failure denoted by this ack message.
+  *
+  * @param status
+  *     The status code associated with this ack message, which may indicate
+  *     success or an error.
+  *
+  * @return
+  *     Zero on success, non-zero on error.
+  */
+ static int guac_common_ssh_sftp_ls_ack_handler(guac_user* user,
+         guac_stream* stream, char* message, guac_protocol_status status) {
+ 
+     int bytes_read;
+     int blob_written = 0;
+ 
+     char filename[GUAC_COMMON_SSH_SFTP_MAX_PATH];
+     LIBSSH2_SFTP_ATTRIBUTES attributes;
+ 
+     guac_common_ssh_sftp_ls_state* list_state =
+         (guac_common_ssh_sftp_ls_state*) stream->data;
+ 
+     guac_common_ssh_sftp_filesystem* filesystem = list_state->filesystem;
+ 
+     LIBSSH2_SFTP* sftp = filesystem->sftp_session;
+ 
+     /* If unsuccessful, free stream and abort */
+     if (status != GUAC_PROTOCOL_STATUS_SUCCESS) {
+         libssh2_sftp_closedir(list_state->directory);
+         guac_user_free_stream(user, stream);
+         free(list_state);
+         return 0;
+     }
+ 
+     /* While directory entries remain */
+     while ((bytes_read = libssh2_sftp_readdir(list_state->directory,
+                 filename, sizeof(filename), &attributes)) > 0
+             && !blob_written) {
+ 
+         char absolute_path[GUAC_COMMON_SSH_SFTP_MAX_PATH];
+ 
+         /* Skip current and parent directory entries */
+         if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
+             continue;
+ 
+         /* Concatenate into absolute path - skip if invalid */
+         if (!guac_ssh_append_filename(absolute_path, 
+                     list_state->directory_name, filename)) {
+ 
+             guac_user_log(user, GUAC_LOG_DEBUG,
+                     "Skipping filename \"%s\" - filename is invalid or "
+                     "resulting path is too long", filename);
+ 
+             continue;
+         }
+ 
+         /* Stat explicitly if symbolic link (might point to directory) */
+         if (LIBSSH2_SFTP_S_ISLNK(attributes.permissions))
+             libssh2_sftp_stat(sftp, absolute_path, &attributes);
+ 
+         /* Determine mimetype */
+         const char* mimetype;
+         if (LIBSSH2_SFTP_S_ISDIR(attributes.permissions))
+             mimetype = GUAC_USER_STREAM_INDEX_MIMETYPE;
+         else
+             mimetype = "application/octet-stream";
+ 
+         /* Write entry */
+         blob_written |= guac_common_json_write_property(user, stream,
+                 &list_state->json_state, absolute_path, mimetype);
+ 
+     }
+ 
+     /* Complete JSON and cleanup at end of directory */
+     if (bytes_read <= 0) {
+ 
+         /* Complete JSON object */
+         guac_common_json_end_object(user, stream, &list_state->json_state);
+         guac_common_json_flush(user, stream, &list_state->json_state);
+ 
+         /* Clean up resources */
+         libssh2_sftp_closedir(list_state->directory);
+         free(list_state);
+ 
+         /* Signal of stream */
+         guac_protocol_send_end(user->socket, stream);
+         guac_user_free_stream(user, stream);
+ 
+     }
+ 
+     guac_socket_flush(user->socket);
+     return 0;
+ 
+ }
+ 
+ /**
+  * Handler for get messages. In context of SFTP and the filesystem exposed via
+  * the Guacamole protocol, get messages request the body of a file within the
+  * filesystem.
+  *
+  * @param user
+  *     The user who sent the get message.
+  *
+  * @param object
+  *     The Guacamole protocol object associated with the get request itself.
+  *
+  * @param name
+  *     The name of the input stream (file) being requested.
+  *
+  * @return
+  *     Zero on success, non-zero on error.
+  */
+ static int guac_common_ssh_sftp_get_handler(guac_user* user,
+         guac_object* object, char* name) {
+ 
+     guac_common_ssh_sftp_filesystem* filesystem =
+         (guac_common_ssh_sftp_filesystem*) object->data;
+ 
+     LIBSSH2_SFTP* sftp = filesystem->sftp_session;
+     LIBSSH2_SFTP_ATTRIBUTES attributes;
+ 
+     /* Attempt to read file information */
+     if (libssh2_sftp_stat(sftp, name, &attributes)) {
+         guac_user_log(user, GUAC_LOG_INFO, "Unable to read file \"%s\"",
+                 name);
+         return 0;
+     }
+ 
+     /* If directory, send contents of directory */
+     if (LIBSSH2_SFTP_S_ISDIR(attributes.permissions)) {
+ 
+         /* Open as directory */
+         LIBSSH2_SFTP_HANDLE* dir = libssh2_sftp_opendir(sftp, name);
+         if (dir == NULL) {
+             guac_user_log(user, GUAC_LOG_INFO,
+                     "Unable to read directory \"%s\"", name);
+             return 0;
+         }
+ 
+         /* Init directory listing state */
+         guac_common_ssh_sftp_ls_state* list_state =
+             malloc(sizeof(guac_common_ssh_sftp_ls_state));
+ 
+         list_state->directory = dir;
+         list_state->filesystem = filesystem;
+         strncpy(list_state->directory_name, name,
+                 sizeof(list_state->directory_name) - 1);
+ 
+         /* Allocate stream for body */
+         guac_stream* stream = guac_user_alloc_stream(user);
+         stream->ack_handler = guac_common_ssh_sftp_ls_ack_handler;
+         stream->data = list_state;
+ 
+         /* Init JSON object state */
+         guac_common_json_begin_object(user, stream, &list_state->json_state);
+ 
+         /* Associate new stream with get request */
+         guac_protocol_send_body(user->socket, object, stream,
+                 GUAC_USER_STREAM_INDEX_MIMETYPE, name);
+ 
+     }
+ 
+     /* Otherwise, send file contents */
+     else {
+ 
+         /* Open as normal file */
+         LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, name,
+             LIBSSH2_FXF_READ, 0);
+         if (file == NULL) {
+             guac_user_log(user, GUAC_LOG_INFO,
+                     "Unable to read file \"%s\"", name);
+             return 0;
+         }
+ 
+         /* Allocate stream for body */
+         guac_stream* stream = guac_user_alloc_stream(user);
+         stream->ack_handler = guac_common_ssh_sftp_ack_handler;
+         stream->data = file;
+ 
+         /* Associate new stream with get request */
+         guac_protocol_send_body(user->socket, object, stream,
+                 "application/octet-stream", name);
+ 
+     }
+ 
+     guac_socket_flush(user->socket);
+     return 0;
+ }
+ 
+ /**
+  * Handler for put messages. In context of SFTP and the filesystem exposed via
+  * the Guacamole protocol, put messages request write access to a file within
+  * the filesystem.
+  *
+  * @param user
+  *     The user who sent the put message.
+  *
+  * @param object
+  *     The Guacamole protocol object associated with the put request itself.
+  *
+  * @param stream
+  *     The Guacamole protocol stream along which the user will be sending
+  *     file data.
+  *
+  * @param mimetype
+  *     The mimetype of the data being send along the stream.
+  *
+  * @param name
+  *     The name of the input stream (file) being requested.
+  *
+  * @return
+  *     Zero on success, non-zero on error.
+  */
+ static int guac_common_ssh_sftp_put_handler(guac_user* user,
+         guac_object* object, guac_stream* stream, char* mimetype, char* name) {
+ 
+     guac_common_ssh_sftp_filesystem* filesystem =
+         (guac_common_ssh_sftp_filesystem*) object->data;
+ 
+     LIBSSH2_SFTP* sftp = filesystem->sftp_session;
+ 
+     /* Open file via SFTP */
+     LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, name,
+             LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC,
+             S_IRUSR | S_IWUSR);
+ 
+     /* Acknowledge stream if successful */
+     if (file != NULL) {
+         guac_user_log(user, GUAC_LOG_DEBUG, "File \"%s\" opened", name);
+         guac_protocol_send_ack(user->socket, stream, "SFTP: File opened",
+                 GUAC_PROTOCOL_STATUS_SUCCESS);
+     }
+ 
+     /* Abort on failure */
+     else {
+         guac_user_log(user, GUAC_LOG_INFO,
+                 "Unable to open file \"%s\"", name);
+         guac_protocol_send_ack(user->socket, stream, "SFTP: Open failed",
+                 guac_sftp_get_status(filesystem));
+     }
+ 
+     /* Set handlers for file stream */
+     stream->blob_handler = guac_common_ssh_sftp_blob_handler;
+     stream->end_handler = guac_common_ssh_sftp_end_handler;
+ 
+     /* Store file within stream */
+     stream->data = file;
+ 
+     guac_socket_flush(user->socket);
+     return 0;
+ }
+ 
+ void* guac_common_ssh_expose_sftp_filesystem(guac_user* user, void* data) {
+ 
+     guac_common_ssh_sftp_filesystem* filesystem =
+         (guac_common_ssh_sftp_filesystem*) data;
+ 
+     /* No need to expose if there is no filesystem or the user has left */
+     if (user == NULL || filesystem == NULL)
+         return NULL;
+ 
+     /* Allocate and expose filesystem object for user */
+     return guac_common_ssh_alloc_sftp_filesystem_object(filesystem, user);
+ 
+ }
+ 
+ guac_object* guac_common_ssh_alloc_sftp_filesystem_object(
+         guac_common_ssh_sftp_filesystem* filesystem, guac_user* user) {
+ 
+     /* Init filesystem */
+     guac_object* fs_object = guac_user_alloc_object(user);
+     fs_object->get_handler = guac_common_ssh_sftp_get_handler;
+     fs_object->put_handler = guac_common_ssh_sftp_put_handler;
+     fs_object->data = filesystem;
+ 
+     /* Send filesystem to user */
+     guac_protocol_send_filesystem(user->socket, fs_object, filesystem->name);
+     guac_socket_flush(user->socket);
+ 
+     return fs_object;
+ 
+ }
+ 
+ guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem(
+         guac_common_ssh_session* session, const char* name) {
+ 
+     /* Request SFTP */
+     LIBSSH2_SFTP* sftp_session = libssh2_sftp_init(session->session);
+     if (sftp_session == NULL)
+         return NULL;
+ 
+     /* Allocate data for SFTP session */
+     guac_common_ssh_sftp_filesystem* filesystem =
+         malloc(sizeof(guac_common_ssh_sftp_filesystem));
+ 
+     /* Associate SSH session with SFTP data and user */
+     filesystem->name = strdup(name);
+     filesystem->ssh_session = session;
+     filesystem->sftp_session = sftp_session;
+ 
+     /* Initially upload files to current directory */
+     strcpy(filesystem->upload_path, ".");
+ 
+     /* Return allocated filesystem */
+     return filesystem;
+ 
+ }
+ 
+ void guac_common_ssh_destroy_sftp_filesystem(
+         guac_common_ssh_sftp_filesystem* filesystem) {
+ 
+     /* Shutdown SFTP session */
+     libssh2_sftp_shutdown(filesystem->sftp_session);
+ 
+     /* Free associated memory */
+     free(filesystem->name);
+     free(filesystem);
+ 
+ }
+ 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/c4903a8e/src/common-ssh/ssh.c
----------------------------------------------------------------------
diff --cc src/common-ssh/ssh.c
index 0000000,9d3de19..e4b0d38
mode 000000,100644..100644
--- a/src/common-ssh/ssh.c
+++ b/src/common-ssh/ssh.c
@@@ -1,0 -1,544 +1,544 @@@
+ /*
+  * 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 "common-ssh/key.h"
+ #include "common-ssh/ssh.h"
+ #include "common-ssh/user.h"
+ 
+ #include <guacamole/client.h>
+ #include <libssh2.h>
+ 
+ #ifdef LIBSSH2_USES_GCRYPT
+ #include <gcrypt.h>
+ #endif
+ 
+ #include <openssl/err.h>
+ #include <openssl/ssl.h>
+ 
+ #include <errno.h>
+ #include <netdb.h>
+ #include <netinet/in.h>
+ #include <pthread.h>
+ #include <stddef.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/socket.h>
+ #include <unistd.h>
+ 
+ #ifdef LIBSSH2_USES_GCRYPT
+ GCRY_THREAD_OPTION_PTHREAD_IMPL;
+ #endif
+ 
+ /**
+  * Array of mutexes, used by OpenSSL.
+  */
+ static pthread_mutex_t* guac_common_ssh_openssl_locks = NULL;
+ 
+ /**
+  * Called by OpenSSL when locking or unlocking the Nth mutex.
+  *
+  * @param mode
+  *     A bitmask denoting the action to be taken on the Nth lock, such as
+  *     CRYPTO_LOCK or CRYPTO_UNLOCK.
+  *
+  * @param n
+  *     The index of the lock to lock or unlock.
+  *
+  * @param file
+  *     The filename of the function setting the lock, for debugging purposes.
+  *
+  * @param line
+  *     The line number of the function setting the lock, for debugging
+  *     purposes.
+  */
+ static void guac_common_ssh_openssl_locking_callback(int mode, int n,
+         const char* file, int line){
+ 
+     /* Lock given mutex upon request */
+     if (mode & CRYPTO_LOCK)
+         pthread_mutex_lock(&(guac_common_ssh_openssl_locks[n]));
+ 
+     /* Unlock given mutex upon request */
+     else if (mode & CRYPTO_UNLOCK)
+         pthread_mutex_unlock(&(guac_common_ssh_openssl_locks[n]));
+ 
+ }
+ 
+ /**
+  * Called by OpenSSL when determining the current thread ID.
+  *
+  * @return
+  *     An ID which uniquely identifies the current thread.
+  */
+ static unsigned long guac_common_ssh_openssl_id_callback() {
+     return (unsigned long) pthread_self();
+ }
+ 
+ /**
+  * Creates the given number of mutexes, such that OpenSSL will have at least
+  * this number of mutexes at its disposal.
+  *
+  * @param count
+  *     The number of mutexes (locks) to create.
+  */
+ static void guac_common_ssh_openssl_init_locks(int count) {
+ 
+     int i;
+ 
+     /* Allocate required number of locks */
+     guac_common_ssh_openssl_locks =
+         malloc(sizeof(pthread_mutex_t) * count);
+ 
+     /* Initialize each lock */
+     for (i=0; i < count; i++)
+         pthread_mutex_init(&(guac_common_ssh_openssl_locks[i]), NULL);
+ 
+ }
+ 
+ /**
+  * Frees the given number of mutexes.
+  *
+  * @param count
+  *     The number of mutexes (locks) to free.
+  */
+ static void guac_common_ssh_openssl_free_locks(int count) {
+ 
+     int i;
+ 
+     /* SSL lock array was not initialized */
+     if (guac_common_ssh_openssl_locks == NULL)
+         return;
+ 
+     /* Free all locks */
+     for (i=0; i < count; i++)
+         pthread_mutex_destroy(&(guac_common_ssh_openssl_locks[i]));
+ 
+     /* Free lock array */
+     free(guac_common_ssh_openssl_locks);
+ 
+ }
+ 
+ int guac_common_ssh_init(guac_client* client) {
+ 
+ #ifdef LIBSSH2_USES_GCRYPT
+     /* Init threadsafety in libgcrypt */
+     gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+     if (!gcry_check_version(GCRYPT_VERSION)) {
+         guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch.");
+         return 1;
+     }
+ #endif
+ 
+     /* Init threadsafety in OpenSSL */
+     guac_common_ssh_openssl_init_locks(CRYPTO_num_locks());
+     CRYPTO_set_id_callback(guac_common_ssh_openssl_id_callback);
+     CRYPTO_set_locking_callback(guac_common_ssh_openssl_locking_callback);
+ 
+     /* Init OpenSSL */
+     SSL_library_init();
+     ERR_load_crypto_strings();
+ 
+     /* Init libssh2 */
+     libssh2_init(0);
+ 
+     /* Success */
+     return 0;
+ 
+ }
+ 
+ void guac_common_ssh_uninit() {
+     guac_common_ssh_openssl_free_locks(CRYPTO_num_locks());
+ }
+ 
+ /**
+  * Callback invoked by libssh2 when libssh2_userauth_publickkey() is invoked.
+  * This callback must sign the given data, returning the signature as newly-
+  * allocated buffer space.
+  *
+  * @param session
+  *     The SSH session for which the signature is being generated.
+  *
+  * @param sig
+  *     A pointer to the buffer space containing the signature. This callback
+  *     MUST allocate and assign this space.
+  *
+  * @param sig_len
+  *     The length of the signature within the allocated buffer space, in bytes.
+  *     This value must be set to the size of the signature after the signing
+  *     operation completes.
+  *
+  * @param data
+  *     The arbitrary data that must be signed.
+  *
+  * @param data_len
+  *     The length of the arbitrary data to be signed, in bytes.
+  *
+  * @param abstract
+  *     The value of the abstract parameter provided with the corresponding call
+  *     to libssh2_userauth_publickey().
+  *
+  * @return
+  *     Zero on success, non-zero if the signing operation failed.
+  */
+ static int guac_common_ssh_sign_callback(LIBSSH2_SESSION* session,
+         unsigned char** sig, size_t* sig_len,
+         const unsigned char* data, size_t data_len, void **abstract) {
+ 
+     guac_common_ssh_key* key = (guac_common_ssh_key*) abstract;
+     int length;
+ 
+     /* Allocate space for signature */
+     *sig = malloc(4096);
+ 
+     /* Sign with key */
+     length = guac_common_ssh_key_sign(key, (const char*) data, data_len, *sig);
+     if (length < 0)
+         return 1;
+ 
+     *sig_len = length;
+     return 0;
+ }
+ 
+ /**
+  * Callback for the keyboard-interactive authentication method. Currently
+  * supports just one prompt for the password. This callback is invoked as
+  * needed to fullfill a call to libssh2_userauth_keyboard_interactive().
+  *
+  * @param name
+  *     An arbitrary name which should be printed to the terminal for the
+  *     benefit of the user. This is currently ignored.
+  *
+  * @param name_len
+  *     The length of the name string, in bytes.
+  *
+  * @param instruction
+  *     Arbitrary instructions which should be printed to the terminal for the
+  *     benefit of the user. This is currently ignored.
+  *
+  * @param instruction_len
+  *     The length of the instruction string, in bytes.
+  *
+  * @param num_prompts
+  *     The number of keyboard-interactive prompts for which responses are
+  *     requested. This callback currently only supports one prompt, and assumes
+  *     that this prompt is requesting the password.
+  *
+  * @param prompts
+  *     An array of all keyboard-interactive prompts for which responses are
+  *     requested.
+  *
+  * @param responses
+  *     A parallel array into which all prompt responses should be stored. Each
+  *     entry within this array corresponds to the entry in the prompts array
+  *     with the same index.
+  *
+  * @param abstract
+  *     The value of the abstract parameter provided when the SSH session was
+  *     created with libssh2_session_init_ex().
+  */
+ static void guac_common_ssh_kbd_callback(const char *name, int name_len,
+         const char *instruction, int instruction_len, int num_prompts,
+         const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
+         LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
+         void **abstract) {
+ 
+     guac_common_ssh_session* common_session =
+         (guac_common_ssh_session*) *abstract;
+ 
+     guac_client* client = common_session->client;
+ 
+     /* Send password if only one prompt */
+     if (num_prompts == 1) {
+         char* password = common_session->user->password;
+         responses[0].text = strdup(password);
+         responses[0].length = strlen(password);
+     }
+ 
+     /* If more than one prompt, a single password is not enough */
+     else
+         guac_client_log(client, GUAC_LOG_WARNING,
+                 "Unsupported number of keyboard-interactive prompts: %i",
+                 num_prompts);
+ 
+ }
+ 
+ /**
+  * Authenticates the user associated with the given session over SSH. All
+  * required credentials must already be present within the user object
+  * associated with the given session.
+  *
+  * @param session
+  *     The session associated with the user to be authenticated.
+  *
+  * @return
+  *     Zero if authentication succeeds, or non-zero if authentication has
+  *     failed.
+  */
+ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) {
+ 
+     guac_client* client = common_session->client;
+     guac_common_ssh_user* user = common_session->user;
+     LIBSSH2_SESSION* session = common_session->session;
+ 
+     /* Get user credentials */
+     char* username = user->username;
+     char* password = user->password;
+     guac_common_ssh_key* key = user->private_key;
+ 
+     /* Validate username provided */
+     if (username == NULL) {
+         guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                 "SSH authentication requires a username.");
+         return 1;
+     }
+ 
+     /* Get list of supported authentication methods */
+     char* user_authlist = libssh2_userauth_list(session, username,
+             strlen(username));
+     guac_client_log(client, GUAC_LOG_DEBUG,
+             "Supported authentication methods: %s", user_authlist);
+ 
+     /* Authenticate with private key, if provided */
+     if (key != NULL) {
+ 
+         /* Check if public key auth is supported on the server */
+         if (strstr(user_authlist, "publickey") == NULL) {
+             guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                     "Public key authentication is not supported by "
+                     "the SSH server");
+             return 1;
+         }
+ 
+         /* Attempt public key auth */
+         if (libssh2_userauth_publickey(session, username,
+                     (unsigned char*) key->public_key, key->public_key_length,
+                     guac_common_ssh_sign_callback, (void**) key)) {
+ 
+             /* Abort on failure */
+             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 1;
+ 
+         }
+ 
+         /* Private key authentication succeeded */
+         return 0;
+ 
+     }
+ 
+     /* Authenticate with password, if provided */
+     else if (password != NULL) {
+ 
+         /* Check if password auth is supported on the server */
+         if (strstr(user_authlist, "password") != NULL) {
+ 
+             /* Attempt password authentication */
+             if (libssh2_userauth_password(session, username, password)) {
+ 
+                 /* Abort on failure */
+                 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 1;
+             }
+ 
+             /* Password authentication succeeded */
+             return 0;
+ 
+         }
+ 
+         /* Check if keyboard-interactive auth is supported on the server */
+         if (strstr(user_authlist, "keyboard-interactive") != NULL) {
+ 
+             /* Attempt keyboard-interactive auth using provided password */
+             if (libssh2_userauth_keyboard_interactive(session, username,
+                         &guac_common_ssh_kbd_callback)) {
+ 
+                 /* Abort on failure */
+                 char* error_message;
+                 libssh2_session_last_error(session, &error_message, NULL, 0);
+                 guac_client_abort(client,
+                         GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                         "Keyboard-interactive authentication failed: %s",
+                         error_message);
+ 
+                 return 1;
+             }
+ 
+             /* Keyboard-interactive authentication succeeded */
+             return 0;
+ 
+         }
+ 
+         /* No known authentication types available */
+         guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                 "Password and keyboard-interactive authentication are not "
+                 "supported by the SSH server");
+         return 1;
+ 
+     }
+ 
+     /* No credentials provided */
+     guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+             "SSH authentication requires either a private key or a password.");
+     return 1;
+ 
+ }
+ 
+ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
+         const char* hostname, const char* port, guac_common_ssh_user* user) {
+ 
+     int retval;
+ 
+     int fd;
+     struct addrinfo* addresses;
+     struct addrinfo* current_address;
+ 
+     char connected_address[1024];
+     char connected_port[64];
+ 
+     struct addrinfo hints = {
+         .ai_family   = AF_UNSPEC,
+         .ai_socktype = SOCK_STREAM,
+         .ai_protocol = IPPROTO_TCP
+     };
+ 
+     /* Get socket */
+     fd = socket(AF_INET, SOCK_STREAM, 0);
+     if (fd < 0) {
+         guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+                 "Unable to create socket: %s", strerror(errno));
+         return NULL;
+     }
+ 
+     /* Get addresses connection */
+     if ((retval = getaddrinfo(hostname, port, &hints, &addresses))) {
+         guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+                 "Error parsing given address or port: %s",
+                 gai_strerror(retval));
+         close(fd);
+         return NULL;
+     }
+ 
+     /* Attempt connection to each address until success */
+     current_address = addresses;
+     while (current_address != NULL) {
+ 
+         /* 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(client, GUAC_LOG_DEBUG,
+                     "Unable to resolve host: %s", gai_strerror(retval));
+ 
+         /* Connect */
+         if (connect(fd, current_address->ai_addr,
+                         current_address->ai_addrlen) == 0) {
+ 
+             guac_client_log(client, GUAC_LOG_DEBUG,
+                     "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(client, GUAC_LOG_DEBUG, "Unable to connect to "
+                     "host %s, port %s: %s",
+                     connected_address, connected_port, strerror(errno));
+ 
+         current_address = current_address->ai_next;
+ 
+     }
+ 
+     /* Free addrinfo */
+     freeaddrinfo(addresses);
+ 
+     /* If unable to connect to anything, fail */
+     if (current_address == NULL) {
 -        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
++        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND,
+                 "Unable to connect to any addresses.");
+         close(fd);
+         return NULL;
+     }
+ 
+     /* Allocate new session */
+     guac_common_ssh_session* common_session =
+         malloc(sizeof(guac_common_ssh_session));
+ 
+     /* Open SSH session */
+     LIBSSH2_SESSION* session = libssh2_session_init_ex(NULL, NULL,
+             NULL, common_session);
+     if (session == NULL) {
+         guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+                 "Session allocation failed.");
+         free(common_session);
+         close(fd);
+         return NULL;
+     }
+ 
+     /* Perform handshake */
+     if (libssh2_session_handshake(session, fd)) {
+         guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
+                 "SSH handshake failed.");
+         free(common_session);
+         close(fd);
+         return NULL;
+     }
+ 
+     /* Store basic session data */
+     common_session->client = client;
+     common_session->user = user;
+     common_session->session = session;
+     common_session->fd = fd;
+ 
+     /* Attempt authentication */
+     if (guac_common_ssh_authenticate(common_session)) {
+         free(common_session);
+         close(fd);
+         return NULL;
+     }
+ 
+     /* Return created session */
+     return common_session;
+ 
+ }
+ 
+ void guac_common_ssh_destroy_session(guac_common_ssh_session* session) {
+ 
+     /* Disconnect and clean up libssh2 */
+     libssh2_session_disconnect(session->session, "Bye");
+     libssh2_session_free(session->session);
+ 
+     /* Free all other data */
+     free(session);
+ 
+ }
+ 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/c4903a8e/src/protocols/rdp/rdp.c
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/c4903a8e/src/protocols/telnet/telnet.c
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/c4903a8e/src/protocols/vnc/vnc.c
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/c4903a8e/src/terminal/terminal.c
----------------------------------------------------------------------


Mime
View raw message