199199 .d-nav {
200200 display : flex; flex-wrap : wrap; gap : 16px 26px ; margin-bottom : 10px ;
201201 border-bottom : 1px solid # 222 ; padding-bottom : 10px ;
202+ visibility : hidden;
202203 }
203204 .d-nav-item {
204205 color : var (--ui-grey-dim ); font-size : 1.6em ; cursor : pointer;
480481 # m-viewport ::-webkit-scrollbar { display : none !important ; }
481482
482483 .m-footer { flex : 0 0 auto; background : # 0a0a0a ; border-top : 1px solid # 222 ; padding-bottom : env (safe-area-inset-bottom); }
483- .m-dots { display : flex; justify-content : center; gap : 15px ; padding : 12px 0 ; }
484+ .m-dots { display : flex; justify-content : center; gap : 15px ; padding : 12px 0 ; visibility : hidden; }
484485 .dot { width : 10px ; height : 10px ; border-radius : 50% ; background : # 222 ; transition : 0.3s ; cursor : pointer; }
485486 .dot .active { background : var (--neon-green ); transform : scale (1.4 ); box-shadow : 0 0 8px var (--neon-green ); }
486487
487488 .m-nav-container { position : relative; border-top : 2px solid var (--neon-green ); background : # 000 ; }
488489 .m-nav-container ::before , .m-nav-container ::after { content : '' ; position : absolute; top : 0 ; bottom : 0 ; width : 30px ; z-index : 10 ; pointer-events : none; }
489490 .m-nav-container ::before { left : 0 ; background : linear-gradient (to right, # 000 0% , transparent 100% ); }
490491 .m-nav-container ::after { right : 0 ; background : linear-gradient (to left, # 000 0% , transparent 100% ); }
491- .m-nav { display : flex; gap : 40px ; padding : 15px 30px ; overflow-x : auto; scrollbar-width : none; -ms-overflow-style : none; }
492+ .m-nav { display : flex; gap : 40px ; padding : 15px 30px ; overflow-x : auto; scrollbar-width : none; -ms-overflow-style : none; visibility : hidden; }
492493 .m-nav ::-webkit-scrollbar { display : none; }
493494 .nav-item { color : var (--ui-grey-dim ); font-size : 1.8em ; display : flex; flex-direction : column; align-items : center; cursor : pointer; transition : 0.2s ; flex-shrink : 0 ; }
494495 .nav-item span { font-size : 0.45em ; margin-top : 4px ; font-weight : bold; }
@@ -624,12 +625,9 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
624625 < div class ="d-box " id ="d-box-trivia "> < h2 style ="display:flex;justify-content:space-between;align-items:center; "> TRIVIA< span class ="proto-cycle-btn desktop-readonly " style ="vertical-align:initial; "> ALL</ span > </ h2 > < div id ="d-trivia " class ="d-content "> </ div > </ div >
625626 < div class ="d-box " id ="d-box-jokes "> < h2 > KNOCK-KNOCK JOKES</ h2 > < div id ="d-jokes " class ="d-content " style ="color:var(--neon-green); line-height:1.6; "> </ div > </ div >
626627 < div class ="d-box "> < h2 > ABOUT</ h2 > < div class ="d-content " style ="color:var(--neon-green); line-height:1.6; ">
627- < div style ="font-size:1.4em; font-weight:bold; margin-bottom:20px; "> KNOCK-KNOCK.NET</ div >
628- < p > Set up an unprotected server on the net, and the bots start swarming!</ p > < p > This site shows bots attempting (unsuccessfully) to break into an ordinary internet server.</ p >
629- < p > This constant chatter of bots knocking on the doors of machines on the net has been referred to as < em > "the background radiation of the Internet".</ em > </ p >
630- < p > Knock-knock.net is a visualization of this bot traffic.
631- It shows the bot activity in real-time, and provides historic stats of the bot attacks over time: where they are coming from, the most common usernames and passwords attempted, the worst offending ISPs, and in some cases, why the password or username was chosen.</ p >
632- < p > Have fun! Send questions or comments to:< br > < a class ="contact-email " href ="# " style ="color:var(--data-blue) "> </ a > </ p >
628+ < p > Set up an unprotected server on the net, and the bots start swarming! This site shows their break-in attempts in real-time: where they come from, what credentials they try, and why.</ p >
629+ < p > Questions and comments to: < a class ="contact-email " href ="# " style ="color:var(--data-blue) "> </ a > </ p >
630+ < div id ="d-about-server-info " style ="color:var(--ui-grey-dim); font-size:0.9em; margin-top:15px; margin-bottom:15px; "> </ div >
633631 < div style ="text-align:center; margin-top:30px; ">
634632 < a href ="https://www.abuseipdb.com/user/270949 " title ="AbuseIPDB is an IP address blacklist for webmasters and sysadmins to report IP addresses engaging in abusive behavior on their networks " target ="_blank " rel ="noopener ">
635633 < img src ="https://www.abuseipdb.com/contributor/270949.svg " alt ="AbuseIPDB Contributor Badge " style ="width:200px; border-radius:5px; border:1px solid #333; padding:8px; background:var(--data-blue); box-shadow:0 0 8px rgba(0,251,255,0.3); ">
@@ -672,12 +670,9 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
672670 < div class ="m-pane " id ="m-pane-trivia "> < h2 style ="display:flex;justify-content:space-between;align-items:center; "> TRIVIA< span class ="proto-cycle-btn " onclick ="cycleProto(event) " style ="vertical-align:initial; "> ⟳ ALL</ span > </ h2 > < div id ="m-trivia " class ="m-content "> </ div > </ div >
673671 < div class ="m-pane " id ="m-pane-jokes "> < h2 > KNOCK-KNOCK JOKES</ h2 > < div id ="m-jokes " class ="m-content " style ="color:var(--neon-green); line-height:1.6; "> </ div > </ div >
674672 < div class ="m-pane " id ="m-pane-about "> < h2 > ABOUT</ h2 > < div class ="m-content " style ="color:var(--neon-green); line-height:1.6; ">
675- < div style ="font-size:1.4em; font-weight:bold; margin-bottom:20px; "> KNOCK-KNOCK.NET</ div >
676- < p > Set up an unprotected server on the net, and the bots start swarming!</ p > < p > This site shows bots attempting (unsuccessfully) to break into an ordinary internet server.</ p >
677- < p > This constant chatter of bots knocking on the doors of machines on the net has been referred to as < em > "the background radiation of the Internet".</ em > </ p >
678- < p > Knock-knock.net is a visualization of this bot traffic.
679- It shows the bot activity in real-time, and provides historic stats of the bot attacks over time: where they are coming from, the most common usernames and passwords attempted, the worst offending ISPs, and in some cases, why the password or username was chosen.</ p >
680- < p > Have fun! Send questions or comments to:< br > < a class ="contact-email " href ="# " style ="color:var(--data-blue) "> </ a > </ p >
673+ < p > Set up an unprotected server on the net, and the bots start swarming! This site shows their break-in attempts in real-time: where they come from, what credentials they try, and why.</ p >
674+ < p > Questions and comments to: < a class ="contact-email " href ="# " style ="color:var(--data-blue) "> </ a > </ p >
675+ < div id ="m-about-server-info " style ="color:var(--ui-grey-dim); font-size:0.9em; margin-top:15px; margin-bottom:15px; "> </ div >
681676 < div style ="text-align:center; margin-top:30px; ">
682677 < a href ="https://www.abuseipdb.com/user/270949 " title ="AbuseIPDB is an IP address blacklist for webmasters and sysadmins to report IP addresses engaging in abusive behavior on their networks " target ="_blank " rel ="noopener ">
683678 < img src ="https://www.abuseipdb.com/contributor/270949.svg " alt ="AbuseIPDB Contributor Badge " style ="width:275px; border-radius:5px; border:1px solid #333; padding:8px; background:var(--data-blue); box-shadow:0 0 8px rgba(0,251,255,0.3); ">
@@ -891,15 +886,58 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
891886 let showPasswordPanel = true ;
892887 const lastKnockTimeByProto = { } ;
893888
889+ function formatUptimeCompact ( minutes ) {
890+ const m = Math . floor ( minutes ) ;
891+ if ( m < 60 ) return `${ m } m` ;
892+ const d = Math . floor ( m / 1440 ) ;
893+ const h = Math . floor ( ( m % 1440 ) / 60 ) ;
894+ const rm = m % 60 ;
895+ return ( d > 0 ? `${ d } d ` : '' ) + ( h > 0 ? `${ h } h ` : '' ) + `${ rm } m` ;
896+ }
897+
898+ function colorProtoList ( protocols ) {
899+ return protocols . map ( p => {
900+ const c = PROTO_COLORS [ p ] || PROTO_COLORS . ALL ;
901+ return `<span style="color:${ c } ">${ escapeHtml ( p ) } </span>` ;
902+ } ) . join ( '<span style="color:var(--neon-green)">, </span>' ) ;
903+ }
904+
905+ function updateAboutServerInfo ( ) {
906+ const L = 'color:var(--ui-label)' ;
907+ const G = 'color:var(--neon-green)' ;
908+ const LW = 'display:inline-block;width:6em;flex-shrink:0;' ;
909+ const ROW = 'display:flex;' ;
910+ const lines = [ ] ;
911+ lines . push ( `<div style="${ ROW } "><span style="${ L } ;${ LW } ">Uptime:</span><span style="${ G } ">${ formatUptimeCompact ( uptimeMinutesCache ) } </span></div>` ) ;
912+ lines . push ( `<div style="${ ROW } "><span style="${ L } ;${ LW } ">Monitoring:</span><span style="${ G } ">${ TRACKED_PROTOCOLS . map ( escapeHtml ) . join ( ', ' ) } </span></div>` ) ;
913+ if ( isFiltered ) {
914+ lines . push ( `<div style="${ ROW } "><span style="${ L } ;${ LW } ">Viewing:</span><span style="${ G } ">${ activeProtocols . map ( escapeHtml ) . join ( ', ' ) } only</span></div>` ) ;
915+ }
916+ const html = lines . join ( '' ) ;
917+ [ 'd-about-server-info' , 'm-about-server-info' ] . forEach ( id => {
918+ const el = document . getElementById ( id ) ;
919+ if ( el ) el . innerHTML = html ;
920+ } ) ;
921+ }
922+
894923 function getFilteredTotal ( ) {
895924 let sum = 0 ;
896925 activeProtocols . forEach ( p => { sum += Number ( protoBreakdownCache ?. [ p ] ?. count || 0 ) ; } ) ;
897926 return sum ;
898927 }
899928
900929 function getFilteredKpm ( ) {
901- const mins = Number ( uptimeMinutesCache ) || 0 ;
902- return mins > 0 ? ( getFilteredTotal ( ) / mins ) . toFixed ( 2 ) : '0.00' ;
930+ if ( activeProtocols . length === TRACKED_PROTOCOLS . length ) {
931+ const mins = Number ( uptimeMinutesCache ) || 0 ;
932+ return mins > 0 ? ( getFilteredTotal ( ) / mins ) . toFixed ( 2 ) : '0.00' ;
933+ }
934+ let totalKpm = 0 ;
935+ activeProtocols . forEach ( p => {
936+ const uptime = Number ( protoBreakdownCache ?. [ p ] ?. uptime || 0 ) ;
937+ const count = Number ( protoBreakdownCache ?. [ p ] ?. count || 0 ) ;
938+ if ( uptime > 0 ) totalKpm += count / uptime ;
939+ } ) ;
940+ return totalKpm . toFixed ( 2 ) ;
903941 }
904942
905943 function getFilteredLastKnockTime ( ) {
@@ -1410,7 +1448,9 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
14101448 if ( totalGlobal !== null && totalGlobal > 0 ) {
14111449 pct = Number ( ( ( count * 100 ) / totalGlobal ) . toFixed ( 2 ) ) ;
14121450 }
1413- out [ proto ] = { count, pct } ;
1451+ const uptimeRaw = Number ( row . uptime ) ;
1452+ const uptime = Number . isFinite ( uptimeRaw ) ? Math . max ( 0 , Math . floor ( uptimeRaw ) ) : 0 ;
1453+ out [ proto ] = { count, pct, uptime } ;
14141454 } ) ;
14151455 return out ;
14161456 }
@@ -1419,27 +1459,32 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
14191459 const proto = ( d ?. proto || '' ) . toUpperCase ( ) ;
14201460 if ( ! TRACKED_PROTOCOLS . includes ( proto ) ) return ;
14211461 if ( ! protoBreakdownCache [ proto ] ) {
1422- protoBreakdownCache [ proto ] = { count : 0 , pct : 0 } ;
1462+ protoBreakdownCache [ proto ] = { count : 0 , pct : 0 , uptime : 0 } ;
14231463 }
14241464 protoBreakdownCache [ proto ] . count += 1 ;
1465+ const protoUptime = Number ( d ?. proto_uptime ) ;
1466+ if ( Number . isFinite ( protoUptime ) && protoUptime > 0 ) {
1467+ protoBreakdownCache [ proto ] . uptime = protoUptime ;
1468+ }
14251469 const total = Number ( d ?. total_global ) ;
14261470 if ( Number . isFinite ( total ) && total > 0 ) {
14271471 TRACKED_PROTOCOLS . forEach ( name => {
14281472 const count = Number ( protoBreakdownCache [ name ] ?. count || 0 ) ;
1473+ const uptime = Number ( protoBreakdownCache [ name ] ?. uptime || 0 ) ;
14291474 protoBreakdownCache [ name ] = {
1430- count,
1475+ count, uptime ,
14311476 pct : Number ( ( ( count * 100 ) / total ) . toFixed ( 2 ) )
14321477 } ;
14331478 } ) ;
14341479 }
14351480 }
14361481
14371482 function renderProtoStats ( ) {
1438- const uptimeMinutes = Number ( uptimeMinutesCache ) || 0 ;
14391483 const rows = TRACKED_PROTOCOLS . map ( proto => {
14401484 const count = Number ( protoBreakdownCache ?. [ proto ] ?. count || 0 ) ;
14411485 const pct = Number ( protoBreakdownCache ?. [ proto ] ?. pct || 0 ) ;
1442- const kpm = uptimeMinutes > 0 ? ( count / uptimeMinutes ) : 0 ;
1486+ const protoUptime = Number ( protoBreakdownCache ?. [ proto ] ?. uptime || 0 ) ;
1487+ const kpm = protoUptime > 0 ? ( count / protoUptime ) : 0 ;
14431488 return { proto, count, pct, kpm } ;
14441489 } )
14451490 . sort ( ( a , b ) => ( b . kpm - a . kpm ) || ( b . count - a . count ) || a . proto . localeCompare ( b . proto ) ) ;
@@ -1458,7 +1503,7 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
14581503 </div>
14591504 ` ) . join ( '' ) ;
14601505
1461- const footer = `<div style="margin-top:8px;color:var(--ui-grey-dim);font-size:0.9em;">Uptime basis: ${ uptimeMinutes . toLocaleString ( ) } min</div>` ;
1506+ const footer = '' ;
14621507 const html = header + body + footer ;
14631508 [ 'd-proto-stats' , 'm-proto-stats' ] . forEach ( id => {
14641509 const el = document . getElementById ( id ) ;
@@ -2460,8 +2505,8 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
24602505 } ,
24612506 {
24622507 name : 'mail_envelope' ,
2463- match : ( d , proto ) => proto === 'MAIL' ,
2464- render : d => renderFromToBlock ( d , 'mail_from ' , 'mail_to ' , true ) ,
2508+ match : ( d , proto ) => proto === 'MAIL' && SMTP_MESSAGE_STAGES . has ( String ( d . smtp_stage || '' ) ) ,
2509+ render : d => renderFromToBlock ( d , 'smtp_mail_from ' , 'smtp_rcpt_to ' , true ) ,
24652510 } ,
24662511 {
24672512 name : 'rdp_username' ,
@@ -2763,14 +2808,14 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
27632808 const d = msg . data ;
27642809 if ( msg . type === 'new_knock' ) {
27652810 knockCount ++ ;
2811+ const knockProto = ( d . proto || 'SSH' ) . toUpperCase ( ) ;
27662812 debugLog ( `Knock #${ knockCount } from ${ d . iso || '??' } ` ) ;
2767- playClick ( ) ;
2813+ if ( activeProtocols . includes ( knockProto ) ) playClick ( ) ;
27682814 if ( showProtoChip ) {
27692815 pulseProtoChip ( d . proto , DESKTOP_LATEST_PROTO_CHIP ) ;
27702816 pulseProtoChip ( d . proto , MOBILE_LATEST_PROTO_CHIP ) ;
27712817 }
27722818 if ( knockMatchesFilter ( d ) ) pulseGlobeAtmosphere ( ) ;
2773- const knockProto = ( d . proto || 'SSH' ) . toUpperCase ( ) ;
27742819 const now = Math . floor ( Date . now ( ) / 1000 ) ;
27752820 lastKnockTime = now ;
27762821 lastKnockTimeByProto [ knockProto ] = now ;
@@ -2796,8 +2841,8 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
27962841 if ( hasUserField ( d ) ) incrementAndSort ( 'user' , d . user ) ;
27972842 incrementAndSort ( 'ip' , d . ip ) ;
27982843 }
2799- refreshStatsAndTrivia ( ) ;
28002844 if ( knockMatchesFilter ( d ) ) {
2845+ refreshStatsAndTrivia ( ) ;
28012846 updateGlobeLocation ( d ) ;
28022847 rotateGlobeToLocation ( d . lat , d . lng ) ;
28032848 }
@@ -2818,16 +2863,11 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
28182863 if ( d . proto_stats ) protoStatsCache = d . proto_stats ;
28192864 protoBreakdownCache = normalizeProtoBreakdown ( d . proto_breakdown || { } , d . total || 0 ) ;
28202865
2821- // Seed per-protocol last knock times from history
2822- const ph = d . proto_histories || { } ;
2866+ // Seed per-protocol last knock times from backend
2867+ const plt = d . proto_last_times || { } ;
28232868 activeProtocols . forEach ( p => {
2824- const first = ( ph [ p . toLowerCase ( ) ] || [ ] ) [ 0 ] ;
2825- if ( first ?. timestamp ) {
2826- const ts = typeof first . timestamp === 'string'
2827- ? Math . floor ( new Date ( first . timestamp ) . getTime ( ) / 1000 )
2828- : Number ( first . timestamp ) ;
2829- if ( Number . isFinite ( ts ) ) lastKnockTimeByProto [ p ] = ts ;
2830- }
2869+ const t = plt [ p . toLowerCase ( ) ] ;
2870+ if ( t ) lastKnockTimeByProto [ p ] = t ;
28312871 } ) ;
28322872
28332873 // Update header stats (filtered or global)
@@ -2849,7 +2889,11 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
28492889 }
28502890 applyProtoButtons ( getCurrentMode ( ) ) ;
28512891 applyProtocolPanelVisibility ( ) ;
2892+ document . querySelector ( '.d-nav' ) . style . visibility = 'visible' ;
2893+ document . querySelector ( '.m-nav' ) . style . visibility = 'visible' ;
2894+ document . querySelector ( '.m-dots' ) . style . visibility = 'visible' ;
28522895 renderProtoStats ( ) ;
2896+ updateAboutServerInfo ( ) ;
28532897 // Refresh Heat extrusion colors/heights without re-setting image or polygon data
28542898 refreshHeatGlobe ( getActiveLeaderboards ( ) . loc ) ;
28552899
@@ -2876,8 +2920,8 @@ <h1>BOTS ARE KNOCKING <span class="status-dot"></span></h1>
28762920 } else {
28772921 hideGlobeDot ( ) ;
28782922 }
2923+ refreshStatsAndTrivia ( ) ;
28792924 }
2880- refreshStatsAndTrivia ( ) ;
28812925 }
28822926 } ;
28832927 }
0 commit comments