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