volkan-k / BGP.HE.net - Improved

// ==UserScript==
// @name        BGP.HE.net - Improved
// @namespace   BGP_HE_net
// @description BGP.HE.net - Fills Empty IRR tabs by using data from RADB, Keep browser validation cookie, postpone validation for new sessions. Add onclick links to all country flags for easier browsing experience.
// @include     http://bgp.he.net/*
// @include     https://bgp.he.net/*
// @version     3.0
// @grant       GM_xmlhttpRequest
// @grant       GM_addStyle
// @grant       GM_log
// @require 	https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @require 	https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.0.4/js.cookie.min.js
// @require 	https://cdnjs.cloudflare.com/ajax/libs/jquery-searcher/0.3.0/jquery.searcher.min.js
// @require 	https://cdnjs.cloudflare.com/ajax/libs/df-number-format/2.1.6/jquery.number.min.js
// @author		Volkan K.
// @license		MIT
// @run-at 		document-end
// @connect 	irr.bboi.net
// @connect 	bgp.he.net
// @connect 	peeringdb.com
// @connect 	ip-api.com
// ==/UserScript==

this.$ = this.jQuery = jQuery.noConflict(true);

if(typeof GM_addStyle === "undefined") {
	function GM_addStyle(/* String */ styles) {
		var oStyle = document.createElement("style");
		oStyle.setAttribute("type", "text\/css");
		oStyle.appendChild(document.createTextNode(styles));
		var head = document.getElementsByTagName('head')[0];
		if (!head) { // no head yet, stick it whereever
			document.documentElement.appendChild(oStyle);
		} else {
			head.appendChild(oStyle);
		}
	}
}

if(typeof GM_log === "undefined") {
	function GM_log(log) {
		if(console)
			console.log(log);
		else
			alert(log);
	}
}

var debug_internal = 1; // 1=enable debug , 0 =disable debug

function debugLog(message, do_alert) {
	if (debug_internal==1) {
		console.log("USER-SCRIPT BGP_HE_NET | " + message);
	}
	if (do_alert && typeof alert === "function") {
		alert("USER-SCRIPT BGP_HE_NET | " + message);
	}
}

// Browser Validation no-more
current_c_val = Cookies.get('c');
consistent_c = Cookies.set('c', current_c_val, { expires: 365 });

// Empty IRR tab fix
function append_irr_source(title,id) {
	var html_header="<div class='irrbox'><div class='irrsource'>"+title+"</div><span class='irrdata' id='"+id+"'>";
	var html_footer="</span><div class='clear'></div></div>";
	$("div#irr").append(html_header+html_footer);
}

function add_links_to_result(result) {
	result = result.replace(/([ \t]+)AS-([\w-]+)/ig, '$1<a href="/irr/as-set/AS-$2" title="AS-$2">AS-$2</a>'); // Add links for AS-SETs.
	result = result.replace(/([ \t]+)RS-([\w-]+)/ig, '$1<a href="/irr/route-set/RS-$2" title="RS-$2">RS-$2</a>'); // Add links for ROUTE-SETs.
	result = result.replace(/([ \t]+)AS(\d+)/ig, '$1<a href="/AS$2" title="AS$2">AS$2</a>'); // Add links for ASNs.
	result = result.replace(/\b((?:[0-9]{1,3}\.){3}[0-9]{1,3})\b/g, '<a href="/ip/$1" title="$1">$1</a>'); // Add links for IPv4 addresses (simple regex)
	return result;
}

