diff --git a/lib/tls/mbedtls/mbedtls-server.c b/lib/tls/mbedtls/mbedtls-server.c index 5e8c9b86..0761a646 100644 --- a/lib/tls/mbedtls/mbedtls-server.c +++ b/lib/tls/mbedtls/mbedtls-server.c @@ -631,7 +631,7 @@ lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[], /* subject must be formatted like "C=TW,O=warmcat,CN=myserver" */ - for (n = 0; n < ARRAY_SIZE(x5); n++) { + for (n = 0; n < (int)ARRAY_SIZE(x5); n++) { if (p != subject) *p++ = ','; if (elements[n]) diff --git a/plugins/protocol_lws_sshd_demo.c b/plugins/protocol_lws_sshd_demo.c index b5826fcb..f5665340 100644 --- a/plugins/protocol_lws_sshd_demo.c +++ b/plugins/protocol_lws_sshd_demo.c @@ -319,7 +319,7 @@ bail_p1: } static int -ssh_ops_shell(void *_priv, struct lws *wsi) +ssh_ops_shell(void *_priv, struct lws *wsi, lws_ssh_finish_exec finish, void *finish_handle) { struct sshd_instance_priv *priv = _priv; @@ -372,7 +372,7 @@ static const struct lws_ssh_ops ssh_ops = { .banner = ssh_ops_banner, .disconnect_reason = ssh_ops_disconnect_reason, .server_string = "SSH-2.0-Libwebsockets", - .api_version = 1, + .api_version = 2, }; static int diff --git a/plugins/ssh-base/include/lws-plugin-ssh.h b/plugins/ssh-base/include/lws-plugin-ssh.h index 4ba11658..8626daaa 100644 --- a/plugins/ssh-base/include/lws-plugin-ssh.h +++ b/plugins/ssh-base/include/lws-plugin-ssh.h @@ -24,7 +24,7 @@ #define LWS_CALLBACK_SSH_UART_SET_RXFLOW (LWS_CALLBACK_USER + 800) -#define LWS_SSH_OPS_VERSION 1 +#define LWS_SSH_OPS_VERSION 2 struct lws_ssh_pty { char term[16]; @@ -127,6 +127,8 @@ struct lws_ssh_pty { */ ///@{ +typedef void (*lws_ssh_finish_exec)(void *handle, int retcode); + struct lws_ssh_ops { /** * channel_create() - Channel created @@ -243,21 +245,25 @@ struct lws_ssh_ops { * \param priv: void * you set when this channel was created * \param wsi: the struct lws the connection belongs to * \param command: string containing path to app and arguments + * \param finish: function to call to indicate the exec finished + * \param finish_handle: opaque handle identifying this exec for use with \p finish * * Client requested to exec something. Return nonzero to fail. */ - int (*exec)(void *priv, struct lws *wsi, const char *command); + int (*exec)(void *priv, struct lws *wsi, const char *command, lws_ssh_finish_exec finish, void *finish_handle); /** * shell() - Spawn shell that is appropriate for user * * \param priv: void * you set when this channel was created * \param wsi: the struct lws the connection belongs to + * \param finish: function to call to indicate the exec finished + * \param finish_handle: opaque handle identifying this exec for use with \p finish * * Spawn the appropriate shell for this user. Return 0 for OK * or nonzero to fail. */ - int (*shell)(void *priv, struct lws *wsi); + int (*shell)(void *priv, struct lws *wsi, lws_ssh_finish_exec finish, void *finish_handle); /** * pty_req() - Create a Pseudo-TTY as described in pty diff --git a/plugins/ssh-base/include/lws-ssh.h b/plugins/ssh-base/include/lws-ssh.h index 7259349a..4ffb17ea 100644 --- a/plugins/ssh-base/include/lws-ssh.h +++ b/plugins/ssh-base/include/lws-ssh.h @@ -194,6 +194,7 @@ enum { SSH_WT_CH_CLOSE, SSH_WT_CH_EOF, SSH_WT_WINDOW_ADJUST, + SSH_WT_EXIT_STATUS, /* RX parser states */ @@ -426,8 +427,13 @@ typedef union { struct lws_subprotocol_scp scp; } lws_subprotocol; +struct per_session_data__sshd; + struct lws_ssh_channel { struct lws_ssh_channel *next; + + struct per_session_data__sshd *pss; + lws_subprotocol *sub; /* NULL, or allocated subprotocol state */ void *priv; /* owned by user code */ int type; @@ -438,8 +444,9 @@ struct lws_ssh_channel { uint32_t max_pkt; uint32_t spawn_pid; + int retcode; - uint8_t had_eof:1; + uint8_t scheduled_close:1; uint8_t sent_close:1; uint8_t received_close:1; }; @@ -514,8 +521,8 @@ struct per_session_data__sshd { uint8_t msg_id; uint8_t msg_padding; - uint8_t write_task[4]; - struct lws_ssh_channel *write_channel[4]; + uint8_t write_task[8]; + struct lws_ssh_channel *write_channel[8]; uint8_t wt_head, wt_tail; }; diff --git a/plugins/ssh-base/sshd.c b/plugins/ssh-base/sshd.c index 85ec9de4..3fd245a0 100644 --- a/plugins/ssh-base/sshd.c +++ b/plugins/ssh-base/sshd.c @@ -114,10 +114,21 @@ write_task(struct per_session_data__sshd *pss, struct lws_ssh_channel *ch, { pss->write_task[pss->wt_head] = task; pss->write_channel[pss->wt_head] = ch; - pss->wt_head = (pss->wt_head + 1) & 3; + pss->wt_head = (pss->wt_head + 1) & 7; lws_callback_on_writable(pss->wsi); } +void +write_task_insert(struct per_session_data__sshd *pss, struct lws_ssh_channel *ch, + int task) +{ + pss->wt_tail = (pss->wt_tail - 1) & 7; + pss->write_task[pss->wt_tail] = task; + pss->write_channel[pss->wt_tail] = ch; + lws_callback_on_writable(pss->wsi); +} + + void lws_pad_set_length(struct per_session_data__sshd *pss, void *start, uint8_t **p, struct lws_ssh_keys *keys) @@ -520,6 +531,17 @@ ssh_destroy_channel(struct per_session_data__sshd *pss, lwsl_notice("Failed to delete ch\n"); } +static void +lws_ssh_exec_finish(void *finish_handle, int retcode) +{ + struct lws_ssh_channel *ch = (struct lws_ssh_channel *)finish_handle; + struct per_session_data__sshd *pss = ch->pss; + + ch->retcode = retcode; + write_task(pss, ch, SSH_WT_EXIT_STATUS); + ch->scheduled_close = 1; + write_task(pss, ch, SSH_WT_CH_CLOSE); +} static int lws_ssh_parse_plaintext(struct per_session_data__sshd *pss, uint8_t *p, size_t len) @@ -1359,6 +1381,7 @@ again: return -1; pss->ch_temp->type = SSH_CH_TYPE_SESSION; + pss->ch_temp->pss = pss; state_get_u32(pss, SSHS_NVC_CHOPEN_SENDER_CH); break; @@ -1425,9 +1448,11 @@ again: pss->channel_doing_spawn = pss->ch_temp->server_ch; if (pss->vhd->ops && pss->vhd->ops->shell && !pss->vhd->ops->shell(pss->ch_temp->priv, - pss->wsi)) { + pss->wsi, + lws_ssh_exec_finish, pss->ch_temp)) { + if (pss->rq_want_reply) - write_task(pss, pss->ch_temp, + write_task_insert(pss, pss->ch_temp, SSH_WT_CHRQ_SUCC); pss->parser_state = SSHS_MSG_EAT_PADDING; break; @@ -1544,10 +1569,11 @@ again: if (pss->vhd->ops && pss->vhd->ops->exec && !pss->vhd->ops->exec(pss->ch_temp->priv, pss->wsi, - (const char *)pss->last_alloc)) { + (const char *)pss->last_alloc, + lws_ssh_exec_finish, pss->ch_temp)) { ssh_free(pss->last_alloc); if (pss->rq_want_reply) - write_task(pss, pss->ch_temp, + write_task_insert(pss, pss->ch_temp, SSH_WT_CHRQ_SUCC); pss->parser_state = SSHS_MSG_EAT_PADDING; @@ -1737,11 +1763,14 @@ again: */ lwsl_notice("SSH_MSG_CHANNEL_EOF: %d\n", pss->ch_recip); ch = ssh_get_server_ch(pss, pss->ch_recip); - if (!ch) + if (!ch) { + lwsl_notice("unknown ch %d\n", pss->ch_recip); return -1; + } - if (!ch->had_eof) { - ch->had_eof = 1; + if (!ch->scheduled_close) { + lwsl_notice("scheduling CLOSE\n"); + ch->scheduled_close = 1; write_task(pss, ch, SSH_WT_CH_CLOSE); } pss->parser_state = SSHS_MSG_EAT_PADDING; @@ -1781,6 +1810,7 @@ again: } ch->received_close = 1; + ch->scheduled_close = 1; write_task(pss, ch, SSH_WT_CH_CLOSE); break; @@ -2015,7 +2045,7 @@ lws_callback_raw_sshd(struct lws *wsi, enum lws_callback_reasons reason, * The user code ops api_version has to be current */ if (vhd->ops->api_version != LWS_SSH_OPS_VERSION) { - lwsl_err("FATAL ops is api_version v%d but code is v%d", + lwsl_err("FATAL ops is api_version v%d but code is v%d\n", vhd->ops->api_version, LWS_SSH_OPS_VERSION); return 1; } @@ -2318,6 +2348,21 @@ lws_callback_raw_sshd(struct lws *wsi, enum lws_callback_reasons reason, lwsl_info("send SSH_MSG_CHANNEL_WINDOW_ADJUST\n"); goto pac; + case SSH_WT_EXIT_STATUS: + pp = ps + 5; + *pp++ = SSH_MSG_CHANNEL_REQUEST; + lws_p32(pp, ch->sender_ch); + pp += 4; + lws_p32(pp, 11); + pp += 4; + strcpy((char *)pp, "exit-status"); + pp += 11; + *pp++ = 0; + lws_p32(pp, ch->retcode); + pp += 4; + lwsl_info("send SSH_MSG_CHANNEL_EXIT_STATUS\n"); + goto pac; + case SSH_WT_NONE: default: /* sending payload */ @@ -2432,7 +2477,7 @@ bail: if (o != SSH_WT_NONE) pss->wt_tail = - (pss->wt_tail + 1) & 3; + (pss->wt_tail + 1) & 7; } else if (o == SSH_WT_UA_PK_OK) /* free it either way */ free(ps); @@ -2498,6 +2543,7 @@ bail: if (ch->spawn_pid == len) { lwsl_notice("starting close of ch with PID %d\n", (int)len); + ch->scheduled_close = 1; write_task(pss, ch, SSH_WT_CH_CLOSE); break; } diff --git a/test-apps/test-sshd.c b/test-apps/test-sshd.c index add5e231..27f0f38d 100644 --- a/test-apps/test-sshd.c +++ b/test-apps/test-sshd.c @@ -545,7 +545,7 @@ ssh_ops_child_process_terminated(void *priv, struct lws *wsi) } static int -ssh_ops_exec(void *_priv, struct lws *wsi, const char *command) +ssh_ops_exec(void *_priv, struct lws *wsi, const char *command, lws_ssh_finish_exec finish, void *finish_handle) { lwsl_notice("%s: EXEC %s\n", __func__, command); @@ -554,7 +554,7 @@ ssh_ops_exec(void *_priv, struct lws *wsi, const char *command) } static int -ssh_ops_shell(void *_priv, struct lws *wsi) +ssh_ops_shell(void *_priv, struct lws *wsi, lws_ssh_finish_exec finish, void *finish_handle) { struct sshd_instance_priv *priv = _priv; const char *cmd[] = { @@ -614,7 +614,7 @@ static const struct lws_ssh_ops ssh_ops = { .banner = ssh_ops_banner, .disconnect_reason = ssh_ops_disconnect_reason, .server_string = "SSH-2.0-Libwebsockets", - .api_version = 1, + .api_version = 2, }; /*