0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 04:02:15 +02:00

Refactored java client using ClientEventReceiver interface.

This commit is contained in:
James Yonan 2012-02-15 14:45:55 +00:00
parent aa43f81e04
commit 8b3a5d9c3a
6 changed files with 224 additions and 136 deletions

View File

@ -1,36 +1,12 @@
import java.io.*;
import java.nio.charset.Charset;
public class Client implements ClientEventReceiver {
private ClientImpl client_impl;
public class Client extends OpenVPNClientBase implements Runnable {
private ProvideCreds creds_;
// utility method to read a text file
public static String readTextFile(String file, String csName)
throws IOException {
Charset cs = Charset.forName(csName);
return readTextFile(file, cs);
static class ConfigError extends Exception {
public ConfigError(String msg) { super(msg); }
}
public static String readTextFile(String file, Charset cs)
throws IOException {
// No real need to close the BufferedReader/InputStreamReader
// as they're only wrapping the stream
FileInputStream stream = new FileInputStream(file);
try {
Reader reader = new BufferedReader(new InputStreamReader(stream, cs));
StringBuilder builder = new StringBuilder();
char[] buffer = new char[8192];
int read;
while ((read = reader.read(buffer, 0, buffer.length)) > 0) {
builder.append(buffer, 0, read);
}
return builder.toString();
} finally {
// Potential issue here: if this throws an IOException,
// it will mask any others. Normally I'd use a utility
// method which would log exceptions and swallow them
stream.close();
}
static class CredsUnspecifiedError extends Exception {
public CredsUnspecifiedError(String msg) { super(msg); }
}
// Load OpenVPN core (implements OpenVPNClientBase) from shared library
@ -38,97 +14,55 @@ public class Client extends OpenVPNClientBase implements Runnable {
System.loadLibrary("ovpncli");
}
public static void main(String[] args) throws InterruptedException, IOException {
if (args.length >= 1)
public Client(String config_text, String username, String password) throws ConfigError, CredsUnspecifiedError {
// init client implementation object
client_impl = new ClientImpl();
// load/eval config
Config config = new Config();
config.setContent(config_text);
EvalConfig ec = client_impl.eval_config(config);
if (ec.getError())
throw new ConfigError("OpenVPN config file parse error: " + ec.getMessage());
// handle creds
ProvideCreds creds = new ProvideCreds();
if (!ec.getAutologin())
{
// load config file
Config config = new Config();
config.setContent(readTextFile(args[0], "UTF-8"));
// parse config file
final Client client = new Client();
Status s = client.parse_config(config);
if (s.getError())
if (username.length() > 0)
{
System.err.println("OpenVPN config file parse error: " + s.getMessage());
System.exit(1);
creds.setUsername(username);
creds.setPassword(password);
}
// handle creds
ProvideCreds creds = new ProvideCreds();
RequestCreds need = client.needed_creds();
boolean autologin = need.getAutologin();
if (!autologin)
{
if (args.length >= 3)
{
creds.setUsername(args[1]);
creds.setPassword(args[2]);
}
else
{
System.err.println("OpenVPN config file requires username/password but none provided");
System.exit(1);
}
}
client.provide_creds(creds);
// catch signals
final Thread mainThread = Thread.currentThread();
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
client.stop();
try {
mainThread.join();
} catch (InterruptedException e) {
}
}
});
// execute client in a worker thread
Thread thread = new Thread(client);
thread.start();
// wait for work thread to complete
thread.join();
// show stats before exit
client.show_stats();
}
else
{
System.err.println("OpenVPN Java client");
System.err.println("Usage: java Client <client.ovpn> [username] [password]");
System.exit(2);
else
throw new CredsUnspecifiedError("OpenVPN config file requires username/password but none provided");
}
client_impl.provide_creds(creds);
}
Client() {
}
public void connect() {
// connect
Status status = client_impl.connect(this);
public void provide_creds(ProvideCreds creds)
{
creds_ = creds;
}
public void run() {
Status status = super.connect(creds_);
// show connect status
System.out.format("END Status: err=%b msg='%s'%n", status.getError(), status.getMessage());
}
public void stop() {
client_impl.stop();
}
public void show_stats() {
int n = super.stats_n();
int n = client_impl.stats_n();
for (int i = 0; i < n; ++i)
{
String name = super.stats_name(i);
long value = super.stats_value(i);
String name = client_impl.stats_name(i);
long value = client_impl.stats_value(i);
if (value > 0)
System.out.format("STAT %s=%s%n", name, value);
}
}
@Override
public void event(Event event) {
boolean error = event.getError();
String name = event.getName();
@ -136,7 +70,6 @@ public class Client extends OpenVPNClientBase implements Runnable {
System.out.format("EVENT: err=%b name=%s info='%s'%n", error, name, info);
}
@Override
public void log(LogInfo loginfo) {
String text = loginfo.getText();
System.out.format("LOG: %s", text);

View File

@ -0,0 +1,4 @@
public interface ClientEventReceiver {
void event(Event event);
void log(LogInfo loginfo);
}

52
javacli/ClientImpl.java Normal file
View File

@ -0,0 +1,52 @@
public class ClientImpl extends OpenVPNClientBase implements Runnable {
private ClientEventReceiver parent;
private Status connect_status;
public ClientImpl() {
parent = null;
}
public Status connect(ClientEventReceiver parent_arg) {
// direct client callbacks to parent
parent = parent_arg;
// execute client in a worker thread
Thread thread = new Thread(this);
thread.start();
// wait for worker thread to complete
boolean interrupted;
do {
interrupted = false;
try {
thread.join();
}
catch (InterruptedException e) {
interrupted = true;
super.stop(); // send thread a stop message
}
} while (interrupted);
// dissassociate client callbacks from parent
parent = null;
return connect_status;
}
@Override
public void event(Event event) {
if (parent != null)
parent.event(event);
}
@Override
public void log(LogInfo loginfo) {
if (parent != null)
parent.log(loginfo);
}
@Override
public void run() {
connect_status = super.connect();
}
}

76
javacli/Main.java Normal file
View File

@ -0,0 +1,76 @@
import java.io.*;
import java.nio.charset.Charset;
public class Main {
// utility method to read a text file
public static String readTextFile(String file, String csName) throws IOException {
Charset cs = Charset.forName(csName);
return readTextFile(file, cs);
}
public static String readTextFile(String file, Charset cs) throws IOException {
// No real need to close the BufferedReader/InputStreamReader
// as they're only wrapping the stream
FileInputStream stream = new FileInputStream(file);
try {
Reader reader = new BufferedReader(new InputStreamReader(stream, cs));
StringBuilder builder = new StringBuilder();
char[] buffer = new char[8192];
int read;
while ((read = reader.read(buffer, 0, buffer.length)) > 0) {
builder.append(buffer, 0, read);
}
return builder.toString();
} finally {
// Potential issue here: if this throws an IOException,
// it will mask any others. Normally I'd use a utility
// method which would log exceptions and swallow them
stream.close();
}
}
public static void main(String[] args) throws IOException, Client.ConfigError, Client.CredsUnspecifiedError {
if (args.length >= 1)
{
// load config file
String config = readTextFile(args[0], "UTF-8");
// get creds
String username = "";
String password = "";
if (args.length >= 3)
{
username = args[1];
password = args[2];
}
// instantiate client object
final Client client = new Client(config, username, password);
// catch signals
final Thread mainThread = Thread.currentThread();
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
client.stop();
try {
mainThread.join();
}
catch (InterruptedException e) {
}
}
});
// execute client session
client.connect();
// show stats before exit
client.show_stats();
}
else
{
System.err.println("OpenVPN Java client");
System.err.println("Usage: java Client <client.ovpn> [username] [password]");
System.exit(2);
}
}
}

View File

@ -1,10 +1,15 @@
namespace openvpn {
namespace ClientAPI {
class OptionList;
struct RequestCreds
namespace ClientAPI {
// return properties of config
struct EvalConfig
{
// used by VPN client to indicate which credentials are required
RequestCreds() : staticChallengeEcho(false) {}
EvalConfig() : error(false), staticChallengeEcho(false) {}
bool error; // true if error
std::string message; // if error, message given here
bool autologin; // true: no creds required, false: username/password required
std::string staticChallenge; // static challenge, may be empty, ignored if autologin
bool staticChallengeEcho; // true if static challenge response should be echoed to UI, ignored if autologin
@ -58,21 +63,24 @@ namespace openvpn {
OpenVPNClientBase();
virtual ~OpenVPNClientBase();
// Parse OpenVPN configuration file.
Status parse_config(const Config&);
// Parse config file and determine needed credentials statically.
static EvalConfig eval_config_static(const Config&);
// Determine needed credentials, call after parse_config()
// but before connect().
RequestCreds needed_creds() const;
// Parse OpenVPN configuration file.
EvalConfig eval_config(const Config&) const;
// Provide credentials. Call before connect() if needed_creds()
// indicates that credentials are needed.
void provide_creds(const ProvideCreds&);
// Primary VPN client connect method, doesn't return until disconnect.
// Should be called by a worker thread. This method will make callbacks
// to event() and log() functions. Make sure to call parse_config()
// before this function.
Status connect(const ProvideCreds&);
// to event() and log() functions. Make sure to call eval_config()
// and possibly provide_creds() as well before this function.
Status connect();
// Stop the client. Only meaningful when connect() is running.
// Intended to be called asynchronously from a different thread
// May be called asynchronously from a different thread
// when connect() is running.
void stop();
@ -88,6 +96,8 @@ namespace openvpn {
virtual void log(const LogInfo&) = 0;
private:
static void parse_config(const Config& config, EvalConfig& eval, OptionList& options);
// disable copy and assignment
OpenVPNClientBase(const OpenVPNClientBase&);
OpenVPNClientBase& operator=(const OpenVPNClientBase&);

View File

@ -108,7 +108,7 @@ namespace openvpn {
struct ClientState
{
OptionList options;
RequestCreds req_creds;
ProvideCreds creds;
MySessionStats::Ptr stats;
MyClientEvents::Ptr events;
ClientConnect::Ptr session;
@ -121,43 +121,57 @@ namespace openvpn {
state = new Private::ClientState();
}
inline Status OpenVPNClientBase::parse_config(const Config& config)
inline void OpenVPNClientBase::parse_config(const Config& config, EvalConfig& eval, OptionList& options)
{
Status ret;
try {
// parse config
state->options.parse_from_config(config.content);
state->options.update_map();
options.parse_from_config(config.content);
options.update_map();
// fill out RequestCreds struct
{
const Option *o = state->options.get_ptr("auth-user-pass");
state->req_creds.autologin = !o;
const Option *o = options.get_ptr("auth-user-pass");
eval.autologin = !o;
}
{
const Option *o = state->options.get_ptr("static-challenge");
const Option *o = options.get_ptr("static-challenge");
if (o)
{
state->req_creds.staticChallenge = o->get(1);
eval.staticChallenge = o->get(1);
if (o->get(2) == "1")
state->req_creds.staticChallengeEcho = true;
eval.staticChallengeEcho = true;
}
}
}
catch (std::exception& e)
{
ret.error = true;
ret.message = e.what();
eval.error = true;
eval.message = e.what();
}
return ret;
}
inline RequestCreds OpenVPNClientBase::needed_creds() const
EvalConfig OpenVPNClientBase::eval_config_static(const Config& config)
{
return state->req_creds;
EvalConfig eval;
OptionList options;
parse_config(config, eval, options);
return eval;
}
inline Status OpenVPNClientBase::connect(const ProvideCreds& creds)
EvalConfig OpenVPNClientBase::eval_config(const Config& config) const
{
EvalConfig eval;
state->options.clear();
parse_config(config, eval, state->options);
return eval;
}
void OpenVPNClientBase::provide_creds(const ProvideCreds& creds)
{
state->creds = creds;
}
inline Status OpenVPNClientBase::connect()
{
boost::asio::detail::signal_blocker signal_blocker; // signals should be handled by parent thread
Log::Context log_context(this);
@ -175,9 +189,8 @@ namespace openvpn {
// load options
ClientOptions::Ptr client_options = new ClientOptions(state->options, state->stats, state->events);
// get creds if needed
if (client_options->need_creds())
client_options->submit_creds(creds.username, creds.password);
// configure creds in options
client_options->submit_creds(state->creds.username, state->creds.password);
// initialize the Asio io_service object
io_service.reset(new boost::asio::io_service(1)); // concurrency hint=1