diff options
author | Arne Schwabe <arne@rfc2549.org> | 2012-05-06 23:52:22 +0200 |
---|---|---|
committer | Arne Schwabe <arne@rfc2549.org> | 2012-05-06 23:52:22 +0200 |
commit | ab9933e33e5e7b5066659b4c8911480a70f360f5 (patch) | |
tree | 48c8f08d3f2ce778a28f17f0abd25f5c75d67814 | |
parent | 231395d4df16f42107c57211c76075aefcd66c75 (diff) |
All control of openvpn is now over the unix socket. JNI is only used for starting openvpn.
Fix configuration if no DNS information is available. (closes issue #7 hopefully)
Version 0.5.0
-rw-r--r-- | AndroidManifest.xml | 4 | ||||
-rw-r--r-- | openvpn/src/openvpn/manage.c | 50 | ||||
-rw-r--r-- | openvpn/src/openvpn/manage.h | 1 | ||||
-rw-r--r-- | openvpn/src/openvpn/tun.c | 8 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/OpenVPN.java | 16 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/OpenVpnManagementThread.java | 70 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/OpenVpnService.java | 43 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/VpnProfile.java | 65 |
8 files changed, 153 insertions, 104 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 263cc8c7..7c1e07b9 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -17,8 +17,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.blinkt.openvpn" - android:versionCode="19" - android:versionName="0.4.9" > + android:versionCode="20" + android:versionName="0.4.10" > <uses-permission android:name="android.permission.INTERNET" /> diff --git a/openvpn/src/openvpn/manage.c b/openvpn/src/openvpn/manage.c index f28278f9..96ca6eaa 100644 --- a/openvpn/src/openvpn/manage.c +++ b/openvpn/src/openvpn/manage.c @@ -67,6 +67,7 @@ struct management *management; /* GLOBAL */ static void man_output_standalone (struct management *man, volatile int *signal_received); static void man_reset_client_socket (struct management *man, const bool exiting); static ssize_t write_fd (int fd, void *ptr, size_t nbytes, int flags, int sendfd); +static ssize_t read_fd(int fd, void *ptr, size_t nbytes, int flags, int *recvfd); static void @@ -1815,8 +1816,15 @@ man_read (struct management *man) */ unsigned char buf[256]; int len = 0; + int fd = -1; - len = recv (man->connection.sd_cli, buf, sizeof (buf), MSG_NOSIGNAL); +#ifdef TARGET_ANDROID + len = read_fd (man->connection.sd_cli, buf, sizeof (buf), MSG_NOSIGNAL, &fd); + if(fd >= 0) + man->connection.lastfdreceived = fd; +#else + len = recv (man->connection.sd_cli, buf, sizeof (buf), MSG_NOSIGNAL); +#endif if (len == 0) { man_reset_client_socket (man, false); @@ -3092,6 +3100,7 @@ management_query_rsa_sig (struct management *man, #endif +#ifdef TARGET_ANDROID static ssize_t write_fd (int fd, void *ptr, size_t nbytes, int flags, int sendfd) { struct msghdr msg; @@ -3123,6 +3132,45 @@ static ssize_t write_fd (int fd, void *ptr, size_t nbytes, int flags, int sendfd return (sendmsg(fd, &msg, flags)); } +static ssize_t read_fd(int fd, void *ptr, size_t nbytes, int flags, int *recvfd) +{ + struct msghdr msghdr; + struct iovec iov[1]; + ssize_t n; + + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof (int))]; + } control_un; + struct cmsghdr *cmptr; + + msghdr.msg_control = control_un.control; + msghdr.msg_controllen = sizeof(control_un.control); + + msghdr.msg_name = NULL; + msghdr.msg_namelen = 0; + + iov[0].iov_base = ptr; + iov[0].iov_len = nbytes; + msghdr.msg_iov = iov; + msghdr.msg_iovlen = 1; + + if ( (n = recvmsg(fd, &msghdr, flags)) <= 0) + return (n); + + if ( (cmptr = CMSG_FIRSTHDR(&msghdr)) != NULL && + cmptr->cmsg_len == CMSG_LEN(sizeof(int))) { + if (cmptr->cmsg_level != SOL_SOCKET) + msg (M_ERR, "control level != SOL_SOCKET"); + if (cmptr->cmsg_type != SCM_RIGHTS) + msg (M_ERR, "control type != SCM_RIGHTS"); + *recvfd = *((int *) CMSG_DATA(cmptr)); + } else + *recvfd = -1; /* descriptor was not passed */ + + return (n); +} +#endif /* diff --git a/openvpn/src/openvpn/manage.h b/openvpn/src/openvpn/manage.h index 71e1a84e..c7ffb42a 100644 --- a/openvpn/src/openvpn/manage.h +++ b/openvpn/src/openvpn/manage.h @@ -305,6 +305,7 @@ struct man_connection { #endif #ifdef TARGET_ANDROID int fdtosend; + int lastfdreceived; #endif }; diff --git a/openvpn/src/openvpn/tun.c b/openvpn/src/openvpn/tun.c index 66a09ea0..d2123766 100644 --- a/openvpn/src/openvpn/tun.c +++ b/openvpn/src/openvpn/tun.c @@ -1401,7 +1401,13 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu management_query_user_pass(management, &up , "DNSDOMAIN", GET_USER_PASS_NEED_OK,(void*) 0); } - if((tt->fd = android_open_tun())< 0){ + strcpy(up.username , dev); + management_query_user_pass(management, &up , "OPENTUN", GET_USER_PASS_NEED_OK,(void*) 0); + + tt->fd = management->connection.lastfdreceived; + management->connection.lastfdreceived=-1; + + if( (tt->fd < 0) || ! (strcmp("ok",up.password)==0)) { msg (M_ERR, "ERROR: Cannot open TUN"); } gc_free (&gc); diff --git a/src/de/blinkt/openvpn/OpenVPN.java b/src/de/blinkt/openvpn/OpenVPN.java index cc827aae..e524da1c 100644 --- a/src/de/blinkt/openvpn/OpenVPN.java +++ b/src/de/blinkt/openvpn/OpenVPN.java @@ -8,7 +8,6 @@ import android.util.Log; public class OpenVPN { private static OpenVpnService mOpenVpnService; private static final int MAXLOGENTRIES = 500; - public static native int startOpenVPNThread(); public static native int startOpenVPNThreadArgs(String argv[]); private static final String TAG = "OpenVpn"; @@ -30,14 +29,9 @@ public class OpenVPN { System.loadLibrary("openvpn"); } - static void addRoute(String dest,String mask, String gw) { - Log.i("openvpn" ,"Got Routing information " + dest + " " + mask + " " + gw ); - mOpenVpnService.addRoute(dest,mask); - } - synchronized static void logMessage(int level,String prefix, String message) { - logbuffer.addLast(prefix + " " + message); + logbuffer.addLast(prefix + message); if(logbuffer.size()>MAXLOGENTRIES) logbuffer.removeFirst(); @@ -49,7 +43,7 @@ public class OpenVPN { } for (LogListener ll : logListener) { - ll.newLog(prefix + " " + message); + ll.newLog(prefix + message); } } @@ -72,11 +66,7 @@ public class OpenVPN { public static void setCallback(OpenVpnService openVpnService) { mOpenVpnService = openVpnService; } - - public static int openTunDevice() { - Log.d(TAG,"Opening tun device"); - return mOpenVpnService.openTun(); - } + //! Dummy method being called to force loading of JNI Libraries public static void foo() { } diff --git a/src/de/blinkt/openvpn/OpenVpnManagementThread.java b/src/de/blinkt/openvpn/OpenVpnManagementThread.java index fcdb6046..fd7fe8a8 100644 --- a/src/de/blinkt/openvpn/OpenVpnManagementThread.java +++ b/src/de/blinkt/openvpn/OpenVpnManagementThread.java @@ -34,7 +34,6 @@ public class OpenVpnManagementThread implements Runnable { mSocket.getOutputStream().write(cmd.getBytes());
mSocket.getOutputStream().flush();
} catch (IOException e) {
- e.printStackTrace();
}
}
@@ -94,7 +93,7 @@ public class OpenVpnManagementThread implements Runnable { try {
Method getInt = FileDescriptor.class.getDeclaredMethod("getInt$");
int fdint = (Integer) getInt.invoke(fd);
-
+
Log.d("Openvpn", "Got FD from socket: " + fd + " " + fdint);
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(fdint);
mOpenVPNService.protect(fdint);
@@ -146,13 +145,17 @@ public class OpenVpnManagementThread implements Runnable { } else if (cmd.equals("NEED-OK")) {
processNeedCommand(argument);
} else {
+ OpenVPN.logMessage(0, "MGMT:", "Got unrecognized command" + command);
Log.i(TAG, "Got unrecognized command" + command);
}
+ } else if (command.startsWith("SUCCESS:")) {
+ // ignore
} else {
Log.i(TAG, "Got unrecognized line from managment" + command);
+ OpenVPN.logMessage(0, "MGMT:", "Got unrecognized line from management:" + command);
}
}
-
+
private void processNeedCommand(String argument) {
int p1 =argument.indexOf('\'');
int p2 = argument.indexOf('\'',p1+1);
@@ -160,6 +163,9 @@ public class OpenVpnManagementThread implements Runnable { String needed = argument.substring(p1+1, p2);
String extra = argument.split(":",2)[1];
+ String status = "ok";
+
+
if (needed.equals("PROTECTFD")) {
FileDescriptor fdtoprotect = mFDList.pollFirst();
protectFileDescriptor(fdtoprotect);
@@ -174,16 +180,66 @@ public class OpenVpnManagementThread implements Runnable { String[] ifconfigparts = extra.split(" ");
int mtu = Integer.parseInt(ifconfigparts[2]);
mOpenVPNService.setLocalIP(ifconfigparts[0], ifconfigparts[1],mtu);
-
+ } else if (needed.equals("OPENTUN")) {
+ if(sendTunFD(needed,extra))
+ return;
+ else
+ status="cancel";
+ // This not nice or anything but setFileDescriptors accepts only FilDescriptor class :(
+
} else {
Log.e(TAG,"Unkown needok command " + argument);
return;
}
-
- String cmd = String.format("needok '%s' %s\n", needed, "ok");
+
+ String cmd = String.format("needok '%s' %s\n", needed, status);
managmentCommand(cmd);
}
+ private boolean sendTunFD (String needed, String extra) {
+ if(!extra.equals("tun")) {
+ // We only support tun
+ String errmsg = String.format("Devicetype %s requested, but only tun is possible with the Android API, sorry!",extra);
+ OpenVPN.logMessage(0, "", errmsg );
+
+ return false;
+ }
+ ParcelFileDescriptor pfd = mOpenVPNService.openTun();
+ if(pfd==null)
+ return false;
+
+ Method setInt;
+ int fdint = pfd.getFd();
+ try {
+ setInt = FileDescriptor.class.getDeclaredMethod("setInt$",int.class);
+ FileDescriptor fdtosend = new FileDescriptor();
+
+ setInt.invoke(fdtosend,fdint);
+
+ FileDescriptor[] fds = {fdtosend};
+ mSocket.setFileDescriptorsForSend(fds);
+
+ Log.d("Openvpn", "Sending FD tosocket: " + fdtosend + " " + fdint);
+ // Trigger a send so we can close the fd on our side of the channel
+ String cmd = String.format("needok '%s' %s\n", needed, "ok");
+ managmentCommand(cmd);
+ pfd.close();
+ return true;
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+
private void processPWCommand(String argument) {
//argument has the form Need 'Private Key' password
int p1 =argument.indexOf('\'');
@@ -192,7 +248,7 @@ public class OpenVpnManagementThread implements Runnable { String needed = argument.substring(p1+1, p2);
String pw=null;
-
+
if(needed.equals("Private Key")) {
pw = mProfile.getPasswordPrivateKey();
diff --git a/src/de/blinkt/openvpn/OpenVpnService.java b/src/de/blinkt/openvpn/OpenVpnService.java index 5937f48e..5c9df8b4 100644 --- a/src/de/blinkt/openvpn/OpenVpnService.java +++ b/src/de/blinkt/openvpn/OpenVpnService.java @@ -65,7 +65,7 @@ public class OpenVpnService extends VpnService implements Handler.Callback { // Add 33. bit to ensure the loop terminates netmask += 1l << 32; - + int lenZeros = 0; while((netmask & 0x1) == 0) { lenZeros++; @@ -78,13 +78,13 @@ public class OpenVpnService extends VpnService implements Handler.Callback { } else { len =32 -lenZeros; } - + } @Override public String toString() { return String.format("%s/%d",mIp,len); } - + public boolean normalise(){ long ip=0; @@ -94,7 +94,7 @@ public class OpenVpnService extends VpnService implements Handler.Callback { ip += Integer.parseInt(ipt[1])<< 16; ip += Integer.parseInt(ipt[2])<< 8; ip += Integer.parseInt(ipt[3]); - + long newip = ip & (0xffffffffl << (32 -len)); if (newip != ip){ mIp = String.format("%d.%d.%d.%d", (newip & 0xff000000) >> 24,(newip & 0xff0000) >> 16, (newip & 0xff00) >> 8 ,newip & 0xff); @@ -213,7 +213,7 @@ public class OpenVpnService extends VpnService implements Handler.Callback { - public int openTun() { + public ParcelFileDescriptor openTun() { Builder builder = new Builder(); builder.addAddress(mLocalIP.mIp, mLocalIP.len); @@ -221,7 +221,7 @@ public class OpenVpnService extends VpnService implements Handler.Callback { for (String dns : mDnslist ) { builder.addDnsServer(dns); } - + builder.setMtu(mMtu); @@ -237,46 +237,47 @@ public class OpenVpnService extends VpnService implements Handler.Callback { builder.addSearchDomain(mDomain); String bconfig[] = new String[5]; - + bconfig[0]= getString(R.string.last_openvpn_tun_config); bconfig[1] = String.format(getString(R.string.local_ip_info,mLocalIP.mIp,mLocalIP.len,mMtu)); bconfig[2] = String.format(getString(R.string.dns_server_info, joinString(mDnslist))); bconfig[3] = String.format(getString(R.string.dns_domain_info, mDomain)); bconfig[4] = String.format(getString(R.string.routes_info, joinString(mRoutes))); - - + + OpenVPN.logBuilderConfig(bconfig); - + mDnslist.clear(); mRoutes.clear(); builder.setSession(mProfile.mName + " - " + mLocalIP); - + // Let the configure Button show the Log Intent intent = new Intent(getBaseContext(),LogWindow.class); PendingIntent startLW = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0); builder.setConfigureIntent(startLW); try { ParcelFileDescriptor pfd = builder.establish(); - return pfd.detachFd(); + return pfd; } catch (Exception e) { OpenVPN.logMessage(0, "", getString(R.string.tun_open_error)); OpenVPN.logMessage(0, "", getString(R.string.error) + e.getLocalizedMessage()); OpenVPN.logMessage(0, "", getString(R.string.tun_error_helpful)); - return -1; + return null; } } - + // Ugly, but java has no such method private <T> String joinString(Vector<T> vec) { String ret = ""; - if(vec.size() > 0); - ret = vec.get(0).toString(); - for(int i=1;i < vec.size();i++) { - ret = ret + ", " + vec.get(i).toString(); + if(vec.size() > 0){ + ret = vec.get(0).toString(); + for(int i=1;i < vec.size();i++) { + ret = ret + ", " + vec.get(i).toString(); + } } return ret; } @@ -303,10 +304,10 @@ public class OpenVpnService extends VpnService implements Handler.Callback { if(route.len == 32 && !mask.equals("255.255.255.255")) { OpenVPN.logMessage(0, "", String.format(getString(R.string.route_not_cidr,dest,mask))); } - + if(route.normalise()) OpenVPN.logMessage(0, "", String.format(getString(R.string.route_not_netip,dest,route.len,route.mIp))); - + mRoutes.add(route); } @@ -314,7 +315,7 @@ public class OpenVpnService extends VpnService implements Handler.Callback { public void setLocalIP(String local, String netmask,int mtu) { mLocalIP = new CIDRIP(local, netmask); mMtu = mtu; - + if(mLocalIP.len == 32 && !netmask.equals("255.255.255.255")) { OpenVPN.logMessage(0, "", String.format(getString(R.string.ip_not_cidr, local,netmask))); } diff --git a/src/de/blinkt/openvpn/VpnProfile.java b/src/de/blinkt/openvpn/VpnProfile.java index 75f0235d..3cbac8d7 100644 --- a/src/de/blinkt/openvpn/VpnProfile.java +++ b/src/de/blinkt/openvpn/VpnProfile.java @@ -92,62 +92,6 @@ public class VpnProfile implements Serializable{ public String mVerb="1"; - - public int describeContents() { - return 0; - } - - // Not used - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mAuthenticationType); - out.writeLong(mUuid.getMostSignificantBits()); - out.writeLong(mUuid.getLeastSignificantBits()); - out.writeString(mName); - out.writeString(mAlias); - out.writeString(mClientCertFilename); - out.writeString(mTLSAuthDirection); - out.writeString(mTLSAuthFilename); - out.writeString(mClientKeyFilename); - out.writeString(mCaFilename); - out.writeValue(mUseLzo); - out.writeString(mServerPort); - out.writeValue(mUseUdp); - out.writeString(mPKCS12Filename); - out.writeString(mPKCS12Password); - out.writeValue(mUseTLSAuth); - out.writeString(mServerName); - } - - private VpnProfile(Parcel in) { - mAuthenticationType = in.readInt(); - mUuid = new UUID(in.readLong(), in.readLong()); - mName = in.readString(); - mAlias = in.readString(); - mClientCertFilename = in.readString(); - mTLSAuthDirection = in.readString(); - mTLSAuthFilename = in.readString(); - mClientKeyFilename = in.readString(); - mCaFilename = in.readString(); - mUseLzo = (Boolean) in.readValue(null); - mServerPort = in.readString(); - mUseUdp = (Boolean) in.readValue(null); - mPKCS12Filename = in.readString(); - mPKCS12Password = in.readString(); - mUseTLSAuth = (Boolean) in.readValue(null); - mServerName = in.readString(); - } - - public static final Parcelable.Creator<VpnProfile> CREATOR - = new Parcelable.Creator<VpnProfile>() { - public VpnProfile createFromParcel(Parcel in) { - return new VpnProfile(in); - } - - public VpnProfile[] newArray(int size) { - return new VpnProfile[size]; - } - }; - public static String openVpnEscape(String unescape) { String escapedString = unescape.replace("\\", "\\\\"); escapedString = escapedString.replace("\"","\\\""); @@ -180,19 +124,22 @@ public class VpnProfile implements Serializable{ String cfg=""; - // Enable managment interface + // Enable managment interface + cfg += "# Enables connection to GUI\n"; cfg += "management "; cfg +=cacheDir.getAbsolutePath() + "/" + "mgmtsocket"; cfg += " unix\n"; cfg += "management-hold\n\n"; - /* only needed if client is compiled with P2MP Server support as early version - * accidently were */ cfg+="# /tmp does not exist on Android\n"; cfg+="tmp-dir "; cfg+=cacheDir.getAbsolutePath(); cfg+="\n\n"; + + cfg+="# Silences script security warning\n"; + cfg+="script-security 0\n\n"; + boolean useTLSClient = (mAuthenticationType != TYPE_STATICKEYS); |