From 44a48a6b4af4e6f6ae702b858ea3f5344b7883e0 Mon Sep 17 00:00:00 2001 From: dosse91 Date: Wed, 24 Feb 2021 08:14:30 +0100 Subject: [PATCH] Merged with master --- backend/garbage.php | 72 ++++-- backend/getIP.php | 444 +++++++++++++++++++++++--------- backend/getIP_ipInfo_apikey.php | 5 +- results/idObfuscation.php | 109 +++++--- results/index.php | 366 +++++++++++++++----------- results/stats.php | 317 ++++++++++++----------- results/telemetry.php | 103 +++----- results/telemetry_db.php | 220 ++++++++++++++++ results/telemetry_settings.php | 36 +-- 9 files changed, 1083 insertions(+), 589 deletions(-) create mode 100644 results/telemetry_db.php diff --git a/backend/garbage.php b/backend/garbage.php index e2fcc78..89ab6c9 100644 --- a/backend/garbage.php +++ b/backend/garbage.php @@ -1,31 +1,63 @@ 1024) { + return 1024; + } + + return (int) $_GET['ckSize']; } -// Download follows... -header('Content-Description: File Transfer'); -header('Content-Type: application/octet-stream'); -header('Content-Disposition: attachment; filename=random.dat'); -header('Content-Transfer-Encoding: binary'); -// Never cache me -header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0'); -header('Cache-Control: post-check=0, pre-check=0', false); -header('Pragma: no-cache'); + +/** + * @return void + */ +function sendHeaders() +{ + header('HTTP/1.1 200 OK'); + + if (isset($_GET['cors'])) { + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Methods: GET, POST'); + } + + // Indicate a file download + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename=random.dat'); + header('Content-Transfer-Encoding: binary'); + + // Cache settings: never cache this request + header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0'); + header('Cache-Control: post-check=0, pre-check=0', false); + header('Pragma: no-cache'); +} + +// Determine how much data we should send +$chunks = getChunkCount(); + // Generate data -$data=openssl_random_pseudo_bytes(1048576); +$data = openssl_random_pseudo_bytes(1048576); + // Deliver chunks of 1048576 bytes -$chunks=isset($_GET['ckSize']) ? intval($_GET['ckSize']) : 4; -if(empty($chunks)){$chunks = 4;} -if($chunks>1024){$chunks = 1024;} -for($i=0;$i<$chunks;$i++){ +sendHeaders(); +for ($i = 0; $i < $chunks; $i++) { echo $data; flush(); } -?> diff --git a/backend/getIP.php b/backend/getIP.php index 7d7c400..a2ed9a0 100644 --- a/backend/getIP.php +++ b/backend/getIP.php @@ -1,59 +1,177 @@ $ip . " - localhost IPv6 access", 'rawIspInfo' => ""]); - die(); + // simplified IPv6 link-local address (should match fe80::/10) + if (stripos($ip, 'fe80:') === 0) { + return 'link-local IPv6 access'; + } + + // anything within the 127/8 range is localhost ipv4, the ip must start with 127.0 + if (strpos($ip, '127.') === 0) { + return 'localhost IPv4 access'; + } + + // 10/8 private IPv4 + if (strpos($ip, '10.') === 0) { + return 'private IPv4 access'; + } + + // 172.16/12 private IPv4 + if (preg_match('/^172\.(1[6-9]|2\d|3[01])\./', $ip) === 1) { + return 'private IPv4 access'; + } + + // 192.168/16 private IPv4 + if (strpos($ip, '192.168.') === 0) { + return 'private IPv4 access'; + } + + // IPv4 link-local + if (strpos($ip, '169.254.') === 0) { + return 'link-local IPv4 access'; + } + + return null; } -if (stripos($ip, 'fe80:') === 0) { // simplified IPv6 link-local address (should match fe80::/10) - echo json_encode(['processedString' => $ip . " - link-local IPv6 access", 'rawIspInfo' => ""]); - die(); + +/** + * @return string + */ +function getIpInfoTokenString() +{ + if ( + !file_exists(API_KEY_FILE) + || !is_readable(API_KEY_FILE) + ) { + return ''; + } + + require API_KEY_FILE; + + if (empty($IPINFO_APIKEY)) { + return ''; + } + + return '?token='.$IPINFO_APIKEY; } -if (strpos($ip, '127.') === 0) { //anything within the 127/8 range is localhost ipv4, the ip must start with 127.0 - echo json_encode(['processedString' => $ip . " - localhost IPv4 access", 'rawIspInfo' => ""]); - die(); + +/** + * @param string $ip + * + * @return array|null + */ +function getIspInfo($ip) +{ + $json = file_get_contents('https://ipinfo.io/'.$ip.'/json'.getIpInfoTokenString()); + if (!is_string($json)) { + return null; + } + + $data = json_decode($json, true); + if (!is_array($data)) { + return null; + } + + return $data; } -if (strpos($ip, '10.') === 0) { // 10/8 private IPv4 - echo json_encode(['processedString' => $ip . " - private IPv4 access", 'rawIspInfo' => ""]); - die(); + +/** + * @param array|null $rawIspInfo + * + * @return string + */ +function getIsp($rawIspInfo) +{ + if ( + !is_array($rawIspInfo) + || !array_key_exists('org', $rawIspInfo) + || !is_string($rawIspInfo['org']) + || empty($rawIspInfo['org']) + ) { + return 'Unknown ISP'; + } + + // Remove AS##### from ISP name, if present + return preg_replace('/AS\\d+\\s/', '', $rawIspInfo['org']); } -if (preg_match('/^172\.(1[6-9]|2\d|3[01])\./', $ip) === 1) { // 172.16/12 private IPv4 - echo json_encode(['processedString' => $ip . " - private IPv4 access", 'rawIspInfo' => ""]); - die(); -} -if (strpos($ip, '192.168.') === 0) { // 192.168/16 private IPv4 - echo json_encode(['processedString' => $ip . " - private IPv4 access", 'rawIspInfo' => ""]); - die(); -} -if (strpos($ip, '169.254.') === 0) { // IPv4 link-local - echo json_encode(['processedString' => $ip . " - link-local IPv4 access", 'rawIspInfo' => ""]); - die(); + +/** + * @return string|null + */ +function getServerLocation() +{ + $serverLoc = null; + if ( + file_exists(SERVER_LOCATION_CACHE_FILE) + && is_readable(SERVER_LOCATION_CACHE_FILE) + ) { + require SERVER_LOCATION_CACHE_FILE; + } + if (is_string($serverLoc) && !empty($serverLoc)) { + return $serverLoc; + } + + $json = file_get_contents('https://ipinfo.io/json'.getIpInfoTokenString()); + if (!is_string($json)) { + return null; + } + + $details = json_decode($json, true); + if ( + !is_array($details) + || !array_key_exists('loc', $details) + || !is_string($details['loc']) + || empty($details['loc']) + ) { + return null; + } + + $serverLoc = $details['loc']; + $cacheData = " $ip . " - " . $isp, 'rawIspInfo' => $rawIspInfo]); -} else { - echo json_encode(['processedString' => $ip, 'rawIspInfo' => ""]); + + $unit = $_GET['distance']; + $clientLocation = $rawIspInfo['loc']; + $serverLocation = getServerLocation(); + + if (!is_string($serverLocation)) { + return null; + } + + return calculateDistance( + $serverLocation, + $clientLocation, + $unit + ); } -?> + +/** + * @param string $clientLocation + * @param string $serverLocation + * @param string $unit + * + * @return string + */ +function calculateDistance($clientLocation, $serverLocation, $unit) +{ + list($clientLatitude, $clientLongitude) = explode(',', $clientLocation); + list($serverLatitude, $serverLongitude) = explode(',', $serverLocation); + $dist = distance( + $clientLatitude, + $clientLongitude, + $serverLatitude, + $serverLongitude + ); + + if ('mi' === $unit) { + $dist /= 1.609344; + $dist = round($dist, -1); + if ($dist < 15) { + $dist = '<15'; + } + + return $dist.' mi'; + } + + if ('km' === $unit) { + $dist = round($dist, -1); + if ($dist < 20) { + $dist = '<20'; + } + + return $dist.' km'; + } + + return null; +} + +/** + * @return void + */ +function sendHeaders() +{ + header('Content-Type: application/json; charset=utf-8'); + + if (isset($_GET['cors'])) { + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Methods: GET, POST'); + } + + header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0'); + header('Cache-Control: post-check=0, pre-check=0', false); + header('Pragma: no-cache'); +} + +/** + * @param string $ip + * @param string|null $ipInfo + * @param string|null $distance + * @param array|null $rawIspInfo + * + * @return void + */ +function sendResponse( + $ip, + $ipInfo = null, + $distance = null, + $rawIspInfo = null +) { + $processedString = $ip; + if (is_string($ipInfo)) { + $processedString .= ' - '.$ipInfo; + } + + if ( + is_array($rawIspInfo) + && array_key_exists('country', $rawIspInfo) + ) { + $processedString .= ', '.$rawIspInfo['country']; + } + if (is_string($distance)) { + $processedString .= ' ('.$distance.')'; + } + + sendHeaders(); + echo json_encode([ + 'processedString' => $processedString, + 'rawIspInfo' => $rawIspInfo ?: '', + ]); +} + +$ip = getClientIp(); + +$localIpInfo = getLocalOrPrivateIpInfo($ip); +// local ip, no need to fetch further information +if (is_string($localIpInfo)) { + sendResponse($ip, $localIpInfo); + exit; +} + +if (!isset($_GET['isp'])) { + sendResponse($ip); + exit; +} + +$rawIspInfo = getIspInfo($ip); +$isp = getIsp($rawIspInfo); +$distance = getDistance($rawIspInfo); + +sendResponse($ip, $isp, $distance, $rawIspInfo); diff --git a/backend/getIP_ipInfo_apikey.php b/backend/getIP_ipInfo_apikey.php index 49da1dc..e200c3f 100644 --- a/backend/getIP_ipInfo_apikey.php +++ b/backend/getIP_ipInfo_apikey.php @@ -1,3 +1,4 @@ \ No newline at end of file + +// put your token between the quotes if you have one +$IPINFO_APIKEY = ''; diff --git a/results/idObfuscation.php b/results/idObfuscation.php index 3b4482a..f7dd8a5 100644 --- a/results/idObfuscation.php +++ b/results/idObfuscation.php @@ -1,43 +1,72 @@ >1)|($id&0x55555555)<<1; - $id=(($id&0x0000FFFF)<<16)|(($id&0xFFFF0000)>>16); - return $id; - }else{ - $id=(($id&0x0000FFFF)<<16)|(($id&0xFFFF0000)>>16); - $id=(($id&0xAAAAAAAA)>>1)|($id&0x55555555)<<1; - return $id^$salt; - } -} -function obfuscateId($id){ - return str_pad(base_convert(obfdeobf($id+1,false),10,36),7,0,STR_PAD_LEFT); -} -function deobfuscateId($id){ - return obfdeobf(base_convert($id,36,10),true)-1; + +define('ID_OBFUSCATION_SALT_FILE', __DIR__.'/idObfuscation_salt.php'); + +/** + * @return string|int + */ +function getObfuscationSalt() +{ + if (!file_exists(ID_OBFUSCATION_SALT_FILE)) { + $bytes = openssl_random_pseudo_bytes(4); + + $saltData = " \ No newline at end of file +/** + * This is a simple reversible hash function I made for encoding and decoding test IDs. + * It is not cryptographically secure, don't use it to hash passwords or something! + * + * @param int|string $id + * @param bool $dec + * + * @return int|string + */ +function obfdeobf($id, $dec) +{ + $salt = getObfuscationSalt() & 0xFFFFFFFF; + $id &= 0xFFFFFFFF; + if ($dec) { + $id ^= $salt; + $id = (($id & 0xAAAAAAAA) >> 1) | ($id & 0x55555555) << 1; + $id = (($id & 0x0000FFFF) << 16) | (($id & 0xFFFF0000) >> 16); + + return $id; + } + + $id = (($id & 0x0000FFFF) << 16) | (($id & 0xFFFF0000) >> 16); + $id = (($id & 0xAAAAAAAA) >> 1) | ($id & 0x55555555) << 1; + + return $id ^ $salt; +} + +/** + * @param int $id + * + * @return string + */ +function obfuscateId($id) +{ + return str_pad(base_convert(obfdeobf($id + 1, false), 10, 36), 7, 0, STR_PAD_LEFT); +} + +/** + * @param string $id + * + * @return int + */ +function deobfuscateId($id) +{ + return obfdeobf(base_convert($id, 36, 10), true) - 1; +} diff --git a/results/index.php b/results/index.php index a9dac38..bfd6025 100644 --- a/results/index.php +++ b/results/index.php @@ -1,164 +1,224 @@ prepare("select ispinfo,dl,ul,ping,jitter from speedtest_users where id=?"); - $q->bind_param("i",$id); - $q->execute(); - $q->bind_result($ispinfo,$dl,$ul,$ping,$jit); - $q->fetch(); -}else if($db_type=="sqlite"){ - $conn = new PDO("sqlite:$Sqlite_db_file") or die(); - $q=$conn->prepare("select ispinfo,dl,ul,ping,jitter from speedtest_users where id=?") or die(); - $q->execute(array($id)) or die(); - $row=$q->fetch() or die(); - $ispinfo=$row["ispinfo"]; - $dl=$row["dl"]; - $ul=$row["ul"]; - $ping=$row["ping"]; - $jit=$row["jitter"]; - $conn=null; -}else if($db_type=="postgresql"){ - $conn_host = "host=$PostgreSql_hostname"; - $conn_db = "dbname=$PostgreSql_databasename"; - $conn_user = "user=$PostgreSql_username"; - $conn_password = "password=$PostgreSql_password"; - $conn = new PDO("pgsql:$conn_host;$conn_db;$conn_user;$conn_password") or die(); - $q=$conn->prepare("select ispinfo,dl,ul,ping,jitter from speedtest_users where id=?") or die(); - $q->execute(array($id)) or die(); - $row=$q->fetch() or die(); - $ispinfo=$row["ispinfo"]; - $dl=$row["dl"]; - $ul=$row["ul"]; - $ping=$row["ping"]; - $jit=$row["jitter"]; - $conn=null; -}else die(); + return number_format($d, 0, '.', ''); +} -$dl=format($dl); -$ul=format($ul); -$ping=format($ping); -$jit=format($jit); +/** + * @param array $speedtest + * + * @return array + */ +function formatSpeedtestDataForImage($speedtest) +{ + // format values for the image + $speedtest['dl'] = format($speedtest['dl']); + $speedtest['ul'] = format($speedtest['ul']); + $speedtest['ping'] = format($speedtest['ping']); + $speedtest['jit'] = format($speedtest['jitter']); + $speedtest['timestamp'] = $speedtest['timestamp']; -$ispinfo=json_decode($ispinfo,true)["processedString"]; -$dash=strpos($ispinfo,"-"); -if(!($dash===FALSE)){ - $ispinfo=substr($ispinfo,$dash+2); - $par=strrpos($ispinfo,"("); - if(!($par===FALSE)) $ispinfo=substr($ispinfo,0,$par); -}else $ispinfo=""; + $ispinfo = json_decode($speedtest['ispinfo'], true)['processedString']; + $dash = strpos($ispinfo, '-'); + if ($dash !== false) { + $ispinfo = substr($ispinfo, $dash + 2); + $par = strrpos($ispinfo, '('); + if ($par !== false) { + $ispinfo = substr($ispinfo, 0, $par); + } + } else { + $ispinfo = ''; + } -$dlBbox=imageftbbox($FONT_LABEL_SIZE_BIG,0,$FONT_LABEL,$DL_TEXT); -$ulBbox=imageftbbox($FONT_LABEL_SIZE_BIG,0,$FONT_LABEL,$UL_TEXT); -$pingBbox=imageftbbox($FONT_LABEL_SIZE,0,$FONT_LABEL,$PING_TEXT); -$jitBbox=imageftbbox($FONT_LABEL_SIZE,0,$FONT_LABEL,$JIT_TEXT); -$dlMeterBbox=imageftbbox($FONT_METER_SIZE_BIG,0,$FONT_METER,$dl); -$ulMeterBbox=imageftbbox($FONT_METER_SIZE_BIG,0,$FONT_METER,$ul); -$pingMeterBbox=imageftbbox($FONT_METER_SIZE,0,$FONT_METER,$ping); -$jitMeterBbox=imageftbbox($FONT_METER_SIZE,0,$FONT_METER,$jit); -$mbpsBbox=imageftbbox($FONT_MEASURE_SIZE_BIG,0,$FONT_MEASURE,$MBPS_TEXT); -$msBbox=imageftbbox($FONT_MEASURE_SIZE,0,$FONT_MEASURE,$MS_TEXT); -$watermarkBbox=imageftbbox($FONT_WATERMARK_SIZE,0,$FONT_WATERMARK,$WATERMARK_TEXT); -$POSITION_X_WATERMARK=$WIDTH-$watermarkBbox[4]-4*$SCALE; + $speedtest['ispinfo'] = $ispinfo; -imagefilledrectangle($im, 0, 0, $WIDTH, $HEIGHT, $BACKGROUND_COLOR); -imagefttext($im,$FONT_LABEL_SIZE_BIG,0,$POSITION_X_DL-$dlBbox[4]/2,$POSITION_Y_DL_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$DL_TEXT); -imagefttext($im,$FONT_LABEL_SIZE_BIG,0,$POSITION_X_UL-$ulBbox[4]/2,$POSITION_Y_UL_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$UL_TEXT); -imagefttext($im,$FONT_LABEL_SIZE,0,$POSITION_X_PING-$pingBbox[4]/2,$POSITION_Y_PING_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$PING_TEXT); -imagefttext($im,$FONT_LABEL_SIZE,0,$POSITION_X_JIT-$jitBbox[4]/2,$POSITION_Y_JIT_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$JIT_TEXT); -imagefttext($im,$FONT_METER_SIZE_BIG,0,$POSITION_X_DL-$dlMeterBbox[4]/2,$POSITION_Y_DL_METER,$TEXT_COLOR_DL_METER,$FONT_METER,$dl); -imagefttext($im,$FONT_METER_SIZE_BIG,0,$POSITION_X_UL-$ulMeterBbox[4]/2,$POSITION_Y_UL_METER,$TEXT_COLOR_UL_METER,$FONT_METER,$ul); -imagefttext($im,$FONT_METER_SIZE,0,$POSITION_X_PING-$pingMeterBbox[4]/2-$msBbox[4]/2-$SMALL_SEP/2,$POSITION_Y_PING_METER,$TEXT_COLOR_PING_METER,$FONT_METER,$ping); -imagefttext($im,$FONT_METER_SIZE,0,$POSITION_X_JIT-$jitMeterBbox[4]/2-$msBbox[4]/2-$SMALL_SEP/2,$POSITION_Y_JIT_METER,$TEXT_COLOR_JIT_METER,$FONT_METER,$jit); -imagefttext($im,$FONT_MEASURE_SIZE_BIG,0,$POSITION_X_DL-$mbpsBbox[4]/2,$POSITION_Y_DL_MEASURE,$TEXT_COLOR_MEASURE,$FONT_MEASURE,$MBPS_TEXT); -imagefttext($im,$FONT_MEASURE_SIZE_BIG,0,$POSITION_X_UL-$mbpsBbox[4]/2,$POSITION_Y_UL_MEASURE,$TEXT_COLOR_MEASURE,$FONT_MEASURE,$MBPS_TEXT); -imagefttext($im,$FONT_MEASURE_SIZE,0,$POSITION_X_PING+$pingMeterBbox[4]/2+$SMALL_SEP/2-$msBbox[4]/2,$POSITION_Y_PING_MEASURE,$TEXT_COLOR_MEASURE,$FONT_MEASURE,$MS_TEXT); -imagefttext($im,$FONT_MEASURE_SIZE,0,$POSITION_X_JIT+$jitMeterBbox[4]/2+$SMALL_SEP/2-$msBbox[4]/2,$POSITION_Y_JIT_MEASURE,$TEXT_COLOR_MEASURE,$FONT_MEASURE,$MS_TEXT); -imagefttext($im,$FONT_ISP_SIZE,0,$POSITION_X_ISP,$POSITION_Y_ISP,$TEXT_COLOR_ISP,$FONT_ISP,$ispinfo); -imagefttext($im,$FONT_WATERMARK_SIZE,0,$POSITION_X_WATERMARK,$POSITION_Y_WATERMARK,$TEXT_COLOR_WATERMARK,$FONT_WATERMARK,$WATERMARK_TEXT); -imagefilledrectangle($im, 0, $SEPARATOR_Y, $WIDTH, $SEPARATOR_Y, $SEPARATOR_COLOR); -header('Content-Type: image/png'); -imagepng($im); -imagedestroy($im); + return $speedtest; +} -?> +/** + * @param array $speedtest + * + * @return void + */ +function drawImage($speedtest) +{ + // format values for the image + $data = formatSpeedtestDataForImage($speedtest); + $dl = $data['dl']; + $ul = $data['ul']; + $ping = $data['ping']; + $jit = $data['jitter']; + $ispinfo = $data['ispinfo']; + $timestamp = $data['timestamp']; + + // initialize the image + $SCALE = 1.25; + $SMALL_SEP = 8 * $SCALE; + $WIDTH = 400 * $SCALE; + $HEIGHT = 229 * $SCALE; + $im = imagecreatetruecolor($WIDTH, $HEIGHT); + $BACKGROUND_COLOR = imagecolorallocate($im, 255, 255, 255); + + // configure fonts + $FONT_LABEL = tryFont('OpenSans-Semibold'); + $FONT_LABEL_SIZE = 14 * $SCALE; + $FONT_LABEL_SIZE_BIG = 16 * $SCALE; + + $FONT_METER = tryFont('OpenSans-Light'); + $FONT_METER_SIZE = 20 * $SCALE; + $FONT_METER_SIZE_BIG = 22 * $SCALE; + + $FONT_MEASURE = tryFont('OpenSans-Semibold'); + $FONT_MEASURE_SIZE = 12 * $SCALE; + $FONT_MEASURE_SIZE_BIG = 12 * $SCALE; + + $FONT_ISP = tryFont('OpenSans-Semibold'); + $FONT_ISP_SIZE = 9 * $SCALE; + + $FONT_TIMESTAMP = tryFont("OpenSans-Light"); + $FONT_TIMESTAMP_SIZE = 8 * $SCALE; + + $FONT_WATERMARK = tryFont('OpenSans-Light'); + $FONT_WATERMARK_SIZE = 8 * $SCALE; + + // configure text colors + $TEXT_COLOR_LABEL = imagecolorallocate($im, 40, 40, 40); + $TEXT_COLOR_PING_METER = imagecolorallocate($im, 170, 96, 96); + $TEXT_COLOR_JIT_METER = imagecolorallocate($im, 170, 96, 96); + $TEXT_COLOR_DL_METER = imagecolorallocate($im, 96, 96, 170); + $TEXT_COLOR_UL_METER = imagecolorallocate($im, 96, 96, 96); + $TEXT_COLOR_MEASURE = imagecolorallocate($im, 40, 40, 40); + $TEXT_COLOR_ISP = imagecolorallocate($im, 40, 40, 40); + $SEPARATOR_COLOR = imagecolorallocate($im, 192, 192, 192); + $TEXT_COLOR_TIMESTAMP = imagecolorallocate($im, 160, 160, 160); + $TEXT_COLOR_WATERMARK = imagecolorallocate($im, 160, 160, 160); + + // configure positioning or the different parts on the image + $POSITION_X_PING = 125 * $SCALE; + $POSITION_Y_PING_LABEL = 24 * $SCALE; + $POSITION_Y_PING_METER = 60 * $SCALE; + $POSITION_Y_PING_MEASURE = 60 * $SCALE; + + $POSITION_X_JIT = 275 * $SCALE; + $POSITION_Y_JIT_LABEL = 24 * $SCALE; + $POSITION_Y_JIT_METER = 60 * $SCALE; + $POSITION_Y_JIT_MEASURE = 60 * $SCALE; + + $POSITION_X_DL = 120 * $SCALE; + $POSITION_Y_DL_LABEL = 105 * $SCALE; + $POSITION_Y_DL_METER = 143 * $SCALE; + $POSITION_Y_DL_MEASURE = 169 * $SCALE; + + $POSITION_X_UL = 280 * $SCALE; + $POSITION_Y_UL_LABEL = 105 * $SCALE; + $POSITION_Y_UL_METER = 143 * $SCALE; + $POSITION_Y_UL_MEASURE = 169 * $SCALE; + + $POSITION_X_ISP = 4 * $SCALE; + $POSITION_Y_ISP = 205 * $SCALE; + + $SEPARATOR_Y = 211 * $SCALE; + + $POSITION_X_TIMESTAMP= 4 * $SCALE; + $POSITION_Y_TIMESTAMP = 223 * $SCALE; + + $POSITION_Y_WATERMARK = 223 * $SCALE; + + // configure labels + $MBPS_TEXT = 'Mbps'; + $MS_TEXT = 'ms'; + $PING_TEXT = 'Ping'; + $JIT_TEXT = 'Jitter'; + $DL_TEXT = 'Download'; + $UL_TEXT = 'Upload'; + $WATERMARK_TEXT = 'LibreSpeed'; + + // create text boxes for each part of the image + $mbpsBbox = imageftbbox($FONT_MEASURE_SIZE_BIG, 0, $FONT_MEASURE, $MBPS_TEXT); + $msBbox = imageftbbox($FONT_MEASURE_SIZE, 0, $FONT_MEASURE, $MS_TEXT); + $pingBbox = imageftbbox($FONT_LABEL_SIZE, 0, $FONT_LABEL, $PING_TEXT); + $pingMeterBbox = imageftbbox($FONT_METER_SIZE, 0, $FONT_METER, $ping); + $jitBbox = imageftbbox($FONT_LABEL_SIZE, 0, $FONT_LABEL, $JIT_TEXT); + $jitMeterBbox = imageftbbox($FONT_METER_SIZE, 0, $FONT_METER, $jit); + $dlBbox = imageftbbox($FONT_LABEL_SIZE_BIG, 0, $FONT_LABEL, $DL_TEXT); + $dlMeterBbox = imageftbbox($FONT_METER_SIZE_BIG, 0, $FONT_METER, $dl); + $ulBbox = imageftbbox($FONT_LABEL_SIZE_BIG, 0, $FONT_LABEL, $UL_TEXT); + $ulMeterBbox = imageftbbox($FONT_METER_SIZE_BIG, 0, $FONT_METER, $ul); + $watermarkBbox = imageftbbox($FONT_WATERMARK_SIZE, 0, $FONT_WATERMARK, $WATERMARK_TEXT); + $POSITION_X_WATERMARK = $WIDTH - $watermarkBbox[4] - 4 * $SCALE; + + // put the parts together to draw the image + imagefilledrectangle($im, 0, 0, $WIDTH, $HEIGHT, $BACKGROUND_COLOR); + // ping + imagefttext($im, $FONT_LABEL_SIZE, 0, $POSITION_X_PING - $pingBbox[4] / 2, $POSITION_Y_PING_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $PING_TEXT); + imagefttext($im, $FONT_METER_SIZE, 0, $POSITION_X_PING - $pingMeterBbox[4] / 2 - $msBbox[4] / 2 - $SMALL_SEP / 2, $POSITION_Y_PING_METER, $TEXT_COLOR_PING_METER, $FONT_METER, $ping); + imagefttext($im, $FONT_MEASURE_SIZE, 0, $POSITION_X_PING + $pingMeterBbox[4] / 2 + $SMALL_SEP / 2 - $msBbox[4] / 2, $POSITION_Y_PING_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MS_TEXT); + // jitter + imagefttext($im, $FONT_LABEL_SIZE, 0, $POSITION_X_JIT - $jitBbox[4] / 2, $POSITION_Y_JIT_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $JIT_TEXT); + imagefttext($im, $FONT_METER_SIZE, 0, $POSITION_X_JIT - $jitMeterBbox[4] / 2 - $msBbox[4] / 2 - $SMALL_SEP / 2, $POSITION_Y_JIT_METER, $TEXT_COLOR_JIT_METER, $FONT_METER, $jit); + imagefttext($im, $FONT_MEASURE_SIZE, 0, $POSITION_X_JIT + $jitMeterBbox[4] / 2 + $SMALL_SEP / 2 - $msBbox[4] / 2, $POSITION_Y_JIT_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MS_TEXT); + // dl + imagefttext($im, $FONT_LABEL_SIZE_BIG, 0, $POSITION_X_DL - $dlBbox[4] / 2, $POSITION_Y_DL_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $DL_TEXT); + imagefttext($im, $FONT_METER_SIZE_BIG, 0, $POSITION_X_DL - $dlMeterBbox[4] / 2, $POSITION_Y_DL_METER, $TEXT_COLOR_DL_METER, $FONT_METER, $dl); + imagefttext($im, $FONT_MEASURE_SIZE_BIG, 0, $POSITION_X_DL - $mbpsBbox[4] / 2, $POSITION_Y_DL_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MBPS_TEXT); + // ul + imagefttext($im, $FONT_LABEL_SIZE_BIG, 0, $POSITION_X_UL - $ulBbox[4] / 2, $POSITION_Y_UL_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $UL_TEXT); + imagefttext($im, $FONT_METER_SIZE_BIG, 0, $POSITION_X_UL - $ulMeterBbox[4] / 2, $POSITION_Y_UL_METER, $TEXT_COLOR_UL_METER, $FONT_METER, $ul); + imagefttext($im, $FONT_MEASURE_SIZE_BIG, 0, $POSITION_X_UL - $mbpsBbox[4] / 2, $POSITION_Y_UL_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MBPS_TEXT); + // isp + imagefttext($im, $FONT_ISP_SIZE, 0, $POSITION_X_ISP, $POSITION_Y_ISP, $TEXT_COLOR_ISP, $FONT_ISP, $ispinfo); + // separator + imagefilledrectangle($im, 0, $SEPARATOR_Y, $WIDTH, $SEPARATOR_Y, $SEPARATOR_COLOR); + // timestamp + imagefttext($im, $FONT_TIMESTAMP_SIZE, 0, $POSITION_X_TIMESTAMP, $POSITION_Y_TIMESTAMP, $TEXT_COLOR_TIMESTAMP, $FONT_TIMESTAMP, $timestamp); + // watermark + imagefttext($im, $FONT_WATERMARK_SIZE, 0, $POSITION_X_WATERMARK, $POSITION_Y_WATERMARK, $TEXT_COLOR_WATERMARK, $FONT_WATERMARK, $WATERMARK_TEXT); + + // send the image to the browser + header('Content-Type: image/png'); + imagepng($im); +} + +$speedtest = getSpeedtestUserById($_GET['id']); +if (!is_array($speedtest)) { + exit(1); +} + +drawImage($speedtest); diff --git a/results/stats.php b/results/stats.php index 1e6ff1c..be35a6e 100644 --- a/results/stats.php +++ b/results/stats.php @@ -1,6 +1,10 @@ - -LibreSpeed - Stats - - - -

