Nmap readingsGroup

Begonnen von igami, 03 April 2017, 17:07:36

Vorheriges Thema - Nächstes Thema

curt

Ich habe das mal laienhaft so übernommen, das ging so ohne Fehler:
define new_host_notify notify Netzwerk:new.host:..+ {\
my $newDeviceName = "";;\
my $ip = "";;\
my $regex = qr/host: (.*)? \(/p;;\
if ( $EVENT =~ /$regex/g )\
  {\
  $ip = $1;;\
  }\
$regex = qr/\./p;;\
my $subst = '_';;\
$newDeviceName = $ip =~ s/$regex/$subst/rg;;\
fhem("defmod $newDeviceName dummy");;\
fhem("setreading $newDeviceName IP $ip");;\
fhem("set $newDeviceName ".ReadingsVal("Netzwerk",$ip.'_state','unknown'));;\
#...\
}
attr new_host_notify room 05 Die Lage


Damit purzelt sowas in die fhem.cfg:

define 192_168_1_1 dummy
define 192_168_1_103 dummy
define 192_168_1_104 dummy


Das nehme ich jetzt mal als schönen Zwischenstand, da ich bis Ostern leider nur wenig Zeit haben werde. Herzlichen Dank!
RPI 4 - Jeelink HomeMatic Z-Wave

curt

Ganz kurz:
Es scheint mir (ungetestet) möglich, auf diesem Weg SYSMON automatisiert die IP der im Intranet befindlichen Geräte zuzuführen. Und die haben wir ja dank des NMAP-Moduls.

Je IP ein Device sowie zugehörige Attribute, das scheint automatisiert machbar. Insoweit habe ich mir SYSMON angesehen, das sollte gehen. Da hat man dann für Server Plattenbelegung und weitere Interna.

Mit einem ganz schweren Nachteil: Das läuft von FHEM-Server via TELNET. Das setzt voraus, dass auf jedem Zielsystem telnetd als Dienst installiert ist. Real schiebt SYSMON via TELNET-Login Abfrage-Befehle. Also Sicherheit sieht anders aus.

Allgemeiner:
Es stellt sich die Frage, was wir überhaupt wollen, @igami hat das vor langer Zeit sinngemäß mit 'wofür brauchst Du denn einen Portscan" auf den Punkt gebracht.

Es gibt genau zwei Versionen:

1) Zeitkritisch
Ich will genau sehen, wer JETZT (sagen wir: vor einer Minute) in meinem Netz ist. Und wie es dem geht.

2) Systemkritisch
Zeit ist relativ untergeordnet, ich will aber sehen wie es dem Plattenplatz geht, ob die Hardwareports Schluckauf haben, ob mal wieder ein Update anliegt. - Da reicht "einmal am Tag", vielleicht noch weniger.

1) und 2) beißen sich, das kann man nicht gleichzeitig haben.

Für 2) Muss definitiv ein aktiver Client auf der Seite der zu überwachenden Server sein. Soweit das hier als grundsätzliche Basis diskutierte Modul NMap besteht, muss man damit leben, dass im Intranet angemeldete Handys usw. genau nichts liefern, da können wir ja keinen Zulieferer installieren.

Grenzfall ist der von @supernova1963 immer wieder angeforderte Portscan. Der könnte durchaus in der Fallgruppe 2) einmal täglich laufen. Oder (ich bin nicht fähig, FHEM-Knöpfe mit Aktionen zu machen) der Portscan ist abhängig von einer realen Aktivität eines Nutzers (Nutzer klickt Link).

P.S: Da @supernova1963 sagte, dass er sich mit NMap nicht auskennt: Probiere mal dieses auf die IP Deines Netzes:
nmap -sS -O -p1-1024 192.168.1.123

Du erhältst die offenen Ports im Bereich 1 - 1024 (die möglichen offenen Ports gehen bis 65536, bis 1024 ist aber typisch). Du erhältst zudem eine von außen gemachte Abschätzung, welches Betriebssystem Dir da eigentlich antwortet.
RPI 4 - Jeelink HomeMatic Z-Wave

supernova1963

Vielen Dank für deine Nmap Parameter Empfehlung.
Sie hilft mir nicht wirklich, da insbesondere Homeautomation Geräte i.d.R. Ports > 1024 verwenden.
Ich hatte eher an:
sudo nmap -v -O 192.168.1.10
Den Unterschied sehe bzw. kenne ich nicht, aber OS X HighSierra, Tasmota oder ESPEasy werden in beiden Fällen nicht erkannt.
(Der Bitte um Erfassung des fingerprints in einem web-Formular, dass an eine Adresse, die ich nicht kenne, gesendet wird, habe ich bisher nicht erfüllt, weil ich den Inhalt bzw. die in meinem System mit root Rechten zusammengetragenen Daten nicht kenne.)
Insgesamt dauert der scan auch ohne Betriebssystemerkennung bei nicht Windows oder Linux Geräten ca. 1 bis 2 Minuten.

Weißt du, welche Daten in den Fingerprints abgelegt werden und gibt es die Möglichkeit z.B. über eine lokale Datei mit fingerprints die Erkennung gezielt zu erweitern und zu beschleunigen?

Danke

Gernot

P.S.: Hier eine erweiterte Variante mit Erstellung von Dummies dessen readings aktualisiert werden, wenn ein neuer nmap Scan gelaufen ist (mit Link auf die IP Adresse, unabhängig ob der Port 80 vorhanden ist oder nicht):


defmod nmapClientAddNew notify nmap:new.host:..+ {\
my $newDeviceName = "";;\
my $ip = "";;\
my $alias = "";;\
my $temp = "";;\
my $regex = qr/new host:\s(.*)?\s\((.*)\)/p;;\
\
if ( $EVENT =~ /$regex/g ) \
{\
$alias = $1;;\
$temp = $1;;\
$ip = $2;;\
}\
$regex = qr/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/p;;\
if ( $ip =~ /$regex/ )  \
{\
$regex = qr/\./p;;\
my ($A,$B,$C,$D) = split(/\./, $ip);;\
$newDeviceName = substr("000".$A, -3, 3)."_".substr("000".$B, -3, 3)."_".substr("000".$C, -3, 3)."_".substr("000".$D, -3, 3);;\
}\
else\
{\
$newDeviceName = $alias;;\
}\
\
fhem("defmod ".$newDeviceName." dummy");;\
fhem("attr ".$newDeviceName." room nmap");;\
fhem("attr ".$newDeviceName." group nmapClient");;\
fhem("attr ".$newDeviceName." devStateIcon present:10px-kreis-gruen absent:10px-kreis-rot .*:10px-kreis-gelb");;\
fhem("setreading ".$newDeviceName." IP ".$ip);;\
my $userreadings = ""\
."state { ReadingsVal('nmap','".$ip."_state','na');;;;},"\
."hostname { ReadingsVal('nmap','".$ip."_hostname','na');;;;},"\
."macAddress { ReadingsVal('nmap','".$ip."_macAddress','na');;;;},"\
."macVendor { ReadingsVal('nmap','".$ip."_macVendor','na');;;;},"\
."http80 { return('<html><a href=\"http://".$ip."\"><img src=\"/fhem/images/default/icoWelt.png\"></a></html>');;;;},"\
."lastSeen { ReadingsVal('nmap','".$ip."_lastSeen','na');;;;},"\
."uptime { ReadingsVal('nmap','".$ip."_uptime','na');;;;},"\
."uptimeText { ReadingsVal('nmap','".$ip."_uptimeText','na');;;;}";;\
fhem("attr ".$newDeviceName." userReadings ".$userreadings);;\
fhem("set ".$newDeviceName." ".ReadingsVal('nmap',$ip.'_state','na'));;\
}
attr nmapClientAddNew room nmap

