Merge branch 'development' into tweak/gravity

This commit is contained in:
DL6ER 2017-09-21 17:05:30 +02:00
commit ee2169dd13
No known key found for this signature in database
GPG key ID: 00135ACBD90B28DD
7 changed files with 683 additions and 338 deletions

View file

@ -1,4 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1090
# Pi-hole: A black hole for Internet advertisements # Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net) # (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
@ -30,6 +32,7 @@ Options:
-f, fahrenheit Set Fahrenheit as preferred temperature unit -f, fahrenheit Set Fahrenheit as preferred temperature unit
-k, kelvin Set Kelvin as preferred temperature unit -k, kelvin Set Kelvin as preferred temperature unit
-r, hostrecord Add a name to the DNS associated to an IPv4/IPv6 address -r, hostrecord Add a name to the DNS associated to an IPv4/IPv6 address
-e, email Set an administrative contact address for the Block Page
-h, --help Show this help dialog -h, --help Show this help dialog
-i, interface Specify dnsmasq's interface listening behavior -i, interface Specify dnsmasq's interface listening behavior
Add '-h' for more info on interface usage" Add '-h' for more info on interface usage"
@ -414,6 +417,27 @@ Options:
RestartDNS RestartDNS
} }
SetAdminEmail() {
if [[ "${1}" == *"-h"* ]]; then
echo "Usage: pihole -a email <address>
Example: 'pihole -a email admin@address.com'
Set an administrative contact address for the Block Page
Options:
\"\" Empty: Remove admin contact
-h, --help Show this help dialog"
exit 0
fi
if [[ -n "${args[2]}" ]]; then
change_setting "ADMIN_EMAIL" "${args[2]}"
echo -e " ${TICK} Setting admin contact to ${args[2]}"
else
change_setting "ADMIN_EMAIL" ""
echo -e " ${TICK} Removing admin contact"
fi
}
SetListeningMode() { SetListeningMode() {
source "${setupVars}" source "${setupVars}"
@ -484,6 +508,7 @@ main() {
"addstaticdhcp" ) AddDHCPStaticAddress;; "addstaticdhcp" ) AddDHCPStaticAddress;;
"removestaticdhcp" ) RemoveDHCPStaticAddress;; "removestaticdhcp" ) RemoveDHCPStaticAddress;;
"-r" | "hostrecord" ) SetHostRecord "$3";; "-r" | "hostrecord" ) SetHostRecord "$3";;
"-e" | "email" ) SetAdminEmail "$3";;
"-i" | "interface" ) SetListeningMode "$@";; "-i" | "interface" ) SetListeningMode "$@";;
"-t" | "teleporter" ) Teleporter;; "-t" | "teleporter" ) Teleporter;;
"adlist" ) CustomizeAdLists;; "adlist" ) CustomizeAdLists;;

View file

