7
\$\begingroup\$

I’ve done port scanner program. It works well and accurate, but there is a problem.

It’s SUPER slow. That’s how it looks:

% ./scanner (/*means name of a program*/) 132.163.96.4 (/*desired IP address to connect*/) 12 15 (/*desired port range to scan*/)
%   Running the programm...
    FOR loop begin...
    Connecting to the port... 
    12 port is closed           // wait for a minute or two

    FOR loop begin...
    Connecting to the port...
    13 port is open             // instantly

    FOR loop begin...
    Connecting to the port...
    14 port is open             // instantly

    FOR loop begin...
    Connecting to the port...
    15 port is closed           // wait for a minute or two

    Program is executed!
% …

What happened here is simple to understand. Socket trying to connect through unsupported/closed port, and time that it takes to server finally declines our message to join is precisely 2 or 1 minute. Ok sorted out.

How to make happen to this program skips immediately this closed port? Ideally program should scan thousands of ports in a couple of seconds, 2 to be exact.

There is the code:

// Begining of the program
// Include Files
#include <stdio.h>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <cstdlib>
#include <sys/select.h>

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

  // Declare variables
  struct sockaddr_in mysock;
  struct hostent *hostzahod;
  struct in_addr ip;
  struct timeval tv;
  int sock;
  int bytes;
  char buffer[BUFSIZ+1];

