p2p2 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/p2p2/p2pd.rb CHANGED
@@ -1,286 +1,135 @@
1
- require 'p2p2/head'
2
- require 'p2p2/version'
3
- require 'socket'
4
-
5
- ##
6
- # P2p2::P2pd - 内网里的任意应用,访问另一个内网里的应用服务端。配对服务器端。
7
- #
8
- # 1.
9
- #
10
- # ```
11
- # p2pd
12
- # ^ ^
13
- # ^ ^
14
- # “周立波的房间” “周立波的房间”
15
- # ^ ^
16
- # ^ ^
17
- # p1 --> nat --><-- nat <-- p2
18
- #
19
- # ```
20
- #
21
- # 2.
22
- #
23
- # ```
24
- # ssh --> p2 --> (encode) --> p1 --> (decode) --> sshd
25
- # ```
26
- #
27
- # usage
28
- # =====
29
- #
30
- # 1. Girl::P2pd.new( 5050 ).looping # @server
31
- #
32
- # 2. Girl::P1.new( 'your.server.ip', 5050, '127.0.0.1', 22, '周立波' ).looping # @home1
33
- #
34
- # 3. Girl::P2.new( 'your.server.ip', 5050, '0.0.0.0', 2222, '周立波' ).looping # @home2
35
- #
36
- # 4. ssh -p2222 libo@localhost
37
- #
38
- module P2p2
39
- class P2pd
40
-
41
- ##
42
- # roomd_port 配对服务器端口
43
- # roomd_dir 可在该目录下看到所有的p1
44
- def initialize( roomd_port = 5050, roomd_dir = '/tmp' )
45
- @roomd_port = roomd_port
46
- @roomd_dir = roomd_dir
47
- @mutex = Mutex.new
48
- @reads = []
49
- @writes = []
50
- @closings = []
51
- @roles = {} # sock => :roomd / :room
52
- @infos = {}
53
- @rooms = {} # object_id => room
54
- @p1s = {} # title => room
55
- @p2s = {} # title => room
56
-
57
- ctlr, ctlw = IO.pipe
58
- @ctlw = ctlw
59
- @roles[ ctlr ] = :ctlr
60
- @reads << ctlr
61
-
62
- roomd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
63
- roomd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
64
- roomd.bind( Socket.pack_sockaddr_in( @roomd_port, '0.0.0.0' ) )
65
- roomd.listen( 511 )
66
-
67
- @roles[ roomd ] = :roomd
68
- @reads << roomd
69
- end
70
-
71
- def looping
72
- puts 'looping'
73
-
74
- loop_expire
75
-
76
- loop do
77
- rs, ws = IO.select( @reads, @writes )
78
-
79
- @mutex.synchronize do
80
- rs.each do | sock |
81
- case @roles[ sock ]
82
- when :ctlr
83
- read_ctlr( sock )
84
- when :roomd
85
- read_roomd( sock )
86
- when :room
87
- read_room( sock )
88
- end
89
- end
90
-
91
- ws.each do | sock |
92
- case @roles[ sock ]
93
- when :room
94
- write_room( sock )
95
- end
96
- end
97
- end
98
- end
99
- rescue Interrupt => e
100
- puts e.class
101
- quit!
102
- end
103
-
104
- def quit!
105
- exit
106
- end
107
-
108
- private
109
-
110
- def loop_expire
111
- Thread.new do
112
- loop do
113
- sleep 3600
114
-
115
- if @infos.any?
116
- @mutex.synchronize do
117
- now = Time.new
118
-
119
- @infos.select{ | _, info | now - info[ :updated_at ] > 86400 }.each do | room, _ |
120
- @ctlw.write( [ CTL_CLOSE_ROOM, [ room.object_id ].pack( 'N' ) ].join )
121
- end
122
- end
123
- end
124
- end
125
- end
126
- end
127
-
128
- def read_ctlr( ctlr )
129
- case ctlr.read( 1 )
130
- when CTL_CLOSE_ROOM
131
- room_id = ctlr.read( 4 ).unpack( 'N' ).first
132
- room = @rooms[ room_id ]
133
-
134
- if room
135
- add_closing( room )
136
- end
137
- end
138
- end
139
-
140
- def read_roomd( roomd )
141
- begin
142
- room, addrinfo = roomd.accept_nonblock
143
- rescue IO::WaitReadable, Errno::EINTR
144
- return
145
- end
146
-
147
- @rooms[ room.object_id ] = room
148
- @roles[ room ] = :room
149
- @infos[ room ] = {
150
- title: nil,
151
- wbuff: '',
152
- sockaddr: addrinfo.to_sockaddr,
153
- updated_at: Time.new
154
- }
155
- @reads << room
156
- end
157
-
158
- def read_room( room )
159
- begin
160
- data = room.read_nonblock( PACK_SIZE )
161
- rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
162
- return
163
- rescue Exception => e
164
- add_closing( room )
165
- return
166
- end
167
-
168
- info = @infos[ room ]
169
- info[ :updated_at ] = Time.new
170
- ctl_num = data[ 0 ].unpack( 'C' ).first
171
-
172
- case ctl_num
173
- when SET_TITLE
174
- title = data[ 1..-1 ]
175
-
176
- if title.size > 255
177
- puts 'title too long'
178
- add_closing( room )
179
- return
180
- end
181
-
182
- if @p1s.include?( title )
183
- puts "p1 #{ title.inspect } already exist #{ Time.new }"
184
- add_closing( room )
185
- return
186
- end
187
-
188
- if @p2s.include?( title )
189
- p2 = @p2s[ title ]
190
- p2_info = @infos[ p2 ]
191
- add_write( room, p2_info[ :sockaddr ] )
192
- add_write( p2, info[ :sockaddr ] )
193
- return
194
- end
195
-
196
- begin
197
- File.open( File.join( @roomd_dir, title ), 'w' )
198
- rescue Errno::ENOENT, ArgumentError => e
199
- puts "open title path #{ e.class }"
200
- add_closing( room )
201
- return
202
- end
203
-
204
- info[ :title ] = title
205
- @p1s[ title ] = room
206
- when PAIRING
207
- title = data[ 1..-1 ]
208
-
209
- if title.size > 255
210
- puts 'pairing title too long'
211
- add_closing( room )
212
- return
213
- end
214
-
215
- if @p2s.include?( title )
216
- puts "p2 #{ title.inspect } already exist #{ Time.new }"
217
- add_closing( room )
218
- return
219
- end
220
-
221
- if @p1s.include?( title )
222
- p1 = @p1s[ title ]
223
- p1_info = @infos[ p1 ]
224
- add_write( room, p1_info[ :sockaddr ] )
225
- add_write( p1, info[ :sockaddr ] )
226
- return
227
- end
228
-
229
- info[ :title ] = title
230
- @p2s[ title ] = room
231
- end
232
- end
233
-
234
- def write_room( room )
235
- if @closings.include?( room )
236
- close_room( room )
237
- return
238
- end
239
-
240
- info = @infos[ room ]
241
- room.write( info[ :wbuff ] )
242
- @writes.delete( room )
243
- end
244
-
245
- def add_closing( sock )
246
- unless @closings.include?( sock )
247
- @reads.delete( sock )
248
- @closings << sock
249
- end
250
-
251
- add_write( sock )
252
- end
253
-
254
- def add_write( sock, data = nil )
255
- if data
256
- info = @infos[ sock ]
257
- info[ :wbuff ] = data
258
- end
259
-
260
- unless @writes.include?( sock )
261
- @writes << sock
262
- end
263
- end
264
-
265
- def close_room( room )
266
- room.close
267
- @rooms.delete( room.object_id )
268
- @reads.delete( room )
269
- @writes.delete( room )
270
- @closings.delete( room )
271
- @roles.delete( room )
272
- info = @infos.delete( room )
273
-
274
- if info && info[ :title ]
275
- title_path = File.join( @roomd_dir, info[ :title ] )
276
-
277
- if File.exist?( title_path )
278
- File.delete( title_path )
279
- @p1s.delete( info[ :title ] )
280
- else
281
- @p2s.delete( info[ :title ] )
282
- end
283
- end
284
- end
285
- end
286
- end
1
+ require 'p2p2/head'
2
+ require 'p2p2/version'
3
+ require 'socket'
4
+
5
+ ##
6
+ # P2p2::P2pd - 内网里的任意应用,访问另一个内网里的应用服务端。配对服务器端。
7
+ #
8
+ # 1.
9
+ #
10
+ # ```
11
+ # p2pd
12
+ # ^ ^
13
+ # ^ ^
14
+ # “周立波的房间” “周立波的房间”
15
+ # ^ ^
16
+ # ^ ^
17
+ # p1 --> nat --><-- nat <-- p2
18
+ #
19
+ # ```
20
+ #
21
+ # 2.
22
+ #
23
+ # ```
24
+ # ssh --> p2 --> (encode) --> p1 --> (decode) --> sshd
25
+ # ```
26
+ #
27
+ # usage
28
+ # =====
29
+ #
30
+ # 1. Girl::P2pd.new( 5050 ).looping # @server
31
+ #
32
+ # 2. Girl::P1.new( 'your.server.ip', 5050, '127.0.0.1', 22, '周立波' ).looping # @home1
33
+ #
34
+ # 3. Girl::P2.new( 'your.server.ip', 5050, '0.0.0.0', 2222, '周立波' ).looping # @home2
35
+ #
36
+ # 4. ssh -p2222 libo@localhost
37
+ #
38
+ # 包结构
39
+ # ======
40
+ #
41
+ # Q>: 1+ app/shadow_id -> Q>: pack_id -> traffic
42
+ # 0 ctlmsg -> C: 1 peer addr -> p1/p2_sockaddr
43
+ # 2 heartbeat -> C: random char
44
+ # 3 a new app -> Q>: app_id
45
+ # 4 paired -> Q>Q>: app_id shadow_id
46
+ # 5 shadow status -> Q>Q>Q>: shadow_id biggest_shadow_pack_id continue_app_pack_id
47
+ # 6 app status -> Q>Q>Q>: app_id biggest_app_pack_id continue_shadow_pack_id
48
+ # 7 miss -> Q>Q>Q>: app/shadow_id pack_id_begin pack_id_end
49
+ # 8 fin1 -> Q>: app/shadow_id
50
+ # 9 got fin1 -> Q>: app/shadow_id
51
+ # 10 fin2 -> Q>: app/shadow_id
52
+ # 11 got fin2 -> Q>: app/shadow_id
53
+ # 12 p1 fin
54
+ # 13 p2 fin
55
+ #
56
+ module P2p2
57
+ class P2pd
58
+
59
+ ##
60
+ # p2pd_port 配对服务器端口
61
+ # p2pd_dir 可在该目录下看到所有的房间
62
+ def initialize( p2pd_port = 5050, p2pd_dir = '/tmp' )
63
+ p2pd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
64
+ p2pd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
65
+ p2pd.bind( Socket.pack_sockaddr_in( p2pd_port, '0.0.0.0' ) )
66
+
67
+ @p2pd = p2pd
68
+ @p2pd_dir = p2pd_dir
69
+ end
70
+
71
+ def looping
72
+ puts 'looping'
73
+
74
+ loop do
75
+ rs, _ = IO.select( [ @p2pd ] )
76
+ read_p2pd( rs.first )
77
+ end
78
+ rescue Interrupt => e
79
+ puts e.class
80
+ quit!
81
+ end
82
+
83
+ def quit!
84
+ exit
85
+ end
86
+
87
+ private
88
+
89
+ def read_p2pd( p2pd )
90
+ data, addrinfo, rflags, *controls = p2pd.recvmsg
91
+ return if data.size > 255
92
+
93
+ sockaddr = addrinfo.to_sockaddr
94
+ title_path = File.join( @p2pd_dir, data )
95
+
96
+ unless File.exist?( title_path )
97
+ write_title( title_path, sockaddr )
98
+ return
99
+ end
100
+
101
+ if Time.new - File.mtime( title_path ) > 300
102
+ write_title( title_path, sockaddr )
103
+ return
104
+ end
105
+
106
+ op_sockaddr = IO.binread( title_path )
107
+
108
+ if Addrinfo.new( op_sockaddr ).ip_address == addrinfo.ip_address
109
+ write_title( title_path, sockaddr )
110
+ return
111
+ end
112
+
113
+ send_pack( p2pd, "#{ [ 0, PEER_ADDR ].pack( 'Q>C' ) }#{ op_sockaddr }", sockaddr )
114
+ send_pack( p2pd, "#{ [ 0, PEER_ADDR ].pack( 'Q>C' ) }#{ sockaddr }", op_sockaddr )
115
+ end
116
+
117
+ def write_title( title_path, sockaddr )
118
+ begin
119
+ # puts "debug write title #{ title_path } #{ Time.new }"
120
+ IO.binwrite( title_path, sockaddr )
121
+ rescue Errno::ENOENT, ArgumentError => e
122
+ puts "binwrite #{ e.class } #{ Time.new }"
123
+ end
124
+ end
125
+
126
+ def send_pack( sock, data, target_sockaddr )
127
+ begin
128
+ # puts "debug sendmsg #{ data.inspect } #{ Time.new }"
129
+ sock.sendmsg( data, 0, target_sockaddr )
130
+ rescue IO::WaitWritable, Errno::EINTR => e
131
+ puts "sendmsg #{ e.class } #{ Time.new }"
132
+ end
133
+ end
134
+ end
135
+ end
data/lib/p2p2/version.rb CHANGED
@@ -1,3 +1,3 @@
1
- module P2p2
2
- VERSION = "0.7.2"
3
- end
1
+ module P2p2
2
+ VERSION = "0.8.0"
3
+ end
data/p2p2.gemspec CHANGED
@@ -1,29 +1,29 @@
1
-
2
- lib = File.expand_path("../lib", __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "p2p2/version"
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "p2p2"
8
- spec.version = P2p2::VERSION
9
- spec.authors = ["takafan"]
10
- spec.email = ["qqtakafan@gmail.com"]
11
-
12
- spec.summary = %q{p2p}
13
- spec.description = %q{内网里的任意应用,访问另一个内网里的应用服务端。}
14
- spec.homepage = "https://github.com/takafan/p2p2"
15
- spec.license = "MIT"
16
-
17
- spec.files = %w[
18
- p2p2.gemspec
19
- lib/p2p2.rb
20
- lib/p2p2/head.rb
21
- lib/p2p2/hex.rb
22
- lib/p2p2/p1.rb
23
- lib/p2p2/p2.rb
24
- lib/p2p2/p2pd.rb
25
- lib/p2p2/version.rb
26
- ]
27
-
28
- spec.require_paths = ["lib"]
29
- end
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "p2p2/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "p2p2"
8
+ spec.version = P2p2::VERSION
9
+ spec.authors = ["takafan"]
10
+ spec.email = ["qqtakafan@gmail.com"]
11
+
12
+ spec.summary = %q{p2p}
13
+ spec.description = %q{内网里的任意应用,访问另一个内网里的应用服务端。}
14
+ spec.homepage = "https://github.com/takafan/p2p2"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = %w[
18
+ p2p2.gemspec
19
+ lib/p2p2.rb
20
+ lib/p2p2/head.rb
21
+ lib/p2p2/hex.rb
22
+ lib/p2p2/p1.rb
23
+ lib/p2p2/p2.rb
24
+ lib/p2p2/p2pd.rb
25
+ lib/p2p2/version.rb
26
+ ]
27
+
28
+ spec.require_paths = ["lib"]
29
+ end