[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