diff --git a/examples/cc3000/shell.c b/examples/cc3000/shell.c index aa45973f3..a1d8deb9b 100644 --- a/examples/cc3000/shell.c +++ b/examples/cc3000/shell.c @@ -1,7 +1,7 @@ /**************************************************************************** * apps/examples/cc3000/shell.c * - * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2013, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * This is a leverage of similar logic from uIP: @@ -217,6 +217,7 @@ int shell_main(int argc, char *argv[]) /* Configure the telnet daemon */ config.d_port = HTONS(23); + config.d_family = AF_INET; config.d_priority = CONFIG_EXAMPLES_TELNETD_DAEMONPRIO; config.d_stacksize = CONFIG_EXAMPLES_TELNETD_DAEMONSTACKSIZE; config.t_priority = CONFIG_EXAMPLES_TELNETD_CLIENTPRIO; diff --git a/examples/nsh/nsh_main.c b/examples/nsh/nsh_main.c index 2a81d5425..32ea17ae5 100644 --- a/examples/nsh/nsh_main.c +++ b/examples/nsh/nsh_main.c @@ -73,6 +73,16 @@ # undef CONFIG_NSH_TELNET #endif +/* If Telnet is used and both IPv6 and IPv4 are enabled, then we need to + * pick one. + */ + +#ifdef CONFIG_NET_IPv6 +# define ADDR_FAMILY AF_INET6 +#else +# define ADDR_FAMILY AF_INET +#endif + /**************************************************************************** * Private Types ****************************************************************************/ @@ -153,11 +163,14 @@ int nsh_main(int argc, char *argv[]) nsh_initialize(); /* If the Telnet console is selected as a front-end, then start the - * Telnet daemon. + * Telnet daemon UNLESS network initialization is deferred via + * CONFIG_NSH_NETLOCAL. In that case, the telnet daemon must be + * started manually with the telnetd command after the network has + * been initialized */ -#ifdef CONFIG_NSH_TELNET - ret = nsh_telnetstart(); +#if defined(CONFIG_NSH_TELNET) && !defined(CONFIG_NSH_NETLOCAL) + ret = nsh_telnetstart(ADDR_FAMILY); if (ret < 0) { /* The daemon is NOT running. Report the error then fail... diff --git a/examples/nxterm/nxterm_main.c b/examples/nxterm/nxterm_main.c index cd06a82b4..6f211135f 100644 --- a/examples/nxterm/nxterm_main.c +++ b/examples/nxterm/nxterm_main.c @@ -71,6 +71,26 @@ #include "nxterm_internal.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The NSH telnet console requires networking support (and TCP/IP) */ + +#ifndef CONFIG_NET +# undef CONFIG_NSH_TELNET +#endif + +/* If Telnet is used and both IPv6 and IPv4 are enabled, then we need to + * pick one. + */ + +#ifdef CONFIG_NET_IPv6 +# define ADDR_FAMILY AF_INET6 +#else +# define ADDR_FAMILY AF_INET +#endif + /**************************************************************************** * Public Data ****************************************************************************/ @@ -234,7 +254,7 @@ int nxterm_main(int argc, char **argv) */ #ifdef CONFIG_NSH_TELNET - ret = nsh_telnetstart(); + ret = nsh_telnetstart(ADDR_FAMILY); if (ret < 0) { /* The daemon is NOT running. Report the error then fail... diff --git a/examples/telnetd/telnetd.c b/examples/telnetd/telnetd.c index 5bb5a4c56..cc03c70f4 100644 --- a/examples/telnetd/telnetd.c +++ b/examples/telnetd/telnetd.c @@ -1,7 +1,7 @@ /**************************************************************************** * examples/telnetd/telnetd.c * - * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2012, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * This is a leverage of similar logic from uIP: @@ -53,10 +53,6 @@ #include "telnetd.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - /**************************************************************************** * Private Types ****************************************************************************/ @@ -247,6 +243,7 @@ int telnetd_main(int argc, char *argv[]) /* Configure the telnet daemon */ config.d_port = HTONS(23); + config.d_family = AF_INET; config.d_priority = CONFIG_EXAMPLES_TELNETD_DAEMONPRIO; config.d_stacksize = CONFIG_EXAMPLES_TELNETD_DAEMONSTACKSIZE; config.t_priority = CONFIG_EXAMPLES_TELNETD_CLIENTPRIO; diff --git a/include/netutils/telnetc.h b/include/netutils/telnetc.h new file mode 100644 index 000000000..bf8a4e8a9 --- /dev/null +++ b/include/netutils/telnetc.h @@ -0,0 +1,850 @@ +/**************************************************************************** + * apps/netutils/telnetc/telnetc.c + * + * Leveraged from libtelnet, https://github.com/seanmiddleditch/libtelnet. + * Modified and re-released under the BSD license: + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * The original authors of libtelnet are listed below. Per their licesne, + * "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." + * + * Author: Sean Middleditch + * (Also listed in the AUTHORS file are Jack Kelly + * and Katherine Flavel ) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/* 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. + * + * Author: Sean Middleditch + */ + +#ifndef __APPS_INCLUDE_NETUTILS_TELNETC_H +#define __APPS_INCLUDE_NETUTILS_TELNETC_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/* C++ support */ + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/**************************************************************************** + * Pre-proecessor Definitions + ****************************************************************************/ + +/* 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 + +/* 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 + +/* TERMINAL-TYPE codes. */ + +#define TELNET_TTYPE_IS 0 +#define TELNET_TTYPE_SEND 1 + +/* 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 + +/* MSSP codes. */ + +#define TELNET_MSSP_VAR 1 +#define TELNET_MSSP_VAL 2 + +/* Telnet state tracker flags. */ + +#define TELNET_FLAG_PROXY (1 << 0) +#define TELNET_PFLAG_DEFLATE (1 << 7) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Error codes */ + +enum telnet_error_u +{ + 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 */ +}; + +/* Event codes */ + +enum telnet_event_type_e +{ + 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 */ +}; + +/* Environ/MSSP command information */ + +struct telnet_environ_s +{ + 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 */ +}; + +/* State tracker -- private data structure */ + +struct telnet_s; /* Forward reference */ + +/* Event information */ + +union telnet_event_u +{ + /* 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_e type; + + /* Data event: for DATA and SEND events */ + + struct + { + enum telnet_event_type_e _type; /* Alias for type */ + const char *buffer; /* Byte buffer */ + size_t size; /* Number of bytes in buffer */ + } data; + + /* WARNING and ERROR events */ + + struct + { + enum telnet_event_type_e _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 */ + enum telnet_error_u errcode; /* Error code */ + } error; + + /* Command event: for IAC */ + + struct + { + enum telnet_event_type_e _type; /* Alias for type */ + unsigned char cmd; /* Telnet command received */ + } iac; + + /* Negotiation event: WILL, WONT, DO, DONT */ + + struct + { + enum telnet_event_type_e _type; /* Alias for type */ + unsigned char telopt; /* Option being negotiated */ + } neg; + + /* Subnegotiation event */ + + struct + { + enum telnet_event_type_e _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; + + /* ZMP event */ + + struct + { + enum telnet_event_type_e _type; /* Alias for type */ + const char **argv; /* Array of argument string */ + size_t argc; /* Number of elements in argv */ + } zmp; + + /* TTYPE event */ + + struct + { + enum telnet_event_type_e _type; /* Alias for type */ + unsigned char cmd; /* TELNET_TTYPE_IS or TELNET_TTYPE_SEND */ + const char *name; /* Terminal type name (IS only) */ + } ttype; + + /* COMPRESS event */ + + struct + { + enum telnet_event_type_e _type; /* Alias for type */ + unsigned char state; /* 1 if compression is enabled, 0 if + * disabled */ + } compress; + + /* ENVIRON/NEW-ENVIRON event */ + + struct + { + enum telnet_event_type_e _type; /* Alias for type */ + const struct telnet_environ_s *values; /* Array of variable values */ + size_t size; /* Number of elements in values */ + unsigned char cmd; /* SEND, IS, or INFO */ + } envevent; + + /* MSSP event */ + + struct + { + enum telnet_event_type_e _type; /* Alias for type */ + const struct telnet_environ_s *values; /* Array of variable values */ + size_t size; /* Number of elements in values */ + } mssp; +}; + +/* Name: telnet_event_handler_t + * + * Description: + * 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. + * + * Input Parameters: + * telnet The telnet object that generated the event + * event Event structure with details about the event + * user_data User-supplied pointer + */ + +typedef void (*telnet_event_handler_t)(struct telnet_s *telnet, + union telnet_event_u *event, + void *user_data); + +/* telopt support table element; use telopt of -1 for end marker */ + +struct telnet_telopt_s +{ + 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 */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: telnet_init + * + * Description: + * 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. + * + * Input Parameters: + * telopts Table of TELNET options the application supports. + * eh Event handler function called for every event. + * flags 0 or TELNET_FLAG_PROXY. + * user_data Optional data pointer that will be passsed to eh. + * + * Returned Value: + * Telent state tracker object. + * + ****************************************************************************/ + +struct telnet_s *telnet_init(const struct telnet_telopt_s *telopts, + telnet_event_handler_t eh, unsigned char flags, + void *user_data); + +/**************************************************************************** + * Name: telnet_free + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +void telnet_free(struct telnet_s *telnet); + +/**************************************************************************** + * Name: telnet_recv + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * buffer Pointer to byte buffer. + * size Number of bytes pointed to by buffer. + * + ****************************************************************************/ + +void telnet_recv(struct telnet_s *telnet, const char *buffer, size_t size); + +/**************************************************************************** + * Name: telnet_iac + * + * Description: + * Send a telnet command. + * + * Input Parameters: + * telnet Telnet state tracker object. + * cmd Command to send. + * + ****************************************************************************/ + +void telnet_iac(struct telnet_s *telnet, unsigned char cmd); + +/**************************************************************************** + * Name: telnet_negotiate + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * cmd TELNET_WILL, TELNET_WONT, TELNET_DO, or TELNET_DONT. + * opt One of the TELNET_TELOPT_* values. + * + ****************************************************************************/ + +void telnet_negotiate(struct telnet_s *telnet, unsigned char cmd, + unsigned char opt); + +/**************************************************************************** + * Name: telnet_send + * + * Description: + * Send non-command data (escapes IAC bytes). + * + * Input Parameters: + * telnet Telnet state tracker object. + * buffer Buffer of bytes to send. + * size Number of bytes to send. + * + ****************************************************************************/ + +void telnet_send(struct telnet_s *telnet, const char *buffer, size_t size); + +/**************************************************************************** + * Name: telnet_begin_sb + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * telopt One of the TELNET_TELOPT_* values. + * + ****************************************************************************/ + +void telnet_begin_sb(struct telnet_s *telnet, unsigned char telopt); + +/**************************************************************************** + * Name: telnet_finish_sb + * + * Description: + * Finish a sub-negotiation command. + * + * This must be called after a call to telnet_begin_sb() to finish a + * sub-negotiation command. + * + * Input Parameters: + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +#define telnet_finish_sb(telnet) telnet_iac((telnet), TELNET_SE) + +/**************************************************************************** + * Name: telnet_subnegotiation + * + * Description: + * Send a complete subnegotiation buffer. + * + * Equivalent to: + * telnet_begin_sb(telnet, telopt); + * telnet_send(telnet, buffer, size); + * telnet_finish_sb(telnet); + * + * Input Parameters: + * telnet Telnet state tracker format. + * telopt One of the TELNET_TELOPT_* values. + * buffer Byte buffer for sub-negotiation data. + * size Number of bytes to use for sub-negotiation data. + * + ****************************************************************************/ + +void telnet_subnegotiation(struct telnet_s *telnet, unsigned char telopt, + const char *buffer, size_t size); + +/**************************************************************************** + * Name: telnet_begin_compress2 + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +void telnet_begin_compress2(struct telnet_s *telnet); + +/**************************************************************************** + * Name: telnet_printf + * + * Description: + * 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(). + * + * Input Parameters: + * telnet Telnet state tracker object. + * fmt Format string. + * + * Returned Value: + * Number of bytes sent. + * + ****************************************************************************/ + +int telnet_printf(struct telnet_s *telnet, const char *fmt, ...); + +/**************************************************************************** + * Name: telnet_vprintf + * + * Description: + * Send formatted data with \r and \n translation in addition to IAC IAC + * + * See telnet_printf(). + * + ****************************************************************************/ + +int telnet_vprintf(struct telnet_s *telnet, const char *fmt, va_list va); + +/**************************************************************************** + * Name: telnet_raw_printf + * + * Description: + * 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(). + * + * Input Parameters: + * telnet Telnet state tracker object. + * fmt Format string. + * + * Returned Value: + * Number of bytes sent. + * + ****************************************************************************/ + +int telnet_raw_printf(struct telnet_s *telnet, const char *fmt, ...); + +/**************************************************************************** + * Name: telnet_raw_vprintf + * + * Description: + * Send formatted data (no newline escaping). + * + * See telnet_raw_printf(). + * + ****************************************************************************/ + +int telnet_raw_vprintf(struct telnet_s *telnet, const char *fmt, va_list va); + +/**************************************************************************** + * Name: telnet_begin_newenviron + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * type One of TELNET_ENVIRON_SEND, TELNET_ENVIRON_IS, or + * TELNET_ENVIRON_INFO. + * + ****************************************************************************/ + +void telnet_begin_newenviron(struct telnet_s *telnet, unsigned char type); + +/**************************************************************************** + * Name: telnet_newenviron_value + * + * Description: + * Send a NEW-ENVIRON variable name or value. + * + * This can only be called between calls to telnet_begin_newenviron() and + * telnet_finish_newenviron(). + * + * Input Parameters: + * telnet Telnet state tracker object. + * type One of TELNET_ENVIRON_VAR, TELNET_ENVIRON_USERVAR, or + * TELNET_ENVIRON_VALUE. + * string Variable name or value. + * + ****************************************************************************/ + +void telnet_newenviron_value(struct telnet_s *telnet, unsigned char type, + const char *string); + +/**************************************************************************** + * Name: telnet_finish_newenviron + * + * Description: + * Finish a NEW-ENVIRON command. + * + * This must be called after a call to telnet_begin_newenviron() to finish a + * NEW-ENVIRON variable list. + * + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +#define telnet_finish_newenviron(telnet) telnet_finish_sb((telnet)) + +/**************************************************************************** + * Name: telnet_ttype_send + * + * Description: + * Send the TERMINAL-TYPE SEND command. + * + * Sends the sequence IAC TERMINAL-TYPE SEND. + * + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +void telnet_ttype_send(struct telnet_s * telnet); + +/**************************************************************************** + * Name: telnet_ttype_is + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * ttype Name of the terminal-type being sent. + * + ****************************************************************************/ + +void telnet_ttype_is(struct telnet_s * telnet, const char *ttype); + +/**************************************************************************** + * Name: telnet_send_zmp + * + * Description: + * Send a ZMP command. + * + * Input Parameters: + * telnet Telnet state tracker object. + * argc Number of ZMP commands being sent. + * argv Array of argument strings. + * + ****************************************************************************/ + +void telnet_send_zmp(struct telnet_s * telnet, size_t argc, + const char **argv); + +/**************************************************************************** + * Name: telnet_send_zmpv + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +void telnet_send_zmpv(struct telnet_s * telnet, ...); + +/**************************************************************************** + * Name: telnet_send_vzmpv + * + * Description: + * Send a ZMP command. + * + * See telnet_send_zmpv(). + * + ****************************************************************************/ + +void telnet_send_vzmpv(struct telnet_s * telnet, va_list va); + +/**************************************************************************** + * Name: telnet_begin_zmp + * + * Description: + * Begin sending a ZMP command + * + * Input Parameters: + * telnet Telnet state tracker object. + * cmd The first argument (command name) for the ZMP command. + * + ****************************************************************************/ + +void telnet_begin_zmp(struct telnet_s * telnet, const char *cmd); + +/**************************************************************************** + * Name: telnet_zmp_arg + * + * Description: + * Send a ZMP command argument. + * + * Input Parameters: + * telnet Telnet state tracker object. + * arg Telnet argument string. + * + ****************************************************************************/ + +void telnet_zmp_arg(struct telnet_s * telnet, const char *arg); + +/**************************************************************************** + * Name: telnet_finish_zmp + * + * Description: + * Finish a ZMP command. + * + * This must be called after a call to telnet_begin_zmp() to finish a + * ZMP argument list. + * + * Input Parameters: + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +#define telnet_finish_zmp(telnet) telnet_finish_sb((telnet)) + +#if defined(__cplusplus) +} +#endif + +#endif /* __APPS_INCLUDE_NETUTILS_TELNETC_H */ diff --git a/include/netutils/telnetd.h b/include/netutils/telnetd.h index 1a4000d24..1e385d670 100644 --- a/include/netutils/telnetd.h +++ b/include/netutils/telnetd.h @@ -1,7 +1,7 @@ /**************************************************************************** * apps/include/netutils/telnetd.h * - * Copyright (C) 2007, 2011-2012, 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2011-2012, 2015, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -61,18 +61,22 @@ struct telnetd_config_s { /* These fields describe the telnet daemon */ - int d_port; /* The port to listen on (in network byte order) */ - int d_priority; /* The execution priority of the Telnet daemon task */ - int d_stacksize; /* The stack size needed by the Telnet daemon task */ + uint8_t d_priority; /* The execution priority of the Telnet daemon task */ + size_t d_stacksize; /* The stack size needed by the Telnet daemon task */ + + /* These fields describe the network connection */ + + uint16_t d_port; /* The port to listen on (in network byte order) */ + sa_family_t d_family; /* Address family */ /* These fields describe the priority of each thread created by the Telnet * daemon. */ - int t_priority; /* The execution priority of the spawned task, */ - int t_stacksize; /* The stack size needed by the spawned task */ - main_t t_entry; /* The entrypoint of the task to spawn when a new - * connection is accepted. */ + uint8_t t_priority; /* The execution priority of the spawned task, */ + size_t t_stacksize; /* The stack size needed by the spawned task */ + main_t t_entry; /* The entrypoint of the task to spawn when a new + * connection is accepted. */ }; /**************************************************************************** diff --git a/include/nshlib/nshlib.h b/include/nshlib/nshlib.h index 109664956..6a8b07612 100644 --- a/include/nshlib/nshlib.h +++ b/include/nshlib/nshlib.h @@ -1,7 +1,7 @@ /**************************************************************************** * apps/include/nshlib/nshlib.h * - * Copyright (C) 2011, 2013, 2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2011, 2013, 2016-2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -147,8 +147,12 @@ int nsh_consolemain(int argc, char *argv[]); * the daemon has been started. * * Input Parameters: - * None. All of the properties of the Telnet daemon are controlled by - * NuttX configuration setting. + * family - Provides the IP family to use by the server. May be either + * AF_INET or AF_INET6. This is needed because both both may be + * enabled in the configuration. + * + * All of the other properties of the Telnet daemon are controlled by + * NuttX configuration settings. * * Returned Values: * The task ID of the Telnet daemon was successfully started. A negated @@ -156,7 +160,7 @@ int nsh_consolemain(int argc, char *argv[]); * ****************************************************************************/ -int nsh_telnetstart(void); +int nsh_telnetstart(sa_family_t family); /**************************************************************************** * Name: platform_motd diff --git a/include/system/readline.h b/include/system/readline.h index 8020cf2c9..a2ef10bbf 100644 --- a/include/system/readline.h +++ b/include/system/readline.h @@ -216,11 +216,7 @@ ssize_t readline(FAR char *buf, int buflen, FILE *instream, FILE *outstream); * ****************************************************************************/ -#if CONFIG_NFILE_STREAMS > 0 -# define std_readline(b,s) readline(b,s,stdin,stdout) -#else -ssize_t std_readline(FAR char *buf, int buflen); -#endif +#define std_readline(b,s) readline(b,s,stdin,stdout) #undef EXTERN #ifdef __cplusplus diff --git a/include/wireless/ieee802154.h b/include/wireless/ieee802154.h index 276c90ad6..da84dcfcd 100644 --- a/include/wireless/ieee802154.h +++ b/include/wireless/ieee802154.h @@ -98,6 +98,10 @@ int ieee802154_getsaddr(int fd, FAR uint8_t *saddr); int ieee802154_seteaddr(int fd, FAR const uint8_t *eaddr); int ieee802154_geteaddr(int fd, FAR uint8_t *eaddr); +int ieee802154_getcoordsaddr(int fd, FAR uint8_t *saddr); + +int ieee802154_getcoordeaddr(int fd, FAR uint8_t *eaddr); + int ieee802154_setpromisc(int fd, bool promisc); int ieee802154_getpromisc(int fd, FAR bool *promisc); diff --git a/netutils/README.txt b/netutils/README.txt index c2f3c4391..d0822e3bf 100644 --- a/netutils/README.txt +++ b/netutils/README.txt @@ -55,6 +55,12 @@ highly influenced by uIP) include: and adapted for NuttX by Darcy Gong. tftpc - TFTP client. See apps/include/netutils/tftp.h for interface information. + telnetc - This is a port of libtelnet to NuttX. This is a public domain + Telnet client library available from + https://github.com/seanmiddleditch/libtelnet modified for use + with NuttX. Original Authors: Sean Middleditch , + Jack Kelly , and Katherine Flavel + telnetd - TELNET server. This is the Telnet logic adapted from uIP and generalized for use as the front end to any shell. The telnet daemon creates sessions that are diff --git a/netutils/telnetc/.gitignore b/netutils/telnetc/.gitignore new file mode 100644 index 000000000..f21c854d1 --- /dev/null +++ b/netutils/telnetc/.gitignore @@ -0,0 +1,6 @@ +/.built +/.depend +/Make.dep +/*.src +/*.obj +/*.lst diff --git a/netutils/telnetc/Kconfig b/netutils/telnetc/Kconfig new file mode 100644 index 000000000..ab3d42a7f --- /dev/null +++ b/netutils/telnetc/Kconfig @@ -0,0 +1,20 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config NETUTILS_TELNETC + bool "Telnet client library" + default n + depends on NET && NET_TCP + ---help--- + Enable support for the libtelnet. This is a public domain + Telnet client library available from https://github.com/seanmiddleditch/libtelnet + modified for use with NuttX. Original Authors: + + Sean Middleditch + Jack Kelly + Katherine Flavel + +if NETUTILS_TELNETC +endif diff --git a/netutils/telnetc/Make.defs b/netutils/telnetc/Make.defs new file mode 100644 index 000000000..1d5a07ee0 --- /dev/null +++ b/netutils/telnetc/Make.defs @@ -0,0 +1,39 @@ +# apps/netutils/telnetc/Make.defs +# Adds selected applications to apps/ build +# +# Copyright (C) 2017 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_NETUTILS_TELNETC),y) +CONFIGURED_APPS += netutils/telnetc +endif + diff --git a/netutils/telnetc/Makefile b/netutils/telnetc/Makefile new file mode 100644 index 000000000..138fedc08 --- /dev/null +++ b/netutils/telnetc/Makefile @@ -0,0 +1,104 @@ +############################################################################ +# apps/netutils/telnetc/Makefile +# +# Copyright (C) 2017 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +# Telnet daemon + +ASRCS = +CSRCS = + +ifeq ($(CONFIG_NET_TCP),y) +CSRCS += telnetc.c +endif + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(CONFIG_WINDOWS_NATIVE),y) + BIN = ..\..\libapps$(LIBEXT) +else +ifeq ($(WINTOOL),y) + BIN = ..\\..\\libapps$(LIBEXT) +else + BIN = ../../libapps$(LIBEXT) +endif +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: context depend clean distclean preconfig + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + $(call ARCHIVE, $(BIN), $(OBJS)) + $(Q) touch .built + +install: + +context: + +.depend: Makefile $(SRCS) + $(Q) $(MKDEP) $(ROOTDEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep + $(Q) touch $@ + +depend: .depend + +clean: + $(call DELFILE, .built) + $(call CLEAN) + +distclean: clean + $(call DELFILE, Make.dep) + $(call DELFILE, .depend) + +preconfig: + +-include Make.dep diff --git a/netutils/telnetc/telnetc.c b/netutils/telnetc/telnetc.c new file mode 100644 index 000000000..d27e93ced --- /dev/null +++ b/netutils/telnetc/telnetc.c @@ -0,0 +1,2410 @@ +/**************************************************************************** + * apps/netutils/telnetc/telnetc.c + * + * Leveraged from libtelnet, https://github.com/seanmiddleditch/libtelnet. + * Modified and re-released under the BSD license: + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * The original authors of libtelnet are listed below. Per their licesne, + * "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." + * + * Author: Sean Middleditch + * (Also listed in the AUTHORS file are Jack Kelly + * and Katherine Flavel ) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_ZLIB) +# include +#endif + +#include "netutils/telnetc.h" + +/**************************************************************************** + * Pre-proecessor Definitions + ****************************************************************************/ + +/* Helper for Q-method option tracking */ + +#define Q_US(q) ( (q).state & 0x0F) +#define Q_HIM(q) (((q).state & 0xF0) >> 4) +#define Q_MAKE(us,him) ((us) | ((him) << 4)) + +/* Helper for the negotiation routines */ + +#define NEGOTIATE_EVENT(telnet,cmd,opt) \ + ev.type = (cmd); \ + ev.neg.telopt = (opt); \ + (telnet)->eh((telnet), &ev, (telnet)->ud); + +/* RFC1143 state names */ + +#define Q_NO 0 +#define Q_YES 1 +#define Q_WANTNO 2 +#define Q_WANTYES 3 +#define Q_WANTNO_OP 4 +#define Q_WANTYES_OP 5 + +/* To send bags of unsigned chars */ + +#define _sendu(t, d, s) _send((t), (const char*)(d), (s)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Telnet state codes */ + +enum telnet_state_e +{ + TELNET_STATE_DATA = 0, + TELNET_STATE_IAC, + TELNET_STATE_WILL, + TELNET_STATE_WONT, + TELNET_STATE_DO, + TELNET_STATE_DONT, + TELNET_STATE_SB, + TELNET_STATE_SB_DATA, + TELNET_STATE_SB_DATA_IAC +}; + +/* Telnet state tracker */ + +struct telnet_s +{ + /* User data */ + + void *ud; + + /* Telopt support table */ + + const struct telnet_telopt_s *telopts; + + /* Event handler */ + + telnet_event_handler_t eh; + +#if defined(HAVE_ZLIB) + /* zlib (mccp2) compression */ + + z_stream *z; +#endif + + /* RFC1143 option negotiation states */ + + struct telnet_rfc1143_s *q; + + /* Sub-request buffer */ + + char *buffer; + + /* Current size of the buffer */ + + size_t buffer_size; + + /* Current buffer write position (also length of buffer data) */ + + size_t buffer_pos; + + /* Current state */ + + enum telnet_state_e state; + + /* Option flags */ + + unsigned char flags; + + /* Current subnegotiation telopt */ + + unsigned char sb_telopt; + + /* Length of RFC1143 queue */ + + unsigned char q_size; +}; + +/* RFC1143 option negotiation state */ + +struct telnet_rfc1143_s +{ + unsigned char telopt; + unsigned char state; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Buffer sizes */ + +static const size_t _buffer_sizes[] = +{ + 0, 512, 2048, 8192, 16384, +}; + +static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) / + sizeof(_buffer_sizes[0]); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Error generation function */ + +static enum telnet_error_u _error(struct telnet_s *telnet, unsigned line, + const char *func, enum telnet_error_u err, + int fatal, const char *fmt, ...) +{ + union telnet_event_u ev; + char buffer[512]; + va_list va; + + /* Format informational text */ + + va_start(va, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, va); + va_end(va); + + /* Send error event to the user */ + + ev.type = fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING; + ev.error.file = __FILE__; + ev.error.func = func; + ev.error.line = line; + ev.error.msg = buffer; + telnet->eh(telnet, &ev, telnet->ud); + + return err; +} + +/* Initialize the zlib box for a telnet box; if deflate is non-zero, it + * initializes zlib for delating (compression), otherwise for inflating + * (decompression). returns TELNET_EOK on success, something else on + * failure. + */ + +#if defined(HAVE_ZLIB) +enum telnet_error_u _init_zlib(struct telnet_s *telnet, int deflate, + int err_fatal) +{ + z_stream *z; + int rs; + + /* If compression is already enabled, fail loudly */ + + if (telnet->z != 0) + { + return _error(telnet, __LINE__, __func__, TELNET_EBADVAL, + err_fatal, "cannot initialize compression twice"); + } + + /* Allocate zstream box */ + + if ((z = (z_stream *) calloc(1, sizeof(z_stream))) == 0) + { + return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal, + "malloc() failed: %d", errno); + } + + /* Initialize */ + + if (deflate) + { + if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) + { + free(z); + return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, + err_fatal, "deflateInit() failed: %s", zError(rs)); + } + + telnet->flags |= TELNET_PFLAG_DEFLATE; + } + else + { + if ((rs = inflateInit(z)) != Z_OK) + { + free(z); + return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, + err_fatal, "inflateInit() failed: %s", zError(rs)); + } + + telnet->flags &= ~TELNET_PFLAG_DEFLATE; + } + + telnet->z = z; + return TELNET_EOK; +} +#endif /* HAVE_ZLIB */ + +/* Push bytes out, compressing them first if need be */ + +static void _send(struct telnet_s *telnet, const char *buffer, size_t size) +{ + union telnet_event_u ev; + +#if defined(HAVE_ZLIB) + /* If we have a deflate (compression) zlib box, use it */ + + if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) + { + char deflate_buffer[1024]; + int rs; + + /* Initialize z state */ + + telnet->z->next_in = (unsigned char *)buffer; + telnet->z->avail_in = (unsigned int)size; + telnet->z->next_out = (unsigned char *)deflate_buffer; + telnet->z->avail_out = sizeof(deflate_buffer); + + /* Deflate until buffer exhausted and all output is produced */ + + while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) + { + /* Compress */ + + if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) + { + _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1, + "deflate() failed: %s", zError(rs)); + deflateEnd(telnet->z); + free(telnet->z); + telnet->z = 0; + break; + } + + /* Send event */ + + ev.type = TELNET_EV_SEND; + ev.data.buffer = deflate_buffer; + ev.data.size = sizeof(deflate_buffer) - telnet->z->avail_out; + telnet->eh(telnet, &ev, telnet->ud); + + /* Prepare output buffer for next run */ + + telnet->z->next_out = (unsigned char *)deflate_buffer; + telnet->z->avail_out = sizeof(deflate_buffer); + } + + /* Do not continue with remaining code */ + + return; + } +#endif /* HAVE_ZLIB */ + + ev.type = TELNET_EV_SEND; + ev.data.buffer = buffer; + ev.data.size = size; + telnet->eh(telnet, &ev, telnet->ud); +} + +/* Check if we support a particular telopt; if us is non-zero, we + * check if we (local) supports it, otherwise we check if he (remote) + * supports it. return non-zero if supported, zero if not supported. + */ + +static inline int _check_telopt(struct telnet_s *telnet, + unsigned char telopt, int us) +{ + int i; + + /* If we have no telopts table, we obviously don't support it */ + + if (telnet->telopts == 0) + { + return 0; + } + + /* Loop unti found or end marker (us and him both 0) */ + + for (i = 0; telnet->telopts[i].telopt != -1; ++i) + { + if (telnet->telopts[i].telopt == telopt) + { + if (us && telnet->telopts[i].us == TELNET_WILL) + { + return 1; + } + + else if (!us && telnet->telopts[i].him == TELNET_DO) + { + return 1; + } + else + { + return 0; + } + } + } + + /* Not found, so not supported */ + + return 0; +} + +/* Retrieve RFC1143 option state */ + +static inline struct telnet_rfc1143_s _get_rfc1143(struct telnet_s *telnet, + unsigned char telopt) +{ + struct telnet_rfc1143_s empty; + int i; + + /* Search for entry */ + + for (i = 0; i != telnet->q_size; ++i) + { + if (telnet->q[i].telopt == telopt) + { + return telnet->q[i]; + } + } + + /* Not found, return empty value */ + + empty.telopt = telopt; + empty.state = 0; + return empty; +} + +/* Save RFC1143 option state */ + +static inline void _set_rfc1143(struct telnet_s *telnet, unsigned char telopt, + char us, char him) +{ + struct telnet_rfc1143_s *qtmp; + int i; + + /* Search for entry */ + + for (i = 0; i != telnet->q_size; ++i) + { + if (telnet->q[i].telopt == telopt) + { + telnet->q[i].state = Q_MAKE(us, him); + return; + } + } + + /* we're going to need to track state for it, so grow the queue by 4 (four) + * elements and put the telopt into it; bail on allocation error. we go by + * four because it seems like a reasonable guess as to the number of enabled + * options for most simple code, and it allows for an acceptable number of + * reallocations for complex code. + */ + + qtmp = (struct telnet_rfc1143_s *) + realloc(telnet->q, + sizeof(struct telnet_rfc1143_s) * (telnet->q_size + 4)); + if (qtmp == 0) + { + _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, + "realloc() failed: %d", errno); + return; + } + + memset(&qtmp[telnet->q_size], 0, sizeof(struct telnet_rfc1143_s) * 4); + telnet->q = qtmp; + telnet->q[telnet->q_size].telopt = telopt; + telnet->q[telnet->q_size].state = Q_MAKE(us, him); + telnet->q_size += 4; +} + +/* Send negotiation bytes */ + +static inline void _send_negotiate(struct telnet_s *telnet, unsigned char cmd, + unsigned char telopt) +{ + unsigned char bytes[3]; + bytes[0] = TELNET_IAC; + bytes[1] = cmd; + bytes[2] = telopt; + _sendu(telnet, bytes, 3); +} + +/* Negotiation handling magic for RFC1143 */ + +static void _negotiate(struct telnet_s *telnet, unsigned char telopt) +{ + union telnet_event_u ev; + struct telnet_rfc1143_s q; + + /* In PROXY mode, just pass it thru and do nothing */ + + if (telnet->flags & TELNET_FLAG_PROXY) + { + switch ((int)telnet->state) + { + case TELNET_STATE_WILL: + NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); + break; + + case TELNET_STATE_WONT: + NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); + break; + + case TELNET_STATE_DO: + NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); + break; + + case TELNET_STATE_DONT: + NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt); + break; + } + + return; + } + + /* Lookup the current state of the option */ + + q = _get_rfc1143(telnet, telopt); + + /* Start processing... */ + + switch ((int)telnet->state) + { + /* Request to enable option on remote end or confirm DO */ + + case TELNET_STATE_WILL: + switch (Q_HIM(q)) + { + case Q_NO: + if (_check_telopt(telnet, telopt, 0)) + { + _set_rfc1143(telnet, telopt, Q_US(q), Q_YES); + _send_negotiate(telnet, TELNET_DO, telopt); + NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); + } + else + _send_negotiate(telnet, TELNET_DONT, telopt); + break; + + case Q_WANTNO: + _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); + NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "DONT answered by WILL"); + break; + + case Q_WANTNO_OP: + _set_rfc1143(telnet, telopt, Q_US(q), Q_YES); + NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "DONT answered by WILL"); + break; + + case Q_WANTYES: + _set_rfc1143(telnet, telopt, Q_US(q), Q_YES); + NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); + break; + + case Q_WANTYES_OP: + _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO); + _send_negotiate(telnet, TELNET_DONT, telopt); + NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); + break; + } + break; + + /* Request to disable option on remote end, confirm DONT, reject DO */ + + case TELNET_STATE_WONT: + switch (Q_HIM(q)) + { + case Q_YES: + _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); + _send_negotiate(telnet, TELNET_DONT, telopt); + NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); + break; + + case Q_WANTNO: + _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); + NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); + break; + + case Q_WANTNO_OP: + _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES); + NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); + break; + + case Q_WANTYES: + case Q_WANTYES_OP: + _set_rfc1143(telnet, telopt, Q_US(q), Q_NO); + break; + } + break; + + /* Request to enable option on local end or confirm WILL */ + + case TELNET_STATE_DO: + switch (Q_US(q)) + { + case Q_NO: + if (_check_telopt(telnet, telopt, 1)) + { + _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q)); + _send_negotiate(telnet, TELNET_WILL, telopt); + NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); + } + else + _send_negotiate(telnet, TELNET_WONT, telopt); + break; + + case Q_WANTNO: + _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); + NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt); + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "WONT answered by DO"); + break; + + case Q_WANTNO_OP: + _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q)); + NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "WONT answered by DO"); + break; + + case Q_WANTYES: + _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q)); + NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); + break; + + case Q_WANTYES_OP: + _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q)); + _send_negotiate(telnet, TELNET_WONT, telopt); + NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt); + break; + } + break; + + /* Request to disable option on local end, confirm WONT, reject WILL */ + + case TELNET_STATE_DONT: + switch (Q_US(q)) + { + case Q_YES: + _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); + _send_negotiate(telnet, TELNET_WONT, telopt); + NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt); + break; + + case Q_WANTNO: + _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); + NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt); + break; + + case Q_WANTNO_OP: + _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q)); + _send_negotiate(telnet, TELNET_WILL, telopt); + NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt); + break; + + case Q_WANTYES: + case Q_WANTYES_OP: + _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q)); + break; + } + break; + } +} + +/* Process an ENVIRON/NEW-ENVIRON subnegotiation buffer + * + * the algorithm and approach used here is kind of a hack, + * but it reduces the number of memory allocations we have + * to make. + * + * we copy the bytes back into the buffer, starting at the very + * beginning, which makes it easy to handle the ENVIRON ESC + * escape mechanism as well as ensure the variable name and + * value strings are NUL-terminated, all while fitting inside + * of the original buffer. + */ + +static int _environ_telnet(struct telnet_s *telnet, unsigned char type, + char *buffer, size_t size) +{ + union telnet_event_u ev; + struct telnet_environ_s *values = 0; + char *c; + char *last; + char *out; + size_t index; + size_t count; + + /* If we have no data, just pass it through */ + + if (size == 0) + { + return 0; + } + + /* First byte must be a valid command */ + + if ((unsigned)buffer[0] != TELNET_ENVIRON_SEND && + (unsigned)buffer[0] != TELNET_ENVIRON_IS && + (unsigned)buffer[0] != TELNET_ENVIRON_INFO) + { + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "telopt %d subneg has invalid command", type); + return 0; + } + + /* Store ENVIRON command */ + + ev.envevent.cmd = buffer[0]; + + /* If we have no arguments, send an event with no data end return */ + + if (size == 1) + { + /* No list of variables given */ + + ev.envevent.values = 0; + ev.envevent.size = 0; + + /* Invoke event with our arguments */ + + ev.type = TELNET_EV_ENVIRON; + telnet->eh(telnet, &ev, telnet->ud); + return 1; + } + + /* Every second byte must be VAR or USERVAR, if present */ + + if ((unsigned)buffer[1] != TELNET_ENVIRON_VAR && + (unsigned)buffer[1] != TELNET_ENVIRON_USERVAR) + { + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "telopt %d subneg missing variable type", type); + return 0; + } + + /* Ensure last byte is not an escape byte (makes parsing later easier) */ + + if ((unsigned)buffer[size - 1] == TELNET_ENVIRON_ESC) + { + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "telopt %d subneg ends with ESC", type); + return 0; + } + + /* Count arguments; each valid entry starts with VAR or USERVAR */ + + count = 0; + for (c = buffer + 1; c < buffer + size; ++c) + { + if (*c == TELNET_ENVIRON_VAR || *c == TELNET_ENVIRON_USERVAR) + { + ++count; + } + else if (*c == TELNET_ENVIRON_ESC) + { + /* Skip the next byte */ + ++c; + } + } + + /* Allocate argument array, bail on error */ + + values = (struct telnet_environ_s *) + calloc(count, sizeof(struct telnet_environ_s)); + if (values == 0) + { + _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, + "calloc() failed: %d", errno); + return 0; + } + + /* Parse argument array strings */ + + out = buffer; + c = buffer + 1; + for (index = 0; index != count; ++index) + { + /* Remember the variable type (will be VAR or USERVAR) */ + + values[index].type = *c++; + + /* Scan until we find an end-marker, and buffer up unescaped bytes into + * our buffer. + */ + + last = out; + while (c < buffer + size) + { + /* Stop at the next variable or at the value */ + + if ((unsigned)*c == TELNET_ENVIRON_VAR || + (unsigned)*c == TELNET_ENVIRON_VALUE || + (unsigned)*c == TELNET_ENVIRON_USERVAR) + { + break; + } + + /* Buffer next byte (taking into account ESC) */ + + if (*c == TELNET_ENVIRON_ESC) + { + ++c; + } + + *out++ = *c++; + } + + *out++ = '\0'; + + /* Store the variable name we have just received */ + + values[index].var = last; + values[index].value = ""; + + /* If we got a value, find the next end marker and store the value; + * otherwise, store empty string. + */ + + if (c < buffer + size && *c == TELNET_ENVIRON_VALUE) + { + ++c; + last = out; + while (c < buffer + size) + { + /* Stop when we find the start of the next variable */ + + if ((unsigned)*c == TELNET_ENVIRON_VAR || + (unsigned)*c == TELNET_ENVIRON_USERVAR) + { + break; + } + + /* Buffer next byte (taking into account ESC) */ + + if (*c == TELNET_ENVIRON_ESC) + { + ++c; + } + + *out++ = *c++; + } + + *out++ = '\0'; + + /* Store the variable value */ + + values[index].value = last; + } + } + + /* Pass values array and count to event */ + + ev.envevent.values = values; + ev.envevent.size = count; + + /* Invoke event with our arguments */ + + ev.type = TELNET_EV_ENVIRON; + telnet->eh(telnet, &ev, telnet->ud); + + /* Clean up */ + + free(values); + return 1; +} + +/* Process an MSSP subnegotiation buffer */ + +static int _mssp_telnet(struct telnet_s *telnet, char *buffer, size_t size) +{ + union telnet_event_u ev; + struct telnet_environ_s *values; + char *var = 0; + char *c, *last, *out; + size_t i, count; + unsigned char next_type; + + /* If we have no data, just pass it through */ + + if (size == 0) + { + return 0; + } + + /* First byte must be a VAR */ + + if ((unsigned)buffer[0] != TELNET_MSSP_VAR) + { + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "MSSP subnegotiation has invalid data"); + return 0; + } + + /* Count the arguments, any part that starts with VALUE */ + + for (count = 0, i = 0; i != size; ++i) + { + if ((unsigned)buffer[i] == TELNET_MSSP_VAL) + { + ++count; + } + } + + /* Allocate argument array, bail on error */ + + values = (struct telnet_environ_s *) + calloc(count, sizeof(struct telnet_environ_s)); + if (values == 0) + { + _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, + "calloc() failed: %d", errno); + return 0; + } + + ev.mssp.values = values; + ev.mssp.size = count; + + /* Allocate strings in argument array */ + + out = last = buffer; + next_type = buffer[0]; + for (i = 0, c = buffer + 1; c < buffer + size;) + { + /* Search for end marker */ + + while (c < buffer + size && (unsigned)*c != TELNET_MSSP_VAR && + (unsigned)*c != TELNET_MSSP_VAL) + { + *out++ = *c++; + } + + *out++ = '\0'; + + /* If it's a variable name, just store the name for now */ + + if (next_type == TELNET_MSSP_VAR) + { + var = last; + } + else if (next_type == TELNET_MSSP_VAL && var != 0) + { + values[i].var = var; + values[i].value = last; + ++i; + } + else + { + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "invalid MSSP subnegotiation data"); + free(values); + return 0; + } + + /* Remember our next type and increment c for next loop run */ + + last = out; + next_type = *c++; + } + + /* Invoke event with our arguments */ + + ev.type = TELNET_EV_MSSP; + telnet->eh(telnet, &ev, telnet->ud); + + /* Clean up */ + + free(values); + return 0; +} + +/* Parse ZMP command subnegotiation buffers */ + +static int _zmp_telnet(struct telnet_s *telnet, const char *buffer, size_t size) +{ + union telnet_event_u ev; + char **argv; + const char *c; + size_t i; + size_t argc; + + /* Make sure this is a valid ZMP buffer */ + + if (size == 0 || buffer[size - 1] != 0) + { + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "incomplete ZMP frame"); + return 0; + } + + /* Count arguments */ + + for (argc = 0, c = buffer; c != buffer + size; ++argc) + { + c += strlen(c) + 1; + } + + /* Allocate argument array, bail on error */ + + if ((argv = (char **)calloc(argc, sizeof(char *))) == 0) + { + _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, + "calloc() failed: %d", errno); + return 0; + } + + /* Populate argument array */ + + for (i = 0, c = buffer; i != argc; ++i) + { + argv[i] = (char *)c; + c += strlen(c) + 1; + } + + /* Invoke event with our arguments */ + + ev.type = TELNET_EV_ZMP; + ev.zmp.argv = (const char **)argv; + ev.zmp.argc = argc; + telnet->eh(telnet, &ev, telnet->ud); + + /* Clean up */ + + free(argv); + return 0; +} + +/* Parse TERMINAL-TYPE command subnegotiation buffers */ + +static int _ttype_telnet(struct telnet_s *telnet, const char *buffer, + size_t size) +{ + union telnet_event_u ev; + + /* Make sure request is not empty */ + + if (size == 0) + { + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "incomplete TERMINAL-TYPE request"); + return 0; + } + + /* Make sure request has valid command type */ + + if (buffer[0] != TELNET_TTYPE_IS && buffer[0] != TELNET_TTYPE_SEND) + { + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "TERMINAL-TYPE request has invalid type"); + return 0; + } + + /* Send proper event */ + + if (buffer[0] == TELNET_TTYPE_IS) + { + char *name; + + /* Allocate space for name */ + + if ((name = (char *)malloc(size)) == 0) + { + _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, + "malloc() failed: %d", errno); + return 0; + } + + memcpy(name, buffer + 1, size - 1); + name[size - 1] = '\0'; + + ev.type = TELNET_EV_TTYPE; + ev.ttype.cmd = TELNET_TTYPE_IS; + ev.ttype.name = name; + telnet->eh(telnet, &ev, telnet->ud); + + /* Clean up */ + + free(name); + } + else + { + ev.type = TELNET_EV_TTYPE; + ev.ttype.cmd = TELNET_TTYPE_SEND; + ev.ttype.name = 0; + telnet->eh(telnet, &ev, telnet->ud); + } + + return 0; +} + +/* Process a subnegotiation buffer; return non-zero if the current buffer + * must be aborted and reprocessed due to COMPRESS2 being activated + */ + +static int _subnegotiate(struct telnet_s *telnet) +{ + union telnet_event_u ev; + + /* Standard subnegotiation event */ + + ev.type = TELNET_EV_SUBNEGOTIATION; + ev.sub.telopt = telnet->sb_telopt; + ev.sub.buffer = telnet->buffer; + ev.sub.size = telnet->buffer_pos; + telnet->eh(telnet, &ev, telnet->ud); + + switch (telnet->sb_telopt) + { +#if defined(HAVE_ZLIB) + /* Received COMPRESS2 begin marker, setup our zlib box and start handling + * the compressed stream if it's not already. + */ + + case TELNET_TELOPT_COMPRESS2: + if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) + { + if (_init_zlib(telnet, 0, 1) != TELNET_EOK) + { + return 0; + } + + /* Notify app that compression was enabled */ + + ev.type = TELNET_EV_COMPRESS; + ev.compress.state = 1; + telnet->eh(telnet, &ev, telnet->ud); + return 1; + } + return 0; +#endif /* HAVE_ZLIB */ + + /* Specially handled subnegotiation telopt types */ + + case TELNET_TELOPT_ZMP: + return _zmp_telnet(telnet, telnet->buffer, telnet->buffer_pos); + + case TELNET_TELOPT_TTYPE: + return _ttype_telnet(telnet, telnet->buffer, telnet->buffer_pos); + + case TELNET_TELOPT_ENVIRON: + case TELNET_TELOPT_NEW_ENVIRON: + return _environ_telnet(telnet, telnet->sb_telopt, telnet->buffer, + telnet->buffer_pos); + + case TELNET_TELOPT_MSSP: + return _mssp_telnet(telnet, telnet->buffer, telnet->buffer_pos); + + default: + return 0; + } +} + +/**************************************************************************** + * Name: telnet_init + * + * Description: + * 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. + * + * Input Parameters: + * telopts Table of TELNET options the application supports. + * eh Event handler function called for every event. + * flags 0 or TELNET_FLAG_PROXY. + * user_data Optional data pointer that will be passsed to eh. + * + * Returned Value: + * Telent state tracker object. + * + ****************************************************************************/ + +struct telnet_s *telnet_init(const struct telnet_telopt_s *telopts, + telnet_event_handler_t eh, + unsigned char flags, + void *user_data) +{ + /* Allocate structure */ + + struct telnet_s *telnet = (struct telnet_s *) calloc(1, sizeof(struct telnet_s)); + if (telnet == 0) + { + return 0; + } + + /* Initialize data */ + + telnet->ud = user_data; + telnet->telopts = telopts; + telnet->eh = eh; + telnet->flags = flags; + + return telnet; +} + +/**************************************************************************** + * Name: telnet_free + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +void telnet_free(struct telnet_s *telnet) +{ + /* Free sub-request buffer */ + + if (telnet->buffer != 0) + { + free(telnet->buffer); + telnet->buffer = 0; + telnet->buffer_size = 0; + telnet->buffer_pos = 0; + } + +#if defined(HAVE_ZLIB) + /* Free zlib box */ + + if (telnet->z != 0) + { + if (telnet->flags & TELNET_PFLAG_DEFLATE) + { + deflateEnd(telnet->z); + } + else + { + inflateEnd(telnet->z); + } + + free(telnet->z); + telnet->z = 0; + } +#endif /* HAVE_ZLIB */ + + /* Free RFC1143 queue */ + + if (telnet->q) + { + free(telnet->q); + telnet->q = 0; + telnet->q_size = 0; + } + + /* Free the telnet structure itself */ + + free(telnet); +} + +/* Push a byte into the telnet buffer */ + +static enum telnet_error_u _buffer_byte(struct telnet_s *telnet, + unsigned char byte) +{ + char *new_buffer; + size_t i; + + /* Check if we're out of room */ + + if (telnet->buffer_pos == telnet->buffer_size) + { + /* Find the next buffer size */ + + for (i = 0; i != _buffer_sizes_count; ++i) + { + if (_buffer_sizes[i] == telnet->buffer_size) + { + break; + } + } + + /* Overflow -- can't grow any more */ + + if (i >= _buffer_sizes_count - 1) + { + _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0, + "subnegotiation buffer size limit reached"); + return TELNET_EOVERFLOW; + } + + /* (Re)allocate buffer */ + + new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]); + if (new_buffer == 0) + { + _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, + "realloc() failed"); + return TELNET_ENOMEM; + } + + telnet->buffer = new_buffer; + telnet->buffer_size = _buffer_sizes[i + 1]; + } + + /* Push the byte, all set */ + + telnet->buffer[telnet->buffer_pos++] = byte; + return TELNET_EOK; +} + +static void _process(struct telnet_s *telnet, const char *buffer, + size_t size) +{ + union telnet_event_u ev; + unsigned char byte; + size_t start; + size_t i; + + for (i = start = 0; i != size; ++i) + { + byte = buffer[i]; + switch (telnet->state) + { + /* Regular data */ + + case TELNET_STATE_DATA: + /* On an IAC byte, pass through all pending bytes and switch states */ + + if (byte == TELNET_IAC) + { + if (i != start) + { + ev.type = TELNET_EV_DATA; + ev.data.buffer = buffer + start; + ev.data.size = i - start; + telnet->eh(telnet, &ev, telnet->ud); + } + + telnet->state = TELNET_STATE_IAC; + } + break; + + /* IAC command */ + + case TELNET_STATE_IAC: + switch (byte) + { + /* Subnegotiation */ + + case TELNET_SB: + telnet->state = TELNET_STATE_SB; + break; + + /* Negotiation commands */ + + case TELNET_WILL: + telnet->state = TELNET_STATE_WILL; + break; + + case TELNET_WONT: + telnet->state = TELNET_STATE_WONT; + break; + + case TELNET_DO: + telnet->state = TELNET_STATE_DO; + break; + + case TELNET_DONT: + telnet->state = TELNET_STATE_DONT; + break; + + /* IAC escaping */ + + case TELNET_IAC: + /* Event */ + + ev.type = TELNET_EV_DATA; + ev.data.buffer = (char *)&byte; + ev.data.size = 1; + telnet->eh(telnet, &ev, telnet->ud); + + /* State update */ + + start = i + 1; + telnet->state = TELNET_STATE_DATA; + break; + + /* Some other command */ + + default: + /* Event */ + + ev.type = TELNET_EV_IAC; + ev.iac.cmd = byte; + telnet->eh(telnet, &ev, telnet->ud); + + /* State update */ + + start = i + 1; + telnet->state = TELNET_STATE_DATA; + } + break; + + /* Negotiation commands */ + + case TELNET_STATE_WILL: + case TELNET_STATE_WONT: + case TELNET_STATE_DO: + case TELNET_STATE_DONT: + _negotiate(telnet, byte); + start = i + 1; + telnet->state = TELNET_STATE_DATA; + break; + + /* Subnegotiation -- determine subnegotiation telopt */ + + case TELNET_STATE_SB: + telnet->sb_telopt = byte; + telnet->buffer_pos = 0; + telnet->state = TELNET_STATE_SB_DATA; + break; + + /* Subnegotiation -- buffer bytes until end request */ + + case TELNET_STATE_SB_DATA: + /* IAC command in subnegotiation -- either IAC SE or IAC IAC */ + + if (byte == TELNET_IAC) + { + telnet->state = TELNET_STATE_SB_DATA_IAC; + } + else if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS && + byte == TELNET_WILL) + { + /* In 1998 MCCP used TELOPT 85 and the protocol defined an + * invalid subnegotiation sequence (IAC SB 85 WILL SE) to start + * compression. Subsequently MCCP version 2 was created in 2000 + * using TELOPT 86 and a valid subnegotiation (IAC SB 86 IAC SE). + * libtelnet for now just captures and discards MCCPv1 sequences. + */ + + start = i + 2; + telnet->state = TELNET_STATE_DATA; + + /* Buffer the byte, or bail if we can't */ + } + else if (_buffer_byte(telnet, byte) != TELNET_EOK) + { + start = i + 1; + telnet->state = TELNET_STATE_DATA; + } + break; + + /* IAC escaping inside a subnegotiation */ + + case TELNET_STATE_SB_DATA_IAC: + switch (byte) + { + /* End subnegotiation */ + + case TELNET_SE: + /* Return to default state */ + + start = i + 1; + telnet->state = TELNET_STATE_DATA; + + /* Process subnegotiation */ + + if (_subnegotiate(telnet) != 0) + { + /* Any remaining bytes in the buffer are compressed. we have + * to re-invoke telnet_recv to get those bytes inflated and + * abort trying to process the remaining compressed bytes in + * the current _process buffer argument. + */ + + telnet_recv(telnet, &buffer[start], size - start); + return; + } + break; + + /* Escaped IAC byte */ + + case TELNET_IAC: + /* Push IAC into buffer */ + + if (_buffer_byte(telnet, TELNET_IAC) != TELNET_EOK) + { + start = i + 1; + telnet->state = TELNET_STATE_DATA; + } + else + { + telnet->state = TELNET_STATE_SB_DATA; + } + break; + + /* Something else -- protocol error. attempt to process content + * in subnegotiation buffer, then evaluate the given command as + * an IAC code. + */ + + default: + _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0, + "unexpected byte after IAC inside SB: %d", byte); + + /* Enter IAC state */ + + start = i + 1; + telnet->state = TELNET_STATE_IAC; + + /* Process subnegotiation; see comment in + * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv() + */ + + if (_subnegotiate(telnet) != 0) + { + telnet_recv(telnet, &buffer[start], size - start); + return; + } + else + { + /* Recursive call to get the current input byte processed as + * a regular IAC command. we could use a goto, but that + * would be gross. + */ + + _process(telnet, (char *)&byte, 1); + } + break; + } + break; + } + } + + /* Pass through any remaining bytes */ + + if (telnet->state == TELNET_STATE_DATA && i != start) + { + ev.type = TELNET_EV_DATA; + ev.data.buffer = buffer + start; + ev.data.size = i - start; + telnet->eh(telnet, &ev, telnet->ud); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: telnet_recv + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * buffer Pointer to byte buffer. + * size Number of bytes pointed to by buffer. + * + ****************************************************************************/ + +void telnet_recv(struct telnet_s *telnet, const char *buffer, size_t size) +{ +#if defined(HAVE_ZLIB) + /* If we have an inflate (decompression) zlib stream, use it */ + + if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) + { + char inflate_buffer[1024]; + int rs; + + /* Initialize zlib state */ + + telnet->z->next_in = (unsigned char *)buffer; + telnet->z->avail_in = (unsigned int)size; + telnet->z->next_out = (unsigned char *)inflate_buffer; + telnet->z->avail_out = sizeof(inflate_buffer); + + /* Inflate until buffer exhausted and all output is produced */ + + while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) + { + /* Reset output buffer */ + /* Decompress */ + + rs = inflate(telnet->z, Z_SYNC_FLUSH); + + /* Process the decompressed bytes on success */ + + if (rs == Z_OK || rs == Z_STREAM_END) + { + _process(telnet, inflate_buffer, sizeof(inflate_buffer) - + telnet->z->avail_out); + } + else + { + _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1, + "inflate() failed: %s", zError(rs)); + } + + /* Prepare output buffer for next run */ + + telnet->z->next_out = (unsigned char *)inflate_buffer; + telnet->z->avail_out = sizeof(inflate_buffer); + + /* On error (or on end of stream) disable further inflation */ + + if (rs != Z_OK) + { + union telnet_event_u ev; + + /* Disable compression */ + + inflateEnd(telnet->z); + free(telnet->z); + telnet->z = 0; + + /* Send event */ + + ev.type = TELNET_EV_COMPRESS; + ev.compress.state = 0; + telnet->eh(telnet, &ev, telnet->ud); + break; + } + } + + /* COMPRESS2 is not negotiated, just process */ + } + else +#endif /* HAVE_ZLIB */ + _process(telnet, buffer, size); +} + +/**************************************************************************** + * Name: telnet_iac + * + * Description: + * Send a telnet command. + * + * Input Parameters: + * telnet Telnet state tracker object. + * cmd Command to send. + * + ****************************************************************************/ + +void telnet_iac(struct telnet_s *telnet, unsigned char cmd) +{ + unsigned char bytes[2]; + bytes[0] = TELNET_IAC; + bytes[1] = cmd; + _sendu(telnet, bytes, 2); +} + +/**************************************************************************** + * Name: telnet_negotiate + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * cmd TELNET_WILL, TELNET_WONT, TELNET_DO, or TELNET_DONT. + * opt One of the TELNET_TELOPT_* values. + * + ****************************************************************************/ + +void telnet_negotiate(struct telnet_s *telnet, unsigned char cmd, + unsigned char telopt) +{ + struct telnet_rfc1143_s q; + + /* If we're in proxy mode, just send it now */ + + if (telnet->flags & TELNET_FLAG_PROXY) + { + unsigned char bytes[3]; + bytes[0] = TELNET_IAC; + bytes[1] = cmd; + bytes[2] = telopt; + _sendu(telnet, bytes, 3); + return; + } + + /* Get current option states */ + + q = _get_rfc1143(telnet, telopt); + + switch (cmd) + { + /* Advertise willingess to support an option */ + + case TELNET_WILL: + switch (Q_US(q)) + { + case Q_NO: + _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q)); + _send_negotiate(telnet, TELNET_WILL, telopt); + break; + + case Q_WANTNO: + _set_rfc1143(telnet, telopt, Q_WANTNO_OP, Q_HIM(q)); + break; + + case Q_WANTYES_OP: + _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q)); + break; + } + break; + + /* Force turn-off of locally enabled option */ + + case TELNET_WONT: + switch (Q_US(q)) + { + case Q_YES: + _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q)); + _send_negotiate(telnet, TELNET_WONT, telopt); + break; + + case Q_WANTYES: + _set_rfc1143(telnet, telopt, Q_WANTYES_OP, Q_HIM(q)); + break; + + case Q_WANTNO_OP: + _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q)); + break; + } + break; + + /* Ask remote end to enable an option */ + + case TELNET_DO: + switch (Q_HIM(q)) + { + case Q_NO: + _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES); + _send_negotiate(telnet, TELNET_DO, telopt); + break; + + case Q_WANTNO: + _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO_OP); + break; + + case Q_WANTYES_OP: + _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES); + break; + } + break; + + /* Demand remote end disable an option */ + + case TELNET_DONT: + switch (Q_HIM(q)) + { + case Q_YES: + _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO); + _send_negotiate(telnet, TELNET_DONT, telopt); + break; + + case Q_WANTYES: + _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES_OP); + break; + + case Q_WANTNO_OP: + _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO); + break; + } + break; + } +} + +/**************************************************************************** + * Name: telnet_send + * + * Description: + * Send non-command data (escapes IAC bytes). + * + * Input Parameters: + * telnet Telnet state tracker object. + * buffer Buffer of bytes to send. + * size Number of bytes to send. + * + ****************************************************************************/ + +void telnet_send(struct telnet_s *telnet, const char *buffer, size_t size) +{ + size_t l; + size_t i; + + for (l = i = 0; i != size; ++i) + { + /* Dump prior portion of text, send escaped bytes */ + + if (buffer[i] == (char)TELNET_IAC) + { + /* Dump prior text if any */ + + if (i != l) + { + _send(telnet, buffer + l, i - l); + } + + l = i + 1; + + /* Send escape */ + + telnet_iac(telnet, TELNET_IAC); + } + } + + /* Send whatever portion of buffer is left */ + + if (i != l) + { + _send(telnet, buffer + l, i - l); + } +} + +/**************************************************************************** + * Name: telnet_begin_sb + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * telopt One of the TELNET_TELOPT_* values. + * + ****************************************************************************/ + +void telnet_begin_sb(struct telnet_s *telnet, unsigned char telopt) +{ + unsigned char sb[3]; + sb[0] = TELNET_IAC; + sb[1] = TELNET_SB; + sb[2] = telopt; + _sendu(telnet, sb, 3); +} + +/**************************************************************************** + * Name: telnet_subnegotiation + * + * Description: + * Send a complete subnegotiation buffer. + * + * Equivalent to: + * telnet_begin_sb(telnet, telopt); + * telnet_send(telnet, buffer, size); + * telnet_finish_sb(telnet); + * + * Input Parameters: + * telnet Telnet state tracker format. + * telopt One of the TELNET_TELOPT_* values. + * buffer Byte buffer for sub-negotiation data. + * size Number of bytes to use for sub-negotiation data. + * + ****************************************************************************/ + +void telnet_subnegotiation(struct telnet_s *telnet, unsigned char telopt, + const char *buffer, size_t size) +{ + unsigned char bytes[5]; + bytes[0] = TELNET_IAC; + bytes[1] = TELNET_SB; + bytes[2] = telopt; + bytes[3] = TELNET_IAC; + bytes[4] = TELNET_SE; + + _sendu(telnet, bytes, 3); + telnet_send(telnet, buffer, size); + _sendu(telnet, bytes + 3, 2); + +#if defined(HAVE_ZLIB) + /* If we're a proxy and we just sent the COMPRESS2 marker, we must make sure + * all further data is compressed if not already. + */ + + if (telnet->flags & TELNET_FLAG_PROXY && telopt == TELNET_TELOPT_COMPRESS2) + { + union telnet_event_u ev; + + if (_init_zlib(telnet, 1, 1) != TELNET_EOK) + { + return; + } + + /* Notify app that compression was enabled */ + + ev.type = TELNET_EV_COMPRESS; + ev.compress.state = 1; + telnet->eh(telnet, &ev, telnet->ud); + } +#endif /* HAVE_ZLIB */ +} + +/**************************************************************************** + * Name: telnet_begin_compress2 + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +void telnet_begin_compress2(struct telnet_s *telnet) +{ +#if defined(HAVE_ZLIB) + static const unsigned char compress2[] = + { + TELNET_IAC, TELNET_SB, TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE + }; + union telnet_event_u ev; + + /* Attempt to create output stream first, bail if we can't */ + + if (_init_zlib(telnet, 1, 0) != TELNET_EOK) + { + return; + } + + /* Send compression marker. we send directly to the event handler instead of + * passing through _send because _send would result in the compress marker + * itself being compressed. + */ + + ev.type = TELNET_EV_SEND; + ev.data.buffer = (const char *)compress2; + ev.data.size = sizeof(compress2); + telnet->eh(telnet, &ev, telnet->ud); + + /* Notify app that compression was successfully enabled */ + + ev.type = TELNET_EV_COMPRESS; + ev.compress.state = 1; + telnet->eh(telnet, &ev, telnet->ud); +#endif /* HAVE_ZLIB */ +} + +/**************************************************************************** + * Name: telnet_vprintf + * + * Description: + * Send formatted data with \r and \n translation in addition to IAC IAC + * + * See telnet_printf(). + * + ****************************************************************************/ + +int telnet_vprintf(struct telnet_s *telnet, const char *fmt, va_list va) +{ + static const char CRLF[] = { '\r', '\n' }; + static const char CRNUL[] = { '\r', '\0' }; + char buffer[1024]; + char *output = buffer; + int rs; + int l; + int i; + + /* Format */ + + va_list va2; + va_copy(va2, va); + rs = vsnprintf(buffer, sizeof(buffer), fmt, va); + if (rs >= sizeof(buffer)) + { + output = (char *)malloc(rs + 1); + if (output == 0) + { + _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, + "malloc() failed: %d", errno); + return -1; + } + + rs = vsnprintf(output, rs + 1, fmt, va2); + } + + va_end(va2); + va_end(va); + + /* Send */ + + for (l = i = 0; i != rs; ++i) + { + /* Special characters */ + + if (output[i] == (char)TELNET_IAC || output[i] == '\r' || + output[i] == '\n') + { + /* Dump prior portion of text */ + + if (i != l) + { + _send(telnet, output + l, i - l); + } + + l = i + 1; + + /* IAC -> IAC IAC */ + + if (output[i] == (char)TELNET_IAC) + { + telnet_iac(telnet, TELNET_IAC); + } + + /* Automatic translation of \r -> CRNUL */ + + else if (output[i] == '\r') + { + _send(telnet, CRNUL, 2); + } + + /* Automatic translation of \n -> CRLF */ + + else if (output[i] == '\n') + { + _send(telnet, CRLF, 2); + } + } + } + + /* Send whatever portion of output is left */ + + if (i != l) + { + _send(telnet, output + l, i - l); + } + + /* Free allocated memory, if any */ + + if (output != buffer) + { + free(output); + } + + return rs; +} + +/**************************************************************************** + * Name: telnet_printf + * + * Description: + * 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(). + * + * Input Parameters: + * telnet Telnet state tracker object. + * fmt Format string. + * + * Returned Value: + * Number of bytes sent. + * + ****************************************************************************/ + +int telnet_printf(struct telnet_s *telnet, const char *fmt, ...) +{ + va_list va; + int rs; + + va_start(va, fmt); + rs = telnet_vprintf(telnet, fmt, va); + va_end(va); + + return rs; +} + +/**************************************************************************** + * Name: telnet_raw_vprintf + * + * Description: + * Send formatted data (no newline escaping). + * + * See telnet_raw_printf(). + * + ****************************************************************************/ + +int telnet_raw_vprintf(struct telnet_s *telnet, const char *fmt, va_list va) +{ + char buffer[1024]; + char *output = buffer; + int rs; + + /* Format; allocate more space if necessary */ + + va_list va2; + va_copy(va2, va); + rs = vsnprintf(buffer, sizeof(buffer), fmt, va); + if (rs >= sizeof(buffer)) + { + output = (char *)malloc(rs + 1); + if (output == 0) + { + _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0, + "malloc() failed: %d", errno); + return -1; + } + + rs = vsnprintf(output, rs + 1, fmt, va2); + } + + va_end(va2); + va_end(va); + + /* Send out the formatted data */ + + telnet_send(telnet, output, rs); + + /* Release allocated memory, if any */ + + if (output != buffer) + { + free(output); + } + + return rs; +} + +/**************************************************************************** + * Name: telnet_raw_printf + * + * Description: + * 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(). + * + * Input Parameters: + * telnet Telnet state tracker object. + * fmt Format string. + * + * Returned Value: + * Number of bytes sent. + * + ****************************************************************************/ + +int telnet_raw_printf(struct telnet_s *telnet, const char *fmt, ...) +{ + va_list va; + int rs; + + va_start(va, fmt); + rs = telnet_raw_vprintf(telnet, fmt, va); + va_end(va); + + return rs; +} + +/**************************************************************************** + * Name: telnet_begin_newenviron + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * type One of TELNET_ENVIRON_SEND, TELNET_ENVIRON_IS, or + * TELNET_ENVIRON_INFO. + * + ****************************************************************************/ + +void telnet_begin_newenviron(struct telnet_s *telnet, unsigned char cmd) +{ + telnet_begin_sb(telnet, TELNET_TELOPT_NEW_ENVIRON); + telnet_send(telnet, (const char *)&cmd, 1); +} + +/**************************************************************************** + * Name: telnet_newenviron_value + * + * Description: + * Send a NEW-ENVIRON variable name or value. + * + * This can only be called between calls to telnet_begin_newenviron() and + * telnet_finish_newenviron(). + * + * Input Parameters: + * telnet Telnet state tracker object. + * type One of TELNET_ENVIRON_VAR, TELNET_ENVIRON_USERVAR, or + * TELNET_ENVIRON_VALUE. + * string Variable name or value. + * + ****************************************************************************/ + +void telnet_newenviron_value(struct telnet_s *telnet, unsigned char type, + const char *string) +{ + telnet_send(telnet, (const char *)&type, 1); + + if (string != 0) + { + telnet_send(telnet, string, strlen(string)); + } +} + +/**************************************************************************** + * Name: telnet_ttype_send + * + * Description: + * Send the TERMINAL-TYPE SEND command. + * + * Sends the sequence IAC TERMINAL-TYPE SEND. + * + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +void telnet_ttype_send(struct telnet_s *telnet) +{ + static const unsigned char SEND[] = + { + TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_SEND, + TELNET_IAC, TELNET_SE + }; + + _sendu(telnet, SEND, sizeof(SEND)); +} + +/**************************************************************************** + * Name: telnet_ttype_is + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * ttype Name of the terminal-type being sent. + * + ****************************************************************************/ + +void telnet_ttype_is(struct telnet_s *telnet, const char *ttype) +{ + static const unsigned char IS[] = + { + TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_IS + }; + + _sendu(telnet, IS, sizeof(IS)); + _send(telnet, ttype, strlen(ttype)); + telnet_finish_sb(telnet); +} + +/**************************************************************************** + * Name: telnet_send_zmp + * + * Description: + * Send a ZMP command. + * + * Input Parameters: + * telnet Telnet state tracker object. + * argc Number of ZMP commands being sent. + * argv Array of argument strings. + * + ****************************************************************************/ + +void telnet_send_zmp(struct telnet_s *telnet, size_t argc, const char **argv) +{ + size_t i; + + /* ZMP header */ + + telnet_begin_zmp(telnet, argv[0]); + + /* Send out each argument, including trailing NUL byte */ + + for (i = 1; i != argc; ++i) + { + telnet_zmp_arg(telnet, argv[i]); + } + + /* ZMP footer */ + + telnet_finish_zmp(telnet); +} + +/**************************************************************************** + * Name: telnet_send_vzmpv + * + * Description: + * Send a ZMP command. + * + * See telnet_send_zmpv(). + * + ****************************************************************************/ + +void telnet_send_vzmpv(struct telnet_s *telnet, va_list va) +{ + const char *arg; + + /* ZMP header */ + + telnet_begin_sb(telnet, TELNET_TELOPT_ZMP); + + /* Send out each argument, including trailing NUL byte */ + + while ((arg = va_arg(va, const char *)) != 0) + { + telnet_zmp_arg(telnet, arg); + } + + /* ZMP footer */ + + telnet_finish_zmp(telnet); +} + +/**************************************************************************** + * Name: telnet_send_zmpv + * + * Description: + * 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. + * + * Input Parameters: + * telnet Telnet state tracker object. + * + ****************************************************************************/ + +void telnet_send_zmpv(struct telnet_s *telnet, ...) +{ + va_list va; + + va_start(va, telnet); + telnet_send_vzmpv(telnet, va); + va_end(va); +} + +/**************************************************************************** + * Name: telnet_begin_zmp + * + * Description: + * Begin sending a ZMP command + * + * Input Parameters: + * telnet Telnet state tracker object. + * cmd The first argument (command name) for the ZMP command. + * + ****************************************************************************/ + +void telnet_begin_zmp(struct telnet_s *telnet, const char *cmd) +{ + telnet_begin_sb(telnet, TELNET_TELOPT_ZMP); + telnet_zmp_arg(telnet, cmd); +} + +/**************************************************************************** + * Name: telnet_zmp_arg + * + * Description: + * Send a ZMP command argument. + * + * Input Parameters: + * telnet Telnet state tracker object. + * arg Telnet argument string. + * + ****************************************************************************/ + +void telnet_zmp_arg(struct telnet_s *telnet, const char *arg) +{ + telnet_send(telnet, arg, strlen(arg) + 1); +} diff --git a/netutils/telnetd/telnetd_daemon.c b/netutils/telnetd/telnetd_daemon.c index 2e140b712..c8aabf263 100644 --- a/netutils/telnetd/telnetd_daemon.c +++ b/netutils/telnetd/telnetd_daemon.c @@ -1,7 +1,7 @@ /**************************************************************************** * netutils/telnetd/telnetd_daemon.c * - * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2012, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -72,9 +73,10 @@ struct telnetd_s { - int port; /* The port to listen on (in network byte order) */ - int priority; /* The execution priority of the spawned task, */ - int stacksize; /* The stack size needed by the spawned task */ + uint16_t port; /* The port to listen on (in network byte order) */ + sa_family_t family; /* Address family */ + uint8_t priority; /* The execution priority of the spawned task, */ + size_t stacksize; /* The stack size needed by the spawned task */ main_t entry; /* The entrypoint of the task to spawn when a new * connection is accepted. */ }; @@ -126,7 +128,16 @@ static struct telnetd_common_s g_telnetdcommon; static int telnetd_daemon(int argc, char *argv[]) { FAR struct telnetd_s *daemon; - struct sockaddr_in myaddr; + union + { + struct sockaddr generic; +#ifdef CONFIG_NET_IPv4 + struct sockaddr_in ipv4; +#endif +#ifdef CONFIG_NET_IPv6 + struct sockaddr_in6 ipv6; +#endif + } addr; struct telnet_session_s session; #ifdef CONFIG_NET_SOLINGER struct linger ling; @@ -187,11 +198,12 @@ static int telnetd_daemon(int argc, char *argv[]) /* Create a new TCP socket to use to listen for connections */ - listensd = socket(PF_INET, SOCK_STREAM, 0); + listensd = socket(daemon->family, SOCK_STREAM, 0); if (listensd < 0) { int errval = errno; - nerr("ERROR: socket failure: %d\n", errval); + nerr("ERROR: socket() failed for family %u: %d\n", + daemon->family, errval); return -errval; } @@ -208,11 +220,33 @@ static int telnetd_daemon(int argc, char *argv[]) /* Bind the socket to a local address */ - myaddr.sin_family = AF_INET; - myaddr.sin_port = daemon->port; - myaddr.sin_addr.s_addr = INADDR_ANY; +#ifdef CONFIG_NET_IPv4 + if (daemon->family == AF_INET) + { + addr.ipv4.sin_family = AF_INET; + addr.ipv4.sin_port = daemon->port; + addr.ipv4.sin_addr.s_addr = INADDR_ANY; + addrlen = sizeof(struct sockaddr_in); + } + else +#endif +#ifdef CONFIG_NET_IPv6 + if (daemon->family == AF_INET6) + { + addr.ipv6.sin6_family = AF_INET6; + addr.ipv6.sin6_port = daemon->port; + addrlen = sizeof(struct sockaddr_in6); - if (bind(listensd, (struct sockaddr*)&myaddr, sizeof(struct sockaddr_in)) < 0) + memset(addr.ipv6.sin6_addr.s6_addr, 0, addrlen); + } + else +#endif + { + nerr("ERROR: Unsupported address family: %u", daemon->family); + goto errout_with_socket; + } + + if (bind(listensd, &addr.generic, addrlen) < 0) { nerr("ERROR: bind failure: %d\n", errno); goto errout_with_socket; @@ -240,8 +274,8 @@ static int telnetd_daemon(int argc, char *argv[]) { ninfo("Accepting connections on port %d\n", ntohs(daemon->port)); - addrlen = sizeof(struct sockaddr_in); - acceptsd = accept(listensd, (struct sockaddr*)&myaddr, &addrlen); + socklen_t accptlen = sizeof(addr); + acceptsd = accept(listensd, &addr.generic, &accptlen); if (acceptsd < 0) { /* Accept failed */ @@ -390,6 +424,7 @@ int telnetd_start(FAR struct telnetd_config_s *config) /* Initialize the daemon structure */ daemon->port = config->d_port; + daemon->family = config->d_family; daemon->priority = config->t_priority; daemon->stacksize = config->t_stacksize; daemon->entry = config->t_entry; diff --git a/nshlib/Kconfig b/nshlib/Kconfig index cabfd52b2..d5b37672b 100644 --- a/nshlib/Kconfig +++ b/nshlib/Kconfig @@ -483,6 +483,11 @@ config NSH_DISABLE_TEST bool "Disable test" default n +config NSH_DISABLE_TELNETD + bool "Disable telnetd" + default n if !NSH_NETLOCAL + default y if NSH_NETLOCAL + config NSH_DISABLE_UMOUNT bool "Disable umount" default n diff --git a/nshlib/README.txt b/nshlib/README.txt index 8edf053b7..0bf4a50f7 100644 --- a/nshlib/README.txt +++ b/nshlib/README.txt @@ -1080,6 +1080,21 @@ o sleep Pause execution (sleep) of seconds. +o telnetd + + The Telnet daemon may be started either programmatically by calling + nsh_telnetstart() or it may be started from the NSH command line using + this telnetd command. + + Normally this command would be suppressed with CONFIG_NSH_DISABLE_TELNETD + because the Telnet daemon is automatically started in nsh_main.c. The + exception is when CONFIG_NSH_NETLOCAL is selected. IN that case, the + network is not enabled at initialization but rather must be enabled from + the NSH command line or via other applications. + + In that case, calling nsh_telnetstart() before the the network is + initialized will fail. + o time "" Perform command timing. This command will execute the following @@ -1274,6 +1289,7 @@ Command Dependencies on Configuration Settings shutdown CONFIG_BOARDCTL_POWEROFF || CONFIG_BOARDCTL_RESET sleep !CONFIG_DISABLE_SIGNALS test !CONFIG_NSH_DISABLESCRIPT + telnetd CONFIG_NSH_TELNET && !CONFIG_NSH_DISABLE_TELNETD time --- umount !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_READABLE uname !CONFIG_NSH_DISABLE_UNAME diff --git a/nshlib/nsh.h b/nshlib/nsh.h index 73cfe9ec5..637a975cc 100644 --- a/nshlib/nsh.h +++ b/nshlib/nsh.h @@ -1187,6 +1187,11 @@ int cmd_lsmod(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); int cmd_wget(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); # endif # endif +# if defined(CONFIG_NSH_TELNET) +# ifndef CONFIG_NSH_DISABLE_TELNETD + int cmd_telnetd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# endif #endif /* CONFIG_NET */ #if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) && \ diff --git a/nshlib/nsh_command.c b/nshlib/nsh_command.c index 2d8b731d2..b4e334e0f 100644 --- a/nshlib/nsh_command.c +++ b/nshlib/nsh_command.c @@ -472,6 +472,14 @@ static const struct cmdmap_s g_cmdmap[] = { "test", cmd_test, 3, CONFIG_NSH_MAXARGUMENTS, "" }, #endif +#if defined(CONFIG_NSH_TELNET) && !defined(CONFIG_NSH_DISABLE_TELNETD) +#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) + {"telnetd", cmd_telnetd, 2, 2, [ipv4|ipv6] }, +#else + {"telnetd", cmd_telnetd, 1, 1, NULL }, +#endif +#endif + #ifndef CONFIG_NSH_DISABLE_TIME { "time", cmd_time, 2, 2, "\"\"" }, #endif diff --git a/nshlib/nsh_telnetd.c b/nshlib/nsh_telnetd.c index 74a9ab82d..ae65dda89 100644 --- a/nshlib/nsh_telnetd.c +++ b/nshlib/nsh_telnetd.c @@ -1,7 +1,7 @@ /**************************************************************************** * apps/nshlib/nsh_telnetd.c * - * Copyright (C) 2007-2013, 2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2013, 2016-2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -40,6 +40,8 @@ #include #include +#include +#include #include #include @@ -51,6 +53,17 @@ #ifdef CONFIG_NSH_TELNET +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum telnetd_state_e +{ + TELNETD_NOTRUNNING = 0, + TELNETD_STARTED, + TELNETD_RUNNING +}; + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -167,8 +180,12 @@ static int nsh_telnetmain(int argc, char *argv[]) * the daemon has been started. * * Input Parameters: - * None. All of the properties of the Telnet daemon are controlled by - * NuttX configuration setting. + * family - Provides the IP family to use by the server. May be either + * AF_INET or AF_INET6. This is needed because both both may be + * enabled in the configuration. + * + * All of the other properties of the Telnet daemon are controlled by + * NuttX configuration settings. * * Returned Values: * The task ID of the Telnet daemon was successfully started. A negated @@ -176,39 +193,104 @@ static int nsh_telnetmain(int argc, char *argv[]) * ****************************************************************************/ -int nsh_telnetstart(void) +int nsh_telnetstart(sa_family_t family) { - struct telnetd_config_s config; - int ret; + static enum telnetd_state_e state = TELNETD_NOTRUNNING; + int ret = OK; - /* Initialize any USB tracing options that were requested. If standard - * console is also defined, then we will defer this step to the standard - * console. - */ + if (state == TELNETD_NOTRUNNING) + { + struct telnetd_config_s config; + + /* There is a tiny race condition here if two tasks were to try to + * start the Telnet daemon concurrently. + */ + + state = TELNETD_STARTED; + + /* Initialize any USB tracing options that were requested. If + * standard console is also defined, then we will defer this step to + * the standard console. + */ #if defined(CONFIG_NSH_USBDEV_TRACE) && !defined(CONFIG_NSH_CONSOLE) - usbtrace_enable(TRACE_BITSET); + usbtrace_enable(TRACE_BITSET); #endif - /* Configure the telnet daemon */ + /* Configure the telnet daemon */ - config.d_port = HTONS(CONFIG_NSH_TELNETD_PORT); - config.d_priority = CONFIG_NSH_TELNETD_DAEMONPRIO; - config.d_stacksize = CONFIG_NSH_TELNETD_DAEMONSTACKSIZE; - config.t_priority = CONFIG_NSH_TELNETD_CLIENTPRIO; - config.t_stacksize = CONFIG_NSH_TELNETD_CLIENTSTACKSIZE; - config.t_entry = nsh_telnetmain; + config.d_port = HTONS(CONFIG_NSH_TELNETD_PORT); + config.d_family = family; + config.d_priority = CONFIG_NSH_TELNETD_DAEMONPRIO; + config.d_stacksize = CONFIG_NSH_TELNETD_DAEMONSTACKSIZE; + config.t_priority = CONFIG_NSH_TELNETD_CLIENTPRIO; + config.t_stacksize = CONFIG_NSH_TELNETD_CLIENTSTACKSIZE; + config.t_entry = nsh_telnetmain; - /* Start the telnet daemon */ + /* Start the telnet daemon */ - _info("Starting the Telnet daemon\n"); - ret = telnetd_start(&config); - if (ret < 0) - { - _err("ERROR: Failed to tart the Telnet daemon: %d\n", ret); + _info("Starting the Telnet daemon\n"); + + ret = telnetd_start(&config); + if (ret < 0) + { + _err("ERROR: Failed to tart the Telnet daemon: %d\n", ret); + state = TELNETD_NOTRUNNING; + } + else + { + state = TELNETD_RUNNING; + } } return ret; } +/**************************************************************************** + * Name: cmd_telnetd + * + * Description: + * The Telnet daemon may be started either programmatically by calling + * nsh_telnetstart() or it may be started from the NSH command line using + * this telnetd command. + * + * Normally this command would be suppressed with CONFIG_NSH_DISABLE_TELNETD + * because the Telnet daemon is automatically started in nsh_main.c. The + * exception is when CONFIG_NSH_NETLOCAL is selected. IN that case, the + * network is not enabled at initialization but rather must be enabled from + * the NSH command line or via other applications. + * + * In that case, calling nsh_telnetstart() before the the network is + * initialized will fail. + * + * Input Parameters: + * None. All of the properties of the Telnet daemon are controlled by + * NuttX configuration setting. + * + * Returned Values: + * OK is returned on success; ERROR is return on failure. + * + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_TELNETD +int cmd_telnetd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + sa_family_t family; + + /* If both IPv6 nd IPv4 are enabled, then the address family must + * be specified on the command line. + */ + +#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) + family = (strcmp(argv[1], "ipv6") == 0) ? AF_INET6 : AF_INET; +#elif defined(CONFIG_NET_IPv6) + family = AF_INET6; +#else /* if defined(CONFIG_NET_IPv4) */ + family = AF_INET; +#endif + + return nsh_telnetstart(family) < 0 ? ERROR : OK; +} +#endif + #endif /* CONFIG_NSH_TELNET */ diff --git a/system/telnet/.gitignore b/system/telnet/.gitignore new file mode 100644 index 000000000..fa1ec7579 --- /dev/null +++ b/system/telnet/.gitignore @@ -0,0 +1,11 @@ +/Make.dep +/.depend +/.built +/*.asm +/*.obj +/*.rel +/*.lst +/*.sym +/*.adb +/*.lib +/*.src diff --git a/system/telnet/Kconfig b/system/telnet/Kconfig new file mode 100644 index 000000000..7cc99de39 --- /dev/null +++ b/system/telnet/Kconfig @@ -0,0 +1,61 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig SYSTEM_TELNET_CHATD + bool "Telnet chat deamon" + default n + depends on NET && NET_TCP && !DISABLE_POLL + select NETUTILS_TELNETC + ---help--- + Enable the Telnet Chat daemon. + +if SYSTEM_TELNET_CHATD + +config SYSTEM_TELNET_CHATD_PROGNAME + string "Chat daemon program name" + default "chatd" + depends on BUILD_KERNEL + +if NSH_BUILTIN_APPS + +config SYSTEM_TELNET_CHATD_STACKSIZE + int "Chat daemon stacksize" + default 2048 + +config SYSTEM_TELNET_CHATD_PRIORITY + int "Chat daemon priority" + default 100 + +endif # NSH_BUILTIN_APPS +endif # SYSTEM_TELNET_CHATD + +menuconfig SYSTEM_TELNET_CLIENT + bool "Telnet client" + default n + depends on NET && NET_TCP && !DISABLE_POLL + select NETUTILS_TELNETC + select SYSTEM_READLINE + ---help--- + Enable the Telnet client program. + +if SYSTEM_TELNET_CLIENT + +config SYSTEM_TELNET_CLIENT_PROGNAME + string "Telnet client program name" + default "chatd" + depends on BUILD_KERNEL + +if NSH_BUILTIN_APPS + +config SYSTEM_TELNET_CLIENT_STACKSIZE + int "Telnet client stacksize" + default 2048 + +config SYSTEM_TELNET_CLIENT_PRIORITY + int "Telnet client priority" + default 100 + +endif # NSH_BUILTIN_APPS +endif # SYSTEM_TELNET_CLIENT diff --git a/system/telnet/Make.defs b/system/telnet/Make.defs new file mode 100644 index 000000000..cb9c7a480 --- /dev/null +++ b/system/telnet/Make.defs @@ -0,0 +1,41 @@ +############################################################################ +# apps/system/usbmsc/Make.defs +# Adds selected applications to apps/ build +# +# Copyright (C) 2016 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_SYSTEM_TELNET_CHATD),y) +CONFIGURED_APPS += system/telnet +else ifeq ($(CONFIG_SYSTEM_TELNET_CLIENT),y) +CONFIGURED_APPS += system/telnet +endif diff --git a/system/telnet/Makefile b/system/telnet/Makefile new file mode 100644 index 000000000..0ff663985 --- /dev/null +++ b/system/telnet/Makefile @@ -0,0 +1,205 @@ +############################################################################ +# apps/system/telnet/Makefile +# +# Copyright (C) 2017 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +# Telnet Programs + +ifeq ($(CONFIG_SYSTEM_TELNET_CHATD),y) + +# Chatd files + +CHATD_CSRCS = +CHATD_MAINSRC = telnet_chatd.c + +CHATD_COBJS = $(CHATD_CSRCS:.c=$(OBJEXT)) +CHATD_MAINOBJ = $(CHATD_MAINSRC:.c=$(OBJEXT)) + +# Chatd Application Info + +CONFIG_SYSTEM_TELNET_CHATD_PROGNAME ?= chatd +CONFIG_SYSTEM_TELNET_CHATD_PRIORITY ?= 100 +CONFIG_SYSTEM_TELNET_CHATD_STACKSIZE ?= 2048 + +CHATD_MAINNAME = chatd_main +CHATD_APPNAME = chatd +CHATD_PROGNAME = $(CONFIG_SYSTEM_TELNET_CHATD_PROGNAME) +CHATD_PRIORITY = $(CONFIG_SYSTEM_TELNET_CHATD_PRIORITY) +CHATD_STACKSIZE = $(CONFIG_SYSTEM_TELNET_CHATD_STACKSIZE) + +endif + +ifeq ($(CONFIG_SYSTEM_TELNET_CLIENT),y) + +# Telnet client files + +CLIENT_CSRCS = +CLIENT_MAINSRC = telnet_client.c + +CLIENT_COBJS = $(CLIENT_CSRCS:.c=$(OBJEXT)) +CLIENT_MAINOBJ = $(CLIENT_MAINSRC:.c=$(OBJEXT)) + +# Telnet client Application Info + +CONFIG_SYSTEM_TELNET_CLIENT_PROGNAME ?= tcpclient +CONFIG_SYSTEM_TELNET_CLIENT_PRIORITY ?= 100 +CONFIG_SYSTEM_TELNET_CLIENT_STACKSIZE ?= 2048 + +CLIENT_MAINNAME = telnet_main +CLIENT_APPNAME = telnet +CLIENT_PROGNAME = $(CONFIG_SYSTEM_TELNET_CLIENT_PROGNAME) +CLIENT_PRIORITY = $(CONFIG_SYSTEM_TELNET_CLIENT_PRIORITY) +CLIENT_STACKSIZE = $(CONFIG_SYSTEM_TELNET_CLIENT_STACKSIZE) + +endif + +# All programns + +TARG_SRCS = $(CHATD_CRCS) $(CHATD_MAINSRC) $(CLIENT_CSRCS) $(CLIENT_MAINSRC) +TARG_OBJS = $(CHATD_COBJS) $(CLIENT_COBJS) + +ifneq ($(CONFIG_BUILD_KERNEL),y) + TARG_OBJS += $(CHATD_MAINOBJ) $(CLIENT_MAINOBJ) +endif + +ifeq ($(CONFIG_WINDOWS_NATIVE),y) + TARG_BIN = ..\..\libapps$(LIBEXT) +else +ifeq ($(WINTOOL),y) + TARG_BIN = ..\\..\\libapps$(LIBEXT) +else + TARG_BIN = ../../libapps$(LIBEXT) +endif +endif + +ifeq ($(WINTOOL),y) + INSTALL_DIR = "${shell cygpath -w $(BIN_DIR)}" +else + INSTALL_DIR = $(BIN_DIR) +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: clean depend distclean preconfig + +$(CHATD_COBJS) $(CHATD_MAINOBJ) $(CLIENT_COBJS) $(CLIENT_MAINOBJ) : %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(TARG_OBJS) + $(call ARCHIVE, $(TARG_BIN), $(TARG_OBJS)) + $(Q) touch .built + +ifeq ($(CONFIG_BUILD_KERNEL),y) +ifeq ($(CONFIG_SYSTEM_TELNET_CHATD),y) + +$(BIN_DIR)$(DELIM)$(CHATD_PROGNAME): $(OBJS) $(CHATD_MAINOBJ) + @echo "LD: $(CHATD_PROGNAME)" + $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $(INSTALL_DIR)$(DELIM)$(CHATD_PROGNAME) $(ARCHCRT0OBJ) $(CHATD_MAINOBJ) $(LDLIBS) + $(Q) $(NM) -u $(INSTALL_DIR)$(DELIM)$(CHATD_PROGNAME) + +chatd_install: $(BIN_DIR)$(DELIM)$(CHATD_PROGNAME) +else +chatd_install: +endif + +ifeq ($(CONFIG_SYSTEM_TELNET_CLIENT),y) + +$(BIN_DIR)$(DELIM)$(CLIENT_PROGNAME): $(OBJS) $(CLIENT_MAINOBJ) + @echo "LD: $(CLIENT_PROGNAME)" + $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $(INSTALL_DIR)$(DELIM)$(CLIENT_PROGNAME) $(ARCHCRT0OBJ) $(CLIENT_MAINOBJ) $(LDLIBS) + $(Q) $(NM) -u $(INSTALL_DIR)$(DELIM)$(CLIENT_PROGNAME) + +client_install: $(BIN_DIR)$(DELIM)$(CLIENT_PROGNAME) +else +client_install: +endif + +install: chatd_install client_install + +else +install: + +endif + +ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) +ifeq ($(CONFIG_SYSTEM_TELNET_CHATD),y) + +$(BUILTIN_REGISTRY)$(DELIM)$(CHATD_APPNAME)_main.bdat: $(DEPCONFIG) Makefile + $(call REGISTER,$(CHATD_APPNAME),$(CHATD_PRIORITY),$(CHATD_STACKSIZE),$(CHATD_MAINNAME)) + +chatd_register: $(BUILTIN_REGISTRY)$(DELIM)$(CHATD_APPNAME)_main.bdat +else +chatd_register: +endif + +ifeq ($(CONFIG_SYSTEM_TELNET_CLIENT),y) +$(BUILTIN_REGISTRY)$(DELIM)$(CLIENT_APPNAME)_main.bdat: $(DEPCONFIG) Makefile + $(call REGISTER,$(CLIENT_APPNAME),$(CLIENT_PRIORITY),$(CLIENT_STACKSIZE),$(CLIENT_MAINNAME)) + +client_register: $(BUILTIN_REGISTRY)$(DELIM)$(CLIENT_APPNAME)_main.bdat +else +client_register: +endif + +context: chatd_register client_register +else +context: +endif + +.depend: Makefile $(TARG_CSRCS) + @$(MKDEP) $(ROOTDEPPATH) "$(CC)" -- $(CFLAGS) -- $(TARG_CSRCS) >Make.dep + @touch $@ + +depend: .depend + +clean: + $(call DELFILE, .built) + $(call DELFILE, *.dSYM) + $(call CLEAN) + +distclean: clean + $(call DELFILE, Make.dep) + $(call DELFILE, .depend) + +preconfig: + +-include Make.dep diff --git a/system/telnet/telnet_chatd.c b/system/telnet/telnet_chatd.c new file mode 100644 index 000000000..423724a79 --- /dev/null +++ b/system/telnet/telnet_chatd.c @@ -0,0 +1,518 @@ +/**************************************************************************** + * apps/system/telnet/telnet_chatd.c + * + * Leveraged from libtelnet, https://github.com/seanmiddleditch/libtelnet. + * Modified and re-released under the BSD license: + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * The original authors of libtelnet are listed below. Per their licesne, + * "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." + * + * Author: Sean Middleditch + * (Also listed in the AUTHORS file are Jack Kelly + * and Katherine Flavel ) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "netutils/telnetc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_USERS 64 +#define LINEBUFFER_SIZE 256 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct user_s +{ + char *name; + int sock; + struct telnet_s *telnet; + char linebuf[256]; + int linepos; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct telnet_telopt_s g_telopts[] = +{ + { + TELNET_TELOPT_COMPRESS2, TELNET_WILL, TELNET_DONT + }, + { + -1, 0, 0 + } +}; + +static struct user_s g_users[MAX_USERS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void linebuffer_push(char *buffer, size_t size, int *linepos, + char ch, void (*cb) (const char *line, int 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') + { + /* So 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 != 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 != MAX_USERS; ++i) + { + if (g_users[i].sock != -1) + { + telnet_printf(g_users[i].telnet, "%s: %s\n", from, msg); + } + } +} + +static void _send(int sock, const char *buffer, unsigned int size) +{ + int ret; + + /* Ignore on invalid socket */ + + if (sock == -1) + { + return; + } + + /* Send data */ + + while (size > 0) + { + if ((ret = send(sock, buffer, size, 0)) == -1) + { + if (errno != EINTR && errno != ECONNRESET) + { + fprintf(stderr, "send() failed: %d\n", errno); + exit(1); + } + else + { + return; + } + } + else if (ret == 0) + { + fprintf(stderr, "send() unexpectedly returned 0\n"); + exit(1); + } + + /* Update pointer and size to see if we've got more to send */ + + buffer += ret; + size -= ret; + } +} + +/* Process input line */ + +static void _online(const char *line, int overflow, void *ud) +{ + struct user_s *user = (struct user_s *)ud; + int i; + + /* 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.\nEnter name: "); + return; + } + + /* Must not already be in use */ + + for (i = 0; i != MAX_USERS; ++i) + { + if (g_users[i].name != 0 && strcmp(g_users[i].name, line) == 0) + { + telnet_printf(user->telnet, "Name in use.\nEnter name: "); + return; + } + } + + /* Keep name */ + + user->name = strdup(line); + telnet_printf(user->telnet, "Welcome, %s!\n", line); + return; + } + + /* If line is "quit" then, well, quit */ + + if (strcmp(line, "quit") == 0) + { + close(user->sock); + user->sock = -1; + _message(user->name, "** HAS QUIT **"); + free(user->name); + user->name = 0; + return; + } + + /* Just a message -- send to all users */ + + _message(user->name, line); +} + +static void _input(struct user_s *user, const char *buffer, unsigned int size) +{ + unsigned int i; + + for (i = 0; i != size; ++i) + { + linebuffer_push(user->linebuf, sizeof(user->linebuf), &user->linepos, + (char)buffer[i], _online, user); + } +} + +static void _event_handler(struct telnet_s *telnet, + union telnet_event_u *ev, void *user_data) +{ + struct user_s *user = (struct user_s *)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; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef CONFIG_BUILD_KERNEL +int main(int argc, FAR char *argv[]) +#else +int chatd_main(int argc, char *argv[]) +#endif +{ + char buffer[512]; + short listen_port; + int listen_sock; + int ret; + int i; + struct sockaddr_in addr; + socklen_t addrlen; + struct pollfd pfd[MAX_USERS + 1]; + + /* Check usage */ + + if (argc != 2) + { + fprintf(stderr, "Usage:\n %s \n", argv[0]); + return 1; + } + + /* Initialize data structures */ + + memset(&pfd, 0, sizeof(pfd)); + memset(g_users, 0, sizeof(g_users)); + + for (i = 0; i != MAX_USERS; ++i) + { + g_users[i].sock = -1; + } + + /* Parse listening port */ + + listen_port = (short)strtol(argv[1], 0, 10); + + /* Create listening socket */ + + if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + fprintf(stderr, "socket() failed: %d\n", errno); + return 1; + } + + /* Reuse address option */ + + ret = 1; + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *)&ret, sizeof(ret)); + + /* 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(listen_port); + if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) + { + fprintf(stderr, "bind() failed: %d\n", errno); + return 1; + } + + /* Listen for clients */ + + if (listen(listen_sock, 5) == -1) + { + fprintf(stderr, "listen() failed: %d\n", errno); + return 1; + } + + printf("LISTENING ON PORT %d\n", listen_port); + + /* Initialize listening descriptors */ + + pfd[MAX_USERS].fd = listen_sock; + pfd[MAX_USERS].events = POLLIN; + + /* Loop for ever */ + + for (;;) + { + /* Prepare for poll */ + + for (i = 0; i != MAX_USERS; ++i) + { + if (g_users[i].sock != -1) + { + pfd[i].fd = g_users[i].sock; + pfd[i].events = POLLIN; + } + else + { + pfd[i].fd = -1; + pfd[i].events = 0; + } + } + + /* Poll */ + + ret = poll(pfd, MAX_USERS + 1, -1); + if (ret == -1 && errno != EINTR) + { + fprintf(stderr, "poll() failed: %d\n", errno); + return 1; + } + + /* New connection */ + + if (pfd[MAX_USERS].revents & POLLIN) + { + /* Accept the sock */ + + addrlen = sizeof(addr); + if ((ret = accept(listen_sock, (struct sockaddr *)&addr, + &addrlen)) == -1) + { + fprintf(stderr, "accept() failed: %d\n", errno); + return 1; + } + + printf("Connection received.\n"); + + /* Find a free user */ + + for (i = 0; i != MAX_USERS; ++i) + { + if (g_users[i].sock == -1) + { + break; + } + } + + if (i == MAX_USERS) + { + printf(" rejected (too many users)\n"); + _send(ret, "Too many users.\r\n", 14); + close(ret); + } + + /* Init, welcome */ + + g_users[i].sock = ret; + g_users[i].telnet = telnet_init(g_telopts, _event_handler, 0, &g_users[i]); + telnet_negotiate(g_users[i].telnet, TELNET_WILL, + TELNET_TELOPT_COMPRESS2); + telnet_printf(g_users[i].telnet, "Enter name: "); + + telnet_negotiate(g_users[i].telnet, TELNET_WILL, TELNET_TELOPT_ECHO); + } + + /* Read from client */ + + for (i = 0; i != MAX_USERS; ++i) + { + /* Skip users that aren't actually connected */ + + if (g_users[i].sock == -1) + { + continue; + } + + if (pfd[i].revents & POLLIN) + { + if ((ret = recv(g_users[i].sock, buffer, sizeof(buffer), 0)) > 0) + { + telnet_recv(g_users[i].telnet, buffer, ret); + } + else if (ret == 0) + { + printf("Connection closed.\n"); + close(g_users[i].sock); + if (g_users[i].name != 0) + { + _message(g_users[i].name, "** HAS DISCONNECTED **"); + free(g_users[i].name); + g_users[i].name = 0; + } + + telnet_free(g_users[i].telnet); + g_users[i].sock = -1; + break; + } + else if (errno != EINTR) + { + fprintf(stderr, "recv(client) failed: %d\n", errno); + exit(1); + } + } + } + } + + /* Not that we can reach this, but GCC will cry if it's not here */ + + return 0; +} diff --git a/system/telnet/telnet_client.c b/system/telnet/telnet_client.c new file mode 100644 index 000000000..41f41791b --- /dev/null +++ b/system/telnet/telnet_client.c @@ -0,0 +1,457 @@ +/**************************************************************************** + * apps/system/telnet/telnet_client.c + * + * Leveraged from libtelnet, https://github.com/seanmiddleditch/libtelnet. + * Modified and re-released under the BSD license: + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * The original authors of libtelnet are listed below. Per their licesne, + * "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." + * + * Author: Sean Middleditch + * (Also listed in the AUTHORS file are Jack Kelly + * and Katherine Flavel ) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_ZLIB +# include "zlib.h" +#endif + +#include "system/readline.h" +#include "netutils/telnetc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_NSH_TELNETD_PORT +# define DEFAULT_PORT CONFIG_NSH_TELNETD_PORT +#else +# define DEFAULT_PORT 23 +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct telnet_s *g_telnet; +static int g_echo; + +static const struct telnet_telopt_s g_telopts[] = +{ + { + TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DO + }, + { + TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT + }, + { + TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DO + }, + { + TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DO + }, + { + -1, 0, 0 + } +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void send_local_input(char *buffer, int size) +{ + static char crlf[] = { '\r', '\n' }; + int i; + + for (i = 0; i != size; ++i) + { + /* If we got a CR or LF, replace with CRLF NOTE that usually you'd get a + * CR in UNIX, but in raw mode we get LF instead (not sure why). + */ + + if (buffer[i] == '\r' || buffer[i] == '\n') + { + if (g_echo) + { + printf("\r\n"); + } + + telnet_send(g_telnet, crlf, 2); + } + else + { + if (g_echo) + { + putchar(buffer[i]); + } + + telnet_send(g_telnet, buffer + i, 1); + } + } + + fflush(stdout); +} + +static void telnet_ev_send(int sock, const char *buffer, size_t size) +{ + int ret; + + /* Send data */ + + while (size > 0) + { + if ((ret = send(sock, buffer, size, 0)) == -1) + { + fprintf(stderr, "send() failed: %d\n", errno); + exit(1); + } + else if (ret == 0) + { + fprintf(stderr, "send() unexpectedly returned 0\n"); + exit(1); + } + + /* Update pointer and size to see if we've got more to send */ + + buffer += ret; + size -= ret; + } +} + +static void _event_handler(struct telnet_s *telnet, + union telnet_event_u *ev, void *user_data) +{ + int sock = *(int *)user_data; + + switch (ev->type) + { + /* Data received */ + + case TELNET_EV_DATA: + printf("%.*s", (int)ev->data.size, ev->data.buffer); + fflush(stdout); + break; + + /* Data must be sent */ + + case TELNET_EV_SEND: + telnet_ev_send(sock, ev->data.buffer, ev->data.size); + break; + + /* Request to enable remote feature (or receipt) */ + + case TELNET_EV_WILL: + /* We'll agree to turn off our echo if server wants us to stop */ + + if (ev->neg.telopt == TELNET_TELOPT_ECHO) + { + g_echo = 0; + } + + break; + + /* Notification of disabling remote feature (or receipt) */ + + case TELNET_EV_WONT: + if (ev->neg.telopt == TELNET_TELOPT_ECHO) + { + g_echo = 1; + } + break; + + /* Request to enable local feature (or receipt) */ + + case TELNET_EV_DO: + break; + + /* Demand to disable local feature (or receipt) */ + + case TELNET_EV_DONT: + break; + + /* Respond to TTYPE commands */ + + case TELNET_EV_TTYPE: + /* Respond with our terminal type, if requested */ + + if (ev->ttype.cmd == TELNET_TTYPE_SEND) + { + telnet_ttype_is(telnet, getenv("TERM")); + } + break; + + /* Respond to particular subnegotiations */ + + case TELNET_EV_SUBNEGOTIATION: + break; + + /* Error */ + + case TELNET_EV_ERROR: + fprintf(stderr, "ERROR: %s\n", ev->error.msg); + exit(1); + + default: + /* Ignore */ + + break; + } +} + +static void show_usage(const char *progname, int exitcode) +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\t%s []\n", progname); + fprintf(stderr, "Where:\n"); + fprintf(stderr, "\t is the address of the Telnet server. Either\n"); + fprintf(stderr, "\t\tIPv4 form: ddd.ddd.ddd.ddd\n"); + fprintf(stderr, "\t\tIPv6 form: xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx\n"); + fprintf(stderr, "\t is the (optional) listening port of the Telnet server.\n"); + fprintf(stderr, "\t\tDefault: %u\n", DEFAULT_PORT); + exit(exitcode) ; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef CONFIG_BUILD_KERNEL +int main(int argc, FAR char *argv[]) +#else +int telnet_main(int argc, char *argv[]) +#endif +{ + char buffer[512]; + union + { + struct sockaddr generic; +#ifdef CONFIG_NET_IPv6 + struct sockaddr_in6 ipv6; +#endif +#ifdef CONFIG_NET_IPv4 + struct sockaddr_in ipv4; +#endif + } server; + union + { +#ifdef CONFIG_NET_IPv6 + struct sockaddr_in6 ipv6; +#endif +#ifdef CONFIG_NET_IPv4 + struct sockaddr_in ipv4; +#endif + } local; + struct pollfd pfd[2]; + sa_family_t family; + uint16_t addrlen; + int portno; + int sock; + int ret; + + /* Check usage */ + + if (argc < 2 || argc > 3) + { + fprintf(stderr, "Invalid number of arguments\n"); + show_usage(argv[0], 1); + } + + /* Convert the port number to binary if provided */ + + if (argc == 3) + { + portno = atoi(argv[2]); + if (portno < 0 || portno > UINT16_MAX) + { + fprintf(stderr, "Invalid port number\n"); + show_usage(argv[0], 1); + } + } + else + { + portno = DEFAULT_PORT; + } + + /* Convert the argument into a binary address */ + + memset(&local, 0, sizeof(local)); + +#ifdef CONFIG_EXAMPLES_NETTEST_IPv6 + addrlen = sizeof(struct sockaddr_in6); + family = AF_INET6; + + local.ipv6.sin6_family = AF_INET6; + server.ipv6.sin6_family = AF_INET6; + server.ipv6.sin6_port = htons(portno); + + ret = inet_pton(AF_INET6, argv[1], server.ipv6.sin6_addr.s6_addr); + if (ret < 0) +#endif +#ifdef CONFIG_EXAMPLES_NETTEST_IPv + { + addrlen = sizeof(struct sockaddr_in); + family = AF_INET; + + local.ipv4.sin_family = AF_INET; + server.ipv4.sin_family = AF_INET; + server.ipv4.sin_port = htons(portno); + + ret = inet_pton(AF_INET, argv[1], &server.ipv4.sin_addr); + } + + if (ret < 0) +#endif + { + fprintf(stderr, "ERROR: is invalid\n"); + show_usage(argv[0], 1); + } + + /* Create server socket */ + + sock = socket(family, SOCK_STREAM, 0); + if (sock < 0) + { + fprintf(stderr, "socket() failed: %d\n", errno); + return 1; + } + + /* Bind server socket */ + + ret = bind(sock, (struct sockaddr *)&local, addrlen); + if (ret < 0) + { + fprintf(stderr, "bind() failed: %d\n", errno); + return 1; + } + + /* Connect */ + + ret = connect(sock, &server.generic, addrlen); + if (ret < 0) + { + fprintf(stderr, "connect() failed: %d\n", errno); + return 1; + } + + /* Set input echoing on by default */ + + g_echo = 1; + + /* Initialize telnet box */ + + g_telnet = telnet_init(g_telopts, _event_handler, 0, &sock); + + /* Initialize poll descriptors */ + + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = 1; + pfd[0].events = POLLIN; + pfd[1].fd = sock; + pfd[1].events = POLLIN; + + /* Loop while both connections are open */ + + while (poll(pfd, 2, -1) != -1) + { + /* Read from stdin */ + + if (pfd[0].revents & POLLIN) + { + ret = std_readline(buffer, sizeof(buffer)); + if (ret > 0) + { + send_local_input(buffer, ret); + } + else if (ret == 0) + { + break; + } + else + { + fprintf(stderr, "recv(server) failed: %d\n", errno); + exit(1); + } + } + + /* Read from client */ + + if (pfd[1].revents & POLLIN) + { + if ((ret = recv(sock, buffer, sizeof(buffer), 0)) > 0) + { + telnet_recv(g_telnet, buffer, ret); + } + else if (ret == 0) + { + break; + } + else + { + fprintf(stderr, "recv(client) failed: %d\n", errno); + exit(1); + } + } + } + + /* Clean up */ + + telnet_free(g_telnet); + close(sock); + return 0; +} diff --git a/wireless/ieee802154/i8sak/Makefile b/wireless/ieee802154/i8sak/Makefile index cef46ecfe..d24344887 100644 --- a/wireless/ieee802154/i8sak/Makefile +++ b/wireless/ieee802154/i8sak/Makefile @@ -48,7 +48,7 @@ STACKSIZE = 4096 ASRCS = CSRCS = i8sak_acceptassoc.c i8sak_assoc.c i8sak_scan.c i8sak_blaster.c i8sak_poll.c -CSRCS += i8sak_sniffer.c i8sak_startpan.c i8sak_tx.c i8sak_chan.c +CSRCS += i8sak_sniffer.c i8sak_startpan.c i8sak_tx.c i8sak_chan.c i8sak_coordinfo.c CSRCS += wpanlistener.c MAINSRC = i8sak_main.c diff --git a/wireless/ieee802154/i8sak/i8sak.h b/wireless/ieee802154/i8sak/i8sak.h index 0d6d1395a..cb1eeb3e5 100644 --- a/wireless/ieee802154/i8sak/i8sak.h +++ b/wireless/ieee802154/i8sak/i8sak.h @@ -99,7 +99,14 @@ /* /dev/ is 5 characters */ #define I8SAK_DAEMONNAME_FMTLEN (6 + (I8SAK_MAX_DEVNAME-5) + 1) +/* Helper Macros *************************************************************/ +#define PRINT_COORDEADDR(eaddr) \ + printf(" Coordinator EADDR: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", \ + eaddr[0], eaddr[1], eaddr[2], eaddr[3], eaddr[4], eaddr[5], eaddr[6], eaddr[7]); + +#define PRINT_COORDSADDR(saddr) \ + printf(" Coordinator SADDR: %02X:%02X\n", saddr[0], saddr[1]); /**************************************************************************** * Public Types @@ -183,7 +190,8 @@ void i8sak_tx_cmd (FAR struct i8sak_s *i8sak, int argc, FAR char *argv[ void i8sak_poll_cmd (FAR struct i8sak_s *i8sak, int argc, FAR char *argv[]); void i8sak_sniffer_cmd (FAR struct i8sak_s *i8sak, int argc, FAR char *argv[]); void i8sak_blaster_cmd (FAR struct i8sak_s *i8sak, int argc, FAR char *argv[]); -void i8sak_chan_cmd(FAR struct i8sak_s *i8sak, int argc, FAR char *argv[]); +void i8sak_chan_cmd (FAR struct i8sak_s *i8sak, int argc, FAR char *argv[]); +void i8sak_coordinfo_cmd (FAR struct i8sak_s *i8sak, int argc, FAR char *argv[]); /**************************************************************************** * Inline Functions diff --git a/wireless/ieee802154/i8sak/i8sak_coordinfo.c b/wireless/ieee802154/i8sak/i8sak_coordinfo.c new file mode 100644 index 000000000..e359b3228 --- /dev/null +++ b/wireless/ieee802154/i8sak/i8sak_coordinfo.c @@ -0,0 +1,141 @@ +/**************************************************************************** + * apps/wireless/ieee802154/i8sak/i8sak_coordinfo.c + * IEEE 802.15.4 Swiss Army Knife + * + * Copyright (C) 2017 Verge Inc. All rights reserved. + * Author: Anthony Merlino + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + + /**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i8sak.h" + +#include +#include +#include "wireless/ieee802154.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name : i8sak_coordinfo_cmd + * + * Description : + * Print various coordinator attributes + ****************************************************************************/ + +void i8sak_coordinfo_cmd(FAR struct i8sak_s *i8sak, int argc, FAR char *argv[]) +{ + int option; + int fd; + int argind; + bool geteaddr = false; + bool getsaddr = false; + uint8_t eaddr[IEEE802154_EADDRSIZE]; + uint8_t saddr[IEEE802154_SADDRSIZE]; + + argind = 1; + while ((option = getopt(argc, argv, ":haes")) != ERROR) + { + switch (option) + { + argind++; + case 'h': + fprintf(stderr, "Prints coordinator attributes\n" + "Usage: %s [-h]\n" + " -h = this help menu\n" + , argv[0]); + /* Must manually reset optind if we are going to exit early */ + + optind = -1; + return; + + case 'a': + geteaddr = true; + getsaddr = true; + break; + + case 'e': + geteaddr = true; + break; + + case 's': + getsaddr = true; + break; + + case ':': + fprintf(stderr, "ERROR: missing argument\n"); + /* Must manually reset optind if we are going to exit early */ + + optind = -1; + i8sak_cmd_error(i8sak); /* This exits for us */ + case '?': + fprintf(stderr, "ERROR: unknown argument\n"); + /* Must manually reset optind if we are going to exit early */ + + optind = -1; + i8sak_cmd_error(i8sak); /* This exits for us */ + } + } + + fd = open(i8sak->devname, O_RDWR); + if (fd < 0) + { + printf("cannot open %s, errno=%d\n", i8sak->devname, errno); + i8sak_cmd_error(i8sak); + } + + if (geteaddr) + { + ieee802154_getcoordeaddr(fd, eaddr); + PRINT_COORDEADDR(eaddr); + } + + if (getsaddr) + { + ieee802154_getcoordsaddr(fd, saddr); + PRINT_COORDSADDR(saddr); + } + + close(fd); +} diff --git a/wireless/ieee802154/i8sak/i8sak_main.c b/wireless/ieee802154/i8sak/i8sak_main.c index 0e15fe48e..4d74de876 100644 --- a/wireless/ieee802154/i8sak/i8sak_main.c +++ b/wireless/ieee802154/i8sak/i8sak_main.c @@ -110,6 +110,7 @@ static const struct i8sak_command_s g_i8sak_commands[] = {"sniffer", (CODE void *)i8sak_sniffer_cmd}, {"blaster", (CODE void *)i8sak_blaster_cmd}, {"chan", (CODE void *)i8sak_chan_cmd}, + {"coordinfo", (CODE void *)i8sak_coordinfo_cmd}, }; #define NCOMMANDS (sizeof(g_i8sak_commands) / sizeof(struct i8sak_command_s)) @@ -738,7 +739,8 @@ static int i8sak_showusage(FAR const char *progname, int exitcode) " poll [-h]\n" " blaster [-h|q|f |p ]\n" " sniffer [-h|q]\n" - " chan [-h|g] []" + " chan [-h|g] []\n" + " coordinfo [-h|a|e|s]\n" , progname); exit(exitcode); } diff --git a/wireless/ieee802154/i8sak/i8sak_scan.c b/wireless/ieee802154/i8sak/i8sak_scan.c index afacc60a9..069949f33 100644 --- a/wireless/ieee802154/i8sak/i8sak_scan.c +++ b/wireless/ieee802154/i8sak/i8sak_scan.c @@ -233,21 +233,11 @@ static void scan_eventcb(FAR struct ieee802154_notif_s *notif, FAR void *arg) if (scan->pandescs[i].coordaddr.mode == IEEE802154_ADDRMODE_SHORT) { - printf(" Coordinator saddr: %02X:%02X\n", - scan->pandescs[i].coordaddr.saddr[0], - scan->pandescs[i].coordaddr.saddr[1]); + PRINT_COORDSADDR(scan->pandescs[i].coordaddr.saddr); } else { - printf(" Coordinator eaddr: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - scan->pandescs[i].coordaddr.eaddr[0], - scan->pandescs[i].coordaddr.eaddr[1], - scan->pandescs[i].coordaddr.eaddr[2], - scan->pandescs[i].coordaddr.eaddr[3], - scan->pandescs[i].coordaddr.eaddr[4], - scan->pandescs[i].coordaddr.eaddr[5], - scan->pandescs[i].coordaddr.eaddr[6], - scan->pandescs[i].coordaddr.eaddr[7]); + PRINT_COORDEADDR(scan->pandescs[i].coordaddr.eaddr); } } } diff --git a/wireless/ieee802154/libmac/Makefile b/wireless/ieee802154/libmac/Makefile index 76c817573..11014cfc3 100644 --- a/wireless/ieee802154/libmac/Makefile +++ b/wireless/ieee802154/libmac/Makefile @@ -54,7 +54,10 @@ CSRCS += ieee802154_seteaddr.c ieee802154_geteaddr.c CSRCS += ieee802154_setpromisc.c ieee802154_getpromisc.c CSRCS += ieee802154_setrxonidle.c ieee802154_getrxonidle.c CSRCS += ieee802154_settxpwr.c ieee802154_gettxpwr.c -CSRCS += ieee802154_getdevmode.c ieee802154_setassocpermit.c +CSRCS += ieee802154_getdevmode.c +CSRCS += ieee802154_setassocpermit.c +CSRCS += ieee802154_getcoordsaddr.c +CSRCS += ieee802154_getcoordeaddr.c ifeq ($(CONFIG_NET_6LOWPAN),y) # Add Get/Set Attribute helpers diff --git a/wireless/ieee802154/libmac/ieee802154_getcoordeaddr.c b/wireless/ieee802154/libmac/ieee802154_getcoordeaddr.c new file mode 100644 index 000000000..31e5e3097 --- /dev/null +++ b/wireless/ieee802154/libmac/ieee802154_getcoordeaddr.c @@ -0,0 +1,67 @@ +/**************************************************************************** + * apps/wireless/ieee802154/libmac/ieee802154_getcoordeaddr.c + * + * Copyright (C) 2017 Verge Inc. All rights reserved. + * Author: Anthony Merlino + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "wireless/ieee802154.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int ieee802154_getcoordeaddr(int fd, FAR uint8_t *eaddr) +{ + struct ieee802154_get_req_s req; + int ret; + + req.attr = IEEE802154_ATTR_MAC_COORD_EADDR; + ret = ieee802154_get_req(fd, &req); + + IEEE802154_EADDRCOPY(eaddr, req.attrval.mac.eaddr); + + return ret; +} diff --git a/wireless/ieee802154/libmac/ieee802154_getcoordsaddr.c b/wireless/ieee802154/libmac/ieee802154_getcoordsaddr.c new file mode 100644 index 000000000..81e4bce76 --- /dev/null +++ b/wireless/ieee802154/libmac/ieee802154_getcoordsaddr.c @@ -0,0 +1,66 @@ +/**************************************************************************** + * apps/wireless/ieee802154/libmac/ieee802154_getcoordsaddr.c + * + * Copyright (C) 2017 Verge Inc. All rights reserved. + * Author: Anthony Merlino + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "wireless/ieee802154.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int ieee802154_getcoordsaddr(int fd, FAR uint8_t *saddr) +{ + struct ieee802154_get_req_s req; + int ret; + + req.attr = IEEE802154_ATTR_MAC_COORD_SADDR; + ret = ieee802154_get_req(fd, &req); + + IEEE802154_SADDRCOPY(saddr, req.attrval.mac.saddr); + return ret; +}