mirror of
https://github.com/bolucat/Archive.git
synced 2025-09-27 04:30:12 +08:00
Update On Sun Jun 23 20:28:30 CEST 2024
This commit is contained in:
1
.github/update.log
vendored
1
.github/update.log
vendored
@@ -681,3 +681,4 @@ Update On Wed Jun 19 20:31:46 CEST 2024
|
||||
Update On Thu Jun 20 20:32:09 CEST 2024
|
||||
Update On Fri Jun 21 20:31:57 CEST 2024
|
||||
Update On Sat Jun 22 20:28:32 CEST 2024
|
||||
Update On Sun Jun 23 20:28:19 CEST 2024
|
||||
|
@@ -209,7 +209,6 @@ func findPackageName(uid uint32) string {
|
||||
})
|
||||
|
||||
if sharedPackage, loaded := packageManager.SharedPackageByID(uid % 100000); loaded {
|
||||
fmt.Println(loaded)
|
||||
return sharedPackage
|
||||
}
|
||||
if packageName, loaded := packageManager.PackageByID(uid % 100000); loaded {
|
||||
|
@@ -1057,6 +1057,16 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.
|
||||
var nameservers []dns.NameServer
|
||||
|
||||
for idx, server := range servers {
|
||||
if strings.HasPrefix(server, "dhcp://") {
|
||||
nameservers = append(
|
||||
nameservers,
|
||||
dns.NameServer{
|
||||
Net: "dhcp",
|
||||
Addr: server[len("dhcp://"):],
|
||||
},
|
||||
)
|
||||
continue
|
||||
}
|
||||
server = parsePureDNSServer(server)
|
||||
u, err := url.Parse(server)
|
||||
if err != nil {
|
||||
@@ -1099,9 +1109,6 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.
|
||||
}
|
||||
}
|
||||
}
|
||||
case "dhcp":
|
||||
addr = u.Host
|
||||
dnsNetType = "dhcp" // UDP from DHCP
|
||||
case "quic":
|
||||
addr, err = hostWithDefaultPort(u.Host, "853")
|
||||
dnsNetType = "quic" // DNS over QUIC
|
||||
@@ -1174,6 +1181,7 @@ func parsePureDNSServer(server string) string {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) {
|
||||
policy := orderedmap.New[string, []dns.NameServer]()
|
||||
updatedPolicy := orderedmap.New[string, any]()
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 1,
|
||||
"latest": {
|
||||
"mihomo": "v1.18.5",
|
||||
"mihomo_alpha": "alpha-917c5fd",
|
||||
"mihomo_alpha": "alpha-a9ecc62",
|
||||
"clash_rs": "v0.1.18",
|
||||
"clash_premium": "2023-09-05-gdcc8d87"
|
||||
},
|
||||
@@ -36,5 +36,5 @@
|
||||
"darwin-x64": "clash-darwin-amd64-n{}.gz"
|
||||
}
|
||||
},
|
||||
"updated_at": "2024-06-19T22:20:00.814Z"
|
||||
"updated_at": "2024-06-22T22:19:42.822Z"
|
||||
}
|
||||
|
@@ -135,7 +135,7 @@ impl PrfItem {
|
||||
"local" => {
|
||||
let name = item.name.unwrap_or("Local File".into());
|
||||
let desc = item.desc.unwrap_or("".into());
|
||||
PrfItem::from_local(name, desc, file_data)
|
||||
PrfItem::from_local(name, desc, file_data, item.option)
|
||||
}
|
||||
"merge" => {
|
||||
let name = item.name.unwrap_or("Merge".into());
|
||||
@@ -153,7 +153,12 @@ impl PrfItem {
|
||||
|
||||
/// ## Local type
|
||||
/// create a new item from name/desc
|
||||
pub fn from_local(name: String, desc: String, file_data: Option<String>) -> Result<PrfItem> {
|
||||
pub fn from_local(
|
||||
name: String,
|
||||
desc: String,
|
||||
file_data: Option<String>,
|
||||
option: Option<PrfOption>,
|
||||
) -> Result<PrfItem> {
|
||||
let uid = help::get_uid("l");
|
||||
let file = format!("{uid}.yaml");
|
||||
|
||||
@@ -166,7 +171,10 @@ impl PrfItem {
|
||||
url: None,
|
||||
selected: None,
|
||||
extra: None,
|
||||
option: None,
|
||||
option: Some(PrfOption {
|
||||
update_interval: option.unwrap_or_default().update_interval,
|
||||
..PrfOption::default()
|
||||
}),
|
||||
home: None,
|
||||
updated: Some(chrono::Local::now().timestamp() as usize),
|
||||
file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())),
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import dayjs from "dayjs";
|
||||
import { useMemo, useState } from "react";
|
||||
import { DataGrid, GridColDef } from "@mui/x-data-grid";
|
||||
import { useThemeMode } from "@/services/states";
|
||||
import { truncateStr } from "@/utils/truncate-str";
|
||||
import parseTraffic from "@/utils/parse-traffic";
|
||||
import { t } from "i18next";
|
||||
@@ -12,6 +13,9 @@ interface Props {
|
||||
|
||||
export const ConnectionTable = (props: Props) => {
|
||||
const { connections, onShowDetail } = props;
|
||||
const mode = useThemeMode();
|
||||
const isDark = mode === "light" ? false : true;
|
||||
const backgroundColor = isDark ? "#282A36" : "#ffffff";
|
||||
|
||||
const [columnVisible, setColumnVisible] = useState<
|
||||
Partial<Record<keyof IConnectionsItem, boolean>>
|
||||
@@ -96,7 +100,6 @@ export const ConnectionTable = (props: Props) => {
|
||||
source: `${metadata.sourceIP}:${metadata.sourcePort}`,
|
||||
destinationIP: metadata.destinationIP,
|
||||
type: `${metadata.type}(${metadata.network})`,
|
||||
|
||||
connectionData: each,
|
||||
};
|
||||
});
|
||||
@@ -112,8 +115,8 @@ export const ConnectionTable = (props: Props) => {
|
||||
sx={{
|
||||
border: "none",
|
||||
"div:focus": { outline: "none !important" },
|
||||
"& div[aria-rowindex='1']": {
|
||||
backgroundColor: "inherit !important",
|
||||
"& div[aria-rowindex]": {
|
||||
backgroundColor: `${backgroundColor} !important`,
|
||||
},
|
||||
}}
|
||||
columnVisibilityModel={columnVisible}
|
||||
|
@@ -95,6 +95,7 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
||||
if (form.type !== "remote" && form.type !== "local") {
|
||||
delete form.option;
|
||||
}
|
||||
|
||||
if (form.option?.update_interval) {
|
||||
form.option.update_interval = +form.option.update_interval;
|
||||
} else {
|
||||
@@ -228,16 +229,11 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
||||
<TextField
|
||||
{...text}
|
||||
{...field}
|
||||
onChange={(e) => {
|
||||
e.target.value = e.target.value
|
||||
?.replace(/\D/, "")
|
||||
.slice(0, 10);
|
||||
field.onChange(e);
|
||||
}}
|
||||
type="number"
|
||||
label={t("Update Interval")}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">mins</InputAdornment>
|
||||
<InputAdornment position="end">{t("mins")}</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
@@ -248,10 +244,7 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
||||
{isLocal && openType === "new" && (
|
||||
<FileInput
|
||||
onChange={(file, val) => {
|
||||
if (!formIns.getValues("name")) {
|
||||
const name = file.name.substring(0, file.name.lastIndexOf("."));
|
||||
formIns.setValue("name", name);
|
||||
}
|
||||
formIns.setValue("name", formIns.getValues("name") || file.name);
|
||||
fileDataRef.current = val;
|
||||
}}
|
||||
/>
|
||||
|
@@ -8,6 +8,7 @@ import {
|
||||
MenuItem,
|
||||
Select,
|
||||
TextField,
|
||||
InputAdornment,
|
||||
} from "@mui/material";
|
||||
import { useVerge } from "@/hooks/use-verge";
|
||||
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
|
||||
@@ -81,12 +82,12 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
size="small"
|
||||
sx={{ width: 100, "> div": { py: "7.5px" } }}
|
||||
value={values.appLogLevel}
|
||||
onChange={(e) => {
|
||||
onChange={(e) =>
|
||||
setValues((v) => ({
|
||||
...v,
|
||||
appLogLevel: e.target.value as string,
|
||||
}));
|
||||
}}
|
||||
}))
|
||||
}
|
||||
>
|
||||
{["trace", "debug", "info", "warn", "error", "silent"].map((i) => (
|
||||
<MenuItem value={i} key={i}>
|
||||
@@ -130,20 +131,20 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Proxy Layout Column")} />
|
||||
<ListItemText primary={t("Proxy Layout Columns")} />
|
||||
<Select
|
||||
size="small"
|
||||
sx={{ width: 135, "> div": { py: "7.5px" } }}
|
||||
value={values.proxyLayoutColumn}
|
||||
onChange={(e) => {
|
||||
onChange={(e) =>
|
||||
setValues((v) => ({
|
||||
...v,
|
||||
proxyLayoutColumn: e.target.value as number,
|
||||
}));
|
||||
}}
|
||||
}))
|
||||
}
|
||||
>
|
||||
<MenuItem value={6} key={6}>
|
||||
Auto
|
||||
{t("Auto Columns")}
|
||||
</MenuItem>
|
||||
{[1, 2, 3, 4, 5].map((i) => (
|
||||
<MenuItem value={i} key={i}>
|
||||
@@ -159,12 +160,12 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
size="small"
|
||||
sx={{ width: 135, "> div": { py: "7.5px" } }}
|
||||
value={values.autoLogClean}
|
||||
onChange={(e) => {
|
||||
onChange={(e) =>
|
||||
setValues((v) => ({
|
||||
...v,
|
||||
autoLogClean: e.target.value as number,
|
||||
}));
|
||||
}}
|
||||
}))
|
||||
}
|
||||
>
|
||||
{[
|
||||
{ key: "Never Clean", value: 0 },
|
||||
@@ -214,6 +215,11 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
defaultLatencyTimeout: parseInt(e.target.value),
|
||||
}))
|
||||
}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">{t("millis")}</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"millis": "millis",
|
||||
"mins": "mins",
|
||||
|
||||
"Back": "Back",
|
||||
"Close": "Close",
|
||||
"Cancel": "Cancel",
|
||||
@@ -208,7 +211,8 @@
|
||||
"Auto Close Connections": "Auto Close Connections",
|
||||
"Auto Check Update": "Auto Check Update",
|
||||
"Enable Builtin Enhanced": "Enable Builtin Enhanced",
|
||||
"Proxy Layout Column": "Proxy Layout Column",
|
||||
"Proxy Layout Columns": "Proxy Layout Columns",
|
||||
"Auto Columns": "Auto Columns",
|
||||
"Auto Log Clean": "Auto Log Clean",
|
||||
"Never Clean": "Never Clean",
|
||||
"Retain 7 Days": "Retain 7 Days",
|
||||
|
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"millis": "میلیثانیه",
|
||||
"mins": "دقیقه",
|
||||
|
||||
"Back": "بازگشت",
|
||||
"Close": "بستن",
|
||||
"Cancel": "لغو",
|
||||
@@ -208,7 +211,8 @@
|
||||
"Auto Close Connections": "بستن خودکار اتصالات",
|
||||
"Auto Check Update": "بررسی خودکار بهروزرسانی",
|
||||
"Enable Builtin Enhanced": "فعال کردن تقویت داخلی",
|
||||
"Proxy Layout Column": "ستون چیدمان پراکسی",
|
||||
"Proxy Layout Columns": "ستون چیدمان پراکسی",
|
||||
"Auto Columns": "ستونهای خودکار",
|
||||
"Auto Log Clean": "پاکسازی خودکار لاگ",
|
||||
"Never Clean": "هرگز پاک نکن",
|
||||
"Retain 7 Days": "نگهداری به مدت 7 روز",
|
||||
|
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"millis": "миллисекунды",
|
||||
"mins": "минуты",
|
||||
|
||||
"Back": "Назад",
|
||||
"Close": "Закрыть",
|
||||
"Cancel": "Отмена",
|
||||
@@ -208,7 +211,8 @@
|
||||
"Auto Close Connections": "Автоматическое закрытие соединений",
|
||||
"Auto Check Update": "Автоматическая проверка обновлений",
|
||||
"Enable Builtin Enhanced": "Включить встроенные улучшения",
|
||||
"Proxy Layout Column": "Количество столбцов в макете прокси",
|
||||
"Proxy Layout Columns": "Количество столбцов в макете прокси",
|
||||
"Auto Columns": "Авто колонки",
|
||||
"Auto Log Clean": "Автоматическая очистка журналов",
|
||||
"Never Clean": "Никогда не очищать",
|
||||
"Retain 7 Days": "Сохранять 7 дней",
|
||||
|
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"millis": "毫秒",
|
||||
"mins": "分钟",
|
||||
|
||||
"Back": "返回",
|
||||
"Close": "关闭",
|
||||
"Cancel": "取消",
|
||||
@@ -54,7 +57,7 @@
|
||||
"Update Interval": "更新间隔",
|
||||
"Choose File": "选择文件",
|
||||
"Use System Proxy": "使用系统代理更新",
|
||||
"Use Clash Proxy": "使用 Clash 代理更新",
|
||||
"Use Clash Proxy": "使用内核代理更新",
|
||||
"Accept Invalid Certs (Danger)": "允许无效证书 (危险)",
|
||||
"Refresh": "刷新",
|
||||
"Home": "首页",
|
||||
@@ -208,7 +211,8 @@
|
||||
"Auto Close Connections": "自动关闭连接",
|
||||
"Auto Check Update": "自动检查更新",
|
||||
"Enable Builtin Enhanced": "内置增强功能",
|
||||
"Proxy Layout Column": "代理页布局列数",
|
||||
"Proxy Layout Columns": "代理页布局列数",
|
||||
"Auto Columns": "自动列数",
|
||||
"Auto Log Clean": "自动清理日志",
|
||||
"Never Clean": "不清理",
|
||||
"Retain 7 Days": "保留7天",
|
||||
|
3
geoip/.github/workflows/build.yml
vendored
3
geoip/.github/workflows/build.yml
vendored
@@ -94,6 +94,7 @@ jobs:
|
||||
mkdir -p publish
|
||||
mv ./output/dat/*.dat ./output/dat/*.sha256sum ./output/maxmind/*.mmdb ./output/maxmind/*.sha256sum *.gz *.zip ./publish/
|
||||
cp -fpPR ./output/text ./publish
|
||||
cp -fpPR ./output/srs ./publish
|
||||
|
||||
- name: Git push assets to "release" branch
|
||||
run: |
|
||||
@@ -115,7 +116,7 @@ jobs:
|
||||
done
|
||||
|
||||
- name: Remove some files to avoid publishing to GitHub release
|
||||
run: rm -rf ./publish/*.{gz,zip} ./publish/text
|
||||
run: rm -rf ./publish/*.{gz,zip} ./publish/text ./publish/srs
|
||||
|
||||
- name: Upload files to GitHub release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
|
55
geoip/.gitignore
vendored
55
geoip/.gitignore
vendored
@@ -1,3 +1,55 @@
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
@@ -19,3 +71,6 @@ data/
|
||||
geolite2/
|
||||
output/
|
||||
geoip
|
||||
*.dat
|
||||
*.mmdb
|
||||
*.srs
|
||||
|
@@ -1,8 +1,8 @@
|
||||
# 简介
|
||||
|
||||
本项目每周四自动生成 GeoIP 文件,同时提供命令行界面(CLI)供用户自行定制 GeoIP 文件,包括但不限于 V2Ray dat 格式路由规则文件 `geoip.dat` 和 MaxMind mmdb 格式文件 `Country.mmdb`。
|
||||
本项目每周四自动生成 GeoIP 文件,同时提供命令行界面(CLI)供用户自行定制 GeoIP 文件,包括但不限于 V2Ray dat 格式路由规则文件 `geoip.dat`、MaxMind mmdb 格式文件 `Country.mmdb` 和 sing-box SRS 格式文件。
|
||||
|
||||
This project releases GeoIP files automatically every Thursday. It also provides a command line interface(CLI) for users to customize their own GeoIP files, included but not limited to V2Ray dat format file `geoip.dat` and MaxMind mmdb format file `Country.mmdb`.
|
||||
This project releases GeoIP files automatically every Thursday. It also provides a command line interface(CLI) for users to customize their own GeoIP files, included but not limited to V2Ray dat format file `geoip.dat`, MaxMind mmdb format file `Country.mmdb` and sing-box SRS format files.
|
||||
|
||||
## 与官方版 GeoIP 的区别
|
||||
|
||||
@@ -64,6 +64,37 @@ rules:
|
||||
|
||||
在 [Leaf](https://github.com/eycorsican/leaf) 中使用本项目 `.mmdb` 格式文件的参考配置,查看[官方 README](https://github.com/eycorsican/leaf/blob/master/README.zh.md#geoip)。
|
||||
|
||||
在 [sing-box](https://github.com/SagerNet/sing-box) 中使用本项目 `.srs` 格式文件的参考配置:
|
||||
|
||||
```json
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"rule_set": "geoip-cn",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"rule_set": "geoip-us",
|
||||
"outbound": "block"
|
||||
}
|
||||
],
|
||||
"rule_set": [
|
||||
{
|
||||
"tag": "geoip-cn",
|
||||
"type": "remote",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/Loyalsoldier/geoip/release/srs/cn.srs"
|
||||
},
|
||||
{
|
||||
"tag": "geoip-us",
|
||||
"type": "remote",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/Loyalsoldier/geoip/release/srs/us.srs"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 下载地址
|
||||
|
||||
> 如果无法访问域名 `raw.githubusercontent.com`,可以使用第二个地址 `cdn.jsdelivr.net`。
|
||||
@@ -127,6 +158,12 @@ rules:
|
||||
- [https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country-asn.mmdb.sha256sum](https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country-asn.mmdb.sha256sum)
|
||||
- [https://cdn.jsdelivr.net/gh/Loyalsoldier/geoip@release/Country-asn.mmdb.sha256sum](https://cdn.jsdelivr.net/gh/Loyalsoldier/geoip@release/Country-asn.mmdb.sha256sum)
|
||||
|
||||
### sing-box SRS 格式路由规则文件
|
||||
|
||||
> 适用于 [sing-box](https://github.com/SagerNet/sing-box)。
|
||||
|
||||
请查看本项目 `release` 分支下的 [srs](https://github.com/Loyalsoldier/geoip/tree/release/srs) 目录。
|
||||
|
||||
## 定制 GeoIP 文件
|
||||
|
||||
可通过以下几种方式定制 GeoIP 文件:
|
||||
@@ -162,6 +199,7 @@ These two concepts are notable: `input` and `output`. The `input` is the data so
|
||||
- **v2rayGeoIPDat**:V2Ray GeoIP dat 格式(`geoip.dat`)
|
||||
- **maxmindMMDB**:MaxMind mmdb 数据格式(`GeoLite2-Country.mmdb`)
|
||||
- **maxmindGeoLite2CountryCSV**:MaxMind GeoLite2 country CSV 数据(`GeoLite2-Country-CSV.zip`)
|
||||
- **singboxSRS**:sing-box SRS 格式(`geoip-cn.srs`)
|
||||
- **clashRuleSetClassical**:[classical 类型的 Clash RuleSet](https://github.com/Dreamacro/clash/wiki/premium-core-features#classical)
|
||||
- **clashRuleSet**:[ipcidr 类型的 Clash RuleSet](https://github.com/Dreamacro/clash/wiki/premium-core-features#ipcidr)
|
||||
- **surgeRuleSet**:[Surge RuleSet](https://manual.nssurge.com/rule/ruleset.html)
|
||||
@@ -171,6 +209,7 @@ These two concepts are notable: `input` and `output`. The `input` is the data so
|
||||
- **text**:纯文本 CIDR(例如:`1.0.0.0/24`)
|
||||
- **v2rayGeoIPDat**:V2Ray GeoIP dat 格式(`geoip.dat`,适用于 [V2Ray](https://github.com/v2fly/v2ray-core)、[Xray-core](https://github.com/XTLS/Xray-core) 和 [Trojan-Go](https://github.com/p4gefau1t/trojan-go))
|
||||
- **maxmindMMDB**:MaxMind mmdb 数据格式(`GeoLite2-Country.mmdb`,适用于 [Clash](https://github.com/Dreamacro/clash) 和 [Leaf](https://github.com/eycorsican/leaf))
|
||||
- **singboxSRS**:sing-box SRS 格式(`geoip-cn.srs`,适用于 [sing-box](https://github.com/SagerNet/sing-box))
|
||||
- **clashRuleSetClassical**:[classical 类型的 Clash RuleSet](https://github.com/Dreamacro/clash/wiki/premium-core-features#classical)
|
||||
- **clashRuleSet**:[ipcidr 类型的 Clash RuleSet](https://github.com/Dreamacro/clash/wiki/premium-core-features#ipcidr)
|
||||
- **surgeRuleSet**:[Surge RuleSet](https://manual.nssurge.com/rule/ruleset.html)
|
||||
@@ -207,12 +246,20 @@ $ ./geoip -c config.json
|
||||
2021/08/29 12:11:39 ✅ [text] cloudfront.txt --> output/text
|
||||
2021/08/29 12:11:39 ✅ [text] facebook.txt --> output/text
|
||||
2021/08/29 12:11:39 ✅ [text] fastly.txt --> output/text
|
||||
2021/08/29 12:11:45 ✅ [singboxSRS] netflix.txt --> output/srs
|
||||
2021/08/29 12:11:45 ✅ [singboxSRS] telegram.txt --> output/srs
|
||||
2021/08/29 12:11:45 ✅ [singboxSRS] cn.txt --> output/srs
|
||||
2021/08/29 12:11:45 ✅ [singboxSRS] cloudflare.txt --> output/srs
|
||||
2021/08/29 12:11:45 ✅ [singboxSRS] cloudfront.txt --> output/srs
|
||||
2021/08/29 12:11:45 ✅ [singboxSRS] facebook.txt --> output/srs
|
||||
2021/08/29 12:11:45 ✅ [singboxSRS] fastly.txt --> output/srs
|
||||
|
||||
$ ./geoip -l
|
||||
All available input formats:
|
||||
- v2rayGeoIPDat (Convert V2Ray GeoIP dat to other formats)
|
||||
- maxmindMMDB (Convert MaxMind mmdb database to other formats)
|
||||
- maxmindGeoLite2CountryCSV (Convert MaxMind GeoLite2 country CSV data to other formats)
|
||||
- singboxSRS (Convert sing-box SRS data to other formats)
|
||||
- private (Convert LAN and private network CIDR to other formats)
|
||||
- text (Convert plaintext IP & CIDR to other formats)
|
||||
- clashRuleSetClassical (Convert classical type of Clash RuleSet to other formats (just processing IP & CIDR lines))
|
||||
@@ -223,6 +270,7 @@ All available input formats:
|
||||
All available output formats:
|
||||
- v2rayGeoIPDat (Convert data to V2Ray GeoIP dat format)
|
||||
- maxmindMMDB (Convert data to MaxMind mmdb database format)
|
||||
- singboxSRS (Convert data to sing-box SRS format)
|
||||
- clashRuleSetClassical (Convert data to classical type of Clash RuleSet)
|
||||
- clashRuleSet (Convert data to ipcidr type of Clash RuleSet)
|
||||
- surgeRuleSet (Convert data to Surge RuleSet)
|
||||
|
@@ -56,6 +56,31 @@
|
||||
"onlyIPType": "ipv6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "singboxSRS",
|
||||
"action": "add",
|
||||
"args": {
|
||||
"name": "cn",
|
||||
"uri": "https://raw.githubusercontent.com/Loyalsoldier/geoip/release/srs/cn.srs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "singboxSRS",
|
||||
"action": "add",
|
||||
"args": {
|
||||
"name": "cn",
|
||||
"uri": "./srs/cn.srs",
|
||||
"onlyIPType": "ipv4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "singboxSRS",
|
||||
"action": "add",
|
||||
"args": {
|
||||
"inputDir": "./srs",
|
||||
"onlyIPType": "ipv6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cutter",
|
||||
"action": "remove",
|
||||
@@ -164,6 +189,28 @@
|
||||
"onlyIPType": "ipv4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "singboxSRS",
|
||||
"action": "output",
|
||||
"args": {
|
||||
"outputDir": "./publish"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "singboxSRS",
|
||||
"action": "output",
|
||||
"args": {
|
||||
"wantedList": ["cn", "us"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "singboxSRS",
|
||||
"action": "output",
|
||||
"args": {
|
||||
"wantedList": ["cn", "us"],
|
||||
"onlyIPType": "ipv4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"action": "output",
|
||||
|
@@ -165,6 +165,10 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "singboxSRS",
|
||||
"action": "output"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"action": "output"
|
||||
|
19
geoip/go.mod
19
geoip/go.mod
@@ -7,24 +7,31 @@ toolchain go1.21.10
|
||||
require (
|
||||
github.com/maxmind/mmdbwriter v1.0.0
|
||||
github.com/oschwald/maxminddb-golang v1.13.0
|
||||
github.com/sagernet/sing-box v1.9.3
|
||||
github.com/v2fly/v2ray-core/v5 v5.16.1
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
google.golang.org/protobuf v1.34.1
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/adrg/xdg v0.4.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/miekg/dns v1.1.59 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||
github.com/quic-go/quic-go v0.43.0 // indirect
|
||||
github.com/sagernet/sing v0.4.1 // indirect
|
||||
github.com/sagernet/sing-dns v0.2.0 // indirect
|
||||
go.starlark.net v0.0.0-20230612165344-9532f5667272 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/tools v0.21.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
61
geoip/go.sum
61
geoip/go.sum
@@ -71,8 +71,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg=
|
||||
@@ -83,8 +83,6 @@ github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/q
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/reedsolomon v1.11.7 h1:9uaHU0slncktTEEg4+7Vl7q7XUNMBUOK4R9gnKhMjAU=
|
||||
github.com/klauspost/reedsolomon v1.11.7/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
@@ -93,10 +91,14 @@ github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||
github.com/maxmind/mmdbwriter v1.0.0 h1:bieL4P6yaYaHvbtLSwnKtEvScUKKD6jcKaLiTM3WSMw=
|
||||
github.com/maxmind/mmdbwriter v1.0.0/go.mod h1:noBMCUtyN5PUQ4H8ikkOvGSHhzhLok51fON2hcrpKj8=
|
||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||
github.com/mustafaturan/bus v1.0.2 h1:2x3ErwZ0uUPwwZ5ZZoknEQprdaxr68Yl3mY8jDye1Ws=
|
||||
github.com/mustafaturan/bus v1.0.2/go.mod h1:h7gfehm8TThv4Dcaa+wDQG7r7j6p74v+7ftr0Rq9i1Q=
|
||||
github.com/mustafaturan/monoton v1.0.0 h1:8SCej+JiNn0lyps7V+Jzc1CRAkDR4EZPWrTupQ61YCQ=
|
||||
github.com/mustafaturan/monoton v1.0.0/go.mod h1:FOnE7NV3s3EWPXb8/7+/OSdiMBbdlkV0Lz8p1dc+vy8=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.10.0 h1:sfUl4qgLdvkChZrWCYndY2EAu9BRIw1YphNAzy1VNWs=
|
||||
github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
@@ -119,18 +121,27 @@ github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3
|
||||
github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.43.0 h1:sjtsTKWX0dsHpuMJvLxGqoQdtgJnbAPWY+W+5vjYW/g=
|
||||
github.com/quic-go/quic-go v0.43.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
|
||||
github.com/refraction-networking/utls v1.6.5 h1:Jlfqgs/t1Uy6FHHQ8Fz9ZTrRmP/zS7d/NZw7BLahaL8=
|
||||
github.com/refraction-networking/utls v1.6.5/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/sagernet/quic-go v0.43.1-beta.2 h1:6YRCE9t1Q3UbNX1/dJGqpwFQbh6DXC6XBrQr2xp6hXY=
|
||||
github.com/sagernet/quic-go v0.43.1-beta.2/go.mod h1:BkrQYeop7Jx3hN3TW8/76CXcdhYiNPyYEBL/BVJ1ifc=
|
||||
github.com/sagernet/sing v0.4.1 h1:zVlpE+7k7AFoC2pv6ReqLf0PIHjihL/jsBl5k05PQFk=
|
||||
github.com/sagernet/sing v0.4.1/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
|
||||
github.com/sagernet/sing-box v1.9.3 h1:jXiAqQRzBeXCSLTTl0Z92OLs5GkVotsdiNRVATZWpoY=
|
||||
github.com/sagernet/sing-box v1.9.3/go.mod h1:6Rx5nzbqIfN7HlUaHgO/IdkP7fDPPQ/U/TAC5asEjSM=
|
||||
github.com/sagernet/sing-dns v0.2.0 h1:dka3weRX6+CrYO3v+hrTy2z68rCOCZXNBiNXpLZ6JNs=
|
||||
github.com/sagernet/sing-dns v0.2.0/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg=
|
||||
github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 h1:zOjq+1/uLzn/Xo40stbvjIY/yehG0+mfmlsiEmc0xmQ=
|
||||
github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4/go.mod h1:aI+8yClBW+1uovkHw6HM01YXnYB8vohtB9C83wzx34E=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
||||
@@ -160,22 +171,22 @@ go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -187,20 +198,20 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
||||
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
@@ -223,11 +234,11 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
_ "github.com/Loyalsoldier/geoip/plugin/maxmind"
|
||||
_ "github.com/Loyalsoldier/geoip/plugin/plaintext"
|
||||
_ "github.com/Loyalsoldier/geoip/plugin/singbox"
|
||||
_ "github.com/Loyalsoldier/geoip/plugin/special"
|
||||
_ "github.com/Loyalsoldier/geoip/plugin/v2ray"
|
||||
)
|
||||
|
232
geoip/plugin/singbox/srs_in.go
Normal file
232
geoip/plugin/singbox/srs_in.go
Normal file
@@ -0,0 +1,232 @@
|
||||
package singbox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/Loyalsoldier/geoip/lib"
|
||||
"github.com/sagernet/sing-box/common/srs"
|
||||
)
|
||||
|
||||
const (
|
||||
typeSRSIn = "singboxSRS"
|
||||
descSRSIn = "Convert sing-box SRS data to other formats"
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib.RegisterInputConfigCreator(typeSRSIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
return newSRSIn(action, data)
|
||||
})
|
||||
lib.RegisterInputConverter(typeSRSIn, &srsIn{
|
||||
Description: descSRSIn,
|
||||
})
|
||||
}
|
||||
|
||||
func newSRSIn(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
var tmp struct {
|
||||
Name string `json:"name"`
|
||||
URI string `json:"uri"`
|
||||
InputDir string `json:"inputDir"`
|
||||
OnlyIPType lib.IPType `json:"onlyIPType"`
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tmp.Name == "" && tmp.URI == "" && tmp.InputDir == "" {
|
||||
return nil, fmt.Errorf("type %s | action %s missing inputdir or name or uri", typeSRSIn, action)
|
||||
}
|
||||
|
||||
if (tmp.Name != "" && tmp.URI == "") || (tmp.Name == "" && tmp.URI != "") {
|
||||
return nil, fmt.Errorf("type %s | action %s name & uri must be specified together", typeSRSIn, action)
|
||||
}
|
||||
|
||||
return &srsIn{
|
||||
Type: typeSRSIn,
|
||||
Action: action,
|
||||
Description: descSRSIn,
|
||||
Name: tmp.Name,
|
||||
URI: tmp.URI,
|
||||
InputDir: tmp.InputDir,
|
||||
OnlyIPType: tmp.OnlyIPType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type srsIn struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
Name string
|
||||
URI string
|
||||
InputDir string
|
||||
OnlyIPType lib.IPType
|
||||
}
|
||||
|
||||
func (s *srsIn) GetType() string {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
func (s *srsIn) GetAction() lib.Action {
|
||||
return s.Action
|
||||
}
|
||||
|
||||
func (s *srsIn) GetDescription() string {
|
||||
return s.Description
|
||||
}
|
||||
|
||||
func (s *srsIn) Input(container lib.Container) (lib.Container, error) {
|
||||
entries := make(map[string]*lib.Entry)
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case s.InputDir != "":
|
||||
err = s.walkDir(s.InputDir, entries)
|
||||
case s.Name != "" && s.URI != "":
|
||||
switch {
|
||||
case strings.HasPrefix(s.URI, "http://"), strings.HasPrefix(s.URI, "https://"):
|
||||
err = s.walkRemoteFile(s.URI, s.Name, entries)
|
||||
default:
|
||||
err = s.walkLocalFile(s.URI, s.Name, entries)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("config missing argument inputDir or name or uri")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ignoreIPType lib.IgnoreIPOption
|
||||
switch s.OnlyIPType {
|
||||
case lib.IPv4:
|
||||
ignoreIPType = lib.IgnoreIPv6
|
||||
case lib.IPv6:
|
||||
ignoreIPType = lib.IgnoreIPv4
|
||||
}
|
||||
|
||||
if len(entries) == 0 {
|
||||
return nil, fmt.Errorf("type %s | action %s no entry are generated", s.Type, s.Action)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
switch s.Action {
|
||||
case lib.ActionAdd:
|
||||
if err := container.Add(entry, ignoreIPType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case lib.ActionRemove:
|
||||
container.Remove(entry.GetName(), ignoreIPType)
|
||||
}
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func (s *srsIn) walkDir(dir string, entries map[string]*lib.Entry) error {
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.walkLocalFile(path, "", entries); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *srsIn) walkLocalFile(path, name string, entries map[string]*lib.Entry) error {
|
||||
name = strings.TrimSpace(name)
|
||||
var filename string
|
||||
if name != "" {
|
||||
filename = name
|
||||
} else {
|
||||
filename = filepath.Base(path)
|
||||
}
|
||||
|
||||
// check filename
|
||||
if !regexp.MustCompile(`^[a-zA-Z0-9_.\-]+$`).MatchString(filename) {
|
||||
return fmt.Errorf("filename %s cannot be entry name, please remove special characters in it", filename)
|
||||
}
|
||||
dotIndex := strings.LastIndex(filename, ".")
|
||||
if dotIndex > 0 {
|
||||
filename = filename[:dotIndex]
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err := s.generateEntries(filename, file, entries); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *srsIn) walkRemoteFile(url, name string, entries map[string]*lib.Entry) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("failed to get remote file %s, http status code %d", url, resp.StatusCode)
|
||||
}
|
||||
|
||||
if err := s.generateEntries(name, resp.Body, entries); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *srsIn) generateEntries(name string, reader io.Reader, entries map[string]*lib.Entry) error {
|
||||
entry := lib.NewEntry(name)
|
||||
if theEntry, found := entries[entry.GetName()]; found {
|
||||
fmt.Printf("⚠️ [type %s | action %s] found duplicated entry: %s. Process anyway\n", typeSRSIn, s.Action, name)
|
||||
entry = theEntry
|
||||
}
|
||||
|
||||
plainRuleSet, err := srs.Read(reader, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rule := range plainRuleSet.Rules {
|
||||
for _, cidrStr := range rule.DefaultOptions.IPCIDR {
|
||||
switch s.Action {
|
||||
case lib.ActionAdd:
|
||||
if err := entry.AddPrefix(cidrStr); err != nil {
|
||||
return err
|
||||
}
|
||||
case lib.ActionRemove:
|
||||
if err := entry.RemovePrefix(cidrStr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entries[entry.GetName()] = entry
|
||||
|
||||
return nil
|
||||
}
|
183
geoip/plugin/singbox/srs_out.go
Normal file
183
geoip/plugin/singbox/srs_out.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package singbox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Loyalsoldier/geoip/lib"
|
||||
"github.com/sagernet/sing-box/common/srs"
|
||||
"github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
const (
|
||||
typeSRSOut = "singboxSRS"
|
||||
descSRSOut = "Convert data to sing-box SRS format"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultOutputDir = filepath.Join("./", "output", "srs")
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib.RegisterOutputConfigCreator(typeSRSOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
|
||||
return newSRSOut(action, data)
|
||||
})
|
||||
lib.RegisterOutputConverter(typeSRSOut, &srsOut{
|
||||
Description: descSRSOut,
|
||||
})
|
||||
}
|
||||
|
||||
func newSRSOut(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
|
||||
var tmp struct {
|
||||
OutputDir string `json:"outputDir"`
|
||||
Want []string `json:"wantedList"`
|
||||
OnlyIPType lib.IPType `json:"onlyIPType"`
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tmp.OutputDir == "" {
|
||||
tmp.OutputDir = defaultOutputDir
|
||||
}
|
||||
|
||||
return &srsOut{
|
||||
Type: typeSRSOut,
|
||||
Action: action,
|
||||
Description: descSRSOut,
|
||||
OutputDir: tmp.OutputDir,
|
||||
Want: tmp.Want,
|
||||
OnlyIPType: tmp.OnlyIPType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type srsOut struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
OutputDir string
|
||||
Want []string
|
||||
OnlyIPType lib.IPType
|
||||
}
|
||||
|
||||
func (s *srsOut) GetType() string {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
func (s *srsOut) GetAction() lib.Action {
|
||||
return s.Action
|
||||
}
|
||||
|
||||
func (s *srsOut) GetDescription() string {
|
||||
return s.Description
|
||||
}
|
||||
|
||||
func (s *srsOut) Output(container lib.Container) error {
|
||||
// Filter want list
|
||||
wantList := make(map[string]bool)
|
||||
for _, want := range s.Want {
|
||||
if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
|
||||
wantList[want] = true
|
||||
}
|
||||
}
|
||||
|
||||
switch len(wantList) {
|
||||
case 0:
|
||||
for entry := range container.Loop() {
|
||||
if err := s.run(entry); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
for name := range wantList {
|
||||
entry, found := container.GetEntry(name)
|
||||
if !found {
|
||||
log.Printf("❌ entry %s not found", name)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := s.run(entry); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *srsOut) run(entry *lib.Entry) error {
|
||||
ruleset, err := s.generateRuleSet(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := strings.ToLower(entry.GetName()) + ".srs"
|
||||
if err := s.writeFile(filename, ruleset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *srsOut) generateRuleSet(entry *lib.Entry) (*option.PlainRuleSet, error) {
|
||||
var entryCidr []string
|
||||
var err error
|
||||
switch s.OnlyIPType {
|
||||
case lib.IPv4:
|
||||
entryCidr, err = entry.MarshalText(lib.IgnoreIPv6)
|
||||
case lib.IPv6:
|
||||
entryCidr, err = entry.MarshalText(lib.IgnoreIPv4)
|
||||
default:
|
||||
entryCidr, err = entry.MarshalText()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var headlessRule option.DefaultHeadlessRule
|
||||
headlessRule.IPCIDR = entryCidr
|
||||
|
||||
var plainRuleSet option.PlainRuleSet
|
||||
plainRuleSet.Rules = []option.HeadlessRule{
|
||||
{
|
||||
Type: constant.RuleTypeDefault,
|
||||
DefaultOptions: headlessRule,
|
||||
},
|
||||
}
|
||||
|
||||
if len(headlessRule.IPCIDR) > 0 {
|
||||
return &plainRuleSet, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("entry %s has no CIDR", entry.GetName())
|
||||
}
|
||||
|
||||
func (s *srsOut) writeFile(filename string, ruleset *option.PlainRuleSet) error {
|
||||
if err := os.MkdirAll(s.OutputDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(filepath.Join(s.OutputDir, filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = srs.Write(f, *ruleset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("✅ [%s] %s --> %s", s.Type, filename, s.OutputDir)
|
||||
|
||||
return nil
|
||||
}
|
@@ -44,6 +44,7 @@ define KernelPackage/ipt-fullconenat
|
||||
CONFIG_NF_CONNTRACK_EVENTS=y \
|
||||
CONFIG_NF_CONNTRACK_CHAIN_EVENTS=y
|
||||
FILES:=$(PKG_BUILD_DIR)/xt_FULLCONENAT.ko
|
||||
AUTOLOAD:=$(call AutoProbe,xt_FULLCONENAT)
|
||||
endef
|
||||
|
||||
include $(INCLUDE_DIR)/kernel-defaults.mk
|
||||
|
@@ -209,7 +209,6 @@ func findPackageName(uid uint32) string {
|
||||
})
|
||||
|
||||
if sharedPackage, loaded := packageManager.SharedPackageByID(uid % 100000); loaded {
|
||||
fmt.Println(loaded)
|
||||
return sharedPackage
|
||||
}
|
||||
if packageName, loaded := packageManager.PackageByID(uid % 100000); loaded {
|
||||
|
@@ -1057,6 +1057,16 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.
|
||||
var nameservers []dns.NameServer
|
||||
|
||||
for idx, server := range servers {
|
||||
if strings.HasPrefix(server, "dhcp://") {
|
||||
nameservers = append(
|
||||
nameservers,
|
||||
dns.NameServer{
|
||||
Net: "dhcp",
|
||||
Addr: server[len("dhcp://"):],
|
||||
},
|
||||
)
|
||||
continue
|
||||
}
|
||||
server = parsePureDNSServer(server)
|
||||
u, err := url.Parse(server)
|
||||
if err != nil {
|
||||
@@ -1099,9 +1109,6 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.
|
||||
}
|
||||
}
|
||||
}
|
||||
case "dhcp":
|
||||
addr = u.Host
|
||||
dnsNetType = "dhcp" // UDP from DHCP
|
||||
case "quic":
|
||||
addr, err = hostWithDefaultPort(u.Host, "853")
|
||||
dnsNetType = "quic" // DNS over QUIC
|
||||
@@ -1174,6 +1181,7 @@ func parsePureDNSServer(server string) string {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) {
|
||||
policy := orderedmap.New[string, []dns.NameServer]()
|
||||
updatedPolicy := orderedmap.New[string, any]()
|
||||
|
@@ -818,24 +818,34 @@ struct PingChecker {
|
||||
impl PingChecker {
|
||||
/// Checks server's score and update into `ServerScore<E>`
|
||||
async fn check_update_score(self) {
|
||||
let score = match self.check_delay().await {
|
||||
Ok(d) => match self.server_type {
|
||||
ServerType::Tcp => self.server.tcp_score().push_score(Score::Latency(d)).await,
|
||||
ServerType::Udp => self.server.udp_score().push_score(Score::Latency(d)).await,
|
||||
},
|
||||
// Penalty
|
||||
Err(..) => match self.server_type {
|
||||
ServerType::Tcp => self.server.tcp_score().push_score(Score::Errored).await,
|
||||
ServerType::Udp => self.server.udp_score().push_score(Score::Errored).await,
|
||||
},
|
||||
let server_score = match self.server_type {
|
||||
ServerType::Tcp => self.server.tcp_score(),
|
||||
ServerType::Udp => self.server.udp_score(),
|
||||
};
|
||||
|
||||
trace!(
|
||||
"updated remote {} server {} (score: {})",
|
||||
let (score, stat_data) = match self.check_delay().await {
|
||||
Ok(d) => server_score.push_score_fetch_statistic(Score::Latency(d)).await,
|
||||
// Penalty
|
||||
Err(..) => server_score.push_score_fetch_statistic(Score::Errored).await,
|
||||
};
|
||||
|
||||
if stat_data.fail_rate > 0.8 {
|
||||
warn!(
|
||||
"balancer: checked & updated remote {} server {} (score: {}), {:?}",
|
||||
self.server_type,
|
||||
self.server.server_config().addr(),
|
||||
score
|
||||
ServerConfigFormatter::new(self.server.server_config()),
|
||||
score,
|
||||
stat_data,
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
"balancer: checked & updated remote {} server {} (score: {}), {:?}",
|
||||
self.server_type,
|
||||
ServerConfigFormatter::new(self.server.server_config()),
|
||||
score,
|
||||
stat_data,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect TCP connectivity with Chromium [Network Portal Detection](https://www.chromium.org/chromium-os/chromiumos-design-docs/network-portal-detection)
|
||||
@@ -988,7 +998,7 @@ impl PingChecker {
|
||||
trace!(
|
||||
"checked remote {} server {} latency with {} ms",
|
||||
self.server_type,
|
||||
self.server.server_config().addr(),
|
||||
ServerConfigFormatter::new(self.server.server_config()),
|
||||
elapsed
|
||||
);
|
||||
Ok(elapsed)
|
||||
@@ -997,7 +1007,7 @@ impl PingChecker {
|
||||
debug!(
|
||||
"failed to check {} server {}, error: {}",
|
||||
self.server_type,
|
||||
self.server.server_config().addr(),
|
||||
ServerConfigFormatter::new(self.server.server_config()),
|
||||
err
|
||||
);
|
||||
|
||||
@@ -1011,7 +1021,7 @@ impl PingChecker {
|
||||
trace!(
|
||||
"checked remote {} server {} latency timeout, elapsed {} ms",
|
||||
self.server_type,
|
||||
self.server.server_config().addr(),
|
||||
ServerConfigFormatter::new(self.server.server_config()),
|
||||
elapsed
|
||||
);
|
||||
|
||||
|
@@ -14,7 +14,7 @@ use tokio::sync::Mutex;
|
||||
|
||||
use crate::{config::ServerInstanceConfig, local::context::ServiceContext};
|
||||
|
||||
use super::server_stat::{Score, ServerStat};
|
||||
use super::server_stat::{Score, ServerStat, ServerStatData};
|
||||
|
||||
/// Server's statistic score
|
||||
pub struct ServerScore {
|
||||
@@ -49,10 +49,25 @@ impl ServerScore {
|
||||
updated_score
|
||||
}
|
||||
|
||||
/// Append a `Score` into statistic and recalculate score of the server
|
||||
pub async fn push_score_fetch_statistic(&self, score: Score) -> (u32, ServerStatData) {
|
||||
let (updated_score, data) = {
|
||||
let mut stat = self.stat_data.lock().await;
|
||||
(stat.push_score(score), stat.data().clone())
|
||||
};
|
||||
self.score.store(updated_score, Ordering::Release);
|
||||
(updated_score, data)
|
||||
}
|
||||
|
||||
/// Report request failure of this server, which will eventually records an `Errored` score
|
||||
pub async fn report_failure(&self) -> u32 {
|
||||
self.push_score(Score::Errored).await
|
||||
}
|
||||
|
||||
/// Get statistic data
|
||||
pub async fn stat_data(&self) -> ServerStatData {
|
||||
self.stat_data.lock().await.data().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ServerScore {
|
||||
|
@@ -19,30 +19,37 @@ pub enum Score {
|
||||
Errored,
|
||||
}
|
||||
|
||||
/// Statistic of a remote server
|
||||
#[derive(Debug)]
|
||||
pub struct ServerStat {
|
||||
/// Server statistic data
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ServerStatData {
|
||||
/// Median of latency time (in millisec)
|
||||
///
|
||||
/// Use median instead of average time,
|
||||
/// because probing result may have some really bad cases
|
||||
rtt: u32,
|
||||
pub latency_median: u32,
|
||||
/// Total_Fail / Total_Probe
|
||||
pub fail_rate: f64,
|
||||
/// Score's standard deviation
|
||||
pub latency_stdev: f64,
|
||||
/// Score's average
|
||||
pub latency_mean: f64,
|
||||
}
|
||||
|
||||
/// Statistic of a remote server
|
||||
#[derive(Debug)]
|
||||
pub struct ServerStat {
|
||||
/// MAX server's RTT, normally the check timeout milliseconds
|
||||
max_server_rtt: u32,
|
||||
/// Total_Fail / Total_Probe
|
||||
fail_rate: f64,
|
||||
/// Recently probe data
|
||||
latency_queue: VecDeque<(Score, Instant)>,
|
||||
/// Score's standard deviation
|
||||
latency_stdev: f64,
|
||||
/// Score's standard deviation MAX
|
||||
max_latency_stdev: f64,
|
||||
/// Score's average
|
||||
latency_mean: f64,
|
||||
/// User's customized weight
|
||||
user_weight: f32,
|
||||
/// Checking window size
|
||||
check_window: Duration,
|
||||
/// Statistic Data
|
||||
data: ServerStatData,
|
||||
}
|
||||
|
||||
fn max_latency_stdev(max_server_rtt: u32) -> f64 {
|
||||
@@ -58,25 +65,28 @@ impl ServerStat {
|
||||
pub fn new(user_weight: f32, max_server_rtt: u32, check_window: Duration) -> ServerStat {
|
||||
assert!((0.0..=1.0).contains(&user_weight));
|
||||
|
||||
let max_latency_stdev = max_latency_stdev(max_server_rtt);
|
||||
ServerStat {
|
||||
rtt: max_server_rtt,
|
||||
max_server_rtt,
|
||||
fail_rate: 1.0,
|
||||
latency_queue: VecDeque::new(),
|
||||
latency_stdev: 0.0,
|
||||
max_latency_stdev: max_latency_stdev(max_server_rtt),
|
||||
latency_mean: 0.0,
|
||||
max_latency_stdev,
|
||||
user_weight,
|
||||
check_window,
|
||||
data: ServerStatData {
|
||||
latency_median: max_server_rtt,
|
||||
fail_rate: 1.0,
|
||||
latency_stdev: max_latency_stdev,
|
||||
latency_mean: max_server_rtt as f64,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn score(&self) -> u32 {
|
||||
// Normalize rtt
|
||||
let nrtt = self.rtt as f64 / self.max_server_rtt as f64;
|
||||
let nrtt = self.data.latency_median as f64 / self.max_server_rtt as f64;
|
||||
|
||||
// Normalize stdev
|
||||
let nstdev = self.latency_stdev / self.max_latency_stdev;
|
||||
let nstdev = self.data.latency_stdev / self.max_latency_stdev;
|
||||
|
||||
const SCORE_RTT_WEIGHT: f64 = 1.0;
|
||||
const SCORE_FAIL_WEIGHT: f64 = 3.0;
|
||||
@@ -92,7 +102,7 @@ impl ServerStat {
|
||||
// 2. The lower errored count, the better
|
||||
// 3. The lower latency's stdev, the better
|
||||
// 4. The higher user's weight, the better
|
||||
let score = (nrtt * SCORE_RTT_WEIGHT + self.fail_rate * SCORE_FAIL_WEIGHT + nstdev * SCORE_STDEV_WEIGHT)
|
||||
let score = (nrtt * SCORE_RTT_WEIGHT + self.data.fail_rate * SCORE_FAIL_WEIGHT + nstdev * SCORE_STDEV_WEIGHT)
|
||||
/ (SCORE_RTT_WEIGHT + SCORE_FAIL_WEIGHT + SCORE_STDEV_WEIGHT)
|
||||
/ user_weight as f64;
|
||||
|
||||
@@ -132,15 +142,17 @@ impl ServerStat {
|
||||
}
|
||||
|
||||
// Error rate
|
||||
self.fail_rate = cerr as f64 / self.latency_queue.len() as f64;
|
||||
self.data.fail_rate = cerr as f64 / self.latency_queue.len() as f64;
|
||||
|
||||
self.data.latency_stdev = self.max_latency_stdev;
|
||||
self.data.latency_mean = self.max_server_rtt as f64;
|
||||
if !vlat.is_empty() {
|
||||
vlat.sort_unstable();
|
||||
|
||||
// Find median of latency
|
||||
let mid = vlat.len() / 2;
|
||||
|
||||
self.rtt = if vlat.len() % 2 == 0 {
|
||||
self.data.latency_median = if vlat.len() % 2 == 0 {
|
||||
(vlat[mid] + vlat[mid - 1]) / 2
|
||||
} else {
|
||||
vlat[mid]
|
||||
@@ -154,17 +166,23 @@ impl ServerStat {
|
||||
for s in &vlat {
|
||||
total_lat += *s;
|
||||
}
|
||||
self.latency_mean = total_lat as f64 / n;
|
||||
self.data.latency_mean = total_lat as f64 / n;
|
||||
let mut acc_diff = 0.0;
|
||||
for s in &vlat {
|
||||
let diff = *s as f64 - self.latency_mean;
|
||||
let diff = *s as f64 - self.data.latency_mean;
|
||||
acc_diff += diff * diff;
|
||||
}
|
||||
// Corrected Sample Standard Deviation
|
||||
self.latency_stdev = ((1.0 / (n - 1.0)) * acc_diff).sqrt();
|
||||
self.data.latency_stdev = ((1.0 / (n - 1.0)) * acc_diff).sqrt();
|
||||
} else {
|
||||
self.data.latency_mean = vlat[0] as f64;
|
||||
}
|
||||
}
|
||||
|
||||
self.score()
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &ServerStatData {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
3
xray-core/.github/workflows/docker.yml
vendored
3
xray-core/.github/workflows/docker.yml
vendored
@@ -1,6 +1,8 @@
|
||||
name: Build docker image
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
@@ -19,6 +21,7 @@ jobs:
|
||||
images: ghcr.io/${{ github.repository_owner }}/xray-core
|
||||
flavor: latest=true
|
||||
tags: |
|
||||
type=sha
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
|
@@ -58,6 +58,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
||||
}
|
||||
|
||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||
isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1")
|
||||
|
||||
var gotlsConfig *gotls.Config
|
||||
|
||||
@@ -88,7 +89,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
||||
var uploadTransport http.RoundTripper
|
||||
var downloadTransport http.RoundTripper
|
||||
|
||||
if tlsConfig != nil {
|
||||
if isH2 {
|
||||
downloadTransport = &http2.Transport{
|
||||
DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
|
||||
return dialContext(ctxInner)
|
||||
@@ -121,7 +122,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
||||
upload: &http.Client{
|
||||
Transport: uploadTransport,
|
||||
},
|
||||
isH2: tlsConfig != nil,
|
||||
isH2: isH2,
|
||||
uploadRawPool: &sync.Pool{},
|
||||
dialUploadConn: dialContext,
|
||||
}
|
||||
|
@@ -19,6 +19,8 @@ import (
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
v2tls "github.com/xtls/xray-core/transport/internet/tls"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
)
|
||||
|
||||
type requestHandler struct {
|
||||
@@ -268,16 +270,21 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
||||
}
|
||||
}
|
||||
|
||||
l.listener = listener
|
||||
|
||||
l.server = http.Server{
|
||||
Handler: &requestHandler{
|
||||
handler := &requestHandler{
|
||||
host: shSettings.Host,
|
||||
path: shSettings.GetNormalizedPath(),
|
||||
ln: l,
|
||||
sessions: sync.Map{},
|
||||
localAddr: localAddr,
|
||||
},
|
||||
}
|
||||
|
||||
// h2cHandler can handle both plaintext HTTP/1.1 and h2c
|
||||
h2cHandler := h2c.NewHandler(handler, &http2.Server{})
|
||||
|
||||
l.listener = listener
|
||||
|
||||
l.server = http.Server{
|
||||
Handler: h2cHandler,
|
||||
ReadHeaderTimeout: time.Second * 4,
|
||||
MaxHeaderBytes: 8192,
|
||||
}
|
||||
|
@@ -2,7 +2,10 @@ package splithttp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
gotls "crypto/tls"
|
||||
"fmt"
|
||||
gonet "net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -15,6 +18,7 @@ import (
|
||||
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
func Test_listenSHAndDial(t *testing.T) {
|
||||
@@ -152,3 +156,50 @@ func Test_listenSHAndDial_TLS(t *testing.T) {
|
||||
t.Error("end: ", end, " start: ", start)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_listenSHAndDial_H2C(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
return
|
||||
}
|
||||
|
||||
listenPort := tcp.PickPort()
|
||||
|
||||
streamSettings := &internet.MemoryStreamConfig{
|
||||
ProtocolName: "splithttp",
|
||||
ProtocolSettings: &Config{
|
||||
Path: "shs",
|
||||
},
|
||||
}
|
||||
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
||||
go func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
})
|
||||
common.Must(err)
|
||||
defer listen.Close()
|
||||
|
||||
client := http.Client{
|
||||
Transport: &http2.Transport{
|
||||
// So http2.Transport doesn't complain the URL scheme isn't 'https'
|
||||
AllowHTTP: true,
|
||||
// even with AllowHTTP, http2.Transport will attempt to establish
|
||||
// the connection using DialTLSContext. Disable TLS with custom
|
||||
// dial context.
|
||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (gonet.Conn, error) {
|
||||
var d gonet.Dialer
|
||||
return d.DialContext(ctx, network, addr)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := client.Get("http://" + net.LocalHostIP.String() + ":" + listenPort.String())
|
||||
common.Must(err)
|
||||
|
||||
if resp.StatusCode != 404 {
|
||||
t.Error("Expected 404 but got:", resp.StatusCode)
|
||||
}
|
||||
|
||||
if resp.ProtoMajor != 2 {
|
||||
t.Error("Expected h2 but got:", resp.ProtoMajor)
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,9 @@
|
||||
count += 1
|
||||
console.log("Prepare", url)
|
||||
var ws = new WebSocket(url)
|
||||
// arraybuffer is significantly faster in chrome than default
|
||||
// blob, tested with chrome 123
|
||||
ws.binaryType = "arraybuffer";
|
||||
var wss = undefined
|
||||
var first = true
|
||||
ws.onmessage = function (event) {
|
||||
@@ -23,6 +26,7 @@
|
||||
var arr = event.data.split(" ")
|
||||
console.log("Dial", arr[0], arr[1])
|
||||
wss = new WebSocket(arr[0], arr[1])
|
||||
wss.binaryType = "arraybuffer";
|
||||
var opened = false
|
||||
wss.onopen = function (event) {
|
||||
opened = true
|
||||
|
@@ -67,6 +67,7 @@ class TestCookies(unittest.TestCase):
|
||||
({'XDG_CURRENT_DESKTOP': 'GNOME'}, _LinuxDesktopEnvironment.GNOME),
|
||||
({'XDG_CURRENT_DESKTOP': 'GNOME:GNOME-Classic'}, _LinuxDesktopEnvironment.GNOME),
|
||||
({'XDG_CURRENT_DESKTOP': 'GNOME : GNOME-Classic'}, _LinuxDesktopEnvironment.GNOME),
|
||||
({'XDG_CURRENT_DESKTOP': 'ubuntu:GNOME'}, _LinuxDesktopEnvironment.GNOME),
|
||||
|
||||
({'XDG_CURRENT_DESKTOP': 'Unity', 'DESKTOP_SESSION': 'gnome-fallback'}, _LinuxDesktopEnvironment.GNOME),
|
||||
({'XDG_CURRENT_DESKTOP': 'KDE', 'KDE_SESSION_VERSION': '5'}, _LinuxDesktopEnvironment.KDE5),
|
||||
|
@@ -740,20 +740,19 @@ def _get_linux_desktop_environment(env, logger):
|
||||
xdg_current_desktop = env.get('XDG_CURRENT_DESKTOP', None)
|
||||
desktop_session = env.get('DESKTOP_SESSION', None)
|
||||
if xdg_current_desktop is not None:
|
||||
xdg_current_desktop = xdg_current_desktop.split(':')[0].strip()
|
||||
|
||||
if xdg_current_desktop == 'Unity':
|
||||
for part in map(str.strip, xdg_current_desktop.split(':')):
|
||||
if part == 'Unity':
|
||||
if desktop_session is not None and 'gnome-fallback' in desktop_session:
|
||||
return _LinuxDesktopEnvironment.GNOME
|
||||
else:
|
||||
return _LinuxDesktopEnvironment.UNITY
|
||||
elif xdg_current_desktop == 'Deepin':
|
||||
elif part == 'Deepin':
|
||||
return _LinuxDesktopEnvironment.DEEPIN
|
||||
elif xdg_current_desktop == 'GNOME':
|
||||
elif part == 'GNOME':
|
||||
return _LinuxDesktopEnvironment.GNOME
|
||||
elif xdg_current_desktop == 'X-Cinnamon':
|
||||
elif part == 'X-Cinnamon':
|
||||
return _LinuxDesktopEnvironment.CINNAMON
|
||||
elif xdg_current_desktop == 'KDE':
|
||||
elif part == 'KDE':
|
||||
kde_version = env.get('KDE_SESSION_VERSION', None)
|
||||
if kde_version == '5':
|
||||
return _LinuxDesktopEnvironment.KDE5
|
||||
@@ -764,15 +763,14 @@ def _get_linux_desktop_environment(env, logger):
|
||||
else:
|
||||
logger.info(f'unknown KDE version: "{kde_version}". Assuming KDE4')
|
||||
return _LinuxDesktopEnvironment.KDE4
|
||||
elif xdg_current_desktop == 'Pantheon':
|
||||
elif part == 'Pantheon':
|
||||
return _LinuxDesktopEnvironment.PANTHEON
|
||||
elif xdg_current_desktop == 'XFCE':
|
||||
elif part == 'XFCE':
|
||||
return _LinuxDesktopEnvironment.XFCE
|
||||
elif xdg_current_desktop == 'UKUI':
|
||||
elif part == 'UKUI':
|
||||
return _LinuxDesktopEnvironment.UKUI
|
||||
elif xdg_current_desktop == 'LXQt':
|
||||
elif part == 'LXQt':
|
||||
return _LinuxDesktopEnvironment.LXQT
|
||||
else:
|
||||
logger.info(f'XDG_CURRENT_DESKTOP is set to an unknown value: "{xdg_current_desktop}"')
|
||||
|
||||
elif desktop_session is not None:
|
||||
|
Reference in New Issue
Block a user