面向未知

路漫漫其修远兮

欢迎来到我的日志

(。・ω・)ノ゙

这里的内容主要和我的学习路线有关
当然也有 Archlinux 的内容
学习是永无止境的
学得越多就越会觉得自己无知
人类终究是有极限的
除非...

说笑了
总之
全員一層奮励努力せよ!

线程安全问题

SQLite3 在编译的时候可以通过添加定义来控制整个库的线程安全问题。

SQLITE_THREADSAFE 这个宏为 0 时,整个库是线程不安全的。

SQLITE_THREADSAFE 这个宏为 1 时,整个库是线程安全的。

SQLITE_THREADSAFE 这个宏为 2 时,整个库是线程安全的,但是同一个 sqlite3* db 不能被多个线程同时使用。

当编译的时候 SQLITE_THREADSAFE1 或者 2 时,可以通过 sqlite3_config() 这个接口来更改线程安全模式:

  • SQLITE_CONFIG_SINGLETHREAD 单线程
  • SQLITE_CONFIG_MULTITHREAD 多线程
  • SQLITE_CONFIG_SERIALIZED 串行化

面向对象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <functional>
#include <string>
#include <string_view>
#include <unordered_map>

class SQLite3 {
public:
using TableRow = std::unordered_map<std::string, std::string>;
using ExecCallback = std::function<bool(TableRow)>;

public:
SQLite3(const std::string_view& db_name);
~SQLite3();

int close();

void exec(const std::string_view& sql);
void exec(const std::string_view& sql, ExecCallback callback);

private:
std::string db_name_;
sqlite3* db_{nullptr};
};

我设置了两个成员变量,一个是数据库连接,另一个是打开的数据库名称。

1
2
std::string db_name_;
sqlite3* db_{nullptr};

数据库的开启

首先是类的构造函数,主要是打开数据库连接,并在出错时抛出异常信息。

1
2
3
4
5
6
SQLite3::SQLite3(const std::string_view& db_name) : db_name_(db_name) {
auto errcode = sqlite3_open(db_name.data(), &db_);
if (errcode != SQLITE_OK) {
// 抛出或处理异常信息
}
}

数据库的关闭

然后是析构和关闭数据库连接。

析构函数因为不能抛出异常,所以我使用了 sqlite3_close_v2 来关闭连接。

主动 close 则是使用 sqlite3_close 接口,并返回错误代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SQLite3::~SQLite3() {
if (db_) {
sqlite3_close_v2(db_);
db_ = nullptr;
}
}

int SQLite3::close() {
auto errcode = sqlite3_close(db_);
if (errcode == SQLITE_OK) {
db_ = nullptr;
}
return errcode;
}

执行 SQL 语句

无回调函数

无回调函数的操作,只需要执行 SQL 语句,并抛出或者处理执行失败的情况即可。

1
2
3
4
5
6
7
8
9
10
11
void SQLite3::exec(const std::string_view& sql) {
char* errstr = nullptr;
int errcode = sqlite3_exec(db_, sql.data(), nullptr, nullptr, &errstr);

if (errcode != SQLITE_OK) {
std::string msg = errstr ? errstr : "Unknown error";
sqlite3_free(errstr);
throw std::runtime_error(
std::format("Failed to execute SQL: {}\nError SQL: {}", msg, sql));
}
}

有回调函数

当执行一些带有结果的 SQL 语句时,就需要回调函数去处理结果。

我定义了两个类型用来封装 sqlite3_exec 中的回调函数。

ExecCallback 对象的返回值为 bool 类型,用来指示回调函数是否成功执行。

并且 ExecCallback 对象拥有一个参数 TableRow,这是一个哈希表,存储该行的每一个列数据的 name 和 value 的键值对。

1
2
using TableRow = std::unordered_map<std::string, std::string>;
using ExecCallback = std::function<bool(TableRow)>;

我通过 void* context 传递 ExecCallback 对象。

