Browse Source

create debug session and attempt to write

master
Beau Kujath 1 year ago
parent
commit
b4151cf76d
  1. 30
      NetGuard/.gitignore
  2. 210
      NetGuard/ADBLOCKING.md
  3. 1502
      NetGuard/FAQ.md
  4. 2
      NetGuard/FUNDING.yml
  5. 1350
      NetGuard/LICENSE
  6. 822
      NetGuard/README.md
  7. 1
      NetGuard/app/.gitignore
  8. 23
      NetGuard/app/CMakeLists.txt
  9. 105
      NetGuard/app/build.gradle
  10. 62
      NetGuard/app/proguard-rules.pro
  11. 287
      NetGuard/app/src/main/AndroidManifest.xml
  12. 144
      NetGuard/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl
  13. BIN
      NetGuard/app/src/main/ic_launcher-web.png
  14. BIN
      NetGuard/app/src/main/ic_launcher_foreground.xcf
  15. BIN
      NetGuard/app/src/main/ic_launcher_round-web.png
  16. 256
      NetGuard/app/src/main/java/eu/faircode/netguard/ActivityDns.java
  17. 130
      NetGuard/app/src/main/java/eu/faircode/netguard/ActivityForwardApproval.java
  18. 251
      NetGuard/app/src/main/java/eu/faircode/netguard/ActivityForwarding.java
  19. 643
      NetGuard/app/src/main/java/eu/faircode/netguard/ActivityLog.java
  20. 1304
      NetGuard/app/src/main/java/eu/faircode/netguard/ActivityMain.java
  21. 447
      NetGuard/app/src/main/java/eu/faircode/netguard/ActivityPro.java
  22. 1466
      NetGuard/app/src/main/java/eu/faircode/netguard/ActivitySettings.java
  23. 186
      NetGuard/app/src/main/java/eu/faircode/netguard/AdapterAccess.java
  24. 95
      NetGuard/app/src/main/java/eu/faircode/netguard/AdapterDns.java
  25. 74
      NetGuard/app/src/main/java/eu/faircode/netguard/AdapterForwarding.java
  26. 370
      NetGuard/app/src/main/java/eu/faircode/netguard/AdapterLog.java
  27. 1033
      NetGuard/app/src/main/java/eu/faircode/netguard/AdapterRule.java
  28. 35
      NetGuard/app/src/main/java/eu/faircode/netguard/Allowed.java
  29. 78
      NetGuard/app/src/main/java/eu/faircode/netguard/ApplicationEx.java
  30. 1164
      NetGuard/app/src/main/java/eu/faircode/netguard/DatabaseHelper.java
  31. 181
      NetGuard/app/src/main/java/eu/faircode/netguard/DownloadTask.java
  32. 45
      NetGuard/app/src/main/java/eu/faircode/netguard/ExpandedListView.java
  33. 33
      NetGuard/app/src/main/java/eu/faircode/netguard/Forward.java
  34. 32
      NetGuard/app/src/main/java/eu/faircode/netguard/FragmentSettings.java
  35. 8
      NetGuard/app/src/main/java/eu/faircode/netguard/GlideHelper.java
  36. 240
      NetGuard/app/src/main/java/eu/faircode/netguard/IAB.java
  37. 140
      NetGuard/app/src/main/java/eu/faircode/netguard/IPUtil.java
  38. 42
      NetGuard/app/src/main/java/eu/faircode/netguard/Packet.java
  39. 132
      NetGuard/app/src/main/java/eu/faircode/netguard/ReceiverAutostart.java
  40. 50
      NetGuard/app/src/main/java/eu/faircode/netguard/ReceiverPackageRemoved.java
  41. 47
      NetGuard/app/src/main/java/eu/faircode/netguard/ResourceRecord.java
  42. 453
      NetGuard/app/src/main/java/eu/faircode/netguard/Rule.java
  43. 146
      NetGuard/app/src/main/java/eu/faircode/netguard/ServiceExternal.java
  44. 3335
      NetGuard/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java
  45. 81
      NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileFilter.java
  46. 80
      NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileGraph.java
  47. 75
      NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileLockdown.java
  48. 103
      NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileMain.java
  49. 39
      NetGuard/app/src/main/java/eu/faircode/netguard/SwitchPreference.java
  50. 46
      NetGuard/app/src/main/java/eu/faircode/netguard/Usage.java
  51. 1075
      NetGuard/app/src/main/java/eu/faircode/netguard/Util.java
  52. 50
      NetGuard/app/src/main/java/eu/faircode/netguard/Version.java
  53. 99
      NetGuard/app/src/main/java/eu/faircode/netguard/WidgetAdmin.java
  54. 70
      NetGuard/app/src/main/java/eu/faircode/netguard/WidgetLockdown.java
  55. 70
      NetGuard/app/src/main/java/eu/faircode/netguard/WidgetMain.java
  56. 189
      NetGuard/app/src/main/jni/netguard/debug_conn.c
  57. 143
      NetGuard/app/src/main/jni/netguard/dhcp.c
  58. 239
      NetGuard/app/src/main/jni/netguard/dns.c
  59. 374
      NetGuard/app/src/main/jni/netguard/icmp.c
  60. 552
      NetGuard/app/src/main/jni/netguard/ip.c
  61. 1113
      NetGuard/app/src/main/jni/netguard/netguard.c
  62. 589
      NetGuard/app/src/main/jni/netguard/netguard.h
  63. 78
      NetGuard/app/src/main/jni/netguard/pcap.c
  64. 370
      NetGuard/app/src/main/jni/netguard/session.c
  65. 1360
      NetGuard/app/src/main/jni/netguard/tcp.c
  66. 549
      NetGuard/app/src/main/jni/netguard/udp.c
  67. 182
      NetGuard/app/src/main/jni/netguard/util.c
  68. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_add_circle_outline_white_24dp.png
  69. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_attach_money_black_24dp.png
  70. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_attach_money_white_24dp.png
  71. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png
  72. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png
  73. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_cloud_upload_white_24dp.png
  74. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_delete_black_24dp.png
  75. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_delete_white_24dp.png
  76. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_equalizer_white_24dp.png
  77. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_equalizer_white_24dp_60.png
  78. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_error_white_24dp.png
  79. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.png
  80. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_expand_less_white_24dp.png
  81. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.png
  82. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_expand_more_white_24dp.png
  83. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_file_download_white_24dp.png
  84. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_filter_list_white_24dp.png
  85. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_filter_list_white_24dp_60.png
  86. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_hourglass_empty_black_24dp.png
  87. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_hourglass_empty_white_24dp.png
  88. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_launch_black_24dp.png
  89. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_launch_white_24dp.png
  90. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_lock_open_white_24dp.png
  91. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_lock_outline_white_24dp.png
  92. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_lock_outline_white_24dp_60.png
  93. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_pause_black_24dp.png
  94. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_pause_white_24dp.png
  95. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_perm_data_setting_black_24dp.png
  96. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_perm_data_setting_white_24dp.png
  97. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_perm_identity_white_24dp.png
  98. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_play_arrow_black_24dp.png
  99. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
  100. BIN
      NetGuard/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png

