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:
parent
aa43f81e04
commit
8b3a5d9c3a
@ -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);
|
||||
|
4
javacli/ClientEventReceiver.java
Normal file
4
javacli/ClientEventReceiver.java
Normal file
@ -0,0 +1,4 @@
|
||||
public interface ClientEventReceiver {
|
||||
void event(Event event);
|
||||
void log(LogInfo loginfo);
|
||||
}
|
52
javacli/ClientImpl.java
Normal file
52
javacli/ClientImpl.java
Normal 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
76
javacli/Main.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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&);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user