summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/btr/proxy/search/wpad
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/btr/proxy/search/wpad')
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java234
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java315
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java880
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java235
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java107
5 files changed, 1771 insertions, 0 deletions
diff --git a/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java
new file mode 100644
index 0000000..cf01fd3
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java
@@ -0,0 +1,234 @@
+package com.btr.proxy.search.wpad;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+
+
+/*****************************************************************************
+ * Uses automatic proxy script search (WPAD) to find an PAC file automatically.
+ * <p>
+ * Note: at the moment only the DNS name guessing schema is implemented.
+ * All others are missing.
+ * </p><p>
+ * For more information about WPAD:
+ * <a href="http://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol">Web_Proxy_Autodiscovery_Protocol</a>
+ * </p><p>
+ * Outdated RFC draft:
+ * <a href="http://www.web-cache.com/Writings/Internet-Drafts/draft-ietf-wrec-wpad-01.txt">draft-ietf-wrec-wpad-01.txt</a>
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class WpadProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public WpadProxySearchStrategy() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings from a PAC file.
+ * The location of the PAC file is determined automatically.
+ * @return a configured ProxySelector, null if none is found.
+ * @throws ProxyException on error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ try {
+ Logger.log(getClass(), LogLevel.TRACE, "Using WPAD to find a proxy");
+
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE, "PAC script url found: {0}", pacScriptUrl);
+ return ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Error during WPAD search.", e);
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Loads the settings and stores them in a properties map.
+ * @return the settings.
+ ************************************************************************/
+
+ public Properties readSettings() {
+ try {
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Properties result = new Properties();
+ result.setProperty("url", pacScriptUrl);
+ return result;
+ } catch (IOException e) {
+ // Ignore and return empty properties.
+ return new Properties();
+ }
+ }
+
+ /*************************************************************************
+ * Uses DNS to find the script URL.
+ * Attention: this detection method is known to have some severe security issues.
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDNS() throws IOException {
+ String result = null;
+ // String fqdn = InetAddress.getLocalHost().getCanonicalHostName();
+
+ Logger.log(getClass(), LogLevel.TRACE, "Searching per DNS guessing.");
+ // Logger.log(getClass(), LogLevel.INFO, "fqdn: ", fqdn);
+
+ /** Reading address from "/etc/resolv.conf"; file looks like:
+ *
+ # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
+ # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
+ nameserver 127.0.1.1
+ search ruf.uni-freiburg.de lp.ruf.uni-freiburg.de
+ */
+
+ FileReader fr = new FileReader("/etc/resolv.conf");
+ BufferedReader br = new BufferedReader(fr);
+
+ String input;
+ String[] addresses = null;
+ // using the 4th line of the file.
+ while ((input = br.readLine()) != null) {
+ if (input.startsWith("search")) {
+ // the first one is "search" and afterwards addresses are following.
+ addresses = input.substring(6).split(" ");
+ break;
+ }
+ }
+
+ if (addresses == null) {
+ addresses = new String[]{""};
+ }
+
+
+ for (int i = 0; i < addresses.length; ++i) {
+ String address = addresses[i];
+ int index = -1;
+ do {
+ if (index != -1) {
+ address = address.substring(index);
+ } else {
+ address = "";
+ }
+
+ // Try to connect to URL
+ try {
+ address = ".uni-freiburg.de";
+ URL lookupURL = new URL("http://wpad"+ address +"/wpad.dat");
+ Logger.log(getClass(), LogLevel.TRACE, "Trying url: {0}", lookupURL);
+
+ HttpURLConnection con = (HttpURLConnection) lookupURL.openConnection(Proxy.NO_PROXY);
+ con.setInstanceFollowRedirects(true);
+ con.setRequestProperty("accept", "application/x-ns-proxy-autoconfig");
+ if (con.getResponseCode() == 200) {
+ result = lookupURL.toString();
+ return result;
+ }
+ con.disconnect();
+ } catch (UnknownHostException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Not available!");
+ // Not a real error, try next address
+ }
+ if (address.length() == 0) {
+ break;
+ }
+ index = address.indexOf('.', 1);
+ } while (true);
+ }
+
+ return null;
+ }
+
+ /*************************************************************************
+ * Uses DHCP to find the script URL.
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDHCP() {
+ Logger.log(getClass(), LogLevel.DEBUG, "Searching per DHCP not supported yet.");
+ // TODO Rossi 28.04.2009 Not implemented yet.
+ return null;
+ }
+
+ // Main method for testing.
+ public static void main( String[] args ) throws IOException {
+ WpadProxySearchStrategy wPSS = new WpadProxySearchStrategy();
+// System.setProperty("com.btr.proxy.pac.overrideLocalIP", "10.0.0.1");
+ try {
+ ProxySelector pS = wPSS.getProxySelector();
+ ProxySelector.setDefault(pS);
+ List<Proxy> proxyList = pS.select(new URI("http://www.google.de"));
+
+ if (proxyList.isEmpty()) {
+ Logger.log(WpadProxySearchStrategy.class, LogLevel.INFO, "ProxyList is empty!");
+ } else {
+ Logger.log(WpadProxySearchStrategy.class, LogLevel.INFO, "proxyList contains: {0}", proxyList.toString());
+ }
+ } catch (ProxyException e) {
+ // TODO bjoern 28.10.2014 Auto-generated catch block
+ e.printStackTrace();
+ } catch (URISyntaxException e) {
+ // TODO bjoern 28.10.2014 Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ URL test = new URL("http://www.google.de");
+ URLConnection uc = test.openConnection();
+ BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream()));
+
+ String inputLine;
+ while ((inputLine = br.readLine()) != null) {
+ System.out.println(inputLine);
+ }
+
+ Socket socket = new Socket("www.google.de", 80);
+ BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
+ bw.write("GET / HTTP/1.1\r\nHost: www.google.de\r\nConnection: close\r\nAccept-Encoding: *\r\n\r\n");
+ bw.flush();
+
+ br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ while ((inputLine = br.readLine()) != null) {
+ System.out.println(inputLine);
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java
new file mode 100644
index 0000000..bb56cc0
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java
@@ -0,0 +1,315 @@
+package com.btr.proxy.search.wpad;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.Properties;
+import java.util.Random;
+import java.util.StringTokenizer;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.wpad.dhcp.DHCPMessage;
+import com.btr.proxy.search.wpad.dhcp.DHCPOptions;
+import com.btr.proxy.search.wpad.dhcp.DHCPSocket;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+
+/*****************************************************************************
+ * Uses automatic proxy script search (WPAD) to find an PAC file automatically.
+ * <p>
+ * Note: at the moment only the DNS name guessing schema is implemented. All
+ * others are missing.
+ * </p>
+ * <p>
+ * For more information about WPAD: <a
+ * href="http://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol"
+ * >Web_Proxy_Autodiscovery_Protocol</a>
+ * </p>
+ * <p>
+ * Outdated RFC draft: <a href=
+ * "http://www.web-cache.com/Writings/Internet-Drafts/draft-ietf-wrec-wpad-01.txt"
+ * >draft-ietf-wrec-wpad-01.txt</a>
+ * </p>
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class WpadProxySearchStrategyWithDHPC implements ProxySearchStrategy {
+ DHCPSocket bindSocket = null;
+ byte hwaddr[] = new byte[16];
+ InetAddress serverIP;
+ int portNum;
+ boolean gSentinel;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public WpadProxySearchStrategyWithDHPC() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings from a PAC file. The location of the PAC file is
+ * determined automatically.
+ *
+ * @return a configured ProxySelector, null if none is found.
+ * @throws ProxyException
+ * on error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ try {
+ Logger.log(getClass(), LogLevel.TRACE, "Using WPAD to find a proxy");
+
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE, "PAC script url found: {0}",
+ pacScriptUrl);
+ return ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Error during WPAD search.",
+ e);
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Loads the settings and stores them in a properties map.
+ *
+ * @return the settings.
+ ************************************************************************/
+
+ public Properties readSettings() {
+ try {
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Properties result = new Properties();
+ result.setProperty("url", pacScriptUrl);
+ return result;
+ } catch (IOException e) {
+ // Ignore and return empty properties.
+ return new Properties();
+ }
+ }
+
+ /*************************************************************************
+ * Uses DNS to find the script URL. Attention: this detection method is
+ * known to have some severe security issues.
+ *
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDNS() throws IOException {
+ String result = null;
+ // String fqdn = InetAddress.getLocalHost().getCanonicalHostName();
+
+ Logger.log(getClass(), LogLevel.TRACE, "Searching per DNS guessing.");
+ // Logger.log(getClass(), LogLevel.INFO, "fqdn: ", fqdn);
+
+ /**
+ * Reading address from "/etc/resolv.conf"; file looks like:
+ *
+ * # Dynamic resolv.conf(5) file for glibc resolver(3) generated by
+ * resolvconf(8) # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE
+ * OVERWRITTEN nameserver 127.0.1.1 search ruf.uni-freiburg.de
+ * lp.ruf.uni-freiburg.de
+ */
+
+ FileReader fr = new FileReader("/etc/resolv.conf");
+ BufferedReader br = new BufferedReader(fr);
+
+ String input;
+ // using the 4th line of the file.
+ br.readLine();
+ br.readLine();
+ br.readLine();
+ input = br.readLine();
+
+ String[] addresses = input.split(" ");
+ // the first one is "search" and afterwards addresses are following.
+
+ for (int i = 0; i < addresses.length; ++i) {
+ String address = addresses[i];
+ int index = -1;
+ do {
+ address = address.substring(index + 1);
+
+ // if we are already on TLD level then escape
+ if (address.indexOf('.') == -1) {
+ break;
+ }
+
+ // Try to connect to URL
+ try {
+ URL lookupURL = new URL("http://wpad." + address
+ + "/wpad.dat");
+ Logger.log(getClass(), LogLevel.TRACE, "Trying url: {0}",
+ lookupURL);
+
+ HttpURLConnection con = (HttpURLConnection) lookupURL
+ .openConnection(Proxy.NO_PROXY);
+ con.setInstanceFollowRedirects(true);
+ con.setRequestProperty("accept",
+ "application/x-ns-proxy-autoconfig");
+ if (con.getResponseCode() == 200) {
+ result = lookupURL.toString();
+ return result;
+ }
+ con.disconnect();
+ } catch (UnknownHostException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Not available!");
+ // Not a real error, try next address
+ }
+ index = address.indexOf('.');
+ } while (index != -1);
+ }
+
+ return null;
+ }
+
+ /*************************************************************************
+ * Uses DHCP to find the script URL.
+ *
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDHCP() {
+ Logger.log(getClass(), LogLevel.DEBUG,
+ "Searching per DHCP not supported yet.");
+
+ // create socket.
+ try {
+ // bindSocket = new DHCPSocket(DHCPMessage.CLIENT_PORT);
+ // --> Not able to use ports under 1024 without being root.
+ this.bindSocket = new DHCPSocket(1068);
+ } catch (SocketException e) {
+ System.err.println(e);
+ return null;
+ }
+
+
+
+ return null;
+ }
+
+ // Sends DHCPDISCOVER Message and returns server message
+ private DHCPMessage SendDiscover() {
+ DHCPSocket bindSocket = null;
+ try {
+ bindSocket = new DHCPSocket(1068);
+ } catch (SocketException e1) {
+ // TODO bjoern 29.10.2014 Auto-generated catch block
+ e1.printStackTrace();
+ return null;
+ }
+
+ Random ranXid = new Random();
+ DHCPMessage messageOut = new DHCPMessage(DHCPMessage.BROADCAST_ADDR,
+ DHCPMessage.SERVER_PORT);
+ DHCPMessage messageIn = new DHCPMessage(DHCPMessage.BROADCAST_ADDR,
+ DHCPMessage.SERVER_PORT);
+ try {
+ // Specify type of message: 1 = request - message, 2 = reply - message
+ messageOut.setOp((byte) 1);
+ // set Hardware type: 1 = Ethernet, 6 = IEEE 802 Networks, ...
+ messageOut.setHtype((byte) 1); // 1 = ethernet
+ // set hardware address length: 6 for MAC address
+ messageOut.setHlen((byte) 6);
+ messageOut.setHops((byte) 0);
+ // set session ID for later comparing with incoming message.
+ messageOut.setXid(ranXid.nextInt());
+ messageOut.setSecs((short) 0);
+ messageOut.setFlags((short) 0x8000);
+ // set client hardware address, in this case my own hardware address of eth0.
+ messageOut.setChaddr(ChaddrToByte("D8:D3:85:80:8F:C9"));
+
+ byte[] opt = new byte[1];
+ byte[] wpadOpt = new byte[1];
+ opt[0] = DHCPMessage.DHCPDISCOVER;
+
+ // change message type
+ messageOut.setOption(DHCPOptions.OPTION_DHCP_MESSAGE_TYPE, opt);
+ wpadOpt[0] = DHCPMessage.OPTION_DHCP_WPAD;
+ messageOut.setOption(DHCPOptions.OPTION_DHCP_PARAMETER_REQUEST_LIST, wpadOpt);
+// messageOut.setOption(DHCPOptions.OPTION_DHCP_IP_ADRESS_REQUESTED,
+// offerMessageIn.getYiaddr());
+
+ bindSocket.send(messageOut); // send DHCPREQUEST
+ System.out.println("Sending DHCPDISCOVER ...");
+ boolean sentinal = true;
+ int counter = 0;
+ while (sentinal) {
+ if (counter == 2) { return null; }
+ if (bindSocket.receive(messageIn)) {
+ if (messageOut.getXid() == messageIn.getXid()) {
+ sentinal = false;
+ } else {
+ bindSocket.send(messageOut);
+ counter++;
+ }
+ } else {
+ bindSocket.send(messageOut);
+ counter++;
+ }
+ }
+ } catch (SocketException e) {
+ System.err.println(e);
+ return null;
+ } catch (IOException e) {
+ System.err.println(e);
+ return null;
+ } finally {
+ try {
+ bindSocket.close();
+ } catch (Exception e) {
+ // do nothing.
+ }
+ } // end catch
+ return messageIn;
+ }
+
+ // Main method for testing.
+ public static void main(String[] args) throws UnsupportedEncodingException {
+ WpadProxySearchStrategyWithDHPC wPSSDHCP = new WpadProxySearchStrategyWithDHPC();
+ DHCPMessage serverAnswer = wPSSDHCP.SendDiscover();
+
+ Logger.log(WpadProxySearchStrategyWithDHPC.class, LogLevel.INFO,
+ "DHCP serverAnswer: {0}", new String(serverAnswer.getOption(DHCPMessage.OPTION_DHCP_WPAD), "UTF-8"));
+ }
+
+ private byte[] ChaddrToByte(String inChaddr) {
+ StringTokenizer token = new StringTokenizer(inChaddr, ":");
+ Integer tempInt = new Integer(0);
+ byte outHwaddr[] = new byte[16];
+ int temp;
+ int i = 0;
+ while (i < 6) {
+ temp = tempInt.parseInt(token.nextToken(), 16);
+ outHwaddr[i] = (byte) temp;
+ i++;
+ }
+ return outHwaddr;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java
new file mode 100644
index 0000000..86323d5
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java
@@ -0,0 +1,880 @@
+package com.btr.proxy.search.wpad.dhcp;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This class represents a DHCP Message.
+ *
+ * @author Jason Goldschmidt, Nick Stone and Simon Frankenberger
+ */
+public class DHCPMessage {
+ // -----------------------------------------------------------
+ // Constants
+ // -----------------------------------------------------------
+ /**
+ * Operation for a request
+ */
+ public static final byte OP_REQUEST = 1;
+
+ /**
+ * Operation for a reply
+ */
+ public static final byte OP_REPLY = 2;
+
+ /**
+ * Message Code representing a DHCPDISCOVER message
+ */
+ public static final byte DHCPDISCOVER = 1;
+
+ /**
+ * Message Code representing a DHCPOFFER message
+ */
+ public static final byte DHCPOFFER = 2;
+
+ /**
+ * Message Code representing a DHCPREQUEST message
+ */
+ public static final byte DHCPREQUEST = 3;
+
+ /**
+ * Message Code representing a DHCPDECLINE message
+ */
+ public static final byte DHCPDECLINE = 4;
+
+ /**
+ * Message Code representing a DHCPACK message
+ */
+ public static final byte DHCPACK = 5;
+
+ /**
+ * Message Code representing a DHCPNAK message
+ */
+ public static final byte DHCPNAK = 6;
+
+ /**
+ * Message Code representing a DHCPRELEASE message
+ */
+ public static final byte DHCPRELEASE = 7;
+
+ /**
+ * Message Code representing a DHCPINFORM message
+ */
+ public static final byte DHCPINFORM = 8;
+
+ /**
+ * Default DHCP client port
+ */
+ public static final int CLIENT_PORT = 68; // client port (by default)
+
+ /**
+ * Default DHCP server port
+ */
+ public static final int SERVER_PORT = 67; // server port (by default)
+
+ /**
+ * Broadcast Adress to send packets to
+ */
+ public static InetAddress BROADCAST_ADDR = null;
+
+ public static final byte OPTION_DHCP_WPAD = (byte) 252;
+
+
+
+ // -----------------------------------------------------------
+ // Fields defining a dhcp message
+ // -----------------------------------------------------------
+ /**
+ * Operation Code.<br>
+ * <br>
+ * Can either be {@link #OP_REQUEST} or {@link #OP_REPLY}.
+ */
+ private byte op;
+
+ /**
+ * Networktype as defined by
+ * <a href="http://tools.ietf.org/html/rfc1340#page-54">RFC1340 page 54</a>.
+ */
+ private byte htype;
+
+ /**
+ * Hardware address length (e.g. '6' for ethernet).
+ */
+ private byte hlen;
+
+ /**
+ * Client sets to zero, optionally used by relay-agents
+ * when booting via a relay-agent.
+ */
+ private byte hops;
+
+ /**
+ * Transaction ID, a random number chosen by the
+ * client, used by the client and server to associate
+ * messages and responses between a client and a
+ * server.
+ */
+ private int xid;
+
+ /**
+ * Filled in by client, seconds elapsed since client
+ * started trying to boot.
+ */
+ private short secs;
+
+ /**
+ * Flags for this message.<br>
+ * The leftmost bit is defined as the BROADCAST (B) flag.
+ */
+ private short flags;
+
+ /**
+ * Client IP address; filled in by client in
+ * DHCPREQUEST if verifying previously allocated
+ * configuration parameters.
+ */
+ private byte ciaddr[] = new byte[4];
+
+ /**
+ * 'your' (client) IP address.
+ */
+ private byte yiaddr[] = new byte[4];
+
+ /**
+ * IP address of next server to use in bootstrap;
+ * returned in DHCPOFFER, DHCPACK and DHCPNAK by
+ * server.
+ */
+ private byte siaddr[] = new byte[4];
+
+ /**
+ * Relay agent IP address, used in booting via a
+ * relay-agent.
+ */
+ private byte giaddr[] = new byte[4];
+
+ /**
+ * Client hardware address.
+ */
+ private byte chaddr[] = new byte[16];
+
+ /**
+ * Optional server host name, null terminated string.
+ */
+ private byte sname[] = new byte[64];
+
+ /**
+ * Boot file name, null terminated string; "generic"
+ * name or null in DHCPDISCOVER, fully qualified
+ * directory-path name in DHCPOFFER.
+ */
+ private byte file[] = new byte[128];
+
+ /**
+ * Internal representation of the given DHCP options.
+ */
+ private DHCPOptions optionsList = null;
+
+ /**
+ * global port variable for this message
+ */
+ private int gPort;
+
+ /**
+ * The destination IP-Adress of this message
+ */
+ private InetAddress destination_IP;
+
+ static {
+ try {
+ BROADCAST_ADDR = InetAddress.getByName("255.255.255.255");
+ // broadcast address(by default)
+ }
+ catch (UnknownHostException e) {
+ // Broadcast address must always exist
+ }
+ }
+
+ // -----------------------------------------------------------
+ // Constructors
+ // -----------------------------------------------------------
+
+ /**
+ * Creates empty DHCPMessage object,
+ * initializes the object, sets the host to the broadcast address,
+ * the local subnet, binds to the default server port.
+ */
+ public DHCPMessage() {
+ initialize();
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Copy constructor
+ * creates DHCPMessage from inMessage
+ *
+ * @param inMessage The message to be copied
+ */
+ public DHCPMessage(DHCPMessage inMessage) {
+ initialize();
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = SERVER_PORT;
+ this.op = inMessage.getOp();
+ this.htype = inMessage.getHtype();
+ this.hlen = inMessage.getHlen();
+ this.hops = inMessage.getHops();
+ this.xid = inMessage.getXid();
+ this.secs = inMessage.getSecs();
+ this.flags = inMessage.getFlags();
+ this.ciaddr = inMessage.getCiaddr();
+ this.yiaddr = inMessage.getYiaddr();
+ this.siaddr = inMessage.getSiaddr();
+ this.giaddr = inMessage.getGiaddr();
+ this.chaddr = inMessage.getChaddr();
+ this.sname = inMessage.getSname();
+ this.file = inMessage.getFile();
+ this.optionsList.internalize(inMessage.getOptions());
+ }
+
+ /**
+ * Copy constructor
+ * creates DHCPMessage from inMessage and sets server and port.
+ *
+ * @param inMessage The message to be copied
+ * @param inServername The host name
+ * @param inPort The port number
+ */
+ public DHCPMessage(DHCPMessage inMessage, InetAddress inServername, int inPort) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = inPort;
+
+ this.op = inMessage.getOp();
+ this.htype = inMessage.getHtype();
+ this.hlen = inMessage.getHlen();
+ this.hops = inMessage.getHops();
+ this.xid = inMessage.getXid();
+ this.secs = inMessage.getSecs();
+ this.flags = inMessage.getFlags();
+ this.ciaddr = inMessage.getCiaddr();
+ this.yiaddr = inMessage.getYiaddr();
+ this.siaddr = inMessage.getSiaddr();
+ this.giaddr = inMessage.getGiaddr();
+ this.chaddr = inMessage.getChaddr();
+ this.sname = inMessage.getSname();
+ this.file = inMessage.getFile();
+ this.optionsList.internalize(inMessage.getOptions());
+ }
+
+ /**
+ * Copy constructor
+ * creates DHCPMessage from inMessage and sets server name.
+ *
+ * @param inMessage The message to be copied
+ * @param inServername The host name
+ */
+ public DHCPMessage(DHCPMessage inMessage, InetAddress inServername) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = SERVER_PORT;
+
+ this.op = inMessage.getOp();
+ this.htype = inMessage.getHtype();
+ this.hlen = inMessage.getHlen();
+ this.hops = inMessage.getHops();
+ this.xid = inMessage.getXid();
+ this.secs = inMessage.getSecs();
+ this.flags = inMessage.getFlags();
+ this.ciaddr = inMessage.getCiaddr();
+ this.yiaddr = inMessage.getYiaddr();
+ this.siaddr = inMessage.getSiaddr();
+ this.giaddr = inMessage.getGiaddr();
+ this.chaddr = inMessage.getChaddr();
+ this.sname = inMessage.getSname();
+ this.file = inMessage.getFile();
+ this.optionsList.internalize(inMessage.getOptions());
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object, sets the host to a specified host name,
+ * and binds to a specified port.
+ *
+ * @param inServername The host name
+ * @param inPort The port number
+ */
+ public DHCPMessage(InetAddress inServername, int inPort) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object, sets the host to a specified host name,
+ * and binds to the default port.
+ *
+ * @param inServername The host name
+ */
+ public DHCPMessage(InetAddress inServername) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object, sets the host to the broadcast address,
+ * and binds to a specified port.
+ *
+ * @param inPort The port number
+ */
+ public DHCPMessage(int inPort) {
+ initialize();
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to default host name, the
+ * local subnet, and bind to the default server port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ */
+ public DHCPMessage(byte[] ibuf) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to specified host name,
+ * and binds to the specified port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ * @param inServername The hostname
+ * @param inPort The port number
+ */
+ public DHCPMessage(byte[] ibuf, InetAddress inServername, int inPort) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = inServername;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to broadcast address,
+ * and binds to the specified port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ * @param inPort The port number
+ */
+ public DHCPMessage(byte ibuf[], int inPort) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to specified host name,
+ * and binds to the specified port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ * @param inServername The hostname
+ */
+ public DHCPMessage(byte[] ibuf, InetAddress inServername) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = inServername;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Creates a new DHCPMessage object from the giben DataInputStream.
+ *
+ * @param inStream The stream to read from
+ */
+ public DHCPMessage(DataInputStream inStream) {
+ initialize();
+
+ try {
+ this.op = inStream.readByte();
+ this.htype = inStream.readByte();
+ this.hlen = inStream.readByte();
+ this.hops = inStream.readByte();
+ this.xid = inStream.readInt();
+ this.secs = inStream.readShort();
+ this.flags = inStream.readShort();
+ inStream.readFully(this.ciaddr, 0, 4);
+ inStream.readFully(this.yiaddr, 0, 4);
+ inStream.readFully(this.siaddr, 0, 4);
+ inStream.readFully(this.giaddr, 0, 4);
+ inStream.readFully(this.chaddr, 0, 16);
+ inStream.readFully(this.sname, 0, 64);
+ inStream.readFully(this.file, 0, 128);
+ byte[] options = new byte[312];
+ inStream.readFully(options, 0, 312);
+ this.optionsList.internalize(options);
+ }
+ catch (IOException e) {
+ System.err.println(e);
+ }
+ }
+
+ // -----------------------------------------------------------
+ // Methods
+ // -----------------------------------------------------------
+ /**
+ * Initializes datamembers in the constructors
+ * every empty DHCPMessage object will by default contain these params.
+ * Initializes options array from linked list form.
+ */
+ private void initialize() {
+ this.optionsList = new DHCPOptions();
+ }
+
+ /**
+ * Converts a DHCPMessage object to a byte array.
+ *
+ * @return A byte array with information from DHCPMessage object,
+ * ready to send.
+ */
+ public synchronized byte[] externalize() {
+ ByteArrayOutputStream outBStream = new ByteArrayOutputStream();
+ DataOutputStream outStream = new DataOutputStream(outBStream);
+
+ try {
+ outStream.writeByte(this.op);
+ outStream.writeByte(this.htype);
+ outStream.writeByte(this.hlen);
+ outStream.writeByte(this.hops);
+ outStream.writeInt(this.xid);
+ outStream.writeShort(this.secs);
+ outStream.writeShort(this.flags);
+ outStream.write(this.ciaddr, 0, 4);
+ outStream.write(this.yiaddr, 0, 4);
+ outStream.write(this.siaddr, 0, 4);
+ outStream.write(this.giaddr, 0, 4);
+ outStream.write(this.chaddr, 0, 16);
+ outStream.write(this.sname, 0, 64);
+ outStream.write(this.file, 0, 128);
+
+ byte[] options = new byte[312];
+ if (this.optionsList == null) {
+ initialize();
+ }
+
+ options = this.optionsList.externalize();
+ outStream.write(options, 0, 312);
+ } catch (IOException e) {
+ System.err.println(e);
+ }
+
+ // extract the byte array from the Stream
+ byte data[] = outBStream.toByteArray();
+
+ return data;
+ }
+
+ /**
+ * Convert a specified byte array containing a DHCP message into a
+ * DHCPMessage object.
+ *
+ * @param ibuff Byte array to convert to a DHCPMessage object
+ * @return A DHCPMessage object with information from byte array.
+ */
+
+ public synchronized DHCPMessage internalize(byte[] ibuff) {
+ ByteArrayInputStream inBStream =
+ new ByteArrayInputStream(ibuff, 0, ibuff.length);
+ DataInputStream inStream = new DataInputStream(inBStream);
+
+ try {
+ this.op = inStream.readByte();
+ this.htype = inStream.readByte();
+ this.hlen = inStream.readByte();
+ this.hops = inStream.readByte();
+ this.xid = inStream.readInt();
+ this.secs = inStream.readShort();
+ this.flags = inStream.readShort();
+ inStream.readFully(this.ciaddr, 0, 4);
+ inStream.readFully(this.yiaddr, 0, 4);
+ inStream.readFully(this.siaddr, 0, 4);
+ inStream.readFully(this.giaddr, 0, 4);
+ inStream.readFully(this.chaddr, 0, 16);
+ inStream.readFully(this.sname, 0, 64);
+ inStream.readFully(this.file, 0, 128);
+
+ byte[] options = new byte[312];
+ inStream.readFully(options, 0, 312);
+ if (this.optionsList == null) {
+ initialize();
+ }
+
+ this.optionsList.internalize(options);
+ }
+ catch (IOException e) {
+ System.err.println(e);
+ } // end catch
+
+ return this;
+ }
+
+ /**
+ * Set message Op code / message type.
+ *
+ * @param inOp message Op code / message type
+ */
+ public void setOp(byte inOp) {
+ this.op = inOp;
+ }
+
+ /**
+ * Set hardware address type.
+ *
+ * @param inHtype hardware address type
+ */
+ public void setHtype(byte inHtype) {
+ this.htype = inHtype;
+ }
+
+ /**
+ * Set hardware address length.
+ *
+ * @param inHlen hardware address length
+ */
+ public void setHlen(byte inHlen) {
+ this.hlen = inHlen;
+ }
+
+ /**
+ * Set hops field.
+ *
+ * @param inHops hops field
+ */
+ public void setHops(byte inHops) {
+ this.hops = inHops;
+ }
+
+ /**
+ * Set transaction ID.
+ *
+ * @param inXid transactionID
+ */
+ public void setXid(int inXid) {
+ this.xid = inXid;
+ }
+
+ /**
+ * Set seconds elapsed since client began address acquisition or
+ * renewal process.
+ *
+ * @param inSecs Seconds elapsed since client began address acquisition
+ * or renewal process
+ */
+ public void setSecs(short inSecs) {
+ this.secs = inSecs;
+ }
+
+ /**
+ * Set flags field.
+ *
+ * @param inFlags flags field
+ */
+ public void setFlags(short inFlags) {
+ this.flags = inFlags;
+ }
+
+ /**
+ * Set client IP address.
+ *
+ * @param inCiaddr client IP address
+ */
+ public void setCiaddr(byte[] inCiaddr) {
+ this.ciaddr = inCiaddr;
+ }
+
+ /**
+ * Set 'your' (client) IP address.
+ *
+ * @param inYiaddr 'your' (client) IP address
+ */
+ public void setYiaddr(byte[] inYiaddr) {
+ this.yiaddr = inYiaddr;
+ }
+
+ /**
+ * Set address of next server to use in bootstrap.
+ *
+ * @param inSiaddr address of next server to use in bootstrap
+ */
+ public void setSiaddr(byte[] inSiaddr) {
+ this.siaddr = inSiaddr;
+ }
+
+ /**
+ * Set relay agent IP address.
+ *
+ * @param inGiaddr relay agent IP address
+ */
+ public void setGiaddr(byte[] inGiaddr) {
+ this.giaddr = inGiaddr;
+ }
+
+ /**
+ * Set client harware address.
+ *
+ * @param inChaddr client hardware address
+ */
+ public void setChaddr(byte[] inChaddr) {
+ this.chaddr = inChaddr;
+ }
+
+ /**
+ * Set optional server host name.
+ *
+ * @param inSname server host name
+ */
+ public void setSname(byte[] inSname) {
+ this.sname = inSname;
+ }
+
+ /**
+ * Set boot file name.
+ *
+ * @param inFile boot file name
+ */
+ public void setFile(byte[] inFile) {
+ this.file = inFile;
+ }
+
+ /**
+ * Set message destination port.
+ *
+ * @param inPortNum port on message destination host
+ */
+ public void setPort(int inPortNum) {
+ this.gPort = inPortNum;
+ }
+
+ /**
+ * Set message destination IP
+ * @param inHost string representation of message destination IP or
+ * hostname
+ */
+ public void setDestinationHost(String inHost) {
+ try {
+ this.destination_IP = InetAddress.getByName(inHost);
+ }
+ catch (Exception e) {
+ System.err.println(e);
+ }
+ }
+
+ /**
+ * @return message Op code / message type.
+ */
+ public byte getOp() {
+ return this.op;
+ }
+
+ /**
+ * @return hardware address type.
+ */
+ public byte getHtype() {
+ return this.htype;
+ }
+
+ /**
+ * @return hardware address length.
+ */
+ public byte getHlen() {
+ return this.hlen;
+ }
+
+ /**
+ * @return hops field.
+ */
+ public byte getHops() {
+ return this.hops;
+ }
+
+ /**
+ * @return transaction ID.
+ */
+ public int getXid() {
+ return this.xid;
+ }
+
+ /**
+ * @return seconds elapsed since client began address
+ * acquisition or renewal process.
+ */
+ public short getSecs() {
+ return this.secs;
+ }
+
+ /**
+ * @return flags field.
+ */
+ public short getFlags() {
+ return this.flags;
+ }
+
+ /**
+ * @return client IP address.
+ */
+ public byte[] getCiaddr() {
+ return this.ciaddr;
+ }
+
+ /**
+ * @return 'your' (client) IP address.
+ */
+ public byte[] getYiaddr() {
+ return this.yiaddr;
+ }
+
+ /**
+ * @return address of next server to use in bootstrap.
+ */
+ public byte[] getSiaddr() {
+ return this.siaddr;
+ }
+
+ /**
+ * @return relay agent IP address.
+ */
+ public byte[] getGiaddr() {
+ return this.giaddr;
+ }
+
+ /**
+ * @return client harware address.
+ */
+ public byte[] getChaddr() {
+ return this.chaddr;
+ }
+
+ /**
+ * @return optional server host name.
+ */
+ public byte[] getSname() {
+ return this.sname;
+ }
+
+ /**
+ * @return boot file name.
+ */
+ public byte[] getFile() {
+ return this.file;
+ }
+
+ /**
+ * @return a byte array containing options
+ */
+ public byte[] getOptions() {
+ if (this.optionsList == null) {
+ initialize();
+ }
+ return this.optionsList.externalize();
+ }
+
+ /**
+ * @return An interger representation of the message
+ * destination port
+ */
+ public int getPort() {
+ return this.gPort;
+ }
+
+ /**
+ * Get message destination hostname
+ *
+ * @return A string representing the hostname of the
+ * message destination server
+ */
+ public String getDestinationAddress() {
+ return this.destination_IP.getHostAddress();
+ }
+
+ /**
+ * Sets DHCP options in DHCPMessage. If option already exists
+ * then remove old option and insert a new one.
+ *
+ * @param inOptNum option number
+ * @param inOptionData option data
+ */
+ public void setOption(int inOptNum, byte[] inOptionData) {
+ this.optionsList.setOption((byte) inOptNum, inOptionData);
+ }
+
+ /**
+ * Returns specified DHCP option that matches the input code. Null is
+ * returned if option is not set.
+ *
+ * @param inOptNum option number
+ *
+ * @return the option matching input code
+ */
+ public byte[] getOption(int inOptNum) {
+ if (this.optionsList == null) {
+ initialize();
+ }
+ return this.optionsList.getOption((byte) inOptNum);
+ }
+
+ /**
+ * Removes the specified DHCP option that matches the input code.
+ *
+ * @param inOptNum option number
+ */
+ public void removeOption(int inOptNum) {
+ if (this.optionsList == null) {
+ initialize();
+ }
+ this.optionsList.removeOption((byte) inOptNum);
+ }
+
+ /**
+ * Report whether or not the input option is set.
+ *
+ * @param inOptNum option number
+ *
+ * @return is the given option set?
+ */
+ public boolean IsOptSet(int inOptNum) {
+ if (this.optionsList == null) {
+ initialize();
+ }
+
+ return this.optionsList.contains((byte) inOptNum);
+ }
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java
new file mode 100644
index 0000000..1a07efb
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java
@@ -0,0 +1,235 @@
+package com.btr.proxy.search.wpad.dhcp;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+/**
+ * This class represents a linked list of options for a DHCP message.
+ * Its purpose is to ease option handling such as add, remove or change.
+ *
+ * @author Jason Goldschmidt and Simon Frankenberger
+ */
+public class DHCPOptions {
+ public static final int OPTION_PAD = 0;
+ public static final int OPTION_NETMASK = 1;
+ public static final int OPTION_TIME_OFFSET = 2;
+ public static final int OPTION_ROUTERS = 3;
+ public static final int OPTION_TIME_SERVERS = 4;
+ public static final int OPTION_NAME_SERVERS = 5;
+ public static final int OPTION_DNS_SERVERS = 6;
+ public static final int OPTION_LOG_SERVERS = 7;
+ public static final int OPTION_COOKIE_SERVERS = 8;
+ public static final int OPTION_LPR_SERVERS = 9;
+ public static final int OPTION_IMPRESS_SERVERS = 10;
+ public static final int OPTION_RESSOURCE_LOCATION_SERVERS = 11;
+ public static final int OPTION_HOSTNAME = 12;
+ public static final int OPTION_BOOT_FILESIZE = 13;
+ public static final int OPTION_MERIT_DUMPFILE = 14;
+ public static final int OPTION_DOMAIN_NAME = 15;
+ public static final int OPTION_SWAP_SERVER = 16;
+ public static final int OPTION_ROOT_PATH = 17;
+ public static final int OPTION_EXTENSIONS_PATH = 18;
+ public static final int OPTION_END = 255;
+
+ public static final int OPTION_IP_HOST_FORWARDING_ENABLE = 19;
+ public static final int OPTION_IP_HOST_NON_LOCAL_SOURCE_ROUTING_ENABLE = 20;
+ public static final int OPTION_IP_HOST_POLICY_FILTERS = 21;
+ public static final int OPTION_IP_HOST_MAXIMUM_DATAGRAM_REASSEMBLY_SIZE = 22;
+ public static final int OPTION_IP_HOST_DEFAULT_TTL = 23;
+ public static final int OPTION_IP_HOST_MTU_AGEING_TIMEOUT = 24;
+ public static final int OPTION_IP_HOST_MTU_PLATEAU_TABLE = 25;
+
+ public static final int OPTION_IP_INTERFACE_MTU = 26;
+ public static final int OPTION_IP_INTERFACE_ALL_SUBNETS_LOCAL_ENABLE = 27;
+ public static final int OPTION_IP_INTERFACE_BROADCAST_ADDRESS = 28;
+ public static final int OPTION_IP_INTERFACE_PERFORM_MASK_DISCOVERY_ENABLE = 29;
+ public static final int OPTION_IP_INTERFACE_MASK_SUPPLIER_ENABLE = 30;
+ public static final int OPTION_IP_INTERFACE_PERFORM_ROUTER_DISCOVERY_ENABLE = 31;
+ public static final int OPTION_IP_INTERFACE_ROUTER_SOLICITATION_ADDRESS = 32;
+ public static final int OPTION_IP_INTERFACE_STATIC_ROUTES = 33;
+
+ public static final int OPTION_LINK_TRAILER_ENCAPSULATION_ENABLE = 34;
+ public static final int OPTION_LINK_ARP_CACHE_TIMEOUT = 35;
+ public static final int OPTION_LINK_ETHERNET_ENCAPSULATION_ENABLE = 36;
+
+ public static final int OPTION_TCP_DEFAULT_TTL = 37;
+ public static final int OPTION_TCP_KEEP_ALIVE_INTERVAL = 38;
+ public static final int OPTION_TCP_KEEP_ALIVE_GERBAGE_ENABLE = 39;
+
+ public static final int OPTION_NIS_DOMAIN = 40;
+ public static final int OPTION_NIS_SERVERS = 41;
+ public static final int OPTION_NTP_SERVERS = 42;
+
+ public static final int OPTION_SERVICE_VENDOR_SPECIFIC_INFORMATIONS = 43;
+ public static final int OPTION_SERVICE_NETBOIS_NAME_SERVERS = 44;
+ public static final int OPTION_SERVICE_NETBOIS_DATAGRAM_DISTRIBUTION_SERVERS = 45;
+ public static final int OPTION_SERVICE_NETBOIS_NODE_TYPE = 46;
+ public static final int OPTION_SERVICE_NETBOIS_SCOPE_TYPE = 47;
+ public static final int OPTION_SERVICE_X_FONT_SERVERS = 48;
+ public static final int OPTION_SERVICE_X_DISPLAY_MANAGERS = 49;
+
+ public static final int OPTION_DHCP_IP_ADRESS_REQUESTED = 50;
+ public static final int OPTION_DHCP_IP_LEASE_TIME = 51;
+ public static final int OPTION_DHCP_OVERLOAD = 52;
+ public static final int OPTION_DHCP_MESSAGE_TYPE = 53;
+ public static final int OPTION_DHCP_SERVER_IDENTIFIER = 54;
+ public static final int OPTION_DHCP_PARAMETER_REQUEST_LIST = 55;
+ public static final int OPTION_DHCP_MESSAGE = 56;
+ public static final int OPTION_DHCP_MAXIMUM_MESSAGE_SIZE = 57;
+ public static final int OPTION_DHCP_RENEWAL_TIME = 58;
+ public static final int OPTION_DHCP_REBIND_TIME = 59;
+ public static final int OPTION_DHCP_CLASS_IDENTIFIER = 60;
+ public static final int OPTION_DHCP_CLIENT_IDENTIFIER = 61;
+
+
+ /**
+ *This inner class represent an entry in the Option Table
+ */
+
+ class DHCPOptionsEntry {
+ protected byte code;
+ protected byte length;
+ protected byte content[];
+
+ public DHCPOptionsEntry(byte entryCode, byte entryLength,
+ byte entryContent[]) {
+ this.code = entryCode;
+ this.length = entryLength;
+ this.content = entryContent;
+ }
+
+ @Override
+ public String toString() {
+ return "Code: " + this.code + "\nContent: " + new String(this.content);
+ }
+ }
+
+ private Hashtable<Byte, DHCPOptionsEntry> optionsTable = null;
+
+ public DHCPOptions() {
+ this.optionsTable = new Hashtable<Byte, DHCPOptionsEntry>();
+ }
+
+ /**
+ * Removes option with specified bytecode
+ * @param entryCode The code of option to be removed
+ */
+
+ public void removeOption(byte entryCode) {
+ this.optionsTable.remove(new Byte(entryCode));
+ }
+
+ /**
+ * Returns true if option code is set in list; false otherwise
+ * @param entryCode The node's option code
+ * @return true if option is set, otherwise false
+ */
+ public boolean contains(byte entryCode) {
+ return this.optionsTable.containsKey(new Byte(entryCode));
+ }
+
+ /**
+ * Determines if list is empty
+ * @return true if there are no options set, otherwise false
+ */
+ public boolean isEmpty() {
+ return this.optionsTable.isEmpty();
+ }
+
+ /**
+ * Fetches value of option by its option code
+ * @param entryCode The node's option code
+ * @return byte array containing the value of option entryCode.
+ * null is returned if option is not set.
+ */
+ public byte[] getOption(byte entryCode) {
+ if (this.contains(entryCode)) {
+ DHCPOptionsEntry ent = this.optionsTable.get(new Byte(entryCode));
+ return ent.content;
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * Changes an existing option to new value
+ * @param entryCode The node's option code
+ * @param value Content of node option
+ */
+ public void setOption(byte entryCode, byte value[]) {
+ DHCPOptionsEntry opt = new DHCPOptionsEntry(entryCode, (byte) value.length, value);
+ this.optionsTable.put(new Byte(entryCode), opt);
+ }
+
+ /**
+ * Returns the option value of a specified option code in a byte array
+ * @param length Length of option content
+ * @param position Location in array of option node
+ * @param options The byte array of options
+ * @return byte array containing the value for the option
+ */
+ private byte[] getArrayOption(int length, int position, byte options[]) {
+ byte value[] = new byte[length];
+ for (int i = 0; i < length; i++) {
+ value[i] = options[position + i];
+ }
+ return value;
+ }
+
+ /**
+ * Converts an options byte array to a linked list
+ * @param optionsArray The byte array representation of the options list
+ */
+ public void internalize(byte[] optionsArray) {
+
+ /* Assume options valid and correct */
+ int pos = 4; // ignore vendor magic cookie
+ byte code, length;
+ byte value[];
+
+ while (optionsArray[pos] != (byte) 255) { // until end option
+ code = optionsArray[pos++];
+ length = optionsArray[pos++];
+ value = getArrayOption(length, pos, optionsArray);
+ setOption(code, value);
+ pos += length; // increment position pointer
+ }
+ }
+
+ /**
+ * Converts a linked options list to a byte array
+ * @return array representation of optionsTable
+ */
+ // todo provide overflow return
+ public byte[] externalize() {
+ byte[] options = new byte[312];
+
+ options[0] = (byte) 99; // insert vendor magic cookie
+ options[1] = (byte) 130;
+ options[2] = (byte) 83;
+ options[3] = (byte) 99;
+
+ int position = 4;
+ Enumeration<DHCPOptionsEntry> e = this.optionsTable.elements();
+
+ while (e.hasMoreElements()) {
+ DHCPOptionsEntry entry = e.nextElement();
+ options[position++] = entry.code;
+ options[position++] = entry.length;
+ for (int i = 0; i < entry.length; ++i) {
+ options[position++] = entry.content[i];
+ }
+ }
+
+ options[position] = (byte) 255; // insert end option
+ return options;
+ }
+
+ /**
+ * Prints the options linked list: For testing only.
+ */
+ public void printList() {
+ System.out.println(this.optionsTable.toString());
+ }
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java
new file mode 100644
index 0000000..6dbe2bf
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java
@@ -0,0 +1,107 @@
+package com.btr.proxy.search.wpad.dhcp;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+/**
+ * This class represents a Socket for sending DHCP Messages
+ *
+ * @author Jason Goldschmidt and Simon Frankenberger
+ *
+ * @see java.net.DatagramSocket
+ */
+
+public class DHCPSocket extends DatagramSocket {
+ /**
+ * Default socket timeout (1 second)
+ */
+ private int SOCKET_TIMEOUT = 5000;
+
+ /**
+ * Default MTU (Maximum Transmission Unit) for ethernet (in bytes)
+ */
+ private int mtu = 1500;
+
+ /**
+ * Constructor for creating DHCPSocket on a specific port on the local
+ * machine.
+ *
+ * @param inPort The port for the application to bind.
+ *
+ * @throws SocketException As thrown by the {@link Socket} constructor
+ */
+ public DHCPSocket(int inPort) throws SocketException {
+ super(inPort);
+ setSoTimeout(this.SOCKET_TIMEOUT);
+ }
+
+ /**
+ * Sets the Maximum Transfer Unit for the UDP DHCP Packets to be set.
+ *
+ * @param inSize Integer representing desired MTU
+ */
+ public void setMTU(int inSize) {
+ this.mtu = inSize;
+ }
+
+ /**
+ * Returns the set MTU for this socket
+ *
+ * @return The Maximum Transfer Unit set for this socket
+ */
+ public int getMTU() {
+ return this.mtu;
+ }
+
+ /**
+ * Sends a DHCPMessage object to a predifined host.
+ *
+ * @param inMessage Well-formed DHCPMessage to be sent to a server
+ *
+ * @throws IOException If the message could not be sent.
+ */
+ public synchronized void send(DHCPMessage inMessage) throws IOException {
+ byte data[] = new byte[this.mtu];
+ data = inMessage.externalize();
+ InetAddress dest = null;
+ try {
+ dest = InetAddress.getByName(inMessage.getDestinationAddress());
+ }
+ catch (UnknownHostException e) {
+ }
+
+ DatagramPacket outgoing = new DatagramPacket(data, data.length, dest,
+ inMessage.getPort());
+
+ send(outgoing); // send outgoing message
+ }
+
+ /**
+ * Receives a datagram packet containing a DHCP Message into
+ * a DHCPMessage object.
+ *
+ * @return <code>true</code> if message is received,
+ * <code>false</code> if timeout occurs.
+ * @param outMessage DHCPMessage object to receive new message into
+ */
+ public synchronized boolean receive(DHCPMessage outMessage) {
+ try {
+ DatagramPacket incoming = new DatagramPacket(new byte[this.mtu],
+ this.mtu);
+ //gSocket.
+ receive(incoming); // block on receive for SOCKET_TIMEOUT
+
+ outMessage.internalize(incoming.getData());
+ }
+ catch (java.io.IOException e) {
+ e.printStackTrace();
+ return false;
+ } // end catch
+ return true;
+ }
+}