From d573932062cb4afd7f001899978e37ce446934e7 Mon Sep 17 00:00:00 2001
From: Arne Schwabe <arne@rfc2549.org>
Date: Tue, 10 Apr 2012 00:34:51 +0200
Subject: [PATCH] initial android hacks

---
 src/compat/compat-rsa_generate_key.c |   47 ++++++++++
 src/openvpn/error.c                  |   19 ++++-
 src/openvpn/jniglue.c                |  162 ++++++++++++++++++++++++++++++++++
 src/openvpn/jniglue.h                |   19 ++++
 src/openvpn/options.c                |    1 +
 src/openvpn/route.c                  |    7 ++-
 src/openvpn/socket.c                 |   25 +++++
 src/openvpn/ssl.c                    |    2 +
 src/openvpn/syshead.h                |    2 +-
 src/openvpn/tun.c                    |   22 ++++-
 src/openvpn/tun.h                    |    2 +
 11 files changed, 303 insertions(+), 5 deletions(-)
 create mode 100644 src/compat/compat-rsa_generate_key.c
 create mode 100644 src/openvpn/jniglue.c
 create mode 100644 src/openvpn/jniglue.h

diff --git a/src/compat/compat-rsa_generate_key.c b/src/compat/compat-rsa_generate_key.c
new file mode 100644
index 0000000..99725da
--- /dev/null
+++ b/src/compat/compat-rsa_generate_key.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <time.h>
+#include "cryptlib.h"
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+RSA *RSA_generate_key(int bits, unsigned long e_value,
+		      void (*callback)(int,int,void *), void *cb_arg)
+{
+  BN_GENCB cb;
+  int i;
+  RSA *rsa = RSA_new();
+  BIGNUM *e = BN_new();
+
+  if(!rsa || !e) goto err;
+
+  /* The problem is when building with 8, 16, or 32 BN_ULONG,
+   * unsigned long can be larger */
+  for (i=0; i<(int)sizeof(unsigned long)*8; i++)
+    {
+      if (e_value & (1UL<<i))
+	if (BN_set_bit(e,i) == 0)
+	  goto err;
+    }
+
+  BN_GENCB_set_old(&cb, callback, cb_arg);
+
+  if(RSA_generate_key_ex(rsa, bits, e, &cb)) {
+    BN_free(e);
+    return rsa;
+  }
+ err:
+  if(e) BN_free(e);
+  if(rsa) RSA_free(rsa);
+  return 0;
+}
+
+
+
+void mlockall(){}
+char *
+getpass (prompt)
+     const char *prompt;
+{
+  return "";
+}
+
diff --git a/src/openvpn/error.c b/src/openvpn/error.c
index d6ad639..d4a3cff 100644
--- a/src/openvpn/error.c
+++ b/src/openvpn/error.c
@@ -199,6 +199,16 @@ msg_fp(const unsigned int flags)
 
 int x_msg_line_num; /* GLOBAL */
 
+#include "android/log.h"
+void x_msg2(const unsigned int flags, const char *format, ...) {
+    va_list arglist;
+    va_start(arglist,format);
+    //	vsnprintf(m1, 100, "foo bbaz",arglist);
+    __android_log_vprint(ANDROID_LOG_DEBUG, "openvpn",format, arglist);
+    va_end(arglist);
+}
+
+
 void x_msg (const unsigned int flags, const char *format, ...)
 {
   struct gc_arena gc;
@@ -350,6 +360,10 @@ void x_msg (const unsigned int flags, const char *format, ...)
 	}
     }
 
+#ifdef TARGET_ANDROID
+   android_openvpn_log(prefix,prefix_sep,m1);;
+#endif
+    
   if (flags & M_FATAL)
     msg (M_INFO, "Exiting due to fatal error");
 
@@ -701,7 +715,10 @@ openvpn_exit (const int status)
       if (status == OPENVPN_EXIT_STATUS_GOOD)
 	perf_output_results ();
     }
-
+#ifdef TARGET_ANDROID
+    android_openvpn_exit(status);
+#endif
+    
   exit (status);
 }
 
