Justin Braham Justin Braham -3 years ago 92
C Question

Speedup Windows C Port Scanner

So I developed a port scanner for C on windows but I have noticed on some IP's it runs very slowly. Here's my code for it:

DWORD WINAPI connectPortW(LPVOID lpParam)
{
HANDLE hStdout;
PMYDATA pDataArray;

WSADATA firstsock;
SOCKET s;
struct sockaddr_in sa;
int err;

char * openPorts = (char *)malloc(sizeof(char)*256);
memset(&openPorts[0], 0, strlen(openPorts));

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if(hStdout == INVALID_HANDLE_VALUE )
{
return 1;
}

pDataArray = (PMYDATA)lpParam;

strncpy((char *)&sa,"",sizeof sa);
sa.sin_family = AF_INET;

if (WSAStartup(MAKEWORD(2,0),&firstsock) != 0)
{
fprintf(stderr,"WSAStartup() failed");
exit(1);
}

sa.sin_addr.s_addr = inet_addr(pDataArray->ip);

s = socket(AF_INET, SOCK_STREAM, 0); //make net a valid socket handle
if(s < 0)
{
perror("\nSocket creation failed"); // perror function prints an error message to stderr
exit(1);
}

sa.sin_port = htons(pDataArray->port);
err = connect(s, (struct sockaddr *)&sa, sizeof sa);

//connection not accepted
if(err == SOCKET_ERROR)
{
printf("%s %-5d Winsock Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
strcpy("NULL", openPorts);
fflush(stdout);
}
//connection accepted
else
{
printf("%s %-5d accepted \n", pDataArray->ip, pDataArray->port);
sprintf(openPorts, "%i,", pDataArray->port);
if(shutdown(s, SD_BOTH ) == SOCKET_ERROR )
{
perror("\nshutdown");
exit(1);
}
}
closesocket(s);

fflush(stdout);

strcpy(pDataArray->openPorts, openPorts);

free(openPorts);

return 0;
}


Keep in mind I already use threads and each thread calls this function for a different port (0 - 1024) on the same IP.

So how can I speed this up? I keep seeing people talking about non-blocking, would that speed it up and if so how can I implement that. Thanks!

Edit: It is taking 614 seconds (10 minutes) to scan from 0 - 1024 on one of the aforementioned 'slow' ip's

Edit 2: I started trying to use non-blocking... Am I doing this right?

ioctlsocket(s, FIONBIO, &on);
connect(s, (struct sockaddr *)&sa, sizeof sa);
FD_ZERO(&fds);
FD_SET(s, &fds);

err = select(s, &fds, &fds, &fds, &tv);

if (err != SOCKET_ERROR && err != 0)
{
sprintf(openPorts + strlen(openPorts),"%i,", pDataArray->port);
}
closesocket(s);


Edit 3: It seems this new method is giving me inaccurate results but much much faster. I seem to be getting more open ports then compared to the results of running nmap on the same IP.

Answer Source

I see a lot of problems with your thread code:

  • it is leaking memory if a failure happens.

  • You are misusing strlen() when calling memset() on your openports variable. Just remove the memset() altogether and use calloc() or LocalAlloc(LMEM_ZEROINIT) instead when allocating openports. Or, just use the call stack instead, since the variable is small: char openPorts[256] = {0}; Or better, don't even use a local openports variable at all, simply write to pDataArray->openPorts directly when you have a result available.

  • You should not be using exit() at all. Use return instead.

  • it is not technically illegal to call WSAStartup()/WSACleanup() multiple times, since WinSock is reference counted, however it is best to call them only once at program startup/exit, not per thread. But, since you are calling WSAStartup(), you must call WSACleanup() to keep the WinSock reference count balanced.

  • What are you trying to do with strcpy("NULL", openPorts);? You are writing to read-only memory. I think you mean strcpy(openPorts, "NULL"); instead.

  • writing to pDataArray->openPorts is not thread-safe if multiple threads are sharing a single buffer (your use of , in your sprintf() string implies that may be the case). You need to synchronize access to the buffer when writing to it across multiple threads. you can use a critical section or mutex for that purpose.

That being said, you are using a blocking socket, so connect() will block the thread until WinSock times out internally, which may take awhile on slow networks. To speed it up, switch the socket to non-blocking mode using ioctrlsocket(FIONBIO), and then use select() to implement your own timeout for connect(), eg:

DWORD WINAPI connectPortW(LPVOID lpParam)
{
    PMYDATA pDataArray = (PMYDATA) lpParam;

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hStdout == INVALID_HANDLE_VALUE)
        return 1;

    WSADATA wsa;
    int err = WSAStartup(MAKEWORD(2,0), &wsa);
    if (err != 0)
    {
        fprintf(stderr, "%s %d WSAStartup() failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
        return 1;
    }

    SOCKET s = socket(AF_INET, SOCK_STREAM, 0); //make net a valid socket handle
    if (s == INVALID_SOCKET)
    {
        fprintf(stderr, "%s %d Socket creation failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
        WSACleanup();
        return 1;
    }

    u_long enabled = 1;
    if (ioctlsocket(s, FIONBIO, &enabled) == SOCKET_ERROR)
    {
        fprintf(stderr, "%s %d Socket non-blocking mode failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    struct sockaddr_in sa = {0};
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr(pDataArray->ip); 
    sa.sin_port = htons(pDataArray->port);

    if (connect(s, (struct sockaddr *)&sa, sizeof sa) == SOCKET_ERROR)
    {
        err = WSAGetLastError();
        if (err != WSAEWOULDBLOCK)
        {
            fprintf(stderr, "%s %d Socket connect failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
            closesocket(s);
            WSACleanup();
            return 1;
        }

        fd_set wfd, efd;

        FD_ZERO(s, &wfd);
        FD_SET(s, &wfd);

        FD_ZERO(s, &efd);
        FD_SET(s, &efd)'

        timeval timeout;
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;

        err = select(0, NULL, &wfd, &efd, &timeout);
        if (err == SOCKET_ERROR)
        {
            fprintf(stderr, "%s %d Socket select failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
            closesocket(s);
            WSACleanup();
            return 1;
        }

        if (err == 0)
        {
            // connect timeout
            closesocket(s);
            WSACleanup();
            return 0;
        }

        if (FD_ISSET(s, &efd))
        {
            err = 0;
            getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, sizeof err);
            closesocket(s);
            WSACleanup();

            switch (err)
            {
                case WSAETIMEDOUT: // connect timeout
                case WSAECONNREFUSED: // port closed
                    return 0;
            }

            fprintf(stderr, "%s %d Socket connect failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
            return 1;
        }
    }

    // connected!
    printf("%s %d accepted\n", pDataArray->ip, pDataArray->port);

    // note, this is not thread-safe! Need to sync access to openPorts...
    sprintf(pDataArray->openPorts + strlen(pDataArray->openPorts), "%d,", pDataArray->port);

    closesocket(s); 
    WSACleanup();
    return 0;
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download