/Sicherheit: Untaint my Vars und per sudo Ausführen
Robert Bienert
- perl
Hallo Forum,
mehr aus Spaß habe ich ein Perlprogramm geschrieben, welches auf einem bestimmten TCP-Port Verbindungen entgegen nimmt und beantwortet. (Es handelt sich dabei um einen sehr rudimentären Webserver, aber das tut nichts zur Sache.) Da Port 80 bei Unix und Linux normalerweise root vorbehalten ist, habe ich mit exec(3pl) und sudo(8) experimentiert. Mein Programm prüft nun die Portnummer und gegebenenfalls an Hand der Umgebungsvariablen USER, ob es mit root-Rechten ausgeführt wird. Falls dies nicht zutrifft, ruft sich das Programm selbst via sudo erneut auf. Meine Versuche auf der Konsole haben so funktioniert, wie ich mir das vorstelle.
Die Frage ist allerdings, welche weiteren Sicherheitslücken ich noch übersehen habe. Fällt euch etwas auf:
#! /usr/bin/perl -Tw
$ENV{'PATH'} = '/usr/bin'; # sicherer path
use strict;
use IO::Socket;
my $port = ($ARGV[0] || 2999); # Port
my $app;
# untainting $0 and @ARGV:
if ($0 =~ /^(\.\/[[:alpha:]]+)/) { # lasse nur lokale Dateien zu
$app = $1
}
else {
$app = './WebServer.pl' # Standard Skriptname
}
$ARGV[0] = $1 if ($ARGV[0] =~ /^([1-9]{1}[0-9]*)$/);
if ($port < 1024 && $ENV{'USER'} ne 'root') {
print 'sudo benoetigt um auf Port ', $port, " zuzugreifen\n";
exec 'sudo', './WebServer.pl', @ARGV
# schlaegt fehl falls @ARGV nicht untainted werden konnte
}
my $socket = new IO::Socket::INET (
Listen => 5,
LocalPort => $port,
Proto => 'tcp',
Reuse => 1,
);
die $! unless $socket;
print STDERR 'Socket erzeugt fuer Port ', $port,
' (als Benutzer ', $ENV{'USER'}, ").\n"
Viele Grüße,
Robert
Moin Moin!
Warum so umständlich? Und warum so viele Rechte für das Programm?
Starte einen Prozess, der Port 80 als Server öffnet, mit Root-Rechten. Werfe in dem Prozess alle Rechte weg und werde zu nobody / www / wwwrun. Ersetze den Prozess durch einen anderen, der den noch offenen Port bedient.
Lektüre: http://cr.yp.to/ucspi-tcp.html, insbesondere http://cr.yp.to/ucspi-tcp/tcpserver.html, das fast genauso arbeitet. Allerdingss wird dort erst die fertig aufgebaute Client-Verbindung weitergereicht, nicht schon der offene Server-Port.
Mein Programm prüft nun die Portnummer und gegebenenfalls an Hand der Umgebungsvariablen USER, ob es mit root-Rechten ausgeführt wird.
Warum anhand von $ENV{'USER'}? Lies bitte mal in perlvar nach, was $< und $> bedeuten.
Falls dies nicht zutrifft, ruft sich das Programm selbst via sudo erneut auf.
Warum?
Wenn der User keine Root-Rechte hat und auch nicht haben darf, hilft sudo auch nicht.
Wenn der User sudo ausführen darf, dann soll er das bitte explizit machen, damit er sich bewußt bleibt, dass er mehr Rechte als normal hat.
Die Frage ist allerdings, welche weiteren Sicherheitslücken ich noch übersehen habe.
Du hast neue eingebaut. Angefangen bei sudo.
Fällt euch etwas auf:
Oh ja.
#! /usr/bin/perl -Tw
$ENV{'PATH'} = '/usr/bin'; # sicherer path
PATH ist nicht die einzige Environment-Variable, die Du auf sichere Werte setzen solltest. Lies bitte [link:http://perldoc.perl.org/perlsec.html@title=perlsec].
use strict;
use IO::Socket;my $port = ($ARGV[0] || 2999); # Port
my $app;untainting $0 and @ARGV:
if ($0 =~ /^(./[[:alpha:]]+)/) { # lasse nur lokale Dateien zu
$app = $1
Was genau läßt Du hier zu?
.
gefolgt von
/
gefolgt von
mindestens einem alphanumerischen Zeichen
gefolgt von
beliebig vielen beliebigen Zeichen
Das trifft z.B. auch auf ./a/../../../../.. zu.
}
else {
$app = './WebServer.pl' # Standard Skriptname
}
Und wenn das Pattern nicht paßt, nimmst Du willkürlich einen Namen, von dem Du nicht einmal prüfst, ob er paßt.
$ARGV[0] = $1 if ($ARGV[0] =~ /^([1-9]{1}[0-9]*)$/);
if ($port < 1024 && $ENV{'USER'} ne 'root') {
print 'sudo benoetigt um auf Port ', $port, " zuzugreifen\n";
exec 'sudo', './WebServer.pl', @ARGV
Wozu überhaupt das Theater, wenn Du ohnehin den festen Namen benutzt?
Wo war noch gleich sudo? /home/foken/bin/evil/sudo?
OK, Du hast $ENV{'PATH'} gesetzt, und endest damit zwangsläufig bei /usr/bin/sudo.
Und wo war gleich noch ./WebServer.pl?
Du hast ein /home/robert/WebServer.pl.
Ich bringe dich dazu, in /tmp zu arbeiten.
Dort habe ich MEIN WebServer.pl abgelegt. Das macht böse Dinge.
Du startest in /tmp dein Script, als robert.
perl -Tw /home/robert/WebServer.pl
oder meinetwegen
/home/robert/WebServer.pl
Du endest bei der Zeile exec 'sudo','./WebServer.pl'.
sudo fragt Dich nach Deinem Passwort. Du gibst es ein, denn schließlich startet das ja nur Dein Script, oder?
Das aktuelle Verzeichnis ist /tmp, nicht /home/robert.
sudo startet /tmp/WebServer.pl mit root-Rechten.
MEIN WebServer.pl, dass mir die volle Kontrolle über Deinen Rechner verschafft. Nicht DEIN WebServer.pl.
Du stehst mit runtergelassener Hose da. Sorry.
Die außerdem bestehende [link:http://en.wikipedia.org/wiki/Time-of-check-to-time-of-use@title=TOCTOU]-Lücke findest Du in der WP erklärt. Hinweis: Ich ersetze Dein Script zwischen dem Aufruf von /usr/bin/perl und dem Aufruf von sudo durch ein anderes Programm.
# schlaegt fehl falls @ARGV nicht untainted werden konnte
Und im Fehlerfall ignorierst Du den Fehler komplett? Keine Meldung, einfach fröhlich weiter im Text? Und hoffen, dass irgendwann die fehlenden Root-Rechte einen Fehler erzeugen?
}
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
Moin Alexander,
vielen Dank für die ausführliche und lehrreiche Kritik!
Starte einen Prozess, der Port 80 als Server öffnet, mit Root-Rechten. Werfe in dem Prozess alle Rechte weg und werde zu nobody / www / wwwrun. Ersetze den Prozess durch einen anderen, der den noch offenen Port bedient.
Nobody ist keine gute Idee. Wenn du (zu) viele Dienste unter diesem Benutzer ausführst, wird der Nutzer selbst sehr mächtig. Am Besten ist ein eigener Nutzer für jeden Dienst.
Lektüre: http://cr.yp.to/ucspi-tcp.html, insbesondere http://cr.yp.to/ucspi-tcp/tcpserver.html, das fast genauso arbeitet. Allerdingss wird dort erst die fertig aufgebaute Client-Verbindung weitergereicht, nicht schon der offene Server-Port.
Wo finde ich denn den Quellcode der Programme? Dieser Ansatz ist sinnvoller.
Warum anhand von $ENV{'USER'}? Lies bitte mal in perlvar nach, was $< und $> bedeuten.
$ perl -e 'print $<, " ", $>, "\n"
'
501 501
$ sudo perl -e 'print $<, " ", $>, "\n"
'
0 0
Alles klar, kannte ich vorher nicht.
Und an dieser Stelle braucht gar nicht mehr weiter diskutiert zu werden, weil das IMHO das Killerargument gegen den von mir skizzierten Entwurf ist:
Wenn der User sudo ausführen darf, dann soll er das bitte explizit machen, damit er sich bewußt bleibt, dass er mehr Rechte als normal hat.
Hiermit fällt dann nämlich auch das Problem mit den Pfaden und Dateinamen weg. (BTW: Wie bekomme ich denn unter Unix/Linux den kompletten Pfad eines ausgeführten Programms? $0
oder argv[0] in C enthalten nur den Namen, wie er auf der Konsole eingegeben wird.)
--
Der Vollständigkeit halber die weiteren Bugs:
if ($0 =~ /^(./[[:alpha:]]+)/) { # lasse nur lokale Dateien zu
$app = $1
>
> Was genau läßt Du hier zu?
Oha, da fehlte noch ein $:
^(\.\/[[:alpha:]]+)$
> > # schlaegt fehl falls @ARGV nicht untainted werden konnte
>
> Und im Fehlerfall ignorierst Du den Fehler komplett
Mein perl 5.8.8 terminiert ein Skript bei der unzulässigen Verwendung einer tainted var. Etwas Anderes wäre an dieser Stelle auch von mir aus nicht sinnvoll.
Vielen Dank und viele Grüße,
Robert
Moin Moin!
Lektüre: http://cr.yp.to/ucspi-tcp.html, insbesondere http://cr.yp.to/ucspi-tcp/tcpserver.html, das fast genauso arbeitet. Allerdingss wird dort erst die fertig aufgebaute Client-Verbindung weitergereicht, nicht schon der offene Server-Port.
Wo finde ich denn den Quellcode der Programme? Dieser Ansatz ist sinnvoller.
Exakt dort. How to install ucspi-tcp.
Die daemontools solltest Du auch kennen, die machen das Schreiben eines Daemons samt zuverlässigem Logger zu einem Kinderspiel. Für einen trivialen Daemon kommst Du mit 10 Zeilen Shell-Script aus. Siehe auch the djb way. Beachte dass die daemontools dank der Dickköpfigkeit von DJB nicht Hilfe unter Linux compilieren und typischerweise bei errno auf die Nase fallen. Das liegt daran, dass DJB die entsprechenden Standards nicht verstehen will. Abhilfe.
Hiermit fällt dann nämlich auch das Problem mit den Pfaden und Dateinamen weg. (BTW: Wie bekomme ich denn unter Unix/Linux den kompletten Pfad eines ausgeführten Programms?
$0
oder argv[0] in C enthalten nur den Namen, wie er auf der Konsole eingegeben wird.)
Im Zweifel gar nicht.
Je nach Aufrufer kann argv[0] beliebige Werte annehmen. Viele Shells nehmen, was eingetippt wurde, andere Shells blasen argv[0] auf den vollen Namen auf. Manche lösen Symlinks auf (was z.B. für Busybox fatal wäre), andere nicht. Wieder andere Programme setzen auch mal ganz andere Dinge in argv[0] ein. login startet die Shell (in /bin/sh) typischerweise mit argv[0]="-/bin/sh".
Viele, aber nicht alle Unixe haben ein /proc-Dateisystem, von denen haben wiederum einige den Symlink /proc/self, andere nicht, dort mußt Du Dir die PID über getpid() besorgen. Schließlich findest Du in /proc/self bzw. /proc/$PID gelegentlich einen Symlink, der auf das zum Prozess gehörende Executable zeigt. Unter Linux heißt der Symlink exe, andere Unixe haben andere Namen. /proc muß übrigens nicht zwingend gemountet sein, und andere Unixe verstecken diese Information auch gerne mal an anderer Stelle.
Übrigens: Rate mal, was folgendes Programm unter Linux ausspuckt. Und zwar egal, wie Du es aufrufst. Nicht mogeln! Erst mal aufschreiben, dann ausprobieren:
#!/usr/bin/perl
print readlink("/proc/self/exe"),"\n";
1. chmod +x /tmp/self.pl && /tmp/self.pl
2. ( cd /tmp && ./self.pl )
3. perl /tmp/self.pl
4. ( cd /tmp && perl self.pl )
Du kannst ja spaßeshalber auch mal $0 und $^X ausgeben lassen:
#!/usr/bin/perl
print "/proc/self/exe=",readlink("/proc/self/exe")," \$0=$0 \$^X=$^X\n";
Du suchst den absoluten Pfad des aktuellen Scripts, und der ist nicht immer leicht zu ermitteln. FindBin schafft das oft, aber nicht immer. Einige Probleme sind in der Doku beschrieben, aber wohl nicht alle.
Ich denke, wenn Du dich auf den absoluten Pfad verlassen mußt, hast Du ohnehin ein konzeptionelles Problem. Du rennst dann fast automatisch in irgendwelche Race Conditions / TOCTOU rein.
Der Vollständigkeit halber die weiteren Bugs:
if ($0 =~ /^(./[[:alpha:]]+)/) { # lasse nur lokale Dateien zu
$app = $1
> >
> > Was genau läßt Du hier zu?
>
> Oha, da fehlte noch ein $:
>
> ^(\.\/[[:alpha:]]+)$
Richtig, und an dieser Stelle hilft Dir selbst der Taint-Mode nicht mehr. Du hast zu schlecht geprüft, und mir fällt auf Anhieb keine Automatik ein, die das prüfen oder wenigstens erkennen kann.
Genau aus diesem Grund sollte man sicherheitskritischen Code nicht alleine konzipieren oder schreiben, und schon gar nicht "zwischen Tür und Angel". Man übersieht so etwas viel zu leicht.
> > > # schlaegt fehl falls @ARGV nicht untainted werden konnte
> >
> > Und im Fehlerfall ignorierst Du den Fehler komplett
>
> Mein perl 5.8.8 terminiert ein Skript bei der unzulässigen Verwendung einer tainted var. Etwas Anderes wäre an dieser Stelle auch von mir aus nicht sinnvoll.
Was passiert, wenn exec() fehlschlägt?
Weder Linux noch POSIX garantieren, dass exec() immer funktioniert und nie zurückkehrt. Perl übrigens [auch nicht](http://perldoc.perl.org/functions/exec.html).
Und übrigens: Nicht jedes Unix hat sudo an Bord. Windows schon mal gar nicht. Dann schlägt exec() garantiert fehl. Solaris beispielsweise hat stattdessen [pfexec](http://docs.sun.com/app/docs/doc/816-5165/pfexec-1?a=view), natürlich mit völlig anderen Parametern. Wäre sonst ja auch zu einfach.
In Deinem kleinen Script rennst Du in ...
~~~perl
my $socket = new IO::Socket::INET (
Listen => 5,
LocalPort => $port,
Proto => 'tcp',
Reuse => 1,
);
die $! unless $socket;
... rein. Und das fällt unter Linux wegen $port<1024 fürchterlich auf die Nase. Glück gehabt. ;-)
Im Ernst: Ich würde (ohne tcpserver und ähnliche Helferlein) das ganze "ich mach mich selbst zu root"-Zeug ersatzlos streichen und das Programm an exakt dieser Stelle ins Messer laufen lassen. Wenn das Programm auf einem OS läuft, dass die Low Ports auf root beschränkt, fällt es ohne root-Rechte auf die Nase. Das das so ist, steht in der Dokumentation, ebenso der Hinweis, dass man sich in dem Fall irgendwie root-Rechte verschaffen muß. Wenn das Programm auf einem OS oder dem Versuch eines OS läuft, dass diese Einschränkung nicht hat, wie z.B. Windows, brauchst Du das ganze "ich werde root"-Theater ohnehin nicht.
Damit wird Dein Code drastisch kürzer und portabler. Was nicht da ist, kann keine Fehler enthalten. Letztlich machst Du es einem Mit-Arbeiter leichter, den Code zu lesen, zu verstehen und Fehler zu finden.
Wieso schreibst Du eigentlich die $! unless $socket
? Erstens ist das nicht das gewohnte Pattern ... or die
, zweitens fehlt da noch Text. Hast Du schlecht bei perlipc/TCP Servers with IO::Socket abgeschrieben? Dort fehlt übrigens seit Jahren die Fehlermeldung ($!
).
Und die Indirect Object Notation $object=new CLASSNAME (@args);
sollte man sich auch verkneifen, weil die nicht immer eindeutig ist und daher mehr Arbeit beim Parsen macht.
Schreib's so:
my $socket=IO::Socket::INET->new(
Listen => 5,
LocalPort => $port,
Proto => 'tcp',
Reuse => 1,
) or die "Failed to create server socket: $!";
Alexander
Moin Alexander,
Exakt dort. How to install ucspi-tcp.
Die daemontools solltest Du auch kennen, die machen das Schreiben eines Daemons samt zuverlässigem Logger zu einem Kinderspiel.
Vielen Dank, Lesezeichen ist gesetzt, für den Fall der Fälle (Dämonen gehören normalerweise nicht zu meinem Tagesgeschäft).
Für einen trivialen Daemon kommst Du mit 10 Zeilen Shell-Script aus. Siehe auch the djb way.
Muss ich mir mal im Detail anschauen, denn das Symlinken und die Arbeit mit relativen Pfaden wollen geübt sein.
(BTW: Wie bekomme ich denn unter Unix/Linux den kompletten Pfad eines ausgeführten Programms?
$0
oder argv[0] in C enthalten nur den Namen, wie er auf der Konsole eingegeben wird.)Im Zweifel gar nicht.
Viele, aber nicht alle Unixe haben ein /proc-Dateisystem, von denen haben wiederum einige den Symlink /proc/self, andere nicht, dort mußt Du Dir die PID über getpid() besorgen. Schließlich findest Du in /proc/self bzw. /proc/$PID gelegentlich einen Symlink, der auf das zum Prozess gehörende Executable zeigt. Unter Linux heißt der Symlink exe, andere Unixe haben andere Namen. /proc muß übrigens nicht zwingend gemountet sein, und andere Unixe verstecken diese Information auch gerne mal an anderer Stelle.
Mac OS X z.B. scheint kein /proc zu kennen.
Übrigens: Rate mal, was folgendes Programm unter Linux ausspuckt. Und zwar egal, wie Du es aufrufst. Nicht mogeln! Erst mal aufschreiben, dann ausprobieren:
#!/usr/bin/perl
print readlink("/proc/self/exe"),"\n";
Wenn ich [readlink](http://perldoc.perl.org/functions/exec.html) richtig verstanden habe, gibt die Funktion das Ziel eines Symlinks zurück. Jetzt wäre nur noch die Frage, welches die zum Prozess gehörende Executable ist. Nachdem ich gerade ein wenig mit ps(1) herum gespielt habe, teilt #! in der ersten Zeile einer ausführbaren Datei der Shell mit: Nimm den folgenden Pfad bis zum Zeilenende als Interpreter dieser Datei. Genau das zeigt ps auch an.
(Ein recht kurzes Programm, das sich selbst ausgibt: #!/bin/cat)
> Du suchst den absoluten Pfad des aktuellen Scripts, und der ist nicht immer leicht zu ermitteln. [FindBin](http://search.cpan.org/perldoc?FindBin) schafft das oft, aber nicht immer. Einige Probleme sind in der Doku beschrieben, aber wohl nicht alle.
~~~perl
#!/usr/bin/perl -Tw
use strict;
use FindBin;
print $FindBin::Script, ' ', $FindBin::RealScript, "\n"
$ ./f
f f
$ $PWD/f
f f
Sollte da nicht etwas Anderes herauskommen?
Genau aus diesem Grund sollte man sicherheitskritischen Code nicht alleine konzipieren oder schreiben, und schon gar nicht "zwischen Tür und Angel". Man übersieht so etwas viel zu leicht.
Deshalb gibt es ja auch das SELFHTML-Forum ;-)
Was passiert, wenn exec() fehlschlägt?
Sofern -T nicht zuschlägt, läuft das Programm weiter. In diesem Falle wäre also exec @args or die $!
die richtige Wahl – „beende dich oder sterbe“.
In Deinem kleinen Script rennst Du in ...
my $socket = new IO::Socket::INET (
Listen => 5,
LocalPort => $port,
Proto => 'tcp',
Reuse => 1,
);
die $! unless $socket;
>
> ... rein. Und das fällt unter Linux wegen $port<1024 fürchterlich auf die Nase. Glück gehabt. ;-)
Und Windows interessiert mich an dieser Stelle auch gar nicht.
> Wieso schreibst Du eigentlich `die $! unless $socket`{:.language-perl}? Erstens ist das nicht das gewohnte Pattern `... or die`{:.language-perl}, zweitens fehlt da noch Text.
Was denn für Text? Das Programm basiert auf einem Projekt noch aus der Schulzeit, was schon etliche Jahre her ist.
> Und die Indirect Object Notation `$object=new CLASSNAME (@args);`{:.language-perl} sollte man sich auch verkneifen, weil die nicht immer eindeutig ist und daher mehr Arbeit beim Parsen macht.
Wie jetzt?
Viele Grüße,
Robert
Moin Moin!
Für einen trivialen Daemon kommst Du mit 10 Zeilen Shell-Script aus. Siehe auch the djb way.
Muss ich mir mal im Detail anschauen, denn das Symlinken und die Arbeit mit relativen Pfaden wollen geübt sein.
Du brauchst weder relative Pfade noch Symlinks für die daemontools.
(BTW: Wie bekomme ich denn unter Unix/Linux den kompletten Pfad eines ausgeführten Programms?
$0
oder argv[0] in C enthalten nur den Namen, wie er auf der Konsole eingegeben wird.)Im Zweifel gar nicht.
Viele, aber nicht alle Unixe haben ein /proc-Dateisystem, von denen haben wiederum einige den Symlink /proc/self, andere nicht, dort mußt Du Dir die PID über getpid() besorgen. Schließlich findest Du in /proc/self bzw. /proc/$PID gelegentlich einen Symlink, der auf das zum Prozess gehörende Executable zeigt. Unter Linux heißt der Symlink exe, andere Unixe haben andere Namen. /proc muß übrigens nicht zwingend gemountet sein, und andere Unixe verstecken diese Information auch gerne mal an anderer Stelle.
Mac OS X z.B. scheint kein /proc zu kennen.
Mag wohl sein, bei mir gammelt kein angefressenes Obst rum. Ein Unix-Derivat auf Intel- oder AMD-Standard-Hardware kann man wesentlich billiger bekommen.
Übrigens: Rate mal, was folgendes Programm unter Linux ausspuckt. Und zwar egal, wie Du es aufrufst. Nicht mogeln! Erst mal aufschreiben, dann ausprobieren:
#!/usr/bin/perl
print readlink("/proc/self/exe"),"\n";
>
> Wenn ich [readlink](http://perldoc.perl.org/functions/exec.html) richtig verstanden habe, gibt die Funktion das Ziel eines Symlinks zurück. Jetzt wäre nur noch die Frage, welches die zum Prozess gehörende Executable ist. Nachdem ich gerade ein wenig mit ps(1) herum gespielt habe, teilt #! in der ersten Zeile einer ausführbaren Datei der Shell mit: Nimm den folgenden Pfad bis zum Zeilenende als Interpreter dieser Datei. Genau das zeigt ps auch an.
Exakt. /proc/self/exe nützt dir exakt gar nichts, wenn Du dich mit einem Interpreter rumschlägst. Du findest immer nur den Interpreter, nicht das Script.
> > Du suchst den absoluten Pfad des aktuellen Scripts, und der ist nicht immer leicht zu ermitteln. [FindBin](http://search.cpan.org/perldoc?FindBin) schafft das oft, aber nicht immer. Einige Probleme sind in der Doku beschrieben, aber wohl nicht alle.
>
> ~~~perl
#!/usr/bin/perl -Tw
>
> use strict;
> use FindBin;
>
> print $FindBin::Script, ' ', $FindBin::RealScript, "\n"
$ ./f
f f
$ $PWD/f
f fSollte da nicht etwas Anderes herauskommen?
Nö: $Script - basename of script from which perl was invoked
Ändere das Script mal in:
#!/usr/bin/perl -Tw
use strict;
use FindBin;
print "Script=$FindBin::Script Bin=$FindBin::Bin RealScript=$FindBin::RealScript RealBin=$FindBin::RealBin\n";
(cd ; ln -s /tmp/f xyz)
(cd ; ./xyz )
$HOME/xyz
(cd /tmp ; ./f)
/tmp/f
Was passiert, wenn exec() fehlschlägt?
Sofern -T nicht zuschlägt, läuft das Programm weiter. In diesem Falle wäre also
exec @args or die $!
die richtige Wahl – „beende dich oder sterbe“.
Richtig! Wobei Du da auch ausnahmsweise das "or" gegen ein Semikolon tauschen kannst.
In Deinem kleinen Script rennst Du in ...
my $socket = new IO::Socket::INET (
Listen => 5,
LocalPort => $port,
Proto => 'tcp',
Reuse => 1,
);
die $! unless $socket;
> >
> > ... rein. Und das fällt unter Linux wegen $port<1024 fürchterlich auf die Nase. Glück gehabt. ;-)
>
> Und Windows interessiert mich an dieser Stelle auch gar nicht.
Hab ich was von Windows gesagt?
> > Wieso schreibst Du eigentlich `die $! unless $socket`{:.language-perl}? Erstens ist das nicht das gewohnte Pattern `... or die`{:.language-perl}, zweitens fehlt da noch Text.
>
> Was denn für Text?
Sowas wie "Could not open socket: ". Sonst bekommst Du sowas:
/tmp>cat foo
~~~perl
#!/usr/bin/perl -w
use strict;
use warnings;
use IO::Socket;
my $sock=IO::Socket::INET->new(
Proto => 'tcp',
Reuse => 1,
Listen => 1,
LocalPort => 80,
) or die $!;
/tmp>chmod +x foo
/tmp>./foo
Permission denied at ./foo line 6.
Und die Indirect Object Notation
$object=new CLASSNAME (@args);
sollte man sich auch verkneifen, weil die nicht immer eindeutig ist und daher mehr Arbeit beim Parsen macht.Wie jetzt?
# veraltet, indirekt
my $socket=new IO::Socket::INET (...);
# gut, direkt
my $socket=IO::Socket::INET->new(...);
Siehe auch: Perl Best Practices, perlobj/Indirect Object Syntax: "The other way to invoke a method is by using the so-called "indirect object" notation. This syntax was available in Perl 4 long before objects were introduced" und "The -> notation suffers from neither of these disturbing ambiguities, so we recommend you use it exclusively."
Alexander
Nabend,
Ein Unix-Derivat auf Intel- oder AMD-Standard-Hardware kann man wesentlich billiger bekommen.
Sind Notebooks auch Standard-Hardware? Ich brauche vor allem eine gute Soundkarte fürs DJing, problemlosen Anschluss von Beamern und WLAN. Als ich den Apfel gekauft habe, hat es an mindestens einer Anforderung unter Linux gehapert. Außerdem will ich kein Windows dazu aufgezwungen bekommen, was ich im Zweifelsfall auch noch benutzen muss, weil irgend ein Treiber proprietär ist.
Viele Grüße,
Robert
Nabend noch einmal,
my $socket=new IO::Socket::INET (...);
gut, direkt
my $socket=IO::Socket::INET->new(...);
>
> Siehe auch: Perl Best Practices, [perlobj/Indirect Object Syntax](http://perldoc.perl.org/perlobj.html#Indirect-Object-Syntax)
> a call to a method new in indirect notation (as C++ programmers are wont to make)
Ich habe in den letzten Jahren hauptsächlich C++ programmiert ;-)
Viele Grüße,
Robert