Skip to content

Commit

Permalink
Merge STARTTLS changes from cobratbq/irc-api
Browse files Browse the repository at this point in the history
  • Loading branch information
maxpowa committed Oct 26, 2016
1 parent f9316f0 commit b22b495
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 4 deletions.
11 changes: 11 additions & 0 deletions src/main/java/com/ircclouds/irc/api/AbstractIRCSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.IOException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;

public abstract class AbstractIRCSession implements IIRCSession
{
Expand Down Expand Up @@ -168,4 +169,14 @@ public void run()
}
}.start();
}

@Override
public void secureConnection(final SSLContext aContext, final String aHostname, final int aPort) throws SSLException
{
if (!(this.conn instanceof SocketChannelConnection))
{
throw new IllegalArgumentException("unsupported connection type in use");
}
this.conn = new SSLSocketChannelConnection((SocketChannelConnection) this.conn, aContext, aHostname, aPort);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public boolean available() throws IOException
{
if (canRead)
{
ircData.append(getConnection().read());
ircData.append(getConnection().read());
canRead = false;

trySetNewLine();
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/ircclouds/irc/api/IRCApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import java.io.File;
import java.io.IOException;
import java.net.Proxy;
Expand Down Expand Up @@ -512,6 +514,12 @@ private void closeSession(Callback<IIRCState> aCallback, Dirty _d)
}
}

@Override
public void secureConnection(final SSLContext aContext, final String aHostname, final int aPort) throws SSLException
{
this.session.secureConnection(aContext, aHostname, aPort);
}

private void checkConnected()
{
if (!state.isConnected())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,37 @@ public class SSLSocketChannelConnection implements IConnection
private HandshakeStatus hStatus;
private int remaingUnwraps;

public SSLSocketChannelConnection()
{
}

/**
* Convert existing SocketChannelConnection into
* SSLSocketChannelConnection.
*
* @param aConnection Plain text socket channel connection.
* @param aContext SSL Context, may be null to use default SSL context.
* @param aHostname Host name.
* @param aPort Port number.
* @throws javax.net.ssl.SSLException Exception in case we fail to start
* an SSL handshake.
*/
public SSLSocketChannelConnection(SocketChannelConnection aConnection, SSLContext aContext, String aHostname, int aPort) throws SSLException
{
sChannel = aConnection.getSocketChannel();

sslEngine = aContext != null ? aContext.createSSLEngine(aHostname, aPort) : getDefaultSSLContext().createSSLEngine(aHostname, aPort);
sslEngine.setNeedClientAuth(false);
sslEngine.setUseClientMode(true);
sslEngine.beginHandshake();
hStatus = sslEngine.getHandshakeStatus();

appSendBuffer = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize());
cipherSendBuffer = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
cipherRecvBuffer = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
appRecvBuffer = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize());
}

@Override
public boolean open(String aHostname, int aPort, SSLContext aContext, Proxy aProxy, boolean resolveThroughProxy) throws IOException
{
Expand Down Expand Up @@ -216,7 +247,7 @@ private int wrapAndWrite() throws SSLException, IOException

private void executeTasks()
{
Runnable _r = null;
Runnable _r;
while ((_r = sslEngine.getDelegatedTask()) != null)
{
new Thread(_r).start();
Expand All @@ -225,22 +256,26 @@ private void executeTasks()
hStatus = sslEngine.getHandshakeStatus();
}

@SuppressWarnings("UseSpecificCatch")
private SSLContext getDefaultSSLContext()
{
try
{
SSLContext _sslCtx = SSLContext.getInstance("SSL");
_sslCtx.init(null, new TrustManager[] { new X509TrustManager()
{
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}

@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}

@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,9 @@ public void close() throws IOException
channel.close();
}
}

SocketChannel getSocketChannel()
{
return this.channel;
}
}
16 changes: 15 additions & 1 deletion src/main/java/com/ircclouds/irc/api/interfaces/IIRCApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import net.engio.mbassy.bus.MBassador;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import java.io.File;
import java.net.Proxy;
import java.net.SocketAddress;
Expand Down Expand Up @@ -388,4 +390,16 @@ public interface IIRCApi
* @param aListener A message listener
*/
void unregister(Object aListener);
}

/**
* Convert a plain text connection into an SSL/TLS secured connection.
*
* @param aContext An SSL context, optional, if null will fall back to
* default SSLContext of IRCApi.
* @param aHostname The IRC server host name.
* @param aPort The IRC server port.
* @throws SSLException Throws SSLException in case the SSL/TLS
* initiation fails, specifically the beginning of the handshake.
*/
void secureConnection(SSLContext aContext, String aHostname, int aPort) throws SSLException;
}
24 changes: 23 additions & 1 deletion src/main/java/com/ircclouds/irc/api/interfaces/IIRCSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import net.engio.mbassy.bus.MBassador;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import java.io.IOException;

