diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index d9a8e0b..66e3a9c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,35 @@ -# ip.zuim.de +# [ip.zuim.de](https://ip.zuim.de) -Shows information on connectivity of a user, by testing both IPv4 and IPv6. Also shows geoip data. \ No newline at end of file +Shows information on connectivity of a user, by testing both IPv4 and IPv6. Also shows geoip data. + +### Warning: +This code is mostly unmaintained and uses several old javascript libraries. + +### Setup: + +To get the geoip data, the geoip2 module for nginx is used with the following configuration: +``` +geoip2 /var/lib/GeoIP/GeoLite2-City.mmdb { + auto_reload 1h; + $geoip2_data_continent_code continent code; + $geoip2_data_continent_name continent names en; + $geoip2_data_country_code country iso_code; + $geoip2_data_country_name country names en; + $geoip2_data_subdivision subdivisions 0 names en; + $geoip2_data_city_name city names en; + $geoip2_data_postal_code postal code; + $geoip2_data_location_time location time_zone; +} + +geoip2 /var/lib/GeoIP/GeoLite2-ASN.mmdb { + auto_reload 1h; + $geoip2_data_asn autonomous_system_organization; +} +``` + +Download these files directly and add them in the main folder: +- https://ip.zuim.de/0B.zip +- https://ip.zuim.de/1MB.zip +- https://ip.zuim.de/10MB.zip +- https://ip.zuim.de/100MB.zip +- https://ip.zuim.de/img.jpg?t=1721317515702 diff --git a/include/advip.php b/include/advip.php new file mode 100644 index 0000000..d95316a --- /dev/null +++ b/include/advip.php @@ -0,0 +1,10 @@ + diff --git a/include/config.php b/include/config.php new file mode 100644 index 0000000..67f08af --- /dev/null +++ b/include/config.php @@ -0,0 +1,10 @@ + "89.58.44.51", + "ipv6" => "2a03:4000:67:e03::1", + "nameDS" => "ip.zuim.de", + "namev4" => "v4.ip.zuim.de", # only use an A record for this domain + "namev6" => "v6.ip.zuim.de", # only use an AAAA record for this domain + "app_path" => "/", +); +?> diff --git a/include/ipinfo.php b/include/ipinfo.php new file mode 100644 index 0000000..f91476a --- /dev/null +++ b/include/ipinfo.php @@ -0,0 +1,52 @@ + diff --git a/index.php b/index.php new file mode 100755 index 0000000..58c9950 --- /dev/null +++ b/index.php @@ -0,0 +1,135 @@ + + + + + + Netzwerktest (<?php echo $ip["name"]; ?>) + + + + + + + + + + + +
+ +

"> + - Testseite +

+ + +
+
+
Warte auf IPv4
+
+
Warte auf IPv6
+ +
+
+

Informationen & Downloads

+
+ IPv4 ausgewählt, um sich zu verbinden!
"; + } + else + { + echo "Dein Browser hat IPv6 ausgewählt, um sich zu verbinden!
"; + } + } + else { + echo "Wenn diese Seite angezeigt wird unterstützt der Client ".$ip["name"]."!
"; + } + + if(isset($_SERVER['HTTPS']) && "on" == $_SERVER['HTTPS']) + { + echo "Du bist mit https verbunden."; + } + else + { + echo "Du bist mit http verbunden."; + } + ?> +

Download mit : + 1MB Download, + 10MB Download, + 100MB Download

+ +

Weitere Informationen: + /raw/" target="_blank">Nur IP Adresse als Text,   + Server Statusseite,   + zuim.de Hauptseite

+
+ +

+ Rufe diese Seite direkt über + "> IPV4 + " class="secondary_link">(Ohne DNS)   + ">Dualstack   + "> IPV6 + " class="secondary_link">(Ohne DNS) + auf.

+ + +
+

Ping mit Graph messen:

+ +
+ +
+

Andere Tests für Geschwindigkeit & Ping:

+ Speedtest mit ajax   + Speedtest mit websocket   + Pingtest mit websocket + LibreSpeed Instanz +
+
+

Bild(14MB) parallel mit IPv4 und IPv6 laden:

+
+
+

IPv4

+ v4 Test +

+
+
+

IPv6

+ v6 Test +

+
+
+

