diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index fe82c07f6..d65fb0b24 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -294,6 +294,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { Thread thread = new Thread(runnable); thread.setName("Smack Cached Executor"); thread.setDaemon(true); + thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread t, Throwable e) { + LOGGER.log(Level.WARNING, t + " encountered uncaught exception", e); + } + }); return thread; } }); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index 7115a2bc3..d14a82d46 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2017-2018 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2017-2019 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.net.SocketFactory; import javax.net.ssl.HostnameVerifier; @@ -46,6 +48,7 @@ import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.stringprep.XmppStringprepException; import org.minidns.dnsname.DnsName; +import org.minidns.dnsname.InvalidDnsNameException; import org.minidns.util.InetAddressUtil; /** @@ -61,6 +64,8 @@ public abstract class ConnectionConfiguration { SmackConfiguration.getVersion(); } + private static final Logger LOGGER = Logger.getLogger(ConnectionConfiguration.class.getName()); + /** * The XMPP domain of the XMPP Service. Usually servers use the same service name as the name * of the server. However, there are some servers like google where host would be @@ -68,6 +73,8 @@ public abstract class ConnectionConfiguration { */ protected final DomainBareJid xmppServiceDomain; + protected final DnsName xmppServiceDomainDnsName; + protected final InetAddress hostAddress; protected final DnsName host; protected final int port; @@ -140,6 +147,19 @@ public abstract class ConnectionConfiguration { if (xmppServiceDomain == null) { throw new IllegalArgumentException("Must define the XMPP domain"); } + + DnsName xmppServiceDomainDnsName; + try { + xmppServiceDomainDnsName = DnsName.from(xmppServiceDomain); + } catch (InvalidDnsNameException e) { + LOGGER.log(Level.INFO, + "Could not transform XMPP service domain '" + xmppServiceDomain + + "' to a DNS name. TLS X.509 certificate validiation may not be possible.", + e); + xmppServiceDomainDnsName = null; + } + this.xmppServiceDomainDnsName = xmppServiceDomainDnsName; + hostAddress = builder.hostAddress; host = builder.host; port = builder.port; @@ -201,6 +221,17 @@ public abstract class ConnectionConfiguration { return xmppServiceDomain; } + /** + * Returns the XMPP service domain as DNS name if possible. Note that since not every XMPP address domainpart is a + * valid DNS name, this method may return null. + * + * @return the XMPP service domain as DNS name or null. + * @since 4.3.4 + */ + public DnsName getXmppServiceDomainAsDnsNameIfPossible() { + return xmppServiceDomainDnsName; + } + /** * Returns the TLS security mode used when making the connection. By default, * the mode is {@link SecurityMode#ifpossible}. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/XHTMLText.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/XHTMLText.java index b08507ad0..2ef66949f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/XHTMLText.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/XHTMLText.java @@ -105,7 +105,7 @@ public class XHTMLText { private XHTMLText appendOpenBodyTag(String style, String lang) { text.halfOpenElement(Message.BODY); text.xmlnsAttribute(NAMESPACE); - text.optElement(STYLE, style); + text.optAttribute(STYLE, style); text.xmllangAttribute(lang); text.rightAngleBracket(); return this; diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index a08221ddb..0329ef4b1 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -141,6 +141,7 @@ import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.stringprep.XmppStringprepException; import org.jxmpp.util.XmppStringUtils; +import org.minidns.dnsname.DnsName; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -861,8 +862,27 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { final HostnameVerifier verifier = getConfiguration().getHostnameVerifier(); if (verifier == null) { throw new IllegalStateException("No HostnameVerifier set. Use connectionConfiguration.setHostnameVerifier() to configure."); - } else if (!verifier.verify(getXMPPServiceDomain().toString(), sslSocket.getSession())) { - throw new CertificateException("Hostname verification of certificate failed. Certificate does not authenticate " + getXMPPServiceDomain()); + } + + final String verifierHostname; + { + DnsName xmppServiceDomainDnsName = getConfiguration().getXmppServiceDomainAsDnsNameIfPossible(); + // Try to convert the XMPP service domain, which potentially includes Unicode characters, into ASCII + // Compatible Encoding (ACE) to match RFC3280 dNSname IA5String constraint. + // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=280839#c1 + if (xmppServiceDomainDnsName != null) { + verifierHostname = xmppServiceDomainDnsName.ace; + } + else { + LOGGER.log(Level.WARNING, "XMPP service domain name '" + getXMPPServiceDomain() + + "' can not be represented as DNS name. TLS X.509 certificate validiation may fail."); + verifierHostname = getXMPPServiceDomain().toString(); + } + } + if (!verifier.verify(verifierHostname, sslSocket.getSession())) { + throw new CertificateException( + "Hostname verification of certificate failed. Certificate does not authenticate " + + getXMPPServiceDomain()); } // Set that TLS was successful