550 lines
15 KiB
C
550 lines
15 KiB
C
#include <sys/types.h>
|
||
#include <sys/socket.h>
|
||
#include <netinet/in.h>
|
||
#include <arpa/inet.h>
|
||
#include <netdb.h>
|
||
#include <stdio.h>
|
||
#include <unistd.h>
|
||
#include <fcntl.h>
|
||
#include <errno.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <signal.h>
|
||
#include "telnet.h"
|
||
#define TELNET_PORT 223
|
||
#define RX_BUFFER_SIZE 1024
|
||
#define CMD_SIZE 1024
|
||
#define TEL_PROMPT "admin@telnet:~$ "
|
||
#define PASSWD "admin"
|
||
// extern void printp(char *format, ...);
|
||
// #define debug_print(fmt, ...) printp(fmt, ##__VA_ARGS__)
|
||
// #define debug_print(fmt...) printp(...)
|
||
#define debug_print(fmt, ...) printf(fmt, ##__VA_ARGS__)
|
||
|
||
typedef struct telnet_t
|
||
{
|
||
struct sockaddr_in server_addr;
|
||
int sock_server;
|
||
struct sockaddr_in client_addr;
|
||
int sock_client;
|
||
unsigned char rec_buff[RX_BUFFER_SIZE];
|
||
int rec_len;
|
||
unsigned char cmd_buff[CMD_SIZE];
|
||
int cmd_pos;
|
||
unsigned short win_width;
|
||
unsigned short win_height;
|
||
unsigned char cilent_type_name[10];
|
||
char cilent_type;
|
||
char *passwd;
|
||
char passwd_pos;
|
||
char *tel_prompt;
|
||
char init : 1;
|
||
char login_in : 1;
|
||
char tel_login_show : 1;
|
||
char f_echo : 1;
|
||
} telnet_t;
|
||
|
||
telnet_t telnet = {
|
||
.passwd = PASSWD,
|
||
.tel_prompt = TEL_PROMPT,
|
||
};
|
||
int telnet_init(telnet_t *tel, int port)
|
||
{
|
||
int telnet_addr_len = sizeof(struct sockaddr_in);
|
||
|
||
// server socket create
|
||
tel->sock_server = socket(AF_INET, SOCK_STREAM, 0);
|
||
if (tel->sock_server < 0)
|
||
{
|
||
debug_print("telnet: opening socket");
|
||
return -1;
|
||
}
|
||
// server socket set
|
||
int keepalive = 1;
|
||
if (setsockopt(tel->sock_server, SOL_SOCKET, SO_REUSEADDR, (void *)&keepalive, sizeof(keepalive)) < 0)
|
||
{
|
||
debug_print("telnet:set socket keepalive failed\n");
|
||
return -1;
|
||
}
|
||
// server socket bind and start listen
|
||
tel->server_addr.sin_family = AF_INET;
|
||
tel->server_addr.sin_port = htons(port);
|
||
tel->server_addr.sin_addr.s_addr = INADDR_ANY;
|
||
if (bind(tel->sock_server, (void *)&tel->server_addr, sizeof(tel->server_addr)))
|
||
{
|
||
debug_print("error: binding tcp socket ");
|
||
return -1;
|
||
}
|
||
if (listen(tel->sock_server, 1) == -1)
|
||
{
|
||
debug_print("error: listen");
|
||
return -1;
|
||
}
|
||
debug_print("telnet: telnet server init success\n");
|
||
tel->init = 1;
|
||
return 0;
|
||
}
|
||
|
||
#define send_will(buf, a) \
|
||
{ \
|
||
buf[0] = IAC; \
|
||
buf[1] = WILL; \
|
||
buf[2] = a; \
|
||
send(tel->sock_client, buf, 3, 0); \
|
||
};
|
||
#define send_wont(buf, a) \
|
||
{ \
|
||
buf[0] = IAC; \
|
||
buf[1] = WONT; \
|
||
buf[2] = a; \
|
||
send(tel->sock_client, buf, 3, 0); \
|
||
};
|
||
#define send_do(buf, a) \
|
||
{ \
|
||
buf[0] = IAC; \
|
||
buf[1] = DO; \
|
||
buf[2] = a; \
|
||
send(tel->sock_client, buf, 3, 0); \
|
||
};
|
||
#define send_dont(buf, a) \
|
||
{ \
|
||
buf[0] = IAC; \
|
||
buf[1] = DONT; \
|
||
buf[2] = a; \
|
||
send(tel->sock_client, buf, 3, 0); \
|
||
};
|
||
|
||
int read_terminal_type(telnet_t *tel)
|
||
{
|
||
unsigned char terminal[] = {IAC, SB, TELOPT_TTYPE, 1, IAC, SE};
|
||
send(tel->sock_client, terminal, sizeof(terminal), 0);
|
||
sleep(1);
|
||
tel->rec_len = recv(tel->sock_client, tel->rec_buff, RX_BUFFER_SIZE, 0);
|
||
if (tel->rec_len < 0)
|
||
{
|
||
debug_print("error \n");
|
||
return -1;
|
||
}
|
||
}
|
||
int telnet_config(telnet_t *tel)
|
||
{
|
||
if (tel->sock_client < 0)
|
||
{
|
||
return -1;
|
||
}
|
||
unsigned char cmd[3];
|
||
// send_dont(cmd, TELOPT_LINEMODE);
|
||
// send(tel->sock_client,"\033[7e",sizeof("\033[7e"),0);
|
||
// send_do(cmd, 24);
|
||
send_will(cmd, TELOPT_ECHO);
|
||
send_will(cmd, TELOPT_SGA);
|
||
send_wont(cmd, TELOPT_LINEMODE);
|
||
|
||
// unsigned char line_mode={IAC,SB,TELOPT_LINEMODE,1,0xf,IAC,SE};
|
||
// send(tel->sock_client,line_mode,sizeof(line_mode),0);
|
||
// 清理下之前的信息
|
||
// tel->rec_len = recv(tel->sock_client, tel->rec_buff, RX_BUFFER_SIZE, 0);
|
||
send_do(cmd, TELOPT_TTYPE);
|
||
// 获取下终端类型
|
||
unsigned char terminal[] = {IAC, SB, TELOPT_TTYPE, 1, IAC, SE};
|
||
send(tel->sock_client, terminal, sizeof(terminal), 0);
|
||
// send_wont(cmd, TELOPT_LINEMODE);
|
||
// send_dont(cmd, TELOPT_LINEMODE);
|
||
// send
|
||
// send
|
||
#if 0
|
||
/**
|
||
* 客户端会按照顺序进行回复
|
||
*/
|
||
unsigned char cmd[3];
|
||
send_do(cmd, 24);
|
||
send_do(cmd, 32);
|
||
send_do(cmd, 35);
|
||
send_do(cmd, 39);
|
||
tel->rec_len = recv(tel->sock_client, tel->rec_buff, RX_BUFFER_SIZE, 0);
|
||
tel->cilent_type = 0;
|
||
if (tel->rec_len < 0)
|
||
{
|
||
return -1;
|
||
}
|
||
else
|
||
{
|
||
if (tel->rec_len == 12)
|
||
{
|
||
int i = 0;
|
||
for (; i < 12; i++)
|
||
{
|
||
if (tel->rec_buff[i] == 32 && tel->rec_buff[i - 1] == WONT)
|
||
{
|
||
// busy client
|
||
tel->cilent_type = 1;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
tel->cilent_type = 2;
|
||
}
|
||
}
|
||
// cilent_type = 3;
|
||
switch (tel->cilent_type)
|
||
{
|
||
case 0:
|
||
|
||
send_will(cmd, 3);
|
||
send_do(cmd, 1);
|
||
send_do(cmd, 31);
|
||
send_will(cmd, 5);
|
||
send_do(cmd, 33);
|
||
tel->rec_len = recv(tel->sock_client, tel->rec_buff, RX_BUFFER_SIZE, 0);
|
||
send_will(cmd, 1);
|
||
break;
|
||
case 1:
|
||
send_will(cmd, 3);
|
||
send_do(cmd, 1);
|
||
send_do(cmd, 31);
|
||
send_will(cmd, 5);
|
||
send_do(cmd, 33);
|
||
tel->rec_len = recv(tel->sock_client, tel->rec_buff, RX_BUFFER_SIZE, 0);
|
||
send_will(cmd, 1);
|
||
break;
|
||
case 2:
|
||
send_do(cmd, 31);
|
||
send_will(cmd, 1);
|
||
send_do(cmd, 3);
|
||
send_will(cmd, 3);
|
||
tel->rec_len = recv(tel->sock_client, tel->rec_buff, RX_BUFFER_SIZE, 0);
|
||
send_do(cmd, 1);
|
||
send_do(cmd, 31);
|
||
send_will(cmd, 5);
|
||
send_do(cmd, 33);
|
||
break;
|
||
case 3:
|
||
send_do(cmd, 31);
|
||
send_will(cmd, 3);
|
||
send_do(cmd, 1);
|
||
send_do(cmd, 31);
|
||
send_will(cmd, 5);
|
||
send_do(cmd, 33);
|
||
send_dont(cmd, 1);
|
||
tel->rec_len = recv(tel->sock_client, tel->rec_buff, RX_BUFFER_SIZE, 0);
|
||
send_dont(cmd, 1);
|
||
tel->rec_len = recv(tel->sock_client, tel->rec_buff, RX_BUFFER_SIZE, 0);
|
||
send_will(cmd, 1);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
int telnet_cmd_handle(telnet_t *tel)
|
||
{
|
||
|
||
send(tel->sock_client, tel->cmd_buff, tel->cmd_pos, 0);
|
||
|
||
tel->cmd_pos = 0;
|
||
|
||
|
||
}
|
||
int telnet_shell_handle(telnet_t *tel, char c)
|
||
{
|
||
if (c == 13)
|
||
{
|
||
send(tel->sock_client, "\r\n", 2, 0);
|
||
telnet_cmd_handle(tel);
|
||
send(tel->sock_client, "\r\n", 2, 0);
|
||
// send(tel->sock_client,"123",3,0);
|
||
send(tel->sock_client, tel->tel_prompt, sizeof(TEL_PROMPT), 0);
|
||
// send(tel->sock_client, " ", 1, 0);
|
||
unsigned char clear_line[] = "\r\033[K";
|
||
send(tel->sock_client, clear_line, sizeof(clear_line), 0);
|
||
return;
|
||
}
|
||
else if (c == 0)
|
||
{
|
||
send(tel->sock_client, " ", 1, 0);
|
||
}
|
||
else if (c == 10)
|
||
{
|
||
send(tel->sock_client, " ", 1, 0);
|
||
}
|
||
else if (c == 9)
|
||
{
|
||
}
|
||
else
|
||
{
|
||
if (tel->cmd_pos < CMD_SIZE)
|
||
{
|
||
tel->cmd_buff[tel->cmd_pos++] = c;
|
||
send(tel->sock_client, &c, 1, 0);
|
||
send(tel->sock_client, " ", 1, 0);
|
||
}
|
||
else
|
||
{
|
||
send(tel->sock_client, " ", 2, 0);
|
||
}
|
||
}
|
||
}
|
||
int telnet_nor_handle(telnet_t *tel, char c)
|
||
{
|
||
if (tel->f_echo == 0)
|
||
{
|
||
char cmd[3] = {0x08, ' ', 0x08};
|
||
send(tel->sock_client, cmd, 3, 0);
|
||
}
|
||
if (tel->login_in == 0)
|
||
{
|
||
if (tel->tel_login_show == 0)
|
||
{
|
||
char cmd[] = "\033[?25l";
|
||
send(tel->sock_client, cmd, sizeof(cmd), 0);
|
||
tel->tel_login_show = 1;
|
||
}
|
||
// 无论如何回复一个空格
|
||
send(tel->sock_client, " ", 1, 0);
|
||
if (tel->passwd[tel->passwd_pos] == c)
|
||
{
|
||
tel->passwd_pos++;
|
||
if (tel->passwd[tel->passwd_pos] == '\0')
|
||
{
|
||
tel->login_in = 1;
|
||
tel->passwd_pos = 0;
|
||
send(tel->sock_client, "\r\nlogin success\r\n", 17, 0);
|
||
send(tel->sock_client, tel->tel_prompt, sizeof(TEL_PROMPT), 0);
|
||
if (tel->tel_login_show == 1)
|
||
{
|
||
char cmd[] = "\033[?25h";
|
||
send(tel->sock_client, cmd, sizeof(cmd), 0);
|
||
tel->tel_login_show = 0;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
tel->passwd_pos = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// telnet_shell_handle(tel, c);
|
||
// char see = 0;
|
||
telnet_shell_handle(tel, c);
|
||
}
|
||
}
|
||
int telnet_rec_handle(telnet_t *tel, unsigned char *c, int len)
|
||
{
|
||
#define MOD_NORMAL 0
|
||
#define MOD_IAC 1
|
||
#define MOD_WILL 2
|
||
#define MOD_WONT 3
|
||
#define MOD_DO 4
|
||
#define MOD_DONT 5
|
||
#define MOD_SB 6
|
||
int state = MOD_NORMAL;
|
||
for (int i = 0; i < len; i++)
|
||
{
|
||
switch (state)
|
||
{
|
||
case MOD_NORMAL:
|
||
if (c[i] == IAC)
|
||
{
|
||
state = MOD_IAC;
|
||
}
|
||
else
|
||
{
|
||
telnet_nor_handle(tel, c[i]);
|
||
}
|
||
break;
|
||
case MOD_IAC:
|
||
if (c[i] == IAC)
|
||
{
|
||
state = MOD_NORMAL;
|
||
// 2个IAC代表字符255,也需要进行处理
|
||
telnet_nor_handle(tel, c[i]);
|
||
}
|
||
else if (c[i] == DO)
|
||
{
|
||
state = MOD_DO;
|
||
}
|
||
else if (c[i] == DONT)
|
||
{
|
||
state = MOD_DONT;
|
||
}
|
||
else if (c[i] == WILL)
|
||
{
|
||
state = MOD_WILL;
|
||
}
|
||
else if (c[i] == WONT)
|
||
{
|
||
state = MOD_WONT;
|
||
}
|
||
else if (c[i] == SB)
|
||
{
|
||
state = MOD_SB;
|
||
}
|
||
else
|
||
{
|
||
state = MOD_NORMAL;
|
||
telnet_nor_handle(tel, c[i]);
|
||
}
|
||
break;
|
||
case MOD_WILL:
|
||
debug_print("telnet: WILL %d\n", c[i]);
|
||
if (c[i] == TELOPT_NAWS)
|
||
{
|
||
if (c[i + 1] == IAC && c[i + 2] == SB && c[i + 3] == TELOPT_NAWS && c[i + 8] == IAC && c[i + 9] == SE)
|
||
{
|
||
tel->win_width = ((unsigned short)c[i + 4] << 8) + (unsigned short)c[i + 5];
|
||
tel->win_height = ((unsigned short)c[i + 6] << 8) + (unsigned short)c[i + 7];
|
||
i = i + 9;
|
||
debug_print("telnet: win_width = %d, win_height = %d\n", tel->win_width, tel->win_height);
|
||
}
|
||
}
|
||
state = MOD_NORMAL;
|
||
break;
|
||
case MOD_WONT:
|
||
debug_print("telnet: WONT %d\n", c[i]);
|
||
state = MOD_NORMAL;
|
||
break;
|
||
case MOD_DO:
|
||
|
||
debug_print("telnet: DO %d\n", c[i]);
|
||
state = MOD_NORMAL;
|
||
break;
|
||
case MOD_DONT:
|
||
if (c[i] == TELOPT_ECHO)
|
||
{
|
||
tel->f_echo = 0;
|
||
}
|
||
debug_print("telnet: DONT %d\n", c[i]);
|
||
state = MOD_NORMAL;
|
||
break;
|
||
case MOD_SB:
|
||
if (c[i] == TELOPT_TTYPE && c[i + 1] == 0)
|
||
{
|
||
i = i + 2;
|
||
int k = 0;
|
||
// 客户端类型
|
||
while (1)
|
||
{
|
||
|
||
if ((c[i] == IAC && c[i + 1] == SE) || i == len)
|
||
{
|
||
tel->cilent_type_name[k] = '\0';
|
||
printf("telnet: client type = %s\n", tel->cilent_type_name);
|
||
break;
|
||
}
|
||
if (i == len)
|
||
{
|
||
break;
|
||
}
|
||
tel->cilent_type_name[k] = c[i];
|
||
i++;
|
||
k++;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
int telnet_core(telnet_t *tel)
|
||
{
|
||
while (1)
|
||
{
|
||
if (tel->init == 0)
|
||
{
|
||
sleep(1);
|
||
continue;
|
||
}
|
||
debug_print("\ntelnet: waiting for connection\n");
|
||
while (1)
|
||
{
|
||
int addr_len = sizeof(tel->client_addr);
|
||
tel->sock_client = accept(tel->sock_server, (void *)&tel->client_addr, &addr_len);
|
||
if (tel->sock_client < 0)
|
||
{
|
||
sleep(1);
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
debug_print("telnet: new telnet client(%s:%d) connection, switch console to telnet...\n", inet_ntoa(tel->client_addr.sin_addr), tel->client_addr.sin_port);
|
||
int ret = telnet_config(tel);
|
||
if (ret < 0)
|
||
{
|
||
debug_print("telnet: client disconnected\n");
|
||
close(tel->sock_client);
|
||
continue;
|
||
}
|
||
if (tel->login_in == 0)
|
||
{
|
||
|
||
send(tel->sock_client, "admin login: \r\n", 15, 0);
|
||
}
|
||
while (1)
|
||
{
|
||
// telnet_host_function();
|
||
tel->rec_len = recv(tel->sock_client, tel->rec_buff, RX_BUFFER_SIZE, 0);
|
||
// if (tel->rec_buff[0] == IAC && tel->rec_buff[1] != IAC)
|
||
// {
|
||
// telnet_config_back(tel);
|
||
// }
|
||
if (tel->rec_len > 0)
|
||
{
|
||
debug_print("telnet: recv_len = %d\n", tel->rec_len);
|
||
for (int i = 0; i < tel->rec_len; i++)
|
||
{
|
||
switch (tel->rec_buff[i])
|
||
{
|
||
case IAC:
|
||
debug_print("IAC,");
|
||
break;
|
||
case DO:
|
||
debug_print("DO,");
|
||
break;
|
||
case DONT:
|
||
debug_print("DONT,");
|
||
break;
|
||
case WILL:
|
||
debug_print("WILL,");
|
||
break;
|
||
case WONT:
|
||
debug_print("WONT,");
|
||
break;
|
||
case SB:
|
||
debug_print("SB,");
|
||
break;
|
||
case SE:
|
||
debug_print("SE,");
|
||
break;
|
||
default:
|
||
debug_print("%d,", tel->rec_buff[i]);
|
||
break;
|
||
}
|
||
}
|
||
debug_print("\n");
|
||
telnet_rec_handle(tel, tel->rec_buff, tel->rec_len);
|
||
}
|
||
else
|
||
{
|
||
debug_print("telnet: client disconnected\n");
|
||
tel->login_in = 0;
|
||
close(tel->sock_client);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
int main()
|
||
{
|
||
telnet_init(&telnet, TELNET_PORT);
|
||
telnet_core(&telnet);
|
||
return 0;
|
||
}
|
||
|