diff --git a/src/openvpn/jniglue.c b/src/openvpn/jniglue.c
new file mode 100644
index 0000000..2d52936
--- /dev/null
+++ b/src/openvpn/jniglue.c
@@ -0,0 +1,162 @@
+#include <jni.h>
+#include <android/log.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+#include "jniglue.h"
+
+JNIEXPORT jint Java_de_blinkt_OpenVPN_startOpenVPNThread(JNIEnv* env, jclass jc);
+
+
+extern int main (int argc, char *argv[]);
+
+static jmp_buf jump_buffer;
+
+int callmain (int argc, char *argv[]) {
+    if(!setjmp(jump_buffer))
+        main(argc,argv);
+}
+
+
+void android_openvpn_exit(int status) {
+    longjmp(jump_buffer,status+1);
+}
+
+
+void testmsg(char* m1, ...) {
+	va_list arglist;
+	va_start(arglist,m1);
+	//	vsnprintf(m1, 100, "foo bbaz",arglist);
+	__android_log_vprint(ANDROID_LOG_DEBUG, "openvpn",m1, arglist);
+		va_end(arglist);
+}
+
+// Store env and class, we allow only one instance
+// so make these variables global for now
+jclass openvpnclass;
+JNIEnv* openvpnjenv;
+
+//Lde/blinkt/openvpn/OpenVPN startOpenVPNThread startOpenVPNThread
+ jint Java_de_blinkt_openvpn_OpenVPN_startOpenVPNThread(JNIEnv* env, jclass jc){
+    char* argv[] = {"openvpn", "--client",
+                    "--dev","tun",
+                    "--comp-lzo",
+//                    "--redirect-gateway","def1",
+//                    "--pkcs12","/mnt/sdcard/Network_Certificate.p12",
+                    "--remote-cert-eku", "TLS Web Server Authentication",
+                    "--remote","openvpn.uni-paderborn.de",
+                    "--ca","/mnt/sdcard/ca.pem",
+                    "--key","/mnt/sdcard/schwabe.key",
+                    "--cert","/mnt/sdcard/schwabe.pem",
+                    "--verb","4"
+                };
+     
+     openvpnclass = jc;
+     openvpnjenv= env;
+     int argc=17;
+
+    return callmain(argc,argv);
+ }
+
+void Java_de_blinkt_openvpn_OpenVPN_startOpenVPNThreadArgs(JNIEnv *env,jclass jc, jobjectArray stringArray) {
+    openvpnclass = jc;
+    openvpnjenv= env;
+    
+    int stringCount = (*env)->GetArrayLength(env, stringArray);
+    
+    
+    const char** argv = calloc(stringCount,sizeof(const char*));
+    
+    int i;
+    for (i=0; i<stringCount; i++) {
+        jstring string = (jstring) (*env)->GetObjectArrayElement(env, stringArray, i);
+        jboolean isCopy;
+        const char* rawString = (*env)->GetStringUTFChars(env, string, &isCopy);
+        
+        // Copy the string to able to release it
+        argv[i] = rawString;
+
+    }
+    
+    // Call main
+    callmain(stringCount,argv);
+    
+    // Release the Strings
+    for(i=0; i<stringCount;i++){
+        jstring string = (jstring) (*env)->GetObjectArrayElement(env, stringArray, i);
+        (*env)->ReleaseStringUTFChars(env,string,argv[i]);
+    }
+    free(argv);
+}
+    
+
+
+
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    __android_log_write(ANDROID_LOG_DEBUG,"openvpn", "Loading openvpn native library $id$ compiled on "   __DATE__ " " __TIME__ );
+    return JNI_VERSION_1_2;
+}
+
+void addInterfaceInformation(int mtu,const char* ifconfig_local, const char* ifconfig_remote)
+{
+    jstring jlocal = (*openvpnjenv)->NewStringUTF(openvpnjenv, ifconfig_local);
+    jstring jremote = (*openvpnjenv)->NewStringUTF(openvpnjenv, ifconfig_remote);
+
+    jmethodID aMethodID = (*openvpnjenv)->GetStaticMethodID(openvpnjenv, openvpnclass, "addInterfaceInfo", 
+                                                            "(ILjava/lang/String;Ljava/lang/String;)V");
+    (*openvpnjenv)->CallStaticVoidMethod(openvpnjenv,openvpnclass,aMethodID,mtu,jlocal,jremote);
+    
+    (*openvpnjenv)->DeleteLocalRef(openvpnjenv,jlocal);
+    (*openvpnjenv)->DeleteLocalRef(openvpnjenv,jremote);
+
+    
+}
+
+void android_openvpn_log(int level,const char* prefix,const char* prefix_sep,const char* m1)
+{
+    __android_log_print(ANDROID_LOG_DEBUG,"openvpn","%s%s%s",prefix,prefix_sep,m1);
+
+    jstring jprefix = (*openvpnjenv)->NewStringUTF(openvpnjenv, prefix);
+    jstring jmessage = (*openvpnjenv)->NewStringUTF(openvpnjenv, m1);
+    
+    jmethodID aMethodID = (*openvpnjenv)->GetStaticMethodID(openvpnjenv, openvpnclass, "logMessage", 
+                                                            "(ILjava/lang/String;Ljava/lang/String;)V");
+    
+    (*openvpnjenv)->CallStaticVoidMethod(openvpnjenv,openvpnclass,aMethodID,level,jprefix,jmessage);
+    
+    (*openvpnjenv)->DeleteLocalRef(openvpnjenv,jprefix);
+    (*openvpnjenv)->DeleteLocalRef(openvpnjenv,jmessage);
+    
+}
+
+
+unsigned char android_protect_socket(int sockfd) {
+    jmethodID aMethodID = (*openvpnjenv)->GetStaticMethodID(openvpnjenv, openvpnclass, "protectSocket", 
+                                                            "(I)Z");
+    return (*openvpnjenv)->CallStaticBooleanMethod(openvpnjenv,openvpnclass,aMethodID,sockfd);
+
+}
+int android_open_tun () {
+    jmethodID aMethodID = (*openvpnjenv)->GetStaticMethodID(openvpnjenv, openvpnclass, "openTunDevice", 
+                                                            "()I");
+    return (*openvpnjenv)->CallStaticIntMethod(openvpnjenv,openvpnclass,aMethodID);
+
+}
+
+void addRouteInformation(const char* dest, const char* mask, const char* gw) {
+    
+    jstring jmask =  (*openvpnjenv)->NewStringUTF(openvpnjenv, mask);
+    jstring jdest =  (*openvpnjenv)->NewStringUTF(openvpnjenv, dest);
+    jstring jgw =    (*openvpnjenv)->NewStringUTF(openvpnjenv, gw);
+    jmethodID aMethodID = (*openvpnjenv)->GetStaticMethodID(openvpnjenv, openvpnclass, "addRoute", 
+                                            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+    (*openvpnjenv)->CallStaticVoidMethod(openvpnjenv,openvpnclass,aMethodID,jdest,jmask,jgw);
+
+    (*openvpnjenv)->DeleteLocalRef(openvpnjenv,jmask);
+    (*openvpnjenv)->DeleteLocalRef(openvpnjenv,jdest);
+    (*openvpnjenv)->DeleteLocalRef(openvpnjenv,jgw);
+
+
+}
+
+
diff --git a/src/openvpn/jniglue.h b/src/openvpn/jniglue.h
new file mode 100644
index 0000000..cb3ae41
--- /dev/null
+++ b/src/openvpn/jniglue.h
@@ -0,0 +1,19 @@
+//
+//  jniglue.h
+//  xcopenvpn
+//
+//  Created by Arne Schwabe on 29.03.12.
+//  Copyright (c) 2012 Universität Paderborn. All rights reserved.
+//
+
+#ifndef xcopenvpn_jniglue_h
+#define xcopenvpn_jniglue_h
+
+void testmsg(char* m1, ...);
+void addRouteInformation(const char* dest, const char* mask, const char* gw);
+void addInterfaceInformation(int mtu,const char* ifconfig_local, const char* ifconfig_remote);
+void android_openvpn_log(int level,const char* prefix,const char* prefix_sep,const char* m1);
+void android_openvpn_exit(int status);
+
+
+#endif
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index bd83843..fcf436c 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -560,6 +560,7 @@ static const char usage_message[] =
 #if OPENSSL_VERSION_NUMBER >= 0x00907000L
   " and CRLs).\n"
 #else /* OPENSSL_VERSION_NUMBER >= 0x00907000L */
+#error WTF!
   ").\n"
   "                  WARNING: no support of CRL available with this version.\n"
 #endif /* OPENSSL_VERSION_NUMBER >= 0x00907000L */
diff --git a/src/openvpn/route.c b/src/openvpn/route.c
index 7c25c77..6c3ad90 100644
--- a/src/openvpn/route.c
+++ b/src/openvpn/route.c
@@ -1344,6 +1344,11 @@ add_route (struct route *r,
   argv_msg (D_ROUTE, &argv);
   status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed");
 
+#elif defined (TARGET_ANDROID)
+#include "jniglue.h"
+
+    addRouteInformation(network, netmask, gateway);
+
 #elif defined (WIN32)
   {
     DWORD ai = TUN_ADAPTER_INDEX_INVALID;
@@ -2371,7 +2376,7 @@ show_routes (int msglev)
   gc_free (&gc);
 }
 
-#elif defined(TARGET_LINUX)
+#elif defined(TARGET_LINUX) || defined(TARGET_ANDROID)
 
 void
 get_default_gateway (struct route_gateway_info *rgi)
diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c
index 6b1f8d2..364b97e 100644
--- a/src/openvpn/socket.c
+++ b/src/openvpn/socket.c
@@ -41,6 +41,11 @@
 
 #include "memdbg.h"
 
+#ifdef TARGET_ANDROID
+#include "jniglue.h"
+#endif 
+
+
 const int proto_overhead[] = { /* indexed by PROTO_x */
   0,
   IPv4_UDP_HEADER_SIZE, /* IPv4 */
@@ -858,6 +863,11 @@ create_socket_tcp (void)
   }
 #endif
 
+#ifdef TARGET_ANDROID
+    /* Protects the socket from being routed via VPN */
+    android_protect_socket(sd);
+#endif
+
   return sd;
 }
 
@@ -885,6 +895,12 @@ create_socket_udp (const unsigned int flags)
 #endif
     }
 #endif
+
+#ifdef TARGET_ANDROID
+    /* Protects the socket from being routed via VPN */
+    android_protect_socket(sd);
+#endif
+
   return sd;
 }
 
@@ -904,6 +920,11 @@ create_socket_udp6 (const unsigned int flags)
 	msg(M_SOCKERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO");
     }
 #endif
+#ifdef TARGET_ANDROID
+    /* Protects the socket from being routed via VPN */
+    android_protect_socket(sd);
+#endif
+
   return sd;
 }
 
@@ -922,6 +943,10 @@ create_socket_tcp6 (void)
 		    (void *) &on, sizeof (on)) < 0)
       msg (M_SOCKERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP6 socket");
   }
+#ifdef TARGET_ANDROID
+    /* Protects the socket from being routed via VPN */
+    android_protect_socket(sd);
+#endif
 
   return sd;
 }
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 251f8ed..537fc12 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -1645,6 +1645,8 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
       buf_printf (&out, "IV_PLAT=netbsd\n");
 #elif defined(TARGET_FREEBSD)
       buf_printf (&out, "IV_PLAT=freebsd\n");
+#elif defined(TARGET_ANDROID)
+      buf_printf(&out, "IV_PLAT=android\n");
 #elif defined(WIN32)
       buf_printf (&out, "IV_PLAT=win\n");
 #endif
diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h
index 0595b67..56a5429 100644
--- a/src/openvpn/syshead.h
+++ b/src/openvpn/syshead.h
@@ -211,7 +211,7 @@
 #include <net/if_tap.h>
 #endif
 
-#ifdef TARGET_LINUX
+#if defined(TARGET_LINUX) || defined (TARGET_ANDROID)
 
 #if defined(HAVE_NETINET_IF_ETHER_H)
 #include <netinet/if_ether.h>
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index b99071c..db8104d 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -49,6 +49,11 @@
 
 #include "memdbg.h"
 
+#ifdef TARGET_ANDROID
+#include "jniglue.h"
+#endif
+
+
 #ifdef WIN32
 
 /* #define SIMULATE_DHCP_FAILED */       /* simulate bad DHCP negotiation */
@@ -768,6 +773,8 @@ do_ifconfig (struct tuntap *tt,
       tt->did_ifconfig = true;
 
 #endif /*ENABLE_IPROUTE*/
+#elif defined(TARGET_ANDROID)
+       addInterfaceInformation(tun_mtu,ifconfig_local, ifconfig_remote_netmask);
 #elif defined(TARGET_SOLARIS)
 
       /* Solaris 2.6 (and 7?) cannot set all parameters in one go...
@@ -1365,7 +1372,16 @@ close_tun_generic (struct tuntap *tt)
 
 #endif
 
-#if defined(TARGET_LINUX)
+#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
+
+#if defined (TARGET_ANDROID)
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+{
+    tt->fd = android_open_tun();
+}
+
+#else
 
 #ifdef HAVE_LINUX_IF_TUN_H	/* New driver support */
 
@@ -1373,6 +1389,7 @@ close_tun_generic (struct tuntap *tt)
 #error header file linux/sockios.h required
 #endif
 
+
 #if defined(HAVE_TUN_PI) && defined(HAVE_IPHDR) && defined(HAVE_IOVEC) && defined(ETH_P_IPV6) && defined(ETH_P_IP) && defined(HAVE_READV) && defined(HAVE_WRITEV)
 #define LINUX_IPV6 1
 /* #warning IPv6 ON */
@@ -1426,7 +1443,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu
       if (!tt->ipv6)
 	ifr.ifr_flags = IFF_NO_PI;
 
-#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
+#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN) && !defined(TARGET_ANDROID)
       ifr.ifr_flags |= IFF_ONE_QUEUE;
 #endif
 
@@ -1516,6 +1533,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu
 }
 
 #endif /* HAVE_LINUX_IF_TUN_H */
+#endif /* TARGET_ANDROID */
 
 #ifdef TUNSETPERSIST
 
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 9bd990f..63ab872 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -292,6 +292,8 @@ ifconfig_order(void)
   return IFCONFIG_AFTER_TUN_OPEN;
 #elif defined(WIN32)
   return IFCONFIG_BEFORE_TUN_OPEN;
+#elif defined(TARGET_ANDROID)
+  return IFCONFIG_BEFORE_TUN_OPEN;
 #else
   return IFCONFIG_DEFAULT;
 #endif
-- 
1.7.5.4