diff options
| author | Shulhan <ms@kilabit.info> | 2022-05-17 23:39:10 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2022-05-17 23:51:00 +0700 |
| commit | bb08b5bb6ad6cf3e1c409db7d69ace4364e7bc8d (patch) | |
| tree | 5fed4b58027ada9acafb5af0795e2acc1868325d | |
| parent | be96c68a070474364690f0655b26dc8823eea293 (diff) | |
| download | rescached-bb08b5bb6ad6cf3e1c409db7d69ace4364e7bc8d.tar.xz | |
cmd/resolver: implement command to delete record on zone
Given the following command
$ resolver zone.d rr delete <zone> <"@" | subdomain> <type> <class> <value>
It will delete the record in zone by given subdomain, type, class, and
value.
| -rw-r--r-- | _doc/resolver.adoc | 20 | ||||
| -rw-r--r-- | _sys/usr/share/man/man1/resolver.1.gz | bin | 3508 -> 3596 bytes | |||
| -rw-r--r-- | client.go | 46 | ||||
| -rw-r--r-- | cmd/resolver/resolver.go | 105 | ||||
| -rw-r--r-- | httpd.go | 45 |
5 files changed, 192 insertions, 24 deletions
diff --git a/_doc/resolver.adoc b/_doc/resolver.adoc index 362bc62..d2d96c5 100644 --- a/_doc/resolver.adoc +++ b/_doc/resolver.adoc @@ -245,6 +245,13 @@ we pass two parameters: See the example below for more information. -- +`zone.d rr delete <zone> <"@" | subdomain> <type> <class> <value>`:: ++ +-- +Delete record from zone by its subdomain, type, class, and value. +-- + + == EXIT STATUS Upon exit and success +resolver+ will return 0, or 1 otherwise. @@ -394,6 +401,10 @@ Get all records in the 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 + +or + + $ resolver zone.d rr add my.zone "" 0 A IN 127.0.0.1 { "Value": "127.0.0.1", "Name": "my.zone", @@ -402,6 +413,11 @@ Add IPv4 address "127.0.0.1" for domain my.zone, "TTL": 604800 } +and to delete the above record, + + $ resolver zone.d rr delete my.zone @ A IN 127.0.0.1 + OK + 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 @@ -413,6 +429,10 @@ Add subdomain "www" with IPv4 address "192.168.1.2" to zone "my.zone", "TTL": 604800 } +and to delete the above record, + + $ resolver zone.d rr delete my.zone www A IN 192.168.1.2 + OK == AUTHOR diff --git a/_sys/usr/share/man/man1/resolver.1.gz b/_sys/usr/share/man/man1/resolver.1.gz Binary files differindex a3b628c..38963de 100644 --- a/_sys/usr/share/man/man1/resolver.1.gz +++ b/_sys/usr/share/man/man1/resolver.1.gz @@ -570,3 +570,49 @@ func (cl *Client) ZonedRecordAdd(name string, rreq dns.ResourceRecord) (rres *dn return rres, nil } + +// ZonedRecordDelete delete record from zone file. +func (cl *Client) ZonedRecordDelete(name string, rreq dns.ResourceRecord) (zoneRecords dns.ZoneRecords, err error) { + var ( + logp = "ZonedRecordDelete" + params = url.Values{} + + res *libhttp.EndpointResponse + vstr string + rawb []byte + ok bool + ) + + params.Set(paramNameName, name) + + vstr, ok = dns.RecordTypeNames[rreq.Type] + if !ok { + return nil, fmt.Errorf("%s: unknown record type: %d", logp, rreq.Type) + } + params.Set(paramNameType, vstr) + + rawb, err = json.Marshal(rreq) + if err != nil { + return nil, fmt.Errorf("%s: %w", logp, err) + } + vstr = base64.StdEncoding.EncodeToString(rawb) + params.Set(paramNameRecord, vstr) + + _, rawb, err = cl.Delete(apiZonedRR, nil, params) + if err != nil { + return nil, fmt.Errorf("%s: %w", logp, err) + } + + res = &libhttp.EndpointResponse{ + Data: &zoneRecords, + } + 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 zoneRecords, nil +} diff --git a/cmd/resolver/resolver.go b/cmd/resolver/resolver.go index ead3cfc..b22bee1 100644 --- a/cmd/resolver/resolver.go +++ b/cmd/resolver/resolver.go @@ -534,16 +534,18 @@ func (rsol *resolver) doCmdZonedRR(resc *rescached.Client, args []string) { } var ( - cmdAction string + cmdAction = strings.ToLower(args[0]) ) - cmdAction = strings.ToLower(args[0]) args = args[1:] switch cmdAction { case subCmdAdd: rsol.doCmdZonedRRAdd(resc, args) + case subCmdDelete: + rsol.doCmdZonedRRDelete(resc, args) + case subCmdGet: rsol.doCmdZonedRRGet(resc, args) @@ -636,6 +638,72 @@ func (rsol *resolver) doCmdZonedRRAdd(resc *rescached.Client, args []string) { fmt.Println(string(vbytes)) } +// doCmdZonedRRDelete delete record from zone. +// This command accept the following arguments: +// +// 0 1 2 3 4 +// <zone> <domain> <type> <class> <value> ... +func (rsol *resolver) doCmdZonedRRDelete(resc *rescached.Client, args []string) { + if len(args) < 5 { + log.Fatalf("resolver: %s %s %s: missing arguments", rsol.cmd, subCmdRR, subCmdDelete) + } + + var ( + zone = strings.ToLower(args[0]) + rreq = dns.ResourceRecord{} + + zoneRecords dns.ZoneRecords + vstr string + vuint uint64 + err error + ok bool + ) + + rreq.Name = strings.ToLower(args[1]) + if rreq.Name == "@" { + rreq.Name = "" + } + + vstr = strings.ToUpper(args[2]) + rreq.Type, ok = dns.RecordTypes[vstr] + if !ok { + log.Fatalf("resolver: invalid record type: %q", vstr) + } + + vstr = strings.ToUpper(args[3]) + rreq.Class, ok = dns.RecordClasses[vstr] + if !ok { + log.Fatalf("resolver: invalid record class: %q", vstr) + } + + vstr = args[4] + + if rreq.Type == dns.RecordTypeMX { + if len(args) < 5 { + 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[5], + } + rreq.Value = rrMX + } else { + rreq.Value = vstr + } + + zoneRecords, err = resc.ZonedRecordDelete(zone, rreq) + if err != nil { + log.Fatalf("resolver: %s", err) + } + + fmt.Println("OK") + printZoneRecords(zoneRecords) +} + // doCmdZonedRRGet get and print the records on zone. func (rsol *resolver) doCmdZonedRRGet(resc *rescached.Client, args []string) { if len(args) == 0 { @@ -644,9 +712,6 @@ func (rsol *resolver) doCmdZonedRRGet(resc *rescached.Client, args []string) { var ( zoneRecords dns.ZoneRecords - dname string - listRR []*dns.ResourceRecord - rr *dns.ResourceRecord err error ) @@ -655,16 +720,7 @@ func (rsol *resolver) doCmdZonedRRGet(resc *rescached.Client, args []string) { log.Fatalf("resolver: %s", err) } - for dname, listRR = range zoneRecords { - fmt.Println(dname) - for _, rr = range listRR { - fmt.Printf(" %6d %2s %2s %v\n", - rr.TTL, - dns.RecordTypeNames[rr.Type], - dns.RecordClassName[rr.Class], - rr.Value) - } - } + printZoneRecords(zoneRecords) } // initSystemResolver read the system resolv.conf to create fallback DNS @@ -843,3 +899,22 @@ func printQueryResponse(nameserver string, msg *dns.Message) { fmt.Println(b.String()) printMessage(msg) } + +func printZoneRecords(zr dns.ZoneRecords) { + var ( + dname string + listRR []*dns.ResourceRecord + rr *dns.ResourceRecord + ) + + for dname, listRR = range zr { + fmt.Println(dname) + for _, rr = range listRR { + fmt.Printf(" %6d %2s %2s %v\n", + rr.TTL, + dns.RecordTypeNames[rr.Type], + dns.RecordClassName[rr.Class], + rr.Value) + } + } +} @@ -1301,14 +1301,19 @@ func (srv *Server) apiZonedRRAdd(epr *libhttp.EndpointRequest) (resb []byte, err // # Response // // On success it will return all the records in the zone. +// +// On fail it will return, +// - 400: if one of the parameter is invalid or empty. +// - 404: if the record to be deleted not found. func (srv *Server) apiZonedRRDelete(epr *libhttp.EndpointRequest) (resbody []byte, err error) { var ( res = libhttp.EndpointResponse{} req = zoneRecordRequest{} - rr = dns.ResourceRecord{} + rr = &dns.ResourceRecord{} - zone *dns.Zone - ok bool + zone *dns.Zone + rrValue string + ok bool ) res.Code = http.StatusBadRequest @@ -1344,27 +1349,49 @@ func (srv *Server) apiZonedRRDelete(epr *libhttp.EndpointRequest) (resbody []byt rr.Value = &dns.RDataSOA{} case dns.RecordTypeMX: rr.Value = &dns.RDataMX{} + default: + rr.Value = rrValue } - err = json.Unmarshal(req.recordRaw, &rr) + err = json.Unmarshal(req.recordRaw, rr) if err != nil { res.Message = "json.Unmarshal: " + err.Error() return nil, &res } - if len(rr.Name) == 0 { - res.Message = "invalid or empty ResourceRecord.Name" - return nil, &res + rr.Name = strings.TrimRight(rr.Name, ".") + + if rr.Type == dns.RecordTypePTR { + if len(rr.Name) == 0 { + res.Message = "empty PTR name" + return nil, &res + } + if len(rrValue) == 0 { + rr.Value = req.Name + } else { + rr.Value = rrValue + "." + req.Name + } + } else { + if len(rr.Name) == 0 { + rr.Name = req.Name + } else { + rr.Name += "." + req.Name + } } // Remove the RR from caches. - err = srv.dns.RemoveCachesByRR(&rr) + rr, err = srv.dns.RemoveCachesByRR(rr) if err != nil { res.Message = err.Error() return nil, &res } + if rr == nil { + res.Code = http.StatusNotFound + res.Message = "record not found" + return nil, &res + } // Remove the RR from zone file. - err = zone.Remove(&rr) + err = zone.Remove(rr) if err != nil { res.Message = err.Error() return nil, &res |
