欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

模拟telnet协议C语言客户端程序

程序员文章站 2022-05-09 11:31:25
首先要了解telnet协议,一下两篇blog给了我初步的思路 https://www.cnblogs.com/liang ling/p/5833489.html 这篇有比较基础的介绍 以及IAC命令含义解释 https://www.cnblogs.com/image eye/archive/2012 ......

首先要了解telnet协议,一下两篇blog给了我初步的思路
https://www.cnblogs.com/liang-ling/p/5833489.html 这篇有比较基础的介绍 以及iac命令含义解释
https://www.cnblogs.com/image-eye/archive/2012/03/28/2421726.html 这篇就很能抓住重点 另外这位博主使用的是c# 写的程序十分完整 我这里关于iac指令处理有部分是直接借鉴他的程序 因此注释都没有修改仍然使用的是他的注释

以上两篇大致看过之后 就可以了解本次po出来的程序了 内容较为朴素

utils.h

#ifndef __utils__h
#define __utils__h

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>
#include <sys/poll.h>
#include <signal.h>
#include <sys/wait.h>

typedef signed long int ssize_t;
typedef unsigned long int size_t;

ssize_t readn(int fd, void *vptr,size_t n);
ssize_t writen(int fd, const void *vptr, size_t n);
ssize_t readline(int fd,void *vptr,size_t maxlen);

#endif

utils.c

#include"utils.h"


ssize_t readn(int fd, void *vptr,size_t n)
{
    size_t nleft;
    ssize_t nread;
    char *ptr;
    
    ptr = vptr;
    nleft = n;
    while(nleft >0)
    {
        if((nread = read(fd,ptr,nleft))<0)
        {
            if(errno == eintr)//error:为eagain,表示在非阻塞下,此时无数据到达,立即返回。
                nread = 0;            //error:为eintr,表示被信号中断了。
            else
                return (-1);
        }
        else if(nread == 0)
            break;
        else 
            /* do nothing */
        nleft -= nread;
        ptr += nread;
    }
    return n-nleft;//实际读了多少字节
}

ssize_t writen(int fd, const void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;
    
    ptr = vptr;
    nleft = n;
    while(nleft > 0)
    {
        if((nwritten = write(fd,ptr,nleft)) < 0)
        {
            if(nwritten <0 &&  errno == eintr)
            {
                nwritten = 0;
            }
            else
                return (-1);
        }
        else if(nwritten == 0)   
            break;
        else //nwritten > 0
        {
            /*do nothing*/
        }
        nleft = nleft - nwritten;
        ptr = ptr + nwritten;
    }
    return (n- nleft);//实际写了多少字节
}

ssize_t readline(int fd,void *vptr,size_t maxlen)
{
    ssize_t n =0,rc;
    char c,*ptr;
    
    ptr = vptr;
    while(1)
    {
        if((rc = read(fd,&c,1)) == 1)
        {
            *ptr++ = c;
            n++;
            if(c == '\n')
                break;
        }
        else if (rc == 0)
        {
            *ptr = '\0';
            return (n -1);
        }
        else
        {
            if(errno == eintr)
                continue;
            else
                return (-1);
        }
    }
    *ptr = '\0';
    return n;
}

telnet.h

#ifndef __telnet__h
#define __telnet__h

//<iac cmd op >

#define iac   255

//command word
#define nul        0
#define bel        7
#define bs         8
#define ht         9
#define lf         10
#define vt         11
#define ff         12
#define cr         13
#define se         240
#define nop        241
#define dm         242
#define brk        243
#define ip         244
#define ao         245
#define ayt        246
#define ec         247
#define el         248
#define ga         249
#define sb         250
#define will       251
#define wont       252
#define do         253
#define dont       254

typedef unsigned char uint8;
typedef unsigned int  uint32;


//operation options

typedef enum tagoperation_options
{
    topt_bin = 0,
    topt_echo = 1,
    topt_recn = 2,
    topt_supp = 3
    
}operation_options;


uint32 get_every_frame(uint8* recvbuf,uint32 len,uint8* sendbuf,uint32 sendlen);

#endif

telnet.c




#include"telnet.h"
#include<string.h>
#include<stdio.h>


#define maxline 1024
#define send    1
#define is      0



static uint32 handle_telnetcmd_from_server(uint8* buf,uint32 len,uint8* resp,uint32 n);
static uint32 process_every_frame(uint8* startbyte,uint8* endbyte,uint8* sendbuf,uint32 startsendbyte);


