2013-01-15 147 views
0

我有一个TCP端口扫描器,它可以扫描给定IP上的所有开放端口,并只返回开放端口和服务名称。要做到这一点,我创建一个套接字,将其设置为非阻塞模式,如果它超时,则表示端口关闭,否则打开。问题是我的select()总是超时,即使我试图扫描打开的端口。我希望有人把我的错误指向我,我的逻辑不好?Unix TCP端口扫描器

#include <stdlib.h> 
#include <iostream> 
#include <cstdio> 
#include <string.h> 
#include <netdb.h> 
#include <fcntl.h> 
#include <assert.h> 
#include <sys/time.h> 
#include <errno.h> 

using namespace std; 
fd_set working_set; 
hostent *he; 
char* protoc [2] = { "tcp","udpn" }; 
int port; 
struct sockaddr_in servaddr; 
int sendfd; 
servent *srvport; 
void set_nonblock(int socket) { 
    int flags; 
    flags = fcntl(socket,F_GETFL,0); 
    assert(flags != -1); 
    fcntl(socket, F_SETFL, flags | O_NONBLOCK); 
} 
void set_block(int socket) { 
    int flags; 
    flags = fcntl(socket,F_GETFL,0); 
    assert(flags != -1); 
    fcntl(socket, F_SETFL, flags | ~O_NONBLOCK); 
} 

int main(int argc, char* argv[]) 
{ 

    struct timeval timeout; 
    timeout.tv_sec = 1; 
    timeout.tv_usec = 0; 
    char* host = argv[1]; 
    char* pro = argv[2]; 
    int portlow = atoi(argv[3]); 
    int porthigh = atoi(argv[4]); 

    fprintf(stderr, "n Scanning host=%s, protocol=%s, ports: %d -> %d \n", 
      host, pro, portlow, porthigh); 

    if(strcmp(pro, protoc[0])==0) 
     pro = protoc[0]; 
    else if (strcmp(pro, protoc[1])==0) 
     pro = protoc[1]; 
    else 
    { 
     herror("n specify valid protocol - tcp or udpn"); 
     exit(-1); 
    } 


    if((he = gethostbyname(argv[1])) == NULL) 
    { 
     herror("n *** gethostbyname() failed ***n"); 
     exit(-1); 
    } 
    /*In case TCP protocol is selected for scan, app opens streaming socket 

for every port to be scanned, tries to connect to it, and if successful 

it displays information about service using struct servent. 
*/ 

    if(strcmp(pro, protoc[0])==0) // tcp scan 
    { 
     for(port = portlow; port <= porthigh; port++) 
     { 
      // open stream socket 
      if((sendfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
      { 
       perror("*** socket(,SOCK_STREAM,) failed ***n"); 
       exit(-1); 
      } 
      set_nonblock(sendfd); 
      bzero(&servaddr, sizeof(servaddr)); 

      servaddr.sin_family = AF_INET; 
      servaddr.sin_port = htons(port); 
      servaddr.sin_addr = *((struct in_addr *)he->h_addr); 
      int res = connect(sendfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 
int ser; 
      if (res < 0) { 
       if (errno == EINPROGRESS) { 
        timeout.tv_sec = 0; 
        timeout.tv_usec = 10; 
        FD_SET(sendfd, &working_set); 

        if ((ser=select(sendfd+1, NULL, &working_set, NULL, &timeout)) > 0) { 
          srvport = getservbyport(htons(port), protoc[0]); 

        } 
        else { 
         fprintf(stderr, "Timeout or error() %d\n",ser); 
         perror("select(): "); 

        } 
       } 
       else { 
        fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno)); 
       } 
       if(srvport != NULL) 
        printf("tport %d: %sn \n ", port, srvport->s_name); 
       else if (ser=0) 
        close(sendfd); 
       fflush(stdout); 
       } 

      //set_block(sendfd); 
     }//end of for() 
    } 
} 

回答

5

这是相当多的代码。我没有运行它。但是,这个:

if (ser=(select(sendfd, NULL, &working_set, NULL, &timeout)) > 0) { 

是错误的。 select()的第一个参数是“三个集合中任何一个中编号最高的文件描述符,加上1”(请参阅​​man page)。

此外,你应该加上括号像这样:

if ((ser = select(...)) > 0) { 

,因为现在你分配>运营商的ser变量,这可能不是你所期望的结果。

+0

我修正了这个错误,但现在我得到的所有端口的列表,就是这样,我关闭套接字也不好?另外我更新了帖子中的代码。 – Karudi

0

使用I/O的描述符集,working_set之前,你需要确保你明确表示:

FD_ZERO(&working_set);