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-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.4
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
168 
169  //Valid socket handle
170  if(socket != NULL)
171  {
172  //Associate the socket with the relevant interface
173  socketBindToInterface(socket, context->interface);
174  //Set timeout
175  socketSetTimeout(socket, context->timeout);
176 
177  //Open a new SSH connection
178  connection = sshOpenConnection(&context->sshContext, socket);
179 
180  //Failed to open connection?
181  if(connection == NULL)
182  {
183  //Clean up side effects
185  //Report an error
186  error = ERROR_OPEN_FAILED;
187  }
188  }
189  else
190  {
191  //Failed to open socket
192  error = ERROR_OPEN_FAILED;
193  }
194 
195  //Return status code
196  return error;
197 }
198 
199 
200 /**
201  * @brief Establish SSH connection
202  * @param[in] context Pointer to the shell client context
203  * @return Error code
204  **/
205 
207 {
208  error_t error;
209 
210  //Check the state of the SSH connection
211  if(context->sshConnection.state < SSH_CONN_STATE_OPEN)
212  {
213  //Perform SSH key exchange and user authentication
214  error = shellClientProcessEvents(context);
215  }
216  else if(context->sshConnection.state == SSH_CONN_STATE_OPEN)
217  {
218  //The SSH connection is established
220  //Successful processing
221  error = NO_ERROR;
222  }
223  else
224  {
225  //Invalid state
226  error = ERROR_WRONG_STATE;
227  }
228 
229  //Return status code
230  return error;
231 }
232 
233 
234 /**
235  * @brief Close SSH connection
236  * @param[in] context Pointer to the shell client context
237  **/
238 
240 {
241  //Check the state of the SSH connection
242  if(context->sshConnection.state != SSH_CONN_STATE_CLOSED)
243  {
244  //Close SSH connection
245  sshCloseConnection(&context->sshConnection);
246  }
247 
248  //Release SSH context
249  sshDeinit(&context->sshContext);
250 }
251 
252 
253 /**
254  * @brief Process shell client events
255  * @param[in] context Pointer to the shell client context
256  * @return Error code
257  **/
258 
260 {
261  error_t error;
262  uint_t i;
263  SshContext *sshContext;
264  SshConnection *connection;
265 
266  //Point to the SSH context
267  sshContext = &context->sshContext;
268 
269  //Clear event descriptor set
270  osMemset(sshContext->eventDesc, 0, sizeof(sshContext->eventDesc));
271 
272  //Specify the events the application is interested in
273  for(i = 0; i < sshContext->numConnections; i++)
274  {
275  //Point to the structure describing the current connection
276  connection = &sshContext->connections[i];
277 
278  //Loop through active connections only
279  if(connection->state != SSH_CONN_STATE_CLOSED)
280  {
281  //Register the events related to the current SSH connection
282  sshRegisterConnectionEvents(sshContext, connection, &sshContext->eventDesc[i]);
283  }
284  }
285 
286  //Wait for one of the set of sockets to become ready to perform I/O
287  error = socketPoll(sshContext->eventDesc, sshContext->numConnections,
288  &sshContext->event, context->timeout);
289 
290  //Verify status code
291  if(error == NO_ERROR || error == ERROR_WAIT_CANCELED)
292  {
293  //Clear status code
294  error = NO_ERROR;
295 
296  //Event-driven processing
297  for(i = 0; i < sshContext->numConnections && !error; i++)
298  {
299  //Point to the structure describing the current connection
300  connection = &sshContext->connections[i];
301 
302  //Loop through active connections only
303  if(connection->state != SSH_CONN_STATE_CLOSED)
304  {
305  //Check whether the socket is ready to perform I/O
306  if(sshContext->eventDesc[i].eventFlags != 0)
307  {
308  //Connection event handler
309  error = sshProcessConnectionEvents(sshContext, connection);
310  }
311  }
312  }
313  }
314 
315  //Check status code
316  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
317  {
318  //Check whether the timeout has elapsed
319  error = shellClientCheckTimeout(context);
320  }
321 
322  //Return status code
323  return error;
324 }
325 
326 
327 /**
328  * @brief Determine whether a timeout error has occurred
329  * @param[in] context Pointer to the shell client context
330  * @return Error code
331  **/
332 
334 {
335  error_t error;
336  systime_t time;
337 
338  //Get current time
339  time = osGetSystemTime();
340 
341  //Check whether the timeout has elapsed
342  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
343  {
344  //Report a timeout error
345  error = ERROR_TIMEOUT;
346  }
347  else
348  {
349 #if (NET_RTOS_SUPPORT == ENABLED)
350  //Successful operation
351  error = NO_ERROR;
352 #else
353  //The operation would block
354  error = ERROR_WOULD_BLOCK;
355 #endif
356  }
357 
358  //Return status code
359  return error;
360 }
361 
362 #endif
@ SSH_CONN_STATE_OPEN
Definition: ssh.h:1062
@ 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:66
uint8_t data[]
Definition: ethernet.h:222
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:2062
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:1586
@ ERROR_WRONG_STATE
Definition: error.h:209
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:870
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:2556
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:892
@ ERROR_UNKNOWN_REQUEST
Definition: error.h:277
error_t sshProcessConnectionEvents(SshContext *context, SshConnection *connection)
Connection event handler.
Definition: ssh_misc.c:372
void sshCloseConnection(SshConnection *connection)
Close SSH connection.
Definition: ssh_misc.c:172
@ SHELL_CLIENT_STATE_CONNECTED
Definition: shell_client.h:77
#define TRACE_INFO(...)
Definition: debug.h:95
uint8_t length
Definition: tcp.h:368
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:125
String.
Definition: ssh_types.h:56
@ SSH_CONN_STATE_CLOSED
Definition: ssh.h:1033
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:2149
#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:280
#define SshConnection
Definition: ssh.h:874
#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:50
#define osMemset(p, value, length)
Definition: os_port.h:135
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:148
Global request and channel request handling.
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define SshChannel
Definition: ssh.h:878
systime_t osGetSystemTime(void)
Retrieve system time.