Commit f9e1ffd8 authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gaston

Initial version. JM-6


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@1368 b35dd754-fafc-0310-a699-88a17e54d16e
parent 3693743a
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger;
/**
* Thrown when something failed verifying the key of a Originating Server with an Authoritative
* Server in a dialback operation.
*
* @author Gaston Dombiak
*/
public class RemoteConnectionFailedException extends Exception {
public RemoteConnectionFailedException() {
super();
}
public RemoteConnectionFailedException(String msg) {
super(msg);
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.net;
import org.dom4j.Element;
import org.jivesoftware.messenger.ClientSession;
import org.jivesoftware.messenger.PacketRouter;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.xmlpull.v1.XmlPullParserException;
import org.xmpp.packet.IQ;
import org.xmpp.packet.Message;
import org.xmpp.packet.Presence;
import java.io.IOException;
import java.net.Socket;
/**
* A SocketReader specialized for client connections. This reader will be used when the open
* stream contains a jabber:client namespace. Received packet will have their FROM attribute
* overriden to avoid spoofing.
*
* @author Gaston Dombiak
*/
public class ClientSocketReader extends SocketReader {
public ClientSocketReader(PacketRouter router, String serverName, Socket socket,
SocketConnection connection) {
super(router, serverName, socket, connection);
}
protected void processIQ(IQ packet) throws UnauthorizedException {
// Overwrite the FROM attribute to avoid spoofing
packet.setFrom(session.getAddress());
super.processIQ(packet);
}
protected void processPresence(Presence packet) throws UnauthorizedException {
// Overwrite the FROM attribute to avoid spoofing
packet.setFrom(session.getAddress());
super.processPresence(packet);
}
protected void processMessage(Message packet) throws UnauthorizedException {
// Overwrite the FROM attribute to avoid spoofing
packet.setFrom(session.getAddress());
super.processMessage(packet);
}
/**
* Only packets of type Message, Presence and IQ can be processed by this class. Any other
* type of packet is unknown and thus rejected generating the connection to be closed.
*
* @param doc the unknown DOM element that was received
* @return always false.
*/
protected boolean processUnknowPacket(Element doc) {
return false;
}
boolean createSession(String namespace) throws UnauthorizedException, XmlPullParserException,
IOException {
if ("jabber:client".equals(namespace)) {
// The connected client is a regular client so create a ClientSession
session = ClientSession.createSession(serverName, reader, connection);
return true;
}
return false;
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.net;
import org.dom4j.Element;
import org.jivesoftware.messenger.ComponentSession;
import org.jivesoftware.messenger.PacketRouter;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.net.Socket;
/**
* A SocketReader specialized for component connections. This reader will be used when the open
* stream contains a jabber:component:accept namespace.
*
* @author Gaston Dombiak
*/
public class ComponentSocketReader extends SocketReader {
public ComponentSocketReader(PacketRouter router, String serverName, Socket socket,
SocketConnection connection) {
super(router, serverName, socket, connection);
}
/**
* Only packets of type Message, Presence and IQ can be processed by this class. Any other
* type of packet is unknown and thus rejected generating the connection to be closed.
*
* @param doc the unknown DOM element that was received
* @return always false.
*/
protected boolean processUnknowPacket(Element doc) {
return false;
}
boolean createSession(String namespace) throws UnauthorizedException, XmlPullParserException,
IOException {
if ("jabber:component:accept".equals(namespace)) {
// The connected client is a component so create a ComponentSession
session = ComponentSession.createSession(serverName, reader, connection);
return true;
}
return false;
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.net;
import org.dom4j.Element;
import org.jivesoftware.messenger.PacketRouter;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.interceptor.PacketRejectedException;
import org.jivesoftware.messenger.server.IncomingServerSession;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.xmlpull.v1.XmlPullParserException;
import org.xmpp.packet.*;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* A SocketReader specialized for server connections. This reader will be used when the open
* stream contains a jabber:server namespace. Server-to-server communication requires two
* TCP connections between the servers where one is used for sending packets whilst the other
* connection is used for receiving packets. The connection used for receiving packets will use
* a ServerSocketReader since the other connection will not receive packets.<p>
*
* The received packets will be routed using another thread to ensure that many received packets
* could be routed at the same time. To avoid creating new threads every time a packet is received
* each <tt>ServerSocketReader</tt> instance uses a {@link ThreadPoolExecutor}. By default the
* maximum number of threads that the executor may have is 50. However, this value may be modified
* by changing the property <b>xmpp.server.processing.threads</b>.
*
* @author Gaston Dombiak
*/
public class ServerSocketReader extends SocketReader {
/**
* Pool of threads that are available for processing the requests.
*/
private ThreadPoolExecutor threadPool;
public ServerSocketReader(PacketRouter router, String serverName, Socket socket,
SocketConnection connection) {
super(router, serverName, socket, connection);
// Create a pool of threads that will process received packets. If more threads are
// required then the command will be executed on the SocketReader process
int maxThreads = JiveGlobals.getIntProperty("xmpp.server.processing.threads", 50);
threadPool =
new ThreadPoolExecutor(1, maxThreads, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
/**
* Processes the packet in another thread if the packet has not been rejected.
*
* @param packet the received packet.
*/
protected void processIQ(final IQ packet) throws UnauthorizedException {
try {
packetReceived(packet);
// Process the packet in another thread
threadPool.execute(new Runnable() {
public void run() {
try {
ServerSocketReader.super.processIQ(packet);
}
catch (UnauthorizedException e) {
Log.error("Error processing packet", e);
}
}
});
}
catch (PacketRejectedException e) {
// Do nothing
}
}
/**
* Processes the packet in another thread if the packet has not been rejected.
*
* @param packet the received packet.
*/
protected void processPresence(final Presence packet) throws UnauthorizedException {
try {
packetReceived(packet);
// Process the packet in another thread
threadPool.execute(new Runnable() {
public void run() {
try {
ServerSocketReader.super.processPresence(packet);
}
catch (UnauthorizedException e) {
Log.error("Error processing packet", e);
}
}
});
}
catch (PacketRejectedException e) {
// Do nothing
}
}
/**
* Processes the packet in another thread if the packet has not been rejected.
*
* @param packet the received packet.
*/
protected void processMessage(final Message packet) throws UnauthorizedException {
try {
packetReceived(packet);
// Process the packet in another thread
threadPool.execute(new Runnable() {
public void run() {
try {
ServerSocketReader.super.processMessage(packet);
}
catch (UnauthorizedException e) {
Log.error("Error processing packet", e);
}
}
});
}
catch (PacketRejectedException e) {
// Do nothing
}
}
/**
* Remote servers may send subsequent db:result packets so we need to process them in order
* to validate new domains.
*
* @param doc the unknown DOM element that was received
* @return true if the packet is a db:result packet otherwise false.
*/
protected boolean processUnknowPacket(Element doc) {
// Handle subsequent db:result packets
if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) {
if (!((IncomingServerSession) session).validateSubsequentDomain(doc)) {
open = false;
}
return true;
}
return false;
}
/**
* Make sure that the received packet has a TO and FROM values defined and that it was sent
* from a previously validated domain. If the packet does not matches any of the above
* conditions then a PacketRejectedException will be thrown.
*
* @param packet the received packet.
* @throws PacketRejectedException if the packet does not include a TO or FROM or if the packet
* was sent from a domain that was not previously validated.
*/
private void packetReceived(Packet packet) throws PacketRejectedException {
if (packet.getTo() == null || packet.getFrom() == null) {
// Send a stream error saying that the packet includes no TO or FROM
StreamError error = new StreamError(StreamError.Condition.improper_addressing);
connection.deliverRawText(error.toXML());
// Close the underlying connection
connection.close();
open = false;
throw new PacketRejectedException("Packet with no TO or FROM attributes");
}
else if (!((IncomingServerSession) session).isValidDomain(packet.getFrom().getDomain())) {
// Send a stream error saying that the packet includes an invalid FROM
StreamError error = new StreamError(StreamError.Condition.invalid_from);
connection.deliverRawText(error.toXML());
// Close the underlying connection
connection.close();
open = false;
throw new PacketRejectedException("Packet with no TO or FROM attributes");
}
}
protected void shutdown() {
super.shutdown();
// Shutdown the pool of threads that are processing packets sent by
// the remote server
threadPool.shutdown();
}
boolean createSession(String namespace) throws UnauthorizedException, XmlPullParserException,
IOException {
if ("jabber:server".equals(namespace)) {
// The connected client is a server so create an IncomingServerSession
session = IncomingServerSession.createSession(serverName, reader, connection);
return true;
}
return false;
}
}
This diff is collapsed.
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.server;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.net.SocketConnection;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.xmpp.packet.Packet;
import org.dom4j.io.XPPPacketReader;
import org.dom4j.Element;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParser;
import java.io.IOException;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Collections;
/**
* Server-to-server communication is done using two TCP connections between the servers. One
* connection is used for sending packets while the other connection is used for receiving packets.
* The <tt>IncomingServerSession</tt> represents the connection to a remote server that will only
* be used for receiving packets.<p>
*
* Currently only the Server Dialback method is being used for authenticating the remote server.
* Once the remote server has been authenticated incoming packets will be processed by this server.
* It is also possible for remote servers to authenticate more domains once the session has been
* established. For optimization reasons the existing connection is used between the servers.
* Therefore, the incoming server session holds the list of authenticated domains which are allowed
* to send packets to this server.<p>
*
* Using the Server Dialback method it is possible that this server may also act as the
* Authoritative Server. This implies that an incoming connection will be established with this
* server for authenticating a domain. This incoming connection will only last for a brief moment
* and after the domain has been authenticated the connection will be closed and no session will
* exist.
*
* @author Gaston Dombiak
*/
public class IncomingServerSession extends Session {
private Collection<String> validatedDomains = 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
* <tt>null</tt>.<p>
*
* Currently the Server Dialback method is the only way to authenticate a remote server. Since
* Server Dialback requires an Authoritative Server, it is possible for this server to receive
* an incoming connection that will only exist until the requested domain has been validated.
* In this case, this method will return <tt>null</tt> since the connection is closed after
* the domain was validated. See
* {@link ServerDialback#createIncomingSession(org.dom4j.io.XPPPacketReader)} for more
* information.
*
* @param serverName hostname of this server.
* @param reader reader on the new established connection with the remote server.
* @param connection the new established connection with the remote server.
* @return a new session that will receive packets or null if a problem occured while
* authenticating the remote server or when acting as the Authoritative Server during
* a Server Dialback authentication process.
* @throws XmlPullParserException if an error occurs while parsing the XML.
* @throws IOException if an input/output error occurs while using the connection.
*/
public static Session createSession(String serverName, XPPPacketReader reader,
SocketConnection connection) throws XmlPullParserException, IOException {
XmlPullParser xpp = reader.getXPPParser();
if (xpp.getNamespace("db") != null) {
ServerDialback method = new ServerDialback(connection, serverName);
return method.createIncomingSession(reader);
}
// Close the connection since we only support server dialback for s2s communication
connection.close();
return null;
}
public IncomingServerSession(String serverName, Connection connection, StreamID streamID) {
super(serverName, connection, streamID);
}
public void process(Packet packet) throws UnauthorizedException, PacketException {
//TODO Should never be called? Should be passed to the outgoing connection?
}
/**
* Returns true if the request of a new domain was valid. Sessions may receive subsequent
* domain validation request. If the validation of the new domain fails then the session and
* the underlying TCP connection will be closed.<p>
*
* For optimization reasons, the same session may be servicing several domains of a
* remote server.
*
* @param dbResult the DOM stanza requesting the domain validation.
* @return true if the requested domain was valid.
*/
public boolean validateSubsequentDomain(Element dbResult) {
ServerDialback method = new ServerDialback(getConnection(), getServerName());
if (method.validateRemoteDomain(dbResult, getStreamID())) {
addValidatedDomain(dbResult.attributeValue("from"));
return true;
}
return false;
}
/**
* Returns true if the specified domain has been validated for this session. The remote
* server should send a "db:result" packet for registering new subdomains or even
* virtual hosts.<p>
*
* In the spirit of being flexible we allow remote servers to not register subdomains
* and even so consider subdomains that include the server domain in their domain part
* as valid domains.
*
* @param domain the domain to validate.
* @return true if the specified domain has been validated for this session.
*/
public boolean isValidDomain(String domain) {
// Check if the specified domain is contained in any of the validated domains
for (String validatedDomain : getValidatedDomains()) {
if (domain.contains(validatedDomain)) {
return true;
}
}
return false;
}
/**
* Returns a collection with all the domains, subdomains and virtual hosts that where
* validated. The remote server is allowed to send packets from any of these domains,
* subdomains and virtual hosts.
*
* @return domains, subdomains and virtual hosts that where validated.
*/
public Collection<String> getValidatedDomains() {
return Collections.unmodifiableCollection(validatedDomains);
}
/**
* Adds a new validated domain, subdomain or virtual host to the list of
* validated domains for the remote server.
*
* @param domain the new validated domain, subdomain or virtual host to add.
*/
public void addValidatedDomain(String domain) {
if (validatedDomains.add(domain)) {
// Register the new validated domain for this server session in SessionManager
SessionManager.getInstance().registerIncomingServerSession(domain, this);
}
}
/**
* Removes the previously validated domain from the list of validated domains. The remote
* server will no longer be able to send packets from the removed domain, subdomain or
* virtual host.
*
* @param domain the domain, subdomain or virtual host to remove from the list of
* validated domains.
*/
public void removeValidatedDomain(String domain) {
validatedDomains.remove(domain);
// Unregister the validated domain for this server session in SessionManager
SessionManager.getInstance().unregisterIncomingServerSession(domain);
}
}
This diff is collapsed.
<body>
Classes used for server-to-server communication.
</body>
\ 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