mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-01 07:19:24 +00:00
bf3d1a0700
introduced GPLv2 git-svn-id: https://svn.fhem.de/fhem/trunk@2099 2b470e98-0d58-463d-a4d8-8e2adae1ed80
151 lines
3.4 KiB
Perl
151 lines
3.4 KiB
Perl
##############################################
|
|
# $Id$
|
|
|
|
package main;
|
|
use strict;
|
|
use warnings;
|
|
use IO::Socket;
|
|
|
|
sub
|
|
TcpServer_Open($$$)
|
|
{
|
|
my ($hash, $port, $global) = @_;
|
|
my $name = $hash->{NAME};
|
|
|
|
if($port =~ m/^IPV6:(\d+)$/i) {
|
|
$port = $1;
|
|
eval "require IO::Socket::INET6; use Socket6;";
|
|
if($@) {
|
|
Log 1, $@;
|
|
Log 1, "$name: Can't load INET6, falling back to IPV4";
|
|
} else {
|
|
$hash->{IPV6} = 1;
|
|
}
|
|
}
|
|
|
|
my @opts = (
|
|
Domain => ($hash->{IPV6} ? AF_INET6() : AF_UNSPEC), # Linux bug
|
|
LocalHost => ($global ? undef : "localhost"),
|
|
LocalPort => $port,
|
|
Listen => 10,
|
|
ReuseAddr => 1
|
|
);
|
|
$hash->{STATE} = "Initialized";
|
|
$hash->{SERVERSOCKET} = $hash->{IPV6} ?
|
|
IO::Socket::INET6->new(@opts) :
|
|
IO::Socket::INET->new(@opts);
|
|
|
|
if(!$hash->{SERVERSOCKET}) {
|
|
return "$name: Can't open server port at $port: $!";
|
|
}
|
|
|
|
$hash->{FD} = $hash->{SERVERSOCKET}->fileno();
|
|
$hash->{PORT} = $port;
|
|
|
|
$selectlist{"$name.$port"} = $hash;
|
|
Log(3, "$name: port $port opened");
|
|
return undef;
|
|
}
|
|
|
|
sub
|
|
TcpServer_Accept($$)
|
|
{
|
|
my ($hash, $type) = @_;
|
|
|
|
my $name = $hash->{NAME};
|
|
my $ll = GetLogLevel($name,4);
|
|
my @clientinfo = $hash->{SERVERSOCKET}->accept();
|
|
if(!@clientinfo) {
|
|
Log 1, "Accept failed ($name: $!)";
|
|
return undef;
|
|
}
|
|
$hash->{CONNECTS}++;
|
|
|
|
my ($port, $iaddr) = $hash->{IPV6} ?
|
|
sockaddr_in6($clientinfo[1]) :
|
|
sockaddr_in($clientinfo[1]);
|
|
my $caddr = $hash->{IPV6} ?
|
|
inet_ntop(AF_INET6(), $iaddr) :
|
|
inet_ntoa($iaddr);
|
|
|
|
my $af = $attr{$name}{allowfrom};
|
|
if($af) {
|
|
if($caddr !~ m/$af/) {
|
|
my $hostname = gethostbyaddr($iaddr, AF_INET);
|
|
if(!$hostname || $hostname !~ m/$af/) {
|
|
Log 1, "Connection refused from $caddr:$port";
|
|
close($clientinfo[0]);
|
|
return undef;
|
|
}
|
|
}
|
|
}
|
|
|
|
if($hash->{SSL}) {
|
|
# Certs directory must be in the modpath, i.e. at the same level as the
|
|
# FHEM directory
|
|
my $mp = AttrVal("global", "modpath", ".");
|
|
my $ret = IO::Socket::SSL->start_SSL($clientinfo[0], {
|
|
SSL_server => 1,
|
|
SSL_key_file => "$mp/certs/server-key.pem",
|
|
SSL_cert_file => "$mp/certs/server-cert.pem",
|
|
});
|
|
if(!$ret && $! ne "Socket is not connected") {
|
|
Log 1, "$type SSL/HTTPS error: $!";
|
|
close($clientinfo[0]);
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
my $cname = "$type:$caddr:$port";
|
|
my %nhash;
|
|
$nhash{NR} = $devcount++;
|
|
$nhash{NAME} = $cname;
|
|
$nhash{FD} = $clientinfo[0]->fileno();
|
|
$nhash{CD} = $clientinfo[0]; # sysread / close won't work on fileno
|
|
$nhash{TYPE} = $type;
|
|
$nhash{STATE} = "Connected";
|
|
$nhash{SNAME} = $name;
|
|
$nhash{TEMPORARY} = 1; # Don't want to save it
|
|
$nhash{BUF} = "";
|
|
$attr{$cname}{room} = "hidden";
|
|
$defs{$cname} = \%nhash;
|
|
$selectlist{$nhash{NAME}} = \%nhash;
|
|
|
|
|
|
Log($ll, "Connection accepted from $nhash{NAME}");
|
|
return \%nhash;
|
|
}
|
|
|
|
sub
|
|
TcpServer_SetSSL($)
|
|
{
|
|
my ($hash) = @_;
|
|
eval "require IO::Socket::SSL";
|
|
if($@) {
|
|
Log 1, $@;
|
|
Log 1, "Can't load IO::Socket::SSL, falling back to HTTP";
|
|
} else {
|
|
$hash->{SSL} = 1;
|
|
}
|
|
}
|
|
|
|
|
|
sub
|
|
TcpServer_Close($)
|
|
{
|
|
my ($hash) = @_;
|
|
my $name = $hash->{NAME};
|
|
|
|
if(defined($hash->{CD})) { # Clients
|
|
close($hash->{CD});
|
|
delete($selectlist{$name});
|
|
}
|
|
if(defined($hash->{SERVERSOCKET})) { # Server
|
|
close($hash->{SERVERSOCKET});
|
|
$name = $name . "." . $hash->{PORT};
|
|
delete($selectlist{$name});
|
|
}
|
|
return undef;
|
|
}
|
|
1;
|