/ javacli / OpenVPNClientThread.java
OpenVPNClientThread.java
  1  //    OpenVPN -- An application to securely tunnel IP networks
  2  //               over a single port, with support for SSL/TLS-based
  3  //               session authentication and key exchange,
  4  //               packet encryption, packet authentication, and
  5  //               packet compression.
  6  //
  7  //    Copyright (C) 2012- OpenVPN Inc.
  8  //
  9  //    SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
 10  //
 11  
 12  // package OPENVPN_PACKAGE
 13  
 14  import java.util.HashSet;
 15  
 16  public class OpenVPNClientThread extends ClientAPI_OpenVPNClient implements Runnable {
 17      private EventReceiver parent;
 18      private TunBuilder tun_builder;
 19      private Thread thread;
 20      private ClientAPI_Status m_connect_status;
 21      private boolean connect_called = false;
 22  
 23      private int bytes_in_index = -1;
 24      private int bytes_out_index = -1;
 25  
 26      // thrown if instantiator attempts to call connect more than once
 27      public static class ConnectCalledTwice extends RuntimeException {
 28      }
 29  
 30      public interface EventReceiver {
 31  	// Called with events from core
 32  	void event(ClientAPI_Event event);
 33  
 34  	// Called with log text from core
 35  	void log(ClientAPI_LogInfo loginfo);
 36  
 37  	// Called when connect() thread exits
 38  	void done(ClientAPI_Status status);
 39  
 40  	// Called to "protect" a socket from being routed through the tunnel
 41  	boolean socket_protect(int socket);
 42  
 43  	// When a connection is close to timeout, the core will call this
 44  	// method.  If it returns false, the core will disconnect with a
 45  	// CONNECTION_TIMEOUT event.  If true, the core will enter a PAUSE
 46  	// state.
 47  	boolean pause_on_connection_timeout();
 48  
 49  	// Callback to construct a new tun builder
 50  	TunBuilder tun_builder_new();
 51  
 52  	// Callback to get a certificate
 53  	void external_pki_cert_request(ClientAPI_ExternalPKICertRequest req);
 54  
 55  	// Callback to sign data
 56  	void external_pki_sign_request(ClientAPI_ExternalPKISignRequest req);
 57      }
 58  
 59      public interface TunBuilder {
 60  	// Tun builder methods.
 61  	// Methods documented in openvpn/tun/builder/base.hpp
 62  
 63  	boolean tun_builder_set_remote_address(String address, boolean ipv6);
 64  	boolean tun_builder_add_address(String address, int prefix_length, String gateway, boolean ipv6, boolean net30);
 65  	boolean tun_builder_reroute_gw(boolean ipv4, boolean ipv6, long flags);
 66  	boolean tun_builder_add_route(String address, int prefix_length, boolean ipv6);
 67  	boolean tun_builder_exclude_route(String address, int prefix_length, boolean ipv6);
 68  	boolean tun_builder_add_dns_server(String address, boolean ipv6);
 69  	boolean tun_builder_add_search_domain(String domain);
 70  	boolean tun_builder_set_mtu(int mtu);
 71  	boolean tun_builder_set_session_name(String name);
 72  	int tun_builder_establish();
 73  	void tun_builder_teardown(boolean disconnect);
 74      }
 75  
 76      public OpenVPNClientThread() {
 77  	final int n = stats_n();
 78  	for (int i = 0; i < n; ++i)
 79  	    {
 80  		String name = stats_name(i);
 81  		if (name.equals("BYTES_IN"))
 82  		    bytes_in_index = i;
 83  		if (name.equals("BYTES_OUT"))
 84  		    bytes_out_index = i;
 85  	    }
 86      }
 87  
 88      // start connect session in worker thread
 89      public void connect(EventReceiver parent_arg) {
 90  	if (connect_called)
 91  	    throw new ConnectCalledTwice();
 92  	connect_called = true;
 93  
 94  	// direct client callbacks to parent
 95  	parent = parent_arg;
 96  
 97  	// clear status
 98  	m_connect_status = null;
 99  
100  	// execute client in a worker thread
101  	thread = new Thread(this, "OpenVPNClientThread");
102  	thread.start();
103      }
104  
105      // Wait for worker thread to complete; to stop thread,
106      // first call super stop() method then wait_thread().
107      // This method will give the thread one second to
108      // exit and will abandon it after this time.
109      public void wait_thread_short() {
110  	final int wait_millisecs = 5000; // max time that we will wait for thread to exit
111  	Thread th = thread;
112  	if (th != null) {
113  	    try {
114  		th.join(wait_millisecs);
115  	    }
116  	    catch (InterruptedException e) {
117  	    }
118  
119  	    // thread failed to stop?
120  	    if (th.isAlive()) {
121  		// abandon thread and deliver our own status object to instantiator.
122  		ClientAPI_Status status = new ClientAPI_Status();
123  		status.setError(true);
124  		status.setMessage("CORE_THREAD_ABANDONED");
125  		call_done(status);
126  	    }
127  	}
128      }
129  
130      // Wait for worker thread to complete; to stop thread,
131      // first call super stop() method then wait_thread().
132      // This method will wait forever for the thread to exit.
133      public void wait_thread_long() {
134          if (thread != null) {
135              boolean interrupted;
136              do {
137                  interrupted = false;
138                  try {
139                      thread.join();
140                  }
141                  catch (InterruptedException e) {
142                      interrupted = true;
143                      super.stop(); // send thread a stop message
144                  }
145              } while (interrupted);
146          }
147      }
148  
149      public long bytes_in()
150      {
151  	return super.stats_value(bytes_in_index);
152      }
153  
154      public long bytes_out()
155      {
156  	return super.stats_value(bytes_out_index);
157      }
158  
159      private void call_done(ClientAPI_Status status)
160      {
161  	EventReceiver p = finalize_thread(status);
162  	if (p != null)
163  	    p.done(m_connect_status);
164      }
165  
166      private synchronized EventReceiver finalize_thread(ClientAPI_Status connect_status)
167      {
168  	EventReceiver p = parent;
169  	if (p != null) {
170  	    // save thread connection status
171  	    m_connect_status = connect_status;
172  
173  	    // disassociate client callbacks from parent
174  	    parent = null;
175  	    tun_builder = null;
176  	    thread = null;
177  	}
178  	return p;
179      }
180  
181      // Runnable overrides
182  
183      @Override
184      public void run() {
185  	// Call out to core to start connection.
186  	// Doesn't return until connection has terminated.
187  	ClientAPI_Status status = super.connect();
188  	call_done(status);
189      }
190  
191      // ClientAPI_OpenVPNClient (C++ class) overrides
192  
193      @Override
194      public boolean socket_protect(int socket) {
195  	EventReceiver p = parent;
196  	if (p != null)
197  	    return p.socket_protect(socket);
198  	else
199  	    return false;
200      }
201  
202      @Override
203      public boolean pause_on_connection_timeout() {
204  	EventReceiver p = parent;
205  	if (p != null)
206  	    return p.pause_on_connection_timeout();
207  	else
208  	    return false;
209      }
210  
211      @Override
212      public void event(ClientAPI_Event event) {
213  	EventReceiver p = parent;
214  	if (p != null)
215  	    p.event(event);
216      }
217  
218      @Override
219      public void log(ClientAPI_LogInfo loginfo) {
220  	EventReceiver p = parent;
221  	if (p != null)
222  	    p.log(loginfo);
223      }
224  
225      @Override
226      public void external_pki_cert_request(ClientAPI_ExternalPKICertRequest req) {
227  	EventReceiver p = parent;
228  	if (p != null)
229  	    p.external_pki_cert_request(req);
230      }
231  
232      @Override
233      public void external_pki_sign_request(ClientAPI_ExternalPKISignRequest req) {
234  	EventReceiver p = parent;
235  	if (p != null)
236  	    p.external_pki_sign_request(req);
237      }
238  
239      // TunBuilderBase (C++ class) overrides
240  
241      @Override
242      public boolean tun_builder_new() {
243  	EventReceiver p = parent;
244  	if (p != null) {
245  	    tun_builder = p.tun_builder_new();
246  	    return tun_builder != null;
247  	} else
248  	    return false;
249      }
250  
251      @Override
252      public boolean tun_builder_set_remote_address(String address, boolean ipv6) {
253  	TunBuilder tb = tun_builder;
254  	if (tb != null)
255  	    return tb.tun_builder_set_remote_address(address, ipv6);
256  	else
257  	    return false;
258      }
259  
260      @Override
261      public boolean tun_builder_add_address(String address, int prefix_length, String gateway, boolean ipv6, boolean net30) {
262  	TunBuilder tb = tun_builder;
263  	if (tb != null)
264  	    return tb.tun_builder_add_address(address, prefix_length, gateway, ipv6, net30);
265  	else
266  	    return false;
267      }
268  
269      @Override
270      public boolean tun_builder_reroute_gw(boolean ipv4, boolean ipv6, long flags) {
271  	TunBuilder tb = tun_builder;
272  	if (tb != null)
273  	    return tb.tun_builder_reroute_gw(ipv4, ipv6, flags);
274  	else
275  	    return false;
276      }
277  
278      @Override
279      public boolean tun_builder_add_route(String address, int prefix_length, int metric, boolean ipv6) {
280  	TunBuilder tb = tun_builder;
281  	if (tb != null)
282  	    return tb.tun_builder_add_route(address, prefix_length, ipv6);
283  	else
284  	    return false;
285      }
286  
287      @Override
288      public boolean tun_builder_exclude_route(String address, int prefix_length, int metric, boolean ipv6) {
289  	TunBuilder tb = tun_builder;
290  	if (tb != null)
291  	    return tb.tun_builder_exclude_route(address, prefix_length, ipv6);
292  	else
293  	    return false;
294      }
295  
296      @Override
297      public boolean tun_builder_add_dns_server(String address, boolean ipv6) {
298  	TunBuilder tb = tun_builder;
299  	if (tb != null)
300  	    return tb.tun_builder_add_dns_server(address, ipv6);
301  	else
302  	    return false;
303      }
304  
305      @Override
306      public boolean tun_builder_add_search_domain(String domain)
307      {
308  	TunBuilder tb = tun_builder;
309  	if (tb != null)
310  	    return tb.tun_builder_add_search_domain(domain);
311  	else
312  	    return false;
313      }
314  
315      @Override
316      public boolean tun_builder_set_mtu(int mtu) {
317  	TunBuilder tb = tun_builder;
318  	if (tb != null)
319  	    return tb.tun_builder_set_mtu(mtu);
320  	else
321  	    return false;
322      }
323  
324      @Override
325      public boolean tun_builder_set_session_name(String name)
326      {
327  	TunBuilder tb = tun_builder;
328  	if (tb != null)
329  	    return tb.tun_builder_set_session_name(name);
330  	else
331  	    return false;
332      }
333  
334      @Override
335      public int tun_builder_establish() {
336  	TunBuilder tb = tun_builder;
337  	if (tb != null)
338  	    return tb.tun_builder_establish();
339  	else
340  	    return -1;
341      }
342  
343      @Override
344      public void tun_builder_teardown(boolean disconnect) {
345  	TunBuilder tb = tun_builder;
346  	if (tb != null)
347  	    tb.tun_builder_teardown(disconnect);
348      }
349  }