1 /*
   2  * Copyright (c) 1996, 2008, 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 
  43 /**
  44  * CGIServerException is thrown when an error occurs here on the server.
  45  */
  46 class CGIServerException extends Exception {
  47 
  48     private static final long serialVersionUID = 6928425456704527017L;
  49 
  50     public CGIServerException(String s) {
  51         super(s);
  52     }
  53 }
  54 
  55 /**
  56  * CGICommandHandler is the interface to an object that handles a
  57  * particular supported command.
  58  */
  59 interface CGICommandHandler {
  60 
  61     /**
  62      * Return the string form of the command
  63      * to be recognized in the query string.
  64      */
  65     public String getName();
  66 
  67     /**
  68      * Execute the command with the given string as parameter.
  69      */
  70     public void execute(String param) throws CGIClientException, CGIServerException;
  71 }
  72 
  73 /**
  74  * The CGIHandler class contains methods for executing as a CGI program.
  75  * The main function interprets the query string as a command of the form
  76  * "<command>=<parameters>".
  77  *
  78  * This class depends on the CGI 1.0 environment variables being set as
  79  * properties of the same name in this Java VM.
  80  *
  81  * All data and methods of this class are static because they are specific
  82  * to this particular CGI process.
  83  */
  84 public final class CGIHandler {
  85 
  86     /* get CGI parameters that we need */
  87     static int ContentLength;
  88     static String QueryString;
  89     static String RequestMethod;
  90     static String ServerName;
  91     static int ServerPort;
  92 
  93     static {
  94         java.security.AccessController.doPrivileged(
  95             new java.security.PrivilegedAction<Void>() {
  96             public Void run() {
  97                 ContentLength =
  98                     Integer.getInteger("CONTENT_LENGTH", 0).intValue();
  99                 QueryString = System.getProperty("QUERY_STRING", "");
 100                 RequestMethod = System.getProperty("REQUEST_METHOD", "");
 101                 ServerName = System.getProperty("SERVER_NAME", "");
 102                 ServerPort = Integer.getInteger("SERVER_PORT", 0).intValue();
 103                 return null;
 104             }
 105         });
 106     }
 107 
 108     /* list of handlers for supported commands */
 109     private static CGICommandHandler commands[] = {
 110         new CGIForwardCommand(),
 111         new CGIGethostnameCommand(),
 112         new CGIPingCommand(),
 113         new CGITryHostnameCommand()
 114     };
 115 
 116     /* construct table mapping command strings to handlers */
 117     private static Hashtable<String, CGICommandHandler> commandLookup;
 118     static {
 119         commandLookup = new Hashtable<>();
 120         for (int i = 0; i < commands.length; ++ i)
 121             commandLookup.put(commands[i].getName(), commands[i]);
 122     }
 123 
 124     /* prevent instantiation of this class */
 125     private CGIHandler() {}
 126 
 127     /**
 128      * Execute command given in query string on URL.  The string before
 129      * the first '=' is interpreted as the command name, and the string
 130      * after the first '=' is the parameters to the command.
 131      */
 132     public static void main(String args[])
 133     {
 134         try {
 135             String command, param;
 136             int delim = QueryString.indexOf("=");
 137             if (delim == -1) {
 138                 command = QueryString;
 139                 param = "";
 140             }
 141             else {
 142                 command = QueryString.substring(0, delim);
 143                 param = QueryString.substring(delim + 1);
 144             }
 145             CGICommandHandler handler =
 146                 commandLookup.get(command);
 147             if (handler != null)
 148                 try {
 149                     handler.execute(param);
 150                 } catch (CGIClientException e) {
 151                     returnClientError(e.getMessage());
 152                 } catch (CGIServerException e) {
 153                     returnServerError(e.getMessage());
 154                 }
 155             else
 156                 returnClientError("invalid command: " + command);
 157         } catch (Exception e) {
 158             returnServerError("internal error: " + e.getMessage());
 159         }
 160         System.exit(0);
 161     }
 162 
 163     /**
 164      * Return an HTML error message indicating there was error in
 165      * the client's request.
 166      */
 167     private static void returnClientError(String message)
 168     {
 169         System.out.println("Status: 400 Bad Request: " + message);
 170         System.out.println("Content-type: text/html");
 171         System.out.println("");
 172         System.out.println("<HTML>" +
 173                            "<HEAD><TITLE>Java RMI Client Error" +
 174                            "</TITLE></HEAD>" +
 175                            "<BODY>");
 176         System.out.println("<H1>Java RMI Client Error</H1>");
 177         System.out.println("");
 178         System.out.println(message);
 179         System.out.println("</BODY></HTML>");
 180         System.exit(1);
 181     }
 182 
 183     /**
 184      * Return an HTML error message indicating an error occurred
 185      * here on the server.
 186      */
 187     private static void returnServerError(String message)
 188     {
 189         System.out.println("Status: 500 Server Error: " + message);
 190         System.out.println("Content-type: text/html");
 191         System.out.println("");
 192         System.out.println("<HTML>" +
 193                            "<HEAD><TITLE>Java RMI Server Error" +
 194                            "</TITLE></HEAD>" +
 195                            "<BODY>");
 196         System.out.println("<H1>Java RMI Server Error</H1>");
 197         System.out.println("");
 198         System.out.println(message);
 199         System.out.println("</BODY></HTML>");
 200         System.exit(1);
 201     }
 202 }
 203 
 204 /**
 205  * "forward" command: Forward request body to local port on the server,
 206  * and send response back to client.
 207  */
 208 final class CGIForwardCommand implements CGICommandHandler {
 209 
 210     public String getName() {
 211         return "forward";
 212     }
 213     
 214     @SuppressWarnings("deprecation")
 215     private String getLine (DataInputStream socketIn) throws IOException {
 216         return socketIn.readLine();
 217     }
 218 
 219     public void execute(String param) throws CGIClientException, CGIServerException
 220     {
 221         if (!CGIHandler.RequestMethod.equals("POST"))
 222             throw new CGIClientException("can only forward POST requests");
 223 
 224         int port;
 225         try {
 226             port = Integer.parseInt(param);
 227         } catch (NumberFormatException e) {
 228             throw new CGIClientException("invalid port number: " + param);
 229         }
 230         if (port <= 0 || port > 0xFFFF)
 231             throw new CGIClientException("invalid port: " + port);
 232         if (port < 1024)
 233             throw new CGIClientException("permission denied for port: " +
 234                                          port);
 235 
 236         byte buffer[];
 237         Socket socket;
 238         try {
 239             socket = new Socket(InetAddress.getLocalHost(), port);
 240         } catch (IOException e) {
 241             throw new CGIServerException("could not connect to local port");
 242         }
 243 
 244         /*
 245          * read client's request body
 246          */
 247         DataInputStream clientIn = new DataInputStream(System.in);
 248         buffer = new byte[CGIHandler.ContentLength];
 249         try {
 250             clientIn.readFully(buffer);
 251         } catch (EOFException e) {
 252             throw new CGIClientException("unexpected EOF reading request body");
 253         } catch (IOException e) {
 254             throw new CGIClientException("error reading request body");
 255         }
 256 
 257         /*
 258          * send to local server in HTTP
 259          */
 260         try {
 261             DataOutputStream socketOut =
 262                 new DataOutputStream(socket.getOutputStream());
 263             socketOut.writeBytes("POST / HTTP/1.0\r\n");
 264             socketOut.writeBytes("Content-length: " +
 265                                  CGIHandler.ContentLength + "\r\n\r\n");
 266             socketOut.write(buffer);
 267             socketOut.flush();
 268         } catch (IOException e) {
 269             throw new CGIServerException("error writing to server");
 270         }
 271 
 272         /*
 273          * read response
 274          */
 275         DataInputStream socketIn;
 276         try {
 277             socketIn = new DataInputStream(socket.getInputStream());
 278         } catch (IOException e) {
 279             throw new CGIServerException("error reading from server");
 280         }
 281         String key = "Content-length:".toLowerCase();
 282         boolean contentLengthFound = false;
 283         String line;
 284         int responseContentLength = -1;
 285         do {
 286             try {
 287                 line = getLine(socketIn);
 288             } catch (IOException e) {
 289                 throw new CGIServerException("error reading from server");
 290             }
 291             if (line == null)
 292                 throw new CGIServerException(
 293                     "unexpected EOF reading server response");
 294 
 295             if (line.toLowerCase().startsWith(key)) {
 296                 if (contentLengthFound){
 297                     // what would we want to do in this case??
 298                 }
 299                 responseContentLength =
 300                     Integer.parseInt(line.substring(key.length()).trim());
 301                 contentLengthFound = true;
 302             }
 303         } while ((line.length() != 0) &&
 304                  (line.charAt(0) != '\r') && (line.charAt(0) != '\n'));
 305 
 306         if (!contentLengthFound || responseContentLength < 0)
 307             throw new CGIServerException(
 308                 "missing or invalid content length in server response");
 309         buffer = new byte[responseContentLength];
 310         try {
 311             socketIn.readFully(buffer);
 312         } catch (EOFException e) {
 313             throw new CGIServerException(
 314                 "unexpected EOF reading server response");
 315         } catch (IOException e) {
 316             throw new CGIServerException("error reading from server");
 317         }
 318 
 319         /*
 320          * send response back to client
 321          */
 322         System.out.println("Status: 200 OK");
 323         System.out.println("Content-type: application/octet-stream");
 324         System.out.println("");
 325         try {
 326             System.out.write(buffer);
 327         } catch (IOException e) {
 328             throw new CGIServerException("error writing response");
 329         }
 330         System.out.flush();
 331     }
 332 }
 333 
 334 /**
 335  * "gethostname" command: Return the host name of the server as the
 336  * response body
 337  */
 338 final class CGIGethostnameCommand implements CGICommandHandler {
 339 
 340     public String getName() {
 341         return "gethostname";
 342     }
 343 
 344     public void execute(String param)
 345     {
 346         System.out.println("Status: 200 OK");
 347         System.out.println("Content-type: application/octet-stream");
 348         System.out.println("Content-length: " +
 349                            CGIHandler.ServerName.length());
 350         System.out.println("");
 351         System.out.print(CGIHandler.ServerName);
 352         System.out.flush();
 353     }
 354 }
 355 
 356 /**
 357  * "ping" command: Return an OK status to indicate that connection
 358  * was successful.
 359  */
 360 final class CGIPingCommand implements CGICommandHandler {
 361 
 362     public String getName() {
 363         return "ping";
 364     }
 365 
 366     public void execute(String param)
 367     {
 368         System.out.println("Status: 200 OK");
 369         System.out.println("Content-type: application/octet-stream");
 370         System.out.println("Content-length: 0");
 371         System.out.println("");
 372     }
 373 }
 374 
 375 /**
 376  * "tryhostname" command: Return a human readable message describing
 377  * what host name is available to local Java VMs.
 378  */
 379 final class CGITryHostnameCommand implements CGICommandHandler {
 380 
 381     public String getName() {
 382         return "tryhostname";
 383     }
 384 
 385     public void execute(String param)
 386     {
 387         System.out.println("Status: 200 OK");
 388         System.out.println("Content-type: text/html");
 389         System.out.println("");
 390         System.out.println("<HTML>" +
 391                            "<HEAD><TITLE>Java RMI Server Hostname Info" +
 392                            "</TITLE></HEAD>" +
 393                            "<BODY>");
 394         System.out.println("<H1>Java RMI Server Hostname Info</H1>");
 395         System.out.println("<H2>Local host name available to Java VM:</H2>");
 396         System.out.print("<P>InetAddress.getLocalHost().getHostName()");
 397         try {
 398             String localHostName = InetAddress.getLocalHost().getHostName();
 399 
 400             System.out.println(" = " + localHostName);
 401         } catch (UnknownHostException e) {
 402             System.out.println(" threw java.net.UnknownHostException");
 403         }
 404 
 405         System.out.println("<H2>Server host information obtained through CGI interface from HTTP server:</H2>");
 406         System.out.println("<P>SERVER_NAME = " + CGIHandler.ServerName);
 407         System.out.println("<P>SERVER_PORT = " + CGIHandler.ServerPort);
 408         System.out.println("</BODY></HTML>");
 409     }
 410 }