1 package dns
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "errors"
7 "fmt"
8 "net"
9 "sort"
10 "strconv"
11 "strings"
12 )
13
14
15 type SVCBKey uint16
16
17
18 const (
19 SVCB_MANDATORY SVCBKey = iota
20 SVCB_ALPN
21 SVCB_NO_DEFAULT_ALPN
22 SVCB_PORT
23 SVCB_IPV4HINT
24 SVCB_ECHCONFIG
25 SVCB_IPV6HINT
26 SVCB_DOHPATH
27
28 svcb_RESERVED SVCBKey = 65535
29 )
30
31 var svcbKeyToStringMap = map[SVCBKey]string{
32 SVCB_MANDATORY: "mandatory",
33 SVCB_ALPN: "alpn",
34 SVCB_NO_DEFAULT_ALPN: "no-default-alpn",
35 SVCB_PORT: "port",
36 SVCB_IPV4HINT: "ipv4hint",
37 SVCB_ECHCONFIG: "ech",
38 SVCB_IPV6HINT: "ipv6hint",
39 SVCB_DOHPATH: "dohpath",
40 }
41
42 var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap)
43
44 func reverseSVCBKeyMap(m map[SVCBKey]string) map[string]SVCBKey {
45 n := make(map[string]SVCBKey, len(m))
46 for u, s := range m {
47 n[s] = u
48 }
49 return n
50 }
51
52
53
54
55 func (key SVCBKey) String() string {
56 if x := svcbKeyToStringMap[key]; x != "" {
57 return x
58 }
59 if key == svcb_RESERVED {
60 return ""
61 }
62 return "key" + strconv.FormatUint(uint64(key), 10)
63 }
64
65
66
67
68 func svcbStringToKey(s string) SVCBKey {
69 if strings.HasPrefix(s, "key") {
70 a, err := strconv.ParseUint(s[3:], 10, 16)
71
72
73 if err != nil || a == 65535 || s[3] == '0' || svcbKeyToStringMap[SVCBKey(a)] != "" {
74 return svcb_RESERVED
75 }
76 return SVCBKey(a)
77 }
78 if key, ok := svcbStringToKeyMap[s]; ok {
79 return key
80 }
81 return svcb_RESERVED
82 }
83
84 func (rr *SVCB) parse(c *zlexer, o string) *ParseError {
85 l, _ := c.Next()
86 i, e := strconv.ParseUint(l.token, 10, 16)
87 if e != nil || l.err {
88 return &ParseError{l.token, "bad SVCB priority", l}
89 }
90 rr.Priority = uint16(i)
91
92 c.Next()
93 l, _ = c.Next()
94 rr.Target = l.token
95
96 name, nameOk := toAbsoluteName(l.token, o)
97 if l.err || !nameOk {
98 return &ParseError{l.token, "bad SVCB Target", l}
99 }
100 rr.Target = name
101
102
103 l, _ = c.Next()
104 var xs []SVCBKeyValue
105
106
107 canHaveNextKey := true
108 for l.value != zNewline && l.value != zEOF {
109 switch l.value {
110 case zString:
111 if !canHaveNextKey {
112
113
114 return &ParseError{l.token, "bad SVCB value quotation", l}
115 }
116
117
118
119
120
121 idx := strings.IndexByte(l.token, '=')
122 var key, value string
123 if idx < 0 {
124
125 key = l.token
126 } else if idx == 0 {
127 return &ParseError{l.token, "bad SVCB key", l}
128 } else {
129 key, value = l.token[:idx], l.token[idx+1:]
130
131 if value == "" {
132
133
134 l, _ = c.Next()
135 if l.value == zQuote {
136
137
138 canHaveNextKey = false
139
140 l, _ = c.Next()
141 switch l.value {
142 case zString:
143
144 value = l.token
145 l, _ = c.Next()
146 if l.value != zQuote {
147 return &ParseError{l.token, "SVCB unterminated value", l}
148 }
149 case zQuote:
150
151 default:
152 return &ParseError{l.token, "bad SVCB value", l}
153 }
154 }
155 }
156 }
157 kv := makeSVCBKeyValue(svcbStringToKey(key))
158 if kv == nil {
159 return &ParseError{l.token, "bad SVCB key", l}
160 }
161 if err := kv.parse(value); err != nil {
162 return &ParseError{l.token, err.Error(), l}
163 }
164 xs = append(xs, kv)
165 case zQuote:
166 return &ParseError{l.token, "SVCB key can't contain double quotes", l}
167 case zBlank:
168 canHaveNextKey = true
169 default:
170 return &ParseError{l.token, "bad SVCB values", l}
171 }
172 l, _ = c.Next()
173 }
174
175
176
177
178
179
180
181 rr.Value = xs
182 return nil
183 }
184
185
186 func makeSVCBKeyValue(key SVCBKey) SVCBKeyValue {
187 switch key {
188 case SVCB_MANDATORY:
189 return new(SVCBMandatory)
190 case SVCB_ALPN:
191 return new(SVCBAlpn)
192 case SVCB_NO_DEFAULT_ALPN:
193 return new(SVCBNoDefaultAlpn)
194 case SVCB_PORT:
195 return new(SVCBPort)
196 case SVCB_IPV4HINT:
197 return new(SVCBIPv4Hint)
198 case SVCB_ECHCONFIG:
199 return new(SVCBECHConfig)
200 case SVCB_IPV6HINT:
201 return new(SVCBIPv6Hint)
202 case SVCB_DOHPATH:
203 return new(SVCBDoHPath)
204 case svcb_RESERVED:
205 return nil
206 default:
207 e := new(SVCBLocal)
208 e.KeyCode = key
209 return e
210 }
211 }
212
213
214
215
216
217
218 type SVCB struct {
219 Hdr RR_Header
220 Priority uint16
221 Target string `dns:"domain-name"`
222 Value []SVCBKeyValue `dns:"pairs"`
223 }
224
225
226
227
228
229
230
231 type HTTPS struct {
232 SVCB
233 }
234
235 func (rr *HTTPS) String() string {
236 return rr.SVCB.String()
237 }
238
239 func (rr *HTTPS) parse(c *zlexer, o string) *ParseError {
240 return rr.SVCB.parse(c, o)
241 }
242
243
244
245 type SVCBKeyValue interface {
246 Key() SVCBKey
247 pack() ([]byte, error)
248 unpack([]byte) error
249 String() string
250 parse(string) error
251 copy() SVCBKeyValue
252 len() int
253 }
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277 type SVCBMandatory struct {
278 Code []SVCBKey
279 }
280
281 func (*SVCBMandatory) Key() SVCBKey { return SVCB_MANDATORY }
282
283 func (s *SVCBMandatory) String() string {
284 str := make([]string, len(s.Code))
285 for i, e := range s.Code {
286 str[i] = e.String()
287 }
288 return strings.Join(str, ",")
289 }
290
291 func (s *SVCBMandatory) pack() ([]byte, error) {
292 codes := cloneSlice(s.Code)
293 sort.Slice(codes, func(i, j int) bool {
294 return codes[i] < codes[j]
295 })
296 b := make([]byte, 2*len(codes))
297 for i, e := range codes {
298 binary.BigEndian.PutUint16(b[2*i:], uint16(e))
299 }
300 return b, nil
301 }
302
303 func (s *SVCBMandatory) unpack(b []byte) error {
304 if len(b)%2 != 0 {
305 return errors.New("dns: svcbmandatory: value length is not a multiple of 2")
306 }
307 codes := make([]SVCBKey, 0, len(b)/2)
308 for i := 0; i < len(b); i += 2 {
309
310 codes = append(codes, SVCBKey(binary.BigEndian.Uint16(b[i:])))
311 }
312 s.Code = codes
313 return nil
314 }
315
316 func (s *SVCBMandatory) parse(b string) error {
317 codes := make([]SVCBKey, 0, strings.Count(b, ",")+1)
318 for len(b) > 0 {
319 var key string
320 key, b, _ = strings.Cut(b, ",")
321 codes = append(codes, svcbStringToKey(key))
322 }
323 s.Code = codes
324 return nil
325 }
326
327 func (s *SVCBMandatory) len() int {
328 return 2 * len(s.Code)
329 }
330
331 func (s *SVCBMandatory) copy() SVCBKeyValue {
332 return &SVCBMandatory{cloneSlice(s.Code)}
333 }
334
335
336
337
338
339
340
341
342
343
344
345
346 type SVCBAlpn struct {
347 Alpn []string
348 }
349
350 func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN }
351
352 func (s *SVCBAlpn) String() string {
353
354
355
356
357
358
359
360
361
362
363 var str strings.Builder
364 for i, alpn := range s.Alpn {
365
366 str.Grow(4*len(alpn) + 1)
367 if i > 0 {
368 str.WriteByte(',')
369 }
370 for j := 0; j < len(alpn); j++ {
371 e := alpn[j]
372 if ' ' > e || e > '~' {
373 str.WriteString(escapeByte(e))
374 continue
375 }
376 switch e {
377
378 case '"', ';', ' ':
379 str.WriteByte('\\')
380 str.WriteByte(e)
381
382
383
384
385 case ',':
386 str.WriteString(`\\\044`)
387 case '\\':
388 str.WriteString(`\\\092`)
389 default:
390 str.WriteByte(e)
391 }
392 }
393 }
394 return str.String()
395 }
396
397 func (s *SVCBAlpn) pack() ([]byte, error) {
398
399 b := make([]byte, 0, 10*len(s.Alpn))
400 for _, e := range s.Alpn {
401 if e == "" {
402 return nil, errors.New("dns: svcbalpn: empty alpn-id")
403 }
404 if len(e) > 255 {
405 return nil, errors.New("dns: svcbalpn: alpn-id too long")
406 }
407 b = append(b, byte(len(e)))
408 b = append(b, e...)
409 }
410 return b, nil
411 }
412
413 func (s *SVCBAlpn) unpack(b []byte) error {
414
415 alpn := make([]string, 0, len(b)/4)
416 for i := 0; i < len(b); {
417 length := int(b[i])
418 i++
419 if i+length > len(b) {
420 return errors.New("dns: svcbalpn: alpn array overflowing")
421 }
422 alpn = append(alpn, string(b[i:i+length]))
423 i += length
424 }
425 s.Alpn = alpn
426 return nil
427 }
428
429 func (s *SVCBAlpn) parse(b string) error {
430 if len(b) == 0 {
431 s.Alpn = []string{}
432 return nil
433 }
434
435 alpn := []string{}
436 a := []byte{}
437 for p := 0; p < len(b); {
438 c, q := nextByte(b, p)
439 if q == 0 {
440 return errors.New("dns: svcbalpn: unterminated escape")
441 }
442 p += q
443
444 if c == ',' {
445 if len(a) == 0 {
446 return errors.New("dns: svcbalpn: empty protocol identifier")
447 }
448 alpn = append(alpn, string(a))
449 a = []byte{}
450 continue
451 }
452
453 if c == '\\' {
454 dc, dq := nextByte(b, p)
455 if dq == 0 {
456 return errors.New("dns: svcbalpn: unterminated escape decoding comma-separated list")
457 }
458 if dc != '\\' && dc != ',' {
459 return errors.New("dns: svcbalpn: bad escaped character decoding comma-separated list")
460 }
461 p += dq
462 c = dc
463 }
464 a = append(a, c)
465 }
466
467 if len(a) == 0 {
468 return errors.New("dns: svcbalpn: last protocol identifier empty")
469 }
470 s.Alpn = append(alpn, string(a))
471 return nil
472 }
473
474 func (s *SVCBAlpn) len() int {
475 var l int
476 for _, e := range s.Alpn {
477 l += 1 + len(e)
478 }
479 return l
480 }
481
482 func (s *SVCBAlpn) copy() SVCBKeyValue {
483 return &SVCBAlpn{cloneSlice(s.Alpn)}
484 }
485
486
487
488
489
490
491
492
493
494
495
496 type SVCBNoDefaultAlpn struct{}
497
498 func (*SVCBNoDefaultAlpn) Key() SVCBKey { return SVCB_NO_DEFAULT_ALPN }
499 func (*SVCBNoDefaultAlpn) copy() SVCBKeyValue { return &SVCBNoDefaultAlpn{} }
500 func (*SVCBNoDefaultAlpn) pack() ([]byte, error) { return []byte{}, nil }
501 func (*SVCBNoDefaultAlpn) String() string { return "" }
502 func (*SVCBNoDefaultAlpn) len() int { return 0 }
503
504 func (*SVCBNoDefaultAlpn) unpack(b []byte) error {
505 if len(b) != 0 {
506 return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
507 }
508 return nil
509 }
510
511 func (*SVCBNoDefaultAlpn) parse(b string) error {
512 if b != "" {
513 return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
514 }
515 return nil
516 }
517
518
519
520
521
522
523
524
525 type SVCBPort struct {
526 Port uint16
527 }
528
529 func (*SVCBPort) Key() SVCBKey { return SVCB_PORT }
530 func (*SVCBPort) len() int { return 2 }
531 func (s *SVCBPort) String() string { return strconv.FormatUint(uint64(s.Port), 10) }
532 func (s *SVCBPort) copy() SVCBKeyValue { return &SVCBPort{s.Port} }
533
534 func (s *SVCBPort) unpack(b []byte) error {
535 if len(b) != 2 {
536 return errors.New("dns: svcbport: port length is not exactly 2 octets")
537 }
538 s.Port = binary.BigEndian.Uint16(b)
539 return nil
540 }
541
542 func (s *SVCBPort) pack() ([]byte, error) {
543 b := make([]byte, 2)
544 binary.BigEndian.PutUint16(b, s.Port)
545 return b, nil
546 }
547
548 func (s *SVCBPort) parse(b string) error {
549 port, err := strconv.ParseUint(b, 10, 16)
550 if err != nil {
551 return errors.New("dns: svcbport: port out of range")
552 }
553 s.Port = uint16(port)
554 return nil
555 }
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572 type SVCBIPv4Hint struct {
573 Hint []net.IP
574 }
575
576 func (*SVCBIPv4Hint) Key() SVCBKey { return SVCB_IPV4HINT }
577 func (s *SVCBIPv4Hint) len() int { return 4 * len(s.Hint) }
578
579 func (s *SVCBIPv4Hint) pack() ([]byte, error) {
580 b := make([]byte, 0, 4*len(s.Hint))
581 for _, e := range s.Hint {
582 x := e.To4()
583 if x == nil {
584 return nil, errors.New("dns: svcbipv4hint: expected ipv4, hint is ipv6")
585 }
586 b = append(b, x...)
587 }
588 return b, nil
589 }
590
591 func (s *SVCBIPv4Hint) unpack(b []byte) error {
592 if len(b) == 0 || len(b)%4 != 0 {
593 return errors.New("dns: svcbipv4hint: ipv4 address byte array length is not a multiple of 4")
594 }
595 b = cloneSlice(b)
596 x := make([]net.IP, 0, len(b)/4)
597 for i := 0; i < len(b); i += 4 {
598 x = append(x, net.IP(b[i:i+4]))
599 }
600 s.Hint = x
601 return nil
602 }
603
604 func (s *SVCBIPv4Hint) String() string {
605 str := make([]string, len(s.Hint))
606 for i, e := range s.Hint {
607 x := e.To4()
608 if x == nil {
609 return "<nil>"
610 }
611 str[i] = x.String()
612 }
613 return strings.Join(str, ",")
614 }
615
616 func (s *SVCBIPv4Hint) parse(b string) error {
617 if b == "" {
618 return errors.New("dns: svcbipv4hint: empty hint")
619 }
620 if strings.Contains(b, ":") {
621 return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6")
622 }
623
624 hint := make([]net.IP, 0, strings.Count(b, ",")+1)
625 for len(b) > 0 {
626 var e string
627 e, b, _ = strings.Cut(b, ",")
628 ip := net.ParseIP(e).To4()
629 if ip == nil {
630 return errors.New("dns: svcbipv4hint: bad ip")
631 }
632 hint = append(hint, ip)
633 }
634 s.Hint = hint
635 return nil
636 }
637
638 func (s *SVCBIPv4Hint) copy() SVCBKeyValue {
639 hint := make([]net.IP, len(s.Hint))
640 for i, ip := range s.Hint {
641 hint[i] = cloneSlice(ip)
642 }
643 return &SVCBIPv4Hint{Hint: hint}
644 }
645
646
647
648
649
650
651
652
653
654 type SVCBECHConfig struct {
655 ECH []byte
656 }
657
658 func (*SVCBECHConfig) Key() SVCBKey { return SVCB_ECHCONFIG }
659 func (s *SVCBECHConfig) String() string { return toBase64(s.ECH) }
660 func (s *SVCBECHConfig) len() int { return len(s.ECH) }
661
662 func (s *SVCBECHConfig) pack() ([]byte, error) {
663 return cloneSlice(s.ECH), nil
664 }
665
666 func (s *SVCBECHConfig) copy() SVCBKeyValue {
667 return &SVCBECHConfig{cloneSlice(s.ECH)}
668 }
669
670 func (s *SVCBECHConfig) unpack(b []byte) error {
671 s.ECH = cloneSlice(b)
672 return nil
673 }
674
675 func (s *SVCBECHConfig) parse(b string) error {
676 x, err := fromBase64([]byte(b))
677 if err != nil {
678 return errors.New("dns: svcbech: bad base64 ech")
679 }
680 s.ECH = x
681 return nil
682 }
683
684
685
686
687
688
689
690
691
692
693
694
695 type SVCBIPv6Hint struct {
696 Hint []net.IP
697 }
698
699 func (*SVCBIPv6Hint) Key() SVCBKey { return SVCB_IPV6HINT }
700 func (s *SVCBIPv6Hint) len() int { return 16 * len(s.Hint) }
701
702 func (s *SVCBIPv6Hint) pack() ([]byte, error) {
703 b := make([]byte, 0, 16*len(s.Hint))
704 for _, e := range s.Hint {
705 if len(e) != net.IPv6len || e.To4() != nil {
706 return nil, errors.New("dns: svcbipv6hint: expected ipv6, hint is ipv4")
707 }
708 b = append(b, e...)
709 }
710 return b, nil
711 }
712
713 func (s *SVCBIPv6Hint) unpack(b []byte) error {
714 if len(b) == 0 || len(b)%16 != 0 {
715 return errors.New("dns: svcbipv6hint: ipv6 address byte array length not a multiple of 16")
716 }
717 b = cloneSlice(b)
718 x := make([]net.IP, 0, len(b)/16)
719 for i := 0; i < len(b); i += 16 {
720 ip := net.IP(b[i : i+16])
721 if ip.To4() != nil {
722 return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4")
723 }
724 x = append(x, ip)
725 }
726 s.Hint = x
727 return nil
728 }
729
730 func (s *SVCBIPv6Hint) String() string {
731 str := make([]string, len(s.Hint))
732 for i, e := range s.Hint {
733 if x := e.To4(); x != nil {
734 return "<nil>"
735 }
736 str[i] = e.String()
737 }
738 return strings.Join(str, ",")
739 }
740
741 func (s *SVCBIPv6Hint) parse(b string) error {
742 if b == "" {
743 return errors.New("dns: svcbipv6hint: empty hint")
744 }
745
746 hint := make([]net.IP, 0, strings.Count(b, ",")+1)
747 for len(b) > 0 {
748 var e string
749 e, b, _ = strings.Cut(b, ",")
750 ip := net.ParseIP(e)
751 if ip == nil {
752 return errors.New("dns: svcbipv6hint: bad ip")
753 }
754 if ip.To4() != nil {
755 return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6")
756 }
757 hint = append(hint, ip)
758 }
759 s.Hint = hint
760 return nil
761 }
762
763 func (s *SVCBIPv6Hint) copy() SVCBKeyValue {
764 hint := make([]net.IP, len(s.Hint))
765 for i, ip := range s.Hint {
766 hint[i] = cloneSlice(ip)
767 }
768 return &SVCBIPv6Hint{Hint: hint}
769 }
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790 type SVCBDoHPath struct {
791 Template string
792 }
793
794 func (*SVCBDoHPath) Key() SVCBKey { return SVCB_DOHPATH }
795 func (s *SVCBDoHPath) String() string { return svcbParamToStr([]byte(s.Template)) }
796 func (s *SVCBDoHPath) len() int { return len(s.Template) }
797 func (s *SVCBDoHPath) pack() ([]byte, error) { return []byte(s.Template), nil }
798
799 func (s *SVCBDoHPath) unpack(b []byte) error {
800 s.Template = string(b)
801 return nil
802 }
803
804 func (s *SVCBDoHPath) parse(b string) error {
805 template, err := svcbParseParam(b)
806 if err != nil {
807 return fmt.Errorf("dns: svcbdohpath: %w", err)
808 }
809 s.Template = string(template)
810 return nil
811 }
812
813 func (s *SVCBDoHPath) copy() SVCBKeyValue {
814 return &SVCBDoHPath{
815 Template: s.Template,
816 }
817 }
818
819
820
821
822
823
824
825
826
827
828
829 type SVCBLocal struct {
830 KeyCode SVCBKey
831 Data []byte
832 }
833
834 func (s *SVCBLocal) Key() SVCBKey { return s.KeyCode }
835 func (s *SVCBLocal) String() string { return svcbParamToStr(s.Data) }
836 func (s *SVCBLocal) pack() ([]byte, error) { return cloneSlice(s.Data), nil }
837 func (s *SVCBLocal) len() int { return len(s.Data) }
838
839 func (s *SVCBLocal) unpack(b []byte) error {
840 s.Data = cloneSlice(b)
841 return nil
842 }
843
844 func (s *SVCBLocal) parse(b string) error {
845 data, err := svcbParseParam(b)
846 if err != nil {
847 return fmt.Errorf("dns: svcblocal: svcb private/experimental key %w", err)
848 }
849 s.Data = data
850 return nil
851 }
852
853 func (s *SVCBLocal) copy() SVCBKeyValue {
854 return &SVCBLocal{s.KeyCode, cloneSlice(s.Data)}
855 }
856
857 func (rr *SVCB) String() string {
858 s := rr.Hdr.String() +
859 strconv.Itoa(int(rr.Priority)) + " " +
860 sprintName(rr.Target)
861 for _, e := range rr.Value {
862 s += " " + e.Key().String() + "=\"" + e.String() + "\""
863 }
864 return s
865 }
866
867
868
869 func areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool {
870 a = cloneSlice(a)
871 b = cloneSlice(b)
872 sort.Slice(a, func(i, j int) bool { return a[i].Key() < a[j].Key() })
873 sort.Slice(b, func(i, j int) bool { return b[i].Key() < b[j].Key() })
874 for i, e := range a {
875 if e.Key() != b[i].Key() {
876 return false
877 }
878 b1, err1 := e.pack()
879 b2, err2 := b[i].pack()
880 if err1 != nil || err2 != nil || !bytes.Equal(b1, b2) {
881 return false
882 }
883 }
884 return true
885 }
886
887
888 func svcbParamToStr(s []byte) string {
889 var str strings.Builder
890 str.Grow(4 * len(s))
891 for _, e := range s {
892 if ' ' <= e && e <= '~' {
893 switch e {
894 case '"', ';', ' ', '\\':
895 str.WriteByte('\\')
896 str.WriteByte(e)
897 default:
898 str.WriteByte(e)
899 }
900 } else {
901 str.WriteString(escapeByte(e))
902 }
903 }
904 return str.String()
905 }
906
907
908 func svcbParseParam(b string) ([]byte, error) {
909 data := make([]byte, 0, len(b))
910 for i := 0; i < len(b); {
911 if b[i] != '\\' {
912 data = append(data, b[i])
913 i++
914 continue
915 }
916 if i+1 == len(b) {
917 return nil, errors.New("escape unterminated")
918 }
919 if isDigit(b[i+1]) {
920 if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) {
921 a, err := strconv.ParseUint(b[i+1:i+4], 10, 8)
922 if err == nil {
923 i += 4
924 data = append(data, byte(a))
925 continue
926 }
927 }
928 return nil, errors.New("bad escaped octet")
929 } else {
930 data = append(data, b[i+1])
931 i += 2
932 }
933 }
934 return data, nil
935 }
936
View as plain text