girl 0.86.0 → 0.90.0

This diff has not been reviewed by any users.
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48f438a56f35bb4fd0fbb76e57f08b5ea1a6e8890b8a2b7542881b1f69e89461
4
- data.tar.gz: 8ad2943b900e91f8a6e29306fe29249d467374f738261daadaaa429961370206
3
+ metadata.gz: 2e1916068595360e4ab83189c93759e2c6ed05979d4a4128ff83f95390e91dce
4
+ data.tar.gz: 9f39d67731002186f66f04e4bb463834980a243fc0d6322b5e35f749fa356f95
5
5
  SHA512:
6
- metadata.gz: 98512a3678ebde91bff827995447765c57c558f885313d05ecd8c831e85354a9ea81beb5ff038c18787b1bd0c15fe88317b85a3b6e84458a5709bcf59b027fff
7
- data.tar.gz: 263aff6f7b79025d5404f6b6e4a68b01dfa899df0af4556b0085f72e9bb3227d6423be77e3c1cd12445fb1c9727c426be9750a9f7cea72d112b72c59702917a7
6
+ metadata.gz: 18e61543e05ae79c31ee76a8504cfcfef1271aa1419430ae667b46c8291dcc790630aa1123bc662ce817117a26b68ac343f5c7679134a3205548f32ba28cfb31
7
+ data.tar.gz: 797fa163ab50c3ace753ac833381bdeb040876c12de7d2b5570dbe0f3a799a91b18fa220ca5ea4ecbc8cfad69aabbd1dd245ad1bcd404b92423020890f5d38d1
@@ -1,18 +1,12 @@
1
1
  module Girl
2
- PACK_SIZE = 1328 # 包大小 1400(console MTU) - 8(PPPoE header) - 40(IPv6 header) - 8(UDP header) - 8(pack id) - 8(src id) = 1328
3
- CHUNK_SIZE = PACK_SIZE * 1000 # 块大小
4
- WBUFFS_LIMIT = 1000 # 写前上限,超过上限结一个块
5
- WMEMS_LIMIT = 100_000 # 写后上限,达到上限暂停写
6
- RESUME_BELOW = 50_000 # 降到多少以下恢复写
7
- EXPIRE_NEW = 10 # 创建之后多久没有流量进来,过期
8
- EXPIRE_AFTER = 300 # 多久没有新流量,过期
9
- CHECK_EXPIRE_INTERVAL = 30 # 检查过期间隔
10
- CHECK_STATUS_INTERVAL = 0.5 # 发送状态间隔
11
- SEND_STATUS_UNTIL = 10 # 持续的告之对面状态,直到没有流量往来,持续多少秒
12
- MISS_RANGE_LIMIT = 500 # miss段上限,达到上限忽略要后面的段
13
- RESENDING_LIMIT = 5000 # 重传队列上限
14
- CONFUSE_UNTIL = 5 # 混淆前几个包
15
- RESOLV_CACHE_EXPIRE = 300 # dns查询结果缓存多久过期
2
+ READ_SIZE = 1024 * 1024 # 一次读多少
3
+ WBUFF_LIMIT = 100 * 1024 * 1024 # 写前上限,超过上限暂停读src/dst
4
+ RESUME_BELOW = WBUFF_LIMIT / 2 # 降到多少以下恢复读
5
+ SEND_HELLO_COUNT = 10 # hello最多发几次
6
+ EXPIRE_AFTER = 300 # 多久没有新流量,过期
7
+ CHECK_EXPIRE_INTERVAL = 30 # 检查过期间隔
8
+ CHECK_RESUME_INTERVAL = 1 # 检查恢复读间隔
9
+ RESOLV_CACHE_EXPIRE = 300 # dns查询结果缓存多久过期
16
10
  TUND_PORT = 1
17
11
  HEARTBEAT = 2
18
12
  A_NEW_SOURCE = 3
@@ -27,6 +21,11 @@ module Girl
27
21
  TUND_FIN = 12
28
22
  TUN_FIN = 13
29
23
  IP_CHANGED = 14
24
+ SINGLE_MISS = 15
25
+ RANGE_MISS = 16
26
+ CONTINUE = 17
27
+ IS_RESEND_READY = 18
28
+ RESEND_READY = 19
30
29
  HTTP_OK = "HTTP/1.1 200 OK\r\n\r\n"
31
30
  # https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
32
31
  RESERVED_ROUTE = <<EOF
@@ -38,4 +37,14 @@ module Girl
38
37
  192.168.0.0/16
39
38
  255.255.255.255/32
40
39
  EOF
40
+ CONSTS = %w[
41
+ READ_SIZE
42
+ WBUFF_LIMIT
43
+ RESUME_BELOW
44
+ SEND_HELLO_COUNT
45
+ EXPIRE_AFTER
46
+ CHECK_EXPIRE_INTERVAL
47
+ CHECK_RESUME_INTERVAL
48
+ RESOLV_CACHE_EXPIRE
49
+ ]
41
50
  end
@@ -7,66 +7,45 @@ require 'ipaddr'
7
7
  require 'json'
8
8
  require 'socket'
9
9
 
10
- ##
10
+ =begin
11
11
  # Girl::Proxy - 代理服务,近端。
12
- #
13
- # 包结构
14
- # ======
15
- #
16
- # tun-proxyd:
17
- #
18
- # hello
19
- #
20
- # proxyd-tun:
21
- #
22
- # Q>: 0 ctlmsg -> C: 1 tund port -> n: tund port
23
- #
24
- # tun-tund:
25
- #
26
- # Q>: 0 ctlmsg -> C: 2 heartbeat -> C: random char
27
- # 3 a new source -> Q>: src id -> encoded destination address
28
- # 4 paired -> Q>: src id -> n: dst port
29
- # 5 dest status -> n: dst port -> Q>: biggest relayed dst pack id -> Q>: continue src pack id
30
- # 6 source status -> Q>: src id -> Q>: biggest relayed src pack id -> Q>: continue dst pack id
31
- # 7 miss -> Q>/n: src id / dst port -> Q>: pack id begin -> Q>: pack id end
32
- # 8 fin1 -> Q>/n: src id / dst port -> Q>: biggest src pack id / biggest dst pack id -> Q>: continue dst pack id / continue src pack id
33
- # 9 not use
34
- # 10 fin2 -> Q>/n: src id / dst port
35
- # 11 not use
36
- # 12 tund fin
37
- # 13 tun fin
38
- # 14 tun ip changed
39
- #
40
- # Q>: 1+ pack_id -> Q>/n: src id / dst port -> traffic
41
- #
42
- # close logic
43
- # ===========
44
- #
45
- # 1-1. after close src -> dst closed ? no -> send fin1
46
- # 1-2. tun recv fin2 -> del src ext
47
- #
48
- # 2-1. tun recv fin1 -> all traffic received ? -> close src after write
49
- # 2-2. tun recv traffic -> dst closed and all traffic received ? -> close src after write
50
- # 2-3. after close src -> dst closed ? yes -> del src ext -> send fin2
51
- #
52
- # 3-1. after close dst -> src closed ? no -> send fin1
53
- # 3-2. tund recv fin2 -> del dst ext
54
- #
55
- # 4-1. tund recv fin1 -> all traffic received ? -> close dst after write
56
- # 4-2. tund recv traffic -> src closed and all traffic received ? -> close dst after write
57
- # 4-3. after close dst -> src closed ? yes -> del dst ext -> send fin2
58
- #
12
+
13
+ ## 包结构
14
+
15
+ tun-proxyd:
16
+
17
+ hello
18
+
19
+ proxyd-tun:
20
+
21
+ Q>: 0 ctlmsg -> C: 1 tund port -> n: tund port -> n: tcpd port
22
+
23
+ tun-tund:
24
+
25
+ Q>: 0 ctlmsg -> C: 2 heartbeat [not use]
26
+ 3 a new source -> Q>: src id -> encoded destination address
27
+ 4 paired -> Q>: src id -> n: dst id
28
+ 5 dest status [not use]
29
+ 6 source status [not use]
30
+ 7 miss [not use]
31
+ 8 fin1 [not use]
32
+ 9 confirm fin1 [not use]
33
+ 10 fin2 [not use]
34
+ 11 confirm fin2 [not use]
35
+ 12 tund fin
36
+ 13 tun fin
37
+ 14 tun ip changed
38
+ =end
39
+
59
40
  module Girl
60
41
  class Proxy
61
42
 
62
43
  def initialize( config_path = nil )
63
- unless config_path
44
+ unless config_path then
64
45
  config_path = File.expand_path( '../girl.conf.json', __FILE__ )
65
46
  end
66
47
 
67
- unless File.exist?( config_path )
68
- raise "missing config file #{ config_path }"
69
- end
48
+ raise "missing config file #{ config_path }" unless File.exist?( config_path )
70
49
 
71
50
  # {
72
51
  # "proxy_port": 6666, // 代理服务,近端(本地)端口
@@ -74,8 +53,6 @@ module Girl
74
53
  # "proxyd_port": 6060, // 代理服务,远端端口
75
54
  # "direct_path": "girl.direct.txt", // 直连ip段
76
55
  # "remote_path": "girl.remote.txt", // 交给远端解析的域名列表
77
- # "proxy_tmp_dir": "/tmp/girl.proxy", // 近端缓存根路径
78
- # "proxyd_tmp_dir": "/tmp/girl.proxyd", // 远端缓存根路径
79
56
  # "im": "girl", // 标识,用来识别近端
80
57
  # "worker_count": 4 // 子进程数,默认取cpu个数
81
58
  # }
@@ -85,75 +62,40 @@ module Girl
85
62
  proxyd_port = conf[ :proxyd_port ]
86
63
  direct_path = conf[ :direct_path ]
87
64
  remote_path = conf[ :remote_path ]
88
- proxy_tmp_dir = conf[ :proxy_tmp_dir ]
89
65
  im = conf[ :im ]
90
66
  worker_count = conf[ :worker_count ]
91
67
 
92
- unless proxy_port
68
+ unless proxy_port then
93
69
  proxy_port = 6666
94
70
  end
95
71
 
96
- unless proxyd_host
97
- raise "missing proxyd host"
98
- end
72
+ raise "missing proxyd host" unless proxyd_host
99
73
 
100
- unless proxyd_port
74
+ unless proxyd_port then
101
75
  proxyd_port = 6060
102
76
  end
103
77
 
104
78
  directs = []
105
79
 
106
- if direct_path
107
- unless File.exist?( direct_path )
108
- raise "not found direct file #{ direct_path }"
109
- end
110
-
111
- directs = ( RESERVED_ROUTE.split( "\n" ) + IO.binread( direct_path ).split( "\n" ) ).map { | line | IPAddr.new( line.strip ) }
80
+ if direct_path then
81
+ raise "not found direct file #{ direct_path }" unless File.exist?( direct_path )
82
+ directs = ( RESERVED_ROUTE.split( "\n" ) + IO.binread( direct_path ).split( "\n" ) ).map{ | line | IPAddr.new( line.strip ) }
112
83
  end
113
84
 
114
85
  remotes = []
115
86
 
116
- if remote_path
117
- unless File.exist?( remote_path )
118
- raise "not found remote file #{ remote_path }"
119
- end
120
-
121
- remotes = IO.binread( remote_path ).split( "\n" ).map { | line | line.strip }
87
+ if remote_path then
88
+ raise "not found remote file #{ remote_path }" unless File.exist?( remote_path )
89
+ remotes = IO.binread( remote_path ).split( "\n" ).map{ | line | line.strip }
122
90
  end
123
91
 
124
- unless proxy_tmp_dir
125
- proxy_tmp_dir = '/tmp/girl.proxy'
126
- end
127
-
128
- unless File.exist?( proxy_tmp_dir )
129
- Dir.mkdir( proxy_tmp_dir )
130
- end
131
-
132
- src_chunk_dir = File.join( proxy_tmp_dir, 'src.chunk' )
133
-
134
- unless Dir.exist?( src_chunk_dir )
135
- Dir.mkdir( src_chunk_dir )
136
- end
137
-
138
- dst_chunk_dir = File.join( proxy_tmp_dir, 'dst.chunk' )
139
-
140
- unless Dir.exist?( dst_chunk_dir )
141
- Dir.mkdir( dst_chunk_dir )
142
- end
143
-
144
- tun_chunk_dir = File.join( proxy_tmp_dir, 'tun.chunk' )
145
-
146
- unless Dir.exist?( tun_chunk_dir )
147
- Dir.mkdir( tun_chunk_dir )
148
- end
149
-
150
- unless im
92
+ unless im then
151
93
  im = 'girl'
