emonti-rbkb 0.6.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,220 @@
1
+
2
+ module Plug
3
+ module Blit
4
+ include Base
5
+
6
+ DEFAULT_IPADDR = "127.0.0.1"
7
+ DEFAULT_PORT = 25195
8
+ DEFAULT_PROTOCOL = :TCP
9
+
10
+ OPCODES = {
11
+ 0 => :squelch,
12
+ 1 => :unsquelch,
13
+ 2 => :delete,
14
+ 5 => :sendmsg,
15
+ 6 => :list_peers,
16
+
17
+ 0xfe => :clear,
18
+ 0xff => :kill,
19
+ }
20
+
21
+ attr_accessor :kind
22
+
23
+ def initialize(transport, slave)
24
+ super(transport)
25
+
26
+ @kind = :blitsrv
27
+ @slave = slave
28
+ @peers = slave.peers
29
+ initbuf
30
+ end
31
+
32
+ def post_init
33
+ # override so we don't get unneccessary "Start" message from Base
34
+ end
35
+
36
+ def unbind
37
+ # override so we don't get unneccessary "closed" message from Base
38
+ end
39
+
40
+
41
+ ### Blit protocol stuff
42
+ SIG = "BLT"
43
+
44
+ # (re)initializes the blit buffer
45
+ def initbuf
46
+ @buf = StringIO.new
47
+ end
48
+
49
+ def receive_data dat
50
+ return unless (@buf.write(dat) > SIG.size) or (@buf.pos > (SIG.size + 1))
51
+
52
+ @buf.rewind
53
+
54
+ return unless @buf.read(SIG.size) == SIG and
55
+ op = OPCODES[ @buf.read(1)[0] ]
56
+
57
+ initbuf if self.send(op)
58
+ end
59
+
60
+
61
+ def self.blit_header(op)
62
+ return nil unless opno = OPCODES.invert[op]
63
+ SIG + opno.chr
64
+ end
65
+
66
+ def mute
67
+ unless ( peerno=@buf.read(2) and peerno.size == 2 and
68
+ peer=@peers[peerno.dat_to_num(:big)] )
69
+
70
+ UI.log "** BLIT-ERROR(Malformed or missing peer for mute)"
71
+ return true
72
+ end
73
+ end
74
+
75
+ def self.make_mute(peerno)
76
+ self.blit_header(:squelch) +
77
+ peerno.to_bytes(2, :big)
78
+ end
79
+
80
+ def unmute
81
+ unless ( peerno=@buf.read(2) and peerno.size == 2 and
82
+ peer=@peers[peerno.dat_to_num(:big)] )
83
+ UI.log "** BLIT-ERROR(Malformed or missing peer for unmute)"
84
+ return true
85
+ end
86
+ end
87
+
88
+ def self.make_squelch(peerno)
89
+ self.blit_header(:squelch) +
90
+ peerno.to_bytes(2, :big)
91
+ end
92
+
93
+ def sendmsg
94
+ unless peerno=@buf.read(2) and peerno.size == 2 and
95
+ bufsiz=@buf.read(4) and bufsiz.size == 4
96
+ UI.log "** BLIT-ERROR(Malformed sendmsg)"
97
+ return true
98
+ end
99
+
100
+ peerno = peerno.dat_to_num(:big)
101
+ bufsiz = bufsiz.dat_to_num(:big)
102
+
103
+ if (rdat=@buf.read(bufsiz)).size == bufsiz
104
+ if peer=@peers[peerno]
105
+ peer.say(rdat, self)
106
+ return true
107
+ else
108
+ UI.log "** BLIT-ERROR(Invalid peer index #{peerno})"
109
+ return true
110
+ end
111
+ else
112
+ return nil
113
+ end
114
+ end
115
+
116
+ # Blit packed message format is (SUBJECT TO CHANGE):
117
+ # "BLT"
118
+ # char opcode
119
+ # uint16be idx = index of slave peer to send to
120
+ # uint32le size = length of data
121
+ # str data
122
+ def self.make_sendmsg(idx, dat)
123
+ self.blit_header(:sendmsg) +
124
+ idx.to_bytes(2, :big) +
125
+ dat.size.to_bytes(4, :big) +
126
+ dat
127
+ end
128
+
129
+ def kill
130
+ UI.log("** BLIT-KILL - Received shutdown command")
131
+ EM.stop
132
+ end
133
+
134
+ def self.make_kill(idx=nil)
135
+ self.blit_header(:kill)
136
+ end
137
+
138
+ def clear
139
+ @peers.each { |p| p.close }
140
+ @peers.replace []
141
+ end
142
+
143
+ def self.make_clear
144
+ self.blit_header(:clear)
145
+ end
146
+
147
+ def delete(peerno)
148
+ @peers.delete(peerno)
149
+ end
150
+
151
+ def self.make_delete(idx=0)
152
+ self.blit_header(:delete) +
153
+ idx.to_bytes(2, :big)
154
+ end
155
+
156
+ def list_peers
157
+ UI.log("** BLIT-LISTPEERS - Received list peers command")
158
+
159
+ @peers.each_index {|i| UI.log "** #{i} - #{@peers[i].name}"}
160
+ UI.log("** BLIT-LISTPEERS-END - End of peer list")
161
+ end
162
+
163
+ def self.make_list_peers
164
+ self.blit_header(:list_peers)
165
+ end
166
+
167
+ #----------------------------------------------------------------------
168
+ # Convenience methods for blit clients
169
+ #----------------------------------------------------------------------
170
+
171
+ BLIT_HANDLERS = {
172
+ :TCP => lambda {|msg|
173
+ s=TCPSocket.new(@blit_addr, @blit_port)
174
+ wl=s.write(msg)
175
+ s.close
176
+ return wl
177
+ },
178
+ :UDP => lambda {|msg|
179
+ s=UDPSocket.new
180
+ wl=s.send( msg, 0, @blit_addr, @blit_port)
181
+ s.close
182
+ return wl
183
+ }
184
+ }
185
+
186
+ def self.blit_init(opts={})
187
+ @blit_addr = (opts[:addr] || DEFAULT_IPADDR)
188
+ @blit_port = (opts[:port] || DEFAULT_PORT)
189
+ proto = (opts[:protocol] || DEFAULT_PROTOCOL)
190
+ @blit_handler = BLIT_HANDLERS[ proto ]
191
+ raise "invalid blit transport protocol" unless @blit_handler
192
+ end
193
+
194
+ def self.initialized?
195
+ @blit_addr and @blit_port and @blit_handler
196
+ end
197
+
198
+ def self.blit_send(data, idx=0)
199
+ msg = make_sendmsg(idx, data)
200
+ blit_raw(msg)
201
+ end
202
+
203
+ def self.blit_raw(buf)
204
+ raise "use blit_init first!" unless self.initialized?
205
+ @blit_handler.call buf
206
+ end
207
+
208
+ end # of module Blit
209
+
210
+
211
+ end # of module Plug
212
+
213
+ class String
214
+ #----------------------------------------------------------------------
215
+ # A Blit sender convenience method for strings
216
+ def blit(idx=0)
217
+ raise "blit must be initialized with blit_init" unless Plug::Blit.initialized?
218
+ Plug::Blit.blit_send(self, idx)
219
+ end
220
+ end
@@ -0,0 +1,64 @@
1
+ require 'socket'
2
+
3
+ module Plug
4
+
5
+ class Peer
6
+ attr_reader :addr, :transport, :name, :owner, :host, :port
7
+ attr_accessor :mute
8
+
9
+ def initialize(addr, owner)
10
+ @addr = addr
11
+ @owner = owner
12
+ @transport = @owner.transport
13
+
14
+ @port, @host = Socket.unpack_sockaddr_in(@addr)
15
+ @name = "PEER-#{@host}:#{@port}(#{@transport})"
16
+ end
17
+
18
+ def say(dat, sender)
19
+ UI.dump(sender.name, self.name, dat)
20
+
21
+ if @transport == :UDP
22
+ @owner.send_datagram(dat, @host, @port)
23
+ else
24
+ @owner.send_data(dat)
25
+ end
26
+ end
27
+
28
+ def close
29
+ @owner.unbind unless @transport == :UDP
30
+ end
31
+ end
32
+
33
+
34
+ class PeerList < Array
35
+ def initialize(owner, *args)
36
+ @owner = owner
37
+ @transport = @owner.transport
38
+
39
+ super(*args)
40
+ end
41
+
42
+ def find_peer(addr)
43
+ self.find {|p| p.addr == addr }
44
+ return nil
45
+ end
46
+
47
+ def add_peer(addr)
48
+ self << Peer.new(addr, @owner)
49
+ self.last
50
+ end
51
+
52
+ def add_peer_manually(host, port)
53
+ addr = Socket.pack_sockaddr_in(port, host)
54
+ return (find_peer(addr) || add_peer(addr))
55
+ end
56
+
57
+ def delete(addr)
58
+ if p=find_peer(addr)
59
+ p.close
60
+ super(p)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,195 @@
1
+
2
+ require "rbkb/plug/peer.rb"
3
+ require 'stringio'
4
+ require 'socket'
5
+
6
+
7
+ module Plug
8
+ module UI
9
+ LOGCFG = {:out => STDERR, :dump => :hex}
10
+
11
+ def self.prompt(*msg); STDERR.puts msg ; STDIN.gets ; end
12
+
13
+ def self.log(*msg); LOGCFG[:out].puts msg ; end
14
+
15
+ def self.verbose(*msg); LOGCFG[:out].puts msg if LOGCFG[:verbose] ; end
16
+
17
+ def self.debug(*msg); LOGCFG[:out].puts msg if LOGCFG[:debug] ; end
18
+
19
+ def self.dump(from, to, dat)
20
+ if dump=LOGCFG[:dump]
21
+ LOGCFG[:out].puts "%% #{from} SAYS TO #{to} LEN=#{dat.size}",
22
+ (dump == :hex)? dat.hexdump(:out => StringIO.new) : dat,
23
+ "%%"
24
+ end
25
+ end
26
+ end
27
+
28
+
29
+ module Base
30
+ attr_accessor :peers, :transport, :kind
31
+
32
+ def initialize(transport)
33
+ # raise "Invalid transport #{transport.inspect}" unless (:UDP, :TCP).include?(transport)
34
+ @transport = transport
35
+ @peers = PeerList.new(self)
36
+ end
37
+
38
+ def name
39
+ sn = get_sockname
40
+ addr = sn ? Socket.unpack_sockaddr_in(sn).reverse.join(":") : "PENDING"
41
+ "#{kind.to_s.upcase}-#{addr}(#{@transport})"
42
+ end
43
+
44
+
45
+ # plug_peer creates a peering association for a given peer based on
46
+ # get_peername. The existing or newly created peer object is returned.
47
+ def plug_peer
48
+ paddr = get_peername
49
+ peer = (@peers.find_peer(paddr) || @peers.add_peer(paddr) )
50
+ end
51
+
52
+
53
+ # plug_receive is used by receive_data to divert incoming messages.
54
+ # The "peer" is added if it is not already present. This instance
55
+ # will check whether # a peer is "muted" and will return the peer if not.
56
+ # This method can be overriden by child classes to implement additional
57
+ # checks. It receives "dat" so that such checks can optionally make
58
+ # forwarding decisions based on message data contents as well.
59
+ #
60
+ # Returns:
61
+ # - nil : indicates that the message should be stifled
62
+ # - A peer object : indicates that the message should be processed
63
+ # further
64
+ def plug_receive(dat)
65
+ peer = plug_peer
66
+ return peer unless peer.mute
67
+ end
68
+
69
+ # This instance of the say method is an abstract stub and just
70
+ # "dumps" the message. It should be overridden and optionally called
71
+ # with super() if you actually want to do anything useful when
72
+ # incoming messages are received.
73
+ def say(dat, sender)
74
+ UI.dump(sender.name, self.name, dat)
75
+ end
76
+
77
+ def post_init
78
+ UI.verbose "** #{name} Started"
79
+ if @kind==:server and peer=plug_peer
80
+ UI.log "** #{name} CONNECTED TO #{peer.name}"
81
+ end
82
+ end
83
+
84
+ def receive_data(dat)
85
+ if peer=plug_receive(dat)
86
+ say(dat, peer)
87
+ end
88
+ return peer
89
+ end
90
+
91
+ def connection_completed
92
+ peer = plug_peer
93
+ UI.log "** #{name} CONNECTED TO #{peer.name}"
94
+ return peer
95
+ end
96
+
97
+ def unbind
98
+ UI.log "** Connection " + ((@peers.empty?)? "refused." : "closed.")
99
+ EM.stop
100
+ end
101
+ end
102
+
103
+
104
+ # An abstract module to implement custom servers for any protocol
105
+ # incoming messages are diverted to 'process(dat, sender)' which takes
106
+ # a block, the yields to which are messages to respond with
107
+ module UdpServer
108
+ include Base
109
+
110
+ def kind ; :server ; end
111
+
112
+ def say(dat, sender)
113
+ super(dat, sender)
114
+
115
+ if self.respond_to? :process
116
+ self.send(:process, dat, sender) { |rply| sender.say(rply, self) }
117
+ end
118
+ end
119
+ end
120
+
121
+
122
+
123
+ # Telson is just a receiver for blit
124
+ module Telson
125
+ include Base
126
+ def kind ; :telson ; end
127
+ end
128
+
129
+
130
+ # Uses an array of static messages as a datasource for opaque protocol
131
+ # messages. Useful as a generic blit-able loop
132
+ module ArrayFeeder
133
+ include Base
134
+ attr_accessor :pos, :feed, :step, :close_at_end, :go_first,
135
+ :squelch_exhausted
136
+
137
+ def initialize(transport, opts={})
138
+ super(transport)
139
+
140
+ @pos = 0
141
+ @feed = []
142
+
143
+ opts.each_pair do |k,v|
144
+ accessor = k.to_s + "="
145
+ if self.respond_to?(accessor)
146
+ self.send(accessor, v)
147
+ else
148
+ raise "Bad attribute: #{k}"
149
+ end
150
+ end
151
+ raise "feed must be enumerable" unless @feed.kind_of? Enumerable
152
+ end
153
+
154
+ def go
155
+ if @go_first
156
+ feed_data
157
+ @go_first = false
158
+ end
159
+ end
160
+
161
+ def connection_completed
162
+ peer=super()
163
+ go if @go_first
164
+ return peer
165
+ end
166
+
167
+
168
+ def say(dat, sender)
169
+ super(dat, sender)
170
+ if @step
171
+ EventMachine.defer(
172
+ proc { UI.prompt ">> Hit [enter] to continue at #{@pos}:" },
173
+ proc {|x| feed_data }
174
+ )
175
+ else
176
+ feed_data
177
+ end
178
+ end
179
+
180
+ def feed_data(dst=plug_peer)
181
+ unless dat=@feed[@pos]
182
+ UI.log "** FEED EXHAUSTED" unless @squelch_exhausted
183
+ return nil
184
+ end
185
+
186
+ dst.say dat.to_s, self
187
+
188
+ if (@pos += 1) >= @feed.size and @close_at_end
189
+ close_connection_after_writing
190
+ end
191
+ end
192
+
193
+ end # ArrayFeeder
194
+ end # Plug
195
+