/* * Copyright (C) 2005-2010 Jive Software. All rights reserved. * * 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 org.jinglenodes; import java.io.File; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.net.InetSocketAddress; import org.jivesoftware.openfire.container.Plugin; import org.jivesoftware.openfire.container.PluginManager; import org.jivesoftware.util.JiveGlobals; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xmpp.component.ComponentException; import org.xmpp.component.ComponentManager; import org.xmpp.component.ComponentManagerFactory; import org.xmpp.jnodes.RelayChannel; import org.xmpp.jnodes.nio.LocalIPResolver; import org.xmpp.jnodes.nio.PublicIPResolver; public class JingleNodesPlugin implements Plugin { private static final Logger Log = LoggerFactory.getLogger(JingleNodesPlugin.class); public static final String JN_LOCAL_IP_PROPERTY = "jinglenodes.localip"; public static final String JN_PUBLIC_IP_PROPERTY = "jinglenodes.publicip"; public static final String JN_MIN_PORT_PROPERTY = "jinglenodes.minport"; public static final String JN_MAX_PORT_PROPERTY = "jinglenodes.maxport"; public static final String JN_TEST_STUN_SERVER_PROPERTY = "jinglenodes.teststunserver"; public static final String JN_TEST_STUN_PORT_PROPERTY = "jinglenodes.teststunport"; private ComponentManager componentManager; private final ConcurrentHashMap<String, RelayChannel> channels = new ConcurrentHashMap<String, RelayChannel>(); private final long timeout = 60000; private final AtomicInteger ids = new AtomicInteger(0); private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); private final String serviceName = "relay"; private final boolean bindAllInterfaces; private boolean hasPublicIP = false; public JingleNodesPlugin() { final String os = System.getProperty("os.name"); bindAllInterfaces = !(os != null && os.toLowerCase().indexOf("win") > -1); } public void initializePlugin(PluginManager manager, File pluginDirectory) { componentManager = ComponentManagerFactory.getComponentManager(); JingleNodesComponent component = new JingleNodesComponent(this); try { componentManager.addComponent(serviceName, component); } catch (ComponentException e) { Log.error("Could NOT load " + component.getName()); } setup(); } private void setup() { executor.scheduleWithFixedDelay(new Runnable() { public void run() { for (final RelayChannel c : channels.values()) { final long current = System.currentTimeMillis(); final long da = current - c.getLastReceivedTimeA(); final long db = current - c.getLastReceivedTimeB(); if (da > timeout || db > timeout) { removeChannel(c); } } } }, timeout, timeout, TimeUnit.MILLISECONDS); Log.info("Jingle Nodes Loaded."); verifyNetwork(); } public void verifyNetwork() { final String localAddress = JiveGlobals.getProperty(JN_LOCAL_IP_PROPERTY, LocalIPResolver.getLocalIP()); LocalIPResolver.setOverrideIp(localAddress); final InetSocketAddress publicAddress = PublicIPResolver.getPublicAddress(getTestSTUNServer(), getTestSTUNPort()); hasPublicIP = publicAddress != null && publicAddress.getAddress().getHostAddress().equals(getPublicIP()); // Provide a more detailed log message to help users reasonably debug this situation if (!hasPublicIP) { if (publicAddress != null) { Log.error("verifyNetwork Failed - public IP address from test STUN server [" + publicAddress.getAddress().getHostAddress() + "] does not match server configuration [" + getPublicIP() + "]"); } else { Log.error("verifyNetwork Failed - failed to retrieve public address from test STUN server " + getTestSTUNServer() + ":" + Integer.toString(getTestSTUNPort())); } } } private void closeAllChannels() { for (final RelayChannel c : channels.values()) { removeChannel(c); } } public RelayChannel createRelayChannel() { RelayChannel rc = null; try { rc = RelayChannel.createLocalRelayChannel(bindAllInterfaces ? "0.0.0.0" : LocalIPResolver.getLocalIP(), getMinPort(), getMaxPort()); final int id = ids.incrementAndGet(); final String sId = String.valueOf(id); rc.setAttachment(sId); channels.put(sId, rc); } catch (IOException e) { Log.error("Could Not Create Channel.", e); } return rc; } private void removeChannel(final RelayChannel c) { channels.remove((String) c.getAttachment()); c.close(); } public String getServiceName() { return serviceName; } public void destroyPlugin() { try { componentManager.removeComponent(serviceName); } catch (ComponentException e) { Log.error("Could NOT Remove " + serviceName + " Component"); } closeAllChannels(); executor.shutdownNow(); } public boolean hasPublicIP() { return hasPublicIP; } public int getActiveChannelCount() { return channels.size(); } // Changed all previous hardcoded values to be properties that can be overridden public String getPublicIP() { return JiveGlobals.getProperty(JN_PUBLIC_IP_PROPERTY, LocalIPResolver.getLocalIP()); } public int getMinPort() { return Integer.parseInt(JiveGlobals.getProperty(JN_MIN_PORT_PROPERTY, "30000")); } public int getMaxPort() { return Integer.parseInt(JiveGlobals.getProperty(JN_MAX_PORT_PROPERTY, "50000")); } public String getTestSTUNServer() { return JiveGlobals.getProperty(JN_TEST_STUN_SERVER_PROPERTY, "stun.l.google.com"); } public int getTestSTUNPort() { return Integer.parseInt(JiveGlobals.getProperty(JN_TEST_STUN_PORT_PROPERTY, "19302")); } }