152
94
  end
153
95
 
154
96
  nprocessors = Etc.nprocessors
155
97
 
156
- if worker_count.nil? || worker_count <= 0 || worker_count > nprocessors
98
+ if worker_count.nil? || worker_count <= 0 || worker_count > nprocessors then
157
99
  worker_count = nprocessors
158
100
  end
159
101
 
@@ -164,43 +106,23 @@ module Girl
164
106
  puts "proxyd port #{ proxyd_port }"
165
107
  puts "#{ direct_path } #{ directs.size } directs"
166
108
  puts "#{ remote_path } #{ remotes.size } remotes"
167
- puts "src chunk dir #{ src_chunk_dir }"
168
- puts "dst chunk dir #{ dst_chunk_dir }"
169
- puts "tun chunk dir #{ tun_chunk_dir }"
170
109
  puts "im #{ im }"
171
110
  puts "worker count #{ worker_count }"
172
111
 
173
- names = %w[
174
- PACK_SIZE
175
- CHUNK_SIZE
176
- WBUFFS_LIMIT
177
- WMEMS_LIMIT
178
- RESUME_BELOW
179
- EXPIRE_NEW
180
- EXPIRE_AFTER
181
- CHECK_EXPIRE_INTERVAL
182
- CHECK_STATUS_INTERVAL
183
- SEND_STATUS_UNTIL
184
- MISS_RANGE_LIMIT
185
- RESENDING_LIMIT
186
- CONFUSE_UNTIL
187
- RESOLV_CACHE_EXPIRE
188
- ]
189
-
190
- len = names.map{ | name | name.size }.max
191
-
192
- names.each do | name |
112
+ len = CONSTS.map{ | name | name.size }.max
113
+
114
+ CONSTS.each do | name |
193
115
  puts "#{ name.gsub( '_', ' ' ).ljust( len ) } #{ Girl.const_get( name ) }"
194
116
  end
195
117
 
196
- if RUBY_PLATFORM.include?( 'linux' )
118
+ if RUBY_PLATFORM.include?( 'linux' ) then
197
119
  $0 = title
198
120
  workers = []
199
121
 
200
122
  worker_count.times do | i |
201
123
  workers << fork do
202
124
  $0 = 'girl proxy worker'
203
- worker = Girl::ProxyWorker.new( proxy_port, proxyd_host, proxyd_port, directs, remotes, src_chunk_dir, dst_chunk_dir, tun_chunk_dir, im )
125
+ worker = Girl::ProxyWorker.new( proxy_port, proxyd_host, proxyd_port, directs, remotes, im )
204
126
 
205
127
  Signal.trap( :TERM ) do
206
128
  puts "w#{ i } exit"
@@ -224,7 +146,7 @@ module Girl
224
146
 
225
147
  Process.waitall
226
148
  else
227
- Girl::ProxyWorker.new( proxy_port, proxyd_host, proxyd_port, directs, remotes, src_chunk_dir, dst_chunk_dir, tun_chunk_dir, im ).looping
149
+ Girl::ProxyWorker.new( proxy_port, proxyd_host, proxyd_port, directs, remotes, im ).looping
228
150
  end
229
151
  end
230
152
 
@@ -4,21 +4,19 @@ module Girl
4
4
  ##
5
5
  # initialize
6
6
  #
7
- def initialize( proxy_port, proxyd_host, proxyd_port, directs, remotes, src_chunk_dir, dst_chunk_dir, tun_chunk_dir, im )
7
+ def initialize( proxy_port, proxyd_host, proxyd_port, directs, remotes, im )
8
8
  @proxyd_host = proxyd_host
9
9
  @proxyd_addr = Socket.sockaddr_in( proxyd_port, proxyd_host )
10
10
  @directs = directs
11
11
  @remotes = remotes
12
- @src_chunk_dir = src_chunk_dir
13
- @dst_chunk_dir = dst_chunk_dir
14
- @tun_chunk_dir = tun_chunk_dir
15
12
  @custom = Girl::ProxyCustom.new( im )
16
13
  @mutex = Mutex.new
17
14
  @reads = []
18
15
  @writes = []
19
- @roles = {} # sock => :dotr / :proxy / :src / :dst / :tun
16
+ @roles = {} # sock => :dotr / :proxy / :src / :dst / :tun / :stream
20
17
  @src_infos = {} # src => {}
21
18
  @dst_infos = {} # dst => {}
19
+ @stream_infos = {} # stream => {}
22
20
  @resolv_caches = {} # domain => [ ip, created_at ]
23
21
 
24
22
  dotr, dotw = IO.pipe
@@ -33,36 +31,40 @@ module Girl
33
31
  def looping
34
32
  puts "p#{ Process.pid } #{ Time.new } looping"
35
33
  loop_check_expire
36
- loop_check_status
34
+ loop_check_resume
37
35
 
38
36
  loop do
39
37
  rs, ws = IO.select( @reads, @writes )
40
38
 
41
39
  @mutex.synchronize do
42
- # 先写,再读
43
- ws.each do | sock |
44
- case @roles[ sock ]
45
- when :src
46
- write_src( sock )
47
- when :dst
48
- write_dst( sock )
49
- when :tun
50
- write_tun( sock )
51
- end
52
- end
53
-
40
+ # 先读,再写,避免打上关闭标记后读到
54
41
  rs.each do | sock |
55
42
  case @roles[ sock ]
56
- when :dotr
43
+ when :dotr then
57
44
  read_dotr( sock )
58
- when :proxy
45
+ when :proxy then
59
46
  read_proxy( sock )
60
- when :src
47
+ when :tun then
48
+ read_tun( sock )
49
+ when :src then
61
50
  read_src( sock )
62
- when :dst
51
+ when :dst then
63
52
  read_dst( sock )
64
- when :tun
65
- read_tun( sock )
53
+ when :stream then
54
+ read_stream( sock )
55
+ end
56
+ end
57
+
58
+ ws.each do | sock |
59
+ case @roles[ sock ]
60
+ when :tun then
61
+ write_tun( sock )
62
+ when :src then
63
+ write_src( sock )
64
+ when :dst then
65
+ write_dst( sock )
66
+ when :stream then
67
+ write_stream( sock )
66
68
  end
67
69
  end
68
70
  end
@@ -76,7 +78,7 @@ module Girl
76
78
  # quit!
77
79
  #
78
80
  def quit!
79
- if @tun && !@tun.closed? && @tun_info[ :tund_addr ]
81
+ if @tun && !@tun.closed? && @tun_info[ :tund_addr ] then
80
82
  # puts "debug1 send tun fin"
81
83
  data = [ 0, TUN_FIN ].pack( 'Q>C' )
82
84
  @tun.sendmsg( data, 0, @tun_info[ :tund_addr ] )
@@ -89,175 +91,223 @@ module Girl
89
91
  private
90
92
 
91
93
  ##
92
- # loop check expire
94
+ # add ctlmsg
93
95
  #
94
- def loop_check_expire
95
- Thread.new do
96
- loop do
97
- sleep CHECK_EXPIRE_INTERVAL
98
-
99
- @mutex.synchronize do
100
- need_trigger = false
101
- now = Time.new
102
-
103
- if @tun && !@tun.closed?
104
- is_expired = @tun_info[ :last_recv_at ] ? ( now - @tun_info[ :last_recv_at ] > EXPIRE_AFTER ) : ( now - @tun_info[ :created_at ] > EXPIRE_NEW )
105
-
106
- if is_expired
107
- puts "p#{ Process.pid } #{ Time.new } expire tun"
108
- set_is_closing( @tun )
109
- else
110
- data = [ 0, HEARTBEAT, rand( 128 ) ].pack( 'Q>CC' )
111
- # puts "debug1 #{ Time.new } heartbeat"
112
- add_tun_ctlmsg( data )
113
-
114
- @tun_info[ :src_exts ].each do | src_id, src_ext |
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_ext[ :destination_domain ] }"
117
- del_src_ext( src_id )
118
- end
119
- end
120
- end
121
-
122
- need_trigger = true
123
- end
96
+ def add_ctlmsg( data, to_addr = nil )
97
+ unless to_addr then
98
+ to_addr = @tun_info[ :tund_addr ]
99
+ end
124
100
 
125
- @src_infos.each do | src, src_info |
126
- if now - src_info[ :last_continue_at ] > EXPIRE_AFTER
127
- puts "p#{ Process.pid } #{ Time.new } expire src #{ src_info[ :destination_domain ] }"
128
- set_is_closing( src )
129
- need_trigger = true
130
- end
131
- end
101
+ if to_addr then
102
+ @tun_info[ :ctlmsgs ] << [ data, to_addr ]
103
+ add_write( @tun )
104
+ end
105
+ end
132
106
 
133
- @dst_infos.each do | dst, dst_info |
134
- if now - dst_info[ :last_continue_at ] > EXPIRE_AFTER
135
- puts "p#{ Process.pid } #{ Time.new } expire dst #{ dst_info[ :domain ] }"
136
- set_is_closing( dst )
137
- need_trigger = true
138
- end
139
- end
107
+ ##
108
+ # add read
109
+ #
110
+ def add_read( sock, role = nil )
111
+ unless @reads.include?( sock ) then
112
+ @reads << sock
140
113
 
141
- if need_trigger
142
- next_tick
143
- end
144
- end
114
+ if role then
115
+ @roles[ sock ] = role
145
116
  end
146
117
  end
147
118
  end
148
119
 
149
120
  ##
150
- # loop check status
121
+ # add src wbuff
151
122
  #
152
- def loop_check_status
153
- Thread.new do
154
- loop do
155
- sleep CHECK_STATUS_INTERVAL
156
-
157
- @mutex.synchronize do
158
- if @tun && !@tun.closed? && @tun_info[ :tund_addr ]
159
- need_trigger = false
160
-
161
- if @tun_info[ :src_exts ].any?
162
- now = Time.new
163
-
164
- @tun_info[ :src_exts ].each do | src_id, src_ext |
165
- if src_ext[ :dst_port ] && ( now - src_ext[ :last_continue_at ] < SEND_STATUS_UNTIL )
166
- data = [ 0, SOURCE_STATUS, src_id, src_ext[ :relay_pack_id ], src_ext[ :continue_dst_pack_id ] ].pack( 'Q>CQ>Q>Q>' )
167
- add_tun_ctlmsg( data )
168
- need_trigger = true
169
- end
170
- end
171
- end
123
+ def add_src_wbuff( src, data )
124
+ return if src.closed?
125
+ src_info = @src_infos[ src ]
126
+ src_info[ :wbuff ] << data
127
+ add_write( src )
128
+ src_info[ :last_recv_at ] = Time.new
129
+ end
172
130
 
173
- if @tun_info[ :paused ] && ( @tun_info[ :src_exts ].map{ | _, src_ext | src_ext[ :wmems ].size }.sum < RESUME_BELOW )
174
- puts "p#{ Process.pid } #{ Time.new } resume tun"
175
- @tun_info[ :paused ] = false
176
- add_write( @tun )
177
- need_trigger = true
178
- end
131
+ ##
132
+ # add src wbuff socks5 conn reply
133
+ #
134
+ def add_src_wbuff_socks5_conn_reply( src )
135
+ # +----+-----+-------+------+----------+----------+
136
+ # |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
137
+ # +----+-----+-------+------+----------+----------+
138
+ # | 1 | 1 | X'00' | 1 | Variable | 2 |
139
+ # +----+-----+-------+------+----------+----------+
140
+ proxy_ip, proxy_port = @proxy_local_address.ip_unpack
141
+ data = [ [ 5, 0, 0, 1 ].pack( 'C4' ), IPAddr.new( proxy_ip ).hton, [ proxy_port ].pack( 'n' ) ].join
142
+ # puts "debug1 add src wbuff socks5 conn reply #{ data.inspect }"
143
+ add_src_wbuff( src, data )
144
+ end
179
145
 
180
- if need_trigger
181
- next_tick
182
- end
183
- end
184
- end
185
- end
146
+ ##
147
+ # add write
148
+ #
149
+ def add_write( sock )
150
+ unless @writes.include?( sock ) then
151
+ @writes << sock
186
152
  end
187
153
  end
188
154
 
189
155
  ##
190
- # loop send a new source
156
+ # close sock
191
157
  #
