VxWorks Telnet

VxWorks下Telnet客户端的C语言实现代码

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;
}

原文链接:VxWorks下Telnet客户端的C语言实现代码