uint32 get_every_frame(uint8* recvbuf,uint32 len,uint8* sendbuf,uint32 sendlen)
{
    uint32 i =0,n=0,sum =0;
    //uint8* p = sendbuf;
    uint8* prear = &recvbuf[len];
    uint8* startbyte = recvbuf;
    uint8* endbyte = recvbuf;
    
    printf("-sum-receivelen----%d-------\n",len);
    printf("receive :<*");
    
    for(i =0 ;i<len;i++)
    {
        printf("%x*",recvbuf[i]);
    }
    printf("*>\n");
    
    
    
    while(startbyte != prear)
    {
        if(*startbyte == iac)
        {
            sum = sum + n;
            switch(*(++endbyte))
            {
        /*fa 250 */case sb:while(*(++endbyte) != se){};n = process_every_frame(startbyte,endbyte,sendbuf,sum);break;
        /*fb 251 */case will:endbyte +=2;n = process_every_frame(startbyte,endbyte,sendbuf,sum);break;
        /*fc 252 */case wont:endbyte +=2;n = process_every_frame(startbyte,endbyte,sendbuf,sum);break;
        /*fd 253 */case do:endbyte +=2;n = process_every_frame(startbyte,endbyte,sendbuf,sum);break;
        /*fe 254 */case dont:endbyte +=2;n = process_every_frame(startbyte,endbyte,sendbuf,sum);break;  
        /* 240 */case se:break;
        /* sss */default : break;
            }
        }
        startbyte = endbyte;
    }
    if(sum > sendlen)
    {
        printf("--error3---sum > maxline-----\n");
    }
    printf("--------------sum is %d ----\n",sum);
    return sum;
}



static uint32 process_every_frame(uint8* startbyte,uint8* endbyte,uint8* sendbuf,uint32 startsendbyte)
{
    uint8 n = 0 ;
    uint8* pstartbyte = startbyte;
    
    while(pstartbyte != endbyte)
    {
        n++;
        pstartbyte++;
    }
    return handle_telnetcmd_from_server(startbyte,n,&sendbuf[startsendbyte],maxline);
}

static uint32 handle_telnetcmd_from_server(uint8* buf,uint32 len,uint8* resp,uint32 n)
{
    uint32 i =0;
    uint8 *p = resp;
    operation_options optioncode;
    uint8 cmdcode,ch;
    uint32 resplen =0;
    memset(resp,0,len);
    //first display cmd from server in string
    
    printf("--receivelen----%d-------\n",len);
    printf("receive :<*");
    for(i =0 ;i<len;i++)
    {
        printf("%x*",buf[i]);
    }
    printf("*>\n");
    
    if(len < 3)
    {
        printf("iac command length is %d less then 3\n",len);
        return -1;
    }
    
    //获得命令码
    cmdcode = buf[1];
    //获得选项码
    optioncode = buf[2];
    
    
    //response requests from server
    
    *p = iac;
    resplen++;
    if(optioncode == topt_echo || optioncode == topt_supp)
    {
        if (cmdcode == do)
        {
            //我设置我应答的命令码为 251(will) 即为支持 回显或抑制继续进行
            ch = will;
            *(++p) = ch;
            *(++p)= optioncode;
            resplen += 2;

        }
        //如果命令码为 254(dont)
        else if (cmdcode == dont)
        {
            //我设置我应答的命令码为 252(wont) 即为我也会"拒绝启动" 回显或抑制继续进行
            ch = wont;
            *(++p)= ch;
            *(++p)= optioncode;
            resplen += 2;

        }
        //如果命令码为251(will)
        else if (cmdcode == will)
        {
            //我设置我应答的命令码为 253(do) 即为我认可你使用回显或抑制继续进行
            ch = do;
            *(++p)= ch;
            *(++p)= optioncode;
            resplen += 2;
            //break;
        }
        //如果接受到的命令码为251(wont) 
        else if (cmdcode == wont)
        {
            //应答  我也拒绝选项请求回显或抑制继续进行
            ch = dont;
            *(++p)= ch;
            *(++p)= optioncode;
            resplen += 2;
            //    break;
        }
        //如果接受到250(sb,标志子选项开始)
        else if (cmdcode == sb)
        {
            /*
             * 因为启动了子标志位,命令长度扩展到了4字节,
             * 取最后一个标志字节为选项码
             * 如果这个选项码字节为1(send)
             * 则回发为 250(sb子选项开始) + 获取的第二个字节 + 0(is) + 255(标志位iac) + 240(se子选项结束)
            */
            ch = buf[3];
            if (ch == send)
            {
                ch = sb;
                *(++p)= ch;
                *(++p)= optioncode;
                *(++p)= is;
                *(++p)= iac;
                *(++p)= se;
                resplen += 5;
            }
            else
            {
                printf("ch != send\n");
            }
        }
        else
        {
            /* do nothing */
        }
    }
    else/* 如果选项码不是1 或者3  */
    {
        // 底下一系列代表,无论你发那种请求,我都不干
        if (cmdcode == do)
        {
            ch = wont;
            *(++p)= ch;
            *(++p)= optioncode;
            resplen += 2;
        }
        else if (cmdcode == dont)
        {
            ch = wont;
            *(++p)= ch;
            *(++p)= optioncode;
            resplen += 2;
        }
        else if (cmdcode == will)
        {
            ch = dont;
            *(++p)= ch;
            *(++p)= optioncode;
            resplen += 2;
        }
        else if (cmdcode == wont)
        {
            ch = dont;
            *(++p)= ch;
            *(++p)= optioncode;
            resplen += 2;
        }
        else
        {
            /* do nothing */
        }
            
    }
    
    printf("--resplen---%d-------\n",resplen);
    printf("response :<*");
    for(i =0 ;i<resplen;i++)
    {
        printf("%x*",resp[i]);
    }
    printf("*>\n");
    
    
    
    if(n < resplen )
    {
        printf("error n < resplen !!! \n");
    }
    if(resplen < 3 )
    {
        printf("resplen < 3 \n");
    }
    return resplen;
}

