shell_client_misc.c
Go to the documentation of this file.
1 /**
2  * @file shell_client_misc.c
3  * @brief Helper functions for SSH secure shell 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 SHELL_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ssh/ssh.h"
36 #include "ssh/ssh_connection.h"
37 #include "ssh/ssh_request.h"
38 #include "ssh/ssh_misc.h"
39 #include "shell/shell_client.h"
41 #include "debug.h"
42 
43 //Check SSH stack configuration
44 #if (SHELL_CLIENT_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief Update Shell client state
49  * @param[in] context Pointer to the shell client context
50  * @param[in] newState New state to switch to
51  **/
52 
54  ShellClientState newState)
55 {
56  //Switch to the new state
57  context->state = newState;
58 
59  //Save current time
60  context->timestamp = osGetSystemTime();
61 }
62 
63 
64 /**
65  * @brief SSH channel request callback
66  * @param[in] channel Handle referencing an SSH channel
67  * @param[in] type Request type
68  * @param[in] data Request-specific data
69  * @param[in] length Length of the request-specific data, in bytes
70  * @param[in] param Pointer to the shell client context
71  * @return Error code
72  **/
73 
75  const SshString *type, const uint8_t *data, size_t length,
76  void *param)
77 {
78  error_t error;
79  ShellClientContext *context;
80 
81  //Debug message
82  TRACE_INFO("Shell client: SSH channel request callback...\r\n");
83 
84  //Point to the shell client context
85  context = (ShellClientContext *) param;
86 
87  //Check request type
88  if(sshCompareString(type, "exit-status"))
89  {
90  SshExitStatusParams requestParams;
91 
92  //An SSH_MSG_CHANNEL_REQUEST message can be sent to return the exit
93  //status when the command running at the other end terminates (refer
94  //to RFC 4254, section 6.10)
95  error = sshParseExitStatusParams(data, length, &requestParams);
96 
97  //Check status code
98  if(!error)
99  {
100  //Matching channel?
101  if(channel == &context->sshChannel)
102  {
103  //Save exit status
104  context->exitStatus = requestParams.exitStatus;
105  }
106  else
107  {
108  //Unknown channel
109  error = ERROR_UNKNOWN_REQUEST;
110  }
111  }
112  }
113  else
114  {
115  //The request is not supported
116  error = ERROR_UNKNOWN_REQUEST;
117  }
118 
119  //Return status code
120  return error;
121 }
122 
123 
124 /**
125  * @brief Open SSH connection
126  * @param[in] context Pointer to the shell client context
127  * @return Error code
128  **/
129 
131 {
132  error_t error;
133  Socket *socket;
134  SshConnection *connection;
135 
136  //Initialize SSH context
137  error = sshInit(&context->sshContext, &context->sshConnection, 1,
138  &context->sshChannel, 1);
139  //Any error to report?
140  if(error)
141  return error;
142 
143  //Select client operation mode
144  error = sshSetOperationMode(&context->sshContext, SSH_OPERATION_MODE_CLIENT);
145  //Any error to report?
146  if(error)
147  return error;
148 
149  //Register channel request processing callback
150  error = sshRegisterChannelRequestCallback(&context->sshContext,
152  //Any error to report?
153  if(error)
154  return error;
155 
156  //Invoke user-defined callback, if any
157  if(context->sshInitCallback != NULL)
158  {
159  //Perform SSH related initialization
160  error = context->sshInitCallback(context, &context->sshContext);
161  //Any error to report?
162  if(error)
163  return error;
164  }
165 
166  //Open a TCP socket
167  socket = socketOpenEx(context->netContext, SOCKET_TYPE_STREAM,
169 
170  //Valid socket handle
171  if(socket != NULL)
172  {
173  //Associate the socket with the relevant interface
174  socketBindToInterface(socket, context->interface);
175  //Set timeout
176  socketSetTimeout(socket, context->timeout);
177 
178  //Open a new SSH connection
179  connection = sshOpenConnection(&context->sshContext, socket);
180 
181  //Failed to open connection?
182  if(connection == NULL)
183  {
184  //Clean up side effects
186  //Report an error
187  error = ERROR_OPEN_FAILED;
188  }
189  }
190  else
191  {
192  //Failed to open socket
193  error = ERROR_OPEN_FAILED;
194  }
195 
196  //Return status code
197  return error;
198 }
199 
200 
201 /**
202  * @brief Establish SSH connection
203  * @param[in] context Pointer to the shell client context
204  * @return Error code
205  **/
206 
208 {
209  error_t error;
210 
211  //Check the state of the SSH connection
212  if(context->sshConnection.state < SSH_CONN_STATE_OPEN)
213  {
214  //Perform SSH key exchange and user authentication
215  error = shellClientProcessEvents(context);
216  }
217  else if(context->sshConnection.state == SSH_CONN_STATE_OPEN)
218  {
219  //The SSH connection is established
221  //Successful processing
222  error = NO_ERROR;
223  }
224  else
225  {
226  //Invalid state
227  error = ERROR_WRONG_STATE;
228  }
229 
230  //Return status code
231  return error;
232 }
233 
234 
235 /**
236  * @brief Close SSH connection
237  * @param[in] context Pointer to the shell client context
238  **/
239 
241 {
242  //Check the state of the SSH connection
243  if(context->sshConnection.state != SSH_CONN_STATE_CLOSED)
244  {
245  //Close SSH connection
246  sshCloseConnection(&context->sshConnection);
247  }
248 
249  //Release SSH context
250  sshDeinit(&context->sshContext);
251 }
252 
253 
254 /**
255  * @brief Process shell client events
256  * @param[in] context Pointer to the shell client context
257  * @return Error code
258  **/
259 
261 {
262  error_t error;
263  uint_t i;
264  SshContext *sshContext;
265  SshConnection *connection;
266 
267  //Point to the SSH context
268  sshContext = &context->sshContext;
269 
270  //Clear event descriptor set
271  osMemset(sshContext->eventDesc, 0, sizeof(sshContext->eventDesc));
272 
273  //Specify the events the application is interested in
274  for(i = 0; i < sshContext->numConnections; i++)
275  {
276  //Point to the structure describing the current connection
277  connection = &sshContext->connections[i];
278 
279  //Loop through active connections only
280  if(connection->state != SSH_CONN_STATE_CLOSED)
281  {
282  //Register the events related to the current SSH connection
283  sshRegisterConnectionEvents(sshContext, connection, &sshContext->eventDesc[i]);
284  }
285  }
286 
287  //Wait for one of the set of sockets to become ready to perform I/O
288  error = socketPoll(sshContext->eventDesc, sshContext->numConnections,
289  &sshContext->event, context->timeout);
290 
291  //Verify status code
292  if(error == NO_ERROR || error == ERROR_WAIT_CANCELED)
293  {
294  //Clear status code
295  error = NO_ERROR;
296 
297  //Event-driven processing
298  for(i = 0; i < sshContext->numConnections && !error; i++)
299  {
300  //Point to the structure describing the current connection
301  connection = &sshContext->connections[i];
302 
303  //Loop through active connections only
304  if(connection->state != SSH_CONN_STATE_CLOSED)
305  {
306  //Check whether the socket is ready to perform I/O
307  if(sshContext->eventDesc[i].eventFlags != 0)
308  {
309  //Connection event handler
310  error = sshProcessConnectionEvents(sshContext, connection);
311  }
312  }
313  }
314  }
315 
316  //Check status code
317  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
318  {
319  //Check whether the timeout has elapsed
320  error = shellClientCheckTimeout(context);
321  }
322 
323  //Return status code
324  return error;
325 }
326 
327 
328 /**
329  * @brief Determine whether a timeout error has occurred
330  * @param[in] context Pointer to the shell client context
331  * @return Error code
332  **/
333 
335 {
336  error_t error;
337  systime_t time;
338 
339  //Get current time
340  time = osGetSystemTime();
341 
342  //Check whether the timeout has elapsed
343  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
344  {
345  //Report a timeout error
346  error = ERROR_TIMEOUT;
347  }
348  else
349  {
350 #if (NET_RTOS_SUPPORT == ENABLED)
351  //Successful operation
352  error = NO_ERROR;
353 #else
354  //The operation would block
355  error = ERROR_WOULD_BLOCK;
356 #endif
357  }
358 
359  //Return status code
360  return error;
361 }
362 
363 #endif
@ SSH_CONN_STATE_OPEN
Definition: ssh.h:1088
@ ERROR_WOULD_BLOCK
Definition: error.h:96
Helper functions for SSH secure shell client.
SSH connection protocol.
SshConnection * sshOpenConnection(SshContext *context, Socket *socket)
Open a new SSH connection.
Definition: ssh_misc.c:68
uint8_t data[]
Definition: ethernet.h:224
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:2094
error_t sshRegisterChannelRequestCallback(SshContext *context, SshChannelReqCallback callback, void *param)
Register channel request callback function.
Definition: ssh.c:705
error_t sshParseExitStatusParams(const uint8_t *p, size_t length, SshExitStatusParams *params)
Parse "exit-status" channel request parameters.
Definition: ssh_request.c:1708
uint8_t type
Definition: coap_common.h:176
#define ShellClientContext
Definition: shell_client.h:60
@ SOCKET_TYPE_STREAM
Definition: socket.h:92
#define timeCompare(t1, t2)
Definition: os_port.h:40
bool_t sshCompareString(const SshString *string, const char_t *value)
Compare a binary string against the supplied value.
Definition: ssh_misc.c:1691
@ ERROR_WRONG_STATE
Definition: error.h:210
error_t sshInit(SshContext *context, SshConnection *connections, uint_t numConnections, SshChannel *channels, uint_t numChannels)
SSH context initialization.
Definition: ssh.c:58
@ ERROR_OPEN_FAILED
Definition: error.h:75
SSH secure shell client.
#define SshContext
Definition: ssh.h:892
error_t shellClientProcessEvents(ShellClientContext *context)
Process shell client events.
error_t
Error codes.
Definition: error.h:43
void sshDeinit(SshContext *context)
Release SSH context.
Definition: ssh.c:2581
error_t shellClientChannelRequestCallback(SshChannel *channel, const SshString *type, const uint8_t *data, size_t length, void *param)
SSH channel request callback.
int_t socket(int_t family, int_t type, int_t protocol)
Create a socket that is bound to a specific transport service provider.
Definition: bsd_socket.c:65
@ SSH_OPERATION_MODE_CLIENT
Definition: ssh.h:914
@ ERROR_UNKNOWN_REQUEST
Definition: error.h:278
error_t sshProcessConnectionEvents(SshContext *context, SshConnection *connection)
Connection event handler.
Definition: ssh_misc.c:375
void sshCloseConnection(SshConnection *connection)
Close SSH connection.
Definition: ssh_misc.c:174
@ SHELL_CLIENT_STATE_CONNECTED
Definition: shell_client.h:77
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
String.
Definition: ssh_types.h:56
@ SSH_CONN_STATE_CLOSED
Definition: ssh.h:1057
error_t shellClientOpenConnection(ShellClientContext *context)
Open SSH connection.
error_t socketPoll(SocketEventDesc *eventDesc, uint_t size, OsEvent *extEvent, systime_t timeout)
Wait for one of a set of sockets to become ready to perform I/O.
Definition: socket.c:2182
#define socketBindToInterface
Definition: net_legacy.h:193
void shellClientChangeState(ShellClientContext *context, ShellClientState newState)
Update Shell client state.
uint32_t systime_t
System time.
"exit-status" channel request parameters
Definition: ssh_request.h:172
@ ERROR_TIMEOUT
Definition: error.h:95
uint32_t time
void shellClientCloseConnection(ShellClientContext *context)
Close SSH connection.
void sshRegisterConnectionEvents(SshContext *context, SshConnection *connection, SocketEventDesc *eventDesc)
Register connection events.
Definition: ssh_misc.c:282
#define SshConnection
Definition: ssh.h:896
Socket * socketOpenEx(NetContext *context, uint_t type, uint_t protocol)
Create a socket.
Definition: socket.c:146
#define Socket
Definition: socket.h:36
error_t shellClientCheckTimeout(ShellClientContext *context)
Determine whether a timeout error has occurred.
@ ERROR_WAIT_CANCELED
Definition: error.h:73
SSH helper functions.
ShellClientState
Shell client state.
Definition: shell_client.h:73
unsigned int uint_t
Definition: compiler_port.h:57
#define osMemset(p, value, length)
Definition: os_port.h:138
error_t sshSetOperationMode(SshContext *context, SshOperationMode mode)
Set operation mode (client or server)
Definition: ssh.c:167
Secure Shell (SSH)
error_t shellClientEstablishConnection(ShellClientContext *context)
Establish SSH connection.
@ SOCKET_IP_PROTO_TCP
Definition: socket.h:107
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:169
Global request and channel request handling.
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define SshChannel
Definition: ssh.h:900
systime_t osGetSystemTime(void)
Retrieve system time.