dns/dnsmessage: fix bug in length fixup

If during packing, the byte slice gets re-allocated between packing the
ResourceHeader and ResourceBody, the length will get updated in the old
byte slice before re-allocation resulting in a zero length in the final
packed message.

This change fixes the bug by passing the offset at which the length
should be written instead of a slice of the output byte slice from
ResourceHeader encoding step.

Updates golang/go#16218

Change-Id: Ifd7e2f549b7087ed5b52eaa6ae78970fec4ad988
Reviewed-on: https://go-review.googlesource.com/123835
Run-TryBot: Ian Gudger <igudger@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/dns/dnsmessage/message.go b/dns/dnsmessage/message.go
index dc5b143..13fbc08 100644
--- a/dns/dnsmessage/message.go
+++ b/dns/dnsmessage/message.go
@@ -493,7 +493,7 @@
 	}
 	oldMsg := msg
 	r.Header.Type = r.Body.realType()
-	msg, length, err := r.Header.pack(msg, compression, compressionOff)
+	msg, lenOff, err := r.Header.pack(msg, compression, compressionOff)
 	if err != nil {
 		return msg, &nestedError{"ResourceHeader", err}
 	}
@@ -502,7 +502,7 @@
 	if err != nil {
 		return msg, &nestedError{"content", err}
 	}
-	if err := r.Header.fixLen(msg, length, preLen); err != nil {
+	if err := r.Header.fixLen(msg, lenOff, preLen); err != nil {
 		return oldMsg, err
 	}
 	return msg, nil
@@ -1323,7 +1323,7 @@
 		return err
 	}
 	h.Type = r.realType()
-	msg, length, err := h.pack(b.msg, b.compression, b.start)
+	msg, lenOff, err := h.pack(b.msg, b.compression, b.start)
 	if err != nil {
 		return &nestedError{"ResourceHeader", err}
 	}
@@ -1331,7 +1331,7 @@
 	if msg, err = r.pack(msg, b.compression, b.start); err != nil {
 		return &nestedError{"CNAMEResource body", err}
 	}
