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.问题就是还需要实现扫更多的端口,暂时不忙这个,似乎不难~