19ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk/*
29ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * Copyright (C) 2013 The Android Open Source Project
39ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk *
49ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * Licensed under the Apache License, Version 2.0 (the "License");
59ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * you may not use this file except in compliance with the License.
69ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * You may obtain a copy of the License at
79ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk *
89ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk *      http://www.apache.org/licenses/LICENSE-2.0
99ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk *
109ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * Unless required by applicable law or agreed to in writing, software
119ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * distributed under the License is distributed on an "AS IS" BASIS,
129ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * See the License for the specific language governing permissions and
149ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * limitations under the License.
159ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk */
16602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
17602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkpackage android.net;
18602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
19602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.os.ServiceManager;
20a48ad8bd858d6ffe77838a282dbf71e01967957cWink Savilleimport android.util.Log;
21602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
22602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport com.android.net.IProxyService;
23602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport com.google.android.collect.Lists;
24602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
25602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.io.IOException;
26602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.InetSocketAddress;
27602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.MalformedURLException;
28602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.Proxy;
29602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.Proxy.Type;
30602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.ProxySelector;
31602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.SocketAddress;
32602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.URI;
3308ec0e53b8ab0c3c6ab605c237fcc6df4cad2fcdPaul Jensenimport java.net.URISyntaxException;
34602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.util.List;
35602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
36602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk/**
37602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk * @hide
38602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk */
39602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkpublic class PacProxySelector extends ProxySelector {
40a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville    private static final String TAG = "PacProxySelector";
41602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    public static final String PROXY_SERVICE = "com.android.net.IProxyService";
424385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk    private static final String SOCKS = "SOCKS ";
434385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk    private static final String PROXY = "PROXY ";
444385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk
45602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private IProxyService mProxyService;
469ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    private final List<Proxy> mDefaultList;
47602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
48602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    public PacProxySelector() {
49602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        mProxyService = IProxyService.Stub.asInterface(
50602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                ServiceManager.getService(PROXY_SERVICE));
51a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville        if (mProxyService == null) {
52a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville            // Added because of b10267814 where mako is restarting.
539ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            Log.e(TAG, "PacManager: no proxy service");
54a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville        }
559ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
56602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
57602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
58602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    @Override
59602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    public List<Proxy> select(URI uri) {
60a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville        if (mProxyService == null) {
619ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            mProxyService = IProxyService.Stub.asInterface(
629ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    ServiceManager.getService(PROXY_SERVICE));
639ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        }
649ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        if (mProxyService == null) {
65a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville            Log.e(TAG, "select: no proxy service return NO_PROXY");
66a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville            return Lists.newArrayList(java.net.Proxy.NO_PROXY);
67a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville        }
68602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        String response = null;
69602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        String urlString;
70602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        try {
7108ec0e53b8ab0c3c6ab605c237fcc6df4cad2fcdPaul Jensen            // Strip path and username/password from URI so it's not visible to PAC script. The
7208ec0e53b8ab0c3c6ab605c237fcc6df4cad2fcdPaul Jensen            // path often contains credentials the app does not want exposed to a potentially
7308ec0e53b8ab0c3c6ab605c237fcc6df4cad2fcdPaul Jensen            // malicious PAC script.
7408ec0e53b8ab0c3c6ab605c237fcc6df4cad2fcdPaul Jensen            if (!"http".equalsIgnoreCase(uri.getScheme())) {
7508ec0e53b8ab0c3c6ab605c237fcc6df4cad2fcdPaul Jensen                uri = new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), "/", null, null);
7608ec0e53b8ab0c3c6ab605c237fcc6df4cad2fcdPaul Jensen            }
77602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            urlString = uri.toURL().toString();
7808ec0e53b8ab0c3c6ab605c237fcc6df4cad2fcdPaul Jensen        } catch (URISyntaxException e) {
7908ec0e53b8ab0c3c6ab605c237fcc6df4cad2fcdPaul Jensen            urlString = uri.getHost();
80602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        } catch (MalformedURLException e) {
81602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            urlString = uri.getHost();
82602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
83602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        try {
84602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            response = mProxyService.resolvePacFile(uri.getHost(), urlString);
8544c02590374bda5928669be0d14110b12c271461Andrei Kapishnikov        } catch (Exception e) {
8644c02590374bda5928669be0d14110b12c271461Andrei Kapishnikov            Log.e(TAG, "Error resolving PAC File", e);
87602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
889ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        if (response == null) {
899ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            return mDefaultList;
909ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        }
91602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
92602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        return parseResponse(response);
93602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
94602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
95602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private static List<Proxy> parseResponse(String response) {
96602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        String[] split = response.split(";");
97602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        List<Proxy> ret = Lists.newArrayList();
98602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        for (String s : split) {
99602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            String trimmed = s.trim();
100602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            if (trimmed.equals("DIRECT")) {
101602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                ret.add(java.net.Proxy.NO_PROXY);
1024385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk            } else if (trimmed.startsWith(PROXY)) {
1034385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk                Proxy proxy = proxyFromHostPort(Type.HTTP, trimmed.substring(PROXY.length()));
1044385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk                if (proxy != null) {
1054385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk                    ret.add(proxy);
1064385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk                }
1074385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk            } else if (trimmed.startsWith(SOCKS)) {
1084385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk                Proxy proxy = proxyFromHostPort(Type.SOCKS, trimmed.substring(SOCKS.length()));
1094385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk                if (proxy != null) {
1104385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk                    ret.add(proxy);
111602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                }
112602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            }
113602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
114602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        if (ret.size() == 0) {
115602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            ret.add(java.net.Proxy.NO_PROXY);
116602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
117602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        return ret;
118602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
119602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
1204385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk    private static Proxy proxyFromHostPort(Proxy.Type type, String hostPortString) {
1214385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk        try {
1224385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk            String[] hostPort = hostPortString.split(":");
1234385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk            String host = hostPort[0];
1244385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk            int port = Integer.parseInt(hostPort[1]);
1254385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk            return new Proxy(type, InetSocketAddress.createUnresolved(host, port));
1264385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk        } catch (NumberFormatException|ArrayIndexOutOfBoundsException e) {
1274385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk            Log.d(TAG, "Unable to parse proxy " + hostPortString + " " + e);
1284385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk            return null;
1294385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk        }
1304385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk    }
1314385af32eff1b99802e6ce233a2b10d002bf4eb1Jason Monk
132602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    @Override
133602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    public void connectFailed(URI uri, SocketAddress address, IOException failure) {
134602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
135602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
136602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
137602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk}
138