192
- def loop_send_a_new_source( src_ext, data )
193
- Thread.new do
194
- EXPIRE_NEW.times do
195
- if src_ext[ :src ].closed? || src_ext[ :dst_port ]
196
- # puts "debug1 break loop send a new source #{ src_ext[ :dst_port ] }"
197
- break
198
- end
158
+ def close_sock( sock )
159
+ sock.close
160
+ @reads.delete( sock )
161
+ @writes.delete( sock )
162
+ @roles.delete( sock )
163
+ end
199
164
 
200
- @mutex.synchronize do
201
- # puts "debug1 send a new source #{ data.inspect }"
202
- add_tun_ctlmsg( data )
203
- next_tick
204
- end
165
+ ##
166
+ # close dst
167
+ #
168
+ def close_dst( dst )
169
+ # puts "debug1 close dst"
170
+ close_sock( dst )
171
+ @dst_infos.delete( dst )
172
+ end
205
173
 
206
- sleep 1
207
- end
174
+ ##
175
+ # close read src
176
+ #
177
+ def close_read_src( src )
178
+ return if src.closed?
179
+ # puts "debug1 close read src"
180
+ src.close_read
181
+ @reads.delete( src )
182
+
183
+ if src.closed? then
184
+ # puts "debug1 delete src info"
185
+ @roles.delete( src )
186
+ src_info = del_src_info( src )
187
+ else
188
+ src_info = @src_infos[ src ]
208
189
  end
190
+
191
+ src_info[ :paused ] = false
192
+ src_info
209
193
  end
210
194
 
211
195
  ##
212
- # resolve domain
196
+ # close read dst
213
197
  #
214
- def resolve_domain( src, domain )
215
- if @remotes.any? { | remote | ( domain.size >= remote.size ) && ( domain[ ( remote.size * -1 )..-1 ] == remote ) }
216
- # puts "debug1 #{ domain } hit remotes"
217
- new_a_src_ext( src )
218
- return
198
+ def close_read_dst( dst )
199
+ return if dst.closed?
200
+ # puts "debug1 close read dst"
201
+ dst.close_read
202
+ @reads.delete( dst )
203
+
204
+ if dst.closed? then
205
+ # puts "debug1 delete dst info"
206
+ @roles.delete( dst )
207
+ dst_info = @dst_infos.delete( dst )
208
+ else
209
+ dst_info = @dst_infos[ dst ]
219
210
  end
220
211
 
221
- resolv_cache = @resolv_caches[ domain ]
212
+ dst_info
213
+ end
222
214
 
223
- if resolv_cache
224
- ip_info, created_at = resolv_cache
215
+ ##
216
+ # close read stream
217
+ #
218
+ def close_read_stream( stream )
219
+ return if stream.closed?
220
+ # puts "debug1 close read stream"
221
+ stream.close_read
222
+ @reads.delete( stream )
223
+
224
+ if stream.closed? then
225
+ # puts "debug1 delete stream info"
226
+ @roles.delete( stream )
227
+ stream_info = @stream_infos.delete( stream )
228
+ else
229
+ stream_info = @stream_infos[ stream ]
230
+ end
225
231
 
226
- if Time.new - created_at < RESOLV_CACHE_EXPIRE
227
- # puts "debug1 #{ domain } hit resolv cache #{ ip_info.inspect }"
228
- deal_with_destination_ip( src, ip_info )
229
- return
230
- end
232
+ stream_info
233
+ end
231
234
 
232
- # puts "debug1 expire #{ domain } resolv cache"
233
- @resolv_caches.delete( domain )
234
- end
235
+ ##
236
+ # close src
237
+ #
238
+ def close_src( src )
239
+ # puts "debug1 close src"
240
+ close_sock( src )
241
+ del_src_info( src )
242
+ end
235
243
 
236
- src_info = @src_infos[ src ]
237
- src_info[ :proxy_type ] = :checking
244
+ ##
245
+ # close tun
246
+ #
247
+ def close_tun( tun )
248
+ # puts "debug1 close tun"
249
+ close_sock( tun )
250
+ @tun_info[ :srcs ].each{ | _, src | set_src_closing( src ) }
251
+ end
238
252
 
239
- Thread.new do
240
- begin
241
- ip_info = Addrinfo.ip( domain )
242
- rescue Exception => e
243
- puts "p#{ Process.pid } #{ Time.new } resolv #{ domain } #{ e.class }"
244
- end
253
+ ##
254
+ # close write src
255
+ #
256
+ def close_write_src( src )
257
+ return if src.closed?
258
+ # puts "debug1 close write src"
259
+ src.close_write
260
+ @writes.delete( src )
261
+
262
+ if src.closed? then
263
+ # puts "debug1 delete src info"
264
+ @roles.delete( src )
265
+ src_info = del_src_info( src )
266
+ else
267
+ src_info = @src_infos[ src ]
268
+ end
245
269
 
246
- @mutex.synchronize do
247
- if ip_info
248
- @resolv_caches[ domain ] = [ ip_info, Time.new ]
270
+ src_info
271
+ end
249
272
 
250
- unless src.closed?
251
- puts "p#{ Process.pid } #{ Time.new } resolved #{ domain } #{ ip_info.ip_address }"
252
- deal_with_destination_ip( src, ip_info )
253
- end
254
- else
255
- set_is_closing( src )
256
- end
273
+ ##
274
+ # close write dst
275
+ #
276
+ def close_write_dst( dst )
277
+ return if dst.closed?
278
+ # puts "debug1 close write dst"
279
+ dst.close_write
280
+ @writes.delete( dst )
281
+
282
+ if dst.closed? then
283
+ # puts "debug1 delete dst info"
284
+ @roles.delete( dst )
285
+ dst_info = @dst_infos.delete( dst )
286
+ else
287
+ dst_info = @dst_infos[ dst ]
288
+ end
257
289
 
258
- next_tick
259
- end
290
+ dst_info
291
+ end
292
+
293
+ ##
294
+ # close write stream
295
+ #
296
+ def close_write_stream( stream )
297
+ return if stream.closed?
298
+ # puts "debug1 close write stream"
299
+ stream.close_write
300
+ @writes.delete( stream )
301
+
302
+ if stream.closed? then
303
+ # puts "debug1 delete stream info"
304
+ @roles.delete( stream )
305
+ stream_info = @stream_infos.delete( stream )
306
+ else
307
+ stream_info = @stream_infos[ stream ]
260
308
  end
309
+
310
+ stream_info
261
311
  end
262
312
 
263
313
  ##
@@ -266,468 +316,313 @@ module Girl
266
316
  def deal_with_destination_ip( src, ip_info )
267
317
  src_info = @src_infos[ src ]
268
318
 
269
- if ( @directs.any? { | direct | direct.include?( ip_info.ip_address ) } ) || ( ( src_info[ :destination_domain ] == @proxyd_host ) && ![ 80, 443 ].include?( src_info[ :destination_port ] ) )
319
+ if ( @directs.any? { | direct | direct.include?( ip_info.ip_address ) } ) || ( ( src_info[ :destination_domain ] == @proxyd_host ) && ![ 80, 443 ].include?( src_info[ :destination_port ] ) ) then
270
320
  # ip命中直连列表,或者访问远端非80/443端口,直连
271
321
  # puts "debug1 #{ ip_info.inspect } hit directs"
272
322
  new_a_dst( src, ip_info )
273
323
  else
274
324
  # 走远端
275
- new_a_src_ext( src )
325
+ # puts "debug1 #{ ip_info.inspect } go tunnel"
326
+ set_src_proxy_type_tunnel( src )
276
327
  end
277
328
  end
278
329
 
279
330
  ##
280
- # new a proxy
331
+ # del src info
281
332
  #
282
- def new_a_proxy( proxy_port )
283
- proxy = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
284
- proxy.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
333
+ def del_src_info( src )
334
+ src_info = @src_infos.delete( src )
285
335
 
286
- if RUBY_PLATFORM.include?( 'linux' )
287
- proxy.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
288
- proxy.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
336
+ if src_info[ :stream ] && @tun && !@tun.closed? then
337
+ @tun_info[ :srcs ].delete( src_info[ :id ] )
289
338
  end
290
339
 
291
- proxy.bind( Socket.sockaddr_in( proxy_port, '0.0.0.0' ) )
292
- proxy.listen( 511 )
293
- puts "p#{ Process.pid } #{ Time.new } proxy listen on #{ proxy_port }"
294
- add_read( proxy, :proxy )
295
- @proxy_local_address = proxy.local_address
296
- end
297
-
298
- ##
299
- # new a tun
300
- #
301
- def new_a_tun
302
- tun = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
303
- tun.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
304
- port = tun.local_address.ip_port
305
- tun_info = {
306
- port: port, # 端口
307
- ctlmsg_rbuffs: [], # 还没配上tund,暂存的ctlmsg
308
- ctlmsgs: [], # [ to_addr, data ]
309
- wbuffs: [], # 写前缓存 [ src_id, pack_id, data ]
310
- caches: [], # 块读出缓存 [ src_id, pack_id, data ]
311
- chunks: [], # 块队列 filename
312
- spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
313
- tund_addr: nil, # tund地址
314
- src_exts: {}, # src额外信息 src_id => {}
315
- src_ids: {}, # dst_port => src_id
316
- paused: false, # 是否暂停写
317
- resendings: [], # 重传队列 [ src_id, pack_id ]
318
- created_at: Time.new, # 创建时间
319
- last_recv_at: nil, # 上一次收到流量的时间,过期关闭
320
- is_closing: false # 是否准备关闭
321
- }
322
-
323
- @tun = tun
324
- @tun_info = tun_info
325
-
326
- add_read( tun, :tun )
327
- data = @custom.hello
328
- puts "p#{ Process.pid } #{ Time.new } hello i'm tun"
329
- # puts "debug1 #{ data.inspect }"
330
- add_tun_ctlmsg( data, @proxyd_addr )
340
+ src_info
331
341
  end
332
342
 
333
343
  ##
334
- # new a dst
344
+ # loop check expire
335
345
  #
336
- def new_a_dst( src, ip_info )
337
- src_info = @src_infos[ src ]
338
- domain = src_info[ :destination_domain ]
339
- destination_addr = Socket.sockaddr_in( src_info[ :destination_port ], ip_info.ip_address )
340
- dst = Socket.new( ip_info.ipv4? ? Socket::AF_INET : Socket::AF_INET6, Socket::SOCK_STREAM, 0 )
346
+ def loop_check_expire
347
+ Thread.new do
348
+ loop do
349
+ sleep CHECK_EXPIRE_INTERVAL
341
350
 
342
- if RUBY_PLATFORM.include?( 'linux' )
343
- dst.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
344
- end
351
+ @mutex.synchronize do
352
+ trigger = false
353
+ now = Time.new
345
354
 
346
- begin
347
- dst.connect_nonblock( destination_addr )
348
- rescue IO::WaitWritable
349
- # connect nonblock 必抛 wait writable,这里仅仅接一下,逻辑写外面,整齐
350
- rescue Exception => e
351
- puts "p#{ Process.pid } #{ Time.new } connect destination #{ e.class }, close src"
352
- set_is_closing( src )
353
- return
354
- end
355
+ if @tun && !@tun.closed? then
356
+ last_recv_at = @tun_info[ :last_recv_at ] || @tun_info[ :created_at ]
357
+ last_sent_at = @tun_info[ :last_sent_at ] || @tun_info[ :created_at ]
355
358
 
356
- # puts "debug1 a new dst #{ dst.local_address.inspect }"
357
- local_port = dst.local_address.ip_port
358
- @dst_infos[ dst ] = {
359
- local_port: local_port, # 本地端口
360
- src: src, # 对应src
361
- domain: domain, # 域名
362
- wbuff: '', # 写前
363
- cache: '', # 块读出缓存
364
- chunks: [], # 块队列,写前达到块大小时结一个块 filename
365
- spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
366
- last_continue_at: Time.new, # 上一次发生流量的时间
367
- is_closing: false # 是否准备关闭
368
- }
359
+ if @tun_info[ :srcs ].empty? && ( now - last_recv_at >= EXPIRE_AFTER ) && ( now - last_sent_at >= EXPIRE_AFTER ) then
360
+ puts "p#{ Process.pid } #{ Time.new } expire tun"
361
+ set_tun_closing
362
+ trigger = true
363
+ end
364
+ end
369
365
 
