1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
29 #include <wolfssh/ssh.h>
30 #include <wolfssh/wolfsftp.h>
35 #include "curl_path.h"
36 #include "strtoofft.h"
38 #include "speedcheck.h"
43 /* The last 3 #include files should be in this order */
44 #include "curl_printf.h"
45 #include "curl_memory.h"
48 static CURLcode wssh_connect(struct Curl_easy *data, bool *done);
49 static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done);
50 static CURLcode wssh_do(struct Curl_easy *data, bool *done);
52 static CURLcode wscp_done(struct Curl_easy *data,
53 CURLcode, bool premature);
54 static CURLcode wscp_doing(struct Curl_easy *data,
56 static CURLcode wscp_disconnect(struct Curl_easy *data,
57 struct connectdata *conn,
58 bool dead_connection);
60 static CURLcode wsftp_done(struct Curl_easy *data,
61 CURLcode, bool premature);
62 static CURLcode wsftp_doing(struct Curl_easy *data,
64 static CURLcode wsftp_disconnect(struct Curl_easy *data,
65 struct connectdata *conn,
67 static int wssh_getsock(struct Curl_easy *data,
68 struct connectdata *conn,
70 static CURLcode wssh_setup_connection(struct Curl_easy *data,
71 struct connectdata *conn);
75 * SCP protocol handler.
78 const struct Curl_handler Curl_handler_scp = {
80 wssh_setup_connection, /* setup_connection */
83 ZERO_NULL, /* do_more */
84 wssh_connect, /* connect_it */
85 wssh_multi_statemach, /* connecting */
86 wscp_doing, /* doing */
87 wssh_getsock, /* proto_getsock */
88 wssh_getsock, /* doing_getsock */
89 ZERO_NULL, /* domore_getsock */
90 wssh_getsock, /* perform_getsock */
91 wscp_disconnect, /* disconnect */
92 ZERO_NULL, /* readwrite */
93 ZERO_NULL, /* connection_check */
94 ZERO_NULL, /* attach connection */
95 PORT_SSH, /* defport */
96 CURLPROTO_SCP, /* protocol */
97 PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
98 | PROTOPT_NOURLQUERY /* flags */
104 * SFTP protocol handler.
107 const struct Curl_handler Curl_handler_sftp = {
109 wssh_setup_connection, /* setup_connection */
111 wsftp_done, /* done */
112 ZERO_NULL, /* do_more */
113 wssh_connect, /* connect_it */
114 wssh_multi_statemach, /* connecting */
115 wsftp_doing, /* doing */
116 wssh_getsock, /* proto_getsock */
117 wssh_getsock, /* doing_getsock */
118 ZERO_NULL, /* domore_getsock */
119 wssh_getsock, /* perform_getsock */
120 wsftp_disconnect, /* disconnect */
121 ZERO_NULL, /* readwrite */
122 ZERO_NULL, /* connection_check */
123 ZERO_NULL, /* attach connection */
124 PORT_SSH, /* defport */
125 CURLPROTO_SFTP, /* protocol */
126 CURLPROTO_SFTP, /* family */
127 PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
128 | PROTOPT_NOURLQUERY /* flags */
132 * SSH State machine related code
134 /* This is the ONLY way to change SSH state! */
135 static void state(struct Curl_easy *data, sshstate nowstate)
137 struct connectdata *conn = data->conn;
138 struct ssh_conn *sshc = &conn->proto.sshc;
139 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
140 /* for debug purposes */
141 static const char * const names[] = {
147 "SSH_AUTH_PKEY_INIT",
149 "SSH_AUTH_PASS_INIT",
151 "SSH_AUTH_AGENT_INIT",
152 "SSH_AUTH_AGENT_LIST",
154 "SSH_AUTH_HOST_INIT",
162 "SSH_SFTP_QUOTE_INIT",
163 "SSH_SFTP_POSTQUOTE_INIT",
165 "SSH_SFTP_NEXT_QUOTE",
166 "SSH_SFTP_QUOTE_STAT",
167 "SSH_SFTP_QUOTE_SETSTAT",
168 "SSH_SFTP_QUOTE_SYMLINK",
169 "SSH_SFTP_QUOTE_MKDIR",
170 "SSH_SFTP_QUOTE_RENAME",
171 "SSH_SFTP_QUOTE_RMDIR",
172 "SSH_SFTP_QUOTE_UNLINK",
173 "SSH_SFTP_QUOTE_STATVFS",
176 "SSH_SFTP_TRANS_INIT",
177 "SSH_SFTP_UPLOAD_INIT",
178 "SSH_SFTP_CREATE_DIRS_INIT",
179 "SSH_SFTP_CREATE_DIRS",
180 "SSH_SFTP_CREATE_DIRS_MKDIR",
181 "SSH_SFTP_READDIR_INIT",
183 "SSH_SFTP_READDIR_LINK",
184 "SSH_SFTP_READDIR_BOTTOM",
185 "SSH_SFTP_READDIR_DONE",
186 "SSH_SFTP_DOWNLOAD_INIT",
187 "SSH_SFTP_DOWNLOAD_STAT",
190 "SSH_SCP_TRANS_INIT",
191 "SSH_SCP_UPLOAD_INIT",
192 "SSH_SCP_DOWNLOAD_INIT",
197 "SSH_SCP_WAIT_CLOSE",
198 "SSH_SCP_CHANNEL_FREE",
199 "SSH_SESSION_DISCONNECT",
204 /* a precaution to make sure the lists are in sync */
205 DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
207 if(sshc->state != nowstate) {
208 infof(data, "wolfssh %p state change from %s to %s",
209 (void *)sshc, names[sshc->state], names[nowstate]);
213 sshc->state = nowstate;
216 static ssize_t wscp_send(struct Curl_easy *data, int sockindex,
217 const void *mem, size_t len, CURLcode *err)
221 (void)sockindex; /* we only support SCP on the fixed known primary socket */
229 static ssize_t wscp_recv(struct Curl_easy *data, int sockindex,
230 char *mem, size_t len, CURLcode *err)
234 (void)sockindex; /* we only support SCP on the fixed known primary socket */
242 /* return number of sent bytes */
243 static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
244 const void *mem, size_t len, CURLcode *err)
246 struct connectdata *conn = data->conn;
247 struct ssh_conn *sshc = &conn->proto.sshc;
252 offset[0] = (word32)sshc->offset&0xFFFFFFFF;
253 offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
255 rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle,
258 (byte *)mem, (word32)len);
260 if(rc == WS_FATAL_ERROR)
261 rc = wolfSSH_get_error(sshc->ssh_session);
262 if(rc == WS_WANT_READ) {
263 conn->waitfor = KEEP_RECV;
267 else if(rc == WS_WANT_WRITE) {
268 conn->waitfor = KEEP_SEND;
273 failf(data, "wolfSSH_SFTP_SendWritePacket returned %d", rc);
276 DEBUGASSERT(rc == (int)len);
277 infof(data, "sent %zd bytes SFTP from offset %zd",
284 * Return number of received (decrypted) bytes
287 static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
288 char *mem, size_t len, CURLcode *err)
291 struct connectdata *conn = data->conn;
292 struct ssh_conn *sshc = &conn->proto.sshc;
296 offset[0] = (word32)sshc->offset&0xFFFFFFFF;
297 offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
299 rc = wolfSSH_SFTP_SendReadPacket(sshc->ssh_session, sshc->handle,
302 (byte *)mem, (word32)len);
303 if(rc == WS_FATAL_ERROR)
304 rc = wolfSSH_get_error(sshc->ssh_session);
305 if(rc == WS_WANT_READ) {
306 conn->waitfor = KEEP_RECV;
310 else if(rc == WS_WANT_WRITE) {
311 conn->waitfor = KEEP_SEND;
316 DEBUGASSERT(rc <= (int)len);
319 failf(data, "wolfSSH_SFTP_SendReadPacket returned %d", rc);
328 * SSH setup and connection
330 static CURLcode wssh_setup_connection(struct Curl_easy *data,
331 struct connectdata *conn)
333 struct SSHPROTO *ssh;
336 data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
338 return CURLE_OUT_OF_MEMORY;
343 static Curl_recv wscp_recv, wsftp_recv;
344 static Curl_send wscp_send, wsftp_send;
346 static int userauth(byte authtype,
347 WS_UserAuthData* authdata,
350 struct Curl_easy *data = ctx;
351 DEBUGF(infof(data, "wolfssh callback: type %s",
352 authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" :
354 if(authtype == WOLFSSH_USERAUTH_PASSWORD) {
355 authdata->sf.password.password = (byte *)data->conn->passwd;
356 authdata->sf.password.passwordSz = (word32) strlen(data->conn->passwd);
362 static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
364 struct connectdata *conn = data->conn;
365 struct ssh_conn *sshc;
366 curl_socket_t sock = conn->sock[FIRSTSOCKET];
369 /* initialize per-handle data if not already */
371 wssh_setup_connection(data, conn);
373 /* We default to persistent connections. We set this already in this connect
374 function to make the re-use checks properly be able to check this bit. */
375 connkeep(conn, "SSH default");
377 if(conn->handler->protocol & CURLPROTO_SCP) {
378 conn->recv[FIRSTSOCKET] = wscp_recv;
379 conn->send[FIRSTSOCKET] = wscp_send;
382 conn->recv[FIRSTSOCKET] = wsftp_recv;
383 conn->send[FIRSTSOCKET] = wsftp_send;
385 sshc = &conn->proto.sshc;
386 sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
388 failf(data, "No wolfSSH context");
392 sshc->ssh_session = wolfSSH_new(sshc->ctx);
393 if(!sshc->ssh_session) {
394 failf(data, "No wolfSSH session");
398 rc = wolfSSH_SetUsername(sshc->ssh_session, conn->user);
399 if(rc != WS_SUCCESS) {
400 failf(data, "wolfSSH failed to set user name");
404 /* set callback for authentication */
405 wolfSSH_SetUserAuth(sshc->ctx, userauth);
406 wolfSSH_SetUserAuthCtx(sshc->ssh_session, data);
408 rc = wolfSSH_set_fd(sshc->ssh_session, (int)sock);
410 failf(data, "wolfSSH failed to set socket");
415 wolfSSH_Debugging_ON();
419 if(conn->handler->protocol & CURLPROTO_SCP)
420 state(data, SSH_INIT);
422 state(data, SSH_SFTP_INIT);
424 return wssh_multi_statemach(data, done);
426 wolfSSH_free(sshc->ssh_session);
427 wolfSSH_CTX_free(sshc->ctx);
428 return CURLE_FAILED_INIT;
432 * wssh_statemach_act() runs the SSH state machine as far as it can without
433 * blocking and without reaching the end. The data the pointer 'block' points
434 * to will be set to TRUE if the wolfssh function returns EAGAIN meaning it
435 * wants to be called again when the socket is ready
438 static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
440 CURLcode result = CURLE_OK;
441 struct connectdata *conn = data->conn;
442 struct ssh_conn *sshc = &conn->proto.sshc;
443 struct SSHPROTO *sftp_scp = data->req.p.ssh;
446 *block = FALSE; /* we're not blocking by default */
449 switch(sshc->state) {
451 state(data, SSH_S_STARTUP);
455 rc = wolfSSH_connect(sshc->ssh_session);
457 rc = wolfSSH_get_error(sshc->ssh_session);
458 if(rc == WS_WANT_READ) {
460 conn->waitfor = KEEP_RECV;
463 else if(rc == WS_WANT_WRITE) {
465 conn->waitfor = KEEP_SEND;
468 else if(rc != WS_SUCCESS) {
469 state(data, SSH_STOP);
472 infof(data, "wolfssh connected");
473 state(data, SSH_STOP);
479 rc = wolfSSH_SFTP_connect(sshc->ssh_session);
481 rc = wolfSSH_get_error(sshc->ssh_session);
482 if(rc == WS_WANT_READ) {
484 conn->waitfor = KEEP_RECV;
487 else if(rc == WS_WANT_WRITE) {
489 conn->waitfor = KEEP_SEND;
492 else if(rc == WS_SUCCESS) {
493 infof(data, "wolfssh SFTP connected");
494 state(data, SSH_SFTP_REALPATH);
497 failf(data, "wolfssh SFTP connect error %d", rc);
501 case SSH_SFTP_REALPATH:
502 name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)".");
503 rc = wolfSSH_get_error(sshc->ssh_session);
504 if(rc == WS_WANT_READ) {
506 conn->waitfor = KEEP_RECV;
509 else if(rc == WS_WANT_WRITE) {
511 conn->waitfor = KEEP_SEND;
514 else if(name && (rc == WS_SUCCESS)) {
515 sshc->homedir = malloc(name->fSz + 1);
517 sshc->actualcode = CURLE_OUT_OF_MEMORY;
520 memcpy(sshc->homedir, name->fName, name->fSz);
521 sshc->homedir[name->fSz] = 0;
522 infof(data, "wolfssh SFTP realpath succeeded");
524 wolfSSH_SFTPNAME_list_free(name);
525 state(data, SSH_STOP);
528 failf(data, "wolfssh SFTP realpath %d", rc);
531 case SSH_SFTP_QUOTE_INIT:
532 result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path);
534 sshc->actualcode = result;
535 state(data, SSH_STOP);
539 if(data->set.quote) {
540 infof(data, "Sending quote commands");
541 sshc->quote_item = data->set.quote;
542 state(data, SSH_SFTP_QUOTE);
545 state(data, SSH_SFTP_GETINFO);
548 case SSH_SFTP_GETINFO:
549 if(data->set.get_filetime) {
550 state(data, SSH_SFTP_FILETIME);
553 state(data, SSH_SFTP_TRANS_INIT);
556 case SSH_SFTP_TRANS_INIT:
558 state(data, SSH_SFTP_UPLOAD_INIT);
560 if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
561 state(data, SSH_SFTP_READDIR_INIT);
563 state(data, SSH_SFTP_DOWNLOAD_INIT);
566 case SSH_SFTP_UPLOAD_INIT: {
568 WS_SFTP_FILEATRB createattrs;
569 if(data->state.resume_from) {
570 WS_SFTP_FILEATRB attrs;
571 if(data->state.resume_from < 0) {
572 rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path,
578 data->state.resume_from = 0;
581 curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0];
583 failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
584 return CURLE_BAD_DOWNLOAD_RESUME;
586 data->state.resume_from = size;
591 if(data->set.remote_append)
592 /* Try to open for append, but create if nonexisting */
593 flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND;
594 else if(data->state.resume_from > 0)
595 /* If we have restart position then open for append */
596 flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND;
598 /* Clear file before writing (normal behavior) */
599 flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC;
601 memset(&createattrs, 0, sizeof(createattrs));
602 createattrs.per = (word32)data->set.new_file_perms;
603 sshc->handleSz = sizeof(sshc->handle);
604 rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
606 sshc->handle, &sshc->handleSz);
607 if(rc == WS_FATAL_ERROR)
608 rc = wolfSSH_get_error(sshc->ssh_session);
609 if(rc == WS_WANT_READ) {
611 conn->waitfor = KEEP_RECV;
614 else if(rc == WS_WANT_WRITE) {
616 conn->waitfor = KEEP_SEND;
619 else if(rc == WS_SUCCESS) {
620 infof(data, "wolfssh SFTP open succeeded");
623 failf(data, "wolfssh SFTP upload open failed: %d", rc);
626 state(data, SSH_SFTP_DOWNLOAD_STAT);
628 /* If we have a restart point then we need to seek to the correct
630 if(data->state.resume_from > 0) {
631 /* Let's read off the proper amount of bytes from the input. */
632 int seekerr = CURL_SEEKFUNC_OK;
633 if(conn->seek_func) {
634 Curl_set_in_callback(data, true);
635 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
637 Curl_set_in_callback(data, false);
640 if(seekerr != CURL_SEEKFUNC_OK) {
641 curl_off_t passed = 0;
643 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
644 failf(data, "Could not seek stream");
645 return CURLE_FTP_COULDNT_USE_REST;
647 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
649 size_t readthisamountnow =
650 (data->state.resume_from - passed > data->set.buffer_size) ?
651 (size_t)data->set.buffer_size :
652 curlx_sotouz(data->state.resume_from - passed);
655 Curl_set_in_callback(data, true);
656 actuallyread = data->state.fread_func(data->state.buffer, 1,
659 Curl_set_in_callback(data, false);
661 passed += actuallyread;
662 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
663 /* this checks for greater-than only to make sure that the
664 CURL_READFUNC_ABORT return code still aborts */
665 failf(data, "Failed to read data");
666 return CURLE_FTP_COULDNT_USE_REST;
668 } while(passed < data->state.resume_from);
671 /* now, decrease the size of the read */
672 if(data->state.infilesize > 0) {
673 data->state.infilesize -= data->state.resume_from;
674 data->req.size = data->state.infilesize;
675 Curl_pgrsSetUploadSize(data, data->state.infilesize);
678 sshc->offset += data->state.resume_from;
680 if(data->state.infilesize > 0) {
681 data->req.size = data->state.infilesize;
682 Curl_pgrsSetUploadSize(data, data->state.infilesize);
685 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
687 /* not set by Curl_setup_transfer to preserve keepon bits */
688 conn->sockfd = conn->writesockfd;
691 state(data, SSH_SFTP_CLOSE);
692 sshc->actualcode = result;
695 /* store this original bitmask setup to use later on if we can't
696 figure out a "real" bitmask */
697 sshc->orig_waitfor = data->req.keepon;
699 /* we want to use the _sending_ function even when the socket turns
700 out readable as the underlying libssh2 sftp send function will deal
701 with both accordingly */
702 conn->cselect_bits = CURL_CSELECT_OUT;
704 /* since we don't really wait for anything at this point, we want the
705 state machine to move on as soon as possible so we set a very short
707 Curl_expire(data, 0, EXPIRE_RUN_NOW);
709 state(data, SSH_STOP);
713 case SSH_SFTP_DOWNLOAD_INIT:
714 sshc->handleSz = sizeof(sshc->handle);
715 rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
716 WOLFSSH_FXF_READ, NULL,
717 sshc->handle, &sshc->handleSz);
718 if(rc == WS_FATAL_ERROR)
719 rc = wolfSSH_get_error(sshc->ssh_session);
720 if(rc == WS_WANT_READ) {
722 conn->waitfor = KEEP_RECV;
725 else if(rc == WS_WANT_WRITE) {
727 conn->waitfor = KEEP_SEND;
730 else if(rc == WS_SUCCESS) {
731 infof(data, "wolfssh SFTP open succeeded");
732 state(data, SSH_SFTP_DOWNLOAD_STAT);
736 failf(data, "wolfssh SFTP open failed: %d", rc);
739 case SSH_SFTP_DOWNLOAD_STAT: {
740 WS_SFTP_FILEATRB attrs;
743 rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, &attrs);
744 if(rc == WS_FATAL_ERROR)
745 rc = wolfSSH_get_error(sshc->ssh_session);
746 if(rc == WS_WANT_READ) {
748 conn->waitfor = KEEP_RECV;
751 else if(rc == WS_WANT_WRITE) {
753 conn->waitfor = KEEP_SEND;
756 else if(rc == WS_SUCCESS) {
757 infof(data, "wolfssh STAT succeeded");
760 failf(data, "wolfssh SFTP open failed: %d", rc);
762 data->req.maxdownload = -1;
763 Curl_pgrsSetDownloadSize(data, -1);
767 size = ((curl_off_t)attrs.sz[1] <<32) | attrs.sz[0];
769 data->req.size = size;
770 data->req.maxdownload = size;
771 Curl_pgrsSetDownloadSize(data, size);
773 infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size);
775 /* We cannot seek with wolfSSH so resuming and range requests are not
777 if(data->state.use_range || data->state.resume_from) {
778 infof(data, "wolfSSH cannot do range/seek on SFTP");
779 return CURLE_BAD_DOWNLOAD_RESUME;
782 /* Setup the actual download */
783 if(data->req.size == 0) {
784 /* no data to transfer */
785 Curl_setup_transfer(data, -1, -1, FALSE, -1);
786 infof(data, "File already completely downloaded");
787 state(data, SSH_STOP);
790 Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
792 /* not set by Curl_setup_transfer to preserve keepon bits */
793 conn->writesockfd = conn->sockfd;
795 /* we want to use the _receiving_ function even when the socket turns
796 out writableable as the underlying libssh2 recv function will deal
797 with both accordingly */
798 conn->cselect_bits = CURL_CSELECT_IN;
801 /* this should never occur; the close state should be entered
802 at the time the error occurs */
803 state(data, SSH_SFTP_CLOSE);
804 sshc->actualcode = result;
807 state(data, SSH_STOP);
813 rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle,
816 rc = WS_SUCCESS; /* directory listing */
817 if(rc == WS_WANT_READ) {
819 conn->waitfor = KEEP_RECV;
822 else if(rc == WS_WANT_WRITE) {
824 conn->waitfor = KEEP_SEND;
827 else if(rc == WS_SUCCESS) {
828 state(data, SSH_STOP);
832 failf(data, "wolfssh SFTP CLOSE failed: %d", rc);
835 case SSH_SFTP_READDIR_INIT:
836 Curl_pgrsSetDownloadSize(data, -1);
837 if(data->set.opt_no_body) {
838 state(data, SSH_STOP);
841 state(data, SSH_SFTP_READDIR);
844 case SSH_SFTP_READDIR:
845 name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path);
847 rc = wolfSSH_get_error(sshc->ssh_session);
851 if(rc == WS_WANT_READ) {
853 conn->waitfor = KEEP_RECV;
856 else if(rc == WS_WANT_WRITE) {
858 conn->waitfor = KEEP_SEND;
861 else if(name && (rc == WS_SUCCESS)) {
862 WS_SFTPNAME *origname = name;
865 char *line = aprintf("%s\n",
866 data->set.list_only ?
867 name->fName : name->lName);
869 state(data, SSH_SFTP_CLOSE);
870 sshc->actualcode = CURLE_OUT_OF_MEMORY;
873 result = Curl_client_write(data, CLIENTWRITE_BODY,
877 sshc->actualcode = result;
882 wolfSSH_SFTPNAME_list_free(origname);
883 state(data, SSH_STOP);
886 failf(data, "wolfssh SFTP ls failed: %d", rc);
889 case SSH_SFTP_SHUTDOWN:
890 Curl_safefree(sshc->homedir);
891 wolfSSH_free(sshc->ssh_session);
892 wolfSSH_CTX_free(sshc->ctx);
893 state(data, SSH_STOP);
898 } while(!rc && (sshc->state != SSH_STOP));
902 /* called repeatedly until done from multi.c */
903 static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done)
905 struct connectdata *conn = data->conn;
906 struct ssh_conn *sshc = &conn->proto.sshc;
907 CURLcode result = CURLE_OK;
908 bool block; /* we store the status and use that to provide a ssh_getsock()
911 result = wssh_statemach_act(data, &block);
912 *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
913 /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then
916 DEBUGF(infof(data, "wssh_statemach_act says DONE"));
918 } while(!result && !*done && !block);
924 CURLcode wscp_perform(struct Curl_easy *data,
935 CURLcode wsftp_perform(struct Curl_easy *data,
939 CURLcode result = CURLE_OK;
940 struct connectdata *conn = data->conn;
942 DEBUGF(infof(data, "DO phase starts"));
944 *dophase_done = FALSE; /* not done yet */
946 /* start the first command in the DO phase */
947 state(data, SSH_SFTP_QUOTE_INIT);
949 /* run the state-machine */
950 result = wssh_multi_statemach(data, dophase_done);
952 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
955 DEBUGF(infof(data, "DO phase is complete"));
962 * The DO function is generic for both protocols.
964 static CURLcode wssh_do(struct Curl_easy *data, bool *done)
968 struct connectdata *conn = data->conn;
969 struct ssh_conn *sshc = &conn->proto.sshc;
971 *done = FALSE; /* default to false */
972 data->req.size = -1; /* make sure this is unknown at this point */
973 sshc->actualcode = CURLE_OK; /* reset error code */
974 sshc->secondCreateDirs = 0; /* reset the create dir attempt state
977 Curl_pgrsSetUploadCounter(data, 0);
978 Curl_pgrsSetDownloadCounter(data, 0);
979 Curl_pgrsSetUploadSize(data, -1);
980 Curl_pgrsSetDownloadSize(data, -1);
982 if(conn->handler->protocol & CURLPROTO_SCP)
983 result = wscp_perform(data, &connected, done);
985 result = wsftp_perform(data, &connected, done);
990 static CURLcode wssh_block_statemach(struct Curl_easy *data,
993 struct connectdata *conn = data->conn;
994 struct ssh_conn *sshc = &conn->proto.sshc;
995 CURLcode result = CURLE_OK;
997 while((sshc->state != SSH_STOP) && !result) {
999 timediff_t left = 1000;
1000 struct curltime now = Curl_now();
1002 result = wssh_statemach_act(data, &block);
1007 if(Curl_pgrsUpdate(data))
1008 return CURLE_ABORTED_BY_CALLBACK;
1010 result = Curl_speedcheck(data, now);
1014 left = Curl_timeleft(data, NULL, FALSE);
1016 failf(data, "Operation timed out");
1017 return CURLE_OPERATION_TIMEDOUT;
1022 int dir = conn->waitfor;
1023 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1024 curl_socket_t fd_read = CURL_SOCKET_BAD;
1025 curl_socket_t fd_write = CURL_SOCKET_BAD;
1026 if(dir == KEEP_RECV)
1028 else if(dir == KEEP_SEND)
1031 /* wait for the socket to become ready */
1032 (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write,
1033 left>1000?1000:left); /* ignore result */
1040 /* generic done function for both SCP and SFTP called from their specific
1042 static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
1044 CURLcode result = CURLE_OK;
1045 struct SSHPROTO *sftp_scp = data->req.p.ssh;
1048 /* run the state-machine */
1049 result = wssh_block_statemach(data, FALSE);
1055 Curl_safefree(sftp_scp->path);
1056 if(Curl_pgrsDone(data))
1057 return CURLE_ABORTED_BY_CALLBACK;
1059 data->req.keepon = 0; /* clear all bits */
1064 static CURLcode wscp_done(struct Curl_easy *data,
1065 CURLcode code, bool premature)
1067 CURLcode result = CURLE_OK;
1075 static CURLcode wscp_doing(struct Curl_easy *data,
1078 CURLcode result = CURLE_OK;
1085 static CURLcode wscp_disconnect(struct Curl_easy *data,
1086 struct connectdata *conn, bool dead_connection)
1088 CURLcode result = CURLE_OK;
1091 (void)dead_connection;
1097 static CURLcode wsftp_done(struct Curl_easy *data,
1098 CURLcode code, bool premature)
1101 state(data, SSH_SFTP_CLOSE);
1103 return wssh_done(data, code);
1106 static CURLcode wsftp_doing(struct Curl_easy *data,
1109 CURLcode result = wssh_multi_statemach(data, dophase_done);
1112 DEBUGF(infof(data, "DO phase is complete"));
1117 static CURLcode wsftp_disconnect(struct Curl_easy *data,
1118 struct connectdata *conn,
1121 CURLcode result = CURLE_OK;
1124 DEBUGF(infof(data, "SSH DISCONNECT starts now"));
1126 if(conn->proto.sshc.ssh_session) {
1127 /* only if there's a session still around to use! */
1128 state(data, SSH_SFTP_SHUTDOWN);
1129 result = wssh_block_statemach(data, TRUE);
1132 DEBUGF(infof(data, "SSH DISCONNECT is done"));
1136 static int wssh_getsock(struct Curl_easy *data,
1137 struct connectdata *conn,
1138 curl_socket_t *sock)
1140 int bitmap = GETSOCK_BLANK;
1141 int dir = conn->waitfor;
1143 sock[0] = conn->sock[FIRSTSOCKET];
1145 if(dir == KEEP_RECV)
1146 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
1147 else if(dir == KEEP_SEND)
1148 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
1153 void Curl_ssh_version(char *buffer, size_t buflen)
1155 (void)msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING);
1158 CURLcode Curl_ssh_init(void)
1160 if(WS_SUCCESS != wolfSSH_Init()) {
1161 DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n"));
1162 return CURLE_FAILED_INIT;
1167 void Curl_ssh_cleanup(void)
1171 #endif /* USE_WOLFSSH */