function convert_pdb_arr_to_str (result_arr){
	result = "";
	for (x in result_arr) {
		switch(x){
			case "id":
				key = "Network ID";
				break;
			case "org_id":
				key = "Organization ID";
				break;
			case "name":
				key = "Organization Name";
				break;
			case "aka":
				key = "Also Known As";
				break;
			case "website":
				key = "Company Website";
				break;
			case "asn":
				key = "Primary ASN";
				break;
			case "looking_glass":
				key = "Looking Glass URL";
				break;
			case "route_server":
				key = "Route Server URL";
				break;
			case "irr_as_set":
				key = "IRR Record AS Set";
				break;
			case "info_type":
				key = "Network Type";
				break;
			case "info_prefixes4":
				key = "IPv4 Prefixes";
				break;
			case "info_prefixes6":
				key = "IPv6 Prefixes";
				break;
			case "info_traffic":
				key = "Traffic Levels";
				break;
			case "info_ratio":
				key = "Traffic Ratios";
				break;
			case "info_scope":
				key = "Geographic Scope";
				break;
			case "info_unicast":
				key = "Unicast Support";
				break;
			case "info_multicast":
				key = "Multicast Support";
				break;
			case "info_ipv6":
				key = "IPv6 Support";
				break;
			case "notes":
				key = "Notes";
				break;
			case "policy_url":
				key = "Peering Policy URL";
				break;
			case "policy_general":
				key = "General Policy";
				break;
			case "policy_locations":
				key = "Multiple Locations Policy";
				break;
			case "policy_ratio":
				key = "Ratio Requirement Policy";
				break;
			case "policy_contracts":
				key = "Contract Requirement Policy";
				break;
			case "created":
				key = "Record Created";
				break;
			case "updated":
				key = "Record Updated";
				break;
			case "status":
				key = "Record Status";
				break;
			default:
				key = x;
		}
		result += key+" : " + result_arr[x] + "<br />";
	}
	return result;
}
var myregexp = /AS(\d+)/i;
var match = myregexp.exec(window.location.pathname);
if (match != null) {
	current_as = match[1];
} else {
	current_as = "";
}

var is_ixp_report = false;
var ixp_report_regexp = /report\/exchanges/i;
var ixp_report_match = ixp_report_regexp.exec(window.location.pathname);
if (ixp_report_match != null) {
	is_ixp_report=true;
	//console.log("we should be on IXP report page."); // for debug, comment on production
}

if (is_ixp_report==true) {
	var ixp_count=$("table#exchangestable > tbody > tr").length;
	//console.log("ixp_count="+ixp_count); // for debug, comment on production
	$("table#exchangestable > thead > tr > th:first").append("&nbsp;("+ixp_count+")");
}

if (($("div#irr").children().size() < 1 || $("div.irrsource:contains('RADB')").length<1)&& current_as != "") {
	// no siblings , BGP tool does not have IRR info to display. do your job..
	// Note: you can use AS38861 and AS24482 for testing, these currently dont have IRR record on HE.net BGP tool.
	append_irr_source('RADB', 'radb_place_elem');
GM_xmlhttpRequest({
	method: "GET",
	url: "http://irr.bboi.net/cgi-bin/whois.php?value=AS"+current_as+"&submit=WhoIS",
	onerror: function(oEvent){ alert("Error " + oEvent.target.status + " occurred while receiving the document."); },
	onload: function(response){
		if (response.readyState !== 4 || response.status !== 200) return;
		var response_parsed=$.parseHTML(response.responseText);
		var whois_data = $('pre:contains("whois.radb.net")', response_parsed);
		//alert(whois_data); alert(whois_data.length); // for debug
		if (whois_data.length != 0) {
			result = whois_data.html();
/*
	result ="[Querying whois.radb.net]"+"\n"
+"[whois.radb.net]"+"\n"
+"aut-num:        AS24482"+"\n"
+"as-name:        SGGS-AS-AP"+"\n"
+"descr:          SG.GS"+"\n"
+"country:        SG"+"\n"
+"admin-c:        HC755-AP"+"\n"
+"tech-c:         HC755-AP"+"\n"
+"mnt-by:         MAINT-SG-SGGS"+"\n"
+"changed:        admin@sg.gs 20140128"+"\n"
+"mnt-irt:        IRT-SGGS-SG"+"\n"
+"import:         from AS6777 accept ANY"+"\n"
+"import:         from AS8631 accept AS-MSKROUTESERVER"+"\n"
+"import:         from AS61955 accept AS-COLOCATIONIX"+"\n"
+"export:         to AS6777 action community .= { 6777:6777, 6777:855, 6777:4651, 6777:4761, 6777:6327, 6777:6939, 6777:8529, 6777:9304, 6777:9318, 6777:9329, 6777:9498, 6777:9505, 6777:10026, 6777:20080, 6777:38082, 6777:16276, 6777:45629, 6777:46786 }; announce AS-SGGS"+"\n"
+"export:         to AS8631 announce AS-SGGS"+"\n"
+"export:         to AS61955 announce AS-SGGS"+"\n"
+"source:         APNIC"; // for testing and debugging.
	*/
	result = add_links_to_result(result);
	} else {
			// no match, error
			result = "I couldn't find RADB record :(";
	}
	$("span#radb_place_elem").append(result);
	/*if( $("div#irr").is(":visible") ){ // this is not necessary. for testing-only.
		$("div#irr").hide();
		$("div#irr").show();
	}*/
	}
});

}

