You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
5.1 KiB
162 lines
5.1 KiB
2 years ago
|
From c28f0584ba37cd6b0e9919dcbec652a34a420843 Mon Sep 17 00:00:00 2001
|
||
|
From: Lumir Balhar <lbalhar@redhat.com>
|
||
|
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
|
||
|
|