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 {
* 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
* 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
......@@ -437,24 +439,59 @@ public class SessionManager extends BasicModule {
/**
* 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
* 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 session the incoming server session to the remote server.
*/
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
* 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.
*/
public void unregisterIncomingServerSession(String hostname) {
incomingServerSessions.remove(hostname);
public void unregisterIncomingServerSessions(String 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 {
}
/**
* Returns a session that was originated by a remote server. IncomingServerSession can only
* receive packets from the remote server but are not capable of sending packets to the remote
* server.
* Returns the list of sessions that were originated by a remote server. The list will be
* ordered chronologically. IncomingServerSession can only receive packets from the remote
* server but are not capable of sending packets to 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) {
return incomingServerSessions.get(hostname);
public List<IncomingServerSession> getIncomingServerSessions(String 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 {
IncomingServerSession session = (IncomingServerSession)handback;
// Remove all the hostnames that were registered for this server session
for (String hostname : session.getValidatedDomains()) {
unregisterIncomingServerSession(hostname);
unregisterIncomingServerSession(hostname, session);
}
}
}
......@@ -1495,14 +1538,16 @@ public class SessionManager extends BasicModule {
}
}
// Check incoming server sessions
for (IncomingServerSession session : incomingServerSessions.values()) {
try {
if (session.getLastActiveDate().getTime() < deadline) {
session.getConnection().close();
for (List<IncomingServerSession> sessions : incomingServerSessions.values()) {
for (IncomingServerSession session : sessions) {
try {
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$
* $Date$
*
......@@ -48,8 +48,20 @@ import java.util.Collections;
*/
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>();
/**
* 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
* before being returned. If the authentication process fails then the answer will be
......@@ -106,6 +118,7 @@ public class IncomingServerSession extends Session {
public boolean validateSubsequentDomain(Element dbResult) {
ServerDialback method = new ServerDialback(getConnection(), getServerName());
if (method.validateRemoteDomain(dbResult, getStreamID())) {
// Add the validated domain as a valid domain
addValidatedDomain(dbResult.attributeValue("from"));
return true;
}
......@@ -169,7 +182,32 @@ public class IncomingServerSession extends Session {
public void removeValidatedDomain(String domain) {
validatedDomains.remove(domain);
// 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$
* $Date$
*
......@@ -90,9 +90,8 @@ public class OutgoingServerSession extends Session {
OutgoingServerSession session = sessionManager.getOutgoingServerSession(hostname);
if (session == null) {
// Try locating if the remote server has previously authenticated with this server
IncomingServerSession incomingSession = sessionManager.getIncomingServerSession(
hostname);
if (incomingSession != null) {
for (IncomingServerSession incomingSession : sessionManager
.getIncomingServerSessions(hostname)) {
for (String otherHostname : incomingSession.getValidatedDomains()) {
session = sessionManager.getOutgoingServerSession(otherHostname);
if (session != null) {
......
......@@ -59,11 +59,10 @@ public class RemoteServerManager {
config.setPermission(Permission.blocked);
addConfiguration(config);
// Check if the remote server was connected and proceed to close the connection
Session session = SessionManager.getInstance().getIncomingServerSession(domain);
if (session != null) {
for (Session session : SessionManager.getInstance().getIncomingServerSessions(domain)) {
session.getConnection().close();
}
session = SessionManager.getInstance().getOutgoingServerSession(domain);
Session session = SessionManager.getInstance().getOutgoingServerSession(domain);
if (session != null) {
session.getConnection().close();
}
......@@ -311,8 +310,10 @@ public class RemoteServerManager {
// Check if the connected servers can remain connected to the server
for (String hostname : SessionManager.getInstance().getIncomingServers()) {
if (!canAccess(hostname)) {
Session session = SessionManager.getInstance().getIncomingServerSession(hostname);
session.getConnection().close();
for (Session session : SessionManager.getInstance()
.getIncomingServerSessions(hostname)) {
session.getConnection().close();
}
}
}
for (String hostname : SessionManager.getInstance().getOutgoingServers()) {
......
/**
* $RCSfile$
* $RCSfile: ServerDialback.java,v $
* $Revision$
* $Date$
*
......@@ -33,6 +33,7 @@ import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.TimeUnit;
import java.util.Collection;
/**
* Implementation of the Server Dialback method as defined by the RFC3920.
......@@ -318,6 +319,7 @@ class ServerDialback {
if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) {
if (validateRemoteDomain(doc, streamID)) {
String hostname = doc.attributeValue("from");
String recipient = doc.attributeValue("to");
// Create a server Session for the remote server
IncomingServerSession session = sessionManager.
createIncomingServerSession(connection, streamID);
......@@ -325,6 +327,9 @@ class ServerDialback {
session.setAddress(new JID(null, hostname, null));
// Add the validated domain as a valid domain
session.addValidatedDomain(hostname);
// Add the domain or subdomain of the local server used when
// validating the session
session.addLocalDomain(recipient);
return session;
}
}
......@@ -417,7 +422,15 @@ class ServerDialback {
return false;
}
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
error = new StreamError(StreamError.Condition.not_authorized);
sb = new StringBuilder();
......
<%--
- $RCSfile$
- $RCSfile: server-session-details.jsp,v $
- $Revision$
- $Date$
-
......@@ -39,7 +39,7 @@
// Get the session & address objects
SessionManager sessionManager = webManager.getSessionManager();
IncomingServerSession inSession = sessionManager.getIncomingServerSession(hostname);
List<IncomingServerSession> inSessions = sessionManager.getIncomingServerSessions(hostname);
OutgoingServerSession outSession = sessionManager.getOutgoingServerSession(hostname);
// Number dateFormatter for all numbers on this page:
......@@ -79,10 +79,10 @@
<fmt:message key="server.session.label.connection" />
</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" />">
<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" />">
<fmt:message key="server.session.connection.outgoing" />
<% } else { %>
......@@ -96,10 +96,10 @@
<fmt:message key="server.session.details.hostname" />
</td>
<td>
<% if (inSession != null) { %>
<%= inSession.getConnection().getInetAddress().getHostAddress() %>
<% if (!inSessions.isEmpty()) { %>
<%= inSessions.get(0).getConnection().getInetAddress().getHostAddress() %>
/
<%= inSession.getConnection().getInetAddress().getHostName() %>
<%= inSessions.get(0).getConnection().getInetAddress().getHostName() %>
<% } else if (outSession != null) { %>
<%= outSession.getConnection().getInetAddress().getHostAddress() %>
/
......@@ -112,8 +112,8 @@
</div>
<br>
<% // Show details of the incoming session
if (inSession != null) {
<% // Show details of the incoming sessions
for (IncomingServerSession inSession : inSessions) {
%>
<b><fmt:message key="server.session.details.incoming_session" /></b>
<div class="jive-table">
......
......@@ -7,8 +7,9 @@
java.util.Calendar,
javax.swing.*,
java.net.URL"%>
<%@ page import="org.jivesoftware.messenger.server.IncomingServerSession"%>
<%--
- $RCSfile$
- $RCSfile: server-session-row.jspf,v $
- $Revision$
- $Date$
--%>
......@@ -40,12 +41,12 @@
</tr>
</table>
</td>
<% if (inSession != null && outSession == null) { %>
<% if (!inSessions.isEmpty() && outSession == null) { %>
<td width="1%">
<img src="images/incoming_32x16.gif" width="32" height="16" border="0" title="<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%">
<img src="images/outgoing_32x16.gif" width="32" height="16" border="0" title="<fmt:message key="server.session.connection.outgoing" />">
</td>
......@@ -59,17 +60,35 @@
<% Date creationDate = null;
Date lastActiveDate = null;
if (inSession != null && outSession == null) {
creationDate = inSession.getCreationDate();
lastActiveDate = inSession.getLastActiveDate();
if (!inSessions.isEmpty() && outSession == null) {
for (IncomingServerSession inSession : inSessions) {
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();
lastActiveDate = outSession.getLastActiveDate();
}
else {
creationDate = inSession.getCreationDate().before(outSession.getCreationDate()) ? inSession.getCreationDate() : outSession.getCreationDate();
lastActiveDate = inSession.getLastActiveDate().after(outSession.getLastActiveDate()) ? inSession.getLastActiveDate() : outSession.getLastActiveDate();
for (IncomingServerSession inSession : inSessions) {
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();
creationCal.setTime(creationDate);
......
......@@ -60,11 +60,11 @@
// Close all connections related to the specified host
if (close) {
try {
Session sess = sessionManager.getIncomingServerSession(hostname);
if (sess != null) {
for (Session sess : sessionManager.getIncomingServerSessions(hostname)) {
sess.getConnection().close();
}
sess = sessionManager.getOutgoingServerSession(hostname);
Session sess = sessionManager.getOutgoingServerSession(hostname);
if (sess != null) {
sess.getConnection().close();
}
......@@ -183,9 +183,9 @@
hostnames = new ArrayList<String>(hostnames).subList(start, maxIndex);
for (String host : hostnames) {
count++;
IncomingServerSession inSession = sessionManager.getIncomingServerSession(host);
List<IncomingServerSession> inSessions = sessionManager.getIncomingServerSessions(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
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