xref: /kernel/liteos_a/net/telnet/src/telnet_loop.c (revision 0d163575)
10d163575Sopenharmony_ci/*
20d163575Sopenharmony_ci * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
30d163575Sopenharmony_ci * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
40d163575Sopenharmony_ci *
50d163575Sopenharmony_ci * Redistribution and use in source and binary forms, with or without modification,
60d163575Sopenharmony_ci * are permitted provided that the following conditions are met:
70d163575Sopenharmony_ci *
80d163575Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright notice, this list of
90d163575Sopenharmony_ci *    conditions and the following disclaimer.
100d163575Sopenharmony_ci *
110d163575Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright notice, this list
120d163575Sopenharmony_ci *    of conditions and the following disclaimer in the documentation and/or other materials
130d163575Sopenharmony_ci *    provided with the distribution.
140d163575Sopenharmony_ci *
150d163575Sopenharmony_ci * 3. Neither the name of the copyright holder nor the names of its contributors may be used
160d163575Sopenharmony_ci *    to endorse or promote products derived from this software without specific prior written
170d163575Sopenharmony_ci *    permission.
180d163575Sopenharmony_ci *
190d163575Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
200d163575Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
210d163575Sopenharmony_ci * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
220d163575Sopenharmony_ci * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
230d163575Sopenharmony_ci * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
240d163575Sopenharmony_ci * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
250d163575Sopenharmony_ci * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
260d163575Sopenharmony_ci * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
270d163575Sopenharmony_ci * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
280d163575Sopenharmony_ci * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
290d163575Sopenharmony_ci * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
300d163575Sopenharmony_ci */
310d163575Sopenharmony_ci
320d163575Sopenharmony_ci#include "telnet_loop.h"
330d163575Sopenharmony_ci#ifdef LOSCFG_NET_TELNET
340d163575Sopenharmony_ci#include "stdio.h"
350d163575Sopenharmony_ci#include "stdlib.h"
360d163575Sopenharmony_ci#include "unistd.h"
370d163575Sopenharmony_ci#include "pthread.h"
380d163575Sopenharmony_ci#include "netinet/tcp.h"
390d163575Sopenharmony_ci#include "sys/select.h"
400d163575Sopenharmony_ci#include "sys/types.h"
410d163575Sopenharmony_ci#include "sys/prctl.h"
420d163575Sopenharmony_ci
430d163575Sopenharmony_ci#include "los_task.h"
440d163575Sopenharmony_ci#include "linux/atomic.h"
450d163575Sopenharmony_ci#include "lwip/sockets.h"
460d163575Sopenharmony_ci#include "lwip/inet.h"
470d163575Sopenharmony_ci#include "lwip/netif.h"
480d163575Sopenharmony_ci#include "console.h"
490d163575Sopenharmony_ci#ifdef LOSCFG_SHELL
500d163575Sopenharmony_ci#include "shell.h"
510d163575Sopenharmony_ci#include "shcmd.h"
520d163575Sopenharmony_ci#endif
530d163575Sopenharmony_ci#include "telnet_pri.h"
540d163575Sopenharmony_ci#include "telnet_dev.h"
550d163575Sopenharmony_ci
560d163575Sopenharmony_ci
570d163575Sopenharmony_ci/* TELNET commands in RFC854 */
580d163575Sopenharmony_ci#define TELNET_SB   250 /* Indicates that what follows is subnegotiation of the indicated option */
590d163575Sopenharmony_ci#define TELNET_WILL 251 /* Indicates the desire to perform the indicated option */
600d163575Sopenharmony_ci#define TELNET_DO   253 /* Indicates the request for the other party to perform the indicated option */
610d163575Sopenharmony_ci#define TELNET_IAC  255 /* Interpret as Command */
620d163575Sopenharmony_ci
630d163575Sopenharmony_ci/* telnet options in IANA */
640d163575Sopenharmony_ci#define TELNET_ECHO 1    /* Echo */
650d163575Sopenharmony_ci#define TELNET_SGA  3    /* Suppress Go Ahead */
660d163575Sopenharmony_ci#define TELNET_NAWS 31   /* Negotiate About Window Size */
670d163575Sopenharmony_ci#define TELNET_NOP  0xf1 /* Unassigned in IANA, putty use this to keepalive */
680d163575Sopenharmony_ci
690d163575Sopenharmony_ci#define LEN_IAC_CMD      2 /* Only 2 char: |IAC|cmd| */
700d163575Sopenharmony_ci#define LEN_IAC_CMD_OPT  3 /* Only 3 char: |IAC|cmd|option| */
710d163575Sopenharmony_ci#define LEN_IAC_CMD_NAWS 9 /* NAWS: |IAC|SB|NAWS|x1|x2|x3|x4|IAC|SE| */
720d163575Sopenharmony_ci
730d163575Sopenharmony_ci/* server/client settings */
740d163575Sopenharmony_ci#define TELNET_TASK_STACK_SIZE  0x2000
750d163575Sopenharmony_ci#define TELNET_TASK_PRIORITY    9
760d163575Sopenharmony_ci
770d163575Sopenharmony_ci/* server settings */
780d163575Sopenharmony_ci#define TELNET_LISTEN_BACKLOG   128
790d163575Sopenharmony_ci#define TELNET_ACCEPT_INTERVAL  200
800d163575Sopenharmony_ci
810d163575Sopenharmony_ci/* client settings */
820d163575Sopenharmony_ci#define TELNET_CLIENT_POLL_TIMEOUT  2000
830d163575Sopenharmony_ci#define TELNET_CLIENT_READ_BUF_SIZE 256
840d163575Sopenharmony_ci#define TELNET_CLIENT_READ_FILTER_BUF_SIZE (8 * 1024)
850d163575Sopenharmony_ci
860d163575Sopenharmony_ci/* limitation: only support 1 telnet client connection */
870d163575Sopenharmony_ciSTATIC volatile INT32 g_telnetClientFd = -1;  /* client fd */
880d163575Sopenharmony_ci
890d163575Sopenharmony_ci/* limitation: only support 1 telnet server */
900d163575Sopenharmony_ciSTATIC volatile INT32 g_telnetListenFd = -1;  /* listen fd of telnetd */
910d163575Sopenharmony_ci
920d163575Sopenharmony_ci/* each bit for a client connection, although only support 1 connection for now */
930d163575Sopenharmony_ciSTATIC volatile UINT32 g_telnetMask = 0;
940d163575Sopenharmony_ci/* taskID of telnetd */
950d163575Sopenharmony_ciSTATIC atomic_t g_telnetTaskId = 0;
960d163575Sopenharmony_ci/* protect listenFd, clientFd etc. */
970d163575Sopenharmony_cipthread_mutex_t g_telnetMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
980d163575Sopenharmony_ci
990d163575Sopenharmony_ciVOID TelnetLock(VOID)
1000d163575Sopenharmony_ci{
1010d163575Sopenharmony_ci    (VOID)pthread_mutex_lock(&g_telnetMutex);
1020d163575Sopenharmony_ci}
1030d163575Sopenharmony_ci
1040d163575Sopenharmony_ciVOID TelnetUnlock(VOID)
1050d163575Sopenharmony_ci{
1060d163575Sopenharmony_ci    (VOID)pthread_mutex_unlock(&g_telnetMutex);
1070d163575Sopenharmony_ci}
1080d163575Sopenharmony_ci
1090d163575Sopenharmony_ci/* filter out iacs from client stream */
1100d163575Sopenharmony_ciSTATIC UINT8 *ReadFilter(const UINT8 *src, UINT32 srcLen, UINT32 *dstLen)
1110d163575Sopenharmony_ci{
1120d163575Sopenharmony_ci    STATIC UINT8 buf[TELNET_CLIENT_READ_FILTER_BUF_SIZE];
1130d163575Sopenharmony_ci    UINT8 *dst = buf;
1140d163575Sopenharmony_ci    UINT32 left = srcLen;
1150d163575Sopenharmony_ci
1160d163575Sopenharmony_ci    while (left > 0) {
1170d163575Sopenharmony_ci        if (*src != TELNET_IAC) {
1180d163575Sopenharmony_ci            *dst = *src;
1190d163575Sopenharmony_ci            dst++;
1200d163575Sopenharmony_ci            src++;
1210d163575Sopenharmony_ci            left--;
1220d163575Sopenharmony_ci            continue;
1230d163575Sopenharmony_ci        }
1240d163575Sopenharmony_ci
1250d163575Sopenharmony_ci        /*
1260d163575Sopenharmony_ci         * if starting with IAC, filter out IAC as following
1270d163575Sopenharmony_ci         * |IAC|          --> skip
1280d163575Sopenharmony_ci         * |IAC|NOP|...   --> ... : skip for putty keepalive etc.
1290d163575Sopenharmony_ci         * |IAC|x|        --> skip
1300d163575Sopenharmony_ci         * |IAC|IAC|x|... --> |IAC|... : skip for literal cmds
1310d163575Sopenharmony_ci         * |IAC|SB|NAWS|x1|x2|x3|x4|IAC|SE|... --> ... : skip NAWS(unsupported)
1320d163575Sopenharmony_ci         * |IAC|x|x|...   --> ... : skip unsupported IAC
1330d163575Sopenharmony_ci         */
1340d163575Sopenharmony_ci        if (left == 1) {
1350d163575Sopenharmony_ci            break;
1360d163575Sopenharmony_ci        }
1370d163575Sopenharmony_ci        /* left no less than 2 */
1380d163575Sopenharmony_ci        if (*(src + 1) == TELNET_NOP) {
1390d163575Sopenharmony_ci            src += LEN_IAC_CMD;
1400d163575Sopenharmony_ci            left -= LEN_IAC_CMD;
1410d163575Sopenharmony_ci            continue;
1420d163575Sopenharmony_ci        }
1430d163575Sopenharmony_ci        if (left == LEN_IAC_CMD) {
1440d163575Sopenharmony_ci            break;
1450d163575Sopenharmony_ci        }
1460d163575Sopenharmony_ci        /* left no less than 3 */
1470d163575Sopenharmony_ci        if (*(src + 1) == TELNET_IAC) {
1480d163575Sopenharmony_ci            *dst = TELNET_IAC;
1490d163575Sopenharmony_ci            dst++;
1500d163575Sopenharmony_ci            src += LEN_IAC_CMD;
1510d163575Sopenharmony_ci            left -= LEN_IAC_CMD;
1520d163575Sopenharmony_ci            continue;
1530d163575Sopenharmony_ci        }
1540d163575Sopenharmony_ci        if ((*(src + 1) == TELNET_SB) && (*(src + LEN_IAC_CMD) == TELNET_NAWS)) {
1550d163575Sopenharmony_ci            if (left > LEN_IAC_CMD_NAWS) {
1560d163575Sopenharmony_ci                src += LEN_IAC_CMD_NAWS;
1570d163575Sopenharmony_ci                left -= LEN_IAC_CMD_NAWS;
1580d163575Sopenharmony_ci                continue;
1590d163575Sopenharmony_ci            }
1600d163575Sopenharmony_ci            break;
1610d163575Sopenharmony_ci        }
1620d163575Sopenharmony_ci        src += LEN_IAC_CMD_OPT;
1630d163575Sopenharmony_ci        left -= LEN_IAC_CMD_OPT;
1640d163575Sopenharmony_ci    }
1650d163575Sopenharmony_ci
1660d163575Sopenharmony_ci    if (dstLen != NULL) {
1670d163575Sopenharmony_ci        *dstLen = dst - buf;
1680d163575Sopenharmony_ci    }
1690d163575Sopenharmony_ci    return buf;
1700d163575Sopenharmony_ci}
1710d163575Sopenharmony_ci
1720d163575Sopenharmony_ci/*
1730d163575Sopenharmony_ci * Description : Write data to fd.
1740d163575Sopenharmony_ci * Input       : fd     --- the fd to write.
1750d163575Sopenharmony_ci *             : src    --- data pointer.
1760d163575Sopenharmony_ci *             : srcLen --- data length.
1770d163575Sopenharmony_ci * Return      : length of written data.
1780d163575Sopenharmony_ci */
1790d163575Sopenharmony_ciSTATIC ssize_t WriteToFd(INT32 fd, const CHAR *src, size_t srcLen)
1800d163575Sopenharmony_ci{
1810d163575Sopenharmony_ci    size_t sizeLeft;
1820d163575Sopenharmony_ci    ssize_t sizeWritten;
1830d163575Sopenharmony_ci
1840d163575Sopenharmony_ci    sizeLeft = srcLen;
1850d163575Sopenharmony_ci    while (sizeLeft > 0) {
1860d163575Sopenharmony_ci        sizeWritten = write(fd, src, sizeLeft);
1870d163575Sopenharmony_ci        if (sizeWritten < 0) {
1880d163575Sopenharmony_ci            /* last write failed */
1890d163575Sopenharmony_ci            if (sizeLeft == srcLen) {
1900d163575Sopenharmony_ci                /* nothing was written in any loop */
1910d163575Sopenharmony_ci                return -1;
1920d163575Sopenharmony_ci            } else {
1930d163575Sopenharmony_ci                /* something was written in previous loop */
1940d163575Sopenharmony_ci                break;
1950d163575Sopenharmony_ci            }
1960d163575Sopenharmony_ci        } else if (sizeWritten == 0) {
1970d163575Sopenharmony_ci            break;
1980d163575Sopenharmony_ci        }
1990d163575Sopenharmony_ci        sizeLeft -= (size_t)sizeWritten;
2000d163575Sopenharmony_ci        src += sizeWritten;
2010d163575Sopenharmony_ci    }
2020d163575Sopenharmony_ci
2030d163575Sopenharmony_ci    return (ssize_t)(srcLen - sizeLeft);
2040d163575Sopenharmony_ci}
2050d163575Sopenharmony_ci
2060d163575Sopenharmony_ci/* Try to remove the client device if there is any client connection */
2070d163575Sopenharmony_ciSTATIC VOID TelnetClientClose(VOID)
2080d163575Sopenharmony_ci{
2090d163575Sopenharmony_ci    /* check if there is any client connection */
2100d163575Sopenharmony_ci    if (g_telnetMask == 0) {
2110d163575Sopenharmony_ci        return;
2120d163575Sopenharmony_ci    }
2130d163575Sopenharmony_ci    (VOID)TelnetDevDeinit();
2140d163575Sopenharmony_ci    g_telnetMask = 0;
2150d163575Sopenharmony_ci    printf("telnet client disconnected.\n");
2160d163575Sopenharmony_ci}
2170d163575Sopenharmony_ci
2180d163575Sopenharmony_ci/* Release the client and server fd */
2190d163575Sopenharmony_ciSTATIC VOID TelnetRelease(VOID)
2200d163575Sopenharmony_ci{
2210d163575Sopenharmony_ci    if (g_telnetClientFd >= 0) {
2220d163575Sopenharmony_ci        (VOID)close(g_telnetClientFd);
2230d163575Sopenharmony_ci        g_telnetClientFd = -1;
2240d163575Sopenharmony_ci    }
2250d163575Sopenharmony_ci
2260d163575Sopenharmony_ci    if (g_telnetListenFd >= 0) {
2270d163575Sopenharmony_ci        (VOID)close(g_telnetListenFd);
2280d163575Sopenharmony_ci        g_telnetListenFd = -1;
2290d163575Sopenharmony_ci    }
2300d163575Sopenharmony_ci}
2310d163575Sopenharmony_ci
2320d163575Sopenharmony_ci/* Stop telnet server */
2330d163575Sopenharmony_ciSTATIC VOID TelnetdDeinit(VOID)
2340d163575Sopenharmony_ci{
2350d163575Sopenharmony_ci    if (atomic_read(&g_telnetTaskId) == 0) {
2360d163575Sopenharmony_ci        PRINTK("telnet server is not running!\n");
2370d163575Sopenharmony_ci        return;
2380d163575Sopenharmony_ci    }
2390d163575Sopenharmony_ci
2400d163575Sopenharmony_ci    TelnetRelease();
2410d163575Sopenharmony_ci    (VOID)TelnetedUnregister();
2420d163575Sopenharmony_ci    atomic_set(&g_telnetTaskId, 0);
2430d163575Sopenharmony_ci    PRINTK("telnet server closed.\n");
2440d163575Sopenharmony_ci}
2450d163575Sopenharmony_ci
2460d163575Sopenharmony_ci/*
2470d163575Sopenharmony_ci * Description : Setup the listen fd for telnetd with specific port.
2480d163575Sopenharmony_ci * Input       : port --- the port at which telnet server listens.
2490d163575Sopenharmony_ci * Return      : -1           --- on error
2500d163575Sopenharmony_ci *             : non-negative --- listen fd if OK
2510d163575Sopenharmony_ci */
2520d163575Sopenharmony_ciSTATIC INT32 TelnetdInit(UINT16 port)
2530d163575Sopenharmony_ci{
2540d163575Sopenharmony_ci    INT32 listenFd;
2550d163575Sopenharmony_ci    INT32 reuseAddr = 1;
2560d163575Sopenharmony_ci    struct sockaddr_in inTelnetAddr;
2570d163575Sopenharmony_ci
2580d163575Sopenharmony_ci    listenFd = socket(AF_INET, SOCK_STREAM, 0);
2590d163575Sopenharmony_ci    if (listenFd == -1) {
2600d163575Sopenharmony_ci        PRINT_ERR("TelnetdInit : socket error.\n");
2610d163575Sopenharmony_ci        goto ERR_OUT;
2620d163575Sopenharmony_ci    }
2630d163575Sopenharmony_ci
2640d163575Sopenharmony_ci    /* reuse listen port */
2650d163575Sopenharmony_ci    if (setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) != 0) {
2660d163575Sopenharmony_ci        PRINT_ERR("TelnetdInit : setsockopt REUSEADDR error.\n");
2670d163575Sopenharmony_ci        goto ERR_CLOSE_FD;
2680d163575Sopenharmony_ci    }
2690d163575Sopenharmony_ci
2700d163575Sopenharmony_ci    (VOID)memset_s(&inTelnetAddr, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
2710d163575Sopenharmony_ci    inTelnetAddr.sin_family = AF_INET;
2720d163575Sopenharmony_ci    inTelnetAddr.sin_addr.s_addr = INADDR_ANY;
2730d163575Sopenharmony_ci    inTelnetAddr.sin_port = htons(port);
2740d163575Sopenharmony_ci
2750d163575Sopenharmony_ci    if (bind(listenFd, (const struct sockaddr *)&inTelnetAddr, sizeof(struct sockaddr_in)) == -1) {
2760d163575Sopenharmony_ci        PRINT_ERR("TelnetdInit : bind error.\n");
2770d163575Sopenharmony_ci        goto ERR_CLOSE_FD;
2780d163575Sopenharmony_ci    }
2790d163575Sopenharmony_ci
2800d163575Sopenharmony_ci    if (listen(listenFd, TELNET_LISTEN_BACKLOG) == -1) {
2810d163575Sopenharmony_ci        PRINT_ERR("TelnetdInit : listen error.\n");
2820d163575Sopenharmony_ci        goto ERR_CLOSE_FD;
2830d163575Sopenharmony_ci    }
2840d163575Sopenharmony_ci
2850d163575Sopenharmony_ci    return listenFd;
2860d163575Sopenharmony_ci
2870d163575Sopenharmony_ciERR_CLOSE_FD:
2880d163575Sopenharmony_ci    (VOID)close(listenFd);
2890d163575Sopenharmony_ciERR_OUT:
2900d163575Sopenharmony_ci    return -1;
2910d163575Sopenharmony_ci}
2920d163575Sopenharmony_ci
2930d163575Sopenharmony_ciSTATIC INT32 TelnetClientPrepare(INT32 clientFd)
2940d163575Sopenharmony_ci{
2950d163575Sopenharmony_ci    INT32 keepAlive = TELNET_KEEPALIVE;
2960d163575Sopenharmony_ci    INT32 keepIdle = TELNET_KEEPIDLE;
2970d163575Sopenharmony_ci    INT32 keepInterval = TELNET_KEEPINTV;
2980d163575Sopenharmony_ci    INT32 keepCnt = TELNET_KEEPCNT;
2990d163575Sopenharmony_ci    const UINT8 doEcho[] = { TELNET_IAC, TELNET_DO, TELNET_ECHO };
3000d163575Sopenharmony_ci    const UINT8 doNaws[] = { TELNET_IAC, TELNET_DO, TELNET_NAWS };
3010d163575Sopenharmony_ci    const UINT8 willEcho[] = { TELNET_IAC, TELNET_WILL, TELNET_ECHO };
3020d163575Sopenharmony_ci    const UINT8 willSga[] = { TELNET_IAC, TELNET_WILL, TELNET_SGA };
3030d163575Sopenharmony_ci
3040d163575Sopenharmony_ci    if (g_telnetListenFd == -1) {
3050d163575Sopenharmony_ci        return -1;
3060d163575Sopenharmony_ci    }
3070d163575Sopenharmony_ci    g_telnetClientFd = clientFd;
3080d163575Sopenharmony_ci    if (TelnetDevInit(clientFd) != 0) {
3090d163575Sopenharmony_ci        g_telnetClientFd = -1;
3100d163575Sopenharmony_ci        return -1;
3110d163575Sopenharmony_ci    }
3120d163575Sopenharmony_ci    g_telnetMask = 1;
3130d163575Sopenharmony_ci
3140d163575Sopenharmony_ci    /* negotiate with client */
3150d163575Sopenharmony_ci    (VOID)WriteToFd(clientFd, (CHAR *)doEcho, sizeof(doEcho));
3160d163575Sopenharmony_ci    (VOID)WriteToFd(clientFd, (CHAR *)doNaws, sizeof(doNaws));
3170d163575Sopenharmony_ci    (VOID)WriteToFd(clientFd, (CHAR *)willEcho, sizeof(willEcho));
3180d163575Sopenharmony_ci    (VOID)WriteToFd(clientFd, (CHAR *)willSga, sizeof(willSga));
3190d163575Sopenharmony_ci
3200d163575Sopenharmony_ci    /* enable TCP keepalive to check whether telnet link is alive */
3210d163575Sopenharmony_ci    if (setsockopt(clientFd, SOL_SOCKET, SO_KEEPALIVE, (VOID *)&keepAlive, sizeof(keepAlive)) < 0) {
3220d163575Sopenharmony_ci        PRINT_ERR("telnet setsockopt SO_KEEPALIVE error.\n");
3230d163575Sopenharmony_ci    }
3240d163575Sopenharmony_ci    if (setsockopt(clientFd, IPPROTO_TCP, TCP_KEEPIDLE, (VOID *)&keepIdle, sizeof(keepIdle)) < 0) {
3250d163575Sopenharmony_ci        PRINT_ERR("telnet setsockopt TCP_KEEPIDLE error.\n");
3260d163575Sopenharmony_ci    }
3270d163575Sopenharmony_ci    if (setsockopt(clientFd, IPPROTO_TCP, TCP_KEEPINTVL, (VOID *)&keepInterval, sizeof(keepInterval)) < 0) {
3280d163575Sopenharmony_ci        PRINT_ERR("telnet setsockopt TCP_KEEPINTVL error.\n");
3290d163575Sopenharmony_ci    }
3300d163575Sopenharmony_ci    if (setsockopt(clientFd, IPPROTO_TCP, TCP_KEEPCNT, (VOID *)&keepCnt, sizeof(keepCnt)) < 0) {
3310d163575Sopenharmony_ci        PRINT_ERR("telnet setsockopt TCP_KEEPCNT error.\n");
3320d163575Sopenharmony_ci    }
3330d163575Sopenharmony_ci    return 0;
3340d163575Sopenharmony_ci}
3350d163575Sopenharmony_ci
3360d163575Sopenharmony_ciSTATIC VOID *TelnetClientLoop(VOID *arg)
3370d163575Sopenharmony_ci{
3380d163575Sopenharmony_ci    struct pollfd pollFd;
3390d163575Sopenharmony_ci    INT32 ret;
3400d163575Sopenharmony_ci    INT32 nRead;
3410d163575Sopenharmony_ci    UINT32 len;
3420d163575Sopenharmony_ci    UINT8 buf[TELNET_CLIENT_READ_BUF_SIZE];
3430d163575Sopenharmony_ci    UINT8 *cmdBuf = NULL;
3440d163575Sopenharmony_ci    INT32 clientFd = (INT32)(UINTPTR)arg;
3450d163575Sopenharmony_ci
3460d163575Sopenharmony_ci    (VOID)prctl(PR_SET_NAME, "TelnetClientLoop", 0, 0, 0);
3470d163575Sopenharmony_ci    TelnetLock();
3480d163575Sopenharmony_ci    if (TelnetClientPrepare(clientFd) != 0) {
3490d163575Sopenharmony_ci        TelnetUnlock();
3500d163575Sopenharmony_ci        (VOID)close(clientFd);
3510d163575Sopenharmony_ci        return NULL;
3520d163575Sopenharmony_ci    }
3530d163575Sopenharmony_ci    TelnetUnlock();
3540d163575Sopenharmony_ci
3550d163575Sopenharmony_ci    while (1) {
3560d163575Sopenharmony_ci        pollFd.fd = clientFd;
3570d163575Sopenharmony_ci        pollFd.events = POLLIN | POLLRDHUP;
3580d163575Sopenharmony_ci        pollFd.revents = 0;
3590d163575Sopenharmony_ci
3600d163575Sopenharmony_ci        ret = poll(&pollFd, 1, TELNET_CLIENT_POLL_TIMEOUT);
3610d163575Sopenharmony_ci        if (ret < 0) {
3620d163575Sopenharmony_ci            break;
3630d163575Sopenharmony_ci        }
3640d163575Sopenharmony_ci        if (ret == 0) {
3650d163575Sopenharmony_ci            continue;
3660d163575Sopenharmony_ci        }
3670d163575Sopenharmony_ci        /* connection reset, maybe keepalive failed or reset by peer */
3680d163575Sopenharmony_ci        if ((UINT16)pollFd.revents & (POLLERR | POLLHUP | POLLRDHUP)) {
3690d163575Sopenharmony_ci            break;
3700d163575Sopenharmony_ci        }
3710d163575Sopenharmony_ci
3720d163575Sopenharmony_ci        if ((UINT16)pollFd.revents & POLLIN) {
3730d163575Sopenharmony_ci            nRead = read(clientFd, buf, sizeof(buf));
3740d163575Sopenharmony_ci            if (nRead <= 0) {
3750d163575Sopenharmony_ci                /* telnet client shutdown */
3760d163575Sopenharmony_ci                break;
3770d163575Sopenharmony_ci            }
3780d163575Sopenharmony_ci            cmdBuf = ReadFilter(buf, (UINT32)nRead, &len);
3790d163575Sopenharmony_ci            if (len > 0) {
3800d163575Sopenharmony_ci                (VOID)TelnetTx((CHAR *)cmdBuf, len);
3810d163575Sopenharmony_ci            }
3820d163575Sopenharmony_ci        }
3830d163575Sopenharmony_ci    }
3840d163575Sopenharmony_ci    TelnetLock();
3850d163575Sopenharmony_ci    TelnetClientClose();
3860d163575Sopenharmony_ci    (VOID)close(clientFd);
3870d163575Sopenharmony_ci    clientFd = -1;
3880d163575Sopenharmony_ci    g_telnetClientFd = -1;
3890d163575Sopenharmony_ci    TelnetUnlock();
3900d163575Sopenharmony_ci    return NULL;
3910d163575Sopenharmony_ci}
3920d163575Sopenharmony_ci
3930d163575Sopenharmony_ciSTATIC VOID TelnetClientTaskAttr(pthread_attr_t *threadAttr)
3940d163575Sopenharmony_ci{
3950d163575Sopenharmony_ci    (VOID)pthread_attr_init(threadAttr);
3960d163575Sopenharmony_ci    threadAttr->inheritsched = PTHREAD_EXPLICIT_SCHED;
3970d163575Sopenharmony_ci    threadAttr->schedparam.sched_priority = TELNET_TASK_PRIORITY;
3980d163575Sopenharmony_ci    threadAttr->detachstate = PTHREAD_CREATE_DETACHED;
3990d163575Sopenharmony_ci    (VOID)pthread_attr_setstacksize(threadAttr, TELNET_TASK_STACK_SIZE);
4000d163575Sopenharmony_ci}
4010d163575Sopenharmony_ci
4020d163575Sopenharmony_ci/*
4030d163575Sopenharmony_ci * Description : Handle the client connection request.
4040d163575Sopenharmony_ci *               Create a client connection if permitted.
4050d163575Sopenharmony_ci * Return      : 0  --- please continue the server accept loop
4060d163575Sopenharmony_ci *             : -1 --- please stop the server accept loop.
4070d163575Sopenharmony_ci */
4080d163575Sopenharmony_ciSTATIC INT32 TelnetdAcceptClient(INT32 clientFd, const struct sockaddr_in *inTelnetAddr)
4090d163575Sopenharmony_ci{
4100d163575Sopenharmony_ci    INT32 ret = 0;
4110d163575Sopenharmony_ci    pthread_t tmp;
4120d163575Sopenharmony_ci    pthread_attr_t useAttr;
4130d163575Sopenharmony_ci
4140d163575Sopenharmony_ci    TelnetClientTaskAttr(&useAttr);
4150d163575Sopenharmony_ci
4160d163575Sopenharmony_ci    if (clientFd < 0) {
4170d163575Sopenharmony_ci        ret = -1;
4180d163575Sopenharmony_ci        goto ERROUT;
4190d163575Sopenharmony_ci    }
4200d163575Sopenharmony_ci
4210d163575Sopenharmony_ci    TelnetLock();
4220d163575Sopenharmony_ci
4230d163575Sopenharmony_ci    if (g_telnetListenFd == -1) {
4240d163575Sopenharmony_ci        /* server already has been closed, so quit this task now */
4250d163575Sopenharmony_ci        ret = -1;
4260d163575Sopenharmony_ci        goto ERROUT_UNLOCK;
4270d163575Sopenharmony_ci    }
4280d163575Sopenharmony_ci
4290d163575Sopenharmony_ci    if (g_telnetClientFd >= 0) {
4300d163575Sopenharmony_ci        /* already connected and support only one */
4310d163575Sopenharmony_ci        goto ERROUT_UNLOCK;
4320d163575Sopenharmony_ci    }
4330d163575Sopenharmony_ci
4340d163575Sopenharmony_ci    g_telnetClientFd = clientFd;
4350d163575Sopenharmony_ci
4360d163575Sopenharmony_ci    if (pthread_create(&tmp, &useAttr, TelnetClientLoop, (VOID *)(UINTPTR)clientFd) != 0) {
4370d163575Sopenharmony_ci        PRINT_ERR("Failed to create client handle task\n");
4380d163575Sopenharmony_ci        g_telnetClientFd = -1;
4390d163575Sopenharmony_ci        goto ERROUT_UNLOCK;
4400d163575Sopenharmony_ci    }
4410d163575Sopenharmony_ci    TelnetUnlock();
4420d163575Sopenharmony_ci    return ret;
4430d163575Sopenharmony_ci
4440d163575Sopenharmony_ciERROUT_UNLOCK:
4450d163575Sopenharmony_ci    (VOID)close(clientFd);
4460d163575Sopenharmony_ci    clientFd = -1;
4470d163575Sopenharmony_ci    TelnetUnlock();
4480d163575Sopenharmony_ciERROUT:
4490d163575Sopenharmony_ci    return ret;
4500d163575Sopenharmony_ci}
4510d163575Sopenharmony_ci
4520d163575Sopenharmony_ci/*
4530d163575Sopenharmony_ci * Waiting for client's connection. Only allow 1 connection, and others will be discarded.
4540d163575Sopenharmony_ci */
4550d163575Sopenharmony_ciSTATIC VOID TelnetdAcceptLoop(INT32 listenFd)
4560d163575Sopenharmony_ci{
4570d163575Sopenharmony_ci    INT32 clientFd = -1;
4580d163575Sopenharmony_ci    struct sockaddr_in inTelnetAddr;
4590d163575Sopenharmony_ci    INT32 len = sizeof(inTelnetAddr);
4600d163575Sopenharmony_ci
4610d163575Sopenharmony_ci    TelnetLock();
4620d163575Sopenharmony_ci    g_telnetListenFd = listenFd;
4630d163575Sopenharmony_ci
4640d163575Sopenharmony_ci    while (g_telnetListenFd >= 0) {
4650d163575Sopenharmony_ci        TelnetUnlock();
4660d163575Sopenharmony_ci
4670d163575Sopenharmony_ci        (VOID)memset_s(&inTelnetAddr, sizeof(inTelnetAddr), 0, sizeof(inTelnetAddr));
4680d163575Sopenharmony_ci        clientFd = accept(listenFd, (struct sockaddr *)&inTelnetAddr, (socklen_t *)&len);
4690d163575Sopenharmony_ci        if (TelnetdAcceptClient(clientFd, &inTelnetAddr) == 0) {
4700d163575Sopenharmony_ci            /*
4710d163575Sopenharmony_ci             * Sleep sometime before next loop: mostly we already have one connection here,
4720d163575Sopenharmony_ci             * and the next connection will be declined. So don't waste our cpu.
4730d163575Sopenharmony_ci             */
4740d163575Sopenharmony_ci            LOS_Msleep(TELNET_ACCEPT_INTERVAL);
4750d163575Sopenharmony_ci        } else {
4760d163575Sopenharmony_ci            return;
4770d163575Sopenharmony_ci        }
4780d163575Sopenharmony_ci        TelnetLock();
4790d163575Sopenharmony_ci    }
4800d163575Sopenharmony_ci    TelnetUnlock();
4810d163575Sopenharmony_ci}
4820d163575Sopenharmony_ci
4830d163575Sopenharmony_ciSTATIC INT32 TelnetdMain(VOID)
4840d163575Sopenharmony_ci{
4850d163575Sopenharmony_ci    INT32 sock;
4860d163575Sopenharmony_ci    INT32 ret;
4870d163575Sopenharmony_ci
4880d163575Sopenharmony_ci    sock = TelnetdInit(TELNETD_PORT);
4890d163575Sopenharmony_ci    if (sock == -1) {
4900d163575Sopenharmony_ci        PRINT_ERR("telnet init error.\n");
4910d163575Sopenharmony_ci        return -1;
4920d163575Sopenharmony_ci    }
4930d163575Sopenharmony_ci
4940d163575Sopenharmony_ci    TelnetLock();
4950d163575Sopenharmony_ci    ret = TelnetedRegister();
4960d163575Sopenharmony_ci    TelnetUnlock();
4970d163575Sopenharmony_ci
4980d163575Sopenharmony_ci    if (ret != 0) {
4990d163575Sopenharmony_ci        PRINT_ERR("telnet register error.\n");
5000d163575Sopenharmony_ci        (VOID)close(sock);
5010d163575Sopenharmony_ci        return -1;
5020d163575Sopenharmony_ci    }
5030d163575Sopenharmony_ci
5040d163575Sopenharmony_ci    PRINTK("start telnet server successfully, waiting for connection.\n");
5050d163575Sopenharmony_ci    TelnetdAcceptLoop(sock);
5060d163575Sopenharmony_ci    return 0;
5070d163575Sopenharmony_ci}
5080d163575Sopenharmony_ci
5090d163575Sopenharmony_ci/*
5100d163575Sopenharmony_ci * Try to create telnetd task.
5110d163575Sopenharmony_ci */
5120d163575Sopenharmony_ciSTATIC VOID TelnetdTaskInit(VOID)
5130d163575Sopenharmony_ci{
5140d163575Sopenharmony_ci    UINT32 ret;
5150d163575Sopenharmony_ci    TSK_INIT_PARAM_S initParam = {0};
5160d163575Sopenharmony_ci
5170d163575Sopenharmony_ci    initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)TelnetdMain;
5180d163575Sopenharmony_ci    initParam.uwStackSize = TELNET_TASK_STACK_SIZE;
5190d163575Sopenharmony_ci    initParam.pcName = "TelnetServer";
5200d163575Sopenharmony_ci    initParam.usTaskPrio = TELNET_TASK_PRIORITY;
5210d163575Sopenharmony_ci    initParam.uwResved = LOS_TASK_STATUS_DETACHED;
5220d163575Sopenharmony_ci
5230d163575Sopenharmony_ci    if (atomic_read(&g_telnetTaskId) != 0) {
5240d163575Sopenharmony_ci        PRINT_ERR("telnet server is already running!\n");
5250d163575Sopenharmony_ci        return;
5260d163575Sopenharmony_ci    }
5270d163575Sopenharmony_ci
5280d163575Sopenharmony_ci    ret = LOS_TaskCreate((UINT32 *)&g_telnetTaskId, &initParam);
5290d163575Sopenharmony_ci    if (ret != LOS_OK) {
5300d163575Sopenharmony_ci        PRINT_ERR("failed to create telnet server task!\n");
5310d163575Sopenharmony_ci    }
5320d163575Sopenharmony_ci}
5330d163575Sopenharmony_ci
5340d163575Sopenharmony_ci/*
5350d163575Sopenharmony_ci * Try to destroy telnetd task.
5360d163575Sopenharmony_ci */
5370d163575Sopenharmony_ciSTATIC VOID TelnetdTaskDeinit(VOID)
5380d163575Sopenharmony_ci{
5390d163575Sopenharmony_ci    if (atomic_read(&g_telnetTaskId) == 0) {
5400d163575Sopenharmony_ci        PRINTK("telnet server is not running, please start up telnet server first.\n");
5410d163575Sopenharmony_ci        return;
5420d163575Sopenharmony_ci    }
5430d163575Sopenharmony_ci
5440d163575Sopenharmony_ci    TelnetLock();
5450d163575Sopenharmony_ci    TelnetClientClose();
5460d163575Sopenharmony_ci    TelnetdDeinit();
5470d163575Sopenharmony_ci    TelnetUnlock();
5480d163575Sopenharmony_ci}
5490d163575Sopenharmony_ci
5500d163575Sopenharmony_ciSTATIC VOID TelnetUsage(VOID)
5510d163575Sopenharmony_ci{
5520d163575Sopenharmony_ci    PRINTK("Usage: telnet [OPTION]...\n");
5530d163575Sopenharmony_ci    PRINTK("Start or close telnet server.\n\n");
5540d163575Sopenharmony_ci    PRINTK("  on       Init the telnet server\n");
5550d163575Sopenharmony_ci    PRINTK("  off      Deinit the telnet server\n");
5560d163575Sopenharmony_ci}
5570d163575Sopenharmony_ci
5580d163575Sopenharmony_ciINT32 TelnetCmd(UINT32 argc, const CHAR **argv)
5590d163575Sopenharmony_ci{
5600d163575Sopenharmony_ci    if (argc != 1) {
5610d163575Sopenharmony_ci        TelnetUsage();
5620d163575Sopenharmony_ci        return 0;
5630d163575Sopenharmony_ci    }
5640d163575Sopenharmony_ci
5650d163575Sopenharmony_ci    if (strcmp(argv[0], "on") == 0) {
5660d163575Sopenharmony_ci        /* telnet on: try to start telnet server task */
5670d163575Sopenharmony_ci        TelnetdTaskInit();
5680d163575Sopenharmony_ci        return 0;
5690d163575Sopenharmony_ci    }
5700d163575Sopenharmony_ci    if (strcmp(argv[0], "off") == 0) {
5710d163575Sopenharmony_ci        /* telnet off: try to stop clients, then stop server task */
5720d163575Sopenharmony_ci        TelnetdTaskDeinit();
5730d163575Sopenharmony_ci        return 0;
5740d163575Sopenharmony_ci    }
5750d163575Sopenharmony_ci
5760d163575Sopenharmony_ci    TelnetUsage();
5770d163575Sopenharmony_ci    return 0;
5780d163575Sopenharmony_ci}
5790d163575Sopenharmony_ci
5800d163575Sopenharmony_ci#ifdef LOSCFG_SHELL_CMD_DEBUG
5810d163575Sopenharmony_ciSHELLCMD_ENTRY(telnet_shellcmd, CMD_TYPE_EX, "telnet", 1, (CmdCallBackFunc)TelnetCmd);
5820d163575Sopenharmony_ci#endif /* LOSCFG_SHELL_CMD_DEBUG */
5830d163575Sopenharmony_ci#endif
5840d163575Sopenharmony_ci
585