From 6e7327347464b49081322624e67937204241f4f0 Mon Sep 17 00:00:00 2001 From: Alexandre Sieira Date: Tue, 25 Feb 2014 17:07:39 -0300 Subject: [PATCH 1/4] Reimplemented with python-requests Replaced usage of `urllib2` with `requests`, using explicit option to disable SSL certificate validation (for now). --- .gitignore | 2 ++ dnsdb_query.py | 24 +++++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9af213 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.project +.pydevproject diff --git a/dnsdb_query.py b/dnsdb_query.py index d8f3fdc..c166169 100755 --- a/dnsdb_query.py +++ b/dnsdb_query.py @@ -20,7 +20,7 @@ import os import sys import time -import urllib2 +import requests from cStringIO import StringIO try: @@ -68,23 +68,25 @@ def query_rdata_ip(self, rdata_ip): def _query(self, path): res = [] url = '%s/lookup/%s' % (self.server, path) + headers = {'Accept': 'application/json', 'X-Api-Key': self.apikey} + #try: if self.limit: - url += '?limit=%d' % self.limit - req = urllib2.Request(url) - req.add_header('Accept', 'application/json') - req.add_header('X-Api-Key', self.apikey) - try: - http = urllib2.urlopen(req) - while True: - line = http.readline() + response = requests.get(url, params='?limit=%d' % self.limit, headers=headers, verify=False) + else: + response = requests.get(url, headers=headers, verify=False) + if (response.status_code != 200): + sys.stderr.write(str(response.status_code) + " " + response.reason) + else: + for line in response.iter_lines(): if not line: break if self.json: res.append(line) else: res.append(json.loads(line)) - except urllib2.HTTPError, e: - sys.stderr.write(str(e) + '\n') + #except: + # e = sys.exc_info()[0] + # sys.stderr.write(str(e) + '\n') return res def sec_to_text(ts): From 01cabc2e0835895e30f49b69db1b6707d4030a96 Mon Sep 17 00:00:00 2001 From: Alexandre Sieira Date: Tue, 25 Feb 2014 17:25:45 -0300 Subject: [PATCH 2/4] Certificate validation with pinning Implemented two new options on the configuration file: * `VERIFY`: if true, will make the server certificate be validated as a browser would. Defaults to no validation for backwards compatibility with previous version. * `CA_BUNDLE`: path to file containing additional certificate(s) to consider valid, such as a self-signed certificate used by your server. --- dnsdb_query.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/dnsdb_query.py b/dnsdb_query.py index c166169..c595cd9 100755 --- a/dnsdb_query.py +++ b/dnsdb_query.py @@ -69,13 +69,14 @@ def _query(self, path): res = [] url = '%s/lookup/%s' % (self.server, path) headers = {'Accept': 'application/json', 'X-Api-Key': self.apikey} - #try: if self.limit: - response = requests.get(url, params='?limit=%d' % self.limit, headers=headers, verify=False) + params = '?limit=%d' % self.limit else: - response = requests.get(url, headers=headers, verify=False) + params = "" + #try: + response = requests.get(url, params=params, headers=headers, verify=cfg['VERIFY']) if (response.status_code != 200): - sys.stderr.write(str(response.status_code) + " " + response.reason) + sys.stderr.write(str(response.status_code) + " " + response.reason + "\n") else: for line in response.iter_lines(): if not line: @@ -221,7 +222,15 @@ def main(): if not 'APIKEY' in cfg: sys.stderr.write('dnsdb_query: APIKEY not defined in config file\n') sys.exit(1) - + if not 'VERIFY' in cfg: + cfg['VERIFY'] = False + elif cfg['VERIFY'].lower() in ['true', '1', 't', 'y', 'yes']: + cfg['VERIFY'] = True + if 'CA_BUNDLE' in cfg: + cfg['VERIFY'] = cfg['CA_BUNDLE'] + else: + cfg['VERIFY'] = False + client = DnsdbClient(cfg['DNSDB_SERVER'], cfg['APIKEY'], options.limit, options.json) if options.rrset: res_list = client.query_rrset(*options.rrset.split('/')) From 9dc937f3cee12148d3d2eb25f3a300a9429f8390 Mon Sep 17 00:00:00 2001 From: Alexandre Sieira Date: Tue, 25 Feb 2014 17:30:49 -0300 Subject: [PATCH 3/4] Moved verify option to DnsdbClient class Following structure of previous code, which allows different DnsdbClient instances to behave differently if the class is ever used by more complex code. --- dnsdb_query.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/dnsdb_query.py b/dnsdb_query.py index c595cd9..7f2a6cf 100755 --- a/dnsdb_query.py +++ b/dnsdb_query.py @@ -37,11 +37,12 @@ locale.setlocale(locale.LC_ALL, '') class DnsdbClient(object): - def __init__(self, server, apikey, limit=None, json=False): + def __init__(self, server, apikey, limit=None, json=False, verify=False): self.server = server self.apikey = apikey self.limit = limit self.json = json + self.verify = verify def query_rrset(self, oname, rrtype=None, bailiwick=None): if bailiwick: @@ -73,8 +74,7 @@ def _query(self, path): params = '?limit=%d' % self.limit else: params = "" - #try: - response = requests.get(url, params=params, headers=headers, verify=cfg['VERIFY']) + response = requests.get(url, params=params, headers=headers, verify=self.verify) if (response.status_code != 200): sys.stderr.write(str(response.status_code) + " " + response.reason + "\n") else: @@ -85,9 +85,6 @@ def _query(self, path): res.append(line) else: res.append(json.loads(line)) - #except: - # e = sys.exc_info()[0] - # sys.stderr.write(str(e) + '\n') return res def sec_to_text(ts): @@ -231,7 +228,7 @@ def main(): else: cfg['VERIFY'] = False - client = DnsdbClient(cfg['DNSDB_SERVER'], cfg['APIKEY'], options.limit, options.json) + client = DnsdbClient(cfg['DNSDB_SERVER'], cfg['APIKEY'], options.limit, options.json, cfg['VERIFY']) if options.rrset: res_list = client.query_rrset(*options.rrset.split('/')) fmt_func = rrset_to_text From 7efd5fa2395adb98f46d7b9f68ee66657c3d90f5 Mon Sep 17 00:00:00 2001 From: Alexandre Sieira Date: Tue, 25 Feb 2014 18:04:44 -0300 Subject: [PATCH 4/4] Fix to handling errors (non-200 status code) --- dnsdb_query.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsdb_query.py b/dnsdb_query.py index 7f2a6cf..445e1c3 100755 --- a/dnsdb_query.py +++ b/dnsdb_query.py @@ -75,8 +75,9 @@ def _query(self, path): else: params = "" response = requests.get(url, params=params, headers=headers, verify=self.verify) - if (response.status_code != 200): + if response.status_code != 200: sys.stderr.write(str(response.status_code) + " " + response.reason + "\n") + return res else: for line in response.iter_lines(): if not line: