From: Bob Halley Date: Tue, 12 Jan 2010 23:14:46 +0000 (-0800) Subject: handle escapes outside of names when reading text format X-Git-Tag: v1.8.0~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f3e9aa06b282570b0fadda772663d50c61c4edea;p=thirdparty%2Fdnspython.git handle escapes outside of names when reading text format --- diff --git a/dns/rdtypes/ANY/CERT.py b/dns/rdtypes/ANY/CERT.py index 225a9210..87d3cb66 100644 --- a/dns/rdtypes/ANY/CERT.py +++ b/dns/rdtypes/ANY/CERT.py @@ -86,7 +86,7 @@ class CERT(dns.rdata.Rdata): raise dns.exception.SyntaxError, "bad algorithm type" chunks = [] while 1: - t = tok.get() + t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): diff --git a/dns/rdtypes/ANY/LOC.py b/dns/rdtypes/ANY/LOC.py index 7fe1a80e..793c5afa 100644 --- a/dns/rdtypes/ANY/LOC.py +++ b/dns/rdtypes/ANY/LOC.py @@ -229,19 +229,19 @@ class LOC(dns.rdata.Rdata): t = t[0 : -1] altitude = float(t) * 100.0 # m -> cm - token = tok.get() + token = tok.get().unescape() if not token.is_eol_or_eof(): value = token.value if value[-1] == 'm': value = value[0 : -1] size = float(value) * 100.0 # m -> cm - token = tok.get() + token = tok.get().unescape() if not token.is_eol_or_eof(): value = token.value if value[-1] == 'm': value = value[0 : -1] hprec = float(value) * 100.0 # m -> cm - token = tok.get() + token = tok.get().unescape() if not token.is_eol_or_eof(): value = token.value if value[-1] == 'm': diff --git a/dns/rdtypes/ANY/NSEC.py b/dns/rdtypes/ANY/NSEC.py index ee9a009c..d548b84e 100644 --- a/dns/rdtypes/ANY/NSEC.py +++ b/dns/rdtypes/ANY/NSEC.py @@ -54,7 +54,7 @@ class NSEC(dns.rdata.Rdata): next = next.choose_relativity(origin, relativize) rdtypes = [] while 1: - token = tok.get() + token = tok.get().unescape() if token.is_eol_or_eof(): break nrdtype = dns.rdatatype.from_text(token.value) diff --git a/dns/rdtypes/ANY/NSEC3.py b/dns/rdtypes/ANY/NSEC3.py index 1d1782ff..8833bc69 100644 --- a/dns/rdtypes/ANY/NSEC3.py +++ b/dns/rdtypes/ANY/NSEC3.py @@ -93,7 +93,7 @@ class NSEC3(dns.rdata.Rdata): next = base64.b32decode(next) rdtypes = [] while 1: - token = tok.get() + token = tok.get().unescape() if token.is_eol_or_eof(): break nrdtype = dns.rdatatype.from_text(token.value) diff --git a/dns/rdtypes/ANY/NXT.py b/dns/rdtypes/ANY/NXT.py index 10443a3b..07a99b71 100644 --- a/dns/rdtypes/ANY/NXT.py +++ b/dns/rdtypes/ANY/NXT.py @@ -53,7 +53,7 @@ class NXT(dns.rdata.Rdata): '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00' ] while 1: - token = tok.get() + token = tok.get().unescape() if token.is_eol_or_eof(): break if token.value.isdigit(): diff --git a/dns/rdtypes/IN/APL.py b/dns/rdtypes/IN/APL.py index 21c02d53..8ac76664 100644 --- a/dns/rdtypes/IN/APL.py +++ b/dns/rdtypes/IN/APL.py @@ -91,7 +91,7 @@ class APL(dns.rdata.Rdata): def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): items = [] while 1: - token = tok.get() + token = tok.get().unescape() if token.is_eol_or_eof(): break item = token.value diff --git a/dns/rdtypes/IN/DHCID.py b/dns/rdtypes/IN/DHCID.py index 934d4784..71bbe74b 100644 --- a/dns/rdtypes/IN/DHCID.py +++ b/dns/rdtypes/IN/DHCID.py @@ -35,7 +35,7 @@ class DHCID(dns.rdata.Rdata): def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): chunks = [] while 1: - t = tok.get() + t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): diff --git a/dns/rdtypes/IN/IPSECKEY.py b/dns/rdtypes/IN/IPSECKEY.py index 15eb546a..2e1bfb37 100644 --- a/dns/rdtypes/IN/IPSECKEY.py +++ b/dns/rdtypes/IN/IPSECKEY.py @@ -86,7 +86,7 @@ class IPSECKEY(dns.rdata.Rdata): gateway = tok.get_string() chunks = [] while 1: - t = tok.get() + t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): @@ -96,7 +96,7 @@ class IPSECKEY(dns.rdata.Rdata): key = b64.decode('base64_codec') return cls(rdclass, rdtype, precedence, gateway_type, algorithm, gateway, key) - + from_text = classmethod(from_text) def to_wire(self, file, compress = None, origin = None): @@ -114,7 +114,7 @@ class IPSECKEY(dns.rdata.Rdata): else: raise ValueError, 'invalid gateway type' file.write(self.key) - + def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): if rdlen < 3: raise dns.exception.FormError @@ -156,5 +156,5 @@ class IPSECKEY(dns.rdata.Rdata): other.to_wire(f) wire2 = f.getvalue() f.close() - + return cmp(wire1, wire2) diff --git a/dns/rdtypes/IN/WKS.py b/dns/rdtypes/IN/WKS.py index 4fdded65..d3270a78 100644 --- a/dns/rdtypes/IN/WKS.py +++ b/dns/rdtypes/IN/WKS.py @@ -60,7 +60,7 @@ class WKS(dns.rdata.Rdata): protocol = socket.getprotobyname(protocol) bitmap = [] while 1: - token = tok.get() + token = tok.get().unescape() if token.is_eol_or_eof(): break if token.value.isdigit(): diff --git a/dns/rdtypes/dsbase.py b/dns/rdtypes/dsbase.py index 09b86cc1..fb604c4f 100644 --- a/dns/rdtypes/dsbase.py +++ b/dns/rdtypes/dsbase.py @@ -53,7 +53,7 @@ class DSBase(dns.rdata.Rdata): digest_type = tok.get_uint8() chunks = [] while 1: - t = tok.get() + t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): diff --git a/dns/rdtypes/keybase.py b/dns/rdtypes/keybase.py index 52114f6a..a9a96fe8 100644 --- a/dns/rdtypes/keybase.py +++ b/dns/rdtypes/keybase.py @@ -112,7 +112,7 @@ class KEYBase(dns.rdata.Rdata): algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) chunks = [] while 1: - t = tok.get() + t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): diff --git a/dns/rdtypes/sigbase.py b/dns/rdtypes/sigbase.py index e035bff3..3430039a 100644 --- a/dns/rdtypes/sigbase.py +++ b/dns/rdtypes/sigbase.py @@ -109,7 +109,7 @@ class SIGBase(dns.rdata.Rdata): signer = signer.choose_relativity(origin, relativize) chunks = [] while 1: - t = tok.get() + t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): diff --git a/dns/rdtypes/txtbase.py b/dns/rdtypes/txtbase.py index b3dd086d..b87d7d6a 100644 --- a/dns/rdtypes/txtbase.py +++ b/dns/rdtypes/txtbase.py @@ -45,7 +45,7 @@ class TXTBase(dns.rdata.Rdata): def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): strings = [] while 1: - token = tok.get() + token = tok.get().unescape() if token.is_eol_or_eof(): break if not (token.is_quoted_string() or token.is_identifier()): diff --git a/dns/tokenizer.py b/dns/tokenizer.py index cc4566ed..8253e043 100644 --- a/dns/tokenizer.py +++ b/dns/tokenizer.py @@ -53,6 +53,8 @@ class Token(object): @type ttype: int @ivar value: The token value @type value: string + @ivar has_escape: Does the token value contain escapes? + @type has_escape: bool """ def __init__(self, ttype, value='', has_escape=False): @@ -108,6 +110,35 @@ class Token(object): def __str__(self): return '%d "%s"' % (self.ttype, self.value) + def unescape(self): + if not self.has_escape: + return self + unescaped = '' + l = len(self.value) + i = 0 + while i < l: + c = self.value[i] + i += 1 + if c == '\\': + if i >= l: + raise dns.exception.UnexpectedEnd + c = self.value[i] + i += 1 + if c.isdigit(): + if i >= l: + raise dns.exception.UnexpectedEnd + c2 = self.value[i] + i += 1 + if i >= l: + raise dns.exception.UnexpectedEnd + c3 = self.value[i] + i += 1 + if not (c2.isdigit() and c3.isdigit()): + raise dns.exception.SyntaxError + c = chr(int(c) * 100 + int(c2) * 10 + int(c3)) + unescaped += c + return Token(self.ttype, unescaped) + class Tokenizer(object): """A DNS master file format tokenizer. @@ -264,6 +295,7 @@ class Tokenizer(object): return Token(WHITESPACE, ' ') token = '' ttype = IDENTIFIER + has_escape = False while True: c = self._get_char() if c == '' or c in self.delimiters: @@ -341,13 +373,14 @@ class Tokenizer(object): raise dns.exception.SyntaxError, 'newline in quoted string' elif c == '\\': # - # Treat \ followed by a delimiter as the - # delimiter, otherwise leave it alone. + # It's an escape. Put it and the next character into + # the token; it will be checked later for goodness. # + token += c + has_escape = True c = self._get_char() - if c == '' or not c in self.delimiters: - self._unget_char(c) - c = '\\' + if c == '' or c == '\n': + raise dns.exception.UnexpectedEnd token += c if token == '' and ttype != QUOTED_STRING: if self.multiline: @@ -393,7 +426,7 @@ class Tokenizer(object): @rtype: int """ - token = self.get() + token = self.get().unescape() if not token.is_identifier(): raise dns.exception.SyntaxError, 'expecting an identifier' if not token.value.isdigit(): @@ -436,7 +469,7 @@ class Tokenizer(object): @rtype: int """ - token = self.get() + token = self.get().unescape() if not token.is_identifier(): raise dns.exception.SyntaxError, 'expecting an identifier' if not token.value.isdigit(): @@ -454,7 +487,7 @@ class Tokenizer(object): @rtype: string """ - token = self.get() + token = self.get().unescape() if not (token.is_identifier() or token.is_quoted_string()): raise dns.exception.SyntaxError, 'expecting a string' return token.value @@ -466,7 +499,7 @@ class Tokenizer(object): @rtype: string """ - token = self.get() + token = self.get().unescape() if not token.is_identifier(): raise dns.exception.SyntaxError, 'expecting an identifier' return token.value @@ -497,7 +530,7 @@ class Tokenizer(object): return token.value def get_ttl(self): - token = self.get() + token = self.get().unescape() if not token.is_identifier(): raise dns.exception.SyntaxError, 'expecting an identifier' return dns.ttl.from_text(token.value) diff --git a/dns/zone.py b/dns/zone.py index 6e1231f1..5f2009b4 100644 --- a/dns/zone.py +++ b/dns/zone.py @@ -654,7 +654,7 @@ class _MasterReader(object): try: while 1: - token = self.tok.get(True, True) + token = self.tok.get(True, True).unescape() if token.is_eof(): if not self.current_file is None: self.current_file.close() diff --git a/tests/tokenizer.py b/tests/tokenizer.py index c5d41ac7..29146eb7 100644 --- a/tests/tokenizer.py +++ b/tests/tokenizer.py @@ -159,12 +159,32 @@ class TokenizerTestCase(unittest.TestCase): def testEscapedDelimiter1(self): tok = dns.tokenizer.Tokenizer(r'ch\ ld') t = tok.get() - self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch ld') + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\ ld') def testEscapedDelimiter2(self): tok = dns.tokenizer.Tokenizer(r'ch\0ld') t = tok.get() self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\0ld') + def testEscapedDelimiter3(self): + tok = dns.tokenizer.Tokenizer(r'ch\032ld') + t = tok.get() + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\032ld') + + def testEscapedDelimiter1u(self): + tok = dns.tokenizer.Tokenizer(r'ch\ ld') + t = tok.get().unescape() + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\ ld') + + def testEscapedDelimiter2u(self): + tok = dns.tokenizer.Tokenizer(r'ch\0ld') + t = tok.get().unescape() + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\0ld') + + def testEscapedDelimiter3u(self): + tok = dns.tokenizer.Tokenizer(r'ch\032ld') + t = tok.get().unescape() + self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\032ld') + if __name__ == '__main__': unittest.main()