Skip to main content

Testing for errant network connections

Posted by tomwhite on February 8, 2007 at 1:37 AM PST

We kept breaking our XML catalog resolution in the course of developing an application. We would refactor the parser code, or we would upgrade a schema and forget to upgrade the catalog. The application wouldn't break, but it took longer to run since resources were being retrieved over the network rather than using the local catalog. Because we didn't time our test runs, and because we had lots of non-network dependent tests in the suite, this regression would go unnoticed for a while. When it was noticed we'd fix the symptom, then move on. Until it happened again...

After the sixth or so occurrence in few years, I wrote a class to detect the problem. It's a simple implementation of SecurityManager that throws an exception when an attempt is made to connect to a site that is not on the list of approved hosts. The code appears below.

To use it we set the -Djava.security.manager command line argument (to the fully-qualified classname of RestrictedNetworkAccessSecurityManager) when running our test suites. Tests that access hosts that we aren't expecting will then fail with an error.

It's not just for applications that use XML catalogs. It's useful for almost any code, as a way to audit - and regression test - the network resources your application depends on. (Like a database you thought wasn't being used any more.) It's also a simple way to discover when third-party libraries connect to the internet unannounced.

The code is rough and ready - it's good enough for testing, but not really suitable for anything else, since it is totally permissive except for the checks on outbound connections. Feel free to use it, and suggest improvements or ways you've tackled the same problem in the comments section.

import java.net.SocketPermission;
import java.security.AccessControlException;
import java.security.Permission;
import java.util.Arrays;
import java.util.List;

public class RestrictedNetworkAccessSecurityManager extends SecurityManager {
  private static final List<String> ALLOWED_HOSTNAMES = Arrays.asList(new String[]{
    "localhost", "127.0.0.1",
  });

  @Override
  public void checkConnect(String host, int port, Object context) {
    checkConnect(host, port);
  }

  @Override
  public void checkConnect(String host, int port) {
    if (host == null) {
      throw new NullPointerException("host can't be null");
    }
    if (!host.startsWith("[") && host.indexOf(':') != -1) {
      host = "[" + host + "]";
    }
    if (ALLOWED_HOSTNAMES.contains(host)) {
      return;
    }
    String hostPort;
    if (port == -1) {
      hostPort = host;
    } else {
      hostPort = host + ":" + port;
    }
    String message = "Opening a socket connection to " + hostPort + " is restricted.";
    throw new AccessControlException(message, new SocketPermission(hostPort, "connect"));
  }

  @Override
  public void checkPermission(Permission perm) {
    // Allow all other actions
  }

  @Override
  public void checkPermission(Permission perm, Object context) {
    // Allow all other actions
  }
}
Related Topics >>