1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
package rfc1035label
import (
"errors"
"fmt"
"strings"
)
// Labels represents RFC1035 labels
//
// This implements RFC 1035 labels, including compression.
// https://tools.ietf.org/html/rfc1035#section-4.1.4
type Labels struct {
// original contains the original bytes if the object was parsed from a byte
// sequence, or nil otherwise. The `original` field is necessary to deal
// with compressed labels. If the labels are further modified, the original
// content is invalidated and no compression will be used.
original []byte
// Labels contains the parsed labels. A change here invalidates the
// `original` object.
Labels []string
}
// same compares two string arrays
func same(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
// String prints labels.
func (l *Labels) String() string {
return fmt.Sprintf("%v", l.Labels)
}
// ToBytes returns a byte sequence representing the labels. If the original
// sequence is modified, the labels are parsed again, otherwise the original
// byte sequence is returned.
func (l *Labels) ToBytes() []byte {
// if the original byte sequence has been modified, invalidate it and
// serialize again.
// NOTE: this function is not thread-safe. If multiple threads modify
// the `Labels` field, the result may be wrong.
originalLabels, err := labelsFromBytes(l.original)
// if the original object has not been modified, or we cannot parse it,
// return the original bytes.
if err != nil || (l.original != nil && same(originalLabels, l.Labels)) {
return l.original
}
return labelsToBytes(l.Labels)
}
// Length returns the length in bytes of the serialized labels
func (l *Labels) Length() int {
return len(l.ToBytes())
}
// NewLabels returns an initialized Labels object.
func NewLabels() *Labels {
return &Labels{
Labels: make([]string, 0),
}
}
// FromBytes reads labels from a bytes stream according to RFC 1035.
func (l *Labels) FromBytes(data []byte) error {
labs, err := labelsFromBytes(data)
if err != nil {
return err
}
l.original = data
l.Labels = labs
return nil
}
// FromBytes returns a Labels object from the given byte sequence, or an error if
// any.
func FromBytes(data []byte) (*Labels, error) {
var l Labels
if err := l.FromBytes(data); err != nil {
return nil, err
}
return &l, nil
}
// fromBytes decodes a serialized stream and returns a list of labels
func labelsFromBytes(buf []byte) ([]string, error) {
var (
labels = make([]string, 0)
pos, oldPos int
label string
handlingPointer bool
)
for {
if pos >= len(buf) {
// interpret label without trailing zero-length byte as a partial
// domain name field as per RFC 4704 Section 4.2
if label != "" {
labels = append(labels, label)
}
break
}
length := int(buf[pos])
pos++
var chunk string
if length == 0 {
labels = append(labels, label)
label = ""
if handlingPointer {
pos = oldPos
handlingPointer = false
}
} else if length&0xc0 == 0xc0 {
// compression pointer
if handlingPointer {
return nil, errors.New("rfc1035label: cannot handle nested pointers")
}
handlingPointer = true
if pos+1 > len(buf) {
return nil, errors.New("rfc1035label: pointer buffer too short")
}
off := int(buf[pos-1]&^0xc0)<<8 + int(buf[pos])
oldPos = pos + 1
pos = off
} else {
if pos+length > len(buf) {
return nil, errors.New("rfc1035label: buffer too short")
}
chunk = string(buf[pos : pos+length])
if label != "" {
label += "."
}
label += chunk
pos += length
}
}
return labels, nil
}
// labelToBytes encodes a label and returns a serialized stream of bytes
func labelToBytes(label string) []byte {
var encodedLabel []byte
if len(label) == 0 {
return []byte{0}
}
for _, part := range strings.Split(label, ".") {
encodedLabel = append(encodedLabel, byte(len(part)))
encodedLabel = append(encodedLabel, []byte(part)...)
}
return append(encodedLabel, 0)
}
// labelsToBytes encodes a list of labels and returns a serialized stream of
// bytes
func labelsToBytes(labels []string) []byte {
var encodedLabels []byte
for _, label := range labels {
encodedLabels = append(encodedLabels, labelToBytes(label)...)
}
return encodedLabels
}
|