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