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