Browse Source

Functionally complete (I think) IDNA encode/decode support

pull/903/head
Ross McFarland 4 years ago
parent
commit
08523cd4ba
No known key found for this signature in database GPG Key ID: 943B179E15D3B22A
2 changed files with 34 additions and 15 deletions
  1. +20
    -14
      octodns/idna.py
  2. +14
    -1
      tests/test_octodns_idna.py

+ 20
- 14
octodns/idna.py View File

@ -6,21 +6,27 @@ from idna import decode as _decode, encode as _encode
def idna_encode(name):
if not name:
# idna.encode doesn't handle ''
# Based on https://github.com/psf/requests/pull/3695/files
# #diff-0debbb2447ce5debf2872cb0e17b18babe3566e9d9900739e8581b355bd513f7R39
try:
name.encode('ascii')
# No utf8 chars, just use as-is
return name
elif name.startswith('*'):
# idna.encode doesn't like the *
name = _encode(name[2:]).decode('utf-8')
return f'*.{name}'
return _encode(name).decode('utf-8')
except UnicodeEncodeError:
if name.startswith('*'):
# idna.encode doesn't like the *
name = _encode(name[2:]).decode('utf-8')
return f'*.{name}'
return _encode(name).decode('utf-8')
def idna_decode(name):
if not name:
# idna.decode doesn't handle ''
return name
elif name.startswith('*'):
# idna.decode doesn't like the *
return f'*.{_decode(name[2:])}'
return _decode(name)
pieces = name.lower().split('.')
if any([p.startswith('xn--') for p in pieces]):
# it's idna
if name.startswith('*'):
# idna.decode doesn't like the *
return f'*.{_decode(name[2:])}'
return _decode(name)
# not idna, just return as-is
return name

+ 14
- 1
tests/test_octodns_idna.py View File

@ -16,7 +16,7 @@ class TestIdna(TestCase):
got = idna_encode(value)
self.assertEqual(expected, got)
# round tripped
self.assertEqual(value, idna_decode(value))
self.assertEqual(value, idna_decode(got))
def test_noops(self):
# empty
@ -41,3 +41,16 @@ class TestIdna(TestCase):
# encoded with encoded name
self.assertIdna('zajęzyk.zajęzyk.pl.',
'xn--zajzyk-y4a.xn--zajzyk-y4a.pl.')
self.assertIdna('déjàvu.com.', 'xn--djvu-1na6c.com.')
self.assertIdna('déjà-vu.com.', 'xn--dj-vu-sqa5d.com.')
def test_underscores(self):
# underscores aren't valid in idna names, so these are all ascii
self.assertIdna('foo_bar.pl.', 'foo_bar.pl.')
self.assertIdna('bleep_bloop.foo_bar.pl.', 'bleep_bloop.foo_bar.pl.')
def test_case_insensitivity(self):
# Shouldn't be hit by octoDNS use cases, but checked anyway
self.assertEqual('zajęzyk.pl.', idna_decode('XN--ZAJZYK-Y4A.PL.'))

Loading…
Cancel
Save