-
30NetGuard/.gitignore
-
210NetGuard/ADBLOCKING.md
-
1502NetGuard/FAQ.md
-
2NetGuard/FUNDING.yml
-
1350NetGuard/LICENSE
-
822NetGuard/README.md
-
1NetGuard/app/.gitignore
-
23NetGuard/app/CMakeLists.txt
-
105NetGuard/app/build.gradle
-
62NetGuard/app/proguard-rules.pro
-
287NetGuard/app/src/main/AndroidManifest.xml
-
144NetGuard/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl
-
BINNetGuard/app/src/main/ic_launcher-web.png
-
BINNetGuard/app/src/main/ic_launcher_foreground.xcf
-
BINNetGuard/app/src/main/ic_launcher_round-web.png
-
256NetGuard/app/src/main/java/eu/faircode/netguard/ActivityDns.java
-
130NetGuard/app/src/main/java/eu/faircode/netguard/ActivityForwardApproval.java
-
251NetGuard/app/src/main/java/eu/faircode/netguard/ActivityForwarding.java
-
643NetGuard/app/src/main/java/eu/faircode/netguard/ActivityLog.java
-
1304NetGuard/app/src/main/java/eu/faircode/netguard/ActivityMain.java
-
447NetGuard/app/src/main/java/eu/faircode/netguard/ActivityPro.java
-
1466NetGuard/app/src/main/java/eu/faircode/netguard/ActivitySettings.java
-
186NetGuard/app/src/main/java/eu/faircode/netguard/AdapterAccess.java
-
95NetGuard/app/src/main/java/eu/faircode/netguard/AdapterDns.java
-
74NetGuard/app/src/main/java/eu/faircode/netguard/AdapterForwarding.java
-
370NetGuard/app/src/main/java/eu/faircode/netguard/AdapterLog.java
-
1033NetGuard/app/src/main/java/eu/faircode/netguard/AdapterRule.java
-
35NetGuard/app/src/main/java/eu/faircode/netguard/Allowed.java
-
78NetGuard/app/src/main/java/eu/faircode/netguard/ApplicationEx.java
-
1164NetGuard/app/src/main/java/eu/faircode/netguard/DatabaseHelper.java
-
181NetGuard/app/src/main/java/eu/faircode/netguard/DownloadTask.java
-
45NetGuard/app/src/main/java/eu/faircode/netguard/ExpandedListView.java
-
33NetGuard/app/src/main/java/eu/faircode/netguard/Forward.java
-
32NetGuard/app/src/main/java/eu/faircode/netguard/FragmentSettings.java
-
8NetGuard/app/src/main/java/eu/faircode/netguard/GlideHelper.java
-
240NetGuard/app/src/main/java/eu/faircode/netguard/IAB.java
-
140NetGuard/app/src/main/java/eu/faircode/netguard/IPUtil.java
-
42NetGuard/app/src/main/java/eu/faircode/netguard/Packet.java
-
132NetGuard/app/src/main/java/eu/faircode/netguard/ReceiverAutostart.java
-
50NetGuard/app/src/main/java/eu/faircode/netguard/ReceiverPackageRemoved.java
-
47NetGuard/app/src/main/java/eu/faircode/netguard/ResourceRecord.java
-
453NetGuard/app/src/main/java/eu/faircode/netguard/Rule.java
-
146NetGuard/app/src/main/java/eu/faircode/netguard/ServiceExternal.java
-
3335NetGuard/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java
-
81NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileFilter.java
-
80NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileGraph.java
-
75NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileLockdown.java
-
103NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileMain.java
-
39NetGuard/app/src/main/java/eu/faircode/netguard/SwitchPreference.java
-
46NetGuard/app/src/main/java/eu/faircode/netguard/Usage.java
-
1075NetGuard/app/src/main/java/eu/faircode/netguard/Util.java
-
50NetGuard/app/src/main/java/eu/faircode/netguard/Version.java
-
99NetGuard/app/src/main/java/eu/faircode/netguard/WidgetAdmin.java
-
70NetGuard/app/src/main/java/eu/faircode/netguard/WidgetLockdown.java
-
70NetGuard/app/src/main/java/eu/faircode/netguard/WidgetMain.java
-
189NetGuard/app/src/main/jni/netguard/debug_conn.c
-
143NetGuard/app/src/main/jni/netguard/dhcp.c
-
239NetGuard/app/src/main/jni/netguard/dns.c
-
374NetGuard/app/src/main/jni/netguard/icmp.c
-
552NetGuard/app/src/main/jni/netguard/ip.c
-
1113NetGuard/app/src/main/jni/netguard/netguard.c
-
589NetGuard/app/src/main/jni/netguard/netguard.h
-
78NetGuard/app/src/main/jni/netguard/pcap.c
-
370NetGuard/app/src/main/jni/netguard/session.c
-
1360NetGuard/app/src/main/jni/netguard/tcp.c
-
549NetGuard/app/src/main/jni/netguard/udp.c
-
182NetGuard/app/src/main/jni/netguard/util.c
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_add_circle_outline_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_attach_money_black_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_attach_money_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_cloud_upload_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_delete_black_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_delete_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_equalizer_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_equalizer_white_24dp_60.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_error_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_expand_less_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_expand_more_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_file_download_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_filter_list_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_filter_list_white_24dp_60.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_hourglass_empty_black_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_hourglass_empty_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_launch_black_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_launch_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_lock_open_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_lock_outline_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_lock_outline_white_24dp_60.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_pause_black_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_pause_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_perm_data_setting_black_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_perm_data_setting_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_perm_identity_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_play_arrow_black_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
-
BINNetGuard/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png
@ -1,15 +1,15 @@ |
|||||
*.iml |
|
||||
.gradle |
|
||||
/local.properties |
|
||||
/.idea |
|
||||
/.idea/workspace.xml |
|
||||
/.idea/libraries |
|
||||
.DS_Store |
|
||||
/build |
|
||||
/captures |
|
||||
/tools/config.sh |
|
||||
/app/.externalNativeBuild |
|
||||
/app/release |
|
||||
/app/play |
|
||||
keystore.properties |
|
||||
crowdin.properties |
|
||||
|
*.iml |
||||
|
.gradle |
||||
|
/local.properties |
||||
|
/.idea |
||||
|
/.idea/workspace.xml |
||||
|
/.idea/libraries |
||||
|
.DS_Store |
||||
|
/build |
||||
|
/captures |
||||
|
/tools/config.sh |
||||
|
/app/.externalNativeBuild |
||||
|
/app/release |
||||
|
/app/play |
||||
|
keystore.properties |
||||
|
crowdin.properties |
@ -1,105 +1,105 @@ |
|||||
Ad Blocking with NetGuard |
|
||||
------------------------- |
|
||||
|
|
||||
Instructions (you need to follow **all** the steps): |
|
||||
|
|
||||
1. Download/install the latest NetGuard version [from GitHub](https://github.com/M66B/NetGuard/releases) (ad blocking is not possible with the Play store version because Google does not allow ad blocking apps in the Play store) |
|
||||
1. Enable the setting *'Filter traffic'* in the advanced options (three dot menu > Settings > Advanced options > Filter traffic; default is disabled except always enabled in Android 5.0 and earlier) |
|
||||
1. Enable the setting *'Block domain names'* in the advanced options (three dot menu > Settings > Advanced options > Block domain names; default is enabled) |
|
||||
1. Import or download [a hosts file](https://en.wikipedia.org/wiki/Hosts_(file)) using the NetGuard backup settings (three dot menu > Settings > Backup > Download hosts file) |
|
||||
1. Disable browser compression (in Chrome: three dot menu > Settings > Lite mode > Off) |
|
||||
1. Wait at least 10 minutes to let the Android DNS cache time out (clear via Chrome: [chrome://net-internals/#dns](chrome://net-internals/#dns)) |
|
||||
1. Test to see if ad blocking works by opening [this page](http://www.netguard.me/test) |
|
||||
1. Enjoy ad blocking, but don't forget to support application developers and website authors in other ways |
|
||||
|
|
||||
<br /> |
|
||||
|
|
||||
Troubleshooting: |
|
||||
|
|
||||
Because of routing bugs, some devices/Android versions require: |
|
||||
|
|
||||
* the advanced option *Manage system applications* to be enabled and/or |
|
||||
* the network option *Subnet routing* to be disabled and/or |
|
||||
* two (not just one) DNS server addresses to be set in the advanced options, for example 8.8.8.8 and 8.8.4.4 or more privacy friendly [these](https://dns.watch/) |
|
||||
* disabling of private DNS |
|
||||
|
|
||||
<br /> |
|
||||
|
|
||||
Note that: |
|
||||
|
|
||||
* applications, like web browsers, may cache data, so you may need to clear caches |
|
||||
* applications, browsers mostly, that have a *"data saver"*-like feature that proxies requests through their servers (eg. Opera w/ Turbo, Opera Max, Puffin, Chrome w/ data saver, UC Browser, Yandex w/ Turbo, Apus Browser, KK Browser, Onavo Extend, Maxthon) will not have ads blocked as NetGuard cannot see those domain requests |
|
||||
* applications, browsers mostly, can have a private DNS feature (Chrome: three-dots menu, Settings, Privacy, Use secure DNS, turn off) ** |
|
||||
* applications, including browser, can be system apps, which require managing system apps in the advanced settings to be enabled |
|
||||
* the Android always-on VPN setting *Block connections without VPN* will result in stop sending domain names to the VPN after some time |
|
||||
* YouTube ads are not domain-based, and thus cannot be blocked with NetGuard |
|
||||
* NetGuard ignores the IP addresses in the hosts file, because it does not route blocked domains to localhost |
|
||||
* When NetGuard imports the hosts file, it automatically discards any duplicates entries, so duplicate entries are not a problem and have no performance impact after the file is imported |
|
||||
* you can check the number of hosts (domains) imported by pulling the NetGuard notification down using two fingers if your version of Android supports that functionality |
|
||||
* wildcards are not supported due to performance and battery usage reasons |
|
||||
* it is not possible to edit the hosts file (change/add/delete domain names) with NetGuard |
|
||||
* you can disable ad blocking by disabling the setting *'Block domain names'* in the advanced options |
|
||||
* you cannot exclude a single app from ad blocking because Android resolves domain names on behalf of all apps |
|
||||
* **ad blocking is provided as-is**, see also [here](https://forum.xda-developers.com/showpost.php?p=71805655&postcount=4668) |
|
||||
* **ad blocking is not available when NetGuard was installed from the Google Play store!** (disable automatic updates of NetGuard in the Play store application) |
|
||||
|
|
||||
** Some browsers (and also apps) now use DNS over TLS (DoT) or DNS over |
|
||||
HTTPS (DoH). If one of the two protocols is active in the browser, |
|
||||
NetGuard cannot "see" the outgoing DNS requests (due to encryption). |
|
||||
They still flow through NetGuard, but are not treated as DNS requests, |
|
||||
but as normal connections (via port 853 or 443). It is therefore not |
|
||||
sufficient to disable Private DNS within Android, but you must also |
|
||||
check the settings for DoT and DoH (especially for browsers). |
|
||||
|
|
||||
<br /> |
|
||||
|
|
||||
The NetGuard version from GitHub: |
|
||||
|
|
||||
* is signed with the same signature as the version from the Google Play store, so any purchases will be restored (this will not happen with for example the F-Droid version) |
|
||||
* will automatically notify you if there are updates available via GitHub (this can be switched off in NetGuard's settings) |
|
||||
|
|
||||
<br /> |
|
||||
|
|
||||
Which hosts (ad servers) will be blocked depends on the hosts file being used. |
|
||||
NetGuard downloads the [StevenBlack hosts file](https://github.com/StevenBlack/hosts) by default. |
|
||||
|
|
||||
<br /> |
|
||||
|
|
||||
Automation: |
|
||||
|
|
||||
You can automatically download a hosts file by sending this service intent with your favorite automation tool, like Tasker: |
|
||||
|
|
||||
`eu.faircode.netguard.DOWNLOAD_HOSTS_FILE` |
|
||||
|
|
||||
For example using [adb](https://developer.android.com/studio/command-line/adb.html) from the command line: |
|
||||
|
|
||||
`adb shell am startservice -a eu.faircode.netguard.DOWNLOAD_HOSTS_FILE` |
|
||||
|
|
||||
<br /> |
|
||||
|
|
||||
Apart from using a hosts file, you can block most in-app ads by blocking this address in the access list of Google Play services: |
|
||||
|
|
||||
*googleads.g.doubleclick.net/443* |
|
||||
|
|
||||
You'll need to enable filtering and (temporarily) logging for this (you can do this by using the *Configure* button; check both options) |
|
||||
and you'll need to wait until the address appears (you can speed this up by opening some apps with in-app ads). |
|
||||
Note that ads are likely being cached, so this may not take effect immediately. |
|
||||
|
|
||||
<br /> |
|
||||
|
|
||||
An alternate way to block advertisements is by using special DNS servers, like these: |
|
||||
|
|
||||
* [AdGuard DNS](https://adguard.com/en/adguard-dns/overview.html) - Free |
|
||||
* [Alternate DNS](https://alternate-dns.com/) - 14 day free trial |
|
||||
* [NoAd](https://noad.zone/) - Not working as of 2017 June 03 |
|
||||
|
|
||||
Be sure to read the privacy policies of these services as they might log your DNS requests. |
|
||||
|
|
||||
You can set DNS server addresses for all connection types in NetGuard's *Advanced options*. |
|
||||
Note that when you set two DNS server addresses, the default (operating system/network provider) DNS servers will not be used anymore. |
|
||||
|
|
||||
Feel free to let me know about other servers or request to add them in alphabetic order by doing a pull request. |
|
||||
|
|
||||
<br /> |
|
||||
|
|
||||
**Please do not mention this feature in Google Play store comments, since Google does not allow ad blocking applications in the Google Play store.** |
|
||||
|
Ad Blocking with NetGuard |
||||
|
------------------------- |
||||
|
|
||||
|
Instructions (you need to follow **all** the steps): |
||||
|
|
||||
|
1. Download/install the latest NetGuard version [from GitHub](https://github.com/M66B/NetGuard/releases) (ad blocking is not possible with the Play store version because Google does not allow ad blocking apps in the Play store) |
||||
|
1. Enable the setting *'Filter traffic'* in the advanced options (three dot menu > Settings > Advanced options > Filter traffic; default is disabled except always enabled in Android 5.0 and earlier) |
||||
|
1. Enable the setting *'Block domain names'* in the advanced options (three dot menu > Settings > Advanced options > Block domain names; default is enabled) |
||||
|
1. Import or download [a hosts file](https://en.wikipedia.org/wiki/Hosts_(file)) using the NetGuard backup settings (three dot menu > Settings > Backup > Download hosts file) |
||||
|
1. Disable browser compression (in Chrome: three dot menu > Settings > Lite mode > Off) |
||||
|
1. Wait at least 10 minutes to let the Android DNS cache time out (clear via Chrome: [chrome://net-internals/#dns](chrome://net-internals/#dns)) |
||||
|
1. Test to see if ad blocking works by opening [this page](http://www.netguard.me/test) |
||||
|
1. Enjoy ad blocking, but don't forget to support application developers and website authors in other ways |
||||
|
|
||||
|
<br /> |
||||
|
|
||||
|
Troubleshooting: |
||||
|
|
||||
|
Because of routing bugs, some devices/Android versions require: |
||||
|
|
||||
|
* the advanced option *Manage system applications* to be enabled and/or |
||||
|
* the network option *Subnet routing* to be disabled and/or |
||||
|
* two (not just one) DNS server addresses to be set in the advanced options, for example 8.8.8.8 and 8.8.4.4 or more privacy friendly [these](https://dns.watch/) |
||||
|
* disabling of private DNS |
||||
|
|
||||
|
<br /> |
||||
|
|
||||
|
Note that: |
||||
|
|
||||
|
* applications, like web browsers, may cache data, so you may need to clear caches |
||||
|
* applications, browsers mostly, that have a *"data saver"*-like feature that proxies requests through their servers (eg. Opera w/ Turbo, Opera Max, Puffin, Chrome w/ data saver, UC Browser, Yandex w/ Turbo, Apus Browser, KK Browser, Onavo Extend, Maxthon) will not have ads blocked as NetGuard cannot see those domain requests |
||||
|
* applications, browsers mostly, can have a private DNS feature (Chrome: three-dots menu, Settings, Privacy, Use secure DNS, turn off) ** |
||||
|
* applications, including browser, can be system apps, which require managing system apps in the advanced settings to be enabled |
||||
|
* the Android always-on VPN setting *Block connections without VPN* will result in stop sending domain names to the VPN after some time |
||||
|
* YouTube ads are not domain-based, and thus cannot be blocked with NetGuard |
||||
|
* NetGuard ignores the IP addresses in the hosts file, because it does not route blocked domains to localhost |
||||
|
* When NetGuard imports the hosts file, it automatically discards any duplicates entries, so duplicate entries are not a problem and have no performance impact after the file is imported |
||||
|
* you can check the number of hosts (domains) imported by pulling the NetGuard notification down using two fingers if your version of Android supports that functionality |
||||
|
* wildcards are not supported due to performance and battery usage reasons |
||||
|
* it is not possible to edit the hosts file (change/add/delete domain names) with NetGuard |
||||
|
* you can disable ad blocking by disabling the setting *'Block domain names'* in the advanced options |
||||
|
* you cannot exclude a single app from ad blocking because Android resolves domain names on behalf of all apps |
||||
|
* **ad blocking is provided as-is**, see also [here](https://forum.xda-developers.com/showpost.php?p=71805655&postcount=4668) |
||||
|
* **ad blocking is not available when NetGuard was installed from the Google Play store!** (disable automatic updates of NetGuard in the Play store application) |
||||
|
|
||||
|
** Some browsers (and also apps) now use DNS over TLS (DoT) or DNS over |
||||
|
HTTPS (DoH). If one of the two protocols is active in the browser, |
||||
|
NetGuard cannot "see" the outgoing DNS requests (due to encryption). |
||||
|
They still flow through NetGuard, but are not treated as DNS requests, |
||||
|
but as normal connections (via port 853 or 443). It is therefore not |
||||
|
sufficient to disable Private DNS within Android, but you must also |
||||
|
check the settings for DoT and DoH (especially for browsers). |
||||
|
|
||||
|
<br /> |
||||
|
|
||||
|
The NetGuard version from GitHub: |
||||
|
|
||||
|
* is signed with the same signature as the version from the Google Play store, so any purchases will be restored (this will not happen with for example the F-Droid version) |
||||
|
* will automatically notify you if there are updates available via GitHub (this can be switched off in NetGuard's settings) |
||||
|
|
||||
|
<br /> |
||||
|
|
||||
|
Which hosts (ad servers) will be blocked depends on the hosts file being used. |
||||
|
NetGuard downloads the [StevenBlack hosts file](https://github.com/StevenBlack/hosts) by default. |
||||
|
|
||||
|
<br /> |
||||
|
|
||||
|
Automation: |
||||
|
|
||||
|
You can automatically download a hosts file by sending this service intent with your favorite automation tool, like Tasker: |
||||
|
|
||||
|
`eu.faircode.netguard.DOWNLOAD_HOSTS_FILE` |
||||
|
|
||||
|
For example using [adb](https://developer.android.com/studio/command-line/adb.html) from the command line: |
||||
|
|
||||
|
`adb shell am startservice -a eu.faircode.netguard.DOWNLOAD_HOSTS_FILE` |
||||
|
|
||||
|
<br /> |
||||
|
|
||||
|
Apart from using a hosts file, you can block most in-app ads by blocking this address in the access list of Google Play services: |
||||
|
|
||||
|
*googleads.g.doubleclick.net/443* |
||||
|
|
||||
|
You'll need to enable filtering and (temporarily) logging for this (you can do this by using the *Configure* button; check both options) |
||||
|
and you'll need to wait until the address appears (you can speed this up by opening some apps with in-app ads). |
||||
|
Note that ads are likely being cached, so this may not take effect immediately. |
||||
|
|
||||
|
<br /> |
||||
|
|
||||
|
An alternate way to block advertisements is by using special DNS servers, like these: |
||||
|
|
||||
|
* [AdGuard DNS](https://adguard.com/en/adguard-dns/overview.html) - Free |
||||
|
* [Alternate DNS](https://alternate-dns.com/) - 14 day free trial |
||||
|
* [NoAd](https://noad.zone/) - Not working as of 2017 June 03 |
||||
|
|
||||
|
Be sure to read the privacy policies of these services as they might log your DNS requests. |
||||
|
|
||||
|
You can set DNS server addresses for all connection types in NetGuard's *Advanced options*. |
||||
|
Note that when you set two DNS server addresses, the default (operating system/network provider) DNS servers will not be used anymore. |
||||
|
|
||||
|
Feel free to let me know about other servers or request to add them in alphabetic order by doing a pull request. |
||||
|
|
||||
|
<br /> |
||||
|
|
||||
|
**Please do not mention this feature in Google Play store comments, since Google does not allow ad blocking applications in the Google Play store.** |
1502
NetGuard/FAQ.md
File diff suppressed because it is too large
View File
@ -1 +1 @@ |
|||||
github: [M66B] |
|
||||
|
github: [M66B] |
1350
NetGuard/LICENSE
File diff suppressed because it is too large
View File
@ -1,411 +1,411 @@ |
|||||
# NetGuard |
|
||||
|
|
||||
# Edits in NetGuard Code |
|
||||
The NetGuard code has been taken from https://github.com/M66B/NetGuard |
|
||||
The compilation of the code was problematic and the problem was solved by modifiying the following lines of code in app --> Gradle Scripts --> build.gradle (Module :app) |
|
||||
|
|
||||
Add the following lines: |
|
||||
|
|
||||
storeFile file("my.keystore") |
|
||||
storePassword "store_password" |
|
||||
keyAlias "my_key_alias" |
|
||||
keyPassword "key_password" |
|
||||
|
|
||||
and comment out the following lines: |
|
||||
|
|
||||
// def keystorePropertiesFile = rootProject.file("keystore.properties") |
|
||||
// def keystoreProperties = new Properties() |
|
||||
// keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) |
|
||||
|
|
||||
// storeFile file(keystoreProperties['storeFile']) |
|
||||
// storePassword keystoreProperties['storePassword'] |
|
||||
// keyAlias keystoreProperties['keyAlias'] |
|
||||
// keyPassword keystoreProperties['keyPassword'] |
|
||||
|
|
||||
# The readme file from this point onwards is the original readme file. |
|
||||
*NetGuard* provides simple and advanced ways to block access to the internet - no root required. |
|
||||
Applications and addresses can individually be allowed or denied access to your Wi-Fi and/or mobile connection. |
|
||||
|
|
||||
<br> |
|
||||
|
|
||||
**WARNING: there is an app in the Samsung Galaxy app store "*Play Music - MP3 Music player*" |
|
||||
with the same package name as NetGuard, which will be installed as update without your confirmation. |
|
||||
This app is probably malicious and was reported to Samsung on December 8, 2021.** |
|
||||
|
|
||||
<br> |
|
||||
|
|
||||
Blocking access to the internet can help: |
|
||||
|
|
||||
* reduce your data usage |
|
||||
* save your battery |
|
||||
* increase your privacy |
|
||||
|
|
||||
NetGuard is the first free and open source no-root firewall for Android. |
|
||||
|
|
||||
Features: |
|
||||
|
|
||||
* Simple to use |
|
||||
* No root required |
|
||||
* 100% open source |
|
||||
* No calling home |
|
||||
* No tracking or analytics |
|
||||
* Actively developed and supported |
|
||||
* Android 5.1 and later supported |
|
||||
* IPv4/IPv6 TCP/UDP supported |
|
||||
* Tethering supported |
|
||||
* Optionally allow when screen on |
|
||||
* Optionally block when roaming |
|
||||
* Optionally block system applications |
|
||||
* Optionally forward ports, also to external addresses (not available if installed from the Play store) |
|
||||
* Optionally notify when an application accesses the internet |
|
||||
* Optionally record network usage per application per address |
|
||||
* Optionally [block ads using a hosts file](https://github.com/M66B/NetGuard/blob/master/ADBLOCKING.md) (not available if installed from the Play store) |
|
||||
* Material design theme with light and dark theme |
|
||||
|
|
||||
PRO features: |
|
||||
|
|
||||
* Log all outgoing traffic; search and filter access attempts; export PCAP files to analyze traffic |
|
||||
* Allow/block individual addresses per application |
|
||||
* New application notifications; configure NetGuard directly from the notification |
|
||||
* Display network speed graph in a status bar notification |
|
||||
* Select from five additional themes in both light and dark version |
|
||||
|
|
||||
There is no other no-root firewall offering all these features. |
|
||||
|
|
||||
Requirements: |
|
||||
|
|
||||
* Android 5.1 or later |
|
||||
* A [compatible device](#compatibility) |
|
||||
|
|
||||
Downloads: |
|
||||
|
|
||||
* [GitHub](https://github.com/M66B/NetGuard/releases) |
|
||||
* [Google Play](https://play.google.com/store/apps/details?id=eu.faircode.netguard) |
|
||||
|
|
||||
Certificate fingerprints: |
|
||||
|
|
||||
* MD5: B6:4A:E8:08:1C:3C:9C:19:D6:9E:29:00:46:89:DA:73 |
|
||||
* SHA1: EF:46:F8:13:D2:C8:A0:64:D7:2C:93:6B:9B:96:D1:CC:CC:98:93:78 |
|
||||
* SHA256: E4:A2:60:A2:DC:E7:B7:AF:23:EE:91:9C:48:9E:15:FD:01:02:B9:3F:9E:7C:9D:82:B0:9C:0B:39:50:00:E4:D4 |
|
||||
|
|
||||
Usage: |
|
||||
|
|
||||
* Enable the firewall using the switch in the action bar |
|
||||
* Allow/deny Wi-Fi/mobile internet access using the icons along the right side of the application list |
|
||||
|
|
||||
You can use the settings menu to change from blacklist mode (allow all in *Settings* but block unwanted applications in list) to whitelist mode (block all in *Settings* but allow favorite applications in list). |
|
||||
|
|
||||
* Red/orange/yellow/amber = internet access denied |
|
||||
* Teal/blue/purple/grey = internet access allowed |
|
||||
|
|
||||
<img src="https://raw.githubusercontent.com/M66B/NetGuard/master/screenshots/01-main.png" width="320" height="569" /> |
|
||||
<img src="https://raw.githubusercontent.com/M66B/NetGuard/master/screenshots/02-main-details.png" width="320" height="569" /> |
|
||||
<img src="https://raw.githubusercontent.com/M66B/NetGuard/master/screenshots/03-main-access.png" width="320" height="569" /> |
|
||||
<img src="https://raw.githubusercontent.com/M66B/NetGuard/master/screenshots/08-notifications.png" width="320" height="569" /> |
|
||||
|
|
||||
For more screenshots, see [here](https://github.com/M66B/NetGuard/tree/master/screenshots). |
|
||||
|
|
||||
Compatibility |
|
||||
------------- |
|
||||
|
|
||||
The only way to build a no-root firewall on Android is to use the Android VPN service. |
|
||||
Android doesn't allow chaining of VPN services, so you cannot use NetGuard together with other VPN based applications. |
|
||||
See also [this FAQ](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq2). |
|
||||
|
|
||||
NetGuard can be used on rooted devices too and even offers more features than most root firewalls. |
|
||||
|
|
||||
Some older Android versions, especially Samsung's Android versions, have a buggy VPN implementation, |
|
||||
which results in Android refusing to start the VPN service in certain circumstances, |
|
||||
like when there is no internet connectivity yet (when starting up your device) |
|
||||
or when incorrectly requiring manual approval of the VPN service again (when starting up your device). |
|
||||
NetGuard will try to workaround this and remove the error message when it succeeds, else you are out of luck. |
|
||||
|
|
||||
Some LineageOS versions have a broken Android VPN implementation, causing all traffic to be blocked, |
|
||||
please see [this FAQ](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq51) for more information. |
|
||||
|
|
||||
NetGuard is not supported for apps installed in a [work profile](https://developer.android.com/work/managed-profiles), |
|
||||
or in a [Secure Folder](https://www.samsung.com/uk/support/mobile-devices/what-is-the-secure-folder-and-how-do-i-use-it/) (Samsung), |
|
||||
or as second instance (MIUI), or as Parallel app (OnePlus), or as Xiaomi dual app |
|
||||
because the Android VPN service too often does not work correctly in this situation, which can't be fixed by NetGuard. |
|
||||
|
|
||||
Filtering mode cannot be used on [CopperheadOS](https://copperhead.co/android/). |
|
||||
|
|
||||
NetGuard will not work or crash when the package *com.android.vpndialogs* has been removed or otherwise is unavailable. |
|
||||
Removing this package is possible with root permissions only. |
|
||||
If you disable this package, you can enable it with this command again: |
|
||||
|
|
||||
``` |
|
||||
adb shell pm enable --user 0 com.android.vpndialogs |
|
||||
``` |
|
||||
|
|
||||
NetGuard is supported for phones and tablets only, so not for other device types like on a television or in a car. |
|
||||
|
|
||||
Android does not allow incoming connections (not the same as incoming traffic) and the Android VPN service has no support for this either. |
|
||||
Therefore managing incoming connections for servers running on your device is not supported. |
|
||||
|
|
||||
Wi-Fi or IP calling will not work if your provider uses [IPsec](https://en.wikipedia.org/wiki/IPsec) to encrypt your phone calls, SMS messages and/or MMS messages, |
|
||||
unless there was made an exception in NetGuard for your provider (currently for T-Mobile and Verizon). |
|
||||
I am happy to add exceptions for other providers, but I need the [MCC](https://en.wikipedia.org/wiki/Mobile_country_code) codes, [MNC](https://en.wikipedia.org/wiki/MNC) codes and [IP address](https://en.wikipedia.org/wiki/IP_address) ranges your provider is using. |
|
||||
As an alternative you can enable the option '*Disable on call*', which is available since version 2.113. |
|
||||
|
|
||||
|
|
||||
<a name="FAQ"></a> |
|
||||
Frequently Asked Questions (FAQ) |
|
||||
-------------------------------- |
|
||||
|
|
||||
<a name="FAQ0"></a> |
|
||||
[**(0) How do I use NetGuard?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq0) |
|
||||
|
|
||||
<a name="FAQ1"></a> |
|
||||
[**(1) Can NetGuard completely protect my privacy?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq1) |
|
||||
|
|
||||
<a name="FAQ2"></a> |
|
||||
[**(2) Can I use another VPN application while using NetGuard?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq2) |
|
||||
|
|
||||
<a name="FAQ3"></a> |
|
||||
[**(3) Can I use NetGuard on any Android version?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq3) |
|
||||
|
|
||||
<a name="FAQ4"></a> |
|
||||
[**(4) Will NetGuard use extra battery power?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq4) |
|
||||
|
|
||||
<a name="FAQ6"></a> |
|
||||
[**(6) Will NetGuard send my internet traffic to an external (VPN) server?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq6) |
|
||||
|
|
||||
<a name="FAQ7"></a> |
|
||||
[**(7) Why are applications without internet permission shown?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq7) |
|
||||
|
|
||||
<a name="FAQ8"></a> |
|
||||
[**(8) What do I need to enable for the Google Play™ store app to work?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq8) |
|
||||
|
|
||||
<a name="FAQ9"></a> |
|
||||
[**(9) Why is the VPN service being restarted?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq9) |
|
||||
|
|
||||
<a name="FAQ10"></a> |
|
||||
[**(10) Will you provide a Tasker plug-in?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq10) |
|
||||
|
|
||||
<a name="FAQ13"></a> |
|
||||
[**(13) How can I remove the ongoing NetGuard entry in the notification screen?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq13) |
|
||||
|
|
||||
<a name="FAQ14"></a> |
|
||||
[**(14) Why can't I select OK to approve the VPN connection request?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq14) |
|
||||
|
|
||||
<a name="FAQ15"></a> |
|
||||
[**(15) Are F-Droid builds supported?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq15) |
|
||||
|
|
||||
<a name="FAQ16"></a> |
|
||||
[**(16) Why are some applications shown dimmed?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq16) |
|
||||
|
|
||||
<a name="FAQ17"></a> |
|
||||
[**(17) Why is NetGuard using so much memory?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq17) |
|
||||
|
|
||||
<a name="FAQ18"></a> |
|
||||
[**(18) Why can't I find NetGuard in the Google Play™ store app?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq18) |
|
||||
|
|
||||
<a name="FAQ19"></a> |
|
||||
[**(19) Why does application XYZ still have internet access?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq19) |
|
||||
|
|
||||
<a name="FAQ20"></a> |
|
||||
[**(20) Can I Greenify/hibernate NetGuard?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq20) |
|
||||
|
|
||||
<a name="FAQ21"></a> |
|
||||
[**(21) Does doze mode affect NetGuard?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq21) |
|
||||
|
|
||||
<a name="FAQ22"></a> |
|
||||
[**(22) Can I tether (use the Android hotspot) / use Wi-Fi calling while using NetGuard?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq22) |
|
||||
|
|
||||
<a name="FAQ24"></a> |
|
||||
[**(24) Can you remove the notification from the status bar?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq24) |
|
||||
|
|
||||
<a name="FAQ25"></a> |
|
||||
[**(25) Can you add a 'select all'?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq25) |
|
||||
|
|
||||
<a name="FAQ27"></a> |
|
||||
[**(27) How do I read the blocked traffic log?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq27) |
|
||||
|
|
||||
<a name="FAQ28"></a> |
|
||||
[**(28) Why is Google connectivity services allowed internet access by default?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq28) |
|
||||
|
|
||||
<a name="FAQ29"></a> |
|
||||
[**(29) Why do I get 'The item you requested is not available for purchase'?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq29) |
|
||||
|
|
||||
<a name="FAQ30"></a> |
|
||||
[**(30) Can I also run AFWall+ on the same device?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq30) |
|
||||
|
|
||||
<a name="FAQ31"></a> |
|
||||
[**(31) Why can some applications be configured as a group only?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq31) |
|
||||
|
|
||||
<a name="FAQ32"></a> |
|
||||
[**(32) Why is the battery/network usage of NetGuard so high**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq32) |
|
||||
|
|
||||
<a name="FAQ33"></a> |
|
||||
[**(33) Can you add profiles?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq33) |
|
||||
|
|
||||
<a name="FAQ34"></a> |
|
||||
[**(34) Can you add the condition 'when on foreground'?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq34) |
|
||||
|
|
||||
<a name="FAQ35"></a> |
|
||||
[**(35) Why does the VPN not start?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq35) |
|
||||
|
|
||||
<a name="FAQ36"></a> |
|
||||
[**(36) Can you add PIN or password protection?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq36) |
|
||||
|
|
||||
<a name="FAQ37"></a> |
|
||||
[**(37) Why are the pro features so expensive?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq37) |
|
||||
|
|
||||
<a name="FAQ38"></a> |
|
||||
[**(38) Why did NetGuard stop running?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq38) |
|
||||
|
|
||||
<a name="FAQ39"></a> |
|
||||
[**(39) How does a VPN based firewall differ from a iptables based firewall?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq39) |
|
||||
|
|
||||
<a name="FAQ40"></a> |
|
||||
[**(40) Can you add schedules?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq40) |
|
||||
|
|
||||
<a name="FAQ41"></a> |
|
||||
[**(41) Can you add wildcards?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq41) |
|
||||
|
|
||||
<a name="FAQ42"></a> |
|
||||
[**(42) Why is permission ... needed?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq42) |
|
||||
|
|
||||
<a name="FAQ43"></a> |
|
||||
[**(43) I get 'This app is causing your device to run slowly'**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq43) |
|
||||
|
|
||||
<a name="FAQ44"></a> |
|
||||
[**(44) I don't get notifications on access**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq44) |
|
||||
|
|
||||
<a name="FAQ45"></a> |
|
||||
[**(45) Does NetGuard handle incoming connections?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq45) |
|
||||
|
|
||||
<a name="FAQ46"></a> |
|
||||
[**(46) Can I get a refund?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq46) |
|
||||
|
|
||||
<a name="FAQ47"></a> |
|
||||
[**(47) Why are there in application advertisements?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq47) |
|
||||
|
|
||||
<a name="FAQ48"></a> |
|
||||
[**(48) Why are some domain names blocked while they are set to be allowed?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq48) |
|
||||
|
|
||||
<a name="FAQ49"></a> |
|
||||
[**(49) Does NetGuard encrypt my internet traffic / hide my IP address?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq49) |
|
||||
|
|
||||
<a name="FAQ50"></a> |
|
||||
[**(50) Will NetGuard automatically start on boot?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq50) |
|
||||
|
|
||||
<a name="FAQ51"></a> |
|
||||
[**(51) NetGuard blocks all internet traffic!**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq51) |
|
||||
|
|
||||
<a name="FAQ52"></a> |
|
||||
[**(52) What is lockdown mode?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq52) |
|
||||
|
|
||||
<a name="FAQ53"></a> |
|
||||
[**(53) The translation in my language is missing / incorrect / incomplete!**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq53) |
|
||||
|
|
||||
<a name="FAQ54"></a> |
|
||||
[**(54) How to tunnel all TCP connections through the Tor network?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq54) |
|
||||
|
|
||||
<a name="FAQ55"></a> |
|
||||
[**(55) Why does NetGuard connect to Amazon / ipinfo.io?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq55) |
|
||||
|
|
||||
<a name="FAQ56"></a> |
|
||||
[**(56) NetGuard allows all internet traffic!**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq56) |
|
||||
|
|
||||
<a name="FAQ57"></a> |
|
||||
[**(57) Why does NetGuard use so much data?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq57) |
|
||||
|
|
||||
<a name="FAQ58"></a> |
|
||||
[**(58) Why does loading the application list take a long time?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq58) |
|
||||
|
|
||||
<a name="FAQ59"></a> |
|
||||
[**(59) Can you help me restore my purchase?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq59) |
|
||||
|
|
||||
<a name="FAQ60"></a> |
|
||||
[**(60) Why does IP (Wi-Fi) calling/SMS/MMS not work?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq60) |
|
||||
|
|
||||
<a name="FAQ61"></a> |
|
||||
[**(61) Help, NetGuard crashed!**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq61) |
|
||||
|
|
||||
<a name="FAQ62"></a> |
|
||||
[**(62) How can I solve 'There was a problem parsing the package' ?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq62) |
|
||||
|
|
||||
<a name="FAQ63"></a> |
|
||||
[**(63) Why is all DNS traffic allowed?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq63) |
|
||||
|
|
||||
<a name="FAQ64"></a> |
|
||||
[**(64) Can you add DNS over TLS?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq64) |
|
||||
|
|
||||
<a name="FAQ65"></a> |
|
||||
[**(65) Why can NetGuard not block itself?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq65) |
|
||||
|
|
||||
Support |
|
||||
------- |
|
||||
|
|
||||
For questions, feature requests and bug reports, please [use this XDA-Developers forum thread](http://forum.xda-developers.com/showthread.php?t=3233012). |
|
||||
|
|
||||
There is support on the latest version of NetGuard only. |
|
||||
|
|
||||
There is no support on things that are not directly related to NetGuard. |
|
||||
|
|
||||
There is no support on building and developing things by yourself. |
|
||||
|
|
||||
**NetGuard is supported for phones and tablets only, so not for other device types like on a television or in a car.** |
|
||||
|
|
||||
Contributing |
|
||||
------------ |
|
||||
|
|
||||
*Building* |
|
||||
|
|
||||
Building is simple, if you install the right tools: |
|
||||
|
|
||||
* [Android Studio](http://developer.android.com/sdk/) |
|
||||
* [Android NDK](http://developer.android.com/tools/sdk/ndk/) |
|
||||
|
|
||||
The native code is built as part of the Android Studio project. |
|
||||
|
|
||||
It is expected that you can solve build problems yourself, so there is no support on building. |
|
||||
If you cannot build yourself, there are prebuilt versions of NetGuard available [here](https://github.com/M66B/NetGuard/releases). |
|
||||
|
|
||||
*Translating* |
|
||||
|
|
||||
* Translations to other languages are welcomed |
|
||||
* You can translate online [here](https://crowdin.com/project/netguard/) |
|
||||
* If your language is not listed, please send a message to marcel(plus)netguard(at)faircode(dot)eu |
|
||||
* You can see the status of all translations [here](https://crowdin.com/project/netguard). |
|
||||
|
|
||||
Please note that by contributing you agree to the license below, including the copyright, without any additional terms or conditions. |
|
||||
|
|
||||
Attribution |
|
||||
----------- |
|
||||
|
|
||||
NetGuard uses: |
|
||||
|
|
||||
* [Glide](https://bumptech.github.io/glide/) |
|
||||
* [Android Support Library](https://developer.android.com/tools/support-library/) |
|
||||
|
|
||||
License |
|
||||
------- |
|
||||
|
|
||||
[GNU General Public License version 3](http://www.gnu.org/licenses/gpl.txt) |
|
||||
|
|
||||
Copyright (c) 2015-2018 Marcel Bokhorst ([M66B](https://contact.faircode.eu/)) |
|
||||
|
|
||||
All rights reserved |
|
||||
|
|
||||
This file is part of NetGuard. |
|
||||
|
|
||||
NetGuard is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your discretion) any later version. |
|
||||
|
|
||||
NetGuard 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 General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with NetGuard. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). |
|
||||
|
|
||||
Trademarks |
|
||||
---------- |
|
||||
|
|
||||
*Android is a trademark of Google Inc. Google Play is a trademark of Google Inc* |
|
||||
|
# NetGuard |
||||
|
|
||||
|
# Edits in NetGuard Code |
||||
|
The NetGuard code has been taken from https://github.com/M66B/NetGuard |
||||
|
The compilation of the code was problematic and the problem was solved by modifiying the following lines of code in app --> Gradle Scripts --> build.gradle (Module :app) |
||||
|
|
||||
|
Add the following lines: |
||||
|
|
||||
|
storeFile file("my.keystore") |
||||
|
storePassword "store_password" |
||||
|
keyAlias "my_key_alias" |
||||
|
keyPassword "key_password" |
||||
|
|
||||
|
and comment out the following lines: |
||||
|
|
||||
|
// def keystorePropertiesFile = rootProject.file("keystore.properties") |
||||
|
// def keystoreProperties = new Properties() |
||||
|
// keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) |
||||
|
|
||||
|
// storeFile file(keystoreProperties['storeFile']) |
||||
|
// storePassword keystoreProperties['storePassword'] |
||||
|
// keyAlias keystoreProperties['keyAlias'] |
||||
|
// keyPassword keystoreProperties['keyPassword'] |
||||
|
|
||||
|
# The readme file from this point onwards is the original readme file. |
||||
|
*NetGuard* provides simple and advanced ways to block access to the internet - no root required. |
||||
|
Applications and addresses can individually be allowed or denied access to your Wi-Fi and/or mobile connection. |
||||
|
|
||||
|
<br> |
||||
|
|
||||
|
**WARNING: there is an app in the Samsung Galaxy app store "*Play Music - MP3 Music player*" |
||||
|
with the same package name as NetGuard, which will be installed as update without your confirmation. |
||||
|
This app is probably malicious and was reported to Samsung on December 8, 2021.** |
||||
|
|
||||
|
<br> |
||||
|
|
||||
|
Blocking access to the internet can help: |
||||
|
|
||||
|
* reduce your data usage |
||||
|
* save your battery |
||||
|
* increase your privacy |
||||
|
|
||||
|
NetGuard is the first free and open source no-root firewall for Android. |
||||
|
|
||||
|
Features: |
||||
|
|
||||
|
* Simple to use |
||||
|
* No root required |
||||
|
* 100% open source |
||||
|
* No calling home |
||||
|
* No tracking or analytics |
||||
|
* Actively developed and supported |
||||
|
* Android 5.1 and later supported |
||||
|
* IPv4/IPv6 TCP/UDP supported |
||||
|
* Tethering supported |
||||
|
* Optionally allow when screen on |
||||
|
* Optionally block when roaming |
||||
|
* Optionally block system applications |
||||
|
* Optionally forward ports, also to external addresses (not available if installed from the Play store) |
||||
|
* Optionally notify when an application accesses the internet |
||||
|
* Optionally record network usage per application per address |
||||
|
* Optionally [block ads using a hosts file](https://github.com/M66B/NetGuard/blob/master/ADBLOCKING.md) (not available if installed from the Play store) |
||||
|
* Material design theme with light and dark theme |
||||
|
|
||||
|
PRO features: |
||||
|
|
||||
|
* Log all outgoing traffic; search and filter access attempts; export PCAP files to analyze traffic |
||||
|
* Allow/block individual addresses per application |
||||
|
* New application notifications; configure NetGuard directly from the notification |
||||
|
* Display network speed graph in a status bar notification |
||||
|
* Select from five additional themes in both light and dark version |
||||
|
|
||||
|
There is no other no-root firewall offering all these features. |
||||
|
|
||||
|
Requirements: |
||||
|
|
||||
|
* Android 5.1 or later |
||||
|
* A [compatible device](#compatibility) |
||||
|
|
||||
|
Downloads: |
||||
|
|
||||
|
* [GitHub](https://github.com/M66B/NetGuard/releases) |
||||
|
* [Google Play](https://play.google.com/store/apps/details?id=eu.faircode.netguard) |
||||
|
|
||||
|
Certificate fingerprints: |
||||
|
|
||||
|
* MD5: B6:4A:E8:08:1C:3C:9C:19:D6:9E:29:00:46:89:DA:73 |
||||
|
* SHA1: EF:46:F8:13:D2:C8:A0:64:D7:2C:93:6B:9B:96:D1:CC:CC:98:93:78 |
||||
|
* SHA256: E4:A2:60:A2:DC:E7:B7:AF:23:EE:91:9C:48:9E:15:FD:01:02:B9:3F:9E:7C:9D:82:B0:9C:0B:39:50:00:E4:D4 |
||||
|
|
||||
|
Usage: |
||||
|
|
||||
|
* Enable the firewall using the switch in the action bar |
||||
|
* Allow/deny Wi-Fi/mobile internet access using the icons along the right side of the application list |
||||
|
|
||||
|
You can use the settings menu to change from blacklist mode (allow all in *Settings* but block unwanted applications in list) to whitelist mode (block all in *Settings* but allow favorite applications in list). |
||||
|
|
||||
|
* Red/orange/yellow/amber = internet access denied |
||||
|
* Teal/blue/purple/grey = internet access allowed |
||||
|
|
||||
|
<img src="https://raw.githubusercontent.com/M66B/NetGuard/master/screenshots/01-main.png" width="320" height="569" /> |
||||
|
<img src="https://raw.githubusercontent.com/M66B/NetGuard/master/screenshots/02-main-details.png" width="320" height="569" /> |
||||
|
<img src="https://raw.githubusercontent.com/M66B/NetGuard/master/screenshots/03-main-access.png" width="320" height="569" /> |
||||
|
<img src="https://raw.githubusercontent.com/M66B/NetGuard/master/screenshots/08-notifications.png" width="320" height="569" /> |
||||
|
|
||||
|
For more screenshots, see [here](https://github.com/M66B/NetGuard/tree/master/screenshots). |
||||
|
|
||||
|
Compatibility |
||||
|
------------- |
||||
|
|
||||
|
The only way to build a no-root firewall on Android is to use the Android VPN service. |
||||
|
Android doesn't allow chaining of VPN services, so you cannot use NetGuard together with other VPN based applications. |
||||
|
See also [this FAQ](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq2). |
||||
|
|
||||
|
NetGuard can be used on rooted devices too and even offers more features than most root firewalls. |
||||
|
|
||||
|
Some older Android versions, especially Samsung's Android versions, have a buggy VPN implementation, |
||||
|
which results in Android refusing to start the VPN service in certain circumstances, |
||||
|
like when there is no internet connectivity yet (when starting up your device) |
||||
|
or when incorrectly requiring manual approval of the VPN service again (when starting up your device). |
||||
|
NetGuard will try to workaround this and remove the error message when it succeeds, else you are out of luck. |
||||
|
|
||||
|
Some LineageOS versions have a broken Android VPN implementation, causing all traffic to be blocked, |
||||
|
please see [this FAQ](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq51) for more information. |
||||
|
|
||||
|
NetGuard is not supported for apps installed in a [work profile](https://developer.android.com/work/managed-profiles), |
||||
|
or in a [Secure Folder](https://www.samsung.com/uk/support/mobile-devices/what-is-the-secure-folder-and-how-do-i-use-it/) (Samsung), |
||||
|
or as second instance (MIUI), or as Parallel app (OnePlus), or as Xiaomi dual app |
||||
|
because the Android VPN service too often does not work correctly in this situation, which can't be fixed by NetGuard. |
||||
|
|
||||
|
Filtering mode cannot be used on [CopperheadOS](https://copperhead.co/android/). |
||||
|
|
||||
|
NetGuard will not work or crash when the package *com.android.vpndialogs* has been removed or otherwise is unavailable. |
||||
|
Removing this package is possible with root permissions only. |
||||
|
If you disable this package, you can enable it with this command again: |
||||
|
|
||||
|
``` |
||||
|
adb shell pm enable --user 0 com.android.vpndialogs |
||||
|
``` |
||||
|
|
||||
|
NetGuard is supported for phones and tablets only, so not for other device types like on a television or in a car. |
||||
|
|
||||
|
Android does not allow incoming connections (not the same as incoming traffic) and the Android VPN service has no support for this either. |
||||
|
Therefore managing incoming connections for servers running on your device is not supported. |
||||
|
|
||||
|
Wi-Fi or IP calling will not work if your provider uses [IPsec](https://en.wikipedia.org/wiki/IPsec) to encrypt your phone calls, SMS messages and/or MMS messages, |
||||
|
unless there was made an exception in NetGuard for your provider (currently for T-Mobile and Verizon). |
||||
|
I am happy to add exceptions for other providers, but I need the [MCC](https://en.wikipedia.org/wiki/Mobile_country_code) codes, [MNC](https://en.wikipedia.org/wiki/MNC) codes and [IP address](https://en.wikipedia.org/wiki/IP_address) ranges your provider is using. |
||||
|
As an alternative you can enable the option '*Disable on call*', which is available since version 2.113. |
||||
|
|
||||
|
|
||||
|
<a name="FAQ"></a> |
||||
|
Frequently Asked Questions (FAQ) |
||||
|
-------------------------------- |
||||
|
|
||||
|
<a name="FAQ0"></a> |
||||
|
[**(0) How do I use NetGuard?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq0) |
||||
|
|
||||
|
<a name="FAQ1"></a> |
||||
|
[**(1) Can NetGuard completely protect my privacy?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq1) |
||||
|
|
||||
|
<a name="FAQ2"></a> |
||||
|
[**(2) Can I use another VPN application while using NetGuard?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq2) |
||||
|
|
||||
|
<a name="FAQ3"></a> |
||||
|
[**(3) Can I use NetGuard on any Android version?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq3) |
||||
|
|
||||
|
<a name="FAQ4"></a> |
||||
|
[**(4) Will NetGuard use extra battery power?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq4) |
||||
|
|
||||
|
<a name="FAQ6"></a> |
||||
|
[**(6) Will NetGuard send my internet traffic to an external (VPN) server?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq6) |
||||
|
|
||||
|
<a name="FAQ7"></a> |
||||
|
[**(7) Why are applications without internet permission shown?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq7) |
||||
|
|
||||
|
<a name="FAQ8"></a> |
||||
|
[**(8) What do I need to enable for the Google Play™ store app to work?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq8) |
||||
|
|
||||
|
<a name="FAQ9"></a> |
||||
|
[**(9) Why is the VPN service being restarted?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq9) |
||||
|
|
||||
|
<a name="FAQ10"></a> |
||||
|
[**(10) Will you provide a Tasker plug-in?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq10) |
||||
|
|
||||
|
<a name="FAQ13"></a> |
||||
|
[**(13) How can I remove the ongoing NetGuard entry in the notification screen?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq13) |
||||
|
|
||||
|
<a name="FAQ14"></a> |
||||
|
[**(14) Why can't I select OK to approve the VPN connection request?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq14) |
||||
|
|
||||
|
<a name="FAQ15"></a> |
||||
|
[**(15) Are F-Droid builds supported?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq15) |
||||
|
|
||||
|
<a name="FAQ16"></a> |
||||
|
[**(16) Why are some applications shown dimmed?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq16) |
||||
|
|
||||
|
<a name="FAQ17"></a> |
||||
|
[**(17) Why is NetGuard using so much memory?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq17) |
||||
|
|
||||
|
<a name="FAQ18"></a> |
||||
|
[**(18) Why can't I find NetGuard in the Google Play™ store app?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq18) |
||||
|
|
||||
|
<a name="FAQ19"></a> |
||||
|
[**(19) Why does application XYZ still have internet access?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq19) |
||||
|
|
||||
|
<a name="FAQ20"></a> |
||||
|
[**(20) Can I Greenify/hibernate NetGuard?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq20) |
||||
|
|
||||
|
<a name="FAQ21"></a> |
||||
|
[**(21) Does doze mode affect NetGuard?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq21) |
||||
|
|
||||
|
<a name="FAQ22"></a> |
||||
|
[**(22) Can I tether (use the Android hotspot) / use Wi-Fi calling while using NetGuard?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq22) |
||||
|
|
||||
|
<a name="FAQ24"></a> |
||||
|
[**(24) Can you remove the notification from the status bar?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq24) |
||||
|
|
||||
|
<a name="FAQ25"></a> |
||||
|
[**(25) Can you add a 'select all'?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq25) |
||||
|
|
||||
|
<a name="FAQ27"></a> |
||||
|
[**(27) How do I read the blocked traffic log?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq27) |
||||
|
|
||||
|
<a name="FAQ28"></a> |
||||
|
[**(28) Why is Google connectivity services allowed internet access by default?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq28) |
||||
|
|
||||
|
<a name="FAQ29"></a> |
||||
|
[**(29) Why do I get 'The item you requested is not available for purchase'?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq29) |
||||
|
|
||||
|
<a name="FAQ30"></a> |
||||
|
[**(30) Can I also run AFWall+ on the same device?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq30) |
||||
|
|
||||
|
<a name="FAQ31"></a> |
||||
|
[**(31) Why can some applications be configured as a group only?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq31) |
||||
|
|
||||
|
<a name="FAQ32"></a> |
||||
|
[**(32) Why is the battery/network usage of NetGuard so high**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq32) |
||||
|
|
||||
|
<a name="FAQ33"></a> |
||||
|
[**(33) Can you add profiles?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq33) |
||||
|
|
||||
|
<a name="FAQ34"></a> |
||||
|
[**(34) Can you add the condition 'when on foreground'?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq34) |
||||
|
|
||||
|
<a name="FAQ35"></a> |
||||
|
[**(35) Why does the VPN not start?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq35) |
||||
|
|
||||
|
<a name="FAQ36"></a> |
||||
|
[**(36) Can you add PIN or password protection?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq36) |
||||
|
|
||||
|
<a name="FAQ37"></a> |
||||
|
[**(37) Why are the pro features so expensive?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq37) |
||||
|
|
||||
|
<a name="FAQ38"></a> |
||||
|
[**(38) Why did NetGuard stop running?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq38) |
||||
|
|
||||
|
<a name="FAQ39"></a> |
||||
|
[**(39) How does a VPN based firewall differ from a iptables based firewall?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq39) |
||||
|
|
||||
|
<a name="FAQ40"></a> |
||||
|
[**(40) Can you add schedules?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq40) |
||||
|
|
||||
|
<a name="FAQ41"></a> |
||||
|
[**(41) Can you add wildcards?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq41) |
||||
|
|
||||
|
<a name="FAQ42"></a> |
||||
|
[**(42) Why is permission ... needed?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq42) |
||||
|
|
||||
|
<a name="FAQ43"></a> |
||||
|
[**(43) I get 'This app is causing your device to run slowly'**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq43) |
||||
|
|
||||
|
<a name="FAQ44"></a> |
||||
|
[**(44) I don't get notifications on access**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq44) |
||||
|
|
||||
|
<a name="FAQ45"></a> |
||||
|
[**(45) Does NetGuard handle incoming connections?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq45) |
||||
|
|
||||
|
<a name="FAQ46"></a> |
||||
|
[**(46) Can I get a refund?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq46) |
||||
|
|
||||
|
<a name="FAQ47"></a> |
||||
|
[**(47) Why are there in application advertisements?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq47) |
||||
|
|
||||
|
<a name="FAQ48"></a> |
||||
|
[**(48) Why are some domain names blocked while they are set to be allowed?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq48) |
||||
|
|
||||
|
<a name="FAQ49"></a> |
||||
|
[**(49) Does NetGuard encrypt my internet traffic / hide my IP address?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq49) |
||||
|
|
||||
|
<a name="FAQ50"></a> |
||||
|
[**(50) Will NetGuard automatically start on boot?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq50) |
||||
|
|
||||
|
<a name="FAQ51"></a> |
||||
|
[**(51) NetGuard blocks all internet traffic!**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq51) |
||||
|
|
||||
|
<a name="FAQ52"></a> |
||||
|
[**(52) What is lockdown mode?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq52) |
||||
|
|
||||
|
<a name="FAQ53"></a> |
||||
|
[**(53) The translation in my language is missing / incorrect / incomplete!**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq53) |
||||
|
|
||||
|
<a name="FAQ54"></a> |
||||
|
[**(54) How to tunnel all TCP connections through the Tor network?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq54) |
||||
|
|
||||
|
<a name="FAQ55"></a> |
||||
|
[**(55) Why does NetGuard connect to Amazon / ipinfo.io?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq55) |
||||
|
|
||||
|
<a name="FAQ56"></a> |
||||
|
[**(56) NetGuard allows all internet traffic!**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq56) |
||||
|
|
||||
|
<a name="FAQ57"></a> |
||||
|
[**(57) Why does NetGuard use so much data?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq57) |
||||
|
|
||||
|
<a name="FAQ58"></a> |
||||
|
[**(58) Why does loading the application list take a long time?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq58) |
||||
|
|
||||
|
<a name="FAQ59"></a> |
||||
|
[**(59) Can you help me restore my purchase?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq59) |
||||
|
|
||||
|
<a name="FAQ60"></a> |
||||
|
[**(60) Why does IP (Wi-Fi) calling/SMS/MMS not work?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq60) |
||||
|
|
||||
|
<a name="FAQ61"></a> |
||||
|
[**(61) Help, NetGuard crashed!**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq61) |
||||
|
|
||||
|
<a name="FAQ62"></a> |
||||
|
[**(62) How can I solve 'There was a problem parsing the package' ?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq62) |
||||
|
|
||||
|
<a name="FAQ63"></a> |
||||
|
[**(63) Why is all DNS traffic allowed?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq63) |
||||
|
|
||||
|
<a name="FAQ64"></a> |
||||
|
[**(64) Can you add DNS over TLS?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq64) |
||||
|
|
||||
|
<a name="FAQ65"></a> |
||||
|
[**(65) Why can NetGuard not block itself?**](https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq65) |
||||
|
|
||||
|
Support |
||||
|
------- |
||||
|
|
||||
|
For questions, feature requests and bug reports, please [use this XDA-Developers forum thread](http://forum.xda-developers.com/showthread.php?t=3233012). |
||||
|
|
||||
|
There is support on the latest version of NetGuard only. |
||||
|
|
||||
|
There is no support on things that are not directly related to NetGuard. |
||||
|
|
||||
|
There is no support on building and developing things by yourself. |
||||
|
|
||||
|
**NetGuard is supported for phones and tablets only, so not for other device types like on a television or in a car.** |
||||
|
|
||||
|
Contributing |
||||
|
------------ |
||||
|
|
||||
|
*Building* |
||||
|
|
||||
|
Building is simple, if you install the right tools: |
||||
|
|
||||
|
* [Android Studio](http://developer.android.com/sdk/) |
||||
|
* [Android NDK](http://developer.android.com/tools/sdk/ndk/) |
||||
|
|
||||
|
The native code is built as part of the Android Studio project. |
||||
|
|
||||
|
It is expected that you can solve build problems yourself, so there is no support on building. |
||||
|
If you cannot build yourself, there are prebuilt versions of NetGuard available [here](https://github.com/M66B/NetGuard/releases). |
||||
|
|
||||
|
*Translating* |
||||
|
|
||||
|
* Translations to other languages are welcomed |
||||
|
* You can translate online [here](https://crowdin.com/project/netguard/) |
||||
|
* If your language is not listed, please send a message to marcel(plus)netguard(at)faircode(dot)eu |
||||
|
* You can see the status of all translations [here](https://crowdin.com/project/netguard). |
||||
|
|
||||
|
Please note that by contributing you agree to the license below, including the copyright, without any additional terms or conditions. |
||||
|
|
||||
|
Attribution |
||||
|
----------- |
||||
|
|
||||
|
NetGuard uses: |
||||
|
|
||||
|
* [Glide](https://bumptech.github.io/glide/) |
||||
|
* [Android Support Library](https://developer.android.com/tools/support-library/) |
||||
|
|
||||
|
License |
||||
|
------- |
||||
|
|
||||
|
[GNU General Public License version 3](http://www.gnu.org/licenses/gpl.txt) |
||||
|
|
||||
|
Copyright (c) 2015-2018 Marcel Bokhorst ([M66B](https://contact.faircode.eu/)) |
||||
|
|
||||
|
All rights reserved |
||||
|
|
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your discretion) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). |
||||
|
|
||||
|
Trademarks |
||||
|
---------- |
||||
|
|
||||
|
*Android is a trademark of Google Inc. Google Play is a trademark of Google Inc* |
@ -0,0 +1 @@ |
|||||
|
/build |
@ -0,0 +1,23 @@ |
|||||
|
|
||||
|
cmake_minimum_required(VERSION 3.4.1) |
||||
|
|
||||
|
add_library( netguard |
||||
|
SHARED |
||||
|
src/main/jni/netguard/netguard.c |
||||
|
src/main/jni/netguard/session.c |
||||
|
src/main/jni/netguard/ip.c |
||||
|
src/main/jni/netguard/tcp.c |
||||
|
src/main/jni/netguard/udp.c |
||||
|
src/main/jni/netguard/icmp.c |
||||
|
src/main/jni/netguard/dns.c |
||||
|
src/main/jni/netguard/dhcp.c |
||||
|
src/main/jni/netguard/pcap.c |
||||
|
src/main/jni/netguard/util.c ) |
||||
|
|
||||
|
include_directories( src/main/jni/netguard/ ) |
||||
|
|
||||
|
find_library( log-lib |
||||
|
log ) |
||||
|
|
||||
|
target_link_libraries( netguard |
||||
|
${log-lib} ) |
@ -0,0 +1,105 @@ |
|||||
|
apply plugin: 'com.android.application' |
||||
|
|
||||
|
// def keystorePropertiesFile = rootProject.file("keystore.properties") |
||||
|
// def keystoreProperties = new Properties() |
||||
|
// keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) |
||||
|
|
||||
|
android { |
||||
|
compileSdkVersion = 31 |
||||
|
|
||||
|
defaultConfig { |
||||
|
applicationId = "eu.faircode.netguard" |
||||
|
versionName = "2.303" |
||||
|
minSdkVersion 22 |
||||
|
targetSdkVersion 31 |
||||
|
versionCode = 2022111001 |
||||
|
archivesBaseName = "NetGuard-v$versionName" |
||||
|
|
||||
|
externalNativeBuild { |
||||
|
cmake { |
||||
|
cppFlags "" |
||||
|
arguments "-DANDROID_PLATFORM=android-22" |
||||
|
// https://developer.android.com/ndk/guides/cmake.html |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//ndkVersion "21.4.7075529" |
||||
|
ndk { |
||||
|
// https://developer.android.com/ndk/guides/abis.html#sa |
||||
|
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' |
||||
|
} |
||||
|
} |
||||
|
signingConfigs { |
||||
|
release { |
||||
|
// storeFile file(keystoreProperties['storeFile']) |
||||
|
// storePassword keystoreProperties['storePassword'] |
||||
|
// keyAlias keystoreProperties['keyAlias'] |
||||
|
// keyPassword keystoreProperties['keyPassword'] |
||||
|
|
||||
|
storeFile file("my.keystore") |
||||
|
storePassword "store_password" |
||||
|
keyAlias "my_key_alias" |
||||
|
keyPassword "key_password" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
externalNativeBuild { |
||||
|
cmake { |
||||
|
path "CMakeLists.txt" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
buildTypes { |
||||
|
release { |
||||
|
minifyEnabled = true |
||||
|
proguardFiles.add(file('proguard-rules.pro')) |
||||
|
signingConfig signingConfigs.release |
||||
|
buildConfigField "boolean", "PLAY_STORE_RELEASE", "false" |
||||
|
buildConfigField "String", "HOSTS_FILE_URI", "\"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts\"" |
||||
|
buildConfigField "String", "GITHUB_LATEST_API", "\"https://api.github.com/repos/M66B/NetGuard/releases/latest\"" |
||||
|
} |
||||
|
play { |
||||
|
minifyEnabled = true |
||||
|
signingConfig signingConfigs.release |
||||
|
proguardFiles.add(file('proguard-rules.pro')) |
||||
|
buildConfigField "boolean", "PLAY_STORE_RELEASE", "true" |
||||
|
buildConfigField "String", "HOSTS_FILE_URI", "\"\"" |
||||
|
buildConfigField "String", "GITHUB_LATEST_API", "\"\"" |
||||
|
} |
||||
|
debug { |
||||
|
minifyEnabled = true |
||||
|
proguardFiles.add(file('proguard-rules.pro')) |
||||
|
buildConfigField "boolean", "PLAY_STORE_RELEASE", "false" |
||||
|
buildConfigField "String", "HOSTS_FILE_URI", "\"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts\"" |
||||
|
buildConfigField "String", "GITHUB_LATEST_API", "\"https://api.github.com/repos/M66B/NetGuard/releases/latest\"" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
compileOptions { |
||||
|
sourceCompatibility JavaVersion.VERSION_1_7 |
||||
|
targetCompatibility JavaVersion.VERSION_1_7 |
||||
|
} |
||||
|
|
||||
|
lint { |
||||
|
disable 'MissingTranslation' |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
dependencies { |
||||
|
implementation fileTree(dir: 'libs', include: ['*.jar']) |
||||
|
|
||||
|
// https://developer.android.com/jetpack/androidx/releases/ |
||||
|
implementation 'androidx.appcompat:appcompat:1.3.1' |
||||
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' |
||||
|
implementation 'androidx.recyclerview:recyclerview:1.2.1' |
||||
|
implementation 'androidx.preference:preference:1.1.1' |
||||
|
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' |
||||
|
annotationProcessor 'androidx.annotation:annotation:1.2.0' |
||||
|
|
||||
|
// https://bumptech.github.io/glide/ |
||||
|
implementation('com.github.bumptech.glide:glide:4.11.0') { |
||||
|
exclude group: "com.android.support" |
||||
|
} |
||||
|
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' |
||||
|
} |
@ -0,0 +1,62 @@ |
|||||
|
# Add project specific ProGuard rules here. |
||||
|
# By default, the flags in this file are appended to flags specified |
||||
|
# in /home/marcel/Android/Sdk/tools/proguard/proguard-android.txt |
||||
|
# You can edit the include path and order by changing the proguardFiles |
||||
|
# directive in build.gradle. |
||||
|
# |
||||
|
# For more details, see |
||||
|
# http://developer.android.com/guide/developing/tools/proguard.html |
||||
|
|
||||
|
# Add any project specific keep options here: |
||||
|
|
||||
|
# If your project uses WebView with JS, uncomment the following |
||||
|
# and specify the fully qualified class name to the JavaScript interface |
||||
|
# class: |
||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
||||
|
# public *; |
||||
|
#} |
||||
|
|
||||
|
#Line numbers |
||||
|
-renamesourcefileattribute SourceFile |
||||
|
-keepattributes SourceFile,LineNumberTable |
||||
|
|
||||
|
#NetGuard |
||||
|
-keepnames class eu.faircode.netguard.** { *; } |
||||
|
|
||||
|
#JNI |
||||
|
-keepclasseswithmembernames class * { |
||||
|
native <methods>; |
||||
|
} |
||||
|
|
||||
|
#JNI callbacks |
||||
|
-keep class eu.faircode.netguard.Allowed { *; } |
||||
|
-keep class eu.faircode.netguard.Packet { *; } |
||||
|
-keep class eu.faircode.netguard.ResourceRecord { *; } |
||||
|
-keep class eu.faircode.netguard.Usage { *; } |
||||
|
-keep class eu.faircode.netguard.ServiceSinkhole { |
||||
|
void nativeExit(java.lang.String); |
||||
|
void nativeError(int, java.lang.String); |
||||
|
void logPacket(eu.faircode.netguard.Packet); |
||||
|
void dnsResolved(eu.faircode.netguard.ResourceRecord); |
||||
|
boolean isDomainBlocked(java.lang.String); |
||||
|
int getUidQ(int, int, java.lang.String, int, java.lang.String, int); |
||||
|
eu.faircode.netguard.Allowed isAddressAllowed(eu.faircode.netguard.Packet); |
||||
|
void accountUsage(eu.faircode.netguard.Usage); |
||||
|
} |
||||
|
|
||||
|
#AndroidX |
||||
|
-keep class androidx.appcompat.widget.** { *; } |
||||
|
-keep class androidx.appcompat.app.AppCompatViewInflater { <init>(...); } |
||||
|
-keepclassmembers class * implements android.os.Parcelable { static ** CREATOR; } |
||||
|
|
||||
|
#Glide |
||||
|
-keep public class * implements com.bumptech.glide.module.GlideModule |
||||
|
-keep public class * extends com.bumptech.glide.module.AppGlideModule |
||||
|
-keep enum com.bumptech.glide.** {*;} |
||||
|
#-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { |
||||
|
# **[] $VALUES; |
||||
|
# public *; |
||||
|
#} |
||||
|
|
||||
|
#AdMob |
||||
|
-dontwarn com.google.android.gms.internal.** |
@ -0,0 +1,287 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||
|
package="eu.faircode.netguard" |
||||
|
android:installLocation="internalOnly"> |
||||
|
|
||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
||||
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> |
||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> |
||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> |
||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" /> |
||||
|
<uses-permission android:name="com.android.vending.BILLING" /> |
||||
|
<uses-permission android:name="android.permission.INTERNET" /> |
||||
|
<uses-permission android:name="android.permission.VIBRATE" /> |
||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> |
||||
|
<!-- http://developer.android.com/guide/topics/security/permissions.html#normal-dangerous --> |
||||
|
|
||||
|
<!-- https://developer.android.com/preview/privacy/package-visibility --> |
||||
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> |
||||
|
<!--queries> |
||||
|
<intent> |
||||
|
<action android:name="android.intent.action.MAIN" /> |
||||
|
<category android:name="android.intent.category.LAUNCHER" /> |
||||
|
</intent> |
||||
|
</queries--> |
||||
|
|
||||
|
<permission |
||||
|
android:name="eu.faircode.netguard.permission.ADMIN" |
||||
|
android:description="@string/app_description" |
||||
|
android:label="@string/app_name" |
||||
|
android:protectionLevel="signature" /> |
||||
|
|
||||
|
<uses-permission android:name="eu.faircode.netguard.permission.ADMIN" /> |
||||
|
|
||||
|
<uses-feature |
||||
|
android:name="android.hardware.wifi" |
||||
|
android:required="false" /> |
||||
|
<uses-feature |
||||
|
android:name="android.hardware.telephony" |
||||
|
android:required="false" /> |
||||
|
<uses-feature |
||||
|
android:name="android.software.app_widgets" |
||||
|
android:required="false" /> |
||||
|
<uses-feature |
||||
|
android:name="android.hardware.touchscreen" |
||||
|
android:required="false" /> |
||||
|
|
||||
|
<application |
||||
|
android:name="ApplicationEx" |
||||
|
android:allowBackup="false" |
||||
|
android:appCategory="productivity" |
||||
|
android:description="@string/app_description" |
||||
|
android:icon="@mipmap/ic_launcher" |
||||
|
android:label="@string/app_name" |
||||
|
android:networkSecurityConfig="@xml/network_security_config" |
||||
|
android:roundIcon="@mipmap/ic_launcher_round" |
||||
|
android:supportsRtl="true" |
||||
|
android:theme="@style/AppThemeTeal" |
||||
|
tools:ignore="ManifestResource"> |
||||
|
|
||||
|
<meta-data |
||||
|
android:name="android.max_aspect" |
||||
|
android:value="2.1" /> |
||||
|
|
||||
|
<activity |
||||
|
android:name=".ActivityMain" |
||||
|
android:configChanges="orientation|screenSize" |
||||
|
android:exported="true" |
||||
|
android:label="@string/app_name" |
||||
|
android:launchMode="singleTop" |
||||
|
android:resizeableActivity="true"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.intent.action.MAIN" /> |
||||
|
<category android:name="android.intent.category.LAUNCHER" /> |
||||
|
</intent-filter> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" /> |
||||
|
<category android:name="android.intent.category.DEFAULT" /> |
||||
|
</intent-filter> |
||||
|
<!-- intent-filter android:label="@string/app_name"> |
||||
|
<action android:name="android.intent.action.VIEW" /> |
||||
|
|
||||
|
<category android:name="android.intent.category.DEFAULT" /> |
||||
|
<category android:name="android.intent.category.BROWSABLE" /> |
||||
|
|
||||
|
<data |
||||
|
android:host="www.netguard.me" |
||||
|
android:pathPrefix="/" |
||||
|
android:scheme="https" /> |
||||
|
</intent-filter--> |
||||
|
|
||||
|
<meta-data |
||||
|
android:name="android.app.shortcuts" |
||||
|
android:resource="@xml/shortcuts" /> |
||||
|
</activity> |
||||
|
|
||||
|
<activity |
||||
|
android:name=".ActivitySettings" |
||||
|
android:configChanges="orientation|screenSize" |
||||
|
android:exported="true" |
||||
|
android:label="@string/menu_settings" |
||||
|
android:parentActivityName=".ActivityMain"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" /> |
||||
|
</intent-filter> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.intent.action.APPLICATION_PREFERENCES" /> |
||||
|
<category android:name="android.intent.category.DEFAULT" /> |
||||
|
</intent-filter> |
||||
|
|
||||
|
<meta-data |
||||
|
android:name="android.support.PARENT_ACTIVITY" |
||||
|
android:value=".ActivityMain" /> |
||||
|
</activity> |
||||
|
|
||||
|
<activity |
||||
|
android:name=".ActivityLog" |
||||
|
android:configChanges="orientation|screenSize" |
||||
|
android:label="@string/menu_log" |
||||
|
android:parentActivityName=".ActivityMain"> |
||||
|
<meta-data |
||||
|
android:name="android.support.PARENT_ACTIVITY" |
||||
|
android:value=".ActivityMain" /> |
||||
|
</activity> |
||||
|
|
||||
|
<activity |
||||
|
android:name=".ActivityPro" |
||||
|
android:configChanges="orientation|screenSize" |
||||
|
android:label="@string/title_pro" |
||||
|
android:parentActivityName=".ActivityMain"> |
||||
|
<meta-data |
||||
|
android:name="android.support.PARENT_ACTIVITY" |
||||
|
android:value=".ActivityMain" /> |
||||
|
</activity> |
||||
|
|
||||
|
<activity |
||||
|
android:name=".ActivityDns" |
||||
|
android:configChanges="orientation|screenSize" |
||||
|
android:label="@string/setting_show_resolved" |
||||
|
android:parentActivityName=".ActivitySettings"> |
||||
|
<meta-data |
||||
|
android:name="android.support.PARENT_ACTIVITY" |
||||
|
android:value=".ActivitySettings" /> |
||||
|
</activity> |
||||
|
|
||||
|
<activity |
||||
|
android:name=".ActivityForwarding" |
||||
|
android:configChanges="orientation|screenSize" |
||||
|
android:label="@string/setting_forwarding" |
||||
|
android:parentActivityName=".ActivitySettings"> |
||||
|
<meta-data |
||||
|
android:name="android.support.PARENT_ACTIVITY" |
||||
|
android:value=".ActivitySettings" /> |
||||
|
</activity> |
||||
|
|
||||
|
<activity |
||||
|
android:name=".ActivityForwardApproval" |
||||
|
android:configChanges="orientation|screenSize" |
||||
|
android:exported="true" |
||||
|
android:label="@string/app_name" |
||||
|
android:theme="@style/AppDialog"> |
||||
|
<intent-filter> |
||||
|
<action android:name="eu.faircode.netguard.START_PORT_FORWARD" /> |
||||
|
<action android:name="eu.faircode.netguard.STOP_PORT_FORWARD" /> |
||||
|
</intent-filter> |
||||
|
</activity> |
||||
|
|
||||
|
<service |
||||
|
android:name=".ServiceSinkhole" |
||||
|
android:exported="true" |
||||
|
android:label="@string/app_name" |
||||
|
android:permission="android.permission.BIND_VPN_SERVICE"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.net.VpnService" /> |
||||
|
</intent-filter> |
||||
|
</service> |
||||
|
|
||||
|
<service |
||||
|
android:name=".ServiceExternal" |
||||
|
android:exported="true" |
||||
|
android:label="@string/app_name"> |
||||
|
<intent-filter> |
||||
|
<action android:name="eu.faircode.netguard.DOWNLOAD_HOSTS_FILE" /> |
||||
|
</intent-filter> |
||||
|
</service> |
||||
|
|
||||
|
<service |
||||
|
android:name=".ServiceTileMain" |
||||
|
android:exported="true" |
||||
|
android:icon="@drawable/ic_security_white_24dp" |
||||
|
android:label="@string/app_name" |
||||
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.service.quicksettings.action.QS_TILE" /> |
||||
|
</intent-filter> |
||||
|
</service> |
||||
|
|
||||
|
<service |
||||
|
android:name=".ServiceTileGraph" |
||||
|
android:exported="true" |
||||
|
android:icon="@drawable/ic_equalizer_white_24dp" |
||||
|
android:label="@string/setting_stats_category" |
||||
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.service.quicksettings.action.QS_TILE" /> |
||||
|
</intent-filter> |
||||
|
</service> |
||||
|
|
||||
|
<service |
||||
|
android:name=".ServiceTileFilter" |
||||
|
android:exported="true" |
||||
|
android:icon="@drawable/ic_filter_list_white_24dp" |
||||
|
android:label="@string/setting_filter" |
||||
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.service.quicksettings.action.QS_TILE" /> |
||||
|
</intent-filter> |
||||
|
</service> |
||||
|
|
||||
|
<service |
||||
|
android:name=".ServiceTileLockdown" |
||||
|
android:exported="true" |
||||
|
android:icon="@drawable/ic_lock_outline_white_24dp" |
||||
|
android:label="@string/setting_lockdown" |
||||
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.service.quicksettings.action.QS_TILE" /> |
||||
|
</intent-filter> |
||||
|
</service> |
||||
|
|
||||
|
<receiver |
||||
|
android:name=".ReceiverAutostart" |
||||
|
android:exported="true" |
||||
|
android:label="@string/app_name"> |
||||
|
<intent-filter android:priority="999"> |
||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" /> |
||||
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" /> |
||||
|
</intent-filter> |
||||
|
</receiver> |
||||
|
|
||||
|
<receiver |
||||
|
android:name=".ReceiverPackageRemoved" |
||||
|
android:exported="true"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" /> |
||||
|
<data android:scheme="package" /> |
||||
|
</intent-filter> |
||||
|
</receiver> |
||||
|
|
||||
|
<receiver |
||||
|
android:name=".WidgetMain" |
||||
|
android:exported="true" |
||||
|
android:label="@string/app_name"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> |
||||
|
</intent-filter> |
||||
|
<meta-data |
||||
|
android:name="android.appwidget.provider" |
||||
|
android:resource="@xml/widgetmain" /> |
||||
|
</receiver> |
||||
|
|
||||
|
<receiver |
||||
|
android:name=".WidgetLockdown" |
||||
|
android:exported="true" |
||||
|
android:label="@string/setting_lockdown"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> |
||||
|
</intent-filter> |
||||
|
<meta-data |
||||
|
android:name="android.appwidget.provider" |
||||
|
android:resource="@xml/widgetlockdown" /> |
||||
|
</receiver> |
||||
|
|
||||
|
<receiver |
||||
|
android:name=".WidgetAdmin" |
||||
|
android:exported="true" |
||||
|
android:label="@string/app_name" |
||||
|
android:permission="eu.faircode.netguard.permission.ADMIN"> |
||||
|
<intent-filter> |
||||
|
<action android:name="eu.faircode.netguard.ON" /> |
||||
|
<action android:name="eu.faircode.netguard.OFF" /> |
||||
|
<action android:name="eu.faircode.netguard.LOCKDOWN_ON" /> |
||||
|
<action android:name="eu.faircode.netguard.LOCKDOWN_OFF" /> |
||||
|
</intent-filter> |
||||
|
</receiver> |
||||
|
</application> |
||||
|
</manifest> |
@ -0,0 +1,144 @@ |
|||||
|
/* |
||||
|
* Copyright (C) 2012 The Android Open Source Project |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
package com.android.vending.billing; |
||||
|
|
||||
|
import android.os.Bundle; |
||||
|
|
||||
|
/** |
||||
|
* InAppBillingService is the service that provides in-app billing version 3 and beyond. |
||||
|
* This service provides the following features: |
||||
|
* 1. Provides a new API to get details of in-app items published for the app including |
||||
|
* price, type, title and description. |
||||
|
* 2. The purchase flow is synchronous and purchase information is available immediately |
||||
|
* after it completes. |
||||
|
* 3. Purchase information of in-app purchases is maintained within the Google Play system |
||||
|
* till the purchase is consumed. |
||||
|
* 4. An API to consume a purchase of an inapp item. All purchases of one-time |
||||
|
* in-app items are consumable and thereafter can be purchased again. |
||||
|
* 5. An API to get current purchases of the user immediately. This will not contain any |
||||
|
* consumed purchases. |
||||
|
* |
||||
|
* All calls will give a response code with the following possible values |
||||
|
* RESULT_OK = 0 - success |
||||
|
* RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog |
||||
|
* RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested |
||||
|
* RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase |
||||
|
* RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API |
||||
|
* RESULT_ERROR = 6 - Fatal error during the API action |
||||
|
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned |
||||
|
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned |
||||
|
*/ |
||||
|
interface IInAppBillingService { |
||||
|
/** |
||||
|
* Checks support for the requested billing API version, package and in-app type. |
||||
|
* Minimum API version supported by this interface is 3. |
||||
|
* @param apiVersion the billing version which the app is using |
||||
|
* @param packageName the package name of the calling app |
||||
|
* @param type type of the in-app item being purchased "inapp" for one-time purchases |
||||
|
* and "subs" for subscription. |
||||
|
* @return RESULT_OK(0) on success, corresponding result code on failures |
||||
|
*/ |
||||
|
int isBillingSupported(int apiVersion, String packageName, String type); |
||||
|
|
||||
|
/** |
||||
|
* Provides details of a list of SKUs |
||||
|
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle |
||||
|
* with a list JSON strings containing the productId, price, title and description. |
||||
|
* This API can be called with a maximum of 20 SKUs. |
||||
|
* @param apiVersion billing API version that the Third-party is using |
||||
|
* @param packageName the package name of the calling app |
||||
|
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST" |
||||
|
* @return Bundle containing the following key-value pairs |
||||
|
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on |
||||
|
* failure as listed above. |
||||
|
* "DETAILS_LIST" with a StringArrayList containing purchase information |
||||
|
* in JSON format similar to: |
||||
|
* '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00", |
||||
|
* "title : "Example Title", "description" : "This is an example description" }' |
||||
|
*/ |
||||
|
Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle); |
||||
|
|
||||
|
/** |
||||
|
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU, |
||||
|
* the type, a unique purchase token and an optional developer payload. |
||||
|
* @param apiVersion billing API version that the app is using |
||||
|
* @param packageName package name of the calling app |
||||
|
* @param sku the SKU of the in-app item as published in the developer console |
||||
|
* @param type the type of the in-app item ("inapp" for one-time purchases |
||||
|
* and "subs" for subscription). |
||||
|
* @param developerPayload optional argument to be sent back with the purchase information |
||||
|
* @return Bundle containing the following key-value pairs |
||||
|
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on |
||||
|
* failure as listed above. |
||||
|
* "BUY_INTENT" - PendingIntent to start the purchase flow |
||||
|
* |
||||
|
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow |
||||
|
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED. |
||||
|
* If the purchase is successful, the result data will contain the following key-value pairs |
||||
|
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on |
||||
|
* failure as listed above. |
||||
|
* "INAPP_PURCHASE_DATA" - String in JSON format similar to |
||||
|
* '{"orderId":"12999763169054705758.1371079406387615", |
||||
|
* "packageName":"com.example.app", |
||||
|
* "productId":"exampleSku", |
||||
|
* "purchaseTime":1345678900000, |
||||
|
* "purchaseToken" : "122333444455555", |
||||
|
* "developerPayload":"example developer payload" }' |
||||
|
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that |
||||
|
* was signed with the private key of the developer |
||||
|
* TODO: change this to app-specific keys. |
||||
|
*/ |
||||
|
Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type, |
||||
|
String developerPayload); |
||||
|
|
||||
|
/** |
||||
|
* Returns the current SKUs owned by the user of the type and package name specified along with |
||||
|
* purchase information and a signature of the data to be validated. |
||||
|
* This will return all SKUs that have been purchased in V3 and managed items purchased using |
||||
|
* V1 and V2 that have not been consumed. |
||||
|
* @param apiVersion billing API version that the app is using |
||||
|
* @param packageName package name of the calling app |
||||
|
* @param type the type of the in-app items being requested |
||||
|
* ("inapp" for one-time purchases and "subs" for subscription). |
||||
|
* @param continuationToken to be set as null for the first call, if the number of owned |
||||
|
* skus are too many, a continuationToken is returned in the response bundle. |
||||
|
* This method can be called again with the continuation token to get the next set of |
||||
|
* owned skus. |
||||
|
* @return Bundle containing the following key-value pairs |
||||
|
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on |
||||
|
* failure as listed above. |
||||
|
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs |
||||
|
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information |
||||
|
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures |
||||
|
* of the purchase information |
||||
|
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the |
||||
|
* next set of in-app purchases. Only set if the |
||||
|
* user has more owned skus than the current list. |
||||
|
*/ |
||||
|
Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken); |
||||
|
|
||||
|
/** |
||||
|
* Consume the last purchase of the given SKU. This will result in this item being removed |
||||
|
* from all subsequent responses to getPurchases() and allow re-purchase of this item. |
||||
|
* @param apiVersion billing API version that the app is using |
||||
|
* @param packageName package name of the calling app |
||||
|
* @param purchaseToken token in the purchase information JSON that identifies the purchase |
||||
|
* to be consumed |
||||
|
* @return 0 if consumption succeeded. Appropriate error values for failures. |
||||
|
*/ |
||||
|
int consumePurchase(int apiVersion, String packageName, String purchaseToken); |
||||
|
} |
After Width: 512 | Height: 512 | Size: 24 KiB |
After Width: 512 | Height: 512 | Size: 42 KiB |
@ -0,0 +1,256 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.Intent; |
||||
|
import android.content.pm.PackageManager; |
||||
|
import android.database.Cursor; |
||||
|
import android.net.Uri; |
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Bundle; |
||||
|
import android.util.Log; |
||||
|
import android.util.Xml; |
||||
|
import android.view.Menu; |
||||
|
import android.view.MenuInflater; |
||||
|
import android.view.MenuItem; |
||||
|
import android.widget.ListView; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.appcompat.app.AppCompatActivity; |
||||
|
|
||||
|
import org.xmlpull.v1.XmlSerializer; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.io.OutputStream; |
||||
|
import java.text.DateFormat; |
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.util.Date; |
||||
|
import java.util.Locale; |
||||
|
|
||||
|
public class ActivityDns extends AppCompatActivity { |
||||
|
private static final String TAG = "NetGuard.DNS"; |
||||
|
|
||||
|
private static final int REQUEST_EXPORT = 1; |
||||
|
|
||||
|
private boolean running; |
||||
|
private AdapterDns adapter = null; |
||||
|
|
||||
|
@Override |
||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||
|
Util.setTheme(this); |
||||
|
super.onCreate(savedInstanceState); |
||||
|
setContentView(R.layout.resolving); |
||||
|
|
||||
|
getSupportActionBar().setTitle(R.string.setting_show_resolved); |
||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
||||
|
|
||||
|
ListView lvDns = findViewById(R.id.lvDns); |
||||
|
adapter = new AdapterDns(this, DatabaseHelper.getInstance(this).getDns()); |
||||
|
lvDns.setAdapter(adapter); |
||||
|
|
||||
|
running = true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onCreateOptionsMenu(Menu menu) { |
||||
|
MenuInflater inflater = getMenuInflater(); |
||||
|
inflater.inflate(R.menu.dns, menu); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onPrepareOptionsMenu(Menu menu) { |
||||
|
PackageManager pm = getPackageManager(); |
||||
|
menu.findItem(R.id.menu_export).setEnabled(getIntentExport().resolveActivity(pm) != null); |
||||
|
return super.onPrepareOptionsMenu(menu); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onOptionsItemSelected(MenuItem item) { |
||||
|
switch (item.getItemId()) { |
||||
|
case R.id.menu_refresh: |
||||
|
refresh(); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_cleanup: |
||||
|
cleanup(); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_clear: |
||||
|
Util.areYouSure(this, R.string.menu_clear, new Util.DoubtListener() { |
||||
|
@Override |
||||
|
public void onSure() { |
||||
|
clear(); |
||||
|
} |
||||
|
}); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_export: |
||||
|
export(); |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private void refresh() { |
||||
|
updateAdapter(); |
||||
|
} |
||||
|
|
||||
|
private void cleanup() { |
||||
|
new AsyncTask<Object, Object, Object>() { |
||||
|
@Override |
||||
|
protected Long doInBackground(Object... objects) { |
||||
|
Log.i(TAG, "Cleanup DNS"); |
||||
|
DatabaseHelper.getInstance(ActivityDns.this).cleanupDns(); |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(Object result) { |
||||
|
ServiceSinkhole.reload("DNS cleanup", ActivityDns.this, false); |
||||
|
updateAdapter(); |
||||
|
} |
||||
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
} |
||||
|
|
||||
|
private void clear() { |
||||
|
new AsyncTask<Object, Object, Object>() { |
||||
|
@Override |
||||
|
protected Long doInBackground(Object... objects) { |
||||
|
Log.i(TAG, "Clear DNS"); |
||||
|
DatabaseHelper.getInstance(ActivityDns.this).clearDns(); |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(Object result) { |
||||
|
ServiceSinkhole.reload("DNS clear", ActivityDns.this, false); |
||||
|
updateAdapter(); |
||||
|
} |
||||
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
} |
||||
|
|
||||
|
private void export() { |
||||
|
startActivityForResult(getIntentExport(), REQUEST_EXPORT); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
||||
|
super.onActivityResult(requestCode, resultCode, data); |
||||
|
Log.i(TAG, "onActivityResult request=" + requestCode + " result=" + requestCode + " ok=" + (resultCode == RESULT_OK)); |
||||
|
if (requestCode == REQUEST_EXPORT) { |
||||
|
if (resultCode == RESULT_OK && data != null) |
||||
|
handleExport(data); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Intent getIntentExport() { |
||||
|
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); |
||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE); |
||||
|
intent.setType("*/*"); // text/xml |
||||
|
intent.putExtra(Intent.EXTRA_TITLE, "netguard_dns_" + new SimpleDateFormat("yyyyMMdd").format(new Date().getTime()) + ".xml"); |
||||
|
return intent; |
||||
|
} |
||||
|
|
||||
|
private void handleExport(final Intent data) { |
||||
|
new AsyncTask<Object, Object, Throwable>() { |
||||
|
@Override |
||||
|
protected Throwable doInBackground(Object... objects) { |
||||
|
OutputStream out = null; |
||||
|
try { |
||||
|
Uri target = data.getData(); |
||||
|
Log.i(TAG, "Writing URI=" + target); |
||||
|
out = getContentResolver().openOutputStream(target); |
||||
|
xmlExport(out); |
||||
|
return null; |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
return ex; |
||||
|
} finally { |
||||
|
if (out != null) |
||||
|
try { |
||||
|
out.close(); |
||||
|
} catch (IOException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(Throwable ex) { |
||||
|
if (running) { |
||||
|
if (ex == null) |
||||
|
Toast.makeText(ActivityDns.this, R.string.msg_completed, Toast.LENGTH_LONG).show(); |
||||
|
else |
||||
|
Toast.makeText(ActivityDns.this, ex.toString(), Toast.LENGTH_LONG).show(); |
||||
|
} |
||||
|
} |
||||
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
} |
||||
|
|
||||
|
private void xmlExport(OutputStream out) throws IOException { |
||||
|
XmlSerializer serializer = Xml.newSerializer(); |
||||
|
serializer.setOutput(out, "UTF-8"); |
||||
|
serializer.startDocument(null, true); |
||||
|
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); |
||||
|
serializer.startTag(null, "netguard"); |
||||
|
|
||||
|
DateFormat df = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss Z", Locale.US); // RFC 822 |
||||
|
|
||||
|
try (Cursor cursor = DatabaseHelper.getInstance(this).getDns()) { |
||||
|
int colTime = cursor.getColumnIndex("time"); |
||||
|
int colQName = cursor.getColumnIndex("qname"); |
||||
|
int colAName = cursor.getColumnIndex("aname"); |
||||
|
int colResource = cursor.getColumnIndex("resource"); |
||||
|
int colTTL = cursor.getColumnIndex("ttl"); |
||||
|
while (cursor.moveToNext()) { |
||||
|
long time = cursor.getLong(colTime); |
||||
|
String qname = cursor.getString(colQName); |
||||
|
String aname = cursor.getString(colAName); |
||||
|
String resource = cursor.getString(colResource); |
||||
|
int ttl = cursor.getInt(colTTL); |
||||
|
|
||||
|
serializer.startTag(null, "dns"); |
||||
|
serializer.attribute(null, "time", df.format(time)); |
||||
|
serializer.attribute(null, "qname", qname); |
||||
|
serializer.attribute(null, "aname", aname); |
||||
|
serializer.attribute(null, "resource", resource); |
||||
|
serializer.attribute(null, "ttl", Integer.toString(ttl)); |
||||
|
serializer.endTag(null, "dns"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
serializer.endTag(null, "netguard"); |
||||
|
serializer.endDocument(); |
||||
|
serializer.flush(); |
||||
|
} |
||||
|
|
||||
|
private void updateAdapter() { |
||||
|
if (adapter != null) |
||||
|
adapter.changeCursor(DatabaseHelper.getInstance(this).getDns()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onDestroy() { |
||||
|
running = false; |
||||
|
adapter = null; |
||||
|
super.onDestroy(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,130 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.app.Activity; |
||||
|
import android.os.Bundle; |
||||
|
import android.text.TextUtils; |
||||
|
import android.util.Log; |
||||
|
import android.view.View; |
||||
|
import android.widget.Button; |
||||
|
import android.widget.TextView; |
||||
|
|
||||
|
import java.net.InetAddress; |
||||
|
|
||||
|
public class ActivityForwardApproval extends Activity { |
||||
|
private static final String TAG = "NetGuard.Forward"; |
||||
|
private static final String ACTION_START_PORT_FORWARD = "eu.faircode.netguard.START_PORT_FORWARD"; |
||||
|
private static final String ACTION_STOP_PORT_FORWARD = "eu.faircode.netguard.STOP_PORT_FORWARD"; |
||||
|
|
||||
|
static { |
||||
|
try { |
||||
|
System.loadLibrary("netguard"); |
||||
|
} catch (UnsatisfiedLinkError ignored) { |
||||
|
System.exit(1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
setContentView(R.layout.forwardapproval); |
||||
|
|
||||
|
final int protocol = getIntent().getIntExtra("protocol", 0); |
||||
|
final int dport = getIntent().getIntExtra("dport", 0); |
||||
|
String addr = getIntent().getStringExtra("raddr"); |
||||
|
final int rport = getIntent().getIntExtra("rport", 0); |
||||
|
final int ruid = getIntent().getIntExtra("ruid", 0); |
||||
|
final String raddr = (addr == null ? "127.0.0.1" : addr); |
||||
|
|
||||
|
try { |
||||
|
InetAddress iraddr = InetAddress.getByName(raddr); |
||||
|
if (rport < 1024 && (iraddr.isLoopbackAddress() || iraddr.isAnyLocalAddress())) |
||||
|
throw new IllegalArgumentException("Port forwarding to privileged port on local address not possible"); |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
finish(); |
||||
|
} |
||||
|
|
||||
|
String pname; |
||||
|
if (protocol == 6) |
||||
|
pname = getString(R.string.menu_protocol_tcp); |
||||
|
else if (protocol == 17) |
||||
|
pname = getString(R.string.menu_protocol_udp); |
||||
|
else |
||||
|
pname = Integer.toString(protocol); |
||||
|
|
||||
|
TextView tvForward = findViewById(R.id.tvForward); |
||||
|
if (ACTION_START_PORT_FORWARD.equals(getIntent().getAction())) |
||||
|
tvForward.setText(getString(R.string.msg_start_forward, |
||||
|
pname, dport, raddr, rport, |
||||
|
TextUtils.join(", ", Util.getApplicationNames(ruid, this)))); |
||||
|
else |
||||
|
tvForward.setText(getString(R.string.msg_stop_forward, pname, dport)); |
||||
|
|
||||
|
Button btnOk = findViewById(R.id.btnOk); |
||||
|
Button btnCancel = findViewById(R.id.btnCancel); |
||||
|
|
||||
|
btnOk.setOnClickListener(new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
if (ACTION_START_PORT_FORWARD.equals(getIntent().getAction())) { |
||||
|
/* |
||||
|
am start -a eu.faircode.netguard.START_PORT_FORWARD \ |
||||
|
-n eu.faircode.netguard/eu.faircode.netguard.ActivityForwardApproval \ |
||||
|
--ei protocol 17 \ |
||||
|
--ei dport 53 \ |
||||
|
--es raddr 8.8.4.4 \ |
||||
|
--ei rport 53 \ |
||||
|
--ei ruid 9999 \ |
||||
|
--user 0 |
||||
|
*/ |
||||
|
Log.i(TAG, "Start forwarding protocol " + protocol + " port " + dport + " to " + raddr + "/" + rport + " uid " + ruid); |
||||
|
DatabaseHelper dh = DatabaseHelper.getInstance(ActivityForwardApproval.this); |
||||
|
dh.deleteForward(protocol, dport); |
||||
|
dh.addForward(protocol, dport, raddr, rport, ruid); |
||||
|
|
||||
|
} else if (ACTION_STOP_PORT_FORWARD.equals(getIntent().getAction())) { |
||||
|
/* |
||||
|
am start -a eu.faircode.netguard.STOP_PORT_FORWARD \ |
||||
|
-n eu.faircode.netguard/eu.faircode.netguard.ActivityForwardApproval \ |
||||
|
--ei protocol 17 \ |
||||
|
--ei dport 53 \ |
||||
|
--user 0 |
||||
|
*/ |
||||
|
Log.i(TAG, "Stop forwarding protocol " + protocol + " port " + dport); |
||||
|
DatabaseHelper.getInstance(ActivityForwardApproval.this).deleteForward(protocol, dport); |
||||
|
} |
||||
|
|
||||
|
ServiceSinkhole.reload("forwarding", ActivityForwardApproval.this, false); |
||||
|
|
||||
|
finish(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
btnCancel.setOnClickListener(new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
finish(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,251 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.DialogInterface; |
||||
|
import android.database.Cursor; |
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Bundle; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.Menu; |
||||
|
import android.view.MenuInflater; |
||||
|
import android.view.MenuItem; |
||||
|
import android.view.View; |
||||
|
import android.widget.AdapterView; |
||||
|
import android.widget.ArrayAdapter; |
||||
|
import android.widget.EditText; |
||||
|
import android.widget.ListView; |
||||
|
import android.widget.PopupMenu; |
||||
|
import android.widget.ProgressBar; |
||||
|
import android.widget.Spinner; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.appcompat.app.AlertDialog; |
||||
|
import androidx.appcompat.app.AppCompatActivity; |
||||
|
|
||||
|
import java.net.InetAddress; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class ActivityForwarding extends AppCompatActivity { |
||||
|
private boolean running; |
||||
|
private ListView lvForwarding; |
||||
|
private AdapterForwarding adapter; |
||||
|
private AlertDialog dialog = null; |
||||
|
|
||||
|
private DatabaseHelper.ForwardChangedListener listener = new DatabaseHelper.ForwardChangedListener() { |
||||
|
@Override |
||||
|
public void onChanged() { |
||||
|
runOnUiThread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
if (adapter != null) |
||||
|
adapter.changeCursor(DatabaseHelper.getInstance(ActivityForwarding.this).getForwarding()); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
@Override |
||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||
|
Util.setTheme(this); |
||||
|
super.onCreate(savedInstanceState); |
||||
|
setContentView(R.layout.forwarding); |
||||
|
running = true; |
||||
|
|
||||
|
getSupportActionBar().setTitle(R.string.setting_forwarding); |
||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
||||
|
|
||||
|
|
||||
|
lvForwarding = findViewById(R.id.lvForwarding); |
||||
|
adapter = new AdapterForwarding(this, DatabaseHelper.getInstance(this).getForwarding()); |
||||
|
lvForwarding.setAdapter(adapter); |
||||
|
|
||||
|
lvForwarding.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
||||
|
@Override |
||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
||||
|
Cursor cursor = (Cursor) adapter.getItem(position); |
||||
|
final int protocol = cursor.getInt(cursor.getColumnIndex("protocol")); |
||||
|
final int dport = cursor.getInt(cursor.getColumnIndex("dport")); |
||||
|
final String raddr = cursor.getString(cursor.getColumnIndex("raddr")); |
||||
|
final int rport = cursor.getInt(cursor.getColumnIndex("rport")); |
||||
|
|
||||
|
PopupMenu popup = new PopupMenu(ActivityForwarding.this, view); |
||||
|
popup.inflate(R.menu.forward); |
||||
|
popup.getMenu().findItem(R.id.menu_port).setTitle( |
||||
|
Util.getProtocolName(protocol, 0, false) + " " + |
||||
|
dport + " > " + raddr + "/" + rport); |
||||
|
|
||||
|
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { |
||||
|
@Override |
||||
|
public boolean onMenuItemClick(MenuItem menuItem) { |
||||
|
if (menuItem.getItemId() == R.id.menu_delete) { |
||||
|
DatabaseHelper.getInstance(ActivityForwarding.this).deleteForward(protocol, dport); |
||||
|
ServiceSinkhole.reload("forwarding", ActivityForwarding.this, false); |
||||
|
adapter = new AdapterForwarding(ActivityForwarding.this, |
||||
|
DatabaseHelper.getInstance(ActivityForwarding.this).getForwarding()); |
||||
|
lvForwarding.setAdapter(adapter); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
popup.show(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onResume() { |
||||
|
super.onResume(); |
||||
|
DatabaseHelper.getInstance(this).addForwardChangedListener(listener); |
||||
|
if (adapter != null) |
||||
|
adapter.changeCursor(DatabaseHelper.getInstance(ActivityForwarding.this).getForwarding()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPause() { |
||||
|
super.onPause(); |
||||
|
DatabaseHelper.getInstance(this).removeForwardChangedListener(listener); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onDestroy() { |
||||
|
running = false; |
||||
|
adapter = null; |
||||
|
if (dialog != null) { |
||||
|
dialog.dismiss(); |
||||
|
dialog = null; |
||||
|
} |
||||
|
super.onDestroy(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onCreateOptionsMenu(Menu menu) { |
||||
|
MenuInflater inflater = getMenuInflater(); |
||||
|
inflater.inflate(R.menu.forwarding, menu); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onOptionsItemSelected(MenuItem item) { |
||||
|
switch (item.getItemId()) { |
||||
|
case R.id.menu_add: |
||||
|
LayoutInflater inflater = LayoutInflater.from(this); |
||||
|
View view = inflater.inflate(R.layout.forwardadd, null, false); |
||||
|
final Spinner spProtocol = view.findViewById(R.id.spProtocol); |
||||
|
final EditText etDPort = view.findViewById(R.id.etDPort); |
||||
|
final EditText etRAddr = view.findViewById(R.id.etRAddr); |
||||
|
final EditText etRPort = view.findViewById(R.id.etRPort); |
||||
|
final ProgressBar pbRuid = view.findViewById(R.id.pbRUid); |
||||
|
final Spinner spRuid = view.findViewById(R.id.spRUid); |
||||
|
|
||||
|
final AsyncTask task = new AsyncTask<Object, Object, List<Rule>>() { |
||||
|
@Override |
||||
|
protected void onPreExecute() { |
||||
|
pbRuid.setVisibility(View.VISIBLE); |
||||
|
spRuid.setVisibility(View.GONE); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected List<Rule> doInBackground(Object... objects) { |
||||
|
return Rule.getRules(true, ActivityForwarding.this); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(List<Rule> rules) { |
||||
|
ArrayAdapter spinnerArrayAdapter = |
||||
|
new ArrayAdapter(ActivityForwarding.this, |
||||
|
android.R.layout.simple_spinner_item, rules); |
||||
|
spRuid.setAdapter(spinnerArrayAdapter); |
||||
|
pbRuid.setVisibility(View.GONE); |
||||
|
spRuid.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
}; |
||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
|
||||
|
dialog = new AlertDialog.Builder(this) |
||||
|
.setView(view) |
||||
|
.setCancelable(true) |
||||
|
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(DialogInterface dialog, int which) { |
||||
|
try { |
||||
|
int pos = spProtocol.getSelectedItemPosition(); |
||||
|
String[] values = getResources().getStringArray(R.array.protocolValues); |
||||
|
final int protocol = Integer.valueOf(values[pos]); |
||||
|
final int dport = Integer.parseInt(etDPort.getText().toString()); |
||||
|
final String raddr = etRAddr.getText().toString(); |
||||
|
final int rport = Integer.parseInt(etRPort.getText().toString()); |
||||
|
final int ruid = ((Rule) spRuid.getSelectedItem()).uid; |
||||
|
|
||||
|
InetAddress iraddr = InetAddress.getByName(raddr); |
||||
|
if (rport < 1024 && (iraddr.isLoopbackAddress() || iraddr.isAnyLocalAddress())) |
||||
|
throw new IllegalArgumentException("Port forwarding to privileged port on local address not possible"); |
||||
|
|
||||
|
new AsyncTask<Object, Object, Throwable>() { |
||||
|
@Override |
||||
|
protected Throwable doInBackground(Object... objects) { |
||||
|
try { |
||||
|
DatabaseHelper.getInstance(ActivityForwarding.this) |
||||
|
.addForward(protocol, dport, raddr, rport, ruid); |
||||
|
return null; |
||||
|
} catch (Throwable ex) { |
||||
|
return ex; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(Throwable ex) { |
||||
|
if (running) |
||||
|
if (ex == null) { |
||||
|
ServiceSinkhole.reload("forwarding", ActivityForwarding.this, false); |
||||
|
adapter = new AdapterForwarding(ActivityForwarding.this, |
||||
|
DatabaseHelper.getInstance(ActivityForwarding.this).getForwarding()); |
||||
|
lvForwarding.setAdapter(adapter); |
||||
|
} else |
||||
|
Toast.makeText(ActivityForwarding.this, ex.toString(), Toast.LENGTH_LONG).show(); |
||||
|
} |
||||
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
} catch (Throwable ex) { |
||||
|
Toast.makeText(ActivityForwarding.this, ex.toString(), Toast.LENGTH_LONG).show(); |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(DialogInterface dialog, int which) { |
||||
|
task.cancel(false); |
||||
|
dialog.dismiss(); |
||||
|
} |
||||
|
}) |
||||
|
.setOnDismissListener(new DialogInterface.OnDismissListener() { |
||||
|
@Override |
||||
|
public void onDismiss(DialogInterface dialogInterface) { |
||||
|
dialog = null; |
||||
|
} |
||||
|
}) |
||||
|
.create(); |
||||
|
dialog.show(); |
||||
|
return true; |
||||
|
default: |
||||
|
return super.onOptionsItemSelected(item); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,643 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.ClipData; |
||||
|
import android.content.ClipboardManager; |
||||
|
import android.content.Intent; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.content.pm.PackageManager; |
||||
|
import android.database.Cursor; |
||||
|
import android.net.Uri; |
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Build; |
||||
|
import android.os.Bundle; |
||||
|
import android.text.TextUtils; |
||||
|
import android.util.Log; |
||||
|
import android.view.Menu; |
||||
|
import android.view.MenuInflater; |
||||
|
import android.view.MenuItem; |
||||
|
import android.view.View; |
||||
|
import android.widget.AdapterView; |
||||
|
import android.widget.CompoundButton; |
||||
|
import android.widget.FilterQueryProvider; |
||||
|
import android.widget.ListView; |
||||
|
import android.widget.PopupMenu; |
||||
|
import android.widget.TextView; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.appcompat.app.AppCompatActivity; |
||||
|
import androidx.appcompat.widget.SearchView; |
||||
|
import androidx.appcompat.widget.SwitchCompat; |
||||
|
import androidx.core.app.NavUtils; |
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FileInputStream; |
||||
|
import java.io.IOException; |
||||
|
import java.io.OutputStream; |
||||
|
import java.net.InetAddress; |
||||
|
import java.net.UnknownHostException; |
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
public class ActivityLog extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener { |
||||
|
private static final String TAG = "NetGuard.Log"; |
||||
|
|
||||
|
private boolean running = false; |
||||
|
private ListView lvLog; |
||||
|
private AdapterLog adapter; |
||||
|
private MenuItem menuSearch = null; |
||||
|
|
||||
|
private boolean live; |
||||
|
private boolean resolve; |
||||
|
private boolean organization; |
||||
|
private InetAddress vpn4 = null; |
||||
|
private InetAddress vpn6 = null; |
||||
|
|
||||
|
private static final int REQUEST_PCAP = 1; |
||||
|
|
||||
|
private DatabaseHelper.LogChangedListener listener = new DatabaseHelper.LogChangedListener() { |
||||
|
@Override |
||||
|
public void onChanged() { |
||||
|
runOnUiThread(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
updateAdapter(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
@Override |
||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||
|
if (!IAB.isPurchased(ActivityPro.SKU_LOG, this)) { |
||||
|
startActivity(new Intent(this, ActivityPro.class)); |
||||
|
finish(); |
||||
|
} |
||||
|
|
||||
|
Util.setTheme(this); |
||||
|
super.onCreate(savedInstanceState); |
||||
|
setContentView(R.layout.logging); |
||||
|
running = true; |
||||
|
|
||||
|
// Action bar |
||||
|
View actionView = getLayoutInflater().inflate(R.layout.actionlog, null, false); |
||||
|
SwitchCompat swEnabled = actionView.findViewById(R.id.swEnabled); |
||||
|
|
||||
|
getSupportActionBar().setDisplayShowCustomEnabled(true); |
||||
|
getSupportActionBar().setCustomView(actionView); |
||||
|
|
||||
|
getSupportActionBar().setTitle(R.string.menu_log); |
||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
||||
|
|
||||
|
// Get settings |
||||
|
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
resolve = prefs.getBoolean("resolve", false); |
||||
|
organization = prefs.getBoolean("organization", false); |
||||
|
boolean log = prefs.getBoolean("log", false); |
||||
|
|
||||
|
// Show disabled message |
||||
|
TextView tvDisabled = findViewById(R.id.tvDisabled); |
||||
|
tvDisabled.setVisibility(log ? View.GONE : View.VISIBLE); |
||||
|
|
||||
|
// Set enabled switch |
||||
|
swEnabled.setChecked(log); |
||||
|
swEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { |
||||
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { |
||||
|
prefs.edit().putBoolean("log", isChecked).apply(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Listen for preference changes |
||||
|
prefs.registerOnSharedPreferenceChangeListener(this); |
||||
|
|
||||
|
lvLog = findViewById(R.id.lvLog); |
||||
|
|
||||
|
boolean udp = prefs.getBoolean("proto_udp", true); |
||||
|
boolean tcp = prefs.getBoolean("proto_tcp", true); |
||||
|
boolean other = prefs.getBoolean("proto_other", true); |
||||
|
boolean allowed = prefs.getBoolean("traffic_allowed", true); |
||||
|
boolean blocked = prefs.getBoolean("traffic_blocked", true); |
||||
|
|
||||
|
adapter = new AdapterLog(this, DatabaseHelper.getInstance(this).getLog(udp, tcp, other, allowed, blocked), resolve, organization); |
||||
|
adapter.setFilterQueryProvider(new FilterQueryProvider() { |
||||
|
public Cursor runQuery(CharSequence constraint) { |
||||
|
return DatabaseHelper.getInstance(ActivityLog.this).searchLog(constraint.toString()); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
lvLog.setAdapter(adapter); |
||||
|
|
||||
|
try { |
||||
|
vpn4 = InetAddress.getByName(prefs.getString("vpn4", "10.1.10.1")); |
||||
|
vpn6 = InetAddress.getByName(prefs.getString("vpn6", "fd00:1:fd00:1:fd00:1:fd00:1")); |
||||
|
} catch (UnknownHostException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
|
||||
|
lvLog.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
||||
|
@Override |
||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
||||
|
PackageManager pm = getPackageManager(); |
||||
|
Cursor cursor = (Cursor) adapter.getItem(position); |
||||
|
long time = cursor.getLong(cursor.getColumnIndex("time")); |
||||
|
int version = cursor.getInt(cursor.getColumnIndex("version")); |
||||
|
int protocol = cursor.getInt(cursor.getColumnIndex("protocol")); |
||||
|
final String saddr = cursor.getString(cursor.getColumnIndex("saddr")); |
||||
|
final int sport = (cursor.isNull(cursor.getColumnIndex("sport")) ? -1 : cursor.getInt(cursor.getColumnIndex("sport"))); |
||||
|
final String daddr = cursor.getString(cursor.getColumnIndex("daddr")); |
||||
|
final int dport = (cursor.isNull(cursor.getColumnIndex("dport")) ? -1 : cursor.getInt(cursor.getColumnIndex("dport"))); |
||||
|
final String dname = cursor.getString(cursor.getColumnIndex("dname")); |
||||
|
final int uid = (cursor.isNull(cursor.getColumnIndex("uid")) ? -1 : cursor.getInt(cursor.getColumnIndex("uid"))); |
||||
|
int allowed = (cursor.isNull(cursor.getColumnIndex("allowed")) ? -1 : cursor.getInt(cursor.getColumnIndex("allowed"))); |
||||
|
|
||||
|
// Get external address |
||||
|
InetAddress addr = null; |
||||
|
try { |
||||
|
addr = InetAddress.getByName(daddr); |
||||
|
} catch (UnknownHostException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
|
||||
|
String ip; |
||||
|
int port; |
||||
|
if (addr.equals(vpn4) || addr.equals(vpn6)) { |
||||
|
ip = saddr; |
||||
|
port = sport; |
||||
|
} else { |
||||
|
ip = daddr; |
||||
|
port = dport; |
||||
|
} |
||||
|
|
||||
|
// Build popup menu |
||||
|
PopupMenu popup = new PopupMenu(ActivityLog.this, findViewById(R.id.vwPopupAnchor)); |
||||
|
popup.inflate(R.menu.log); |
||||
|
|
||||
|
// Application name |
||||
|
if (uid >= 0) |
||||
|
popup.getMenu().findItem(R.id.menu_application).setTitle(TextUtils.join(", ", Util.getApplicationNames(uid, ActivityLog.this))); |
||||
|
else |
||||
|
popup.getMenu().removeItem(R.id.menu_application); |
||||
|
|
||||
|
// Destination IP |
||||
|
popup.getMenu().findItem(R.id.menu_protocol).setTitle(Util.getProtocolName(protocol, version, false)); |
||||
|
|
||||
|
// Whois |
||||
|
final Intent lookupIP = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.dnslytics.com/whois-lookup/" + ip)); |
||||
|
if (pm.resolveActivity(lookupIP, 0) == null) |
||||
|
popup.getMenu().removeItem(R.id.menu_whois); |
||||
|
else |
||||
|
popup.getMenu().findItem(R.id.menu_whois).setTitle(getString(R.string.title_log_whois, ip)); |
||||
|
|
||||
|
// Lookup port |
||||
|
final Intent lookupPort = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.speedguide.net/port.php?port=" + port)); |
||||
|
if (port <= 0 || pm.resolveActivity(lookupPort, 0) == null) |
||||
|
popup.getMenu().removeItem(R.id.menu_port); |
||||
|
else |
||||
|
popup.getMenu().findItem(R.id.menu_port).setTitle(getString(R.string.title_log_port, port)); |
||||
|
|
||||
|
if (prefs.getBoolean("filter", false)) { |
||||
|
if (uid <= 0) { |
||||
|
popup.getMenu().removeItem(R.id.menu_allow); |
||||
|
popup.getMenu().removeItem(R.id.menu_block); |
||||
|
} |
||||
|
} else { |
||||
|
popup.getMenu().removeItem(R.id.menu_allow); |
||||
|
popup.getMenu().removeItem(R.id.menu_block); |
||||
|
} |
||||
|
|
||||
|
final Packet packet = new Packet(); |
||||
|
packet.version = version; |
||||
|
packet.protocol = protocol; |
||||
|
packet.daddr = daddr; |
||||
|
packet.dport = dport; |
||||
|
packet.time = time; |
||||
|
packet.uid = uid; |
||||
|
packet.allowed = (allowed > 0); |
||||
|
|
||||
|
// Time |
||||
|
popup.getMenu().findItem(R.id.menu_time).setTitle(SimpleDateFormat.getDateTimeInstance().format(time)); |
||||
|
|
||||
|
// Handle click |
||||
|
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { |
||||
|
@Override |
||||
|
public boolean onMenuItemClick(MenuItem menuItem) { |
||||
|
switch (menuItem.getItemId()) { |
||||
|
case R.id.menu_application: { |
||||
|
Intent main = new Intent(ActivityLog.this, ActivityMain.class); |
||||
|
main.putExtra(ActivityMain.EXTRA_SEARCH, Integer.toString(uid)); |
||||
|
startActivity(main); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
case R.id.menu_whois: |
||||
|
startActivity(lookupIP); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_port: |
||||
|
startActivity(lookupPort); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_allow: |
||||
|
if (IAB.isPurchased(ActivityPro.SKU_FILTER, ActivityLog.this)) { |
||||
|
DatabaseHelper.getInstance(ActivityLog.this).updateAccess(packet, dname, 0); |
||||
|
ServiceSinkhole.reload("allow host", ActivityLog.this, false); |
||||
|
Intent main = new Intent(ActivityLog.this, ActivityMain.class); |
||||
|
main.putExtra(ActivityMain.EXTRA_SEARCH, Integer.toString(uid)); |
||||
|
startActivity(main); |
||||
|
} else |
||||
|
startActivity(new Intent(ActivityLog.this, ActivityPro.class)); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_block: |
||||
|
if (IAB.isPurchased(ActivityPro.SKU_FILTER, ActivityLog.this)) { |
||||
|
DatabaseHelper.getInstance(ActivityLog.this).updateAccess(packet, dname, 1); |
||||
|
ServiceSinkhole.reload("block host", ActivityLog.this, false); |
||||
|
Intent main = new Intent(ActivityLog.this, ActivityMain.class); |
||||
|
main.putExtra(ActivityMain.EXTRA_SEARCH, Integer.toString(uid)); |
||||
|
startActivity(main); |
||||
|
} else |
||||
|
startActivity(new Intent(ActivityLog.this, ActivityPro.class)); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_copy: |
||||
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); |
||||
|
ClipData clip = ClipData.newPlainText("netguard", dname == null ? daddr : dname); |
||||
|
clipboard.setPrimaryClip(clip); |
||||
|
return true; |
||||
|
|
||||
|
default: |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Show |
||||
|
popup.show(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
live = true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onResume() { |
||||
|
super.onResume(); |
||||
|
if (live) { |
||||
|
DatabaseHelper.getInstance(this).addLogChangedListener(listener); |
||||
|
updateAdapter(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPause() { |
||||
|
super.onPause(); |
||||
|
if (live) |
||||
|
DatabaseHelper.getInstance(this).removeLogChangedListener(listener); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onDestroy() { |
||||
|
running = false; |
||||
|
adapter = null; |
||||
|
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this); |
||||
|
super.onDestroy(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onSharedPreferenceChanged(SharedPreferences prefs, String name) { |
||||
|
Log.i(TAG, "Preference " + name + "=" + prefs.getAll().get(name)); |
||||
|
if ("log".equals(name)) { |
||||
|
// Get enabled |
||||
|
boolean log = prefs.getBoolean(name, false); |
||||
|
|
||||
|
// Display disabled warning |
||||
|
TextView tvDisabled = findViewById(R.id.tvDisabled); |
||||
|
tvDisabled.setVisibility(log ? View.GONE : View.VISIBLE); |
||||
|
|
||||
|
// Check switch state |
||||
|
SwitchCompat swEnabled = getSupportActionBar().getCustomView().findViewById(R.id.swEnabled); |
||||
|
if (swEnabled.isChecked() != log) |
||||
|
swEnabled.setChecked(log); |
||||
|
|
||||
|
ServiceSinkhole.reload("changed " + name, ActivityLog.this, false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onCreateOptionsMenu(Menu menu) { |
||||
|
MenuInflater inflater = getMenuInflater(); |
||||
|
inflater.inflate(R.menu.logging, menu); |
||||
|
|
||||
|
menuSearch = menu.findItem(R.id.menu_search); |
||||
|
SearchView searchView = (SearchView) menuSearch.getActionView(); |
||||
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { |
||||
|
@Override |
||||
|
public boolean onQueryTextSubmit(String query) { |
||||
|
if (adapter != null) |
||||
|
adapter.getFilter().filter(getUidForName(query)); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onQueryTextChange(String newText) { |
||||
|
if (adapter != null) |
||||
|
adapter.getFilter().filter(getUidForName(newText)); |
||||
|
return true; |
||||
|
} |
||||
|
}); |
||||
|
searchView.setOnCloseListener(new SearchView.OnCloseListener() { |
||||
|
@Override |
||||
|
public boolean onClose() { |
||||
|
if (adapter != null) |
||||
|
adapter.getFilter().filter(null); |
||||
|
return true; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onPrepareOptionsMenu(Menu menu) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
|
||||
|
// https://gist.github.com/granoeste/5574148 |
||||
|
File pcap_file = new File(getDir("data", MODE_PRIVATE), "netguard.pcap"); |
||||
|
|
||||
|
boolean export = (getPackageManager().resolveActivity(getIntentPCAPDocument(), 0) != null); |
||||
|
|
||||
|
menu.findItem(R.id.menu_protocol_udp).setChecked(prefs.getBoolean("proto_udp", true)); |
||||
|
menu.findItem(R.id.menu_protocol_tcp).setChecked(prefs.getBoolean("proto_tcp", true)); |
||||
|
menu.findItem(R.id.menu_protocol_other).setChecked(prefs.getBoolean("proto_other", true)); |
||||
|
menu.findItem(R.id.menu_traffic_allowed).setEnabled(prefs.getBoolean("filter", false)); |
||||
|
menu.findItem(R.id.menu_traffic_allowed).setChecked(prefs.getBoolean("traffic_allowed", true)); |
||||
|
menu.findItem(R.id.menu_traffic_blocked).setChecked(prefs.getBoolean("traffic_blocked", true)); |
||||
|
|
||||
|
menu.findItem(R.id.menu_refresh).setEnabled(!menu.findItem(R.id.menu_log_live).isChecked()); |
||||
|
menu.findItem(R.id.menu_log_resolve).setChecked(prefs.getBoolean("resolve", false)); |
||||
|
menu.findItem(R.id.menu_log_organization).setChecked(prefs.getBoolean("organization", false)); |
||||
|
menu.findItem(R.id.menu_pcap_enabled).setChecked(prefs.getBoolean("pcap", false)); |
||||
|
menu.findItem(R.id.menu_pcap_export).setEnabled(pcap_file.exists() && export); |
||||
|
|
||||
|
return super.onPrepareOptionsMenu(menu); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onOptionsItemSelected(MenuItem item) { |
||||
|
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
final File pcap_file = new File(getDir("data", MODE_PRIVATE), "netguard.pcap"); |
||||
|
|
||||
|
switch (item.getItemId()) { |
||||
|
case android.R.id.home: |
||||
|
Log.i(TAG, "Up"); |
||||
|
NavUtils.navigateUpFromSameTask(this); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_protocol_udp: |
||||
|
item.setChecked(!item.isChecked()); |
||||
|
prefs.edit().putBoolean("proto_udp", item.isChecked()).apply(); |
||||
|
updateAdapter(); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_protocol_tcp: |
||||
|
item.setChecked(!item.isChecked()); |
||||
|
prefs.edit().putBoolean("proto_tcp", item.isChecked()).apply(); |
||||
|
updateAdapter(); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_protocol_other: |
||||
|
item.setChecked(!item.isChecked()); |
||||
|
prefs.edit().putBoolean("proto_other", item.isChecked()).apply(); |
||||
|
updateAdapter(); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_traffic_allowed: |
||||
|
item.setChecked(!item.isChecked()); |
||||
|
prefs.edit().putBoolean("traffic_allowed", item.isChecked()).apply(); |
||||
|
updateAdapter(); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_traffic_blocked: |
||||
|
item.setChecked(!item.isChecked()); |
||||
|
prefs.edit().putBoolean("traffic_blocked", item.isChecked()).apply(); |
||||
|
updateAdapter(); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_log_live: |
||||
|
item.setChecked(!item.isChecked()); |
||||
|
live = item.isChecked(); |
||||
|
if (live) { |
||||
|
DatabaseHelper.getInstance(this).addLogChangedListener(listener); |
||||
|
updateAdapter(); |
||||
|
} else |
||||
|
DatabaseHelper.getInstance(this).removeLogChangedListener(listener); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_refresh: |
||||
|
updateAdapter(); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_log_resolve: |
||||
|
item.setChecked(!item.isChecked()); |
||||
|
prefs.edit().putBoolean("resolve", item.isChecked()).apply(); |
||||
|
adapter.setResolve(item.isChecked()); |
||||
|
adapter.notifyDataSetChanged(); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_log_organization: |
||||
|
item.setChecked(!item.isChecked()); |
||||
|
prefs.edit().putBoolean("organization", item.isChecked()).apply(); |
||||
|
adapter.setOrganization(item.isChecked()); |
||||
|
adapter.notifyDataSetChanged(); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_pcap_enabled: |
||||
|
item.setChecked(!item.isChecked()); |
||||
|
prefs.edit().putBoolean("pcap", item.isChecked()).apply(); |
||||
|
ServiceSinkhole.setPcap(item.isChecked(), ActivityLog.this); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_pcap_export: |
||||
|
startActivityForResult(getIntentPCAPDocument(), REQUEST_PCAP); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_log_clear: |
||||
|
new AsyncTask<Object, Object, Object>() { |
||||
|
@Override |
||||
|
protected Object doInBackground(Object... objects) { |
||||
|
DatabaseHelper.getInstance(ActivityLog.this).clearLog(-1); |
||||
|
if (prefs.getBoolean("pcap", false)) { |
||||
|
ServiceSinkhole.setPcap(false, ActivityLog.this); |
||||
|
if (pcap_file.exists() && !pcap_file.delete()) |
||||
|
Log.w(TAG, "Delete PCAP failed"); |
||||
|
ServiceSinkhole.setPcap(true, ActivityLog.this); |
||||
|
} else { |
||||
|
if (pcap_file.exists() && !pcap_file.delete()) |
||||
|
Log.w(TAG, "Delete PCAP failed"); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(Object result) { |
||||
|
if (running) |
||||
|
updateAdapter(); |
||||
|
} |
||||
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
return true; |
||||
|
|
||||
|
case R.id.menu_log_support: |
||||
|
Intent intent = new Intent(Intent.ACTION_VIEW); |
||||
|
intent.setData(Uri.parse("https://github.com/M66B/NetGuard/blob/master/FAQ.md#user-content-faq27")); |
||||
|
if (getPackageManager().resolveActivity(intent, 0) != null) |
||||
|
startActivity(intent); |
||||
|
return true; |
||||
|
|
||||
|
default: |
||||
|
return super.onOptionsItemSelected(item); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void updateAdapter() { |
||||
|
if (adapter != null) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
boolean udp = prefs.getBoolean("proto_udp", true); |
||||
|
boolean tcp = prefs.getBoolean("proto_tcp", true); |
||||
|
boolean other = prefs.getBoolean("proto_other", true); |
||||
|
boolean allowed = prefs.getBoolean("traffic_allowed", true); |
||||
|
boolean blocked = prefs.getBoolean("traffic_blocked", true); |
||||
|
adapter.changeCursor(DatabaseHelper.getInstance(this).getLog(udp, tcp, other, allowed, blocked)); |
||||
|
if (menuSearch != null && menuSearch.isActionViewExpanded()) { |
||||
|
SearchView searchView = (SearchView) menuSearch.getActionView(); |
||||
|
adapter.getFilter().filter(getUidForName(searchView.getQuery().toString())); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private String getUidForName(String query) { |
||||
|
if (query != null && query.length() > 0) { |
||||
|
for (Rule rule : Rule.getRules(true, ActivityLog.this)) |
||||
|
if (rule.name != null && rule.name.toLowerCase().contains(query.toLowerCase())) { |
||||
|
String newQuery = Integer.toString(rule.uid); |
||||
|
Log.i(TAG, "Search " + query + " found " + rule.name + " new " + newQuery); |
||||
|
return newQuery; |
||||
|
} |
||||
|
Log.i(TAG, "Search " + query + " not found"); |
||||
|
} |
||||
|
return query; |
||||
|
} |
||||
|
|
||||
|
private Intent getIntentPCAPDocument() { |
||||
|
Intent intent; |
||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { |
||||
|
if (Util.isPackageInstalled("org.openintents.filemanager", this)) { |
||||
|
intent = new Intent("org.openintents.action.PICK_DIRECTORY"); |
||||
|
} else { |
||||
|
intent = new Intent(Intent.ACTION_VIEW); |
||||
|
intent.setData(Uri.parse("https://play.google.com/store/apps/details?id=org.openintents.filemanager")); |
||||
|
} |
||||
|
} else { |
||||
|
intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); |
||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE); |
||||
|
intent.setType("application/octet-stream"); |
||||
|
intent.putExtra(Intent.EXTRA_TITLE, "netguard_" + new SimpleDateFormat("yyyyMMdd").format(new Date().getTime()) + ".pcap"); |
||||
|
} |
||||
|
return intent; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onActivityResult(int requestCode, int resultCode, final Intent data) { |
||||
|
Log.i(TAG, "onActivityResult request=" + requestCode + " result=" + requestCode + " ok=" + (resultCode == RESULT_OK)); |
||||
|
|
||||
|
if (requestCode == REQUEST_PCAP) { |
||||
|
if (resultCode == RESULT_OK && data != null) |
||||
|
handleExportPCAP(data); |
||||
|
|
||||
|
} else { |
||||
|
Log.w(TAG, "Unknown activity result request=" + requestCode); |
||||
|
super.onActivityResult(requestCode, resultCode, data); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void handleExportPCAP(final Intent data) { |
||||
|
new AsyncTask<Object, Object, Throwable>() { |
||||
|
@Override |
||||
|
protected Throwable doInBackground(Object... objects) { |
||||
|
OutputStream out = null; |
||||
|
FileInputStream in = null; |
||||
|
try { |
||||
|
// Stop capture |
||||
|
ServiceSinkhole.setPcap(false, ActivityLog.this); |
||||
|
|
||||
|
Uri target = data.getData(); |
||||
|
if (data.hasExtra("org.openintents.extra.DIR_PATH")) |
||||
|
target = Uri.parse(target + "/netguard.pcap"); |
||||
|
Log.i(TAG, "Export PCAP URI=" + target); |
||||
|
out = getContentResolver().openOutputStream(target); |
||||
|
|
||||
|
File pcap = new File(getDir("data", MODE_PRIVATE), "netguard.pcap"); |
||||
|
in = new FileInputStream(pcap); |
||||
|
|
||||
|
int len; |
||||
|
long total = 0; |
||||
|
byte[] buf = new byte[4096]; |
||||
|
while ((len = in.read(buf)) > 0) { |
||||
|
out.write(buf, 0, len); |
||||
|
total += len; |
||||
|
} |
||||
|
Log.i(TAG, "Copied bytes=" + total); |
||||
|
|
||||
|
return null; |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
return ex; |
||||
|
} finally { |
||||
|
if (out != null) |
||||
|
try { |
||||
|
out.close(); |
||||
|
} catch (IOException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
if (in != null) |
||||
|
try { |
||||
|
in.close(); |
||||
|
} catch (IOException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
|
||||
|
// Resume capture |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ActivityLog.this); |
||||
|
if (prefs.getBoolean("pcap", false)) |
||||
|
ServiceSinkhole.setPcap(true, ActivityLog.this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(Throwable ex) { |
||||
|
if (ex == null) |
||||
|
Toast.makeText(ActivityLog.this, R.string.msg_completed, Toast.LENGTH_LONG).show(); |
||||
|
else |
||||
|
Toast.makeText(ActivityLog.this, ex.toString(), Toast.LENGTH_LONG).show(); |
||||
|
} |
||||
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
} |
||||
|
} |
1304
NetGuard/app/src/main/java/eu/faircode/netguard/ActivityMain.java
File diff suppressed because it is too large
View File
@ -0,0 +1,447 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.app.PendingIntent; |
||||
|
import android.content.ClipData; |
||||
|
import android.content.ClipboardManager; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.graphics.Paint; |
||||
|
import android.net.Uri; |
||||
|
import android.os.Build; |
||||
|
import android.os.Bundle; |
||||
|
import android.provider.Settings; |
||||
|
import android.text.Editable; |
||||
|
import android.text.TextWatcher; |
||||
|
import android.util.Log; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.Menu; |
||||
|
import android.view.MenuInflater; |
||||
|
import android.view.MenuItem; |
||||
|
import android.view.View; |
||||
|
import android.view.WindowManager; |
||||
|
import android.widget.Button; |
||||
|
import android.widget.EditText; |
||||
|
import android.widget.ImageButton; |
||||
|
import android.widget.TextView; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.appcompat.app.AlertDialog; |
||||
|
import androidx.appcompat.app.AppCompatActivity; |
||||
|
import androidx.core.app.NavUtils; |
||||
|
|
||||
|
import static android.content.ClipDescription.MIMETYPE_TEXT_PLAIN; |
||||
|
|
||||
|
public class ActivityPro extends AppCompatActivity { |
||||
|
private static final String TAG = "NetGuard.Pro"; |
||||
|
|
||||
|
private IAB iab; |
||||
|
|
||||
|
// adb shell pm clear com.android.vending |
||||
|
// android.test.purchased |
||||
|
|
||||
|
private static final int SKU_LOG_ID = 1; |
||||
|
private static final int SKU_FILTER_ID = 2; |
||||
|
private static final int SKU_NOTIFY_ID = 3; |
||||
|
private static final int SKU_SPEED_ID = 4; |
||||
|
private static final int SKU_THEME_ID = 5; |
||||
|
private static final int SKU_PRO1_ID = 6; |
||||
|
private static final int SKU_SUPPORT1_ID = 7; |
||||
|
private static final int SKU_SUPPORT2_ID = 8; |
||||
|
|
||||
|
public static final String SKU_LOG = "log"; |
||||
|
public static final String SKU_FILTER = "filter"; |
||||
|
public static final String SKU_NOTIFY = "notify"; |
||||
|
public static final String SKU_SPEED = "speed"; |
||||
|
public static final String SKU_THEME = "theme"; |
||||
|
public static final String SKU_PRO1 = "pro1"; |
||||
|
public static final String SKU_SUPPORT1 = "support1"; |
||||
|
public static final String SKU_SUPPORT2 = "support2"; |
||||
|
public static final String SKU_DONATION = "donation"; |
||||
|
|
||||
|
@Override |
||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||
|
Log.i(TAG, "Create"); |
||||
|
Util.setTheme(this); |
||||
|
super.onCreate(savedInstanceState); |
||||
|
setContentView(R.layout.pro); |
||||
|
|
||||
|
getSupportActionBar().setTitle(R.string.title_pro); |
||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
||||
|
|
||||
|
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); |
||||
|
|
||||
|
// Initial state |
||||
|
updateState(); |
||||
|
|
||||
|
TextView tvLogTitle = findViewById(R.id.tvLogTitle); |
||||
|
TextView tvFilterTitle = findViewById(R.id.tvFilterTitle); |
||||
|
TextView tvNotifyTitle = findViewById(R.id.tvNotifyTitle); |
||||
|
TextView tvSpeedTitle = findViewById(R.id.tvSpeedTitle); |
||||
|
TextView tvThemeTitle = findViewById(R.id.tvThemeTitle); |
||||
|
TextView tvAllTitle = findViewById(R.id.tvAllTitle); |
||||
|
TextView tvDev1Title = findViewById(R.id.tvDev1Title); |
||||
|
TextView tvDev2Title = findViewById(R.id.tvDev2Title); |
||||
|
|
||||
|
tvLogTitle.setPaintFlags(tvLogTitle.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); |
||||
|
tvFilterTitle.setPaintFlags(tvLogTitle.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); |
||||
|
tvNotifyTitle.setPaintFlags(tvLogTitle.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); |
||||
|
tvSpeedTitle.setPaintFlags(tvLogTitle.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); |
||||
|
tvThemeTitle.setPaintFlags(tvLogTitle.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); |
||||
|
tvAllTitle.setPaintFlags(tvLogTitle.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); |
||||
|
tvDev1Title.setPaintFlags(tvLogTitle.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); |
||||
|
tvDev2Title.setPaintFlags(tvLogTitle.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); |
||||
|
|
||||
|
View.OnClickListener listener = new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
String sku; |
||||
|
switch (view.getId()) { |
||||
|
case R.id.tvLogTitle: |
||||
|
sku = SKU_LOG; |
||||
|
break; |
||||
|
case R.id.tvFilterTitle: |
||||
|
sku = SKU_FILTER; |
||||
|
break; |
||||
|
case R.id.tvNotifyTitle: |
||||
|
sku = SKU_NOTIFY; |
||||
|
break; |
||||
|
case R.id.tvSpeedTitle: |
||||
|
sku = SKU_SPEED; |
||||
|
break; |
||||
|
case R.id.tvThemeTitle: |
||||
|
sku = SKU_THEME; |
||||
|
break; |
||||
|
case R.id.tvAllTitle: |
||||
|
sku = SKU_PRO1; |
||||
|
break; |
||||
|
case R.id.tvDev1Title: |
||||
|
sku = SKU_SUPPORT1; |
||||
|
break; |
||||
|
case R.id.tvDev2Title: |
||||
|
sku = SKU_SUPPORT2; |
||||
|
break; |
||||
|
default: |
||||
|
sku = SKU_PRO1; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
Intent intent = new Intent(Intent.ACTION_VIEW); |
||||
|
intent.setData(Uri.parse("http://www.netguard.me/#" + sku)); |
||||
|
if (intent.resolveActivity(getPackageManager()) != null) |
||||
|
startActivity(intent); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
tvLogTitle.setOnClickListener(listener); |
||||
|
tvFilterTitle.setOnClickListener(listener); |
||||
|
tvNotifyTitle.setOnClickListener(listener); |
||||
|
tvSpeedTitle.setOnClickListener(listener); |
||||
|
tvThemeTitle.setOnClickListener(listener); |
||||
|
tvAllTitle.setOnClickListener(listener); |
||||
|
tvDev1Title.setOnClickListener(listener); |
||||
|
tvDev2Title.setOnClickListener(listener); |
||||
|
|
||||
|
try { |
||||
|
iab = new IAB(new IAB.Delegate() { |
||||
|
@Override |
||||
|
public void onReady(final IAB iab) { |
||||
|
Log.i(TAG, "IAB ready"); |
||||
|
try { |
||||
|
iab.updatePurchases(); |
||||
|
updateState(); |
||||
|
|
||||
|
final Button btnLog = findViewById(R.id.btnLog); |
||||
|
final Button btnFilter = findViewById(R.id.btnFilter); |
||||
|
final Button btnNotify = findViewById(R.id.btnNotify); |
||||
|
final Button btnSpeed = findViewById(R.id.btnSpeed); |
||||
|
final Button btnTheme = findViewById(R.id.btnTheme); |
||||
|
final Button btnAll = findViewById(R.id.btnAll); |
||||
|
final Button btnDev1 = findViewById(R.id.btnDev1); |
||||
|
final Button btnDev2 = findViewById(R.id.btnDev2); |
||||
|
|
||||
|
View.OnClickListener listener = new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
try { |
||||
|
int id = 0; |
||||
|
PendingIntent pi = null; |
||||
|
if (view == btnLog) { |
||||
|
id = SKU_LOG_ID; |
||||
|
pi = iab.getBuyIntent(SKU_LOG, false); |
||||
|
} else if (view == btnFilter) { |
||||
|
id = SKU_FILTER_ID; |
||||
|
pi = iab.getBuyIntent(SKU_FILTER, false); |
||||
|
} else if (view == btnNotify) { |
||||
|
id = SKU_NOTIFY_ID; |
||||
|
pi = iab.getBuyIntent(SKU_NOTIFY, false); |
||||
|
} else if (view == btnSpeed) { |
||||
|
id = SKU_SPEED_ID; |
||||
|
pi = iab.getBuyIntent(SKU_SPEED, false); |
||||
|
} else if (view == btnTheme) { |
||||
|
id = SKU_THEME_ID; |
||||
|
pi = iab.getBuyIntent(SKU_THEME, false); |
||||
|
} else if (view == btnAll) { |
||||
|
id = SKU_PRO1_ID; |
||||
|
pi = iab.getBuyIntent(SKU_PRO1, false); |
||||
|
} else if (view == btnDev1) { |
||||
|
id = SKU_SUPPORT1_ID; |
||||
|
pi = iab.getBuyIntent(SKU_SUPPORT1, true); |
||||
|
} else if (view == btnDev2) { |
||||
|
id = SKU_SUPPORT2_ID; |
||||
|
pi = iab.getBuyIntent(SKU_SUPPORT2, true); |
||||
|
} |
||||
|
|
||||
|
if (id > 0 && pi != null) |
||||
|
startIntentSenderForResult(pi.getIntentSender(), id, new Intent(), 0, 0, 0); |
||||
|
} catch (Throwable ex) { |
||||
|
Log.i(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
btnLog.setOnClickListener(listener); |
||||
|
btnFilter.setOnClickListener(listener); |
||||
|
btnNotify.setOnClickListener(listener); |
||||
|
btnSpeed.setOnClickListener(listener); |
||||
|
btnTheme.setOnClickListener(listener); |
||||
|
btnAll.setOnClickListener(listener); |
||||
|
btnDev1.setOnClickListener(listener); |
||||
|
btnDev2.setOnClickListener(listener); |
||||
|
|
||||
|
btnLog.setEnabled(true); |
||||
|
btnFilter.setEnabled(true); |
||||
|
btnNotify.setEnabled(true); |
||||
|
btnSpeed.setEnabled(true); |
||||
|
btnTheme.setEnabled(true); |
||||
|
btnAll.setEnabled(true); |
||||
|
btnDev1.setEnabled(true); |
||||
|
btnDev2.setEnabled(true); |
||||
|
|
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} |
||||
|
}, this); |
||||
|
iab.bind(); |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onDestroy() { |
||||
|
Log.i(TAG, "Destroy"); |
||||
|
iab.unbind(); |
||||
|
iab = null; |
||||
|
super.onDestroy(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onCreateOptionsMenu(Menu menu) { |
||||
|
MenuInflater inflater = getMenuInflater(); |
||||
|
inflater.inflate(R.menu.pro, menu); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onOptionsItemSelected(MenuItem item) { |
||||
|
switch (item.getItemId()) { |
||||
|
case android.R.id.home: |
||||
|
Log.i(TAG, "Up"); |
||||
|
NavUtils.navigateUpFromSameTask(this); |
||||
|
return true; |
||||
|
case R.id.menu_challenge: |
||||
|
menu_challenge(); |
||||
|
return true; |
||||
|
default: |
||||
|
return super.onOptionsItemSelected(item); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onPrepareOptionsMenu(Menu menu) { |
||||
|
if (IAB.isPurchased(SKU_DONATION, this) || Util.isPlayStoreInstall(this)) |
||||
|
menu.removeItem(R.id.menu_challenge); |
||||
|
|
||||
|
return super.onPrepareOptionsMenu(menu); |
||||
|
} |
||||
|
|
||||
|
private void menu_challenge() { |
||||
|
LayoutInflater inflater = LayoutInflater.from(this); |
||||
|
View view = inflater.inflate(R.layout.challenge, null, false); |
||||
|
|
||||
|
final AlertDialog dialog = new AlertDialog.Builder(this) |
||||
|
.setView(view) |
||||
|
.setCancelable(true) |
||||
|
.create(); |
||||
|
|
||||
|
String android_id = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID); |
||||
|
final String challenge = (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? Build.SERIAL : "O3" + android_id); |
||||
|
String seed = (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? "NetGuard2" : "NetGuard3"); |
||||
|
|
||||
|
// Challenge |
||||
|
TextView tvChallenge = view.findViewById(R.id.tvChallenge); |
||||
|
tvChallenge.setText(challenge); |
||||
|
|
||||
|
ImageButton ibCopy = view.findViewById(R.id.ibCopy); |
||||
|
ibCopy.setOnClickListener(new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); |
||||
|
ClipData clip = ClipData.newPlainText(getString(R.string.title_pro_challenge), challenge); |
||||
|
clipboard.setPrimaryClip(clip); |
||||
|
Toast.makeText(ActivityPro.this, android.R.string.copy, Toast.LENGTH_LONG).show(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Response |
||||
|
final EditText etResponse = view.findViewById(R.id.etResponse); |
||||
|
try { |
||||
|
final String response = Util.md5(challenge, seed); |
||||
|
etResponse.addTextChangedListener(new TextWatcher() { |
||||
|
@Override |
||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) { |
||||
|
// Do nothing |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) { |
||||
|
// Do nothing |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void afterTextChanged(Editable editable) { |
||||
|
if (response.equals(editable.toString().toUpperCase())) { |
||||
|
IAB.setBought(SKU_DONATION, ActivityPro.this); |
||||
|
dialog.dismiss(); |
||||
|
invalidateOptionsMenu(); |
||||
|
updateState(); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
|
||||
|
ImageButton ibPaste = view.findViewById(R.id.ibPaste); |
||||
|
ibPaste.setOnClickListener(new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); |
||||
|
if (clipboard != null && |
||||
|
clipboard.hasPrimaryClip() && |
||||
|
clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN)) { |
||||
|
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0); |
||||
|
etResponse.setText(item.getText().toString()); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
dialog.show(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
||||
|
super.onActivityResult(requestCode, resultCode, data); |
||||
|
if (resultCode == RESULT_OK) { |
||||
|
switch (requestCode) { |
||||
|
case SKU_LOG_ID: |
||||
|
IAB.setBought(SKU_LOG, this); |
||||
|
updateState(); |
||||
|
break; |
||||
|
case SKU_FILTER_ID: |
||||
|
IAB.setBought(SKU_FILTER, this); |
||||
|
updateState(); |
||||
|
break; |
||||
|
case SKU_NOTIFY_ID: |
||||
|
IAB.setBought(SKU_NOTIFY, this); |
||||
|
updateState(); |
||||
|
break; |
||||
|
case SKU_SPEED_ID: |
||||
|
IAB.setBought(SKU_SPEED, this); |
||||
|
updateState(); |
||||
|
break; |
||||
|
case SKU_THEME_ID: |
||||
|
IAB.setBought(SKU_THEME, this); |
||||
|
updateState(); |
||||
|
break; |
||||
|
case SKU_PRO1_ID: |
||||
|
IAB.setBought(SKU_PRO1, this); |
||||
|
updateState(); |
||||
|
break; |
||||
|
case SKU_SUPPORT1_ID: |
||||
|
IAB.setBought(SKU_SUPPORT1, this); |
||||
|
updateState(); |
||||
|
break; |
||||
|
case SKU_SUPPORT2_ID: |
||||
|
IAB.setBought(SKU_SUPPORT2, this); |
||||
|
updateState(); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void updateState() { |
||||
|
Button btnLog = findViewById(R.id.btnLog); |
||||
|
Button btnFilter = findViewById(R.id.btnFilter); |
||||
|
Button btnNotify = findViewById(R.id.btnNotify); |
||||
|
Button btnSpeed = findViewById(R.id.btnSpeed); |
||||
|
Button btnTheme = findViewById(R.id.btnTheme); |
||||
|
Button btnAll = findViewById(R.id.btnAll); |
||||
|
Button btnDev1 = findViewById(R.id.btnDev1); |
||||
|
Button btnDev2 = findViewById(R.id.btnDev2); |
||||
|
TextView tvLog = findViewById(R.id.tvLog); |
||||
|
TextView tvFilter = findViewById(R.id.tvFilter); |
||||
|
TextView tvNotify = findViewById(R.id.tvNotify); |
||||
|
TextView tvSpeed = findViewById(R.id.tvSpeed); |
||||
|
TextView tvTheme = findViewById(R.id.tvTheme); |
||||
|
TextView tvAll = findViewById(R.id.tvAll); |
||||
|
TextView tvDev1 = findViewById(R.id.tvDev1); |
||||
|
TextView tvDev2 = findViewById(R.id.tvDev2); |
||||
|
|
||||
|
TextView tvLogUnavailable = findViewById(R.id.tvLogUnavailable); |
||||
|
TextView tvFilterUnavailable = findViewById(R.id.tvFilterUnavailable); |
||||
|
|
||||
|
boolean can = Util.canFilter(this); |
||||
|
|
||||
|
btnLog.setVisibility(IAB.isPurchased(SKU_LOG, this) || !can ? View.GONE : View.VISIBLE); |
||||
|
btnFilter.setVisibility(IAB.isPurchased(SKU_FILTER, this) || !can ? View.GONE : View.VISIBLE); |
||||
|
btnNotify.setVisibility(IAB.isPurchased(SKU_NOTIFY, this) ? View.GONE : View.VISIBLE); |
||||
|
btnSpeed.setVisibility(IAB.isPurchased(SKU_SPEED, this) ? View.GONE : View.VISIBLE); |
||||
|
btnTheme.setVisibility(IAB.isPurchased(SKU_THEME, this) ? View.GONE : View.VISIBLE); |
||||
|
btnAll.setVisibility(IAB.isPurchased(SKU_PRO1, this) ? View.GONE : View.VISIBLE); |
||||
|
btnDev1.setVisibility(IAB.isPurchased(SKU_SUPPORT1, this) ? View.GONE : View.VISIBLE); |
||||
|
btnDev2.setVisibility(IAB.isPurchased(SKU_SUPPORT2, this) ? View.GONE : View.VISIBLE); |
||||
|
|
||||
|
tvLog.setVisibility(IAB.isPurchased(SKU_LOG, this) && can ? View.VISIBLE : View.GONE); |
||||
|
tvFilter.setVisibility(IAB.isPurchased(SKU_FILTER, this) && can ? View.VISIBLE : View.GONE); |
||||
|
tvNotify.setVisibility(IAB.isPurchased(SKU_NOTIFY, this) ? View.VISIBLE : View.GONE); |
||||
|
tvSpeed.setVisibility(IAB.isPurchased(SKU_SPEED, this) ? View.VISIBLE : View.GONE); |
||||
|
tvTheme.setVisibility(IAB.isPurchased(SKU_THEME, this) ? View.VISIBLE : View.GONE); |
||||
|
tvAll.setVisibility(IAB.isPurchased(SKU_PRO1, this) ? View.VISIBLE : View.GONE); |
||||
|
tvDev1.setVisibility(IAB.isPurchased(SKU_SUPPORT1, this) ? View.VISIBLE : View.GONE); |
||||
|
tvDev2.setVisibility(IAB.isPurchased(SKU_SUPPORT2, this) ? View.VISIBLE : View.GONE); |
||||
|
|
||||
|
tvLogUnavailable.setVisibility(can ? View.GONE : View.VISIBLE); |
||||
|
tvFilterUnavailable.setVisibility(can ? View.GONE : View.VISIBLE); |
||||
|
} |
||||
|
} |
1466
NetGuard/app/src/main/java/eu/faircode/netguard/ActivitySettings.java
File diff suppressed because it is too large
View File
@ -0,0 +1,186 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.content.res.TypedArray; |
||||
|
import android.database.Cursor; |
||||
|
import android.graphics.drawable.Drawable; |
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Build; |
||||
|
import android.text.SpannableString; |
||||
|
import android.text.style.UnderlineSpan; |
||||
|
import android.util.TypedValue; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.widget.CursorAdapter; |
||||
|
import android.widget.ImageView; |
||||
|
import android.widget.LinearLayout; |
||||
|
import android.widget.TextView; |
||||
|
|
||||
|
import androidx.core.graphics.drawable.DrawableCompat; |
||||
|
import androidx.core.view.ViewCompat; |
||||
|
|
||||
|
import java.net.InetAddress; |
||||
|
import java.net.UnknownHostException; |
||||
|
import java.text.SimpleDateFormat; |
||||
|
|
||||
|
public class AdapterAccess extends CursorAdapter { |
||||
|
private int colVersion; |
||||
|
private int colProtocol; |
||||
|
private int colDaddr; |
||||
|
private int colDPort; |
||||
|
private int colTime; |
||||
|
private int colAllowed; |
||||
|
private int colBlock; |
||||
|
private int colCount; |
||||
|
private int colSent; |
||||
|
private int colReceived; |
||||
|
private int colConnections; |
||||
|
|
||||
|
private int colorText; |
||||
|
private int colorOn; |
||||
|
private int colorOff; |
||||
|
|
||||
|
public AdapterAccess(Context context, Cursor cursor) { |
||||
|
super(context, cursor, 0); |
||||
|
colVersion = cursor.getColumnIndex("version"); |
||||
|
colProtocol = cursor.getColumnIndex("protocol"); |
||||
|
colDaddr = cursor.getColumnIndex("daddr"); |
||||
|
colDPort = cursor.getColumnIndex("dport"); |
||||
|
colTime = cursor.getColumnIndex("time"); |
||||
|
colAllowed = cursor.getColumnIndex("allowed"); |
||||
|
colBlock = cursor.getColumnIndex("block"); |
||||
|
colCount = cursor.getColumnIndex("count"); |
||||
|
colSent = cursor.getColumnIndex("sent"); |
||||
|
colReceived = cursor.getColumnIndex("received"); |
||||
|
colConnections = cursor.getColumnIndex("connections"); |
||||
|
|
||||
|
TypedArray ta = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.textColorSecondary}); |
||||
|
try { |
||||
|
colorText = ta.getColor(0, 0); |
||||
|
} finally { |
||||
|
ta.recycle(); |
||||
|
} |
||||
|
|
||||
|
TypedValue tv = new TypedValue(); |
||||
|
context.getTheme().resolveAttribute(R.attr.colorOn, tv, true); |
||||
|
colorOn = tv.data; |
||||
|
context.getTheme().resolveAttribute(R.attr.colorOff, tv, true); |
||||
|
colorOff = tv.data; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) { |
||||
|
return LayoutInflater.from(context).inflate(R.layout.access, parent, false); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void bindView(final View view, final Context context, final Cursor cursor) { |
||||
|
// Get values |
||||
|
final int version = cursor.getInt(colVersion); |
||||
|
final int protocol = cursor.getInt(colProtocol); |
||||
|
final String daddr = cursor.getString(colDaddr); |
||||
|
final int dport = cursor.getInt(colDPort); |
||||
|
long time = cursor.getLong(colTime); |
||||
|
int allowed = cursor.getInt(colAllowed); |
||||
|
int block = cursor.getInt(colBlock); |
||||
|
int count = cursor.getInt(colCount); |
||||
|
long sent = cursor.isNull(colSent) ? -1 : cursor.getLong(colSent); |
||||
|
long received = cursor.isNull(colReceived) ? -1 : cursor.getLong(colReceived); |
||||
|
int connections = cursor.isNull(colConnections) ? -1 : cursor.getInt(colConnections); |
||||
|
|
||||
|
// Get views |
||||
|
TextView tvTime = view.findViewById(R.id.tvTime); |
||||
|
ImageView ivBlock = view.findViewById(R.id.ivBlock); |
||||
|
final TextView tvDest = view.findViewById(R.id.tvDest); |
||||
|
LinearLayout llTraffic = view.findViewById(R.id.llTraffic); |
||||
|
TextView tvConnections = view.findViewById(R.id.tvConnections); |
||||
|
TextView tvTraffic = view.findViewById(R.id.tvTraffic); |
||||
|
|
||||
|
// Set values |
||||
|
tvTime.setText(new SimpleDateFormat("dd HH:mm").format(time)); |
||||
|
if (block < 0) |
||||
|
ivBlock.setImageDrawable(null); |
||||
|
else { |
||||
|
ivBlock.setImageResource(block > 0 ? R.drawable.host_blocked : R.drawable.host_allowed); |
||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |
||||
|
Drawable wrap = DrawableCompat.wrap(ivBlock.getDrawable()); |
||||
|
DrawableCompat.setTint(wrap, block > 0 ? colorOff : colorOn); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
String dest = Util.getProtocolName(protocol, version, true) + |
||||
|
" " + daddr + (dport > 0 ? "/" + dport : "") + (count > 1 ? " ?" + count : ""); |
||||
|
SpannableString span = new SpannableString(dest); |
||||
|
span.setSpan(new UnderlineSpan(), 0, dest.length(), 0); |
||||
|
tvDest.setText(span); |
||||
|
|
||||
|
if (Util.isNumericAddress(daddr)) |
||||
|
new AsyncTask<String, Object, String>() { |
||||
|
@Override |
||||
|
protected void onPreExecute() { |
||||
|
ViewCompat.setHasTransientState(tvDest, true); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected String doInBackground(String... args) { |
||||
|
try { |
||||
|
return InetAddress.getByName(args[0]).getHostName(); |
||||
|
} catch (UnknownHostException ignored) { |
||||
|
return args[0]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(String addr) { |
||||
|
tvDest.setText( |
||||
|
Util.getProtocolName(protocol, version, true) + |
||||
|
" >" + addr + (dport > 0 ? "/" + dport : "")); |
||||
|
ViewCompat.setHasTransientState(tvDest, false); |
||||
|
} |
||||
|
}.execute(daddr); |
||||
|
|
||||
|
if (allowed < 0) |
||||
|
tvDest.setTextColor(colorText); |
||||
|
else if (allowed > 0) |
||||
|
tvDest.setTextColor(colorOn); |
||||
|
else |
||||
|
tvDest.setTextColor(colorOff); |
||||
|
|
||||
|
llTraffic.setVisibility(connections > 0 || sent > 0 || received > 0 ? View.VISIBLE : View.GONE); |
||||
|
if (connections > 0) |
||||
|
tvConnections.setText(context.getString(R.string.msg_count, connections)); |
||||
|
|
||||
|
if (sent > 1024 * 1204 * 1024L || received > 1024 * 1024 * 1024L) |
||||
|
tvTraffic.setText(context.getString(R.string.msg_gb, |
||||
|
(sent > 0 ? sent / (1024 * 1024 * 1024f) : 0), |
||||
|
(received > 0 ? received / (1024 * 1024 * 1024f) : 0))); |
||||
|
else if (sent > 1204 * 1024L || received > 1024 * 1024L) |
||||
|
tvTraffic.setText(context.getString(R.string.msg_mb, |
||||
|
(sent > 0 ? sent / (1024 * 1024f) : 0), |
||||
|
(received > 0 ? received / (1024 * 1024f) : 0))); |
||||
|
else |
||||
|
tvTraffic.setText(context.getString(R.string.msg_kb, |
||||
|
(sent > 0 ? sent / 1024f : 0), |
||||
|
(received > 0 ? received / 1024f : 0))); |
||||
|
} |
||||
|
} |
@ -0,0 +1,95 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.database.Cursor; |
||||
|
import android.graphics.Color; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.widget.CursorAdapter; |
||||
|
import android.widget.TextView; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
public class AdapterDns extends CursorAdapter { |
||||
|
private int colorExpired; |
||||
|
|
||||
|
private int colTime; |
||||
|
private int colQName; |
||||
|
private int colAName; |
||||
|
private int colResource; |
||||
|
private int colTTL; |
||||
|
|
||||
|
public AdapterDns(Context context, Cursor cursor) { |
||||
|
super(context, cursor, 0); |
||||
|
|
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
|
||||
|
if (prefs.getBoolean("dark_theme", false)) |
||||
|
colorExpired = Color.argb(128, Color.red(Color.DKGRAY), Color.green(Color.DKGRAY), Color.blue(Color.DKGRAY)); |
||||
|
else |
||||
|
colorExpired = Color.argb(128, Color.red(Color.LTGRAY), Color.green(Color.LTGRAY), Color.blue(Color.LTGRAY)); |
||||
|
|
||||
|
colTime = cursor.getColumnIndex("time"); |
||||
|
colQName = cursor.getColumnIndex("qname"); |
||||
|
colAName = cursor.getColumnIndex("aname"); |
||||
|
colResource = cursor.getColumnIndex("resource"); |
||||
|
colTTL = cursor.getColumnIndex("ttl"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) { |
||||
|
return LayoutInflater.from(context).inflate(R.layout.dns, parent, false); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void bindView(final View view, final Context context, final Cursor cursor) { |
||||
|
// Get values |
||||
|
long time = cursor.getLong(colTime); |
||||
|
String qname = cursor.getString(colQName); |
||||
|
String aname = cursor.getString(colAName); |
||||
|
String resource = cursor.getString(colResource); |
||||
|
int ttl = cursor.getInt(colTTL); |
||||
|
|
||||
|
long now = new Date().getTime(); |
||||
|
boolean expired = (time + ttl < now); |
||||
|
view.setBackgroundColor(expired ? colorExpired : Color.TRANSPARENT); |
||||
|
|
||||
|
// Get views |
||||
|
TextView tvTime = view.findViewById(R.id.tvTime); |
||||
|
TextView tvQName = view.findViewById(R.id.tvQName); |
||||
|
TextView tvAName = view.findViewById(R.id.tvAName); |
||||
|
TextView tvResource = view.findViewById(R.id.tvResource); |
||||
|
TextView tvTTL = view.findViewById(R.id.tvTTL); |
||||
|
|
||||
|
// Set values |
||||
|
tvTime.setText(new SimpleDateFormat("dd HH:mm").format(time)); |
||||
|
tvQName.setText(qname); |
||||
|
tvAName.setText(aname); |
||||
|
tvResource.setText(resource); |
||||
|
tvTTL.setText("+" + Integer.toString(ttl / 1000)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,74 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.database.Cursor; |
||||
|
import android.text.TextUtils; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.widget.CursorAdapter; |
||||
|
import android.widget.TextView; |
||||
|
|
||||
|
public class AdapterForwarding extends CursorAdapter { |
||||
|
private int colProtocol; |
||||
|
private int colDPort; |
||||
|
private int colRAddr; |
||||
|
private int colRPort; |
||||
|
private int colRUid; |
||||
|
|
||||
|
public AdapterForwarding(Context context, Cursor cursor) { |
||||
|
super(context, cursor, 0); |
||||
|
colProtocol = cursor.getColumnIndex("protocol"); |
||||
|
colDPort = cursor.getColumnIndex("dport"); |
||||
|
colRAddr = cursor.getColumnIndex("raddr"); |
||||
|
colRPort = cursor.getColumnIndex("rport"); |
||||
|
colRUid = cursor.getColumnIndex("ruid"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) { |
||||
|
return LayoutInflater.from(context).inflate(R.layout.forward, parent, false); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void bindView(final View view, final Context context, final Cursor cursor) { |
||||
|
// Get values |
||||
|
int protocol = cursor.getInt(colProtocol); |
||||
|
int dport = cursor.getInt(colDPort); |
||||
|
String raddr = cursor.getString(colRAddr); |
||||
|
int rport = cursor.getInt(colRPort); |
||||
|
int ruid = cursor.getInt(colRUid); |
||||
|
|
||||
|
// Get views |
||||
|
TextView tvProtocol = view.findViewById(R.id.tvProtocol); |
||||
|
TextView tvDPort = view.findViewById(R.id.tvDPort); |
||||
|
TextView tvRAddr = view.findViewById(R.id.tvRAddr); |
||||
|
TextView tvRPort = view.findViewById(R.id.tvRPort); |
||||
|
TextView tvRUid = view.findViewById(R.id.tvRUid); |
||||
|
|
||||
|
tvProtocol.setText(Util.getProtocolName(protocol, 0, false)); |
||||
|
tvDPort.setText(Integer.toString(dport)); |
||||
|
tvRAddr.setText(raddr); |
||||
|
tvRPort.setText(Integer.toString(rport)); |
||||
|
tvRUid.setText(TextUtils.join(", ", Util.getApplicationNames(ruid, context))); |
||||
|
} |
||||
|
} |
@ -0,0 +1,370 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.content.pm.ApplicationInfo; |
||||
|
import android.content.pm.PackageManager; |
||||
|
import android.database.Cursor; |
||||
|
import android.graphics.drawable.Drawable; |
||||
|
import android.net.Uri; |
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Build; |
||||
|
import android.text.TextUtils; |
||||
|
import android.util.Log; |
||||
|
import android.util.TypedValue; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.widget.CursorAdapter; |
||||
|
import android.widget.ImageView; |
||||
|
import android.widget.TextView; |
||||
|
|
||||
|
import androidx.core.graphics.drawable.DrawableCompat; |
||||
|
import androidx.core.view.ViewCompat; |
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
import java.net.InetAddress; |
||||
|
import java.net.UnknownHostException; |
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class AdapterLog extends CursorAdapter { |
||||
|
private static String TAG = "NetGuard.Log"; |
||||
|
|
||||
|
private boolean resolve; |
||||
|
private boolean organization; |
||||
|
private int colTime; |
||||
|
private int colVersion; |
||||
|
private int colProtocol; |
||||
|
private int colFlags; |
||||
|
private int colSAddr; |
||||
|
private int colSPort; |
||||
|
private int colDAddr; |
||||
|
private int colDPort; |
||||
|
private int colDName; |
||||
|
private int colUid; |
||||
|
private int colData; |
||||
|
private int colAllowed; |
||||
|
private int colConnection; |
||||
|
private int colInteractive; |
||||
|
private int colorOn; |
||||
|
private int colorOff; |
||||
|
private int iconSize; |
||||
|
private InetAddress dns1 = null; |
||||
|
private InetAddress dns2 = null; |
||||
|
private InetAddress vpn4 = null; |
||||
|
private InetAddress vpn6 = null; |
||||
|
|
||||
|
public AdapterLog(Context context, Cursor cursor, boolean resolve, boolean organization) { |
||||
|
super(context, cursor, 0); |
||||
|
this.resolve = resolve; |
||||
|
this.organization = organization; |
||||
|
colTime = cursor.getColumnIndex("time"); |
||||
|
colVersion = cursor.getColumnIndex("version"); |
||||
|
colProtocol = cursor.getColumnIndex("protocol"); |
||||
|
colFlags = cursor.getColumnIndex("flags"); |
||||
|
colSAddr = cursor.getColumnIndex("saddr"); |
||||
|
colSPort = cursor.getColumnIndex("sport"); |
||||
|
colDAddr = cursor.getColumnIndex("daddr"); |
||||
|
colDPort = cursor.getColumnIndex("dport"); |
||||
|
colDName = cursor.getColumnIndex("dname"); |
||||
|
colUid = cursor.getColumnIndex("uid"); |
||||
|
colData = cursor.getColumnIndex("data"); |
||||
|
colAllowed = cursor.getColumnIndex("allowed"); |
||||
|
colConnection = cursor.getColumnIndex("connection"); |
||||
|
colInteractive = cursor.getColumnIndex("interactive"); |
||||
|
|
||||
|
TypedValue tv = new TypedValue(); |
||||
|
context.getTheme().resolveAttribute(R.attr.colorOn, tv, true); |
||||
|
colorOn = tv.data; |
||||
|
context.getTheme().resolveAttribute(R.attr.colorOff, tv, true); |
||||
|
colorOff = tv.data; |
||||
|
|
||||
|
iconSize = Util.dips2pixels(24, context); |
||||
|
|
||||
|
try { |
||||
|
List<InetAddress> lstDns = ServiceSinkhole.getDns(context); |
||||
|
dns1 = (lstDns.size() > 0 ? lstDns.get(0) : null); |
||||
|
dns2 = (lstDns.size() > 1 ? lstDns.get(1) : null); |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
vpn4 = InetAddress.getByName(prefs.getString("vpn4", "10.1.10.1")); |
||||
|
vpn6 = InetAddress.getByName(prefs.getString("vpn6", "fd00:1:fd00:1:fd00:1:fd00:1")); |
||||
|
} catch (UnknownHostException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void setResolve(boolean resolve) { |
||||
|
this.resolve = resolve; |
||||
|
} |
||||
|
|
||||
|
public void setOrganization(boolean organization) { |
||||
|
this.organization = organization; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) { |
||||
|
return LayoutInflater.from(context).inflate(R.layout.log, parent, false); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void bindView(final View view, final Context context, final Cursor cursor) { |
||||
|
// Get values |
||||
|
long time = cursor.getLong(colTime); |
||||
|
int version = (cursor.isNull(colVersion) ? -1 : cursor.getInt(colVersion)); |
||||
|
int protocol = (cursor.isNull(colProtocol) ? -1 : cursor.getInt(colProtocol)); |
||||
|
String flags = cursor.getString(colFlags); |
||||
|
String saddr = cursor.getString(colSAddr); |
||||
|
int sport = (cursor.isNull(colSPort) ? -1 : cursor.getInt(colSPort)); |
||||
|
String daddr = cursor.getString(colDAddr); |
||||
|
int dport = (cursor.isNull(colDPort) ? -1 : cursor.getInt(colDPort)); |
||||
|
String dname = (cursor.isNull(colDName) ? null : cursor.getString(colDName)); |
||||
|
int uid = (cursor.isNull(colUid) ? -1 : cursor.getInt(colUid)); |
||||
|
String data = cursor.getString(colData); |
||||
|
int allowed = (cursor.isNull(colAllowed) ? -1 : cursor.getInt(colAllowed)); |
||||
|
int connection = (cursor.isNull(colConnection) ? -1 : cursor.getInt(colConnection)); |
||||
|
int interactive = (cursor.isNull(colInteractive) ? -1 : cursor.getInt(colInteractive)); |
||||
|
|
||||
|
// Get views |
||||
|
TextView tvTime = view.findViewById(R.id.tvTime); |
||||
|
TextView tvProtocol = view.findViewById(R.id.tvProtocol); |
||||
|
TextView tvFlags = view.findViewById(R.id.tvFlags); |
||||
|
TextView tvSAddr = view.findViewById(R.id.tvSAddr); |
||||
|
TextView tvSPort = view.findViewById(R.id.tvSPort); |
||||
|
final TextView tvDaddr = view.findViewById(R.id.tvDAddr); |
||||
|
TextView tvDPort = view.findViewById(R.id.tvDPort); |
||||
|
final TextView tvOrganization = view.findViewById(R.id.tvOrganization); |
||||
|
final ImageView ivIcon = view.findViewById(R.id.ivIcon); |
||||
|
TextView tvUid = view.findViewById(R.id.tvUid); |
||||
|
TextView tvData = view.findViewById(R.id.tvData); |
||||
|
ImageView ivConnection = view.findViewById(R.id.ivConnection); |
||||
|
ImageView ivInteractive = view.findViewById(R.id.ivInteractive); |
||||
|
|
||||
|
// Show time |
||||
|
tvTime.setText(new SimpleDateFormat("HH:mm:ss").format(time)); |
||||
|
|
||||
|
// Show connection type |
||||
|
if (connection <= 0) |
||||
|
ivConnection.setImageResource(allowed > 0 ? R.drawable.host_allowed : R.drawable.host_blocked); |
||||
|
else { |
||||
|
if (allowed > 0) |
||||
|
ivConnection.setImageResource(connection == 1 ? R.drawable.wifi_on : R.drawable.other_on); |
||||
|
else |
||||
|
ivConnection.setImageResource(connection == 1 ? R.drawable.wifi_off : R.drawable.other_off); |
||||
|
} |
||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |
||||
|
Drawable wrap = DrawableCompat.wrap(ivConnection.getDrawable()); |
||||
|
DrawableCompat.setTint(wrap, allowed > 0 ? colorOn : colorOff); |
||||
|
} |
||||
|
|
||||
|
// Show if screen on |
||||
|
if (interactive <= 0) |
||||
|
ivInteractive.setImageDrawable(null); |
||||
|
else { |
||||
|
ivInteractive.setImageResource(R.drawable.screen_on); |
||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |
||||
|
Drawable wrap = DrawableCompat.wrap(ivInteractive.getDrawable()); |
||||
|
DrawableCompat.setTint(wrap, colorOn); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Show protocol name |
||||
|
tvProtocol.setText(Util.getProtocolName(protocol, version, false)); |
||||
|
|
||||
|
// SHow TCP flags |
||||
|
tvFlags.setText(flags); |
||||
|
tvFlags.setVisibility(TextUtils.isEmpty(flags) ? View.GONE : View.VISIBLE); |
||||
|
|
||||
|
// Show source and destination port |
||||
|
if (protocol == 6 || protocol == 17) { |
||||
|
tvSPort.setText(sport < 0 ? "" : getKnownPort(sport)); |
||||
|
tvDPort.setText(dport < 0 ? "" : getKnownPort(dport)); |
||||
|
} else { |
||||
|
tvSPort.setText(sport < 0 ? "" : Integer.toString(sport)); |
||||
|
tvDPort.setText(dport < 0 ? "" : Integer.toString(dport)); |
||||
|
} |
||||
|
|
||||
|
// Application icon |
||||
|
ApplicationInfo info = null; |
||||
|
PackageManager pm = context.getPackageManager(); |
||||
|
String[] pkg = pm.getPackagesForUid(uid); |
||||
|
if (pkg != null && pkg.length > 0) |
||||
|
try { |
||||
|
info = pm.getApplicationInfo(pkg[0], 0); |
||||
|
} catch (PackageManager.NameNotFoundException ignored) { |
||||
|
} |
||||
|
|
||||
|
if (info == null) |
||||
|
ivIcon.setImageDrawable(null); |
||||
|
else { |
||||
|
if (info.icon <= 0) |
||||
|
ivIcon.setImageResource(android.R.drawable.sym_def_app_icon); |
||||
|
else { |
||||
|
Uri uri = Uri.parse("android.resource://" + info.packageName + "/" + info.icon); |
||||
|
GlideApp.with(context) |
||||
|
.load(uri) |
||||
|
//.diskCacheStrategy(DiskCacheStrategy.NONE) |
||||
|
//.skipMemoryCache(true) |
||||
|
.override(iconSize, iconSize) |
||||
|
.into(ivIcon); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
boolean we = (android.os.Process.myUid() == uid); |
||||
|
|
||||
|
// https://android.googlesource.com/platform/system/core/+/master/include/private/android_filesystem_config.h |
||||
|
uid = uid % 100000; // strip off user ID |
||||
|
if (uid == -1) |
||||
|
tvUid.setText(""); |
||||
|
else if (uid == 0) |
||||
|
tvUid.setText(context.getString(R.string.title_root)); |
||||
|
else if (uid == 9999) |
||||
|
tvUid.setText("-"); // nobody |
||||
|
else |
||||
|
tvUid.setText(Integer.toString(uid)); |
||||
|
|
||||
|
// Show source address |
||||
|
tvSAddr.setText(getKnownAddress(saddr)); |
||||
|
|
||||
|
// Show destination address |
||||
|
if (!we && resolve && !isKnownAddress(daddr)) |
||||
|
if (dname == null) { |
||||
|
tvDaddr.setText(daddr); |
||||
|
new AsyncTask<String, Object, String>() { |
||||
|
@Override |
||||
|
protected void onPreExecute() { |
||||
|
ViewCompat.setHasTransientState(tvDaddr, true); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected String doInBackground(String... args) { |
||||
|
try { |
||||
|
return InetAddress.getByName(args[0]).getHostName(); |
||||
|
} catch (UnknownHostException ignored) { |
||||
|
return args[0]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(String name) { |
||||
|
tvDaddr.setText(">" + name); |
||||
|
ViewCompat.setHasTransientState(tvDaddr, false); |
||||
|
} |
||||
|
}.execute(daddr); |
||||
|
} else |
||||
|
tvDaddr.setText(dname); |
||||
|
else |
||||
|
tvDaddr.setText(getKnownAddress(daddr)); |
||||
|
|
||||
|
// Show organization |
||||
|
tvOrganization.setVisibility(View.GONE); |
||||
|
if (!we && organization) { |
||||
|
if (!isKnownAddress(daddr)) |
||||
|
new AsyncTask<String, Object, String>() { |
||||
|
@Override |
||||
|
protected void onPreExecute() { |
||||
|
ViewCompat.setHasTransientState(tvOrganization, true); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected String doInBackground(String... args) { |
||||
|
try { |
||||
|
return Util.getOrganization(args[0]); |
||||
|
} catch (Throwable ex) { |
||||
|
Log.w(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(String organization) { |
||||
|
if (organization != null) { |
||||
|
tvOrganization.setText(organization); |
||||
|
tvOrganization.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
ViewCompat.setHasTransientState(tvOrganization, false); |
||||
|
} |
||||
|
}.execute(daddr); |
||||
|
} |
||||
|
|
||||
|
// Show extra data |
||||
|
if (TextUtils.isEmpty(data)) { |
||||
|
tvData.setText(""); |
||||
|
tvData.setVisibility(View.GONE); |
||||
|
} else { |
||||
|
tvData.setText(data); |
||||
|
tvData.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public boolean isKnownAddress(String addr) { |
||||
|
try { |
||||
|
InetAddress a = InetAddress.getByName(addr); |
||||
|
if (a.equals(dns1) || a.equals(dns2) || a.equals(vpn4) || a.equals(vpn6)) |
||||
|
return true; |
||||
|
} catch (UnknownHostException ignored) { |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private String getKnownAddress(String addr) { |
||||
|
try { |
||||
|
InetAddress a = InetAddress.getByName(addr); |
||||
|
if (a.equals(dns1)) |
||||
|
return "dns1"; |
||||
|
if (a.equals(dns2)) |
||||
|
return "dns2"; |
||||
|
if (a.equals(vpn4) || a.equals(vpn6)) |
||||
|
return "vpn"; |
||||
|
} catch (UnknownHostException ignored) { |
||||
|
} |
||||
|
return addr; |
||||
|
} |
||||
|
|
||||
|
private String getKnownPort(int port) { |
||||
|
// https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports |
||||
|
switch (port) { |
||||
|
case 7: |
||||
|
return "echo"; |
||||
|
case 25: |
||||
|
return "smtp"; |
||||
|
case 53: |
||||
|
return "dns"; |
||||
|
case 80: |
||||
|
return "http"; |
||||
|
case 110: |
||||
|
return "pop3"; |
||||
|
case 143: |
||||
|
return "imap"; |
||||
|
case 443: |
||||
|
return "https"; |
||||
|
case 465: |
||||
|
return "smtps"; |
||||
|
case 993: |
||||
|
return "imaps"; |
||||
|
case 995: |
||||
|
return "pop3s"; |
||||
|
default: |
||||
|
return Integer.toString(port); |
||||
|
} |
||||
|
} |
||||
|
} |
1033
NetGuard/app/src/main/java/eu/faircode/netguard/AdapterRule.java
File diff suppressed because it is too large
View File
@ -0,0 +1,35 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
public class Allowed { |
||||
|
public String raddr; |
||||
|
public int rport; |
||||
|
|
||||
|
public Allowed() { |
||||
|
this.raddr = null; |
||||
|
this.rport = 0; |
||||
|
} |
||||
|
|
||||
|
public Allowed(String raddr, int rport) { |
||||
|
this.raddr = raddr; |
||||
|
this.rport = rport; |
||||
|
} |
||||
|
} |
@ -0,0 +1,78 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.annotation.TargetApi; |
||||
|
import android.app.Application; |
||||
|
import android.app.Notification; |
||||
|
import android.app.NotificationChannel; |
||||
|
import android.app.NotificationManager; |
||||
|
import android.content.Context; |
||||
|
import android.os.Build; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
public class ApplicationEx extends Application { |
||||
|
private static final String TAG = "NetGuard.App"; |
||||
|
|
||||
|
private Thread.UncaughtExceptionHandler mPrevHandler; |
||||
|
|
||||
|
@Override |
||||
|
public void onCreate() { |
||||
|
super.onCreate(); |
||||
|
Log.i(TAG, "Create version=" + Util.getSelfVersionName(this) + "/" + Util.getSelfVersionCode(this)); |
||||
|
|
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) |
||||
|
createNotificationChannels(); |
||||
|
|
||||
|
mPrevHandler = Thread.getDefaultUncaughtExceptionHandler(); |
||||
|
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { |
||||
|
@Override |
||||
|
public void uncaughtException(Thread thread, Throwable ex) { |
||||
|
if (Util.ownFault(ApplicationEx.this, ex) |
||||
|
&& Util.isPlayStoreInstall(ApplicationEx.this)) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
mPrevHandler.uncaughtException(thread, ex); |
||||
|
} else { |
||||
|
Log.w(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
System.exit(1); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@TargetApi(Build.VERSION_CODES.O) |
||||
|
private void createNotificationChannels() { |
||||
|
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); |
||||
|
|
||||
|
NotificationChannel foreground = new NotificationChannel("foreground", getString(R.string.channel_foreground), NotificationManager.IMPORTANCE_MIN); |
||||
|
foreground.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT); |
||||
|
nm.createNotificationChannel(foreground); |
||||
|
|
||||
|
NotificationChannel notify = new NotificationChannel("notify", getString(R.string.channel_notify), NotificationManager.IMPORTANCE_DEFAULT); |
||||
|
notify.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT); |
||||
|
notify.setBypassDnd(true); |
||||
|
nm.createNotificationChannel(notify); |
||||
|
|
||||
|
NotificationChannel access = new NotificationChannel("access", getString(R.string.channel_access), NotificationManager.IMPORTANCE_DEFAULT); |
||||
|
access.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT); |
||||
|
access.setBypassDnd(true); |
||||
|
nm.createNotificationChannel(access); |
||||
|
} |
||||
|
} |
1164
NetGuard/app/src/main/java/eu/faircode/netguard/DatabaseHelper.java
File diff suppressed because it is too large
View File
@ -0,0 +1,181 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.app.Activity; |
||||
|
import android.app.PendingIntent; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Build; |
||||
|
import android.os.PowerManager; |
||||
|
import android.util.Log; |
||||
|
import android.util.TypedValue; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.core.app.NotificationCompat; |
||||
|
import androidx.core.app.NotificationManagerCompat; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FileOutputStream; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
import java.io.OutputStream; |
||||
|
import java.net.HttpURLConnection; |
||||
|
import java.net.URL; |
||||
|
import java.net.URLConnection; |
||||
|
|
||||
|
public class DownloadTask extends AsyncTask<Object, Integer, Object> { |
||||
|
private static final String TAG = "NetGuard.Download"; |
||||
|
|
||||
|
private Context context; |
||||
|
private URL url; |
||||
|
private File file; |
||||
|
private Listener listener; |
||||
|
private PowerManager.WakeLock wakeLock; |
||||
|
|
||||
|
public interface Listener { |
||||
|
void onCompleted(); |
||||
|
|
||||
|
void onCancelled(); |
||||
|
|
||||
|
void onException(Throwable ex); |
||||
|
} |
||||
|
|
||||
|
public DownloadTask(Activity context, URL url, File file, Listener listener) { |
||||
|
this.context = context; |
||||
|
this.url = url; |
||||
|
this.file = file; |
||||
|
this.listener = listener; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPreExecute() { |
||||
|
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); |
||||
|
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); |
||||
|
wakeLock.acquire(); |
||||
|
showNotification(0); |
||||
|
Toast.makeText(context, context.getString(R.string.msg_downloading, url.toString()), Toast.LENGTH_SHORT).show(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected Object doInBackground(Object... args) { |
||||
|
Log.i(TAG, "Downloading " + url + " into " + file); |
||||
|
|
||||
|
InputStream in = null; |
||||
|
OutputStream out = null; |
||||
|
URLConnection connection = null; |
||||
|
try { |
||||
|
connection = url.openConnection(); |
||||
|
connection.connect(); |
||||
|
|
||||
|
if (connection instanceof HttpURLConnection) { |
||||
|
HttpURLConnection httpConnection = (HttpURLConnection) connection; |
||||
|
if (httpConnection.getResponseCode() != HttpURLConnection.HTTP_OK) |
||||
|
throw new IOException(httpConnection.getResponseCode() + " " + httpConnection.getResponseMessage()); |
||||
|
} |
||||
|
|
||||
|
int contentLength = connection.getContentLength(); |
||||
|
Log.i(TAG, "Content length=" + contentLength); |
||||
|
in = connection.getInputStream(); |
||||
|
out = new FileOutputStream(file); |
||||
|
|
||||
|
long size = 0; |
||||
|
byte buffer[] = new byte[4096]; |
||||
|
int bytes; |
||||
|
while (!isCancelled() && (bytes = in.read(buffer)) != -1) { |
||||
|
out.write(buffer, 0, bytes); |
||||
|
|
||||
|
size += bytes; |
||||
|
if (contentLength > 0) |
||||
|
publishProgress((int) (size * 100 / contentLength)); |
||||
|
} |
||||
|
|
||||
|
Log.i(TAG, "Downloaded size=" + size); |
||||
|
return null; |
||||
|
} catch (Throwable ex) { |
||||
|
return ex; |
||||
|
} finally { |
||||
|
try { |
||||
|
if (out != null) |
||||
|
out.close(); |
||||
|
} catch (IOException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
try { |
||||
|
if (in != null) |
||||
|
in.close(); |
||||
|
} catch (IOException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
|
||||
|
if (connection instanceof HttpURLConnection) |
||||
|
((HttpURLConnection) connection).disconnect(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onProgressUpdate(Integer... progress) { |
||||
|
super.onProgressUpdate(progress); |
||||
|
showNotification(progress[0]); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onCancelled() { |
||||
|
super.onCancelled(); |
||||
|
Log.i(TAG, "Cancelled"); |
||||
|
listener.onCancelled(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(Object result) { |
||||
|
wakeLock.release(); |
||||
|
NotificationManagerCompat.from(context).cancel(ServiceSinkhole.NOTIFY_DOWNLOAD); |
||||
|
if (result instanceof Throwable) { |
||||
|
Log.e(TAG, result.toString() + "\n" + Log.getStackTraceString((Throwable) result)); |
||||
|
listener.onException((Throwable) result); |
||||
|
} else |
||||
|
listener.onCompleted(); |
||||
|
} |
||||
|
|
||||
|
private void showNotification(int progress) { |
||||
|
Intent main = new Intent(context, ActivitySettings.class); |
||||
|
PendingIntent pi = PendingIntent.getActivity(context, ServiceSinkhole.NOTIFY_DOWNLOAD, main, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); |
||||
|
|
||||
|
TypedValue tv = new TypedValue(); |
||||
|
context.getTheme().resolveAttribute(R.attr.colorOff, tv, true); |
||||
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "notify"); |
||||
|
builder.setSmallIcon(R.drawable.ic_file_download_white_24dp) |
||||
|
.setContentTitle(context.getString(R.string.app_name)) |
||||
|
.setContentText(context.getString(R.string.msg_downloading, url.toString())) |
||||
|
.setContentIntent(pi) |
||||
|
.setProgress(100, progress, false) |
||||
|
.setColor(tv.data) |
||||
|
.setOngoing(true) |
||||
|
.setAutoCancel(false); |
||||
|
|
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) |
||||
|
builder.setCategory(NotificationCompat.CATEGORY_STATUS) |
||||
|
.setVisibility(NotificationCompat.VISIBILITY_SECRET); |
||||
|
|
||||
|
NotificationManagerCompat.from(context).notify(ServiceSinkhole.NOTIFY_DOWNLOAD, builder.build()); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,45 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.util.AttributeSet; |
||||
|
import android.widget.ListView; |
||||
|
|
||||
|
// This requires list view items with equal heights |
||||
|
|
||||
|
public class ExpandedListView extends ListView { |
||||
|
public ExpandedListView(Context context) { |
||||
|
super(context); |
||||
|
} |
||||
|
|
||||
|
public ExpandedListView(Context context, AttributeSet attrs) { |
||||
|
super(context, attrs); |
||||
|
} |
||||
|
|
||||
|
public ExpandedListView(Context context, AttributeSet attrs, int defStyleAttr) { |
||||
|
super(context, attrs, defStyleAttr); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
||||
|
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 4, MeasureSpec.AT_MOST)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
public class Forward { |
||||
|
public int protocol; |
||||
|
public int dport; |
||||
|
public String raddr; |
||||
|
public int rport; |
||||
|
public int ruid; |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "protocol=" + protocol + " port " + dport + " to " + raddr + "/" + rport + " uid " + ruid; |
||||
|
} |
||||
|
} |
@ -0,0 +1,32 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.os.Bundle; |
||||
|
import android.preference.PreferenceFragment; |
||||
|
|
||||
|
public class FragmentSettings extends PreferenceFragment { |
||||
|
|
||||
|
@Override |
||||
|
public void onCreate(Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
addPreferencesFromResource(R.xml.preferences); |
||||
|
} |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
import com.bumptech.glide.annotation.GlideModule; |
||||
|
import com.bumptech.glide.module.AppGlideModule; |
||||
|
|
||||
|
@GlideModule |
||||
|
public final class GlideHelper extends AppGlideModule { |
||||
|
} |
@ -0,0 +1,240 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.app.PendingIntent; |
||||
|
import android.content.ComponentName; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.content.ServiceConnection; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.os.Bundle; |
||||
|
import android.os.IBinder; |
||||
|
import android.os.RemoteException; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
import com.android.vending.billing.IInAppBillingService; |
||||
|
|
||||
|
import org.json.JSONException; |
||||
|
import org.json.JSONObject; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class IAB implements ServiceConnection { |
||||
|
private static final String TAG = "NetGuard.IAB"; |
||||
|
|
||||
|
private Context context; |
||||
|
private Delegate delegate; |
||||
|
private IInAppBillingService service = null; |
||||
|
|
||||
|
private static final int IAB_VERSION = 3; |
||||
|
|
||||
|
public interface Delegate { |
||||
|
void onReady(IAB iab); |
||||
|
} |
||||
|
|
||||
|
public IAB(Delegate delegate, Context context) { |
||||
|
this.context = context.getApplicationContext(); |
||||
|
this.delegate = delegate; |
||||
|
} |
||||
|
|
||||
|
public void bind() { |
||||
|
Log.i(TAG, "Bind"); |
||||
|
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); |
||||
|
serviceIntent.setPackage("com.android.vending"); |
||||
|
context.bindService(serviceIntent, this, Context.BIND_AUTO_CREATE); |
||||
|
} |
||||
|
|
||||
|
public void unbind() { |
||||
|
if (service != null) { |
||||
|
Log.i(TAG, "Unbind"); |
||||
|
context.unbindService(this); |
||||
|
service = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onServiceConnected(ComponentName name, IBinder binder) { |
||||
|
Log.i(TAG, "Connected"); |
||||
|
service = IInAppBillingService.Stub.asInterface(binder); |
||||
|
delegate.onReady(this); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onServiceDisconnected(ComponentName name) { |
||||
|
Log.i(TAG, "Disconnected"); |
||||
|
service = null; |
||||
|
} |
||||
|
|
||||
|
public boolean isAvailable(String sku) throws RemoteException, JSONException { |
||||
|
// Get available SKUs |
||||
|
ArrayList<String> skuList = new ArrayList<>(); |
||||
|
skuList.add(sku); |
||||
|
Bundle query = new Bundle(); |
||||
|
query.putStringArrayList("ITEM_ID_LIST", skuList); |
||||
|
Bundle bundle = service.getSkuDetails(IAB_VERSION, context.getPackageName(), "inapp", query); |
||||
|
Log.i(TAG, "getSkuDetails"); |
||||
|
Util.logBundle(bundle); |
||||
|
int response = (bundle == null ? -1 : bundle.getInt("RESPONSE_CODE", -1)); |
||||
|
Log.i(TAG, "Response=" + getResult(response)); |
||||
|
if (response != 0) |
||||
|
throw new IllegalArgumentException(getResult(response)); |
||||
|
|
||||
|
// Check available SKUs |
||||
|
boolean found = false; |
||||
|
ArrayList<String> details = bundle.getStringArrayList("DETAILS_LIST"); |
||||
|
if (details != null) |
||||
|
for (String item : details) { |
||||
|
JSONObject object = new JSONObject(item); |
||||
|
if (sku.equals(object.getString("productId"))) { |
||||
|
found = true; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
Log.i(TAG, sku + "=" + found); |
||||
|
|
||||
|
return found; |
||||
|
} |
||||
|
|
||||
|
public void updatePurchases() throws RemoteException { |
||||
|
// Get purchases |
||||
|
List<String> skus = new ArrayList<>(); |
||||
|
skus.addAll(getPurchases("inapp")); |
||||
|
skus.addAll(getPurchases("subs")); |
||||
|
|
||||
|
SharedPreferences prefs = context.getSharedPreferences("IAB", Context.MODE_PRIVATE); |
||||
|
SharedPreferences.Editor editor = prefs.edit(); |
||||
|
for (String product : prefs.getAll().keySet()) |
||||
|
if (!ActivityPro.SKU_DONATION.equals(product)) { |
||||
|
Log.i(TAG, "removing SKU=" + product); |
||||
|
editor.remove(product); |
||||
|
} |
||||
|
for (String sku : skus) { |
||||
|
Log.i(TAG, "adding SKU=" + sku); |
||||
|
editor.putBoolean(sku, true); |
||||
|
} |
||||
|
editor.apply(); |
||||
|
} |
||||
|
|
||||
|
public boolean isPurchased(String sku, String type) throws RemoteException { |
||||
|
return getPurchases(type).contains(sku); |
||||
|
} |
||||
|
|
||||
|
public List<String> getPurchases(String type) throws RemoteException { |
||||
|
// Get purchases |
||||
|
Bundle bundle = service.getPurchases(IAB_VERSION, context.getPackageName(), type, null); |
||||
|
Log.i(TAG, "getPurchases"); |
||||
|
Util.logBundle(bundle); |
||||
|
int response = (bundle == null ? -1 : bundle.getInt("RESPONSE_CODE", -1)); |
||||
|
Log.i(TAG, "Response=" + getResult(response)); |
||||
|
if (response != 0) |
||||
|
throw new IllegalArgumentException(getResult(response)); |
||||
|
|
||||
|
ArrayList<String> details = bundle.getStringArrayList("INAPP_PURCHASE_ITEM_LIST"); |
||||
|
return (details == null ? new ArrayList<String>() : details); |
||||
|
} |
||||
|
|
||||
|
public PendingIntent getBuyIntent(String sku, boolean subscription) throws RemoteException { |
||||
|
if (service == null) |
||||
|
return null; |
||||
|
Bundle bundle = service.getBuyIntent(IAB_VERSION, context.getPackageName(), sku, subscription ? "subs" : "inapp", "netguard"); |
||||
|
Log.i(TAG, "getBuyIntent sku=" + sku + " subscription=" + subscription); |
||||
|
Util.logBundle(bundle); |
||||
|
int response = (bundle == null ? -1 : bundle.getInt("RESPONSE_CODE", -1)); |
||||
|
Log.i(TAG, "Response=" + getResult(response)); |
||||
|
if (response != 0) |
||||
|
throw new IllegalArgumentException(getResult(response)); |
||||
|
if (!bundle.containsKey("BUY_INTENT")) |
||||
|
throw new IllegalArgumentException("BUY_INTENT missing"); |
||||
|
return bundle.getParcelable("BUY_INTENT"); |
||||
|
} |
||||
|
|
||||
|
public static void setBought(String sku, Context context) { |
||||
|
Log.i(TAG, "Bought " + sku); |
||||
|
SharedPreferences prefs = context.getSharedPreferences("IAB", Context.MODE_PRIVATE); |
||||
|
prefs.edit().putBoolean(sku, true).apply(); |
||||
|
} |
||||
|
|
||||
|
public static boolean isPurchased(String sku, Context context) { |
||||
|
try { |
||||
|
if (Util.isDebuggable(context)) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
return !prefs.getBoolean("debug_iab", false); |
||||
|
} |
||||
|
|
||||
|
SharedPreferences prefs = context.getSharedPreferences("IAB", Context.MODE_PRIVATE); |
||||
|
if (ActivityPro.SKU_SUPPORT1.equals(sku) || ActivityPro.SKU_SUPPORT2.equals(sku)) |
||||
|
return prefs.getBoolean(sku, false); |
||||
|
|
||||
|
return (prefs.getBoolean(sku, false) || |
||||
|
prefs.getBoolean(ActivityPro.SKU_PRO1, false) || |
||||
|
prefs.getBoolean(ActivityPro.SKU_SUPPORT1, false) || |
||||
|
prefs.getBoolean(ActivityPro.SKU_SUPPORT2, false) || |
||||
|
prefs.getBoolean(ActivityPro.SKU_DONATION, false)); |
||||
|
} catch (SecurityException ignored) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static boolean isPurchasedAny(Context context) { |
||||
|
try { |
||||
|
if (Util.isDebuggable(context)) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
return !(prefs.getBoolean("debug_iab", false)); |
||||
|
} |
||||
|
|
||||
|
SharedPreferences prefs = context.getSharedPreferences("IAB", Context.MODE_PRIVATE); |
||||
|
for (String key : prefs.getAll().keySet()) |
||||
|
if (prefs.getBoolean(key, false)) |
||||
|
return true; |
||||
|
return false; |
||||
|
} catch (SecurityException ignored) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static String getResult(int responseCode) { |
||||
|
switch (responseCode) { |
||||
|
case 0: |
||||
|
return "OK"; |
||||
|
case 1: |
||||
|
return "USER_CANCELED"; |
||||
|
case 2: |
||||
|
return "SERVICE_UNAVAILABLE"; |
||||
|
case 3: |
||||
|
return "BILLING_UNAVAILABLE"; |
||||
|
case 4: |
||||
|
return "ITEM_UNAVAILABLE"; |
||||
|
case 5: |
||||
|
return "DEVELOPER_ERROR"; |
||||
|
case 6: |
||||
|
return "ERROR"; |
||||
|
case 7: |
||||
|
return "ITEM_ALREADY_OWNED"; |
||||
|
case 8: |
||||
|
return "ITEM_NOT_OWNED"; |
||||
|
default: |
||||
|
return Integer.toString(responseCode); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,140 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.util.Log; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
|
||||
|
import java.net.InetAddress; |
||||
|
import java.net.UnknownHostException; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class IPUtil { |
||||
|
private static final String TAG = "NetGuard.IPUtil"; |
||||
|
|
||||
|
public static List<CIDR> toCIDR(String start, String end) throws UnknownHostException { |
||||
|
return toCIDR(InetAddress.getByName(start), InetAddress.getByName(end)); |
||||
|
} |
||||
|
|
||||
|
public static List<CIDR> toCIDR(InetAddress start, InetAddress end) throws UnknownHostException { |
||||
|
List<CIDR> listResult = new ArrayList<>(); |
||||
|
|
||||
|
Log.i(TAG, "toCIDR(" + start.getHostAddress() + "," + end.getHostAddress() + ")"); |
||||
|
|
||||
|
long from = inet2long(start); |
||||
|
long to = inet2long(end); |
||||
|
while (to >= from) { |
||||
|
byte prefix = 32; |
||||
|
while (prefix > 0) { |
||||
|
long mask = prefix2mask(prefix - 1); |
||||
|
if ((from & mask) != from) |
||||
|
break; |
||||
|
prefix--; |
||||
|
} |
||||
|
|
||||
|
byte max = (byte) (32 - Math.floor(Math.log(to - from + 1) / Math.log(2))); |
||||
|
if (prefix < max) |
||||
|
prefix = max; |
||||
|
|
||||
|
listResult.add(new CIDR(long2inet(from), prefix)); |
||||
|
|
||||
|
from += Math.pow(2, (32 - prefix)); |
||||
|
} |
||||
|
|
||||
|
for (CIDR cidr : listResult) |
||||
|
Log.i(TAG, cidr.toString()); |
||||
|
|
||||
|
return listResult; |
||||
|
} |
||||
|
|
||||
|
private static long prefix2mask(int bits) { |
||||
|
return (0xFFFFFFFF00000000L >> bits) & 0xFFFFFFFFL; |
||||
|
} |
||||
|
|
||||
|
private static long inet2long(InetAddress addr) { |
||||
|
long result = 0; |
||||
|
if (addr != null) |
||||
|
for (byte b : addr.getAddress()) |
||||
|
result = result << 8 | (b & 0xFF); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private static InetAddress long2inet(long addr) { |
||||
|
try { |
||||
|
byte[] b = new byte[4]; |
||||
|
for (int i = b.length - 1; i >= 0; i--) { |
||||
|
b[i] = (byte) (addr & 0xFF); |
||||
|
addr = addr >> 8; |
||||
|
} |
||||
|
return InetAddress.getByAddress(b); |
||||
|
} catch (UnknownHostException ignore) { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static InetAddress minus1(InetAddress addr) { |
||||
|
return long2inet(inet2long(addr) - 1); |
||||
|
} |
||||
|
|
||||
|
public static InetAddress plus1(InetAddress addr) { |
||||
|
return long2inet(inet2long(addr) + 1); |
||||
|
} |
||||
|
|
||||
|
public static class CIDR implements Comparable<CIDR> { |
||||
|
public InetAddress address; |
||||
|
public int prefix; |
||||
|
|
||||
|
public CIDR(InetAddress address, int prefix) { |
||||
|
this.address = address; |
||||
|
this.prefix = prefix; |
||||
|
} |
||||
|
|
||||
|
public CIDR(String ip, int prefix) { |
||||
|
try { |
||||
|
this.address = InetAddress.getByName(ip); |
||||
|
this.prefix = prefix; |
||||
|
} catch (UnknownHostException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public InetAddress getStart() { |
||||
|
return long2inet(inet2long(this.address) & prefix2mask(this.prefix)); |
||||
|
} |
||||
|
|
||||
|
public InetAddress getEnd() { |
||||
|
return long2inet((inet2long(this.address) & prefix2mask(this.prefix)) + (1L << (32 - this.prefix)) - 1); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return address.getHostAddress() + "/" + prefix + "=" + getStart().getHostAddress() + "..." + getEnd().getHostAddress(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int compareTo(@NonNull CIDR other) { |
||||
|
Long lcidr = IPUtil.inet2long(this.address); |
||||
|
Long lother = IPUtil.inet2long(other.address); |
||||
|
return lcidr.compareTo(lother); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,42 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
public class Packet { |
||||
|
public long time; |
||||
|
public int version; |
||||
|
public int protocol; |
||||
|
public String flags; |
||||
|
public String saddr; |
||||
|
public int sport; |
||||
|
public String daddr; |
||||
|
public int dport; |
||||
|
public String data; |
||||
|
public int uid; |
||||
|
public boolean allowed; |
||||
|
|
||||
|
public Packet() { |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "uid=" + uid + " v" + version + " p" + protocol + " " + daddr + "/" + dport; |
||||
|
} |
||||
|
} |
@ -0,0 +1,132 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.BroadcastReceiver; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.os.Build; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
|
||||
|
public class ReceiverAutostart extends BroadcastReceiver { |
||||
|
private static final String TAG = "NetGuard.Receiver"; |
||||
|
|
||||
|
@Override |
||||
|
public void onReceive(final Context context, Intent intent) { |
||||
|
Log.i(TAG, "Received " + intent); |
||||
|
Util.logExtras(intent); |
||||
|
|
||||
|
String action = (intent == null ? null : intent.getAction()); |
||||
|
if (Intent.ACTION_BOOT_COMPLETED.equals(action) || Intent.ACTION_MY_PACKAGE_REPLACED.equals(action)) |
||||
|
try { |
||||
|
// Upgrade settings |
||||
|
upgrade(true, context); |
||||
|
|
||||
|
// Start service |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
if (prefs.getBoolean("enabled", false)) |
||||
|
ServiceSinkhole.start("receiver", context); |
||||
|
else if (prefs.getBoolean("show_stats", false)) |
||||
|
ServiceSinkhole.run("receiver", context); |
||||
|
|
||||
|
if (Util.isInteractive(context)) |
||||
|
ServiceSinkhole.reloadStats("receiver", context); |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void upgrade(boolean initialized, Context context) { |
||||
|
synchronized (context.getApplicationContext()) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
int oldVersion = prefs.getInt("version", -1); |
||||
|
int newVersion = Util.getSelfVersionCode(context); |
||||
|
if (oldVersion == newVersion) |
||||
|
return; |
||||
|
Log.i(TAG, "Upgrading from version " + oldVersion + " to " + newVersion); |
||||
|
|
||||
|
SharedPreferences.Editor editor = prefs.edit(); |
||||
|
|
||||
|
if (initialized) { |
||||
|
if (oldVersion < 38) { |
||||
|
Log.i(TAG, "Converting screen wifi/mobile"); |
||||
|
editor.putBoolean("screen_wifi", prefs.getBoolean("unused", false)); |
||||
|
editor.putBoolean("screen_other", prefs.getBoolean("unused", false)); |
||||
|
editor.remove("unused"); |
||||
|
|
||||
|
SharedPreferences unused = context.getSharedPreferences("unused", Context.MODE_PRIVATE); |
||||
|
SharedPreferences screen_wifi = context.getSharedPreferences("screen_wifi", Context.MODE_PRIVATE); |
||||
|
SharedPreferences screen_other = context.getSharedPreferences("screen_other", Context.MODE_PRIVATE); |
||||
|
|
||||
|
Map<String, ?> punused = unused.getAll(); |
||||
|
SharedPreferences.Editor edit_screen_wifi = screen_wifi.edit(); |
||||
|
SharedPreferences.Editor edit_screen_other = screen_other.edit(); |
||||
|
for (String key : punused.keySet()) { |
||||
|
edit_screen_wifi.putBoolean(key, (Boolean) punused.get(key)); |
||||
|
edit_screen_other.putBoolean(key, (Boolean) punused.get(key)); |
||||
|
} |
||||
|
edit_screen_wifi.apply(); |
||||
|
edit_screen_other.apply(); |
||||
|
|
||||
|
} else if (oldVersion <= 2017032112) |
||||
|
editor.remove("ip6"); |
||||
|
|
||||
|
} else { |
||||
|
Log.i(TAG, "Initializing sdk=" + Build.VERSION.SDK_INT); |
||||
|
editor.putBoolean("filter_udp", true); |
||||
|
editor.putBoolean("whitelist_wifi", false); |
||||
|
editor.putBoolean("whitelist_other", false); |
||||
|
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) |
||||
|
editor.putBoolean("filter", true); // Optional |
||||
|
} |
||||
|
|
||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) |
||||
|
editor.putBoolean("filter", true); // Mandatory |
||||
|
|
||||
|
if (!Util.canFilter(context)) { |
||||
|
editor.putBoolean("log_app", false); |
||||
|
editor.putBoolean("filter", false); |
||||
|
} |
||||
|
|
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { |
||||
|
editor.remove("show_top"); |
||||
|
if ("data".equals(prefs.getString("sort", "name"))) |
||||
|
editor.remove("sort"); |
||||
|
} |
||||
|
|
||||
|
if (Util.isPlayStoreInstall(context)) { |
||||
|
editor.remove("update_check"); |
||||
|
editor.remove("use_hosts"); |
||||
|
editor.remove("hosts_url"); |
||||
|
} |
||||
|
|
||||
|
if (!Util.isDebuggable(context)) |
||||
|
editor.remove("loglevel"); |
||||
|
|
||||
|
editor.putInt("version", newVersion); |
||||
|
editor.apply(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,50 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.BroadcastReceiver; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import androidx.core.app.NotificationManagerCompat; |
||||
|
|
||||
|
public class ReceiverPackageRemoved extends BroadcastReceiver { |
||||
|
private static final String TAG = "NetGuard.Receiver"; |
||||
|
|
||||
|
@Override |
||||
|
public void onReceive(final Context context, Intent intent) { |
||||
|
Log.i(TAG, "Received " + intent); |
||||
|
Util.logExtras(intent); |
||||
|
|
||||
|
String action = (intent == null ? null : intent.getAction()); |
||||
|
if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) { |
||||
|
int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); |
||||
|
if (uid > 0) { |
||||
|
DatabaseHelper dh = DatabaseHelper.getInstance(context); |
||||
|
dh.clearLog(uid); |
||||
|
dh.clearAccess(uid, false); |
||||
|
|
||||
|
NotificationManagerCompat.from(context).cancel(uid); // installed notification |
||||
|
NotificationManagerCompat.from(context).cancel(uid + 10000); // access notification |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import java.text.DateFormat; |
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
public class ResourceRecord { |
||||
|
public long Time; |
||||
|
public String QName; |
||||
|
public String AName; |
||||
|
public String Resource; |
||||
|
public int TTL; |
||||
|
|
||||
|
private static DateFormat formatter = SimpleDateFormat.getDateTimeInstance(); |
||||
|
|
||||
|
public ResourceRecord() { |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return formatter.format(new Date(Time).getTime()) + |
||||
|
" Q " + QName + |
||||
|
" A " + AName + |
||||
|
" R " + Resource + |
||||
|
" TTL " + TTL + |
||||
|
" " + formatter.format(new Date(Time + TTL * 1000L).getTime()); |
||||
|
} |
||||
|
} |
@ -0,0 +1,453 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.content.pm.ApplicationInfo; |
||||
|
import android.content.pm.PackageInfo; |
||||
|
import android.content.pm.PackageManager; |
||||
|
import android.content.res.XmlResourceParser; |
||||
|
import android.database.Cursor; |
||||
|
import android.os.Build; |
||||
|
import android.os.Process; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
import org.xmlpull.v1.XmlPullParser; |
||||
|
|
||||
|
import java.text.Collator; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.Collections; |
||||
|
import java.util.Comparator; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Locale; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
public class Rule { |
||||
|
private static final String TAG = "NetGuard.Rule"; |
||||
|
|
||||
|
public int uid; |
||||
|
public String packageName; |
||||
|
public int icon; |
||||
|
public String name; |
||||
|
public String version; |
||||
|
public boolean system; |
||||
|
public boolean internet; |
||||
|
public boolean enabled; |
||||
|
public boolean pkg = true; |
||||
|
|
||||
|
public boolean wifi_default = false; |
||||
|
public boolean other_default = false; |
||||
|
public boolean screen_wifi_default = false; |
||||
|
public boolean screen_other_default = false; |
||||
|
public boolean roaming_default = false; |
||||
|
|
||||
|
public boolean wifi_blocked = false; |
||||
|
public boolean other_blocked = false; |
||||
|
public boolean screen_wifi = false; |
||||
|
public boolean screen_other = false; |
||||
|
public boolean roaming = false; |
||||
|
public boolean lockdown = false; |
||||
|
|
||||
|
public boolean apply = true; |
||||
|
public boolean notify = true; |
||||
|
|
||||
|
public boolean relateduids = false; |
||||
|
public String[] related = null; |
||||
|
|
||||
|
public long hosts; |
||||
|
public boolean changed; |
||||
|
|
||||
|
public boolean expanded = false; |
||||
|
|
||||
|
private static List<PackageInfo> cachePackageInfo = null; |
||||
|
private static Map<PackageInfo, String> cacheLabel = new HashMap<>(); |
||||
|
private static Map<String, Boolean> cacheSystem = new HashMap<>(); |
||||
|
private static Map<String, Boolean> cacheInternet = new HashMap<>(); |
||||
|
private static Map<PackageInfo, Boolean> cacheEnabled = new HashMap<>(); |
||||
|
|
||||
|
private static List<PackageInfo> getPackages(Context context) { |
||||
|
if (cachePackageInfo == null) { |
||||
|
PackageManager pm = context.getPackageManager(); |
||||
|
cachePackageInfo = pm.getInstalledPackages(0); |
||||
|
} |
||||
|
return new ArrayList<>(cachePackageInfo); |
||||
|
} |
||||
|
|
||||
|
private static String getLabel(PackageInfo info, Context context) { |
||||
|
if (!cacheLabel.containsKey(info)) { |
||||
|
PackageManager pm = context.getPackageManager(); |
||||
|
cacheLabel.put(info, info.applicationInfo.loadLabel(pm).toString()); |
||||
|
} |
||||
|
return cacheLabel.get(info); |
||||
|
} |
||||
|
|
||||
|
private static boolean isSystem(String packageName, Context context) { |
||||
|
if (!cacheSystem.containsKey(packageName)) |
||||
|
cacheSystem.put(packageName, Util.isSystem(packageName, context)); |
||||
|
return cacheSystem.get(packageName); |
||||
|
} |
||||
|
|
||||
|
private static boolean hasInternet(String packageName, Context context) { |
||||
|
if (!cacheInternet.containsKey(packageName)) |
||||
|
cacheInternet.put(packageName, Util.hasInternet(packageName, context)); |
||||
|
return cacheInternet.get(packageName); |
||||
|
} |
||||
|
|
||||
|
private static boolean isEnabled(PackageInfo info, Context context) { |
||||
|
if (!cacheEnabled.containsKey(info)) |
||||
|
cacheEnabled.put(info, Util.isEnabled(info, context)); |
||||
|
return cacheEnabled.get(info); |
||||
|
} |
||||
|
|
||||
|
public static void clearCache(Context context) { |
||||
|
Log.i(TAG, "Clearing cache"); |
||||
|
synchronized (context.getApplicationContext()) { |
||||
|
cachePackageInfo = null; |
||||
|
cacheLabel.clear(); |
||||
|
cacheSystem.clear(); |
||||
|
cacheInternet.clear(); |
||||
|
cacheEnabled.clear(); |
||||
|
} |
||||
|
|
||||
|
DatabaseHelper dh = DatabaseHelper.getInstance(context); |
||||
|
dh.clearApps(); |
||||
|
} |
||||
|
|
||||
|
private Rule(DatabaseHelper dh, PackageInfo info, Context context) { |
||||
|
this.uid = info.applicationInfo.uid; |
||||
|
this.packageName = info.packageName; |
||||
|
this.icon = info.applicationInfo.icon; |
||||
|
this.version = info.versionName; |
||||
|
if (info.applicationInfo.uid == 0) { |
||||
|
this.name = context.getString(R.string.title_root); |
||||
|
this.system = true; |
||||
|
this.internet = true; |
||||
|
this.enabled = true; |
||||
|
this.pkg = false; |
||||
|
} else if (info.applicationInfo.uid == 1013) { |
||||
|
this.name = context.getString(R.string.title_mediaserver); |
||||
|
this.system = true; |
||||
|
this.internet = true; |
||||
|
this.enabled = true; |
||||
|
this.pkg = false; |
||||
|
} else if (info.applicationInfo.uid == 1020) { |
||||
|
this.name = "MulticastDNSResponder"; |
||||
|
this.system = true; |
||||
|
this.internet = true; |
||||
|
this.enabled = true; |
||||
|
this.pkg = false; |
||||
|
} else if (info.applicationInfo.uid == 1021) { |
||||
|
this.name = context.getString(R.string.title_gpsdaemon); |
||||
|
this.system = true; |
||||
|
this.internet = true; |
||||
|
this.enabled = true; |
||||
|
this.pkg = false; |
||||
|
} else if (info.applicationInfo.uid == 1051) { |
||||
|
this.name = context.getString(R.string.title_dnsdaemon); |
||||
|
this.system = true; |
||||
|
this.internet = true; |
||||
|
this.enabled = true; |
||||
|
this.pkg = false; |
||||
|
} else if (info.applicationInfo.uid == 9999) { |
||||
|
this.name = context.getString(R.string.title_nobody); |
||||
|
this.system = true; |
||||
|
this.internet = true; |
||||
|
this.enabled = true; |
||||
|
this.pkg = false; |
||||
|
} else { |
||||
|
Cursor cursor = null; |
||||
|
try { |
||||
|
cursor = dh.getApp(this.packageName); |
||||
|
if (cursor.moveToNext()) { |
||||
|
this.name = cursor.getString(cursor.getColumnIndex("label")); |
||||
|
this.system = cursor.getInt(cursor.getColumnIndex("system")) > 0; |
||||
|
this.internet = cursor.getInt(cursor.getColumnIndex("internet")) > 0; |
||||
|
this.enabled = cursor.getInt(cursor.getColumnIndex("enabled")) > 0; |
||||
|
} else { |
||||
|
this.name = getLabel(info, context); |
||||
|
this.system = isSystem(info.packageName, context); |
||||
|
this.internet = hasInternet(info.packageName, context); |
||||
|
this.enabled = isEnabled(info, context); |
||||
|
|
||||
|
dh.addApp(this.packageName, this.name, this.system, this.internet, this.enabled); |
||||
|
} |
||||
|
} finally { |
||||
|
if (cursor != null) |
||||
|
cursor.close(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static List<Rule> getRules(final boolean all, Context context) { |
||||
|
synchronized (context.getApplicationContext()) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
SharedPreferences wifi = context.getSharedPreferences("wifi", Context.MODE_PRIVATE); |
||||
|
SharedPreferences other = context.getSharedPreferences("other", Context.MODE_PRIVATE); |
||||
|
SharedPreferences screen_wifi = context.getSharedPreferences("screen_wifi", Context.MODE_PRIVATE); |
||||
|
SharedPreferences screen_other = context.getSharedPreferences("screen_other", Context.MODE_PRIVATE); |
||||
|
SharedPreferences roaming = context.getSharedPreferences("roaming", Context.MODE_PRIVATE); |
||||
|
SharedPreferences lockdown = context.getSharedPreferences("lockdown", Context.MODE_PRIVATE); |
||||
|
SharedPreferences apply = context.getSharedPreferences("apply", Context.MODE_PRIVATE); |
||||
|
SharedPreferences notify = context.getSharedPreferences("notify", Context.MODE_PRIVATE); |
||||
|
|
||||
|
// Get settings |
||||
|
boolean default_wifi = prefs.getBoolean("whitelist_wifi", true); |
||||
|
boolean default_other = prefs.getBoolean("whitelist_other", true); |
||||
|
boolean default_screen_wifi = prefs.getBoolean("screen_wifi", false); |
||||
|
boolean default_screen_other = prefs.getBoolean("screen_other", false); |
||||
|
boolean default_roaming = prefs.getBoolean("whitelist_roaming", true); |
||||
|
|
||||
|
boolean manage_system = prefs.getBoolean("manage_system", false); |
||||
|
boolean screen_on = prefs.getBoolean("screen_on", true); |
||||
|
boolean show_user = prefs.getBoolean("show_user", true); |
||||
|
boolean show_system = prefs.getBoolean("show_system", false); |
||||
|
boolean show_nointernet = prefs.getBoolean("show_nointernet", true); |
||||
|
boolean show_disabled = prefs.getBoolean("show_disabled", true); |
||||
|
|
||||
|
default_screen_wifi = default_screen_wifi && screen_on; |
||||
|
default_screen_other = default_screen_other && screen_on; |
||||
|
|
||||
|
// Get predefined rules |
||||
|
Map<String, Boolean> pre_wifi_blocked = new HashMap<>(); |
||||
|
Map<String, Boolean> pre_other_blocked = new HashMap<>(); |
||||
|
Map<String, Boolean> pre_roaming = new HashMap<>(); |
||||
|
Map<String, String[]> pre_related = new HashMap<>(); |
||||
|
Map<String, Boolean> pre_system = new HashMap<>(); |
||||
|
try { |
||||
|
XmlResourceParser xml = context.getResources().getXml(R.xml.predefined); |
||||
|
int eventType = xml.getEventType(); |
||||
|
while (eventType != XmlPullParser.END_DOCUMENT) { |
||||
|
if (eventType == XmlPullParser.START_TAG) |
||||
|
if ("wifi".equals(xml.getName())) { |
||||
|
String pkg = xml.getAttributeValue(null, "package"); |
||||
|
boolean pblocked = xml.getAttributeBooleanValue(null, "blocked", false); |
||||
|
pre_wifi_blocked.put(pkg, pblocked); |
||||
|
|
||||
|
} else if ("other".equals(xml.getName())) { |
||||
|
String pkg = xml.getAttributeValue(null, "package"); |
||||
|
boolean pblocked = xml.getAttributeBooleanValue(null, "blocked", false); |
||||
|
boolean proaming = xml.getAttributeBooleanValue(null, "roaming", default_roaming); |
||||
|
pre_other_blocked.put(pkg, pblocked); |
||||
|
pre_roaming.put(pkg, proaming); |
||||
|
|
||||
|
} else if ("relation".equals(xml.getName())) { |
||||
|
String pkg = xml.getAttributeValue(null, "package"); |
||||
|
String[] rel = xml.getAttributeValue(null, "related").split(","); |
||||
|
pre_related.put(pkg, rel); |
||||
|
|
||||
|
} else if ("type".equals(xml.getName())) { |
||||
|
String pkg = xml.getAttributeValue(null, "package"); |
||||
|
boolean system = xml.getAttributeBooleanValue(null, "system", true); |
||||
|
pre_system.put(pkg, system); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
eventType = xml.next(); |
||||
|
} |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
|
||||
|
// Build rule list |
||||
|
List<Rule> listRules = new ArrayList<>(); |
||||
|
List<PackageInfo> listPI = getPackages(context); |
||||
|
|
||||
|
int userId = Process.myUid() / 100000; |
||||
|
|
||||
|
// Add root |
||||
|
PackageInfo root = new PackageInfo(); |
||||
|
root.packageName = "root"; |
||||
|
root.versionCode = Build.VERSION.SDK_INT; |
||||
|
root.versionName = Build.VERSION.RELEASE; |
||||
|
root.applicationInfo = new ApplicationInfo(); |
||||
|
root.applicationInfo.uid = 0; |
||||
|
root.applicationInfo.icon = 0; |
||||
|
listPI.add(root); |
||||
|
|
||||
|
// Add mediaserver |
||||
|
PackageInfo media = new PackageInfo(); |
||||
|
media.packageName = "android.media"; |
||||
|
media.versionCode = Build.VERSION.SDK_INT; |
||||
|
media.versionName = Build.VERSION.RELEASE; |
||||
|
media.applicationInfo = new ApplicationInfo(); |
||||
|
media.applicationInfo.uid = 1013 + userId * 100000; |
||||
|
media.applicationInfo.icon = 0; |
||||
|
listPI.add(media); |
||||
|
|
||||
|
// MulticastDNSResponder |
||||
|
PackageInfo mdr = new PackageInfo(); |
||||
|
mdr.packageName = "android.multicast"; |
||||
|
mdr.versionCode = Build.VERSION.SDK_INT; |
||||
|
mdr.versionName = Build.VERSION.RELEASE; |
||||
|
mdr.applicationInfo = new ApplicationInfo(); |
||||
|
mdr.applicationInfo.uid = 1020 + userId * 100000; |
||||
|
mdr.applicationInfo.icon = 0; |
||||
|
listPI.add(mdr); |
||||
|
|
||||
|
// Add GPS daemon |
||||
|
PackageInfo gps = new PackageInfo(); |
||||
|
gps.packageName = "android.gps"; |
||||
|
gps.versionCode = Build.VERSION.SDK_INT; |
||||
|
gps.versionName = Build.VERSION.RELEASE; |
||||
|
gps.applicationInfo = new ApplicationInfo(); |
||||
|
gps.applicationInfo.uid = 1021 + userId * 100000; |
||||
|
gps.applicationInfo.icon = 0; |
||||
|
listPI.add(gps); |
||||
|
|
||||
|
// Add DNS daemon |
||||
|
PackageInfo dns = new PackageInfo(); |
||||
|
dns.packageName = "android.dns"; |
||||
|
dns.versionCode = Build.VERSION.SDK_INT; |
||||
|
dns.versionName = Build.VERSION.RELEASE; |
||||
|
dns.applicationInfo = new ApplicationInfo(); |
||||
|
dns.applicationInfo.uid = 1051 + userId * 100000; |
||||
|
dns.applicationInfo.icon = 0; |
||||
|
listPI.add(dns); |
||||
|
|
||||
|
// Add nobody |
||||
|
PackageInfo nobody = new PackageInfo(); |
||||
|
nobody.packageName = "nobody"; |
||||
|
nobody.versionCode = Build.VERSION.SDK_INT; |
||||
|
nobody.versionName = Build.VERSION.RELEASE; |
||||
|
nobody.applicationInfo = new ApplicationInfo(); |
||||
|
nobody.applicationInfo.uid = 9999; |
||||
|
nobody.applicationInfo.icon = 0; |
||||
|
listPI.add(nobody); |
||||
|
|
||||
|
DatabaseHelper dh = DatabaseHelper.getInstance(context); |
||||
|
for (PackageInfo info : listPI) |
||||
|
try { |
||||
|
// Skip self |
||||
|
if (info.applicationInfo.uid == Process.myUid()) |
||||
|
continue; |
||||
|
|
||||
|
Rule rule = new Rule(dh, info, context); |
||||
|
|
||||
|
if (pre_system.containsKey(info.packageName)) |
||||
|
rule.system = pre_system.get(info.packageName); |
||||
|
if (info.applicationInfo.uid == Process.myUid()) |
||||
|
rule.system = true; |
||||
|
|
||||
|
if (all || |
||||
|
((rule.system ? show_system : show_user) && |
||||
|
(show_nointernet || rule.internet) && |
||||
|
(show_disabled || rule.enabled))) { |
||||
|
|
||||
|
rule.wifi_default = (pre_wifi_blocked.containsKey(info.packageName) ? pre_wifi_blocked.get(info.packageName) : default_wifi); |
||||
|
rule.other_default = (pre_other_blocked.containsKey(info.packageName) ? pre_other_blocked.get(info.packageName) : default_other); |
||||
|
rule.screen_wifi_default = default_screen_wifi; |
||||
|
rule.screen_other_default = default_screen_other; |
||||
|
rule.roaming_default = (pre_roaming.containsKey(info.packageName) ? pre_roaming.get(info.packageName) : default_roaming); |
||||
|
|
||||
|
rule.wifi_blocked = (!(rule.system && !manage_system) && wifi.getBoolean(info.packageName, rule.wifi_default)); |
||||
|
rule.other_blocked = (!(rule.system && !manage_system) && other.getBoolean(info.packageName, rule.other_default)); |
||||
|
rule.screen_wifi = screen_wifi.getBoolean(info.packageName, rule.screen_wifi_default) && screen_on; |
||||
|
rule.screen_other = screen_other.getBoolean(info.packageName, rule.screen_other_default) && screen_on; |
||||
|
rule.roaming = roaming.getBoolean(info.packageName, rule.roaming_default); |
||||
|
rule.lockdown = lockdown.getBoolean(info.packageName, false); |
||||
|
|
||||
|
rule.apply = apply.getBoolean(info.packageName, true); |
||||
|
rule.notify = notify.getBoolean(info.packageName, true); |
||||
|
|
||||
|
// Related packages |
||||
|
List<String> listPkg = new ArrayList<>(); |
||||
|
if (pre_related.containsKey(info.packageName)) |
||||
|
listPkg.addAll(Arrays.asList(pre_related.get(info.packageName))); |
||||
|
for (PackageInfo pi : listPI) |
||||
|
if (pi.applicationInfo.uid == rule.uid && !pi.packageName.equals(rule.packageName)) { |
||||
|
rule.relateduids = true; |
||||
|
listPkg.add(pi.packageName); |
||||
|
} |
||||
|
rule.related = listPkg.toArray(new String[0]); |
||||
|
|
||||
|
rule.hosts = dh.getHostCount(rule.uid, true); |
||||
|
|
||||
|
rule.updateChanged(default_wifi, default_other, default_roaming); |
||||
|
|
||||
|
listRules.add(rule); |
||||
|
} |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
|
||||
|
// Sort rule list |
||||
|
final Collator collator = Collator.getInstance(Locale.getDefault()); |
||||
|
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc |
||||
|
|
||||
|
String sort = prefs.getString("sort", "name"); |
||||
|
if ("uid".equals(sort)) |
||||
|
Collections.sort(listRules, new Comparator<Rule>() { |
||||
|
@Override |
||||
|
public int compare(Rule rule, Rule other) { |
||||
|
if (rule.uid < other.uid) |
||||
|
return -1; |
||||
|
else if (rule.uid > other.uid) |
||||
|
return 1; |
||||
|
else { |
||||
|
int i = collator.compare(rule.name, other.name); |
||||
|
return (i == 0 ? rule.packageName.compareTo(other.packageName) : i); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
else |
||||
|
Collections.sort(listRules, new Comparator<Rule>() { |
||||
|
@Override |
||||
|
public int compare(Rule rule, Rule other) { |
||||
|
if (all || rule.changed == other.changed) { |
||||
|
int i = collator.compare(rule.name, other.name); |
||||
|
return (i == 0 ? rule.packageName.compareTo(other.packageName) : i); |
||||
|
} |
||||
|
return (rule.changed ? -1 : 1); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return listRules; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void updateChanged(boolean default_wifi, boolean default_other, boolean default_roaming) { |
||||
|
changed = (wifi_blocked != default_wifi || |
||||
|
(other_blocked != default_other) || |
||||
|
(wifi_blocked && screen_wifi != screen_wifi_default) || |
||||
|
(other_blocked && screen_other != screen_other_default) || |
||||
|
((!other_blocked || screen_other) && roaming != default_roaming) || |
||||
|
hosts > 0 || lockdown || !apply); |
||||
|
} |
||||
|
|
||||
|
public void updateChanged(Context context) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
boolean screen_on = prefs.getBoolean("screen_on", false); |
||||
|
boolean default_wifi = prefs.getBoolean("whitelist_wifi", true) && screen_on; |
||||
|
boolean default_other = prefs.getBoolean("whitelist_other", true) && screen_on; |
||||
|
boolean default_roaming = prefs.getBoolean("whitelist_roaming", true); |
||||
|
updateChanged(default_wifi, default_other, default_roaming); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
// This is used in the port forwarding dialog application selector |
||||
|
return this.name; |
||||
|
} |
||||
|
} |
@ -0,0 +1,146 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.app.IntentService; |
||||
|
import android.app.Notification; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import androidx.core.app.NotificationCompat; |
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FileOutputStream; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
import java.io.OutputStream; |
||||
|
import java.net.HttpURLConnection; |
||||
|
import java.net.URL; |
||||
|
import java.net.URLConnection; |
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
public class ServiceExternal extends IntentService { |
||||
|
private static final String TAG = "NetGuard.External"; |
||||
|
private static final String ACTION_DOWNLOAD_HOSTS_FILE = "eu.faircode.netguard.DOWNLOAD_HOSTS_FILE"; |
||||
|
|
||||
|
// am startservice -a eu.faircode.netguard.DOWNLOAD_HOSTS_FILE |
||||
|
|
||||
|
public ServiceExternal() { |
||||
|
super(TAG); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onHandleIntent(Intent intent) { |
||||
|
try { |
||||
|
startForeground(ServiceSinkhole.NOTIFY_EXTERNAL, getForegroundNotification(this)); |
||||
|
|
||||
|
Log.i(TAG, "Received " + intent); |
||||
|
Util.logExtras(intent); |
||||
|
|
||||
|
if (ACTION_DOWNLOAD_HOSTS_FILE.equals(intent.getAction())) { |
||||
|
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
|
||||
|
String hosts_url = prefs.getString("hosts_url", null); |
||||
|
if ("https://www.netguard.me/hosts".equals(hosts_url)) |
||||
|
hosts_url = BuildConfig.HOSTS_FILE_URI; |
||||
|
|
||||
|
File tmp = new File(getFilesDir(), "hosts.tmp"); |
||||
|
File hosts = new File(getFilesDir(), "hosts.txt"); |
||||
|
|
||||
|
InputStream in = null; |
||||
|
OutputStream out = null; |
||||
|
URLConnection connection = null; |
||||
|
try { |
||||
|
URL url = new URL(hosts_url); |
||||
|
connection = url.openConnection(); |
||||
|
connection.connect(); |
||||
|
|
||||
|
if (connection instanceof HttpURLConnection) { |
||||
|
HttpURLConnection httpConnection = (HttpURLConnection) connection; |
||||
|
if (httpConnection.getResponseCode() != HttpURLConnection.HTTP_OK) |
||||
|
throw new IOException(httpConnection.getResponseCode() + " " + httpConnection.getResponseMessage()); |
||||
|
} |
||||
|
|
||||
|
int contentLength = connection.getContentLength(); |
||||
|
Log.i(TAG, "Content length=" + contentLength); |
||||
|
in = connection.getInputStream(); |
||||
|
out = new FileOutputStream(tmp); |
||||
|
|
||||
|
long size = 0; |
||||
|
byte buffer[] = new byte[4096]; |
||||
|
int bytes; |
||||
|
while ((bytes = in.read(buffer)) != -1) { |
||||
|
out.write(buffer, 0, bytes); |
||||
|
size += bytes; |
||||
|
} |
||||
|
|
||||
|
Log.i(TAG, "Downloaded size=" + size); |
||||
|
|
||||
|
if (hosts.exists()) |
||||
|
hosts.delete(); |
||||
|
tmp.renameTo(hosts); |
||||
|
|
||||
|
String last = SimpleDateFormat.getDateTimeInstance().format(new Date().getTime()); |
||||
|
prefs.edit().putString("hosts_last_download", last).apply(); |
||||
|
|
||||
|
ServiceSinkhole.reload("hosts file download", this, false); |
||||
|
|
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
|
||||
|
if (tmp.exists()) |
||||
|
tmp.delete(); |
||||
|
} finally { |
||||
|
try { |
||||
|
if (out != null) |
||||
|
out.close(); |
||||
|
} catch (IOException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
try { |
||||
|
if (in != null) |
||||
|
in.close(); |
||||
|
} catch (IOException ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
|
||||
|
if (connection instanceof HttpURLConnection) |
||||
|
((HttpURLConnection) connection).disconnect(); |
||||
|
} |
||||
|
} |
||||
|
} finally { |
||||
|
stopForeground(true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static Notification getForegroundNotification(Context context) { |
||||
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "foreground"); |
||||
|
builder.setSmallIcon(R.drawable.ic_hourglass_empty_white_24dp); |
||||
|
builder.setPriority(NotificationCompat.PRIORITY_MIN); |
||||
|
builder.setCategory(NotificationCompat.CATEGORY_STATUS); |
||||
|
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); |
||||
|
builder.setContentTitle(context.getString(R.string.app_name)); |
||||
|
return builder.build(); |
||||
|
} |
||||
|
} |
3335
NetGuard/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java
File diff suppressed because it is too large
View File
@ -0,0 +1,81 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
import android.annotation.TargetApi; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.graphics.drawable.Icon; |
||||
|
import android.os.Build; |
||||
|
import android.service.quicksettings.Tile; |
||||
|
import android.service.quicksettings.TileService; |
||||
|
import android.util.Log; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
@TargetApi(Build.VERSION_CODES.N) |
||||
|
public class ServiceTileFilter extends TileService implements SharedPreferences.OnSharedPreferenceChangeListener { |
||||
|
private static final String TAG = "NetGuard.TileFilter"; |
||||
|
|
||||
|
public void onStartListening() { |
||||
|
Log.i(TAG, "Start listening"); |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
prefs.registerOnSharedPreferenceChangeListener(this); |
||||
|
update(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { |
||||
|
if ("filter".equals(key)) |
||||
|
update(); |
||||
|
} |
||||
|
|
||||
|
private void update() { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
boolean filter = prefs.getBoolean("filter", false); |
||||
|
Tile tile = getQsTile(); |
||||
|
if (tile != null) { |
||||
|
tile.setState(filter ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); |
||||
|
tile.setIcon(Icon.createWithResource(this, filter ? R.drawable.ic_filter_list_white_24dp : R.drawable.ic_filter_list_white_24dp_60)); |
||||
|
tile.updateTile(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void onStopListening() { |
||||
|
Log.i(TAG, "Stop listening"); |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
prefs.unregisterOnSharedPreferenceChangeListener(this); |
||||
|
} |
||||
|
|
||||
|
public void onClick() { |
||||
|
Log.i(TAG, "Click"); |
||||
|
|
||||
|
if (Util.canFilter(this)) { |
||||
|
if (IAB.isPurchased(ActivityPro.SKU_FILTER, this)) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
prefs.edit().putBoolean("filter", !prefs.getBoolean("filter", false)).apply(); |
||||
|
ServiceSinkhole.reload("tile", this, false); |
||||
|
} else |
||||
|
Toast.makeText(this, R.string.title_pro_feature, Toast.LENGTH_SHORT).show(); |
||||
|
} else |
||||
|
Toast.makeText(this, R.string.msg_unavailable, Toast.LENGTH_SHORT).show(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,80 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
import android.annotation.TargetApi; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.graphics.drawable.Icon; |
||||
|
import android.os.Build; |
||||
|
import android.service.quicksettings.Tile; |
||||
|
import android.service.quicksettings.TileService; |
||||
|
import android.util.Log; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
@TargetApi(Build.VERSION_CODES.N) |
||||
|
public class ServiceTileGraph extends TileService implements SharedPreferences.OnSharedPreferenceChangeListener { |
||||
|
private static final String TAG = "NetGuard.TileGraph"; |
||||
|
|
||||
|
public void onStartListening() { |
||||
|
Log.i(TAG, "Start listening"); |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
prefs.registerOnSharedPreferenceChangeListener(this); |
||||
|
update(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { |
||||
|
if ("show_stats".equals(key)) |
||||
|
update(); |
||||
|
} |
||||
|
|
||||
|
private void update() { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
boolean stats = prefs.getBoolean("show_stats", false); |
||||
|
Tile tile = getQsTile(); |
||||
|
if (tile != null) { |
||||
|
tile.setState(stats ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); |
||||
|
tile.setIcon(Icon.createWithResource(this, stats ? R.drawable.ic_equalizer_white_24dp : R.drawable.ic_equalizer_white_24dp_60)); |
||||
|
tile.updateTile(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void onStopListening() { |
||||
|
Log.i(TAG, "Stop listening"); |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
prefs.unregisterOnSharedPreferenceChangeListener(this); |
||||
|
} |
||||
|
|
||||
|
public void onClick() { |
||||
|
Log.i(TAG, "Click"); |
||||
|
|
||||
|
// Check state |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
boolean stats = !prefs.getBoolean("show_stats", false); |
||||
|
if (stats && !IAB.isPurchased(ActivityPro.SKU_SPEED, this)) |
||||
|
Toast.makeText(this, R.string.title_pro_feature, Toast.LENGTH_SHORT).show(); |
||||
|
else |
||||
|
prefs.edit().putBoolean("show_stats", stats).apply(); |
||||
|
ServiceSinkhole.reloadStats("tile", this); |
||||
|
} |
||||
|
} |
@ -0,0 +1,75 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
import android.annotation.TargetApi; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.graphics.drawable.Icon; |
||||
|
import android.os.Build; |
||||
|
import android.service.quicksettings.Tile; |
||||
|
import android.service.quicksettings.TileService; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
@TargetApi(Build.VERSION_CODES.N) |
||||
|
public class ServiceTileLockdown extends TileService implements SharedPreferences.OnSharedPreferenceChangeListener { |
||||
|
private static final String TAG = "NetGuard.TileLockdown"; |
||||
|
|
||||
|
public void onStartListening() { |
||||
|
Log.i(TAG, "Start listening"); |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
prefs.registerOnSharedPreferenceChangeListener(this); |
||||
|
update(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { |
||||
|
if ("lockdown".equals(key)) |
||||
|
update(); |
||||
|
} |
||||
|
|
||||
|
private void update() { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
boolean lockdown = prefs.getBoolean("lockdown", false); |
||||
|
Tile tile = getQsTile(); |
||||
|
if (tile != null) { |
||||
|
tile.setState(lockdown ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); |
||||
|
tile.setIcon(Icon.createWithResource(this, lockdown ? R.drawable.ic_lock_outline_white_24dp : R.drawable.ic_lock_outline_white_24dp_60)); |
||||
|
tile.updateTile(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void onStopListening() { |
||||
|
Log.i(TAG, "Stop listening"); |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
prefs.unregisterOnSharedPreferenceChangeListener(this); |
||||
|
} |
||||
|
|
||||
|
public void onClick() { |
||||
|
Log.i(TAG, "Click"); |
||||
|
|
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
prefs.edit().putBoolean("lockdown", !prefs.getBoolean("lockdown", false)).apply(); |
||||
|
ServiceSinkhole.reload("tile", this, false); |
||||
|
WidgetLockdown.updateWidgets(this); |
||||
|
} |
||||
|
} |
@ -0,0 +1,103 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
import android.annotation.TargetApi; |
||||
|
import android.app.AlarmManager; |
||||
|
import android.app.PendingIntent; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.graphics.drawable.Icon; |
||||
|
import android.os.Build; |
||||
|
import android.service.quicksettings.Tile; |
||||
|
import android.service.quicksettings.TileService; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
|
||||
|
@TargetApi(Build.VERSION_CODES.N) |
||||
|
public class ServiceTileMain extends TileService implements SharedPreferences.OnSharedPreferenceChangeListener { |
||||
|
private static final String TAG = "NetGuard.TileMain"; |
||||
|
|
||||
|
public void onStartListening() { |
||||
|
Log.i(TAG, "Start listening"); |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
prefs.registerOnSharedPreferenceChangeListener(this); |
||||
|
update(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { |
||||
|
if ("enabled".equals(key)) |
||||
|
update(); |
||||
|
} |
||||
|
|
||||
|
private void update() { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
boolean enabled = prefs.getBoolean("enabled", false); |
||||
|
Tile tile = getQsTile(); |
||||
|
if (tile != null) { |
||||
|
tile.setState(enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); |
||||
|
tile.setIcon(Icon.createWithResource(this, enabled ? R.drawable.ic_security_white_24dp : R.drawable.ic_security_white_24dp_60)); |
||||
|
tile.updateTile(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void onStopListening() { |
||||
|
Log.i(TAG, "Stop listening"); |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
prefs.unregisterOnSharedPreferenceChangeListener(this); |
||||
|
} |
||||
|
|
||||
|
public void onClick() { |
||||
|
Log.i(TAG, "Click"); |
||||
|
|
||||
|
// Cancel set alarm |
||||
|
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); |
||||
|
Intent intent = new Intent(WidgetAdmin.INTENT_ON); |
||||
|
intent.setPackage(getPackageName()); |
||||
|
PendingIntent pi = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); |
||||
|
am.cancel(pi); |
||||
|
|
||||
|
// Check state |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
boolean enabled = !prefs.getBoolean("enabled", false); |
||||
|
prefs.edit().putBoolean("enabled", enabled).apply(); |
||||
|
if (enabled) |
||||
|
ServiceSinkhole.start("tile", this); |
||||
|
else { |
||||
|
ServiceSinkhole.stop("tile", this, false); |
||||
|
|
||||
|
// Auto enable |
||||
|
int auto = Integer.parseInt(prefs.getString("auto_enable", "0")); |
||||
|
if (auto > 0) { |
||||
|
Log.i(TAG, "Scheduling enabled after minutes=" + auto); |
||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) |
||||
|
am.set(AlarmManager.RTC_WAKEUP, new Date().getTime() + auto * 60 * 1000L, pi); |
||||
|
else |
||||
|
am.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, new Date().getTime() + auto * 60 * 1000L, pi); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.util.AttributeSet; |
||||
|
|
||||
|
// https://code.google.com/p/android/issues/detail?id=26194 |
||||
|
|
||||
|
public class SwitchPreference extends android.preference.SwitchPreference { |
||||
|
public SwitchPreference(Context context) { |
||||
|
this(context, null); |
||||
|
} |
||||
|
|
||||
|
public SwitchPreference(Context context, AttributeSet attrs) { |
||||
|
this(context, attrs, android.R.attr.switchPreferenceStyle); |
||||
|
} |
||||
|
|
||||
|
public SwitchPreference(Context context, AttributeSet attrs, int defStyle) { |
||||
|
super(context, attrs, defStyle); |
||||
|
} |
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import java.text.DateFormat; |
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
public class Usage { |
||||
|
public long Time; |
||||
|
public int Version; |
||||
|
public int Protocol; |
||||
|
public String DAddr; |
||||
|
public int DPort; |
||||
|
public int Uid; |
||||
|
public long Sent; |
||||
|
public long Received; |
||||
|
|
||||
|
private static DateFormat formatter = SimpleDateFormat.getDateTimeInstance(); |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return formatter.format(new Date(Time).getTime()) + |
||||
|
" v" + Version + " p" + Protocol + |
||||
|
" " + DAddr + "/" + DPort + |
||||
|
" uid " + Uid + |
||||
|
" out " + Sent + " in " + Received; |
||||
|
} |
||||
|
} |
1075
NetGuard/app/src/main/java/eu/faircode/netguard/Util.java
File diff suppressed because it is too large
View File
@ -0,0 +1,50 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
public class Version implements Comparable<Version> { |
||||
|
|
||||
|
private String version; |
||||
|
|
||||
|
public Version(String version) { |
||||
|
this.version = version.replace("-beta", ""); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int compareTo(Version other) { |
||||
|
String[] lhs = this.version.split("\\."); |
||||
|
String[] rhs = other.version.split("\\."); |
||||
|
int length = Math.max(lhs.length, rhs.length); |
||||
|
for (int i = 0; i < length; i++) { |
||||
|
int vLhs = (i < lhs.length ? Integer.parseInt(lhs[i]) : 0); |
||||
|
int vRhs = (i < rhs.length ? Integer.parseInt(rhs[i]) : 0); |
||||
|
if (vLhs < vRhs) |
||||
|
return -1; |
||||
|
if (vLhs > vRhs) |
||||
|
return 1; |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return version; |
||||
|
} |
||||
|
} |
@ -0,0 +1,99 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.app.AlarmManager; |
||||
|
import android.app.PendingIntent; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.os.Build; |
||||
|
import android.os.VibrationEffect; |
||||
|
import android.os.Vibrator; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
|
||||
|
public class WidgetAdmin extends ReceiverAutostart { |
||||
|
private static final String TAG = "NetGuard.Widget"; |
||||
|
|
||||
|
public static final String INTENT_ON = "eu.faircode.netguard.ON"; |
||||
|
public static final String INTENT_OFF = "eu.faircode.netguard.OFF"; |
||||
|
|
||||
|
public static final String INTENT_LOCKDOWN_ON = "eu.faircode.netguard.LOCKDOWN_ON"; |
||||
|
public static final String INTENT_LOCKDOWN_OFF = "eu.faircode.netguard.LOCKDOWN_OFF"; |
||||
|
|
||||
|
@Override |
||||
|
public void onReceive(Context context, Intent intent) { |
||||
|
super.onReceive(context, intent); |
||||
|
|
||||
|
Log.i(TAG, "Received " + intent); |
||||
|
Util.logExtras(intent); |
||||
|
|
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
|
||||
|
// Cancel set alarm |
||||
|
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); |
||||
|
Intent i = new Intent(INTENT_ON); |
||||
|
i.setPackage(context.getPackageName()); |
||||
|
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); |
||||
|
if (INTENT_ON.equals(intent.getAction()) || INTENT_OFF.equals(intent.getAction())) |
||||
|
am.cancel(pi); |
||||
|
|
||||
|
// Vibrate |
||||
|
Vibrator vs = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); |
||||
|
if (vs.hasVibrator()) |
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) |
||||
|
vs.vibrate(VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE)); |
||||
|
else |
||||
|
vs.vibrate(50); |
||||
|
|
||||
|
try { |
||||
|
if (INTENT_ON.equals(intent.getAction()) || INTENT_OFF.equals(intent.getAction())) { |
||||
|
boolean enabled = INTENT_ON.equals(intent.getAction()); |
||||
|
prefs.edit().putBoolean("enabled", enabled).apply(); |
||||
|
if (enabled) |
||||
|
ServiceSinkhole.start("widget", context); |
||||
|
else |
||||
|
ServiceSinkhole.stop("widget", context, false); |
||||
|
|
||||
|
// Auto enable |
||||
|
int auto = Integer.parseInt(prefs.getString("auto_enable", "0")); |
||||
|
if (!enabled && auto > 0) { |
||||
|
Log.i(TAG, "Scheduling enabled after minutes=" + auto); |
||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) |
||||
|
am.set(AlarmManager.RTC_WAKEUP, new Date().getTime() + auto * 60 * 1000L, pi); |
||||
|
else |
||||
|
am.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, new Date().getTime() + auto * 60 * 1000L, pi); |
||||
|
} |
||||
|
|
||||
|
} else if (INTENT_LOCKDOWN_ON.equals(intent.getAction()) || INTENT_LOCKDOWN_OFF.equals(intent.getAction())) { |
||||
|
boolean lockdown = INTENT_LOCKDOWN_ON.equals(intent.getAction()); |
||||
|
prefs.edit().putBoolean("lockdown", lockdown).apply(); |
||||
|
ServiceSinkhole.reload("widget", context, false); |
||||
|
WidgetLockdown.updateWidgets(context); |
||||
|
} |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,70 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.app.PendingIntent; |
||||
|
import android.appwidget.AppWidgetManager; |
||||
|
import android.appwidget.AppWidgetProvider; |
||||
|
import android.content.ComponentName; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.util.Log; |
||||
|
import android.widget.RemoteViews; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
public class WidgetLockdown extends AppWidgetProvider { |
||||
|
private static final String TAG = "NetGuard.WidgetLock"; |
||||
|
|
||||
|
@Override |
||||
|
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { |
||||
|
update(appWidgetIds, appWidgetManager, context); |
||||
|
} |
||||
|
|
||||
|
private static void update(int[] appWidgetIds, AppWidgetManager appWidgetManager, Context context) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
boolean lockdown = prefs.getBoolean("lockdown", false); |
||||
|
|
||||
|
try { |
||||
|
try { |
||||
|
Intent intent = new Intent(lockdown ? WidgetAdmin.INTENT_LOCKDOWN_OFF : WidgetAdmin.INTENT_LOCKDOWN_ON); |
||||
|
intent.setPackage(context.getPackageName()); |
||||
|
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); |
||||
|
for (int id : appWidgetIds) { |
||||
|
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widgetlockdown); |
||||
|
views.setOnClickPendingIntent(R.id.ivEnabled, pi); |
||||
|
views.setImageViewResource(R.id.ivEnabled, lockdown ? R.drawable.ic_lock_outline_white_24dp : R.drawable.ic_lock_open_white_24dp); |
||||
|
appWidgetManager.updateAppWidget(id, views); |
||||
|
} |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void updateWidgets(Context context) { |
||||
|
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); |
||||
|
int appWidgetIds[] = AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, WidgetLockdown.class)); |
||||
|
update(appWidgetIds, appWidgetManager, context); |
||||
|
} |
||||
|
} |
@ -0,0 +1,70 @@ |
|||||
|
package eu.faircode.netguard; |
||||
|
|
||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
import android.app.PendingIntent; |
||||
|
import android.appwidget.AppWidgetManager; |
||||
|
import android.appwidget.AppWidgetProvider; |
||||
|
import android.content.ComponentName; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.util.Log; |
||||
|
import android.widget.RemoteViews; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
public class WidgetMain extends AppWidgetProvider { |
||||
|
private static final String TAG = "NetGuard.Widget"; |
||||
|
|
||||
|
@Override |
||||
|
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { |
||||
|
update(appWidgetIds, appWidgetManager, context); |
||||
|
} |
||||
|
|
||||
|
private static void update(int[] appWidgetIds, AppWidgetManager appWidgetManager, Context context) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
||||
|
boolean enabled = prefs.getBoolean("enabled", false); |
||||
|
|
||||
|
try { |
||||
|
try { |
||||
|
Intent intent = new Intent(enabled ? WidgetAdmin.INTENT_OFF : WidgetAdmin.INTENT_ON); |
||||
|
intent.setPackage(context.getPackageName()); |
||||
|
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); |
||||
|
for (int id : appWidgetIds) { |
||||
|
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widgetmain); |
||||
|
views.setOnClickPendingIntent(R.id.ivEnabled, pi); |
||||
|
views.setImageViewResource(R.id.ivEnabled, enabled ? R.drawable.ic_security_color_24dp : R.drawable.ic_security_white_24dp_60); |
||||
|
appWidgetManager.updateAppWidget(id, views); |
||||
|
} |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} catch (Throwable ex) { |
||||
|
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void updateWidgets(Context context) { |
||||
|
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); |
||||
|
int appWidgetIds[] = AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, WidgetMain.class)); |
||||
|
update(appWidgetIds, appWidgetManager, context); |
||||
|
} |
||||
|
} |
@ -0,0 +1,189 @@ |
|||||
|
// |
||||
|
// Created by conntrack on 4/30/23. |
||||
|
// |
||||
|
|
||||
|
|
||||
|
#include "netguard.h" |
||||
|
|
||||
|
struct ng_session *debug_socket; |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
int open_debug_socket(const struct arguments *args, int epoll_fd) { |
||||
|
|
||||
|
void *saddr; |
||||
|
void *daddr; |
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
|
||||
|
int version = 4; |
||||
|
int uid = 0; |
||||
|
|
||||
|
uint16_t mss = get_default_mss(version); |
||||
|
uint8_t ws = 8; |
||||
|
|
||||
|
int send_window = ntohs(65535); |
||||
|
int sequence_number = ntohs(5000); |
||||
|
|
||||
|
int sport = ntohs(40404); |
||||
|
int dport = ntohs(50508); |
||||
|
|
||||
|
|
||||
|
int packet = 2; |
||||
|
|
||||
|
struct allowed *redirect = NULL; |
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "%d new debug session mss %u ws %u window %u", |
||||
|
packet, mss, ws, send_window << ws); |
||||
|
|
||||
|
// Register session |
||||
|
struct ng_session *s = ng_malloc(sizeof(struct ng_session), "tcp session"); |
||||
|
s->protocol = IPPROTO_TCP; |
||||
|
|
||||
|
s->tcp.time = time(NULL); |
||||
|
s->tcp.uid = uid; |
||||
|
s->tcp.version = version; |
||||
|
s->tcp.mss = mss; |
||||
|
s->tcp.recv_scale = ws; |
||||
|
s->tcp.send_scale = ws; |
||||
|
s->tcp.send_window = ((uint32_t) send_window) << ws; |
||||
|
|
||||
|
|
||||
|
s->tcp.unconfirmed = 0; |
||||
|
s->tcp.remote_seq = (uint32_t) sequence_number; // probably should change hardcoded seq # |
||||
|
//s->tcp.remote_seq = ntohl(tcphdr->seq); // ISN remote |
||||
|
s->tcp.local_seq = (uint32_t) rand(); // ISN local |
||||
|
s->tcp.remote_start = s->tcp.remote_seq; |
||||
|
s->tcp.local_start = s->tcp.local_seq; |
||||
|
s->tcp.acked = 0; |
||||
|
s->tcp.last_keep_alive = 0; |
||||
|
s->tcp.sent = 0; |
||||
|
s->tcp.received = 0; |
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "got to change address.."); |
||||
|
|
||||
|
if (version == 4) { |
||||
|
inet_aton("10.1.10.1", &s->tcp.saddr.ip4); |
||||
|
inet_aton("some_server_ip", &s->tcp.daddr.ip4); |
||||
|
} |
||||
|
|
||||
|
saddr = &s->tcp.saddr.ip4; |
||||
|
daddr = &s->tcp.daddr.ip4; |
||||
|
|
||||
|
inet_ntop(AF_INET, saddr, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, daddr, dest, sizeof(dest)); |
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "new debug IP packet has source: %s, dest: %s", source, dest); |
||||
|
|
||||
|
s->tcp.source = sport; //tcphdr->source; |
||||
|
s->tcp.dest = dport; // tcphdr->dest; |
||||
|
s->tcp.state = TCP_LISTEN; |
||||
|
s->tcp.socks5 = SOCKS5_NONE; |
||||
|
s->tcp.forward = NULL; |
||||
|
s->next = NULL; |
||||
|
|
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "got to data with source:"); |
||||
|
|
||||
|
/* |
||||
|
if (datalen) { |
||||
|
log_android(ANDROID_LOG_WARN, "%s SYN data", packet); |
||||
|
s->tcp.forward = ng_malloc(sizeof(struct segment), "syn segment"); |
||||
|
s->tcp.forward->seq = s->tcp.remote_seq; |
||||
|
s->tcp.forward->len = datalen; |
||||
|
s->tcp.forward->sent = 0; |
||||
|
s->tcp.forward->psh = tcphdr->psh; |
||||
|
s->tcp.forward->data = ng_malloc(datalen, "syn segment data"); |
||||
|
memcpy(s->tcp.forward->data, data, datalen); |
||||
|
s->tcp.forward->next = NULL; |
||||
|
} |
||||
|
*/ |
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "got to open socket with sport: %d, dport %d", sport, dport); |
||||
|
// Open socket |
||||
|
s->socket = open_tcp_socket(args, &s->tcp, redirect); |
||||
|
if (s->socket < 0) { |
||||
|
// Remote might retry |
||||
|
ng_free(s, __FILE__, __LINE__); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
s->tcp.recv_window = get_receive_window(s); |
||||
|
log_android(ANDROID_LOG_DEBUG, "TCP socket %d lport %d", |
||||
|
s->socket, get_local_port(s->socket)); |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
// Monitor events |
||||
|
memset(&s->ev, 0, sizeof(struct epoll_event)); |
||||
|
s->ev.events = EPOLLOUT | EPOLLERR; |
||||
|
s->ev.data.ptr = s; |
||||
|
|
||||
|
|
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "DEBUG adding epoll monitor events: %d", epoll_fd); |
||||
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s->socket, &s->ev)) |
||||
|
log_android(ANDROID_LOG_ERROR, "epoll add tcp error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
|
||||
|
|
||||
|
s->next = args->ctx->ng_session; |
||||
|
//args->ctx->ng_session->next = s; |
||||
|
debug_socket = s; |
||||
|
|
||||
|
|
||||
|
return 1; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
void debug_socket_init(const struct arguments *args, int epoll_fd) { |
||||
|
// TODO: Init the socket. Initialize this socket kind of like what happens |
||||
|
// in tcp.c for open_tcp_socket. |
||||
|
// debug_socket = open() |
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "initalizing debug socket"); |
||||
|
open_debug_socket(args, epoll_fd); |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
void read_debug_socket() { |
||||
|
// TODO: Figure out what needs to be passed as parameters to this function |
||||
|
return ; |
||||
|
} |
||||
|
|
||||
|
void write_debug_socket(const struct arguments *args, const uint8_t *buffer, size_t length) { |
||||
|
// TODO: This function is modelled after write_pcap_ret so I made |
||||
|
// parameters for this function the same since we basically want to do the same thing. |
||||
|
|
||||
|
//struct tcp_session *cur = &debug_socket->tcp; |
||||
|
|
||||
|
// test write to the debug socket |
||||
|
//write_data(args, cur, buffer, length); |
||||
|
|
||||
|
|
||||
|
// Forward to tun |
||||
|
if (write_data(args, &debug_socket->tcp, buffer, length) >= 0) { |
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "Writing to debug socket with length: %d", length); |
||||
|
debug_socket->tcp.local_seq += length; |
||||
|
debug_socket->tcp.unconfirmed++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,143 @@ |
|||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
#include "netguard.h" |
||||
|
|
||||
|
int check_dhcp(const struct arguments *args, const struct udp_session *u, |
||||
|
const uint8_t *data, const size_t datalen) { |
||||
|
|
||||
|
// This is untested |
||||
|
// Android routing of DHCP is erroneous |
||||
|
|
||||
|
log_android(ANDROID_LOG_WARN, "DHCP check"); |
||||
|
|
||||
|
if (datalen < sizeof(struct dhcp_packet)) { |
||||
|
log_android(ANDROID_LOG_WARN, "DHCP packet size %d", datalen); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
const struct dhcp_packet *request = (struct dhcp_packet *) data; |
||||
|
|
||||
|
if (ntohl(request->option_format) != DHCP_OPTION_MAGIC_NUMBER) { |
||||
|
log_android(ANDROID_LOG_WARN, "DHCP invalid magic %x", request->option_format); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (request->htype != 1 || request->hlen != 6) { |
||||
|
log_android(ANDROID_LOG_WARN, "DHCP unknown hardware htype %d hlen %d", |
||||
|
request->htype, request->hlen); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
log_android(ANDROID_LOG_WARN, "DHCP opcode", request->opcode); |
||||
|
|
||||
|
// Discover: source 0.0.0.0:68 destination 255.255.255.255:67 |
||||
|
// Offer: source 10.1.10.1:67 destination 255.255.255.255:68 |
||||
|
// Request: source 0.0.0.0:68 destination 255.255.255.255:67 |
||||
|
// Ack: source: 10.1.10.1 destination: 255.255.255.255 |
||||
|
|
||||
|
if (request->opcode == 1) { // Discover/request |
||||
|
struct dhcp_packet *response = ng_calloc(500, 1, "dhcp"); |
||||
|
|
||||
|
// Hack |
||||
|
inet_pton(AF_INET, "10.1.10.1", (void *) &u->saddr); |
||||
|
|
||||
|
/* |
||||
|
Discover: |
||||
|
DHCP option 53: DHCP Discover |
||||
|
DHCP option 50: 192.168.1.100 requested |
||||
|
DHCP option 55: Parameter Request List: |
||||
|
Request Subnet Mask (1), Router (3), Domain Name (15), Domain Name Server (6) |
||||
|
|
||||
|
Request |
||||
|
DHCP option 53: DHCP Request |
||||
|
DHCP option 50: 192.168.1.100 requested |
||||
|
DHCP option 54: 192.168.1.1 DHCP server. |
||||
|
*/ |
||||
|
|
||||
|
memcpy(response, request, sizeof(struct dhcp_packet)); |
||||
|
response->opcode = (uint8_t) (request->siaddr == 0 ? 2 /* Offer */ : /* Ack */ 4); |
||||
|
response->secs = 0; |
||||
|
response->flags = 0; |
||||
|
memset(&response->ciaddr, 0, sizeof(response->ciaddr)); |
||||
|
inet_pton(AF_INET, "10.1.10.2", &response->yiaddr); |
||||
|
inet_pton(AF_INET, "10.1.10.1", &response->siaddr); |
||||
|
memset(&response->giaddr, 0, sizeof(response->giaddr)); |
||||
|
|
||||
|
// https://tools.ietf.org/html/rfc2132 |
||||
|
uint8_t *options = (uint8_t *) (response + sizeof(struct dhcp_packet)); |
||||
|
|
||||
|
int idx = 0; |
||||
|
*(options + idx++) = 53; // Message type |
||||
|
*(options + idx++) = 1; |
||||
|
*(options + idx++) = (uint8_t) (request->siaddr == 0 ? 2 : 5); |
||||
|
/* |
||||
|
1 DHCPDISCOVER |
||||
|
2 DHCPOFFER |
||||
|
3 DHCPREQUEST |
||||
|
4 DHCPDECLINE |
||||
|
5 DHCPACK |
||||
|
6 DHCPNAK |
||||
|
7 DHCPRELEASE |
||||
|
8 DHCPINFORM |
||||
|
*/ |
||||
|
|
||||
|
*(options + idx++) = 1; // subnet mask |
||||
|
*(options + idx++) = 4; // IP4 length |
||||
|
inet_pton(AF_INET, "255.255.255.0", options + idx); |
||||
|
idx += 4; |
||||
|
|
||||
|
*(options + idx++) = 3; // gateway |
||||
|
*(options + idx++) = 4; // IP4 length |
||||
|
inet_pton(AF_INET, "10.1.10.1", options + idx); |
||||
|
idx += 4; |
||||
|
|
||||
|
*(options + idx++) = 51; // lease time |
||||
|
*(options + idx++) = 4; // quad |
||||
|
*((uint32_t *) (options + idx)) = 3600; |
||||
|
idx += 4; |
||||
|
|
||||
|
*(options + idx++) = 54; // DHCP |
||||
|
*(options + idx++) = 4; // IP4 length |
||||
|
inet_pton(AF_INET, "10.1.10.1", options + idx); |
||||
|
idx += 4; |
||||
|
|
||||
|
*(options + idx++) = 6; // DNS |
||||
|
*(options + idx++) = 4; // IP4 length |
||||
|
inet_pton(AF_INET, "8.8.8.8", options + idx); |
||||
|
idx += 4; |
||||
|
|
||||
|
*(options + idx++) = 255; // End |
||||
|
|
||||
|
/* |
||||
|
DHCP option 53: DHCP Offer |
||||
|
DHCP option 1: 255.255.255.0 subnet mask |
||||
|
DHCP option 3: 192.168.1.1 router |
||||
|
DHCP option 51: 86400s (1 day) IP address lease time |
||||
|
DHCP option 54: 192.168.1.1 DHCP server |
||||
|
DHCP option 6: DNS servers 9.7.10.15 |
||||
|
*/ |
||||
|
|
||||
|
write_udp(args, u, (uint8_t *) response, 500); |
||||
|
|
||||
|
ng_free(response, __FILE__, __LINE__); |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,239 @@ |
|||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
#include "netguard.h" |
||||
|
|
||||
|
int32_t get_qname(const uint8_t *data, const size_t datalen, uint16_t off, char *qname) { |
||||
|
*qname = 0; |
||||
|
|
||||
|
if (off >= datalen) |
||||
|
return -1; |
||||
|
|
||||
|
uint16_t c = 0; |
||||
|
uint8_t noff = 0; |
||||
|
uint16_t ptr = off; |
||||
|
uint8_t len = *(data + ptr); |
||||
|
uint8_t count = 0; |
||||
|
while (len) { |
||||
|
if (count++ > 25) |
||||
|
break; |
||||
|
|
||||
|
if (ptr + 1 < datalen && (len & 0xC0)) { |
||||
|
uint16_t jump = (uint16_t) ((len & 0x3F) * 256 + *(data + ptr + 1)); |
||||
|
if (jump >= datalen) { |
||||
|
log_android(ANDROID_LOG_DEBUG, "DNS invalid jump"); |
||||
|
break; |
||||
|
} |
||||
|
ptr = jump; |
||||
|
len = *(data + ptr); |
||||
|
log_android(ANDROID_LOG_DEBUG, "DNS qname compression ptr %d len %d", ptr, len); |
||||
|
if (!c) { |
||||
|
c = 1; |
||||
|
off += 2; |
||||
|
} |
||||
|
} else if (ptr + 1 + len < datalen && noff + len <= DNS_QNAME_MAX) { |
||||
|
memcpy(qname + noff, data + ptr + 1, len); |
||||
|
*(qname + noff + len) = '.'; |
||||
|
noff += (len + 1); |
||||
|
|
||||
|
uint16_t jump = (uint16_t) (ptr + 1 + len); |
||||
|
if (jump >= datalen) { |
||||
|
log_android(ANDROID_LOG_DEBUG, "DNS invalid jump"); |
||||
|
break; |
||||
|
} |
||||
|
ptr = jump; |
||||
|
len = *(data + ptr); |
||||
|
} else |
||||
|
break; |
||||
|
} |
||||
|
ptr++; |
||||
|
|
||||
|
if (len > 0 || noff == 0) { |
||||
|
log_android(ANDROID_LOG_ERROR, "DNS qname invalid len %d noff %d", len, noff); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
*(qname + noff - 1) = 0; |
||||
|
log_android(ANDROID_LOG_DEBUG, "qname %s", qname); |
||||
|
|
||||
|
return (c ? off : ptr); |
||||
|
} |
||||
|
|
||||
|
void parse_dns_response(const struct arguments *args, const struct ng_session *s, |
||||
|
const uint8_t *data, size_t *datalen) { |
||||
|
if (*datalen < sizeof(struct dns_header) + 1) { |
||||
|
log_android(ANDROID_LOG_WARN, "DNS response length %d", *datalen); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Check if standard DNS query |
||||
|
// TODO multiple qnames |
||||
|
struct dns_header *dns = (struct dns_header *) data; |
||||
|
int qcount = ntohs(dns->q_count); |
||||
|
int acount = ntohs(dns->ans_count); |
||||
|
if (dns->qr == 1 && dns->opcode == 0 && qcount > 0 && acount > 0) { |
||||
|
log_android(ANDROID_LOG_DEBUG, "DNS response qcount %d acount %d", qcount, acount); |
||||
|
if (qcount > 1) |
||||
|
log_android(ANDROID_LOG_WARN, "DNS response qcount %d acount %d", qcount, acount); |
||||
|
|
||||
|
// http://tools.ietf.org/html/rfc1035 |
||||
|
char name[DNS_QNAME_MAX + 1]; |
||||
|
int32_t off = sizeof(struct dns_header); |
||||
|
|
||||
|
uint16_t qtype; |
||||
|
uint16_t qclass; |
||||
|
char qname[DNS_QNAME_MAX + 1]; |
||||
|
|
||||
|
for (int q = 0; q < 1; q++) { |
||||
|
off = get_qname(data, *datalen, (uint16_t) off, name); |
||||
|
if (off > 0 && off + 4 <= *datalen) { |
||||
|
// TODO multiple qnames? |
||||
|
if (q == 0) { |
||||
|
strcpy(qname, name); |
||||
|
qtype = ntohs(*((uint16_t *) (data + off))); |
||||
|
qclass = ntohs(*((uint16_t *) (data + off + 2))); |
||||
|
log_android(ANDROID_LOG_DEBUG, |
||||
|
"DNS question %d qtype %d qclass %d qname %s", |
||||
|
q, qtype, qclass, qname); |
||||
|
} |
||||
|
off += 4; |
||||
|
} else { |
||||
|
log_android(ANDROID_LOG_WARN, |
||||
|
"DNS response Q invalid off %d datalen %d", off, *datalen); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
short svcb = 0; |
||||
|
int32_t aoff = off; |
||||
|
for (int a = 0; a < acount; a++) { |
||||
|
off = get_qname(data, *datalen, (uint16_t) off, name); |
||||
|
if (off > 0 && off + 10 <= *datalen) { |
||||
|
uint16_t qtype = ntohs(*((uint16_t *) (data + off))); |
||||
|
uint16_t qclass = ntohs(*((uint16_t *) (data + off + 2))); |
||||
|
uint32_t ttl = ntohl(*((uint32_t *) (data + off + 4))); |
||||
|
uint16_t rdlength = ntohs(*((uint16_t *) (data + off + 8))); |
||||
|
off += 10; |
||||
|
|
||||
|
if (off + rdlength <= *datalen) { |
||||
|
if (qclass == DNS_QCLASS_IN && |
||||
|
(qtype == DNS_QTYPE_A || qtype == DNS_QTYPE_AAAA)) { |
||||
|
|
||||
|
char rd[INET6_ADDRSTRLEN + 1]; |
||||
|
if (qtype == DNS_QTYPE_A) { |
||||
|
if (off + sizeof(__be32) <= *datalen) |
||||
|
inet_ntop(AF_INET, data + off, rd, sizeof(rd)); |
||||
|
else |
||||
|
return; |
||||
|
} else if (qclass == DNS_QCLASS_IN && qtype == DNS_QTYPE_AAAA) { |
||||
|
if (off + sizeof(struct in6_addr) <= *datalen) |
||||
|
inet_ntop(AF_INET6, data + off, rd, sizeof(rd)); |
||||
|
else |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
dns_resolved(args, qname, name, rd, ttl); |
||||
|
log_android(ANDROID_LOG_DEBUG, |
||||
|
"DNS answer %d qname %s qtype %d ttl %d data %s", |
||||
|
a, name, qtype, ttl, rd); |
||||
|
} else if (qclass == DNS_QCLASS_IN && |
||||
|
(qtype == DNS_SVCB || qtype == DNS_HTTPS)) { |
||||
|
// https://tools.ietf.org/id/draft-ietf-dnsop-svcb-https-01.html |
||||
|
svcb = 1; |
||||
|
log_android(ANDROID_LOG_WARN, |
||||
|
"SVCB answer %d qname %s qtype %d", a, name, qtype); |
||||
|
} else |
||||
|
log_android(ANDROID_LOG_DEBUG, |
||||
|
"DNS answer %d qname %s qclass %d qtype %d ttl %d length %d", |
||||
|
a, name, qclass, qtype, ttl, rdlength); |
||||
|
|
||||
|
off += rdlength; |
||||
|
} else { |
||||
|
log_android(ANDROID_LOG_WARN, |
||||
|
"DNS response A invalid off %d rdlength %d datalen %d", |
||||
|
off, rdlength, *datalen); |
||||
|
return; |
||||
|
} |
||||
|
} else { |
||||
|
log_android(ANDROID_LOG_WARN, |
||||
|
"DNS response A invalid off %d datalen %d", off, *datalen); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (qcount > 0 && |
||||
|
(svcb || is_domain_blocked(args, qname))) { |
||||
|
dns->qr = 1; |
||||
|
dns->aa = 0; |
||||
|
dns->tc = 0; |
||||
|
dns->rd = 0; |
||||
|
dns->ra = 0; |
||||
|
dns->z = 0; |
||||
|
dns->ad = 0; |
||||
|
dns->cd = 0; |
||||
|
dns->rcode = (uint16_t) args->rcode; |
||||
|
dns->ans_count = 0; |
||||
|
dns->auth_count = 0; |
||||
|
dns->add_count = 0; |
||||
|
*datalen = aoff; |
||||
|
|
||||
|
int version; |
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
uint16_t sport; |
||||
|
uint16_t dport; |
||||
|
|
||||
|
if (s->protocol == IPPROTO_UDP) { |
||||
|
version = s->udp.version; |
||||
|
sport = ntohs(s->udp.source); |
||||
|
dport = ntohs(s->udp.dest); |
||||
|
if (s->udp.version == 4) { |
||||
|
inet_ntop(AF_INET, &s->udp.saddr.ip4, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest)); |
||||
|
} else { |
||||
|
inet_ntop(AF_INET6, &s->udp.saddr.ip6, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest)); |
||||
|
} |
||||
|
} else { |
||||
|
version = s->tcp.version; |
||||
|
sport = ntohs(s->tcp.source); |
||||
|
dport = ntohs(s->tcp.dest); |
||||
|
if (s->tcp.version == 4) { |
||||
|
inet_ntop(AF_INET, &s->tcp.saddr.ip4, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, &s->tcp.daddr.ip4, dest, sizeof(dest)); |
||||
|
} else { |
||||
|
inet_ntop(AF_INET6, &s->tcp.saddr.ip6, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET6, &s->tcp.daddr.ip6, dest, sizeof(dest)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Log qname |
||||
|
char name[DNS_QNAME_MAX + 40 + 1]; |
||||
|
sprintf(name, "qtype %d qname %s rcode %d", qtype, qname, dns->rcode); |
||||
|
jobject objPacket = create_packet( |
||||
|
args, version, s->protocol, "", |
||||
|
source, sport, dest, dport, |
||||
|
name, 0, 0); |
||||
|
log_packet(args, objPacket); |
||||
|
} |
||||
|
} else if (acount > 0) |
||||
|
log_android(ANDROID_LOG_WARN, |
||||
|
"DNS response qr %d opcode %d qcount %d acount %d", |
||||
|
dns->qr, dns->opcode, qcount, acount); |
||||
|
} |
@ -0,0 +1,374 @@ |
|||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
#include "netguard.h" |
||||
|
|
||||
|
extern FILE *pcap_file; |
||||
|
|
||||
|
int get_icmp_timeout(const struct icmp_session *u, int sessions, int maxsessions) { |
||||
|
int timeout = ICMP_TIMEOUT; |
||||
|
|
||||
|
int scale = 100 - sessions * 100 / maxsessions; |
||||
|
timeout = timeout * scale / 100; |
||||
|
|
||||
|
return timeout; |
||||
|
} |
||||
|
|
||||
|
int check_icmp_session(const struct arguments *args, struct ng_session *s, |
||||
|
int sessions, int maxsessions) { |
||||
|
time_t now = time(NULL); |
||||
|
|
||||
|
int timeout = get_icmp_timeout(&s->icmp, sessions, maxsessions); |
||||
|
if (s->icmp.stop || s->icmp.time + timeout < now) { |
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
if (s->icmp.version == 4) { |
||||
|
inet_ntop(AF_INET, &s->icmp.saddr.ip4, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, &s->icmp.daddr.ip4, dest, sizeof(dest)); |
||||
|
} else { |
||||
|
inet_ntop(AF_INET6, &s->icmp.saddr.ip6, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET6, &s->icmp.daddr.ip6, dest, sizeof(dest)); |
||||
|
} |
||||
|
log_android(ANDROID_LOG_WARN, "ICMP idle %d/%d sec stop %d from %s to %s", |
||||
|
now - s->icmp.time, timeout, s->icmp.stop, dest, source); |
||||
|
|
||||
|
if (close(s->socket)) |
||||
|
log_android(ANDROID_LOG_ERROR, "ICMP close %d error %d: %s", |
||||
|
s->socket, errno, strerror(errno)); |
||||
|
s->socket = -1; |
||||
|
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
void check_icmp_socket(const struct arguments *args, const struct epoll_event *ev) { |
||||
|
struct ng_session *s = (struct ng_session *) ev->data.ptr; |
||||
|
|
||||
|
// Check socket error |
||||
|
if (ev->events & EPOLLERR) { |
||||
|
s->icmp.time = time(NULL); |
||||
|
|
||||
|
int serr = 0; |
||||
|
socklen_t optlen = sizeof(int); |
||||
|
int err = getsockopt(s->socket, SOL_SOCKET, SO_ERROR, &serr, &optlen); |
||||
|
if (err < 0) |
||||
|
log_android(ANDROID_LOG_ERROR, "ICMP getsockopt error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
else if (serr) |
||||
|
log_android(ANDROID_LOG_ERROR, "ICMP SO_ERROR %d: %s", |
||||
|
serr, strerror(serr)); |
||||
|
|
||||
|
s->icmp.stop = 1; |
||||
|
} else { |
||||
|
// Check socket read |
||||
|
if (ev->events & EPOLLIN) { |
||||
|
s->icmp.time = time(NULL); |
||||
|
|
||||
|
uint16_t blen = (uint16_t) (s->icmp.version == 4 ? ICMP4_MAXMSG : ICMP6_MAXMSG); |
||||
|
uint8_t *buffer = ng_malloc(blen, "icmp socket"); |
||||
|
ssize_t bytes = recv(s->socket, buffer, blen, 0); |
||||
|
if (bytes < 0) { |
||||
|
// Socket error |
||||
|
log_android(ANDROID_LOG_WARN, "ICMP recv error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
|
||||
|
if (errno != EINTR && errno != EAGAIN) |
||||
|
s->icmp.stop = 1; |
||||
|
} else if (bytes == 0) { |
||||
|
log_android(ANDROID_LOG_WARN, "ICMP recv eof"); |
||||
|
s->icmp.stop = 1; |
||||
|
|
||||
|
} else { |
||||
|
// Socket read data |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
if (s->icmp.version == 4) |
||||
|
inet_ntop(AF_INET, &s->icmp.daddr.ip4, dest, sizeof(dest)); |
||||
|
else |
||||
|
inet_ntop(AF_INET6, &s->icmp.daddr.ip6, dest, sizeof(dest)); |
||||
|
|
||||
|
// cur->id should be equal to icmp->icmp_id |
||||
|
// but for some unexplained reason this is not the case |
||||
|
// some bits seems to be set extra |
||||
|
struct icmp *icmp = (struct icmp *) buffer; |
||||
|
log_android( |
||||
|
s->icmp.id == icmp->icmp_id ? ANDROID_LOG_INFO : ANDROID_LOG_WARN, |
||||
|
"ICMP recv bytes %d from %s for tun type %d code %d id %x/%x seq %d", |
||||
|
bytes, dest, |
||||
|
icmp->icmp_type, icmp->icmp_code, |
||||
|
s->icmp.id, icmp->icmp_id, icmp->icmp_seq); |
||||
|
|
||||
|
// restore original ID |
||||
|
icmp->icmp_id = s->icmp.id; |
||||
|
uint16_t csum = 0; |
||||
|
if (s->icmp.version == 6) { |
||||
|
// Untested |
||||
|
struct ip6_hdr_pseudo pseudo; |
||||
|
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo)); |
||||
|
memcpy(&pseudo.ip6ph_src, &s->icmp.daddr.ip6, 16); |
||||
|
memcpy(&pseudo.ip6ph_dst, &s->icmp.saddr.ip6, 16); |
||||
|
pseudo.ip6ph_len = bytes - sizeof(struct ip6_hdr); |
||||
|
pseudo.ip6ph_nxt = IPPROTO_ICMPV6; |
||||
|
csum = calc_checksum( |
||||
|
0, (uint8_t *) &pseudo, sizeof(struct ip6_hdr_pseudo)); |
||||
|
} |
||||
|
icmp->icmp_cksum = 0; |
||||
|
icmp->icmp_cksum = ~calc_checksum(csum, buffer, (size_t) bytes); |
||||
|
|
||||
|
// Forward to tun |
||||
|
if (write_icmp(args, &s->icmp, buffer, (size_t) bytes) < 0) |
||||
|
s->icmp.stop = 1; |
||||
|
} |
||||
|
ng_free(buffer, __FILE__, __LINE__); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
jboolean handle_icmp(const struct arguments *args, |
||||
|
const uint8_t *pkt, size_t length, |
||||
|
const uint8_t *payload, |
||||
|
int uid, |
||||
|
const int epoll_fd) { |
||||
|
// Get headers |
||||
|
const uint8_t version = (*pkt) >> 4; |
||||
|
const struct iphdr *ip4 = (struct iphdr *) pkt; |
||||
|
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt; |
||||
|
struct icmp *icmp = (struct icmp *) payload; |
||||
|
size_t icmplen = length - (payload - pkt); |
||||
|
|
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
if (version == 4) { |
||||
|
inet_ntop(AF_INET, &ip4->saddr, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, &ip4->daddr, dest, sizeof(dest)); |
||||
|
} else { |
||||
|
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest)); |
||||
|
} |
||||
|
|
||||
|
if (icmp->icmp_type != ICMP_ECHO) { |
||||
|
log_android(ANDROID_LOG_WARN, "ICMP type %d code %d from %s to %s not supported", |
||||
|
icmp->icmp_type, icmp->icmp_code, source, dest); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
// Search session |
||||
|
struct ng_session *cur = args->ctx->ng_session; |
||||
|
while (cur != NULL && |
||||
|
!((cur->protocol == IPPROTO_ICMP || cur->protocol == IPPROTO_ICMPV6) && |
||||
|
!cur->icmp.stop && cur->icmp.version == version && |
||||
|
(version == 4 ? cur->icmp.saddr.ip4 == ip4->saddr && |
||||
|
cur->icmp.daddr.ip4 == ip4->daddr |
||||
|
: memcmp(&cur->icmp.saddr.ip6, &ip6->ip6_src, 16) == 0 && |
||||
|
memcmp(&cur->icmp.daddr.ip6, &ip6->ip6_dst, 16) == 0))) |
||||
|
cur = cur->next; |
||||
|
|
||||
|
// Create new session if needed |
||||
|
if (cur == NULL) { |
||||
|
log_android(ANDROID_LOG_INFO, "ICMP new session from %s to %s", source, dest); |
||||
|
|
||||
|
// Register session |
||||
|
struct ng_session *s = ng_malloc(sizeof(struct ng_session), "icmp session"); |
||||
|
s->protocol = (uint8_t) (version == 4 ? IPPROTO_ICMP : IPPROTO_ICMPV6); |
||||
|
|
||||
|
s->icmp.time = time(NULL); |
||||
|
s->icmp.uid = uid; |
||||
|
s->icmp.version = version; |
||||
|
|
||||
|
if (version == 4) { |
||||
|
s->icmp.saddr.ip4 = (__be32) ip4->saddr; |
||||
|
s->icmp.daddr.ip4 = (__be32) ip4->daddr; |
||||
|
} else { |
||||
|
memcpy(&s->icmp.saddr.ip6, &ip6->ip6_src, 16); |
||||
|
memcpy(&s->icmp.daddr.ip6, &ip6->ip6_dst, 16); |
||||
|
} |
||||
|
|
||||
|
s->icmp.id = icmp->icmp_id; // store original ID |
||||
|
|
||||
|
s->icmp.stop = 0; |
||||
|
s->next = NULL; |
||||
|
|
||||
|
// Open UDP socket |
||||
|
s->socket = open_icmp_socket(args, &s->icmp); |
||||
|
if (s->socket < 0) { |
||||
|
ng_free(s, __FILE__, __LINE__); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
log_android(ANDROID_LOG_DEBUG, "ICMP socket %d id %x", s->socket, s->icmp.id); |
||||
|
|
||||
|
// Monitor events |
||||
|
memset(&s->ev, 0, sizeof(struct epoll_event)); |
||||
|
s->ev.events = EPOLLIN | EPOLLERR; |
||||
|
s->ev.data.ptr = s; |
||||
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s->socket, &s->ev)) |
||||
|
log_android(ANDROID_LOG_ERROR, "epoll add icmp error %d: %s", errno, strerror(errno)); |
||||
|
|
||||
|
s->next = args->ctx->ng_session; |
||||
|
args->ctx->ng_session = s; |
||||
|
|
||||
|
cur = s; |
||||
|
} |
||||
|
|
||||
|
// Modify ID |
||||
|
// http://lwn.net/Articles/443051/ |
||||
|
icmp->icmp_id = ~icmp->icmp_id; |
||||
|
uint16_t csum = 0; |
||||
|
if (version == 6) { |
||||
|
// Untested |
||||
|
struct ip6_hdr_pseudo pseudo; |
||||
|
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo)); |
||||
|
memcpy(&pseudo.ip6ph_src, &ip6->ip6_dst, 16); |
||||
|
memcpy(&pseudo.ip6ph_dst, &ip6->ip6_src, 16); |
||||
|
pseudo.ip6ph_len = ip6->ip6_ctlun.ip6_un1.ip6_un1_plen; |
||||
|
pseudo.ip6ph_nxt = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt; |
||||
|
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ip6_hdr_pseudo)); |
||||
|
} |
||||
|
icmp->icmp_cksum = 0; |
||||
|
icmp->icmp_cksum = ~calc_checksum(csum, (uint8_t *) icmp, icmplen); |
||||
|
|
||||
|
log_android(ANDROID_LOG_INFO, |
||||
|
"ICMP forward from tun %s to %s type %d code %d id %x seq %d data %d", |
||||
|
source, dest, |
||||
|
icmp->icmp_type, icmp->icmp_code, icmp->icmp_id, icmp->icmp_seq, icmplen); |
||||
|
|
||||
|
cur->icmp.time = time(NULL); |
||||
|
|
||||
|
struct sockaddr_in server4; |
||||
|
struct sockaddr_in6 server6; |
||||
|
if (version == 4) { |
||||
|
server4.sin_family = AF_INET; |
||||
|
server4.sin_addr.s_addr = (__be32) ip4->daddr; |
||||
|
server4.sin_port = 0; |
||||
|
} else { |
||||
|
server6.sin6_family = AF_INET6; |
||||
|
memcpy(&server6.sin6_addr, &ip6->ip6_dst, 16); |
||||
|
server6.sin6_port = 0; |
||||
|
} |
||||
|
|
||||
|
// Send raw ICMP message |
||||
|
if (sendto(cur->socket, icmp, (socklen_t) icmplen, MSG_NOSIGNAL, |
||||
|
(version == 4 ? (const struct sockaddr *) &server4 |
||||
|
: (const struct sockaddr *) &server6), |
||||
|
(socklen_t) (version == 4 ? sizeof(server4) : sizeof(server6))) != icmplen) { |
||||
|
log_android(ANDROID_LOG_ERROR, "ICMP sendto error %d: %s", errno, strerror(errno)); |
||||
|
if (errno != EINTR && errno != EAGAIN) { |
||||
|
cur->icmp.stop = 1; |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
int open_icmp_socket(const struct arguments *args, const struct icmp_session *cur) { |
||||
|
int sock; |
||||
|
|
||||
|
// Get UDP socket |
||||
|
sock = socket(cur->version == 4 ? PF_INET : PF_INET6, SOCK_DGRAM, IPPROTO_ICMP); |
||||
|
if (sock < 0) { |
||||
|
log_android(ANDROID_LOG_ERROR, "ICMP socket error %d: %s", errno, strerror(errno)); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
// Protect socket |
||||
|
if (protect_socket(args, sock) < 0) |
||||
|
return -1; |
||||
|
|
||||
|
return sock; |
||||
|
} |
||||
|
|
||||
|
ssize_t write_icmp(const struct arguments *args, const struct icmp_session *cur, |
||||
|
uint8_t *data, size_t datalen) { |
||||
|
size_t len; |
||||
|
u_int8_t *buffer; |
||||
|
struct icmp *icmp = (struct icmp *) data; |
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
|
||||
|
// Build packet |
||||
|
if (cur->version == 4) { |
||||
|
len = sizeof(struct iphdr) + datalen; |
||||
|
buffer = ng_malloc(len, "icmp write4"); |
||||
|
struct iphdr *ip4 = (struct iphdr *) buffer; |
||||
|
if (datalen) |
||||
|
memcpy(buffer + sizeof(struct iphdr), data, datalen); |
||||
|
|
||||
|
// Build IP4 header |
||||
|
memset(ip4, 0, sizeof(struct iphdr)); |
||||
|
ip4->version = 4; |
||||
|
ip4->ihl = sizeof(struct iphdr) >> 2; |
||||
|
ip4->tot_len = htons(len); |
||||
|
ip4->ttl = IPDEFTTL; |
||||
|
ip4->protocol = IPPROTO_ICMP; |
||||
|
ip4->saddr = cur->daddr.ip4; |
||||
|
ip4->daddr = cur->saddr.ip4; |
||||
|
|
||||
|
// Calculate IP4 checksum |
||||
|
ip4->check = ~calc_checksum(0, (uint8_t *) ip4, sizeof(struct iphdr)); |
||||
|
} else { |
||||
|
len = sizeof(struct ip6_hdr) + datalen; |
||||
|
buffer = ng_malloc(len, "icmp write6"); |
||||
|
struct ip6_hdr *ip6 = (struct ip6_hdr *) buffer; |
||||
|
if (datalen) |
||||
|
memcpy(buffer + sizeof(struct ip6_hdr), data, datalen); |
||||
|
|
||||
|
// Build IP6 header |
||||
|
memset(ip6, 0, sizeof(struct ip6_hdr)); |
||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = 0; |
||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len - sizeof(struct ip6_hdr)); |
||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6; |
||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = IPDEFTTL; |
||||
|
ip6->ip6_ctlun.ip6_un2_vfc = IPV6_VERSION; |
||||
|
memcpy(&(ip6->ip6_src), &cur->daddr.ip6, 16); |
||||
|
memcpy(&(ip6->ip6_dst), &cur->saddr.ip6, 16); |
||||
|
} |
||||
|
|
||||
|
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6, |
||||
|
cur->version == 4 ? (const void *) &cur->saddr.ip4 : (const void *) &cur->saddr.ip6, |
||||
|
source, sizeof(source)); |
||||
|
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6, |
||||
|
cur->version == 4 ? (const void *) &cur->daddr.ip4 : (const void *) &cur->daddr.ip6, |
||||
|
dest, sizeof(dest)); |
||||
|
|
||||
|
// Send raw ICMP message |
||||
|
log_android(ANDROID_LOG_WARN, |
||||
|
"ICMP sending to tun %d from %s to %s data %u type %d code %d id %x seq %d", |
||||
|
args->tun, dest, source, datalen, |
||||
|
icmp->icmp_type, icmp->icmp_code, icmp->icmp_id, icmp->icmp_seq); |
||||
|
|
||||
|
ssize_t res = write(args->tun, buffer, len); |
||||
|
|
||||
|
// Write PCAP record |
||||
|
if (res >= 0) { |
||||
|
if (pcap_file != NULL) |
||||
|
write_pcap_rec(buffer, (size_t) res); |
||||
|
} else |
||||
|
log_android(ANDROID_LOG_WARN, "ICMP write error %d: %s", errno, strerror(errno)); |
||||
|
|
||||
|
ng_free(buffer, __FILE__, __LINE__); |
||||
|
|
||||
|
if (res != len) { |
||||
|
log_android(ANDROID_LOG_ERROR, "write %d/%d", res, len); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
return res; |
||||
|
} |
@ -0,0 +1,552 @@ |
|||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
#include "netguard.h" |
||||
|
|
||||
|
int max_tun_msg = 0; |
||||
|
extern int loglevel; |
||||
|
extern FILE *pcap_file; |
||||
|
|
||||
|
|
||||
|
extern int debug_set = 0; |
||||
|
|
||||
|
uint16_t get_mtu() { |
||||
|
return 10000; |
||||
|
} |
||||
|
|
||||
|
uint16_t get_default_mss(int version) { |
||||
|
if (version == 4) |
||||
|
return (uint16_t) (get_mtu() - sizeof(struct iphdr) - sizeof(struct tcphdr)); |
||||
|
else |
||||
|
return (uint16_t) (get_mtu() - sizeof(struct ip6_hdr) - sizeof(struct tcphdr)); |
||||
|
} |
||||
|
|
||||
|
int check_tun(const struct arguments *args, |
||||
|
const struct epoll_event *ev, |
||||
|
const int epoll_fd, |
||||
|
int sessions, int maxsessions) { |
||||
|
// Check tun error |
||||
|
if (ev->events & EPOLLERR) { |
||||
|
log_android(ANDROID_LOG_ERROR, "tun %d exception", args->tun); |
||||
|
if (fcntl(args->tun, F_GETFL) < 0) { |
||||
|
log_android(ANDROID_LOG_ERROR, "fcntl tun %d F_GETFL error %d: %s", |
||||
|
args->tun, errno, strerror(errno)); |
||||
|
report_exit(args, "fcntl tun %d F_GETFL error %d: %s", |
||||
|
args->tun, errno, strerror(errno)); |
||||
|
} else |
||||
|
report_exit(args, "tun %d exception", args->tun); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
// Check tun read |
||||
|
if (ev->events & EPOLLIN) { |
||||
|
uint8_t *buffer = ng_malloc(get_mtu(), "tun read"); |
||||
|
ssize_t length = read(args->tun, buffer, get_mtu()); |
||||
|
if (length < 0) { |
||||
|
ng_free(buffer, __FILE__, __LINE__); |
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "tun %d read error %d: %s", |
||||
|
args->tun, errno, strerror(errno)); |
||||
|
if (errno == EINTR || errno == EAGAIN) |
||||
|
// Retry later |
||||
|
return 0; |
||||
|
else { |
||||
|
report_exit(args, "tun %d read error %d: %s", |
||||
|
args->tun, errno, strerror(errno)); |
||||
|
return -1; |
||||
|
} |
||||
|
} else if (length > 0) { |
||||
|
// Write pcap record |
||||
|
if (pcap_file != NULL) |
||||
|
write_pcap_rec(buffer, (size_t) length); |
||||
|
|
||||
|
if (length > max_tun_msg) { |
||||
|
max_tun_msg = length; |
||||
|
log_android(ANDROID_LOG_WARN, "Maximum tun msg length %d", max_tun_msg); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// Handle IP from tun |
||||
|
handle_ip(args, buffer, (size_t) length, epoll_fd, sessions, maxsessions); |
||||
|
|
||||
|
ng_free(buffer, __FILE__, __LINE__); |
||||
|
} else { |
||||
|
// tun eof |
||||
|
ng_free(buffer, __FILE__, __LINE__); |
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "tun %d empty read", args->tun); |
||||
|
report_exit(args, "tun %d empty read", args->tun); |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
// https://en.wikipedia.org/wiki/IPv6_packet#Extension_headers |
||||
|
// http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml |
||||
|
int is_lower_layer(int protocol) { |
||||
|
// No next header = 59 |
||||
|
return (protocol == 0 || // Hop-by-Hop Options |
||||
|
protocol == 60 || // Destination Options (before routing header) |
||||
|
protocol == 43 || // Routing |
||||
|
protocol == 44 || // Fragment |
||||
|
protocol == 51 || // Authentication Header (AH) |
||||
|
protocol == 50 || // Encapsulating Security Payload (ESP) |
||||
|
protocol == 60 || // Destination Options (before upper-layer header) |
||||
|
protocol == 135); // Mobility |
||||
|
} |
||||
|
|
||||
|
int is_upper_layer(int protocol) { |
||||
|
return (protocol == IPPROTO_TCP || |
||||
|
protocol == IPPROTO_UDP || |
||||
|
protocol == IPPROTO_ICMP || |
||||
|
protocol == IPPROTO_ICMPV6); |
||||
|
} |
||||
|
|
||||
|
void handle_ip(const struct arguments *args, |
||||
|
const uint8_t *pkt, const size_t length, |
||||
|
const int epoll_fd, |
||||
|
int sessions, int maxsessions) { |
||||
|
uint8_t protocol; |
||||
|
void *saddr; |
||||
|
void *daddr; |
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
char flags[10]; |
||||
|
char data[16]; |
||||
|
int flen = 0; |
||||
|
uint8_t *payload; |
||||
|
|
||||
|
// Get protocol, addresses & payload |
||||
|
uint8_t version = (*pkt) >> 4; |
||||
|
if (version == 4) { |
||||
|
if (length < sizeof(struct iphdr)) { |
||||
|
log_android(ANDROID_LOG_WARN, "IP4 packet too short length %d", length); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
struct iphdr *ip4hdr = (struct iphdr *) pkt; |
||||
|
|
||||
|
protocol = ip4hdr->protocol; |
||||
|
saddr = &ip4hdr->saddr; |
||||
|
daddr = &ip4hdr->daddr; |
||||
|
|
||||
|
|
||||
|
if (ip4hdr->frag_off & IP_MF) { |
||||
|
log_android(ANDROID_LOG_ERROR, "IP fragment offset %u", |
||||
|
(ip4hdr->frag_off & IP_OFFMASK) * 8); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
uint8_t ipoptlen = (uint8_t) ((ip4hdr->ihl - 5) * 4); |
||||
|
payload = (uint8_t *) (pkt + sizeof(struct iphdr) + ipoptlen); |
||||
|
|
||||
|
if (ntohs(ip4hdr->tot_len) != length) { |
||||
|
log_android(ANDROID_LOG_ERROR, "Invalid length %u header length %u", |
||||
|
length, ntohs(ip4hdr->tot_len)); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (loglevel < ANDROID_LOG_WARN) { |
||||
|
if (!calc_checksum(0, (uint8_t *) ip4hdr, sizeof(struct iphdr))) { |
||||
|
log_android(ANDROID_LOG_ERROR, "Invalid IP checksum"); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} else if (version == 6) { |
||||
|
if (length < sizeof(struct ip6_hdr)) { |
||||
|
log_android(ANDROID_LOG_WARN, "IP6 packet too short length %d", length); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
struct ip6_hdr *ip6hdr = (struct ip6_hdr *) pkt; |
||||
|
|
||||
|
// Skip extension headers |
||||
|
uint16_t off = 0; |
||||
|
protocol = ip6hdr->ip6_nxt; |
||||
|
if (!is_upper_layer(protocol)) { |
||||
|
log_android(ANDROID_LOG_WARN, "IP6 extension %d", protocol); |
||||
|
off = sizeof(struct ip6_hdr); |
||||
|
struct ip6_ext *ext = (struct ip6_ext *) (pkt + off); |
||||
|
while (is_lower_layer(ext->ip6e_nxt) && !is_upper_layer(protocol)) { |
||||
|
protocol = ext->ip6e_nxt; |
||||
|
log_android(ANDROID_LOG_WARN, "IP6 extension %d", protocol); |
||||
|
|
||||
|
off += (8 + ext->ip6e_len); |
||||
|
ext = (struct ip6_ext *) (pkt + off); |
||||
|
} |
||||
|
if (!is_upper_layer(protocol)) { |
||||
|
off = 0; |
||||
|
protocol = ip6hdr->ip6_nxt; |
||||
|
log_android(ANDROID_LOG_WARN, "IP6 final extension %d", protocol); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
saddr = &ip6hdr->ip6_src; |
||||
|
daddr = &ip6hdr->ip6_dst; |
||||
|
|
||||
|
payload = (uint8_t *) (pkt + sizeof(struct ip6_hdr) + off); |
||||
|
|
||||
|
// TODO checksum |
||||
|
} else { |
||||
|
log_android(ANDROID_LOG_ERROR, "Unknown version %d", version); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
inet_ntop(version == 4 ? AF_INET : AF_INET6, saddr, source, sizeof(source)); |
||||
|
inet_ntop(version == 4 ? AF_INET : AF_INET6, daddr, dest, sizeof(dest)); |
||||
|
|
||||
|
log_android(ANDROID_LOG_ERROR, "handling IP packet with source: %s, dest: %s", source, dest); |
||||
|
|
||||
|
// START: create debug tcp session and write packets to it |
||||
|
debug_set += 1; |
||||
|
|
||||
|
// TODO: need to make sure we're not forwarding the IP packet of our debug packet |
||||
|
log_android(ANDROID_LOG_ERROR, "length of dest: %d", strcmp(dest, "207.246.62.210")); |
||||
|
|
||||
|
|
||||
|
if (debug_set == 30) { |
||||
|
log_android(ANDROID_LOG_ERROR, "handling debug socket init"); |
||||
|
debug_socket_init(args, epoll_fd); |
||||
|
|
||||
|
|
||||
|
} else if(debug_set > 60 && debug_set < 80) { |
||||
|
log_android(ANDROID_LOG_ERROR, "Test writing to debug socket with length: %d", length); |
||||
|
write_debug_socket(args, pkt, (size_t) length); |
||||
|
|
||||
|
} else if(debug_set < 30) { |
||||
|
log_android(ANDROID_LOG_ERROR, "Waiting for more packets to start debug sesh --> %d/30", debug_set); |
||||
|
} else if (debug_set > 30 && debug_set < 60) { |
||||
|
log_android(ANDROID_LOG_ERROR, "Waiting for more packets to start writing to the debug sesh --> %d/60", debug_set); |
||||
|
} else { |
||||
|
log_android(ANDROID_LOG_ERROR, "Finished writing to debug server --> %d", debug_set); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
// END: debug session |
||||
|
|
||||
|
|
||||
|
// Get ports & flags |
||||
|
int syn = 0; |
||||
|
uint16_t sport = 0; |
||||
|
uint16_t dport = 0; |
||||
|
*data = 0; |
||||
|
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6) { |
||||
|
if (length - (payload - pkt) < ICMP_MINLEN) { |
||||
|
log_android(ANDROID_LOG_WARN, "ICMP packet too short"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
struct icmp *icmp = (struct icmp *) payload; |
||||
|
|
||||
|
sprintf(data, "type %d/%d", icmp->icmp_type, icmp->icmp_code); |
||||
|
|
||||
|
// http://lwn.net/Articles/443051/ |
||||
|
sport = ntohs(icmp->icmp_id); |
||||
|
dport = ntohs(icmp->icmp_id); |
||||
|
|
||||
|
} else if (protocol == IPPROTO_UDP) { |
||||
|
if (length - (payload - pkt) < sizeof(struct udphdr)) { |
||||
|
log_android(ANDROID_LOG_WARN, "UDP packet too short"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
struct udphdr *udp = (struct udphdr *) payload; |
||||
|
|
||||
|
sport = ntohs(udp->source); |
||||
|
dport = ntohs(udp->dest); |
||||
|
|
||||
|
// TODO checksum (IPv6) |
||||
|
} else if (protocol == IPPROTO_TCP) { |
||||
|
if (length - (payload - pkt) < sizeof(struct tcphdr)) { |
||||
|
log_android(ANDROID_LOG_WARN, "TCP packet too short"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
struct tcphdr *tcp = (struct tcphdr *) payload; |
||||
|
|
||||
|
sport = ntohs(tcp->source); |
||||
|
dport = ntohs(tcp->dest); |
||||
|
|
||||
|
if (tcp->syn) { |
||||
|
syn = 1; |
||||
|
flags[flen++] = 'S'; |
||||
|
} |
||||
|
if (tcp->ack) |
||||
|
flags[flen++] = 'A'; |
||||
|
if (tcp->psh) |
||||
|
flags[flen++] = 'P'; |
||||
|
if (tcp->fin) |
||||
|
flags[flen++] = 'F'; |
||||
|
if (tcp->rst) |
||||
|
flags[flen++] = 'R'; |
||||
|
|
||||
|
// TODO checksum |
||||
|
} else if (protocol != IPPROTO_HOPOPTS && protocol != IPPROTO_IGMP && protocol != IPPROTO_ESP) |
||||
|
log_android(ANDROID_LOG_WARN, "Unknown protocol %d", protocol); |
||||
|
|
||||
|
flags[flen] = 0; |
||||
|
|
||||
|
// Limit number of sessions |
||||
|
if (sessions >= maxsessions) { |
||||
|
if ((protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6) || |
||||
|
(protocol == IPPROTO_UDP && !has_udp_session(args, pkt, payload)) || |
||||
|
(protocol == IPPROTO_TCP && syn)) { |
||||
|
log_android(ANDROID_LOG_ERROR, |
||||
|
"%d of max %d sessions, dropping version %d protocol %d", |
||||
|
sessions, maxsessions, protocol, version); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Get uid |
||||
|
jint uid = -1; |
||||
|
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6 || |
||||
|
(protocol == IPPROTO_UDP && !has_udp_session(args, pkt, payload)) || |
||||
|
(protocol == IPPROTO_TCP && syn)) { |
||||
|
if (args->ctx->sdk <= 28) // Android 9 Pie |
||||
|
uid = get_uid(version, protocol, saddr, sport, daddr, dport); |
||||
|
else |
||||
|
uid = get_uid_q(args, version, protocol, source, sport, dest, dport); |
||||
|
} |
||||
|
|
||||
|
log_android(ANDROID_LOG_DEBUG, |
||||
|
"Packet v%d %s/%u > %s/%u proto %d flags %s uid %d", |
||||
|
version, source, sport, dest, dport, protocol, flags, uid); |
||||
|
|
||||
|
|
||||
|
|
||||
|
// could create new function to handle passing this raw packet info to debug socket here |
||||
|
|
||||
|
// Check if allowed |
||||
|
int allowed = 0; |
||||
|
struct allowed *redirect = NULL; |
||||
|
if (protocol == IPPROTO_UDP && has_udp_session(args, pkt, payload)) |
||||
|
allowed = 1; // could be a lingering/blocked session |
||||
|
else if (protocol == IPPROTO_TCP && (!syn || (uid == 0 && dport == 53))) |
||||
|
allowed = 1; // assume existing session |
||||
|
else { |
||||
|
jobject objPacket = create_packet( |
||||
|
args, version, protocol, flags, source, sport, dest, dport, data, uid, 0); |
||||
|
redirect = is_address_allowed(args, objPacket); |
||||
|
allowed = (redirect != NULL); |
||||
|
if (redirect != NULL && (*redirect->raddr == 0 || redirect->rport == 0)) |
||||
|
redirect = NULL; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
if (allowed) { |
||||
|
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6) |
||||
|
handle_icmp(args, pkt, length, payload, uid, epoll_fd); |
||||
|
else if (protocol == IPPROTO_UDP) |
||||
|
handle_udp(args, pkt, length, payload, uid, redirect, epoll_fd); |
||||
|
else if (protocol == IPPROTO_TCP) |
||||
|
handle_tcp(args, pkt, length, payload, uid, allowed, redirect, epoll_fd); |
||||
|
} else { |
||||
|
if (protocol == IPPROTO_UDP) |
||||
|
block_udp(args, pkt, length, payload, uid); |
||||
|
|
||||
|
log_android(ANDROID_LOG_WARN, "Address v%d p%d %s/%u syn %d not allowed", |
||||
|
version, protocol, dest, dport, syn); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
jint get_uid(const int version, const int protocol, |
||||
|
const void *saddr, const uint16_t sport, |
||||
|
const void *daddr, const uint16_t dport) { |
||||
|
jint uid = -1; |
||||
|
|
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
inet_ntop(version == 4 ? AF_INET : AF_INET6, saddr, source, sizeof(source)); |
||||
|
inet_ntop(version == 4 ? AF_INET : AF_INET6, daddr, dest, sizeof(dest)); |
||||
|
|
||||
|
struct timeval time; |
||||
|
gettimeofday(&time, NULL); |
||||
|
long now = (time.tv_sec * 1000) + (time.tv_usec / 1000); |
||||
|
|
||||
|
// Check IPv6 table first |
||||
|
if (version == 4) { |
||||
|
int8_t saddr128[16]; |
||||
|
memset(saddr128, 0, 10); |
||||
|
saddr128[10] = (uint8_t) 0xFF; |
||||
|
saddr128[11] = (uint8_t) 0xFF; |
||||
|
memcpy(saddr128 + 12, saddr, 4); |
||||
|
|
||||
|
int8_t daddr128[16]; |
||||
|
memset(daddr128, 0, 10); |
||||
|
daddr128[10] = (uint8_t) 0xFF; |
||||
|
daddr128[11] = (uint8_t) 0xFF; |
||||
|
memcpy(daddr128 + 12, daddr, 4); |
||||
|
|
||||
|
uid = get_uid_sub(6, protocol, saddr128, sport, daddr128, dport, source, dest, now); |
||||
|
log_android(ANDROID_LOG_DEBUG, "uid v%d p%d %s/%u > %s/%u => %d as inet6", |
||||
|
version, protocol, source, sport, dest, dport, uid); |
||||
|
} |
||||
|
|
||||
|
if (uid == -1) { |
||||
|
uid = get_uid_sub(version, protocol, saddr, sport, daddr, dport, source, dest, now); |
||||
|
log_android(ANDROID_LOG_DEBUG, "uid v%d p%d %s/%u > %s/%u => %d fallback", |
||||
|
version, protocol, source, sport, dest, dport, uid); |
||||
|
} |
||||
|
|
||||
|
if (uid == -1) |
||||
|
log_android(ANDROID_LOG_WARN, "uid v%d p%d %s/%u > %s/%u => not found", |
||||
|
version, protocol, source, sport, dest, dport); |
||||
|
else if (uid >= 0) |
||||
|
log_android(ANDROID_LOG_INFO, "uid v%d p%d %s/%u > %s/%u => %d", |
||||
|
version, protocol, source, sport, dest, dport, uid); |
||||
|
|
||||
|
return uid; |
||||
|
} |
||||
|
|
||||
|
int uid_cache_size = 0; |
||||
|
struct uid_cache_entry *uid_cache = NULL; |
||||
|
|
||||
|
jint get_uid_sub(const int version, const int protocol, |
||||
|
const void *saddr, const uint16_t sport, |
||||
|
const void *daddr, const uint16_t dport, |
||||
|
const char *source, const char *dest, |
||||
|
long now) { |
||||
|
// NETLINK is not available on Android due to SELinux policies :-( |
||||
|
// http://stackoverflow.com/questions/27148536/netlink-implementation-for-the-android-ndk |
||||
|
// https://android.googlesource.com/platform/system/sepolicy/+/master/private/app.te (netlink_tcpdiag_socket) |
||||
|
|
||||
|
static uint8_t zero[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
||||
|
|
||||
|
int ws = (version == 4 ? 1 : 4); |
||||
|
|
||||
|
// Check cache |
||||
|
for (int i = 0; i < uid_cache_size; i++) |
||||
|
if (now - uid_cache[i].time <= UID_MAX_AGE && |
||||
|
uid_cache[i].version == version && |
||||
|
uid_cache[i].protocol == protocol && |
||||
|
uid_cache[i].sport == sport && |
||||
|
(uid_cache[i].dport == dport || uid_cache[i].dport == 0) && |
||||
|
(memcmp(uid_cache[i].saddr, saddr, (size_t) (ws * 4)) == 0 || |
||||
|
memcmp(uid_cache[i].saddr, zero, (size_t) (ws * 4)) == 0) && |
||||
|
(memcmp(uid_cache[i].daddr, daddr, (size_t) (ws * 4)) == 0 || |
||||
|
memcmp(uid_cache[i].daddr, zero, (size_t) (ws * 4)) == 0)) { |
||||
|
|
||||
|
log_android(ANDROID_LOG_INFO, "uid v%d p%d %s/%u > %s/%u => %d (from cache)", |
||||
|
version, protocol, source, sport, dest, dport, uid_cache[i].uid); |
||||
|
|
||||
|
return uid_cache[i].uid; |
||||
|
} |
||||
|
|
||||
|
// Get proc file name |
||||
|
char *fn = NULL; |
||||
|
if (protocol == IPPROTO_ICMP && version == 4) |
||||
|
fn = "/proc/net/icmp"; |
||||
|
else if (protocol == IPPROTO_ICMPV6 && version == 6) |
||||
|
fn = "/proc/net/icmp6"; |
||||
|
else if (protocol == IPPROTO_TCP) |
||||
|
fn = (version == 4 ? "/proc/net/tcp" : "/proc/net/tcp6"); |
||||
|
else if (protocol == IPPROTO_UDP) |
||||
|
fn = (version == 4 ? "/proc/net/udp" : "/proc/net/udp6"); |
||||
|
else |
||||
|
return -1; |
||||
|
|
||||
|
// Open proc file |
||||
|
FILE *fd = fopen(fn, "r"); |
||||
|
if (fd == NULL) { |
||||
|
log_android(ANDROID_LOG_ERROR, "fopen %s error %d: %s", fn, errno, strerror(errno)); |
||||
|
return -2; |
||||
|
} |
||||
|
|
||||
|
jint uid = -1; |
||||
|
|
||||
|
char line[250]; |
||||
|
int fields; |
||||
|
|
||||
|
char shex[16 * 2 + 1]; |
||||
|
uint8_t _saddr[16]; |
||||
|
int _sport; |
||||
|
|
||||
|
char dhex[16 * 2 + 1]; |
||||
|
uint8_t _daddr[16]; |
||||
|
int _dport; |
||||
|
|
||||
|
jint _uid; |
||||
|
|
||||
|
// Scan proc file |
||||
|
int l = 0; |
||||
|
*line = 0; |
||||
|
int c = 0; |
||||
|
const char *fmt = (version == 4 |
||||
|
? "%*d: %8s:%X %8s:%X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld" |
||||
|
: "%*d: %32s:%X %32s:%X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld"); |
||||
|
while (fgets(line, sizeof(line), fd) != NULL) { |
||||
|
if (!l++) |
||||
|
continue; |
||||
|
|
||||
|
fields = sscanf(line, fmt, shex, &_sport, dhex, &_dport, &_uid); |
||||
|
if (fields == 5 && strlen(shex) == ws * 8 && strlen(dhex) == ws * 8) { |
||||
|
hex2bytes(shex, _saddr); |
||||
|
hex2bytes(dhex, _daddr); |
||||
|
|
||||
|
for (int w = 0; w < ws; w++) |
||||
|
((uint32_t *) _saddr)[w] = htonl(((uint32_t *) _saddr)[w]); |
||||
|
|
||||
|
for (int w = 0; w < ws; w++) |
||||
|
((uint32_t *) _daddr)[w] = htonl(((uint32_t *) _daddr)[w]); |
||||
|
|
||||
|
if (_sport == sport && |
||||
|
(_dport == dport || _dport == 0) && |
||||
|
(memcmp(_saddr, saddr, (size_t) (ws * 4)) == 0 || |
||||
|
memcmp(_saddr, zero, (size_t) (ws * 4)) == 0) && |
||||
|
(memcmp(_daddr, daddr, (size_t) (ws * 4)) == 0 || |
||||
|
memcmp(_daddr, zero, (size_t) (ws * 4)) == 0)) |
||||
|
uid = _uid; |
||||
|
|
||||
|
for (; c < uid_cache_size; c++) |
||||
|
if (now - uid_cache[c].time > UID_MAX_AGE) |
||||
|
break; |
||||
|
|
||||
|
if (c >= uid_cache_size) { |
||||
|
if (uid_cache_size == 0) |
||||
|
uid_cache = ng_malloc(sizeof(struct uid_cache_entry), "uid_cache init"); |
||||
|
else |
||||
|
uid_cache = ng_realloc(uid_cache, |
||||
|
sizeof(struct uid_cache_entry) * |
||||
|
(uid_cache_size + 1), "uid_cache extend"); |
||||
|
c = uid_cache_size; |
||||
|
uid_cache_size++; |
||||
|
} |
||||
|
|
||||
|
uid_cache[c].version = (uint8_t) version; |
||||
|
uid_cache[c].protocol = (uint8_t) protocol; |
||||
|
memcpy(uid_cache[c].saddr, _saddr, (size_t) (ws * 4)); |
||||
|
uid_cache[c].sport = (uint16_t) _sport; |
||||
|
memcpy(uid_cache[c].daddr, _daddr, (size_t) (ws * 4)); |
||||
|
uid_cache[c].dport = (uint16_t) _dport; |
||||
|
uid_cache[c].uid = _uid; |
||||
|
uid_cache[c].time = now; |
||||
|
} else { |
||||
|
log_android(ANDROID_LOG_ERROR, "Invalid field #%d: %s", fields, line); |
||||
|
return -2; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (fclose(fd)) |
||||
|
log_android(ANDROID_LOG_ERROR, "fclose %s error %d: %s", fn, errno, strerror(errno)); |
||||
|
|
||||
|
return uid; |
||||
|
} |
1113
NetGuard/app/src/main/jni/netguard/netguard.c
File diff suppressed because it is too large
View File
@ -0,0 +1,589 @@ |
|||||
|
#include <jni.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <ctype.h> |
||||
|
#include <time.h> |
||||
|
#include <unistd.h> |
||||
|
#include <pthread.h> |
||||
|
#include <setjmp.h> |
||||
|
#include <errno.h> |
||||
|
#include <fcntl.h> |
||||
|
#include <dirent.h> |
||||
|
#include <poll.h> |
||||
|
#include <sys/types.h> |
||||
|
#include <sys/ioctl.h> |
||||
|
#include <sys/socket.h> |
||||
|
#include <sys/epoll.h> |
||||
|
#include <dlfcn.h> |
||||
|
#include <sys/stat.h> |
||||
|
#include <sys/resource.h> |
||||
|
|
||||
|
#include <netdb.h> |
||||
|
#include <arpa/inet.h> |
||||
|
#include <netinet/in.h> |
||||
|
#include <netinet/in6.h> |
||||
|
#include <netinet/ip.h> |
||||
|
#include <netinet/ip6.h> |
||||
|
#include <netinet/udp.h> |
||||
|
#include <netinet/tcp.h> |
||||
|
#include <netinet/ip_icmp.h> |
||||
|
#include <netinet/icmp6.h> |
||||
|
|
||||
|
#include <android/log.h> |
||||
|
#include <sys/system_properties.h> |
||||
|
|
||||
|
#define TAG "NetGuard.JNI" |
||||
|
|
||||
|
// #define PROFILE_JNI 5 |
||||
|
// #define PROFILE_MEMORY |
||||
|
|
||||
|
#define EPOLL_TIMEOUT 3600 // seconds |
||||
|
#define EPOLL_EVENTS 20 |
||||
|
#define EPOLL_MIN_CHECK 100 // milliseconds |
||||
|
|
||||
|
#define TUN_YIELD 10 // packets |
||||
|
|
||||
|
#define ICMP4_MAXMSG (IP_MAXPACKET - 20 - 8) // bytes (socket) |
||||
|
#define ICMP6_MAXMSG (IPV6_MAXPACKET - 40 - 8) // bytes (socket) |
||||
|
#define UDP4_MAXMSG (IP_MAXPACKET - 20 - 8) // bytes (socket) |
||||
|
#define UDP6_MAXMSG (IPV6_MAXPACKET - 40 - 8) // bytes (socket) |
||||
|
|
||||
|
#define ICMP_TIMEOUT 5 // seconds |
||||
|
|
||||
|
#define UDP_TIMEOUT_53 15 // seconds |
||||
|
#define UDP_TIMEOUT_ANY 300 // seconds |
||||
|
#define UDP_KEEP_TIMEOUT 60 // seconds |
||||
|
#define UDP_YIELD 10 // packets |
||||
|
|
||||
|
#define TCP_INIT_TIMEOUT 20 // seconds ~net.inet.tcp.keepinit |
||||
|
#define TCP_IDLE_TIMEOUT 3600 // seconds ~net.inet.tcp.keepidle |
||||
|
#define TCP_CLOSE_TIMEOUT 20 // seconds |
||||
|
#define TCP_KEEP_TIMEOUT 300 // seconds |
||||
|
// https://en.wikipedia.org/wiki/Maximum_segment_lifetime |
||||
|
|
||||
|
#define SESSION_LIMIT 40 // percent |
||||
|
#define SESSION_MAX (1024 * SESSION_LIMIT / 100) // number |
||||
|
|
||||
|
#define SEND_BUF_DEFAULT 163840 // bytes |
||||
|
|
||||
|
#define UID_MAX_AGE 30000 // milliseconds |
||||
|
|
||||
|
#define SOCKS5_NONE 1 |
||||
|
#define SOCKS5_HELLO 2 |
||||
|
#define SOCKS5_AUTH 3 |
||||
|
#define SOCKS5_CONNECT 4 |
||||
|
#define SOCKS5_CONNECTED 5 |
||||
|
|
||||
|
struct context { |
||||
|
pthread_mutex_t lock; |
||||
|
int pipefds[2]; |
||||
|
int stopping; |
||||
|
int sdk; |
||||
|
struct ng_session *ng_session; |
||||
|
}; |
||||
|
|
||||
|
struct arguments { |
||||
|
JNIEnv *env; |
||||
|
jobject instance; |
||||
|
int tun; |
||||
|
jboolean fwd53; |
||||
|
jint rcode; |
||||
|
struct context *ctx; |
||||
|
}; |
||||
|
|
||||
|
struct allowed { |
||||
|
char raddr[INET6_ADDRSTRLEN + 1]; |
||||
|
uint16_t rport; // host notation |
||||
|
}; |
||||
|
|
||||
|
struct segment { |
||||
|
uint32_t seq; |
||||
|
uint16_t len; |
||||
|
uint16_t sent; |
||||
|
int psh; |
||||
|
uint8_t *data; |
||||
|
struct segment *next; |
||||
|
}; |
||||
|
|
||||
|
struct icmp_session { |
||||
|
time_t time; |
||||
|
jint uid; |
||||
|
int version; |
||||
|
|
||||
|
union { |
||||
|
__be32 ip4; // network notation |
||||
|
struct in6_addr ip6; |
||||
|
} saddr; |
||||
|
|
||||
|
union { |
||||
|
__be32 ip4; // network notation |
||||
|
struct in6_addr ip6; |
||||
|
} daddr; |
||||
|
|
||||
|
uint16_t id; |
||||
|
|
||||
|
uint8_t stop; |
||||
|
}; |
||||
|
|
||||
|
#define UDP_ACTIVE 0 |
||||
|
#define UDP_FINISHING 1 |
||||
|
#define UDP_CLOSED 2 |
||||
|
#define UDP_BLOCKED 3 |
||||
|
|
||||
|
struct udp_session { |
||||
|
time_t time; |
||||
|
jint uid; |
||||
|
int version; |
||||
|
uint16_t mss; |
||||
|
|
||||
|
uint64_t sent; |
||||
|
uint64_t received; |
||||
|
|
||||
|
union { |
||||
|
__be32 ip4; // network notation |
||||
|
struct in6_addr ip6; |
||||
|
} saddr; |
||||
|
__be16 source; // network notation |
||||
|
|
||||
|
union { |
||||
|
__be32 ip4; // network notation |
||||
|
struct in6_addr ip6; |
||||
|
} daddr; |
||||
|
__be16 dest; // network notation |
||||
|
|
||||
|
uint8_t state; |
||||
|
}; |
||||
|
|
||||
|
struct tcp_session { |
||||
|
jint uid; |
||||
|
time_t time; |
||||
|
int version; |
||||
|
uint16_t mss; |
||||
|
uint8_t recv_scale; |
||||
|
uint8_t send_scale; |
||||
|
uint32_t recv_window; // host notation, scaled |
||||
|
uint32_t send_window; // host notation, scaled |
||||
|
uint16_t unconfirmed; // packets |
||||
|
|
||||
|
uint32_t remote_seq; // confirmed bytes received, host notation |
||||
|
uint32_t local_seq; // confirmed bytes sent, host notation |
||||
|
uint32_t remote_start; |
||||
|
uint32_t local_start; |
||||
|
|
||||
|
uint32_t acked; // host notation |
||||
|
long long last_keep_alive; |
||||
|
|
||||
|
uint64_t sent; |
||||
|
uint64_t received; |
||||
|
|
||||
|
union { |
||||
|
__be32 ip4; // network notation |
||||
|
struct in6_addr ip6; |
||||
|
} saddr; |
||||
|
__be16 source; // network notation |
||||
|
|
||||
|
union { |
||||
|
__be32 ip4; // network notation |
||||
|
struct in6_addr ip6; |
||||
|
} daddr; |
||||
|
__be16 dest; // network notation |
||||
|
|
||||
|
uint8_t state; |
||||
|
uint8_t socks5; |
||||
|
struct segment *forward; |
||||
|
}; |
||||
|
|
||||
|
struct ng_session { |
||||
|
uint8_t protocol; |
||||
|
union { |
||||
|
struct icmp_session icmp; |
||||
|
struct udp_session udp; |
||||
|
struct tcp_session tcp; |
||||
|
}; |
||||
|
jint socket; |
||||
|
struct epoll_event ev; |
||||
|
struct ng_session *next; |
||||
|
}; |
||||
|
|
||||
|
struct uid_cache_entry { |
||||
|
uint8_t version; |
||||
|
uint8_t protocol; |
||||
|
uint8_t saddr[16]; |
||||
|
uint16_t sport; |
||||
|
uint8_t daddr[16]; |
||||
|
uint16_t dport; |
||||
|
jint uid; |
||||
|
long time; |
||||
|
}; |
||||
|
|
||||
|
// IPv6 |
||||
|
|
||||
|
struct ip6_hdr_pseudo { |
||||
|
struct in6_addr ip6ph_src; |
||||
|
struct in6_addr ip6ph_dst; |
||||
|
u_int32_t ip6ph_len; |
||||
|
u_int8_t ip6ph_zero[3]; |
||||
|
u_int8_t ip6ph_nxt; |
||||
|
} __packed; |
||||
|
|
||||
|
// PCAP |
||||
|
// https://wiki.wireshark.org/Development/LibpcapFileFormat |
||||
|
|
||||
|
typedef uint16_t guint16_t; |
||||
|
typedef uint32_t guint32_t; |
||||
|
typedef int32_t gint32_t; |
||||
|
|
||||
|
typedef struct pcap_hdr_s { |
||||
|
guint32_t magic_number; |
||||
|
guint16_t version_major; |
||||
|
guint16_t version_minor; |
||||
|
gint32_t thiszone; |
||||
|
guint32_t sigfigs; |
||||
|
guint32_t snaplen; |
||||
|
guint32_t network; |
||||
|
} __packed pcap_hdr_s; |
||||
|
|
||||
|
typedef struct pcaprec_hdr_s { |
||||
|
guint32_t ts_sec; |
||||
|
guint32_t ts_usec; |
||||
|
guint32_t incl_len; |
||||
|
guint32_t orig_len; |
||||
|
} __packed pcaprec_hdr_s; |
||||
|
|
||||
|
#define LINKTYPE_RAW 101 |
||||
|
|
||||
|
// DNS |
||||
|
|
||||
|
#define DNS_QCLASS_IN 1 |
||||
|
#define DNS_QTYPE_A 1 // IPv4 |
||||
|
#define DNS_QTYPE_AAAA 28 // IPv6 |
||||
|
|
||||
|
#define DNS_SVCB 64 |
||||
|
#define DNS_HTTPS 65 |
||||
|
|
||||
|
#define DNS_QNAME_MAX 255 |
||||
|
#define DNS_TTL (10 * 60) // seconds |
||||
|
|
||||
|
struct dns_header { |
||||
|
uint16_t id; // identification number |
||||
|
# if __BYTE_ORDER == __LITTLE_ENDIAN |
||||
|
uint16_t rd :1; // recursion desired |
||||
|
uint16_t tc :1; // truncated message |
||||
|
uint16_t aa :1; // authoritive answer |
||||
|
uint16_t opcode :4; // purpose of message |
||||
|
uint16_t qr :1; // query/response flag |
||||
|
uint16_t rcode :4; // response code |
||||
|
uint16_t cd :1; // checking disabled |
||||
|
uint16_t ad :1; // authenticated data |
||||
|
uint16_t z :1; // its z! reserved |
||||
|
uint16_t ra :1; // recursion available |
||||
|
#elif __BYTE_ORDER == __BIG_ENDIAN |
||||
|
uint16_t qr :1; // query/response flag |
||||
|
uint16_t opcode :4; // purpose of message |
||||
|
uint16_t aa :1; // authoritive answer |
||||
|
uint16_t tc :1; // truncated message |
||||
|
uint16_t rd :1; // recursion desired |
||||
|
uint16_t ra :1; // recursion available |
||||
|
uint16_t z :1; // its z! reserved |
||||
|
uint16_t ad :1; // authenticated data |
||||
|
uint16_t cd :1; // checking disabled |
||||
|
uint16_t rcode :4; // response code |
||||
|
# else |
||||
|
# error "Adjust your <bits/endian.h> defines" |
||||
|
#endif |
||||
|
uint16_t q_count; // number of question entries |
||||
|
uint16_t ans_count; // number of answer entries |
||||
|
uint16_t auth_count; // number of authority entries |
||||
|
uint16_t add_count; // number of resource entries |
||||
|
} __packed; |
||||
|
|
||||
|
typedef struct dns_rr { |
||||
|
__be16 qname_ptr; |
||||
|
__be16 qtype; |
||||
|
__be16 qclass; |
||||
|
__be32 ttl; |
||||
|
__be16 rdlength; |
||||
|
} __packed dns_rr; |
||||
|
|
||||
|
// DHCP |
||||
|
|
||||
|
#define DHCP_OPTION_MAGIC_NUMBER (0x63825363) |
||||
|
|
||||
|
typedef struct dhcp_packet { |
||||
|
uint8_t opcode; |
||||
|
uint8_t htype; |
||||
|
uint8_t hlen; |
||||
|
uint8_t hops; |
||||
|
uint32_t xid; |
||||
|
uint16_t secs; |
||||
|
uint16_t flags; |
||||
|
uint32_t ciaddr; |
||||
|
uint32_t yiaddr; |
||||
|
uint32_t siaddr; |
||||
|
uint32_t giaddr; |
||||
|
uint8_t chaddr[16]; |
||||
|
uint8_t sname[64]; |
||||
|
uint8_t file[128]; |
||||
|
uint32_t option_format; |
||||
|
} __packed dhcp_packet; |
||||
|
|
||||
|
typedef struct dhcp_option { |
||||
|
uint8_t code; |
||||
|
uint8_t length; |
||||
|
} __packed dhcp_option; |
||||
|
|
||||
|
// Prototypes |
||||
|
|
||||
|
void handle_signal(int sig, siginfo_t *info, void *context); |
||||
|
|
||||
|
void *handle_events(void *a); |
||||
|
|
||||
|
void report_exit(const struct arguments *args, const char *fmt, ...); |
||||
|
|
||||
|
void report_error(const struct arguments *args, jint error, const char *fmt, ...); |
||||
|
|
||||
|
void check_allowed(const struct arguments *args); |
||||
|
|
||||
|
void clear(struct context *ctx); |
||||
|
|
||||
|
int check_icmp_session(const struct arguments *args, |
||||
|
struct ng_session *s, |
||||
|
int sessions, int maxsessions); |
||||
|
|
||||
|
int check_udp_session(const struct arguments *args, |
||||
|
struct ng_session *s, |
||||
|
int sessions, int maxsessions); |
||||
|
|
||||
|
int check_tcp_session(const struct arguments *args, |
||||
|
struct ng_session *s, |
||||
|
int sessions, int maxsessions); |
||||
|
|
||||
|
int monitor_tcp_session(const struct arguments *args, struct ng_session *s, int epoll_fd); |
||||
|
|
||||
|
int get_icmp_timeout(const struct icmp_session *u, int sessions, int maxsessions); |
||||
|
|
||||
|
int get_udp_timeout(const struct udp_session *u, int sessions, int maxsessions); |
||||
|
|
||||
|
int get_tcp_timeout(const struct tcp_session *t, int sessions, int maxsessions); |
||||
|
|
||||
|
uint16_t get_mtu(); |
||||
|
|
||||
|
uint16_t get_default_mss(int version); |
||||
|
|
||||
|
int check_tun(const struct arguments *args, |
||||
|
const struct epoll_event *ev, |
||||
|
const int epoll_fd, |
||||
|
int sessions, int maxsessions); |
||||
|
|
||||
|
void check_icmp_socket(const struct arguments *args, const struct epoll_event *ev); |
||||
|
|
||||
|
void check_udp_socket(const struct arguments *args, const struct epoll_event *ev); |
||||
|
|
||||
|
int32_t get_qname(const uint8_t *data, const size_t datalen, uint16_t off, char *qname); |
||||
|
|
||||
|
void parse_dns_response(const struct arguments *args, const struct ng_session *session, |
||||
|
const uint8_t *data, size_t *datalen); |
||||
|
|
||||
|
uint32_t get_send_window(const struct tcp_session *cur); |
||||
|
|
||||
|
uint32_t get_receive_buffer(const struct ng_session *cur); |
||||
|
|
||||
|
uint32_t get_receive_window(const struct ng_session *cur); |
||||
|
|
||||
|
void check_tcp_socket(const struct arguments *args, |
||||
|
const struct epoll_event *ev, |
||||
|
const int epoll_fd); |
||||
|
|
||||
|
int is_lower_layer(int protocol); |
||||
|
|
||||
|
int is_upper_layer(int protocol); |
||||
|
|
||||
|
void handle_ip(const struct arguments *args, |
||||
|
const uint8_t *buffer, size_t length, |
||||
|
const int epoll_fd, |
||||
|
int sessions, int maxsessions); |
||||
|
|
||||
|
jboolean handle_icmp(const struct arguments *args, |
||||
|
const uint8_t *pkt, size_t length, |
||||
|
const uint8_t *payload, |
||||
|
int uid, |
||||
|
const int epoll_fd); |
||||
|
|
||||
|
int has_udp_session(const struct arguments *args, const uint8_t *pkt, const uint8_t *payload); |
||||
|
|
||||
|
void block_udp(const struct arguments *args, |
||||
|
const uint8_t *pkt, size_t length, |
||||
|
const uint8_t *payload, |
||||
|
int uid); |
||||
|
|
||||
|
jboolean handle_udp(const struct arguments *args, |
||||
|
const uint8_t *pkt, size_t length, |
||||
|
const uint8_t *payload, |
||||
|
int uid, struct allowed *redirect, |
||||
|
const int epoll_fd); |
||||
|
|
||||
|
int check_dhcp(const struct arguments *args, const struct udp_session *u, |
||||
|
const uint8_t *data, const size_t datalen); |
||||
|
|
||||
|
void clear_tcp_data(struct tcp_session *cur); |
||||
|
|
||||
|
jboolean handle_tcp(const struct arguments *args, |
||||
|
const uint8_t *pkt, size_t length, |
||||
|
const uint8_t *payload, |
||||
|
int uid, int allowed, struct allowed *redirect, |
||||
|
const int epoll_fd); |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
jboolean handle_tcp_debug(const struct arguments *args, |
||||
|
const uint8_t *pkt, size_t length, |
||||
|
const uint8_t *payload, |
||||
|
int uid, int allowed, struct allowed *redirect, |
||||
|
const int epoll_fd); |
||||
|
|
||||
|
|
||||
|
|
||||
|
void debug_socket_init(const struct arguments *args, int epoll_fd); |
||||
|
void read_debug_socket(); |
||||
|
void write_debug_socket(const struct arguments *args, const uint8_t *buffer, size_t length); |
||||
|
|
||||
|
|
||||
|
|
||||
|
void queue_tcp(const struct arguments *args, |
||||
|
const struct tcphdr *tcphdr, |
||||
|
const char *session, struct tcp_session *cur, |
||||
|
const uint8_t *data, uint16_t datalen); |
||||
|
|
||||
|
int open_icmp_socket(const struct arguments *args, const struct icmp_session *cur); |
||||
|
|
||||
|
int open_udp_socket(const struct arguments *args, |
||||
|
const struct udp_session *cur, const struct allowed *redirect); |
||||
|
|
||||
|
int open_tcp_socket(const struct arguments *args, |
||||
|
const struct tcp_session *cur, const struct allowed *redirect); |
||||
|
|
||||
|
int32_t get_local_port(const int sock); |
||||
|
|
||||
|
int write_syn_ack(const struct arguments *args, struct tcp_session *cur); |
||||
|
|
||||
|
int write_ack(const struct arguments *args, struct tcp_session *cur); |
||||
|
|
||||
|
int write_data(const struct arguments *args, struct tcp_session *cur, |
||||
|
const uint8_t *buffer, size_t length); |
||||
|
|
||||
|
int write_fin_ack(const struct arguments *args, struct tcp_session *cur); |
||||
|
|
||||
|
void write_rst(const struct arguments *args, struct tcp_session *cur); |
||||
|
|
||||
|
void write_rst_ack(const struct arguments *args, struct tcp_session *cur); |
||||
|
|
||||
|
ssize_t write_icmp(const struct arguments *args, const struct icmp_session *cur, |
||||
|
uint8_t *data, size_t datalen); |
||||
|
|
||||
|
ssize_t write_udp(const struct arguments *args, const struct udp_session *cur, |
||||
|
uint8_t *data, size_t datalen); |
||||
|
|
||||
|
ssize_t write_tcp(const struct arguments *args, const struct tcp_session *cur, |
||||
|
const uint8_t *data, size_t datalen, |
||||
|
int syn, int ack, int fin, int rst); |
||||
|
|
||||
|
uint8_t char2nible(const char c); |
||||
|
|
||||
|
void hex2bytes(const char *hex, uint8_t *buffer); |
||||
|
|
||||
|
jint get_uid(const int version, const int protocol, |
||||
|
const void *saddr, const uint16_t sport, |
||||
|
const void *daddr, const uint16_t dport); |
||||
|
|
||||
|
jint get_uid_sub(const int version, const int protocol, |
||||
|
const void *saddr, const uint16_t sport, |
||||
|
const void *daddr, const uint16_t dport, |
||||
|
const char *source, const char *dest, |
||||
|
long now); |
||||
|
|
||||
|
int protect_socket(const struct arguments *args, int socket); |
||||
|
|
||||
|
uint16_t calc_checksum(uint16_t start, const uint8_t *buffer, size_t length); |
||||
|
|
||||
|
jobject jniGlobalRef(JNIEnv *env, jobject cls); |
||||
|
|
||||
|
jclass jniFindClass(JNIEnv *env, const char *name); |
||||
|
|
||||
|
jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature); |
||||
|
|
||||
|
jfieldID jniGetFieldID(JNIEnv *env, jclass cls, const char *name, const char *type); |
||||
|
|
||||
|
jobject jniNewObject(JNIEnv *env, jclass cls, jmethodID constructor, const char *name); |
||||
|
|
||||
|
int jniCheckException(JNIEnv *env); |
||||
|
|
||||
|
int sdk_int(JNIEnv *env); |
||||
|
|
||||
|
void log_android(int prio, const char *fmt, ...); |
||||
|
|
||||
|
void log_packet(const struct arguments *args, jobject jpacket); |
||||
|
|
||||
|
void dns_resolved(const struct arguments *args, |
||||
|
const char *qname, const char *aname, const char *resource, int ttl); |
||||
|
|
||||
|
jboolean is_domain_blocked(const struct arguments *args, const char *name); |
||||
|
|
||||
|
jint get_uid_q(const struct arguments *args, |
||||
|
jint version, |
||||
|
jint protocol, |
||||
|
const char *source, |
||||
|
jint sport, |
||||
|
const char *dest, |
||||
|
jint dport); |
||||
|
|
||||
|
struct allowed *is_address_allowed(const struct arguments *args, jobject objPacket); |
||||
|
|
||||
|
jobject create_packet(const struct arguments *args, |
||||
|
jint version, |
||||
|
jint protocol, |
||||
|
const char *flags, |
||||
|
const char *source, |
||||
|
jint sport, |
||||
|
const char *dest, |
||||
|
jint dport, |
||||
|
const char *data, |
||||
|
jint uid, |
||||
|
jboolean allowed); |
||||
|
|
||||
|
void account_usage(const struct arguments *args, jint version, jint protocol, |
||||
|
const char *daddr, jint dport, jint uid, jlong sent, jlong received); |
||||
|
|
||||
|
void write_pcap_hdr(); |
||||
|
|
||||
|
void write_pcap_rec(const uint8_t *buffer, size_t len); |
||||
|
|
||||
|
void write_pcap(const void *ptr, size_t len); |
||||
|
|
||||
|
int compare_u32(uint32_t seq1, uint32_t seq2); |
||||
|
|
||||
|
const char *strstate(const int state); |
||||
|
|
||||
|
char *hex(const u_int8_t *data, const size_t len); |
||||
|
|
||||
|
int is_readable(int fd); |
||||
|
|
||||
|
int is_writable(int fd); |
||||
|
|
||||
|
long long get_ms(); |
||||
|
|
||||
|
void ng_add_alloc(void *ptr, const char *tag); |
||||
|
|
||||
|
void ng_delete_alloc(void *ptr, const char *file, int line); |
||||
|
|
||||
|
void *ng_malloc(size_t __byte_count, const char *tag); |
||||
|
|
||||
|
void *ng_calloc(size_t __item_count, size_t __item_size, const char *tag); |
||||
|
|
||||
|
void *ng_realloc(void *__ptr, size_t __byte_count, const char *tag); |
||||
|
|
||||
|
void ng_free(void *__ptr, const char *file, int line); |
||||
|
|
||||
|
void ng_dump(); |
@ -0,0 +1,78 @@ |
|||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
#include "netguard.h" |
||||
|
|
||||
|
FILE *pcap_file = NULL; |
||||
|
size_t pcap_record_size = 64; |
||||
|
long pcap_file_size = 2 * 1024 * 1024; |
||||
|
|
||||
|
void write_pcap_hdr() { |
||||
|
struct pcap_hdr_s pcap_hdr; |
||||
|
pcap_hdr.magic_number = 0xa1b2c3d4; |
||||
|
pcap_hdr.version_major = 2; |
||||
|
pcap_hdr.version_minor = 4; |
||||
|
pcap_hdr.thiszone = 0; |
||||
|
pcap_hdr.sigfigs = 0; |
||||
|
pcap_hdr.snaplen = pcap_record_size; |
||||
|
pcap_hdr.network = LINKTYPE_RAW; |
||||
|
write_pcap(&pcap_hdr, sizeof(struct pcap_hdr_s)); |
||||
|
} |
||||
|
|
||||
|
void write_pcap_rec(const uint8_t *buffer, size_t length) { |
||||
|
struct timespec ts; |
||||
|
if (clock_gettime(CLOCK_REALTIME, &ts)) |
||||
|
log_android(ANDROID_LOG_ERROR, "clock_gettime error %d: %s", errno, strerror(errno)); |
||||
|
|
||||
|
size_t plen = (length < pcap_record_size ? length : pcap_record_size); |
||||
|
size_t rlen = sizeof(struct pcaprec_hdr_s) + plen; |
||||
|
struct pcaprec_hdr_s *pcap_rec = ng_malloc(rlen, "pcap"); |
||||
|
|
||||
|
pcap_rec->ts_sec = (guint32_t) ts.tv_sec; |
||||
|
pcap_rec->ts_usec = (guint32_t) (ts.tv_nsec / 1000); |
||||
|
pcap_rec->incl_len = (guint32_t) plen; |
||||
|
pcap_rec->orig_len = (guint32_t) length; |
||||
|
|
||||
|
memcpy(((uint8_t *) pcap_rec) + sizeof(struct pcaprec_hdr_s), buffer, plen); |
||||
|
|
||||
|
write_pcap(pcap_rec, rlen); |
||||
|
|
||||
|
ng_free(pcap_rec, __FILE__, __LINE__); |
||||
|
} |
||||
|
|
||||
|
void write_pcap(const void *ptr, size_t len) { |
||||
|
if (fwrite(ptr, len, 1, pcap_file) < 1) |
||||
|
log_android(ANDROID_LOG_ERROR, "PCAP fwrite error %d: %s", errno, strerror(errno)); |
||||
|
else { |
||||
|
long fsize = ftell(pcap_file); |
||||
|
log_android(ANDROID_LOG_VERBOSE, "PCAP wrote %d @%ld", len, fsize); |
||||
|
|
||||
|
if (fsize > pcap_file_size) { |
||||
|
log_android(ANDROID_LOG_WARN, "PCAP truncate @%ld", fsize); |
||||
|
if (ftruncate(fileno(pcap_file), sizeof(struct pcap_hdr_s))) |
||||
|
log_android(ANDROID_LOG_ERROR, "PCAP ftruncate error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
else { |
||||
|
if (!lseek(fileno(pcap_file), sizeof(struct pcap_hdr_s), SEEK_SET)) |
||||
|
log_android(ANDROID_LOG_ERROR, "PCAP ftruncate error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,370 @@ |
|||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
#include "netguard.h" |
||||
|
|
||||
|
void clear(struct context *ctx) { |
||||
|
struct ng_session *s = ctx->ng_session; |
||||
|
while (s != NULL) { |
||||
|
if (s->socket >= 0 && close(s->socket)) |
||||
|
log_android(ANDROID_LOG_ERROR, "close %d error %d: %s", |
||||
|
s->socket, errno, strerror(errno)); |
||||
|
if (s->protocol == IPPROTO_TCP) |
||||
|
clear_tcp_data(&s->tcp); |
||||
|
struct ng_session *p = s; |
||||
|
s = s->next; |
||||
|
ng_free(p, __FILE__, __LINE__); |
||||
|
} |
||||
|
ctx->ng_session = NULL; |
||||
|
} |
||||
|
|
||||
|
void *handle_events(void *a) { |
||||
|
struct arguments *args = (struct arguments *) a; |
||||
|
log_android(ANDROID_LOG_ERROR, "Start events tun=%d", args->tun); |
||||
|
|
||||
|
// Get max number of sessions |
||||
|
int maxsessions = SESSION_MAX; |
||||
|
struct rlimit rlim; |
||||
|
if (getrlimit(RLIMIT_NOFILE, &rlim)) |
||||
|
log_android(ANDROID_LOG_WARN, "getrlimit error %d: %s", errno, strerror(errno)); |
||||
|
else { |
||||
|
maxsessions = (int) (rlim.rlim_cur * SESSION_LIMIT / 100); |
||||
|
if (maxsessions > SESSION_MAX) |
||||
|
maxsessions = SESSION_MAX; |
||||
|
log_android(ANDROID_LOG_WARN, "getrlimit soft %d hard %d max sessions %d", |
||||
|
rlim.rlim_cur, rlim.rlim_max, maxsessions); |
||||
|
} |
||||
|
|
||||
|
// Terminate existing sessions not allowed anymore |
||||
|
check_allowed(args); |
||||
|
|
||||
|
// Open epoll file |
||||
|
int epoll_fd = epoll_create(1); |
||||
|
if (epoll_fd < 0) { |
||||
|
log_android(ANDROID_LOG_ERROR, "epoll create error %d: %s", errno, strerror(errno)); |
||||
|
report_exit(args, "epoll create error %d: %s", errno, strerror(errno)); |
||||
|
args->ctx->stopping = 1; |
||||
|
} |
||||
|
|
||||
|
// Monitor stop events |
||||
|
struct epoll_event ev_pipe; |
||||
|
memset(&ev_pipe, 0, sizeof(struct epoll_event)); |
||||
|
ev_pipe.events = EPOLLIN | EPOLLERR; |
||||
|
ev_pipe.data.ptr = &ev_pipe; |
||||
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, args->ctx->pipefds[0], &ev_pipe)) { |
||||
|
log_android(ANDROID_LOG_ERROR, "epoll add pipe error %d: %s", errno, strerror(errno)); |
||||
|
report_exit(args, "epoll add pipe error %d: %s", errno, strerror(errno)); |
||||
|
args->ctx->stopping = 1; |
||||
|
} |
||||
|
|
||||
|
// Monitor tun events |
||||
|
struct epoll_event ev_tun; |
||||
|
memset(&ev_tun, 0, sizeof(struct epoll_event)); |
||||
|
ev_tun.events = EPOLLIN | EPOLLERR; |
||||
|
ev_tun.data.ptr = NULL; |
||||
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, args->tun, &ev_tun)) { |
||||
|
log_android(ANDROID_LOG_ERROR, "epoll add tun error %d: %s", errno, strerror(errno)); |
||||
|
report_exit(args, "epoll add tun error %d: %s", errno, strerror(errno)); |
||||
|
args->ctx->stopping = 1; |
||||
|
} |
||||
|
|
||||
|
// Loop |
||||
|
long long last_check = 0; |
||||
|
while (!args->ctx->stopping) { |
||||
|
log_android(ANDROID_LOG_DEBUG, "Loop"); |
||||
|
|
||||
|
int recheck = 0; |
||||
|
int timeout = EPOLL_TIMEOUT; |
||||
|
|
||||
|
// Count sessions |
||||
|
int isessions = 0; |
||||
|
int usessions = 0; |
||||
|
int tsessions = 0; |
||||
|
struct ng_session *s = args->ctx->ng_session; |
||||
|
while (s != NULL) { |
||||
|
if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) { |
||||
|
if (!s->icmp.stop) |
||||
|
isessions++; |
||||
|
} else if (s->protocol == IPPROTO_UDP) { |
||||
|
if (s->udp.state == UDP_ACTIVE) |
||||
|
usessions++; |
||||
|
} else if (s->protocol == IPPROTO_TCP) { |
||||
|
if (s->tcp.state != TCP_CLOSING && s->tcp.state != TCP_CLOSE) |
||||
|
tsessions++; |
||||
|
if (s->socket >= 0) |
||||
|
recheck = recheck | monitor_tcp_session(args, s, epoll_fd); |
||||
|
} |
||||
|
s = s->next; |
||||
|
} |
||||
|
int sessions = isessions + usessions + tsessions; |
||||
|
|
||||
|
// Check sessions |
||||
|
long long ms = get_ms(); |
||||
|
if (ms - last_check > EPOLL_MIN_CHECK) { |
||||
|
last_check = ms; |
||||
|
|
||||
|
time_t now = time(NULL); |
||||
|
struct ng_session *sl = NULL; |
||||
|
s = args->ctx->ng_session; |
||||
|
while (s != NULL) { |
||||
|
int del = 0; |
||||
|
if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) { |
||||
|
del = check_icmp_session(args, s, sessions, maxsessions); |
||||
|
if (!s->icmp.stop && !del) { |
||||
|
int stimeout = s->icmp.time + |
||||
|
get_icmp_timeout(&s->icmp, sessions, maxsessions) - now + 1; |
||||
|
if (stimeout > 0 && stimeout < timeout) |
||||
|
timeout = stimeout; |
||||
|
} |
||||
|
} else if (s->protocol == IPPROTO_UDP) { |
||||
|
del = check_udp_session(args, s, sessions, maxsessions); |
||||
|
if (s->udp.state == UDP_ACTIVE && !del) { |
||||
|
int stimeout = s->udp.time + |
||||
|
get_udp_timeout(&s->udp, sessions, maxsessions) - now + 1; |
||||
|
if (stimeout > 0 && stimeout < timeout) |
||||
|
timeout = stimeout; |
||||
|
} |
||||
|
} else if (s->protocol == IPPROTO_TCP) { |
||||
|
del = check_tcp_session(args, s, sessions, maxsessions); |
||||
|
if (s->tcp.state != TCP_CLOSING && s->tcp.state != TCP_CLOSE && !del) { |
||||
|
int stimeout = s->tcp.time + |
||||
|
get_tcp_timeout(&s->tcp, sessions, maxsessions) - now + 1; |
||||
|
if (stimeout > 0 && stimeout < timeout) |
||||
|
timeout = stimeout; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (del) { |
||||
|
if (sl == NULL) |
||||
|
args->ctx->ng_session = s->next; |
||||
|
else |
||||
|
sl->next = s->next; |
||||
|
|
||||
|
struct ng_session *c = s; |
||||
|
s = s->next; |
||||
|
if (c->protocol == IPPROTO_TCP) |
||||
|
clear_tcp_data(&c->tcp); |
||||
|
ng_free(c, __FILE__, __LINE__); |
||||
|
} else { |
||||
|
sl = s; |
||||
|
s = s->next; |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
recheck = 1; |
||||
|
log_android(ANDROID_LOG_DEBUG, "Skipped session checks"); |
||||
|
} |
||||
|
|
||||
|
log_android(ANDROID_LOG_DEBUG, |
||||
|
"sessions ICMP %d UDP %d TCP %d max %d/%d timeout %d recheck %d", |
||||
|
isessions, usessions, tsessions, sessions, maxsessions, timeout, recheck); |
||||
|
|
||||
|
// Poll |
||||
|
struct epoll_event ev[EPOLL_EVENTS]; |
||||
|
int ready = epoll_wait(epoll_fd, ev, EPOLL_EVENTS, |
||||
|
recheck ? EPOLL_MIN_CHECK : timeout * 1000); |
||||
|
|
||||
|
if (ready < 0) { |
||||
|
if (errno == EINTR) { |
||||
|
log_android(ANDROID_LOG_DEBUG, "epoll interrupted tun %d", args->tun); |
||||
|
continue; |
||||
|
} else { |
||||
|
log_android(ANDROID_LOG_ERROR, |
||||
|
"epoll tun %d error %d: %s", |
||||
|
args->tun, errno, strerror(errno)); |
||||
|
report_exit(args, "epoll tun %d error %d: %s", |
||||
|
args->tun, errno, strerror(errno)); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (ready == 0) |
||||
|
log_android(ANDROID_LOG_DEBUG, "epoll timeout"); |
||||
|
else { |
||||
|
|
||||
|
if (pthread_mutex_lock(&args->ctx->lock)) |
||||
|
log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed"); |
||||
|
|
||||
|
int error = 0; |
||||
|
|
||||
|
for (int i = 0; i < ready; i++) { |
||||
|
if (ev[i].data.ptr == &ev_pipe) { |
||||
|
// Check pipe |
||||
|
uint8_t buffer[1]; |
||||
|
if (read(args->ctx->pipefds[0], buffer, 1) < 0) |
||||
|
log_android(ANDROID_LOG_WARN, "Read pipe error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
else |
||||
|
log_android(ANDROID_LOG_WARN, "Read pipe"); |
||||
|
|
||||
|
} else if (ev[i].data.ptr == NULL) { |
||||
|
// Check upstream |
||||
|
log_android(ANDROID_LOG_DEBUG, "epoll ready %d/%d in %d out %d err %d hup %d", |
||||
|
i, ready, |
||||
|
(ev[i].events & EPOLLIN) != 0, |
||||
|
(ev[i].events & EPOLLOUT) != 0, |
||||
|
(ev[i].events & EPOLLERR) != 0, |
||||
|
(ev[i].events & EPOLLHUP) != 0); |
||||
|
|
||||
|
int count = 0; |
||||
|
while (count < TUN_YIELD && !error && !args->ctx->stopping && |
||||
|
is_readable(args->tun)) { |
||||
|
count++; |
||||
|
if (check_tun(args, &ev[i], epoll_fd, sessions, maxsessions) < 0) |
||||
|
error = 1; |
||||
|
} |
||||
|
|
||||
|
} else { |
||||
|
// Check downstream |
||||
|
log_android(ANDROID_LOG_DEBUG, |
||||
|
"epoll ready %d/%d in %d out %d err %d hup %d prot %d sock %d", |
||||
|
i, ready, |
||||
|
(ev[i].events & EPOLLIN) != 0, |
||||
|
(ev[i].events & EPOLLOUT) != 0, |
||||
|
(ev[i].events & EPOLLERR) != 0, |
||||
|
(ev[i].events & EPOLLHUP) != 0, |
||||
|
((struct ng_session *) ev[i].data.ptr)->protocol, |
||||
|
((struct ng_session *) ev[i].data.ptr)->socket); |
||||
|
|
||||
|
struct ng_session *session = (struct ng_session *) ev[i].data.ptr; |
||||
|
if (session->protocol == IPPROTO_ICMP || |
||||
|
session->protocol == IPPROTO_ICMPV6) |
||||
|
check_icmp_socket(args, &ev[i]); |
||||
|
else if (session->protocol == IPPROTO_UDP) { |
||||
|
int count = 0; |
||||
|
while (count < UDP_YIELD && !args->ctx->stopping && |
||||
|
!(ev[i].events & EPOLLERR) && (ev[i].events & EPOLLIN) && |
||||
|
is_readable(session->socket)) { |
||||
|
count++; |
||||
|
check_udp_socket(args, &ev[i]); |
||||
|
} |
||||
|
} else if (session->protocol == IPPROTO_TCP) |
||||
|
check_tcp_socket(args, &ev[i], epoll_fd); |
||||
|
} |
||||
|
|
||||
|
if (error) |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (pthread_mutex_unlock(&args->ctx->lock)) |
||||
|
log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed"); |
||||
|
|
||||
|
if (error) |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Close epoll file |
||||
|
if (epoll_fd >= 0 && close(epoll_fd)) |
||||
|
log_android(ANDROID_LOG_ERROR, |
||||
|
"epoll close error %d: %s", errno, strerror(errno)); |
||||
|
|
||||
|
// Cleanup |
||||
|
ng_free(args, __FILE__, __LINE__); |
||||
|
|
||||
|
log_android(ANDROID_LOG_WARN, "Stopped events tun=%d", args->tun); |
||||
|
return NULL; |
||||
|
} |
||||
|
|
||||
|
void check_allowed(const struct arguments *args) { |
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
|
||||
|
struct ng_session *l = NULL; |
||||
|
struct ng_session *s = args->ctx->ng_session; |
||||
|
while (s != NULL) { |
||||
|
if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) { |
||||
|
if (!s->icmp.stop) { |
||||
|
if (s->icmp.version == 4) { |
||||
|
inet_ntop(AF_INET, &s->icmp.saddr.ip4, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, &s->icmp.daddr.ip4, dest, sizeof(dest)); |
||||
|
} else { |
||||
|
inet_ntop(AF_INET6, &s->icmp.saddr.ip6, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET6, &s->icmp.daddr.ip6, dest, sizeof(dest)); |
||||
|
} |
||||
|
|
||||
|
jobject objPacket = create_packet( |
||||
|
args, s->icmp.version, IPPROTO_ICMP, "", |
||||
|
source, 0, dest, 0, "", s->icmp.uid, 0); |
||||
|
if (is_address_allowed(args, objPacket) == NULL) { |
||||
|
s->icmp.stop = 1; |
||||
|
log_android(ANDROID_LOG_WARN, "ICMP terminate %d uid %d", |
||||
|
s->socket, s->icmp.uid); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} else if (s->protocol == IPPROTO_UDP) { |
||||
|
if (s->udp.state == UDP_ACTIVE) { |
||||
|
if (s->udp.version == 4) { |
||||
|
inet_ntop(AF_INET, &s->udp.saddr.ip4, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest)); |
||||
|
} else { |
||||
|
inet_ntop(AF_INET6, &s->udp.saddr.ip6, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest)); |
||||
|
} |
||||
|
|
||||
|
jobject objPacket = create_packet( |
||||
|
args, s->udp.version, IPPROTO_UDP, "", |
||||
|
source, ntohs(s->udp.source), dest, ntohs(s->udp.dest), "", s->udp.uid, 0); |
||||
|
if (is_address_allowed(args, objPacket) == NULL) { |
||||
|
s->udp.state = UDP_FINISHING; |
||||
|
log_android(ANDROID_LOG_WARN, "UDP terminate session socket %d uid %d", |
||||
|
s->socket, s->udp.uid); |
||||
|
} |
||||
|
} else if (s->udp.state == UDP_BLOCKED) { |
||||
|
log_android(ANDROID_LOG_WARN, "UDP remove blocked session uid %d", s->udp.uid); |
||||
|
|
||||
|
if (l == NULL) |
||||
|
args->ctx->ng_session = s->next; |
||||
|
else |
||||
|
l->next = s->next; |
||||
|
|
||||
|
struct ng_session *c = s; |
||||
|
s = s->next; |
||||
|
ng_free(c, __FILE__, __LINE__); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
} else if (s->protocol == IPPROTO_TCP) { |
||||
|
if (s->tcp.state != TCP_CLOSING && s->tcp.state != TCP_CLOSE) { |
||||
|
if (s->tcp.version == 4) { |
||||
|
inet_ntop(AF_INET, &s->tcp.saddr.ip4, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, &s->tcp.daddr.ip4, dest, sizeof(dest)); |
||||
|
} else { |
||||
|
inet_ntop(AF_INET6, &s->tcp.saddr.ip6, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET6, &s->tcp.daddr.ip6, dest, sizeof(dest)); |
||||
|
} |
||||
|
|
||||
|
jobject objPacket = create_packet( |
||||
|
args, s->tcp.version, IPPROTO_TCP, "", |
||||
|
source, ntohs(s->tcp.source), dest, ntohs(s->tcp.dest), "", s->tcp.uid, 0); |
||||
|
if (is_address_allowed(args, objPacket) == NULL) { |
||||
|
write_rst(args, &s->tcp); |
||||
|
log_android(ANDROID_LOG_WARN, "TCP terminate socket %d uid %d", |
||||
|
s->socket, s->tcp.uid); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
l = s; |
||||
|
s = s->next; |
||||
|
} |
||||
|
} |
||||
|
|
1360
NetGuard/app/src/main/jni/netguard/tcp.c
File diff suppressed because it is too large
View File
@ -0,0 +1,549 @@ |
|||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
#include "netguard.h" |
||||
|
|
||||
|
extern FILE *pcap_file; |
||||
|
|
||||
|
int get_udp_timeout(const struct udp_session *u, int sessions, int maxsessions) { |
||||
|
int timeout = (ntohs(u->dest) == 53 ? UDP_TIMEOUT_53 : UDP_TIMEOUT_ANY); |
||||
|
|
||||
|
int scale = 100 - sessions * 100 / maxsessions; |
||||
|
timeout = timeout * scale / 100; |
||||
|
|
||||
|
return timeout; |
||||
|
} |
||||
|
|
||||
|
int check_udp_session(const struct arguments *args, struct ng_session *s, |
||||
|
int sessions, int maxsessions) { |
||||
|
time_t now = time(NULL); |
||||
|
|
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
if (s->udp.version == 4) { |
||||
|
inet_ntop(AF_INET, &s->udp.saddr.ip4, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest)); |
||||
|
} else { |
||||
|
inet_ntop(AF_INET6, &s->udp.saddr.ip6, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest)); |
||||
|
} |
||||
|
|
||||
|
// Check session timeout |
||||
|
int timeout = get_udp_timeout(&s->udp, sessions, maxsessions); |
||||
|
if (s->udp.state == UDP_ACTIVE && s->udp.time + timeout < now) { |
||||
|
log_android(ANDROID_LOG_WARN, "UDP idle %d/%d sec state %d from %s/%u to %s/%u", |
||||
|
now - s->udp.time, timeout, s->udp.state, |
||||
|
source, ntohs(s->udp.source), dest, ntohs(s->udp.dest)); |
||||
|
s->udp.state = UDP_FINISHING; |
||||
|
} |
||||
|
|
||||
|
// Check finished sessions |
||||
|
if (s->udp.state == UDP_FINISHING) { |
||||
|
log_android(ANDROID_LOG_INFO, "UDP close from %s/%u to %s/%u socket %d", |
||||
|
source, ntohs(s->udp.source), dest, ntohs(s->udp.dest), s->socket); |
||||
|
|
||||
|
if (close(s->socket)) |
||||
|
log_android(ANDROID_LOG_ERROR, "UDP close %d error %d: %s", |
||||
|
s->socket, errno, strerror(errno)); |
||||
|
s->socket = -1; |
||||
|
|
||||
|
s->udp.time = time(NULL); |
||||
|
s->udp.state = UDP_CLOSED; |
||||
|
} |
||||
|
|
||||
|
if (s->udp.state == UDP_CLOSED && (s->udp.sent || s->udp.received)) { |
||||
|
account_usage(args, s->udp.version, IPPROTO_UDP, |
||||
|
dest, ntohs(s->udp.dest), s->udp.uid, s->udp.sent, s->udp.received); |
||||
|
s->udp.sent = 0; |
||||
|
s->udp.received = 0; |
||||
|
} |
||||
|
|
||||
|
// Cleanup lingering sessions |
||||
|
if ((s->udp.state == UDP_CLOSED || s->udp.state == UDP_BLOCKED) && |
||||
|
s->udp.time + UDP_KEEP_TIMEOUT < now) |
||||
|
return 1; |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
void check_udp_socket(const struct arguments *args, const struct epoll_event *ev) { |
||||
|
struct ng_session *s = (struct ng_session *) ev->data.ptr; |
||||
|
|
||||
|
// Check socket error |
||||
|
if (ev->events & EPOLLERR) { |
||||
|
s->udp.time = time(NULL); |
||||
|
|
||||
|
int serr = 0; |
||||
|
socklen_t optlen = sizeof(int); |
||||
|
int err = getsockopt(s->socket, SOL_SOCKET, SO_ERROR, &serr, &optlen); |
||||
|
if (err < 0) |
||||
|
log_android(ANDROID_LOG_ERROR, "UDP getsockopt error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
else if (serr) |
||||
|
log_android(ANDROID_LOG_ERROR, "UDP SO_ERROR %d: %s", serr, strerror(serr)); |
||||
|
|
||||
|
s->udp.state = UDP_FINISHING; |
||||
|
} else { |
||||
|
// Check socket read |
||||
|
if (ev->events & EPOLLIN) { |
||||
|
s->udp.time = time(NULL); |
||||
|
|
||||
|
uint8_t *buffer = ng_malloc(s->udp.mss, "udp recv"); |
||||
|
ssize_t bytes = recv(s->socket, buffer, s->udp.mss, 0); |
||||
|
if (bytes < 0) { |
||||
|
// Socket error |
||||
|
log_android(ANDROID_LOG_WARN, "UDP recv error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
|
||||
|
if (errno != EINTR && errno != EAGAIN) |
||||
|
s->udp.state = UDP_FINISHING; |
||||
|
} else if (bytes == 0) { |
||||
|
log_android(ANDROID_LOG_WARN, "UDP recv eof"); |
||||
|
s->udp.state = UDP_FINISHING; |
||||
|
|
||||
|
} else { |
||||
|
// Socket read data |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
if (s->udp.version == 4) |
||||
|
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest)); |
||||
|
else |
||||
|
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest)); |
||||
|
log_android(ANDROID_LOG_INFO, "UDP recv bytes %d from %s/%u for tun", |
||||
|
bytes, dest, ntohs(s->udp.dest)); |
||||
|
|
||||
|
s->udp.received += bytes; |
||||
|
|
||||
|
// Process DNS response |
||||
|
if (ntohs(s->udp.dest) == 53) |
||||
|
parse_dns_response(args, s, buffer, (size_t *) &bytes); |
||||
|
|
||||
|
// Forward to tun |
||||
|
if (write_udp(args, &s->udp, buffer, (size_t) bytes) < 0) |
||||
|
s->udp.state = UDP_FINISHING; |
||||
|
else { |
||||
|
// Prevent too many open files |
||||
|
if (ntohs(s->udp.dest) == 53) |
||||
|
s->udp.state = UDP_FINISHING; |
||||
|
} |
||||
|
} |
||||
|
ng_free(buffer, __FILE__, __LINE__); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int has_udp_session(const struct arguments *args, const uint8_t *pkt, const uint8_t *payload) { |
||||
|
// Get headers |
||||
|
const uint8_t version = (*pkt) >> 4; |
||||
|
const struct iphdr *ip4 = (struct iphdr *) pkt; |
||||
|
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt; |
||||
|
const struct udphdr *udphdr = (struct udphdr *) payload; |
||||
|
|
||||
|
if (ntohs(udphdr->dest) == 53 && !args->fwd53) |
||||
|
return 1; |
||||
|
|
||||
|
// Search session |
||||
|
struct ng_session *cur = args->ctx->ng_session; |
||||
|
while (cur != NULL && |
||||
|
!(cur->protocol == IPPROTO_UDP && |
||||
|
cur->udp.version == version && |
||||
|
cur->udp.source == udphdr->source && cur->udp.dest == udphdr->dest && |
||||
|
(version == 4 ? cur->udp.saddr.ip4 == ip4->saddr && |
||||
|
cur->udp.daddr.ip4 == ip4->daddr |
||||
|
: memcmp(&cur->udp.saddr.ip6, &ip6->ip6_src, 16) == 0 && |
||||
|
memcmp(&cur->udp.daddr.ip6, &ip6->ip6_dst, 16) == 0))) |
||||
|
cur = cur->next; |
||||
|
|
||||
|
return (cur != NULL); |
||||
|
} |
||||
|
|
||||
|
void block_udp(const struct arguments *args, |
||||
|
const uint8_t *pkt, size_t length, |
||||
|
const uint8_t *payload, |
||||
|
int uid) { |
||||
|
// Get headers |
||||
|
const uint8_t version = (*pkt) >> 4; |
||||
|
const struct iphdr *ip4 = (struct iphdr *) pkt; |
||||
|
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt; |
||||
|
const struct udphdr *udphdr = (struct udphdr *) payload; |
||||
|
|
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
if (version == 4) { |
||||
|
inet_ntop(AF_INET, &ip4->saddr, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, &ip4->daddr, dest, sizeof(dest)); |
||||
|
} else { |
||||
|
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest)); |
||||
|
} |
||||
|
|
||||
|
log_android(ANDROID_LOG_INFO, "UDP blocked session from %s/%u to %s/%u", |
||||
|
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest)); |
||||
|
|
||||
|
// Register session |
||||
|
struct ng_session *s = ng_malloc(sizeof(struct ng_session), "udp session block"); |
||||
|
s->protocol = IPPROTO_UDP; |
||||
|
|
||||
|
s->udp.time = time(NULL); |
||||
|
s->udp.uid = uid; |
||||
|
s->udp.version = version; |
||||
|
|
||||
|
if (version == 4) { |
||||
|
s->udp.saddr.ip4 = (__be32) ip4->saddr; |
||||
|
s->udp.daddr.ip4 = (__be32) ip4->daddr; |
||||
|
} else { |
||||
|
memcpy(&s->udp.saddr.ip6, &ip6->ip6_src, 16); |
||||
|
memcpy(&s->udp.daddr.ip6, &ip6->ip6_dst, 16); |
||||
|
} |
||||
|
|
||||
|
s->udp.source = udphdr->source; |
||||
|
s->udp.dest = udphdr->dest; |
||||
|
s->udp.state = UDP_BLOCKED; |
||||
|
s->socket = -1; |
||||
|
|
||||
|
s->next = args->ctx->ng_session; |
||||
|
args->ctx->ng_session = s; |
||||
|
} |
||||
|
|
||||
|
jboolean handle_udp(const struct arguments *args, |
||||
|
const uint8_t *pkt, size_t length, |
||||
|
const uint8_t *payload, |
||||
|
int uid, struct allowed *redirect, |
||||
|
const int epoll_fd) { |
||||
|
// Get headers |
||||
|
const uint8_t version = (*pkt) >> 4; |
||||
|
const struct iphdr *ip4 = (struct iphdr *) pkt; |
||||
|
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt; |
||||
|
const struct udphdr *udphdr = (struct udphdr *) payload; |
||||
|
const uint8_t *data = payload + sizeof(struct udphdr); |
||||
|
const size_t datalen = length - (data - pkt); |
||||
|
|
||||
|
// Search session |
||||
|
struct ng_session *cur = args->ctx->ng_session; |
||||
|
while (cur != NULL && |
||||
|
!(cur->protocol == IPPROTO_UDP && |
||||
|
cur->udp.version == version && |
||||
|
cur->udp.source == udphdr->source && cur->udp.dest == udphdr->dest && |
||||
|
(version == 4 ? cur->udp.saddr.ip4 == ip4->saddr && |
||||
|
cur->udp.daddr.ip4 == ip4->daddr |
||||
|
: memcmp(&cur->udp.saddr.ip6, &ip6->ip6_src, 16) == 0 && |
||||
|
memcmp(&cur->udp.daddr.ip6, &ip6->ip6_dst, 16) == 0))) |
||||
|
cur = cur->next; |
||||
|
|
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
if (version == 4) { |
||||
|
inet_ntop(AF_INET, &ip4->saddr, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET, &ip4->daddr, dest, sizeof(dest)); |
||||
|
} else { |
||||
|
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source)); |
||||
|
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest)); |
||||
|
} |
||||
|
|
||||
|
if (cur != NULL && cur->udp.state != UDP_ACTIVE) { |
||||
|
log_android(ANDROID_LOG_INFO, "UDP ignore session from %s/%u to %s/%u state %d", |
||||
|
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest), cur->udp.state); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
// Create new session if needed |
||||
|
if (cur == NULL) { |
||||
|
log_android(ANDROID_LOG_INFO, "UDP new session from %s/%u to %s/%u", |
||||
|
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest)); |
||||
|
|
||||
|
// Register session |
||||
|
struct ng_session *s = ng_malloc(sizeof(struct ng_session), "udp session"); |
||||
|
s->protocol = IPPROTO_UDP; |
||||
|
|
||||
|
s->udp.time = time(NULL); |
||||
|
s->udp.uid = uid; |
||||
|
s->udp.version = version; |
||||
|
|
||||
|
int rversion; |
||||
|
if (redirect == NULL) |
||||
|
rversion = s->udp.version; |
||||
|
else |
||||
|
rversion = (strstr(redirect->raddr, ":") == NULL ? 4 : 6); |
||||
|
s->udp.mss = (uint16_t) (rversion == 4 ? UDP4_MAXMSG : UDP6_MAXMSG); |
||||
|
|
||||
|
s->udp.sent = 0; |
||||
|
s->udp.received = 0; |
||||
|
|
||||
|
if (version == 4) { |
||||
|
s->udp.saddr.ip4 = (__be32) ip4->saddr; |
||||
|
s->udp.daddr.ip4 = (__be32) ip4->daddr; |
||||
|
} else { |
||||
|
memcpy(&s->udp.saddr.ip6, &ip6->ip6_src, 16); |
||||
|
memcpy(&s->udp.daddr.ip6, &ip6->ip6_dst, 16); |
||||
|
} |
||||
|
|
||||
|
s->udp.source = udphdr->source; |
||||
|
s->udp.dest = udphdr->dest; |
||||
|
s->udp.state = UDP_ACTIVE; |
||||
|
s->next = NULL; |
||||
|
|
||||
|
// Open UDP socket |
||||
|
s->socket = open_udp_socket(args, &s->udp, redirect); |
||||
|
if (s->socket < 0) { |
||||
|
ng_free(s, __FILE__, __LINE__); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
log_android(ANDROID_LOG_DEBUG, "UDP socket %d", s->socket); |
||||
|
|
||||
|
// Monitor events |
||||
|
memset(&s->ev, 0, sizeof(struct epoll_event)); |
||||
|
s->ev.events = EPOLLIN | EPOLLERR; |
||||
|
s->ev.data.ptr = s; |
||||
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s->socket, &s->ev)) |
||||
|
log_android(ANDROID_LOG_ERROR, "epoll add udp error %d: %s", errno, strerror(errno)); |
||||
|
|
||||
|
s->next = args->ctx->ng_session; |
||||
|
args->ctx->ng_session = s; |
||||
|
|
||||
|
cur = s; |
||||
|
} |
||||
|
|
||||
|
// Check for DHCP (tethering) |
||||
|
if (ntohs(udphdr->source) == 68 || ntohs(udphdr->dest) == 67) { |
||||
|
if (check_dhcp(args, &cur->udp, data, datalen) >= 0) |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
log_android(ANDROID_LOG_INFO, "UDP forward from tun %s/%u to %s/%u data %d", |
||||
|
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest), datalen); |
||||
|
|
||||
|
cur->udp.time = time(NULL); |
||||
|
|
||||
|
int rversion; |
||||
|
struct sockaddr_in addr4; |
||||
|
struct sockaddr_in6 addr6; |
||||
|
if (redirect == NULL) { |
||||
|
rversion = cur->udp.version; |
||||
|
if (cur->udp.version == 4) { |
||||
|
addr4.sin_family = AF_INET; |
||||
|
addr4.sin_addr.s_addr = (__be32) cur->udp.daddr.ip4; |
||||
|
addr4.sin_port = cur->udp.dest; |
||||
|
} else { |
||||
|
addr6.sin6_family = AF_INET6; |
||||
|
memcpy(&addr6.sin6_addr, &cur->udp.daddr.ip6, 16); |
||||
|
addr6.sin6_port = cur->udp.dest; |
||||
|
} |
||||
|
} else { |
||||
|
rversion = (strstr(redirect->raddr, ":") == NULL ? 4 : 6); |
||||
|
log_android(ANDROID_LOG_WARN, "UDP%d redirect to %s/%u", |
||||
|
rversion, redirect->raddr, redirect->rport); |
||||
|
|
||||
|
if (rversion == 4) { |
||||
|
addr4.sin_family = AF_INET; |
||||
|
inet_pton(AF_INET, redirect->raddr, &addr4.sin_addr); |
||||
|
addr4.sin_port = htons(redirect->rport); |
||||
|
} else { |
||||
|
addr6.sin6_family = AF_INET6; |
||||
|
inet_pton(AF_INET6, redirect->raddr, &addr6.sin6_addr); |
||||
|
addr6.sin6_port = htons(redirect->rport); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (sendto(cur->socket, data, (socklen_t) datalen, MSG_NOSIGNAL, |
||||
|
(rversion == 4 ? (const struct sockaddr *) &addr4 |
||||
|
: (const struct sockaddr *) &addr6), |
||||
|
(socklen_t) (rversion == 4 ? sizeof(addr4) : sizeof(addr6))) != datalen) { |
||||
|
log_android(ANDROID_LOG_ERROR, "UDP sendto error %d: %s", errno, strerror(errno)); |
||||
|
if (errno != EINTR && errno != EAGAIN) { |
||||
|
cur->udp.state = UDP_FINISHING; |
||||
|
return 0; |
||||
|
} |
||||
|
} else |
||||
|
cur->udp.sent += datalen; |
||||
|
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
int open_udp_socket(const struct arguments *args, |
||||
|
const struct udp_session *cur, const struct allowed *redirect) { |
||||
|
int sock; |
||||
|
int version; |
||||
|
if (redirect == NULL) |
||||
|
version = cur->version; |
||||
|
else |
||||
|
version = (strstr(redirect->raddr, ":") == NULL ? 4 : 6); |
||||
|
|
||||
|
// Get UDP socket |
||||
|
sock = socket(version == 4 ? PF_INET : PF_INET6, SOCK_DGRAM, IPPROTO_UDP); |
||||
|
if (sock < 0) { |
||||
|
log_android(ANDROID_LOG_ERROR, "UDP socket error %d: %s", errno, strerror(errno)); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
// Protect socket |
||||
|
if (protect_socket(args, sock) < 0) |
||||
|
return -1; |
||||
|
|
||||
|
// Check for broadcast/multicast |
||||
|
if (cur->version == 4) { |
||||
|
uint32_t broadcast4 = INADDR_BROADCAST; |
||||
|
if (memcmp(&cur->daddr.ip4, &broadcast4, sizeof(broadcast4)) == 0) { |
||||
|
log_android(ANDROID_LOG_WARN, "UDP4 broadcast"); |
||||
|
int on = 1; |
||||
|
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on))) |
||||
|
log_android(ANDROID_LOG_ERROR, "UDP setsockopt SO_BROADCAST error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
} |
||||
|
} else { |
||||
|
// http://man7.org/linux/man-pages/man7/ipv6.7.html |
||||
|
if (*((uint8_t *) &cur->daddr.ip6) == 0xFF) { |
||||
|
log_android(ANDROID_LOG_WARN, "UDP6 broadcast"); |
||||
|
|
||||
|
int loop = 1; // true |
||||
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop))) |
||||
|
log_android(ANDROID_LOG_ERROR, |
||||
|
"UDP setsockopt IPV6_MULTICAST_LOOP error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
|
||||
|
int ttl = -1; // route default |
||||
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl))) |
||||
|
log_android(ANDROID_LOG_ERROR, |
||||
|
"UDP setsockopt IPV6_MULTICAST_HOPS error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
|
||||
|
struct ipv6_mreq mreq6; |
||||
|
memcpy(&mreq6.ipv6mr_multiaddr, &cur->daddr.ip6, sizeof(struct in6_addr)); |
||||
|
mreq6.ipv6mr_interface = INADDR_ANY; |
||||
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6))) |
||||
|
log_android(ANDROID_LOG_ERROR, |
||||
|
"UDP setsockopt IPV6_ADD_MEMBERSHIP error %d: %s", |
||||
|
errno, strerror(errno)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return sock; |
||||
|
} |
||||
|
|
||||
|
ssize_t write_udp(const struct arguments *args, const struct udp_session *cur, |
||||
|
uint8_t *data, size_t datalen) { |
||||
|
size_t len; |
||||
|
u_int8_t *buffer; |
||||
|
struct udphdr *udp; |
||||
|
uint16_t csum; |
||||
|
char source[INET6_ADDRSTRLEN + 1]; |
||||
|
char dest[INET6_ADDRSTRLEN + 1]; |
||||
|
|
||||
|
// Build packet |
||||
|
if (cur->version == 4) { |
||||
|
len = sizeof(struct iphdr) + sizeof(struct udphdr) + datalen; |
||||
|
buffer = ng_malloc(len, "udp write4"); |
||||
|
struct iphdr *ip4 = (struct iphdr *) buffer; |
||||
|
udp = (struct udphdr *) (buffer + sizeof(struct iphdr)); |
||||
|
if (datalen) |
||||
|
memcpy(buffer + sizeof(struct iphdr) + sizeof(struct udphdr), data, datalen); |
||||
|
|
||||
|
// Build IP4 header |
||||
|
memset(ip4, 0, sizeof(struct iphdr)); |
||||
|
ip4->version = 4; |
||||
|
ip4->ihl = sizeof(struct iphdr) >> 2; |
||||
|
ip4->tot_len = htons(len); |
||||
|
ip4->ttl = IPDEFTTL; |
||||
|
ip4->protocol = IPPROTO_UDP; |
||||
|
ip4->saddr = cur->daddr.ip4; |
||||
|
ip4->daddr = cur->saddr.ip4; |
||||
|
|
||||
|
// Calculate IP4 checksum |
||||
|
ip4->check = ~calc_checksum(0, (uint8_t *) ip4, sizeof(struct iphdr)); |
||||
|
|
||||
|
// Calculate UDP4 checksum |
||||
|
struct ippseudo pseudo; |
||||
|
memset(&pseudo, 0, sizeof(struct ippseudo)); |
||||
|
pseudo.ippseudo_src.s_addr = (__be32) ip4->saddr; |
||||
|
pseudo.ippseudo_dst.s_addr = (__be32) ip4->daddr; |
||||
|
pseudo.ippseudo_p = ip4->protocol; |
||||
|
pseudo.ippseudo_len = htons(sizeof(struct udphdr) + datalen); |
||||
|
|
||||
|
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ippseudo)); |
||||
|
} else { |
||||
|
len = sizeof(struct ip6_hdr) + sizeof(struct udphdr) + datalen; |
||||
|
buffer = ng_malloc(len, "udp write6"); |
||||
|
struct ip6_hdr *ip6 = (struct ip6_hdr *) buffer; |
||||
|
udp = (struct udphdr *) (buffer + sizeof(struct ip6_hdr)); |
||||
|
if (datalen) |
||||
|
memcpy(buffer + sizeof(struct ip6_hdr) + sizeof(struct udphdr), data, datalen); |
||||
|
|
||||
|
// Build IP6 header |
||||
|
memset(ip6, 0, sizeof(struct ip6_hdr)); |
||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = 0; |
||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len - sizeof(struct ip6_hdr)); |
||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_UDP; |
||||
|
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = IPDEFTTL; |
||||
|
ip6->ip6_ctlun.ip6_un2_vfc = IPV6_VERSION; |
||||
|
memcpy(&(ip6->ip6_src), &cur->daddr.ip6, 16); |
||||
|
memcpy(&(ip6->ip6_dst), &cur->saddr.ip6, 16); |
||||
|
|
||||
|
// Calculate UDP6 checksum |
||||
|
struct ip6_hdr_pseudo pseudo; |
||||
|
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo)); |
||||
|
memcpy(&pseudo.ip6ph_src, &ip6->ip6_dst, 16); |
||||
|
memcpy(&pseudo.ip6ph_dst, &ip6->ip6_src, 16); |
||||
|
pseudo.ip6ph_len = ip6->ip6_ctlun.ip6_un1.ip6_un1_plen; |
||||
|
pseudo.ip6ph_nxt = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt; |
||||
|
|
||||
|
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ip6_hdr_pseudo)); |
||||
|
} |
||||
|
|
||||
|
// Build UDP header |
||||
|
memset(udp, 0, sizeof(struct udphdr)); |
||||
|
udp->source = cur->dest; |
||||
|
udp->dest = cur->source; |
||||
|
udp->len = htons(sizeof(struct udphdr) + datalen); |
||||
|
|
||||
|
// Continue checksum |
||||
|
csum = calc_checksum(csum, (uint8_t *) udp, sizeof(struct udphdr)); |
||||
|
csum = calc_checksum(csum, data, datalen); |
||||
|
udp->check = ~csum; |
||||
|
|
||||
|
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6, |
||||
|
(cur->version == 4 ? (const void *) &cur->saddr.ip4 : (const void *) &cur->saddr.ip6), |
||||
|
source, |
||||
|
sizeof(source)); |
||||
|
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6, |
||||
|
(cur->version == 4 ? (const void *) &cur->daddr.ip4 : (const void *) &cur->daddr.ip6), |
||||
|
dest, |
||||
|
sizeof(dest)); |
||||
|
|
||||
|
// Send packet |
||||
|
log_android(ANDROID_LOG_DEBUG, |
||||
|
"UDP sending to tun %d from %s/%u to %s/%u data %u", |
||||
|
args->tun, dest, ntohs(cur->dest), source, ntohs(cur->source), len); |
||||
|
|
||||
|
ssize_t res = write(args->tun, buffer, len); |
||||
|
|
||||
|
// Write PCAP record |
||||
|
if (res >= 0) { |
||||
|
if (pcap_file != NULL) |
||||
|
write_pcap_rec(buffer, (size_t) res); |
||||
|
} else |
||||
|
log_android(ANDROID_LOG_WARN, "UDP write error %d: %s", errno, strerror(errno)); |
||||
|
|
||||
|
ng_free(buffer, __FILE__, __LINE__); |
||||
|
|
||||
|
if (res != len) { |
||||
|
log_android(ANDROID_LOG_ERROR, "write %d/%d", res, len); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
return res; |
||||
|
} |
@ -0,0 +1,182 @@ |
|||||
|
/* |
||||
|
This file is part of NetGuard. |
||||
|
|
||||
|
NetGuard is free software: you can redistribute it and/or modify |
||||
|
it under the terms of the GNU General Public License as published by |
||||
|
the Free Software Foundation, either version 3 of the License, or |
||||
|
(at your option) any later version. |
||||
|
|
||||
|
NetGuard 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 General Public License for more details. |
||||
|
|
||||
|
You should have received a copy of the GNU General Public License |
||||
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
Copyright 2015-2019 by Marcel Bokhorst (M66B) |
||||
|
*/ |
||||
|
|
||||
|
#include "netguard.h" |
||||
|
|
||||
|
extern int loglevel; |
||||
|
|
||||
|
uint16_t calc_checksum(uint16_t start, const uint8_t *buffer, size_t length) { |
||||
|
register uint32_t sum = start; |
||||
|
register uint16_t *buf = (uint16_t *) buffer; |
||||
|
register size_t len = length; |
||||
|
|
||||
|
while (len > 1) { |
||||
|
sum += *buf++; |
||||
|
len -= 2; |
||||
|
} |
||||
|
|
||||
|
if (len > 0) |
||||
|
sum += *((uint8_t *) buf); |
||||
|
|
||||
|
while (sum >> 16) |
||||
|
sum = (sum & 0xFFFF) + (sum >> 16); |
||||
|
|
||||
|
return (uint16_t) sum; |
||||
|
} |
||||
|
|
||||
|
int compare_u32(uint32_t s1, uint32_t s2) { |
||||
|
// https://tools.ietf.org/html/rfc1982 |
||||
|
if (s1 == s2) |
||||
|
return 0; |
||||
|
|
||||
|
uint32_t i1 = s1; |
||||
|
uint32_t i2 = s2; |
||||
|
if ((i1 < i2 && i2 - i1 < 0x7FFFFFFF) || |
||||
|
(i1 > i2 && i1 - i2 > 0x7FFFFFFF)) |
||||
|
return -1; |
||||
|
else |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
int sdk_int(JNIEnv *env) { |
||||
|
jclass clsVersion = jniFindClass(env, "android/os/Build$VERSION"); |
||||
|
jfieldID fid = (*env)->GetStaticFieldID(env, clsVersion, "SDK_INT", "I"); |
||||
|
return (*env)->GetStaticIntField(env, clsVersion, fid); |
||||
|
} |
||||
|
|
||||
|
void log_android(int prio, const char *fmt, ...) { |
||||
|
if (prio >= loglevel) { |
||||
|
char line[1024]; |
||||
|
va_list argptr; |
||||
|
va_start(argptr, fmt); |
||||
|
vsprintf(line, fmt, argptr); |
||||
|
__android_log_print(prio, TAG, "%s", line); |
||||
|
va_end(argptr); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
uint8_t char2nible(const char c) { |
||||
|
if (c >= '0' && c <= '9') return (uint8_t) (c - '0'); |
||||
|
if (c >= 'a' && c <= 'f') return (uint8_t) ((c - 'a') + 10); |
||||
|
if (c >= 'A' && c <= 'F') return (uint8_t) ((c - 'A') + 10); |
||||
|
return 255; |
||||
|
} |
||||
|
|
||||
|
void hex2bytes(const char *hex, uint8_t *buffer) { |
||||
|
size_t len = strlen(hex); |
||||
|
for (int i = 0; i < len; i += 2) |
||||
|
buffer[i / 2] = (char2nible(hex[i]) << 4) | char2nible(hex[i + 1]); |
||||
|
} |
||||
|
|
||||
|
char *trim(char *str) { |
||||
|
while (isspace(*str)) |
||||
|
str++; |
||||
|
if (*str == 0) |
||||
|
return str; |
||||
|
|
||||
|
char *end = str + strlen(str) - 1; |
||||
|
while (end > str && isspace(*end)) |
||||
|
end--; |
||||
|
*(end + 1) = 0; |
||||
|
return str; |
||||
|
} |
||||
|
|
||||
|
const char *strstate(const int state) { |
||||
|
switch (state) { |
||||
|
case TCP_ESTABLISHED: |
||||
|
return "ESTABLISHED"; |
||||
|
case TCP_SYN_SENT: |
||||
|
return "SYN_SENT"; |
||||
|
case TCP_SYN_RECV: |
||||
|
return "SYN_RECV"; |
||||
|
case TCP_FIN_WAIT1: |
||||
|
return "FIN_WAIT1"; |
||||
|
case TCP_FIN_WAIT2: |
||||
|
return "FIN_WAIT2"; |
||||
|
case TCP_TIME_WAIT: |
||||
|
return "TIME_WAIT"; |
||||
|
case TCP_CLOSE: |
||||
|
return "CLOSE"; |
||||
|
case TCP_CLOSE_WAIT: |
||||
|
return "CLOSE_WAIT"; |
||||
|
case TCP_LAST_ACK: |
||||
|
return "LAST_ACK"; |
||||
|
case TCP_LISTEN: |
||||
|
return "LISTEN"; |
||||
|
case TCP_CLOSING: |
||||
|
return "CLOSING"; |
||||
|
default: |
||||
|
return "UNKNOWN"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
char *hex(const u_int8_t *data, const size_t len) { |
||||
|
char hex_str[] = "0123456789ABCDEF"; |
||||
|
|
||||
|
char *hexout; |
||||
|
hexout = (char *) ng_malloc(len * 3 + 1, "hex"); // TODO free |
||||
|
|
||||
|
for (size_t i = 0; i < len; i++) { |
||||
|
hexout[i * 3 + 0] = hex_str[(data[i] >> 4) & 0x0F]; |
||||
|
hexout[i * 3 + 1] = hex_str[(data[i]) & 0x0F]; |
||||
|
hexout[i * 3 + 2] = ' '; |
||||
|
} |
||||
|
hexout[len * 3] = 0; |
||||
|
|
||||
|
return hexout; |
||||
|
} |
||||
|
|
||||
|
int32_t get_local_port(const int sock) { |
||||
|
struct sockaddr_in sin; |
||||
|
socklen_t len = sizeof(sin); |
||||
|
if (getsockname(sock, (struct sockaddr *) &sin, &len) < 0) { |
||||
|
log_android(ANDROID_LOG_ERROR, "getsockname error %d: %s", errno, strerror(errno)); |
||||
|
return -1; |
||||
|
} else |
||||
|
return ntohs(sin.sin_port); |
||||
|
} |
||||
|
|
||||
|
int is_event(int fd, short event) { |
||||
|
struct pollfd p; |
||||
|
p.fd = fd; |
||||
|
p.events = event; |
||||
|
p.revents = 0; |
||||
|
int r = poll(&p, 1, 0); |
||||
|
if (r < 0) { |
||||
|
log_android(ANDROID_LOG_ERROR, "poll readable error %d: %s", errno, strerror(errno)); |
||||
|
return 0; |
||||
|
} else if (r == 0) |
||||
|
return 0; |
||||
|
else |
||||
|
return (p.revents & event); |
||||
|
} |
||||
|
|
||||
|
int is_readable(int fd) { |
||||
|
return is_event(fd, POLLIN); |
||||
|
} |
||||
|
|
||||
|
int is_writable(int fd) { |
||||
|
return is_event(fd, POLLOUT); |
||||
|
} |
||||
|
|
||||
|
long long get_ms() { |
||||
|
struct timespec ts; |
||||
|
clock_gettime(CLOCK_MONOTONIC, &ts); |
||||
|
return ts.tv_sec * 1000LL + ts.tv_nsec / 1e6; |
||||
|
} |
After Width: 36 | Height: 36 | Size: 487 B |
After Width: 36 | Height: 36 | Size: 395 B |
After Width: 36 | Height: 36 | Size: 397 B |
After Width: 36 | Height: 36 | Size: 181 B |
After Width: 36 | Height: 36 | Size: 221 B |
After Width: 36 | Height: 36 | Size: 345 B |
After Width: 36 | Height: 36 | Size: 155 B |
After Width: 36 | Height: 36 | Size: 161 B |
After Width: 36 | Height: 36 | Size: 106 B |
After Width: 36 | Height: 36 | Size: 178 B |
After Width: 36 | Height: 36 | Size: 324 B |
After Width: 36 | Height: 36 | Size: 149 B |
After Width: 36 | Height: 36 | Size: 156 B |
After Width: 36 | Height: 36 | Size: 151 B |
After Width: 36 | Height: 36 | Size: 159 B |
After Width: 36 | Height: 36 | Size: 163 B |
After Width: 36 | Height: 36 | Size: 111 B |
After Width: 36 | Height: 36 | Size: 196 B |
After Width: 36 | Height: 36 | Size: 154 B |
After Width: 36 | Height: 36 | Size: 159 B |
After Width: 36 | Height: 36 | Size: 250 B |
After Width: 36 | Height: 36 | Size: 253 B |
After Width: 36 | Height: 36 | Size: 306 B |
After Width: 36 | Height: 36 | Size: 303 B |
After Width: 36 | Height: 36 | Size: 463 B |
After Width: 36 | Height: 36 | Size: 102 B |
After Width: 36 | Height: 36 | Size: 105 B |
After Width: 36 | Height: 36 | Size: 363 B |
After Width: 36 | Height: 36 | Size: 386 B |
After Width: 36 | Height: 36 | Size: 365 B |
After Width: 36 | Height: 36 | Size: 194 B |
After Width: 36 | Height: 36 | Size: 195 B |
After Width: 36 | Height: 36 | Size: 396 B |