LibreSpeed - Stats

- - Please set $stats_password in telemetry_settings.php to enable access. - -
-
-

Search test results

- - - - -
- prepare("select id,timestamp,ip,ispinfo,ua,lang,dl,ul,ping,jitter,log,extra from speedtest_users where id=?"); - $q->bind_param("i",$id); - $q->execute(); - $q->store_result(); - $q->bind_result($id,$timestamp,$ip,$ispinfo,$ua,$lang,$dl,$ul,$ping,$jitter,$log,$extra); - } else if($db_type=="sqlite"||$db_type=="postgresql"){ - $q=$conn->prepare("select id,timestamp,ip,ispinfo,ua,lang,dl,ul,ping,jitter,log,extra from speedtest_users where id=?"); - $q->execute(array($id)); - } else die(); - }else{ - if($db_type=="mysql"){ - $q=$conn->prepare("select id,timestamp,ip,ispinfo,ua,lang,dl,ul,ping,jitter,log,extra from speedtest_users order by timestamp desc limit 0,100"); - $q->execute(); - $q->store_result(); - $q->bind_result($id,$timestamp,$ip,$ispinfo,$ua,$lang,$dl,$ul,$ping,$jitter,$log,$extra); - } else if($db_type=="sqlite"||$db_type=="postgresql"){ - $q=$conn->prepare("select id,timestamp,ip,ispinfo,ua,lang,dl,ul,ping,jitter,log,extra from speedtest_users order by timestamp desc limit 100"); - $q->execute(); - }else die(); - } - while(true){ - $id=null; $timestamp=null; $ip=null; $ispinfo=null; $ua=null; $lang=null; $dl=null; $ul=null; $ping=null; $jitter=null; $log=null; $extra=null; - if($db_type=="mysql"){ - if(!$q->fetch()) break; - } else if($db_type=="sqlite"||$db_type=="postgresql"){ - if(!($row=$q->fetch())) break; - $id=$row["id"]; - $timestamp=$row["timestamp"]; - $ip=$row["ip"]; - $ispinfo=$row["ispinfo"]; - $ua=$row["ua"]; - $lang=$row["lang"]; - $dl=$row["dl"]; - $ul=$row["ul"]; - $ping=$row["ping"]; - $jitter=$row["jitter"]; - $log=$row["log"]; - $extra=$row["extra"]; - }else die(); - ?> - - - - - - - - - - - -
Test ID
Date and time
IP and ISP Info
User agent and locale
Download speed
Upload speed
Ping
Jitter
Log
Extra info
- - -
-

Login

- - -
- - + + LibreSpeed - Stats + + + +

LibreSpeed - Stats

+ + Please set $stats_password in telemetry_settings.php to enable access. + +
+
+

Search test results

+ + + + +
+ There was an error trying to fetch the speedtest result for ID "'.$_GET['id'].'".'; + } elseif (null === $speedtest) { + echo '
Could not find a speedtest result for ID "'.$_GET['id'].'".
'; + } else { + $speedtests = [$speedtest]; + } + } else { + $speedtests = getLatestSpeedtestUsers(); + if (false === $speedtests) { + echo '
There was an error trying to fetch latest speedtest results.
'; + } elseif (empty($speedtests)) { + echo '
Could not find any speedtest results in database.
'; + } + } + foreach ($speedtests as $speedtest) { + ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Test ID
Date and time
IP and ISP Info +
+ +
User agent and locale
+ +
Download speed
Upload speed
Ping
Jitter
Log
Extra info
+ +
+

Login

+ + +
+ + diff --git a/results/telemetry.php b/results/telemetry.php index 5699777..5824c43 100644 --- a/results/telemetry.php +++ b/results/telemetry.php @@ -1,81 +1,42 @@ prepare("INSERT INTO speedtest_users (ip,ispinfo,extra,ua,lang,dl,ul,ping,jitter,log) VALUES (?,?,?,?,?,?,?,?,?,?)") or die("2"); - $stmt->bind_param("ssssssssss",$ip,$ispinfo,$extra,$ua,$lang,$dl,$ul,$ping,$jitter,$log) or die("3"); - $stmt->execute() or die("4"); - $stmt->close() or die("5"); - $id=$conn->insert_id; - echo "id ".($enable_id_obfuscation?obfuscateId($id):$id); - $conn->close() or die("6"); - -}elseif($db_type=="sqlite"){ - $conn = new PDO("sqlite:$Sqlite_db_file") or die("1"); - $conn->exec(" - CREATE TABLE IF NOT EXISTS `speedtest_users` ( - `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - `ispinfo` text, - `extra` text, - `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `ip` text NOT NULL, - `ua` text NOT NULL, - `lang` text NOT NULL, - `dl` text, - `ul` text, - `ping` text, - `jitter` text, - `log` longtext - ); - "); - $stmt = $conn->prepare("INSERT INTO speedtest_users (ip,ispinfo,extra,ua,lang,dl,ul,ping,jitter,log) VALUES (?,?,?,?,?,?,?,?,?,?)") or die("2"); - $stmt->execute(array($ip,$ispinfo,$extra,$ua,$lang,$dl,$ul,$ping,$jitter,$log)) or die("3"); - $id=$conn->lastInsertId(); - echo "id ".($enable_id_obfuscation?obfuscateId($id):$id); - $conn = null; -}elseif($db_type=="postgresql"){ - // Prepare connection parameters for db connection - $conn_host = "host=$PostgreSql_hostname"; - $conn_db = "dbname=$PostgreSql_databasename"; - $conn_user = "user=$PostgreSql_username"; - $conn_password = "password=$PostgreSql_password"; - // Create db connection - $conn = new PDO("pgsql:$conn_host;$conn_db;$conn_user;$conn_password") or die("1"); - $stmt = $conn->prepare("INSERT INTO speedtest_users (ip,ispinfo,extra,ua,lang,dl,ul,ping,jitter,log) VALUES (?,?,?,?,?,?,?,?,?,?)") or die("2"); - $stmt->execute(array($ip,$ispinfo,$extra,$ua,$lang,$dl,$ul,$ping,$jitter,$log)) or die("3"); - $id=$conn->lastInsertId(); - echo "id ".($enable_id_obfuscation?obfuscateId($id):$id); - $conn = null; +$id = insertSpeedtestUser($ip, $ispinfo, $extra, $ua, $lang, $dl, $ul, $ping, $jitter, $log); +if (false === $id) { + exit(1); } -else die("-1"); -?> + +echo 'id '.$id; diff --git a/results/telemetry_db.php b/results/telemetry_db.php new file mode 100644 index 0000000..6a1aee3 --- /dev/null +++ b/results/telemetry_db.php @@ -0,0 +1,220 @@ + PDO::ERRMODE_EXCEPTION + ]; + + try { + if ('mysql' === $db_type) { + if (!isset( + $MySql_hostname, + $MySql_port, + $MySql_databasename, + $MySql_username, + $MySql_password + )) { + return false; + } + + $dsn = 'mysql:' + .'host='.$MySql_hostname + .';port='.$MySql_port + .';dbname='.$MySql_databasename; + + return new PDO($dsn, $MySql_username, $MySql_password, $pdoOptions); + } + + if ('sqlite' === $db_type) { + if (!isset($Sqlite_db_file)) { + return false; + } + + $pdo = new PDO('sqlite:'.$Sqlite_db_file, null, null, $pdoOptions); + + $pdo->exec(' + CREATE TABLE IF NOT EXISTS `speedtest_users` ( + `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + `ispinfo` text, + `extra` text, + `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ip` text NOT NULL, + `ua` text NOT NULL, + `lang` text NOT NULL, + `dl` text, + `ul` text, + `ping` text, + `jitter` text, + `log` longtext + ); + '); + + return $pdo; + } + + if ('postgresql' === $db_type) { + if (!isset( + $PostgreSql_hostname, + $PostgreSql_databasename, + $PostgreSql_username, + $PostgreSql_password + )) { + return false; + } + + $dsn = 'pgsql:' + .'host='.$PostgreSql_hostname + .';dbname='.$PostgreSql_databasename; + + return new PDO($dsn, $PostgreSql_username, $PostgreSql_password, $pdoOptions); + } + } catch (Exception $e) { + return false; + } + + return false; +} + +/** + * @return bool + */ +function isObfuscationEnabled() +{ + require TELEMETRY_SETTINGS_FILE; + + return + isset($enable_id_obfuscation) + && true === $enable_id_obfuscation; +} + +/** + * @return string|false returns the id of the inserted column or false on error + */ +function insertSpeedtestUser($ip, $ispinfo, $extra, $ua, $lang, $dl, $ul, $ping, $jitter, $log) +{ + $pdo = getPdo(); + if (!($pdo instanceof PDO)) { + return false; + } + + try { + $stmt = $pdo->prepare( + 'INSERT INTO speedtest_users + (ip,ispinfo,extra,ua,lang,dl,ul,ping,jitter,log) + VALUES (?,?,?,?,?,?,?,?,?,?)' + ); + $stmt->execute([ + $ip, $ispinfo, $extra, $ua, $lang, $dl, $ul, $ping, $jitter, $log + ]); + $id = $pdo->lastInsertId(); + } catch (Exception $e) { + return false; + } + + if (isObfuscationEnabled()) { + return obfuscateId($id); + } + + return $id; +} + +/** + * @param int|string $id + * + * @return array|null|false returns the speedtest data as array, null + * if no data is found for the given id or + * false if there was an error + * + * @throws RuntimeException + */ +function getSpeedtestUserById($id) +{ + $pdo = getPdo(); + if (!($pdo instanceof PDO)) { + return false; + } + + if (isObfuscationEnabled()) { + $id = deobfuscateId($id); + } + + try { + $stmt = $pdo->prepare( + 'SELECT + id, timestamp, ip, ispinfo, ua, lang, dl, ul, ping, jitter, log, extra + FROM speedtest_users + WHERE id = :id' + ); + $stmt->bindValue(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + } catch (Exception $e) { + return false; + } + + if (!is_array($row)) { + return null; + } + + $row['id_formatted'] = $row['id']; + if (isObfuscationEnabled()) { + $row['id_formatted'] = obfuscateId($row['id']).' (deobfuscated: '.$row['id'].')'; + } + + return $row; +} + +/** + * @return array|false + */ +function getLatestSpeedtestUsers() +{ + $pdo = getPdo(); + if (!($pdo instanceof PDO)) { + return false; + } + + try { + $stmt = $pdo->query( + 'SELECT + id, timestamp, ip, ispinfo, ua, lang, dl, ul, ping, jitter, log, extra + FROM speedtest_users + ORDER BY timestamp DESC + LIMIT 100' + ); + + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($rows as $i => $row) { + $rows[$i]['id_formatted'] = $row['id']; + if (isObfuscationEnabled()) { + $rows[$i]['id_formatted'] = obfuscateId($row['id']).' (deobfuscated: '.$row['id'].')'; + } + } + } catch (Exception $e) { + return false; + } + + return $rows; +} diff --git a/results/telemetry_settings.php b/results/telemetry_settings.php index ad80cf3..78bc343 100644 --- a/results/telemetry_settings.php +++ b/results/telemetry_settings.php @@ -1,26 +1,26 @@ \ No newline at end of file +$PostgreSql_username = 'USERNAME'; +$PostgreSql_password = 'PASSWORD'; +$PostgreSql_hostname = 'DB_HOSTNAME'; +$PostgreSql_databasename = 'DB_NAME';