summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2022-05-15 22:57:26 +0700
committerShulhan <ms@kilabit.info>2022-05-15 22:57:26 +0700
commitf21e7c468c4d4479919c4a483451c6672698ab0a (patch)
treede450231be1c96eeb8161f2844baa3879de8b515
parentfe92d71726313f54ea91980d40c8ef52872ac1f2 (diff)
downloadrescached-f21e7c468c4d4479919c4a483451c6672698ab0a.tar.xz
cmd/resolver: implement command to add new record to zone
The command has the following signature, zone.d rr add <zone> <"@" | subdomain> <ttl> <type> <class> <value> ...:: The domain name can be set to origin using "@" or empty string, subdomain (without ending with "."), or fully qualified domain name (end with "."). If ttl is set to 0, it will default to 604800 (7 days). List of valid type are A, NS, CNAME, PTR, MX, TXT, and AAAA. List of valid class are IN, CS, HS.
-rw-r--r--.gitignore1
-rw-r--r--_doc/resolver.adoc52
-rw-r--r--_sys/usr/share/man/man1/resolver.1.gzbin2973 -> 3324 bytes
-rw-r--r--client.go48
-rw-r--r--cmd/resolver/main.go16
-rw-r--r--cmd/resolver/resolver.go110
6 files changed, 222 insertions, 5 deletions
diff --git a/.gitignore b/.gitignore
index 2e5d9f4..0fa8a44 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
/_bin/linux_amd64
/_doc/*.html
/_test/etc/rescached/hosts.d/hosts
+/_test/etc/rescached/zone.d/
/_test/var/cache/rescached/
/cover.html
/cover.out
diff --git a/_doc/resolver.adoc b/_doc/resolver.adoc
index b67a0e0..0ca00fc 100644
--- a/_doc/resolver.adoc
+++ b/_doc/resolver.adoc
@@ -213,6 +213,30 @@ zone.d delete <name>::
Delete zone file inside the zone.d directory.
+=== MANAGING RECORD IN ZONE.D
+
+zone.d rr add <zone> <"@" | subdomain> <ttl> <type> <class> <value> ...::
++
+--
+Add new record into the zone file.
+
+The domain name can be set to origin using "@" or empty string, subdomain
+(without ending with "."), or fully qualified domain name (end with ".").
+
+If ttl is set to 0, it will default to 604800 (7 days).
+
+List of valid type are A, NS, CNAME, PTR, MX, TXT, and AAAA.
+
+List of valid class are IN, CS, HS.
+
+The value parameter can be more than one, for example, the MX record
+we pass two parameters:
+
+ <pref> <exchange>
+
+See the example below for more information.
+--
+
== EXIT STATUS
Upon exit and success +resolver+ will return 0, or 1 otherwise.
@@ -336,6 +360,34 @@ Delete record "my.hosts" from hosts file "hosts",
"TTL": 604800
}
+
+=== MANAGING RECORD IN ZONE.D
+
+Assume that we have create zone "my.zone".
+
+Add IPv4 address "127.0.0.1" for domain my.zone,
+
+ $ resolver zone.d rr add my.zone @ 0 A IN 127.0.0.1
+ {
+ "Value": "127.0.0.1",
+ "Name": "my.zone",
+ "Type": 1,
+ "Class": 1,
+ "TTL": 604800
+ }
+
+Add subdomain "www" with IPv4 address "192.168.1.2" to zone "my.zone",
+
+ $ resolver zone.d rr add my.zone www 0 A IN 192.168.1.2
+ {
+ "Value": "192.168.1.2",
+ "Name": "www.my.zone",
+ "Type": 1,
+ "Class": 1,
+ "TTL": 604800
+ }
+
+
== AUTHOR
This software is developed by M. Shulhan (ms@kilabit.info).
diff --git a/_sys/usr/share/man/man1/resolver.1.gz b/_sys/usr/share/man/man1/resolver.1.gz
index 90dbf93..68e19f8 100644
--- a/_sys/usr/share/man/man1/resolver.1.gz
+++ b/_sys/usr/share/man/man1/resolver.1.gz
Binary files differ
diff --git a/client.go b/client.go
index bfec2fa..2833768 100644
--- a/client.go
+++ b/client.go
@@ -4,6 +4,7 @@
package rescached
import (
+ "encoding/base64"
"encoding/json"
"fmt"
"net/http"
@@ -473,3 +474,50 @@ func (cl *Client) ZonedDelete(name string) (zone *dns.Zone, err error) {
}
return zone, nil
}
+
+// ZonedRecordAdd add new record to zone file.
+func (cl *Client) ZonedRecordAdd(zone string, rreq dns.ResourceRecord) (rres *dns.ResourceRecord, err error) {
+ var (
+ logp = "ZonedRecordAdd"
+ zrr = zoneRecordRequest{
+ Zone: zone,
+ }
+
+ res *libhttp.EndpointResponse
+ rawb []byte
+ ok bool
+ )
+
+ fmt.Printf("ZonedRecordAdd: %+v\n", rreq)
+
+ zrr.Type, ok = dns.RecordTypeNames[rreq.Type]
+ if !ok {
+ return nil, fmt.Errorf("%s: unknown record type: %d", logp, rreq.Type)
+ }
+
+ rawb, err = json.Marshal(rreq)
+ if err != nil {
+ return nil, fmt.Errorf("%s: %w", logp, err)
+ }
+
+ zrr.Record = base64.StdEncoding.EncodeToString(rawb)
+
+ _, rawb, err = cl.PostJSON(apiZonedRR, nil, zrr)
+ if err != nil {
+ return nil, fmt.Errorf("%s: %w", logp, err)
+ }
+
+ rres = &dns.ResourceRecord{}
+ res = &libhttp.EndpointResponse{
+ Data: rres,
+ }
+ err = json.Unmarshal(rawb, res)
+ if err != nil {
+ return nil, fmt.Errorf("%s: %w", logp, err)
+ }
+ if res.Code != http.StatusOK {
+ return nil, fmt.Errorf("%s: %d %s", logp, res.Code, res.Message)
+ }
+
+ return rres, nil
+}
diff --git a/cmd/resolver/main.go b/cmd/resolver/main.go
index 62af801..556cff1 100644
--- a/cmd/resolver/main.go
+++ b/cmd/resolver/main.go
@@ -148,7 +148,7 @@ query <domain / ip-address> [type] [class]
Valid type are either A, NS, CNAME, SOA, MB, MG, MR, NULL,
WKS, PTR, HINFO, MINFO, MX, TXT, AAAA, or SRV.
- Default value is A."
+ Default value is A.
Valid class are either IN, CS, HS.
Default value is IN.
@@ -246,6 +246,20 @@ zone.d delete <name>
Delete zone file inside the zone.d directory.
+=== MANAGING RECORD IN ZONE.D
+
+zone.d rr add <zone> <domain> <ttl> <type> <class> <value> ...
+
+ Add new record into the zone file.
+ If domain ttl is set to 0, it will default to 604800 (7 days).
+ List of valid type are A, NS, CNAME, PTR, MX, TXT, and AAAA.
+ The value parameter can be more than one, for example, the MX record
+ we pass two parameters:
+
+ <pref> <exchange>
+
+ See the example below for more information.
+
== Examples
=== QUERY
diff --git a/cmd/resolver/resolver.go b/cmd/resolver/resolver.go
index 75287db..36175a1 100644
--- a/cmd/resolver/resolver.go
+++ b/cmd/resolver/resolver.go
@@ -10,6 +10,7 @@ import (
"log"
"math/rand"
"os"
+ "strconv"
"strings"
"time"
@@ -475,9 +476,9 @@ func (rsol *resolver) doCmdZoned(args []string) {
var (
subCmd = strings.ToLower(args[0])
+ resc = rsol.newRescachedClient()
- resc *rescached.Client
- err error
+ err error
)
switch subCmd {
@@ -487,7 +488,6 @@ func (rsol *resolver) doCmdZoned(args []string) {
log.Fatalf("resolver: %s %s: missing parameter name", rsol.cmd, subCmd)
}
- resc = rsol.newRescachedClient()
_, err = resc.ZonedCreate(args[0])
if err != nil {
log.Fatalf("resolver: %s", err)
@@ -500,18 +500,120 @@ func (rsol *resolver) doCmdZoned(args []string) {
log.Fatalf("resolver: %s %s: missing parameter name", rsol.cmd, subCmd)
}
- resc = rsol.newRescachedClient()
_, err = resc.ZonedDelete(args[0])
if err != nil {
log.Fatalf("resolver: %s", err)
}
fmt.Println("OK")
+ case subCmdRR:
+ rsol.doCmdZonedRR(resc, args[1:])
+
default:
log.Fatalf("resolver: %s: unknown sub command: %s", rsol.cmd, subCmd)
}
}
+func (rsol *resolver) doCmdZonedRR(resc *rescached.Client, args []string) {
+ var (
+ cmdAction = strings.ToLower(args[0])
+ )
+
+ args = args[1:]
+
+ switch cmdAction {
+ case subCmdAdd:
+ rsol.doCmdZonedRRAdd(resc, args)
+
+ default:
+ log.Fatalf("resolver: %s %s: unknown action: %q", rsol.cmd, subCmdRR, cmdAction)
+ }
+}
+
+// doCmdZonedRRAdd add new record to the zone.
+// This command accept the following arguments:
+//
+// 0 1 2 3 4 5
+// <zone> <domain> <ttl> <type> <class> <value> ...
+//
+// List of valid type are A, NS, CNAME, PTR, MX, TXT, and AAAA.
+// For the MX record we pass two parameters:
+//
+// 5 6
+// <pref> <exchange>
+func (rsol *resolver) doCmdZonedRRAdd(resc *rescached.Client, args []string) {
+ if len(args) < 6 {
+ log.Fatalf("resolver: %s %s %s: missing arguments", rsol.cmd, subCmdRR, subCmdAdd)
+ }
+
+ var (
+ zone = strings.ToLower(args[0])
+ rreq = dns.ResourceRecord{}
+
+ rres *dns.ResourceRecord
+ vstr string
+ vuint uint64
+ vbytes []byte
+ err error
+ ok bool
+ )
+
+ rreq.Name = strings.ToLower(args[1])
+ if rreq.Name == "@" {
+ rreq.Name = ""
+ }
+
+ vuint, err = strconv.ParseUint(args[2], 10, 64)
+ if err != nil {
+ log.Fatalf("resolver: invalid TTL: %q", args[2])
+ }
+
+ rreq.TTL = uint32(vuint)
+
+ vstr = strings.ToUpper(args[3])
+ rreq.Type, ok = dns.RecordTypes[vstr]
+ if !ok {
+ log.Fatalf("resolver: invalid record type: %q", vstr)
+ }
+
+ vstr = strings.ToUpper(args[4])
+ rreq.Class, ok = dns.RecordClasses[vstr]
+ if !ok {
+ log.Fatalf("resolver: invalid record class: %q", vstr)
+ }
+
+ vstr = args[5]
+
+ if rreq.Type == dns.RecordTypeMX {
+ if len(args) < 6 {
+ log.Fatalf("resolver: missing argument for MX record")
+ }
+ vuint, err = strconv.ParseUint(vstr, 10, 64)
+ if err != nil {
+ log.Fatalf("resolver: invalid MX preference: %q", vstr)
+ }
+ var rrMX = &dns.RDataMX{
+ Preference: int16(vuint),
+ Exchange: args[6],
+ }
+ rreq.Value = rrMX
+ } else {
+ rreq.Value = args[5]
+ }
+
+ rres, err = resc.ZonedRecordAdd(zone, rreq)
+ if err != nil {
+ log.Fatalf("resolver: %s", err)
+ }
+
+ vbytes, err = json.MarshalIndent(rres, "", " ")
+ if err != nil {
+ log.Fatalf("resolver: %s", err)
+ }
+
+ fmt.Println(string(vbytes))
+}
+
// initSystemResolver read the system resolv.conf to create fallback DNS
// resolver.
func (rsol *resolver) initSystemResolver() (err error) {