2013-02-07 15:19:47 +01:00
|
|
|
/**
|
2014-02-19 10:38:30 +01:00
|
|
|
*
|
|
|
|
* Copyright the original author or authors
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
2014-02-17 18:57:38 +01:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
2013-02-07 15:19:47 +01:00
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
2013-02-07 15:19:47 +01:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
2013-02-07 15:19:47 +01:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2007-02-22 20:14:18 +01:00
|
|
|
*/
|
2014-08-20 00:25:57 +02:00
|
|
|
package org.jivesoftware.smackx.jingleold.nat;
|
2007-02-22 20:14:18 +01:00
|
|
|
|
2007-02-23 17:07:23 +01:00
|
|
|
import java.net.InetAddress;
|
|
|
|
import java.net.UnknownHostException;
|
|
|
|
import java.util.List;
|
2015-03-17 21:19:06 +01:00
|
|
|
import java.util.logging.Level;
|
2014-02-19 10:38:30 +01:00
|
|
|
import java.util.logging.Logger;
|
2008-10-30 22:20:29 +01:00
|
|
|
|
2007-02-22 20:14:18 +01:00
|
|
|
/**
|
|
|
|
* ICE Transport candidate.
|
2017-12-25 12:51:41 +01:00
|
|
|
*
|
2007-02-22 20:14:18 +01:00
|
|
|
* A candidate represents the possible transport for data interchange between
|
|
|
|
* the two endpoints.
|
|
|
|
*
|
|
|
|
* @author Thiago Camargo
|
|
|
|
*/
|
2018-10-31 16:06:31 +01:00
|
|
|
public final class ICECandidate extends TransportCandidate implements Comparable<ICECandidate> {
|
2007-02-22 20:14:18 +01:00
|
|
|
|
2017-02-07 22:02:40 +01:00
|
|
|
private static final Logger LOGGER = Logger.getLogger(ICECandidate.class.getName());
|
2008-10-30 22:20:29 +01:00
|
|
|
|
2017-02-07 22:02:40 +01:00
|
|
|
private String id; // An identification
|
2007-02-22 20:14:18 +01:00
|
|
|
|
|
|
|
private String username;
|
|
|
|
|
|
|
|
private int preference;
|
|
|
|
|
|
|
|
private Protocol proto;
|
|
|
|
|
|
|
|
private Channel channel;
|
|
|
|
|
|
|
|
private int network;
|
|
|
|
|
2007-04-16 17:44:55 +02:00
|
|
|
private Type type;
|
|
|
|
|
|
|
|
public enum Type {
|
|
|
|
relay, srflx, prflx, local, host
|
|
|
|
}
|
2007-02-22 20:14:18 +01:00
|
|
|
|
|
|
|
public ICECandidate() {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor with the basic elements of a transport definition.
|
|
|
|
*
|
|
|
|
* @param ip the IP address to use as a local address
|
|
|
|
* @param generation used to keep track of the candidates
|
|
|
|
* @param network used for diagnostics (used when the machine has
|
|
|
|
* several NICs)
|
|
|
|
* @param password user name, as it is used in ICE
|
|
|
|
* @param port the port at the candidate IP address
|
|
|
|
* @param username user name, as it is used in ICE
|
|
|
|
* @param preference preference for this transportElement, as it is used
|
|
|
|
* in ICE
|
|
|
|
* @param type type as defined in ICE-12
|
|
|
|
*/
|
|
|
|
public ICECandidate(String ip, int generation, int network,
|
2007-03-11 23:11:42 +01:00
|
|
|
String password, int port, String username,
|
2007-04-16 17:44:55 +02:00
|
|
|
int preference, Type type) {
|
2007-02-22 20:14:18 +01:00
|
|
|
super(ip, port, generation);
|
|
|
|
|
|
|
|
proto = Protocol.UDP;
|
|
|
|
channel = Channel.MYRTPVOICE;
|
|
|
|
|
|
|
|
this.network = network;
|
|
|
|
this.password = password;
|
|
|
|
this.username = username;
|
|
|
|
this.preference = preference;
|
|
|
|
this.type = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Get the ID.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @return the id
|
|
|
|
*/
|
|
|
|
public String getId() {
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Set the ID.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @param id the id to set
|
|
|
|
*/
|
|
|
|
public void setId(String id) {
|
|
|
|
this.id = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Get the protocol used for the transmission.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @return the protocol used for transmission
|
|
|
|
*/
|
|
|
|
public Protocol getProto() {
|
|
|
|
return proto;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Set the protocol for the transmission.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @param proto the protocol to use
|
|
|
|
*/
|
|
|
|
public void setProto(Protocol proto) {
|
|
|
|
this.proto = proto;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Get the network interface used for this connection.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @return the interface number
|
|
|
|
*/
|
|
|
|
public int getNetwork() {
|
|
|
|
return network;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Set the interface for this connection.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @param network the interface number
|
|
|
|
*/
|
|
|
|
public void setNetwork(int network) {
|
|
|
|
this.network = network;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Get the username for this transportElement in ICE.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @return a username string
|
|
|
|
*/
|
|
|
|
public String getUsername() {
|
|
|
|
return username;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Get the channel.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @return the channel associated
|
|
|
|
*/
|
|
|
|
public Channel getChannel() {
|
|
|
|
return channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Set the channel for this transportElement.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @param channel the new channel
|
|
|
|
*/
|
|
|
|
public void setChannel(Channel channel) {
|
|
|
|
this.channel = channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Set the username for this transportElement in ICE.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @param username the username used in ICE
|
|
|
|
*/
|
|
|
|
public void setUsername(String username) {
|
|
|
|
this.username = username;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Get the preference number for this transportElement.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @return the preference for this transportElement
|
|
|
|
*/
|
|
|
|
public int getPreference() {
|
|
|
|
return preference;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Set the preference order for this transportElement.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @param preference a number identifying the preference (as defined in
|
|
|
|
* ICE)
|
|
|
|
*/
|
|
|
|
public void setPreference(int preference) {
|
|
|
|
this.preference = preference;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Get the Candidate Type.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @return candidate Type
|
|
|
|
*/
|
2007-04-16 17:44:55 +02:00
|
|
|
public Type getType() {
|
2007-02-22 20:14:18 +01:00
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Set the Candidate Type.
|
2007-02-22 20:14:18 +01:00
|
|
|
*
|
|
|
|
* @param type candidate type.
|
|
|
|
*/
|
2007-04-16 17:44:55 +02:00
|
|
|
public void setType(Type type) {
|
2007-02-22 20:14:18 +01:00
|
|
|
this.type = type;
|
|
|
|
}
|
|
|
|
|
2007-02-23 17:07:23 +01:00
|
|
|
/**
|
|
|
|
* Check if a transport candidate is usable. The transport resolver should
|
|
|
|
* check if the transport candidate the other endpoint has provided is
|
|
|
|
* usable.
|
2017-12-25 12:51:41 +01:00
|
|
|
*
|
2007-02-23 17:07:23 +01:00
|
|
|
* ICE Candidate can check connectivity using UDP echo Test.
|
|
|
|
*/
|
2019-05-07 22:58:02 +02:00
|
|
|
@SuppressWarnings("UnusedVariable")
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2007-02-23 17:07:23 +01:00
|
|
|
public void check(final List<TransportCandidate> localCandidates) {
|
2017-11-20 08:53:19 +01:00
|
|
|
// TODO candidate is being checked trigger
|
|
|
|
// candidatesChecking.add(cand);
|
2007-02-23 17:07:23 +01:00
|
|
|
|
2007-03-12 20:09:19 +01:00
|
|
|
final ICECandidate checkingCandidate = this;
|
|
|
|
|
2007-02-23 17:07:23 +01:00
|
|
|
Thread checkThread = new Thread(new Runnable() {
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2007-02-23 17:07:23 +01:00
|
|
|
public void run() {
|
2007-03-11 23:11:42 +01:00
|
|
|
|
|
|
|
final TestResult result = new TestResult();
|
2007-02-23 17:07:23 +01:00
|
|
|
|
2009-09-08 15:12:28 +02:00
|
|
|
// Media Proxy don't have Echo features.
|
2017-12-13 23:10:11 +01:00
|
|
|
// If its a relayed candidate we assumed that is NOT Valid while other candidates still being checked.
|
2007-03-11 23:11:42 +01:00
|
|
|
// The negotiator MUST add then in the correct situations
|
2017-02-11 16:16:41 +01:00
|
|
|
if (getType().equals(Type.relay)) {
|
2007-03-11 23:11:42 +01:00
|
|
|
triggerCandidateChecked(false);
|
2007-02-23 20:51:21 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-03-11 23:11:42 +01:00
|
|
|
ResultListener resultListener = new ResultListener() {
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2007-03-12 20:09:19 +01:00
|
|
|
public void testFinished(TestResult testResult, TransportCandidate candidate) {
|
|
|
|
if (testResult.isReachable() && checkingCandidate.equals(candidate)) {
|
2007-03-11 23:11:42 +01:00
|
|
|
result.setResult(true);
|
2017-05-23 16:45:04 +02:00
|
|
|
LOGGER.fine("Candidate reachable: " + candidate.getIp() + ":" + candidate.getPort() + " from " + getIp() + ":" + getPort());
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2007-02-23 17:07:23 +01:00
|
|
|
for (TransportCandidate candidate : localCandidates) {
|
|
|
|
CandidateEcho echo = candidate.getCandidateEcho();
|
|
|
|
if (echo != null) {
|
2007-03-11 23:11:42 +01:00
|
|
|
if (candidate instanceof ICECandidate) {
|
|
|
|
ICECandidate iceCandidate = (ICECandidate) candidate;
|
2007-03-12 20:09:19 +01:00
|
|
|
if (iceCandidate.getType().equals(getType())) {
|
2007-03-11 23:11:42 +01:00
|
|
|
try {
|
|
|
|
echo.addResultListener(resultListener);
|
|
|
|
InetAddress address = InetAddress.getByName(getIp());
|
2007-03-12 20:09:19 +01:00
|
|
|
echo.testASync(checkingCandidate, getPassword());
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
catch (UnknownHostException e) {
|
2015-03-17 21:19:06 +01:00
|
|
|
LOGGER.log(Level.WARNING, "exception", e);
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
}
|
2007-02-23 17:07:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-11 23:11:42 +01:00
|
|
|
for (int i = 0; i < 10 && !result.isReachable(); i++)
|
|
|
|
try {
|
2014-02-19 10:38:30 +01:00
|
|
|
LOGGER.severe("ICE Candidate retry #" + i);
|
2007-03-13 02:39:18 +01:00
|
|
|
Thread.sleep(400);
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
catch (InterruptedException e) {
|
2015-03-17 21:19:06 +01:00
|
|
|
LOGGER.log(Level.WARNING, "exception", e);
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
2007-02-23 17:07:23 +01:00
|
|
|
|
2007-03-13 18:30:58 +01:00
|
|
|
for (TransportCandidate candidate : localCandidates) {
|
|
|
|
CandidateEcho echo = candidate.getCandidateEcho();
|
|
|
|
if (echo != null) {
|
|
|
|
echo.removeResultListener(resultListener);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-11 23:11:42 +01:00
|
|
|
triggerCandidateChecked(result.isReachable());
|
2007-02-23 17:07:23 +01:00
|
|
|
|
2017-11-20 08:53:19 +01:00
|
|
|
// TODO candidate is being checked trigger
|
|
|
|
// candidatesChecking.remove(cand);
|
2007-02-23 17:07:23 +01:00
|
|
|
}
|
|
|
|
}, "Transport candidate check");
|
|
|
|
|
|
|
|
checkThread.setName("Transport candidate test");
|
|
|
|
checkThread.start();
|
|
|
|
}
|
|
|
|
|
2015-04-03 19:15:35 +02:00
|
|
|
@Override
|
2007-02-22 20:14:18 +01:00
|
|
|
public boolean equals(Object obj) {
|
|
|
|
if (this == obj) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!super.equals(obj)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (getClass() != obj.getClass()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
final ICECandidate other = (ICECandidate) obj;
|
|
|
|
if (getChannel() == null) {
|
|
|
|
if (other.getChannel() != null) {
|
|
|
|
return false;
|
|
|
|
}
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
else if (!getChannel().equals(other.getChannel())) {
|
2007-02-22 20:14:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (getId() == null) {
|
|
|
|
if (other.getId() != null) {
|
|
|
|
return false;
|
|
|
|
}
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
else if (!getId().equals(other.getId())) {
|
2007-02-22 20:14:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (getNetwork() != other.getNetwork()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (getPassword() == null) {
|
|
|
|
if (other.getPassword() != null) {
|
|
|
|
return false;
|
|
|
|
}
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
else if (!getPassword().equals(other.password)) {
|
2007-02-22 20:14:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (getPreference() != other.getPreference()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (getProto() == null) {
|
|
|
|
if (other.getProto() != null) {
|
|
|
|
return false;
|
|
|
|
}
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
else if (!getProto().equals(other.getProto())) {
|
2007-02-22 20:14:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (getUsername() == null) {
|
|
|
|
if (other.getUsername() != null) {
|
|
|
|
return false;
|
|
|
|
}
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
else if (!getUsername().equals(other.getUsername())) {
|
2007-02-22 20:14:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
2007-03-13 18:30:58 +01:00
|
|
|
|
|
|
|
if (getIp() == null) {
|
|
|
|
if (other.getIp() != null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!getIp().equals(other.getIp())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getPort() != other.getPort()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getType() == null) {
|
|
|
|
if (other.getType() != null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!getType().equals(other.getType())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2007-02-22 20:14:18 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-04-03 19:15:35 +02:00
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
|
|
|
int res = 37;
|
|
|
|
res = 37 * res + (getChannel() == null ? 0 : getChannel().hashCode());
|
|
|
|
res = 37 * res + (getId() == null ? 0 : getId().hashCode());
|
|
|
|
res = 37 * res + getNetwork();
|
|
|
|
res = 37 * res + (getPassword() == null ? 0 : getPassword().hashCode());
|
|
|
|
res = 37 * res + getPreference();
|
|
|
|
res = 37 * res + (getProto() == null ? 0 : getProto().hashCode());
|
|
|
|
res = 37 * res + (getUsername() == null ? 0 : getUsername().hashCode());
|
|
|
|
res = 37 * res + (getIp() == null ? 0 : getIp().hashCode());
|
|
|
|
res = 37 * res + getPort();
|
|
|
|
res = 37 * res + (getType() == null ? 0 : getType().hashCode());
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2007-02-22 20:14:18 +01:00
|
|
|
public boolean isNull() {
|
|
|
|
if (super.isNull()) {
|
|
|
|
return true;
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
else if (getProto().isNull()) {
|
2007-02-22 20:14:18 +01:00
|
|
|
return true;
|
2007-03-11 23:11:42 +01:00
|
|
|
}
|
|
|
|
else if (getChannel().isNull()) {
|
2007-02-22 20:14:18 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare the to other Transport candidate.
|
|
|
|
*
|
|
|
|
* @param arg another Transport candidate
|
|
|
|
* @return a negative integer, zero, or a positive integer as this
|
|
|
|
* object is less than, equal to, or greater than the specified
|
|
|
|
* object
|
|
|
|
*/
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2017-02-07 22:02:40 +01:00
|
|
|
public int compareTo(ICECandidate arg) {
|
|
|
|
if (getPreference() < arg.getPreference()) {
|
|
|
|
return -1;
|
|
|
|
} else if (getPreference() > arg.getPreference()) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2007-03-12 20:09:19 +01:00
|
|
|
|
2007-02-22 20:14:18 +01:00
|
|
|
}
|
|
|
|
|