/** 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.localhandler; import static org.waarp.openr66.context.R66FiniteDualStates.*; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import org.waarp.common.command.exception.Reply421Exception; import org.waarp.common.command.exception.Reply530Exception; import org.waarp.common.digest.FilesystemBasedDigest; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; import org.waarp.openr66.commander.ClientRunner; import org.waarp.openr66.context.ErrorCode; import org.waarp.openr66.context.R66Result; import org.waarp.openr66.context.R66Session; import org.waarp.openr66.context.authentication.R66Auth; import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException; import org.waarp.openr66.database.data.DbHostAuth; import org.waarp.openr66.database.data.DbTaskRunner; import org.waarp.openr66.protocol.configuration.Configuration; import org.waarp.openr66.protocol.configuration.Messages; import org.waarp.openr66.protocol.exception.OpenR66Exception; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessCancelException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessNoWriteBackException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessQueryAlreadyFinishedException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessQueryStillRunningException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessRemoteFileNotFoundException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessStopException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolNotAuthenticatedException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException; import org.waarp.openr66.protocol.localhandler.packet.AuthentPacket; import org.waarp.openr66.protocol.localhandler.packet.ConnectionErrorPacket; import org.waarp.openr66.protocol.localhandler.packet.ErrorPacket; import org.waarp.openr66.protocol.localhandler.packet.StartupPacket; import org.waarp.openr66.protocol.networkhandler.NetworkTransaction; import org.waarp.openr66.protocol.utils.ChannelCloseTimer; import org.waarp.openr66.protocol.utils.ChannelUtils; import org.waarp.openr66.protocol.utils.R66Future; /** * Class to implement actions related to general connection handler: channelClosed, startup, authentication, and error. * Used to store and retrieve the session information. * * @author "Frederic Bregier" * */ public abstract class ConnectionActions { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(ConnectionActions.class); /** * Session */ protected volatile R66Session session; /** * Local Channel Reference */ protected volatile LocalChannelReference localChannelReference; /** * Global Digest in receive */ protected FilesystemBasedDigest globalDigest; /** * Global Digest in receive using local hash if necessary */ protected FilesystemBasedDigest localDigest; protected void setFrom(ConnectionActions handler) { this.globalDigest = handler.globalDigest; this.localChannelReference = handler.localChannelReference; this.localDigest = handler.localDigest; this.session = handler.session; } void businessError() { if (session.getBusinessObject() != null) { session.getBusinessObject().checkAtError(session); } } /** * @return the session */ public R66Session getSession() { return session; } /** * @return the localChannelReference */ public LocalChannelReference getLocalChannelReference() { return localChannelReference; } /** * @return the globalDigest */ public FilesystemBasedDigest getGlobalDigest() { return globalDigest; } /** * @return the localDigest */ public FilesystemBasedDigest getLocalDigest() { return localDigest; } /** * Operations to ensure that channel closing is done correctly * * @param e */ public void channelClosed(ChannelHandlerContext e) { DbTaskRunner runner = session.getRunner(); try { logger.debug("Local Server Channel Closed: {} {}", (localChannelReference != null ? localChannelReference : "no LocalChannelReference"), (runner != null ? runner.toShortString() : "no runner")); // clean session objects like files boolean mustFinalize = true; if (localChannelReference != null && localChannelReference.getFutureRequest().isDone()) { // already done mustFinalize = false; } else { if (localChannelReference != null) { R66Future fvr = localChannelReference.getFutureValidRequest(); try { fvr.await(Configuration.configuration.getTIMEOUTCON() * 2, TimeUnit.MILLISECONDS); } catch (InterruptedException e1) { } if (fvr.isDone()) { if (!fvr.isSuccess()) { // test if remote server was Overloaded if (fvr.getResult() != null && fvr.getResult().getCode() == ErrorCode.ServerOverloaded) { // ignore mustFinalize = false; } } else { mustFinalize = false; } } logger.debug("Must Finalize: " + mustFinalize); if (mustFinalize) { session.newState(ERROR); R66Result finalValue = new R66Result( new OpenR66ProtocolSystemException( Messages.getString("LocalServerHandler.4")), //$NON-NLS-1$ session, true, ErrorCode.FinalOp, runner); // True since closed try { tryFinalizeRequest(finalValue); } catch (OpenR66Exception e2) { } } } } if (mustFinalize && runner != null) { if (runner.isSelfRequested() && localChannelReference != null) { R66Future transfer = localChannelReference.getFutureRequest(); // Since requested : log R66Result result = transfer.getResult(); if (transfer.isDone() && transfer.isSuccess()) { logger.info("TRANSFER REQUESTED RESULT: SUCCESS " + (result != null ? result.toString() : "no result")); } else { logger.error("TRANSFER REQUESTED RESULT: FAILURE " + (result != null ? result.toString() : "no result")); } } } session.setStatus(50); session.newState(CLOSEDCHANNEL); session.partialClear(); session.setStatus(51); if (localChannelReference != null) { if (localChannelReference.getDbSession() != null) { localChannelReference.getDbSession().endUseConnection(); logger.debug("End Use Connection"); } NetworkTransaction.checkClosingNetworkChannel(localChannelReference.getNetworkChannelObject(), localChannelReference); session.setStatus(52); } else { logger.debug("Local Server Channel Closed but no LocalChannelReference: " + e.channel().id()); } // Now if runner is not yet finished, finish it by force if (mustFinalize && localChannelReference != null && (!localChannelReference.getFutureRequest().isDone())) { R66Result finalValue = new R66Result( new OpenR66ProtocolSystemException( Messages.getString("LocalServerHandler.11")), //$NON-NLS-1$ session, true, ErrorCode.FinalOp, runner); localChannelReference.invalidateRequest(finalValue); // In case stop the attached thread if any ClientRunner clientRunner = localChannelReference.getClientRunner(); if (clientRunner != null) { try { Thread.sleep(Configuration.RETRYINMS); } catch (InterruptedException e1) { } clientRunner.interrupt(); } } } finally { if (runner != null) { runner.clean(); } if (localChannelReference != null) { localChannelReference.close(); } } } /** * Create a new Session at startup of the channel */ public void newSession() { session = new R66Session(); session.setStatus(60); session.setBusinessObject(Configuration.configuration.getR66BusinessFactory().getBusinessInterface(session)); } /** * Startup of the session and the local channel reference * * @param channel * @param packet * @throws OpenR66ProtocolPacketException */ public void startup(Channel channel, StartupPacket packet) throws OpenR66ProtocolPacketException { for (int i = 0; i < Configuration.RETRYNB; i++) { localChannelReference = Configuration.configuration .getLocalTransaction().getFromId(packet.getLocalId()); if (localChannelReference != null) { break; } } if (localChannelReference == null) { session.newState(ERROR); logger.error(Messages.getString("LocalServerHandler.1")); //$NON-NLS-1$ if (channel.isActive()) { ErrorPacket error = new ErrorPacket("Cannot startup connection", ErrorCode.ConnectionImpossible.getCode(), ErrorPacket.FORWARDCLOSECODE); channel.writeAndFlush(error).addListener(ChannelFutureListener.CLOSE); // Cannot do writeBack(error, true); session.setStatus(40); } return; } if (session.getBusinessObject() != null) { try { session.getBusinessObject().checkAtConnection(session); } catch (OpenR66RunnerErrorException e) { ErrorPacket error = new ErrorPacket("Connection refused by business logic", ErrorCode.ConnectionImpossible.getCode(), ErrorPacket.FORWARDCLOSECODE); channel.writeAndFlush(error).addListener(ChannelFutureListener.CLOSE); session.setStatus(40); return; } } session.newState(STARTUP); localChannelReference.validateStartup(true); session.setLocalChannelReference(localChannelReference); channel.writeAndFlush(packet); session.setStatus(41); } /** * Refuse a connection * * @param channel * @param packet * @param e1 * @throws OpenR66ProtocolPacketException */ private final void refusedConnection(Channel channel, AuthentPacket packet, Exception e1) throws OpenR66ProtocolPacketException { logger.error(Messages.getString("LocalServerHandler.6") + //$NON-NLS-1$ localChannelReference.getNetworkChannel().remoteAddress() + " : " + packet.getHostId()); logger.debug(Messages.getString("LocalServerHandler.6") + //$NON-NLS-1$ localChannelReference.getNetworkChannel().remoteAddress() + " : " + packet.getHostId(), e1); if (Configuration.configuration.getR66Mib() != null) { Configuration.configuration.getR66Mib().notifyError( "Connection not allowed from " + localChannelReference.getNetworkChannel().remoteAddress() + " since " + e1.getMessage(), packet.getHostId()); } DbHostAuth auth = R66Auth.getServerAuth(localChannelReference.getDbSession(), packet.getHostId()); if (auth != null && !auth.isActive()) { e1 = new Reply530Exception("Host is Inactive therefore connection is refused"); } R66Result result = new R66Result( new OpenR66ProtocolSystemException( Messages.getString("LocalServerHandler.6") + //$NON-NLS-1$ localChannelReference.getNetworkChannel().remoteAddress(), e1), session, true, ErrorCode.BadAuthent, null); localChannelReference.invalidateRequest(result); session.newState(ERROR); ErrorPacket error = new ErrorPacket(e1.getMessage(), ErrorCode.BadAuthent.getCode(), ErrorPacket.FORWARDCLOSECODE); ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); localChannelReference.validateConnection(false, result); Channel networkchannel = localChannelReference.getNetworkChannel(); boolean valid = NetworkTransaction.shuttingDownNetworkChannelBlackList(localChannelReference .getNetworkChannelObject()); logger.warn("Closing and blacklisting NetworkChannelReference since LocalChannel is not authenticated: " + valid); ChannelCloseTimer.closeFutureChannel(channel); ChannelCloseTimer.closeFutureChannel(networkchannel); businessError(); } /** * Authentication * * @param channel * @param packet * @throws OpenR66ProtocolPacketException */ public void authent(Channel channel, AuthentPacket packet) throws OpenR66ProtocolPacketException { if (packet.isToValidate()) { session.newState(AUTHENTR); } if (localChannelReference.getDbSession() != null) { localChannelReference.getDbSession().useConnection(); } if (localChannelReference.getNetworkChannelObject() != null) { localChannelReference.getNetworkChannelObject().setHostId(packet.getHostId()); } else { session.newState(ERROR); logger.error("Service unavailable: " + packet.getHostId()); R66Result result = new R66Result( new OpenR66ProtocolSystemException("Service unavailable"), session, true, ErrorCode.ConnectionImpossible, null); localChannelReference.invalidateRequest(result); ErrorPacket error = new ErrorPacket("Service unavailable", ErrorCode.ConnectionImpossible.getCode(), ErrorPacket.FORWARDCLOSECODE); ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); localChannelReference.validateConnection(false, result); ChannelCloseTimer.closeFutureChannel(channel); session.setStatus(43); businessError(); return; } try { session.getAuth().connection(localChannelReference.getDbSession(), packet.getHostId(), packet.getKey()); } catch (Reply530Exception e1) { refusedConnection(channel, packet, e1); session.setStatus(42); return; } catch (Reply421Exception e1) { session.newState(ERROR); logger.error("Service unavailable: " + packet.getHostId(), e1); R66Result result = new R66Result( new OpenR66ProtocolSystemException("Service unavailable", e1), session, true, ErrorCode.ConnectionImpossible, null); localChannelReference.invalidateRequest(result); ErrorPacket error = new ErrorPacket("Service unavailable", ErrorCode.ConnectionImpossible.getCode(), ErrorPacket.FORWARDCLOSECODE); ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); localChannelReference.validateConnection(false, result); ChannelCloseTimer.closeFutureChannel(channel); session.setStatus(43); businessError(); return; } localChannelReference.setPartner(packet.getHostId()); // Now if configuration say to do so: check remote ip address if (Configuration.configuration.isCheckRemoteAddress() && !localChannelReference.getPartner().isProxified()) { DbHostAuth host = R66Auth.getServerAuth(localChannelReference.getDbSession(), packet.getHostId()); boolean toTest = false; if (!host.isProxified()) { if (host.isClient()) { if (Configuration.configuration.isCheckClientAddress()) { if (host.isNoAddress()) { // 0.0.0.0 so nothing toTest = false; } else { toTest = true; } } } else { toTest = true; } } if (toTest) { // Real address so compare String address = host.getAddress(); InetAddress[] inetAddress = null; try { inetAddress = InetAddress.getAllByName(address); } catch (UnknownHostException e) { inetAddress = null; } if (inetAddress != null) { InetSocketAddress socketAddress = (InetSocketAddress) session .getRemoteAddress(); boolean found = false; for (int i = 0; i < inetAddress.length; i++) { if (socketAddress.getAddress().equals(inetAddress[i])) { found = true; break; } } if (!found) { // error refusedConnection(channel, packet, new OpenR66ProtocolNotAuthenticatedException( "Server IP not authenticated: " + inetAddress[0].toString() + " compare to " + socketAddress.getAddress().toString())); session.setStatus(104); return; } } } } if (session.getBusinessObject() != null) { try { session.getBusinessObject().checkAtAuthentication(session); } catch (OpenR66RunnerErrorException e) { refusedConnection(channel, packet, new OpenR66ProtocolNotAuthenticatedException(e.getMessage())); session.setStatus(104); return; } } R66Result result = new R66Result(session, true, ErrorCode.InitOk, null); session.newState(AUTHENTD); localChannelReference.validateConnection(true, result); logger.debug("Local Server Channel Validated: {} ", (localChannelReference != null ? localChannelReference : "no LocalChannelReference")); session.setStatus(44); NetworkTransaction.addClient(localChannelReference.getNetworkChannelObject(), packet.getHostId()); if (packet.isToValidate()) { // only requested packet.validate(session.getAuth().isSsl()); ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet, false); session.setStatus(98); } logger.debug("Partner: {} from {}", localChannelReference.getPartner(), Configuration.configuration.getVersions()); } /** * Receive a connection error * * @param channel * @param packet */ public void connectionError(Channel channel, ConnectionErrorPacket packet) { // do something according to the error logger.error(channel.id() + ": " + packet.toString()); ErrorCode code = ErrorCode.ConnectionImpossible; if (packet.getSmiddle() != null) { code = ErrorCode.getFromCode(packet.getSmiddle()); } localChannelReference.invalidateRequest(new R66Result( new OpenR66ProtocolSystemException(packet.getSheader()), session, true, code, null)); // True since closing session.newState(ERROR); session.setStatus(45); channel.close(); businessError(); } /** * Class to finalize a runner when the future is over * * @author Frederic Bregier * */ private static final class RunnerChannelFutureListener implements ChannelFutureListener { private LocalChannelReference localChannelReference; private R66Result result; public RunnerChannelFutureListener(LocalChannelReference localChannelReference, R66Result result) { this.localChannelReference = localChannelReference; this.result = result; } public void operationComplete(ChannelFuture future) throws Exception { localChannelReference.invalidateRequest(result); ChannelCloseTimer.closeFutureChannel(localChannelReference.getLocalChannel()); } } /** * Receive a remote error * * @param channel * @param packet * @throws OpenR66RunnerErrorException * @throws OpenR66ProtocolSystemException * @throws OpenR66ProtocolBusinessException */ public void errorMesg(Channel channel, ErrorPacket packet) throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException, OpenR66ProtocolBusinessException { // do something according to the error if (session.getLocalChannelReference().getFutureRequest().isDone()) { // already canceled or successful return; } logger.error(channel.id() + ": " + packet.toString()); session.setStatus(46); ErrorCode code = ErrorCode.getFromCode(packet.getSmiddle()); session.getLocalChannelReference().setErrorMessage(packet.getSheader(), code); OpenR66ProtocolBusinessException exception; if (code.code == ErrorCode.CanceledTransfer.code) { exception = new OpenR66ProtocolBusinessCancelException(packet.getSheader()); int rank = 0; DbTaskRunner runner = this.session.getRunner(); if (runner != null) { runner.setRankAtStartup(rank); runner.stopOrCancelRunner(code); } R66Result result = new R66Result(exception, session, true, code, runner); // now try to inform other session.setFinalizeTransfer(false, result); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet, false). addListener(new RunnerChannelFutureListener(localChannelReference, result)); } catch (OpenR66ProtocolPacketException e) { } return; } else if (code.code == ErrorCode.StoppedTransfer.code) { exception = new OpenR66ProtocolBusinessStopException(packet.getSheader()); String[] vars = packet.getSheader().split(" "); String var = vars[vars.length - 1]; int rank = Integer.parseInt(var); DbTaskRunner runner = this.session.getRunner(); if (runner != null) { if (rank < runner.getRank()) { runner.setRankAtStartup(rank); } runner.stopOrCancelRunner(code); } R66Result result = new R66Result(exception, session, true, code, runner); // now try to inform other session.setFinalizeTransfer(false, result); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet, false). addListener(new RunnerChannelFutureListener(localChannelReference, result)); } catch (OpenR66ProtocolPacketException e) { } return; } else if (code.code == ErrorCode.QueryAlreadyFinished.code) { DbTaskRunner runner = session.getRunner(); if (runner == null) { exception = new OpenR66ProtocolBusinessCancelException(packet.toString()); } else { if (runner.isSender()) { exception = new OpenR66ProtocolBusinessQueryAlreadyFinishedException( packet.getSheader()); runner.finishTransferTask(code); tryFinalizeRequest(new R66Result(exception, session, true, code, runner)); } else { exception = new OpenR66ProtocolBusinessCancelException(packet.toString()); } } throw exception; } else if (code.code == ErrorCode.QueryStillRunning.code) { exception = new OpenR66ProtocolBusinessQueryStillRunningException(packet.getSheader()); throw exception; } else if (code.code == ErrorCode.BadAuthent.code) { exception = new OpenR66ProtocolNotAuthenticatedException(packet.toString()); } else if (code.code == ErrorCode.QueryRemotelyUnknown.code) { exception = new OpenR66ProtocolBusinessCancelException(packet.toString()); } else if (code.code == ErrorCode.FileNotFound.code) { exception = new OpenR66ProtocolBusinessRemoteFileNotFoundException(packet.toString()); } else { exception = new OpenR66ProtocolBusinessNoWriteBackException(packet.toString()); } session.setFinalizeTransfer(false, new R66Result(exception, session, true, code, session.getRunner())); throw exception; } /** * Try to finalize the request if possible * * @param errorValue * in case of Error * @throws OpenR66ProtocolSystemException * @throws OpenR66RunnerErrorException */ public final void tryFinalizeRequest(R66Result errorValue) throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException { session.tryFinalizeRequest(errorValue); } }