/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * 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 net.java.sip.communicator.impl.protocol.jabber; import java.util.*; import net.java.sip.communicator.util.*; import org.ice4j.*; import org.ice4j.ice.*; import org.ice4j.ice.harvest.*; import org.ice4j.socket.*; import org.jivesoftware.smack.*; import org.xmpp.jnodes.smack.*; /** * Implements a CandidateHarvester which gathers Candidates * for a specified {@link Component} using Jingle Nodes as defined in * XEP 278 "Jingle Relay Nodes". * * @author Sebastien Vincent */ public class JingleNodesHarvester extends CandidateHarvester { /** * The Logger used by the JingleNodesHarvester class and * its instances for logging output. */ private static final Logger logger = Logger.getLogger(JingleNodesHarvester.class.getName()); /** * XMPP connection. */ private SmackServiceNode serviceNode = null; /** * JingleNodes relay allocate two address/port couple for us. Due to the * architecture of Ice4j that harvest address for each component, we store * the second address/port couple. */ private TransportAddress localAddressSecond = null; /** * JingleNodes relay allocate two address/port couple for us. Due to the * architecture of Ice4j that harvest address for each component, we store * the second address/port couple. */ private TransportAddress relayedAddressSecond = null; /** * Constructor. * * @param serviceNode the SmackServiceNode */ public JingleNodesHarvester(SmackServiceNode serviceNode) { this.serviceNode = serviceNode; } /** * Gathers Jingle Nodes candidates for all host Candidates that are * already present in the specified component. This method relies * on the specified component to already contain all its host * candidates so that it would resolve them. * * @param component the {@link Component} that we'd like to gather candidate * Jingle Nodes Candidates for * @return the LocalCandidates gathered by this * CandidateHarvester */ @Override public synchronized Collection harvest(Component component) { logger.info("harvest Jingle Nodes"); Collection candidates = new HashSet(); String ip = null; int port = -1; /* if we have already a candidate (RTCP) allocated, get it */ if(localAddressSecond != null && relayedAddressSecond != null) { LocalCandidate candidate = createJingleNodesCandidate( relayedAddressSecond, component, localAddressSecond); //try to add the candidate to the component and then only add it to //the harvest not redundant (not sure how it could be red. but ...) if( component.addLocalCandidate(candidate)) { candidates.add(candidate); } localAddressSecond = null; relayedAddressSecond = null; return candidates; } XMPPConnection conn = serviceNode.getConnection(); JingleChannelIQ ciq = null; if (serviceNode != null) { final TrackerEntry preferred = serviceNode.getPreferedRelay(); if (preferred != null) { ciq = SmackServiceNode.getChannel(conn, preferred.getJid()); } } if (ciq != null && ciq.getRemoteport() > 0) { ip = ciq.getHost(); port = ciq.getRemoteport(); if(logger.isInfoEnabled()) { logger.info("JN relay: " + ip + " remote port:" + port + " local port: " + ciq.getLocalport()); } /* RTP */ TransportAddress relayedAddress = new TransportAddress(ip, port, Transport.UDP); TransportAddress localAddress = new TransportAddress(ip, ciq.getLocalport(), Transport.UDP); LocalCandidate local = createJingleNodesCandidate( relayedAddress, component, localAddress); /* RTCP */ relayedAddressSecond = new TransportAddress(ip, port + 1,Transport.UDP); localAddressSecond = new TransportAddress(ip, ciq.getLocalport() + 1, Transport.UDP); //try to add the candidate to the component and then only add it to //the harvest not redundant (not sure how it could be red. but ...) if( component.addLocalCandidate(local)) { candidates.add(local); } } return candidates; } /** * Creates a new JingleNodesRelayedCandidate instance which is to * represent a specific TransportAddress. * * @param transportAddress the TransportAddress allocated by the * relay * @param component the Component for which the candidate will be * added * @param localEndPoint TransportAddress of the Jingle Nodes relay * where we will send our packet. * @return a new JingleNodesRelayedCandidate instance which * represents the specified TransportAddress */ protected JingleNodesCandidate createJingleNodesCandidate( TransportAddress transportAddress, Component component, TransportAddress localEndPoint) { JingleNodesCandidate cand = null; try { cand = new JingleNodesCandidate(transportAddress, component, localEndPoint); IceSocketWrapper stunSocket = cand.getStunSocket(null); cand.getStunStack().addSocket(stunSocket); } catch(Throwable e) { logger.debug( "Exception occurred when creating JingleNodesCandidate: " + e); } return cand; } }