if (($("div#irr").children().size() < 1 || $("div.irrsource:contains('PeeringDB')").length<1)&& current_as != "") {
	// no siblings , BGP tool does not have IRR info to display. do your job..
	// Note: you can use AS38861 and AS24482 for testing, these currently dont have IRR record on HE.net BGP tool.
	append_irr_source('PeeringDB', 'pdb_place_elem');
	GM_xmlhttpRequest({
		method: "GET",
		url: "https://www.peeringdb.com/api/net?depth=0&asn="+current_as,
		onerror: function(oEvent){ 
			$("span#pdb_place_elem").append("Error " + oEvent.target.status + " occurred while receiving the PeeringDB record."); 
			return;
		},
		onload: function(response){
			if (response.readyState !== 4 || response.status !== 200){
				$("span#pdb_place_elem").append(response.statusText);
				return;
			}
			// we can parse now
			myObj = JSON.parse(response.responseText);
			if (myObj["data"].length>0) {
				result_arr = myObj["data"][0];
				//console.log(result_arr); // for debugging
				result = convert_pdb_arr_to_str(result_arr);
				result = add_links_to_result(result);
			} else {
				// no match, error
				result = "I couldn't find PeeringDB record :(";
			}
			$("span#pdb_place_elem").append(result);
			/*if( $("div#irr").is(":visible") ){ // this is not necessary. for testing-only.
				$("div#irr").hide();
				$("div#irr").show();
			}*/
		}
	});
}

function fetch_peeringdb_link(current_as) {
	GM_xmlhttpRequest({
			method: "HEAD",
			url: 'https://www.peeringdb.com/asn/'+current_as,
			onerror: function(oEvent){ 
				debugLog("Error " + oEvent.status + " occurred while receiving the document."); 
				prepend_peeringdb_link(current_as,"Error " + oEvent.status);
			},
			onload: function(response){
				if (response.readyState !== 4 || response.status !== 200) {
					debugLog("ERROR: Response status is not OK (200) but "+response.status);
					if (response.status==404){
						prepend_peeringdb_link(current_as,"N/A");
					} else {
						prepend_peeringdb_link(current_as,"Server Error");
					}
				} else {
					// show result
					if (response.statusText=="") {
						prepend_peeringdb_link(current_as,"OK");
					} else {
						prepend_peeringdb_link(current_as,response.statusText);
					}
				}
				// console.log(response); // for debug only.
				debugLog("PeeringDB response.statusText = "+ response.statusText);
			}
		});
}

function prepend_peeringdb_link(current_as,responseText){
	peeringdb_html='<div class="asleft" id="peeringdb">PeeringDB Page:</div>'+"\n"
	+'<div class="asright"><a target="_blank" href="https://www.peeringdb.com/asn/'+current_as+'">https://peeringdb.com/asn/'+current_as+'</a>';
	if (responseText!=null && responseText != "") {
		peeringdb_html+=' ('+responseText+')';
	}
	peeringdb_html+='</div>';
	$( "div.asinfotext" ).prepend(peeringdb_html);
}

// Add PeeringDB.com link
if ($("div#peeringdb").size() < 1 && current_as != "") {
	// good to go.
	fetch_peeringdb_link(current_as);
}

