Author: rgodfrey
Date: Thu Apr 13 09:22:55 2017
New Revision: 1791223

URL: http://svn.apache.org/viewvc?rev=1791223&view=rev
Log:
QPID-7742 : Take hostname from SNI and SASL when available

Modified:
    
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnection.java
    
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java
    
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/ServerNetworkConnection.java
    
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java
    
qpid/java/trunk/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java

Modified: 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnection.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnection.java?rev=1791223&r1=1791222&r2=1791223&view=diff
==============================================================================
--- 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnection.java
 (original)
+++ 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnection.java
 Thu Apr 13 09:22:55 2017
@@ -76,6 +76,7 @@ public class NonBlockingConnection imple
     private final List<SchedulingDelayNotificationListener> 
_schedulingDelayNotificationListeners = new CopyOnWriteArrayList<>();
     private final AtomicBoolean _hasShutdown = new AtomicBoolean();
     private volatile long _bufferedSize;
+    private String _selectedHost;
 
     public NonBlockingConnection(SocketChannel socketChannel,
                                  ProtocolEngine protocolEngine,
@@ -658,4 +659,15 @@ public class NonBlockingConnection imple
     {
         _selectionTask = selectionTask;
     }
+
+    public void setSelectedHost(final String selectedHost)
+    {
+        _selectedHost = selectedHost;
+    }
+
+    @Override
+    public String getSelectedHost()
+    {
+        return _selectedHost;
+    }
 }

Modified: 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java?rev=1791223&r1=1791222&r2=1791223&view=diff
==============================================================================
--- 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java
 (original)
+++ 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java
 Thu Apr 13 09:22:55 2017
@@ -55,6 +55,7 @@ public class NonBlockingConnectionTLSDel
     private Principal _principal;
     private Certificate _peerCertificate;
     private boolean _principalChecked;
+    private volatile boolean _hostChecked;
     private QpidByteBuffer _netInputBuffer;
     private QpidByteBuffer _netOutputBuffer;
     private QpidByteBuffer _applicationBuffer;
@@ -87,6 +88,31 @@ public class NonBlockingConnectionTLSDel
     @Override
     public boolean processData() throws IOException
     {
+        if(!_hostChecked)
+        {
+            QpidByteBuffer buffer = _netInputBuffer.duplicate();
+            try
+            {
+                buffer.flip();
+                if (SSLUtil.isSufficientToDetermineClientSNIHost(buffer))
+                {
+                    String hostName = 
SSLUtil.getServerNameFromTLSClientHello(buffer);
+                    if (hostName != null)
+                    {
+                        _parent.setSelectedHost(hostName);
+                    }
+                    _hostChecked = true;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+            finally
+            {
+                buffer.dispose();;
+            }
+        }
         _netInputBuffer.flip();
         boolean readData = false;
         boolean tasksRun;

Modified: 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/ServerNetworkConnection.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/ServerNetworkConnection.java?rev=1791223&r1=1791222&r2=1791223&view=diff
==============================================================================
--- 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/ServerNetworkConnection.java
 (original)
+++ 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/ServerNetworkConnection.java
 Thu Apr 13 09:22:55 2017
@@ -30,4 +30,6 @@ public interface ServerNetworkConnection
     void 
addSchedulingDelayNotificationListeners(SchedulingDelayNotificationListener 
listener);
 
     void 
removeSchedulingDelayNotificationListeners(SchedulingDelayNotificationListener 
listener);
+
+    String getSelectedHost();
 }

Modified: 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java?rev=1791223&r1=1791222&r2=1791223&view=diff
==============================================================================
--- 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java
 (original)
+++ 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java
 Thu Apr 13 09:22:55 2017
@@ -56,15 +56,17 @@ import java.util.TreeSet;
 import javax.naming.InvalidNameException;
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
+import javax.net.ssl.SNIHostName;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSocket;
+import javax.net.ssl.StandardConstants;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.qpid.server.bytebuffer.QpidByteBuffer;
 import org.apache.qpid.server.transport.TransportException;
 import org.apache.qpid.server.util.Strings;
 
@@ -612,4 +614,142 @@ public class SSLUtil
         throw new NoSuchAlgorithmException(String.format("Could not create 
SSLContext with one of the requested protocols: %s",
                                                          
Arrays.toString(protocols)));
     }
+
+    public static boolean isSufficientToDetermineClientSNIHost(QpidByteBuffer 
buffer)
+    {
+        if(buffer.remaining() < 6)
+        {
+            return false;
+        }
+        else if(looksLikeSSLv3ClientHello(buffer))
+        {
+            final int position = buffer.position();
+            final int recordSize = 5 + (((buffer.get(position + 3) & 0xFF) << 
8) | (buffer.get(position + 4) & 0xFF));
+            return buffer.remaining() >= recordSize;
+        }
+        else
+        {
+            return true;
+        }
+    }
+
+    private static boolean looksLikeSSLv3ClientHello(QpidByteBuffer buffer)
+    {
+        int first = buffer.get(buffer.position()+0);
+        int second = buffer.get(buffer.position()+1);
+        int third = buffer.get(buffer.position()+2);
+        int sixth = buffer.get(buffer.position()+5);
+
+        return first == 22 && // SSL Handshake
+                   (second == 3 && // SSL 3.0 / TLS 1.x
+                    (third == 0 || // SSL 3.0
+                     third == 1 || // TLS 1.0
+                     third == 2 || // TLS 1.1
+                     third == 3)) && // TLS1.2
+                   (sixth == 1); // client_hello
+    }
+
+    public final static String getServerNameFromTLSClientHello(QpidByteBuffer 
source)
+    {
+
+        QpidByteBuffer input = source.duplicate();
+        try
+        {
+
+            // Do we have a complete header?
+            if (input.remaining() < 5)
+            {
+                return null;
+            }
+
+            byte first = input.get();
+            byte second = input.get();
+            byte third = input.get();
+            if (first == 22 && third != 0x01)
+            {
+
+                int recordLength = input.getUnsignedShort();
+                if (recordLength > input.remaining())
+                {
+                    return null;
+                }
+
+                if (input.get() == 0x01)
+                {
+                    // 24-bit length field
+                    int length = ((input.get() & 0xFF) << 16) | ((input.get() 
& 0xFF) << 8) | (input.get() & 0xFF);
+
+                    input.limit(length + input.position());
+
+                    input.position(input.position() + 34);  // hello 
minor/major version + random
+                    int skip = (int) input.get(); // session-id
+                    input.position(input.position() + skip);
+                    skip = input.getUnsignedShort(); // cipher suites
+                    input.position(input.position() + skip);
+                    skip = (int) input.get(); // compression methods
+                    input.position(input.position() + skip);
+
+                    if (input.hasRemaining())
+                    {
+
+                        int remaining = input.getUnsignedShort();
+                        input.limit(input.position()+remaining);
+                        while (input.hasRemaining())
+                        {
+                            int extensionType = input.getUnsignedShort();
+
+                            int extensionLength = input.getUnsignedShort();
+
+                            if (extensionType == 0x00)
+                            {
+
+                                int extensionDataRemaining = extensionLength;
+                                if (extensionDataRemaining >= 2)
+                                {
+                                    int listLength = input.getUnsignedShort(); 
    // length of server_name_list
+                                    if (listLength + 2 != 
extensionDataRemaining)
+                                    {
+                                        // invalid format
+                                        return null;
+                                    }
+
+                                    extensionDataRemaining -= 2;
+                                    while (extensionDataRemaining > 0)
+                                    {
+                                        int code = input.get();
+                                        int serverNameLength = 
input.getUnsignedShort();
+                                        if (serverNameLength > 
extensionDataRemaining)
+                                        {
+                                            // invalid format;
+                                            return null;
+                                        }
+                                        byte[] encoded = new 
byte[serverNameLength];
+                                        input.get(encoded);
+
+                                        if (code == 
StandardConstants.SNI_HOST_NAME)
+                                        {
+
+                                            return new 
SNIHostName(encoded).getAsciiName();
+                                        }
+                                        extensionDataRemaining -= 
serverNameLength + 3;
+                                    }
+                                }
+                                return null;
+                            }
+                            else
+                            {
+                                input.position(input.position() + 
extensionLength);
+                            }
+                        }
+                    }
+                }
+            }
+            return null;
+
+        }
+        finally
+        {
+            input.dispose();
+        }
+    }
 }

