From c28f0584ba37cd6b0e9919dcbec652a34a420843 Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Wed, 28 Aug 2019 14:10:36 +0200 Subject: [PATCH] Backported original patch from: https://github.com/rthalley/dnspython/commit/c76aa6ac9969447220c8e807aa1e5640a6c12924 Unicode label escapify was not escapifying special characters. [Issue #339] --- dns/name.py | 57 +++++++++++++++++++++++----------------------- tests/test_name.py | 5 ++++ 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/dns/name.py b/dns/name.py index 97e216c..4a064d6 100644 --- a/dns/name.py +++ b/dns/name.py @@ -116,20 +116,28 @@ class IDNACodec(object): def __init__(self): pass + def is_idna(self, label): + return label.lower().startswith(b'xn--') + + def is_all_ascii(self, label): + for c in label: + if ord(c) > 0x7f: + return False + return True + def encode(self, label): raise NotImplementedError def decode(self, label): - # We do not apply any IDNA policy on decode; we just - downcased = label.lower() - if downcased.startswith(b'xn--'): + # We do not apply any IDNA policy on decode. + if self.is_idna(label): try: - label = downcased[4:].decode('punycode') + label = label[4:].decode('punycode') except Exception as e: raise IDNAException(idna_exception=e) else: label = maybe_decode(label) - return _escapify(label, True) + return _escapify(label) class IDNA2003Codec(IDNACodec): @@ -159,7 +167,7 @@ class IDNA2003Codec(IDNACodec): if label == b'': return u'' try: - return _escapify(encodings.idna.ToUnicode(label), True) + return _escapify(encodings.idna.ToUnicode(label)) except Exception as e: raise IDNAException(idna_exception=e) @@ -197,12 +205,6 @@ class IDNA2008Codec(IDNACodec): self.allow_pure_ascii = allow_pure_ascii self.strict_decode = strict_decode - def is_all_ascii(self, label): - for c in label: - if ord(c) > 0x7f: - return False - return True - def encode(self, label): if label == '': return b'' @@ -227,11 +229,12 @@ class IDNA2008Codec(IDNACodec): try: if self.uts_46: label = idna.uts46_remap(label, False, False) - return _escapify(idna.ulabel(label), True) + return _escapify(idna.ulabel(label)) except idna.IDNAError as e: raise IDNAException(idna_exception=e) _escaped = bytearray(b'"().;\\@$') +_escaped_text = '"().;\\@$' IDNA_2003_Practical = IDNA2003Codec(False) IDNA_2003_Strict = IDNA2003Codec(True) @@ -242,13 +245,13 @@ IDNA_2008_Strict = IDNA2008Codec(False, False, False, True) IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False) IDNA_2008 = IDNA_2008_Practical -def _escapify(label, unicode_mode=False): +def _escapify(label): """Escape the characters in label which need it. - @param unicode_mode: escapify only special and whitespace (<= 0x20) - characters @returns: the escaped string @rtype: string""" - if not unicode_mode: + if isinstance(label, bytes): + # Ordinary DNS label mode. Escape special characters and values + # < 0x20 or > 0x7f. text = '' if isinstance(label, text_type): label = label.encode() @@ -259,19 +262,17 @@ def _escapify(label, unicode_mode=False): text += chr(c) else: text += '\\%03d' % c - return text.encode() + return text + # Unicode label mode. Escape only special characters and values < 0x20 text = u'' - if isinstance(label, binary_type): - label = label.decode() for c in label: - if c > u'\x20' and c < u'\x7f': - text += c + if c in _escaped_text: + text += '\\' + c + elif c <= '\x20': + text += '\\%03d' % ord(c) else: - if c >= u'\x7f': - text += c - else: - text += u'\\%03d' % ord(c) + text += c return text def _validate_labels(labels): @@ -519,8 +520,8 @@ class Name(object): l = self.labels[:-1] else: l = self.labels - s = b'.'.join(map(_escapify, l)) - return maybe_decode(s) + s = '.'.join(map(_escapify, l)) + return s def to_unicode(self, omit_final_dot=False, idna_codec=None): """Convert name to Unicode text format. diff --git a/tests/test_name.py b/tests/test_name.py index f2a8773..fa1d3eb 100644 --- a/tests/test_name.py +++ b/tests/test_name.py @@ -255,6 +255,11 @@ class NameTestCase(unittest.TestCase): t = dns.name.root.to_unicode() self.assertEqual(t, '.') + def testToText12(self): + n = dns.name.from_text(r'a\.b.c') + t = n.to_unicode() + self.assertEqual(t, r'a\.b.c.') + def testSlice1(self): n = dns.name.from_text(r'a.b.c.', origin=None) s = n[:] -- 2.21.0