首先要了解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指令處理有部分是直接借鑑他的程序 所以註釋都沒有修改仍然使用的是他的註釋html
以上兩篇大體看過以後 就能夠了解本次po出來的程序了 內容較爲樸素less
utils.hsocket
#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.cui
#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.hcode
#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.cserver
#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.chtm
//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); } } }
makefileblog
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_telnetrem
而後是運行截圖如此下:get
以上