30
NetGuard/.gitignore

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

210
NetGuard/ADBLOCKING.md

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

2
NetGuard/FUNDING.yml

@ -1 +1 @@
github: [M66B]
github: [M66B]

1350
NetGuard/LICENSE
File diff suppressed because it is too large
View File

822
NetGuard/README.md

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

1
NetGuard/app/.gitignore

@ -0,0 +1 @@
/build

23
NetGuard/app/CMakeLists.txt

@ -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} )

105
NetGuard/app/build.gradle

@ -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'
}

62
NetGuard/app/proguard-rules.pro

@ -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.**

287
NetGuard/app/src/main/AndroidManifest.xml

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

144
NetGuard/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl

@ -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);
}

BIN
NetGuard/app/src/main/ic_launcher-web.png

After

Width: 512  |  Height: 512  |  Size: 24 KiB

BIN
NetGuard/app/src/main/ic_launcher_foreground.xcf

BIN
NetGuard/app/src/main/ic_launcher_round-web.png

After

Width: 512  |  Height: 512  |  Size: 42 KiB

256
NetGuard/app/src/main/java/eu/faircode/netguard/ActivityDns.java

@ -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();
}
}

130
NetGuard/app/src/main/java/eu/faircode/netguard/ActivityForwardApproval.java

@ -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();
}
});
}
}

251
NetGuard/app/src/main/java/eu/faircode/netguard/ActivityForwarding.java

@ -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);
}
}
}

643
NetGuard/app/src/main/java/eu/faircode/netguard/ActivityLog.java

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

447
NetGuard/app/src/main/java/eu/faircode/netguard/ActivityPro.java

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

186
NetGuard/app/src/main/java/eu/faircode/netguard/AdapterAccess.java

@ -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)));
}
}

95
NetGuard/app/src/main/java/eu/faircode/netguard/AdapterDns.java

@ -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));
}
}

74
NetGuard/app/src/main/java/eu/faircode/netguard/AdapterForwarding.java

@ -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)));
}
}

370
NetGuard/app/src/main/java/eu/faircode/netguard/AdapterLog.java

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

35
NetGuard/app/src/main/java/eu/faircode/netguard/Allowed.java

@ -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;
}
}

78
NetGuard/app/src/main/java/eu/faircode/netguard/ApplicationEx.java

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

181
NetGuard/app/src/main/java/eu/faircode/netguard/DownloadTask.java

@ -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());
}
}

45
NetGuard/app/src/main/java/eu/faircode/netguard/ExpandedListView.java