Modified: 
qpid/java/trunk/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java?rev=1791223&r1=1791222&r2=1791223&view=diff
==============================================================================
--- 
qpid/java/trunk/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java
 (original)
+++ 
qpid/java/trunk/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java
 Thu Apr 13 09:22:55 2017
@@ -732,7 +732,16 @@ public class AMQPConnection_1_0Impl exte
                 ? getBroker().getNetworkBufferSize()
                 : Math.min(open.getMaxFrameSize().intValue(), 
getBroker().getNetworkBufferSize());
         _remoteContainerId = open.getContainerId();
-        _localHostname = open.getHostname();
+
+        if(open.getHostname() != null && !"".equals(open.getHostname().trim()))
+        {
+            _localHostname = open.getHostname();
+        }
+
+        if(_localHostname == null || "".equals(_localHostname.trim()) && 
getNetwork().getSelectedHost() != null)
+        {
+            _localHostname = getNetwork().getSelectedHost();
+        }
         if (open.getIdleTimeOut() != null)
         {
             _idleTimeout = open.getIdleTimeOut().longValue();
@@ -932,6 +941,14 @@ public class AMQPConnection_1_0Impl exte
     public void receiveSaslInit(final SaslInit saslInit)
     {
         assertState(FrameReceivingState.SASL_INIT_ONLY);
+        if(saslInit.getHostname() != null && 
!"".equals(saslInit.getHostname().trim()))
+        {
+            _localHostname = saslInit.getHostname();
+        }
+        else if(getNetwork().getSelectedHost() != null)
+        {
+            _localHostname = getNetwork().getSelectedHost();
+        }
         String mechanism = saslInit.getMechanism() == null ? null : 
saslInit.getMechanism().toString();
         final Binary initialResponse = saslInit.getInitialResponse();
         byte[] response = initialResponse == null ? new byte[0] : 
initialResponse.getArray();
@@ -940,6 +957,12 @@ public class AMQPConnection_1_0Impl exte
         processSaslResponse(response);
     }
 
+    @Override
+    public String getLocalFQDN()
+    {
+        return _localHostname != null ? _localHostname : super.getLocalFQDN();
+    }
+
     private void processSaslResponse(final byte[] response)
     {
         byte[] challenge = null;



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org
For additional commands, e-mail: commits-h...@qpid.apache.org

Reply via email to