girl 0.94.0 → 0.99.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.

@@ -9,6 +9,13 @@ module Girl
9
9
  @mutex = Mutex.new
10
10
  @reads = []
11
11
  @writes = []
12
+ @closing_tunds = []
13
+ @closing_dsts = []
14
+ @closing_streamds = []
15
+ @paused_dsts = []
16
+ @paused_streamds = []
17
+ @resume_dsts = []
18
+ @resume_streamds = []
12
19
  @roles = {} # sock => :dotr / :proxyd / :infod / :dst / :tund / :tcpd / :streamd
13
20
  @tund_infos = {} # tund => {}
14
21
  @tcpd_infos = {} # tcpd => {}
@@ -39,7 +46,6 @@ module Girl
39
46
  rs, ws = IO.select( @reads, @writes )
40
47
 
41
48
  @mutex.synchronize do
42
- # 先读,再写,避免打上关闭标记后读到
43
49
  rs.each do | sock |
44
50
  case @roles[ sock ]
45
51
  when :dotr then
@@ -98,12 +104,30 @@ module Girl
98
104
  private
99
105
 
100
106
  ##
101
- # add proxyd ctlmsg
107
+ # add closing dst
102
108
  #
103
- def add_proxyd_ctlmsg_tund_port( tund_info )
104
- data = [ 0, TUND_PORT, tund_info[ :port ], tund_info[ :tcpd_port ] ].pack( 'Q>Cnn' )
105
- @proxyd_info[ :ctlmsgs ] << [ data, tund_info[ :tun_addr ] ]
106
- add_write( @proxyd )
109
+ def add_closing_dst( dst )
110
+ return if dst.closed? || @closing_dsts.include?( dst )
111
+ @closing_dsts << dst
112
+ next_tick
113
+ end
114
+
115
+ ##
116
+ # add closing streamd
117
+ #
118
+ def add_closing_streamd( streamd )
119
+ return if streamd.closed? || @closing_streamds.include?( streamd )
120
+ @closing_streamds << streamd
121
+ next_tick
122
+ end
123
+
124
+ ##
125
+ # add closing tund
126
+ #
127
+ def add_closing_tund( tund )
128
+ return if tund.closed? || @closing_tunds.include?( tund )
129
+ @closing_tunds << tund
130
+ next_tick
107
131
  end
108
132
 
109
133
  ##
@@ -115,11 +139,68 @@ module Girl
115
139
  add_write( tund )
116
140
  end
117
141
 
142
+ ##
143
+ # add dst rbuff
144
+ #
145
+ def add_dst_rbuff( dst, data )
146
+ dst_info = @dst_infos[ dst ]
147
+ dst_info[ :rbuff ] << data
148
+
149
+ if dst_info[ :rbuff ].bytesize >= WBUFF_LIMIT then
150
+ # puts "debug1 dst.rbuff full"
151
+ add_closing_dst( dst )
152
+ end
153
+ end
154
+
155
+ ##
156
+ # add dst wbuff
157
+ #
158
+ def add_dst_wbuff( dst, data )
159
+ return if dst.closed? || @closing_dsts.include?( dst )
160
+
161
+ dst_info = @dst_infos[ dst ]
162
+ dst_info[ :wbuff ] << data
163
+ dst_info[ :last_recv_at ] = Time.new
164
+ add_write( dst )
165
+
166
+ if dst_info[ :wbuff ].bytesize >= WBUFF_LIMIT then
167
+ puts "p#{ Process.pid } #{ Time.new } pause streamd #{ dst_info[ :domain_port ] }"
168
+ add_paused_streamd( dst_info[ :streamd ] )
169
+ end
170
+ end
171
+
172
+ ##
173
+ # add paused dst
174
+ #
175
+ def add_paused_dst( dst )
176
+ return if dst.closed? || @paused_dsts.include?( dst )
177
+ @reads.delete( dst )
178
+ @paused_dsts << dst
179
+ end
180
+
181
+ ##
182
+ # add paused streamd
183
+ #
184
+ def add_paused_streamd( streamd )
185
+ return if streamd.closed? || @paused_streamds.include?( streamd )
186
+ @reads.delete( streamd )
187
+ @paused_streamds << streamd
188
+ end
189
+
190
+ ##
191
+ # add proxyd ctlmsg
192
+ #
193
+ def add_proxyd_ctlmsg_tund_port( tund_info )
194
+ data = [ 0, TUND_PORT, tund_info[ :port ], tund_info[ :tcpd_port ] ].pack( 'Q>Cnn' )
195
+ @proxyd_info[ :ctlmsgs ] << [ data, tund_info[ :tun_addr ] ]
196
+ add_write( @proxyd )
197
+ end
198
+
118
199
  ##
