Skip to content

Commit

Permalink
Added HOST command
Browse files Browse the repository at this point in the history
  • Loading branch information
Guichaguri committed Jun 21, 2018
1 parent 7edccd1 commit a7b20ce
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 13 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Although it's named "minimal", it supports a bunch of features:

* 100% Java, no libraries
* Lightweight
* Supports 55 FTP commands
* Supports 57 FTP commands
* TLS/SSL support
* Custom File System support
* Custom User Authentication support
Expand All @@ -31,7 +31,7 @@ The required minimum implementation is already done, however, there are still co
* [RFC 3659](https://tools.ietf.org/html/rfc3659) - Extensions to FTP (4/4)
* [RFC 4217](https://tools.ietf.org/html/rfc4217) - Securing FTP with TLS
* [RFC 5797](https://tools.ietf.org/html/rfc5797) - FTP Command and Extension Registry
* [RFC 7151](https://tools.ietf.org/html/rfc7151) - File Transfer Protocol HOST Command for Virtual Hosts (0/1)
* [RFC 7151](https://tools.ietf.org/html/rfc7151) - File Transfer Protocol HOST Command for Virtual Hosts (1/1)
* [draft-twine-ftpmd5-00](https://tools.ietf.org/html/draft-twine-ftpmd5-00) - The "MD5" and "MMD5" FTP Command Extensions (2/2) (Obsolete)
* [draft-somers-ftp-mfxx-04](https://tools.ietf.org/html/draft-somers-ftp-mfxx-04) - The "MFMT", "MFCT", and "MFF" Command Extensions for FTP (1/3)
* [draft-bryan-ftpext-hash-02](https://tools.ietf.org/html/draft-bryan-ftpext-hash-02) - File Transfer Protocol HASH Command for Cryptographic Hashes (1/1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.guichaguri.minimalftp.api;

import com.guichaguri.minimalftp.FTPConnection;
import java.net.InetAddress;

/**
* Represents an user authenticator.
Expand All @@ -26,6 +27,19 @@
*/
public interface IUserAuthenticator {

/**
* Whether the user is allowed to connect through the specified host.
*
* The client will not send the host address if {@link #needsUsername(FTPConnection)} returns {@code false}
* or the client does not support custom hosts.
*
* @param host The host address
* @return Whether the specified host is accepted
*/
default boolean acceptsHost(FTPConnection con, InetAddress host) {
return true;
}

/**
* Whether this authenticator requires a username.
*
Expand All @@ -41,23 +55,27 @@ public interface IUserAuthenticator {
*
* @param con The FTP connection
* @param username The username
* @param host The host address or {@code null} if it's not specified by the client
* @return {@code true} if this authenticator requires a password
*/
boolean needsPassword(FTPConnection con, String username);
boolean needsPassword(FTPConnection con, String username, InetAddress host);

/**
* Authenticates a user synchronously.
*
* You use a custom file system depending on the user
* You can use a custom file system depending on the user.
*
* If the {@param host} is {@code null}, you can use a "default host" or a union of all hosts combined
* as some clients might not support custom hosts.
*
* @param con The FTP connection
* @param host The host address or {@code null} when the client didn't specified the hostname
* @param username The username or {@code null} when {@link #needsUsername(FTPConnection)} returns false
* @param password The password or {@code null} when {@link #needsPassword(FTPConnection, String)} returns false
* @param password The password or {@code null} when {@link #needsPassword(FTPConnection, String, InetAddress)} returns false
* @return A file system if the authentication succeeded
* @throws AuthException When the authentication failed
*/
IFileSystem authenticate(FTPConnection con, String username, String password) throws AuthException;

IFileSystem authenticate(FTPConnection con, InetAddress host, String username, String password) throws AuthException;

/**
* The exception that should be thrown when the authentication fails
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
import com.guichaguri.minimalftp.FTPServer;
import com.guichaguri.minimalftp.Utils;
import com.guichaguri.minimalftp.api.IUserAuthenticator;
import com.guichaguri.minimalftp.api.IUserAuthenticator.AuthException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
Expand All @@ -36,6 +38,7 @@ public class ConnectionHandler {

private final FTPConnection con;

private InetAddress address = null;
private boolean authenticated = false;
private String username = null;

Expand Down Expand Up @@ -129,6 +132,8 @@ public void registerCommands() {
con.registerCommand("EPSV", "EPSV", this::epsv); // Extended Passive Mode (RFC 2428)
con.registerCommand("EPRT", "EPRT <address>", this::eprt); // Extended Active Mode (RFC 2428)

con.registerCommand("HOST", "HOST <address>", this::host, false); // Custom Virtual Hosts (RFC 7151)

con.registerFeature("base"); // Base Commands (RFC 5797)
con.registerFeature("secu"); // Security Commands (RFC 5797)
con.registerFeature("hist"); // Obsolete Commands (RFC 5797)
Expand All @@ -139,6 +144,7 @@ public void registerCommands() {
con.registerFeature("PROT"); // Protection Level (RFC 2228)
con.registerFeature("EPSV"); // Extended Passive Mode (RFC 2428)
con.registerFeature("EPRT"); // Extended Active Mode (RFC 2428)
con.registerFeature("HOST"); // Custom Virtual Hosts (RFC 7151)
}

private void noop() {
Expand Down Expand Up @@ -191,6 +197,28 @@ private void mode(String mode) throws IOException {
}
}

private void host(String host) throws IOException {
if(authenticated) {
con.sendResponse(503, "The user is already authenticated");
return;
}

try {
IUserAuthenticator auth = con.getServer().getAuthenticator();
InetAddress address = InetAddress.getByName(host);

if(auth.acceptsHost(con, address)) {
this.address = address;
con.sendResponse(220, "Host accepted");
} else {
this.address = null;
con.sendResponse(504, "Host denied");
}
} catch(UnknownHostException ex) {
con.sendResponse(501, "Invalid host");
}
}

private void user(String username) throws IOException {
if(authenticated) {
con.sendResponse(230, "Logged in!");
Expand All @@ -200,7 +228,7 @@ private void user(String username) throws IOException {
this.username = username;

IUserAuthenticator auth = con.getServer().getAuthenticator();
if(auth.needsPassword(con, username)) {
if(auth.needsPassword(con, username, address)) {
// Requests a password for the authentication
con.sendResponse(331, "Needs a password");
} else {
Expand Down Expand Up @@ -257,6 +285,7 @@ private void syst() {
private void rein() {
authenticated = false;
username = null;
address = null;
con.sendResponse(220, "Ready for a new user");
}

Expand Down Expand Up @@ -444,10 +473,13 @@ private void eprt(String data) {

private boolean authenticate(IUserAuthenticator auth, String password) {
try {
con.setFileSystem(auth.authenticate(con, username, password));
con.setFileSystem(auth.authenticate(con, address, username, password));
authenticated = true;
return true;
} catch(AuthException ex) {
return false;
} catch(Exception ex) {
ex.printStackTrace();
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.guichaguri.minimalftp.FTPConnection;
import com.guichaguri.minimalftp.api.IFileSystem;
import com.guichaguri.minimalftp.api.IUserAuthenticator;
import java.net.InetAddress;

/**
* No Operation Authenticator
Expand All @@ -45,12 +46,12 @@ public boolean needsUsername(FTPConnection con) {
}

@Override
public boolean needsPassword(FTPConnection con, String username) {
public boolean needsPassword(FTPConnection con, String username, InetAddress address) {
return false;
}

@Override
public IFileSystem authenticate(FTPConnection con, String username, String password) throws AuthException {
public IFileSystem authenticate(FTPConnection con, InetAddress address, String username, String password) throws AuthException {
return fs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.guichaguri.minimalftp.api.IUserAuthenticator;
import com.guichaguri.minimalftp.impl.NativeFileSystem;
import java.io.File;
import java.net.InetAddress;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
Expand Down Expand Up @@ -37,12 +38,12 @@ public boolean needsUsername(FTPConnection con) {
}

@Override
public boolean needsPassword(FTPConnection con, String username) {
public boolean needsPassword(FTPConnection con, String username, InetAddress address) {
return true;
}

@Override
public IFileSystem authenticate(FTPConnection con, String username, String password) throws AuthException {
public IFileSystem authenticate(FTPConnection con, InetAddress address, String username, String password) throws AuthException {
// Check for a user with that username in the database
if(!userbase.containsKey(username)) {
throw new AuthException();
Expand Down

0 comments on commit a7b20ce

Please sign in to comment.