function search_text_node(my_selector,my_text) {
	return $(my_selector)
		.contents()
		.filter(function(){
			return this.nodeType === 3;
		})
		.filter(function(){
			// Only match when contains 'simple string' anywhere in the text
			return this.nodeValue.indexOf(my_text) != -1;
		});
}

// Add v4-only and v6-only counts
if ($("div#v4-v6-only").size() < 1 && current_as != "") {
	// good to go.
	bgp_all = search_text_node('div.asleft', "BGP Peers Observed (all):").eq(-1).text().replace(/[\s,.]/g, '').split(":")[1];
	bgp_v4 = search_text_node('div.asleft', "BGP Peers Observed (v4):").eq(-1).text().replace(/[\s,.]/g, '').split(":")[1];
	bgp_v6 = search_text_node('div.asleft', "BGP Peers Observed (v6):").eq(-1).text().replace(/[\s,.]/g, '').split(":")[1];
	debugLog("bgp_all = "+bgp_all+" , bgp_v4 = "+bgp_v4+" , bgp_v6 = "+bgp_v6); // for debug only.
	if ( $.isNumeric(bgp_all) && $.isNumeric(bgp_v4) && $.isNumeric(bgp_v6) ) {
		// we got values now we calculate and print v4-only and v6-only
		bgp_v4_only = parseInt(bgp_all)-parseInt(bgp_v6);
		bgp_v6_only = parseInt(bgp_all)-parseInt(bgp_v4);
		bgp_dual_stack = parseInt(bgp_all)-parseInt(bgp_v4_only)-parseInt(bgp_v6_only);
		debugLog("bgp_v4_only = "+bgp_v4_only+" , bgp_v6_only = "+bgp_v6_only+" , bgp_dual_stack = "+bgp_dual_stack); // for debug only.
		div_v4_v6_only_html = "<div id='v4-v6-only'><hr />BGP Peers Observed (v4-only): "+$.number( bgp_v4_only,0,'.',',' )+"<br />BGP Peers Observed (v6-only): "+$.number( bgp_v6_only,0,'.',',' )+"<br />BGP Peers Observed (DualStack): "+$.number( bgp_dual_stack,0,'.',',' )+"</div>";
		search_text_node('div.asleft', "BGP Peers Observed (v6):").eq(-1).after(div_v4_v6_only_html);
	}
}

var myregexp2 = /country\/(\w+)/i;
var match2 = myregexp2.exec(window.location.pathname);
if (match2 != null) {
	current_cc = match2[1];
} else {
	current_cc = "";
}

if (current_cc != "") {
	// country reports need to be improved
	$( "ul.tabmenu" ).append('<li id="tab_exchanges" class="tabmenuli">Internet Exchanges</li>');
	$( "div#content" ).append('<div id="exchanges" class="tabdata hidden"></div>');
	GM_xmlhttpRequest({
	method: "GET",
	url: location.protocol+"//bgp.he.net/report/exchanges",
	onerror: function(oEvent){ alert("Error " + oEvent.target.status + " occurred while receiving the document."); },
	onload: function(response){
		/*alert("response.status="+response.status
			+";response.readyState="+response.readyState
			+";response.responseText="+response.responseText); // for debug*/
		if (response.readyState !== 4 || response.status !== 200) return;
		// we can parse now
		var response_parsed=$.parseHTML(response.responseText);
		var exchanges_table = $('table#exchangestable', response_parsed);
		//alert(exchanges_table); alert(exchanges_table.length); // for debug
		if (exchanges_table.length != 0) {
			result = exchanges_table.get(0);
		} else {
			// no match, error
			result = "I couldn't find Internet Exchange records for country :(";
		}
		$("div#exchanges").append(result);
		$("div#exchanges table tbody tr").each(function() {
			if ($( this ).find("td:nth-child(4):contains('"+current_cc+"')").length<1 ) {
				$( this ).remove(); // wrong country code, remove!
			}
		});
		$("div#exchanges table").css('width', '100%');
		if ( document.location.hash ) {
			hash=document.location.hash.substring(1);
			$('.tabmenuselected').removeClass('tabmenuselected'); 
			$('.tabdata').addClass('hidden');
			$('#tab' + hash).addClass('tabmenuselected');
			$('#' + hash.substring(1)).removeClass('hidden');
		}
		sortables_init();
		run_tables_searcher();
	}
});

}