119
200
  # add read
120
201
  #
121
202
  def add_read( sock, role = nil )
122
- unless @reads.include?( sock ) then
203
+ if !sock.closed? && !@reads.include?( sock ) then
123
204
  @reads << sock
124
205
 
125
206
  if role then
@@ -128,11 +209,44 @@ module Girl
128
209
  end
129
210
  end
130
211
 
212
+ ##
213
+ # add resume dst
214
+ #
215
+ def add_resume_dst( dst )
216
+ return if @resume_dsts.include?( dst )
217
+ @resume_dsts << dst
218
+ next_tick
219
+ end
220
+
221
+ ##
222
+ # add resume streamd
223
+ #
224
+ def add_resume_streamd( streamd )
225
+ return if @resume_streamds.include?( streamd )
226
+ @resume_streamds << streamd
227
+ next_tick
228
+ end
229
+
230
+ ##
231
+ # add streamd wbuff
232
+ #
233
+ def add_streamd_wbuff( streamd, data )
234
+ return if streamd.closed? || @closing_streamds.include?( streamd )
235
+ streamd_info = @streamd_infos[ streamd ]
236
+ streamd_info[ :wbuff ] << data
237
+ add_write( streamd )
238
+
239
+ if streamd_info[ :wbuff ].bytesize >= WBUFF_LIMIT then
240
+ puts "p#{ Process.pid } #{ Time.new } pause dst #{ streamd_info[ :domain_port ] }"
241
+ add_paused_dst( streamd_info[ :dst ] )
242
+ end
243
+ end
244
+
131
245
  ##
132
246
  # add write
133
247
  #
134
248
  def add_write( sock )
135
- unless @writes.include?( sock ) then
249
+ if !sock.closed? && !@writes.include?( sock ) then
136
250
  @writes << sock
137
251
  end
138
252
  end
@@ -141,6 +255,7 @@ module Girl
141
255
  # close dst
142
256
  #
143
257
  def close_dst( dst )
258
+ return if dst.closed?
144
259
  # puts "debug1 close dst"
145
260
  close_sock( dst )
146
261
  dst_info = del_dst_info( dst )
@@ -169,7 +284,6 @@ module Girl
169
284
  dst_info = @dst_infos[ dst ]
170
285
  end
171
286
 
172
- dst_info[ :paused ] = false
173
287
  dst_info
174
288
  end
175
289
 
@@ -207,11 +321,12 @@ module Girl
207
321
  # close streamd
208
322
  #
209
323
  def close_streamd( streamd )
324
+ return if streamd.closed?
210
325
  # puts "debug1 close streamd"
211
326
  close_sock( streamd )
212
327
  streamd_info = @streamd_infos.delete( streamd )
213
328
  dst = streamd_info[ :dst ]
214
-
329
+
215
330
  if dst then
216
331
  close_read_dst( dst )
217
332
  set_dst_closing_write( dst )
@@ -228,8 +343,8 @@ module Girl
228
343
  tcpd = tund_info[ :tcpd ]
229
344
  close_sock( tcpd )
230
345
  @tcpd_infos.delete( tcpd )
231
- tund_info[ :dsts ].each{ | _, dst | set_dst_closing( dst ) }
232
346
  @tunneling_tunds.delete( tund_info[ :tun_addr ] )
347
+ tund_info[ :dsts ].each{ | _, dst | close_dst( dst ) }
233
348
  end
234
349
 
235
350
  ##
@@ -249,7 +364,6 @@ module Girl
249
364
  dst_info = @dst_infos[ dst ]
250
365
  end
251
366
 
252
- dst_info[ :closed_write ] = true
253
367
  dst_info
254
368
  end
255
369
 
@@ -270,7 +384,6 @@ module Girl
270
384
  streamd_info = @streamd_infos[ streamd ]
271
385
  end
272
386
 
