domain_name 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -2
- data/Rakefile +0 -8
- data/VERSION +1 -1
- data/domain_name.gemspec +6 -9
- data/lib/domain_name/etld_data.rb +4300 -4298
- data/lib/domain_name/etld_data.rb.erb +5 -3
- data/lib/domain_name/punycode.rb +198 -93
- data/test/test_domain_name-punycode.rb +1 -0
- metadata +37 -23
@@ -1,7 +1,9 @@
|
|
1
1
|
class DomainName
|
2
|
+
ETLD_DATA = {
|
3
|
+
<% etld_data.each_pair { |key, value| %> <%= key.inspect %> => <%= value.inspect %>,
|
4
|
+
<% } %> }
|
5
|
+
|
2
6
|
def self.etld_data
|
3
|
-
|
4
|
-
<% etld_data.each_pair { |key, value| %> <%= key.inspect %> => <%= value.inspect %>,
|
5
|
-
<% } %> }
|
7
|
+
ETLD_DATA
|
6
8
|
end
|
7
9
|
end
|
data/lib/domain_name/punycode.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# punycode.rb - PunyCode encoder for the Domain Name library
|
4
4
|
#
|
5
|
-
# Copyright (C) 2011 Akinori MUSHA, All rights reserved.
|
5
|
+
# Copyright (C) 2011, 2012 Akinori MUSHA, All rights reserved.
|
6
6
|
#
|
7
7
|
# Ported from puny.c, a part of VeriSign XCode (encode/decode) IDN
|
8
8
|
# Library.
|
@@ -58,125 +58,230 @@ class DomainName
|
|
58
58
|
INITIAL_N = 0x80
|
59
59
|
DELIMITER = '-'
|
60
60
|
|
61
|
-
|
62
|
-
MAXINT = (1 << 64) - 1
|
61
|
+
MAXINT = (1 << 32) - 1
|
63
62
|
|
64
|
-
# Used in the calculation of bias:
|
65
63
|
LOBASE = BASE - TMIN
|
66
|
-
|
67
|
-
# Used in the calculation of bias:
|
68
64
|
CUTOFF = LOBASE * TMAX / 2
|
69
65
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
(d + 22 + (d < 26 ? 75 : 0) - (flag ? (1 << 5) : 0)).chr
|
80
|
-
# 0..25 map to ASCII a..z or A..Z
|
81
|
-
# 26..35 map to ASCII 0..9
|
82
|
-
end
|
83
|
-
module_function :encode_digit
|
66
|
+
RE_NONBASIC = /[^\x00-\x7f]/
|
67
|
+
|
68
|
+
DECODE_DIGIT = {}.tap { |map|
|
69
|
+
# ASCII A..Z map to 0..25
|
70
|
+
# ASCII a..z map to 0..25
|
71
|
+
(0..25).each { |i| map[65 + i] = map[97 + i] = i }
|
72
|
+
# ASCII 0..9 map to 26..35
|
73
|
+
(26..35).each { |i| map[22 + i] = i }
|
74
|
+
}
|
84
75
|
|
85
|
-
#
|
86
|
-
|
87
|
-
|
88
|
-
output = ''
|
76
|
+
# Most errors we raise are basically kind of ArgumentError.
|
77
|
+
class ArgumentError < ::ArgumentError; end
|
78
|
+
class BufferOverflowError < ArgumentError; end
|
89
79
|
|
90
|
-
|
91
|
-
|
92
|
-
delta = 0
|
93
|
-
bias = INITIAL_BIAS;
|
80
|
+
class << self
|
81
|
+
private
|
94
82
|
|
95
|
-
#
|
96
|
-
|
83
|
+
# Returns the basic code point whose value (when used for
|
84
|
+
# representing integers) is d, which must be in the range 0 to
|
85
|
+
# BASE-1. The lowercase form is used unless flag is true, in
|
86
|
+
# which case the uppercase form is used. The behavior is
|
87
|
+
# undefined if flag is nonzero and digit d has no uppercase
|
88
|
+
# form.
|
89
|
+
def encode_digit(d, flag)
|
90
|
+
(d + 22 + (d < 26 ? 75 : 0) - (flag ? (1 << 5) : 0)).chr
|
91
|
+
# 0..25 map to ASCII a..z or A..Z
|
92
|
+
# 26..35 map to ASCII 0..9
|
93
|
+
end
|
97
94
|
|
98
|
-
|
95
|
+
# Returns the numeric value of a basic code point (for use in
|
96
|
+
# representing integers) in the range 0 to base-1, or nil if cp
|
97
|
+
# is does not represent a value.
|
98
|
+
def decode_digit(cp)
|
99
|
+
DECODE_DIGIT[cp]
|
100
|
+
end
|
99
101
|
|
100
|
-
|
101
|
-
# number of basic code points, and out is the number of characters
|
102
|
-
# that have been output.
|
102
|
+
public
|
103
103
|
|
104
|
-
|
104
|
+
# Encode a +string+ in Punycode
|
105
|
+
def encode(string)
|
106
|
+
input = string.unpack('U*')
|
107
|
+
output = ''
|
105
108
|
|
106
|
-
|
109
|
+
# Initialize the state
|
110
|
+
n = INITIAL_N
|
111
|
+
delta = 0
|
112
|
+
bias = INITIAL_BIAS
|
107
113
|
|
108
|
-
|
109
|
-
|
110
|
-
# the next larger one
|
114
|
+
# Handle the basic code points
|
115
|
+
input.each { |cp| output << cp.chr if cp < 0x80 }
|
111
116
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
117
|
+
h = b = output.length
|
118
|
+
|
119
|
+
# h is the number of code points that have been handled, b is the
|
120
|
+
# number of basic code points, and out is the number of characters
|
121
|
+
# that have been output.
|
122
|
+
|
123
|
+
output << DELIMITER if b > 0
|
124
|
+
|
125
|
+
# Main encoding loop
|
126
|
+
|
127
|
+
while h < input.length
|
128
|
+
# All non-basic code points < n have been handled already. Find
|
129
|
+
# the next larger one
|
130
|
+
|
131
|
+
m = MAXINT
|
132
|
+
input.each { |cp|
|
133
|
+
m = cp if (n...m) === cp
|
134
|
+
}
|
116
135
|
|
117
|
-
|
118
|
-
|
136
|
+
# Increase delta enough to advance the decoder's <n,i> state to
|
137
|
+
# <m,0>, but guard against overflow
|
119
138
|
|
120
|
-
|
121
|
-
raise BufferOverflowError
|
139
|
+
delta += (m - n) * (h + 1)
|
140
|
+
raise BufferOverflowError if delta > MAXINT
|
141
|
+
n = m
|
142
|
+
|
143
|
+
input.each { |cp|
|
144
|
+
# AMC-ACE-Z can use this simplified version instead
|
145
|
+
if cp < n
|
146
|
+
delta += 1
|
147
|
+
raise BufferOverflowError if delta > MAXINT
|
148
|
+
elsif cp == n
|
149
|
+
# Represent delta as a generalized variable-length integer
|
150
|
+
q = delta
|
151
|
+
k = BASE
|
152
|
+
loop {
|
153
|
+
t = k <= bias ? TMIN : k - bias >= TMAX ? TMAX : k - bias
|
154
|
+
break if q < t
|
155
|
+
q, r = (q - t).divmod(BASE - t)
|
156
|
+
output << encode_digit(t + r, false)
|
157
|
+
k += BASE
|
158
|
+
}
|
159
|
+
|
160
|
+
output << encode_digit(q, false)
|
161
|
+
|
162
|
+
# Adapt the bias
|
163
|
+
delta = h == b ? delta / DAMP : delta >> 1
|
164
|
+
delta += delta / (h + 1)
|
165
|
+
bias = 0
|
166
|
+
while delta > CUTOFF
|
167
|
+
delta /= LOBASE
|
168
|
+
bias += BASE
|
169
|
+
end
|
170
|
+
bias += (LOBASE + 1) * delta / (delta + SKEW)
|
171
|
+
|
172
|
+
delta = 0
|
173
|
+
h += 1
|
174
|
+
end
|
175
|
+
}
|
176
|
+
|
177
|
+
delta += 1
|
178
|
+
n += 1
|
122
179
|
end
|
123
|
-
delta += (m - n) * (h + 1)
|
124
|
-
n = m
|
125
180
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
181
|
+
output
|
182
|
+
end
|
183
|
+
|
184
|
+
# Encode a hostname using IDN/Punycode algorithms
|
185
|
+
def encode_hostname(hostname)
|
186
|
+
hostname.match(RE_NONBASIC) or return hostname
|
187
|
+
|
188
|
+
hostname.split('.').map { |name|
|
189
|
+
if name.match(RE_NONBASIC)
|
190
|
+
'xn--' << encode(name)
|
191
|
+
else
|
192
|
+
name
|
130
193
|
end
|
194
|
+
}.join('.')
|
195
|
+
end
|
131
196
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
197
|
+
# Decode a +string+ encoded in Punycode
|
198
|
+
def decode(string)
|
199
|
+
# Initialize the state
|
200
|
+
n = INITIAL_N
|
201
|
+
i = 0
|
202
|
+
bias = INITIAL_BIAS
|
203
|
+
|
204
|
+
if j = string.rindex(DELIMITER)
|
205
|
+
b = string[0...j]
|
206
|
+
|
207
|
+
b.match(RE_NONBASIC) and
|
208
|
+
raise ArgumentError, "Illegal character is found in basic part: #{string.inspect}"
|
209
|
+
|
210
|
+
# Handle the basic code points
|
211
|
+
|
212
|
+
output = b.unpack('U*')
|
213
|
+
u = string[(j + 1)..-1]
|
214
|
+
else
|
215
|
+
output = []
|
216
|
+
u = string
|
217
|
+
end
|
218
|
+
|
219
|
+
# Main decoding loop: Start just after the last delimiter if any
|
220
|
+
# basic code points were copied; start at the beginning
|
221
|
+
# otherwise.
|
222
|
+
|
223
|
+
input = u.unpack('C*')
|
224
|
+
input_length = input.length
|
225
|
+
h = 0
|
226
|
+
out = output.length
|
227
|
+
|
228
|
+
while h < input_length
|
229
|
+
# Decode a generalized variable-length integer into delta,
|
230
|
+
# which gets added to i. The overflow checking is easier
|
231
|
+
# if we increase i as we go, then subtract off its starting
|
232
|
+
# value at the end to obtain delta.
|
233
|
+
|
234
|
+
oldi = i
|
235
|
+
w = 1
|
236
|
+
k = BASE
|
155
237
|
|
156
|
-
|
238
|
+
loop {
|
239
|
+
digit = decode_digit(input[h]) or
|
240
|
+
raise ArgumentError, "Illegal character is found in non-basic part: #{string.inspect}"
|
157
241
|
h += 1
|
242
|
+
i += digit * w
|
243
|
+
raise BufferOverflowError if i > MAXINT
|
244
|
+
t = k <= bias ? TMIN : k - bias >= TMAX ? TMAX : k - bias
|
245
|
+
break if digit < t
|
246
|
+
w *= BASE - t
|
247
|
+
raise BufferOverflowError if w > MAXINT
|
248
|
+
k += BASE
|
249
|
+
h < input_length or raise ArgumentError, "Malformed input given: #{string.inspect}"
|
250
|
+
}
|
251
|
+
|
252
|
+
# Adapt the bias
|
253
|
+
delta = oldi == 0 ? i / DAMP : (i - oldi) >> 1
|
254
|
+
delta += delta / (out + 1)
|
255
|
+
bias = 0
|
256
|
+
while delta > CUTOFF
|
257
|
+
delta /= LOBASE
|
258
|
+
bias += BASE
|
158
259
|
end
|
159
|
-
|
260
|
+
bias += (LOBASE + 1) * delta / (delta + SKEW)
|
160
261
|
|
161
|
-
|
162
|
-
|
163
|
-
end
|
262
|
+
# i was supposed to wrap around from out+1 to 0, incrementing
|
263
|
+
# n each time, so we'll fix that now:
|
164
264
|
|
165
|
-
|
166
|
-
|
167
|
-
|
265
|
+
q, i = i.divmod(out + 1)
|
266
|
+
n += q
|
267
|
+
raise BufferOverflowError if n > MAXINT
|
168
268
|
|
169
|
-
|
170
|
-
hostname.match(/[^\x00-\x7f]/) or return hostname
|
269
|
+
# Insert n at position i of the output:
|
171
270
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
name
|
271
|
+
output[i, 0] = n
|
272
|
+
|
273
|
+
out += 1
|
274
|
+
i += 1
|
177
275
|
end
|
178
|
-
|
276
|
+
output.pack('U*')
|
277
|
+
end
|
278
|
+
|
279
|
+
# Decode a hostname using IDN/Punycode algorithms
|
280
|
+
def decode_hostname(hostname)
|
281
|
+
hostname.gsub(/(\A|\.)xn--([^.]*)/) {
|
282
|
+
$1 << decode($2)
|
283
|
+
}
|
284
|
+
end
|
179
285
|
end
|
180
|
-
module_function :encode_hostname
|
181
286
|
end
|
182
287
|
end
|
@@ -91,6 +91,7 @@ class TestDomainName < Test::Unit::TestCase
|
|
91
91
|
'-> $1.00 <--']
|
92
92
|
].each { |title, cps, punycode|
|
93
93
|
assert_equal punycode, DomainName::Punycode.encode(cps.pack('U*')), title
|
94
|
+
assert_equal cps.pack('U*').to_nfc, DomainName::Punycode.decode(punycode), title
|
94
95
|
}
|
95
96
|
end
|
96
97
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: domain_name
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-04-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: unf
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: 0.0.3
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.0.3
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: shoulda
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,21 +37,31 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: bundler
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ~>
|
42
52
|
- !ruby/object:Gem::Version
|
43
|
-
version: 1.
|
53
|
+
version: 1.1.0
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.1.0
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: jeweler
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ~>
|
@@ -54,21 +69,15 @@ dependencies:
|
|
54
69
|
version: 1.6.4
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
58
|
-
- !ruby/object:Gem::Dependency
|
59
|
-
name: rcov
|
60
|
-
requirement: &70324603439500 !ruby/object:Gem::Requirement
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
73
|
none: false
|
62
74
|
requirements:
|
63
|
-
- -
|
75
|
+
- - ~>
|
64
76
|
- !ruby/object:Gem::Version
|
65
|
-
version:
|
66
|
-
type: :development
|
67
|
-
prerelease: false
|
68
|
-
version_requirements: *70324603439500
|
77
|
+
version: 1.6.4
|
69
78
|
- !ruby/object:Gem::Dependency
|
70
79
|
name: rdoc
|
71
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
72
81
|
none: false
|
73
82
|
requirements:
|
74
83
|
- - ! '>='
|
@@ -76,7 +85,12 @@ dependencies:
|
|
76
85
|
version: 2.4.2
|
77
86
|
type: :development
|
78
87
|
prerelease: false
|
79
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 2.4.2
|
80
94
|
description: ! 'This is a Domain Name manipulation library for Ruby.
|
81
95
|
|
82
96
|
|
@@ -123,7 +137,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
123
137
|
version: '0'
|
124
138
|
segments:
|
125
139
|
- 0
|
126
|
-
hash:
|
140
|
+
hash: -1258501941076469497
|
127
141
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
142
|
none: false
|
129
143
|
requirements:
|
@@ -132,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
146
|
version: '0'
|
133
147
|
requirements: []
|
134
148
|
rubyforge_project:
|
135
|
-
rubygems_version: 1.8.
|
149
|
+
rubygems_version: 1.8.21
|
136
150
|
signing_key:
|
137
151
|
specification_version: 3
|
138
152
|
summary: Domain Name manipulation library for Ruby
|