+
+
+
+ + + + + + + + + + + + + + + + diff --git a/raw/index.php b/raw/index.php new file mode 100755 index 0000000..5aae546 --- /dev/null +++ b/raw/index.php @@ -0,0 +1,3 @@ + diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..a72c3df --- /dev/null +++ b/robots.txt @@ -0,0 +1,7 @@ +User-agent: * +Disallow: 100KB.zip +Disallow: 500KB.zip +Disallow: 1MB.zip +Disallow: 10MB.zip +Disallow: 100MB.zip +Disallow: 1GB.zip diff --git a/script.js b/script.js new file mode 100644 index 0000000..0e9aa22 --- /dev/null +++ b/script.js @@ -0,0 +1,509 @@ +function copyBtn(className, btn){ + var ele = $("."+className); + highlight(ele[0]); + document.execCommand('copy'); + $(btn).css("backgroundColor","#0c0").delay(500).queue(function() { + $(btn).css("backgroundColor",""); + $(btn).dequeue(); + }); + console.log("copied: "+ele.html()); +} +function highlight(ele){ + var range, selection; + if (document.body.createTextRange) + { + range = document.body.createTextRange(); + range.moveToElementText(ele); + range.select(); + } else if (window.getSelection) + { + selection = window.getSelection(); + range = document.createRange(); + range.selectNodeContents(ele); + selection.removeAllRanges(); + selection.addRange(range); + } +} + + + +$(document).ready(function() +{ + var v4Div = $(".ip_v4"); + var v6Div = $(".ip_v6"); + var dsDiv = $(".ip_ds"); + + var testingResult = {}; + + function getIpInfo(url, div) + { + var infoDivIp = div.children(".info_ip"); + var infoDivGeo = div.children(".info_geo"); + var version = url.includes("4")?4:6 + + infoDivGeo.hide(); + + + function getIpInfoDone(data) { + var className = "ip_"+url.replace(/\./g,""); + testingResult[version] = true; + if(testingResult[4] && testingResult[6]) { + $("#resultExplanation").html("Die Verbindung konnte über IPv4 und IPv6 hergestellt werden, also wird Dual-Stack unterstützt! Dies kann nativ, durch DS-Lite oder einen 6to4/4to6 Tunnel umgesetzt sein.

"); + } + + div.addClass("success"); + infoDivIp.html("

Deine "+data.name+" Adresse: "+ + ""+data.ip+" "+ + "

"); + if(data.proxyIp != ""){ + infoDivIp.append("

Vom Proxy angegebene Ip: "+data.proxyIp+" (HTTP_X_FORWARDED_FOR)

"); + } + //if(data.clientIp != ""){ + // infoDivIp.append("

Vom Client angegebene Ip: "+data.clientIp+" (HTTP_CLIENT_IP)

"); + //} + + for(var ele in data.geoIp) + { + if(data.geoIp[ele] != ""){ + infoDivGeo.append(""+ele+":"+data.geoIp[ele]+"
"); + } + } + infoDivGeo.fadeIn(); + + //start hostname request + $.ajax("//"+url+"/include/advip.php", {dataType: "json", timeout: 5000}) + .done(function(data, status, xhr){ + for(var ele in data) + { + if(data[ele] != ""){ + infoDivGeo.prepend(""+ele+":"+data[ele]+"
"); + } + } + + }).fail(function(){ + //fail geoip silently + }); + } + + + if(ipInfo_php.conHostType==(div==v4Div?0:(div==v6Div?2:1))) { + console.log("Loaded over PHP: "+ipInfo_php.name); + getIpInfoDone(ipInfo_php); + } + else { + $.ajax("//"+url+"/include/ipinfo.php", {dataType: "json", timeout: 5000}) + .done(function(data, status, xhr){ + console.log("Loaded over Ajax: "+data.name); + getIpInfoDone(data); + }).fail(function(err){ + testingResult[className] = false; + console.log(err); + div.addClass("failure"); + infoDivIp.html("IPv"+version+" nicht verfügbar."); + + var ipWithoutDNS; + var name; + if(version == 4){ + ipWithoutDNS = config["ipv4"]; + } + else { + ipWithoutDNS = "["+config["ipv6"]+"]"; + } + infoDivIp.append("
Versuche eine IPv"+version+" Adresse direkt aufzurufen, um auf DNS Fehler zu prüfen:"+ + ""+ipWithoutDNS+""); + }); + } + } + + + getIpInfo(config.namev4, v4Div); + getIpInfo(config.namev6, v6Div); + //getIpInfo(config.nameDS, dsDiv); + + var pingduration; + var pingcount; + var pingmax = 3000; + var pingInterval; + + $(".ping").click(function() + { + $(".ping").prop('disabled', true); + + pingduration = 0; + pingcount = 0; + + ping(); + + setTimeout(function() + { + if(pingcount != -1) + { + $(".pingresult").html("Keine Antwort nach 5 Sekunden!"); + } + + $(".ping").prop('disabled', false); + },5000); + }); + + function ping() + { + var startTime = new Date().getTime(); + var req = new XMLHttpRequest(); +/* + 0 UNSENT open()wurde noch nicht aufgerufen. + 1 OPENED send()wurde noch nicht aufgerufen. + 2 HEADERS_RECEIVED send() wurde aufgerufen, und Headers sowie Status sind verfügbar. + 3 LOADING Download ist im Gange; responseText enthält bereits unvollständige Daten. + 4 DONE Der Vorgang ist abgeschlossen.*/ + req.onreadystatechange = function() + { + + //console.log(req.readyState+" "+(new Date().getTime() - startTime)); + if(req.readyState == 2 && pingcount >= 0) + { + var duration = new Date().getTime() - startTime; + + pingduration += duration; + pingcount++; + + $(".pingresult").html("Anzahl der Pings: "+pingcount+"
Zeit: "+Math.round(pingduration/pingcount * 10)/10 + "ms"); + + if(pingduration > pingmax) + { + $(".pingresult").append("
Fertig!"); + pingcount = -1; + clearInterval(pingInterval); + } + else + { + ping(); + } + } + }; + req.open("GET", "/0B.zip?"+startTime); + req.send(); + } + + $(".speed").click(function() + { + var v4 = -1; + var v6 = -1; + var v4Speed = -1; + var v6Speed = -1; + var timeCount = 15; + + $(this).hide(); + $(".images").show(); + time = new Date().getTime(); + $(".v4 img").attr("src","//"+config.namev4+"/img.jpg?t="+time); + $(".v6 img").attr("src","//"+config.namev6+"/img.jpg?t="+time); + $(".v4 img, .v6 img").on("load", function() + { + dauer = (new Date().getTime()-time)/1000; + speed = Math.round((14*8)/dauer*100)/100; + + $(this).next().html(speed + " Mbit/s für "+dauer + " Sekunden "); + + //wert setzen + if($(this).parent().hasClass("v4")) + { + v4 = dauer; + v4Speed = speed; + } + if($(this).parent().hasClass("v6")) + { + v6 = dauer; + v6Speed = speed; + } + + //fertig + fertig(); + }); + + + var intervall = setInterval(function(){timer()},100); + + function timer() + { + //count + timeCount = (timeCount*10-1)/10; + + //ausgabe + $(".ausg").html("Timeout: "+timeCount); + + fertig(); + } + + function fertig() + { + if(timeCount <= 0 || v4 != -1 && v6 != -1) + { + clearInterval(intervall); + ausg = ""; + win = -1; + loose = -1; + if(v4 == -1 && v6 == -1) + { + ausg = "Beide haben das laden nach Beenden des Timeout nicht abgeschlossen!"; + win = 0; + loose = 0; + } + else if(v4 == -1) + { + ausg = "Nur IPv6 hat mit "+v6+" Sekunden funktioniert!"; + win = 6; + loose = 4; + } + else if(v6 == -1) + { + ausg = "Nur IPv4 hat mit "+v4+" Sekunden funktioniert!"; + win = 4; + loose = 6; + } + else if(v4 < v6) + { + ausg = "IPv4 war " + Math.round((v6/v4)*100)/100 + " mal schneller als IPv6"; + win = 4; + loose = 6; + } + else if(v6 <= v4) + { + ausg = "IPv6 war " + Math.round((v4/v6)*100)/100 + " mal schneller als IPv4"; + win = 6; + loose = 4; + } + + $(".ausg").html("

"+ausg+"

"); + + $(".v"+win).css("border", "5px solid #0a0"); + $(".v"+loose).css("border", "5px solid #a00"); + } + } + }); + + var active = false; + + var id = -1; //ignore first measurement + var times4 = []; + var times6 = []; + var labels = []; + + var startT; + + $(".start").click(function() { + active = !active; + + if(active) { + $(".start").html("Stop"); + loadChart(); + + s4 = io('wss://'+config.namev4+'/', {path: '/speedtest'}); + s6 = io('wss://'+config.namev6+'/', {path: '/speedtest'}); + + s4.on('pongDown', function(msg) { + times4[msg] = new Date().getTime() - times4[msg]; + //console.log("Received via v4: " + msg + " Time:" + times4[msg]); + updateChart(msg, false); + }); + s6.on('pongDown', function(msg) { + times6[msg] = new Date().getTime() - times6[msg]; + //console.log("Received via v6: " + msg + " Time:" + times6[msg]); + updateChart(msg, true); + }); + + $(".out").html("Starte websocket
Wenn dies nicht funktioniert ist der NodeJS Server offline!"); + $(".ct-chart").show(); + $("html, body").scrollTop($(".ct-chart").offset().top); + } + else { + times4 = []; + times6 = []; + labels = []; + nTimes4 = []; + nTimes6 = []; + nLabels = []; + $(".start").html("Start"); + } + + }); + + setInterval(function() { + if(active) { + + if(id >= 0) { + times4[id] = new Date().getTime(); + times6[id] = new Date().getTime(); + labels[id] = ""+(id/5); + } + s4.emit('pingUp', id); + s6.emit('pingUp', id); + + id++; + } + }, 200); + + function calcAvg() + { + var n4 = 0; + var avgAct4 = 0; + for(var i = 0; i < nTimes4.length; i++) + { + if(nTimes4[i]) { + avgAct4 += nTimes4[i]; + n4++; + } + } + avgAct4 /= n4; + + + var n6 = 0; + var avgAct6 = 0; + for(var i = 0; i < nTimes6.length; i++) + { + if(nTimes6[i]) { + avgAct6 += nTimes6[i]; + n6++; + } + } + avgAct6 /= n6; + + $(".out").html("Ping Durchschnitt: IPv4: "+avgAct4.toFixed(1)+" ms   /   IPv6: "+avgAct6.toFixed(1)+" ms (Anfragen: "+n4+")"); + } + + + + var nTimes4 = []; + var nTimes6 = []; + var nLabels = []; + var chart; + function loadChart() + { + //trim data + /*var n = 50; + var start = 0>times4.length-n?0:times4.length-n; + + for(var i = start; i < times4.length; i++) + { + if(times4[i] > 100*1000) + break; + + nTimes4[i-start] = times4[i]; + nLabels[i-start] = labels[i]; + } + + var n = 50; + var start = 0>times6.length-n?0:times6.length-n; + + for(var i = start; i < times6.length; i++) + { + if(times6[i] > 100*1000) + break; + + nTimes6[i-start] = times6[i]; + nLabels[i-start] = labels[i]; + } + */ + + calcAvg(nTimes4); + calcAvg(nTimes6); + + //draw chart + var options = + { + low: 0, + + axisX: + { + labelInterpolationFnc: function skipLabels(value, index) + { + return index % 10 === 0 ? value : null; + } + }, + + chartPadding: + { + top: 50, + right: 5, + bottom: 20, + left: 25 + }, + + plugins: [ + Chartist.plugins.legend({ + clickable: false, + legendNames: ['IPv4 Ping', 'IPv6 Ping'] + }), + Chartist.plugins.ctAxisTitle( + { + axisX: { + axisTitle: 'Zeit in Sekunden', + axisClass: 'ct-axis-title', + offset: { + x: 0, + y: 35 + }, + textAnchor: 'middle' + }, + axisY: { + axisTitle: 'Ping in ms', + axisClass: 'ct-axis-title', + offset: { + x: 0, + y: -10 + }, + textAnchor: 'middle', + flipTitle: false + } + }) + ] + } + + chart = new Chartist.Line('.ct-chart', {labels: nLabels, series: [nTimes4,nTimes6]}, options); + console.log(chart); + } + + var idToIndex = []; + function updateChart(id, v6) { + if(id < 0) { + return; + } + + var limit = 50; + + if(nLabels.length >= limit) { + nLabels.shift(); + } + if(nTimes4.length >= limit) { + nTimes4.shift(); + } + if(nTimes6.length >= limit) { + nTimes6.shift(); + } + + if(!nLabels.includes(labels[id])) { + nLabels.push(labels[id]); + + //set other value to null while waiting + if(v6) { + nTimes6.push(times6[id]); + nTimes4.push(null); + } else { + nTimes4.push(times4[id]); + nTimes6.push(null); + } + } + else{ + //replace previously inserted null value + if(v6) { + nTimes6[nTimes6.length-1] = times6[id]; + } else { + nTimes4[nTimes4.length-1] = times4[id]; + } + } + + calcAvg(nTimes4); + calcAvg(nTimes6); + + chart.update({labels: nLabels, series: [nTimes4,nTimes6]}); + } +}); diff --git a/style.css b/style.css new file mode 100644 index 0000000..14efa87 --- /dev/null +++ b/style.css @@ -0,0 +1,265 @@ +* +{ + font-family: Arial; +} +h4,h3,h5,h6 +{ + margin: 5px 0; +} +.v4, .v6 +{ + float:left; + width: 45%; + border: 3px solid black; + border-radius:10px; + margin: 5px; + padding: 0; + overflow:hidden; +} +.v4 p, .v4 h3, .v6 p, .v6 h3 +{ + margin: 0 5px; +} +.images +{ + display:none; +} +.testimg +{ + height: 400px; +} + +html,body +{ + margin: 0; + padding: 0; + background-color: #1e1e1e; +} + +/*Hauptcontainer*/ +.cont +{ + position: absolute; + left: 0; + right:0; + width: 100%; + max-width: 1000px; + min-height: 100%; + margin: -10px auto -10px auto; + background: #f0f0f0; + border: 1px solid #999; + border-width: 0 1px; + overflow-x: hidden; + overflow-y: visible; + padding: 10px 10px 0; + +} +.cont h1 +{ + background: #4B87E7; + width: 200%; + margin-left: -10px; + padding: 10px; + color: #e7e7e7; +} +.headCont +{ + padding: unset; +} +.headCont a +{ + margin:3px; + padding: 5px; +} +hr +{ + width: 200%; + margin: 10px 0 10px -30px; + height: 3px; + background: rgba(0,0,0,0.4); + border: none; + box-shadow: none; +} +span.info +{ + display:inline-block; + font-weight:bold; + width:105px; +} +a +{ + text-decoration: none; + color: #4B87E7; + border-radius: 3px; + padding: 0 2px; + transition: all 0.2s; +} +a.title +{ + color: #eaeaea; +} +a:hover +{ + background: #4B87E7; + color: #f5f5f5; +} +a.title:hover +{ + color: #111; +} +.secondary_link +{ + font-size:70%; + vertical-align:top; +} + +.ipAddr { + font-weight:normal; + cursor:pointer; +} +.ipTest{ + margin: -10px; +} +.ipTest > div{ + padding: 10px; + transition: all 0.4s; + background-color: rgba(0, 0, 0, 0); +} +.ipTest .success { + background-color: rgb(220, 255, 160); +} +.ipTest .failure { + background-color: rgba(255, 100, 136, 0.46); +} +.info_geo{ + transition: all 0.3s; + padding: 7px; + margin: -7px; + border-radius: 10px; + display: inline-block; +} +.info_geo:hover{ + background: rgba(255,255,255,0.8); + box-shadow: inset 0 0 10px black; +} + + +/* loading icon */ +.lds-ellipsis { + margin-left: 20px; + display: inline-block; + position: relative; + width: 40px; + height: 11px; +} +.lds-ellipsis div { + position: absolute; + width: 13px; + height: 13px; + border-radius: 50%; + background: #fff; + animation-timing-function: cubic-bezier(0, 1, 1, 0); + box-shadow: 0 0 5px black; +} +.lds-ellipsis div:nth-child(1) { + left: 8px; + animation: lds-ellipsis1 0.6s infinite; +} +.lds-ellipsis div:nth-child(2) { + left: 8px; + animation: lds-ellipsis2 0.6s infinite; +} +.lds-ellipsis div:nth-child(3) { + left: 32px; + animation: lds-ellipsis2 0.6s infinite; +} +.lds-ellipsis div:nth-child(4) { + left: 56px; + animation: lds-ellipsis3 0.6s infinite; +} +@keyframes lds-ellipsis1 { + 0% { + transform: scale(0); + } + 100% { + transform: scale(1); + } +} +@keyframes lds-ellipsis3 { + 0% { + transform: scale(1); + } + 100% { + transform: scale(0); + } +} +@keyframes lds-ellipsis2 { + 0% { + transform: translate(0, 0); + } + 100% { + transform: translate(24px, 0); + } +} + +/*chart*/ + +/*v4*/ +.ct-series-a .ct-line, +.ct-legend .ct-series-0:before { + stroke: #363880 !important; + background-color: #363880 !important; + border-color: #363880 !important; +} +.ct-series-a .ct-point +{ + fill: #363880 !important; + stroke: #363880 !important; +} +/*v6*/ +.ct-series-b .ct-line, +.ct-legend .ct-series-1:before { + stroke: #FA9100 !important; + background-color: #FA9100 !important; + border-color: #FA9100 !important; +} +.ct-series-b .ct-point +{ + fill: #FA9100 !important; + stroke: #FA9100 !important; +} + + +/*Legende*/ +.ct-legend { + background: rgba(255,255,255,0.7); + padding: 3px; + border-radius: 5px; + position: absolute; + right:10px; + top: 10px; + z-index: 10; + list-style: none; +} +.ct-legend li { + position: relative; + padding-left: 23px; + margin-bottom: 3px; +} +.ct-legend li:before { + width: 12px; + height: 12px; + position: absolute; + left: 0; + content: ''; + border: 3px solid transparent; + border-radius: 2px; +} +.ct-legend li.inactive:before { + background: transparent; +} +.ct-legend.ct-legend-inside { + position: absolute; + top: 0; + right: 0; +}