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.
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;
}