mirror of https://github.com/kenzok8/small.git
update 2024-09-08 22:33:35
This commit is contained in:
parent
fe33ea74ec
commit
b177d2de76
|
@ -0,0 +1,10 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_VERSION:=1.8.0
|
||||
|
||||
LUCI_TITLE:=LuCI Support for mihomo
|
||||
LUCI_DEPENDS:=+luci-base +mihomo
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
|
@ -0,0 +1,113 @@
|
|||
'use strict';
|
||||
'require baseclass';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require rpc';
|
||||
|
||||
const homeDir = '/etc/mihomo';
|
||||
const profilesDir = `${homeDir}/profiles`;
|
||||
const mixinFilePath = `${homeDir}/mixin.yaml`;
|
||||
const runDir = `${homeDir}/run`;
|
||||
const runAppLogPath = `${runDir}/app.log`;
|
||||
const runCoreLogPath = `${runDir}/core.log`;
|
||||
const runProfilePath = `${runDir}/config.yaml`;
|
||||
const nftDir = `${homeDir}/nftables`;
|
||||
const reservedIPNFT = `${nftDir}/reserved_ip.nft`;
|
||||
const reservedIP6NFT = `${nftDir}/reserved_ip6.nft`;
|
||||
|
||||
return baseclass.extend({
|
||||
homeDir: homeDir,
|
||||
profilesDir: profilesDir,
|
||||
mixinFilePath: mixinFilePath,
|
||||
runDir: runDir,
|
||||
runAppLogPath: runAppLogPath,
|
||||
runCoreLogPath: runCoreLogPath,
|
||||
runProfilePath: runProfilePath,
|
||||
reservedIPNFT: reservedIPNFT,
|
||||
reservedIP6NFT: reservedIP6NFT,
|
||||
|
||||
callServiceList: rpc.declare({
|
||||
object: 'service',
|
||||
method: 'list',
|
||||
params: ['name'],
|
||||
expect: { '': {} }
|
||||
}),
|
||||
|
||||
getAppLog: function () {
|
||||
return L.resolveDefault(fs.read_direct(this.runAppLogPath));
|
||||
},
|
||||
|
||||
getCoreLog: function () {
|
||||
return L.resolveDefault(fs.read_direct(this.runCoreLogPath));
|
||||
},
|
||||
|
||||
clearAppLog: function () {
|
||||
return fs.exec_direct('/usr/libexec/mihomo-call', ['clear', 'app_log']);
|
||||
},
|
||||
|
||||
clearCoreLog: function () {
|
||||
return fs.exec_direct('/usr/libexec/mihomo-call', ['clear', 'core_log']);
|
||||
},
|
||||
|
||||
listProfiles: function () {
|
||||
return L.resolveDefault(fs.list(this.profilesDir), []);
|
||||
},
|
||||
|
||||
status: async function () {
|
||||
try {
|
||||
return (await this.callServiceList('mihomo'))['mihomo']['instances']['mihomo']['running'];
|
||||
} catch (ignored) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
reload: function () {
|
||||
return fs.exec_direct('/usr/libexec/mihomo-call', ['service', 'reload']);
|
||||
},
|
||||
|
||||
restart: function () {
|
||||
return fs.exec_direct('/usr/libexec/mihomo-call', ['service', 'restart']);
|
||||
},
|
||||
|
||||
appVersion: function () {
|
||||
return L.resolveDefault(fs.exec_direct('/usr/libexec/mihomo-call', ['version', 'app']), 'Unknown');
|
||||
},
|
||||
|
||||
coreVersion: function () {
|
||||
return L.resolveDefault(fs.exec_direct('/usr/libexec/mihomo-call', ['version', 'core']), 'Unknown');
|
||||
},
|
||||
|
||||
callMihomoAPI: async function (method, path, body) {
|
||||
const running = await this.status();
|
||||
if (running) {
|
||||
const apiPort = uci.get('mihomo', 'mixin', 'api_port');
|
||||
const apiSecret = uci.get('mihomo', 'mixin', 'api_secret');
|
||||
const url = `http://${window.location.hostname}:${apiPort}${path}`;
|
||||
await fetch(url, {
|
||||
method: method,
|
||||
headers: { 'Authorization': `Bearer ${apiSecret}` },
|
||||
body: body
|
||||
})
|
||||
} else {
|
||||
alert(_('Service is not running.'));
|
||||
}
|
||||
},
|
||||
|
||||
openDashboard: async function () {
|
||||
const running = await this.status();
|
||||
if (running) {
|
||||
const uiName = uci.get('mihomo', 'mixin', 'ui_name');
|
||||
const apiPort = uci.get('mihomo', 'mixin', 'api_port');
|
||||
const apiSecret = uci.get('mihomo', 'mixin', 'api_secret');
|
||||
let url;
|
||||
if (uiName) {
|
||||
url = `http://${window.location.hostname}:${apiPort}/ui/${uiName}/?host=${window.location.hostname}&hostname=${window.location.hostname}&port=${apiPort}&secret=${apiSecret}`;
|
||||
} else {
|
||||
url = `http://${window.location.hostname}:${apiPort}/ui/?host=${window.location.hostname}&hostname=${window.location.hostname}&port=${apiPort}&secret=${apiSecret}`;
|
||||
}
|
||||
setTimeout(() => window.open(url, '_blank'), 0);
|
||||
} else {
|
||||
alert(_('Service is not running.'));
|
||||
}
|
||||
},
|
||||
})
|
|
@ -0,0 +1,515 @@
|
|||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require network';
|
||||
'require rpc';
|
||||
'require poll';
|
||||
'require tools.widgets as widgets';
|
||||
'require tools.mihomo as mihomo';
|
||||
|
||||
function renderStatus(running) {
|
||||
return updateStatus(E('input', { id: 'core_status', style: 'border: unset; font-style: italic; font-weight: bold;', readonly: '' }), running);
|
||||
}
|
||||
|
||||
function updateStatus(element, running) {
|
||||
if (element) {
|
||||
element.style.color = running ? 'green' : 'red';
|
||||
element.value = running ? _('Running') : _('Not Running');
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load('mihomo'),
|
||||
mihomo.listProfiles(),
|
||||
mihomo.appVersion(),
|
||||
mihomo.coreVersion(),
|
||||
mihomo.status(),
|
||||
network.getHostHints(),
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const subscriptions = uci.sections('mihomo', 'subscription');
|
||||
const profiles = data[1];
|
||||
const appVersion = data[2];
|
||||
const coreVersion = data[3];
|
||||
const running = data[4];
|
||||
const hosts = data[5].hosts;
|
||||
|
||||
let m, s, o, so;
|
||||
|
||||
m = new form.Map('mihomo', _('MihomoTProxy'), `${_('Transparent Proxy with Mihomo on OpenWrt.')} <a href="https://github.com/morytyann/OpenWrt-mihomo/wiki" target="_blank">${_('How To Use')}</a>`);
|
||||
|
||||
s = m.section(form.NamedSection, 'status', 'status', _('Status'));
|
||||
|
||||
o = s.option(form.Value, '_app_version', _('App Version'));
|
||||
o.readonly = true;
|
||||
o.load = function (section_id) {
|
||||
return appVersion.trim();
|
||||
};
|
||||
o.write = function () { };
|
||||
|
||||
o = s.option(form.Value, '_core_version', _('Core Version'));
|
||||
o.readonly = true;
|
||||
o.load = function (section_id) {
|
||||
return coreVersion.trim();
|
||||
};
|
||||
o.write = function () { };
|
||||
|
||||
o = s.option(form.DummyValue, '_core_status', _('Core Status'));
|
||||
o.cfgvalue = function (section_id) {
|
||||
return renderStatus(running);
|
||||
};
|
||||
poll.add(function () {
|
||||
return L.resolveDefault(mihomo.status()).then(function (running) {
|
||||
updateStatus(document.getElementById('core_status'), running);
|
||||
});
|
||||
});
|
||||
|
||||
o = s.option(form.Button, 'reload', '-');
|
||||
o.inputstyle = 'action';
|
||||
o.inputtitle = _('Reload Service');
|
||||
o.onclick = function () {
|
||||
return mihomo.reload();
|
||||
};
|
||||
|
||||
o = s.option(form.Button, 'restart', '-');
|
||||
o.inputstyle = 'negative';
|
||||
o.inputtitle = _('Restart Service');
|
||||
o.onclick = function () {
|
||||
return mihomo.restart();
|
||||
};
|
||||
|
||||
o = s.option(form.Button, 'update_dashboard', '-');
|
||||
o.inputstyle = 'positive';
|
||||
o.inputtitle = _('Update Dashboard');
|
||||
o.onclick = function () {
|
||||
return mihomo.callMihomoAPI('POST', '/upgrade/ui');
|
||||
};
|
||||
|
||||
o = s.option(form.Button, 'open_dashboard', '-');
|
||||
o.inputtitle = _('Open Dashboard');
|
||||
o.onclick = function () {
|
||||
return mihomo.openDashboard();
|
||||
};
|
||||
|
||||
s = m.section(form.NamedSection, 'config', 'config', _('Basic Config'));
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enable'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Flag, 'scheduled_restart', _('Scheduled Restart'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'cron_expression', _('Cron Expression'));
|
||||
o.retain = true;
|
||||
o.rmempty = false;
|
||||
o.depends('scheduled_restart', '1');
|
||||
|
||||
o = s.option(form.ListValue, 'profile', _('Choose Profile'));
|
||||
o.rmempty = false;
|
||||
|
||||
for (const profile of profiles) {
|
||||
o.value('file:' + profile.name, _('File:') + profile.name);
|
||||
}
|
||||
|
||||
for (const subscription of subscriptions) {
|
||||
o.value('subscription:' + subscription['.name'], _('Subscription:') + subscription.name);
|
||||
}
|
||||
|
||||
o = s.option(form.FileUpload, 'upload_profile', _('Upload Profile'));
|
||||
o.root_directory = mihomo.profilesDir;
|
||||
|
||||
o = s.option(form.Flag, 'mixin', _('Mixin'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Flag, 'test_profile', _('Test Profile'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Flag, 'fast_reload', _('Fast Reload'));
|
||||
o.rmempty = false;
|
||||
|
||||
s = m.section(form.NamedSection, 'proxy', 'proxy', _('Proxy Config'));
|
||||
|
||||
s.tab('transparent_proxy', _('Transparent Proxy'));
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'transparent_proxy', _('Enable'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.ListValue, 'tcp_transparent_proxy_mode', _('TCP Proxy Mode'));
|
||||
o.value('redirect', _('Redirect Mode'));
|
||||
o.value('tproxy', _('TPROXY Mode'));
|
||||
o.value('tun', _('TUN Mode'));
|
||||
|
||||
o = s.taboption('transparent_proxy', form.ListValue, 'udp_transparent_proxy_mode', _('UDP Proxy Mode'));
|
||||
o.value('tproxy', _('TPROXY Mode'));
|
||||
o.value('tun', _('TUN Mode'));
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'ipv4_dns_hijack', _('IPv4 DNS Hijack'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'ipv6_dns_hijack', _('IPv6 DNS Hijack'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'ipv4_proxy', _('IPv4 Proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'ipv6_proxy', _('IPv6 Proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'router_proxy', _('Router Proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'lan_proxy', _('Lan Proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
s.tab('access_control', _('Access Control'));
|
||||
|
||||
o = s.taboption('access_control', form.ListValue, 'access_control_mode', _('Mode'));
|
||||
o.value('all', _('All Mode'));
|
||||
o.value('allow', _('Allow Mode'));
|
||||
o.value('block', _('Block Mode'));
|
||||
|
||||
o = s.taboption('access_control', form.DynamicList, 'acl_ip', 'IP');
|
||||
o.datatype = 'ipmask4';
|
||||
o.retain = true;
|
||||
o.depends('access_control_mode', 'allow');
|
||||
o.depends('access_control_mode', 'block');
|
||||
|
||||
for (const mac in hosts) {
|
||||
const host = hosts[mac];
|
||||
for (const ip of host.ipaddrs) {
|
||||
const hint = host.name || mac;
|
||||
o.value(ip, hint ? '%s (%s)'.format(ip, hint) : ip);
|
||||
}
|
||||
}
|
||||
|
||||
o = s.taboption('access_control', form.DynamicList, 'acl_ip6', 'IP6');
|
||||
o.datatype = 'ipmask6';
|
||||
o.retain = true;
|
||||
o.depends('access_control_mode', 'allow');
|
||||
o.depends('access_control_mode', 'block');
|
||||
|
||||
for (const mac in hosts) {
|
||||
const host = hosts[mac];
|
||||
for (const ip of host.ip6addrs) {
|
||||
const hint = host.name || mac;
|
||||
o.value(ip, hint ? '%s (%s)'.format(ip, hint) : ip);
|
||||
}
|
||||
}
|
||||
|
||||
o = s.taboption('access_control', form.DynamicList, 'acl_mac', 'MAC');
|
||||
o.datatype = 'macaddr';
|
||||
o.retain = true;
|
||||
o.depends('access_control_mode', 'allow');
|
||||
o.depends('access_control_mode', 'block');
|
||||
|
||||
for (const mac in hosts) {
|
||||
const host = hosts[mac];
|
||||
const hint = host.name || host.ipaddrs[0];
|
||||
o.value(mac, hint ? '%s (%s)'.format(mac, hint) : mac);
|
||||
}
|
||||
|
||||
s.tab('bypass', _('Bypass'));
|
||||
|
||||
o = s.taboption('bypass', form.Flag, 'bypass_china_mainland_ip', _('Bypass China Mainland IP'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('bypass', form.Value, 'acl_tcp_dport', _('Destination TCP Port to Proxy'));
|
||||
o.rmempty = false;
|
||||
o.value('0-65535', _('All Port'));
|
||||
o.value('21 22 80 110 143 194 443 465 993 995 8080 8443', _('Commonly Used Port'));
|
||||
|
||||
o = s.taboption('bypass', form.Value, 'acl_udp_dport', _('Destination UDP Port to Proxy'));
|
||||
o.rmempty = false;
|
||||
o.value('0-65535', _('All Port'));
|
||||
o.value('123 443 8443', _('Commonly Used Port'));
|
||||
|
||||
s = m.section(form.TableSection, 'subscription', _('Subscription Config'));
|
||||
s.addremove = true;
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Value, 'name', _('Subscription Name'));
|
||||
o.rmempty = false;
|
||||
o.width = '15%';
|
||||
|
||||
o = s.option(form.Value, 'url', _('Subscription Url'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'user_agent', _('User Agent'));
|
||||
o.default = 'mihomo';
|
||||
o.rmempty = false;
|
||||
o.width = '15%';
|
||||
o.value('mihomo');
|
||||
o.value('clash.meta');
|
||||
o.value('clash');
|
||||
|
||||
s = m.section(form.NamedSection, 'mixin', 'mixin', _('Mixin Config'));
|
||||
|
||||
s.tab('general', _('General Config'));
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'log_level', _('Log Level'));
|
||||
o.value('silent');
|
||||
o.value('error');
|
||||
o.value('warning');
|
||||
o.value('info');
|
||||
o.value('debug');
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'mode', _('Proxy Mode'));
|
||||
o.value('general', _('Global Mode'));
|
||||
o.value('rule', _('Rule Mode'));
|
||||
o.value('direct', _('Direct Mode'));
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'match_process', _('Match Process'));
|
||||
o.value('strict', _('Auto'));
|
||||
o.value('always', _('Enable'));
|
||||
o.value('off', _('Disable'));
|
||||
|
||||
o = s.taboption('general', widgets.NetworkSelect, 'outbound_interface', _('Outbound Interface'));
|
||||
o.optional = true;
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.Flag, 'ipv6', _('IPv6'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.Value, 'tcp_keep_alive_idle', _('TCP Keep Alive Idle'));
|
||||
o.datatype = 'integer';
|
||||
o.placeholder = '600';
|
||||
|
||||
o = s.taboption('general', form.Value, 'tcp_keep_alive_interval', _('TCP Keep Alive Interval'));
|
||||
o.datatype = 'integer';
|
||||
o.placeholder = '15';
|
||||
|
||||
s.tab('external_control', _('External Control Config'));
|
||||
|
||||
o = s.taboption('external_control', form.Value, 'ui_name', _('UI Name'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('external_control', form.Value, 'ui_url', _('UI Url'));
|
||||
o.rmempty = false;
|
||||
o.value('https://mirror.ghproxy.com/https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip', 'MetaCubeXD')
|
||||
o.value('https://mirror.ghproxy.com/https://github.com/MetaCubeX/Yacd-meta/archive/refs/heads/gh-pages.zip', 'YACD')
|
||||
o.value('https://mirror.ghproxy.com/https://github.com/MetaCubeX/Razord-meta/archive/refs/heads/gh-pages.zip', 'Razord')
|
||||
|
||||
o = s.taboption('external_control', form.Value, 'api_port', _('API Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '9090';
|
||||
|
||||
o = s.taboption('external_control', form.Value, 'api_secret', _('API Secret'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('external_control', form.Flag, 'selection_cache', _('Save Proxy Selection'));
|
||||
o.rmempty = false;
|
||||
|
||||
s.tab('inbound', _('Inbound Config'));
|
||||
|
||||
o = s.taboption('inbound', form.Flag, 'allow_lan', _('Allow Lan'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('inbound', form.Value, 'http_port', _('HTTP Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '8080';
|
||||
|
||||
o = s.taboption('inbound', form.Value, 'socks_port', _('SOCKS Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '1080';
|
||||
|
||||
o = s.taboption('inbound', form.Value, 'mixed_port', _('Mixed Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '7890';
|
||||
|
||||
o = s.taboption('inbound', form.Value, 'redir_port', _('Redirect Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '7891';
|
||||
|
||||
o = s.taboption('inbound', form.Value, 'tproxy_port', _('TPROXY Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '7892';
|
||||
|
||||
o = s.taboption('inbound', form.Flag, 'authentication', _('Authentication'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('inbound', form.SectionValue, '_authentications', form.TableSection, 'authentication', _('Edit Authentications'));
|
||||
o.retain = true;
|
||||
o.depends('authentication', '1');
|
||||
|
||||
o.subsection.anonymous = true;
|
||||
o.subsection.addremove = true;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'enabled', _('Enable'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.Value, 'username', _('Username'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.Value, 'password', _('Password'));
|
||||
so.rmempty = false;
|
||||
|
||||
s.tab('tun', _('TUN Config'));
|
||||
|
||||
o = s.taboption('tun', form.ListValue, 'tun_stack', _('Stack'));
|
||||
o.value('system', 'System');
|
||||
o.value('gvisor', 'gVisor');
|
||||
o.value('mixed', 'Mixed');
|
||||
|
||||
o = s.taboption('tun', form.Value, 'tun_mtu', _('MTU'));
|
||||
o.placeholder = '9000';
|
||||
|
||||
o = s.taboption('tun', form.Flag, 'tun_gso', _('GSO'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('tun', form.Value, 'tun_gso_max_size', _('GSO Max Size'));
|
||||
o.placeholder = '65536';
|
||||
o.depends('tun_gso', '1');
|
||||
|
||||
o = s.taboption('tun', form.Flag, 'tun_endpoint_independent_nat', _('Endpoint Independent NAT'));
|
||||
o.rmempty = false;
|
||||
|
||||
s.tab('dns', _('DNS Config'));
|
||||
|
||||
o = s.taboption('dns', form.Value, 'dns_port', _('DNS Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '1053';
|
||||
|
||||
o = s.taboption('dns', form.ListValue, 'dns_mode', _('DNS Mode'));
|
||||
o.value('normal', 'Normal');
|
||||
o.value('fake-ip', 'Fake-IP');
|
||||
o.value('redir-host', 'Redir-Host');
|
||||
|
||||
o = s.taboption('dns', form.Value, 'fake_ip_range', _('Fake-IP Range'));
|
||||
o.datatype = 'cidr4';
|
||||
o.placeholder = '198.18.0.1/16';
|
||||
o.retain = true;
|
||||
o.depends('dns_mode', 'fake-ip');
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'fake_ip_filter', _('Overwrite Fake-IP Filter'));
|
||||
o.retain = true;
|
||||
o.rmempty = false;
|
||||
o.depends('dns_mode', 'fake-ip');
|
||||
|
||||
o = s.taboption('dns', form.DynamicList, 'fake_ip_filters', _('Edit Fake-IP Filters'));
|
||||
o.retain = true;
|
||||
o.depends({ 'dns_mode': 'fake-ip', 'fake_ip_filter': '1' });
|
||||
|
||||
o = s.taboption('dns', form.ListValue, 'fake_ip_filter_mode', _('Fake-IP Filter Mode'))
|
||||
o.value('blacklist', _('Block Mode'));
|
||||
o.value('whitelist', _('Allow Mode'));
|
||||
o.depends({ 'dns_mode': 'fake-ip', 'fake_ip_filter': '1' });
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'fake_ip_cache', _('Fake-IP Cache'));
|
||||
o.retain = true;
|
||||
o.rmempty = false;
|
||||
o.depends('dns_mode', 'fake-ip');
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_respect_rules', _('Respect Rules'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_ipv6', _('IPv6'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_system_hosts', _('Use System Hosts'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_hosts', _('Use Hosts'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'hosts', _('Overwrite Hosts'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.SectionValue, '_hosts', form.TableSection, 'host', _('Edit Hosts'));
|
||||
o.retain = true;
|
||||
o.depends('hosts', '1');
|
||||
|
||||
o.subsection.anonymous = true;
|
||||
o.subsection.addremove = true;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'enabled', _('Enable'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.Value, 'domain_name', _('Domain Name'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'ip', _('IP'));
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_nameserver', _('Overwrite Nameserver'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.SectionValue, '_dns_nameserver', form.TableSection, 'nameserver', _('Edit Nameservers'));
|
||||
o.retain = true;
|
||||
o.depends('dns_nameserver', '1');
|
||||
|
||||
o.subsection.anonymous = true;
|
||||
o.subsection.addremove = false;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'enabled', _('Enable'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.ListValue, 'type', _('Type'));
|
||||
so.readonly = true;
|
||||
so.value('default-nameserver');
|
||||
so.value('proxy-server-nameserver');
|
||||
so.value('nameserver');
|
||||
so.value('fallback');
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'nameserver', _('Nameserver'));
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_nameserver_policy', _('Overwrite Nameserver Policy'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.SectionValue, '_dns_nameserver_policies', form.TableSection, 'nameserver_policy', _('Edit Nameserver Policies'));
|
||||
o.retain = true;
|
||||
o.depends('dns_nameserver_policy', '1');
|
||||
|
||||
o.subsection.anonymous = true;
|
||||
o.subsection.addremove = true;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'enabled', _('Enable'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.Value, 'matcher', _('Matcher'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'nameserver', _('Nameserver'));
|
||||
|
||||
s.tab('geox', _('GeoX Config'));
|
||||
|
||||
o = s.taboption('geox', form.ListValue, 'geoip_format', _('GeoIP Format'));
|
||||
o.value('dat', 'DAT');
|
||||
o.value('mmdb', 'MMDB');
|
||||
|
||||
o = s.taboption('geox', form.ListValue, 'geodata_loader', _('GeoData Loader'));
|
||||
o.value('standard', _('Standard Loader'));
|
||||
o.value('memconservative', _('Memory Conservative Loader'));
|
||||
|
||||
o = s.taboption('geox', form.Value, 'geosite_url', _('GeoSite Url'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('geox', form.Value, 'geoip_mmdb_url', _('GeoIP(MMDB) Url'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('geox', form.Value, 'geoip_dat_url', _('GeoIP(DAT) Url'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('geox', form.Value, 'geoip_asn_url', _('GeoIP(ASN) Url'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('geox', form.Flag, 'geox_auto_update', _('GeoX Auto Update'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('geox', form.Value, 'geox_update_interval', _('GeoX Update Interval'), _('Hour'));
|
||||
o.datatype = 'integer';
|
||||
o.placeholder = '24';
|
||||
o.retain = true;
|
||||
o.depends('geox_auto_update', '1');
|
||||
|
||||
s.tab('mixin_file_content', _('Mixin File Content'), _('Please go to the editor tab to edit the file for mixin'));
|
||||
|
||||
o = s.taboption('mixin_file_content', form.HiddenValue, '_mixin_file_content');
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require tools.mihomo as mihomo'
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load('mihomo'),
|
||||
mihomo.listProfiles(),
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const profiles = data[1];
|
||||
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('mihomo');
|
||||
|
||||
s = m.section(form.NamedSection, 'editor', 'editor');
|
||||
|
||||
o = s.option(form.ListValue, '_profile', _('Choose Profile'));
|
||||
o.optional = true;
|
||||
|
||||
for (const profile of profiles) {
|
||||
o.value(mihomo.profilesDir + '/' + profile.name, _('File:') + profile.name);
|
||||
}
|
||||
o.value(mihomo.mixinFilePath, _('File for Mixin'));
|
||||
o.value(mihomo.runProfilePath, _('Profile for Startup'));
|
||||
o.value(mihomo.reservedIPNFT, _('File for Reserved IP'));
|
||||
o.value(mihomo.reservedIP6NFT, _('File for Reserved IP6'));
|
||||
|
||||
o.write = function (section_id, formvalue) {
|
||||
return true;
|
||||
};
|
||||
o.onchange = function (event, section_id, value) {
|
||||
return L.resolveDefault(fs.read_direct(value), '').then(function (content) {
|
||||
m.lookupOption('mihomo.editor._profile_content')[0].getUIElement('editor').setValue(content);
|
||||
});
|
||||
};
|
||||
|
||||
o = s.option(form.TextValue, '_profile_content',);
|
||||
o.rows = 25;
|
||||
o.write = function (section_id, formvalue) {
|
||||
const path = m.lookupOption('mihomo.editor._profile')[0].formvalue('editor');
|
||||
return fs.write(path, formvalue);
|
||||
};
|
||||
o.remove = function (section_id) {
|
||||
const path = m.lookupOption('mihomo.editor._profile')[0].formvalue('editor');
|
||||
return fs.write(path);
|
||||
};
|
||||
|
||||
return m.render();
|
||||
},
|
||||
handleSaveApply: function (ev, mode) {
|
||||
return this.handleSave(ev).finally(function() {
|
||||
return mode === '0' ? mihomo.reload() : mihomo.restart();
|
||||
});
|
||||
},
|
||||
handleReset: null
|
||||
});
|
|
@ -0,0 +1,98 @@
|
|||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require poll';
|
||||
'require tools.mihomo as mihomo'
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load('mihomo'),
|
||||
mihomo.getAppLog(),
|
||||
mihomo.getCoreLog()
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const appLog = data[1];
|
||||
const coreLog = data[2];
|
||||
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('mihomo');
|
||||
|
||||
s = m.section(form.NamedSection, 'log', 'log');
|
||||
|
||||
s.tab('app_log', _('App Log'));
|
||||
|
||||
o = s.taboption('app_log', form.Button, 'clear_app_log');
|
||||
o.inputstyle = 'negative';
|
||||
o.inputtitle = _('Clear Log');
|
||||
o.onclick = function () {
|
||||
m.lookupOption('mihomo.log._app_log')[0].getUIElement('log').setValue('');
|
||||
return mihomo.clearAppLog();
|
||||
};
|
||||
|
||||
o = s.taboption('app_log', form.TextValue, '_app_log');
|
||||
o.rows = 25;
|
||||
o.wrap = false;
|
||||
o.load = function (section_id) {
|
||||
return appLog;
|
||||
};
|
||||
o.write = function (section_id, formvalue) {
|
||||
return true;
|
||||
};
|
||||
poll.add(L.bind(function () {
|
||||
const option = this;
|
||||
return L.resolveDefault(mihomo.getAppLog()).then(function (log) {
|
||||
option.getUIElement('log').setValue(log);
|
||||
});
|
||||
}, o));
|
||||
|
||||
o = s.taboption('app_log', form.Button, 'scroll_app_log_to_bottom');
|
||||
o.inputtitle = _('Scroll To Bottom');
|
||||
o.onclick = function () {
|
||||
const element = m.lookupOption('mihomo.log._app_log')[0].getUIElement('log').node.firstChild;
|
||||
element.scrollTop = element.scrollHeight;
|
||||
};
|
||||
|
||||
s.tab('core_log', _('Core Log'));
|
||||
|
||||
o = s.taboption('core_log', form.Button, 'clear_core_log');
|
||||
o.inputstyle = 'negative';
|
||||
o.inputtitle = _('Clear Log');
|
||||
o.onclick = function () {
|
||||
m.lookupOption('mihomo.log._core_log')[0].getUIElement('log').setValue('');
|
||||
return mihomo.clearCoreLog();
|
||||
};
|
||||
|
||||
o = s.taboption('core_log', form.TextValue, '_core_log');
|
||||
o.rows = 25;
|
||||
o.wrap = false;
|
||||
o.load = function (section_id) {
|
||||
return coreLog;
|
||||
};
|
||||
o.write = function (section_id, formvalue) {
|
||||
return true;
|
||||
};
|
||||
poll.add(L.bind(function () {
|
||||
const option = this;
|
||||
return L.resolveDefault(mihomo.getCoreLog()).then(function (log) {
|
||||
option.getUIElement('log').setValue(log);
|
||||
});
|
||||
}, o));
|
||||
|
||||
o = s.taboption('core_log', form.Button, 'scroll_core_log_to_bottom');
|
||||
o.inputtitle = _('Scroll To Bottom');
|
||||
o.onclick = function () {
|
||||
const element = m.lookupOption('mihomo.log._core_log')[0].getUIElement('log').node.firstChild;
|
||||
element.scrollTop = element.scrollHeight;
|
||||
};
|
||||
|
||||
return m.render();
|
||||
},
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
zh_Hans
|
|
@ -0,0 +1,416 @@
|
|||
msgid "MihomoTProxy"
|
||||
msgstr "MihomoTProxy"
|
||||
|
||||
msgid "Transparent Proxy with Mihomo on OpenWrt."
|
||||
msgstr "在 OpenWrt 上使用 Mihomo 进行透明代理。"
|
||||
|
||||
msgid "How To Use"
|
||||
msgstr "使用说明"
|
||||
|
||||
msgid "Config"
|
||||
msgstr "配置"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "状态"
|
||||
|
||||
msgid "App Version"
|
||||
msgstr "插件版本"
|
||||
|
||||
msgid "Core Version"
|
||||
msgstr "核心版本"
|
||||
|
||||
msgid "Core Status"
|
||||
msgstr "核心状态"
|
||||
|
||||
msgid "Running"
|
||||
msgstr "运行中"
|
||||
|
||||
msgid "Not Running"
|
||||
msgstr "未在运行"
|
||||
|
||||
msgid "Reload Service"
|
||||
msgstr "重载服务"
|
||||
|
||||
msgid "Restart Service"
|
||||
msgstr "重启服务"
|
||||
|
||||
msgid "Update Dashboard"
|
||||
msgstr "更新面板"
|
||||
|
||||
msgid "Open Dashboard"
|
||||
msgstr "打开面板"
|
||||
|
||||
msgid "Basic Config"
|
||||
msgstr "基础配置"
|
||||
|
||||
msgid "Enable"
|
||||
msgstr "启用"
|
||||
|
||||
msgid "Disable"
|
||||
msgstr "禁用"
|
||||
|
||||
msgid "Auto"
|
||||
msgstr "自动"
|
||||
|
||||
msgid "Mode"
|
||||
msgstr "模式"
|
||||
|
||||
msgid "Type"
|
||||
msgstr "类型"
|
||||
|
||||
msgid "Value"
|
||||
msgstr "值"
|
||||
|
||||
msgid "Scheduled Restart"
|
||||
msgstr "定时重启"
|
||||
|
||||
msgid "Cron Expression"
|
||||
msgstr "Cron 表达式"
|
||||
|
||||
msgid "Choose Profile"
|
||||
msgstr "选择配置文件"
|
||||
|
||||
msgid "File:"
|
||||
msgstr "文件:"
|
||||
|
||||
msgid "Subscription:"
|
||||
msgstr "订阅:"
|
||||
|
||||
msgid "Upload Profile"
|
||||
msgstr "上传配置文件"
|
||||
|
||||
msgid "Mixin"
|
||||
msgstr "混入"
|
||||
|
||||
msgid "Test Profile"
|
||||
msgstr "检查配置文件"
|
||||
|
||||
msgid "Fast Reload"
|
||||
msgstr "快速重载"
|
||||
|
||||
msgid "Proxy Config"
|
||||
msgstr "代理配置"
|
||||
|
||||
msgid "Transparent Proxy"
|
||||
msgstr "透明代理"
|
||||
|
||||
msgid "TCP Proxy Mode"
|
||||
msgstr "TCP 代理模式"
|
||||
|
||||
msgid "UDP Proxy Mode"
|
||||
msgstr "UDP 代理模式"
|
||||
|
||||
msgid "Redirect Mode"
|
||||
msgstr "Redirect 模式"
|
||||
|
||||
msgid "TPROXY Mode"
|
||||
msgstr "TPROXY 模式"
|
||||
|
||||
msgid "TUN Mode"
|
||||
msgstr "TUN 模式"
|
||||
|
||||
msgid "IPv4 DNS Hijack"
|
||||
msgstr "IPv4 DNS 劫持"
|
||||
|
||||
msgid "IPv6 DNS Hijack"
|
||||
msgstr "IPv6 DNS 劫持"
|
||||
|
||||
msgid "IPv4 Proxy"
|
||||
msgstr "IPv4 代理"
|
||||
|
||||
msgid "IPv6 Proxy"
|
||||
msgstr "IPv6 代理"
|
||||
|
||||
msgid "Router Proxy"
|
||||
msgstr "路由器代理"
|
||||
|
||||
msgid "Lan Proxy"
|
||||
msgstr "局域网代理"
|
||||
|
||||
msgid "Access Control"
|
||||
msgstr "访问控制"
|
||||
|
||||
msgid "All Mode"
|
||||
msgstr "全部模式"
|
||||
|
||||
msgid "Allow Mode"
|
||||
msgstr "白名单模式"
|
||||
|
||||
msgid "Block Mode"
|
||||
msgstr "黑名单模式"
|
||||
|
||||
msgid "Bypass"
|
||||
msgstr "绕过"
|
||||
|
||||
msgid "Bypass China Mainland IP"
|
||||
msgstr "绕过中国大陆 IP"
|
||||
|
||||
msgid "Destination TCP Port to Proxy"
|
||||
msgstr "要代理的 TCP 目标端口"
|
||||
|
||||
msgid "Destination UDP Port to Proxy"
|
||||
msgstr "要代理的 UDP 目标端口"
|
||||
|
||||
msgid "All Port"
|
||||
msgstr "全部端口"
|
||||
|
||||
msgid "Commonly Used Port"
|
||||
msgstr "常用端口"
|
||||
|
||||
msgid "Subscription Config"
|
||||
msgstr "订阅配置"
|
||||
|
||||
msgid "Subscription Name"
|
||||
msgstr "订阅名称"
|
||||
|
||||
msgid "Subscription Url"
|
||||
msgstr "订阅链接"
|
||||
|
||||
msgid "User Agent"
|
||||
msgstr "用户代理(UA)"
|
||||
|
||||
msgid "Mixin Config"
|
||||
msgstr "混入配置"
|
||||
|
||||
msgid "General Config"
|
||||
msgstr "全局配置"
|
||||
|
||||
msgid "Proxy Mode"
|
||||
msgstr "代理模式"
|
||||
|
||||
msgid "Global Mode"
|
||||
msgstr "全局模式"
|
||||
|
||||
msgid "Rule Mode"
|
||||
msgstr "规则模式"
|
||||
|
||||
msgid "Direct Mode"
|
||||
msgstr "直连模式"
|
||||
|
||||
msgid "Match Process"
|
||||
msgstr "匹配进程"
|
||||
|
||||
msgid "Outbound Interface"
|
||||
msgstr "出站接口"
|
||||
|
||||
msgid "TCP Keep Alive Idle"
|
||||
msgstr "TCP Keep Alive 空闲"
|
||||
|
||||
msgid "TCP Keep Alive Interval"
|
||||
msgstr "TCP Keep Alive 间隔"
|
||||
|
||||
msgid "Log Level"
|
||||
msgstr "日志级别"
|
||||
|
||||
msgid "External Control Config"
|
||||
msgstr "外部控制配置"
|
||||
|
||||
msgid "UI Name"
|
||||
msgstr "UI 名称"
|
||||
|
||||
msgid "UI Url"
|
||||
msgstr "UI 下载地址"
|
||||
|
||||
msgid "Service is not running."
|
||||
msgstr "服务未在运行。"
|
||||
|
||||
msgid "API Port"
|
||||
msgstr "API 端口"
|
||||
|
||||
msgid "API Secret"
|
||||
msgstr "API 密钥"
|
||||
|
||||
msgid "Save Proxy Selection"
|
||||
msgstr "保存节点/策略组选择"
|
||||
|
||||
msgid "Inbound Config"
|
||||
msgstr "入站配置"
|
||||
|
||||
msgid "Allow Lan"
|
||||
msgstr "允许局域网访问"
|
||||
|
||||
msgid "HTTP Port"
|
||||
msgstr "HTTP 端口"
|
||||
|
||||
msgid "SOCKS Port"
|
||||
msgstr "SOCKS 端口"
|
||||
|
||||
msgid "Mixed Port"
|
||||
msgstr "混合端口"
|
||||
|
||||
msgid "Redirect Port"
|
||||
msgstr "Redirect 端口"
|
||||
|
||||
msgid "TPROXY Port"
|
||||
msgstr "TPROXY 端口"
|
||||
|
||||
msgid "Authentication"
|
||||
msgid "身份验证"
|
||||
|
||||
msgid "Edit Authentications"
|
||||
msgstr "编辑身份验证"
|
||||
|
||||
msgid "username"
|
||||
msgstr "用户名"
|
||||
|
||||
msgid "password"
|
||||
msgstr "密码"
|
||||
|
||||
msgid "TUN Config"
|
||||
msgstr "TUN 配置"
|
||||
|
||||
msgid "Stack"
|
||||
msgstr "栈"
|
||||
|
||||
msgid "Device"
|
||||
msgstr "设备"
|
||||
|
||||
msgid "MTU"
|
||||
msgstr "最大传输单元"
|
||||
|
||||
msgid "GSO"
|
||||
msgstr "通用分段卸载"
|
||||
|
||||
msgid "GSO Max Size"
|
||||
msgstr "分段最大长度"
|
||||
|
||||
msgid "Endpoint Independent NAT"
|
||||
msgstr "独立于端点的 NAT"
|
||||
|
||||
msgid "DNS Config"
|
||||
msgstr "DNS 配置"
|
||||
|
||||
msgid "DNS Port"
|
||||
msgstr "DNS 端口"
|
||||
|
||||
msgid "DNS Mode"
|
||||
msgstr "DNS 模式"
|
||||
|
||||
msgid "Fake-IP Range"
|
||||
msgstr "Fake-IP 范围"
|
||||
|
||||
msgid "Overwrite Fake-IP Filter"
|
||||
msgstr "覆盖 Fake-IP 过滤列表"
|
||||
|
||||
msgid "Edit Fake-IP Filters"
|
||||
msgstr "编辑 Fake-IP 过滤列表"
|
||||
|
||||
msgid "Fake-IP Filter Mode"
|
||||
msgstr "Fake-IP 过滤模式"
|
||||
|
||||
msgid "Fake-IP Cache"
|
||||
msgstr "Fake-IP 缓存"
|
||||
|
||||
msgid "Respect Rules"
|
||||
msgstr "遵循分流规则"
|
||||
|
||||
msgid "Use System Hosts"
|
||||
msgstr "使用系统的 Hosts"
|
||||
|
||||
msgid "Use Hosts"
|
||||
msgstr "使用 Hosts"
|
||||
|
||||
msgid "Overwrite Hosts"
|
||||
msgstr "覆盖 Hosts"
|
||||
|
||||
msgid "Edit Hosts"
|
||||
msgstr "编辑 Hosts"
|
||||
|
||||
msgid "Domain Name"
|
||||
msgstr "域名"
|
||||
|
||||
msgid "Overwrite Nameserver"
|
||||
msgstr "覆盖 DNS 服务器"
|
||||
|
||||
msgid "Edit Nameservers"
|
||||
msgstr "编辑 DNS 服务器"
|
||||
|
||||
msgid "Nameserver"
|
||||
msgstr "DNS 服务器"
|
||||
|
||||
msgid "Overwrite Fallback Filter"
|
||||
msgstr "覆盖 Fallback 过滤列表"
|
||||
|
||||
msgid "Edit Fallback Filters"
|
||||
msgstr "编辑 Fallback 过滤列表"
|
||||
|
||||
msgid "Overwrite Nameserver Policy"
|
||||
msgstr "覆盖 DNS 服务器查询策略"
|
||||
|
||||
msgid "Edit Nameserver Policies"
|
||||
msgstr "编辑 DNS 服务器查询策略"
|
||||
|
||||
msgid "Matcher"
|
||||
msgstr "匹配"
|
||||
|
||||
msgid "GeoX Config"
|
||||
msgstr "GeoX 配置"
|
||||
|
||||
msgid "GeoIP Format"
|
||||
msgstr "GeoIP 格式"
|
||||
|
||||
msgid "GeoData Loader"
|
||||
msgstr "GeoData 加载器"
|
||||
|
||||
msgid "Standard Loader"
|
||||
msgstr "标准加载器"
|
||||
|
||||
msgid "Memory Conservative Loader"
|
||||
msgstr "为内存受限设备优化的加载器"
|
||||
|
||||
msgid "GeoSite Url"
|
||||
msgstr "GeoSite 下载地址"
|
||||
|
||||
msgid "GeoIP(MMDB) Url"
|
||||
msgstr "GeoIP(MMDB) 下载地址"
|
||||
|
||||
msgid "GeoIP(DAT) Url"
|
||||
msgstr "GeoIP(DAT) 下载地址"
|
||||
|
||||
msgid "GeoIP(ASN) Url"
|
||||
msgstr "GeoIP(ASN) 下载地址"
|
||||
|
||||
msgid "GeoX Auto Update"
|
||||
msgstr "定时更新GeoX文件"
|
||||
|
||||
msgid "GeoX Update Interval"
|
||||
msgstr "GeoX 文件更新间隔"
|
||||
|
||||
msgid "Hour"
|
||||
msgstr "小时"
|
||||
|
||||
msgid "Mixin File Content"
|
||||
msgstr "混入文件内容"
|
||||
|
||||
msgid "Please go to the editor tab to edit the file for mixin"
|
||||
msgstr "请前往编辑器标签编辑用于混入的文件"
|
||||
|
||||
msgid "Editor"
|
||||
msgstr "编辑器"
|
||||
|
||||
msgid "File for Mixin"
|
||||
msgstr "用于混入的文件"
|
||||
|
||||
msgid "Profile for Startup"
|
||||
msgstr "用于启动的配置文件"
|
||||
|
||||
msgid "File for Reserved IP"
|
||||
msgstr "IPv4 保留地址"
|
||||
|
||||
msgid "File for Reserved IP6"
|
||||
msgstr "IPv6 保留地址"
|
||||
|
||||
msgid "Log"
|
||||
msgstr "日志"
|
||||
|
||||
msgid "App Log"
|
||||
msgstr "应用日志"
|
||||
|
||||
msgid "Core Log"
|
||||
msgstr "核心日志"
|
||||
|
||||
msgid "Clear Log"
|
||||
msgstr "清空日志"
|
||||
|
||||
msgid "Scroll To Bottom"
|
||||
msgstr "滚动到底部"
|
|
@ -0,0 +1,46 @@
|
|||
#!/bin/sh
|
||||
|
||||
. $IPKG_INSTROOT/etc/mihomo/scripts/constants.sh
|
||||
|
||||
action=$1
|
||||
shift
|
||||
|
||||
case "$action" in
|
||||
clear)
|
||||
case "$1" in
|
||||
app_log)
|
||||
echo -n > "$RUN_APP_LOG_PATH"
|
||||
;;
|
||||
core_log)
|
||||
echo -n > "$RUN_CORE_LOG_PATH"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
load)
|
||||
case "$1" in
|
||||
profile)
|
||||
yq -M -o json < "$RUN_PROFILE_PATH"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
service)
|
||||
case "$1" in
|
||||
reload)
|
||||
/etc/init.d/mihomo reload
|
||||
;;
|
||||
restart)
|
||||
/etc/init.d/mihomo restart
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
version)
|
||||
case "$1" in
|
||||
app)
|
||||
opkg list-installed | grep "luci-app-mihomo" | cut -d " " -f 3
|
||||
;;
|
||||
core)
|
||||
mihomo -v | grep "Mihomo" | cut -d " " -f 3
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"admin/services/mihomo": {
|
||||
"title": "MihomoTProxy",
|
||||
"action": {
|
||||
"type": "alias",
|
||||
"path": "admin/services/mihomo/config"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-mihomo" ],
|
||||
"uci": { "mihomo": true }
|
||||
}
|
||||
},
|
||||
"admin/services/mihomo/config": {
|
||||
"title": "Config",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "mihomo/config"
|
||||
}
|
||||
},
|
||||
"admin/services/mihomo/editor": {
|
||||
"title": "Editor",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "mihomo/editor"
|
||||
}
|
||||
},
|
||||
"admin/services/mihomo/log": {
|
||||
"title": "Log",
|
||||
"order": 30,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "mihomo/log"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"luci-app-mihomo": {
|
||||
"description": "Grant access to mihomo procedures",
|
||||
"read": {
|
||||
"uci": [ "mihomo" ],
|
||||
"ubus": {
|
||||
"service": [ "list" ]
|
||||
},
|
||||
"file": {
|
||||
"/etc/mihomo/profiles/*.yaml": ["read"],
|
||||
"/etc/mihomo/profiles/*.yml": ["read"],
|
||||
"/etc/mihomo/mixin.yaml": ["read"],
|
||||
"/etc/mihomo/run/config.yaml": ["read"],
|
||||
"/etc/mihomo/nftables/reserved_ip.nft": ["read"],
|
||||
"/etc/mihomo/nftables/reserved_ip6.nft": ["read"],
|
||||
"/etc/mihomo/run/*.log": ["read"],
|
||||
"/usr/libexec/mihomo-call": ["exec"]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "mihomo" ],
|
||||
"file": {
|
||||
"/etc/mihomo/profiles/*.yaml": ["write"],
|
||||
"/etc/mihomo/profiles/*.yml": ["write"],
|
||||
"/etc/mihomo/mixin.yaml": ["write"],
|
||||
"/etc/mihomo/run/config.yaml": ["write"],
|
||||
"/etc/mihomo/nftables/reserved_ip.nft": ["write"],
|
||||
"/etc/mihomo/nftables/reserved_ip6.nft": ["write"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=mihomo
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/MetaCubeX/mihomo.git
|
||||
PKG_SOURCE_DATE:=2024-09-07
|
||||
PKG_SOURCE_VERSION:=ade4234615e7747ff79e98b27ff117a05c8110fa
|
||||
PKG_MIRROR_HASH:=b68d0af02c4f06672deee7abbf96078975c710dc73a02a3eeb3ff5c67e5d74e5
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_MAINTAINER:=Joseph Mory <morytyann@gmail.com>
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
PKG_BUILD_VERSION:=alpha-$(shell printf '%.8s' $(PKG_SOURCE_VERSION))
|
||||
PKG_BUILD_TIME:=$(shell date -u -Iseconds)
|
||||
|
||||
GO_PKG:=github.com/metacubex/mihomo
|
||||
GO_PKG_LDFLAGS_X:=$(GO_PKG)/constant.Version=$(PKG_BUILD_VERSION) $(GO_PKG)/constant.BuildTime=$(PKG_BUILD_TIME)
|
||||
GO_PKG_TAGS:=with_gvisor
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
|
||||
|
||||
define Package/mihomo
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=A rule based proxy in Go.
|
||||
URL:=https://wiki.metacubex.one
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle +curl +yq firewall4 +kmod-nft-tproxy +ip-full +kmod-tun
|
||||
USERID:=mihomo=7890:mihomo=7890
|
||||
endef
|
||||
|
||||
define Package/mihomo/description
|
||||
A rule based proxy in Go.
|
||||
endef
|
||||
|
||||
define Package/mihomo/conffiles
|
||||
/etc/config/mihomo
|
||||
/etc/mihomo/mixin.yaml
|
||||
/etc/mihomo/nftables/reserved_ip.nft
|
||||
/etc/mihomo/nftables/reserved_ip6.nft
|
||||
endef
|
||||
|
||||
define Package/mihomo/install
|
||||
$(call GoPackage/Package/Install/Bin,$(1))
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/mihomo
|
||||
$(INSTALL_DIR) $(1)/etc/mihomo/scripts
|
||||
$(INSTALL_DIR) $(1)/etc/mihomo/nftables
|
||||
$(INSTALL_DIR) $(1)/etc/mihomo/profiles
|
||||
$(INSTALL_DIR) $(1)/etc/mihomo/run
|
||||
$(INSTALL_DIR) $(1)/etc/mihomo/run/rules
|
||||
$(INSTALL_DIR) $(1)/etc/mihomo/run/ui
|
||||
|
||||
$(INSTALL_DATA) $(CURDIR)/files/mixin.yaml $(1)/etc/mihomo/mixin.yaml
|
||||
|
||||
$(INSTALL_BIN) $(CURDIR)/files/scripts/constants.sh $(1)/etc/mihomo/scripts/constants.sh
|
||||
$(INSTALL_BIN) $(CURDIR)/files/scripts/tun.sh $(1)/etc/mihomo/scripts/tun.sh
|
||||
|
||||
$(INSTALL_BIN) $(CURDIR)/files/nftables/hijack.nft $(1)/etc/mihomo/nftables/hijack.nft
|
||||
$(INSTALL_BIN) $(CURDIR)/files/nftables/reserved_ip.nft $(1)/etc/mihomo/nftables/reserved_ip.nft
|
||||
$(INSTALL_BIN) $(CURDIR)/files/nftables/reserved_ip6.nft $(1)/etc/mihomo/nftables/reserved_ip6.nft
|
||||
$(INSTALL_BIN) $(CURDIR)/files/nftables/geoip_cn.nft $(1)/etc/mihomo/nftables/geoip_cn.nft
|
||||
$(INSTALL_BIN) $(CURDIR)/files/nftables/geoip6_cn.nft $(1)/etc/mihomo/nftables/geoip6_cn.nft
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) $(CURDIR)/files/mihomo.conf $(1)/etc/config/mihomo
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) $(CURDIR)/files/mihomo.init $(1)/etc/init.d/mihomo
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
$(INSTALL_BIN) $(CURDIR)/files/uci-defaults/init.sh $(1)/etc/uci-defaults/99_init_mihomo
|
||||
$(INSTALL_BIN) $(CURDIR)/files/uci-defaults/migrate.sh $(1)/etc/uci-defaults/99_migrate_mihomo
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/capabilities
|
||||
$(INSTALL_DATA) $(CURDIR)/files/capabilities.json $(1)/etc/capabilities/mihomo.json
|
||||
|
||||
$(INSTALL_DIR) $(1)/lib/upgrade/keep.d
|
||||
$(INSTALL_DATA) $(CURDIR)/files/mihomo.upgrade $(1)/lib/upgrade/keep.d/mihomo
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
$(Build/Prepare/Default)
|
||||
$(RM) -r $(PKG_BUILD_DIR)/rules/logic_test
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,mihomo))
|
||||
$(eval $(call BuildPackage,mihomo))
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"permitted": [
|
||||
"CAP_FOWNER",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_RAW"
|
||||
],
|
||||
"effective": [
|
||||
"CAP_FOWNER",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_RAW"
|
||||
],
|
||||
"bounding": [
|
||||
"CAP_FOWNER",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_RAW"
|
||||
],
|
||||
"inheritable": [
|
||||
"CAP_FOWNER",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_RAW"
|
||||
],
|
||||
"ambient": [
|
||||
"CAP_FOWNER",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_RAW"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
config status 'status'
|
||||
|
||||
config config 'config'
|
||||
option 'init' '1'
|
||||
option 'enabled' '0'
|
||||
option 'scheduled_restart' '0'
|
||||
option 'cron_expression' '0 3 * * *'
|
||||
option 'profile' 'subscription:subscription'
|
||||
option 'mixin' '1'
|
||||
option 'test_profile' '1'
|
||||
|
||||
config proxy 'proxy'
|
||||
option 'transparent_proxy' '1'
|
||||
option 'tcp_transparent_proxy_mode' 'tproxy'
|
||||
option 'udp_transparent_proxy_mode' 'tproxy'
|
||||
option 'ipv4_dns_hijack' '1'
|
||||
option 'ipv6_dns_hijack' '1'
|
||||
option 'ipv4_proxy' '1'
|
||||
option 'ipv6_proxy' '0'
|
||||
option 'router_proxy' '1'
|
||||
option 'lan_proxy' '1'
|
||||
option 'access_control_mode' 'all'
|
||||
option 'acl_ip' ''
|
||||
option 'acl_ip6' ''
|
||||
option 'acl_mac' ''
|
||||
option 'bypass_china_mainland_ip' '0'
|
||||
option 'acl_tcp_dport' '0-65535'
|
||||
option 'acl_udp_dport' '0-65535'
|
||||
|
||||
config subscription 'subscription'
|
||||
option 'name' 'default'
|
||||
option 'url' 'http://example.com/default.yaml'
|
||||
option 'user_agent' 'mihomo'
|
||||
|
||||
config mixin 'mixin'
|
||||
option 'log_level' 'info'
|
||||
option 'mode' 'rule'
|
||||
option 'match_process' 'off'
|
||||
option 'outbound_interface' ''
|
||||
option 'ipv6' '0'
|
||||
option 'tcp_keep_alive_idle' '600'
|
||||
option 'tcp_keep_alive_interval' '15'
|
||||
option 'ui_name' 'metacubexd'
|
||||
option 'ui_url' 'https://mirror.ghproxy.com/https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip'
|
||||
option 'api_port' '9090'
|
||||
option 'api_secret' ''
|
||||
option 'selection_cache' '1'
|
||||
option 'allow_lan' '1'
|
||||
option 'http_port' '8080'
|
||||
option 'socks_port' '1080'
|
||||
option 'mixed_port' '7890'
|
||||
option 'redir_port' '7891'
|
||||
option 'tproxy_port' '7892'
|
||||
option 'authentication' '1'
|
||||
option 'tun_stack' 'system'
|
||||
option 'tun_mtu' '9000'
|
||||
option 'tun_gso' '1'
|
||||
option 'tun_gso_max_size' '65536'
|
||||
option 'tun_endpoint_independent_nat' '0'
|
||||
option 'dns_port' '1053'
|
||||
option 'dns_mode' 'fake-ip'
|
||||
option 'fake_ip_range' '198.18.0.1/16'
|
||||
option 'fake_ip_filter' '0'
|
||||
list 'fake_ip_filters' '+.lan'
|
||||
list 'fake_ip_filters' '+.local'
|
||||
option 'fake_ip_cache' '1'
|
||||
option 'respect_rules' '1'
|
||||
option 'dns_ipv6' '0'
|
||||
option 'dns_system_hosts' '0'
|
||||
option 'dns_hosts' '0'
|
||||
option 'hosts' '0'
|
||||
option 'dns_nameserver' '0'
|
||||
option 'dns_nameserver_policy' '0'
|
||||
option 'geoip_format' 'dat'
|
||||
option 'geodata_loader' 'memconservative'
|
||||
option 'geosite_url' 'https://mirror.ghproxy.com/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat'
|
||||
option 'geoip_mmdb_url' 'https://mirror.ghproxy.com/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip-lite.metadb'
|
||||
option 'geoip_dat_url' 'https://mirror.ghproxy.com/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip-lite.dat'
|
||||
option 'geoip_asn_url' 'https://mirror.ghproxy.com/https://github.com/xishang0128/geoip/releases/download/latest/GeoLite2-ASN.mmdb'
|
||||
option 'geox_auto_update' '0'
|
||||
option 'geox_update_interval' '24'
|
||||
|
||||
config authentication
|
||||
option 'enabled' '1'
|
||||
option 'username' 'mihomo'
|
||||
option 'password' ''
|
||||
|
||||
config host
|
||||
option 'enabled' '0'
|
||||
option 'domain_name' 'localhost'
|
||||
list 'ip' '127.0.0.1'
|
||||
list 'ip' '::1'
|
||||
|
||||
config nameserver
|
||||
option 'enabled' '1'
|
||||
option 'type' 'default-nameserver'
|
||||
list 'nameserver' '223.5.5.5'
|
||||
list 'nameserver' '119.29.29.29'
|
||||
|
||||
config nameserver
|
||||
option 'enabled' '1'
|
||||
option 'type' 'proxy-server-nameserver'
|
||||
list 'nameserver' 'https://dns.alidns.com/dns-query'
|
||||
list 'nameserver' 'https://doh.pub/dns-query'
|
||||
|
||||
config nameserver
|
||||
option 'enabled' '1'
|
||||
option 'type' 'nameserver'
|
||||
list 'nameserver' 'https://dns.alidns.com/dns-query'
|
||||
list 'nameserver' 'https://doh.pub/dns-query'
|
||||
|
||||
config nameserver
|
||||
option 'enabled' '1'
|
||||
option 'type' 'fallback'
|
||||
list 'nameserver' 'https://dns.cloudflare.com/dns-query'
|
||||
list 'nameserver' 'https://dns.google/dns-query'
|
||||
|
||||
config nameserver_policy
|
||||
option 'enabled' '1'
|
||||
option 'matcher' 'geosite:cn,private'
|
||||
list 'nameserver' 'https://dns.alidns.com/dns-query'
|
||||
list 'nameserver' 'https://doh.pub/dns-query'
|
||||
|
||||
config nameserver_policy
|
||||
option 'enabled' '1'
|
||||
option 'matcher' 'geosite:geolocation-!cn'
|
||||
list 'nameserver' 'https://dns.cloudflare.com/dns-query'
|
||||
list 'nameserver' 'https://dns.google/dns-query'
|
||||
|
||||
config editor 'editor'
|
||||
|
||||
config log 'log'
|
|
@ -0,0 +1,511 @@
|
|||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=99
|
||||
STOP=10
|
||||
USE_PROCD=1
|
||||
|
||||
. "$IPKG_INSTROOT/lib/functions/network.sh"
|
||||
. "$IPKG_INSTROOT/etc/mihomo/scripts/constants.sh"
|
||||
|
||||
start_service() {
|
||||
# clear log
|
||||
clear_all_log
|
||||
# load config
|
||||
config_load mihomo
|
||||
# check if enabled
|
||||
local enabled
|
||||
config_get_bool enabled "config" "enabled" 0
|
||||
if [ "$enabled" == 0 ]; then
|
||||
log "App is disabled."
|
||||
log "Exiting..."
|
||||
return
|
||||
fi
|
||||
log "App is enabled."
|
||||
log "Starting..."
|
||||
# get config
|
||||
## app config
|
||||
local scheduled_restart cron_expression profile mixin test_profile fast_reload
|
||||
config_get_bool scheduled_restart "config" "scheduled_restart" 0
|
||||
config_get cron_expression "config" "cron_expression"
|
||||
config_get profile "config" "profile"
|
||||
config_get_bool mixin "config" "mixin" 0
|
||||
config_get_bool test_profile "config" "test_profile" 0
|
||||
config_get_bool fast_reload "config" "fast_reload" 0
|
||||
## proxy config
|
||||
### transparent proxy
|
||||
local transparent_proxy tcp_transparent_proxy_mode udp_transparent_proxy_mode ipv4_dns_hijack ipv6_dns_hijack ipv4_proxy ipv6_proxy router_proxy lan_proxy
|
||||
config_get_bool transparent_proxy "proxy" "transparent_proxy" 0
|
||||
config_get tcp_transparent_proxy_mode "proxy" "tcp_transparent_proxy_mode" "tproxy"
|
||||
config_get udp_transparent_proxy_mode "proxy" "udp_transparent_proxy_mode" "tproxy"
|
||||
config_get_bool ipv4_dns_hijack "proxy" "ipv4_dns_hijack" 0
|
||||
config_get_bool ipv6_dns_hijack "proxy" "ipv6_dns_hijack" 0
|
||||
config_get_bool ipv4_proxy "proxy" "ipv4_proxy" 0
|
||||
config_get_bool ipv6_proxy "proxy" "ipv6_proxy" 0
|
||||
config_get_bool router_proxy "proxy" "router_proxy" 0
|
||||
config_get_bool lan_proxy "proxy" "lan_proxy" 0
|
||||
### access control
|
||||
local access_control_mode bypass_china_mainland_ip acl_tcp_dport acl_udp_dport
|
||||
config_get access_control_mode "proxy" "access_control_mode"
|
||||
config_get_bool bypass_china_mainland_ip "proxy" "bypass_china_mainland_ip" 0
|
||||
config_get acl_tcp_dport "proxy" "acl_tcp_dport" "0-65535"
|
||||
config_get acl_udp_dport "proxy" "acl_udp_dport" "0-65535"
|
||||
## mixin config
|
||||
### general
|
||||
local mode match_process outbound_interface ipv6 tcp_keep_alive_idle tcp_keep_alive_interval log_level
|
||||
config_get mode "mixin" "mode" "rule"
|
||||
config_get match_process "mixin" "match_process" "off"
|
||||
config_get outbound_interface "mixin" "outbound_interface"
|
||||
config_get_bool ipv6 "mixin" "ipv6" 0
|
||||
config_get tcp_keep_alive_idle "mixin" "tcp_keep_alive_idle" 600
|
||||
config_get tcp_keep_alive_interval "mixin" "tcp_keep_alive_interval" 15
|
||||
config_get log_level "mixin" "log_level" "info"
|
||||
### external control
|
||||
local ui_name ui_url api_port api_secret selection_cache
|
||||
config_get ui_name "mixin" "ui_name"
|
||||
config_get ui_url "mixin" "ui_url"
|
||||
config_get api_port "mixin" "api_port" "9090"
|
||||
config_get api_secret "mixin" "api_secret" "666666"
|
||||
config_get_bool selection_cache "mixin" "selection_cache" 0
|
||||
### inbound
|
||||
local allow_lan http_port socks_port mixed_port redir_port tproxy_port authentication
|
||||
config_get_bool allow_lan "mixin" "allow_lan" 0
|
||||
config_get http_port "mixin" "http_port" "8080"
|
||||
config_get socks_port "mixin" "socks_port" "1080"
|
||||
config_get mixed_port "mixin" "mixed_port" "7890"
|
||||
config_get redir_port "mixin" "redir_port" "7891"
|
||||
config_get tproxy_port "mixin" "tproxy_port" "7892"
|
||||
config_get_bool authentication "mixin" "authentication" 0
|
||||
### tun
|
||||
local tun_stack tun_mtu tun_gso tun_gso_max_size tun_endpoint_independent_nat
|
||||
config_get tun_stack "mixin" "tun_stack" "system"
|
||||
config_get tun_mtu "mixin" "tun_mtu" "9000"
|
||||
config_get_bool tun_gso "mixin" "tun_gso" 0
|
||||
config_get tun_gso_max_size "mixin" "tun_gso_max_size" "65536"
|
||||
config_get_bool tun_endpoint_independent_nat "mixin" "tun_endpoint_independent_nat" 0
|
||||
### dns
|
||||
local dns_port dns_mode fake_ip_range fake_ip_filter fake_ip_filter_mode fake_ip_cache dns_respect_rules dns_ipv6 dns_system_hosts dns_hosts hosts dns_nameserver dns_nameserver_policy
|
||||
config_get dns_port "mixin" "dns_port" "1053"
|
||||
config_get dns_mode "mixin" "dns_mode" "redir-host"
|
||||
config_get fake_ip_range "mixin" "fake_ip_range" "198.18.0.1/16"
|
||||
config_get_bool fake_ip_filter "mixin" "fake_ip_filter" 0
|
||||
config_get fake_ip_filter_mode "mixin" "fake_ip_filter_mode" "blacklist"
|
||||
config_get_bool fake_ip_cache "mixin" "fake_ip_cache" 0
|
||||
config_get_bool dns_respect_rules "mixin" "dns_respect_rules" 0
|
||||
config_get_bool dns_ipv6 "mixin" "dns_ipv6" 0
|
||||
config_get_bool dns_system_hosts "mixin" "dns_system_hosts" 0
|
||||
config_get_bool dns_hosts "mixin" "dns_hosts" 0
|
||||
config_get_bool hosts "mixin" "hosts" 0
|
||||
config_get_bool dns_nameserver "mixin" "dns_nameserver" 0
|
||||
config_get_bool dns_nameserver_policy "mixin" "dns_nameserver_policy" 0
|
||||
### geox
|
||||
local geoip_format geodata_loader geosite_url geoip_mmdb_url geoip_dat_url geoip_asn_url geox_auto_update geox_update_interval
|
||||
config_get geoip_format "mixin" "geoip_format" "mmdb"
|
||||
config_get geodata_loader "mixin" "geodata_loader" "memconservative"
|
||||
config_get geosite_url "mixin" "geosite_url"
|
||||
config_get geoip_mmdb_url "mixin" "geoip_mmdb_url"
|
||||
config_get geoip_dat_url "mixin" "geoip_dat_url"
|
||||
config_get geoip_asn_url "mixin" "geoip_asn_url"
|
||||
config_get_bool geox_auto_update "mixin" "geox_auto_update" 0
|
||||
config_get geox_update_interval "mixin" "geox_update_interval" "24"
|
||||
# prepare
|
||||
local tproxy_enable; tproxy_enable=0
|
||||
if [[ "$tcp_transparent_proxy_mode" == "tproxy" || "$udp_transparent_proxy_mode" == "tproxy" ]]; then
|
||||
tproxy_enable=1
|
||||
fi
|
||||
local tun_enable; tun_enable=0
|
||||
if [[ "$tcp_transparent_proxy_mode" == "tun" || "$udp_transparent_proxy_mode" == "tun" ]]; then
|
||||
tun_enable=1
|
||||
fi
|
||||
# get profile
|
||||
if [[ "$profile" == "file:"* ]]; then
|
||||
local profile_name; profile_name=$(basename "${profile/file:/}")
|
||||
cp -f "$PROFILES_DIR/$profile_name" "$RUN_PROFILE_PATH"
|
||||
log "Use Profile: $profile_name"
|
||||
elif [[ "$profile" == "subscription:"* ]]; then
|
||||
local subscription_section; subscription_section="${profile/subscription:/}"
|
||||
local subscription_name subscription_url subscription_user_agent
|
||||
config_get subscription_name "$subscription_section" "name"
|
||||
config_get subscription_url "$subscription_section" "url"
|
||||
config_get subscription_user_agent "$subscription_section" "user_agent"
|
||||
curl -s --connect-timeout 15 --retry 3 -o "$RUN_PROFILE_PATH" -L -H "User-Agent: $subscription_user_agent" "$subscription_url"
|
||||
if [ "$?" != 0 ]; then
|
||||
log "Subscription download failed."
|
||||
log "Exiting..."
|
||||
return
|
||||
fi
|
||||
log "Use Subscription: $subscription_name"
|
||||
else
|
||||
return
|
||||
fi
|
||||
# mixin
|
||||
if [ "$mixin" == 0 ]; then
|
||||
log "Mixin is disabled, only mixin neccesary config."
|
||||
# do mixin
|
||||
log_level="$log_level" ipv6="$ipv6" \
|
||||
ui_path="ui" ui_name="$ui_name" ui_url="$ui_url" api_listen="0.0.0.0:$api_port" api_secret="$api_secret" \
|
||||
http_port="$http_port" socks_port="$socks_port" mixed_port="$mixed_port" redir_port="$redir_port" tproxy_port="$tproxy_port" \
|
||||
tun_enable="$tun_enable" tun_stack="$tun_stack" tun_device="$TUN_DEVICE" tun_mtu="$tun_mtu" tun_gso="$tun_gso" tun_gso_max_size="$tun_gso_max_size" tun_endpoint_independent_nat="$tun_endpoint_independent_nat" \
|
||||
dns_enable="true" dns_listen="0.0.0.0:$dns_port" \
|
||||
yq -M -i '
|
||||
.log-level = env(log_level) | .ipv6 = env(ipv6) == 1 |
|
||||
.external-ui = env(ui_path) | .external-ui-name = env(ui_name) | .external-ui-url = env(ui_url) | .external-controller = env(api_listen) | .secret = env(api_secret) |
|
||||
.port = env(http_port) | .socks-port = env(socks_port) | .mixed-port = env(mixed_port) | .redir-port = env(redir_port) | .tproxy-port = env(tproxy_port) |
|
||||
.tun.enable = env(tun_enable) == 1 | .tun.stack = env(tun_stack) | .tun.device = env(tun_device) | .tun.mtu = env(tun_mtu) | .tun.gso = env(tun_gso) == 1 | .tun.gso-max-size = env(tun_gso_max_size) | .tun.endpoint-independent-nat = env(tun_endpoint_independent_nat) == 1 |
|
||||
.dns.enable = env(dns_enable) | .dns.listen = env(dns_listen)
|
||||
' "$RUN_PROFILE_PATH"
|
||||
else
|
||||
log "Mixin is enabled, mixin all config."
|
||||
# do mixin
|
||||
log_level="$log_level" mode="$mode" match_process="$match_process" tcp_keep_alive_idle="$tcp_keep_alive_idle" tcp_keep_alive_interval="$tcp_keep_alive_interval" ipv6="$ipv6" \
|
||||
ui_path="ui" ui_name="$ui_name" ui_url="$ui_url" api_listen="0.0.0.0:$api_port" api_secret="$api_secret" selection_cache="$selection_cache" \
|
||||
allow_lan="$allow_lan" http_port="$http_port" socks_port="$socks_port" mixed_port="$mixed_port" redir_port="$redir_port" tproxy_port="$tproxy_port" \
|
||||
tun_enable="$tun_enable" tun_stack="$tun_stack" tun_device="$TUN_DEVICE" tun_mtu="$tun_mtu" tun_gso="$tun_gso" tun_gso_max_size="$tun_gso_max_size" tun_endpoint_independent_nat="$tun_endpoint_independent_nat" \
|
||||
dns_enable="true" dns_listen="0.0.0.0:$dns_port" dns_mode="$dns_mode" fake_ip_range="$fake_ip_range" fake_ip_cache="$fake_ip_cache" \
|
||||
dns_respect_rules="$dns_respect_rules" dns_ipv6="$dns_ipv6" dns_system_hosts="$dns_system_hosts" dns_hosts="$dns_hosts" \
|
||||
geoip_format="$geoip_format" geodata_loader="$geodata_loader" geosite_url="$geosite_url" geoip_mmdb_url="$geoip_mmdb_url" geoip_dat_url="$geoip_dat_url" geoip_asn_url="$geoip_asn_url" \
|
||||
geox_auto_update="$geox_auto_update" geox_update_interval="$geox_update_interval" \
|
||||
yq -M -i '
|
||||
.log-level = env(log_level) | .mode = env(mode) | .find-process-mode = env(match_process) | .keep-alive-idle = env(tcp_keep_alive_idle) | .keep-alive-interval = env(tcp_keep_alive_interval) | .ipv6 = env(ipv6) == 1 |
|
||||
.external-ui = env(ui_path) | .external-ui-name = env(ui_name) | .external-ui-url = env(ui_url) | .external-controller = env(api_listen) | .secret = env(api_secret) | .profile.store-selected = env(selection_cache) == 1 |
|
||||
.allow-lan = env(allow_lan) == 1 | .port = env(http_port) | .socks-port = env(socks_port) | .mixed-port = env(mixed_port) | .redir-port = env(redir_port) | .tproxy-port = env(tproxy_port) |
|
||||
.tun.enable = env(tun_enable) == 1 | .tun.stack = env(tun_stack) | .tun.device = env(tun_device) | .tun.mtu = env(tun_mtu) | .tun.gso = env(tun_gso) == 1 | .tun.gso-max-size = env(tun_gso_max_size) | .tun.endpoint-independent-nat = env(tun_endpoint_independent_nat) == 1 |
|
||||
.dns.enable = env(dns_enable) | .dns.listen = env(dns_listen) | .dns.enhanced-mode = env(dns_mode) | .dns.fake-ip-range = env(fake_ip_range) | .profile.store-fake-ip = env(fake_ip_cache) == 1 |
|
||||
.dns.respect-rules = env(dns_respect_rules) == 1 | .dns.ipv6 = env(dns_ipv6) == 1 | .dns.use-system-hosts = env(dns_system_hosts) == 1 | .dns.use-hosts = env(dns_hosts) == 1 |
|
||||
.geodata-mode = env(geoip_format) == "dat" | .geodata-loader = env(geodata_loader) | .geox-url.geosite = env(geosite_url) | .geox-url.mmdb = env(geoip_mmdb_url) | .geox-url.geoip = env(geoip_dat_url) | .geox-url.asn = env(geoip_asn_url) |
|
||||
.geo-auto-update = env(geox_auto_update) == 1 | .geo-update-interval = env(geox_update_interval)
|
||||
' "$RUN_PROFILE_PATH"
|
||||
|
||||
if [ "$authentication" == 1 ]; then
|
||||
yq -M -i 'del(.authentication)' "$RUN_PROFILE_PATH"
|
||||
config_foreach mixin_authentications "authentication"
|
||||
fi
|
||||
if [ "$fake_ip_filter" == 1 ]; then
|
||||
fake_ip_filter_mode="$fake_ip_filter_mode" \
|
||||
yq -M -i 'del(.dns.fake-ip-filter) | .dns.fake-ip-filter-mode = env(fake_ip_filter_mode)' "$RUN_PROFILE_PATH"
|
||||
config_list_foreach "mixin" "fake_ip_filters" mixin_fake_ip_filters
|
||||
fi
|
||||
if [ "$hosts" == 1 ]; then
|
||||
yq -M -i 'del(.hosts)' "$RUN_PROFILE_PATH"
|
||||
config_foreach mixin_hosts "host"
|
||||
fi
|
||||
if [ "$dns_nameserver" == 1 ]; then
|
||||
yq -M -i 'del(.dns.default-nameserver) | del(.dns.proxy-server-nameserver) | del(.dns.nameserver) | del(.dns.fallback)' "$RUN_PROFILE_PATH"
|
||||
config_foreach mixin_nameservers "nameserver"
|
||||
fi
|
||||
if [ "$dns_nameserver_policy" == 1 ]; then
|
||||
yq -M -i 'del(.dns.nameserver-policy)' "$RUN_PROFILE_PATH"
|
||||
config_foreach mixin_nameserver_policies "nameserver_policy"
|
||||
fi
|
||||
# mixin file
|
||||
if [ -s "$MIXIN_FILE_PATH" ]; then
|
||||
yq ea -M -i '. as $item ireduce ({}; . * $item ) | ... comments=""' "$RUN_PROFILE_PATH" "$MIXIN_FILE_PATH"
|
||||
fi
|
||||
fi
|
||||
if [ "$tun_enable" == 1 ]; then
|
||||
yq -M -i '.tun.auto-route = false | .tun.auto-redirect = false | .tun.auto-detect-interface = false | .tun.dns-hijack = []' "$RUN_PROFILE_PATH"
|
||||
fi
|
||||
if [ -n "$outbound_interface" ]; then
|
||||
local outbound_device
|
||||
network_get_device outbound_device "$outbound_interface"
|
||||
if [ -n "$outbound_device" ]; then
|
||||
outbound_device="$outbound_device" yq -M -i '.interface-name = env(outbound_device)' "$RUN_PROFILE_PATH"
|
||||
fi
|
||||
fi
|
||||
# test profile
|
||||
if [ "$test_profile" == 1 ]; then
|
||||
log "Profile testing..."
|
||||
if ($PROG -d "$RUN_DIR" -t >> "$RUN_CORE_LOG_PATH" 2>&1); then
|
||||
log "Profile test passed!"
|
||||
else
|
||||
log "Profile test failed!"
|
||||
log "Exiting..."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
# start core
|
||||
log "Start Core"
|
||||
procd_open_instance mihomo
|
||||
|
||||
procd_set_param command /bin/sh -c "$PROG -d $RUN_DIR >> $RUN_CORE_LOG_PATH 2>&1"
|
||||
procd_set_param file "$RUN_PROFILE_PATH"
|
||||
if [ "$fast_reload" == 1 ]; then
|
||||
procd_set_param reload_signal HUP
|
||||
fi
|
||||
procd_set_param respawn
|
||||
procd_set_param user "$MIHOMO_USER"
|
||||
procd_set_param group "$MIHOMO_GROUP"
|
||||
|
||||
procd_add_jail mihomo requirejail procfs
|
||||
procd_add_jail_mount "$PROG" /etc/TZ /etc/localtime /etc/hosts /etc/ssl/certs
|
||||
procd_add_jail_mount_rw "$RUN_DIR" /dev/net
|
||||
procd_set_param capabilities /etc/capabilities/mihomo.json
|
||||
procd_set_param no_new_privs 1
|
||||
|
||||
procd_close_instance
|
||||
# transparent proxy
|
||||
if [ "$transparent_proxy" == 1 ]; then
|
||||
log "Transparent Proxy is enabled."
|
||||
log "Transparent Proxy: Start hijack."
|
||||
# prepare
|
||||
if [ "$tproxy_enable" == 1 ]; then
|
||||
ip route add local default dev lo table "$TPROXY_ROUTE_TABLE"
|
||||
fi
|
||||
if [ "$tun_enable" == 1 ]; then
|
||||
ip tuntap add dev "$TUN_DEVICE" mode tun vnet_hdr
|
||||
ip link set "$TUN_DEVICE" up
|
||||
ip route add unicast default dev $TUN_DEVICE table "$TUN_ROUTE_TABLE"
|
||||
$TUN_SH
|
||||
fi
|
||||
local tcp_route_table
|
||||
if [ "$tcp_transparent_proxy_mode" == "tproxy" ]; then
|
||||
tcp_route_table="$TPROXY_ROUTE_TABLE"
|
||||
elif [ "$tcp_transparent_proxy_mode" == "tun" ]; then
|
||||
tcp_route_table="$TUN_ROUTE_TABLE"
|
||||
fi
|
||||
if [ -n "$tcp_route_table" ]; then
|
||||
if [ "$ipv4_proxy" == 1 ]; then
|
||||
ip rule add pref "$TCP_RULE_PREF" fwmark "$FW_MARK/$FW_MARK_MASK" ipproto tcp table "$tcp_route_table"
|
||||
fi
|
||||
if [ "$ipv6_proxy" == 1 ]; then
|
||||
ip -6 rule add pref "$TCP_RULE_PREF" fwmark "$FW_MARK/$FW_MARK_MASK" ipproto tcp table "$tcp_route_table"
|
||||
fi
|
||||
fi
|
||||
local udp_route_table
|
||||
if [ "$udp_transparent_proxy_mode" == "tproxy" ]; then
|
||||
udp_route_table="$TPROXY_ROUTE_TABLE"
|
||||
elif [ "$udp_transparent_proxy_mode" == "tun" ]; then
|
||||
udp_route_table="$TUN_ROUTE_TABLE"
|
||||
fi
|
||||
if [ -n "$udp_route_table" ]; then
|
||||
if [ "$ipv4_proxy" == 1 ]; then
|
||||
ip rule add pref "$UDP_RULE_PREF" fwmark "$FW_MARK/$FW_MARK_MASK" ipproto udp table "$udp_route_table"
|
||||
fi
|
||||
if [ "$ipv6_proxy" == 1 ]; then
|
||||
ip -6 rule add pref "$UDP_RULE_PREF" fwmark "$FW_MARK/$FW_MARK_MASK" ipproto udp table "$udp_route_table"
|
||||
fi
|
||||
fi
|
||||
nft -f "$HIJACK_NFT" -D FW_MARK="$FW_MARK" -D FW_MARK_MASK="$FW_MARK_MASK" -D MIHOMO_USER="$MIHOMO_USER" -D TUN_DEVICE="$TUN_DEVICE" -D DNS_PORT="$dns_port" -D REDIR_PORT="$redir_port" -D TPROXY_PORT="$tproxy_port"
|
||||
nft -f "$RESERVED_IP_NFT"
|
||||
nft -f "$RESERVED_IP6_NFT"
|
||||
nft add element inet "$FW_TABLE" fake_ip \{ "$fake_ip_range" \}
|
||||
# dns hijack
|
||||
if [ "$ipv4_dns_hijack" == 1 ]; then
|
||||
log "Transparent Proxy: IPv4 DNS Hijack is enabled, IPv4 dns request will redirect to the core."
|
||||
nft add element inet "$FW_TABLE" dns_hijack_nfproto \{ ipv4 \}
|
||||
fi
|
||||
if [ "$ipv6_dns_hijack" == 1 ]; then
|
||||
log "Transparent Proxy: IPv6 DNS Hijack is enabled, IPv6 dns request will redirect to the core."
|
||||
nft add element inet "$FW_TABLE" dns_hijack_nfproto \{ ipv6 \}
|
||||
fi
|
||||
# proxy
|
||||
if [ "$ipv4_proxy" == 1 ]; then
|
||||
log "Transparent Proxy: IPv4 Proxy is enabled, set proxy for IPv4 traffic."
|
||||
nft add element inet "$FW_TABLE" proxy_nfproto \{ ipv4 \}
|
||||
fi
|
||||
if [ "$ipv6_proxy" == 1 ]; then
|
||||
log "Transparent Proxy: IPv6 Proxy is enabled, set proxy for IPv6 traffic."
|
||||
nft add element inet "$FW_TABLE" proxy_nfproto \{ ipv6 \}
|
||||
fi
|
||||
# bypass china mainland ip
|
||||
if [ "$bypass_china_mainland_ip" == 1 ]; then
|
||||
log "Transparent Proxy: Bypass china mainland ip is enabled."
|
||||
if [ "$ipv4_proxy" == 1 ]; then
|
||||
nft -f "$GEOIP_CN_NFT"
|
||||
fi
|
||||
if [ "$ipv6_proxy" == 1 ]; then
|
||||
nft -f "$GEOIP6_CN_NFT"
|
||||
fi
|
||||
fi
|
||||
# destination port to proxy
|
||||
log "Transparent Proxy: Destination TCP Port to Proxy: $acl_tcp_dport."
|
||||
log "Transparent Proxy: Destination UDP Port to Proxy: $acl_udp_dport."
|
||||
local acl_dport
|
||||
for acl_dport in $acl_tcp_dport; do
|
||||
nft add element inet "$FW_TABLE" acl_dport \{ "tcp" . "$acl_dport" \}
|
||||
done
|
||||
for acl_dport in $acl_udp_dport; do
|
||||
nft add element inet "$FW_TABLE" acl_dport \{ "udp" . "$acl_dport" \}
|
||||
done
|
||||
# router proxy
|
||||
if [ "$router_proxy" == 1 ]; then
|
||||
log "Transparent Proxy: Router Proxy is enabled, set proxy for router."
|
||||
nft insert rule inet "$FW_TABLE" nat_output jump router_dns_hijack
|
||||
if [ "$tcp_transparent_proxy_mode" == "redirect" ]; then
|
||||
nft add rule inet "$FW_TABLE" nat_output meta l4proto tcp jump router_redirect
|
||||
else
|
||||
nft add rule inet "$FW_TABLE" mangle_output meta l4proto tcp jump router_reroute
|
||||
fi
|
||||
nft add rule inet "$FW_TABLE" mangle_output meta l4proto udp jump router_reroute
|
||||
fi
|
||||
# lan proxy
|
||||
if [ "$lan_proxy" == 1 ]; then
|
||||
log "Transparent Proxy: Lan Proxy is enabled, set proxy for lan."
|
||||
# access control
|
||||
if [ "$access_control_mode" == "all" ]; then
|
||||
log "Transparent Proxy: Access Control is using all mode, set proxy for all client."
|
||||
elif [ "$access_control_mode" == "allow" ]; then
|
||||
log "Transparent Proxy: Access Control is using allow mode, set proxy for client which is in acl."
|
||||
elif [ "$access_control_mode" == "block" ]; then
|
||||
log "Transparent Proxy: Access Control is using block mode, set proxy for client which is not in acl."
|
||||
fi
|
||||
config_list_foreach "proxy" "acl_ip" add_acl_ip
|
||||
config_list_foreach "proxy" "acl_ip6" add_acl_ip6
|
||||
config_list_foreach "proxy" "acl_mac" add_acl_mac
|
||||
nft insert rule inet "$FW_TABLE" dstnat jump "${access_control_mode}_dns_hijack"
|
||||
if [ "$tcp_transparent_proxy_mode" == "redirect" ]; then
|
||||
nft add rule inet "$FW_TABLE" dstnat meta l4proto tcp jump "${access_control_mode}_redirect"
|
||||
else
|
||||
nft add rule inet "$FW_TABLE" mangle_prerouting meta l4proto tcp jump "${access_control_mode}_${tcp_transparent_proxy_mode}"
|
||||
fi
|
||||
nft add rule inet "$FW_TABLE" mangle_prerouting meta l4proto udp jump "${access_control_mode}_${udp_transparent_proxy_mode}"
|
||||
fi
|
||||
fi
|
||||
# cron
|
||||
if [[ "$scheduled_restart" == 1 && -n "$cron_expression" ]]; then
|
||||
log "Add crontab for scheduled restart."
|
||||
echo "$cron_expression /etc/init.d/mihomo restart #mihomo" >> "/etc/crontabs/root"
|
||||
/etc/init.d/cron restart
|
||||
fi
|
||||
log "Start Successful!"
|
||||
}
|
||||
|
||||
service_stopped() {
|
||||
cleanup
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
cleanup
|
||||
start
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "mihomo"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
# delete routing policy
|
||||
ip rule del ipproto tcp table "$TPROXY_ROUTE_TABLE" > /dev/null 2>&1
|
||||
ip rule del ipproto udp table "$TPROXY_ROUTE_TABLE" > /dev/null 2>&1
|
||||
ip rule del ipproto tcp table "$TUN_ROUTE_TABLE" > /dev/null 2>&1
|
||||
ip rule del ipproto udp table "$TUN_ROUTE_TABLE" > /dev/null 2>&1
|
||||
ip -6 rule del ipproto tcp table "$TPROXY_ROUTE_TABLE" > /dev/null 2>&1
|
||||
ip -6 rule del ipproto udp table "$TPROXY_ROUTE_TABLE" > /dev/null 2>&1
|
||||
ip -6 rule del ipproto tcp table "$TUN_ROUTE_TABLE" > /dev/null 2>&1
|
||||
ip -6 rule del ipproto udp table "$TUN_ROUTE_TABLE" > /dev/null 2>&1
|
||||
# delete routing table
|
||||
ip route flush table "$TPROXY_ROUTE_TABLE" > /dev/null 2>&1
|
||||
ip route flush table "$TUN_ROUTE_TABLE" > /dev/null 2>&1
|
||||
ip -6 route flush table "$TPROXY_ROUTE_TABLE" > /dev/null 2>&1
|
||||
ip -6 route flush table "$TUN_ROUTE_TABLE" > /dev/null 2>&1
|
||||
# delete tun
|
||||
ip link set "$TUN_DEVICE" down
|
||||
ip tuntap del dev "$TUN_DEVICE" mode tun > /dev/null 2>&1
|
||||
# delete hijack
|
||||
nft delete table inet "$FW_TABLE" > /dev/null 2>&1
|
||||
local handles handle
|
||||
handles=$(nft --json list table inet fw4 | yq '.nftables[] | select(has("rule")) | .rule | select(.family == "inet" and .table == "fw4" and .chain == "input" and .expr[0].match.right == "tun") | .handle')
|
||||
for handle in $handles; do
|
||||
nft delete rule inet fw4 input handle "$handle"
|
||||
done
|
||||
handles=$(nft --json list table inet fw4 | yq '.nftables[] | select(has("rule")) | .rule | select(.family == "inet" and .table == "fw4" and .chain == "forward" and .expr[0].match.right == "tun") | .handle')
|
||||
for handle in $handles; do
|
||||
nft delete rule inet fw4 forward handle "$handle"
|
||||
done
|
||||
# delete cron
|
||||
sed -i '/#mihomo/d' "/etc/crontabs/root" > /dev/null 2>&1
|
||||
/etc/init.d/cron restart
|
||||
}
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$RUN_APP_LOG_PATH"
|
||||
}
|
||||
|
||||
clear_all_log() {
|
||||
echo -n > "$RUN_APP_LOG_PATH"
|
||||
echo -n > "$RUN_CORE_LOG_PATH"
|
||||
}
|
||||
|
||||
mixin_authentications() {
|
||||
local section="$1"
|
||||
|
||||
local enabled username password
|
||||
config_get_bool enabled "$section" "enabled" 0
|
||||
config_get username "$section" "username"
|
||||
config_get password "$section" "password"
|
||||
|
||||
if [ "$enabled" == 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
authentication="$username:$password" yq -M -i '.authentication += [env(authentication)]' "$RUN_PROFILE_PATH"
|
||||
}
|
||||
|
||||
mixin_fake_ip_filters() {
|
||||
domain_name="$1" yq -M -i '.dns.fake-ip-filter += [env(domain_name)]' "$RUN_PROFILE_PATH"
|
||||
}
|
||||
|
||||
mixin_hosts() {
|
||||
local section="$1"
|
||||
|
||||
local enabled domain_name
|
||||
config_get_bool enabled "$section" "enabled" 0
|
||||
config_get domain_name "$section" "domain_name"
|
||||
|
||||
if [ "$enabled" == 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
config_list_foreach "$section" "ip" mixin_host "$domain_name"
|
||||
}
|
||||
|
||||
mixin_host() {
|
||||
ip="$1" domain_name="$2" yq -M -i '.hosts.[env(domain_name)] += [env(ip)]' "$RUN_PROFILE_PATH"
|
||||
}
|
||||
|
||||
mixin_nameservers() {
|
||||
local section="$1"
|
||||
|
||||
local enabled type
|
||||
config_get_bool enabled "$section" "enabled" 0
|
||||
config_get type "$section" "type"
|
||||
|
||||
if [ "$enabled" == 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
config_list_foreach "$section" "nameserver" mixin_nameserver "$type"
|
||||
}
|
||||
|
||||
mixin_nameserver() {
|
||||
nameserver="$1" type="$2" yq -M -i '.dns.[env(type)] += [env(nameserver)]' "$RUN_PROFILE_PATH"
|
||||
}
|
||||
|
||||
mixin_nameserver_policies() {
|
||||
local section="$1"
|
||||
|
||||
local enabled matcher
|
||||
config_get_bool enabled "$section" "enabled" 0
|
||||
config_get matcher "$section" "matcher"
|
||||
|
||||
if [ "$enabled" == 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
config_list_foreach "$section" "nameserver" mixin_nameserver_policy "$matcher"
|
||||
}
|
||||
|
||||
mixin_nameserver_policy() {
|
||||
nameserver="$1" matcher="$2" yq -M -i '.dns.nameserver-policy.[env(matcher)] += [env(nameserver)]' "$RUN_PROFILE_PATH"
|
||||
}
|
||||
|
||||
add_acl_ip() {
|
||||
nft add element inet "$FW_TABLE" acl_ip \{ "$1" \}
|
||||
}
|
||||
|
||||
add_acl_ip6() {
|
||||
nft add element inet "$FW_TABLE" acl_ip6 \{ "$1" \}
|
||||
}
|
||||
|
||||
add_acl_mac() {
|
||||
nft add element inet "$FW_TABLE" acl_mac \{ "$1" \}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
/etc/mihomo
|
|
@ -0,0 +1,28 @@
|
|||
# Mixin File
|
||||
# You can set any mihomo profile's config at here, it will mixin to the profile.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# global-client-fingerprint: chrome # set fingerprint for TLS transport
|
||||
# experimental: # experimental config
|
||||
# quic-go-disable-gso: false # disable quic-go GSO support
|
||||
# quic-go-disable-ecn: false # disable quic-go ECN support
|
||||
# dialer-ip4p-convert: false # IP4P support
|
||||
# proxies: # overwrite proxies
|
||||
# - name: "PROXY"
|
||||
# type: ss
|
||||
# server: proxy.example.com
|
||||
# port: 443
|
||||
# cipher: chacha20-ietf-poly1305
|
||||
# password: "password"
|
||||
# rules: # overwrite rules
|
||||
# - DOMAIN,google.com,PROXY
|
||||
# - DOMAIN-SUFFIX,google.com,PROXY
|
||||
# - DOMAIN-KEYWORD,google,PROXY
|
||||
# - DOMAIN-REGEX,^google.*com,PROXY
|
||||
# - GEOSITE,google,PROXY
|
||||
# - GEOSITE,cn,DIRECT
|
||||
# - IP-CIDR,8.8.8.8/32,DIRECT,no-resolve
|
||||
# - GEOIP,telegram,DIRECT
|
||||
# - GEOIP,cn,DIRECT
|
||||
# - Match,PROXY
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,198 @@
|
|||
#!/usr/sbin/nft -f
|
||||
|
||||
table inet mihomo {
|
||||
set dns_hijack_nfproto {
|
||||
type nf_proto
|
||||
flags interval
|
||||
}
|
||||
|
||||
set proxy_nfproto {
|
||||
type nf_proto
|
||||
flags interval
|
||||
}
|
||||
|
||||
set china_ip {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
}
|
||||
|
||||
set china_ip6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
}
|
||||
|
||||
set reserved_ip {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
}
|
||||
|
||||
set reserved_ip6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
}
|
||||
|
||||
set fake_ip {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
}
|
||||
|
||||
set acl_dport {
|
||||
type inet_proto . inet_service
|
||||
flags interval
|
||||
auto-merge
|
||||
}
|
||||
|
||||
set acl_ip {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
}
|
||||
|
||||
set acl_ip6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
}
|
||||
|
||||
set acl_mac {
|
||||
type ether_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
}
|
||||
|
||||
chain router_dns_hijack {
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 oifname lo meta skuid != $MIHOMO_USER counter redirect to :$DNS_PORT
|
||||
}
|
||||
|
||||
chain all_dns_hijack {
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter redirect to :$DNS_PORT
|
||||
}
|
||||
|
||||
chain allow_dns_hijack {
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 ip saddr @acl_ip counter redirect to :$DNS_PORT
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 ip6 saddr @acl_ip6 counter redirect to :$DNS_PORT
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 ether saddr @acl_mac counter redirect to :$DNS_PORT
|
||||
}
|
||||
|
||||
chain block_dns_hijack {
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 ip saddr @acl_ip counter return
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 ip6 saddr @acl_ip6 counter return
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 ether saddr @acl_mac counter return
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter redirect to :$DNS_PORT
|
||||
}
|
||||
|
||||
chain all_redirect {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } counter redirect to :$REDIR_PORT
|
||||
}
|
||||
|
||||
chain allow_redirect {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip saddr @acl_ip counter redirect to :$REDIR_PORT
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip6 saddr @acl_ip6 counter redirect to :$REDIR_PORT
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr @acl_mac counter redirect to :$REDIR_PORT
|
||||
}
|
||||
|
||||
chain block_redirect {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip saddr @acl_ip counter return
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip6 saddr @acl_ip6 counter return
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr @acl_mac counter return
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } counter redirect to :$REDIR_PORT
|
||||
}
|
||||
|
||||
chain all_tproxy {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK tproxy to :$TPROXY_PORT counter accept
|
||||
}
|
||||
|
||||
chain allow_tproxy {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip saddr @acl_ip meta mark set mark ^ $FW_MARK tproxy ip to :$TPROXY_PORT counter accept
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip6 saddr @acl_ip6 meta mark set mark ^ $FW_MARK tproxy ip6 to :$TPROXY_PORT counter accept
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr @acl_mac meta mark set mark ^ $FW_MARK tproxy to :$TPROXY_PORT counter accept
|
||||
}
|
||||
|
||||
chain block_tproxy {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip saddr @acl_ip counter return
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip6 saddr @acl_ip6 counter return
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr @acl_mac counter return
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK tproxy to :$TPROXY_PORT counter accept
|
||||
}
|
||||
|
||||
chain all_tun {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK counter
|
||||
}
|
||||
|
||||
chain allow_tun {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip saddr @acl_ip meta mark set mark ^ $FW_MARK counter
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip6 saddr @acl_ip6 meta mark set mark ^ $FW_MARK counter
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr @acl_mac meta mark set mark ^ $FW_MARK counter
|
||||
}
|
||||
|
||||
chain block_tun {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip saddr @acl_ip counter return
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip6 saddr @acl_ip6 counter return
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr @acl_mac counter return
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK counter
|
||||
}
|
||||
|
||||
chain router_redirect {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } counter redirect to :$REDIR_PORT
|
||||
}
|
||||
|
||||
chain router_reroute {
|
||||
meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK counter accept
|
||||
}
|
||||
|
||||
chain dstnat {
|
||||
type nat hook prerouting priority dstnat + 1; policy accept;
|
||||
fib daddr type local counter return
|
||||
ct direction reply counter return
|
||||
ip daddr @reserved_ip counter return
|
||||
ip6 daddr @reserved_ip6 counter return
|
||||
ip daddr @china_ip counter return
|
||||
ip6 daddr @china_ip6 counter return
|
||||
meta l4proto . th dport != @acl_dport ip daddr != @fake_ip counter return
|
||||
meta nfproto ipv6 meta l4proto . th dport != @acl_dport counter return
|
||||
}
|
||||
|
||||
chain nat_output {
|
||||
type nat hook output priority filter; policy accept;
|
||||
meta skuid $MIHOMO_USER counter return
|
||||
fib daddr type local counter return
|
||||
ct direction reply counter return
|
||||
ip daddr @reserved_ip counter return
|
||||
ip6 daddr @reserved_ip6 counter return
|
||||
ip daddr @china_ip counter return
|
||||
ip6 daddr @china_ip6 counter return
|
||||
meta l4proto . th dport != @acl_dport ip daddr != @fake_ip counter return
|
||||
meta nfproto ipv6 meta l4proto . th dport != @acl_dport counter return
|
||||
}
|
||||
|
||||
chain mangle_prerouting {
|
||||
type filter hook prerouting priority mangle; policy accept;
|
||||
meta l4proto { tcp, udp } iifname lo meta mark & $FW_MARK_MASK == $FW_MARK tproxy to :$TPROXY_PORT counter accept
|
||||
meta l4proto { tcp, udp } iifname $TUN_DEVICE counter return
|
||||
fib daddr type local counter return
|
||||
ct direction reply counter return
|
||||
ip daddr @reserved_ip counter return
|
||||
ip6 daddr @reserved_ip6 counter return
|
||||
ip daddr @china_ip counter return
|
||||
ip6 daddr @china_ip6 counter return
|
||||
meta l4proto . th dport != @acl_dport ip daddr != @fake_ip counter return
|
||||
meta nfproto ipv6 meta l4proto . th dport != @acl_dport counter return
|
||||
meta l4proto { tcp, udp } th dport 53 counter return
|
||||
}
|
||||
|
||||
chain mangle_output {
|
||||
type route hook output priority mangle; policy accept;
|
||||
meta skuid $MIHOMO_USER counter return
|
||||
fib daddr type local counter return
|
||||
ct direction reply counter return
|
||||
ip daddr @reserved_ip counter return
|
||||
ip6 daddr @reserved_ip6 counter return
|
||||
ip daddr @china_ip counter return
|
||||
ip6 daddr @china_ip6 counter return
|
||||
meta l4proto . th dport != @acl_dport ip daddr != @fake_ip counter return
|
||||
meta nfproto ipv6 meta l4proto . th dport != @acl_dport counter return
|
||||
meta l4proto { tcp, udp } th dport 53 counter return
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/sbin/nft -f
|
||||
|
||||
table inet mihomo {
|
||||
set reserved_ip {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
elements = {
|
||||
0.0.0.0/8,
|
||||
10.0.0.0/8,
|
||||
127.0.0.0/8,
|
||||
100.64.0.0/10,
|
||||
169.254.0.0/16,
|
||||
172.16.0.0/12,
|
||||
192.168.0.0/16,
|
||||
224.0.0.0/4,
|
||||
240.0.0.0/4
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/sbin/nft -f
|
||||
|
||||
table inet mihomo {
|
||||
set reserved_ip6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
elements = {
|
||||
::/128,
|
||||
::1/128,
|
||||
::ffff:0:0/96,
|
||||
100::/64,
|
||||
64:ff9b::/96,
|
||||
2001::/32,
|
||||
2001:10::/28,
|
||||
2001:20::/28,
|
||||
2001:db8::/32,
|
||||
2002::/16,
|
||||
fc00::/7,
|
||||
fe80::/10,
|
||||
ff00::/8
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/sh
|
||||
|
||||
# permission
|
||||
MIHOMO_USER="mihomo"
|
||||
MIHOMO_GROUP="mihomo"
|
||||
|
||||
# routing
|
||||
FW_TABLE="mihomo"
|
||||
FW_MARK="0x80"
|
||||
FW_MARK_MASK="0xFF"
|
||||
TCP_RULE_PREF="1024"
|
||||
UDP_RULE_PREF="1025"
|
||||
TPROXY_ROUTE_TABLE="80"
|
||||
TUN_ROUTE_TABLE="81"
|
||||
TUN_DEVICE="tun"
|
||||
|
||||
# paths
|
||||
PROG="/usr/bin/mihomo"
|
||||
HOME_DIR="/etc/mihomo"
|
||||
PROFILES_DIR="$HOME_DIR/profiles"
|
||||
MIXIN_FILE_PATH="$HOME_DIR/mixin.yaml"
|
||||
RUN_DIR="$HOME_DIR/run"
|
||||
RUN_APP_LOG_PATH="$RUN_DIR/app.log"
|
||||
RUN_CORE_LOG_PATH="$RUN_DIR/core.log"
|
||||
RUN_PROFILE_PATH="$RUN_DIR/config.yaml"
|
||||
RUN_UI_DIR="$RUN_DIR/ui"
|
||||
|
||||
# scripts
|
||||
SH_DIR="$HOME_DIR/scripts"
|
||||
TUN_SH="$SH_DIR/tun.sh"
|
||||
|
||||
# nftables
|
||||
NFT_DIR="$HOME_DIR/nftables"
|
||||
HIJACK_NFT="$NFT_DIR/hijack.nft"
|
||||
RESERVED_IP_NFT="$NFT_DIR/reserved_ip.nft"
|
||||
RESERVED_IP6_NFT="$NFT_DIR/reserved_ip6.nft"
|
||||
GEOIP_CN_NFT="$NFT_DIR/geoip_cn.nft"
|
||||
GEOIP6_CN_NFT="$NFT_DIR/geoip6_cn.nft"
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
|
||||
. "$IPKG_INSTROOT/lib/functions.sh"
|
||||
. "$IPKG_INSTROOT/etc/mihomo/scripts/constants.sh"
|
||||
|
||||
config_load mihomo
|
||||
config_get enabled "config" "enabled" 0
|
||||
config_get tcp_transparent_proxy_mode "proxy" "tcp_transparent_proxy_mode"
|
||||
config_get udp_transparent_proxy_mode "proxy" "udp_transparent_proxy_mode"
|
||||
|
||||
if [ "$enabled" == 1 ] && [[ "$tcp_transparent_proxy_mode" == "tun" || "$udp_transparent_proxy_mode" == "tun" ]]; then
|
||||
nft insert rule inet fw4 input iifname "$TUN_DEVICE" counter accept
|
||||
nft insert rule inet fw4 forward oifname "$TUN_DEVICE" counter accept
|
||||
nft insert rule inet fw4 forward iifname "$TUN_DEVICE" counter accept
|
||||
fi
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/sh
|
||||
|
||||
. "$IPKG_INSTROOT/etc/mihomo/scripts/constants.sh"
|
||||
|
||||
# add firewall include for tun
|
||||
uci -q batch <<-EOF > /dev/null
|
||||
delete firewall.mihomo
|
||||
set firewall.mihomo=include
|
||||
set firewall.mihomo.type=script
|
||||
set firewall.mihomo.path=$TUN_SH
|
||||
set firewall.mihomo.fw4_compatible=1
|
||||
commit firewall
|
||||
EOF
|
||||
|
||||
# check mihomo.config.init
|
||||
init=$(uci -q get mihomo.config.init); [ -z "$init" ] && return
|
||||
|
||||
# generate random string for api secret and authentication password
|
||||
random=$(awk 'BEGIN{srand(); print int(rand() * 1000000)}')
|
||||
|
||||
# set mihomo.mixin.api_secret
|
||||
uci set mihomo.mixin.api_secret="$random"
|
||||
|
||||
# set mihomo.@authentication[0].password
|
||||
uci set mihomo.@authentication[0].password="$random"
|
||||
|
||||
# remove mihomo.config.init
|
||||
uci del mihomo.config.init
|
||||
|
||||
# commit
|
||||
uci commit mihomo
|
||||
|
||||
# exit with 0
|
||||
exit 0
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/sh
|
||||
|
||||
. "$IPKG_INSTROOT/etc/mihomo/scripts/constants.sh"
|
||||
|
||||
# since 1.8.0
|
||||
|
||||
|
||||
# commit
|
||||
uci commit mihomo
|
||||
|
||||
# exit with 0
|
||||
exit 0
|
Loading…
Reference in New Issue