Options

Why is my program soooo slow in Windows

CodeBloxCodeBlox Member Posts: 1,363 ■■■■□□□□□□
I wrote a simple port scanner and under linux it's lightning fast while on windows (After small modifications to port over) it's dog slow:

Usage is: portscan.exe [ip address]
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>


#define PORT_MIN 0x0000
#define PORT_MAX 0xFFFF


void test_port(SOCKET, struct sockaddr_in *, unsigned short, char *);

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


    SOCKET client_socket;
    WSADATA wsaData;
    struct sockaddr_in target;
    unsigned short port = PORT_MIN;


    WSAStartup(MAKEWORD(2, 2), &wsaData);


    memset(&target, sizeof(target), 0);


    if((target.sin_addr.s_addr = inet_addr(argv[1])) != INADDR_NONE)
        while(port++ < PORT_MAX)
            test_port(client_socket, &target, port, "tcp");


    return 0;
}


void test_port(SOCKET client_socket, struct sockaddr_in * target, unsigned short port, char * svc_proto){


    struct servent * service;


    printf("Testing %s port %u\r", svc_proto, port);


    target->sin_family = AF_INET;
    target->sin_port = htons(port);


    if((client_socket = socket(AF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET)
        if(connect(client_socket, (struct sockaddr *)target, sizeof(*target)) != SOCKET_ERROR){
            closesocket(client_socket);
            if(service = getservbyport(port, svc_proto))
                printf("Discovered open port %u (%s)/%s\n", port, service->s_name, svc_proto);
            else
                printf("Discovered open port %u/%s\n", port, svc_proto);
        }


}



What could cause it to be significantly slower?
Currently reading: Network Warrior, Unix Network Programming by Richard Stevens

Comments

  • Options
    paul78paul78 Member Posts: 3,016 ■■■■■■■■■■
    It because you are using a blocking connect(). Not sure if Windows supports using setsockopt() timeout options on connect() but that's one thing you can try.

    I vaguely recall (its been a while) that in Windows to do what you want efficiently, you would use an async/nonblocking connect() and write your own handler with select().

    Good luck - sounds like a fun project.
  • Options
    newt.chapmannewt.chapman Member Posts: 34 ■■□□□□□□□□
    (Anthony) wrote:
    most likely due to the connection recycling wait and tcp connection limit in windows.

    Quoted from a programming friendly board that I took the liberty of posting this problem on.
  • Options
    paul78paul78 Member Posts: 3,016 ■■■■■■■■■■
    connection recycling wait
    That's the cause of a blocking connect(). That's why it's normally done with an async connect() and then you can poll with select().
  • Options
    CodeBloxCodeBlox Member Posts: 1,363 ■■■■□□□□□□
    Sooo, I revised the program to scan a whole subnet and it seems to work, only now under even Linux, it starts going at a snails pace after the second IP... Any thoughts?

    usage: ./portscan [subnet] [mask]
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <netdb.h>
    
    
    #define NET_MASK 0XFFFFFFFF
    #define PORT_LIMIT 65535
    #define BASE_PORT 0
    
    
    void get_addr_space(unsigned long int *, unsigned long int *, struct in_addr, struct in_addr);
    void scan_addr(unsigned long int, struct sockaddr_in *, unsigned short);
    int test_port(int, unsigned short, struct sockaddr_in *, char *);
    void port_info(unsigned short, char *);
    
    
    
    
    int main(int argc, char ** argv){
    
    
      unsigned short port = BASE_PORT;
      unsigned long int curr_addr = 0, max_addr = 0;
      struct sockaddr_in target;
      struct in_addr mask;
      
      inet_pton(AF_INET, argv[1], &target.sin_addr);
      inet_pton(AF_INET, argv[2], &mask);
      
      get_addr_space(&curr_addr, &max_addr, target.sin_addr, mask);
      
      target.sin_family = AF_INET;
      
      curr_addr = ntohl(curr_addr);
      max_addr = ntohl(max_addr);
      
      while(curr_addr < max_addr - 1)
        scan_addr(curr_addr++, &target, port);
      
      return 0;
    }
    
    
    void get_addr_space(unsigned long int * min, unsigned long int * max, struct in_addr subnet, struct in_addr mask){
      
      unsigned long int subnet_bits = subnet.s_addr & mask.s_addr;
      
      *min = subnet_bits;
      *max = subnet_bits;
      
      //*max = ((~*max + subnet_bits) & ~mask.s_addr) + *max;
      *max = NET_MASK & ~mask.s_addr + *max;
    }
    
    
    void scan_addr(unsigned long int dec_addr, struct sockaddr_in * target, unsigned short port){
      
      int client_socket = 0;
      char ipv4[INET_ADDRSTRLEN];
      
      target->sin_addr.s_addr = htonl(dec_addr);
      target->sin_port = port;
      printf("\nTrying IP %s\n", inet_ntop(AF_INET, &target->sin_addr, ipv4, INET_ADDRSTRLEN)); 
      
      while(port < PORT_LIMIT)
        if(!test_port(client_socket, port++, target, "tcp"))
          port_info(port - 1, "tcp");
      
    }
    
    
    int test_port(int client_socket, unsigned short port, struct sockaddr_in * target, char * svc_proto){
      
      printf("\t - Trying %s port %u\r", svc_proto, port); 
      
      target->sin_family = AF_INET;
      target->sin_port = htons(port);
      
      if((client_socket = socket(AF_INET, (svc_proto == "tcp" ? SOCK_STREAM : SOCK_DGRAM), 0)) > 0){
        if(connect(client_socket, (struct sockaddr *)target, sizeof(*target))){
          close();
          return 1;
        }
        else
          return 0;
      }else
        perror("");
      
    }
    
    
    void port_info(unsigned short port, char * svc_proto){
      
      struct servent * service;
      
      if((service = getservbyport(htons(port), svc_proto)))
        printf("\t - %s port %u (%s) is open\n", svc_proto, port, service->s_name);
      else
        printf("\t - %s port %u is open\n", svc_proto, port);
      
      
    }
    
    
    Currently reading: Network Warrior, Unix Network Programming by Richard Stevens
  • Options
    paul78paul78 Member Posts: 3,016 ■■■■■■■■■■
    I just tried it on Linux - it seems to work pretty quick for me. About 1000 tcp ports per second. Are you expecting something else?
  • Options
    CodeBloxCodeBlox Member Posts: 1,363 ■■■■□□□□□□
    Well, it works fine for the first IP, really fast but when it gets to 192.168.0.2 it goes at a snails pace... Come to think of it... I think I know why that is. There is no host at that address! :P
    Currently reading: Network Warrior, Unix Network Programming by Richard Stevens
  • Options
    paul78paul78 Member Posts: 3,016 ■■■■■■■■■■
    Opps - I was reading your note incorrectly - yes - in that situation - connect() will block while it waits for the SYN packet response.

    [edit] - Actually - I'm not sure that my theory makes sense. The connect() will block even if there is a host at that address but the target host doesn't respond to the SYN.
  • Options
    CodeBloxCodeBlox Member Posts: 1,363 ■■■■□□□□□□
    What I could do is assume some threshold of ports that I fail to connect on and test errno for ETIMEDOUT... Maybe this is a valid way of checking for now until I buckle down on raw sockets. As far as I know though, connect won't block forever, it'll eventuall return something and if failed set errno.
    Currently reading: Network Warrior, Unix Network Programming by Richard Stevens
  • Options
    paul78paul78 Member Posts: 3,016 ■■■■■■■■■■
    Yeah - that could work. I looked a bit closer and it looks like my hosts are returning RST so getting ECPNNREFUSED - so that explains why I was seeing the faster connect() fails. It's the ETIMEDOUT that's the issue.

    Sounds like you understand that the right way is to use SOCK_RAW. icon_smile.gif Big fun!
  • Options
    CodeBloxCodeBlox Member Posts: 1,363 ■■■■□□□□□□
    Got it working as desired for now (continues past "offline" host) by checking if errno == EHOSTUNREACH. See below:
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <errno.h>
    
    
    #define NET_MASK 0XFFFFFFFF
    #define PORT_LIMIT 0XFFFF
    #define BASE_PORT 0X0000
    
    
    void get_addr_space(unsigned long int *, unsigned long int *, struct in_addr, struct in_addr);
    void scan_addr(unsigned long int, struct sockaddr_in *, unsigned short);
    int test_port(int, unsigned short, struct sockaddr_in *, char *);
    void port_info(unsigned short, char *);
    
    
    
    
    int main(int argc, char ** argv){
      
      unsigned short port = BASE_PORT;
      unsigned long int curr_addr = 0, max_addr = 0;
      struct sockaddr_in target;
      struct in_addr mask;
      
      inet_pton(AF_INET, argv[1], &target.sin_addr);
      inet_pton(AF_INET, argv[2], &mask);
      
      get_addr_space(&curr_addr, &max_addr, target.sin_addr, mask);
      
      target.sin_family = AF_INET;
      
      curr_addr = ntohl(curr_addr);
      max_addr = ntohl(max_addr);
      
      //printf("Min: %u, Max: %u\n", curr_addr, max_addr + 1);
      
      
      while(curr_addr < max_addr - 1)
        scan_addr(++curr_addr, &target, port);
      
      return 0;
    }
    
    
    void get_addr_space(unsigned long int * min, unsigned long int * max, struct in_addr subnet, struct in_addr mask){
      
      unsigned long int subnet_bits = subnet.s_addr & mask.s_addr;
      
      *min = subnet_bits;
      *max = subnet_bits;
      
      //*max = ((~*max + subnet_bits) & ~mask.s_addr) + *max;
      *max = NET_MASK & ~mask.s_addr + *max;
    }
    
    
    void scan_addr(unsigned long int dec_addr, struct sockaddr_in * target, unsigned short port){
      
      int client_socket = 0;
      char ipv4[INET_ADDRSTRLEN];
      
      target->sin_addr.s_addr = htonl(dec_addr);
      
      target->sin_port = htons(port);
      
      printf("Trying IP %s\n\n", inet_ntop(AF_INET, &target->sin_addr, ipv4, INET_ADDRSTRLEN)); 
      
      
      while(port < PORT_LIMIT){
        if(test_port(client_socket, port++, target, "tcp"))
          port_info(port - 1, "tcp");
        
        else if(errno == EHOSTUNREACH)
          break;
        else
          continue;
      }
      
    }
    
    
    int test_port(int client_socket, unsigned short port, struct sockaddr_in * target, char * svc_proto){
      
      printf("Trying %s port %u\r", svc_proto, port); 
      
      target->sin_family = AF_INET;
      target->sin_port = htons(port);
      
      if((client_socket = socket(AF_INET, (svc_proto == "tcp" ? SOCK_STREAM : SOCK_DGRAM), 0)) > 0){
        if(!connect(client_socket, (struct sockaddr *)target, sizeof(*target))){
          close(client_socket);
          return 1;
        }
        else
          close(client_socket);
        return 0;
      }else
        perror("");
      
    }
    
    
    void port_info(unsigned short port, char * svc_proto){
      
      struct servent * service;
      
      if((service = getservbyport(htons(port), svc_proto)))
        printf("%s port %u (%s) is open\n", svc_proto, port, service->s_name);
      else
        printf("%s port %u is open\n", svc_proto, port);
      
      
    }
    
    
    Currently reading: Network Warrior, Unix Network Programming by Richard Stevens
  • Options
    paul78paul78 Member Posts: 3,016 ■■■■■■■■■■
    Nice work CodeBlox!!! Now if you thread it, you can even make it more efficient icon_smile.gif What you can do is to create a separate threads as well - perhaps one thread per IP.
Sign In or Register to comment.