370
- add_read( dst, :dst )
371
- src_info[ :proxy_type ] = :direct
372
- src_info[ :dst ] = dst
366
+ @src_infos.each do | src, src_info |
367
+ last_recv_at = src_info[ :last_recv_at ] || src_info[ :created_at ]
368
+ last_sent_at = src_info[ :last_sent_at ] || src_info[ :created_at ]
373
369
 
374
- if src_info[ :proxy_proto ] == :http
375
- if src_info[ :is_connect ]
376
- # puts "debug1 add src wbuff http ok"
377
- add_src_wbuff( src, HTTP_OK )
378
- else
379
- # puts "debug1 add src rbuffs to dst wbuff"
370
+ if ( now - last_recv_at >= EXPIRE_AFTER ) && ( now - last_sent_at >= EXPIRE_AFTER ) then
371
+ puts "p#{ Process.pid } #{ Time.new } expire src #{ src_info[ :destination_domain ] }"
372
+ set_src_closing( src )
373
+ trigger = true
374
+ end
375
+ end
380
376
 
381
- src_info[ :rbuffs ].each do | _, data |
382
- add_dst_wbuff( dst, data )
377
+ next_tick if trigger
383
378
  end
384
379
  end
385
- elsif src_info[ :proxy_proto ] == :socks5
386
- add_src_wbuff_socks5_conn_reply( src )
387
380
  end
388
381
  end
389
382
 
390
383
  ##
391
- # new a src ext
384
+ # loop check resume
392
385
  #
393
- def new_a_src_ext( src )
394
- if @tun.nil? || @tun.closed?
395
- new_a_tun
396
- end
386
+ def loop_check_resume
387
+ Thread.new do
388
+ loop do
389
+ sleep CHECK_RESUME_INTERVAL
397
390
 
398
- src_info = @src_infos[ src ]
399
- src_id = src_info[ :id ]
400
- destination_port = src_info[ :destination_port ]
401
- destination_domain = src_info[ :destination_domain ]
402
-
403
- src_ext = {
404
- src: src, # src
405
- dst_port: nil, # 远端dst端口
406
- destination_domain: destination_domain, # 目的地域名
407
- wmems: {}, # 写后 pack_id => data
408
- send_ats: {}, # 上一次发出时间 pack_id => send_at
409
- relay_pack_id: 0, # 转发到几
410
- continue_dst_pack_id: 0, # 收到几
411
- pieces: {}, # 跳号包 dst_pack_id => data
412
- is_dst_closed: false, # dst是否已关闭
413
- biggest_dst_pack_id: 0, # dst最大包号码
414
- completed_pack_id: 0, # 完成到几(对面收到几)
415
- last_continue_at: Time.new # 上一次发生流量的时间
416
- }
391
+ @mutex.synchronize do
392
+ trigger = false
417
393
 
418
- @tun_info[ :src_exts ][ src_id ] = src_ext
419
- src_info[ :proxy_type ] = :tunnel
420
-
421
- destination_domain_port = [ destination_domain, destination_port ].join( ':' )
422
- data = [ [ 0, A_NEW_SOURCE, src_id ].pack( 'Q>CQ>' ), @custom.encode( destination_domain_port ) ].join
423
- loop_send_a_new_source( src_ext, data )
424
- end
425
-
426
- ##
427
- # add src wbuff socks5 conn reply
428
- #
429
- def add_src_wbuff_socks5_conn_reply( src )
430
- # +----+-----+-------+------+----------+----------+
431
- # |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
432
- # +----+-----+-------+------+----------+----------+
433
- # | 1 | 1 | X'00' | 1 | Variable | 2 |
434
- # +----+-----+-------+------+----------+----------+
435
- proxy_ip, proxy_port = @proxy_local_address.ip_unpack
436
- data = [ [ 5, 0, 0, 1 ].pack( 'C4' ), IPAddr.new( proxy_ip ).hton, [ proxy_port ].pack( 'n' ) ].join
437
- # puts "debug1 add src wbuff socks5 conn reply #{ data.inspect }"
438
- add_src_wbuff( src, data )
439
- end
394
+ @src_infos.select{ | _, src_info | src_info[ :paused ] }.each do | src, src_info |
395
+ dst = src_info[ :dst ]
440
396
 
441
- ##
442
- # sub http request
443
- #
444
- def sub_http_request( data )
445
- lines = data.split( "\r\n" )
397
+ if dst then
398
+ dst_info = @dst_infos[ dst ]
446
399
 
447
- if lines.empty?
448
- return [ data, nil ]
449
- end
400
+ if dst_info[ :wbuff ].size < RESUME_BELOW then
401
+ puts "p#{ Process.pid } #{ Time.new } dst.wbuff below #{ RESUME_BELOW }, resume src #{ src_info[ :destination_domain ] }"
402
+ resume_src( src )
403
+ trigger = true
404
+ end
405
+ else
406
+ stream = src_info[ :stream ]
407
+ stream_info = @stream_infos[ stream ]
450
408
 
451
- method, url, proto = lines.first.split( ' ' )
409
+ if stream_info[ :wbuff ].size < RESUME_BELOW then
410
+ puts "p#{ Process.pid } #{ Time.new } stream.wbuff below #{ RESUME_BELOW }, resume src #{ src_info[ :destination_domain ] }"
411
+ resume_src( src )
412
+ trigger = true
413
+ end
414
+ end
415
+ end
452
416
 
453
- if proto && url && proto[ 0, 4 ] == 'HTTP' && url[ 0, 7 ] == 'http://'
454
- domain_and_port = url.split( '/' )[ 2 ]
455
- data = data.sub( "http://#{ domain_and_port }", '' )
456
- # puts "debug1 subed #{ data.inspect } #{ domain_and_port }"
417
+ next_tick if trigger
418
+ end
419
+ end
457
420
  end
458
-
459
- [ data, domain_and_port ]
460
421
  end
461
422
 
462
423
  ##
463
- # add tun ctlmsg
424
+ # loop send a new source
464
425
  #
465
- def add_tun_ctlmsg( data, to_addr = nil )
466
- unless to_addr
467
- to_addr = @tun_info[ :tund_addr ]
468
- end
426
+ def loop_send_a_new_source( src )
427
+ src_info = @src_infos[ src ]
469
428
 
470
- if to_addr
471
- @tun_info[ :ctlmsgs ] << [ to_addr, data ]
472
- add_write( @tun )
473
- else
474
- @tun_info[ :ctlmsg_rbuffs ] << data
475
- end
476
- end
429
+ if src_info then
430
+ destination_domain = src_info[ :destination_domain ]
431
+ destination_port = src_info[ :destination_port ]
432
+ domain_port = [ destination_domain, destination_port ].join( ':' )
433
+ data = [ [ 0, A_NEW_SOURCE, src_info[ :id ] ].pack( 'Q>CQ>' ), @custom.encode( domain_port ) ].join
434
+
435
+ Thread.new do
436
+ SEND_HELLO_COUNT.times do | i |
437
+ if @tun.nil? || @tun.closed? || src.closed? || src_info[ :stream ] then
438
+ # puts "debug1 break loop send a new source #{ src_info[ :dst_port ] }"
439
+ break
440
+ end
477
441
 
478
- ##
479
- # add tun wbuff
480
- #
481
- def add_tun_wbuff( src_id, pack_id, data )
482
- @tun_info[ :wbuffs ] << [ src_id, pack_id, data ]
442
+ @mutex.synchronize do
443
+ if i >= 1 then
444
+ puts "p#{ Process.pid } #{ Time.new } resend a new source #{ domain_port } #{ i }"
445
+ end
483
446
 
484
- if @tun_info[ :wbuffs ].size >= WBUFFS_LIMIT
485
- spring = @tun_info[ :chunks ].size > 0 ? ( @tun_info[ :spring ] + 1 ) : 0
486
- filename = "#{ Process.pid }-#{ @tun_info[ :port ] }.#{ spring }"
487
- chunk_path = File.join( @tun_chunk_dir, filename )
488
- datas = @tun_info[ :wbuffs ].map{ | _src_id, _pack_id, _data | [ [ _src_id, _pack_id, _data.bytesize ].pack( 'Q>Q>n' ), _data ].join }
447
+ add_ctlmsg( data )
448
+ next_tick
449
+ end
489
450
 
490
- begin
491
- IO.binwrite( chunk_path, datas.join )
492
- rescue Errno::ENOSPC => e
493
- puts "p#{ Process.pid } #{ Time.new } #{ e.class }, close tun"
494
- set_is_closing( @tun )
495
- return
451
+ sleep 1
452
+ end
496
453
  end
497
-
498
- @tun_info[ :chunks ] << filename
499
- @tun_info[ :spring ] = spring
500
- @tun_info[ :wbuffs ].clear
501
454
  end
502
-
503
- add_write( @tun )
504
455
  end
505
456
 
506
457
  ##
507
- # add src wbuff
458
+ # new a dst
508
459
  #
509
- def add_src_wbuff( src, data )
460
+ def new_a_dst( src, ip_info )
510
461
  src_info = @src_infos[ src ]
511
- src_info[ :wbuff ] << data
512
-
513
- if src_info[ :wbuff ].bytesize >= CHUNK_SIZE
514
- spring = src_info[ :chunks ].size > 0 ? ( src_info[ :spring ] + 1 ) : 0
515
- filename = "#{ Process.pid }-#{ src_info[ :id ] }.#{ spring }"
516
- chunk_path = File.join( @src_chunk_dir, filename )
517
-
518
- begin
519
- IO.binwrite( chunk_path, src_info[ :wbuff ] )
520
- rescue Errno::ENOSPC => e
521
- puts "p#{ Process.pid } #{ Time.new } #{ e.class }, close src"
522
- set_is_closing( src )
523
- return
524
- end
462
+ domain = src_info[ :destination_domain ]
463
+ destination_addr = Socket.sockaddr_in( src_info[ :destination_port ], ip_info.ip_address )
464
+ dst = Socket.new( ip_info.ipv4? ? Socket::AF_INET : Socket::AF_INET6, Socket::SOCK_STREAM, 0 )
465
+ dst.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 ) if RUBY_PLATFORM.include?( 'linux' )
525
466
 
526
- src_info[ :chunks ] << filename
527
- src_info[ :spring ] = spring
528
- src_info[ :wbuff ].clear
467
+ begin
468
+ dst.connect_nonblock( destination_addr )
469
+ rescue IO::WaitWritable
470
+ # connect nonblock 必抛 wait writable
471
+ rescue Exception => e
472
+ puts "p#{ Process.pid } #{ Time.new } dst connect destination #{ e.class }, close src"
473
+ set_src_closing( src )
474
+ return
529
475
  end
530
476
 
531
- add_write( src )
532
- end
533
-
534
- ##
535
- # add dst wbuff
536
- #
537
- def add_dst_wbuff( dst, data )
538
- dst_info = @dst_infos[ dst ]
539
- dst_info[ :wbuff ] << data
477
+ # puts "debug1 a new dst #{ dst.local_address.inspect }"
478
+ local_port = dst.local_address.ip_port
479
+ dst_info = {
480
+ local_port: local_port, # 本地端口
481
+ src: src, # 对应src
482
+ domain: domain, # 域名
483
+ wbuff: '', # 写前,从src读到的流量
484
+ closing: false, # 准备关闭
485
+ closing_read: false, # 准备关闭读
486
+ closing_write: false # 准备关闭写
487
+ }
540
488
 
541
- if dst_info[ :wbuff ].bytesize >= CHUNK_SIZE
542
- spring = dst_info[ :chunks ].size > 0 ? ( dst_info[ :spring ] + 1 ) : 0
543
- filename = "#{ Process.pid }-#{ dst_info[ :local_port ] }.#{ spring }"
544
- chunk_path = File.join( @dst_chunk_dir, filename )
489
+ @dst_infos[ dst ] = dst_info
490
+ add_read( dst, :dst )
491
+ src_info[ :proxy_type ] = :direct
492
+ src_info[ :dst ] = dst
545
493
 
546
- begin
547
- IO.binwrite( chunk_path, dst_info[ :wbuff ] )
548
- rescue Errno::ENOSPC => e
549
- puts "p#{ Process.pid } #{ Time.new } #{ e.class }, close dst"
550
- set_is_closing( dst )
551
- return
494
+ if src_info[ :proxy_proto ] == :http then
495
+ if src_info[ :is_connect ] then
496
+ # puts "debug1 add src.wbuff http ok"
497
+ add_src_wbuff( src, HTTP_OK )
498
+ elsif src_info[ :rbuff ] then
499
+ # puts "debug1 move src.rbuff to dst.wbuff"
500
+ dst_info[ :wbuff ] << src_info[ :rbuff ]
501
+ add_write( dst )
552
502
  end