273
- streamd_info[ :closed_write ] = true
274
387
  streamd_info
275
388
  end
276
389
 
@@ -304,10 +417,7 @@ module Girl
304
417
  created_at: Time.new, # 创建时间
305
418
  last_recv_at: nil, # 上一次收到新流量(由streamd收到)的时间
306
419
  last_sent_at: nil, # 上一次发出流量(由streamd发出)的时间
307
- paused: false, # 是否已暂停读
308
- closing: false, # 准备关闭
309
- closing_write: false, # 准备关闭写
310
- closed_write: false # 已关闭写
420
+ closing_write: false # 准备关闭写
311
421
  }
312
422
 
313
423
  add_read( dst, :dst )
@@ -327,13 +437,13 @@ module Girl
327
437
  def del_dst_info( dst )
328
438
  dst_info = @dst_infos.delete( dst )
329
439
  tund = dst_info[ :tund ]
440
+ tund_info = @tund_infos[ tund ]
330
441
 
331
- unless tund.closed? then
332
- tund_info = @tund_infos[ tund ]
442
+ if tund_info then
333
443
  tund_info[ :dsts ].delete( dst_info[ :id ] )
334
444
  tund_info[ :dst_ids ].delete( dst_info[ :src_id ] )
335
445
  end
336
-
446
+
337
447
  dst_info
338
448
  end
339
449
 
@@ -346,7 +456,6 @@ module Girl
346
456
  sleep CHECK_EXPIRE_INTERVAL
347
457
 
348
458
  @mutex.synchronize do
349
- trigger = false
350
459
  now = Time.new
351
460
 
352
461
  @tund_infos.each do | tund, tund_info |
@@ -354,8 +463,7 @@ module Girl
354
463
 
355
464
  if tund_info[ :dsts ].empty? && ( now - last_recv_at >= EXPIRE_AFTER ) then
356
465
  puts "p#{ Process.pid } #{ Time.new } expire tund #{ tund_info[ :port ] }"
357
- set_tund_closing( tund )
358
- trigger = true
466
+ add_closing_tund( tund )
359
467
  end
360
468
  end
361
469
 
@@ -365,12 +473,9 @@ module Girl
365
473
 
366
474
  if ( now - last_recv_at >= EXPIRE_AFTER ) && ( now - last_sent_at >= EXPIRE_AFTER ) then
367
475
  puts "p#{ Process.pid } #{ Time.new } expire dst #{ dst_info[ :domain_port ] }"
368
- set_dst_closing( dst )
369
- trigger = true
476
+ add_closing_dst( dst )
370
477
  end
371
478
  end
372
-
373
- next_tick if trigger
374
479
  end
375
480
  end
376
481
  end
@@ -385,33 +490,35 @@ module Girl
385
490
  sleep CHECK_RESUME_INTERVAL
386
491
 
387
492
  @mutex.synchronize do
388
- trigger = false
389
-
390
- @dst_infos.select{ | _, dst_info | dst_info[ :paused ] }.each do | dst, dst_info |
391
- streamd = dst_info[ :streamd ]
392
- streamd_info = @streamd_infos[ streamd ]
393
-
394
- if streamd_info[ :wbuff ].size < RESUME_BELOW then
395
- puts "p#{ Process.pid } #{ Time.new } resume dst #{ dst_info[ :domain_port ] }"
396
- dst_info[ :paused ] = false
397
- add_read( dst )
398
- trigger = true
493
+ @paused_dsts.each do | dst |
494
+ if dst.closed? then
495
+ add_resume_dst( dst )
496
+ else
497
+ dst_info = @dst_infos[ dst ]
498
+ streamd = dst_info[ :streamd ]
499
+ streamd_info = @streamd_infos[ streamd ]
500
+
501
+ if streamd_info[ :wbuff ].size < RESUME_BELOW then
502
+ puts "p#{ Process.pid } #{ Time.new } resume dst #{ dst_info[ :domain_port ] }"
503
+ add_resume_dst( dst )
504
+ end
399
505
  end
400
506
  end
401
507
 
402
- @streamd_infos.select{ | _, streamd_info | streamd_info[ :paused ] }.each do | streamd, streamd_info |
403
- dst = streamd_info[ :dst ]
404
- dst_info = @dst_infos[ dst ]
405
-
406
- if dst_info[ :wbuff ].size < RESUME_BELOW then
407
- puts "p#{ Process.pid } #{ Time.new } resume streamd #{ streamd_info[ :domain_port ] }"
408
- streamd_info[ :paused ] = false
409
- add_read( streamd )
410
- trigger = true
508
+ @paused_streamds.each do | streamd |
509
+ if streamd.closed? then
510
+ add_resume_streamd( streamd )
511
+ else
512
+ streamd_info = @streamd_infos[ streamd ]
513
+ dst = streamd_info[ :dst ]
514
+ dst_info = @dst_infos[ dst ]
515
+
516
+ if dst_info[ :wbuff ].size < RESUME_BELOW then
517
+ puts "p#{ Process.pid } #{ Time.new } resume streamd #{ streamd_info[ :domain_port ] }"
518
+ add_resume_streamd( streamd )
519
+ end
411
520
  end
412
521
  end
413
-
414
- next_tick if trigger
415
522
  end
416
523
  end
417
524
  end
@@ -538,81 +645,83 @@ module Girl
538
645
  written
539
646
  end
540
647
 
541
- ##
542
- # set dst closing
543
- #
544
- def set_dst_closing( dst )
545
- return if dst.closed?
546
-
547
- dst_info = @dst_infos[ dst ]
548
-
549
- if dst_info[ :closed_write ] then
550
- close_read_dst( dst )
551
- else
552
- dst_info[ :closing ] = true
553
- @reads.delete( dst )
554
- add_write( dst )
555
- end
556
- end
557
-
558
648
  ##
559
649
  # set dst closing write
560
650
  #
561
651
  def set_dst_closing_write( dst )
562
- return if dst.closed?
652
+ return if dst.closed? || @closing_dsts.include?( dst )
563
653
 
564
654
  dst_info = @dst_infos[ dst ]
565
- return if dst_info[ :closed_write ]
655
+ return if dst_info[ :closing_write ]
566
656
 
567
657
  dst_info[ :closing_write ] = true
568
658
  add_write( dst )
569
659
  end
570
660
 
571
- ##
572
- # set streamd closing
573
- #
574
- def set_streamd_closing( streamd )
575
- return if streamd.closed?
576
- streamd_info = @streamd_infos[ streamd ]
577
-
578
- if streamd_info[ :closed_write ] then
579
- close_read_streamd( streamd )
580
- else
581
- streamd_info[ :closing ] = true
582
- @reads.delete( streamd )
583
- add_write( streamd )
584
- end
585
- end
586
-
587
661
  ##
588
662
  # set streamd closing write
589
663
  #
590
664
  def set_streamd_closing_write( streamd )
591
- return if streamd.closed?
665
+ return if streamd.closed? || @closing_streamds.include?( streamd )
592
666
 
593
667
  streamd_info = @streamd_infos[ streamd ]
594
- return if streamd_info[ :closed_write ]
668
+ return if streamd_info[ :closing_write ]
595
669
 
596
670
  streamd_info[ :closing_write ] = true
597
671
  add_write( streamd )
598
672
  end
599
673
 
600
- ##
601
- # set tund is closing
602
- #
603
- def set_tund_closing( tund )
604
- return if tund.closed?
605
- tund_info = @tund_infos[ tund ]
606
- tund_info[ :closing ] = true
607
- @reads.delete( tund )
608
- add_write( tund )
609
- end
610
-
611
674
  ##
612
675
  # read dotr
613
676
  #
614
677
  def read_dotr( dotr )
615
- dotr.read( 1 )
678
+ dotr.read_nonblock( READ_SIZE )
679
+
680
+ # 处理关闭
681
+ if @closing_tunds.any? then
682
+ @closing_tunds.each do | tund |
683
+ unless tund.closed? then
684
+ tund_info = @tund_infos[ tund ]
685
+
686
+ if tund_info[ :changed_tun_addr ] then
687
+ data = [ 0, IP_CHANGED ].pack( 'Q>C' )
688
+ send_data( tund, data, tund_info[ :changed_tun_addr ] )
689
+ end
690
+
691
+ close_tund( tund )
692
+ end
693
+ end
694
+
695
+ @closing_tunds.clear
696
+ end
697
+
698
+ if @closing_dsts.any? then
699
+ @closing_dsts.each{ | dst | close_dst( dst ) }
700
+ @closing_dsts.clear
701
+ end
702
+
703
+ if @closing_streamds.any? then
704
+ @closing_streamds.each{ | streamd | close_streamd( streamd ) }
705
+ @closing_streamds.clear
706
+ end
707
+
708
+ if @resume_dsts.any? then
709
+ @resume_dsts.each do | dst |
710
+ add_read( dst )
711
+ @paused_dsts.delete( dst )
712
+ end
713
+
714
+ @resume_dsts.clear
715
+ end
716
+
717
+ if @resume_streamds.any? then
718
+ @resume_streamds.each do | streamd |
719
+ add_read( streamd )
720
+ @paused_streamds.delete( streamd )
721
+ end
722
+
723
+ @resume_streamds.clear
724
+ end
616
725
  end
617
726
 
618
727
  ##
@@ -667,7 +776,6 @@ module Girl
667
776
  dst_ids: {}, # src_id => dst_id
668
777
  created_at: Time.new, # 创建时间
669
778
  last_recv_at: nil, # 上一次收到流量的时间
670
- closing: false, # 准备关闭
671
779
  changed_tun_addr: nil # 记录到和tun addr不符的来源地址
672
780
  }
673
781
 
@@ -707,6 +815,8 @@ module Girl
707
815
  # read tund
708
816
  #
709
817
  def read_tund( tund )
818
+ return if tund.closed?
819
+
710
820
  begin
711
821
  data, addrinfo, rflags, *controls = tund.recvmsg_nonblock
712
822
  rescue IO::WaitReadable, Errno::EINTR
@@ -721,7 +831,7 @@ module Girl
721
831
  # 通常是光猫刷新ip和端口,但万一不是,为了避免脏数据注入,关闭tund
722
832
  puts "p#{ Process.pid } #{ Time.new } from #{ addrinfo.inspect } not match tun addr #{ Addrinfo.new( tund_info[ :tun_addr ] ).inspect }"
723
833
  tund_info[ :changed_tun_addr ] = from_addr
724
- set_tund_closing( tund )
834
+ add_closing_tund( tund )
725
835
  return
726
836
  end
727
837
 
@@ -756,7 +866,7 @@ module Girl
756
866
  resolve_domain( tund, src_id, domain_port )
757
867
  when TUN_FIN then
758
868
  puts "p#{ Process.pid } #{ Time.new } recv tun fin"
759
- set_tund_closing( tund )
869
+ add_closing_tund( tund )
760
870
  end
761
871
  end
762
872
 
@@ -764,6 +874,8 @@ module Girl
764
874
  # read tcpd
765
875
  #
766
876
  def read_tcpd( tcpd )
877
+ return if tcpd.closed?
878
+
767
879
  begin
768
880
  streamd, addrinfo = tcpd.accept_nonblock
769
881
  rescue IO::WaitReadable, Errno::EINTR
@@ -782,10 +894,7 @@ module Girl
782
894
  dst: nil, # 对应dst
783
895
  domain_port: nil, # dst的目的地和端口
784
896
  wbuff: '', # 写前,写往近端stream
785
- paused: false, # 是否已暂停读
786
- closing: false, # 准备关闭
787
- closing_write: false, # 准备关闭写
788
- closed_write: false # 已关闭写
897
+ closing_write: false # 准备关闭写
789
898
  }
790
899
 
791
900
  add_read( streamd, :streamd )
@@ -819,22 +928,10 @@ module Girl
819
928
  streamd_info = @streamd_infos[ streamd ]
820
929
  data = @custom.encode( data )
821
930
  # puts "debug2 add streamd.wbuff encoded #{ data.bytesize }"
822
- streamd_info[ :wbuff ] << data
823
- add_write( streamd )
824
-
825
- if streamd_info[ :wbuff ].bytesize >= WBUFF_LIMIT then
826
- puts "p#{ Process.pid } #{ Time.new } pause dst #{ dst_info[ :domain_port ] }"
827
- dst_info[ :paused ] = true
828
- @reads.delete( dst )
829
- end
931
+ add_streamd_wbuff( streamd, data )
830
932
  end
831
933
  else
832
- dst_info[ :rbuff ] << data
833
-
834
- if dst_info[ :rbuff ].bytesize >= WBUFF_LIMIT then
835
- # puts "debug1 dst.rbuff full"
836
- set_dst_closing( dst )
837
- end
934
+ add_dst_rbuff( dst, data )
838
935
  end
839
936
  end
840
937
 
@@ -866,7 +963,7 @@ module Girl
866
963
  tund = streamd_info[ :tund ]
867
964
 
868
965
  if tund.closed? then
869
- set_streamd_closing( streamd )
966
+ add_closing_streamd( streamd )
870
967
  return
871
968
  end
872
969
 
@@ -874,7 +971,7 @@ module Girl
874
971
  dst = tund_info[ :dsts ][ dst_id ]
875
972
 
876
973
  unless dst then
877
- set_streamd_closing( streamd )
974
+ add_closing_streamd( streamd )
878
975
  return
879
976
  end
880
977
 
@@ -885,7 +982,8 @@ module Girl
885
982
 
886
983
  unless dst_info[ :rbuff ].empty? then
887
984
  # puts "debug1 encode and move dst.rbuff to streamd.wbuff"
888
- streamd_info[ :wbuff ] << @custom.encode( dst_info[ :rbuff ] )
985
+ data2 = @custom.encode( dst_info[ :rbuff ] )
986
+ add_streamd_wbuff( streamd, data2 )
889
987
  end
890
988
 
891
989
  dst_info[ :streamd ] = streamd
@@ -894,20 +992,9 @@ module Girl
894
992
  return if data.empty?
895
993
  end
896
994
 
897
- unless dst.closed? then
898
- dst_info = @dst_infos[ dst ]
899
- data = @custom.decode( data )
900
- # puts "debug2 add dst.wbuff decoded #{ data.bytesize }"
901
- dst_info[ :wbuff ] << data
902
- dst_info[ :last_recv_at ] = Time.new
903
- add_write( dst )
904
-
905
- if dst_info[ :wbuff ].bytesize >= WBUFF_LIMIT then
906
- puts "p#{ Process.pid } #{ Time.new } pause streamd #{ streamd_info[ :domain_port ] }"
907
- streamd_info[ :paused ] = true
908
- @reads.delete( streamd )
909
- end
910
- end
995
+ data = @custom.decode( data )
996
+ # puts "debug2 add dst.wbuff decoded #{ data.bytesize }"
997
+ add_dst_wbuff( dst, data )
911
998
  end
912
999
 
913
1000
  ##
@@ -934,21 +1021,9 @@ module Girl
934
1021
  # write tund
935
1022
  #
936
1023
  def write_tund( tund )
1024
+ return if tund.closed?
937
1025
  tund_info = @tund_infos[ tund ]
938
1026
 
939
- # 处理关闭
940
- if tund_info[ :closing ] then
941
- if tund_info[ :changed_tun_addr ] then
942
- data = [ 0, IP_CHANGED ].pack( 'Q>C' )
943
- send_data( tund, data, tund_info[ :changed_tun_addr ] )
944
- end
945
-
946
- close_tund( tund )
947
- return
948
- end
949
-
950
- now = Time.new
951
-
952
1027
  # 发ctlmsg
953
1028
  while tund_info[ :ctlmsgs ].any? do
954
1029
  data = tund_info[ :ctlmsgs ].first
@@ -975,13 +1050,6 @@ module Girl
975
1050
  return if dst.closed?
976
1051
  dst_info = @dst_infos[ dst ]
977
1052
  streamd = dst_info[ :streamd ]
978
-
979
- # 处理关闭
980
- if dst_info[ :closing ] then
981
- close_dst( dst )
982
- return
983
- end
984
-
985
1053
  data = dst_info[ :wbuff ]
986
1054
 
987
1055
  # 写前为空,处理关闭写
@@ -1021,13 +1089,6 @@ module Girl
1021
1089
  return if streamd.closed?
1022
1090
  streamd_info = @streamd_infos[ streamd ]
1023
1091
  dst = streamd_info[ :dst ]
1024
-
1025
- # 处理关闭
1026
- if streamd_info[ :closing ] then
1027
- close_streamd( streamd )
1028
- return
1029
- end
1030
-
1031
1092
  data = streamd_info[ :wbuff ]
1032
1093
 
1033
1094
  # 写前为空,处理关闭写