利用TCP的带外数据来控制远程服务器端的连接 bkbll(bkbll@cnhonker.net) http://www.cnhonker.net
在连接内网机器的时候,需要在目标的网关或者代理上运行一个用来建立自己和内网指定机器和端口的socket通道的程序,但是该程序运行一次只能连接一个服务器和端口,如果需要更换连接内网的服务器和端口需要再一次指定参数运行一次,这样不但麻烦,而且增加了服务器的开销以及浪费大量的时间,有没有办法在建立好服务器端后,我们能远程控制服务器来提供我们想要的socket通道? 答案当然是肯定的.通常有很多种方法来做到这一点.但通常的做法是利用客户端将我们想要连接的内网服务器的IP和端口发送给我们的服务器端,再由服务器端根据协议来完成我们的任务.但是普通的发送和接受方式是不可能完成我们的任务,不能及时得到系统的响应,而且有可能被永远的阻塞,或者在传送的时候会丢失数据,如果我们需要将数据一一的验证,那么不但花费时间,而且也会增加编程难度和增加开销. 其实我们利用TCP的带外数据来控制远程我们的服务器端程序.以下是TCP带外数据的概述:”传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据,如过通信一放有重要的数据需要通知对方时,协议能够将这些数据快速地发送到对方.为了发送这些数据,协议一般不使用与普通数据相同的通道,而是使用另外的通道.linux系统的套接字机制支持低层协议发送和接受带外数据.但是TCP协议没有真正意义上的带外数据.为了发送重要协议,TCP提供了一种称为紧急模式(urgent mode)的机智.TCP协议在数据段中设置URG位,表示进入紧急模式.接收方可以对紧急模式采取特殊的处理.”很容易看出来,这种方式数据不容易被阻塞,并且可以通过在我们的服务器端程序里面捕捉SIGURG信号来及时接受数据.这正是我们所要求的效果. 为了达到目的,我们需要让我们的服务器端软件知道三组数据:需要监听的端口、需要连接的IP、需要连接IP的端口.由于TCP协议只能每次只能发送和接受带外数据一个字节,所以,我们可以通过设置一个数组,利用发送数组下标的办法让服务器程序能够知道自己要监听的端口以及要连接的服务器IP/port.由于限定在1个字节,所以我们最多只能控制255个port的连接,255个内网机器(不过同一子网的机器不会超过255J),同样也只能控制255个监听端口,不过这些已经足够了. 下面是服务器端程序source codes: /*use tcp OOB sign to select port and host for connecting. powerd by bkbll(bkbll@cnhonker.net) welcome to visite our website:http://www.cnhonker.net. tested in Redhat 6.2(kernel:2.2.14 on i386) */ #include #include #include #include #include #include "inet.h"
#define MAXSIZE 10240 #define max(a,b) (a)>(b)?(a):(b) #define TIMEOUT 300 #define CONNECTNUM 5
extern int create_socket(); extern int create_serv(); extern int client_connect();
//定义连接内网机器的端口 int portlist[255]={0,21,22,23,25,53,79,80,110,111,119,139,143,389,513,514,515,1080,1433,3389}; //定义在服务器端监听的端口 int portlist2[255]={0,4444,5555,6666,7777,8888,9999,4446,4447,4448,4449,5500,3389}; //定义需要连接内网机器的IP.域名也可以. char *hostlist[255]={"","192.168.0.1","192.168.0.2","192.168.0.3"}; //第一个数据都为空是为了方便远程结束服务器段的执行.:0 int remotefd=0,oob,su=0,svfd=0,check_fd=0; int portnum_listen,hostnum,portnum_connect; main(int argc,char **argv) { int localp,remotep,size; struct sockaddr_in remote_client; struct sockaddr_in sock; struct timeval time_out; void get_oob(int signo); char buffer; int num=0,portnum; pthread_t thread1; void out2in(); void setfail(); //这里的监听端口是用来和发送带外数据的客户端程序连接所用到的. if(argc<2){printf("usage:%s n",argv[0]);exit(0);} localp=atoi(argv[1]); if(!(svfd=create_socket()))exit(0); if(!create_serv(svfd,localp))exit(0); printf("#################################################################n"); printf("####### server_convert socket bye bkbll(bkbll@cnhonker.net)######n"); printf("########### (H.U.C) http://www.cnhonker.net 2001-9-20 ###########nn"); for(;;) { size=sizeof(struct sockaddr); printf("nwaiting for client connecting........n"); if((remotefd=accept(svfd,(struct sockaddr *)&remote_client,&size))<0){printf("accept errorn");continue;} printf("accept a client on %d from %sn",localp,inet_ntoa(remote_client.sin_addr)); check_fd=0; time_out.tv_sec=6; time_out.tv_usec=0; setsockopt(remotefd,SOL_SOCKET,SO_RCVTIMEO,&time_out,sizeof(time_out)); //设置套接字的owner. fcntl(remotefd,F_SETOWN,getpid()); //捕获SIGURG信号,读取带外数据 signal(SIGURG,&get_oob); //捕获SIGALRM信号,在发送带外数据客户端以外结束连接后能保证程序不被无限阻塞. signal(SIGALRM,&setfail); for(num=0;num<3;num++){ fprintf(stderr,"waiting for MSG_OOB data%d........",num); //设置6秒超时. alarm(6); while(!su); if(num==0){portnum_listen=oob;} if(num==1){hostnum=oob;} if(num==2){portnum_connect=oob;} su=0; alarm(0); } printf("portnum_listen=%d,hostnum=%d,portnum_connect=%dn",portnum_listen,hostnum,portnum_connect); if((portnum_listen==0)&&(hostnum==0)&&(portnum_connect==0)){close(remotefd);printf("shutdown.:)n");break;} if(check_fd==1){continue;} close(remotefd); printf("Creating a thread for converting socket.listenport:%d,connect host|port:%s:%d,n",portlist2[portnum_listen],hostlist[hostnum],portlist[portnum_connect]); //创建一个线程用来建立指定的监听端口,连接指定IP和端口的socket通道 pthread_create(&thread1,NULL,(void *)&out2in,NULL); sleep(5); } close(svfd); }
void get_oob(int signo) { //接受带外数据 if(recv(remotefd,&oob,1,MSG_OOB)<0) { fprintf(stderr,"get error:%sn",strerror(errno)); return; } fprintf(stderr,"successed,MSG_OOB=%dn",oob); su=1; return; }
void setfail() { //超时则结束连接 su=1; check_fd=1; return; }
void out2in() { int out_fd,in_fd,serverd; int listenport2,port_connect,maxfd,re_bind=1; char host[15]; struct timeval timeset; struct sockaddr_in clientaddr; fd_set readfd,writefd; int result,i=0,sizelen; char read_in1[MAXSIZE],send_out1[MAXSIZE]; char read_in2[MAXSIZE],send_out2[MAXSIZE]; int read1=0,totalread1=0,send1=0; int read2=0,totalread2=0,send2=0; int check_num; listenport2=portlist2[portnum_listen]; port_connect=portlist[portnum_connect]; bzero(host,15); memcpy(host,hostlist[hostnum],15); bzero(read_in1,MAXSIZE); bzero(read_in2,MAXSIZE); bzero(send_out1,MAXSIZE); bzero(send_out2,MAXSIZE); timeset.tv_sec=TIMEOUT; timeset.tv_usec=0; sizelen=sizeof(struct sockaddr); //创建一个监听端口 if(!(serverd=create_socket())){return;} setsockopt(serverd,SOL_SOCKET,SO_REUSEADDR,&re_bind,sizeof(re_bind)); if(!create_serv(serverd,listenport2)){close(serverd);return;} for(check_num=0;check_num fprintf(stderr,"the %d time,listening on %d.....",check_num,listenport2); //接受外界连接 if((out_fd=accept(serverd,(struct sockaddr *)&clientaddr,&sizelen))<0){printf("accept errorn");break;} printf("accept a client from %sn",inet_ntoa(clientaddr.sin_addr)); //连接内网机器指定端口 if(!(in_fd=create_socket())){close(out_fd);break;} if(!client_connect(in_fd,host,port_connect)){close(in_fd);close(out_fd);break;} maxfd=max(out_fd,in_fd)+1; while(1) { FD_ZERO(&readfd); FD_ZERO(&writefd); FD_SET(out_fd,&readfd); FD_SET(in_fd,&writefd); FD_SET(out_fd,&writefd); FD_SET(in_fd,&readfd); result=select(maxfd,&readfd,&writefd,NULL,×et); if(result<0){printf("select errorn");break;} else if(result==0){printf("time outn");break;} if(FD_ISSET(out_fd,&readfd)) { read1=recv(out_fd,read_in1,MAXSIZE,0); if(read1==0)break; if(read1<0){printf("read data error:%sn",strerror(errno));break;} memcpy(send_out1+totalread1,read_in1,read1); totalread1+=read1; bzero(read_in1,MAXSIZE); } if(FD_ISSET(in_fd,&writefd)) { while(totalread1>0) { send1=write(in_fd,send_out1,totalread1); if(send1==0)break; if(send1<0){printf("unknow error:%sn",strerror(errno));continue;} totalread1-=send1; } bzero(send_out1,MAXSIZE); } if(FD_ISSET(in_fd,&readfd)) { read2=recv(in_fd,read_in2,MAXSIZE,0); if(read2==0)break; if(read2<0){printf("read data error:%sn",strerror(errno));break;} memcpy(send_out2+totalread2,read_in2,read2); totalread2+=read2; bzero(read_in2,MAXSIZE); } if(FD_ISSET(out_fd,&writefd)) { while(totalread2>0) { send2=write(out_fd,send_out2,totalread2); if(send2==0)break; if(send2<0){printf("unknow error:%sn",strerror(errno));continue;} totalread2-=send2; } bzero(send_out2,MAXSIZE); } } close(out_fd); close(in_fd); } close(serverd); return; }
/* http://www.cnhonker.net Sep 19,2001*/
下面是发送带外数据的客户端程序. /*use tcp OOB sign to select port and host for connecting. powerd by bkbll(bkbll@cnhonker.net) welcome to visite our website:http://www.cnhonker.net. tested in Redhat 6.2(kernel:2.2.14 on i386) */ #include #include #include #include "inet.h"
extern int create_socket(); extern int create_serv(); extern int client_connect();
main(int argc,char **argv) { int sockfd; int listp,inhost,inhostp,a; int byte; char b; /*发送带外数据到指定的IP和端口.第一个host表示我们的服务器端软件的IP,第二个port表示服务器端软件的端口. 第三个port是表示我们希望在目标服务器上建立的监听端口,第四个host表示我们希望服务器端软件连接的内网机器IP 最后一个port表示希望连接内网机器的端口. 注意:后三个数据都是小与256的int型数据,比如:1 2 3或者4 5 6等等。.发送0 0 0 表示结束远程服务器端程序. */ if(argc<6){printf("Usage:%s n",argv[0]);exit(0);} listp=atoi(argv[3]); inhost=atoi(argv[4]); inhostp=atoi(argv[5]); b=a;
printf("#################################################################n"); printf("######### remote control server connect client bye bkbll ########n"); printf("########### (H.U.C) http://www.cnhonker.net 2001-9-20 ###########nn"); if(!(sockfd=create_socket()))exit(0); if(!client_connect(sockfd,argv[1],atoi(argv[2]))){close(sockfd);exit(0);} //? write(sockfd,&b,1); sleep(1); //发送第一个带外数据 if((byte=send(sockfd,&listp,1,MSG_OOB))<0){printf("send MSG_OOB error1:%sn",strerror(errno));} printf("send success,data:%d,total %d byten",listp,byte); sleep(1); //发送第二个带外数据 if((byte=send(sockfd,&inhost,1,MSG_OOB))<0){printf("send MSG_OOB error2:%sn",strerror(errno));} printf("send success,data:%d,total %d byten",inhost,byte); sleep(1); //发送第三个带外数据. if((byte=send(sockfd,&inhostp,1,MSG_OOB))<0){printf("send MSG_OOB error3:%sn",strerror(errno));} printf("send success,data:%d,total %d byten",inhostp,byte); sleep(1);
close(sockfd); }
2001-10-24 01:00
|