Lernprojekt - Nonblocking TCP Portscanner

Greenleon

Greenleon

Tripel-As
Hallo,

ich moechte meine C-Kenntnisse etwas vertiefen. Dazu moechte ich einen Portscanner schreiben, der versucht eine TCP-Verbindung zu einem Bestimmten Port einer Liste von Hosts aufzubauen und meldet ob der Port auf dem Rechner offen ist.

In meiner Recherche bin ich ueber 3 Ansaetze gestolpert:
1. Verbindung aufbauen; timeout || sucess; naechste Verbindung, etc.
Das dauert natuerlich sehr lange, da 0 Parallelisierung

2. Threads: Wie 1., nur dass das ganze in mehreren Threads parallelisiert wird.
Machen wohl die meisten so. Ist aber mit ziemlich viel Ressourcen verbunden und soll auch nicht so effektiv sein.

3. Die NMAP-Methode: Das ganze wird mit nichtblockenden Ports gemacht, die mit Select ueberwacht werden. Diese Methode moechte ich auch implementieren.

Leider bin ich nicht auf wirklich brauchbaren Quelltext gestossen, an dem ich mich orientieren koennte, deswegen hab ich ein paar grundsaetzliche Fragen.

Was ich bereits verstanden hab (glaube ich):
- Ich gehe in einer Schleife die Liste der Zielrechner durch, erstelle jeweils einen socket(), connect()e diesen Socket im nonblocking Modus zum Zielrechner und fuege ihn dann in das fd_set fuer select() ein.

Ist das soweit richtig?
Was muss ich jetzt mit select anstellen?

Gruss
Leon
 
Hi,

ich will dich nicht ueberzeugen was anderes zu machen, aber solltest du dir Variante 2 nochmal angucken wollen, dann schau dir mal OpenMP an (nicht zu verwechseln mit OpenMPI ;)). Ich war letztens hin und weg wie einfach sich damit Sachen parallelisieren lassen, ohne dass du deinen Code grossartig aendern musst. Natuerlich muss man einiges beachten, aber im grossen und ganzen ist der Aufwand wirklich minimal.

Von wegen select, hattest du diesen Thread hier schon gesehen?
http://www.unixboard.de/vb3/showthread.php?t=44172
Vielleicht gibt das schon Anregungen?

Leider bin ich nicht auf wirklich brauchbaren Quelltext gestossen, an dem ich mich orientieren koennte
Was ist mit nmap selbst? Zu komplex? (Ich hab da selbst nie reingeguckt.)

mfg,
bytepool
 
Der nmap Code. Naja, bis ich da die passende Stelle gefunden hab, bin ich 50. Den Thread kannte ich noch nicht. Ich werds mir mal zu Gemuete fuehren.

Nee Variante 2 wir hier stark kritisiert http://groups.google.de/group/de.or...&oe=UTF-8&q=dingler+portscanning&btnG=Google+.
Ist auch nachvollziehbar die Kritik. Und sooo schwer kann das mit select() nicht sein.

Hier mal, was ich heute geschafft hab:
Code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/select.h>

#define DEBUG 1

// error number
extern int errno;

const int MAX_SESSIONS = 255;
unsigned long ip_list[MAX_SESSIONS];
short ipc = 0;

int main(int argc, char** argv)
{
  return 1;
}

void scan()
{
  const unsigned short scanp = 337;
  bool open_list[MAX_SESSIONS];

  // fds
  fd_set master;
  fd_set read_fds;

  int fdmax = 0;

  // address information
  struct sockaddr_in tsin;

  // timeout
  struct timeval to;
  int i, s;

  // clear fd sets
  FD_ZERO(&master);

  i = 0;
  do{
     // fill in connection information
     memset(&tsin,0,sizeof(struct sockaddr_in));
     tsin.sin_family = AF_INET;
     tsin.sin_port = htons(scanp);
     tsin.sin_addr.s_addr = ip_list[i]; // take an ip from the list

     // open a new socket
     if(s = socket(AF_INET,SOCK_STREAM,0)==-1)
	fprintf(stderr,"Unable to open socket()! Errno: %i\n",errno);

     // set the socket to nonblocking-mode
     if(fcntl(s, F_SETFL, O_NONBLOCK)==-1)
        fprintf(stderr,"Unable to set socket to O_NONBLOCK! Errno: %i\n",errno);

     // TODO: set fdmax

     // connect
     if(connect(s,(struct sockaddr *) &tsin,sizeof(&tsin))==-1)
     {
        /*connect() returned with an error*/
	if(errno == EINPROGRESS || errno == EALREADY)
	   /*the errno indicates this socket can be monitored with select()*/
           FD_SET(s,master);
        else
	   /*something went wrong*/
	   fprintf(sterr,"Unable to connect()! Errno: %i\n",errno);
     }
     else
     {
	/*connect() succeeded; remember and close() socket*/
	open_list[i] = true;
	if(close(s) == -1)
	   fprintf(stderr,"Unable to close() socket! Errno: %i\n",errno);
     }

  }while(++i <= ipc)

  //TODO: select the list


  //TODO: print results
}


