2007-01-04 18:25:30 +01:00
|
|
|
|
/**
|
|
|
|
|
* $RCSfile$
|
|
|
|
|
* $Revision$
|
|
|
|
|
* $Date$
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2002-2006 Jive Software. All rights reserved.
|
|
|
|
|
* ====================================================================
|
|
|
|
|
* The Jive Software License (based on Apache Software License, Version 1.1)
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
|
* are met:
|
|
|
|
|
*
|
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
|
*
|
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
|
* distribution.
|
|
|
|
|
*
|
|
|
|
|
* 3. The end-user documentation included with the redistribution,
|
|
|
|
|
* if any, must include the following acknowledgment:
|
|
|
|
|
* "This product includes software developed by
|
|
|
|
|
* Jive Software (http://www.jivesoftware.com)."
|
|
|
|
|
* Alternately, this acknowledgment may appear in the software itself,
|
|
|
|
|
* if and wherever such third-party acknowledgments normally appear.
|
|
|
|
|
*
|
|
|
|
|
* 4. The names "Smack" and "Jive Software" must not be used to
|
|
|
|
|
* endorse or promote products derived from this software without
|
|
|
|
|
* prior written permission. For written permission, please
|
|
|
|
|
* contact webmaster@jivesoftware.com.
|
|
|
|
|
*
|
|
|
|
|
* 5. Products derived from this software may not be called "Smack",
|
|
|
|
|
* nor may "Smack" appear in their name, without prior written
|
|
|
|
|
* permission of Jive Software.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
|
|
|
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
|
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
|
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
|
* ====================================================================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package org.jivesoftware.smackx.jingle.nat;
|
|
|
|
|
|
|
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
|
|
|
|
|
2007-02-07 03:35:27 +01:00
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.net.*;
|
2007-01-04 18:25:30 +01:00
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
import java.util.Random;
|
|
|
|
|
import java.util.Arrays;
|
2007-01-04 18:25:30 +01:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Transport candidate.
|
2007-02-07 03:35:27 +01:00
|
|
|
|
* <p/>
|
2007-01-04 18:25:30 +01:00
|
|
|
|
* A candidate represents the possible transport for data interchange between
|
|
|
|
|
* the two endpoints.
|
|
|
|
|
*
|
|
|
|
|
* @author Thiago Camargo
|
|
|
|
|
* @author Alvaro Saurin
|
|
|
|
|
*/
|
|
|
|
|
public abstract class TransportCandidate {
|
|
|
|
|
|
|
|
|
|
private String name;
|
|
|
|
|
|
|
|
|
|
private String ip; // IP address
|
|
|
|
|
|
|
|
|
|
private int port; // Port to use, or 0 for any port
|
|
|
|
|
|
|
|
|
|
private String localIp;
|
|
|
|
|
|
|
|
|
|
private int generation;
|
|
|
|
|
|
|
|
|
|
protected String password;
|
|
|
|
|
|
|
|
|
|
private String sessionId;
|
|
|
|
|
|
|
|
|
|
private XMPPConnection connection;
|
|
|
|
|
|
|
|
|
|
private TransportCandidate symmetric;
|
|
|
|
|
|
2007-02-07 03:35:27 +01:00
|
|
|
|
private CandidateEcho candidateEcho = null;
|
|
|
|
|
|
|
|
|
|
private Thread echoThread = null;
|
|
|
|
|
|
2007-01-04 18:25:30 +01:00
|
|
|
|
// Listeners for events
|
|
|
|
|
private final List<TransportResolverListener.Checker> listeners = new ArrayList();
|
|
|
|
|
|
2007-02-07 03:35:27 +01:00
|
|
|
|
public void addCandidateEcho() throws SocketException, UnknownHostException {
|
|
|
|
|
candidateEcho = new CandidateEcho(this);
|
|
|
|
|
echoThread = new Thread(candidateEcho);
|
|
|
|
|
echoThread.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void removeCandidateEcho() {
|
2007-02-23 17:07:23 +01:00
|
|
|
|
if (candidateEcho != null)
|
|
|
|
|
candidateEcho.cancel();
|
2007-02-07 03:35:27 +01:00
|
|
|
|
candidateEcho = null;
|
|
|
|
|
echoThread = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public CandidateEcho getCandidateEcho() {
|
|
|
|
|
return candidateEcho;
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-04 18:25:30 +01:00
|
|
|
|
public String getIp() {
|
|
|
|
|
return ip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the IP address.
|
|
|
|
|
*
|
|
|
|
|
* @param ip the IP address
|
|
|
|
|
*/
|
|
|
|
|
public void setIp(String ip) {
|
|
|
|
|
this.ip = ip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get local IP to bind to this candidate
|
|
|
|
|
*
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public String getLocalIp() {
|
|
|
|
|
return localIp == null ? ip : localIp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set local IP to bind to this candidate
|
|
|
|
|
*
|
|
|
|
|
* @param localIp
|
|
|
|
|
*/
|
|
|
|
|
public void setLocalIp(String localIp) {
|
|
|
|
|
this.localIp = localIp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the symetric candidate for this candidate if it exists.
|
|
|
|
|
*
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public TransportCandidate getSymmetric() {
|
|
|
|
|
return symmetric;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the symetric candidate for this candidate.
|
|
|
|
|
*
|
|
|
|
|
* @param symetric
|
|
|
|
|
*/
|
|
|
|
|
public void setSymmetric(TransportCandidate symetric) {
|
|
|
|
|
this.symmetric = symetric;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the password used by ICE or relayed candidate
|
|
|
|
|
*
|
|
|
|
|
* @return a password
|
|
|
|
|
*/
|
|
|
|
|
public String getPassword() {
|
|
|
|
|
return password;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the password used by ICE or relayed candidate
|
|
|
|
|
*
|
|
|
|
|
* @param password a password
|
|
|
|
|
*/
|
|
|
|
|
public void setPassword(String password) {
|
|
|
|
|
this.password = password;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the XMPPConnection use to send or receive this candidate
|
|
|
|
|
*
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public XMPPConnection getConnection() {
|
|
|
|
|
return connection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the XMPPConnection use to send or receive this candidate
|
|
|
|
|
*
|
|
|
|
|
* @param connection
|
|
|
|
|
*/
|
|
|
|
|
public void setConnection(XMPPConnection connection) {
|
|
|
|
|
this.connection = connection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the jingle<EFBFBD>s sessionId that is using this candidate
|
|
|
|
|
*
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public String getSessionId() {
|
|
|
|
|
return sessionId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the jingle<EFBFBD>s sessionId that is using this candidate
|
|
|
|
|
*
|
|
|
|
|
* @param sessionId
|
|
|
|
|
*/
|
|
|
|
|
public void setSessionId(String sessionId) {
|
|
|
|
|
this.sessionId = sessionId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Empty constructor
|
|
|
|
|
*/
|
|
|
|
|
public TransportCandidate() {
|
|
|
|
|
this(null, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructor with IP address and port
|
|
|
|
|
*
|
|
|
|
|
* @param ip The IP address.
|
|
|
|
|
* @param port The port number.
|
|
|
|
|
*/
|
|
|
|
|
public TransportCandidate(String ip, int port) {
|
|
|
|
|
this(ip, port, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructor with IP address and port
|
|
|
|
|
*
|
|
|
|
|
* @param ip The IP address.
|
|
|
|
|
* @param port The port number.
|
|
|
|
|
* @param generation The generation
|
|
|
|
|
*/
|
|
|
|
|
public TransportCandidate(String ip, int port, int generation) {
|
|
|
|
|
this.ip = ip;
|
|
|
|
|
this.port = port;
|
|
|
|
|
this.generation = generation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return true if the candidate is not valid.
|
|
|
|
|
*
|
|
|
|
|
* @return true if the candidate is null.
|
|
|
|
|
*/
|
|
|
|
|
public boolean isNull() {
|
|
|
|
|
if (ip == null) {
|
|
|
|
|
return true;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (ip.length() == 0) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return true;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (port < 0) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return true;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the port, or 0 for any port.
|
|
|
|
|
*
|
|
|
|
|
* @return the port or 0
|
|
|
|
|
*/
|
|
|
|
|
public int getPort() {
|
|
|
|
|
return port;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the port, using 0 for any port
|
|
|
|
|
*
|
|
|
|
|
* @param port the port
|
|
|
|
|
*/
|
|
|
|
|
public void setPort(int port) {
|
|
|
|
|
this.port = port;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the generation for a transportElement definition
|
|
|
|
|
*
|
|
|
|
|
* @return the generation
|
|
|
|
|
*/
|
|
|
|
|
public int getGeneration() {
|
|
|
|
|
return generation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the generation for a transportElement definition.
|
|
|
|
|
*
|
|
|
|
|
* @param generation the generation number
|
|
|
|
|
*/
|
|
|
|
|
public void setGeneration(int generation) {
|
|
|
|
|
this.generation = generation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the name used for identifying this transportElement method (optional)
|
|
|
|
|
*
|
|
|
|
|
* @return a name used for identifying this transportElement (ie,
|
|
|
|
|
* "myrtpvoice1")
|
|
|
|
|
*/
|
|
|
|
|
public String getName() {
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set a name for identifying this transportElement.
|
|
|
|
|
*
|
|
|
|
|
* @param name the name used for the transportElement
|
|
|
|
|
*/
|
|
|
|
|
public void setName(String name) {
|
|
|
|
|
this.name = name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* (non-Javadoc)
|
|
|
|
|
*
|
|
|
|
|
* @see java.lang.Object#equals(java.lang.Object)
|
|
|
|
|
*/
|
|
|
|
|
public boolean equals(Object obj) {
|
|
|
|
|
if (this == obj) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (obj == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (getClass() != obj.getClass()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
final TransportCandidate other = (TransportCandidate) obj;
|
|
|
|
|
if (generation != other.generation) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (getIp() == null) {
|
|
|
|
|
if (other.getIp() != null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (!getIp().equals(other.getIp())) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (getName() == null) {
|
|
|
|
|
if (other.getName() != null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (!getName().equals(other.getName())) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (getPort() != other.getPort()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if a transport candidate is usable. The transport resolver should
|
|
|
|
|
* check if the transport candidate the other endpoint has provided is
|
|
|
|
|
* usable.
|
|
|
|
|
* <p/>
|
2007-02-08 23:36:54 +01:00
|
|
|
|
* Subclasses should provide better methods if they can...
|
2007-01-04 18:25:30 +01:00
|
|
|
|
*/
|
2007-02-23 17:07:23 +01:00
|
|
|
|
public void check(final List<TransportCandidate> localCandidates) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
//TODO candidate is being checked trigger
|
|
|
|
|
//candidatesChecking.add(cand);
|
|
|
|
|
|
|
|
|
|
Thread checkThread = new Thread(new Runnable() {
|
|
|
|
|
public void run() {
|
|
|
|
|
boolean isUsable;
|
|
|
|
|
|
|
|
|
|
InetAddress candAddress;
|
|
|
|
|
try {
|
|
|
|
|
candAddress = InetAddress.getByName(getIp());
|
2007-02-08 23:36:54 +01:00
|
|
|
|
isUsable = candAddress.isReachable(TransportResolver.CHECK_TIMEOUT);
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
catch (Exception e) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
isUsable = false;
|
|
|
|
|
}
|
|
|
|
|
triggerCandidateChecked(isUsable);
|
|
|
|
|
|
|
|
|
|
//TODO candidate is being checked trigger
|
|
|
|
|
//candidatesChecking.remove(cand);
|
|
|
|
|
}
|
|
|
|
|
}, "Transport candidate check");
|
|
|
|
|
|
|
|
|
|
checkThread.setName("Transport candidate test");
|
|
|
|
|
checkThread.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Trigger a new candidate checked event.
|
|
|
|
|
*
|
|
|
|
|
* @param result The result.
|
|
|
|
|
*/
|
2007-02-23 17:07:23 +01:00
|
|
|
|
void triggerCandidateChecked(boolean result) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
|
|
|
|
|
for (TransportResolverListener.Checker trl : getListenersList()) {
|
|
|
|
|
trl.candidateChecked(this, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the list of listeners
|
|
|
|
|
*
|
|
|
|
|
* @return the list of listeners
|
|
|
|
|
*/
|
|
|
|
|
public List<TransportResolverListener.Checker> getListenersList() {
|
|
|
|
|
synchronized (listeners) {
|
|
|
|
|
return new ArrayList(listeners);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a transport resolver listener.
|
|
|
|
|
*
|
|
|
|
|
* @param li The transport resolver listener to be added.
|
|
|
|
|
*/
|
|
|
|
|
public void addListener(TransportResolverListener.Checker li) {
|
|
|
|
|
synchronized (listeners) {
|
|
|
|
|
listeners.add(li);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fixed transport candidate
|
|
|
|
|
*/
|
|
|
|
|
public static class Fixed extends TransportCandidate {
|
|
|
|
|
|
|
|
|
|
public Fixed() {
|
|
|
|
|
super();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructor with IP address and port
|
|
|
|
|
*
|
|
|
|
|
* @param ip The IP address.
|
|
|
|
|
* @param port The port number.
|
|
|
|
|
*/
|
|
|
|
|
public Fixed(String ip, int port) {
|
|
|
|
|
super(ip, port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructor with IP address and port
|
|
|
|
|
*
|
|
|
|
|
* @param ip The IP address.
|
|
|
|
|
* @param port The port number.
|
|
|
|
|
* @param generation The generation
|
|
|
|
|
*/
|
|
|
|
|
public Fixed(String ip, int port, int generation) {
|
|
|
|
|
super(ip, port, generation);
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-02-23 17:07:23 +01:00
|
|
|
|
|
2007-01-04 18:25:30 +01:00
|
|
|
|
/**
|
|
|
|
|
* Type-safe enum for the transportElement protocol
|
|
|
|
|
*/
|
|
|
|
|
public static class Protocol {
|
|
|
|
|
|
|
|
|
|
public static final Protocol UDP = new Protocol("udp");
|
|
|
|
|
|
|
|
|
|
public static final Protocol TCP = new Protocol("tcp");
|
|
|
|
|
|
|
|
|
|
public static final Protocol TCPACT = new Protocol("tcp-act");
|
|
|
|
|
|
|
|
|
|
public static final Protocol TCPPASS = new Protocol("tcp-pass");
|
|
|
|
|
|
|
|
|
|
public static final Protocol SSLTCP = new Protocol("ssltcp");
|
|
|
|
|
|
|
|
|
|
private String value;
|
|
|
|
|
|
|
|
|
|
public Protocol(String value) {
|
|
|
|
|
this.value = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String toString() {
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the Protocol constant associated with the String value.
|
|
|
|
|
*/
|
|
|
|
|
public static Protocol fromString(String value) {
|
|
|
|
|
if (value == null) {
|
|
|
|
|
return UDP;
|
|
|
|
|
}
|
|
|
|
|
value = value.toLowerCase();
|
|
|
|
|
if (value.equals("udp")) {
|
|
|
|
|
return UDP;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (value.equals("tcp")) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return TCP;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (value.equals("tcp-act")) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return TCPACT;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (value.equals("tcp-pass")) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return TCPPASS;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (value.equals("ssltcp")) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return SSLTCP;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return UDP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* (non-Javadoc)
|
|
|
|
|
*
|
|
|
|
|
* @see java.lang.Object#equals(java.lang.Object)
|
|
|
|
|
*/
|
|
|
|
|
public boolean equals(Object obj) {
|
|
|
|
|
if (this == obj) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (obj == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (getClass() != obj.getClass()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
final Protocol other = (Protocol) obj;
|
|
|
|
|
if (value == null) {
|
|
|
|
|
if (other.value != null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (!value.equals(other.value)) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return true if the protocol is not valid.
|
|
|
|
|
*
|
|
|
|
|
* @return true if the protocol is null
|
|
|
|
|
*/
|
|
|
|
|
public boolean isNull() {
|
|
|
|
|
if (value == null) {
|
|
|
|
|
return true;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (value.length() == 0) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return true;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Type-safe enum for the transportElement channel
|
|
|
|
|
*/
|
|
|
|
|
public static class Channel {
|
|
|
|
|
|
|
|
|
|
public static final Channel MYRTPVOICE = new Channel("myrtpvoice");
|
|
|
|
|
|
|
|
|
|
public static final Channel MYRTCPVOICE = new Channel("myrtcpvoice");
|
|
|
|
|
|
|
|
|
|
private String value;
|
|
|
|
|
|
|
|
|
|
public Channel(String value) {
|
|
|
|
|
this.value = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String toString() {
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the MediaChannel constant associated with the String value.
|
|
|
|
|
*/
|
|
|
|
|
public static Channel fromString(String value) {
|
|
|
|
|
if (value == null) {
|
|
|
|
|
return MYRTPVOICE;
|
|
|
|
|
}
|
|
|
|
|
value = value.toLowerCase();
|
|
|
|
|
if (value.equals("myrtpvoice")) {
|
|
|
|
|
return MYRTPVOICE;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (value.equals("tcp")) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return MYRTCPVOICE;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return MYRTPVOICE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* (non-Javadoc)
|
|
|
|
|
*
|
|
|
|
|
* @see java.lang.Object#equals(java.lang.Object)
|
|
|
|
|
*/
|
|
|
|
|
public boolean equals(Object obj) {
|
|
|
|
|
if (this == obj) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (obj == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (getClass() != obj.getClass()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
final Channel other = (Channel) obj;
|
|
|
|
|
if (value == null) {
|
|
|
|
|
if (other.value != null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (!value.equals(other.value)) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return true if the channel is not valid.
|
|
|
|
|
*
|
|
|
|
|
* @return true if the channel is null
|
|
|
|
|
*/
|
|
|
|
|
public boolean isNull() {
|
|
|
|
|
if (value == null) {
|
|
|
|
|
return true;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else if (value.length() == 0) {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return true;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-02-07 03:35:27 +01:00
|
|
|
|
|
|
|
|
|
public class CandidateEcho implements Runnable {
|
|
|
|
|
|
|
|
|
|
DatagramSocket socket = null;
|
|
|
|
|
byte password[] = null;
|
|
|
|
|
List<DatagramListener> listeners = new ArrayList<DatagramListener>();
|
|
|
|
|
boolean enabled = true;
|
2007-02-23 17:36:50 +01:00
|
|
|
|
boolean ended = false;
|
|
|
|
|
long tries = 10;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
|
|
|
|
|
public CandidateEcho(TransportCandidate candidate) throws UnknownHostException, SocketException {
|
2007-02-08 23:36:54 +01:00
|
|
|
|
this.socket = new DatagramSocket(candidate.getPort(), InetAddress.getByName("0.0.0.0"));
|
2007-02-07 03:35:27 +01:00
|
|
|
|
Random r = new Random();
|
|
|
|
|
password = longToByteArray((Math.abs(r.nextLong())));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void run() {
|
|
|
|
|
try {
|
2007-02-08 23:36:54 +01:00
|
|
|
|
System.out.println("Listening for ECHO: " + socket.getLocalAddress().getHostAddress() + ":" + socket.getLocalPort());
|
2007-02-07 03:35:27 +01:00
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
|
|
DatagramPacket packet = new DatagramPacket(new byte[8], 8);
|
|
|
|
|
|
|
|
|
|
socket.receive(packet);
|
|
|
|
|
|
2007-02-08 23:36:54 +01:00
|
|
|
|
System.out.println("ECHO Packet Received in: " + socket.getLocalAddress().getHostAddress() + ":" + socket.getLocalPort() + " From: " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
|
|
|
|
|
|
2007-02-07 03:35:27 +01:00
|
|
|
|
for (DatagramListener listener : listeners) {
|
|
|
|
|
listener.datagramReceived(packet);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
packet.setAddress(packet.getAddress());
|
|
|
|
|
packet.setPort(packet.getPort());
|
2007-02-23 17:36:50 +01:00
|
|
|
|
|
|
|
|
|
long delay = 1000 / tries / 2;
|
|
|
|
|
|
|
|
|
|
if (delay < 0) delay = 10;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
if (!Arrays.equals(packet.getData(), password))
|
2007-02-23 17:36:50 +01:00
|
|
|
|
for (int i = 0; i < tries; i++) {
|
2007-02-07 03:35:27 +01:00
|
|
|
|
socket.send(packet);
|
2007-02-23 17:36:50 +01:00
|
|
|
|
if (!enabled) break;
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(delay);
|
|
|
|
|
}
|
|
|
|
|
catch (InterruptedException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (UnknownHostException uhe) {
|
|
|
|
|
if (enabled) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (SocketException se) {
|
|
|
|
|
if (enabled) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (IOException ioe) {
|
|
|
|
|
if (enabled) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void cancel() {
|
|
|
|
|
this.enabled = false;
|
|
|
|
|
socket.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean test(final InetAddress address, final int port) {
|
2007-02-08 23:36:54 +01:00
|
|
|
|
return test(address, port, 2000);
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean test(final InetAddress address, final int port, int timeout) {
|
|
|
|
|
|
2007-02-23 17:36:50 +01:00
|
|
|
|
ended=false;
|
|
|
|
|
|
2007-02-07 03:35:27 +01:00
|
|
|
|
final TestResults testResults = new TestResults();
|
|
|
|
|
|
|
|
|
|
DatagramListener listener = new DatagramListener() {
|
|
|
|
|
public boolean datagramReceived(DatagramPacket datagramPacket) {
|
|
|
|
|
if (datagramPacket.getAddress().equals(address) && datagramPacket.getPort() == port) {
|
|
|
|
|
if (Arrays.equals(datagramPacket.getData(), password)) {
|
|
|
|
|
testResults.setResult(true);
|
2007-02-23 17:36:50 +01:00
|
|
|
|
ended = true;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
testResults.setResult(false);
|
2007-02-23 17:36:50 +01:00
|
|
|
|
ended = true;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.addListener(listener);
|
|
|
|
|
|
|
|
|
|
DatagramPacket packet = new DatagramPacket(password, password.length);
|
|
|
|
|
|
|
|
|
|
packet.setAddress(address);
|
|
|
|
|
packet.setPort(port);
|
|
|
|
|
|
2007-02-23 17:36:50 +01:00
|
|
|
|
long delay = timeout / tries;
|
|
|
|
|
if (delay < 0) delay = 10;
|
|
|
|
|
|
2007-02-07 03:35:27 +01:00
|
|
|
|
try {
|
2007-02-23 17:36:50 +01:00
|
|
|
|
for (int i = 0; i < tries; i++) {
|
2007-02-07 03:35:27 +01:00
|
|
|
|
socket.send(packet);
|
2007-02-23 17:36:50 +01:00
|
|
|
|
if (ended) break;
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(delay);
|
|
|
|
|
}
|
|
|
|
|
catch (InterruptedException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
catch (IOException e) {
|
2007-03-09 23:04:14 +01:00
|
|
|
|
// Do Nothing
|
2007-02-07 03:35:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
2007-02-08 23:36:54 +01:00
|
|
|
|
this.removeListener(listener);
|
|
|
|
|
|
2007-02-07 03:35:27 +01:00
|
|
|
|
return testResults.isReachable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void addListener(DatagramListener listener) {
|
|
|
|
|
listeners.add(listener);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void removeListener(DatagramListener listener) {
|
|
|
|
|
listeners.remove(listener);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class TestResults {
|
|
|
|
|
|
2007-03-09 23:04:14 +01:00
|
|
|
|
private boolean result=false;
|
2007-02-07 03:35:27 +01:00
|
|
|
|
|
|
|
|
|
public boolean isReachable() {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setResult(boolean result) {
|
|
|
|
|
this.result = result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static byte[] longToByteArray(long valor) {
|
|
|
|
|
byte[] result = new byte[8];
|
|
|
|
|
for (int i = 0; i < result.length; i++) {
|
|
|
|
|
result[7 - i] = (byte) (valor & 0xFF);
|
|
|
|
|
valor = valor >> 8;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-04 18:25:30 +01:00
|
|
|
|
}
|