553
-
554
- dst_info[ :chunks ] << filename
555
- dst_info[ :spring ] = spring
556
- dst_info[ :wbuff ].clear
503
+ elsif src_info[ :proxy_proto ] == :socks5 then
504
+ add_src_wbuff_socks5_conn_reply( src )
557
505
  end
558
-
559
- add_write( dst )
560
506
  end
561
507
 
562
508
  ##
563
- # add read
509
+ # new a stream
564
510
  #
565
- def add_read( sock, role )
566
- unless @reads.include?( sock )
567
- @reads << sock
568
- end
511
+ def new_a_stream( src_id, dst_id )
512
+ src = @tun_info[ :srcs ][ src_id ]
513
+ return if src.nil? || src.closed?
569
514
 
570
- @roles[ sock ] = role
571
- end
515
+ src_info = @src_infos[ src ]
516
+ return if src_info[ :dst_id ]
572
517
 
573
- ##
574
- # add write
575
- #
576
- def add_write( sock )
577
- if sock && !sock.closed? && !@writes.include?( sock )
578
- @writes << sock
518
+ if dst_id == 0 then
519
+ puts "p#{ Process.pid } #{ Time.new } remote dst already closed"
520
+ set_src_closing( src )
521
+ return
579
522
  end
580
- end
581
523
 
582
- ##
583
- # set is closing
584
- #
585
- def set_is_closing( sock )
586
- if sock && !sock.closed?
587
- role = @roles[ sock ]
588
- # puts "debug1 set #{ role.to_s } is closing"
589
-
590
- case role
591
- when :src
592
- src_info = @src_infos[ sock ]
593
- src_info[ :is_closing ] = true
594
- when :dst
595
- dst_info = @dst_infos[ sock ]
596
- dst_info[ :is_closing ] = true
597
- when :tun
598
- @tun_info[ :is_closing ] = true
599
- end
524
+ stream = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
525
+ stream.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 ) if RUBY_PLATFORM.include?( 'linux' )
600
526
 
601
- @reads.delete( sock )
602
- add_write( sock )
603
- end
604
- end
605
-
606
- ##
607
- # send data
608
- #
609
- def send_data( tun, data, to_addr )
610
527
  begin
611
- tun.sendmsg( data, 0, to_addr )
612
- rescue IO::WaitWritable, Errno::EINTR
613
- return false
614
- rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENETDOWN => e
615
- puts "#{ Time.new } #{ e.class }, close tun"
616
- close_tun( tun )
617
- return false
528
+ stream.connect_nonblock( @tun_info[ :tcpd_addr ] )
529
+ rescue IO::WaitWritable
530
+ rescue Exception => e
531
+ puts "p#{ Process.pid } #{ Time.new } connect tcpd #{ e.class }"
532
+ return
618
533
  end
619
534
 
620
- true
621
- end
622
-
623
- ##
624
- # close src
625
- #
626
- def close_src( src )
627
- # puts "debug1 close src"
628
- close_sock( src )
629
- src_info = @src_infos.delete( src )
535
+ # puts "debug1 set stream.wbuff #{ dst_id }"
536
+ data = [ dst_id ].pack( 'n' )
630
537
 
631
- src_info[ :chunks ].each do | filename |
632
- begin
633
- File.delete( File.join( @src_chunk_dir, filename ) )
634
- rescue Errno::ENOENT
635
- end
538
+ unless src_info[ :rbuff ].empty? then
539
+ # puts "debug1 encode and move src.rbuff to stream.wbuff"
540
+ data << @custom.encode( src_info[ :rbuff ] )
636
541
  end
637
542
 
638
- src_id = src_info[ :id ]
639
-
640
- if src_info[ :proxy_type ] == :tunnel
641
- return if @tun.closed?
543
+ @stream_infos[ stream ] = {
544
+ src: src, # 对应src
545
+ wbuff: data # 写前,写往远端streamd
546
+ }
642
547
 
643
- src_ext = @tun_info[ :src_exts ][ src_id ]
644
- return if src_ext.nil? || src_ext[ :dst_port ].nil?
548
+ src_info[ :dst_id ] = dst_id
549
+ src_info[ :stream ] = stream
550
+ add_read( stream, :stream )
551
+ add_write( stream )
645
552
 
646
- if src_ext[ :is_dst_closed ]
647
- # puts "debug1 2-3. after close src -> dst closed ? yes -> del src ext -> send fin2"
648
- del_src_ext( src_id )
649
- data = [ 0, FIN2, src_id ].pack( 'Q>CQ>' )
650
- add_tun_ctlmsg( data )
651
- else
652
- # puts "debug1 1-1. after close src -> dst closed ? no -> send fin1"
653
- data = [ 0, FIN1, src_id, src_info[ :biggest_pack_id ], src_ext[ :continue_dst_pack_id ] ].pack( 'Q>CQ>Q>Q>' )
654
- add_tun_ctlmsg( data )
553
+ if src_info[ :proxy_proto ] == :http then
554
+ if src_info[ :is_connect ] then
555
+ # puts "debug1 add src.wbuff http ok"
556
+ add_src_wbuff( src, HTTP_OK )
655
557
  end
656
- elsif src_info[ :proxy_type ] == :direct
657
- set_is_closing( src_info[ :dst ] )
558
+ elsif src_info[ :proxy_proto ] == :socks5 then
559
+ add_src_wbuff_socks5_conn_reply( src )
658
560
  end
659
561
  end
660
562
 
661
563
  ##
662
- # close dst
564
+ # new a proxy
663
565
  #
664
- def close_dst( dst )
665
- # puts "debug1 close dst"
666
- close_sock( dst )
667
- dst_info = @dst_infos.delete( dst )
566
+ def new_a_proxy( proxy_port )
567
+ proxy = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
568
+ proxy.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
668
569
 
669
- dst_info[ :chunks ].each do | filename |
670
- begin
671
- File.delete( File.join( @dst_chunk_dir, filename ) )
672
- rescue Errno::ENOENT
673
- end
570
+ if RUBY_PLATFORM.include?( 'linux' ) then
571
+ proxy.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
572
+ proxy.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
674
573
  end
675
574
 
676
- set_is_closing( dst_info[ :src ] )
575
+ proxy.bind( Socket.sockaddr_in( proxy_port, '0.0.0.0' ) )
576
+ proxy.listen( 127 )
577
+ puts "p#{ Process.pid } #{ Time.new } proxy listen on #{ proxy_port }"
578
+ add_read( proxy, :proxy )
579
+ @proxy_local_address = proxy.local_address
677
580
  end
678
581
 
679
582
  ##
680
- # close tun
583
+ # new a tun
681
584
  #
682
- def close_tun( tun )
683
- # puts "debug1 close tun"
684
- close_sock( tun )
585
+ def new_a_tun
586
+ tun = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
587
+ tun.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
588
+ port = tun.local_address.ip_port
589
+ tun_info = {
590
+ port: port, # 端口
591
+ pending_sources: [], # 还没配上tund,暂存的src
592
+ ctlmsgs: [], # [ ctlmsg, to_addr ]
593
+ tund_addr: nil, # tund地址
594
+ tcpd_addr: nil, # tcpd地址
595
+ srcs: {}, # src_id => src
596
+ created_at: Time.new, # 创建时间
597
+ last_recv_at: nil, # 上一次收到流量的时间
598
+ last_sent_at: nil, # 上一次发出流量的时间
599
+ closing: false # 是否准备关闭
600
+ }
685
601
 
686
- @tun_info[ :chunks ].each do | filename |
687
- begin
688
- File.delete( File.join( @tun_chunk_dir, filename ) )
689
- rescue Errno::ENOENT
690
- end
691
- end
602
+ @tun = tun
603
+ @tun_info = tun_info
692
604
 
693
- @tun_info[ :src_exts ].each{ | _, src_ext | set_is_closing( src_ext[ :src ] ) }
694
- end
605
+ add_read( tun, :tun )
606
+ data = @custom.hello
695
607
 
696
- ##
697
- # close sock
698
- #
699
- def close_sock( sock )
700
- sock.close
701
- @reads.delete( sock )
702
- @writes.delete( sock )
703
- @roles.delete( sock )
704
- end
608
+ Thread.new do
609
+ SEND_HELLO_COUNT.times do | i |
610
+ if @tun.nil? || @tun.closed? || @tun_info[ :tund_addr ] then
611
+ # puts "debug1 break loop send hello"
612
+ break
613
+ end
705
614
 
706
- ##
707
- # del src ext
708
- #
709
- def del_src_ext( src_id )
710
- src_ext = @tun_info[ :src_exts ].delete( src_id )
615
+ @mutex.synchronize do
616
+ msg = i >= 1 ? "resend hello #{ i }" : "hello i'm tun"
617
+ puts "p#{ Process.pid } #{ Time.new } #{ msg }"
618
+ # puts "debug1 #{ data.inspect }"
711
619
 
712
- if src_ext
713
- @tun_info[ :src_ids ].delete( src_ext[ :dst_port ] )
714
- end
715
- end
620
+ add_ctlmsg( data, @proxyd_addr )
621
+ next_tick
622
+ end
716
623
 
717
- ##
718
- # release wmems
719
- #
720
- def release_wmems( src_ext, completed_pack_id )
721
- if completed_pack_id > src_ext[ :completed_pack_id ]
722
- # puts "debug2 update completed pack #{ completed_pack_id }"
723
- pack_ids = src_ext[ :wmems ].keys.select { | pack_id | pack_id <= completed_pack_id }
724
-
725
- pack_ids.each do | pack_id |
726
- src_ext[ :wmems ].delete( pack_id )
727
- src_ext[ :send_ats ].delete( pack_id )
624
+ sleep 1
728
625
  end
729
-
730
- src_ext[ :completed_pack_id ] = completed_pack_id
731
626
  end
732
627
  end
733
628
 
@@ -739,213 +634,176 @@ module Girl
739
634
  end
740
635
 
741
636
  ##
742
- # write src
637
+ # resolve domain
743
638
  #
744
- def write_src( src )
745
- src_info = @src_infos[ src ]
746
- from, data = :cache, src_info[ :cache ]
747
-
748
- if data.empty?
749
- if src_info[ :chunks ].any?
750
- path = File.join( @src_chunk_dir, src_info[ :chunks ].shift )
751
-
752
- begin
753
- data = src_info[ :cache ] = IO.binread( path )
754
- File.delete( path )
755
- rescue Errno::ENOENT => e
756
- puts "p#{ Process.pid } #{ Time.new } read #{ path } #{ e.class }"
757
- close_src( src )
758
- return
759
- end
760
- else
761
- from, data = :wbuff, src_info[ :wbuff ]
762
- end
763
- end
764
-
765
- if data.empty?
766
- if src_info[ :is_closing ]
767
- close_src( src )
768
- else
769
- @writes.delete( src )
770
- end
771
-
772
- return
773
- end
774
-
775
- begin
776
- written = src.write_nonblock( data )
777
- rescue IO::WaitWritable, Errno::EINTR
778
- return
779
- rescue Exception => e
780
- close_src( src )
639
+ def resolve_domain( src, domain )
640
+ if @remotes.any? { | remote | ( domain.size >= remote.size ) && ( domain[ ( remote.size * -1 )..-1 ] == remote ) } then
641
+ # puts "debug1 #{ domain } hit remotes"
642
+ set_src_proxy_type_tunnel( src )
781
643
  return
782
644
  end
783
645
 
784
- # puts "debug2 write src #{ written }"
785
- data = data[ written..-1 ]
786
- src_info[ from ] = data
787
- src_info[ :last_continue_at ] = Time.new
788
- end
646
+ resolv_cache = @resolv_caches[ domain ]
789
647
 
790
- ##
791
- # write dst
792
- #
793
- def write_dst( dst )
794
- dst_info = @dst_infos[ dst ]
795
- from, data = :cache, dst_info[ :cache ]
796
-
797
- if data.empty?
798
- if dst_info[ :chunks ].any?
799
- path = File.join( @dst_chunk_dir, dst_info[ :chunks ].shift )
800
-
801
- begin
802
- data = dst_info[ :cache ] = IO.binread( path )
803
- File.delete( path )
804
- rescue Errno::ENOENT => e
805
- puts "p#{ Process.pid } #{ Time.new } read #{ path } #{ e.class }"
806
- close_dst( dst )
807
- return
808
- end
809
- else
810
- from, data = :wbuff, dst_info[ :wbuff ]
811
- end
812
- end
648
+ if resolv_cache then
649
+ ip_info, created_at = resolv_cache
813
650
 
