Changeset 178:37f36ea93838 for wokkel


Ignore:
Timestamp:
Jan 12, 2013, 4:40:38 PM (7 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Message:

Properly encode IDN domain names for SRV lookups.

Before Twisted 12.3.0, the SRV lookup done for outgoing
connections succeeded if passed a unicode string with all-ASCII
code points. A recent change made the DNS code more strict, and
only byte string are accepted as domain name. See
http://twistedmatrix.com/trac/ticket/6245 for details.

This change makes sure domain names are encoded to their ASCII
Compatible Equivalent (ACE) version before passing the resulting
byte string to twisted.names.srvconnect.SRVConnector, as per
RFC 3490.

Note that while connecting to servers with an IDN domain name
now works properly, authentication using MD5-Digest SASL mechanism
will fail until http://twistedmatrix.com/trac/ticket/5066 has been
resolved.

Fixes: #77.

Location:
wokkel
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • wokkel/client.py

    r96 r178  
    8888    def __init__(self, jid, password, host=None, port=5222):
    8989        self.jid = jid
    90         self.domain = jid.host
     90        self.domain = generic.prepareIDNName(jid.host)
    9191        self.host = host
    9292        self.port = port
     
    183183
    184184def clientCreator(factory):
    185     domain = factory.authenticator.jid.host
     185    domain = generic.prepareIDNName(factory.authenticator.jid.host)
    186186    c = XMPPClientConnector(reactor, domain, factory)
    187187    c.connect()
  • wokkel/generic.py

    r171 r178  
    77Generic XMPP protocol helpers.
    88"""
     9
     10from encodings import idna
    911
    1012from zope.interface import implements
     
    328330    def clientConnectionFailed(self, connector, reason):
    329331        self.deferred.errback(reason)
     332
     333
     334
     335def prepareIDNName(name):
     336    """
     337    Encode a unicode IDN Domain Name into its ACE equivalent.
     338
     339    This will encode the domain labels, separated by allowed dot code points,
     340    to their ASCII Compatible Encoding (ACE) equivalent, using punycode. The
     341    result is an ASCII byte string of the encoded labels, separated by the
     342    standard full stop.
     343    """
     344    result = []
     345    labels = idna.dots.split(name)
     346
     347    if labels and len(labels[-1]) == 0:
     348        trailing_dot = b'.'
     349        del labels[-1]
     350    else:
     351        trailing_dot = b''
     352
     353    for label in labels:
     354        result.append(idna.ToASCII(label))
     355
     356    return b'.'.join(result) + trailing_dot
  • wokkel/server.py

    r165 r178  
    3030from twisted.words.xish import domish
    3131
    32 from wokkel.generic import DeferredXmlStreamFactory, XmlPipe
     32from wokkel.generic import DeferredXmlStreamFactory, XmlPipe, prepareIDNName
    3333
    3434NS_DIALBACK = 'jabber:server:dialback'
     
    469469
    470470def initiateS2S(factory):
    471     domain = factory.authenticator.otherHost
     471    domain = prepareIDNName(factory.authenticator.otherHost)
    472472    c = XMPPServerConnector(reactor, domain, factory)
    473473    c.connect()
  • wokkel/test/test_client.py

    r165 r178  
    4141        xs.dispatch(xs, xmlstream.STREAM_AUTHD_EVENT)
    4242        self.assertEquals(JID('user@example.org/test'), self.client.jid)
     43
     44
     45    def test_domain(self):
     46        """
     47        The domain to connect to is a byte string derived from the JID host.
     48        """
     49        self.assertIsInstance(self.client.domain, bytes)
     50        self.assertEqual(b'example.org', self.client.domain)
    4351
    4452
     
    143151        def cb(connector):
    144152            self.assertEqual('xmpp-client', connector.service)
    145             self.assertEqual('example.org', connector.domain)
     153            self.assertIsInstance(connector.domain, bytes)
     154            self.assertEqual(b'example.org', connector.domain)
    146155            self.assertEqual(factory, connector.factory)
    147156
  • wokkel/test/test_generic.py

    r171 r178  
    269269        """
    270270        self.assertIdentical(None, self.request.timeout)
     271
     272
     273
     274class PrepareIDNNameTests(unittest.TestCase):
     275    """
     276    Tests for L{wokkel.generic.prepareIDNName}.
     277    """
     278
     279    def test_bytestring(self):
     280        """
     281        An ASCII-encoded byte string is left as-is.
     282        """
     283        name = b"example.com"
     284        result = generic.prepareIDNName(name)
     285        self.assertEqual(b"example.com", result)
     286
     287
     288    def test_unicode(self):
     289        """
     290        A unicode all-ASCII name is converted to an ASCII byte string.
     291        """
     292        name = u"example.com"
     293        result = generic.prepareIDNName(name)
     294        self.assertEqual(b"example.com", result)
     295
     296
     297    def test_unicodeNonASCII(self):
     298        """
     299        A unicode with non-ASCII is converted to its ACE equivalent.
     300        """
     301        name = u"\u00e9chec.example.com"
     302        result = generic.prepareIDNName(name)
     303        self.assertEqual(b"xn--chec-9oa.example.com", result)
     304
     305
     306    def test_unicodeHalfwidthIdeographicFullStop(self):
     307        """
     308        Exotic dots in unicode names are converted to Full Stop.
     309        """
     310        name = u"\u00e9chec.example\uff61com"
     311        result = generic.prepareIDNName(name)
     312        self.assertEqual(b"xn--chec-9oa.example.com", result)
     313
     314
     315    def test_unicodeTrailingDot(self):
     316        """
     317        Unicode names with trailing dots retain the trailing dot.
     318
     319        L{encodings.idna.ToASCII} doesn't allow the empty string as the input,
     320        hence the implementation needs to strip a trailing dot, and re-add it
     321        after encoding the labels.
     322        """
     323        name = u"example.com."
     324        result = generic.prepareIDNName(name)
     325        self.assertEqual(b"example.com.", result)
Note: See TracChangeset for help on using the changeset viewer.