当回调函数触发时,填充 TableRow row 对象的数据,并以此为参数调用 ExecCallback 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void SQLite3::exec(const std::string_view& sql, ExecCallback callback) {
auto c_callback = [](void* context, int col_size, char** col_values,
char** col_names) -> int {
auto* cb = static_cast<ExecCallback*>(context);
TableRow row;
for (int i = 0; i < col_size; i++) {
std::string col_name = col_names[i];
std::string value = col_values[i] ? col_values[i] : "";
row[col_name] = value;
}
bool success = (*cb)(row);
return success ? 0 : 1;
};

char* errstr = nullptr;
int errcode = sqlite3_exec(db_, sql.data(), c_callback, &callback, &errstr);
if (errcode != SQLITE_OK) {
std::string msg = errstr ? errstr : "Unknown error";
sqlite3_free(errstr);
throw std::runtime_error(std::format(
"Failed to execute SQL with callback: {}\nError SQL: {}", msg, sql));
}
}

测试一下这个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main() {
SQLite3 db("test.db");

try {
db.exec("create table if not exists users (id integer primary key autoincrement, name text);");
db.exec("insert into users (id, name) values (1, 'Alice');");
db.exec("insert into users (id, name) values (2, 'Bob');");
db.exec("insert into users (name) values ('Charlie');");
db.exec("select * from users;", [](SQLite3::TableRow row) {
std::cout << "User ID: " << row["id"] << ", Name: " << row["name"] << '\n';
return true; // Continue iteration
});
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
}

return 0;
}

运行上述 main 函数后,获得输出如下:

1
2
3
User ID: 1, Name: Alice
User ID: 2, Name: Bob
User ID: 3, Name: Charlie

再次运行该代码,输出如下:

1
2
Failed to execute SQL: UNIQUE constraint failed: users.id
Error SQL: insert into users (id, name) values (1, 'Alice');

测试完毕,这样一个初步的面向对象封装就完成了。

介绍

最基本的使用 sqlite3 的 c api 还是挺简单的。只需要记住三个接口即可。

  • 打开数据库:sqlite3_open()

  • 关闭数据库:sqlite3_close()

  • 执行 SQL 语句:sqlite3_exec()

开启数据库

先来看看如何开启数据库,我们需要提供两个参数,数据库名称和一个 sqlite3 指针的指针。

1
2
3
4
int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);

具体的使用基本如下:

1
2
3
char* db_name = "test.db";
sqlite3* db;
sqlite3_open(db_name, &db);

SQLite3 还提供了可以更加精细的控制打开数据库方式的接口:sqlite3_open_v2()

1
2
3
4
5
6
int sqlite3_open_v2(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb, /* OUT: SQLite db handle */
int flags, /* Flags */
const char *zVfs /* Name of VFS module to use */
);

可以看到多了两个参数,可以控制数据库只读或其他更精细的操作。sqlite3_open() 可以看作是下述特定参数 sqlite3_open_v2()

1
2
3
char* db_name = "test.db";
sqlite3* db;
sqlite3_open_v2(db_name, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);

具体的 sqlite3_open_v2() 使用可以参考官方文档 Opening A New Database Connection.

关闭数据库

关闭数据库只需要把 sqlite3* db 作为参数传入即可。使用十分简单。

就像 open 接口有多个版本一样,close 接口也有多个版本。

1
2
int sqlite3_close(sqlite3*);
int sqlite3_close_v2(sqlite3*);

主要区别是,当 db 还有某些未完成的任务时,两个接口会有不同的行为。

sqlite3_close 会返回 SQLITE_BUSY

sqlite3_close_v2 会返回 SQLITE_OK。但不会立即释放数据库连接,而是将数据库连接标记为不可用的“僵尸”状态,并安排在所有预处理语句完成、所有 BLOB 句柄关闭以及所有备份操作结束后自动释放数据库连接。

执行 SQL 语句

1
2
3
4
5
6
7
int sqlite3_exec(
sqlite3* db, /* 数据库连接 */
const char* sql, /* 要执行的 SQL 语句 */
int (*callback)(void*, int, char**, char**), /* 回调函数 */
void* data, /* 传递给回调函数的参数 */
char** errmsg /* 错误信息指针 */
);

db 是打开的数据库连接。

sql 是要执行的 SQL 语句。

callback 是执行完 SQL 语句后进行的回调函数。(可选参数,不需要可传递空指针)

data 是传给 callback 的第一个参数,可用于传递上下文。(可选参数,不需要可传递空指针)

errmsg 会返回相应的错误信息,如果无错误则被设为空指针。(可选参数,不需要可传递空指针)

回调函数

1
int callback(void* context, int col_size, char** col_values, char** col_names);

