cancer 0.1.0.a1
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/.autotest +3 -0
- data/.gitignore +5 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/cancer.gemspec +156 -0
- data/documentation/STREAM_INITIATION.markdown +18 -0
- data/examples/example.rb +80 -0
- data/examples/example_2.rb +20 -0
- data/examples/example_em.rb +11 -0
- data/examples/example_em_helper.rb +23 -0
- data/examples/example_im_roster.rb +26 -0
- data/examples/example_xep_0004.rb +124 -0
- data/examples/example_xep_0047.rb +35 -0
- data/examples/example_xep_0050.rb +78 -0
- data/examples/example_xep_0065.rb +66 -0
- data/examples/example_xep_0096.dup.rb +40 -0
- data/examples/example_xep_0096_xep_0047.rb +42 -0
- data/examples/example_xep_0096_xep_0065.rb +40 -0
- data/lib/cancer.rb +122 -0
- data/lib/cancer/adapter.rb +33 -0
- data/lib/cancer/adapters/em.rb +88 -0
- data/lib/cancer/adapters/socket.rb +122 -0
- data/lib/cancer/builder.rb +28 -0
- data/lib/cancer/controller.rb +32 -0
- data/lib/cancer/dependencies.rb +187 -0
- data/lib/cancer/events/abstract_event.rb +10 -0
- data/lib/cancer/events/base_matchers.rb +63 -0
- data/lib/cancer/events/binary_matchers.rb +30 -0
- data/lib/cancer/events/eventable.rb +92 -0
- data/lib/cancer/events/exception_events.rb +28 -0
- data/lib/cancer/events/handler.rb +33 -0
- data/lib/cancer/events/manager.rb +130 -0
- data/lib/cancer/events/named_events.rb +25 -0
- data/lib/cancer/events/proc_matcher.rb +16 -0
- data/lib/cancer/events/xml_events.rb +44 -0
- data/lib/cancer/exceptions.rb +53 -0
- data/lib/cancer/jid.rb +215 -0
- data/lib/cancer/lock.rb +32 -0
- data/lib/cancer/spec.rb +35 -0
- data/lib/cancer/spec/error.rb +18 -0
- data/lib/cancer/spec/extentions.rb +79 -0
- data/lib/cancer/spec/matcher.rb +30 -0
- data/lib/cancer/spec/mock_adapter.rb +139 -0
- data/lib/cancer/spec/mock_stream.rb +15 -0
- data/lib/cancer/spec/transcript.rb +107 -0
- data/lib/cancer/stream.rb +182 -0
- data/lib/cancer/stream/builder.rb +20 -0
- data/lib/cancer/stream/controller.rb +36 -0
- data/lib/cancer/stream/event_helper.rb +12 -0
- data/lib/cancer/stream/xep.rb +52 -0
- data/lib/cancer/stream_parser.rb +144 -0
- data/lib/cancer/support.rb +27 -0
- data/lib/cancer/support/hash.rb +32 -0
- data/lib/cancer/support/string.rb +22 -0
- data/lib/cancer/synchronized_stanza.rb +79 -0
- data/lib/cancer/thread_pool.rb +118 -0
- data/lib/cancer/xep.rb +43 -0
- data/lib/cancer/xeps/core.rb +109 -0
- data/lib/cancer/xeps/core/bind.rb +33 -0
- data/lib/cancer/xeps/core/sasl.rb +113 -0
- data/lib/cancer/xeps/core/session.rb +22 -0
- data/lib/cancer/xeps/core/stream.rb +18 -0
- data/lib/cancer/xeps/core/terminator.rb +21 -0
- data/lib/cancer/xeps/core/tls.rb +34 -0
- data/lib/cancer/xeps/im.rb +323 -0
- data/lib/cancer/xeps/xep_0004_x_data.rb +692 -0
- data/lib/cancer/xeps/xep_0020_feature_neg.rb +35 -0
- data/lib/cancer/xeps/xep_0030_disco.rb +167 -0
- data/lib/cancer/xeps/xep_0047_ibb.rb +322 -0
- data/lib/cancer/xeps/xep_0050_commands.rb +256 -0
- data/lib/cancer/xeps/xep_0065_bytestreams.rb +306 -0
- data/lib/cancer/xeps/xep_0066_oob.rb +69 -0
- data/lib/cancer/xeps/xep_0095_si.rb +211 -0
- data/lib/cancer/xeps/xep_0096_si_filetransfer.rb +173 -0
- data/lib/cancer/xeps/xep_0114_component.rb +73 -0
- data/lib/cancer/xeps/xep_0115_caps.rb +180 -0
- data/lib/cancer/xeps/xep_0138_compress.rb +134 -0
- data/lib/cancer/xeps/xep_0144_rosterx.rb +250 -0
- data/lib/cancer/xeps/xep_0184_receipts.rb +40 -0
- data/lib/cancer/xeps/xep_0199_ping.rb +41 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/stream/stanza_errors_spec.rb +47 -0
- data/spec/stream/stream_errors_spec.rb +38 -0
- data/spec/stream/stream_initialization_spec.rb +160 -0
- data/spec/xep_0050/local_spec.rb +165 -0
- data/spec/xep_0050/remote_spec.rb +44 -0
- metadata +200 -0
@@ -0,0 +1,256 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
# Ad-Hoc Commands
|
4
|
+
# http://xmpp.org/extensions/xep-0050.html
|
5
|
+
module XEP_0050
|
6
|
+
include Cancer::XEP
|
7
|
+
|
8
|
+
NS = "http://jabber.org/protocol/commands"
|
9
|
+
NAMESPACES = { 'c' => CLIENT_NS, 'cmd' => NS, 'x' => Cancer::XEP_0004::NS }
|
10
|
+
|
11
|
+
dependency 'core'
|
12
|
+
dependency 'xep-0004'
|
13
|
+
dependency 'xep-0030'
|
14
|
+
|
15
|
+
def self.enhance_stream(stream)
|
16
|
+
stream.extend_stream do
|
17
|
+
include Cancer::XEP_0050::StreamHelpers
|
18
|
+
end
|
19
|
+
stream.install_controller Cancer::XEP_0050::Controller
|
20
|
+
stream.install_event_helper Cancer::XEP_0050::EventDSL
|
21
|
+
stream.disco.feature(NS)
|
22
|
+
end
|
23
|
+
|
24
|
+
module StreamHelpers
|
25
|
+
|
26
|
+
def commands_provided_by(peer)
|
27
|
+
disco_items_for(peer, NS).inject({}) { |commands, item| commands[item['node'].to_s] = item['name'].to_s ; commands }
|
28
|
+
end
|
29
|
+
|
30
|
+
def command(to, node)
|
31
|
+
session = RemoteSession.new(self, to, node)
|
32
|
+
yield(session) if block_given?
|
33
|
+
session
|
34
|
+
end
|
35
|
+
|
36
|
+
def register_command(uri, name=nil)
|
37
|
+
@disco_commands ||= disco.item(:node => NS, :jid => self.options[:jid])
|
38
|
+
@disco_commands.item(:node => uri, :name => name, :jid => self.options[:jid])
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class RemoteSession
|
44
|
+
attr_reader :peer, :node, :id, :status, :allowed_actions, :execute_action
|
45
|
+
|
46
|
+
def initialize(stream, peer, node)
|
47
|
+
@stream, @peer, @node = stream, peer, node
|
48
|
+
@status = @id = nil
|
49
|
+
@allowed_actions = [:execute]
|
50
|
+
@execute_action = :execute
|
51
|
+
@stages = []
|
52
|
+
end
|
53
|
+
|
54
|
+
def send(action, form=nil)
|
55
|
+
unless @allowed_actions.include?(action.to_sym)
|
56
|
+
raise "Action #{action} is not allowed (#{@allowed_actions.join(', ')})"
|
57
|
+
end
|
58
|
+
|
59
|
+
attributes = {:xmlns => NS, :node => @node, :action => action}
|
60
|
+
attributes[:sessionid] = @id if @id
|
61
|
+
result = @stream.send_iq(@peer, :set) do |x|
|
62
|
+
x.command(attributes) do
|
63
|
+
x.x(form) if form
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
command_node = result.first('/c:iq/cmd:command', NAMESPACES)
|
68
|
+
actions_node = command_node.all('./cmd:actions', NAMESPACES)
|
69
|
+
form_node = command_node.all('./x:x', NAMESPACES)
|
70
|
+
|
71
|
+
@id = command_node[:sessionid].to_s if command_node[:sessionid]
|
72
|
+
@status = (command_node[:status] || :completed).to_sym
|
73
|
+
@allowed_actions = actions_node.children.collect { |n| n.name.to_sym }
|
74
|
+
@execute_action = (actions_node[:execute] || :next).to_sym
|
75
|
+
|
76
|
+
form = Cancer::XEP_0004::Form.load(form_node)
|
77
|
+
@stages << form
|
78
|
+
|
79
|
+
form
|
80
|
+
end
|
81
|
+
|
82
|
+
def execute!(form=nil)
|
83
|
+
send(@execute_action, form)
|
84
|
+
end
|
85
|
+
|
86
|
+
def next!(form=nil)
|
87
|
+
send(:next, form)
|
88
|
+
end
|
89
|
+
|
90
|
+
def complete!(form=nil)
|
91
|
+
send(:complete, form)
|
92
|
+
end
|
93
|
+
|
94
|
+
def prev!
|
95
|
+
send(:prev)
|
96
|
+
end
|
97
|
+
|
98
|
+
def cancel!
|
99
|
+
send(:cancel)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class LocalSession
|
104
|
+
attr_reader :peer, :node, :id, :current_stage, :stages
|
105
|
+
attr_accessor :next_stage, :iq_id
|
106
|
+
|
107
|
+
def initialize(controller, peer, node, id, initial_stage=:initial)
|
108
|
+
@controller = controller
|
109
|
+
@peer, @node, @id = peer, node, id
|
110
|
+
@current_stage = nil
|
111
|
+
@next_stage = initial_stage.to_sym
|
112
|
+
@stages = {}
|
113
|
+
@iq_id = nil
|
114
|
+
end
|
115
|
+
|
116
|
+
def complete!
|
117
|
+
@controller.send_iq(@peer, :result, @iq_id) do |x|
|
118
|
+
x.command(:xmlns => NS, :node => @node, :sessionid => @id, :status => 'completed') do
|
119
|
+
yield x
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def next!(next_stage, form=nil, options={})
|
125
|
+
options, form = form, nil if Hash === form
|
126
|
+
|
127
|
+
@next_stage = next_stage.to_sym
|
128
|
+
@controller.send_iq(@peer, :result, @iq_id) do |x|
|
129
|
+
x.command(:xmlns => NS, :node => @node, :sessionid => @id, :status => 'executing') do
|
130
|
+
|
131
|
+
x.actions(options[:execute] ? {:execute => options[:execute]} : {}) do
|
132
|
+
(options[:actions] || []).each do |action|
|
133
|
+
x.send(action)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
if form
|
138
|
+
form = case form
|
139
|
+
when Cancer::XEP_0004::Form then form
|
140
|
+
when Cancer::XEP_0004::FormDefinition then form
|
141
|
+
else @controller.form(form)
|
142
|
+
end
|
143
|
+
|
144
|
+
x.x(form, options.merge(:verbose => true))
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def form
|
152
|
+
@stages[@current_stage]
|
153
|
+
end
|
154
|
+
|
155
|
+
def push_stage!(iq_id, form_data)
|
156
|
+
@iq_id = iq_id
|
157
|
+
@stages[@next_stage] = (form_data ? Cancer::XEP_0004::Form.load(form_data) : nil)
|
158
|
+
@current_stage = @next_stage
|
159
|
+
@next_stage = nil
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class Controller < Cancer::Controller
|
164
|
+
|
165
|
+
on { |e| e.xpath('/c:iq[@type="set"]/cmd:command', NAMESPACES) }
|
166
|
+
def handle_command(e)
|
167
|
+
command_node = e.xml.first('/c:iq/cmd:command', NAMESPACES)
|
168
|
+
session_id = (command_node[:sessionid] || rand(1<<100)).to_s
|
169
|
+
session = command_sessions[session_id]
|
170
|
+
|
171
|
+
unless session
|
172
|
+
session = LocalSession.new(self, e.sender, command_node[:node].to_s, session_id)
|
173
|
+
command_sessions[session_id] = session
|
174
|
+
end
|
175
|
+
|
176
|
+
session.push_stage!(e.id, e.xml.first('/c:iq/cmd:command/x:x', NAMESPACES))
|
177
|
+
|
178
|
+
fire! Cancer::XEP_0050::Event, self, session, command_node[:action]
|
179
|
+
end
|
180
|
+
|
181
|
+
def command_sessions
|
182
|
+
@command_sessions ||= {}
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
class Event < Cancer::Events::AbstractEvent
|
188
|
+
attr_reader :session, :action
|
189
|
+
def initialize(controller, session, action)
|
190
|
+
@controller, @session, @action = controller, session, action
|
191
|
+
@action = @action.to_sym if @action
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class EventMatcher < Cancer::Events::Matcher
|
196
|
+
|
197
|
+
def initialize(node, options={})
|
198
|
+
@node = node.to_s
|
199
|
+
@stage = (options.delete(:stage) || :initial).to_sym
|
200
|
+
@action = (options.delete(:action) || :execute).to_sym
|
201
|
+
@default_action = (options.delete(:default_action) || :execute).to_sym
|
202
|
+
end
|
203
|
+
|
204
|
+
def match?(event)
|
205
|
+
(
|
206
|
+
Cancer::XEP_0050::Event === event and
|
207
|
+
matching_node?(event) and
|
208
|
+
matching_stage?(event) and
|
209
|
+
matching_action?(event)
|
210
|
+
)
|
211
|
+
end
|
212
|
+
|
213
|
+
def matching_node?(event)
|
214
|
+
@node ? (@node == event.session.node) : true
|
215
|
+
end
|
216
|
+
|
217
|
+
def matching_stage?(event)
|
218
|
+
@stage ? (@stage == event.session.current_stage) : true
|
219
|
+
end
|
220
|
+
|
221
|
+
def matching_action?(event)
|
222
|
+
@action ? (@action == (event.action || @default_action)) : true
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
module EventDSL
|
228
|
+
|
229
|
+
def command(node, options={})
|
230
|
+
Cancer::XEP_0050::EventMatcher.new(node, options)
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
=begin
|
239
|
+
class Controller
|
240
|
+
|
241
|
+
on { |e| e.command(:config) }
|
242
|
+
def config_initial_stage(e)
|
243
|
+
if ok? current_stage
|
244
|
+
e.session.next!(:ask_details, 'my_form', default_values)
|
245
|
+
else
|
246
|
+
# error
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
on { |e| e.command(:config, :stage => :ask_details) }
|
251
|
+
def config_ask_details_stage(e)
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
=end
|
@@ -0,0 +1,306 @@
|
|
1
|
+
|
2
|
+
require 'ipaddr'
|
3
|
+
require 'digest/sha1'
|
4
|
+
|
5
|
+
module Cancer
|
6
|
+
# SOCKS5 Bytestreams
|
7
|
+
# http://xmpp.org/extensions/xep-0065.html
|
8
|
+
module XEP_0065
|
9
|
+
include Cancer::XEP
|
10
|
+
|
11
|
+
dependency 'core'
|
12
|
+
dependency 'xep-0030'
|
13
|
+
dependency 'xep-0095', :weak => true
|
14
|
+
|
15
|
+
NS = 'http://jabber.org/protocol/bytestreams'
|
16
|
+
|
17
|
+
def self.enhance_stream(stream)
|
18
|
+
stream.extend_stream do
|
19
|
+
include Cancer::XEP_0065::StreamHelpers
|
20
|
+
end
|
21
|
+
stream.install_controller Cancer::XEP_0065::Controller
|
22
|
+
stream.install_event_helper Cancer::XEP_0065::EventDSL
|
23
|
+
if stream.respond_to?(:register_stream_method)
|
24
|
+
stream.register_stream_method(Cancer::XEP_0065::StreamMethod, Cancer::XEP_0065::NS)
|
25
|
+
stream.install_controller Cancer::XEP_0065::SOCKS5Controller
|
26
|
+
end
|
27
|
+
stream.disco.feature NS
|
28
|
+
end
|
29
|
+
|
30
|
+
module StreamHelpers
|
31
|
+
|
32
|
+
def open_socks5_bytestream(to, sid=nil)
|
33
|
+
sid ||= rand(1<<100).to_s
|
34
|
+
|
35
|
+
streamhosts = discover_streamhosts
|
36
|
+
|
37
|
+
result = send_iq(to, :set) do |x|
|
38
|
+
x.query(:xmlns => Cancer::XEP_0065::NS, :sid => sid, :mode => 'tcp') do
|
39
|
+
|
40
|
+
streamhosts.each do |jid, options|
|
41
|
+
x.streamhost(options.merge(:jid => jid))
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
namespaces = { 'c' => CLIENT_NS, 's' => Cancer::XEP_0065::NS }
|
48
|
+
streamhost = result.first("/c:iq/s:query/s:streamhost-used", namespaces)
|
49
|
+
if streamhost
|
50
|
+
streamhost_jid = streamhost[:jid].to_s
|
51
|
+
streamhost = streamhosts[streamhost_jid]
|
52
|
+
|
53
|
+
address_base = "#{sid}#{result[:to]}#{result[:from]}"
|
54
|
+
address = Digest::SHA1.hexdigest(address_base)
|
55
|
+
|
56
|
+
socket = TCPSocket.new(streamhost[:host], streamhost[:port])
|
57
|
+
socket.extend Cancer::XEP_0065::TCPSocketHelper
|
58
|
+
socket.extend Cancer::XEP_0065::SOCKS_5
|
59
|
+
socket.auth
|
60
|
+
socket.connect_domain(address, 0)
|
61
|
+
|
62
|
+
send_iq(streamhost_jid, :set) do |x|
|
63
|
+
x.query(:xmlns => Cancer::XEP_0065::NS, :sid => sid) do
|
64
|
+
x.activate(result[:from].to_s)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
socket.peer = result[:from].to_s
|
69
|
+
socket.stream_id = sid
|
70
|
+
socket.streamhost = streamhost_jid
|
71
|
+
|
72
|
+
if block_given?
|
73
|
+
yield(socket)
|
74
|
+
socket.close
|
75
|
+
else
|
76
|
+
socket
|
77
|
+
end
|
78
|
+
else
|
79
|
+
return false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def discover_streamhosts(server_jid=nil)
|
84
|
+
@streamhosts ||= {}
|
85
|
+
server_jid ||= options[:jid].server_jid
|
86
|
+
@streamhosts[server_jid.to_s] ||= begin
|
87
|
+
discover_socks5_proxies(server_jid).inject({}) do |streamhosts, proxy|
|
88
|
+
streamhosts.merge discover_address_for_socks5_proxy(proxy)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def discover_socks5_proxies(server_jid)
|
96
|
+
proxies = []
|
97
|
+
|
98
|
+
disco_items_for(server_jid).each do |item|
|
99
|
+
info = disco_info_for(item['jid'].to_s)
|
100
|
+
has_identity = info.identities.any? { |identity| identity[:category] == 'proxy' }
|
101
|
+
has_feature = info.features.include?(Cancer::XEP_0065::NS)
|
102
|
+
proxies.push(item['jid'].to_s) if has_identity and has_feature
|
103
|
+
end
|
104
|
+
|
105
|
+
proxies
|
106
|
+
end
|
107
|
+
|
108
|
+
def discover_address_for_socks5_proxy(jid, sid=nil)
|
109
|
+
attributes = { :xmlns => Cancer::XEP_0065::NS }
|
110
|
+
attributes[:sid] = sid if sid
|
111
|
+
result = send_iq(jid) do |x|
|
112
|
+
x.query(attributes)
|
113
|
+
end
|
114
|
+
|
115
|
+
streamhosts = {}
|
116
|
+
|
117
|
+
namespaces = { 'c' => CLIENT_NS, 's' => Cancer::XEP_0065::NS }
|
118
|
+
result.all('/c:iq/s:query/s:streamhost', namespaces) do |streamhost|
|
119
|
+
streamhosts[streamhost[:jid].to_s] = {
|
120
|
+
:host => streamhost[:host],
|
121
|
+
:port => (streamhost[:port].to_i rescue 1080),
|
122
|
+
:zeroconf => streamhost[:zeroconf],
|
123
|
+
}.clean
|
124
|
+
end
|
125
|
+
|
126
|
+
streamhosts
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
class Controller < Cancer::Controller
|
132
|
+
|
133
|
+
NAMESPACES = { 'c' => CLIENT_NS, 'bs' => NS }
|
134
|
+
|
135
|
+
on { |e| e.xpath('/c:iq/bs:query/bs:streamhost', NAMESPACES) }
|
136
|
+
def open_bytestream(e)
|
137
|
+
query = e.xml.first('/c:iq/bs:query', NAMESPACES)
|
138
|
+
|
139
|
+
streamhosts = {}
|
140
|
+
e.xml.all('/c:iq/bs:query/bs:streamhost', NAMESPACES) do |streamhost|
|
141
|
+
streamhosts[streamhost[:jid].to_s] = {
|
142
|
+
:host => streamhost[:host],
|
143
|
+
:port => (streamhost[:port].to_i rescue 1080),
|
144
|
+
:zeroconf => streamhost[:zeroconf],
|
145
|
+
}.clean
|
146
|
+
end
|
147
|
+
|
148
|
+
fire!(Event, self,
|
149
|
+
:streamhosts => streamhosts,
|
150
|
+
:iq_id => e.id,
|
151
|
+
:peer => e.sender,
|
152
|
+
:self => e.receiver,
|
153
|
+
:mode => query[:mode],
|
154
|
+
:sid => query[:sid]
|
155
|
+
)
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
class Event < Cancer::Events::AbstractEvent
|
161
|
+
attr_reader :bytestream, :options
|
162
|
+
def initialize(controller, options)
|
163
|
+
@controller, @bytestream, @options = controller, nil, options
|
164
|
+
end
|
165
|
+
|
166
|
+
def accept!(&proc)
|
167
|
+
if block_given?
|
168
|
+
@controller.enqueue(self, proc) do |e, proc|
|
169
|
+
bs = e.connect_with_socks5_proxy_and_handle
|
170
|
+
proc.call(bs) if bs
|
171
|
+
end
|
172
|
+
else
|
173
|
+
bs = self.connect_with_socks5_proxy_and_handle
|
174
|
+
proc.call(bs) if bs
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def reject!
|
179
|
+
@controller.send_iq(@options[:peer], :error, @options[:iq_id]) do |x|
|
180
|
+
x.error(:code => 406, :type => 'auth') do
|
181
|
+
x.send('not-acceptable', :xmlns => 'urn:ietf:params:xml:ns:xmpp-stanzas')
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def connect_with_socks5_proxy_and_handle
|
187
|
+
@bytestream = connect_with_socks5_proxy
|
188
|
+
|
189
|
+
if @bytestream
|
190
|
+
@controller.send_iq(@options[:peer], :result, @options[:iq_id]) do |x|
|
191
|
+
x.query(:xmlns => Cancer::XEP_0065::NS, :sid => @options[:sid]) do
|
192
|
+
x.send('streamhost-used', :jid => @bytestream.streamhost)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
else
|
196
|
+
@controller.send_iq(@options[:peer], :error, @options[:iq_id]) do |x|
|
197
|
+
x.error(:code => 404, :type => 'cancel') do
|
198
|
+
x.send('item-not-found', :xmlns => 'urn:ietf:params:xml:ns:xmpp-stanzas')
|
199
|
+
end
|
200
|
+
end
|
201
|
+
return false
|
202
|
+
end
|
203
|
+
|
204
|
+
return @bytestream
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def connect_with_socks5_proxy
|
210
|
+
@options[:streamhosts].each do |jid, streamhost|
|
211
|
+
|
212
|
+
begin
|
213
|
+
|
214
|
+
address_base = "#{@options[:sid]}#{@options[:peer]}#{@options[:self]}"
|
215
|
+
address = Digest::SHA1.hexdigest(address_base)
|
216
|
+
|
217
|
+
socket = TCPSocket.new(streamhost[:host].to_s, streamhost[:port])
|
218
|
+
socket.extend Cancer::XEP_0065::TCPSocketHelper
|
219
|
+
socket.extend Cancer::XEP_0065::SOCKS_5
|
220
|
+
socket.auth
|
221
|
+
socket.connect_domain(address, 0)
|
222
|
+
|
223
|
+
socket.peer = @options[:from].to_s
|
224
|
+
socket.stream_id = @options[:sid]
|
225
|
+
socket.streamhost = jid.to_s
|
226
|
+
|
227
|
+
return socket
|
228
|
+
rescue IOError => e
|
229
|
+
Cancer.logger.debug e
|
230
|
+
end
|
231
|
+
end
|
232
|
+
return nil
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
class EventMatcher < Cancer::Events::Matcher
|
238
|
+
def match?(event)
|
239
|
+
Cancer::XEP_0065::Event === event
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
module EventDSL
|
244
|
+
def socks5_bytestream
|
245
|
+
Cancer::XEP_0065::EventMatcher.new
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
module TCPSocketHelper
|
250
|
+
attr_accessor :peer, :stream_id, :streamhost
|
251
|
+
end
|
252
|
+
|
253
|
+
module SOCKS_5
|
254
|
+
class Error < IOError ; end
|
255
|
+
def auth
|
256
|
+
write("\x05\x01\x00")
|
257
|
+
buf = read(2)
|
258
|
+
if buf.nil? or buf != "\x05\x00"
|
259
|
+
close
|
260
|
+
raise SOCKS_5::Error.new("Invalid SOCKS5 authentication: #{buf.inspect}")
|
261
|
+
end
|
262
|
+
|
263
|
+
self
|
264
|
+
end
|
265
|
+
|
266
|
+
def connect_domain(domain, port)
|
267
|
+
buf_out = "\x05\x01\x00\x03#{domain.size.chr}#{domain}#{[port].pack("n")}"
|
268
|
+
write(buf_out)
|
269
|
+
buf = read(4)
|
270
|
+
if buf.nil? or buf[0..1] != "\005\000"
|
271
|
+
close
|
272
|
+
raise SOCKS_5::Error.new("Invalid SOCKS5 connect: #{buf_out.inspect} => #{buf.inspect}")
|
273
|
+
end
|
274
|
+
|
275
|
+
case buf.respond_to?(:bytes) ? buf.bytes.to_a[3] : buf[3]
|
276
|
+
when 1 then read(6) # IPv4 addr
|
277
|
+
when 3 then read(3 + domain.size) # Domain
|
278
|
+
when 4 then read(18) # IPv6 addr
|
279
|
+
else
|
280
|
+
close
|
281
|
+
raise SOCKS_5::Error.new("Invalid SOCKS5 address type #{buf[3].to_s}")
|
282
|
+
end
|
283
|
+
|
284
|
+
self
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
class StreamMethod < Cancer::XEP_0095::StreamMethod
|
289
|
+
def handle(to, id, &proc)
|
290
|
+
@stream.open_socks5_bytestream(to, id, &proc)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
class SOCKS5Controller < Cancer::Controller
|
295
|
+
on { |e| e.socks5_bytestream }
|
296
|
+
def catch_socks5(e)
|
297
|
+
controller = self.controllers_by_name['Cancer::XEP_0095::Controller']
|
298
|
+
si_event = controller.pending_streams[e.options[:sid].to_s]
|
299
|
+
if si_event
|
300
|
+
si_event.caught_stream!(e)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|
306
|
+
end
|