1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2019 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
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.
13 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23#include "curl_setup.h"
24
25#ifdef USE_WOLFSSH
26
27#include <limits.h>
28
29#include <wolfssh/ssh.h>
30#include <wolfssh/wolfsftp.h>
31#include "urldata.h"
32#include "connect.h"
33#include "sendf.h"
34#include "progress.h"
35#include "curl_path.h"
36#include "strtoofft.h"
37#include "transfer.h"
38#include "speedcheck.h"
39#include "select.h"
40#include "multiif.h"
41#include "warnless.h"
42
43/* The last 3 #include files should be in this order */
44#include "curl_printf.h"
45#include "curl_memory.h"
46#include "memdebug.h"
47
48static CURLcode wssh_connect(struct Curl_easy *data, bool *done);
49static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done);
50static CURLcode wssh_do(struct Curl_easy *data, bool *done);
51#if 0
52static CURLcode wscp_done(struct Curl_easy *data,
53 CURLcode, bool premature);
54static CURLcode wscp_doing(struct Curl_easy *data,
55 bool *dophase_done);
56static CURLcode wscp_disconnect(struct Curl_easy *data,
57 struct connectdata *conn,
58 bool dead_connection);
59#endif
60static CURLcode wsftp_done(struct Curl_easy *data,
61 CURLcode, bool premature);
62static CURLcode wsftp_doing(struct Curl_easy *data,
63 bool *dophase_done);
64static CURLcode wsftp_disconnect(struct Curl_easy *data,
65 struct connectdata *conn,
66 bool dead);
67static int wssh_getsock(struct Curl_easy *data,
68 struct connectdata *conn,
69 curl_socket_t *sock);
70static CURLcode wssh_setup_connection(struct Curl_easy *data,
71 struct connectdata *conn);
72
73#if 0
74/*
75 * SCP protocol handler.
76 */
77
78const struct Curl_handler Curl_handler_scp = {
79 "SCP", /* scheme */
80 wssh_setup_connection, /* setup_connection */
81 wssh_do, /* do_it */
82 wscp_done, /* done */
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 */
99};
100
101#endif
102
103/*
104 * SFTP protocol handler.
105 */
106
107const struct Curl_handler Curl_handler_sftp = {
108 "SFTP", /* scheme */
109 wssh_setup_connection, /* setup_connection */
110 wssh_do, /* do_it */
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 */
129};
130
131/*
132 * SSH State machine related code
133 */
134/* This is the ONLY way to change SSH state! */
135static void state(struct Curl_easy *data, sshstate nowstate)
136{
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[] = {
142 "SSH_STOP",
143 "SSH_INIT",
144 "SSH_S_STARTUP",
145 "SSH_HOSTKEY",
146 "SSH_AUTHLIST",
147 "SSH_AUTH_PKEY_INIT",
148 "SSH_AUTH_PKEY",
149 "SSH_AUTH_PASS_INIT",
150 "SSH_AUTH_PASS",
151 "SSH_AUTH_AGENT_INIT",
152 "SSH_AUTH_AGENT_LIST",
153 "SSH_AUTH_AGENT",
154 "SSH_AUTH_HOST_INIT",
155 "SSH_AUTH_HOST",
156 "SSH_AUTH_KEY_INIT",
157 "SSH_AUTH_KEY",
158 "SSH_AUTH_GSSAPI",
159 "SSH_AUTH_DONE",
160 "SSH_SFTP_INIT",
161 "SSH_SFTP_REALPATH",
162 "SSH_SFTP_QUOTE_INIT",
163 "SSH_SFTP_POSTQUOTE_INIT",
164 "SSH_SFTP_QUOTE",
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",
174 "SSH_SFTP_GETINFO",
175 "SSH_SFTP_FILETIME",
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",
182 "SSH_SFTP_READDIR",
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",
188 "SSH_SFTP_CLOSE",
189 "SSH_SFTP_SHUTDOWN",
190 "SSH_SCP_TRANS_INIT",
191 "SSH_SCP_UPLOAD_INIT",
192 "SSH_SCP_DOWNLOAD_INIT",
193 "SSH_SCP_DOWNLOAD",
194 "SSH_SCP_DONE",
195 "SSH_SCP_SEND_EOF",
196 "SSH_SCP_WAIT_EOF",
197 "SSH_SCP_WAIT_CLOSE",
198 "SSH_SCP_CHANNEL_FREE",
199 "SSH_SESSION_DISCONNECT",
200 "SSH_SESSION_FREE",
201 "QUIT"
202 };
203
204 /* a precaution to make sure the lists are in sync */
205 DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
206
207 if(sshc->state != nowstate) {
208 infof(data, "wolfssh %p state change from %s to %s",
209 (void *)sshc, names[sshc->state], names[nowstate]);
210 }
211#endif
212
213 sshc->state = nowstate;
214}
215
216static ssize_t wscp_send(struct Curl_easy *data, int sockindex,
217 const void *mem, size_t len, CURLcode *err)
218{
219 ssize_t nwrite = 0;
220 (void)data;
221 (void)sockindex; /* we only support SCP on the fixed known primary socket */
222 (void)mem;
223 (void)len;
224 (void)err;
225
226 return nwrite;
227}
228
229static ssize_t wscp_recv(struct Curl_easy *data, int sockindex,
230 char *mem, size_t len, CURLcode *err)
231{
232 ssize_t nread = 0;
233 (void)data;
234 (void)sockindex; /* we only support SCP on the fixed known primary socket */
235 (void)mem;
236 (void)len;
237 (void)err;
238
239 return nread;
240}
241
242/* return number of sent bytes */
243static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
244 const void *mem, size_t len, CURLcode *err)
245{
246 struct connectdata *conn = data->conn;
247 struct ssh_conn *sshc = &conn->proto.sshc;
248 word32 offset[2];
249 int rc;
250 (void)sockindex;
251
252 offset[0] = (word32)sshc->offset&0xFFFFFFFF;
253 offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
254
255 rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle,
256 sshc->handleSz,
257 &offset[0],
258 (byte *)mem, (word32)len);
259
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;
264 *err = CURLE_AGAIN;
265 return -1;
266 }
267 else if(rc == WS_WANT_WRITE) {
268 conn->waitfor = KEEP_SEND;
269 *err = CURLE_AGAIN;
270 return -1;
271 }
272 if(rc < 0) {
273 failf(data, "wolfSSH_SFTP_SendWritePacket returned %d", rc);
274 return -1;
275 }
276 DEBUGASSERT(rc == (int)len);
277 infof(data, "sent %zd bytes SFTP from offset %zd",
278 len, sshc->offset);
279 sshc->offset += len;
280 return (ssize_t)rc;
281}
282
283/*
284 * Return number of received (decrypted) bytes
285 * or <0 on error
286 */
287static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
288 char *mem, size_t len, CURLcode *err)
289{
290 int rc;
291 struct connectdata *conn = data->conn;
292 struct ssh_conn *sshc = &conn->proto.sshc;
293 word32 offset[2];
294 (void)sockindex;
295
296 offset[0] = (word32)sshc->offset&0xFFFFFFFF;
297 offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
298
299 rc = wolfSSH_SFTP_SendReadPacket(sshc->ssh_session, sshc->handle,
300 sshc->handleSz,
301 &offset[0],
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;
307 *err = CURLE_AGAIN;
308 return -1;
309 }
310 else if(rc == WS_WANT_WRITE) {
311 conn->waitfor = KEEP_SEND;
312 *err = CURLE_AGAIN;
313 return -1;
314 }
315
316 DEBUGASSERT(rc <= (int)len);
317
318 if(rc < 0) {
319 failf(data, "wolfSSH_SFTP_SendReadPacket returned %d", rc);
320 return -1;
321 }
322 sshc->offset += len;
323
324 return (ssize_t)rc;
325}
326
327/*
328 * SSH setup and connection
329 */
330static CURLcode wssh_setup_connection(struct Curl_easy *data,
331 struct connectdata *conn)
332{
333 struct SSHPROTO *ssh;
334 (void)conn;
335
336 data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
337 if(!ssh)
338 return CURLE_OUT_OF_MEMORY;
339
340 return CURLE_OK;
341}
342
343static Curl_recv wscp_recv, wsftp_recv;
344static Curl_send wscp_send, wsftp_send;
345
346static int userauth(byte authtype,
347 WS_UserAuthData* authdata,
348 void *ctx)
349{
350 struct Curl_easy *data = ctx;
351 DEBUGF(infof(data, "wolfssh callback: type %s",
352 authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" :
353 "PUBLICCKEY"));
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);
357 }
358
359 return 0;
360}
361
362static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
363{
364 struct connectdata *conn = data->conn;
365 struct ssh_conn *sshc;
366 curl_socket_t sock = conn->sock[FIRSTSOCKET];
367 int rc;
368
369 /* initialize per-handle data if not already */
370 if(!data->req.p.ssh)
371 wssh_setup_connection(data, conn);
372
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");
376
377 if(conn->handler->protocol & CURLPROTO_SCP) {
378 conn->recv[FIRSTSOCKET] = wscp_recv;
379 conn->send[FIRSTSOCKET] = wscp_send;
380 }
381 else {
382 conn->recv[FIRSTSOCKET] = wsftp_recv;
383 conn->send[FIRSTSOCKET] = wsftp_send;
384 }
385 sshc = &conn->proto.sshc;
386 sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
387 if(!sshc->ctx) {
388 failf(data, "No wolfSSH context");
389 goto error;
390 }
391
392 sshc->ssh_session = wolfSSH_new(sshc->ctx);
393 if(!sshc->ssh_session) {
394 failf(data, "No wolfSSH session");
395 goto error;
396 }
397
398 rc = wolfSSH_SetUsername(sshc->ssh_session, conn->user);
399 if(rc != WS_SUCCESS) {
400 failf(data, "wolfSSH failed to set user name");
401 goto error;
402 }
403
404 /* set callback for authentication */
405 wolfSSH_SetUserAuth(sshc->ctx, userauth);
406 wolfSSH_SetUserAuthCtx(sshc->ssh_session, data);
407
408 rc = wolfSSH_set_fd(sshc->ssh_session, (int)sock);
409 if(rc) {
410 failf(data, "wolfSSH failed to set socket");
411 goto error;
412 }
413
414#if 0
415 wolfSSH_Debugging_ON();
416#endif
417
418 *done = TRUE;
419 if(conn->handler->protocol & CURLPROTO_SCP)
420 state(data, SSH_INIT);
421 else
422 state(data, SSH_SFTP_INIT);
423
424 return wssh_multi_statemach(data, done);
425 error:
426 wolfSSH_free(sshc->ssh_session);
427 wolfSSH_CTX_free(sshc->ctx);
428 return CURLE_FAILED_INIT;
429}
430
431/*
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
436 */
437
438static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
439{
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;
444 WS_SFTPNAME *name;
445 int rc = 0;
446 *block = FALSE; /* we're not blocking by default */
447
448 do {
449 switch(sshc->state) {
450 case SSH_INIT:
451 state(data, SSH_S_STARTUP);
452 /* FALLTHROUGH */
453 case SSH_S_STARTUP:
454 rc = wolfSSH_connect(sshc->ssh_session);
455 if(rc != WS_SUCCESS)
456 rc = wolfSSH_get_error(sshc->ssh_session);
457 if(rc == WS_WANT_READ) {
458 *block = TRUE;
459 conn->waitfor = KEEP_RECV;
460 return CURLE_OK;
461 }
462 else if(rc == WS_WANT_WRITE) {
463 *block = TRUE;
464 conn->waitfor = KEEP_SEND;
465 return CURLE_OK;
466 }
467 else if(rc != WS_SUCCESS) {
468 state(data, SSH_STOP);
469 return CURLE_SSH;
470 }
471 infof(data, "wolfssh connected!");
472 state(data, SSH_STOP);
473 break;
474 case SSH_STOP:
475 break;
476
477 case SSH_SFTP_INIT:
478 rc = wolfSSH_SFTP_connect(sshc->ssh_session);
479 if(rc != WS_SUCCESS)
480 rc = wolfSSH_get_error(sshc->ssh_session);
481 if(rc == WS_WANT_READ) {
482 *block = TRUE;
483 conn->waitfor = KEEP_RECV;
484 return CURLE_OK;
485 }
486 else if(rc == WS_WANT_WRITE) {
487 *block = TRUE;
488 conn->waitfor = KEEP_SEND;
489 return CURLE_OK;
490 }
491 else if(rc == WS_SUCCESS) {
492 infof(data, "wolfssh SFTP connected!");
493 state(data, SSH_SFTP_REALPATH);
494 }
495 else {
496 failf(data, "wolfssh SFTP connect error %d", rc);
497 return CURLE_SSH;
498 }
499 break;
500 case SSH_SFTP_REALPATH:
501 name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)".");
502 rc = wolfSSH_get_error(sshc->ssh_session);
503 if(rc == WS_WANT_READ) {
504 *block = TRUE;
505 conn->waitfor = KEEP_RECV;
506 return CURLE_OK;
507 }
508 else if(rc == WS_WANT_WRITE) {
509 *block = TRUE;
510 conn->waitfor = KEEP_SEND;
511 return CURLE_OK;
512 }
513 else if(name && (rc == WS_SUCCESS)) {
514 sshc->homedir = malloc(name->fSz + 1);
515 if(!sshc->homedir) {
516 sshc->actualcode = CURLE_OUT_OF_MEMORY;
517 }
518 else {
519 memcpy(sshc->homedir, name->fName, name->fSz);
520 sshc->homedir[name->fSz] = 0;
521 infof(data, "wolfssh SFTP realpath succeeded!");
522 }
523 wolfSSH_SFTPNAME_list_free(name);
524 state(data, SSH_STOP);
525 return CURLE_OK;
526 }
527 failf(data, "wolfssh SFTP realpath %d", rc);
528 return CURLE_SSH;
529
530 case SSH_SFTP_QUOTE_INIT:
531 result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path);
532 if(result) {
533 sshc->actualcode = result;
534 state(data, SSH_STOP);
535 break;
536 }
537
538 if(data->set.quote) {
539 infof(data, "Sending quote commands");
540 sshc->quote_item = data->set.quote;
541 state(data, SSH_SFTP_QUOTE);
542 }
543 else {
544 state(data, SSH_SFTP_GETINFO);
545 }
546 break;
547 case SSH_SFTP_GETINFO:
548 if(data->set.get_filetime) {
549 state(data, SSH_SFTP_FILETIME);
550 }
551 else {
552 state(data, SSH_SFTP_TRANS_INIT);
553 }
554 break;
555 case SSH_SFTP_TRANS_INIT:
556 if(data->set.upload)
557 state(data, SSH_SFTP_UPLOAD_INIT);
558 else {
559 if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
560 state(data, SSH_SFTP_READDIR_INIT);
561 else
562 state(data, SSH_SFTP_DOWNLOAD_INIT);
563 }
564 break;
565 case SSH_SFTP_UPLOAD_INIT: {
566 word32 flags;
567 WS_SFTP_FILEATRB createattrs;
568 if(data->state.resume_from) {
569 WS_SFTP_FILEATRB attrs;
570 if(data->state.resume_from < 0) {
571 rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path,
572 &attrs);
573 if(rc != WS_SUCCESS)
574 break;
575
576 if(rc) {
577 data->state.resume_from = 0;
578 }
579 else {
580 curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0];
581 if(size < 0) {
582 failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
583 return CURLE_BAD_DOWNLOAD_RESUME;
584 }
585 data->state.resume_from = size;
586 }
587 }
588 }
589
590 if(data->set.remote_append)
591 /* Try to open for append, but create if nonexisting */
592 flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND;
593 else if(data->state.resume_from > 0)
594 /* If we have restart position then open for append */
595 flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND;
596 else
597 /* Clear file before writing (normal behavior) */
598 flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC;
599
600 memset(&createattrs, 0, sizeof(createattrs));
601 createattrs.per = (word32)data->set.new_file_perms;
602 sshc->handleSz = sizeof(sshc->handle);
603 rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
604 flags, &createattrs,
605 sshc->handle, &sshc->handleSz);
606 if(rc == WS_FATAL_ERROR)
607 rc = wolfSSH_get_error(sshc->ssh_session);
608 if(rc == WS_WANT_READ) {
609 *block = TRUE;
610 conn->waitfor = KEEP_RECV;
611 return CURLE_OK;
612 }
613 else if(rc == WS_WANT_WRITE) {
614 *block = TRUE;
615 conn->waitfor = KEEP_SEND;
616 return CURLE_OK;
617 }
618 else if(rc == WS_SUCCESS) {
619 infof(data, "wolfssh SFTP open succeeded!");
620 }
621 else {
622 failf(data, "wolfssh SFTP upload open failed: %d", rc);
623 return CURLE_SSH;
624 }
625 state(data, SSH_SFTP_DOWNLOAD_STAT);
626
627 /* If we have a restart point then we need to seek to the correct
628 position. */
629 if(data->state.resume_from > 0) {
630 /* Let's read off the proper amount of bytes from the input. */
631 int seekerr = CURL_SEEKFUNC_OK;
632 if(conn->seek_func) {
633 Curl_set_in_callback(data, true);
634 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
635 SEEK_SET);
636 Curl_set_in_callback(data, false);
637 }
638
639 if(seekerr != CURL_SEEKFUNC_OK) {
640 curl_off_t passed = 0;
641
642 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
643 failf(data, "Could not seek stream");
644 return CURLE_FTP_COULDNT_USE_REST;
645 }
646 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
647 do {
648 size_t readthisamountnow =
649 (data->state.resume_from - passed > data->set.buffer_size) ?
650 (size_t)data->set.buffer_size :
651 curlx_sotouz(data->state.resume_from - passed);
652
653 size_t actuallyread;
654 Curl_set_in_callback(data, true);
655 actuallyread = data->state.fread_func(data->state.buffer, 1,
656 readthisamountnow,
657 data->state.in);
658 Curl_set_in_callback(data, false);
659
660 passed += actuallyread;
661 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
662 /* this checks for greater-than only to make sure that the
663 CURL_READFUNC_ABORT return code still aborts */
664 failf(data, "Failed to read data");
665 return CURLE_FTP_COULDNT_USE_REST;
666 }
667 } while(passed < data->state.resume_from);
668 }
669
670 /* now, decrease the size of the read */
671 if(data->state.infilesize > 0) {
672 data->state.infilesize -= data->state.resume_from;
673 data->req.size = data->state.infilesize;
674 Curl_pgrsSetUploadSize(data, data->state.infilesize);
675 }
676
677 sshc->offset += data->state.resume_from;
678 }
679 if(data->state.infilesize > 0) {
680 data->req.size = data->state.infilesize;
681 Curl_pgrsSetUploadSize(data, data->state.infilesize);
682 }
683 /* upload data */
684 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
685
686 /* not set by Curl_setup_transfer to preserve keepon bits */
687 conn->sockfd = conn->writesockfd;
688
689 if(result) {
690 state(data, SSH_SFTP_CLOSE);
691 sshc->actualcode = result;
692 }
693 else {
694 /* store this original bitmask setup to use later on if we can't
695 figure out a "real" bitmask */
696 sshc->orig_waitfor = data->req.keepon;
697
698 /* we want to use the _sending_ function even when the socket turns
699 out readable as the underlying libssh2 sftp send function will deal
700 with both accordingly */
701 conn->cselect_bits = CURL_CSELECT_OUT;
702
703 /* since we don't really wait for anything at this point, we want the
704 state machine to move on as soon as possible so we set a very short
705 timeout here */
706 Curl_expire(data, 0, EXPIRE_RUN_NOW);
707
708 state(data, SSH_STOP);
709 }
710 break;
711 }
712 case SSH_SFTP_DOWNLOAD_INIT:
713 sshc->handleSz = sizeof(sshc->handle);
714 rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
715 WOLFSSH_FXF_READ, NULL,
716 sshc->handle, &sshc->handleSz);
717 if(rc == WS_FATAL_ERROR)
718 rc = wolfSSH_get_error(sshc->ssh_session);
719 if(rc == WS_WANT_READ) {
720 *block = TRUE;
721 conn->waitfor = KEEP_RECV;
722 return CURLE_OK;
723 }
724 else if(rc == WS_WANT_WRITE) {
725 *block = TRUE;
726 conn->waitfor = KEEP_SEND;
727 return CURLE_OK;
728 }
729 else if(rc == WS_SUCCESS) {
730 infof(data, "wolfssh SFTP open succeeded!");
731 state(data, SSH_SFTP_DOWNLOAD_STAT);
732 return CURLE_OK;
733 }
734
735 failf(data, "wolfssh SFTP open failed: %d", rc);
736 return CURLE_SSH;
737
738 case SSH_SFTP_DOWNLOAD_STAT: {
739 WS_SFTP_FILEATRB attrs;
740 curl_off_t size;
741
742 rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, &attrs);
743 if(rc == WS_FATAL_ERROR)
744 rc = wolfSSH_get_error(sshc->ssh_session);
745 if(rc == WS_WANT_READ) {
746 *block = TRUE;
747 conn->waitfor = KEEP_RECV;
748 return CURLE_OK;
749 }
750 else if(rc == WS_WANT_WRITE) {
751 *block = TRUE;
752 conn->waitfor = KEEP_SEND;
753 return CURLE_OK;
754 }
755 else if(rc == WS_SUCCESS) {
756 infof(data, "wolfssh STAT succeeded!");
757 }
758 else {
759 failf(data, "wolfssh SFTP open failed: %d", rc);
760 data->req.size = -1;
761 data->req.maxdownload = -1;
762 Curl_pgrsSetDownloadSize(data, -1);
763 return CURLE_SSH;
764 }
765
766 size = ((curl_off_t)attrs.sz[1] <<32) | attrs.sz[0];
767
768 data->req.size = size;
769 data->req.maxdownload = size;
770 Curl_pgrsSetDownloadSize(data, size);
771
772 infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size);
773
774 /* We cannot seek with wolfSSH so resuming and range requests are not
775 possible */
776 if(data->state.use_range || data->state.resume_from) {
777 infof(data, "wolfSSH cannot do range/seek on SFTP");
778 return CURLE_BAD_DOWNLOAD_RESUME;
779 }
780
781 /* Setup the actual download */
782 if(data->req.size == 0) {
783 /* no data to transfer */
784 Curl_setup_transfer(data, -1, -1, FALSE, -1);
785 infof(data, "File already completely downloaded");
786 state(data, SSH_STOP);
787 break;
788 }
789 Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
790
791 /* not set by Curl_setup_transfer to preserve keepon bits */
792 conn->writesockfd = conn->sockfd;
793
794 /* we want to use the _receiving_ function even when the socket turns
795 out writableable as the underlying libssh2 recv function will deal
796 with both accordingly */
797 conn->cselect_bits = CURL_CSELECT_IN;
798
799 if(result) {
800 /* this should never occur; the close state should be entered
801 at the time the error occurs */
802 state(data, SSH_SFTP_CLOSE);
803 sshc->actualcode = result;
804 }
805 else {
806 state(data, SSH_STOP);
807 }
808 break;
809 }
810 case SSH_SFTP_CLOSE:
811 if(sshc->handleSz)
812 rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle,
813 sshc->handleSz);
814 else
815 rc = WS_SUCCESS; /* directory listing */
816 if(rc == WS_WANT_READ) {
817 *block = TRUE;
818 conn->waitfor = KEEP_RECV;
819 return CURLE_OK;
820 }
821 else if(rc == WS_WANT_WRITE) {
822 *block = TRUE;
823 conn->waitfor = KEEP_SEND;
824 return CURLE_OK;
825 }
826 else if(rc == WS_SUCCESS) {
827 state(data, SSH_STOP);
828 return CURLE_OK;
829 }
830
831 failf(data, "wolfssh SFTP CLOSE failed: %d", rc);
832 return CURLE_SSH;
833
834 case SSH_SFTP_READDIR_INIT:
835 Curl_pgrsSetDownloadSize(data, -1);
836 if(data->set.opt_no_body) {
837 state(data, SSH_STOP);
838 break;
839 }
840 state(data, SSH_SFTP_READDIR);
841 /* FALLTHROUGH */
842 case SSH_SFTP_READDIR:
843 name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path);
844 if(!name)
845 rc = wolfSSH_get_error(sshc->ssh_session);
846 else
847 rc = WS_SUCCESS;
848
849 if(rc == WS_WANT_READ) {
850 *block = TRUE;
851 conn->waitfor = KEEP_RECV;
852 return CURLE_OK;
853 }
854 else if(rc == WS_WANT_WRITE) {
855 *block = TRUE;
856 conn->waitfor = KEEP_SEND;
857 return CURLE_OK;
858 }
859 else if(name && (rc == WS_SUCCESS)) {
860 WS_SFTPNAME *origname = name;
861 result = CURLE_OK;
862 while(name) {
863 char *line = aprintf("%s\n",
864 data->set.list_only ?
865 name->fName : name->lName);
866 if(!line) {
867 state(data, SSH_SFTP_CLOSE);
868 sshc->actualcode = CURLE_OUT_OF_MEMORY;
869 break;
870 }
871 result = Curl_client_write(data, CLIENTWRITE_BODY,
872 line, strlen(line));
873 free(line);
874 if(result) {
875 sshc->actualcode = result;
876 break;
877 }
878 name = name->next;
879 }
880 wolfSSH_SFTPNAME_list_free(origname);
881 state(data, SSH_STOP);
882 return result;
883 }
884 failf(data, "wolfssh SFTP ls failed: %d", rc);
885 return CURLE_SSH;
886
887 case SSH_SFTP_SHUTDOWN:
888 Curl_safefree(sshc->homedir);
889 wolfSSH_free(sshc->ssh_session);
890 wolfSSH_CTX_free(sshc->ctx);
891 state(data, SSH_STOP);
892 return CURLE_OK;
893 default:
894 break;
895 }
896 } while(!rc && (sshc->state != SSH_STOP));
897 return result;
898}
899
900/* called repeatedly until done from multi.c */
901static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done)
902{
903 struct connectdata *conn = data->conn;
904 struct ssh_conn *sshc = &conn->proto.sshc;
905 CURLcode result = CURLE_OK;
906 bool block; /* we store the status and use that to provide a ssh_getsock()
907 implementation */
908 do {
909 result = wssh_statemach_act(data, &block);
910 *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
911 /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then
912 try again */
913 if(*done) {
914 DEBUGF(infof(data, "wssh_statemach_act says DONE"));
915 }
916 } while(!result && !*done && !block);
917
918 return result;
919}
920
921static
922CURLcode wscp_perform(struct Curl_easy *data,
923 bool *connected,
924 bool *dophase_done)
925{
926 (void)data;
927 (void)connected;
928 (void)dophase_done;
929 return CURLE_OK;
930}
931
932static
933CURLcode wsftp_perform(struct Curl_easy *data,
934 bool *connected,
935 bool *dophase_done)
936{
937 CURLcode result = CURLE_OK;
938 struct connectdata *conn = data->conn;
939
940 DEBUGF(infof(data, "DO phase starts"));
941
942 *dophase_done = FALSE; /* not done yet */
943
944 /* start the first command in the DO phase */
945 state(data, SSH_SFTP_QUOTE_INIT);
946
947 /* run the state-machine */
948 result = wssh_multi_statemach(data, dophase_done);
949
950 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
951
952 if(*dophase_done) {
953 DEBUGF(infof(data, "DO phase is complete"));
954 }
955
956 return result;
957}
958
959/*
960 * The DO function is generic for both protocols.
961 */
962static CURLcode wssh_do(struct Curl_easy *data, bool *done)
963{
964 CURLcode result;
965 bool connected = 0;
966 struct connectdata *conn = data->conn;
967 struct ssh_conn *sshc = &conn->proto.sshc;
968
969 *done = FALSE; /* default to false */
970 data->req.size = -1; /* make sure this is unknown at this point */
971 sshc->actualcode = CURLE_OK; /* reset error code */
972 sshc->secondCreateDirs = 0; /* reset the create dir attempt state
973 variable */
974
975 Curl_pgrsSetUploadCounter(data, 0);
976 Curl_pgrsSetDownloadCounter(data, 0);
977 Curl_pgrsSetUploadSize(data, -1);
978 Curl_pgrsSetDownloadSize(data, -1);
979
980 if(conn->handler->protocol & CURLPROTO_SCP)
981 result = wscp_perform(data, &connected, done);
982 else
983 result = wsftp_perform(data, &connected, done);
984
985 return result;
986}
987
988static CURLcode wssh_block_statemach(struct Curl_easy *data,
989 bool disconnect)
990{
991 struct connectdata *conn = data->conn;
992 struct ssh_conn *sshc = &conn->proto.sshc;
993 CURLcode result = CURLE_OK;
994
995 while((sshc->state != SSH_STOP) && !result) {
996 bool block;
997 timediff_t left = 1000;
998 struct curltime now = Curl_now();
999
1000 result = wssh_statemach_act(data, &block);
1001 if(result)
1002 break;
1003
1004 if(!disconnect) {
1005 if(Curl_pgrsUpdate(data))
1006 return CURLE_ABORTED_BY_CALLBACK;
1007
1008 result = Curl_speedcheck(data, now);
1009 if(result)
1010 break;
1011
1012 left = Curl_timeleft(data, NULL, FALSE);
1013 if(left < 0) {
1014 failf(data, "Operation timed out");
1015 return CURLE_OPERATION_TIMEDOUT;
1016 }
1017 }
1018
1019 if(!result) {
1020 int dir = conn->waitfor;
1021 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1022 curl_socket_t fd_read = CURL_SOCKET_BAD;
1023 curl_socket_t fd_write = CURL_SOCKET_BAD;
1024 if(dir == KEEP_RECV)
1025 fd_read = sock;
1026 else if(dir == KEEP_SEND)
1027 fd_write = sock;
1028
1029 /* wait for the socket to become ready */
1030 (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write,
1031 left>1000?1000:left); /* ignore result */
1032 }
1033 }
1034
1035 return result;
1036}
1037
1038/* generic done function for both SCP and SFTP called from their specific
1039 done functions */
1040static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
1041{
1042 CURLcode result = CURLE_OK;
1043 struct SSHPROTO *sftp_scp = data->req.p.ssh;
1044
1045 if(!status) {
1046 /* run the state-machine */
1047 result = wssh_block_statemach(data, FALSE);
1048 }
1049 else
1050 result = status;
1051
1052 if(sftp_scp)
1053 Curl_safefree(sftp_scp->path);
1054 if(Curl_pgrsDone(data))
1055 return CURLE_ABORTED_BY_CALLBACK;
1056
1057 data->req.keepon = 0; /* clear all bits */
1058 return result;
1059}
1060
1061#if 0
1062static CURLcode wscp_done(struct Curl_easy *data,
1063 CURLcode code, bool premature)
1064{
1065 CURLcode result = CURLE_OK;
1066 (void)conn;
1067 (void)code;
1068 (void)premature;
1069
1070 return result;
1071}
1072
1073static CURLcode wscp_doing(struct Curl_easy *data,
1074 bool *dophase_done)
1075{
1076 CURLcode result = CURLE_OK;
1077 (void)conn;
1078 (void)dophase_done;
1079
1080 return result;
1081}
1082
1083static CURLcode wscp_disconnect(struct Curl_easy *data,
1084 struct connectdata *conn, bool dead_connection)
1085{
1086 CURLcode result = CURLE_OK;
1087 (void)data;
1088 (void)conn;
1089 (void)dead_connection;
1090
1091 return result;
1092}
1093#endif
1094
1095static CURLcode wsftp_done(struct Curl_easy *data,
1096 CURLcode code, bool premature)
1097{
1098 (void)premature;
1099 state(data, SSH_SFTP_CLOSE);
1100
1101 return wssh_done(data, code);
1102}
1103
1104static CURLcode wsftp_doing(struct Curl_easy *data,
1105 bool *dophase_done)
1106{
1107 CURLcode result = wssh_multi_statemach(data, dophase_done);
1108
1109 if(*dophase_done) {
1110 DEBUGF(infof(data, "DO phase is complete"));
1111 }
1112 return result;
1113}
1114
1115static CURLcode wsftp_disconnect(struct Curl_easy *data,
1116 struct connectdata *conn,
1117 bool dead)
1118{
1119 CURLcode result = CURLE_OK;
1120 (void)dead;
1121
1122 DEBUGF(infof(data, "SSH DISCONNECT starts now"));
1123
1124 if(conn->proto.sshc.ssh_session) {
1125 /* only if there's a session still around to use! */
1126 state(data, SSH_SFTP_SHUTDOWN);
1127 result = wssh_block_statemach(data, TRUE);
1128 }
1129
1130 DEBUGF(infof(data, "SSH DISCONNECT is done"));
1131 return result;
1132}
1133
1134static int wssh_getsock(struct Curl_easy *data,
1135 struct connectdata *conn,
1136 curl_socket_t *sock)
1137{
1138 int bitmap = GETSOCK_BLANK;
1139 int dir = conn->waitfor;
1140 (void)data;
1141 sock[0] = conn->sock[FIRSTSOCKET];
1142
1143 if(dir == KEEP_RECV)
1144 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
1145 else if(dir == KEEP_SEND)
1146 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
1147
1148 return bitmap;
1149}
1150
1151void Curl_ssh_version(char *buffer, size_t buflen)
1152{
1153 (void)msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING);
1154}
1155
1156CURLcode Curl_ssh_init(void)
1157{
1158 if(WS_SUCCESS != wolfSSH_Init()) {
1159 DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n"));
1160 return CURLE_FAILED_INIT;
1161 }
1162
1163 return CURLE_OK;
1164}
1165void Curl_ssh_cleanup(void)
1166{
1167}
1168
1169#endif /* USE_WOLFSSH */
1170