@ -1,136 +1,384 @@
/* CSS Reset */ /* Pi-hole: A black hole for Internet advertisements
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } * (c) 2017 Pi-hole, LLC (https://pi-hole.net)
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } * Network-wide ad blocking via your own hardware.
body { line-height: 1; } *
ol, ul { list-style: none; } * This file is copyright under the latest version of the EUPL.
blockquote, q { quotes: none; } * Please see LICENSE file for your rights under this license. */
blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; }
table { border-collapse: collapse; border-spacing: 0; }
html { height: 100%; overflow-x: hidden; }
/* General Style */ /* Text Customisation Options ======> */
a { color: rgba(0,60,120,0.95); text-decoration: none; } /* 1E3C5A */ .title:before { content: "Website Blocked"; }
a:hover { color: rgba(210,120,0,0.95); transition-duration: .2s; } /* 255, 128, 0 */ .altBtn:before { content: "Why am I here?"; }
divs a { border-bottom: 1px dashed rgba(30,60,90,0.3); } .linkPH:before { content: "About Pi-hole"; }
b { font-weight: bold; } .linkEmail:before { content: "Contact Admin"; }
i { font-style: italic; }
footer, pre, td { font-family: monospace; padding-left: 15px; } #bpOutput.add:before { content: "Info"; }
/*body, header { background: #E1E1E1; }*/ #bpOutput.add:after { content: "The domain is being whitelisted..."; }
#bpOutput.error:before, .unhandled:before { content: "Error"; }
#bpOutput.unhandled:after { content: "An unhandled exception occured. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; }
#bpOutput.success:before { content: "Success"; }
#bpOutput.success:after { content: "Website has been whitelisted! You may need to flush your DNS cache"; }
.recentwl:before { content: "This site has been whitelisted. Please flush your DNS cache and/or restart your browser."; }
.unknown:before { content: "This website is not found in any of Pi-hole's blacklists. The reason you have arrived here is unknown."; }
.cname:before { content: "This site is an alias for "; } /* <a href="http://cname.com">cname.com</a> */
.cname:after { content: ", which may be blocked by Pi-hole."; }
.blacklist:before { content: "Manually Blacklisted"; }
.wildcard:before { content: "Manually Blacklisted by Wildcard"; }
.noblock:before { content: "Not found on any Blacklist"; }
#bpBlock:before { content: "Access to the following website has been denied:"; }
#bpFlag:before { content: "This is primarily due to being flagged as:"; }
#bpHelpTxt:before { content: "If you have an ongoing use for this website, please "; }
#bpHelpTxt a:before, #bpHelpTxt span:before { content: "ask the administrator"; }
#bpHelpTxt:after{ content: " of the Pi-hole on this network to have it whitelisted"; }
#bpBack:before { content: "Back to safety"; }
#bpInfo:before { content: "Technical Info"; }
#bpFoundIn:before { content: "This site is found in "; }
#bpFoundIn span:after { content: " of "; }
#bpFoundIn:after { content: " lists:"; }
#bpWhitelist:before { content: "Whitelist"; }
footer span:before { content: "Page generated on "; }
/* Hide whitelisting form entirely */
/* #bpWLButtons { display: none; } */
/* Text Customisation Options <=============================== */
/* http://necolas.github.io/normalize.css ======> */
html { font-family: sans-serif; line-height: 1.15; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; }
body { margin: 0; }
article, aside, footer, header, nav, section { display: block; }
h1 { font-size: 2em; margin: 0.67em 0; }
figcaption, figure, main { display: block; }
figure { margin: 1em 40px; }
hr { box-sizing: content-box; height: 0; overflow: visible; }
pre { font-family: monospace, monospace; font-size: 1em; }
a { background-color: transparent; -webkit-text-decoration-skip: objects; }
a:active, a:hover { outline-width: 0; }
abbr[title] { border-bottom: none; text-decoration: underline; text-decoration: underline dotted; }
b, strong { font-weight: inherit; }
b, strong { font-weight: bolder; }
code, kbd, samp { font-family: monospace, monospace; font-size: 1em; }
dfn { font-style: italic; }
mark { background-color: #ff0; color: #000; }
small { font-size: 80%; }
sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
sub { bottom: -0.25em; }
sup { top: -0.5em; }
audio, video { display: inline-block; }
audio:not([controls]) { display: none; height: 0; }
img { border-style: none; }
svg:not(:root) { overflow: hidden; }
button, input, optgroup, select, textarea { font-family: sans-serif; font-size: 100%; line-height: 1.15; margin: 0; }
button, input { overflow: visible; }
button, select { text-transform: none; }
button, html [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; }
button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { border-style: none; padding: 0; }
button:-moz-focusring, [type="button"]:-moz-focusring, [type="reset"]:-moz-focusring, [type="submit"]:-moz-focusring { outline: 1px dotted ButtonText; }
fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }
legend { box-sizing: border-box; color: inherit; display: table; max-width: 100%; padding: 0; white-space: normal; }
progress { display: inline-block; vertical-align: baseline; }
textarea { overflow: auto; }
[type="checkbox"], [type="radio"] { box-sizing: border-box; padding: 0; }
[type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { height: auto; }
[type="search"] { -webkit-appearance: textfield; outline-offset: -2px; }
[type="search"]::-webkit-search-cancel-button, [type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
::-webkit-file-upload-button { -webkit-appearance: button; font: inherit; }
details, menu { display: block; }
summary { display: list-item; }
canvas { display: inline-block; }
template { display: none; }
[hidden] { display: none; }
/* Normalize.css <=============================== */
html { font-size: 62.5%; }
a { color: #3c8dbc; text-decoration: none; }
a:hover { color: #72afda; text-decoration: underline; }
b { color: rgb(68,68,68); }
p { margin: 0; }
label, .buttons a {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
label, .buttons *:not([disabled]) { cursor: pointer; }
/* Touch device dark tap highlight */
header h1 a, label, .buttons * { -webkit-tap-highlight-color: transparent; }
/* Webkit Focus Glow */
textarea, input, button { outline: none; }
@font-face {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
src: local("Source Sans Pro"), local("SourceSansPro-Regular"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Regular.ttf") format("truetype");
}
@font-face {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 700;
src: local("Source Sans Pro Bold"), local("SourceSansPro-Bold"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Bold.ttf") format("truetype");
}
body { body {
background-image: -webkit-linear-gradient(top, rgba(240,240,240,0.95), rgba(190,190,190,0.95)); background: #dbdbdb url("/admin/img/boxed-bg.jpg") repeat fixed;
background-image: linear-gradient(to bottom, rgba(240,240,240,0.95), rgba(190,190,190,0.95)); color: #333;
background-attachment: fixed; font: 1.4rem "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: rgba(64,64,64,0.95); line-height: 2.2rem;
font: 14px, sans-serif; }
line-height: 1em;
/* User is greeted with a splash page when browsing to Pi-hole IP address */
#splashpage { background: #222; color: rgba(255,255,255,0.7); text-align: center; }
#splashpage img { margin: 5px; width: 256px; }
#splashpage b { color: inherit; }
#bpWrapper {
margin: 0 auto;
max-width: 1250px;
box-shadow: 0 0 8px rgba(0,0,0,0.5);
} }
header { header {
min-width: 320px; background: #3c8dbc;
width: 100%; display: table;
text-shadow: 0 1px rgba(255,255,255,0.6); position: relative;
display: table; width: 100%;
table-layout: fixed;
border: 1px solid rgba(0,0,0,0.25);
border-top-color: rgba(255,255,255,0.85);
border-style: solid none;
background-image: -webkit-linear-gradient(top, rgba(240,240,240,0.95), rgba(220,220,220,0.95));
background-image: linear-gradient(to bottom, rgba(240,240,240,0.95), rgba(220,220,220,0.95));
box-shadow: 0 0 1px 1px rgba(0,0,0,0.04);
} }
header h1, header div { header h1, header h1 a, header .spc, header #bpAlt label {
display: table-cell; display: table-cell;
color: inherit; color: #fff;
font-weight: bold; white-space: nowrap;
vertical-align: middle; vertical-align: middle;
white-space: nowrap; height: 50px; /* Must match #bpAbout top value */
overflow: hidden;
box-sizing: border-box;
} }
header h1 { h1 a {
font-size: 22px; background-color: rgba(0,0,0,0.1);
font-weight: bold; font-family: "Helvetica Neue", Helvetica, Arial ,sans-serif;
width: 100%; font-size: 2rem;
padding: 8px 0; font-weight: normal;
text-indent: 32px; min-width: 230px;
background: url("http://pi.hole/admin/img/logo.svg") left no-repeat; text-align: center;
background-size: 30px 22px;
} }
header h1 a, h1 a:hover { color: inherit; } h1 a:hover, header #bpAlt:hover { background-color: rgba(0,0,0,0.12); color: inherit; text-decoration: none; }
header .alt { width: 85px; font-size: 0.8em; padding-right: 4px; text-align: right; line-height: 1.25em; }
.active { color: green; } header .spc { width: 100%; }
.inactive { color: red; }
header #bpAlt label {
background: url("/admin/img/logo.svg") no-repeat center left 15px;
background-size: 15px 23px;
padding: 0 15px;
text-indent: 30px;
}
[type=checkbox][id$="Toggle"] { display: none; }
[type=checkbox][id$="Toggle"]:checked ~ #bpAbout,
[type=checkbox][id$="Toggle"]:checked ~ #bpMoreInfo {
display: block; }
/* Click anywhere else on screen to hide #bpAbout */
#bpAboutToggle:checked {
display: block;
height: 300px; /* VH Fallback */
height: 100vh;
left: 0;
top: 0;
opacity: 0;
position: absolute;
width: 100%;
}
#bpAbout {
background: #3c8dbc;
border-bottom-left-radius: 5px;
border: 1px solid #FFF;
border-right-width: 0;
box-shadow: -1px 1px 1px rgba(0,0,0,0.12);
box-sizing: border-box;
display: none;
font-size: 1.7rem;
top: 50px;
position: absolute;
right: 0;
width: 280px;
z-index: 1;
}
.aboutPH {
box-sizing: border-box;
color: rgba(255,255,255,0.8);
display: block;
padding: 10px;
width: 100%;
text-align: center;
}
.aboutImg {
background: url("/admin/img/logo.svg") no-repeat center;
background-size: 90px 90px;
border: 3px solid rgba(255,255,255,0.2);
height: 90px;
margin: 0 auto;
padding: 2px;
width: 90px;
}
.aboutPH p { margin: 10px 0; }
.aboutPH small { display: block; font-size: 1.2rem; }
.aboutLink {
background: #fff;
border-top: 1px solid #ddd;
display: table;
font-size: 1.4rem;
text-align: center;
width: 100%;
}
.aboutLink a {
display: table-cell;
padding: 14px;
min-width: 50%;
}
main { main {
display: block; background: #ecf0f5;
width: 80%; font-size: 1.65rem;
padding: 10px; padding: 10px;
font-size: 1em;
background-color: rgba(255,255,255,0.85);
margin: 8px auto;
box-sizing: border-box;
border: 1px solid rgba(0,0,0,0.25);
box-shadow: 4px 4px rgba(0,0,0,0.1);
line-height: 1.2em;
border-radius: 8px;
} }
h2 { /* Rgba is shared with .transparent th */ #bpOutput {
font: 1.15em sans-serif; background: #00c0ef;
background-color: rgba(255,0,0,0.4); border-radius: 3px;
text-shadow: none; border: 1px solid rgba(0,0,0,0.1);
line-height: 1.1em; color: #fff;
padding-bottom: 1px; font-size: 1.4rem;
margin-top: 8px; margin-bottom: 10px;
margin-bottom: 4px; margin-top: 5px;
background: -webkit-linear-gradient(left, rgba(0,0,0,0.25), transparent 80%) no-repeat; padding: 15px;
background: linear-gradient(to right, rgba(0,0,0,0.25), transparent 80%) no-repeat;
background-size: 100% 1px;
background-position: 0 17px;
} }
h2:first-child { margin-top: 0; } #bpOutput:before {
h2 ~ *:not(h2) { margin-left: 4px; } background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='14' viewBox='0 0 7 14'%3E%3Cpath fill='%23fff' d='M6,11a1.371,1.371,0,0,1,1,1v1a1.371,1.371,0,0,1-1,1H1a1.371,1.371,0,0,1-1-1V12a1.371,1.371,0,0,1,1-1H2V8H1A1.371,1.371,0,0,1,0,7V6A1.371,1.371,0,0,1,1,5H4A1.371,1.371,0,0,1,5,6v5H6ZM3.5,0A1.5,1.5,0,1,1,2,1.5,1.5,1.5,0,0,1,3.5,0Z'/%3E%3C/svg%3E") no-repeat center left;
li { padding: 2px 0; } display: block;
li::before { content: "\00BB\00a0"; } font-size: 1.8rem;
li a { position: relative; top: 1px; } /* Center bullet-point arrows */ text-indent: 15px;
/* Button Style */
.buttons a, button, input, .transparent th a { /* Swapped rgba is shared with input[type='url'] */
display: inline-block;
color: rgba(32,32,32,0.9);
font-weight: bold;
text-align: center;
cursor: pointer;
text-shadow: 0 1px rgba(255,255,255,0.2);
line-height: 0.86em;
font-size: 1em;
padding: 4px 8px;
background: #FAFAFA;
background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.05), rgba(0,0,0,0.05));
background-image: linear-gradient(to bottom, rgba(255,255,255,0.05), rgba(0,0,0,0.05));
border: 1px solid rgba(0,0,0,0.25);
border-radius: 4px;
box-shadow: 0 1px 0 rgba(0,0,0,0.04);
} }
.buttons { white-space: nowrap; width: 100%; display: table; } #bpOutput.hidden { display: none; }
.buttons33 { white-space: nowrap; width: 33.333%; display: table; text-align: center; margin-left: 33.333% } #bpOutput.success { background: #00a65a; }
.mini a { width: 50%; } #bpOutput.error { background: #dd4b39; }
a.safe { background-color: rgba(0,220,0,0.5); }
button.safe { background-color: rgba(0,220,0,0.5); }
a.warn { background-color: rgba(220,0,0,0.5); }
.blocked a, .mini a { display: table-cell; } .blockMsg, .flagMsg {
.blocked a.safe50 { width: 50%; background-color: rgba(0,220,0,0.5); } font: bold 1.8rem Consolas, Courier, monospace;
.blocked a.safe33 { width: 33.333%; background-color: rgba(0,220,0,0.5); } padding: 5px 10px 10px 10px;
text-indent: 15px;
}
/* Types of text */ #bpHelpTxt { padding-bottom: 10px; }
.msg { white-space: pre; overflow: auto; -webkit-overflow-scrolling: touch; display: block; line-height: 1.2em; font-weight: bold; font-size: 1.15em; margin: 4px 8px 8px 8px; white-space: pre-line; }
footer { font-size: 0.8em; text-align: center; width: 87%; margin: 4px auto; } .buttons {
border-spacing: 5px 0;
display: table;
width: 100%;
}
.buttons * {
-moz-appearance: none;
-webkit-appearance: none;
border-radius: 3px;
border: 1px solid rgba(0,0,0,0.1);
box-sizing: content-box;
display: table-cell;
font-size: 1.65rem;
margin-right: 5px;
min-height: 20px;
padding: 6px 12px;
position: relative;
text-align: center;
vertical-align: top;
white-space: nowrap;
width: auto;
}
.buttons a:hover { text-decoration: none; }
/* Button hover dark overlay */
.buttons *:not(input):not([disabled]):hover {
background-image: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0.1));
color: #FFF;
}
/* Button active shadow inset */
.buttons *:not([disabled]):not(input):active {
box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);
}
/* Input border colour */
.buttons *:not([disabled]):hover, .buttons input:focus {
border-color: rgba(0,0,0,0.25);
}
#bpButtons * { width: 50%; color: #FFF; }
#bpBack { background-color: #00a65a; }
#bpInfo { background-color: #3c8dbc; }
#bpWhitelist { background-color: #dd4b39; }
#blockpage .buttons [type=password][disabled] { color: rgba(0,0,0,1); }
#blockpage .buttons [disabled] { color: rgba(0,0,0,0.55); background-color: #e3e3e3; }
#blockpage .buttons [type=password]:-ms-input-placeholder { color: rgba(51,51,51,0.8); }
input[type=password] { font-size: 1.5rem; }
@keyframes slidein { from { max-height: 0; opacity: 0; } to { max-height: 300px; opacity: 1; } }
#bpMoreToggle:checked ~ #bpMoreInfo { display: block; margin-top: 8px; animation: slidein 0.05s linear; }
#bpMoreInfo { display: none; margin-top: 10px; }
#bpQueryOutput {
font-size: 1.2rem;
line-height: 1.65rem;
margin: 5px 0 0 0;
overflow: auto;
padding: 0 5px;
-webkit-overflow-scrolling: touch;
}
#bpQueryOutput span { margin-right: 4px; }
#bpWLButtons { width: auto; margin-top: 10px; }
#bpWLButtons * { display: inline-block; }
#bpWLDomain { display: none; }
#bpWLPassword { width: 160px; }
#bpWhitelist { color: #fff; }
footer {
background: #fff;
border-top: 1px solid #d2d6de;
color: #444;
font: 1.2rem Consolas, Courier, monospace;
padding: 8px;
}
/* Responsive Content */
@media only screen and (max-width: 500px) {
h1 a { font-size: 1.8rem; min-width: 170px; }
footer span:before { content: "Generated "; }
footer span { display: block; }
}
@media only screen and (min-width: 1251px) {
#bpWrapper, footer { border-radius: 0 0 5px 5px; }
#bpAbout { border-right-width: 1px; }
}

