girl 0.93.0 → 0.98.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of girl might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/girl/head.rb +3 -0
- data/lib/girl/proxy.rb +24 -7
- data/lib/girl/proxy_worker.rb +80 -100
- data/lib/girl/proxyd.rb +11 -1
- data/lib/girl/proxyd_worker.rb +174 -67
- data/lib/girl/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0201dd1519345fc7a831c89a0a8ab139d2fa96d66da2545fcfd1de9102bbe23
|
4
|
+
data.tar.gz: e529faf1d41f6d80074eed26519c0fef85a092058dc74a936e17c067ce3a6f1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eec9fbcfa84d537bf1c9a301f2974ac5d2b3b3a0b6a4614d071b39c329faf8800e168afa618851a083a3bfc1df0770bb8da7252457394b6f3a1b02778fdae293
|
7
|
+
data.tar.gz: 37dc3789f90c73e6116a15cce0b37b1532f6c2b250996e8db0b0b1ad21ffb8d9a7909b6d06218c20574026f3730b134a0383dc348cad12c5ff56fe5897df420a
|
data/lib/girl/head.rb
CHANGED
@@ -5,6 +5,8 @@ module Girl
|
|
5
5
|
SEND_HELLO_COUNT = 10 # hello/a new source最多发几次
|
6
6
|
SEND_HELLO_INTERVAL = 0.5 # 发送hello/a new source间隔
|
7
7
|
EXPIRE_AFTER = 300 # 多久没有新流量,过期
|
8
|
+
RESET_TRAFF_DAY = 1 # 流量计数重置日,0为不重置
|
9
|
+
CHECK_TRAFF_INTERVAL = 86400 # 检查今天是否是流量计数重置日间隔
|
8
10
|
CHECK_EXPIRE_INTERVAL = 30 # 检查过期间隔
|
9
11
|
CHECK_RESUME_INTERVAL = 1 # 检查恢复读间隔
|
10
12
|
RESOLV_CACHE_EXPIRE = 300 # dns查询结果缓存多久过期
|
@@ -27,6 +29,7 @@ module Girl
|
|
27
29
|
CONTINUE = 17
|
28
30
|
IS_RESEND_READY = 18
|
29
31
|
RESEND_READY = 19
|
32
|
+
TRAFF_INFOS = 101
|
30
33
|
HTTP_OK = "HTTP/1.1 200 OK\r\n\r\n"
|
31
34
|
# https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
|
32
35
|
RESERVED_ROUTE = <<EOF
|
data/lib/girl/proxy.rb
CHANGED
@@ -20,21 +20,34 @@ proxyd-tun:
|
|
20
20
|
|
21
21
|
Q>: 0 ctlmsg -> C: 1 tund port -> n: tund port -> n: tcpd port
|
22
22
|
|
23
|
+
local-infod:
|
24
|
+
|
25
|
+
C: 101 traff infos
|
26
|
+
|
27
|
+
infod-local:
|
28
|
+
|
29
|
+
C: 101 traff infos -> [ C: im len -> im -> Q>: traff in -> Q>: traff out ]
|
30
|
+
|
23
31
|
tun-tund:
|
24
32
|
|
25
33
|
Q>: 0 ctlmsg -> C: 2 heartbeat
|
26
34
|
3 a new source -> Q>: src id -> encoded destination address
|
27
35
|
4 paired -> Q>: src id -> n: dst id
|
28
|
-
5 dest status
|
29
|
-
6 source status
|
30
|
-
7 miss
|
31
|
-
8 fin1
|
32
|
-
9 confirm fin1
|
33
|
-
10 fin2
|
34
|
-
11 confirm fin2
|
36
|
+
5 dest status NOT USE
|
37
|
+
6 source status NOT USE
|
38
|
+
7 miss NOT USE
|
39
|
+
8 fin1 NOT USE
|
40
|
+
9 confirm fin1 NOT USE
|
41
|
+
10 fin2 NOT USE
|
42
|
+
11 confirm fin2 NOT USE
|
35
43
|
12 tund fin
|
36
44
|
13 tun fin
|
37
45
|
14 tun ip changed
|
46
|
+
15 single miss NOT USE
|
47
|
+
16 range miss NOT USE
|
48
|
+
17 continue NOT USE
|
49
|
+
18 is resend ready NOT USE
|
50
|
+
19 resend ready NOT USE
|
38
51
|
=end
|
39
52
|
|
40
53
|
module Girl
|
@@ -109,6 +122,10 @@ module Girl
|
|
109
122
|
puts "im #{ im }"
|
110
123
|
puts "worker count #{ worker_count }"
|
111
124
|
|
125
|
+
Girl::Custom.constants.each do | name |
|
126
|
+
puts "#{ name } #{ Girl::Custom.const_get( name ).inspect }"
|
127
|
+
end
|
128
|
+
|
112
129
|
len = CONSTS.map{ | name | name.size }.max
|
113
130
|
|
114
131
|
CONSTS.each do | name |
|
data/lib/girl/proxy_worker.rb
CHANGED
@@ -13,6 +13,7 @@ module Girl
|
|
13
13
|
@mutex = Mutex.new
|
14
14
|
@reads = []
|
15
15
|
@writes = []
|
16
|
+
@closing_srcs = []
|
16
17
|
@roles = {} # sock => :dotr / :proxy / :src / :dst / :tun / :stream
|
17
18
|
@src_infos = {} # src => {}
|
18
19
|
@dst_infos = {} # dst => {}
|
@@ -37,7 +38,6 @@ module Girl
|
|
37
38
|
rs, ws = IO.select( @reads, @writes )
|
38
39
|
|
39
40
|
@mutex.synchronize do
|
40
|
-
# 先读,再写,避免打上关闭标记后读到
|
41
41
|
rs.each do | sock |
|
42
42
|
case @roles[ sock ]
|
43
43
|
when :dotr then
|
@@ -108,7 +108,7 @@ module Girl
|
|
108
108
|
# add read
|
109
109
|
#
|
110
110
|
def add_read( sock, role = nil )
|
111
|
-
|
111
|
+
if !sock.closed? && !@reads.include?( sock ) then
|
112
112
|
@reads << sock
|
113
113
|
|
114
114
|
if role then
|
@@ -121,7 +121,7 @@ module Girl
|
|
121
121
|
# add src wbuff
|
122
122
|
#
|
123
123
|
def add_src_wbuff( src, data )
|
124
|
-
return if src.closed?
|
124
|
+
return if src.closed? || @closing_srcs.include?( src )
|
125
125
|
src_info = @src_infos[ src ]
|
126
126
|
src_info[ :wbuff ] << data
|
127
127
|
src_info[ :last_recv_at ] = Time.new
|
@@ -167,7 +167,7 @@ module Girl
|
|
167
167
|
# add write
|
168
168
|
#
|
169
169
|
def add_write( sock )
|
170
|
-
|
170
|
+
if !sock.closed? && !@writes.include?( sock ) then
|
171
171
|
@writes << sock
|
172
172
|
end
|
173
173
|
end
|
@@ -188,7 +188,10 @@ module Girl
|
|
188
188
|
def close_dst( dst )
|
189
189
|
# puts "debug1 close dst"
|
190
190
|
close_sock( dst )
|
191
|
-
@dst_infos.delete( dst )
|
191
|
+
dst_info = @dst_infos.delete( dst )
|
192
|
+
src = dst_info[ :src ]
|
193
|
+
close_read_src( src )
|
194
|
+
set_src_closing_write( src )
|
192
195
|
end
|
193
196
|
|
194
197
|
##
|
@@ -258,7 +261,32 @@ module Girl
|
|
258
261
|
def close_src( src )
|
259
262
|
# puts "debug1 close src"
|
260
263
|
close_sock( src )
|
261
|
-
del_src_info( src )
|
264
|
+
src_info = del_src_info( src )
|
265
|
+
dst = src_info[ :dst ]
|
266
|
+
|
267
|
+
if dst then
|
268
|
+
close_read_dst( dst )
|
269
|
+
set_dst_closing_write( dst )
|
270
|
+
else
|
271
|
+
stream = src_info[ :stream ]
|
272
|
+
|
273
|
+
if stream then
|
274
|
+
close_read_stream( stream )
|
275
|
+
set_stream_closing_write( stream )
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
##
|
281
|
+
# close stream
|
282
|
+
#
|
283
|
+
def close_stream( stream )
|
284
|
+
# puts "debug1 close stream"
|
285
|
+
close_sock( stream )
|
286
|
+
stream_info = @stream_infos.delete( stream )
|
287
|
+
src = stream_info[ :src ]
|
288
|
+
close_read_src( src )
|
289
|
+
set_src_closing_write( src )
|
262
290
|
end
|
263
291
|
|
264
292
|
##
|
@@ -267,7 +295,6 @@ module Girl
|
|
267
295
|
def close_tun( tun )
|
268
296
|
# puts "debug1 close tun"
|
269
297
|
close_sock( tun )
|
270
|
-
@tun_info[ :srcs ].each{ | _, src | set_src_closing( src ) }
|
271
298
|
end
|
272
299
|
|
273
300
|
##
|
@@ -372,7 +399,7 @@ module Girl
|
|
372
399
|
trigger = false
|
373
400
|
now = Time.new
|
374
401
|
|
375
|
-
if @tun && !@tun.closed? then
|
402
|
+
if @tun && !@tun.closed? && !@tun_info[ :closing ] then
|
376
403
|
last_recv_at = @tun_info[ :last_recv_at ] || @tun_info[ :created_at ]
|
377
404
|
|
378
405
|
if @tun_info[ :srcs ].empty? && ( now - last_recv_at >= EXPIRE_AFTER ) then
|
@@ -482,7 +509,7 @@ module Girl
|
|
482
509
|
|
483
510
|
Thread.new do
|
484
511
|
SEND_HELLO_COUNT.times do | i |
|
485
|
-
if @tun.nil? || @tun.closed? || src.closed? || src_info[ :stream ] then
|
512
|
+
if @tun.nil? || @tun.closed? || @tun_info[ :closing ] || src.closed? || src_info[ :stream ] then
|
486
513
|
# puts "debug1 break loop send a new source #{ src_info[ :dst_port ] }"
|
487
514
|
break
|
488
515
|
end
|
@@ -523,15 +550,12 @@ module Girl
|
|
523
550
|
end
|
524
551
|
|
525
552
|
# puts "debug1 a new dst #{ dst.local_address.inspect }"
|
526
|
-
local_port = dst.local_address.ip_port
|
527
553
|
dst_info = {
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
closing: false, # 准备关闭
|
534
|
-
closing_write: false # 准备关闭写
|
554
|
+
src: src, # 对应src
|
555
|
+
domain: domain, # 目的地
|
556
|
+
wbuff: '', # 写前,从src读到的流量
|
557
|
+
paused: false, # 是否已暂停读
|
558
|
+
closing_write: false # 准备关闭写
|
535
559
|
}
|
536
560
|
|
537
561
|
@dst_infos[ dst ] = dst_info
|
@@ -590,10 +614,11 @@ module Girl
|
|
590
614
|
|
591
615
|
domain = src_info[ :destination_domain ]
|
592
616
|
@stream_infos[ stream ] = {
|
593
|
-
src: src,
|
594
|
-
domain: domain,
|
595
|
-
wbuff: data,
|
596
|
-
paused: false
|
617
|
+
src: src, # 对应src
|
618
|
+
domain: domain, # 目的地
|
619
|
+
wbuff: data, # 写前,写往远端streamd
|
620
|
+
paused: false, # 是否已暂停读
|
621
|
+
closing_write: false # 准备关闭写
|
597
622
|
}
|
598
623
|
|
599
624
|
src_info[ :dst_id ] = dst_id
|
@@ -657,7 +682,7 @@ module Girl
|
|
657
682
|
|
658
683
|
Thread.new do
|
659
684
|
SEND_HELLO_COUNT.times do | i |
|
660
|
-
if @tun.nil? || @tun.closed? || @tun_info[ :tund_addr ] then
|
685
|
+
if @tun.nil? || @tun.closed? || @tun_info[ :closing ] || @tun_info[ :tund_addr ] then
|
661
686
|
# puts "debug1 break loop send hello"
|
662
687
|
break
|
663
688
|
end
|
@@ -725,12 +750,12 @@ module Girl
|
|
725
750
|
unless src.closed? then
|
726
751
|
puts "p#{ Process.pid } #{ Time.new } resolved #{ domain } #{ ip_info.ip_address }"
|
727
752
|
deal_with_destination_ip( src, ip_info )
|
753
|
+
next_tick
|
728
754
|
end
|
729
755
|
else
|
730
756
|
set_src_closing( src )
|
757
|
+
next_tick
|
731
758
|
end
|
732
|
-
|
733
|
-
next_tick
|
734
759
|
end
|
735
760
|
end
|
736
761
|
end
|
@@ -744,23 +769,15 @@ module Girl
|
|
744
769
|
add_read( src )
|
745
770
|
end
|
746
771
|
|
747
|
-
##
|
748
|
-
# set dst closing
|
749
|
-
#
|
750
|
-
def set_dst_closing( dst )
|
751
|
-
return if dst.closed?
|
752
|
-
dst_info = @dst_infos[ dst ]
|
753
|
-
dst_info[ :closing ] = true
|
754
|
-
@reads.delete( dst )
|
755
|
-
add_write( dst )
|
756
|
-
end
|
757
|
-
|
758
772
|
##
|
759
773
|
# set dst closing write
|
760
774
|
#
|
761
775
|
def set_dst_closing_write( dst )
|
762
776
|
return if dst.closed?
|
777
|
+
|
763
778
|
dst_info = @dst_infos[ dst ]
|
779
|
+
return if dst_info[ :closing_write ]
|
780
|
+
|
764
781
|
dst_info[ :closing_write ] = true
|
765
782
|
add_write( dst )
|
766
783
|
end
|
@@ -769,19 +786,22 @@ module Girl
|
|
769
786
|
# set src is closing
|
770
787
|
#
|
771
788
|
def set_src_closing( src )
|
772
|
-
return if src.closed?
|
789
|
+
return if src.closed? || @closing_srcs.include?( src )
|
773
790
|
@reads.delete( src )
|
774
|
-
|
775
|
-
|
776
|
-
add_write(
|
791
|
+
@writes.delete( src )
|
792
|
+
@closing_srcs << src
|
793
|
+
add_write( @tun ) if @tun
|
777
794
|
end
|
778
795
|
|
779
796
|
##
|
780
797
|
# set src closing write
|
781
798
|
#
|
782
799
|
def set_src_closing_write( src )
|
783
|
-
return if src.closed?
|
800
|
+
return if src.closed? || @closing_srcs.include?( src )
|
801
|
+
|
784
802
|
src_info = @src_infos[ src ]
|
803
|
+
return if src_info[ :closing_write ]
|
804
|
+
|
785
805
|
src_info[ :closing_write ] = true
|
786
806
|
add_write( src )
|
787
807
|
end
|
@@ -790,10 +810,6 @@ module Girl
|
|
790
810
|
# set src proxy type tunnel
|
791
811
|
#
|
792
812
|
def set_src_proxy_type_tunnel( src )
|
793
|
-
if @tun.nil? || @tun.closed? then
|
794
|
-
new_a_tun
|
795
|
-
end
|
796
|
-
|
797
813
|
src_info = @src_infos[ src ]
|
798
814
|
src_info[ :proxy_type ] = :tunnel
|
799
815
|
src_id = src_info[ :id ]
|
@@ -806,23 +822,15 @@ module Girl
|
|
806
822
|
end
|
807
823
|
end
|
808
824
|
|
809
|
-
##
|
810
|
-
# set stream closing
|
811
|
-
#
|
812
|
-
def set_stream_closing( stream )
|
813
|
-
return if stream.closed?
|
814
|
-
stream_info = @stream_infos[ stream ]
|
815
|
-
stream_info[ :closing ] = true
|
816
|
-
@reads.delete( stream )
|
817
|
-
add_write( stream )
|
818
|
-
end
|
819
|
-
|
820
825
|
##
|
821
826
|
# set stream closing write
|
822
827
|
#
|
823
828
|
def set_stream_closing_write( stream )
|
824
829
|
return if stream.closed?
|
830
|
+
|
825
831
|
stream_info = @stream_infos[ stream ]
|
832
|
+
return if stream_info[ :closing_write ]
|
833
|
+
|
826
834
|
stream_info[ :closing_write ] = true
|
827
835
|
add_write( stream )
|
828
836
|
end
|
@@ -831,10 +839,11 @@ module Girl
|
|
831
839
|
# set tun is closing
|
832
840
|
#
|
833
841
|
def set_tun_closing
|
834
|
-
return if @tun.closed?
|
842
|
+
return if @tun.closed? || @tun_info[ :closing ]
|
835
843
|
@tun_info[ :closing ] = true
|
836
844
|
@reads.delete( @tun )
|
837
845
|
add_write( @tun )
|
846
|
+
@tun_info[ :srcs ].each{ | _, src | set_src_closing( src ) }
|
838
847
|
end
|
839
848
|
|
840
849
|
##
|
@@ -893,11 +902,14 @@ module Girl
|
|
893
902
|
last_recv_at: nil, # 上一次收到新流量(由dst收到,或者由stream收到)的时间
|
894
903
|
last_sent_at: nil, # 上一次发出流量(由dst发出,或者由stream发出)的时间
|
895
904
|
paused: false, # 是否已暂停读
|
896
|
-
closing: false, # 准备关闭
|
897
905
|
closing_write: false # 准备关闭写
|
898
906
|
}
|
899
907
|
|
900
908
|
add_read( src, :src )
|
909
|
+
|
910
|
+
if @tun.nil? || @tun.closed? then
|
911
|
+
new_a_tun
|
912
|
+
end
|
901
913
|
end
|
902
914
|
|
903
915
|
##
|
@@ -961,6 +973,8 @@ module Girl
|
|
961
973
|
# read src
|
962
974
|
#
|
963
975
|
def read_src( src )
|
976
|
+
return if src.closed?
|
977
|
+
|
964
978
|
begin
|
965
979
|
data = src.read_nonblock( READ_SIZE )
|
966
980
|
rescue IO::WaitReadable, Errno::EINTR
|
@@ -1164,6 +1178,8 @@ module Girl
|
|
1164
1178
|
# read dst
|
1165
1179
|
#
|
1166
1180
|
def read_dst( dst )
|
1181
|
+
return if dst.closed?
|
1182
|
+
|
1167
1183
|
begin
|
1168
1184
|
data = dst.read_nonblock( READ_SIZE )
|
1169
1185
|
rescue IO::WaitReadable, Errno::EINTR
|
@@ -1186,6 +1202,8 @@ module Girl
|
|
1186
1202
|
# read stream
|
1187
1203
|
#
|
1188
1204
|
def read_stream( stream )
|
1205
|
+
return if stream.closed?
|
1206
|
+
|
1189
1207
|
begin
|
1190
1208
|
data = stream.read_nonblock( READ_SIZE )
|
1191
1209
|
rescue IO::WaitReadable, Errno::EINTR
|
@@ -1211,6 +1229,11 @@ module Girl
|
|
1211
1229
|
#
|
1212
1230
|
def write_tun( tun )
|
1213
1231
|
# 处理关闭
|
1232
|
+
if @closing_srcs.any? then
|
1233
|
+
@closing_srcs.each{ | src | close_src( src ) }
|
1234
|
+
@closing_srcs.clear
|
1235
|
+
end
|
1236
|
+
|
1214
1237
|
if @tun_info[ :closing ] then
|
1215
1238
|
close_tun( tun )
|
1216
1239
|
return
|
@@ -1246,27 +1269,6 @@ module Girl
|
|
1246
1269
|
return if src.closed?
|
1247
1270
|
src_info = @src_infos[ src ]
|
1248
1271
|
dst = src_info[ :dst ]
|
1249
|
-
|
1250
|
-
# 处理关闭
|
1251
|
-
if src_info[ :closing ] then
|
1252
|
-
close_src( src )
|
1253
|
-
|
1254
|
-
if dst then
|
1255
|
-
close_read_dst( dst )
|
1256
|
-
set_dst_closing_write( dst )
|
1257
|
-
else
|
1258
|
-
stream = src_info[ :stream ]
|
1259
|
-
|
1260
|
-
if stream then
|
1261
|
-
close_read_stream( stream )
|
1262
|
-
set_stream_closing_write( stream )
|
1263
|
-
end
|
1264
|
-
end
|
1265
|
-
|
1266
|
-
return
|
1267
|
-
end
|
1268
|
-
|
1269
|
-
# 处理wbuff
|
1270
1272
|
data = src_info[ :wbuff ]
|
1271
1273
|
|
1272
1274
|
# 写前为空,处理关闭写
|
@@ -1312,19 +1314,6 @@ module Girl
|
|
1312
1314
|
return if dst.closed?
|
1313
1315
|
dst_info = @dst_infos[ dst ]
|
1314
1316
|
src = dst_info[ :src ]
|
1315
|
-
|
1316
|
-
# 处理关闭
|
1317
|
-
if dst_info[ :closing ] then
|
1318
|
-
close_dst( dst )
|
1319
|
-
|
1320
|
-
if src then
|
1321
|
-
close_read_src( src )
|
1322
|
-
set_src_closing_write( src )
|
1323
|
-
end
|
1324
|
-
|
1325
|
-
return
|
1326
|
-
end
|
1327
|
-
|
1328
1317
|
data = dst_info[ :wbuff ]
|
1329
1318
|
|
1330
1319
|
# 写前为空,处理关闭写
|
@@ -1367,15 +1356,6 @@ module Girl
|
|
1367
1356
|
return if stream.closed?
|
1368
1357
|
stream_info = @stream_infos[ stream ]
|
1369
1358
|
src = stream_info[ :src ]
|
1370
|
-
|
1371
|
-
# 处理关闭
|
1372
|
-
if stream_info[ :closing ] then
|
1373
|
-
close_stream( stream )
|
1374
|
-
close_read_src( src )
|
1375
|
-
set_src_closing_write( src )
|
1376
|
-
return
|
1377
|
-
end
|
1378
|
-
|
1379
1359
|
data = stream_info[ :wbuff ]
|
1380
1360
|
|
1381
1361
|
# 写前为空,处理关闭写
|
data/lib/girl/proxyd.rb
CHANGED
@@ -17,6 +17,7 @@ module Girl
|
|
17
17
|
raise "not found config file #{ config_path }" unless File.exist?( config_path )
|
18
18
|
conf = JSON.parse( IO.binread( config_path ), symbolize_names: true )
|
19
19
|
proxyd_port = conf[ :proxyd_port ]
|
20
|
+
infod_port = conf[ :infod_port ]
|
20
21
|
worker_count = conf[ :worker_count ]
|
21
22
|
end
|
22
23
|
|
@@ -24,6 +25,10 @@ module Girl
|
|
24
25
|
proxyd_port = 6060
|
25
26
|
end
|
26
27
|
|
28
|
+
unless infod_port then
|
29
|
+
infod_port = 6070
|
30
|
+
end
|
31
|
+
|
27
32
|
nprocessors = Etc.nprocessors
|
28
33
|
|
29
34
|
if worker_count.nil? || worker_count <= 0 || worker_count > nprocessors then
|
@@ -33,8 +38,13 @@ module Girl
|
|
33
38
|
title = "girl proxyd #{ Girl::VERSION }"
|
34
39
|
puts title
|
35
40
|
puts "proxyd port #{ proxyd_port }"
|
41
|
+
puts "infod port #{ infod_port }"
|
36
42
|
puts "worker count #{ worker_count }"
|
37
43
|
|
44
|
+
Girl::Custom.constants.each do | name |
|
45
|
+
puts "#{ name } #{ Girl::Custom.const_get( name ).inspect }"
|
46
|
+
end
|
47
|
+
|
38
48
|
len = CONSTS.map{ | name | name.size }.max
|
39
49
|
|
40
50
|
CONSTS.each do | name |
|
@@ -47,7 +57,7 @@ module Girl
|
|
47
57
|
worker_count.times do | i |
|
48
58
|
workers << fork do
|
49
59
|
$0 = 'girl proxyd worker'
|
50
|
-
worker = Girl::ProxydWorker.new( proxyd_port )
|
60
|
+
worker = Girl::ProxydWorker.new( proxyd_port, infod_port )
|
51
61
|
|
52
62
|
Signal.trap( :TERM ) do
|
53
63
|
puts "w#{ i } exit"
|
data/lib/girl/proxyd_worker.rb
CHANGED
@@ -4,23 +4,28 @@ module Girl
|
|
4
4
|
##
|
5
5
|
# initialize
|
6
6
|
#
|
7
|
-
def initialize( proxyd_port )
|
7
|
+
def initialize( proxyd_port, infod_port )
|
8
8
|
@custom = Girl::ProxydCustom.new
|
9
9
|
@mutex = Mutex.new
|
10
10
|
@reads = []
|
11
11
|
@writes = []
|
12
|
-
@
|
12
|
+
@closing_dsts = []
|
13
|
+
@closing_streamds = []
|
14
|
+
@roles = {} # sock => :dotr / :proxyd / :infod / :dst / :tund / :tcpd / :streamd
|
13
15
|
@tund_infos = {} # tund => {}
|
14
16
|
@tcpd_infos = {} # tcpd => {}
|
15
17
|
@dst_infos = {} # dst => {}
|
16
18
|
@streamd_infos = {} # streamd => {}
|
17
19
|
@tunneling_tunds = {} # tunneling_addr => tund
|
18
20
|
@resolv_caches = {} # domain => [ ip, created_at ]
|
21
|
+
@traff_ins = {} # im => 0
|
22
|
+
@traff_outs = {} # im => 0
|
19
23
|
|
20
24
|
dotr, dotw = IO.pipe
|
21
25
|
@dotw = dotw
|
22
26
|
add_read( dotr, :dotr )
|
23
27
|
new_a_proxyd( proxyd_port )
|
28
|
+
new_a_infod( infod_port )
|
24
29
|
end
|
25
30
|
|
26
31
|
##
|
@@ -30,18 +35,20 @@ module Girl
|
|
30
35
|
puts "p#{ Process.pid } #{ Time.new } looping"
|
31
36
|
loop_check_expire
|
32
37
|
loop_check_resume
|
38
|
+
loop_check_traff
|
33
39
|
|
34
40
|
loop do
|
35
41
|
rs, ws = IO.select( @reads, @writes )
|
36
42
|
|
37
43
|
@mutex.synchronize do
|
38
|
-
# 先读,再写,避免打上关闭标记后读到
|
39
44
|
rs.each do | sock |
|
40
45
|
case @roles[ sock ]
|
41
46
|
when :dotr then
|
42
47
|
read_dotr( sock )
|
43
48
|
when :proxyd then
|
44
49
|
read_proxyd( sock )
|
50
|
+
when :infod then
|
51
|
+
read_infod( sock )
|
45
52
|
when :tund then
|
46
53
|
read_tund( sock )
|
47
54
|
when :tcpd then
|
@@ -113,7 +120,7 @@ module Girl
|
|
113
120
|
# add read
|
114
121
|
#
|
115
122
|
def add_read( sock, role = nil )
|
116
|
-
|
123
|
+
if !sock.closed? && !@reads.include?( sock ) then
|
117
124
|
@reads << sock
|
118
125
|
|
119
126
|
if role then
|
@@ -122,11 +129,32 @@ module Girl
|
|
122
129
|
end
|
123
130
|
end
|
124
131
|
|
132
|
+
##
|
133
|
+
# add streamd wbuff
|
134
|
+
#
|
135
|
+
def add_streamd_wbuff( streamd, data )
|
136
|
+
return if streamd.closed? || @closing_streamds.include?( streamd )
|
137
|
+
streamd_info = @streamd_infos[ streamd ]
|
138
|
+
streamd_info[ :wbuff ] << data
|
139
|
+
add_write( streamd )
|
140
|
+
|
141
|
+
if streamd_info[ :wbuff ].bytesize >= WBUFF_LIMIT then
|
142
|
+
dst = streamd_info[ :dst ]
|
143
|
+
dst_info = @dst_infos[ dst ]
|
144
|
+
|
145
|
+
unless dst_info[ :paused ] then
|
146
|
+
puts "p#{ Process.pid } #{ Time.new } pause dst #{ dst_info[ :domain_port ] }"
|
147
|
+
dst_info[ :paused ] = true
|
148
|
+
@reads.delete( dst )
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
125
153
|
##
|
126
154
|
# add write
|
127
155
|
#
|
128
156
|
def add_write( sock )
|
129
|
-
|
157
|
+
if !sock.closed? && !@writes.include?( sock ) then
|
130
158
|
@writes << sock
|
131
159
|
end
|
132
160
|
end
|
@@ -137,7 +165,13 @@ module Girl
|
|
137
165
|
def close_dst( dst )
|
138
166
|
# puts "debug1 close dst"
|
139
167
|
close_sock( dst )
|
140
|
-
del_dst_info( dst )
|
168
|
+
dst_info = del_dst_info( dst )
|
169
|
+
streamd = dst_info[ :streamd ]
|
170
|
+
|
171
|
+
if streamd then
|
172
|
+
close_read_streamd( streamd )
|
173
|
+
set_streamd_closing_write( streamd )
|
174
|
+
end
|
141
175
|
end
|
142
176
|
|
143
177
|
##
|
@@ -197,7 +231,13 @@ module Girl
|
|
197
231
|
def close_streamd( streamd )
|
198
232
|
# puts "debug1 close streamd"
|
199
233
|
close_sock( streamd )
|
200
|
-
@streamd_infos.delete( streamd )
|
234
|
+
streamd_info = @streamd_infos.delete( streamd )
|
235
|
+
dst = streamd_info[ :dst ]
|
236
|
+
|
237
|
+
if dst then
|
238
|
+
close_read_dst( dst )
|
239
|
+
set_dst_closing_write( dst )
|
240
|
+
end
|
201
241
|
end
|
202
242
|
|
203
243
|
##
|
@@ -210,7 +250,6 @@ module Girl
|
|
210
250
|
tcpd = tund_info[ :tcpd ]
|
211
251
|
close_sock( tcpd )
|
212
252
|
@tcpd_infos.delete( tcpd )
|
213
|
-
tund_info[ :dsts ].each{ | _, dst | set_dst_closing( dst ) }
|
214
253
|
@tunneling_tunds.delete( tund_info[ :tun_addr ] )
|
215
254
|
end
|
216
255
|
|
@@ -270,10 +309,12 @@ module Girl
|
|
270
309
|
end
|
271
310
|
|
272
311
|
dst_id = dst.local_address.ip_port
|
312
|
+
tund_info = @tund_infos[ tund ]
|
273
313
|
|
274
314
|
@dst_infos[ dst ] = {
|
275
315
|
id: dst_id, # id
|
276
316
|
tund: tund, # 对应tund
|
317
|
+
im: tund_info[ :im ], # 标识
|
277
318
|
domain_port: domain_port, # 目的地和端口
|
278
319
|
rbuff: '', # 对应的streamd没准备好,暂存读到的流量
|
279
320
|
streamd: nil, # 对应的streamd
|
@@ -283,13 +324,11 @@ module Girl
|
|
283
324
|
last_recv_at: nil, # 上一次收到新流量(由streamd收到)的时间
|
284
325
|
last_sent_at: nil, # 上一次发出流量(由streamd发出)的时间
|
285
326
|
paused: false, # 是否已暂停读
|
286
|
-
closing: false, # 准备关闭
|
287
327
|
closing_write: false # 准备关闭写
|
288
328
|
}
|
289
329
|
|
290
330
|
add_read( dst, :dst )
|
291
331
|
|
292
|
-
tund_info = @tund_infos[ tund ]
|
293
332
|
tund_info[ :dst_ids ][ src_id ] = dst_id
|
294
333
|
tund_info[ :dsts ][ dst_id ] = dst
|
295
334
|
|
@@ -328,12 +367,14 @@ module Girl
|
|
328
367
|
now = Time.new
|
329
368
|
|
330
369
|
@tund_infos.each do | tund, tund_info |
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
370
|
+
unless tund_info[ :closing ] then
|
371
|
+
last_recv_at = tund_info[ :last_recv_at ] || tund_info[ :created_at ]
|
372
|
+
|
373
|
+
if tund_info[ :dsts ].empty? && ( now - last_recv_at >= EXPIRE_AFTER ) then
|
374
|
+
puts "p#{ Process.pid } #{ Time.new } expire tund #{ tund_info[ :port ] }"
|
375
|
+
set_tund_closing( tund )
|
376
|
+
trigger = true
|
377
|
+
end
|
337
378
|
end
|
338
379
|
end
|
339
380
|
|
@@ -395,6 +436,27 @@ module Girl
|
|
395
436
|
end
|
396
437
|
end
|
397
438
|
|
439
|
+
##
|
440
|
+
# loop check traff
|
441
|
+
#
|
442
|
+
def loop_check_traff
|
443
|
+
if RESET_TRAFF_DAY > 0 then
|
444
|
+
Thread.new do
|
445
|
+
loop do
|
446
|
+
sleep CHECK_TRAFF_INTERVAL
|
447
|
+
|
448
|
+
@mutex.synchronize do
|
449
|
+
if Time.new.day == RESET_TRAFF_DAY then
|
450
|
+
puts "p#{ Process.pid } #{ Time.new } reset traffs"
|
451
|
+
@traff_ins.transform_values!{ | _ | 0 }
|
452
|
+
@traff_outs.transform_values!{ | _ | 0 }
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
398
460
|
##
|
399
461
|
# new a proxyd
|
400
462
|
#
|
@@ -412,6 +474,18 @@ module Girl
|
|
412
474
|
add_read( proxyd, :proxyd )
|
413
475
|
end
|
414
476
|
|
477
|
+
##
|
478
|
+
# new a infod
|
479
|
+
#
|
480
|
+
def new_a_infod( infod_port )
|
481
|
+
infod = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
|
482
|
+
infod.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
|
483
|
+
infod.bind( Socket.sockaddr_in( infod_port, '127.0.0.1' ) )
|
484
|
+
|
485
|
+
puts "p#{ Process.pid } #{ Time.new } infod bind on #{ infod_port }"
|
486
|
+
add_read( infod, :infod )
|
487
|
+
end
|
488
|
+
|
415
489
|
##
|
416
490
|
# next tick
|
417
491
|
#
|
@@ -487,19 +561,23 @@ module Girl
|
|
487
561
|
# set dst closing
|
488
562
|
#
|
489
563
|
def set_dst_closing( dst )
|
490
|
-
return if dst.closed?
|
491
|
-
dst_info = @dst_infos[ dst ]
|
492
|
-
dst_info[ :closing ] = true
|
564
|
+
return if dst.closed? || @closing_dsts.include?( dst )
|
493
565
|
@reads.delete( dst )
|
494
|
-
|
566
|
+
@writes.delete( dst )
|
567
|
+
@closing_dsts << dst
|
568
|
+
dst_info = @dst_infos[ dst ]
|
569
|
+
add_write( dst_info[ :tund ] )
|
495
570
|
end
|
496
571
|
|
497
572
|
##
|
498
573
|
# set dst closing write
|
499
574
|
#
|
500
575
|
def set_dst_closing_write( dst )
|
501
|
-
return if dst.closed?
|
576
|
+
return if dst.closed? || @closing_dsts.include?( dst )
|
577
|
+
|
502
578
|
dst_info = @dst_infos[ dst ]
|
579
|
+
return if dst_info[ :closing_write ]
|
580
|
+
|
503
581
|
dst_info[ :closing_write ] = true
|
504
582
|
add_write( dst )
|
505
583
|
end
|
@@ -508,19 +586,23 @@ module Girl
|
|
508
586
|
# set streamd closing
|
509
587
|
#
|
510
588
|
def set_streamd_closing( streamd )
|
511
|
-
return if streamd.closed?
|
512
|
-
streamd_info = @streamd_infos[ streamd ]
|
513
|
-
streamd_info[ :closing ] = true
|
589
|
+
return if streamd.closed? || @closing_streamds.include?( streamd )
|
514
590
|
@reads.delete( streamd )
|
515
|
-
|
591
|
+
@writes.delete( streamd )
|
592
|
+
@closing_streamds << streamd
|
593
|
+
streamd_info = @streamd_infos[ streamd ]
|
594
|
+
add_write( streamd_info[ :tund ] )
|
516
595
|
end
|
517
596
|
|
518
597
|
##
|
519
598
|
# set streamd closing write
|
520
599
|
#
|
521
600
|
def set_streamd_closing_write( streamd )
|
522
|
-
return if streamd.closed?
|
601
|
+
return if streamd.closed? || @closing_streamds.include?( streamd )
|
602
|
+
|
523
603
|
streamd_info = @streamd_infos[ streamd ]
|
604
|
+
return if streamd_info[ :closing_write ]
|
605
|
+
|
524
606
|
streamd_info[ :closing_write ] = true
|
525
607
|
add_write( streamd )
|
526
608
|
end
|
@@ -530,10 +612,14 @@ module Girl
|
|
530
612
|
#
|
531
613
|
def set_tund_closing( tund )
|
532
614
|
return if tund.closed?
|
615
|
+
|
533
616
|
tund_info = @tund_infos[ tund ]
|
617
|
+
return if tund_info[ :closing ]
|
618
|
+
|
534
619
|
tund_info[ :closing ] = true
|
535
620
|
@reads.delete( tund )
|
536
621
|
add_write( tund )
|
622
|
+
tund_info[ :dsts ].each{ | _, dst | set_dst_closing( dst ) }
|
537
623
|
end
|
538
624
|
|
539
625
|
##
|
@@ -565,6 +651,13 @@ module Girl
|
|
565
651
|
return
|
566
652
|
end
|
567
653
|
|
654
|
+
im = data
|
655
|
+
|
656
|
+
unless @traff_ins.include?( im ) then
|
657
|
+
@traff_ins[ im ] = 0
|
658
|
+
@traff_outs[ im ] = 0
|
659
|
+
end
|
660
|
+
|
568
661
|
tund = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
|
569
662
|
tund.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
|
570
663
|
tund_port = tund.local_address.ip_port
|
@@ -578,6 +671,7 @@ module Girl
|
|
578
671
|
add_read( tcpd, :tcpd )
|
579
672
|
|
580
673
|
tund_info = {
|
674
|
+
im: im, # 标识
|
581
675
|
port: tund_port, # 端口
|
582
676
|
tcpd: tcpd, # 对应的tcpd
|
583
677
|
tcpd_port: tcpd_port, # tcpd端口
|
@@ -601,6 +695,28 @@ module Girl
|
|
601
695
|
add_proxyd_ctlmsg_tund_port( tund_info )
|
602
696
|
end
|
603
697
|
|
698
|
+
##
|
699
|
+
# read infod
|
700
|
+
#
|
701
|
+
def read_infod( infod )
|
702
|
+
data, addrinfo, rflags, *controls = infod.recvmsg
|
703
|
+
ctl_num = data[ 0 ].unpack( 'C' ).first
|
704
|
+
# puts "debug1 infod recv #{ ctl_num } #{ addrinfo.inspect }"
|
705
|
+
|
706
|
+
case ctl_num
|
707
|
+
when TRAFF_INFOS then
|
708
|
+
data2 = [ TRAFF_INFOS ].pack( 'C' )
|
709
|
+
|
710
|
+
@traff_ins.keys.sort.each do | im |
|
711
|
+
traff_in = @traff_ins[ im ]
|
712
|
+
traff_out = @traff_outs[ im ]
|
713
|
+
data2 << [ [ im.bytesize ].pack( 'C' ), im, [ traff_in, traff_out ].pack( 'Q>Q>' ) ].join
|
714
|
+
end
|
715
|
+
|
716
|
+
send_data( infod, data2, addrinfo )
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
604
720
|
##
|
605
721
|
# read tund
|
606
722
|
#
|
@@ -662,6 +778,8 @@ module Girl
|
|
662
778
|
# read tcpd
|
663
779
|
#
|
664
780
|
def read_tcpd( tcpd )
|
781
|
+
return if tcpd.closed?
|
782
|
+
|
665
783
|
begin
|
666
784
|
streamd, addrinfo = tcpd.accept_nonblock
|
667
785
|
rescue IO::WaitReadable, Errno::EINTR
|
@@ -672,13 +790,16 @@ module Girl
|
|
672
790
|
# puts "debug1 accept a streamd"
|
673
791
|
tcpd_info = @tcpd_infos[ tcpd ]
|
674
792
|
tund = tcpd_info[ :tund ]
|
793
|
+
tund_info = @tund_infos[ tund ]
|
675
794
|
|
676
795
|
@streamd_infos[ streamd ] = {
|
677
|
-
tund: tund,
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
796
|
+
tund: tund, # 对应tund
|
797
|
+
im: tund_info[ :im ], # 标识
|
798
|
+
dst: nil, # 对应dst
|
799
|
+
domain_port: nil, # dst的目的地和端口
|
800
|
+
wbuff: '', # 写前,写往近端stream
|
801
|
+
paused: false, # 是否已暂停读
|
802
|
+
closing_write: false # 准备关闭写
|
682
803
|
}
|
683
804
|
|
684
805
|
add_read( streamd, :streamd )
|
@@ -688,6 +809,8 @@ module Girl
|
|
688
809
|
# read dst
|
689
810
|
#
|
690
811
|
def read_dst( dst )
|
812
|
+
return if dst.closed?
|
813
|
+
|
691
814
|
begin
|
692
815
|
data = dst.read_nonblock( READ_SIZE )
|
693
816
|
rescue IO::WaitReadable, Errno::EINTR
|
@@ -702,6 +825,7 @@ module Girl
|
|
702
825
|
end
|
703
826
|
|
704
827
|
dst_info = @dst_infos[ dst ]
|
828
|
+
@traff_ins[ dst_info[ :im ] ] += data.bytesize
|
705
829
|
streamd = dst_info[ :streamd ]
|
706
830
|
|
707
831
|
if streamd then
|
@@ -709,14 +833,7 @@ module Girl
|
|
709
833
|
streamd_info = @streamd_infos[ streamd ]
|
710
834
|
data = @custom.encode( data )
|
711
835
|
# puts "debug2 add streamd.wbuff encoded #{ data.bytesize }"
|
712
|
-
|
713
|
-
add_write( streamd )
|
714
|
-
|
715
|
-
if streamd_info[ :wbuff ].bytesize >= WBUFF_LIMIT then
|
716
|
-
puts "p#{ Process.pid } #{ Time.new } pause dst #{ dst_info[ :domain_port ] }"
|
717
|
-
dst_info[ :paused ] = true
|
718
|
-
@reads.delete( dst )
|
719
|
-
end
|
836
|
+
add_streamd_wbuff( streamd, data )
|
720
837
|
end
|
721
838
|
else
|
722
839
|
dst_info[ :rbuff ] << data
|
@@ -732,6 +849,8 @@ module Girl
|
|
732
849
|
# read streamd
|
733
850
|
#
|
734
851
|
def read_streamd( streamd )
|
852
|
+
return if streamd.closed?
|
853
|
+
|
735
854
|
begin
|
736
855
|
data = streamd.read_nonblock( READ_SIZE )
|
737
856
|
rescue IO::WaitReadable, Errno::EINTR
|
@@ -746,6 +865,7 @@ module Girl
|
|
746
865
|
end
|
747
866
|
|
748
867
|
streamd_info = @streamd_infos[ streamd ]
|
868
|
+
@traff_ins[ streamd_info[ :im ] ] += data.bytesize
|
749
869
|
dst = streamd_info[ :dst ]
|
750
870
|
|
751
871
|
unless dst then
|
@@ -772,7 +892,8 @@ module Girl
|
|
772
892
|
|
773
893
|
unless dst_info[ :rbuff ].empty? then
|
774
894
|
# puts "debug1 encode and move dst.rbuff to streamd.wbuff"
|
775
|
-
|
895
|
+
data2 = @custom.encode( dst_info[ :rbuff ] )
|
896
|
+
add_streamd_wbuff( streamd, data2 )
|
776
897
|
end
|
777
898
|
|
778
899
|
dst_info[ :streamd ] = streamd
|
@@ -781,7 +902,7 @@ module Girl
|
|
781
902
|
return if data.empty?
|
782
903
|
end
|
783
904
|
|
784
|
-
|
905
|
+
if !dst.closed? && !@closing_dsts.include?( dst ) then
|
785
906
|
dst_info = @dst_infos[ dst ]
|
786
907
|
data = @custom.decode( data )
|
787
908
|
# puts "debug2 add dst.wbuff decoded #{ data.bytesize }"
|
@@ -824,6 +945,16 @@ module Girl
|
|
824
945
|
tund_info = @tund_infos[ tund ]
|
825
946
|
|
826
947
|
# 处理关闭
|
948
|
+
if @closing_dsts.any? then
|
949
|
+
@closing_dsts.each{ | dst | close_dst( dst ) }
|
950
|
+
@closing_dsts.clear
|
951
|
+
end
|
952
|
+
|
953
|
+
if @closing_streamds.any? then
|
954
|
+
@closing_streamds.each{ | streamd | close_streamd( streamd ) }
|
955
|
+
@closing_streamds.clear
|
956
|
+
end
|
957
|
+
|
827
958
|
if tund_info[ :closing ] then
|
828
959
|
if tund_info[ :changed_tun_addr ] then
|
829
960
|
data = [ 0, IP_CHANGED ].pack( 'Q>C' )
|
@@ -862,19 +993,6 @@ module Girl
|
|
862
993
|
return if dst.closed?
|
863
994
|
dst_info = @dst_infos[ dst ]
|
864
995
|
streamd = dst_info[ :streamd ]
|
865
|
-
|
866
|
-
# 处理关闭
|
867
|
-
if dst_info[ :closing ] then
|
868
|
-
close_dst( dst )
|
869
|
-
|
870
|
-
if streamd then
|
871
|
-
close_read_streamd( streamd )
|
872
|
-
set_streamd_closing_write( streamd )
|
873
|
-
end
|
874
|
-
|
875
|
-
return
|
876
|
-
end
|
877
|
-
|
878
996
|
data = dst_info[ :wbuff ]
|
879
997
|
|
880
998
|
# 写前为空,处理关闭写
|
@@ -904,6 +1022,7 @@ module Girl
|
|
904
1022
|
# puts "debug2 written dst #{ written }"
|
905
1023
|
data = data[ written..-1 ]
|
906
1024
|
dst_info[ :wbuff ] = data
|
1025
|
+
@traff_outs[ dst_info[ :im ] ] += written
|
907
1026
|
end
|
908
1027
|
|
909
1028
|
##
|
@@ -913,19 +1032,6 @@ module Girl
|
|
913
1032
|
return if streamd.closed?
|
914
1033
|
streamd_info = @streamd_infos[ streamd ]
|
915
1034
|
dst = streamd_info[ :dst ]
|
916
|
-
|
917
|
-
# 处理关闭
|
918
|
-
if streamd_info[ :closing ] then
|
919
|
-
close_streamd( streamd )
|
920
|
-
|
921
|
-
if dst then
|
922
|
-
close_read_dst( dst )
|
923
|
-
set_dst_closing_write( dst )
|
924
|
-
end
|
925
|
-
|
926
|
-
return
|
927
|
-
end
|
928
|
-
|
929
1035
|
data = streamd_info[ :wbuff ]
|
930
1036
|
|
931
1037
|
# 写前为空,处理关闭写
|
@@ -955,6 +1061,7 @@ module Girl
|
|
955
1061
|
# puts "debug2 written streamd #{ written }"
|
956
1062
|
data = data[ written..-1 ]
|
957
1063
|
streamd_info[ :wbuff ] = data
|
1064
|
+
@traff_outs[ streamd_info[ :im ] ] += written
|
958
1065
|
|
959
1066
|
if dst && !dst.closed? then
|
960
1067
|
dst_info = @dst_infos[ dst ]
|
data/lib/girl/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: girl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.98.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- takafan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-10-
|
11
|
+
date: 2020-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: escape evil.
|
14
14
|
email:
|