Improvements for telnet server
Author: Xiang Xiao <xiaoxiang@xiaomi.com> Check POLLHUP and POLLERR in telnet poll loop to handle the remote end close correctly Send "NVT"(network virtual terminal) as the default if getenv("TERM") return NULL telnet should trigger the error handling if inet_pton return zero since zero mean the string has format error Don't return 1 in _environ_telnet to avoid trigger the compression and remove the redundant TELNET_TELOPT_COMPRESS2 check Change telnet_error_u to telnet_error_e required by the coding standard Ensure telnet object get freed before the abnormal exit
This commit is contained in:
parent
43a36996ca
commit
bf3e6e2367
@ -1,5 +1,5 @@
|
||||
/****************************************************************************
|
||||
* apps/netutils/telnetc/telnetc.c
|
||||
* apps/include/netutils/telnetc.h
|
||||
*
|
||||
* Leveraged from libtelnet, https://github.com/seanmiddleditch/libtelnet.
|
||||
* Modified and re-released under the BSD license:
|
||||
@ -208,7 +208,7 @@ extern "C"
|
||||
|
||||
/* Error codes */
|
||||
|
||||
enum telnet_error_u
|
||||
enum telnet_error_e
|
||||
{
|
||||
TELNET_EOK = 0, /* No error */
|
||||
TELNET_EBADVAL, /* Invalid parameter, or API misuse */
|
||||
@ -284,7 +284,7 @@ union telnet_event_u
|
||||
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 */
|
||||
enum telnet_error_e errcode; /* Error code */
|
||||
} error;
|
||||
|
||||
/* Command event: for IAC */
|
||||
|
@ -197,8 +197,8 @@ static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
|
||||
|
||||
/* Error generation function */
|
||||
|
||||
static enum telnet_error_u _error(struct telnet_s *telnet, unsigned line,
|
||||
const char *func, enum telnet_error_u err,
|
||||
static enum telnet_error_e _error(struct telnet_s *telnet, unsigned line,
|
||||
const char *func, enum telnet_error_e err,
|
||||
int fatal, const char *fmt, ...)
|
||||
{
|
||||
union telnet_event_u ev;
|
||||
@ -230,7 +230,7 @@ static enum telnet_error_u _error(struct telnet_s *telnet, unsigned line,
|
||||
*/
|
||||
|
||||
#if defined(HAVE_ZLIB)
|
||||
enum telnet_error_u _init_zlib(struct telnet_s *telnet, int deflate,
|
||||
enum telnet_error_e _init_zlib(struct telnet_s *telnet, int deflate,
|
||||
int err_fatal)
|
||||
{
|
||||
z_stream *z;
|
||||
@ -361,7 +361,7 @@ static inline int _check_telopt(struct telnet_s *telnet,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Loop unti found or end marker (us and him both 0) */
|
||||
/* Loop until found or end marker (us and him both 0) */
|
||||
|
||||
for (i = 0; telnet->telopts[i].telopt != -1; ++i)
|
||||
{
|
||||
@ -441,7 +441,7 @@ static inline void _set_rfc1143(struct telnet_s *telnet, unsigned char telopt,
|
||||
|
||||
qtmp = (struct telnet_rfc1143_s *)
|
||||
realloc(telnet->q,
|
||||
sizeof(struct telnet_rfc1143_s) * (telnet->q_size + 4));
|
||||
sizeof(struct telnet_rfc1143_s) * (telnet->q_size + 4));
|
||||
if (qtmp == 0)
|
||||
{
|
||||
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
|
||||
@ -713,7 +713,7 @@ static int _environ_telnet(struct telnet_s *telnet, unsigned char type,
|
||||
|
||||
ev.type = TELNET_EV_ENVIRON;
|
||||
telnet->eh(telnet, &ev, telnet->ud);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Every second byte must be VAR or USERVAR, if present */
|
||||
@ -854,7 +854,7 @@ static int _environ_telnet(struct telnet_s *telnet, unsigned char type,
|
||||
/* Clean up */
|
||||
|
||||
free(values);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Process an MSSP subnegotiation buffer */
|
||||
@ -1104,21 +1104,17 @@ static int _subnegotiate(struct telnet_s *telnet)
|
||||
*/
|
||||
|
||||
case TELNET_TELOPT_COMPRESS2:
|
||||
if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2)
|
||||
if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
#endif /* HAVE_ZLIB */
|
||||
|
||||
/* Specially handled subnegotiation telopt types */
|
||||
@ -1248,7 +1244,7 @@ void telnet_free(struct telnet_s *telnet)
|
||||
|
||||
/* Push a byte into the telnet buffer */
|
||||
|
||||
static enum telnet_error_u _buffer_byte(struct telnet_s *telnet,
|
||||
static enum telnet_error_e _buffer_byte(struct telnet_s *telnet,
|
||||
unsigned char byte)
|
||||
{
|
||||
char *new_buffer;
|
||||
@ -2283,6 +2279,11 @@ void telnet_ttype_is(struct telnet_s *telnet, const char *ttype)
|
||||
TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_IS
|
||||
};
|
||||
|
||||
if (!ttype)
|
||||
{
|
||||
ttype = "NVT";
|
||||
}
|
||||
|
||||
_sendu(telnet, IS, sizeof(IS));
|
||||
_send(telnet, ttype, strlen(ttype));
|
||||
telnet_finish_sb(telnet);
|
||||
|
@ -1,5 +1,5 @@
|
||||
############################################################################
|
||||
# apps/system/usbmsc/Make.defs
|
||||
# apps/system/telnet/Make.defs
|
||||
# Adds selected applications to apps/ build
|
||||
#
|
||||
# Copyright (C) 2016 Gregory Nutt. All rights reserved.
|
||||
|
@ -107,6 +107,23 @@ static struct user_s g_users[MAX_USERS];
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static void cleanup_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i != MAX_USERS; ++i)
|
||||
{
|
||||
if (g_users[i].sock != -1)
|
||||
{
|
||||
close(g_users[i].sock);
|
||||
free(g_users[i].name);
|
||||
telnet_free(g_users[i].telnet);
|
||||
}
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void linebuffer_push(char *buffer, size_t size, int *linepos,
|
||||
char ch, void (*cb) (const char *line, int overflow,
|
||||
void *ud), void *ud)
|
||||
@ -180,7 +197,7 @@ static void _send(int sock, const char *buffer, unsigned int size)
|
||||
if (errno != EINTR && errno != ECONNRESET)
|
||||
{
|
||||
fprintf(stderr, "send() failed: %d\n", errno);
|
||||
exit(1);
|
||||
cleanup_exit();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -190,7 +207,7 @@ static void _send(int sock, const char *buffer, unsigned int size)
|
||||
else if (ret == 0)
|
||||
{
|
||||
fprintf(stderr, "send() unexpectedly returned 0\n");
|
||||
exit(1);
|
||||
cleanup_exit();
|
||||
}
|
||||
|
||||
/* Update pointer and size to see if we've got more to send */
|
||||
@ -246,6 +263,7 @@ static void _online(const char *line, int overflow, void *ud)
|
||||
_message(user->name, "** HAS QUIT **");
|
||||
free(user->name);
|
||||
user->name = 0;
|
||||
telnet_free(user->telnet);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -258,7 +276,7 @@ static void _input(struct user_s *user, const char *buffer, unsigned int size)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i != size; ++i)
|
||||
for (i = 0; user->sock != -1 && i != size; ++i)
|
||||
{
|
||||
linebuffer_push(user->linebuf, sizeof(user->linebuf), &user->linepos,
|
||||
(char)buffer[i], _online, user);
|
||||
@ -420,12 +438,12 @@ int main(int argc, FAR char *argv[])
|
||||
if (ret == -1 && errno != EINTR)
|
||||
{
|
||||
fprintf(stderr, "poll() failed: %d\n", errno);
|
||||
return 1;
|
||||
cleanup_exit();
|
||||
}
|
||||
|
||||
/* New connection */
|
||||
|
||||
if (pfd[MAX_USERS].revents & POLLIN)
|
||||
if (pfd[MAX_USERS].revents & (POLLIN | POLLERR | POLLHUP))
|
||||
{
|
||||
/* Accept the sock */
|
||||
|
||||
@ -434,7 +452,7 @@ int main(int argc, FAR char *argv[])
|
||||
&addrlen)) == -1)
|
||||
{
|
||||
fprintf(stderr, "accept() failed: %d\n", errno);
|
||||
return 1;
|
||||
cleanup_exit();
|
||||
}
|
||||
|
||||
printf("Connection received.\n");
|
||||
@ -478,7 +496,7 @@ int main(int argc, FAR char *argv[])
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pfd[i].revents & POLLIN)
|
||||
if (pfd[i].revents & (POLLIN | POLLERR | POLLHUP))
|
||||
{
|
||||
if ((ret = recv(g_users[i].sock, buffer, sizeof(buffer), 0)) > 0)
|
||||
{
|
||||
@ -488,6 +506,7 @@ int main(int argc, FAR char *argv[])
|
||||
{
|
||||
printf("Connection closed.\n");
|
||||
close(g_users[i].sock);
|
||||
g_users[i].sock = -1;
|
||||
if (g_users[i].name != 0)
|
||||
{
|
||||
_message(g_users[i].name, "** HAS DISCONNECTED **");
|
||||
@ -496,13 +515,11 @@ int main(int argc, FAR char *argv[])
|
||||
}
|
||||
|
||||
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);
|
||||
cleanup_exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,14 +156,17 @@ static void telnet_ev_send(int sock, const char *buffer, size_t size)
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
if ((ret = send(sock, buffer, size, 0)) == -1)
|
||||
if ((ret = send(sock, buffer, size, 0)) <= 0)
|
||||
{
|
||||
fprintf(stderr, "send() failed: %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
else if (ret == 0)
|
||||
{
|
||||
fprintf(stderr, "send() unexpectedly returned 0\n");
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "send() failed: %d\n", errno);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "send() unexpectedly returned 0\n");
|
||||
}
|
||||
telnet_free(g_telnet);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -245,6 +248,7 @@ static void _event_handler(struct telnet_s *telnet,
|
||||
|
||||
case TELNET_EV_ERROR:
|
||||
fprintf(stderr, "ERROR: %s\n", ev->error.msg);
|
||||
telnet_free(g_telnet);
|
||||
exit(1);
|
||||
|
||||
default:
|
||||
@ -264,7 +268,7 @@ static void show_usage(const char *progname, int exitcode)
|
||||
fprintf(stderr, "\t\tIPv6 form: xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx\n");
|
||||
fprintf(stderr, "\t<port> is the (optional) listening port of the Telnet server.\n");
|
||||
fprintf(stderr, "\t\tDefault: %u\n", DEFAULT_PORT);
|
||||
exit(exitcode) ;
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -337,7 +341,7 @@ int main(int argc, FAR char *argv[])
|
||||
server.ipv6.sin6_port = htons(portno);
|
||||
|
||||
ret = inet_pton(AF_INET6, argv[1], server.ipv6.sin6_addr.s6_addr);
|
||||
if (ret < 0)
|
||||
if (ret <= 0)
|
||||
#endif
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
{
|
||||
@ -351,7 +355,7 @@ int main(int argc, FAR char *argv[])
|
||||
ret = inet_pton(AF_INET, argv[1], &server.ipv4.sin_addr);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
if (ret <= 0)
|
||||
#endif
|
||||
{
|
||||
fprintf(stderr, "ERROR: <server-IP-addr> is invalid\n");
|
||||
@ -407,40 +411,40 @@ int main(int argc, FAR char *argv[])
|
||||
{
|
||||
/* Read from stdin */
|
||||
|
||||
if (pfd[0].revents & POLLIN)
|
||||
if (pfd[0].revents & (POLLIN | POLLERR | POLLHUP))
|
||||
{
|
||||
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);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "recv(server) failed: %d\n", errno);
|
||||
ret = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read from client */
|
||||
|
||||
if (pfd[1].revents & POLLIN)
|
||||
if (pfd[1].revents & (POLLIN | POLLERR | POLLHUP))
|
||||
{
|
||||
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);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "recv(client) failed: %d\n", errno);
|
||||
ret = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -449,5 +453,5 @@ int main(int argc, FAR char *argv[])
|
||||
|
||||
telnet_free(g_telnet);
|
||||
close(sock);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user