1package dns 2 3import ( 4 "fmt" 5 "regexp" 6 "strconv" 7 "strings" 8 "testing" 9) 10 11const maxPrintableLabel = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789x" 12 13var ( 14 longDomain = maxPrintableLabel[:53] + strings.TrimSuffix( 15 strings.Join([]string{".", ".", ".", ".", "."}, maxPrintableLabel[:49]), ".") 16 reChar = regexp.MustCompile(`.`) 17 i = -1 18 maxUnprintableLabel = reChar.ReplaceAllStringFunc(maxPrintableLabel, func(ch string) string { 19 if i++; i >= 32 { 20 i = 0 21 } 22 return fmt.Sprintf("\\%03d", i) 23 }) 24) 25 26func TestPackNoSideEffect(t *testing.T) { 27 m := new(Msg) 28 m.SetQuestion(Fqdn("example.com."), TypeNS) 29 30 a := new(Msg) 31 o := &OPT{ 32 Hdr: RR_Header{ 33 Name: ".", 34 Rrtype: TypeOPT, 35 }, 36 } 37 o.SetUDPSize(DefaultMsgSize) 38 39 a.Extra = append(a.Extra, o) 40 a.SetRcode(m, RcodeBadVers) 41 42 a.Pack() 43 if a.Rcode != RcodeBadVers { 44 t.Errorf("after pack: Rcode is expected to be BADVERS") 45 } 46} 47 48func TestUnpackDomainName(t *testing.T) { 49 var cases = []struct { 50 label string 51 input string 52 expectedOutput string 53 expectedError string 54 }{ 55 {"empty domain", 56 "\x00", 57 ".", 58 ""}, 59 {"long label", 60 string(63) + maxPrintableLabel + "\x00", 61 maxPrintableLabel + ".", 62 ""}, 63 {"unprintable label", 64 string(63) + regexp.MustCompile(`\\[0-9]+`).ReplaceAllStringFunc(maxUnprintableLabel, 65 func(escape string) string { 66 n, _ := strconv.ParseInt(escape[1:], 10, 8) 67 return string(n) 68 }) + "\x00", 69 maxUnprintableLabel + ".", 70 ""}, 71 {"long domain", 72 string(53) + strings.Replace(longDomain, ".", string(49), -1) + "\x00", 73 longDomain + ".", 74 ""}, 75 {"compression pointer", 76 // an unrealistic but functional test referencing an offset _inside_ a label 77 "\x03foo" + "\x05\x03com\x00" + "\x07example" + "\xC0\x05", 78 "foo.\\003com\\000.example.com.", 79 ""}, 80 81 {"too long domain", 82 string(54) + "x" + strings.Replace(longDomain, ".", string(49), -1) + "\x00", 83 "x" + longDomain + ".", 84 ErrLongDomain.Error()}, 85 {"too long by pointer", 86 // a matryoshka doll name to get over 255 octets after expansion via internal pointers 87 string([]byte{ 88 // 11 length values, first to last 89 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 0, 90 // 12 filler values 91 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 92 // 10 pointers, last to first 93 192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1, 94 }), 95 "", 96 ErrLongDomain.Error()}, 97 {"long by pointer", 98 // a matryoshka doll name _not_ exceeding 255 octets after expansion 99 string([]byte{ 100 // 11 length values, first to last 101 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 0, 102 // 9 filler values 103 120, 120, 120, 120, 120, 120, 120, 120, 120, 104 // 10 pointers, last to first 105 192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1, 106 }), 107 "" + 108 (`\"\031\028\025\022\019\016\013\010\000xxxxxxxxx` + 109 `\192\010\192\009\192\008\192\007\192\006\192\005\192\004\192\003\192\002.`) + 110 (`\031\028\025\022\019\016\013\010\000xxxxxxxxx` + 111 `\192\010\192\009\192\008\192\007\192\006\192\005\192\004\192\003.`) + 112 (`\028\025\022\019\016\013\010\000xxxxxxxxx` + 113 `\192\010\192\009\192\008\192\007\192\006\192\005\192\004.`) + 114 (`\025\022\019\016\013\010\000xxxxxxxxx` + 115 `\192\010\192\009\192\008\192\007\192\006\192\005.`) + 116 `\022\019\016\013\010\000xxxxxxxxx\192\010\192\009\192\008\192\007\192\006.` + 117 `\019\016\013\010\000xxxxxxxxx\192\010\192\009\192\008\192\007.` + 118 `\016\013\010\000xxxxxxxxx\192\010\192\009\192\008.` + 119 `\013\010\000xxxxxxxxx\192\010\192\009.` + 120 `\010\000xxxxxxxxx\192\010.` + 121 `\000xxxxxxxxx.`, 122 ""}, 123 {"truncated name", "\x07example\x03", "", "dns: buffer size too small"}, 124 {"non-absolute name", "\x07example\x03com", "", "dns: buffer size too small"}, 125 {"compression pointer cycle", 126 "\x03foo" + "\x03bar" + "\x07example" + "\xC0\x04", 127 "", 128 "dns: too many compression pointers"}, 129 {"reserved compression pointer 0b10", "\x07example\x80", "", "dns: bad rdata"}, 130 {"reserved compression pointer 0b01", "\x07example\x40", "", "dns: bad rdata"}, 131 } 132 for _, test := range cases { 133 output, idx, err := UnpackDomainName([]byte(test.input), 0) 134 if test.expectedOutput != "" && output != test.expectedOutput { 135 t.Errorf("%s: expected %s, got %s", test.label, test.expectedOutput, output) 136 } 137 if test.expectedError == "" && err != nil { 138 t.Errorf("%s: expected no error, got %d %v", test.label, idx, err) 139 } else if test.expectedError != "" && (err == nil || err.Error() != test.expectedError) { 140 t.Errorf("%s: expected error %s, got %d %v", test.label, test.expectedError, idx, err) 141 } 142 } 143} 144 145func TestPackDomainNameNSECTypeBitmap(t *testing.T) { 146 ownername := "some-very-long-ownername.com." 147 msg := &Msg{ 148 Compress: true, 149 Answer: []RR{ 150 &NS{ 151 Hdr: RR_Header{ 152 Name: ownername, 153 Rrtype: TypeNS, 154 Class: ClassINET, 155 }, 156 Ns: "ns1.server.com.", 157 }, 158 &NSEC{ 159 Hdr: RR_Header{ 160 Name: ownername, 161 Rrtype: TypeNSEC, 162 Class: ClassINET, 163 }, 164 NextDomain: "a.com.", 165 TypeBitMap: []uint16{TypeNS, TypeNSEC}, 166 }, 167 }, 168 } 169 170 // Pack msg and then unpack into msg2 171 buf, err := msg.Pack() 172 if err != nil { 173 t.Fatalf("msg.Pack failed: %v", err) 174 } 175 176 var msg2 Msg 177 if err := msg2.Unpack(buf); err != nil { 178 t.Fatalf("msg2.Unpack failed: %v", err) 179 } 180 181 if !IsDuplicate(msg.Answer[1], msg2.Answer[1]) { 182 t.Error("message differs after packing and unpacking") 183 184 // Print NSEC RR for both cases 185 t.Logf("expected: %v", msg.Answer[1]) 186 t.Logf("got: %v", msg2.Answer[1]) 187 } 188} 189