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 * "{@code <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 }