diff options
16 files changed, 1173 insertions, 758 deletions
diff --git a/lib/felix.unit.test.properties b/lib/felix.unit.test.properties index fabf3dc..4ff1686 100644 --- a/lib/felix.unit.test.properties +++ b/lib/felix.unit.test.properties @@ -28,7 +28,8 @@ org.osgi.framework.system.packages.extra= \ sun.net.util; \ sun.net.dns; \ sun.security.action; \ - sun.security.pkcs11; + sun.security.pkcs11; \ + sun.reflect; # @@ -41,7 +42,10 @@ org.osgi.framework.system.packages.extra= \ # felix.auto.start.10= \ - file:lib/bundle/junit.jar + reference:file:lib/bundle/junit.jar \ + reference:file:lib/installer-exclude/easymock-3.1.jar \ + reference:file:lib/installer-exclude/objenesis-1.2.jar \ + reference:file:lib/installer-exclude/cglib-nodep.osgi-2.1_3.jar # file:lib/bundle/shell.jar \ # file:lib/bundle/bundlerepository.jar \ # file:lib/bundle/servicebinder.jar \ diff --git a/lib/installer-exclude/cglib-nodep.osgi-2.1_3.jar b/lib/installer-exclude/cglib-nodep.osgi-2.1_3.jar Binary files differnew file mode 100644 index 0000000..3064d35 --- /dev/null +++ b/lib/installer-exclude/cglib-nodep.osgi-2.1_3.jar diff --git a/lib/installer-exclude/easymock-3.1.jar b/lib/installer-exclude/easymock-3.1.jar Binary files differnew file mode 100644 index 0000000..d8232ab --- /dev/null +++ b/lib/installer-exclude/easymock-3.1.jar diff --git a/lib/installer-exclude/objenesis-1.2.jar b/lib/installer-exclude/objenesis-1.2.jar Binary files differnew file mode 100644 index 0000000..759b623 --- /dev/null +++ b/lib/installer-exclude/objenesis-1.2.jar diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java index 1c782e5..6cff570 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java @@ -9,6 +9,7 @@ package net.java.sip.communicator.impl.protocol.sip; import gov.nist.javax.sip.address.*; import gov.nist.javax.sip.header.*; import gov.nist.javax.sip.message.*; +import net.java.sip.communicator.impl.protocol.sip.net.*; import net.java.sip.communicator.impl.protocol.sip.security.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; @@ -155,31 +156,9 @@ public class ProtocolProviderServiceSipImpl private SipSecurityManager sipSecurityManager = null; /** - * The address and port of an outbound proxy if we have one (remains null - * if we are not using a proxy). + * Address resolver for the outbound proxy connection. */ - private InetSocketAddress outboundProxySocketAddress = null; - - /** - * The transport used by our outbound proxy (remains null - * if we are not using a proxy). - */ - private String outboundProxyTransport = null; - - /** - * Array of ordered addresses to try when connecting. - */ - private InetSocketAddress[] connectionAddresses = null; - - /** - * Array of ordered addresses transports to try when connecting. - */ - private String[] connectionTransports = null; - - /** - * The InetAddress we are connecting to, outbound proxy. - */ - private InetSocketAddress currentConnectionAddress = null; + private ProxyConnection connection; /** * The logo corresponding to the jabber protocol. @@ -320,11 +299,17 @@ public class ProtocolProviderServiceSipImpl sipSecurityManager.setSecurityAuthority(authority); initRegistrarConnection(); - initOutboundProxy(0); - //connect to the Registrar. - if (sipRegistrarConnection != null) - sipRegistrarConnection.register(); + connection = ProxyConnection.create(this); + if(!registerUsingNextAddress()) + { + logger.error("No address found for " + this); + fireRegistrationStateChanged( + RegistrationState.UNREGISTERED, + RegistrationState.CONNECTION_FAILED, + RegistrationStateChangeEvent.REASON_SERVER_NOT_FOUND, + "Invalid or inaccessible server address."); + } } /** @@ -340,7 +325,7 @@ public class ProtocolProviderServiceSipImpl { if(getRegistrationState().equals(RegistrationState.UNREGISTERED) || getRegistrationState().equals(RegistrationState.UNREGISTERING) - || getRegistrationState().equals(RegistrationState.CONNECTION_FAILED)) + || getRegistrationState().equals(RegistrationState.CONNECTION_FAILED)) { return; } @@ -1013,9 +998,7 @@ public class ProtocolProviderServiceSipImpl addressFactory = null; sipSecurityManager = null; - connectionAddresses = null; - connectionTransports = null; - currentConnectionAddress = null; + connection = null; methodProcessors.clear(); @@ -1386,18 +1369,14 @@ public class ProtocolProviderServiceSipImpl logger.trace("Query for a " + transport + " listening point"); //override the transport in case we have an outbound proxy. - if(outboundProxySocketAddress != null) + if(connection.getAddress() != null) { if (logger.isTraceEnabled()) logger.trace("Will use proxy address"); - transport = outboundProxyTransport; + transport = connection.getTransport(); } - if( transport == null - || transport.trim().length() == 0 - || ( ! transport.trim().equalsIgnoreCase(ListeningPoint.TCP) - && ! transport.trim().equalsIgnoreCase(ListeningPoint.UDP) - && ! transport.trim().equalsIgnoreCase(ListeningPoint.TLS))) + if(!isValidTransport(transport)) { transport = getDefaultTransport(); } @@ -1435,26 +1414,9 @@ public class ProtocolProviderServiceSipImpl * @return the listening point that we should use to contact the * intended destination. */ - public ListeningPoint getListeningPoint(Address intendedDestination) - { - return getListeningPoint((SipURI)intendedDestination.getURI()); - } - - /** - * Returns the default listening point that we should use to contact the - * intended destination. - * - * @param intendedDestination the address that we will be trying to contact - * through the listening point we are trying to obtain. - * - * @return the listening point that we should use to contact the - * intended destination. - */ public ListeningPoint getListeningPoint(SipURI intendedDestination) { - String transport = intendedDestination.getTransportParam(); - - return getListeningPoint(transport); + return getListeningPoint(intendedDestination.getTransportParam()); } /** @@ -1652,48 +1614,9 @@ public class ProtocolProviderServiceSipImpl } } - /** - * 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 String getOutboundProxyString() + public ProxyConnection getConnection() { - InetAddress proxyAddress = outboundProxySocketAddress.getAddress(); - StringBuilder proxyStringBuffer - = new StringBuilder(proxyAddress.getHostAddress()); - - if(proxyAddress instanceof Inet6Address) - { - proxyStringBuffer.insert(0, '['); - proxyStringBuffer.append(']'); - } - - proxyStringBuffer.append(':'); - proxyStringBuffer.append(outboundProxySocketAddress.getPort()); - proxyStringBuffer.append('/'); - proxyStringBuffer.append(outboundProxyTransport); - - 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 boolean matchesInetAddress(InetAddress addressToTest) - { - // if the proxy is not yet initialized then this is not the provider that - // caused this comparison - if(outboundProxySocketAddress == null) - return false; - return addressToTest == outboundProxySocketAddress.getAddress(); + return connection; } /** @@ -1704,259 +1627,7 @@ public class ProtocolProviderServiceSipImpl */ public boolean isSignalingTransportSecure() { - return ListeningPoint.TLS.equalsIgnoreCase(outboundProxyTransport); - } - - /** - * Extracts all properties concerning the usage of an outbound proxy for - * this account. - * @param ix index of the address to use. - */ - private void initOutboundProxy(int ix) - { - //First init the proxy address - String proxyAddressStr = - accountID - .getAccountPropertyString(ProtocolProviderFactory. - PROXY_ADDRESS); - boolean proxyAddressAndPortEntered = false; - - if(proxyAddressStr == null || proxyAddressStr.trim().length() == 0 - || accountID.getAccountPropertyBoolean( - ProtocolProviderFactory.PROXY_AUTO_CONFIG, false)) - { - String userID = accountID.getAccountPropertyString( - ProtocolProviderFactory.USER_ID); - - int domainIx = userID.indexOf("@"); - - if(domainIx > 0) - { - proxyAddressStr = userID.substring(domainIx + 1); - } - else - { - proxyAddressStr = accountID - .getAccountPropertyString(ProtocolProviderFactory. - SERVER_ADDRESS); - - if(proxyAddressStr == null || proxyAddressStr.trim().length() == 0) - { - /* registrarless account */ - return; - } - } - } - else - { - if(accountID.getAccountProperty(ProtocolProviderFactory.PROXY_PORT) - != null) - { - proxyAddressAndPortEntered = true; - } - } - - //init proxy port - int proxyPort = ListeningPoint.PORT_5060; - - //proxy transport - String proxyTransport = accountID.getAccountPropertyString( - ProtocolProviderFactory.PREFERRED_TRANSPORT); - - if (proxyTransport != null && proxyTransport.length() > 0) - { - if (!proxyTransport.equals(ListeningPoint.UDP) - && !proxyTransport.equals(ListeningPoint.TCP) - && !proxyTransport.equals(ListeningPoint.TLS)) - { - throw new IllegalArgumentException(proxyTransport - + " is not a valid transport protocol. Transport must be " - + "left blank or set to TCP, UDP or TLS."); - } - } - else - { - proxyTransport = getDefaultTransport(); - } - - InetSocketAddress proxySocketAddress = null; - try - { - //check if user has overridden proxy port. - proxyPort = accountID.getAccountPropertyInt( - ProtocolProviderFactory.PROXY_PORT, - proxyPort); - if (proxyPort > NetworkUtils.MAX_PORT_NUMBER) - { - throw new IllegalArgumentException(proxyPort + " is larger than " - + NetworkUtils.MAX_PORT_NUMBER - + " and does not therefore represent a valid port number."); - } - - if(accountID.getAccountPropertyBoolean( - ProtocolProviderFactory.PROXY_AUTO_CONFIG, false)) - { - if(connectionAddresses == null) - { - ArrayList<InetSocketAddress> proxySocketAddressesList - = new ArrayList<InetSocketAddress>(); - ArrayList<String> proxyTransportsList - = new ArrayList<String>(); - - resolveSipAddress( - proxyAddressStr, - proxyTransport, - proxySocketAddressesList, - proxyTransportsList, - true); - - connectionTransports = proxyTransportsList.toArray( - new String[proxyTransportsList.size()]); - connectionAddresses = proxySocketAddressesList.toArray( - new InetSocketAddress[proxySocketAddressesList.size()]); - } - - proxyTransport = connectionTransports[ix]; - proxySocketAddress = connectionAddresses[ix]; - } - else - { - // according rfc3263 if proxy address and port are - // explicitly entered don't make SRV queries - if(proxyAddressAndPortEntered) - { - if(connectionAddresses == null) - { - ArrayList<InetSocketAddress> addresses - = new ArrayList<InetSocketAddress>(); - - resolveAddresses( - proxyAddressStr, - addresses, - proxyPort); - - connectionAddresses = addresses.toArray( - new InetSocketAddress[addresses.size()]); - - // we have transport, use it for all addresses - connectionTransports = new String[addresses.size()]; - Arrays.fill(connectionTransports, proxyTransport); - } - // only set if enough results found - if(connectionAddresses.length > ix) - proxySocketAddress = connectionAddresses[ix]; - } - else - { - if(connectionAddresses == null) - { - ArrayList<InetSocketAddress> proxySocketAddressesList - = new ArrayList<InetSocketAddress>(); - ArrayList<String> proxyTransportsList - = new ArrayList<String>(); - - resolveSipAddress( - proxyAddressStr, - proxyTransport, - proxySocketAddressesList, - proxyTransportsList, - false); - - connectionTransports = proxyTransportsList.toArray( - new String[proxyTransportsList.size()]); - connectionAddresses = - proxySocketAddressesList.toArray( - new InetSocketAddress[ - proxySocketAddressesList.size()]); - } - - proxyTransport = connectionTransports[ix]; - proxySocketAddress = connectionAddresses[ix]; - } - } - - if(proxySocketAddress == null) - throw new UnknownHostException(); - - if(this.currentConnectionAddress == null) - this.currentConnectionAddress = proxySocketAddress; - - proxyPort = proxySocketAddress.getPort(); - - if (logger.isTraceEnabled()) - logger.trace("Setting proxy address = " + proxyAddressStr); - - // We should set here the property to indicate that the proxy - // address is validated. When we load stored accounts we check - // this property in order to prevent checking again the proxy - // address. this is needed because in the case we don't have - // network while loading the application we still want to have - // our accounts loaded. - accountID.putAccountProperty( - ProtocolProviderFactory.PROXY_ADDRESS_VALIDATED, - Boolean.toString(true)); - } - catch (UnknownHostException ex) - { - logger.error(proxyAddressStr - + " appears to be an either invalid" - + " or inaccessible address.", - ex); - - boolean isProxyValidated = - accountID.getAccountPropertyBoolean( - ProtocolProviderFactory.PROXY_ADDRESS_VALIDATED, false); - - // We should check here if the proxy address was already validated. - // When we load stored accounts we want to prevent checking again the - // proxy address. This is needed because in the case we don't have - // network while loading the application we still want to have our - // accounts loaded. - if (!isProxyValidated) - { - throw new IllegalArgumentException( - proxyAddressStr - + " appears to be an either invalid or" - + " inaccessible address.", - ex); - } - } - - if(connectionAddresses == null - || connectionAddresses.length == 0) - { - sipRegistrarConnection = null; - // no addresses, maybe network down, lets try - // resolving them later - connectionAddresses = null; - connectionTransports = null; - - fireRegistrationStateChanged( - RegistrationState.UNREGISTERED, - RegistrationState.CONNECTION_FAILED, - RegistrationStateChangeEvent.REASON_SERVER_NOT_FOUND, - "Invalid or inaccessible server address."); - - return; - } - - // Return if no proxy is specified or if the proxyAddress is null. - if(proxySocketAddress == null) - { - return; - } - - // lets change registrar connections transport - // make sure it reflects the transport we choose from DNS records - // registrar connection can be null while creating accounts - if(sipRegistrarConnection != null) - sipRegistrarConnection.setTransport(proxyTransport); - - //store a reference to our sip proxy so that we can use it when - //constructing via and contact headers. - this.outboundProxySocketAddress - = new InetSocketAddress(proxySocketAddress.getAddress(), proxyPort); - this.outboundProxyTransport = proxyTransport; + return ListeningPoint.TLS.equalsIgnoreCase(connection.getTransport()); } /** @@ -2057,20 +1728,13 @@ public class ProtocolProviderServiceSipImpl */ public String getDefaultTransport() { - if( sipRegistrarConnection != null) - { - String registrarTransport = sipRegistrarConnection.getTransport(); - if( registrarTransport != null - && registrarTransport.length() > 0) - { - return registrarTransport; - } - } - - if(outboundProxySocketAddress != null - && outboundProxyTransport != null) + if(sipRegistrarConnection != null + && !sipRegistrarConnection.isRegistrarless() + && connection != null + && connection.getAddress() != null + && connection.getTransport() != null) { - return outboundProxyTransport; + return connection.getTransport(); } else { @@ -2109,19 +1773,6 @@ public class ProtocolProviderServiceSipImpl } /** - * Returns the listening point that corresponds to the transport returned by - * getDefaultTransport(). Equivalent to calling - * getListeningPoint(getDefaultTransport()) - * - * @return the Jain SipProvider that corresponds to the transport returned - * by getDefaultTransport(). - */ - public ListeningPoint getDefaultListeningPoint() - { - return getListeningPoint(getDefaultTransport()); - } - - /** * Returns the display name string that the user has set as a display name * for this account. * @@ -2518,320 +2169,6 @@ public class ProtocolProviderServiceSipImpl } /** - * Tries to resolve <tt>address</tt> into a valid InetSocketAddress using - * an <tt>SRV</tt> query where it exists and A/AAAA where it doesn't. - * If there is no SRV,A or AAAA records return the socket address created - * with the supplied <tt>address</tt>, so we can keep old behaviour. - * When letting underling libs and java to resolve the address. - * - * @param address the address we'd like to resolve. - * @param transport the protocol that we'd like to use when accessing - * address. - * @param resultAddresses the list that will be filled with the result - * addresses. An <tt>InetSocketAddress</tt> instances containing the - * <tt>SRV</tt> record for <tt>address</tt> if one has been defined and the - * A/AAAA record where it hasn't. - * @param resultTransports the transports for the <tt>resultAddresses</tt>. - * @param resolveProtocol if the protocol should be resolved - * @return the transports that is used, when the <tt>transport</tt> is with - * value Auto we resolve the address with NAPTR query, if no NAPTR record - * we will use the default one. - * - * @throws UnknownHostException if <tt>address</tt> is not a valid host - * address. - */ - public void resolveSipAddress( - String address, String transport, - List<InetSocketAddress> resultAddresses, - List<String> resultTransports, - boolean resolveProtocol) - throws UnknownHostException - { - //we need to resolve the address only if its a hostname. - if(NetworkUtils.isValidIPAddress(address)) - { - InetAddress addressObj = NetworkUtils.getInetAddress(address); - - //this is an ip address so we need to return default ports since - //we can't get them from a DNS. - int port = ListeningPoint.PORT_5060; - if(transport.equalsIgnoreCase(ListeningPoint.TLS)) - port = ListeningPoint.PORT_5061; - - resultTransports.add(transport); - resultAddresses.add(new InetSocketAddress(addressObj, port)); - - // as its ip address return, no dns is needed. - return; - } - - // first make NAPTR resolve to get protocol, if needed - if(resolveProtocol) - { - try - { - String[][] naptrRecords = NetworkUtils.getNAPTRRecords(address); - - if(naptrRecords != null && naptrRecords.length > 0) - { - for(String[] rec : naptrRecords) - { - resolveSRV( - rec[2], - rec[1], - resultAddresses, - resultTransports, - true); - } - - // NAPTR found use only it - if(logger.isInfoEnabled()) - logger.info("Found NAPTR record and using it:" - + resultAddresses); - - // return only if found something - if(resultAddresses.size() > 0 - && resultTransports.size() > 0) - return; - } - } - catch (ParseException e) - { - logger.error("Error parsing dns record.", e); - } - } - - //try to obtain SRV mappings from the DNS - try - { - if(resolveProtocol) - { - String[] transports = new String[]{ - ListeningPoint.TLS, - ListeningPoint.TCP, - ListeningPoint.UDP - }; - for(String tp : transports) - { - resolveSRV( - address, - tp, - resultAddresses, - resultTransports, - false); - if(!resultAddresses.isEmpty()) - { - transport = tp; - break; - } - } - } - else - { - resolveSRV( - address, - transport, - resultAddresses, - resultTransports, - false); - } - } - catch (ParseException e) - { - logger.error("Error parsing dns record.", e); - } - - //no SRV means default ports - int defaultPort = ListeningPoint.PORT_5060; - if(transport.equalsIgnoreCase(ListeningPoint.TLS)) - defaultPort = ListeningPoint.PORT_5061; - - ArrayList<InetSocketAddress> tempResultAddresses - = new ArrayList<InetSocketAddress>(); - - // after checking SRVs, lets add and AAAA and A records - resolveAddresses( - address, - tempResultAddresses, - defaultPort); - for(InetSocketAddress a : tempResultAddresses) - { - if(!resultAddresses.contains(a)) - { - resultAddresses.add(a); - resultTransports.add(transport); - } - } - - // make sure we don't return empty array - if(resultAddresses.size() == 0) - { - resultAddresses.add(new InetSocketAddress(address, defaultPort)); - resultTransports.add(transport); - - // there were no SRV mappings so we only need to A/AAAA resolve the - // address. Do this before we instantiate the - // resulting InetSocketAddress because its constructor - // suppresses UnknownHostException-s and we want to know if - // something goes wrong. - @SuppressWarnings("unused") - InetAddress addressObj = InetAddress.getByName(address); - } - } - - /** - * Resolves the given address. Resolves A and AAAA records and returns - * them in <tt>resultAddresses</tt> ordered according - * <tt>preferIPv6Addresses</tt> option. - * @param address the address to resolve. - * @param resultAddresses the List in which we provide the result. - * @param defaultPort the port to use for the result address. - * @throws UnknownHostException its not supposed to be thrown, cause - * the address we use is an ip address. - */ - private void resolveAddresses( - String address, List<InetSocketAddress> resultAddresses, - int defaultPort) - throws UnknownHostException - { - //we need to resolve the address only if its a hostname. - if(NetworkUtils.isValidIPAddress(address)) - { - InetAddress addressObj = NetworkUtils.getInetAddress(address); - - resultAddresses.add(new InetSocketAddress(addressObj, defaultPort)); - - // as its ip address return, no dns is needed. - return; - } - - try - { - for(InetSocketAddress a : - NetworkUtils.getAandAAAARecords(address, defaultPort)) - { - resultAddresses.add(a); - } - } - catch (ParseException ex) - { - logger.error("Error parsing dns record.", ex); - } - - if(resultAddresses.size() == 0) - logger.warn("No AAAA and A record found for " + address); - } - - /** - * Tries to resolve <tt>address</tt> into a valid InetSocketAddress using - * an <tt>SRV</tt> query where it exists and A/AAAA where it doesn't. The - * method assumes that the transport that we'll be using when connecting to - * address is the one that has been defined as default for this provider. - * - * @param address the address we'd like to resolve. - * - * @return an <tt>InetSocketAddress</tt> instance containing the - * <tt>SRV</tt> record for <tt>address</tt> if one has been defined and the - * A/AAAA record where it hasn't. - * - * @throws UnknownHostException if <tt>address</tt> is not a valid host - * address. - */ - public InetSocketAddress resolveSipAddress(String address) - throws UnknownHostException - { - ArrayList<InetSocketAddress> socketAddressesList = - new ArrayList<InetSocketAddress>(); - - resolveSipAddress(address, getDefaultTransport(), socketAddressesList, - new ArrayList<String>(), - getAccountID().getAccountPropertyBoolean( - ProtocolProviderFactory.PROXY_AUTO_CONFIG, false)); - - return socketAddressesList.get(0); - } - - /** - * Resolves the SRV records add their corresponding AAAA and A records - * in the <tt>resultAddresses</tt> ordered by the preference - * <tt>preferIPv6Addresses</tt> and their corresponding <tt>transport</tt> - * in the <tt>resultTransports</tt>. - * @param address the address to resolve. - * @param transport the transport to use - * @param resultAddresses the result address after resolve. - * @param resultTransports and the addresses transports. - * @param srvQueryString is the supplied address is of type - * _sip(s)._protocol.address, a string ready for srv queries, used - * when we have a NAPTR returned records with value <tt>true</tt>. - * @throws ParseException exception when parsing dns address - */ - private void resolveSRV(String address, - String transport, - List<InetSocketAddress> resultAddresses, - List<String> resultTransports, - boolean srvQueryString) - throws ParseException - { - SRVRecord srvRecords[] = null; - - if(srvQueryString) - { - srvRecords = NetworkUtils.getSRVRecords(address); - } - else - { - srvRecords = NetworkUtils.getSRVRecords( - transport.equalsIgnoreCase(ListeningPoint.TLS) - ? "sips" : "sip", - transport.equalsIgnoreCase(ListeningPoint.UDP) - ? ListeningPoint.UDP : ListeningPoint.TCP, - address); - } - - if(srvRecords != null) - { - ArrayList<InetSocketAddress> tempResultAddresses - = new ArrayList<InetSocketAddress>(); - - for(SRVRecord s : srvRecords) - { - // add corresponding A and AAAA records - // to the host address from SRV records - try - { - // as these are already resolved addresses (the SRV res.) - // lets get it without triggering a PTR - resolveAddresses( - s.getTarget(), - tempResultAddresses, - s.getPort()); - } - catch(UnknownHostException e) - { - logger.warn("Address unknown:" + s.getTarget(), e); - } - - /* add and every SRV address itself if not already there - if(!tempResultAddresses.contains(s)) - tempResultAddresses.add(s); - */ - - if (logger.isTraceEnabled()) - logger.trace("Returned SRV " + s); - } - - for(InetSocketAddress a : tempResultAddresses) - { - if(!resultAddresses.contains(a)) - { - resultAddresses.add(a); - resultTransports.add(transport); - } - } - } - } - - /** * Returns the <tt>InetAddress</tt> that is most likely to be to be used * as a next hop when contacting the specified <tt>destination</tt>. This is * an utility method that is used whenever we have to choose one of our @@ -2854,7 +2191,6 @@ public class ProtocolProviderServiceSipImpl return getIntendedDestination((SipURI)destination.getURI()); } - /** * Returns the <tt>InetAddress</tt> that is most likely to be to be used * as a next hop when contacting the specified <tt>destination</tt>. This is @@ -2905,7 +2241,7 @@ public class ProtocolProviderServiceSipImpl //but the destination could only be known to our outbound proxy //if we have one. If this is the case replace the destination //address with that of the proxy.(report by Dan Bogos) - InetSocketAddress outboundProxy = outboundProxySocketAddress; + InetSocketAddress outboundProxy = connection.getAddress(); if(outboundProxy != null) { @@ -2915,16 +2251,11 @@ public class ProtocolProviderServiceSipImpl } else { - try - { - destinationInetAddress = resolveSipAddress(host); - } - catch (UnknownHostException ex) - { - throw new IllegalArgumentException( - host + " is not a valid internet address.", - ex); - } + ProxyConnection tempConn = new AutoProxyConnection( + (SipAccountID)getAccountID(), + host, + getDefaultTransport()); + destinationInetAddress = tempConn.getAddress(); } if(logger.isDebugEnabled()) @@ -3046,66 +2377,42 @@ public class ProtocolProviderServiceSipImpl */ boolean registerUsingNextAddress() { - // means no connection - if(connectionAddresses == null) + if(connection == null) return false; - int i = 0; - for (; i < connectionAddresses.length; i++) - { - if(connectionAddresses[i].equals(currentConnectionAddress)) - break; - } - - if(i + 1 < connectionAddresses.length) + try { - changeAddress(i + 1); - - try + if(sipRegistrarConnection.isRegistrarless()) { + sipRegistrarConnection.setTransport(getDefaultTransport()); sipRegistrarConnection.register(); + return true; + } - catch (Throwable e) + else if(connection.getNextAddress()) { - logger.error("Cannot send register!", e); - sipRegistrarConnection.setRegistrationState( - RegistrationState.CONNECTION_FAILED, - RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, - "A timeout occurred while trying to connect to the server."); + sipRegistrarConnection.setTransport(connection.getTransport()); + sipRegistrarConnection.register(); + return true; } - - return true; + } + catch (Throwable e) + { + logger.error("Cannot send register!", e); + sipRegistrarConnection.setRegistrationState( + RegistrationState.CONNECTION_FAILED, + RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, + "A timeout occurred while trying to connect to the server."); } // as we reached the last address lets change it to the first one // so we don't get stuck to the last one forever, and the next time // use again the first one - changeAddress(0); - + connection.reset(); return false; } /** - * Change the current address used to register to ix one. - * @param ix the index of the new address to assign. - */ - private void changeAddress(int ix) - { - if(ix >= connectionAddresses.length) - return; - - this.currentConnectionAddress = connectionAddresses[ix]; - sipRegistrarConnection.setTransport(connectionTransports[ix]); - - initOutboundProxy(ix); - } - - protected InetSocketAddress getCurrentConnectionAddress() - { - return currentConnectionAddress; - } - - /** * If somewhere we got for example timeout of receiving answer to our * requests we consider problem with network and notify the provider. */ @@ -3130,4 +2437,17 @@ public class ProtocolProviderServiceSipImpl "A timeout occurred while trying to connect to the server."); } } + + /** + * Determines whether the supplied transport is a known SIP transport method + * + * @param transport the SIP transport to check + * @return True when transport is one of UDP, TCP or TLS. + */ + public static boolean isValidTransport(String transport) + { + return ListeningPoint.UDP.equalsIgnoreCase(transport) + || ListeningPoint.TLS.equalsIgnoreCase(transport) + || ListeningPoint.TCP.equalsIgnoreCase(transport); + } } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java b/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java index 75b6f2c..555eb98 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java @@ -126,7 +126,7 @@ public class ProxyRouter ProtocolProviderServiceSipImpl sipProvider = ((ProtocolProviderServiceSipImpl) service); - String proxy = sipProvider.getOutboundProxyString(); + String proxy = sipProvider.getConnection().getOutboundProxyString(); boolean forceLooseRouting = Boolean.valueOf((String) sipProvider.getAccountID().getAccountProperty( diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ServerStoredContactListXivoImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ServerStoredContactListXivoImpl.java index ad6de0f..3748718 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ServerStoredContactListXivoImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ServerStoredContactListXivoImpl.java @@ -173,7 +173,7 @@ public class ServerStoredContactListXivoImpl connection = new Socket(serverAddress, 5003); else // lets try using our sip connected address connection = new Socket( - sipProvider.getCurrentConnectionAddress().getAddress(), 5003); + sipProvider.getConnection().getAddress().getAddress(), 5003); connectionWriter = new PrintStream(connection.getOutputStream()); } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/AutoProxyConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/net/AutoProxyConnection.java new file mode 100644 index 0000000..0f84e67 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/net/AutoProxyConnection.java @@ -0,0 +1,384 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.sip.net;
+
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.*;
+
+import java.net.*;
+import java.text.*;
+
+import javax.sip.*;
+
+import net.java.sip.communicator.impl.protocol.sip.*;
+import net.java.sip.communicator.util.*;
+import static javax.sip.ListeningPoint.*;
+
+/**
+ * Implementation of the autodetect proxy connection. Tries to resolve a SIP-
+ * server by querying DNS in this order: NAPTR-SRV-A; SRV-A; A.
+ *
+ * @author Ingo Bauersachs
+ */
+public class AutoProxyConnection
+ extends ProxyConnection
+{
+ private enum State
+ {
+ New,
+ Naptr,
+ NaptrSrv,
+ NaptrSrvHosts,
+ NaptrSrvHostIPs,
+ Srv,
+ SrvHosts,
+ SrvHostIPs,
+ Hosts,
+ IP
+ }
+
+ /**
+ * Wrapper around {@link NetworkUtils} to support Unit Tests.
+ */
+ protected static class LocalNetworkUtils
+ {
+ public InetAddress getInetAddress(String address)
+ throws UnknownHostException
+ {
+ return NetworkUtils.getInetAddress(address);
+ }
+
+ public String[][] getNAPTRRecords(String address)
+ throws ParseException
+ {
+ return NetworkUtils.getNAPTRRecords(address);
+ }
+
+ public SRVRecord[] getSRVRecords(String service, String proto,
+ String address) throws ParseException
+ {
+ return NetworkUtils.getSRVRecords(service, proto, address);
+ }
+
+ public InetSocketAddress[] getAandAAAARecords(String target, int port)
+ throws ParseException
+ {
+ return NetworkUtils.getAandAAAARecords(target, port);
+ }
+
+ public boolean isValidIPAddress(String address)
+ {
+ return NetworkUtils.isValidIPAddress(address);
+ }
+
+ public SRVRecord[] getSRVRecords(String domain)
+ throws ParseException
+ {
+ return NetworkUtils.getSRVRecords(domain);
+ }
+ }
+
+ private final static Logger logger
+ = Logger.getLogger(AutoProxyConnection.class);
+
+ private State state;
+ private String address;
+ private final String defaultTransport;
+ private LocalNetworkUtils nu = new LocalNetworkUtils();
+
+ private final static String[] transports = new String[]
+ {
+ ListeningPoint.TLS,
+ ListeningPoint.TCP,
+ ListeningPoint.UDP
+ };
+ private boolean hadSrvResults;
+ private String[][] naptrRecords;
+ private int naptrIndex;
+ private SRVRecord[] srvRecords;
+ private int srvRecordsIndex;
+ private int srvTransportIndex;
+ private InetSocketAddress socketAddresses[];
+ private int socketAddressIndex;
+
+ /**
+ * Creates a new instance of this class. Uses the server from the account.
+ *
+ * @param account the account of this SIP protocol instance
+ * @param defaultTransport the default transport to use when DNS does not
+ * provide a protocol through NAPTR or SRV
+ */
+ public AutoProxyConnection(SipAccountID account, String defaultTransport)
+ {
+ super(account);
+ this.defaultTransport = defaultTransport;
+ reset();
+ }
+
+ /**
+ * Creates a new instance of this class. Uses the supplied address instead
+ * of the server address from the account.
+ *
+ * @param account the account of this SIP protocol instance
+ * @param address the domain on which to perform autodetection
+ * @param defaultTransport the default transport to use when DNS does not
+ * provide a protocol through NAPTR or SRV
+ */
+ public AutoProxyConnection(SipAccountID account, String address,
+ String defaultTransport)
+ {
+ super(account);
+ this.defaultTransport = defaultTransport;
+ reset();
+ this.address = address;
+ }
+
+ /**
+ * Sets the NetworkUtils wrapper. Used for Unit-Testing.
+ * @param nu the the NetworkUtils wrapper.
+ */
+ protected void setNetworkUtils(LocalNetworkUtils nu)
+ {
+ this.nu = nu;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#
+ * getNextAddressFromDns()
+ */
+ protected boolean getNextAddressFromDns()
+ {
+ try
+ {
+ return getNextAddressInternal();
+ }
+ catch(ParseException ex)
+ {
+ logger.error("Unable to get DNS data for <" + address
+ + "> in state" + state, ex);
+ }
+ return false;
+ }
+
+ /**
+ * Gets the next address from DNS.
+ */
+ private boolean getNextAddressInternal()
+ throws ParseException
+ {
+ switch(state)
+ {
+ case New:
+ state = State.Naptr;
+ return getNextAddressFromDns();
+ case IP:
+ if(socketAddressIndex == 0)
+ {
+ socketAddressIndex++;
+ try
+ {
+ socketAddress = new InetSocketAddress(
+ nu.getInetAddress(address),
+ ListeningPoint.TLS.equalsIgnoreCase(transport)
+ ? ListeningPoint.PORT_5061
+ : ListeningPoint.PORT_5060
+ );
+ }
+ catch (UnknownHostException e)
+ {
+ //this is not supposed to happen
+ logger.error("invalid IP address: " + address, e);
+ return false;
+ }
+ transport = defaultTransport;
+ return true;
+ }
+ return false;
+ case Naptr:
+ naptrRecords = nu.getNAPTRRecords(address);
+ if(naptrRecords != null && naptrRecords.length > 0)
+ {
+ state = State.NaptrSrv;
+ naptrIndex = 0;
+ }
+ else
+ {
+ hadSrvResults = false;
+ state = State.Srv;
+ srvTransportIndex = 0;
+ }
+
+ return getNextAddressFromDns();
+ case NaptrSrv:
+ for(; naptrIndex < naptrRecords.length; naptrIndex++)
+ {
+ srvRecords = nu.getSRVRecords(
+ naptrRecords[naptrIndex][2]);
+ if(srvRecords != null && srvRecords.length > 0)
+ {
+ state = State.NaptrSrvHosts;
+ if(TLS.equalsIgnoreCase(naptrRecords[naptrIndex][1]))
+ transport = TLS;
+ else if(TCP.equalsIgnoreCase(naptrRecords[naptrIndex][1]))
+ transport = TCP;
+ else
+ transport = UDP;
+ srvRecordsIndex = 0;
+ if(getNextAddressFromDns())
+ {
+ naptrIndex++;
+ return true;
+ }
+ }
+ }
+ return false; //no more naptr's
+ case NaptrSrvHosts:
+ for(; srvRecordsIndex < srvRecords.length; srvRecordsIndex++)
+ {
+ socketAddresses = nu.getAandAAAARecords(
+ srvRecords[srvRecordsIndex].getTarget(),
+ srvRecords[srvRecordsIndex].getPort());
+ if(socketAddresses != null && socketAddresses.length > 0)
+ {
+ state = State.NaptrSrvHostIPs;
+ socketAddressIndex = 0;
+ if(getNextAddressFromDns())
+ {
+ srvRecordsIndex++;
+ return true;
+ }
+ }
+ }
+ state = State.NaptrSrv;
+ return getNextAddressFromDns(); //backtrack to next naptr
+ case NaptrSrvHostIPs:
+ if(socketAddressIndex >= socketAddresses.length)
+ {
+ state = State.NaptrSrvHosts;
+ return getNextAddressFromDns(); //backtrack to next srv
+ }
+ socketAddress = socketAddresses[socketAddressIndex];
+ socketAddressIndex++;
+ return true;
+ case Srv:
+ for(;srvTransportIndex < transports.length; srvTransportIndex++)
+ {
+ srvRecords = nu.getSRVRecords(
+ (TLS.equals(transports[srvTransportIndex])
+ ? "sips"
+ : "sip"),
+ (UDP.equalsIgnoreCase(transports[srvTransportIndex])
+ ? UDP
+ : TCP),
+ address);
+ if(srvRecords != null && srvRecords.length > 0)
+ {
+ hadSrvResults = true;
+ state = State.SrvHosts;
+ srvRecordsIndex = 0;
+ transport = transports[srvTransportIndex];
+ if(getNextAddressFromDns())
+ {
+ srvTransportIndex++;
+ return true;
+ }
+ }
+ }
+ if(!hadSrvResults)
+ {
+ state = State.Hosts;
+ socketAddressIndex = 0;
+ return getNextAddressFromDns();
+ }
+ return false;
+ case SrvHosts:
+ for(; srvRecordsIndex < srvRecords.length; srvRecordsIndex++)
+ {
+ socketAddresses = nu.getAandAAAARecords(
+ srvRecords[srvRecordsIndex].getTarget(),
+ srvRecords[srvRecordsIndex].getPort());
+ if(socketAddresses != null && socketAddresses.length > 0)
+ {
+ state = State.SrvHostIPs;
+ socketAddressIndex = 0;
+ if(getNextAddressFromDns())
+ {
+ srvRecordsIndex++;
+ return true;
+ }
+ }
+ }
+ state = State.Srv;
+ return getNextAddressFromDns(); //backtrack to next srv record
+ case SrvHostIPs:
+ if(socketAddressIndex >= socketAddresses.length)
+ {
+ state = State.SrvHosts;
+ return getNextAddressFromDns();
+ }
+ socketAddress = socketAddresses[socketAddressIndex];
+ socketAddressIndex++;
+ return true;
+ case Hosts:
+ transport = defaultTransport;
+
+ if(socketAddresses == null)
+ {
+ socketAddresses = nu.getAandAAAARecords(
+ address,
+ ListeningPoint.PORT_5060);
+ }
+
+ if(socketAddresses != null && socketAddresses.length > 0
+ && socketAddressIndex < socketAddresses.length)
+ {
+ socketAddress = socketAddresses[socketAddressIndex++];
+ return true;
+ }
+ return false;
+ }
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#reset()
+ */
+ @Override
+ public void reset()
+ {
+ super.reset();
+ state = State.New;
+
+ //determine the hostname of the proxy for autodetection:
+ //1) server part of the user ID
+ //2) name of the registrar when the user ID contains no domain
+ String userID = account.getAccountPropertyString(USER_ID);
+ int domainIx = userID.indexOf("@");
+ if(domainIx > 0)
+ {
+ address = userID.substring(domainIx + 1);
+ }
+ else
+ {
+ address = account.getAccountPropertyString(SERVER_ADDRESS);
+ if(address == null || address.trim().length() == 0)
+ {
+ //registrarless account
+ return;
+ }
+ }
+ if(nu.isValidIPAddress(address))
+ {
+ state = State.IP;
+ socketAddressIndex = 0;
+ }
+ }
+}
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 new file mode 100644 index 0000000..93a3e42 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java @@ -0,0 +1,114 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.sip.net;
+
+import static javax.sip.ListeningPoint.*;
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.*;
+
+import java.net.*;
+import java.text.*;
+
+import net.java.sip.communicator.impl.protocol.sip.*;
+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(SipAccountID account)
+ {
+ super(account);
+ reset();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#
+ * getNextAddress()
+ */
+ @Override
+ public boolean getNextAddressFromDns()
+ {
+ 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 new file mode 100644 index 0000000..4de0685 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java @@ -0,0 +1,163 @@ +package net.java.sip.communicator.impl.protocol.sip.net;
+
+import java.net.*;
+import java.util.*;
+
+import net.java.sip.communicator.impl.protocol.sip.*;
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.*;
+
+/**
+ * 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 SipAccountID account;
+
+ /**
+ * Creates a new instance of this class.
+ * @param account the account of this SIP protocol instance
+ */
+ protected ProxyConnection(SipAccountID 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()
+ {
+ if(socketAddress == null)
+ getNextAddress();
+ return socketAddress;
+ }
+
+ /**
+ * Gets the transport to use for the next connection attempt.
+ * @return the transport of the last lookup.
+ */
+ public final String getTransport()
+ {
+ if(transport == null)
+ getNextAddress();
+ 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)
+ if(!getNextAddress())
+ 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()}.
+ */
+ public final boolean getNextAddress()
+ {
+ 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.
+ */
+ protected abstract boolean getNextAddressFromDns();
+
+ /**
+ * 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((SipAccountID) pps.getAccountID(),
+ pps.getDefaultTransport());
+ else
+ return new ManualProxyConnection((SipAccountID) pps.getAccountID());
+ }
+}
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 243350d..f5aa540 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 @@ -190,7 +190,8 @@ public class SslNetworkLayer for (ProtocolProviderServiceSipImpl pps : ProtocolProviderServiceSipImpl .getAllInstances()) { - if (pps.matchesInetAddress(address)) + if (pps.getConnection() != null + && pps.getConnection().isSameInetAddress(address)) { provider = pps; break; diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf index 6373236..9ebb963 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf +++ b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf @@ -65,6 +65,7 @@ Import-Package: org.apache.log4j, javax.xml.transform.dom, javax.xml.transform.stream, Export-Package: net.java.sip.communicator.impl.protocol.sip, + net.java.sip.communicator.impl.protocol.sip.net, net.java.sip.communicator.impl.protocol.sip.xcap, net.java.sip.communicator.impl.protocol.sip.xcap.utils, net.java.sip.communicator.impl.protocol.sip.xcap.model, diff --git a/test/net/java/sip/communicator/slick/protocol/sip/SipProtocolProviderServiceLick.java b/test/net/java/sip/communicator/slick/protocol/sip/SipProtocolProviderServiceLick.java index 77af005..4e07318 100644 --- a/test/net/java/sip/communicator/slick/protocol/sip/SipProtocolProviderServiceLick.java +++ b/test/net/java/sip/communicator/slick/protocol/sip/SipProtocolProviderServiceLick.java @@ -79,6 +79,9 @@ public class SipProtocolProviderServiceLick // xcap parsing tests addTest(TestXCapParse.suite()); + //proxy detection tests + addTestSuite(TestAutoProxyDetection.class); + //First test account installation so that the service that has //been installed by it gets tested by the rest of the tests. addTestSuite(TestAccountInstallation.class); diff --git a/test/net/java/sip/communicator/slick/protocol/sip/TestAutoProxyDetection.java b/test/net/java/sip/communicator/slick/protocol/sip/TestAutoProxyDetection.java new file mode 100644 index 0000000..3b1b76f --- /dev/null +++ b/test/net/java/sip/communicator/slick/protocol/sip/TestAutoProxyDetection.java @@ -0,0 +1,427 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.slick.protocol.sip;
+
+import static org.easymock.EasyMock.*;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.text.ParseException;
+
+import net.java.sip.communicator.impl.protocol.sip.SipAccountID;
+import net.java.sip.communicator.impl.protocol.sip.net.AutoProxyConnection;
+import net.java.sip.communicator.util.SRVRecord;
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.*;
+import junit.framework.TestCase;
+
+/**
+ * Tests all variations of automatic proxy detection through (simulated) DNS.
+ *
+ * @author Ingo Bauersachs
+ */
+public class TestAutoProxyDetection
+ extends TestCase
+{
+ private static class TestedAutoProxyDetection extends AutoProxyConnection
+ {
+ public TestedAutoProxyDetection(SipAccountID account,
+ String defaultTransport)
+ {
+ super(account, defaultTransport);
+ }
+
+ @Override
+ public void setNetworkUtils(LocalNetworkUtils nu)
+ {
+ super.setNetworkUtils(nu);
+ }
+
+ public static class NetworkUtils extends LocalNetworkUtils
+ {
+ }
+ }
+
+ private SipAccountID account;
+ private TestedAutoProxyDetection.NetworkUtils nu;
+ private SRVRecord srv1;
+ private SRVRecord srv2;
+ private SRVRecord srv3;
+ private InetSocketAddress a1;
+ private InetSocketAddress a2;
+ private InetSocketAddress a3;
+ private InetSocketAddress a4;
+ private final static String DOMAIN = "example.com";
+ private InetAddress ia1;
+ private InetAddress ia2;
+ private InetAddress ia3;
+ private InetAddress ia4;
+ private TestedAutoProxyDetection apd;
+
+ public void setUp()
+ {
+ account = createMock(SipAccountID.class);
+ expect(account.getAccountPropertyString(USER_ID))
+ .andReturn("unit@" + DOMAIN);
+ replay(account);
+
+ nu = createMock(TestedAutoProxyDetection.NetworkUtils.class);
+ apd = new TestedAutoProxyDetection(account, "UDP");
+ apd.setNetworkUtils(nu);
+
+ srv1 = createMock(SRVRecord.class);
+ expect(srv1.getTarget()).andReturn("proxy1."+DOMAIN);
+ expect(srv1.getPort()).andReturn(5060);
+ srv2 = createMock(SRVRecord.class);
+ expect(srv2.getTarget()).andReturn("proxy2."+DOMAIN);
+ expect(srv2.getPort()).andReturn(5061);
+ srv3 = createMock(SRVRecord.class);
+ expect(srv3.getTarget()).andReturn("proxy3."+DOMAIN);
+ expect(srv3.getPort()).andReturn(5062);
+ try
+ {
+ ia1 = InetAddress.getByAddress("proxy1." + DOMAIN,
+ new byte[]{0x7f,0,0,1});
+ ia2 = InetAddress.getByAddress("proxy2." + DOMAIN,
+ new byte[]{0x7f,0,0,2});
+ ia3 = InetAddress.getByAddress("proxy3." + DOMAIN,
+ new byte[]{0x7f,0,0,3});
+ ia4 = InetAddress.getByAddress("proxy4." + DOMAIN,
+ new byte[]{0x7f,0,0,4});
+ }
+ catch (UnknownHostException e)
+ {
+ fail("unable to initialize: " + e.getMessage());
+ }
+ a1 = new InetSocketAddress(ia1, 5060);
+ a2 = new InetSocketAddress(ia2, 5061);
+ a3 = new InetSocketAddress(ia3, 5062);
+ a4 = new InetSocketAddress(ia4, 5063);
+ }
+
+ private void prepareOneNaptrOneSrv() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{
+ {"0", "udp", "_sip._udp." + DOMAIN}
+ });
+ expect(nu.getSRVRecords("_sip._udp."+DOMAIN))
+ .andReturn(new SRVRecord[]{ srv1 });
+ }
+
+ private void prepareOneNaptrTwoSrv() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{
+ {"0", "udp", "_sip._udp." + DOMAIN}
+ });
+ expect(nu.getSRVRecords("_sip._udp."+DOMAIN))
+ .andReturn(new SRVRecord[]{ srv1, srv2 });
+ }
+
+ public void testOneNaptrNoSrv() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{
+ {"0", "udp", "_sip._udp." + DOMAIN}
+ });
+ expect(nu.getSRVRecords("_sip._udp." + DOMAIN)).andReturn(null);
+ replay(nu);
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu);
+ }
+
+ public void testOneNaptrOneSrvOneA() throws ParseException
+ {
+ prepareOneNaptrOneSrv();
+ expect(nu.getAandAAAARecords("proxy1." + DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1});
+ replay(nu, srv1);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu, srv1);
+ }
+
+ public void testOneNaptrOneSrvTwoA() throws ParseException
+ {
+ prepareOneNaptrOneSrv();
+ expect(nu.getAandAAAARecords("proxy1." + DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1, a2});
+ replay(nu, srv1);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+ assertTrue(apd.getNextAddress());
+ assertEquals(a2, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu, srv1);
+ }
+
+ //-----------------------
+
+ public void testOneNaptrTwoSrvOneA() throws ParseException
+ {
+ prepareOneNaptrTwoSrv();
+ expect(nu.getAandAAAARecords("proxy1." + DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1});
+ expect(nu.getAandAAAARecords("proxy2." + DOMAIN, 5061))
+ .andReturn(new InetSocketAddress[]{a2});
+ replay(nu, srv1, srv2);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+ assertTrue(apd.getNextAddress());
+ assertEquals(a2, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu, srv1, srv2);
+ }
+
+ public void testOneNaptrTwoSrvTwoA() throws ParseException
+ {
+ prepareOneNaptrTwoSrv();
+ expect(nu.getAandAAAARecords("proxy1." + DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1, a2});
+ expect(nu.getAandAAAARecords("proxy2." + DOMAIN, 5061))
+ .andReturn(new InetSocketAddress[]{a3, a4});
+ replay(nu, srv1, srv2);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertTrue(apd.getNextAddress());
+ assertEquals(a2, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertTrue(apd.getNextAddress());
+ assertEquals(a3, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertTrue(apd.getNextAddress());
+ assertEquals(a4, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu, srv1, srv2);
+ }
+
+ //-------------------
+
+ public void testThreeNaptrOneSrvEachOneAEach() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{
+ {"0", "udp", "_sip._udp." + DOMAIN},
+ {"0", "tcp", "_sip._tcp." + DOMAIN},
+ {"0", "tls", "_sips._tcp." + DOMAIN}
+ });
+ expect(nu.getSRVRecords("_sip._udp."+DOMAIN))
+ .andReturn(new SRVRecord[]{ srv1 });
+ expect(nu.getSRVRecords("_sip._tcp."+DOMAIN))
+ .andReturn(new SRVRecord[]{ srv2 });
+ expect(nu.getSRVRecords("_sips._tcp."+DOMAIN))
+ .andReturn(new SRVRecord[]{ srv3 });
+ expect(nu.getAandAAAARecords("proxy1." + DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1});
+ expect(nu.getAandAAAARecords("proxy2." + DOMAIN, 5061))
+ .andReturn(new InetSocketAddress[]{a1});
+ expect(nu.getAandAAAARecords("proxy3." + DOMAIN, 5062))
+ .andReturn(new InetSocketAddress[]{a1});
+
+ replay(nu, srv1, srv2, srv3);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertTrue(apd.getNextAddress());
+ assertEquals(a1, apd.getAddress());
+ assertEquals("TCP", apd.getTransport());
+
+ assertTrue(apd.getNextAddress());
+ assertEquals(a1, apd.getAddress());
+ assertEquals("TLS", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu, srv1, srv2, srv3);
+ }
+
+ //-----------------------
+
+ public void testNoSrvOneA() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{});
+ expect(nu.getSRVRecords("sips", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "UDP", DOMAIN)).andReturn(null);
+ expect(nu.getAandAAAARecords(DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1});
+
+ replay(nu);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu);
+ }
+
+ public void testOneSrvOneA() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{});
+ expect(nu.getSRVRecords("sips", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "UDP", DOMAIN))
+ .andReturn(new SRVRecord[]{srv1});
+ expect(nu.getAandAAAARecords("proxy1." + DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1});
+
+ replay(nu, srv1);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu, srv1);
+ }
+
+ public void testOneSrvTwoA() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{});
+ expect(nu.getSRVRecords("sips", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "UDP", DOMAIN))
+ .andReturn(new SRVRecord[]{srv1});
+ expect(nu.getAandAAAARecords("proxy1." + DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1, a2});
+
+ replay(nu, srv1);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertTrue(apd.getNextAddress());
+ assertEquals(a2, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu, srv1);
+ }
+
+ public void testTwoSrvOneA() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{});
+ expect(nu.getSRVRecords("sips", "TCP", DOMAIN))
+ .andReturn(new SRVRecord[]{srv2});
+ expect(nu.getSRVRecords("sip", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "UDP", DOMAIN))
+ .andReturn(new SRVRecord[]{srv1});
+ expect(nu.getAandAAAARecords("proxy1." + DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1});
+ expect(nu.getAandAAAARecords("proxy2." + DOMAIN, 5061))
+ .andReturn(new InetSocketAddress[]{a2});
+
+ replay(nu, srv1, srv2);
+
+ assertEquals(a2, apd.getAddress());
+ assertEquals("TLS", apd.getTransport());
+
+ assertTrue(apd.getNextAddress());
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu, srv1, srv2);
+ }
+
+ //----------------------
+
+ public void testNoA() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{});
+ expect(nu.getSRVRecords("sips", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "UDP", DOMAIN)).andReturn(null);
+ expect(nu.getAandAAAARecords(DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{});
+
+ replay(nu);
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu);
+ }
+
+ public void testOneA() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{});
+ expect(nu.getSRVRecords("sips", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "UDP", DOMAIN)).andReturn(null);
+ expect(nu.getAandAAAARecords(DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1});
+
+ replay(nu);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu);
+ }
+
+ public void testTwoA() throws ParseException
+ {
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{});
+ expect(nu.getSRVRecords("sips", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "TCP", DOMAIN)).andReturn(null);
+ expect(nu.getSRVRecords("sip", "UDP", DOMAIN)).andReturn(null);
+ expect(nu.getAandAAAARecords(DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1, a2});
+
+ replay(nu);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertTrue(apd.getNextAddress());
+ assertEquals(a2, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu);
+ }
+
+ public void testNotReturningSameAddressTwice() throws ParseException
+ {
+ expect(srv1.getTarget()).andReturn("proxy1."+DOMAIN);
+ expect(srv1.getPort()).andReturn(5060);
+ expect(nu.getNAPTRRecords(DOMAIN)).andReturn(new String[][]{
+ {"0", "udp", "_sip._udp." + DOMAIN},
+ {"1", "udp", "_sip._udp." + DOMAIN}
+ });
+ expect(nu.getSRVRecords("_sip._udp."+DOMAIN)).andReturn(new SRVRecord[]{
+ srv1
+ });
+ expect(nu.getSRVRecords("_sip._udp."+DOMAIN)).andReturn(new SRVRecord[]{
+ srv1
+ });
+ expect(nu.getAandAAAARecords("proxy1." + DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1});
+ expect(nu.getAandAAAARecords("proxy1." + DOMAIN, 5060))
+ .andReturn(new InetSocketAddress[]{a1});
+
+ replay(nu, srv1);
+
+ assertEquals(a1, apd.getAddress());
+ assertEquals("UDP", apd.getTransport());
+
+ assertFalse(apd.getNextAddress());
+ verify(account, nu, srv1);
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/sip/TestOperationSetPresence.java b/test/net/java/sip/communicator/slick/protocol/sip/TestOperationSetPresence.java index ca723d7..1fe4f26 100644 --- a/test/net/java/sip/communicator/slick/protocol/sip/TestOperationSetPresence.java +++ b/test/net/java/sip/communicator/slick/protocol/sip/TestOperationSetPresence.java @@ -118,11 +118,7 @@ public class TestOperationSetPresence if(SipSlickFixture.onlineTestingDisabled) { TestSuite suite = new TestSuite(); - //the only test around here that we could run without net - //connectivity - suite.addTest( - new TestOperationSetPresence( - "testSupportedStatusSetForCompleteness")); + //currently no tests without online connection return suite; } diff --git a/test/net/java/sip/communicator/slick/protocol/sip/sip.provider.slick.manifest.mf b/test/net/java/sip/communicator/slick/protocol/sip/sip.provider.slick.manifest.mf index 57320fb..e5a7495 100644 --- a/test/net/java/sip/communicator/slick/protocol/sip/sip.provider.slick.manifest.mf +++ b/test/net/java/sip/communicator/slick/protocol/sip/sip.provider.slick.manifest.mf @@ -7,13 +7,15 @@ System-Bundle: yes Import-Package: net.java.sip.communicator.service.configuration, junit.framework, org.osgi.framework, - org.w3c.dom; - javax.xml.namespace; + org.w3c.dom, + org.easymock, + javax.xml.namespace, net.java.sip.communicator.util, net.java.sip.communicator.util.xml, net.java.sip.communicator.service.protocol, net.java.sip.communicator.service.protocol.event, net.java.sip.communicator.impl.protocol.sip, + net.java.sip.communicator.impl.protocol.sip.net, net.java.sip.communicator.impl.protocol.sip.xcap, net.java.sip.communicator.impl.protocol.sip.xcap.utils, net.java.sip.communicator.impl.protocol.sip.xcap.model, |