scp_common.c
Go to the documentation of this file.
1 /**
2  * @file scp_common.c
3  * @brief Definitions common to SCP client and server
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 CycloneTCP 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.4
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_misc.h"
37 #include "scp/scp_common.h"
38 #include "debug.h"
39 
40 
41 /**
42  * @brief Format SCP directive
43  * @param[in] directive SCP directive parameters
44  * @param[out] buffer Buffer where to format the directive line
45  * @return Error code
46  **/
47 
48 size_t scpFormatDirective(const ScpDirective *directive, char_t *buffer)
49 {
50  size_t n;
51 
52  //Length of the directive line
53  n = 0;
54 
55  //Set directive opcode
56  buffer[n++] = directive->opcode;
57 
58  //Check directive opcode
59  if(directive->opcode == SCP_OPCODE_OK)
60  {
61  //Debug message
62  TRACE_DEBUG("Sending SCP success directive...\r\n");
63  }
64  else if(directive->opcode == SCP_OPCODE_WARNING ||
65  directive->opcode == SCP_OPCODE_ERROR)
66  {
67  //Warning and error directives can be followed by a textual description
68  n += osSprintf(buffer + n, "%s\n", directive->message);
69 
70  //Debug message
71  TRACE_DEBUG("Sending SCP error directive...\r\n");
72  }
73  else if(directive->opcode == SCP_OPCODE_FILE ||
74  directive->opcode == SCP_OPCODE_DIR)
75  {
76  //The 'C' directive indicates the next file to be transferred. The 'D'
77  //directive indicates a directory change
78  if(directive->size <= (uint64_t) UINT32_MAX)
79  {
80  n += osSprintf(buffer + n, "%04" PRIo32 " %" PRIu32 " %s\n",
81  directive->mode, (uint32_t) directive->size, directive->filename);
82  }
83  else
84  {
85  n += osSprintf(buffer + n, "%04" PRIo32 " %" PRIu64 " %s\n",
86  directive->mode, directive->size, directive->filename);
87  }
88 
89  //Debug message
90  TRACE_DEBUG("Sending SCP '%c' directive...\r\n", directive->opcode);
91  }
92  else if(directive->opcode == SCP_OPCODE_END)
93  {
94  //The 'E' directive indicates the end of the directory
95  buffer[n++] = '\n';
96 
97  //Debug message
98  TRACE_DEBUG("Sending SCP '%c' directive...\r\n", directive->opcode);
99  }
100  else if(directive->opcode == SCP_OPCODE_TIME)
101  {
102  //The 'T' directive indicates that the next file to be transferred must
103  //have mtime and atime attributes preserved
104  n += osSprintf(buffer + n, "%" PRIu32 " 0 %" PRIu32 " 0\n",
105  directive->mtime, directive->atime);
106 
107  //Debug message
108  TRACE_DEBUG("Sending SCP '%c' directive...\r\n", directive->opcode);
109  }
110  else
111  {
112  //Unknown directive
113  }
114 
115  //Return the length of the directive line
116  return n;
117 }
118 
119 
120 /**
121  * @brief Parse SCP directive
122  * @param[in] buffer Pointer to the directive line
123  * @param[out] directive SCP directive parameters
124  * @return Error code
125  **/
126 
127 error_t scpParseDirective(const char_t *buffer, ScpDirective *directive)
128 {
129  error_t error;
130  char_t *p;
131 
132  //Initialize status code
133  error = NO_ERROR;
134 
135  //Initialize SCP directive
136  osMemset(directive, 0, sizeof(ScpDirective));
137 
138  //Save directive opcode
139  directive->opcode = (ScpOpcode) buffer[0];
140 
141  //Check directive opcode
142  if(directive->opcode == SCP_OPCODE_OK)
143  {
144  //Debug message
145  TRACE_DEBUG("SCP success directive received...\r\n");
146  }
147  else if(directive->opcode == SCP_OPCODE_WARNING ||
148  directive->opcode == SCP_OPCODE_ERROR)
149  {
150  //Debug message
151  TRACE_DEBUG("SCP error directive received...\r\n");
152 
153  //Warning and error directives can be followed by a textual description
154  directive->message = (char_t *) buffer + 1;
155  }
156  else if(directive->opcode == SCP_OPCODE_FILE ||
157  directive->opcode == SCP_OPCODE_DIR)
158  {
159  //Debug message
160  TRACE_DEBUG("SCP '%c' directive received...\r\n", directive->opcode);
161 
162  //Start of exception handling block
163  do
164  {
165  //Get file permissions
166  directive->mode = osStrtoul(buffer + 1, &p, 8);
167 
168  //Any syntax error?
169  if(!osIsblank(*p))
170  {
171  error = ERROR_INVALID_SYNTAX;
172  break;
173  }
174 
175  //Skip whitespace characters
176  while(osIsblank(*p))
177  {
178  p++;
179  }
180 
181  //Get the size of the file
182  directive->size = osStrtoull(p, &p, 10);
183 
184  //Any syntax error?
185  if(!osIsblank(*p))
186  {
187  error = ERROR_INVALID_SYNTAX;
188  break;
189  }
190 
191  //Skip whitespace characters
192  while(osIsblank(*p))
193  {
194  p++;
195  }
196 
197  //Get the name of the file
198  directive->filename = p;
199 
200  //End of exception handling block
201  } while(0);
202  }
203  else if(directive->opcode == SCP_OPCODE_END)
204  {
205  //Debug message
206  TRACE_DEBUG("SCP '%c' directive received...\r\n", directive->opcode);
207  }
208  else if(directive->opcode == SCP_OPCODE_TIME)
209  {
210  //Debug message
211  TRACE_DEBUG("SCP '%c' directive received...\r\n", directive->opcode);
212 
213  //Start of exception handling block
214  do
215  {
216  //Get modification time of the file (in seconds)
217  directive->mtime = osStrtoul(buffer + 1, &p, 10);
218 
219  //Any syntax error?
220  if(!osIsblank(*p))
221  {
222  error = ERROR_INVALID_SYNTAX;
223  break;
224  }
225 
226  //Skip the microseconds field
227  osStrtoul(p, &p, 10);
228 
229  //Any syntax error?
230  if(!osIsblank(*p))
231  {
232  error = ERROR_INVALID_SYNTAX;
233  break;
234  }
235 
236  //Get the access time of the file (in seconds)
237  directive->atime = osStrtoul(p, &p, 10);
238 
239  //Any syntax error?
240  if(!osIsblank(*p))
241  {
242  error = ERROR_INVALID_SYNTAX;
243  break;
244  }
245 
246  //Skip the microseconds field
247  osStrtoul(p, &p, 10);
248 
249  //Any syntax error?
250  if(*p != '\0')
251  {
252  error = ERROR_INVALID_SYNTAX;
253  break;
254  }
255 
256  //End of exception handling block
257  } while(0);
258  }
259  else
260  {
261  //Debug message
262  TRACE_WARNING("SCP unknown directive received...\r\n");
263 
264  //Unknown directive
265  error = ERROR_INVALID_COMMAND;
266  }
267 
268  //Return status code
269  return error;
270 }
uint32_t atime
Definition: scp_common.h:83
uint64_t size
Definition: scp_common.h:81
uint32_t mtime
Definition: scp_common.h:82
uint8_t p
Definition: ndp.h:300
@ SCP_OPCODE_OK
Definition: scp_common.h:63
@ ERROR_INVALID_COMMAND
Definition: error.h:100
ScpOpcode
SCP directive opcodes.
Definition: scp_common.h:62
@ SCP_OPCODE_END
Definition: scp_common.h:68
error_t
Error codes.
Definition: error.h:43
#define osSprintf(dest,...)
Definition: os_port.h:231
#define osIsblank(c)
Definition: os_port.h:296
SCP directive parameters.
Definition: scp_common.h:78
@ SCP_OPCODE_TIME
Definition: scp_common.h:69
uint32_t mode
Definition: scp_common.h:80
ScpOpcode opcode
Definition: scp_common.h:79
@ SCP_OPCODE_DIR
Definition: scp_common.h:67
#define osStrtoull(s, endptr, base)
Definition: os_port.h:261
Definitions common to SCP client and server.
#define osStrtoul(s, endptr, base)
Definition: os_port.h:255
#define TRACE_WARNING(...)
Definition: debug.h:85
#define TRACE_DEBUG(...)
Definition: debug.h:107
char char_t
Definition: compiler_port.h:48
size_t scpFormatDirective(const ScpDirective *directive, char_t *buffer)
Format SCP directive.
Definition: scp_common.c:48
@ SCP_OPCODE_FILE
Definition: scp_common.h:66
uint8_t n
const char_t * filename
Definition: scp_common.h:84
const char_t * message
Definition: scp_common.h:85
SSH helper functions.
@ ERROR_INVALID_SYNTAX
Definition: error.h:68
error_t scpParseDirective(const char_t *buffer, ScpDirective *directive)
Parse SCP directive.
Definition: scp_common.c:127
#define osMemset(p, value, length)
Definition: os_port.h:135
@ SCP_OPCODE_ERROR
Definition: scp_common.h:65
Secure Shell (SSH)
@ SCP_OPCODE_WARNING
Definition: scp_common.h:64
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.