/** * This file is part of Waarp Project. * * Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the * COPYRIGHT.txt in the distribution for a full listing of individual contributors. * * All Waarp Project is free software: you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with Waarp . If not, see * <http://www.gnu.org/licenses/>. */ package org.waarp.openr66.protocol.networkhandler; import java.net.BindException; import java.net.SocketAddress; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.timeout.IdleStateEvent; import io.netty.handler.timeout.ReadTimeoutException; import org.waarp.common.crypto.ssl.WaarpSslUtility; import org.waarp.common.database.DbSession; import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; import org.waarp.openr66.database.DbConstant; import org.waarp.openr66.protocol.configuration.Configuration; import org.waarp.openr66.protocol.exception.OpenR66Exception; import org.waarp.openr66.protocol.exception.OpenR66ExceptionTrappedFactory; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessNoWriteBackException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolNoConnectionException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolRemoteShutdownException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException; import org.waarp.openr66.protocol.localhandler.LocalChannelReference; import org.waarp.openr66.protocol.localhandler.packet.AbstractLocalPacket; import org.waarp.openr66.protocol.localhandler.packet.ConnectionErrorPacket; import org.waarp.openr66.protocol.localhandler.packet.KeepAlivePacket; import org.waarp.openr66.protocol.localhandler.packet.LocalPacketCodec; import org.waarp.openr66.protocol.localhandler.packet.LocalPacketFactory; import org.waarp.openr66.protocol.networkhandler.packet.NetworkPacket; import org.waarp.openr66.protocol.utils.ChannelCloseTimer; import org.waarp.openr66.protocol.utils.ChannelUtils; import org.waarp.openr66.protocol.utils.R66ShutdownHook; /** * Network Server Handler (Requester side) * * @author frederic bregier */ public class NetworkServerHandler extends SimpleChannelInboundHandler<NetworkPacket> { // extends SimpleChannelHandler { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(NetworkServerHandler.class); /** * The associated Remote Address */ private SocketAddress remoteAddress; /** * The associated NetworkChannelReference */ private NetworkChannelReference networkChannelReference; /** * The Database connection attached to this NetworkChannelReference shared among all associated * LocalChannels */ private DbSession dbSession; /** * Does this Handler is for SSL */ protected boolean isSSL = false; /** * Is this Handler a server side */ protected boolean isServer = false; /** * To handle the keep alive */ private volatile int keepAlivedSent = 0; /** * Is this network connection being refused (black listed) */ protected volatile boolean isBlackListed = false; /** * * @param isServer */ public NetworkServerHandler(boolean isServer) { this.isServer = isServer; } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { if (networkChannelReference != null) { if (networkChannelReference.nbLocalChannels() > 0) { logger.info("Network Channel Closed: {} LocalChannels Left: {}", ctx.channel().id(), networkChannelReference.nbLocalChannels()); // Give an extra time if necessary to let the local channel being closed try { Thread.sleep(Configuration.RETRYINMS * 2); } catch (InterruptedException e1) { } } NetworkTransaction.closedNetworkChannel(networkChannelReference); } else { if (remoteAddress == null) { remoteAddress = ctx.channel().remoteAddress(); } NetworkTransaction.closedNetworkChannel(remoteAddress); } // Now force the close of the database after a wait if (dbSession != null && DbConstant.admin != null && DbConstant.admin.getSession() != null && !dbSession.equals(DbConstant.admin.getSession())) { dbSession.forceDisconnect(); dbSession = null; } } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Channel netChannel = ctx.channel(); this.remoteAddress = netChannel.remoteAddress(); logger.debug("Will the Connection be refused if Partner is BlackListed from " + remoteAddress.toString()); if (NetworkTransaction.isBlacklisted(netChannel)) { logger.warn("Connection refused since Partner is BlackListed from " + remoteAddress.toString()); isBlackListed = true; if (Configuration.configuration.getR66Mib() != null) { Configuration.configuration.getR66Mib().notifyError( "Black Listed connection temptative", "During connection"); } // close immediately the connection WaarpSslUtility.closingSslChannel(netChannel); return; } try { this.networkChannelReference = NetworkTransaction.addNetworkChannel(netChannel); } catch (OpenR66ProtocolRemoteShutdownException e2) { logger.warn("Connection refused since Partner is in Shutdown from " + remoteAddress.toString() + " : {}", e2.getMessage()); isBlackListed = true; // close immediately the connection WaarpSslUtility.closingSslChannel(netChannel); return; } try { if (DbConstant.admin.isActive()) { if (DbConstant.admin.isCompatibleWithThreadSharedConnexion()) { this.dbSession = new DbSession(DbConstant.admin, false); this.dbSession.useConnection(); } else { logger.debug("DbSession will be adjusted on LocalChannelReference"); this.dbSession = DbConstant.admin.getSession(); } } } catch (WaarpDatabaseNoConnectionException e1) { // Cannot connect so use default connection logger.warn("Use default database connection"); this.dbSession = DbConstant.admin.getSession(); } logger.debug("Network Channel Connected: {} ", ctx.channel().id()); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (Configuration.configuration.isShutdown()) return; if (evt instanceof IdleStateEvent) { if (this.networkChannelReference != null && this.networkChannelReference.checkLastTime(Configuration.configuration.getTIMEOUTCON() * 2) <= 0) { keepAlivedSent = 0; return; } if (keepAlivedSent > 0) { if (this.networkChannelReference != null) { if (this.networkChannelReference.nbLocalChannels() > 0 && keepAlivedSent < 5) { // ignore this time keepAlivedSent++; return; } } logger.error("Not getting KAlive: closing channel"); if (Configuration.configuration.getR66Mib() != null) { Configuration.configuration.getR66Mib().notifyWarning( "KeepAlive get no answer", "Closing network connection"); } ChannelCloseTimer.closeFutureChannel(ctx.channel()); } else { keepAlivedSent = 1; KeepAlivePacket keepAlivePacket = new KeepAlivePacket(); NetworkPacket response = new NetworkPacket(ChannelUtils.NOCHANNEL, ChannelUtils.NOCHANNEL, keepAlivePacket, null); logger.info("Write KAlive"); ctx.channel().writeAndFlush(response); } } } public void setKeepAlivedSent() { keepAlivedSent = 0; } @Override public void channelRead0(ChannelHandlerContext ctx, NetworkPacket msg) throws Exception { if (isBlackListed) { // ignore message since close on going return; } final NetworkPacket packet = (NetworkPacket) msg; Channel channel = ctx.channel(); if (packet.getCode() == LocalPacketFactory.NOOPPACKET) { if (networkChannelReference != null) { networkChannelReference.useIfUsed(); } packet.clear(); // Do nothing return; } else if (packet.getCode() == LocalPacketFactory.CONNECTERRORPACKET) { logger.debug("NetworkRecv: {}", packet); // Special code to STOP here if (packet.getLocalId() == ChannelUtils.NOCHANNEL) { int nb = this.networkChannelReference.nbLocalChannels(); if (nb > 0) { logger.warn("Temptative of connection failed but still some connection are there so not closing the server channel immediately: " + nb); NetworkTransaction.shuttingDownNetworkChannel(networkChannelReference); return; } // No way to know what is wrong: close all connections with // remote host logger.error("Will close NETWORK channel, Cannot continue connection with remote Host: " + packet.toString() + " : " + channel.remoteAddress() + " : " + nb); WaarpSslUtility.closingSslChannel(channel); packet.clear(); return; } } else if (packet.getCode() == LocalPacketFactory.KEEPALIVEPACKET) { if (networkChannelReference != null) { networkChannelReference.useIfUsed(); } keepAlivedSent = 0; try { KeepAlivePacket keepAlivePacket = (KeepAlivePacket) LocalPacketCodec.decodeNetworkPacket(packet.getBuffer()); if (keepAlivePacket.isToValidate()) { keepAlivePacket.validate(); NetworkPacket response = new NetworkPacket(ChannelUtils.NOCHANNEL, ChannelUtils.NOCHANNEL, keepAlivePacket, null); logger.info("Answer KAlive"); ctx.channel().writeAndFlush(response); } else { logger.info("Get KAlive"); } } catch (OpenR66ProtocolPacketException e1) { } packet.clear(); return; } logger.debug("GET MSG: " + packet.getCode()); this.networkChannelReference.use(); LocalChannelReference localChannelReference = null; if (packet.getLocalId() == ChannelUtils.NOCHANNEL) { logger.debug("NetworkRecv Create: {} {}", packet, channel.id()); NetworkTransaction.createConnectionFromNetworkChannelStartup( this.networkChannelReference, packet); return; } else { if (packet.getCode() == LocalPacketFactory.ENDREQUESTPACKET) { // Not a local error but a remote one try { localChannelReference = Configuration.configuration .getLocalTransaction().getClient(packet.getRemoteId(), packet.getLocalId()); } catch (OpenR66ProtocolSystemException e1) { // do not send anything since the packet is external try { logger.debug("Cannot get LocalChannel while an end of request comes: {}", LocalPacketCodec.decodeNetworkPacket(packet.getBuffer())); } catch (OpenR66ProtocolPacketException e2) { logger.debug("Cannot get LocalChannel while an end of request comes: {}", packet.toString()); } packet.clear(); return; } // OK continue and send to the local channel } else if (packet.getCode() == LocalPacketFactory.CONNECTERRORPACKET) { // Not a local error but a remote one try { localChannelReference = Configuration.configuration .getLocalTransaction().getClient(packet.getRemoteId(), packet.getLocalId()); } catch (OpenR66ProtocolSystemException e1) { // do not send anything since the packet is external try { logger.debug("Cannot get LocalChannel while an external error comes: {}", LocalPacketCodec.decodeNetworkPacket(packet.getBuffer())); } catch (OpenR66ProtocolPacketException e2) { logger.debug("Cannot get LocalChannel while an external error comes: {}", packet.toString()); } packet.clear(); return; } // OK continue and send to the local channel } else { try { localChannelReference = Configuration.configuration .getLocalTransaction().getClient(packet.getRemoteId(), packet.getLocalId()); } catch (OpenR66ProtocolSystemException e1) { if (remoteAddress == null) { remoteAddress = channel.remoteAddress(); } if (NetworkTransaction.isShuttingdownNetworkChannel(remoteAddress) || R66ShutdownHook.isShutdownStarting()) { // ignore packet.clear(); return; } // try to send later Configuration.configuration.getLocalTransaction() .sendLaterToClient(channel, packet.getRemoteId(), packet.getLocalId(), packet); /*logger.debug("Cannot get LocalChannel: " + packet + " due to " + e1.getMessage()); final ConnectionErrorPacket error = new ConnectionErrorPacket( "Cannot get localChannel since localId is not found anymore", "" + packet.getLocalId()); writeError(channel, packet.getRemoteId(), packet.getLocalId(), error);*/ packet.clear(); return; } } } // check if not already in shutdown or closed if (NetworkTransaction.isShuttingdownNetworkChannel(remoteAddress) || R66ShutdownHook.isShutdownStarting() || ! localChannelReference.getLocalChannel().isActive()) { logger.debug("Cannot use LocalChannel since already in shutdown: " + packet); // ignore packet.clear(); return; } ByteBuf buf = packet.getBuffer(); localChannelReference.getLocalChannel().writeAndFlush(buf); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { Channel channel = ctx.channel(); if (isBlackListed) { logger.info("While partner is blacklisted, Network Channel Exception: {}", channel.id(), cause.getClass() .getName() + " : " + cause); // ignore return; } logger.debug("Network Channel Exception: {}", channel.id(), cause); if (cause instanceof ReadTimeoutException) { ReadTimeoutException exception = (ReadTimeoutException) cause; // No read for too long logger.error("ReadTimeout so Will close NETWORK channel {}", exception.getClass().getName() + " : " + exception.getMessage()); ChannelCloseTimer.closeFutureChannel(channel); return; } if (cause instanceof BindException) { // received when not yet connected logger.debug("BindException"); ChannelCloseTimer.closeFutureChannel(channel); return; } OpenR66Exception exception = OpenR66ExceptionTrappedFactory .getExceptionFromTrappedException(channel, cause); if (exception != null) { if (exception instanceof OpenR66ProtocolBusinessNoWriteBackException) { if (this.networkChannelReference != null && this.networkChannelReference.nbLocalChannels() > 0) { logger.debug( "Network Channel Exception: {} {}", channel.id(), exception.getClass().getName() + " : " + exception.getMessage()); } logger.debug("Will close NETWORK channel"); ChannelCloseTimer.closeFutureChannel(channel); return; } else if (exception instanceof OpenR66ProtocolNoConnectionException) { logger.debug("Connection impossible with NETWORK channel {}", exception.getClass().getName() + " : " + exception.getMessage()); channel.close(); return; } else { logger.debug( "Network Channel Exception: {} {}", channel.id(), exception.getClass().getName() + " : " + exception.getMessage()); } final ConnectionErrorPacket errorPacket = new ConnectionErrorPacket( exception.getClass().getName() + " : " + exception.getMessage(), null); writeError(channel, ChannelUtils.NOCHANNEL, ChannelUtils.NOCHANNEL, errorPacket); logger.debug("Will close NETWORK channel: {}", exception.getClass().getName() + " : " + exception.getMessage()); ChannelCloseTimer.closeFutureChannel(channel); } else { // Nothing to do return; } } /** * Write error back to remote client * * @param channel * @param remoteId * @param localId * @param error */ public static void writeError(Channel channel, Integer remoteId, Integer localId, AbstractLocalPacket error) { NetworkPacket networkPacket = null; try { networkPacket = new NetworkPacket(localId, remoteId, error, null); } catch (OpenR66ProtocolPacketException e) { } try { if (channel.isActive()) { channel.writeAndFlush(networkPacket).await(Configuration.WAITFORNETOP); } } catch (InterruptedException e) { } } /** * @return the dbSession */ public DbSession getDbSession() { return dbSession; } /** * * @return True if this Handler is for SSL */ public boolean isSsl() { return isSSL; } }