diff --git a/SOURCES/fix_unicode_label_escaping.patch b/SOURCES/fix_unicode_label_escaping.patch new file mode 100644 index 0000000..6fa427d --- /dev/null +++ b/SOURCES/fix_unicode_label_escaping.patch @@ -0,0 +1,161 @@ +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 + diff --git a/SPECS/python-dns.spec b/SPECS/python-dns.spec index 5590b6c..18cf813 100644 --- a/SPECS/python-dns.spec +++ b/SPECS/python-dns.spec @@ -1,6 +1,6 @@ Name: python-dns Version: 1.15.0 -Release: 8%{?dist} +Release: 10%{?dist} Summary: DNS toolkit for Python License: MIT @@ -10,6 +10,9 @@ Source0: http://www.dnspython.org/kits/%{version}/dnspython-%{version}.tar.gz BuildArch: noarch Patch0: test_fails_on_missing_file.patch +# Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1731081 +# Upstream issue: https://github.com/rthalley/dnspython/issues/339 +Patch1: fix_unicode_label_escaping.patch BuildRequires: python3-devel BuildRequires: python3-setuptools @@ -68,7 +71,15 @@ find examples -type f | xargs chmod a-x %changelog -* Mon Aug 27 2018 Miro Hrončok +* Thu Oct 24 2019 Lumír Balhar - 1.15.0-10 +- Release bump for gating +Related: rhbz#1731081 + +* Wed Aug 28 2019 Lumír Balhar - 1.15.0-9 +- Fix unicode label escaping +Resolves: rhbz#1731081 + +* Mon Aug 27 2018 Miro Hrončok - 1.15.0-8 - Drop python2 subpackage (#1567168) * Mon Jun 25 2018 Petr Viktorin - 1.15.0-7