| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- /*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010 - 2019 Andy Green <[email protected]>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
- #include "libwebsockets.h"
- #include "lws-ssh.h"
- #include <string.h>
- struct per_vhost_data__telnet {
- struct lws_context *context;
- struct lws_vhost *vhost;
- const struct lws_protocols *protocol;
- struct per_session_data__telnet *live_pss_list;
- const struct lws_ssh_ops *ops;
- };
- struct per_session_data__telnet {
- struct per_session_data__telnet *next;
- struct per_vhost_data__telnet *vhd;
- uint32_t rx_tail;
- void *priv;
- uint32_t initial:1;
- char state;
- uint8_t cmd;
- };
- enum {
- LTS_BINARY_XMIT,
- LTS_ECHO,
- LTS_SUPPRESS_GA,
- LTSC_SUBOPT_END = 240,
- LTSC_BREAK = 243,
- LTSC_SUBOPT_START = 250,
- LTSC_WILL = 251,
- LTSC_WONT,
- LTSC_DO,
- LTSC_DONT,
- LTSC_IAC,
- LTST_WAIT_IAC = 0,
- LTST_GOT_IAC,
- LTST_WAIT_OPT,
- };
- static int
- telnet_ld(struct per_session_data__telnet *pss, uint8_t c)
- {
- switch (pss->state) {
- case LTST_WAIT_IAC:
- if (c == LTSC_IAC) {
- pss->state = LTST_GOT_IAC;
- return 0;
- }
- return 1;
- case LTST_GOT_IAC:
- pss->state = LTST_WAIT_IAC;
- switch (c) {
- case LTSC_BREAK:
- return 0;
- case LTSC_WILL:
- case LTSC_WONT:
- case LTSC_DO:
- case LTSC_DONT:
- pss->cmd = c;
- pss->state = LTST_WAIT_OPT;
- return 0;
- case LTSC_IAC:
- return 1; /* double IAC */
- }
- return 0; /* ignore unknown */
- case LTST_WAIT_OPT:
- lwsl_notice(" tld: cmd %d: opt %d\n", pss->cmd, c);
- pss->state = LTST_WAIT_IAC;
- return 0;
- }
- return 0;
- }
- static uint8_t init[] = {
- LTSC_IAC, LTSC_WILL, 3,
- LTSC_IAC, LTSC_WILL, 1,
- LTSC_IAC, LTSC_DONT, 1,
- LTSC_IAC, LTSC_DO, 0
- };
- static int
- lws_callback_raw_telnet(struct lws *wsi, enum lws_callback_reasons reason,
- void *user, void *in, size_t len)
- {
- struct per_session_data__telnet *pss =
- (struct per_session_data__telnet *)user, **p;
- struct per_vhost_data__telnet *vhd =
- (struct per_vhost_data__telnet *)
- lws_protocol_vh_priv_get(lws_get_vhost(wsi),
- lws_get_protocol(wsi));
- const struct lws_protocol_vhost_options *pvo =
- (const struct lws_protocol_vhost_options *)in;
- int n, m;
- uint8_t buf[LWS_PRE + 800], *pu = in;
- switch ((int)reason) {
- case LWS_CALLBACK_PROTOCOL_INIT:
- vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
- lws_get_protocol(wsi),
- sizeof(struct per_vhost_data__telnet));
- vhd->context = lws_get_context(wsi);
- vhd->protocol = lws_get_protocol(wsi);
- vhd->vhost = lws_get_vhost(wsi);
- while (pvo) {
- if (!strcmp(pvo->name, "ops"))
- vhd->ops = (const struct lws_ssh_ops *)pvo->value;
- pvo = pvo->next;
- }
- if (!vhd->ops) {
- lwsl_err("telnet pvo \"ops\" is mandatory\n");
- return -1;
- }
- break;
- case LWS_CALLBACK_RAW_ADOPT:
- pss->next = vhd->live_pss_list;
- vhd->live_pss_list = pss;
- pss->vhd = vhd;
- pss->state = LTST_WAIT_IAC;
- pss->initial = 0;
- if (vhd->ops->channel_create)
- vhd->ops->channel_create(wsi, &pss->priv);
- lws_callback_on_writable(wsi);
- break;
- case LWS_CALLBACK_RAW_CLOSE:
- p = &vhd->live_pss_list;
- while (*p) {
- if ((*p) == pss) {
- if (vhd->ops->channel_destroy)
- vhd->ops->channel_destroy(pss->priv);
- *p = pss->next;
- continue;
- }
- p = &((*p)->next);
- }
- break;
- case LWS_CALLBACK_RAW_RX:
- n = 0;
- /* this stuff is coming in telnet line discipline, we
- * have to strip IACs and process IAC repeats */
- while (len--) {
- if (telnet_ld(pss, *pu))
- buf[n++] = *pu++;
- else
- pu++;
- if (n > 100 || !len)
- pss->vhd->ops->rx(pss->priv, wsi, buf, n);
- }
- break;
- case LWS_CALLBACK_RAW_WRITEABLE:
- n = 0;
- if (!pss->initial) {
- memcpy(buf + LWS_PRE, init, sizeof(init));
- n = sizeof(init);
- pss->initial = 1;
- } else {
- /* bring any waiting tx into second half of buffer
- * restrict how much we can send to 1/4 of the buffer,
- * because we have to apply telnet line discipline...
- * in the worst case of all 0xff, doubling the size
- */
- pu = buf + LWS_PRE + 400;
- m = (int)pss->vhd->ops->tx(pss->priv, LWS_STDOUT, pu,
- ((int)sizeof(buf) - LWS_PRE - n - 401) / 2);
- /*
- * apply telnet line discipline and copy into place
- * in output buffer
- */
- while (m--) {
- if (*pu == 0xff)
- buf[LWS_PRE + n++] = 0xff;
- buf[LWS_PRE + n++] = *pu++;
- }
- }
- if (n > 0) {
- m = lws_write(wsi, (unsigned char *)buf + LWS_PRE, n,
- LWS_WRITE_HTTP);
- if (m < 0) {
- lwsl_err("ERROR %d writing to di socket\n", m);
- return -1;
- }
- }
- if (vhd->ops->tx_waiting(&pss->priv))
- lws_callback_on_writable(wsi);
- break;
- case LWS_CALLBACK_SSH_UART_SET_RXFLOW:
- /*
- * this is sent to set rxflow state on any connections that
- * sink on a particular uart. The uart index affected is in len
- *
- * More than one protocol may sink to the same uart, and the
- * protocol may select the uart itself, eg, in the URL used
- * to set up the connection.
- */
- lws_rx_flow_control(wsi, len & 1);
- break;
- default:
- break;
- }
- return 0;
- }
- const struct lws_protocols protocols_telnet[] = {
- {
- "lws-telnetd-base",
- lws_callback_raw_telnet,
- sizeof(struct per_session_data__telnet),
- 1024, 0, NULL, 900
- },
- { NULL, NULL, 0, 0, 0, NULL, 0 } /* terminator */
- };
|