/* * Copyright (c) 2001, 2010, 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.*; /* * @test * @bug 4398880 * @summary FTP URL processing modified to conform to RFC 1738 */ public class FtpURL { /** * A class that simulates, on a separate, an FTP server. */ private class FtpServer extends Thread { private ServerSocket server; private int port; private boolean done = false; private boolean portEnabled = true; private boolean pasvEnabled = 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; String[] cmds = { "USER", "PASS", "CWD", "CDUP", "PWD", "TYPE", "NOOP", "RETR", "PASV", "PORT", "LIST", "REIN", "QUIT", "STOR", "NLST", "RNFR", "RNTO" }; 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) { 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 "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 PASV: if (!pasvEnabled) { out.println("500 PASV is disabled, use PORT instead."); continue; } try { if (pasv == null) pasv = new ServerSocket(0); int port = pasv.getLocalPort(); out.println("227 Entering Passive Mode (127,0,0,1," + (port >> 8) + "," + (port & 0xff) +")"); } 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.port = port; try { server = new ServerSocket(port); } catch (IOException e) { } } public FtpServer() { this(21); } public int getPort() { if (server != null) return server.getLocalPort(); return 0; } /** * 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 { FtpURL test = new FtpURL(); } public FtpURL() throws Exception { FtpServer server = new FtpServer(0); BufferedReader in = null; try { server.start(); int port = server.getPort(); // Now let's check the URL handler URL url = new URL("ftp://user:password@localhost:" + port + "/%2Fetc/motd;type=a"); URLConnection con = url.openConnection(); 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 port = server.getPort(); // Now let's check the URL handler url = new URL("ftp://user2@localhost:" + port + "/%2Fusr/bin;type=d"); con = url.openConnection(); 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()); } finally { try { in.close(); } catch (IOException unused) {} server.terminate(); server.server.close(); } } }