/* add an IP-address of the type "ccc.ccc.ccc.ccc" to the list */
int addip(char* ip)
{
   if(ipc >= MAX_SESSIONS)
      return 1;

   ip_list[ipc++] = inet_addr(ip);
   return 0;
}
 
Zuletzt bearbeitet:
Soo eine erste Version laeuft :-).

Code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/select.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#define DEBUG 1

// error number
extern int errno;

#define MAX_SESSIONS 255
unsigned long int ip_list[MAX_SESSIONS];
int open_list[MAX_SESSIONS] = {0};
int fd_list[MAX_SESSIONS] = {-1};

short ipc = 0;

char * ntoa(unsigned long);
int add_ip(char*);
void scan(const int, const unsigned long*, int*);

int main(int argc, char** argv)
{
 
  char * tip = malloc(16);
  int i;

  for(i=0;i<255;i++)
  {
     sprintf(tip,"192.168.0.%i",i);
     add_ip(tip);
  }


  scan(MAX_SESSIONS, ip_list, open_list);

  return 0;
}

void scan(const int num, const unsigned long* ip_list, int* open_list)
{
  const unsigned short scanp = 337;

  // fds
  fd_set master;
  fd_set tfds;
  int fds[num];

  int fdmax = -1;

  // address information
  struct sockaddr_in tsin;

  // timeout
  struct timeval to;

  int i, sockfd, sret, scount;

  // clear fd sets
  FD_ZERO(&master);

  scount = 0;

  i = 0;
  do{
     // fill in connection information
     memset(&tsin,0,sizeof(tsin));
     tsin.sin_family = AF_INET;
     tsin.sin_port = htons(scanp);
     tsin.sin_addr.s_addr = ip_list[i]; // take an ip from the list

     // open a new socket
     if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1)
	perror("Unable to open socket()!");

     // set the socket to nonblocking-mode
     if(fcntl(sockfd, F_SETFL, O_NONBLOCK)==-1)
        perror("Unable to set socket to O_NONBLOCK!");

     // connect
     if(connect(sockfd, (struct sockaddr *) &tsin, sizeof(tsin)) == -1)
     {
        /*connect() returned with an error*/
	if(errno == EINPROGRESS || errno == EALREADY)
	{

           #ifdef DEBUG
              printf("DEBUG: Adding to select-list: socket %i\n",sockfd);
           #endif

	   /*the errno indicates this socket can be monitored with select()*/
           FD_SET(sockfd,&master); // select the socket
           
           // set fdmax
           fdmax = (fdmax>sockfd ? fdmax : sockfd);
	   scount++;
	   fd_list[i] = sockfd;
        }
        else
	   /*something went wrong*/
	   perror("Unable to connect()!");
     }
     else
     {
	/*connect() succeeded; remember and close() socket*/
	open_list[i] = 1;
        printf("Port %i on host %s is open.\n",scanp,ip_list[i]);

	if(close(sockfd) == -1)
	   perror("Unable to close() socket!");
     }

  }while(++i < ipc);

  /* only select() if master contains at least one fd */
  if(fdmax >=0)
  /* the select() loop */
  while(scount>0)
  {

    #ifdef DEBUG
       printf("DEBUG: Setting timeout\n");
    #endif

    // set timeout. reset after every select, because select() may alter it
    to.tv_sec = 2;
    to.tv_usec = 0;
 
    // copy the fd_list
    tfds = master;
 
    #ifdef DEBUG
       printf("DEBUG: Selecting. fdmax %i; ipc %i\n",fdmax,ipc);
    #endif

    // select the list
    sret = select(fdmax+1,&tfds,NULL,NULL,&to);
    

    if(sret == -1)
    {
      perror("Unable to select()!");
      return;
    }else if(sret == 0)
    {
      // timed out... the remaining hosts are probably down
       #ifdef DEBUG
          printf("DEBUG: Select timed out. Finishing...\n",fdmax);
       #endif
      break;
    }else
    {
      // something changed
      #ifdef DEBUG
          printf("DEBUG: Select returned %i.\n",sret);
      #endif

      for(i=0;i<ipc;i++)
      {
        if(fd_list[i] > 0 && FD_ISSET(fd_list[i],&tfds))
        {
           /* got an answer. remove it from master and close socket. */

           printf("%s:%i is open.\n",ntoa(ip_list[i]),scanp);
           FD_CLR(fd_list[i],&master);
           scount--;

	   if(close(fd_list[i]) == -1)
	      perror("Unable to close() socket!");
        }
      }
    }
  }
}



