Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b04d298 authored by Hung-ying Tyan's avatar Hung-ying Tyan
Browse files

Revise VpnService to use new vpn.* properties.

* Changes
  + Make VpnService get dns info from vpn.dns1/dns2 and set
    net.dns1/dns2.
  + Make VpnService wait on vpn.up instead of monitoring on ip-up-vpn.
  + Change commands sent to racoon.
  + Remove original sendCommand() from AndroidServiceProxy and rename
    sendCommand2() to sendCommand().
  + Add L2tpIpsecPskService.java.
  + Add short stop after sending ipsec command.
parent 5c7d9e81
Loading
Loading
Loading
Loading
+3 −18
Original line number Diff line number Diff line
@@ -78,20 +78,10 @@ public class AndroidServiceProxy extends ProcessProxy {

    /**
     * Sends a command with arguments to the service through the control socket.
     * Each argument is sent as a C-style zero-terminated string.
     */
    public void sendCommand(String ...args) throws IOException {
        OutputStream out = getControlSocketOutput();
        for (String arg : args) outputString(out, arg);
        checkSocketResult();
    }

    /**
     * Sends a command with arguments to the service through the control socket.
     */
    public void sendCommand2(String ...args) throws IOException {
        OutputStream out = getControlSocketOutput();
        for (String arg : args) outputString2(out, arg);
        out.write(END_OF_ARGUMENTS);
        out.flush();
        checkSocketResult();
@@ -128,8 +118,9 @@ public class AndroidServiceProxy extends ProcessProxy {

                        if (data == 0) {
                            // re-establish the connection:
                            // synchronized here so that checkSocketResult() returns
                            // when new mKeepaliveSocket is available for next cmd
                            // synchronized here so that checkSocketResult()
                            // returns when new mKeepaliveSocket is available for
                            // next cmd
                            synchronized (this) {
                                setResultAndCloseControlSocket((byte) data);
                                s = mKeepaliveSocket = createServiceSocket();
@@ -244,12 +235,6 @@ public class AndroidServiceProxy extends ProcessProxy {
    }

    private void outputString(OutputStream out, String s) throws IOException {
        out.write(s.getBytes());
        out.write(0);
        out.flush();
    }

    private void outputString2(OutputStream out, String s) throws IOException {
        byte[] bytes = s.getBytes();
        out.write(bytes.length);
        out.write(bytes);
+49 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2009, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.vpn;

import android.net.vpn.L2tpIpsecPskProfile;

import java.io.IOException;

/**
 * The service that manages the preshared key based L2TP-over-IPSec VPN
 * connection.
 */
class L2tpIpsecPskService extends VpnService<L2tpIpsecPskProfile> {
    private static final String IPSEC_DAEMON = "racoon";

    @Override
    protected void connect(String serverIp, String username, String password)
            throws IOException {
        String hostIp = getHostIp();
        L2tpIpsecPskProfile p = getProfile();

        // IPSEC
        AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
        ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT,
                p.getPresharedKey());

        sleep(2000); // 2 seconds

        // L2TP
        MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp,
                L2tpService.L2TP_PORT,
                (p.isSecretEnabled() ? p.getSecretString() : null),
                username, password);
    }
}
+5 −6
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ import android.security.Keystore;
import java.io.IOException;

/**
 * The service that manages the L2TP-over-IPSec VPN connection.
 * The service that manages the certificate based L2TP-over-IPSec VPN connection.
 */
class L2tpIpsecService extends VpnService<L2tpIpsecProfile> {
    private static final String IPSEC_DAEMON = "racoon";
@@ -34,11 +34,10 @@ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> {

        // IPSEC
        AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
        ipsecService.sendCommand(
                String.format("SETKEY %s %s", hostIp, serverIp));
        ipsecService.sendCommand(String.format("SET_CERTS %s %s %s %s",
                serverIp, getCaCertPath(), getUserCertPath(),
                getUserkeyPath()));
        ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT,
                getUserkeyPath(), getUserCertPath(), getCaCertPath());

        sleep(2000); // 2 seconds

        // L2TP
        L2tpIpsecProfile p = getProfile();
+1 −1
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ class MtpdHelper {
        addPppArguments(vpnService, args, serverIp, username, password);

        AndroidServiceProxy mtpd = vpnService.startService(MTPD_SERVICE);
        mtpd.sendCommand2(args.toArray(new String[args.size()]));
        mtpd.sendCommand(args.toArray(new String[args.size()]));
    }

    private static void addPppArguments(VpnService<?> vpnService,
+45 −173
Original line number Diff line number Diff line
@@ -24,14 +24,12 @@ import android.net.NetworkUtils;
import android.net.vpn.VpnManager;
import android.net.vpn.VpnProfile;
import android.net.vpn.VpnState;
import android.os.FileObserver;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.util.ArrayList;
@@ -43,21 +41,18 @@ import java.util.List;
 */
abstract class VpnService<E extends VpnProfile> {
    private static final int NOTIFICATION_ID = 1;
    private static final String PROFILES_ROOT = VpnManager.PROFILES_PATH + "/";
    public static final String DEFAULT_CONFIG_PATH = "/etc";

    private static final int DNS_TIMEOUT = 3000; // ms
    private static final String DNS1 = "net.dns1";
    private static final String DNS2 = "net.dns2";
    private static final String VPN_DNS1 = "vpn.dns1";
    private static final String VPN_DNS2 = "vpn.dns2";
    private static final String VPN_UP = "vpn.up";
    private static final String VPN_IS_UP = "1";
    private static final String VPN_IS_DOWN = "0";

    private static final String REMOTE_IP = "net.ipremote";
    private static final String DNS_DOMAIN_SUFFICES = "net.dns.search";
    private static final String SERVER_IP = "net.vpn.server_ip";

    private static final int VPN_TIMEOUT = 30000; // milliseconds
    private static final int ONE_SECOND = 1000; // milliseconds
    private static final int FIVE_SECOND = 5000; // milliseconds

    private static final String LOGWRAPPER = "/system/bin/logwrapper";
    private final String TAG = VpnService.class.getSimpleName();

    E mProfile;
@@ -76,13 +71,6 @@ abstract class VpnService<E extends VpnProfile> {

    private long mStartTime; // VPN connection start time

    // monitors if the VPN connection is sucessfully established
    private FileMonitor mConnectMonitor;

    // watch dog timer; fired up if the connection cannot be established within
    // VPN_TIMEOUT
    private Object mWatchdog;

    // for helping managing multiple Android services
    private ServiceHelper mServiceHelper = new ServiceHelper();

@@ -110,19 +98,6 @@ abstract class VpnService<E extends VpnProfile> {
        return mServiceHelper.startService(serviceName);
    }

    protected String getPppOptionFilePath() throws IOException {
        String subpath = getProfileSubpath("/ppp/peers");
        String[] kids = new File(subpath).list();
        if ((kids == null) || (kids.length == 0)) {
            throw new IOException("no option file found in " + subpath);
        }
        if (kids.length > 1) {
            Log.w(TAG, "more than one option file found in " + subpath
                    + ", arbitrarily choose " + kids[0]);
        }
        return subpath + "/" + kids[0];
    }

    /**
     * Returns the VPN profile associated with the connection.
     */
@@ -130,23 +105,6 @@ abstract class VpnService<E extends VpnProfile> {
        return mProfile;
    }

    /**
     * Returns the profile path where configuration files reside.
     */
    protected String getProfilePath() throws IOException {
        String path = PROFILES_ROOT + mProfile.getId();
        File dir = new File(path);
        if (!dir.exists()) throw new IOException("Profile dir does not exist");
        return path;
    }

    /**
     * Returns the path where default configuration files reside.
     */
    protected String getDefaultConfigPath() throws IOException {
        return DEFAULT_CONFIG_PATH;
    }

    /**
     * Returns the host IP for establishing the VPN connection.
     */
@@ -177,14 +135,6 @@ abstract class VpnService<E extends VpnProfile> {
        throw new IOException("Default gateway is not available");
    }

    /**
     * Returns the path of the script file that is executed when the VPN
     * connection is established.
     */
    protected String getConnectMonitorFile() {
        return "/etc/ppp/ip-up-vpn";
    }

    /**
     * Sets the system property. The method is blocked until the value is
     * settled in.
@@ -222,10 +172,10 @@ abstract class VpnService<E extends VpnProfile> {
        broadcastConnectivity(VpnState.CONNECTING);

        String serverIp = getIp(getProfile().getServerName());
        setSystemProperty(SERVER_IP, serverIp);
        onBeforeConnect();

        onBeforeConnect();
        connect(serverIp, username, password);
        waitUntilConnectedOrTimedout();
    }

    synchronized void onDisconnect(boolean cleanUpServices) {
@@ -259,39 +209,36 @@ abstract class VpnService<E extends VpnProfile> {
        }
    }

    private void createConnectMonitor() {
        mConnectMonitor = new FileMonitor(getConnectMonitorFile(),
                new Runnable() {
                    public void run() {
                        onConnectMonitorTriggered();
                    }
                });
    }

    private void onBeforeConnect() {
        mNotification.disableNotification();

        createConnectMonitor();
        mConnectMonitor.startWatching();
        saveOriginalDnsProperties();
        SystemProperties.set(VPN_DNS1, "-");
        SystemProperties.set(VPN_DNS2, "-");
        SystemProperties.set(VPN_UP, VPN_IS_DOWN);
        Log.d(TAG, "       VPN UP: " + SystemProperties.get(VPN_UP));
    }

        mWatchdog = startTimer(VPN_TIMEOUT, new Runnable() {
            public void run() {
                synchronized (VpnService.this) {
    private void waitUntilConnectedOrTimedout() {
        sleep(2000); // 2 seconds
        for (int i = 0; i < 60; i++) {
            if (VPN_IS_UP.equals(SystemProperties.get(VPN_UP))) {
                onConnected();
                return;
            }
            sleep(500); // 0.5 second
        }

        synchronized (this) {
            if (mState == VpnState.CONNECTING) {
                        Log.d(TAG, "       watchdog timer is fired !!");
                Log.d(TAG, "       connecting timed out !!");
                onError();
            }
        }
    }
        });
    }

    private synchronized void onConnectMonitorTriggered() {
        Log.d(TAG, "onConnectMonitorTriggered()");
    private synchronized void onConnected() {
        Log.d(TAG, "onConnected()");

        stopTimer(mWatchdog);
        mConnectMonitor.stopWatching();
        saveVpnDnsProperties();
        saveAndSetDomainSuffices();
        startConnectivityMonitor();
@@ -310,8 +257,6 @@ abstract class VpnService<E extends VpnProfile> {

        restoreOriginalDnsProperties();
        restoreOriginalDomainSuffices();
        if (mConnectMonitor != null) mConnectMonitor.stopWatching();
        if (mWatchdog != null) stopTimer(mWatchdog);
        mState = VpnState.IDLE;
        broadcastConnectivity(VpnState.IDLE);

@@ -345,13 +290,6 @@ abstract class VpnService<E extends VpnProfile> {
        }
    }

    private void saveOriginalDnsProperties() {
        mOriginalDns1 = SystemProperties.get(DNS1);
        mOriginalDns2 = SystemProperties.get(DNS2);
        Log.d(TAG, String.format("save original dns prop: %s, %s",
                mOriginalDns1, mOriginalDns2));
    }

    private void restoreOriginalDnsProperties() {
        // restore only if they are not overridden
        if (mVpnDns1.equals(SystemProperties.get(DNS1))) {
@@ -365,15 +303,21 @@ abstract class VpnService<E extends VpnProfile> {
    }

    private void saveVpnDnsProperties() {
        mVpnDns1 = mVpnDns2 = "";
        mOriginalDns1 = mOriginalDns2 = "";
        for (int i = 0; i < 10; i++) {
            mVpnDns1 = SystemProperties.get(DNS1);
            mVpnDns2 = SystemProperties.get(DNS2);
            if (mVpnDns1.equals(mOriginalDns1)) {
            mVpnDns1 = SystemProperties.get(VPN_DNS1);
            mVpnDns2 = SystemProperties.get(VPN_DNS2);
            if (mOriginalDns1.equals(mVpnDns1)) {
                Log.d(TAG, "wait for vpn dns to settle in..." + i);
                sleep(500);
            } else {
                Log.d(TAG, String.format("save vpn dns prop: %s, %s",
                mOriginalDns1 = SystemProperties.get(DNS1);
                mOriginalDns2 = SystemProperties.get(DNS2);
                SystemProperties.set(DNS1, mVpnDns1);
                SystemProperties.set(DNS2, mVpnDns2);
                Log.d(TAG, String.format("save original dns prop: %s, %s",
                        mOriginalDns1, mOriginalDns2));
                Log.d(TAG, String.format("set vpn dns prop: %s, %s",
                        mVpnDns1, mVpnDns2));
                return;
            }
@@ -381,23 +325,11 @@ abstract class VpnService<E extends VpnProfile> {
        Log.e(TAG, "saveVpnDnsProperties(): DNS not updated??");
    }

    private void restoreVpnDnsProperties() {
        if (isNullOrEmpty(mVpnDns1) && isNullOrEmpty(mVpnDns2)) {
            return;
        }
        Log.d(TAG, String.format("restore vpn dns prop: %s --> %s",
                SystemProperties.get(DNS1), mVpnDns1));
        Log.d(TAG, String.format("restore vpn dns prop: %s --> %s",
                SystemProperties.get(DNS2), mVpnDns2));
        SystemProperties.set(DNS1, mVpnDns1);
        SystemProperties.set(DNS2, mVpnDns2);
    }

    private void saveAndSetDomainSuffices() {
        mOriginalDomainSuffices = SystemProperties.get(DNS_DOMAIN_SUFFICES);
        Log.d(TAG, "save original dns search: " + mOriginalDomainSuffices);
        String list = mProfile.getDomainSuffices();
        if (!isNullOrEmpty(list)) {
        if (!TextUtils.isEmpty(list)) {
            SystemProperties.set(DNS_DOMAIN_SUFFICES, list);
        }
    }
@@ -423,7 +355,7 @@ abstract class VpnService<E extends VpnProfile> {
                            if (mState != VpnState.CONNECTED) break;
                            mNotification.update();
                            checkConnectivity();
                            VpnService.this.wait(ONE_SECOND);
                            VpnService.this.wait(1000); // 1 second
                        }
                    }
                } catch (InterruptedException e) {
@@ -446,32 +378,6 @@ abstract class VpnService<E extends VpnProfile> {
        }
    }

    private Object startTimer(final int milliseconds, final Runnable task) {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                Log.d(TAG, "watchdog timer started");
                Thread t = Thread.currentThread();
                try {
                    synchronized (t) {
                        t.wait(milliseconds);
                    }
                    task.run();
                } catch (InterruptedException e) {
                    // ignored
                }
                Log.d(TAG, "watchdog timer stopped");
            }
        });
        thread.start();
        return thread;
    }

    private void stopTimer(Object timer) {
        synchronized (timer) {
            timer.notify();
        }
    }

    private String reallyGetHostIp() throws IOException {
        Enumeration<NetworkInterface> ifces =
                NetworkInterface.getNetworkInterfaces();
@@ -487,33 +393,13 @@ abstract class VpnService<E extends VpnProfile> {
        throw new IOException("Host IP is not available");
    }

    private String getProfileSubpath(String subpath) throws IOException {
        String path = getProfilePath() + subpath;
        if (new File(path).exists()) {
            return path;
        } else {
            Log.w(TAG, "Profile subpath does not exist: " + path
                    + ", use default one");
            String path2 = getDefaultConfigPath() + subpath;
            if (!new File(path2).exists()) {
                throw new IOException("Profile subpath does not exist at "
                        + path + " or " + path2);
            }
            return path2;
        }
    }

    private void sleep(int ms) {
    protected void sleep(int ms) {
        try {
            Thread.currentThread().sleep(ms);
        } catch (InterruptedException e) {
        }
    }

    private static boolean isNullOrEmpty(String message) {
        return ((message == null) || (message.length() == 0));
    }

    private InetAddress toInetAddress(int addr) throws IOException {
        byte[] aa = new byte[4];
        for (int i= 0; i < aa.length; i++) {
@@ -564,20 +450,6 @@ abstract class VpnService<E extends VpnProfile> {
        }
    }

    private class FileMonitor extends FileObserver {
        private Runnable mCallback;

        FileMonitor(String path, Runnable callback) {
            super(path, CLOSE_NOWRITE);
            mCallback = callback;
        }

        @Override
        public void onEvent(int event, String path) {
            if ((event & CLOSE_NOWRITE) > 0) mCallback.run();
        }
    }

    // Helper class for showing, updating notification.
    private class NotificationHelper {
        void update() {