diff --git a/Makefile b/Makefile index de81862..0263974 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ validns: main.o carp.o mempool.o textparse.o base64.o base32hex.o \ dname.o tlsa.o nid.o l32.o l64.o lp.o \ ipseckey.o cbtree.o mb.o mg.o mr.o minfo.o \ afsdb.o x25.o isdn.o rt.o px.o kx.o \ - dlv.o dhcid.o nsap.o + dlv.o dhcid.o nsap.o caa.o $(CC) $(CFLAGS) $(OPTIMIZE) -o validns \ main.o carp.o mempool.o textparse.o base64.o base32hex.o \ rr.o soa.o a.o cname.o mx.o ns.o \ @@ -38,7 +38,7 @@ validns: main.o carp.o mempool.o textparse.o base64.o base32hex.o \ dname.o tlsa.o nid.o l32.o l64.o lp.o \ ipseckey.o cbtree.o mb.o mg.o mr.o minfo.o \ afsdb.o x25.o isdn.o rt.o px.o kx.o \ - dlv.o dhcid.o nsap.o \ + dlv.o dhcid.o nsap.o caa.o \ -L/usr/local/lib -L/opt/local/lib $(EXTRALPATH) \ -lJudy -lcrypto $(EXTRALIBS) $(EXTRALINKING) @@ -53,7 +53,7 @@ clean: -rm -f nid.o l32.o l64.o lp.o ipseckey.o -rm -f cbtree.o mb.o mg.o mr.o minfo.o -rm -f afsdb.o x25.o isdn.o rt.o px.o kx.o - -rm -f dlv.o dhcid.o nsap.o + -rm -f dlv.o dhcid.o nsap.o caa.o -rm -f validns.core core @echo ':-)' @@ -177,6 +177,9 @@ ptr.o: ptr.c common.h textparse.h mempool.h carp.h rr.h sshfp.o: sshfp.c common.h textparse.h mempool.h carp.h rr.h $(CC) $(CFLAGS) $(OPTIMIZE) -c -o sshfp.o sshfp.c $(INCPATH) +caa.o: caa.c common.h textparse.h mempool.h carp.h rr.h + $(CC) $(CFLAGS) $(OPTIMIZE) -c -o caa.o caa.c $(INCPATH) + rp.o: rp.c common.h textparse.h mempool.h carp.h rr.h $(CC) $(CFLAGS) $(OPTIMIZE) -c -o rp.o rp.c $(INCPATH) diff --git a/caa.c b/caa.c new file mode 100644 index 0000000..9323b31 --- /dev/null +++ b/caa.c @@ -0,0 +1,81 @@ +/* + * Part of DNS zone file validator `validns`. + * + * Copyright 2011-2017 Anton Berezin + * Modified BSD license. + * (See LICENSE file in the distribution.) + * + */ +#include +#include +#include +#include + +#include "common.h" +#include "textparse.h" +#include "mempool.h" +#include "carp.h" +#include "rr.h" + +static struct rr* caa_parse(char *name, long ttl, int type, char *s) +{ + struct rr_caa *rr = getmem(sizeof(*rr)); + int algorithm, fp_type; + + algorithm = extract_integer(&s, "algorithm", NULL); + if (algorithm < 0) return NULL; + if (algorithm != 1 && algorithm != 2 && algorithm != 3 && algorithm != 4) + return bitch("unsupported algorithm"); + rr->algorithm = algorithm; + + fp_type = extract_integer(&s, "fp type", NULL); + if (fp_type < 0) return NULL; + if (fp_type != 1 && fp_type != 2) + return bitch("unsupported fp_type"); + rr->fp_type = fp_type; + + rr->fingerprint = extract_hex_binary_data(&s, "fingerprint", EXTRACT_EAT_WHITESPACE); + if (rr->fingerprint.length < 0) return NULL; + + if (rr->fp_type == 1 && rr->fingerprint.length != SHA1_BYTES) { + return bitch("wrong SHA-1 fingerprint length: %d bytes found, %d bytes expected", + rr->fingerprint.length, SHA1_BYTES); + } + if (rr->fp_type == 2 && rr->fingerprint.length != SHA256_BYTES) { + return bitch("wrong SHA-256 fingerprint length: %d bytes found, %d bytes expected", + rr->fingerprint.length, SHA256_BYTES); + } + + if (*s) { + return bitch("garbage after valid SSHFP data"); + } + return store_record(type, name, ttl, rr); +} + +static char* caa_human(struct rr *rrv) +{ + RRCAST(caa); + char ss[4096]; + char *s = ss; + int l; + int i; + + l = snprintf(s, 4096, "%u %u ", rr->algorithm, rr->fp_type); + s += l; + for (i = 0; i < rr->fingerprint.length; i++) { + l = snprintf(s, 4096-(s-ss), "%02X", (unsigned char)rr->fingerprint.data[i]); + s += l; + } + return quickstrdup_temp(ss); +} + +static struct binary_data caa_wirerdata(struct rr *rrv) +{ + RRCAST(caa); + + return compose_binary_data("11d", 1, + rr->algorithm, rr->fp_type, + rr->fingerprint); +} + +struct rr_methods caa_methods = { caa_parse, caa_human, caa_wirerdata, NULL, NULL }; diff --git a/t/test.pl b/t/test.pl index 371a273..c7f196d 100644 --- a/t/test.pl +++ b/t/test.pl @@ -126,6 +126,24 @@ like(shift @e, qr/CNAME and other data/, "DNAME+CNAME"); like(shift @e, qr/DNAME must not have any children \(but z.zzzz5.galaxyplus.org. exists\)/, "DNAME with children 2"); +like(shift @e, qr/CAA flag expected/, "CAA without a flag"); +like(shift @e, qr/CAA tag expected/, "CAA without a tag"); +like(shift @e, qr/CAA unrecognized flag value/, "CAA with bad flag"); +like(shift @e, qr/CAA unrecognized tag name/, "CAA with bad tag"); +like(shift @e, qr/CAA bad characters in tag/, "CAA with bad chars in tag"); +like(shift @e, qr/CAA reserved tag name/, "CAA with reserved tag 1"); +like(shift @e, qr/CAA reserved tag name/, "CAA with reserved tag 2"); +like(shift @e, qr/CAA reserved tag name/, "CAA with reserved tag 3"); +like(shift @e, qr/CAA missing tag value/, "CAA without a tag value"); +like(shift @e, qr/CAA invalid issue domain/, "CAA bad issue domain"); +like(shift @e, qr/CAA missing issue parameter value/, "CAA missing issue parameter value"); +like(shift @e, qr/CAA missing issue parameter tag/, "CAA missing issue parameter tag"); +like(shift @e, qr/CAA invalid issuewild domain/, "CAA bad issuewild domain"); +like(shift @e, qr/CAA missing issuewild parameter value/, "CAA missing issuewild parameter value"); +like(shift @e, qr/CAA missing issuewild parameter tag/, "CAA missing issuewild parameter tag"); +like(shift @e, qr/CAA iodef value not a URL/, "CAA iodef value is not a URL"); +like(shift @e, qr/CAA iodef value unrecognized URL/, "CAA iodef value unrecognized URL"); + is(+@e, 0, "no unaccounted errors"); #like(stdout, qr/validation errors: XX/, "error count"); diff --git a/t/zones/manyerrors.zone b/t/zones/manyerrors.zone index 6682983..b3a22c6 100644 --- a/t/zones/manyerrors.zone +++ b/t/zones/manyerrors.zone @@ -156,6 +156,35 @@ zzzz5 DNAME a.b.org. ; fine x.y.z.zzzz5 A 5.6.7.8 ; DNAME must not have any children (but z.zzzz5.galaxyplus.org exists) - yuck zzzz6 DNAME x.y.dk. ; fine, no induced error +@ CAA ; CAA flag expected + CAA 0 ; CAA tag expected + CAA 45 ; CAA unrecognized flag value + CAA 0 meow ; CAA unrecognized tag name + CAA 0 x-y ; CAA bad characters in tag + CAA 0 auth ; CAA reserved tag name + CAA 0 path ; CAA reserved tag name + CAA 0 policy ; CAA reserved tag name + CAA 0 issue ; CAA missing tag value + CAA 0 issue ";" ; fine + CAA 0 issue "hello/world" ; CAA invalid issue domain + CAA 0 issue "example.net" ; fine + CAA 0 issue "example.net ;" ; fine + CAA 0 issue "example.net ; sometag" ; CAA missing issue parameter value + CAA 0 issue "example.net ; =sometag" ; CAA missing issue parameter tag + CAA 0 issue "example.net ; sometag=somevalue othertag=othervalue" ; fine + CAA 0 issuewild ";" ; fine + CAA 0 issuewild "hello/world" ; CAA invalid issuewild domain + CAA 0 issuewild "example.net" ; fine + CAA 0 issuewild "example.net ;" ; fine + CAA 0 issuewild "example.net ; sometag" ; CAA missing issuewild parameter value + CAA 0 issuewild "example.net ; =sometag" ; CAA missing issuewild parameter tag + CAA 0 issuewild "example.net ; sometag=somevalue othertag=othervalue" ; fine + CAA 0 iodef "hello-world" ; CAA iodef value not a URL + CAA 0 iodef "hello:world" ; CAA iodef value unrecognized URL + CAA 0 iodef "mailto:tobez@tobez.org" ; fine + CAA 0 iodef "http://example.net" ; fine + CAA 128 iodef "https://example.net" ; fine + @ SOA ns1.catpipe.net. hostmaster.catpipe.net. ( ; skipped again 2011011400 ; Serial 1H ; Refresh