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.

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