// Use click events for faster load, as it wont alter page code unless you click.
//$('div.flag > img[src*="/flags/"]').click(add_country_report_links);

// Use mouseenter events for fastest browsing, as it wont alter page code unless the mouse enters flag element and prompt is not used.
$('div.flag > img[src*="/flags/"]').mouseenter(add_country_report_links);
GM_addStyle("div.flag > img {cursor: pointer; cursor: hand;}");

$("ul.tabmenu li.tabmenuli").each(function() {
  //alert( $(this).attr('id').substring(3) );
  $(this).wrapInner('<a href="#'+$(this).attr('id').substring(3)+'" style="text-decoration:none;"></a>');
});

function add_country_report_links (event) {
	//alert ("debug1");
	var myregexp = /\/flags\/([\w]{2})\.gif/i;
	var match = myregexp.exec($( this ).attr('src'));
	if (match != null) {
		//alert ("debug2");
		var country_code = match[1].toUpperCase();
		if (/^mouse/i.test(event.type)){ // this should be mouse event, so we do wrap. 
			$( this ).wrap("<a href='/country/" + country_code + "'>");
			$( this ).off("mouseenter mouseleave mousedown mouseup mouseover mouseout mousemove");
		} else { // this should be click() call, so we do redirect.
			var r= confirm ("Do you want to be redirected to country report for "+$( this ).attr('alt')+" ("+country_code+") ?")
			if (r == true) {
				window.location.href = "/country/"+country_code;
			} else {
				$( this ).wrap("<a href='/country/" + country_code + "'>");
				$( this ).off("click");
			}
		}
		return true;
	} else {
		//alert ("debug3");
		var country_code = "";
		return false;
	}
}

function run_tables_searcher () {
$("table.sortable, div#search > table").each(function( index, element ) {
	//console.log( $( this ).text() ); // for debug.
	if($(this).is('[class*="mysearchtable"]')) {
		return ;
	}
	$( this ).before('<label>Search for: <input id="mysearchinput'+index+'" type="text"></label>');
	$( this ).addClass('mysearchtable'+index);
	$(this).searcher({
		itemSelector: "tbody > tr",
		textSelector: "td",
		inputSelector: "#mysearchinput"+index,
		toggle: function(item, containsText) {
			// use a typically jQuery effect instead of simply showing/hiding the item element
			if (containsText)
				$(item).fadeIn();
			else
				$(item).fadeOut();
		}
	});
});
}

run_tables_searcher();

function add_ipv4_to_home () {
	GM_xmlhttpRequest({
	method: "GET",
	url: 'http://ip-api.com/json',
	onerror: function(oEvent){ alert("Error " + oEvent.target.status + " occurred while receiving the document."); },
	onload: function(response){
		/*alert("response.status="+response.status
			+";response.readyState="+response.readyState
			+";response.responseText="+response.responseText); // for debug*/
		if (response.readyState !== 4 || response.status !== 200) return;
		// we can parse now
		var data=JSON.parse(response.responseText);
		//console.log(JSON.stringify(data, null, 2)); //for debugging
		var asn=data.as.split(" ")[0];
		var isp=data.org;
		var ipa=data.query;
		var cc=data.countryCode.toLowerCase();
		var cc_uc=data.countryCode.toUpperCase();
		var cn=data.country;
		$("div#welcome").append('<p>&nbsp;</p><div>\
		Your IPv4 address is <a href="/ip/'+ipa+'" class="boldlink">'+ipa+'</a><br>Announced By <a href="/'+asn+'" class="boldlink">'+asn+'</a> ('+isp+')\
		<div class="flag alignright floatright"><a href="https://bgp.he.net/country/'+cc_uc+'"><img alt="'+cn+'" src="/images/flags/'+cc+'.gif" title="'+cn+'"></a></div>\
		</div>');
	}
	});
}

add_ipv4_to_home();