1 /*
2 * Copyright 2013 Tenkiv, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 * the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 * specific language governing permissions and limitations under the License.
12 */
13
14 /**
15 * @file TelnetServer.c
16 * @brief Implements a control interface for the Tekdaqc via the Telnet protocol.
17 *
18 * Implements a control interface for the Tekdaqc via the Telnet protocol. Only a single connection
19 * is allowed at a time. Any attempts to connect while an active connection is present will result in
20 * an error message from the board.
21 *
22 * This file based on the Telnet server implementation in the TI Stellaris example Cave Adventure game,
23 * in particular, the methods for processing the Telnet state machine.
24 *
25 * @author Jared Woolston (jwoolston@tenkiv.com)
26 * @since v1.0.0.0
27 */
28
29 /*--------------------------------------------------------------------------------------------------------*/
30 /* INCLUDES */
31 /*--------------------------------------------------------------------------------------------------------*/
32 #include "lwip/opt.h"
33 #include "lwip/apps/telnetserver.h"
34 #include "lwip/debug.h"
35 #include "lwip/stats.h"
36 #include "lwip/tcp.h"
37 #include <stdio.h>
38 #include <inttypes.h>
39 #define PRINT_TAG "TELNETD"
40 /*--------------------------------------------------------------------------------------------------------*/
41 /* PRIVATE DEFINES */
42 /*--------------------------------------------------------------------------------------------------------*/
43 #define SIZE_TOSTRING_BUFFER 512U
44
45 #define TELNET_PORT 23U
46
47 #define ERROR_MESSAGE_HEADER "\n\r--------------------\n\rError Message\n\r\tMessage: %s\n\r--------------------\n\r\x1E"
48 #define STATUS_MESSAGE_HEADER "\n\r--------------------\n\rStatus Message\n\r\tMessage: %s\n\r--------------------\n\r\x1E"
49 #define DEBUG_MESSAGE_HEADER "\n\r--------------------\n\rDebug Message\n\r\tMessage: %s\n\r--------------------\n\r\x1E"
50 #define COMMAND_DATA_MESSAGE_HEADER "\n\r--------------------\n\rCommand Data Message\n\r\tMessage: %s\n\r--------------------\n\r\x1E"
51
52 //#define TELNET_DEBUG
53 //#define TELNET_CHAR_DEBUG
54 /*--------------------------------------------------------------------------------------------------------*/
55 /* PRIVATE VARIABLES */
56 /*--------------------------------------------------------------------------------------------------------*/
57
58 /**
59 * @internal
60 * @brief Pointer to the TCP port being used for the Telnet server.
61 */
62 static struct tcp_pcb *telnet_pcb;
63
64 /**
65 * @internal
66 * @brief Pointer to the current Telnet server.
67 */
68 static TelnetServer_t telnet_server;
69
70 /**
71 * @internal
72 * @brief Indicates the connection status of the telnet server.
73 */
74 static bool IsConnected = false;
75
76 /**
77 * @internal
78 * @brief Buffer for printing the TOSTRING_BUFFER with additional formatting.
79 */
80 static char MESSAGE_BUFFER[SIZE_TOSTRING_BUFFER];
81
82 /**
83 * @internal
84 * @brief The error message provided when an attempt is made to play the game when
85 * it is already being played over a different interface.
86 */
87 static const char ErrorMessage[53] =
88 "The Tekdaqc is already in use...try again later!\r\n";
89
90 /**
91 * @internal
92 * @brief The initialization sequence sent to a remote telnet client when it first connects to the telnet server.
93 */
94 static const char TelnetInit[] = { TELNET_IAC, TELNET_DO,
95 TELNET_OPT_SUPPRESS_GA, TELNET_IAC, TELNET_WILL, TELNET_OPT_ECHO };
96
97 /**
98 * @internal
99 * @brief This telnet server will always suppress go ahead generation, regardless of this setting.
100 */
101 static TelnetOpts_t TelnetOptions[] = { { .option = TELNET_OPT_SUPPRESS_GA,
102 .flags = (0x01 << OPT_FLAG_WILL) }, { .option = TELNET_OPT_ECHO,
103 .flags = (1 << OPT_FLAG_DO) } };
104
105 /*--------------------------------------------------------------------------------------------------------*/
106 /* PRIVATE FUNCTION PROTOTYPES */
107 /*--------------------------------------------------------------------------------------------------------*/
108
109 /**
110 * @brief Called when the lwIP TCP/IP stack has an incoming connection request on the telnet port.
111 */
112 static err_t TelnetAccept(void *arg, struct tcp_pcb *pcb, err_t err);
113
114 /**
115 * @brief Called when the lwIP TCP/IP stack has an incoming packet to be processed.
116 */
117 static err_t TelnetReceive(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
118
119 /**
120 * @brief Called when the lwIP TCP/IP stack has received an acknowledge for data that has been transmitted.
121 */
122 static err_t TelnetSent(void *arg, struct tcp_pcb *pcb, u16_t len);
123
124 /**
125 * @brief Called when the lwIP TCP/IP stack has detected an error.
126 */
127 static void TelnetError(void *arg, err_t err);
128
129 /**
130 * @brief Clears the internal message string buffer.
131 */
132 static void ClearToMessageBuffer(void);
133
134 /**
135 * @brief Creates an initalizes a Telnet server.
136 */
137 static TelnetServer_t* CreateTelnetServer(void);
138
139 /*--------------------------------------------------------------------------------------------------------*/
140 /* PRIVATE METHODS */
141 /*--------------------------------------------------------------------------------------------------------*/
142
143 /*
144 * @internal
145 * This function is called when the lwIP TCP/IP stack has an incoming
146 * connection request on the telnet port.
147 *
148 * @param arg void* Argument pointer passed to the handler by the lwIP stack.
149 * @param pcb tcp_pcb* struct The PCB structure this callback is for.
150 * @param err lwIP err_t with the current error status.
151 * @retval err lwIP err_t with the result of the this function.
152 */
TelnetAccept(void * arg,struct tcp_pcb * pcb,err_t err)153 static err_t TelnetAccept(void *arg, struct tcp_pcb *pcb, err_t err) {
154 #ifdef TELNET_DEBUG
155 LOGD(PRINT_TAG, "[Telnet Server] Incoming connection requested on Telnet port.\n\r");
156 #endif
157 err_t ret_err;
158
159 LWIP_UNUSED_ARG(arg);
160 LWIP_UNUSED_ARG(err);
161
162 /* Check if already connected. */
163 if (TelnetIsConnected() == true) {
164 /* There is already a connected client, so refuse this connection with
165 a message indicating this fact. */
166 #ifdef TELNET_DEBUG
167 LOGD(PRINT_TAG, "[Telnet Server] A connection was attempted while an active connection is open.\n\r");
168 #endif
169 tcp_accepted(pcb);
170 tcp_arg(pcb, NULL);
171 tcp_sent(pcb, TelnetSent);
172 tcp_write(pcb, ErrorMessage, sizeof(ErrorMessage), 1);
173 tcp_output(pcb);
174 /* Temporarily accept this connection until the message is transmitted. */
175 return (ERR_OK);
176 }
177
178 /* Setup the TCP connection priority. */
179 tcp_setprio(pcb, TCP_PRIO_MIN);
180
181 CreateTelnetServer();
182 #ifdef TELNET_DEBUG
183 LOGD(PRINT_TAG, "[Telnet Server] Initializing telnet server.\n\r");
184 #endif
185 tcp_nagle_enable(pcb);
186 telnet_server.pcb = pcb;
187 telnet_server.pcb->so_options |= SOF_KEEPALIVE;
188 telnet_server.pcb->keep_idle = 300000UL; // 5 Minutes
189 telnet_server.pcb->keep_intvl = 1000UL; // 1 Second
190 telnet_server.pcb->keep_cnt = 9; // 9 Consecutive failures terminate
191
192 /* Mark that a client has connected. */
193 IsConnected = true;
194 /* Accept this connection. */
195 tcp_accepted(pcb);
196 #ifdef TELNET_DEBUG
197 LOGD(PRINT_TAG, "[Telnet Server] An incoming connection was accepted.\n\r");
198 #endif
199
200 /* Setup the TCP callback argument. */
201 tcp_arg(pcb, &telnet_server);
202
203 /* Initialize lwIP tcp_recv callback function for pcb */
204 tcp_recv(pcb, TelnetReceive);
205
206 /* Initialize lwIP tcp_err callback function for pcb */
207 tcp_err(pcb, TelnetError);
208
209 /* Initialize lwIP tcp_poll callback function for pcb */
210 tcp_poll(pcb, TelnetPoll, 1);
211
212 /* Setup the TCP sent callback function. */
213 tcp_sent(pcb, TelnetSent);
214 /* Initialize the count of outstanding bytes. The initial byte acked as
215 part of the SYN -> SYN/ACK sequence is included so that the byte count
216 works out correctly at the end. */
217 telnet_server.outstanding = sizeof(TelnetInit) + 1;
218 /* Do not close the telnet connection until requested. */
219 telnet_server.close = 0;
220 #ifdef TELNET_DEBUG
221 LOGD(PRINT_TAG, "[Telnet Server] Writing the init messages\n\r");
222 #endif
223 /* Send the telnet initialization string. */
224 tcp_write(pcb, TelnetInit, sizeof(TelnetInit), 1);
225 tcp_output(pcb);
226
227 TelnetWriteDebugMessage("[TELNET] Telnet Server Connected. Welcome.");
228
229 #ifdef TELNET_DEBUG
230 LOGD(PRINT_TAG, "[Telnet Server] Telnet Server Connected. Welcome.\n\r");
231 #endif
232 /* Return a success code. */
233 ret_err = ERR_OK;
234 return ret_err;
235 }
236
237 /*
238 * @internal
239 * This function is called when the lwIP TCP/IP stack has an incoming packet to
240 * be processed.
241 *
242 * @param arg void* Argument pointer passed to the handler by the lwIP stack.
243 * @param pcb tcp_pcb* struct The PCB structure this callback is for.
244 * @param p pbuf* struct The data buffer from the lwIP stack.
245 * @param err lwIP err_t with the current error status.
246 * @retval err lwIP err_t with the result of the this function.
247 */
TelnetReceive(void * arg,struct tcp_pcb * pcb,struct pbuf * p,err_t err)248 static err_t TelnetReceive(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
249 struct pbuf *q;
250 unsigned long ulIdx;
251 unsigned char *pucData;
252 TelnetServer_t* server;
253 if (arg != NULL) {
254 server = (TelnetServer_t*) arg;
255
256 /* Process the incoming packet. */
257 if ((err == ERR_OK) && (p != NULL)) {
258 #ifdef TELNET_DEBUG
259 LOGD(PRINT_TAG, "[Telnet Server] Processing received packet.\n\r");
260 #endif
261 /* Accept the packet from TCP. */
262 tcp_recved(pcb, p->tot_len);
263 /* Loop through the pbufs in this packet. */
264 for (q = p, pucData = (unsigned char*) q->payload; q != NULL; q = q->next) {
265 /* Loop through the bytes in this pbuf. */
266 for (ulIdx = 0; ulIdx < q->len; ulIdx++) {
267 /* Process this character. */
268 TelnetProcessCharacter(pucData[ulIdx]);
269 }
270 }
271
272 /* Free the pbuf. */
273 pbuf_free(p);
274 } else if ((err == ERR_OK) && (p == NULL)) {
275 /* If a null packet is passed in, close the connection. */
276 server->length = 0;
277 TelnetClose();
278 }
279 } else {
280 #ifdef TELNET_DEBUG
281 LOGD(PRINT_TAG, "[Telnet Server] Could not cast receive args to telnet server struct because they were null.\n\r");
282 return ERR_VAL;
283 #endif
284 }
285 /* Return okay. */
286 return (ERR_OK);
287 }
288
289 /**
290 * @internal
291 * This function is called when the lwIP TCP/IP stack has received an
292 * acknowledge for data that has been transmitted.
293 *
294 * @param arg void* Argument pointer passed to the handler by the lwIP stack.
295 * @param pcb tcp_pcb* struct The PCB structure this callback is for.
296 * @param len u16_t The number of bytes which were sent.
297 * @retval err lwIP err_t with the result of the this function.
298 */
TelnetSent(void * arg,struct tcp_pcb * pcb,u16_t len)299 static err_t TelnetSent(void *arg, struct tcp_pcb *pcb, u16_t len)
300 {
301 /* See if this is for the game connection or for a secondary connection. */
302 if (arg) {
303 TelnetServer_t* server = (TelnetServer_t*) arg;
304 #ifdef TELNET_DEBUG
305 LOGD(PRINT_TAG, "[Telnet Server] Sent packet was acknowledged.\n\r");
306 #endif
307 /* Decrement the count of outstanding bytes. */
308 server->outstanding -= len;
309 } else {
310 /* See if this is the ACK for the error message. */
311 if (len == sizeof(ErrorMessage)) {
312 /* Close this telnet connection now that the error message has been transmitted. */
313 tcp_sent(pcb, 0);
314 tcp_close(pcb);
315 }
316 }
317 /* Return OK. */
318 return (ERR_OK);
319 }
320
321 /**
322 * @internal
323 * This function implements the tcp_err callback function (called when a fatal
324 * tcp_connection error occurs. As soon as this function is called, the Telnet
325 * server is invalid and should not be used anymore.
326 *
327 * @param arg Pointer to argument parameter
328 * @param err Not used
329 * @retval None
330 */
TelnetError(void * arg,err_t err)331 static void TelnetError(void *arg, err_t err)
332 {
333 LWIP_UNUSED_ARG(err);
334 //#ifdef TELNET_DEBUG
335 LOGD(PRINT_TAG, "[Telnet Server] Telnet error received: %i\n\r", err);
336 //#endif
337 TelnetServer_t* server;
338 server = (TelnetServer_t*) arg;
339 if (server != NULL) {
340 /* free es structure */
341 mem_free(server);
342 }
343 IsConnected = false;
344 }
345
346 /**
347 * @internal
348 * Creates and initializes a Telnet server.
349 *
350 * @param none
351 * @retval TelnetServer_t* The structure used to represent the current server.
352 */
CreateTelnetServer(void)353 static TelnetServer_t* CreateTelnetServer(void)
354 {
355 #ifdef TELNET_DEBUG
356 LOGD(PRINT_TAG, "[Telnet Server] Allocating memory for server.\n\r");
357 #endif
358 /* Allocate structure server to maintain Telnet connection information */
359 IsConnected = false;
360 telnet_server.halt = false;
361 telnet_server.recvWrite = 0;
362 telnet_server.recvRead = 0;
363 telnet_server.previous = 0;
364 telnet_server.length = 0;
365 for (int i = 0; i < TELNET_BUFFER_LENGTH; ++i) {
366 telnet_server.recvBuffer[i] = 0;
367 }
368 return &telnet_server;
369 }
370
371 /**
372 * @internal
373 * Clears the internal message string buffer.
374 *
375 * @param none
376 * @retval none
377 */
ClearToMessageBuffer(void)378 static void ClearToMessageBuffer(void) {
379 for (int i = 0; i < SIZE_TOSTRING_BUFFER; ++i) {
380 MESSAGE_BUFFER[i] = '\0';
381 }
382 }
383
384 /**
385 * Initializes the provided TelnetServer_t struct with default values and creates a TCP port for it.
386 *
387 * @param none
388 * @retval none
389 */
InitializeTelnetServer(void)390 TelnetStatus_t InitializeTelnetServer(void) {
391 telnet_server.length = 0;
392 telnet_server.outstanding = 0;
393 for (uint_fast16_t i = 0; i < sizeof(telnet_server.buffer); ++i) {
394 telnet_server.buffer[i] = 0;
395 }
396 /* Create a new tcp pcb */
397 #ifdef TELNET_DEBUG
398 LOGD(PRINT_TAG, "[Telnet Server] Creating TCP port for Telnet server.\n\r");
399 #endif
400 telnet_pcb = tcp_new();
401
402 if (telnet_pcb != NULL) {
403 #ifdef TELNET_DEBUG
404 LOGD(PRINT_TAG, "[Telnet Server] Successfully created Telnet TCP port.\n\r");
405 #endif
406 err_t err;
407 /* Bind telnet to port TELNET_PORT */
408 err = tcp_bind(telnet_pcb, IP_ADDR_ANY, TELNET_PORT);
409
410 if (err == ERR_OK) {
411 /* Start tcp listening for Telnet PCB */
412 telnet_pcb = tcp_listen(telnet_pcb);
413 #ifdef TELNET_DEBUG
414 LOGD(PRINT_TAG, "[Telnet Server] Now listening for incoming connections on port %i\n\r", TELNET_PORT);
415 #endif
416 /* Initialize LwIP tcp_accept callback function */
417 tcp_accept(telnet_pcb, TelnetAccept);
418 return TELNET_OK;
419 } else {
420 /* Deallocate the pcb */
421 memp_free(MEMP_TCP_PCB, telnet_pcb);
422 #ifdef TELNET_DEBUG
423 LOGD(PRINT_TAG, "[Telnet Server] Can not bind pcb\n\r");
424 #endif
425 return TELNET_ERR_BIND;
426 }
427 } else {
428 #ifdef TELNET_DEBUG
429 LOGD(PRINT_TAG, "[Telnet Server] Can not create new TCP port.\n\r");
430 #endif
431 return TELNET_ERR_PCBCREATE;
432 }
433 }
434
435 /**
436 * This function is called when the the TCP connection should be closed.
437 *
438 * @param none
439 * @retval none
440 */
TelnetClose(void)441 void TelnetClose(void) {
442 LOGD(PRINT_TAG, "Closing telnet connection.\n\r");
443 struct tcp_pcb *pcb = telnet_server.pcb;
444
445 /* Remove all callbacks */
446 tcp_arg(pcb, NULL);
447 tcp_sent(pcb, NULL);
448 tcp_recv(pcb, NULL);
449 tcp_err(pcb, NULL);
450 tcp_poll(pcb, NULL, 0);
451
452 /* Clear the telnet data structure pointer, to indicate that there is no longer a connection. */
453 telnet_server.pcb = 0;
454
455 /* Close tcp connection */
456 tcp_close(pcb);
457 IsConnected = false;
458
459 /* Re-initialize the Telnet Server */
460 InitializeTelnetServer();
461 }
462
463 /**
464 * Returns the connection status of the Telnet server. Since this implementation supports only
465 * one client at a time, this method can be used to determine if an incoming request can be honored
466 * as well as allowing other parts of the program to adjust their behavior depending on if a client
467 * is connected.
468 *
469 * @param none
470 * @retval bool TRUE if the telnet server has an active connection.
471 */
TelnetIsConnected(void)472 bool TelnetIsConnected(void) {
473 return (IsConnected);
474 }
475
476 /**
477 * Called by the lwIP stack when there is data to send/receive to/from the Telnet server.
478 *
479 * @param arg void* to argument passed to callback by lwIP stack.
480 * @param tpcb tcp_pcb* To the tcp_pcb struct for the current tcp connection.
481 * @retval err_t The error/status code.
482 */
TelnetPoll(void * arg,struct tcp_pcb * tpcb)483 err_t TelnetPoll(void *arg, struct tcp_pcb *tpcb) {
484 err_t ret_err;
485 TelnetServer_t* server;
486 server = (TelnetServer_t*) arg;
487
488 if (server != NULL) {
489 unsigned long length = server->length;
490 if ((server->pcb != NULL) && (length != 0)) {
491 /* Write the data from the transmit buffer. */
492 tcp_write(server->pcb, server->buffer, length, 1);
493
494 /* Increment the count of outstanding bytes. */
495 server->outstanding += length;
496
497 /* Output the telnet data. */
498 tcp_output(server->pcb);
499
500 /* Reset the size of the data in the transmit buffer. */
501 server->length = 0;
502 }
503 /* See if the telnet connection should be closed; this will only occur once
504 all transmitted data has been ACKed by the client (so that some or all
505 of the final message is not lost). */
506 if (server->pcb && (server->outstanding == 0) && (server->close != 0)) {
507 #ifdef TELNET_DEBUG
508 LOGD(PRINT_TAG, "[Telnet Server] Telnet server should be closed.\n\r");
509 #endif
510 TelnetClose();
511 }
512 ret_err = ERR_OK;
513 } else {
514 #ifdef TELNET_DEBUG
515 LOGD(PRINT_TAG, "[Telnet Server] Cannot process poll request due to null server structure.\n\r");
516 #endif
517 /* Nothing to be done */
518 tcp_abort(tpcb);
519 ret_err = ERR_ABRT;
520 }
521 return ret_err;
522 }
523
524 /**
525 * Writes a character into the telnet receive buffer.
526 *
527 * @param character char The character to write.
528 * @retval none
529 */
TelnetRecvBufferWrite(char character)530 void TelnetRecvBufferWrite(char character) {
531 unsigned long ulWrite;
532 /* Ignore this character if it is the NULL character. */
533 if (character == 0) {
534 #ifdef TELNET_CHAR_DEBUG
535 LOGD(PRINT_TAG, "[Telnet Server] Ignorning NULL character.\n\r");
536 #endif
537 return;
538 }
539
540 /* Ignore this character if it is the second part of a CR/LF or LF/CR sequence. */
541 if (((character == '\r') && (telnet_server.previous == '\n'))
542 || ((character == '\n') && (telnet_server.previous == '\r'))) {
543 return;
544 }
545
546 /* Store this character into the receive buffer if there is space for it. */
547 ulWrite = telnet_server.recvWrite;
548 if (((ulWrite + 1) % sizeof(telnet_server.recvBuffer))
549 != telnet_server.recvRead) {
550 telnet_server.recvBuffer[ulWrite] = character;
551 telnet_server.recvWrite = (ulWrite + 1) % sizeof(telnet_server.recvBuffer);
552 #ifdef TELNET_CHAR_DEBUG
553 for (int i = 0; i <= telnet_server.recvWrite; ++i) {
554 LOGD(PRINT_TAG, "%c", telnet_server.recvBuffer[i]);
555 }
556 LOGD(PRINT_TAG, "\n\r");
557 #endif
558 } else {
559 #ifdef TELNET_DEBUG
560 LOGD(PRINT_TAG, "[Telnet Server] Could not store new character because buffer was full.\n\r");
561 #endif
562 }
563
564 /* Save this character as the previously received telnet character. */
565 telnet_server.previous = character;
566 }
567
568 /**
569 * Reads a character from the telnet interface.
570 *
571 * @param none
572 * @retval char The character read from the telnet interface.
573 */
TelnetRead(void)574 char TelnetRead(void) {
575 if (TelnetIsConnected() == true) {
576 uint32_t read;
577 char ret;
578 /* Return a NULL if there is no data in the receive buffer. */
579 read = telnet_server.recvRead;
580 if (read == telnet_server.recvWrite) {
581 return (0);
582 }
583 /* Read the next byte from the receive buffer. */
584 ret = telnet_server.recvBuffer[read];
585 telnet_server.recvRead = (read + 1) % sizeof(telnet_server.recvBuffer);
586 /* Return the byte that was read. */
587 return (ret);
588 } else {
589 return 0;
590 }
591 }
592
593 /**
594 * Writes a character to the telnet interface.
595 *
596 * @param character const char The character to write to the interface.
597 * @retval none
598 */
TelnetWrite(const char character)599 void TelnetWrite(const char character) {
600 if (TelnetIsConnected() == true) {
601 /* Delay until there is some space in the output buffer. The buffer is not
602 completly filled here to leave some room for the processing of received
603 telnet commands. */
604 while (telnet_server.length > (sizeof(telnet_server.buffer) - 32)) {
605 #ifdef TELNET_DEBUG
606 LOGD(PRINT_TAG, "[Telnet Server] Telnet buffer is full!\n\r");
607 #endif
608 /* Handle periodic timers for LwIP */
609 aos_msleep(200);
610 //LwIP_Periodic_Handle(GetLocalTime());
611 }
612
613 /* Write this character into the output buffer. */
614 telnet_server.buffer[telnet_server.length++] = character;
615 }
616 }
617
618 /**
619 * Writes a string to the specified Telnet server. Disables Ethernet
620 * interrupts during this process in order to prevent an intervening
621 * interrupt from corrupting the output buffer.
622 *
623 * @param string char* Pointer to a C-String to write to the interface.
624 * @retval none
625 */
TelnetWriteString(char * string)626 void TelnetWriteString(char* string) {
627 if (TelnetIsConnected() == true) {
628 #if 0
629 Eth_EXTI_Disable();
630 #endif
631 while (*string) {
632 TelnetWrite(*string);
633 ++string;
634 }
635 #if 0
636 Eth_EXTI_Enable();
637 #endif
638 }
639 }
640
641 /**
642 * This function will handle a WILL request for a telnet option. If it is an
643 * option that is known by the telnet server, a DO response will be generated
644 * if the option is not already enabled. For unknown options, a DONT response
645 * will always be generated.
646 *
647 * The response (if any) is written into the telnet transmit buffer.
648 *
649 * @param option char Option for the WILL command.
650 * @retval none
651 */
TelnetProcessWill(char option)652 void TelnetProcessWill(char option) {
653 unsigned long ulIdx;
654 #ifdef TELNET_CHAR_DEBUG
655 LOGD(PRINT_TAG, "[Telnet Server] Processing WILL command with option: %c/0x%02X\n\r", option, option);
656 #endif
657 /* Loop through the known options. */
658 for (ulIdx = 0; ulIdx < (sizeof(TelnetOptions) / sizeof(TelnetOptions[0])); ulIdx++) {
659 /* See if this option matches the option in question. */
660 if (TelnetOptions[ulIdx].option == option) {
661 /* See if the WILL flag for this option has already been set. */
662 if (((TelnetOptions[ulIdx].flags >> OPT_FLAG_WILL) & 0x01) == 0) {
663 /* Set the WILL flag for this option. */
664 TelnetOptions[ulIdx].flags = (TelnetOptions[ulIdx].flags & 0xFD)
665 | (0x01 << OPT_FLAG_WILL);
666 /* Send a DO response to this option. */
667 telnet_server.buffer[telnet_server.length++] = TELNET_IAC;
668 telnet_server.buffer[telnet_server.length++] = TELNET_DO;
669 telnet_server.buffer[telnet_server.length++] = option;
670 }
671 /* Return without any further processing. */
672 return;
673 }
674 }
675
676 /* This option is not recognized, so send a DONT response. */
677 telnet_server.buffer[telnet_server.length++] = TELNET_IAC;
678 telnet_server.buffer[telnet_server.length++] = TELNET_DONT;
679 telnet_server.buffer[telnet_server.length++] = option;
680 }
681
682 /**
683 * This function will handle a WONT request for a telnet option. If it is an
684 * option that is known by the telnet server, a DONT response will be generated
685 * if the option is not already disabled. For unknown options, a DONT response
686 * will always be generated.
687 *
688 * The response (if any) is written into the telnet transmit buffer.
689 *
690 * @param server Pointer to the server to process.
691 * @param option char Option for the WONT command.
692 * @retval none
693 */
TelnetProcessWont(char option)694 void TelnetProcessWont(char option) {
695 unsigned long ulIdx;
696 #ifdef TELNET_CHAR_DEBUG
697 LOGD(PRINT_TAG, "[Telnet Server] Processing WONT command with option: %c/0x%02X\n\r", option, option);
698 #endif
699 /* Loop through the known options. */
700 for (ulIdx = 0; ulIdx < (sizeof(TelnetOptions) / sizeof(TelnetOptions[0])); ulIdx++) {
701 /* See if this option matches the option in question. */
702 if (TelnetOptions[ulIdx].option == option) {
703 /* See if the WILL flag for this option is currently set. */
704 if (((TelnetOptions[ulIdx].flags >> OPT_FLAG_WILL) & 0x01) == 1) {
705 /* Clear the WILL flag for this option. */
706 TelnetOptions[ulIdx].flags = (TelnetOptions[ulIdx].flags & 0xFD)
707 | 0x00;
708 /* Send a DONT response to this option. */
709 telnet_server.buffer[telnet_server.length++] = TELNET_IAC;
710 telnet_server.buffer[telnet_server.length++] = TELNET_DONT;
711 telnet_server.buffer[telnet_server.length++] = option;
712 }
713 /* Return without any further processing. */
714 return;
715 }
716 }
717
718 /* This option is not recognized, so send a DONT response. */
719 telnet_server.buffer[telnet_server.length++] = TELNET_IAC;
720 telnet_server.buffer[telnet_server.length++] = TELNET_DONT;
721 telnet_server.buffer[telnet_server.length++] = option;
722 }
723
724 /**
725 * This function will handle a DO request for a telnet option. If it is an
726 * option that is known by the telnet server, a WILL response will be generated
727 * if the option is not already enabled. For unknown options, a WONT response
728 * will always be generated.
729 *
730 * The response (if any) is written into the telnet transmit buffer.
731 *
732 * @param option char Option for the DO command.
733 * @return none
734 */
TelnetProcessDo(char option)735 void TelnetProcessDo(char option) {
736 unsigned long ulIdx;
737 #ifdef TELNET_CHAR_DEBUG
738 LOGD(PRINT_TAG, "[Telnet Server] Processing DO command with option: %c/0x%02X\n\r", option, option);
739 #endif
740 /* Loop through the known options. */
741 for (ulIdx = 0; ulIdx < (sizeof(TelnetOptions) / sizeof(TelnetOptions[0])); ulIdx++) {
742 /* See if this option matches the option in question. */
743 if (TelnetOptions[ulIdx].option == option) {
744 /* See if the DO flag for this option has already been set. */
745 if (((TelnetOptions[ulIdx].flags >> OPT_FLAG_DO) & 0x01) == 0) {
746 /* Set the DO flag for this option. */
747 TelnetOptions[ulIdx].flags = (TelnetOptions[ulIdx].flags & 0xFB)
748 | (0x01 << OPT_FLAG_DO);
749 /* Send a WILL response to this option. */
750 telnet_server.buffer[telnet_server.length++] = TELNET_IAC;
751 telnet_server.buffer[telnet_server.length++] = TELNET_WILL;
752 telnet_server.buffer[telnet_server.length++] = option;
753 }
754 /* Return without any further processing. */
755 return;
756 }
757 }
758
759 // This option is not recognized, so send a WONT response.
760 telnet_server.buffer[telnet_server.length++] = TELNET_IAC;
761 telnet_server.buffer[telnet_server.length++] = TELNET_WONT;
762 telnet_server.buffer[telnet_server.length++] = option;
763 }
764
765 /**
766 * This funciton will handle a DONT request for a telnet option. If it is an
767 * option that is known by the telnet server, a WONT response will be generated
768 * if the option is not already disabled. For unknown options, a WONT resopnse
769 * will always be generated.
770 *
771 * The response (if any) is written into the telnet transmit buffer.
772 *
773 * @param option char Option for the DONT command.
774 * @return none
775 */
TelnetProcessDont(char option)776 void TelnetProcessDont(char option) {
777 unsigned long ulIdx;
778 #ifdef TELNET_CHAR_DEBUG
779 LOGD(PRINT_TAG, "[Telnet Server] Processing DONT command with option: %c/0x%02X\n\r", option, option);
780 #endif
781 /* Loop through the known options. */
782 for (ulIdx = 0; ulIdx < (sizeof(TelnetOptions) / sizeof(TelnetOptions[0])); ulIdx++) {
783 /* See if this option matches the option in question. */
784 if (TelnetOptions[ulIdx].option == option) {
785 /* See if the DO flag for this option is currently set. */
786 if (((TelnetOptions[ulIdx].flags >> OPT_FLAG_DO) & 0x01) == 1) {
787 /* Clear the DO flag for this option. */
788 TelnetOptions[ulIdx].flags = (TelnetOptions[ulIdx].flags & 0xFB)
789 | 0x00;
790 /* Send a WONT response to this option. */
791 telnet_server.buffer[telnet_server.length++] = TELNET_IAC;
792 telnet_server.buffer[telnet_server.length++] = TELNET_WONT;
793 telnet_server.buffer[telnet_server.length++] = option;
794 }
795 /* Return without any further processing. */
796 return;
797 }
798 }
799
800 /* This option is not recognized, so send a WONT response. */
801 telnet_server.buffer[telnet_server.length++] = TELNET_IAC;
802 telnet_server.buffer[telnet_server.length++] = TELNET_WONT;
803 telnet_server.buffer[telnet_server.length++] = option;
804 }
805
806 /*
807 * This function processes a character received from the telnet port, handling
808 * the interpretation of telnet commands (as indicated by the telnet interpret
809 * as command (IAC) byte).
810 *
811 * @param character char The character to process.
812 * @retval none
813 */
TelnetProcessCharacter(char character)814 void TelnetProcessCharacter(char character) {
815 #ifdef TELNET_CHAR_DEBUG
816 LOGD(PRINT_TAG, "[Telnet Server] Processing Character: %c/0x%02X\n\r", character, character);
817 #endif
818 /* Determine the current state of the telnet command parser. */
819 switch (telnet_server.state) {
820 /* The normal state of the parser, were each character is either sent
821 to the UART or is a telnet IAC character. */
822 case STATE_NORMAL: {
823 /* See if this character is the IAC character. */
824 if (character == TELNET_IAC) {
825 /* Skip this character and go to the IAC state. */
826 telnet_server.state = STATE_IAC;
827 } else {
828 /* Write this character to the receive buffer. */
829 TelnetRecvBufferWrite(character);
830 /* Echo this character */
831 TelnetWrite(character); //Echo the character back
832 }
833 break;
834 }
835 /* The previous character was the IAC character. */
836 case STATE_IAC: {
837 /* Determine how to interpret this character. */
838 switch (character) {
839 /* See if this character is also an IAC character. */
840 case TELNET_IAC: {
841 /* Write 0xff to the receive buffer. */
842 TelnetRecvBufferWrite(0xff);
843 /* Switch back to normal mode. */
844 telnet_server.state = STATE_NORMAL;
845 /* This character has been handled. */
846 break;
847 }
848 /* See if this character is the WILL request. */
849 case TELNET_WILL: {
850 /* Switch to the WILL mode; the next character will have
851 the option in question. */
852 telnet_server.state = STATE_WILL;
853 /* This character has been handled. */
854 break;
855 }
856 /* See if this character is the WONT request. */
857 case TELNET_WONT: {
858 /* Switch to the WONT mode; the next character will have
859 the option in question. */
860 telnet_server.state = STATE_WONT;
861 /* This character has been handled. */
862 break;
863 }
864 /* See if this character is the DO request. */
865 case TELNET_DO: {
866 /* Switch to the DO mode; the next character will have the
867 option in question. */
868 telnet_server.state = STATE_DO;
869 /* This character has been handled. */
870 break;
871 }
872 /* See if this character is the DONT request. */
873 case TELNET_DONT: {
874 /* Switch to the DONT mode; the next character will have
875 the option in question. */
876 telnet_server.state = STATE_DONT;
877 /* This character has been handled. */
878 break;
879 }
880 /* See if this character is the AYT request. */
881 case TELNET_AYT: {
882 /* Send a short string back to the client so that it knows
883 that the server is still alive. */
884 telnet_server.buffer[telnet_server.length++] = '\r';
885 telnet_server.buffer[telnet_server.length++] = '\n';
886 telnet_server.buffer[telnet_server.length++] = '[';
887 telnet_server.buffer[telnet_server.length++] = 'Y';
888 telnet_server.buffer[telnet_server.length++] = 'e';
889 telnet_server.buffer[telnet_server.length++] = 's';
890 telnet_server.buffer[telnet_server.length++] = ']';
891 telnet_server.buffer[telnet_server.length++] = '\r';
892 telnet_server.buffer[telnet_server.length++] = '\n';
893 /* Switch back to normal mode. */
894 telnet_server.state = STATE_NORMAL;
895 /* This character has been handled. */
896 break;
897 }
898 /* Explicitly ignore the GA and NOP request, plus provide a
899 catch-all ignore for unrecognized requests. */
900 case TELNET_GA:
901 case TELNET_NOP:
902 default: {
903 /* Switch back to normal mode. */
904 telnet_server.state = STATE_NORMAL;
905 /* This character has been handled. */
906 break;
907 }
908 }
909 /* This state has been handled. */
910 break;
911 }
912 /* The previous character sequence was IAC WILL. */
913 case STATE_WILL: {
914 /* Process the WILL request on this option. */
915 TelnetProcessWill(character);
916 /* Switch back to normal mode. */
917 telnet_server.state = STATE_NORMAL;
918 /* This state has been handled. */
919 break;
920 }
921 /* The previous character sequence was IAC WONT. */
922 case STATE_WONT: {
923 /* Process the WONT request on this option. */
924 TelnetProcessWont(character);
925 /* Switch back to normal mode. */
926 telnet_server.state = STATE_NORMAL;
927 /* This state has been handled. */
928 break;
929 }
930 /* The previous character sequence was IAC DO. */
931 case STATE_DO: {
932 /* Process the DO request on this option. */
933 TelnetProcessDo(character);
934 /* Switch back to normal mode. */
935 telnet_server.state = STATE_NORMAL;
936 /* This state has been handled. */
937 break;
938 }
939 /* The previous character sequence was IAC DONT. */
940 case STATE_DONT: {
941 /* Process the DONT request on this option. */
942 TelnetProcessDont(character);
943 /* Switch back to normal mode. */
944 telnet_server.state = STATE_NORMAL;
945 /* This state has been handled. */
946 break;
947 }
948 /* A catch-all for unknown states. This should never be reached, but
949 is provided just in case it is ever needed. */
950 default: {
951 /* Switch back to normal mode. */
952 telnet_server.state = STATE_NORMAL;
953 /* This state has been handled. */
954 break;
955 }
956 }
957 }
958
959 /**
960 * Print a message to the telnet connection formatted as an error.
961 *
962 * @param message char* Pointer to the string to send
963 * @retval none
964 */
TelnetWriteErrorMessage(char * message)965 void TelnetWriteErrorMessage(char* message) {
966 if (TelnetIsConnected() == true) {
967 ClearToMessageBuffer();
968 char* character = message;
969 while (*character) {
970 character++;
971 }
972 uint16_t n = snprintf(MESSAGE_BUFFER, sizeof(MESSAGE_BUFFER),
973 ERROR_MESSAGE_HEADER, message);
974 if (n > 0) {
975 TelnetWriteString(MESSAGE_BUFFER);
976 }
977 }
978 }
979
980 /**
981 * Print a message to the telnet connection formatted as a status.
982 *
983 * @param message char* Pointer to the string to send
984 * @retval none
985 */
TelnetWriteStatusMessage(char * message)986 void TelnetWriteStatusMessage(char* message) {
987 if (TelnetIsConnected() == true) {
988 ClearToMessageBuffer();
989 uint8_t count = 0;
990 char* character = message;
991 while (*character) {
992 ++character;
993 ++count;
994 }
995 uint16_t n = snprintf(MESSAGE_BUFFER, sizeof(MESSAGE_BUFFER),
996 STATUS_MESSAGE_HEADER, message);
997 if (n > 0) {
998 TelnetWriteString(MESSAGE_BUFFER);
999 }
1000 }
1001 }
1002
1003 /**
1004 * Print a message to the telnet connection formatted as a debug.
1005 *
1006 * @param message char* Pointer to the string to send
1007 * @retval none
1008 */
TelnetWriteDebugMessage(char * message)1009 void TelnetWriteDebugMessage(char* message) {
1010 if (TelnetIsConnected() == true) {
1011 ClearToMessageBuffer();
1012 uint8_t count = 0;
1013 char* character = message;
1014 while (*character) {
1015 ++character;
1016 ++count;
1017 }
1018 uint16_t n = snprintf(MESSAGE_BUFFER, sizeof(MESSAGE_BUFFER),
1019 DEBUG_MESSAGE_HEADER, message);
1020 if (n > 0) {
1021 TelnetWriteString(MESSAGE_BUFFER);
1022 }
1023 }
1024 }
1025
1026 /**
1027 * Print a message to the telnet connection formatted as a command data.
1028 *
1029 * @param message char* Pointer to the string to send
1030 * @retval none
1031 */
TelnetWriteCommandDataMessage(char * message)1032 void TelnetWriteCommandDataMessage(char* message) {
1033 if (TelnetIsConnected() == true) {
1034 ClearToMessageBuffer();
1035 uint8_t count = 0;
1036 char* character = message;
1037 while (*character) {
1038 ++character;
1039 ++count;
1040 }
1041 uint16_t n = snprintf(MESSAGE_BUFFER, sizeof(MESSAGE_BUFFER),
1042 COMMAND_DATA_MESSAGE_HEADER, message);
1043 if (n > 0) {
1044 TelnetWriteString(MESSAGE_BUFFER);
1045 }
1046 }
1047 }
1048