org.apache.cassandra.net.InboundConnectionInitiator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cassandra-all Show documentation
Show all versions of cassandra-all Show documentation
The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.net;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.security.cert.Certificate;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import org.apache.cassandra.auth.IInternodeAuthenticator;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.EncryptionOptions;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.net.OutboundConnectionSettings.Framing;
import org.apache.cassandra.security.ISslContextFactory;
import org.apache.cassandra.security.SSLFactory;
import org.apache.cassandra.streaming.StreamDeserializingTask;
import org.apache.cassandra.streaming.StreamingChannel;
import org.apache.cassandra.streaming.async.NettyStreamingChannel;
import org.apache.cassandra.utils.memory.BufferPools;
import static java.lang.Math.*;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.apache.cassandra.auth.IInternodeAuthenticator.InternodeConnectionDirection.INBOUND;
import static org.apache.cassandra.concurrent.ExecutorFactory.Global.executorFactory;
import static org.apache.cassandra.net.InternodeConnectionUtils.DISCARD_HANDLER_NAME;
import static org.apache.cassandra.net.InternodeConnectionUtils.SSL_HANDLER_NAME;
import static org.apache.cassandra.net.InternodeConnectionUtils.certificates;
import static org.apache.cassandra.net.MessagingService.*;
import static org.apache.cassandra.net.SocketFactory.WIRETRACE;
import static org.apache.cassandra.net.SocketFactory.newSslHandler;
public class InboundConnectionInitiator
{
private static final Logger logger = LoggerFactory.getLogger(InboundConnectionInitiator.class);
private static class Initializer extends ChannelInitializer
{
private static final String PIPELINE_INTERNODE_ERROR_EXCLUSIONS = "Internode Error Exclusions";
private final InboundConnectionSettings settings;
private final ChannelGroup channelGroup;
private final Consumer pipelineInjector;
Initializer(InboundConnectionSettings settings, ChannelGroup channelGroup,
Consumer pipelineInjector)
{
this.settings = settings;
this.channelGroup = channelGroup;
this.pipelineInjector = pipelineInjector;
}
@Override
public void initChannel(SocketChannel channel) throws Exception
{
// if any of the handlers added fail they will send the error to the "head", so this needs to be first
channel.pipeline().addFirst(PIPELINE_INTERNODE_ERROR_EXCLUSIONS, new InternodeErrorExclusionsHandler());
channelGroup.add(channel);
channel.config().setOption(ChannelOption.ALLOCATOR, GlobalBufferPoolAllocator.instance);
channel.config().setOption(ChannelOption.SO_KEEPALIVE, true);
channel.config().setOption(ChannelOption.SO_REUSEADDR, true);
channel.config().setOption(ChannelOption.TCP_NODELAY, true); // we only send handshake messages; no point ever delaying
ChannelPipeline pipeline = channel.pipeline();
pipelineInjector.accept(pipeline);
// order of handlers: ssl -> client-authentication -> logger -> handshakeHandler
// For either unencrypted or transitional modes, allow Ssl optionally.
switch(settings.encryption.tlsEncryptionPolicy())
{
case UNENCRYPTED:
// Handler checks for SSL connection attempts and cleanly rejects them if encryption is disabled
pipeline.addAfter(PIPELINE_INTERNODE_ERROR_EXCLUSIONS, "rejectssl", new RejectSslHandler());
break;
case OPTIONAL:
pipeline.addAfter(PIPELINE_INTERNODE_ERROR_EXCLUSIONS, SSL_HANDLER_NAME, new OptionalSslHandler(settings.encryption));
break;
case ENCRYPTED:
SslHandler sslHandler = getSslHandler("creating", channel, settings.encryption);
pipeline.addAfter(PIPELINE_INTERNODE_ERROR_EXCLUSIONS, SSL_HANDLER_NAME, sslHandler);
break;
}
// Pipeline for performing client authentication
pipeline.addLast("client-authentication", new ClientAuthenticationHandler(settings.authenticator));
if (WIRETRACE)
pipeline.addLast("logger", new LoggingHandler(LogLevel.INFO));
channel.pipeline().addLast("handshake", new Handler(settings));
}
}
private static class InternodeErrorExclusionsHandler extends ChannelInboundHandlerAdapter
{
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
if (DatabaseDescriptor.getInternodeErrorReportingExclusions().contains(ctx.channel().remoteAddress()))
{
logger.debug("Excluding internode exception for {}; address contained in internode_error_reporting_exclusions", ctx.channel().remoteAddress(), cause);
return;
}
super.exceptionCaught(ctx, cause);
}
}
/**
* Create a {@link Channel} that listens on the {@code localAddr}. This method will block while trying to bind to the address,
* but it does not make a remote call.
*/
private static ChannelFuture bind(Initializer initializer) throws ConfigurationException
{
logger.info("Listening on {}", initializer.settings);
ServerBootstrap bootstrap = initializer.settings.socketFactory
.newServerBootstrap()
.option(ChannelOption.SO_BACKLOG, 1 << 9)
.option(ChannelOption.ALLOCATOR, GlobalBufferPoolAllocator.instance)
.option(ChannelOption.SO_REUSEADDR, true)
.childHandler(initializer);
int socketReceiveBufferSizeInBytes = initializer.settings.socketReceiveBufferSizeInBytes;
if (socketReceiveBufferSizeInBytes > 0)
bootstrap.childOption(ChannelOption.SO_RCVBUF, socketReceiveBufferSizeInBytes);
InetAddressAndPort bind = initializer.settings.bindAddress;
ChannelFuture channelFuture = bootstrap.bind(new InetSocketAddress(bind.getAddress(), bind.getPort()));
if (!channelFuture.awaitUninterruptibly().isSuccess())
{
if (channelFuture.channel().isOpen())
channelFuture.channel().close();
Throwable failedChannelCause = channelFuture.cause();
String causeString = "";
if (failedChannelCause != null && failedChannelCause.getMessage() != null)
causeString = failedChannelCause.getMessage();
if (causeString.contains("in use"))
{
throw new ConfigurationException(bind + " is in use by another process. Change listen_address:storage_port " +
"in cassandra.yaml to values that do not conflict with other services");
}
else if (causeString.contains("cannot assign requested address"))
{
throw new ConfigurationException("Unable to bind to address " + bind
+ ". Set listen_address in cassandra.yaml to an interface you can bind to, e.g., your private IP address on EC2");
}
else
{
throw new ConfigurationException("failed to bind to: " + bind, failedChannelCause);
}
}
return channelFuture;
}
public static ChannelFuture bind(InboundConnectionSettings settings, ChannelGroup channelGroup,
Consumer pipelineInjector)
{
return bind(new Initializer(settings, channelGroup, pipelineInjector));
}
/**
* Handler to perform authentication for internode inbound connections.
* This handler is called even before messaging handshake starts.
*/
private static class ClientAuthenticationHandler extends ByteToMessageDecoder
{
private final IInternodeAuthenticator authenticator;
public ClientAuthenticationHandler(IInternodeAuthenticator authenticator)
{
this.authenticator = authenticator;
}
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy