def add_default_soa_record(self, endpoint=None): """Returns None if already exists, DoesNotExist bubbles up when no default soa exists""" # sanity check if not self.domain_id: raise Exception( "Cannot add default records when domain_id is not set") # don't duplicate SOA record try: existing_soa = Record.get(Record.domain_id == self.domain_id, Record.type == RecordType().set("SOA")) return None except DoesNotExist: # create SOA record, let DoesNotExist bubble up default_soa = DefaultRecord.get( DefaultRecord.type == RecordType().set("SOA")) soa = Record() soa.domain_id = self.domain_id soa.host = default_soa.host.replace("DOMAIN", self.domain) soa.val = default_soa.val soa.ttl = default_soa.ttl soa.type = default_soa.type # replace uses of DOMAIN soa.save() if endpoint is not None: endpoint.dns_log(soa.domain_id, "added soa") return soa
def post(self): domain_id = request.form.get('domain_id') if domain_id is None: abort(400, message="domain_id parameter is required") domain_id = int(domain_id) # check if the domain exists try: domain = self.get_domain(domain_id) except peewee.DoesNotExist: abort(404, message="domain_id does not exist: " + str(domain_id)) record_type = request.form.get('record_type') try: readable_type = RecordType().set(record_type) except RecordTypeException: abort(400, message="Invalid record_type: " + str(record_type)) # If SOA, make sure a record doesn't yet exist if record_type == "SOA": try: ModelRecord.get( ModelRecord.type == RecordType().set(record_type), ModelRecord.domain_id == domain_id) abort(400, message="SOA record already exists for this domain") except peewee.DoesNotExist: pass TypeModel = RecordType().get_class(RecordType().set(record_type))() self.request_form_to_type_model(request.form, TypeModel, domain) TypeModel.values["domain_id"] = domain.domain_id model = TypeModel.to_model() # get domain and check authorization domain = self.auth.account.get_domain_by_record_acl( domain_id, model.host, record_type) if domain is False: self.auth.account.load_domains() domain = self.get_write_domain(domain_id) try: model.save() except RecordValueException as e: abort(400, message=e.message) self.dns_log(domain.domain_id, ("added " + record_type + " with host " + model.host + " and value " + model.val)) # notify listeners of dns data change self.send_update_notification() return {'status': 'ok', 'record': model.to_recordtype().to_dict()}, 201
def post(self): domain_id = request.form.get('domain_id') if domain_id is None: abort(400, message="domain_id parameter is required") domain_id = int(domain_id) # check if the domain exists try: self.get_domain(domain_id) except peewee.DoesNotExist: abort(404, message="domain_id does not exist: " + domain_id) # get domain and check authorization self.auth.account.load_domains() domain = self.get_write_domain(domain_id) record_type = request.form.get('record_type') try: readable_type = RecordType().set(record_type) except RecordTypeException: abort(400, message="Invalid record_type: " + str(record_type)) # If SOA, make sure a record doesn't yet exist if record_type == "SOA": try: ModelRecord.get( ModelRecord.type == RecordType().set(record_type), ModelRecord.domain_id == domain_id ) abort(400, message="SOA record already exists for this domain") except peewee.DoesNotExist: pass TypeModel = RecordType().get_class(RecordType().set(record_type))() self.request_form_to_type_model(request.form, TypeModel, domain) TypeModel.values["domain_id"] = domain.domain_id model = TypeModel.to_model() try: model.save() except RecordValueException as e: abort(400, message=e.message) self.dns_log( domain.domain_id, ( "added " + record_type + " with host " + model.host + " and value " + model.val ) ) return {'status': 'ok', 'record': model.to_recordtype().to_dict()}, 201
def test_hostname_in_domain(self): record = Record() domain = "foobar.com" bad = "wwwfoobar.com" good = "www.foobar.com" self.assertTrue(record.hostname_in_domain(domain, domain)) self.assertFalse(record.hostname_in_domain(bad, domain)) self.assertTrue(record.hostname_in_domain(good, domain))
def test_spf_record(self): model = Record() model.type = 'F' model.host = 'example.com' model.val = 'v=spf1 mx -all' model.ttl = '3600' self.assertEquals( self.export.data_line_from_model(model), ":example.com:99:" r"\016" "v=spf1 mx -all:3600\n" )
def test_ptr_record(self): model = Record() model.type = 'T' model.host = 'vegadns.ubuntu' model.val = 'v=spf1 mx a ip4:1.2.3.0/24' model.ttl = '3600' self.assertEquals( self.export.data_line_from_model(model), "'vegadns.ubuntu:v=spf1 mx a ip4" r"\072" + "1.2.3.0/24:3600\n" )
def test_mx_record(self): model = Record() model.type = 'P' model.host = '4.3.2.1.in-addr.arpa' model.val = 'www.vegadns.ubuntu' model.ttl = '3600' self.assertEquals( self.export.data_line_from_model(model), '^4.3.2.1.in-addr.arpa:www.vegadns.ubuntu:3600\n' )
def test_a_record(self): model = Record() model.type = 'A' model.host = 'foo.vegadns.ubuntu' model.val = '1.2.3.4' model.ttl = '3600' self.assertEquals( self.export.data_line_from_model(model), '+foo.vegadns.ubuntu:1.2.3.4:3600\n' )
def test_ns_record(self): model = Record() model.type = 'N' model.host = 'example.com' model.val = 'ns1.example.com' model.ttl = 3600 expected = "&example.com::ns1.example.com:3600\n" self.assertEquals( self.export.data_line_from_model(model), expected )
def get_records(self): return (ModelRecord.select(ModelDomain, ModelRecord).join( ModelDomain, on=ModelDomain.domain_id == ModelRecord.domain_id).where( ModelDomain.status == 'active').order_by( ModelDomain.domain.asc(), ModelRecord.type.asc(), ModelRecord.host.asc(), ModelRecord.val.asc()))
def check_domain_suffix(self, domain): # make sure hostname ends in domain name name = request.form.get("name") if not name or not ModelRecord.hostname_in_domain(name, domain): abort( 400, message="Name does not end in domain name: " + str(name) )
def test_aaaa_record(self): model = Record() model.type = '3' model.host = 'example.com' model.val = '0000:0000:0000:0000:0000:ffff:169.254.123.231' model.ttl = '3600' expected = ( ":example.com:28:" r"\000\000\000\000\000\000\000\000\000\000\377\377\251\376\173\347" ":3600\n" ) self.assertEquals( self.export.data_line_from_model(model), expected )
def test_cname_validation_fail(self): record = Record() record.type = 'C' record.domain_id = 1 record.host = 'foobar.com' record.val = 'www.example.com ' with self.assertRaises(RecordValueException) as cm: record.validate() self.assertEquals('Invalid cname value: www.example.com ', cm.exception.message)
def test_cname_validation_success(self): record = Record() record.type = 'C' record.domain_id = 1 record.host = 'foobar.com' record.val = 'www.example.com' self.assertIsNone(record.validate())
def test_srv_record(self): model = Record() model.type = 'V' model.host = '_xmpp-client._tcp.example.com.' model.val = 'xmpp.example.com' model.ttl = '3600' model.distance = 0 model.weight = 10 model.port = 5222 expected = (":_xmpp-client._tcp.example.com." ":33" r":\000\000\000\012\024\146\004xmpp\007example\003com\000" ":3600\n") self.assertEquals(self.export.data_line_from_model(model), expected)
def test_soa_record(self): domain = Domain() domain.domain = 'example.com' model = Record() # joined domain on domain_id model.domain_id = domain model.type = 'S' model.host = 'hostmaster.example.com:ns1.example.com' model.val = '16384:2048:1048576:2560:' model.ttl = '86400' expected = ( "Zexample.com" ":ns1.example.com" ":hostmaster.example.com" ":" ":16384" ":2048" ":1048576" ":2560" ":86400\n" ) self.assertEquals( self.export.data_line_from_model(model), expected )
def test_mx_record(self): model = Record() model.type = 'M' model.host = 'vegadns.ubuntu' model.val = 'mail.vegadns.ubuntu' model.distance = 10 model.ttl = '3600' self.assertEquals(self.export.data_line_from_model(model), '@vegadns.ubuntu::mail.vegadns.ubuntu:10:3600\n')
def test_cname_validation_fail(self): record = Record() record.type = 'C' record.domain_id = 1 record.host = 'foobar.com' record.val = 'www.example.com ' with self.assertRaises(RecordValueException) as cm: record.validate() self.assertEquals( 'Invalid cname value: www.example.com ', cm.exception.message )
def count_records(self, filter_record_type, search_name=None, search_value=None): if not self.domain_id: raise Exception("Cannot get records, domain_id is not set") query = Record.select(Record).where(Record.domain_id == self.domain_id) if filter_record_type is not None: query = query.where(Record.type == RecordType().set(filter_record_type)) if search_name is not None: query = query.where((Record.host ** ("%" + search_name + "%"))) if search_value is not None: query = query.where((Record.val ** ("%" + search_value + "%"))) return query.count()
def get_records(self): return ( ModelRecord.select(ModelDomain, ModelRecord) .join( ModelDomain, on=ModelDomain.domain_id == ModelRecord.domain_id ) .where(ModelDomain.status == 'active') .order_by( ModelDomain.domain.asc(), ModelRecord.type.asc(), ModelRecord.host.asc(), ModelRecord.val.asc() ) )
def test_a_record(self): model = Record() model.type = 'A' model.host = 'foo.vegadns.ubuntu' model.val = '1.2.3.4' model.ttl = '3600' self.assertEquals(self.export.data_line_from_model(model), '+foo.vegadns.ubuntu:1.2.3.4:3600\n')
def test_mx_record(self): model = Record() model.type = 'P' model.host = '4.3.2.1.in-addr.arpa' model.val = 'www.vegadns.ubuntu' model.ttl = '3600' self.assertEquals(self.export.data_line_from_model(model), '^4.3.2.1.in-addr.arpa:www.vegadns.ubuntu:3600\n')
def test_ns_record(self): model = Record() model.type = 'N' model.host = 'example.com' model.val = 'ns1.example.com' model.ttl = 3600 expected = "&example.com::ns1.example.com:3600\n" self.assertEquals(self.export.data_line_from_model(model), expected)
def test_ptr_record(self): model = Record() model.type = 'T' model.host = 'vegadns.ubuntu' model.val = 'v=spf1 mx a ip4:1.2.3.0/24' model.ttl = '3600' self.assertEquals( self.export.data_line_from_model(model), "'vegadns.ubuntu:v=spf1 mx a ip4" r"\072" + "1.2.3.0/24:3600\n")
def test_spf_record(self): model = Record() model.type = 'F' model.host = 'example.com' model.val = 'v=spf1 mx -all' model.ttl = '3600' self.assertEquals(self.export.data_line_from_model(model), ":example.com:99:" r"\016" "v=spf1 mx -all:3600\n")
def delete(self, location_id): if self.auth.account.account_type != "senior_admin": abort(403, message="Insufficient privileges to delete a location") try: locationdb = ModelLocation.get( ModelLocation.location_id == location_id) except peewee.DoesNotExist: abort(404, message="location not found") locationdb.delete_prefixes() # update records that have this id to null (public) query = ModelRecord.update(location_id=None).where( ModelRecord.location_id == location_id) query.execute() locationdb.delete_instance() return {'status': 'ok'}
def test_mx_record(self): model = Record() model.type = 'M' model.host = 'vegadns.ubuntu' model.val = 'mail.vegadns.ubuntu' model.distance = 10 model.ttl = '3600' self.assertEquals( self.export.data_line_from_model(model), '@vegadns.ubuntu::mail.vegadns.ubuntu:10:3600\n' )
def count_records(self, filter_record_type, search_name=None, search_value=None): if not self.domain_id: raise Exception("Cannot get records, domain_id is not set") query = Record.select(Record).where(Record.domain_id == self.domain_id) if filter_record_type is not None: query = query.where( Record.type == RecordType().set(filter_record_type)) if search_name is not None: query = query.where((Record.host**('%' + search_name + '%'))) if search_value is not None: query = query.where((Record.val**('%' + search_value + '%'))) return query.count()
def test_aaaaptr_record(self): model = Record() model.type = '3' model.host = 'example.com' model.val = '0000:0000:0000:0000:0000:ffff:169.254.123.231' model.ttl = '3600' expected = ( ":example.com:28:" r"\000\000\000\000\000\000\000\000\000\000\377\377\251\376\173\347" ":3600\n") self.assertEquals(self.export.data_line_from_model(model), expected)
def test_srv_record(self): model = Record() model.type = 'V' model.host = '_xmpp-client._tcp.example.com.' model.val = 'xmpp.example.com' model.ttl = '3600' model.distance = 0 model.weight = 10 model.port = 5222 expected = ( ":_xmpp-client._tcp.example.com." ":33" r":\000\000\000\012\024\146\004xmpp\007example\003com\000" ":3600\n" ) self.assertEquals( self.export.data_line_from_model(model), expected )
def delete(self, location_id): if self.auth.account.account_type != "senior_admin": abort(403, message="Insufficient privileges to delete a location") try: locationdb = ModelLocation.get( ModelLocation.location_id == location_id ) except peewee.DoesNotExist: abort(404, message="location not found") locationdb.delete_prefixes() # update records that have this id to null (public) query = ModelRecord.update(location_id=None).where( ModelRecord.location_id == location_id ) query.execute() locationdb.delete_instance() return {'status': 'ok'}
def test_soa_record(self): domain = Domain() domain.domain = 'example.com' model = Record() # joined domain on domain_id model.domain_id = domain model.type = 'S' model.host = 'hostmaster.example.com:ns1.example.com' model.val = '16384:2048:1048576:2560:' model.ttl = '86400' expected = ("Zexample.com" ":ns1.example.com" ":hostmaster.example.com" ":" ":16384" ":2048" ":1048576" ":2560" ":86400\n") self.assertEquals(self.export.data_line_from_model(model), expected)
def get_records(self): if not self.domain_id: raise Exception("Cannot get records, domain_id is not set") return Record.select(Record).where(Record.domain_id == self.domain_id)
def get_record(self, record_id): return ModelRecord.get(ModelRecord.record_id == record_id)
def add_default_records(self, endpoint=None): # sanity check if not self.domain_id: raise Exception("Cannot add default records when domain_id is not set") # don't duplicate SOA record try: existing_soa = Record.get(Record.domain_id == self.domain_id, Record.type == RecordType().set("SOA")) except DoesNotExist: # create SOA record try: default_soa = DefaultRecord.get(DefaultRecord.type == RecordType().set("SOA")) soa = Record() soa.domain_id = self.domain_id soa.host = default_soa.host.replace("DOMAIN", self.domain) soa.val = default_soa.val soa.ttl = default_soa.ttl soa.type = default_soa.type # replace uses of DOMAIN soa.save() if endpoint is not None: endpoint.dns_log(soa.domain_id, "added soa") except DoesNotExist: # no default SOA record set! pass # create all other records default_records = DefaultRecord.select().where(DefaultRecord.type != RecordType().set("SOA")) for record in default_records: new = Record() new.domain_id = self.domain_id new.distance = record.distance new.host = record.host.replace("DOMAIN", self.domain) new.val = record.val.replace("DOMAIN", self.domain) new.distance = record.distance new.port = record.port new.ttl = record.ttl new.type = record.type new.weight = record.weight new.save() if endpoint is not None: endpoint.dns_log( new.domain_id, ("added " + RecordType().get(new.type) + " with host " + new.host + " and value " + new.val), )
def add_default_records(self, endpoint=None, skipSoa=False): # sanity check if not self.domain_id: raise Exception( "Cannot add default records when domain_id is not set") if not skipSoa: try: soa = self.add_default_soa_record(endpoint) except DoesNotExist: # no default SOA record set! pass # create all other records default_records = DefaultRecord.select().where( DefaultRecord.type != RecordType().set("SOA")) for record in default_records: new = Record() new.domain_id = self.domain_id new.distance = record.distance new.host = record.host.replace("DOMAIN", self.domain) new.val = record.val.replace("DOMAIN", self.domain) new.distance = record.distance new.port = record.port new.ttl = record.ttl new.type = record.type new.weight = record.weight new.save() if endpoint is not None: endpoint.dns_log( new.domain_id, ("added " + RecordType().get(new.type) + " with host " + new.host + " and value " + new.val))
def get(self, format): if format != 'tinydns': abort(400, message="invalid format: " + format) records = self.get_records() domains = {} for record in records: if record.domain_id.domain not in domains: domains[record.domain_id.domain] = [] domains[record.domain_id.domain].append(record) organized = [] for key, val in sorted(domains.items()): organized.append( { 'domain_name': key, 'records': val } ) locations = self.get_locations() prefixes = self.get_prefixes() locationdata = "# locations\n" # need to build this manually since peewee join results are limiting for location in locations: temp_list = [] for prefix in prefixes: if prefix.location_id != location.location_id: continue else: if prefix.prefix_type == "ipv4": temp_list.append(prefix.prefix) else: temp_list.append( "s" + str(prefix.prefix).replace( ":", "" ) ) if len(temp_list) == 0: locationdata += "%" + location.location + "\n" else: for i in temp_list: locationdata += "%" + location.location + ":" + i + "\n" datafile = locationdata + "\n" tinydns = ExportTinydnsData() datafile += tinydns.export_domains(organized, locations) generation_record_host = config.get( 'monitoring', 'vegadns_generation_txt_record' ) if generation_record_host \ and Validate().record_hostname(generation_record_host): timestamp = self.get_latest_log_timestamp() md5 = hashlib.md5(datafile + "\n").hexdigest() model = ModelRecord() model.type = RecordType().set('TXT') model.host = generation_record_host model.val = str(timestamp) + "-" + md5 model.ttl = 3600 generation_record_line = tinydns.data_line_from_model(model) datafile += "\n\n# VegaDNS Generation TXT Record\n" datafile += generation_record_line.rstrip("\n") response = make_response(datafile) response.headers['content-type'] = 'text/plain' return response
def get(self, format): if format != 'tinydns': abort(400, message="invalid format: " + format) records = self.get_records() domains = {} for record in records: if record.domain_id.domain not in domains: domains[record.domain_id.domain] = [] domains[record.domain_id.domain].append(record) organized = [] for key, val in sorted(domains.items()): organized.append({'domain_name': key, 'records': val}) locations = self.get_locations() prefixes = self.get_prefixes() locationdata = "# locations\n" # need to build this manually since peewee join results are limiting for location in locations: temp_list = [] for prefix in prefixes: if prefix.location_id != location.location_id: continue else: if prefix.prefix_type == "ipv4": temp_list.append(prefix.prefix) else: temp_list.append("s" + str(prefix.prefix).replace(":", "")) if len(temp_list) == 0: locationdata += "%" + location.location + "\n" else: for i in temp_list: locationdata += "%" + location.location + ":" + i + "\n" datafile = locationdata + "\n" tinydns = ExportTinydnsData() datafile += tinydns.export_domains(organized, locations) generation_record_host = config.get('monitoring', 'vegadns_generation_txt_record') if generation_record_host \ and Validate().record_hostname(generation_record_host): timestamp = self.get_latest_log_timestamp() md5 = hashlib.md5(datafile + "\n").hexdigest() model = ModelRecord() model.type = RecordType().set('TXT') model.host = generation_record_host model.val = str(timestamp) + "-" + md5 model.ttl = 3600 generation_record_line = tinydns.data_line_from_model(model) datafile += "\n\n# VegaDNS Generation TXT Record\n" datafile += generation_record_line.rstrip("\n") response = make_response(datafile) response.headers['content-type'] = 'text/plain' return response