模拟telnet协议C语言客户端程序
首先要了解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>
#include <stdlib.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);
/* 2020-01-13 begin */
extern uint32 SOCKFD ;
extern void telnet_client_send_msg(int fd, const void *vptr, size_t n);
/* 2020-01-13 end*/
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;
uint8* strSendToMQTT = NULL;
strSendToMQTT = (uint8*)malloc(MAXLINE);
if(strSendToMQTT == NULL)
{
printf("==========strSendToMQTT malloc failed==============\n");
return 0;
}
#if 1
printf("-sum-receivelen----%d-------\n",len);
printf("receive :<*");
for(i =0 ;i<len;i++)
{
printf("%x*",recvbuf[i]);
}
printf("*>\n");
#endif
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;
}
else
{
/* 2020-01-13 begin :按照原来的写法 else分支会造成死循环,这里修改 */
i = 0;
memset(strSendToMQTT,0,MAXLINE);
while((startByte != pRear)&&(*startByte != IAC ))
{
strSendToMQTT[i++] = *startByte ;
++startByte;
}
strSendToMQTT[i] = '\0';
/* 2020-01-13 strSendToMQTT的字符串应当也要发送出去 */
telnet_client_send_msg(SOCKFD,strSendToMQTT,i+1);
/* 2020-01-13 end :按照原来的写法 else分支会造成死循环,这里修改 */
}
}
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
#if 1
printf("--receivelen----%d-------\n",len);
printf("receive :<*");
for(i =0 ;i<len;i++)
{
printf("%x*",buf[i]);
}
printf("*>\n");
#endif
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_SUPP)//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 */
}
}
#if 1
printf("--resplen---%d-------\n",resplen);
printf("response :<*");
for(i =0 ;i<resplen;i++)
{
printf("%x*",resp[i]);
}
printf("*>\n");
#endif
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
{
telnet_client_send_msg(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);
telnet_client_send_msg(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语言客户端程序的更多相关文章
- 模拟I2C协议学习点滴之程序相关定义
由于主机和从机都会给数据线SDA发信号,比如主机先给SDA发送数据后,从机收到数据后发送应答信号将SDA拉低,故SDA类型设定为inout.而DATA设定为inout类型,是起到校验通信的作用(后续的 ...
- c++下基于windows socket的服务器客户端程序(基于UDP协议)
前天写了一个基于tcp协议的服务器客户端程序,今天写了一个基于UDP协议的,由于在上一篇使用TCP协议的服务器中注释已经较为详细,且许多api的调用是相同的,故不再另外注释. 使用UDP协议需要注意几 ...
- 关于telnet协议的研究以及用java进行封装实现自己的telnet客户端(转)
最近在做一个远程控制的模块,其中用到了telnet协议,开始用的是apache-net包的telnetclient,但发现问题不少,比较慢,还有就是判断是否read完毕的问题.后来经过讨论打算实现自己 ...
- c++下基于windows socket的单线程服务器客户端程序(基于TCP协议)
今天自己编写了一个简单的c++服务器客户端程序,注释较详细,在此做个笔记. windows下socket编程的主要流程可概括如下:初始化ws2_32.dll动态库-->创建套接字-->绑定 ...
- Mina入门级客户端程序实现telnet程序
Mina入门级客户端程序实现telnet程序,其实mina的客户端和服务端很相似 1.编写客户端MinaClient.java和客户端处理类MyClientHandler.java2.MinaClie ...
- RedHat下安装Telnet服务端及客户端远程连接配置
Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式.它为用户提供了在本地计算机上完成远程主机工作的能力. 配置之前请确保网络连通,如防火墙影响连接,请先关 ...
- 使用 Socket 通信实现 FTP 客户端程序(来自IBM)
FTP 客户端如 FlashFXP,File Zilla 被广泛应用,原理上都是用底层的 Socket 来实现.FTP 客户端与服务器端进行数据交换必须建立两个套接字,一个作为命令通道,一个作为数据通 ...
- NodeJs开发的CLI——与telnet进行通信的聊天程序
前言: (在NodeJs中,我们想要开启一个tcp协议的做法就是引入net内置对象: const net = require('net'); ——ES6 var net = req ...
- 基于UDP协议的控制台聊天程序(c++版)
本博客由Rcchio原创,转载请告知作者 ------------------------------------------------------------------------------- ...
随机推荐
- windows系统下安装JDK8
学习JAVA,必须得安装一下JDK(java development kit java开发工具包),配置一下环境就可以学习JAVA了,下面是下载和安装JDK的教程: 一.下载 1.JDK下载地址: h ...
- .net core 自定义404 500页面
常用处理方式 自己定制网站的404.500页面的方式有很多,比如修改nginx配置文件,指定请求返回码对应的页面, .netframework项目中修改webconfig文件,指定customerro ...
- pytho的traceback的解读
写 Python 代码的时候,当代码中出现错误,会在输出的时候打印 Traceback 错误信息,很多初学者看到那一堆错误信息,往往都会处于懵逼状态,脑中总会冒出一句,这都是些啥玩意.如果你是第一次 ...
- 4、链栈的实现(java代码)
1.链节点 public class Node<T> { public T data; public Node next; } 2.实现代码 public class Stack<T ...
- 大数据平台搭建 - cdh5.11.1 - hive客户端安装
一.简介 hive是基于hadoop的一种数据仓库工具,可以将结构化的文件映射成为数据库的一张表,并提供简单sql查询功能,底层实现是转化为MapReduce任务计算. 二.安装 (1)下载 从cdh ...
- 安全性测试:OWASP ZAP 2.8 使用指南(二):ZAP基础操作
ZAP桌面应用 ZAP桌面应用由以下元素组成: 1. 菜单栏 – 提供多种自动化和手动工具的访问 2. 工具栏 – 提供快速访问最常用组件的用户接口 3. 树结构窗口 – 展示被测网站树结构和脚 ...
- PLC与上位机的socket通讯——ABB机器人程序(三)
源程序:https://github.com/935094505/ABB-socket-communication 程序范例 觉得有帮助,别忘了打赏下
- 上手Dubbo之 环境搭建
和传统ssm整合--写XML配置文件 搭建服务的提供者和服务的消费者,实现服务消费者跨应用远程调用服务提供者 公共模块抽取 公共模块的抽取 服务的消费者远程调用服务的提供者, 最起码他自己要得到在服务 ...
- KeyValue对RDDs
创建KeyValue对RDDs 使用函数map,返回keyvalue对 例如,包含数行数据的RDD,那每行的第一个单词作为keys. sparl.txt中的内容如下: hello ! hello wo ...
- [C++]类的设计(2)——拷贝控制(阻止拷贝)
1.阻止拷贝的原因:对于某些类来说,拷贝构造函数和拷贝赋值运算符没有意义.举例:iostream类阻止了拷贝,以避免多个对象写入或者读取相同的IO缓冲. 2.阻止拷贝的方法有两个:新标准中可以将成 ...