Commit 8852c1f1 authored by guus's avatar guus

First version of the new Jingle Relay Nodes plugin (OF-379)

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@11780 b35dd754-fafc-0310-a699-88a17e54d16e
parent fe39d5e9
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Jingle Nodes Plugin Changelog</title>
<style type="text/css">
BODY {
font-size: 100%;
}
BODY, TD, TH {
font-family: tahoma, verdana, arial, helvetica, sans-serif;
font-size: 0.8em;
}
H2 {
font-size: 10pt;
font-weight: bold;
padding-left: 1em;
}
A:hover {
text-decoration: none;
}
H1 {
font-family: tahoma, arial, helvetica, sans-serif;
font-size: 1.4em;
font-weight: bold;
border-bottom: 1px #ccc solid;
padding-bottom: 2px;
}
TT {
font-family: courier new;
font-weight: bold;
color: #060;
}
PRE {
font-family: courier new;
font-size: 100%;
}
</style>
</head>
<body>
<h1>
Jingle Nodes Plugin Changelog
</h1>
<p><b>0.0.1</b> -- July 12, 2010</p>
<ul>
<li>Initial release.</li>
<li>Fixed port Range from 30000 to 50000</li>
<li>Auto Discovery of IP with Public Interface</li>
</ul>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<class>org.jinglenodes.JingleNodesPlugin</class>
<name>Jingle Nodes Plugin</name>
<description>Provides support for Jingle Nodes</description>
<author>Jingle Nodes (Rodrigo Martins)</author>
<version>0.0.1</version>
<date>16/7/2010</date>
<minServerVersion>3.7.0</minServerVersion>
</plugin>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Jingle Nodes Plugin Readme</title>
<style type="text/css">
BODY {
font-size: 100%;
}
BODY, TD, TH {
font-family: tahoma, verdana, arial, helvetica, sans-serif;
font-size: 0.8em;
}
H2 {
font-size: 10pt;
font-weight: bold;
}
A:hover {
text-decoration: none;
}
H1 {
font-family: tahoma, arial, helvetica, sans-serif;
font-size: 1.4em;
font-weight: bold;
border-bottom: 1px #ccc solid;
padding-bottom: 2px;
}
TT {
font-family: courier new;
font-weight: bold;
color: #060;
}
PRE {
font-family: courier new;
font-size: 100%;
}
</style>
</head>
<body>
<h1>
Jingle Nodes Plugin Readme
</h1>
<h2>Overview</h2>
<p>
Implementation of http://xmpp.org/extensions/xep-0278.html.
Provides a NIO Implementation of Jingle Nodes, which is an easy to NAT Traversal service purely based on XMPP and
aims mainly, but not limited to, Jingle Clients.
</p>
<h2>Installation</h2>
<p>Make sure Openfire is running on a Public IP interface and with Firewall opened for UDP Traffic both ways on port
range 30000 to 50000.</p>
</body>
</html>
/**
* $Revision $
* $Date $
*
* 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 org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.component.Component;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.jnodes.RelayChannel;
import org.xmpp.jnodes.smack.JingleChannelIQ;
import org.xmpp.jnodes.nio.LocalIPResolver;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;
class JingleNodesComponent implements Component {
private static final Logger Log = LoggerFactory.getLogger(JingleNodesComponent.class);
private final ComponentManager componentManager;
private static final String UDP = "udp";
private static final String PROTOCOL = "protocol";
private static final String HOST = "host";
private static final String LOCAL_PORT = "localport";
private static final String REMOTE_PORT = "remoteport";
private final JingleNodesPlugin plugin;
public JingleNodesComponent(final ComponentManager componentManager, final JingleNodesPlugin plugin) {
this.componentManager = componentManager;
this.plugin = plugin;
}
public String getName() {
return "JingleRelayNode";
}
public String getDescription() {
return "Jingle Relay Service";
}
public void processPacket(Packet packet) {
if (Log.isDebugEnabled()) {
Log.debug("Processing packet: {}", packet.toXML());
}
if (packet instanceof IQ) {
// Handle disco packets
IQ iq = (IQ) packet;
// Ignore IQs of type ERROR or RESULT
if (IQ.Type.error == iq.getType() || IQ.Type.result == iq.getType()) {
return;
}
processIQ(iq);
}
}
private void processIQ(final IQ iq) {
final IQ reply = IQ.createResultIQ(iq);
final Element element = iq.getChildElement();
if (element != null) {
final String namespace = element.getNamespaceURI();
if ("http://jabber.org/protocol/disco#info".equals(namespace)) {
if (iq.getTo().getNode() == null) {
// Return service identity and features
Element identity = element.addElement("identity");
identity.addAttribute("category", "component");
identity.addAttribute("type", "relay");
identity.addAttribute("name", getName());
element.addElement("feature").addAttribute("var", "http://jabber.org/protocol/disco#info");
element.addElement("feature").addAttribute("var", JingleChannelIQ.NAMESPACE);
}
} else if (JingleChannelIQ.NAME.equals(element.getName()) && JingleChannelIQ.NAMESPACE.equals(namespace) && UDP.equals(element.attributeValue(PROTOCOL))) {
final Element childElement = iq.getChildElement().createCopy();
final RelayChannel channel = plugin.createRelayChannel();
if (channel != null) {
childElement.addAttribute(HOST, LocalIPResolver.getLocalIP());
childElement.addAttribute(LOCAL_PORT, Integer.toString(channel.getPortA()));
childElement.addAttribute(REMOTE_PORT, Integer.toString(channel.getPortB()));
reply.setChildElement(childElement);
} else {
reply.setError(PacketError.Condition.internal_server_error);
}
} else {
reply.setError(PacketError.Condition.feature_not_implemented);
}
} else {
reply.setError(PacketError.Condition.feature_not_implemented);
}
try {
componentManager.sendPacket(this, reply);
if (Log.isDebugEnabled()) {
Log.debug("Packet sent: {}", reply.toXML());
}
}
catch (Exception e) {
Log.error(e.getMessage(), e);
}
}
public void initialize(final JID jid, final ComponentManager componentManager) throws ComponentException {
}
public void start() {
}
public void shutdown() {
}
}
/**
* $Revision $
* $Date $
*
* 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 org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
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 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;
public class JingleNodesPlugin implements Plugin {
private static final Logger Log = LoggerFactory.getLogger(JingleNodesPlugin.class);
private ComponentManager componentManager;
private JingleNodesComponent component;
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";
public void initializePlugin(PluginManager manager, File pluginDirectory) {
componentManager = ComponentManagerFactory.getComponentManager();
component = new JingleNodesComponent(componentManager, 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.");
}
private void closeAllChannels() {
for (final RelayChannel c : channels.values()) {
removeChannel(c);
}
}
public RelayChannel createRelayChannel() {
RelayChannel rc = null;
try {
rc = RelayChannel.createLocalRelayChannel("0.0.0.0", 30000, 50000);
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 void destroyPlugin() {
component.shutdown();
try {
componentManager.removeComponent(serviceName);
} catch (ComponentException e) {
Log.error("Could NOT Remove " + serviceName + " Component");
}
closeAllChannels();
executor.shutdownNow();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment