Расширение браузера для управления маршрутами на Микротике07.02.2024 09:30
let opened_tab = null;
let api_ip_address = null;
let api_ip_query = '';
let routes_query = '';
let domains_query = '';
let domains_regular = ',';
let exclude_minprefix = 32;
let exclude_maxprefix = 0;
let notrouted_minprefix = 32;
let notrouted_maxprefix = 0;
async function InitPage()
{
let load_settings = null;
await chrome.storage.local.get(["MikroTikControlPanelSettings"]).then((result) => {
if (typeof result.MikroTikControlPanelSettings != 'undefined')
load_settings = result.MikroTikControlPanelSettings;
});
if (load_settings !== null) {
settings['user'] = DecodeString(load_settings['an']);
settings['password'] = DecodeString(load_settings['ap']);
settings['localhost'] = load_settings['localhost'];
settings['router'] = load_settings['router'];
settings['protocol'] = load_settings['protocol'];
settings['defaults']['dynamic'] = load_settings['defaults']['dynamic'];
settings['defaults']['time'] = load_settings['defaults']['time'];
settings['defaults']['www'] = load_settings['defaults']['www'];
settings['defaults']['what'] = load_settings['defaults']['what'];
settings['defaults']['how'] = load_settings['defaults']['how'];
} else {
if ((typeof settings['user'] == 'undefined' || !settings['user']) && typeof settings['an'] != 'undefined' && settings['an'])
settings['user'] = ae(settings['an']);
if ((typeof settings['password'] == 'undefined' || !settings['password']) && typeof settings['ap'] != 'undefined' && settings['ap'])
settings['password'] = ae(settings['ap']);
}
for (let subnet in settings['exclude']) {
if (settings['exclude'][subnet] > exclude_maxprefix)
exclude_maxprefix = settings['exclude'][subnet];
if (settings['exclude'][subnet] < exclude_minprefix)
exclude_minprefix = settings['exclude'][subnet];
}
for (let subnet in settings['notrouted']) {
if (settings['notrouted'][subnet] > notrouted_maxprefix)
notrouted_maxprefix = settings['notrouted'][subnet];
if (settings['notrouted'][subnet] < notrouted_minprefix)
notrouted_minprefix = settings['notrouted'][subnet];
}
document.getElementById("local-address").value = settings['localhost'];
document.getElementById("router-address").value = settings['router'];
document.getElementById("router-user").value = settings['user'];
document.getElementById("router-password").value = settings['password'];
document.getElementById("protocol-selection").value = settings['protocol'];
document.getElementById("domain-dynamic").checked = settings['defaults']['dynamic'];
document.getElementById("domain-time").value = settings['defaults']['time'];
document.getElementById("domain-" + settings['defaults']['how']).checked = true;
document.getElementById("domain-" + settings['defaults']['what']).checked = true;
document.getElementById("top-domains").value = settings['defaults']['www'];
let routes_options = '';
let domains_options = '';
for (let idx in settings['routes']) {
routes_options += '';
if (settings['routes'][idx])
routes_query += '"list=' + settings['routes'][idx] + '",' + (routes_query ? '"#|",' : '');
}
for (let idx in settings['domains']) {
domains_options += '';
domains_regular += settings['domains'][idx] + ',';
if (settings['domains'][idx])
domains_query += '"list=' + settings['domains'][idx] + '",' + (domains_query ? '"#|",' : '');
}
document.getElementById("route-selection").innerHTML = routes_options;
document.getElementById("domain-selection").innerHTML = domains_options;
document.getElementById("settings-button").addEventListener("click", () => {
HideViewBlock('settings-block');
});
await chrome.tabs.query({active: true}, (tabs) => {
const tab = tabs[0];
if (tab) {
opened_tab = tab;
const url = new URL(tab.url);
const hostname = url.hostname;
document.getElementById("current-page").innerHTML = hostname;
if ((/^(\d{1,3}\.){3}\d{1,3}$/).test(hostname))
document.getElementById("domain-button").disabled = IpIsExclude(hostname);
if (hostname.indexOf('.') > 0) {
fetch("http://api.syo.su/ipwhois?" + hostname, {
method: "GET",
headers: { "Accept": "application/json" }
}).then((response) => { return response.json(); }).then((whois) => {
document.getElementById("page-address").innerHTML = whois['ip'];
document.getElementById("page-city").innerHTML = whois['ip2location']['city'] + ", " + whois['ip2location']['country'];
document.getElementById("domain-button").disabled = IpIsExclude(whois['ip']);
RefreshDomainInfo();
}).catch(function() {
SetExternalApiError();
});
} else {
document.getElementById("domain-button").disabled = true;
}
} else {
document.getElementById("domain-button").disabled = true;
}
});
RefreshAddressInfo();
let authuser = 'Basic ' + btoa(settings['user'] + ":" + settings['password']);
fetch(settings['protocol'] + "://" + settings['router'] + "/rest/ip/firewall/address-list/print", {
method: "POST",
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{".query": [' + routes_query + '"address=' + settings['localhost'] + '","disabled=false"]}'
}).then((response) => { return response.json(); }).then((data) => {
document.getElementById("route-selection").value = (data.length ? data[0]['list'] : '');
}).catch(function() {
SetRouterApiError();
});
await fetch("http://api.syo.su/gethost?api.syo.su", {
method: "GET",
headers: { "Accept": "text/html" }
}).then((response) => { return response.text(); }).then((ip) => {
api_ip_address = ip;
api_ip_query = '"dst-address=' + ip + ':443","dst-address=' + ip + ':80","#|"';
});
document.getElementById("route-button").addEventListener("click", () => {
SetLocalhostRoute();
});
document.getElementById("domain-button").addEventListener("click", () => {
SetDomainRoute();
});
document.getElementById("save-button").addEventListener("click", () => {
SaveSettings();
});
}
function HideViewBlock(block_name)
{
let info_block = document.getElementById(block_name);
info_block.hidden = !info_block.hidden;
}
function SetRouterApiError()
{
document.getElementById("error-messages").innerHTML += "Error connecting to router API
Check router address, username, password and protocol in settings";
document.getElementById("route-button").disabled = true;
document.getElementById("domain-button").disabled = true;
}
function SetExternalApiError()
{
document.getElementById("error-messages").innerHTML += "Error connecting to external API
Check availability http://api.syo.su";
document.getElementById("domain-button").disabled = true;
}
function RefreshAddressInfo()
{
fetch("http://api.syo.su/myip", {
method: "GET",
headers: { "Accept": "text/html" }
}).then((response) => { return response.text(); }).then((data) => {
document.getElementById("current-address").innerHTML = data;
fetch("http://api.syo.su/ipwhois?" + data, {
method: "GET",
headers: { "Accept": "application/json" }
}).then((response) => { return response.json(); }).then((whois) => {
document.getElementById("address-city").innerHTML = whois['ip2location']['city'] + ", " + whois['ip2location']['country'];
document.getElementById("address-provider").innerHTML = whois['ip2location']['provider'];
}).catch(function() {
SetExternalApiError();
});
}).catch(function() {
SetExternalApiError();
});
}
async function SetLocalhostRoute()
{
let localhost = document.getElementById("local-address").value.replaceAll(' ', '');
let addrlist = document.getElementById("route-selection").value;
let address = document.getElementById("page-address").innerHTML;
let domain = document.getElementById("current-page").innerHTML;
let router = document.getElementById("router-address").value.replaceAll(' ', '');
let username = document.getElementById("router-user").value;
let userpass = document.getElementById("router-password").value;
let protocol = document.getElementById("protocol-selection").value;
let authuser = 'Basic ' + btoa(username + ":" + userpass);
let is_exclude = (!address || address == '-' || IpIsExclude(address));
let disable_from = 0;
let current_list = await fetch(protocol + "://" + router + "/rest/ip/firewall/address-list/print", {
method: "POST",
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{".query": [' + routes_query + '"address=' + localhost + '"]}'
}).then((response) => { return response.json(); }).then((data) => {
return data;
});
let current_conn_query = api_ip_query;
if (!is_exclude)
current_conn_query += '"dst-address=' + address + ':443","dst-address=' + address + ':80","#|"' + (api_ip_query ? '"#|"' : "");
let current_conn = await fetch(protocol + "://" + router + "/rest/ip/firewall/connection/print", {
method: "POST",
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{".query": [' + current_conn_query + ']}'
}).then((response) => { return response.json(); }).then((data) => {
return data;
});
let gateway_address = null;
if (current_conn.length)
gateway_address = current_conn[0]['reply-dst-address'].split(':')[0];
if (addrlist) {
disable_from = 1;
let fetch_method = null;
let fetch_url = null;
let sendbody = '"address":"' + localhost + '","disabled":"false","dynamic":"false","list":"' + addrlist + '"';
if (current_list.length) {
fetch_method = "PATCH";
fetch_url = protocol + "://" + router + "/rest/ip/firewall/address-list/" + current_list[0]['.id'];
let comment = (typeof current_list[0]['comment'] != 'undefined' ? ',"comment":"' + current_list[0]['comment'] + '"' : "");
sendbody = '".id":"' + current_list[0]['.id'] + '",' + sendbody + comment;
} else {
fetch_method = "POST";
fetch_url = protocol + "://" + router + "/rest/ip/firewall/address-list/add";
}
await fetch(fetch_url, {
method: fetch_method,
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{' + sendbody + '}'
}).then((response) => { return response; });
}
for (let i = disable_from; i < current_list.length; i++)
if (current_list[i]['disabled'] == 'false')
await fetch(protocol + "://" + router + "/rest/ip/firewall/address-list/" + current_list[i]['.id'], {
method: "PATCH",
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{".id":"' + current_list[i]['.id'] + '","address":"' + localhost + '","disabled":"true","dynamic":"' + current_list[i]['dynamic'] + '","list":"' + current_list[i]['list'] + '"}'
}).then((response) => { return response; });
for (let i = 0; i < current_conn.length; i++) {
if (localhost == current_conn[i]['src-address'].split(':')[0]) {
let check_address = await fetch(protocol + "://" + router + "/rest/ip/firewall/connection/" + current_conn[i]['.id'], {
method: "DELETE",
headers: { "Accept": "application/json", "Authorization": authuser }
}).then((response) => { return response; });
}
};
setTimeout(RefreshAddressInfo, 1000);
if (!is_exclude)
setTimeout(function() { chrome.tabs.reload(opened_tab.id); }, 1000);
}
function RefreshDomainInfo()
{
let domain = document.getElementById("current-page").innerHTML;
let address = document.getElementById("page-address").innerHTML;
let router = document.getElementById("router-address").value.replaceAll(' ', '');
let username = document.getElementById("router-user").value;
let userpass = document.getElementById("router-password").value;
let protocol = document.getElementById("protocol-selection").value;
let authuser = 'Basic ' + btoa(username + ":" + userpass);
fetch(protocol + "://" + router + "/rest/ip/firewall/address-list/print", {
method: "POST",
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{".query": [' + domains_query + '"comment=' + domain + '","address=' + address + '","#&","address=' + domain + '","#|","disabled=false","timeout","#!","dynamic=true","#&!"]}'
}).then((response) => { return response.json(); }).then((data) => {
if (data.length) {
document.getElementById("domain-time").value = (data[0]['dynamic'] == 'true' ? data[0]['timeout'] : settings['defaults']['time']);
document.getElementById("domain-dynamic").checked = (data[0]['dynamic'] == 'true');
document.getElementById("domain-selection").value = data[0]['list'];
} else {
document.getElementById("domain-time").value = settings['defaults']['time'];
document.getElementById("domain-dynamic").checked = settings['defaults']['dynamic'];
document.getElementById("domain-selection").value = "";
}
});
}
async function SetDomainRoute()
{
let domain = document.getElementById("current-page").innerHTML;
let address = document.getElementById("page-address").innerHTML;
let addrlist = document.getElementById("domain-selection").value;
let dynamic = document.getElementById("domain-dynamic").checked;
let dyntime = document.getElementById("domain-time").value.replaceAll(' ', '');
let router = document.getElementById("router-address").value.replaceAll(' ', '');
let username = document.getElementById("router-user").value;
let userpass = document.getElementById("router-password").value;
let protocol = document.getElementById("protocol-selection").value;
let top_domains_template = ',' + document.getElementById("top-domains").value.replaceAll(' ', '').toLowerCase() + ',';
let as_address = document.getElementById("domain-address").checked;
let add_www = document.getElementById("domain-www").checked;
let add_all = document.getElementById("domain-all").checked;
let authuser = 'Basic ' + btoa(username + ":" + userpass);
let is_exclude = (!address || address == '-' || IpIsExclude(address));
if (is_exclude)
return 0;
let domains = new Array();
let addresses = new Array();
let error_messages = new Array();
if (domain != address) {
let domain_struct = domain.split('.');
let steps_count = 1;
if (add_www) {
if (top_domains_template.indexOf(',' + domain_struct[0] + ',') >= 0)
steps_count = 2;
} else if (add_all) {
steps_count = domain_struct.length - 1;
}
let domain_next = domain;
for (let i = 0; i < steps_count; i++) {
domains[domain_next] = new Array();
if (as_address) {
let hosts = await fetch("http://api.syo.su/gethosts?" + domain_next, {
method: "GET",
headers: { "Accept": "text/html" }
}).then((response) => {
return response.text().split('
');
}).catch(function() {
return null;
});
if (hosts !== null) {
for (let i = 0; i < hosts.length; i++)
if (!addresses.includes(hosts[i])) {
addresses.push(hosts[i]);
domains[domain_next].push(hosts[i]);
}
} else
error_messages.push("Error API access");
} else {
domains[domain_next].push(domain_next);
}
domain_next = domain_next.substr(domain_struct[i].length + 1);
}
}
let current_query = '';
let current_addr_query = '';
for (let dom in domains) {
current_query += '"comment=' + dom + '","address=' + dom + '","#|",' + (current_query ? '"#|",' : '');
current_addr_query += '"comment=' + dom + '",' + (current_addr_query ? '"#|",' : '');
}
let current_list = await fetch(protocol + "://" + router + "/rest/ip/firewall/address-list/print", {
method: "POST",
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{".query": [' + domains_query + current_query + '"disabled=false","timeout","#!","dynamic=true","#&!"]}'
}).then((response) => { return response.json(); }).then((data) => {
return data;
});
let current_addrs = new Array();
let current_num = null;
if (current_list.length)
current_num = 0;
if (addrlist) {
for (let dom in domains) {
for (let i = 0; i < domains[dom].length; i++) {
let set_address = domains[dom][i];
let sendbody = '"address":"' + set_address + '","list":"' + addrlist + '","disabled":"false"';
if (as_address)
sendbody += ',"comment":"' + dom + '"';
if (dynamic)
sendbody += ',"dynamic":"true","timeout":"' + dyntime + '"';
else
sendbody += ',"dynamic":"false"';
let fetch_method = null;
let fetch_url = null;
if (current_num !== null && current_num < current_list.length) {
fetch_method = "PATCH";
fetch_url = protocol + "://" + router + "/rest/ip/firewall/address-list/" + current_list[current_num]['.id'];
sendbody = '".id":"' + current_list[current_num]['.id'] + '",' + sendbody;
current_num++;
} else {
fetch_method = "POST";
fetch_url = protocol + "://" + router + "/rest/ip/firewall/address-list/add";
}
let check_address = await fetch(fetch_url, {
method: fetch_method,
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{' + sendbody + '}'
}).then((response) => { return response.json(); }).then((answer) => {
return answer;
});
}
}
} else {
current_addrs = await fetch(protocol + "://" + router + "/rest/ip/firewall/address-list/print", {
method: "POST",
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{".query": [' + domains_query + current_addr_query + '"disabled=false"]}'
}).then((response) => { return response.json(); }).then((data) => {
return data;
});
}
if (current_num !== null)
for (let i = current_num; i < current_list.length; i++) {
let check_address = await fetch(protocol + "://" + router + "/rest/ip/firewall/address-list/" + current_list[i]['.id'], {
method: "DELETE",
headers: { "Accept": "application/json", "Authorization": authuser }
}).then((response) => { return response; });
}
if (addrlist)
current_addrs = await fetch(protocol + "://" + router + "/rest/ip/firewall/address-list/print", {
method: "POST",
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{".query": [' + domains_query + current_addr_query + '"disabled=false"]}'
}).then((response) => { return response.json(); }).then((data) => {
return data;
});
let current_conn_query = '';
for (let i = 0; i < current_addrs.length; i++) {
current_conn_query += '"dst-address=' + current_addrs[i]['address'] + ':443","dst-address=' + current_addrs[i]['address'] + ':80","#|",' + (current_conn_query ? '"#|",' : '');
}
let current_conn = await fetch(protocol + "://" + router + "/rest/ip/firewall/connection/print", {
method: "POST",
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization": authuser },
body: '{".query": [' + current_conn_query + ']}'
}).then((response) => { return response.json(); }).then((data) => {
return data;
});
for (let i = 0; i < current_conn.length; i++) {
let check_address = await fetch(protocol + "://" + router + "/rest/ip/firewall/connection/" + current_conn[i]['.id'], {
method: "DELETE",
headers: { "Accept": "application/json", "Authorization": authuser }
}).then((response) => { return response; });
}
setTimeout(function() { chrome.tabs.reload(opened_tab.id); }, 1000);
}
function SaveSettings()
{
let localhost = document.getElementById("local-address").value;
let dynamic = document.getElementById("domain-dynamic").checked;
let dyntime = document.getElementById("domain-time").value;
let router = document.getElementById("router-address").value;
let username = document.getElementById("router-user").value;
let userpass = document.getElementById("router-password").value;
let protocol = document.getElementById("protocol-selection").value;
let top_domains_template = document.getElementById("top-domains").value;
let as_address = document.getElementById("domain-address").checked;
let add_www = document.getElementById("domain-www").checked;
let add_all = document.getElementById("domain-all").checked;
let save_settings = {
localhost: localhost,
router: router,
an: CodeString(username),
ap: CodeString(userpass),
protocol: protocol,
defaults: {
dynamic: dynamic,
time: dyntime,
www: top_domains_template,
what: (add_www ? "www" : (add_all ? "all" : "top")),
how: (as_address ? "address" : "name")
}
};
chrome.storage.local.set({"MikroTikControlPanelSettings": save_settings }).then(() => {
document.getElementById("settings-info").innerHTML = "Settings saved";
});
}
const delay = (delayInms) => {
return new Promise(resolve => setTimeout(resolve, delayInms));
};
function IpIsExclude(ip_str)
{
let ip = ParseIp(ip_str);
if (ip === null)
return false;
return IpInArray(ip, settings['exclude'], exclude_minprefix, exclude_maxprefix)
|| IpInArray(ip, settings['notrouted'], notrouted_minprefix, notrouted_maxprefix);
}
function IpInArray(ip, arr, prefmin, prefmax)
{
let submask = new Uint32Array([0xffffffff << (32 - prefmax)])[0];
let network = ip & submask;
for (let prefix = prefmax; prefix >= prefmin; prefix--) {
let subnet = ((network >>> 24) & 0xff).toString() + '.' + ((network >>> 16) & 0xff).toString() + '.' + ((network >>> 8) & 0xff).toString() + '.' + (network & 0xff).toString();
if (typeof arr[subnet] != 'undefined' && arr[subnet] <= prefix)
return true;
submask = submask << 1;
network = network & submask;
}
return false;
}
function ParseIp(ip_str, ip_format = 10)
{
let ip_octets = ip_str.split('.');
ip_octets[0] = parseInt(ip_octets[0], ip_format);
ip_octets[1] = parseInt(ip_octets[1], ip_format);
ip_octets[2] = parseInt(ip_octets[2], ip_format);
ip_octets[3] = parseInt(ip_octets[3], ip_format);
if (isNaN(ip_octets[0]) || isNaN(ip_octets[1]) || isNaN(ip_octets[2]) || isNaN(ip_octets[3])
|| ip_octets[0] > 255 || ip_octets[1] > 255 || ip_octets[2] > 255 || ip_octets[3] > 255
|| ip_octets[0] < 0 || ip_octets[1] < 0 || ip_octets[2] < 0 || ip_octets[3] < 0)
return null;
return new Uint32Array([((ip_octets[0] << 24) + (ip_octets[1] << 16) + (ip_octets[2] << 8) + ip_octets[3])])[0];
}
function ae(ai)
{
let f = "";
for (let c = 0; c < ai.length; c++)
f += String.fromCharCode(ai.charCodeAt(c) ^ (1 + (ai.length - c) % 32));
return f;
}
function StringToHex(str)
{
let hex = '';
for (let i = 0; i < str.length; i++)
hex += str.charCodeAt(i).toString(16).padStart(2, '0');
return hex;
}
function HexToString(hex)
{
let str = '';
for (let i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substring(i, i + 2), 16));
return str;
}
function CodeString(str)
{
return StringToHex(ae(btoa(str)));
}
function DecodeString(str)
{
return atob(ae(HexToString(str)));
}
InitPage();
© Habrahabr.ru