From d74554472ef94fcb64029986cc0f1afac4b10acf Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Sun, 5 May 2019 17:58:25 +0200
Subject: [PATCH] wolfSSH: support for new SSH backend

---
 configure.ac     |  40 ++-
 lib/Makefile.inc |   2 +-
 lib/easy.c       |  11 +
 lib/ssh.h        |  13 +-
 lib/version.c    |   5 +
 lib/wolfssh.c    | 749 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/wolfssh.h    |  28 ++
 7 files changed, 841 insertions(+), 7 deletions(-)
 create mode 100644 lib/wolfssh.c
 create mode 100644 lib/wolfssh.h

diff --git a/configure.ac b/configure.ac
index 00fef94891..4061aa352f 100755
--- a/configure.ac
+++ b/configure.ac
@@ -2853,17 +2853,23 @@ dnl **********************************************************************
 dnl Default to compiler & linker defaults for LIBSSH2 files & libraries.
 OPT_LIBSSH2=off
 AC_ARG_WITH(libssh2,dnl
-AC_HELP_STRING([--with-libssh2=PATH],[Where to look for libssh2, PATH points to the LIBSSH2 installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option])
-AC_HELP_STRING([--with-libssh2], [enable LIBSSH2]),
+AC_HELP_STRING([--with-libssh2=PATH],[Where to look for libssh2, PATH points to the libssh2 installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option])
+AC_HELP_STRING([--with-libssh2], [enable libssh2]),
   OPT_LIBSSH2=$withval, OPT_LIBSSH2=no)
 
 
 OPT_LIBSSH=off
 AC_ARG_WITH(libssh,dnl
-AC_HELP_STRING([--with-libssh=PATH],[Where to look for libssh, PATH points to the LIBSSH installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option])
-AC_HELP_STRING([--with-libssh], [enable LIBSSH]),
+AC_HELP_STRING([--with-libssh=PATH],[Where to look for libssh, PATH points to the libssh installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option])
+AC_HELP_STRING([--with-libssh], [enable libssh]),
   OPT_LIBSSH=$withval, OPT_LIBSSH=no)
 
+OPT_WOLFSSH=off
+AC_ARG_WITH(wolfssh,dnl
+AC_HELP_STRING([--with-wolfssh=PATH],[Where to look for wolfssh, PATH points to the wolfSSH installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option])
+AC_HELP_STRING([--with-wolfssh], [enable wolfssh]),
+  OPT_WOLFSSH=$withval, OPT_WOLFSSH=no)
+
 if test X"$OPT_LIBSSH2" != Xno; then
   dnl backup the pre-libssh2 variables
   CLEANLDFLAGS="$LDFLAGS"
@@ -3010,6 +3016,28 @@ elif test X"$OPT_LIBSSH" != Xno; then
     CPPFLAGS=$CLEANCPPFLAGS
     LIBS=$CLEANLIBS
   fi
+elif test X"$OPT_WOLFSSH" != Xno; then
+  dnl backup the pre-wolfssh variables
+  CLEANLDFLAGS="$LDFLAGS"
+  CLEANCPPFLAGS="$CPPFLAGS"
+  CLEANLIBS="$LIBS"
+
+
+  if test "$OPT_WOLFSSH" != yes; then
+     WOLFCONFIG="$OPT_WOLFSSH/bin/wolfssh-config"
+     LDFLAGS="$LDFLAGS `$WOLFCONFIG --libs`"
+     CPPFLAGS="$CPPFLAGS `$WOLFCONFIG --cflags`"
+  fi
+
+  AC_CHECK_LIB(wolfssh, wolfSSH_Init)
+
+  AC_CHECK_HEADERS(wolfssh/ssh.h,
+    curl_ssh_msg="enabled (wolfSSH)"
+    WOLFSSH_ENABLED=1
+    AC_DEFINE(USE_WOLFSSH, 1, [if wolfSSH is in use])
+    AC_SUBST(USE_WOLFSSH, [1])
+  )
+
 fi
 
 dnl **********************************************************************
