All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lastbamboo.common.ice.IceStunUdpPeer Maven / Gradle / Ivy

package org.lastbamboo.common.ice;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;

import org.littleshoot.mina.common.IoHandler;
import org.littleshoot.mina.common.IoServiceListener;
import org.littleshoot.mina.filter.codec.ProtocolCodecFactory;
import org.lastbamboo.common.stun.client.StunClient;
import org.lastbamboo.common.stun.client.UdpStunClient;
import org.lastbamboo.common.stun.server.StunServer;
import org.lastbamboo.common.stun.server.UdpStunServer;
import org.littleshoot.stun.stack.message.BindingRequest;
import org.littleshoot.stun.stack.message.StunMessage;
import org.littleshoot.stun.stack.transaction.StunTransactionTracker;
import org.littleshoot.util.CandidateProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * ICE STUN peer class for UDP.  This is basically just a STUN client for 
 * candidate gathering and a STUN server for processing incoming requests
 * from peer reflexive candidates.
 * 
 * NOTE: This class takes a little work to wrap one's head around as far as
 * the socket SO_REUSEADDRESS option goes.  Basically, we run both a client
 * and a server on the same port.  We're using MINA as the underlying IO
 * framework, though, and MINA has connectors and acceptors.  MINA calls 
 * connect on the underlying DatagramChannel for the connector.  When that call
 * is made, the channel will only accept incoming data from the host it's
 * connected to.  We also bind to that port with an accepting channel,
 * though, so incoming data from other hosts goes to the accepting channel.

* * Note also that the behavior for what packets go where differs by operating * system. On Windows, for example, packets from a host that has had * DatagramSocket.connect() called for that host will not necessarily go to * the "connected" host when there's another server socket bound to that port * (using SO_REUSEADDRESS). If you followed that sentence, this means that * both the connected "client" and the listening "server" message handling * code needs to be prepared to be prepared to handle any message. */ public class IceStunUdpPeer implements StunClient, StunServer { private final Logger m_log = LoggerFactory.getLogger(getClass()); private final StunClient m_stunClient; private final StunServer m_stunServer; private InetSocketAddress m_serverReflexiveAddress; /** * Creates a new ICE STUN UDP peer. * @param demuxingCodecFactory The class for interpreting the multiple * protocols on the wire -- STUN and whatever protocol it's negotiating * a connection for. * @param ioHandler The class for handling read and written * messages. * @param controlling Whether or not this agent is controlling. * @param transactionTracker The class for tracking STUN transactions. * @throws IOException If there's an error connecting the client or server. */ public IceStunUdpPeer( final ProtocolCodecFactory demuxingCodecFactory, final IoHandler ioHandler, final boolean controlling, final StunTransactionTracker transactionTracker, final CandidateProvider stunServerCandidateProvider) throws IOException { // We pass the IoHandler here because we need to be prepared to handle // STUN and protocol specific messages on all code bound to the same // local port. This is because different OSes handle // SO_REUSEADDRESS slightly differently, particularly with regards to // sockets that are "connected" to specific external hosts. We need // to use the same IoHandler for all of them to make sure it's // created here as well as the separate connectors created when we're // making connectivity consistent, with "all of them" meaning the STUN // client and server checks. this.m_stunClient = new UdpStunClient(transactionTracker, ioHandler, stunServerCandidateProvider); this.m_stunClient.connect(); this.m_serverReflexiveAddress = this.m_stunClient .getServerReflexiveAddress(); if (this.m_serverReflexiveAddress == null) { final String msg = "Could not get server reflexive address. " + "Did STUN server respond??"; m_log.error(msg); throw new IOException(msg); } // We also add whether we're controlling for thread // naming here just to make log reading easier. final String controllingString; if (controlling) { controllingString = "-Controlling"; } else { controllingString = "-Not-Controlling"; } // NOTE: We're starting the server here before external code has // had the chance to add listeners. In this case, it will be fine // because the caller cannot have sent the offer or answer until // the listeners are added (or SHOULD not have), so there's no way // of missing any relevant events. this.m_stunServer = new UdpStunServer(demuxingCodecFactory, ioHandler, controllingString); // Just bind to the same port as the client. // Note this only works because both the client and server are using // the SO_REUSEADDRESS option. this.m_stunServer.start(this.m_stunClient.getHostAddress()); m_log.debug("Started STUN CLIENT on local address: {}", this.m_stunClient.getHostAddress()); m_log.debug("Started STUN SERVER on local address: {}", this.m_stunServer.getBoundAddress()); } public void connect() throws IOException { // We don't do anything here because the client is already connected // in the constructor!! // this.m_stunClient.connect(); } public InetSocketAddress getHostAddress() { return this.m_stunClient.getHostAddress(); } public InetSocketAddress getRelayAddress() { return this.m_stunClient.getRelayAddress(); } public InetSocketAddress getServerReflexiveAddress() { m_log.info("Getting server reflexive address"); // We return the cached server reflexive address because we need to // get it before the "server side" UDP handler binds to the same // port, as it can "steal" incoming packets on Windows. return this.m_serverReflexiveAddress; } public InetAddress getStunServerAddress() { return this.m_stunClient.getStunServerAddress(); } public StunMessage write(final BindingRequest request, final InetSocketAddress remoteAddress) { // return this.m_stunClient.write(request, remoteAddress); m_log.error("Unsupported!!!!!!!"); throw new IllegalStateException("Not implemented."); } public StunMessage write(final BindingRequest request, final InetSocketAddress remoteAddress, final long rto) { // return this.m_stunClient.write(request, remoteAddress, rto); m_log.error("Unsupported!!!!!!!"); throw new IllegalStateException("Not implemented."); } public void start() { // We've already started the server for ICE. } public void start(final InetSocketAddress bindAddress) { // We've already started the server for ICE. } public InetSocketAddress getBoundAddress() { return this.m_stunServer.getBoundAddress(); } public void addIoServiceListener(final IoServiceListener serviceListener) { this.m_stunClient.addIoServiceListener(serviceListener); this.m_stunServer.addIoServiceListener(serviceListener); } public void close() { m_log.debug("Closing ICE UDP peer..."); this.m_stunClient.close(); this.m_stunServer.close(); } public boolean hostPortMapped() { // We don't currently do any mapping for UDP. return false; } public StunServer getStunServer() { return m_stunServer; } @Override public String toString() { return getClass().getSimpleName(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy