Skip to content

Commit

Permalink
Correct socket use in cert_expiry platform (home-assistant#25011)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
elupus authored and pvizeli committed Jul 8, 2019
1 parent 31d7b70 commit c2f1c4b
Showing 1 changed file with 20 additions and 14 deletions.
34 changes: 20 additions & 14 deletions homeassistant/components/cert_expiry/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def __init__(self, sensor_name, server_name, server_port):
self.server_port = server_port
self._name = sensor_name
self._state = None
self._available = False

@property
def name(self):
Expand All @@ -76,34 +77,39 @@ def icon(self):
"""Icon to use in the frontend, if any."""
return 'mdi:certificate'

@property
def available(self):
"""Icon to use in the frontend, if any."""
return self._available

def update(self):
"""Fetch the certificate information."""
ctx = ssl.create_default_context()
try:
ctx = ssl.create_default_context()
host_info = socket.getaddrinfo(self.server_name, self.server_port)
family = host_info[0][0]
sock = ctx.wrap_socket(
socket.socket(family=family), server_hostname=self.server_name)
sock.settimeout(TIMEOUT)
sock.connect((self.server_name, self.server_port))
address = (self.server_name, self.server_port)
with socket.create_connection(
address, timeout=TIMEOUT) as sock:
with ctx.wrap_socket(
sock, server_hostname=address[0]) as ssock:
cert = ssock.getpeercert()

except socket.gaierror:
_LOGGER.error("Cannot resolve hostname: %s", self.server_name)
self._available = False
return
except socket.timeout:
_LOGGER.error(
"Connection timeout with server: %s", self.server_name)
self._available = False
return
except OSError:
_LOGGER.error("Cannot connect to %s", self.server_name)
return

try:
cert = sock.getpeercert()
except OSError:
_LOGGER.error("Cannot fetch certificate from %s", self.server_name)
_LOGGER.error("Cannot fetch certificate from %s",
self.server_name, exc_info=1)
self._available = False
return

ts_seconds = ssl.cert_time_to_seconds(cert['notAfter'])
timestamp = datetime.fromtimestamp(ts_seconds)
expiry = timestamp - datetime.today()
self._available = True
self._state = expiry.days

0 comments on commit c2f1c4b

Please sign in to comment.