summaryrefslogblamecommitdiffstats
path: root/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java
blob: bb56cc026036246a7ab2f34b1d60b746dcba0053 (plain) (tree)


























































































































































































































































































































                                                                                                                                     
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;
	}

}