linux c 非阻塞connect版端口扫描程序

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.问题就是还需要实现扫更多的端口,暂时不忙这个,似乎不难~
posted @ 2011-05-24 22:03  ACE封印  Views(1151)  Comments(0Edit  收藏  举报