@ -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));
}
}

33
NetGuard/app/src/main/java/eu/faircode/netguard/Forward.java

@ -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;
}
}

32
NetGuard/app/src/main/java/eu/faircode/netguard/FragmentSettings.java

@ -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);
}
}

8
NetGuard/app/src/main/java/eu/faircode/netguard/GlideHelper.java

@ -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 {
}

240
NetGuard/app/src/main/java/eu/faircode/netguard/IAB.java

@ -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);
}
}
}

140
NetGuard/app/src/main/java/eu/faircode/netguard/IPUtil.java

@ -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);
}
}
}

42
NetGuard/app/src/main/java/eu/faircode/netguard/Packet.java

@ -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;
}
}

132
NetGuard/app/src/main/java/eu/faircode/netguard/ReceiverAutostart.java

@ -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();
}
}
}

50
NetGuard/app/src/main/java/eu/faircode/netguard/ReceiverPackageRemoved.java

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

47
NetGuard/app/src/main/java/eu/faircode/netguard/ResourceRecord.java

@ -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());
}
}

453
NetGuard/app/src/main/java/eu/faircode/netguard/Rule.java

@ -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;
}
}

146
NetGuard/app/src/main/java/eu/faircode/netguard/ServiceExternal.java

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

81
NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileFilter.java

@ -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();
}
}

80
NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileGraph.java

@ -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);
}
}

75
NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileLockdown.java

@ -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);
}
}

103
NetGuard/app/src/main/java/eu/faircode/netguard/ServiceTileMain.java

@ -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);
}
}
}
}

39
NetGuard/app/src/main/java/eu/faircode/netguard/SwitchPreference.java

@ -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);
}
}

46
NetGuard/app/src/main/java/eu/faircode/netguard/Usage.java

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

50
NetGuard/app/src/main/java/eu/faircode/netguard/Version.java

@ -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;
}
}

99
NetGuard/app/src/main/java/eu/faircode/netguard/WidgetAdmin.java

@ -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));
}
}
}

70
NetGuard/app/src/main/java/eu/faircode/netguard/WidgetLockdown.java

@ -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);
}
}

70
NetGuard/app/src/main/java/eu/faircode/netguard/WidgetMain.java

@ -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);
}
}

189
NetGuard/app/src/main/jni/netguard/debug_conn.c

@ -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++;
}
}

143
NetGuard/app/src/main/jni/netguard/dhcp.c

@ -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;
}

239
NetGuard/app/src/main/jni/netguard/dns.c

@ -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);
}

374
NetGuard/app/src/main/jni/netguard/icmp.c

@ -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;
}

552
NetGuard/app/src/main/jni/netguard/ip.c

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

589
NetGuard/app/src/main/jni/netguard/netguard.h

@ -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();

78
NetGuard/app/src/main/jni/netguard/pcap.c

@ -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));
}
}
}
}

370
NetGuard/app/src/main/jni/netguard/session.c

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

549
NetGuard/app/src/main/jni/netguard/udp.c

@ -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;
}

182
NetGuard/app/src/main/jni/netguard/util.c

@ -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;
}

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_add_circle_outline_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 487 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_attach_money_black_24dp.png

After

Width: 36  |  Height: 36  |  Size: 395 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_attach_money_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 397 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 181 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 221 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_cloud_upload_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 345 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_delete_black_24dp.png

After

Width: 36  |  Height: 36  |  Size: 155 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_delete_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 161 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_equalizer_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 106 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_equalizer_white_24dp_60.png

After

Width: 36  |  Height: 36  |  Size: 178 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_error_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 324 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.png

After

Width: 36  |  Height: 36  |  Size: 149 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_expand_less_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 156 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.png

After

Width: 36  |  Height: 36  |  Size: 151 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_expand_more_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 159 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_file_download_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 163 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_filter_list_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 111 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_filter_list_white_24dp_60.png

After

Width: 36  |  Height: 36  |  Size: 196 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_hourglass_empty_black_24dp.png

After

Width: 36  |  Height: 36  |  Size: 154 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_hourglass_empty_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 159 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_launch_black_24dp.png

After

Width: 36  |  Height: 36  |  Size: 250 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_launch_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 253 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_lock_open_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 306 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_lock_outline_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 303 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_lock_outline_white_24dp_60.png

After

Width: 36  |  Height: 36  |  Size: 463 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_pause_black_24dp.png

After

Width: 36  |  Height: 36  |  Size: 102 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_pause_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 105 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_perm_data_setting_black_24dp.png

After

Width: 36  |  Height: 36  |  Size: 363 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_perm_data_setting_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 386 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_perm_identity_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 365 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_play_arrow_black_24dp.png

After

Width: 36  |  Height: 36  |  Size: 194 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 195 B

BIN
NetGuard/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png

After

Width: 36  |  Height: 36  |  Size: 396 B

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save