/** * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ package org.jivesoftware.smackx.jingle.nat; import de.javawi.jstun.test.demo.StunServer; import de.javawi.jstun.test.demo.ice.Candidate; import de.javawi.jstun.test.demo.ice.ICENegociator; import de.javawi.jstun.util.UtilityException; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.test.SmackTestCase; import org.jivesoftware.smackx.jingle.JingleManager; import org.jivesoftware.smackx.jingle.JingleSession; import org.jivesoftware.smackx.jingle.JingleSessionRequest; import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener; import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener; import org.jivesoftware.smackx.jingle.media.JingleMediaManager; import org.jivesoftware.smackx.jingle.media.PayloadType; import org.jivesoftware.smackx.jingle.mediaimpl.test.TestMediaManager; import org.jivesoftware.smackx.jingle.nat.STUNResolver.STUNService; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; /** * Test the STUN IP resolver. * * @author Thiago Camargo */ public class STUNResolverTest extends SmackTestCase { // Counter management public STUNResolverTest(final String arg) { super(arg); } private int counter; private final Object mutex = new Object(); private void resetCounter() { synchronized (mutex) { counter = 0; } } private void incCounter() { synchronized (mutex) { counter++; } } private int valCounter() { int val; synchronized (mutex) { val = counter; } return val; } /** * Test for getPreferredCandidate() * * @throws Exception if an exception occurs. */ public void testGetPreferredCandidate() throws Exception { int highestPref = 100; TransportCandidate cand1 = new ICECandidate("192.168.2.1", 3, 2, "password", 3468, "username1", 1, ICECandidate.Type.prflx); TransportCandidate cand2 = new ICECandidate("192.168.5.1", 2, 10, "password", 3469, "username2", 15, ICECandidate.Type.prflx); TransportCandidate candH = new ICECandidate("192.168.2.11", 1, 2, "password", 3468, "usernameH", highestPref, ICECandidate.Type.prflx); TransportCandidate cand3 = new ICECandidate("192.168.2.10", 2, 10, "password", 3469, "username3", 2, ICECandidate.Type.prflx); TransportCandidate cand4 = new ICECandidate("192.168.4.1", 3, 2, "password", 3468, "username4", 78, ICECandidate.Type.prflx); STUNResolver stunResolver = new STUNResolver() { }; stunResolver.addCandidate(cand1); stunResolver.addCandidate(cand2); stunResolver.addCandidate(candH); stunResolver.addCandidate(cand3); stunResolver.addCandidate(cand4); assertEquals(stunResolver.getPreferredCandidate(), candH); } /** * Test for getPreferredCandidate() * * @throws Exception if an exception occurs. */ public void testGetPreferredCandidateICE() throws Exception { int highestPref = 100; TransportCandidate cand1 = new ICECandidate("192.168.2.1", 3, 2, "password", 3468, "username1", 1, ICECandidate.Type.prflx); TransportCandidate cand2 = new ICECandidate("192.168.5.1", 2, 10, "password", 3469, "username2", 15, ICECandidate.Type.prflx); TransportCandidate candH = new ICECandidate("192.168.2.11", 1, 2, "password", 3468, "usernameH", highestPref, ICECandidate.Type.prflx); TransportCandidate cand3 = new ICECandidate("192.168.2.10", 2, 10, "password", 3469, "username3", 2, ICECandidate.Type.prflx); TransportCandidate cand4 = new ICECandidate("192.168.4.1", 3, 2, "password", 3468, "username4", 78, ICECandidate.Type.prflx); ICEResolver iceResolver = new ICEResolver(getConnection(0), "stun.xten.net", 3478) { }; iceResolver.addCandidate(cand1); iceResolver.addCandidate(cand2); iceResolver.addCandidate(candH); iceResolver.addCandidate(cand3); iceResolver.addCandidate(cand4); assertEquals(iceResolver.getPreferredCandidate(), candH); } /** * Test priority generated by STUN lib * * @throws Exception if an exception occurs. */ public void testICEPriority() throws Exception { String first = ""; for (int i = 0; i < 100; i++) { ICENegociator cc = new ICENegociator((short) 1); // gather candidates cc.gatherCandidateAddresses(); // priorize candidates cc.prioritizeCandidates(); // get SortedCandidates for (Candidate candidate : cc.getSortedCandidates()) { short nicNum = 0; try { Enumeration nics = NetworkInterface.getNetworkInterfaces(); short tempNic = 0; NetworkInterface nic = NetworkInterface.getByInetAddress(candidate.getAddress().getInetAddress()); while(nics.hasMoreElements()) { NetworkInterface checkNIC = nics.nextElement(); if (checkNIC.equals(nic)) { nicNum = tempNic; break; } i++; } } catch (SocketException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { TransportCandidate transportCandidate = new ICECandidate(candidate.getAddress().getInetAddress() .getHostAddress(), 1, nicNum, "1", candidate.getPort(), "1", candidate.getPriority(), ICECandidate.Type.prflx); transportCandidate.setLocalIp(candidate.getBase().getAddress().getInetAddress().getHostAddress()); System.out.println("C: " + candidate.getAddress().getInetAddress() + "|" + candidate.getBase().getAddress().getInetAddress() + " p:" + candidate.getPriority()); } catch (UtilityException e) { LOGGER.log(Level.WARNING, "exception", e); } catch (UnknownHostException e) { LOGGER.log(Level.WARNING, "exception", e); } } Candidate candidate = cc.getSortedCandidates().get(0); String temp = "C: " + candidate.getAddress().getInetAddress() + "|" + candidate.getBase().getAddress().getInetAddress() + " p:" + candidate.getPriority(); if (first.equals("")) first = temp; assertEquals(first, temp); first = temp; } } /** * Test for loadSTUNServers() * * @throws Exception if an exception occurs. */ public void testLoadSTUNServers() throws Exception { STUNResolver stunResolver = new STUNResolver() { }; ArrayList stunServers = stunResolver.loadSTUNServers(); assertTrue(stunServers.size() > 0); System.out.println(stunServers.size() + " servers loaded"); } public void testGetSTUNServer() { System.out.println(STUN.serviceAvailable(getConnection(0))); STUN stun = STUN.getSTUNServer(getConnection(0)); for (STUN.StunServerAddress stunServerAddress : stun.getServers()) System.out.println(stunServerAddress.getServer() + ":" + stunServerAddress.getPort()); System.out.println(stun.getPublicIp()); } /** * Test for resolve() * * @throws Exception if an exception occurs. */ public void testResolve() throws Exception { final STUNResolver stunResolver = new STUNResolver() { }; stunResolver.addListener(new TransportResolverListener.Resolver() { public void candidateAdded(final TransportCandidate cand) { incCounter(); String addr = cand.getIp(); int port = cand.getPort(); System.out.println("Addr: " + addr + " port:" + port); } public void init() { System.out.println("Resolution started"); } public void end() { System.out.println("Resolution finished"); } }); try { stunResolver.initializeAndWait(); Thread.sleep(55000); assertTrue(valCounter() > 0); } catch (Exception e) { LOGGER.log(Level.WARNING, "exception", e); } } /** * Generate a list of payload types * * @return A testing list */ private ArrayList getTestPayloads1() { ArrayList result = new ArrayList(); result.add(new PayloadType.Audio(34, "supercodec-1", 2, 14000)); result.add(new PayloadType.Audio(56, "supercodec-2", 1, 44000)); result.add(new PayloadType.Audio(36, "supercodec-3", 2, 28000)); result.add(new PayloadType.Audio(45, "supercodec-4", 1, 98000)); return result; } private ArrayList getTestPayloads2() { ArrayList result = new ArrayList(); result.add(new PayloadType.Audio(27, "supercodec-3", 2, 28000)); result.add(new PayloadType.Audio(56, "supercodec-2", 1, 44000)); result.add(new PayloadType.Audio(32, "supercodec-4", 1, 98000)); result.add(new PayloadType.Audio(34, "supercodec-1", 2, 14000)); return result; } /** * This is a simple test where the user_2 rejects the Jingle session. */ public void testSTUNJingleSession() { resetCounter(); try { TransportResolver tr1 = new STUNResolver() { }; TransportResolver tr2 = new STUNResolver() { }; // Explicit resolution tr1.resolve(null); tr2.resolve(null); STUNTransportManager stm0 = new STUNTransportManager(); TestMediaManager tmm0 = new TestMediaManager(stm0); tmm0.setPayloads(getTestPayloads1()); List trl0 = new ArrayList(); trl0.add(tmm0); STUNTransportManager stm1 = new STUNTransportManager(); TestMediaManager tmm1 = new TestMediaManager(stm1); tmm1.setPayloads(getTestPayloads2()); List trl1 = new ArrayList(); trl1.add(tmm1); final JingleManager man0 = new JingleManager(getConnection(0), trl0); final JingleManager man1 = new JingleManager(getConnection(1), trl1); man1.addJingleSessionRequestListener(new JingleSessionRequestListener() { /** * Called when a new session request is detected */ public void sessionRequested(final JingleSessionRequest request) { System.out.println("Session request detected, from " + request.getFrom() + ": accepting."); // We accept the request JingleSession session1; try { session1 = request.accept(); session1.addListener(new JingleSessionListener() { public void sessionClosed(String reason, JingleSession jingleSession) { } public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { } public void sessionDeclined(String reason, JingleSession jingleSession) { } public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) { incCounter(); System.out.println("Responder: the session is fully established."); System.out.println("+ Payload Type: " + pt.getId()); System.out.println("+ Local IP/port: " + lc.getIp() + ":" + lc.getPort()); System.out.println("+ Remote IP/port: " + rc.getIp() + ":" + rc.getPort()); } public void sessionRedirected(String redirection, JingleSession jingleSession) { } public void sessionMediaReceived(JingleSession jingleSession, String participant) { // Do Nothing } }); session1.startIncoming(); } catch (XMPPException e) { LOGGER.log(Level.WARNING, "exception", e); } } }); // Session 0 starts a request System.out.println("Starting session request, to " + getFullJID(1) + "..."); JingleSession session0 = man0.createOutgoingJingleSession(getFullJID(1)); session0.addListener(new JingleSessionListener() { public void sessionClosed(String reason, JingleSession jingleSession) { } public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { } public void sessionDeclined(String reason, JingleSession jingleSession) { } public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) { incCounter(); System.out.println("Initiator: the session is fully established."); System.out.println("+ Payload Type: " + pt.getId()); System.out.println("+ Local IP/port: " + lc.getIp() + ":" + lc.getPort()); System.out.println("+ Remote IP/port: " + rc.getIp() + ":" + rc.getPort()); } public void sessionMediaReceived(JingleSession jingleSession, String participant) { // Do Nothing } public void sessionRedirected(String redirection, JingleSession jingleSession) { } }); session0.startOutgoing(); Thread.sleep(60000); assertTrue(valCounter() == 2); } catch (Exception e) { LOGGER.log(Level.WARNING, "exception", e); fail("An error occurred with Jingle"); } } protected int getMaxConnections() { return 2; } }