View file

@ -1 +0,0 @@
var x = "Pi-hole: A black hole for Internet advertisements."

View file

@ -1,225 +1,319 @@
<?php <?php
/* Detailed Pi-hole Block Page: Show "Website Blocked" if user browses to site, but not to image/file requests based on the work of WaLLy3K for DietPi & Pi-Hole */ /* Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
function validIP($address){ // Sanitise HTTP_HOST output
if (preg_match('/[.:0]/', $address) && !preg_match('/[1-9a-f]/', $address)) { $serverName = htmlspecialchars($_SERVER["HTTP_HOST"]);
// Test if address contains either `:` or `0` but not 1-9 or a-f
return false; // Get values from setupVars.conf
} if (is_file("/etc/pihole/setupVars.conf")) {
return !filter_var($address, FILTER_VALIDATE_IP) === false; $setupVars = parse_ini_file("/etc/pihole/setupVars.conf");
$svFQDN = $setupVars["FQDN"];
$svPasswd = !empty($setupVars["WEBPASSWORD"]);
$svEmail = (!empty($setupVars["ADMIN_EMAIL"]) && filter_var($setupVars["ADMIN_EMAIL"], FILTER_VALIDATE_EMAIL)) ? $setupVars["ADMIN_EMAIL"] : "";
unset($setupVars);
} else {
die("[ERROR] File not found: <code>/etc/pihole/setupVars.conf</code>");
} }
$uri = escapeshellcmd($_SERVER['REQUEST_URI']); // Set landing page location, found within /var/www/html/
$serverName = escapeshellcmd($_SERVER['SERVER_NAME']); $landPage = "../landing.php";
// If the server name is 'pi.hole', it's likely a user trying to get to the admin panel. // Set empty array for hostnames to be accepted as self address for splash page
// Let's be nice and redirect them. $authorizedHosts = [];
if ($serverName === 'pi.hole')
{ // Append FQDN to $authorizedHosts
header('HTTP/1.1 301 Moved Permanently'); if (!empty($svFQDN)) array_push($authorizedHosts, $svFQDN);
header("Location: /admin/");
// Append virtual hostname to $authorizedHosts
if (!empty($_SERVER["VIRTUAL_HOST"])) {
array_push($authorizedHosts, $_SERVER["VIRTUAL_HOST"]);
} }
// Retrieve server URI extension (EG: jpg, exe, php) // Set which extension types render as Block Page (Including "" for index.wxyz)
// strtok($uri, '\?') splits the querystring from the path (if there is a querystring) $validExtTypes = array("asp", "htm", "html", "php", "rss", "xml", "");
ini_set('pcre.recursion_limit',100);
$uriExt = pathinfo(strtok($uri,'\?'), PATHINFO_EXTENSION);
// Define which URL extensions get rendered as "Website Blocked" // Get extension of current URL
$webExt = array('asp', 'htm', 'html', 'php', 'rss', 'xml'); $currentUrlExt = pathinfo($_SERVER["REQUEST_URI"], PATHINFO_EXTENSION);
// Get IPv4 and IPv6 addresses from setupVars.conf (if available) // Set mobile friendly viewport
$setupVars = parse_ini_file("/etc/pihole/setupVars.conf"); $viewPort = '<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>';
$ipv4 = isset($setupVars["IPV4_ADDRESS"]) ? explode("/", $setupVars["IPV4_ADDRESS"])[0] : $_SERVER['SERVER_ADDR'];
$ipv6 = isset($setupVars["IPV6_ADDRESS"]) ? explode("/", $setupVars["IPV6_ADDRESS"])[0] : $_SERVER['SERVER_ADDR'];
$AUTHORIZED_HOSTNAMES = array( // Set response header
$ipv4, function setHeader($type = "x") {
$ipv6, header("X-Pi-hole: A black hole for Internet advertisements.");
str_replace(array("[","]"), array("",""), $_SERVER["SERVER_ADDR"]), if (isset($type) && $type === "js") header("Content-Type: application/javascript");
"pi.hole",
"localhost");
// Allow user set virtual hostnames
$virtual_host = getenv('VIRTUAL_HOST');
if (!empty($virtual_host))
array_push($AUTHORIZED_HOSTNAMES, $virtual_host);
// Immediately quit since we didn't block this page (the IP address or pi.hole is explicitly requested)
if(validIP($serverName) || in_array($serverName,$AUTHORIZED_HOSTNAMES))
{
http_response_code(404);
die();
} }
if(in_array($uriExt, $webExt) || empty($uriExt)) // Determine block page redirect type
{ if ($serverName === "pi.hole") {
// Requested resource has an extension listed in $webExt exit(header("Location: /admin"));
// or no extension (index access to some folder incl. the root dir) } elseif (filter_var($serverName, FILTER_VALIDATE_IP) || in_array($serverName, $authorizedHosts)) {
$showPage = true; // Set Splash Page output
} $splashPage = "
else <html><head>
{ $viewPort
// Something else <link rel='stylesheet' href='/pihole/blockingpage.css' type='text/css'/>
$showPage = false; </head><body id='splashpage'><img src='/admin/img/logo.svg'/><br/>Pi-<b>hole</b>: Your black hole for Internet advertisements</body></html>
";
// Render splash page or landing page when directly browsing via IP or auth'd hostname
$renderPage = is_file(getcwd()."/$landPage") ? include $landPage : "$splashPage";
unset($serverName, $svFQDN, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort);
exit($renderPage);
} elseif ($currentUrlExt === "js") {
// Serve dummy Javascript for blocked domains
exit(setHeader("js").'var x = "Pi-hole: A black hole for Internet advertisements."');
} elseif (strpos($_SERVER["REQUEST_URI"], "?") !== FALSE && isset($_SERVER["HTTP_REFERER"])) {
// Serve blank image upon receiving REQUEST_URI w/ query string & HTTP_REFERRER (e.g: an iframe of a blocked domain)
exit(setHeader().'<html>
<head><script>window.close();</script></head>
<body><img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="></body>
</html>');
} elseif (!in_array($currentUrlExt, $validExtTypes) || substr_count($_SERVER["REQUEST_URI"], "?")) {
// Serve SVG upon receiving non $validExtTypes URL extension or query string (e.g: not an iframe of a blocked domain)
$blockImg = '<a href="/"><svg xmlns="http://www.w3.org/2000/svg" width="110" height="16"><defs><style>a {text-decoration: none;} circle {stroke: rgba(152,2,2,0.5); fill: none; stroke-width: 2;} rect {fill: rgba(152,2,2,0.5);} text {opacity: 0.3; font: 11px Arial;}</style></defs><circle cx="8" cy="8" r="7"/><rect x="10.3" y="-6" width="2" height="12" transform="rotate(45)"/><text x="19.3" y="12">Blocked by Pi-hole</text></svg></a>';
exit(setHeader()."<html>
<head>$viewPort</head>
<body>$blockImg</body>
</html>");
} }
// Handle incoming URI types /* Start processing Block Page from here */
if (!$showPage)
{ // Determine placeholder text based off $svPasswd presence
?> $wlPlaceHolder = empty($svPasswd) ? "No admin password set" : "Javascript disabled";
<html>
<head> // Define admin email address text
<script>window.close();</script></head> $bpAskAdmin = !empty($svEmail) ? '<a href="mailto:'.$svEmail.'?subject=Site Blocked: '.$serverName.'"></a>' : "<span/>";
<body>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"> // Determine if at least one block list has been generated
</body> if (empty(glob("/etc/pihole/list.0.*.domains")))
</html> die("[ERROR] There are no domain lists generated lists within <code>/etc/pihole/</code>! Please update gravity by running <code>pihole -g</code>, or repair Pi-hole using <code>pihole -r</code>.");
<?php
die(); // Set location of adlists file
if (is_file("/etc/pihole/adlists.list")) {
$adLists = "/etc/pihole/adlists.list";
} elseif (is_file("/etc/pihole/adlists.default")) {
$adLists = "/etc/pihole/adlists.default";
} else {
die("[ERROR] File not found: <code>/etc/pihole/adlists.list</code>");
} }
// Get Pi-hole version // Get all URLs starting with "http" or "www" from adlists and re-index array numerically
$piHoleVersion = exec('cd /etc/.pihole/ && git describe --tags --abbrev=0'); $adlistsUrls = array_values(preg_grep("/(^http)|(^www)/i", file($adLists, FILE_IGNORE_NEW_LINES)));
if (empty($adlistsUrls))
die("[ERROR]: There are no adlist URL's found within <code>$adLists</code>");
// Get total number of blocklists (Including Whitelist, Blacklist & Wildcard lists)
$adlistsCount = count($adlistsUrls) + 3;
// Get results of queryads.php exact search
ini_set("default_socket_timeout", 3);
function queryAds($serverName) {
// Determine the time it takes while querying adlists
$preQueryTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"];
$queryAds = file("http://127.0.0.1/admin/scripts/pi-hole/php/queryads.php?domain=$serverName&bp", FILE_IGNORE_NEW_LINES);
$queryAds = array_values(array_filter(preg_replace("/data:\s+/", "", $queryAds)));
$queryTime = sprintf("%.0f", (microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]) - $preQueryTime);
// Exception Handling
try {
if ($queryTime >= ini_get("default_socket_timeout")) {
throw new Exception ("Connection timeout (".ini_get("default_socket_timeout")."s)");
} elseif (!strpos($queryAds[0], ".") !== false) {
if (strpos($queryAds[0], "No exact results") !== FALSE) return array("0" => "none");
throw new Exception ("Unhandled error message (<code>$queryAds[0]</code>)");
}
return $queryAds;
} catch (Exception $e) {
return array("0" => "error", "1" => $e->getMessage());
}
// Don't show the URI if it is the root directory
if($uri == "/")
{
$uri = "";
} }
$queryAds = queryAds($serverName);
if ($queryAds[0] === "error") {
die("[ERROR]: Unable to parse results from <i>queryads.php</i>: <code>".$queryAds[1]."</code>");
} else {
$featuredTotal = count($queryAds);
// Place results into key => value array
$queryResults = null;
foreach ($queryAds as $str) {
$value = explode(" ", $str);
@$queryResults[$value[0]] .= "$value[1]";
}
}
// Determine if domain has been blacklisted, whitelisted, wildcarded or CNAME blocked
if (strpos($queryAds[0], "blacklist") !== FALSE) {
$notableFlagClass = "blacklist";
$adlistsUrls = array("π" => substr($queryAds[0], 2));
} elseif (strpos($queryAds[0], "whitelist") !== FALSE) {
$notableFlagClass = "noblock";
$adlistsUrls = array("π" => substr($queryAds[0], 2));
$wlInfo = "recentwl";
} elseif (strpos($queryAds[0], "wildcard") !== FALSE) {
$notableFlagClass = "wildcard";
$adlistsUrls = array("π" => substr($queryAds[0], 2));
} elseif ($queryAds[0] === "none") {
$featuredTotal = "0";
$notableFlagClass = "noblock";
// Determine appropriate info message if CNAME exists
$dnsRecord = dns_get_record("$serverName")[0];
if (array_key_exists("target", $dnsRecord)) {
$wlInfo = $dnsRecord['target'];
} else {
$wlInfo = "unknown";
}
}
// Set #bpOutput notification
$wlOutputClass = (isset($wlInfo) && $wlInfo === "recentwl") ? $wlInfo : "hidden";
$wlOutput = (isset($wlInfo) && $wlInfo !== "recentwl") ? "<a href='http://$wlInfo'>$wlInfo</a>" : "";
// Get Pi-hole Core version
$phVersion = exec("cd /etc/.pihole/ && git describe --long --tags");
// Print $execTime on development branches
// Marginally faster than "git rev-parse --abbrev-ref HEAD"
if (explode("-", $phVersion)[1] != "0")
$execTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"];
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<!-- Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* This file is copyright under the latest version of the EUPL. -->
<html> <html>
<head> <head>
<meta charset='UTF-8'/> <meta charset="UTF-8">
<title>Website Blocked</title> <?=$viewPort ?>
<link rel='stylesheet' href='http://pi.hole/pihole/blockingpage.css'/> <?=setHeader() ?>
<link rel='shortcut icon' href='http://pi.hole/admin/img/favicon.png' type='image/png'/> <meta name="robots" content="noindex,nofollow"/>
<meta name='viewport' content='width=device-width,initial-scale=1.0,maximum-scale=1.0, user-scalable=no'/> <meta http-equiv="x-dns-prefetch-control" content="off">
<meta name='robots' content='noindex,nofollow'/> <link rel="shortcut icon" href="http://pi.hole/admin/img/favicon.png" type="image/x-icon"/>
<link rel="stylesheet" href="http://pi.hole/pihole/blockingpage.css" type="text/css"/>
<title> <?=$serverName ?></title>
<script src="http://pi.hole/admin/scripts/vendor/jquery.min.js"></script>
<script>
window.onload = function () {
<?php
// Remove href fallback from "Back to safety" button
if ($featuredTotal > 0) echo '$("#bpBack").removeAttr("href");';
// Enable whitelisting if $svPasswd is present & JS is available
if (!empty($svPasswd) && $featuredTotal > 0) {
echo '$("#bpWLPassword, #bpWhitelist").prop("disabled", false);';
echo '$("#bpWLPassword").attr("placeholder", "Password");';
}
?>
}
</script>
</head> </head>
<body id="body"> <body id="blockpage"><div id="bpWrapper">
<header> <header>
<h1><a href='/'>Website Blocked</a></h1> <h1 id="bpTitle">
<a class="title" href="/"><?php //Website Blocked ?></a>
</h1>
<div class="spc"></div>
<input id="bpAboutToggle" type="checkbox"/>
<div id="bpAbout">
<div class="aboutPH">
<div class="aboutImg"/></div>
<p>Open Source Ad Blocker
<small>Designed for Raspberry Pi</small>
</p>
</div>
<div class="aboutLink">
<a class="linkPH" href="https://github.com/pi-hole/pi-hole/wiki/What-is-Pi-hole%3F-A-simple-explanation"><?php //About PH ?></a>
<?php if (!empty($svEmail)) echo '<a class="linkEmail" href="mailto:'.$svEmail.'"></a>'; ?>
</div>
</div>
<div id="bpAlt">
<label class="altBtn" for="bpAboutToggle"><?php //Why am I here? ?></label>
</div>
</header> </header>
<main> <main>
<div>Access to the following site has been blocked:<br/> <div id="bpOutput" class="<?=$wlOutputClass ?>"><?=$wlOutput ?></div>
<span class='pre msg'><?php echo $serverName.$uri; ?></span></div> <div id="bpBlock">
<div>If you have an ongoing use for this website, please ask the owner of the Pi-hole in your network to have it whitelisted.</div> <p class="blockMsg"><?=$serverName ?></p>
<input id="domain" type="hidden" value="<?php echo $serverName; ?>"> </div>
<input id="quiet" type="hidden" value="yes"> <?php if(isset($notableFlagClass)) { ?>
<button id="btnSearch" class="buttons blocked" type="button" style="visibility: hidden;"></button> <div id="bpFlag">
This page is blocked because it is explicitly contained within the following block list(s): <p class="flagMsg <?=$notableFlagClass ?>"></p>
<pre id="output" style="width: 100%; height: 100%;" hidden="true"></pre><br/> </div>
<div class='buttons blocked'> <?php } ?>
<a class='safe33' href='javascript:history.back()'>Go back</a> <div id="bpHelpTxt"><?=$bpAskAdmin ?></div>
<a class='safe33' id="whitelisting">Whitelist this page</a> <div id="bpButtons" class="buttons">
<a class='safe33' href='javascript:window.close()'>Close window</a> <a id="bpBack" onclick="javascript:history.back()" href="about:home"></a>
</div> <?php if ($featuredTotal > 0) echo '<label id="bpInfo" for="bpMoreToggle"></label>'; ?>
<div style="width: 98%; text-align: center; padding: 10px;" hidden="true" id="whitelistingform"> </div>
<p>Note that whitelisting domains which are blocked using the wildcard method won't work.</p> <input id="bpMoreToggle" type="checkbox">
<p>Password required!</p><br/> <div id="bpMoreInfo">
<form> <span id="bpFoundIn"><span><?=$featuredTotal ?></span><?=$adlistsCount ?></span>
<input name="list" type="hidden" value="white"><br/> <pre id='bpQueryOutput'><?php if ($featuredTotal > 0) foreach ($queryResults as $num => $value) { echo "<span>[$num]:</span>$adlistsUrls[$num]\n"; } ?></pre>
Domain:<br/>
<input name="domain" value="<?php echo $serverName ?>" disabled><br/><br/> <form id="bpWLButtons" class="buttons">
Password:<br/> <input id="bpWLDomain" type="text" value="<?=$serverName ?>" disabled/>
<input type="password" id="pw" name="pw"><br/><br/> <input id="bpWLPassword" type="password" placeholder="<?=$wlPlaceHolder ?>" disabled/><button id="bpWhitelist" type="button" disabled></button>
<button class="buttons33 safe" id="btnAdd" type="button">Whitelist</button> </form>
</form><br/> </div>
<pre id="whitelistingoutput" style="width: 100%; height: 100%; padding: 5px;" hidden="true"></pre><br/>
</div>
</main> </main>
<footer>Generated <?php echo date('D g:i A, M d'); ?> by Pi-hole <?php echo $piHoleVersion; ?></footer>
<script src="http://pi.hole/admin/scripts/vendor/jquery.min.js"></script>
<script>
// Create event for when the output is appended to
(function($) {
var origAppend = $.fn.append;
$.fn.append = function () { <footer><span><?=date("l g:i A, F dS"); ?>.</span> Pi-hole <?=$phVersion ?> (<?=gethostname()."/".$_SERVER["SERVER_ADDR"]; if (isset($execTime)) printf("/%.2fs", $execTime); ?>)</footer>
return origAppend.apply(this, arguments).trigger("append"); </div>
};
})(jQuery);
</script>
<script src="http://pi.hole/admin/scripts/pi-hole/js/queryads.js"></script>
<script> <script>
function inIframe () { function add() {
try { $("#bpOutput").removeClass("hidden error exception");
return window.self !== window.top; $("#bpOutput").addClass("add");
} catch (e) { var domain = "<?=$serverName ?>";
return true; var pw = $("#bpWLPassword");
if(domain.length === 0) {
return;
} }
} $.ajax({
url: "/admin/scripts/pi-hole/php/add.php",
// Try to detect if page is loaded within iframe method: "post",
if(inIframe()) data: {"domain":domain, "list":"white", "pw":pw.val()},
{ success: function(response) {
// Within iframe if(response.indexOf("Pi-hole blocking") !== -1) {
// hide content of page setTimeout(function(){window.location.reload(1);}, 10000);
$('#body').hide(); $("#bpOutput").removeClass("add");
// remove background $("#bpOutput").addClass("success");
document.body.style.backgroundImage = "none"; } else {
} $("#bpOutput").removeClass("add");
else $("#bpOutput").addClass("error");
{ $("#bpOutput").html(""+response+"");
// Query adlists }
$( "#btnSearch" ).click(); },
} error: function(jqXHR, exception) {
$("#bpOutput").removeClass("add");
$( "#whitelisting" ).on( "click", function(){ $( "#whitelistingform" ).removeAttr( "hidden" ); }); $("#bpOutput").addClass("exception");
}
// Remove whitelist functionality if the domain was blocked because of a wildcard });
$( "#output" ).bind("append", function(){ }
if($( "#output" ).contents()[0].data.indexOf("Wildcard blocking") !== -1) <?php if ($featuredTotal > 0) { ?>
{ $(document).keypress(function(e) {
$( "#whitelisting" ).hide(); if(e.which === 13 && $("#bpWLPassword").is(":focus")) {
$( "#whitelistingform" ).hide(); add();
} }
}); });
$("#bpWhitelist").on("click", function() {
function add() {
var domain = $("#domain");
var pw = $("#pw");
if(domain.val().length === 0){
return;
}
$.ajax({
url: "/admin/scripts/pi-hole/php/add.php",
method: "post",
data: {"domain":domain.val(), "list":"white", "pw":pw.val()},
success: function(response) {
$( "#whitelistingoutput" ).removeAttr( "hidden" );
if(response.indexOf("Pi-hole blocking") !== -1)
{
// Reload page after 5 seconds
setTimeout(function(){window.location.reload(1);}, 5000);
$( "#whitelistingoutput" ).html("---> Success <---<br/>You may have to flush your DNS cache");
}
else
{
$( "#whitelistingoutput" ).html("---> "+response+" <---");
}
},
error: function(jqXHR, exception) {
$( "#whitelistingoutput" ).removeAttr( "hidden" );
$( "#whitelistingoutput" ).html("---> Unknown Error <---");
}
});
}
// Handle enter button for adding domains
$(document).keypress(function(e) {
if(e.which === 13 && $("#pw").is(":focus")) {
add(); add();
} });
}); <?php } ?>
// Handle buttons
$("#btnAdd").on("click", function() {
add();
});
</script> </script>
</body> </body></html>
</html>

View file

@ -50,7 +50,8 @@ compress.filetype = ( "application/javascript", "text/css", "text/html
# default listening port for IPv6 falls back to the IPv4 port # default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl" include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl" #include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
include_shell "find /etc/lighttpd/conf-enabled -name '*.conf' -a ! -name 'letsencrypt.conf' -printf 'include \"%p\"\n' 2>/dev/null"
# If the URL starts with /admin, it is the Web interface # If the URL starts with /admin, it is the Web interface
$HTTP["url"] =~ "^/admin/" { $HTTP["url"] =~ "^/admin/" {
@ -59,21 +60,9 @@ $HTTP["url"] =~ "^/admin/" {
"X-Pi-hole" => "The Pi-hole Web interface is working!", "X-Pi-hole" => "The Pi-hole Web interface is working!",
"X-Frame-Options" => "DENY" "X-Frame-Options" => "DENY"
) )
} $HTTP["url"] =~ ".ttf$" {
# Allow Block Page access to local fonts
# Rewite js requests, must be out of $HTTP block due to bug #2526 setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
url.rewrite = ( "^(?!/admin/).*\.js$" => "pihole/index.js" )
# If the URL does not start with /admin, then it is a query for an ad domain
$HTTP["url"] =~ "^(?!/admin)/.*" {
# Create a response header for debugging using curl -I
setenv.add-response-header = ( "X-Pi-hole" => "A black hole for Internet advertisements." )
}
# Entering just "pi.hole" into a browser redirects to "pi.hole/admin/"
$HTTP["host"] == "pi.hole" {
$HTTP["url"] == "/" {
url.redirect = ( "" => "/admin/" )
} }
} }

View file

@ -75,22 +75,13 @@ fastcgi.server = ( ".php" =>
# If the URL starts with /admin, it is the Web interface # If the URL starts with /admin, it is the Web interface
$HTTP["url"] =~ "^/admin/" { $HTTP["url"] =~ "^/admin/" {
# Create a response header for debugging using curl -I # Create a response header for debugging using curl -I
setenv.add-response-header = ( "X-Pi-hole" => "The Pi-hole Web interface is working!" ) setenv.add-response-header = (
} "X-Pi-hole" => "The Pi-hole Web interface is working!",
"X-Frame-Options" => "DENY"
# Rewite js requests, must be out of $HTTP block due to bug #2526 )
url.rewrite = ( "^(?!/admin/).*\.js$" => "pihole/index.js" ) $HTTP["url"] =~ ".ttf$" {
# Allow Block Page access to local fonts
# If the URL does not start with /admin, then it is a query for an ad domain setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
$HTTP["url"] =~ "^(?!/admin)/.*" {
# Create a response header for debugging using curl -I
setenv.add-response-header = ( "X-Pi-hole" => "A black hole for Internet advertisements." )
}
# Entering just "pi.hole" into a browser redirects to "pi.hole/admin/"
$HTTP["host"] == "pi.hole" {
$HTTP["url"] == "/" {
url.redirect = ( "" => "/admin/" )
} }
} }

View file

@ -186,7 +186,6 @@ def test_installPiholeWeb_fresh_install_no_errors(Pihole):
assert tick_box + ' Installing sudoer file' in installWeb.stdout assert tick_box + ' Installing sudoer file' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory assert 'blockingpage.css' in web_directory
def test_update_package_cache_success_no_errors(Pihole): def test_update_package_cache_success_no_errors(Pihole):