Commit 6e1b6eff authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Remote servers may have more than one connection to the local server. JM-407

git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@2853 b35dd754-fafc-0310-a699-88a17e54d16e
parent c7229ba9
...@@ -89,9 +89,11 @@ public class SessionManager extends BasicModule { ...@@ -89,9 +89,11 @@ public class SessionManager extends BasicModule {
* The sessions contained in this Map are server sessions originated by a remote server. These * The sessions contained in this Map are server sessions originated by a remote server. These
* sessions can only receive packets from the remote server but are not capable of sending * sessions can only receive packets from the remote server but are not capable of sending
* packets to the remote server. Sessions will be added to this collecion only after they were * packets to the remote server. Sessions will be added to this collecion only after they were
* authenticated. The key of the Map is the hostname of the remote server. * authenticated. The key of the Map is the hostname of the remote server. The value is a
* list of IncomingServerSession that will keep each session created by a remote server to
* this server.
*/ */
private Map<String, IncomingServerSession> incomingServerSessions = new ConcurrentHashMap<String, IncomingServerSession>(); private Map<String, List<IncomingServerSession>> incomingServerSessions = new ConcurrentHashMap<String, List<IncomingServerSession>>();
/** /**
* The sessions contained in this Map are server sessions originated from this server to remote * The sessions contained in this Map are server sessions originated from this server to remote
...@@ -437,24 +439,59 @@ public class SessionManager extends BasicModule { ...@@ -437,24 +439,59 @@ public class SessionManager extends BasicModule {
/** /**
* Registers that a server session originated by a remote server is hosting a given hostname. * Registers that a server session originated by a remote server is hosting a given hostname.
* Notice that the remote server may be hosting several subdomains as well as virtual hosts so * Notice that the remote server may be hosting several subdomains as well as virtual hosts so
* the same IncomingServerSession may be associated with many keys. * the same IncomingServerSession may be associated with many keys. If the remote server
* creates many sessions to this server (eg. one for each subdomain) then associate all
* the sessions with the originating server that created all the sessions.
* *
* @param hostname the hostname that is being served by the remote server. * @param hostname the hostname that is being served by the remote server.
* @param session the incoming server session to the remote server. * @param session the incoming server session to the remote server.
*/ */
public void registerIncomingServerSession(String hostname, IncomingServerSession session) { public void registerIncomingServerSession(String hostname, IncomingServerSession session) {
incomingServerSessions.put(hostname, session); synchronized (incomingServerSessions) {
List<IncomingServerSession> sessions = incomingServerSessions.get(hostname);
if (sessions == null || sessions.isEmpty()) {
// First session from the remote server to this server so create a
// new association
List<IncomingServerSession> value = new ArrayList<IncomingServerSession>();
value.add(session);
incomingServerSessions.put(hostname, value);
}
else {
// Add new session to the existing list of sessions originated by
// the remote server
sessions.add(session);
}
}
} }
/** /**
* Unregisters that a server session originated by a remote server is hosting a given hostname. * Unregisters the server sessions originated by a remote server with the specified hostname.
* Notice that the remote server may be hosting several subdomains as well as virtual hosts so * Notice that the remote server may be hosting several subdomains as well as virtual hosts so
* the same IncomingServerSession may be associated with many keys. * the same IncomingServerSession may be associated with many keys. The remote server may have
* many sessions established with this server (eg. to the server itself and to subdomains
* hosted by this server).
* *
* @param hostname the hostname that is being served by the remote server. * @param hostname the hostname that is being served by the remote server.
*/ */
public void unregisterIncomingServerSession(String hostname) { public void unregisterIncomingServerSessions(String hostname) {
incomingServerSessions.remove(hostname); synchronized (incomingServerSessions) {
incomingServerSessions.remove(hostname);
}
}
/**
* Unregisters the specified remote server session originiated by the specified remote server.
*
* @param hostname the hostname that is being served by the remote server.
* @param session the session to unregiser.
*/
private void unregisterIncomingServerSession(String hostname, IncomingServerSession session) {
synchronized (incomingServerSessions) {
List<IncomingServerSession> sessions = incomingServerSessions.get(hostname);
if (sessions != null) {
sessions.remove(session);
}
}
} }
/** /**
...@@ -917,15 +954,21 @@ public class SessionManager extends BasicModule { ...@@ -917,15 +954,21 @@ public class SessionManager extends BasicModule {
} }
/** /**
* Returns a session that was originated by a remote server. IncomingServerSession can only * Returns the list of sessions that were originated by a remote server. The list will be
* receive packets from the remote server but are not capable of sending packets to the remote * ordered chronologically. IncomingServerSession can only receive packets from the remote
* server. * server but are not capable of sending packets to the remote server.
* *
* @param hostname the name of the remote server. * @param hostname the name of the remote server.
* @return a session that was originated by a remote server. * @return the sessions that were originated by a remote server.
*/ */
public IncomingServerSession getIncomingServerSession(String hostname) { public List<IncomingServerSession> getIncomingServerSessions(String hostname) {
return incomingServerSessions.get(hostname); List<IncomingServerSession> sessions = incomingServerSessions.get(hostname);
if (sessions == null) {
return Collections.emptyList();
}
else {
return Collections.unmodifiableList(sessions);
}
} }
/** /**
...@@ -1292,7 +1335,7 @@ public class SessionManager extends BasicModule { ...@@ -1292,7 +1335,7 @@ public class SessionManager extends BasicModule {
IncomingServerSession session = (IncomingServerSession)handback; IncomingServerSession session = (IncomingServerSession)handback;
// Remove all the hostnames that were registered for this server session // Remove all the hostnames that were registered for this server session
for (String hostname : session.getValidatedDomains()) { for (String hostname : session.getValidatedDomains()) {
unregisterIncomingServerSession(hostname); unregisterIncomingServerSession(hostname, session);
} }
} }
} }
...@@ -1495,14 +1538,16 @@ public class SessionManager extends BasicModule { ...@@ -1495,14 +1538,16 @@ public class SessionManager extends BasicModule {
} }
} }
// Check incoming server sessions // Check incoming server sessions
for (IncomingServerSession session : incomingServerSessions.values()) { for (List<IncomingServerSession> sessions : incomingServerSessions.values()) {
try { for (IncomingServerSession session : sessions) {
if (session.getLastActiveDate().getTime() < deadline) { try {
session.getConnection().close(); if (session.getLastActiveDate().getTime() < deadline) {
session.getConnection().close();
}
}
catch (Throwable e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
} }
}
catch (Throwable e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
} }
} }
} }
......
/** /**
* $RCSfile$ * $RCSfile: IncomingServerSession.java,v $
* $Revision$ * $Revision$
* $Date$ * $Date$
* *
...@@ -48,8 +48,20 @@ import java.util.Collections; ...@@ -48,8 +48,20 @@ import java.util.Collections;
*/ */
public class IncomingServerSession extends Session { public class IncomingServerSession extends Session {
/**
* List of domains, subdomains and virtual hostnames of the remote server that were
* validated with this server. The remote server is allowed to send packets to this
* server from any of the validated domains.
*/
private Collection<String> validatedDomains = new ArrayList<String>(); private Collection<String> validatedDomains = new ArrayList<String>();
/**
* List of domains or subdomains of this server that were used by the remote server
* when validating the new connection. For instance, when using ServerDialback the
* list will contain the different TO values used in the <tt>db:result</tt> packet.
*/
private Collection<String> localDomains = new ArrayList<String>();
/** /**
* Creates a new session that will receive packets. The new session will be authenticated * Creates a new session that will receive packets. The new session will be authenticated
* before being returned. If the authentication process fails then the answer will be * before being returned. If the authentication process fails then the answer will be
...@@ -106,6 +118,7 @@ public class IncomingServerSession extends Session { ...@@ -106,6 +118,7 @@ public class IncomingServerSession extends Session {
public boolean validateSubsequentDomain(Element dbResult) { public boolean validateSubsequentDomain(Element dbResult) {
ServerDialback method = new ServerDialback(getConnection(), getServerName()); ServerDialback method = new ServerDialback(getConnection(), getServerName());
if (method.validateRemoteDomain(dbResult, getStreamID())) { if (method.validateRemoteDomain(dbResult, getStreamID())) {
// Add the validated domain as a valid domain
addValidatedDomain(dbResult.attributeValue("from")); addValidatedDomain(dbResult.attributeValue("from"));
return true; return true;
} }
...@@ -169,7 +182,32 @@ public class IncomingServerSession extends Session { ...@@ -169,7 +182,32 @@ public class IncomingServerSession extends Session {
public void removeValidatedDomain(String domain) { public void removeValidatedDomain(String domain) {
validatedDomains.remove(domain); validatedDomains.remove(domain);
// Unregister the validated domain for this server session in SessionManager // Unregister the validated domain for this server session in SessionManager
SessionManager.getInstance().unregisterIncomingServerSession(domain); SessionManager.getInstance().unregisterIncomingServerSessions(domain);
}
/**
* Returns a collection with the domains and subdomains of the local server used by
* the remote server when validating the session. This information is only used to
* prevent many connections from the same remote server to the same domain or
* subdomain of the local server.
*
* @return collection with the domains and subdomains of the local server used by
* the remote server when validating the session.
*/
public Collection<String> getLocalDomains() {
return Collections.unmodifiableCollection(localDomains);
}
/**
* Adds a new domain or subdomain of the local server used by the remote server when asking
* to validate the session. This information is only used to prevent many connections from
* the same remote server to the same domain or subdomain of the local server.
*
* @param domain the domain or subdomain of the local server used when validating the
* session.
*/
public void addLocalDomain(String domain) {
localDomains.add(domain);
} }
/** /**
......
/** /**
* $RCSfile$ * $RCSfile: OutgoingServerSession.java,v $
* $Revision$ * $Revision$
* $Date$ * $Date$
* *
...@@ -90,9 +90,8 @@ public class OutgoingServerSession extends Session { ...@@ -90,9 +90,8 @@ public class OutgoingServerSession extends Session {
OutgoingServerSession session = sessionManager.getOutgoingServerSession(hostname); OutgoingServerSession session = sessionManager.getOutgoingServerSession(hostname);
if (session == null) { if (session == null) {
// Try locating if the remote server has previously authenticated with this server // Try locating if the remote server has previously authenticated with this server
IncomingServerSession incomingSession = sessionManager.getIncomingServerSession( for (IncomingServerSession incomingSession : sessionManager
hostname); .getIncomingServerSessions(hostname)) {
if (incomingSession != null) {
for (String otherHostname : incomingSession.getValidatedDomains()) { for (String otherHostname : incomingSession.getValidatedDomains()) {
session = sessionManager.getOutgoingServerSession(otherHostname); session = sessionManager.getOutgoingServerSession(otherHostname);
if (session != null) { if (session != null) {
......
...@@ -59,11 +59,10 @@ public class RemoteServerManager { ...@@ -59,11 +59,10 @@ public class RemoteServerManager {
config.setPermission(Permission.blocked); config.setPermission(Permission.blocked);
addConfiguration(config); addConfiguration(config);
// Check if the remote server was connected and proceed to close the connection // Check if the remote server was connected and proceed to close the connection
Session session = SessionManager.getInstance().getIncomingServerSession(domain); for (Session session : SessionManager.getInstance().getIncomingServerSessions(domain)) {
if (session != null) {
session.getConnection().close(); session.getConnection().close();
} }
session = SessionManager.getInstance().getOutgoingServerSession(domain); Session session = SessionManager.getInstance().getOutgoingServerSession(domain);
if (session != null) { if (session != null) {
session.getConnection().close(); session.getConnection().close();
} }
...@@ -311,8 +310,10 @@ public class RemoteServerManager { ...@@ -311,8 +310,10 @@ public class RemoteServerManager {
// Check if the connected servers can remain connected to the server // Check if the connected servers can remain connected to the server
for (String hostname : SessionManager.getInstance().getIncomingServers()) { for (String hostname : SessionManager.getInstance().getIncomingServers()) {
if (!canAccess(hostname)) { if (!canAccess(hostname)) {
Session session = SessionManager.getInstance().getIncomingServerSession(hostname); for (Session session : SessionManager.getInstance()
session.getConnection().close(); .getIncomingServerSessions(hostname)) {
session.getConnection().close();
}
} }
} }
for (String hostname : SessionManager.getInstance().getOutgoingServers()) { for (String hostname : SessionManager.getInstance().getOutgoingServers()) {
......
/** /**
* $RCSfile$ * $RCSfile: ServerDialback.java,v $
* $Revision$ * $Revision$
* $Date$ * $Date$
* *
...@@ -33,6 +33,7 @@ import java.io.*; ...@@ -33,6 +33,7 @@ import java.io.*;
import java.net.Socket; import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.Collection;
/** /**
* Implementation of the Server Dialback method as defined by the RFC3920. * Implementation of the Server Dialback method as defined by the RFC3920.
...@@ -318,6 +319,7 @@ class ServerDialback { ...@@ -318,6 +319,7 @@ class ServerDialback {
if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) { if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) {
if (validateRemoteDomain(doc, streamID)) { if (validateRemoteDomain(doc, streamID)) {
String hostname = doc.attributeValue("from"); String hostname = doc.attributeValue("from");
String recipient = doc.attributeValue("to");
// Create a server Session for the remote server // Create a server Session for the remote server
IncomingServerSession session = sessionManager. IncomingServerSession session = sessionManager.
createIncomingServerSession(connection, streamID); createIncomingServerSession(connection, streamID);
...@@ -325,6 +327,9 @@ class ServerDialback { ...@@ -325,6 +327,9 @@ class ServerDialback {
session.setAddress(new JID(null, hostname, null)); session.setAddress(new JID(null, hostname, null));
// Add the validated domain as a valid domain // Add the validated domain as a valid domain
session.addValidatedDomain(hostname); session.addValidatedDomain(hostname);
// Add the domain or subdomain of the local server used when
// validating the session
session.addLocalDomain(recipient);
return session; return session;
} }
} }
...@@ -417,7 +422,15 @@ class ServerDialback { ...@@ -417,7 +422,15 @@ class ServerDialback {
return false; return false;
} }
else { else {
if (sessionManager.getIncomingServerSession(hostname) != null) { // Check if the remote server already has a connection to the target domain/subdomain
boolean alreadyExists = false;
for (IncomingServerSession session : sessionManager
.getIncomingServerSessions(hostname)) {
if (session.getLocalDomains().contains(recipient)) {
alreadyExists = true;
}
}
if (alreadyExists) {
// Remote server already has a IncomingServerSession created // Remote server already has a IncomingServerSession created
error = new StreamError(StreamError.Condition.not_authorized); error = new StreamError(StreamError.Condition.not_authorized);
sb = new StringBuilder(); sb = new StringBuilder();
......
<%-- <%--
- $RCSfile$ - $RCSfile: server-session-details.jsp,v $
- $Revision$ - $Revision$
- $Date$ - $Date$
- -
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
// Get the session & address objects // Get the session & address objects
SessionManager sessionManager = webManager.getSessionManager(); SessionManager sessionManager = webManager.getSessionManager();
IncomingServerSession inSession = sessionManager.getIncomingServerSession(hostname); List<IncomingServerSession> inSessions = sessionManager.getIncomingServerSessions(hostname);
OutgoingServerSession outSession = sessionManager.getOutgoingServerSession(hostname); OutgoingServerSession outSession = sessionManager.getOutgoingServerSession(hostname);
// Number dateFormatter for all numbers on this page: // Number dateFormatter for all numbers on this page:
...@@ -79,10 +79,10 @@ ...@@ -79,10 +79,10 @@
<fmt:message key="server.session.label.connection" /> <fmt:message key="server.session.label.connection" />
</td> </td>
<td> <td>
<% if (inSession != null && outSession == null) { %> <% if (!inSessions.isEmpty() && outSession == null) { %>
<img src="images/incoming_32x16.gif" width="32" height="16" border="0" title="<fmt:message key="server.session.connection.incoming" />"> <img src="images/incoming_32x16.gif" width="32" height="16" border="0" title="<fmt:message key="server.session.connection.incoming" />">
<fmt:message key="server.session.connection.incoming" /> <fmt:message key="server.session.connection.incoming" />
<% } else if (inSession == null && outSession != null) { %> <% } else if (inSessions.isEmpty() && outSession != null) { %>
<img src="images/outgoing_32x16.gif" width="32" height="16" border="0" title="<fmt:message key="server.session.connection.outgoing" />"> <img src="images/outgoing_32x16.gif" width="32" height="16" border="0" title="<fmt:message key="server.session.connection.outgoing" />">
<fmt:message key="server.session.connection.outgoing" /> <fmt:message key="server.session.connection.outgoing" />
<% } else { %> <% } else { %>
...@@ -96,10 +96,10 @@ ...@@ -96,10 +96,10 @@
<fmt:message key="server.session.details.hostname" /> <fmt:message key="server.session.details.hostname" />
</td> </td>
<td> <td>
<% if (inSession != null) { %> <% if (!inSessions.isEmpty()) { %>
<%= inSession.getConnection().getInetAddress().getHostAddress() %> <%= inSessions.get(0).getConnection().getInetAddress().getHostAddress() %>
/ /
<%= inSession.getConnection().getInetAddress().getHostName() %> <%= inSessions.get(0).getConnection().getInetAddress().getHostName() %>
<% } else if (outSession != null) { %> <% } else if (outSession != null) { %>
<%= outSession.getConnection().getInetAddress().getHostAddress() %> <%= outSession.getConnection().getInetAddress().getHostAddress() %>
/ /
...@@ -112,8 +112,8 @@ ...@@ -112,8 +112,8 @@
</div> </div>
<br> <br>
<% // Show details of the incoming session <% // Show details of the incoming sessions
if (inSession != null) { for (IncomingServerSession inSession : inSessions) {
%> %>
<b><fmt:message key="server.session.details.incoming_session" /></b> <b><fmt:message key="server.session.details.incoming_session" /></b>
<div class="jive-table"> <div class="jive-table">
......
...@@ -7,8 +7,9 @@ ...@@ -7,8 +7,9 @@
java.util.Calendar, java.util.Calendar,
javax.swing.*, javax.swing.*,
java.net.URL"%> java.net.URL"%>
<%@ page import="org.jivesoftware.messenger.server.IncomingServerSession"%>
<%-- <%--
- $RCSfile$ - $RCSfile: server-session-row.jspf,v $
- $Revision$ - $Revision$
- $Date$ - $Date$
--%> --%>
...@@ -40,12 +41,12 @@ ...@@ -40,12 +41,12 @@
</tr> </tr>
</table> </table>
</td> </td>
<% if (inSession != null && outSession == null) { %> <% if (!inSessions.isEmpty() && outSession == null) { %>
<td width="1%"> <td width="1%">
<img src="images/incoming_32x16.gif" width="32" height="16" border="0" title="<fmt:message key="server.session.connection.incoming" />"> <img src="images/incoming_32x16.gif" width="32" height="16" border="0" title="<fmt:message key="server.session.connection.incoming" />">
</td> </td>
<td width="10%"><fmt:message key="server.session.connection.incoming" /></td> <td width="10%"><fmt:message key="server.session.connection.incoming" /></td>
<% } else if (inSession == null && outSession != null) { %> <% } else if (inSessions.isEmpty() && outSession != null) { %>
<td width="1%"> <td width="1%">
<img src="images/outgoing_32x16.gif" width="32" height="16" border="0" title="<fmt:message key="server.session.connection.outgoing" />"> <img src="images/outgoing_32x16.gif" width="32" height="16" border="0" title="<fmt:message key="server.session.connection.outgoing" />">
</td> </td>
...@@ -59,17 +60,35 @@ ...@@ -59,17 +60,35 @@
<% Date creationDate = null; <% Date creationDate = null;
Date lastActiveDate = null; Date lastActiveDate = null;
if (inSession != null && outSession == null) { if (!inSessions.isEmpty() && outSession == null) {
creationDate = inSession.getCreationDate(); for (IncomingServerSession inSession : inSessions) {
lastActiveDate = inSession.getLastActiveDate(); if (creationDate == null || creationDate.after(inSession.getCreationDate())) {
// Use the creation date of the oldest incoming session
creationDate = inSession.getCreationDate();
}
if (lastActiveDate == null || lastActiveDate.before(inSession.getLastActiveDate())) {
// Use the last active date of the newest incoming session
lastActiveDate = inSession.getLastActiveDate();
}
}
} }
else if (inSession == null && outSession != null) { else if (inSessions.isEmpty() && outSession != null) {
creationDate = outSession.getCreationDate(); creationDate = outSession.getCreationDate();
lastActiveDate = outSession.getLastActiveDate(); lastActiveDate = outSession.getLastActiveDate();
} }
else { else {
creationDate = inSession.getCreationDate().before(outSession.getCreationDate()) ? inSession.getCreationDate() : outSession.getCreationDate(); for (IncomingServerSession inSession : inSessions) {
lastActiveDate = inSession.getLastActiveDate().after(outSession.getLastActiveDate()) ? inSession.getLastActiveDate() : outSession.getLastActiveDate(); if (creationDate == null || creationDate.after(inSession.getCreationDate())) {
// Use the creation date of the oldest incoming session
creationDate = inSession.getCreationDate();
}
if (lastActiveDate == null || lastActiveDate.before(inSession.getLastActiveDate())) {
// Use the last active date of the newest incoming session
lastActiveDate = inSession.getLastActiveDate();
}
}
creationDate = creationDate.before(outSession.getCreationDate()) ? creationDate : outSession.getCreationDate();
lastActiveDate = lastActiveDate.after(outSession.getLastActiveDate()) ? lastActiveDate : outSession.getLastActiveDate();
} }
Calendar creationCal = Calendar.getInstance(); Calendar creationCal = Calendar.getInstance();
creationCal.setTime(creationDate); creationCal.setTime(creationDate);
......
...@@ -60,11 +60,11 @@ ...@@ -60,11 +60,11 @@
// Close all connections related to the specified host // Close all connections related to the specified host
if (close) { if (close) {
try { try {
Session sess = sessionManager.getIncomingServerSession(hostname); for (Session sess : sessionManager.getIncomingServerSessions(hostname)) {
if (sess != null) {
sess.getConnection().close(); sess.getConnection().close();
} }
sess = sessionManager.getOutgoingServerSession(hostname);
Session sess = sessionManager.getOutgoingServerSession(hostname);
if (sess != null) { if (sess != null) {
sess.getConnection().close(); sess.getConnection().close();
} }
...@@ -183,9 +183,9 @@ ...@@ -183,9 +183,9 @@
hostnames = new ArrayList<String>(hostnames).subList(start, maxIndex); hostnames = new ArrayList<String>(hostnames).subList(start, maxIndex);
for (String host : hostnames) { for (String host : hostnames) {
count++; count++;
IncomingServerSession inSession = sessionManager.getIncomingServerSession(host); List<IncomingServerSession> inSessions = sessionManager.getIncomingServerSessions(host);
OutgoingServerSession outSession = sessionManager.getOutgoingServerSession(host); OutgoingServerSession outSession = sessionManager.getOutgoingServerSession(host);
if (inSession == null && outSession == null) { if (inSessions.isEmpty() && outSession == null) {
// If the connections were just closed then skip this host // If the connections were just closed then skip this host
continue; continue;
} }
......
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