814
- if data.empty?
815
- if dst_info[ :is_closing ]
816
- close_dst( dst )
817
- else
818
- @writes.delete( dst )
651
+ if Time.new - created_at < RESOLV_CACHE_EXPIRE then
652
+ # puts "debug1 #{ domain } hit resolv cache #{ ip_info.inspect }"
653
+ deal_with_destination_ip( src, ip_info )
654
+ return
819
655
  end
820
656
 
821
- return
822
- end
823
-
824
- begin
825
- written = dst.write_nonblock( data )
826
- rescue IO::WaitWritable, Errno::EINTR
827
- return
828
- rescue Exception => e
829
- # puts "debug1 write dst #{ e.class }"
830
- close_dst( dst )
831
- return
832
- end
833
-
834
- # puts "debug2 write dst #{ written }"
835
- data = data[ written..-1 ]
836
- dst_info[ from ] = data
837
- dst_info[ :last_continue_at ] = Time.new
838
- end
839
-
840
- ##
841
- # write tun
842
- #
843
- def write_tun( tun )
844
- if @tun_info[ :is_closing ]
845
- close_tun( tun )
846
- return
657
+ # puts "debug1 expire #{ domain } resolv cache"
658
+ @resolv_caches.delete( domain )
847
659
  end
848
660
 
849
- now = Time.new
850
-
851
- # 传ctlmsg
852
- while @tun_info[ :ctlmsgs ].any?
853
- to_addr, data = @tun_info[ :ctlmsgs ].first
661
+ src_info = @src_infos[ src ]
662
+ src_info[ :proxy_type ] = :checking
854
663
 
855
- unless send_data( tun, data, to_addr )
856
- return
664
+ Thread.new do
665
+ begin
666
+ ip_info = Addrinfo.ip( domain )
667
+ rescue Exception => e
668
+ puts "p#{ Process.pid } #{ Time.new } resolv #{ domain } #{ e.class }"
857
669
  end
858
670
 
859
- @tun_info[ :ctlmsgs ].shift
860
- end
861
-
862
- # 重传
863
- while @tun_info[ :resendings ].any?
864
- src_id, pack_id = @tun_info[ :resendings ].first
865
- src_ext = @tun_info[ :src_exts ][ src_id ]
866
-
867
- if src_ext
868
- data = src_ext[ :wmems ][ pack_id ]
671
+ @mutex.synchronize do
672
+ if ip_info then
673
+ @resolv_caches[ domain ] = [ ip_info, Time.new ]
869
674
 
870
- if data
871
- unless send_data( tun, data, @tun_info[ :tund_addr ] )
872
- return
675
+ unless src.closed? then
676
+ puts "p#{ Process.pid } #{ Time.new } resolved #{ domain } #{ ip_info.ip_address }"
677
+ deal_with_destination_ip( src, ip_info )
873
678
  end
874
-
875
- src_ext[ :last_continue_at ] = now
679
+ else
680
+ set_src_closing( src )
876
681
  end
877
- end
878
682
 
879
- @tun_info[ :resendings ].shift
683
+ next_tick
684
+ end
880
685
  end
686
+ end
881
687
 
882
- # 若写后达到上限,暂停取写前
883
- if @tun_info[ :src_exts ].map{ | _, src_ext | src_ext[ :wmems ].size }.sum >= WMEMS_LIMIT
884
- unless @tun_info[ :paused ]
885
- puts "p#{ Process.pid } #{ Time.new } pause tun"
886
- @tun_info[ :paused ] = true
887
- end
688
+ ##
689
+ # resume src
690
+ #
691
+ def resume_src( src )
692
+ src_info = @src_infos[ src ]
693
+ src_info[ :paused ] = false
694
+ add_read( src )
695
+ end
888
696
 
889
- @writes.delete( tun )
890
- return
891
- end
697
+ ##
698
+ # set dst closing
699
+ #
700
+ def set_dst_closing( dst )
701
+ return if dst.closed?
702
+ dst_info = @dst_infos[ dst ]
703
+ dst_info[ :closing ] = true
704
+ @reads.delete( dst )
705
+ add_write( dst )
706
+ end
707
+
708
+ ##
709
+ # set dst closing write
710
+ #
711
+ def set_dst_closing_write( dst )
712
+ return if dst.closed?
713
+ dst_info = @dst_infos[ dst ]
714
+ dst_info[ :closing_write ] = true
715
+ add_write( dst )
716
+ end
892
717
 
893
- # 取写前
894
- if @tun_info[ :caches ].any?
895
- datas = @tun_info[ :caches ]
896
- elsif @tun_info[ :chunks ].any?
897
- path = File.join( @tun_chunk_dir, @tun_info[ :chunks ].shift )
718
+ ##
719
+ # set src is closing
720
+ #
721
+ def set_src_closing( src )
722
+ return if src.closed?
723
+ @reads.delete( src )
724
+ src_info = @src_infos[ src ]
725
+ src_info[ :closing ] = true
726
+ add_write( src )
727
+ end
898
728
 
899
- begin
900
- data = IO.binread( path )
901
- File.delete( path )
902
- rescue Errno::ENOENT => e
903
- puts "p#{ Process.pid } #{ Time.new } read #{ path } #{ e.class }"
904
- close_tun( tun )
905
- return
906
- end
729
+ ##
730
+ # set src closing write
731
+ #
732
+ def set_src_closing_write( src )
733
+ return if src.closed?
734
+ src_info = @src_infos[ src ]
735
+ src_info[ :closing_write ] = true
736
+ add_write( src )
737
+ end
907
738
 
908
- caches = []
739
+ ##
740
+ # set src proxy type tunnel
741
+ #
742
+ def set_src_proxy_type_tunnel( src )
743
+ if @tun.nil? || @tun.closed? then
744
+ new_a_tun
745
+ end
909
746
 
910
- until data.empty?
911
- _src_id, _pack_id, pack_size = data[ 0, 18 ].unpack( 'Q>Q>n' )
912
- caches << [ _src_id, _pack_id, data[ 18, pack_size ] ]
913
- data = data[ ( 18 + pack_size )..-1 ]
914
- end
747
+ src_info = @src_infos[ src ]
748
+ src_info[ :proxy_type ] = :tunnel
749
+ src_id = src_info[ :id ]
750
+ @tun_info[ :srcs ][ src_id ] = src
915
751
 
916
- datas = @tun_info[ :caches ] = caches
917
- elsif @tun_info[ :wbuffs ].any?
918
- datas = @tun_info[ :wbuffs ]
752
+ if @tun_info[ :tund_addr ] then
753
+ loop_send_a_new_source( src )
919
754
  else
920
- @writes.delete( tun )
921
- return
755
+ @tun_info[ :pending_sources ] << src
922
756
  end
757
+ end
923
758
 
924
- while datas.any?
925
- src_id, pack_id, data = datas.first
926
- src_ext = @tun_info[ :src_exts ][ src_id ]
759
+ ##
760
+ # set stream closing
761
+ #
762
+ def set_stream_closing( stream )
763
+ return if stream.closed?
764
+ stream_info = @stream_infos[ stream ]
765
+ stream_info[ :closing ] = true
766
+ @reads.delete( stream )
767
+ add_write( stream )
768
+ end
927
769
 
928
- if src_ext
929
- if pack_id <= CONFUSE_UNTIL
930
- data = @custom.encode( data )
931
- # puts "debug1 encoded pack #{ pack_id }"
932
- end
770
+ ##
771
+ # set stream closing write
772
+ #
773
+ def set_stream_closing_write( stream )
774
+ return if stream.closed?
775
+ stream_info = @stream_infos[ stream ]
776
+ stream_info[ :closing_write ] = true
777
+ add_write( stream )
778
+ end
933
779
 
934
- data = [ [ pack_id, src_id ].pack( 'Q>Q>' ), data ].join
780
+ ##
781
+ # set tun is closing
782
+ #
783
+ def set_tun_closing
784
+ return if @tun.closed?
785
+ @tun_info[ :closing ] = true
786
+ @reads.delete( @tun )
787
+ add_write( @tun )
788
+ end
935
789
 
936
- unless send_data( tun, data, @tun_info[ :tund_addr ] )
937
- return
938
- end
790
+ ##
791
+ # sub http request
792
+ #
793
+ def sub_http_request( data )
794
+ lines = data.split( "\r\n" )
939
795
 
940
- # puts "debug2 written pack #{ pack_id }"
941
- src_ext[ :relay_pack_id ] = pack_id
942
- src_ext[ :wmems ][ pack_id ] = data
943
- src_ext[ :send_ats ][ pack_id ] = now
944
- src_ext[ :last_continue_at ] = now
945
- end
796
+ return [ data, nil ] if lines.empty?
797
+
798
+ method, url, proto = lines.first.split( ' ' )
946
799
 
947
- datas.shift
800
+ if proto && url && proto[ 0, 4 ] == 'HTTP' && url[ 0, 7 ] == 'http://' then
801
+ domain_port = url.split( '/' )[ 2 ]
802
+ data = data.sub( "http://#{ domain_port }", '' )
803
+ # puts "debug1 subed #{ data.inspect } #{ domain_port }"
948
804
  end
805
+
806
+ [ data, domain_port ]
949
807
  end
950
808
 
951
809
  ##
@@ -962,64 +820,133 @@ module Girl
962
820
  begin
963
821
  src, addrinfo = proxy.accept_nonblock
964
822
  rescue IO::WaitReadable, Errno::EINTR
823
+ print 'r'
965
824
  return
966
825
  end
967
826
 
968
- id = rand( ( 2 ** 64 ) - 2 ) + 1
969
- # puts "debug1 accept a src #{ addrinfo.inspect } #{ id }"
827
+ src_id = rand( ( 2 ** 64 ) - 2 ) + 1
828
+ # puts "debug1 accept a src #{ addrinfo.inspect } #{ src_id }"
970
829
 
971
830
  @src_infos[ src ] = {
972
- id: id, # id
973
- proxy_proto: :uncheck, # :uncheck / :http / :socks5
974
- proxy_type: :uncheck, # :uncheck / :checking / :direct / :tunnel / :negotiation
975
- dst: nil, # :direct的场合,对应的dst
976
- destination_domain: nil, # 目的地域名
977
- destination_port: nil, # 目的地端口
978
- biggest_pack_id: 0, # 最大包号码
979
- is_connect: true, # 代理协议是http的场合,是否是CONNECT
980
- rbuffs: [], # 非CONNECT,dst或者远端dst未准备好,暂存流量 [ pack_id, data ]
981
- wbuff: '', # 写前
982
- cache: '', # 块读出缓存
983
- chunks: [], # 块队列,写前达到块大小时结一个块 filename
984
- spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
985
- last_continue_at: Time.new, # 上一次发生流量的时间
986
- is_closing: false # 是否准备关闭
831
+ id: src_id, # id
832
+ proxy_proto: :uncheck, # :uncheck / :http / :socks5
833
+ proxy_type: :uncheck, # :uncheck / :checking / :direct / :tunnel / :negotiation
834
+ destination_domain: nil, # 目的地域名
835
+ destination_port: nil, # 目的地端口
836
+ is_connect: true, # 代理协议是http的场合,是否是CONNECT
837
+ rbuff: '', # 读到的流量
838
+ dst: nil, # :direct的场合,对应的dst
839
+ stream: nil, # :tunnel的场合,对应的stream
840
+ dst_id: nil, # 远端dst id
841
+ wbuff: '', # 从dst/stream读到的流量
842
+ created_at: Time.new, # 创建时间
843
+ last_recv_at: nil, # 上一次收到新流量(由dst收到,或者由stream收到)的时间
844
+ last_sent_at: nil, # 上一次发出流量(由dst发出,或者由stream发出)的时间
845
+ paused: false, # 是否已暂停
846
+ closing: false, # 准备关闭
847
+ closing_read: false, # 准备关闭读
848
+ closing_write: false # 准备关闭写
987
849
  }
988
850
 
989
851
  add_read( src, :src )
990
852
  end
991
853
 
