SaslInitiationHandler.java
/*
* SPDX-FileCopyrightText: 2023-2025 Lucimber UG
* SPDX-License-Identifier: Apache-2.0
*/
package com.lucimber.dbus.netty.sasl;
import com.lucimber.dbus.netty.DBusChannelEvent;
import com.lucimber.dbus.netty.WriteOperationListener;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A Netty channel handler that initiates the DBus SASL authentication process by sending a NUL byte
* when the channel becomes active.
*
* <p>After successfully sending the NUL byte, this handler fires a {@link
* DBusChannelEvent#SASL_NUL_BYTE_SENT} user event and then removes itself from the pipeline.
*/
public class SaslInitiationHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(SaslInitiationHandler.class);
private static final byte[] NUL_BYTE_ARRAY = new byte[] {0};
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
// Handle reconnection events
if (evt == DBusChannelEvent.RECONNECTION_STARTING) {
reset();
}
// Always propagate events
super.userEventTriggered(ctx, evt);
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
LOGGER.debug("Channel active. Sending SASL NUL byte to {}.", ctx.channel().remoteAddress());
// Send the NUL byte
ctx.writeAndFlush(Unpooled.wrappedBuffer(NUL_BYTE_ARRAY))
.addListener(
new WriteOperationListener<>(
LOGGER,
future -> {
if (future.isSuccess()) {
LOGGER.debug("SASL NUL byte sent successfully.");
// Fire event to signal the next stage of SASL can begin
ctx.fireUserEventTriggered(
DBusChannelEvent.SASL_NUL_BYTE_SENT);
// Remove this handler from the pipeline as its job is done
ctx.pipeline().remove(SaslInitiationHandler.this);
LOGGER.debug(
"SaslInitiationHandler removed from pipeline as NUL byte sending is complete.");
} else {
LOGGER.error(
"Failed to send SASL NUL byte. Closing channel.",
future.cause());
ctx.close(); // Close channel on failure to send
// critical initial byte
}
}));
}
/**
* Resets the SASL initiation handler to its initial state for reconnection. This method is
* called when the connection needs to be re-established.
*/
public void reset() {
LOGGER.debug("Resetting SASL initiation handler for reconnection");
// This handler has no state to reset
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
LOGGER.error("Exception in SaslInitiationHandler. Closing channel.", cause);
ctx.close();
}
}