Path: blob/master/test/jdk/sun/net/ftp/FtpURL.java
41149 views
/*1* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.*;24import java.net.*;25import jdk.test.lib.net.IPSupport;2627/*28* @test29* @bug 439888030* @summary FTP URL processing modified to conform to RFC 173831* @library /test/lib32* @run main/othervm FtpURL33* @run main/othervm -Djava.net.preferIPv4Stack=true FtpURL34* @run main/othervm -Djava.net.preferIPv6Addresses=true FtpURL35*/3637public class FtpURL {38/**39* A class that simulates, on a separate, an FTP server.40*/4142private class FtpServer extends Thread {43private final ServerSocket server;44private final int port;45private boolean done = false;46private boolean portEnabled = true;47private boolean pasvEnabled = true;48private boolean extendedEnabled = true;49private String username;50private String password;51private String cwd;52private String filename;53private String type;54private boolean list = false;5556/**57* This Inner class will handle ONE client at a time.58* That's where 99% of the protocol handling is done.59*/6061private class FtpServerHandler {62BufferedReader in;63PrintWriter out;64Socket client;65private final int ERROR = 0;66private final int USER = 1;67private final int PASS = 2;68private final int CWD = 3;69private final int CDUP = 4;70private final int PWD = 5;71private final int TYPE = 6;72private final int NOOP = 7;73private final int RETR = 8;74private final int PASV = 9;75private final int PORT = 10;76private final int LIST = 11;77private final int REIN = 12;78private final int QUIT = 13;79private final int STOR = 14;80private final int NLST = 15;81private final int RNFR = 16;82private final int RNTO = 17;83private final int EPSV = 18;84String[] cmds = { "USER", "PASS", "CWD", "CDUP", "PWD", "TYPE",85"NOOP", "RETR", "PASV", "PORT", "LIST", "REIN",86"QUIT", "STOR", "NLST", "RNFR", "RNTO", "EPSV" };87private String arg = null;88private ServerSocket pasv = null;89private int data_port = 0;90private InetAddress data_addr = null;9192/**93* Parses a line to match it with one of the supported FTP commands.94* Returns the command number.95*/9697private int parseCmd(String cmd) {98System.out.println("Received command: " + cmd);99if (cmd == null || cmd.length() < 3)100return ERROR;101int blank = cmd.indexOf(' ');102if (blank < 0)103blank = cmd.length();104if (blank < 3)105return ERROR;106String s = cmd.substring(0, blank);107if (cmd.length() > blank+1)108arg = cmd.substring(blank+1, cmd.length());109else110arg = null;111for (int i = 0; i < cmds.length; i++) {112if (s.equalsIgnoreCase(cmds[i]))113return i+1;114}115return ERROR;116}117118public FtpServerHandler(Socket cl) {119client = cl;120}121122protected boolean isPasvSet() {123if (pasv != null && !pasvEnabled) {124try {125pasv.close();126} catch (IOException ex) {127}128pasv = null;129}130if (pasvEnabled && pasv != null)131return true;132return false;133}134135/**136* Open the data socket with the client. This can be the137* result of a "EPSV", "PASV" or "PORT" command.138*/139140protected OutputStream getOutDataStream() {141try {142if (isPasvSet()) {143Socket s = pasv.accept();144return s.getOutputStream();145}146if (data_addr != null) {147Socket s = new Socket(data_addr, data_port);148data_addr = null;149data_port = 0;150return s.getOutputStream();151}152} catch (Exception e) {153e.printStackTrace();154}155return null;156}157158protected InputStream getInDataStream() {159try {160if (isPasvSet()) {161Socket s = pasv.accept();162return s.getInputStream();163}164if (data_addr != null) {165Socket s = new Socket(data_addr, data_port);166data_addr = null;167data_port = 0;168return s.getInputStream();169}170} catch (Exception e) {171e.printStackTrace();172}173return null;174}175176/**177* Handles the protocol exchange with the client.178*/179180public void run() {181boolean done = false;182String str;183int res;184boolean logged = false;185boolean waitpass = false;186187try {188in = new BufferedReader(new InputStreamReader(client.getInputStream()));189out = new PrintWriter(client.getOutputStream(), true);190out.println("220 tatooine FTP server (SunOS 5.8) ready.");191} catch (Exception ex) {192return;193}194synchronized (FtpServer.this) {195while (!done) {196try {197str = in.readLine();198res = parseCmd(str);199if ((res > PASS && res != QUIT) && !logged) {200out.println("530 Not logged in.");201continue;202}203switch (res) {204case ERROR:205out.println("500 '" + str + "': command not understood.");206break;207case USER:208if (!logged && !waitpass) {209username = str.substring(5);210password = null;211cwd = null;212if ("user2".equals(username)) {213out.println("230 Guest login ok, access restrictions apply.");214logged = true;215} else {216out.println("331 Password required for " + arg);217waitpass = true;218}219} else {220out.println("503 Bad sequence of commands.");221}222break;223case PASS:224if (!logged && waitpass) {225out.println("230 Guest login ok, access restrictions apply.");226password = str.substring(5);227logged = true;228waitpass = false;229} else230out.println("503 Bad sequence of commands.");231break;232case QUIT:233out.println("221 Goodbye.");234out.flush();235out.close();236if (pasv != null)237pasv.close();238done = true;239break;240case TYPE:241out.println("200 Type set to " + arg + ".");242type = arg;243break;244case CWD:245out.println("250 CWD command successful.");246if (cwd == null)247cwd = str.substring(4);248else249cwd = cwd + "/" + str.substring(4);250break;251case CDUP:252out.println("250 CWD command successful.");253break;254case PWD:255out.println("257 \"" + cwd + "\" is current directory");256break;257case EPSV:258if (!extendedEnabled || !pasvEnabled) {259out.println("500 EPSV is disabled, " +260"use PORT instead.");261continue;262}263if (!(server.getInetAddress() instanceof Inet6Address)) {264// pretend EPSV is not implemented265out.println("500 '" + str + "': command not understood.");266break;267}268if ("all".equalsIgnoreCase(arg)) {269out.println("200 EPSV ALL command successful.");270continue;271}272try {273if (pasv == null)274pasv = new ServerSocket(0, 0, server.getInetAddress());275int port = pasv.getLocalPort();276out.println("229 Entering Extended" +277" Passive Mode (|||" + port + "|)");278} catch (IOException ssex) {279out.println("425 Can't build data connection:" +280" Connection refused.");281}282break;283284case PASV:285if (!pasvEnabled) {286out.println("500 PASV is disabled, use PORT instead.");287continue;288}289try {290if (pasv == null) {291// Not sure how to support PASV mode over292// IPv6293pasv = new ServerSocket();294pasv.bind(new InetSocketAddress("127.0.0.1", 0));295}296int port = pasv.getLocalPort();297out.println("227 Entering Passive Mode (127,0,0,1," +298(port >> 8) + "," + (port & 0xff) +")");299} catch (IOException ssex) {300out.println("425 Can't build data connection: Connection refused.");301}302break;303case PORT:304if (!portEnabled) {305out.println("500 PORT is disabled, use PASV instead");306continue;307}308StringBuffer host;309int i=0, j=4;310while (j>0) {311i = arg.indexOf(',', i+1);312if (i < 0)313break;314j--;315}316if (j != 0) {317out.println("500 '" + arg + "': command not understood.");318continue;319}320try {321host = new StringBuffer(arg.substring(0,i));322for (j=0; j < host.length(); j++)323if (host.charAt(j) == ',')324host.setCharAt(j, '.');325String ports = arg.substring(i+1);326i = ports.indexOf(',');327data_port = Integer.parseInt(ports.substring(0,i)) << 8;328data_port += (Integer.parseInt(ports.substring(i+1)));329data_addr = InetAddress.getByName(host.toString());330out.println("200 Command okay.");331} catch (Exception ex3) {332data_port = 0;333data_addr = null;334out.println("500 '" + arg + "': command not understood.");335}336break;337case RETR:338{339filename = str.substring(5);340OutputStream dout = getOutDataStream();341if (dout != null) {342out.println("200 Command okay.");343PrintWriter pout = new PrintWriter(new BufferedOutputStream(dout));344pout.println("Hello World!");345pout.flush();346pout.close();347list = false;348} else349out.println("425 Can't build data connection: Connection refused.");350}351break;352case NLST:353filename = arg;354case LIST:355{356OutputStream dout = getOutDataStream();357if (dout != null) {358out.println("200 Command okay.");359PrintWriter pout = new PrintWriter(new BufferedOutputStream(dout));360pout.println("total 130");361pout.println("drwxrwxrwt 7 sys sys 577 May 12 03:30 .");362pout.println("drwxr-xr-x 39 root root 1024 Mar 27 12:55 ..");363pout.println("drwxrwxr-x 2 root root 176 Apr 10 12:02 .X11-pipe");364pout.println("drwxrwxr-x 2 root root 176 Apr 10 12:02 .X11-unix");365pout.println("drwxrwxrwx 2 root root 179 Mar 30 15:09 .pcmcia");366pout.println("drwxrwxrwx 2 jladen staff 117 Mar 30 18:18 .removable");367pout.println("drwxrwxrwt 2 root root 327 Mar 30 15:08 .rpc_door");368pout.println("-rw-r--r-- 1 root other 21 May 5 16:59 hello2.txt");369pout.println("-rw-rw-r-- 1 root sys 5968 Mar 30 15:08 ps_data");370pout.flush();371pout.close();372list = true;373try {374FtpServer.this.wait ();375} catch (Exception e) {}376} else377out.println("425 Can't build data connection: Connection refused.");378}379break;380case STOR:381{382InputStream is = getInDataStream();383if (is != null) {384out.println("200 Command okay.");385BufferedInputStream din = new BufferedInputStream(is);386int val;387do {388val = din.read();389} while (val != -1);390din.close();391} else392out.println("425 Can't build data connection: Connection refused.");393}394break;395}396} catch (IOException ioe) {397ioe.printStackTrace();398try {399out.close();400} catch (Exception ex2) {401}402done = true;403}404}405}406}407}408409public FtpServer(int port) {410this(InetAddress.getLoopbackAddress(), port);411}412413public FtpServer(InetAddress address, int port) {414this.port = port;415try {416if (address == null) {417server = new ServerSocket(port);418} else {419server = new ServerSocket();420server.bind(new InetSocketAddress(address, port));421}422} catch (IOException e) {423throw new UncheckedIOException(e);424}425}426427public FtpServer() {428this(null, 21);429}430431public int getPort() {432return server.getLocalPort();433}434435public String getAuthority() {436InetAddress address = server.getInetAddress();437String hostaddr = address.isAnyLocalAddress()438? "localhost" : address.getHostAddress();439if (hostaddr.indexOf(':') > -1) {440hostaddr = "[" + hostaddr +"]";441}442return hostaddr + ":" + getPort();443}444445/**446* A way to tell the server that it can stop.447*/448synchronized public void terminate() {449done = true;450}451452synchronized public void setPortEnabled(boolean ok) {453portEnabled = ok;454}455456synchronized public void setPasvEnabled(boolean ok) {457pasvEnabled = ok;458}459460String getUsername() {461return username;462}463464String getPassword() {465return password;466}467468String pwd() {469return cwd;470}471472String getFilename() {473return filename;474}475476String getType() {477return type;478}479480synchronized boolean getList() {481notify ();482return list;483}484485/*486* All we got to do here is create a ServerSocket and wait for connections.487* When a connection happens, we just have to create a thread that will488* handle it.489*/490public void run() {491try {492Socket client;493for (int i=0; i<2; i++) {494client = server.accept();495(new FtpServerHandler(client)).run();496}497} catch(Exception e) {498} finally {499try { server.close(); } catch (IOException unused) {}500}501}502}503public static void main(String[] args) throws Exception {504IPSupport.throwSkippedExceptionIfNonOperational();505FtpURL test = new FtpURL();506}507508public FtpURL() throws Exception {509FtpServer server = new FtpServer(InetAddress.getLoopbackAddress(), 0);510BufferedReader in = null;511try {512server.start();513String authority = server.getAuthority();514System.out.println("FTP server waiting for connections at: " + authority);515assert authority != null;516517// Now let's check the URL handler518519URL url = new URL("ftp://user:password@" + authority + "/%2Fetc/motd;type=a");520URLConnection con = url.openConnection(Proxy.NO_PROXY);521in = new BufferedReader(new InputStreamReader(con.getInputStream()));522String s;523do {524s = in.readLine();525} while (s != null);526if (!("user".equals(server.getUsername())))527throw new RuntimeException("Inccorect username received");528if (!("password".equals(server.getPassword())))529throw new RuntimeException("Inccorect password received");530if (!("/etc".equals(server.pwd())))531throw new RuntimeException("Inccorect directory received");532if (!("motd".equals(server.getFilename())))533throw new RuntimeException("Inccorect username received");534if (!("A".equals(server.getType())))535throw new RuntimeException("Incorrect type received");536537in.close();538// We're done!539540// Second URL test541542// Now let's check the URL handler543544url = new URL("ftp://user2@" + authority + "/%2Fusr/bin;type=d");545con = url.openConnection(Proxy.NO_PROXY);546in = new BufferedReader(new InputStreamReader(con.getInputStream()));547do {548s = in.readLine();549} while (s != null);550if (!server.getList())551throw new RuntimeException(";type=d didn't generate a NLST");552if (server.getPassword() != null)553throw new RuntimeException("password should be null!");554if (! "bin".equals(server.getFilename()))555throw new RuntimeException("Incorrect filename received");556if (! "/usr".equals(server.pwd()))557throw new RuntimeException("Incorrect pwd received");558// We're done!559560} catch (Exception e) {561throw new RuntimeException("FTP support error: " + e.getMessage(), e);562} finally {563try { in.close(); } catch (Exception unused) {}564server.terminate();565server.server.close();566}567}568}569570571