/* ----------------------If we specify ranges of ports --------------------------*/

  printf("Running the programm...\n");
  if (argc > 2)
  {
    int i, someVal, iscon;
    int RangeFrom = atoi(argv[2]);
    int RangeTo = atoi(argv[3]);
    fd_set ReadFileDesc, WriteFileDesc, ExceptFileDesc;

     for(i = RangeFrom; i <= RangeTo; i++) {

      printf("FOR loop begin...\n");
      sock = socket(AF_INET, SOCK_STREAM, 0);
      bzero(&mysock, sizeof mysock);

      tv.tv_sec         = 1;
      mysock.sin_family = AF_INET;
      mysock.sin_port   = htons(i);

      inet_pton(AF_INET, argv[1], &ip);
      hostzahod = gethostbyaddr((const void*)&ip, sizeof ip, AF_INET);

      bcopy(hostzahod->h_addr_list[0],&mysock.sin_addr, hostzahod->h_length);

      // start scanning for open ports
      printf("Connecting to the port...\n");
      if (connect(sock, (struct sockaddr*)&mysock, sizeof mysock) < 0) {
        printf("%d port is closed\n\n", i);
        close(sock);

      } else {
        printf("%d port is open\n\n", i);
        close(sock);

      } // closing FOR loop

    } // closing IF loop

  printf("Programm is executed!\n");


Originally program divided by two parts, so don't pay attention to "If we specify ranges of ports" comment.

\$\endgroup\$
3
  • 5
    \$\begingroup\$ Are you trying to program in C or C++? The code looks almost completely C, the only thing that is C++ is #include <string> and #include <cstdlib>. \$\endgroup\$
    – G. Sliepen
    Commented Apr 5 at 18:08
  • \$\begingroup\$ @G.Sliepen this is c++ \$\endgroup\$
    – Eudziro
    Commented Apr 5 at 18:09
  • 1
    \$\begingroup\$ To scan all ports in a couple of seconds, you will need to scan multiple ports in parallel, eg using threads (pthreads in C probably). \$\endgroup\$
    – abligh
    Commented Apr 7 at 16:09

3 Answers 3

8
\$\begingroup\$

Skipping blocked ports quickly

It is possible to reduce the timeout for connect() when trying to connect to unresponsive ports, by setting the TCP_USER_TIMEOUT option to some low value (see also this post). However, its effect depends on the exact operating system you are using, and it might not be possible to set a very low timeout. Another issue is that sometimes network packets are dropped, or the network is congested and packets arrive a bit late. So you don't want to give up on a connection too quickly, otherwise you falsely think a port is blocked.

The other option is to have many connect() calls running in parallel. There are two ways to do that. The first is to use multiple threads; just have one thread per connect(). A possible problem here is that you might run out of filedescriptors.

The second way is to make the socket non-blocking by setting the O_NONBLOCK flag using fcntl(). That way, connect() will return immediately, and you can use poll() or select() at a later time to check whether any activity happened (either it being open or explicitly closed), or nothing (which is probably a blocked port). This avoids having to use threads. You still have to worry about the maximum number of filedescriptors you can have open at the same time.

Tools like nmap that can scan thousands of ports per second use another method entirely: they don't use connect(), but rather send raw TCP SYN packets and watch the network interface for responses to those packets.

Use getaddrinfo() instead of inet_ntop()/gethostbyaddr()

With inet_ntop() you need to know whether the input is an IPv4 or IPv6 address beforehand, and gethostbyaddr() has been removed in POSIX.1-2008. Don't use these functions anymore, instead use the much better getaddrinfo() to convert textual hostnames and IP addresses to struct sockaddrs.

C vs. C++

Your code can be compiled using a C compiler if you replace:

#include <string>
…
#include <cstdlib>

With:

#include <string.h>
…
#include <stdlib.h>

Because apart from those includes, there is nothing C++ about your code.

\$\endgroup\$
4
  • 3
    \$\begingroup\$ Indeed, #include <string.h> and #include <stdlib.h> are legal in C++, too. \$\endgroup\$
    – Davislor
    Commented Apr 6 at 17:00
  • \$\begingroup\$ Nice answer but I don't need to worry about filedescriptors in that particular program because every time when i increments, socket(fd) opens, it do something, and is closes (filedescriptor deleting). So in some others program it's need to worry about it but in scanner don't. \$\endgroup\$
    – Eudziro
    Commented Apr 7 at 19:13
  • 5
    \$\begingroup\$ You say you want to scan thousands of ports in 2 seconds. If you do them one by one, then you only have at most 2 milliseconds per port to send a packet and wait for a response. That might work on an Ethernet LAN, but for anything else (pinging computers over the Internet, or even local Wifi) that is not long enough. So to get the performance you want, you really have to do things in parallel, one way or another. \$\endgroup\$
    – G. Sliepen
    Commented Apr 7 at 20:51
  • 1
    \$\begingroup\$ Advise using non-blocking sockets over threads. Though I have not used it in quite this situation, so I could be wrong. Scale using threads (to the thousands range) is harder and going to use up significant resources. \$\endgroup\$ Commented Apr 9 at 22:28
10
\$\begingroup\$

From atoi()'s man page:

BUGS

errno is not set on error so there is no way to distinguish between 0 as an error and as the converted value. No checks for overflow or underflow are done. Only base-10 input can be converted. It is recommended to instead use the strtol() and strtoul() family of functions in new programs.

As mentioned above, use the strtol() and strtoul() family of functions.


From bcopy()'s man page:

HISTORY

4.3 BSD.

Marked as LEGACY in POSIX.1-2001: use memcpy(3) or memmove(3) in new programs. Note that the first two arguments are interchanged for memcpy(3) and memmove(3). POSIX.1-2008 removes the specification of bcopy().

The function declaration also has the [[deprecated]] attribute on it. Switch to memcpy().


Assuming you're not using C89 (a 35 years old standard), you do not need to declare all variables at the top of each block. I suggest declaring them at the first point of use.


Comments like // Closing FOR loop and // Closing IF loop are bad and not helpful. Firstly, they add no useful information to the code. Secondly, everyone can tell where the matching brace is ending. Thirdly, if is not a loop. Simply elide them.


You've included sys/select.h, but aren't using select() in your code. You should only be including what you're using.


Your claim:

this is c++

is true in that you've used cstdlib instead of stdlib.h and are compiling the program with a C++ compiler. Otherwise, I see nothing specific to C++ in your code. (If you were to change them to their C counterparts, your code would be legal C and legal C++ and would compile under a C compiler and a C++ compiler.)

You're also not using anything from the string header, and have failed to include strings.h which defines bcopy().


if (argc > 2)

Allows more than just 3 arguments. One could pass 5, or 50. You will be limited to the maximum size of a signed integer (implementation-dependent but guaranteed to be at least 32,767). Currently, your program is ignoring extra arguments.

If you do not desire that, consider changing the controlling expression to:

if (argc == 3) 
\$\endgroup\$
2
  • 2
    \$\begingroup\$ +1 for the bad comments \$\endgroup\$ Commented Apr 9 at 22:32
  • 1
    \$\begingroup\$ I note also that OP includes <cstdlib> which declares std::atoi() but then proceeds to assume global-namespace atoi() is available. So it's really not good C++. \$\endgroup\$ Commented Apr 12 at 7:36
1
\$\begingroup\$

If you want to scan thousands of ports per second it must be done using UDP packets instead of TCP, as UDP packets do not need to wait for confirmation that the packet was delivered successfully or not.

To use UDP, you would begin by changing this line:

sock = socket(AF_INET, SOCK_STREAM, 0);

to this:

sock = socket(AF_INET, SOCK_DGRAM, 0);

You will need to adjust the rest of your script as well to properly listen for the responses, there is a pretty good example script of UDP in C at this link: https://matrixsust.blogspot.com/2011/10/udp-server-client-in-c.html

An alternative, and potentially much more powerful solution is to invoke nmap from your program and listen for the responses by reading the piped file. Nmap's API is written in Lua and I do not think there are any C/C++ ports available, and so you would probably want to do something like the example shown here which invokes nmap as a sub-process and then reads the response from the piped output: https://stackoverflow.com/a/49841181

Nmap is generally considered the best port mapping program and is especially fast, and also has a wide range of command options that may give you a lot of advanced options if you can integrate with it. You can find the Nmap documentation here to get an idea of what all it can do: https://nmap.org/book/man.html

You may try looking around for a more plug-and-play solution to interface with Nmap from C, I did some searching but did not find one, but the example I linked to should get the job done if you can write the code to fire off the Nmap command and then listen for responses from it, and then as I said you would have the wide variety of options it supports.

If you do stick with the method of creating the socket yourself though then by using UDP you should see the instantaneous scanning you are going for.

\$\endgroup\$
5
  • \$\begingroup\$ The same is true of TCP when port scanning, the port may respond or it may not, but of course if you are scanning for a specific service that only uses TCP then opening a TCP/stream socket may be required, it depends on what you are scanning for. To receive the UDP packets, what you do is send one out and then have a loop running that listens for them. There is a loop in the example I posted, but here is a simpler single-packet example, at this link scroll down to where it says "Filename: UDPClient.c" for an easy example- scaler.com/topics/udp-server-client-implementation-in-c \$\endgroup\$ Commented Apr 8 at 12:36
  • \$\begingroup\$ One more thing that I personally do when reading a socket now is just use a JSON library to read from the socket, and what this does is take care of all of the background details of the connection including closing it when you are done reading from it, and this will perform a instant read and close out the socket when done instead of timing out. It is just so easy to use a JSON reader that I wouldn't do it any other way for TCP or even UDP for that matter, it just makes everything run smooth having it handle the connection state. I use json_dumps(), see - stackoverflow.com/a/40366346 \$\endgroup\$ Commented Apr 8 at 12:49
  • 3
    \$\begingroup\$ You cannot scan for open TCP ports using UDP packets. \$\endgroup\$
    – G. Sliepen
    Commented Apr 8 at 18:19
  • \$\begingroup\$ Of course you can, many applications allow you to poll or request information about the socket using poll and select for example, and you can even receive a UDP datagram response and initiate a TCP stream connection over that same connection in many applications, upgrading the connection from UDP to TCP. DNS will respond to either protocol and uses both interchangeably. Also, we already talked about the difference we didn't need a redundant statement of the fact. \$\endgroup\$ Commented Apr 8 at 23:06
  • 3
    \$\begingroup\$ that's a nonsense \$\endgroup\$
    – Eudziro
    Commented Apr 9 at 6:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.