From 6b4f5b228eb37582313be748623199ab006706c1 Mon Sep 17 00:00:00 2001 From: Stephane Bortzmeyer Date: Wed, 7 Jul 2021 11:57:27 +0200 Subject: [PATCH] Timeouts in HTTP requests --- check_expire | 38 +++++++++++++++++++++++++++----------- ianardap.py | 3 ++- tests.yaml | 12 +++++++++++- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/check_expire b/check_expire index be4f531..f4ae0af 100755 --- a/check_expire +++ b/check_expire @@ -43,10 +43,10 @@ domain = None critical_t = datetime.timedelta(days=2) warning_t = datetime.timedelta(days=7) unixtime = False -# TODO implement timeout for HTTPS requests. -t option. Does Requests allow it? +timeout = 20 # Seconds def usage(msg=None): - print("Usage: %s -H domain-name [-c critical -w warning -u]" % sys.argv[0], end="") + print("Usage: %s -H domain-name [-c critical -w warning -u -t timeout]" % sys.argv[0], end="") if msg is not None and msg != "": print(" (%s)" % msg) else: @@ -73,6 +73,13 @@ def warning(msg=None): details() sys.exit(STATE_WARNING) +def unknown(msg=None): + if msg is None: + msg = "Unknown" + print("%s UNKNOWN: %s" % (domain, msg), end="") + details() + sys.exit(STATE_UNKNOWN) + def ok(msg=None): if msg is None: msg = "Unknown message but everything is OK" @@ -81,8 +88,10 @@ def ok(msg=None): sys.exit(STATE_OK) try: - optlist, args = getopt.getopt (sys.argv[1:], "c:hH:uvVw:", - ["critical=", "expiration", "help", "unixtime", "verbose", "version", "warning="]) + optlist, args = getopt.getopt (sys.argv[1:], "c:hH:t:uvVw:", + ["critical=", "expiration", "help", + "timeout=", "unixtime", "verbose", + "version", "warning="]) for option, value in optlist: if option == "--critical" or option == "-c": critical_t = datetime.timedelta(days=int(value)) @@ -91,6 +100,10 @@ try: sys.exit(STATE_OK) elif option == "--hostname" or option == "-H": domain = value + elif option == "--timeout" or option == "-t": + timeout = int(value) + elif option == "--unixtime" or option == "-u": + unixtime = True elif option == "--verbose" or option == "-v": verbose = True elif option == "--version" or option == "-V": @@ -98,8 +111,6 @@ try: sys.exit(STATE_OK) elif option == "--warning" or option == "-w": warning_t = datetime.timedelta(days=int(value)) - elif option == "--unixtime" or option == "-u": - unixtime = True else: # Should never occur, it is trapped by getopt print("Unknown option %s" % option) @@ -113,13 +124,19 @@ if len(args) != 0: if domain is None: usage("-H is mandatory") sys.exit(STATE_UNKNOWN) -database = ianardap.IanaRDAPDatabase() +try: + database = ianardap.IanaRDAPDatabase() +except Exception as e: + unknown("Exception when retrieving the IANA database: \"%s\"" % e) server = database.find(domain) if server is None: - error("No RDAP server found for %s" % domain) + unknown("No RDAP server found for %s" % domain) if server.endswith("/"): server = server[:-1] # Donuts RDAP server balks when there are two slashes and reply 404 -response = requests.get("%s/domain/%s" % (server, domain)) +try: + response = requests.get("%s/domain/%s" % (server, domain), timeout=timeout) +except requests.exceptions.Timeout: + unknown("Timeout when trying to reach %s" % server) if response.status_code != 200: error("Invalid RDAP return code: %s" % response.status_code) rdap = json.loads(response.content) @@ -136,8 +153,7 @@ for event in rdap["events"]: dt = re.sub(r"\+([0-9]{2}):([0-9]{2})$", r"+\1\2", event["eventDate"]) expiration = datetime.datetime.strptime(dt, RFC3339TZ) else: - print("No recognized format for datetime \"%s\"" % event["eventDate"]) - sys.exit(STATE_UNKNOWN) + unknown("No recognized format for datetime \"%s\"" % event["eventDate"]) now = datetime.datetime.now(tz=UTCINFO) if unixtime == True: print(int(expiration.strftime("%s"))) diff --git a/ianardap.py b/ianardap.py index 6e976d4..24cd5e6 100644 --- a/ianardap.py +++ b/ianardap.py @@ -17,6 +17,7 @@ import fcntl IANABASE = "https://data.iana.org/rdap/dns.json" CACHE = os.environ["HOME"] + "/.ianardapcache.json" MAXAGE = 24 # Hours +IANATIMEOUT = 10 class IanaRDAPDatabase(): @@ -36,7 +37,7 @@ class IanaRDAPDatabase(): self.unlock() else: self.unlock() - response = requests.get(IANABASE) + response = requests.get(IANABASE, timeout=IANATIMEOUT) if response.status_code != 200: raise Exception("Invalid HTTPS return code when trying to get %s: %s" % (IANABASE, response.status_code)) content = response.content diff --git a/tests.yaml b/tests.yaml index f5581e3..d23002c 100644 --- a/tests.yaml +++ b/tests.yaml @@ -33,7 +33,7 @@ tests: args: - '-H' - 'bie.re' - retcode: 2 + retcode: 3 partstdout: 'No RDAP server' # 2021-07-05: no expiration in RDAP for this TLD (but there is one @@ -45,6 +45,16 @@ tests: retcode: 2 partstdout: 'No expiration found' + # Far away and slow, timeout is expected + - exe: './check_expire' + args: + - '-t' + - '1' + - '-H' + - 'nic.ar' + retcode: 3 + partstdout: 'Timeout' + - exe: './check_expire' args: - '-H'