1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
11 import org.ibex.util.*;
12 import org.ibex.net.*;
13 import org.ibex.crypto.*;
15 public class ProxyAutoConfig {
17 public static class PacProxy extends HTTP.Proxy {
18 private final JS pacFunc;
19 public PacProxy(JS pacFunc) { this.pacFunc = pacFunc; }
20 public Socket attempt(String url, String host, HTTP http) {
21 if (Log.verbose) Log.info(this, "evaluating PAC script");
24 pac = JSU.toString(pacFunc.call(null, new JS[] { JSU.S(url), JSU.S(host) }));
25 if (Log.verbose) Log.info(this, " PAC script returned \"" + pac + "\"");
26 } catch (Throwable e) {
27 if (Log.on) Log.info(this, "PAC script threw exception " + e);
30 StringTokenizer st = new StringTokenizer(pac, ";", false);
31 while (st.hasMoreTokens()) {
32 String token = st.nextToken().trim();
33 if (Log.verbose) Log.info(this, " trying \"" + token + "\"...");
36 if (token.startsWith("DIRECT"))
37 ret = attemptDirect(http);
38 else if (token.startsWith("PROXY"))
39 ret = attemptHttpProxy(http,
40 token.substring(token.indexOf(' ') + 1, token.indexOf(':')),
41 Integer.parseInt(token.substring(token.indexOf(':') + 1)));
42 else if (token.startsWith("SOCKS"))
43 ret = attemptSocksProxy(http,
44 token.substring(token.indexOf(' ') + 1, token.indexOf(':')),
45 Integer.parseInt(token.substring(token.indexOf(':') + 1)));
46 if (ret != null) return ret;
47 } catch (Throwable e) {
48 if (Log.on) Log.info(this, "attempt at \"" + token + "\" failed due to " + e + "; trying next token");
51 if (Log.on) Log.info(this, "all PAC results exhausted");
56 public static class ProxyAutoConfigRootScope extends JSScope.Global {
58 public ProxyAutoConfigRootScope() { super(); }
60 private final static JS.Method METHOD = new JS.Method();
61 public JS get(JS name) throws JSExn {
63 case "isPlainHostName": return METHOD;
64 case "dnsDomainIs": return METHOD;
65 case "localHostOrDomainIs": return METHOD;
66 case "isResolvable": return METHOD;
67 case "isInNet": return METHOD;
68 case "dnsResolve": return METHOD;
69 case "myIpAddress": return METHOD;
70 case "dnsDomainLevels": return METHOD;
71 case "shExpMatch": return METHOD;
72 case "weekdayRange": return METHOD;
73 case "dateRange": return METHOD;
74 case "timeRange": return METHOD;
75 case "ProxyConfig": return ProxyConfig;
77 return super.get(name);
80 private static final JS proxyConfigBindings = new JS.Obj();
81 private static final JS ProxyConfig = new JS.Obj() {
82 public JS get(JS name) throws JSExn {
84 case "bindings": return proxyConfigBindings;
90 public JS call(JS method, JS[] args) throws JSExn {
92 case "isPlainHostName": return JSU.B(JSU.toString(args[0]).indexOf('.') == -1);
93 case "dnsDomainIs": return JSU.B(JSU.toString(args[0]).endsWith(JSU.toString(args[1])));
94 case "localHostOrDomainIs":
95 return JSU.B(args[0].equals(args[1]) ||
96 (JSU.toString(args[0]).indexOf('.') == -1 && JSU.toString(args[1]).startsWith(JSU.toString(args[0]))));
97 case "isResolvable": try {
98 return JSU.B(InetAddress.getByName(JSU.toString(args[0])) != null);
99 } catch (UnknownHostException e) { return JSU.F; }
101 if (args.length != 3) return JSU.F;
103 byte[] host = InetAddress.getByName(JSU.toString(args[0])).getAddress();
104 byte[] net = InetAddress.getByName(JSU.toString(args[1])).getAddress();
105 byte[] mask = InetAddress.getByName(args[2].toString()).getAddress();
106 return JSU.B((host[0] & mask[0]) == net[0] &&
107 (host[1] & mask[1]) == net[1] &&
108 (host[2] & mask[2]) == net[2] &&
109 (host[3] & mask[3]) == net[3]);
110 } catch (Exception e) {
111 throw new JSExn("exception in isInNet(): " + e);
115 return JSU.S(InetAddress.getByName(JSU.toString(args[0])).getHostAddress());
116 } catch (UnknownHostException e) {
121 return JSU.S(InetAddress.getLocalHost().getHostAddress());
122 } catch (UnknownHostException e) {
123 if (Log.on) Log.info(this, "strange... host does not know its own address");
126 case "dnsDomainLevels":
127 String s = JSU.toString(args[0]);
129 while((i = s.indexOf('.', i)) != -1) i++;
132 StringTokenizer st = new StringTokenizer(JSU.toString(args[1]), "*", false);
133 String[] arr = new String[st.countTokens()];
134 String s = JSU.toString(args[0]);
135 for (int i=0; st.hasMoreTokens(); i++) arr[i] = st.nextToken();
136 return JSU.B(match(arr, s, 0));
138 TimeZone tz = (args.length < 3 || args[2] == null || !args[2].equals("GMT")) ?
139 TimeZone.getTimeZone("UTC") : TimeZone.getDefault();
140 Calendar c = new GregorianCalendar();
142 c.setTime(new java.util.Date());
143 java.util.Date d = c.getTime();
144 int day = d.getDay();
145 String d1s = JSU.toString(args[0]).toUpperCase();
147 for(int i=0; i<days.length; i++) if (days[i].equals(d1s)) d1 = i;
149 if (args.length == 1) return JSU.B(d1 == day);
151 String d2s = JSU.toString(args[1]).toUpperCase();
152 for(int i=0; i<days.length; i++) if (days[i].equals(d2s)) d2 = i;
154 return JSU.B((d1 <= d2 && day >= d1 && day <= d2) || (d1 > d2 && (day >= d1 || day <= d2)));
156 case "dateRange": throw new JSExn("Ibex does not support dateRange() in PAC scripts");
157 case "timeRange": throw new JSExn("Ibex does not support timeRange() in PAC scripts");
159 return super.call(method, args);
161 private static boolean match(String[] arr, String s, int index) {
162 if (index >= arr.length) return true;
163 for(int i=0; i<s.length(); i++) {
164 String s2 = s.substring(i);
165 if (s2.startsWith(arr[index]) && match(arr, s2.substring(arr[index].length()), index + 1)) return true;
169 public static String[] days = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" };
172 public static JS proxyAutoConfigRootScope = new ProxyAutoConfigRootScope();
173 public static JS getProxyAutoConfigFunction(String url) {
175 BufferedReader br = new BufferedReader(new InputStreamReader(new HTTP(url, true).GET(null, null)));
178 while((s = br.readLine()) != null) script += s + "\n";
179 if (Log.on) Log.info(Proxy.class, "successfully retrieved WPAD PAC:");
180 if (Log.on) Log.info(Proxy.class, script);
183 Vector carpHosts = new Vector();
184 for(int i=0; i<script.length(); i++)
185 if (script.regionMatches(i, "new Node(", 0, 9)) {
186 String host = script.substring(i + 10, script.indexOf('\"', i + 11));
187 if (Log.on) Log.info(Proxy.class, "Detected MS Proxy Server CARP Script, Host=" + host);
188 carpHosts.addElement(host);
190 if (carpHosts.size() > 0) {
191 script = "function FindProxyForURL(url, host) {\nreturn \"";
192 for(int i=0; i<carpHosts.size(); i++)
193 script += "PROXY " + carpHosts.elementAt(i) + "; ";
195 if (Log.on) Log.info(Proxy.class, "DeCARPed PAC script:");
196 if (Log.on) Log.info(Proxy.class, script);
199 JS scr = JSU.fromReader("PAC script at " + url, 0, new StringReader(script));
200 JSU.cloneWithNewGlobalScope(scr, proxyAutoConfigRootScope).call(null, null);
201 return (JS)proxyAutoConfigRootScope.get(JSU.S("FindProxyForURL"));
202 } catch (Exception e) {
204 Log.info(ProxyAutoConfig.class, "WPAD detection failed due to:");
205 if (e instanceof JSExn) {
207 org.ibex.js.JSArray arr = new org.ibex.js.JSArray();
208 arr.add(((JSExn)e).getObject());
209 } catch (Exception e2) {
210 Log.info(ProxyAutoConfig.class, e);
213 else Log.info(ProxyAutoConfig.class, e);