/*
* Copyright ( c ) 2001 , 2019 , Oracle and / or its affiliates . All rights reserved .
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER .
*
* This code is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 only , as
* published by the Free Software Foundation .
*
* This code 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
* version 2 for more details ( a copy is included in the LICENSE file that
* accompanied this code ) .
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work ; if not , write to the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* Please contact Oracle , 500 Oracle Parkway , Redwood Shores , CA 94065 USA
* or visit www . oracle . com if you need additional information or have any
* questions .
*/
import java.io.*;
import java.net.*;
import jdk.test.lib.net.IPSupport;
/*
* @ test
* @ bug 4398880
* @ summary FTP URL processing modified to conform to RFC 1738
* @ library / test / lib
* @ run main / othervm FtpURL
* @ run main / othervm - Djava . net . preferIPv4Stack = true FtpURL
* @ run main / othervm - Djava . net . preferIPv6Addresses = true FtpURL
*/
public class FtpURL {
/**
* A class that simulates , on a separate , an FTP server .
*/
private class FtpServer extends Thread {
private final ServerSocket server;
private final int port;
private boolean done = false ;
private boolean portEnabled = true ;
private boolean pasvEnabled = true ;
private boolean extendedEnabled = true ;
private String username;
private String password;
private String cwd;
private String filename;
private String type;
private boolean list = false ;
/**
* This Inner class will handle ONE client at a time .
* That ' s where 99 % of the protocol handling is done .
*/
private class FtpServerHandler {
BufferedReader in;
PrintWriter out;
Socket client;
private final int ERROR = 0 ;
private final int USER = 1 ;
private final int PASS = 2 ;
private final int CWD = 3 ;
private final int CDUP = 4 ;
private final int PWD = 5 ;
private final int TYPE = 6 ;
private final int NOOP = 7 ;
private final int RETR = 8 ;
private final int PASV = 9 ;
private final int PORT = 10 ;
private final int LIST = 11 ;
private final int REIN = 12 ;
private final int QUIT = 13 ;
private final int STOR = 14 ;
private final int NLST = 15 ;
private final int RNFR = 16 ;
private final int RNTO = 17 ;
private final int EPSV = 18 ;
String[] cmds = { "USER" , "PASS" , "CWD" , "CDUP" , "PWD" , "TYPE" ,
"NOOP" , "RETR" , "PASV" , "PORT" , "LIST" , "REIN" ,
"QUIT" , "STOR" , "NLST" , "RNFR" , "RNTO" , "EPSV" };
private String arg = null ;
private ServerSocket pasv = null ;
private int data_port = 0 ;
private InetAddress data_addr = null ;
/**
* Parses a line to match it with one of the supported FTP commands .
* Returns the command number .
*/
private int parseCmd(String cmd) {
System.out.println("Received command: " + cmd);
if (cmd == null || cmd.length() < 3 )
return ERROR;
int blank = cmd.indexOf(' ' );
if (blank < 0 )
blank = cmd.length();
if (blank < 3 )
return ERROR;
String s = cmd.substring(0 , blank);
if (cmd.length() > blank+1 )
arg = cmd.substring(blank+1 , cmd.length());
else
arg = null ;
for (int i = 0 ; i < cmds.length; i++) {
if (s.equalsIgnoreCase(cmds[i]))
return i+1 ;
}
return ERROR;
}
public FtpServerHandler(Socket cl) {
client = cl;
}
protected boolean isPasvSet() {
if (pasv != null && !pasvEnabled) {
try {
pasv.close();
} catch (IOException ex) {
}
pasv = null ;
}
if (pasvEnabled && pasv != null )
return true ;
return false ;
}
/**
* Open the data socket with the client . This can be the
* result of a " EPSV " , " PASV " or " PORT " command .
*/
protected OutputStream getOutDataStream() {
try {
if (isPasvSet()) {
Socket s = pasv.accept();
return s.getOutputStream();
}
if (data_addr != null ) {
Socket s = new Socket(data_addr, data_port);
data_addr = null ;
data_port = 0 ;
return s.getOutputStream();
}
} catch (Exception e) {
e.printStackTrace();
}
return null ;
}
protected InputStream getInDataStream() {
try {
if (isPasvSet()) {
Socket s = pasv.accept();
return s.getInputStream();
}
if (data_addr != null ) {
Socket s = new Socket(data_addr, data_port);
data_addr = null ;
data_port = 0 ;
return s.getInputStream();
}
} catch (Exception e) {
e.printStackTrace();
}
return null ;
}
/**
* Handles the protocol exchange with the client .
*/
public void run() {
boolean done = false ;
String str;
int res;
boolean logged = false ;
boolean waitpass = false ;
try {
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
out = new PrintWriter(client.getOutputStream(), true );
out.println("220 tatooine FTP server (SunOS 5.8) ready." );
} catch (Exception ex) {
return ;
}
synchronized (FtpServer.this ) {
while (!done) {
try {
str = in.readLine();
res = parseCmd(str);
if ((res > PASS && res != QUIT) && !logged) {
out.println("530 Not logged in." );
continue ;
}
switch (res) {
case ERROR:
out.println("500 '" + str + "': command not understood." );
break ;
case USER:
if (!logged && !waitpass) {
username = str.substring(5 );
password = null ;
cwd = null ;
if ("user2" .equals(username)) {
out.println("230 Guest login ok, access restrictions apply." );
logged = true ;
} else {
out.println("331 Password required for " + arg);
waitpass = true ;
}
} else {
out.println("503 Bad sequence of commands." );
}
break ;
case PASS:
if (!logged && waitpass) {
out.println("230 Guest login ok, access restrictions apply." );
password = str.substring(5 );
logged = true ;
waitpass = false ;
} else
out.println("503 Bad sequence of commands." );
break ;
case QUIT:
out.println("221 Goodbye." );
out.flush();
out.close();
if (pasv != null )
pasv.close();
done = true ;
break ;
case TYPE:
out.println("200 Type set to " + arg + "." );
type = arg;
break ;
case CWD:
out.println("250 CWD command successful." );
if (cwd == null )
cwd = str.substring(4 );
else
cwd = cwd + "/" + str.substring(4 );
break ;
case CDUP:
out.println("250 CWD command successful." );
break ;
case PWD:
out.println("257 \" " + cwd + " \" is current directory" );
break ;
case EPSV:
if (!extendedEnabled || !pasvEnabled) {
out.println("500 EPSV is disabled, " +
"use PORT instead." );
continue ;
}
if (!(server.getInetAddress() instanceof Inet6Address)) {
// pretend EPSV is not implemented
out.println("500 '" + str + "': command not understood." );
break ;
}
if ("all" .equalsIgnoreCase(arg)) {
out.println("200 EPSV ALL command successful." );
continue ;
}
try {
if (pasv == null )
pasv = new ServerSocket(0 , 0 , server.getInetAddress());
int port = pasv.getLocalPort();
out.println("229 Entering Extended" +
" Passive Mode (|||" + port + "|)" );
} catch (IOException ssex) {
out.println("425 Can't build data connection:" +
" Connection refused." );
}
break ;
case PASV:
if (!pasvEnabled) {
out.println("500 PASV is disabled, use PORT instead." );
continue ;
}
try {
if (pasv == null ) {
// Not sure how to support PASV mode over
// IPv6
pasv = new ServerSocket();
pasv.bind(new InetSocketAddress("127.0.0.1" , 0 ));
}
int port = pasv.getLocalPort();
out.println("227 Entering Passive Mode (127,0,0,1," +
(port >> 8 ) + "," + (port & 0 xff) +")" );
} catch (IOException ssex) {
out.println("425 Can't build data connection: Connection refused." );
}
break ;
case PORT:
if (!portEnabled) {
out.println("500 PORT is disabled, use PASV instead" );
continue ;
}
StringBuffer host;
int i=0 , j=4 ;
while (j>0 ) {
i = arg.indexOf(',' , i+1 );
if (i < 0 )
break ;
j--;
}
if (j != 0 ) {
out.println("500 '" + arg + "': command not understood." );
continue ;
}
try {
host = new StringBuffer(arg.substring(0 ,i));
for (j=0 ; j < host.length(); j++)
if (host.charAt(j) == ',' )
host.setCharAt(j, '.' );
String ports = arg.substring(i+1 );
i = ports.indexOf(',' );
data_port = Integer.parseInt(ports.substring(0 ,i)) << 8 ;
data_port += (Integer.parseInt(ports.substring(i+1 )));
data_addr = InetAddress.getByName(host.toString());
out.println("200 Command okay." );
} catch (Exception ex3) {
data_port = 0 ;
data_addr = null ;
out.println("500 '" + arg + "': command not understood." );
}
break ;
case RETR:
{
filename = str.substring(5 );
OutputStream dout = getOutDataStream();
if (dout != null ) {
out.println("200 Command okay." );
PrintWriter pout = new PrintWriter(new BufferedOutputStream(dout));
pout.println("Hello World!" );
pout.flush();
pout.close();
list = false ;
} else
out.println("425 Can't build data connection: Connection refused." );
}
break ;
case NLST:
filename = arg;
case LIST:
{
OutputStream dout = getOutDataStream();
if (dout != null ) {
out.println("200 Command okay." );
PrintWriter pout = new PrintWriter(new BufferedOutputStream(dout));
pout.println("total 130" );
pout.println("drwxrwxrwt 7 sys sys 577 May 12 03:30 ." );
pout.println("drwxr-xr-x 39 root root 1024 Mar 27 12:55 .." );
pout.println("drwxrwxr-x 2 root root 176 Apr 10 12:02 .X11-pipe" );
pout.println("drwxrwxr-x 2 root root 176 Apr 10 12:02 .X11-unix" );
pout.println("drwxrwxrwx 2 root root 179 Mar 30 15:09 .pcmcia" );
pout.println("drwxrwxrwx 2 jladen staff 117 Mar 30 18:18 .removable" );
pout.println("drwxrwxrwt 2 root root 327 Mar 30 15:08 .rpc_door" );
pout.println("-rw-r--r-- 1 root other 21 May 5 16:59 hello2.txt" );
pout.println("-rw-rw-r-- 1 root sys 5968 Mar 30 15:08 ps_data" );
pout.flush();
pout.close();
list = true ;
try {
FtpServer.this .wait ();
} catch (Exception e) {}
} else
out.println("425 Can't build data connection: Connection refused." );
}
break ;
case STOR:
{
InputStream is = getInDataStream();
if (is != null ) {
out.println("200 Command okay." );
BufferedInputStream din = new BufferedInputStream(is);
int val;
do {
val = din.read();
} while (val != -1 );
din.close();
} else
out.println("425 Can't build data connection: Connection refused." );
}
break ;
}
} catch (IOException ioe) {
ioe.printStackTrace();
try {
out.close();
} catch (Exception ex2) {
}
done = true ;
}
}
}
}
}
public FtpServer(int port) {
this (InetAddress.getLoopbackAddress(), port);
}
public FtpServer(InetAddress address, int port) {
this .port = port;
try {
if (address == null ) {
server = new ServerSocket(port);
} else {
server = new ServerSocket();
server.bind(new InetSocketAddress(address, port));
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public FtpServer() {
this (null , 21 );
}
public int getPort() {
return server.getLocalPort();
}
public String getAuthority() {
InetAddress address = server.getInetAddress();
String hostaddr = address.isAnyLocalAddress()
? "localhost" : address.getHostAddress();
if (hostaddr.indexOf(':' ) > -1 ) {
hostaddr = "[" + hostaddr +"]" ;
}
return hostaddr + ":" + getPort();
}
/**
* A way to tell the server that it can stop .
*/
synchronized public void terminate() {
done = true ;
}
synchronized public void setPortEnabled(boolean ok) {
portEnabled = ok;
}
synchronized public void setPasvEnabled(boolean ok) {
pasvEnabled = ok;
}
String getUsername() {
return username;
}
String getPassword() {
return password;
}
String pwd() {
return cwd;
}
String getFilename() {
return filename;
}
String getType() {
return type;
}
synchronized boolean getList() {
notify ();
return list;
}
/*
* All we got to do here is create a ServerSocket and wait for connections .
* When a connection happens , we just have to create a thread that will
* handle it .
*/
public void run() {
try {
Socket client;
for (int i=0 ; i<2 ; i++) {
client = server.accept();
(new FtpServerHandler(client)).run();
}
} catch (Exception e) {
} finally {
try { server.close(); } catch (IOException unused) {}
}
}
}
public static void main(String[] args) throws Exception {
IPSupport.throwSkippedExceptionIfNonOperational();
FtpURL test = new FtpURL();
}
public FtpURL() throws Exception {
FtpServer server = new FtpServer(InetAddress.getLoopbackAddress(), 0 );
BufferedReader in = null ;
try {
server.start();
String authority = server.getAuthority();
System.out.println("FTP server waiting for connections at: " + authority);
assert authority != null ;
// Now let's check the URL handler
URL url = new URL("ftp://user:password@" + authority + "/%2Fetc/motd;type=a");
URLConnection con = url.openConnection(Proxy.NO_PROXY);
in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String s;
do {
s = in.readLine();
} while (s != null );
if (!("user" .equals(server.getUsername())))
throw new RuntimeException("Inccorect username received" );
if (!("password" .equals(server.getPassword())))
throw new RuntimeException("Inccorect password received" );
if (!("/etc" .equals(server.pwd())))
throw new RuntimeException("Inccorect directory received" );
if (!("motd" .equals(server.getFilename())))
throw new RuntimeException("Inccorect username received" );
if (!("A" .equals(server.getType())))
throw new RuntimeException("Incorrect type received" );
in.close();
// We're done!
// Second URL test
// Now let's check the URL handler
url = new URL("ftp://user2@" + authority + "/%2Fusr/bin;type=d");
con = url.openConnection(Proxy.NO_PROXY);
in = new BufferedReader(new InputStreamReader(con.getInputStream()));
do {
s = in.readLine();
} while (s != null );
if (!server.getList())
throw new RuntimeException(";type=d didn't generate a NLST" );
if (server.getPassword() != null )
throw new RuntimeException("password should be null!" );
if (! "bin" .equals(server.getFilename()))
throw new RuntimeException("Incorrect filename received" );
if (! "/usr" .equals(server.pwd()))
throw new RuntimeException("Incorrect pwd received" );
// We're done!
} catch (Exception e) {
throw new RuntimeException("FTP support error: " + e.getMessage(), e);
} finally {
try { in.close(); } catch (Exception unused) {}
server.terminate();
server.server.close();
}
}
}
Messung V0.5 in Prozent C=97 H=86 G=91
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-06-09)
¤
*© Formatika GbR, Deutschland