From 477cb4d3fac4a78db02381f0bc2bad51f9cce3d2 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sun, 25 Jan 2015 10:58:30 +0000 Subject: [PATCH] Implement IPSECKEY IPSECKEY is kinda strange because it has a type selector which tells what type a later rdata field has. The type can be a domainname, address or v6 address. You sort of wish Go would have a union type for this, but alas. Currently this is implemented as: GatewayA net.IP `dns:"a"` GatewayAAAA net.IP `dns:"aaaa"` GatewayName string `dns:"domain-name"` In the IPSECKEY. Only one of these is active at any one time. When parsing/packing and unpacking the value of GatewayType is checked to see what to do. Parsing from strings is also implemented properly and tested. The Unpack function still needs work. --- README.md | 3 +- dns_test.go | 14 +++++++ msg.go | 19 +++++++++ parse_test.go | 34 ++++++++++++++++ types.go | 49 ++++++++++++++++++----- zscan_rr.go | 105 ++++++++++++++++++++++++++++++++------------------ 6 files changed, 175 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 9de7f1c7..e3d8470d 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,8 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 3225 - DO bit (DNSSEC OK) * 340{1,2,3} - NAPTR record * 3445 - Limiting the scope of (DNS)KEY -* 3597 - Unkown RRs +* 3597 - Unknown RRs +* 4025 - IPSECKEY * 403{3,4,5} - DNSSEC + validation functions * 4255 - SSHFP record * 4343 - Case insensitivity diff --git a/dns_test.go b/dns_test.go index 16c86f47..9c39c12e 100644 --- a/dns_test.go +++ b/dns_test.go @@ -509,3 +509,17 @@ func TestCopy(t *testing.T) { t.Fatalf("Copy() failed %s != %s", rr.String(), rr1.String()) } } + +func TestPackIPSECKEY(t *testing.T) { + // TODO(miek): finish + tests := []string{ + "38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )", + "38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )", + "38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 192.0.2.3 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )", + "38.1.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )", + "0.d.4.0.3.0.e.f.f.f.3.f.0.1.2.0 7200 IN IPSECKEY ( 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )", + } + buf := make([]byte, 1024) + rr, _ := NewRR(tests[0]) + PackRR(rr, buf, 0, nil, false) +} diff --git a/msg.go b/msg.go index ecba463d..dbde557f 100644 --- a/msg.go +++ b/msg.go @@ -652,6 +652,12 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str off += len(b) } case `dns:"a"`: + if val.Type().String() == "dns.IPSECKEY" { + // Field(2) is GatewayType, must be 1 + if val.Field(2).Uint() != 1 { + continue + } + } // It must be a slice of 4, even if it is 16, we encode // only the first 4 if off+net.IPv4len > lenmsg { @@ -676,6 +682,12 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str return lenmsg, &Error{err: "overflow packing a"} } case `dns:"aaaa"`: + if val.Type().String() == "dns.IPSECKEY" { + // Field(2) is GatewayType, must be 2 + if val.Field(2).Uint() != 2 { + continue + } + } if fv.Len() == 0 { break } @@ -821,6 +833,13 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str copy(msg[off:off+len(b64)], b64) off += len(b64) case `dns:"domain-name"`: + if val.Type().String() == "dns.IPSECKEY" { + // Field(2) is GatewayType, 1 and 2 or used for addresses + x := val.Field(2).Uint() + if x == 1 || x == 2 { + continue + } + } if off, err = PackDomainName(s, msg, off, compression, false && compress); err != nil { return lenmsg, err } diff --git a/parse_test.go b/parse_test.go index c9c4c519..baee2a95 100644 --- a/parse_test.go +++ b/parse_test.go @@ -1373,3 +1373,37 @@ func TestPrintfVerbsRdata(t *testing.T) { t.Errorf("should be empty") } } + +func TestParseIPSECKEY(t *testing.T) { + tests := []string{ + "38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )", + "38.2.0.192.in-addr.arpa.\t7200\tIN\tIPSECKEY\t10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==", + + "38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )", + "38.2.0.192.in-addr.arpa.\t7200\tIN\tIPSECKEY\t10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==", + + "38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 192.0.2.3 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )", + "38.2.0.192.in-addr.arpa.\t7200\tIN\tIPSECKEY\t10 1 2 192.0.2.3 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==", + + "38.1.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )", + "38.1.0.192.in-addr.arpa.\t7200\tIN\tIPSECKEY\t10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==", + + "0.d.4.0.3.0.e.f.f.f.3.f.0.1.2.0 7200 IN IPSECKEY ( 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )", + "0.d.4.0.3.0.e.f.f.f.3.f.0.1.2.0.\t7200\tIN\tIPSECKEY\t10 2 2 2001:db8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ==", + } + for i := 0; i < len(tests)-1; i++ { + t1 := tests[i] + e1 := tests[i+1] + r, e := NewRR(t1) + if e != nil { + t.Logf("failed to parse IPSECKEY %s", e) + continue + } + if r.String() != e1 { + t.Logf("these two IPSECKEY records should match") + t.Logf("\n%s\n%s\n", r.String(), e1) + t.Fail() + } + i++ + } +} diff --git a/types.go b/types.go index 05b9ea21..783d5e45 100644 --- a/types.go +++ b/types.go @@ -1029,30 +1029,59 @@ func (rr *SSHFP) String() string { } type IPSECKEY struct { - Hdr RR_Header - Precedence uint8 + Hdr RR_Header + Precedence uint8 + // GatewayType: 1: A record, 2: AAAA record, 3: domainname. + // 0 is use for no type and GatewayName should be "." then. GatewayType uint8 Algorithm uint8 - Gateway string `dns:"ipseckey"` + // Gateway can be an A record, AAAA record or a domain name. + GatewayA net.IP `dns:"a"` + GatewayAAAA net.IP `dns:"aaaa"` + GatewayName string `dns:"domain-name"` PublicKey string `dns:"base64"` } func (rr *IPSECKEY) Header() *RR_Header { return &rr.Hdr } func (rr *IPSECKEY) copy() RR { - return &IPSECKEY{*rr.Hdr.copyHeader(), rr.Precedence, rr.GatewayType, rr.Algorithm, rr.Gateway, rr.PublicKey} + return &IPSECKEY{*rr.Hdr.copyHeader(), rr.Precedence, rr.GatewayType, rr.Algorithm, rr.GatewayA, rr.GatewayAAAA, rr.GatewayName, rr.PublicKey} } func (rr *IPSECKEY) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) + + s := rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) + " " + strconv.Itoa(int(rr.GatewayType)) + - " " + strconv.Itoa(int(rr.Algorithm)) + - " " + rr.Gateway + - " " + rr.PublicKey + " " + strconv.Itoa(int(rr.Algorithm)) + switch rr.GatewayType { + case 0: + fallthrough + case 3: + s += " " + rr.GatewayName + case 1: + s += " " + rr.GatewayA.String() + case 2: + s += " " + rr.GatewayAAAA.String() + default: + s += " ." + } + s += " " + rr.PublicKey + return s } func (rr *IPSECKEY) len() int { - return rr.Hdr.len() + 3 + len(rr.Gateway) + 1 + - base64.StdEncoding.DecodedLen(len(rr.PublicKey)) + l := rr.Hdr.len() + 3 + 1 + switch rr.GatewayType { + default: + fallthrough + case 0: + fallthrough + case 3: + l += len(rr.GatewayName) + case 1: + l += 4 + case 2: + l += 16 + } + return l + base64.StdEncoding.DecodedLen(len(rr.PublicKey)) } type KEY struct { diff --git a/zscan_rr.go b/zscan_rr.go index 5088cdb5..ed796989 100644 --- a/zscan_rr.go +++ b/zscan_rr.go @@ -1847,44 +1847,6 @@ func setURI(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setIPSECKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { - rr := new(IPSECKEY) - rr.Hdr = h - - l := <-c - if l.length == 0 { - return rr, nil, l.comment - } - if i, e := strconv.Atoi(l.token); e != nil { - return nil, &ParseError{f, "bad IPSECKEY Precedence", l}, "" - } else { - rr.Precedence = uint8(i) - } - <-c // _BLANK - l = <-c - if i, e := strconv.Atoi(l.token); e != nil { - return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, "" - } else { - rr.GatewayType = uint8(i) - } - <-c // _BLANK - l = <-c - if i, e := strconv.Atoi(l.token); e != nil { - return nil, &ParseError{f, "bad IPSECKEY Algorithm", l}, "" - } else { - rr.Algorithm = uint8(i) - } - <-c - l = <-c - rr.Gateway = l.token - s, e, c1 := endingToString(c, "bad IPSECKEY PublicKey", f) - if e != nil { - return nil, e, c1 - } - rr.PublicKey = s - return rr, nil, c1 -} - func setDHCID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { // awesome record to parse! rr := new(DHCID) @@ -2087,6 +2049,73 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } +func setIPSECKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { + rr := new(IPSECKEY) + rr.Hdr = h + l := <-c + if l.length == 0 { + return rr, nil, l.comment + } + if i, err := strconv.Atoi(l.token); err != nil { + return nil, &ParseError{f, "bad IPSECKEY Precedence", l}, "" + } else { + rr.Precedence = uint8(i) + } + <-c // _BLANK + l = <-c + if i, err := strconv.Atoi(l.token); err != nil { + return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, "" + } else { + rr.GatewayType = uint8(i) + } + <-c // _BLANK + l = <-c + if i, err := strconv.Atoi(l.token); err != nil { + return nil, &ParseError{f, "bad IPSECKEY Algorithm", l}, "" + } else { + rr.Algorithm = uint8(i) + } + + // Now according to GatewayType we can have different elements here + <-c // _BLANK + l = <-c + switch rr.GatewayType { + case 0: + fallthrough + case 3: + rr.GatewayName = l.token + if l.token == "@" { + rr.GatewayName = o + } + _, ok := IsDomainName(l.token) + if !ok { + return nil, &ParseError{f, "bad IPSECKEY GatewayName", l}, "" + } + if rr.GatewayName[l.length-1] != '.' { + rr.GatewayName = appendOrigin(rr.GatewayName, o) + } + case 1: + rr.GatewayA = net.ParseIP(l.token) + if rr.GatewayA == nil { + return nil, &ParseError{f, "bad IPSECKEY GatewayA", l}, "" + } + case 2: + rr.GatewayAAAA = net.ParseIP(l.token) + if rr.GatewayAAAA == nil { + return nil, &ParseError{f, "bad IPSECKEY GatewayAAAA", l}, "" + } + default: + return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, "" + } + + s, e, c1 := endingToString(c, "bad IPSECKEY PublicKey", f) + if e != nil { + return nil, e, c1 + } + rr.PublicKey = s + return rr, nil, c1 +} + var typeToparserFunc = map[uint16]parserFunc{ TypeAAAA: parserFunc{setAAAA, false}, TypeAFSDB: parserFunc{setAFSDB, false},