• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3  * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this list of
9  *    conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12  *    of conditions and the following disclaimer in the documentation and/or other materials
13  *    provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16  *    to endorse or promote products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "telnet_loop.h"
33 #ifdef LOSCFG_NET_TELNET
34 #include "stdio.h"
35 #include "stdlib.h"
36 #include "unistd.h"
37 #include "pthread.h"
38 #include "netinet/tcp.h"
39 #include "sys/select.h"
40 #include "sys/types.h"
41 #include "sys/prctl.h"
42 
43 #include "los_task.h"
44 #include "linux/atomic.h"
45 #include "lwip/sockets.h"
46 #include "lwip/inet.h"
47 #include "lwip/netif.h"
48 #include "console.h"
49 #ifdef LOSCFG_SHELL
50 #include "shell.h"
51 #include "shcmd.h"
52 #endif
53 #include "telnet_pri.h"
54 #include "telnet_dev.h"
55 
56 
57 /* TELNET commands in RFC854 */
58 #define TELNET_SB   250 /* Indicates that what follows is subnegotiation of the indicated option */
59 #define TELNET_WILL 251 /* Indicates the desire to perform the indicated option */
60 #define TELNET_DO   253 /* Indicates the request for the other party to perform the indicated option */
61 #define TELNET_IAC  255 /* Interpret as Command */
62 
63 /* telnet options in IANA */
64 #define TELNET_ECHO 1    /* Echo */
65 #define TELNET_SGA  3    /* Suppress Go Ahead */
66 #define TELNET_NAWS 31   /* Negotiate About Window Size */
67 #define TELNET_NOP  0xf1 /* Unassigned in IANA, putty use this to keepalive */
68 
69 #define LEN_IAC_CMD      2 /* Only 2 char: |IAC|cmd| */
70 #define LEN_IAC_CMD_OPT  3 /* Only 3 char: |IAC|cmd|option| */
71 #define LEN_IAC_CMD_NAWS 9 /* NAWS: |IAC|SB|NAWS|x1|x2|x3|x4|IAC|SE| */
72 
73 /* server/client settings */
74 #define TELNET_TASK_STACK_SIZE  0x2000
75 #define TELNET_TASK_PRIORITY    9
76 
77 /* server settings */
78 #define TELNET_LISTEN_BACKLOG   128
79 #define TELNET_ACCEPT_INTERVAL  200
80 
81 /* client settings */
82 #define TELNET_CLIENT_POLL_TIMEOUT  2000
83 #define TELNET_CLIENT_READ_BUF_SIZE 256
84 #define TELNET_CLIENT_READ_FILTER_BUF_SIZE (8 * 1024)
85 
86 /* limitation: only support 1 telnet client connection */
87 STATIC volatile INT32 g_telnetClientFd = -1;  /* client fd */
88 
89 /* limitation: only support 1 telnet server */
90 STATIC volatile INT32 g_telnetListenFd = -1;  /* listen fd of telnetd */
91 
92 /* each bit for a client connection, although only support 1 connection for now */
93 STATIC volatile UINT32 g_telnetMask = 0;
94 /* taskID of telnetd */
95 STATIC atomic_t g_telnetTaskId = 0;
96 /* protect listenFd, clientFd etc. */
97 pthread_mutex_t g_telnetMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
98 
TelnetLock(VOID)99 VOID TelnetLock(VOID)
100 {
101     (VOID)pthread_mutex_lock(&g_telnetMutex);
102 }
103 
TelnetUnlock(VOID)104 VOID TelnetUnlock(VOID)
105 {
106     (VOID)pthread_mutex_unlock(&g_telnetMutex);
107 }
108 
109 /* filter out iacs from client stream */
ReadFilter(const UINT8 * src,UINT32 srcLen,UINT32 * dstLen)110 STATIC UINT8 *ReadFilter(const UINT8 *src, UINT32 srcLen, UINT32 *dstLen)
111 {
112     STATIC UINT8 buf[TELNET_CLIENT_READ_FILTER_BUF_SIZE];
113     UINT8 *dst = buf;
114     UINT32 left = srcLen;
115 
116     while (left > 0) {
117         if (*src != TELNET_IAC) {
118             *dst = *src;
119             dst++;
120             src++;
121             left--;
122             continue;
123         }
124 
125         /*
126          * if starting with IAC, filter out IAC as following
127          * |IAC|          --> skip
128          * |IAC|NOP|...   --> ... : skip for putty keepalive etc.
129          * |IAC|x|        --> skip
130          * |IAC|IAC|x|... --> |IAC|... : skip for literal cmds
131          * |IAC|SB|NAWS|x1|x2|x3|x4|IAC|SE|... --> ... : skip NAWS(unsupported)
132          * |IAC|x|x|...   --> ... : skip unsupported IAC
133          */
134         if (left == 1) {
135             break;
136         }
137         /* left no less than 2 */
138         if (*(src + 1) == TELNET_NOP) {
139             src += LEN_IAC_CMD;
140             left -= LEN_IAC_CMD;
141             continue;
142         }
143         if (left == LEN_IAC_CMD) {
144             break;
145         }
146         /* left no less than 3 */
147         if (*(src + 1) == TELNET_IAC) {
148             *dst = TELNET_IAC;
149             dst++;
150             src += LEN_IAC_CMD;
151             left -= LEN_IAC_CMD;
152             continue;
153         }
154         if ((*(src + 1) == TELNET_SB) && (*(src + LEN_IAC_CMD) == TELNET_NAWS)) {
155             if (left > LEN_IAC_CMD_NAWS) {
156                 src += LEN_IAC_CMD_NAWS;
157                 left -= LEN_IAC_CMD_NAWS;
158                 continue;
159             }
160             break;
161         }
162         src += LEN_IAC_CMD_OPT;
163         left -= LEN_IAC_CMD_OPT;
164     }
165 
166     if (dstLen != NULL) {
167         *dstLen = dst - buf;
168     }
169     return buf;
170 }
171 
172 /*
173  * Description : Write data to fd.
174  * Input       : fd     --- the fd to write.
175  *             : src    --- data pointer.
176  *             : srcLen --- data length.
177  * Return      : length of written data.
178  */
WriteToFd(INT32 fd,const CHAR * src,size_t srcLen)179 STATIC ssize_t WriteToFd(INT32 fd, const CHAR *src, size_t srcLen)
180 {
181     size_t sizeLeft;
182     ssize_t sizeWritten;
183 
184     sizeLeft = srcLen;
185     while (sizeLeft > 0) {
186         sizeWritten = write(fd, src, sizeLeft);
187         if (sizeWritten < 0) {
188             /* last write failed */
189             if (sizeLeft == srcLen) {
190                 /* nothing was written in any loop */
191                 return -1;
192             } else {
193                 /* something was written in previous loop */
194                 break;
195             }
196         } else if (sizeWritten == 0) {
197             break;
198         }
199         sizeLeft -= (size_t)sizeWritten;
200         src += sizeWritten;
201     }
202 
203     return (ssize_t)(srcLen - sizeLeft);
204 }
205 
206 /* Try to remove the client device if there is any client connection */
TelnetClientClose(VOID)207 STATIC VOID TelnetClientClose(VOID)
208 {
209     /* check if there is any client connection */
210     if (g_telnetMask == 0) {
211         return;
212     }
213     (VOID)TelnetDevDeinit();
214     g_telnetMask = 0;
215     printf("telnet client disconnected.\n");
216 }
217 
218 /* Release the client and server fd */
TelnetRelease(VOID)219 STATIC VOID TelnetRelease(VOID)
220 {
221     if (g_telnetClientFd >= 0) {
222         (VOID)close(g_telnetClientFd);
223         g_telnetClientFd = -1;
224     }
225 
226     if (g_telnetListenFd >= 0) {
227         (VOID)close(g_telnetListenFd);
228         g_telnetListenFd = -1;
229     }
230 }
231 
232 /* Stop telnet server */
TelnetdDeinit(VOID)233 STATIC VOID TelnetdDeinit(VOID)
234 {
235     if (atomic_read(&g_telnetTaskId) == 0) {
236         PRINTK("telnet server is not running!\n");
237         return;
238     }
239 
240     TelnetRelease();
241     (VOID)TelnetedUnregister();
242     atomic_set(&g_telnetTaskId, 0);
243     PRINTK("telnet server closed.\n");
244 }
245 
246 /*
247  * Description : Setup the listen fd for telnetd with specific port.
248  * Input       : port --- the port at which telnet server listens.
249  * Return      : -1           --- on error
250  *             : non-negative --- listen fd if OK
251  */
TelnetdInit(UINT16 port)252 STATIC INT32 TelnetdInit(UINT16 port)
253 {
254     INT32 listenFd;
255     INT32 reuseAddr = 1;
256     struct sockaddr_in inTelnetAddr;
257 
258     listenFd = socket(AF_INET, SOCK_STREAM, 0);
259     if (listenFd == -1) {
260         PRINT_ERR("TelnetdInit : socket error.\n");
261         goto ERR_OUT;
262     }
263 
264     /* reuse listen port */
265     if (setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) != 0) {
266         PRINT_ERR("TelnetdInit : setsockopt REUSEADDR error.\n");
267         goto ERR_CLOSE_FD;
268     }
269 
270     (VOID)memset_s(&inTelnetAddr, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
271     inTelnetAddr.sin_family = AF_INET;
272     inTelnetAddr.sin_addr.s_addr = INADDR_ANY;
273     inTelnetAddr.sin_port = htons(port);
274 
275     if (bind(listenFd, (const struct sockaddr *)&inTelnetAddr, sizeof(struct sockaddr_in)) == -1) {
276         PRINT_ERR("TelnetdInit : bind error.\n");
277         goto ERR_CLOSE_FD;
278     }
279 
280     if (listen(listenFd, TELNET_LISTEN_BACKLOG) == -1) {
281         PRINT_ERR("TelnetdInit : listen error.\n");
282         goto ERR_CLOSE_FD;
283     }
284 
285     return listenFd;
286 
287 ERR_CLOSE_FD:
288     (VOID)close(listenFd);
289 ERR_OUT:
290     return -1;
291 }
292 
TelnetClientPrepare(INT32 clientFd)293 STATIC INT32 TelnetClientPrepare(INT32 clientFd)
294 {
295     INT32 keepAlive = TELNET_KEEPALIVE;
296     INT32 keepIdle = TELNET_KEEPIDLE;
297     INT32 keepInterval = TELNET_KEEPINTV;
298     INT32 keepCnt = TELNET_KEEPCNT;
299     const UINT8 doEcho[] = { TELNET_IAC, TELNET_DO, TELNET_ECHO };
300     const UINT8 doNaws[] = { TELNET_IAC, TELNET_DO, TELNET_NAWS };
301     const UINT8 willEcho[] = { TELNET_IAC, TELNET_WILL, TELNET_ECHO };
302     const UINT8 willSga[] = { TELNET_IAC, TELNET_WILL, TELNET_SGA };
303 
304     if (g_telnetListenFd == -1) {
305         return -1;
306     }
307     g_telnetClientFd = clientFd;
308     if (TelnetDevInit(clientFd) != 0) {
309         g_telnetClientFd = -1;
310         return -1;
311     }
312     g_telnetMask = 1;
313 
314     /* negotiate with client */
315     (VOID)WriteToFd(clientFd, (CHAR *)doEcho, sizeof(doEcho));
316     (VOID)WriteToFd(clientFd, (CHAR *)doNaws, sizeof(doNaws));
317     (VOID)WriteToFd(clientFd, (CHAR *)willEcho, sizeof(willEcho));
318     (VOID)WriteToFd(clientFd, (CHAR *)willSga, sizeof(willSga));
319 
320     /* enable TCP keepalive to check whether telnet link is alive */
321     if (setsockopt(clientFd, SOL_SOCKET, SO_KEEPALIVE, (VOID *)&keepAlive, sizeof(keepAlive)) < 0) {
322         PRINT_ERR("telnet setsockopt SO_KEEPALIVE error.\n");
323     }
324     if (setsockopt(clientFd, IPPROTO_TCP, TCP_KEEPIDLE, (VOID *)&keepIdle, sizeof(keepIdle)) < 0) {
325         PRINT_ERR("telnet setsockopt TCP_KEEPIDLE error.\n");
326     }
327     if (setsockopt(clientFd, IPPROTO_TCP, TCP_KEEPINTVL, (VOID *)&keepInterval, sizeof(keepInterval)) < 0) {
328         PRINT_ERR("telnet setsockopt TCP_KEEPINTVL error.\n");
329     }
330     if (setsockopt(clientFd, IPPROTO_TCP, TCP_KEEPCNT, (VOID *)&keepCnt, sizeof(keepCnt)) < 0) {
331         PRINT_ERR("telnet setsockopt TCP_KEEPCNT error.\n");
332     }
333     return 0;
334 }
335 
TelnetClientLoop(VOID * arg)336 STATIC VOID *TelnetClientLoop(VOID *arg)
337 {
338     struct pollfd pollFd;
339     INT32 ret;
340     INT32 nRead;
341     UINT32 len;
342     UINT8 buf[TELNET_CLIENT_READ_BUF_SIZE];
343     UINT8 *cmdBuf = NULL;
344     INT32 clientFd = (INT32)(UINTPTR)arg;
345 
346     (VOID)prctl(PR_SET_NAME, "TelnetClientLoop", 0, 0, 0);
347     TelnetLock();
348     if (TelnetClientPrepare(clientFd) != 0) {
349         TelnetUnlock();
350         (VOID)close(clientFd);
351         return NULL;
352     }
353     TelnetUnlock();
354 
355     while (1) {
356         pollFd.fd = clientFd;
357         pollFd.events = POLLIN | POLLRDHUP;
358         pollFd.revents = 0;
359 
360         ret = poll(&pollFd, 1, TELNET_CLIENT_POLL_TIMEOUT);
361         if (ret < 0) {
362             break;
363         }
364         if (ret == 0) {
365             continue;
366         }
367         /* connection reset, maybe keepalive failed or reset by peer */
368         if ((UINT16)pollFd.revents & (POLLERR | POLLHUP | POLLRDHUP)) {
369             break;
370         }
371 
372         if ((UINT16)pollFd.revents & POLLIN) {
373             nRead = read(clientFd, buf, sizeof(buf));
374             if (nRead <= 0) {
375                 /* telnet client shutdown */
376                 break;
377             }
378             cmdBuf = ReadFilter(buf, (UINT32)nRead, &len);
379             if (len > 0) {
380                 (VOID)TelnetTx((CHAR *)cmdBuf, len);
381             }
382         }
383     }
384     TelnetLock();
385     TelnetClientClose();
386     (VOID)close(clientFd);
387     clientFd = -1;
388     g_telnetClientFd = -1;
389     TelnetUnlock();
390     return NULL;
391 }
392 
TelnetClientTaskAttr(pthread_attr_t * threadAttr)393 STATIC VOID TelnetClientTaskAttr(pthread_attr_t *threadAttr)
394 {
395     (VOID)pthread_attr_init(threadAttr);
396     threadAttr->inheritsched = PTHREAD_EXPLICIT_SCHED;
397     threadAttr->schedparam.sched_priority = TELNET_TASK_PRIORITY;
398     threadAttr->detachstate = PTHREAD_CREATE_DETACHED;
399     (VOID)pthread_attr_setstacksize(threadAttr, TELNET_TASK_STACK_SIZE);
400 }
401 
402 /*
403  * Description : Handle the client connection request.
404  *               Create a client connection if permitted.
405  * Return      : 0  --- please continue the server accept loop
406  *             : -1 --- please stop the server accept loop.
407  */
TelnetdAcceptClient(INT32 clientFd,const struct sockaddr_in * inTelnetAddr)408 STATIC INT32 TelnetdAcceptClient(INT32 clientFd, const struct sockaddr_in *inTelnetAddr)
409 {
410     INT32 ret = 0;
411     pthread_t tmp;
412     pthread_attr_t useAttr;
413 
414     TelnetClientTaskAttr(&useAttr);
415 
416     if (clientFd < 0) {
417         ret = -1;
418         goto ERROUT;
419     }
420 
421     TelnetLock();
422 
423     if (g_telnetListenFd == -1) {
424         /* server already has been closed, so quit this task now */
425         ret = -1;
426         goto ERROUT_UNLOCK;
427     }
428 
429     if (g_telnetClientFd >= 0) {
430         /* already connected and support only one */
431         goto ERROUT_UNLOCK;
432     }
433 
434     g_telnetClientFd = clientFd;
435 
436     if (pthread_create(&tmp, &useAttr, TelnetClientLoop, (VOID *)(UINTPTR)clientFd) != 0) {
437         PRINT_ERR("Failed to create client handle task\n");
438         g_telnetClientFd = -1;
439         goto ERROUT_UNLOCK;
440     }
441     TelnetUnlock();
442     return ret;
443 
444 ERROUT_UNLOCK:
445     (VOID)close(clientFd);
446     clientFd = -1;
447     TelnetUnlock();
448 ERROUT:
449     return ret;
450 }
451 
452 /*
453  * Waiting for client's connection. Only allow 1 connection, and others will be discarded.
454  */
TelnetdAcceptLoop(INT32 listenFd)455 STATIC VOID TelnetdAcceptLoop(INT32 listenFd)
456 {
457     INT32 clientFd = -1;
458     struct sockaddr_in inTelnetAddr;
459     INT32 len = sizeof(inTelnetAddr);
460 
461     TelnetLock();
462     g_telnetListenFd = listenFd;
463 
464     while (g_telnetListenFd >= 0) {
465         TelnetUnlock();
466 
467         (VOID)memset_s(&inTelnetAddr, sizeof(inTelnetAddr), 0, sizeof(inTelnetAddr));
468         clientFd = accept(listenFd, (struct sockaddr *)&inTelnetAddr, (socklen_t *)&len);
469         if (TelnetdAcceptClient(clientFd, &inTelnetAddr) == 0) {
470             /*
471              * Sleep sometime before next loop: mostly we already have one connection here,
472              * and the next connection will be declined. So don't waste our cpu.
473              */
474             LOS_Msleep(TELNET_ACCEPT_INTERVAL);
475         } else {
476             return;
477         }
478         TelnetLock();
479     }
480     TelnetUnlock();
481 }
482 
TelnetdMain(VOID)483 STATIC INT32 TelnetdMain(VOID)
484 {
485     INT32 sock;
486     INT32 ret;
487 
488     sock = TelnetdInit(TELNETD_PORT);
489     if (sock == -1) {
490         PRINT_ERR("telnet init error.\n");
491         return -1;
492     }
493 
494     TelnetLock();
495     ret = TelnetedRegister();
496     TelnetUnlock();
497 
498     if (ret != 0) {
499         PRINT_ERR("telnet register error.\n");
500         (VOID)close(sock);
501         return -1;
502     }
503 
504     PRINTK("start telnet server successfully, waiting for connection.\n");
505     TelnetdAcceptLoop(sock);
506     return 0;
507 }
508 
509 /*
510  * Try to create telnetd task.
511  */
TelnetdTaskInit(VOID)512 STATIC VOID TelnetdTaskInit(VOID)
513 {
514     UINT32 ret;
515     TSK_INIT_PARAM_S initParam = {0};
516 
517     initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)TelnetdMain;
518     initParam.uwStackSize = TELNET_TASK_STACK_SIZE;
519     initParam.pcName = "TelnetServer";
520     initParam.usTaskPrio = TELNET_TASK_PRIORITY;
521     initParam.uwResved = LOS_TASK_STATUS_DETACHED;
522 
523     if (atomic_read(&g_telnetTaskId) != 0) {
524         PRINT_ERR("telnet server is already running!\n");
525         return;
526     }
527 
528     ret = LOS_TaskCreate((UINT32 *)&g_telnetTaskId, &initParam);
529     if (ret != LOS_OK) {
530         PRINT_ERR("failed to create telnet server task!\n");
531     }
532 }
533 
534 /*
535  * Try to destroy telnetd task.
536  */
TelnetdTaskDeinit(VOID)537 STATIC VOID TelnetdTaskDeinit(VOID)
538 {
539     if (atomic_read(&g_telnetTaskId) == 0) {
540         PRINTK("telnet server is not running, please start up telnet server first.\n");
541         return;
542     }
543 
544     TelnetLock();
545     TelnetClientClose();
546     TelnetdDeinit();
547     TelnetUnlock();
548 }
549 
TelnetUsage(VOID)550 STATIC VOID TelnetUsage(VOID)
551 {
552     PRINTK("Usage: telnet [OPTION]...\n");
553     PRINTK("Start or close telnet server.\n\n");
554     PRINTK("  on       Init the telnet server\n");
555     PRINTK("  off      Deinit the telnet server\n");
556 }
557 
TelnetCmd(UINT32 argc,const CHAR ** argv)558 INT32 TelnetCmd(UINT32 argc, const CHAR **argv)
559 {
560     if (argc != 1) {
561         TelnetUsage();
562         return 0;
563     }
564 
565     if (strcmp(argv[0], "on") == 0) {
566         /* telnet on: try to start telnet server task */
567         TelnetdTaskInit();
568         return 0;
569     }
570     if (strcmp(argv[0], "off") == 0) {
571         /* telnet off: try to stop clients, then stop server task */
572         TelnetdTaskDeinit();
573         return 0;
574     }
575 
576     TelnetUsage();
577     return 0;
578 }
579 
580 #ifdef LOSCFG_SHELL_CMD_DEBUG
581 SHELLCMD_ENTRY(telnet_shellcmd, CMD_TYPE_EX, "telnet", 1, (CmdCallBackFunc)TelnetCmd);
582 #endif /* LOSCFG_SHELL_CMD_DEBUG */
583 #endif
584 
585