use of org.jivesoftware.openfire.session.DomainPair in project Openfire by igniterealtime.
the class RoutingTableImpl method removeComponentRoute.
/**
* Remove local or remote component route.
*
* @param route the route of the component to be removed.
* @param nodeID The node to which the to-be-removed component was connected to.
*/
private boolean removeComponentRoute(JID route, NodeID nodeID) {
String address = route.getDomain();
boolean removed = false;
Lock lock = componentsCache.getLock(address);
lock.lock();
try {
HashSet<NodeID> nodes = componentsCache.get(address);
if (nodes != null) {
nodes.remove(nodeID);
if (nodes.isEmpty()) {
componentsCache.remove(address);
removed = true;
} else {
componentsCache.put(address, nodes);
}
}
} finally {
lock.unlock();
}
if (removed || XMPPServer.getInstance().getNodeID().equals(nodeID)) {
localRoutingTable.removeRoute(new DomainPair("", address));
}
return removed;
}
use of org.jivesoftware.openfire.session.DomainPair in project Openfire by igniterealtime.
the class RoutingTableImpl method routeToRemoteDomain.
/**
* Routes packets that are sent to other XMPP domains than the local XMPP
* domain.
*
* @param jid
* the recipient of the packet to route.
* @param packet
* the packet to route.
* @throws PacketException
* thrown if the packet is malformed (results in the sender's
* session being shutdown).
* @return {@code true} if the packet was routed successfully,
* {@code false} otherwise.
*/
private boolean routeToRemoteDomain(JID jid, Packet packet) {
if (!JiveGlobals.getBooleanProperty(ConnectionSettings.Server.ALLOW_ANONYMOUS_OUTBOUND_DATA, false)) {
// Disallow anonymous local users to send data to other domains than the local domain.
if (isAnonymousRoute(packet.getFrom())) {
Log.info("The anonymous user '{}' attempted to send data to '{}', which is on a remote domain. Openfire is configured to not allow anonymous users to send data to remote domains.", packet.getFrom(), jid);
return false;
}
}
if (!RemoteServerManager.canAccess(jid.getDomain())) {
// Check if the remote domain is in the blacklist
Log.info("Will not route: Remote domain {} is not accessible according to our configuration (typical causes: server federation is disabled, or domain is blacklisted).", jid.getDomain());
return false;
}
DomainPair domainPair = new DomainPair(packet.getFrom().getDomain(), jid.getDomain());
Log.trace("Routing to remote domain: {}", packet);
// block until until the OutgoingSessionPromise is done processing, and will then route the stanza through the localRoutingTable.
synchronized (OutgoingSessionPromise.getInstance().getMutex(domainPair)) {
if (OutgoingSessionPromise.getInstance().hasProcess(domainPair)) {
Log.trace("An outgoing session for {} is in process of being established. Queuing stanza for delivery when that's done.", domainPair);
OutgoingSessionPromise.getInstance().queue(domainPair, packet);
return true;
} else {
NodeID nodeID = serversCache.get(domainPair);
if (nodeID != null) {
if (server.getNodeID().equals(nodeID)) {
Log.trace("An outgoing session for {} is available on the local cluster node. Delivering stanza.", domainPair);
try {
localRoutingTable.getRoute(domainPair).process(packet);
return true;
} catch (UnauthorizedException e) {
Log.error("Unable to route packet " + packet.toXML(), e);
return false;
}
} else {
if (remotePacketRouter != null) {
Log.trace("An outgoing session for {} is available on a remote cluster node. Asking that node to deliver stanza.", domainPair);
return remotePacketRouter.routePacket(nodeID.toByteArray(), jid, packet);
} else {
Log.error("An outgoing session for {} is available on a remote cluster node, but no RemotePacketRouter exists!", domainPair);
return false;
}
}
} else {
Log.trace("A new outgoing session for {} is needed. Instantiating a new queue stanza for delivery when that's done.", domainPair);
OutgoingSessionPromise.getInstance().createProcess(domainPair, packet);
return true;
}
}
}
}
use of org.jivesoftware.openfire.session.DomainPair in project Openfire by igniterealtime.
the class RoutingTableImpl method restoreCacheContent.
/**
* When the local node is joining or leaving a cluster, {@link org.jivesoftware.util.cache.CacheFactory} will swap
* the implementation used to instantiate caches. This causes the cache content to be 'reset': it will no longer
* contain the data that's provided by the local node. This method restores data that's provided by the local node
* in the cache. It is expected to be invoked right after joining ({@link #joinedCluster()} or leaving
* ({@link #leftCluster()} a cluster.
*/
private void restoreCacheContent() {
Log.debug("Restoring cache content for cache '{}' by adding all outgoing server routes that are connected to the local cluster node.", serversCache.getName());
// Check if there are local s2s connections that are already in the cache for remote nodes
Set<DomainPair> localServerRoutesToRemove = new HashSet<>();
localRoutingTable.getServerRoutes().forEach(route -> route.getOutgoingDomainPairs().forEach(address -> {
final Lock lock = serversCache.getLock(address);
lock.lock();
try {
if (serversCache.containsKey(address)) {
Log.info("We have an s2s connection to {}, but this connection also exists on other nodes. They are not allowed to both exist, so this local s2s connection will be terminated.", address);
localServerRoutesToRemove.add(address);
} else {
serversCache.put(address, server.getNodeID());
}
} finally {
lock.unlock();
}
}));
for (DomainPair localServerRouteToRemove : localServerRoutesToRemove) {
final RoutableChannelHandler route = localRoutingTable.getRoute(localServerRouteToRemove);
if (route instanceof LocalOutgoingServerSession) {
// That will result in the s2s connection actually being removed from the LocalRoutingTable.
try {
LocalOutgoingServerSession.class.cast(route).close();
} catch (Exception e) {
Log.warn("Failed to terminate the local s2s connection for " + localServerRouteToRemove + ".", e);
}
} else {
Log.warn("Failed to terminate the local s2s connection for {} because it is a {} instead of a LocalOutgoingServerSession.", localServerRouteToRemove, route.getClass());
}
}
Log.debug("Restoring cache content for cache '{}' by adding all component routes that are connected to the local cluster node.", componentsCache.getName());
localRoutingTable.getComponentRoute().forEach(route -> CacheUtil.addValueToMultiValuedCache(componentsCache, route.getAddress().getDomain(), server.getNodeID(), HashSet::new));
addLocalClientRoutesToCache();
}
use of org.jivesoftware.openfire.session.DomainPair in project Openfire by igniterealtime.
the class RemoteServerManager method blockAccess.
/**
* Blocks a remote server from connecting to the local server. If the remote server was
* connected when the permission was revoked then the connection of the entity will be closed.
*
* @param domain the domain of the remote server that is not allowed to connect.
*/
public static void blockAccess(String domain) {
// Remove any previous configuration for this remote server
deleteConfiguration(domain);
// Update the database with the new revoked permission
RemoteServerConfiguration config = new RemoteServerConfiguration(domain);
config.setPermission(Permission.blocked);
addConfiguration(config);
// Check if the remote server was connected and proceed to close the connection
for (Session session : SessionManager.getInstance().getIncomingServerSessions(domain)) {
Log.debug("Closing session for domain '{}' as the domain is being blocked. Affected session: {}", domain, session);
session.close();
}
// Can't just lookup a single remote server anymore, so check them all.
for (DomainPair domainPair : SessionManager.getInstance().getOutgoingDomainPairs()) {
if (domainPair.getRemote().equals(domain)) {
Session session = SessionManager.getInstance().getOutgoingServerSession(domainPair);
Log.debug("Closing (domain-pair) session for domain '{}' as the domain is being blocked. Affected session: {}", domain, session);
session.close();
}
}
}
use of org.jivesoftware.openfire.session.DomainPair in project Openfire by igniterealtime.
the class IQRouter method handle.
private void handle(IQ packet) {
JID recipientJID = packet.getTo();
// Check if the packet was sent to the server hostname
if (recipientJID != null && recipientJID.getNode() == null && recipientJID.getResource() == null && serverName.equals(recipientJID.getDomain())) {
Element childElement = packet.getChildElement();
if (childElement != null && childElement.element("addresses") != null) {
// Packet includes multicast processing instructions. Ask the multicastRouter
// to route this packet
multicastRouter.route(packet);
return;
}
}
if (packet.getID() != null && (IQ.Type.result == packet.getType() || IQ.Type.error == packet.getType())) {
// The server got an answer to an IQ packet that was sent from the server
// If there's a listener for this result at all, then it's likely that that listener had been registered
// on this cluster node. For efficiency, try the local cluster node before triggering tasks in the rest
// of the cluster.
IQResultListener iqResultListener = resultListeners.remove(packet.getID());
if (iqResultListener != null) {
resultTimeout.remove(packet.getID());
resultPending.remove(packet.getID());
try {
iqResultListener.receivedAnswer(packet);
} catch (Exception e) {
Log.error("Error processing answer of remote entity. Answer: " + packet.toXML(), e);
}
return;
} else if (ClusterManager.isClusteringStarted()) {
// Only do lookups in the cluster, after it's determined that the local node cannot process the result.
// remove it, to reduce the risk of this packet being sent back and forth.
final NodeID nodeID = resultPending.remove(packet.getID());
if (nodeID != null && !XMPPServer.getInstance().getNodeID().equals(nodeID)) {
CacheFactory.doClusterTask(new IQResultListenerTask(packet), nodeID.toByteArray());
return;
}
}
}
try {
// they are not allowed in s2s traffic.
if (packet.getFrom() == null && !XMPPServer.getInstance().isLocal(recipientJID)) {
// Stanzas that originate from clients _always_ have a 'from' attribute (as that attribute value is set/
// overwritten by Openfire upon receiving the stanza, to prevent abuse where a user tries to impersonate
// someone else). That means that, if we're processing a stanza without a 'from' attribute, that the
// stanza is very likely to originate from Openfire's code. If we have code that generates a stanza
// without a 'from' address but addressed to a remote domain, this simply is a bug that we should very
// verbosely warn about.
Log.error("Unable to process a stanza that has no 'from' attribute, addressed to a remote entity. Stanza is being dropped: {}", packet.toXML());
return;
}
if (recipientJID != null && (routingTable.hasComponentRoute(recipientJID) || (packet.getFrom() != null && routingTable.hasServerRoute(new DomainPair(packet.getFrom().getDomain(), recipientJID.getDomain()))))) {
// A component/service/remote server was found that can handle the Packet
routingTable.routePacket(recipientJID, packet, false);
return;
}
if (isLocalServer(recipientJID)) {
// Let the server handle the Packet
Element childElement = packet.getChildElement();
String namespace = null;
if (childElement != null) {
namespace = childElement.getNamespaceURI();
}
if (namespace == null) {
if (packet.getType() != IQ.Type.result && packet.getType() != IQ.Type.error) {
// Do nothing. We can't handle queries outside of a valid namespace
Log.warn("Unknown packet " + packet.toXML());
}
} else {
// Check if communication to local users is allowed
if (recipientJID != null && userManager.isRegisteredUser(recipientJID, false)) {
PrivacyList list = PrivacyListManager.getInstance().getDefaultPrivacyList(recipientJID.getNode());
if (list != null && list.shouldBlockPacket(packet)) {
// Communication is blocked
if (IQ.Type.set == packet.getType() || IQ.Type.get == packet.getType()) {
// Answer that the service is unavailable
sendErrorPacket(packet, PacketError.Condition.service_unavailable);
}
return;
}
}
IQHandler handler = getHandler(namespace);
if (handler == null) {
if (recipientJID == null) {
// Answer an error since the server can't handle the requested namespace
sendErrorPacket(packet, PacketError.Condition.service_unavailable);
} else if (recipientJID.getNode() == null || "".equals(recipientJID.getNode())) {
// Answer an error if JID is of the form <domain>
sendErrorPacket(packet, PacketError.Condition.feature_not_implemented);
} else {
// JID is of the form <node@domain>
// Answer an error since the server can't handle packets sent to a node
sendErrorPacket(packet, PacketError.Condition.service_unavailable);
}
} else {
handler.process(packet);
}
}
} else {
// If the user account identified by the 'to' attribute does not exist, how the stanza is processed depends on the stanza type.
if (recipientJID != null && recipientJID.getNode() != null && serverName.equals(recipientJID.getDomain()) && !userManager.isRegisteredUser(recipientJID, false) && sessionManager.getSession(recipientJID) == null && (IQ.Type.set == packet.getType() || IQ.Type.get == packet.getType())) {
// For an IQ stanza, the server MUST return a <service-unavailable/> stanza error to the sender.
sendErrorPacket(packet, PacketError.Condition.service_unavailable);
return;
}
ClientSession session = sessionManager.getSession(packet.getFrom());
boolean isAcceptable = true;
if (session instanceof LocalClientSession) {
// Check if we could process IQ stanzas from the recipient.
// If not, return a not-acceptable error as per XEP-0016:
// If the user attempts to send an outbound stanza to a contact and that stanza type is blocked, the user's server MUST NOT route the stanza to the contact but instead MUST return a <not-acceptable/> error
IQ dummyIQ = packet.createCopy();
dummyIQ.setFrom(packet.getTo());
dummyIQ.setTo(packet.getFrom());
if (!((LocalClientSession) session).canProcess(dummyIQ)) {
packet.setTo(session.getAddress());
packet.setFrom((JID) null);
packet.setError(PacketError.Condition.not_acceptable);
session.process(packet);
isAcceptable = false;
}
}
if (isAcceptable) {
// JID is of the form <node@domain/resource> or belongs to a remote server
// or to an uninstalled component
routingTable.routePacket(recipientJID, packet, false);
}
}
} catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error.routing"), e);
Session session = sessionManager.getSession(packet.getFrom());
if (session != null) {
IQ reply = IQ.createResultIQ(packet);
reply.setError(PacketError.Condition.internal_server_error);
session.process(reply);
}
}
}
Aggregations