1 /*
   2  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package sun.rmi.transport.proxy;
  26 
  27 import java.io.*;
  28 import java.net.*;
  29 import java.util.Hashtable;
  30 
  31 /**
  32  * CGIClientException is thrown when an error is detected
  33  * in a client's request.
  34  */
  35 class CGIClientException extends Exception {
  36     private static final long serialVersionUID = 8147981687059865216L;
  37 
  38     public CGIClientException(String s) {
  39         super(s);
  40     }
  41 
  42     public CGIClientException(String s, Throwable cause) {
  43         super(s, cause);
  44     }
  45 }
  46 
  47 /**
  48  * CGIServerException is thrown when an error occurs here on the server.
  49  */
  50 class CGIServerException extends Exception {
  51 
  52     private static final long serialVersionUID = 6928425456704527017L;
  53 
  54     public CGIServerException(String s) {
  55         super(s);
  56     }
  57 
  58     public CGIServerException(String s, Throwable cause) {
  59         super(s, cause);
  60     }
  61 }
  62 
  63 /**
  64  * CGICommandHandler is the interface to an object that handles a
  65  * particular supported command.
  66  */
  67 interface CGICommandHandler {
  68 
  69     /**
  70      * Return the string form of the command
  71      * to be recognized in the query string.
  72      */
  73     public String getName();
  74 
  75     /**
  76      * Execute the command with the given string as parameter.
  77      */
  78     public void execute(String param) throws CGIClientException, CGIServerException;
  79 }
  80 
  81 /**
  82  * The CGIHandler class contains methods for executing as a CGI program.
  83  * The main function interprets the query string as a command of the form
  84  * "<command>=<parameters>".
  85  *
  86  * This class depends on the CGI 1.0 environment variables being set as
  87  * properties of the same name in this Java VM.
  88  *
  89  * All data and methods of this class are static because they are specific
  90  * to this particular CGI process.
  91  */
  92 public final class CGIHandler {
  93 
  94     /* get CGI parameters that we need */
  95     static int ContentLength;
  96     static String QueryString;
  97     static String RequestMethod;
  98     static String ServerName;
  99     static int ServerPort;
 100 
 101     static {
 102         java.security.AccessController.doPrivileged(
 103             new java.security.PrivilegedAction<Void>() {
 104             public Void run() {
 105                 ContentLength =
 106                     Integer.getInteger("CONTENT_LENGTH", 0).intValue();
 107                 QueryString = System.getProperty("QUERY_STRING", "");
 108                 RequestMethod = System.getProperty("REQUEST_METHOD", "");
 109                 ServerName = System.getProperty("SERVER_NAME", "");
 110                 ServerPort = Integer.getInteger("SERVER_PORT", 0).intValue();
 111                 return null;
 112             }
 113         });
 114     }
 115 
 116     /* list of handlers for supported commands */
 117     private static CGICommandHandler commands[] = {
 118         new CGIForwardCommand(),
 119         new CGIGethostnameCommand(),
 120         new CGIPingCommand(),
 121         new CGITryHostnameCommand()
 122     };
 123 
 124     /* construct table mapping command strings to handlers */
 125     private static Hashtable<String, CGICommandHandler> commandLookup;
 126     static {
 127         commandLookup = new Hashtable<>();
 128         for (int i = 0; i < commands.length; ++ i)
 129             commandLookup.put(commands[i].getName(), commands[i]);
 130     }
 131 
 132     /* prevent instantiation of this class */
 133     private CGIHandler() {}
 134 
 135     /**
 136      * Execute command given in query string on URL.  The string before
 137      * the first '=' is interpreted as the command name, and the string
 138      * after the first '=' is the parameters to the command.
 139      */
 140     public static void main(String args[])
 141     {
 142         try {
 143             String command, param;
 144             int delim = QueryString.indexOf('=');
 145             if (delim == -1) {
 146                 command = QueryString;
 147                 param = "";
 148             }
 149             else {
 150                 command = QueryString.substring(0, delim);
 151                 param = QueryString.substring(delim + 1);
 152             }
 153             CGICommandHandler handler =
 154                 commandLookup.get(command);
 155             if (handler != null)
 156                 try {
 157                     handler.execute(param);
 158                 } catch (CGIClientException e) {
 159                     e.printStackTrace();
 160                     returnClientError(e.getMessage());
 161                 } catch (CGIServerException e) {
 162                     e.printStackTrace();
 163                     returnServerError(e.getMessage());
 164                 }
 165             else
 166                 returnClientError("invalid command.");
 167         } catch (Exception e) {
 168             e.printStackTrace();
 169             returnServerError("internal error: " + e.getMessage());
 170         }
 171         System.exit(0);
 172     }
 173 
 174     /**
 175      * Return an HTML error message indicating there was error in
 176      * the client's request.
 177      */
 178     private static void returnClientError(String message)
 179     {
 180         System.out.println("Status: 400 Bad Request: " + message);
 181         System.out.println("Content-type: text/html");
 182         System.out.println("");
 183         System.out.println("<HTML>" +
 184                            "<HEAD><TITLE>Java RMI Client Error" +
 185                            "</TITLE></HEAD>" +
 186                            "<BODY>");
 187         System.out.println("<H1>Java RMI Client Error</H1>");
 188         System.out.println("");
 189         System.out.println(message);
 190         System.out.println("</BODY></HTML>");
 191         System.exit(1);
 192     }
 193 
 194     /**
 195      * Return an HTML error message indicating an error occurred
 196      * here on the server.
 197      */
 198     private static void returnServerError(String message)
 199     {
 200         System.out.println("Status: 500 Server Error: " + message);
 201         System.out.println("Content-type: text/html");
 202         System.out.println("");
 203         System.out.println("<HTML>" +
 204                            "<HEAD><TITLE>Java RMI Server Error" +
 205                            "</TITLE></HEAD>" +
 206                            "<BODY>");
 207         System.out.println("<H1>Java RMI Server Error</H1>");
 208         System.out.println("");
 209         System.out.println(message);
 210         System.out.println("</BODY></HTML>");
 211         System.exit(1);
 212     }
 213 }
 214 
 215 /**
 216  * "forward" command: Forward request body to local port on the server,
 217  * and send response back to client.
 218  */
 219 final class CGIForwardCommand implements CGICommandHandler {
 220 
 221     public String getName() {
 222         return "forward";
 223     }
 224 
 225     @SuppressWarnings("deprecation")
 226     private String getLine (DataInputStream socketIn) throws IOException {
 227         return socketIn.readLine();
 228     }
 229 
 230     public void execute(String param) throws CGIClientException, CGIServerException
 231     {
 232         if (!CGIHandler.RequestMethod.equals("POST"))
 233             throw new CGIClientException("can only forward POST requests");
 234 
 235         int port;
 236         try {
 237             port = Integer.parseInt(param);
 238         } catch (NumberFormatException e) {
 239             throw new CGIClientException("invalid port number.", e);
 240         }
 241         if (port <= 0 || port > 0xFFFF)
 242             throw new CGIClientException("invalid port: " + port);
 243         if (port < 1024)
 244             throw new CGIClientException("permission denied for port: " +
 245                                          port);
 246 
 247         byte buffer[];
 248         Socket socket;
 249         try {
 250             socket = new Socket(InetAddress.getLocalHost(), port);
 251         } catch (IOException e) {
 252             throw new CGIServerException("could not connect to local port", e);
 253         }
 254 
 255         /*
 256          * read client's request body
 257          */
 258         DataInputStream clientIn = new DataInputStream(System.in);
 259         buffer = new byte[CGIHandler.ContentLength];
 260         try {
 261             clientIn.readFully(buffer);
 262         } catch (EOFException e) {
 263             throw new CGIClientException("unexpected EOF reading request body", e);
 264         } catch (IOException e) {
 265             throw new CGIClientException("error reading request body", e);
 266         }
 267 
 268         /*
 269          * send to local server in HTTP
 270          */
 271         try {
 272             DataOutputStream socketOut =
 273                 new DataOutputStream(socket.getOutputStream());
 274             socketOut.writeBytes("POST / HTTP/1.0\r\n");
 275             socketOut.writeBytes("Content-length: " +
 276                                  CGIHandler.ContentLength + "\r\n\r\n");
 277             socketOut.write(buffer);
 278             socketOut.flush();
 279         } catch (IOException e) {
 280             throw new CGIServerException("error writing to server", e);
 281         }
 282 
 283         /*
 284          * read response
 285          */
 286         DataInputStream socketIn;
 287         try {
 288             socketIn = new DataInputStream(socket.getInputStream());
 289         } catch (IOException e) {
 290             throw new CGIServerException("error reading from server", e);
 291         }
 292         String key = "Content-length:".toLowerCase();
 293         boolean contentLengthFound = false;
 294         String line;
 295         int responseContentLength = -1;
 296         do {
 297             try {
 298                 line = getLine(socketIn);
 299             } catch (IOException e) {
 300                 throw new CGIServerException("error reading from server", e);
 301             }
 302             if (line == null)
 303                 throw new CGIServerException(
 304                     "unexpected EOF reading server response");
 305 
 306             if (line.toLowerCase().startsWith(key)) {
 307                 if (contentLengthFound) {
 308                     throw new CGIServerException(
 309                             "Multiple Content-length entries found.");
 310                 } else {
 311                     responseContentLength =
 312                         Integer.parseInt(line.substring(key.length()).trim());
 313                     contentLengthFound = true;
 314                 }
 315             }
 316         } while ((line.length() != 0) &&
 317                  (line.charAt(0) != '\r') && (line.charAt(0) != '\n'));
 318 
 319         if (!contentLengthFound || responseContentLength < 0)
 320             throw new CGIServerException(
 321                 "missing or invalid content length in server response");
 322         buffer = new byte[responseContentLength];
 323         try {
 324             socketIn.readFully(buffer);
 325         } catch (EOFException e) {
 326             throw new CGIServerException(
 327                 "unexpected EOF reading server response", e);
 328         } catch (IOException e) {
 329             throw new CGIServerException("error reading from server", e);
 330         }
 331 
 332         /*
 333          * send response back to client
 334          */
 335         System.out.println("Status: 200 OK");
 336         System.out.println("Content-type: application/octet-stream");
 337         System.out.println("");
 338         try {
 339             System.out.write(buffer);
 340         } catch (IOException e) {
 341             throw new CGIServerException("error writing response", e);
 342         }
 343         System.out.flush();
 344     }
 345 }
 346 
 347 /**
 348  * "gethostname" command: Return the host name of the server as the
 349  * response body
 350  */
 351 final class CGIGethostnameCommand implements CGICommandHandler {
 352 
 353     public String getName() {
 354         return "gethostname";
 355     }
 356 
 357     public void execute(String param)
 358     {
 359         System.out.println("Status: 200 OK");
 360         System.out.println("Content-type: application/octet-stream");
 361         System.out.println("Content-length: " +
 362                            CGIHandler.ServerName.length());
 363         System.out.println("");
 364         System.out.print(CGIHandler.ServerName);
 365         System.out.flush();
 366     }
 367 }
 368 
 369 /**
 370  * "ping" command: Return an OK status to indicate that connection
 371  * was successful.
 372  */
 373 final class CGIPingCommand implements CGICommandHandler {
 374 
 375     public String getName() {
 376         return "ping";
 377     }
 378 
 379     public void execute(String param)
 380     {
 381         System.out.println("Status: 200 OK");
 382         System.out.println("Content-type: application/octet-stream");
 383         System.out.println("Content-length: 0");
 384         System.out.println("");
 385     }
 386 }
 387 
 388 /**
 389  * "tryhostname" command: Return a human readable message describing
 390  * what host name is available to local Java VMs.
 391  */
 392 final class CGITryHostnameCommand implements CGICommandHandler {
 393 
 394     public String getName() {
 395         return "tryhostname";
 396     }
 397 
 398     public void execute(String param)
 399     {
 400         System.out.println("Status: 200 OK");
 401         System.out.println("Content-type: text/html");
 402         System.out.println("");
 403         System.out.println("<HTML>" +
 404                            "<HEAD><TITLE>Java RMI Server Hostname Info" +
 405                            "</TITLE></HEAD>" +
 406                            "<BODY>");
 407         System.out.println("<H1>Java RMI Server Hostname Info</H1>");
 408         System.out.println("<H2>Local host name available to Java VM:</H2>");
 409         System.out.print("<P>InetAddress.getLocalHost().getHostName()");
 410         try {
 411             String localHostName = InetAddress.getLocalHost().getHostName();
 412 
 413             System.out.println(" = " + localHostName);
 414         } catch (UnknownHostException e) {
 415             System.out.println(" threw java.net.UnknownHostException");
 416         }
 417 
 418         System.out.println("<H2>Server host information obtained through CGI interface from HTTP server:</H2>");
 419         System.out.println("<P>SERVER_NAME = " + CGIHandler.ServerName);
 420         System.out.println("<P>SERVER_PORT = " + CGIHandler.ServerPort);
 421         System.out.println("</BODY></HTML>");
 422     }
 423 }