Telnet Client连接过程:
- TCP连接telnet server 23端口
- NVT扩展自动协商
- 用户名+密码登录
- 通过telnet server端命令行交互
- 退出登录
概述
Telnet 协议是 TCP/IP 协议族中应用最广泛的协议。
它允许用户(Telnet 客户端)通过一个协商过程来与一个远程设备进行通信。
Telnet 协议是基于网络虚拟终端 NVT(Network Virtual Termina1)的实现,NVT 是虚拟设备,连接双方(客户机和服务器)都必须把它们的物理终端和 NVT 进行相互转换。
操作协商
只要客户机或服务器要发送命令序列而不是数据流,它就在数据流中插入一个特殊的保留字符,该保留字符叫做“解释为命令”(IAC ,Interpret As Command) 字符。当接收方在一个入数据流中发现 IAC 字符时,它就把后继的字节处理为一个命令序列。
双方在进行Telnet连接时,要进行选项协商。
比如:使用字符方式、窗口的大小,终端的类型都要进行协商。而协商是通过TELNET所支持的命令来实现的。
协商完成,telnet server才返回登录信息,否则无法登录。
本文协商过程通过程序的一个函数实现自动化。
VxWorks下telnet client实现代码
#include "string.h"
#include "stdio.h"
#include "vxWorks.h"
#include "sockLib.h"
#include "telnetLib.h"
#include "inetLib.h"
#include "stdioLib.h"
#include "strLib.h"
#include "hostLib.h"
#include "ioLib.h"
// telnet server 地址及端口
#define TELNET_SERVER_NAME "172.18.101.193"
#define TELNET_SERVER_PORT 23
// telnet NVT扩展选项协商,常用命令定义,命令与数值的对应关系
#define DO '\xFD'
#define WONT '\xFC'
#define WILL '\xFB'
#define DONT '\xFE'
#define IAC '\xFF'
#define SB '\xFA'
#define SE '\xF0'
#define CMD_WINDOW_SIZE 31
// 定义缓冲buffer长度,最大message长度,字符串最大长度
#define BUFFER_SIZE 20480
#define MAX_MSG_LENGTH 20480
#define STRING_MAX_SIZE 20480
// 全局变量
char go_string[STRING_MAX_SIZE];
char dealed_string[STRING_MAX_SIZE];
char back_string[STRING_MAX_SIZE];
// TCP client 主动连接:输入TCP服务端地址和端口,返回client_socket
int tcp_client_conn(char *server_name, int port)
{
struct sockaddr_in server_addr;
int client_socket;
/*创建套接字*/
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket < 0)
{
printf("Create Socket Failed! \n");
return -1;
}
// 填充server地址信息
bzero((char *)&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
if (((server_addr.sin_addr.s_addr = inet_addr(server_name)) == ERROR) &&
((server_addr.sin_addr.s_addr = hostGetByName(server_name)) == ERROR))
{
printf("Get Server Address Error! \n");
return -1;
}
server_addr.sin_port = htons(port);
server_addr.sin_len = sizeof(server_addr);
/* connect to server */
if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == ERROR)
{
printf("Connet to Server Error! \n");
return -1;
}
return client_socket;
}
// 连接telnet server,自动协商过程
void negotiate(int client_socket)
{
char buffer[BUFFER_SIZE];
int count = 0;
while (1)
{
bzero(buffer, BUFFER_SIZE);
// 读一个字节
if (recv(client_socket, buffer, 1, 0) < 0)
{
printf("Receive Data Failed! \n");
return;
}
if (buffer[0] == IAC)
{
// read 2 more bytes
if (recv(client_socket, buffer + 1, 2, 0) < 0)
{
printf("Receive Data Failed! \n");
return;
}
// TAC协商窗口大小
if (buffer[1] == DO && buffer[2] == CMD_WINDOW_SIZE)
{
char tmp1[10] = {IAC, WILL, CMD_WINDOW_SIZE};
if (send(client_socket, tmp1, 3, 0) < 0)
{
printf("Send Data Failed! \n");
return;
}
char tmp2[10] = {IAC, SB, CMD_WINDOW_SIZE, 0, 80, 0, 24, IAC, SE};
if (send(client_socket, tmp2, 9, 0) < 0)
{
printf("Send Data Failed! \n");
return;
}
continue;
}
// 处理一个IAC协商命令
int i;
for (i = 0; i < 3; i++)
{
if (buffer[i] == DO)
{
buffer[i] = WONT;
}
else if (buffer[i] == WILL)
{
buffer[i] = DO;
}
}
if (send(client_socket, buffer, 3, 0) < 0)
{
printf("Send Data Failed! \n");
return;
}
}
//结束循环条件,IAC协商结束
if (buffer[0] == '\n')
{
count += 1;
}
if (count == 2)
{
break;
}
}
}
// TCP client 接收服务端返回的数据
void tcp_client_receive_data(int client_socket)
{
char receive_buffer[BUFFER_SIZE];
int length;
// 接受服务器返回的状态信息
bzero(receive_buffer, BUFFER_SIZE);
if ((length = recv(client_socket, receive_buffer, MAX_MSG_LENGTH, 0)) == ERROR)
{
printf("Client Recieve Data Failed! \n");
return;
}
bzero(back_string, STRING_MAX_SIZE);
strncpy(back_string, receive_buffer, (strlen(receive_buffer) > STRING_MAX_SIZE ? STRING_MAX_SIZE : strlen(receive_buffer)));
printf("receive data from equipment: %s \n", back_string);
}
// TCP client 发送处理后的数据到目标TCP
void tcp_client_send_data(int client_socket)
{
char send_buffer[BUFFER_SIZE];
/* send request to server */
bzero(send_buffer, BUFFER_SIZE);
strncpy(send_buffer, dealed_string, strlen(dealed_string));
// buffer发送长度+1,目的带上'\0'结束符,strlen()函数读取到'\0'结束
int ret = send(client_socket, send_buffer, (strlen(send_buffer) + 1), 0);
if (ret == ERROR)
{
printf("Send Data to Server Error! \n");
return;
}
printf("send data to equipment: %s \n", send_buffer);
}
// telnet操作,命令数据处理
void telnet_deal_data(char *go_string, char *dealed_string)
{
bzero(dealed_string, STRING_MAX_SIZE);
strncpy(dealed_string, go_string, (strlen(go_string) > STRING_MAX_SIZE ? STRING_MAX_SIZE : strlen(go_string)));
// 命令结尾增加\n
dealed_string[strlen(dealed_string)] = '\n';
}
// telnet登录,输入用户名,接收password
void telnet_login_receive_password(int telnet_socket)
{
// recv当次接收的数据长度
int filled = 0;
// recv接收的全部数据的长度
int length = 0;
char buffer[BUFFER_SIZE];
bzero(buffer, BUFFER_SIZE);
// telnet登录:输入用户名后,接收全部返回数据
// 网络环境不同,执行情况不同,全部数据有可能一次接收到,也有可能两次才能全部接收到
while (1)
{
if ((filled = recv(telnet_socket, buffer + length, MAX_MSG_LENGTH, 0)) == ERROR)
{
printf("Accept Data Failed! \n");
return;
}
length += filled;
// 判断是否全部接收完返回数据
// 接收完全部数据,结束循环,即结束接收数据
if (buffer[length - 2 < 0 ? 0 : length - 2] == ':')
{
break;
}
}
bzero(back_string, STRING_MAX_SIZE);
strncpy(back_string, buffer, (strlen(buffer) > STRING_MAX_SIZE ? STRING_MAX_SIZE : strlen(buffer)));
printf("receive data from equipment: %s \n", buffer);
}
// telnet 登录成功,接收telnet server返回数据
// 环境不同,执行情况也会不同,全部数据可能一次接收完,也可能两次接收完,或者三次接收完
int telnet_login_whether_success_receive_data(int telnet_socket)
{
// 登录成功接收三次数据,telnet server连续三次send
// recv单次接收字节流长度filled
int filled = 0;
// recv接收的全部数据的长度length
int length = 0;
// telnet server 是否登录成功
int whether;
char buffer[BUFFER_SIZE];
bzero(buffer, BUFFER_SIZE);
while (1)
{
if ((filled = recv(telnet_socket, buffer + length, MAX_MSG_LENGTH, 0)) == ERROR)
{
printf("Accept Data Failed! \n");
return -1;
}
length += filled;
// 判断是否全部接收完返回数据
// 数据结尾部分:[xxxx@localhost ~]$
if (buffer[length - 3 < 0 ? 0 : length - 3] == ']')
{
// 接收完全部数据,结束接收
// 收到登录成功的全部数据,whether=1
whether = 1;
break;
}
if (buffer[length - 2 < 0 ? 0 : length - 2] == ':')
{
// 接收完全部数据,结束接收
// 收到登录失败的全部数据,whether=0
whether = 0;
break;
}
// 只接收最后一次send的全部数据
// bzero(buffer, BUFFER_SIZE);
}
bzero(back_string, STRING_MAX_SIZE);
strncpy(back_string, buffer, (strlen(buffer) > STRING_MAX_SIZE ? STRING_MAX_SIZE : strlen(buffer)));
printf("receive data from equipment: %s \n", buffer);
return whether;
}
// telnet发送命令到telnet server,命令结尾必须带'\n'
void send_cmd(int telnet_socket, char *cmd)
{
char buffer[BUFFER_SIZE];
bzero(buffer, BUFFER_SIZE);
strncpy(buffer, cmd, (strlen(cmd) > BUFFER_SIZE ? BUFFER_SIZE : strlen(cmd)));
if (send(telnet_socket, buffer, (strlen(buffer) + 1), 0) < 0)
{
printf("Send cmd Failed! \n");
return;
}
}
// telnet login 登录到server
void telnet_login(int telnet_socket)
{
// 是否登录成功,用户名和密码是否正确
int whether;
// localhost login:
tcp_client_receive_data(telnet_socket);
while (1)
{
// 控制端的用户名
strcpy(go_string, "username");
telnet_deal_data(go_string, dealed_string);
tcp_client_send_data(telnet_socket);
// calos@Password:
telnet_login_receive_password(telnet_socket);
// 控制端的密码
strcpy(go_string, "password");
telnet_deal_data(go_string, dealed_string);
tcp_client_send_data(telnet_socket);
// 判断是否登录成功
whether = telnet_login_whether_success_receive_data(telnet_socket);
if (whether)
{
// 登录成功 [xxxx@localhost ~]$
break;
}
// 登录失败,继续登录
}
}
// telnet返回数据处理,telnet server有时候一次返回全部数据,有时候两次甚至多次返回全部数据
void telnet_receive_data_add_deal(int telnet_socket)
{
char receive_buffer[BUFFER_SIZE];
// 做数据处理的temp_buffer
char temp_buffer[BUFFER_SIZE];
// 第一次出现某字符的位置location=&receive_buffer+字符的偏移地址
char *location;
// 第一次出现某字符的偏移地址
int location_number;
// 当次recv数据的长度
int filled = 0;
// recv全部数据的长度
int length = 0;
// 循环接收全部的返回数据
bzero(receive_buffer, BUFFER_SIZE);
while (1)
{
// 接受telnet server返回数据
if ((filled = recv(telnet_socket, receive_buffer + length, MAX_MSG_LENGTH, 0)) == ERROR)
{
printf("Client Recieve Data Failed! \n");
return;
}
length += filled;
// 判断telnet server返回数据是否全部收到
if (receive_buffer[length - 3 < 0 ? 0 : length - 3] == ']')
{
break;
}
}
// 只取第二次返回数据,针对一次性接收完数据的,需要去除第一次send的数据
// 第一次send的数据:xxxx\r\n
location = strchr(receive_buffer, '\n');
// 计算偏移地址
location_number = location - receive_buffer + 1;
bzero(temp_buffer, BUFFER_SIZE);
strncpy(temp_buffer, receive_buffer + location_number, BUFFER_SIZE);
bzero(receive_buffer, BUFFER_SIZE);
strncpy(receive_buffer, temp_buffer, BUFFER_SIZE);
bzero(back_string, STRING_MAX_SIZE);
strncpy(back_string, receive_buffer, (strlen(receive_buffer) > STRING_MAX_SIZE ? STRING_MAX_SIZE : strlen(receive_buffer)));
printf("receive data from equipment: %s \n", back_string);
}
// 当前设备操作结束条件:控制端输入END,结束当前设备操作
int finish_equipment_operation(int sock)
{
char buffer[BUFFER_SIZE];
bzero(buffer, BUFFER_SIZE);
strncpy(buffer, go_string, (strlen(go_string) > BUFFER_SIZE ? BUFFER_SIZE : strlen(go_string)));
if (strcmp(buffer, "END") == 0)
{
close(sock);
return 1;
}
else
{
return 0;
}
}
int main(void)
{
int telnet_socket = tcp_client_conn(TELNET_SERVER_NAME, TELNET_SERVER_PORT);
if (telnet_socket < 0)
{
return -1;
}
// telnet 自动协商
negotiate(telnet_socket);
// 登录
telnet_login(telnet_socket);
//命令回显
while (1)
{
// 输入需要执行的命令到go_string
strcpy(go_string, "ll");
telnet_deal_data(go_string, dealed_string);
send_cmd(telnet_socket, dealed_string);
telnet_receive_data_add_deal(telnet_socket);
// 设备操作结束条件
if (finish_equipment_operation(telnet_socket))
{
break;
}
}
// 异常退出,关闭socket
close(telnet_socket);
return 0;
}