diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/nat/DatagramListener.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/nat/DatagramListener.java new file mode 100644 index 000000000..dc8c0a7ed --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/nat/DatagramListener.java @@ -0,0 +1,21 @@ +package org.jivesoftware.smackx.jingle.nat; + +import java.net.DatagramPacket; + +/** + * Listener for datagram packets received. + * + * @author Thiago Camargo + */ +public interface DatagramListener { + + /** + * Called when a datagram is received. If the method returns false, the + * packet MUST NOT be resent from the received Channel. + * + * @param datagramPacket the datagram packet received. + * @return ? + */ + public boolean datagramReceived(DatagramPacket datagramPacket); + +} \ No newline at end of file diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/nat/TransportCandidate.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/nat/TransportCandidate.java index 1a660a7c5..1fddc2a1a 100644 --- a/jingle/extension/source/org/jivesoftware/smackx/jingle/nat/TransportCandidate.java +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/nat/TransportCandidate.java @@ -54,13 +54,16 @@ package org.jivesoftware.smackx.jingle.nat; import org.jivesoftware.smack.XMPPConnection; -import java.net.InetAddress; +import java.io.IOException; +import java.net.*; import java.util.ArrayList; import java.util.List; +import java.util.Random; +import java.util.Arrays; /** * Transport candidate. - * + *

* A candidate represents the possible transport for data interchange between * the two endpoints. * @@ -87,9 +90,29 @@ public abstract class TransportCandidate { private TransportCandidate symmetric; + private CandidateEcho candidateEcho = null; + + private Thread echoThread = null; + // Listeners for events private final List listeners = new ArrayList(); + public void addCandidateEcho() throws SocketException, UnknownHostException { + candidateEcho = new CandidateEcho(this); + echoThread = new Thread(candidateEcho); + echoThread.start(); + } + + public void removeCandidateEcho() { + candidateEcho.cancel(); + candidateEcho = null; + echoThread = null; + } + + public CandidateEcho getCandidateEcho() { + return candidateEcho; + } + public String getIp() { return ip; } @@ -231,11 +254,14 @@ public abstract class TransportCandidate { public boolean isNull() { if (ip == null) { return true; - } else if (ip.length() == 0) { + } + else if (ip.length() == 0) { return true; - } else if (port < 0) { + } + else if (port < 0) { return true; - } else { + } + else { return false; } } @@ -318,14 +344,16 @@ public abstract class TransportCandidate { if (other.getIp() != null) { return false; } - } else if (!getIp().equals(other.getIp())) { + } + else if (!getIp().equals(other.getIp())) { return false; } if (getName() == null) { if (other.getName() != null) { return false; } - } else if (!getName().equals(other.getName())) { + } + else if (!getName().equals(other.getName())) { return false; } if (getPort() != other.getPort()) { @@ -356,7 +384,8 @@ public abstract class TransportCandidate { try { candAddress = InetAddress.getByName(getIp()); isUsable = true;//candAddress.isReachable(CHECK_TIMEOUT); - } catch (Exception e) { + } + catch (Exception e) { isUsable = false; } triggerCandidateChecked(isUsable); @@ -473,8 +502,8 @@ public abstract class TransportCandidate { * @param type type as defined in ICE-12 */ public Ice(String ip, int generation, int network, - String password, int port, String username, - int preference, String type) { + String password, int port, String username, + int preference, String type) { super(ip, port, generation); proto = Protocol.UDP; @@ -598,6 +627,7 @@ public abstract class TransportCandidate { /** * Get the Candidate Type + * * @return candidate Type */ public String getType() { @@ -606,6 +636,7 @@ public abstract class TransportCandidate { /** * Set the Candidate Type + * * @param type candidate type. */ public void setType(String type) { @@ -633,14 +664,16 @@ public abstract class TransportCandidate { if (other.getChannel() != null) { return false; } - } else if (!getChannel().equals(other.getChannel())) { + } + else if (!getChannel().equals(other.getChannel())) { return false; } if (getId() == null) { if (other.getId() != null) { return false; } - } else if (!getId().equals(other.getId())) { + } + else if (!getId().equals(other.getId())) { return false; } if (getNetwork() != other.getNetwork()) { @@ -650,7 +683,8 @@ public abstract class TransportCandidate { if (other.getPassword() != null) { return false; } - } else if (!getPassword().equals(other.password)) { + } + else if (!getPassword().equals(other.password)) { return false; } if (getPreference() != other.getPreference()) { @@ -660,14 +694,16 @@ public abstract class TransportCandidate { if (other.getProto() != null) { return false; } - } else if (!getProto().equals(other.getProto())) { + } + else if (!getProto().equals(other.getProto())) { return false; } if (getUsername() == null) { if (other.getUsername() != null) { return false; } - } else if (!getUsername().equals(other.getUsername())) { + } + else if (!getUsername().equals(other.getUsername())) { return false; } return true; @@ -676,9 +712,11 @@ public abstract class TransportCandidate { public boolean isNull() { if (super.isNull()) { return true; - } else if (getProto().isNull()) { + } + else if (getProto().isNull()) { return true; - } else if (getChannel().isNull()) { + } + else if (getChannel().isNull()) { return true; } return false; @@ -697,7 +735,8 @@ public abstract class TransportCandidate { TransportCandidate.Ice tc = (TransportCandidate.Ice) arg; if (getPreference() < tc.getPreference()) { return -1; - } else if (getPreference() > tc.getPreference()) { + } + else if (getPreference() > tc.getPreference()) { return 1; } } @@ -740,15 +779,20 @@ public abstract class TransportCandidate { value = value.toLowerCase(); if (value.equals("udp")) { return UDP; - } else if (value.equals("tcp")) { + } + else if (value.equals("tcp")) { return TCP; - } else if (value.equals("tcp-act")) { + } + else if (value.equals("tcp-act")) { return TCPACT; - } else if (value.equals("tcp-pass")) { + } + else if (value.equals("tcp-pass")) { return TCPPASS; - } else if (value.equals("ssltcp")) { + } + else if (value.equals("ssltcp")) { return SSLTCP; - } else { + } + else { return UDP; } } @@ -773,7 +817,8 @@ public abstract class TransportCandidate { if (other.value != null) { return false; } - } else if (!value.equals(other.value)) { + } + else if (!value.equals(other.value)) { return false; } return true; @@ -787,9 +832,11 @@ public abstract class TransportCandidate { public boolean isNull() { if (value == null) { return true; - } else if (value.length() == 0) { + } + else if (value.length() == 0) { return true; - } else { + } + else { return false; } } @@ -824,9 +871,11 @@ public abstract class TransportCandidate { value = value.toLowerCase(); if (value.equals("myrtpvoice")) { return MYRTPVOICE; - } else if (value.equals("tcp")) { + } + else if (value.equals("tcp")) { return MYRTCPVOICE; - } else { + } + else { return MYRTPVOICE; } } @@ -851,7 +900,8 @@ public abstract class TransportCandidate { if (other.value != null) { return false; } - } else if (!value.equals(other.value)) { + } + else if (!value.equals(other.value)) { return false; } return true; @@ -865,11 +915,143 @@ public abstract class TransportCandidate { public boolean isNull() { if (value == null) { return true; - } else if (value.length() == 0) { + } + else if (value.length() == 0) { return true; - } else { + } + else { return false; } } } + + public class CandidateEcho implements Runnable { + + DatagramSocket socket = null; + byte password[] = null; + List listeners = new ArrayList(); + boolean enabled = true; + + public CandidateEcho(TransportCandidate candidate) throws UnknownHostException, SocketException { + this.socket = new DatagramSocket(candidate.getPort(), InetAddress.getByName(candidate.getLocalIp())); + Random r = new Random(); + password = longToByteArray((Math.abs(r.nextLong()))); + } + + public void run() { + try { + while (true) { + + DatagramPacket packet = new DatagramPacket(new byte[8], 8); + + socket.receive(packet); + + for (DatagramListener listener : listeners) { + listener.datagramReceived(packet); + } + + packet.setAddress(packet.getAddress()); + packet.setPort(packet.getPort()); + if (!Arrays.equals(packet.getData(), password)) + for (int i = 0; i < 3; i++) + socket.send(packet); + } + } + 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) { + return test(address,port,2000); + } + + public boolean test(final InetAddress address, final int port, int timeout) { + + 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); + return true; + } + } + testResults.setResult(false); + return false; + } + }; + + this.addListener(listener); + + DatagramPacket packet = new DatagramPacket(password, password.length); + + packet.setAddress(address); + packet.setPort(port); + + try { + for (int i = 0; i < 3; i++) + socket.send(packet); + } + catch (IOException e) { + e.printStackTrace(); + } + + try { + Thread.sleep(timeout); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + + return testResults.isReachable(); + } + + public void addListener(DatagramListener listener) { + listeners.add(listener); + } + + public void removeListener(DatagramListener listener) { + listeners.remove(listener); + } + + public class TestResults { + + private boolean result; + + 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; + } + }