/* add an IP-address of the type "ccc.ccc.ccc.ccc" to the list */
int add_ip(char* ip)
{
   if(ipc >= MAX_SESSIONS)
      return 1;

   ip_list[ipc++] = inet_addr(ip);
   return 0;
}

char * ntoa(unsigned long in)
{
   struct sockaddr_in addr;
   memset(&addr,0,sizeof(addr));
   addr.sin_addr.s_addr = in;
   return inet_ntoa(addr.sin_addr);
}

Es wird ueberprueft, welche Hosts im Bereich 192.168.0.0-254 einen offenen Port 254 haben.

Was sehr seltsam ist: Wenn ich das Timeout fuer Select auf >= 3 Sekunden setze werden alle Hosts gelistet, als ob alle einen offenen Port 337 haetten. Fuer to < 3s funktioniert alles korrekt. Hat jemand ne Idee, woran das liegen koennte?
 
Wenn du das select timeout grösser als 3 Sekunden einstellst, dann ist es anscheinend größer als das connect timeout. In diesem Fall wird also das connect mit einem timeout abgeschlossen und das select meldet dir, dass der Filedescriptor nun wieder zum lesen (für schreiben und exception verwendest du ja NULL) verwendet werden kann. Der select lässt sich nicht darüber aus, ob der connect erfolgreich war.
Das kannst du am einfachsten mit einem write von 0 bytes oder mit einem Aufruf von getpeername nach dem return von select feststellen. Ist der write von 0 bytes oder der Aufruf von getpeername erfolgreich, so hast du dich erfolgreich zu dem entsprechenden Port verbunden. Ansonsten war der connect nicht erfolgreich.

LG,
Faxe
 
Wenn du das select timeout grösser als 3 Sekunden einstellst, dann ist es anscheinend größer als das connect timeout. In diesem Fall wird also das connect mit einem timeout abgeschlossen und das select meldet dir, dass der Filedescriptor nun wieder zum lesen (für schreiben und exception verwendest du ja NULL) verwendet werden kann. Der select lässt sich nicht darüber aus, ob der connect erfolgreich war.
Das kannst du am einfachsten mit einem write von 0 bytes oder mit einem Aufruf von getpeername nach dem return von select feststellen. Ist der write von 0 bytes oder der Aufruf von getpeername erfolgreich, so hast du dich erfolgreich zu dem entsprechenden Port verbunden. Ansonsten war der connect nicht erfolgreich.

LG,
Faxe

Ah, danke! Klingt plausibel.
Sollte aber nicht noetig sein. Nach 1 Sekunde muessten doch alle Verbindungen stehen, selbst bei hoher Latenz. Was meinst du?
 
Für ein lokales Netzwerk sollte eine Sekunde ausreichen.
Aber wie gesagt, eigentlich kann (bzw. darf?) man vom return von select nicht automatisch daraus schliessen, ob der connect erfolgreich war oder nicht (vielleicht gibt es andere Scenarios, in denen der connect nicht in ein timeout läuft sondern schon vorher darauf kommt, dass keine Verbindung möglich ist).

LG,
Faxe
 
So hier ist eine erste funktionierende Version. qt-GUI folgt ;)

Code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/select.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#undef DEBUG 

const int MAX_SESSIONS = 255;


unsigned long int * ip_list;
int * open_list;
int * fd_list;
const unsigned short scanp = 445;
short ipc = 0;

char * ntoa(unsigned long);
int add_ip(char*);
void scan(const int, const unsigned long*, int*);

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

  ip_list = malloc(MAX_SESSIONS * sizeof(unsigned long int));
  open_list = malloc(MAX_SESSIONS * sizeof(int));
  fd_list = malloc(MAX_SESSIONS * sizeof(int));

  memset(open_list,0,MAX_SESSIONS * sizeof(int));
  memset(fd_list,-1,MAX_SESSIONS * sizeof(int));

  char * tip = malloc(16);
  int i;

  for(i=1;i<10;i++)
  {
     sprintf(tip,"192.168.0.%i",i);
     add_ip(tip);
  }

  scan(MAX_SESSIONS, ip_list, open_list);

  printf("scanned port %i on %i hosts\n",scanp,ipc);
  printf("%-15s\t%-10s\n","host","status");
  printf("----------------------\n");
  for(i=0;i<ipc;i++)
     printf("%-15s\t%-10s\n",ntoa(ip_list[i]), (open_list[i] ? "open" : "closed" ));
  

  return 0;
}

