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.ExecutorService;
011import java.util.concurrent.Executors;
012import java.util.concurrent.Future;
013
014import javax.net.ServerSocketFactory;
015import javax.net.ssl.SSLContext;
016import javax.net.ssl.SSLEngine;
017
018import org.apache.activemq.broker.BrokerService;
019import org.apache.activemq.broker.BrokerServiceAware;
020import org.apache.activemq.transport.Transport;
021import org.apache.activemq.transport.auto.AutoTcpTransportServer;
022import org.apache.activemq.transport.nio.AutoInitNioSSLTransport;
023import org.apache.activemq.transport.nio.NIOSSLTransport;
024import org.apache.activemq.transport.tcp.TcpTransport;
025import org.apache.activemq.transport.tcp.TcpTransport.InitBuffer;
026import org.apache.activemq.transport.tcp.TcpTransportFactory;
027import org.apache.activemq.transport.tcp.TcpTransportServer;
028import org.apache.activemq.util.IntrospectionSupport;
029import org.apache.activemq.wireformat.WireFormat;
030
031/**
032 * Licensed to the Apache Software Foundation (ASF) under one or more
033 * contributor license agreements.  See the NOTICE file distributed with
034 * this work for additional information regarding copyright ownership.
035 * The ASF licenses this file to You under the Apache License, Version 2.0
036 * (the "License"); you may not use this file except in compliance with
037 * the License.  You may obtain a copy of the License at
038 *
039 *      http://www.apache.org/licenses/LICENSE-2.0
040 *
041 * Unless required by applicable law or agreed to in writing, software
042 * distributed under the License is distributed on an "AS IS" BASIS,
043 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
044 * See the License for the specific language governing permissions and
045 * limitations under the License.
046 */
047public class AutoNIOSSLTransportServer extends AutoTcpTransportServer {
048
049    private SSLContext context;
050
051    public AutoNIOSSLTransportServer(SSLContext context, TcpTransportFactory transportFactory, URI location, ServerSocketFactory serverSocketFactory,
052            BrokerService brokerService, Set<String> enabledProtocols) throws IOException, URISyntaxException {
053        super(transportFactory, location, serverSocketFactory, brokerService, enabledProtocols);
054
055        this.context = context;
056    }
057
058    private boolean needClientAuth;
059    private boolean wantClientAuth;
060
061    protected Transport createTransport(Socket socket, WireFormat format, SSLEngine engine,
062            InitBuffer initBuffer, ByteBuffer inputBuffer, TcpTransportFactory detectedFactory) throws IOException {
063        NIOSSLTransport transport = new NIOSSLTransport(format, socket, engine, initBuffer, inputBuffer);
064        if (context != null) {
065            transport.setSslContext(context);
066        }
067
068        transport.setNeedClientAuth(needClientAuth);
069        transport.setWantClientAuth(wantClientAuth);
070
071
072        return transport;
073    }
074
075    @Override
076    protected TcpTransport createTransport(Socket socket, WireFormat format) throws IOException {
077        throw new UnsupportedOperationException("method not supported");
078    }
079
080    @Override
081    public boolean isSslServer() {
082        return true;
083    }
084
085    public boolean isNeedClientAuth() {
086        return this.needClientAuth;
087    }
088
089    public void setNeedClientAuth(boolean value) {
090        this.needClientAuth = value;
091    }
092
093    public boolean isWantClientAuth() {
094        return this.wantClientAuth;
095    }
096
097    public void setWantClientAuth(boolean value) {
098        this.wantClientAuth = value;
099    }
100
101
102    @Override
103    protected TransportInfo configureTransport(final TcpTransportServer server, final Socket socket) throws Exception {
104        ExecutorService executor = Executors.newSingleThreadExecutor();
105
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        in.start();
118        SSLEngine engine = in.getSslSession();
119
120        Future<?> future = executor.submit(new Runnable() {
121            @Override
122            public void run() {
123                //Wait for handshake to finish initializing
124                do {
125                    in.serviceRead();
126                } while(in.getReadSize().get() < 8);
127            }
128        });
129
130        waitForProtocolDetectionFinish(future, in.getReadSize());
131        in.stop();
132
133        initBuffer = new InitBuffer(in.getReadSize().get(), ByteBuffer.allocate(in.getReadData().length));
134        initBuffer.buffer.put(in.getReadData());
135
136        ProtocolInfo protocolInfo = detectProtocol(in.getReadData());
137
138        if (protocolInfo.detectedTransportFactory instanceof BrokerServiceAware) {
139            ((BrokerServiceAware) protocolInfo.detectedTransportFactory).setBrokerService(brokerService);
140        }
141
142        WireFormat format = protocolInfo.detectedWireFormatFactory.createWireFormat();
143        Transport transport = createTransport(socket, format, engine, initBuffer, in.getInputBuffer(), protocolInfo.detectedTransportFactory);
144
145        return new TransportInfo(format, transport, protocolInfo.detectedTransportFactory);
146    }
147
148}
149
150