WHY?
之前有个connect的端口扫描程序,那个东西只适合扫localhost,一扫其他机器就×××××,反正是不能扫内网其他机器,更不要说外网主机 了。这是由于connect的返回超时设置问题,《UNP》第一卷 第三版 P85-P86指出伯克利系统的超时时限为75s,Solaris9超时时限为4分钟,所以一般认为是75s到几分钟不等,而我测试的时限为189s(Linux Kernel 2.6.24),SYN6次重传~~
好了,这就是为什么要用非阻塞connect了。
一.非阻塞connect的一般步骤:
0. sockfd=socket();
1. fcntl设置sockfd为非阻塞;
2. connect();
3. select();
4. FD_ISSET();
上面这个步骤也就只能执行一次,一个connect.
二.所以下面是执行多个connect的步骤:
for(;;)
{
0. sockfd=socket();
1. fcntl设置sockfd为非阻塞;
2. connect();
}
3. select();
4. FD_ISSET();
NOTE:
下面的程序在select()之前设置了sleep(5),也可设置其他时间,这个似乎比较重要,因为这样让select之前返回所以应该返回的connect.
三.代码如下:
#include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/select.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <netinet/in.h> #include <errno.h> #include <netdb.h> #define MINPORT 0 #define MAXPORT 1000 int main(int argc,char **argv) { int fd[MAXPORT]; int flags,n,i,rst,maxfd,count=0; int port[MAXPORT]; struct sockaddr_in addr[MAXPORT]; fd_set rset,wset; struct timeval tm; int addrsize=sizeof(struct sockaddr); struct servent *sent; FD_ZERO(&rset); FD_ZERO(&wset); tm.tv_sec=5; tm.tv_usec=0; for(i=MINPORT;i<MAXPORT;i++) /* 可能在此循环完前就有connect返回 */ { if((fd[i]=socket(AF_INET,SOCK_STREAM,0))==-1) { perror("Socket"); exit(-1); } /* 设置fd为非阻塞 */ flags=fcntl(fd[i],F_GETFL,0); fcntl(fd[i],F_SETFL,flags|O_NONBLOCK); port[i]=i+1; bzero((struct sockaddr*)&addr[i],addrsize); addr[i].sin_family=AF_INET; addr[i].sin_addr.s_addr=inet_addr(argv[1]); addr[i].sin_port=htons((short)port[i]); if((n=connect(fd[i],(struct sockaddr *)&addr[i],addrsize))<0) if(errno!=EINPROGRESS) {printf("Connecting 1 error!\n"); exit(1);} else if(n==0) { //This case may be happen on localhost printf("Connecting 1 success! \n"); exit(0); } FD_SET(fd[i],&rset); } wset=rset; sleep(5); rst=select(MAXPORT+3, &rset,&wset,NULL,&tm); switch (rst) { case -1: perror("Select error"); exit(-1); case 0: for(i=MINPORT;i<MAXPORT;i++) close(fd[i]); printf("Timed Out!\n"); break; default: for(i=MINPORT;i<MAXPORT;i++) { if (FD_ISSET(fd[i],&rset)||FD_ISSET(fd[i],&wset)) { int error; socklen_t len = sizeof (error); if(getsockopt(fd[i],SOL_SOCKET,SO_ERROR,&error,&len) < 0) { printf ("getsockopt fail,connected fail\n"); return -1; } if(error==0) { if((sent=getservbyport(htons(i+1),"tcp"))==NULL) { printf("Unknown service(port %d) is available. \n",i+1); } else { printf("%s\tservice(port %d) is avilable. \n",sent->s_name,i+1); } count++; } } close(fd[i]); } } if(count==0) printf("There is no port open or the domain has down!\n"); return 0; } |
NOTE:
1. MINPORT其实不是端口号,而是最小端口号-1,方便循环而已~~
2. select的第一个参数最大为1024,所以设置的MAXPORT 1000检测1000个端口
四.问题???
1. 有时还是有问题,用nmap能扫到的端口用这个扫不到。确实很无解,只能先放一放了~
2. 扫内网有一台机器时21端口始终扫不出来,nmap可以,难道是拒绝链接?OR?继续无解
3. UBUNTU中文论坛的服务器真牛B,开了十多个端口,不过用这个扫有一个扫不到~
4.问题就是还需要实现扫更多的端口,暂时不忙这个,似乎不难~