[PATCH/RFC V2 23/26] JNI: Initial commit of Java library + example program

Kevin Cernekee cernekee at gmail.com
Sun Aug 11 21:49:24 EDT 2013


Signed-off-by: Kevin Cernekee <cernekee at gmail.com>
---
 java/.gitignore                                    |    2 +
 java/README                                        |   22 ++
 java/build.xml                                     |   33 +++
 java/src/com/example/LibTest.java                  |  201 +++++++++++++++++
 .../infradead/libopenconnect/LibOpenConnect.java   |  231 ++++++++++++++++++++
 5 files changed, 489 insertions(+)
 create mode 100644 java/.gitignore
 create mode 100644 java/README
 create mode 100644 java/build.xml
 create mode 100644 java/src/com/example/LibTest.java
 create mode 100644 java/src/org/infradead/libopenconnect/LibOpenConnect.java

diff --git a/java/.gitignore b/java/.gitignore
new file mode 100644
index 0000000..edd9d60
--- /dev/null
+++ b/java/.gitignore
@@ -0,0 +1,2 @@
+build/
+dist/
diff --git a/java/README b/java/README
new file mode 100644
index 0000000..1bdec00
--- /dev/null
+++ b/java/README
@@ -0,0 +1,22 @@
+Description:
+
+This directory contains a JNI interface layer for libopenconnect, and a
+demo program to show how it can be used.
+
+Build instructions:
+
+From the top level, run:
+
+    ./configure --with-java
+    make
+    cd java
+    ant
+    sudo java -Djava.library.path=../.libs -jar dist/example.jar <server_ip>
+
+If ocproxy[1] is installed somewhere in your $PATH, this can be run as a
+non-root user and it should be pingable from across the VPN.
+
+Test/demo code is in src/com/example/
+OpenConnect wrapper library is in src/org/infradead/libopenconnect/
+
+[1] http://repo.or.cz/w/ocproxy.git
diff --git a/java/build.xml b/java/build.xml
new file mode 100644
index 0000000..acbe259
--- /dev/null
+++ b/java/build.xml
@@ -0,0 +1,33 @@
+<project name="LibOpenConnect" default="dist" basedir=".">
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+	<property name="src"   location="src"/>
+
+	<property name="jar_lib" location="${dist}/openconnect-wrapper.jar"/>
+	<property name="jar_app" location="${dist}/example.jar"/>
+
+	<target name="init">
+		<mkdir dir="${build}"/>
+	</target>
+
+	<target name="compile" depends="init">
+		<javac srcdir="${src}" destdir="${build}"
+		       includeantruntime="false"/>
+	</target>
+
+	<target name="dist" depends="compile">
+		<jar jarfile="${jar_lib}" basedir="${build}"
+		     includes="org/infradead/libopenconnect/*" />
+
+		<jar jarfile="${jar_app}" basedir="${build}">
+			<manifest>
+				<attribute name="Main-Class" value="com.example.LibTest" />
+			</manifest>
+		</jar>
+	</target>
+
+	<target name="clean">
+		<delete dir="${build}"/>
+		<delete dir="${dist}"/>
+	</target>
+</project>
diff --git a/java/src/com/example/LibTest.java b/java/src/com/example/LibTest.java
new file mode 100644
index 0000000..6d7f3cb
--- /dev/null
+++ b/java/src/com/example/LibTest.java
@@ -0,0 +1,201 @@
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright © 2013 Kevin Cernekee <cernekee at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to:
+ *
+ *   Free Software Foundation, Inc.
+ *   51 Franklin Street, Fifth Floor,
+ *   Boston, MA 02110-1301 USA
+ */
+
+package com.example;
+
+import java.io.*;
+import java.util.*;
+import org.infradead.libopenconnect.LibOpenConnect;
+
+public final class LibTest {
+
+	private static void die(String msg) {
+		System.out.println(msg);
+		System.exit(1);
+	}
+
+	private static String getline() {
+		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+		try {
+			String line = br.readLine();
+			return line;
+		} catch (IOException e) {
+			die("\nI/O error");
+		}
+		return "";
+	}
+
+	private static class TestLib extends LibOpenConnect {
+		public int onValidatePeerCert(String msg) {
+			System.out.println("cert warning: " + msg);
+			System.out.println("cert SHA1: " + getCertSHA1());
+			System.out.println("cert details: " + getCertDetails());
+
+			byte der[] = getCertDER();
+			System.out.println("DER is " + der.length + " bytes long");
+
+			System.out.print("\nAccept this certificate? [n] ");
+			String s = getline();
+			if (s.startsWith("y") || s.startsWith("Y")) {
+				return 0;
+			} else {
+				return -1;
+			}
+		}
+
+		public int onWriteNewConfig(byte[] buf) {
+			System.out.println("new config: " + buf.length + " bytes");
+			return 0;
+		}
+
+		public int onProcessAuthForm(LibOpenConnect.AuthForm authForm) {
+			System.out.println("\nAuthForm:");
+			System.out.println("+-banner: " + authForm.banner);
+			System.out.println("+-message: " + authForm.message);
+			System.out.println("+-error: " + authForm.error);
+			System.out.println("+-authID: " + authForm.authID);
+			System.out.println("+-method: " + authForm.method);
+			System.out.println("+-action: " + authForm.action);
+
+			for (FormOpt fo : authForm.opts) {
+				System.out.println("->FormOpt: ");
+				System.out.println("  +-type: " + fo.type);
+				System.out.println("  +-name: " + fo.name);
+				System.out.println("  +-label: " + fo.label);
+
+				if (fo.type == OC_FORM_OPT_SELECT) {
+					for (FormChoice fc : fo.choices) {
+						System.out.println("--->FormChoice: ");
+						System.out.println("    +-name: " + fc.name);
+						System.out.println("    +-label: " + fc.label);
+						System.out.println("    +-authType: " + fc.authType);
+						System.out.println("    +-overrideName: " + fc.overrideName);
+						System.out.println("    +-overrideLabel: " + fc.overrideLabel);
+					}
+				}
+
+				if (fo.type == OC_FORM_OPT_TEXT ||
+				    fo.type == OC_FORM_OPT_PASSWORD ||
+				    fo.type == OC_FORM_OPT_SELECT) {
+					System.out.print("\n" + fo.label + " ");
+					fo.setValue(getline());
+				}
+			}
+			System.out.println("");
+
+			return AUTH_FORM_PARSED;
+		}
+
+		public void onProgress(int level, String msg) {
+			switch (level) {
+			case LibOpenConnect.PRG_TRACE:
+				System.out.print("TRACE: " + msg);
+				break;
+			case LibOpenConnect.PRG_DEBUG:
+				System.out.print("DEBUG: " + msg);
+				break;
+			case LibOpenConnect.PRG_INFO:
+				System.out.print("INFO:  " + msg);
+				break;
+			case LibOpenConnect.PRG_ERR:
+				System.out.print("ERROR: " + msg);
+				break;
+			}
+		}
+	}
+
+	private static void printList(String pfx, List<String> ss) {
+		System.out.print(pfx + ":");
+
+		if (ss.size() == 0) {
+			System.out.println(" <empty>");
+			return;
+		}
+		for (String s : ss) {
+			System.out.print(" " + s);
+		}
+		System.out.println("");
+	}
+
+	private static void printIPInfo(LibOpenConnect.IPInfo ip) {
+		System.out.println("\nIPInfo:");
+		System.out.println("+-IPv4: " + ip.addr + " / " + ip.netmask);
+		System.out.println("+-IPv6: " + ip.addr6 + " / " + ip.netmask6);
+		System.out.println("+-Domain: " + ip.domain);
+		System.out.println("+-proxy.pac: " + ip.proxyPac);
+		System.out.println("+-MTU: " + ip.MTU);
+		printList("+-DNS", ip.DNS);
+		printList("+-NBNS", ip.NBNS);
+		printList("+-Split DNS", ip.splitDNS);
+		printList("+-Split includes", ip.splitIncludes);
+		printList("+-Split excludes", ip.splitExcludes);
+		System.out.println("");
+	}
+
+	public static void main(String argv[]) {
+		System.loadLibrary("openconnect-wrapper");
+		LibOpenConnect lib = new TestLib();
+
+		if (argv.length != 1)
+			die("usage: LibTest <server_name>");
+
+		System.out.println("OpenConnect version: " + lib.getVersion());
+		System.out.println("  PKCS=" + lib.hasPKCS11Support() +
+				   ", TSS=" + lib.hasTSSBlobSupport() +
+				   ", STOKEN=" + lib.hasStokenSupport() +
+				   ", OATH=" + lib.hasOATHSupport());
+		lib.setReportedOS("win");
+		lib.setLogLevel(lib.PRG_DEBUG);
+		//lib.setTokenMode(LibOpenConnect.OC_TOKEN_MODE_STOKEN, null);
+		if (new File("csd.sh").exists()) {
+			lib.setCSDWrapper("csd.sh");
+		}
+		lib.parseURL(argv[0]);
+
+		int ret = lib.obtainCookie();
+		if (ret < 0)
+			die("obtainCookie() returned error");
+		else if (ret > 0)
+			die("Aborted by user");
+
+		String cookie = lib.getCookie();
+		if (cookie.length() > 40) {
+			System.out.println("Cookie: " + cookie.substring(0, 40) + "...");
+		} else {
+			System.out.println("Cookie: " + cookie);
+		}
+
+		if (lib.makeCSTPConnection() != 0)
+			die("Error establishing VPN link");
+
+		printIPInfo(lib.getIPInfo());
+
+		if (lib.setupTunDevice("/etc/vpnc/vpnc-script", null) != 0 &&
+		    lib.setupTunScript("ocproxy") != 0)
+			die("Error setting up tunnel");
+
+		if (lib.setupDTLS(60) != 0)
+			die("Error setting up DTLS");
+
+		lib.mainloop();
+	}
+}
diff --git a/java/src/org/infradead/libopenconnect/LibOpenConnect.java b/java/src/org/infradead/libopenconnect/LibOpenConnect.java
new file mode 100644
index 0000000..8911634
--- /dev/null
+++ b/java/src/org/infradead/libopenconnect/LibOpenConnect.java
@@ -0,0 +1,231 @@
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright © 2013 Kevin Cernekee <cernekee at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to:
+ *
+ *   Free Software Foundation, Inc.
+ *   51 Franklin Street, Fifth Floor,
+ *   Boston, MA 02110-1301 USA
+ */
+
+package org.infradead.libopenconnect;
+
+import java.util.ArrayList;
+
+public abstract class LibOpenConnect {
+
+	/* constants */
+
+	public static final int AUTH_FORM_ERROR = -1;
+	public static final int AUTH_FORM_PARSED = 0;
+	public static final int AUTH_FORM_CANCELLED = 1;
+
+	public static final int OC_FORM_OPT_TEXT = 1;
+	public static final int OC_FORM_OPT_PASSWORD = 2;
+	public static final int OC_FORM_OPT_SELECT = 3;
+	public static final int OC_FORM_OPT_HIDDEN = 4;
+	public static final int OC_FORM_OPT_TOKEN = 5;
+
+	public static final int OC_TOKEN_MODE_NONE = 0;
+	public static final int OC_TOKEN_MODE_STOKEN = 1;
+	public static final int OC_TOKEN_MODE_TOTP = 2;
+
+	public static final int PRG_ERR = 0;
+	public static final int PRG_INFO = 1;
+	public static final int PRG_DEBUG = 2;
+	public static final int PRG_TRACE = 3;
+
+	/* required callbacks */
+
+	public abstract int onValidatePeerCert(String msg);
+	public abstract int onWriteNewConfig(byte[] buf);
+	public abstract int onProcessAuthForm(AuthForm authForm);
+	public abstract void onProgress(int level, String msg);
+
+	/* create/destroy library instances */
+
+	public LibOpenConnect() {
+		libctx = init("OpenConnect VPN Agent (Java)");
+	}
+
+	public synchronized void destroy() {
+		if (libctx != 0) {
+			free();
+			libctx = 0;
+		}
+	}
+
+	public void cancel() {
+		synchronized (cancelLock) {
+			if (!canceled) {
+				doCancel();
+				canceled = true;
+			}
+		}
+	}
+
+	public boolean isCanceled() {
+		synchronized (cancelLock) {
+			return canceled;
+		}
+	}
+
+	/* control operations */
+
+	public synchronized native int parseURL(String url);
+	public synchronized native int obtainCookie();
+	public synchronized native void clearCookie();
+	public synchronized native void resetSSL();
+	public synchronized native int makeCSTPConnection();
+	public synchronized native int setupTunDevice(String vpncScript, String IFName);
+	public synchronized native int setupTunScript(String tunScript);
+	public synchronized native int setupTunFD(int tunFD);
+	public synchronized native int setupDTLS(int attemptPeriod);
+	public synchronized native int mainloop();
+
+	/* connection settings */
+
+	public synchronized native void setLogLevel(int level);
+	public synchronized native int passphraseFromFSID();
+	public synchronized native void setCertExpiryWarning(int seconds);
+	public synchronized native int setHTTPProxy(String proxy);
+	public synchronized native void setXMLSHA1(String hash);
+	public synchronized native void setHostname(String hostname);
+	public synchronized native void setUrlpath(String urlpath);
+	public synchronized native void setCAFile(String caFile);
+	public synchronized native void setReportedOS(String os);
+	public synchronized native int setTokenMode(int tokenMode, String tokenString);
+	public synchronized native void setCSDWrapper(String wrapper);
+	public synchronized native void setClientCert(String cert, String sslKey);
+	public synchronized native void setServerCertSHA1(String hash);
+	public synchronized native void setReqMTU(int mtu);
+
+	/* connection info */
+
+	public synchronized native String getHostname();
+	public synchronized native String getUrlpath();
+	public synchronized native int getPort();
+	public synchronized native String getCookie();
+	public synchronized native String getIFName();
+	public synchronized native IPInfo getIPInfo();
+
+	/* certificate info */
+
+	public synchronized native String getCertSHA1();
+	public synchronized native String getCertDetails();
+	public synchronized native byte[] getCertDER();
+
+	/* library info */
+
+	public static native String getVersion();
+	public static native boolean hasPKCS11Support();
+	public static native boolean hasTSSBlobSupport();
+	public static native boolean hasStokenSupport();
+	public static native boolean hasOATHSupport();
+
+	/* public data structures */
+
+	public static class FormOpt {
+		public int type;
+		public String name;
+		public String label;
+		public ArrayList<FormChoice> choices = new ArrayList<FormChoice>();
+		String value;
+
+		public void setValue(String value) {
+			this.value = value;
+		}
+
+		/* FormOpt internals (called from JNI) */
+
+		void addChoice(FormChoice fc) {
+			this.choices.add(fc);
+		}
+	};
+
+	public static class FormChoice {
+		public String name;
+		public String label;
+		public String authType;
+		public String overrideName;
+		public String overrideLabel;
+	};
+
+	public static class AuthForm {
+		public String banner;
+		public String message;
+		public String error;
+		public String authID;
+		public String method;
+		public String action;
+		public ArrayList<FormOpt> opts = new ArrayList<FormOpt>();
+
+		/* AuthForm internals (called from JNI) */
+
+		FormOpt addOpt() {
+			FormOpt fo = new FormOpt();
+			opts.add(fo);
+			return fo;
+		}
+
+		String getOptValue(String name) {
+			for (FormOpt fo : opts) {
+				if (fo.name.equals(name)) {
+					return fo.value;
+				}
+			}
+			return null;
+		}
+	}
+
+	public static class IPInfo {
+		public String addr;
+		public String netmask;
+		public String addr6;
+		public String netmask6;
+		public ArrayList<String> DNS = new ArrayList<String>();
+		public ArrayList<String> NBNS = new ArrayList<String>();
+		public String domain;
+		public String proxyPac;
+		public int MTU;
+
+		public ArrayList<String> splitDNS = new ArrayList<String>();
+		public ArrayList<String> splitIncludes = new ArrayList<String>();
+		public ArrayList<String> splitExcludes = new ArrayList<String>();
+
+		/* IPInfo internals (called from JNI) */
+
+		void addDNS(String arg) { DNS.add(arg); }
+		void addNBNS(String arg) { NBNS.add(arg); }
+		void addSplitDNS(String arg) { splitDNS.add(arg); }
+		void addSplitInclude(String arg) { splitIncludes.add(arg); }
+		void addSplitExclude(String arg) { splitExcludes.add(arg); }
+	}
+
+	/* LibOpenConnect internals */
+
+	long libctx;
+	boolean canceled = false;
+	Object cancelLock = new Object();
+
+	static synchronized native void globalInit();
+	static {
+		globalInit();
+	}
+
+	synchronized native long init(String useragent);
+	synchronized native void free();
+	native void doCancel();
+}
-- 
1.7.9.5




More information about the openconnect-devel mailing list