Commit 7aaf1b0f authored by Dave Cridland's avatar Dave Cridland Committed by GitHub

Merge pull request #720 from guusdk/OF-512_filetransferproxy

OF-512: Allow 'reported' streamhost to be modified.
parents 92c97e50 3f76e421
...@@ -2705,9 +2705,12 @@ filetransferproxy.settings.label_disable=Disabled ...@@ -2705,9 +2705,12 @@ filetransferproxy.settings.label_disable=Disabled
filetransferproxy.settings.label_disable_info=This server will not act as a file transfer proxy. filetransferproxy.settings.label_disable_info=This server will not act as a file transfer proxy.
filetransferproxy.settings.label_enable=Enabled filetransferproxy.settings.label_enable=Enabled
filetransferproxy.settings.label_enable_info=This server will act as a file transfer proxy on port: filetransferproxy.settings.label_enable_info=This server will act as a file transfer proxy on port:
filetransferproxy.settings.label_port=Port:
filetransferproxy.settings.label_hardcoded_address=Override reported address
filetransferproxy.settings.label_hardcoded_optionality=(leave empty if an override is not needed)
filetransferproxy.settings.valid.port=Please enter a valid port. filetransferproxy.settings.valid.port=Please enter a valid port.
filetransferproxy.settings.confirm.updated=File transfer proxy settings updated succesfully. filetransferproxy.settings.confirm.updated=File transfer proxy settings updated succesfully.
filetransfer.error.disabled=File Transfer Has Been Disabled, Proxy Won't Function filetransfer.error.disabled=File Transfer Has Been Disabled, Proxy Won't Function.
# File Transfer Proxy Stats # File Transfer Proxy Stats
......
...@@ -63,8 +63,8 @@ import org.xmpp.packet.PacketError; ...@@ -63,8 +63,8 @@ import org.xmpp.packet.PacketError;
public class FileTransferProxy extends BasicModule public class FileTransferProxy extends BasicModule
implements ServerItemsProvider, DiscoInfoProvider, DiscoItemsProvider, implements ServerItemsProvider, DiscoInfoProvider, DiscoItemsProvider,
RoutableChannelHandler { RoutableChannelHandler {
private static final Logger Log = LoggerFactory.getLogger(FileTransferProxy.class); private static final Logger Log = LoggerFactory.getLogger( FileTransferProxy.class);
/** /**
* The JiveProperty relating to whether or not the file treansfer proxy is enabled. * The JiveProperty relating to whether or not the file treansfer proxy is enabled.
...@@ -77,6 +77,11 @@ public class FileTransferProxy extends BasicModule ...@@ -77,6 +77,11 @@ public class FileTransferProxy extends BasicModule
*/ */
public static final String JIVEPROPERTY_PORT = "xmpp.proxy.port"; public static final String JIVEPROPERTY_PORT = "xmpp.proxy.port";
/**
* Name of the property that hardcodes the external IP that is being listened on.
*/
public static final String PROPERTY_EXTERNALIP = "xmpp.proxy.externalip";
/** /**
* Whether or not the file transfer proxy is enabled by default. * Whether or not the file transfer proxy is enabled by default.
*/ */
...@@ -132,15 +137,28 @@ public class FileTransferProxy extends BasicModule ...@@ -132,15 +137,28 @@ public class FileTransferProxy extends BasicModule
else if (FileTransferManager.NAMESPACE_BYTESTREAMS.equals(namespace)) { else if (FileTransferManager.NAMESPACE_BYTESTREAMS.equals(namespace)) {
if (packet.getType() == IQ.Type.get) { if (packet.getType() == IQ.Type.get) {
IQ reply = IQ.createResultIQ(packet); IQ reply = IQ.createResultIQ(packet);
Element newChild = reply.setChildElement("query", Element newChild = reply.setChildElement("query", FileTransferManager.NAMESPACE_BYTESTREAMS);
FileTransferManager.NAMESPACE_BYTESTREAMS);
for ( InetAddress address : getAddresses() ) final String externalIP = JiveGlobals.getProperty( PROPERTY_EXTERNALIP );
if ( externalIP != null && !externalIP.isEmpty() )
{ {
Element response = newChild.addElement( "streamhost" ); // OF-512: Override the automatic detection with a specific address (useful for NATs, proxies, etc)
final Element response = newChild.addElement( "streamhost" );
response.addAttribute( "jid", getServiceDomain() ); response.addAttribute( "jid", getServiceDomain() );
response.addAttribute( "host", address.getHostAddress() ); response.addAttribute( "host", externalIP );
response.addAttribute( "port", String.valueOf( connectionManager.getProxyPort() ) ); response.addAttribute( "port", String.valueOf( connectionManager.getProxyPort() ) );
} }
else
{
// Report all network addresses that we know that we're servicing.
for ( final InetAddress address : getAddresses() )
{
final Element response = newChild.addElement( "streamhost" );
response.addAttribute( "jid", getServiceDomain() );
response.addAttribute( "host", address.getHostAddress() );
response.addAttribute( "port", String.valueOf( connectionManager.getProxyPort() ) );
}
}
router.route(reply); router.route(reply);
return true; return true;
} }
...@@ -179,71 +197,54 @@ public class FileTransferProxy extends BasicModule ...@@ -179,71 +197,54 @@ public class FileTransferProxy extends BasicModule
routingTable = server.getRoutingTable(); routingTable = server.getRoutingTable();
router = server.getPacketRouter(); router = server.getPacketRouter();
final String hardCodedProxyIP = JiveGlobals.getProperty( "xmpp.proxy.externalip" ); connectionManager = new ProxyConnectionManager(getFileTransferManager(server));
}
/**
* Returns the IP address(es) that the proxy connection manager is servicing.
*/
private Set<InetAddress> getAddresses()
{
final String interfaceName = JiveGlobals.getXMLProperty( "network.interface" ); final String interfaceName = JiveGlobals.getXMLProperty( "network.interface" );
if ( hardCodedProxyIP != null && !hardCodedProxyIP.trim().isEmpty() )
{ final Set<InetAddress> result = new HashSet<>();
// First choice: a hardcoded IP address, if one exists.
try // Let's see if we hardcoded a specific interface, then use its address.
{ if ( interfaceName != null && !interfaceName.trim().isEmpty() )
bindInterface = InetAddress.getByName( hardCodedProxyIP.trim() );
}
catch ( UnknownHostException e )
{
Log.error( "Error binding to xmpp.proxy.externalip '{}'", interfaceName, e );
}
}
else if ( interfaceName != null && !interfaceName.trim().isEmpty() )
{ {
// No hardcoded IP? Let's see if we hardcoded a specific interface, then use its address.
try try
{ {
bindInterface = InetAddress.getByName( interfaceName.trim() ); bindInterface = InetAddress.getByName( interfaceName.trim() );
result.add( bindInterface );
return result;
} }
catch ( UnknownHostException e ) catch ( UnknownHostException e )
{ {
Log.error( "Error binding to network.interface '{}'", interfaceName, e ); Log.error( "Error binding to network.interface '{}'", interfaceName, e );
} }
} }
else
{
// If no configuration is available, use all available addresses.
bindInterface = null;
}
connectionManager = new ProxyConnectionManager(getFileTransferManager(server));
}
/** // When there's no specific address configured, return all available (non-loopback) addresses.
* Returns the IP address(es) that are used. try
*/
private Set<InetAddress> getAddresses()
{
final Set<InetAddress> result = new HashSet<>();
if ( bindInterface != null )
{ {
result.add( bindInterface ); final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
} while ( networkInterfaces.hasMoreElements() )
else
{
// When there's no specific address configured, return all available addresses.
try
{ {
final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); final NetworkInterface networkInterface = networkInterfaces.nextElement();
while ( networkInterfaces.hasMoreElements() ) if ( networkInterface.isLoopback() )
{ {
final NetworkInterface networkInterface = networkInterfaces.nextElement(); continue;
final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses(); }
while ( inetAddresses.hasMoreElements() ) final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
{ while ( inetAddresses.hasMoreElements() )
result.add( inetAddresses.nextElement() ); {
} result.add( inetAddresses.nextElement() );
} }
} }
catch ( SocketException e ) }
{ catch ( SocketException e )
Log.error( "Error determining all addresses for this server", e ); {
} Log.error( "Error determining all addresses for this server", e );
} }
return result; return result;
} }
...@@ -253,7 +254,7 @@ public class FileTransferProxy extends BasicModule ...@@ -253,7 +254,7 @@ public class FileTransferProxy extends BasicModule
} }
@Override @Override
public void start() { public void start() {
super.start(); super.start();
if (isEnabled()) { if (isEnabled()) {
...@@ -273,7 +274,7 @@ public class FileTransferProxy extends BasicModule ...@@ -273,7 +274,7 @@ public class FileTransferProxy extends BasicModule
} }
@Override @Override
public void stop() { public void stop() {
super.stop(); super.stop();
XMPPServer.getInstance().getIQDiscoItemsHandler() XMPPServer.getInstance().getIQDiscoItemsHandler()
...@@ -283,7 +284,7 @@ public class FileTransferProxy extends BasicModule ...@@ -283,7 +284,7 @@ public class FileTransferProxy extends BasicModule
} }
@Override @Override
public void destroy() { public void destroy() {
super.destroy(); super.destroy();
connectionManager.shutdown(); connectionManager.shutdown();
...@@ -291,7 +292,8 @@ public class FileTransferProxy extends BasicModule ...@@ -291,7 +292,8 @@ public class FileTransferProxy extends BasicModule
public void enableFileTransferProxy(boolean isEnabled) { public void enableFileTransferProxy(boolean isEnabled) {
JiveGlobals.setProperty(FileTransferProxy.JIVEPROPERTY_PROXY_ENABLED, JiveGlobals.setProperty(FileTransferProxy.JIVEPROPERTY_PROXY_ENABLED,
Boolean.toString(isEnabled)); Boolean.toString(isEnabled));
setEnabled( isEnabled );
} }
private void setEnabled(boolean isEnabled) { private void setEnabled(boolean isEnabled) {
...@@ -358,9 +360,9 @@ public class FileTransferProxy extends BasicModule ...@@ -358,9 +360,9 @@ public class FileTransferProxy extends BasicModule
} }
final DiscoServerItem item = new DiscoServerItem(new JID( final DiscoServerItem item = new DiscoServerItem(new JID(
getServiceDomain()), "Socks 5 Bytestreams Proxy", null, null, this, getServiceDomain()), "Socks 5 Bytestreams Proxy", null, null, this,
this); this);
return Collections.singleton(item).iterator(); return Collections.singleton(item).iterator();
} }
...@@ -378,7 +380,7 @@ public class FileTransferProxy extends BasicModule ...@@ -378,7 +380,7 @@ public class FileTransferProxy extends BasicModule
@Override @Override
public Iterator<String> getFeatures(String name, String node, JID senderJID) { public Iterator<String> getFeatures(String name, String node, JID senderJID) {
return Arrays.asList(FileTransferManager.NAMESPACE_BYTESTREAMS, return Arrays.asList(FileTransferManager.NAMESPACE_BYTESTREAMS,
"http://jabber.org/protocol/disco#info").iterator(); "http://jabber.org/protocol/disco#info").iterator();
} }
@Override @Override
...@@ -415,11 +417,21 @@ public class FileTransferProxy extends BasicModule ...@@ -415,11 +417,21 @@ public class FileTransferProxy extends BasicModule
private class FileTransferPropertyListener implements PropertyEventListener { private class FileTransferPropertyListener implements PropertyEventListener {
@Override @Override
public void propertySet(String property, Map params) { public void propertySet(String property, Map params)
{
if ( isEnabled() )
{
// Restart when configuration changed.
if (JIVEPROPERTY_PORT.equalsIgnoreCase( property ))
{
setEnabled( false );
setEnabled( true );
}
}
if(JIVEPROPERTY_PROXY_ENABLED.equalsIgnoreCase(property)) { if(JIVEPROPERTY_PROXY_ENABLED.equalsIgnoreCase(property)) {
Object value = params.get("value"); Object value = params.get("value");
boolean isEnabled = (value != null ? Boolean.parseBoolean(value.toString()) : boolean isEnabled = (value != null ? Boolean.parseBoolean(value.toString()) : DEFAULT_IS_PROXY_ENABLED);
DEFAULT_IS_PROXY_ENABLED);
setEnabled(isEnabled); setEnabled(isEnabled);
} }
} }
...@@ -429,6 +441,16 @@ public class FileTransferProxy extends BasicModule ...@@ -429,6 +441,16 @@ public class FileTransferProxy extends BasicModule
if(JIVEPROPERTY_PROXY_ENABLED.equalsIgnoreCase(property)) { if(JIVEPROPERTY_PROXY_ENABLED.equalsIgnoreCase(property)) {
setEnabled(DEFAULT_IS_PROXY_ENABLED); setEnabled(DEFAULT_IS_PROXY_ENABLED);
} }
if ( isEnabled() )
{
// Restart when configuration changed.
if (JIVEPROPERTY_PORT.equalsIgnoreCase( property ) )
{
setEnabled( false );
setEnabled( true );
}
}
} }
@Override @Override
......
...@@ -22,65 +22,89 @@ ...@@ -22,65 +22,89 @@
<%@ page import="org.jivesoftware.openfire.filetransfer.proxy.FileTransferProxy" %> <%@ page import="org.jivesoftware.openfire.filetransfer.proxy.FileTransferProxy" %>
<%@ page import="java.util.HashMap" %> <%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %> <%@ page import="java.util.Map" %>
<%@ page import="org.jivesoftware.openfire.XMPPServer"%> <%@ page import="org.jivesoftware.openfire.XMPPServer" %>
<%@ page import="org.jivesoftware.util.JiveGlobals" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager"/> <jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager"/>
<% webManager.init(request, response, session, application, out); %> <% webManager.init( request, response, session, application, out ); %>
<% <%
Map<String, String> errors = new HashMap<String, String>(); Map<String, String> errors = new HashMap<String, String>();
FileTransferProxy transferProxy = XMPPServer.getInstance().getFileTransferProxy(); FileTransferProxy transferProxy = XMPPServer.getInstance().getFileTransferProxy();
boolean isUpdated = request.getParameter("update") != null; boolean isUpdated = request.getParameter( "update" ) != null;
boolean isProxyEnabled = ParamUtils.getBooleanParameter(request, "proxyEnabled"); boolean isProxyEnabled = ParamUtils.getBooleanParameter( request, "proxyEnabled" );
int port = ParamUtils.getIntParameter(request, "port", 0); String hardcodedAddress = ParamUtils.getParameter( request, "hardcodedAddress" );
Cookie csrfCookie = CookieUtils.getCookie(request, "csrf"); int port = ParamUtils.getIntParameter( request, "port", 0 );
String csrfParam = ParamUtils.getParameter(request, "csrf"); Cookie csrfCookie = CookieUtils.getCookie( request, "csrf" );
String csrfParam = ParamUtils.getParameter( request, "csrf" );
if (isUpdated) { if ( isUpdated )
if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) { {
if ( csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals( csrfParam ) )
{
isUpdated = false; isUpdated = false;
errors.put("csrf", "CSRF Failure!"); errors.put( "csrf", "CSRF Failure!" );
} }
} }
csrfParam = StringUtils.randomString(15); csrfParam = StringUtils.randomString( 15 );
CookieUtils.setCookie(request, response, "csrf", csrfParam, -1); CookieUtils.setCookie( request, response, "csrf", csrfParam, -1 );
pageContext.setAttribute("csrf", csrfParam); pageContext.setAttribute( "csrf", csrfParam );
if (isUpdated) { if ( isUpdated )
if (isProxyEnabled) { {
if (port <= 0) { if ( hardcodedAddress != null && !hardcodedAddress.matches( "^[A-Za-z0-9-\\.]+$" ) )
errors.put("port", ""); {
} errors.put( "address", "" );
}
if ( port <= 0 )
{
errors.put( "port", "" );
} }
if (errors.isEmpty()) { if ( errors.isEmpty() )
if (isProxyEnabled) { {
transferProxy.setProxyPort(port); JiveGlobals.setProperty( "xmpp.proxy.externalip", hardcodedAddress );
if ( isProxyEnabled )
{
transferProxy.setProxyPort( port );
} }
transferProxy.enableFileTransferProxy(isProxyEnabled); transferProxy.enableFileTransferProxy( isProxyEnabled );
// Log the event // Log the event
webManager.logEvent("edited file transfer proxy settings", "port = "+port+"\nenabled = "+isProxyEnabled); webManager.logEvent( "edited file transfer proxy settings", "port = " + port + "\nhardcodedAddress = " + hardcodedAddress + "\nenabled = " + isProxyEnabled );
} }
} }
if (errors.isEmpty()) { if ( errors.isEmpty() )
isProxyEnabled = transferProxy.isProxyEnabled(); {
port = transferProxy.getProxyPort(); port = transferProxy.getProxyPort();
} }
else { else
if (port == 0) { {
isUpdated = false;
if ( port == 0 )
{
port = transferProxy.getProxyPort(); port = transferProxy.getProxyPort();
} }
} }
pageContext.setAttribute( "errors", errors );
pageContext.setAttribute( "isUpdated", isUpdated );
pageContext.setAttribute( "port", port );
pageContext.setAttribute( "hardcodedAddress", JiveGlobals.getProperty( "xmpp.proxy.externalip" ) );
pageContext.setAttribute( "fileTransferProxy", XMPPServer.getInstance().getFileTransferProxy() );
%> %>
<html> <html>
<head> <head>
<title><fmt:message key="filetransferproxy.settings.title"/></title> <title><fmt:message key="filetransferproxy.settings.title"/></title>
</head> </head>
<meta name="pageID" content="server-transfer-proxy"/> <meta name="pageID" content="server-transfer-proxy"/>
<body> <body>
...@@ -89,83 +113,75 @@ ...@@ -89,83 +113,75 @@
<fmt:message key="filetransferproxy.settings.info"/> <fmt:message key="filetransferproxy.settings.info"/>
</p> </p>
<% if (!errors.isEmpty()) { %> <c:forEach var="err" items="${errors}" varStatus="varStatus">
<div class="jive-error"> <admin:infobox type="error">
<table cellpadding="0" cellspacing="0" border="0"> <c:choose>
<tbody> <c:when test="${err.key eq 'port'}">
<tr> <fmt:message key="filetransferproxy.settings.valid.port"/>
<td class="jive-icon"><img alt="error" src="images/error-16x16.gif" width="16" height="16" </c:when>
border="0"/></td>
<td class="jive-icon-label">
<% if (errors.get("port") != null) { %>
<fmt:message key="filetransferproxy.settings.valid.port"/>
<% } %>
</td>
</tr>
</tbody>
</table>
</div>
<br>
<% }
else if (isUpdated) { %>
<div class="jive-success">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr><td class="jive-icon"><img alt="Success" src="images/success-16x16.gif" width="16" height="16"
border="0"></td>
<td class="jive-icon-label">
<fmt:message key="filetransferproxy.settings.confirm.updated"/>
</td></tr>
</tbody>
</table>
</div><br>
<% }
else { %>
<br>
<% } %>
<c:otherwise>
<c:if test="${not empty err.value}">
<fmt:message key="admin.error"/>: <c:out value="${err.value}"/>
</c:if>
(<c:out value="${err.key}"/>)
</c:otherwise>
</c:choose>
</admin:infobox>
<c:if test="${not varStatus.last}">
<br/>
</c:if>
</c:forEach>
<c:if test="${isUpdated}">
<admin:infobox type="success">
<fmt:message key="filetransferproxy.settings.confirm.updated"/>
</admin:infobox>
</c:if>
<br/>
<!-- BEGIN 'Proxy Service' --> <!-- BEGIN 'Proxy Service' -->
<c:set var="title"><fmt:message key="filetransferproxy.settings.enabled.legend"/></c:set>
<form action="file-transfer-proxy.jsp" method="post"> <form action="file-transfer-proxy.jsp" method="post">
<input type="hidden" name="csrf" value="${csrf}"> <input type="hidden" name="csrf" value="${csrf}">
<div class="jive-contentBoxHeader"> <admin:contentBox title="${title}">
<fmt:message key="filetransferproxy.settings.enabled.legend"/> <table cellpadding="3" cellspacing="0" border="0">
</div> <tbody>
<div class="jive-contentBox"> <tr valign="top">
<table cellpadding="3" cellspacing="0" border="0"> <td width="1%" nowrap>
<tbody> <input type="radio" name="proxyEnabled" value="true" id="rb02" ${fileTransferProxy.proxyEnabled ? 'checked' : ''}>
<tr valign="middle"> </td>
<td width="1%" nowrap> <td width="99%">
<input type="radio" name="proxyEnabled" value="true" id="rb02" <label for="rb02"><b><fmt:message key="filetransferproxy.settings.label_enable"/></b>- <fmt:message key="filetransferproxy.settings.label_enable_info"/></label>
<%= (isProxyEnabled ? "checked" : "") %> > <table border="0">
</td> <tr>
<td width="99%"> <td><label for="port"><fmt:message key="filetransferproxy.settings.label_port"/></label></td>
<label for="rb02"> <td><input type="text" size="5" maxlength="10" id="port" name="port" value="${port}"></td>
<b><fmt:message key="filetransferproxy.settings.label_enable"/></b> </tr>
- <fmt:message key="filetransferproxy.settings.label_enable_info"/> <tr>
</label> <input type="text" size="5" maxlength="10" name="port" <td><label for="hardcodedAddress"><fmt:message key="filetransferproxy.settings.label_hardcoded_address"/></label></td>
value="<%= port %>" > <td>
</td> <input type="text" size="40" maxlength="255" id="hardcodedAddress" name="hardcodedAddress" value="${hardcodedAddress}"> <fmt:message key="filetransferproxy.settings.label_hardcoded_optionality"/>
</tr> </td>
<tr valign="middle"> </tr>
<td width="1%" nowrap> </table>
<input type="radio" name="proxyEnabled" value="false" id="rb01" </td>
<%= (!isProxyEnabled ? "checked" : "") %> > </tr>
</td> <tr valign="middle">
<td width="99%"> <td width="1%" nowrap>
<label for="rb01"> <input type="radio" name="proxyEnabled" value="false" id="rb01" ${fileTransferProxy.proxyEnabled ? '' : 'checked'}>
<b><fmt:message key="filetransferproxy.settings.label_disable"/></b> </td>
- <fmt:message key="filetransferproxy.settings.label_disable_info"/> <td width="99%">
</label> <label for="rb01"><b><fmt:message key="filetransferproxy.settings.label_disable"/></b> - <fmt:message key="filetransferproxy.settings.label_disable_info"/></label>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </admin:contentBox>
<input type="submit" name="update" value="<fmt:message key="global.save_settings" />"> <input type="submit" name="update" value="<fmt:message key="global.save_settings" />">
</form> </form>
<!-- END 'Proxy Service' --> <!-- END 'Proxy Service' -->
</body> </body>
</html> </html>
\ No newline at end of file
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