diff --git a/fhem/CHANGED b/fhem/CHANGED
index dd3685364..1923c9d74 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
+ - change: 34_ESPEasy: adjustments due to changes in TcpServerUtils.pm
- feature: 44_S7_ARead: supports for Logo7 and Logo8 short notations
- feature: 44_S7_AWrite: supports for Logo7 and Logo8 short notations
- feature: 37_Spotify: specify default device by its name
diff --git a/fhem/FHEM/34_ESPEasy.pm b/fhem/FHEM/34_ESPEasy.pm
index 266cb1f4c..50961d1b3 100644
--- a/fhem/FHEM/34_ESPEasy.pm
+++ b/fhem/FHEM/34_ESPEasy.pm
@@ -36,7 +36,7 @@ use Color;
# ------------------------------------------------------------------------------
# global/default values
# ------------------------------------------------------------------------------
-my $module_version = 1.11; # Version of this module
+my $module_version = 1.12; # Version of this module
my $minEEBuild = 128; # informational
my $minJsonVersion = 1.02; # checked in received data
@@ -52,6 +52,10 @@ my $d_resendFailedCmd = 0; # resend failed http requests by default?
my $d_displayTextEncode = 1; # urlEncode Text for Displays
my $d_displayTextWidth = 0; # display width, 0 => disable formating
+# IP ranges that are allowed to connect to ESPEasy without attr allowedIPs set.
+my $d_allowedIPs = "192.168.0.0/16,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,"
+ . "fe80::/10,::1";
+
# ------------------------------------------------------------------------------
# "setCmds" => "min. number of parameters"
# ------------------------------------------------------------------------------
@@ -553,9 +557,11 @@ sub ESPEasy_Read($) {
$Data::Dumper::Indent = 0;
$Data::Dumper::Terse = 1;
+ # Levering new TcpServerUtils security feature.
+ #$attr{$name}{allowfrom} = ".*" if !$attr{$name}{allowfrom};
# Accept and create a child
if( $hash->{SERVERSOCKET} ) {
- my $aRet = TcpServer_Accept($hash,"ESPEasy");
+ my $aRet = ESPEasy_TcpServer_Accept($hash,"ESPEasy");
return;
}
@@ -572,13 +578,13 @@ sub ESPEasy_Read($) {
return;
}
- $bhash->{SESSIONS} = scalar devspec2array("TYPE=$btype:FILTER=TEMPORARY=1")-1;
+ #$bhash->{SESSIONS} = scalar devspec2array("TYPE=$btype:FILTER=TEMPORARY=1")-1;
# Check attr disabled
return if (IsDisabled $bname);
# Check allowed IPs
- if ( !( ESPEasy_isPeerAllowed($peer,AttrVal($bname,"allowedIPs",1)) &&
+ if ( !( ESPEasy_isPeerAllowed($peer,AttrVal($bname,"allowedIPs", $d_allowedIPs)) &&
!ESPEasy_isPeerAllowed($peer,AttrVal($bname,"deniedIPs",0)) ) ) {
Log3 $bname, 2, "$btype $name: Peer address rejected";
return;
@@ -1561,6 +1567,121 @@ sub ESPEasy_tcpServerOpen($) {
}
+# ------------------------------------------------------------------------------
+# Duplicated sub from TcpServerUtils as a workaround for new security feature:
+# https://forum.fhem.de/index.php/topic,72717.0.html
+sub ESPEasy_TcpServer_Accept($$)
+{
+ my ($hash, $type) = @_;
+
+ my $name = $hash->{NAME};
+ my @clientinfo = $hash->{SERVERSOCKET}->accept();
+ if(!@clientinfo) {
+ Log3 $name, 1, "Accept failed ($name: $!)" if($! != EAGAIN);
+ 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);
+
+# ------------------------------------------------------------------------------
+# Removed from sub because we have our own access control system that works in
+# a more readable and flexible way (network ranges with allow/deny vs regexp).
+# Our new allowed ranges default are also now:
+# 127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fe80::/10,::1
+# ------------------------------------------------------------------------------
+#
+# my $af = $attr{$name}{allowfrom};
+# if(!$af) {
+# my $re = "^(127|192.168|172.(1[6-9]|2[0-9]|3[01])|10|169.254)\\.|".
+# "^(fe[89ab]|::1)";
+# if($caddr !~ m/$re/) {
+# my %empty;
+# $hash->{SNAME} = $hash->{NAME};
+# my $auth = Authenticate($hash, \%empty);
+# delete $hash->{SNAME};
+# if($auth == 0) {
+# Log3 $name, 1,
+# "Connection refused from the non-local address $caddr:$port, ".
+# "as there is no working allowed instance defined for it";
+# close($clientinfo[0]);
+# return undef;
+# }
+# }
+# }
+#
+# if($af) {
+# if($caddr !~ m/$af/) {
+# my $hostname = gethostbyaddr($iaddr, AF_INET);
+# if(!$hostname || $hostname !~ m/$af/) {
+# Log3 $name, 1, "Connection refused from $caddr:$port";
+# close($clientinfo[0]);
+# return undef;
+# }
+# }
+# }
+
+ #$clientinfo[0]->blocking(0); # Forum #24799
+
+ if($hash->{SSL}) {
+ # Forum #27565: SSLv23:!SSLv3:!SSLv2', #35004: TLSv12:!SSLv3
+ my $sslVersion = AttrVal($hash->{NAME}, "sslVersion",
+ AttrVal("global", "sslVersion", "TLSv12:!SSLv3"));
+
+ # 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",
+ SSL_version => $sslVersion,
+ SSL_cipher_list => 'HIGH:!RC4:!eNULL:!aNULL',
+ Timeout => 4,
+ });
+ my $err = $!;
+ if( !$ret
+ && $err != EWOULDBLOCK
+ && $err ne "Socket is not connected") {
+ $err = "" if(!$err);
+ $err .= " ".($SSL_ERROR ? $SSL_ERROR : IO::Socket::SSL::errstr());
+ Log3 $name, 1, "$type SSL/HTTPS error: $err"
+ if($err !~ m/error:00000000:lib.0.:func.0.:reason.0./); #Forum 56364
+ close($clientinfo[0]);
+ return undef;
+ }
+ }
+
+ my $cname = "${name}_${caddr}_${port}";
+ my %nhash;
+ $nhash{NR} = $devcount++;
+ $nhash{NAME} = $cname;
+ $nhash{PEER} = $caddr;
+ $nhash{PORT} = $port;
+ $nhash{FD} = $clientinfo[0]->fileno();
+ $nhash{CD} = $clientinfo[0]; # sysread / close won't work on fileno
+ $nhash{TYPE} = $type;
+ $nhash{SSL} = $hash->{SSL};
+ $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;
+
+ my $ret = $clientinfo[0]->setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1);
+
+ Log3 $name, 4, "Connection accepted from $nhash{NAME}";
+ return \%nhash;
+}
+
+
# ------------------------------------------------------------------------------
sub ESPEasy_header2Hash($) {
my ($string) = @_;
@@ -2532,7 +2653,8 @@ sub ESPEasy_removeGit($)
as bitmask or dotted decimal. Domain names for dns lookups are not
supported.
Possible values: IPv64 address, IPv64/netmask
- Default: 0.0.0.0/0 (all IPs are allowed)
+ Default: 127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fe80::/10,::1
+
Eg. 10.68.30.147
Eg. 10.68.30.0/24,10.68.31.0/255.255.248.0
Eg. fe80::/10,2001:1a59:50a9::/48,2002:1a59:50a9::,2003:1a59:50a9:acdc::36