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