-
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 |