a go dns packet parser
at codereview 3.2 kB view raw
1package magna 2 3import ( 4 "encoding/binary" 5 "strings" 6) 7 8// decode_domain decodes a domain name from a buffer starting at offset. 9// It returns the domain name along with the offset and error. 10func decode_domain(buf []byte, offset int) (string, int, error) { 11 var builder strings.Builder 12 firstLabel := true 13 14 seen_offsets := make(map[int]struct{}) 15 finalOffsetAfterJump := -1 16 17 currentOffset := offset 18 19 for { 20 if _, found := seen_offsets[currentOffset]; found { 21 return "", len(buf), &DomainCompressionError{} 22 } 23 seen_offsets[currentOffset] = struct{}{} 24 25 length, nextOffsetAfterLen, err := getU8(buf, currentOffset) 26 if err != nil { 27 return "", len(buf), err 28 } 29 30 if length == 0 { 31 currentOffset = nextOffsetAfterLen 32 break 33 } 34 35 if (length & 0xC0) == 0xC0 { 36 sec, nextOffsetAfterPtr, err := getU8(buf, nextOffsetAfterLen) 37 if err != nil { 38 return "", len(buf), err 39 } 40 41 jumpTargetOffset := int(length&0x3F)<<8 | int(sec) 42 43 if jumpTargetOffset >= len(buf) { 44 return "", len(buf), &BufferOverflowError{Length: len(buf), Offset: jumpTargetOffset} 45 } 46 if _, found := seen_offsets[jumpTargetOffset]; found { 47 return "", len(buf), &DomainCompressionError{} 48 } 49 50 if finalOffsetAfterJump == -1 { 51 finalOffsetAfterJump = nextOffsetAfterPtr 52 } 53 54 currentOffset = jumpTargetOffset 55 continue 56 } 57 58 if length > 63 { 59 return "", len(buf), &InvalidLabelError{Length: int(length)} 60 } 61 62 labelBytes, nextOffsetAfterLabel, err := getSlice(buf, nextOffsetAfterLen, int(length)) 63 if err != nil { 64 return "", len(buf), err 65 } 66 67 if !firstLabel { 68 builder.WriteByte('.') 69 } 70 builder.Write(labelBytes) 71 firstLabel = false 72 73 currentOffset = nextOffsetAfterLabel 74 75 } 76 77 finalReadOffset := currentOffset 78 if finalOffsetAfterJump != -1 { 79 finalReadOffset = finalOffsetAfterJump 80 } 81 82 return builder.String(), finalReadOffset, nil 83} 84 85// encode_domain returns the bytes of the input bytes appened with the encoded domain name. 86func encode_domain(bytes []byte, domain_name string, offsets *map[string]uint16) []byte { 87 if domain_name == "." || domain_name == "" { 88 return append(bytes, 0) 89 } 90 91 clean_domain := strings.TrimSuffix(domain_name, ".") 92 if clean_domain == "" { 93 return append(bytes, 0) 94 } 95 96 start := 0 97 for start < len(clean_domain) { 98 suffix := clean_domain[start:] 99 100 if offset, found := (*offsets)[suffix]; found { 101 if offset > 0x3FFF { 102 end := strings.IndexByte(suffix, '.') 103 if end == -1 { 104 end = len(suffix) 105 } 106 } else { 107 pointer := 0xC000 | offset 108 return binary.BigEndian.AppendUint16(bytes, pointer) 109 } 110 } 111 112 currentPos := uint16(len(bytes)) 113 if currentPos <= 0x3FFF { 114 (*offsets)[suffix] = currentPos 115 } 116 117 end := strings.IndexByte(suffix, '.') 118 var label string 119 nextStart := len(clean_domain) 120 121 if end == -1 { 122 label = suffix 123 start = nextStart 124 } else { 125 label = suffix[:end] 126 nextStart = start + end + 1 127 start = nextStart 128 } 129 130 labelBytes := []byte(label) 131 132 if len(labelBytes) > 63 { 133 // XXX: maybe should return an error 134 labelBytes = labelBytes[:63] 135 } 136 137 bytes = append(bytes, byte(len(labelBytes))) 138 bytes = append(bytes, labelBytes...) 139 } 140 141 return append(bytes, 0) 142}