mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-04 22:19:38 +00:00
telnet client mode
git-svn-id: https://svn.fhem.de/fhem/trunk@2463 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
73d45dd278
commit
9e8332ae5a
@ -48,6 +48,7 @@
|
|||||||
OWFS
|
OWFS
|
||||||
- feature: stateFormat (readingsFn modules) and showInternalValues attributes
|
- feature: stateFormat (readingsFn modules) and showInternalValues attributes
|
||||||
- feature: new readingsFn modules: FS20 CUL_WS HMS CUL_EM CUL_TX EnOcean ZWave
|
- feature: new readingsFn modules: FS20 CUL_WS HMS CUL_EM CUL_TX EnOcean ZWave
|
||||||
|
- feature: telnet client mode
|
||||||
|
|
||||||
- 2012-10-28 (5.3)
|
- 2012-10-28 (5.3)
|
||||||
- feature: added functions trim, ltrim, rtrim, UntoggleDirect,
|
- feature: added functions trim, ltrim, rtrim, UntoggleDirect,
|
||||||
|
@ -21,7 +21,7 @@ telnet_Initialize($)
|
|||||||
$hash->{AttrFn} = "telnet_Attr";
|
$hash->{AttrFn} = "telnet_Attr";
|
||||||
$hash->{NotifyFn}= "telnet_SecurityCheck";
|
$hash->{NotifyFn}= "telnet_SecurityCheck";
|
||||||
$hash->{AttrList} = "loglevel:0,1,2,3,4,5,6 globalpassword password ".
|
$hash->{AttrList} = "loglevel:0,1,2,3,4,5,6 globalpassword password ".
|
||||||
"allowfrom SSL";
|
"allowfrom SSL connectTimeout connectInterval";
|
||||||
}
|
}
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
@ -45,6 +45,63 @@ telnet_SecurityCheck($$)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##########################
|
||||||
|
sub
|
||||||
|
telnet_ClientConnect($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
$hash->{DEF} =~ m/^(IPV6:)?(.*):(\d+)$/;
|
||||||
|
my ($isIPv6, $server, $port) = ($1, $2, $3);
|
||||||
|
|
||||||
|
Log GetLogLevel($name,4), "$name: Connecting to $server:$port...";
|
||||||
|
my @opts = (
|
||||||
|
PeerAddr => "$server:$port",
|
||||||
|
Timeout => AttrVal($name, "connectTimeout", 2),
|
||||||
|
);
|
||||||
|
|
||||||
|
my $client;
|
||||||
|
if($hash->{SSL}) {
|
||||||
|
$client = IO::Socket::SSL->new(@opts);
|
||||||
|
} else {
|
||||||
|
$client = IO::Socket::INET->new(@opts);
|
||||||
|
}
|
||||||
|
if($client) {
|
||||||
|
$hash->{FD} = $client->fileno();
|
||||||
|
$hash->{CD} = $client; # sysread / close won't work on fileno
|
||||||
|
$hash->{BUF} = "";
|
||||||
|
$hash->{CONNECTS}++;
|
||||||
|
$selectlist{$name} = $hash;
|
||||||
|
$hash->{STATE} = "Connected";
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
Log(GetLogLevel($name,3), "$name: connected to $server:$port");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
telnet_ClientDisconnect($hash, 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
##########################
|
||||||
|
sub
|
||||||
|
telnet_ClientDisconnect($$)
|
||||||
|
{
|
||||||
|
my ($hash, $connect) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
close($hash->{CD}) if($hash->{CD});
|
||||||
|
delete($hash->{FD});
|
||||||
|
delete($hash->{CD});
|
||||||
|
delete($selectlist{$name});
|
||||||
|
$hash->{STATE} = "Disconnected";
|
||||||
|
InternalTimer(gettimeofday()+AttrVal($name, "connectInterval", 60),
|
||||||
|
"telnet_ClientConnect", $hash, 0);
|
||||||
|
if($connect) {
|
||||||
|
Log GetLogLevel($name,4), "$name: Connect failed.";
|
||||||
|
} else {
|
||||||
|
Log GetLogLevel($name,3), "$name: Disconnected";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
sub
|
sub
|
||||||
telnet_Define($$$)
|
telnet_Define($$$)
|
||||||
@ -53,18 +110,30 @@ telnet_Define($$$)
|
|||||||
|
|
||||||
my @a = split("[ \t][ \t]*", $def);
|
my @a = split("[ \t][ \t]*", $def);
|
||||||
my ($name, $type, $port, $global) = split("[ \t]+", $def);
|
my ($name, $type, $port, $global) = split("[ \t]+", $def);
|
||||||
return "Usage: define <name> telnet [IPV6:]<tcp-portnr> [global]"
|
|
||||||
if($port !~ m/^(IPV6:)?\d+$/ || ($global && $global ne "global"));
|
|
||||||
|
|
||||||
my $ret = TcpServer_Open($hash, $port, $global);
|
my $isServer = 1 if($port && $port =~ m/^(IPV6:)?\d+$/);
|
||||||
|
my $isClient = 1 if($port && $port =~ m/^(IPV6:)?.*:\d+$/);
|
||||||
|
|
||||||
|
return "Usage: define <name> telnet { [IPV6:]<tcp-portnr> [global] | ".
|
||||||
|
" [IPV6:]serverName:port }"
|
||||||
|
if(!($isServer || $isClient) ||
|
||||||
|
($isClient && $global) ||
|
||||||
|
($global && $global ne "global"));
|
||||||
|
|
||||||
# Make sure that fhem only runs once
|
# Make sure that fhem only runs once
|
||||||
if($ret && !$init_done) {
|
if($isServer) {
|
||||||
Log 1, "$ret. Exiting.";
|
my $ret = TcpServer_Open($hash, $port, $global);
|
||||||
exit(1);
|
if($ret && !$init_done) {
|
||||||
|
Log 1, "$ret. Exiting.";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
}
|
}
|
||||||
return $ret;
|
|
||||||
|
|
||||||
|
if($isClient) {
|
||||||
|
$hash->{isClient} = 1;
|
||||||
|
telnet_ClientConnect($hash);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub
|
||||||
@ -97,16 +166,22 @@ telnet_Read($)
|
|||||||
my $buf;
|
my $buf;
|
||||||
my $ret = sysread($hash->{CD}, $buf, 256);
|
my $ret = sysread($hash->{CD}, $buf, 256);
|
||||||
if(!defined($ret) || $ret <= 0) {
|
if(!defined($ret) || $ret <= 0) {
|
||||||
CommandDelete(undef, $name);
|
if($hash->{isClient}) {
|
||||||
|
telnet_ClientDisconnect($hash, 0);
|
||||||
|
} else {
|
||||||
|
CommandDelete(undef, $name);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ord($buf) == 4) { # EOT / ^D
|
if(ord($buf) == 4) { # EOT / ^D
|
||||||
CommandQuit($hash, "");
|
CommandQuit($hash, "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$buf =~ s/\r//g;
|
$buf =~ s/\r//g;
|
||||||
my $pw = telnet_pw($hash->{SNAME}, $name);
|
my $sname = ($hash->{isClient} ? $name : $hash->{SNAME});
|
||||||
|
my $pw = telnet_pw($sname, $name);
|
||||||
if($pw) {
|
if($pw) {
|
||||||
$buf =~ s/\xff..//g; # Telnet IAC stuff
|
$buf =~ s/\xff..//g; # Telnet IAC stuff
|
||||||
$buf =~ s/\xfd(.)//; # Telnet Do ?
|
$buf =~ s/\xfd(.)//; # Telnet Do ?
|
||||||
@ -136,7 +211,12 @@ telnet_Read($)
|
|||||||
$hash->{pwEntered} = 1;
|
$hash->{pwEntered} = 1;
|
||||||
next;
|
next;
|
||||||
} else {
|
} else {
|
||||||
CommandDelete(undef, $name);
|
if($hash->{isClient}) {
|
||||||
|
telnet_ClientDisconnect($hash, 0);
|
||||||
|
} else {
|
||||||
|
delete($hash->{rcvdQuit});
|
||||||
|
CommandDelete(undef, $name);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,8 +255,17 @@ telnet_Read($)
|
|||||||
last if(!$l || $l == length($ret));
|
last if(!$l || $l == length($ret));
|
||||||
$ret = substr($ret, $l);
|
$ret = substr($ret, $l);
|
||||||
}
|
}
|
||||||
|
$hash->{CD}->flush();
|
||||||
|
|
||||||
|
}
|
||||||
|
if($hash->{rcvdQuit}) {
|
||||||
|
if($hash->{isClient}) {
|
||||||
|
delete($hash->{rcvdQuit});
|
||||||
|
telnet_ClientDisconnect($hash, 0);
|
||||||
|
} else {
|
||||||
|
CommandDelete(undef, $name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CommandDelete(undef, $name) if($hash->{rcvdQuit});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
@ -188,6 +277,10 @@ telnet_Attr(@)
|
|||||||
|
|
||||||
if($a[0] eq "set" && $a[2] eq "SSL") {
|
if($a[0] eq "set" && $a[2] eq "SSL") {
|
||||||
TcpServer_SetSSL($hash);
|
TcpServer_SetSSL($hash);
|
||||||
|
if($hash->{CD}) {
|
||||||
|
my $ret = IO::Socket::SSL->start_SSL($hash->{CD});
|
||||||
|
Log 1, "$hash->{NAME} start_SSL: $ret" if($ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@ -211,30 +304,48 @@ telnet_Undef($$)
|
|||||||
<a name="telnetdefine"></a>
|
<a name="telnetdefine"></a>
|
||||||
<b>Define</b>
|
<b>Define</b>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> telnet <portNumber> [global]</code>
|
<code>define <name> telnet <portNumber> [global]</code><br>
|
||||||
|
or<br>
|
||||||
|
<code>define <name> telnet <servername>:<portNumber></code>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
|
First form, <b>server</b> mode:<br>
|
||||||
Listen on the TCP/IP port <code><portNumber></code> for incoming
|
Listen on the TCP/IP port <code><portNumber></code> for incoming
|
||||||
connections. If the second parameter global is <b>not</b> specified,
|
connections. If the second parameter global is <b>not</b> specified,
|
||||||
the server will only listen to localhost connections.
|
the server will only listen to localhost connections.
|
||||||
<br><br>
|
<br>
|
||||||
|
|
||||||
To use IPV6, specify the portNumber as IPV6:<number>, in this
|
To use IPV6, specify the portNumber as IPV6:<number>, in this
|
||||||
case the perl module IO::Socket:INET6 will be requested.
|
case the perl module IO::Socket:INET6 will be requested.
|
||||||
On Linux you may have to install it with cpan -i IO::Socket::INET6 or
|
On Linux you may have to install it with cpan -i IO::Socket::INET6 or
|
||||||
apt-get libio-socket-inet6-perl; OSX and the FritzBox-7390 perl already has
|
apt-get libio-socket-inet6-perl; OSX and the FritzBox-7390 perl already has
|
||||||
this module.
|
this module.<br>
|
||||||
<br><br>
|
|
||||||
Examples:
|
Examples:
|
||||||
<ul>
|
<ul>
|
||||||
<code>define tPort telnet 7072 global</code><br>
|
<code>define tPort telnet 7072 global</code><br>
|
||||||
<code>attr tPort globalpassword mySecret</code><br>
|
<code>attr tPort globalpassword mySecret</code><br>
|
||||||
<code>attr tPort SSL</code><br>
|
<code>attr tPort SSL</code><br>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
|
||||||
Note: The old global attribute port is automatically converted to a
|
Note: The old global attribute port is automatically converted to a
|
||||||
telnet instance with the name telnetPort. The global allowfrom attibute is
|
telnet instance with the name telnetPort. The global allowfrom attibute is
|
||||||
lost in this conversion.
|
lost in this conversion.
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
Second form, <b>client</b> mode:<br>
|
||||||
|
Connect to the specified server port, and execute commands received from
|
||||||
|
there just like in server mode. This can be used to connect to a fhem
|
||||||
|
instance sitting behind a firewall, when installing exceptions in the
|
||||||
|
firewall is not desired or possible. Note: this client mode supprts SSL,
|
||||||
|
but not IPV6.<br>
|
||||||
|
Example:
|
||||||
|
<ul>
|
||||||
|
Start tcptee first on publicly reachable host outside the firewall.<ul>
|
||||||
|
perl contrib/tcptee.pl --bidi 3000</ul>
|
||||||
|
Configure fhem inside the firewall:<ul>
|
||||||
|
define tClient telnet <tcptee_host>:3000</ul>
|
||||||
|
Connect to the fhem from outside of the firewall:<ul>
|
||||||
|
telnet <tcptee_host> 3000</ul>
|
||||||
|
</ul>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@ -291,6 +402,19 @@ telnet_Undef($$)
|
|||||||
only connections from these addresses are allowed.
|
only connections from these addresses are allowed.
|
||||||
</li><br>
|
</li><br>
|
||||||
|
|
||||||
|
<a name="connectTimeout"></a>
|
||||||
|
<li>connectTimeout<br>
|
||||||
|
Wait at maximum this many seconds for the connection to be established.
|
||||||
|
Default is 2.
|
||||||
|
</li><br>
|
||||||
|
|
||||||
|
<a name="connectInterval"></a>
|
||||||
|
<li>connectInterval<br>
|
||||||
|
After closing a connection, or if a connection cannot be estblished,
|
||||||
|
try to connect again after this many seconds. Default is 60.
|
||||||
|
</li><br>
|
||||||
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -10,10 +10,11 @@ my $bidi;
|
|||||||
my $loop;
|
my $loop;
|
||||||
my $myIp;
|
my $myIp;
|
||||||
my $myPort;
|
my $myPort;
|
||||||
|
my $ssl;
|
||||||
my $serverHost;
|
my $serverHost;
|
||||||
my $serverPort;
|
my $serverPort;
|
||||||
my $usage = "Usage: tcptee.pl [--bidi] [--loop] " .
|
my $usage = "Usage: tcptee.pl [--bidi] [--loop] [--ssl] " .
|
||||||
"[myIp:]myPort:serverHost:serverPort\n";
|
"[myIp:]myPort[:serverHost:serverPort]\n";
|
||||||
|
|
||||||
while(@ARGV) {
|
while(@ARGV) {
|
||||||
my $opt = shift @ARGV;
|
my $opt = shift @ARGV;
|
||||||
@ -24,13 +25,19 @@ while(@ARGV) {
|
|||||||
} elsif($opt =~ m/^--loop$/i) {
|
} elsif($opt =~ m/^--loop$/i) {
|
||||||
$loop = 1
|
$loop = 1
|
||||||
|
|
||||||
} elsif($opt =~ m/^(.*):(\d+):(.*):(\d+)/) {
|
} elsif($opt =~ m/^--ssl$/i) {
|
||||||
|
$ssl = 1
|
||||||
|
|
||||||
|
} elsif($opt =~ m/^(\d+)$/) {
|
||||||
|
$myPort = $opt;
|
||||||
|
|
||||||
|
} elsif($opt =~ m/^(.*):(\d+):(.*):(\d+)$/) {
|
||||||
$myIp = $1;
|
$myIp = $1;
|
||||||
$myPort = $2;
|
$myPort = $2;
|
||||||
$serverHost = $3;
|
$serverHost = $3;
|
||||||
$serverPort = $4;
|
$serverPort = $4;
|
||||||
|
|
||||||
} elsif($opt =~ m/^(\d+):(.*):(\d+)/) {
|
} elsif($opt =~ m/^(\d+):(.*):(\d+)$/) {
|
||||||
$myPort = $1;
|
$myPort = $1;
|
||||||
$serverHost = $2;
|
$serverHost = $2;
|
||||||
$serverPort = $3;
|
$serverPort = $3;
|
||||||
@ -41,10 +48,10 @@ while(@ARGV) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
die $usage if(!$serverHost);
|
|
||||||
|
|
||||||
my ($sfd, $myfd, %clients, $discoMsg);
|
my ($sfd, $myfd, %clients, $discoMsg);
|
||||||
|
|
||||||
|
die $usage if(!$myPort);
|
||||||
|
|
||||||
sub
|
sub
|
||||||
tPrint($)
|
tPrint($)
|
||||||
{
|
{
|
||||||
@ -58,16 +65,18 @@ tPrint($)
|
|||||||
for(;;) {
|
for(;;) {
|
||||||
|
|
||||||
# Open the server first
|
# Open the server first
|
||||||
$sfd = IO::Socket::INET->new(PeerAddr => "$serverHost:$serverPort");
|
if($serverHost) {
|
||||||
if(!$sfd) {
|
$sfd = IO::Socket::INET->new(PeerAddr => "$serverHost:$serverPort");
|
||||||
tPrint "Cannot connect to $serverHost:$serverPort : $!" if(!$discoMsg);
|
if(!$sfd) {
|
||||||
|
tPrint "Cannot connect to $serverHost:$serverPort : $!" if(!$discoMsg);
|
||||||
|
$discoMsg = 1;
|
||||||
|
last if(!$loop);
|
||||||
|
sleep(5);
|
||||||
|
next;
|
||||||
|
}
|
||||||
$discoMsg = 1;
|
$discoMsg = 1;
|
||||||
last if(!$loop);
|
tPrint "Connected to $serverHost:$serverPort";
|
||||||
sleep(5);
|
|
||||||
next;
|
|
||||||
}
|
}
|
||||||
$discoMsg = 1;
|
|
||||||
tPrint "Connected to $serverHost:$serverPort";
|
|
||||||
|
|
||||||
|
|
||||||
# Now open our listener
|
# Now open our listener
|
||||||
@ -85,7 +94,7 @@ for(;;) {
|
|||||||
# Data loop
|
# Data loop
|
||||||
for(;;) {
|
for(;;) {
|
||||||
my ($rin,$rout) = ('','');
|
my ($rin,$rout) = ('','');
|
||||||
vec($rin, $sfd->fileno(), 1) = 1;
|
vec($rin, $sfd->fileno(), 1) = 1 if($sfd);
|
||||||
vec($rin, $myfd->fileno(), 1) = 1;
|
vec($rin, $myfd->fileno(), 1) = 1;
|
||||||
foreach my $c (keys %clients) {
|
foreach my $c (keys %clients) {
|
||||||
vec($rin, fileno($clients{$c}{fd}), 1) = 1;
|
vec($rin, fileno($clients{$c}{fd}), 1) = 1;
|
||||||
@ -110,11 +119,29 @@ for(;;) {
|
|||||||
$clients{$fd}{addr} = inet_ntoa($iaddr) . ":$port";
|
$clients{$fd}{addr} = inet_ntoa($iaddr) . ":$port";
|
||||||
tPrint "Connection accepted from $clients{$fd}{addr}";
|
tPrint "Connection accepted from $clients{$fd}{addr}";
|
||||||
|
|
||||||
|
if($ssl) {
|
||||||
|
tPrint "Attaching SSL";
|
||||||
|
eval "require IO::Socket::SSL";
|
||||||
|
if($@) {
|
||||||
|
tPrint "Can't load IO::Socket::SSL, falling back to plain";
|
||||||
|
} else {
|
||||||
|
my $ret = IO::Socket::SSL->start_SSL($fd, {
|
||||||
|
SSL_server => 1,
|
||||||
|
SSL_key_file => "certs/server-key.pem",
|
||||||
|
SSL_cert_file => "certs/server-cert.pem",
|
||||||
|
});
|
||||||
|
if(!$ret && $! ne "Socket is not connected") {
|
||||||
|
die "SSL/HTTPS error: $!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
syswrite($fd, $firstmsg) if($firstmsg);
|
syswrite($fd, $firstmsg) if($firstmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Data from the server
|
# Data from the server
|
||||||
if(vec($rout, $sfd->fileno(), 1)) {
|
if($sfd && vec($rout, $sfd->fileno(), 1)) {
|
||||||
my $buf;
|
my $buf;
|
||||||
my $ret = sysread($sfd, $buf, 256);
|
my $ret = sysread($sfd, $buf, 256);
|
||||||
if(!defined($ret) || $ret <= 0) {
|
if(!defined($ret) || $ret <= 0) {
|
||||||
@ -123,33 +150,37 @@ for(;;) {
|
|||||||
}
|
}
|
||||||
foreach my $c (keys %clients) {
|
foreach my $c (keys %clients) {
|
||||||
syswrite($clients{$c}{fd}, $buf);
|
syswrite($clients{$c}{fd}, $buf);
|
||||||
|
$clients{$c}{fd}->flush();
|
||||||
}
|
}
|
||||||
$firstmsg = $buf if(!$firstmsg);
|
$firstmsg = $buf if(!$firstmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Data from one of the clients
|
# Data from one of the clients
|
||||||
foreach my $c (keys %clients) {
|
CLIENT:foreach my $c (keys %clients) {
|
||||||
next if(!vec($rout, fileno($clients{$c}{fd}), 1));
|
next if(!vec($rout, fileno($clients{$c}{fd}), 1));
|
||||||
my $buf;
|
for(;;) {
|
||||||
my $ret = sysread($clients{$c}{fd}, $buf, 256);
|
my $buf;
|
||||||
if(!defined($ret) || $ret <= 0) {
|
my $ret = sysread($clients{$c}{fd}, $buf, 256);
|
||||||
close($clients{$c}{fd});
|
if(!defined($ret) || $ret <= 0) {
|
||||||
tPrint "Client $clients{$c}{addr} left us";
|
close($clients{$c}{fd});
|
||||||
delete($clients{$c});
|
tPrint "Client $clients{$c}{addr} left us";
|
||||||
next;
|
delete($clients{$c});
|
||||||
}
|
next CLIENT;
|
||||||
|
|
||||||
syswrite($sfd, $buf);
|
|
||||||
if($bidi) {
|
|
||||||
foreach my $c2 (keys %clients) {
|
|
||||||
syswrite($clients{$c2}{fd}, $buf) if($c2 ne $c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syswrite($sfd, $buf) if($sfd);
|
||||||
|
if($bidi) {
|
||||||
|
foreach my $c2 (keys %clients) {
|
||||||
|
syswrite($clients{$c2}{fd}, $buf) if($c2 ne $c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last if(!$ssl || !$clients{$c}{fd}->pending());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close($sfd);
|
close($sfd) if($sfd);
|
||||||
close($myfd);
|
close($myfd);
|
||||||
foreach my $c (keys %clients) {
|
foreach my $c (keys %clients) {
|
||||||
close($clients{$c}{fd});
|
close($clients{$c}{fd});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user