/* * This file is part of JSTUN. * * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights * reserved. * * This software is licensed under either the GNU Public License (GPL), * or the Apache 2.0 license. Copies of both license agreements are * included in this distribution. */ package de.javawi.jstun.test.demo.ice; import java.io.IOException; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.javawi.jstun.attribute.MessageAttributeException; import de.javawi.jstun.header.MessageHeaderParsingException; import de.javawi.jstun.test.DiscoveryInfo; import de.javawi.jstun.test.DiscoveryTest; import de.javawi.jstun.test.demo.ice.Candidate.CandidateType; import de.javawi.jstun.util.Address; import de.javawi.jstun.util.UtilityException; public class ICENegotiator { private static final Logger LOGGER = LoggerFactory.getLogger(ICENegotiator.class); // type preference must be an integer from 0 (=lowest) to 126 (=highest) (inclusive) private final static int LOCAL_PREFERENCE = 0; private final static int SERVER_REFLEXIVE_PREFERENCE = 42; private final static int PEER_REFLEXIVE_PREFERENCE = 84; private final static int RELAYED_PREFERENCE = 126; // component id private short componentId; private String stunServer = "jstun.javawi.de"; private int stunPort = 3478; // candidates HashSet<Candidate> candidates; public ICENegotiator(short componentId) { this.componentId = componentId; candidates = new HashSet<Candidate>(); } public ICENegotiator(String stunServer, int stunPort, short componentId) { this.stunServer = stunServer; this.stunPort = stunPort; this.componentId = componentId; candidates = new HashSet<Candidate>(); } /* * This method gathers candidate addresses as described in draft-ietf-mmusic-ice-12.txt Chapter 2.1 * Unfortunately, only the candidates of the direct attached network interfaces and server reflexive * addreses are gathered. So far, no support for relayed candidates is available (because I am not * aware of any STUN relay server). */ public void gatherCandidateAddresses() { try { candidates = new HashSet<Candidate>(); Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); while (ifaces.hasMoreElements()) { NetworkInterface iface = ifaces.nextElement(); Enumeration<InetAddress> iaddresses = iface.getInetAddresses(); while (iaddresses.hasMoreElements()) { InetAddress iaddress = iaddresses.nextElement(); if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress()) { try { // add host candidate Candidate local = new Candidate(new Address(iaddress.getAddress()), componentId); candidates.add(local); // add server reflexive address DiscoveryTest test = new DiscoveryTest(iaddress, 0, stunServer, stunPort); DiscoveryInfo di = test.test(); if (di.getPublicIP() != null) { Candidate cand = new Candidate(new Address(di.getPublicIP().getAddress()), CandidateType.ServerReflexive, componentId, local); cand.setComponentId(componentId); candidates.add(cand); } } catch (MessageHeaderParsingException mhpe) { LOGGER.debug("MessageHeaderParsingException while gathering candidate addresses."); // mhpe.printStackTrace(); } catch (MessageAttributeException mae) { LOGGER.debug("MessageAttributeException while gathering candidate addresses."); // mae.printStackTrace(); } catch (UtilityException ue) { LOGGER.debug("UtilityException while gathering candidate addresses."); // ue.printStackTrace(); } catch (UnknownHostException uhe) { LOGGER.debug("MessageHeaderParsingException while gathering candidate addresses."); // uhe.printStackTrace(); } catch (SocketException se) { LOGGER.debug("SocketException while gathering candidate addresses."); // se.printStackTrace(); } catch (IOException ioe) { LOGGER.debug("IOException while gathering candidate addresses."); // ioe.printStackTrace(); } } } } } catch (SocketException se) { LOGGER.debug("SocketException while enumatering over the network cards."); // se.printStackTrace(); } } public void prioritizeCandidates() { // count number of candidate types int numberLocal = 0; int numberServerReflexive = 0; int numberPeerReflexive = 0; int numberRelayed = 0; // count number of candidates of a particular type Iterator<Candidate> iterCandidates = candidates.iterator(); while (iterCandidates.hasNext()) { Candidate cand = iterCandidates.next(); CandidateType type = cand.getCandidateType(); if (type == CandidateType.Local) numberLocal++; else if (type == CandidateType.ServerReflexive) numberServerReflexive++; else if (type == CandidateType.PeerReflexive) numberPeerReflexive++; else if (type == CandidateType.Relayed) numberRelayed++; } // assign priorities iterCandidates = candidates.iterator(); while (iterCandidates.hasNext()) { int typeValue = 0; int localValue = 0; int componentValue = 0; Candidate cand = iterCandidates.next(); CandidateType type = cand.getCandidateType(); if (type == CandidateType.Local) { typeValue = LOCAL_PREFERENCE; localValue = numberLocal--; } else if (type == CandidateType.ServerReflexive) { typeValue = SERVER_REFLEXIVE_PREFERENCE; localValue = numberServerReflexive--; } else if (type == CandidateType.PeerReflexive) { typeValue = PEER_REFLEXIVE_PREFERENCE; localValue = numberPeerReflexive--; } else if (type == CandidateType.Relayed) { typeValue = RELAYED_PREFERENCE; localValue = numberRelayed--; } componentValue = cand.getComponentId(); int priority = ((2 ^ 24) * typeValue) + ((2 ^ 8) * localValue) + componentValue; cand.setPriority(priority); } } public List<Candidate> getSortedCandidates() { Vector<Candidate> sortedCandidates = new Vector<Candidate>(candidates); Collections.sort(sortedCandidates); return sortedCandidates; } public static void main(String args[]) { ICENegotiator cc = new ICENegotiator((short) 1); // gather candidates cc.gatherCandidateAddresses(); // priorize candidates cc.prioritizeCandidates(); // get SortedCandidates List<Candidate> sortedCandidates = cc.getSortedCandidates(); // sent sorted candidate addresses to peer over SDP // received sorted candidate addresses of peer over SDP } }