VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/vssh/wolfssh.c@ 106754

Last change on this file since 106754 was 104083, checked in by vboxsync, 14 months ago

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette