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.

@@ -0,0 +1,108 @@
1
+ require 'etc'
2
+ require 'girl/head'
3
+ require 'girl/proxyd_custom'
4
+ require 'girl/proxyd_worker'
5
+ require 'girl/version'
6
+ require 'json'
7
+ require 'socket'
8
+
9
+ ##
10
+ # Girl::Proxyd - 代理服务,远端。
11
+ #
12
+ # close logic
13
+ # ===========
14
+ #
15
+ # 1-1. after close dst -> src closed ? no -> send fin1
16
+ # 1-2. recv fin2 -> del dst ext
17
+ #
18
+ # 2-1. recv traffic/fin1/src status -> src closed and all traffic received ? -> close dst after write
19
+ # 2-2. after close dst -> src closed ? yes -> del dst ext -> send fin2
20
+ #
21
+ module Girl
22
+ class Proxyd
23
+
24
+ def initialize( config_path = nil )
25
+ if config_path
26
+ unless File.exist?( config_path )
27
+ raise "not found config file #{ config_path }"
28
+ end
29
+
30
+ conf = JSON.parse( IO.binread( config_path ), symbolize_names: true )
31
+ proxyd_port = conf[ :proxyd_port ]
32
+ proxyd_tmp_dir = conf[ :proxyd_tmp_dir ]
33
+ worker_count = conf[ :worker_count ]
34
+ end
35
+
36
+ unless proxyd_port
37
+ proxyd_port = 6060
38
+ end
39
+
40
+ unless proxyd_tmp_dir
41
+ proxyd_tmp_dir = '/tmp/girl.proxyd'
42
+ end
43
+
44
+ unless File.exist?( proxyd_tmp_dir )
45
+ Dir.mkdir( proxyd_tmp_dir )
46
+ end
47
+
48
+ dst_chunk_dir = File.join( proxyd_tmp_dir, 'dst.chunk' )
49
+ tund_chunk_dir = File.join( proxyd_tmp_dir, 'tund.chunk' )
50
+
51
+ unless Dir.exist?( proxyd_tmp_dir )
52
+ Dir.mkdir( proxyd_tmp_dir )
53
+ end
54
+
55
+ unless Dir.exist?( dst_chunk_dir )
56
+ Dir.mkdir( dst_chunk_dir )
57
+ end
58
+
59
+ unless Dir.exist?( tund_chunk_dir )
60
+ Dir.mkdir( tund_chunk_dir )
61
+ end
62
+
63
+ nprocessors = Etc.nprocessors
64
+
65
+ if worker_count.nil? || worker_count <= 0 || worker_count > nprocessors
66
+ worker_count = nprocessors
67
+ end
68
+
69
+ title = "girl proxyd #{ Girl::VERSION }"
70
+ puts title
71
+ puts "proxyd port #{ proxyd_port }"
72
+ puts "dst chunk dir #{ dst_chunk_dir }"
73
+ puts "tund chunk dir #{ tund_chunk_dir }"
74
+ puts "worker count #{ worker_count }"
75
+
76
+ $0 = title
77
+ workers = []
78
+
79
+ worker_count.times do | i |
80
+ workers << fork do
81
+ $0 = 'girl proxyd worker'
82
+ worker = Girl::ProxydWorker.new( proxyd_port, dst_chunk_dir, tund_chunk_dir )
83
+
84
+ Signal.trap( :TERM ) do
85
+ puts "w#{ i } exit"
86
+ worker.quit!
87
+ end
88
+
89
+ worker.looping
90
+ end
91
+ end
92
+
93
+ Signal.trap( :TERM ) do
94
+ puts 'trap TERM'
95
+ workers.each do | pid |
96
+ begin
97
+ Process.kill( :TERM, pid )
98
+ rescue Errno::ESRCH => e
99
+ puts e.class
100
+ end
101
+ end
102
+ end
103
+
104
+ Process.waitall
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,12 @@
1
+ require 'girl/custom'
2
+
3
+ module Girl
4
+ class ProxydCustom
5
+ include Custom
6
+
7
+ def check( data, addrinfo )
8
+ :success
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,991 @@
1
+ module Girl
2
+ class ProxydWorker
3
+
4
+ ##
5
+ # initialize
6
+ #
7
+ def initialize( proxyd_port, dst_chunk_dir, tund_chunk_dir )
8
+ @dst_chunk_dir = dst_chunk_dir
9
+ @tund_chunk_dir = tund_chunk_dir
10
+ @custom = Girl::ProxydCustom.new
11
+ @mutex = Mutex.new
12
+ @reads = []
13
+ @writes = []
14
+ @roles = {} # sock => :dotr / :proxyd / :dst / :tund
15
+ @dst_infos = {} # dst => {}
16
+ @tunds = {} # port => tund
17
+ @tund_infos = {} # tund => {}
18
+ @tunneling_tunds = {} # tunneling_addr => tund
19
+ @resolv_caches = {} # domain => [ ip, created_at ]
20
+
21
+ dotr, dotw = IO.pipe
22
+ @dotw = dotw
23
+ add_read( dotr, :dotr )
24
+ new_a_proxyd( proxyd_port )
25
+ end
26
+
27
+ ##
28
+ # looping
29
+ #
30
+ def looping
31
+ puts "p#{ Process.pid } #{ Time.new } looping"
32
+ loop_check_expire
33
+ loop_check_status
34
+
35
+ loop do
36
+ rs, ws = IO.select( @reads, @writes )
37
+
38
+ @mutex.synchronize do
39
+ # 先写,再读
40
+ ws.each do | sock |
41
+ case @roles[ sock ]
42
+ when :proxyd
43
+ write_proxyd( sock )
44
+ when :dst
45
+ write_dst( sock )
46
+ when :tund
47
+ write_tund( sock )
48
+ end
49
+ end
50
+
51
+ rs.each do | sock |
52
+ case @roles[ sock ]
53
+ when :dotr
54
+ read_dotr( sock )
55
+ when :proxyd
56
+ read_proxyd( sock )
57
+ when :dst
58
+ read_dst( sock )
59
+ when :tund
60
+ read_tund( sock )
61
+ end
62
+ end
63
+ end
64
+ end
65
+ rescue Interrupt => e
66
+ puts e.class
67
+ quit!
68
+ end
69
+
70
+ ##
71
+ # quit!
72
+ #
73
+ def quit!
74
+ data = [ 0, TUND_FIN ].pack( 'Q>C' )
75
+
76
+ @tund_infos.each do | tund, tund_info |
77
+ if !tund.closed? && tund_info[ :tun_addr ]
78
+ # puts "debug1 send tund fin"
79
+ tund.sendmsg( data, 0, tund_info[ :tun_addr ] )
80
+ end
81
+ end
82
+
83
+ # puts "debug1 exit"
84
+ exit
85
+ end
86
+
87
+ private
88
+
89
+ ##
90
+ # loop check expire
91
+ #
92
+ def loop_check_expire
93
+ Thread.new do
94
+ loop do
95
+ sleep CHECK_EXPIRE_INTERVAL
96
+
97
+ @mutex.synchronize do
98
+ need_trigger = false
99
+ now = Time.new
100
+
101
+ @tund_infos.each do | tund, tund_info |
102
+ unless tund.closed?
103
+ is_expired = tund_info[ :last_recv_at ] ? ( now - tund_info[ :last_recv_at ] > EXPIRE_AFTER ) : ( now - tund_info[ :created_at ] > EXPIRE_NEW )
104
+
105
+ if is_expired
106
+ puts "p#{ Process.pid } #{ Time.new } expire tund"
107
+ set_is_closing( tund )
108
+ need_trigger = true
109
+ else
110
+ tund_info[ :dst_exts ].each do | dst_local_port, dst_ext |
111
+ if dst_ext[ :dst ].closed? && ( now - dst_ext[ :last_continue_at ] > EXPIRE_AFTER )
112
+ puts "p#{ Process.pid } #{ Time.new } expire dst_ext #{ dst_local_port }"
113
+ del_dst_ext( tund, dst_local_port )
114
+ need_trigger = true
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ @dst_infos.each do | dst, dst_info |
122
+ is_expired = dst_info[ :last_recv_at ] ? ( now - dst_info[ :last_recv_at ] > EXPIRE_AFTER ) : ( now - dst_info[ :created_at ] > EXPIRE_NEW )
123
+
124
+ if is_expired
125
+ puts "p#{ Process.pid } #{ Time.new } expire dst"
126
+ set_is_closing( dst )
127
+ need_trigger = true
128
+ end
129
+ end
130
+
131
+ if need_trigger
132
+ next_tick
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ ##
140
+ # loop check status
141
+ #
142
+ def loop_check_status
143
+ Thread.new do
144
+ loop do
145
+ sleep STATUS_INTERVAL
146
+
147
+ if @tunds.any?
148
+ @mutex.synchronize do
149
+ need_trigger = false
150
+
151
+ @tunds.each do | tund_port, tund |
152
+ tund_info = @tund_infos[ tund ]
153
+
154
+ if tund_info[ :dst_exts ].any?
155
+ now = Time.new
156
+
157
+ tund_info[ :dst_exts ].each do | dst_local_port, dst_ext |
158
+ if now - dst_ext[ :last_continue_at ] < SEND_STATUS_UNTIL
159
+ data = [ 0, DEST_STATUS, dst_local_port, dst_ext[ :biggest_pack_id ], dst_ext[ :continue_src_pack_id ] ].pack( 'Q>CnQ>Q>' )
160
+ add_tund_ctlmsg( tund, data )
161
+ need_trigger = true
162
+ end
163
+ end
164
+ end
165
+
166
+ if tund_info[ :paused ] && ( tund_info[ :dst_exts ].map{ | _, dst_ext | dst_ext[ :wmems ].size }.sum < RESUME_BELOW )
167
+ puts "p#{ Process.pid } #{ Time.new } resume tund"
168
+ tund_info[ :paused ] = false
169
+ add_write( tund )
170
+ need_trigger = true
171
+ end
172
+ end
173
+
174
+ if need_trigger
175
+ next_tick
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ ##
184
+ # resolve domain
185
+ #
186
+ def resolve_domain( tund, src_addr, destination_domain_port )
187
+ resolv_cache = @resolv_caches[ destination_domain_port ]
188
+
189
+ if resolv_cache
190
+ destination_addr, created_at = resolv_cache
191
+
192
+ if Time.new - created_at < RESOLV_CACHE_EXPIRE
193
+ # puts "debug1 #{ destination_domain_port } hit resolv cache #{ Addrinfo.new( destination_addr ).inspect }"
194
+ deal_with_destination_addr( tund, src_addr, destination_addr )
195
+ return
196
+ end
197
+
198
+ # puts "debug1 expire #{ destination_domain_port } resolv cache"
199
+ @resolv_caches.delete( destination_domain_port )
200
+ end
201
+
202
+ Thread.new do
203
+ destination_domain, destination_port = destination_domain_port.split( ':' )
204
+ destination_port = destination_port.to_i
205
+
206
+ begin
207
+ destination_addr = Socket.sockaddr_in( destination_port, destination_domain )
208
+ rescue Exception => e
209
+ puts "p#{ Process.pid } #{ Time.new } sockaddr in #{ destination_domain_port } #{ e.class }"
210
+ end
211
+
212
+ @mutex.synchronize do
213
+ if destination_addr
214
+ # puts "debug1 resolved #{ destination_domain_port } #{ Addrinfo.new( destination_addr ).inspect }"
215
+ @resolv_caches[ destination_domain_port ] = [ destination_addr, Time.new ]
216
+
217
+ unless tund.closed?
218
+ if deal_with_destination_addr( tund, src_addr, destination_addr )
219
+ next_tick
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ ##
228
+ # deal with destination addr
229
+ #
230
+ def deal_with_destination_addr( tund, src_addr, destination_addr )
231
+ dst = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
232
+ dst.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
233
+
234
+ begin
235
+ dst.connect_nonblock( destination_addr )
236
+ rescue IO::WaitWritable
237
+ rescue Exception => e
238
+ puts "p#{ Process.pid } #{ Time.new } connect destination #{ e.class }"
239
+ return false
240
+ end
241
+
242
+ local_port = dst.local_address.ip_unpack.last
243
+
244
+ @dst_infos[ dst ] = {
245
+ local_port: local_port, # 本地端口
246
+ tund: tund, # 对应tund
247
+ wbuff: '', # 写前
248
+ cache: '', # 块读出缓存
249
+ chunks: [], # 块队列,写前达到块大小时结一个块 filename
250
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
251
+ created_at: Time.new, # 创建时间
252
+ last_recv_at: nil, # 上一次收到流量的时间,过期关闭
253
+ is_closing: false # 是否准备关闭
254
+ }
255
+ add_read( dst, :dst )
256
+
257
+ tund_info = @tund_infos[ tund ]
258
+ tund_info[ :dst_local_ports ][ src_addr ] = local_port
259
+ tund_info[ :dst_exts ][ local_port ] = {
260
+ dst: dst, # dst
261
+ src_addr: src_addr, # 近端src地址
262
+ wmems: {}, # 写后 pack_id => data
263
+ send_ats: {}, # 上一次发出时间 pack_id => send_at
264
+ biggest_pack_id: 0, # 发到几
265
+ continue_src_pack_id: 0, # 收到几
266
+ pieces: {}, # 跳号包 src_pack_id => data
267
+ is_src_closed: false, # 对面是否已关闭
268
+ biggest_src_pack_id: 0, # 对面发到几
269
+ completed_pack_id: 0, # 完成到几(对面收到几)
270
+ last_continue_at: Time.new # 创建,或者上一次收到连续流量,或者发出新包的时间
271
+ }
272
+
273
+ data = [ [ 0, PAIRED ].pack( 'Q>C' ), src_addr, [ local_port ].pack( 'n' ) ].join
274
+ # puts "debug1 add ctlmsg paired #{ data.inspect }"
275
+ add_tund_ctlmsg( tund, data )
276
+
277
+ true
278
+ end
279
+
280
+ ##
281
+ # new a proxyd
282
+ #
283
+ def new_a_proxyd( proxyd_port )
284
+ proxyd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
285
+ proxyd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
286
+ proxyd.bind( Socket.sockaddr_in( proxyd_port, '0.0.0.0' ) )
287
+
288
+ puts "p#{ Process.pid } #{ Time.new } proxyd bind on #{ proxyd_port }"
289
+ @proxyd = proxyd
290
+ @proxyd_ctlmsgs = [] # [ to_addr, data ]
291
+ add_read( proxyd, :proxyd )
292
+ end
293
+
294
+ ##
295
+ # add proxyd ctlmsg
296
+ #
297
+ def add_proxyd_ctlmsg( data, to_addr )
298
+ @proxyd_ctlmsgs << [ to_addr, data ]
299
+ add_write( @proxyd )
300
+ end
301
+
302
+ ##
303
+ # add tund ctlmsg
304
+ #
305
+ def add_tund_ctlmsg( tund, data )
306
+ tund_info = @tund_infos[ tund ]
307
+ tund_info[ :ctlmsgs ] << data
308
+ add_write( tund )
309
+ end
310
+
311
+ ##
312
+ # add tund wbuff
313
+ #
314
+ def add_tund_wbuff( tund, dst_local_port, data )
315
+ tund_info = @tund_infos[ tund ]
316
+ tund_info[ :wbuffs ] << [ dst_local_port, data ]
317
+
318
+ if tund_info[ :wbuffs ].size >= WBUFFS_LIMIT
319
+ spring = tund_info[ :chunks ].size > 0 ? ( tund_info[ :spring ] + 1 ) : 0
320
+ filename = "#{ Process.pid }-#{ tund_info[ :port ] }.#{ spring }"
321
+ chunk_path = File.join( @tund_chunk_dir, filename )
322
+ wbuffs = tund_info[ :wbuffs ].map{ | _dst_local_port, _data | [ [ _dst_local_port, _data.bytesize ].pack( 'nn' ), _data ].join }
323
+
324
+ begin
325
+ IO.binwrite( chunk_path, wbuffs.join )
326
+ rescue Errno::ENOSPC => e
327
+ puts "p#{ Process.pid } #{ Time.new } #{ e.class }, close tund"
328
+ set_is_closing( tund )
329
+ return
330
+ end
331
+
332
+ tund_info[ :chunks ] << filename
333
+ tund_info[ :spring ] = spring
334
+ tund_info[ :wbuffs ].clear
335
+ end
336
+
337
+ add_write( tund )
338
+ end
339
+
340
+ ##
341
+ # add dst wbuff
342
+ #
343
+ def add_dst_wbuff( dst, data )
344
+ dst_info = @dst_infos[ dst ]
345
+ dst_info[ :wbuff ] << data
346
+
347
+ if dst_info[ :wbuff ].bytesize >= PROXY_CHUNK_SIZE
348
+ spring = dst_info[ :chunks ].size > 0 ? ( dst_info[ :spring ] + 1 ) : 0
349
+ filename = "#{ Process.pid }-#{ dst_info[ :local_port ] }.#{ spring }"
350
+ chunk_path = File.join( @dst_chunk_dir, filename )
351
+
352
+ begin
353
+ IO.binwrite( chunk_path, dst_info[ :wbuff ] )
354
+ rescue Errno::ENOSPC => e
355
+ puts "p#{ Process.pid } #{ Time.new } #{ e.class }, close dst"
356
+ set_is_closing( dst )
357
+ return
358
+ end
359
+
360
+ dst_info[ :chunks ] << filename
361
+ dst_info[ :spring ] = spring
362
+ dst_info[ :wbuff ].clear
363
+ end
364
+
365
+ add_write( dst )
366
+ end
367
+
368
+ ##
369
+ # add read
370
+ #
371
+ def add_read( sock, role )
372
+ unless @reads.include?( sock )
373
+ @reads << sock
374
+ end
375
+
376
+ @roles[ sock ] = role
377
+ end
378
+
379
+ ##
380
+ # add write
381
+ #
382
+ def add_write( sock )
383
+ unless @writes.include?( sock )
384
+ @writes << sock
385
+ end
386
+ end
387
+
388
+ ##
389
+ # set is closing
390
+ #
391
+ def set_is_closing( sock )
392
+ if sock && !sock.closed?
393
+ role = @roles[ sock ]
394
+ # puts "debug1 set #{ role.to_s } is closing"
395
+
396
+ case role
397
+ when :dst
398
+ dst_info = @dst_infos[ sock ]
399
+ dst_info[ :is_closing ] = true
400
+ when :tund
401
+ tund_info = @tund_infos[ sock ]
402
+ tund_info[ :is_closing ] = true
403
+ end
404
+
405
+ @reads.delete( sock )
406
+ add_write( sock )
407
+ end
408
+ end
409
+
410
+ ##
411
+ # close dst
412
+ #
413
+ def close_dst( dst )
414
+ # puts "debug1 close dst"
415
+ close_sock( dst )
416
+ dst_info = @dst_infos.delete( dst )
417
+
418
+ dst_info[ :chunks ].each do | filename |
419
+ begin
420
+ File.delete( File.join( @dst_chunk_dir, filename ) )
421
+ rescue Errno::ENOENT
422
+ end
423
+ end
424
+
425
+ tund = dst_info[ :tund ]
426
+ return if tund.closed?
427
+
428
+ tund_info = @tund_infos[ tund ]
429
+ local_port = dst_info[ :local_port ]
430
+ dst_ext = tund_info[ :dst_exts ][ local_port ]
431
+ return unless dst_ext
432
+
433
+ if dst_ext[ :is_src_closed ]
434
+ # puts "debug1 2-2. after close dst -> src closed ? yes -> del dst ext -> send fin2"
435
+ del_dst_ext( tund, local_port )
436
+ data = [ 0, FIN2, local_port ].pack( 'Q>Cn' )
437
+ add_tund_ctlmsg( tund, data )
438
+ else
439
+ # puts "debug1 1-1. after close dst -> src closed ? no -> send fin1"
440
+ data = [ 0, FIN1, local_port, dst_ext[ :biggest_pack_id ], dst_ext[ :continue_src_pack_id ] ].pack( 'Q>CnQ>Q>' )
441
+ add_tund_ctlmsg( tund, data )
442
+ end
443
+ end
444
+
445
+ ##
446
+ # close tun
447
+ #
448
+ def close_tund( tund )
449
+ # puts "debug1 close tund"
450
+ close_sock( tund )
451
+
452
+ tund_info = @tund_infos.delete( tund )
453
+ tund_info[ :chunks ].each do | filename |
454
+ begin
455
+ File.delete( File.join( @tund_chunk_dir, filename ) )
456
+ rescue Errno::ENOENT
457
+ end
458
+ end
459
+
460
+ tund_info[ :dst_exts ].each{ | _, dst_ext | set_is_closing( dst_ext[ :dst ] ) }
461
+ @tunneling_tunds.delete( tund_info[ :tun_addr ] )
462
+ @tunds.delete( tund_info[ :port ] )
463
+ end
464
+
465
+ ##
466
+ # close sock
467
+ #
468
+ def close_sock( sock )
469
+ sock.close
470
+ @reads.delete( sock )
471
+ @writes.delete( sock )
472
+ @roles.delete( sock )
473
+ end
474
+
475
+ ##
476
+ # del dst ext
477
+ #
478
+ def del_dst_ext( tund, dst_local_port )
479
+ tund_info = @tund_infos[ tund ]
480
+ dst_ext = tund_info[ :dst_exts ].delete( dst_local_port )
481
+
482
+ if dst_ext
483
+ tund_info[ :dst_local_ports ].delete( dst_ext[ :src_addr ] )
484
+ end
485
+ end
486
+
487
+ ##
488
+ # release wmems
489
+ #
490
+ def release_wmems( dst_ext, completed_pack_id )
491
+ if completed_pack_id > dst_ext[ :completed_pack_id ]
492
+ # puts "debug2 update completed pack #{ completed_pack_id }"
493
+
494
+ pack_ids = dst_ext[ :wmems ].keys.select { | pack_id | pack_id <= completed_pack_id }
495
+
496
+ pack_ids.each do | pack_id |
497
+ dst_ext[ :wmems ].delete( pack_id )
498
+ dst_ext[ :send_ats ].delete( pack_id )
499
+ end
500
+
501
+ dst_ext[ :completed_pack_id ] = completed_pack_id
502
+ end
503
+ end
504
+
505
+ ##
506
+ # next tick
507
+ #
508
+ def next_tick
509
+ @dotw.write( '.' )
510
+ end
511
+
512
+ ##
513
+ # write proxyd
514
+ #
515
+ def write_proxyd( proxyd )
516
+ while @proxyd_ctlmsgs.any?
517
+ to_addr, data = @proxyd_ctlmsgs.first
518
+
519
+ begin
520
+ proxyd.sendmsg( data, 0, to_addr )
521
+ rescue IO::WaitWritable, Errno::EINTR
522
+ return
523
+ end
524
+
525
+ @proxyd_ctlmsgs.shift
526
+ end
527
+
528
+ @writes.delete( proxyd )
529
+ end
530
+
531
+ ##
532
+ # write dst
533
+ #
534
+ def write_dst( dst )
535
+ dst_info = @dst_infos[ dst ]
536
+ data = dst_info[ :cache ]
537
+ from = :cache
538
+
539
+ if data.empty?
540
+ if dst_info[ :chunks ].any?
541
+ path = File.join( @dst_chunk_dir, dst_info[ :chunks ].shift )
542
+
543
+ begin
544
+ dst_info[ :cache ] = data = IO.binread( path )
545
+ File.delete( path )
546
+ rescue Errno::ENOENT => e
547
+ puts "p#{ Process.pid } #{ Time.new } read #{ path } #{ e.class }"
548
+ close_dst( dst )
549
+ return
550
+ end
551
+ else
552
+ data = dst_info[ :wbuff ]
553
+ from = :wbuff
554
+ end
555
+ end
556
+
557
+ if data.empty?
558
+ if dst_info[ :is_closing ]
559
+ close_dst( dst )
560
+ else
561
+ @writes.delete( dst )
562
+ end
563
+
564
+ return
565
+ end
566
+
567
+ begin
568
+ written = dst.write_nonblock( data )
569
+ rescue IO::WaitWritable, Errno::EINTR
570
+ return
571
+ rescue Exception => e
572
+ # puts "debug1 write dst #{ e.class }"
573
+ close_dst( dst )
574
+ return
575
+ end
576
+
577
+ # puts "debug2 write dst #{ written }"
578
+ data = data[ written..-1 ]
579
+ dst_info[ from ] = data
580
+ end
581
+
582
+ ##
583
+ # write tund
584
+ #
585
+ def write_tund( tund )
586
+ tund_info = @tund_infos[ tund ]
587
+
588
+ if tund_info[ :is_closing ]
589
+ close_tund( tund )
590
+ return
591
+ end
592
+
593
+ # 传ctlmsg
594
+ while tund_info[ :ctlmsgs ].any?
595
+ data = tund_info[ :ctlmsgs ].first
596
+
597
+ begin
598
+ tund.sendmsg( data, 0, tund_info[ :tun_addr ] )
599
+ rescue IO::WaitWritable, Errno::EINTR
600
+ return
601
+ end
602
+
603
+ tund_info[ :ctlmsgs ].shift
604
+ end
605
+
606
+ # 重传
607
+ while tund_info[ :resendings ].any?
608
+ dst_local_port, pack_id = tund_info[ :resendings ].first
609
+ dst_ext = tund_info[ :dst_exts ][ dst_local_port ]
610
+
611
+ if dst_ext
612
+ data = dst_ext[ :wmems ][ pack_id ]
613
+
614
+ if data
615
+ begin
616
+ tund.sendmsg( data, 0, tund_info[ :tun_addr ] )
617
+ rescue IO::WaitWritable, Errno::EINTR
618
+ return
619
+ end
620
+ end
621
+ end
622
+
623
+ tund_info[ :resendings ].shift
624
+ return
625
+ end
626
+
627
+ # 若写后达到上限,暂停取写前
628
+ if tund_info[ :dst_exts ].map{ | _, dst_ext | dst_ext[ :wmems ].size }.sum >= WMEMS_LIMIT
629
+ unless tund_info[ :paused ]
630
+ puts "p#{ Process.pid } #{ Time.new } pause tund #{ tund_info[ :port ] }"
631
+ tund_info[ :paused ] = true
632
+ end
633
+
634
+ @writes.delete( tund )
635
+ return
636
+ end
637
+
638
+ # 取写前
639
+ if tund_info[ :caches ].any?
640
+ dst_local_port, data = tund_info[ :caches ].first
641
+ from = :caches
642
+ elsif tund_info[ :chunks ].any?
643
+ path = File.join( @tund_chunk_dir, tund_info[ :chunks ].shift )
644
+
645
+ begin
646
+ data = IO.binread( path )
647
+ File.delete( path )
648
+ rescue Errno::ENOENT => e
649
+ puts "p#{ Process.pid } #{ Time.new } read #{ path } #{ e.class }"
650
+ close_tund( tund )
651
+ return
652
+ end
653
+
654
+ caches = []
655
+
656
+ until data.empty?
657
+ _dst_local_port, pack_size = data[ 0, 4 ].unpack( 'nn' )
658
+ caches << [ _dst_local_port, data[ 4, pack_size ] ]
659
+ data = data[ ( 4 + pack_size )..-1 ]
660
+ end
661
+
662
+ tund_info[ :caches ] = caches
663
+ dst_local_port, data = caches.first
664
+ from = :caches
665
+ elsif tund_info[ :wbuffs ].any?
666
+ dst_local_port, data = tund_info[ :wbuffs ].first
667
+ from = :wbuffs
668
+ else
669
+ @writes.delete( tund )
670
+ return
671
+ end
672
+
673
+ dst_ext = tund_info[ :dst_exts ][ dst_local_port ]
674
+
675
+ if dst_ext
676
+ pack_id = dst_ext[ :biggest_pack_id ] + 1
677
+
678
+ if pack_id <= CONFUSE_UNTIL
679
+ data = @custom.encode( data )
680
+ # puts "debug1 encoded pack #{ pack_id }"
681
+ end
682
+
683
+ data = [ [ pack_id, dst_local_port ].pack( 'Q>n' ), data ].join
684
+
685
+ begin
686
+ tund.sendmsg( data, 0, tund_info[ :tun_addr ] )
687
+ rescue IO::WaitWritable, Errno::EINTR
688
+ return
689
+ end
690
+
691
+ # puts "debug2 written pack #{ pack_id }"
692
+ now = Time.new
693
+ dst_ext[ :biggest_pack_id ] = pack_id
694
+ dst_ext[ :wmems ][ pack_id ] = data
695
+ dst_ext[ :send_ats ][ pack_id ] = now
696
+ dst_ext[ :last_continue_at ] = now
697
+ end
698
+
699
+ tund_info[ from ].shift
700
+ end
701
+
702
+ ##
703
+ # read dotr
704
+ #
705
+ def read_dotr( dotr )
706
+ dotr.read( 1 )
707
+ end
708
+
709
+ ##
710
+ # read proxyd
711
+ #
712
+ def read_proxyd( proxyd )
713
+ data, addrinfo, rflags, *controls = proxyd.recvmsg
714
+ from_addr = addrinfo.to_sockaddr
715
+
716
+ return if @tunneling_tunds.include?( from_addr )
717
+
718
+ result = @custom.check( data, addrinfo )
719
+
720
+ if result != :success
721
+ puts "p#{ Process.pid } #{ Time.new } #{ result }"
722
+ return
723
+ end
724
+
725
+ tund = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
726
+ tund.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
727
+ port = tund.local_address.ip_unpack.last
728
+
729
+ @tunneling_tunds[ from_addr ] = tund
730
+ @tunds[ port ] = tund
731
+ @tund_infos[ tund ] = {
732
+ port: port, # 端口
733
+ ctlmsgs: [], # data
734
+ wbuffs: [], # 写前缓存 [ src_dst_addr, data ]
735
+ caches: [], # 块读出缓存 [ src_dst_addr, data ]
736
+ chunks: [], # 块队列 filename
737
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
738
+ tun_addr: from_addr, # tun地址
739
+ is_tunneled: false, # 是否已和tun打通
740
+ dst_exts: {}, # dst额外信息 dst_addr => {}
741
+ dst_local_ports: {}, # src_addr => dst_local_port
742
+ paused: false, # 是否暂停写
743
+ resendings: [], # 重传队列 [ dst_addr, pack_id ]
744
+ created_at: Time.new, # 创建时间
745
+ last_recv_at: nil, # 上一次收到流量的时间,过期关闭
746
+ is_closing: false # 是否准备关闭
747
+ }
748
+
749
+ add_read( tund, :tund )
750
+
751
+ data = [ 0, TUND_PORT, port ].pack( 'Q>Cn' )
752
+ puts "p#{ Process.pid } #{ Time.new } a new tunnel #{ addrinfo.ip_unpack.inspect } - #{ port }, #{ @tunds.size } tunds"
753
+ add_proxyd_ctlmsg( data, from_addr )
754
+ end
755
+
756
+ ##
757
+ # read dst
758
+ #
759
+ def read_dst( dst )
760
+ begin
761
+ data = dst.read_nonblock( PROXY_PACK_SIZE )
762
+ rescue IO::WaitReadable, Errno::EINTR
763
+ return
764
+ rescue Exception => e
765
+ # puts "debug1 read dst #{ e.class }"
766
+ set_is_closing( dst )
767
+ return
768
+ end
769
+
770
+ # puts "debug2 read dst #{ data.inspect }"
771
+ dst_info = @dst_infos[ dst ]
772
+ dst_info[ :last_recv_at ] = Time.new
773
+ tund = dst_info[ :tund ]
774
+
775
+ if tund.closed?
776
+ puts "p#{ Process.pid } #{ Time.new } tund closed, close dst"
777
+ set_is_closing( dst )
778
+ return
779
+ end
780
+
781
+ add_tund_wbuff( tund, dst_info[ :local_port ], data )
782
+ end
783
+
784
+ ##
785
+ # read tund
786
+ #
787
+ def read_tund( tund )
788
+ data, addrinfo, rflags, *controls = tund.recvmsg
789
+ from_addr = addrinfo.to_sockaddr
790
+ now = Time.new
791
+ tund_info = @tund_infos[ tund ]
792
+
793
+ if from_addr != tund_info[ :tun_addr ]
794
+ puts "p#{ Process.pid } #{ Time.new } #{ addrinfo.inspect } not match #{ Addrinfo.new( tund_info[ :tun_addr ] ).inspect }"
795
+ return
796
+ end
797
+
798
+ tund_info[ :is_tunneled ] = true
799
+ pack_id = data[ 0, 8 ].unpack( 'Q>' ).first
800
+
801
+ if pack_id == 0
802
+ ctl_num = data[ 8 ].unpack( 'C' ).first
803
+
804
+ case ctl_num
805
+ when A_NEW_SOURCE
806
+ src_addr = data[ 9, 16 ]
807
+ dst_local_port = tund_info[ :dst_local_ports ][ src_addr ]
808
+ # puts "debug1 got a new source #{ Addrinfo.new( src_addr ).inspect }"
809
+
810
+ if dst_local_port
811
+ dst_ext = tund_info[ :dst_exts ][ dst_local_port ]
812
+ return unless dst_ext
813
+
814
+ if dst_ext[ :dst ].closed?
815
+ dst_local_port = 0
816
+ end
817
+
818
+ # puts "debug1 readd ctlmsg paired #{ dst_local_port }"
819
+ data2 = [ [ 0, PAIRED ].pack( 'Q>C' ), src_addr, [ dst_local_port ].pack( 'n' ) ].join
820
+ add_tund_ctlmsg( tund, data2 )
821
+ return
822
+ end
823
+
824
+ tund_info[ :last_recv_at ] = now
825
+
826
+ data = data[ 25..-1 ]
827
+ # puts "debug1 #{ data }"
828
+ destination_domain_port = @custom.decode( data )
829
+ resolve_domain( tund, src_addr, destination_domain_port )
830
+ when SOURCE_STATUS
831
+ src_addr = data[ 9, 16 ]
832
+ biggest_src_pack_id, continue_dst_pack_id = data[ 25, 16 ].unpack( 'Q>Q>' )
833
+
834
+ dst_local_port = tund_info[ :dst_local_ports ][ src_addr ]
835
+ return unless dst_local_port
836
+
837
+ dst_ext = tund_info[ :dst_exts ][ dst_local_port ]
838
+ return unless dst_ext
839
+
840
+ # puts "debug2 got source status"
841
+ tund_info[ :last_recv_at ] = now
842
+
843
+ # 更新对面发到几
844
+ if biggest_src_pack_id > dst_ext[ :biggest_src_pack_id ]
845
+ # puts "debug2 update biggest src pack #{ biggest_src_pack_id }"
846
+ dst_ext[ :biggest_src_pack_id ] = biggest_src_pack_id
847
+
848
+ # 接到对面状态,若对面已关闭,且最后一个包已经进写前,关闭dst
849
+ if dst_ext[ :is_src_closed ] && ( biggest_src_pack_id == dst_ext[ :continue_src_pack_id ] )
850
+ # puts "debug1 2-1. recv traffic/fin1/src status -> src closed and all traffic received ? -> close dst after write"
851
+ set_is_closing( dst_ext[ :dst ] )
852
+ end
853
+ end
854
+
855
+ release_wmems( dst_ext, continue_dst_pack_id )
856
+
857
+ # 发miss
858
+ if !dst_ext[ :dst ].closed? && ( dst_ext[ :continue_src_pack_id ] < dst_ext[ :biggest_src_pack_id ] )
859
+ ranges = []
860
+ curr_pack_id = dst_ext[ :continue_src_pack_id ] + 1
861
+
862
+ dst_ext[ :pieces ].keys.sort.each do | pack_id |
863
+ if pack_id > curr_pack_id
864
+ ranges << [ curr_pack_id, pack_id - 1 ]
865
+ end
866
+
867
+ curr_pack_id = pack_id + 1
868
+ end
869
+
870
+ if curr_pack_id <= dst_ext[ :biggest_src_pack_id ]
871
+ ranges << [ curr_pack_id, dst_ext[ :biggest_src_pack_id ] ]
872
+ end
873
+
874
+ pack_count = 0
875
+ # puts "debug1 continue/biggest #{ dst_ext[ :continue_src_pack_id ] }/#{ dst_ext[ :biggest_src_pack_id ] } send MISS #{ ranges.size }"
876
+
877
+ ranges.each do | pack_id_begin, pack_id_end |
878
+ if pack_count >= BREAK_SEND_MISS
879
+ puts "p#{ Process.pid } #{ Time.new } break send miss at #{ pack_id_begin }"
880
+ break
881
+ end
882
+
883
+ data2 = [ [ 0, MISS ].pack( 'Q>C' ), src_addr, [ pack_id_begin, pack_id_end ].pack( 'Q>Q>' ) ].join
884
+ add_tund_ctlmsg( tund, data2 )
885
+ pack_count += ( pack_id_end - pack_id_begin + 1 )
886
+ end
887
+ end
888
+ when MISS
889
+ dst_local_port, pack_id_begin, pack_id_end = data[ 9, 18 ].unpack( 'nQ>Q>' )
890
+
891
+ dst_ext = tund_info[ :dst_exts ][ dst_local_port ]
892
+ return unless dst_ext
893
+
894
+ tund_info[ :last_recv_at ] = now
895
+
896
+ ( pack_id_begin..pack_id_end ).each do | pack_id |
897
+ send_at = dst_ext[ :send_ats ][ pack_id ]
898
+
899
+ if send_at
900
+ break if now - send_at < STATUS_INTERVAL
901
+ tund_info[ :resendings ] << [ dst_local_port, pack_id ]
902
+ end
903
+ end
904
+
905
+ add_write( tund )
906
+ when FIN1
907
+ src_addr = data[ 9, 16 ]
908
+ biggest_src_pack_id, continue_dst_pack_id = data[ 25, 16 ].unpack( 'Q>Q>' )
909
+
910
+ dst_local_port = tund_info[ :dst_local_ports ][ src_addr ]
911
+ return unless dst_local_port
912
+
913
+ dst_ext = tund_info[ :dst_exts ][ dst_local_port ]
914
+ return unless dst_ext
915
+
916
+ tund_info[ :last_recv_at ] = now
917
+
918
+ # puts "debug1 got fin1 #{ Addrinfo.new( src_addr ).inspect } biggest src pack #{ biggest_src_pack_id } completed dst pack #{ continue_dst_pack_id }"
919
+ dst_ext[ :is_src_closed ] = true
920
+ dst_ext[ :biggest_src_pack_id ] = biggest_src_pack_id
921
+ release_wmems( dst_ext, continue_dst_pack_id )
922
+
923
+ # 接到对面已关闭,若最后一个包已经进写前,关闭dst
924
+ if biggest_src_pack_id == dst_ext[ :continue_src_pack_id ]
925
+ # puts "debug1 2-1. recv traffic/fin1/src status -> src closed and all traffic received ? -> close dst after write"
926
+ set_is_closing( dst_ext[ :dst ] )
927
+ end
928
+ when FIN2
929
+ src_addr = data[ 9, 16 ]
930
+
931
+ dst_local_port = tund_info[ :dst_local_ports ][ src_addr ]
932
+ return unless dst_local_port
933
+
934
+ tund_info[ :last_recv_at ] = now
935
+
936
+ # puts "debug1 1-2. recv fin2 -> del dst ext"
937
+ del_dst_ext( tund, dst_local_port )
938
+ when TUN_FIN
939
+ puts "p#{ Process.pid } #{ Time.new } recv tun fin"
940
+ tund_info[ :last_recv_at ] = now
941
+ set_is_closing( tund )
942
+ end
943
+
944
+ return
945
+ end
946
+
947
+ src_addr = data[ 8, 16 ]
948
+
949
+ dst_local_port = tund_info[ :dst_local_ports ][ src_addr ]
950
+ return unless dst_local_port
951
+
952
+ dst_ext = tund_info[ :dst_exts ][ dst_local_port ]
953
+ return if dst_ext.nil? || dst_ext[ :dst ].closed?
954
+ return if ( pack_id <= dst_ext[ :continue_src_pack_id ] ) || dst_ext[ :pieces ].include?( pack_id )
955
+
956
+ tund_info[ :last_recv_at ] = now
957
+
958
+ data = data[ 24..-1 ]
959
+ # puts "debug2 got pack #{ pack_id }"
960
+
961
+ if pack_id <= CONFUSE_UNTIL
962
+ # puts "debug2 #{ data.inspect }"
963
+ data = @custom.decode( data )
964
+ # puts "debug1 decoded pack #{ pack_id }"
965
+ end
966
+
967
+ # 放进写前,跳号放碎片缓存
968
+ if pack_id - dst_ext[ :continue_src_pack_id ] == 1
969
+ while dst_ext[ :pieces ].include?( pack_id + 1 )
970
+ data << dst_ext[ :pieces ].delete( pack_id + 1 )
971
+ pack_id += 1
972
+ end
973
+
974
+ dst_ext[ :continue_src_pack_id ] = pack_id
975
+ dst_ext[ :last_continue_at ] = now
976
+ add_dst_wbuff( dst_ext[ :dst ], data )
977
+ # puts "debug2 update continue src pack #{ pack_id }"
978
+
979
+ # 接到流量,若对面已关闭,且流量正好收全,关闭dst
980
+ if dst_ext[ :is_src_closed ] && ( pack_id == dst_ext[ :biggest_src_pack_id ] )
981
+ # puts "debug1 2-1. recv traffic/fin1/src status -> src closed and all traffic received ? -> close dst after write"
982
+ set_is_closing( dst_ext[ :dst ] )
983
+ return
984
+ end
985
+ else
986
+ dst_ext[ :pieces ][ pack_id ] = data
987
+ end
988
+ end
989
+
990
+ end
991
+ end