/*
* 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 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 ICENegociator {
// 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;
public static void main(String args[]) {
final ICENegociator cc = new ICENegociator((short) 1);
// gather candidates
cc.gatherCandidateAddresses();
// priorize candidates
cc.prioritizeCandidates();
// get SortedCandidates
// final List<Candidate> sortedCandidates = cc.getSortedCandidates();
// sent sorted candidate addresses to peer over SDP
// received sorted candidate addresses of peer over SDP
}
// component id
private final short componentId;
private String stunServer = "jstun.javawi.de";
private int stunPort = 3478;
// candidates
HashSet<Candidate> candidates;
public ICENegociator(short componentId) {
this.componentId = componentId;
candidates = new HashSet<Candidate>();
}
public ICENegociator(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>();
final Enumeration<NetworkInterface> ifaces = NetworkInterface
.getNetworkInterfaces();
while (ifaces.hasMoreElements()) {
final NetworkInterface iface = ifaces.nextElement();
final Enumeration<InetAddress> iaddresses = iface
.getInetAddresses();
while (iaddresses.hasMoreElements()) {
final InetAddress iaddress = iaddresses.nextElement();
if (!iaddress.isLoopbackAddress()
&& !iaddress.isLinkLocalAddress()) {
try {
// add host candidate
final Candidate local = new Candidate(new Address(
iaddress.getAddress()), componentId);
candidates.add(local);
// add server reflexive address
final DiscoveryTest test = new DiscoveryTest(
iaddress, stunServer, stunPort);
final DiscoveryInfo di = test.test();
if (di.getPublicIP() != null) {
final Candidate cand = new Candidate(
new Address(di.getPublicIP()
.getAddress()),
CandidateType.ServerReflexive,
componentId, local);
cand.setComponentId(componentId);
candidates.add(cand);
}
} catch (final MessageHeaderParsingException mhpe) {
mhpe.printStackTrace();
} catch (final MessageAttributeException mae) {
mae.printStackTrace();
} catch (final UtilityException ue) {
ue.printStackTrace();
} catch (final UnknownHostException uhe) {
uhe.printStackTrace();
} catch (final SocketException se) {
se.printStackTrace();
} catch (final IOException ioe) {
ioe.printStackTrace();
}
}
}
}
} catch (final SocketException se) {
se.printStackTrace();
}
}
public List<Candidate> getSortedCandidates() {
final Vector<Candidate> sortedCandidates = new Vector<Candidate>(
candidates);
Collections.sort(sortedCandidates);
return sortedCandidates;
}
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()) {
final Candidate cand = iterCandidates.next();
final 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;
final Candidate cand = iterCandidates.next();
final 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();
final int priority = ((2 ^ 24) * typeValue)
+ ((2 ^ 8) * localValue) + componentValue;
cand.setPriority(priority);
}
}
}