🌈 Sync 2026-07-29 14:47:30

This commit is contained in:
github-actions[bot] committed 2026-07-29 14:47:30 +08:00
1 parent d31b90c3d6
commit 9419456fd2
48 files changed
+13901 -3399

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -1 +1 @@
20260704060757
202606132337
@@ -1,6 +1,8 @@
2001:250::/30
2001:254::/31
2001:254::/33
2001:255::/32
2001:256:100::/48
2001:678:53c::/48
2001:c68::/32
2001:cc0::/32
2001:da8::/32
@@ -11,8 +13,11 @@
2001:dc7::/32
2001:dd8:1::/48
2001:dd9::/48
2001:df5:4740::/48
2001:df6:40::/48
2001:4510:400::/40
2001:4510:1480::/41
2001:4511:1480::/41
2400:1160::/32
2400:3200::/32
2400:5280:f803::/48
@@ -20,6 +25,8 @@
2400:6000::/32
2400:6600::/32
2400:6e60:1301::/48
2400:7100::/32
2400:73e0::/32
2400:75aa::/32
2400:7fc0::/40
2400:7fc0:220::/44
@@ -36,9 +43,14 @@
2400:8200::/32
2400:89c0:1010::/44
2400:89c0:1050::/46
2400:89c0:1130::/44
2400:89c0:1150::/48
2400:89c0:2100::/48
2400:89c0:2200::/48
2400:89c0:3010::/44
2400:89c0:6000::/48
2400:8fc0::/32
2400:89c0:6100::/48
2400:8fc0:571::/48
2400:9020:f010::/46
2400:9380:8001::/48
2400:9380:8003::/48
@@ -85,15 +97,19 @@
2400:cb80:e40::/44
2400:da00::/32
2400:dd00::/28
2400:ee00:ffec::/46
2400:ee00:fff0::/44
2400:e680::/32
2400:ee00::/32
2400:f6e0::/32
2400:f720::/32
2400:fe00::/32
2401:20::/40
2401:20:1330::/46
2401:800::/32
2401:1160::/32
2401:1200::/48
2401:1320::/32
2401:1d40::/32
2401:2780::/32
2401:2a00:f000::/43
2401:2e00::/32
2401:3480::/36
@@ -101,11 +117,39 @@
2401:3480:3000::/36
2401:34a0::/31
2401:3800::/32
2401:3880::/32
2401:3980::/32
2401:3a80::/32
2401:3b80::/32
2401:3c80::/32
2401:3d80::/32
2401:3e80::/32
2401:3f80::/32
2401:4180::/32
2401:4280::/32
2401:4380::/32
2401:4480::/32
2401:4580::/32
2401:4680::/32
2401:4780::/32
2401:4880::/32
2401:4a80::/32
2401:5c20:10::/48
2401:71c0::/48
2401:7660::/48
2401:7700::/32
2401:7d40::/32
2401:7e00::/32
2401:8be0::/48
2401:8d00::/47
2401:8d00:3::/48
2401:8d00:4::/47
2401:8d00:7::/48
2401:8d00:8::/47
2401:8d00:b::/48
2401:8d00:c::/48
2401:8d00:f::/48
2401:8d00:12::/48
2401:9a00::/44
2401:9a00:10::/46
2401:a140:1::/48
@@ -121,26 +165,56 @@
2401:c020:6::/48
2401:c020:8::/47
2401:c020:14::/48
2401:ca00::/32
2401:cb80::/32
2401:cc00::/32
2401:ce00::/32
2401:d0e0:3::/48
2401:d180::/46
2401:d180:10::/47
2401:d180:111::/48
2401:d180:2120::/48
2401:d780::/32
2401:d920::/48
2401:de00::/32
2401:ec00::/32
2401:fa00:40::/48
2402:840:d000::/46
2402:840:e000::/46
2402:840:f000::/38
2402:c60::/48
2402:1440::/32
2402:18a0::/32
2402:2000::/32
2402:2e60::/39
2402:34e0::/32
2402:36e0::/44
2402:3c00::/32
2402:3f80:1400::/40
2402:4440:9000::/39
2402:4440:9200::/48
2402:4440:9400::/48
2402:4440:9600::/48
2402:4440:9800::/48
2402:4440:9810::/48
2402:4440:9900::/48
2402:4440:9a00::/48
2402:4440:9b00::/48
2402:4440:9c00::/48
2402:4440:9d00::/48
2402:4440:9e00::/48
2402:4440:a000::/39
2402:4440:a800::/46
2402:4440:a810::/46
2402:4b80::/32
2402:4e00::/32
2402:5e40::/32
2402:5ec0::/32
2402:6e80::/32
2402:6f40::/48
2402:6f40:2::/47
2402:6fc0::/48
2402:7040::/32
2402:7d80::/48
2402:7d80:240::/48
2402:7d80:8888::/48
@@ -162,7 +236,12 @@
2402:dfc0:50::/44
2402:e480::/32
2402:e740::/32
2402:e7c0::/32
2402:e7c0::/37
2402:e7c0:800::/38
2402:e7c0:1000::/36
2402:e7c0:2000::/35
2402:e7c0:4000::/34
2402:e7c0:8000::/33
2402:e880::/48
2402:ef40::/32
2402:f000::/32
@@ -190,6 +269,7 @@
2403:7580::/32
2403:8080:101::/48
2403:8c00::/32
2403:9b00:2000::/48
2403:9b00:2400::/48
2403:9b00:6000::/48
2403:a100::/48
@@ -251,6 +331,10 @@
2404:c940::/48
2404:e280::/47
2404:e8c0::/32
2404:f4c0:f000::/48
2404:f4c0:f003::/48
2404:f4c0:f004::/46
2404:f4c0:f008::/45
2405:80:10::/48
2405:80:13::/48
2405:6c0:2::/48
@@ -270,24 +354,130 @@
2405:6f00:c602::/48
2405:7040:6000::/47
2405:78c0:6e00::/43
2405:8280::/32
2405:8a40::/32
2405:a900:ffee::/48
2405:a900:fffe::/48
2405:ad00::/32
2405:b7c0::/32
2405:d900::/32
2405:e000::/32
2405:e600:2::/48
2405:f940::/32
2406:280::/32
2406:840:9680::/44
2406:840:8100::/40
2406:840:9178::/48
2406:840:9962::/47
2406:840:9966::/48
2406:840:996c::/48
2406:840:e080::/44
2406:840:e0cf::/48
2406:840:e0e0::/46
2406:840:e0e4::/47
2406:840:e10f::/48
2406:840:e14f::/48
2406:840:e330::/44
2406:840:e600::/44
2406:840:e620::/47
2406:840:e80f::/48
2406:840:eb01::/48
2406:840:eb02::/47
2406:840:eb04::/47
2406:840:eb07::/48
2406:840:eb08::/48
2406:840:eb0b::/48
2406:840:ee40::/47
2406:840:ee44::/48
2406:840:ee4b::/48
2406:840:ee4d::/48
2406:840:eee5::/48
2406:840:f380::/44
2406:840:f44f::/48
2406:840:f889::/48
2406:840:fa01::/48
2406:840:fa60::/44
2406:840:fc10::/47
2406:840:fc12::/48
2406:840:fc20::/47
2406:840:fc22::/48
2406:840:fc30::/47
2406:840:fc32::/48
2406:840:fc40::/47
2406:840:fc42::/48
2406:840:fc50::/47
2406:840:fc52::/48
2406:840:fc60::/47
2406:840:fc62::/48
2406:840:fc70::/47
2406:840:fc72::/48
2406:840:fc90::/47
2406:840:fc92::/48
2406:840:fca0::/47
2406:840:fca2::/48
2406:840:fcb0::/47
2406:840:fcb2::/48
2406:840:fcd0::/48
2406:840:fd40::/48
2406:840:fd46::/47
2406:840:fd48::/48
2406:840:fd50::/48
2406:840:fd56::/47
2406:840:fd58::/48
2406:840:fd60::/48
2406:840:fd66::/47
2406:840:fd68::/48
2406:840:fd70::/48
2406:840:fd76::/47
2406:840:fd78::/48
2406:840:fd80::/48
2406:840:fd86::/47
2406:840:fd88::/48
2406:840:fd90::/47
2406:840:fd92::/48
2406:840:fd9f::/48
2406:840:fda0::/48
2406:840:fda7::/48
2406:840:fdb0::/48
2406:840:fdb7::/48
2406:840:fdc0::/48
2406:840:fdc7::/48
2406:840:fe90::/46
2406:840:fe94::/48
2406:840:fe96::/47
2406:840:fe98::/46
2406:840:fe9d::/48
2406:840:fe9e::/48
2406:840:fea2::/47
2406:840:fea4::/46
2406:840:fea8::/48
2406:840:feaa::/47
2406:840:feac::/46
2406:840:fec0::/48
2406:840:fec2::/47
2406:840:fec4::/48
2406:840:feca::/47
2406:840:fecf::/48
2406:840:fed1::/48
2406:840:fed8::/48
2406:840:fedb::/48
2406:840:fedc::/48
2406:840:fef3::/48
2406:840:fef4::/48
2406:840:fef6::/47
2406:840:fef8::/46
2406:840:fefc::/48
2406:840:feff::/48
2406:1080::/48
2406:1e40:f012::/47
2406:2700::/32
2406:3340::/32
2406:3640:1::/48
2406:4d00::/48
2406:52c0::/32
2406:5340:6666::/48
2406:5340:8888::/48
2406:6100::/32
2406:8880::/48
2406:b640:100::/48
2406:b640:4100::/48
2406:cac0::/40
@@ -304,6 +494,7 @@
2407:23c0::/48
2407:2840::/48
2407:37c0::/32
2407:4980::/32
2407:5380::/32
2407:6c40:1100::/48
2407:6c40:1210::/48
@@ -322,7 +513,51 @@
2407:c080:6000::/36
2407:c080:8000::/36
2407:d9c0::/32
2408:4000::/22
2408:4000::/40
2408:4000:100::/47
2408:4000:103::/48
2408:4000:104::/46
2408:4000:108::/45
2408:4000:110::/44
2408:4000:120::/43
2408:4000:140::/42
2408:4000:180::/41
2408:4000:200::/39
2408:4000:400::/38
2408:4000:800::/37
2408:4000:1000::/36
2408:4000:2000::/35
2408:4000:4000::/34
2408:4000:8000::/33
2408:4001::/32
2408:4002::/31
2408:4004::/30
2408:4008::/29
2408:4010::/30
2408:4014::/31
2408:4016:1::/48
2408:4016:2::/47
2408:4016:4::/46
2408:4016:8::/45
2408:4016:10::/44
2408:4016:20::/43
2408:4016:40::/42
2408:4016:80::/41
2408:4016:100::/40
2408:4016:200::/39
2408:4016:400::/38
2408:4016:800::/37
2408:4016:1000::/36
2408:4016:2000::/35
2408:4016:4000::/34
2408:4016:8000::/33
2408:4017::/32
2408:4018::/29
2408:4020::/27
2408:4040::/26
2408:4080::/25
2408:4100::/24
2408:4200::/23
2408:8000::/48
2408:8000:2::/47
2408:8000:1000::/36
@@ -579,103 +814,86 @@
2408:840c:dd00::/40
2408:840c:de00::/40
2408:840d::/42
2408:840d:40::/43
2408:840d:200::/42
2408:840d:400::/42
2408:840d:440::/43
2408:840d:600::/42
2408:840d:d00::/42
2408:840d:d40::/43
2408:840d:f00::/42
2408:840d:1100::/42
2408:840d:1140::/43
2408:840d:1300::/42
2408:840d:1a00::/42
2408:840d:1a40::/43
2408:840d:1c00::/42
2408:840d:1e00::/42
2408:840d:1e40::/43
2408:840d:2000::/42
2408:840d:2700::/42
2408:840d:2740::/43
2408:840d:2900::/42
2408:840d:2b00::/42
2408:840d:2b40::/43
2408:840d:2d00::/42
2408:840d:3400::/42
2408:840d:3440::/43
2408:840d:3600::/42
2408:840d:3800::/42
2408:840d:3840::/43
2408:840d:3a00::/42
2408:840d:4e00::/42
2408:840d:4e40::/43
2408:840d:5000::/42
2408:840d:5200::/42
2408:840d:5240::/43
2408:840d:5400::/42
2408:840d:5b00::/42
2408:840d:5b40::/43
2408:840d:5d00::/42
2408:840d:5f00::/42
2408:840d:5f40::/43
2408:840d:6100::/42
2408:840d:6800::/42
2408:840d:6840::/43
2408:840d:6a00::/42
2408:840d:6c00::/42
2408:840d:6c40::/43
2408:840d:6e00::/42
2408:840d:7500::/42
2408:840d:7540::/43
2408:840d:7700::/42
2408:840d:7900::/42
2408:840d:7940::/43
2408:840d:7b00::/42
2408:840d:8200::/42
2408:840d:8240::/43
2408:840d:8400::/42
2408:840d:8600::/42
2408:840d:8640::/43
2408:840d:8800::/42
2408:840d:8f00::/42
2408:840d:8f40::/43
2408:840d:9100::/42
2408:840d:9300::/42
2408:840d:9340::/43
2408:840d:9500::/42
2408:840d:9c00::/42
2408:840d:9c40::/43
2408:840d:9e00::/42
2408:840d:a000::/42
2408:840d:a040::/43
2408:840d:a200::/42
2408:840d:a900::/42
2408:840d:a940::/43
2408:840d:ab00::/42
2408:840d:ad00::/42
2408:840d:ad40::/43
2408:840d:af00::/42
2408:840d:b600::/42
2408:840d:b640::/43
2408:840d:b800::/42
2408:840d:ba00::/42
2408:840d:ba40::/43
2408:840d:bc00::/42
2408:840d:c300::/42
2408:840d:c340::/43
2408:840d:c500::/42
2408:840d:c700::/42
2408:840d:c740::/43
2408:840d:c900::/42
2408:840d:d000::/42
2408:840d:d040::/43
2408:840d:d200::/42
2408:840d:d400::/42
2408:840d:d440::/43
2408:840d:d600::/42
2408:840d:dd00::/42
2408:840d:dd40::/43
2408:840d:de00::/42
2408:840e:dd00::/40
2408:840e:de00::/40
@@ -856,7 +1074,6 @@
2408:8a06:100::/47
2408:8a07::/40
2408:8a21:4000::/35
2408:8a22::/35
2408:8a23:4000::/34
2408:8a24:4000::/34
2408:8a26:c000::/34
@@ -870,14 +1087,47 @@
2409:27fc::/48
2409:27fe::/33
2409:6100::/44
2409:8000::/20
2409:8000::/21
2409:8800::/22
2409:8c00::/25
2409:8c80::/30
2409:8c84::/32
2409:8c85::/34
2409:8c85:4000::/36
2409:8c85:5000::/38
2409:8c85:5400::/43
2409:8c85:5420::/44
2409:8c85:5430::/47
2409:8c85:5433::/48
2409:8c85:5434::/46
2409:8c85:5438::/45
2409:8c85:5440::/42
2409:8c85:5480::/41
2409:8c85:5500::/40
2409:8c85:5600::/39
2409:8c85:5800::/37
2409:8c85:6000::/35
2409:8c85:8000::/33
2409:8c86::/31
2409:8c88::/29
2409:8c90::/28
2409:8ca0::/27
2409:8cc0::/26
2409:8d00::/24
2409:8e00::/23
240a:2000::/40
240a:2001:100::/40
240a:2001:1000::/36
240a:4002:1b00::/40
240a:4010:8000::/33
240a:4020:83a::/48
240a:4020:883a::/48
240a:4021:83a::/48
240a:4021:883a::/48
240a:4080:8000::/35
240a:4084:2000::/35
240a:4088:a000::/35
240a:408c:2000::/35
240a:4090:50::/48
240a:4090:120::/48
240a:4090:250::/48
@@ -892,11 +1142,20 @@
240a:4090:5200::/40
240a:4090:7000::/39
240a:4090:7200::/40
240a:4090:8000::/34
240a:4093::/35
240a:4094:2000::/35
240a:409c:2000::/35
240a:40a4:2000::/35
240a:40a8:8000::/35
240a:40ac:2000::/35
240a:40b0:83a::/48
240a:40b0:283a::/48
240a:40b0:483a::/48
240a:40b0:683a::/48
240a:40c0:8000::/43
240a:40c0:8200::/48
240a:40c0:8240::/48
240a:40c0:a000::/43
240a:40c0:c000::/43
240a:40c0:e000::/43
@@ -923,10 +1182,31 @@
240a:40c3:8000::/43
240a:40c3:a000::/43
240a:40c3:c000::/43
240a:40c3:c200::/48
240a:40c3:c240::/48
240a:40c3:e000::/43
240a:40c4::/43
240a:40c4:2000::/43
240a:40c4:4000::/43
240a:4172::/31
240a:4180::/31
240a:41b0::/34
240a:41b8:8800::/40
240a:41b8:a000::/40
240a:41b8:d400::/40
240a:41b8:e400::/40
240a:41d6::/31
240a:41f2::/31
240a:4208::/30
240a:4224:9000::/44
240a:4224:a000::/44
240a:4224:c000::/44
240a:4224:d000::/44
240a:4224:e000::/44
240a:4225:1000::/44
240a:4225:3000::/44
240a:4230::/31
240a:4242::/31
240a:4280::/26
240a:42c0::/27
240a:42e0::/28
@@ -943,7 +1223,69 @@
240c:4000::/22
240c:c000::/20
240d:4000::/21
240e::/20
240e::/21
240e:800::/24
240e:900::/26
240e:940::/27
240e:960::/28
240e:970::/29
240e:978::/30
240e:97c::/32
240e:97d::/33
240e:97d:8001::/48
240e:97d:8002::/47
240e:97d:8004::/46
240e:97d:8008::/45
240e:97d:8010::/44
240e:97d:8020::/43
240e:97d:8040::/42
240e:97d:8080::/41
240e:97d:8100::/40
240e:97d:8200::/39
240e:97d:8400::/38
240e:97d:8800::/37
240e:97d:9000::/36
240e:97d:a000::/35
240e:97d:c000::/34
240e:97e::/31
240e:980::/31
240e:982::/32
240e:983::/35
240e:983:2000::/44
240e:983:2011::/48
240e:983:2012::/48
240e:983:2014::/46
240e:983:2018::/45
240e:983:2021::/48
240e:983:2023::/48
240e:983:2024::/46
240e:983:2028::/45
240e:983:2031::/48
240e:983:2032::/47
240e:983:2034::/46
240e:983:2038::/45
240e:983:2040::/42
240e:983:2080::/41
240e:983:2100::/40
240e:983:2200::/39
240e:983:2400::/38
240e:983:2800::/37
240e:983:3000::/36
240e:983:4000::/34
240e:983:8000::/33
240e:984::/30
240e:988::/29
240e:990::/28
240e:9a0::/27
240e:9c0::/26
240e:a00::/23
240e:c00::/22
2602:f92a:a468::/48
2602:f92a:a46d::/48
2602:f92a:a47d::/48
2602:f9ba:a8::/48
2602:f9ba:10c::/48
2602:f9f6:450::/48
2605:9d80:8001::/48
2605:9d80:8011::/48
2605:9d80:8021::/48
@@ -959,6 +1301,8 @@
2605:9d80:9092::/48
2804:1e48:9001::/48
2804:1e48:9002::/48
2a01:e281:a400::/48
2a01:e281:a410::/44
2a04:3e00:1002::/48
2a04:f580:8010::/47
2a04:f580:8090::/48
@@ -983,26 +1327,163 @@
2a04:f580:9270::/48
2a04:f580:9280::/48
2a04:f580:9290::/48
2a0a:d685:1ff::/48
2a0a:d685:300::/40
2a0b:4340:90::/48
2a0b:4340:97::/48
2a0d:c7c7:400::/38
2a0e:aa06:49f::/48
2a05:1087::/32
2a06:3600:e000::/40
2a06:3600:fb00::/40
2a06:3600:fc00::/38
2a06:3605::/32
2a06:3606::/31
2a06:a005:260::/43
2a06:a005:280::/43
2a06:a005:2a0::/44
2a07:54c1:2200::/46
2a07:54c1:2205::/48
2a0a:d681:e000::/40
2a0a:d681:fb00::/40
2a0a:d681:fc00::/38
2a0a:d687:f001::/48
2a0c:b641:571::/48
2a0e:aa06::/40
2a0e:aa06:440::/48
2a0e:aa06:450::/44
2a0e:aa06:490::/44
2a0e:aa06:4e0::/44
2a0e:aa07:e144::/48
2a0e:aa07:e146::/48
2a0e:aa07:e148::/48
2a0e:aa07:e280::/47
2a0e:aa07:e284::/46
2a0e:aa07:e288::/47
2a0e:aa07:e28a::/48
2a0e:aa07:e28d::/48
2a0e:aa07:e28e::/48
2a0e:aa07:f0d0::/46
2a0e:aa07:f0d4::/47
2a0e:aa07:f0d8::/48
2a0e:aa07:f0de::/47
2a0e:b107:740::/44
2a0e:b107:2440::/44
2a0f:1cc5:1600::/44
2a0f:1cc5:4700::/40
2a12:cb41:1200::/44
2a12:cb41:1300::/43
2a12:cb41:13f0::/44
2a0e:b107:da0::/44
2a0e:b107:14a0::/44
2a0f:1cc5:14::/46
2a0f:1cc5:1f::/48
2a0f:1cc5:40::/48
2a0f:1cc5:110::/44
2a0f:1cc5:130::/44
2a0f:1cc5:140::/42
2a0f:1cc5:600::/47
2a0f:1cc5:603::/48
2a0f:1cc5:642::/48
2a0f:1cc5:661::/48
2a0f:1cc5:662::/48
2a0f:1cc5:6a0::/48
2a0f:1cc5:f01::/48
2a0f:1cc5:f02::/47
2a0f:1cc5:f04::/46
2a0f:1cc5:f09::/48
2a0f:1cc5:fff::/48
2a0f:1cc5:1000::/48
2a0f:1cc5:1010::/44
2a0f:1cc5:1032::/47
2a0f:1cc5:1034::/47
2a0f:1cc5:1c00::/47
2a0f:1cc5:1c02::/48
2a0f:1cc5:1c20::/48
2a0f:1cc5:1c30::/48
2a0f:1cc5:1cc0::/48
2a0f:1cc5:1d10::/47
2a0f:1cc5:2000::/40
2a0f:1cc5:2510::/44
2a0f:1cc5:2520::/44
2a0f:1cc5:25ff::/48
2a0f:1cc5:2600::/41
2a0f:1cc5:2680::/42
2a0f:1cc5:2d01::/48
2a0f:1cc5:2d03::/48
2a0f:1cc5:3200::/46
2a0f:1cc5:3222::/48
2a0f:1cc6:b110::/47
2a0f:1cc6:b210::/47
2a0f:1cc6:b212::/48
2a0f:2706::/32
2a0f:85c1:bfe::/48
2a12:cb41::/44
2a12:cb46::/36
2a12:cb47:ffff::/48
2a13:1800::/48
2a13:1800:10::/48
2a13:1800:80::/44
2a13:1800:300::/44
2a13:1801:180::/43
2a13:a5c3:f000::/40
2a14:7580:d00::/43
2a14:7580:d20::/46
2a14:7580:d41::/48
2a14:7580:d42::/47
2a14:7580:dff::/48
2a13:1801:200::/44
2a13:8c87::/32
2a13:a5c3:ff41::/48
2a13:a5c3:ff50::/44
2a14:67c1:70::/48
2a14:67c1:74::/48
2a14:67c1:a110::/44
2a14:67c1:a123::/48
2a14:67c1:a125::/48
2a14:67c1:a128::/48
2a14:67c1:b500::/48
2a14:67c1:b511::/48
2a14:67c1:b514::/48
2a14:67c1:b530::/44
2a14:67c1:b563::/48
2a14:67c1:b578::/48
2a14:67c1:b586::/47
2a14:67c1:b588::/47
2a14:67c1:b590::/48
2a14:67c1:b599::/48
2a14:67c1:b5e0::/44
2a14:67c1:c300::/40
2a14:67c2:519::/48
2a14:67c2:a10::/47
2a14:67c2:a21::/48
2a14:67c2:a22::/47
2a14:67c2:a2f::/48
2a14:67c2:a30::/48
2a14:67c2:a41::/48
2a14:67c2:a50::/48
2a14:67c2:a70::/47
2a14:67c2:a80::/47
2a14:67c2:a90::/48
2a14:67c3:c0::/48
2a14:67c3:190::/47
2a14:67c3:192::/48
2a14:67c3:1100::/47
2a14:67c3:9990::/44
2a14:7580:740::/44
2a14:7580:fffa::/48
2a14:7581:ffb::/48
2a14:7581:30b6::/48
2a14:7583:f224::/47
2a14:7583:f228::/48
2a14:7583:f22c::/48
2a14:7583:f244::/48
2a14:7583:f264::/48
2a14:7583:f268::/48
2a14:7583:f300::/46
2a14:7583:f304::/47
2a14:7583:f306::/48
2a14:7583:f411::/48
2a14:7583:f4f0::/48
2a14:7583:f4f4::/48
2a14:7583:f4fe::/48
2a14:7583:f701::/48
2a14:7583:f703::/48
2a14:7583:f704::/46
2a14:7583:f708::/46
2a14:7583:f70c::/48
2a14:7583:f743::/48
2a14:7583:f744::/48
2a14:7583:f764::/48
2a14:7586:6100::/47
2a14:7586:6103::/48
2a14:7586:6104::/47
2a14:7586:6110::/48
2a14:7586:6300::/44
2a14:7dc0:506::/48
2a14:ae00:50::/44
2c0f:f7a8:8011::/48
2c0f:f7a8:8050::/48
2c0f:f7a8:805f::/48
@@ -1011,4 +1492,5 @@
2c0f:f7a8:8211::/48
2c0f:f7a8:9020::/48
2c0f:f7a8:9041::/48
2c0f:f7a8:9211::/48
2c0f:f7a8:9220::/48
@@ -1 +1 @@
20260704060757
202606132337
File diff suppressed because it is too large. Load diff
@@ -1 +1 @@
202607052256
202606132337
@@ -13,7 +13,6 @@
173ng.com
18board.com
18comic.org
18mh.net
1984bbs.com
1991way.com
1e100.net
@@ -74,7 +73,6 @@
9cache.com
9gag.com
9news.com.au
9oju31.launches.appsflyersdk.com
a-normal-day.com
a248.e.akamai.net
aamacau.com
@@ -89,7 +87,6 @@ about.gitlab.com
about.me
abplive.com
acast.com
accountboy.com
accountkit.com
acevpn.com
acg.mn
@@ -131,7 +128,6 @@ aiosearch.com
aiph.net
airconsole.com
airitilibrary.com
airuniversity.af.edu
airvpn.org
ait.org.tw
aiweiweiblog.com
@@ -211,18 +207,13 @@ aoxvpn.com
apartmentratings.com
apartments.com
apat1989.org
api-gl.lbkrs.com
api-secure.recaptcha.net
api-verify.recaptcha.net
api.ai
api.futunn.com
api.lbkrs.com
api.moomoobull.com
api.palworldgame.com
api.pureapk.com
api.recaptcha.net
api.steampowered.com
api5.futunn.com
apiary.io
apigee.com
apk.support
@@ -400,7 +391,6 @@ bigjapanesesex.com
bignews.org
bigone.com
bild.de
bilinovel.com
biliworld.com
binance.com
binance.org
@@ -665,7 +655,6 @@ cdjp.org
cdn-images.mailchimp.com
cdn-telegram.org
cdn.arstechnica.net
cdn.futustatic.com
cdn.jwplayer.com
cdn.printfriendly.com
cdn.seatguru.com
@@ -689,7 +678,6 @@ centurys.net
certificate-transparency.org
certificate.revocationcheck.com
cfr.org
cg.play-analytics.com
cgdepot.org
change.org
changp.com
@@ -871,7 +859,6 @@ crazypool.org
crazyshit.com
crbug.com
crchina.org
creader.com
creaders.net
creativelab5.com
cristyli.com
@@ -1037,7 +1024,6 @@ dougscripts.com
doujincafe.com
dowjones.io
download.dappcdn.com
download.futunn.com
dpool.top
dpp.org.tw
dpr.info
@@ -1163,7 +1149,6 @@ expatshield.com
expecthim.com
expekt.com
exploader.net
expressnews.com
expressvpn.com
exrates.me
extmatrix.com
@@ -1354,36 +1339,7 @@ funkyimg.com
furbo.org
furinkan.com
furrybar.com
futu.cn
futu.com
futu.link
futu0.com
futu1.com
futu2.com
futu3.com
futu4.com
futu5.com
futu6.com
futu7.com
futu8.com
futu9.com
futuau.com
futubull.cn
futuchain.com
futuesop.com
futufin.com
futuhk.com
futuhk2.com
futuhkapp.com
futuhn.com
futuholdings.com
futuinc.com
futuniuniu.com
futunn.com
futuremessage.org
futustatic.com
fututrade.com
fututrustee.com
fw.cm
fxcm-chinese.com
fxnetworks.com
@@ -1406,7 +1362,6 @@ gaoming.net
gaopi.net
gartlive.com
garudalinux.org
gate.com
gate.io
gatecoin.com
gather.com
@@ -1427,7 +1382,6 @@ generated.photos
genius.com
geph.io
get.app
get.dappcdn.com
get.dev
get.how
get.page
@@ -1785,7 +1739,6 @@ gtv.org
gtv1.org
gu-chu-sum.org
guaguass.com
guangming.com.my
guishan.org
gumroad.com
gunsamerica.com
@@ -1876,7 +1829,6 @@ hkgolden.com
hkgpao.com
hklts.org.hk
hkmap.live
hkong.hk
hkopentv.com
hkpeanut.com
hkreporter.com
@@ -2091,7 +2043,6 @@ itasoftware.com
itch.io
itemfix.com
itiger.com
itigerup.com
itshidden.com
itweet.net
iuhrdf.org
@@ -2181,7 +2132,6 @@ kenengba.com
kepard.com
kex.com
keycdn.com
kfor.com
khatrimaza.org
kichiku-doujinko.com
kik.com
@@ -2231,15 +2181,12 @@ landofhope.tv
lantern.io
laogai.org
laogairesearch.org
laohu8.com
laqingdan.net
larsgeorge.com
lastcombat.com
lastfm.es
lausan.hk
lbank.info
lbctrl.com
lbkrs.com
ldplayer.net
ldplayer.tw
le-vpn.com
@@ -2308,15 +2255,6 @@ localdomain.ws
lockestek.com
login.target.com
logos.com.hk
longbridge.cloud
longbridge.com
longbridge.global
longbridge.hk
longbridge.sg
longbridgeapp.com
longbridgehk.com
longportapp.cn
longportapp.com
longtermly.net
longtoes.com
lookpic.com
@@ -2434,7 +2372,6 @@ mfxmedia.com
mgoon.com
mgstage.com
mh4u.org
mhwindow.org
microvpn.com
mihua.org
mij.rip
@@ -2504,9 +2441,6 @@ monitorchina.org
monocloud.me
monster.com
moodyz.com
moomoo.com
moomooequity.com
moomootrustee.com
moon.fm
moonbbs.com
moonbingo.com
@@ -2747,13 +2681,6 @@ ooni.io
ooni.org
open.firstory.me
openai.com
openapi-quote.longbridge.cn
openapi-quote.longbridge.com
openapi-trade.longbridge.cn
openapi-trade.longbridge.com
openapi.futunn.com
openapi.longbridge.cn
openapi.longbridge.com
openart.ai
opencritic.com
opendemocracy.net
@@ -2819,7 +2746,6 @@ pbs.org
pbworks.com
pbxes.com
pbxes.org
pcgamesn.com
pcgamestorrents.com
pcij.org
pct.org.tw
@@ -2838,7 +2764,6 @@ peoplenews.tw
peopo.org
perfect-privacy.com
periscope.tv
perma.cc
perplexity.ai
pewresearch.org
phayul.com
@@ -3010,15 +2935,12 @@ qmp4.com
qoos.com
qq.co.za
qstatus.com
qtcard.futunn.com
qtcardfthk.futufin.com
qtrac.eu
questvisual.com
quitccp.org
quiz.directory
quora.com
quoracdn.net
quote.longbridge.global
quran.com
qz.com
r-pool.net
@@ -3087,7 +3009,6 @@ rentry.co
renyurenquan.org
resilio.com
resistchina.org
restofworld.org
retweetrank.com
reuters.com
reutersmedia.net
@@ -3161,10 +3082,6 @@ saveuighur.org
sbme.me
sbti.unun.dev
schema.org
schwab.co.uk
schwab.com
schwab.com.cn
schwab.com.hk
scmp.com
scramble.io
scratch.mit.edu
@@ -3203,7 +3120,6 @@ sexinsex.net
sf.net
sfshibao.com
sftuk.org
sgqt0j.launches.appsflyersdk.com
shadeyouvpn.com
shadowsocks.be
shadowsocks.com.hk
@@ -3228,7 +3144,6 @@ shodanhq.com
shooshtime.com
shopee.tw
shopping.yahoo.co.jp
shortconn.im.qcloud.com
showwe.tw
shutterstock.com
shwchurch.org
@@ -3253,7 +3168,6 @@ sinoants.com
sinoca.com
sinocast.com
sinoinsider.com
sinyalee.com
sipml5.org
sis001.com
site.new
@@ -3323,7 +3237,6 @@ southmongolia.org
southnews.com.tw
southpark.cc.com
sowers.org.hk
spaceforce.mil
spaces.hightail.com
spacex.com
spankbang.com
@@ -3414,8 +3327,6 @@ supchina.com
superpages.com
supervpn.net
superzooi.com
support.futunn.com
supremecourt.gov
suprememastertv.com
surfeasy.com
surfeasy.com.au
@@ -3527,7 +3438,6 @@ thehansindia.com
thehindu.com
thehun.net
theinitium.com
theintercept.com
thenewslens.com
thepiratebay.org
theporndude.com
@@ -3606,12 +3516,6 @@ tibettelegraph.com
tibettimes.net
tibettruth.com
tibetwrites.org
tigerbbs.cn
tigerbbs.com
tigerbrokers.com
tigerbrokers.net
tigerbrokers.nz
tigerfintech.com
tigervpn.com
tiktok.com
tiktokcdn-eu.com
@@ -3674,9 +3578,6 @@ totalvpn.com
tou.tv
tpi.org.tw
tracfone.com
trade.futunn.com
trade.longportapp.com
tradeup.com
tradingview.com
translate.goog
transparency.org
@@ -3685,7 +3586,6 @@ trendsmap.com
tronscan.org
trouw.nl
trt.net.tr
trtworld.com
truebuddha-md.org
trustwallet.com
truthsocial.com
@@ -3781,7 +3681,6 @@ twitturk.com
twitturly.com
twkan.com
twreporter.org
twstalker.com
twt.tl
twtkr.com
twttr.com
@@ -3996,7 +3895,6 @@ wanz-factory.com
warroom.org
waselpro.com
washingtonpost.com
washingtontimes.com
watchinese.com
watchmygf.net
watchout.tw
@@ -4005,7 +3903,6 @@ wav.tv
waveprotocol.org
waybig.com
waymo.com
wbrks.com
wd.bible
wealth.com.tw
wearn.com
@@ -4015,7 +3912,6 @@ webmproject.org
webpkgcache.com
webrtc.org
websdr.org
webshare.io
website.new
webwarper.net
wechatlawsuit.com
@@ -4185,7 +4081,6 @@ xgmyd.com
xhamster.com
xianjian.tw
xiaohexie.com
xiaohu8.com
xiaolan.me
xiaoma.org
xiaomi.eu
@@ -4210,7 +4105,6 @@ xn--noss43i.com
xn--oiq.cc
xn--p8j9a0d9c9a.xn--q9jyb4c
xn--u2u927b.com
xn--vuqv2cf7wzyig79c.com
xnpool.com
xnxx.com
xpud.org
@@ -4219,7 +4113,6 @@ xskywalker.com
xt.com
xt.pub
xtube.com
xuan.com.my
xuchao.net
xuchao.org
xuehua.us
@@ -4326,8 +4219,6 @@ zhao.1984.city
zhengjian.org
zhengwunet.org
zhenxiang.biz
zhijianfengyi.cn
zhijianfengyi.com
zhizhu.top
zhongguo.ca
zhongguotese.net
@@ -1 +1 @@
202607052256
202606132337
@@ -7,6 +7,7 @@
'use strict';
import { md5 } from 'digest';
import { readfile, writefile } from 'fs';
import { isnan } from 'math';
import { connect } from 'ubus';
@@ -29,7 +30,10 @@ uci.load(uciconfig);
const uciinfra = 'infra',
ucimain = 'config',
ucicontrol = 'control';
ucicontrol = 'control',
uciclashapi = 'clash_api',
ucintp = 'ntp',
ucicache = 'cache';
const ucidnssetting = 'dns',
ucidnsserver = 'dns_server',
@@ -41,23 +45,63 @@ const uciroutingsetting = 'routing',
const ucinode = 'node';
const uciruleset = 'ruleset';
const RULESET_DIR = HP_DIR + '/ruleset';
const routing_mode = uci.get(uciconfig, ucimain, 'routing_mode') || 'bypass_mainland_china';
function normalize_list(value) {
if (isEmpty(value))
return [];
if (type(value) === 'array')
return value;
return [value];
}
function ruleset_file_name(tag, format) {
let filename = replace(tag || 'ruleset', /[^A-Za-z0-9_.-]+/g, '_');
if (isEmpty(filename))
filename = 'ruleset';
return filename + ((format === 'source') ? '.json' : '.srs');
}
function ruleset_default_path(tag, format) {
return RULESET_DIR + '/' + ruleset_file_name(tag, format);
}
function ruleset_remote_path(path, tag, format) {
if (isEmpty(path))
return ruleset_default_path(tag, format);
if (match(path, /\/$/))
return path + ruleset_file_name(tag, format);
return path;
}
let wan_dns = ubus.call('network.interface', 'status', {'interface': 'wan'})?.['dns-server']?.[0];
if (!wan_dns)
wan_dns = (routing_mode in ['proxy_mainland_china', 'global']) ? '8.8.8.8' : '223.5.5.5';
wan_dns = (routing_mode in ['proxy_mainland_china', 'global']) ? '9.9.9.9' : '223.5.5.5';
const dns_port = uci.get(uciconfig, uciinfra, 'dns_port') || '5333';
const ntp_server = uci.get(uciconfig, uciinfra, 'ntp_server') || 'time.apple.com';
let ntp_enabled = uci.get(uciconfig, ucintp, 'enabled'),
ntp_server = uci.get(uciconfig, ucintp, 'server'),
ntp_server_port = uci.get(uciconfig, ucintp, 'server_port'),
ntp_interval = uci.get(uciconfig, ucintp, 'interval');
if (isEmpty(ntp_enabled) && isEmpty(ntp_server)) {
ntp_server = uci.get(uciconfig, uciinfra, 'ntp_server');
ntp_enabled = !isEmpty(ntp_server) ? '1' : '0';
}
const ipv6_support = uci.get(uciconfig, ucimain, 'ipv6_support') || '0';
let main_node, main_udp_node, dedicated_udp_node, default_outbound, default_outbound_dns,
let cache_file_enabled, cache_file_path, cache_store_rdrc, cache_rdrc_timeout,
main_node, main_udp_node, dedicated_udp_node, default_outbound, default_outbound_dns,
domain_strategy, sniff_override, dns_server, china_dns_server, dns_default_strategy,
dns_default_server, dns_disable_cache, dns_disable_cache_expire, dns_independent_cache,
dns_client_subnet, cache_file_store_rdrc, cache_file_rdrc_timeout, direct_domain_list,
dns_client_subnet, direct_domain_list,
proxy_domain_list;
if (routing_mode !== 'custom') {
@@ -93,8 +137,6 @@ if (routing_mode !== 'custom') {
dns_disable_cache_expire = uci.get(uciconfig, ucidnssetting, 'disable_cache_expire');
dns_independent_cache = uci.get(uciconfig, ucidnssetting, 'independent_cache');
dns_client_subnet = uci.get(uciconfig, ucidnssetting, 'client_subnet');
cache_file_store_rdrc = uci.get(uciconfig, ucidnssetting, 'cache_file_store_rdrc'),
cache_file_rdrc_timeout = uci.get(uciconfig, ucidnssetting, 'cache_file_rdrc_timeout');
/* Routing settings */
default_outbound = uci.get(uciconfig, uciroutingsetting, 'default_outbound') || 'nil';
@@ -110,7 +152,7 @@ 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;
udp_timeout;
if (routing_mode === 'custom')
udp_timeout = uci.get(uciconfig, uciroutingsetting, 'udp_timeout');
@@ -130,13 +172,18 @@ if (match(proxy_mode, /tun/)) {
tun_addr6 = uci.get(uciconfig, uciinfra, 'tun_addr6') || 'fdfe:dcba:9876::1/126';
tun_mtu = uci.get(uciconfig, uciinfra, 'tun_mtu') || '9000';
tcpip_stack = 'system';
if (routing_mode === 'custom') {
if (routing_mode === 'custom')
tcpip_stack = uci.get(uciconfig, uciroutingsetting, 'tcpip_stack') || 'system';
endpoint_independent_nat = uci.get(uciconfig, uciroutingsetting, 'endpoint_independent_nat');
}
}
const log_level = uci.get(uciconfig, ucimain, 'log_level') || 'warn';
cache_file_enabled = uci.get(uciconfig, ucicache, 'enabled');
cache_file_path = uci.get(uciconfig, ucicache, 'path');
cache_store_rdrc = uci.get(uciconfig, ucicache, 'store_rdrc');
cache_rdrc_timeout = uci.get(uciconfig, ucicache, 'rdrc_timeout');
let clash_api = null;
/* UCI config end */
/* Config helper start */
@@ -168,6 +215,31 @@ function parse_dnsserver(server_addr, default_protocol) {
}
}
function parse_custom_dnsserver(cfg) {
let server = cfg.server,
server_port = (cfg.type in ['https', 'h3']) ? null : strToInt(cfg.server_port),
path = cfg.path;
if (!isEmpty(server) && match(server, /:\/\//)) {
const server_url = parseURL(server);
server = server_url.hostname;
if (isEmpty(server_port))
server_port = strToInt(server_url.port);
if ((cfg.type in ['https', 'h3']) && isEmpty(path) && server_url.pathname !== '/')
path = server_url.pathname;
}
if ((cfg.type in ['https', 'h3']) && isEmpty(path))
path = '/dns-query';
return {
type: cfg.type,
server,
server_port,
path
};
}
function parse_dnsquery(strquery) {
if (type(strquery) !== 'array' || isEmpty(strquery))
return null;
@@ -180,13 +252,149 @@ function parse_dnsquery(strquery) {
}
function filter_existing_nodes(nodes) {
if (type(nodes) !== 'array' || isEmpty(nodes))
return [];
return filter(nodes, (k) => {
const node = uci.get_all(uciconfig, k) || {};
return !isEmpty(node);
});
}
function collect_group_nodes(groups, subscription_nodes, nodes, legacy_nodes) {
let result = [];
for (let group in normalize_list(groups))
uci.foreach(uciconfig, ucinode, (cfg) => {
if (cfg.grouphash === group && !~index(result, cfg['.name']))
push(result, cfg['.name']);
});
for (let node in filter_existing_nodes(normalize_list(subscription_nodes)))
if (!~index(result, node))
push(result, node);
for (let node in filter_existing_nodes(normalize_list(nodes)))
if (!~index(result, node))
push(result, node);
for (let node in filter_existing_nodes(normalize_list(legacy_nodes)))
if (!~index(result, node))
push(result, node);
return result;
}
function collect_policy_nodes(nodes, current) {
let result = [];
for (let node in normalize_list(nodes)) {
if (node === current || ~index(result, node))
continue;
const outbound = uci.get_all(uciconfig, node) || {};
if (!isEmpty(outbound) && outbound['.type'] === uciroutingnode && outbound.enabled === '1' && outbound.node in ['urltest', 'selector'])
push(result, node);
}
return result;
}
const reserved_outbound_tags = {
'GLOBAL': true,
'main-out': true,
'main-udp-out': true,
'direct-out': true,
'block-out': true
};
let outbound_tag_map = {},
outbound_tag_used = {};
function normalize_outbound_tag(tag) {
if (isEmpty(tag))
return null;
tag = trim(sprintf('%s', tag));
tag = replace(tag, /[\r\n\t]+/g, ' ');
tag = replace(tag, /[\/\\?#%]+/g, '-');
tag = replace(tag, /\s+/g, ' ');
return tag || null;
}
function section_display_name(section) {
if (type(section) !== 'object' || isEmpty(section))
return null;
let label = normalize_outbound_tag(section.label);
if (!isEmpty(label))
return label;
if (section['.type'] === ucinode) {
const address = trim(section.override_address || section.address || '');
const port = trim(section.override_port || section.port || '');
if (address && port)
return normalize_outbound_tag(`${address}:${port}`);
}
return null;
}
function register_outbound_tag(section_id, display_name) {
if (isEmpty(section_id))
return null;
let tag = outbound_tag_map[section_id];
if (!isEmpty(tag))
return tag;
const fallback_tag = `cfg-${section_id}-out`;
const base_tag = normalize_outbound_tag(display_name) || fallback_tag;
tag = base_tag;
let suffix = 2;
while (reserved_outbound_tags[tag] || outbound_tag_used[tag]) {
tag = `${base_tag} (${suffix})`;
suffix++;
}
outbound_tag_map[section_id] = tag;
outbound_tag_used[tag] = true;
return tag;
}
function get_section_outbound_tag(section_id) {
if (isEmpty(section_id))
return null;
let tag = outbound_tag_map[section_id];
if (!isEmpty(tag))
return tag;
const section = uci.get_all(uciconfig, section_id) || {};
return register_outbound_tag(section_id, section_display_name(section));
}
uci.foreach(uciconfig, ucinode, (cfg) => {
register_outbound_tag(cfg['.name'], section_display_name(cfg));
});
uci.foreach(uciconfig, uciroutingnode, (cfg) => {
if (cfg.enabled === '1')
register_outbound_tag(cfg['.name'], section_display_name(cfg));
});
function generate_endpoint(node) {
if (type(node) !== 'object' || isEmpty(node))
return null;
const endpoint = {
type: node.type,
tag: 'cfg-' + node['.name'] + '-out',
tag: get_section_outbound_tag(node['.name']),
address: node.wireguard_local_address,
mtu: strToInt(node.wireguard_mtu),
private_key: node.wireguard_private_key,
@@ -217,13 +425,14 @@ 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',
tag: get_section_outbound_tag(node['.name']),
routing_mark: strToInt(self_mark),
server: node.address,
server_port: strToInt(node.port),
server: (node.type !== 'direct') ? node.address : null,
server_port: (node.type !== 'direct') ? strToInt(node.port) : null,
/* Hysteria(2) */
server_ports: node.hysteria_hopping_port,
@@ -231,10 +440,6 @@ function generate_outbound(node) {
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),
@@ -306,9 +511,9 @@ function generate_outbound(node) {
config: node.tls_ech_config,
config_path: node.tls_ech_config_path
} : null,
utls: !isEmpty(node.tls_utls) ? {
utls: !isEmpty(tls_utls_value) ? {
enabled: true,
fingerprint: node.tls_utls
fingerprint: tls_utls_value
} : null,
reality: (node.tls_reality === '1') ? {
enabled: true,
@@ -364,10 +569,10 @@ function get_outbound(cfg) {
const node = uci.get(uciconfig, cfg, 'node');
if (isEmpty(node))
die(sprintf("%s's node is missing, please check your configuration.", cfg));
else if (node === 'urltest')
return 'cfg-' + cfg + '-out';
else if (node in ['urltest', 'selector'])
return get_section_outbound_tag(cfg);
else
return 'cfg-' + node + '-out';
return get_section_outbound_tag(node);
}
}
}
@@ -390,10 +595,117 @@ function get_ruleset(cfg) {
return null;
let rules = [];
for (let i in cfg)
push(rules, isEmpty(i) ? null : 'cfg-' + i + '-rule');
for (let i in cfg) {
if (isEmpty(i)) {
push(rules, null);
continue;
}
const ruleset = uci.get_all(uciconfig, i);
push(rules, !isEmpty(ruleset?.tag) ? ruleset.tag : ('cfg-' + i + '-rule'));
}
return rules;
}
function subscription_provider_name(name) {
name = trim(name || '');
return name;
}
function unique_provider_name(name, used) {
const base = name;
let suffix = 2;
while (used[name]) {
name = sprintf('%s (%d)', base, suffix);
suffix++;
}
used[name] = true;
return name;
}
function get_subscription_info(group_hash) {
const value = uci.get(uciconfig, 'subscription', 'subinfo_' + substr(group_hash, 0, 16));
if (isEmpty(value))
return null;
try {
const info = json(value);
if (type(info) !== 'object')
return null;
const has_subscription_info = ('upload' in info) || ('download' in info) ||
('total' in info) || ('expire' in info);
return {
updated_at: info.updated_at,
subscription_info: has_subscription_info ? {
upload: info.upload,
download: info.download,
total: info.total,
expire: info.expire
} : null
};
} catch (e) {
return null;
}
}
function build_proxy_providers() {
let providers = [],
used_names = {},
subscription_names = normalize_list(uci.get(uciconfig, 'subscription', 'subscription_name')),
sub_index = 0;
for (let suburl in normalize_list(uci.get(uciconfig, 'subscription', 'subscription_url'))) {
const subscription_name = subscription_names[sub_index];
sub_index++;
if (isEmpty(suburl) || isEmpty(trim(subscription_name || '')))
continue;
const url = replace(suburl, /#.*$/, ''),
group_hash = md5(url);
let proxies = [];
uci.foreach(uciconfig, ucinode, (cfg) => {
if (cfg.grouphash !== group_hash)
return;
const tag = get_section_outbound_tag(cfg['.name']);
if (!isEmpty(tag) && !~index(proxies, tag))
push(proxies, tag);
});
if (isEmpty(proxies))
continue;
const subscription_info = get_subscription_info(group_hash);
push(providers, {
name: unique_provider_name(subscription_provider_name(subscription_name), used_names),
type: 'Proxy',
vehicle_type: 'HTTP',
proxies: proxies,
update_id: (routing_mode === 'custom') ? group_hash : null,
updated_at: subscription_info?.updated_at,
subscription_info: subscription_info?.subscription_info
});
}
return providers;
}
clash_api = {
external_controller: uci.get(uciconfig, uciclashapi, 'external_controller') || '127.0.0.1:9090',
external_ui: uci.get(uciconfig, uciclashapi, 'external_ui'),
external_ui_download_url: uci.get(uciconfig, uciclashapi, 'external_ui_download_url'),
external_ui_download_detour: get_outbound(uci.get(uciconfig, uciclashapi, 'external_ui_download_detour')),
secret: uci.get(uciconfig, uciclashapi, 'secret'),
default_mode: uci.get(uciconfig, uciclashapi, 'default_mode') || 'rule',
proxy_providers: build_proxy_providers()
};
/* Config helper end */
const config = {};
@@ -409,8 +721,10 @@ config.log = {
/* NTP */
if (!isEmpty(ntp_server))
config.ntp = {
enabled: true,
enabled: ntp_enabled === '1',
server: ntp_server,
server_port: strToInt(ntp_server_port),
interval: ntp_interval,
detour: 'direct-out',
domain_resolver: 'default-dns',
};
@@ -520,10 +834,7 @@ if (!isEmpty(main_node)) {
push(config.dns.servers, {
tag: 'cfg-' + cfg['.name'] + '-dns',
type: cfg.type,
server: cfg.server,
server_port: strToInt(cfg.server_port),
path: cfg.path,
...parse_custom_dnsserver(cfg),
headers: cfg.headers,
tls: cfg.tls_sni ? {
enabled: true,
@@ -565,7 +876,6 @@ if (!isEmpty(main_node)) {
user: cfg.user,
rule_set: get_ruleset(cfg.rule_set),
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,
@@ -606,8 +916,6 @@ push(config.inbounds, {
listen: '::',
listen_port: int(mixed_port),
udp_timeout: strToTime(udp_timeout),
sniff: true,
sniff_override_destination: strToBool(sniff_override),
set_system_proxy: false
});
@@ -617,9 +925,7 @@ if (match(proxy_mode, /redirect/))
tag: 'redirect-in',
listen: '::',
listen_port: int(redirect_port),
sniff: true,
sniff_override_destination: strToBool(sniff_override)
listen_port: int(redirect_port)
});
if (match(proxy_mode, /tproxy/))
push(config.inbounds, {
@@ -629,9 +935,7 @@ if (match(proxy_mode, /tproxy/))
listen: '::',
listen_port: int(tproxy_port),
network: 'udp',
udp_timeout: strToTime(udp_timeout),
sniff: true,
sniff_override_destination: strToBool(sniff_override)
udp_timeout: strToTime(udp_timeout)
});
if (match(proxy_mode, /tun/))
push(config.inbounds, {
@@ -642,11 +946,8 @@ 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,
sniff: true,
sniff_override_destination: strToBool(sniff_override)
stack: tcpip_stack
});
/* Inbound end */
@@ -671,14 +972,16 @@ if (!isEmpty(main_node)) {
let urltest_nodes = [];
if (main_node === 'urltest') {
const main_urltest_nodes = uci.get(uciconfig, ucimain, 'main_urltest_nodes') || [];
const main_urltest_nodes = filter_existing_nodes(
normalize_list(uci.get(uciconfig, ucimain, 'main_urltest_nodes'))
);
const main_urltest_interval = uci.get(uciconfig, ucimain, 'main_urltest_interval');
const main_urltest_tolerance = uci.get(uciconfig, ucimain, 'main_urltest_tolerance');
push(config.outbounds, {
type: 'urltest',
tag: 'main-out',
outbounds: map(main_urltest_nodes, (k) => `cfg-${k}-out`),
outbounds: map(main_urltest_nodes, (k) => get_section_outbound_tag(k)),
interval: strToTime(main_urltest_interval),
tolerance: strToInt(main_urltest_tolerance),
idle_timeout: (strToInt(main_urltest_interval) > 1800) ? `${main_urltest_interval * 2}s` : null,
@@ -687,23 +990,31 @@ if (!isEmpty(main_node)) {
} else {
const main_node_cfg = uci.get_all(uciconfig, main_node) || {};
if (main_node_cfg.type === 'wireguard') {
push(config.endpoints, generate_endpoint(main_node_cfg));
config.endpoints[length(config.endpoints)-1].tag = 'main-out';
const main_endpoint = generate_endpoint(main_node_cfg);
if (main_endpoint) {
main_endpoint.tag = 'main-out';
push(config.endpoints, main_endpoint);
}
} else {
push(config.outbounds, generate_outbound(main_node_cfg));
config.outbounds[length(config.outbounds)-1].tag = 'main-out';
const main_outbound = generate_outbound(main_node_cfg);
if (main_outbound) {
main_outbound.tag = 'main-out';
push(config.outbounds, main_outbound);
}
}
}
if (main_udp_node === 'urltest') {
const main_udp_urltest_nodes = uci.get(uciconfig, ucimain, 'main_udp_urltest_nodes') || [];
const main_udp_urltest_nodes = filter_existing_nodes(
normalize_list(uci.get(uciconfig, ucimain, 'main_udp_urltest_nodes'))
);
const main_udp_urltest_interval = uci.get(uciconfig, ucimain, 'main_udp_urltest_interval');
const main_udp_urltest_tolerance = uci.get(uciconfig, ucimain, 'main_udp_urltest_tolerance');
push(config.outbounds, {
type: 'urltest',
tag: 'main-udp-out',
outbounds: map(main_udp_urltest_nodes, (k) => `cfg-${k}-out`),
outbounds: map(main_udp_urltest_nodes, (k) => get_section_outbound_tag(k)),
interval: strToTime(main_udp_urltest_interval),
tolerance: strToInt(main_udp_urltest_tolerance),
idle_timeout: (strToInt(main_udp_urltest_interval) > 1800) ? `${main_udp_urltest_interval * 2}s` : null,
@@ -712,22 +1023,37 @@ if (!isEmpty(main_node)) {
} else if (dedicated_udp_node) {
const main_udp_node_cfg = uci.get_all(uciconfig, main_udp_node) || {};
if (main_udp_node_cfg.type === 'wireguard') {
push(config.endpoints, generate_endpoint(main_udp_node_cfg));
config.endpoints[length(config.endpoints)-1].tag = 'main-udp-out';
const main_udp_endpoint = generate_endpoint(main_udp_node_cfg);
if (main_udp_endpoint) {
main_udp_endpoint.tag = 'main-udp-out';
push(config.endpoints, main_udp_endpoint);
}
} else {
push(config.outbounds, generate_outbound(main_udp_node_cfg));
config.outbounds[length(config.outbounds)-1].tag = 'main-udp-out';
const main_udp_outbound = generate_outbound(main_udp_node_cfg);
if (main_udp_outbound) {
main_udp_outbound.tag = 'main-udp-out';
push(config.outbounds, main_udp_outbound);
}
}
}
for (let i in urltest_nodes) {
const urltest_node = uci.get_all(uciconfig, i) || {};
if (isEmpty(urltest_node))
continue;
if (urltest_node.type === 'wireguard') {
push(config.endpoints, generate_endpoint(urltest_node));
config.endpoints[length(config.endpoints)-1].tag = 'cfg-' + i + '-out';
const endpoint = generate_endpoint(urltest_node);
if (endpoint) {
endpoint.tag = get_section_outbound_tag(i);
push(config.endpoints, endpoint);
}
} else {
push(config.outbounds, generate_outbound(urltest_node));
config.outbounds[length(config.outbounds)-1].tag = 'cfg-' + i + '-out';
const outbound = generate_outbound(urltest_node);
if (outbound) {
outbound.tag = get_section_outbound_tag(i);
push(config.outbounds, outbound);
}
}
}
} else if (!isEmpty(default_outbound)) {
@@ -739,37 +1065,63 @@ if (!isEmpty(main_node)) {
return;
if (cfg.node === 'urltest') {
const legacy_nodes = (isEmpty(cfg.subscription_groups) && isEmpty(cfg.subscription_nodes) && isEmpty(cfg.selected_nodes)) ? cfg.urltest_nodes : null;
const urltest_list = collect_group_nodes(cfg.subscription_groups, cfg.subscription_nodes, cfg.selected_nodes, legacy_nodes);
push(config.outbounds, {
type: 'urltest',
tag: 'cfg-' + cfg['.name'] + '-out',
outbounds: map(cfg.urltest_nodes, (k) => `cfg-${k}-out`),
tag: get_section_outbound_tag(cfg['.name']),
outbounds: map(urltest_list, (k) => get_section_outbound_tag(k)),
url: cfg.urltest_url,
interval: strToTime(cfg.urltest_interval),
tolerance: strToInt(cfg.urltest_tolerance),
idle_timeout: strToTime(cfg.urltest_idle_timeout),
interrupt_exist_connections: strToBool(cfg.urltest_interrupt_exist_connections)
});
urltest_nodes = [...urltest_nodes, ...filter(cfg.urltest_nodes, (l) => !~index(urltest_nodes, l))];
urltest_nodes = [...urltest_nodes, ...filter(urltest_list, (l) => !~index(urltest_nodes, l))];
} else if (cfg.node === 'selector') {
const selector_node_list = collect_group_nodes(cfg.subscription_groups, cfg.subscription_nodes, cfg.selected_nodes, null);
const selector_policy_list = collect_policy_nodes(cfg.policy_nodes, cfg['.name']);
const selector_list = [...selector_node_list, ...filter(selector_policy_list, (l) => !~index(selector_node_list, l))];
const selector_default = (!isEmpty(cfg.selector_default) && ~index(selector_list, cfg.selector_default)) ? cfg.selector_default : null;
push(config.outbounds, {
type: 'selector',
tag: get_section_outbound_tag(cfg['.name']),
outbounds: map(selector_list, (k) => get_section_outbound_tag(k)),
default: selector_default ? get_section_outbound_tag(selector_default) : null,
interrupt_exist_connections: strToBool(cfg.selector_interrupt_exist_connections)
});
urltest_nodes = [...urltest_nodes, ...filter(selector_node_list, (l) => !~index(urltest_nodes, l))];
} else {
const outbound = uci.get_all(uciconfig, cfg.node) || {};
if (isEmpty(outbound))
return;
if (outbound.type === 'wireguard') {
push(config.endpoints, generate_endpoint(outbound));
config.endpoints[length(config.endpoints)-1].bind_interface = cfg.bind_interface;
config.endpoints[length(config.endpoints)-1].detour = get_outbound(cfg.outbound);
const endpoint = generate_endpoint(outbound);
if (!endpoint)
return;
endpoint.bind_interface = cfg.bind_interface;
endpoint.detour = get_outbound(cfg.outbound);
if (cfg.domain_resolver)
config.endpoints[length(config.endpoints)-1].domain_resolver = {
endpoint.domain_resolver = {
server: get_resolver(cfg.domain_resolver),
strategy: cfg.domain_strategy
};
push(config.endpoints, endpoint);
} else {
push(config.outbounds, generate_outbound(outbound));
config.outbounds[length(config.outbounds)-1].bind_interface = cfg.bind_interface;
config.outbounds[length(config.outbounds)-1].detour = get_outbound(cfg.outbound);
const routed_outbound = generate_outbound(outbound);
if (!routed_outbound)
return;
routed_outbound.bind_interface = cfg.bind_interface;
routed_outbound.detour = get_outbound(cfg.outbound);
if (cfg.domain_resolver)
config.outbounds[length(config.outbounds)-1].domain_resolver = {
routed_outbound.domain_resolver = {
server: get_resolver(cfg.domain_resolver),
strategy: cfg.domain_strategy
};
push(config.outbounds, routed_outbound);
}
push(routing_nodes, cfg.node);
}
@@ -777,10 +1129,18 @@ if (!isEmpty(main_node)) {
for (let i in filter(urltest_nodes, (l) => !~index(routing_nodes, l))) {
const urltest_node = uci.get_all(uciconfig, i) || {};
if (urltest_node.type === 'wireguard')
push(config.endpoints, generate_endpoint(urltest_node));
else
push(config.outbounds, generate_outbound(urltest_node));
if (isEmpty(urltest_node))
continue;
if (urltest_node.type === 'wireguard') {
const endpoint = generate_endpoint(urltest_node);
if (endpoint)
push(config.endpoints, endpoint);
} else {
const outbound = generate_outbound(urltest_node);
if (outbound)
push(config.outbounds, outbound);
}
}
}
@@ -795,13 +1155,10 @@ config.route = {
{
inbound: 'dns-in',
action: 'hijack-dns'
},
{
action: 'sniff'
}
/*
* leave for sing-box 1.13.0
* {
* action: 'sniff'
* }
*/
],
rule_set: [],
auto_detect_interface: isEmpty(default_interface) ? true : null,
@@ -866,6 +1223,7 @@ if (!isEmpty(main_node)) {
tag: 'geoip-cn',
format: 'binary',
url: 'https://fastly.jsdelivr.net/gh/1715173329/IPCIDR-CHINA@rule-set/cn.srs',
path: ruleset_default_path('geoip-cn', 'binary'),
download_detour: 'main-out'
});
push(config.route.rule_set, {
@@ -873,6 +1231,7 @@ if (!isEmpty(main_node)) {
tag: 'geosite-cn',
format: 'binary',
url: 'https://fastly.jsdelivr.net/gh/1715173329/sing-geosite@rule-set-unstable/geosite-geolocation-cn.srs',
path: ruleset_default_path('geosite-cn', 'binary'),
download_detour: 'main-out'
});
push(config.route.rule_set, {
@@ -880,6 +1239,7 @@ if (!isEmpty(main_node)) {
tag: 'geosite-noncn',
format: 'binary',
url: 'https://fastly.jsdelivr.net/gh/1715173329/sing-geosite@rule-set-unstable/geosite-geolocation-!cn.srs',
path: ruleset_default_path('geosite-noncn', 'binary'),
download_detour: 'main-out'
});
}
@@ -924,17 +1284,25 @@ if (!isEmpty(main_node)) {
user: cfg.user,
rule_set: get_ruleset(cfg.rule_set),
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),
action: cfg.action,
outbound: get_outbound(cfg.outbound),
override_address: cfg.override_address,
override_port: strToInt(cfg.override_port),
udp_disable_domain_unmapping: strToBool(cfg.udp_disable_domain_unmapping),
udp_connect: strToBool(cfg.udp_connect),
udp_timeout: strToTime(cfg.udp_timeout),
tls_fragment: strToBool(cfg.tls_fragment),
tls_fragment_fallback_delay: strToTime(cfg.tls_fragment_fallback_delay),
tls_record_fragment: strToBool(cfg.tls_record_fragment)
outbound: (cfg.action === 'route') ? get_outbound(cfg.outbound) : null,
server: (cfg.action === 'resolve') ? get_resolver(cfg.resolve_server) : null,
strategy: (cfg.action === 'resolve') ? cfg.resolve_strategy : null,
disable_cache: (cfg.action === 'resolve') ? strToBool(cfg.resolve_disable_cache) : null,
rewrite_ttl: (cfg.action === 'resolve') ? strToInt(cfg.resolve_rewrite_ttl) : null,
client_subnet: (cfg.action === 'resolve') ? cfg.resolve_client_subnet : null,
override_address: (cfg.action in ['route', 'route-options']) ? cfg.override_address : null,
override_port: (cfg.action in ['route', 'route-options']) ? strToInt(cfg.override_port) : null,
udp_disable_domain_unmapping: (cfg.action in ['route', 'route-options']) ? strToBool(cfg.udp_disable_domain_unmapping) : null,
udp_connect: (cfg.action in ['route', 'route-options']) ? strToBool(cfg.udp_connect) : null,
udp_timeout: (cfg.action in ['route', 'route-options']) ? strToTime(cfg.udp_timeout) : null,
tls_fragment: (cfg.action in ['route', 'route-options']) ? strToBool(cfg.tls_fragment) : null,
tls_fragment_fallback_delay: (cfg.action in ['route', 'route-options']) ? strToTime(cfg.tls_fragment_fallback_delay) : null,
tls_record_fragment: (cfg.action in ['route', 'route-options']) ? strToBool(cfg.tls_record_fragment) : null,
method: (cfg.action === 'reject') ? cfg.reject_method : null,
no_drop: (cfg.action === 'reject') ? strToBool(cfg.reject_no_drop) : null
});
});
@@ -945,14 +1313,16 @@ if (!isEmpty(main_node)) {
if (cfg.enabled !== '1')
return null;
const tag = !isEmpty(cfg.tag) ? cfg.tag : ('cfg-' + cfg['.name'] + '-rule');
push(config.route.rule_set, {
type: cfg.type,
tag: 'cfg-' + cfg['.name'] + '-rule',
tag: tag,
format: cfg.format,
path: cfg.path,
url: cfg.url,
path: (cfg.type === 'remote') ? ruleset_remote_path(cfg.remote_path || cfg.path, tag, cfg.format) : cfg.path,
download_detour: get_outbound(cfg.outbound),
update_interval: cfg.update_interval
update_interval: (cfg.type === 'remote') ? '87600h' : null
});
});
}
@@ -961,15 +1331,17 @@ if (!isEmpty(main_node)) {
/* Experimental start */
if (routing_mode in ['bypass_mainland_china', 'custom']) {
config.experimental = {
clash_api,
cache_file: {
enabled: true,
path: RUN_DIR + '/cache.db',
store_rdrc: strToBool(cache_file_store_rdrc),
rdrc_timeout: strToTime(cache_file_rdrc_timeout),
enabled: cache_file_enabled !== '0',
path: cache_file_path || (HP_DIR + '/cache.db'),
store_rdrc: strToBool(cache_store_rdrc),
rdrc_timeout: strToTime(cache_rdrc_timeout),
}
};
}
/* Experimental end */
system('mkdir -p ' + RUN_DIR);
system('mkdir -p ' + RULESET_DIR);
writefile(RUN_DIR + '/sing-box-c.json', sprintf('%.J\n', removeBlankAttrs(config)));
@@ -67,7 +67,7 @@ uci.foreach(uciconfig, uciserver, (cfg) => {
password: cfg.hysteria_obfs_password
} : cfg.hysteria_obfs_password,
recv_window_conn: strToInt(cfg.hysteria_recv_window_conn),
recv_window_client: strToInt(cfg.hysteria_revc_window_client),
recv_window_client: strToInt(cfg.hysteria_recv_window_client),
max_conn_client: strToInt(cfg.hysteria_max_conn_client),
disable_mtu_discovery: strToBool(cfg.hysteria_disable_mtu_discovery),
ignore_client_bandwidth: strToBool(cfg.hysteria_ignore_client_bandwidth),
+22 -3
View File
@@ -66,15 +66,34 @@ export function getTime(epoch) {
};
export function wGET(url, ua) {
export function wGETResponse(url, ua) {
if (!url || type(url) !== 'string')
return null;
if (!ua)
ua = 'Wget/1.21 (HomeProxy, like v2rayN)';
const output = executeCommand(`/usr/bin/wget -qO- --user-agent ${shellQuote(ua)} --timeout=10 ${shellQuote(url)}`) || {};
return trim(output.stdout);
const output = executeCommand(`/usr/bin/wget -O- -S --user-agent ${shellQuote(ua)} --timeout=10 ${shellQuote(url)}`) || {};
return {
body: trim(output.stdout),
headers: output.stderr || '',
exitcode: output.exitcode
};
};
export function wGET(url, ua) {
return wGETResponse(url, ua)?.body;
};
export function wGETHeaders(url, ua) {
if (!url || type(url) !== 'string')
return null;
if (!ua)
ua = 'clash.meta';
const output = executeCommand(`/usr/bin/wget -O /dev/null -S --user-agent ${shellQuote(ua)} --timeout=10 ${shellQuote(url)}`) || {};
return output.stderr || '';
};
/* Utilities end */
@@ -7,14 +7,34 @@
'use strict';
import { popen } from 'fs';
import { cursor } from 'uci';
import { isEmpty, parseURL, validation } from 'homeproxy';
import { isEmpty, parseURL } from 'homeproxy';
const uci = cursor();
const uciconfig = 'homeproxy';
uci.load(uciconfig);
function random_secret() {
const fd = popen('tr -dc "A-Za-z0-9" < /dev/urandom 2>/dev/null | head -c 16');
let random = '';
if (fd) {
random = trim(fd.read('all') || '');
fd.close();
}
if (isEmpty(random)) {
const fallback = popen('awk \'BEGIN{srand(); printf "%06d", int(rand() * 1000000)}\'');
if (fallback) {
random = trim(fallback.read('all') || '');
fallback.close();
}
}
return random;
}
const uciinfra = 'infra',
ucimigration = 'migration',
ucimain = 'config',
@@ -25,8 +45,16 @@ const uciinfra = 'infra',
ucirouting = 'routing',
uciroutingnode = 'routing_node',
uciroutingrule = 'routing_rule',
uciclashapi = 'clash_api',
ucintp = 'ntp',
ucicache = 'cache',
uciserver = 'server';
function is_update_cron(value) {
let matched = match(value || '', /^(\d{1,2})\s+(\d{1,2})\s+\*\s+\*\s+([0-7*])$/);
return !isEmpty(matched) && int(matched[1]) <= 59 && int(matched[2]) <= 23;
}
/* chinadns-ng has been removed */
if (uci.get(uciconfig, uciinfra, 'china_dns_port'))
uci.delete(uciconfig, uciinfra, 'china_dns_port');
@@ -36,9 +64,7 @@ const china_dns_server = uci.get(uciconfig, ucimain, 'china_dns_server');
if (type(china_dns_server) === 'array') {
uci.set(uciconfig, ucimain, 'china_dns_server', china_dns_server[0]);
} else {
if (china_dns_server === 'wan_114')
uci.set(uciconfig, ucimain, 'china_dns_server', '114.114.114.114');
else if (match(china_dns_server, /,/))
if (match(china_dns_server, /,/))
uci.set(uciconfig, ucimain, 'china_dns_server', split(china_dns_server, ',')[0]);
}
@@ -57,6 +83,93 @@ if (!uci.get(uciconfig, uciinfra, 'ntp_server'))
if (!isEmpty(uci.get(uciconfig, uciinfra, 'tun_gso')))
uci.delete(uciconfig, uciinfra, 'tun_gso');
/* endpoint_independent_nat was removed in sing-box 1.13 */
if (!isEmpty(uci.get(uciconfig, ucirouting, 'endpoint_independent_nat')))
uci.delete(uciconfig, ucirouting, 'endpoint_independent_nat');
/* direct outbound proxy_protocol was removed in sing-box 1.13 */
uci.foreach(uciconfig, ucinode, (cfg) => {
if (cfg.type === 'direct' && !isEmpty(cfg.proxy_protocol))
uci.delete(uciconfig, cfg['.name'], 'proxy_protocol');
});
const legacy_panel_url = 'https://github.com/Zephyruso/zashboard/releases/latest/download/dist.zip';
const legacy_panel_proxy_url = 'https://gh-proxy.com/https://github.com/Zephyruso/zashboard/releases/latest/download/dist.zip';
const old_panel_url = 'https://github.com/Zephyruso/zashboard/releases/latest/download/dist-cdn-fonts.zip';
const old_panel_proxy_url = 'https://gh-proxy.com/https://github.com/Zephyruso/zashboard/releases/latest/download/dist-cdn-fonts.zip';
const default_panel_url = 'https://gh-proxy.com/https://github.com/Zephyruso/zashboard/releases/latest/download/dist-cdn-fonts.zip';
/* clash api panel options were introduced */
if (!uci.get(uciconfig, uciclashapi))
uci.set(uciconfig, uciclashapi, uciconfig);
if (isEmpty(uci.get(uciconfig, uciclashapi, 'external_ui')))
uci.set(uciconfig, uciclashapi, 'external_ui', '/etc/homeproxy/run/ui');
const panel_url = uci.get(uciconfig, uciclashapi, 'external_ui_download_url');
if (isEmpty(panel_url) || panel_url === legacy_panel_url || panel_url === legacy_panel_proxy_url || panel_url === old_panel_proxy_url)
uci.set(uciconfig, uciclashapi, 'external_ui_download_url', default_panel_url);
if (isEmpty(uci.get(uciconfig, uciclashapi, 'external_ui_download_detour')))
uci.set(uciconfig, uciclashapi, 'external_ui_download_detour', 'direct-out');
if (isEmpty(uci.get(uciconfig, uciclashapi, 'external_controller')))
uci.set(uciconfig, uciclashapi, 'external_controller', '0.0.0.0:9095');
if (isEmpty(uci.get(uciconfig, uciclashapi, 'secret')) || uci.get(uciconfig, uciclashapi, 'secret') === 'homeproxy') {
let random = random_secret();
if (!isEmpty(random))
uci.set(uciconfig, uciclashapi, 'secret', random);
}
if (isEmpty(uci.get(uciconfig, uciclashapi, 'default_mode')))
uci.set(uciconfig, uciclashapi, 'default_mode', 'rule');
/* ntp options were moved into a dedicated section */
if (!uci.get(uciconfig, ucintp))
uci.set(uciconfig, ucintp, uciconfig);
const legacy_ntp_server = uci.get(uciconfig, uciinfra, 'ntp_server');
if (isEmpty(uci.get(uciconfig, ucintp, 'enabled')))
uci.set(uciconfig, ucintp, 'enabled', '1');
if (isEmpty(uci.get(uciconfig, ucintp, 'server')))
uci.set(uciconfig, ucintp, 'server', !isEmpty(legacy_ntp_server) ? legacy_ntp_server : 'ntp.aliyun.com');
if (isEmpty(uci.get(uciconfig, ucintp, 'server_port')))
uci.set(uciconfig, ucintp, 'server_port', '123');
if (isEmpty(uci.get(uciconfig, ucintp, 'interval')))
uci.set(uciconfig, ucintp, 'interval', '30m');
/* cache file options were moved into a dedicated section */
if (!uci.get(uciconfig, ucicache))
uci.set(uciconfig, ucicache, uciconfig);
if (isEmpty(uci.get(uciconfig, ucicache, 'enabled')))
uci.set(uciconfig, ucicache, 'enabled', '1');
const old_cache_path = '/var/run/homeproxy/cache.db';
const default_cache_path = '/etc/homeproxy/cache.db';
const cache_path = uci.get(uciconfig, ucicache, 'path');
if (isEmpty(cache_path) || cache_path === old_cache_path)
uci.set(uciconfig, ucicache, 'path', default_cache_path);
if (!isEmpty(uci.get(uciconfig, ucicache, 'store_fakeip')))
uci.delete(uciconfig, ucicache, 'store_fakeip');
if (isEmpty(uci.get(uciconfig, ucicache, 'store_rdrc')))
uci.set(uciconfig, ucicache, 'store_rdrc', isEmpty(uci.get(uciconfig, ucidns, 'cache_file_store_rdrc')) ? '1' : uci.get(uciconfig, ucidns, 'cache_file_store_rdrc'));
if (isEmpty(uci.get(uciconfig, ucicache, 'rdrc_timeout')) && !isEmpty(uci.get(uciconfig, ucidns, 'cache_file_rdrc_timeout')))
uci.set(uciconfig, ucicache, 'rdrc_timeout', uci.get(uciconfig, ucidns, 'cache_file_rdrc_timeout'));
if (!isEmpty(uci.get(uciconfig, ucidns, 'cache_file_store_rdrc')))
uci.delete(uciconfig, ucidns, 'cache_file_store_rdrc');
if (!isEmpty(uci.get(uciconfig, ucidns, 'cache_file_rdrc_timeout')))
uci.delete(uciconfig, ucidns, 'cache_file_rdrc_timeout');
/* create migration section */
if (!uci.get(uciconfig, ucimigration))
uci.set(uciconfig, ucimigration, uciconfig);
@@ -75,6 +188,10 @@ if (isEmpty(uci.get(uciconfig, ucimain, 'log_level')))
if (isEmpty(uci.get(uciconfig, uciserver, 'log_level')))
uci.set(uciconfig, uciserver, 'log_level', 'warn');
if (!isEmpty(uci.get(uciconfig, 'subscription', 'auto_update_time')) &&
!is_update_cron(uci.get(uciconfig, 'subscription', 'auto_update_time')))
uci.set(uciconfig, 'subscription', 'auto_update_time', '0 0 * * *');
/* empty value defaults to all ports now */
if (uci.get(uciconfig, ucimain, 'routing_port') === 'all')
uci.delete(uciconfig, ucimain, 'routing_port');
@@ -239,6 +356,47 @@ uci.foreach(uciconfig, uciroutingrule, (cfg) => {
}
});
/* routing node options */
uci.foreach(uciconfig, uciroutingnode, (cfg) => {
if (cfg.node === 'urltest' && !isEmpty(cfg.urltest_nodes)) {
let custom_nodes = [],
subscription_nodes = [];
for (let node in (type(cfg.urltest_nodes) === 'array' ? cfg.urltest_nodes : [ cfg.urltest_nodes ])) {
let node_cfg = uci.get_all(uciconfig, node);
if (isEmpty(node_cfg))
continue;
if (!isEmpty(node_cfg.grouphash))
push(subscription_nodes, node);
else
push(custom_nodes, node);
}
if (isEmpty(cfg.selected_nodes) && !isEmpty(custom_nodes))
uci.set(uciconfig, cfg['.name'], 'selected_nodes', custom_nodes);
if (isEmpty(cfg.subscription_nodes) && !isEmpty(subscription_nodes))
uci.set(uciconfig, cfg['.name'], 'subscription_nodes', subscription_nodes);
uci.delete(uciconfig, cfg['.name'], 'urltest_nodes');
}
});
/* rule set options */
uci.foreach(uciconfig, uciruleset, (cfg) => {
if (isEmpty(cfg.tag))
uci.set(uciconfig, cfg['.name'], 'tag', 'cfg-' + cfg['.name'] + '-rule');
if (cfg.type === 'remote') {
if (isEmpty(cfg.auto_update))
uci.set(uciconfig, cfg['.name'], 'auto_update', '1');
if (!is_update_cron(cfg.update_interval))
uci.set(uciconfig, cfg['.name'], 'update_interval', '0 0 * * *');
}
});
/* server options */
/* auto_firewall was moved into server options */
const auto_firewall = uci.get(uciconfig, uciserver, 'auto_firewall');
@@ -246,6 +404,13 @@ if (!isEmpty(auto_firewall))
uci.delete(uciconfig, uciserver, 'auto_firewall');
uci.foreach(uciconfig, uciserver, (cfg) => {
if (!isEmpty(cfg.hysteria_revc_window_client) && isEmpty(cfg.hysteria_recv_window_client)) {
uci.set(uciconfig, cfg['.name'], 'hysteria_recv_window_client', cfg.hysteria_revc_window_client);
}
if (!isEmpty(cfg.hysteria_revc_window_client))
uci.delete(uciconfig, cfg['.name'], 'hysteria_revc_window_client');
/* auto_firewall was moved into server options */
if (auto_firewall === '1')
uci.set(uciconfig, cfg['.name'], 'firewall' , '1');
@@ -0,0 +1,46 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
set -eu
NAME="homeproxy"
RUN_DIR="/var/run/$NAME"
REQUEST_FILE="$RUN_DIR/provider-update-id"
LOCK_FILE="$RUN_DIR/provider-update-v2.lock"
LOG_FILE="$RUN_DIR/homeproxy.log"
PROVIDER_ID="${1:-}"
log() {
printf '%s [SUBSCRIBE] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >> "$LOG_FILE"
}
case "$PROVIDER_ID" in
*[!0-9a-f]*|'') exit 1 ;;
esac
[ "${#PROVIDER_ID}" -eq 32 ] || exit 1
mkdir -p "$RUN_DIR"
if ! lock -n "$LOCK_FILE" 2>/dev/null; then
log "Another provider update is running; waiting for it to finish..."
lock "$LOCK_FILE"
fi
umask 077
printf '%s\n' "$PROVIDER_ID" > "$REQUEST_FILE"
cleanup() {
rm -f "$REQUEST_FILE"
lock -u "$LOCK_FILE" 2>/dev/null || true
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
# Give the Clash API response time to reach the panel before HomeProxy restarts.
log "Updating provider $PROVIDER_ID from Clash API..."
sleep 1
if /etc/homeproxy/scripts/update_subscriptions.uc; then
log "Provider $PROVIDER_ID update completed."
else
log "Provider $PROVIDER_ID update failed."
exit 4
fi
@@ -21,64 +21,65 @@ to_upper() {
}
check_list_update() {
local listtype="$1"
local listrepo="$2"
local listref="$3"
local listname="$4"
local lock="$RUN_DIR/update_resources-$listtype.lock"
local github_token="$(uci -q get homeproxy.config.github_token)"
local wget="wget --timeout=10 -q"
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)"
exec 200>"$lock"
if ! flock -n 200 &> "/dev/null"; then
log "[$(to_upper "$listtype")] A task is already running."
if ! lock -n "$LOCK_FILE" 2>"/dev/null"; then
log "[$(to_upper "$LIST_FILE")] A task is already running."
return 2
fi
[ -z "$github_token" ] || github_token="--header=Authorization: Bearer $github_token"
local list_info="$($wget "${github_token:--q}" -O- "https://api.github.com/repos/$listrepo/commits?sha=$listref&path=$listname&per_page=1")"
local list_sha="$(echo -e "$list_info" | jsonfilter -qe "@[0].sha")"
local list_ver="$(echo -e "$list_info" | jsonfilter -qe "@[0].commit.message" | grep -Eo "[0-9-]+" | tr -d '-')"
if [ -z "$list_sha" ] || [ -z "$list_ver" ]; then
log "[$(to_upper "$listtype")] Failed to get the latest version, please retry later."
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 local_list_ver="$(cat "$RESOURCES_DIR/$listtype.ver" 2>"/dev/null" || echo "NOT FOUND")"
if [ "$local_list_ver" = "$list_ver" ]; then
log "[$(to_upper "$listtype")] Current version: $list_ver."
log "[$(to_upper "$listtype")] You're already at the latest version."
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 "$listtype")] Local version: $local_list_ver, latest version: $list_ver."
log "[$(to_upper "$LIST_FILE")] Local version: $OLD_VER, latest version: $NEW_VER."
fi
if ! $wget "https://fastly.jsdelivr.net/gh/$listrepo@$list_sha/$listname" -O "$RUN_DIR/$listname" || [ ! -s "$RUN_DIR/$listname" ]; then
rm -f "$RUN_DIR/$listname"
log "[$(to_upper "$listtype")] Update failed."
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/$listname" "$RESOURCES_DIR/$listtype.${listname##*.}"
echo -e "$list_ver" > "$RESOURCES_DIR/$listtype.ver"
log "[$(to_upper "$listtype")] Successfully updated."
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
}
case "$1" in
"china_ip4")
check_list_update "$1" "1715173329/IPCIDR-CHINA" "master" "ipv4.txt"
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" "1715173329/IPCIDR-CHINA" "master" "ipv6.txt"
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/v2ray-rules-dat" "release" "gfw.txt"
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/v2ray-rules-dat" "release" "direct-list.txt" && \
sed -i -e "s/full://g" -e "/:/d" "$RESOURCES_DIR/china_list.txt"
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>"
@@ -0,0 +1,10 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright (C) 2025 ImmortalWrt.org
CACHE_PATH="$(uci -q get homeproxy.cache.path)"
[ -n "$CACHE_PATH" ] || CACHE_PATH="/etc/homeproxy/cache.db"
rm -f "$CACHE_PATH"
/etc/init.d/homeproxy restart
File diff suppressed because it is too large. Load diff