Handle reverse zones

This commit is contained in:
nemunaire 2024-01-19 13:48:33 +01:00
parent e505e002d0
commit 4b5d014500
5 changed files with 181 additions and 7 deletions

91
services/ptr.go Normal file
View File

@ -0,0 +1,91 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2020-2024 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// This program is offered under a commercial and under the AGPL license.
// For commercial licensing, contact us at <contact@happydomain.org>.
//
// For AGPL licensing:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package svcs
import (
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/miekg/dns"
"git.happydns.org/happyDomain/model"
"git.happydns.org/happyDomain/utils"
)
type PTR struct {
Target string
}
func (s *PTR) GetNbResources() int {
return 1
}
func (s *PTR) GenComment(origin string) string {
return strings.TrimSuffix(s.Target, "."+origin)
}
func (s *PTR) GenRRs(domain string, ttl uint32, origin string) (rrs models.Records) {
rr := utils.NewRecordConfig(domain, "PTR", ttl, origin)
rr.SetTarget(utils.DomainFQDN(s.Target, origin))
rrs = append(rrs, rr)
return
}
func pointer_analyze(a *Analyzer) error {
for _, record := range a.SearchRR(AnalyzerRecordFilter{Type: dns.TypePTR}) {
if record.Type == "PTR" {
newrr := &PTR{
Target: strings.TrimSuffix(record.String(), "."+a.origin),
}
a.UseRR(record, record.NameFQDN, newrr)
}
}
return nil
}
func init() {
RegisterService(
func() happydns.Service {
return &PTR{}
},
pointer_analyze,
ServiceInfos{
Name: "Pointer",
Description: "A pointer to another domain.",
Categories: []string{
"internal",
},
RecordTypes: []uint16{
dns.TypePTR,
},
Restrictions: ServiceRestrictions{
Alone: true,
Single: true,
NeedTypes: []uint16{
dns.TypePTR,
},
},
},
99999998,
)
}

View File

@ -34,7 +34,7 @@
import { deleteZoneService } from '$lib/api/zone';
import Service from '$lib/components/domains/Service.svelte';
import { fqdn } from '$lib/dns';
import { fqdn, isReverseZone, unreverseDomain } from '$lib/dns';
import type { Domain, DomainInList } from '$lib/model/domain';
import type { ServiceCombined } from '$lib/model/service';
import { ZoneViewGrid } from '$lib/model/usersettings';
@ -49,12 +49,19 @@
export let services: Array<ServiceCombined>;
export let zoneId: string;
let reverseZone = false;
$: reverseZone = isReverseZone(origin.domain);
let showResources = true;
function isCNAME(services: Array<ServiceCombined>) {
return services.length === 1 && services[0]._svctype === 'svcs.CNAME';
}
function isPTR(services: Array<ServiceCombined>) {
return services.length === 1 && services[0]._svctype === 'svcs.PTR';
}
let deleteServiceInProgress = false;
function deleteCNAME() {
deleteServiceInProgress = true;
@ -75,19 +82,33 @@
}
</script>
{#if isCNAME(services)}
{#if isCNAME(services) || isPTR(services)}
<div id={dn}>
<h2
class="sticky-top"
style="background: white; z-index: 1"
>
<span style="white-space: nowrap">
<Icon name="link" />
{#if isPTR(services)}
<Icon
name="signpost"
title="PTR"
/>
{:else}
<Icon
name="sign-turn-right"
title="CNAME"
/>
{/if}
<span
class="font-monospace"
title={fqdn(dn, origin.domain)}
>
{fqdn(dn, origin.domain)}
{#if reverseZone}
{unreverseDomain(fqdn(dn, origin.domain))}
{:else}
{fqdn(dn, origin.domain)}
{/if}
</span>
</span>
<span style="white-space: nowrap">
@ -131,7 +152,11 @@
{:else}
<Icon name="x-circle" />
{/if}
{$t('domains.drop-alias')}
{#if isPTR(services)}
{$t('domains.drop-pointer')}
{:else}
{$t('domains.drop-alias')}
{/if}
</Button>
</h2>
</div>
@ -143,7 +168,7 @@
>
<h2
style="white-space: nowrap; cursor: pointer;"
class="mb-0"
class="mb-0 text-truncate"
on:click={() => showResources = !showResources}
on:keypress={() => showResources = !showResources}
>
@ -156,7 +181,11 @@
class="font-monospace"
title={fqdn(dn, origin.domain)}
>
{fqdn(dn, origin.domain)}
{#if reverseZone}
{unreverseDomain(fqdn(dn, origin.domain))}
{:else}
{fqdn(dn, origin.domain)}
{/if}
</span>
</h2>
{#if aliases.length != 0}

View File

@ -242,3 +242,55 @@ export function validateDomain(dn: string, origin: string = "", hostname: boolea
return ret;
}
export function isReverseZone(fqdn: string) {
return fqdn.endsWith('in-addr.arpa.') || fqdn.endsWith('ip6.arpa.')
}
export function reverseDomain(ip: string) {
let suffix = 'in-addr.arpa.';
let fields: Array<String>;
if (ip.indexOf(":") > 0) {
suffix = 'ip6.arpa.';
fields = ip.split(':');
fields = fields.map((e) => {
let exp_len = 4;
if (e.length == 0) {
exp_len = 4 * (7 - fields.length);
}
while (e.length < exp_len) {
e = '0' + e;
}
return e;
});
} else {
fields = ip.split('.');
while (fields.length < 4) {
const last = fields.pop();
fields.push('0', last);
}
}
return fields.reduce((a, v) => v.replace(/^0*(0|[^0].*)$/, '$1') + '.' + a, suffix);
}
export function unreverseDomain(dn: string) {
let split_char = '.';
let group = 1;
if (dn.endsWith('ip6.arpa.')) {
split_char = ':';
group = 4;
dn = dn.substring(0, dn.indexOf('.ip6.arpa.'))
} else {
dn = dn.substring(0, dn.indexOf('.in-addr.arpa.'))
}
const fields = dn.split('.');
let ip = fields.reduce((a, v, i) => v + (i % group == 0 ? split_char : '') + a, '');
ip = ip.substring(0, ip.length - 1);
return ip.replace(/:(0000:)+/, '::').replace(/:0+/g, ':');
}

View File

@ -102,6 +102,7 @@
"create-new-key": "Create new {{id}} key",
"discard": "Discard",
"drop-alias": "Drop alias",
"drop-pointer": "Drop pointer",
"edit-target": "Edit target",
"give-explicit-name": "Give an explicit name in order to easily find this service.",
"history": "History",

View File

@ -102,6 +102,7 @@
"create-new-key": "Créer une nouvelle clé {{id}}",
"discard": "Supprimer",
"drop-alias": "Supprimer l'alias",
"drop-pointer": "Supprimer le pointeur",
"edit-target": "Éditer la destination",
"give-explicit-name": "Donnez un nom explicite afin de trouver facilement ce service.",
"history": "Historique",