defmod nmapClientUpdate notify nmap:done { fhem("set 192.168.* 1");;}
attr nmapClientUpdate room nmap



 

curt

Ich habe leider bis Ostern sehr wenig Zeit.

Zitat von: supernova1963 am 22 März 2018, 19:12:43
Vielen Dank für deine Nmap Parameter Empfehlung.
Sie hilft mir nicht wirklich, da insbesondere Homeautomation Geräte i.d.R. Ports > 1024 verwenden.

Das ist kein Problem. Die anzusprechenden Ports werden über den Parameter -p gesteuert. Wenn Du beispielsweise viel Zeit hast, kannst Du mal alle möglichen Ports abklappern: "-p1-65536". Das dauert natürlich. Du kannst auch gruppieren, dann wird mit Komma getrennt: "-p23,80,443,2222-2290" geht beispielsweise auch. Wenn Du also die Ports der von Dir genannten Systeme kennst, können wir uns die anderen Ports ja sparen.

Allgemein gilt:
Ein Portscan ist eine Sache, die man ab und an macht. Es erscheint sinnlos, das alle 5 Minuten oder jede Stunde zu machen. Einmal am Tag - ist im Grunde schon viel zu viel.

Zitat von: supernova1963 am 22 März 2018, 19:12:43
aber OS X HighSierra, Tasmota oder ESPEasy werden in beiden Fällen nicht erkannt.

Das liegt daran, dass nmap mit vielen Heuristiken eigentlich rät. Wenn ein System nur einen oder gar keinen Port offen hat, ist das ausnehmend schwierig - Stichwort Firewall. Aber dieses Feature ist auch nicht dafür gedacht, eine ordentliche Doku der eigenen Systemlandschaft anzulegen, das kommt schon aus dem Baukasten für den kleinen Nachwuchs-Hacker ...

Zitat von: supernova1963 am 22 März 2018, 19:12:43
Weißt du, welche Daten in den Fingerprints abgelegt werden und gibt es die Möglichkeit z.B. über eine lokale Datei mit fingerprints die Erkennung gezielt zu erweitern und zu beschleunigen?

Das sagt Dir nmap doch selbst. Da steht nach einem Scan (bei mir): "Read data files from: /usr/bin/../share/nmap". Das ist also real /usr/share/nmap - da liegen die von Dir gesuchten Dateien.

Vermutlich (!) kannst Du Dir neuere Dateien von der nmap-Projektseite holen, ich selbst habe das noch nie auch nur in Erwägung gezogen.

Zitat von: supernova1963 am 22 März 2018, 19:12:43
P.S.: Hier eine erweiterte Variante mit Erstellung von Dummies dessen readings aktualisiert werden, wenn ein neuer nmap Scan gelaufen ist (mit Link auf die IP Adresse, unabhängig ob der Port 80 vorhanden ist oder nicht):

Leider habe ich im Moment keine Zeit, sonst würde ich mir das mal genauer anschauen.

Ich hatte nun SYSMON derart angesehen, als das ich auf einem zu überwachenden Testsystem TELNET freischaltete. Und mal schaute, was da eigentlich passiert. Denn es scheint ja möglich, das Erstellen der neuen Device so anzupassen, dass die gleich in das Beuteschema von SYSMON fällt, also dessen Überwachung/Darstellung anheim fällt.

Mal ganz abgesehen von TELNET (fällt aus, damit machen wir das nie) ist da wieder mein Problem: Ich sehe die Readings, kann die aber im Grunde nicht formatieren - meine Schwachstelle.

Das von @Reinhart in https://forum.fhem.de/index.php/topic,85498.0.html vorgeschlagene xymon habe ich mir noch nicht daraufhin angesehen, ob man dem auch Devices unterschieben kann.
RPI 4 - Jeelink HomeMatic Z-Wave

supernova1963

Danke für die Erläuterung von Nmap.

Bei dem Thema Serverüberwachung bin ich raus.
Der Ansatz von fhem bzw. anderen Geräten Internas eines Servers abzufragen ist imo nicht der geeignete Ansatz. Server sollten sich, bis auf wenige Punkte z.B. LAN Status und ggf. offene Ports, selbstständig überwachen und wichtige Informationen von sich aus melden. Dafür gibt es etliche Tools je Betriebssystem. Um diese Daten in Fhem anzuzeigen, wäre z.B. ein Tool bzw. Script für ein Tool sinnvoll, das die gewünschten Informationen an einen MQTT Broker sendet. Diese können dann von Fhem empfangen und optisch aufbereitet angezeigt werden.

Ich wünsche dir frohe Ostern, und hoffe, dass du deine Ideallösung findest.

Gernot

curt

@supernova1963 @igami
Zitat von: supernova1963 am 22 März 2018, 19:12:43P.S.: Hier eine erweiterte Variante mit Erstellung von Dummies dessen readings aktualisiert werden, wenn ein neuer nmap Scan gelaufen ist (mit Link auf die IP Adresse, unabhängig ob der Port 80 vorhanden ist oder nicht):

Das habe ich testweise aufgenommen (siehe Grafik - das muss so aussehen?).

Ich habe nicht verstanden - erkläre bitte:
Was genau ist der Unterschied der bisherigen Version zu der nun von Dir vorgeschlagenen Version? Was ist da anders? Was sind die Vorteile?

Zitat von: supernova1963 am 23 März 2018, 05:55:44
Bei dem Thema Serverüberwachung bin ich raus.
Der Ansatz von fhem bzw. anderen Geräten Internas eines Servers abzufragen ist imo nicht der geeignete Ansatz. Server sollten sich, bis auf wenige Punkte z.B. LAN Status und ggf. offene Ports, selbstständig überwachen und wichtige Informationen von sich aus melden. Dafür gibt es etliche Tools je Betriebssystem. Um diese Daten in Fhem anzuzeigen, wäre z.B. ein Tool bzw. Script für ein Tool sinnvoll, das die gewünschten Informationen an einen MQTT Broker sendet. Diese können dann von Fhem empfangen und optisch aufbereitet angezeigt werden.

Ich glaube da liegt -mit Verlaub- der Denkfehler.
Natürlich kann ich beispielsweise Wettervorhersagen im Web abrufen. Und ich kann ich als iframe in FHEM einbinden. Geht, kein Problem. Trotzdem haben mehrere Programmierer mehrere Module geschrieben, um diese Informationen quasi nativ in FHEM zu haben.

