// // example of java.net.* classes. // // adapted from example in ch. 11 of _Exploring Java_, Niemeyer & Peck. // // version 2: security manager that disallows access to any files // outside server's directory. program responds to requests by // sending back the whole file this time. // (security manager also prints messages to standard output whenever // one of its functions is invoked.) // import java.net.* ; import java.io.* ; import java.util.* ; // // ---- main ----------------------------------------------------------- // // command-line argument is port number. // // instructions for testing: // // (1) start the TinyHttpd2 server: // java TinyHttpd2 nnnn // (nnnn is any port number you choose, higher than 1024) // suppose mymachine.org is the name of the machine on which // you've done this, and nnnn = 1234. // // (2) point a Web browser at it, via URLs like: // http://mymachine.org:1234/filename // // filename will be interpreted relative to the directory *in // which you started TinyHttpd2*. to specify an // absolute pathname, include an extra "/", e.g., // http://mymachine.org:1234//toplevel/midlevel/filename. // // if you don't know the name of the machine on which you're // running, you can use its IP address instead. for // a Windows machine, you may be able to get its IP address // by typing "winipcfg" at a DOS prompt. // // note that this version returns file contents, so if you // "serve" an HTML file, it will display in the Web // browser in the usual way. // // when done, stop the server with control-C. // // these instructions have been tested on Unix and Windows systems at // UF, and on Linux systems at Trinity. // public class TinyHttpd2 { public static void main(String[] args) throws IOException { if (args.length < 1) { System.out.println("Argument is portnum") ; System.exit(-1) ; } // install security manager. System.setSecurityManager( new TinyHttpd2SecurityManager()) ; // create server socket to accept client requests. ServerSocket ss = new ServerSocket(Integer.parseInt(args[0])) ; // accept client requests, creating a new thread for // each. while (true) { new TinyHttpd2Connection(ss.accept()).start() ; } } } // // a TinyHttpd2Connection object is a thread that serves a // client request (of the form GET /filename .. options ..). // "filename" (without the leading slash) is assumed to be a file // in the server's directory. // it sends back to the client: // error message for badly formed request, file not found, or // file outside current directory (access not permitted). // file contents otherwise. // class TinyHttpd2Connection extends Thread { Socket client ; OutputStream out ; PrintWriter outPW ; static final int BUFFSZ = 1000 ; TinyHttpd2Connection(Socket client) throws SocketException { this.client = client ; setPriority(NORM_PRIORITY - 1) ; } public void run() { try { // create in, out streams using standard // encoding for HTTP. BufferedReader inBR = new BufferedReader( new InputStreamReader( client.getInputStream(), "8859_1") ) ; out = client.getOutputStream() ; outPW = new PrintWriter( new OutputStreamWriter(out, "8859_1"), true) ; // read and echo request. String request = inBR.readLine() ; System.out.println("Request: " + request) ; // parse request. StringTokenizer st = new StringTokenizer(request) ; if ((st.countTokens() >= 2) && st.nextToken().equals("GET") ) { serviceRequest(st.nextToken()) ; } else { outPW.println("400 Bad Request") ; } client.close() ; } catch (IOException e) { System.out.println("I/O error " + e) ; } } // processes request (filename). private void serviceRequest(String filereq) throws IOException { // strip off leading "/". if (filereq.startsWith("/")) filereq = filereq.substring(1) ; // append "index.html" if name ends with "/". if (filereq.endsWith("/") || filereq.equals("")) filereq = filereq + "index.html" ; // try to open file -- verifies existence and, via // security manager, access permission. try { FileInputStream f = new FileInputStream(filereq) ; // if opened successfully, serve contents. byte[] buffer = new byte[BUFFSZ] ; int count ; while ((count = f.read(buffer)) > 0) { out.write(buffer, 0, count) ; } out.flush() ; } catch (FileNotFoundException e) { outPW.println("404 File Not Found") ; } catch (SecurityException e) { outPW.println("403 Forbidden") ; } } } // // security manager for our server program. here we define which of // the actions governed by the security manager we *do* want to // permit, by overriding methods of SecurityManager (which throw // a SecurityException by default). // class TinyHttpd2SecurityManager extends SecurityManager { // allow things we need for the server to do (printing trace // messages just so we see what's being called). public void checkAccept(String host, int port) { System.out.println("checkAccept: " + "okayed for host = " + host + ", port = " + port) ; } public void checkAccess(Thread g) { System.out.println("checkAccess: " + "okayed for thread = " + g) ; } public void checkAccess(ThreadGroup g) { System.out.println("checkAccess: " + "okayed for thread group = " + g) ; } public void checkLink(String lib) { System.out.println("checkLink: " + "okayed for lib = " + lib) ; } public void checkListen(int port) { System.out.println("checkListen: " + "okayed for port = " + port) ; } public void checkPropertyAccess(String key) { System.out.println("checkPropertyAccess: " + "okayed for key = " + key) ; } public void checkPropertiesAccess() { System.out.println("checkPropertiesAccess: " + "okayed") ; } public void checkRead(FileDescriptor fd) { System.out.println("checkRead: " + "okayed for fd = " + fd) ; } public void checkWrite(FileDescriptor fd) { System.out.println("checkWrite: " + "okayed for fd = " + fd) ; } // check file-read requests one by one: allow only if // file appears to be in server's directory. public void checkRead(String filename) { if (new File(filename).isAbsolute() || filename.indexOf("..") != -1) { System.out.println("checkRead: " + "denied for filename = " + filename) ; throw new SecurityException( "Access to file " + filename + " denied.") ; } else System.out.println("checkRead: " + "okayed for filename = " + filename) ; } }