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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6aa6292b009298e1ef9633f12f0ef40b9a6aff55043c66c9682d2a899bcf9770
4
- data.tar.gz: '09d42bb60e5810e810ca3f180bae4e9ad4f987c6fc192e3308401de9bc0746b1'
3
+ metadata.gz: f7ae98a97c1517a3cabb320198b9e0d909b74a6c610f7b6e0109b6248b60b530
4
+ data.tar.gz: 8c987e8a39f60fe03261ad1b9caadac7c6b6faf3bdb898866d4c507d3a492571
5
5
  SHA512:
6
- metadata.gz: 32685a1122f659fcad2e903befb721198ea646b9e61aab08df0d5480b4b2cee77b85433bb3bff2c5fda00487fedfd89518648346473ba59fa48ae1772b611175
7
- data.tar.gz: 9db2a3b2daafcbd15c19c8b22afe47ae9ac9a04a893b2c8f88b9e59d8ae5f9f9614b5bfd9cb731c2ccb102178ad73e94238b263f9738d9f1b895ddf8e94c67c4
6
+ metadata.gz: 468a1ae9530407076582f1c6fc92cf56e3d36b87dbfc9805b7b2dd3a6b4f77294dd14e98d097ed2ab5b50203a569bffd8b572df3acd8caa07cc8b5c890721c04
7
+ data.tar.gz: c6ef17b8a4dc02a5bd037f4700577c2669d7642b34c51fed9a86ec076e43b7a3810401a24541bd90958a2169406496dbd22a8cf9b1cc062b908a3c95c249ed55
@@ -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
@@ -1,4 +1,2 @@
1
- require 'girl/resolv'
2
- require 'girl/resolvd'
3
- require 'girl/tun'
4
- require 'girl/tund'
1
+ require 'girl/proxy'
2
+ require 'girl/proxyd'
@@ -0,0 +1,12 @@
1
+ module Girl
2
+ module Custom
3
+ def encode( data )
4
+ # overwrite me, you'll be free
5
+ data
6
+ end
7
+
8
+ def decode( data )
9
+ data
10
+ end
11
+ end
12
+ end
@@ -1,14 +1,20 @@
1
1
  module Girl
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
- WBUFFS_LIMIT = 1000 # 写前上限,超过上限结一个块
5
- WMEMS_LIMIT = 100_000 # 写后上限,达到上限暂停写
6
- RESUME_BELOW = 50_000 # 降到多少以下恢复写
7
- EXPIRE_AFTER = 1800 # 多久过期
8
- HEARTBEAT_INTERVAL = 3 # 心跳间隔
9
- STATUS_INTERVAL = 0.3 # 发送状态间隔
10
- SEND_STATUS_UNTIL = 10 # 持续的告之对面状态,直到没有流量往来,持续多少秒
11
- BREAK_SEND_MISS = 10_000 # miss包个数上限,达到上限忽略要后面的段,可控碎片缓存
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
@@ -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,15 @@
1
+ require 'girl/custom'
2
+
3
+ module Girl
4
+ class ProxyCustom
5
+ include Custom
6
+
7
+ def initialize( im )
8
+ @im = im
9
+ end
10
+
11
+ def hello
12
+ @im
13
+ end
14
+ end
15
+ 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