/dumbhippo/trunk/openfire/src/java/org/jivesoftware/openfire/stun/STUNService.java
Java | 430 lines | 296 code | 54 blank | 80 comment | 54 complexity | 55d5c27ddf0a21b42052b57bca9cb4b3 MD5 | raw file
- /**
- * Copyright (C) 2007 Jive Software. All rights reserved.
- *
- * This software is published under the terms of the GNU Public License (GPL),
- * a copy of which is included in this distribution.
- */
-
- package org.jivesoftware.openfire.stun;
-
- import de.javawi.jstun.test.demo.StunServer;
- import org.dom4j.Element;
- import org.jivesoftware.util.JiveGlobals;
- import org.jivesoftware.util.Log;
- import org.jivesoftware.util.PropertyEventDispatcher;
- import org.jivesoftware.util.PropertyEventListener;
- import org.jivesoftware.openfire.*;
- import org.jivesoftware.openfire.handler.IQHandler;
- import org.jivesoftware.openfire.auth.UnauthorizedException;
- import org.jivesoftware.openfire.container.BasicModule;
- import org.xmpp.packet.IQ;
- import org.xmpp.packet.PacketError;
-
- import java.net.InetAddress;
- import java.net.NetworkInterface;
- import java.net.SocketException;
- import java.net.UnknownHostException;
- import java.util.*;
-
- /**
- * STUN server and service module. It provides address discovery for p2p sessions to be
- * used for media transmission and receiving of UDP packets. It's especially useful for
- * clients behind NAT devices.
- *
- * @author Thiago Camargo
- */
- public class STUNService extends BasicModule {
-
- private static final String ELEMENT_NAME = "stun";
- private static final String NAMESPACE = "google:jingleinfo";
- private static final String DEFAULT_EXTERNAL_ADDRESSES =
- "stun.xten.net:3478;" +
- "jivesoftware.com:3478;" +
- "igniterealtime.org:3478;" +
- "stun.fwdnet.net:3478";
-
- private IQHandler stunIQHandler;
- private StunServer stunServer = null;
- private boolean enabled = false;
- private boolean localEnabled = false;
-
- private String primaryAddress = null;
- private String secondaryAddress = null;
- private int primaryPort;
- private int secondaryPort;
-
- private List<StunServerAddress> externalServers = null;
-
- /**
- * Constructs a new STUN Service
- */
- public STUNService() {
- super("STUN Service");
- }
-
- public void destroy() {
- super.destroy();
- stunServer = null;
- }
-
- public void initialize(XMPPServer server) {
- super.initialize(server);
-
- this.enabled = JiveGlobals.getBooleanProperty("stun.enabled", true);
-
- primaryAddress = JiveGlobals.getProperty("stun.address.primary");
- secondaryAddress = JiveGlobals.getProperty("stun.address.secondary");
-
- String addresses = JiveGlobals.getProperty("stun.external.addresses");
- // If no custom external addresses are defined, use the defaults.
- if (addresses == null) {
- addresses = DEFAULT_EXTERNAL_ADDRESSES;
- }
- externalServers = getStunServerAddresses(addresses);
-
- primaryPort = JiveGlobals.getIntProperty("stun.port.primary", 3478);
- secondaryPort = JiveGlobals.getIntProperty("stun.port.secondary", 3479);
-
- this.localEnabled = JiveGlobals.getBooleanProperty("stun.local.enabled", false);
- // If the local server is supposed to be enabled, ensure that primary and secondary
- // addresses are defined.
- if (localEnabled) {
- if (primaryAddress == null || secondaryAddress == null) {
- Log.warn("STUN server cannot be enabled. Primary and secondary addresses must be defined.");
- localEnabled = false;
- }
- }
-
- // Add listeners for STUN service being enabled and disabled via manual property changes.
- PropertyEventDispatcher.addListener(new PropertyEventListener() {
-
- public void propertySet(String property, Map<String, Object> params) {
- if (property.equals("stun.enabled")) {
- boolean oldValue = enabled;
- enabled = JiveGlobals.getBooleanProperty("stun.enabled", true);
- //
- if (enabled && !oldValue) {
- startSTUNService();
- } else if (!enabled && oldValue) {
- stop();
- }
- } else if (property.equals("stun.local.enabled")) {
- localEnabled = JiveGlobals.getBooleanProperty("stun.local.enabled", false);
- }
- }
-
- public void propertyDeleted(String property, Map<String, Object> params) {
- if (property.equals("stun.enabled")) {
- enabled = true;
- } else if (property.equals("stun.local.enabled")) {
- localEnabled = false;
- }
- }
-
- public void xmlPropertySet(String property, Map<String, Object> params) {
- // Ignore.
- }
-
- public void xmlPropertyDeleted(String property, Map<String, Object> params) {
- // Ignore.
- }
- });
- }
-
- public void start() {
- if (isEnabled()) {
- startSTUNService();
- if (isLocalEnabled()) {
- startLocalServer();
- }
- }
- }
-
- private void startLocalServer() {
- try {
- InetAddress primary = InetAddress.getByName(primaryAddress);
- InetAddress secondary = InetAddress.getByName(secondaryAddress);
-
- if (primary != null && secondary != null) {
- stunServer = new StunServer(primaryPort, primary, secondaryPort, secondary);
- stunServer.start();
- } else {
- setLocalEnabled(false);
- }
- }
- catch (SocketException e) {
- Log.error("Disabling STUN server", e);
- setLocalEnabled(false);
- }
- catch (UnknownHostException e) {
- Log.error("Disabling STUN server", e);
- setLocalEnabled(false);
- }
- }
-
- private void startSTUNService() {
- XMPPServer server = XMPPServer.getInstance();
- // Register the STUN feature in disco.
- server.getIQDiscoInfoHandler().addServerFeature(NAMESPACE);
- // Add an IQ handler.
- stunIQHandler = new STUNIQHandler();
- server.getIQRouter().addHandler(stunIQHandler);
- }
-
- private void stopSTUNService() {
- XMPPServer server = XMPPServer.getInstance();
- server.getIQDiscoInfoHandler().removeServerFeature(NAMESPACE);
- if (stunIQHandler != null) {
- server.getIQRouter().removeHandler(stunIQHandler);
- stunIQHandler = null;
- }
- }
-
- public void stop() {
- super.stop();
- this.enabled = false;
- stopSTUNService();
- stopLocal();
- }
-
- private void stopLocal() {
- if (stunServer != null) {
- stunServer.stop();
- }
- stunServer = null;
- }
-
- public String getName() {
- return "stun";
- }
-
- public List<StunServerAddress> getExternalServers() {
- return externalServers;
- }
-
- public void addExternalServer(String server, String port) {
- externalServers.add(new StunServerAddress(server, port));
-
- String property = "";
- for (StunServerAddress stunServerAddress : externalServers) {
- if (!property.equals("")) {
- property += ";";
- }
- property += stunServerAddress.getServer() + ":" + stunServerAddress.getPort();
- }
- JiveGlobals.setProperty("stun.external.addresses", property);
- }
-
- public void removeExternalServer(int index) {
- externalServers.remove(index);
- String property = "";
- for (StunServerAddress stunServerAddress : externalServers) {
- if (!property.equals("")) {
- property += ";";
- }
- property += stunServerAddress.getServer() + ":" + stunServerAddress.getPort();
- }
- JiveGlobals.setProperty("stun.external.addresses", property);
- }
-
- /**
- * Returns true if the service is enabled.
- *
- * @return enabled
- */
- public boolean isEnabled() {
- return enabled;
- }
-
- /**
- * Returns true if the local STUN server is enabled.
- *
- * @return enabled
- */
- public boolean isLocalEnabled() {
- return localEnabled;
- }
-
- /**
- * Set the service enable status.
- *
- * @param enabled boolean to enable or disable
- * @param localEnabled local Server enable or disable
- */
- public void setEnabled(boolean enabled, boolean localEnabled) {
- if (enabled && !this.enabled) {
- startSTUNService();
- if (isLocalEnabled()) {
- startLocalServer();
- }
- } else {
- stopSTUNService();
- }
- this.enabled = enabled;
- this.localEnabled = localEnabled;
- }
-
- /**
- * Set the Local STUN Server enable status.
- *
- * @param enabled boolean to enable or disable
- */
- public void setLocalEnabled(boolean enabled) {
- this.localEnabled = enabled;
- if (isLocalEnabled()) {
- startLocalServer();
- } else {
- stopLocal();
- }
- }
-
- /**
- * Get the secondary Port used by the STUN server
- *
- * @return secondary Port used by the STUN server
- */
- public int getSecondaryPort() {
- return secondaryPort;
- }
-
- /**
- * Get the primary Port used by the STUN server
- *
- * @return primary Port used by the STUN server
- */
- public int getPrimaryPort() {
- return primaryPort;
- }
-
- /**
- * Get the secondary Address used by the STUN server
- *
- * @return secondary Address used by the STUN server
- */
- public String getSecondaryAddress() {
- return secondaryAddress;
- }
-
- /**
- * Get the primary Address used by the STUN server
- *
- * @return primary Address used by the STUN server
- */
- public String getPrimaryAddress() {
- return primaryAddress;
- }
-
- public List<InetAddress> getAddresses() {
- List<InetAddress> list = new ArrayList<InetAddress>();
- try {
- 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()) {
- list.add(iaddress);
- }
- }
- }
- }
- catch (Exception e) {
- // Do Nothing
- }
- return list;
- }
-
- /**
- * Abstraction method used to convert a String into a STUN Server Address List
- *
- * @param addresses the String representation of server addresses with their
- * respective ports (server1:port1;server2:port2).
- * @return STUN server addresses list.
- */
- private List<StunServerAddress> getStunServerAddresses(String addresses) {
-
- List<StunServerAddress> list = new ArrayList<StunServerAddress>();
-
- if (addresses.equals("")) {
- return list;
- }
-
- String servers[] = addresses.split(";");
-
- for (String server : servers) {
- String address[] = server.split(":");
- StunServerAddress aux = new StunServerAddress(address[0], address[1]);
- if (!list.contains(aux)) {
- list.add(aux);
- }
- }
- return list;
- }
-
- /**
- * An IQ handler for STUN requests.
- */
- private class STUNIQHandler extends IQHandler {
-
- public STUNIQHandler() {
- super("stun");
- }
-
- public IQ handleIQ(IQ iq) throws UnauthorizedException {
- IQ reply = IQ.createResultIQ(iq);
- Element childElement = iq.getChildElement();
- String namespace = childElement.getNamespaceURI();
- Element childElementCopy = iq.getChildElement().createCopy();
- reply.setChildElement(childElementCopy);
-
- if (NAMESPACE.equals(namespace)) {
- if (isEnabled()) {
- Element stun = childElementCopy.addElement("stun");
- // If the local server is configured as a STUN server, send it as the first item.
- if (isLocalEnabled()) {
- StunServerAddress local;
- local = new StunServerAddress(primaryAddress, String.valueOf(primaryPort));
- if (!externalServers.contains(local)) {
- Element server = stun.addElement("server");
- server.addAttribute("host", local.getServer());
- server.addAttribute("udp", local.getPort());
- }
- }
- // Add any external STUN servers that are specified.
- for (StunServerAddress stunServerAddress : externalServers) {
- Element server = stun.addElement("server");
- server.addAttribute("host", stunServerAddress.getServer());
- server.addAttribute("udp", stunServerAddress.getPort());
- }
- try {
- String ip = sessionManager.getSession(iq.getFrom()).getConnection().getInetAddress().getHostAddress();
- if (ip != null) {
- Element publicIp = childElementCopy.addElement("publicip");
- publicIp.addAttribute("ip", ip);
- }
- }
- catch (UnknownHostException e) {
- e.printStackTrace();
- }
- }
-
- } else {
- // Answer an error since the server can't handle the requested namespace
- reply.setError(PacketError.Condition.service_unavailable);
- }
-
- try {
- Log.debug("RETURNED:" + reply.toXML());
- }
- catch (Exception e) {
- Log.error(e);
- }
- return reply;
- }
-
- public IQHandlerInfo getInfo() {
- return new IQHandlerInfo(ELEMENT_NAME, NAMESPACE);
- }
- }
- }