📄 outgoingserversession.java
字号:
/**
* $RCSfile: OutgoingServerSession.java,v $
* $Revision: 3188 $
* $Date: 2005-12-12 00:28:19 -0300 (Mon, 12 Dec 2005) $
*
* 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.wildfire.server;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZInputStream;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.XMPPPacketReader;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.wildfire.*;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.net.DNSUtil;
import org.jivesoftware.wildfire.net.MXParser;
import org.jivesoftware.wildfire.net.SocketConnection;
import org.jivesoftware.wildfire.spi.BasicStreamIDFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmpp.packet.*;
import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.regex.Pattern;
/**
* 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>OutgoingServerSession</tt> represents the connection to a remote server that will only
* be used for sending packets.<p>
*
* Currently only the Server Dialback method is being used for authenticating with the remote
* server. Use {@link #authenticateDomain(String, String)} to create a new connection to a remote
* server that will be used for sending packets to the remote server from the specified domain.
* Only the authenticated domains with the remote server will be able to effectively send packets
* to the remote server. The remote server will reject and close the connection if a
* non-authenticated domain tries to send a packet through this connection.<p>
*
* Once the connection has been established with the remote server and at least a domain has been
* authenticated then a new route will be added to the routing table for this connection. For
* optimization reasons the same outgoing connection will be used even if the remote server has
* several hostnames. However, different routes will be created in the routing table for each
* hostname of the remote server.
*
* @author Gaston Dombiak
*/
public class OutgoingServerSession extends Session {
/**
* Regular expression to ensure that the hostname contains letters.
*/
private static Pattern pattern = Pattern.compile("[a-zA-Z]");
private Collection<String> authenticatedDomains = new ArrayList<String>();
private Collection<String> hostnames = new ArrayList<String>();
private OutgoingServerSocketReader socketReader;
/**
* Flag that indicates if the session was created usign server-dialback.
*/
private boolean usingServerDialback = true;
/**
* Creates a new outgoing connection to the specified hostname if no one exists. The port of
* the remote server could be configured by setting the <b>xmpp.server.socket.remotePort</b>
* property or otherwise the standard port 5269 will be used. Either a new connection was
* created or already existed the specified hostname will be authenticated with the remote
* server. Once authenticated the remote server will start accepting packets from the specified
* domain.<p>
*
* The Server Dialback method is currently the only implemented method for server-to-server
* authentication. This implies that the remote server will ask the Authoritative Server
* to verify the domain to authenticate. Most probably this server will act as the
* Authoritative Server. See {@link IncomingServerSession} for more information.
*
* @param domain the local domain to authenticate with the remote server.
* @param hostname the hostname of the remote server.
* @return True if the domain was authenticated by the remote server.
*/
public static boolean authenticateDomain(String domain, String hostname) {
if (hostname == null || hostname.length() == 0 || hostname.trim().indexOf(' ') > -1) {
// Do nothing if the target hostname is empty, null or contains whitespaces
return false;
}
try {
// Check if the remote hostname is in the blacklist
if (!RemoteServerManager.canAccess(hostname)) {
return false;
}
// Check if a session, that is using server dialback, already exists to the desired
// hostname (i.e. remote server). If no one exists then create a new session. The same
// session will be used for the same hostname for all the domains to authenticate
SessionManager sessionManager = SessionManager.getInstance();
OutgoingServerSession session = sessionManager.getOutgoingServerSession(hostname);
if (session == null) {
// Try locating if the remote server has previously authenticated with this server
for (IncomingServerSession incomingSession : sessionManager
.getIncomingServerSessions(hostname)) {
for (String otherHostname : incomingSession.getValidatedDomains()) {
session = sessionManager.getOutgoingServerSession(otherHostname);
if (session != null) {
if (session.usingServerDialback) {
// A session to the same remote server but with different hostname
// was found. Use this session and add the new hostname to the
// session
session.addHostname(hostname);
break;
}
else {
session = null;
}
}
}
}
}
if (session == null) {
int port = RemoteServerManager.getPortForServer(hostname);
// No session was found to the remote server so make sure that only one is created
synchronized (hostname.intern()) {
session = sessionManager.getOutgoingServerSession(hostname);
if (session == null) {
session = createOutgoingSession(domain, hostname, port);
if (session != null) {
// Add the new hostname to the list of names that the server may have
session.addHostname(hostname);
// Add the validated domain as an authenticated domain
session.addAuthenticatedDomain(domain);
// Notify the SessionManager that a new session has been created
sessionManager.outgoingServerSessionCreated(session);
return true;
}
else {
// Ensure that the hostname is not an IP address (i.e. contains chars)
if (!pattern.matcher(hostname).find()) {
return false;
}
// Check if hostname is a subdomain of an existing outgoing session
for (String otherHost : sessionManager.getOutgoingServers()) {
if (hostname.contains(otherHost)) {
session = sessionManager.getOutgoingServerSession(otherHost);
// Add the new hostname to the found session
session.addHostname(hostname);
return true;
}
}
// Try to establish a connection to candidate hostnames. Iterate on the
// substring after the . and try to establish a connection. If a
// connection is established then the same session will be used for
// sending packets to the "candidate hostname" as well as for the
// requested hostname (i.e. the subdomain of the candidate hostname)
// This trick is useful when remote servers haven't registered in their
// DNSs an entry for their subdomains
int index = hostname.indexOf('.');
while (index > -1 && index < hostname.length()) {
String newHostname = hostname.substring(index + 1);
String serverName = XMPPServer.getInstance().getServerInfo()
.getName();
if ("com".equals(newHostname) || "net".equals(newHostname) ||
"org".equals(newHostname) ||
"gov".equals(newHostname) ||
"edu".equals(newHostname) ||
serverName.equals(newHostname)) {
return false;
}
session = createOutgoingSession(domain, newHostname, port);
if (session != null) {
// Add the new hostname to the list of names that the server may have
session.addHostname(hostname);
// Add the validated domain as an authenticated domain
session.addAuthenticatedDomain(domain);
// Notify the SessionManager that a new session has been created
sessionManager.outgoingServerSessionCreated(session);
// Add the new hostname to the found session
session.addHostname(newHostname);
return true;
}
else {
index = hostname.indexOf('.', index + 1);
}
}
return false;
}
}
}
}
// A session already exists. The session was established using server dialback so
// it is possible to do piggybacking to authenticate more domains
if (session.getAuthenticatedDomains().contains(domain)) {
// Do nothing since the domain has already been authenticated
return true;
}
// A session already exists so authenticate the domain using that session
return session.authenticateSubdomain(domain, hostname);
}
catch (Exception e) {
Log.error("Error authenticating domain with remote server: " + hostname, e);
}
return false;
}
/**
* Establishes a new outgoing session to a remote server. If the remote server supports TLS
* and SASL then the new outgoing connection will be secured with TLS and authenticated
* using SASL. However, if TLS or SASL is not supported by the remote server or if an
* error occured while securing or authenticating the connection using SASL then server
* dialback method will be used.
*
* @param domain the local domain to authenticate with the remote server.
* @param hostname the hostname of the remote server.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -