@@ -6,10 +6,11 @@ import (
6
6
"math/rand"
7
7
"net"
8
8
"net/url"
9
+ "regexp"
9
10
"sort"
11
+ "strconv"
10
12
"strings"
11
13
12
- isd "github.com/jbenet/go-is-domain"
13
14
dns "github.com/miekg/dns"
14
15
)
15
16
@@ -112,7 +113,28 @@ func (s ByValue) Less(i, j int) bool { return s.LookupEntries[i].Value < s.Looku
112
113
113
114
type LookupTXTFunc func (name string ) (txt []LookupEntry , err error )
114
115
115
- func NewUDPLookup (servers []string ) LookupTXTFunc {
116
+ var utf8Replace = regexp .MustCompile (`\\\d{3}` )
117
+
118
+ func utf8ReplaceFunc (input []byte ) (result []byte ) {
119
+ result = make ([]byte , 1 )
120
+ num , _ := strconv .ParseUint (string (input [1 :]), 10 , 9 )
121
+ result [0 ] = byte (num )
122
+ return result
123
+ }
124
+
125
+ func utf8Value (input []string ) string {
126
+ str := strings .Join (input , "" )
127
+ return string (utf8Replace .ReplaceAllFunc ([]byte (str ), utf8ReplaceFunc ))
128
+ }
129
+
130
+ func NewUDPLookup (servers []string , udpSize uint16 ) LookupTXTFunc {
131
+ client := new (dns.Client )
132
+ if udpSize == 0 {
133
+ // Running into issues with too small buffer size of dns library in some cases
134
+ client .UDPSize = 4096
135
+ } else {
136
+ client .UDPSize = udpSize
137
+ }
116
138
return func (domain string ) (entries []LookupEntry , err error ) {
117
139
if ! strings .HasSuffix (domain , "." ) {
118
140
domain += "."
@@ -127,7 +149,7 @@ func NewUDPLookup(servers []string) LookupTXTFunc {
127
149
Qclass : dns .ClassINET ,
128
150
}
129
151
server := servers [rand .Intn (len (servers ))]
130
- res , err := dns .Exchange (req , server )
152
+ res , _ , err := client .Exchange (req , server )
131
153
if err != nil {
132
154
return nil , err
133
155
}
@@ -136,7 +158,7 @@ func NewUDPLookup(servers []string) LookupTXTFunc {
136
158
if answer .Header ().Rrtype == dns .TypeTXT {
137
159
txtAnswer := answer .(* dns.TXT )
138
160
entries [index ] = LookupEntry {
139
- Value : strings . Join (txtAnswer .Txt , "" ),
161
+ Value : utf8Value (txtAnswer .Txt ),
140
162
Ttl : txtAnswer .Header ().Ttl ,
141
163
}
142
164
}
@@ -185,11 +207,11 @@ func resolve(r *Resolver, domain string, recursive bool) (result Result, err err
185
207
if lookupTXT == nil {
186
208
lookupTXT = defaultLookupTXT
187
209
}
188
- lookup , error := validateDomain (domain )
210
+ lookup , error := validateDomain (domain , "" )
189
211
result .Links = map [string ][]LookupEntry {}
190
212
result .Path = []PathEntry {}
191
213
result .Log = []LogStatement {}[:]
192
- if lookup = = nil {
214
+ if error ! = nil {
193
215
result .Log = append (result .Log , * error )
194
216
return result , nil
195
217
}
@@ -225,7 +247,7 @@ func resolve(r *Resolver, domain string, recursive bool) (result Result, err err
225
247
}
226
248
}
227
249
228
- func validateDomain (input string ) (* URLParts , * LogStatement ) {
250
+ func validateDomain (input string , entry string ) (* URLParts , * LogStatement ) {
229
251
urlParts := relevantURLParts (input )
230
252
domain := urlParts .Domain
231
253
if strings .HasPrefix (domain , dnsPrefix ) {
@@ -241,21 +263,85 @@ func validateDomain(input string) (*URLParts, *LogStatement) {
241
263
}
242
264
}
243
265
}
244
- if ! isd . IsDomain (domain ) {
266
+ if ! isFqdn (domain ) {
245
267
return nil , & LogStatement {
246
- Code : "INVALID_REDIRECT" ,
247
- Domain : urlParts .Domain ,
248
- Pathname : urlParts .Pathname ,
249
- Search : urlParts .Search ,
268
+ Code : "INVALID_REDIRECT" ,
269
+ Entry : entry ,
250
270
}
251
271
}
272
+ domain = strings .TrimSuffix (domain , "." )
252
273
return & URLParts {
253
274
Domain : dnsPrefix + domain ,
254
275
Pathname : urlParts .Pathname ,
255
276
Search : urlParts .Search ,
256
277
}, nil
257
278
}
258
279
280
+ var intlDomainCharset = regexp .MustCompile ("^([a-z\u00a1 -\uffff ]{2,}|xn[a-z0-9-]{2,})$" )
281
+ var spacesAndSpecialChars = regexp .MustCompile ("[\\ s\u2002 -\u200B \u202F \u205F \u3000 ��\u00A9 \uFFFD \uFEFF ]" )
282
+ var domainCharset = regexp .MustCompile ("^[a-z\u00a1 -\u00ff 0-9-]+$" )
283
+
284
+ func isFqdn (str string ) bool {
285
+ str = strings .TrimSuffix (str , "." )
286
+ if str == "" {
287
+ return false
288
+ }
289
+ parts := strings .Split (str , "." )
290
+ tld := parts [len (parts )- 1 ]
291
+
292
+ // disallow fqdns without tld
293
+ if len (parts ) < 2 {
294
+ return false
295
+ }
296
+
297
+ if ! intlDomainCharset .MatchString (tld ) {
298
+ return false
299
+ }
300
+
301
+ // disallow spaces && special characers
302
+ if spacesAndSpecialChars .MatchString (tld ) {
303
+ return false
304
+ }
305
+
306
+ // disallow all numbers
307
+ if every (parts , isNumber ) {
308
+ return false
309
+ }
310
+
311
+ return every (parts , isDomainPart )
312
+ }
313
+
314
+ func isDomainPart (part string ) bool {
315
+ if len (part ) > 63 {
316
+ return false
317
+ }
318
+
319
+ if ! domainCharset .MatchString (part ) {
320
+ return false
321
+ }
322
+
323
+ // disallow parts starting or ending with hyphen
324
+ if strings .HasPrefix (part , "-" ) || strings .HasSuffix (part , "-" ) {
325
+ return false
326
+ }
327
+
328
+ return true
329
+ }
330
+
331
+ func isNumber (str string ) bool {
332
+ _ , err := strconv .Atoi (str )
333
+ return err == nil
334
+ }
335
+
336
+ func every (strings []string , test func (string ) bool ) bool {
337
+ for _ , str := range strings {
338
+ if ! test (str ) {
339
+ return false
340
+ }
341
+ }
342
+ return true
343
+ }
344
+
259
345
type processedEntry struct {
260
346
value string
261
347
entry string
@@ -331,9 +417,8 @@ func resolveTxtEntries(domain string, recursive bool, txtEntries []LookupEntry)
331
417
hasRedirect := false
332
418
var redirect * URLParts
333
419
for _ , dns := range dnsLinks {
334
- validated , error := validateDomain (dns .value )
420
+ validated , error := validateDomain (dns .value , dns . entry )
335
421
if error != nil {
336
- delete (found , "dns" )
337
422
log = append (log , * error )
338
423
} else if ! hasRedirect {
339
424
hasRedirect = true
@@ -345,11 +430,9 @@ func resolveTxtEntries(domain string, recursive bool, txtEntries []LookupEntry)
345
430
})
346
431
}
347
432
}
433
+ delete (found , "dns" )
348
434
if hasRedirect {
349
- for key , foundEntries := range found {
350
- if key == "dns" {
351
- continue
352
- }
435
+ for _ , foundEntries := range found {
353
436
for _ , foundEntry := range foundEntries {
354
437
log = append (log , LogStatement {
355
438
Code : "UNUSED_ENTRY" ,
0 commit comments