aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/protocol/sip/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/java/sip/communicator/impl/protocol/sip/net')
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java226
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java360
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java418
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java6
4 files changed, 508 insertions, 502 deletions
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java
index 5439d99..ca7faa9 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,115 +15,115 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.sip.net;
-
-import static javax.sip.ListeningPoint.PORT_5060;
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PREFERRED_TRANSPORT;
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_ADDRESS;
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_PORT;
-
-import java.net.*;
-import java.text.*;
-
-import net.java.sip.communicator.impl.protocol.sip.*;
-import net.java.sip.communicator.service.dns.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Implementation of the manually configured SIP proxy connection. IP Address
- * lookups are performed using the account's proxy address.
- *
- * @author Ingo Bauersachs
- */
-public class ManualProxyConnection
- extends ProxyConnection
-{
- private final static Logger logger
- = Logger.getLogger(ManualProxyConnection.class);
-
- private String address;
- private int port;
-
- private InetSocketAddress[] lookups;
- private int lookupIndex;
-
- /**
- * Creates a new instance of this class. Uses the server from the account.
- *
- * @param account the account of this SIP protocol instance
- */
- public ManualProxyConnection(SipAccountIDImpl account)
- {
- super(account);
- reset();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#
- * getNextAddress()
- */
- @Override
- public boolean getNextAddressFromDns()
- throws DnssecException
- {
- if(lookups == null)
- {
- try
- {
- lookupIndex = 0;
- lookups = NetworkUtils.getAandAAAARecords(address, port);
-
- //no result found, reset state and indicate "out of addresses"
- if(lookups.length == 0)
- {
- lookups = null;
- return false;
- }
- }
- catch (ParseException e)
- {
- logger.error("Invalid address <" + address + ">", e);
- return false;
- }
- }
-
- //check if the available addresses are exhausted
- if(lookupIndex >= lookups.length)
- {
- if(logger.isDebugEnabled())
- logger.debug("No more addresses for " + account);
- lookups = null;
- return false;
- }
-
- //assign the next address and return lookup success
- if(logger.isDebugEnabled())
- logger.debug("Returning <" + socketAddress
- + "> as next address for " + account);
- socketAddress = lookups[lookupIndex];
- lookupIndex++;
- return true;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#reset()
- */
- @Override
- public void reset()
- {
- super.reset();
- address = account.getAccountPropertyString(PROXY_ADDRESS);
- port = account.getAccountPropertyInt(PROXY_PORT, PORT_5060);
- transport = account.getAccountPropertyString(PREFERRED_TRANSPORT);
-
- //check property sanity
- if(!ProtocolProviderServiceSipImpl.isValidTransport(transport))
- throw new IllegalArgumentException(
- transport + " is not a valid SIP transport");
- }
-}
+package net.java.sip.communicator.impl.protocol.sip.net;
+
+import static javax.sip.ListeningPoint.PORT_5060;
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PREFERRED_TRANSPORT;
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_ADDRESS;
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_PORT;
+
+import java.net.*;
+import java.text.*;
+
+import net.java.sip.communicator.impl.protocol.sip.*;
+import net.java.sip.communicator.service.dns.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implementation of the manually configured SIP proxy connection. IP Address
+ * lookups are performed using the account's proxy address.
+ *
+ * @author Ingo Bauersachs
+ */
+public class ManualProxyConnection
+ extends ProxyConnection
+{
+ private final static Logger logger
+ = Logger.getLogger(ManualProxyConnection.class);
+
+ private String address;
+ private int port;
+
+ private InetSocketAddress[] lookups;
+ private int lookupIndex;
+
+ /**
+ * Creates a new instance of this class. Uses the server from the account.
+ *
+ * @param account the account of this SIP protocol instance
+ */
+ public ManualProxyConnection(SipAccountIDImpl account)
+ {
+ super(account);
+ reset();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#
+ * getNextAddress()
+ */
+ @Override
+ public boolean getNextAddressFromDns()
+ throws DnssecException
+ {
+ if(lookups == null)
+ {
+ try
+ {
+ lookupIndex = 0;
+ lookups = NetworkUtils.getAandAAAARecords(address, port);
+
+ //no result found, reset state and indicate "out of addresses"
+ if(lookups.length == 0)
+ {
+ lookups = null;
+ return false;
+ }
+ }
+ catch (ParseException e)
+ {
+ logger.error("Invalid address <" + address + ">", e);
+ return false;
+ }
+ }
+
+ //check if the available addresses are exhausted
+ if(lookupIndex >= lookups.length)
+ {
+ if(logger.isDebugEnabled())
+ logger.debug("No more addresses for " + account);
+ lookups = null;
+ return false;
+ }
+
+ //assign the next address and return lookup success
+ if(logger.isDebugEnabled())
+ logger.debug("Returning <" + socketAddress
+ + "> as next address for " + account);
+ socketAddress = lookups[lookupIndex];
+ lookupIndex++;
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#reset()
+ */
+ @Override
+ public void reset()
+ {
+ super.reset();
+ address = account.getAccountPropertyString(PROXY_ADDRESS);
+ port = account.getAccountPropertyInt(PROXY_PORT, PORT_5060);
+ transport = account.getAccountPropertyString(PREFERRED_TRANSPORT);
+
+ //check property sanity
+ if(!ProtocolProviderServiceSipImpl.isValidTransport(transport))
+ throw new IllegalArgumentException(
+ transport + " is not a valid SIP transport");
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java
index 99fcfb2..f7079f4 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java
@@ -1,180 +1,180 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package net.java.sip.communicator.impl.protocol.sip.net;
-
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_AUTO_CONFIG;
-
-import java.net.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.sip.*;
-import net.java.sip.communicator.service.dns.*;
-
-/**
- * Abstract class for the determining the address for the SIP proxy.
- *
- * @author Ingo Bauersachs
- */
-public abstract class ProxyConnection
-{
- private List<String> returnedAddresses = new LinkedList<String>();
-
- protected String transport;
- protected InetSocketAddress socketAddress;
- protected final SipAccountIDImpl account;
-
- /**
- * Creates a new instance of this class.
- * @param account the account of this SIP protocol instance
- */
- protected ProxyConnection(SipAccountIDImpl account)
- {
- this.account = account;
- }
-
- /**
- * Gets the address to use for the next connection attempt.
- * @return the address of the last lookup.
- */
- public final InetSocketAddress getAddress()
- {
- return socketAddress;
- }
-
- /**
- * Gets the transport to use for the next connection attempt.
- * @return the transport of the last lookup.
- */
- public final String getTransport()
- {
- return transport;
- }
-
- /**
- * In case we are using an outbound proxy this method returns
- * a suitable string for use with Router.
- * The method returns <tt>null</tt> otherwise.
- *
- * @return the string of our outbound proxy if we are using one and
- * <tt>null</tt> otherwise.
- */
- public final String getOutboundProxyString()
- {
- if(socketAddress == null)
- return null;
-
- InetAddress proxyAddress = socketAddress.getAddress();
- StringBuilder proxyStringBuffer
- = new StringBuilder(proxyAddress.getHostAddress());
-
- if(proxyAddress instanceof Inet6Address)
- {
- proxyStringBuffer.insert(0, '[');
- proxyStringBuffer.append(']');
- }
-
- proxyStringBuffer.append(':');
- proxyStringBuffer.append(socketAddress.getPort());
- proxyStringBuffer.append('/');
- proxyStringBuffer.append(transport);
-
- return proxyStringBuffer.toString();
- }
-
- /**
- * Compares an InetAddress against the active outbound proxy. The comparison
- * is by reference, not equals.
- *
- * @param addressToTest The addres to test.
- * @return True when the InetAddress is the same as the outbound proxy.
- */
- public final boolean isSameInetAddress(InetAddress addressToTest)
- {
- // if the proxy is not yet initialized then this is not the provider
- // that caused this comparison
- if(socketAddress == null)
- return false;
- return addressToTest == socketAddress.getAddress();
- }
-
- /**
- * Retrieves the next address to use from DNS. Duplicate results are
- * suppressed.
- *
- * @return True if a new address is available through {@link #getAddress()},
- * false if the last address was reached. A new lookup from scratch
- * can be started by calling {@link #reset()}.
- * @throws DnssecException if there is a problem related to DNSSEC
- */
- public final boolean getNextAddress() throws DnssecException
- {
- boolean result;
- String key = null;
- do
- {
- result = getNextAddressFromDns();
- if(result && socketAddress != null)
- {
- key = getOutboundProxyString();
- if(!returnedAddresses.contains(key))
- {
- returnedAddresses.add(key);
- break;
- }
- }
- }
- while(result && returnedAddresses.contains(key));
- return result;
- }
-
- /**
- * Implementations must use this method to get the next address, but do not
- * have to care about duplicate addresses.
- *
- * @return True when a further address was available.
- * @throws DnssecException when a DNSSEC validation failure occured.
- */
- protected abstract boolean getNextAddressFromDns()
- throws DnssecException;
-
- /**
- * Resets the lookup to it's initial state. Overriders methods have to call
- * this method through a super-call.
- */
- public void reset()
- {
- returnedAddresses.clear();
- }
-
- /**
- * Factory method to create a proxy connection based on the account settings
- * of the protocol provider.
- *
- * @param pps the protocol provider that needs a SIP server connection.
- * @return An instance of a derived class.
- */
- public static ProxyConnection create(ProtocolProviderServiceSipImpl pps)
- {
- if (pps.getAccountID().getAccountPropertyBoolean(PROXY_AUTO_CONFIG,
- true))
- return new AutoProxyConnection((SipAccountIDImpl) pps.getAccountID(),
- pps.getDefaultTransport());
- else
- return new ManualProxyConnection((SipAccountIDImpl) pps.getAccountID());
- }
-}
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Copyright @ 2015 Atlassian Pty Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.java.sip.communicator.impl.protocol.sip.net;
+
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_AUTO_CONFIG;
+
+import java.net.*;
+import java.util.*;
+
+import net.java.sip.communicator.impl.protocol.sip.*;
+import net.java.sip.communicator.service.dns.*;
+
+/**
+ * Abstract class for the determining the address for the SIP proxy.
+ *
+ * @author Ingo Bauersachs
+ */
+public abstract class ProxyConnection
+{
+ private List<String> returnedAddresses = new LinkedList<String>();
+
+ protected String transport;
+ protected InetSocketAddress socketAddress;
+ protected final SipAccountIDImpl account;
+
+ /**
+ * Creates a new instance of this class.
+ * @param account the account of this SIP protocol instance
+ */
+ protected ProxyConnection(SipAccountIDImpl account)
+ {
+ this.account = account;
+ }
+
+ /**
+ * Gets the address to use for the next connection attempt.
+ * @return the address of the last lookup.
+ */
+ public final InetSocketAddress getAddress()
+ {
+ return socketAddress;
+ }
+
+ /**
+ * Gets the transport to use for the next connection attempt.
+ * @return the transport of the last lookup.
+ */
+ public final String getTransport()
+ {
+ return transport;
+ }
+
+ /**
+ * In case we are using an outbound proxy this method returns
+ * a suitable string for use with Router.
+ * The method returns <tt>null</tt> otherwise.
+ *
+ * @return the string of our outbound proxy if we are using one and
+ * <tt>null</tt> otherwise.
+ */
+ public final String getOutboundProxyString()
+ {
+ if(socketAddress == null)
+ return null;
+
+ InetAddress proxyAddress = socketAddress.getAddress();
+ StringBuilder proxyStringBuffer
+ = new StringBuilder(proxyAddress.getHostAddress());
+
+ if(proxyAddress instanceof Inet6Address)
+ {
+ proxyStringBuffer.insert(0, '[');
+ proxyStringBuffer.append(']');
+ }
+
+ proxyStringBuffer.append(':');
+ proxyStringBuffer.append(socketAddress.getPort());
+ proxyStringBuffer.append('/');
+ proxyStringBuffer.append(transport);
+
+ return proxyStringBuffer.toString();
+ }
+
+ /**
+ * Compares an InetAddress against the active outbound proxy. The comparison
+ * is by reference, not equals.
+ *
+ * @param addressToTest The addres to test.
+ * @return True when the InetAddress is the same as the outbound proxy.
+ */
+ public final boolean isSameInetAddress(InetAddress addressToTest)
+ {
+ // if the proxy is not yet initialized then this is not the provider
+ // that caused this comparison
+ if(socketAddress == null)
+ return false;
+ return addressToTest == socketAddress.getAddress();
+ }
+
+ /**
+ * Retrieves the next address to use from DNS. Duplicate results are
+ * suppressed.
+ *
+ * @return True if a new address is available through {@link #getAddress()},
+ * false if the last address was reached. A new lookup from scratch
+ * can be started by calling {@link #reset()}.
+ * @throws DnssecException if there is a problem related to DNSSEC
+ */
+ public final boolean getNextAddress() throws DnssecException
+ {
+ boolean result;
+ String key = null;
+ do
+ {
+ result = getNextAddressFromDns();
+ if(result && socketAddress != null)
+ {
+ key = getOutboundProxyString();
+ if(!returnedAddresses.contains(key))
+ {
+ returnedAddresses.add(key);
+ break;
+ }
+ }
+ }
+ while(result && returnedAddresses.contains(key));
+ return result;
+ }
+
+ /**
+ * Implementations must use this method to get the next address, but do not
+ * have to care about duplicate addresses.
+ *
+ * @return True when a further address was available.
+ * @throws DnssecException when a DNSSEC validation failure occured.
+ */
+ protected abstract boolean getNextAddressFromDns()
+ throws DnssecException;
+
+ /**
+ * Resets the lookup to it's initial state. Overriders methods have to call
+ * this method through a super-call.
+ */
+ public void reset()
+ {
+ returnedAddresses.clear();
+ }
+
+ /**
+ * Factory method to create a proxy connection based on the account settings
+ * of the protocol provider.
+ *
+ * @param pps the protocol provider that needs a SIP server connection.
+ * @return An instance of a derived class.
+ */
+ public static ProxyConnection create(ProtocolProviderServiceSipImpl pps)
+ {
+ if (pps.getAccountID().getAccountPropertyBoolean(PROXY_AUTO_CONFIG,
+ true))
+ return new AutoProxyConnection((SipAccountIDImpl) pps.getAccountID(),
+ pps.getDefaultTransport());
+ else
+ return new ManualProxyConnection((SipAccountIDImpl) pps.getAccountID());
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java b/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java
index 0027ec5..262b717 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,211 +15,211 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.sip.net;
-
-import java.security.cert.*;
-import java.text.*;
-import java.util.*;
-import java.util.regex.*;
-
-import javax.sip.address.*;
-
-import net.java.sip.communicator.impl.protocol.sip.*;
-import net.java.sip.communicator.service.certificate.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Matcher that extracts certificate identities according to <a
- * href="http://tools.ietf.org/html/rfc5922#section-7.1">RFC5922, Section
- * 7.1</a> and compares them with the rules from Section 7.2 and 7.3.
- * @see #PNAME_STRICT_RFC5922 for wildcard handling; the default is false
- *
- * @author Ingo Bauersachs
- */
-public class RFC5922Matcher
- implements CertificateMatcher
-{
- /**
- * When set to true, enables strict validation of the hostname according to
- * <a href="http://tools.ietf.org/html/rfc5922#section-7.2">RFC5922 Section
- * 7.2</a>
- */
- public final static String PNAME_STRICT_RFC5922 =
- "net.java.sip.communicator.sip.tls.STRICT_RFC5922";
-
- private ProtocolProviderServiceSipImpl provider;
-
- /**
- * Creates a new instance of this class.
- * @param provider The SIP Provider to which this matcher belongs.
- */
- public RFC5922Matcher(ProtocolProviderServiceSipImpl provider)
- {
- this.provider = provider;
- }
-
- /** Our class logger. */
- private static final Logger logger = Logger
- .getLogger(CertificateMatcher.class);
-
- /*
- * (non-Javadoc)
- *
- * @see
- * net.java.sip.communicator.service.certificate.CertificateMatcher#verify
- * (java.lang.Iterable, java.security.cert.X509Certificate)
- */
- public void verify(Iterable<String> identitiesToTest, X509Certificate cert)
- throws CertificateException
- {
- boolean strict = SipActivator.getConfigurationService()
- .getBoolean(PNAME_STRICT_RFC5922, false);
-
- // if any of the identities is contained in the certificate we're good
- boolean oneMatched = false;
- Iterable<String> certIdentities = extractCertIdentities(cert);
- for (String identity : identitiesToTest)
- {
- // check if the intended hostname is contained in one of the
- // hostnames of the certificate according to
- // http://tools.ietf.org/html/rfc5922#section-7.2
- for(String dnsName : certIdentities)
- {
- try
- {
- if(NetworkUtils.compareDnsNames(dnsName, identity) == 0)
- {
- // one of the hostnames matched, we're good to go
- return;
- }
-
- if(!strict
- // is a wildcard name
- && dnsName.startsWith("*.")
- // contains at least two dots (*.example.com)
- && identity.indexOf(".") < identity.lastIndexOf(".")
- // compare *.example.com stripped to example.com with
- // - foo.example.com stripped to example.com
- // - foo.bar.example.com to bar.example.com
- && NetworkUtils.compareDnsNames(
- dnsName.substring(2),
- identity.substring(identity.indexOf(".")+1)) == 0)
- {
- // the wildcard matched, we're good to go
- return;
- }
- }
- catch (ParseException e)
- {} // we don't care - this hostname did not match
- }
- }
- if (!oneMatched)
- throw new CertificateException("None of <" + identitiesToTest
- + "> matched by the rules of RFC5922 to the cert with CN="
- + cert.getSubjectDN());
- }
-
- private Iterable<String> extractCertIdentities(X509Certificate cert)
- {
- List<String> certIdentities = new ArrayList<String>();
- Collection<List<?>> subjAltNames = null;
- try
- {
- subjAltNames = cert.getSubjectAlternativeNames();
- }
- catch (CertificateParsingException ex)
- {
- logger.error("Error parsing TLS certificate", ex);
- }
- // subjAltName types are defined in rfc2459
- final Integer dnsNameType = 2;
- final Integer uriNameType = 6;
- if (subjAltNames != null)
- {
- if (logger.isDebugEnabled())
- logger.debug("found subjAltNames: " + subjAltNames);
-
- // First look for a URI in the subjectAltName field
- for (List<?> altName : subjAltNames)
- {
- // 0th position is the alt name type
- // 1st position is the alt name data
- if (altName.get(0).equals(uriNameType))
- {
- SipURI altNameUri;
- try
- {
- altNameUri =
- provider.getAddressFactory().createSipURI(
- (String) altName.get(1));
- // only sip URIs are allowed
- if (!"sip".equals(altNameUri.getScheme()))
- continue;
- // user certificates are not allowed
- if (altNameUri.getUser() != null)
- continue;
- String altHostName = altNameUri.getHost();
- if (logger.isDebugEnabled())
- {
- logger.debug("found uri " + altName.get(1)
- + ", hostName " + altHostName);
- }
- certIdentities.add(altHostName);
- }
- catch (ParseException e)
- {
- logger.error("certificate contains invalid uri: "
- + altName.get(1));
- }
- }
-
- }
- // DNS An implementation MUST accept a domain name system
- // identifier as a SIP domain identity if and only if no other
- // identity is found that matches the "sip" URI type described
- // above.
- if (certIdentities.isEmpty())
- {
- for (List<?> altName : subjAltNames)
- {
- if (altName.get(0).equals(dnsNameType))
- {
- if (logger.isDebugEnabled())
- logger.debug("found dns " + altName.get(1));
- certIdentities.add(altName.get(1).toString());
- }
- }
- }
- }
- else
- {
- // If and only if the subjectAltName does not appear in the
- // certificate, the implementation MAY examine the CN field of the
- // certificate. If a valid DNS name is found there, the
- // implementation MAY accept this value as a SIP domain identity.
- String dname = cert.getSubjectDN().getName();
- String cname = "";
- try
- {
- Pattern EXTRACT_CN =
- Pattern.compile(".*CN\\s*=\\s*([\\w*\\.]+).*");
- Matcher matcher = EXTRACT_CN.matcher(dname);
- if (matcher.matches())
- {
- cname = matcher.group(1);
- if (logger.isDebugEnabled())
- {
- logger.debug("found CN: " + cname + " from DN: "
- + dname);
- }
- certIdentities.add(cname);
- }
- }
- catch (Exception ex)
- {
- logger.error("exception while extracting CN", ex);
- }
- }
- return certIdentities;
- }
-}
+package net.java.sip.communicator.impl.protocol.sip.net;
+
+import java.security.cert.*;
+import java.text.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.sip.address.*;
+
+import net.java.sip.communicator.impl.protocol.sip.*;
+import net.java.sip.communicator.service.certificate.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Matcher that extracts certificate identities according to <a
+ * href="http://tools.ietf.org/html/rfc5922#section-7.1">RFC5922, Section
+ * 7.1</a> and compares them with the rules from Section 7.2 and 7.3.
+ * @see #PNAME_STRICT_RFC5922 for wildcard handling; the default is false
+ *
+ * @author Ingo Bauersachs
+ */
+public class RFC5922Matcher
+ implements CertificateMatcher
+{
+ /**
+ * When set to true, enables strict validation of the hostname according to
+ * <a href="http://tools.ietf.org/html/rfc5922#section-7.2">RFC5922 Section
+ * 7.2</a>
+ */
+ public final static String PNAME_STRICT_RFC5922 =
+ "net.java.sip.communicator.sip.tls.STRICT_RFC5922";
+
+ private ProtocolProviderServiceSipImpl provider;
+
+ /**
+ * Creates a new instance of this class.
+ * @param provider The SIP Provider to which this matcher belongs.
+ */
+ public RFC5922Matcher(ProtocolProviderServiceSipImpl provider)
+ {
+ this.provider = provider;
+ }
+
+ /** Our class logger. */
+ private static final Logger logger = Logger
+ .getLogger(CertificateMatcher.class);
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * net.java.sip.communicator.service.certificate.CertificateMatcher#verify
+ * (java.lang.Iterable, java.security.cert.X509Certificate)
+ */
+ public void verify(Iterable<String> identitiesToTest, X509Certificate cert)
+ throws CertificateException
+ {
+ boolean strict = SipActivator.getConfigurationService()
+ .getBoolean(PNAME_STRICT_RFC5922, false);
+
+ // if any of the identities is contained in the certificate we're good
+ boolean oneMatched = false;
+ Iterable<String> certIdentities = extractCertIdentities(cert);
+ for (String identity : identitiesToTest)
+ {
+ // check if the intended hostname is contained in one of the
+ // hostnames of the certificate according to
+ // http://tools.ietf.org/html/rfc5922#section-7.2
+ for(String dnsName : certIdentities)
+ {
+ try
+ {
+ if(NetworkUtils.compareDnsNames(dnsName, identity) == 0)
+ {
+ // one of the hostnames matched, we're good to go
+ return;
+ }
+
+ if(!strict
+ // is a wildcard name
+ && dnsName.startsWith("*.")
+ // contains at least two dots (*.example.com)
+ && identity.indexOf(".") < identity.lastIndexOf(".")
+ // compare *.example.com stripped to example.com with
+ // - foo.example.com stripped to example.com
+ // - foo.bar.example.com to bar.example.com
+ && NetworkUtils.compareDnsNames(
+ dnsName.substring(2),
+ identity.substring(identity.indexOf(".")+1)) == 0)
+ {
+ // the wildcard matched, we're good to go
+ return;
+ }
+ }
+ catch (ParseException e)
+ {} // we don't care - this hostname did not match
+ }
+ }
+ if (!oneMatched)
+ throw new CertificateException("None of <" + identitiesToTest
+ + "> matched by the rules of RFC5922 to the cert with CN="
+ + cert.getSubjectDN());
+ }
+
+ private Iterable<String> extractCertIdentities(X509Certificate cert)
+ {
+ List<String> certIdentities = new ArrayList<String>();
+ Collection<List<?>> subjAltNames = null;
+ try
+ {
+ subjAltNames = cert.getSubjectAlternativeNames();
+ }
+ catch (CertificateParsingException ex)
+ {
+ logger.error("Error parsing TLS certificate", ex);
+ }
+ // subjAltName types are defined in rfc2459
+ final Integer dnsNameType = 2;
+ final Integer uriNameType = 6;
+ if (subjAltNames != null)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("found subjAltNames: " + subjAltNames);
+
+ // First look for a URI in the subjectAltName field
+ for (List<?> altName : subjAltNames)
+ {
+ // 0th position is the alt name type
+ // 1st position is the alt name data
+ if (altName.get(0).equals(uriNameType))
+ {
+ SipURI altNameUri;
+ try
+ {
+ altNameUri =
+ provider.getAddressFactory().createSipURI(
+ (String) altName.get(1));
+ // only sip URIs are allowed
+ if (!"sip".equals(altNameUri.getScheme()))
+ continue;
+ // user certificates are not allowed
+ if (altNameUri.getUser() != null)
+ continue;
+ String altHostName = altNameUri.getHost();
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("found uri " + altName.get(1)
+ + ", hostName " + altHostName);
+ }
+ certIdentities.add(altHostName);
+ }
+ catch (ParseException e)
+ {
+ logger.error("certificate contains invalid uri: "
+ + altName.get(1));
+ }
+ }
+
+ }
+ // DNS An implementation MUST accept a domain name system
+ // identifier as a SIP domain identity if and only if no other
+ // identity is found that matches the "sip" URI type described
+ // above.
+ if (certIdentities.isEmpty())
+ {
+ for (List<?> altName : subjAltNames)
+ {
+ if (altName.get(0).equals(dnsNameType))
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("found dns " + altName.get(1));
+ certIdentities.add(altName.get(1).toString());
+ }
+ }
+ }
+ }
+ else
+ {
+ // If and only if the subjectAltName does not appear in the
+ // certificate, the implementation MAY examine the CN field of the
+ // certificate. If a valid DNS name is found there, the
+ // implementation MAY accept this value as a SIP domain identity.
+ String dname = cert.getSubjectDN().getName();
+ String cname = "";
+ try
+ {
+ Pattern EXTRACT_CN =
+ Pattern.compile(".*CN\\s*=\\s*([\\w*\\.]+).*");
+ Matcher matcher = EXTRACT_CN.matcher(dname);
+ if (matcher.matches())
+ {
+ cname = matcher.group(1);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("found CN: " + cname + " from DN: "
+ + dname);
+ }
+ certIdentities.add(cname);
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.error("exception while extracting CN", ex);
+ }
+ }
+ return certIdentities;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java b/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java
index f1c5856..9d088a2 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java
@@ -18,6 +18,7 @@
package net.java.sip.communicator.impl.protocol.sip.net;
import gov.nist.core.net.*;
+import gov.nist.javax.sip.*;
import java.io.*;
import java.net.*;
@@ -421,4 +422,9 @@ public class SslNetworkLayer
return 0;
}
+
+ @Override
+ public void setSipStack(SipStackImpl sipStack)
+ {
+ }
}