Skip to content
Permalink
4f52943aa2
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
1515 lines (1240 sloc) 38.6 KB
/*
* OpenConnect (SSL + DTLS) VPN client
*
* Copyright © 2013 Kevin Cernekee <cernekee@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.
*/
#include <config.h>
#include <errno.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <jni.h>
#include "openconnect.h"
struct libctx {
JNIEnv *jenv;
jobject jobj;
jobject async_lock;
struct openconnect_info *vpninfo;
int cmd_fd;
int loglevel;
};
static void throw_excep(JNIEnv *jenv, const char *exc, int line)
{
jclass excep;
char msg[64];
snprintf(msg, 64, "%s:%d", __FILE__, line);
(*jenv)->ExceptionClear(jenv);
excep = (*jenv)->FindClass(jenv, exc);
if (excep)
(*jenv)->ThrowNew(jenv, excep, msg);
}
#define OOM(jenv) do { throw_excep(jenv, "java/lang/OutOfMemoryError", __LINE__); } while (0)
static struct libctx *getctx(JNIEnv *jenv, jobject jobj)
{
jclass jcls = (*jenv)->GetObjectClass(jenv, jobj);
jfieldID jfld = (*jenv)->GetFieldID(jenv, jcls, "libctx", "J");
if (!jfld)
return NULL;
return (void *)(unsigned long)(*jenv)->GetLongField(jenv, jobj, jfld);
}
/*
* GetMethodID() and GetFieldID() and NewStringUTF() will automatically throw exceptions on error
*/
static jmethodID get_obj_mid(struct libctx *ctx, jobject jobj, const char *name, const char *sig)
{
jclass jcls = (*ctx->jenv)->GetObjectClass(ctx->jenv, jobj);
jmethodID mid = (*ctx->jenv)->GetMethodID(ctx->jenv, jcls, name, sig);
return mid;
}
static jstring dup_to_jstring(JNIEnv *jenv, const char *in)
{
/*
* Many implementations of NewStringUTF() will return NULL on
* NULL input, but that isn't guaranteed:
* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=35979
*/
return in ? (*jenv)->NewStringUTF(jenv, in) : NULL;
}
static int get_cstring(JNIEnv *jenv, jstring in, const char **out)
{
const char *tmp;
if (in == NULL) {
*out = NULL;
return 0;
}
tmp = (*jenv)->GetStringUTFChars(jenv, in, NULL);
if (!tmp) {
OOM(jenv);
return -1;
}
*out = tmp;
return 0;
}
static void release_cstring(JNIEnv *jenv, jstring jstr, const char *cstr)
{
if (cstr)
(*jenv)->ReleaseStringUTFChars(jenv, jstr, cstr);
}
static int set_int(struct libctx *ctx, jobject jobj, const char *name, int value)
{
jclass jcls = (*ctx->jenv)->GetObjectClass(ctx->jenv, jobj);
jfieldID jfld = (*ctx->jenv)->GetFieldID(ctx->jenv, jcls, name, "I");
if (!jfld)
return -1;
(*ctx->jenv)->SetIntField(ctx->jenv, jobj, jfld, value);
return 0;
}
static int set_long(struct libctx *ctx, jobject jobj, const char *name, uint64_t value)
{
jclass jcls = (*ctx->jenv)->GetObjectClass(ctx->jenv, jobj);
jfieldID jfld = (*ctx->jenv)->GetFieldID(ctx->jenv, jcls, name, "J");
if (!jfld)
return -1;
(*ctx->jenv)->SetLongField(ctx->jenv, jobj, jfld, (jlong)value);
return 0;
}
static int set_string(struct libctx *ctx, jobject jobj, const char *name, const char *value)
{
jclass jcls = (*ctx->jenv)->GetObjectClass(ctx->jenv, jobj);
jfieldID jfld = (*ctx->jenv)->GetFieldID(ctx->jenv, jcls, name, "Ljava/lang/String;");
jstring jarg;
if (!jfld)
return -1;
jarg = dup_to_jstring(ctx->jenv, value);
if (value && !jarg)
return -1;
(*ctx->jenv)->SetObjectField(ctx->jenv, jobj, jfld, jarg);
return 0;
}
static int add_string(struct libctx *ctx, jclass jcls, jobject jobj,
const char *name, const char *value)
{
jmethodID mid = (*ctx->jenv)->GetMethodID(ctx->jenv, jcls, name, "(Ljava/lang/String;)V");
jstring jarg;
if (!value)
return 0;
if (!mid)
return -1;
jarg = dup_to_jstring(ctx->jenv, value);
if (!jarg)
return -1;
(*ctx->jenv)->CallVoidMethod(ctx->jenv, jobj, mid, jarg);
(*ctx->jenv)->DeleteLocalRef(ctx->jenv, jarg);
return 0;
}
static int add_string_pair(struct libctx *ctx, jclass jcls, jobject jobj,
const char *name, const char *key, const char *value)
{
jmethodID mid = (*ctx->jenv)->GetMethodID(ctx->jenv, jcls, name, "(Ljava/lang/String;Ljava/lang/String;)V");
jstring jarg0, jarg1;
if (!key || !value)
return -1;
if (!mid)
return -1;
jarg0 = dup_to_jstring(ctx->jenv, key);
if (!jarg0)
return -1;
jarg1 = dup_to_jstring(ctx->jenv, value);
if (!jarg1) {
(*ctx->jenv)->DeleteLocalRef(ctx->jenv, jarg0);
return -1;
}
(*ctx->jenv)->CallVoidMethod(ctx->jenv, jobj, mid, jarg0, jarg1);
(*ctx->jenv)->DeleteLocalRef(ctx->jenv, jarg1);
(*ctx->jenv)->DeleteLocalRef(ctx->jenv, jarg0);
return 0;
}
static int validate_peer_cert_cb(void *privdata, const char *reason)
{
struct libctx *ctx = privdata;
jstring jreason;
int ret = -1;
jmethodID mid;
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
return -1;
jreason = dup_to_jstring(ctx->jenv, reason);
if (!jreason)
goto out;
mid = get_obj_mid(ctx, ctx->jobj, "onValidatePeerCert", "(Ljava/lang/String;)I");
if (mid)
ret = (*ctx->jenv)->CallIntMethod(ctx->jenv, ctx->jobj, mid, jreason);
out:
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
return ret;
}
static int write_new_config_cb(void *privdata, const char *buf, int buflen)
{
struct libctx *ctx = privdata;
jmethodID mid;
jbyteArray jbuf;
int ret = -1;
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
return -1;
mid = get_obj_mid(ctx, ctx->jobj, "onWriteNewConfig", "([B)I");
if (!mid)
goto out;
jbuf = (*ctx->jenv)->NewByteArray(ctx->jenv, buflen);
if (!jbuf)
goto out;
(*ctx->jenv)->SetByteArrayRegion(ctx->jenv, jbuf, 0, buflen, (jbyte *)buf);
ret = (*ctx->jenv)->CallIntMethod(ctx->jenv, ctx->jobj, mid, jbuf);
out:
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
return ret;
}
static void protect_socket_cb(void *privdata, int fd)
{
struct libctx *ctx = privdata;
jmethodID mid;
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
return;
mid = get_obj_mid(ctx, ctx->jobj, "onProtectSocket", "(I)V");
if (mid)
(*ctx->jenv)->CallVoidMethod(ctx->jenv, ctx->jobj, mid, fd);
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
}
static void stats_cb(void *privdata, const struct oc_stats *stats)
{
struct libctx *ctx = privdata;
jmethodID mid;
jclass jcls;
jobject jobj = NULL;
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
return;
jcls = (*ctx->jenv)->FindClass(ctx->jenv, "org/infradead/libopenconnect/LibOpenConnect$VPNStats");
if (jcls == NULL)
goto out;
mid = (*ctx->jenv)->GetMethodID(ctx->jenv, jcls, "<init>", "()V");
if (!mid)
goto out;
jobj = (*ctx->jenv)->NewObject(ctx->jenv, jcls, mid);
if (!jobj)
goto out;
if (set_long(ctx, jobj, "txPkts", stats->tx_pkts) ||
set_long(ctx, jobj, "txBytes", stats->tx_bytes) ||
set_long(ctx, jobj, "rxPkts", stats->rx_pkts) ||
set_long(ctx, jobj, "rxBytes", stats->rx_bytes))
goto out;
mid = get_obj_mid(ctx, ctx->jobj, "onStatsUpdate",
"(Lorg/infradead/libopenconnect/LibOpenConnect$VPNStats;)V");
if (mid)
(*ctx->jenv)->CallVoidMethod(ctx->jenv, ctx->jobj, mid, jobj);
out:
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
}
static void setup_tun_cb(void *privdata)
{
struct libctx *ctx = privdata;
jmethodID mid;
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
return;
mid = get_obj_mid(ctx, ctx->jobj, "onSetupTun", "()V");
if (mid)
(*ctx->jenv)->CallVoidMethod(ctx->jenv, ctx->jobj, mid);
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
}
static void reconnected_cb(void *privdata)
{
struct libctx *ctx = privdata;
jmethodID mid;
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
return;
mid = get_obj_mid(ctx, ctx->jobj, "onReconnected", "()V");
if (mid)
(*ctx->jenv)->CallVoidMethod(ctx->jenv, ctx->jobj, mid);
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
}
static jobject new_auth_form(struct libctx *ctx, struct oc_auth_form *form)
{
jmethodID mid;
jclass jcls;
jobject jobj = NULL;
jcls = (*ctx->jenv)->FindClass(ctx->jenv, "org/infradead/libopenconnect/LibOpenConnect$AuthForm");
if (jcls == NULL)
return NULL;
mid = (*ctx->jenv)->GetMethodID(ctx->jenv, jcls, "<init>", "()V");
if (!mid)
return NULL;
jobj = (*ctx->jenv)->NewObject(ctx->jenv, jcls, mid);
if (!jobj)
return NULL;
if (set_string(ctx, jobj, "banner", form->banner) ||
set_string(ctx, jobj, "message", form->message) ||
set_string(ctx, jobj, "error", form->error) ||
set_string(ctx, jobj, "authID", form->auth_id) ||
set_string(ctx, jobj, "method", form->method) ||
set_string(ctx, jobj, "action", form->action) ||
set_int(ctx, jobj, "authgroupSelection", form->authgroup_selection)) {
return NULL;
}
return jobj;
}
static jobject new_form_choice(struct libctx *ctx, struct oc_choice *choice)
{
jmethodID mid;
jclass jcls;
jobject jobj = NULL;
jcls = (*ctx->jenv)->FindClass(ctx->jenv,
"org/infradead/libopenconnect/LibOpenConnect$FormChoice");
if (jcls == NULL)
return NULL;
mid = (*ctx->jenv)->GetMethodID(ctx->jenv, jcls, "<init>", "()V");
if (!mid)
return NULL;
jobj = (*ctx->jenv)->NewObject(ctx->jenv, jcls, mid);
if (!jobj)
return NULL;
if (set_string(ctx, jobj, "name", choice->name) ||
set_string(ctx, jobj, "label", choice->label) ||
set_string(ctx, jobj, "authType", choice->auth_type) ||
set_string(ctx, jobj, "overrideName", choice->override_name) ||
set_string(ctx, jobj, "overrideLabel", choice->override_label)) {
return NULL;
}
return jobj;
}
static int populate_select_choices(struct libctx *ctx, jobject jopt, struct oc_form_opt_select *opt)
{
jmethodID mid;
int i;
mid = get_obj_mid(ctx, jopt, "addChoice",
"(Lorg/infradead/libopenconnect/LibOpenConnect$FormChoice;)V");
if (!mid)
return -1;
for (i = 0; i < opt->nr_choices; i++) {
jobject jformchoice = new_form_choice(ctx, opt->choices[i]);
if (!jformchoice)
return -1;
(*ctx->jenv)->CallVoidMethod(ctx->jenv, jopt, mid, jformchoice);
}
return 0;
}
static int add_form_option(struct libctx *ctx, jobject jform, struct oc_form_opt *opt, int is_authgroup)
{
jmethodID addOpt;
jobject jopt;
addOpt = get_obj_mid(ctx, jform, "addOpt",
"(Z)Lorg/infradead/libopenconnect/LibOpenConnect$FormOpt;");
if (!addOpt)
return -1;
jopt = (*ctx->jenv)->CallObjectMethod(ctx->jenv, jform, addOpt, is_authgroup);
if (jopt == NULL)
return -1;
if (set_int(ctx, jopt, "type", opt->type) ||
set_string(ctx, jopt, "name", opt->name) ||
set_string(ctx, jopt, "label", opt->label) ||
set_string(ctx, jopt, "value", opt->_value) ||
set_long(ctx, jopt, "flags", opt->flags))
return -1;
if (opt->type == OC_FORM_OPT_SELECT &&
populate_select_choices(ctx, jopt, (struct oc_form_opt_select *)opt))
return -1;
return 0;
}
static char *lookup_choice_name(struct oc_form_opt_select *opt, const char *name)
{
int i;
/* opt->_value is NOT a caller-allocated string for OC_FORM_OPT_SELECT */
for (i = 0; i < opt->nr_choices; i++)
if (!strcmp(opt->choices[i]->name, name))
return opt->choices[i]->name;
return NULL;
}
static int process_auth_form_cb(void *privdata, struct oc_auth_form *form)
{
struct libctx *ctx = privdata;
jobject jform;
jmethodID callback, getOptValue;
struct oc_form_opt *opt;
jint ret;
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
return -1;
/* create and populate new AuthForm object and option/choice lists */
jform = new_auth_form(ctx, form);
if (!jform)
goto err;
getOptValue = get_obj_mid(ctx, jform, "getOptValue", "(Ljava/lang/String;)Ljava/lang/String;");
if (!getOptValue)
goto err;
for (opt = form->opts; opt; opt = opt->next) {
int is_authgroup = opt == (void *)form->authgroup_opt;
if (add_form_option(ctx, jform, opt, is_authgroup) < 0)
goto err;
}
/* invoke onProcessAuthForm callback */
callback = get_obj_mid(ctx, ctx->jobj, "onProcessAuthForm",
"(Lorg/infradead/libopenconnect/LibOpenConnect$AuthForm;)I");
if (!callback)
goto err;
ret = (*ctx->jenv)->CallIntMethod(ctx->jenv, ctx->jobj, callback, jform);
/* copy any populated form fields back into the C structs */
for (opt = form->opts; opt; opt = opt->next) {
jstring jname, jvalue;
jname = dup_to_jstring(ctx->jenv, opt->name);
if (!jname)
goto err;
jvalue = (*ctx->jenv)->CallObjectMethod(ctx->jenv, jform, getOptValue, jname);
if (jvalue) {
const char *tmp = (*ctx->jenv)->GetStringUTFChars(ctx->jenv, jvalue, NULL);
if (!tmp)
goto err;
if (opt->type == OC_FORM_OPT_SELECT)
opt->_value = lookup_choice_name((void *)opt, tmp);
else {
free(opt->_value);
opt->_value = strdup(tmp);
if (!opt->_value)
OOM(ctx->jenv);
}
(*ctx->jenv)->ReleaseStringUTFChars(ctx->jenv, jvalue, tmp);
}
}
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
return ret;
err:
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
return -1;
}
static void __attribute__ ((format(printf, 3, 4)))
progress_cb(void *privdata, int level, const char *fmt, ...)
{
struct libctx *ctx = privdata;
va_list ap;
char *msg;
jstring jmsg;
int ret, loglevel;
jmethodID mid;
(*ctx->jenv)->MonitorEnter(ctx->jenv, ctx->async_lock);
loglevel = ctx->loglevel;
(*ctx->jenv)->MonitorExit(ctx->jenv, ctx->async_lock);
if (level > loglevel)
return;
va_start(ap, fmt);
ret = vasprintf(&msg, fmt, ap);
va_end(ap);
if (ret < 0) {
OOM(ctx->jenv);
return;
}
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
return;
jmsg = dup_to_jstring(ctx->jenv, msg);
free(msg);
if (!jmsg)
goto out;
mid = get_obj_mid(ctx, ctx->jobj, "onProgress", "(ILjava/lang/String;)V");
if (mid)
(*ctx->jenv)->CallVoidMethod(ctx->jenv, ctx->jobj, mid, level, jmsg);
out:
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
}
static int lock_token_cb(void *privdata)
{
struct libctx *ctx = privdata;
jmethodID mid;
int ret = -1;
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
return -1;
mid = get_obj_mid(ctx, ctx->jobj, "onTokenLock", "()I");
if (mid)
ret = (*ctx->jenv)->CallIntMethod(ctx->jenv, ctx->jobj, mid);
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
return ret;
}
static int unlock_token_cb(void *privdata, const char *new_token)
{
struct libctx *ctx = privdata;
jstring jtoken;
int ret = -1;
jmethodID mid;
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
return -1;
jtoken = dup_to_jstring(ctx->jenv, new_token);
if (!jtoken)
goto out;
mid = get_obj_mid(ctx, ctx->jobj, "onTokenUnlock", "(Ljava/lang/String;)I");
if (mid)
ret = (*ctx->jenv)->CallIntMethod(ctx->jenv, ctx->jobj, mid, jtoken);
out:
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
return ret;
}
/* Library init/uninit */
static jobject init_async_lock(struct libctx *ctx)
{
jclass jcls = (*ctx->jenv)->GetObjectClass(ctx->jenv, ctx->jobj);
jfieldID jfld = (*ctx->jenv)->GetFieldID(ctx->jenv, jcls, "asyncLock", "Ljava/lang/Object;");
jobject jobj = (*ctx->jenv)->GetObjectField(ctx->jenv, ctx->jobj, jfld);
if (jobj)
jobj = (*ctx->jenv)->NewGlobalRef(ctx->jenv, jobj);
return jobj;
}
JNIEXPORT jlong JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_init(
JNIEnv *jenv, jobject jobj, jstring juseragent)
{
char *useragent;
struct libctx *ctx = calloc(1, sizeof(*ctx));
if (!ctx)
goto bad;
ctx->jenv = jenv;
ctx->jobj = (*jenv)->NewGlobalRef(jenv, jobj);
if (!ctx->jobj)
goto bad_free_ctx;
ctx->async_lock = init_async_lock(ctx);
if (!ctx->async_lock)
goto bad_delete_obj_ref;
useragent = (char *)(*jenv)->GetStringUTFChars(jenv, juseragent, NULL);
if (!useragent)
goto bad_delete_ref;
ctx->vpninfo = openconnect_vpninfo_new(useragent, validate_peer_cert_cb,
write_new_config_cb, process_auth_form_cb,
progress_cb, ctx);
(*jenv)->ReleaseStringUTFChars(jenv, juseragent, useragent);
if (!ctx->vpninfo)
goto bad_delete_ref;
openconnect_set_token_callbacks(ctx->vpninfo, ctx, lock_token_cb,
unlock_token_cb);
openconnect_set_protect_socket_handler(ctx->vpninfo, protect_socket_cb);
openconnect_set_stats_handler(ctx->vpninfo, stats_cb);
openconnect_set_setup_tun_handler(ctx->vpninfo, setup_tun_cb);
openconnect_set_reconnected_handler(ctx->vpninfo, reconnected_cb);
ctx->cmd_fd = openconnect_setup_cmd_pipe(ctx->vpninfo);
if (ctx->cmd_fd < 0)
goto bad_free_vpninfo;
ctx->loglevel = PRG_DEBUG;
return (jlong)(unsigned long)ctx;
bad_free_vpninfo:
openconnect_vpninfo_free(ctx->vpninfo);
bad_delete_ref:
(*jenv)->DeleteGlobalRef(jenv, ctx->async_lock);
bad_delete_obj_ref:
(*jenv)->DeleteGlobalRef(jenv, ctx->jobj);
bad_free_ctx:
free(ctx);
bad:
OOM(jenv);
return 0;
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_free(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_vpninfo_free(ctx->vpninfo);
(*jenv)->DeleteGlobalRef(jenv, ctx->async_lock);
(*jenv)->DeleteGlobalRef(jenv, ctx->jobj);
free(ctx);
}
static void write_cmd_pipe(JNIEnv *jenv, jobject jobj, char cmd)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
if (write(ctx->cmd_fd, &cmd, 1) < 0) {
/* probably dead already */
}
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_doCancel(
JNIEnv *jenv, jobject jobj)
{
write_cmd_pipe(jenv, jobj, OC_CMD_CANCEL);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_pause(
JNIEnv *jenv, jobject jobj)
{
write_cmd_pipe(jenv, jobj, OC_CMD_PAUSE);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_requestStats(
JNIEnv *jenv, jobject jobj)
{
write_cmd_pipe(jenv, jobj, OC_CMD_STATS);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_globalInit(
JNIEnv *jenv, jclass jcls)
{
openconnect_init_ssl();
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_obtainCookie(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return 0;
return openconnect_obtain_cookie(ctx->vpninfo);
}
/* special handling: caller-allocated buffer */
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getPeerCertHash(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *hash;
jstring jresult = NULL;
if (!ctx)
return NULL;
hash = openconnect_get_peer_cert_hash(ctx->vpninfo);
if (!hash)
return NULL;
jresult = dup_to_jstring(ctx->jenv, hash);
if (!jresult)
OOM(ctx->jenv);
return jresult;
}
/* special handling: callee-allocated, caller-freed string */
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getPeerCertDetails(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
char *buf = NULL;
jstring jresult = NULL;
if (!ctx)
return NULL;
buf = openconnect_get_peer_cert_details(ctx->vpninfo);
if (!buf)
return NULL;
jresult = dup_to_jstring(ctx->jenv, buf);
if (!jresult)
OOM(ctx->jenv);
openconnect_free_cert_info(ctx->vpninfo, buf);
return jresult;
}
/* special handling: callee-allocated, caller-freed binary buffer */
JNIEXPORT jbyteArray JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getPeerCertDER(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
unsigned char *buf = NULL;
int ret;
jbyteArray jresult = NULL;
if (!ctx)
return NULL;
ret = openconnect_get_peer_cert_DER(ctx->vpninfo, &buf);
if (ret < 0)
return NULL;
jresult = (*ctx->jenv)->NewByteArray(ctx->jenv, ret);
if (jresult)
(*ctx->jenv)->SetByteArrayRegion(ctx->jenv, jresult, 0, ret, (jbyte *) buf);
openconnect_free_cert_info(ctx->vpninfo, buf);
return jresult;
}
/* special handling: callee-allocated, caller-freed binary buffer */
JNIEXPORT jbyteArray JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getPeerCertChain(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
struct oc_cert *chain = NULL, *p;
int cert_list_size, i;
jobjectArray jresult = NULL;
jclass jcls;
if (!ctx)
goto err;
cert_list_size = openconnect_get_peer_cert_chain(ctx->vpninfo, &chain);
if (cert_list_size <= 0)
goto err;
jcls = (*ctx->jenv)->FindClass(ctx->jenv, "[B");
if (!jcls)
goto err;
jresult = (*ctx->jenv)->NewObjectArray(ctx->jenv, cert_list_size, jcls, NULL);
if (!jresult)
goto err;
if ((*ctx->jenv)->PushLocalFrame(ctx->jenv, 256) < 0)
goto err;
for (i = 0, p = chain; i < cert_list_size; i++, p++) {
jbyteArray cert = (*ctx->jenv)->NewByteArray(ctx->jenv, p->der_len);
if (!cert)
goto err2;
(*ctx->jenv)->SetByteArrayRegion(ctx->jenv, cert, 0, p->der_len, (jbyte *)p->der_data);
(*ctx->jenv)->SetObjectArrayElement(ctx->jenv, jresult, i, cert);
}
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
openconnect_free_peer_cert_chain(ctx->vpninfo, chain);
return jresult;
err2:
(*ctx->jenv)->PopLocalFrame(ctx->jenv, NULL);
err:
if (jresult)
(*ctx->jenv)->DeleteLocalRef(ctx->jenv, jresult);
if (chain)
openconnect_free_peer_cert_chain(ctx->vpninfo, chain);
return NULL;
}
/* special handling: two string arguments */
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setClientCert(
JNIEnv *jenv, jobject jobj, jstring jcert, jstring jsslkey)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *cert = NULL, *sslkey = NULL;
if (!ctx)
return;
if (!get_cstring(ctx->jenv, jcert, &cert) &&
!get_cstring(ctx->jenv, jsslkey, &sslkey))
openconnect_set_client_cert(ctx->vpninfo, cert, sslkey);
release_cstring(ctx->jenv, jcert, cert);
release_cstring(ctx->jenv, jsslkey, sslkey);
return;
}
/* special handling: multiple string arguments */
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setupTunDevice(
JNIEnv *jenv, jobject jobj, jstring jarg0, jstring jarg1)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *arg0 = NULL, *arg1 = NULL;
int ret = -ENOMEM;
if (!ctx)
return -EINVAL;
if (!get_cstring(ctx->jenv, jarg0, &arg0) &&
!get_cstring(ctx->jenv, jarg1, &arg1))
ret = openconnect_setup_tun_device(ctx->vpninfo, arg0, arg1);
release_cstring(ctx->jenv, jarg0, arg0);
release_cstring(ctx->jenv, jarg1, arg1);
return ret;
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setCSDWrapper(
JNIEnv *jenv, jobject jobj, jstring jarg0, jstring jarg1, jstring jarg2)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *arg0 = NULL, *arg1 = NULL, *arg2 = NULL;
if (!ctx)
return;
if (!get_cstring(ctx->jenv, jarg0, &arg0) &&
!get_cstring(ctx->jenv, jarg1, &arg1) &&
!get_cstring(ctx->jenv, jarg2, &arg2)) {
openconnect_setup_csd(ctx->vpninfo, getuid(), 1, arg0);
if (arg1) openconnect_set_csd_environ(ctx->vpninfo, "TMPDIR", arg1);
if (arg2) openconnect_set_csd_environ(ctx->vpninfo, "PATH", arg2);
}
release_cstring(ctx->jenv, jarg0, arg0);
release_cstring(ctx->jenv, jarg1, arg1);
release_cstring(ctx->jenv, jarg2, arg2);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setMobileInfo(
JNIEnv *jenv, jobject jobj, jstring jarg0, jstring jarg1, jstring jarg2)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *arg0 = NULL, *arg1 = NULL, *arg2 = NULL;
if (!ctx)
return;
if (!get_cstring(ctx->jenv, jarg0, &arg0) &&
!get_cstring(ctx->jenv, jarg1, &arg1) &&
!get_cstring(ctx->jenv, jarg2, &arg2))
openconnect_set_mobile_info(ctx->vpninfo, arg0, arg1, arg2);
release_cstring(ctx->jenv, jarg0, arg0);
release_cstring(ctx->jenv, jarg1, arg1);
release_cstring(ctx->jenv, jarg2, arg2);
}
/* class methods (general library info) */
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getVersion(
JNIEnv *jenv, jclass jcls)
{
return dup_to_jstring(jenv, openconnect_get_version());
}
JNIEXPORT jboolean JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_hasPKCS11Support(
JNIEnv *jenv, jclass jcls)
{
return openconnect_has_pkcs11_support();
}
JNIEXPORT jboolean JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_hasTSSBlobSupport(
JNIEnv *jenv, jclass jcls)
{
return openconnect_has_tss_blob_support();
}
JNIEXPORT jboolean JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_hasTSS2BlobSupport(
JNIEnv *jenv, jclass jcls)
{
return openconnect_has_tss2_blob_support();
}
JNIEXPORT jboolean JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_hasStokenSupport(
JNIEnv *jenv, jclass jcls)
{
return openconnect_has_stoken_support();
}
JNIEXPORT jboolean JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_hasOATHSupport(
JNIEnv *jenv, jclass jcls)
{
return openconnect_has_oath_support();
}
JNIEXPORT jboolean JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_hasYubiOATHSupport(
JNIEnv *jenv, jclass jcls)
{
return openconnect_has_yubioath_support();
}
/* simple cases: void or int params */
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getPort(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return -EINVAL;
return openconnect_get_port(ctx->vpninfo);
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_passphraseFromFSID(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return -EINVAL;
return openconnect_passphrase_from_fsid(ctx->vpninfo);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_clearCookie(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_clear_cookie(ctx->vpninfo);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_resetSSL(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_reset_ssl(ctx->vpninfo);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_disableIPv6(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_disable_ipv6(ctx->vpninfo);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setCertExpiryWarning(
JNIEnv *jenv, jobject jobj, jint arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_set_cert_expiry_warning(ctx->vpninfo, arg);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setReqMTU(
JNIEnv *jenv, jobject jobj, jint arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_set_reqmtu(ctx->vpninfo, arg);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setDPD(
JNIEnv *jenv, jobject jobj, jint arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_set_dpd(ctx->vpninfo, arg);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setTrojanInterval(
JNIEnv *jenv, jobject jobj, jint arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_set_trojan_interval(ctx->vpninfo, arg);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setPassTOS(
JNIEnv *jenv, jobject jobj, jboolean arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_set_pass_tos(ctx->vpninfo, arg);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setPFS(
JNIEnv *jenv, jobject jobj, jboolean arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_set_pfs(ctx->vpninfo, arg);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setSystemTrust(
JNIEnv *jenv, jobject jobj, jboolean arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_set_system_trust(ctx->vpninfo, arg);
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_makeCSTPConnection(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return -EINVAL;
return openconnect_make_cstp_connection(ctx->vpninfo);
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setupDTLS(
JNIEnv *jenv, jobject jobj, jint arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return -EINVAL;
return openconnect_setup_dtls(ctx->vpninfo, arg);
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_mainloop(
JNIEnv *jenv, jobject jobj, jint arg0, jint arg1)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return -EINVAL;
return openconnect_mainloop(ctx->vpninfo, arg0, arg1);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setLogLevel(
JNIEnv *jenv, jobject jobj, jint arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
(*ctx->jenv)->MonitorEnter(ctx->jenv, ctx->async_lock);
ctx->loglevel = arg;
(*ctx->jenv)->MonitorExit(ctx->jenv, ctx->async_lock);
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setupTunFD(
JNIEnv *jenv, jobject jobj, jint arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return -EINVAL;
return openconnect_setup_tun_fd(ctx->vpninfo, arg);
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setXMLPost(
JNIEnv *jenv, jobject jobj, jboolean arg)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
openconnect_set_xmlpost(ctx->vpninfo, arg);
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getIdleTimeout(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return -EINVAL;
return openconnect_get_idle_timeout(ctx->vpninfo);
}
/* simple cases: return a const string (no need to free it) */
#define RETURN_STRING_START \
struct libctx *ctx = getctx(jenv, jobj); \
const char *buf = NULL; \
jstring jresult = NULL; \
if (!ctx) \
return NULL; \
#define RETURN_STRING_END \
if (!buf) \
return NULL; \
jresult = dup_to_jstring(ctx->jenv, buf); \
if (!jresult) \
OOM(ctx->jenv); \
return jresult;
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getHostname(
JNIEnv *jenv, jobject jobj)
{
RETURN_STRING_START
buf = openconnect_get_hostname(ctx->vpninfo);
RETURN_STRING_END
}
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getDNSName(
JNIEnv *jenv, jobject jobj)
{
RETURN_STRING_START
buf = openconnect_get_dnsname(ctx->vpninfo);
RETURN_STRING_END
}
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getUrlpath(
JNIEnv *jenv, jobject jobj)
{
RETURN_STRING_START
buf = openconnect_get_urlpath(ctx->vpninfo);
RETURN_STRING_END
}
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getCookie(
JNIEnv *jenv, jobject jobj)
{
RETURN_STRING_START
buf = openconnect_get_cookie(ctx->vpninfo);
RETURN_STRING_END
}
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getIFName(
JNIEnv *jenv, jobject jobj)
{
RETURN_STRING_START
buf = openconnect_get_ifname(ctx->vpninfo);
RETURN_STRING_END
}
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getDTLSCipher(
JNIEnv *jenv, jobject jobj)
{
RETURN_STRING_START
buf = openconnect_get_dtls_cipher(ctx->vpninfo);
RETURN_STRING_END
}
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getDTLSCompression(
JNIEnv *jenv, jobject jobj)
{
RETURN_STRING_START
buf = openconnect_get_dtls_compression(ctx->vpninfo);
RETURN_STRING_END
}
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getCSTPCompression(
JNIEnv *jenv, jobject jobj)
{
RETURN_STRING_START
buf = openconnect_get_cstp_compression(ctx->vpninfo);
RETURN_STRING_END
}
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getCSTPCipher(
JNIEnv *jenv, jobject jobj)
{
RETURN_STRING_START
buf = openconnect_get_cstp_cipher(ctx->vpninfo);
RETURN_STRING_END
}
JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getProtocol(
JNIEnv *jenv, jobject jobj)
{
RETURN_STRING_START
buf = openconnect_get_protocol(ctx->vpninfo);
RETURN_STRING_END
}
#define SET_STRING_START() \
struct libctx *ctx = getctx(jenv, jobj); \
const char *arg = NULL; \
if (!ctx) \
return -EINVAL; \
if (get_cstring(ctx->jenv, jarg, &arg)) \
return -ENOMEM;
#define SET_STRING_START_VOID() \
struct libctx *ctx = getctx(jenv, jobj); \
const char *arg = NULL; \
if (!ctx) \
return; \
if (get_cstring(ctx->jenv, jarg, &arg)) \
return;
#define SET_STRING_END() \
release_cstring(ctx->jenv, jarg, arg)
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_checkPeerCertHash(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
int ret;
SET_STRING_START()
ret = openconnect_check_peer_cert_hash(ctx->vpninfo, arg);
SET_STRING_END();
return ret;
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_parseURL(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
int ret;
SET_STRING_START()
ret = openconnect_parse_url(ctx->vpninfo, arg);
SET_STRING_END();
return ret;
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setProxyAuth(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
int ret;
SET_STRING_START()
ret = openconnect_set_proxy_auth(ctx->vpninfo, arg);
SET_STRING_END();
return ret;
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setHTTPProxy(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
int ret;
SET_STRING_START()
ret = openconnect_set_http_proxy(ctx->vpninfo, arg);
SET_STRING_END();
return ret;
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setProtocol(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
int ret;
SET_STRING_START()
ret = openconnect_set_protocol(ctx->vpninfo, arg);
SET_STRING_END();
return ret;
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setXMLSHA1(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
SET_STRING_START_VOID()
openconnect_set_xmlsha1(ctx->vpninfo, arg, strlen(arg) + 1);
SET_STRING_END();
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setHostname(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
SET_STRING_START_VOID()
openconnect_set_hostname(ctx->vpninfo, arg);
SET_STRING_END();
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setVersionString(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
SET_STRING_START_VOID()
openconnect_set_version_string(ctx->vpninfo, arg);
SET_STRING_END();
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setUrlpath(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
SET_STRING_START_VOID()
openconnect_set_urlpath(ctx->vpninfo, arg);
SET_STRING_END();
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setLocalName(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
SET_STRING_START_VOID()
openconnect_set_localname(ctx->vpninfo, arg);
SET_STRING_END();
}
JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setCAFile(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
SET_STRING_START_VOID()
openconnect_set_cafile(ctx->vpninfo, arg);
SET_STRING_END();
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setReportedOS(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
SET_STRING_START()
return openconnect_set_reported_os(ctx->vpninfo, arg);
SET_STRING_END();
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setTokenMode(
JNIEnv *jenv, jobject jobj, jint mode, jstring jarg)
{
int ret;
SET_STRING_START()
ret = openconnect_set_token_mode(ctx->vpninfo, mode, arg);
SET_STRING_END();
return ret;
}
JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setupTunScript(
JNIEnv *jenv, jobject jobj, jstring jarg)
{
int ret;
SET_STRING_START()
ret = openconnect_setup_tun_script(ctx->vpninfo, arg);
SET_STRING_END();
return ret;
}
JNIEXPORT jobject JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getIPInfo(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
jmethodID mid;
jclass jcls;
const struct oc_ip_info *ip;
const struct oc_vpn_option *cstp, *dtls;
struct oc_split_include *inc;
int i;
if (!ctx)
return NULL;
if (openconnect_get_ip_info(ctx->vpninfo, &ip, &cstp, &dtls) < 0)
return NULL;
if (!ip)
return NULL;
jcls = (*ctx->jenv)->FindClass(ctx->jenv,
"org/infradead/libopenconnect/LibOpenConnect$IPInfo");
if (jcls == NULL)
return NULL;
mid = (*ctx->jenv)->GetMethodID(ctx->jenv, jcls, "<init>", "()V");
if (!mid)
return NULL;
jobj = (*ctx->jenv)->NewObject(ctx->jenv, jcls, mid);
if (!jobj)
return NULL;
if (set_string(ctx, jobj, "addr", ip->addr) ||
set_string(ctx, jobj, "netmask", ip->netmask) ||
set_string(ctx, jobj, "addr6", ip->addr6) ||
set_string(ctx, jobj, "netmask6", ip->netmask6) ||
set_string(ctx, jobj, "domain", ip->domain) ||
set_string(ctx, jobj, "proxyPac", ip->proxy_pac) ||
set_string(ctx, jobj, "gatewayAddr", ip->gateway_addr) ||
set_int(ctx, jobj, "MTU", ip->mtu))
return NULL;
for (i = 0; i < 3; i++) {
if (ip->dns[i] && add_string(ctx, jcls, jobj, "addDNS", ip->dns[i]))
return NULL;
if (ip->nbns[i] && add_string(ctx, jcls, jobj, "addNBNS", ip->nbns[i]))
return NULL;
}
for (inc = ip->split_dns; inc; inc = inc->next)
if (add_string(ctx, jcls, jobj, "addSplitDNS", inc->route))
return NULL;
for (inc = ip->split_includes; inc; inc = inc->next)
if (add_string(ctx, jcls, jobj, "addSplitInclude", inc->route))
return NULL;
for (inc = ip->split_excludes; inc; inc = inc->next)
if (add_string(ctx, jcls, jobj, "addSplitExclude", inc->route))
return NULL;
for (; cstp; cstp = cstp->next)
if (add_string_pair(ctx, jcls, jobj, "addCSTPOption", cstp->option, cstp->value))
return NULL;
for (; dtls; dtls = dtls->next)
if (add_string_pair(ctx, jcls, jobj, "addDTLSOption", dtls->option, dtls->value))
return NULL;
return jobj;
}
JNIEXPORT jobjectArray JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getSupportedProtocols(
JNIEnv *jenv, jclass jcls)
{
jmethodID mid;
jobjectArray result;
struct libctx ctx = { .jenv = jenv, .jobj = NULL, .async_lock = NULL, .vpninfo = NULL, .cmd_fd = -1, .loglevel = -1 };
/* call C library */
struct oc_vpn_proto *protos;
int np, ii;
np = openconnect_get_supported_protocols(&protos);
if (np < 0)
return NULL;
/* get VPNProto class, its init method, and create array */
jcls = (*jenv)->FindClass(jenv,
"org/infradead/libopenconnect/LibOpenConnect$VPNProto");
if (jcls == NULL)
goto err;
mid = (*jenv)->GetMethodID(jenv, jcls, "<init>", "()V");
if (!mid)
goto err;
result = (*jenv)->NewObjectArray(jenv, np, jcls, NULL);
if (result == NULL)
goto nomem;
for (ii=0; ii<np; ii++) {
jobject jobj = (*jenv)->NewObject(jenv, jcls, mid);
if (!jobj)
goto nomem;
if (set_string(&ctx, jobj, "name", protos[ii].name) ||
set_string(&ctx, jobj, "prettyName", protos[ii].pretty_name) ||
set_string(&ctx, jobj, "description", protos[ii].description) ||
set_int (&ctx, jobj, "flags", protos[ii].flags))
goto nomem;
(*jenv)->SetObjectArrayElement(jenv, result, ii, jobj);
}
openconnect_free_supported_protocols(protos);
return result;
nomem:
OOM(jenv);
err:
openconnect_free_supported_protocols(protos);
return NULL;
}