📄 serverdialback.java
字号:
/**
* $RCSfile: ServerDialback.java,v $
* $Revision: 3188 $
* $Date: 2005-12-12 00:28:19 -0300 (Mon, 12 Dec 2005) $
*
* Copyright (C) 2008 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, or a commercial license
* agreement with Jive.
*/
package org.jivesoftware.openfire.server;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.XMPPPacketReader;
import org.jivesoftware.openfire.*;
import org.jivesoftware.openfire.auth.AuthFactory;
import org.jivesoftware.openfire.net.DNSUtil;
import org.jivesoftware.openfire.net.MXParser;
import org.jivesoftware.openfire.net.ServerTrafficCounter;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.openfire.session.IncomingServerSession;
import org.jivesoftware.openfire.session.LocalIncomingServerSession;
import org.jivesoftware.openfire.session.LocalOutgoingServerSession;
import org.jivesoftware.openfire.spi.BasicStreamIDFactory;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmpp.packet.JID;
import org.xmpp.packet.StreamError;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* Implementation of the Server Dialback method as defined by the RFC3920.
*
* The dialback method follows the following logic to validate the remote server:
* <ol>
* <li>The Originating Server establishes a connection to the Receiving Server.</li>
* <li>The Originating Server sends a 'key' value over the connection to the Receiving
* Server.</li>
* <li>The Receiving Server establishes a connection to the Authoritative Server.</li>
* <li>The Receiving Server sends the same 'key' value to the Authoritative Server.</li>
* <li>The Authoritative Server replies that key is valid or invalid.</li>
* <li>The Receiving Server informs the Originating Server whether it is authenticated or
* not.</li>
* </ol>
*
* By default a timeout of 20 seconds will be used for reading packets from remote servers. Use
* the property <b>xmpp.server.read.timeout</b> to change that value. The value should be in
* milliseconds.
*
* @author Gaston Dombiak
*/
public class ServerDialback {
/**
* The utf-8 charset for decoding and encoding Jabber packet streams.
*/
protected static String CHARSET = "UTF-8";
/**
* Cache (unlimited, never expire) that holds the secret key to be used for
* encoding and decoding keys used for authentication.
* Key: constant hard coded value, Value: random generated string
*/
private static Cache<String, String> secretKeyCache;
private static XmlPullParserFactory FACTORY = null;
static {
try {
FACTORY = XmlPullParserFactory.newInstance(MXParser.class.getName(), null);
}
catch (XmlPullParserException e) {
Log.error("Error creating a parser factory", e);
}
secretKeyCache = CacheFactory.createCache("Secret Keys Cache");
}
private Connection connection;
private String serverName;
private SessionManager sessionManager = SessionManager.getInstance();
private RoutingTable routingTable = XMPPServer.getInstance().getRoutingTable();
/**
* Returns true if server dialback is enabled. When enabled remote servers may connect to this
* server using the server dialback method and this server may try the server dialback method
* to connect to remote servers.<p>
*
* When TLS is enabled between servers and server dialback method is enabled then TLS is going
* to be tried first, when connecting to a remote server, and if TLS fails then server dialback
* is going to be used as a last resort.
*
* @return true if server dialback is enabled.
*/
public static boolean isEnabled() {
return JiveGlobals.getBooleanProperty("xmpp.server.dialback.enabled", true);
}
/**
* Creates a new instance that will be used for creating {@link IncomingServerSession},
* validating subsequent domains or authenticatig new domains. Use
* {@link #createIncomingSession(org.dom4j.io.XMPPPacketReader)} for creating a new server
* session used for receiving packets from the remote server. Use
* {@link #validateRemoteDomain(org.dom4j.Element, org.jivesoftware.openfire.StreamID)} for
* validating subsequent domains and use
* {@link #authenticateDomain(OutgoingServerSocketReader, String, String, String)} for
* registering new domains that are allowed to send packets to the remote server.<p>
*
* For validating domains a new TCP connection will be established to the Authoritative Server.
* The Authoritative Server may be the same Originating Server or some other machine in the
* Originating Server's network. Once the remote domain gets validated the Originating Server
* will be allowed for sending packets to this server. However, this server will need to
* validate its domain/s with the Originating Server if this server needs to send packets to
* the Originating Server. Another TCP connection will be established for validation this
* server domain/s and for sending packets to the Originating Server.
*
* @param connection the connection created by the remote server.
* @param serverName the name of the local server.
*/
public ServerDialback(Connection connection, String serverName) {
this.connection = connection;
this.serverName = serverName;
}
public ServerDialback() {
}
/**
* Creates a new connection from the Originating Server to the Receiving Server for
* authenticating the specified domain.
*
* @param domain domain of the Originating Server to authenticate with the Receiving Server.
* @param hostname IP address or hostname of the Receiving Server.
* @param port port of the Receiving Server.
* @return an OutgoingServerSession if the domain was authenticated or <tt>null</tt> if none.
*/
public LocalOutgoingServerSession createOutgoingSession(String domain, String hostname, int port) {
String realHostname = null;
int realPort = port;
try {
// Establish a TCP connection to the Receiving Server
// Get the real hostname to connect to using DNS lookup of the specified hostname
DNSUtil.HostAddress address = DNSUtil.resolveXMPPServerDomain(hostname, port);
realHostname = address.getHost();
realPort = address.getPort();
Log.debug("ServerDialback: OS - Trying to connect to " + hostname + ":" + port +
"(DNS lookup: " + realHostname + ":" + realPort + ")");
// Connect to the remote server
Socket socket = new Socket();
socket.connect(new InetSocketAddress(realHostname, realPort),
RemoteServerManager.getSocketTimeout());
Log.debug("ServerDialback: OS - Connection to " + hostname + ":" + port + " successful");
connection =
new SocketConnection(XMPPServer.getInstance().getPacketDeliverer(), socket,
false);
// Get a writer for sending the open stream tag
// Send to the Receiving Server a stream header
StringBuilder stream = new StringBuilder();
stream.append("<stream:stream");
stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
stream.append(" xmlns=\"jabber:server\"");
stream.append(" xmlns:db=\"jabber:server:dialback\">");
connection.deliverRawText(stream.toString());
// Set a read timeout (of 5 seconds) so we don't keep waiting forever
int soTimeout = socket.getSoTimeout();
socket.setSoTimeout(RemoteServerManager.getSocketTimeout());
XMPPPacketReader reader = new XMPPPacketReader();
reader.setXPPFactory(FACTORY);
reader.getXPPParser().setInput(new InputStreamReader(
ServerTrafficCounter.wrapInputStream(socket.getInputStream()), CHARSET));
// Get the answer from the Receiving Server
XmlPullParser xpp = reader.getXPPParser();
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
}
if ("jabber:server:dialback".equals(xpp.getNamespace("db"))) {
// Restore default timeout
socket.setSoTimeout(soTimeout);
String id = xpp.getAttributeValue("", "id");
OutgoingServerSocketReader socketReader = new OutgoingServerSocketReader(reader);
if (authenticateDomain(socketReader, domain, hostname, id)) {
// Domain was validated so create a new OutgoingServerSession
StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
LocalOutgoingServerSession session = new LocalOutgoingServerSession(domain, connection, socketReader, streamID);
connection.init(session);
// Set the hostname as the address of the session
session.setAddress(new JID(null, hostname, null));
return session;
}
else {
// Close the connection
connection.close();
}
}
else {
Log.debug("ServerDialback: OS - Invalid namespace in packet: " + xpp.getText());
// Send an invalid-namespace stream error condition in the response
connection.deliverRawText(
new StreamError(StreamError.Condition.invalid_namespace).toXML());
// Close the connection
connection.close();
}
}
catch (IOException e) {
Log.debug("ServerDialback: Error connecting to the remote server: " + hostname + "(DNS lookup: " +
realHostname + ":" + realPort + ")", e);
// Close the connection
if (connection != null) {
connection.close();
}
}
catch (Exception e) {
Log.error("Error creating outgoing session to remote server: " + hostname +
"(DNS lookup: " +
realHostname +
")",
e);
// Close the connection
if (connection != null) {
connection.close();
}
}
return null;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -