girl 0.73.0 → 0.77.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d975dc92620d5ea2c27493ed0fbbb19a145fc7ecda564c2f5609eb710e06912d
4
- data.tar.gz: 5c098f6b8aecc89d246e8e4b1c477eb42e86ce695b27d4329962255d380b4f09
3
+ metadata.gz: 67fe23b6655278a6589fda1afc4ae23af79dfb0f25d023cd357b4722e07fb1a2
4
+ data.tar.gz: af7f01bb573014f78d4ecbc06926749d2326c74862b02b53b2f300fd2a927cd0
5
5
  SHA512:
6
- metadata.gz: 326b198254e6508e42b00145e00c56214574510448ba05b852b257b44da2c54b443509d1e2af514a668b1394e27cfff2ae93cf1957526adf9315a73e32a3ebbd
7
- data.tar.gz: 21e0cab87ffe323314607d74c3884eb2987208a9375b417c1158d1768e7aafcf536d7370d5fdff58b08d1d7acb95da009eba57ddcc9d9d22702e405078b2c7e5
6
+ metadata.gz: 5f5ef13b0650eb3c289de191f8aeabfd1e288b58a6fbd680ced51c027cd43d9a4bc7ad988398e611a0dd3cc88ddd894e87dc7252b03b24e8a1e7dd5464d81b73
7
+ data.tar.gz: '094ee98b813b04ec12b8a1ea9181c6353cf69eb69ecc595bbbe1f326f1cf6a70b9a11778cfc4d0eee84f59308a0b5ec4f556bab8982befd0d5a1a51b4e850ba3'
@@ -30,21 +30,10 @@ module Girl
30
30
  RESERVED_ROUTE = <<EOF
31
31
  0.0.0.0/8
32
32
  10.0.0.0/8
33
- 100.64.0.0/10
34
33
  127.0.0.0/8
35
34
  169.254.0.0/16
36
35
  172.16.0.0/12
37
- 192.0.0.0/24
38
- 192.0.2.0/24
39
- 192.31.196.0/24
40
- 192.52.193.0/24
41
- 192.88.99.0/24
42
36
  192.168.0.0/16
43
- 192.175.48.0/24
44
- 198.18.0.0/15
45
- 198.51.100.0/24
46
- 203.0.113.0/24
47
- 240.0.0.0/4
48
37
  255.255.255.255/32
49
38
  EOF
50
39
  end
@@ -113,7 +113,7 @@ module Girl
113
113
 
114
114
  @tun_info[ :src_exts ].each do | src_id, src_ext |
115
115
  if src_ext[ :src ].closed? && ( now - src_ext[ :last_continue_at ] > EXPIRE_AFTER )
116
- puts "p#{ Process.pid } #{ Time.new } expire src ext #{ src_id }"
116
+ puts "p#{ Process.pid } #{ Time.new } expire src ext #{ src_ext[ :destination_domain ] }"
117
117
  del_src_ext( src_id )
118
118
  end
119
119
  end
@@ -123,20 +123,16 @@ module Girl
123
123
  end
124
124
 
125
125
  @src_infos.each do | src, src_info |
126
- is_expired = src_info[ :last_recv_at ].nil? && ( now - src_info[ :created_at ] > EXPIRE_NEW )
127
-
128
- if is_expired
129
- puts "p#{ Process.pid } #{ Time.new } expire src"
126
+ if now - src_info[ :last_continue_at ] > EXPIRE_AFTER
127
+ puts "p#{ Process.pid } #{ Time.new } expire src #{ src_info[ :destination_domain ] }"
130
128
  set_is_closing( src )
131
129
  need_trigger = true
132
130
  end
133
131
  end
134
132
 
135
133
  @dst_infos.each do | dst, dst_info |
136
- is_expired = dst_info[ :last_recv_at ].nil? && ( now - dst_info[ :created_at ] > EXPIRE_NEW )
137
-
138
- if is_expired
139
- puts "p#{ Process.pid } #{ Time.new } expire dst"
134
+ if now - dst_info[ :last_continue_at ] > EXPIRE_AFTER
135
+ puts "p#{ Process.pid } #{ Time.new } expire dst #{ dst_info[ :domain ] }"
140
136
  set_is_closing( dst )
141
137
  need_trigger = true
142
138
  end
@@ -337,6 +333,7 @@ module Girl
337
333
  #
338
334
  def new_a_dst( src, ip_info )
339
335
  src_info = @src_infos[ src ]
336
+ domain = src_info[ :destination_domain ]
340
337
  destination_addr = Socket.sockaddr_in( src_info[ :destination_port ], ip_info.ip_address )
341
338
  dst = Socket.new( ip_info.ipv4? ? Socket::AF_INET : Socket::AF_INET6, Socket::SOCK_STREAM, 0 )
342
339
 
@@ -357,15 +354,15 @@ module Girl
357
354
  # puts "debug1 a new dst #{ dst.local_address.inspect }"
358
355
  local_port = dst.local_address.ip_port
359
356
  @dst_infos[ dst ] = {
360
- local_port: local_port, # 本地端口
361
- src: src, # 对应src
362
- wbuff: '', # 写前
363
- cache: '', # 块读出缓存
364
- chunks: [], # 块队列,写前达到块大小时结一个块 filename
365
- spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
366
- created_at: Time.new, # 创建时间
367
- last_recv_at: nil, # 上一次收到流量的时间,过期关闭
368
- is_closing: false # 是否准备关闭
357
+ local_port: local_port, # 本地端口
358
+ src: src, # 对应src
359
+ domain: domain, # 域名
360
+ wbuff: '', # 写前
361
+ cache: '', # 块读出缓存
362
+ chunks: [], # 块队列,写前达到块大小时结一个块 filename
363
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
364
+ last_continue_at: Time.new, # 上一次发生流量的时间
365
+ is_closing: false # 是否准备关闭
369
366
  }
370
367
 
371
368
  add_read( dst, :dst )
@@ -396,27 +393,29 @@ module Girl
396
393
  new_a_tun
397
394
  end
398
395
 
396
+ src_info = @src_infos[ src ]
397
+ src_id = src_info[ :id ]
398
+ destination_port = src_info[ :destination_port ]
399
+ destination_domain = src_info[ :destination_domain ]
400
+
399
401
  src_ext = {
400
- src: src, # src
401
- dst_port: nil, # 远端dst端口
402
- wmems: {}, # 写后 pack_id => data
403
- send_ats: {}, # 上一次发出时间 pack_id => send_at
404
- relay_pack_id: 0, # 转发到几
405
- continue_dst_pack_id: 0, # 收到几
406
- pieces: {}, # 跳号包 dst_pack_id => data
407
- is_dst_closed: false, # dst是否已关闭
408
- biggest_dst_pack_id: 0, # dst最大包号码
409
- completed_pack_id: 0, # 完成到几(对面收到几)
410
- last_continue_at: Time.new # 创建,或者上一次收到连续流量,或者发出新包的时间
402
+ src: src, # src
403
+ dst_port: nil, # 远端dst端口
404
+ destination_domain: destination_domain, # 目的地域名
405
+ wmems: {}, # 写后 pack_id => data
406
+ send_ats: {}, # 上一次发出时间 pack_id => send_at
407
+ relay_pack_id: 0, # 转发到几
408
+ continue_dst_pack_id: 0, # 收到几
409
+ pieces: {}, # 跳号包 dst_pack_id => data
410
+ is_dst_closed: false, # dst是否已关闭
411
+ biggest_dst_pack_id: 0, # dst最大包号码
412
+ completed_pack_id: 0, # 完成到几(对面收到几)
413
+ last_continue_at: Time.new # 上一次发生流量的时间
411
414
  }
412
415
 
413
- src_info = @src_infos[ src ]
414
- src_id = src_info[ :id ]
415
416
  @tun_info[ :src_exts ][ src_id ] = src_ext
416
417
  src_info[ :proxy_type ] = :tunnel
417
418
 
418
- destination_port = src_info[ :destination_port ]
419
- destination_domain = src_info[ :destination_domain ]
420
419
  destination_domain_port = [ destination_domain, destination_port ].join( ':' )
421
420
  data = [ [ 0, A_NEW_SOURCE, src_id ].pack( 'Q>CQ>' ), @custom.encode( destination_domain_port ) ].join
422
421
  loop_send_a_new_source( src_ext, data )
@@ -602,6 +601,23 @@ module Girl
602
601
  end
603
602
  end
604
603
 
604
+ ##
605
+ # send data
606
+ #
607
+ def send_data( tun, data, to_addr )
608
+ begin
609
+ tun.sendmsg( data, 0, to_addr )
610
+ rescue IO::WaitWritable, Errno::EINTR
611
+ return false
612
+ rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENETDOWN => e
613
+ puts "#{ Time.new } #{ e.class }, close tun"
614
+ close_tun( tun )
615
+ return false
616
+ end
617
+
618
+ true
619
+ end
620
+
605
621
  ##
606
622
  # close src
607
623
  #
@@ -768,6 +784,7 @@ module Girl
768
784
  # puts "debug2 write src #{ written }"
769
785
  data = data[ written..-1 ]
770
786
  src_info[ from ] = data
787
+ src_info[ :last_continue_at ] = Time.new
771
788
  end
772
789
 
773
790
  ##
@@ -819,6 +836,7 @@ module Girl
819
836
  # puts "debug2 write dst #{ written }"
820
837
  data = data[ written..-1 ]
821
838
  dst_info[ from ] = data
839
+ dst_info[ :last_continue_at ] = Time.new
822
840
  end
823
841
 
824
842
  ##
@@ -836,13 +854,7 @@ module Girl
836
854
  while @tun_info[ :ctlmsgs ].any?
837
855
  to_addr, data = @tun_info[ :ctlmsgs ].first
838
856
 
839
- begin
840
- tun.sendmsg( data, 0, to_addr )
841
- rescue IO::WaitWritable, Errno::EINTR
842
- return
843
- rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH => e
844
- puts "#{ Time.new } #{ e.class }, close tun"
845
- close_tun( tun )
857
+ unless send_data( tun, data, to_addr )
846
858
  return
847
859
  end
848
860
 
@@ -858,13 +870,7 @@ module Girl
858
870
  data = src_ext[ :wmems ][ pack_id ]
859
871
 
860
872
  if data
861
- begin
862
- tun.sendmsg( data, 0, @tun_info[ :tund_addr ] )
863
- rescue IO::WaitWritable, Errno::EINTR
864
- return
865
- rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH => e
866
- puts "#{ Time.new } #{ e.class }, close tun"
867
- close_tun( tun )
873
+ unless send_data( tun, data, @tun_info[ :tund_addr ] )
868
874
  return
869
875
  end
870
876
  end
@@ -930,13 +936,7 @@ module Girl
930
936
 
931
937
  data = [ [ pack_id, src_id ].pack( 'Q>Q>' ), data ].join
932
938
 
933
- begin
934
- tun.sendmsg( data, 0, @tun_info[ :tund_addr ] )
935
- rescue IO::WaitWritable, Errno::EINTR
936
- return
937
- rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH => e
938
- puts "#{ Time.new } #{ e.class }, close tun"
939
- close_tun( tun )
939
+ unless send_data( tun, data, @tun_info[ :tund_addr ] )
940
940
  return
941
941
  end
942
942
 
@@ -967,26 +967,25 @@ module Girl
967
967
  return
968
968
  end
969
969
 
970
- id = rand( ( 2 ** 64 ) - 1 ) + 1
970
+ id = rand( ( 2 ** 64 ) - 2 ) + 1
971
971
  # puts "debug1 accept a src #{ addrinfo.inspect } #{ id }"
972
972
 
973
973
  @src_infos[ src ] = {
974
- id: id, # id
975
- proxy_proto: :uncheck, # :uncheck / :http / :socks5
976
- proxy_type: :uncheck, # :uncheck / :checking / :direct / :tunnel / :negotiation
977
- dst: nil, # :direct的场合,对应的dst
978
- destination_domain: nil, # 目的地域名
979
- destination_port: nil, # 目的地端口
980
- biggest_pack_id: 0, # 最大包号码
981
- is_connect: true, # 代理协议是http的场合,是否是CONNECT
982
- rbuffs: [], # 非CONNECT,dst或者远端dst未准备好,暂存流量 [ pack_id, data ]
983
- wbuff: '', # 写前
984
- cache: '', # 块读出缓存
985
- chunks: [], # 块队列,写前达到块大小时结一个块 filename
986
- spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
987
- created_at: Time.new, # 创建时间
988
- last_recv_at: nil, # 上一次收到流量的时间,过期关闭
989
- is_closing: false # 是否准备关闭
974
+ id: id, # id
975
+ proxy_proto: :uncheck, # :uncheck / :http / :socks5
976
+ proxy_type: :uncheck, # :uncheck / :checking / :direct / :tunnel / :negotiation
977
+ dst: nil, # :direct的场合,对应的dst
978
+ destination_domain: nil, # 目的地域名
979
+ destination_port: nil, # 目的地端口
980
+ biggest_pack_id: 0, # 最大包号码
981
+ is_connect: true, # 代理协议是http的场合,是否是CONNECT
982
+ rbuffs: [], # 非CONNECT,dst或者远端dst未准备好,暂存流量 [ pack_id, data ]
983
+ wbuff: '', # 写前
984
+ cache: '', # 块读出缓存
985
+ chunks: [], # 块队列,写前达到块大小时结一个块 filename
986
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
987
+ last_continue_at: Time.new, # 上一次发生流量的时间
988
+ is_closing: false # 是否准备关闭
990
989
  }
991
990
 
992
991
  add_read( src, :src )
@@ -1008,7 +1007,7 @@ module Girl
1008
1007
 
1009
1008
  # puts "debug2 read src #{ data.inspect }"
1010
1009
  src_info = @src_infos[ src ]
1011
- src_info[ :last_recv_at ] = Time.new
1010
+ src_info[ :last_continue_at ] = Time.new
1012
1011
  proxy_type = src_info[ :proxy_type ]
1013
1012
 
1014
1013
  case proxy_type
@@ -1198,7 +1197,7 @@ module Girl
1198
1197
 
1199
1198
  # puts "debug2 read dst #{ data.inspect }"
1200
1199
  dst_info = @dst_infos[ dst ]
1201
- dst_info[ :last_recv_at ] = Time.new
1200
+ dst_info[ :last_continue_at ] = Time.new
1202
1201
  src = dst_info[ :src ]
1203
1202
 
1204
1203
  if src.closed?
@@ -112,7 +112,7 @@ module Girl
112
112
 
113
113
  tund_info[ :dst_exts ].each do | dst_local_port, dst_ext |
114
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_local_port }"
115
+ puts "p#{ Process.pid } #{ Time.new } expire dst ext #{ dst_ext[ :domain_port ] }"
116
116
  del_dst_ext( tund, dst_local_port )
117
117
  end
118
118
  end
@@ -123,10 +123,8 @@ module Girl
123
123
  end
124
124
 
125
125
  @dst_infos.each do | dst, dst_info |
126
- is_expired = dst_info[ :last_recv_at ].nil? && ( now - dst_info[ :created_at ] > EXPIRE_NEW )
127
-
128
- if is_expired
129
- puts "p#{ Process.pid } #{ Time.new } expire dst"
126
+ if now - dst_info[ :last_continue_at ] > EXPIRE_AFTER
127
+ puts "p#{ Process.pid } #{ Time.new } expire dst #{ dst_info[ :domain_port ] }"
130
128
  set_is_closing( dst )
131
129
  need_trigger = true
132
130
  end
@@ -195,7 +193,7 @@ module Girl
195
193
 
196
194
  if Time.new - created_at < RESOLV_CACHE_EXPIRE
197
195
  # puts "debug1 #{ destination_domain_port } hit resolv cache #{ Addrinfo.new( destination_addr ).inspect }"
198
- deal_with_destination_addr( tund, src_id, destination_addr )
196
+ deal_with_destination_addr( tund, src_id, destination_addr, destination_domain_port )
199
197
  return
200
198
  end
201
199
 
@@ -223,7 +221,7 @@ module Girl
223
221
  @resolv_caches[ destination_domain_port ] = [ destination_addr, Time.new ]
224
222
 
225
223
  unless tund.closed?
226
- if deal_with_destination_addr( tund, src_id, destination_addr )
224
+ if deal_with_destination_addr( tund, src_id, destination_addr, destination_domain_port )
227
225
  next_tick
228
226
  end
229
227
  end
@@ -235,7 +233,7 @@ module Girl
235
233
  ##
236
234
  # deal with destination addr
237
235
  #
238
- def deal_with_destination_addr( tund, src_id, destination_addr )
236
+ def deal_with_destination_addr( tund, src_id, destination_addr, destination_domain_port )
239
237
  dst = Socket.new( Addrinfo.new( destination_addr ).ipv4? ? Socket::AF_INET : Socket::AF_INET6, Socket::SOCK_STREAM, 0 )
240
238
  dst.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
241
239
 
@@ -250,33 +248,34 @@ module Girl
250
248
  local_port = dst.local_address.ip_port
251
249
 
252
250
  @dst_infos[ dst ] = {
253
- local_port: local_port, # 本地端口
254
- tund: tund, # 对应tund
255
- biggest_pack_id: 0, # 最大包号码
256
- wbuff: '', # 写前
257
- cache: '', # 块读出缓存
258
- chunks: [], # 块队列,写前达到块大小时结一个块 filename
259
- spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
260
- created_at: Time.new, # 创建时间
261
- last_recv_at: nil, # 上一次收到流量的时间,过期关闭
262
- is_closing: false # 是否准备关闭
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 # 是否准备关闭
263
261
  }
264
262
  add_read( dst, :dst )
265
263
 
266
264
  tund_info = @tund_infos[ tund ]
267
265
  tund_info[ :dst_local_ports ][ src_id ] = local_port
268
266
  tund_info[ :dst_exts ][ local_port ] = {
269
- dst: dst, # dst
270
- src_id: src_id, # 近端src id
271
- wmems: {}, # 写后 pack_id => data
272
- send_ats: {}, # 上一次发出时间 pack_id => send_at
273
- relay_pack_id: 0, # 转发到几
274
- continue_src_pack_id: 0, # 收到几
275
- pieces: {}, # 跳号包 src_pack_id => data
276
- is_src_closed: false, # src是否已关闭
277
- biggest_src_pack_id: 0, # src最大包号码
278
- completed_pack_id: 0, # 完成到几(对面收到几)
279
- last_continue_at: Time.new # 创建,或者上一次收到连续流量,或者发出新包的时间
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 # 上一次发生流量的时间
280
279
  }
281
280
 
282
281
  data = [ 0, PAIRED, src_id, local_port ].pack( 'Q>CQ>n' )
@@ -416,6 +415,23 @@ module Girl
416
415
  end
417
416
  end
418
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
+
419
435
  ##
420
436
  # close dst
421
437
  #
@@ -586,6 +602,7 @@ module Girl
586
602
  # puts "debug2 write dst #{ written }"
587
603
  data = data[ written..-1 ]
588
604
  dst_info[ from ] = data
605
+ dst_info[ :last_continue_at ] = Time.new
589
606
  end
590
607
 
591
608
  ##
@@ -603,9 +620,7 @@ module Girl
603
620
  while tund_info[ :ctlmsgs ].any?
604
621
  data = tund_info[ :ctlmsgs ].first
605
622
 
606
- begin
607
- tund.sendmsg( data, 0, tund_info[ :tun_addr ] )
608
- rescue IO::WaitWritable, Errno::EINTR
623
+ unless send_data( tund, data, tund_info[ :tun_addr ] )
609
624
  return
610
625
  end
611
626
 
@@ -621,9 +636,7 @@ module Girl
621
636
  data = dst_ext[ :wmems ][ pack_id ]
622
637
 
623
638
  if data
624
- begin
625
- tund.sendmsg( data, 0, tund_info[ :tun_addr ] )
626
- rescue IO::WaitWritable, Errno::EINTR
639
+ unless send_data( tund, data, tund_info[ :tun_addr ] )
627
640
  return
628
641
  end
629
642
  end
@@ -689,9 +702,7 @@ module Girl
689
702
 
690
703
  data = [ [ pack_id, dst_local_port ].pack( 'Q>n' ), data ].join
691
704
 
692
- begin
693
- tund.sendmsg( data, 0, tund_info[ :tun_addr ] )
694
- rescue IO::WaitWritable, Errno::EINTR
705
+ unless send_data( tund, data, tund_info[ :tun_addr ] )
695
706
  return
696
707
  end
697
708
 
@@ -775,7 +786,7 @@ module Girl
775
786
 
776
787
  # puts "debug2 read dst #{ data.inspect }"
777
788
  dst_info = @dst_infos[ dst ]
778
- dst_info[ :last_recv_at ] = Time.new
789
+ dst_info[ :last_continue_at ] = Time.new
779
790
  tund = dst_info[ :tund ]
780
791
 
781
792
  if tund.closed?
@@ -1,3 +1,3 @@
1
1
  module Girl
2
- VERSION = '0.73.0'.freeze
2
+ VERSION = '0.77.0'.freeze
3
3
  end
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.73.0
4
+ version: 0.77.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-07-16 00:00:00.000000000 Z
11
+ date: 2020-07-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: while internet is evil, here's a girl.
14
14
  email:
@@ -49,8 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
49
  - !ruby/object:Gem::Version
50
50
  version: '0'
51
51
  requirements: []
52
- rubyforge_project:
53
- rubygems_version: 2.7.6.2
52
+ rubygems_version: 3.1.2
54
53
  signing_key:
55
54
  specification_version: 4
56
55
  summary: 妹子