@@ -4338,6 +4366,10 @@ if test "x$USE_LIBSSH" = "x1"; then
   SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SCP"
   SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SFTP"
 fi
+if test "x$USE_WOLFSSH" = "x1"; then
+  SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SCP"
+  SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SFTP"
+fi
 if test "x$CURL_DISABLE_RTSP" != "x1"; then
   SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS RTSP"
 fi
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index 235b82b0e3..832644f310 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -55,7 +55,7 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c   \
   curl_multibyte.c hostcheck.c conncache.c dotdot.c                     \
   x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c      \
   mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c  \
-  doh.c urlapi.c curl_get_line.c altsvc.c
+  doh.c urlapi.c curl_get_line.c altsvc.c wolfssh.c
 
 LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
   formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h         \
diff --git a/lib/easy.c b/lib/easy.c
index 4d8817906b..99112948f8 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -201,6 +201,13 @@ static CURLcode global_init(long flags, bool memoryfuncs)
   }
 #endif
 
+#ifdef USE_WOLFSSH
+  if(WS_SUCCESS != wolfSSH_Init()) {
+    DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n"));
+    return CURLE_FAILED_INIT;
+  }
+#endif
+
   if(flags & CURL_GLOBAL_ACK_EINTR)
     Curl_ack_eintr = 1;
 
@@ -282,6 +289,10 @@ void curl_global_cleanup(void)
   (void)ssh_finalize();
 #endif
 
+#ifdef USE_WOLFSSH
+  (void)wolfSSH_Cleanup();
+#endif
+
   init_flags  = 0;
 }
 
diff --git a/lib/ssh.h b/lib/ssh.h
index 0620aac328..8072a365ff 100644
--- a/lib/ssh.h
+++ b/lib/ssh.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -30,7 +30,11 @@
 #elif defined(HAVE_LIBSSH_LIBSSH_H)
 #include <libssh/libssh.h>
 #include <libssh/sftp.h>
-#endif /* HAVE_LIBSSH2_H */
+#elif defined(USE_WOLFSSH)
+#include <wolfssh/ssh.h>
+#include <wolfssh/wolfsftp.h>
+#include "wolfssh.h"
+#endif
 
 /****************************************************************************
  * SSH unique setup
@@ -188,6 +192,11 @@ struct ssh_conn {
 #ifdef HAVE_LIBSSH2_KNOWNHOST_API
   LIBSSH2_KNOWNHOSTS *kh;
 #endif
+#elif defined(USE_WOLFSSH)
+  WOLFSSH *ssh_session;
+  WOLFSSH_CTX *ctx;
+  word32 handleSz;
+  byte handle[WOLFSSH_MAX_HANDLE];
 #endif /* USE_LIBSSH */
 };
 
diff --git a/lib/version.c b/lib/version.c
index 87ebe0bc7a..d407c380f7 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -182,6 +182,11 @@ char *curl_version(void)
   left -= len;
   ptr += len;
 #endif
+#ifdef USE_WOLFSSH
+  len = msnprintf(ptr, left, " wolfssh/%s", LIBWOLFSSH_VERSION_STRING);
+  left -= len;
+  ptr += len;
+#endif
 #ifdef USE_NGHTTP2
   len = Curl_http2_ver(ptr, left);
   left -= len;