只要理解了这个回调函数会 对结果的每一行数据执行一次,我们就可以轻松的理解这个回调的后面三个和列相关的参数。

  • context:从 sqlite3_exec() 传递过来的用户数据
  • col_size:结果行的列数
  • col_values:列值的字符串数组
  • col_names:列名的字符串数组

返回值为 0 时,继续查询。

返回值为 1 时,终止查询。

虽然已经用了多年的 git, 但是至今都没怎么好好的总结一下 ignore 规则。今天心血来潮,重新仔细的总结一下 gitignore 到底怎么好好的去写,并记录一下 gitignore 的白名单写法。

基础规则

首先是 gitignore 的基础规则。基础规则的总结,我参考了这篇文章 .gitignore File – How to Ignore Files and Folders in Git. 不过参考文章中有些操作在我所使用的 Archlinux 中无法复现, 所以下面的总结以我自己的实际操作为基础。

如何在 Git 中忽略一个文件或文件夹

如何忽略一个文件或目录

1
2
3
4
5
6
7
8
9
10
/text.txt           # 忽略根目录下 text.txt 文件
/test/text.txt # 忽略根目录下 test 目录中的 text.txt 文件

text.txt # 忽略所有名为 text.txt 的文件和目录
test/ # 忽略所有名为 test 的目录
test # 忽略所有名为 test 的文件和目录

*test* # 忽略所有名字带有 test 的文件和目录
img* # 忽略所有名字开头是 img 的文件和目录
*.md # 忽略所有名字结尾是 .md 的文件和目录

如何不忽略一个文件或目录

加入我们忽略了所有 md 文件, 但唯独不忽略 README.md 的情况, 如下所示.

1
2
*.md                # 忽略所有名字结尾是 .md 的文件和目录
!README.md # 不忽略所有名字是 README.md 的文件和目录

无法排除已经被忽略的目录内的一个文件

1
2
test/               # 忽略所有名字带有 test 的目录
!test/example.md # 试图在被忽略的目录内排除一个文件是行不通的

如何忽略以前提交的文件

假如你不小心把 .build 文件提交了, 现在想去除这个文件. 过程如下所示.

1
2
3
4
5
6
7
8
9
10
11

# 给 .gitignore 添加 .build 文件
echo ".build" >> .gitignore

# 从 git 版本库中去除该文件
git rm --cached .build

# 暂存新的 .gitignore 并提交
git add .gitignore
git commit -m " 更新了 .gitignore 文件 "

白名单模式

一般来说 gitignore 是使用黑名单模式,也就是写在 ignore 中的项目会被忽略,但是有些时候使用黑名单模式会有一些疏漏,当项目逐渐庞大起来,难免会有一些遗漏,将一些临时的文件和不希望添加入 git 管理的文件加入 git 中。这个时候白名单模式的 gitignore 能够实现更加精细的管理。

假设我们有以下结构的 C++ 项目需要管理:

1
2
3
4
5
6
7
your-project-dir
├── .gitignore # gitignore 文件
├── build/ # 构建项目的临时目录
├── docs/ # 放置各种文档
├── include/ # 放置 .h 头文件
├── README.md # 自述文档
└── src/ # 放置 .cpp 代码文件

首先忽略全部文件,并取消忽略目录

白名单模式的第一步。这里取消忽略目录,是因为被忽略目录下的文件是必定被忽略的,为了能够指定不忽略的文件,需要取消忽略目录。

1
2
*
!*/

忽略不想要的目录 (可选)

因为 build 目录完全是一个临时目录,我不想提交里面的任何文件,所以可以直接忽略掉。如果这个目录里有某些特定的文件需要提交,则不应该忽略这个目录。

之所以这是一个可选的操作,是因为在上一步忽略了所有文件之后,所有的目录内都没有需要提交的文件,在 git 中这样的“空目录”不会被提交。

1
/build/

取消忽略特定的文件

有些文件我们希望必须提交的,我们可以直接取消忽略这些文件。

1
2
!.gitignore
!README.md

这样之后,任何目录下的 .gitignore 和 README.md 文件都会被提交上去。

取消忽略特定目录下的全部文件

我希望提交 docs 目录下的所有文件,包括其子目录下的所有文件。通过下面这种写法,可以取消忽略 docs 目录下的所有文件,并递归的忽略其子目录下的所有文件。

1
!/docs/**/*

取消忽略特定目录下的特定文件

有的目录我只希望提交特定类型的文件,例如 src 目录下的 .cpp 代码文件和 include 目录下的 .h 头文件。

