Correct socket use in cert_expiry platform (#25011)

* Make sure we use same family for ssl socket and connection

getaddrinfo result could be different from what connection
was made with. It also blocks potential use of
happy eye balls algorithm

This also fixes lingering sockets until python garbage
collection.

* Add availability value if unable to get expiry

* Fix lint issue
This commit is contained in:
Joakim Plate 2019-07-08 11:33:23 +02:00 committed by Pascal Vizeli
parent 31d7b702a6
commit c2f1c4b981

View File

@ -55,6 +55,7 @@ class SSLCertificate(Entity):
self.server_port = server_port self.server_port = server_port
self._name = sensor_name self._name = sensor_name
self._state = None self._state = None
self._available = False
@property @property
def name(self): def name(self):
@ -76,34 +77,39 @@ class SSLCertificate(Entity):
"""Icon to use in the frontend, if any.""" """Icon to use in the frontend, if any."""
return 'mdi:certificate' return 'mdi:certificate'
@property
def available(self):
"""Icon to use in the frontend, if any."""
return self._available
def update(self): def update(self):
"""Fetch the certificate information.""" """Fetch the certificate information."""
try:
ctx = ssl.create_default_context() ctx = ssl.create_default_context()
host_info = socket.getaddrinfo(self.server_name, self.server_port) try:
family = host_info[0][0] address = (self.server_name, self.server_port)
sock = ctx.wrap_socket( with socket.create_connection(
socket.socket(family=family), server_hostname=self.server_name) address, timeout=TIMEOUT) as sock:
sock.settimeout(TIMEOUT) with ctx.wrap_socket(
sock.connect((self.server_name, self.server_port)) sock, server_hostname=address[0]) as ssock:
cert = ssock.getpeercert()
except socket.gaierror: except socket.gaierror:
_LOGGER.error("Cannot resolve hostname: %s", self.server_name) _LOGGER.error("Cannot resolve hostname: %s", self.server_name)
self._available = False
return return
except socket.timeout: except socket.timeout:
_LOGGER.error( _LOGGER.error(
"Connection timeout with server: %s", self.server_name) "Connection timeout with server: %s", self.server_name)
self._available = False
return return
except OSError: except OSError:
_LOGGER.error("Cannot connect to %s", self.server_name) _LOGGER.error("Cannot fetch certificate from %s",
return self.server_name, exc_info=1)
self._available = False
try:
cert = sock.getpeercert()
except OSError:
_LOGGER.error("Cannot fetch certificate from %s", self.server_name)
return return
ts_seconds = ssl.cert_time_to_seconds(cert['notAfter']) ts_seconds = ssl.cert_time_to_seconds(cert['notAfter'])
timestamp = datetime.fromtimestamp(ts_seconds) timestamp = datetime.fromtimestamp(ts_seconds)
expiry = timestamp - datetime.today() expiry = timestamp - datetime.today()
self._available = True
self._state = expiry.days self._state = expiry.days