girl 0.86.0 → 0.90.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of girl might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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 )
1265
+ return if dst.closed?
1266
+ dst_info = @dst_infos[ dst ]
1267
+ src = dst_info[ :src ]
1332
1268
 
1333
- if send_at
1334
- break if now - send_at < CHECK_STATUS_INTERVAL
1335
- @tun_info[ :resendings ] << [ src_id, pack_id ]
1336
- end
1337
- end
1269
+ # 处理关闭
1270
+ if dst_info[ :closing ] then
1271
+ close_dst( dst )
1338
1272
 
1339
- add_write( tun )
1340
- when FIN1
1341
- return if from_addr != @tun_info[ :tund_addr ]
1273
+ if src then
1274
+ close_read_src( src )
1275
+ set_src_closing_write( src )
1276
+ end
1342
1277
 
1343
- dst_port, biggest_dst_pack_id, continue_src_pack_id = data[ 9, 18 ].unpack( 'nQ>Q>' )
1278
+ return
1279
+ end
1344
1280
 
1345
- src_id = @tun_info[ :src_ids ][ dst_port ]
1346
- return unless src_id
1281
+ data = dst_info[ :wbuff ]
1347
1282
 
1348
- src_ext = @tun_info[ :src_exts ][ src_id ]
1349
- return unless src_ext
1283
+ # 写前为空,处理关闭写
1284
+ if data.empty? then
1285
+ if dst_info[ :closing_write ] then
1286
+ close_write_dst( dst )
1287
+ else
1288
+ @writes.delete( dst )
1289
+ end
1350
1290
 
1351
- # puts "debug1 got fin1 #{ dst_port } biggest dst pack #{ biggest_dst_pack_id } completed src pack #{ continue_src_pack_id }"
1352
- src_ext[ :is_dst_closed ] = true
1353
- src_ext[ :biggest_dst_pack_id ] = biggest_dst_pack_id
1354
- release_wmems( src_ext, continue_src_pack_id )
1291
+ return
1292
+ end
1355
1293
 
1356
- if ( biggest_dst_pack_id == src_ext[ :continue_dst_pack_id ] )
1357
- # puts "debug1 2-1. tun recv fin1 -> all traffic received ? -> close src after write"
1358
- set_is_closing( src_ext[ :src ] )
1359
- end
1360
- when FIN2
1361
- return if from_addr != @tun_info[ :tund_addr ]
1294
+ # 写入
1295
+ begin
1296
+ written = dst.write_nonblock( data )
1297
+ rescue IO::WaitWritable, Errno::EINTR
1298
+ print 'w'
1299
+ return
1300
+ rescue Exception => e
1301
+ # puts "debug1 write dst #{ e.class }"
1302
+ close_write_dst( dst )
1303
+ close_read_src( src )
1304
+ return
1305
+ end
1362
1306
 
1363
- dst_port = data[ 9, 2 ].unpack( 'n' ).first
1307
+ data = data[ written..-1 ]
1308
+ dst_info[ :wbuff ] = data
1364
1309
 
1365
- src_id = @tun_info[ :src_ids ][ dst_port ]
1366
- return unless src_id
1310
+ unless src.closed? then
1311
+ src_info = @src_infos[ src ]
1312
+ src_info[ :last_sent_at ] = Time.new
1313
+ end
1314
+ end
1367
1315
 
1368
- # puts "debug1 1-2. tun recv fin2 -> del src ext"
1369
- del_src_ext( src_id )
1370
- when TUND_FIN
1371
- return if from_addr != @tun_info[ :tund_addr ]
1316
+ ##
1317
+ # write stream
1318
+ #
1319
+ def write_stream( stream )
1320
+ return if stream.closed?
1321
+ stream_info = @stream_infos[ stream ]
1322
+ src = stream_info[ :src ]
1323
+
1324
+ # 处理关闭
1325
+ if stream_info[ :closing ] then
1326
+ close_stream( stream )
1327
+ close_read_src( src )
1328
+ set_src_closing_write( src )
1329
+ return
1330
+ end
1372
1331
 
1373
- puts "p#{ Process.pid } #{ Time.new } recv tund fin"
1374
- set_is_closing( tun )
1375
- when IP_CHANGED
1376
- return if from_addr != @tun_info[ :tund_addr ]
1332
+ data = stream_info[ :wbuff ]
1377
1333
 
1378
- puts "p#{ Process.pid } #{ Time.new } recv ip changed"
1379
- set_is_closing( tun )
1334
+ # 写前为空,处理关闭写
1335
+ if data.empty? then
1336
+ if stream_info[ :closing_write ] then
1337
+ close_write_stream( stream )
1338
+ else
1339
+ @writes.delete( stream )
1380
1340
  end
1381
1341
 
1382
1342
  return
1383
1343
  end
1384
1344
 
1385
- return if from_addr != @tun_info[ :tund_addr ]
1386
-
1387
- dst_port = data[ 8, 2 ].unpack( 'n' ).first
1388
-
1389
- src_id = @tun_info[ :src_ids ][ dst_port ]
1390
- return unless src_id
1391
-
1392
- src_ext = @tun_info[ :src_exts ][ src_id ]
1393
- return if src_ext.nil? || src_ext[ :src ].closed?
1394
- return if ( pack_id <= src_ext[ :continue_dst_pack_id ] ) || src_ext[ :pieces ].include?( pack_id )
1395
-
1396
- data = data[ 10..-1 ]
1397
- # puts "debug2 got pack #{ pack_id }"
1398
-
1399
- if pack_id <= CONFUSE_UNTIL
1400
- # puts "debug2 #{ data.inspect }"
1401
- data = @custom.decode( data )
1402
- # puts "debug1 decoded pack #{ pack_id }"
1345
+ # 写入
1346
+ begin
1347
+ written = stream.write_nonblock( data )
1348
+ rescue IO::WaitWritable, Errno::EINTR
1349
+ print 'w'
1350
+ return
1351
+ rescue Exception => e
1352
+ # puts "debug1 write stream #{ e.class }"
1353
+ close_write_stream( stream )
1354
+ close_read_src( src )
1355
+ return
1403
1356
  end
1404
1357
 
1405
- # 放进写前,跳号放碎片缓存
1406
- if pack_id - src_ext[ :continue_dst_pack_id ] == 1
1407
- while src_ext[ :pieces ].include?( pack_id + 1 )
1408
- data << src_ext[ :pieces ].delete( pack_id + 1 )
1409
- pack_id += 1
1410
- end
1411
-
1412
- src_ext[ :continue_dst_pack_id ] = pack_id
1413
- src_ext[ :last_continue_at ] = now
1414
- add_src_wbuff( src_ext[ :src ], data )
1415
- # puts "debug2 update continue dst pack #{ pack_id }"
1358
+ # puts "debug2 written stream #{ written }"
1359
+ data = data[ written..-1 ]
1360
+ stream_info[ :wbuff ] = data
1416
1361
 
1417
- # 接到流量,若对面已关闭,且流量正好收全,关闭src
1418
- if src_ext[ :is_dst_closed ] && ( pack_id == src_ext[ :biggest_dst_pack_id ] )
1419
- # puts "debug1 2-2. tun recv traffic -> dst closed and all traffic received ? -> close src after write"
1420
- set_is_closing( src_ext[ :src ] )
1421
- end
1422
- else
1423
- src_ext[ :pieces ][ pack_id ] = data
1362
+ unless src.closed? then
1363
+ src_info = @src_infos[ src ]
1364
+ src_info[ :last_sent_at ] = Time.new
1424
1365
  end
1425
1366
  end
1426
1367