854
+ ##
855
+ # read tun
856
+ #
857
+ def read_tun( tun )
858
+ begin
859
+ data, addrinfo, rflags, *controls = tun.recvmsg_nonblock
860
+ rescue IO::WaitReadable, Errno::EINTR
861
+ print 'r'
862
+ return
863
+ end
864
+
865
+ from_addr = addrinfo.to_sockaddr
866
+ @tun_info[ :last_recv_at ] = Time.new
867
+ pack_id = data[ 0, 8 ].unpack( 'Q>' ).first
868
+ return if pack_id != 0
869
+
870
+ ctl_num = data[ 8 ].unpack( 'C' ).first
871
+
872
+ case ctl_num
873
+ when TUND_PORT then
874
+ return if ( from_addr != @proxyd_addr ) || @tun_info[ :tund_addr ]
875
+
876
+ tund_port, tcpd_port = data[ 9, 4 ].unpack( 'nn' )
877
+
878
+ puts "p#{ Process.pid } #{ Time.new } got tund port #{ tund_port }, #{ tcpd_port }"
879
+ @tun_info[ :tund_addr ] = Socket.sockaddr_in( tund_port, @proxyd_host )
880
+ @tun_info[ :tcpd_addr ] = Socket.sockaddr_in( tcpd_port, @proxyd_host )
881
+
882
+ if @tun_info[ :pending_sources ].any? then
883
+ puts "p#{ Process.pid } #{ Time.new } send pending sources"
884
+
885
+ @tun_info[ :pending_sources ].each do | src |
886
+ loop_send_a_new_source( src )
887
+ end
888
+
889
+ @tun_info[ :pending_sources ].clear
890
+ end
891
+ when PAIRED then
892
+ return if from_addr != @tun_info[ :tund_addr ]
893
+
894
+ src_id, dst_id = data[ 9, 10 ].unpack( 'Q>n' )
895
+
896
+ # puts "debug1 got paired #{ src_id } #{ dst_id }"
897
+ new_a_stream( src_id, dst_id )
898
+ when TUND_FIN then
899
+ return if from_addr != @tun_info[ :tund_addr ]
900
+
901
+ puts "p#{ Process.pid } #{ Time.new } recv tund fin"
902
+ set_tun_closing
903
+ when IP_CHANGED then
904
+ return if from_addr != @tun_info[ :tund_addr ]
905
+
906
+ puts "p#{ Process.pid } #{ Time.new } recv ip changed"
907
+ set_tun_closing
908
+ end
909
+ end
910
+
992
911
  ##
993
912
  # read src
994
913
  #
995
914
  def read_src( src )
996
915
  begin
997
- data = src.read_nonblock( PACK_SIZE )
916
+ data = src.read_nonblock( READ_SIZE )
998
917
  rescue IO::WaitReadable, Errno::EINTR
918
+ print 'r'
999
919
  return
1000
920
  rescue Exception => e
1001
921
  # puts "debug1 read src #{ e.class }"
1002
- set_is_closing( src )
922
+ src_info = close_read_src( src )
923
+ dst = src_info[ :dst ]
924
+
925
+ if dst then
926
+ set_dst_closing_write( dst )
927
+ else
928
+ stream = src_info[ :stream ]
929
+ set_stream_closing_write( stream ) if stream
930
+ end
931
+
1003
932
  return
1004
933
  end
1005
934
 
1006
- # puts "debug2 read src #{ data.inspect }"
1007
935
  src_info = @src_infos[ src ]
1008
- src_info[ :last_continue_at ] = Time.new
1009
936
  proxy_type = src_info[ :proxy_type ]
1010
937
 
1011
938
  case proxy_type
1012
- when :uncheck
1013
- if data[ 0, 7 ] == 'CONNECT'
939
+ when :uncheck then
940
+ if data[ 0, 7 ] == 'CONNECT' then
1014
941
  # puts "debug1 CONNECT"
1015
- domain_and_port = data.split( "\r\n" )[ 0 ].split( ' ' )[ 1 ]
942
+ domain_port = data.split( "\r\n" )[ 0 ].split( ' ' )[ 1 ]
1016
943
 
1017
- unless domain_and_port
944
+ unless domain_port then
1018
945
  puts "p#{ Process.pid } #{ Time.new } CONNECT miss domain"
1019
- set_is_closing( src )
946
+ set_src_closing( src )
1020
947
  return
1021
948
  end
1022
- elsif data[ 0 ].unpack( 'C' ).first == 5
949
+ elsif data[ 0 ].unpack( 'C' ).first == 5 then
1023
950
  # puts "debug1 socks5 #{ data.inspect }"
1024
951
 
1025
952
  # https://tools.ietf.org/html/rfc1928
@@ -1032,9 +959,9 @@ module Girl
1032
959
  nmethods = data[ 1 ].unpack( 'C' ).first
1033
960
  methods = data[ 2, nmethods ].unpack( 'C*' )
1034
961
 
1035
- unless methods.include?( 0 )
962
+ unless methods.include?( 0 ) then
1036
963
  puts "p#{ Process.pid } #{ Time.new } miss method 00"
1037
- set_is_closing( src )
964
+ set_src_closing( src )
1038
965
  return
1039
966
  end
1040
967
 
@@ -1045,41 +972,37 @@ module Girl
1045
972
  # +----+--------+
1046
973
  data2 = [ 5, 0 ].pack( 'CC' )
1047
974
  add_src_wbuff( src, data2 )
1048
-
1049
975
  src_info[ :proxy_proto ] = :socks5
1050
976
  src_info[ :proxy_type ] = :negotiation
1051
-
1052
977
  return
1053
978
  else
1054
979
  # puts "debug1 not CONNECT #{ data.inspect }"
1055
980
  host_line = data.split( "\r\n" ).find { | _line | _line[ 0, 6 ] == 'Host: ' }
1056
981
 
1057
- unless host_line
982
+ unless host_line then
1058
983
  # puts "debug1 not found host line"
1059
- set_is_closing( src )
984
+ set_src_closing( src )
1060
985
  return
1061
986
  end
1062
987
 
1063
- data, domain_and_port = sub_http_request( data )
988
+ data, domain_port = sub_http_request( data )
1064
989
 
1065
- unless domain_and_port
990
+ unless domain_port then
1066
991
  # puts "debug1 not HTTP"
1067
- domain_and_port = host_line.split( ' ' )[ 1 ]
992
+ domain_port = host_line.split( ' ' )[ 1 ]
1068
993
 
1069
- unless domain_and_port
994
+ unless domain_port then
1070
995
  puts "p#{ Process.pid } #{ Time.new } Host line miss domain"
1071
- set_is_closing( src )
996
+ set_src_closing( src )
1072
997
  return
1073
998
  end
1074
999
  end
1075
1000
 
1076
1001
  src_info[ :is_connect ] = false
1077
- pack_id = src_info[ :biggest_pack_id ] + 1
1078
- src_info[ :biggest_pack_id ] = pack_id
1079
- src_info[ :rbuffs ] << [ pack_id, data ]
1002
+ src_info[ :rbuff ] << data
1080
1003
  end
1081
1004
 
1082
- domain, port = domain_and_port.split( ':' )
1005
+ domain, port = domain_port.split( ':' )
1083
1006
  port = port ? port.to_i : 80
1084
1007
 
1085
1008
  src_info[ :proxy_proto ] = :http
@@ -1087,12 +1010,10 @@ module Girl
1087
1010
  src_info[ :destination_port ] = port
1088
1011
 
1089
1012
  resolve_domain( src, domain )
1090
- when :checking
1091
- # puts "debug1 add src rbuff while checking #{ data.inspect }"
1092
- pack_id = src_info[ :biggest_pack_id ] + 1
1093
- src_info[ :biggest_pack_id ] = pack_id
1094
- src_info[ :rbuffs ] << [ pack_id, data ]
1095
- when :negotiation
1013
+ when :checking then
1014
+ # puts "debug1 add src rbuff before resolved #{ data.inspect }"
1015
+ src_info[ :rbuff ] << data
1016
+ when :negotiation then
1096
1017
  # +----+-----+-------+------+----------+----------+
1097
1018
  # |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
1098
1019
  # +----+-----+-------+------+----------+----------+
@@ -1101,10 +1022,10 @@ module Girl
1101
1022
  # puts "debug1 negotiation #{ data.inspect }"
1102
1023
  ver, cmd, rsv, atyp = data[ 0, 4 ].unpack( 'C4' )
1103
1024
 
1104
- if cmd == 1
1025
+ if cmd == 1 then
1105
1026
  # puts "debug1 socks5 CONNECT"
1106
1027
 
1107
- if atyp == 1
1028
+ if atyp == 1 then
1108
1029
  destination_host, destination_port = data[ 4, 6 ].unpack( 'Nn' )
1109
1030
  destination_addr = Socket.sockaddr_in( destination_port, destination_host )
1110
1031
  destination_addrinfo = Addrinfo.new( destination_addr )
@@ -1113,10 +1034,10 @@ module Girl
1113
1034
  src_info[ :destination_port ] = destination_port
1114
1035
  # puts "debug1 IP V4 address #{ destination_addrinfo.inspect }"
1115
1036
  deal_with_destination_ip( src, destination_addrinfo )
1116
- elsif atyp == 3
1037
+ elsif atyp == 3 then
1117
1038
  domain_len = data[ 4 ].unpack( 'C' ).first
1118
1039
 
1119
- if ( domain_len + 7 ) == data.bytesize
1040
+ if ( domain_len + 7 ) == data.bytesize then
1120
1041
  domain = data[ 5, domain_len ]
1121
1042
  port = data[ ( 5 + domain_len ), 2 ].unpack( 'n' ).first
1122
1043
  src_info[ :destination_domain ] = domain
@@ -1128,53 +1049,64 @@ module Girl
1128
1049
  else
1129
1050
  puts "p#{ Process.pid } #{ Time.new } socks5 cmd #{ cmd } not implement"
1130
1051
  end
1131
- when :tunnel
1132
- src_id = src_info[ :id ]
1133
- src_ext = @tun_info[ :src_exts ][ src_id ]
1134
-
1135
- unless src_ext
1136
- # puts "debug1 not found src ext"
1137
- set_is_closing( src )
1138
- return
1139
- end
1052
+ when :tunnel then
1053
+ stream = src_info[ :stream ]
1140
1054
 
1141
- pack_id = src_info[ :biggest_pack_id ] + 1
1142
- src_info[ :biggest_pack_id ] = pack_id
1055
+ if stream then
1056
+ unless stream.closed? then
1057
+ unless src_info[ :is_connect ] then
1058
+ data, _ = sub_http_request( data )
1059
+ end
1143
1060
 
1144
- if src_ext[ :dst_port ]
1145
- if @tun.closed?
1146
- # puts "debug1 tun closed, close src"
1147
- set_is_closing( src )
1148
- return
1061
+ stream_info = @stream_infos[ stream ]
1062
+ data = @custom.encode( data )
1063
+ # puts "debug2 add stream.wbuff encoded #{ data.bytesize }"
1064
+ stream_info[ :wbuff ] << data
1065
+ add_write( stream )
1066
+
1067
+ if stream_info[ :wbuff ].bytesize >= WBUFF_LIMIT then
1068
+ puts "p#{ Process.pid } #{ Time.new } pause tunnel src #{ src_info[ :id ] } #{ src_info[ :destination_domain ] }"
1069
+ src_info[ :paused ] = true
1070
+ @reads.delete( src )
1071
+ end
1149
1072
  end
1073
+ else
1074
+ # puts "debug1 stream not ready, save data to src.rbuff"
1075
+ src_info[ :rbuff ] << data
1150
1076
 
1151
- unless src_info[ :is_connect ]
1152
- data, _ = sub_http_request( data )
1077
+ if src_info[ :rbuff ].bytesize >= WBUFF_LIMIT then
1078
+ # puts "debug1 tunnel src.rbuff full"
1079
+ set_src_closing( src )
1153
1080
  end
1154
-
1155
- add_tun_wbuff( src_id, pack_id, data )
1156
- else
1157
- # puts "debug1 remote dst not ready, save data to src rbuff"
1158
- src_info[ :rbuffs ] << [ pack_id, data ]
1159
1081
  end
1160
- when :direct
1082
+ when :direct then
1161
1083
  dst = src_info[ :dst ]
1162
1084
 
1163
- if dst
1164
- if dst.closed?
1165
- # puts "debug1 dst closed, close src"
1166
- set_is_closing( src )
1167
- return
1168
- end
1085
+ if dst then
1086
+ unless dst.closed? then
1087
+ unless src_info[ :is_connect ] then
1088
+ data, _ = sub_http_request( data )
1089
+ end
1169
1090
 
