001package org.apache.activemq.transport.auto.nio; 002 003import java.io.IOException; 004import java.net.Socket; 005import java.net.URI; 006import java.net.URISyntaxException; 007import java.nio.ByteBuffer; 008import java.util.HashMap; 009import java.util.Set; 010import java.util.concurrent.Future; 011 012import javax.net.ServerSocketFactory; 013import javax.net.ssl.SSLContext; 014import javax.net.ssl.SSLEngine; 015 016import org.apache.activemq.broker.BrokerService; 017import org.apache.activemq.broker.BrokerServiceAware; 018import org.apache.activemq.broker.TransportConnector; 019import org.apache.activemq.transport.Transport; 020import org.apache.activemq.transport.auto.AutoTcpTransportServer; 021import org.apache.activemq.transport.nio.AutoInitNioSSLTransport; 022import org.apache.activemq.transport.nio.NIOSSLTransport; 023import org.apache.activemq.transport.tcp.TcpTransport; 024import org.apache.activemq.transport.tcp.TcpTransport.InitBuffer; 025import org.apache.activemq.transport.tcp.TcpTransportFactory; 026import org.apache.activemq.transport.tcp.TcpTransportServer; 027import org.apache.activemq.util.IntrospectionSupport; 028import org.apache.activemq.wireformat.WireFormat; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032/** 033 * Licensed to the Apache Software Foundation (ASF) under one or more 034 * contributor license agreements. See the NOTICE file distributed with 035 * this work for additional information regarding copyright ownership. 036 * The ASF licenses this file to You under the Apache License, Version 2.0 037 * (the "License"); you may not use this file except in compliance with 038 * the License. You may obtain a copy of the License at 039 * 040 * http://www.apache.org/licenses/LICENSE-2.0 041 * 042 * Unless required by applicable law or agreed to in writing, software 043 * distributed under the License is distributed on an "AS IS" BASIS, 044 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 045 * See the License for the specific language governing permissions and 046 * limitations under the License. 047 */ 048public class AutoNIOSSLTransportServer extends AutoTcpTransportServer { 049 050 private static final Logger LOG = LoggerFactory.getLogger(AutoNIOSSLTransportServer.class); 051 private SSLContext context; 052 053 public AutoNIOSSLTransportServer(SSLContext context, TcpTransportFactory transportFactory, URI location, ServerSocketFactory serverSocketFactory, 054 BrokerService brokerService, Set<String> enabledProtocols) throws IOException, URISyntaxException { 055 super(transportFactory, location, serverSocketFactory, brokerService, enabledProtocols); 056 057 this.context = context; 058 } 059 060 private boolean needClientAuth; 061 private boolean wantClientAuth; 062 063 protected Transport createTransport(Socket socket, WireFormat format, SSLEngine engine, 064 InitBuffer initBuffer, ByteBuffer inputBuffer, TcpTransportFactory detectedFactory) throws IOException { 065 NIOSSLTransport transport = new NIOSSLTransport(format, socket, engine, initBuffer, inputBuffer); 066 if (context != null) { 067 transport.setSslContext(context); 068 } 069 070 transport.setNeedClientAuth(needClientAuth); 071 transport.setWantClientAuth(wantClientAuth); 072 073 074 return transport; 075 } 076 077 @Override 078 protected TcpTransport createTransport(Socket socket, WireFormat format) throws IOException { 079 throw new UnsupportedOperationException("method not supported"); 080 } 081 082 @Override 083 public boolean isSslServer() { 084 return true; 085 } 086 087 public boolean isNeedClientAuth() { 088 return this.needClientAuth; 089 } 090 091 public void setNeedClientAuth(boolean value) { 092 this.needClientAuth = value; 093 } 094 095 public boolean isWantClientAuth() { 096 return this.wantClientAuth; 097 } 098 099 public void setWantClientAuth(boolean value) { 100 this.wantClientAuth = value; 101 } 102 103 104 @Override 105 protected TransportInfo configureTransport(final TcpTransportServer server, final Socket socket) throws Exception { 106 //The SSLEngine needs to be initialized and handshake done to get the first command and detect the format 107 //The wireformat doesn't need properties set here because we aren't using this format during the SSL handshake 108 final AutoInitNioSSLTransport in = new AutoInitNioSSLTransport(wireFormatFactory.createWireFormat(), socket); 109 if (context != null) { 110 in.setSslContext(context); 111 } 112 //We need to set the transport options on the init transport so that the SSL options are set 113 if (transportOptions != null) { 114 //Clone the map because we will need to set the options later on the actual transport 115 IntrospectionSupport.setProperties(in, new HashMap<>(transportOptions)); 116 } 117 118 //Attempt to read enough bytes to detect the protocol until the timeout period 119 //is reached 120 Future<?> future = protocolDetectionExecutor.submit(new Runnable() { 121 @Override 122 public void run() { 123 try { 124 in.start(); 125 } catch (Exception error) { 126 LOG.warn("Could not accept connection {}: {} ({})", 127 (in.getRemoteAddress() == null ? "" : "from " + in.getRemoteAddress()), error.getMessage(), 128 TransportConnector.getRootCause(error).getMessage()); 129 throw new IllegalStateException("Could not complete Transport start", error); 130 } 131 132 int attempts = 0; 133 do { 134 if(attempts > 0) { 135 try { 136 //increase sleep period each attempt to prevent high cpu usage 137 //if the client is hung and not sending bytes 138 int sleep = attempts >= 1024 ? 1024 : 4 * attempts; 139 Thread.sleep(sleep); 140 } catch (InterruptedException e) { 141 break; 142 } 143 } 144 //In the future it might be better to register a nonblocking selector 145 //to be told when bytes are ready 146 in.serviceRead(); 147 attempts++; 148 } while(in.getReadSize().get() < 8 && !Thread.interrupted()); 149 } 150 }); 151 152 try { 153 //If this fails and throws an exception and the socket will be closed 154 waitForProtocolDetectionFinish(future, in.getReadSize()); 155 } finally { 156 //call cancel in case task didn't complete which will interrupt the task 157 future.cancel(true); 158 } 159 in.stop(); 160 161 InitBuffer initBuffer = new InitBuffer(in.getReadSize().get(), ByteBuffer.allocate(in.getReadData().length)); 162 initBuffer.buffer.put(in.getReadData()); 163 164 ProtocolInfo protocolInfo = detectProtocol(in.getReadData()); 165 166 if (protocolInfo.detectedTransportFactory instanceof BrokerServiceAware) { 167 ((BrokerServiceAware) protocolInfo.detectedTransportFactory).setBrokerService(brokerService); 168 } 169 170 WireFormat format = protocolInfo.detectedWireFormatFactory.createWireFormat(); 171 Transport transport = createTransport(socket, format, in.getSslSession(), initBuffer, in.getInputBuffer(), protocolInfo.detectedTransportFactory); 172 173 return new TransportInfo(format, transport, protocolInfo.detectedTransportFactory); 174 } 175 176} 177 178