Book Home Java Servlet Programming Search this book

4.2. The Server

A servlet can find out much about the server in which it is executing. It can learn the hostname, listening port, and server software, among other things. A servlet can display this information to a client, use it to customize its behavior based on a particular server package, or even use it to explicitly restrict the machines on which the servlet will run.

4.2.1. Getting Information About the Server

There are four methods that a servlet can use to learn about its server: two that are called using the ServletRequest object passed to the servlet and two that are called from the ServletContext object in which the servlet is executing. A servlet can get the name of the server and the port number for a particular request with getServerName() and getServerPort(), respectively:

public String ServletRequest.getServerName()
public int ServletRequest.getServerPort()

These methods are attributes of ServletRequest because the values can change for different requests if the server has more than one name (a technique called virtual hosting). The returned name might be something like "www.servlets.com" while the returned port might be something like "8080".

The getServerInfo() and getAttribute() methods of ServletContext provide information about the server software and its attributes:

public String ServletContext.getServerInfo()
public Object ServletContext.getAttribute(String name)

getServerInfo() returns the name and version of the server software, separated by a slash. The string returned might be something like "JavaWebServer/1.1.1". getAttribute() returns the value of the named server attribute as an Object or null if the attribute does not exist. The attributes are server-dependent. You can think of this method as a back door through which a servlet can get extra information about its server. Attribute names should follow the same convention as package names. The package names java.* and javax.* are reserved for use by the Java Software division of Sun Microsystems (formerly known as JavaSoft), and com.sun.* is reserved for use by Sun Microsystems. See your server's documentation for a list of its attributes. Because these methods are attributes of ServletContext in which the servlet is executing, you have to call them through that object:

String serverInfo = getServletContext().getServerInfo();

The most straightforward use of information about the server is an "About This Server" servlet, as shown in Example 4-3.

Example 4-3. Snooping the server

import java.io.*;
import java.util.*;
import javax.servlet.*;

public class ServerSnoop extends GenericServlet {

  public void service(ServletRequest req, ServletResponse res) 
                             throws ServletException, IOException {
    res.setContentType("text/plain");
    PrintWriter out = res.getWriter();

    out.println("req.getServerName(): " + req.getServerName());
    out.println("req.getServerPort(): " + req.getServerPort());
    out.println("getServletContext().getServerInfo(): " +
                 getServletContext().getServerInfo());
    out.println("getServerInfo() name: " +
                 getServerInfoName(getServletContext().getServerInfo()));
    out.println("getServerInfo() version: " +
                 getServerInfoVersion(getServletContext().getServerInfo()));
    out.println("getServletContext().getAttribute(\"attribute\"): " + 
                 getServletContext().getAttribute("attribute"));
  }

  private String getServerInfoName(String serverInfo) {
    int slash = serverInfo.indexOf('/');
    if (slash == -1) return serverInfo;
    else return serverInfo.substring(0, slash);
  }

  private String getServerInfoVersion(String serverInfo) {
    int slash = serverInfo.indexOf('/');
    if (slash == -1) return null;
    else return serverInfo.substring(slash + 1);
  }
}

This servlet also directly subclasses GenericServlet, demonstrating that all the information about a server is available to servlets of any type. The servlet outputs simple raw text. When accessed, this servlet prints something like:

req.getServerName(): localhost
req.getServerPort(): 8080
getServletContext().getServerInfo(): JavaWebServer/1.1.1
getServerInfo() name: JavaWebServer
getServerInfo() version: 1.1.1
getServletContext().getAttribute("attribute"): null

Unfortunately, there is no server-independent way to determine the server's root directory, referred to in this book as server_root. However, some servers--including the Java Web Server--save the server's root directory name in the server.root system property, where it can be retrieved using System. getProperty("server.root").

4.2.2. Locking a Servlet to a Server

This server information can be put to more productive uses. Let's assume you've written a servlet and you don't want it running just anywhere. Perhaps you want to sell it and, to limit the chance of unauthorized copying, you want to lock the servlet to your customer's machine with a software license. Or, alternatively, you've written a license generator as a servlet and want to make sure it works only behind your firewall. This can be done relatively easily because a servlet has instant access to the information about its server.

Example 4-4 shows a servlet that locks itself to a particular server IP address and port number. It requires an init parameter key that is appropriate for its server IP address and port before it unlocks itself and handles a request. If it does not receive the appropriate key, it refuses to continue. The algorithm used to map the key to the IP address and port (and vice-versa) must be secure.

Example 4-4. A servlet locked to a server

import java.io.*;
import java.net.*;
import java.util.*;
import javax.servlet.*;

public class KeyedServerLock extends GenericServlet {

  // This servlet has no class or instance variables
  // associated with the locking, so as to simplify
  // synchronization issues.

  public void service(ServletRequest req, ServletResponse res) 
                             throws ServletException, IOException {
    res.setContentType("text/plain");
    PrintWriter out = res.getWriter();

    // The piracy check shouldn't be done in init 
    // because name/port are part of request.
    String key = getInitParameter("key");
    String host = req.getServerName();
    int port = req.getServerPort();

    // Check if the init parameter "key" unlocks this server.
    if (! keyFitsServer(key, host, port)) {
      // Explain, condemn, threaten, etc.
      out.println("Pirated!");
    }
    else {
      // Give 'em the goods
      out.println("valid");
      // etc...
    }
  }

  // This method contains the algorithm used to match a key with
  // a server host and port. This example implementation is extremely
  // weak and should not be used by commercial sites.
  //
  private boolean keyFitsServer(String key, String host, int port) {

    if (key == null) return false;

    long numericKey = 0;
    try {
      numericKey = Long.parseLong(key);
    }
    catch (NumberFormatException e) {
      return false;
    }

    // The key must be a 64-bit number equal to the logical not (~) 
    // of the 32-bit IP address concatenated with the 32-bit port number.

    byte hostIP[];
    try {
      hostIP = InetAddress.getByName(host).getAddress();
    } 
    catch (UnknownHostException e) {
      return false;
    }

    // Get the 32-bit IP address
    long servercode = 0;
    for (int i = 0; i < 4; i++) {
      servercode <<= 8;
      servercode |= (hostIP[i] & 255);
    }

    // Concatentate the 32-bit port number
    servercode <<= 32;
    servercode |= port;

    // Logical not
    long accesscode = ~numericKey;

    // The moment of truth: Does the key match?
    return (servercode == accesscode);
  }
}

This servlet refuses to perform unless given the correct key. To really make it secure, however, the simple keyFitsServer() logic should be replaced with a strong algorithm and the whole servlet should be run through an obfuscator to prevent decompiling. Example 4-8 later in this chapter provides the code used to generate keys. If you try this servlet yourself, it's best if you access the server with its actual name, rather than localhost, so the servlet can determine the web server's true name and IP address.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.