Mit Serverüberwachung kenne ich mich aus - oder sagen wir besser: Lasse ich mich auskennen. Klar könnte ich irgendwo ein Nagios aufsetzen. Oder Xymon oderoderoder. Das ist aber alles nicht nativ, da ist dann immer ein weiterer Überwachungsserver im Spiel. Oder man packt das Nagios mit auf den Server, der auch FHEM hostet - ändert aber auch nichts.

Den wirklich genialen gedanklichen Ansatz hattest Du:
Jedes Gerät hinter einer IP ist ein Device. Ein Device mit ganz konkreten Eigenschaften. (Diese Eigenschaften haben wir immer nur nacheilend, nie realtime. Es kann bei FHEM nicht um realtime gehen!)

Selbstverständlich hast Du recht:
Es gibt für jede IP Eigenschaften, die man von außen sehen kann. Vermittels nmap beispielsweise. (Wobei nmap hier eine Doppelfunktion hat: Es liefert vor allem die Information, welche IP überhaupt verfügbar sind!).

Die internen Eigenschaften eines Servers kann nur der Server selbst liefern, natürlich hast Du recht.
Entweder pusht er Daten (mal lieber nicht) oder er stellt Daten für Poll zur Verfügung. DAS setzt zwingend ein wie auch immer geartetes Programm auf der Seite der/des zu überwachenden Systems voraus. Natürlich.

Sysmon löst das so:
Sysmon erwartet auf jedem zu überwachendem System einen offenen telnet-Port sowie gültigen Account. Dann loggt sich Sysmon ein, startet verschiedene Befehle. Und greift sich die Ausgaben und hat sie nativ in FHEM.

Es scheint mir überhaupt kein Problem, die bisherige nMap-Dummy-Device-Erstellung so umzubauen, dass Devices für Sysmon entstehen. Das scheint mir wirklich eine Fingerübung.

Ich schrecke trotzdem zurück. Weil diese Systemidee auf telnet aufbaut. Das ist schon hinreichend gruselig. Zudem ist der Befehlssatz eingeschränkt: Du bekommst die CPU-Temperatur eines "normalen" Linux-Systems - oder nicht von Raspberry: Dort ist der Befehl ein anderer. Und den kennt Sysmon nicht.

Ansich wollte ich nur sagen, dass DEINE Kernidee, dass jede IP für FHEM ein Device sein muss, wirklich genial ist.

P.S: Ganz oben bitte nicht vergessen: Was macht Dein neuer Codeentwurf im Gegensatz zum bisherigen besser/schöner/toller?

PP.S: Frohe Ostern!
RPI 4 - Jeelink HomeMatic Z-Wave

supernova1963

#66
Hallo curt,

Zitat"Muss dass so aussehen?"
Fast, da stimmt noch etwas mit dem userReading nicht, da der Status nicht 'absent' = rot oder 'present' = grün ist (alles andere ergibt den gelben Punkt).

Aber, diese Konkretisierung des notify von igami, sollte nur die Möglichkeit zeigen, dass man auf Basis von neu erkannten Netz-Klienten ein eigenes fhem device, hier als Beispiel auf Basis des dummy - Moduls erstellen kann.
Es ist der erste Schritt, nicht mehr und nicht weniger.

Der nächste Schritt ist, statt der Anlage von fhem devices auf Basis des dummy - Moduls, ein geeignetes bestehendes oder neues fhem Modul zu verwenden.
Ich habe mich vor einiger Zeit, nur zum Verstehen von fhem, 'mal an einem netClient - Modul versucht. Damals auf Basis von fing und mit der Identifikation über die MAC - Adresse.
In der Anlage stelle ich meinen auf Nmap und auf IP zur Identifikation angepassten ersten Grobentwurf eines 74_NmapClient.pm - Moduls zur ersten Demonstration meines Gedankens ein.

UNBEDINGT BEACHTEN:
ES IST NUR EIN GROBENTWURF AUSSCHLIESSLICH FÜR ENTWICKLUNGSUMGEBUNGEN ZUR DEMONSTRATION DES SCHEMAS, AUF KEINEN FALL PRODUKTIV EINSETZBAR!!!

Ich kann kein Perl, wie man unschwer erkennen kann, und, mit der XML Schnittstelle von Nmap bin ich auch nicht klargekommen!

Vielleicht kannst du oder ein anderer Perl-Kenner es optimieren/verbessern/erweitern und ein fhem - Modul daraus machen, dass entweder einzeln oder auf Basis des notify auf die neuen Geräte aus dem Nmap - Modul von igami anlegt.

Vorgehensweise zum Testen:

1. Schritt: Folgenden Code prüfen und ggf. in eine neue Datei /opt/fhem/FHEM/74_NmapClient.pm kopieren und speichern. 
# $Id: 74_NmapClient.pm$
##############################################
#
#     74_NmapClient.pm
#     FHEM module to check remote network device using Nmap.
#
#     Author:
#
#     This file is not part of fhem.
#                  ---
#     Fhem is free software: you can redistribute it and/or modify
#     it under the terms of the GNU General Public License as published by
#     the Free Software Foundation, either version 2 of the License, or
#     (at your option) any later version.
#
#     Fhem is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.
#
#     You should have received a copy of the MIT - License.
#
##############################################################################

package main;

use strict;
use warnings;
use Blocking;
use JSON;
use Data::Dumper;


sub NmapClient_Initialize($)
{
    my ($hash) = @_;

    $hash->{DefFn}         = "NmapClient_Define";
    $hash->{UndefFn}       = "NmapClient_Undefine";
    $hash->{DeleteFn}      = "NmapClient_Delete";
    $hash->{SetFn}         = "NmapClient_Set";
    $hash->{GetFn}         = "NmapClient_Get";
    $hash->{AttrFn}        = "NmapClient_Attr";
    #$hash->{ReadFn}        = "NmapClient_Read";
    #$hash->{ReadyFn}       = "NmapClient_Ready";
    $hash->{NotifyFn}      = "NmapClient_Notify";
    #$hash->{RenameFn}      = "NmapClient_Rename";
    #$hash->{ShutdownFn}    = "NmapClient_Shutdown";


    $hash->{AttrList} = ""
    ."NmapDeviceName "
    ."PortScan:on,off "
    ."PortScanInterval "
    ."Ping:off,on "
    ."PingInterval "
    ."disable:0,1"
    . $readingFnAttributes;
    return undef;
}

