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. *

* Note: at the moment only the DNS name guessing schema is implemented. All * others are missing. *

*

* For more information about WPAD: Web_Proxy_Autodiscovery_Protocol *

*

* Outdated RFC draft: draft-ietf-wrec-wpad-01.txt *

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