update 2026-09-26 11:07:27

This commit is contained in:
action committed 2026-09-26 11:07:27 +08:00
1 parent 7b88b273d0
commit 97ae9f68da
4898 files changed
-1712274

No files matched your search

@@ -1,53 +0,0 @@
{
"admin/services/mosdns": {
"title": "MosDNS",
"order": 30,
"action": {
"type": "firstchild"
},
"depends": {
"acl": [ "luci-app-mosdns" ],
"uci": { "mosdns": true }
}
},
"admin/services/mosdns/basic": {
"title": "Basic Setting",
"order": 10,
"action": {
"type": "view",
"path": "mosdns/basic"
}
},
"admin/services/mosdns/rules": {
"title": "Rules",
"order": 15,
"action": {
"type": "view",
"path": "mosdns/rules"
}
},
"admin/services/mosdns/update": {
"title": "Geodata Update",
"order": 20,
"action": {
"type": "view",
"path": "mosdns/update"
}
},
"admin/services/mosdns/statistics": {
"title": "Statistics",
"order": 25,
"action": {
"type": "view",
"path": "mosdns/statistics"
}
},
"admin/services/mosdns/logs": {
"title": "Logs",
"order": 30,
"action": {
"type": "view",
"path": "mosdns/logs"
}
}
}
Binary file not shown.
@@ -1,354 +0,0 @@
#!/usr/bin/env ucode
'use strict';
import { popen, mkdir, unlink, writefile, open, stat, stdout } from 'fs';
import { cursor } from 'uci';
import { connect } from 'ubus';
function to_array(val) {
let res =[];
if (type(val) == 'array') {
for (let i = 0; i < length(val); i++) {
if (type(val[i]) == 'string') {
let s = replace(val[i], /^\s+|\s+$/g, '');
if (s != "") push(res, s);
}
}
} else if (type(val) == 'string') {
let parts = split(val, /[ \t\n]+/);
for (let i = 0; i < length(parts); i++) {
if (parts[i] != "") push(res, parts[i]);
}
}
return res;
}
function exec_sys(cmd) {
let p = popen(cmd + " 2>&1", "r");
if (!p) return { code: -1, stdout: "" };
let stdout = p.read("all");
let code = p.close();
if (type(stdout) == 'string') {
stdout = replace(stdout, /^\s+|\s+$/g, '');
}
return { code: code, stdout: stdout || "" };
}
function interface_dns() {
let uci_cursor = cursor();
uci_cursor.load('mosdns');
let dns_list =[];
if (uci_cursor.get('mosdns', 'config', 'custom_local_dns') === '1') {
dns_list = to_array(uci_cursor.get('mosdns', 'config', 'local_dns'));
} else {
uci_cursor.load('network');
let peerdns = uci_cursor.get('network', 'wan', 'peerdns');
let proto = uci_cursor.get('network', 'wan', 'proto');
if (peerdns === '0' || proto === 'static') {
dns_list = to_array(uci_cursor.get('network', 'wan', 'dns'));
} else {
let ubus_conn = connect();
if (ubus_conn) {
let status = ubus_conn.call('network.interface.wan', 'status');
if (status && type(status['dns-server']) == 'array' && length(status['dns-server']) > 0) {
dns_list = status['dns-server'];
}
}
}
}
if (length(dns_list) === 0) {
dns_list =['119.29.29.29', '223.5.5.5'];
}
print(join(" ", dns_list) + "\n");
}
function get_adlist() {
let uci_cursor = cursor();
uci_cursor.load('mosdns');
let adblock = uci_cursor.get('mosdns', 'config', 'adblock');
if (adblock !== '1') {
mkdir('/var/mosdns', 0755);
exec_sys('rm -rf /etc/mosdns/rule/adlist /etc/mosdns/rule/.ad_source');
writefile('/var/mosdns/disable-ads.txt', '');
print("/var/mosdns/disable-ads.txt\n");
return;
}
mkdir('/etc/mosdns/rule/adlist', 0755);
let ad_source = to_array(uci_cursor.get('mosdns', 'config', 'ad_source'));
let adlist =[];
for (let i = 0; i < length(ad_source); i++) {
let url = ad_source[i];
if (!url) continue;
if (url === 'geosite.dat') {
push(adlist, '/var/mosdns/geosite_category-ads-all.txt');
} else if (index(url, 'file://') === 0) {
push(adlist, substr(url, 7));
} else {
let parts = split(url, '/');
let filename = parts[length(parts) - 1];
let local_path = `/etc/mosdns/rule/adlist/${filename}`;
if (!stat(local_path)) {
writefile(local_path, '');
}
push(adlist, local_path);
}
}
print(join("\n", adlist) + "\n");
}
function update_adlist() {
let uci_cursor = cursor();
uci_cursor.load('mosdns');
if (uci_cursor.get('mosdns', 'config', 'adblock') !== '1') {
return false;
}
let lock_file = '/var/lock/mosdns_ad_update.lock';
let ad_source = to_array(uci_cursor.get('mosdns', 'config', 'ad_source'));
let github_proxy = uci_cursor.get('mosdns', 'config', 'github_proxy') || '';
mkdir('/etc/mosdns/rule', 0755);
writefile('/etc/mosdns/rule/.ad_source', '');
if (stat(lock_file)) return false;
writefile(lock_file, '');
let tmp_res = exec_sys('mktemp -d');
if (tmp_res.code !== 0) {
unlink(lock_file);
die("Failed to create temp directory for adlist.");
}
let ad_tmpdir = tmp_res.stdout;
let has_update = false;
let download_failed = false;
for (let i = 0; i < length(ad_source); i++) {
let url = ad_source[i];
if (!url) continue;
if (url !== 'geosite.dat' && index(url, 'file://') !== 0) {
has_update = true;
exec_sys(`echo "${url}" >> /etc/mosdns/rule/.ad_source`);
let parts = split(url, '/');
let filename = parts[length(parts) - 1];
let mirror = "";
if (match(url, /^https:\/\/raw\.githubusercontent\.com/)) {
mirror = github_proxy ? github_proxy + '/' : '';
}
print(`Downloading ${mirror}${url}\n`);
stdout.flush();
let dl_res = exec_sys(`wget -4 -q --no-check-certificate -T 90 -O "${ad_tmpdir}/${filename}" "${mirror}${url}"`);
if (dl_res.code !== 0) download_failed = true;
}
}
if (download_failed) {
exec_sys(`rm -rf "${ad_tmpdir}"`);
unlink(lock_file);
die("Rules download failed.");
} else {
if (has_update) {
mkdir('/etc/mosdns/rule/adlist', 0755);
exec_sys('rm -rf /etc/mosdns/rule/adlist/*');
exec_sys(`cp "${ad_tmpdir}"/* /etc/mosdns/rule/adlist/`);
}
}
exec_sys(`rm -rf "${ad_tmpdir}"`);
unlink(lock_file);
return has_update;
}
function update_geodat() {
let uci_cursor = cursor();
uci_cursor.load('mosdns');
let github_proxy = uci_cursor.get('mosdns', 'config', 'github_proxy') || '';
let mirror = github_proxy ? github_proxy + '/' : '';
let geoip_type = uci_cursor.get('mosdns', 'config', 'geoip_type') || 'geoip-only-cn-private';
let v2dat_dir = '/usr/share/v2ray';
let tmp_res = exec_sys('mktemp -d');
if (tmp_res.code !== 0) die("Failed to create temp directory for geodata.");
let tmpdir = tmp_res.stdout;
exec_sys(`mkdir -p "${v2dat_dir}"`);
let geoip_updated = false;
let geoip_url = mirror + "https://github.com/Loyalsoldier/geoip/releases/latest/download/" + geoip_type + ".dat";
print(`Downloading ${geoip_url}.sha256sum\n`);
stdout.flush();
if (exec_sys(`wget -4 -q --no-check-certificate -T 20 -O "${tmpdir}/geoip.dat.sha256sum" "${geoip_url}.sha256sum"`).code !== 0) {
exec_sys(`rm -rf "${tmpdir}"`);
die("Failed to download geoip.dat.sha256sum");
}
let geoip_sum_remote = split(exec_sys(`cat "${tmpdir}/geoip.dat.sha256sum"`).stdout, /[ \t\n]+/)[0];
let geoip_sum_local = "";
if (stat(`${v2dat_dir}/geoip.dat`)) {
geoip_sum_local = split(exec_sys(`sha256sum "${v2dat_dir}/geoip.dat"`).stdout, /[ \t\n]+/)[0];
}
if (geoip_sum_local === geoip_sum_remote) {
print("geoip.dat is up to date.\n");
stdout.flush();
} else {
print(`Downloading ${geoip_url}\n`);
stdout.flush();
if (exec_sys(`wget -4 -q --no-check-certificate -T 120 -O "${tmpdir}/geoip.dat" "${geoip_url}"`).code !== 0) {
exec_sys(`rm -rf "${tmpdir}"`);
die("Failed to download geoip.dat");
}
let sum_downloaded = split(exec_sys(`sha256sum "${tmpdir}/geoip.dat"`).stdout, /[ \t\n]+/)[0];
if (sum_downloaded !== geoip_sum_remote) {
exec_sys(`rm -rf "${tmpdir}"`);
die("geoip.dat checksum error");
}
geoip_updated = true;
}
let geosite_updated = false;
let geosite_url = mirror + "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat";
print(`Downloading ${geosite_url}.sha256sum\n`);
stdout.flush();
if (exec_sys(`wget -4 -q --no-check-certificate -T 20 -O "${tmpdir}/geosite.dat.sha256sum" "${geosite_url}.sha256sum"`).code !== 0) {
exec_sys(`rm -rf "${tmpdir}"`);
die("Failed to download geosite.dat.sha256sum");
}
let geosite_sum_remote = split(exec_sys(`cat "${tmpdir}/geosite.dat.sha256sum"`).stdout, /[ \t\n]+/)[0];
let geosite_sum_local = "";
if (stat(`${v2dat_dir}/geosite.dat`)) {
geosite_sum_local = split(exec_sys(`sha256sum "${v2dat_dir}/geosite.dat"`).stdout, /[ \t\n]+/)[0];
}
if (geosite_sum_local === geosite_sum_remote) {
print("geosite.dat is up to date.\n");
stdout.flush();
} else {
print(`Downloading ${geosite_url}\n`);
stdout.flush();
if (exec_sys(`wget -4 -q --no-check-certificate -T 120 -O "${tmpdir}/geosite.dat" "${geosite_url}"`).code !== 0) {
exec_sys(`rm -rf "${tmpdir}"`);
die("Failed to download geosite.dat");
}
let sum_downloaded = split(exec_sys(`sha256sum "${tmpdir}/geosite.dat"`).stdout, /[ \t\n]+/)[0];
if (sum_downloaded !== geosite_sum_remote) {
exec_sys(`rm -rf "${tmpdir}"`);
die("geosite.dat checksum error");
}
geosite_updated = true;
}
if (geoip_updated) {
exec_sys(`cp -a "${tmpdir}/geoip.dat" "${v2dat_dir}/"`);
}
if (geosite_updated) {
exec_sys(`cp -a "${tmpdir}/geosite.dat" "${v2dat_dir}/"`);
}
exec_sys(`rm -rf "${tmpdir}"`);
}
function v2dat_dump() {
let uci_cursor = cursor();
uci_cursor.load('mosdns');
let v2dat_dir = '/usr/share/v2ray';
let configfile = uci_cursor.get('mosdns', 'config', 'configfile') || '/var/etc/mosdns.json';
let adblock = uci_cursor.get('mosdns', 'config', 'adblock');
let ad_source = uci_cursor.get('mosdns', 'config', 'ad_source') || "";
let streaming_media = uci_cursor.get('mosdns', 'config', 'custom_stream_media_dns');
mkdir('/var/mosdns', 0755);
exec_sys('rm -f /var/mosdns/geo*.txt');
if (configfile === "/var/etc/mosdns.json") {
exec_sys(`geo2txt geoip -f ${v2dat_dir}/geoip.dat -e cn -o /var/mosdns`);
exec_sys(`geo2txt geosite -f ${v2dat_dir}/geosite.dat -e cn -e apple -e 'geolocation-!cn' -o /var/mosdns`);
if (adblock === '1' && index(ad_source, 'geosite.dat') !== -1) {
exec_sys(`geo2txt geosite -f ${v2dat_dir}/geosite.dat -e category-ads-all -o /var/mosdns`);
}
if (streaming_media === '1') {
exec_sys(`geo2txt geosite -f ${v2dat_dir}/geosite.dat -e netflix -e disney -e hulu -o /var/mosdns`);
} else {
writefile('/var/mosdns/geosite_disney.txt', '');
writefile('/var/mosdns/geosite_netflix.txt', '');
writefile('/var/mosdns/geosite_hulu.txt', '');
}
} else {
exec_sys(`geo2txt geoip -f ${v2dat_dir}/geoip.dat -e cn -o /var/mosdns`);
exec_sys(`geo2txt geosite -f ${v2dat_dir}/geosite.dat -e cn -e 'geolocation-!cn' -o /var/mosdns`);
let geoip_tags = to_array(uci_cursor.get('mosdns', 'config', 'geoip_tags'));
if (length(geoip_tags) > 0) {
let tags_str = "-e '" + join("' -e '", geoip_tags) + "'";
exec_sys(`geo2txt geoip -f ${v2dat_dir}/geoip.dat ${tags_str} -o /var/mosdns`);
}
let geosite_tags = to_array(uci_cursor.get('mosdns', 'config', 'geosite_tags'));
if (length(geosite_tags) > 0) {
let tags_str = "-e '" + join("' -e '", geosite_tags) + "'";
exec_sys(`geo2txt geosite -f ${v2dat_dir}/geosite.dat ${tags_str} -o /var/mosdns`);
}
}
}
let action = ARGV[0];
switch (action) {
case "interface_dns":
interface_dns();
break;
case "get_adlist":
get_adlist();
break;
case "update":
if (stat('/var/lock/mosdns_update.lock')) {
print("Another update is already in progress.\n");
exit(1);
}
writefile('/var/lock/mosdns_update.lock', '');
try {
update_geodat();
update_adlist();
v2dat_dump();
print("UPDATE_FINISHED\n");
stdout.flush();
} catch (e) {
print("Update failed: " + e + "\n");
print("UPDATE_EXITED\n");
stdout.flush();
unlink('/var/lock/mosdns_update.lock');
exit(1);
}
unlink('/var/lock/mosdns_update.lock');
break;
case "update_adlist":
update_adlist();
break;
case "v2dat_dump":
v2dat_dump();
break;
default:
exit(0);
}
@@ -1,54 +0,0 @@
{
"luci-app-mosdns": {
"description": "Grant UCI access for luci-app-mosdns",
"read": {
"file": {
"/etc/hosts": [ "read" ],
"/etc/init.d/mosdns": [ "exec" ],
"/etc/mosdns/config_custom.yaml": [ "read" ],
"/etc/mosdns/rule/*": [ "read" ],
"/tmp/dhcp.leases": [ "read" ],
"/tmp/hosts/*": [ "read" ],
"/tmp/odhcpd.leases": [ "read" ],
"/usr/bin/mosdns": [ "exec" ],
"/usr/share/mosdns/mosdns.uc": [ "exec" ],
"/var/log/*": [ "read" ],
"/var/mosdns/*": [ "read" ]
},
"ubus": {
"file": [ "read" ],
"service": [ "list" ],
"luci.mosdns": [
"clean_log",
"clear_query_logs",
"flush_cache",
"get_history",
"get_leases",
"get_logs",
"get_stats",
"get_top",
"get_update_log",
"get_version",
"print_log",
"start_update"
]
},
"uci": [ "mosdns" ]
},
"write": {
"file": {
"/etc/mosdns/config_custom.yaml": [ "write" ],
"/etc/mosdns/rule/*": [ "write" ],
"/usr/share/mosdns/*": [ "write" ],
"/usr/share/v2ray/*": [ "write" ],
"/var/log/*": [ "write" ],
"/var/mosdns/*": [ "write" ]
},
"ubus": {
"file": [ "write" ],
"luci.mosdns": [ "clear_query_logs" ]
},
"uci": [ "mosdns" ]
}
}
}
@@ -1,322 +0,0 @@
#!/usr/bin/env ucode
'use strict';
import { popen, stat, readfile, open } from 'fs';
import { cursor } from 'uci';
function to_array(val) {
let res =[];
if (type(val) == 'array') {
for (let i = 0; i < length(val); i++) {
if (type(val[i]) == 'string') {
let s = replace(val[i], /^\s+|\s+$/g, '');
if (s != "") push(res, s);
}
}
} else if (type(val) == 'string') {
let parts = split(val, /[ \t\n]+/);
for (let i = 0; i < length(parts); i++) {
if (parts[i] != "") push(res, parts[i]);
}
}
return res;
}
function exec_sys(cmd) {
let p = popen(cmd + " 2>&1", "r");
if (!p) return { code: -1, stdout: "" };
let stdout = p.read("all");
let code = p.close();
if (type(stdout) == 'string') {
stdout = replace(stdout, /^\s+|\s+$/g, '');
}
return { code: code, stdout: stdout || "" };
}
function get_logfile_path_internal() {
let uci_cursor = cursor();
uci_cursor.load('mosdns');
let configfile = uci_cursor.get('mosdns', 'config', 'configfile');
if (configfile === '/var/etc/mosdns.json' || !configfile) {
return uci_cursor.get('mosdns', 'config', 'log_file');
} else {
let content = readfile(configfile);
if (content) {
let m = match(content, /file:\s*([^\r\n]+)/);
if (m && m[1]) {
let log_path = m[1];
log_path = replace(log_path, /^\s+|\s+$/g, '');
log_path = replace(log_path, /^["']|["']$/g, '');
if (log_path != "") {
return log_path;
}
}
}
return '/var/log/mosdns.log';
}
}
function parse_json_safe(str) {
if (!str || str == "")
return null;
try {
return json(str);
} catch(e) {
return null;
}
}
function call_mosdns_api(endpoint, method) {
try {
let uci_cursor = cursor();
uci_cursor.load('mosdns');
let port = uci_cursor.get('mosdns', 'config', 'listen_port_api') || '9091';
let http_method = method ? method : 'GET';
let post_flag = (http_method === 'POST') ? '--post-data=""' : '';
let cmd = `wget -q -O - ${post_flag} "http://127.0.0.1:${port}/plugins/stats_collector${endpoint}"`;
let res = exec_sys(cmd);
if (res.code === 0 && res.stdout != "") {
let json_val = parse_json_safe(res.stdout);
if (json_val) return json_val;
}
return { error: "MosDNS API unreachable" };
} catch (e) {
return { error: String(e) };
}
}
function get_client_leases() {
let leases = {};
let add_lease = function(ip, name) {
if (!ip || !name) return;
ip = replace(ip, /^\s+|\s+$/g, '');
name = replace(name, /^\s+|\s+$/g, '');
if (ip == "" || name == "" || name == "*" || name == "unknown") return;
ip = replace(ip, /\/[0-9]+$/, '');
ip = replace(ip, /^::ffff:/i, '');
leases[ip] = name;
try {
if (type(lc) == 'function') {
leases[lc(ip)] = name;
}
} catch (e) {}
};
let parse_hosts_file = function(filepath) {
try {
if (stat(filepath)) {
let content = readfile(filepath);
if (content) {
let lines = split(content, '\n');
for (let i = 0; i < length(lines); i++) {
let line = replace(lines[i], /^\s+|\s+$/g, '');
if (line == "" || match(line, /^#/)) continue;
let parts = split(line, /[ \t]+/);
if (length(parts) >= 2) {
add_lease(parts[0], parts[1]);
}
}
}
}
} catch (e) {}
};
try {
if (stat('/tmp/dhcp.leases')) {
let content = readfile('/tmp/dhcp.leases');
if (content) {
let lines = split(content, '\n');
for (let i = 0; i < length(lines); i++) {
let line = replace(lines[i], /^\s+|\s+$/g, '');
if (line == "" || match(line, /^#/)) continue;
let parts = split(line, /[ \t]+/);
if (length(parts) >= 4) {
add_lease(parts[2], parts[3]);
}
}
}
}
} catch (e) {}
parse_hosts_file('/tmp/hosts/odhcpd');
parse_hosts_file('/tmp/hosts/dhcp');
try {
if (stat('/tmp/odhcpd.leases')) {
let content = readfile('/tmp/odhcpd.leases');
if (content) {
let lines = split(content, '\n');
for (let i = 0; i < length(lines); i++) {
let line = replace(lines[i], /^\s+|\s+$/g, '');
if (line == "" || match(line, /^#/)) continue;
let parts = split(line, /[ \t]+/);
if (length(parts) >= 6) {
let host = parts[3];
for (let j = 5; j < length(parts); j++) {
add_lease(parts[j], host);
}
}
}
}
}
} catch (e) {}
parse_hosts_file('/etc/hosts');
return leases;
}
const methods = {
get_stats: {
call: function() {
return call_mosdns_api('/api/v1/stats', 'GET');
}
},
get_history: {
args: { points: 24 },
call: function(request) {
let points = request?.args?.points || 24;
return call_mosdns_api(`/api/v1/history?points=${points}`, 'GET');
}
},
get_top: {
args: { limit: 10 },
call: function(request) {
let limit = request?.args?.limit || 10;
let res = call_mosdns_api(`/api/v1/top?limit=${limit}`, 'GET');
if (type(res) == 'object' && !res.error) {
res.leases = get_client_leases();
}
return res;
}
},
get_leases: {
call: function() {
return { leases: get_client_leases() };
}
},
get_logs: {
args: { limit: 50, offset: 0, search: '', filter: 'all' },
call: function(request) {
let limit = request?.args?.limit || 50;
let offset = request?.args?.offset || 0;
let search = request?.args?.search || '';
let filter = request?.args?.filter || 'all';
return call_mosdns_api(`/api/v1/logs?limit=${limit}&offset=${offset}&search=${search}&filter=${filter}`, 'GET');
}
},
clear_query_logs: {
call: function() {
return call_mosdns_api('/api/v1/logs/clear', 'POST');
}
},
flush_cache: {
call: function() {
try {
let uci_cursor = cursor();
uci_cursor.load('mosdns');
let port = uci_cursor.get('mosdns', 'config', 'listen_port_api') || '9091';
if (!port) return { error: "API listen port not configured." };
let res = exec_sys(`wget -q -O - "http://127.0.0.1:${port}/plugins/lazy_cache/flush"`);
if (res.code === 0) {
return { success: true };
} else {
return { success: false, error: res.stdout };
}
} catch (e) {
return { error: String(e) };
}
}
},
print_log: {
call: function() {
try {
let path = get_logfile_path_internal();
if (path && stat(path)) {
return { log: readfile(path) };
} else {
return { error: 'Log file not accessible or does not exist.' };
}
} catch (e) {
return { error: String(e) };
}
}
},
clean_log: {
call: function() {
try {
let path = get_logfile_path_internal();
if (path) {
let f = open(path, 'w');
if (f) {
f.close();
return { success: true };
}
return { error: 'Failed to open log file for writing.' };
} else {
return { error: 'Log file path could not be determined.' };
}
} catch (e) {
return { error: String(e) };
}
}
},
get_version: {
call: function() {
let res = exec_sys('mosdns version');
if (res.code === 0) {
return { version: res.stdout };
} else {
return { error: res.stdout };
}
}
},
start_update: {
call: function() {
try {
if (stat('/var/lock/mosdns_update.lock')) {
return { success: false, error: 'Another update is already in progress.' };
}
exec_sys('echo "" > /var/log/mosdns_update.log');
exec_sys('/usr/share/mosdns/mosdns.uc update > /var/log/mosdns_update.log 2>&1 &');
return { success: true };
} catch (e) {
return { success: false, error: String(e) };
}
}
},
get_update_log: {
call: function() {
try {
if (stat('/var/log/mosdns_update.log')) {
return { log: readfile('/var/log/mosdns_update.log') };
} else {
return { log: '' };
}
} catch (e) {
return { error: String(e) };
}
}
}
};
return { "luci.mosdns": methods };
@@ -1,4 +0,0 @@
{
"config": "mosdns",
"init": "mosdns"
}