6256392a086104a5dea4bdf74ad0742ee6622f75
[strongswan.git] / src / frontends / android / app / src / main / java / org / strongswan / android / logic / CharonVpnService.java
1 /*
2 * Copyright (C) 2012-2017 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * HSR Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 package org.strongswan.android.logic;
19
20 import android.annotation.TargetApi;
21 import android.app.Notification;
22 import android.app.NotificationManager;
23 import android.app.PendingIntent;
24 import android.app.Service;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.ServiceConnection;
29 import android.net.VpnService;
30 import android.os.Build;
31 import android.os.Bundle;
32 import android.os.IBinder;
33 import android.os.ParcelFileDescriptor;
34 import android.security.KeyChain;
35 import android.security.KeyChainException;
36 import android.support.v4.app.NotificationCompat;
37 import android.support.v4.content.ContextCompat;
38 import android.system.OsConstants;
39 import android.util.Log;
40
41 import org.strongswan.android.R;
42 import org.strongswan.android.data.VpnProfile;
43 import org.strongswan.android.data.VpnProfileDataSource;
44 import org.strongswan.android.data.VpnType.VpnTypeFeature;
45 import org.strongswan.android.logic.VpnStateService.ErrorState;
46 import org.strongswan.android.logic.VpnStateService.State;
47 import org.strongswan.android.logic.imc.ImcState;
48 import org.strongswan.android.logic.imc.RemediationInstruction;
49 import org.strongswan.android.ui.MainActivity;
50 import org.strongswan.android.utils.SettingsWriter;
51
52 import java.io.File;
53 import java.net.Inet4Address;
54 import java.net.Inet6Address;
55 import java.net.InetAddress;
56 import java.net.UnknownHostException;
57 import java.security.PrivateKey;
58 import java.security.cert.CertificateEncodingException;
59 import java.security.cert.X509Certificate;
60 import java.util.ArrayList;
61 import java.util.List;
62 import java.util.Locale;
63
64 public class CharonVpnService extends VpnService implements Runnable, VpnStateService.VpnStateListener
65 {
66 private static final String TAG = CharonVpnService.class.getSimpleName();
67 public static final String DISCONNECT_ACTION = "org.strongswan.android.CharonVpnService.DISCONNECT";
68 public static final String LOG_FILE = "charon.log";
69 public static final int VPN_STATE_NOTIFICATION_ID = 1;
70
71 private String mLogFile;
72 private VpnProfileDataSource mDataSource;
73 private Thread mConnectionHandler;
74 private VpnProfile mCurrentProfile;
75 private volatile String mCurrentCertificateAlias;
76 private volatile String mCurrentUserCertificateAlias;
77 private VpnProfile mNextProfile;
78 private volatile boolean mProfileUpdated;
79 private volatile boolean mTerminate;
80 private volatile boolean mIsDisconnecting;
81 private volatile boolean mShowNotification;
82 private VpnStateService mService;
83 private final Object mServiceLock = new Object();
84 private final ServiceConnection mServiceConnection = new ServiceConnection() {
85 @Override
86 public void onServiceDisconnected(ComponentName name)
87 { /* since the service is local this is theoretically only called when the process is terminated */
88 synchronized (mServiceLock)
89 {
90 mService = null;
91 }
92 }
93
94 @Override
95 public void onServiceConnected(ComponentName name, IBinder service)
96 {
97 synchronized (mServiceLock)
98 {
99 mService = ((VpnStateService.LocalBinder)service).getService();
100 }
101 /* we are now ready to start the handler thread */
102 mService.registerListener(CharonVpnService.this);
103 mConnectionHandler.start();
104 }
105 };
106
107 /**
108 * as defined in charonservice.h
109 */
110 static final int STATE_CHILD_SA_UP = 1;
111 static final int STATE_CHILD_SA_DOWN = 2;
112 static final int STATE_AUTH_ERROR = 3;
113 static final int STATE_PEER_AUTH_ERROR = 4;
114 static final int STATE_LOOKUP_ERROR = 5;
115 static final int STATE_UNREACHABLE_ERROR = 6;
116 static final int STATE_GENERIC_ERROR = 7;
117
118 @Override
119 public int onStartCommand(Intent intent, int flags, int startId)
120 {
121 if (intent != null)
122 {
123 if (DISCONNECT_ACTION.equals(intent.getAction()))
124 {
125 setNextProfile(null);
126 }
127 else
128 {
129 Bundle bundle = intent.getExtras();
130 VpnProfile profile = null;
131 if (bundle != null)
132 {
133 profile = mDataSource.getVpnProfile(bundle.getLong(VpnProfileDataSource.KEY_ID));
134 if (profile != null)
135 {
136 String password = bundle.getString(VpnProfileDataSource.KEY_PASSWORD);
137 profile.setPassword(password);
138 }
139 }
140 setNextProfile(profile);
141 }
142 }
143 return START_NOT_STICKY;
144 }
145
146 @Override
147 public void onCreate()
148 {
149 mLogFile = getFilesDir().getAbsolutePath() + File.separator + LOG_FILE;
150
151 mDataSource = new VpnProfileDataSource(this);
152 mDataSource.open();
153 /* use a separate thread as main thread for charon */
154 mConnectionHandler = new Thread(this);
155 /* the thread is started when the service is bound */
156 bindService(new Intent(this, VpnStateService.class),
157 mServiceConnection, Service.BIND_AUTO_CREATE);
158 }
159
160 @Override
161 public void onRevoke()
162 { /* the system revoked the rights grated with the initial prepare() call.
163 * called when the user clicks disconnect in the system's VPN dialog */
164 setNextProfile(null);
165 }
166
167 @Override
168 public void onDestroy()
169 {
170 mTerminate = true;
171 setNextProfile(null);
172 try
173 {
174 mConnectionHandler.join();
175 }
176 catch (InterruptedException e)
177 {
178 e.printStackTrace();
179 }
180 if (mService != null)
181 {
182 mService.unregisterListener(this);
183 unbindService(mServiceConnection);
184 }
185 mDataSource.close();
186 }
187
188 /**
189 * Set the profile that is to be initiated next. Notify the handler thread.
190 *
191 * @param profile the profile to initiate
192 */
193 private void setNextProfile(VpnProfile profile)
194 {
195 synchronized (this)
196 {
197 this.mNextProfile = profile;
198 mProfileUpdated = true;
199 notifyAll();
200 }
201 }
202
203 @Override
204 public void run()
205 {
206 while (true)
207 {
208 synchronized (this)
209 {
210 try
211 {
212 while (!mProfileUpdated)
213 {
214 wait();
215 }
216
217 mProfileUpdated = false;
218 stopCurrentConnection();
219 if (mNextProfile == null)
220 {
221 setState(State.DISABLED);
222 if (mTerminate)
223 {
224 break;
225 }
226 }
227 else
228 {
229 mCurrentProfile = mNextProfile;
230 mNextProfile = null;
231
232 /* store this in a separate (volatile) variable to avoid
233 * a possible deadlock during deinitialization */
234 mCurrentCertificateAlias = mCurrentProfile.getCertificateAlias();
235 mCurrentUserCertificateAlias = mCurrentProfile.getUserCertificateAlias();
236
237 startConnection(mCurrentProfile);
238 mIsDisconnecting = false;
239
240 addNotification();
241 BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName(), mCurrentProfile.getSplitTunneling());
242 if (initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD)))
243 {
244 Log.i(TAG, "charon started");
245 SettingsWriter writer = new SettingsWriter();
246 writer.setValue("global.language", Locale.getDefault().getLanguage());
247 writer.setValue("global.mtu", mCurrentProfile.getMTU());
248 writer.setValue("connection.type", mCurrentProfile.getVpnType().getIdentifier());
249 writer.setValue("connection.server", mCurrentProfile.getGateway());
250 writer.setValue("connection.port", mCurrentProfile.getPort());
251 writer.setValue("connection.username", mCurrentProfile.getUsername());
252 writer.setValue("connection.password", mCurrentProfile.getPassword());
253 writer.setValue("connection.local_id", mCurrentProfile.getLocalId());
254 writer.setValue("connection.remote_id", mCurrentProfile.getRemoteId());
255 initiate(writer.serialize());
256 }
257 else
258 {
259 Log.e(TAG, "failed to start charon");
260 setError(ErrorState.GENERIC_ERROR);
261 setState(State.DISABLED);
262 mCurrentProfile = null;
263 }
264 }
265 }
266 catch (InterruptedException ex)
267 {
268 stopCurrentConnection();
269 setState(State.DISABLED);
270 }
271 }
272 }
273 }
274
275 /**
276 * Stop any existing connection by deinitializing charon.
277 */
278 private void stopCurrentConnection()
279 {
280 synchronized (this)
281 {
282 if (mCurrentProfile != null)
283 {
284 setState(State.DISCONNECTING);
285 mIsDisconnecting = true;
286 deinitializeCharon();
287 Log.i(TAG, "charon stopped");
288 mCurrentProfile = null;
289 removeNotification();
290 }
291 }
292 }
293
294 /**
295 * Add a permanent notification while we are connected to avoid the service getting killed by
296 * the system when low on memory.
297 */
298 private void addNotification()
299 {
300 mShowNotification = true;
301 startForeground(VPN_STATE_NOTIFICATION_ID, buildNotification(false));
302 }
303
304 /**
305 * Remove the permanent notification.
306 */
307 private void removeNotification()
308 {
309 mShowNotification = false;
310 stopForeground(true);
311 }
312
313 /**
314 * Build a notification matching the current state
315 */
316 private Notification buildNotification(boolean publicVersion)
317 {
318 VpnProfile profile = mService.getProfile();
319 State state = mService.getState();
320 ErrorState error = mService.getErrorState();
321 String name = "";
322
323 if (profile != null)
324 {
325 name = profile.getName();
326 }
327 android.support.v4.app.NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
328 .setSmallIcon(R.drawable.ic_notification)
329 .setCategory(NotificationCompat.CATEGORY_SERVICE)
330 .setVisibility(publicVersion ? NotificationCompat.VISIBILITY_PUBLIC
331 : NotificationCompat.VISIBILITY_PRIVATE);
332 int s = R.string.state_disabled;
333 if (error != ErrorState.NO_ERROR)
334 {
335 s = R.string.state_error;
336 builder.setSmallIcon(R.drawable.ic_notification_warning);
337 builder.setColor(ContextCompat.getColor(this, R.color.error_text));
338 }
339 else
340 {
341 switch (state)
342 {
343 case CONNECTING:
344 s = R.string.state_connecting;
345 builder.setSmallIcon(R.drawable.ic_notification_warning);
346 builder.setColor(ContextCompat.getColor(this, R.color.warning_text));
347 break;
348 case CONNECTED:
349 s = R.string.state_connected;
350 builder.setColor(ContextCompat.getColor(this, R.color.success_text));
351 builder.setUsesChronometer(true);
352 break;
353 case DISCONNECTING:
354 s = R.string.state_disconnecting;
355 break;
356 }
357 }
358 builder.setContentTitle(getString(s));
359 if (!publicVersion)
360 {
361 builder.setContentText(name);
362 builder.setPublicVersion(buildNotification(true));
363 }
364
365 Intent intent = new Intent(getApplicationContext(), MainActivity.class);
366 PendingIntent pending = PendingIntent.getActivity(getApplicationContext(), 0, intent,
367 PendingIntent.FLAG_UPDATE_CURRENT);
368 builder.setContentIntent(pending);
369 return builder.build();
370 }
371
372 @Override
373 public void stateChanged() {
374 if (mShowNotification)
375 {
376 NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
377 manager.notify(VPN_STATE_NOTIFICATION_ID, buildNotification(false));
378 }
379 }
380
381 /**
382 * Notify the state service about a new connection attempt.
383 * Called by the handler thread.
384 *
385 * @param profile currently active VPN profile
386 */
387 private void startConnection(VpnProfile profile)
388 {
389 synchronized (mServiceLock)
390 {
391 if (mService != null)
392 {
393 mService.startConnection(profile);
394 }
395 }
396 }
397
398 /**
399 * Update the current VPN state on the state service. Called by the handler
400 * thread and any of charon's threads.
401 *
402 * @param state current state
403 */
404 private void setState(State state)
405 {
406 synchronized (mServiceLock)
407 {
408 if (mService != null)
409 {
410 mService.setState(state);
411 }
412 }
413 }
414
415 /**
416 * Set an error on the state service. Called by the handler thread and any
417 * of charon's threads.
418 *
419 * @param error error state
420 */
421 private void setError(ErrorState error)
422 {
423 synchronized (mServiceLock)
424 {
425 if (mService != null)
426 {
427 mService.setError(error);
428 }
429 }
430 }
431
432 /**
433 * Set the IMC state on the state service. Called by the handler thread and
434 * any of charon's threads.
435 *
436 * @param state IMC state
437 */
438 private void setImcState(ImcState state)
439 {
440 synchronized (mServiceLock)
441 {
442 if (mService != null)
443 {
444 mService.setImcState(state);
445 }
446 }
447 }
448
449 /**
450 * Set an error on the state service. Called by the handler thread and any
451 * of charon's threads.
452 *
453 * @param error error state
454 */
455 private void setErrorDisconnect(ErrorState error)
456 {
457 synchronized (mServiceLock)
458 {
459 if (mService != null)
460 {
461 if (!mIsDisconnecting)
462 {
463 mService.setError(error);
464 }
465 }
466 }
467 }
468
469 /**
470 * Updates the state of the current connection.
471 * Called via JNI by different threads (but not concurrently).
472 *
473 * @param status new state
474 */
475 public void updateStatus(int status)
476 {
477 switch (status)
478 {
479 case STATE_CHILD_SA_DOWN:
480 if (!mIsDisconnecting)
481 {
482 setState(State.CONNECTING);
483 }
484 break;
485 case STATE_CHILD_SA_UP:
486 setState(State.CONNECTED);
487 break;
488 case STATE_AUTH_ERROR:
489 setErrorDisconnect(ErrorState.AUTH_FAILED);
490 break;
491 case STATE_PEER_AUTH_ERROR:
492 setErrorDisconnect(ErrorState.PEER_AUTH_FAILED);
493 break;
494 case STATE_LOOKUP_ERROR:
495 setErrorDisconnect(ErrorState.LOOKUP_FAILED);
496 break;
497 case STATE_UNREACHABLE_ERROR:
498 setErrorDisconnect(ErrorState.UNREACHABLE);
499 break;
500 case STATE_GENERIC_ERROR:
501 setErrorDisconnect(ErrorState.GENERIC_ERROR);
502 break;
503 default:
504 Log.e(TAG, "Unknown status code received");
505 break;
506 }
507 }
508
509 /**
510 * Updates the IMC state of the current connection.
511 * Called via JNI by different threads (but not concurrently).
512 *
513 * @param value new state
514 */
515 public void updateImcState(int value)
516 {
517 ImcState state = ImcState.fromValue(value);
518 if (state != null)
519 {
520 setImcState(state);
521 }
522 }
523
524 /**
525 * Add a remediation instruction to the VPN state service.
526 * Called via JNI by different threads (but not concurrently).
527 *
528 * @param xml XML text
529 */
530 public void addRemediationInstruction(String xml)
531 {
532 for (RemediationInstruction instruction : RemediationInstruction.fromXml(xml))
533 {
534 synchronized (mServiceLock)
535 {
536 if (mService != null)
537 {
538 mService.addRemediationInstruction(instruction);
539 }
540 }
541 }
542 }
543
544 /**
545 * Function called via JNI to generate a list of DER encoded CA certificates
546 * as byte array.
547 *
548 * @return a list of DER encoded CA certificates
549 */
550 private byte[][] getTrustedCertificates()
551 {
552 ArrayList<byte[]> certs = new ArrayList<byte[]>();
553 TrustedCertificateManager certman = TrustedCertificateManager.getInstance().load();
554 try
555 {
556 String alias = this.mCurrentCertificateAlias;
557 if (alias != null)
558 {
559 X509Certificate cert = certman.getCACertificateFromAlias(alias);
560 if (cert == null)
561 {
562 return null;
563 }
564 certs.add(cert.getEncoded());
565 }
566 else
567 {
568 for (X509Certificate cert : certman.getAllCACertificates().values())
569 {
570 certs.add(cert.getEncoded());
571 }
572 }
573 }
574 catch (CertificateEncodingException e)
575 {
576 e.printStackTrace();
577 return null;
578 }
579 return certs.toArray(new byte[certs.size()][]);
580 }
581
582 /**
583 * Function called via JNI to get a list containing the DER encoded certificates
584 * of the user selected certificate chain (beginning with the user certificate).
585 *
586 * Since this method is called from a thread of charon's thread pool we are safe
587 * to call methods on KeyChain directly.
588 *
589 * @return list containing the certificates (first element is the user certificate)
590 * @throws InterruptedException
591 * @throws KeyChainException
592 * @throws CertificateEncodingException
593 */
594 private byte[][] getUserCertificate() throws KeyChainException, InterruptedException, CertificateEncodingException
595 {
596 ArrayList<byte[]> encodings = new ArrayList<byte[]>();
597 X509Certificate[] chain = KeyChain.getCertificateChain(getApplicationContext(), mCurrentUserCertificateAlias);
598 if (chain == null || chain.length == 0)
599 {
600 return null;
601 }
602 for (X509Certificate cert : chain)
603 {
604 encodings.add(cert.getEncoded());
605 }
606 return encodings.toArray(new byte[encodings.size()][]);
607 }
608
609 /**
610 * Function called via JNI to get the private key the user selected.
611 *
612 * Since this method is called from a thread of charon's thread pool we are safe
613 * to call methods on KeyChain directly.
614 *
615 * @return the private key
616 * @throws InterruptedException
617 * @throws KeyChainException
618 * @throws CertificateEncodingException
619 */
620 private PrivateKey getUserKey() throws KeyChainException, InterruptedException
621 {
622 return KeyChain.getPrivateKey(getApplicationContext(), mCurrentUserCertificateAlias);
623 }
624
625 /**
626 * Initialization of charon, provided by libandroidbridge.so
627 *
628 * @param builder BuilderAdapter for this connection
629 * @param logfile absolute path to the logfile
630 * @param byod enable BYOD features
631 * @return TRUE if initialization was successful
632 */
633 public native boolean initializeCharon(BuilderAdapter builder, String logfile, boolean byod);
634
635 /**
636 * Deinitialize charon, provided by libandroidbridge.so
637 */
638 public native void deinitializeCharon();
639
640 /**
641 * Initiate VPN, provided by libandroidbridge.so
642 */
643 public native void initiate(String config);
644
645 /**
646 * Adapter for VpnService.Builder which is used to access it safely via JNI.
647 * There is a corresponding C object to access it from native code.
648 */
649 public class BuilderAdapter
650 {
651 private final String mName;
652 private final Integer mSplitTunneling;
653 private VpnService.Builder mBuilder;
654 private BuilderCache mCache;
655 private BuilderCache mEstablishedCache;
656
657 public BuilderAdapter(String name, Integer splitTunneling)
658 {
659 mName = name;
660 mSplitTunneling = splitTunneling;
661 mBuilder = createBuilder(name);
662 mCache = new BuilderCache(mSplitTunneling);
663 }
664
665 private VpnService.Builder createBuilder(String name)
666 {
667 VpnService.Builder builder = new CharonVpnService.Builder();
668 builder.setSession(mName);
669
670 /* even though the option displayed in the system dialog says "Configure"
671 * we just use our main Activity */
672 Context context = getApplicationContext();
673 Intent intent = new Intent(context, MainActivity.class);
674 PendingIntent pending = PendingIntent.getActivity(context, 0, intent,
675 PendingIntent.FLAG_UPDATE_CURRENT);
676 builder.setConfigureIntent(pending);
677 return builder;
678 }
679
680 public synchronized boolean addAddress(String address, int prefixLength)
681 {
682 try
683 {
684 mCache.addAddress(address, prefixLength);
685 }
686 catch (IllegalArgumentException ex)
687 {
688 return false;
689 }
690 return true;
691 }
692
693 public synchronized boolean addDnsServer(String address)
694 {
695 try
696 {
697 mBuilder.addDnsServer(address);
698 mCache.recordAddressFamily(address);
699 }
700 catch (IllegalArgumentException ex)
701 {
702 return false;
703 }
704 return true;
705 }
706
707 public synchronized boolean addRoute(String address, int prefixLength)
708 {
709 try
710 {
711 mCache.addRoute(address, prefixLength);
712 }
713 catch (IllegalArgumentException ex)
714 {
715 return false;
716 }
717 return true;
718 }
719
720 public synchronized boolean addSearchDomain(String domain)
721 {
722 try
723 {
724 mBuilder.addSearchDomain(domain);
725 }
726 catch (IllegalArgumentException ex)
727 {
728 return false;
729 }
730 return true;
731 }
732
733 public synchronized boolean setMtu(int mtu)
734 {
735 try
736 {
737 mCache.setMtu(mtu);
738 }
739 catch (IllegalArgumentException ex)
740 {
741 return false;
742 }
743 return true;
744 }
745
746 public synchronized int establish()
747 {
748 ParcelFileDescriptor fd;
749 try
750 {
751 mCache.applyData(mBuilder);
752 fd = mBuilder.establish();
753 }
754 catch (Exception ex)
755 {
756 ex.printStackTrace();
757 return -1;
758 }
759 if (fd == null)
760 {
761 return -1;
762 }
763 /* now that the TUN device is created we don't need the current
764 * builder anymore, but we might need another when reestablishing */
765 mBuilder = createBuilder(mName);
766 mEstablishedCache = mCache;
767 mCache = new BuilderCache(mSplitTunneling);
768 return fd.detachFd();
769 }
770
771 public synchronized int establishNoDns()
772 {
773 ParcelFileDescriptor fd;
774
775 if (mEstablishedCache == null)
776 {
777 return -1;
778 }
779 try
780 {
781 Builder builder = createBuilder(mName);
782 mEstablishedCache.applyData(builder);
783 fd = builder.establish();
784 }
785 catch (Exception ex)
786 {
787 ex.printStackTrace();
788 return -1;
789 }
790 if (fd == null)
791 {
792 return -1;
793 }
794 return fd.detachFd();
795 }
796 }
797
798 /**
799 * Cache non DNS related information so we can recreate the builder without
800 * that information when reestablishing IKE_SAs
801 */
802 public class BuilderCache
803 {
804 private final List<PrefixedAddress> mAddresses = new ArrayList<PrefixedAddress>();
805 private final List<PrefixedAddress> mRoutesIPv4 = new ArrayList<PrefixedAddress>();
806 private final List<PrefixedAddress> mRoutesIPv6 = new ArrayList<PrefixedAddress>();
807 private final int mSplitTunneling;
808 private int mMtu;
809 private boolean mIPv4Seen, mIPv6Seen;
810
811 public BuilderCache(Integer splitTunneling)
812 {
813 mSplitTunneling = splitTunneling != null ? splitTunneling : 0;
814 }
815
816 public void addAddress(String address, int prefixLength)
817 {
818 mAddresses.add(new PrefixedAddress(address, prefixLength));
819 recordAddressFamily(address);
820 }
821
822 public void addRoute(String address, int prefixLength)
823 {
824 try
825 {
826 if (isIPv6(address))
827 {
828 mRoutesIPv6.add(new PrefixedAddress(address, prefixLength));
829 }
830 else
831 {
832 mRoutesIPv4.add(new PrefixedAddress(address, prefixLength));
833 }
834 }
835 catch (UnknownHostException ex)
836 {
837 ex.printStackTrace();
838 }
839 }
840
841 public void setMtu(int mtu)
842 {
843 mMtu = mtu;
844 }
845
846 public void recordAddressFamily(String address)
847 {
848 try
849 {
850 if (isIPv6(address))
851 {
852 mIPv6Seen = true;
853 }
854 else
855 {
856 mIPv4Seen = true;
857 }
858 }
859 catch (UnknownHostException ex)
860 {
861 ex.printStackTrace();
862 }
863 }
864
865 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
866 public void applyData(VpnService.Builder builder)
867 {
868 for (PrefixedAddress address : mAddresses)
869 {
870 builder.addAddress(address.mAddress, address.mPrefix);
871 }
872 /* add routes depending on whether split tunneling is allowed or not,
873 * that is, whether we have to handle and block non-VPN traffic */
874 if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) == 0)
875 {
876 if (mIPv4Seen)
877 { /* split tunneling is used depending on the routes */
878 for (PrefixedAddress route : mRoutesIPv4)
879 {
880 builder.addRoute(route.mAddress, route.mPrefix);
881 }
882 }
883 else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
884 { /* allow traffic that would otherwise be blocked to bypass the VPN */
885 builder.allowFamily(OsConstants.AF_INET);
886 }
887 }
888 else if (mIPv4Seen)
889 { /* only needed if we've seen any addresses. otherwise, traffic
890 * is blocked by default (we also install no routes in that case) */
891 builder.addRoute("0.0.0.0", 0);
892 }
893 /* same thing for IPv6 */
894 if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) == 0)
895 {
896 if (mIPv6Seen)
897 {
898 for (PrefixedAddress route : mRoutesIPv6)
899 {
900 builder.addRoute(route.mAddress, route.mPrefix);
901 }
902 }
903 else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
904 {
905 builder.allowFamily(OsConstants.AF_INET6);
906 }
907 }
908 else if (mIPv6Seen)
909 {
910 builder.addRoute("::", 0);
911 }
912 builder.setMtu(mMtu);
913 }
914
915 private boolean isIPv6(String address) throws UnknownHostException
916 {
917 InetAddress addr = InetAddress.getByName(address);
918 if (addr instanceof Inet4Address)
919 {
920 return false;
921 }
922 else if (addr instanceof Inet6Address)
923 {
924 return true;
925 }
926 return false;
927 }
928
929 private class PrefixedAddress
930 {
931 public String mAddress;
932 public int mPrefix;
933
934 public PrefixedAddress(String address, int prefix)
935 {
936 this.mAddress = address;
937 this.mPrefix = prefix;
938 }
939 }
940 }
941
942 /**
943 * Function called via JNI to determine information about the Android version.
944 */
945 private static String getAndroidVersion()
946 {
947 return "Android " + Build.VERSION.RELEASE + " - " + Build.DISPLAY +
948 "/" + Build.VERSION.SECURITY_PATCH;
949 }
950
951 /**
952 * Function called via JNI to determine information about the device.
953 */
954 private static String getDeviceString()
955 {
956 return Build.MODEL + " - " + Build.BRAND + "/" + Build.PRODUCT + "/" + Build.MANUFACTURER;
957 }
958
959 /*
960 * The libraries are extracted to /data/data/org.strongswan.android/...
961 * during installation. On newer releases most are loaded in JNI_OnLoad.
962 */
963 static
964 {
965 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
966 {
967 System.loadLibrary("strongswan");
968
969 if (MainActivity.USE_BYOD)
970 {
971 System.loadLibrary("tpmtss");
972 System.loadLibrary("tncif");
973 System.loadLibrary("tnccs");
974 System.loadLibrary("imcv");
975 }
976
977 System.loadLibrary("charon");
978 System.loadLibrary("ipsec");
979 }
980 System.loadLibrary("androidbridge");
981 }
982 }