-	if err := h.fixLen(msg, length, preLen); err != nil {
+	if err := h.fixLen(msg, lenOff, preLen); err != nil {
 		return err
 	}
 	if err := b.incrementSectionCount(); err != nil {
@@ -1347,7 +1347,7 @@
 		return err
 	}
 	h.Type = r.realType()
-	msg, length, err := h.pack(b.msg, b.compression, b.start)
+	msg, lenOff, err := h.pack(b.msg, b.compression, b.start)
 	if err != nil {
 		return &nestedError{"ResourceHeader", err}
 	}
@@ -1355,7 +1355,7 @@
 	if msg, err = r.pack(msg, b.compression, b.start); err != nil {
 		return &nestedError{"MXResource body", err}
 	}
-	if err := h.fixLen(msg, length, preLen); err != nil {
+	if err := h.fixLen(msg, lenOff, preLen); err != nil {
 		return err
 	}
 	if err := b.incrementSectionCount(); err != nil {
@@ -1371,7 +1371,7 @@
 		return err
 	}
 	h.Type = r.realType()
-	msg, length, err := h.pack(b.msg, b.compression, b.start)
+	msg, lenOff, err := h.pack(b.msg, b.compression, b.start)
 	if err != nil {
 		return &nestedError{"ResourceHeader", err}
 	}
@@ -1379,7 +1379,7 @@
 	if msg, err = r.pack(msg, b.compression, b.start); err != nil {
 		return &nestedError{"NSResource body", err}
 	}
-	if err := h.fixLen(msg, length, preLen); err != nil {
+	if err := h.fixLen(msg, lenOff, preLen); err != nil {
 		return err
 	}
 	if err := b.incrementSectionCount(); err != nil {
@@ -1395,7 +1395,7 @@
 		return err
 	}
 	h.Type = r.realType()
-	msg, length, err := h.pack(b.msg, b.compression, b.start)
+	msg, lenOff, err := h.pack(b.msg, b.compression, b.start)
 	if err != nil {
 		return &nestedError{"ResourceHeader", err}
 	}
@@ -1403,7 +1403,7 @@
 	if msg, err = r.pack(msg, b.compression, b.start); err != nil {
 		return &nestedError{"PTRResource body", err}
 	}
-	if err := h.fixLen(msg, length, preLen); err != nil {
+	if err := h.fixLen(msg, lenOff, preLen); err != nil {
 		return err
 	}
 	if err := b.incrementSectionCount(); err != nil {
@@ -1419,7 +1419,7 @@
 		return err
 	}
 	h.Type = r.realType()
-	msg, length, err := h.pack(b.msg, b.compression, b.start)
+	msg, lenOff, err := h.pack(b.msg, b.compression, b.start)
 	if err != nil {
 		return &nestedError{"ResourceHeader", err}
 	}
@@ -1427,7 +1427,7 @@
 	if msg, err = r.pack(msg, b.compression, b.start); err != nil {
 		return &nestedError{"SOAResource body", err}
 	}
-	if err := h.fixLen(msg, length, preLen); err != nil {
+	if err := h.fixLen(msg, lenOff, preLen); err != nil {
 		return err
 	}
 	if err := b.incrementSectionCount(); err != nil {
@@ -1443,7 +1443,7 @@
 		return err
 	}
 	h.Type = r.realType()
-	msg, length, err := h.pack(b.msg, b.compression, b.start)
+	msg, lenOff, err := h.pack(b.msg, b.compression, b.start)
 	if err != nil {
 		return &nestedError{"ResourceHeader", err}
 	}
@@ -1451,7 +1451,7 @@
 	if msg, err = r.pack(msg, b.compression, b.start); err != nil {
 		return &nestedError{"TXTResource body", err}
 	}
-	if err := h.fixLen(msg, length, preLen); err != nil {
+	if err := h.fixLen(msg, lenOff, preLen); err != nil {
 		return err
 	}
 	if err := b.incrementSectionCount(); err != nil {
@@ -1467,7 +1467,7 @@
 		return err
 	}
 	h.Type = r.realType()
-	msg, length, err := h.pack(b.msg, b.compression, b.start)
+	msg, lenOff, err := h.pack(b.msg, b.compression, b.start)
 	if err != nil {
 		return &nestedError{"ResourceHeader", err}
 	}
@@ -1475,7 +1475,7 @@
 	if msg, err = r.pack(msg, b.compression, b.start); err != nil {
 		return &nestedError{"SRVResource body", err}
 	}
-	if err := h.fixLen(msg, length, preLen); err != nil {
+	if err := h.fixLen(msg, lenOff, preLen); err != nil {
 		return err
 	}
 	if err := b.incrementSectionCount(); err != nil {
@@ -1491,7 +1491,7 @@
 		return err
 	}
 	h.Type = r.realType()
-	msg, length, err := h.pack(b.msg, b.compression, b.start)
+	msg, lenOff, err := h.pack(b.msg, b.compression, b.start)
 	if err != nil {
 		return &nestedError{"ResourceHeader", err}
 	}
@@ -1499,7 +1499,7 @@
 	if msg, err = r.pack(msg, b.compression, b.start); err != nil {
 		return &nestedError{"AResource body", err}
 	}
-	if err := h.fixLen(msg, length, preLen); err != nil {
+	if err := h.fixLen(msg, lenOff, preLen); err != nil {
 		return err
 	}
 	if err := b.incrementSectionCount(); err != nil {
@@ -1515,7 +1515,7 @@
 		return err
 	}
 	h.Type = r.realType()
-	msg, length, err := h.pack(b.msg, b.compression, b.start)
+	msg, lenOff, err := h.pack(b.msg, b.compression, b.start)
 	if err != nil {
 		return &nestedError{"ResourceHeader", err}
 	}
@@ -1523,7 +1523,7 @@
 	if msg, err = r.pack(msg, b.compression, b.start); err != nil {
 		return &nestedError{"AAAAResource body", err}
 	}
-	if err := h.fixLen(msg, length, preLen); err != nil {
+	if err := h.fixLen(msg, lenOff, preLen); err != nil {
 		return err
 	}
 	if err := b.incrementSectionCount(); err != nil {
@@ -1539,7 +1539,7 @@
 		return err
 	}
 	h.Type = r.realType()
-	msg, length, err := h.pack(b.msg, b.compression, b.start)
+	msg, lenOff, err := h.pack(b.msg, b.compression, b.start)
 	if err != nil {
 		return &nestedError{"ResourceHeader", err}
 	}
@@ -1547,7 +1547,7 @@
 	if msg, err = r.pack(msg, b.compression, b.start); err != nil {
 		return &nestedError{"OPTResource body", err}
 	}
-	if err := h.fixLen(msg, length, preLen); err != nil {
+	if err := h.fixLen(msg, lenOff, preLen); err != nil {
 		return err
 	}
 	if err := b.incrementSectionCount(); err != nil {
@@ -1606,19 +1606,18 @@
 
 // pack appends the wire format of the ResourceHeader to oldMsg.
 //
-// The bytes where length was packed are returned as a slice so they can be
-// updated after the rest of the Resource has been packed.
-func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]int, compressionOff int) (msg []byte, length []byte, err error) {
+// lenOff is the offset in msg where the Length field was packed.
+func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]int, compressionOff int) (msg []byte, lenOff int, err error) {
 	msg = oldMsg
 	if msg, err = h.Name.pack(msg, compression, compressionOff); err != nil {
-		return oldMsg, nil, &nestedError{"Name", err}
+		return oldMsg, 0, &nestedError{"Name", err}
 	}
 	msg = packType(msg, h.Type)
 	msg = packClass(msg, h.Class)
 	msg = packUint32(msg, h.TTL)
-	lenBegin := len(msg)
+	lenOff = len(msg)
 	msg = packUint16(msg, h.Length)
-	return msg, msg[lenBegin : lenBegin+uint16Len], nil
+	return msg, lenOff, nil
 }
 
 func (h *ResourceHeader) unpack(msg []byte, off int) (int, error) {
@@ -1642,14 +1641,20 @@
 	return newOff, nil
 }
 
-func (h *ResourceHeader) fixLen(msg []byte, length []byte, preLen int) error {
+// fixLen updates a packed ResourceHeader to include the length of the
+// ResourceBody.
+//
+// lenOff is the offset of the ResourceHeader.Length field in msg.
+//
+// preLen is the length that msg was before the ResourceBody was packed.
+func (h *ResourceHeader) fixLen(msg []byte, lenOff int, preLen int) error {
 	conLen := len(msg) - preLen
 	if conLen > int(^uint16(0)) {
 		return errResTooLong
 	}
 
 	// Fill in the length now that we know how long the content is.
-	packUint16(length[:0], uint16(conLen))
+	packUint16(msg[lenOff:lenOff], uint16(conLen))
 	h.Length = uint16(conLen)
 
 	return nil
diff --git a/dns/dnsmessage/message_test.go b/dns/dnsmessage/message_test.go
index d4f3492..25ba8f0 100644
--- a/dns/dnsmessage/message_test.go
+++ b/dns/dnsmessage/message_test.go
@@ -846,6 +846,36 @@
 	}
 }
 
+func TestResourcePackLength(t *testing.T) {
+	r := Resource{
+		ResourceHeader{
+			Name:  MustNewName("."),
+			Type:  TypeA,
+			Class: ClassINET,
+		},
+		&AResource{[4]byte{127, 0, 0, 2}},
+	}
+
+	hb, _, err := r.Header.pack(nil, nil, 0)
+	if err != nil {
+		t.Fatal("ResourceHeader.pack() =", err)
+	}
+	buf := make([]byte, 0, len(hb))
+	buf, err = r.pack(buf, nil, 0)
+	if err != nil {
+		t.Fatal("Resource.pack() =", err)
+	}
+
+	var hdr ResourceHeader
+	if _, err := hdr.unpack(buf, 0); err != nil {
+		t.Fatal("ResourceHeader.unpack() =", err)
+	}
+
+	if got, want := int(hdr.Length), len(buf)-len(hb); got != want {
+		t.Errorf("got hdr.Length = %d, want = %d", got, want)
+	}
+}
+
 func TestOptionPackUnpack(t *testing.T) {
 	for _, tt := range []struct {
 		name     string