void scan(const int num, const unsigned long* ip_list, int* open_list)
{
  char * buffer = "test";
  // fds
  fd_set master;
  fd_set tfds;
  int fds[num];

  int fdmax = -1;

  // address information
  struct sockaddr_in tsin;

  // timeout
  struct timeval to;

  int i, sockfd, sret, scount;

  // clear fd sets
  FD_ZERO(&master);

  scount = 0;

  i = 0;
  do{
     // fill in connection information
     memset(&tsin,0,sizeof(tsin));
     tsin.sin_family = AF_INET;
     tsin.sin_port = htons(scanp);
     tsin.sin_addr.s_addr = ip_list[i]; // take an ip from the list

     // open a new socket
     if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1)
     {
	perror("Unable to open socket()!");
        continue;
     }

     // set the socket to nonblocking-mode
     if(fcntl(sockfd, F_SETFL, O_NONBLOCK)==-1)
     {
        perror("Unable to set socket to O_NONBLOCK!");
        continue;
     }

     // connect
     if(connect(sockfd, (struct sockaddr *) &tsin, sizeof(tsin)) == -1)
     {
        /*connect() returned with an error*/
	if(errno == EINPROGRESS || errno == EALREADY)
	{

           #ifdef DEBUG
              printf("DEBUG: Adding to select-list: socket %i\n",sockfd);
           #endif

	   /*the errno indicates this socket can be monitored with select()*/
           FD_SET(sockfd,&master); // select the socket
           
           // set fdmax
           fdmax = (fdmax>sockfd ? fdmax : sockfd);
	   scount++;
	   fd_list[i] = sockfd;
        }
        else
        {
           #ifdef DEBUG
              printf("DEBUG: fd: %i ",sockfd);
           #endif

	   /*something went wrong - close socket*/
	   perror("Unable to connect()!");
	
           if(close(sockfd) == -1)
	      perror("Unable to close() socket!");
        }
     }
     else
     {
	/*connect() succeeded; remember and close() socket*/
	open_list[i] = 1;

	if(close(sockfd) == -1)
	   perror("Unable to close() socket!");
     }

  }while(++i < ipc);

  /* only select() if master contains at least one fd */
  /* the select() loop */
  while(scount>0)
  {

    #ifdef DEBUG
       printf("DEBUG: Setting timeout\n");
    #endif

    // set timeout. reset after every select, because select() may alter it
    to.tv_sec = 1;
    to.tv_usec = 0;
 
    // copy the fd_list
    tfds = master;
 
    #ifdef DEBUG
       printf("DEBUG: Selecting %i fds\n",scount);
    #endif

    // select the list
    sret = select(fdmax+1,NULL,&tfds,NULL,&to);
    

    if(sret == -1)
    {
      perror("Unable to select()!");
      return;
    }else if(sret == 0)
    {
      // timed out... the remaining hosts are probably down
       #ifdef DEBUG
          printf("DEBUG: Select timed out. Finishing...\n",fdmax);
       #endif
      break;
    }else
    {
      // something changed
      #ifdef DEBUG
          printf("DEBUG: Select returned %i.\n",sret);
      #endif

      for(i=0;i<ipc;i++)
      {
        if(fd_list[i] > 0 && FD_ISSET(fd_list[i],&tfds))
        {
           /* the state of this fd changed. Try to write 0 bytes now */
           if(write(fd_list[i],buffer,0) >= 0)
              open_list[i] = 1;
           #ifdef DEBUG
           else
           {
              printf("DEBUG: %s:%i is closed.\n",ntoa(ip_list[i]),scanp);
              perror("DEBUG: Unable to write()");
           }
           #endif
           
           // remove fd from the masterlist dec scount and close sock
           FD_CLR(fd_list[i],&master);
           scount--;
	   if(close(fd_list[i]) == -1)
	      perror("Unable to close() socket!");
        }
      }
    }
  }
}



/* add an IP-address of the type "ccc.ccc.ccc.ccc" to the list */
int add_ip(char* ip)
{
   if(ipc >= MAX_SESSIONS)
      return 1;

   ip_list[ipc++] = inet_addr(ip);
   return 0;
}

char * ntoa(unsigned long in)
{
   struct sockaddr_in addr;
   memset(&addr,0,sizeof(addr));
   addr.sin_addr.s_addr = in;
   return inet_ntoa(addr.sin_addr);
}
 

Ähnliche Themen

Akonadi startet nicht mehr

CentOS 6.3 RADIUS - Keine Verbindung möglich

OpenVPN - Server kann clients nicht erreichen.

probleme mit select()

Bei PostgreSQL als anderer als der angemeldete Nutzer verbinden - Wo liegt der Fehler

Zurück
Oben