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 | |
48 | static CURLcode wssh_connect(struct Curl_easy *data, bool *done); |
49 | static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done); |
50 | static CURLcode wssh_do(struct Curl_easy *data, bool *done); |
51 | #if 0 |
52 | static CURLcode wscp_done(struct Curl_easy *data, |
53 | CURLcode, bool premature); |
54 | static CURLcode wscp_doing(struct Curl_easy *data, |
55 | bool *dophase_done); |
56 | static CURLcode wscp_disconnect(struct Curl_easy *data, |
57 | struct connectdata *conn, |
58 | bool dead_connection); |
59 | #endif |
60 | static CURLcode wsftp_done(struct Curl_easy *data, |
61 | CURLcode, bool premature); |
62 | static CURLcode wsftp_doing(struct Curl_easy *data, |
63 | bool *dophase_done); |
64 | static CURLcode wsftp_disconnect(struct Curl_easy *data, |
65 | struct connectdata *conn, |
66 | bool dead); |
67 | static int wssh_getsock(struct Curl_easy *data, |
68 | struct connectdata *conn, |
69 | curl_socket_t *sock); |
70 | static CURLcode wssh_setup_connection(struct Curl_easy *data, |
71 | struct connectdata *conn); |
72 | |
73 | #if 0 |
74 | /* |
75 | * SCP protocol handler. |
76 | */ |
77 | |
78 | const 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 | |
107 | const 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! */ |
135 | static 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 | |
216 | static 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 | |
229 | static 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 */ |
243 | static 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 | */ |
287 | static 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 | */ |
330 | static 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 | |
343 | static Curl_recv wscp_recv, wsftp_recv; |
344 | static Curl_send wscp_send, wsftp_send; |
345 | |
346 | static 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 | |
362 | static 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 | |
438 | static 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 */ |
901 | static 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 | |
921 | static |
922 | CURLcode 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 | |
932 | static |
933 | CURLcode 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 | */ |
962 | static 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 | |
988 | static 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 */ |
1040 | static 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 |
1062 | static 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 | |
1073 | static 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 | |
1083 | static 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 | |
1095 | static 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 | |
1104 | static 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 | |
1115 | static 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 | |
1134 | static 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 | |
1151 | void Curl_ssh_version(char *buffer, size_t buflen) |
1152 | { |
1153 | (void)msnprintf(buffer, buflen, "wolfssh/%s" , LIBWOLFSSH_VERSION_STRING); |
1154 | } |
1155 | |
1156 | CURLcode 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 | } |
1165 | void Curl_ssh_cleanup(void) |
1166 | { |
1167 | } |
1168 | |
1169 | #endif /* USE_WOLFSSH */ |
1170 | |