ONE NOTE

Rage, rage against the dying of the light.

https代理工作流程

  • client发送一个http connect 到 proxy , 告诉自己要访问的域名与端口
  • proxy server 进行dns解析,创建与目标的通道,随后告诉client连接成功Connection Established,也就是隧道(tunnel)创建完成。
  • 随后客户端通过这个隧道进行与目标的tls握手,进行加密通讯。

有几个重点

  • proxy 进行dns解析
  • 连接是加密的,对于proxy server,它也没办法获取通讯内容

http代理工作流程

相当于将所有域名绑定到代理服务器地址,由代理获取response后返回client

代码示例

  • 使用python实现一个http and https 代理。
  • 使用多线程来避免阻塞

参考见文末

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import select
import socket
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
from urllib.parse import urlparse


class RequestHandler(BaseHTTPRequestHandler):

def _recv_data_from_remote(self, sock):
data = b''
while True:
recv_data = sock.recv(4096)
if not recv_data:
break
data += recv_data
sock.close()
return data

def send_data(self, sock, data):
# print(data)
bytes_sent = 0
while True:
r = sock.send(data[bytes_sent:])
if r < 0:
return r
bytes_sent += r
if bytes_sent == len(data):
return bytes_sent

def handle_tcp(self, sock, remote):
# 处理 client socket 和 remote socket 的数据流
try:
fdset = [sock, remote]
while True:
# 用 IO 多路复用 select 监听套接字是否有数据流
r, w, e = select.select(fdset, [], [])
if sock in r:
try:
data = sock.recv(4096)
if len(data) <= 0:
break
result = self.send_data(remote, data)
if result < len(data):
raise Exception('failed to send all data')
except:
pass

if remote in r:
try:
data = remote.recv(4096)
if len(data) <= 0:
break
result = self.send_data(sock, data)
if result < len(data):
raise Exception('failed to send all data')
except:
pass
except Exception as e:
raise (e)
finally:
sock.close()
remote.close()

def do_CONNECT(self):
self.log_message(f"CONNECT {self.path}")

uri = self.path.split(":")
host, port = uri[0], int(uri[1])
host_ip = socket.gethostbyname(host)

remote_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote_sock.connect((host_ip, port))
# 告诉客户端 CONNECT 成功
self.wfile.write(
"{protocol_version} 200 Connection Established\r\n\r\n".format(protocol_version=self.protocol_version
).encode()
)

# 转发请求
self.handle_tcp(self.connection, remote_sock)

# http proxy

def do_http(self, method: str):
self.log_message(f"{method} {self.path}")
uri = urlparse(self.path)
host, path = uri.hostname, uri.path
host_ip = socket.gethostbyname(host)
port = uri.port or 80

# 为了简单起见,Connection 都为 close, 也就不需要 Proxy-Connection 判断了
del self.headers['Proxy-Connection']
self.headers['Connection'] = 'close'

# 构造新的 http 请求
send_data = "{method} {path} {protocol_version}\r\n".format(
method=method, path=path, protocol_version=self.protocol_version
)
headers = ''
for key, value in self.headers.items():
headers += "{key}: {value}\r\n".format(key=key, value=value)
headers += '\r\n'
send_data += headers

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host_ip, port))
# 发送请求到目标地址
sock.sendall(send_data.encode())
try:
data = self._recv_data_from_remote(sock)
self.wfile.write(data)
except:
self.wfile.write(b"EOF")

def do_GET(self):
self.do_http("GET")

def do_HEAD(self):
self.do_http("HEAD")

def do_POST(self):
self.do_http("POST")


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
pass


if __name__ == '__main__':
server = ThreadedHTTPServer(('', 8080), RequestHandler)
server.serve_forever()

参考

socket-example

此、彼、夫

これ 此,距离我比较近
それ 彼,距离你比较近
あれ 夫,都比较远
for example:
これわ地図ですか。
(is this map?)

什么人、什么东西

誰(だれ) who 礼貌的说法是どなた
それわ what is it

この その あの 后面必须接名词

この スマートフォン(smart phone)は 李さんのです。

(This smart phone belongs to Mr.Li)

どれ どの

前者后面不接名词,后者必须接名词。

这里、那里、那~里

ここ 这里

そこ 那里

あそこ 那~里

语法

陈述句
名 は 名 です

疑问句,某物在哪里
名 は どこですか

xx也xx
名 も 名 です

存在句

部屋に 机が あります
机に パソコン あぃます
xxに xxが あります

或者动词在前

いすは 部屋に あります
xxは xxに あります

あります只能用在无生命物体上,有生命用います

Now a simpler blog !

Architecture: hexo and githubpage and coludfare cdn

image function test

honkai impact 3 rd

under the the treeeee

markdown documents

doc

This row just used for test if i can post articals by using windows.

由于买不起服务器服务器打理起来太麻烦而且不方便迁移,这个博客即将遭到弃用。

由来

当初出于联系部署服务器的打算,采用了docker+wordpress的方式部署博客,但是我逐渐发现这种方式不适合我:

  • 迁移困难:基本绑定了wordpress这一个框架,想要转移到其他博客十分麻烦

  • 需要服务器:而且想要访问得顺畅,需要额外资金扩展贷款

  • 不符合我的博客定位:本人的博客应该更像一个随手记的记事本,而不是一个可以交互的网站,所以静态博客框架,比如hexo更适合我使用,网站本来也没其他人看。静态页面还可以用免费托管,有利于我提到的上面一点——加快网站访问速度。

  • 编辑器我更喜欢vscode,同时源文件保存为md格式更有安全感。

打算

关于博客,以下有几个选择:

至于已有的文章如何迁移,这个倒不是当务之急。

打算将文章还原为markdown,这点似乎没有找到现成的轮子,也许会自己写一个。(找到的效果不理想,不能把图片也导出来,同时格式有一些错位。)。

edit: 旧博客已经迁移完成,新家:https://hinak0.github.io

五十音

row平仮名「ひらがな」片仮名「カタカナ」
aあいうえおアイウエオ
kかきくけこカキクケコ
sさしすせそサシスセソ
tたちつてとタチツテト
nなにぬねのナニヌネノ
hはひふへほハヒフへホ
mまみむめもマミムメモ
yやーゆーよヤーユーヨ
rらりるれろラリルレロ
wわーーーをワーーーヲ
n

半浊音和浊音

rowalphabet
k-gがぎぐげご
s-zざじずぜぞ
t-dだぢづでど
h-bばびぶべぼ
h-pぱぴぷぺぽ

拗音

将i列的7个清音(不包含い)和5个浊音(じぢ同音,音鸡)共12*3个(论读音是11*3个)

rowalphabet
kiきゃ きゅ きょ
shiしゃ しゅ しょ
thiちゃ ちゅ ちょ
niにゃ にゅ にょ
hiひゃ ひゅ ひょ
miみゃ みゅ みょ
riりゃ りゅ りょ
giぎゃ ぎゅ ぎょ
jiじゃ じゅ じょ
biびゃ びゅ びょ
piぴゃ ぴゅ ぴょ

其他发音规则

  • 促音 「っ」是用来表示停顿的符号,它比「つ」小一号,不发音,但是占一拍

  • 长音

1
2
3
4
5
6
7
a+a
i+i
u+u
e+e
o+o
e+i
o+u

声调

规律:

  • 第一个音和第二个调子不会相同
  • 不会出现高低高的音调
  • 第几个音降下去,就称为几型

examples:

  • 0 型 庭「にわ」\ 壁 \ 国「くに」 \ 椅子「いす」
  • 1 型 猫 \ 母 \ 傘 \ 外「そと」
  • 2 型 花屋「はなや」 \ います \ 寝ます「ねます」 \ 蕎麦屋「そばや」
  • 3 型 ください \ 食べます \ 聞きます「ききます」 \ 読みます「よみます」

为什么udp不能用Redirect

redirect的原理

在nat表修改数据包的目标地址,同时netfilter有 SO_ORIGINAL_DST 选项可以支持拿到原目的地址,netfilter 会将包的原目的地址放在 socket 的 SO_ORIGINAL_DST 属性里.代理客户端依次向原目标发起请求.

为什么udp不能拿到修改前的地址

udp是一种无连接协议,所以没有socket来拿到修改前的地址,改了就改了.

udp怎样实现代理

tproxy是在不修改数据包的前提上,将数据包转发到本地socket,但是这样不够,因为没有改数据包的目标地址,数据包就直接走forword了,所以需要增加一个路由表把所有数据转入本地:

ip route add local 0.0.0.0/0 dev lo table 100

然后增加一个rule,将特定的数据包转入这个路由表:

ip rule add fwmark 1 table 100

最后,该需要走代理的数据包加上这个标记,就可以:

iptables -t mangle -A CLASH_UDP -p udp -j TPROXY --on-port ${tproxy_port} --tproxy-mark 1

如果想代理本机udp,就给本地output增加mark 1:

iptables -t mangle -I OUTPUT -p udp --dport 53 -j MARK --set-mark 1

这样niptables会把本机数据重新路由到prerouting,可以代理本机.

配置规则

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
71
#!/bin/bash

# error break
set -e
# line-number for debug
set -x

lan_ipaddr="192.168.1.5" # 局域网路由器IP地址
dns_port="5354" # DNS转发服务端口
redir_port="7892" # 透明代理转发端口
tproxy_port="7893" # tproxy

# ip rule add fwmark 1 table 100
# ip route add local 0.0.0.0/0 dev lo table 100
clearAllRules() {
# 这里是清除所有规则回到默认状态
iptables-save | awk '/^[*]/ { print $1 } /^:[A-Z]+ [^-]/ { print $1 " ACCEPT" ; } /COMMIT/ { print $0; }' | iptables-restore
}
setProxy() {
# 转发DNS请求到端口 dns_port 解析
iptables -t nat -N CLASH_DNS
iptables -t nat -F CLASH_DNS
iptables -t nat -A CLASH_DNS -p udp -j REDIRECT --to-ports ${dns_port}
iptables -t nat -A PREROUTING -p udp --dport 53 -j CLASH_DNS

# tcp代理 redirect 实现
iptables -t nat -N CLASH_TCP
iptables -t nat -F CLASH_TCP
iptables -t nat -A CLASH_TCP -d 0.0.0.0/8 -j RETURN
iptables -t nat -A CLASH_TCP -d 10.0.0.0/8 -j RETURN
iptables -t nat -A CLASH_TCP -d 127.0.0.0/8 -j RETURN
iptables -t nat -A CLASH_TCP -d 169.254.0.0/16 -j RETURN
iptables -t nat -A CLASH_TCP -d 172.16.0.0/12 -j RETURN
iptables -t nat -A CLASH_TCP -d 192.168.0.0/16 -j RETURN
iptables -t nat -A CLASH_TCP -d 224.0.0.0/4 -j RETURN
iptables -t nat -A CLASH_TCP -d 240.0.0.0/4 -j RETURN
iptables -t nat -A CLASH_TCP -p tcp -j REDIRECT --to-ports ${redir_port}
iptables -t nat -A PREROUTING -p tcp -j CLASH_TCP

# udp代理 tproxy实现
iptables -t mangle -N CLASH_UDP
iptables -t mangle -F CLASH_UDP
iptables -t mangle -A CLASH_UDP -d 0.0.0.0/8 -j RETURN
iptables -t mangle -A CLASH_UDP -d 10.0.0.0/8 -j RETURN
iptables -t mangle -A CLASH_UDP -d 127.0.0.0/8 -j RETURN
iptables -t mangle -A CLASH_UDP -d 169.254.0.0/16 -j RETURN
iptables -t mangle -A CLASH_UDP -d 172.16.0.0/12 -j RETURN
iptables -t mangle -A CLASH_UDP -d 192.168.0.0/16 -j RETURN
iptables -t mangle -A CLASH_UDP -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A CLASH_UDP -d 240.0.0.0/4 -j RETURN
iptables -t mangle -A CLASH_UDP -p udp --dport 53 -j RETURN
iptables -t mangle -A CLASH_UDP -p udp -j TPROXY --on-port ${tproxy_port} --tproxy-mark 1
iptables -t mangle -A PREROUTING -p udp -j CLASH_UDP # 应用规则
}

case "$1" in
clear)
clearAllRules
echo "delete rules successfully"
;;
set)
clearAllRules
setProxy
echo "set rules successfully"
;;
*)
echo "usage: $0 clear|set"
exit 0
;;
esac
exit

参考

透明代理入门 | Project X

透明代理 UDP 为什么要用 TProxy?

Clash旁路网关设置

透明代理(TPROXY)

路由器必须有的3个功能

  • dns

  • dhcp

  • nat

怎么做

  • 使用dnsmasq实现dhcp和dns

  • 使用iptables实现nat转发

  • 使用clash+iptables实现透明代理

dnsmasq配置

首先关闭系统的dns-resolve服务

1
2
3
systemctl stop systemd-resolved
systemctl disable systemd-resolved
systemctl mask systemd-resolved

然后配置dnsmasq服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
port=53
interface=enp3s0

dhcp-option=option:router,192.168.1.1
dhcp-option=option:dns-server,192.168.1.1
dhcp-range=192.168.1.129,192.168.1.192,255.255.255.0,24h

dhcp-option=tag:proxy,option:dns-server,192.168.1.2
dhcp-option=tag:proxy,option:router,192.168.1.2
dhcp-range=tag:proxy,192.168.1.65,192.168.1.127,255.255.255.0,24h

server=223.5.5.5
server=119.29.29.29

no-resolv
domain=lan
local=/lan/

# log-queries
# log-facility=/var/log/dnsmasq.log

dhcp-host=<mac>,<ip>,<domain name>,<tag>
...

iptables规则

1
2
# 实现nat
iptables -t nat -A POSTROUTING -o enp4s0 -j MASQUERADE

这时候下游主机就可以正常上网了

clash配置

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
port: ****
redir-port: ****
tproxy-port: ****
allow-lan: true
mode: rule
log-level: error
ipv6: false
external-controller: 127.0.0.1
external-ui: /var/www/html/
secret: secret
profile:
store-selected: false
store-fake-ip: false
dns:
enable: true
listen: :**
ipv6: false
default-nameserver:
- 127.0.0.1
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
use-hosts: false
fake-ip-filter:
- *.lan
nameserver:
- https://dns.alidns.com/dns-query
fallback:
- https://doh.dns.sb/dns-query
- https://dns.cloudflare.com/dns-query
fallback-filter:
geoip: true
geoip-code: CN
nameserver-policy:
+.lan: 127.0.0.1

iptables选择性转发流量到clash

iptables配置要点:

  • 向wan的流量redir

  • 向lan的流量accept

  • 向server的dns redir 到 tproxy port

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/bin/bash
#!/bin/bash

dns_port=0
redir_port=0
tproxy_port=0

udp_enable=1
nat_enable=0
slient_wan=0

inbound=enp3s0
outbound=enp4s0

config_dir="/var/local/clash"
converter_dir="/home/chlen/converter"
LEASE_FILE="/var/lib/misc/dnsmasq.leases"

# error break
# set -e
# line-number for debug
# set -x
init() {
# route rules
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100

# not global addresses
ipset create local hash:net
ipset add local 0.0.0.0/8
ipset add local 10.0.0.0/8
ipset add local 100.64.0.0/10
ipset add local 127.0.0.0/8
ipset add local 169.254.0.0/16
ipset add local 172.16.0.0/12
ipset add local 192.168.0.0/16
ipset add local 224.0.0.0/4
ipset add local 240.0.0.0/4

setRules
}

clearAllRules() {
# recovery rules
iptables-save | awk '/^[*]/ { print $1 } /^:[A-Z]+ [^-]/ { print $1 " ACCEPT" ; } /COMMIT/ { print $0; }' | iptables-restore

# nat
if [ $nat_enable -eq 1 ]; then
iptables -t nat -A POSTROUTING -o $outbound -j MASQUERADE
fi

# drop initiative package from wan
if [ $slient_wan -eq 1 ]; then
iptables -A INPUT -i $outbound -m state --state NEW -j DROP
fi
}

setRules() {
clearAllRules

# udp tproxy
iptables -t mangle -N PROXY_UDP
iptables -t mangle -F PROXY_UDP
iptables -t mangle -A PROXY_UDP -p udp --dport 53 -j ACCEPT
iptables -t mangle -A PROXY_UDP -p udp -j TPROXY --on-port ${tproxy_port} --tproxy-mark 1

# DNS redirect
iptables -t nat -N PROXY_DNS
iptables -t nat -F PROXY_DNS
iptables -t nat -A PROXY_DNS -p udp -j REDIRECT --to-ports ${dns_port}

# tcp redirect
iptables -t nat -N PROXY_TCP
iptables -t nat -F PROXY_TCP
iptables -t nat -A PROXY_TCP -p tcp -j REDIRECT --to-ports ${redir_port}

# apply rules
if [ $udp_enable -eq 1 ]; then
if [ $nat_enable -eq 1 ]; then
iptables -t mangle -A PREROUTING -s 192.168.1.128/25 -j ACCEPT
fi
iptables -t mangle -A PREROUTING -m set --match-set local dst -j ACCEPT
iptables -t mangle -A PREROUTING -p udp -j PROXY_UDP
fi

# apply rules
if [ $nat_enable -eq 1 ]; then
iptables -t nat -A PREROUTING -s 192.168.1.128/25 -j ACCEPT
fi
iptables -t nat -A PREROUTING -p udp --dport 53 -j PROXY_DNS
iptables -t nat -A PREROUTING -m set --match-set local dst -j ACCEPT
iptables -t nat -A PREROUTING -p tcp -j PROXY_TCP
}

setSelf() {
iptables -t nat -A OUTPUT -m mark --mark 6666 -j ACCEPT
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports ${dns_port}
iptables -t nat -A OUTPUT -m set --match-set local dst -j ACCEPT
iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports ${redir_port}
}

update() {
cd $converter_dir
cat $config_dir/config.yaml >$config_dir/past.yaml

./converter

diffConfig

read -p "Press Enter to continue..."

cat $config_dir/target.yaml >$config_dir/config.yaml
# setRules
systemctl restart clash
}

lease() {
# show dnsmasq lease
while read line; do
timestamp=$(echo "$line" | awk '{print $1}')
mac=$(echo "$line" | awk '{print $2}')
ip=$(echo "$line" | awk '{print $3}')
host=$(echo "$line" | awk '{print $4}')

# Convert the timestamp to a human-readable date
date=$(date -d @$timestamp +"%m-%d %H:%M:%S")

# Output the result
echo "$mac $date $ip $host"
done <"$LEASE_FILE"
}

ban() {
iptables -t mangle -I PREROUTING -s $1 -j DROP
# iptables -A INPUT -s $1 -j DROP
# iptables -I FORWARD -s $1 -j DROP
# iptables -I FORWARD -s $1 -p tcp --dport 443 -j ACCEPT
# iptables -I FORWARD -s $1 -p tcp --dport 80 -j ACCEPT
}

diffConfig() {
# diff $config_dir/config.yaml $config_dir/target.yaml -C 3 --color | less
diff $config_dir/config.yaml $config_dir/target.yaml -C 3 --color
}

recovery() {
cat $config_dir/past.yaml >$config_dir/config.yaml
systemctl restart clash
}

case "$1" in
init)
init
;;
set)
setRules
;;
del)
clearAllRules
;;
update)
update
;;
lease)
lease
;;
recovery)
recovery
;;
diff)
diffConfig
;;
ban)
ban $2
;;
*)
echo "HELLO IT'S ME"
exit 0
;;
esac
exit

持久化

注册账户

1
sudo useradd -d /var/local/clash -s /usr/bin/nologin -r clash

注册clash为服务

1
vim /etc/systemd/system/clash.service

写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=clash daemon
After=network.target

[Service]
User=clash
Type=simple
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
WorkingDirectory=/var/local/clash/
ExecStart=/usr/local/bin/clash -d /var/local/clash/
Restart=on-failure
RestartSec=600s

[Install]
WantedBy=multi-user.target

开机自启动

1
2
systemctl start clash.service
systemctl enable clash.service

iptables规则保存[可选]

1
iptables-save > /etc/iptables