From 0d8d67ac0eda96037e635fce462aa6587606e813 Mon Sep 17 00:00:00 2001 From: Ingo Bauersachs Date: Tue, 8 Apr 2014 23:05:42 +0200 Subject: [PATCH] Respect port in regless SIP calls --- .../sip/ProtocolProviderServiceSipImpl.java | 44 +- .../protocol/sip/net/AutoProxyConnection.java | 810 +++++++++--------- 2 files changed, 424 insertions(+), 430 deletions(-) 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 a63333e01..7877a7cb5 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java @@ -2399,29 +2399,6 @@ public InetSocketAddress getIntendedDestination(Address destination) */ public InetSocketAddress getIntendedDestination(SipURI destination) throws IllegalArgumentException - { - return getIntendedDestination(destination.getHost()); - } - - /** - * Returns the InetAddress that is most likely to be to be used - * as a next hop when contacting the specified destination. This is - * an utility method that is used whenever we have to choose one of our - * local addresses to put in the Via, Contact or (in the case of no - * registrar accounts) From headers. The method also takes into account - * the existence of an outbound proxy and in that case returns its address - * as the next hop. - * - * @param host the destination that we would contact. - * - * @return the InetSocketAddress that is most likely to be to be - * used as a next hop when contacting the specified destination. - * - * @throws IllegalArgumentException if destination is not a valid - * host/ip/fqdn. - */ - public InetSocketAddress getIntendedDestination(String host) - throws IllegalArgumentException { // Address InetSocketAddress destinationInetAddress = null; @@ -2440,16 +2417,29 @@ public InetSocketAddress getIntendedDestination(String host) } else { + String transport = destination.getTransportParam(); + if (transport == null) + { + transport = getDefaultTransport(); + } + + int port = destination.getPort(); + if (port == -1) + { + port = ListeningPoint.PORT_5060; + } + ProxyConnection tempConn = new AutoProxyConnection( (SipAccountIDImpl)getAccountID(), - host, - getDefaultTransport()); + destination.getHost(), + port, + transport); try { if(tempConn.getNextAddress()) destinationInetAddress = tempConn.getAddress(); else - throw new IllegalArgumentException(host + throw new IllegalArgumentException(destination.getHost() + " could not be resolved to an internet address."); } catch (DnssecException e) @@ -2460,7 +2450,7 @@ public InetSocketAddress getIntendedDestination(String host) if(logger.isDebugEnabled()) logger.debug("Returning address " + destinationInetAddress - + " for destination " + host); + + " for destination " + destination.getHost()); return destinationInetAddress; } 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 index 9e8bcf032..cab2a6611 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/net/AutoProxyConnection.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/net/AutoProxyConnection.java @@ -1,403 +1,407 @@ -/* - * 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.TCP; -import static javax.sip.ListeningPoint.TLS; -import static javax.sip.ListeningPoint.UDP; -import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.SERVER_ADDRESS; -import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.USER_ID; - -import java.net.*; -import java.text.*; - -import javax.sip.*; - -import net.java.sip.communicator.impl.protocol.sip.*; -import net.java.sip.communicator.service.dns.*; -import net.java.sip.communicator.util.*; - -/** - * 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, DnssecException - { - return NetworkUtils.getNAPTRRecords(address); - } - - public SRVRecord[] getSRVRecords(String service, String proto, - String address) throws ParseException, DnssecException - { - return NetworkUtils.getSRVRecords(service, proto, address); - } - - public InetSocketAddress[] getAandAAAARecords(String target, int port) - throws ParseException, DnssecException - { - return NetworkUtils.getAandAAAARecords(target, port); - } - - public boolean isValidIPAddress(String address) - { - return NetworkUtils.isValidIPAddress(address); - } - - public SRVRecord[] getSRVRecords(String domain) - throws ParseException, DnssecException - { - 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( SipAccountIDImpl 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( SipAccountIDImpl account, String address, - String defaultTransport ) - { - super(account); - this.defaultTransport = defaultTransport; - reset(); - this.address = address; - if (nu.isValidIPAddress(this.address)) - { - this.state = State.IP; - } - } - - /** - * 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() - */ - @Override - protected boolean getNextAddressFromDns() - throws DnssecException - { - 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. - * - * @throws DnssecException When a DNSSEC failure occured during the lookup. - * @throws ParseException When a domain name (possibly returned from DNS - * itself) is invalid. - */ - private boolean getNextAddressInternal() - throws DnssecException, 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: - if(srvRecordsIndex >= srvRecords.length) - { - state = State.Srv; - return getNextAddressFromDns(); //backtrack to next srv record - } - 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; - } - } - } - return false; - 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; - } - } -} +/* + * 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.TCP; +import static javax.sip.ListeningPoint.TLS; +import static javax.sip.ListeningPoint.UDP; +import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.SERVER_ADDRESS; +import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.USER_ID; + +import java.net.*; +import java.text.*; + +import javax.sip.*; + +import net.java.sip.communicator.impl.protocol.sip.*; +import net.java.sip.communicator.service.dns.*; +import net.java.sip.communicator.util.*; + +/** + * 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, DnssecException + { + return NetworkUtils.getNAPTRRecords(address); + } + + public SRVRecord[] getSRVRecords(String service, String proto, + String address) throws ParseException, DnssecException + { + return NetworkUtils.getSRVRecords(service, proto, address); + } + + public InetSocketAddress[] getAandAAAARecords(String target, int port) + throws ParseException, DnssecException + { + return NetworkUtils.getAandAAAARecords(target, port); + } + + public boolean isValidIPAddress(String address) + { + return NetworkUtils.isValidIPAddress(address); + } + + public SRVRecord[] getSRVRecords(String domain) + throws ParseException, DnssecException + { + return NetworkUtils.getSRVRecords(domain); + } + } + + private final static Logger logger + = Logger.getLogger(AutoProxyConnection.class); + + private State state; + private String address; + private int port; + 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( SipAccountIDImpl account, + String defaultTransport ) + { + super(account); + port = ListeningPoint.PORT_5060; + 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 port the destination socket port + * @param defaultTransport the default transport to use when DNS does not + * provide a protocol through NAPTR or SRV + */ + public AutoProxyConnection( SipAccountIDImpl account, String address, + int port, String defaultTransport ) + { + super(account); + this.defaultTransport = defaultTransport; + this.port = port; + reset(); + this.address = address; + if (nu.isValidIPAddress(this.address)) + { + this.state = State.IP; + } + } + + /** + * 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() + */ + @Override + protected boolean getNextAddressFromDns() + throws DnssecException + { + 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. + * + * @throws DnssecException When a DNSSEC failure occured during the lookup. + * @throws ParseException When a domain name (possibly returned from DNS + * itself) is invalid. + */ + private boolean getNextAddressInternal() + throws DnssecException, 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: + if(srvRecordsIndex >= srvRecords.length) + { + state = State.Srv; + return getNextAddressFromDns(); //backtrack to next srv record + } + 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; + } + } + } + return false; + 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, + port); + } + + 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; + } + } +}