def to_file(self, filename): today = datetime.date.today(); curser = int(self._serial); newser = int('{}{}{}00'.format(today.year, str(today.month).zfill(2), str(today.day).zfill(2))); while newser <= curser: newser += 1; self._records['@']['SOA'][0].serial = newser; self._getSOA(); origin = "{}.".format(self._zname) z = dns.zone.Zone(dns.name.from_text(origin)); for rname in self._records: for rtype in self._records[rname]: dset = dns.rdataset.Rdataset(rdc.from_text('IN'), rdt.from_text(rtype)); dset.ttl = 300; for r in self._records[rname][rtype]: dset.add(r.to_rdata(origin=origin)); z.replace_rdataset(rname, dset); tmpfile = '/tmp/tutu-dns-tmp-{}'.format(self._zname); z.to_file(tmpfile); fr = open(tmpfile, 'r'); with open(filename, 'wt') as fh: fh.write("$ORIGIN {}\n$TTL 300\n".format(origin)); for line in fr.readlines(): fh.write(line);
def to_rdata(self, origin=dns.name.root): if not isinstance(origin, dns.name.Name): if type(origin) == str: origin = dns.name.from_text(origin); params = {}; for attrib in self._slots: if attrib in ('mname', 'rname', 'target', 'exchange'): params[attrib] = dns.name.from_text(getattr(self, attrib), origin); elif attrib in ('serial', 'refresh', 'retry', 'expire', 'minimum', 'priority', 'preference', 'port'): params[attrib] = int(getattr(self, attrib)); else: params[attrib] = getattr(self, attrib); params['rdclass'] = rdc.from_text(self._rclass); params['rdtype'] = rdt.from_text(self._rtype); rdata = dns.rdata.get_rdata_class(rdc.from_text(self._rclass), rdt.from_text(self._rtype))(**params); return rdata;
def __init__(self, rtype, rclass='IN', ttl=300): self._rtype = rtype; self._rclass = rclass; self._ttl = ttl; if not self._check_type(rtype): raise NotImplementedError('Not a valid type ({})'.format(rtype)); if not self._check_class(rclass): raise NotImplementedError('Not a valid class ({})'.format(rclass)); self._slots = dns.rdata.get_rdata_class(rdc.from_text(rclass), rdt.from_text(rtype)).__slots__;
def delete(self): session = self.request.session; try: rzone = session['rzone']; except NameError: return HTTPFound('/dns/zones'); try: oname = session['rname']; rtype = session['rtype']; odata = session['rdata']; rclass = session['rclass']; except NameError: return HTTPFound('/dns/zone/{}'.format(rzone)); namedconf = tutuconfig.get('namedconf', 'dnsbind'); ncp = NamedConfParser(); ncp.from_file(namedconf); zonefile = ncp.find_zone_file(rzone); z = zone.from_file(zonefile); dset = z.get_rdataset(oname, rtype); newdset = dns.rdataset.Rdataset(rdc.from_text(rclass), rdt.from_text(rtype)); newdset.ttl = 300; if rtype == 'SOA': newdset.add(rdata); else: for rd in dset: if odata == rd.to_text(): pass else: newdset.add(rd); z.replace_rdataset(oname, newdset); tutuzone.save_zone(z, zonefile); return HTTPFound('/dns/zone/{}'.format(rzone)); # vim: set ts=2:
def resolve(self, name, qtype): ''' Resolves a Blockchain-based (Namecoin) DNS Name via 2 step process using DNSSEC Step 1: ------- Get NS and DS records for the Namecoin name (for example: www.mattdavid.bit) from the Namecoin Client Step 2: ------- For each listed nameserver: - Create a temporary config file for use with unbound - Set Unbound's Trust Anchor to be the given DS records for the Namecoin-based domain name - Do DNSSEC-enabled DNS resolution for the given name / qtype :param name: DNS Record Name Query (for example: www.mattdavid.bit) :param qtype: String representation of query type (for example: A, AAAA, TXT, NS, SOA, etc...) :return: Resolved value if successful, None if un-successful ''' name = name.rstrip('.') if not name.endswith('.bit'): raise ValueError('This is not a valid .bit domain') domains = name.split('.') domains.reverse() if len(domains) < 2: raise ValueError('At least SLD Required') # Get Namecoin-based Domain Info from Namecoin Blockchain nc_domain = self.nc_name_resolver.name_show(domains[1]) if not nc_domain or not nc_domain.get('value'): log.error( 'No Name Value Data Found for Namecoin-based Domain Name: d/%s' % domains[1]) raise NamecoinValueException('No Name Value Data Found for: d/%s' % domains[1]) nc_value = json.loads(nc_domain.get('value', '{}').replace('\'', '"')) if not nc_value.get('ds'): log.error( 'No DS Records Present for Namecoin-based Domain Name: %s' % name) raise NoDSRecordException() if not nc_value.get('ns'): log.error( 'No NS Records Present for Namecoin-based Domain Name: %s' % name) raise NoNameserverException() sld = '%s.%s.' % (domains[1], domains[0]) ds_record = ' '.join([str(x) for x in nc_value['ds'][0][0:3]]) # Handle both Hex and Base64 encoding (Base64 is the preferred encoding) per: # https://wiki.namecoin.info/index.php?title=Domain_Name_Specification if re.match('^[0-9a-fA-F]*$', nc_value['ds'][0][3]): ds_record += ' %s' % nc_value['ds'][0][3] else: ds_record += ' %s' % base64.b64decode( nc_value['ds'][0][3]).encode('hex').upper() ds_ta = '%s IN DS %s' % (sld, ds_record) ns_ctx = ub_ctx() ns_ctx.resolvconf(self.resolv_conf) if not os.path.isfile(self.dnssec_root_key): log.error("Trust anchor missing or inaccessible") raise Exception("Trust anchor is missing or inaccessible: %s" % self.dnssec_root_key) else: ns_ctx.add_ta_file(self.dnssec_root_key) last_error = None for ns in nc_value.get('ns', []): lookup_value = None status, result = ns_ctx.resolve(ns, rdatatype.from_text('A'), rdataclass.from_text('IN')) # NOTE: We do not require secure DNS resolution here because the Blockchain-stored DS records work as the trust anchor # and the signed RRSIG DNS results from the final DNS+DNSSEC lookup will be able to complete the chain of trust if status == 0 and result and result.data and not result.bogus: tmp_config_file = self._build_temp_unbound_config( sld, result.data.as_address_list()[0]) else: last_error = InvalidNameserverException() log.warn('No or Invalid Resolution Result for Nameserver: %s' % ns) continue ctx = ub_ctx() ctx.config(tmp_config_file) ctx.add_ta(str(ds_ta)) _qtype = None try: _qtype = rdatatype.from_text(qtype) except Exception as e: log.error( 'Unable to get RDATAType for Given Query Type [%s]: %s' % (qtype, str(e))) raise ValueError('Unable to get RDATAType for Query Type %s' % qtype) status, result = ctx.resolve(name, _qtype, rdataclass.from_text('IN')) if status != 0: log.info("DNS Resolution Failed: %s [%s]" % (name, _qtype)) elif status == 0: if not result.secure: log.info( "DNS Resolution Returned Insecure Result: %s [%s]" % (name, qtype)) last_error = InsecureResultException() elif result.bogus: log.info("DNS Resolution Returned Bogus Result: %s [%s]" % (name, qtype)) last_error = BogusResultException() elif not result.havedata: log.info("DNS Resolution Returned Empty Result: %s [%s]" % (name, qtype)) last_error = EmptyResultException() else: # Get appropriate data by query type if qtype in ['A', 'AAAA']: lookup_value = result.data.as_address_list() elif qtype in ['CNAME', 'TXT']: lookup_value = result.data.as_domain_list() elif qtype in ['MX']: lookup_value = result.data.as_mx_list() else: last_error = NotImplementedError( 'Unsupported DNS Query Type: %s' % qtype) self._delete_temp_unbound_config(tmp_config_file) if lookup_value: return lookup_value[0] if last_error and isinstance(last_error, NotImplementedError): raise last_error log.error('DNS Resolution Failed: %s [%s]' % (name, qtype)) if last_error: raise last_error return None
def create(self): if self.posted(): session = self.request.session; try: rzone = session['rzone']; except NameError: return HTTPFound('/dns/zones'); try: oname = session['rname']; rtype = session['rtype']; odata = session['rdata']; rclass = session['rclass']; except NameError: return HTTPFound('/dns/zone/{}'.format(rzone)); errors = {}; errors['name'] = 0; rname = self.request.POST['name']; if len(rname) == 0: errors['name'] = 1; params = {}; record = []; for data in self.request.POST: tmprec = {}; tmprec['name'] = data; tmprec['value'] = self.request.POST[data]; if data != 'name': record.append(tmprec); errors[data] = 0; if len(self.request.POST[data]) == 0: errors[data] = 1; continue; if data in ('serial', 'refresh', 'retry', 'expire', 'minimum', 'priority', 'preference', 'port'): params[data] = int(self.request.POST[data]); elif data != 'name': params[data] = self.request.POST[data]; errorcount = 0; for error in errors: errorcount += errors[error]; if errorcount > 0: if rname == '@': pname = rzone; else: pname = "{}.{}".format(rname, rzone); return {'rname': rname, 'pname':pname, 'rzone': rzone, 'rtype': rtype, 'record': record, 'helpers':tuturecord.helpers, 'errors': errors}; r = tuturecord.Record(rtype, rclass); for param in params: setattr(r, param, params[param]); z = tutuzone.Zone(rzone); z.load(); z.replace_record(oname, odata, rname, r); z.save(); return HTTPFound(location='/dns/zone/{}'.format(rzone)); rzone = self.request.params.get('zone', None); if rzone is None: return HTTPFound(location='/dns/zones'); rtype = self.request.params.get('type', None); if rtype is None: return HTTPFound(location='/dns/zone/{}'.format(rzone)); z = tutuzone.Zone(rzone); if z.is_reverse(): types = tuturecord.Record.reverse_supported_types; else: types = tuturecord.Record.forward_supported_types; if not rtype in types: return HTTPFound(location='/dns/zone/{}'.format(rzone)); session = self.request.session; session['rzone'] = rzone; session['rname'] = None; session['rtype'] = rtype; session['rdata'] = None; session['rclass'] = 'IN'; session.changed(); slots = dns.rdata.get_rdata_class(rdc.from_text('IN'), rdt.from_text(rtype)).__slots__; record = []; errors = {}; for slot in slots: errors[slot] = 0; record.append({'name': slot, 'value': ''}); return {'rname': '', 'pname':'', 'rzone': rzone, 'rtype': rtype, 'record': record, 'helpers':tuturecord.helpers, 'errors': errors, 'newrecord': True};
def resolve(self, name, qtype): ''' Resolves a Blockchain-based (Namecoin) DNS Name via 2 step process using DNSSEC Step 1: ------- Get NS and DS records for the Namecoin name (for example: www.mattdavid.bit) from the Namecoin Client Step 2: ------- For each listed nameserver: - Create a temporary config file for use with unbound - Set Unbound's Trust Anchor to be the given DS records for the Namecoin-based domain name - Do DNSSEC-enabled DNS resolution for the given name / qtype :param name: DNS Record Name Query (for example: www.mattdavid.bit) :param qtype: String representation of query type (for example: A, AAAA, TXT, NS, SOA, etc...) :return: Resolved value if successful, None if un-successful ''' name = name.rstrip('.') if not name.endswith('.bit'): raise ValueError('This is not a valid .bit domain') domains = name.split('.') domains.reverse() if len(domains) < 2: raise ValueError('At least SLD Required') # Get Namecoin-based Domain Info from Namecoin Blockchain nc_domain = self.nc_name_resolver.name_show(domains[1]) if not nc_domain or not nc_domain.get('value'): log.error('No Name Value Data Found for Namecoin-based Domain Name: d/%s' % domains[1]) raise NamecoinValueException('No Name Value Data Found for: d/%s' % domains[1]) nc_value = json.loads(nc_domain.get('value', '{}').replace('\'','"')) if not nc_value.get('ds'): log.error('No DS Records Present for Namecoin-based Domain Name: %s' % name) raise NoDSRecordException() if not nc_value.get('ns'): log.error('No NS Records Present for Namecoin-based Domain Name: %s' % name) raise NoNameserverException() sld = '%s.%s.' % (domains[1], domains[0]) ds_record = ' '.join([str(x) for x in nc_value['ds'][0][0:3]]) # Handle both Hex and Base64 encoding (Base64 is the preferred encoding) per: # https://wiki.namecoin.info/index.php?title=Domain_Name_Specification if re.match('^[0-9a-fA-F]*$', nc_value['ds'][0][3]): ds_record += ' %s' % nc_value['ds'][0][3] else: ds_record += ' %s' % base64.b64decode(nc_value['ds'][0][3]).encode('hex').upper() if qtype in ['DS']: return ds_record ds_ta = '%s IN DS %s' % (sld, ds_record) ns_ctx = ub_ctx() ns_ctx.resolvconf(self.resolv_conf) if not os.path.isfile(self.dnssec_root_key): log.error("Trust anchor missing or inaccessible") raise Exception("Trust anchor is missing or inaccessible: %s" % self.dnssec_root_key) else: ns_ctx.add_ta_file(self.dnssec_root_key) last_error = None for ns in nc_value.get('ns', []): lookup_value = None status, result = ns_ctx.resolve(ns, rdatatype.from_text('A'), rdataclass.from_text('IN')) # NOTE: We do not require secure DNS resolution here because the Blockchain-stored DS records work as the trust anchor # and the signed RRSIG DNS results from the final DNS+DNSSEC lookup will be able to complete the chain of trust if status == 0 and result and result.data and not result.bogus: tmp_config_file = self._build_temp_unbound_config(sld, result.data.as_address_list()[0]) else: last_error = InvalidNameserverException() log.warn('No or Invalid Resolution Result for Nameserver: %s' % ns) continue ctx = ub_ctx() ctx.config(tmp_config_file) ctx.add_ta(str(ds_ta)) _qtype = None try: _qtype = rdatatype.from_text(qtype) except Exception as e: log.error('Unable to get RDATAType for Given Query Type [%s]: %s' % (qtype, str(e))) raise ValueError('Unable to get RDATAType for Query Type %s' % qtype) status, result = ctx.resolve(name, _qtype, rdataclass.from_text('IN')) if status != 0: log.info("DNS Resolution Failed: %s [%s]" % (name, _qtype)) elif status == 0: if not result.secure: log.info("DNS Resolution Returned Insecure Result: %s [%s]" % (name, qtype)) last_error = InsecureResultException() elif result.bogus: log.info("DNS Resolution Returned Bogus Result: %s [%s]" % (name, qtype)) last_error = BogusResultException() elif not result.havedata: log.info("DNS Resolution Returned Empty Result: %s [%s]" % (name, qtype)) last_error = EmptyResultException() else: # Get appropriate data by query type # TODO: Add SOA Record Processing if qtype in ['A','AAAA']: lookup_value = result.data.as_address_list() elif qtype in ['CNAME','TXT','PTR','SPF','CAA','NAPTR']: lookup_value = result.data.as_domain_list() elif qtype in ['MX']: lookup_value = result.data.as_mx_list() elif qtype in ['NS']: lookup_value = nc_value.get('ns', []) elif qtype in ['TLSA']: lookup_value = [NamecoinResolver.tlsa_convert(result.rawdata[0])] else: last_error = NotImplementedError('Unsupported DNS Query Type: %s' % qtype) self._delete_temp_unbound_config(tmp_config_file) if lookup_value: return lookup_value[0] if last_error and isinstance(last_error, NotImplementedError): raise last_error log.error('DNS Resolution Failed: %s [%s]' % (name, qtype)) if last_error: raise last_error return None
def from_text(self, val): rdata = dns.rdata.from_text(rdc.from_text(self._rclass), rdt.from_text(self._rtype), val); self.from_rdata(rdata);