dns/rawmsg.go

144 lines
4.2 KiB
Go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package dns
import (
"fmt"
)
const maxPointer = 2 << 13 // We have 14 bits for the offset
// Function defined in this subpackage work on []byte and but still
// provide some higher level functions.
// RawSetId sets the message ID in buf.
func RawSetId(msg []byte, off int, id uint16) bool {
msg[off], msg[off+1] = packUint16(id)
return true
}
type rawlabel struct {
offset int // offset where this labels starts in the msg buf
name string // the label, not this includes the length at the start
}
type rawmove struct {
offset int // where to point to
from int // where in the buffer set the pointer
length int // used in calculating how much to shrink the message
}
// Compress performs name comression in the dns message contained in buf.
// It returns the number of bytes saved.
func Compress(msg []byte) int {
// First we create a table of domain names to which we
// can link. This is stored in 'table'
// Once for another name the longest possible link is
// found we save how we must change msg to perform this
// compression. This is saved in 'moves'. After we
// traversed the entire message, we perform all the
// moves.
// TODO: Maybe it should be optimized.
// Map the labels to the offset in the message
table := make(map[string]int)
moves := make([]rawmove, 0)
l := make([]rawlabel, 127) // Max labels?
i := 0
// Very much like the loop in msg.go
off := 12 // Start of the first name in the q section
question := true
Loop:
for {
c := int(msg[off])
switch c & 0xC0 {
case 0x00:
if c == 0x00 {
// Do all of the bookkeeping
name := ""
poffset := 0 // Where to point to
moffset := 0 // From where to insert the pointer
for j := i - 1; j >= 0; j-- {
name = l[j].name + name
if idx, ok := table[name]; !ok {
table[name] = l[j].offset
} else {
poffset = idx
moffset = l[j].offset
}
}
if poffset != 0 {
//println("We kunnen verwijzen naar", poffset, "vanaf", moffset, "voor", name)
//println("met lengte", len(name)+1)
// the +1 for the name is for the null byte at the end
// Discount for previous moves, reset the poffset counter
for i := len(moves)-1; i >= 0; i-- {
if poffset > moves[i].from {
poffset -= (moves[i].length - 2)
}
}
// need to shorten the rdlength in the RR too!
moves = append(moves, rawmove{offset: poffset, from: moffset, length: len(name) + 1})
}
// end of the name
if question {
// In question section
off += 4 // type, class + 1
question = false
} else {
// How to handle well known records here
// NS, MX, CNAME? eatName() function?
// In the "body" of the msg
off += 2 + 2 + 4 + 1 // type, class, ttl + 1
// we are at the rdlength
rdlength, _ := unpackUint16(msg, off)
off += int(rdlength) + 1 // Skip the rdata
}
off++
if off+1 > len(msg) {
break Loop
}
i = 0
continue Loop
}
if off+c+1 > len(msg) {
break Loop
}
// If we are too deep in the message we cannot point to
// it, so skip this label.
if off < maxPointer {
l[i] = rawlabel{offset: off, name: string(msg[off : off+c+1])}
i++
}
// save the new names
off += c + 1
default:
break Loop
}
}
// fmt.Printf("table %v\n", table)
// fmt.Printf("moves %v\n", moves)
saved := 0
// Start at the back, easier to move
for i := len(moves) - 1; i >= 0; i-- {
fmt.Printf("%v\n", moves[i])
// move the bytes
copy(msg[moves[i].from+1:], msg[moves[i].from+moves[i].length-1:])
// Now set the pointer at moves[i].from and moves[i].from+1
fmt.Printf("bits %b\n", moves[i].offset^0xC000)
msg[moves[i].from], msg[moves[i].from+1] = packUint16(uint16(moves[i].offset ^ 0xC000))
saved += moves[i].length // minus something
}
return saved
}