Re: [DNSOP] I-D Action: draft-ietf-dnsop-rrserial-00.txt
Hugo Salgado <hsalgado@nic.cl> Sat, 19 June 2021 01:23 UTC
Return-Path: <hsalgado@nic.cl>
X-Original-To: dnsop@ietfa.amsl.com
Delivered-To: dnsop@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id 2847E3A1A50 for <dnsop@ietfa.amsl.com>; Fri, 18 Jun 2021 18:23:06 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -1.9
X-Spam-Level:
X-Spam-Status: No, score=-1.9 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, SPF_PASS=-0.001, URIBL_BLOCKED=0.001] autolearn=ham autolearn_force=no
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id D1ej6QIh00Bq for <dnsop@ietfa.amsl.com>; Fri, 18 Jun 2021 18:23:01 -0700 (PDT)
Received: from mail.nic.cl (mail.nic.cl [200.1.123.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 9D7873A1A4E for <dnsop@ietf.org>; Fri, 18 Jun 2021 18:23:00 -0700 (PDT)
Received: from mail.nic.cl (localhost [127.0.0.1]) by mail.nic.cl (Postfix) with ESMTP id 27BBF195D5BB2; Fri, 18 Jun 2021 21:22:58 -0400 (-04)
Received: from pepino (unknown [190.163.103.228]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.nic.cl (Postfix) with ESMTPSA id 07673195D5B9F; Fri, 18 Jun 2021 21:22:57 -0400 (-04)
Date: Fri, 18 Jun 2021 21:22:56 -0400
From: Hugo Salgado <hsalgado@nic.cl>
To: Stephane Bortzmeyer <bortzmeyer@nic.fr>
Cc: dnsop@ietf.org
Message-ID: <20210619012256.GA68270@pepino>
References: <162342216090.6059.5920585323129043595@ietfa.amsl.com> <20210613161540.GA14433@sources.org> <20210614140322.GA3276@pepino> <20210618123052.GA20671@sources.org>
MIME-Version: 1.0
Content-Type: multipart/signed; micalg="pgp-sha512"; protocol="application/pgp-signature"; boundary="NzB8fVQJ5HfG6fxh"
Content-Disposition: inline
In-Reply-To: <20210618123052.GA20671@sources.org>
X-Virus-Scanned: ClamAV using ClamSMTP on Fri Jun 18 21:22:58 2021 -0400 (-04) (mail.nic.cl)
Archived-At: <https://mailarchive.ietf.org/arch/msg/dnsop/LVZ_Nv6yIiUPN2G5ZvuSbtnpRgY>
Subject: Re: [DNSOP] I-D Action: draft-ietf-dnsop-rrserial-00.txt
X-BeenThere: dnsop@ietf.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: IETF DNSOP WG mailing list <dnsop.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/dnsop>, <mailto:dnsop-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/dnsop/>
List-Post: <mailto:dnsop@ietf.org>
List-Help: <mailto:dnsop-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/dnsop>, <mailto:dnsop-request@ietf.org?subject=subscribe>
X-List-Received-Date: Sat, 19 Jun 2021 01:23:06 -0000
Hi Stephane. Thanks a lot for your implementations! I have a modified dig version with support for rrserial in: https://gitlab.isc.org/huguei/bind9/-/tree/rrserial ; <<>> DiG 9.17.14 <<>> @200.1.122.30 dateserial.example.com txt +rrserial +nsid ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34574 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ; NSID: 63 6c 6e 73 64 74 65 73 74 ("clnsdtest") ; RRSERIAL: 78 49 7a 79 ("2018081401") ;; QUESTION SECTION: ;dateserial.example.com. IN TXT ;; ANSWER SECTION: dateserial.example.com. 43200 IN TXT "Test zone for RRSERIAL record" [ ... ] Also, I can reproduce the bug with NXDOMAINs with the NSD implementation :( Thanks for the notice, I'll look into it ASAP! I plan to keep track to these prototypes and scripts in some wiki too. Best, Hugo On 14:30 18/06, Stephane Bortzmeyer wrote: > On Mon, Jun 14, 2021 at 10:03:22AM -0400, > Hugo Salgado <hsalgado@nic.cl> wrote > a message of 55 lines which said: > > > In the case of NXDOMAIN, the reason for not adding RRSERIAL is > > because the response already has the SOA in the AUTHORITY, which > > would make it redundant. > > OK, I see. Here are two implementations of the client part, in Python > and in Go, using this algorithm (RRSERIAL if NOERROR or SERVFAIL, and > the SOA record if NXDOMAIN). > > Python : > > % ./test-rrserial.py 200.1.122.30 dateserial.example.com TXT > dateserial.example.com. 43200 IN TXT "Test zone for RRSERIAL record" > Serial of the answer is 2018081401 > > % ./test-rrserial.py 200.1.122.30 dateserial.example.com LOC > No value for dateserial.example.com/LOC > Serial found from SOA record: 2018081401 > > Go : > > % ./test-rrserial 200.1.122.30 incserial.example.com MX > incserial.example.com. 43200 IN MX 10 mail.incserial.example.com. > EDNS rrserial found, "1" > > % ./test-rrserial 200.1.122.30 incserial.example.com SSHFP > Empty answer received > EDNS rrserial found, "1" > > Note that you can do it with dig alone but the display of unknown data > is less pretty: > > % dig +ednsopt=65024 @200.1.122.30 dateserial.example.com > ... > ;; OPT PSEUDOSECTION: > ; EDNS: version: 0, flags: do; udp: 4096 > ; OPT=65024: 78 49 7a 79 ("xIzy") > > Note also there is a bug in your server: when the name does not exist, > it sends incorrect messages. dig says "Message parser reports > malformed message packet" and the Python library "DNS message is > malformed". And the Go library: > > % ./test-rrserial 200.1.122.30 doesnotexist.incserial.example.com > Error in query: dns: overflowing header size > #!/usr/bin/env python3 > > """Simple Python program to implement IETF draft > draft-ietf-dnsop-rrserial (hereafter "the draft") > > If you don't know Python: > pip install dnspython > ./test-rrserial.py 200.1.122.30 dateserial.example.com > > """ > > import struct > import sys > > # DNSpython https://www.dnspython.org/ We probably require 2.0 or higher. > import dns.message > import dns.query > import dns.edns > import dns.rdatatype > > # Temporary value in the draft > dns.edns.RRSERIAL = 65024 > > def print_soa(response): > found = False > for rr in response.authority: > if rr.rdtype == dns.rdatatype.SOA: > found = True > print("Serial found from SOA record: %s" % rr[0].serial) > if not found: > print("Negative answer without a SOA record :-(") > > def print_rrserial(response): > found = False > for opt in response.options: > if opt.otype == dns.edns.RRSERIAL: > found = True > print("Serial of the answer is %s" % struct.unpack(">I", opt.data)[0]) > if not found: > print("No EDNS serial option in answer") > > # TODO: it would probably be better (more pythonic) to inherit from > # dns.edns.GenericOption and provide a parser. > opts = [dns.edns.GenericOption(dns.edns.RRSERIAL, b'')] > > if len(sys.argv) != 3 and len(sys.argv) != 4: > raise Exception("Usage: %s server qname qtype [for instance '200.1.122.30 dateserial.example.com AAAA')" % sys.argv[0]) > server = sys.argv[1] > qname = sys.argv[2] > if len(sys.argv) == 4: > qtype = dns.rdatatype.from_text(sys.argv[3]) > else: > qtype = dns.rdatatype.AAAA > message = dns.message.make_query(qname, qtype, options=opts) > response = dns.query.udp(message, server) > if response.rcode() == dns.rcode.NOERROR: > data = False > for rr in response.answer: > if rr.rdtype == qtype: > data = True > print(rr) > if not data: > print("No value for %s/%s" % (qname, dns.rdatatype.to_text(qtype))) > print_soa(response) > else: > print_rrserial(response) > elif response.rcode() == dns.rcode.NXDOMAIN: > print("%s not found" % qname) > print_soa(response) > elif response.rcode() == dns.rcode.SERVFAIL: > print("%s failed" % qname) > print_rrserial(response) > else: > print("Unknown return code %s" % response.rcode()) > > > // Test program to experiment the IETF draft draft-ietf-dnsop-rrserial (hereafter "the draft"). > // Depends on godns <https://miek.nl/2014/august/16/go-dns-package/> <https://github.com/miekg/dns>. > > // If you don't know Go: > // go get github.com/miekg/dns > // go build test-rrserial.go > > package main > > import ( > "encoding/binary" > "fmt" > "github.com/miekg/dns" > "os" > "strings" > ) > > const otype = 65024 > > func print_rrserial(msg *dns.Msg) { > opt := msg.IsEdns0() > for _, v := range opt.Option { > switch v := v.(type) { > case *dns.EDNS0_LOCAL: > if v.Option() == otype { > serial := binary.BigEndian.Uint32(v.Data) > fmt.Printf("EDNS rrserial found, \"%d\"\n", serial) > } > default: > continue > } > } > } > > func main() { > if len(os.Args) != 3 && len(os.Args) != 4 { > fmt.Printf("%s SERVER QNAME [QTYPE]\n", os.Args[0]) > os.Exit(1) > } > ns := os.Args[1] > qname := dns.Fqdn(os.Args[2]) > qtype := dns.TypeAAAA > ok := true > if len(os.Args) == 4 { > if qtype, ok = dns.StringToType[strings.ToUpper(os.Args[3])]; ok { > // Good value > } else { > fmt.Printf("%s is not a known record type\n", strings.ToUpper(os.Args[3])) > os.Exit(1) > } > } > m := new(dns.Msg) > m.Question = make([]dns.Question, 1) > c := new(dns.Client) > o := new(dns.OPT) > o.Hdr.Name = "." > o.Hdr.Rrtype = dns.TypeOPT > o.SetUDPSize(4096) > e := new(dns.EDNS0_LOCAL) > e.Code = otype > e.Data = []byte{} > o.Option = append(o.Option, e) > m.Extra = append(m.Extra, o) > m.Question[0] = dns.Question{qname, qtype, dns.ClassINET} > in, _, err := c.Exchange(m, ns+":53") > if err == nil && in != nil { > if in.Rcode == dns.RcodeRefused { > fmt.Printf("Query refused (may be %s is a resolver, we do not ask for recursion)\n", ns) > os.Exit(1) > } else if in.Rcode == dns.RcodeFormatError { > fmt.Printf("%s claims our format is incorrect, it is wrong\n", ns) > os.Exit(1) > } else if in.Rcode == dns.RcodeNameError { > fmt.Printf("%s not found\n", qname) > gotSoa := false > serial := uint32(0) > for _, rsoa := range in.Ns { > switch rsoa.(type) { > case *dns.SOA: > serial = rsoa.(*dns.SOA).Serial > gotSoa = true > default: > continue > } > } > if !gotSoa { > fmt.Printf("No SOA in the answer :-(\n") > os.Exit(1) > } else { > fmt.Printf("Serial number in SOA %d\n", serial) > } > os.Exit(0) > } else if in.Rcode == dns.RcodeServerFailure { > fmt.Printf("Server failure\n") > print_rrserial(in) > os.Exit(1) > } else if in.Rcode != dns.RcodeSuccess { > fmt.Printf("Wrong return code %d\n", in.Rcode) > os.Exit(1) > } > if len(in.Answer) > 0 { > for _, rec := range in.Answer { > fmt.Printf("%s\n", rec) > } > } else if len(in.Answer) == 0 { > fmt.Printf("Empty answer received\n") > } > print_rrserial(in) > } else { > if err != nil { > fmt.Printf("Error in query: %s\n", err) > } else if in == nil { > fmt.Printf("No answer received\n") > } > os.Exit(1) > } > }
- [DNSOP] I-D Action: draft-ietf-dnsop-rrserial-00.… internet-drafts
- Re: [DNSOP] I-D Action: draft-ietf-dnsop-rrserial… Stephane Bortzmeyer
- Re: [DNSOP] I-D Action: draft-ietf-dnsop-rrserial… Hugo Salgado
- Re: [DNSOP] I-D Action: draft-ietf-dnsop-rrserial… Stephane Bortzmeyer
- Re: [DNSOP] I-D Action: draft-ietf-dnsop-rrserial… Hugo Salgado