Branching from Cathaswatch project
https://git.nerdyssey.de/exodus/1NiM-WatingRoom-WatchController
This commit is contained in:
@@ -0,0 +1 @@
|
||||
e00a16d6372fa31df46b32e330847d6e09af2ec184d8c7cac4d183b3daac02e5
|
||||
@@ -0,0 +1,12 @@
|
||||
Original Telnet Server code is based on the version created and written by the following people:
|
||||
|
||||
- Sean Middleditch <sean@sourcemud.org>
|
||||
- Jack Kelly <endgame.dos@gmail.com>
|
||||
- Katherine Flavel <kate@elide.org>
|
||||
- Daniel Loffgren (https://github.com/RyuKojiro)
|
||||
|
||||
and taken from here: [https://github.com/seanmiddleditch/libtelnet](https://github.com/seanmiddleditch/libtelnet)
|
||||
|
||||
Midificatons are performed by:
|
||||
|
||||
- FrozenEye (https://github.com/frozen-eye)
|
||||
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-05-21T14:59:16.171166+00:00", "files": [{"path": "CMakeLists.txt", "size": 303, "hash": "70671f714f52ac79607119703f1ddab4a0b325f25e8981afe7c93baf7af7cd34"}, {"path": "LICENSE", "size": 1063, "hash": "068129b238141ba0ebcb2158402d73408c798f4d8c5a42f4dafaa41a002b9ea2"}, {"path": "AUTHORS", "size": 452, "hash": "277d45436ae96f30f6ee4b2dcde2661e679f2ded4f790cbee0c726d5f13c8dba"}, {"path": "idf_component.yml", "size": 179, "hash": "39f2a2bd0c96ab90610389356a2cf39842364514e2a69c1241e6ecd84b269d72"}, {"path": "Kconfig", "size": 1114, "hash": "ccc9fc290f957c953bdb3bbc8d1079efe7c5fdb82f10d6c7a2dc9973b4046bd7"}, {"path": ".gitignore", "size": 0, "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, {"path": "src/libtelnet.h", "size": 21611, "hash": "0798f3706642fdd4520923914ddfa59fb9ded670b7716877c80fa852153b8521"}, {"path": "src/server.c", "size": 9226, "hash": "bf90b0df126da4a16d79c626724ce715fdfb8015dd7ea0030b8a6cbf072c947f"}, {"path": "src/libtelnet.c", "size": 44236, "hash": "de15620edc5492aaf6c273ce4c9181cb99490a2153e56db7d7c3f96f3ea7e2b4"}, {"path": "include/telnet/server.h", "size": 1493, "hash": "ebe6d17a86a1c56c97e263bd4d468cffc5d95a3ca810e79388b2a9598f89f8ba"}]}
|
||||
@@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
idf_component_register(
|
||||
INCLUDE_DIRS
|
||||
include
|
||||
src
|
||||
.
|
||||
SRCS
|
||||
src/libtelnet.c
|
||||
src/server.c
|
||||
REQUIRES
|
||||
PRIV_REQUIRES
|
||||
)
|
||||
|
||||
# target_compile_options(${COMPONENT_LIB} PRIVATE -Wall -Wextra -Werror)
|
||||
# target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
|
||||
@@ -0,0 +1,41 @@
|
||||
menu "Telnet Server"
|
||||
|
||||
config TELNET_SERVER_DEFAULT_PORT
|
||||
int "Default Telnet Server Port"
|
||||
default 23
|
||||
help
|
||||
Default Telnet Server Port Number
|
||||
|
||||
config TELNET_SERVER_MAX_CONNECTIONS
|
||||
int "Maximum Telnet Server Connections"
|
||||
default 5
|
||||
help
|
||||
Maximum Number of Telnet Server Connections
|
||||
|
||||
config TELNET_SERVER_STACK_SIZE
|
||||
int "Telnet Server Stack Size"
|
||||
default 4096
|
||||
help
|
||||
Telnet Server Stack Size
|
||||
|
||||
config TELNET_SERVER_TASK_PRIORITY
|
||||
int "Telnet Server Task Priority"
|
||||
range 1 25
|
||||
default 24
|
||||
help
|
||||
Telnet Server Task Priority. In case of high events rate, this parameter could be changed up to (configMAX_PRIORITIES-1).
|
||||
|
||||
config TELNET_SERVER_TASK_CORE
|
||||
int "Telnet Server Task Core"
|
||||
range 0 1
|
||||
default 0
|
||||
help
|
||||
Telnet Server Task Core
|
||||
|
||||
config TELNET_SERVER_REDIRECT_LOGS
|
||||
int "Redirect Logs to Telnet Server"
|
||||
range 0 1
|
||||
default 0
|
||||
help
|
||||
Redirect Logs to Telnet Server
|
||||
endmenu
|
||||
@@ -0,0 +1,21 @@
|
||||
Copyright 2024 Frozen Eye
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,6 @@
|
||||
dependencies:
|
||||
idf:
|
||||
version: '>=4.0'
|
||||
description: esp_telnet is a small implementation of the telnet server.
|
||||
url: https://github.com/frozen-eye/esp_telnet.git
|
||||
version: 0.0.2
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include <libtelnet.h>
|
||||
|
||||
static const telnet_telopt_t default_telopts[] = {
|
||||
{TELNET_TELOPT_COMPRESS2, TELNET_WILL, TELNET_DO}, {TELNET_TELOPT_ZMP, TELNET_WILL, TELNET_DO},
|
||||
{TELNET_TELOPT_MSSP, TELNET_WILL, TELNET_DONT}, {TELNET_TELOPT_NEW_ENVIRON, TELNET_WILL, TELNET_DONT},
|
||||
{TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT}, {-1, 0, 0}};
|
||||
|
||||
struct user_t {
|
||||
char* name;
|
||||
int sock;
|
||||
telnet_t* telnet;
|
||||
char linebuf[255];
|
||||
int linepos;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct telnet_server_config {
|
||||
int port;
|
||||
int stack_size;
|
||||
int task_priority;
|
||||
int task_core;
|
||||
int redirect_logs;
|
||||
int max_connections;
|
||||
const telnet_telopt_t *telnet_opts;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Telnet Server Default Configuration
|
||||
*
|
||||
*/
|
||||
#define TELNET_SERVER_DEFAULT_CONFIG \
|
||||
{ \
|
||||
.port = CONFIG_TELNET_SERVER_DEFAULT_PORT, \
|
||||
.stack_size = CONFIG_TELNET_SERVER_STACK_SIZE, \
|
||||
.task_priority = CONFIG_TELNET_SERVER_TASK_PRIORITY, \
|
||||
.task_core = CONFIG_TELNET_SERVER_TASK_CORE, \
|
||||
.redirect_logs = CONFIG_TELNET_SERVER_REDIRECT_LOGS, \
|
||||
.max_connections = CONFIG_TELNET_SERVER_MAX_CONNECTIONS, \
|
||||
.telnet_opts = default_telopts, \
|
||||
}
|
||||
|
||||
typedef struct telnet_server_config telnet_server_config_t;
|
||||
|
||||
esp_err_t telnet_server_create(telnet_server_config_t* config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,691 @@
|
||||
/*!
|
||||
* \brief libtelnet - TELNET protocol handling library
|
||||
*
|
||||
* SUMMARY:
|
||||
*
|
||||
* libtelnet is a library for handling the TELNET protocol. It includes
|
||||
* routines for parsing incoming data from a remote peer as well as formatting
|
||||
* data to send to the remote peer.
|
||||
*
|
||||
* libtelnet uses a callback-oriented API, allowing application-specific
|
||||
* handling of various events. The callback system is also used for buffering
|
||||
* outgoing protocol data, allowing the application to maintain control over
|
||||
* the actual socket connection.
|
||||
*
|
||||
* Features supported include the full TELNET protocol, Q-method option
|
||||
* negotiation, ZMP, MCCP2, MSSP, and NEW-ENVIRON.
|
||||
*
|
||||
* CONFORMS TO:
|
||||
*
|
||||
* RFC854 - http://www.faqs.org/rfcs/rfc854.html
|
||||
* RFC855 - http://www.faqs.org/rfcs/rfc855.html
|
||||
* RFC1091 - http://www.faqs.org/rfcs/rfc1091.html
|
||||
* RFC1143 - http://www.faqs.org/rfcs/rfc1143.html
|
||||
* RFC1408 - http://www.faqs.org/rfcs/rfc1408.html
|
||||
* RFC1572 - http://www.faqs.org/rfcs/rfc1572.html
|
||||
*
|
||||
* LICENSE:
|
||||
*
|
||||
* The author or authors of this code dedicate any and all copyright interest
|
||||
* in this code to the public domain. We make this dedication for the benefit
|
||||
* of the public at large and to the detriment of our heirs and successors. We
|
||||
* intend this dedication to be an overt act of relinquishment in perpetuity of
|
||||
* all present and future rights to this code under copyright law.
|
||||
*
|
||||
* \file libtelnet.h
|
||||
*
|
||||
* \version 0.23
|
||||
*
|
||||
* \author Sean Middleditch <sean@sourcemud.org>
|
||||
*/
|
||||
|
||||
#if !defined(LIBTELNET_INCLUDE)
|
||||
#define LIBTELNET_INCLUDE 1
|
||||
|
||||
/* standard C headers necessary for the libtelnet API */
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* C++ support */
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* printf type checking feature in GCC and some other compilers */
|
||||
#if __GNUC__
|
||||
# define TELNET_GNU_PRINTF(f,a) __attribute__((format(printf, f, a))) /*!< internal helper */
|
||||
# define TELNET_GNU_SENTINEL __attribute__((sentinel)) /*!< internal helper */
|
||||
#else
|
||||
# define TELNET_GNU_PRINTF(f,a) /*!< internal helper */
|
||||
# define TELNET_GNU_SENTINEL /*!< internal helper */
|
||||
#endif
|
||||
|
||||
/* Disable environ macro for Visual C++ 2015. */
|
||||
#undef environ
|
||||
|
||||
/*! Telnet state tracker object type. */
|
||||
typedef struct telnet_t telnet_t;
|
||||
|
||||
/*! Telnet event object type. */
|
||||
typedef union telnet_event_t telnet_event_t;
|
||||
|
||||
/*! Telnet option table element type. */
|
||||
typedef struct telnet_telopt_t telnet_telopt_t;
|
||||
|
||||
/*! \name Telnet commands */
|
||||
/*@{*/
|
||||
/*! Telnet commands and special values. */
|
||||
#define TELNET_IAC 255
|
||||
#define TELNET_DONT 254
|
||||
#define TELNET_DO 253
|
||||
#define TELNET_WONT 252
|
||||
#define TELNET_WILL 251
|
||||
#define TELNET_SB 250
|
||||
#define TELNET_GA 249
|
||||
#define TELNET_EL 248
|
||||
#define TELNET_EC 247
|
||||
#define TELNET_AYT 246
|
||||
#define TELNET_AO 245
|
||||
#define TELNET_IP 244
|
||||
#define TELNET_BREAK 243
|
||||
#define TELNET_DM 242
|
||||
#define TELNET_NOP 241
|
||||
#define TELNET_SE 240
|
||||
#define TELNET_EOR 239
|
||||
#define TELNET_ABORT 238
|
||||
#define TELNET_SUSP 237
|
||||
#define TELNET_EOF 236
|
||||
/*@}*/
|
||||
|
||||
/*! \name Telnet option values. */
|
||||
/*@{*/
|
||||
/*! Telnet options. */
|
||||
#define TELNET_TELOPT_BINARY 0
|
||||
#define TELNET_TELOPT_ECHO 1
|
||||
#define TELNET_TELOPT_RCP 2
|
||||
#define TELNET_TELOPT_SGA 3
|
||||
#define TELNET_TELOPT_NAMS 4
|
||||
#define TELNET_TELOPT_STATUS 5
|
||||
#define TELNET_TELOPT_TM 6
|
||||
#define TELNET_TELOPT_RCTE 7
|
||||
#define TELNET_TELOPT_NAOL 8
|
||||
#define TELNET_TELOPT_NAOP 9
|
||||
#define TELNET_TELOPT_NAOCRD 10
|
||||
#define TELNET_TELOPT_NAOHTS 11
|
||||
#define TELNET_TELOPT_NAOHTD 12
|
||||
#define TELNET_TELOPT_NAOFFD 13
|
||||
#define TELNET_TELOPT_NAOVTS 14
|
||||
#define TELNET_TELOPT_NAOVTD 15
|
||||
#define TELNET_TELOPT_NAOLFD 16
|
||||
#define TELNET_TELOPT_XASCII 17
|
||||
#define TELNET_TELOPT_LOGOUT 18
|
||||
#define TELNET_TELOPT_BM 19
|
||||
#define TELNET_TELOPT_DET 20
|
||||
#define TELNET_TELOPT_SUPDUP 21
|
||||
#define TELNET_TELOPT_SUPDUPOUTPUT 22
|
||||
#define TELNET_TELOPT_SNDLOC 23
|
||||
#define TELNET_TELOPT_TTYPE 24
|
||||
#define TELNET_TELOPT_EOR 25
|
||||
#define TELNET_TELOPT_TUID 26
|
||||
#define TELNET_TELOPT_OUTMRK 27
|
||||
#define TELNET_TELOPT_TTYLOC 28
|
||||
#define TELNET_TELOPT_3270REGIME 29
|
||||
#define TELNET_TELOPT_X3PAD 30
|
||||
#define TELNET_TELOPT_NAWS 31
|
||||
#define TELNET_TELOPT_TSPEED 32
|
||||
#define TELNET_TELOPT_LFLOW 33
|
||||
#define TELNET_TELOPT_LINEMODE 34
|
||||
#define TELNET_TELOPT_XDISPLOC 35
|
||||
#define TELNET_TELOPT_ENVIRON 36
|
||||
#define TELNET_TELOPT_AUTHENTICATION 37
|
||||
#define TELNET_TELOPT_ENCRYPT 38
|
||||
#define TELNET_TELOPT_NEW_ENVIRON 39
|
||||
#define TELNET_TELOPT_MSSP 70
|
||||
#define TELNET_TELOPT_COMPRESS 85
|
||||
#define TELNET_TELOPT_COMPRESS2 86
|
||||
#define TELNET_TELOPT_ZMP 93
|
||||
#define TELNET_TELOPT_EXOPL 255
|
||||
|
||||
#define TELNET_TELOPT_MCCP2 86
|
||||
/*@}*/
|
||||
|
||||
/*! \name Protocol codes for TERMINAL-TYPE commands. */
|
||||
/*@{*/
|
||||
/*! TERMINAL-TYPE codes. */
|
||||
#define TELNET_TTYPE_IS 0
|
||||
#define TELNET_TTYPE_SEND 1
|
||||
/*@}*/
|
||||
|
||||
/*! \name Protocol codes for NEW-ENVIRON/ENVIRON commands. */
|
||||
/*@{*/
|
||||
/*! NEW-ENVIRON/ENVIRON codes. */
|
||||
#define TELNET_ENVIRON_IS 0
|
||||
#define TELNET_ENVIRON_SEND 1
|
||||
#define TELNET_ENVIRON_INFO 2
|
||||
#define TELNET_ENVIRON_VAR 0
|
||||
#define TELNET_ENVIRON_VALUE 1
|
||||
#define TELNET_ENVIRON_ESC 2
|
||||
#define TELNET_ENVIRON_USERVAR 3
|
||||
/*@}*/
|
||||
|
||||
/*! \name Protocol codes for MSSP commands. */
|
||||
/*@{*/
|
||||
/*! MSSP codes. */
|
||||
#define TELNET_MSSP_VAR 1
|
||||
#define TELNET_MSSP_VAL 2
|
||||
/*@}*/
|
||||
|
||||
/*! \name Telnet state tracker flags. */
|
||||
/*@{*/
|
||||
/*! Control behavior of telnet state tracker. */
|
||||
#define TELNET_FLAG_PROXY (1<<0)
|
||||
#define TELNET_FLAG_NVT_EOL (1<<1)
|
||||
|
||||
/* Internal-only bits in option flags */
|
||||
#define TELNET_FLAG_TRANSMIT_BINARY (1<<5)
|
||||
#define TELNET_FLAG_RECEIVE_BINARY (1<<6)
|
||||
#define TELNET_PFLAG_DEFLATE (1<<7)
|
||||
/*@}*/
|
||||
|
||||
/*!
|
||||
* error codes
|
||||
*/
|
||||
enum telnet_error_t {
|
||||
TELNET_EOK = 0, /*!< no error */
|
||||
TELNET_EBADVAL, /*!< invalid parameter, or API misuse */
|
||||
TELNET_ENOMEM, /*!< memory allocation failure */
|
||||
TELNET_EOVERFLOW, /*!< data exceeds buffer size */
|
||||
TELNET_EPROTOCOL, /*!< invalid sequence of special bytes */
|
||||
TELNET_ECOMPRESS /*!< error handling compressed streams */
|
||||
};
|
||||
typedef enum telnet_error_t telnet_error_t; /*!< Error code type. */
|
||||
|
||||
/*!
|
||||
* event codes
|
||||
*/
|
||||
enum telnet_event_type_t {
|
||||
TELNET_EV_DATA = 0, /*!< raw text data has been received */
|
||||
TELNET_EV_SEND, /*!< data needs to be sent to the peer */
|
||||
TELNET_EV_IAC, /*!< generic IAC code received */
|
||||
TELNET_EV_WILL, /*!< WILL option negotiation received */
|
||||
TELNET_EV_WONT, /*!< WONT option neogitation received */
|
||||
TELNET_EV_DO, /*!< DO option negotiation received */
|
||||
TELNET_EV_DONT, /*!< DONT option negotiation received */
|
||||
TELNET_EV_SUBNEGOTIATION, /*!< sub-negotiation data received */
|
||||
TELNET_EV_COMPRESS, /*!< compression has been enabled */
|
||||
TELNET_EV_ZMP, /*!< ZMP command has been received */
|
||||
TELNET_EV_TTYPE, /*!< TTYPE command has been received */
|
||||
TELNET_EV_ENVIRON, /*!< ENVIRON command has been received */
|
||||
TELNET_EV_MSSP, /*!< MSSP command has been received */
|
||||
TELNET_EV_WARNING, /*!< recoverable error has occured */
|
||||
TELNET_EV_ERROR /*!< non-recoverable error has occured */
|
||||
};
|
||||
typedef enum telnet_event_type_t telnet_event_type_t; /*!< Telnet event type. */
|
||||
|
||||
/*!
|
||||
* environ/MSSP command information
|
||||
*/
|
||||
struct telnet_environ_t {
|
||||
unsigned char type; /*!< either TELNET_ENVIRON_VAR or TELNET_ENVIRON_USERVAR */
|
||||
char *var; /*!< name of the variable being set */
|
||||
char *value; /*!< value of variable being set; empty string if no value */
|
||||
};
|
||||
|
||||
/*!
|
||||
* event information
|
||||
*/
|
||||
union telnet_event_t {
|
||||
/*!
|
||||
* \brief Event type
|
||||
*
|
||||
* The type field will determine which of the other event structure fields
|
||||
* have been filled in. For instance, if the event type is TELNET_EV_ZMP,
|
||||
* then the zmp event field (and ONLY the zmp event field) will be filled
|
||||
* in.
|
||||
*/
|
||||
enum telnet_event_type_t type;
|
||||
|
||||
/*!
|
||||
* data event: for DATA and SEND events
|
||||
*/
|
||||
struct data_t {
|
||||
enum telnet_event_type_t _type; /*!< alias for type */
|
||||
const char *buffer; /*!< byte buffer */
|
||||
size_t size; /*!< number of bytes in buffer */
|
||||
} data; /*!< DATA and SEND */
|
||||
|
||||
/*!
|
||||
* WARNING and ERROR events
|
||||
*/
|
||||
struct error_t {
|
||||
enum telnet_event_type_t _type; /*!< alias for type */
|
||||
const char *file; /*!< file the error occured in */
|
||||
const char *func; /*!< function the error occured in */
|
||||
const char *msg; /*!< error message string */
|
||||
int line; /*!< line of file error occured on */
|
||||
telnet_error_t errcode; /*!< error code */
|
||||
} error; /*!< WARNING and ERROR */
|
||||
|
||||
/*!
|
||||
* command event: for IAC
|
||||
*/
|
||||
struct iac_t {
|
||||
enum telnet_event_type_t _type; /*!< alias for type */
|
||||
unsigned char cmd; /*!< telnet command received */
|
||||
} iac; /*!< IAC */
|
||||
|
||||
/*!
|
||||
* negotiation event: WILL, WONT, DO, DONT
|
||||
*/
|
||||
struct negotiate_t {
|
||||
enum telnet_event_type_t _type; /*!< alias for type */
|
||||
unsigned char telopt; /*!< option being negotiated */
|
||||
} neg; /*!< WILL, WONT, DO, DONT */
|
||||
|
||||
/*!
|
||||
* subnegotiation event
|
||||
*/
|
||||
struct subnegotiate_t {
|
||||
enum telnet_event_type_t _type; /*!< alias for type */
|
||||
const char *buffer; /*!< data of sub-negotiation */
|
||||
size_t size; /*!< number of bytes in buffer */
|
||||
unsigned char telopt; /*!< option code for negotiation */
|
||||
} sub; /*!< SB */
|
||||
|
||||
/*!
|
||||
* ZMP event
|
||||
*/
|
||||
struct zmp_t {
|
||||
enum telnet_event_type_t _type; /*!< alias for type */
|
||||
const char **argv; /*!< array of argument string */
|
||||
size_t argc; /*!< number of elements in argv */
|
||||
} zmp; /*!< ZMP */
|
||||
|
||||
/*!
|
||||
* TTYPE event
|
||||
*/
|
||||
struct ttype_t {
|
||||
enum telnet_event_type_t _type; /*!< alias for type */
|
||||
unsigned char cmd; /*!< TELNET_TTYPE_IS or TELNET_TTYPE_SEND */
|
||||
const char* name; /*!< terminal type name (IS only) */
|
||||
} ttype; /*!< TTYPE */
|
||||
|
||||
/*!
|
||||
* COMPRESS event
|
||||
*/
|
||||
struct compress_t {
|
||||
enum telnet_event_type_t _type; /*!< alias for type */
|
||||
unsigned char state; /*!< 1 if compression is enabled,
|
||||
0 if disabled */
|
||||
} compress; /*!< COMPRESS */
|
||||
|
||||
/*!
|
||||
* ENVIRON/NEW-ENVIRON event
|
||||
*/
|
||||
struct environ_t {
|
||||
enum telnet_event_type_t _type; /*!< alias for type */
|
||||
const struct telnet_environ_t *values; /*!< array of variable values */
|
||||
size_t size; /*!< number of elements in values */
|
||||
unsigned char cmd; /*!< SEND, IS, or INFO */
|
||||
} environ; /*!< ENVIRON, NEW-ENVIRON */
|
||||
|
||||
/*!
|
||||
* MSSP event
|
||||
*/
|
||||
struct mssp_t {
|
||||
enum telnet_event_type_t _type; /*!< alias for type */
|
||||
const struct telnet_environ_t *values; /*!< array of variable values */
|
||||
size_t size; /*!< number of elements in values */
|
||||
} mssp; /*!< MSSP */
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief event handler
|
||||
*
|
||||
* This is the type of function that must be passed to
|
||||
* telnet_init() when creating a new telnet object. The
|
||||
* function will be invoked once for every event generated
|
||||
* by the libtelnet protocol parser.
|
||||
*
|
||||
* \param telnet The telnet object that generated the event
|
||||
* \param event Event structure with details about the event
|
||||
* \param user_data User-supplied pointer
|
||||
*/
|
||||
typedef void (*telnet_event_handler_t)(telnet_t *telnet,
|
||||
telnet_event_t *event, void *user_data);
|
||||
|
||||
/*!
|
||||
* telopt support table element; use telopt of -1 for end marker
|
||||
*/
|
||||
struct telnet_telopt_t {
|
||||
short telopt; /*!< one of the TELOPT codes or -1 */
|
||||
unsigned char us; /*!< TELNET_WILL or TELNET_WONT */
|
||||
unsigned char him; /*!< TELNET_DO or TELNET_DONT */
|
||||
};
|
||||
|
||||
/*!
|
||||
* state tracker -- private data structure
|
||||
*/
|
||||
struct telnet_t;
|
||||
|
||||
/*!
|
||||
* \brief Initialize a telnet state tracker.
|
||||
*
|
||||
* This function initializes a new state tracker, which is used for all
|
||||
* other libtelnet functions. Each connection must have its own
|
||||
* telnet state tracker object.
|
||||
*
|
||||
* \param telopts Table of TELNET options the application supports.
|
||||
* \param eh Event handler function called for every event.
|
||||
* \param flags 0 or TELNET_FLAG_PROXY.
|
||||
* \param user_data Optional data pointer that will be passsed to eh.
|
||||
* \return Telnet state tracker object.
|
||||
*/
|
||||
extern telnet_t* telnet_init(const telnet_telopt_t *telopts,
|
||||
telnet_event_handler_t eh, unsigned char flags, void *user_data);
|
||||
|
||||
/*!
|
||||
* \brief Free up any memory allocated by a state tracker.
|
||||
*
|
||||
* This function must be called when a telnet state tracker is no
|
||||
* longer needed (such as after the connection has been closed) to
|
||||
* release any memory resources used by the state tracker.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
*/
|
||||
extern void telnet_free(telnet_t *telnet);
|
||||
|
||||
/*!
|
||||
* \brief Push a byte buffer into the state tracker.
|
||||
*
|
||||
* Passes one or more bytes to the telnet state tracker for
|
||||
* protocol parsing. The byte buffer is most often going to be
|
||||
* the buffer that recv() was called for while handling the
|
||||
* connection.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param buffer Pointer to byte buffer.
|
||||
* \param size Number of bytes pointed to by buffer.
|
||||
*/
|
||||
extern void telnet_recv(telnet_t *telnet, const char *buffer,
|
||||
size_t size);
|
||||
|
||||
/*!
|
||||
* \brief Send a telnet command.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param cmd Command to send.
|
||||
*/
|
||||
extern void telnet_iac(telnet_t *telnet, unsigned char cmd);
|
||||
|
||||
/*!
|
||||
* \brief Send negotiation command.
|
||||
*
|
||||
* Internally, libtelnet uses RFC1143 option negotiation rules.
|
||||
* The negotiation commands sent with this function may be ignored
|
||||
* if they are determined to be redundant.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param cmd TELNET_WILL, TELNET_WONT, TELNET_DO, or TELNET_DONT.
|
||||
* \param opt One of the TELNET_TELOPT_* values.
|
||||
*/
|
||||
extern void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
|
||||
unsigned char opt);
|
||||
|
||||
/*!
|
||||
* Send non-command data (escapes IAC bytes).
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param buffer Buffer of bytes to send.
|
||||
* \param size Number of bytes to send.
|
||||
*/
|
||||
extern void telnet_send(telnet_t *telnet,
|
||||
const char *buffer, size_t size);
|
||||
|
||||
/*!
|
||||
* Send non-command text (escapes IAC bytes and translates
|
||||
* \\r -> CR-NUL and \\n -> CR-LF unless in BINARY mode.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param buffer Buffer of bytes to send.
|
||||
* \param size Number of bytes to send.
|
||||
*/
|
||||
extern void telnet_send_text(telnet_t *telnet,
|
||||
const char *buffer, size_t size);
|
||||
|
||||
/*!
|
||||
* \brief Begin a sub-negotiation command.
|
||||
*
|
||||
* Sends IAC SB followed by the telopt code. All following data sent
|
||||
* will be part of the sub-negotiation, until telnet_finish_sb() is
|
||||
* called.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param telopt One of the TELNET_TELOPT_* values.
|
||||
*/
|
||||
extern void telnet_begin_sb(telnet_t *telnet,
|
||||
unsigned char telopt);
|
||||
|
||||
/*!
|
||||
* \brief Finish a sub-negotiation command.
|
||||
*
|
||||
* This must be called after a call to telnet_begin_sb() to finish a
|
||||
* sub-negotiation command.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
*/
|
||||
#define telnet_finish_sb(telnet) telnet_iac((telnet), TELNET_SE)
|
||||
|
||||
/*!
|
||||
* \brief Shortcut for sending a complete subnegotiation buffer.
|
||||
*
|
||||
* Equivalent to:
|
||||
* telnet_begin_sb(telnet, telopt);
|
||||
* telnet_send(telnet, buffer, size);
|
||||
* telnet_finish_sb(telnet);
|
||||
*
|
||||
* \param telnet Telnet state tracker format.
|
||||
* \param telopt One of the TELNET_TELOPT_* values.
|
||||
* \param buffer Byte buffer for sub-negotiation data.
|
||||
* \param size Number of bytes to use for sub-negotiation data.
|
||||
*/
|
||||
extern void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
|
||||
const char *buffer, size_t size);
|
||||
|
||||
/*!
|
||||
* \brief Begin sending compressed data.
|
||||
*
|
||||
* This function will begein sending data using the COMPRESS2 option,
|
||||
* which enables the use of zlib to compress data sent to the client.
|
||||
* The client must offer support for COMPRESS2 with option negotiation,
|
||||
* and zlib support must be compiled into libtelnet.
|
||||
*
|
||||
* Only the server may call this command.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
*/
|
||||
extern void telnet_begin_compress2(telnet_t *telnet);
|
||||
|
||||
/*!
|
||||
* \brief Send formatted data.
|
||||
*
|
||||
* This function is a wrapper around telnet_send(). It allows using
|
||||
* printf-style formatting.
|
||||
*
|
||||
* Additionally, this function will translate \\r to the CR NUL construct and
|
||||
* \\n with CR LF, as well as automatically escaping IAC bytes like
|
||||
* telnet_send().
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param fmt Format string.
|
||||
* \return Number of bytes sent.
|
||||
*/
|
||||
extern int telnet_printf(telnet_t *telnet, const char *fmt, ...)
|
||||
TELNET_GNU_PRINTF(2, 3);
|
||||
|
||||
/*!
|
||||
* \brief Send formatted data.
|
||||
*
|
||||
* See telnet_printf().
|
||||
*/
|
||||
extern int telnet_vprintf(telnet_t *telnet, const char *fmt, va_list va);
|
||||
|
||||
/*!
|
||||
* \brief Send formatted data (no newline escaping).
|
||||
*
|
||||
* This behaves identically to telnet_printf(), except that the \\r and \\n
|
||||
* characters are not translated. The IAC byte is still escaped as normal
|
||||
* with telnet_send().
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param fmt Format string.
|
||||
* \return Number of bytes sent.
|
||||
*/
|
||||
extern int telnet_raw_printf(telnet_t *telnet, const char *fmt, ...)
|
||||
TELNET_GNU_PRINTF(2, 3);
|
||||
|
||||
/*!
|
||||
* \brief Send formatted data (no newline escaping).
|
||||
*
|
||||
* See telnet_raw_printf().
|
||||
*/
|
||||
extern int telnet_raw_vprintf(telnet_t *telnet, const char *fmt, va_list va);
|
||||
|
||||
/*!
|
||||
* \brief Begin a new set of NEW-ENVIRON values to request or send.
|
||||
*
|
||||
* This function will begin the sub-negotiation block for sending or
|
||||
* requesting NEW-ENVIRON values.
|
||||
*
|
||||
* The telnet_finish_newenviron() macro must be called after this
|
||||
* function to terminate the NEW-ENVIRON command.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param type One of TELNET_ENVIRON_SEND, TELNET_ENVIRON_IS, or
|
||||
* TELNET_ENVIRON_INFO.
|
||||
*/
|
||||
extern void telnet_begin_newenviron(telnet_t *telnet, unsigned char type);
|
||||
|
||||
/*!
|
||||
* \brief Send a NEW-ENVIRON variable name or value.
|
||||
*
|
||||
* This can only be called between calls to telnet_begin_newenviron() and
|
||||
* telnet_finish_newenviron().
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param type One of TELNET_ENVIRON_VAR, TELNET_ENVIRON_USERVAR, or
|
||||
* TELNET_ENVIRON_VALUE.
|
||||
* \param string Variable name or value.
|
||||
*/
|
||||
extern void telnet_newenviron_value(telnet_t* telnet, unsigned char type,
|
||||
const char *string);
|
||||
|
||||
/*!
|
||||
* \brief Finish a NEW-ENVIRON command.
|
||||
*
|
||||
* This must be called after a call to telnet_begin_newenviron() to finish a
|
||||
* NEW-ENVIRON variable list.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
*/
|
||||
#define telnet_finish_newenviron(telnet) telnet_finish_sb((telnet))
|
||||
|
||||
/*!
|
||||
* \brief Send the TERMINAL-TYPE SEND command.
|
||||
*
|
||||
* Sends the sequence IAC TERMINAL-TYPE SEND.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
*/
|
||||
extern void telnet_ttype_send(telnet_t *telnet);
|
||||
|
||||
/*!
|
||||
* \brief Send the TERMINAL-TYPE IS command.
|
||||
*
|
||||
* Sends the sequence IAC TERMINAL-TYPE IS "string".
|
||||
*
|
||||
* According to the RFC, the recipient of a TERMINAL-TYPE SEND shall
|
||||
* send the next possible terminal-type the client supports. Upon sending
|
||||
* the type, the client should switch modes to begin acting as the terminal
|
||||
* type is just sent.
|
||||
*
|
||||
* The server may continue sending TERMINAL-TYPE IS until it receives a
|
||||
* terminal type is understands. To indicate to the server that it has
|
||||
* reached the end of the available optoins, the client must send the last
|
||||
* terminal type a second time. When the server receives the same terminal
|
||||
* type twice in a row, it knows it has seen all available terminal types.
|
||||
*
|
||||
* After the last terminal type is sent, if the client receives another
|
||||
* TERMINAL-TYPE SEND command, it must begin enumerating the available
|
||||
* terminal types from the very beginning. This allows the server to
|
||||
* scan the available types for a preferred terminal type and, if none
|
||||
* is found, to then ask the client to switch to an acceptable
|
||||
* alternative.
|
||||
*
|
||||
* Note that if the client only supports a single terminal type, then
|
||||
* simply sending that one type in response to every SEND will satisfy
|
||||
* the behavior requirements.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param ttype Name of the terminal-type being sent.
|
||||
*/
|
||||
extern void telnet_ttype_is(telnet_t *telnet, const char* ttype);
|
||||
|
||||
/*!
|
||||
* \brief Send a ZMP command.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param argc Number of ZMP commands being sent.
|
||||
* \param argv Array of argument strings.
|
||||
*/
|
||||
extern void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv);
|
||||
|
||||
/*!
|
||||
* \brief Send a ZMP command.
|
||||
*
|
||||
* Arguments are listed out in var-args style. After the last argument, a
|
||||
* NULL pointer must be passed in as a sentinel value.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
*/
|
||||
extern void telnet_send_zmpv(telnet_t *telnet, ...) TELNET_GNU_SENTINEL;
|
||||
|
||||
/*!
|
||||
* \brief Send a ZMP command.
|
||||
*
|
||||
* See telnet_send_zmpv().
|
||||
*/
|
||||
extern void telnet_send_vzmpv(telnet_t *telnet, va_list va);
|
||||
|
||||
/*!
|
||||
* \brief Begin sending a ZMP command
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param cmd The first argument (command name) for the ZMP command.
|
||||
*/
|
||||
extern void telnet_begin_zmp(telnet_t *telnet, const char *cmd);
|
||||
|
||||
/*!
|
||||
* \brief Send a ZMP command argument.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
* \param arg Telnet argument string.
|
||||
*/
|
||||
extern void telnet_zmp_arg(telnet_t *telnet, const char *arg);
|
||||
|
||||
/*!
|
||||
* \brief Finish a ZMP command.
|
||||
*
|
||||
* This must be called after a call to telnet_begin_zmp() to finish a
|
||||
* ZMP argument list.
|
||||
*
|
||||
* \param telnet Telnet state tracker object.
|
||||
*/
|
||||
#define telnet_finish_zmp(telnet) telnet_finish_sb((telnet))
|
||||
|
||||
/* C++ support */
|
||||
#if defined(__cplusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* !defined(LIBTELNET_INCLUDE) */
|
||||
@@ -0,0 +1,336 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define LINEBUFFER_SIZE 256
|
||||
#define AWAIT_TIMEOUT 10
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <libtelnet.h>
|
||||
#include <lwip/def.h>
|
||||
#include <lwip/sockets.h>
|
||||
#include <telnet/server.h>
|
||||
|
||||
static const char* TAG = "telnet";
|
||||
static struct user_t users[CONFIG_TELNET_SERVER_MAX_CONNECTIONS + 1];
|
||||
|
||||
static void linebuffer_push(char* buffer, size_t size, int* linepos, char ch,
|
||||
void (*cb)(const char* line, size_t overflow, void* ud), void* ud)
|
||||
{
|
||||
/* CRLF -- line terminator */
|
||||
if (ch == '\n' && *linepos > 0 && buffer[*linepos - 1] == '\r') {
|
||||
/* NUL terminate (replaces \r in buffer), notify app, clear */
|
||||
buffer[*linepos - 1] = 0;
|
||||
cb(buffer, 0, ud);
|
||||
*linepos = 0;
|
||||
|
||||
/* CRNUL -- just a CR */
|
||||
}
|
||||
else if (ch == 0 && *linepos > 0 && buffer[*linepos - 1] == '\r') {
|
||||
/* do nothing, the CR is already in the buffer */
|
||||
|
||||
/* anything else (including technically invalid CR followed by
|
||||
* anything besides LF or NUL -- just buffer if we have room
|
||||
* \r
|
||||
*/
|
||||
}
|
||||
else if (*linepos != (int)size) {
|
||||
buffer[(*linepos)++] = ch;
|
||||
|
||||
/* buffer overflow */
|
||||
}
|
||||
else {
|
||||
/* terminate (NOTE: eats a byte), notify app, clear buffer */
|
||||
buffer[size - 1] = 0;
|
||||
cb(buffer, size - 1, ud);
|
||||
*linepos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void _message(const char* from, const char* msg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i != CONFIG_TELNET_SERVER_MAX_CONNECTIONS; ++i) {
|
||||
if (users[i].sock != -1 && users[i].name != 0 && strcmp(users[i].name, from) != 0) {
|
||||
telnet_printf(users[i].telnet, "%s: \"%s\"\n", from, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _broadcast(const char* from, const char* msg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i != CONFIG_TELNET_SERVER_MAX_CONNECTIONS; ++i) {
|
||||
if (users[i].sock != -1) {
|
||||
telnet_printf(users[i].telnet, "%s: \"%s\"\n", from, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _send(int sock, const char* buffer, size_t size)
|
||||
{
|
||||
int rs;
|
||||
|
||||
/* ignore on invalid socket */
|
||||
if (sock == -1)
|
||||
return;
|
||||
|
||||
/* send data */
|
||||
while (size > 0) {
|
||||
if ((rs = send(sock, buffer, (int)size, 0)) == -1) {
|
||||
if (errno != EINTR && errno != ECONNRESET) {
|
||||
ESP_LOGW(TAG, "send() failed: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (rs == 0) {
|
||||
ESP_LOGE(TAG, "send() unexpectedly returned 0");
|
||||
return;
|
||||
}
|
||||
|
||||
/* update pointer and size to see if we've got more to send */
|
||||
buffer += rs;
|
||||
size -= rs;
|
||||
}
|
||||
}
|
||||
|
||||
/* process input line */
|
||||
static void _online(const char* line, size_t overflow, void* ud)
|
||||
{
|
||||
struct user_t* user = (struct user_t*)ud;
|
||||
int i;
|
||||
|
||||
(void)overflow;
|
||||
|
||||
/* if the user has no name, this is his "login" */
|
||||
if (user->name == 0) {
|
||||
/* must not be empty, must be at least 32 chars */
|
||||
if (strlen(line) == 0 || strlen(line) > 32) {
|
||||
telnet_printf(user->telnet, "Invalid name. Enter name: ");
|
||||
return;
|
||||
}
|
||||
|
||||
/* must not already exist */
|
||||
for (i = 0; i != CONFIG_TELNET_SERVER_MAX_CONNECTIONS; ++i) {
|
||||
if (users[i].name != 0 && strcmp(users[i].name, line) == 0) {
|
||||
telnet_printf(user->telnet, "Name already in use. Enter name: ");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* keep name */
|
||||
user->name = strdup(line);
|
||||
telnet_printf(user->telnet, "Welcome, %s!\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
/* execute a command, need to send to the system */
|
||||
// _message(user->name, line);
|
||||
}
|
||||
|
||||
static void _input(struct user_t* user, const char* buffer, size_t size)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; user->sock != -1 && i != size; ++i) {
|
||||
linebuffer_push(user->linebuf, sizeof(user->linebuf), &user->linepos, (char)buffer[i], _online, user);
|
||||
}
|
||||
}
|
||||
|
||||
static void _event_handler(telnet_t* telnet, telnet_event_t* ev, void* user_data)
|
||||
{
|
||||
struct user_t* user = (struct user_t*)user_data;
|
||||
|
||||
switch (ev->type) {
|
||||
/* data received */
|
||||
case TELNET_EV_DATA:
|
||||
_input(user, ev->data.buffer, ev->data.size);
|
||||
// telnet_negotiate(telnet, TELNET_WONT, TELNET_TELOPT_ECHO);
|
||||
// telnet_negotiate(telnet, TELNET_WILL, TELNET_TELOPT_ECHO);
|
||||
break;
|
||||
/* data must be sent */
|
||||
case TELNET_EV_SEND: _send(user->sock, ev->data.buffer, ev->data.size); break;
|
||||
/* enable compress2 if accepted by client */
|
||||
case TELNET_EV_DO:
|
||||
if (ev->neg.telopt == TELNET_TELOPT_COMPRESS2)
|
||||
telnet_begin_compress2(telnet);
|
||||
break;
|
||||
/* error */
|
||||
case TELNET_EV_ERROR:
|
||||
close(user->sock);
|
||||
user->sock = -1;
|
||||
if (user->name != 0) {
|
||||
_message(user->name, "** HAS HAD AN ERROR **");
|
||||
free(user->name);
|
||||
user->name = 0;
|
||||
}
|
||||
telnet_free(user->telnet);
|
||||
break;
|
||||
default:
|
||||
/* ignore */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void telnet_task(void* arg)
|
||||
{
|
||||
static char buffer[512];
|
||||
static struct sockaddr_in addr;
|
||||
static telnet_server_config_t config;
|
||||
|
||||
// save the configuration
|
||||
memcpy(&config, arg, sizeof(telnet_server_config_t));
|
||||
|
||||
int listen_sock;
|
||||
int client_sock;
|
||||
int rs;
|
||||
int i;
|
||||
socklen_t addrlen;
|
||||
struct pollfd pfd[config.max_connections + 1];
|
||||
|
||||
/* initialize data structures */
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
memset(users, 0, sizeof(users));
|
||||
for (i = 0; i != config.max_connections; ++i) {
|
||||
users[i].sock = -1;
|
||||
}
|
||||
|
||||
/* create listening socket */
|
||||
if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
ESP_LOGE(TAG, "socket() failed: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/* reuse address option */
|
||||
rs = 1;
|
||||
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&rs, sizeof(rs));
|
||||
|
||||
/* bind to listening addr/port */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = htons(config.port);
|
||||
if (bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||
ESP_LOGE(TAG, "bind() failed: %s", strerror(errno));
|
||||
close(listen_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* listen for clients */
|
||||
if (listen(listen_sock, 3) == -1) {
|
||||
ESP_LOGE(TAG, "listen() failed: %s", strerror(errno));
|
||||
close(listen_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Telnet server listening on port %d", config.port);
|
||||
|
||||
/* initialize listening descriptors */
|
||||
pfd[config.max_connections].fd = listen_sock;
|
||||
pfd[config.max_connections].events = POLLIN;
|
||||
|
||||
/* loop for ever */
|
||||
while (true) {
|
||||
/* prepare for poll */
|
||||
for (i = 0; i != config.max_connections; ++i) {
|
||||
if (users[i].sock != -1) {
|
||||
pfd[i].fd = users[i].sock;
|
||||
pfd[i].events = POLLIN;
|
||||
}
|
||||
else {
|
||||
pfd[i].fd = -1;
|
||||
pfd[i].events = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* poll */
|
||||
rs = poll(pfd, config.max_connections + 1, AWAIT_TIMEOUT);
|
||||
if (rs == -1 && errno != EINTR) {
|
||||
ESP_LOGE(TAG, "poll() failed: %s", strerror(errno));
|
||||
close(listen_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* new connection */
|
||||
if (pfd[config.max_connections].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI | POLLERR | POLLHUP)) {
|
||||
/* acept the sock */
|
||||
ESP_LOGW(TAG, "New connection");
|
||||
addrlen = sizeof(addr);
|
||||
if ((client_sock = accept(listen_sock, (struct sockaddr*)&addr, &addrlen)) == -1) {
|
||||
ESP_LOGE(TAG, "accept() failed: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Connection received");
|
||||
|
||||
/* find a free user */
|
||||
for (i = 0; i != config.max_connections; ++i) {
|
||||
if (users[i].sock == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == config.max_connections) {
|
||||
ESP_LOGV(TAG, " rejected (too many users)");
|
||||
_send(client_sock, "Too many users.\n", 16);
|
||||
close(client_sock);
|
||||
}
|
||||
|
||||
/* init, welcome */
|
||||
users[i].sock = client_sock;
|
||||
users[i].telnet = telnet_init(config.telnet_opts, _event_handler, 0, &users[i]);
|
||||
telnet_negotiate(users[i].telnet, TELNET_WILL, TELNET_TELOPT_COMPRESS2);
|
||||
telnet_printf(users[i].telnet, "Enter name: ");
|
||||
|
||||
// telnet_negotiate(users[i].telnet, TELNET_WILL, TELNET_TELOPT_ECHO);
|
||||
}
|
||||
|
||||
/* read from client */
|
||||
for (i = 0; i != config.max_connections; ++i) {
|
||||
/* skip users that aren't actually connected */
|
||||
if (users[i].sock == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pfd[i].revents & (POLLIN | POLLERR | POLLHUP)) {
|
||||
if ((rs = recv(users[i].sock, buffer, sizeof(buffer), 0)) > 0) {
|
||||
telnet_recv(users[i].telnet, buffer, rs);
|
||||
}
|
||||
else if (rs == 0) {
|
||||
ESP_LOGV(TAG, "Connection closed.");
|
||||
close(users[i].sock);
|
||||
users[i].sock = -1;
|
||||
if (users[i].name != 0) {
|
||||
// _message(users[i].name, "** HAS DISCONNECTED **");
|
||||
free(users[i].name);
|
||||
users[i].name = 0;
|
||||
}
|
||||
telnet_free(users[i].telnet);
|
||||
}
|
||||
else if (errno != EINTR) {
|
||||
ESP_LOGE(TAG, "recv(client) failed: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static TaskHandle_t xHandle = NULL;
|
||||
|
||||
esp_err_t telnet_server_create(telnet_server_config_t* config)
|
||||
{
|
||||
if (config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
BaseType_t xReturned = xTaskCreate(telnet_task, "telnet_task", config->stack_size, config, config->task_priority, &xHandle);
|
||||
if (xReturned == pdPASS) {
|
||||
ESP_LOGV(TAG, "Telnet task created successfully.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Failed to create telnet task.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
Reference in New Issue
Block a user