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