# IDN and Private RR in Go DNS


Thanks to the excellent work from Alex Sergeyev, [Go DNS](http://github.com/miekg/dns)
has gotten some new features. I want to highlight two: IDN (<https://www.ietf.org/rfc/rfc3492.txt>) and Private
RR support (<http://tools.ietf.org/html/rfc6895>).

## IDN

This adds support for converting from and to Punycode. There is *no* explicit support, you will
need to call `idn.ToPunycode` and `idn.FromPunyCode` yourself if you are dealing with IDNs.

The examples give in the code:

    name := "インターネット.テスト"
    fmt.Printf("%s -> %s", name, idn.ToPunycode(name))

Which outputs:

    インターネット.テスト -> xn--eckucmux0ukc.xn--zckzah

## Private RR

Another thing that was added is the ability to add private RR types: that is new
RR type with a code in the 65,280 - 65,534 range. This makes it possible to specify
your own (vanity) types, which then work just as the normal ones in Go DNS.

Say for instance I want to add a new RR called "MIEK" (with type code 65281), this
RR has two rdata elements: a domain name and it ends with a hexadecimal string. Basically
it looks like:

    www.example.com.    IN MIEK     example.com. AABB1234

Note that you can also use TYPExxxx and RFC3597 (<http://www.ietf.org/rfc/rfc3597.txt>)
for this, but then the above RR would look something like this:

    www.example.com.    IN TYPE65281 \# 8 AABBCCDDAABB1234

Which works, but of course looks bad in the vanity department.

To do this you will need (see
[example_test.go](https://github.com/miekg/dns/blob/master/example_test.go) for
example code in the library) create a new record type,
implement the `PrivateRdata` interface and register the new type with the library.

    const typeMiek = 65281
    type MIEK struct {
        Target string
        Extra  string
    }

Implement PrivateRdata:

1. `String` - returns the string presentation of the rdata;
2. `Len` - returns the length of the rdata;
3. `Pack` and `Unpack` to convert to and from wire format;
4. `Parse` to parse a text presentation of the rdata;
5. `Copy` to allow the private RR to be copied inside Go DNS (sometimes needed for DNSSEC).

So lets start with the implementation beginning with `String()` and `Len()`

    func (m *MIEK) String() string { return m.Target + " " + m.Extra }
    func (m *MIEK) Len() int  { return len(m.Target) + len(m.Extra)/2 }

And the functions to convert from and to wire format:

    func (m *MIEK) Pack(buf []byte) (int, error) {
            off, err := dns.PackDomainName(m.Target, buf, 0, nil, false)
            if err != nil {
                    return off, err
            }
            h, err := hex.DecodeString(m.Extra)
            if err != nil {
                    return off, err
            }
            if off+hex.DecodedLen(len(m.Extra)) > len(buf) {
                    return len(buf), errors.New("overflow packing hex")
            }
            copy(buf[off:off+hex.DecodedLen(len(m.Extra))], h)
            off += hex.DecodedLen(len(m.Extra))
            return off, nil
    }

    func (m *MIEK) Unpack(buf []byte) (int, error) {
            s, off, err := dns.UnpackDomainName(buf, 0)
            if err != nil {
                    return len(buf), err
            }
            m.Target = s
            s = hex.EncodeToString(buf[off:])
            m.Extra = s
            return len(buf), nil
    }

And our `Parse` function that parses the string slice `sx` into `MIEK`'s rdata.

    func (m *MIEK) Parse(sx []string) error {
            if len(sx) < 2 {
                    return errors.New("need at least 2 pieces of rdata")
            }
            m.Target = sx[0]
            if  _, ok := dns.IsDomainName(m.Target); !ok {
                    return errors.New("bad MIEK Target")
            }
            // Hex data can contain spaces.
            for _, s := range sx[1:] {
                    m.Extra += s
            }
            return nil
    }

And `Copy` which is needed to copy the rdata:

    func (m *MIEK) Copy(dest dns.PrivateRdata) error {
            m1, ok := dest.(*MIEK)
            if !ok {
                    return dns.ErrRdata
            }
            m1.Target = m.Target
            m1.Extra = m.Extra
            return nil
    }

And finally we can register the new type:

	dns.PrivateHandle("MIEK", typeMiek, func() dns.PrivateRdata { return new(MIEK) })
	defer dns.PrivateHandleRemove(typeMiek) // when removing again

We can now use all this code to do the following:

    m1, _ := dns.NewRR("example.com. IN MIEK target.example.com. DEADBEEF")
    log.Printf("%s\n", m1.String())

That prints:

     example.com.   3600    IN  MIEK    target.example.com. DEADBEEF

Mission accomplished :) If you need to export this new type, your best bet is to
convert it to an unknown RR:

    u := new(dns.RFC3597)
    u.ToRFC3597(m1)
    log.Printf("%s\n", u.String())

Yields: `example.com.    3600    IN MIEK    \# 24 06746172676574076578616d706c6503636f6d00deadbeef`

