diff --git a/lib/felix.unit.test.properties b/lib/felix.unit.test.properties index fabf3dc80..4ff16860a 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 new file mode 100644 index 000000000..3064d3539 Binary files /dev/null and b/lib/installer-exclude/cglib-nodep.osgi-2.1_3.jar differ diff --git a/lib/installer-exclude/easymock-3.1.jar b/lib/installer-exclude/easymock-3.1.jar new file mode 100644 index 000000000..d8232ab07 Binary files /dev/null and b/lib/installer-exclude/easymock-3.1.jar differ diff --git a/lib/installer-exclude/objenesis-1.2.jar b/lib/installer-exclude/objenesis-1.2.jar new file mode 100644 index 000000000..759b62328 Binary files /dev/null and b/lib/installer-exclude/objenesis-1.2.jar differ 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 1c782e513..6cff570e8 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 @@ 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 void register(SecurityAuthority authority) 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 void unregister() { 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 void run() addressFactory = null; sipSecurityManager = null; - connectionAddresses = null; - connectionTransports = null; - currentConnectionAddress = null; + connection = null; methodProcessors.clear(); @@ -1386,18 +1369,14 @@ public ListeningPoint getListeningPoint(String transport) 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(); } @@ -1425,21 +1404,6 @@ else if(transport.equalsIgnoreCase(ListeningPoint.TLS)) return lp; } - /** - * 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(Address intendedDestination) - { - return getListeningPoint((SipURI)intendedDestination.getURI()); - } - /** * Returns the default listening point that we should use to contact the * intended destination. @@ -1452,9 +1416,7 @@ public ListeningPoint getListeningPoint(Address intendedDestination) */ public ListeningPoint getListeningPoint(SipURI intendedDestination) { - String transport = intendedDestination.getTransportParam(); - - return getListeningPoint(transport); + return getListeningPoint(intendedDestination.getTransportParam()); } /** @@ -1652,48 +1614,9 @@ public Address getOurSipAddress(SipURI intendedDestination) } } - /** - * In case we are using an outbound proxy this method returns - * a suitable string for use with Router. - * The method returns null otherwise. - * - * @return the string of our outbound proxy if we are using one and - * null 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 boolean matchesInetAddress(InetAddress addressToTest) */ 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 proxySocketAddressesList - = new ArrayList(); - ArrayList proxyTransportsList - = new ArrayList(); - - 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 addresses - = new ArrayList(); - - 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 proxySocketAddressesList - = new ArrayList(); - ArrayList proxyTransportsList - = new ArrayList(); - - 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 void unregisterMethodProcessor(String method, */ 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 { @@ -2108,19 +1772,6 @@ public SipProvider getDefaultJainSipProvider() return getJainSipProvider(getDefaultTransport()); } - /** - * 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. @@ -2517,320 +2168,6 @@ public Address parseAddressString(String uriStr) return toAddress; } - /** - * Tries to resolve address into a valid InetSocketAddress using - * an SRV 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 address, 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 InetSocketAddress instances containing the - * SRV record for address if one has been defined and the - * A/AAAA record where it hasn't. - * @param resultTransports the transports for the resultAddresses. - * @param resolveProtocol if the protocol should be resolved - * @return the transports that is used, when the transport is with - * value Auto we resolve the address with NAPTR query, if no NAPTR record - * we will use the default one. - * - * @throws UnknownHostException if address is not a valid host - * address. - */ - public void resolveSipAddress( - String address, String transport, - List resultAddresses, - List 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 tempResultAddresses - = new ArrayList(); - - // 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 resultAddresses ordered according - * preferIPv6Addresses 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 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 address into a valid InetSocketAddress using - * an SRV 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 InetSocketAddress instance containing the - * SRV record for address if one has been defined and the - * A/AAAA record where it hasn't. - * - * @throws UnknownHostException if address is not a valid host - * address. - */ - public InetSocketAddress resolveSipAddress(String address) - throws UnknownHostException - { - ArrayList socketAddressesList = - new ArrayList(); - - resolveSipAddress(address, getDefaultTransport(), socketAddressesList, - new ArrayList(), - getAccountID().getAccountPropertyBoolean( - ProtocolProviderFactory.PROXY_AUTO_CONFIG, false)); - - return socketAddressesList.get(0); - } - - /** - * Resolves the SRV records add their corresponding AAAA and A records - * in the resultAddresses ordered by the preference - * preferIPv6Addresses and their corresponding transport - * in the resultTransports. - * @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 true. - * @throws ParseException exception when parsing dns address - */ - private void resolveSRV(String address, - String transport, - List resultAddresses, - List 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 tempResultAddresses - = new ArrayList(); - - 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 InetAddress that is most likely to be to be used * as a next hop when contacting the specified destination. This is @@ -2854,7 +2191,6 @@ public InetSocketAddress getIntendedDestination(Address destination) return getIntendedDestination((SipURI)destination.getURI()); } - /** * Returns the InetAddress that is most likely to be to be used * as a next hop when contacting the specified destination. This is @@ -2905,7 +2241,7 @@ public InetSocketAddress getIntendedDestination(String host) //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 InetSocketAddress getIntendedDestination(String host) } 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,65 +2377,41 @@ else if(message instanceof TimeoutEvent) */ 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 @@ protected void notifyConnectionFailed() "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 75b6f2ccf..555eb98c7 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 @@ private Router getRouterFor(Request request) 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 ad6de0fbe..374871876 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 @@ private void connect(String serverAddress) 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 000000000..0f84e675c --- /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 000000000..93a3e4227 --- /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 000000000..4de068550 --- /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 returnedAddresses = new LinkedList(); + + 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 null otherwise. + * + * @return the string of our outbound proxy if we are using one and + * null 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 243350dd7..f5aa5409f 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 @@ private SSLSocketFactory getSSLSocketFactory(InetAddress address) 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 6373236f0..9ebb963d2 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 77af005f4..4e07318e1 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 void start(BundleContext context) // 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 000000000..3b1b76f18 --- /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 ca723d715..1fe4f26fa 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 static Test suite() 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 57320fb66..e5a7495de 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,