Source file src/pkg/vendor/golang.org/x/net/idna/idna9.0.0.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package idna
19
20 import (
21 "fmt"
22 "strings"
23 "unicode/utf8"
24
25 "golang.org/x/text/secure/bidirule"
26 "golang.org/x/text/unicode/norm"
27 )
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 func ToASCII(s string) (string, error) {
46 return Punycode.process(s, true)
47 }
48
49
50 func ToUnicode(s string) (string, error) {
51 return Punycode.process(s, false)
52 }
53
54
55 type Option func(*options)
56
57
58
59
60
61
62 func Transitional(transitional bool) Option {
63 return func(o *options) { o.transitional = true }
64 }
65
66
67
68 func VerifyDNSLength(verify bool) Option {
69 return func(o *options) { o.verifyDNSLength = verify }
70 }
71
72
73
74
75
76
77 func RemoveLeadingDots(remove bool) Option {
78 return func(o *options) { o.removeLeadingDots = remove }
79 }
80
81
82
83
84 func ValidateLabels(enable bool) Option {
85 return func(o *options) {
86
87
88 if o.mapping == nil && enable {
89 o.mapping = normalize
90 }
91 o.trie = trie
92 o.validateLabels = enable
93 o.fromPuny = validateFromPunycode
94 }
95 }
96
97
98
99
100
101
102
103
104
105 func StrictDomainName(use bool) Option {
106 return func(o *options) {
107 o.trie = trie
108 o.useSTD3Rules = use
109 o.fromPuny = validateFromPunycode
110 }
111 }
112
113
114
115
116
117
118 func BidiRule() Option {
119 return func(o *options) { o.bidirule = bidirule.ValidString }
120 }
121
122
123
124 func ValidateForRegistration() Option {
125 return func(o *options) {
126 o.mapping = validateRegistration
127 StrictDomainName(true)(o)
128 ValidateLabels(true)(o)
129 VerifyDNSLength(true)(o)
130 BidiRule()(o)
131 }
132 }
133
134
135
136
137
138
139
140
141
142 func MapForLookup() Option {
143 return func(o *options) {
144 o.mapping = validateAndMap
145 StrictDomainName(true)(o)
146 ValidateLabels(true)(o)
147 RemoveLeadingDots(true)(o)
148 }
149 }
150
151 type options struct {
152 transitional bool
153 useSTD3Rules bool
154 validateLabels bool
155 verifyDNSLength bool
156 removeLeadingDots bool
157
158 trie *idnaTrie
159
160
161 fromPuny func(p *Profile, s string) error
162
163
164
165 mapping func(p *Profile, s string) (string, error)
166
167
168
169 bidirule func(s string) bool
170 }
171
172
173 type Profile struct {
174 options
175 }
176
177 func apply(o *options, opts []Option) {
178 for _, f := range opts {
179 f(o)
180 }
181 }
182
183
184
185
186
187
188
189
190
191 func New(o ...Option) *Profile {
192 p := &Profile{}
193 apply(&p.options, o)
194 return p
195 }
196
197
198
199
200
201 func (p *Profile) ToASCII(s string) (string, error) {
202 return p.process(s, true)
203 }
204
205
206
207
208
209 func (p *Profile) ToUnicode(s string) (string, error) {
210 pp := *p
211 pp.transitional = false
212 return pp.process(s, false)
213 }
214
215
216
217 func (p *Profile) String() string {
218 s := ""
219 if p.transitional {
220 s = "Transitional"
221 } else {
222 s = "NonTransitional"
223 }
224 if p.useSTD3Rules {
225 s += ":UseSTD3Rules"
226 }
227 if p.validateLabels {
228 s += ":ValidateLabels"
229 }
230 if p.verifyDNSLength {
231 s += ":VerifyDNSLength"
232 }
233 return s
234 }
235
236 var (
237
238
239 Punycode *Profile = punycode
240
241
242
243
244 Lookup *Profile = lookup
245
246
247
248 Display *Profile = display
249
250
251
252 Registration *Profile = registration
253
254 punycode = &Profile{}
255 lookup = &Profile{options{
256 transitional: true,
257 useSTD3Rules: true,
258 validateLabels: true,
259 removeLeadingDots: true,
260 trie: trie,
261 fromPuny: validateFromPunycode,
262 mapping: validateAndMap,
263 bidirule: bidirule.ValidString,
264 }}
265 display = &Profile{options{
266 useSTD3Rules: true,
267 validateLabels: true,
268 removeLeadingDots: true,
269 trie: trie,
270 fromPuny: validateFromPunycode,
271 mapping: validateAndMap,
272 bidirule: bidirule.ValidString,
273 }}
274 registration = &Profile{options{
275 useSTD3Rules: true,
276 validateLabels: true,
277 verifyDNSLength: true,
278 trie: trie,
279 fromPuny: validateFromPunycode,
280 mapping: validateRegistration,
281 bidirule: bidirule.ValidString,
282 }}
283
284
285
286
287 )
288
289 type labelError struct{ label, code_ string }
290
291 func (e labelError) code() string { return e.code_ }
292 func (e labelError) Error() string {
293 return fmt.Sprintf("idna: invalid label %q", e.label)
294 }
295
296 type runeError rune
297
298 func (e runeError) code() string { return "P1" }
299 func (e runeError) Error() string {
300 return fmt.Sprintf("idna: disallowed rune %U", e)
301 }
302
303
304
305 func (p *Profile) process(s string, toASCII bool) (string, error) {
306 var err error
307 if p.mapping != nil {
308 s, err = p.mapping(p, s)
309 }
310
311 if p.removeLeadingDots {
312 for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
313 }
314 }
315
316
317 if err == nil && p.verifyDNSLength && s == "" {
318 err = &labelError{s, "A4"}
319 }
320 labels := labelIter{orig: s}
321 for ; !labels.done(); labels.next() {
322 label := labels.label()
323 if label == "" {
324
325
326 if err == nil && p.verifyDNSLength {
327 err = &labelError{s, "A4"}
328 }
329 continue
330 }
331 if strings.HasPrefix(label, acePrefix) {
332 u, err2 := decode(label[len(acePrefix):])
333 if err2 != nil {
334 if err == nil {
335 err = err2
336 }
337
338 continue
339 }
340 labels.set(u)
341 if err == nil && p.validateLabels {
342 err = p.fromPuny(p, u)
343 }
344 if err == nil {
345
346
347
348 err = p.validateLabel(u)
349 }
350 } else if err == nil {
351 err = p.validateLabel(label)
352 }
353 }
354 if toASCII {
355 for labels.reset(); !labels.done(); labels.next() {
356 label := labels.label()
357 if !ascii(label) {
358 a, err2 := encode(acePrefix, label)
359 if err == nil {
360 err = err2
361 }
362 label = a
363 labels.set(a)
364 }
365 n := len(label)
366 if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
367 err = &labelError{label, "A4"}
368 }
369 }
370 }
371 s = labels.result()
372 if toASCII && p.verifyDNSLength && err == nil {
373
374 n := len(s)
375 if n > 0 && s[n-1] == '.' {
376 n--
377 }
378 if len(s) < 1 || n > 253 {
379 err = &labelError{s, "A4"}
380 }
381 }
382 return s, err
383 }
384
385 func normalize(p *Profile, s string) (string, error) {
386 return norm.NFC.String(s), nil
387 }
388
389 func validateRegistration(p *Profile, s string) (string, error) {
390 if !norm.NFC.IsNormalString(s) {
391 return s, &labelError{s, "V1"}
392 }
393 for i := 0; i < len(s); {
394 v, sz := trie.lookupString(s[i:])
395
396 switch p.simplify(info(v).category()) {
397
398
399 case valid, deviation:
400 case disallowed, mapped, unknown, ignored:
401 r, _ := utf8.DecodeRuneInString(s[i:])
402 return s, runeError(r)
403 }
404 i += sz
405 }
406 return s, nil
407 }
408
409 func validateAndMap(p *Profile, s string) (string, error) {
410 var (
411 err error
412 b []byte
413 k int
414 )
415 for i := 0; i < len(s); {
416 v, sz := trie.lookupString(s[i:])
417 start := i
418 i += sz
419
420 switch p.simplify(info(v).category()) {
421 case valid:
422 continue
423 case disallowed:
424 if err == nil {
425 r, _ := utf8.DecodeRuneInString(s[start:])
426 err = runeError(r)
427 }
428 continue
429 case mapped, deviation:
430 b = append(b, s[k:start]...)
431 b = info(v).appendMapping(b, s[start:i])
432 case ignored:
433 b = append(b, s[k:start]...)
434
435 case unknown:
436 b = append(b, s[k:start]...)
437 b = append(b, "\ufffd"...)
438 }
439 k = i
440 }
441 if k == 0 {
442
443 s = norm.NFC.String(s)
444 } else {
445 b = append(b, s[k:]...)
446 if norm.NFC.QuickSpan(b) != len(b) {
447 b = norm.NFC.Bytes(b)
448 }
449
450 s = string(b)
451 }
452 return s, err
453 }
454
455
456 type labelIter struct {
457 orig string
458 slice []string
459 curStart int
460 curEnd int
461 i int
462 }
463
464 func (l *labelIter) reset() {
465 l.curStart = 0
466 l.curEnd = 0
467 l.i = 0
468 }
469
470 func (l *labelIter) done() bool {
471 return l.curStart >= len(l.orig)
472 }
473
474 func (l *labelIter) result() string {
475 if l.slice != nil {
476 return strings.Join(l.slice, ".")
477 }
478 return l.orig
479 }
480
481 func (l *labelIter) label() string {
482 if l.slice != nil {
483 return l.slice[l.i]
484 }
485 p := strings.IndexByte(l.orig[l.curStart:], '.')
486 l.curEnd = l.curStart + p
487 if p == -1 {
488 l.curEnd = len(l.orig)
489 }
490 return l.orig[l.curStart:l.curEnd]
491 }
492
493
494 func (l *labelIter) next() {
495 l.i++
496 if l.slice != nil {
497 if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" {
498 l.curStart = len(l.orig)
499 }
500 } else {
501 l.curStart = l.curEnd + 1
502 if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
503 l.curStart = len(l.orig)
504 }
505 }
506 }
507
508 func (l *labelIter) set(s string) {
509 if l.slice == nil {
510 l.slice = strings.Split(l.orig, ".")
511 }
512 l.slice[l.i] = s
513 }
514
515
516 const acePrefix = "xn--"
517
518 func (p *Profile) simplify(cat category) category {
519 switch cat {
520 case disallowedSTD3Mapped:
521 if p.useSTD3Rules {
522 cat = disallowed
523 } else {
524 cat = mapped
525 }
526 case disallowedSTD3Valid:
527 if p.useSTD3Rules {
528 cat = disallowed
529 } else {
530 cat = valid
531 }
532 case deviation:
533 if !p.transitional {
534 cat = valid
535 }
536 case validNV8, validXV8:
537
538 cat = valid
539 }
540 return cat
541 }
542
543 func validateFromPunycode(p *Profile, s string) error {
544 if !norm.NFC.IsNormalString(s) {
545 return &labelError{s, "V1"}
546 }
547 for i := 0; i < len(s); {
548 v, sz := trie.lookupString(s[i:])
549 if c := p.simplify(info(v).category()); c != valid && c != deviation {
550 return &labelError{s, "V6"}
551 }
552 i += sz
553 }
554 return nil
555 }
556
557 const (
558 zwnj = "\u200c"
559 zwj = "\u200d"
560 )
561
562 type joinState int8
563
564 const (
565 stateStart joinState = iota
566 stateVirama
567 stateBefore
568 stateBeforeVirama
569 stateAfter
570 stateFAIL
571 )
572
573 var joinStates = [][numJoinTypes]joinState{
574 stateStart: {
575 joiningL: stateBefore,
576 joiningD: stateBefore,
577 joinZWNJ: stateFAIL,
578 joinZWJ: stateFAIL,
579 joinVirama: stateVirama,
580 },
581 stateVirama: {
582 joiningL: stateBefore,
583 joiningD: stateBefore,
584 },
585 stateBefore: {
586 joiningL: stateBefore,
587 joiningD: stateBefore,
588 joiningT: stateBefore,
589 joinZWNJ: stateAfter,
590 joinZWJ: stateFAIL,
591 joinVirama: stateBeforeVirama,
592 },
593 stateBeforeVirama: {
594 joiningL: stateBefore,
595 joiningD: stateBefore,
596 joiningT: stateBefore,
597 },
598 stateAfter: {
599 joiningL: stateFAIL,
600 joiningD: stateBefore,
601 joiningT: stateAfter,
602 joiningR: stateStart,
603 joinZWNJ: stateFAIL,
604 joinZWJ: stateFAIL,
605 joinVirama: stateAfter,
606 },
607 stateFAIL: {
608 0: stateFAIL,
609 joiningL: stateFAIL,
610 joiningD: stateFAIL,
611 joiningT: stateFAIL,
612 joiningR: stateFAIL,
613 joinZWNJ: stateFAIL,
614 joinZWJ: stateFAIL,
615 joinVirama: stateFAIL,
616 },
617 }
618
619
620
621 func (p *Profile) validateLabel(s string) error {
622 if s == "" {
623 if p.verifyDNSLength {
624 return &labelError{s, "A4"}
625 }
626 return nil
627 }
628 if p.bidirule != nil && !p.bidirule(s) {
629 return &labelError{s, "B"}
630 }
631 if !p.validateLabels {
632 return nil
633 }
634 trie := p.trie
635 if len(s) > 4 && s[2] == '-' && s[3] == '-' {
636 return &labelError{s, "V2"}
637 }
638 if s[0] == '-' || s[len(s)-1] == '-' {
639 return &labelError{s, "V3"}
640 }
641
642 v, sz := trie.lookupString(s)
643 x := info(v)
644 if x.isModifier() {
645 return &labelError{s, "V5"}
646 }
647
648 if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
649 return nil
650 }
651 st := stateStart
652 for i := 0; ; {
653 jt := x.joinType()
654 if s[i:i+sz] == zwj {
655 jt = joinZWJ
656 } else if s[i:i+sz] == zwnj {
657 jt = joinZWNJ
658 }
659 st = joinStates[st][jt]
660 if x.isViramaModifier() {
661 st = joinStates[st][joinVirama]
662 }
663 if i += sz; i == len(s) {
664 break
665 }
666 v, sz = trie.lookupString(s[i:])
667 x = info(v)
668 }
669 if st == stateFAIL || st == stateAfter {
670 return &labelError{s, "C"}
671 }
672 return nil
673 }
674
675 func ascii(s string) bool {
676 for i := 0; i < len(s); i++ {
677 if s[i] >= utf8.RuneSelf {
678 return false
679 }
680 }
681 return true
682 }
683
View as plain text