diff --git a/lib/wolfssh.c b/lib/wolfssh.c
new file mode 100644
index 0000000000..d0bebc47f2
--- /dev/null
+++ b/lib/wolfssh.c
@@ -0,0 +1,749 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_WOLFSSH
+
+#include <limits.h>
+
+#include <wolfssh/ssh.h>
+#include <wolfssh/wolfsftp.h>
+#include "urldata.h"
+#include "connect.h"
+#include "sendf.h"
+#include "progress.h"
+#include "curl_path.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+static CURLcode wssh_connect(struct connectdata *conn, bool *done);
+static CURLcode wssh_multi_statemach(struct connectdata *conn, bool *done);
+static CURLcode wssh_do(struct connectdata *conn, bool *done);
+static CURLcode wscp_done(struct connectdata *conn,
+                         CURLcode, bool premature);
+static CURLcode wscp_doing(struct connectdata *conn,
+                          bool *dophase_done);
+static CURLcode wscp_disconnect(struct connectdata *conn,
+                                bool dead_connection);
+static CURLcode wsftp_done(struct connectdata *conn,
+                          CURLcode, bool premature);
+static CURLcode wsftp_doing(struct connectdata *conn,
+                           bool *dophase_done);
+static CURLcode wsftp_disconnect(struct connectdata *conn, bool dead);
+
+static int wssh_getsock(struct connectdata *conn,
+                       curl_socket_t *sock, /* points to numsocks number
+                                               of sockets */
+                       int numsocks);
+
+static int wssh_perform_getsock(const struct connectdata *conn,
+                               curl_socket_t *sock, /* points to numsocks
+                                                       number of sockets */
+                               int numsocks);
+
+static CURLcode wssh_setup_connection(struct connectdata *conn);
+
+/*
+ * SCP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_scp = {
+  "SCP",                                /* scheme */
+  wssh_setup_connection,                /* setup_connection */
+  wssh_do,                              /* do_it */
+  wscp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  wssh_connect,                         /* connect_it */
+  wssh_multi_statemach,                 /* connecting */
+  wscp_doing,                           /* doing */
+  wssh_getsock,                         /* proto_getsock */
+  wssh_getsock,                         /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  wssh_perform_getsock,                 /* perform_getsock */
+  wscp_disconnect,                      /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* connection_check */
+  PORT_SSH,                             /* defport */
+  CURLPROTO_SCP,                        /* protocol */
+  PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
+  | PROTOPT_NOURLQUERY                  /* flags */
+};
+
+
+/*
+ * SFTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_sftp = {
+  "SFTP",                               /* scheme */
+  wssh_setup_connection,                /* setup_connection */
+  wssh_do,                              /* do_it */
+  wsftp_done,                           /* done */
+  ZERO_NULL,                            /* do_more */
+  wssh_connect,                         /* connect_it */
+  wssh_multi_statemach,                 /* connecting */
+  wsftp_doing,                          /* doing */
+  wssh_getsock,                         /* proto_getsock */
+  wssh_getsock,                         /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  wssh_perform_getsock,                 /* perform_getsock */
+  wsftp_disconnect,                     /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* connection_check */
+  PORT_SSH,                             /* defport */
+  CURLPROTO_SFTP,                       /* protocol */
+  PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
+  | PROTOPT_NOURLQUERY                  /* flags */
+};
+
+/*
+ * SSH State machine related code
+ */
+/* This is the ONLY way to change SSH state! */
+static void state(struct connectdata *conn, sshstate nowstate)
+{
+  struct ssh_conn *sshc = &conn->proto.sshc;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  /* for debug purposes */
+  static const char * const names[] = {
+    "SSH_STOP",
+    "SSH_INIT",
+    "SSH_S_STARTUP",
+    "SSH_HOSTKEY",
+    "SSH_AUTHLIST",
+    "SSH_AUTH_PKEY_INIT",
+    "SSH_AUTH_PKEY",
+    "SSH_AUTH_PASS_INIT",
+    "SSH_AUTH_PASS",
+    "SSH_AUTH_AGENT_INIT",
+    "SSH_AUTH_AGENT_LIST",
+    "SSH_AUTH_AGENT",
+    "SSH_AUTH_HOST_INIT",
+    "SSH_AUTH_HOST",
+    "SSH_AUTH_KEY_INIT",
+    "SSH_AUTH_KEY",
+    "SSH_AUTH_GSSAPI",
+    "SSH_AUTH_DONE",
+    "SSH_SFTP_INIT",
+    "SSH_SFTP_REALPATH",
+    "SSH_SFTP_QUOTE_INIT",
+    "SSH_SFTP_POSTQUOTE_INIT",
+    "SSH_SFTP_QUOTE",
+    "SSH_SFTP_NEXT_QUOTE",
+    "SSH_SFTP_QUOTE_STAT",
+    "SSH_SFTP_QUOTE_SETSTAT",
+    "SSH_SFTP_QUOTE_SYMLINK",
+    "SSH_SFTP_QUOTE_MKDIR",
+    "SSH_SFTP_QUOTE_RENAME",
+    "SSH_SFTP_QUOTE_RMDIR",
+    "SSH_SFTP_QUOTE_UNLINK",
+    "SSH_SFTP_QUOTE_STATVFS",
+    "SSH_SFTP_GETINFO",
+    "SSH_SFTP_FILETIME",
+    "SSH_SFTP_TRANS_INIT",
+    "SSH_SFTP_UPLOAD_INIT",
+    "SSH_SFTP_CREATE_DIRS_INIT",
+    "SSH_SFTP_CREATE_DIRS",
+    "SSH_SFTP_CREATE_DIRS_MKDIR",
+    "SSH_SFTP_READDIR_INIT",
+    "SSH_SFTP_READDIR",
+    "SSH_SFTP_READDIR_LINK",
+    "SSH_SFTP_READDIR_BOTTOM",
+    "SSH_SFTP_READDIR_DONE",
+    "SSH_SFTP_DOWNLOAD_INIT",
+    "SSH_SFTP_DOWNLOAD_STAT",
+    "SSH_SFTP_CLOSE",
+    "SSH_SFTP_SHUTDOWN",
+    "SSH_SCP_TRANS_INIT",
+    "SSH_SCP_UPLOAD_INIT",
+    "SSH_SCP_DOWNLOAD_INIT",
+    "SSH_SCP_DOWNLOAD",
+    "SSH_SCP_DONE",
+    "SSH_SCP_SEND_EOF",
+    "SSH_SCP_WAIT_EOF",
+    "SSH_SCP_WAIT_CLOSE",
+    "SSH_SCP_CHANNEL_FREE",
+    "SSH_SESSION_DISCONNECT",
+    "SSH_SESSION_FREE",
+    "QUIT"
+  };
+
+  /* a precaution to make sure the lists are in sync */
+  DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
+
+  if(sshc->state != nowstate) {
+    infof(conn->data, "wolfssh %p state change from %s to %s\n",
+          (void *)sshc, names[sshc->state], names[nowstate]);
+  }
+#endif
+
+  sshc->state = nowstate;
+}
+
+static ssize_t wscp_send(struct connectdata *conn, int sockindex,
+                         const void *mem, size_t len, CURLcode *err)
+{
+  ssize_t nwrite = 0;
+  (void)conn;
+  (void)sockindex; /* we only support SCP on the fixed known primary socket */
+  (void)mem;
+  (void)len;
+  (void)err;
+
+  return nwrite;
+}
+
+static ssize_t wscp_recv(struct connectdata *conn, int sockindex,
+                         char *mem, size_t len, CURLcode *err)
+{
+  ssize_t nread = 0;
+  (void)conn;
+  (void)sockindex; /* we only support SCP on the fixed known primary socket */
+  (void)mem;
+  (void)len;
+  (void)err;
+
+  return nread;
+}
+
+/* return number of sent bytes */
+static ssize_t wsftp_send(struct connectdata *conn, int sockindex,
+                          const void *mem, size_t len, CURLcode *err)
+{
+  ssize_t nwrite = 0;
+  (void)sockindex;
+  (void)conn;
+  (void)mem;
+  (void)len;
+  (void)err;
+
+  return nwrite;
+}
+
+/*
+ * Return number of received (decrypted) bytes
+ * or <0 on error
+ */
+static ssize_t wsftp_recv(struct connectdata *conn, int sockindex,
+                          char *mem, size_t len, CURLcode *err)
+{
+  ssize_t nread = 0;
+  (void)conn;
+  (void)sockindex;
+  (void)mem;
+  (void)len;
+  (void)err;
+
+  return nread;
+}
+
+/*
+ * SSH setup and connection
+ */
+static CURLcode wssh_setup_connection(struct connectdata *conn)
+{
+  struct SSHPROTO *ssh;
+
+  conn->data->req.protop = ssh = calloc(1, sizeof(struct SSHPROTO));
+  if(!ssh)
+    return CURLE_OUT_OF_MEMORY;
+
+  return CURLE_OK;
+}
+
+static Curl_recv wscp_recv, wsftp_recv;
+static Curl_send wscp_send, wsftp_send;
+
+static int userauth(byte authtype,
+                    WS_UserAuthData* authdata,
+                    void *ctx)
+{
+  struct connectdata *conn = ctx;
+  word32 plen = (word32) strlen(conn->passwd);
+  fprintf(stderr, "wolfssh callback: %s type %s\n", __func__,
+          authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" :
+          "PUBLICCKEY");
+  authdata->sf.password.password = (byte *)conn->user;
+  authdata->sf.password.passwordSz = plen;
+
+  return 0;
+}
+
+static CURLcode wssh_connect(struct connectdata *conn, bool *done)
+{
+  struct Curl_easy *data = conn->data;
+  struct ssh_conn *ssh;
+  curl_socket_t sock = conn->sock[FIRSTSOCKET];
+  int rc;
+
+  /* initialize per-handle data if not already */
+  if(!data->req.protop)
+    wssh_setup_connection(conn);
+
+  /* We default to persistent connections. We set this already in this connect
+     function to make the re-use checks properly be able to check this bit. */
+  connkeep(conn, "SSH default");
+
+  if(conn->handler->protocol & CURLPROTO_SCP) {
+    conn->recv[FIRSTSOCKET] = wscp_recv;
+    conn->send[FIRSTSOCKET] = wscp_send;
+  }
+  else {
+    conn->recv[FIRSTSOCKET] = wsftp_recv;
+    conn->send[FIRSTSOCKET] = wsftp_send;
+  }
+  ssh = &conn->proto.sshc;
+  ssh->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
+  if(!ssh->ctx) {
+    failf(data, "No wolfSSH context");
+    goto error;
+  }
+
+  ssh->ssh_session = wolfSSH_new(ssh->ctx);
+  if(ssh->ssh_session == NULL) {
+    failf(data, "No wolfSSH session");
+    goto error;
+  }
+
+  rc = wolfSSH_SetUsername(ssh->ssh_session, conn->user);
+  if(rc != WS_SUCCESS) {
+    failf(data, "wolfSSH failed to set user name");
+    goto error;
+  }
+
+  /* set callback for authentication */
+  wolfSSH_SetUserAuth(ssh->ctx, userauth);
+  wolfSSH_SetUserAuthCtx(ssh->ssh_session, conn);
+
+  rc = wolfSSH_set_fd(ssh->ssh_session, (int)sock);
+  if(rc) {
+    failf(data, "wolfSSH failed to set socket");
+    goto error;
+  }
+
+#if 1
+  wolfSSH_Debugging_ON();
+#endif
+
+  *done = TRUE;
+  if(conn->handler->protocol & CURLPROTO_SCP)
+    state(conn, SSH_INIT);
+  else
+    state(conn, SSH_SFTP_INIT);
+
+  return wssh_multi_statemach(conn, done);
+  error:
+  wolfSSH_free(ssh->ssh_session);
+  wolfSSH_CTX_free(ssh->ctx);
+  return CURLE_FAILED_INIT;
+}
+
+/*
+ * wssh_statemach_act() runs the SSH state machine as far as it can without
+ * blocking and without reaching the end.  The data the pointer 'block' points
+ * to will be set to TRUE if the wolfssh function returns EAGAIN meaning it
+ * wants to be called again when the socket is ready
+ */
+
+static CURLcode wssh_statemach_act(struct connectdata *conn, bool *block)
+{
+  CURLcode result = CURLE_OK;
+  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct Curl_easy *data = conn->data;
+  struct SSHPROTO *sftp_scp = data->req.protop;
+  WS_SFTPNAME *name;
+  int rc;
+  *block = FALSE; /* we're not blocking by default */
+
+  do {
+    switch(sshc->state) {
+    case SSH_INIT:
+      state(conn, SSH_S_STARTUP);
+      /* FALLTHROUGH */
+    case SSH_S_STARTUP:
+      rc = wolfSSH_connect(sshc->ssh_session);
+      if(rc != WS_SUCCESS)
+        rc = wolfSSH_get_error(sshc->ssh_session);
+      if(rc == WS_WANT_READ) {
+        *block = TRUE;
+        conn->waitfor = KEEP_RECV;
+        return CURLE_OK;
+      }
+      else if(rc == WS_WANT_WRITE) {
+        *block = TRUE;
+        conn->waitfor = KEEP_SEND;
+        return CURLE_OK;
+      }
+      else if(rc != WS_SUCCESS) {
+        state(conn, SSH_STOP);
+        return CURLE_SSH;
+      }
+      infof(data, "wolfssh connected!\n");
+      state(conn, SSH_STOP);
+      break;
+    case SSH_STOP:
+      break;
+    case SSH_SFTP_INIT:
+      rc = wolfSSH_SFTP_connect(sshc->ssh_session);
+      if(rc != WS_SUCCESS)
+        rc = wolfSSH_get_error(sshc->ssh_session);
+      if(rc == WS_WANT_READ) {
+        *block = TRUE;
+        conn->waitfor = KEEP_RECV;
+        return CURLE_OK;
+      }
+      else if(rc == WS_WANT_WRITE) {
+        *block = TRUE;
+        conn->waitfor = KEEP_SEND;
+        return CURLE_OK;
+      }
+      else if(rc == WS_SUCCESS) {
+        infof(data, "wolfssh SFTP connected!\n");
+        state(conn, SSH_SFTP_REALPATH);
+      }
+      else {
+        failf(data, "wolfssh SFTP connect error %d", rc);
+        return CURLE_SSH;
+      }
+      break;
+    case SSH_SFTP_REALPATH:
+      name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)".");
+      rc = wolfSSH_get_error(sshc->ssh_session);
+      if(rc == WS_WANT_READ) {
+        *block = TRUE;
+        conn->waitfor = KEEP_RECV;
+        return CURLE_OK;
+      }
+      else if(rc == WS_WANT_WRITE) {
+        *block = TRUE;
+        conn->waitfor = KEEP_SEND;
+        return CURLE_OK;
+      }
+      else if(name && (rc == WS_SUCCESS)) {
+        sshc->homedir = malloc(name->fSz + 1);
+        if(!sshc->homedir) {
+          sshc->actualcode = CURLE_OUT_OF_MEMORY;
+        }
+        else {
+          memcpy(sshc->homedir, name->fName, name->fSz);
+          sshc->homedir[name->fSz] = 0;
+          infof(data, "wolfssh SFTP realpath succeeded!\n");
+        }
+        wolfSSH_SFTPNAME_list_free(name);
+        state(conn, SSH_STOP);
+        return CURLE_OK;
+      }
+#if 1 /* REMOVE ugly work-around */
+      else if(!name && (rc == WS_SUCCESS)) {
+        sshc->homedir = strdup((char *)"/");
+        infof(data, "wolfssh SFTP realpath FAKE succeeded!\n");
+        state(conn, SSH_STOP);
+        return CURLE_OK;
+      }
+#endif
+      else {
+        failf(data, "wolfssh SFTP realpath %d", rc);
+        return CURLE_SSH;
+      }
+      break;
+    case SSH_SFTP_QUOTE_INIT:
+      result = Curl_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
+      if(result) {
+        sshc->actualcode = result;
+        state(conn, SSH_STOP);
+        break;
+      }
+
+      if(data->set.quote) {
+        infof(data, "Sending quote commands\n");
+        sshc->quote_item = data->set.quote;
+        state(conn, SSH_SFTP_QUOTE);
+      }
+      else {
+        state(conn, SSH_SFTP_GETINFO);
+      }
+      break;
+    case SSH_SFTP_GETINFO:
+      if(data->set.get_filetime) {
+        state(conn, SSH_SFTP_FILETIME);
+      }
+      else {
+        state(conn, SSH_SFTP_TRANS_INIT);
+      }
+      break;
+    case SSH_SFTP_TRANS_INIT:
+      if(data->set.upload)
+        state(conn, SSH_SFTP_UPLOAD_INIT);
+      else {
+        if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
+          state(conn, SSH_SFTP_READDIR_INIT);
+        else
+          state(conn, SSH_SFTP_DOWNLOAD_INIT);
+      }
+      break;
+    case SSH_SFTP_DOWNLOAD_INIT:
+      rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
+                             WOLFSSH_FXF_READ, NULL,
+                             sshc->handle, &sshc->handleSz);
+      if(rc == WS_FATAL_ERROR)
+        rc = wolfSSH_get_error(sshc->ssh_session);
+      if(rc == WS_WANT_READ) {
+        *block = TRUE;
+        conn->waitfor = KEEP_RECV;
+        return CURLE_OK;
+      }
+      else if(rc == WS_WANT_WRITE) {
+        *block = TRUE;
+        conn->waitfor = KEEP_SEND;
+        return CURLE_OK;
+      }
+      else if(rc == WS_SUCCESS) {
+        infof(data, "wolfssh SFTP open succeeded!\n");
+        state(conn, SSH_STOP);
+        return CURLE_OK;
+      }
+      else {
+        failf(data, "wolfssh SFTP open failed: %d", rc);
+        return CURLE_SSH;
+      }
+
+      break;
+
+    case SSH_SFTP_READDIR_INIT:
+      Curl_pgrsSetDownloadSize(data, -1);
+      if(data->set.opt_no_body) {
+        state(conn, SSH_STOP);
+        break;
+      }
+      name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path);
+      rc = wolfSSH_get_error(sshc->ssh_session);
+
+      if(rc == WS_WANT_READ) {
+        *block = TRUE;
+        conn->waitfor = KEEP_RECV;
+        return CURLE_OK;
+      }
+      else if(rc == WS_WANT_WRITE) {
+        *block = TRUE;
+        conn->waitfor = KEEP_SEND;
+        return CURLE_OK;
+      }
+      else if(name && (rc == WS_SUCCESS)) {
+        while(name) {
+          fprintf(stderr, "name: %s\n", name->fName);
+          name = name->next;
+        }
+        wolfSSH_SFTPNAME_list_free(name);
+        state(conn, SSH_STOP);
+        return CURLE_OK;
+      }
+      else {
+        failf(data, "wolfssh SFTP ls failed: %d", rc);
+        return CURLE_SSH;
+      }
+      break;
+
+    default:
+      break;
+    }
+  } while(1);
+  return result;
+}
+
+/* called repeatedly until done from multi.c */
+static CURLcode wssh_multi_statemach(struct connectdata *conn, bool *done)
+{
+  struct ssh_conn *sshc = &conn->proto.sshc;
+  CURLcode result = CURLE_OK;
+  bool block; /* we store the status and use that to provide a ssh_getsock()
+                 implementation */
+  do {
+    result = wssh_statemach_act(conn, &block);
+    *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
+    /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then
+       try again */
+    if(*done) {
+      fprintf(stderr, "%s says DONE\n", __func__);
+    }
+  } while(!result && !*done && !block);
+
+  return result;
+}
+
+static
+CURLcode wscp_perform(struct connectdata *conn,
+                      bool *connected,
+                      bool *dophase_done)
+{
+  (void)conn;
+  (void)connected;
+  (void)dophase_done;
+  return CURLE_OK;
+}
+
+static
+CURLcode wsftp_perform(struct connectdata *conn,
+                       bool *connected,
+                       bool *dophase_done)
+{
+  CURLcode result = CURLE_OK;
+
+  DEBUGF(infof(conn->data, "DO phase starts\n"));
+
+  *dophase_done = FALSE; /* not done yet */
+
+  /* start the first command in the DO phase */
+  state(conn, SSH_SFTP_QUOTE_INIT);
+
+  /* run the state-machine */
+  result = wssh_multi_statemach(conn, dophase_done);
+
+  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
+
+  if(*dophase_done) {
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+  }
+
+  return result;
+}
+
+/*
+ * The DO function is generic for both protocols.
+ */
+static CURLcode wssh_do(struct connectdata *conn, bool *done)
+{
+  CURLcode result;
+  bool connected = 0;
+  struct Curl_easy *data = conn->data;
+  struct ssh_conn *sshc = &conn->proto.sshc;
+
+  *done = FALSE; /* default to false */
+  data->req.size = -1; /* make sure this is unknown at this point */
+  sshc->actualcode = CURLE_OK; /* reset error code */
+  sshc->secondCreateDirs = 0;   /* reset the create dir attempt state
+                                   variable */
+
+  Curl_pgrsSetUploadCounter(data, 0);
+  Curl_pgrsSetDownloadCounter(data, 0);
+  Curl_pgrsSetUploadSize(data, -1);
+  Curl_pgrsSetDownloadSize(data, -1);
+
+  if(conn->handler->protocol & CURLPROTO_SCP)
+    result = wscp_perform(conn, &connected,  done);
+  else
+    result = wsftp_perform(conn, &connected,  done);
+
+  return result;
+}
+
+static CURLcode wscp_done(struct connectdata *conn,
+                         CURLcode code, bool premature)
+{
+  CURLcode result = CURLE_OK;
+  (void)conn;
+  (void)code;
+  (void)premature;
+
+  return result;
+}
+
+static CURLcode wscp_doing(struct connectdata *conn,
+                          bool *dophase_done)
+{
+  CURLcode result = CURLE_OK;
+  (void)conn;
+  (void)dophase_done;
+
+  return result;
+}
+
+static CURLcode wscp_disconnect(struct connectdata *conn, bool dead_connection)
+{
+  CURLcode result = CURLE_OK;
+  (void)conn;
+  (void)dead_connection;
+
+  return result;
+}
+
+static CURLcode wsftp_done(struct connectdata *conn,
+                          CURLcode code, bool premature)
+{
+  CURLcode result = CURLE_OK;
+  (void)conn;
+  (void)code;
+  (void)premature;
+
+  return result;
+}
+
+static CURLcode wsftp_doing(struct connectdata *conn,
+                           bool *dophase_done)
+{
+  CURLcode result = wssh_multi_statemach(conn, dophase_done);
+
+  if(*dophase_done) {
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+  }
+  return result;
+}
+
+static CURLcode wsftp_disconnect(struct connectdata *conn, bool dead)
+{
+  CURLcode result = CURLE_OK;
+  (void)conn;
+  (void)dead;
+
+  return result;
+}
+
+static int wssh_getsock(struct connectdata *conn,
+                       curl_socket_t *sock, /* points to numsocks number
+                                               of sockets */
+                       int numsocks)
+{
+  CURLcode result = CURLE_OK;
+  (void)conn;
+  (void)sock;
+  (void)numsocks;
+
+  return result;
+}
+
+static int wssh_perform_getsock(const struct connectdata *conn,
+                               curl_socket_t *sock, /* points to numsocks
+                                                       number of sockets */
+                               int numsocks)
+{
+  CURLcode result = CURLE_OK;
+  (void)conn;
+  (void)sock;
+  (void)numsocks;
+
+  return result;
+}
+
+#endif /* USE_WOLFSSH */
diff --git a/lib/wolfssh.h b/lib/wolfssh.h
new file mode 100644
index 0000000000..bef34102a5
--- /dev/null
+++ b/lib/wolfssh.h
@@ -0,0 +1,28 @@
+#ifndef HEADER_CURL_WOLFSSH_H
+#define HEADER_CURL_WOLFSSH_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+extern const struct Curl_handler Curl_handler_scp;
+extern const struct Curl_handler Curl_handler_sftp;
+
+#endif /* HEADER_CURL_WOLFSSH_H */
-- 
GitLab