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