p2p2 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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