public interface IIRCSession
Expand All @@ -22,4 +24,24 @@ public interface IIRCSession
void close() throws IOException;

void dispatchClientError(Exception e);
}

/**
* Secure current IRC server connection.
*
* In case the 'tls' capability extension is supported, it is possible
* to convert a plain text connection into a TLS connection. This can
* only be done if the IRC server expects the TLS handshake. When the
* IRC server expects the TLS handshake to occur is specified in the
* capability. At the point when the TLS handshake is expected, this
* method can be called to convert the connection into an SSL
* connection.
*
* @param aContext An SSL context. An optional parameter, we fall back
* to a default SSLContext if none is provided.
* @param aHostname IRC server host name.
* @param aPort IRC server port number.
* @throws SSLException SSLException thrown in case the TLS handshake
* initiation fails.
*/
void secureConnection(SSLContext aContext, String aHostname, int aPort) throws SSLException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
* connection is not fully established yet. This relay instance is a stripped
* interface that allows sending of messages.
*
* TODO Define exception for hard aborting connection to IRC server. There are a
* number of use cases, so far: 1. SaslCapability where authn is required. 2.
* TlsCapability where STARTTLS failed. (more details in spec
* http://ircv3.net/specs/extensions/tls-3.1.html)
*
* @author Danny van Heumen
*/
public interface Relay
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.ircclouds.irc.api.negotiators.capabilities;

import com.ircclouds.irc.api.IRCApi;
import com.ircclouds.irc.api.domain.IRCNumerics;
import com.ircclouds.irc.api.domain.IRCServer;
import com.ircclouds.irc.api.domain.messages.AbstractMessage;
import com.ircclouds.irc.api.domain.messages.ServerNumeric;
import com.ircclouds.irc.api.negotiators.CompositeNegotiator.Capability;
import com.ircclouds.irc.api.negotiators.api.Relay;
import com.ircclouds.irc.api.om.ServerMessageBuilder;

import javax.net.ssl.SSLException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* TLS capability.
*
* @author Danny van Heumen
*/
public class TlsCapability implements Capability {
/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(TlsCapability.class);

// FIXME probably not the prettiest solution for our needs
private final IRCApi irc;

public TlsCapability(final IRCApi aIrc) {
if (aIrc == null) {
throw new NullPointerException("IRC api instance must be provided.");
}
this.irc = aIrc;
}

@Override
public String getId() {
return "tls";
}

@Override
public boolean enable() {
return true;
}

@Override
public boolean converse(Relay relay, AbstractMessage msg) {
if (msg == null) {
relay.send("STARTTLS");
// FIXME also ensure that original message readers stops reading after 670 response
return true;
}
if (!(msg instanceof ServerNumeric)) {
LOG.error("Unexpected message encountered. Aborting securing connection with TLS.");
return false;
}
ServerNumeric numMsg = (ServerNumeric) msg;
switch (numMsg.getNumericCode()) {
case IRCNumerics.RPL_STARTTLS:
// FIXME call back to user code for starting secure connection
try {
this.irc.secureConnection(null, ((IRCServer) numMsg.getSource()).getHostname(), ((IRCServer) numMsg.getSource()).getPort());
} catch (SSLException e) {
LOG.error("error starting handshake", e);
}
return false;
case IRCNumerics.ERR_STARTTLS:
// FIXME abort connection with exception
return false;
default:
LOG.error("Unsupported numeric message from server: {}", numMsg.asRaw());
return false;
}
}
}

0 comments on commit b22b495

Please sign in to comment.