#######################################################################################
sub NmapClient_Define($$)
{
    my ($hash, $def) = @_;
    my @args = split("[ \t][ \t]*", $def);
    return "Usage: define <name> NmapClient <ID = Identifier>"  if(@args < 3);

    my $rc = `sudo nmap -V`;
    my $regex = qr/Nmap version\s(.*)\s\(/p;
    if ( $rc =~ /$regex/ ) {
        $hash->{NmapVersion} = $1;
      }
    else {
        return "Fehler bei der Überprüfung von Nmap!\nKann es sein, dass das Programm nicht installiert ist, oder der sudo Aufruf ohne Passwort nicht definiert ist?\nFür Debian sollte:\n sudo apt update &&sudo apt install Nmap ausreichen um Nmap zu intallieren.\nEintrag in sudoers: sudo visudo -f /etc/sudoers.d/nmap \n'fhem    ALL=(ALL) NOPASSWD: /usr/bin/nmap,'";
    }
    my ($name, $type, $id) = @args;
    $hash->{ID} = $id;
    readingsSingleUpdate($hash, "state", "Initialized", 1);
    $attr{$name}{"NmapDeviceName"} = "nmap" if (!defined($attr{$name}{"NmapDeviceName"}));
    $attr{$name}{"PortScan"} = "off" if (!defined($attr{$name}{"PortScan"}));
    $attr{$name}{"PortScanInterval"} = 60*60*24 if (!defined($attr{$name}{"PortScanInterval"}));
    $attr{$name}{"Ping"} = "off" if (!defined($attr{$name}{"UpdateScan"}));
    $attr{$name}{"PingInterval"} = 60*60*24 if (!defined($attr{$name}{"UpdateScanInterval"}));
    $attr{$name}{"group"} = "NmapClients" if (!defined($attr{$name}{"group"}));
    $attr{$name}{"room"} = AttrVal(AttrVal($name,"NmapDeviceName","nmap"),"room","nmap") if (!defined($attr{$name}{"room"}));
    $attr{$name}{"devStateIcon"} = "present:10px-kreis-gruen absent:10px-kreis-rot .*:10px-kreis-gelb" if (!defined($attr{$name}{"devStateIcon"}));
    NmapClient_SetNextTimer($hash,"all");

    delete $hash->{helper}{PORTSCANRUNNING_PID};
    delete $hash->{helper}{PINGRUNNING_PID};
    return undef;
}
#######################################################################################

#######################################################################################
sub NmapClient_Notify($$) {

    my ($hash, $dev_hash) = @_;
    my $name = $hash->{NAME};
    my $devName = $dev_hash->{NAME};
    my $regex = "";
    my $subst = "";

    return if(IsDisabled($name)); # Return without any further action if the device is disabled

    my $events = deviceEvents($dev_hash,1);

    return if( !$events );

    if($devName eq "global" && grep(m/^INITIALIZED|REREADCFG$/, @{$events}))
    {
      if (AttrVal($name,"PortScan","off") eq "on") {
        NmapClient_SetNextTimer($hash,"PortScan");
      }
      if (AttrVal($name,"Ping","off") eq "on") {
        NmapClient_SetNextTimer($hash,"Ping");
      }
      NmapClient_Basics($hash);
    }
    else {
      foreach my $event (@{$events}) {
        $event = "" if(!defined($event));
        if ($devName eq AttrVal($name,"NmapDeviceName","nmap")) {
          if (index($event,"done") != -1) {
            NmapClient_Basics($hash);
          }
        }
      }
    }
}


#######################################################################################
sub NmapClient_Undefine($$)
{
    my ($hash,$arg) = @_;

    RemoveInternalTimer($hash);

    BlockingKill($hash->{helper}{PORTSCANRUNNING_PID}) if(defined($hash->{helper}{SCANRUNNING_PID}));
    BlockingKill($hash->{helper}{PINGRUNNING_PID}) if(defined($hash->{helper}{PINGRUNNING_PID}));

    return undef;
}
#######################################################################################

#######################################################################################
sub NmapClient_Delete($$)
{
    my ($hash,$arg) = @_;

    RemoveInternalTimer($hash);

    BlockingKill($hash->{helper}{PORTSCANRUNNING_PID}) if(defined($hash->{helper}{SCANRUNNING_PID}));
    BlockingKill($hash->{helper}{PINGRUNNING_PID}) if(defined($hash->{helper}{PINGRUNNING_PID}));

    return undef;
}
#######################################################################################

#######################################################################################
sub NmapClient_Get($@)
{
  return undef;
}
#######################################################################################

#######################################################################################
sub NmapClient_Set($@)
{
  my ($hash, $name, $cmd, @args) = @_;
  my ($arg, @params) = @args;
  my $list = '';
  my $regex = '';
  my $subst = '';
  if ($cmd eq 'PortScan') {
      if ($arg eq 'on') {
        if (AttrVal($name,'PortScan','off') eq 'off') {
          $attr{$name}{"PortScan"} = $arg;
          NmapClient_StartPortScan($hash);
        }
        else {
          Log3 ($hash, 5, "$name: $cmd is already $arg");
        }
      }
      else {
          $attr{$name}{"PortScan"} = "off";
          BlockingKill($hash->{helper}{PORTSCANRUNNING_PID}) if(defined($hash->{helper}{PORTSCANRUNNING_PID}));
          RemoveInternalTimer($hash,"NmapClient_StartPortScan");
      }
  }
  elsif ($cmd eq 'PortScanInterval') {
      if ($arg < 180 || $arg >60*60*24) {
        $attr{$name}{"PortScanInterval"} = 60*60*24;
        NmapClient_SetNextTimer($hash,"PortScan");
      }
      else {
        $attr{$name}{"PortScanInterval"} = $arg;
        NmapClient_SetNextTimer($hash,"PortScan");
      }
  }
  elsif ($cmd eq 'Ping') {
      if ($arg eq 'on') {
        if (AttrVal($name,'Ping','off') eq 'off') {
          $attr{$name}{"Ping"} = $arg;
          NmapClient_StartPing($hash);
        }
      }
      else {
          $attr{$name}{"Ping"} = "off";
          BlockingKill($hash->{helper}{PINGRUNNING_PID}) if(defined($hash->{helper}{PINGRUNNING_PID}));
          RemoveInternalTimer($hash,"NmapClient_StartPing");
      }
  }
  elsif ($cmd eq 'PingInterval') {
      if ($arg < 60 || $arg >60) {
        $attr{$name}{"PingInterval"} = 60*60*24;
        NmapClient_SetNextTimer($hash,"Ping");
      }
      else {
        $attr{$name}{"PingInterval"} = $arg;
        NmapClient_SetNextTimer($hash,"Ping");
      }
  }
  elsif ($cmd eq 'BasicData') {
      NmapClient_Basics($hash);
  }
  elsif ($cmd eq 'disable') {
      if ($arg eq '1') {
        if (AttrVal($name,'disable','0') eq '0') {
          $attr{$name}{"disable"} = 1;
          BlockingKill($hash->{helper}{PORTSCANRUNNING_PID}) if(defined($hash->{helper}{PORTSCANRUNNING_PID}));
          RemoveInternalTimer($hash,"NmapClient_StartPortScan");
          BlockingKill($hash->{helper}{PINGRUNNING_PID}) if(defined($hash->{helper}{PINGRUNNING_PID}));
          RemoveInternalTimer($hash,"NmapClient_StartPing");
        }
      }
      else {
        if (AttrNum($name,"PortScanInterval",0) > 60*60*24 || AttrNum($name,"PortScanInterval",0) < 180) {
          $attr{$name}{"PortScanInterval"} = 60*60*24;
          NmapClient_SetNextTimer($hash,"PortScan");
        }
        else {
          NmapClient_SetNextTimer($hash,"PortScan");
        }
      }
  }
  else {
      $list = ""
        ."Disable:1,0"
        ." BasicData:noArg"
        ." PortScan:on,off"
        ." PortScanInterval"
        ." Ping:on,off"
        ." PingInterval";
      return "Unknown argument $cmd, choose one of $list";
  }
}
#######################################################################################

#######################################################################################
sub NmapClient_Attr($$$$)
{
    # Übergabeparamter auslesen
    my ($command,$name,$attribute,$val) = @_;
    # Einlesen des (device-)hash'es über den (als Paramter übergebenen device-)name
    my $hash = $defs{$name};
    # Beginn der Auswertung des ausgeführten Befehls:
    # Wenn der Befehl "set" ist:
    if ($command eq "set") {
        return fhem( "set $name disable $val" ) if ($attribute eq "disable");
        return fhem( "set $name PortScanInterval $val" ) if ($attribute eq "PortScanInterval");
        return fhem( "set $name PortScan $val" ) if ($attribute eq "PortScan");
        return fhem( "set $name PingInterval $val" ) if ($attribute eq "PingInterval");
        return fhem( "set $name Ping $val" ) if ($attribute eq "Ping");
        return fhem( "set $name BasicData" ) if ($attribute eq "BasicData");
    }
    # Sonst nur den Attributwert ändern
    else {
      $attr{$name}{$command} = $val;
      # Eintrag im Log (Level 5):
      Log3 ($hash, 5, "$hash->{NAME}_Attr: $attribute auf Value: ".$val);
    }
    return undef;
}
#######################################################################################

#######################################################################################
sub NmapClient_SetNextTimer($$)
{
    my ($hash,$scanmode) = @_;
    my $name = $hash->{NAME};
    my $functionName = "";
    my $rc = "";
    my $interval = "";
    if (!$scanmode) {
      my $scanmode = "";
    }
    if ($scanmode eq "PortScan" || $scanmode eq "Ping" || $scanmode eq "all") {
      if ($scanmode eq "PortScan" || $scanmode eq "all") {
          $functionName = "NmapClient_StartPortScan";
          $interval = AttrVal($name,"PortScanInterval",60*60*24);
          if (ReadingsVal($name,"PortScan","off") eq "off") {
              RemoveInternalTimer($hash,$functionName);
              return;
          }
          RemoveInternalTimer($hash,$functionName);
          InternalTimer(gettimeofday() + $interval, $functionName, $hash, 0);
          $rc = "Next timer ".$functionName.": ".FmtDateTime(gettimeofday() + $interval)."\n";
      }
      if ($scanmode eq "Ping" || $scanmode eq "all")  {
        $functionName = "NmapClient_StartPing";
        $interval = AttrVal($name,"PingInterval",60*60*24);
        if (ReadingsVal($name,"Ping","off") eq "off") {
            RemoveInternalTimer($hash,$functionName);
            return;
        }
        RemoveInternalTimer($hash,$functionName);
        InternalTimer(gettimeofday() + $interval, $functionName, $hash, 0);
        $rc = "Next timer ".$functionName.": ".FmtDateTime(gettimeofday() + $interval)."\n";
      }
      return;
    }
    else {
      RemoveInternalTimer($hash);
    }
}
#######################################################################################
#######################################################################################
sub NmapClient_StartPortScan($)
{
    my ($hash) = @_;
    return undef if (IsDisabled($hash->{NAME}));
    my $name = $hash->{NAME};
    return "PortScan = off!" if (AttrVal($name,"PortScan","off") eq "off");

    my $ip = ReadingsVal($name,"IP",undef);
    my $arg = $name."|".$ip;
    my $portname = "";
    my $blockingFn = "NmapClient_PortScan";
    my $finishFn = "NmapClient_PortScanDone";
    my $abortFn = "NmapClient_PortScanAbort";
    if (!(exists($hash->{helper}{PORTSCANRUNNING_PID}))) {
        for (my $i=0; $i <= 65000; $i++) {
          $portname = "PORT".substr("00000".$i, -5, 5);
          readingsSingleUpdate($hash, $portname, "closed", 1) if (defined(ReadingsVal($name,$portname,undef)));
        }
        $hash->{helper}{PORTSCANRUNNING_PID} = BlockingCall($blockingFn,$arg,$finishFn,600,$abortFn,$hash);
        Log3 $hash, 3, $hash->{NAME}." PortScan job run with PID: ".$hash->{helper}{PORTSCANRUNNING_PID}{pid}."!";
    }
    else {
        Log3 $hash, 3, $hash->{NAME}." Blocking Call PortScan is active, no new job started!";
        NmapClient_SetNextTimer($hash,"PortScan");
    }
}
#######################################################################################
#######################################################################################
sub NmapClient_PortScan($)
{
    my ($string) = @_;
    my ($name, $ip) = split("\\|", $string);
    my $hash = $defs{$name};
    my $result = "";
    my $rcCmd = "";
    my $scannedPorts = "";
    my $portPrefix = "";
    my $nmapCommand = "sudo nmap ".$ip;
    $result = `$nmapCommand`;
    #print $result;
    #my $regex = qr/Starting[\w\W]*at (?P<lastScan>.*)\sCE[\w\W]*Nmap scan report for (?P<netName>.*)\s\((?P<ip>.*)\)\s[\w\W]*up \((?P<latency>.*)\slatency[\w\W]*SERVICE\s*(?P<ports>.*[\w\W]*)\n\n[\w\W]*scanned\sin\s(?P<elapsed>.*)\sseconds/p;
    my $regex = qr/Starting[\w\W]*at (?P<lastScan>.*)\sCE[\w\W]*Nmap scan report for (?P<netName>.*)\s[\w\W]*up \((?P<latency>.*)\slatency[\w\W]*SERVICE\s*(?P<ports>.*[\w\W]*)\n[\w\W]*scanned\sin\s(?P<elapsed>.*)\sseconds/p;
    if ( $result =~ /$regex/ ) {

      $rcCmd = "deletereading $name PortScan.*;"
        ."setreading $name PORTScan_lastScan ".$+{lastScan}.";"
        ."setreading $name PORTScan_Name ".$+{netName}.";"
        # ."setreading $name PORTScan_IP ".$+{ip}.";"
        ."setreading $name PORTScan_latency ".$+{latency}.";"
        ."setreading $name PORTScan_Time ".$+{elapsed}.";";
    }
    $scannedPorts = $+{ports};
    if (!$scannedPorts || $scannedPorts eq "") {
      $rcCmd = $rcCmd."setreading PORTScan_OpenPorts 0;";
      return $name."|".$rcCmd;
    }

    $regex = qr/(?P<line>.*)\s?/p;
    my $portlist = "<html><table>";
    my @ports = "";
    my $i = 0;
    $rcCmd = $rcCmd."deletereading $name PORT.*;";
    my @matches = ($scannedPorts =~ /$regex/g);
    foreach my $xline (@matches) {
        if (index($xline,"MAC Address:") == -1) {
          $regex = qr/(?P<port_ID>.*)\/(?P<port_Name>.*) [\s]*(?P<port_State>.*) \s(?P<port_Description>.*)\s?/p;
          if ( $xline =~ /$regex/g ) {
            my $portPrefix = "PORT".substr("00000".$+{port_ID}, -5, 5);
            $rcCmd = $rcCmd.""
              ."setreading ".$name." ".$portPrefix."_ID ".$+{port_ID}.";"
              ."setreading ".$name." ".$portPrefix."_Name ".$+{port_Name}.";"
              ."setreading ".$name." ".$portPrefix."_State ".$+{port_State}.";"
              ."setreading ".$name." ".$portPrefix."_Description ".$+{port_Description}.";"
              ."";

            if ($+{port_ID} eq "80") {
              $rcCmd = $rcCmd.""
              ."setreading ".$name." ".$portPrefix." <html><table><tr><td style=\"text-align:Right;; width:10%\">".$+{port_ID}."</td><td style=\"text-align:Center;; width:10%\">".$+{port_Name}."</td><td><a href=\"http://".ReadingsVal($name,"IP","")."\">".$+{port_Description}."</a></td><td style=\"text-align:Left;; width:10%\">".$+{port_State}."</td></tr></table></html>;"
              ."";
              $portlist = $portlist."<tr><td style=\"text-align:Right;; width:10%\">".$+{port_ID}."</td><td style=\"text-align:Center;; width:10%\">".$+{port_Name}."</td><td><a href=\"http://".ReadingsVal($name,"IP","")."\">".$+{port_Description}."</a></td><td style=\"text-align:Left;; width:10%\">".$+{port_State}."</td></tr>";
            }
            elsif ($+{port_ID} eq "443") {
              $rcCmd = $rcCmd.""
                ."setreading ".$name." ".$portPrefix." <html><table><tr><td style=\"text-align:Right;; width:10%\">".$+{port_ID}."</td><td style=\"text-align:Center;; width:10%\">".$+{port_Name}."</td><td><a href=\"https://".ReadingsVal($name,"IP","")."\">".$+{port_Description}."</a></td><td style=\"text-align:Left;; width:10%\">".$+{port_State}."</td></tr></table></html>;"
                ."";
              $portlist = $portlist."<tr><td style=\"text-align:Right;; width:10%\">".$+{port_ID}."</td><td style=\"text-align:Center;; width:10%\">".$+{port_Name}."</td><td><a href=\"https://".ReadingsVal($name,"IP","")."\">".$+{port_Description}."</a></td><td style=\"text-align:Left;; width:10%\">".$+{port_State}."</td></tr>";
            }
            elsif ($+{port_ID} eq "21") {
              $rcCmd = $rcCmd.""
                ."setreading ".$name." ".$portPrefix." <html><table><tr><td style=\"text-align:Right;; width:10%\">".$+{port_ID}."</td><td style=\"text-align:Center;; width:10%\">".$+{port_Name}."</td><td><a href=\"ftp://".ReadingsVal($name,"IP","")."\">".$+{port_Description}."</a></td><td style=\"text-align:Left;; width:10%\">".$+{port_State}."</td></tr></table></html>;"
                ."";
              $portlist = $portlist."<tr><td style=\"text-align:Right;; width:10%\">".$+{port_ID}."</td><td style=\"text-align:Center;; width:10%\">".$+{port_Name}."</td><td><a href=\"ftp://".ReadingsVal($name,"IP","")."\">".$+{port_Description}."</a></td><td style=\"text-align:Left;; width:10%\">".$+{port_State}."</td></tr>";
            }
            elsif ($+{port_ID} eq "445") {
              $rcCmd = $rcCmd.""
                ."setreading ".$name." ".$portPrefix." <html><table><tr><td style=\"text-align:Right;; width:10%\">".$+{port_ID}."</td><td style=\"text-align:Center;; width:10%\">".$+{port_Name}."</td><td><a href=\"smb://".ReadingsVal($name,"IP","")."\">".$+{port_Description}."</a></td><td style=\"text-align:Left;; width:10%\">".$+{port_State}."</td></tr></table></html>;"
                ."";
              $portlist = $portlist."<tr><td style=\"text-align:Right;; width:10%\">".$+{port_ID}."</td><td style=\"text-align:Center;; width:10%\">".$+{port_Name}."</td><td><a href=\"smb://".ReadingsVal($name,"IP","")."\">".$+{port_Description}."</a></td><td style=\"text-align:Left;; width:10%\">".$+{port_State}."</td></tr>";
            }

            else {
              $rcCmd = $rcCmd.""
                ."setreading ".$name." ".$portPrefix." <html><table><tr><td style=\"text-align:Right;; width:10%\">".$+{port_ID}."</td><td style=\"text-align:Center;; width:10%\">".$+{port_Name}."</td><td>".$+{port_Description}."</td><td style=\"text-align:Left;; width:10%\">".$+{port_State}."</td></tr></table></html>;"
                ."";

                $portlist = $portlist."<tr><td style=\"text-align:Right\">".$+{port_ID}."</td><td style=\"text-align:Center;; width:10%\">".$+{port_Name}."</td><td>".$+{port_Description}."</td><td style=\"text-align:Left;; width:10%\">".$+{port_State}."</td></tr>";
              }
            push @ports, $+{port_ID};
            $i = $i + 1;
          }
        }
    }
    $rcCmd = $rcCmd."setreading $name PORTScan_OpenPorts $i;";
    $portlist = $portlist."</table></html>";
    $rcCmd = $rcCmd."setreading $name PORTScan_List $portlist;";
    my $temp = "";
    foreach my $port (@ports) {
      if ($temp eq "") {
        $temp = $port;
      }
      else {
        $temp = $temp.",".$port;
      }
    }
    $rcCmd = $rcCmd." setreading $name PortScan_Ports $temp;";
    return $name."|".$rcCmd;
}
#######################################################################################
#######################################################################################
sub NmapClient_PortScanDone($)
{
    my ($string) = @_;
    my ($name, $rcCmd) = split("\\|", $string);
    my $hash = $defs{$name};
    my $errors = AnalyzeCommandChain ($hash, $rcCmd);
    if (!defined($errors)) {
      Log3($name, 5,"Success for PortScan: $rcCmd !");
    }
    else {
      Log3($name, 5, "PortScan for ".$name." causes an error: \n".$errors." in: \n".$rcCmd."\n");
    }
    # zum Abschluss wird die "RUNNING_PID des helpers des devices gelöscht
    delete($hash->{helper}{PORTSCANRUNNING_PID});
    # Aufruf der Sub-Routine zum setzten des Timers für die nächste Ausführung
    NmapClient_SetNextTimer($hash,"PortScan");
    # Abschluss der Sub-Routine ohne Rückgabewert
    Log3 $hash, 3, $hash->{NAME}." Success for PortScan job!";

    return undef;
}
#######################################################################################
#######################################################################################
sub NmapClient_PortScanAbort($)
{
    my ($hash) = @_;
    delete($hash->{helper}{PORTSCANRUNNING_PID});
    Log3 $hash->{NAME}, 3, "BlockingCall PortScan für ".$hash->{NAME}." wurde abgebrochen";
    NmapClient_SetNextTimer($hash,"PortScan");
}
#######################################################################################


#######################################################################################
sub NmapClient_StartPing($)
{
    my ($hash) = @_;
    return undef if (IsDisabled($hash->{NAME}));
    my $name = $hash->{NAME};
    return "Ping = off!" if (AttrVal($name,"Ping","off") eq "off");
    my $ip = ReadingsVal($name,"IP",undef);
    if (!defined(ReadingsVal($name,"IP",undef))) {
        if (!$ip =~ /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) {
            Log3 $name, 2, "$name: IP: $ip nicht gültig!" if( $@ );
            return "IP nicht gültig!";
        }
    }

    my $arg = $name."|".$ip;
    my $blockingFn = "NmapClient_Ping";
    my $finishFn = "NmapClient_PingDone";
    my $abortFn = "NmapClient_PingAbort";
    if (!(exists($hash->{helper}{PINGRUNNING_PID}))) {
        $hash->{helper}{PINGRUNNING_PID} = BlockingCall($blockingFn,$arg,$finishFn,20,$abortFn,$hash);
        Log3 $hash, 5, "$hash->{NAME} Ping Auftrag wird mit PID: ".$hash->{helper}{PINGRUNNING_PID}{pid}." ausgeführt!";
    }
    else {
        Log3 $hash, 5, "$hash->{NAME} Blocking Call Ping läuft, es wurde kein neuer gestartet!";
        NmapClient_SetNextTimer($hash,"Ping");
    }
}
#######################################################################################
#######################################################################################
sub NmapClient_Ping($)
{
    my ($string) = @_;
    my ($name, $ip) = split("\\|", $string);
    my $hash = $defs{$name};
    my $result = "";
    my $rcCmd = "";
    my $pingBefehl = "ping ".$ip." -c 5 -q &";
    $result = `$pingBefehl`;
    my $regex = qr/ping statistics[\w\W]*\n(?P<transmitted>.*)\spackets\stransmitted,\s(?P<received>.*)\sreceived,\s(?P<quote>.*)\spacket\sloss,\stime\s(?P<time>.*)ms\srtt[\w\W]*=\s(?P<min>.*)\/(?P<avg>.*)\/(?P<max>.*)\/(?P<mdev>.*)\sms/p;
    if ( $result =~ /$regex/g ) {
      $rcCmd = ""
        ."setreading ".$name." PING_transmitted ".$+{transmitted}.";"
        ."setreading ".$name." PING_received ".$+{received}.";"
        ."setreading ".$name." PING_quote ".$+{quote}.";"
        ."setreading ".$name." PING_time ".$+{time}.";"
        ."setreading ".$name." PING_min ".$+{min}.";"
        ."setreading ".$name." PING_avg ".$+{avg}.";"
        ."setreading ".$name." PING_max ".$+{max}.";"
        ."setreading ".$name." PING_mdev ".$+{mdev}.";"
    }
    return $name."|".$rcCmd;
}
#######################################################################################
#######################################################################################
sub NmapClient_PingDone($)
{
    my ($string) = @_;
    my ($name, $rcCmd) = split("\\|", $string);
    my $hash = $defs{$name};
    my $errors = AnalyzeCommandChain ($hash, $rcCmd);
    if (!defined($errors)) {
      Log3($name, 5,"Success for PortScan: $rcCmd !");
    }
    else {
      Log3($name, 5, "PortScan for ".$name." causes an error: \n".$errors." in: \n".$rcCmd."\n");
    }
    # zum Abschluss wird die "RUNNING_PID des helpers des devices gelöscht
    delete($hash->{helper}{PINGRUNNING_PID});
    # Aufruf der Sub-Routine zum setzten des Timers für die nächste Ausführung
    NmapClient_SetNextTimer($hash,"Ping");
    # Abschluss der Sub-Routine ohne Rückgabewert
}
#######################################################################################
#######################################################################################
sub NmapClient_PingAbort($)
{
    my ($hash) = @_;
    delete($hash->{helper}{PINGRUNNING_PID});
    Log3 $hash->{NAME}, 3, "BlockingCall Ping für ".$hash->{NAME}." wurde abgebrochen";
    NmapClient_SetNextTimer($hash,"Ping");
}
#######################################################################################

#######################################################################################
sub NmapClient_Basics($)
{
    my ($hash) = @_;
    my $name = $hash->{NAME};
    my $nmapDeviceName = AttrVal($name,"NmapDeviceName","nmap");
    my $ip = ReadingsVal($name,"IP",undef);
    readingsBeginUpdate($hash);
      readingsBulkUpdateIfChanged($hash, "ALIAS", ReadingsVal($nmapDeviceName,$ip."_alias",undef), 1);
      readingsBulkUpdateIfChanged($hash, "HOSTNAME", ReadingsVal($nmapDeviceName,$ip."_hostname",undef), 1);
      readingsBulkUpdateIfChanged($hash, "LASTSEEN", ReadingsVal($nmapDeviceName,$ip."_lastSeen",undef), 1);
      readingsBulkUpdateIfChanged($hash, "MAC", ReadingsVal($nmapDeviceName,$ip."_macAddress",undef), 1);
      readingsBulkUpdateIfChanged($hash, "VENDOR", ReadingsVal($nmapDeviceName,$ip."_macVendor",undef), 1);
      readingsBulkUpdateIfChanged($hash, "UPTIME", ReadingsVal($nmapDeviceName,$ip."_uptime",undef), 1);
      readingsBulkUpdateIfChanged($hash, "UPTIMETEXT", ReadingsVal($nmapDeviceName,$ip."_uptimeText",undef), 1);
      readingsBulkUpdateIfChanged($hash, "state", ReadingsVal($nmapDeviceName,$ip."_state",undef), 1);
    readingsEndUpdate($hash, 0);
}
#######################################################################################


# NmapClient
# Kennzeichen für das Ende des fhem - Moduls
1;

=pod
=begin html
<a name="NmapClient"></a>
No english description, please look al german
=end html
=begin html_DE
  <a name="NmapClient"></a>
  <h3>NmapClient</h3>
  <ul>
  <p>Dieses helper-Modul erzeugt aus den im Nmap-Modul mit dem Programm nmap</p>
  <p>gefundenen Netzwerkgeräten einzelen Geräte mit erweiterten Funktionen</p>
  <a name="NmapClient_define"></a>
  <p><b>Define</b></p>
  <ul>
  <p>Im "Normalfall" werden die Geräte durch ein notify automatisch angelegt</p>
  <p><code>defmod nmapClientAddNew notify nmap:new.host:..+ {\
my $newDeviceName = "";;\
my $ip = "";;\
my $hostname = "";;\
my $regex = qr/host:\s(?P<hostname>.*)\s\((?P<ip>.*)\)\Z/p;;\
\
if ( $EVENT =~ /$regex/g ) \
{\
$hostname = $+{hostname};;\
$ip = $+{ip};;\
}\
$regex = qr/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/p;;\
if ( $ip =~ /$regex/ )  \
{\
$regex = qr/\./p;;\
my ($A,$B,$C,$D) = split(/\./, $ip);;\
$newDeviceName = "nc_".substr("000".$A, -3, 3)."_".substr("000".$B, -3, 3)."_".substr("000".$C, -3, 3)."_".substr("000".$D, -3, 3);;\
}\
else\
{\
$newDeviceName = "nc_".$hostname;;\
return "No valid IP!";;\
}\
fhem("defmod ".$newDeviceName." NmapClient ".$newDeviceName);;\
fhem("setreading ".$newDeviceName." IP ".$ip);;\
fhem("attr ".$newDeviceName." NmapDeviceName ".$NAME);;\
if ($ip ne $hostname) \
{\
fhem("attr ".$newDeviceName." alias ".$hostname);;\
} \
}
attr nmapClientAddNew room nmap</code>
  <p><code>define &lt;name&gt; NmapClient &lt;"nc_".ID = MAC-Adress/IP-Adresse&gt</code></p>
  <p>definiert das NmapClient device.<br/>
  &lt;nc_MAC-Adresse/IP-Adressse&gt; sollte als Indentifikation eingegeben werden</p>
   </ul>
  <a name="NmapClient_readings"></a>
  <p><b>Readings</b></p>
  <ul>
  <li>
  <b>state</b><br/>
  [initialized|present|absent]: Zeigt den aktuellen Status.
  </li>
  <li>
  <b>ALIAS | HOSTNAME | IP | LASTSEEN | UPTIME | UPTIMETEXT | MAC | VENDOR </b><br/>
  Zeigen die Daten aus dem anlegendem Gerät auf Basis des Nmap - Moduls
  </li>
  <li>
  <b>PING_<Name></b><br/>
  Zeigen die Daten aus dem mit <code>set <device> Ping on</code> ausgeführten Ping-Befehls.
  </li>
  <li>
  <b>PORT<00000></b><br/>
  Zeigen die offenen Ports aus dem mit <code>set <device> PortScan on</code> ausgeführten nmap-Befehls.
  </li>
  <li>
  <b>PortScan_<Name></b><br/>
  Zeigen die Daten aus dem mit <code>set <device> PortScan on</code> ausgeführten Ping-Befehls.
  </li>
  </ul>
  <a name="NmapClient_attr"></a>
  <p><b>Attributes</b></p>
  <ul>
  <li>
  <b>PORTScanInterval</b><br/>
  Default: 60*60*24. Time after the connection is re-checked.
  </li>
  <li>
  <b>... be continued</b><br/>
  ... .
  </li>
  </ul>
  </ul>
  =end html_DE
=cut

2. Schritt: FHEM über die FHEM-Befehlszeile neu starten:
shutdown restart
3.Schritt: Test mit der manuellen Anlage eines devices auf Basis des NmapClient - Moduls. (ggf. Fehler aus der aktuellen Fhem - LogDatei bereinigen)
define nc_123_123_123_123 NmapClient 123_123_123_123
4. Schritt: Alle Test NmapClient - Devices und alte dummy - Devices löschen z.B. mit den Befehlen in der FHEM Befehlszeile:
delete nc_123_123_123_123
delete 192_168_.*

5. Schritt: Ebenfalls in der FHEM Befehlszeile die "OldReadings" und "Readings" löschen (bitte "Netzwerk" ggf. durch den korrekten Nmap-Device-Namen ersetzen:
set Netzwerk deleteOldReadings 1
set Netzwerk clear Readings

6. Schritt: Nur, wenn die Anlage des Test-Devices funktioniert hat! Nachstehendes notify in FHEM anlegen (raw Definition) und in der 1. Zeile hinter "... notify " den Device Namen "Netzwerk" durch den individuell vergebenen Namen für das Nmap device ersetzen: 
defmod nmapClientAddNew notify Netzwerk:new.host:..+ {\
my $newDeviceName = "";;\
my $ip = "";;\
my $hostname = "";;\
my $regex = qr/host:\s(?P<hostname>.*)\s\((?P<ip>.*)\)\Z/p;;\
\
if ( $EVENT =~ /$regex/g ) \
{\
$hostname = $+{hostname};;\
$ip = $+{ip};;\
}\
$regex = qr/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/p;;\
if ( $ip =~ /$regex/ )  \
{\
$regex = qr/\./p;;\
my ($A,$B,$C,$D) = split(/\./, $ip);;\
$newDeviceName = "nc_".substr("000".$A, -3, 3)."_".substr("000".$B, -3, 3)."_".substr("000".$C, -3, 3)."_".substr("000".$D, -3, 3);;\
}\
else\
{\
$newDeviceName = "nc_".$hostname;;\
return "No valid IP!";;\
}\
fhem("defmod ".$newDeviceName." NmapClient ".$newDeviceName);;\
fhem("setreading ".$newDeviceName." IP ".$ip);;\
fhem("attr ".$newDeviceName." NmapDeviceName ".$NAME);;\
if ($ip ne $hostname) \
{\
fhem("attr ".$newDeviceName." alias ".$hostname);;\
} \
}
attr nmapClientAddNew room nmap

7. Schritt: Warten, bis das Nmap Device turnusmäßig aktualisiert wird, oder über die FHEM - Befehlszeile den "StatusRequest" manuell erzwingen:
set Netzwerk statusRequest

Nachdem Abschluss des Nmap statusRequest sollten im Raum nmap in der Gruppe NmapClients alle derzeit erkennbaren Netzwerkgeräte angelegt sein.

Sie haben zunächst nur 3 rudimentäre Funktionen:

1. BasicData: Daten aus dem Nmap Device abfragen (erfolgt auch automatisch, wenn das Ereignis "Nmap .... done" auftritt)
2. PortScan: ausführen des Befehls nmap <ip> und einlesen des Outputs in Readings (dauert mit unter mehrere Minuten!)
3. Ping: ausführen des Befehls ping <ip> und einlesen des Outputs in Readings

Dazu kann die Vorgabe 1 x täglich als Interval sowohl für den PortScan als auch für Ping geändert werden.

Diese Intervalle werden nur aktiv, wenn die Funktion PortScan oder Ping auf "on" gesetzt sind.

Wird Ping oder PorScan erstmalig auf on gesetzt, wird der entsprechende Befehl zunächst aus geführt und anschliessend ein Timer auf Basis des entsprechenden Intervalls für die nächste Ausführung gesetzt.

Ich hoffe ich konnte in ersten Ansätzen zeigen, was ich mir vorstellte und du erkennst die Möglichkeiten.
Ich bin gespannt, was ein Perl Programmierer daraus macht und welche weiteren Funktionen möglich sind.

Gernot   

P.S.bzw. Off-Topic: Ich meinte nicht, dass man den Zustand eines Servers von aussen nicht sehen sollte, sondern, dass das was überwacht wird, der Server weitgehend selbst liefert (z.B. ausgewählte Auszüge aus log Dateien bzw. der Zustand ausgewählter Dienste oder ausgewählter Hardware - Werte). Also weder telnet noch sonst eine Schnittstelle für die Ausführung von Befehlen auf dem Server öffnen.