下面这种写法取消忽略了对应目录下所有特定后缀的文件,并递归的忽略其子目录下所有特定后缀的文件。

1
2
!/include/be/**/*.h
!/src/be/**/*.cpp

总结

最后的白名单 gitignore 文件看上去是这样的:

1
2
3
4
5
6
7
8
9
10
11
*
!*/

/build/

!.gitignore
!README.md

!/docs/**/*
!/include/be/**/*.h
!/src/be/**/*.cpp

在比较复杂的项目中,或者需要精细控制提交的文件时,白名单模式的 ignore 文件是真的很好用。

因为在 Archlinux 上使用 Clash Verge Rev 经常会出现一些奇怪的问题,比如说 service 无法运行,界面卡顿加载不出来,甚至会直接卡死。所以很早之前我就有了直接跑纯内核的念头。

故现在我终于开始尝试直接跑纯 Mihomo 内核,目前是已经完成了所有的配置,普通模式和 Tun 模式都可以正常使用。现将过程记录如下。

由于我是在 Archlinux 上进行的安装,我可以确保在 Archlinux 上以下操作全部经过检验。

安装 mihomo 核心及其依赖

实际上包管理器会自动处理依赖,所以不用担心。

1
yay -S mihomo

安装 metacubexd 界面

为了能够在 web 端方便的临时调整配置,以及可视化连接及流量,我推荐安装 metacubexd 这个界面。

如果你配置了 archlinuxcn 源,可以直接 pacman 安装预编译版本。

1
sudo pacman -S metacubexd-bin 

也可以在 AUR 中安装,metacubexd-bin 是预编译版本,metacubexd 是源码需要本机上编译。我比较推荐预编译版本。

1
yay -S metacubexd-bin

启动守护进程

AUR 中提供的 mihomo 软件包里面自带了 systemd 守护进程 mihomo.service,我们需要手动 enable 让 mihomo 内核可以开机自启。

1
sudo systemctl enable mihomo.service

然后启动 mihomo 内核进行下一步工作。

1
sudo systemctl start mihomo.service

配置文件

通过查看守护进程文件 /etc/systemd/system/multi-user.target.wants/mihomo.service 可以看到我的 mihomo 的工作目录为 /etc/mihomo, mihomo 内核的配置文件在 /etc/mihomo/config.yaml 中,我们需要修改这个文件来配置我们的 mihomo 内核。

config.yaml 这个文件就是最主要的配置文件,只不过我们需要修改一些内容。来适合我的使用习惯。

首先把 config.yaml 中的所有内容清空,然后把机场给出的配置文件整个的复制过来。

接下来就是修改修改了。

修改端口

因为 Clash Verge Rev 的默认 mixed-port 是 7897, 我的很多软件的代理都设置为了这个端口,所以我也依旧修改 mixed-port 为 7897.

并且我不喜欢开启局域网访问,所以我将 allow-lan 修改为 false.

现在,我的 config.yaml 大概是这个样子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mixed-port: 7897
port: 7890
socks-port: 7891
allow-lan: false
mode: Rule
log-level: info
external-controller: 127.0.0.1:9090

proxies:
# ...
proxy-groups:
# ...
rules:
# ...

我们需要重启 mihomo 内核,让配置生效。

1
sudo systemctl restart mihomo.service

这个时候,mihomo 就已经可以正常的使用了。

但是 Tun 模式目前还用不了,接下来我们继续设置。

配置 metacubexd 界面

1
2
# 前端控制器界面 (archlinuxcn 源中的 metacubexd)
external-ui: /usr/share/metacubexd

只需要在 config.yaml 中加上这一行就可以,archlinuxcn 源中的 metacubexd-bin 的目录是在 /usr/share/metacubexd 中。AUR 中的我没有尝试过,大家下载下来之后可以使用 pacman -Ql metacubexd-bin 查看一下具体的路径。

然后我们需要重启 mihomo 内核,让配置生效。

1
sudo systemctl restart mihomo.service

然然后我们就可以在浏览器中通过配置文件中 external-controller: 127.0.0.1:9090 的地址来访问 metacubexd 界面了。

记得加上 /ui 这个后缀,否则进不去 metacubexd 的界面。

1
http://127.0.0.1:9090/ui

那么现在我们的配置就都完成的差不多了, config.yaml 大概是这个样子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mixed-port: 7897
port: 7890
socks-port: 7891
allow-lan: false
mode: Rule
log-level: info
external-controller: 127.0.0.1:9090

# 前端控制器界面 (archlinuxcn 源中的 metacubexd)
external-ui: /usr/share/metacubexd

proxies:
# ...
proxy-groups:
# ...
rules:
# ...

高级配置

DNS 配置

根据我的测试,不配置 dns 就无法使用 Tun 模式,所以我们这里直接给出我已经配置好的配置,大家可以直接复制粘贴到 config.yaml 中。

具体的选项起到什么作用其实还是很直接的,有想自己自定义的可以查看官方 Wiki 上的配置选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# DNS
dns:
enable: true
ipv6: true
listen: 0.0.0.0:53
enhanced-mode: fake-ip # redir-host
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- geosite:fakeip-filter
default-nameserver:
- system
nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
- https://dns.google/dns-query
- https://cloudflare-dns.com/dns-query
proxy-server-nameserver:
- https://doh.pub/dns-query

Tun 模式配置

Tun 模式的具体配置我没有在 config.yaml 中进行配置,而是直接使用的默认配置,在 web 界面上直接开启 Tun 模式即可使用。

其他配置

我还从 Wiki 上找了几个比较有用的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# web 界面的登录密码
# (因为我这里监听的 127.0.0.1 只有本机能够访问, 暂不设置)
# secret: ""

#【Mihomo 专属】TCP 连接并发,如果域名解析结果对应多个 IP,
# 并发所有 IP,选择握手最快的 IP 进行连接
tcp-concurrent: true

# 配置缓存 (代理中选择的节点和 fake-ip 缓存)
profile:
store-selected: true
store-fake-ip: true

#【Mihomo 专属】使用 geoip.dat 数据库(默认:false 使用 mmdb 数据库)
geodata-mode: true
geo-auto-update: true # 自动更新
geo-update-interval: 24 # 更新间隔 24 小时
geox-url: # 下载 geoip.dat 数据库的地址
geoip: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/geoip-all.dat"
geosite: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/geosite-all.dat"
mmdb: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/Country-all.mmdb"
asn: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/Country-ASN-all.mmdb"

最终配置

最后的配置大概长这个样子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
mixed-port: 7897
port: 7890
socks-port: 7891
allow-lan: false
mode: Rule
log-level: info
external-controller: 127.0.0.1:9090

# 前端控制器界面 (archlinuxcn 源中的 metacubexd)
external-ui: /usr/share/metacubexd

# web 界面的登录密码
# (因为我这里监听的 127.0.0.1 只有本机能够访问, 暂不设置)
# secret: ""

#【Mihomo 专属】TCP 连接并发,如果域名解析结果对应多个 IP,
# 并发所有 IP,选择握手最快的 IP 进行连接
tcp-concurrent: true

# 配置缓存 (代理中选择的节点和 fake-ip 缓存)
profile:
store-selected: true
store-fake-ip: true

#【Mihomo 专属】使用 geoip.dat 数据库(默认:false 使用 mmdb 数据库)
geodata-mode: true
geo-auto-update: true # 自动更新
geo-update-interval: 24 # 更新间隔 24 小时
geox-url: # 下载 geoip.dat 数据库的地址
geoip: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/geoip-all.dat"
geosite: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/geosite-all.dat"
mmdb: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/Country-all.mmdb"
asn: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/Country-ASN-all.mmdb"


# DNS
dns:
enable: true
ipv6: true
listen: 0.0.0.0:53
enhanced-mode: fake-ip # redir-host
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- geosite:fakeip-filter
default-nameserver:
- system
nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
- https://dns.google/dns-query
- https://cloudflare-dns.com/dns-query
proxy-server-nameserver:
- https://doh.pub/dns-query
# nameserver-policy:
# "geosite:cn,private":
# - https://doh.pub/dns-query
# - https://dns.alidns.com/dns-query
# "geosite:ads":
# - rcode://success
# "geosite:!cn,!private,!ads":
# - https://dns.google/dns-query
# - https://cloudflare-dns.com/dns-query


proxies:
# ...
proxy-groups:
# ...
rules:
# ...

在 dns 配置中,可以配置 nameserver-policy 来实现不同的域名使用不同的 DNS 服务器。可以非常有效的解决 dns 泄漏问题,但是不知道为什么,我用起来网址解析的非常慢,所以我没有使用这个配置,而是注释掉了。

现在我的配置已经可以正常使用了,无论是走端口代理,还是直接 Tun 模式接管一切都没有任何问题了。

手写脚本实现一键更新订阅

首先把需要附加的配置写在 append.yaml 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# append.yaml

mixed-port: 7897
port: 7890
socks-port: 7891
allow-lan: false
mode: Rule
log-level: info
external-controller: 127.0.0.1:9090

# 前端控制器界面 (archlinuxcn 源中的 metacubexd)
external-ui: /usr/share/metacubexd

# web 界面的登录密码
# (因为我这里监听的 127.0.0.1 只有本机能够访问, 暂不设置)
# secret: ""

#【Mihomo 专属】TCP 连接并发,如果域名解析结果对应多个 IP,
# 并发所有 IP,选择握手最快的 IP 进行连接
tcp-concurrent: true

# 配置缓存 (代理中选择的节点和 fake-ip 缓存)
profile:
store-selected: true
store-fake-ip: true

#【Mihomo 专属】使用 geoip.dat 数据库(默认:false 使用 mmdb 数据库)
geodata-mode: true
geo-auto-update: true # 自动更新
geo-update-interval: 24 # 更新间隔 24 小时
geox-url: # 下载 geoip.dat 数据库的地址
geoip: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/geoip-all.dat"
geosite: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/geosite-all.dat"
mmdb: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/Country-all.mmdb"
asn: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/Country-ASN-all.mmdb"


# DNS
dns:
enable: true
ipv6: true
listen: 0.0.0.0:53
enhanced-mode: fake-ip # redir-host
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- geosite:fakeip-filter
default-nameserver:
- system
nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
- https://dns.google/dns-query
- https://cloudflare-dns.com/dns-query
proxy-server-nameserver:
- https://doh.pub/dns-query
# nameserver-policy:
# "geosite:cn,private":
# - https://doh.pub/dns-query
# - https://dns.alidns.com/dns-query
# "geosite:ads":
# - rcode://success
# "geosite:!cn,!private,!ads":
# - https://dns.google/dns-query
# - https://cloudflare-dns.com/dns-query

因为我的订阅中已经有了一些配置,这些配置会和 append.yaml 中的配置冲突,所以通过 sed 进行删除处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/bash

# 获取机场配置,去除前 6 行,保存到 tmp.yaml
curl -L '< 你的订阅地址 >' | sed '1,6d' > tmp.yaml

# 附加 append.yaml 到 tmp.yaml
cat append.yaml >> tmp.yaml

# 原来配置的备份到 config.yaml.bak
cp config.yaml config.yaml.bak

# 用 tmp.yaml 替换 config.yaml
cat tmp.yaml > config.yaml

之后使用 sudo systemctl restart mihomo 重启一下 mihomo 服务,或者在 web 界面中重新读取一下配置即可完成更新。

其他

mmdb

  1. 第一次启动的时候 mihomo 会下载 mmdb,可能会因为网络问题下载失败,从而没法成功运行。可以先设置好 geox-url 的 cdn 地址,然后重启即可下载成功。
1
2
3
4
5
geox-url: # 下载 geoip.dat 数据库的地址
geoip: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/geoip-all.dat"
geosite: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/geosite-all.dat"
mmdb: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/Country-all.mmdb"
asn: "https://cdn.jsdelivr.net/gh/DustinWin/ruleset_geodata@mihomo/Country-ASN-all.mmdb"

ssh in Tun mode

  1. Tun 模式下 ssh 之类的软件都无法使用,因为连接的 ip 都会变成 198.18.0.1 这个地址。经过评论区中大佬的提醒,我找到了一个解决方法。在 dns 配置的 fake-ip-filter: 这一项中,把想要 ssh 连接的域名和 ip 填上去就行。否则无论是通过域名进行连接还是直接通过 ip 连接,都会连接不上。

proxy-providers

  1. 经提醒,纯内核也支持配置订阅连接。经过一番折腾过后,我发现虽然支持订阅连接,但是 proxy-providers 似乎只导入其中的 proxies,但是对我来说 proxy-groups 和 rules 也是非常需要的,故暂不使用此功能。

external-ui

  1. 新版的 mihomo 对 web 界面的存放目录有了要求,可能需要将 metacubexd 的存放目录放到符合要求的位置。
0%