1170
- unless src_info[ :is_connect ]
1171
- data, _ = sub_http_request( data )
1172
- end
1091
+ dst_info = @dst_infos[ dst ]
1092
+ # puts "debug2 add dst.wbuff #{ data.bytesize }"
1093
+ dst_info[ :wbuff ] << data
1094
+ add_write( dst )
1173
1095
 
1174
- add_dst_wbuff( dst, data )
1096
+ if dst_info[ :wbuff ].bytesize >= WBUFF_LIMIT then
1097
+ puts "p#{ Process.pid } #{ Time.new } pause direct src #{ src_info[ :id ] } #{ src_info[ :destination_domain ] }"
1098
+ src_info[ :paused ] = true
1099
+ @reads.delete( src )
1100
+ end
1101
+ end
1175
1102
  else
1176
- # puts "debug1 dst not ready, save data to src rbuff"
1177
- src_info[ :rbuffs ] << [ nil, data ]
1103
+ # puts "debug1 dst not ready, save data to src.rbuff"
1104
+ src_info[ :rbuff ] << data
1105
+
1106
+ if src_info[ :rbuff ].bytesize >= WBUFF_LIMIT then
1107
+ # puts "debug1 direct src.rbuff full"
1108
+ set_src_closing( src )
1109
+ end
1178
1110
  end
1179
1111
  end
1180
1112
  end
@@ -1184,243 +1116,252 @@ module Girl
1184
1116
  #
1185
1117
  def read_dst( dst )
1186
1118
  begin
1187
- data = dst.read_nonblock( PACK_SIZE )
1119
+ data = dst.read_nonblock( READ_SIZE )
1188
1120
  rescue IO::WaitReadable, Errno::EINTR
1121
+ print 'r'
1189
1122
  return
1190
1123
  rescue Exception => e
1191
1124
  # puts "debug1 read dst #{ e.class }"
1192
- set_is_closing( dst )
1125
+ dst_info = close_read_dst( dst )
1126
+ src = dst_info[ :src ]
1127
+ set_src_closing_write( src )
1193
1128
  return
1194
1129
  end
1195
1130
 
1196
- # puts "debug2 read dst #{ data.inspect }"
1197
1131
  dst_info = @dst_infos[ dst ]
1198
- dst_info[ :last_continue_at ] = Time.new
1199
1132
  src = dst_info[ :src ]
1133
+ add_src_wbuff( src, data )
1134
+ end
1200
1135
 
1201
- if src.closed?
1202
- puts "p#{ Process.pid } #{ Time.new } src closed, close dst"
1203
- set_is_closing( dst )
1136
+ ##
1137
+ # read stream
1138
+ #
1139
+ def read_stream( stream )
1140
+ begin
1141
+ data = stream.read_nonblock( READ_SIZE )
1142
+ rescue IO::WaitReadable, Errno::EINTR
1143
+ print 'r'
1144
+ return
1145
+ rescue Exception => e
1146
+ # puts "debug1 read stream #{ e.class }"
1147
+ stream_info = close_read_stream( stream )
1148
+ src = stream_info[ :src ]
1149
+ set_src_closing_write( src )
1204
1150
  return
1205
1151
  end
1206
1152
 
1153
+ stream_info = @stream_infos[ stream ]
1154
+ src = stream_info[ :src ]
1155
+ data = @custom.decode( data )
1156
+ # puts "debug2 add src.wbuff decoded #{ data.bytesize }"
1207
1157
  add_src_wbuff( src, data )
1208
1158
  end
1209
1159
 
1210
1160
  ##
1211
- # read tun
1161
+ # write tun
1212
1162
  #
1213
- def read_tun( tun )
1214
- data, addrinfo, rflags, *controls = tun.recvmsg
1215
- from_addr = addrinfo.to_sockaddr
1216
- now = Time.new
1217
- @tun_info[ :last_recv_at ] = now
1218
- pack_id = data[ 0, 8 ].unpack( 'Q>' ).first
1219
-
1220
- if pack_id == 0
1221
- ctl_num = data[ 8 ].unpack( 'C' ).first
1222
-
1223
- case ctl_num
1224
- when TUND_PORT
1225
- return if ( from_addr != @proxyd_addr ) || @tun_info[ :tund_addr ]
1226
-
1227
- tund_port = data[ 9, 2 ].unpack( 'n' ).first
1228
-
1229
- puts "p#{ Process.pid } #{ Time.new } got tund port #{ tund_port }"
1230
- tund_addr = Socket.sockaddr_in( tund_port, @proxyd_host )
1231
- @tun_info[ :tund_addr ] = tund_addr
1232
-
1233
- if @tun_info[ :ctlmsg_rbuffs ].any?
1234
- # puts "debug1 move #{ @tun_info[ :ctlmsg_rbuffs ].size } ctlmsg rbuffs to ctlmsgs"
1235
- @tun_info[ :ctlmsgs ] += @tun_info[ :ctlmsg_rbuffs ].map{ | _data | [ tund_addr, _data ] }
1236
- @tun_info[ :ctlmsg_rbuffs ].clear
1237
- add_write( tun )
1238
- end
1239
- when PAIRED
1240
- return if from_addr != @tun_info[ :tund_addr ]
1163
+ def write_tun( tun )
1164
+ # 处理关闭
1165
+ if @tun_info[ :closing ] then
1166
+ close_tun( tun )
1167
+ return
1168
+ end
1241
1169
 
1242
- src_id, dst_port = data[ 9, 10 ].unpack( 'Q>n' )
1170
+ now = Time.new
1243
1171
 
1244
- src_ext = @tun_info[ :src_exts ][ src_id ]
1245
- return if src_ext.nil? || src_ext[ :dst_port ]
1172
+ # 发ctlmsg
1173
+ while @tun_info[ :ctlmsgs ].any? do
1174
+ data, to_addr = @tun_info[ :ctlmsgs ].first
1246
1175
 
1247
- src = src_ext[ :src ]
1248
- return if src.closed?
1176
+ begin
1177
+ @tun.sendmsg_nonblock( data, 0, to_addr )
1178
+ rescue IO::WaitWritable, Errno::EINTR
1179
+ puts "p#{ Process.pid } #{ Time.new } wait send ctlmsg, left #{ @tun_info[ :ctlmsgs ].size }"
1180
+ @tun_info[ :last_sent_at ] = now
1181
+ return
1182
+ rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENETDOWN => e
1183
+ puts "p#{ Process.pid } #{ Time.new } sendmsg #{ e.class }, close tun"
1184
+ close_tun( tun )
1185
+ return
1186
+ end
1249
1187
 
1250
- # puts "debug1 got paired #{ src_id } #{ dst_port }"
1188
+ @tun_info[ :ctlmsgs ].shift
1189
+ end
1251
1190
 
1252
- if dst_port == 0
1253
- set_is_closing( src )
1254
- return
1255
- end
1191
+ @tun_info[ :last_sent_at ] = now
1192
+ @writes.delete( tun )
1193
+ end
1256
1194
 
1257
- src_ext[ :dst_port ] = dst_port
1258
- @tun_info[ :src_ids ][ dst_port ] = src_id
1195
+ ##
1196
+ # write src
1197
+ #
1198
+ def write_src( src )
1199
+ return if src.closed?
1200
+ src_info = @src_infos[ src ]
1201
+ dst = src_info[ :dst ]
1259
1202
 
1260
- src_info = @src_infos[ src ]
1203
+ # 处理关闭
1204
+ if src_info[ :closing ] then
1205
+ close_src( src )
1261
1206
 
1262
- if src_info[ :proxy_proto ] == :http
1263
- if src_info[ :is_connect ]
1264
- # puts "debug1 add src wbuff http ok"
1265
- add_src_wbuff( src, HTTP_OK )
1266
- else
1267
- # puts "debug1 add src rbuffs to tun wbuffs"
1207
+ if dst then
1208
+ close_read_dst( dst )
1209
+ set_dst_closing_write( dst )
1210
+ else
1211
+ stream = src_info[ :stream ]
1268
1212
 
1269
- src_info[ :rbuffs ].each do | pack_id, _data |
1270
- add_tun_wbuff( src_id, pack_id, _data )
1271
- end
1272
- end
1273
- elsif src_info[ :proxy_proto ] == :socks5
1274
- add_src_wbuff_socks5_conn_reply( src_ext[ :src ] )
1213
+ if stream then
1214
+ close_read_stream( stream )
1215
+ set_stream_closing_write( stream )
1275
1216
  end
1276
- when DEST_STATUS
1277
- return if from_addr != @tun_info[ :tund_addr ]
1278
-
1279
- dst_port, relay_dst_pack_id, continue_src_pack_id = data[ 9, 18 ].unpack( 'nQ>Q>' )
1280
-
1281
- src_id = @tun_info[ :src_ids ][ dst_port ]
1282
- return unless src_id
1283
-
1284
- src_ext = @tun_info[ :src_exts ][ src_id ]
1285
- return unless src_ext
1286
-
1287
- # puts "debug2 got dest status"
1288
-
1289
- release_wmems( src_ext, continue_src_pack_id )
1290
-
1291
- # 发miss
1292
- if !src_ext[ :src ].closed? && ( src_ext[ :continue_dst_pack_id ] < relay_dst_pack_id )
1293
- ranges = []
1294
- ignored = false
1295
- curr_pack_id = src_ext[ :continue_dst_pack_id ] + 1
1217
+ end
1296
1218
 
1297
- src_ext[ :pieces ].keys.sort.each do | pack_id |
1298
- if pack_id > curr_pack_id
1299
- ranges << [ curr_pack_id, pack_id - 1 ]
1219
+ return
1220
+ end
1300
1221
 
1301
- if ranges.size >= MISS_RANGE_LIMIT
1302
- puts "p#{ Process.pid } #{ Time.new } break add miss range at #{ pack_id }"
1303
- ignored = true
1304
- break
1305
- end
1306
- end
1222
+ # 处理wbuff
1223
+ data = src_info[ :wbuff ]
1307
1224
 
1308
- curr_pack_id = pack_id + 1
1309
- end
1225
+ # 写前为空,处理关闭写
1226
+ if data.empty? then
1227
+ if src_info[ :closing_write ] then
1228
+ close_write_src( src )
1229
+ else
1230
+ @writes.delete( src )
1231
+ end
1310
1232
 
1311
- if !ignored && ( curr_pack_id <= relay_dst_pack_id )
1312
- ranges << [ curr_pack_id, relay_dst_pack_id ]
1313
- end
1233
+ return
1234
+ end
1314
1235
 
1315
- # puts "debug1 continue/relay #{ src_ext[ :continue_dst_pack_id ] }/#{ relay_dst_pack_id } send MISS #{ ranges.size }"
1236
+ # 写入
1237
+ begin
1238
+ written = src.write_nonblock( data )
1239
+ rescue IO::WaitWritable, Errno::EINTR
1240
+ print 'w'
1241
+ return
1242
+ rescue Exception => e
1243
+ # puts "debug1 write src #{ e.class }"
1244
+ close_write_src( src )
1316
1245
 
1317
- ranges.each do | pack_id_begin, pack_id_end |
1318
- data2 = [ 0, MISS, dst_port, pack_id_begin, pack_id_end ].pack( 'Q>CnQ>Q>' )
1319
- add_tun_ctlmsg( data2 )
1320
- end
1321
- end
1322
- when MISS
1323
- return if ( from_addr != @tun_info[ :tund_addr ] ) || ( @tun_info[ :resendings ].size >= RESENDING_LIMIT )
1246
+ if dst then
1247
+ close_read_dst( dst )
1248
+ else
1249
+ stream = src_info[ :stream ]
1250
+ close_read_stream( stream ) if stream
1251
+ end
1324
1252
 
1325
- src_id, pack_id_begin, pack_id_end = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
1253
+ return
1254
+ end
1326
1255
 
1327
- src_ext = @tun_info[ :src_exts ][ src_id ]
1328
- return unless src_ext
1256
+ # puts "debug2 written src #{ written }"
1257
+ data = data[ written..-1 ]
1258
+ src_info[ :wbuff ] = data
1259
+ end
1329
1260
 
1330
- ( pack_id_begin..pack_id_end ).each do | pack_id |
1331
- send_at = src_ext[ :send_ats ][ pack_id ]
1261
+ ##
1262
+ # write dst
1263
+ #
1264
+ def write_dst( dst )