mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-07-27 08:31:44 +08:00
update 2026-07-12 00:50:47
This commit is contained in:
parent
274b09a7d6
commit
638ee2bb02
@ -10,12 +10,18 @@ LUCI_DEPENDS:= \
|
||||
+sing-box \
|
||||
+firewall4 \
|
||||
+kmod-nft-tproxy \
|
||||
+curl \
|
||||
+jsonfilter \
|
||||
+ucode-mod-digest
|
||||
|
||||
PKG_NAME:=luci-app-homeproxy
|
||||
PKG_VERSION=$(PKG_SRC_VERSION)
|
||||
PKG_RELEASE:=5
|
||||
PKG_PO_VERSION=$(PKG_SRC_VERSION)-r$(PKG_RELEASE)
|
||||
|
||||
define Package/luci-app-homeproxy/conffiles
|
||||
/etc/config/homeproxy
|
||||
/etc/homeproxy/cache/
|
||||
/etc/homeproxy/certs/
|
||||
/etc/homeproxy/ruleset/
|
||||
/etc/homeproxy/resources/direct_list.txt
|
||||
|
||||
@ -65,7 +65,7 @@ function renderStatus(isRunning, version, currentNode) {
|
||||
renderHTML = spanTemp.format(statusColor, _('HomeProxy'), version, _('NOT RUNNING'));
|
||||
|
||||
if (currentNode)
|
||||
renderHTML += '<div><em><span style="color:%s"><strong>%s</strong></span></em></div>'.format(nodeColor, currentNode);
|
||||
renderHTML += '<div><em><span style="color:%s"><strong>%s</strong></span></em></div>'.format(nodeColor, '%h'.format(currentNode));
|
||||
|
||||
return renderHTML;
|
||||
}
|
||||
@ -101,12 +101,13 @@ return view.extend({
|
||||
/* Cache all configured proxy nodes, they will be called multiple times */
|
||||
let proxy_nodes = {};
|
||||
uci.sections(data[0], 'node', (res) => {
|
||||
let nodeaddr = ((res.type === 'direct') ? res.override_address : res.address) || '',
|
||||
nodeport = ((res.type === 'direct') ? res.override_port : res.port) || '';
|
||||
let nodeaddr = res.address || '',
|
||||
nodeport = res.port || '',
|
||||
endpoint = nodeaddr && nodeport ? ((stubValidator.apply('ip6addr', nodeaddr) ?
|
||||
String.format('[%s]', nodeaddr) : nodeaddr) + ':' + nodeport) : res['.name'];
|
||||
|
||||
proxy_nodes[res['.name']] =
|
||||
String.format('[%s] %s', res.type, res.label || ((stubValidator.apply('ip6addr', nodeaddr) ?
|
||||
String.format('[%s]', nodeaddr) : nodeaddr) + ':' + nodeport));
|
||||
String.format('[%s] %s', res.type, res.label || endpoint);
|
||||
});
|
||||
|
||||
m = new form.Map('homeproxy', _('HomeProxy'),
|
||||
@ -348,13 +349,6 @@ return view.extend({
|
||||
desc.innerHTML = _('Less compatibility and sometimes better performance.');
|
||||
}
|
||||
|
||||
so = ss.option(form.Flag, 'endpoint_independent_nat', _('Enable endpoint-independent NAT'),
|
||||
_('Performance may degrade slightly, so it is not recommended to enable on when it is not needed.'));
|
||||
so.default = so.enabled;
|
||||
so.depends('tcpip_stack', 'mixed');
|
||||
so.depends('tcpip_stack', 'gvisor');
|
||||
so.rmempty = false;
|
||||
|
||||
so = ss.option(form.Value, 'udp_timeout', _('UDP NAT expiration time'),
|
||||
_('In seconds.'));
|
||||
so.datatype = 'uinteger';
|
||||
@ -1410,6 +1404,7 @@ return view.extend({
|
||||
|
||||
return this.super('load', section_id);
|
||||
}
|
||||
so.default = 'direct-out';
|
||||
so.depends('type', 'remote');
|
||||
|
||||
so = ss.option(form.Value, 'update_interval', _('Update interval'),
|
||||
|
||||
@ -923,15 +923,6 @@ function renderNodeSettings(section, data, features, main_node, routing_mode, no
|
||||
}
|
||||
o.modalonly = true;
|
||||
|
||||
/* Direct config */
|
||||
o = s.option(form.ListValue, 'proxy_protocol', _('Proxy protocol'),
|
||||
_('Write proxy protocol in the connection header.'));
|
||||
o.value('', _('Disable'));
|
||||
o.value('1', _('v1'));
|
||||
o.value('2', _('v2'));
|
||||
o.depends('type', 'direct');
|
||||
o.modalonly = true;
|
||||
|
||||
/* AnyTLS config start */
|
||||
o = s.option(form.Value, 'anytls_idle_session_check_interval', _('Idle session check interval'),
|
||||
_('Interval checking for idle sessions, in seconds.'));
|
||||
|
||||
@ -60,56 +60,107 @@ function getConnStat(o, site) {
|
||||
]);
|
||||
}
|
||||
|
||||
function getResVersion(o, type) {
|
||||
const callResVersion = rpc.declare({
|
||||
const resources = [
|
||||
{
|
||||
type: 'china_ip4',
|
||||
name: _('China IPv4 list')
|
||||
},
|
||||
{
|
||||
type: 'china_ip6',
|
||||
name: _('China IPv6 list')
|
||||
},
|
||||
{
|
||||
type: 'china_list',
|
||||
name: _('China domain list')
|
||||
},
|
||||
{
|
||||
type: 'gfw_list',
|
||||
name: _('GFW domain list')
|
||||
}
|
||||
];
|
||||
|
||||
function getResources(o) {
|
||||
const callResStatus = rpc.declare({
|
||||
object: 'luci.homeproxy',
|
||||
method: 'resources_get_version',
|
||||
params: ['type'],
|
||||
method: 'resources_get',
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
const callResUpdate = rpc.declare({
|
||||
object: 'luci.homeproxy',
|
||||
method: 'resources_update',
|
||||
params: ['type'],
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
return L.resolveDefault(callResVersion(type), {}).then((res) => {
|
||||
let spanTemp = E('div', { 'style': 'cbi-value-field' }, [
|
||||
E('button', {
|
||||
'class': 'btn cbi-button cbi-button-action',
|
||||
'click': ui.createHandlerFn(this, () => {
|
||||
return L.resolveDefault(callResUpdate(type), {}).then((res) => {
|
||||
switch (res.status) {
|
||||
case 0:
|
||||
o.description = _('Successfully updated.');
|
||||
break;
|
||||
case 1:
|
||||
o.description = _('Update failed.');
|
||||
break;
|
||||
case 2:
|
||||
o.description = _('Already in updating.');
|
||||
break;
|
||||
case 3:
|
||||
o.description = _('Already at the latest version.');
|
||||
break;
|
||||
default:
|
||||
o.description = _('Unknown error.');
|
||||
break;
|
||||
}
|
||||
|
||||
return o.map.reset();
|
||||
});
|
||||
})
|
||||
}, [ _('Check update') ]),
|
||||
' ',
|
||||
E('strong', { 'style': (res.error ? 'color:red' : 'color:green') },
|
||||
[ res.error ? 'not found' : res.version ]
|
||||
),
|
||||
return L.resolveDefault(callResStatus(), { resources: [] }).then((result) => {
|
||||
const status = {};
|
||||
(result.resources || []).forEach((resource) => {
|
||||
status[resource.type] = resource;
|
||||
});
|
||||
const table = E('table', { 'class': 'table' }, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, _('Name')),
|
||||
E('th', { 'class': 'th' }, _('Version')),
|
||||
E('th', { 'class': 'th' }, _('Source'))
|
||||
])
|
||||
]);
|
||||
const rows = resources.map((resource) => {
|
||||
const resourceStatus = status[resource.type] || {};
|
||||
const available = resourceStatus.version;
|
||||
const source = resourceStatus.source;
|
||||
|
||||
o.default = spanTemp;
|
||||
return [
|
||||
resource.name,
|
||||
E('span', { 'style': available ? 'color:green' : 'color:red' },
|
||||
available || '-'),
|
||||
source ? E('a', {
|
||||
'href': source,
|
||||
'target': '_blank',
|
||||
'rel': 'noreferrer noopener',
|
||||
'style': 'word-break:break-all'
|
||||
}, source) : '-'
|
||||
];
|
||||
});
|
||||
cbi_update_table(table, rows);
|
||||
|
||||
return E('div', { 'class': 'cbi-map' }, [
|
||||
E('h3', { 'name': 'content', 'style': 'align-items:center;display:flex' }, [
|
||||
_('Resources management'),
|
||||
E('button', {
|
||||
'class': 'btn cbi-button cbi-button-action',
|
||||
'style': 'margin-left:4px',
|
||||
'click': ui.createHandlerFn(this, () => {
|
||||
return L.resolveDefault(callResUpdate(), {}).then((res) => {
|
||||
let message, severity = 'info';
|
||||
|
||||
switch (res.status) {
|
||||
case 0:
|
||||
message = _('Successfully updated.');
|
||||
break;
|
||||
case 1:
|
||||
message = _('Update failed.');
|
||||
severity = 'error';
|
||||
break;
|
||||
case 2:
|
||||
message = _('Already in updating.');
|
||||
break;
|
||||
case 3:
|
||||
message = _('Already at the latest version.');
|
||||
break;
|
||||
default:
|
||||
message = _('Unknown error.');
|
||||
severity = 'error';
|
||||
break;
|
||||
}
|
||||
|
||||
ui.addNotification(null, E('p', message), severity);
|
||||
return o.map.reset();
|
||||
});
|
||||
})
|
||||
}, [ _('Update all') ])
|
||||
]),
|
||||
E('div', { 'class': 'cbi-section' }, [ table ])
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
@ -238,24 +289,11 @@ return view.extend({
|
||||
o = s.option(form.DummyValue, '_check_google', _('Google'));
|
||||
o.cfgvalue = L.bind(getConnStat, this, o, 'google');
|
||||
|
||||
s = m.section(form.NamedSection, 'config', 'homeproxy', _('Resources management'));
|
||||
s = m.section(form.NamedSection, 'config', 'homeproxy');
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.DummyValue, '_china_ip4_version', _('China IPv4 list version'));
|
||||
o.cfgvalue = L.bind(getResVersion, this, o, 'china_ip4');
|
||||
o.rawhtml = true;
|
||||
|
||||
o = s.option(form.DummyValue, '_china_ip6_version', _('China IPv6 list version'));
|
||||
o.cfgvalue = L.bind(getResVersion, this, o, 'china_ip6');
|
||||
o.rawhtml = true;
|
||||
|
||||
o = s.option(form.DummyValue, '_china_list_version', _('China list version'));
|
||||
o.cfgvalue = L.bind(getResVersion, this, o, 'china_list');
|
||||
o.rawhtml = true;
|
||||
|
||||
o = s.option(form.DummyValue, '_gfw_list_version', _('GFW list version'));
|
||||
o.cfgvalue = L.bind(getResVersion, this, o, 'gfw_list');
|
||||
o.rawhtml = true;
|
||||
o = s.option(form.DummyValue, '_resources');
|
||||
o.render = L.bind(getResources, this, o);
|
||||
|
||||
o = s.option(form.Value, 'github_token', _('GitHub token'));
|
||||
o.password = true;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ import { cursor } from 'uci';
|
||||
|
||||
import {
|
||||
isEmpty, parseURL, strToBool, strToInt, strToTime,
|
||||
removeBlankAttrs, validation, HP_DIR, RUN_DIR
|
||||
removeBlankAttrs, renderEndpoint, renderOutbound, validation, HP_DIR, RUN_DIR
|
||||
} from 'homeproxy';
|
||||
|
||||
const ubus = connect();
|
||||
@ -117,8 +117,7 @@ const proxy_mode = uci.get(uciconfig, ucimain, 'proxy_mode') || 'redirect_tproxy
|
||||
const mixed_port = uci.get(uciconfig, uciinfra, 'mixed_port') || '5330';
|
||||
|
||||
let self_mark, redirect_port, tproxy_port, tun_name,
|
||||
tun_addr4, tun_addr6, tun_mtu, tcpip_stack,
|
||||
endpoint_independent_nat, udp_timeout;
|
||||
tun_addr4, tun_addr6, tun_mtu, tcpip_stack, udp_timeout;
|
||||
|
||||
if (routing_mode === 'custom')
|
||||
udp_timeout = uci.get(uciconfig, uciroutingsetting, 'udp_timeout');
|
||||
@ -140,7 +139,6 @@ if (match(proxy_mode, /tun/)) {
|
||||
tcpip_stack = 'system';
|
||||
if (routing_mode === 'custom') {
|
||||
tcpip_stack = uci.get(uciconfig, uciroutingsetting, 'tcpip_stack') || 'system';
|
||||
endpoint_independent_nat = uci.get(uciconfig, uciroutingsetting, 'endpoint_independent_nat');
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,168 +196,10 @@ function filter_existing_nodes(nodes) {
|
||||
});
|
||||
}
|
||||
|
||||
function generate_endpoint(node) {
|
||||
if (type(node) !== 'object' || isEmpty(node))
|
||||
return null;
|
||||
|
||||
const endpoint = {
|
||||
type: node.type,
|
||||
tag: 'cfg-' + node['.name'] + '-out',
|
||||
address: node.wireguard_local_address,
|
||||
mtu: strToInt(node.wireguard_mtu),
|
||||
private_key: node.wireguard_private_key,
|
||||
peers: (node.type === 'wireguard') ? [
|
||||
{
|
||||
address: node.address,
|
||||
port: strToInt(node.port),
|
||||
allowed_ips: [
|
||||
'0.0.0.0/0',
|
||||
'::/0'
|
||||
],
|
||||
persistent_keepalive_interval: strToInt(node.wireguard_persistent_keepalive_interval),
|
||||
public_key: node.wireguard_peer_public_key,
|
||||
pre_shared_key: node.wireguard_pre_shared_key,
|
||||
reserved: parse_port(node.wireguard_reserved),
|
||||
}
|
||||
] : null,
|
||||
system: (node.type === 'wireguard') ? false : null,
|
||||
tcp_fast_open: strToBool(node.tcp_fast_open),
|
||||
tcp_multi_path: strToBool(node.tcp_multi_path),
|
||||
udp_fragment: strToBool(node.udp_fragment)
|
||||
};
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
const generate_endpoint = renderEndpoint;
|
||||
|
||||
function generate_outbound(node) {
|
||||
if (type(node) !== 'object' || isEmpty(node))
|
||||
return null;
|
||||
|
||||
const tls_utls_value = (node.type === 'anytls' && isEmpty(node.tls_utls)) ? 'chrome' : node.tls_utls;
|
||||
const outbound = {
|
||||
type: node.type,
|
||||
tag: 'cfg-' + node['.name'] + '-out',
|
||||
routing_mark: strToInt(self_mark),
|
||||
|
||||
server: node.address,
|
||||
server_port: strToInt(node.port),
|
||||
/* Hysteria(2) */
|
||||
server_ports: node.hysteria_hopping_port,
|
||||
|
||||
username: (node.type !== 'ssh') ? node.username : null,
|
||||
user: (node.type === 'ssh') ? node.username : null,
|
||||
password: node.password,
|
||||
|
||||
/* Direct */
|
||||
override_address: node.override_address,
|
||||
override_port: strToInt(node.override_port),
|
||||
proxy_protocol: strToInt(node.proxy_protocol),
|
||||
/* AnyTLS */
|
||||
idle_session_check_interval: strToTime(node.anytls_idle_session_check_interval),
|
||||
idle_session_timeout: strToTime(node.anytls_idle_session_timeout),
|
||||
min_idle_session: strToInt(node.anytls_min_idle_session),
|
||||
/* Hysteria (2) */
|
||||
hop_interval: strToTime(node.hysteria_hop_interval),
|
||||
up_mbps: strToInt(node.hysteria_up_mbps),
|
||||
down_mbps: strToInt(node.hysteria_down_mbps),
|
||||
obfs: node.hysteria_obfs_type ? {
|
||||
type: node.hysteria_obfs_type,
|
||||
password: node.hysteria_obfs_password
|
||||
} : node.hysteria_obfs_password,
|
||||
auth: (node.hysteria_auth_type === 'base64') ? node.hysteria_auth_payload : null,
|
||||
auth_str: (node.hysteria_auth_type === 'string') ? node.hysteria_auth_payload : null,
|
||||
recv_window_conn: strToInt(node.hysteria_recv_window_conn),
|
||||
recv_window: strToInt(node.hysteria_revc_window),
|
||||
disable_mtu_discovery: strToBool(node.hysteria_disable_mtu_discovery),
|
||||
/* Shadowsocks */
|
||||
method: node.shadowsocks_encrypt_method,
|
||||
plugin: node.shadowsocks_plugin,
|
||||
plugin_opts: node.shadowsocks_plugin_opts,
|
||||
/* ShadowTLS / Socks */
|
||||
version: (node.type === 'shadowtls') ? strToInt(node.shadowtls_version) : ((node.type === 'socks') ? node.socks_version : null),
|
||||
/* SSH */
|
||||
client_version: node.ssh_client_version,
|
||||
host_key: node.ssh_host_key,
|
||||
host_key_algorithms: node.ssh_host_key_algo,
|
||||
private_key: node.ssh_priv_key,
|
||||
private_key_passphrase: node.ssh_priv_key_pp,
|
||||
/* Tuic */
|
||||
uuid: node.uuid,
|
||||
congestion_control: node.tuic_congestion_control,
|
||||
udp_relay_mode: node.tuic_udp_relay_mode,
|
||||
udp_over_stream: strToBool(node.tuic_udp_over_stream),
|
||||
zero_rtt_handshake: strToBool(node.tuic_enable_zero_rtt),
|
||||
heartbeat: strToTime(node.tuic_heartbeat),
|
||||
/* VLESS / VMess */
|
||||
flow: node.vless_flow,
|
||||
alter_id: strToInt(node.vmess_alterid),
|
||||
security: node.vmess_encrypt,
|
||||
global_padding: strToBool(node.vmess_global_padding),
|
||||
authenticated_length: strToBool(node.vmess_authenticated_length),
|
||||
packet_encoding: node.packet_encoding,
|
||||
|
||||
multiplex: (node.multiplex === '1') ? {
|
||||
enabled: true,
|
||||
protocol: node.multiplex_protocol,
|
||||
max_connections: strToInt(node.multiplex_max_connections),
|
||||
min_streams: strToInt(node.multiplex_min_streams),
|
||||
max_streams: strToInt(node.multiplex_max_streams),
|
||||
padding: strToBool(node.multiplex_padding),
|
||||
brutal: (node.multiplex_brutal === '1') ? {
|
||||
enabled: true,
|
||||
up_mbps: strToInt(node.multiplex_brutal_up),
|
||||
down_mbps: strToInt(node.multiplex_brutal_down)
|
||||
} : null
|
||||
} : null,
|
||||
tls: (node.tls === '1') ? {
|
||||
enabled: true,
|
||||
server_name: node.tls_sni,
|
||||
insecure: strToBool(node.tls_insecure),
|
||||
alpn: node.tls_alpn,
|
||||
min_version: node.tls_min_version,
|
||||
max_version: node.tls_max_version,
|
||||
cipher_suites: node.tls_cipher_suites,
|
||||
certificate_path: node.tls_cert_path,
|
||||
ech: (node.tls_ech === '1') ? {
|
||||
enabled: true,
|
||||
config: node.tls_ech_config,
|
||||
config_path: node.tls_ech_config_path
|
||||
} : null,
|
||||
utls: !isEmpty(tls_utls_value) ? {
|
||||
enabled: true,
|
||||
fingerprint: tls_utls_value
|
||||
} : null,
|
||||
reality: (node.tls_reality === '1') ? {
|
||||
enabled: true,
|
||||
public_key: node.tls_reality_public_key,
|
||||
short_id: node.tls_reality_short_id
|
||||
} : null
|
||||
} : null,
|
||||
transport: !isEmpty(node.transport) ? {
|
||||
type: node.transport,
|
||||
host: node.http_host || node.httpupgrade_host,
|
||||
path: node.http_path || node.ws_path,
|
||||
headers: node.ws_host ? {
|
||||
Host: node.ws_host
|
||||
} : null,
|
||||
method: node.http_method,
|
||||
max_early_data: strToInt(node.websocket_early_data),
|
||||
early_data_header_name: node.websocket_early_data_header,
|
||||
service_name: node.grpc_servicename,
|
||||
idle_timeout: strToTime(node.http_idle_timeout),
|
||||
ping_timeout: strToTime(node.http_ping_timeout),
|
||||
permit_without_stream: strToBool(node.grpc_permit_without_stream)
|
||||
} : null,
|
||||
udp_over_tcp: (node.udp_over_tcp === '1') ? {
|
||||
enabled: true,
|
||||
version: strToInt(node.udp_over_tcp_version)
|
||||
} : null,
|
||||
tcp_fast_open: strToBool(node.tcp_fast_open),
|
||||
tcp_multi_path: strToBool(node.tcp_multi_path),
|
||||
udp_fragment: strToBool(node.udp_fragment)
|
||||
};
|
||||
|
||||
return outbound;
|
||||
return renderOutbound(node, self_mark);
|
||||
}
|
||||
|
||||
function get_outbound(cfg) {
|
||||
@ -586,7 +426,6 @@ if (!isEmpty(main_node)) {
|
||||
rule_set_ip_cidr_match_source: strToBool(cfg.rule_set_ip_cidr_match_source),
|
||||
rule_set_ip_cidr_accept_empty: strToBool(cfg.rule_set_ip_cidr_accept_empty),
|
||||
invert: strToBool(cfg.invert),
|
||||
outbound: get_outbound(cfg.outbound),
|
||||
action: cfg.action,
|
||||
server: get_resolver(cfg.server),
|
||||
strategy: cfg.domain_strategy,
|
||||
@ -655,7 +494,6 @@ if (match(proxy_mode, /tun/))
|
||||
address: (ipv6_support === '1') ? [tun_addr4, tun_addr6] : [tun_addr4],
|
||||
mtu: strToInt(tun_mtu),
|
||||
auto_route: false,
|
||||
endpoint_independent_nat: strToBool(endpoint_independent_nat),
|
||||
udp_timeout: strToTime(udp_timeout),
|
||||
stack: tcpip_stack
|
||||
});
|
||||
@ -865,7 +703,6 @@ config.route = {
|
||||
if (!isEmpty(main_node)) {
|
||||
/* Avoid DNS loop */
|
||||
config.route.default_domain_resolver = {
|
||||
action: 'route',
|
||||
server: (routing_mode === 'bypass_mainland_china') ? 'china-dns' : 'default-dns',
|
||||
strategy: (ipv6_support !== '1') ? 'prefer_ipv4' : null
|
||||
};
|
||||
@ -919,21 +756,21 @@ if (!isEmpty(main_node)) {
|
||||
tag: 'geoip-cn',
|
||||
format: 'binary',
|
||||
url: 'https://fastly.jsdelivr.net/gh/1715173329/IPCIDR-CHINA@rule-set/cn.srs',
|
||||
download_detour: 'main-out'
|
||||
download_detour: 'direct-out'
|
||||
});
|
||||
push(config.route.rule_set, {
|
||||
type: 'remote',
|
||||
tag: 'geosite-cn',
|
||||
format: 'binary',
|
||||
url: 'https://fastly.jsdelivr.net/gh/1715173329/sing-geosite@rule-set-unstable/geosite-geolocation-cn.srs',
|
||||
download_detour: 'main-out'
|
||||
download_detour: 'direct-out'
|
||||
});
|
||||
push(config.route.rule_set, {
|
||||
type: 'remote',
|
||||
tag: 'geosite-noncn',
|
||||
format: 'binary',
|
||||
url: 'https://fastly.jsdelivr.net/gh/1715173329/sing-geosite@rule-set-unstable/geosite-geolocation-!cn.srs',
|
||||
download_detour: 'main-out'
|
||||
download_detour: 'direct-out'
|
||||
});
|
||||
}
|
||||
|
||||
@ -941,7 +778,6 @@ if (!isEmpty(main_node)) {
|
||||
config.route.rule_set = null;
|
||||
} else if (!isEmpty(default_outbound)) {
|
||||
config.route.default_domain_resolver = {
|
||||
action: 'resolve',
|
||||
server: get_resolver(default_outbound_dns)
|
||||
};
|
||||
|
||||
@ -1004,7 +840,7 @@ if (!isEmpty(main_node)) {
|
||||
format: cfg.format,
|
||||
path: cfg.path,
|
||||
url: cfg.url,
|
||||
download_detour: get_outbound(cfg.outbound),
|
||||
download_detour: get_outbound(cfg.outbound) || ((cfg.type === 'remote') ? 'direct-out' : null),
|
||||
update_interval: cfg.update_interval
|
||||
});
|
||||
});
|
||||
@ -1019,7 +855,7 @@ if (routing_mode in ['bypass_mainland_china', 'custom']) {
|
||||
},
|
||||
cache_file: {
|
||||
enabled: true,
|
||||
path: RUN_DIR + '/cache.db',
|
||||
path: HP_DIR + '/cache/cache.db',
|
||||
store_rdrc: strToBool(cache_file_store_rdrc),
|
||||
rdrc_timeout: strToTime(cache_file_rdrc_timeout),
|
||||
}
|
||||
@ -1028,4 +864,5 @@ if (routing_mode in ['bypass_mainland_china', 'custom']) {
|
||||
/* Experimental end */
|
||||
|
||||
system('mkdir -p ' + RUN_DIR);
|
||||
writefile(RUN_DIR + '/sing-box-c.json', sprintf('%.J\n', removeBlankAttrs(config)));
|
||||
if (!writefile(RUN_DIR + '/sing-box-c.json.new', sprintf('%.J\n', removeBlankAttrs(config))))
|
||||
exit(1);
|
||||
|
||||
@ -129,7 +129,7 @@ uci.foreach(uciconfig, uciserver, (cfg) => {
|
||||
email: cfg.tls_acme_email,
|
||||
provider: cfg.tls_acme_provider,
|
||||
disable_http_challenge: strToBool(cfg.tls_acme_dhc),
|
||||
disable_tls_alpn_challenge: (cfg.tls_acme_dtac),
|
||||
disable_tls_alpn_challenge: strToBool(cfg.tls_acme_dtac),
|
||||
alternative_http_port: strToInt(cfg.tls_acme_ahp),
|
||||
alternative_tls_port: strToInt(cfg.tls_acme_atp),
|
||||
external_account: (cfg.tls_acme_external_account === '1') ? {
|
||||
@ -182,4 +182,5 @@ if (length(config.inbounds) === 0)
|
||||
exit(1);
|
||||
|
||||
system('mkdir -p ' + RUN_DIR);
|
||||
writefile(RUN_DIR + '/sing-box-s.json', sprintf('%.J\n', removeBlankAttrs(config)));
|
||||
if (!writefile(RUN_DIR + '/sing-box-s.json.new', sprintf('%.J\n', removeBlankAttrs(config))))
|
||||
exit(1);
|
||||
|
||||
@ -95,6 +95,161 @@ export function strToTime(str) {
|
||||
return !isEmpty(str) ? (str + 's') : null;
|
||||
};
|
||||
|
||||
function strListToInts(value) {
|
||||
if (type(value) !== 'array' || isEmpty(value))
|
||||
return null;
|
||||
|
||||
return map(value, (item) => int(item));
|
||||
};
|
||||
|
||||
export function renderEndpoint(node) {
|
||||
if (type(node) !== 'object' || isEmpty(node))
|
||||
return null;
|
||||
|
||||
return {
|
||||
type: node.type,
|
||||
tag: 'cfg-' + node['.name'] + '-out',
|
||||
address: node.wireguard_local_address,
|
||||
mtu: strToInt(node.wireguard_mtu),
|
||||
private_key: node.wireguard_private_key,
|
||||
peers: (node.type === 'wireguard') ? [
|
||||
{
|
||||
address: node.address,
|
||||
port: strToInt(node.port),
|
||||
allowed_ips: [
|
||||
'0.0.0.0/0',
|
||||
'::/0'
|
||||
],
|
||||
persistent_keepalive_interval: strToInt(node.wireguard_persistent_keepalive_interval),
|
||||
public_key: node.wireguard_peer_public_key,
|
||||
pre_shared_key: node.wireguard_pre_shared_key,
|
||||
reserved: strListToInts(node.wireguard_reserved)
|
||||
}
|
||||
] : null,
|
||||
system: (node.type === 'wireguard') ? false : null,
|
||||
tcp_fast_open: strToBool(node.tcp_fast_open),
|
||||
tcp_multi_path: strToBool(node.tcp_multi_path),
|
||||
udp_fragment: strToBool(node.udp_fragment)
|
||||
};
|
||||
};
|
||||
|
||||
export function renderOutbound(node, routingMark) {
|
||||
if (type(node) !== 'object' || isEmpty(node))
|
||||
return null;
|
||||
|
||||
const tls_utls_value = (node.type === 'anytls' && isEmpty(node.tls_utls)) ? 'chrome' : node.tls_utls;
|
||||
return {
|
||||
type: node.type,
|
||||
tag: 'cfg-' + node['.name'] + '-out',
|
||||
routing_mark: strToInt(routingMark),
|
||||
|
||||
server: node.address,
|
||||
server_port: strToInt(node.port),
|
||||
server_ports: node.hysteria_hopping_port,
|
||||
|
||||
username: (node.type !== 'ssh') ? node.username : null,
|
||||
user: (node.type === 'ssh') ? node.username : null,
|
||||
password: node.password,
|
||||
|
||||
idle_session_check_interval: strToTime(node.anytls_idle_session_check_interval),
|
||||
idle_session_timeout: strToTime(node.anytls_idle_session_timeout),
|
||||
min_idle_session: strToInt(node.anytls_min_idle_session),
|
||||
hop_interval: strToTime(node.hysteria_hop_interval),
|
||||
up_mbps: strToInt(node.hysteria_up_mbps),
|
||||
down_mbps: strToInt(node.hysteria_down_mbps),
|
||||
obfs: node.hysteria_obfs_type ? {
|
||||
type: node.hysteria_obfs_type,
|
||||
password: node.hysteria_obfs_password
|
||||
} : node.hysteria_obfs_password,
|
||||
auth: (node.hysteria_auth_type === 'base64') ? node.hysteria_auth_payload : null,
|
||||
auth_str: (node.hysteria_auth_type === 'string') ? node.hysteria_auth_payload : null,
|
||||
recv_window_conn: strToInt(node.hysteria_recv_window_conn),
|
||||
recv_window: strToInt(node.hysteria_revc_window),
|
||||
disable_mtu_discovery: strToBool(node.hysteria_disable_mtu_discovery),
|
||||
method: node.shadowsocks_encrypt_method,
|
||||
plugin: node.shadowsocks_plugin,
|
||||
plugin_opts: node.shadowsocks_plugin_opts,
|
||||
version: (node.type === 'shadowtls') ? strToInt(node.shadowtls_version) : ((node.type === 'socks') ? node.socks_version : null),
|
||||
client_version: node.ssh_client_version,
|
||||
host_key: node.ssh_host_key,
|
||||
host_key_algorithms: node.ssh_host_key_algo,
|
||||
private_key: node.ssh_priv_key,
|
||||
private_key_passphrase: node.ssh_priv_key_pp,
|
||||
uuid: node.uuid,
|
||||
congestion_control: node.tuic_congestion_control,
|
||||
udp_relay_mode: node.tuic_udp_relay_mode,
|
||||
udp_over_stream: strToBool(node.tuic_udp_over_stream),
|
||||
zero_rtt_handshake: strToBool(node.tuic_enable_zero_rtt),
|
||||
heartbeat: strToTime(node.tuic_heartbeat),
|
||||
flow: node.vless_flow,
|
||||
alter_id: strToInt(node.vmess_alterid),
|
||||
security: node.vmess_encrypt,
|
||||
global_padding: strToBool(node.vmess_global_padding),
|
||||
authenticated_length: strToBool(node.vmess_authenticated_length),
|
||||
packet_encoding: node.packet_encoding,
|
||||
|
||||
multiplex: (node.multiplex === '1') ? {
|
||||
enabled: true,
|
||||
protocol: node.multiplex_protocol,
|
||||
max_connections: strToInt(node.multiplex_max_connections),
|
||||
min_streams: strToInt(node.multiplex_min_streams),
|
||||
max_streams: strToInt(node.multiplex_max_streams),
|
||||
padding: strToBool(node.multiplex_padding),
|
||||
brutal: (node.multiplex_brutal === '1') ? {
|
||||
enabled: true,
|
||||
up_mbps: strToInt(node.multiplex_brutal_up),
|
||||
down_mbps: strToInt(node.multiplex_brutal_down)
|
||||
} : null
|
||||
} : null,
|
||||
tls: (node.tls === '1') ? {
|
||||
enabled: true,
|
||||
server_name: node.tls_sni,
|
||||
insecure: strToBool(node.tls_insecure),
|
||||
alpn: node.tls_alpn,
|
||||
min_version: node.tls_min_version,
|
||||
max_version: node.tls_max_version,
|
||||
cipher_suites: node.tls_cipher_suites,
|
||||
certificate_path: node.tls_cert_path,
|
||||
ech: (node.tls_ech === '1') ? {
|
||||
enabled: true,
|
||||
config: node.tls_ech_config,
|
||||
config_path: node.tls_ech_config_path
|
||||
} : null,
|
||||
utls: !isEmpty(tls_utls_value) ? {
|
||||
enabled: true,
|
||||
fingerprint: tls_utls_value
|
||||
} : null,
|
||||
reality: (node.tls_reality === '1') ? {
|
||||
enabled: true,
|
||||
public_key: node.tls_reality_public_key,
|
||||
short_id: node.tls_reality_short_id
|
||||
} : null
|
||||
} : null,
|
||||
transport: !isEmpty(node.transport) ? {
|
||||
type: node.transport,
|
||||
host: node.http_host || node.httpupgrade_host,
|
||||
path: node.http_path || node.ws_path,
|
||||
headers: node.ws_host ? {
|
||||
Host: node.ws_host
|
||||
} : null,
|
||||
method: node.http_method,
|
||||
max_early_data: strToInt(node.websocket_early_data),
|
||||
early_data_header_name: node.websocket_early_data_header,
|
||||
service_name: node.grpc_servicename,
|
||||
idle_timeout: strToTime(node.http_idle_timeout),
|
||||
ping_timeout: strToTime(node.http_ping_timeout),
|
||||
permit_without_stream: strToBool(node.grpc_permit_without_stream)
|
||||
} : null,
|
||||
udp_over_tcp: (node.udp_over_tcp === '1') ? {
|
||||
enabled: true,
|
||||
version: strToInt(node.udp_over_tcp_version)
|
||||
} : null,
|
||||
tcp_fast_open: strToBool(node.tcp_fast_open),
|
||||
tcp_multi_path: strToBool(node.tcp_multi_path),
|
||||
udp_fragment: strToBool(node.udp_fragment)
|
||||
};
|
||||
};
|
||||
|
||||
export function removeBlankAttrs(res) {
|
||||
let content;
|
||||
|
||||
|
||||
@ -55,6 +55,10 @@ if (!uci.get(uciconfig, uciinfra, 'ntp_server'))
|
||||
if (!isEmpty(uci.get(uciconfig, uciinfra, 'tun_gso')))
|
||||
uci.delete(uciconfig, uciinfra, 'tun_gso');
|
||||
|
||||
/* endpoint_independent_nat has been removed */
|
||||
if (!isEmpty(uci.get(uciconfig, ucirouting, 'endpoint_independent_nat')))
|
||||
uci.delete(uciconfig, ucirouting, 'endpoint_independent_nat');
|
||||
|
||||
/* create migration section */
|
||||
if (!uci.get(uciconfig, ucimigration))
|
||||
uci.set(uciconfig, ucimigration, uciconfig);
|
||||
@ -99,6 +103,9 @@ uci.foreach(uciconfig, ucidnsserver, (cfg) => {
|
||||
/* legacy format was deprecated in sb 1.12 */
|
||||
if (cfg.address) {
|
||||
const addr = parseURL((!match(cfg.address, /:\/\//) ? 'udp://' : '') + (validation('ip6addr', cfg.address) ? `[${cfg.address}]` : cfg.address));
|
||||
if (!addr)
|
||||
return;
|
||||
|
||||
/* RCode was moved into DNS rules */
|
||||
if (addr.protocol === 'rcode') {
|
||||
dns_server_migration[cfg['.name']] = { action: 'predefined' };
|
||||
@ -157,10 +164,11 @@ uci.foreach(uciconfig, ucidnsrule, (cfg) => {
|
||||
/* outbound was removed in sb 1.12 */
|
||||
if (cfg.outbound) {
|
||||
uci.delete(uciconfig, cfg['.name']);
|
||||
if (!cfg.enabled)
|
||||
if (cfg.enabled !== '1')
|
||||
return;
|
||||
|
||||
map(cfg.outbound, (outbound) => {
|
||||
const outbounds = (type(cfg.outbound) === 'array') ? cfg.outbound : [ cfg.outbound ];
|
||||
for (let outbound in outbounds) {
|
||||
switch (outbound) {
|
||||
case 'direct-out':
|
||||
case 'block-out':
|
||||
@ -169,16 +177,16 @@ uci.foreach(uciconfig, ucidnsrule, (cfg) => {
|
||||
uci.set(uciconfig, ucirouting, 'default_outbound_dns', cfg.server);
|
||||
break;
|
||||
default:
|
||||
uci.set(uciconfig, cfg.outbound, 'domain_resolver', cfg.server);
|
||||
break;
|
||||
uci.set(uciconfig, outbound, 'domain_resolver', cfg.server);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* rule_set_ipcidr_match_source was renamed in sb 1.10 */
|
||||
if (cfg.rule_set_ipcidr_match_source === '1')
|
||||
if (!isEmpty(cfg.rule_set_ipcidr_match_source))
|
||||
uci.rename(uciconfig, cfg['.name'], 'rule_set_ipcidr_match_source', 'rule_set_ip_cidr_match_source');
|
||||
|
||||
/* block-dns was moved into action in sb 1.11 */
|
||||
@ -208,6 +216,11 @@ uci.foreach(uciconfig, ucidnsrule, (cfg) => {
|
||||
|
||||
/* nodes options */
|
||||
uci.foreach(uciconfig, ucinode, (cfg) => {
|
||||
/* destination override and Proxy Protocol were removed from direct outbound */
|
||||
for (let option in ['override_address', 'override_port', 'proxy_protocol'])
|
||||
if (!isEmpty(cfg[option]))
|
||||
uci.delete(uciconfig, cfg['.name'], option);
|
||||
|
||||
/* tls_ech_tls_disable_drs is useless and deprecated in sb 1.12 */
|
||||
if (!isEmpty(cfg.tls_ech_tls_disable_drs))
|
||||
uci.delete(uciconfig, cfg['.name'], 'tls_ech_tls_disable_drs');
|
||||
@ -224,7 +237,7 @@ uci.foreach(uciconfig, ucinode, (cfg) => {
|
||||
/* routing rules options */
|
||||
uci.foreach(uciconfig, uciroutingrule, (cfg) => {
|
||||
/* rule_set_ipcidr_match_source was renamed in sb 1.10 */
|
||||
if (cfg.rule_set_ipcidr_match_source === '1')
|
||||
if (!isEmpty(cfg.rule_set_ipcidr_match_source))
|
||||
uci.rename(uciconfig, cfg['.name'], 'rule_set_ipcidr_match_source', 'rule_set_ip_cidr_match_source');
|
||||
|
||||
/* block-out was moved into action in sb 1.11 */
|
||||
|
||||
@ -5,8 +5,6 @@
|
||||
|
||||
SCRIPTS_DIR="/etc/homeproxy/scripts"
|
||||
|
||||
for i in "china_ip4" "china_ip6" "gfw_list" "china_list"; do
|
||||
"$SCRIPTS_DIR"/update_resources.sh "$i"
|
||||
done
|
||||
"$SCRIPTS_DIR"/update_resources.sh
|
||||
|
||||
"$SCRIPTS_DIR"/update_subscriptions.uc
|
||||
|
||||
@ -5,85 +5,102 @@
|
||||
|
||||
NAME="homeproxy"
|
||||
|
||||
RESOURCES_DIR="/etc/$NAME/resources"
|
||||
mkdir -p "$RESOURCES_DIR"
|
||||
|
||||
RUN_DIR="/var/run/$NAME"
|
||||
RESOURCES_DIR="${RESOURCES_DIR:-/etc/$NAME/resources}"
|
||||
RUN_DIR="${RUN_DIR:-/var/run/$NAME}"
|
||||
LOG_PATH="$RUN_DIR/$NAME.log"
|
||||
mkdir -p "$RUN_DIR"
|
||||
REPO_NAME="Loyalsoldier/surge-rules"
|
||||
REPO_BRANCH="release"
|
||||
SOURCE_BASE="${SOURCE_BASE:-https://cdn.jsdelivr.net/gh/$REPO_NAME@$REPO_BRANCH}"
|
||||
RELEASE_API="${RELEASE_API:-https://api.github.com/repos/$REPO_NAME/releases/latest}"
|
||||
|
||||
mkdir -p "$RESOURCES_DIR" "$RUN_DIR"
|
||||
|
||||
log() {
|
||||
echo -e "$(date "+%Y-%m-%d %H:%M:%S") $*" >> "$LOG_PATH"
|
||||
printf '%s %s\n' "$(date "+%Y-%m-%d %H:%M:%S")" "$*" >> "$LOG_PATH"
|
||||
}
|
||||
|
||||
to_upper() {
|
||||
echo -e "$1" | tr "[a-z]" "[A-Z]"
|
||||
printf '%s\n' "$1" | tr '[:lower:]' '[:upper:]'
|
||||
}
|
||||
|
||||
check_list_update() {
|
||||
local LIST_FILE="$1"
|
||||
local REPO_NAME="$2"
|
||||
local REPO_BRANCH="$3"
|
||||
local REPO_FILE="$4"
|
||||
local LOCK_FILE="$RUN_DIR/update_resources-$LIST_FILE.lock"
|
||||
local GITHUB_TOKEN="$(uci -q get homeproxy.config.github_token)"
|
||||
download() {
|
||||
local source_file="$1"
|
||||
local target_file="$2"
|
||||
|
||||
exec 200>"$LOCK_FILE"
|
||||
if ! flock -n 200 &> "/dev/null"; then
|
||||
log "[$(to_upper "$LIST_FILE")] A task is already running."
|
||||
return 2
|
||||
fi
|
||||
|
||||
local NEW_VER=$(curl -sL ${GITHUB_TOKEN:+-H "Authorization: Bearer $GITHUB_TOKEN"} "https://api.github.com/repos/$REPO_NAME/releases/latest" | jsonfilter -e "@.tag_name")
|
||||
if [ -z "$NEW_VER" ]; then
|
||||
log "[$(to_upper "$LIST_FILE")] Failed to get the latest version, please retry later."
|
||||
|
||||
return 1
|
||||
fi
|
||||
|
||||
local OLD_VER=$(cat "$RESOURCES_DIR/$LIST_FILE.ver" 2>/dev/null || echo "NOT FOUND")
|
||||
if [ "$OLD_VER" = "$NEW_VER" ]; then
|
||||
log "[$(to_upper "$LIST_FILE")] Current version: $NEW_VER."
|
||||
log "[$(to_upper "$LIST_FILE")] You're already at the latest version."
|
||||
|
||||
return 3
|
||||
else
|
||||
log "[$(to_upper "$LIST_FILE")] Local version: $OLD_VER, latest version: $NEW_VER."
|
||||
fi
|
||||
|
||||
if ! curl -sL -o "$RUN_DIR/$REPO_FILE" "https://cdn.jsdelivr.net/gh/$REPO_NAME@$REPO_BRANCH/$REPO_FILE" || [ ! -s "$RUN_DIR/$REPO_FILE" ]; then
|
||||
rm -f "$RUN_DIR/$REPO_FILE"
|
||||
log "[$(to_upper "$LIST_FILE")] Update failed."
|
||||
|
||||
return 1
|
||||
fi
|
||||
|
||||
mv -f "$RUN_DIR/$REPO_FILE" "$RESOURCES_DIR/$LIST_FILE.${REPO_FILE##*.}"
|
||||
echo -e "$NEW_VER" > "$RESOURCES_DIR/$LIST_FILE.ver"
|
||||
log "[$(to_upper "$LIST_FILE")] Successfully updated."
|
||||
|
||||
return 0
|
||||
curl -fsSL --retry 2 --connect-timeout 10 --max-time 60 \
|
||||
-o "$target_file" "$SOURCE_BASE/$source_file" && [ -s "$target_file" ]
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
"china_ip4")
|
||||
check_list_update "$1" "Loyalsoldier/surge-rules" "release" "cncidr.txt" && \
|
||||
sed -i "/IP-CIDR6,/d; s/IP-CIDR,//g" "$RESOURCES_DIR/china_ip4.txt"
|
||||
;;
|
||||
"china_ip6")
|
||||
check_list_update "$1" "Loyalsoldier/surge-rules" "release" "cncidr.txt" && \
|
||||
sed -i "/IP-CIDR,/d; s/IP-CIDR6,//g" "$RESOURCES_DIR/china_ip6.txt"
|
||||
;;
|
||||
"gfw_list")
|
||||
check_list_update "$1" "Loyalsoldier/surge-rules" "release" "gfw.txt" && \
|
||||
sed -i "s/^\.//g" "$RESOURCES_DIR/gfw_list.txt"
|
||||
;;
|
||||
"china_list")
|
||||
check_list_update "$1" "Loyalsoldier/surge-rules" "release" "direct.txt" && \
|
||||
sed -i "s/^\.//g" "$RESOURCES_DIR/china_list.txt"
|
||||
;;
|
||||
*)
|
||||
echo -e "Usage: $0 <china_ip4 / china_ip6 / gfw_list / china_list>"
|
||||
exec 9>"$RUN_DIR/update_resources.lock"
|
||||
if ! flock -n 9 > "/dev/null" 2>&1; then
|
||||
log "[RESOURCES] A task is already running."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
GITHUB_TOKEN="${GITHUB_TOKEN:-$(uci -q get homeproxy.config.github_token)}"
|
||||
if [ -n "$GITHUB_TOKEN" ]; then
|
||||
NEW_VER="$(curl -fsSL --retry 2 --connect-timeout 10 --max-time 30 \
|
||||
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
||||
"$RELEASE_API" | jsonfilter -e "@.tag_name")"
|
||||
else
|
||||
NEW_VER="$(curl -fsSL --retry 2 --connect-timeout 10 --max-time 30 \
|
||||
"$RELEASE_API" | jsonfilter -e "@.tag_name")"
|
||||
fi
|
||||
if [ -z "$NEW_VER" ]; then
|
||||
log "[RESOURCES] Failed to get the latest version, please retry later."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
CURRENT=1
|
||||
for RESOURCE in china_ip4 china_ip6 china_list gfw_list; do
|
||||
OLD_VER="$(cat "$RESOURCES_DIR/$RESOURCE.ver" 2>/dev/null || echo "NOT FOUND")"
|
||||
if [ "$OLD_VER" = "$NEW_VER" ]; then
|
||||
log "[$(to_upper "$RESOURCE")] Current version: $NEW_VER."
|
||||
else
|
||||
CURRENT=0
|
||||
log "[$(to_upper "$RESOURCE")] Local version: $OLD_VER, latest version: $NEW_VER."
|
||||
fi
|
||||
done
|
||||
[ "$CURRENT" -eq 0 ] || {
|
||||
log "[RESOURCES] You're already at the latest version."
|
||||
exit 3
|
||||
}
|
||||
|
||||
TMP_DIR="$(mktemp -d "$RUN_DIR/resources-update.XXXXXX")" || exit 1
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT INT TERM
|
||||
|
||||
if ! download "cncidr.txt" "$TMP_DIR/cncidr.txt" || \
|
||||
! download "direct.txt" "$TMP_DIR/china_list.txt" || \
|
||||
! download "gfw.txt" "$TMP_DIR/gfw_list.txt"; then
|
||||
log "[RESOURCES] Update failed while downloading source lists."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "$TMP_DIR/cncidr.txt" "$TMP_DIR/china_ip4.txt" && \
|
||||
cp "$TMP_DIR/cncidr.txt" "$TMP_DIR/china_ip6.txt" && \
|
||||
sed -i "/IP-CIDR6,/d; s/IP-CIDR,//g" "$TMP_DIR/china_ip4.txt" && \
|
||||
sed -i "/IP-CIDR,/d; s/IP-CIDR6,//g" "$TMP_DIR/china_ip6.txt" && \
|
||||
sed -i "s/^\\.//g" "$TMP_DIR/china_list.txt" "$TMP_DIR/gfw_list.txt"
|
||||
if [ "$?" -ne 0 ]; then
|
||||
log "[RESOURCES] Update failed while processing source lists."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for RESOURCE in china_ip4 china_ip6 china_list gfw_list; do
|
||||
if [ ! -s "$TMP_DIR/$RESOURCE.txt" ] || grep -qi '<html' "$TMP_DIR/$RESOURCE.txt"; then
|
||||
log "[$(to_upper "$RESOURCE")] Update failed: invalid processed list."
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' "$NEW_VER" > "$TMP_DIR/$RESOURCE.ver" || exit 1
|
||||
done
|
||||
|
||||
for RESOURCE in china_ip4 china_ip6 china_list gfw_list; do
|
||||
if ! mv -f "$TMP_DIR/$RESOURCE.txt" "$RESOURCES_DIR/$RESOURCE.txt" || \
|
||||
! mv -f "$TMP_DIR/$RESOURCE.ver" "$RESOURCES_DIR/$RESOURCE.ver"; then
|
||||
log "[$(to_upper "$RESOURCE")] Update failed: unable to replace list."
|
||||
exit 1
|
||||
fi
|
||||
log "[$(to_upper "$RESOURCE")] Successfully updated."
|
||||
done
|
||||
|
||||
exit 0
|
||||
|
||||
@ -17,7 +17,7 @@ import { init_action } from 'luci.sys';
|
||||
|
||||
import {
|
||||
wGET, decodeBase64Str, getTime, isEmpty, parseURL,
|
||||
validation, HP_DIR, RUN_DIR
|
||||
shellQuote, validation, HP_DIR, RUN_DIR
|
||||
} from 'homeproxy';
|
||||
|
||||
/* UCI config start */
|
||||
@ -80,6 +80,18 @@ function log(...args) {
|
||||
logfile.close();
|
||||
}
|
||||
|
||||
if (getenv('HOMEPROXY_SUBSCRIPTION_LOCKED') !== '1') {
|
||||
const lock_path = `${RUN_DIR}/update_subscriptions.lock`;
|
||||
const script_path = `${HP_DIR}/scripts/update_subscriptions.uc`;
|
||||
const status = system(sprintf(
|
||||
'HOMEPROXY_SUBSCRIPTION_LOCKED=1 flock -n %s %s',
|
||||
shellQuote(lock_path), shellQuote(script_path)
|
||||
));
|
||||
if (status !== 0)
|
||||
log('Subscription update did not complete; another task may be running.');
|
||||
exit(status);
|
||||
}
|
||||
|
||||
function has_value(value) {
|
||||
return value !== null && value !== '' && value !== 'nil';
|
||||
}
|
||||
@ -1000,7 +1012,8 @@ function main() {
|
||||
const label = config.label;
|
||||
config.label = null;
|
||||
const confHash = md5(sprintf('%J', config)),
|
||||
nameHash = md5(label);
|
||||
nameHash = md5(groupHash + ':' + label),
|
||||
legacyNameHash = md5(label);
|
||||
config.label = label;
|
||||
|
||||
if (filter_check(config.label))
|
||||
@ -1018,6 +1031,7 @@ function main() {
|
||||
push(node_result[length(node_result)-1], config);
|
||||
node_cache[groupHash][confHash] = config;
|
||||
node_cache[groupHash][nameHash] = config;
|
||||
node_cache[groupHash][legacyNameHash] = config;
|
||||
|
||||
count++;
|
||||
}
|
||||
@ -1040,7 +1054,7 @@ function main() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let added = 0, removed = 0;
|
||||
let added = 0, removed = 0, updated = 0;
|
||||
uci.foreach(uciconfig, ucinode, (cfg) => {
|
||||
/* Nodes created by the user */
|
||||
if (!cfg.grouphash)
|
||||
@ -1054,50 +1068,54 @@ function main() {
|
||||
uci.delete(uciconfig, cfg['.name']);
|
||||
removed++;
|
||||
|
||||
log(sprintf('Removing node: %s.', cfg.label || cfg['name']));
|
||||
log(sprintf('Removing node: %s.', cfg.label || cfg['.name']));
|
||||
} else {
|
||||
map(keys(cfg), (v) => {
|
||||
if (v in node_cache[cfg.grouphash][cfg['.name']])
|
||||
uci.set(uciconfig, cfg['.name'], v, node_cache[cfg.grouphash][cfg['.name']][v]);
|
||||
else
|
||||
uci.delete(uciconfig, cfg['.name'], v);
|
||||
});
|
||||
node_cache[cfg.grouphash][cfg['.name']].isExisting = true;
|
||||
const next = node_cache[cfg.grouphash][cfg['.name']];
|
||||
for (let option in keys(cfg))
|
||||
if (!match(option, /^\./) && !(option in next))
|
||||
uci.delete(uciconfig, cfg['.name'], option);
|
||||
|
||||
for (let option in keys(next))
|
||||
if (option !== 'isExisting')
|
||||
uci.set(uciconfig, cfg['.name'], option, next[option]);
|
||||
|
||||
next.isExisting = true;
|
||||
updated++;
|
||||
}
|
||||
});
|
||||
|
||||
for (let nodes in node_result)
|
||||
map(nodes, (node) => {
|
||||
if (node.isExisting)
|
||||
return null;
|
||||
|
||||
const nameHash = md5(node.label);
|
||||
const nameHash = md5(node.grouphash + ':' + node.label);
|
||||
uci.set(uciconfig, nameHash, 'node');
|
||||
map(keys(node), (v) => uci.set(uciconfig, nameHash, v, node[v]));
|
||||
|
||||
added++;
|
||||
log(sprintf('Adding node: %s.', node.label));
|
||||
});
|
||||
uci.commit(uciconfig);
|
||||
|
||||
let need_restart = (via_proxy !== '1');
|
||||
if (!isEmpty(main_node)) {
|
||||
const first_server = uci.get_first(uciconfig, ucinode);
|
||||
if (first_server) {
|
||||
let main_urltest_nodes;
|
||||
if (main_node === 'urltest') {
|
||||
main_urltest_nodes = filter(uci.get(uciconfig, ucimain, 'main_urltest_nodes'), (v) => {
|
||||
main_urltest_nodes = filter(normalize_list(uci.get(uciconfig, ucimain, 'main_urltest_nodes')) || [], (v) => {
|
||||
if (!uci.get(uciconfig, v)) {
|
||||
log(sprintf('Node %s is gone, removing from urltest list.', v));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (length(main_urltest_nodes))
|
||||
uci.set(uciconfig, ucimain, 'main_urltest_nodes', main_urltest_nodes);
|
||||
}
|
||||
|
||||
if ((main_node === 'urltest') ? !length(main_urltest_nodes) : !uci.get(uciconfig, main_node)) {
|
||||
uci.set(uciconfig, ucimain, 'main_node', first_server);
|
||||
uci.commit(uciconfig);
|
||||
need_restart = true;
|
||||
uci.delete(uciconfig, ucimain, 'main_urltest_nodes');
|
||||
|
||||
log('Main node is gone, switching to the first node.');
|
||||
}
|
||||
@ -1105,19 +1123,20 @@ function main() {
|
||||
if (!isEmpty(main_udp_node) && main_udp_node !== 'same') {
|
||||
let main_udp_urltest_nodes;
|
||||
if (main_udp_node === 'urltest') {
|
||||
main_udp_urltest_nodes = filter(uci.get(uciconfig, ucimain, 'main_udp_urltest_nodes'), (v) => {
|
||||
main_udp_urltest_nodes = filter(normalize_list(uci.get(uciconfig, ucimain, 'main_udp_urltest_nodes')) || [], (v) => {
|
||||
if (!uci.get(uciconfig, v)) {
|
||||
log(sprintf('Node %s is gone, removing from urltest list.', v));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (length(main_udp_urltest_nodes))
|
||||
uci.set(uciconfig, ucimain, 'main_udp_urltest_nodes', main_udp_urltest_nodes);
|
||||
}
|
||||
|
||||
if ((main_udp_node === 'urltest') ? !length(main_udp_urltest_nodes) : !uci.get(uciconfig, main_udp_node)) {
|
||||
uci.set(uciconfig, ucimain, 'main_udp_node', first_server);
|
||||
uci.commit(uciconfig);
|
||||
need_restart = true;
|
||||
uci.delete(uciconfig, ucimain, 'main_udp_urltest_nodes');
|
||||
|
||||
log('Main UDP node is gone, switching to the first node.');
|
||||
}
|
||||
@ -1125,20 +1144,19 @@ function main() {
|
||||
} else {
|
||||
uci.set(uciconfig, ucimain, 'main_node', 'nil');
|
||||
uci.set(uciconfig, ucimain, 'main_udp_node', 'nil');
|
||||
uci.commit(uciconfig);
|
||||
need_restart = true;
|
||||
uci.delete(uciconfig, ucimain, 'main_urltest_nodes');
|
||||
uci.delete(uciconfig, ucimain, 'main_udp_urltest_nodes');
|
||||
|
||||
log('No available node, disable tproxy.');
|
||||
}
|
||||
}
|
||||
uci.commit(uciconfig);
|
||||
|
||||
if (need_restart) {
|
||||
log('Restarting service...');
|
||||
init_action('homeproxy', 'stop');
|
||||
init_action('homeproxy', 'start');
|
||||
}
|
||||
log('Restarting service...');
|
||||
init_action('homeproxy', 'stop');
|
||||
init_action('homeproxy', 'start');
|
||||
|
||||
log(sprintf('%s nodes added, %s removed.', added, removed));
|
||||
log(sprintf('%s nodes added, %s updated, %s removed.', added, updated, removed));
|
||||
log('Successfully updated subscriptions.');
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,8 @@ PROG="/usr/bin/sing-box"
|
||||
HP_DIR="/etc/homeproxy"
|
||||
RUN_DIR="/var/run/homeproxy"
|
||||
LOG_PATH="$RUN_DIR/homeproxy.log"
|
||||
CACHE_DIR="$HP_DIR/cache"
|
||||
CACHE_PATH="$CACHE_DIR/cache.db"
|
||||
|
||||
# we don't know which is the default server, just take the first one
|
||||
DNSMASQ_UCI_CONFIG="$(uci -q show "dhcp.@dnsmasq[0]" | awk 'NR==1 {split($0, conf, /[.=]/); print conf[2]}')"
|
||||
@ -27,6 +29,23 @@ log() {
|
||||
echo -e "$(date "+%Y-%m-%d %H:%M:%S") [DAEMON] $*" >> "$LOG_PATH"
|
||||
}
|
||||
|
||||
validate_cron() {
|
||||
local expression="$1" field
|
||||
|
||||
case "$expression" in
|
||||
""|*[!0-9*/?,\ -]*) return 1 ;;
|
||||
esac
|
||||
|
||||
set -f
|
||||
set -- $expression
|
||||
set +f
|
||||
[ "$#" -eq 5 ] || return 1
|
||||
|
||||
for field in "$@"; do
|
||||
[ -n "$field" ] || return 1
|
||||
done
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load "$CONF"
|
||||
|
||||
@ -49,26 +68,65 @@ start_service() {
|
||||
fi
|
||||
|
||||
mkdir -p "$RUN_DIR"
|
||||
rm -f "$RUN_DIR/sing-box-c.json.new" "$RUN_DIR/sing-box-s.json.new"
|
||||
|
||||
# Generate and validate every enabled instance before changing system state.
|
||||
if [ "$outbound_node" != "nil" ]; then
|
||||
# Generate/Validate client config
|
||||
ucode -S "$HP_DIR/scripts/generate_client.uc" 2>>"$LOG_PATH"
|
||||
|
||||
if [ ! -e "$RUN_DIR/sing-box-c.json" ]; then
|
||||
if ! ucode -S "$HP_DIR/scripts/generate_client.uc" 2>>"$LOG_PATH" || \
|
||||
[ ! -s "$RUN_DIR/sing-box-c.json.new" ]; then
|
||||
log "Error: failed to generate client configuration."
|
||||
rm -f "$RUN_DIR/sing-box-c.json.new" "$RUN_DIR/sing-box-s.json.new"
|
||||
return 1
|
||||
elif ! "$PROG" check --config "$RUN_DIR/sing-box-c.json" 2>>"$LOG_PATH"; then
|
||||
elif ! "$PROG" check --config "$RUN_DIR/sing-box-c.json.new" 2>>"$LOG_PATH"; then
|
||||
log "Error: wrong client configuration detected."
|
||||
rm -f "$RUN_DIR/sing-box-c.json.new" "$RUN_DIR/sing-box-s.json.new"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$server_enabled" = "1" ]; then
|
||||
if ! ucode -S "$HP_DIR/scripts/generate_server.uc" 2>>"$LOG_PATH" || \
|
||||
[ ! -s "$RUN_DIR/sing-box-s.json.new" ]; then
|
||||
log "Error: failed to generate server configuration."
|
||||
rm -f "$RUN_DIR/sing-box-c.json.new" "$RUN_DIR/sing-box-s.json.new"
|
||||
return 1
|
||||
elif ! "$PROG" check --config "$RUN_DIR/sing-box-s.json.new" 2>>"$LOG_PATH"; then
|
||||
log "Error: wrong server configuration detected."
|
||||
rm -f "$RUN_DIR/sing-box-c.json.new" "$RUN_DIR/sing-box-s.json.new"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if { [ "$outbound_node" = "nil" ] || mv -f "$RUN_DIR/sing-box-c.json.new" "$RUN_DIR/sing-box-c.json"; } && \
|
||||
{ [ "$server_enabled" = "0" ] || mv -f "$RUN_DIR/sing-box-s.json.new" "$RUN_DIR/sing-box-s.json"; }; then
|
||||
[ "$outbound_node" != "nil" ] || rm -f "$RUN_DIR/sing-box-c.json"
|
||||
[ "$server_enabled" = "1" ] || rm -f "$RUN_DIR/sing-box-s.json"
|
||||
else
|
||||
log "Error: failed to install generated configuration."
|
||||
rm -f "$RUN_DIR/sing-box-c.json.new" "$RUN_DIR/sing-box-s.json.new"
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "$routing_mode" in
|
||||
"bypass_mainland_china"|"custom")
|
||||
mkdir -p "$CACHE_DIR"
|
||||
touch "$CACHE_PATH"
|
||||
chown -R sing-box:sing-box "$CACHE_DIR"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$outbound_node" != "nil" ]; then
|
||||
# Auto update
|
||||
local auto_update auto_update_time
|
||||
config_get_bool auto_update "subscription" "auto_update" "0"
|
||||
if [ "$auto_update" = "1" ]; then
|
||||
config_get auto_update_time "subscription" "auto_update_time" "0 */6 * * *"
|
||||
sed -i "/#${CONF}_autosetup/d" "/etc/crontabs/root" 2>"/dev/null"
|
||||
echo -e "$auto_update_time $HP_DIR/scripts/update_crond.sh #${CONF}_autosetup" >> "/etc/crontabs/root"
|
||||
if validate_cron "$auto_update_time"; then
|
||||
printf '%s %s #%s_autosetup\n' "$auto_update_time" "$HP_DIR/scripts/update_crond.sh" "$CONF" >> "/etc/crontabs/root"
|
||||
else
|
||||
log "Error: invalid subscription update schedule."
|
||||
fi
|
||||
/etc/init.d/cron restart
|
||||
fi
|
||||
|
||||
@ -79,7 +137,29 @@ start_service() {
|
||||
mkdir -p "$DNSMASQ_DIR"
|
||||
echo -e "conf-dir=$DNSMASQ_DIR" > "$DNSMASQ_DIR/../dnsmasq-homeproxy.conf"
|
||||
case "$routing_mode" in
|
||||
"bypass_mainland_china"|"custom"|"global")
|
||||
"bypass_mainland_china")
|
||||
cat <<-EOF >> "$DNSMASQ_DIR/redirect-dns.conf"
|
||||
no-poll
|
||||
no-resolv
|
||||
server=127.0.0.1#$dns_port
|
||||
EOF
|
||||
|
||||
local wan_interface wan_dns china_dns candidate
|
||||
. /lib/functions/network.sh
|
||||
if network_find_wan wan_interface && network_get_dnsserver wan_dns "$wan_interface"; then
|
||||
for candidate in $wan_dns; do
|
||||
case "$candidate" in
|
||||
*:*) ;;
|
||||
*) china_dns="$candidate"; break ;;
|
||||
esac
|
||||
done
|
||||
[ -n "$china_dns" ] || china_dns="${wan_dns%% *}"
|
||||
fi
|
||||
china_dns="${china_dns:-223.5.5.5}"
|
||||
sed -r -e '/^\s*$/d' -e "s|(.*)|server=/\1/$china_dns|g" \
|
||||
"$HP_DIR/resources/china_list.txt" > "$DNSMASQ_DIR/china_list.conf"
|
||||
;;
|
||||
"custom"|"global")
|
||||
cat <<-EOF >> "$DNSMASQ_DIR/redirect-dns.conf"
|
||||
no-poll
|
||||
no-resolv
|
||||
@ -156,7 +236,7 @@ start_service() {
|
||||
procd_add_jail "sing-box-c" log procfs
|
||||
procd_add_jail_mount "$RUN_DIR/sing-box-c.json"
|
||||
procd_add_jail_mount_rw "$RUN_DIR/sing-box-c.log"
|
||||
[ "$routing_mode" != "bypass_mainland_china" ] || procd_add_jail_mount_rw "$RUN_DIR/cache.db"
|
||||
[ "$routing_mode" != "bypass_mainland_china" ] || procd_add_jail_mount_rw "$CACHE_PATH"
|
||||
procd_add_jail_mount "$HP_DIR/certs/"
|
||||
procd_add_jail_mount "/etc/ssl/"
|
||||
procd_add_jail_mount "/etc/localtime"
|
||||
@ -176,17 +256,6 @@ start_service() {
|
||||
fi
|
||||
|
||||
if [ "$server_enabled" = "1" ]; then
|
||||
# Generate/Validate server config
|
||||
ucode -S "$HP_DIR/scripts/generate_server.uc" 2>>"$LOG_PATH"
|
||||
|
||||
if [ ! -e "$RUN_DIR/sing-box-s.json" ]; then
|
||||
log "Error: failed to generate server configuration."
|
||||
return 1
|
||||
elif ! "$PROG" check --config "$RUN_DIR/sing-box-s.json" 2>>"$LOG_PATH"; then
|
||||
log "Error: wrong server configuration detected."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# sing-box (server)
|
||||
procd_open_instance "sing-box-s"
|
||||
|
||||
@ -226,10 +295,6 @@ start_service() {
|
||||
procd_close_instance
|
||||
|
||||
case "$routing_mode" in
|
||||
"bypass_mainland_china")
|
||||
# Prepare cache db
|
||||
[ -e "$RUN_DIR/cache.db" ] || touch "$RUN_DIR/cache.db"
|
||||
;;
|
||||
"custom")
|
||||
# Prepare ruleset directory
|
||||
[ -d "$HP_DIR/ruleset" ] || mkdir -p "$HP_DIR/ruleset"
|
||||
@ -308,8 +373,8 @@ stop_service() {
|
||||
rm -rf "$DNSMASQ_DIR/../dnsmasq-homeproxy.conf" "$DNSMASQ_DIR"
|
||||
/etc/init.d/dnsmasq restart >"/dev/null" 2>&1
|
||||
|
||||
rm -f "$RUN_DIR/sing-box-c.json" "$RUN_DIR/sing-box-c.log" \
|
||||
"$RUN_DIR/sing-box-s.json" "$RUN_DIR/sing-box-s.log"
|
||||
rm -f "$RUN_DIR/sing-box-c.json" "$RUN_DIR/sing-box-c.json.new" "$RUN_DIR/sing-box-c.log" \
|
||||
"$RUN_DIR/sing-box-s.json" "$RUN_DIR/sing-box-s.json.new" "$RUN_DIR/sing-box-s.log"
|
||||
|
||||
log "Service stopped."
|
||||
}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
ucode "/etc/homeproxy/scripts/migrate_config.uc"
|
||||
|
||||
exit 0
|
||||
exec ucode "/etc/homeproxy/scripts/migrate_config.uc"
|
||||
|
||||
@ -3,21 +3,30 @@
|
||||
"description": "Grant access to homeproxy configuration",
|
||||
"read": {
|
||||
"file": {
|
||||
"/etc/homeproxy/scripts/update_subscriptions.uc": [ "exec" ],
|
||||
"/var/run/homeproxy/homeproxy.log": [ "read" ],
|
||||
"/var/run/homeproxy/sing-box-c.log": [ "read" ],
|
||||
"/var/run/homeproxy/sing-box-s.log": [ "read" ]
|
||||
},
|
||||
"ubus": {
|
||||
"service": [ "list" ],
|
||||
"luci.homeproxy": [ "*" ]
|
||||
"luci.homeproxy": [
|
||||
"acllist_read", "connection_check", "current_node_get",
|
||||
"node_latency_test", "resources_get",
|
||||
"singbox_generator", "singbox_get_features"
|
||||
]
|
||||
},
|
||||
"uci": [ "homeproxy" ]
|
||||
},
|
||||
"write": {
|
||||
"file": {
|
||||
"/etc/homeproxy/scripts/update_subscriptions.uc": [ "exec" ],
|
||||
"/tmp/homeproxy_certificate.tmp": [ "write" ]
|
||||
},
|
||||
"ubus": {
|
||||
"luci.homeproxy": [
|
||||
"acllist_write", "certificate_write", "log_clean", "resources_update"
|
||||
]
|
||||
},
|
||||
"uci": [ "homeproxy" ]
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,9 +7,13 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { access, error, lstat, popen, readfile, writefile } from 'fs';
|
||||
import { access, lstat, popen, readfile, writefile } from 'fs';
|
||||
import { cursor } from 'uci';
|
||||
|
||||
import {
|
||||
removeBlankAttrs, renderEndpoint, renderOutbound
|
||||
} from '/etc/homeproxy/scripts/homeproxy.uc';
|
||||
|
||||
/* Kanged from ucode/luci */
|
||||
function shellquote(s) {
|
||||
return `'${replace(s, "'", "'\\''")}'`;
|
||||
@ -21,6 +25,12 @@ function hasKernelModule(kmod) {
|
||||
|
||||
const HP_DIR = '/etc/homeproxy';
|
||||
const RUN_DIR = '/var/run/homeproxy';
|
||||
const RESOURCE_SOURCES = {
|
||||
china_ip4: 'https://cdn.jsdelivr.net/gh/Loyalsoldier/surge-rules@release/cncidr.txt',
|
||||
china_ip6: 'https://cdn.jsdelivr.net/gh/Loyalsoldier/surge-rules@release/cncidr.txt',
|
||||
china_list: 'https://cdn.jsdelivr.net/gh/Loyalsoldier/surge-rules@release/direct.txt',
|
||||
gfw_list: 'https://cdn.jsdelivr.net/gh/Loyalsoldier/surge-rules@release/gfw.txt'
|
||||
};
|
||||
|
||||
const methods = {
|
||||
acllist_read: {
|
||||
@ -60,7 +70,7 @@ const methods = {
|
||||
certificate_write: {
|
||||
args: { filename: 'filename' },
|
||||
call: function(req) {
|
||||
const writeCertificate = (filename, priv) => {
|
||||
const writeCertificate = (filename, kind) => {
|
||||
const tmpcert = '/tmp/homeproxy_certificate.tmp';
|
||||
const filestat = lstat(tmpcert);
|
||||
|
||||
@ -76,8 +86,10 @@ const methods = {
|
||||
}
|
||||
|
||||
/* Kanged from luci-proto-openconnect */
|
||||
const beg = priv ? /^-----BEGIN (RSA|EC) PRIVATE KEY-----$/ : /^-----BEGIN CERTIFICATE-----$/,
|
||||
end = priv ? /^-----END (RSA|EC) PRIVATE KEY-----$/ : /^-----END CERTIFICATE-----$/,
|
||||
const beg = (kind === 'private') ? /^-----BEGIN (RSA|EC) PRIVATE KEY-----$/ :
|
||||
((kind === 'ech') ? /^-----BEGIN ECH CONFIGS-----$/ : /^-----BEGIN CERTIFICATE-----$/),
|
||||
end = (kind === 'private') ? /^-----END (RSA|EC) PRIVATE KEY-----$/ :
|
||||
((kind === 'ech') ? /^-----END ECH CONFIGS-----$/ : /^-----END CERTIFICATE-----$/),
|
||||
lines = split(trim(filecontent), /[\r\n]/);
|
||||
let start = false, i;
|
||||
|
||||
@ -100,7 +112,10 @@ const methods = {
|
||||
filecontent += '\n';
|
||||
|
||||
system(`mkdir -p ${HP_DIR}/certs`);
|
||||
writefile(`${HP_DIR}/certs/${filename}.pem`, filecontent);
|
||||
const certfile = `${HP_DIR}/certs/${filename}.pem`;
|
||||
writefile(certfile, filecontent);
|
||||
if (kind === 'private')
|
||||
system(`chmod 600 ${certfile}`);
|
||||
system(`rm -f ${tmpcert}`);
|
||||
|
||||
return { result: true };
|
||||
@ -110,10 +125,13 @@ const methods = {
|
||||
switch (filename) {
|
||||
case 'client_ca':
|
||||
case 'server_publickey':
|
||||
return writeCertificate(filename, false);
|
||||
return writeCertificate(filename, 'certificate');
|
||||
break;
|
||||
case 'client_ech_conf':
|
||||
return writeCertificate(filename, 'ech');
|
||||
break;
|
||||
case 'server_privatekey':
|
||||
return writeCertificate(filename, true);
|
||||
return writeCertificate(filename, 'private');
|
||||
break;
|
||||
default:
|
||||
return { result: false, error: 'illegal cerificate filename' };
|
||||
@ -155,8 +173,8 @@ const methods = {
|
||||
if (!section || section['.type'] !== 'node')
|
||||
return { id: section_id, label: fallback_name || section_id, type: null };
|
||||
|
||||
const address = trim(section.override_address || section.address || '');
|
||||
const port = trim(section.override_port || section.port || '');
|
||||
const address = trim(section.address || '');
|
||||
const port = trim(section.port || '');
|
||||
const label = trim(section.label || '') || ((address && port) ? `${address}:${port}` : section_id);
|
||||
return {
|
||||
id: section_id,
|
||||
@ -248,195 +266,6 @@ const methods = {
|
||||
rp_runtime: (extras && extras.rp_runtime) ? extras.rp_runtime : null
|
||||
});
|
||||
|
||||
const strToBool = (str) => ((str === '1') || null);
|
||||
const strToInt = (str) => (str != null && trim(sprintf('%s', str)) !== '') ? (int(str) || null) : null;
|
||||
const strToTime = (str) => (str != null && trim(sprintf('%s', str)) !== '') ? (str + 's') : null;
|
||||
|
||||
function removeBlankAttrs(res) {
|
||||
let content;
|
||||
|
||||
if (type(res) === 'object') {
|
||||
content = {};
|
||||
map(keys(res), (k) => {
|
||||
if (type(res[k]) in ['array', 'object'])
|
||||
content[k] = removeBlankAttrs(res[k]);
|
||||
else if (res[k] !== null && res[k] !== '')
|
||||
content[k] = res[k];
|
||||
});
|
||||
} else if (type(res) === 'array') {
|
||||
content = [];
|
||||
map(res, (k) => {
|
||||
if (type(k) in ['array', 'object'])
|
||||
push(content, removeBlankAttrs(k));
|
||||
else if (k !== null && k !== '')
|
||||
push(content, k);
|
||||
});
|
||||
} else
|
||||
return res;
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
const parse_port = (strport) => {
|
||||
if (type(strport) !== 'array' || length(strport) === 0)
|
||||
return null;
|
||||
|
||||
let ports = [];
|
||||
for (let i = 0; i < length(strport); i++)
|
||||
push(ports, int(strport[i]));
|
||||
|
||||
return ports;
|
||||
};
|
||||
|
||||
const generate_endpoint = (node_cfg) => {
|
||||
if (type(node_cfg) !== 'object' || length(node_cfg) === 0)
|
||||
return null;
|
||||
|
||||
return {
|
||||
type: node_cfg.type,
|
||||
tag: 'cfg-' + node_cfg['.name'] + '-out',
|
||||
address: node_cfg.wireguard_local_address,
|
||||
mtu: strToInt(node_cfg.wireguard_mtu),
|
||||
private_key: node_cfg.wireguard_private_key,
|
||||
peers: (node_cfg.type === 'wireguard') ? [
|
||||
{
|
||||
address: node_cfg.address,
|
||||
port: strToInt(node_cfg.port),
|
||||
allowed_ips: [
|
||||
'0.0.0.0/0',
|
||||
'::/0'
|
||||
],
|
||||
persistent_keepalive_interval: strToInt(node_cfg.wireguard_persistent_keepalive_interval),
|
||||
public_key: node_cfg.wireguard_peer_public_key,
|
||||
pre_shared_key: node_cfg.wireguard_pre_shared_key,
|
||||
reserved: parse_port(node_cfg.wireguard_reserved)
|
||||
}
|
||||
] : null,
|
||||
system: (node_cfg.type === 'wireguard') ? false : null,
|
||||
tcp_fast_open: strToBool(node_cfg.tcp_fast_open),
|
||||
tcp_multi_path: strToBool(node_cfg.tcp_multi_path),
|
||||
udp_fragment: strToBool(node_cfg.udp_fragment)
|
||||
};
|
||||
};
|
||||
|
||||
const generate_outbound = (node_cfg) => {
|
||||
if (type(node_cfg) !== 'object' || length(node_cfg) === 0)
|
||||
return null;
|
||||
|
||||
return {
|
||||
type: node_cfg.type,
|
||||
tag: 'cfg-' + node_cfg['.name'] + '-out',
|
||||
|
||||
server: node_cfg.address,
|
||||
server_port: strToInt(node_cfg.port),
|
||||
server_ports: node_cfg.hysteria_hopping_port,
|
||||
|
||||
username: (node_cfg.type !== 'ssh') ? node_cfg.username : null,
|
||||
user: (node_cfg.type === 'ssh') ? node_cfg.username : null,
|
||||
password: node_cfg.password,
|
||||
|
||||
override_address: node_cfg.override_address,
|
||||
override_port: strToInt(node_cfg.override_port),
|
||||
proxy_protocol: strToInt(node_cfg.proxy_protocol),
|
||||
idle_session_check_interval: strToTime(node_cfg.anytls_idle_session_check_interval),
|
||||
idle_session_timeout: strToTime(node_cfg.anytls_idle_session_timeout),
|
||||
min_idle_session: strToInt(node_cfg.anytls_min_idle_session),
|
||||
hop_interval: strToTime(node_cfg.hysteria_hop_interval),
|
||||
up_mbps: strToInt(node_cfg.hysteria_up_mbps),
|
||||
down_mbps: strToInt(node_cfg.hysteria_down_mbps),
|
||||
obfs: node_cfg.hysteria_obfs_type ? {
|
||||
type: node_cfg.hysteria_obfs_type,
|
||||
password: node_cfg.hysteria_obfs_password
|
||||
} : node_cfg.hysteria_obfs_password,
|
||||
auth: (node_cfg.hysteria_auth_type === 'base64') ? node_cfg.hysteria_auth_payload : null,
|
||||
auth_str: (node_cfg.hysteria_auth_type === 'string') ? node_cfg.hysteria_auth_payload : null,
|
||||
recv_window_conn: strToInt(node_cfg.hysteria_recv_window_conn),
|
||||
recv_window: strToInt(node_cfg.hysteria_revc_window),
|
||||
disable_mtu_discovery: strToBool(node_cfg.hysteria_disable_mtu_discovery),
|
||||
method: node_cfg.shadowsocks_encrypt_method,
|
||||
plugin: node_cfg.shadowsocks_plugin,
|
||||
plugin_opts: node_cfg.shadowsocks_plugin_opts,
|
||||
version: (node_cfg.type === 'shadowtls') ? strToInt(node_cfg.shadowtls_version) : ((node_cfg.type === 'socks') ? node_cfg.socks_version : null),
|
||||
client_version: node_cfg.ssh_client_version,
|
||||
host_key: node_cfg.ssh_host_key,
|
||||
host_key_algorithms: node_cfg.ssh_host_key_algo,
|
||||
private_key: node_cfg.ssh_priv_key,
|
||||
private_key_passphrase: node_cfg.ssh_priv_key_pp,
|
||||
uuid: node_cfg.uuid,
|
||||
congestion_control: node_cfg.tuic_congestion_control,
|
||||
udp_relay_mode: node_cfg.tuic_udp_relay_mode,
|
||||
udp_over_stream: strToBool(node_cfg.tuic_udp_over_stream),
|
||||
zero_rtt_handshake: strToBool(node_cfg.tuic_enable_zero_rtt),
|
||||
heartbeat: strToTime(node_cfg.tuic_heartbeat),
|
||||
flow: node_cfg.vless_flow,
|
||||
alter_id: strToInt(node_cfg.vmess_alterid),
|
||||
security: node_cfg.vmess_encrypt,
|
||||
global_padding: strToBool(node_cfg.vmess_global_padding),
|
||||
authenticated_length: strToBool(node_cfg.vmess_authenticated_length),
|
||||
packet_encoding: node_cfg.packet_encoding,
|
||||
|
||||
multiplex: (node_cfg.multiplex === '1') ? {
|
||||
enabled: true,
|
||||
protocol: node_cfg.multiplex_protocol,
|
||||
max_connections: strToInt(node_cfg.multiplex_max_connections),
|
||||
min_streams: strToInt(node_cfg.multiplex_min_streams),
|
||||
max_streams: strToInt(node_cfg.multiplex_max_streams),
|
||||
padding: strToBool(node_cfg.multiplex_padding),
|
||||
brutal: (node_cfg.multiplex_brutal === '1') ? {
|
||||
enabled: true,
|
||||
up_mbps: strToInt(node_cfg.multiplex_brutal_up),
|
||||
down_mbps: strToInt(node_cfg.multiplex_brutal_down)
|
||||
} : null
|
||||
} : null,
|
||||
tls: (node_cfg.tls === '1') ? {
|
||||
enabled: true,
|
||||
server_name: node_cfg.tls_sni,
|
||||
insecure: strToBool(node_cfg.tls_insecure),
|
||||
alpn: node_cfg.tls_alpn,
|
||||
min_version: node_cfg.tls_min_version,
|
||||
max_version: node_cfg.tls_max_version,
|
||||
cipher_suites: node_cfg.tls_cipher_suites,
|
||||
certificate_path: node_cfg.tls_cert_path,
|
||||
ech: (node_cfg.tls_ech === '1') ? {
|
||||
enabled: true,
|
||||
config: node_cfg.tls_ech_config,
|
||||
config_path: node_cfg.tls_ech_config_path
|
||||
} : null,
|
||||
utls: node_cfg.tls_utls ? {
|
||||
enabled: true,
|
||||
fingerprint: node_cfg.tls_utls
|
||||
} : null,
|
||||
reality: (node_cfg.tls_reality === '1') ? {
|
||||
enabled: true,
|
||||
public_key: node_cfg.tls_reality_public_key,
|
||||
short_id: node_cfg.tls_reality_short_id
|
||||
} : null
|
||||
} : null,
|
||||
transport: node_cfg.transport ? {
|
||||
type: node_cfg.transport,
|
||||
host: node_cfg.http_host || node_cfg.httpupgrade_host,
|
||||
path: node_cfg.http_path || node_cfg.ws_path,
|
||||
headers: node_cfg.ws_host ? {
|
||||
Host: node_cfg.ws_host
|
||||
} : null,
|
||||
method: node_cfg.http_method,
|
||||
max_early_data: strToInt(node_cfg.websocket_early_data),
|
||||
early_data_header_name: node_cfg.websocket_early_data_header,
|
||||
service_name: node_cfg.grpc_servicename,
|
||||
idle_timeout: node_cfg.http_idle_timeout,
|
||||
ping_timeout: node_cfg.http_ping_timeout,
|
||||
permit_without_stream: strToBool(node_cfg.grpc_permit_without_stream)
|
||||
} : null,
|
||||
udp_over_tcp: (node_cfg.udp_over_tcp === '1') ? {
|
||||
enabled: true,
|
||||
version: strToInt(node_cfg.udp_over_tcp_version)
|
||||
} : null,
|
||||
tcp_fast_open: strToBool(node_cfg.tcp_fast_open),
|
||||
tcp_multi_path: strToBool(node_cfg.tcp_multi_path),
|
||||
udp_fragment: strToBool(node_cfg.udp_fragment)
|
||||
};
|
||||
};
|
||||
|
||||
const isControllerPortBusy = (port) => {
|
||||
const hex_port = sprintf('%04X', int(port));
|
||||
const tcp_v4 = readfile('/proc/net/tcp') || '';
|
||||
@ -645,7 +474,7 @@ const methods = {
|
||||
}
|
||||
};
|
||||
|
||||
const rendered_outbound = (node_cfg.type === 'wireguard') ? generate_endpoint(node_cfg) : generate_outbound(node_cfg);
|
||||
const rendered_outbound = (node_cfg.type === 'wireguard') ? renderEndpoint(node_cfg) : renderOutbound(node_cfg);
|
||||
if (rendered_outbound && type(rendered_outbound) === 'object')
|
||||
rendered_outbound.tag = selected_tag;
|
||||
|
||||
@ -1104,22 +933,23 @@ const methods = {
|
||||
}
|
||||
},
|
||||
|
||||
resources_get_version: {
|
||||
args: { type: 'type' },
|
||||
call: function(req) {
|
||||
const version = trim(readfile(`${HP_DIR}/resources/${req.args?.type}.ver`));
|
||||
return { version: version, error: error() };
|
||||
resources_get: {
|
||||
call: function() {
|
||||
let resources = [];
|
||||
for (let resource_type in keys(RESOURCE_SOURCES)) {
|
||||
const version = trim(readfile(`${HP_DIR}/resources/${resource_type}.ver`) || '');
|
||||
push(resources, {
|
||||
type: resource_type,
|
||||
version: version || null,
|
||||
source: RESOURCE_SOURCES[resource_type]
|
||||
});
|
||||
}
|
||||
return { resources };
|
||||
}
|
||||
},
|
||||
resources_update: {
|
||||
args: { type: 'type' },
|
||||
call: function(req) {
|
||||
if (req.args?.type) {
|
||||
const type = shellquote(req.args?.type);
|
||||
const exit_code = system(`${HP_DIR}/scripts/update_resources.sh ${type}`);
|
||||
return { status: exit_code };
|
||||
} else
|
||||
return { status: 255, error: 'illegal type' };
|
||||
call: function() {
|
||||
return { status: system(`${HP_DIR}/scripts/update_resources.sh`) };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -1479,6 +1479,7 @@ if is_finded("xray") then
|
||||
o:value("qq", translate("qq"))
|
||||
o:value("random", translate("random"))
|
||||
o:value("randomized", translate("randomized"))
|
||||
o:value("unsafe", translate("unsafe"))
|
||||
o:value("", translate("disable"))
|
||||
o:depends({type = "v2ray", tls = true})
|
||||
o:depends({type = "v2ray", reality = true})
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=rblibtorrent
|
||||
PKG_VERSION:=2.1.0
|
||||
PKG_VERSION:=2.0.13
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=libtorrent-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/arvidn/libtorrent/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=5d5b264eb960fafe4a5c935f6c71bc00e02d3dab8872e76dee9dadb27282f912
|
||||
PKG_HASH:=86207fbe502153568bb4b11d11bb881d6c1388962e852ee0caacf1c07adeb161
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/libtorrent-$(PKG_VERSION)
|
||||
|
||||
PKG_LICENSE:=BSD
|
||||
|
||||
@ -21,13 +21,13 @@ define Download/geoip
|
||||
HASH:=b71d1999439dde2de2d2b6844a2befa50c50211ff739785c005ca7c230a17d6a
|
||||
endef
|
||||
|
||||
GEOSITE_VER:=20260711034308
|
||||
GEOSITE_VER:=20260711103457
|
||||
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
|
||||
define Download/geosite
|
||||
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
|
||||
URL_FILE:=dlc.dat
|
||||
FILE:=$(GEOSITE_FILE)
|
||||
HASH:=356743c9d1ac82ba9c2ed725cc11e8175d431ca5d327bd1ce457edbba70ffd7c
|
||||
HASH:=1d09df82a533dd9bcdeaa8ddabb81be4cfa031dea211102ab09d115f984fbe36
|
||||
endef
|
||||
|
||||
GEOSITE_IRAN_VER:=202607060129
|
||||
|
||||
Loading…
Reference in New Issue
Block a user