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