diff options
author | Parménides GV <parmegv@sdf.org> | 2014-05-14 21:07:23 +0200 |
---|---|---|
committer | Parménides GV <parmegv@sdf.org> | 2014-05-14 21:07:23 +0200 |
commit | 2d0ed30fd65ce6d5832dc857a69c8063cb8b48b1 (patch) | |
tree | 4dce0cb932dcbc6b2f895e76dbd01d318c31fb8e /app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java | |
parent | edeb42401201c9c933cc2ba8175863225c813723 (diff) |
Filter R and BuildConfig to use ours.
Diffstat (limited to 'app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java')
-rw-r--r-- | app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java | 1196 |
1 files changed, 598 insertions, 598 deletions
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java index 6ef41e1b..4cba4f5f 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java @@ -1,598 +1,598 @@ -package de.blinkt.openvpn.core;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-import android.os.ParcelFileDescriptor;
-import android.preference.PreferenceManager;
-import android.util.Log;
-
-import org.jetbrains.annotations.NotNull;
-
-import de.blinkt.openvpn.R;
-import de.blinkt.openvpn.VpnProfile;
-import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.util.*;
-
-public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
-
- private static final String TAG = "openvpn";
- private LocalSocket mSocket;
- private VpnProfile mProfile;
- private OpenVpnService mOpenVPNService;
- private LinkedList<FileDescriptor> mFDList=new LinkedList<FileDescriptor>();
- private LocalServerSocket mServerSocket;
- private boolean mReleaseHold=true;
- private boolean mWaitingForRelease=false;
- private long mLastHoldRelease=0;
-
- private static Vector<OpenVpnManagementThread> active=new Vector<OpenVpnManagementThread>();
- private LocalSocket mServerSocketLocal;
-
- private pauseReason lastPauseReason = pauseReason.noNetwork;
-
- public OpenVpnManagementThread(VpnProfile profile, OpenVpnService openVpnService) {
- mProfile = profile;
- mOpenVPNService = openVpnService;
-
-
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(openVpnService);
- boolean managemeNetworkState = prefs.getBoolean("netchangereconnect", true);
- if(managemeNetworkState)
- mReleaseHold=false;
-
- }
-
- public boolean openManagementInterface(@NotNull Context c) {
- // Could take a while to open connection
- int tries=8;
-
- String socketName = (c.getCacheDir().getAbsolutePath() + "/" + "mgmtsocket");
- // The mServerSocketLocal is transferred to the LocalServerSocket, ignore warning
-
- mServerSocketLocal = new LocalSocket();
-
- while(tries > 0 && !mServerSocketLocal.isConnected()) {
- try {
- mServerSocketLocal.bind(new LocalSocketAddress(socketName,
- LocalSocketAddress.Namespace.FILESYSTEM));
- } catch (IOException e) {
- // wait 300 ms before retrying
- try { Thread.sleep(300);
- } catch (InterruptedException e1) {
- }
-
- }
- tries--;
- }
-
- try {
-
- mServerSocket = new LocalServerSocket(mServerSocketLocal.getFileDescriptor());
- return true;
- } catch (IOException e) {
- VpnStatus.logException(e);
- }
- return false;
-
-
- }
-
- public void managmentCommand(String cmd) {
- try {
- if(mSocket!=null && mSocket.getOutputStream() !=null) {
- mSocket.getOutputStream().write(cmd.getBytes());
- mSocket.getOutputStream().flush();
- }
- }catch (IOException e) {
- // Ignore socket stack traces
- }
- }
-
-
- @Override
- public void run() {
- byte [] buffer =new byte[2048];
- // mSocket.setSoTimeout(5); // Setting a timeout cannot be that bad
-
- String pendingInput="";
- active.add(this);
-
- try {
- // Wait for a client to connect
- mSocket= mServerSocket.accept();
- InputStream instream = mSocket.getInputStream();
- // Close the management socket after client connected
-
- mServerSocket.close();
- // Closing one of the two sockets also closes the other
- //mServerSocketLocal.close();
-
- while(true) {
- int numbytesread = instream.read(buffer);
- if(numbytesread==-1)
- return;
-
- FileDescriptor[] fds = null;
- try {
- fds = mSocket.getAncillaryFileDescriptors();
- } catch (IOException e) {
- VpnStatus.logException("Error reading fds from socket", e);
- }
- if(fds!=null){
- Collections.addAll(mFDList, fds);
- }
-
- String input = new String(buffer,0,numbytesread,"UTF-8");
-
- pendingInput += input;
-
- pendingInput=processInput(pendingInput);
-
-
-
- }
- } catch (IOException e) {
- if (!e.getMessage().equals("socket closed"))
- VpnStatus.logException(e);
- }
- active.remove(this);
- }
-
- //! Hack O Rama 2000!
- private void protectFileDescriptor(FileDescriptor fd) {
- Exception exp;
- try {
- Method getInt = FileDescriptor.class.getDeclaredMethod("getInt$");
- int fdint = (Integer) getInt.invoke(fd);
-
- // You can even get more evil by parsing toString() and extract the int from that :)
-
- boolean result = mOpenVPNService.protect(fdint);
- if (!result)
- VpnStatus.logWarning("Could not protect VPN socket");
-
-
- //ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(fdint);
- //pfd.close();
- NativeUtils.jniclose(fdint);
- return;
- } catch (NoSuchMethodException e) {
- exp =e;
- } catch (IllegalArgumentException e) {
- exp =e;
- } catch (IllegalAccessException e) {
- exp =e;
- } catch (InvocationTargetException e) {
- exp =e;
- } catch (NullPointerException e) {
- exp =e;
- }
-
- Log.d("Openvpn", "Failed to retrieve fd from socket: " + fd);
- VpnStatus.logException("Failed to retrieve fd from socket (" + fd + ")" , exp);
- }
-
- private String processInput(String pendingInput) {
-
-
- while(pendingInput.contains("\n")) {
- String[] tokens = pendingInput.split("\\r?\\n", 2);
- processCommand(tokens[0]);
- if(tokens.length == 1)
- // No second part, newline was at the end
- pendingInput="";
- else
- pendingInput=tokens[1];
- }
- return pendingInput;
- }
-
-
- private void processCommand(String command) {
- //Log.i(TAG, "Line from managment" + command);
-
-
- if (command.startsWith(">") && command.contains(":")) {
- String[] parts = command.split(":",2);
- String cmd = parts[0].substring(1);
- String argument = parts[1];
-
-
- if(cmd.equals("INFO")) {
- /* Ignore greeting from management */
- return;
- }else if (cmd.equals("PASSWORD")) {
- processPWCommand(argument);
- } else if (cmd.equals("HOLD")) {
- handleHold();
- } else if (cmd.equals("NEED-OK")) {
- processNeedCommand(argument);
- } else if (cmd.equals("BYTECOUNT")){
- processByteCount(argument);
- } else if (cmd.equals("STATE")) {
- processState(argument);
- } else if (cmd.equals("PROXY")) {
- processProxyCMD(argument);
- } else if (cmd.equals("LOG")) {
- processLogMessage(argument);
- } else if (cmd.equals("RSA_SIGN")) {
- processSignCommand(argument);
- } else {
- VpnStatus.logWarning("MGMT: Got unrecognized command" + command);
- Log.i(TAG, "Got unrecognized command" + command);
- }
- } else if (command.startsWith("SUCCESS:")) {
- /* Ignore this kind of message too */
- return;
- } else if (command.startsWith("PROTECTFD: ")) {
- FileDescriptor fdtoprotect = mFDList.pollFirst();
- if (fdtoprotect!=null)
- protectFileDescriptor(fdtoprotect);
- } else {
- Log.i(TAG, "Got unrecognized line from managment" + command);
- VpnStatus.logWarning("MGMT: Got unrecognized line from management:" + command);
- }
- }
-
- private void processLogMessage(String argument) {
- String[] args = argument.split(",",4);
- // 0 unix time stamp
- // 1 log level N,I,E etc.
- /*
- (b) zero or more message flags in a single string:
- I -- informational
- F -- fatal error
- N -- non-fatal error
- W -- warning
- D -- debug, and
- */
- // 2 log message
-
- Log.d("OpenVPN", argument);
-
- VpnStatus.LogLevel level;
- if (args[1].equals("I")) {
- level = VpnStatus.LogLevel.INFO;
- } else if (args[1].equals("W")) {
- level = VpnStatus.LogLevel.WARNING;
- } else if (args[1].equals("D")) {
- level = VpnStatus.LogLevel.VERBOSE;
- } else if (args[1].equals("F")) {
- level = VpnStatus.LogLevel.ERROR;
- } else {
- level = VpnStatus.LogLevel.INFO;
- }
-
- int ovpnlevel = Integer.parseInt(args[2]) & 0x0F;
- String msg = args[3];
-
- if (msg.startsWith("MANAGEMENT: CMD"))
- ovpnlevel = Math.max(4, ovpnlevel);
-
- VpnStatus.logMessageOpenVPN(level,ovpnlevel, msg);
- }
-
- private void handleHold() {
- if(mReleaseHold) {
- releaseHoldCmd();
- } else {
- mWaitingForRelease=true;
-
- VpnStatus.updateStatePause(lastPauseReason);
-
-
- }
- }
- private void releaseHoldCmd() {
- if ((System.currentTimeMillis()- mLastHoldRelease) < 5000) {
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- }
-
- }
- mWaitingForRelease=false;
- mLastHoldRelease = System.currentTimeMillis();
- managmentCommand("hold release\n");
- managmentCommand("bytecount " + mBytecountInterval + "\n");
- managmentCommand("state on\n");
- //managmentCommand("log on all\n");
- }
-
- public void releaseHold() {
- mReleaseHold=true;
- if(mWaitingForRelease)
- releaseHoldCmd();
-
- }
-
- private void processProxyCMD(String argument) {
- String[] args = argument.split(",",3);
- SocketAddress proxyaddr = ProxyDetection.detectProxy(mProfile);
-
-
- if(args.length >= 2) {
- String proto = args[1];
- if(proto.equals("UDP")) {
- proxyaddr=null;
- }
- }
-
- if(proxyaddr instanceof InetSocketAddress ){
- InetSocketAddress isa = (InetSocketAddress) proxyaddr;
-
- VpnStatus.logInfo(R.string.using_proxy, isa.getHostName(), isa.getPort());
-
- String proxycmd = String.format(Locale.ENGLISH,"proxy HTTP %s %d\n", isa.getHostName(),isa.getPort());
- managmentCommand(proxycmd);
- } else {
- managmentCommand("proxy NONE\n");
- }
-
- }
- private void processState(String argument) {
- String[] args = argument.split(",",3);
- String currentstate = args[1];
-
- if(args[2].equals(",,"))
- VpnStatus.updateStateString(currentstate, "");
- else
- VpnStatus.updateStateString(currentstate, args[2]);
- }
-
-
- private void processByteCount(String argument) {
- // >BYTECOUNT:{BYTES_IN},{BYTES_OUT}
- int comma = argument.indexOf(',');
- long in = Long.parseLong(argument.substring(0, comma));
- long out = Long.parseLong(argument.substring(comma+1));
-
- VpnStatus.updateByteCount(in, out);
-
- }
-
-
-
- private void processNeedCommand(String argument) {
- int p1 =argument.indexOf('\'');
- int p2 = argument.indexOf('\'',p1+1);
-
- 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);
- } else if (needed.equals("DNSSERVER")) {
- mOpenVPNService.addDNS(extra);
- }else if (needed.equals("DNSDOMAIN")){
- mOpenVPNService.setDomain(extra);
- } else if (needed.equals("ROUTE")) {
- String[] routeparts = extra.split(" ");
-
- /*
- buf_printf (&out, "%s %s %s dev %s", network, netmask, gateway, rgi->iface);
- else
- buf_printf (&out, "%s %s %s", network, netmask, gateway);
- */
-
- if(routeparts.length==5) {
- assert(routeparts[3].equals("dev"));
- mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], routeparts[4]);
- } else if (routeparts.length >= 3) {
- mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], null);
- } else {
- VpnStatus.logError("Unrecognized ROUTE cmd:" + Arrays.toString(routeparts) + " | " + argument);
- }
-
- } else if (needed.equals("ROUTE6")) {
- String[] routeparts = extra.split(" ");
- mOpenVPNService.addRoutev6(routeparts[0],routeparts[1]);
- } else if (needed.equals("IFCONFIG")) {
- String[] ifconfigparts = extra.split(" ");
- int mtu = Integer.parseInt(ifconfigparts[2]);
- mOpenVPNService.setLocalIP(ifconfigparts[0], ifconfigparts[1],mtu,ifconfigparts[3]);
- } else if (needed.equals("IFCONFIG6")) {
- mOpenVPNService.setLocalIPv6(extra);
-
- } else if (needed.equals("PERSIST_TUN_ACTION")) {
- // check if tun cfg stayed the same
- status = mOpenVPNService.getTunReopenStatus();
- } 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, status);
- managmentCommand(cmd);
- }
-
- private boolean sendTunFD (String needed, String extra) {
- Exception exp;
- if(!extra.equals("tun")) {
- // We only support tun
- VpnStatus.logError(String.format("Device type %s requested, but only tun is possible with the Android API, sorry!",extra));
-
- 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);
-
- // Trigger a send so we can close the fd on our side of the channel
- // The API documentation fails to mention that it will not reset the file descriptor to
- // be send and will happily send the file descriptor on every write ...
- String cmd = String.format("needok '%s' %s\n", needed, "ok");
- managmentCommand(cmd);
-
- // Set the FileDescriptor to null to stop this mad behavior
- mSocket.setFileDescriptorsForSend(null);
-
- pfd.close();
-
- return true;
- } catch (NoSuchMethodException e) {
- exp =e;
- } catch (IllegalArgumentException e) {
- exp =e;
- } catch (IllegalAccessException e) {
- exp =e;
- } catch (InvocationTargetException e) {
- exp =e;
- } catch (IOException e) {
- exp =e;
- }
- VpnStatus.logException("Could not send fd over socket" , exp);
-
- return false;
- }
-
- private void processPWCommand(String argument) {
- //argument has the form Need 'Private Key' password
- // or ">PASSWORD:Verification Failed: '%s' ['%s']"
- String needed;
-
-
-
- try{
-
- int p1 = argument.indexOf('\'');
- int p2 = argument.indexOf('\'',p1+1);
- needed = argument.substring(p1+1, p2);
- if (argument.startsWith("Verification Failed")) {
- proccessPWFailed(needed, argument.substring(p2+1));
- return;
- }
- } catch (StringIndexOutOfBoundsException sioob) {
- VpnStatus.logError("Could not parse management Password command: " + argument);
- return;
- }
-
- String pw=null;
-
- if(needed.equals("Private Key")) {
- pw = mProfile.getPasswordPrivateKey();
- } else if (needed.equals("Auth")) {
- String usercmd = String.format("username '%s' %s\n",
- needed, VpnProfile.openVpnEscape(mProfile.mUsername));
- managmentCommand(usercmd);
- pw = mProfile.getPasswordAuth();
- }
- if(pw!=null) {
- String cmd = String.format("password '%s' %s\n", needed, VpnProfile.openVpnEscape(pw));
- managmentCommand(cmd);
- } else {
- VpnStatus.logError(String.format("Openvpn requires Authentication type '%s' but no password/key information available", needed));
- }
-
- }
-
-
-
-
- private void proccessPWFailed(String needed, String args) {
- VpnStatus.updateStateString("AUTH_FAILED", needed + args, R.string.state_auth_failed, ConnectionStatus.LEVEL_AUTH_FAILED);
- }
-
-
- private static boolean stopOpenVPN() {
- boolean sendCMD=false;
- for (OpenVpnManagementThread mt: active){
- mt.managmentCommand("signal SIGINT\n");
- sendCMD=true;
- try {
- if(mt.mSocket !=null)
- mt.mSocket.close();
- } catch (IOException e) {
- // Ignore close error on already closed socket
- }
- }
- return sendCMD;
- }
-
- @Override
- public void networkChange() {
- if(!mWaitingForRelease)
- managmentCommand("network-change\n");
- }
-
- public void signalusr1() {
- mReleaseHold=false;
-
- if(!mWaitingForRelease)
- managmentCommand("signal SIGUSR1\n");
- else
- // If signalusr1 is called update the state string
- // if there is another for stopping
- VpnStatus.updateStatePause(lastPauseReason);
- }
-
- public void reconnect() {
- signalusr1();
- releaseHold();
- }
-
- private void processSignCommand(String b64data) {
-
- String signed_string = mProfile.getSignedData(b64data);
- if(signed_string==null) {
- managmentCommand("rsa-sig\n");
- managmentCommand("\nEND\n");
- stopOpenVPN();
- return;
- }
- managmentCommand("rsa-sig\n");
- managmentCommand(signed_string);
- managmentCommand("\nEND\n");
- }
-
- @Override
- public void pause (pauseReason reason) {
- lastPauseReason = reason;
- signalusr1();
- }
-
- @Override
- public void resume() {
- releaseHold();
- /* Reset the reason why we are disconnected */
- lastPauseReason = pauseReason.noNetwork;
- }
-
- @Override
- public boolean stopVPN() {
- return stopOpenVPN();
- }
-}
+package de.blinkt.openvpn.core; + +import android.content.Context; +import android.content.SharedPreferences; +import android.net.LocalServerSocket; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.os.ParcelFileDescriptor; +import android.preference.PreferenceManager; +import android.util.Log; + +import org.jetbrains.annotations.NotNull; + +import se.leap.bitmaskclient.R; +import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.*; + +public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { + + private static final String TAG = "openvpn"; + private LocalSocket mSocket; + private VpnProfile mProfile; + private OpenVpnService mOpenVPNService; + private LinkedList<FileDescriptor> mFDList=new LinkedList<FileDescriptor>(); + private LocalServerSocket mServerSocket; + private boolean mReleaseHold=true; + private boolean mWaitingForRelease=false; + private long mLastHoldRelease=0; + + private static Vector<OpenVpnManagementThread> active=new Vector<OpenVpnManagementThread>(); + private LocalSocket mServerSocketLocal; + + private pauseReason lastPauseReason = pauseReason.noNetwork; + + public OpenVpnManagementThread(VpnProfile profile, OpenVpnService openVpnService) { + mProfile = profile; + mOpenVPNService = openVpnService; + + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(openVpnService); + boolean managemeNetworkState = prefs.getBoolean("netchangereconnect", true); + if(managemeNetworkState) + mReleaseHold=false; + + } + + public boolean openManagementInterface(@NotNull Context c) { + // Could take a while to open connection + int tries=8; + + String socketName = (c.getCacheDir().getAbsolutePath() + "/" + "mgmtsocket"); + // The mServerSocketLocal is transferred to the LocalServerSocket, ignore warning + + mServerSocketLocal = new LocalSocket(); + + while(tries > 0 && !mServerSocketLocal.isConnected()) { + try { + mServerSocketLocal.bind(new LocalSocketAddress(socketName, + LocalSocketAddress.Namespace.FILESYSTEM)); + } catch (IOException e) { + // wait 300 ms before retrying + try { Thread.sleep(300); + } catch (InterruptedException e1) { + } + + } + tries--; + } + + try { + + mServerSocket = new LocalServerSocket(mServerSocketLocal.getFileDescriptor()); + return true; + } catch (IOException e) { + VpnStatus.logException(e); + } + return false; + + + } + + public void managmentCommand(String cmd) { + try { + if(mSocket!=null && mSocket.getOutputStream() !=null) { + mSocket.getOutputStream().write(cmd.getBytes()); + mSocket.getOutputStream().flush(); + } + }catch (IOException e) { + // Ignore socket stack traces + } + } + + + @Override + public void run() { + byte [] buffer =new byte[2048]; + // mSocket.setSoTimeout(5); // Setting a timeout cannot be that bad + + String pendingInput=""; + active.add(this); + + try { + // Wait for a client to connect + mSocket= mServerSocket.accept(); + InputStream instream = mSocket.getInputStream(); + // Close the management socket after client connected + + mServerSocket.close(); + // Closing one of the two sockets also closes the other + //mServerSocketLocal.close(); + + while(true) { + int numbytesread = instream.read(buffer); + if(numbytesread==-1) + return; + + FileDescriptor[] fds = null; + try { + fds = mSocket.getAncillaryFileDescriptors(); + } catch (IOException e) { + VpnStatus.logException("Error reading fds from socket", e); + } + if(fds!=null){ + Collections.addAll(mFDList, fds); + } + + String input = new String(buffer,0,numbytesread,"UTF-8"); + + pendingInput += input; + + pendingInput=processInput(pendingInput); + + + + } + } catch (IOException e) { + if (!e.getMessage().equals("socket closed")) + VpnStatus.logException(e); + } + active.remove(this); + } + + //! Hack O Rama 2000! + private void protectFileDescriptor(FileDescriptor fd) { + Exception exp; + try { + Method getInt = FileDescriptor.class.getDeclaredMethod("getInt$"); + int fdint = (Integer) getInt.invoke(fd); + + // You can even get more evil by parsing toString() and extract the int from that :) + + boolean result = mOpenVPNService.protect(fdint); + if (!result) + VpnStatus.logWarning("Could not protect VPN socket"); + + + //ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(fdint); + //pfd.close(); + NativeUtils.jniclose(fdint); + return; + } catch (NoSuchMethodException e) { + exp =e; + } catch (IllegalArgumentException e) { + exp =e; + } catch (IllegalAccessException e) { + exp =e; + } catch (InvocationTargetException e) { + exp =e; + } catch (NullPointerException e) { + exp =e; + } + + Log.d("Openvpn", "Failed to retrieve fd from socket: " + fd); + VpnStatus.logException("Failed to retrieve fd from socket (" + fd + ")" , exp); + } + + private String processInput(String pendingInput) { + + + while(pendingInput.contains("\n")) { + String[] tokens = pendingInput.split("\\r?\\n", 2); + processCommand(tokens[0]); + if(tokens.length == 1) + // No second part, newline was at the end + pendingInput=""; + else + pendingInput=tokens[1]; + } + return pendingInput; + } + + + private void processCommand(String command) { + //Log.i(TAG, "Line from managment" + command); + + + if (command.startsWith(">") && command.contains(":")) { + String[] parts = command.split(":",2); + String cmd = parts[0].substring(1); + String argument = parts[1]; + + + if(cmd.equals("INFO")) { + /* Ignore greeting from management */ + return; + }else if (cmd.equals("PASSWORD")) { + processPWCommand(argument); + } else if (cmd.equals("HOLD")) { + handleHold(); + } else if (cmd.equals("NEED-OK")) { + processNeedCommand(argument); + } else if (cmd.equals("BYTECOUNT")){ + processByteCount(argument); + } else if (cmd.equals("STATE")) { + processState(argument); + } else if (cmd.equals("PROXY")) { + processProxyCMD(argument); + } else if (cmd.equals("LOG")) { + processLogMessage(argument); + } else if (cmd.equals("RSA_SIGN")) { + processSignCommand(argument); + } else { + VpnStatus.logWarning("MGMT: Got unrecognized command" + command); + Log.i(TAG, "Got unrecognized command" + command); + } + } else if (command.startsWith("SUCCESS:")) { + /* Ignore this kind of message too */ + return; + } else if (command.startsWith("PROTECTFD: ")) { + FileDescriptor fdtoprotect = mFDList.pollFirst(); + if (fdtoprotect!=null) + protectFileDescriptor(fdtoprotect); + } else { + Log.i(TAG, "Got unrecognized line from managment" + command); + VpnStatus.logWarning("MGMT: Got unrecognized line from management:" + command); + } + } + + private void processLogMessage(String argument) { + String[] args = argument.split(",",4); + // 0 unix time stamp + // 1 log level N,I,E etc. + /* + (b) zero or more message flags in a single string: + I -- informational + F -- fatal error + N -- non-fatal error + W -- warning + D -- debug, and + */ + // 2 log message + + Log.d("OpenVPN", argument); + + VpnStatus.LogLevel level; + if (args[1].equals("I")) { + level = VpnStatus.LogLevel.INFO; + } else if (args[1].equals("W")) { + level = VpnStatus.LogLevel.WARNING; + } else if (args[1].equals("D")) { + level = VpnStatus.LogLevel.VERBOSE; + } else if (args[1].equals("F")) { + level = VpnStatus.LogLevel.ERROR; + } else { + level = VpnStatus.LogLevel.INFO; + } + + int ovpnlevel = Integer.parseInt(args[2]) & 0x0F; + String msg = args[3]; + + if (msg.startsWith("MANAGEMENT: CMD")) + ovpnlevel = Math.max(4, ovpnlevel); + + VpnStatus.logMessageOpenVPN(level,ovpnlevel, msg); + } + + private void handleHold() { + if(mReleaseHold) { + releaseHoldCmd(); + } else { + mWaitingForRelease=true; + + VpnStatus.updateStatePause(lastPauseReason); + + + } + } + private void releaseHoldCmd() { + if ((System.currentTimeMillis()- mLastHoldRelease) < 5000) { + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + } + + } + mWaitingForRelease=false; + mLastHoldRelease = System.currentTimeMillis(); + managmentCommand("hold release\n"); + managmentCommand("bytecount " + mBytecountInterval + "\n"); + managmentCommand("state on\n"); + //managmentCommand("log on all\n"); + } + + public void releaseHold() { + mReleaseHold=true; + if(mWaitingForRelease) + releaseHoldCmd(); + + } + + private void processProxyCMD(String argument) { + String[] args = argument.split(",",3); + SocketAddress proxyaddr = ProxyDetection.detectProxy(mProfile); + + + if(args.length >= 2) { + String proto = args[1]; + if(proto.equals("UDP")) { + proxyaddr=null; + } + } + + if(proxyaddr instanceof InetSocketAddress ){ + InetSocketAddress isa = (InetSocketAddress) proxyaddr; + + VpnStatus.logInfo(R.string.using_proxy, isa.getHostName(), isa.getPort()); + + String proxycmd = String.format(Locale.ENGLISH,"proxy HTTP %s %d\n", isa.getHostName(),isa.getPort()); + managmentCommand(proxycmd); + } else { + managmentCommand("proxy NONE\n"); + } + + } + private void processState(String argument) { + String[] args = argument.split(",",3); + String currentstate = args[1]; + + if(args[2].equals(",,")) + VpnStatus.updateStateString(currentstate, ""); + else + VpnStatus.updateStateString(currentstate, args[2]); + } + + + private void processByteCount(String argument) { + // >BYTECOUNT:{BYTES_IN},{BYTES_OUT} + int comma = argument.indexOf(','); + long in = Long.parseLong(argument.substring(0, comma)); + long out = Long.parseLong(argument.substring(comma+1)); + + VpnStatus.updateByteCount(in, out); + + } + + + + private void processNeedCommand(String argument) { + int p1 =argument.indexOf('\''); + int p2 = argument.indexOf('\'',p1+1); + + 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); + } else if (needed.equals("DNSSERVER")) { + mOpenVPNService.addDNS(extra); + }else if (needed.equals("DNSDOMAIN")){ + mOpenVPNService.setDomain(extra); + } else if (needed.equals("ROUTE")) { + String[] routeparts = extra.split(" "); + + /* + buf_printf (&out, "%s %s %s dev %s", network, netmask, gateway, rgi->iface); + else + buf_printf (&out, "%s %s %s", network, netmask, gateway); + */ + + if(routeparts.length==5) { + assert(routeparts[3].equals("dev")); + mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], routeparts[4]); + } else if (routeparts.length >= 3) { + mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], null); + } else { + VpnStatus.logError("Unrecognized ROUTE cmd:" + Arrays.toString(routeparts) + " | " + argument); + } + + } else if (needed.equals("ROUTE6")) { + String[] routeparts = extra.split(" "); + mOpenVPNService.addRoutev6(routeparts[0],routeparts[1]); + } else if (needed.equals("IFCONFIG")) { + String[] ifconfigparts = extra.split(" "); + int mtu = Integer.parseInt(ifconfigparts[2]); + mOpenVPNService.setLocalIP(ifconfigparts[0], ifconfigparts[1],mtu,ifconfigparts[3]); + } else if (needed.equals("IFCONFIG6")) { + mOpenVPNService.setLocalIPv6(extra); + + } else if (needed.equals("PERSIST_TUN_ACTION")) { + // check if tun cfg stayed the same + status = mOpenVPNService.getTunReopenStatus(); + } 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, status); + managmentCommand(cmd); + } + + private boolean sendTunFD (String needed, String extra) { + Exception exp; + if(!extra.equals("tun")) { + // We only support tun + VpnStatus.logError(String.format("Device type %s requested, but only tun is possible with the Android API, sorry!",extra)); + + 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); + + // Trigger a send so we can close the fd on our side of the channel + // The API documentation fails to mention that it will not reset the file descriptor to + // be send and will happily send the file descriptor on every write ... + String cmd = String.format("needok '%s' %s\n", needed, "ok"); + managmentCommand(cmd); + + // Set the FileDescriptor to null to stop this mad behavior + mSocket.setFileDescriptorsForSend(null); + + pfd.close(); + + return true; + } catch (NoSuchMethodException e) { + exp =e; + } catch (IllegalArgumentException e) { + exp =e; + } catch (IllegalAccessException e) { + exp =e; + } catch (InvocationTargetException e) { + exp =e; + } catch (IOException e) { + exp =e; + } + VpnStatus.logException("Could not send fd over socket" , exp); + + return false; + } + + private void processPWCommand(String argument) { + //argument has the form Need 'Private Key' password + // or ">PASSWORD:Verification Failed: '%s' ['%s']" + String needed; + + + + try{ + + int p1 = argument.indexOf('\''); + int p2 = argument.indexOf('\'',p1+1); + needed = argument.substring(p1+1, p2); + if (argument.startsWith("Verification Failed")) { + proccessPWFailed(needed, argument.substring(p2+1)); + return; + } + } catch (StringIndexOutOfBoundsException sioob) { + VpnStatus.logError("Could not parse management Password command: " + argument); + return; + } + + String pw=null; + + if(needed.equals("Private Key")) { + pw = mProfile.getPasswordPrivateKey(); + } else if (needed.equals("Auth")) { + String usercmd = String.format("username '%s' %s\n", + needed, VpnProfile.openVpnEscape(mProfile.mUsername)); + managmentCommand(usercmd); + pw = mProfile.getPasswordAuth(); + } + if(pw!=null) { + String cmd = String.format("password '%s' %s\n", needed, VpnProfile.openVpnEscape(pw)); + managmentCommand(cmd); + } else { + VpnStatus.logError(String.format("Openvpn requires Authentication type '%s' but no password/key information available", needed)); + } + + } + + + + + private void proccessPWFailed(String needed, String args) { + VpnStatus.updateStateString("AUTH_FAILED", needed + args, R.string.state_auth_failed, ConnectionStatus.LEVEL_AUTH_FAILED); + } + + + private static boolean stopOpenVPN() { + boolean sendCMD=false; + for (OpenVpnManagementThread mt: active){ + mt.managmentCommand("signal SIGINT\n"); + sendCMD=true; + try { + if(mt.mSocket !=null) + mt.mSocket.close(); + } catch (IOException e) { + // Ignore close error on already closed socket + } + } + return sendCMD; + } + + @Override + public void networkChange() { + if(!mWaitingForRelease) + managmentCommand("network-change\n"); + } + + public void signalusr1() { + mReleaseHold=false; + + if(!mWaitingForRelease) + managmentCommand("signal SIGUSR1\n"); + else + // If signalusr1 is called update the state string + // if there is another for stopping + VpnStatus.updateStatePause(lastPauseReason); + } + + public void reconnect() { + signalusr1(); + releaseHold(); + } + + private void processSignCommand(String b64data) { + + String signed_string = mProfile.getSignedData(b64data); + if(signed_string==null) { + managmentCommand("rsa-sig\n"); + managmentCommand("\nEND\n"); + stopOpenVPN(); + return; + } + managmentCommand("rsa-sig\n"); + managmentCommand(signed_string); + managmentCommand("\nEND\n"); + } + + @Override + public void pause (pauseReason reason) { + lastPauseReason = reason; + signalusr1(); + } + + @Override + public void resume() { + releaseHold(); + /* Reset the reason why we are disconnected */ + lastPauseReason = pauseReason.noNetwork; + } + + @Override + public boolean stopVPN() { + return stopOpenVPN(); + } +} |