diff options
Diffstat (limited to 'src/net/java/sip/communicator/impl/protocol/sip/net')
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) + { + } } |