Commit 4bb50229 authored by Maxym Borodenko's avatar Maxym Borodenko

Apache Mina logging

parent b55f11c4
Pipeline #8918 passed with stage
in 2 minutes and 57 seconds
/*
* Copyright 2019 Global Crop Diversity Trust
*
* Licensed 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.ftpserver.impl;
import java.io.IOException;
import java.nio.charset.MalformedInputException;
import org.apache.ftpserver.command.Command;
import org.apache.ftpserver.command.CommandFactory;
import org.apache.ftpserver.ftplet.DefaultFtpReply;
import org.apache.ftpserver.ftplet.FileSystemView;
import org.apache.ftpserver.ftplet.FtpReply;
import org.apache.ftpserver.ftplet.FtpRequest;
import org.apache.ftpserver.ftplet.FtpletResult;
import org.apache.ftpserver.ftpletcontainer.FtpletContainer;
import org.apache.ftpserver.listener.Listener;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.write.WriteToClosedSessionException;
import org.apache.mina.filter.codec.ProtocolDecoderException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <strong>Internal class, do not use directly.</strong>
*
* The custom {@link FtpHandler} implementation. Based on {@link DefaultFtpHandler}
*/
public class CustomFtpHandler implements FtpHandler {
private final Logger LOG = LoggerFactory.getLogger(CustomFtpHandler.class);
private final static String[] NON_AUTHENTICATED_COMMANDS = new String[] { "USER", "PASS", "AUTH", "QUIT", "PROT", "PBSZ" };
private FtpServerContext context;
private Listener listener;
@Override
public void init(final FtpServerContext context, final Listener listener) {
this.context = context;
this.listener = listener;
}
@Override
public void sessionCreated(final FtpIoSession session) throws Exception {
session.setListener(listener);
ServerFtpStatistics stats = ((ServerFtpStatistics) context.getFtpStatistics());
if (stats != null) {
stats.setOpenConnection(session);
}
}
public void sessionOpened(final FtpIoSession session) throws Exception {
FtpletContainer ftplets = context.getFtpletContainer();
FtpletResult ftpletRet;
try {
ftpletRet = ftplets.onConnect(session.getFtpletSession());
} catch (Exception e) {
LOG.debug("Ftplet threw exception", e);
ftpletRet = FtpletResult.DISCONNECT;
}
if (ftpletRet == FtpletResult.DISCONNECT) {
LOG.debug("Ftplet returned DISCONNECT, session will be closed");
session.close(false).awaitUninterruptibly(10000);
} else {
session.updateLastAccessTime();
session.write(LocalizedFtpReply.translate(session, null, context, FtpReply.REPLY_220_SERVICE_READY, null, null));
}
}
@Override
public void sessionClosed(final FtpIoSession session) throws Exception {
LOG.debug("Closing session");
try {
context.getFtpletContainer().onDisconnect(session.getFtpletSession());
} catch (Exception e) {
// swallow the exception, we're closing down the session anyways
LOG.warn("Ftplet threw an exception on disconnect", e);
}
// make sure we close the data connection if it happens to be open
try {
ServerDataConnectionFactory dc = session.getDataConnection();
if(dc != null) {
dc.closeDataConnection();
}
} catch (Exception e) {
// swallow the exception, we're closing down the session anyways
LOG.warn("Data connection threw an exception on disconnect", e);
}
FileSystemView fs = session.getFileSystemView();
if(fs != null) {
try {
fs.dispose();
} catch (Exception e) {
LOG.warn("FileSystemView threw an exception on disposal", e);
}
}
ServerFtpStatistics stats = ((ServerFtpStatistics) context.getFtpStatistics());
if (stats != null) {
stats.setLogout(session);
stats.setCloseConnection(session);
LOG.debug("Statistics login and connection count decreased due to session close");
} else {
LOG.warn("Statistics not available in session, can not decrease login and connection count");
}
LOG.debug("Session closed");
}
@Override
public void exceptionCaught(final FtpIoSession session, final Throwable cause) throws Exception {
if(cause instanceof ProtocolDecoderException && cause.getCause() instanceof MalformedInputException) {
// client probably sent something which is not UTF-8 and we failed to
// decode it
LOG.warn("Client sent command that could not be decoded: {}", ((ProtocolDecoderException)cause).getHexdump());
session.write(new DefaultFtpReply(FtpReply.REPLY_501_SYNTAX_ERROR_IN_PARAMETERS_OR_ARGUMENTS, "Invalid character in command"));
} else if (cause instanceof WriteToClosedSessionException) {
WriteToClosedSessionException writeToClosedSessionException = (WriteToClosedSessionException) cause;
LOG.warn(
"Client closed connection before all replies could be sent, last reply was {}",
writeToClosedSessionException.getRequest());
session.close(false).awaitUninterruptibly(10000);
} else {
LOG.error("Exception caught, closing session. Reason: {}", cause.getMessage());
session.close(false).awaitUninterruptibly(10000);
}
}
private boolean isCommandOkWithoutAuthentication(String command) {
boolean okay = false;
for (String allowed : NON_AUTHENTICATED_COMMANDS) {
if (allowed.equals(command)) {
okay = true;
break;
}
}
return okay;
}
@Override
public void messageReceived(final FtpIoSession session, final FtpRequest request) throws Exception {
try {
session.updateLastAccessTime();
String commandName = request.getCommand();
CommandFactory commandFactory = context.getCommandFactory();
Command command = commandFactory.getCommand(commandName);
// make sure the user is authenticated before he issues commands
if (!session.isLoggedIn() && !isCommandOkWithoutAuthentication(commandName)) {
session.write(LocalizedFtpReply.translate(session, request,
context, FtpReply.REPLY_530_NOT_LOGGED_IN, "permission", null));
return;
}
FtpletContainer ftplets = context.getFtpletContainer();
FtpletResult ftpletRet;
try {
ftpletRet = ftplets.beforeCommand(session.getFtpletSession(), request);
} catch (Exception e) {
LOG.debug("Ftplet container threw exception", e);
ftpletRet = FtpletResult.DISCONNECT;
}
if (ftpletRet == FtpletResult.DISCONNECT) {
LOG.debug("Ftplet returned DISCONNECT, session will be closed");
session.close(false).awaitUninterruptibly(10000);
return;
} else if (ftpletRet != FtpletResult.SKIP) {
if (command != null) {
synchronized (session) {
command.execute(session, context, request);
}
} else {
session.write(LocalizedFtpReply.translate(session, request, context,
FtpReply.REPLY_502_COMMAND_NOT_IMPLEMENTED, "not.implemented", null));
}
try {
ftpletRet = ftplets.afterCommand(session.getFtpletSession(), request, session.getLastReply());
} catch (Exception e) {
LOG.debug("Ftplet container threw exception", e);
ftpletRet = FtpletResult.DISCONNECT;
}
if (ftpletRet == FtpletResult.DISCONNECT) {
LOG.debug("Ftplet returned DISCONNECT, session will be closed");
session.close(false).awaitUninterruptibly(10000);
return;
}
}
} catch (Exception ex) {
// send error reply
try {
session.write(LocalizedFtpReply.translate(session, request,
context, FtpReply.REPLY_550_REQUESTED_ACTION_NOT_TAKEN, null, null));
} catch (Exception ignored) {}
if (ex instanceof java.io.IOException) {
throw (IOException) ex;
} else {
LOG.warn("RequestHandler.service()", ex);
}
}
}
@Override
public void sessionIdle(final FtpIoSession session, final IdleStatus status) throws Exception {
LOG.info("Session idle, closing");
session.close(false).awaitUninterruptibly(10000);
}
@Override
public void messageSent(final FtpIoSession session, final FtpReply reply) throws Exception {
// do nothing
}
}
/*
* Copyright 2019 Global Crop Diversity Trust
*
* Licensed 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.ftpserver.listener;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import org.apache.ftpserver.FtpServerConfigurationException;
import org.apache.ftpserver.nio.CustomNioListener;
import org.apache.mina.filter.firewall.Subnet;
/**
* Factory for listeners. Listeners themselves are immutable and must be
* created using this factory.
*/
public class CustomListenerFactory extends ListenerFactory {
/**
* Create a listener based on the settings of this factory. The listener is immutable.
* @return The created listener
*/
@Override
public Listener createListener() {
final String serverAddress = getServerAddress();
final List<InetAddress> blockedAddresses = getBlockedAddresses();
final List<Subnet> blockedSubnets = getBlockedSubnets();
try {
InetAddress.getByName(serverAddress);
} catch (UnknownHostException e) {
throw new FtpServerConfigurationException("Unknown host", e);
}
// Deal with the old style black list and new session Filter here.
if (getSessionFilter() != null) {
if (blockedAddresses != null || blockedSubnets != null) {
throw new IllegalStateException(
"Usage of SessionFilter in combination with blockedAddesses/subnets is not supported. ");
}
}
if (blockedAddresses != null || blockedSubnets != null) {
return new CustomNioListener(serverAddress, getPort(), isImplicitSsl(), getSslConfiguration(),
getDataConnectionConfiguration(), getIdleTimeout(), blockedAddresses, blockedSubnets);
} else {
return new CustomNioListener(serverAddress, getPort(), isImplicitSsl(), getSslConfiguration(),
getDataConnectionConfiguration(), getIdleTimeout(), getSessionFilter());
}
}
}
/*
* Copyright 2019 Global Crop Diversity Trust
*
* Licensed 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.ftpserver.nio;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.GeneralSecurityException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.ftpserver.impl.CustomFtpHandler;
import org.apache.ftpserver.DataConnectionConfiguration;
import org.apache.ftpserver.FtpServerConfigurationException;
import org.apache.ftpserver.impl.FtpHandler;
import org.apache.ftpserver.impl.FtpIoSession;
import org.apache.ftpserver.impl.FtpServerContext;
import org.apache.ftpserver.ipfilter.MinaSessionFilter;
import org.apache.ftpserver.ipfilter.SessionFilter;
import org.apache.ftpserver.listener.Listener;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.listener.nio.AbstractListener;
import org.apache.ftpserver.listener.nio.FtpHandlerAdapter;
import org.apache.ftpserver.listener.nio.FtpLoggingFilter;
import org.apache.ftpserver.listener.nio.FtpServerProtocolCodecFactory;
import org.apache.ftpserver.listener.nio.NioListener;
import org.apache.ftpserver.ssl.ClientAuth;
import org.apache.ftpserver.ssl.SslConfiguration;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.firewall.Subnet;
import org.apache.mina.filter.logging.MdcInjectionFilter;
import org.apache.mina.filter.ssl.SslFilter;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <strong>Internal class, do not use directly.</strong>
*
* The custom {@link Listener} implementation. Based on {@link NioListener}
*/
public class CustomNioListener extends AbstractListener {
private final Logger LOG = LoggerFactory.getLogger(CustomNioListener.class);
private SocketAcceptor acceptor;
private InetSocketAddress address;
boolean suspended = false;
private FtpHandler handler = new CustomFtpHandler();
private FtpServerContext context;
/**
* @deprecated Use the constructor with IpFilter instead.
* Constructor for internal use, do not use directly. Instead use {@link ListenerFactory}
*/
@Deprecated
public CustomNioListener(String serverAddress, int port,
boolean implicitSsl,
SslConfiguration sslConfiguration,
DataConnectionConfiguration dataConnectionConfig,
int idleTimeout, List<InetAddress> blockedAddresses, List<Subnet> blockedSubnets) {
super(serverAddress, port, implicitSsl, sslConfiguration, dataConnectionConfig,
idleTimeout, blockedAddresses, blockedSubnets);
}
/**
* Constructor for internal use, do not use directly. Instead use {@link ListenerFactory}
*/
public CustomNioListener(String serverAddress, int port, boolean implicitSsl,
SslConfiguration sslConfiguration,
DataConnectionConfiguration dataConnectionConfig, int idleTimeout,
SessionFilter sessionFilter) {
super(serverAddress, port, implicitSsl, sslConfiguration,
dataConnectionConfig, idleTimeout, sessionFilter);
}
/**
* @see Listener#start(FtpServerContext)
*/
public synchronized void start(FtpServerContext context) {
if(!isStopped()) {
// listener already started, don't allow
throw new IllegalStateException("Listener already started");
}
try {
this.context = context;
acceptor = new NioSocketAcceptor(Runtime.getRuntime()
.availableProcessors());
if (getServerAddress() != null) {
address = new InetSocketAddress(getServerAddress(), getPort());
} else {
address = new InetSocketAddress(getPort());
}
acceptor.setReuseAddress(true);
acceptor.getSessionConfig().setReadBufferSize(2048);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE,
getIdleTimeout());
// Decrease the default receiver buffer size
acceptor.getSessionConfig().setReceiveBufferSize(512);
MdcInjectionFilter mdcFilter = new MdcInjectionFilter();
acceptor.getFilterChain().addLast("mdcFilter", mdcFilter);
SessionFilter sessionFilter = getSessionFilter();
if (sessionFilter != null) {
// add and IP filter to the filter chain.
acceptor.getFilterChain().addLast("sessionFilter",
new MinaSessionFilter(sessionFilter));
}
acceptor.getFilterChain().addLast("threadPool",
new ExecutorFilter(context.getThreadPoolExecutor()));
acceptor.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new FtpServerProtocolCodecFactory()));
acceptor.getFilterChain().addLast("mdcFilter2", mdcFilter);
acceptor.getFilterChain().addLast("logger", new FtpLoggingFilter());
if (isImplicitSsl()) {
SslConfiguration ssl = getSslConfiguration();
SslFilter sslFilter;
try {
sslFilter = new SslFilter(ssl.getSSLContext());
} catch (GeneralSecurityException e) {
throw new FtpServerConfigurationException("SSL could not be initialized, check configuration");
}
if (ssl.getClientAuth() == ClientAuth.NEED) {
sslFilter.setNeedClientAuth(true);
} else if (ssl.getClientAuth() == ClientAuth.WANT) {
sslFilter.setWantClientAuth(true);
}
if (ssl.getEnabledCipherSuites() != null) {
sslFilter.setEnabledCipherSuites(ssl.getEnabledCipherSuites());
}
acceptor.getFilterChain().addFirst("sslFilter", sslFilter);
}
handler.init(context, this);
acceptor.setHandler(new FtpHandlerAdapter(context, handler));
try {
acceptor.bind(address);
} catch (IOException e) {
throw new FtpServerConfigurationException("Failed to bind to address " + address + ", check configuration", e);
}
updatePort();
} catch(RuntimeException e) {
// clean up if we fail to start
stop();
throw e;
}
}
private void updatePort() {
// update the port to the real port bound by the listener
setPort(acceptor.getLocalAddress().getPort());
}
/**
* @see Listener#stop()
*/
public synchronized void stop() {
// close server socket
if (acceptor != null) {
acceptor.unbind();
acceptor.dispose();
acceptor = null;
}
context = null;
}
/**
* @see Listener#isStopped()
*/
public boolean isStopped() {
return acceptor == null;
}
/**
* @see Listener#isSuspended()
*/
public boolean isSuspended() {
return suspended;
}
/**
* @see Listener#resume()
*/
public synchronized void resume() {
if (acceptor != null && suspended) {
try {
LOG.debug("Resuming listener");
acceptor.bind(address);
LOG.debug("Listener resumed");
updatePort();
suspended = false;
} catch (IOException e) {
LOG.error("Failed to resume listener", e);
}
}
}
/**
* @see Listener#suspend()
*/
public synchronized void suspend() {
if (acceptor != null && !suspended) {
LOG.debug("Suspending listener");
acceptor.unbind();
suspended = true;
LOG.debug("Listener suspended");
}
}
/**
* @see Listener#getActiveSessions()
*/
public synchronized Set<FtpIoSession> getActiveSessions() {
Map<Long, IoSession> sessions = acceptor.getManagedSessions();
Set<FtpIoSession> ftpSessions = new HashSet<FtpIoSession>();
for (IoSession session : sessions.values()) {
ftpSessions.add(new FtpIoSession(session, context));
}
return ftpSessions;
}
}
......@@ -30,9 +30,9 @@ import org.apache.ftpserver.ftplet.FileSystemFactory;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.Ftplet;
import org.apache.ftpserver.ftplet.UserManager;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.message.MessageResource;
import org.apache.ftpserver.ssl.SslConfigurationFactory;
import org.apache.ftpserver.listener.CustomListenerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
......@@ -206,7 +206,7 @@ public class RepositoryFtpServer implements InitializingBean, DisposableBean {
}
{
final ListenerFactory factory = new ListenerFactory();
final CustomListenerFactory factory = new CustomListenerFactory();
// set the port of the listener
factory.setPort(ftpPort);
// set idle timeout
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment