girl 0.57.0 → 0.60.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of girl might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/girl.gemspec +7 -0
- data/lib/girl.rb +2 -4
- data/lib/girl/custom.rb +12 -0
- data/lib/girl/head.rb +38 -10
- data/lib/girl/proxy.rb +202 -0
- data/lib/girl/proxy_custom.rb +15 -0
- data/lib/girl/proxy_worker.rb +1434 -0
- data/lib/girl/proxyd.rb +108 -0
- data/lib/girl/proxyd_custom.rb +12 -0
- data/lib/girl/proxyd_worker.rb +991 -0
- data/lib/girl/resolv.rb +1 -1
- data/lib/girl/resolvd.rb +1 -1
- data/lib/girl/tun.rb +35 -52
- data/lib/girl/tund.rb +29 -46
- data/lib/girl/udp.rb +1 -1
- data/lib/girl/udpd.rb +1 -1
- data/lib/girl/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7ae98a97c1517a3cabb320198b9e0d909b74a6c610f7b6e0109b6248b60b530
|
4
|
+
data.tar.gz: 8c987e8a39f60fe03261ad1b9caadac7c6b6faf3bdb898866d4c507d3a492571
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 468a1ae9530407076582f1c6fc92cf56e3d36b87dbfc9805b7b2dd3a6b4f77294dd14e98d097ed2ab5b50203a569bffd8b572df3acd8caa07cc8b5c890721c04
|
7
|
+
data.tar.gz: c6ef17b8a4dc02a5bd037f4700577c2669d7642b34c51fed9a86ec076e43b7a3810401a24541bd90958a2169406496dbd22a8cf9b1cc062b908a3c95c249ed55
|
data/girl.gemspec
CHANGED
@@ -17,8 +17,15 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.files = %w[
|
18
18
|
girl.gemspec
|
19
19
|
lib/girl.rb
|
20
|
+
lib/girl/custom.rb
|
20
21
|
lib/girl/head.rb
|
21
22
|
lib/girl/hex.rb
|
23
|
+
lib/girl/proxy_custom.rb
|
24
|
+
lib/girl/proxy_worker.rb
|
25
|
+
lib/girl/proxy.rb
|
26
|
+
lib/girl/proxyd_custom.rb
|
27
|
+
lib/girl/proxyd_worker.rb
|
28
|
+
lib/girl/proxyd.rb
|
22
29
|
lib/girl/resolv.rb
|
23
30
|
lib/girl/resolvd.rb
|
24
31
|
lib/girl/tun.rb
|
data/lib/girl.rb
CHANGED
data/lib/girl/custom.rb
ADDED
data/lib/girl/head.rb
CHANGED
@@ -1,14 +1,20 @@
|
|
1
1
|
module Girl
|
2
|
-
PACK_SIZE = 1328
|
3
|
-
CHUNK_SIZE = PACK_SIZE * 1000
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
2
|
+
PACK_SIZE = 1328 # 包大小 1400(console MTU) - 8(PPPoE header) - 40(IPv6 header) - 8(UDP header) - 8(source/dest id) - 8(pack id) = 1328
|
3
|
+
CHUNK_SIZE = PACK_SIZE * 1000 # 块大小
|
4
|
+
PROXY_PACK_SIZE = 1320 # 1400(console MTU) - 8(PPPoE header) - 40(IPv6 header) - 8(UDP header) - 8(pack id) - 16(src_addr) = 1320
|
5
|
+
PROXY_CHUNK_SIZE = PROXY_PACK_SIZE * 1000 # proxy块大小
|
6
|
+
WBUFFS_LIMIT = 1000 # 写前上限,超过上限结一个块
|
7
|
+
WMEMS_LIMIT = 100_000 # 写后上限,达到上限暂停写
|
8
|
+
RESUME_BELOW = 50_000 # 降到多少以下恢复写
|
9
|
+
EXPIRE_NEW = 10 # 创建之后多久没有流量进来,过期
|
10
|
+
EXPIRE_AFTER = 900 # 多久没有新流量进来,过期
|
11
|
+
CHECK_EXPIRE_INTERVAL = 30 # 检查过期间隔
|
12
|
+
HEARTBEAT_INTERVAL = 30 # 心跳间隔
|
13
|
+
STATUS_INTERVAL = 0.3 # 发送状态间隔
|
14
|
+
SEND_STATUS_UNTIL = 10 # 持续的告之对面状态,直到没有流量往来,持续多少秒
|
15
|
+
BREAK_SEND_MISS = 10_000 # miss包个数上限,达到上限忽略要后面的段,可控碎片缓存
|
16
|
+
CONFUSE_UNTIL = 5 # 混淆前几个包
|
17
|
+
RESOLV_CACHE_EXPIRE = 300 # dns查询结果缓存多久过期
|
12
18
|
TUND_PORT = 1
|
13
19
|
HEARTBEAT = 2
|
14
20
|
A_NEW_SOURCE = 3
|
@@ -24,4 +30,26 @@ module Girl
|
|
24
30
|
TUN_FIN = 13
|
25
31
|
CTL_CLOSE = 1
|
26
32
|
CTL_RESUME = 2
|
33
|
+
HTTP_OK = "HTTP/1.1 200 OK\r\n\r\n"
|
34
|
+
# https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
|
35
|
+
RESERVED_ROUTE = <<EOF
|
36
|
+
0.0.0.0/8
|
37
|
+
10.0.0.0/8
|
38
|
+
100.64.0.0/10
|
39
|
+
127.0.0.0/8
|
40
|
+
169.254.0.0/16
|
41
|
+
172.16.0.0/12
|
42
|
+
192.0.0.0/24
|
43
|
+
192.0.2.0/24
|
44
|
+
192.31.196.0/24
|
45
|
+
192.52.193.0/24
|
46
|
+
192.88.99.0/24
|
47
|
+
192.168.0.0/16
|
48
|
+
192.175.48.0/24
|
49
|
+
198.18.0.0/15
|
50
|
+
198.51.100.0/24
|
51
|
+
203.0.113.0/24
|
52
|
+
240.0.0.0/4
|
53
|
+
255.255.255.255/32
|
54
|
+
EOF
|
27
55
|
end
|
data/lib/girl/proxy.rb
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'etc'
|
2
|
+
require 'girl/head'
|
3
|
+
require 'girl/proxy_custom'
|
4
|
+
require 'girl/proxy_worker'
|
5
|
+
require 'girl/version'
|
6
|
+
require 'ipaddr'
|
7
|
+
require 'json'
|
8
|
+
require 'socket'
|
9
|
+
|
10
|
+
##
|
11
|
+
# Girl::Proxy - 代理服务,近端。
|
12
|
+
#
|
13
|
+
# 包结构
|
14
|
+
# ======
|
15
|
+
#
|
16
|
+
# tun-proxyd:
|
17
|
+
#
|
18
|
+
# hello
|
19
|
+
#
|
20
|
+
# proxyd-tun:
|
21
|
+
#
|
22
|
+
# Q>: 0 ctlmsg -> C: 1 tund port -> n: tund port
|
23
|
+
#
|
24
|
+
# tun-tund:
|
25
|
+
#
|
26
|
+
# Q>: 0 ctlmsg -> C: 2 heartbeat -> C: random char
|
27
|
+
# 3 a new src -> src_addr -> encoded destination address
|
28
|
+
# 4 paired -> src_addr -> n: dst_port
|
29
|
+
# 5 dst status -> n: dst_port -> Q>Q>: biggest_dst_pack_id continue_src_pack_id
|
30
|
+
# 6 src status -> src_addr -> Q>Q>: biggest_src_pack_id continue_dst_pack_id
|
31
|
+
# 7 miss -> src_addr/n: dst_port -> Q>Q>: pack_id_begin pack_id_end
|
32
|
+
# 8 fin1 -> src_addr/n: dst_port -> Q>Q>: biggest_src_pack_id continue_dst_pack_id / biggest_dst_pack_id continue_src_pack_id
|
33
|
+
# 9 not use
|
34
|
+
# 10 fin2 -> src_addr/n: dst_port
|
35
|
+
# 11 not use
|
36
|
+
# 12 tund fin
|
37
|
+
# 13 tun fin
|
38
|
+
#
|
39
|
+
# Q>: 1+ pack_id -> src_addr/n: dst_port -> traffic
|
40
|
+
#
|
41
|
+
# close logic
|
42
|
+
# ===========
|
43
|
+
#
|
44
|
+
# 1-1. after close src -> dst closed ? no -> send fin1
|
45
|
+
# 1-2. recv fin2 -> del src ext
|
46
|
+
#
|
47
|
+
# 2-1. recv traffic/fin1/dst status -> dst closed and all traffic received ? -> close src after write
|
48
|
+
# 2-2. after close src -> dst closed ? yes -> del src ext -> send fin2
|
49
|
+
#
|
50
|
+
module Girl
|
51
|
+
class Proxy
|
52
|
+
|
53
|
+
def initialize( config_path = nil )
|
54
|
+
unless config_path
|
55
|
+
config_path = File.expand_path( '../girl.conf.json', __FILE__ )
|
56
|
+
end
|
57
|
+
|
58
|
+
unless File.exist?( config_path )
|
59
|
+
raise "missing config file #{ config_path }"
|
60
|
+
end
|
61
|
+
|
62
|
+
# {
|
63
|
+
# "proxy_port": 6666, // 代理服务,近端(本地)端口
|
64
|
+
# "proxyd_host": "1.2.3.4", // 代理服务,远端服务器
|
65
|
+
# "proxyd_port": 6060, // 代理服务,远端端口
|
66
|
+
# "direct_path": "girl.direct.txt", // 直连ip段
|
67
|
+
# "remote_path": "girl.remote.txt", // 交给远端解析的域名列表
|
68
|
+
# "proxy_tmp_dir": "/tmp/girl.proxy", // 近端缓存根路径
|
69
|
+
# "proxyd_tmp_dir": "/tmp/girl.proxyd", // 远端缓存根路径
|
70
|
+
# "im": "girl", // 标识,用来识别近端
|
71
|
+
# "worker_count": 4 // 子进程数,默认取cpu个数
|
72
|
+
# }
|
73
|
+
conf = JSON.parse( IO.binread( config_path ), symbolize_names: true )
|
74
|
+
proxy_port = conf[ :proxy_port ]
|
75
|
+
proxyd_host = conf[ :proxyd_host ]
|
76
|
+
proxyd_port = conf[ :proxyd_port ]
|
77
|
+
direct_path = conf[ :direct_path ]
|
78
|
+
remote_path = conf[ :remote_path ]
|
79
|
+
proxy_tmp_dir = conf[ :proxy_tmp_dir ]
|
80
|
+
im = conf[ :im ]
|
81
|
+
worker_count = conf[ :worker_count ]
|
82
|
+
|
83
|
+
unless proxy_port
|
84
|
+
proxy_port = 6666
|
85
|
+
end
|
86
|
+
|
87
|
+
unless proxyd_host
|
88
|
+
raise "missing proxyd host"
|
89
|
+
end
|
90
|
+
|
91
|
+
unless proxyd_port
|
92
|
+
proxyd_port = 6060
|
93
|
+
end
|
94
|
+
|
95
|
+
directs = []
|
96
|
+
|
97
|
+
if direct_path
|
98
|
+
unless File.exist?( direct_path )
|
99
|
+
raise "not found direct file #{ direct_path }"
|
100
|
+
end
|
101
|
+
|
102
|
+
directs = ( [ Addrinfo.ip( proxyd_host ).ip_address ] + RESERVED_ROUTE.split( "\n" ) + IO.binread( direct_path ).split( "\n" ) ).map { | line | IPAddr.new( line.strip ) }
|
103
|
+
end
|
104
|
+
|
105
|
+
remotes = []
|
106
|
+
|
107
|
+
if remote_path
|
108
|
+
unless File.exist?( remote_path )
|
109
|
+
raise "not found remote file #{ remote_path }"
|
110
|
+
end
|
111
|
+
|
112
|
+
remotes = IO.binread( remote_path ).split( "\n" ).map { | line | line.strip }
|
113
|
+
end
|
114
|
+
|
115
|
+
unless proxy_tmp_dir
|
116
|
+
proxy_tmp_dir = '/tmp/girl.proxy'
|
117
|
+
end
|
118
|
+
|
119
|
+
unless File.exist?( proxy_tmp_dir )
|
120
|
+
Dir.mkdir( proxy_tmp_dir )
|
121
|
+
end
|
122
|
+
|
123
|
+
src_chunk_dir = File.join( proxy_tmp_dir, 'src.chunk' )
|
124
|
+
dst_chunk_dir = File.join( proxy_tmp_dir, 'dst.chunk' )
|
125
|
+
tun_chunk_dir = File.join( proxy_tmp_dir, 'tun.chunk' )
|
126
|
+
|
127
|
+
unless Dir.exist?( proxy_tmp_dir )
|
128
|
+
Dir.mkdir( proxy_tmp_dir )
|
129
|
+
end
|
130
|
+
|
131
|
+
unless Dir.exist?( src_chunk_dir )
|
132
|
+
Dir.mkdir( src_chunk_dir )
|
133
|
+
end
|
134
|
+
|
135
|
+
unless Dir.exist?( dst_chunk_dir )
|
136
|
+
Dir.mkdir( dst_chunk_dir )
|
137
|
+
end
|
138
|
+
|
139
|
+
unless Dir.exist?( tun_chunk_dir )
|
140
|
+
Dir.mkdir( tun_chunk_dir )
|
141
|
+
end
|
142
|
+
|
143
|
+
unless im
|
144
|
+
im = 'girl'
|
145
|
+
end
|
146
|
+
|
147
|
+
nprocessors = Etc.nprocessors
|
148
|
+
|
149
|
+
if worker_count.nil? || worker_count <= 0 || worker_count > nprocessors
|
150
|
+
worker_count = nprocessors
|
151
|
+
end
|
152
|
+
|
153
|
+
title = "girl proxy #{ Girl::VERSION }"
|
154
|
+
puts title
|
155
|
+
puts "proxy port #{ proxy_port }"
|
156
|
+
puts "proxyd host #{ proxyd_host }"
|
157
|
+
puts "proxyd port #{ proxyd_port }"
|
158
|
+
puts "#{ direct_path } #{ directs.size } directs"
|
159
|
+
puts "#{ remote_path } #{ remotes.size } remotes"
|
160
|
+
puts "src chunk dir #{ src_chunk_dir }"
|
161
|
+
puts "dst chunk dir #{ dst_chunk_dir }"
|
162
|
+
puts "tun chunk dir #{ tun_chunk_dir }"
|
163
|
+
puts "im #{ im }"
|
164
|
+
puts "worker count #{ worker_count }"
|
165
|
+
|
166
|
+
if RUBY_PLATFORM.include?( 'linux' )
|
167
|
+
$0 = title
|
168
|
+
workers = []
|
169
|
+
|
170
|
+
worker_count.times do | i |
|
171
|
+
workers << fork do
|
172
|
+
$0 = 'girl proxy worker'
|
173
|
+
worker = Girl::ProxyWorker.new( proxy_port, proxyd_host, proxyd_port, directs, remotes, src_chunk_dir, dst_chunk_dir, tun_chunk_dir, im )
|
174
|
+
|
175
|
+
Signal.trap( :TERM ) do
|
176
|
+
puts "w#{ i } exit"
|
177
|
+
worker.quit!
|
178
|
+
end
|
179
|
+
|
180
|
+
worker.looping
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
Signal.trap( :TERM ) do
|
185
|
+
puts 'trap TERM'
|
186
|
+
workers.each do | pid |
|
187
|
+
begin
|
188
|
+
Process.kill( :TERM, pid )
|
189
|
+
rescue Errno::ESRCH => e
|
190
|
+
puts e.class
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
Process.waitall
|
196
|
+
else
|
197
|
+
Girl::ProxyWorker.new( proxy_port, proxyd_host, proxyd_port, directs, remotes, src_chunk_dir, dst_chunk_dir, tun_chunk_dir, im ).looping
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,1434 @@
|
|
1
|
+
module Girl
|
2
|
+
class ProxyWorker
|
3
|
+
|
4
|
+
##
|
5
|
+
# initialize
|
6
|
+
#
|
7
|
+
def initialize( proxy_port, proxyd_host, proxyd_port, directs, remotes, src_chunk_dir, dst_chunk_dir, tun_chunk_dir, im )
|
8
|
+
@proxyd_host = proxyd_host
|
9
|
+
@proxyd_addr = Socket.sockaddr_in( proxyd_port, proxyd_host )
|
10
|
+
@directs = directs
|
11
|
+
@remotes = remotes
|
12
|
+
@src_chunk_dir = src_chunk_dir
|
13
|
+
@dst_chunk_dir = dst_chunk_dir
|
14
|
+
@tun_chunk_dir = tun_chunk_dir
|
15
|
+
@custom = Girl::ProxyCustom.new( im )
|
16
|
+
@mutex = Mutex.new
|
17
|
+
@reads = []
|
18
|
+
@writes = []
|
19
|
+
@roles = {} # sock => :dotr / :proxy / :src / :dst / :tun
|
20
|
+
@src_infos = {} # src => {}
|
21
|
+
@dsts = {} # local_port => dst
|
22
|
+
@dst_infos = {} # dst => {}
|
23
|
+
@resolv_caches = {} # domain => [ ip, created_at ]
|
24
|
+
|
25
|
+
dotr, dotw = IO.pipe
|
26
|
+
@dotw = dotw
|
27
|
+
add_read( dotr, :dotr )
|
28
|
+
new_a_proxy( proxy_port )
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# looping
|
33
|
+
#
|
34
|
+
def looping
|
35
|
+
puts "p#{ Process.pid } #{ Time.new } looping"
|
36
|
+
loop_check_expire
|
37
|
+
loop_check_status
|
38
|
+
|
39
|
+
loop do
|
40
|
+
rs, ws = IO.select( @reads, @writes )
|
41
|
+
|
42
|
+
@mutex.synchronize do
|
43
|
+
# 先写,再读
|
44
|
+
ws.each do | sock |
|
45
|
+
case @roles[ sock ]
|
46
|
+
when :src
|
47
|
+
write_src( sock )
|
48
|
+
when :dst
|
49
|
+
write_dst( sock )
|
50
|
+
when :tun
|
51
|
+
write_tun( sock )
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
rs.each do | sock |
|
56
|
+
case @roles[ sock ]
|
57
|
+
when :dotr
|
58
|
+
read_dotr( sock )
|
59
|
+
when :proxy
|
60
|
+
read_proxy( sock )
|
61
|
+
when :src
|
62
|
+
read_src( sock )
|
63
|
+
when :dst
|
64
|
+
read_dst( sock )
|
65
|
+
when :tun
|
66
|
+
read_tun( sock )
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
rescue Interrupt => e
|
72
|
+
puts e.class
|
73
|
+
quit!
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# quit!
|
78
|
+
#
|
79
|
+
def quit!
|
80
|
+
if @tun && !@tun.closed? && @tun_info[ :tund_addr ]
|
81
|
+
# puts "debug1 send tun fin"
|
82
|
+
data = [ 0, TUN_FIN ].pack( 'Q>C' )
|
83
|
+
@tun.sendmsg( data, 0, @tun_info[ :tund_addr ] )
|
84
|
+
end
|
85
|
+
|
86
|
+
# puts "debug1 exit"
|
87
|
+
exit
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
##
|
93
|
+
# loop check expire
|
94
|
+
#
|
95
|
+
def loop_check_expire
|
96
|
+
Thread.new do
|
97
|
+
loop do
|
98
|
+
sleep CHECK_EXPIRE_INTERVAL
|
99
|
+
|
100
|
+
@mutex.synchronize do
|
101
|
+
need_trigger = false
|
102
|
+
now = Time.new
|
103
|
+
|
104
|
+
if @tun && !@tun.closed?
|
105
|
+
is_expired = @tun_info[ :last_recv_at ] ? ( now - @tun_info[ :last_recv_at ] > EXPIRE_AFTER ) : ( now - @tun_info[ :created_at ] > EXPIRE_NEW )
|
106
|
+
|
107
|
+
if is_expired
|
108
|
+
puts "p#{ Process.pid } #{ Time.new } expire tun"
|
109
|
+
set_is_closing( @tun )
|
110
|
+
else
|
111
|
+
data = [ 0, HEARTBEAT, rand( 128 ) ].pack( 'Q>CC' )
|
112
|
+
add_tun_ctlmsg( data )
|
113
|
+
|
114
|
+
@tun_info[ :src_exts ].each do | src_addr, src_ext |
|
115
|
+
if src_ext[ :src ].closed? && ( now - src_ext[ :last_continue_at ] > EXPIRE_AFTER )
|
116
|
+
puts "p#{ Process.pid } #{ Time.new } expire src_ext"
|
117
|
+
del_src_ext( src_addr )
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
need_trigger = true
|
123
|
+
end
|
124
|
+
|
125
|
+
@src_infos.each do | src, src_info |
|
126
|
+
is_expired = src_info[ :last_recv_at ].nil? && ( now - src_info[ :created_at ] > EXPIRE_NEW )
|
127
|
+
|
128
|
+
if is_expired
|
129
|
+
puts "p#{ Process.pid } #{ Time.new } expire src"
|
130
|
+
set_is_closing( src )
|
131
|
+
need_trigger = true
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
@dst_infos.each do | dst, dst_info |
|
136
|
+
is_expired = dst_info[ :last_recv_at ].nil? && ( now - dst_info[ :created_at ] > EXPIRE_NEW )
|
137
|
+
|
138
|
+
if is_expired
|
139
|
+
puts "p#{ Process.pid } #{ Time.new } expire dst"
|
140
|
+
set_is_closing( dst )
|
141
|
+
need_trigger = true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
if need_trigger
|
146
|
+
next_tick
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# loop check status
|
155
|
+
#
|
156
|
+
def loop_check_status
|
157
|
+
Thread.new do
|
158
|
+
loop do
|
159
|
+
sleep STATUS_INTERVAL
|
160
|
+
|
161
|
+
@mutex.synchronize do
|
162
|
+
if @tun && !@tun.closed? && @tun_info[ :tund_addr ]
|
163
|
+
need_trigger = false
|
164
|
+
|
165
|
+
if @tun_info[ :src_exts ].any?
|
166
|
+
now = Time.new
|
167
|
+
|
168
|
+
@tun_info[ :src_exts ].each do | src_addr, src_ext |
|
169
|
+
if src_ext[ :dst_port ] && ( now - src_ext[ :last_continue_at ] < SEND_STATUS_UNTIL )
|
170
|
+
# puts "debug2 ctl send status biggest #{ src_ext[ :biggest_pack_id ] } continue dst #{ src_ext[ :continue_dst_pack_id ] }"
|
171
|
+
data = [ [ 0, SOURCE_STATUS ].pack( 'Q>C' ), src_addr, [ src_ext[ :biggest_pack_id ], src_ext[ :continue_dst_pack_id ] ].pack( 'Q>Q>' ) ].join
|
172
|
+
add_tun_ctlmsg( data )
|
173
|
+
need_trigger = true
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
if @tun_info[ :paused ] && ( @tun_info[ :src_exts ].map{ | _, src_ext | src_ext[ :wmems ].size }.sum < RESUME_BELOW )
|
179
|
+
puts "p#{ Process.pid } #{ Time.new } resume tun"
|
180
|
+
@tun_info[ :paused ] = false
|
181
|
+
add_write( @tun )
|
182
|
+
need_trigger = true
|
183
|
+
end
|
184
|
+
|
185
|
+
if need_trigger
|
186
|
+
next_tick
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# loop send a new source
|
196
|
+
#
|
197
|
+
def loop_send_a_new_source( src_ext, data )
|
198
|
+
Thread.new do
|
199
|
+
EXPIRE_NEW.times do
|
200
|
+
if src_ext[ :src ].closed? || src_ext[ :dst_port ]
|
201
|
+
# puts "debug1 break loop send a new source #{ src_ext[ :dst_port ] }"
|
202
|
+
break
|
203
|
+
end
|
204
|
+
|
205
|
+
@mutex.synchronize do
|
206
|
+
# puts "debug1 send a new source #{ data.inspect }"
|
207
|
+
add_tun_ctlmsg( data )
|
208
|
+
next_tick
|
209
|
+
end
|
210
|
+
|
211
|
+
sleep 1
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
##
|
217
|
+
# resolve domain
|
218
|
+
#
|
219
|
+
def resolve_domain( src, domain )
|
220
|
+
if ( /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ domain ) && domain.split( '.' ).all? { | part | part.to_i < 256 }
|
221
|
+
# puts "debug1 #{ domain } is a ip"
|
222
|
+
deal_with_destination_ip( src, domain )
|
223
|
+
return
|
224
|
+
end
|
225
|
+
|
226
|
+
if @remotes.any? { | remote | ( domain.size >= remote.size ) && ( domain[ ( remote.size * -1 )..-1 ] == remote ) }
|
227
|
+
# puts "debug1 #{ domain } hit remotes"
|
228
|
+
new_a_src_ext( src )
|
229
|
+
return
|
230
|
+
end
|
231
|
+
|
232
|
+
resolv_cache = @resolv_caches[ domain ]
|
233
|
+
|
234
|
+
if resolv_cache
|
235
|
+
destination_ip, created_at = resolv_cache
|
236
|
+
|
237
|
+
if Time.new - created_at < RESOLV_CACHE_EXPIRE
|
238
|
+
# puts "debug1 #{ domain } hit resolv cache #{ destination_ip }"
|
239
|
+
deal_with_destination_ip( src, destination_ip )
|
240
|
+
return
|
241
|
+
end
|
242
|
+
|
243
|
+
# puts "debug1 expire #{ domain } resolv cache"
|
244
|
+
@resolv_caches.delete( domain )
|
245
|
+
end
|
246
|
+
|
247
|
+
src_info = @src_infos[ src ]
|
248
|
+
src_info[ :proxy_type ] = :checking
|
249
|
+
|
250
|
+
Thread.new do
|
251
|
+
begin
|
252
|
+
ip_info = Addrinfo.ip( domain )
|
253
|
+
rescue Exception => e
|
254
|
+
puts "p#{ Process.pid } #{ Time.new } resolv #{ domain } #{ e.class }"
|
255
|
+
end
|
256
|
+
|
257
|
+
@mutex.synchronize do
|
258
|
+
if ip_info
|
259
|
+
destination_ip = ip_info.ip_address
|
260
|
+
# puts "debug1 resolved #{ domain } #{ destination_ip }"
|
261
|
+
@resolv_caches[ domain ] = [ destination_ip, Time.new ]
|
262
|
+
|
263
|
+
unless src.closed?
|
264
|
+
deal_with_destination_ip( src, destination_ip )
|
265
|
+
end
|
266
|
+
else
|
267
|
+
set_is_closing( src )
|
268
|
+
end
|
269
|
+
|
270
|
+
next_tick
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
##
|
276
|
+
# deal with destination ip
|
277
|
+
#
|
278
|
+
def deal_with_destination_ip( src, destination_ip )
|
279
|
+
if @directs.any? { | direct | direct.include?( destination_ip ) }
|
280
|
+
# ip命中直连列表,直连
|
281
|
+
# puts "debug1 #{ destination_ip } hit directs"
|
282
|
+
new_a_dst( src, destination_ip )
|
283
|
+
else
|
284
|
+
# 走远端
|
285
|
+
new_a_src_ext( src )
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
##
|
290
|
+
# new a proxy
|
291
|
+
#
|
292
|
+
def new_a_proxy( proxy_port )
|
293
|
+
proxy = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
|
294
|
+
proxy.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
|
295
|
+
|
296
|
+
if RUBY_PLATFORM.include?( 'linux' )
|
297
|
+
proxy.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
|
298
|
+
proxy.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
|
299
|
+
end
|
300
|
+
|
301
|
+
proxy.bind( Socket.sockaddr_in( proxy_port, '0.0.0.0' ) )
|
302
|
+
proxy.listen( 511 )
|
303
|
+
puts "p#{ Process.pid } #{ Time.new } proxy listen on #{ proxy_port }"
|
304
|
+
add_read( proxy, :proxy )
|
305
|
+
@proxy_local_address = proxy.local_address
|
306
|
+
end
|
307
|
+
|
308
|
+
##
|
309
|
+
# new a tun
|
310
|
+
#
|
311
|
+
def new_a_tun
|
312
|
+
tun = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
|
313
|
+
tun.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
|
314
|
+
port = tun.local_address.ip_unpack.last
|
315
|
+
tun_info = {
|
316
|
+
port: port, # 端口
|
317
|
+
ctlmsg_rbuffs: [], # 还没配上tund,暂存的ctlmsg
|
318
|
+
ctlmsgs: [], # [ to_addr, data ]
|
319
|
+
wbuffs: [], # 写前缓存 [ src_addr, data ]
|
320
|
+
caches: [], # 块读出缓存 [ src_addr, data ]
|
321
|
+
chunks: [], # 块队列 filename
|
322
|
+
spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
|
323
|
+
tund_addr: nil, # tund地址
|
324
|
+
src_exts: {}, # src额外信息 src_addr => {}
|
325
|
+
src_addrs: {}, # dst_port => src_addr
|
326
|
+
paused: false, # 是否暂停写
|
327
|
+
resendings: [], # 重传队列 [ src_addr, pack_id ]
|
328
|
+
created_at: Time.new, # 创建时间
|
329
|
+
last_recv_at: nil, # 上一次收到流量的时间,过期关闭
|
330
|
+
is_closing: false # 是否准备关闭
|
331
|
+
}
|
332
|
+
|
333
|
+
@tun = tun
|
334
|
+
@tun_info = tun_info
|
335
|
+
|
336
|
+
add_read( tun, :tun )
|
337
|
+
data = @custom.hello
|
338
|
+
puts "p#{ Process.pid } #{ Time.new } hello i'm tun"
|
339
|
+
# puts "debug1 #{ data.inspect }"
|
340
|
+
add_tun_ctlmsg( data, @proxyd_addr )
|
341
|
+
end
|
342
|
+
|
343
|
+
##
|
344
|
+
# new a dst
|
345
|
+
#
|
346
|
+
def new_a_dst( src, destination_ip )
|
347
|
+
src_info = @src_infos[ src ]
|
348
|
+
destination_addr = Socket.sockaddr_in( src_info[ :destination_port ], destination_ip )
|
349
|
+
dst = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
|
350
|
+
|
351
|
+
if RUBY_PLATFORM.include?( 'linux' )
|
352
|
+
dst.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
|
353
|
+
end
|
354
|
+
|
355
|
+
begin
|
356
|
+
dst.connect_nonblock( destination_addr )
|
357
|
+
rescue IO::WaitWritable
|
358
|
+
# connect nonblock 必抛 wait writable,这里仅仅接一下,逻辑写外面,整齐
|
359
|
+
rescue Exception => e
|
360
|
+
puts "p#{ Process.pid } #{ Time.new } connect destination #{ e.class }, close src"
|
361
|
+
set_is_closing( src )
|
362
|
+
return
|
363
|
+
end
|
364
|
+
|
365
|
+
# puts "debug1 a new dst #{ dst.local_address.inspect }"
|
366
|
+
local_port = dst.local_address.ip_unpack
|
367
|
+
@dsts[ local_port ] = dst
|
368
|
+
@dst_infos[ dst ] = {
|
369
|
+
local_port: local_port, # 本地端口
|
370
|
+
src: src, # 对应src
|
371
|
+
wbuff: '', # 写前
|
372
|
+
cache: '', # 块读出缓存
|
373
|
+
chunks: [], # 块队列,写前达到块大小时结一个块 filename
|
374
|
+
spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
|
375
|
+
created_at: Time.new, # 创建时间
|
376
|
+
last_recv_at: nil, # 上一次收到流量的时间,过期关闭
|
377
|
+
is_closing: false # 是否准备关闭
|
378
|
+
}
|
379
|
+
|
380
|
+
add_read( dst, :dst )
|
381
|
+
src_info[ :proxy_type ] = :direct
|
382
|
+
src_info[ :dst ] = dst
|
383
|
+
|
384
|
+
if src_info[ :proxy_proto ] == :http
|
385
|
+
datas = src_info[ :rbuffs ]
|
386
|
+
|
387
|
+
if datas.empty?
|
388
|
+
# CONNECT
|
389
|
+
# puts "debug1 add src wbuff http ok"
|
390
|
+
add_src_wbuff( src, HTTP_OK )
|
391
|
+
else
|
392
|
+
# not CONNECT
|
393
|
+
# puts "debug1 add src rbuffs to dst wbuff"
|
394
|
+
|
395
|
+
datas.each do | data |
|
396
|
+
add_dst_wbuff( dst, data )
|
397
|
+
end
|
398
|
+
end
|
399
|
+
elsif src_info[ :proxy_proto ] == :socks5
|
400
|
+
add_src_wbuff_socks5_conn_reply( src )
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
##
|
405
|
+
# new a src ext
|
406
|
+
#
|
407
|
+
def new_a_src_ext( src )
|
408
|
+
if @tun.nil? || @tun.closed?
|
409
|
+
new_a_tun
|
410
|
+
end
|
411
|
+
|
412
|
+
src_ext = {
|
413
|
+
src: src, # src
|
414
|
+
dst_port: nil, # 远端dst端口
|
415
|
+
wmems: {}, # 写后 pack_id => data
|
416
|
+
send_ats: {}, # 上一次发出时间 pack_id => send_at
|
417
|
+
biggest_pack_id: 0, # 发到几
|
418
|
+
continue_dst_pack_id: 0, # 收到几
|
419
|
+
pieces: {}, # 跳号包 dst_pack_id => data
|
420
|
+
is_dst_closed: false, # 对面是否已关闭
|
421
|
+
biggest_dst_pack_id: 0, # 对面发到几
|
422
|
+
completed_pack_id: 0, # 完成到几(对面收到几)
|
423
|
+
last_continue_at: Time.new # 创建,或者上一次收到连续流量,或者发出新包的时间
|
424
|
+
}
|
425
|
+
|
426
|
+
src_info = @src_infos[ src ]
|
427
|
+
src_addr = src_info[ :src_addr ]
|
428
|
+
@tun_info[ :src_exts ][ src_addr ] = src_ext
|
429
|
+
src_info[ :proxy_type ] = :tunnel
|
430
|
+
|
431
|
+
destination_port = src_info[ :destination_port ]
|
432
|
+
destination_domain = src_info[ :destination_domain ]
|
433
|
+
destination_domain_port = [ destination_domain, destination_port ].join( ':' )
|
434
|
+
data = [ [ 0, A_NEW_SOURCE ].pack( 'Q>C' ), src_addr, @custom.encode( destination_domain_port ) ].join
|
435
|
+
loop_send_a_new_source( src_ext, data )
|
436
|
+
end
|
437
|
+
|
438
|
+
##
|
439
|
+
# add src wbuff socks5 conn reply
|
440
|
+
#
|
441
|
+
def add_src_wbuff_socks5_conn_reply( src )
|
442
|
+
# +----+-----+-------+------+----------+----------+
|
443
|
+
# |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
|
444
|
+
# +----+-----+-------+------+----------+----------+
|
445
|
+
# | 1 | 1 | X'00' | 1 | Variable | 2 |
|
446
|
+
# +----+-----+-------+------+----------+----------+
|
447
|
+
proxy_ip, proxy_port = @proxy_local_address.ip_unpack
|
448
|
+
data = [ [ 5, 0, 0, 1 ].pack( 'C4' ), IPAddr.new( proxy_ip ).hton, [ proxy_port ].pack( 'n' ) ].join
|
449
|
+
# puts "debug1 add src wbuff socks5 conn reply #{ data.inspect }"
|
450
|
+
add_src_wbuff( src, data )
|
451
|
+
end
|
452
|
+
|
453
|
+
##
|
454
|
+
# sub http request
|
455
|
+
#
|
456
|
+
def sub_http_request( data )
|
457
|
+
method, url, proto = data.split( "\r\n" ).first.split( ' ' )
|
458
|
+
|
459
|
+
if proto && url && proto[ 0, 4 ] == 'HTTP' && url[ 0, 7 ] == 'http://'
|
460
|
+
domain_and_port = url.split( '/' )[ 2 ]
|
461
|
+
data = data.sub( "http://#{ domain_and_port }", '' )
|
462
|
+
# puts "debug1 subed #{ data.inspect } #{ domain_and_port }"
|
463
|
+
end
|
464
|
+
|
465
|
+
[ data, domain_and_port ]
|
466
|
+
end
|
467
|
+
|
468
|
+
##
|
469
|
+
# add tun ctlmsg
|
470
|
+
#
|
471
|
+
def add_tun_ctlmsg( data, to_addr = nil )
|
472
|
+
unless to_addr
|
473
|
+
to_addr = @tun_info[ :tund_addr ]
|
474
|
+
end
|
475
|
+
|
476
|
+
if to_addr
|
477
|
+
@tun_info[ :ctlmsgs ] << [ to_addr, data ]
|
478
|
+
add_write( @tun )
|
479
|
+
else
|
480
|
+
@tun_info[ :ctlmsg_rbuffs ] << data
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
##
|
485
|
+
# add tun wbuff
|
486
|
+
#
|
487
|
+
def add_tun_wbuff( src_addr, data )
|
488
|
+
@tun_info[ :wbuffs ] << [ src_addr, data ]
|
489
|
+
|
490
|
+
if @tun_info[ :wbuffs ].size >= WBUFFS_LIMIT
|
491
|
+
spring = @tun_info[ :chunks ].size > 0 ? ( @tun_info[ :spring ] + 1 ) : 0
|
492
|
+
filename = "#{ Process.pid }-#{ @tun_info[ :port ] }.#{ spring }"
|
493
|
+
chunk_path = File.join( @tun_chunk_dir, filename )
|
494
|
+
wbuffs = @tun_info[ :wbuffs ].map{ | _src_addr, _data | [ _src_addr, _data.bytesize.pack( 'n' ), _data ].join }
|
495
|
+
|
496
|
+
begin
|
497
|
+
IO.binwrite( chunk_path, wbuffs.join )
|
498
|
+
rescue Errno::ENOSPC => e
|
499
|
+
puts "p#{ Process.pid } #{ Time.new } #{ e.class }, close tun"
|
500
|
+
set_is_closing( @tun )
|
501
|
+
return
|
502
|
+
end
|
503
|
+
|
504
|
+
@tun_info[ :chunks ] << filename
|
505
|
+
@tun_info[ :spring ] = spring
|
506
|
+
@tun_info[ :wbuffs ].clear
|
507
|
+
end
|
508
|
+
|
509
|
+
add_write( @tun )
|
510
|
+
end
|
511
|
+
|
512
|
+
##
|
513
|
+
# add src wbuff
|
514
|
+
#
|
515
|
+
def add_src_wbuff( src, data )
|
516
|
+
src_info = @src_infos[ src ]
|
517
|
+
src_info[ :wbuff ] << data
|
518
|
+
|
519
|
+
if src_info[ :wbuff ].bytesize >= PROXY_CHUNK_SIZE
|
520
|
+
spring = src_info[ :chunks ].size > 0 ? ( src_info[ :spring ] + 1 ) : 0
|
521
|
+
filename = "#{ Process.pid }-#{ Addrinfo.new( src_info[ :src_addr ] ).ip_unpack.join( '-' ) }.#{ spring }"
|
522
|
+
chunk_path = File.join( @src_chunk_dir, filename )
|
523
|
+
|
524
|
+
begin
|
525
|
+
IO.binwrite( chunk_path, src_info[ :wbuff ] )
|
526
|
+
rescue Errno::ENOSPC => e
|
527
|
+
puts "p#{ Process.pid } #{ Time.new } #{ e.class }, close src"
|
528
|
+
set_is_closing( src )
|
529
|
+
return
|
530
|
+
end
|
531
|
+
|
532
|
+
src_info[ :chunks ] << filename
|
533
|
+
src_info[ :spring ] = spring
|
534
|
+
src_info[ :wbuff ].clear
|
535
|
+
end
|
536
|
+
|
537
|
+
add_write( src )
|
538
|
+
end
|
539
|
+
|
540
|
+
##
|
541
|
+
# add dst wbuff
|
542
|
+
#
|
543
|
+
def add_dst_wbuff( dst, data )
|
544
|
+
dst_info = @dst_infos[ dst ]
|
545
|
+
dst_info[ :wbuff ] << data
|
546
|
+
|
547
|
+
if dst_info[ :wbuff ].bytesize >= PROXY_CHUNK_SIZE
|
548
|
+
spring = dst_info[ :chunks ].size > 0 ? ( dst_info[ :spring ] + 1 ) : 0
|
549
|
+
filename = "#{ Process.pid }-#{ dst_info[ :local_port ] }.#{ spring }"
|
550
|
+
chunk_path = File.join( @dst_chunk_dir, filename )
|
551
|
+
|
552
|
+
begin
|
553
|
+
IO.binwrite( chunk_path, dst_info[ :wbuff ] )
|
554
|
+
rescue Errno::ENOSPC => e
|
555
|
+
puts "p#{ Process.pid } #{ Time.new } #{ e.class }, close dst"
|
556
|
+
set_is_closing( dst )
|
557
|
+
return
|
558
|
+
end
|
559
|
+
|
560
|
+
dst_info[ :chunks ] << filename
|
561
|
+
dst_info[ :spring ] = spring
|
562
|
+
dst_info[ :wbuff ].clear
|
563
|
+
end
|
564
|
+
|
565
|
+
add_write( dst )
|
566
|
+
end
|
567
|
+
|
568
|
+
##
|
569
|
+
# add read
|
570
|
+
#
|
571
|
+
def add_read( sock, role )
|
572
|
+
unless @reads.include?( sock )
|
573
|
+
@reads << sock
|
574
|
+
end
|
575
|
+
|
576
|
+
@roles[ sock ] = role
|
577
|
+
end
|
578
|
+
|
579
|
+
##
|
580
|
+
# add write
|
581
|
+
#
|
582
|
+
def add_write( sock )
|
583
|
+
unless @writes.include?( sock )
|
584
|
+
@writes << sock
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
##
|
589
|
+
# set is closing
|
590
|
+
#
|
591
|
+
def set_is_closing( sock )
|
592
|
+
if sock && !sock.closed?
|
593
|
+
role = @roles[ sock ]
|
594
|
+
# puts "debug1 set #{ role.to_s } is closing"
|
595
|
+
|
596
|
+
case role
|
597
|
+
when :src
|
598
|
+
src_info = @src_infos[ sock ]
|
599
|
+
src_info[ :is_closing ] = true
|
600
|
+
when :dst
|
601
|
+
dst_info = @dst_infos[ sock ]
|
602
|
+
dst_info[ :is_closing ] = true
|
603
|
+
when :tun
|
604
|
+
@tun_info[ :is_closing ] = true
|
605
|
+
end
|
606
|
+
|
607
|
+
@reads.delete( sock )
|
608
|
+
add_write( sock )
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
##
|
613
|
+
# close src
|
614
|
+
#
|
615
|
+
def close_src( src )
|
616
|
+
# puts "debug1 close src"
|
617
|
+
close_sock( src )
|
618
|
+
src_info = @src_infos.delete( src )
|
619
|
+
|
620
|
+
src_info[ :chunks ].each do | filename |
|
621
|
+
begin
|
622
|
+
File.delete( File.join( @src_chunk_dir, filename ) )
|
623
|
+
rescue Errno::ENOENT
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
src_addr = src_info[ :src_addr ]
|
628
|
+
|
629
|
+
if src_info[ :proxy_type ] == :tunnel
|
630
|
+
return if @tun.closed?
|
631
|
+
|
632
|
+
src_ext = @tun_info[ :src_exts ][ src_addr ]
|
633
|
+
return if src_ext.nil? || src_ext[ :dst_port ].nil?
|
634
|
+
|
635
|
+
if src_ext[ :is_dst_closed ]
|
636
|
+
# puts "debug1 2-2. after close src -> dst closed ? yes -> del src ext -> send fin2"
|
637
|
+
del_src_ext( src_addr )
|
638
|
+
data = [ [ 0, FIN2 ].pack( 'Q>C' ), src_addr ].join
|
639
|
+
add_tun_ctlmsg( data )
|
640
|
+
else
|
641
|
+
# puts "debug1 1-1. after close src -> dst closed ? no -> send fin1"
|
642
|
+
data = [ [ 0, FIN1 ].pack( 'Q>C' ), src_addr, [ src_ext[ :biggest_pack_id ], src_ext[ :continue_dst_pack_id ] ].pack( 'Q>Q>' ) ].join
|
643
|
+
add_tun_ctlmsg( data )
|
644
|
+
end
|
645
|
+
elsif src_info[ :proxy_type ] == :direct
|
646
|
+
set_is_closing( src_info[ :dst ] )
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
##
|
651
|
+
# close dst
|
652
|
+
#
|
653
|
+
def close_dst( dst )
|
654
|
+
# puts "debug1 close dst"
|
655
|
+
close_sock( dst )
|
656
|
+
dst_info = @dst_infos.delete( dst )
|
657
|
+
|
658
|
+
dst_info[ :chunks ].each do | filename |
|
659
|
+
begin
|
660
|
+
File.delete( File.join( @dst_chunk_dir, filename ) )
|
661
|
+
rescue Errno::ENOENT
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
@dsts.delete( dst_info[ :local_port ] )
|
666
|
+
set_is_closing( dst_info[ :src ] )
|
667
|
+
end
|
668
|
+
|
669
|
+
##
|
670
|
+
# close tun
|
671
|
+
#
|
672
|
+
def close_tun( tun )
|
673
|
+
# puts "debug1 close tun"
|
674
|
+
close_sock( tun )
|
675
|
+
|
676
|
+
@tun_info[ :chunks ].each do | filename |
|
677
|
+
begin
|
678
|
+
File.delete( File.join( @tun_chunk_dir, filename ) )
|
679
|
+
rescue Errno::ENOENT
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
@tun_info[ :src_exts ].each{ | _, src_ext | set_is_closing( src_ext[ :src ] ) }
|
684
|
+
end
|
685
|
+
|
686
|
+
##
|
687
|
+
# close sock
|
688
|
+
#
|
689
|
+
def close_sock( sock )
|
690
|
+
sock.close
|
691
|
+
@reads.delete( sock )
|
692
|
+
@writes.delete( sock )
|
693
|
+
@roles.delete( sock )
|
694
|
+
end
|
695
|
+
|
696
|
+
##
|
697
|
+
# del src ext
|
698
|
+
#
|
699
|
+
def del_src_ext( src_addr )
|
700
|
+
src_ext = @tun_info[ :src_exts ].delete( src_addr )
|
701
|
+
|
702
|
+
if src_ext
|
703
|
+
@tun_info[ :src_addrs ].delete( src_ext[ :dst_port ] )
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
##
|
708
|
+
# release wmems
|
709
|
+
#
|
710
|
+
def release_wmems( src_ext, completed_pack_id )
|
711
|
+
if completed_pack_id > src_ext[ :completed_pack_id ]
|
712
|
+
# puts "debug2 update completed pack #{ completed_pack_id }"
|
713
|
+
pack_ids = src_ext[ :wmems ].keys.select { | pack_id | pack_id <= completed_pack_id }
|
714
|
+
|
715
|
+
pack_ids.each do | pack_id |
|
716
|
+
src_ext[ :wmems ].delete( pack_id )
|
717
|
+
src_ext[ :send_ats ].delete( pack_id )
|
718
|
+
end
|
719
|
+
|
720
|
+
src_ext[ :completed_pack_id ] = completed_pack_id
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
##
|
725
|
+
# next tick
|
726
|
+
#
|
727
|
+
def next_tick
|
728
|
+
@dotw.write( '.' )
|
729
|
+
end
|
730
|
+
|
731
|
+
##
|
732
|
+
# write src
|
733
|
+
#
|
734
|
+
def write_src( src )
|
735
|
+
src_info = @src_infos[ src ]
|
736
|
+
data = src_info[ :cache ]
|
737
|
+
from = :cache
|
738
|
+
|
739
|
+
if data.empty?
|
740
|
+
if src_info[ :chunks ].any?
|
741
|
+
path = File.join( @src_chunk_dir, src_info[ :chunks ].shift )
|
742
|
+
|
743
|
+
begin
|
744
|
+
src_info[ :cache ] = data = IO.binread( path )
|
745
|
+
File.delete( path )
|
746
|
+
rescue Errno::ENOENT => e
|
747
|
+
puts "p#{ Process.pid } #{ Time.new } read #{ path } #{ e.class }"
|
748
|
+
close_src( src )
|
749
|
+
return
|
750
|
+
end
|
751
|
+
else
|
752
|
+
data = src_info[ :wbuff ]
|
753
|
+
from = :wbuff
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
757
|
+
if data.empty?
|
758
|
+
if src_info[ :is_closing ]
|
759
|
+
close_src( src )
|
760
|
+
else
|
761
|
+
@writes.delete( src )
|
762
|
+
end
|
763
|
+
|
764
|
+
return
|
765
|
+
end
|
766
|
+
|
767
|
+
begin
|
768
|
+
written = src.write_nonblock( data )
|
769
|
+
rescue IO::WaitWritable, Errno::EINTR
|
770
|
+
return
|
771
|
+
rescue Exception => e
|
772
|
+
close_src( src )
|
773
|
+
return
|
774
|
+
end
|
775
|
+
|
776
|
+
# puts "debug2 write src #{ written }"
|
777
|
+
data = data[ written..-1 ]
|
778
|
+
src_info[ from ] = data
|
779
|
+
end
|
780
|
+
|
781
|
+
##
|
782
|
+
# write dst
|
783
|
+
#
|
784
|
+
def write_dst( dst )
|
785
|
+
dst_info = @dst_infos[ dst ]
|
786
|
+
data = dst_info[ :cache ]
|
787
|
+
from = :cache
|
788
|
+
|
789
|
+
if data.empty?
|
790
|
+
if dst_info[ :chunks ].any?
|
791
|
+
path = File.join( @dst_chunk_dir, dst_info[ :chunks ].shift )
|
792
|
+
|
793
|
+
begin
|
794
|
+
dst_info[ :cache ] = data = IO.binread( path )
|
795
|
+
File.delete( path )
|
796
|
+
rescue Errno::ENOENT => e
|
797
|
+
puts "p#{ Process.pid } #{ Time.new } read #{ path } #{ e.class }"
|
798
|
+
close_dst( dst )
|
799
|
+
return
|
800
|
+
end
|
801
|
+
else
|
802
|
+
data = dst_info[ :wbuff ]
|
803
|
+
from = :wbuff
|
804
|
+
end
|
805
|
+
end
|
806
|
+
|
807
|
+
if data.empty?
|
808
|
+
if dst_info[ :is_closing ]
|
809
|
+
close_dst( dst )
|
810
|
+
else
|
811
|
+
@writes.delete( dst )
|
812
|
+
end
|
813
|
+
|
814
|
+
return
|
815
|
+
end
|
816
|
+
|
817
|
+
begin
|
818
|
+
written = dst.write_nonblock( data )
|
819
|
+
rescue IO::WaitWritable, Errno::EINTR
|
820
|
+
return
|
821
|
+
rescue Exception => e
|
822
|
+
# puts "debug1 write dst #{ e.class }"
|
823
|
+
close_dst( dst )
|
824
|
+
return
|
825
|
+
end
|
826
|
+
|
827
|
+
# puts "debug2 write dst #{ written }"
|
828
|
+
data = data[ written..-1 ]
|
829
|
+
dst_info[ from ] = data
|
830
|
+
end
|
831
|
+
|
832
|
+
##
|
833
|
+
# write tun
|
834
|
+
#
|
835
|
+
def write_tun( tun )
|
836
|
+
if @tun_info[ :is_closing ]
|
837
|
+
close_tun( tun )
|
838
|
+
return
|
839
|
+
end
|
840
|
+
|
841
|
+
now = Time.new
|
842
|
+
|
843
|
+
# 传ctlmsg
|
844
|
+
while @tun_info[ :ctlmsgs ].any?
|
845
|
+
to_addr, data = @tun_info[ :ctlmsgs ].first
|
846
|
+
|
847
|
+
begin
|
848
|
+
tun.sendmsg( data, 0, to_addr )
|
849
|
+
rescue IO::WaitWritable, Errno::EINTR
|
850
|
+
return
|
851
|
+
end
|
852
|
+
|
853
|
+
@tun_info[ :ctlmsgs ].shift
|
854
|
+
end
|
855
|
+
|
856
|
+
# 重传
|
857
|
+
while @tun_info[ :resendings ].any?
|
858
|
+
src_addr, pack_id = @tun_info[ :resendings ].first
|
859
|
+
src_ext = @tun_info[ :src_exts ][ src_addr ]
|
860
|
+
|
861
|
+
if src_ext
|
862
|
+
data = src_ext[ :wmems ][ pack_id ]
|
863
|
+
|
864
|
+
if data
|
865
|
+
begin
|
866
|
+
tun.sendmsg( data, 0, @tun_info[ :tund_addr ] )
|
867
|
+
rescue IO::WaitWritable, Errno::EINTR
|
868
|
+
return
|
869
|
+
end
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
@tun_info[ :resendings ].shift
|
874
|
+
return
|
875
|
+
end
|
876
|
+
|
877
|
+
# 若写后达到上限,暂停取写前
|
878
|
+
if @tun_info[ :src_exts ].map{ | _, src_ext | src_ext[ :wmems ].size }.sum >= WMEMS_LIMIT
|
879
|
+
unless @tun_info[ :paused ]
|
880
|
+
puts "p#{ Process.pid } #{ Time.new } pause tun"
|
881
|
+
@tun_info[ :paused ] = true
|
882
|
+
end
|
883
|
+
|
884
|
+
@writes.delete( tun )
|
885
|
+
return
|
886
|
+
end
|
887
|
+
|
888
|
+
# 取写前
|
889
|
+
if @tun_info[ :caches ].any?
|
890
|
+
src_addr, data = @tun_info[ :caches ].first
|
891
|
+
from = :caches
|
892
|
+
elsif @tun_info[ :chunks ].any?
|
893
|
+
path = File.join( @tun_chunk_dir, @tun_info[ :chunks ].shift )
|
894
|
+
|
895
|
+
begin
|
896
|
+
data = IO.binread( path )
|
897
|
+
File.delete( path )
|
898
|
+
rescue Errno::ENOENT => e
|
899
|
+
puts "p#{ Process.pid } #{ Time.new } read #{ path } #{ e.class }"
|
900
|
+
close_tun( tun )
|
901
|
+
return
|
902
|
+
end
|
903
|
+
|
904
|
+
caches = []
|
905
|
+
|
906
|
+
until data.empty?
|
907
|
+
_src_addr = data[ 0, 16 ]
|
908
|
+
pack_size = data[ 16, 2 ].unpack( 'n' ).first
|
909
|
+
caches << [ _src_addr, data[ 18, pack_size ] ]
|
910
|
+
data = data[ ( 18 + pack_size )..-1 ]
|
911
|
+
end
|
912
|
+
|
913
|
+
@tun_info[ :caches ] = caches
|
914
|
+
src_addr, data = caches.first
|
915
|
+
from = :caches
|
916
|
+
elsif @tun_info[ :wbuffs ].any?
|
917
|
+
src_addr, data = @tun_info[ :wbuffs ].first
|
918
|
+
from = :wbuffs
|
919
|
+
else
|
920
|
+
@writes.delete( tun )
|
921
|
+
return
|
922
|
+
end
|
923
|
+
|
924
|
+
src_ext = @tun_info[ :src_exts ][ src_addr ]
|
925
|
+
|
926
|
+
if src_ext
|
927
|
+
pack_id = src_ext[ :biggest_pack_id ] + 1
|
928
|
+
|
929
|
+
if pack_id <= CONFUSE_UNTIL
|
930
|
+
data = @custom.encode( data )
|
931
|
+
# puts "debug1 encoded pack #{ pack_id }"
|
932
|
+
end
|
933
|
+
|
934
|
+
data = [ [ pack_id ].pack( 'Q>' ), src_addr, data ].join
|
935
|
+
|
936
|
+
begin
|
937
|
+
tun.sendmsg( data, 0, @tun_info[ :tund_addr ] )
|
938
|
+
rescue IO::WaitWritable, Errno::EINTR
|
939
|
+
return
|
940
|
+
end
|
941
|
+
|
942
|
+
# puts "debug2 written pack #{ pack_id }"
|
943
|
+
src_ext[ :biggest_pack_id ] = pack_id
|
944
|
+
src_ext[ :wmems ][ pack_id ] = data
|
945
|
+
src_ext[ :send_ats ][ pack_id ] = now
|
946
|
+
src_ext[ :last_continue_at ] = now
|
947
|
+
end
|
948
|
+
|
949
|
+
@tun_info[ from ].shift
|
950
|
+
end
|
951
|
+
|
952
|
+
##
|
953
|
+
# read dotr
|
954
|
+
#
|
955
|
+
def read_dotr( dotr )
|
956
|
+
dotr.read( 1 )
|
957
|
+
end
|
958
|
+
|
959
|
+
##
|
960
|
+
# read proxy
|
961
|
+
#
|
962
|
+
def read_proxy( proxy )
|
963
|
+
begin
|
964
|
+
src, addrinfo = proxy.accept_nonblock
|
965
|
+
rescue IO::WaitReadable, Errno::EINTR
|
966
|
+
return
|
967
|
+
end
|
968
|
+
|
969
|
+
# puts "debug1 accept a src #{ addrinfo.inspect }"
|
970
|
+
src_addr = addrinfo.to_sockaddr
|
971
|
+
@src_infos[ src ] = {
|
972
|
+
src_addr: src_addr, # src地址
|
973
|
+
proxy_proto: :uncheck, # :uncheck / :http / :socks5
|
974
|
+
proxy_type: :uncheck, # :uncheck / :checking / :direct / :tunnel / :negotiation
|
975
|
+
dst: nil, # :direct的场合,对应的dst
|
976
|
+
destination_domain: nil, # 目的地域名
|
977
|
+
destination_port: nil, # 目的地端口
|
978
|
+
rbuffs: [], # 非CONNECT,dst或者远端dst未准备好,暂存流量
|
979
|
+
wbuff: '', # 写前
|
980
|
+
cache: '', # 块读出缓存
|
981
|
+
chunks: [], # 块队列,写前达到块大小时结一个块 filename
|
982
|
+
spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
|
983
|
+
created_at: Time.new, # 创建时间
|
984
|
+
last_recv_at: nil, # 上一次收到流量的时间,过期关闭
|
985
|
+
is_closing: false # 是否准备关闭
|
986
|
+
}
|
987
|
+
|
988
|
+
add_read( src, :src )
|
989
|
+
end
|
990
|
+
|
991
|
+
##
|
992
|
+
# read src
|
993
|
+
#
|
994
|
+
def read_src( src )
|
995
|
+
begin
|
996
|
+
data = src.read_nonblock( PROXY_PACK_SIZE )
|
997
|
+
rescue IO::WaitReadable, Errno::EINTR
|
998
|
+
return
|
999
|
+
rescue Exception => e
|
1000
|
+
# puts "debug1 read src #{ e.class }"
|
1001
|
+
set_is_closing( src )
|
1002
|
+
return
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
# puts "debug2 read src #{ data.inspect }"
|
1006
|
+
src_info = @src_infos[ src ]
|
1007
|
+
src_info[ :last_recv_at ] = Time.new
|
1008
|
+
proxy_type = src_info[ :proxy_type ]
|
1009
|
+
|
1010
|
+
case proxy_type
|
1011
|
+
when :uncheck
|
1012
|
+
if data[ 0, 7 ] == 'CONNECT'
|
1013
|
+
# puts "debug1 CONNECT"
|
1014
|
+
domain_and_port = data.split( "\r\n" )[ 0 ].split( ' ' )[ 1 ]
|
1015
|
+
elsif data[ 0 ].unpack( 'C' ).first == 5
|
1016
|
+
# puts "debug1 socks5 #{ data.inspect }"
|
1017
|
+
|
1018
|
+
# https://tools.ietf.org/html/rfc1928
|
1019
|
+
#
|
1020
|
+
# +----+----------+----------+
|
1021
|
+
# |VER | NMETHODS | METHODS |
|
1022
|
+
# +----+----------+----------+
|
1023
|
+
# | 1 | 1 | 1 to 255 |
|
1024
|
+
# +----+----------+----------+
|
1025
|
+
nmethods = data[ 1 ].unpack( 'C' ).first
|
1026
|
+
methods = data[ 2, nmethods ].unpack( 'C*' )
|
1027
|
+
|
1028
|
+
unless methods.include?( 0 )
|
1029
|
+
puts "p#{ Process.pid } #{ Time.new } miss method 00"
|
1030
|
+
set_is_closing( src )
|
1031
|
+
return
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
# +----+--------+
|
1035
|
+
# |VER | METHOD |
|
1036
|
+
# +----+--------+
|
1037
|
+
# | 1 | 1 |
|
1038
|
+
# +----+--------+
|
1039
|
+
data2 = [ 5, 0 ].pack( 'CC' )
|
1040
|
+
add_src_wbuff( src, data2 )
|
1041
|
+
|
1042
|
+
src_info[ :proxy_proto ] = :socks5
|
1043
|
+
src_info[ :proxy_type ] = :negotiation
|
1044
|
+
|
1045
|
+
return
|
1046
|
+
else
|
1047
|
+
# puts "debug1 not CONNECT #{ data.inspect }"
|
1048
|
+
host_line = data.split( "\r\n" ).find { | _line | _line[ 0, 6 ] == 'Host: ' }
|
1049
|
+
|
1050
|
+
unless host_line
|
1051
|
+
# puts "debug1 not found host line"
|
1052
|
+
set_is_closing( src )
|
1053
|
+
return
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
data, domain_and_port = sub_http_request( data )
|
1057
|
+
|
1058
|
+
unless domain_and_port
|
1059
|
+
# puts "debug1 not HTTP"
|
1060
|
+
domain_and_port = host_line.split( ' ' )[ 1 ]
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
src_info[ :rbuffs ] << data
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
domain, port = domain_and_port.split( ':' )
|
1067
|
+
port = port ? port.to_i : 80
|
1068
|
+
|
1069
|
+
src_info[ :proxy_proto ] = :http
|
1070
|
+
src_info[ :destination_domain ] = domain
|
1071
|
+
src_info[ :destination_port ] = port
|
1072
|
+
|
1073
|
+
resolve_domain( src, domain )
|
1074
|
+
when :checking
|
1075
|
+
# puts "debug1 add src rbuff while checking #{ data.inspect }"
|
1076
|
+
src_info[ :rbuffs ] << data
|
1077
|
+
when :negotiation
|
1078
|
+
# +----+-----+-------+------+----------+----------+
|
1079
|
+
# |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
|
1080
|
+
# +----+-----+-------+------+----------+----------+
|
1081
|
+
# | 1 | 1 | X'00' | 1 | Variable | 2 |
|
1082
|
+
# +----+-----+-------+------+----------+----------+
|
1083
|
+
# puts "debug1 negotiation #{ data.inspect }"
|
1084
|
+
ver, cmd, rsv, atyp = data[ 0, 4 ].unpack( 'C4' )
|
1085
|
+
|
1086
|
+
if cmd == 1
|
1087
|
+
# puts "debug1 socks5 CONNECT"
|
1088
|
+
|
1089
|
+
if atyp == 1
|
1090
|
+
destination_host, destination_port = data[ 4, 6 ].unpack( 'Nn' )
|
1091
|
+
destination_addr = Socket.sockaddr_in( destination_port, destination_host )
|
1092
|
+
destination_addrinfo = Addrinfo.new( destination_addr )
|
1093
|
+
destination_ip = destination_addrinfo.ip_address
|
1094
|
+
src_info[ :destination_domain ] = destination_ip
|
1095
|
+
src_info[ :destination_port ] = destination_port
|
1096
|
+
# puts "debug1 IP V4 address #{ destination_addrinfo.inspect }"
|
1097
|
+
deal_with_destination_ip( src, destination_ip )
|
1098
|
+
elsif atyp == 3
|
1099
|
+
domain_len = data[ 4 ].unpack( 'C' ).first
|
1100
|
+
|
1101
|
+
if ( domain_len + 7 ) == data.bytesize
|
1102
|
+
domain = data[ 5, domain_len ]
|
1103
|
+
port = data[ ( 5 + domain_len ), 2 ].unpack( 'n' ).first
|
1104
|
+
src_info[ :destination_domain ] = domain
|
1105
|
+
src_info[ :destination_port ] = port
|
1106
|
+
# puts "debug1 DOMAINNAME #{ domain } #{ port }"
|
1107
|
+
resolve_domain( src, domain )
|
1108
|
+
end
|
1109
|
+
end
|
1110
|
+
else
|
1111
|
+
puts "p#{ Process.pid } #{ Time.new } socks5 cmd #{ cmd } not implement"
|
1112
|
+
end
|
1113
|
+
when :tunnel
|
1114
|
+
src_addr = src_info[ :src_addr ]
|
1115
|
+
src_ext = @tun_info[ :src_exts ][ src_addr ]
|
1116
|
+
|
1117
|
+
unless src_ext
|
1118
|
+
# puts "debug1 not found src ext"
|
1119
|
+
set_is_closing( src )
|
1120
|
+
return
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
if src_ext[ :dst_port ]
|
1124
|
+
if @tun.closed?
|
1125
|
+
# puts "debug1 tun closed, close src"
|
1126
|
+
set_is_closing( src )
|
1127
|
+
return
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
if src_info[ :rbuffs ].any?
|
1131
|
+
data, _ = sub_http_request( data )
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
add_tun_wbuff( src_addr, data )
|
1135
|
+
else
|
1136
|
+
# puts "debug1 remote dst not ready, save data to src rbuff"
|
1137
|
+
src_info[ :rbuffs ] << data
|
1138
|
+
end
|
1139
|
+
when :direct
|
1140
|
+
dst = src_info[ :dst ]
|
1141
|
+
|
1142
|
+
if dst
|
1143
|
+
if dst.closed?
|
1144
|
+
# puts "debug1 dst closed, close src"
|
1145
|
+
set_is_closing( src )
|
1146
|
+
return
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
if src_info[ :rbuffs ].any?
|
1150
|
+
data, _ = sub_http_request( data )
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
add_dst_wbuff( dst, data )
|
1154
|
+
else
|
1155
|
+
# puts "debug1 dst not ready, save data to src rbuff"
|
1156
|
+
src_info[ :rbuffs ] << data
|
1157
|
+
end
|
1158
|
+
end
|
1159
|
+
end
|
1160
|
+
|
1161
|
+
##
|
1162
|
+
# read dst
|
1163
|
+
#
|
1164
|
+
def read_dst( dst )
|
1165
|
+
begin
|
1166
|
+
data = dst.read_nonblock( PROXY_PACK_SIZE )
|
1167
|
+
rescue IO::WaitReadable, Errno::EINTR
|
1168
|
+
return
|
1169
|
+
rescue Exception => e
|
1170
|
+
# puts "debug1 read dst #{ e.class }"
|
1171
|
+
set_is_closing( dst )
|
1172
|
+
return
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
# puts "debug2 read dst #{ data.inspect }"
|
1176
|
+
dst_info = @dst_infos[ dst ]
|
1177
|
+
dst_info[ :last_recv_at ] = Time.new
|
1178
|
+
src = dst_info[ :src ]
|
1179
|
+
|
1180
|
+
if src.closed?
|
1181
|
+
puts "p#{ Process.pid } #{ Time.new } src closed, close dst"
|
1182
|
+
set_is_closing( dst )
|
1183
|
+
return
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
add_src_wbuff( src, data )
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
##
|
1190
|
+
# read tun
|
1191
|
+
#
|
1192
|
+
def read_tun( tun )
|
1193
|
+
data, addrinfo, rflags, *controls = tun.recvmsg
|
1194
|
+
from_addr = addrinfo.to_sockaddr
|
1195
|
+
now = Time.new
|
1196
|
+
pack_id = data[ 0, 8 ].unpack( 'Q>' ).first
|
1197
|
+
|
1198
|
+
if pack_id == 0
|
1199
|
+
ctl_num = data[ 8 ].unpack( 'C' ).first
|
1200
|
+
|
1201
|
+
case ctl_num
|
1202
|
+
when TUND_PORT
|
1203
|
+
return if ( from_addr != @proxyd_addr ) || @tun_info[ :tund_addr ]
|
1204
|
+
|
1205
|
+
@tun_info[ :last_recv_at ] = now
|
1206
|
+
tund_port = data[ 9, 2 ].unpack( 'n' ).first
|
1207
|
+
|
1208
|
+
# puts "debug1 got tund port #{ tund_port }"
|
1209
|
+
tund_addr = Socket.sockaddr_in( tund_port, @proxyd_host )
|
1210
|
+
@tun_info[ :tund_addr ] = tund_addr
|
1211
|
+
|
1212
|
+
if @tun_info[ :ctlmsg_rbuffs ].any?
|
1213
|
+
# puts "debug1 move #{ @tun_info[ :ctlmsg_rbuffs ].size } ctlmsg rbuffs to ctlmsgs"
|
1214
|
+
@tun_info[ :ctlmsgs ] += @tun_info[ :ctlmsg_rbuffs ].map{ | _data | [ tund_addr, _data ] }
|
1215
|
+
@tun_info[ :ctlmsg_rbuffs ].clear
|
1216
|
+
add_write( tun )
|
1217
|
+
end
|
1218
|
+
when PAIRED
|
1219
|
+
return if from_addr != @tun_info[ :tund_addr ]
|
1220
|
+
|
1221
|
+
src_addr = data[ 9, 16 ]
|
1222
|
+
dst_port = data[ 25, 2 ].unpack( 'n' ).first
|
1223
|
+
|
1224
|
+
src_ext = @tun_info[ :src_exts ][ src_addr ]
|
1225
|
+
return if src_ext.nil? || src_ext[ :dst_port ]
|
1226
|
+
|
1227
|
+
src = src_ext[ :src ]
|
1228
|
+
return if src.closed?
|
1229
|
+
|
1230
|
+
@tun_info[ :last_recv_at ] = now
|
1231
|
+
|
1232
|
+
# puts "debug1 got paired #{ Addrinfo.new( src_addr ).inspect } #{ dst_port }"
|
1233
|
+
|
1234
|
+
if dst_port == 0
|
1235
|
+
set_is_closing( src )
|
1236
|
+
return
|
1237
|
+
end
|
1238
|
+
|
1239
|
+
src_ext[ :dst_port ] = dst_port
|
1240
|
+
@tun_info[ :src_addrs ][ dst_port ] = src_addr
|
1241
|
+
|
1242
|
+
src_info = @src_infos[ src ]
|
1243
|
+
|
1244
|
+
if src_info[ :proxy_proto ] == :http
|
1245
|
+
datas = src_info[ :rbuffs ]
|
1246
|
+
|
1247
|
+
if datas.empty?
|
1248
|
+
# CONNECT
|
1249
|
+
# puts "debug1 add src wbuff http ok"
|
1250
|
+
add_src_wbuff( src, HTTP_OK )
|
1251
|
+
else
|
1252
|
+
# not CONNECT
|
1253
|
+
# puts "debug1 add src rbuffs to tun wbuffs"
|
1254
|
+
|
1255
|
+
datas.each do | _data |
|
1256
|
+
add_tun_wbuff( src_addr, _data )
|
1257
|
+
end
|
1258
|
+
end
|
1259
|
+
elsif src_info[ :proxy_proto ] == :socks5
|
1260
|
+
add_src_wbuff_socks5_conn_reply( src_ext[ :src ] )
|
1261
|
+
end
|
1262
|
+
when DEST_STATUS
|
1263
|
+
return if from_addr != @tun_info[ :tund_addr ]
|
1264
|
+
|
1265
|
+
dst_port, biggest_dst_pack_id, continue_src_pack_id = data[ 9, 18 ].unpack( 'nQ>Q>' )
|
1266
|
+
|
1267
|
+
src_addr = @tun_info[ :src_addrs ][ dst_port ]
|
1268
|
+
return unless src_addr
|
1269
|
+
|
1270
|
+
src_ext = @tun_info[ :src_exts ][ src_addr ]
|
1271
|
+
return unless src_ext
|
1272
|
+
|
1273
|
+
# puts "debug2 got dest status"
|
1274
|
+
@tun_info[ :last_recv_at ] = now
|
1275
|
+
|
1276
|
+
# 更新对面发到几
|
1277
|
+
if biggest_dst_pack_id > src_ext[ :biggest_dst_pack_id ]
|
1278
|
+
# puts "debug2 update biggest dst pack #{ biggest_dst_pack_id }"
|
1279
|
+
src_ext[ :biggest_dst_pack_id ] = biggest_dst_pack_id
|
1280
|
+
|
1281
|
+
# 接到对面状态,若对面已关闭,且最后一个包已经进写前,关闭src
|
1282
|
+
if src_ext[ :is_dst_closed ] && ( biggest_dst_pack_id == src_ext[ :continue_dst_pack_id ] )
|
1283
|
+
# puts "debug1 2-1. recv traffic/fin1/dst status -> dst closed and all traffic received ? -> close src after write"
|
1284
|
+
set_is_closing( src_ext[ :src ] )
|
1285
|
+
end
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
release_wmems( src_ext, continue_src_pack_id )
|
1289
|
+
|
1290
|
+
# 发miss
|
1291
|
+
if !src_ext[ :src ].closed? && ( src_ext[ :continue_dst_pack_id ] < src_ext[ :biggest_dst_pack_id ] )
|
1292
|
+
ranges = []
|
1293
|
+
curr_pack_id = src_ext[ :continue_dst_pack_id ] + 1
|
1294
|
+
|
1295
|
+
src_ext[ :pieces ].keys.sort.each do | pack_id |
|
1296
|
+
if pack_id > curr_pack_id
|
1297
|
+
ranges << [ curr_pack_id, pack_id - 1 ]
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
curr_pack_id = pack_id + 1
|
1301
|
+
end
|
1302
|
+
|
1303
|
+
if curr_pack_id <= src_ext[ :biggest_dst_pack_id ]
|
1304
|
+
ranges << [ curr_pack_id, src_ext[ :biggest_dst_pack_id ] ]
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
pack_count = 0
|
1308
|
+
# puts "debug1 continue/biggest #{ src_ext[ :continue_dst_pack_id ] }/#{ src_ext[ :biggest_dst_pack_id ] } send MISS #{ ranges.size }"
|
1309
|
+
|
1310
|
+
ranges.each do | pack_id_begin, pack_id_end |
|
1311
|
+
if pack_count >= BREAK_SEND_MISS
|
1312
|
+
puts "p#{ Process.pid } #{ Time.new } break send miss at #{ pack_id_begin }"
|
1313
|
+
break
|
1314
|
+
end
|
1315
|
+
|
1316
|
+
data2 = [ 0, MISS, dst_port, pack_id_begin, pack_id_end ].pack( 'Q>CnQ>Q>' )
|
1317
|
+
add_tun_ctlmsg( data2 )
|
1318
|
+
pack_count += ( pack_id_end - pack_id_begin + 1 )
|
1319
|
+
end
|
1320
|
+
end
|
1321
|
+
when MISS
|
1322
|
+
return if from_addr != @tun_info[ :tund_addr ]
|
1323
|
+
|
1324
|
+
src_addr = data[ 9, 16 ]
|
1325
|
+
pack_id_begin, pack_id_end = data[ 25, 16 ].unpack( 'Q>Q>' )
|
1326
|
+
|
1327
|
+
src_ext = @tun_info[ :src_exts ][ src_addr ]
|
1328
|
+
return unless src_ext
|
1329
|
+
|
1330
|
+
@tun_info[ :last_recv_at ] = now
|
1331
|
+
|
1332
|
+
( pack_id_begin..pack_id_end ).each do | pack_id |
|
1333
|
+
send_at = src_ext[ :send_ats ][ pack_id ]
|
1334
|
+
|
1335
|
+
if send_at
|
1336
|
+
break if now - send_at < STATUS_INTERVAL
|
1337
|
+
@tun_info[ :resendings ] << [ src_addr, pack_id ]
|
1338
|
+
end
|
1339
|
+
end
|
1340
|
+
|
1341
|
+
add_write( tun )
|
1342
|
+
when FIN1
|
1343
|
+
return if from_addr != @tun_info[ :tund_addr ]
|
1344
|
+
|
1345
|
+
dst_port, biggest_dst_pack_id, continue_src_pack_id = data[ 9, 18 ].unpack( 'nQ>Q>' )
|
1346
|
+
|
1347
|
+
src_addr = @tun_info[ :src_addrs ][ dst_port ]
|
1348
|
+
return unless src_addr
|
1349
|
+
|
1350
|
+
src_ext = @tun_info[ :src_exts ][ src_addr ]
|
1351
|
+
return unless src_ext
|
1352
|
+
|
1353
|
+
@tun_info[ :last_recv_at ] = now
|
1354
|
+
|
1355
|
+
# puts "debug1 got fin1 #{ dst_port } biggest dst pack #{ biggest_dst_pack_id } completed src pack #{ continue_src_pack_id }"
|
1356
|
+
src_ext[ :is_dst_closed ] = true
|
1357
|
+
src_ext[ :biggest_dst_pack_id ] = biggest_dst_pack_id
|
1358
|
+
release_wmems( src_ext, continue_src_pack_id )
|
1359
|
+
|
1360
|
+
# 接到对面已关闭,若最后一个包已经进写前,关闭src
|
1361
|
+
if ( biggest_dst_pack_id == src_ext[ :continue_dst_pack_id ] )
|
1362
|
+
# puts "debug1 2-1. recv fin1 -> set dst closed -> all traffic received ? -> close src after write"
|
1363
|
+
set_is_closing( src_ext[ :src ] )
|
1364
|
+
end
|
1365
|
+
when FIN2
|
1366
|
+
return if from_addr != @tun_info[ :tund_addr ]
|
1367
|
+
|
1368
|
+
dst_port = data[ 9, 2 ].unpack( 'n' ).first
|
1369
|
+
|
1370
|
+
src_addr = @tun_info[ :src_addrs ][ dst_port ]
|
1371
|
+
return unless src_addr
|
1372
|
+
|
1373
|
+
@tun_info[ :last_recv_at ] = now
|
1374
|
+
|
1375
|
+
# puts "debug1 1-2. recv fin2 -> del src ext"
|
1376
|
+
del_src_ext( src_addr )
|
1377
|
+
when TUND_FIN
|
1378
|
+
return if from_addr != @tun_info[ :tund_addr ]
|
1379
|
+
|
1380
|
+
@tun_info[ :last_recv_at ] = now
|
1381
|
+
|
1382
|
+
puts "p#{ Process.pid } #{ Time.new } recv tund fin"
|
1383
|
+
set_is_closing( tun )
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
return
|
1387
|
+
end
|
1388
|
+
|
1389
|
+
return if from_addr != @tun_info[ :tund_addr ]
|
1390
|
+
|
1391
|
+
dst_port = data[ 8, 2 ].unpack( 'n' ).first
|
1392
|
+
|
1393
|
+
src_addr = @tun_info[ :src_addrs ][ dst_port ]
|
1394
|
+
return unless src_addr
|
1395
|
+
|
1396
|
+
src_ext = @tun_info[ :src_exts ][ src_addr ]
|
1397
|
+
return if src_ext.nil? || src_ext[ :src ].closed?
|
1398
|
+
return if ( pack_id <= src_ext[ :continue_dst_pack_id ] ) || src_ext[ :pieces ].include?( pack_id )
|
1399
|
+
|
1400
|
+
@tun_info[ :last_recv_at ] = now
|
1401
|
+
|
1402
|
+
data = data[ 10..-1 ]
|
1403
|
+
# puts "debug2 got pack #{ pack_id }"
|
1404
|
+
|
1405
|
+
if pack_id <= CONFUSE_UNTIL
|
1406
|
+
# puts "debug2 #{ data.inspect }"
|
1407
|
+
data = @custom.decode( data )
|
1408
|
+
# puts "debug1 decoded pack #{ pack_id }"
|
1409
|
+
end
|
1410
|
+
|
1411
|
+
# 放进写前,跳号放碎片缓存
|
1412
|
+
if pack_id - src_ext[ :continue_dst_pack_id ] == 1
|
1413
|
+
while src_ext[ :pieces ].include?( pack_id + 1 )
|
1414
|
+
data << src_ext[ :pieces ].delete( pack_id + 1 )
|
1415
|
+
pack_id += 1
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
src_ext[ :continue_dst_pack_id ] = pack_id
|
1419
|
+
src_ext[ :last_continue_at ] = now
|
1420
|
+
add_src_wbuff( src_ext[ :src ], data )
|
1421
|
+
# puts "debug2 update continue dst pack #{ pack_id }"
|
1422
|
+
|
1423
|
+
# 接到流量,若对面已关闭,且流量正好收全,关闭src
|
1424
|
+
if src_ext[ :is_dst_closed ] && ( pack_id == src_ext[ :biggest_dst_pack_id ] )
|
1425
|
+
# puts "debug1 2-1. recv traffic/fin1/dst status -> dst closed and all traffic received ? -> close src after write"
|
1426
|
+
set_is_closing( src_ext[ :src ] )
|
1427
|
+
end
|
1428
|
+
else
|
1429
|
+
src_ext[ :pieces ][ pack_id ] = data
|
1430
|
+
end
|
1431
|
+
end
|
1432
|
+
|
1433
|
+
end
|
1434
|
+
end
|