use of org.jivesoftware.openfire.net.SocketConnection in project Openfire by igniterealtime.
the class LocalOutgoingServerSession method createOutgoingSession.
/**
* Establishes a new outgoing session to a remote domain. If the remote domain 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 domain or if an error occurred while securing or authenticating the connection using SASL
* then server dialback will be used.
*
* @param domainPair the local and remote domain for which a session is to be established.
* @param port default port to use to establish the connection.
* @return new outgoing session to a remote domain, or null.
*/
private static LocalOutgoingServerSession createOutgoingSession(@Nonnull final DomainPair domainPair, int port) {
final Logger log = LoggerFactory.getLogger(Log.getName() + "[Create outgoing session for: " + domainPair + "]");
log.debug("Creating new session...");
// Connect to remote server using XMPP 1.0 (TLS + SASL EXTERNAL or TLS + server dialback or server dialback)
log.debug("Creating plain socket connection to a host that belongs to the remote XMPP domain.");
final Map.Entry<Socket, Boolean> socketToXmppDomain = SocketUtil.createSocketToXmppDomain(domainPair.getRemote(), port);
if (socketToXmppDomain == null) {
log.info("Unable to create new session: Cannot create a plain socket connection with any applicable remote host.");
return null;
}
Socket socket = socketToXmppDomain.getKey();
boolean directTLS = socketToXmppDomain.getValue();
SocketConnection connection = null;
try {
final SocketAddress socketAddress = socket.getRemoteSocketAddress();
log.debug("Opening a new connection to {} {}.", socketAddress, directTLS ? "using directTLS" : "that is initially not encrypted");
connection = new SocketConnection(XMPPServer.getInstance().getPacketDeliverer(), socket, false);
if (directTLS) {
try {
connection.startTLS(true, true);
} catch (SSLException ex) {
if (JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ON_PLAIN_DETECTION_ALLOW_NONDIRECTTLS_FALLBACK, true) && ex.getMessage().contains("plaintext connection?")) {
Log.warn("Plaintext detected on a new connection that is was started in DirectTLS mode (socket address: {}). Attempting to restart the connection in non-DirectTLS mode.", socketAddress);
try {
// Close old socket
socket.close();
} catch (Exception e) {
Log.debug("An exception occurred (and is ignored) while trying to close a socket that was already in an error state.", e);
}
socket = new Socket();
socket.connect(socketAddress, RemoteServerManager.getSocketTimeout());
connection = new SocketConnection(XMPPServer.getInstance().getPacketDeliverer(), socket, false);
directTLS = false;
Log.info("Re-established connection to {}. Proceeding without directTLS.", socketAddress);
} else {
// Do not retry as non-DirectTLS, rethrow the exception.
throw ex;
}
}
}
log.debug("Send the stream header and wait for response...");
StringBuilder openingStream = new StringBuilder();
openingStream.append("<stream:stream");
openingStream.append(" xmlns:db=\"jabber:server:dialback\"");
openingStream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
openingStream.append(" xmlns=\"jabber:server\"");
// OF-673
openingStream.append(" from=\"").append(domainPair.getLocal()).append("\"");
openingStream.append(" to=\"").append(domainPair.getRemote()).append("\"");
openingStream.append(" version=\"1.0\">");
connection.deliverRawText(openingStream.toString());
// Set a read timeout (of 5 seconds) so we don't keep waiting forever
int soTimeout = socket.getSoTimeout();
socket.setSoTimeout(5000);
XMPPPacketReader reader = new XMPPPacketReader();
final InputStream inputStream;
if (directTLS) {
inputStream = connection.getTLSStreamHandler().getInputStream();
} else {
inputStream = socket.getInputStream();
}
reader.getXPPParser().setInput(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
// Get the answer from the Receiving Server
XmlPullParser xpp = reader.getXPPParser();
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG; ) {
eventType = xpp.next();
}
String serverVersion = xpp.getAttributeValue("", "version");
String id = xpp.getAttributeValue("", "id");
log.debug("Got a response (stream ID: {}, version: {}). Check if the remote server is XMPP 1.0 compliant...", id, serverVersion);
if (serverVersion != null && decodeVersion(serverVersion)[0] >= 1) {
log.debug("The remote server is XMPP 1.0 compliant (or at least reports to be).");
// Restore default timeout
socket.setSoTimeout(soTimeout);
log.debug("Processing stream features of the remote domain...");
Element features = reader.parseDocument().getRootElement();
if (features != null) {
if (directTLS) {
log.debug("We connected to the remote server using direct TLS. Authenticate the connection with SASL...");
LocalOutgoingServerSession answer = authenticate(domainPair, connection, reader, openingStream, features, id);
if (answer != null) {
log.debug("Successfully authenticated the connection with SASL)!");
// Everything went fine so return the secured and
// authenticated connection
log.debug("Successfully created new session!");
return answer;
}
log.debug("Unable to authenticate the connection with SASL.");
} else {
log.debug("Check if both us as well as the remote server have enabled STARTTLS and/or dialback ...");
final boolean useTLS = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ENABLED, true);
if (useTLS && features.element("starttls") != null) {
log.debug("Both us and the remote server support the STARTTLS feature. Secure and authenticate the connection with TLS & SASL...");
LocalOutgoingServerSession answer = secureAndAuthenticate(domainPair, connection, reader, openingStream);
if (answer != null) {
log.debug("Successfully secured/authenticated the connection with TLS/SASL)!");
// Everything went fine so return the secured and
// authenticated connection
log.debug("Successfully created new session!");
return answer;
}
log.debug("Unable to secure and authenticate the connection with TLS & SASL.");
} else if (connection.getTlsPolicy() == Connection.TLSPolicy.required) {
log.debug("I have no StartTLS yet I must TLS");
connection.close();
return null;
} else // Check if we are going to try server dialback (XMPP 1.0)
if (ServerDialback.isEnabled() && features.element("dialback") != null) {
log.debug("Both us and the remote server support the 'dialback' feature. Authenticate the connection with dialback...");
ServerDialback method = new ServerDialback(connection, domainPair);
OutgoingServerSocketReader newSocketReader = new OutgoingServerSocketReader(reader);
if (method.authenticateDomain(newSocketReader, id)) {
log.debug("Successfully authenticated the connection with dialback!");
StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
LocalOutgoingServerSession session = new LocalOutgoingServerSession(domainPair.getLocal(), connection, newSocketReader, streamID);
connection.init(session);
// Set the remote domain name as the address of the session.
session.setAddress(new JID(null, domainPair.getRemote(), null));
log.debug("Successfully created new session!");
return session;
} else {
log.debug("Unable to authenticate the connection with dialback.");
}
}
}
} else {
log.debug("Error! No data from the remote server (expected a 'feature' element).");
}
} else {
log.debug("The remote server is not XMPP 1.0 compliant.");
}
log.debug("Something went wrong so close the connection and try server dialback over a plain connection");
if (connection.getTlsPolicy() == Connection.TLSPolicy.required) {
log.debug("I have no StartTLS yet I must TLS");
connection.close();
return null;
}
connection.close();
} catch (SSLHandshakeException e) {
// When not doing direct TLS but startTLS, this a failure as described in RFC3620, section 5.4.3.2 "STARTTLS Failure".
log.info("{} negotiation failed. Closing connection (without sending any data such as <failure/> or </stream>).", (directTLS ? "Direct TLS" : "StartTLS"), e);
// It is probably (see OF-794) best if we, as the initiating entity, therefor don't send any data either.
if (connection != null) {
connection.forceClose();
}
} catch (Exception e) {
// This might be RFC3620, section 5.4.2.2 "Failure Case" or even an unrelated problem. Handle 'normally'.
log.warn("An exception occurred while creating an encrypted session. Closing connection.", e);
if (connection != null) {
connection.close();
}
}
if (ServerDialback.isEnabled()) {
log.debug("Unable to create a new session. Going to try connecting using server dialback as a fallback.");
// Use server dialback (pre XMPP 1.0) over a plain connection
final LocalOutgoingServerSession outgoingSession = new ServerDialback(domainPair).createOutgoingSession(port);
if (outgoingSession != null) {
// TODO this success handler behaves differently from a similar success handler above. Shouldn't those be the same?
log.debug("Successfully created new session (using dialback as a fallback)!");
return outgoingSession;
} else {
log.warn("Unable to create a new session: Dialback (as a fallback) failed.");
return null;
}
} else {
log.warn("Unable to create a new session: exhausted all options (not trying dialback as a fallback, as server dialback is disabled by configuration.");
return null;
}
}
use of org.jivesoftware.openfire.net.SocketConnection in project Openfire by igniterealtime.
the class LocalIncomingServerSession method createSession.
/**
* 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>
*
* @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 org.xmlpull.v1.XmlPullParserException if an error occurs while parsing the XML.
* @throws java.io.IOException if an input/output error occurs while using the connection.
*/
public static LocalIncomingServerSession createSession(String serverName, XMPPPacketReader reader, SocketConnection connection) throws XmlPullParserException, IOException {
XmlPullParser xpp = reader.getXPPParser();
String version = xpp.getAttributeValue("", "version");
String fromDomain = xpp.getAttributeValue("", "from");
String toDomain = xpp.getAttributeValue("", "to");
int[] serverVersion = version != null ? decodeVersion(version) : new int[] { 0, 0 };
if (toDomain == null) {
toDomain = serverName;
}
try {
// Get the stream ID for the new session
StreamID streamID = SessionManager.getInstance().nextStreamID();
// Create a server Session for the remote server
LocalIncomingServerSession session = SessionManager.getInstance().createIncomingServerSession(connection, streamID, fromDomain);
// Send the stream header
StringBuilder openingStream = new StringBuilder();
openingStream.append("<stream:stream");
openingStream.append(" xmlns:db=\"jabber:server:dialback\"");
openingStream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
openingStream.append(" xmlns=\"jabber:server\"");
openingStream.append(" from=\"").append(toDomain).append("\"");
if (fromDomain != null) {
openingStream.append(" to=\"").append(fromDomain).append("\"");
}
openingStream.append(" id=\"").append(streamID).append("\"");
// implementations appears to reduce connection issues with those domains (patch by Marcin Cieślak).
if (serverVersion[0] >= 1) {
openingStream.append(" version=\"1.0\">");
} else {
openingStream.append('>');
}
connection.deliverRawText(openingStream.toString());
if (serverVersion[0] >= 1) {
// Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
// Indicate the TLS policy to use for this connection
Connection.TLSPolicy tlsPolicy = connection.getTlsPolicy();
boolean hasCertificates = false;
try {
hasCertificates = XMPPServer.getInstance().getCertificateStoreManager().getIdentityStore(ConnectionType.SOCKET_S2S).getStore().size() > 0;
} catch (Exception e) {
Log.error(e.getMessage(), e);
}
if (Connection.TLSPolicy.required == tlsPolicy && !hasCertificates) {
Log.error("Server session rejected. TLS is required but no certificates " + "were created.");
return null;
}
connection.setTlsPolicy(hasCertificates ? tlsPolicy : Connection.TLSPolicy.disabled);
}
// Indicate the compression policy to use for this connection
connection.setCompressionPolicy(connection.getConfiguration().getCompressionPolicy());
StringBuilder sb = new StringBuilder();
if (serverVersion[0] >= 1) {
// Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
// Don't offer stream-features to pre-1.0 servers, as it confuses them. Sending features to Openfire < 3.7.1 confuses it too - OF-443)
sb.append("<stream:features>");
if (JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ENABLED, true)) {
sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
if (!ServerDialback.isEnabled()) {
// Server dialback is disabled so TLS is required
sb.append("<required/>");
}
sb.append("</starttls>");
}
// Include available SASL Mechanisms
sb.append(SASLAuthentication.getSASLMechanisms(session));
if (ServerDialback.isEnabled()) {
// Also offer server dialback (when TLS is not required). Server dialback may be offered
// after TLS has been negotiated and a self-signed certificate is being used
sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"><errors/></dialback>");
}
sb.append("</stream:features>");
}
connection.deliverRawText(sb.toString());
// Set the domain or subdomain of the local server targeted by the remote server
session.setLocalDomain(serverName);
return session;
} catch (Exception e) {
Log.error("Error establishing connection from remote server:" + connection, e);
connection.close();
return null;
}
}
use of org.jivesoftware.openfire.net.SocketConnection in project Openfire by igniterealtime.
the class LocalIncomingServerSession method createSession.
/**
* 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
* {@code null}.<p>
*
* @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.
* @param directTLS true of connections are immediately encrypted (as opposed to plain text / startls).
* @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 org.xmlpull.v1.XmlPullParserException if an error occurs while parsing the XML.
* @throws java.io.IOException if an input/output error occurs while using the connection.
*/
public static LocalIncomingServerSession createSession(String serverName, XMPPPacketReader reader, SocketConnection connection, boolean directTLS) throws XmlPullParserException, IOException {
XmlPullParser xpp = reader.getXPPParser();
String version = xpp.getAttributeValue("", "version");
String fromDomain = xpp.getAttributeValue("", "from");
String toDomain = xpp.getAttributeValue("", "to");
int[] serverVersion = version != null ? decodeVersion(version) : new int[] { 0, 0 };
if (toDomain == null) {
toDomain = serverName;
}
try {
// Get the stream ID for the new session
StreamID streamID = SessionManager.getInstance().nextStreamID();
// Create a server Session for the remote server
LocalIncomingServerSession session = SessionManager.getInstance().createIncomingServerSession(connection, streamID, fromDomain);
// Send the stream header
StringBuilder openingStream = new StringBuilder();
openingStream.append("<stream:stream");
openingStream.append(" xmlns:db=\"jabber:server:dialback\"");
openingStream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
openingStream.append(" xmlns=\"jabber:server\"");
openingStream.append(" from=\"").append(toDomain).append("\"");
if (fromDomain != null) {
openingStream.append(" to=\"").append(fromDomain).append("\"");
}
openingStream.append(" id=\"").append(streamID).append("\"");
// implementations appears to reduce connection issues with those domains (patch by Marcin Cieślak).
if (serverVersion[0] >= 1) {
openingStream.append(" version=\"1.0\">");
} else {
openingStream.append('>');
}
connection.deliverRawText(openingStream.toString());
if (serverVersion[0] >= 1) {
// Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
// Indicate the TLS policy to use for this connection
Connection.TLSPolicy tlsPolicy = connection.getTlsPolicy();
boolean hasCertificates = false;
try {
hasCertificates = XMPPServer.getInstance().getCertificateStoreManager().getIdentityStore(ConnectionType.SOCKET_S2S).getStore().size() > 0;
} catch (Exception e) {
Log.error(e.getMessage(), e);
}
if (Connection.TLSPolicy.required == tlsPolicy && !hasCertificates) {
Log.error("Server session rejected. TLS is required but no certificates " + "were created.");
return null;
}
connection.setTlsPolicy(hasCertificates ? tlsPolicy : Connection.TLSPolicy.disabled);
}
// Indicate the compression policy to use for this connection
connection.setCompressionPolicy(connection.getConfiguration().getCompressionPolicy());
StringBuilder sb = new StringBuilder();
if (serverVersion[0] >= 1) {
// Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
// Don't offer stream-features to pre-1.0 servers, as it confuses them. Sending features to Openfire < 3.7.1 confuses it too - OF-443)
sb.append("<stream:features>");
if (!directTLS && JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ENABLED, true)) {
sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
if (!ServerDialback.isEnabled()) {
// Server dialback is disabled so TLS is required
sb.append("<required/>");
}
sb.append("</starttls>");
}
// Include available SASL Mechanisms
sb.append(SASLAuthentication.getSASLMechanisms(session));
if (ServerDialback.isEnabled()) {
// Also offer server dialback (when TLS is not required). Server dialback may be offered
// after TLS has been negotiated and a self-signed certificate is being used
sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"><errors/></dialback>");
}
sb.append("</stream:features>");
}
connection.deliverRawText(sb.toString());
// Set the domain or subdomain of the local server targeted by the remote server
session.setLocalDomain(serverName);
return session;
} catch (Exception e) {
Log.error("Error establishing connection from remote server:" + connection, e);
connection.close();
return null;
}
}
Aggregations