View Javadoc
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 }