client.c

//gcc client.c -o client


#include"utils.h"
#include"telnet.h"


#define ip_address   "127.0.0.1"
#define ip_port      23
#define serv_port    3333
#define maxline      1024

typedef struct sockaddr sa;


void str_cli(file *fp,uint32 sockfd);
uint32 max(uint32 a,uint32 b);
void err_exit(char* s);

uint32 main(uint32 argc,uint32 **argv)
{
    uint32 sockfd,isready=0;
    struct sockaddr_in servaddr;
    uint32 hname[128];

    sockfd = socket(af_inet,sock_stream,0);
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = af_inet;
    servaddr.sin_port = htons(ip_port);
    servaddr.sin_addr.s_addr = inet_addr(ip_address);
    printf("servaddr: ip is %s, port is %d\n",inet_ntoa(servaddr.sin_addr), ntohs(servaddr.sin_port));

    while(connect(sockfd,(sa*)&servaddr,sizeof(servaddr))){};
    printf("connect has been ready\n");
    
    str_cli(stdin,sockfd);
    exit(0);
    return 0;
}
void err_exit(char* s)
{
    perror(s);
    exit(exit_failure);
}
void info_print(char* s)
{
    printf("%s",s);
}
uint32 max(uint32 a,uint32 b)
{
    return (a>b?a:b);
}

void str_cli(file *fp,uint32 sockfd)
{
    uint32 maxfdp1,nready;//stdineof;
    fd_set rset;
    uint8 buf[maxline];
    uint8 respbuff[maxline] = {0};;
    uint32 resplen;
    uint32 n;
    uint8 echo_cmd[] = {0xff,0xfb,0x01};
    //stdineof = 0;
    fd_zero(&rset);
    writen(sockfd,echo_cmd,3);
    
    for(;;)
    {
        //if(stdineof == 0)
        fd_set(fileno(fp),&rset);
        fd_set(sockfd,&rset);
        maxfdp1 = max(fileno(fp),sockfd)+1;
        nready = select(maxfdp1,&rset,null,null,null);
        
        if(nready < 0)
        {
            err_exit("error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        }
        
        if(fd_isset(sockfd,&rset))
        {
            memset(buf,0,maxline);
            if((n = read(sockfd,buf,maxline))==0)
            {
                err_exit("str_cli:server termination prematurely");
            }
            buf[n] = '\0';
            //printf("fd_isset(sockfd,&rset)-------------%s\n",buf);
            
            if(buf[0] == iac)
            {
                memset(respbuff,0,maxline);
                resplen = get_every_frame(buf,n,respbuff,maxline);
                writen(sockfd,respbuff,resplen);
            }
            else
            {
                writen(fileno(stdout),(char *)buf,n);
            }
            
            //writen(fileno(stdout),buf,n);
        }
        if(fd_isset(fileno(fp),&rset))
        {
            memset(buf,0,maxline);
            if((n = readline(fileno(fp),(char *)buf,maxline)) == 0)
            {
                //stdineof = 1;//此时碰到eof 并且马上要发生fin序列 所以标准输入不可读了
                shutdown(sockfd,shut_wr);
                fd_clr(fileno(fp),&rset);
                info_print("nothing input!");
                continue;
            }
            else if(n >0)
            {
                /* do nothing */
            }
            else
            {
                err_exit("some error occurred ");
            }
            //printf("fd_isset(fileno(fp),&rset)----%d--\n",n);
            //memset(buf,0,maxline);
            writen(sockfd,(char *)buf,n);
        }
    }
}

makefile

all:client_telnet
    @echo ""
    @echo "this is telnet client compile......."
    @echo ""
    
client_telnet:client.o utils.o telnet.o
    gcc -g -o client_telnet client.o utils.o telnet.o
    
client.o:client.c utils.h telnet.h
    gcc -g -c client.c
    
utils.o:utils.c utils.h
    gcc -g -c utils.c 
    
telnet.o:telnet.c telnet.h
    gcc -g -c telnet.c
    
clean :
    -rm client.o utils.o telnet.o client_telnet

以上为本次程序使用的源码 程序在linux系统上运行方式为 ./client_telnet

然后是运行截图如此下:

模拟telnet协议C语言客户端程序

模拟telnet协议C语言客户端程序

模拟telnet协议C语言客户端程序

以上