From 3e4d8f433239c40311037616b1b8833a06651ae0 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Mon, 16 Apr 2012 19:21:14 +0200 Subject: Initial import --- src/de/blinkt/openvpn/AboutActivity.java | 22 ++ src/de/blinkt/openvpn/FileSelectLayout.java | 75 ++++ src/de/blinkt/openvpn/LogWindow.java | 37 ++ src/de/blinkt/openvpn/OpenVPN.java | 78 ++++ src/de/blinkt/openvpn/OpenVPNClient.java | 567 ++++++++++++++++++++++++++++ src/de/blinkt/openvpn/OpenVpnService.java | 203 ++++++++++ src/de/blinkt/openvpn/VpnProfile.java | 8 + 7 files changed, 990 insertions(+) create mode 100644 src/de/blinkt/openvpn/AboutActivity.java create mode 100644 src/de/blinkt/openvpn/FileSelectLayout.java create mode 100644 src/de/blinkt/openvpn/LogWindow.java create mode 100644 src/de/blinkt/openvpn/OpenVPN.java create mode 100644 src/de/blinkt/openvpn/OpenVPNClient.java create mode 100644 src/de/blinkt/openvpn/OpenVpnService.java create mode 100644 src/de/blinkt/openvpn/VpnProfile.java (limited to 'src/de/blinkt') diff --git a/src/de/blinkt/openvpn/AboutActivity.java b/src/de/blinkt/openvpn/AboutActivity.java new file mode 100644 index 00000000..0361d67c --- /dev/null +++ b/src/de/blinkt/openvpn/AboutActivity.java @@ -0,0 +1,22 @@ +package de.blinkt.openvpn; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; + +public class AboutActivity extends Activity implements OnClickListener { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.about); + + //findViewById(R.layout.about).setOnClickListener(this); + } + + @Override + public void onClick(View v) { + finish(); + } +} diff --git a/src/de/blinkt/openvpn/FileSelectLayout.java b/src/de/blinkt/openvpn/FileSelectLayout.java new file mode 100644 index 00000000..0615b2ab --- /dev/null +++ b/src/de/blinkt/openvpn/FileSelectLayout.java @@ -0,0 +1,75 @@ +package de.blinkt.openvpn; + +import com.lamerman.FileDialog; +import com.lamerman.SelectionMode; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +import android.view.View; +import android.view.View.OnClickListener; + +public class FileSelectLayout extends LinearLayout implements OnClickListener { + + private TextView mData; + private Activity mActivity; + private int mTaskId; + private Button mSelectButton; + + public FileSelectLayout( Context context,AttributeSet attrset) { + super(context,attrset); + inflate(getContext(), R.layout.file_select, this); + + TypedArray ta = context.obtainStyledAttributes(attrset,R.styleable.FileSelectLayout); + + String title = ta.getString(R.styleable.FileSelectLayout_title); + + TextView tview = (TextView) findViewById(R.id.file_title); + tview.setText(title); + + mData = (TextView) findViewById(R.id.file_selected_item); + mSelectButton = (Button) findViewById(R.id.file_select_button); + mSelectButton.setOnClickListener(this); + + } + + public void setActivity(Activity a, int i) + { + mTaskId = i; + mActivity = a; + } + + public void getCertificateFileDialog() { + Intent startFC = new Intent(getContext(),FileDialog.class); + startFC.putExtra(FileDialog.START_PATH, "/sdcard"); + startFC.putExtra(FileDialog.SELECTION_MODE, SelectionMode.MODE_OPEN); + + mActivity.startActivityForResult(startFC,mTaskId); + } + + + public String getData() { + return mData.getText().toString(); + } + + public void setData(String data) { + mData.setText(data); + + } + + @Override + public void onClick(View v) { + if(v == mSelectButton) { + getCertificateFileDialog(); + } + } + + + +} diff --git a/src/de/blinkt/openvpn/LogWindow.java b/src/de/blinkt/openvpn/LogWindow.java new file mode 100644 index 00000000..a26da2ba --- /dev/null +++ b/src/de/blinkt/openvpn/LogWindow.java @@ -0,0 +1,37 @@ +package de.blinkt.openvpn; + +import android.app.ListActivity; +import android.os.Bundle; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.ListView; + + + +public class LogWindow extends ListActivity implements OnItemClickListener { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //setListAdapter(new ArrayAdapter(this, R.layout.log_entry, COUNTRIES)); + setListAdapter(new ArrayAdapter(this, R.layout.log_entry, OpenVPN.getlogbuffer())); + //setListAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, OpenVPN.logbuffer)); + + ListView lv = getListView(); + lv.setTextFilterEnabled(true); + + lv.setOnItemClickListener(this); + } + + @Override + public void onItemClick(AdapterView parent, View view, + int position, long id) { + // When clicked, show a toast with the TextView text + //Toast.makeText(getApplicationContext(), ((TextView) view).getText(), + //Toast.LENGTH_SHORT).show(); + + setListAdapter(new ArrayAdapter(this, R.layout.log_entry, OpenVPN.getlogbuffer())); + } +} diff --git a/src/de/blinkt/openvpn/OpenVPN.java b/src/de/blinkt/openvpn/OpenVPN.java new file mode 100644 index 00000000..7385726b --- /dev/null +++ b/src/de/blinkt/openvpn/OpenVPN.java @@ -0,0 +1,78 @@ +package de.blinkt.openvpn; + +import java.util.LinkedList; + +import android.os.ParcelFileDescriptor; +import android.util.Log; + +public class OpenVPN { + private static OpenVpnService mOpenVpnService; + private static String localip; + private static final int MAXLOGENTRIES = 500; + public static native int startOpenVPNThread(); + public static native int startOpenVPNThreadArgs(String argv[]); + private static final String TAG = "OpenVpn"; + + + public static LinkedList logbuffer = new LinkedList(); + private static int counter=0; + + + static { + System.loadLibrary("crypto"); + System.loadLibrary("ssl"); + System.loadLibrary("lzo"); + System.loadLibrary("openvpn"); + } + + static void addRoute(String dest,String mask, String gw) { + Log.i("openvpn" ,"Got Routing information " + dest + " " + mask + " " + gw ); + } + + synchronized static void logMessage(int level,String prefix, String message) + { + logbuffer.addFirst(prefix + " " + message); + if(logbuffer.size()>MAXLOGENTRIES) + logbuffer.removeLast(); + + // The garbage collector does not collect the String from native + // but kills me for logging 100 messages with too many references :( + // Force GC how and then to kill loose ends + if(counter++ % 50==0) + System.gc(); + } + + + static void addInterfaceInfo(int mtu, String local, String remote) + { + Log.i("openvpn","Got interface info M" + mtu + " L: " + local + "R: " + remote); + localip=local; + } + + public static void setCallback(OpenVpnService openVpnService) { + mOpenVpnService = openVpnService; + } + + public static boolean protectSocket (int sockfd) + { + boolean p = mOpenVpnService.protect(sockfd); + if(p) + Log.d("openvpn","Protected socket "+ sockfd); + else + Log.e("openvpn","Error protecting socket "+ sockfd); + return p; + } + + public static int openTunDevice() { + Log.d(TAG,"Opening tun device"); + ParcelFileDescriptor pfd = mOpenVpnService.openTun(localip); + return pfd.detachFd(); + } + //! Dummy method being called to force loading of JNI Libraries + public static void foo() { } + + synchronized public static String[] getlogbuffer() { + return (String[]) logbuffer.toArray(new String[logbuffer.size()]); + + } +} diff --git a/src/de/blinkt/openvpn/OpenVPNClient.java b/src/de/blinkt/openvpn/OpenVPNClient.java new file mode 100644 index 00000000..af949c2d --- /dev/null +++ b/src/de/blinkt/openvpn/OpenVPNClient.java @@ -0,0 +1,567 @@ +/* + * Copyright (C) 2011 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 de.blinkt.openvpn; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Random; +import java.util.Vector; + +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.VpnService; +import android.os.Bundle; +import android.os.Handler; +import android.os.Handler.Callback; +import android.os.Message; +import android.security.KeyChain; +import android.security.KeyChainAliasCallback; +import android.security.KeyChainException; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.ToggleButton; + +import com.lamerman.FileDialog; + +import de.blinkt.openvpn.R.id; + +public class OpenVPNClient extends Activity implements View.OnClickListener, OnItemSelectedListener, Callback, OnCheckedChangeListener { + private static final String TAG = "OpenVpnClient"; + + + private static final int START_OPENVPN = 0; + private static final int CHOOSE_FILE_OFFSET = 1000; + private static final int UPDATE_ALIAS = 20; + + private static final String PREFS_NAME = "OVPN_SERVER"; + + private static final String OVPNCONFIGFILE = "android.conf"; + private static final String OVPNCONFIGPKCS12 = "android.pkcs12"; + + + private TextView mServerAddress; + private TextView mServerPort; + private FileSelectLayout mClientCert; + private FileSelectLayout mCaCert; + private FileSelectLayout mClientKey; + private TextView mAliasName; + private CheckBox mUseLzo; + private ToggleButton mTcpUdp; + private Spinner mType; + private String certalias; + private FileSelectLayout mpkcs12; + private TextView mPKCS12Password; + + private Handler mHandler; + + + private CheckBox mUseTlsAuth; + + + private CheckBox mShowAdvanced; + + + private FileSelectLayout mTlsFile; + + private HashMap fileselects = new HashMap(); + + + private Spinner mTLSDirection; + + @Override + protected void onStop(){ + super.onStop(); + savePreferences(); + } + + + public void writeConfigFile() + { + + try { + FileWriter cfg = new FileWriter(getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE); + + + + // TODO "--remote-cert-eku", "TLS Web Server Authentication" + + + // The stoned way of java to return an array from a vector + // brought to you by eclipse auto complete + + cfg.write("client\n"); + cfg.write("verb 2\n"); + + + // /tmp does not exist on Android + cfg.write("tmp-dir "); + cfg.write(getCacheDir().getAbsolutePath()); + cfg.write("\n"); + + // quit after 5 tries + cfg.write("--connect-retry-max 5\n"); + cfg.write("--resolv-retry 5\n"); + + + + // We cannot use anything else than tun + cfg.write("dev tun\n"); + + // Server Address + cfg.write("remote "); + cfg.write(mServerAddress.getText().toString()); + cfg.write(" "); + cfg.write(mServerPort.getText().toString()); + if(mTcpUdp.isChecked()) + cfg.write(" udp\n"); + else + cfg.write(" tcp\n"); + + + + switch(mType.getSelectedItemPosition()) { + case VpnProfile.TYPE_CERTIFICATES: + // Ca + cfg.write("ca "); + cfg.write(mCaCert.getData()); + cfg.write("\n"); + + // Client Cert + Key + cfg.write("key "); + cfg.write(mClientKey.getData()); + cfg.write("\n"); + cfg.write("cert "); + cfg.write(mClientCert.getData()); + cfg.write("\n"); + break; + case VpnProfile.TYPE_PKCS12: + cfg.write("pkcs12 "); + cfg.write(mpkcs12.getData()); + cfg.write("\n"); + cfg.write("management-query-passwords\n"); + break; + + case VpnProfile.TYPE_KEYSTORE: + cfg.write("pkcs12 "); + cfg.write(getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGPKCS12); + cfg.write("\n"); + cfg.write("management-query-passwords\n"); + break; + + } + + if(mUseLzo.isChecked()) { + cfg.write("comp-lzo\n"); + } + + if(mUseTlsAuth.isChecked()) { + cfg.write("tls-auth "); + cfg.write(mTlsFile.getData()); + int tlsdir= mTLSDirection.getSelectedItemPosition(); + // 2 is unspecified + if(tlsdir == 1 || tlsdir==2) { + cfg.write(" "); + cfg.write(new Integer(tlsdir).toString()); + } + cfg.write("\n"); + } + cfg.flush(); + cfg.close(); + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + private void addFileSelectLayout (FileSelectLayout fsl) { + int i = fileselects.size() + CHOOSE_FILE_OFFSET; + fileselects.put(i, fsl); + fsl.setActivity(this,i); + } + + public String[] buildOpenvpnArgv() + { + Vector args = new Vector(); + + // Add fixed paramenters + args.add("openvpn"); + + // Enable managment interface to + // stop openvpn + args.add("--management"); + + args.add(getCacheDir().getAbsolutePath() + "/" + "mgmtsocket"); + args.add("unix"); + //args.add("--management-hold"); + + args.add("--config"); + args.add(getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE); + + + return (String[]) args.toArray(new String[args.size()]); + } + + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.config); + + // Forces early JNI Load + OpenVPN.foo(); + + mServerAddress = (TextView) findViewById(R.id.address); + mServerPort = (TextView) findViewById(R.id.port); + mClientCert = (FileSelectLayout) findViewById(R.id.certselect); + mClientKey = (FileSelectLayout) findViewById(R.id.keyselect); + mCaCert = (FileSelectLayout) findViewById(R.id.certselect); + mpkcs12 = (FileSelectLayout) findViewById(R.id.pkcs12select); + mUseLzo = (CheckBox) findViewById(R.id.lzo); + mTcpUdp = (ToggleButton) findViewById(id.tcpudp); + mType = (Spinner) findViewById(R.id.type); + mPKCS12Password = (TextView) findViewById(R.id.pkcs12password); + mAliasName = (TextView) findViewById(R.id.aliasname); + mUseTlsAuth = (CheckBox) findViewById(R.id.useTLSAuth); + mTLSDirection = (Spinner) findViewById(R.id.tls_direction); + + mShowAdvanced = (CheckBox) findViewById(R.id.show_advanced); + mTlsFile = (FileSelectLayout) findViewById(R.id.tlsAuth); + + + + addFileSelectLayout(mCaCert); + addFileSelectLayout(mClientCert); + addFileSelectLayout(mClientKey); + addFileSelectLayout(mTlsFile); + addFileSelectLayout(mpkcs12); + + loadPreferences(); + + mType.setOnItemSelectedListener(this); + + mShowAdvanced.setOnCheckedChangeListener(this); + mUseTlsAuth.setOnCheckedChangeListener(this); + + + findViewById(R.id.select_keystore_button).setOnClickListener(this); + findViewById(R.id.about).setOnClickListener(this); + findViewById(R.id.connect).setOnClickListener(this); + + if (mHandler == null) { + mHandler = new Handler(this); + } + } + + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (parent == mType) { + changeType(position); + } + } + + + + private void changeType(int type){ + // hide everything + findViewById(R.id.pkcs12).setVisibility(View.GONE); + findViewById(R.id.certs).setVisibility(View.GONE); + findViewById(R.id.commonsecret).setVisibility(View.GONE); + findViewById(R.id.keystore).setVisibility(View.GONE); + + switch(type) { + case VpnProfile.TYPE_CERTIFICATES: + findViewById(R.id.certs).setVisibility(View.VISIBLE); + break; + case VpnProfile.TYPE_PKCS12: + findViewById(R.id.pkcs12).setVisibility(View.VISIBLE); + break; + case VpnProfile.COMMON_SECRET: + findViewById(R.id.commonsecret).setVisibility(View.VISIBLE); + break; + case VpnProfile.TYPE_KEYSTORE: + findViewById(R.id.keystore).setVisibility(View.VISIBLE); + break; + } + + + } + + private void loadPreferences() { + SharedPreferences settings = getSharedPreferences(PREFS_NAME,Activity.MODE_PRIVATE); + + mClientCert.setData(settings.getString("clientcert", "")); + mClientKey.setData(settings.getString("clientkey", "")); + mCaCert.setData(settings.getString("ca", "")); + + mUseLzo.setChecked(settings.getBoolean("useLzo", true)); + mServerPort.setText(settings.getString("port", "1194")); + mServerAddress.setText(settings.getString("server", "openvpn.blinkt.de")); + mTcpUdp.setChecked(settings.getBoolean("udp", true)); + mType.setSelection(settings.getInt("type", VpnProfile.TYPE_PKCS12)); + mpkcs12.setData(settings.getString("pkcs12file", "")); + mPKCS12Password.setText(settings.getString("pkcs12password", "")); + certalias = settings.getString("alias", null); + mUseTlsAuth.setChecked(settings.getBoolean("tlsauth", false)); + onCheckedChanged(mUseTlsAuth,mUseTlsAuth.isChecked()); + + mTlsFile.setData(settings.getString("tlsfile","")); + mTLSDirection.setSelection(settings.getInt("tls-direction", 2)); // Unspecified + setAlias(); + + } + + private void savePreferences() { + // We need an Editor object to make preference changes. + // All objects are from android.context.Context + SharedPreferences settings = getSharedPreferences(PREFS_NAME,Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit(); + + + editor.putString("ca" , mCaCert.getData()); + editor.putString("clientcert", mClientCert.getData()); + editor.putString("clientkey", mClientKey.getData()); + + editor.putBoolean("useLzo",mUseLzo.isChecked()); + editor.putString("port", mServerPort.getText().toString()); + editor.putString("server", mServerAddress.getText().toString()); + editor.putBoolean("udp", mTcpUdp.isChecked()); + + editor.putInt("type",mType.getSelectedItemPosition()); + editor.putString("pkcs12file", mpkcs12.getData()); + editor.putString("pkcs12password", mPKCS12Password.getText().toString()); + editor.putString("alias", certalias); + editor.putBoolean("tlsauth", mUseTlsAuth.isChecked()); + editor.putString("tlsfile", mTlsFile.getData()); + editor.putInt("tls-direction", mTLSDirection.getSelectedItemPosition()); + // Commit the edits! + editor.commit(); + } + + + private void setAlias() { + if(certalias == null) { + mAliasName.setText(R.string.client_no_certificate); + } else { + mAliasName.setText(certalias); + } + } + + public void showCertDialog () { + KeyChain.choosePrivateKeyAlias(this, + new KeyChainAliasCallback() { + + public void alias(String alias) { + // Credential alias selected. Remember the alias selection for future use. + certalias=alias; + mHandler.sendEmptyMessage(UPDATE_ALIAS); + } + + + }, + new String[] {"RSA", "DSA"}, // List of acceptable key types. null for any + null, // issuer, null for any + "internal.example.com", // host name of server requesting the cert, null if unavailable + 443, // port of server requesting the cert, -1 if unavailable + null); // alias to preselect, null if unavailable + } + + private String getRandomPW() { + String pw= ""; + // Put enough digits togher to make a password :) + Random r = new Random(); + for(int i=0;i < 4;i++) { + pw += new Integer(r.nextInt(1000)).toString(); + } + + return pw; + + } + + private String savePKCS12() { + Context context = getBaseContext(); + PrivateKey privateKey = null; + X509Certificate[] cachain=null; + try { + privateKey = KeyChain.getPrivateKey(context,certalias); + cachain = KeyChain.getCertificateChain(context, certalias); + + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + ks.setKeyEntry("usercert", privateKey, null, cachain); + String mypw = getRandomPW(); + FileOutputStream fout = new FileOutputStream(getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGPKCS12); + ks.store(fout,mypw.toCharArray()); + fout.flush(); fout.close(); + return mypw; + } catch (KeyChainException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (KeyStoreException e) { + e.printStackTrace(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (CertificateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return "ERROR"; + + } + + public void testGetallCerts() throws NoSuchAlgorithmException, KeyStoreException { + TrustManagerFactory tmf = TrustManagerFactory + .getInstance(TrustManagerFactory.getDefaultAlgorithm()); + + tmf.init((KeyStore) null); + X509TrustManager xtm = (X509TrustManager) tmf.getTrustManagers()[0]; + + X509Certificate[] foo = xtm.getAcceptedIssuers(); + for (X509Certificate cert : xtm.getAcceptedIssuers()) { + String certStr = "S:" + cert.getSubjectDN().getName() + "\nI:" + + cert.getIssuerDN().getName(); + Log.d(TAG, certStr); + } + System.out.println(foo); + } + + @Override + public void onClick(View v) { + if(v == findViewById(R.id.connect)) { + Intent intent = VpnService.prepare(this); + if (intent != null) { + startActivityForResult(intent, 0); + } else { + onActivityResult(START_OPENVPN, RESULT_OK, null); + } + } else if (v == findViewById(R.id.about)) { + Intent intent = new Intent(getBaseContext(),AboutActivity.class); + startActivity(intent); + } else if (v == findViewById(R.id.select_keystore_button)) { + showCertDialog(); + } + } + + void startOpenVpn() { + String prefix = getPackageName(); + writeConfigFile(); + + Intent intent = new Intent(this, OpenVpnService.class) + .putExtra(prefix + ".ARGV" , buildOpenvpnArgv()); + + if(mType.getSelectedItemPosition()== VpnProfile.TYPE_PKCS12){ + intent.putExtra(prefix + ".PKCS12PASS", + mPKCS12Password.getText().toString()); + } + + if(mType.getSelectedItemPosition() == VpnProfile.TYPE_KEYSTORE) { + String pkcs12pw = savePKCS12(); + intent.putExtra(prefix + ".PKCS12PASS", pkcs12pw); + } + + + startService(intent); + Intent startLW = new Intent(getBaseContext(),LogWindow.class); + startActivity(startLW); + } + + /* (non-Javadoc) + * @see android.app.Activity#onActivityResult(int, int, android.content.Intent) + */ + @Override + protected void onActivityResult(int request, int result, Intent data) { + if (request== START_OPENVPN) { + if (result == RESULT_OK) { + // It always crashes and never saves ;) + savePreferences(); + new startOpenVpnThread().start(); + } + + } else if (request >= CHOOSE_FILE_OFFSET) { + String filepath = data.getStringExtra(FileDialog.RESULT_PATH); + FileSelectLayout fsl = fileselects.get(request); + fsl.setData(filepath); + } + savePreferences(); + } + + + + private class startOpenVpnThread extends Thread { + + @Override + public void run() { + startOpenVpn(); + } + + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + + + @Override + public boolean handleMessage(Message msg) { + setAlias(); + return true; + } + + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + int visibility; + if(isChecked) + visibility =View.VISIBLE; + else + visibility =View.GONE; + + if(buttonView==mShowAdvanced) { + findViewById(R.id.advanced_options).setVisibility(visibility); + } else if (buttonView == mUseTlsAuth) { + findViewById(R.id.tlsauth_options).setVisibility(visibility); + } + } +} diff --git a/src/de/blinkt/openvpn/OpenVpnService.java b/src/de/blinkt/openvpn/OpenVpnService.java new file mode 100644 index 00000000..e8174bcb --- /dev/null +++ b/src/de/blinkt/openvpn/OpenVpnService.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2011 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 de.blinkt.openvpn; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.util.Arrays; + +import android.app.PendingIntent; +import android.content.Intent; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.net.VpnService; +import android.os.Handler; +import android.os.Message; +import android.os.ParcelFileDescriptor; +import android.util.Log; +import android.widget.Toast; + +public class OpenVpnService extends VpnService implements Handler.Callback, Runnable { + private static final String TAG = "OpenVpnService"; + + private String[] mArgv; + + private Handler mHandler; + private Thread mThread; + + private ParcelFileDescriptor mInterface; + + @Override + public void onRevoke() { + managmentCommand("signal SIGINT\n"); + mThread=null; + stopSelf(); + }; + + + public void managmentCommand(String cmd) { + LocalSocket mgmtsocket; + try { + byte[] buffer = new byte[400]; + mgmtsocket = new LocalSocket(); + + mgmtsocket.connect(new LocalSocketAddress(getCacheDir().getAbsolutePath() + "/" + "mgmtsocket", + LocalSocketAddress.Namespace.FILESYSTEM)); + //mgmtsocket = new Dat("127.0.0.1",OpenVPNClient.MANAGMENTPORT)); + + //OutputStreamWriter outw = new OutputStreamWriter(mgmtsocket.getOutputStream()); + mgmtsocket.getInputStream().read(buffer); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + //outw.write(cmd); + mgmtsocket.getOutputStream().write(cmd.getBytes()); + //outw.flush(); + try { + Thread.sleep(400); + } catch (InterruptedException e) { + } + + mgmtsocket.close(); + } catch (UnknownHostException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // The handler is only used to show messages. + if (mHandler == null) { + mHandler = new Handler(this); + } + + // Stop the previous session by interrupting the thread. + if (mThread != null) { + managmentCommand("signal SIGINT\n"); + mThread.interrupt(); + } + + // Thread already running, reuse existing, + + // Extract information from the intent. + String prefix = getPackageName(); + mArgv = intent.getStringArrayExtra(prefix + ".ARGV"); + + + // Start a new session by creating a new thread. + mThread = new Thread(this, "OpenVPNThread"); + mThread.start(); + + if(intent.hasExtra(prefix +".PKCS12PASS")) + { + try { + String pkcs12password = intent.getStringExtra(prefix +".PKCS12PASS"); + Thread.sleep(3000); + + managmentCommand("password 'Private Key' " + pkcs12password + "\n"); + } catch (InterruptedException e) { + } + + } + + + + return START_STICKY; + } + + @Override + public void onDestroy() { + if (mThread != null) { + managmentCommand("signal SIGINT\n"); + + mThread.interrupt(); + } + } + + @Override + public boolean handleMessage(Message message) { + if (message != null) { + Toast.makeText(this, message.what, Toast.LENGTH_SHORT).show(); + } + return true; + } + + @Override + public synchronized void run() { + try { + Log.i(TAG, "Starting o"); + + + OpenVPN.setCallback(this); + + + // We try to create the tunnel for several times. The better way + // is to work with ConnectivityManager, such as trying only when + // the network is avaiable. Here we just use a counter to keep + // things simple. + //for (int attempt = 0; attempt < 10; ++attempt) { + mHandler.sendEmptyMessage(R.string.connecting); + + // Log argv + + OpenVPN.logMessage(0, "argv:" , Arrays.toString(mArgv)); + + OpenVPN.startOpenVPNThreadArgs(mArgv); + + + + // Sleep for a while. This also checks if we got interrupted. + Thread.sleep(3000); + //} + Log.i(TAG, "Giving up"); + } catch (Exception e) { + Log.e(TAG, "Got " + e.toString()); + } finally { + try { + mInterface.close(); + } catch (Exception e) { + // ignore + } + mInterface = null; + + + mHandler.sendEmptyMessage(R.string.disconnected); + Log.i(TAG, "Exiting"); + } + } + + + public ParcelFileDescriptor openTun(String localip) { + // FIXME: hardcoded assumptions + Builder builder = new Builder(); + builder.addRoute("0.0.0.0", 0); + builder.addAddress(localip, 24 ); + builder.addDnsServer("131.234.137.23"); + builder.addSearchDomain("blinkt.de"); + builder.setSession("OpenVPN - " + localip); + Intent intent = new Intent(getBaseContext(),LogWindow.class); + PendingIntent startLW = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0); + builder.setConfigureIntent(startLW); + mInterface = builder.establish(); + return mInterface; + + } +} diff --git a/src/de/blinkt/openvpn/VpnProfile.java b/src/de/blinkt/openvpn/VpnProfile.java new file mode 100644 index 00000000..d01b1104 --- /dev/null +++ b/src/de/blinkt/openvpn/VpnProfile.java @@ -0,0 +1,8 @@ +package de.blinkt.openvpn; + +public class VpnProfile { + static final int TYPE_CERTIFICATES=0; + static final int TYPE_PKCS12=1; + static final int COMMON_SECRET=3; + static final int TYPE_KEYSTORE=2; +} -- cgit v1.2.3