emonti-rbkb 0.6.1.1

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.
@@ -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
+