scp_client.c
Go to the documentation of this file.
1 /**
2  * @file scp_client.c
3  * @brief SCP client
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2019-2026 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneSSH Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.6.6
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SCP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ssh/ssh.h"
36 #include "ssh/ssh_connection.h"
37 #include "ssh/ssh_transport.h"
38 #include "ssh/ssh_request.h"
39 #include "scp/scp_client.h"
40 #include "scp/scp_client_misc.h"
41 #include "path.h"
42 #include "debug.h"
43 
44 //Check SSH stack configuration
45 #if (SCP_CLIENT_SUPPORT == ENABLED)
46 
47 
48 /**
49  * @brief Initialize SCP client context
50  * @param[in] context Pointer to the SCP client context
51  * @return Error code
52  **/
53 
55 {
56  //Make sure the SCP client context is valid
57  if(context == NULL)
59 
60  //Clear SCP client context
61  osMemset(context, 0, sizeof(ScpClientContext));
62 
63  //Attach TCP/IP stack context
64  context->netContext = netGetDefaultContext();
65 
66  //Initialize SCP client state
67  context->state = SCP_CLIENT_STATE_DISCONNECTED;
68  //Default timeout
69  context->timeout = SCP_CLIENT_DEFAULT_TIMEOUT;
70  //Initialize exit status
71  context->exitStatus = 0;
72 
73  //Successful processing
74  return NO_ERROR;
75 }
76 
77 
78 /**
79  * @brief Register SSH initialization callback function
80  * @param[in] context Pointer to the SCP client context
81  * @param[in] callback SSH initialization callback function
82  * @return Error code
83  **/
84 
86  ScpClientSshInitCallback callback)
87 {
88  //Check parameters
89  if(context == NULL || callback == NULL)
91 
92  //Save callback function
93  context->sshInitCallback = callback;
94 
95  //Successful processing
96  return NO_ERROR;
97 }
98 
99 
100 /**
101  * @brief Set communication timeout
102  * @param[in] context Pointer to the SCP client context
103  * @param[in] timeout Timeout value, in milliseconds
104  * @return Error code
105  **/
106 
108 {
109  //Make sure the SCP client context is valid
110  if(context == NULL)
112 
113  //Save timeout value
114  context->timeout = timeout;
115 
116  //Successful processing
117  return NO_ERROR;
118 }
119 
120 
121 /**
122  * @brief Bind the SCP client to a particular network interface
123  * @param[in] context Pointer to the SCP client context
124  * @param[in] interface Network interface to be used
125  * @return Error code
126  **/
127 
129  NetInterface *interface)
130 {
131  //Make sure the SCP client context is valid
132  if(context == NULL)
134 
135  //Explicitly associate the SCP client with the specified interface
136  context->interface = interface;
137 
138  //Successful processing
139  return NO_ERROR;
140 }
141 
142 
143 /**
144  * @brief Establish a connection with the specified SCP server
145  * @param[in] context Pointer to the SCP client context
146  * @param[in] serverIpAddr IP address of the SCP server to connect to
147  * @param[in] serverPort Port number
148  * @return Error code
149  **/
150 
152  const IpAddr *serverIpAddr, uint16_t serverPort)
153 {
154  error_t error;
155 
156  //Make sure the SCP client context is valid
157  if(context == NULL)
159 
160  //Initialize status code
161  error = NO_ERROR;
162 
163  //Establish connection with the SCP server
164  while(!error)
165  {
166  //Check current state
167  if(context->state == SCP_CLIENT_STATE_DISCONNECTED)
168  {
169  //Open network connection
170  error = scpClientOpenConnection(context);
171 
172  //Check status code
173  if(!error)
174  {
175  //Update SCP client state
177  }
178  }
179  else if(context->state == SCP_CLIENT_STATE_CONNECTING_1)
180  {
181  //Establish network connection
182  error = socketConnect(context->sshConnection.socket, serverIpAddr,
183  serverPort);
184 
185  //Check status code
186  if(error == NO_ERROR)
187  {
188  //Force the socket to operate in non-blocking mode
189  socketSetTimeout(context->sshConnection.socket, 0);
190 
191  //Update SCP client state
193  }
194  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
195  {
196  //Check whether the timeout has elapsed
197  error = scpClientCheckTimeout(context);
198  }
199  else
200  {
201  //Communication error
202  }
203  }
204  else if(context->state == SCP_CLIENT_STATE_CONNECTING_2)
205  {
206  //Establish SSH connection
207  error = scpClientEstablishConnection(context);
208  }
209  else if(context->state == SCP_CLIENT_STATE_CONNECTED)
210  {
211  //The SCP client is connected
212  break;
213  }
214  else
215  {
216  //Invalid state
217  error = ERROR_WRONG_STATE;
218  }
219  }
220 
221  //Failed to establish connection with the SCP server?
222  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
223  {
224  //Clean up side effects
225  scpClientCloseConnection(context);
226  //Update SCP client state
228  }
229 
230  //Return status code
231  return error;
232 }
233 
234 
235 /**
236  * @brief Open a file for writing
237  * @param[in] context Pointer to the SCP client context
238  * @param[in] path Path to the file to be be opened
239  * @param[in] mode File permissions
240  * @param[in] size Size of the file, in bytes
241  * @return Error code
242  **/
243 
245  const char_t *path, uint_t mode, uint64_t size)
246 {
247  error_t error;
248  ScpDirective directive;
249  SshConnection *connection;
250  SshChannel *channel;
251 
252  //Check parameters
253  if(context == NULL || path == NULL)
255 
256  //Initialize SCP directive
257  osMemset(&directive, 0, sizeof(ScpDirective));
258 
259  //Point to the SSH connection
260  connection = &context->sshConnection;
261  //Point to the SSH channel
262  channel = &context->sshChannel;
263 
264  //Initialize status code
265  error = NO_ERROR;
266 
267  //Open the specified file for writing
268  while(!error)
269  {
270  //Check the state of the SCP client
271  if(context->state == SCP_CLIENT_STATE_CONNECTED)
272  {
273  //Initialize exit status
274  context->exitStatus = 0;
275 
276  //Allocate a new SSH channel
277  channel = sshCreateChannel(connection);
278 
279  //Valid channel handle?
280  if(channel != NULL)
281  {
282  //Force the channel to operate in non-blocking mode
283  error = sshSetChannelTimeout(channel, 0);
284 
285  //Check status code
286  if(!error)
287  {
288  //The client sends an SSH_MSG_CHANNEL_OPEN message to the server
289  //in order to open a new channel
290  error = sshSendChannelOpen(channel, "session", NULL);
291  }
292 
293  //Check status code
294  if(!error)
295  {
296  //Update SCP client state
298  }
299  }
300  else
301  {
302  //Report an error
303  error = ERROR_OPEN_FAILED;
304  }
305  }
306  else if(context->state == SCP_CLIENT_STATE_CHANNEL_OPEN)
307  {
308  //Wait for server's response
309  error = scpClientProcessEvents(context);
310 
311  //Check status code
312  if(!error)
313  {
314  //Check the state of the channel
315  if(channel->state == SSH_CHANNEL_STATE_RESERVED)
316  {
317  //Continue processing
318  }
319  else if(channel->state == SSH_CHANNEL_STATE_OPEN)
320  {
321  //An SSH_MSG_CHANNEL_OPEN_CONFIRMATION message has been received
323  }
324  else if(channel->state == SSH_CHANNEL_STATE_CLOSED)
325  {
326  //Release SSH channel
327  sshDeleteChannel(&context->sshChannel);
328  //Update SCP client state
330  //An SSH_MSG_CHANNEL_OPEN_FAILURE message has been received
331  error = ERROR_OPEN_FAILED;
332  }
333  else
334  {
335  //Invalid state
336  error = ERROR_WRONG_STATE;
337  }
338  }
339  }
340  else if(context->state == SCP_CLIENT_STATE_CHANNEL_REQUEST)
341  {
342  SshExecParams requestParams;
343 
344  //Format SCP command line
345  sprintf(context->buffer, "scp -t %s", path);
346 
347  //Set "exec" request parameters
348  requestParams.command.value = context->buffer;
349  requestParams.command.length = osStrlen(context->buffer);
350 
351  //Send an SSH_MSG_CHANNEL_REQUEST message to the server
352  error = sshSendChannelRequest(channel, "exec", &requestParams, TRUE);
353 
354  //Check status code
355  if(!error)
356  {
357  //Update SCP client state
359  }
360  }
361  else if(context->state == SCP_CLIENT_STATE_CHANNEL_REPLY)
362  {
363  //Wait for server's response
364  error = scpClientProcessEvents(context);
365 
366  //Check status code
367  if(!error)
368  {
369  //Check the state of the channel request
370  if(channel->requestState == SSH_REQUEST_STATE_PENDING)
371  {
372  //Continue processing
373  }
374  else if(channel->requestState == SSH_REQUEST_STATE_SUCCESS)
375  {
376  //An SSH_MSG_CHANNEL_SUCCESS message has been received
378  }
379  else if(channel->requestState == SSH_REQUEST_STATE_FAILURE)
380  {
381  //An SSH_MSG_CHANNEL_FAILURE message has been received
383  }
384  else
385  {
386  //Invalid state
387  error = ERROR_WRONG_STATE;
388  }
389  }
390  }
391  else if(context->state == SCP_CLIENT_STATE_WRITE_INIT)
392  {
393  //Wait for a status directive from the SCP server
394  error = scpClientReceiveDirective(context, &directive);
395 
396  //Check status code
397  if(!error)
398  {
399  //Check directive opcode
400  if(directive.opcode == SCP_OPCODE_OK)
401  {
402  //A success directive has been received
404  }
405  else
406  {
407  //A warning or an error message has been received
409  }
410  }
411  }
412  else if(context->state == SCP_CLIENT_STATE_WRITE_COMMAND)
413  {
414  //The 'C' directive indicates the next file to be transferred
415  directive.opcode = SCP_OPCODE_FILE;
416  directive.filename = pathGetFilename(path);
417  directive.mode = mode;
418  directive.size = size;
419 
420  //Send the directive to the SCP server
421  error = scpClientSendDirective(context, &directive);
422 
423  //Check status code
424  if(!error)
425  {
426  //Save the size of the file
427  context->fileSize = size;
428  context->fileOffset = 0;
429 
430  //Update SCP client state
432  }
433  }
434  else if(context->state == SCP_CLIENT_STATE_WRITE_ACK)
435  {
436  //Wait for a status directive from the SCP server
437  error = scpClientReceiveDirective(context, &directive);
438 
439  //Check status code
440  if(!error)
441  {
442  //Check directive opcode
443  if(directive.opcode == SCP_OPCODE_OK)
444  {
445  //A success directive has been received
447  //We are done
448  break;
449  }
450  else
451  {
452  //A warning or an error message has been received
454  }
455  }
456  }
457  else if(context->state == SCP_CLIENT_STATE_CHANNEL_CLOSE)
458  {
459  //When either party wishes to terminate the channel, it sends an
460  //SSH_MSG_CHANNEL_CLOSE message
461  error = sshCloseChannel(&context->sshChannel);
462 
463  //Check status code
464  if(error == NO_ERROR)
465  {
466  //Wait for the SSH_MSG_CHANNEL_CLOSE message to be transmitted
467  if(context->sshConnection.txBufferLen > 0)
468  {
469  //Flush pending data
470  error = scpClientProcessEvents(context);
471  }
472  else
473  {
474  //Release SSH channel
475  sshDeleteChannel(&context->sshChannel);
476  //Update SCP client state
478  //Report an error
480  }
481  }
482  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
483  {
484  //Process SSH connection events
485  error = scpClientProcessEvents(context);
486  }
487  else
488  {
489  //Just for sanity
490  }
491  }
492  else
493  {
494  //Invalid state
495  error = ERROR_WRONG_STATE;
496  }
497  }
498 
499  //Return status code
500  return error;
501 }
502 
503 
504 /**
505  * @brief Open a file for reading
506  * @param[in] context Pointer to the SCP client context
507  * @param[in] path Path to the file to be be opened
508  * @param[out] size Size of the file, in bytes (optional parameter)
509  * @return Error code
510  **/
511 
513  const char_t *path, uint64_t *size)
514 {
515  error_t error;
516  ScpDirective directive;
517  SshConnection *connection;
518  SshChannel *channel;
519 
520  //Check parameters
521  if(context == NULL || path == NULL)
523 
524  //Initialize SCP directive
525  osMemset(&directive, 0, sizeof(ScpDirective));
526 
527  //Point to the SSH connection
528  connection = &context->sshConnection;
529  //Point to the SSH channel
530  channel = &context->sshChannel;
531 
532  //Initialize status code
533  error = NO_ERROR;
534 
535  //Open the specified file for reading
536  while(!error)
537  {
538  //Check the state of the SCP client
539  if(context->state == SCP_CLIENT_STATE_CONNECTED)
540  {
541  //Initialize exit status
542  context->exitStatus = 0;
543 
544  //Allocate a new SSH channel
545  channel = sshCreateChannel(connection);
546 
547  //Valid channel handle?
548  if(channel != NULL)
549  {
550  //Force the channel to operate in non-blocking mode
551  error = sshSetChannelTimeout(channel, 0);
552 
553  //Check status code
554  if(!error)
555  {
556  //The client sends an SSH_MSG_CHANNEL_OPEN message to the server
557  //in order to open a new channel
558  error = sshSendChannelOpen(channel, "session", NULL);
559  }
560 
561  //Check status code
562  if(!error)
563  {
564  //Update SCP client state
566  }
567  }
568  else
569  {
570  //Report an error
571  error = ERROR_OPEN_FAILED;
572  }
573  }
574  else if(context->state == SCP_CLIENT_STATE_CHANNEL_OPEN)
575  {
576  //Wait for server's response
577  error = scpClientProcessEvents(context);
578 
579  //Check status code
580  if(!error)
581  {
582  //Check the state of the channel
583  if(channel->state == SSH_CHANNEL_STATE_RESERVED)
584  {
585  //Continue processing
586  }
587  else if(channel->state == SSH_CHANNEL_STATE_OPEN)
588  {
589  //An SSH_MSG_CHANNEL_OPEN_CONFIRMATION message has been received
591  }
592  else if(channel->state == SSH_CHANNEL_STATE_CLOSED)
593  {
594  //Release SSH channel
595  sshDeleteChannel(&context->sshChannel);
596  //Update SCP client state
598  //An SSH_MSG_CHANNEL_OPEN_FAILURE message has been received
599  error = ERROR_OPEN_FAILED;
600  }
601  else
602  {
603  //Invalid state
604  error = ERROR_WRONG_STATE;
605  }
606  }
607  }
608  else if(context->state == SCP_CLIENT_STATE_CHANNEL_REQUEST)
609  {
610  SshExecParams requestParams;
611 
612  //Format SCP command line
613  osSprintf(context->buffer, "scp -f %s", path);
614 
615  //Set "exec" request parameters
616  requestParams.command.value = context->buffer;
617  requestParams.command.length = osStrlen(context->buffer);
618 
619  //Send an SSH_MSG_CHANNEL_REQUEST message to the server
620  error = sshSendChannelRequest(channel, "exec", &requestParams, TRUE);
621 
622  //Check status code
623  if(!error)
624  {
625  //Update SCP client state
627  }
628  }
629  else if(context->state == SCP_CLIENT_STATE_CHANNEL_REPLY)
630  {
631  //Wait for server's response
632  error = scpClientProcessEvents(context);
633 
634  //Check status code
635  if(!error)
636  {
637  //Check the state of the channel request
638  if(channel->requestState == SSH_REQUEST_STATE_PENDING)
639  {
640  //Continue processing
641  }
642  else if(channel->requestState == SSH_REQUEST_STATE_SUCCESS)
643  {
644  //An SSH_MSG_CHANNEL_SUCCESS message has been received
646  }
647  else if(channel->requestState == SSH_REQUEST_STATE_FAILURE)
648  {
649  //An SSH_MSG_CHANNEL_FAILURE message has been received
651  }
652  else
653  {
654  //Invalid state
655  error = ERROR_WRONG_STATE;
656  }
657  }
658  }
659  else if(context->state == SCP_CLIENT_STATE_READ_INIT)
660  {
661  //This status directive indicates a success
662  directive.opcode = SCP_OPCODE_OK;
663  //Send the directive to the SCP server
664  error = scpClientSendDirective(context, &directive);
665 
666  //Check status code
667  if(!error)
668  {
669  //Update SCP client state
671  }
672  }
673  else if(context->state == SCP_CLIENT_STATE_READ_COMMAND)
674  {
675  //Wait for a directive from the SCP server
676  error = scpClientReceiveDirective(context, &directive);
677 
678  //Check status code
679  if(!error)
680  {
681  //Check directive opcode
682  if(directive.opcode == SCP_OPCODE_FILE)
683  {
684  //Save the size of the file
685  context->fileSize = directive.size;
686  context->fileOffset = 0;
687 
688  //A valid directive has been received
690  }
691  else
692  {
693  //A warning or an error message has been received
695  }
696  }
697  }
698  else if(context->state == SCP_CLIENT_STATE_READ_ACK)
699  {
700  //This status directive indicates a success
701  directive.opcode = SCP_OPCODE_OK;
702  //Send the directive to the SCP server
703  error = scpClientSendDirective(context, &directive);
704 
705  //Check status code
706  if(!error)
707  {
708  //Update SCP client state
710  //We are done
711  break;
712  }
713  }
714  else if(context->state == SCP_CLIENT_STATE_CHANNEL_CLOSE)
715  {
716  //When either party wishes to terminate the channel, it sends an
717  //SSH_MSG_CHANNEL_CLOSE message
718  error = sshCloseChannel(&context->sshChannel);
719 
720  //Check status code
721  if(error == NO_ERROR)
722  {
723  //Wait for the SSH_MSG_CHANNEL_CLOSE message to be transmitted
724  if(context->sshConnection.txBufferLen > 0)
725  {
726  //Flush pending data
727  error = scpClientProcessEvents(context);
728  }
729  else
730  {
731  //Release SSH channel
732  sshDeleteChannel(&context->sshChannel);
733  //Update SCP client state
735  //Report an error
737  }
738  }
739  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
740  {
741  //Process SSH connection events
742  error = scpClientProcessEvents(context);
743  }
744  else
745  {
746  //Just for sanity
747  }
748  }
749  else
750  {
751  //Invalid state
752  error = ERROR_WRONG_STATE;
753  }
754  }
755 
756  //Check status code
757  if(!error)
758  {
759  //The parameter is optional
760  if(size != NULL)
761  {
762  //Return the size of the file, in bytes
763  *size = context->fileSize;
764  }
765  }
766 
767  //Return status code
768  return error;
769 }
770 
771 
772 /**
773  * @brief Write to a remote file
774  * @param[in] context Pointer to the SCP client context
775  * @param[in] data Pointer to a buffer containing the data to be written
776  * @param[in] length Number of data bytes to write
777  * @param[in] written Number of bytes that have been written (optional parameter)
778  * @param[in] flags Set of flags that influences the behavior of this function
779  * @return Error code
780  **/
781 
783  size_t length, size_t *written, uint_t flags)
784 {
785  error_t error;
786  uint64_t m;
787  size_t n;
788  size_t totalLength;
789 
790  //Make sure the SCP client context is valid
791  if(context == NULL)
793 
794  //Check parameters
795  if(data == NULL && length != 0)
797 
798  //Initialize status code
799  error = NO_ERROR;
800  //Actual number of bytes written
801  totalLength = 0;
802 
803  //Write as much data as possible
804  while(totalLength < length && !error)
805  {
806  //Check the state of the SCP client
807  if(context->state == SCP_CLIENT_STATE_WRITE_DATA)
808  {
809  //Check file offset
810  if(context->fileOffset < context->fileSize)
811  {
812  //Number of data bytes left to write
813  m = context->fileSize - context->fileOffset;
814  //Number of bytes available in the buffer
815  n = length - totalLength;
816 
817  //Limit the number of bytes to write at a time
818  if((uint64_t) n > m)
819  {
820  n = (size_t) m;
821  }
822 
823  //Send more data
824  error = sshWriteChannel(&context->sshChannel, data, n, &n, flags);
825 
826  //Check status code
827  if(error == NO_ERROR || error == ERROR_TIMEOUT)
828  {
829  //Any data transmitted?
830  if(n > 0)
831  {
832  //Advance data pointer
833  data = (uint8_t *) data + n;
834  totalLength += n;
835 
836  //Increment file offset
837  context->fileOffset += n;
838 
839  //Save current time
840  context->timestamp = osGetSystemTime();
841  }
842  }
843 
844  //Check status code
845  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
846  {
847  //Process SSH connection events
848  error = scpClientProcessEvents(context);
849  }
850  }
851  else
852  {
853  //Update SCP client state
855  }
856  }
857  else if(context->state == SCP_CLIENT_STATE_WRITE_STATUS)
858  {
859  //Any data left to write?
860  if(totalLength < length)
861  {
862  //The length of the data cannot exceed the value specified in
863  //the SCP directive
864  error = ERROR_INVALID_LENGTH;
865  }
866  else
867  {
868  //Successful write operation
869  error = NO_ERROR;
870  }
871 
872  //We are done
873  break;
874  }
875  else
876  {
877  //Invalid state
878  error = ERROR_WRONG_STATE;
879  }
880  }
881 
882  //The parameter is optional
883  if(written != NULL)
884  {
885  //Total number of data that have been written
886  *written = totalLength;
887  }
888 
889  //Return status code
890  return error;
891 }
892 
893 
894 /**
895  * @brief Read from a remote file
896  * @param[in] context Pointer to the SCP client context
897  * @param[out] data Buffer where to store the incoming data
898  * @param[in] size Maximum number of bytes that can be read
899  * @param[out] received Actual number of bytes that have been read
900  * @param[in] flags Set of flags that influences the behavior of this function
901  * @return Error code
902  **/
903 
904 error_t scpClientReadFile(ScpClientContext *context, void *data, size_t size,
905  size_t *received, uint_t flags)
906 {
907  error_t error;
908  uint64_t m;
909  size_t n;
910 
911  //Check parameters
912  if(context == NULL || data == NULL || received == NULL)
914 
915  //Initialize status code
916  error = NO_ERROR;
917  //No data has been read yet
918  *received = 0;
919 
920  //Read as much data as possible
921  while(*received < size && !error)
922  {
923  //Check the state of the SCP client
924  if(context->state == SCP_CLIENT_STATE_READ_DATA)
925  {
926  //Check file offset
927  if(context->fileOffset < context->fileSize)
928  {
929  //Number of data bytes left to read
930  m = context->fileSize - context->fileOffset;
931  //Number of bytes available in the buffer
932  n = size - *received;
933 
934  //Limit the number of bytes to read at a time
935  if((uint64_t) n > m)
936  {
937  n = (size_t) m;
938  }
939 
940  //Receive more data
941  error = sshReadChannel(&context->sshChannel, data, n, &n, flags);
942 
943  //Check status code
944  if(error == NO_ERROR)
945  {
946  //Advance data pointer
947  data = (uint8_t *) data + n;
948  *received += n;
949 
950  //Increment file offset
951  context->fileOffset += n;
952 
953  //Save current time
954  context->timestamp = osGetSystemTime();
955  }
956  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
957  {
958  //Process SSH connection events
959  error = scpClientProcessEvents(context);
960  }
961  else
962  {
963  //Communication error
964  }
965  }
966  else
967  {
968  //Update SCP client state
970  }
971  }
972  else if(context->state == SCP_CLIENT_STATE_READ_STATUS)
973  {
974  //The user must be satisfied with data already on hand
975  if(*received > 0)
976  {
977  //Some data are pending in the receive buffer
978  error = NO_ERROR;
979  }
980  else
981  {
982  //The end of the file has been reached
983  error = ERROR_END_OF_STREAM;
984  }
985 
986  //We are done
987  break;
988  }
989  else
990  {
991  //Invalid state
992  error = ERROR_WRONG_STATE;
993  }
994  }
995 
996  //Return status code
997  return error;
998 }
999 
1000 
1001 /**
1002  * @brief Close file
1003  * @param[in] context Pointer to the SCP client context
1004  * @return Error code
1005  **/
1006 
1008 {
1009  error_t error;
1010  ScpDirective directive;
1011 
1012  //Make sure the SCP client context is valid
1013  if(context == NULL)
1014  return ERROR_INVALID_PARAMETER;
1015 
1016  //Initialize SCP directive
1017  osMemset(&directive, 0, sizeof(ScpDirective));
1018 
1019  //Initialize status code
1020  error = NO_ERROR;
1021 
1022  //Close the file
1023  while(!error)
1024  {
1025  //Check the state of the SCP client
1026  if(context->state == SCP_CLIENT_STATE_WRITE_INIT ||
1027  context->state == SCP_CLIENT_STATE_WRITE_COMMAND ||
1028  context->state == SCP_CLIENT_STATE_WRITE_ACK ||
1029  context->state == SCP_CLIENT_STATE_WRITE_DATA ||
1030  context->state == SCP_CLIENT_STATE_READ_INIT ||
1031  context->state == SCP_CLIENT_STATE_READ_COMMAND ||
1032  context->state == SCP_CLIENT_STATE_READ_ACK ||
1033  context->state == SCP_CLIENT_STATE_READ_DATA)
1034  {
1035  //Update SCP client state
1037  }
1038  else if(context->state == SCP_CLIENT_STATE_WRITE_STATUS)
1039  {
1040  //This status directive indicates a success
1041  directive.opcode = SCP_OPCODE_OK;
1042  //Send the directive to the SCP server
1043  error = scpClientSendDirective(context, &directive);
1044 
1045  //Check status code
1046  if(!error)
1047  {
1048  //Update SCP client state
1050  }
1051  }
1052  else if(context->state == SCP_CLIENT_STATE_WRITE_FIN)
1053  {
1054  //Wait for a status directive from the SCP server
1055  error = scpClientReceiveDirective(context, &directive);
1056 
1057  //Check status code
1058  if(!error)
1059  {
1060  //Save SCP status code
1061  context->statusCode = directive.opcode;
1062  //Update SCP client state
1064  }
1065  }
1066  else if(context->state == SCP_CLIENT_STATE_READ_STATUS)
1067  {
1068  //Wait for a status directive from the SCP server
1069  error = scpClientReceiveDirective(context, &directive);
1070 
1071  //Check status code
1072  if(!error)
1073  {
1074  //Save SCP status code
1075  context->statusCode = directive.opcode;
1076 
1077  //Check directive opcode
1078  if(directive.opcode == SCP_OPCODE_OK)
1079  {
1080  //A success directive has been received
1082  }
1083  else
1084  {
1085  //A warning or error directive has been received
1087  }
1088  }
1089  }
1090  else if(context->state == SCP_CLIENT_STATE_READ_FIN)
1091  {
1092  //This status directive indicates a success
1093  directive.opcode = SCP_OPCODE_OK;
1094  //Send the directive to the SCP server
1095  error = scpClientSendDirective(context, &directive);
1096 
1097  //Check status code
1098  if(!error)
1099  {
1100  //Update SCP client state
1102  }
1103  }
1104  else if(context->state == SCP_CLIENT_STATE_CHANNEL_CLOSE)
1105  {
1106  //When either party wishes to terminate the channel, it sends an
1107  //SSH_MSG_CHANNEL_CLOSE message
1108  error = sshCloseChannel(&context->sshChannel);
1109 
1110  //Check status code
1111  if(error == NO_ERROR)
1112  {
1113  //Wait for the SSH_MSG_CHANNEL_CLOSE message to be transmitted
1114  if(context->sshConnection.txBufferLen > 0)
1115  {
1116  //Flush pending data
1117  error = scpClientProcessEvents(context);
1118  }
1119  else
1120  {
1121  //Release SSH channel
1122  sshDeleteChannel(&context->sshChannel);
1123  //Update SCP client state
1125  }
1126  }
1127  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1128  {
1129  //Process SSH connection events
1130  error = scpClientProcessEvents(context);
1131  }
1132  else
1133  {
1134  //Just for sanity
1135  }
1136  }
1137  else if(context->state == SCP_CLIENT_STATE_CONNECTED)
1138  {
1139  //Check SCP status code
1140  if(context->statusCode == SCP_OPCODE_OK)
1141  {
1142  //Check the exit status of the SCP command
1143  if(context->exitStatus == 0)
1144  {
1145  error = NO_ERROR;
1146  }
1147  else
1148  {
1149  error = ERROR_UNEXPECTED_STATUS;
1150  }
1151  }
1152  else
1153  {
1154  //A warning or error directive has been received
1155  error = ERROR_UNEXPECTED_RESPONSE;
1156  }
1157 
1158  //We are done
1159  break;
1160  }
1161  else
1162  {
1163  //Invalid state
1164  error = ERROR_WRONG_STATE;
1165  }
1166  }
1167 
1168  //Return status code
1169  return error;
1170 }
1171 
1172 
1173 /**
1174  * @brief Gracefully disconnect from the SCP server
1175  * @param[in] context Pointer to the SCP client context
1176  * @return Error code
1177  **/
1178 
1180 {
1181  error_t error;
1182 
1183  //Make sure the SCP client context is valid
1184  if(context == NULL)
1185  return ERROR_INVALID_PARAMETER;
1186 
1187  //Initialize status code
1188  error = NO_ERROR;
1189 
1190  //Gracefully disconnect from the SCP server
1191  while(!error)
1192  {
1193  //Check current state
1194  if(context->state == SCP_CLIENT_STATE_CONNECTED)
1195  {
1196  //Send an SSH_MSG_DISCONNECT message
1197  error = sshSendDisconnect(&context->sshConnection,
1198  SSH_DISCONNECT_BY_APPLICATION, "Connection closed by user");
1199 
1200  //Check status code
1201  if(!error)
1202  {
1203  //Update SCP client state
1205  }
1206  }
1207  else if(context->state == SCP_CLIENT_STATE_DISCONNECTING_1)
1208  {
1209  //Wait for the SSH_MSG_DISCONNECT message to be transmitted
1210  error = scpClientProcessEvents(context);
1211 
1212  //Check status code
1213  if(error == ERROR_CONNECTION_CLOSING)
1214  {
1215  //Catch exception
1216  error = NO_ERROR;
1217  //Set timeout
1218  socketSetTimeout(context->sshConnection.socket, context->timeout);
1219  //Update SCP client state
1221  }
1222  }
1223  else if(context->state == SCP_CLIENT_STATE_DISCONNECTING_2)
1224  {
1225  //Shutdown TCP connection
1226  error = socketShutdown(context->sshConnection.socket, SOCKET_SD_BOTH);
1227 
1228  //Check status code
1229  if(error == NO_ERROR)
1230  {
1231  //Close network connection
1232  scpClientCloseConnection(context);
1233  //Update SCP client state
1235  }
1236  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1237  {
1238  //Check whether the timeout has elapsed
1239  error = scpClientCheckTimeout(context);
1240  }
1241  else
1242  {
1243  //A communication error has occurred
1244  }
1245  }
1246  else if(context->state == SCP_CLIENT_STATE_DISCONNECTED)
1247  {
1248  //We are done
1249  break;
1250  }
1251  else
1252  {
1253  //Invalid state
1254  error = ERROR_WRONG_STATE;
1255  }
1256  }
1257 
1258  //Failed to gracefully disconnect from the SCP server?
1259  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
1260  {
1261  //Close network connection
1262  scpClientCloseConnection(context);
1263  //Update SCP client state
1265  }
1266 
1267  //Return status code
1268  return error;
1269 }
1270 
1271 
1272 /**
1273  * @brief Close the connection with the SCP server
1274  * @param[in] context Pointer to the SCP client context
1275  * @return Error code
1276  **/
1277 
1279 {
1280  //Make sure the SCP client context is valid
1281  if(context == NULL)
1282  return ERROR_INVALID_PARAMETER;
1283 
1284  //Close network connection
1285  scpClientCloseConnection(context);
1286  //Update SCP client state
1288 
1289  //Successful processing
1290  return NO_ERROR;
1291 }
1292 
1293 
1294 /**
1295  * @brief Release SCP client context
1296  * @param[in] context Pointer to the SCP client context
1297  **/
1298 
1300 {
1301  //Make sure the SCP client context is valid
1302  if(context != NULL)
1303  {
1304  //Close network connection
1305  scpClientCloseConnection(context);
1306 
1307  //Clear SCP client context
1308  osMemset(context, 0, sizeof(ScpClientContext));
1309  }
1310 }
1311 
1312 #endif
Path manipulation helper functions.
SCP client.
Helper functions for SCP client.
@ ERROR_WOULD_BLOCK
Definition: error.h:96
uint64_t size
Definition: scp_common.h:81
IP network address.
Definition: ip.h:94
SSH connection protocol.
@ SCP_CLIENT_STATE_WRITE_INIT
Definition: scp_client.h:87
#define TRUE
Definition: os_port.h:50
uint8_t data[]
Definition: ethernet.h:224
error_t sshCloseChannel(SshChannel *channel)
Close channel.
Definition: ssh.c:2558
error_t scpClientProcessEvents(ScpClientContext *context)
Process SCP client events.
error_t scpClientSendDirective(ScpClientContext *context, const ScpDirective *directive)
Send a SCP directive to the server.
error_t scpClientInit(ScpClientContext *context)
Initialize SCP client context.
Definition: scp_client.c:54
@ SCP_CLIENT_STATE_DISCONNECTED
Definition: scp_client.h:80
@ SCP_CLIENT_STATE_WRITE_ACK
Definition: scp_client.h:89
@ SCP_OPCODE_OK
Definition: scp_common.h:63
@ SCP_CLIENT_STATE_READ_DATA
Definition: scp_client.h:96
SSH transport layer protocol.
"exec" channel request parameters
Definition: ssh_request.h:119
error_t scpClientConnect(ScpClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Establish a connection with the specified SCP server.
Definition: scp_client.c:151
@ SCP_CLIENT_STATE_CHANNEL_REPLY
Definition: scp_client.h:86
size_t length
Definition: ssh_types.h:58
uint16_t totalLength
Definition: ipv4.h:349
void sshDeleteChannel(SshChannel *channel)
Release channel.
Definition: ssh.c:2629
#define osStrlen(s)
Definition: os_port.h:171
void scpClientChangeState(ScpClientContext *context, ScpClientState newState)
Update SCP client state.
@ SCP_CLIENT_STATE_READ_COMMAND
Definition: scp_client.h:94
@ ERROR_END_OF_STREAM
Definition: error.h:211
@ SCP_CLIENT_STATE_CONNECTED
Definition: scp_client.h:83
const char_t * pathGetFilename(const char_t *path)
Extract the file name from the supplied path.
Definition: path.c:81
@ SCP_CLIENT_STATE_CONNECTING_1
Definition: scp_client.h:81
@ ERROR_WRONG_STATE
Definition: error.h:210
error_t sshReadChannel(SshChannel *channel, void *data, size_t size, size_t *received, uint_t flags)
Receive data from the specified channel.
Definition: ssh.c:2206
@ SSH_REQUEST_STATE_PENDING
Definition: ssh.h:1152
@ SCP_CLIENT_STATE_WRITE_FIN
Definition: scp_client.h:92
@ ERROR_OPEN_FAILED
Definition: error.h:75
@ SSH_DISCONNECT_BY_APPLICATION
Definition: ssh.h:1069
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
@ ERROR_UNEXPECTED_STATUS
Definition: error.h:284
const char_t * value
Definition: ssh_types.h:57
error_t
Error codes.
Definition: error.h:43
#define osSprintf(dest,...)
Definition: os_port.h:237
error_t scpClientBindToInterface(ScpClientContext *context, NetInterface *interface)
Bind the SCP client to a particular network interface.
Definition: scp_client.c:128
SCP directive parameters.
Definition: scp_common.h:78
error_t scpClientOpenConnection(ScpClientContext *context)
Open SSH connection.
error_t scpClientRegisterSshInitCallback(ScpClientContext *context, ScpClientSshInitCallback callback)
Register SSH initialization callback function.
Definition: scp_client.c:85
#define ScpClientContext
Definition: scp_client.h:66
@ SCP_CLIENT_STATE_CONNECTING_2
Definition: scp_client.h:82
error_t scpClientReadFile(ScpClientContext *context, void *data, size_t size, size_t *received, uint_t flags)
Read from a remote file.
Definition: scp_client.c:904
uint32_t mode
Definition: scp_common.h:80
#define NetInterface
Definition: net.h:40
ScpOpcode opcode
Definition: scp_common.h:79
@ ERROR_INVALID_LENGTH
Definition: error.h:111
SshChannel * sshCreateChannel(SshConnection *connection)
Create a new SSH channel.
Definition: ssh.c:1989
@ SCP_CLIENT_STATE_READ_ACK
Definition: scp_client.h:95
NetContext * netGetDefaultContext(void)
Get default TCP/IP stack context.
Definition: net.c:527
@ SSH_CHANNEL_STATE_OPEN
Definition: ssh.h:1140
error_t sshWriteChannel(SshChannel *channel, const void *data, size_t length, size_t *written, uint_t flags)
Write data to the specified channel.
Definition: ssh.c:2077
error_t sshSendDisconnect(SshConnection *connection, uint32_t reasonCode, const char_t *description)
Send SSH_MSG_DISCONNECT message.
error_t socketConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort)
Establish a connection to a specified socket.
Definition: socket.c:1374
error_t socketShutdown(Socket *socket, uint_t how)
Disable reception, transmission, or both.
Definition: socket.c:2015
@ ERROR_UNEXPECTED_RESPONSE
Definition: error.h:70
@ SCP_CLIENT_STATE_WRITE_DATA
Definition: scp_client.h:90
uint8_t length
Definition: tcp.h:375
error_t scpClientDisconnect(ScpClientContext *context)
Gracefully disconnect from the SCP server.
Definition: scp_client.c:1179
error_t scpClientOpenFileForReading(ScpClientContext *context, const char_t *path, uint64_t *size)
Open a file for reading.
Definition: scp_client.c:512
@ ERROR_CONNECTION_CLOSING
Definition: error.h:78
@ SCP_CLIENT_STATE_READ_STATUS
Definition: scp_client.h:97
@ SSH_REQUEST_STATE_FAILURE
Definition: ssh.h:1154
void scpClientDeinit(ScpClientContext *context)
Release SCP client context.
Definition: scp_client.c:1299
uint32_t systime_t
System time.
@ ERROR_TIMEOUT
Definition: error.h:95
char char_t
Definition: compiler_port.h:55
@ SCP_CLIENT_STATE_CHANNEL_OPEN
Definition: scp_client.h:84
@ SSH_REQUEST_STATE_SUCCESS
Definition: ssh.h:1153
@ SCP_OPCODE_FILE
Definition: scp_common.h:66
void scpClientCloseConnection(ScpClientContext *context)
Close SSH connection.
uint8_t m
Definition: ndp.h:304
uint8_t n
@ SCP_CLIENT_STATE_WRITE_STATUS
Definition: scp_client.h:91
@ SCP_CLIENT_STATE_READ_INIT
Definition: scp_client.h:93
#define SCP_CLIENT_DEFAULT_TIMEOUT
Definition: scp_client.h:47
error_t(* ScpClientSshInitCallback)(ScpClientContext *context, SshContext *sshContext)
SSH initialization callback function.
Definition: scp_client.h:109
const char_t * filename
Definition: scp_common.h:84
#define SshConnection
Definition: ssh.h:935
error_t scpClientCloseFile(ScpClientContext *context)
Close file.
Definition: scp_client.c:1007
@ SSH_CHANNEL_STATE_RESERVED
Definition: ssh.h:1139
error_t scpClientOpenFileForWriting(ScpClientContext *context, const char_t *path, uint_t mode, uint64_t size)
Open a file for writing.
Definition: scp_client.c:244
error_t scpClientClose(ScpClientContext *context)
Close the connection with the SCP server.
Definition: scp_client.c:1278
error_t scpClientWriteFile(ScpClientContext *context, const void *data, size_t length, size_t *written, uint_t flags)
Write to a remote file.
Definition: scp_client.c:782
SshString command
Definition: ssh_request.h:120
@ SCP_CLIENT_STATE_CHANNEL_REQUEST
Definition: scp_client.h:85
uint8_t flags
Definition: tcp.h:358
error_t sshSendChannelRequest(SshChannel *channel, const char_t *requestType, const void *requestParams, bool_t wantReply)
Send SSH_MSG_CHANNEL_REQUEST message.
Definition: ssh_request.c:179
@ SCP_CLIENT_STATE_DISCONNECTING_2
Definition: scp_client.h:101
error_t sshSendChannelOpen(SshChannel *channel, const char_t *channelType, const void *channelParams)
Send SSH_MSG_CHANNEL_OPEN message.
unsigned int uint_t
Definition: compiler_port.h:57
@ SSH_CHANNEL_STATE_CLOSED
Definition: ssh.h:1141
#define osMemset(p, value, length)
Definition: os_port.h:141
error_t sshSetChannelTimeout(SshChannel *channel, systime_t timeout)
Set timeout for read/write operations.
Definition: ssh.c:2053
Secure Shell (SSH)
@ SCP_CLIENT_STATE_DISCONNECTING_1
Definition: scp_client.h:100
@ SOCKET_SD_BOTH
Definition: socket.h:161
error_t scpClientCheckTimeout(ScpClientContext *context)
Determine whether a timeout error has occurred.
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:166
@ SCP_CLIENT_STATE_WRITE_COMMAND
Definition: scp_client.h:88
Global request and channel request handling.
@ SCP_CLIENT_STATE_READ_FIN
Definition: scp_client.h:98
error_t scpClientReceiveDirective(ScpClientContext *context, ScpDirective *directive)
Receive a SCP directive from the server.
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
error_t scpClientSetTimeout(ScpClientContext *context, systime_t timeout)
Set communication timeout.
Definition: scp_client.c:107
#define SshChannel
Definition: ssh.h:939
error_t scpClientEstablishConnection(ScpClientContext *context)
Establish SSH connection.
@ SCP_CLIENT_STATE_CHANNEL_CLOSE
Definition: scp_client.h:99
systime_t osGetSystemTime(void)
Retrieve system time.