Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Socks5Proxy.cs
1using System;
3using System.Text;
4using System.Threading.Tasks;
5using System.Xml;
6using Waher.Content;
8using Waher.Events;
12
14{
19 {
23 public const string Namespace = "http://jabber.org/protocol/bytestreams";
24
25 private readonly Dictionary<string, Socks5Client> streams = new Dictionary<string, Socks5Client>();
26 private readonly IEndToEndEncryption e2e;
27 private bool hasProxy = false;
28 private string jid = null;
29 private string host = null;
30 private int port = 0;
31
37 : this(Client, null)
38 {
39 }
40
47 : base(Client)
48 {
49 this.e2e = E2E;
50
51 this.client.RegisterIqSetHandler("query", Namespace, this.QueryHandler, true);
52 }
53
55 public override void Dispose()
56 {
57 this.client.UnregisterIqSetHandler("query", Namespace, this.QueryHandler, true);
58 base.Dispose();
59 }
60
64 public override string[] Extensions => new string[] { "XEP-0065" };
65
69 public bool HasProxy => this.hasProxy;
70
74 public string JID => this.jid;
75
79 public string Host => this.host;
80
84 public int Port => this.port;
85
90 public Task StartSearch(EventHandlerAsync Callback)
91 {
92 this.hasProxy = false;
93 this.jid = null;
94 this.host = null;
95 this.port = 0;
96
97 return this.client.SendServiceItemsDiscoveryRequest(this.client.Domain, this.SearchResponse, Callback);
98 }
99
100 private Task SearchResponse(object Sender, ServiceItemsDiscoveryEventArgs e)
101 {
103 SearchState State = new SearchState(this, e.Items, Callback);
104 return State.DoQuery();
105 }
106
107 private class SearchState
108 {
109 public Socks5Proxy Proxy;
110 public EventHandlerAsync Callback;
111 public string Component = string.Empty;
112 public Item[] Items;
113 public int Pos = 0;
114 public int NrItems;
115
116 public SearchState(Socks5Proxy Proxy, Item[] Items, EventHandlerAsync Callback)
117 {
118 this.Proxy = Proxy;
119 this.Callback = Callback;
120 this.Items = Items;
121 this.NrItems = Items.Length;
122 }
123
124 public Task Advance()
125 {
126 this.Pos++;
127 return this.DoQuery();
128 }
129
130 public Task DoQuery()
131 {
132 if (this.Pos < this.NrItems)
133 return this.Proxy.client.SendServiceDiscoveryRequest(this.Items[this.Pos].JID, this.ItemDiscoveryResponse, null);
134 else
135 return this.SearchDone();
136 }
137
138 private async Task ItemDiscoveryResponse(object Sender, ServiceDiscoveryEventArgs e2)
139 {
140 if (e2.Features.ContainsKey(Namespace))
141 {
142 this.Component = this.Items[this.Pos].JID;
143 await this.Proxy.client.SendIqGet(this.Component, "<query xmlns=\"" + Namespace + "\"/>", this.SocksQueryResponse, null);
144 }
145 else
146 await this.Advance();
147 }
148
149 private Task SocksQueryResponse(object Sender, IqResultEventArgs e3)
150 {
151 if (e3.Ok)
152 {
153 XmlElement E = e3.FirstElement;
154
155 if (E.LocalName == "query" && E.NamespaceURI == Namespace)
156 {
157 E = (XmlElement)E.FirstChild;
158
159 if (E.LocalName == "streamhost" && E.NamespaceURI == Namespace)
160 {
161 this.Proxy.jid = XML.Attribute(E, "jid");
162 this.Proxy.port = XML.Attribute(E, "port", 0);
163 this.Proxy.host = XML.Attribute(E, "host");
164 this.Proxy.hasProxy = !string.IsNullOrEmpty(this.Proxy.jid) &&
165 !string.IsNullOrEmpty(this.Proxy.host) &&
166 this.Proxy.port > 0;
167
168 if (this.Proxy.hasProxy)
169 return this.SearchDone();
170 else
171 return this.Advance();
172 }
173 else
174 return this.Advance();
175 }
176 else
177 return this.Advance();
178 }
179 else
180 return this.Advance();
181 }
182
183 private Task SearchDone()
184 {
185 return this.Callback.Raise(this.Proxy, EventArgs.Empty);
186 }
187 }
188
196 public void Use(string Host, int Port, string JID)
197 {
198 this.host = Host;
199 this.port = Port;
200 this.jid = JID;
201 this.hasProxy = !string.IsNullOrEmpty(this.jid) && !string.IsNullOrEmpty(this.host) && this.port > 0;
202 }
203
210 public Task InitiateSession(string DestinationJid, EventHandlerAsync<StreamEventArgs> Callback, object State)
211 {
212 return this.InitiateSession(DestinationJid, null, true, Callback, State);
213 }
214
222 public Task InitiateSession(string DestinationJid, string StreamId, EventHandlerAsync<StreamEventArgs> Callback, object State)
223 {
224 return this.InitiateSession(DestinationJid, StreamId, true, Callback, State);
225 }
226
236 public async Task InitiateSession(string DestinationJid, string StreamId, bool InstantiateSocks5Client,
237 EventHandlerAsync<StreamEventArgs> Callback, object State)
238 {
239 if (!this.hasProxy)
240 {
241 await this.Callback(Callback, State, false, null, null);
242 return;
243 }
244
245 lock (this.streams)
246 {
247 if (string.IsNullOrEmpty(StreamId))
248 {
249 do
250 {
251 StreamId = Guid.NewGuid().ToString().Replace("-", string.Empty);
252 }
253 while (this.streams.ContainsKey(StreamId));
254 }
255 else if (this.streams.ContainsKey(StreamId))
256 StreamId = null;
257
258 if (!(StreamId is null))
259 this.streams[StreamId] = null;
260 }
261
262 if (StreamId is null)
263 {
264 await this.Callback(Callback, State, false, null, null);
265 return;
266 }
267
268 StringBuilder Xml = new StringBuilder();
269
270 Xml.Append("<query xmlns=\"");
271 Xml.Append(Namespace);
272 Xml.Append("\" sid=\"");
273 Xml.Append(StreamId);
274 Xml.Append("\"><streamhost host=\"");
275 Xml.Append(this.host);
276 Xml.Append("\" jid=\"");
277 Xml.Append(this.jid);
278 Xml.Append("\" port=\"");
279 Xml.Append(this.port.ToString());
280 Xml.Append("\"/></query>");
281
282 InitiationRec Rec = new InitiationRec()
283 {
284 destinationJid = DestinationJid,
285 streamId = StreamId,
286 callback = Callback,
287 state = State,
288 proxy = this,
289 instantiateSocks5Client = InstantiateSocks5Client
290 };
291
292 if (!(this.e2e is null))
293 await this.e2e.SendIqSet(this.client, E2ETransmission.NormalIfNotE2E, DestinationJid, Xml.ToString(), this.InitiationResponse, Rec);
294 else
295 await this.client.SendIqSet(DestinationJid, Xml.ToString(), this.InitiationResponse, Rec);
296 }
297
298 private class InitiationRec
299 {
300 public string destinationJid;
301 public string streamId;
302 public object state;
303 public bool instantiateSocks5Client;
304 public EventHandlerAsync<StreamEventArgs> callback;
305 public Socks5Client stream = null;
306 public Socks5Proxy proxy;
307
308 internal async Task StateChanged(object Sender, EventArgs e)
309 {
310 switch (this.stream.State)
311 {
312 case Socks5State.Authenticated:
313 await this.stream.CONNECT(this.streamId, this.proxy.client.FullJID, this.destinationJid);
314 break;
315
316 case Socks5State.Connected:
317 StringBuilder Xml = new StringBuilder();
318
319 Xml.Append("<query xmlns=\"");
320 Xml.Append(Namespace);
321 Xml.Append("\" sid=\"");
322 Xml.Append(this.streamId);
323 Xml.Append("\"><activate>");
324 Xml.Append(this.destinationJid);
325 Xml.Append("</activate></query>");
326
327 if (!(this.proxy.e2e is null))
328 {
329 await this.proxy.e2e.SendIqSet(this.proxy.client, E2ETransmission.NormalIfNotE2E, this.proxy.jid, Xml.ToString(),
330 this.proxy.ActivationResponse, this);
331 }
332 else
333 await this.proxy.client.SendIqSet(this.proxy.jid, Xml.ToString(), this.proxy.ActivationResponse, this);
334 break;
335
336 case Socks5State.Error:
337 case Socks5State.Offline:
338 if (!(this.stream is null))
339 await this.stream.DisposeAsync();
340
341 await this.proxy.Callback(this.callback, this.state, false, null, this.streamId);
342 this.callback = null;
343 break;
344 }
345 }
346 }
347
348 private async Task InitiationResponse(object Sender, IqResultEventArgs e)
349 {
350 InitiationRec Rec = (InitiationRec)e.State;
351
352 if (e.Ok)
353 {
354 XmlElement E = e.FirstElement;
355
356 if (!(E is null) && E.LocalName == "query" && E.NamespaceURI == Namespace && XML.Attribute(E, "sid") == Rec.streamId)
357 {
358 XmlElement E2;
359 string StreamHostUsed = null;
360
361 foreach (XmlNode N in E.ChildNodes)
362 {
363 E2 = N as XmlElement;
364 if (E2.LocalName == "streamhost-used" && E2.NamespaceURI == Namespace)
365 {
366 StreamHostUsed = XML.Attribute(E2, "jid");
367 break;
368 }
369 }
370
371 if (!string.IsNullOrEmpty(StreamHostUsed) && StreamHostUsed == this.host)
372 {
373 if (Rec.instantiateSocks5Client)
374 {
375 Rec.stream = new Socks5Client(this.host, this.port, this.jid);
376 Rec.stream.OnStateChange += Rec.StateChanged;
377
378 lock (this.streams)
379 {
380 this.streams[Rec.streamId] = Rec.stream;
381 }
382 }
383 else
384 await this.Callback(Rec.callback, Rec.state, true, null, Rec.streamId);
385 }
386 else
387 await this.Callback(Rec.callback, Rec.state, false, null, Rec.streamId);
388 }
389 else
390 await this.Callback(Rec.callback, Rec.state, false, null, Rec.streamId);
391 }
392 else
393 await this.Callback(Rec.callback, Rec.state, false, null, Rec.streamId);
394 }
395
396 private async Task ActivationResponse(object Sender, IqResultEventArgs e)
397 {
398 InitiationRec Rec = (InitiationRec)e.State;
399
400 if (e.Ok)
401 await this.Callback(Rec.callback, Rec.state, true, Rec.stream, Rec.streamId);
402 else
403 {
404 await Rec.stream.DisposeAsync();
405 await this.Callback(Rec.callback, Rec.state, false, null, Rec.streamId);
406 }
407
408 Rec.callback = null;
409 }
410
411 private async Task Callback(EventHandlerAsync<StreamEventArgs> Callback, object State, bool Ok, Socks5Client Stream, string StreamId)
412 {
413 if (!Ok && !string.IsNullOrEmpty(StreamId))
414 {
415 lock (this.streams)
416 {
417 this.streams.Remove(StreamId);
418 }
419 }
420
421 await Callback.Raise(this, new StreamEventArgs(Ok, Stream, State));
422 }
423
424 private async Task QueryHandler(object Sender, IqEventArgs e)
425 {
426 string StreamId = XML.Attribute(e.Query, "sid");
427 XmlElement E;
428
429 if (string.IsNullOrEmpty(StreamId) || StreamId != Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(StreamId)))
430 throw new NotAcceptableException("Invalid Stream ID.", e.IQ);
431
432 string Host = null;
433 string JID = null;
434 int Port = 0;
435
436 foreach (XmlNode N in e.Query.ChildNodes)
437 {
438 E = N as XmlElement;
439 if (E is null)
440 continue;
441
442 if (E.LocalName == "streamhost" && E.NamespaceURI == Namespace)
443 {
444 Host = XML.Attribute(E, "host");
445 JID = XML.Attribute(E, "jid");
446 Port = XML.Attribute(E, "port", 0);
447
448 break;
449 }
450 }
451
452 if (string.IsNullOrEmpty(JID) || string.IsNullOrEmpty(Host) || Port <= 0 || Port >= 0x10000)
453 throw new BadRequestException("Invalid parameters.", e.IQ);
454
455 ValidateStreamEventArgs e2 = new ValidateStreamEventArgs(this.client, e, StreamId);
456 await this.OnOpen.Raise(this, e2, false);
457
458 if (e2.DataCallback is null || e2.CloseCallback is null)
459 throw new NotAcceptableException("Stream not expected.", e.IQ);
460
461 Socks5Client Client;
462
463 lock (this.streams)
464 {
465 if (this.streams.ContainsKey(StreamId))
466 throw new ConflictException("Stream already exists.", e.IQ);
467
468 Client = new Socks5Client(Host, Port, JID)
469 {
470 CallbackState = e2.State
471 };
472
473 this.streams[StreamId] = Client;
474 }
475
476 Client.Tag = new Socks5QueryState()
477 {
478 streamId = StreamId,
479 eventargs = e,
480 eventargs2 = e2
481 };
482
483 Client.OnDataReceived += e2.DataCallback;
484 Client.OnStateChange += this.ClientStateChanged;
485 }
486
487 private class Socks5QueryState
488 {
489 public string streamId;
490 public IqEventArgs eventargs;
491 public ValidateStreamEventArgs eventargs2;
492 }
493
494 private async Task ClientStateChanged(object Sender, EventArgs e3)
495 {
496 Socks5Client Client = (Socks5Client)Sender;
497 Socks5QueryState State = (Socks5QueryState)Client.Tag;
498
499 switch (Client.State)
500 {
501 case Socks5State.Authenticated:
502 await Client.CONNECT(State.streamId, State.eventargs.From, this.client.FullJID);
503 break;
504
505 case Socks5State.Connected:
506 StringBuilder Xml = new StringBuilder();
507
508 Xml.Append("<query xmlns=\"");
509 Xml.Append(Namespace);
510 Xml.Append("\" sid=\"");
511 Xml.Append(State.streamId);
512 Xml.Append("\"><streamhost-used jid=\"");
513 Xml.Append(Client.Host);
514 Xml.Append("\"/></query>");
515
516 await State.eventargs.IqResult(Xml.ToString());
517 break;
518
519 case Socks5State.Error:
520 case Socks5State.Offline:
521 if (Client.State == Socks5State.Error)
522 await State.eventargs.IqError(new BadRequestException("Unable to establish a SOCKS5 connection.", State.eventargs.IQ));
523
524 await Client.DisposeAsync();
525
526 lock (this.streams)
527 {
528 this.streams.Remove(State.streamId);
529 }
530
531 await State.eventargs2.CloseCallback.Raise(this, new StreamEventArgs(false, Client, State.eventargs2.State));
532 break;
533 }
534 }
535
540 public event EventHandlerAsync<ValidateStreamEventArgs> OnOpen = null;
541
542 }
543}
Helps with common XML-related tasks.
Definition: XML.cs:21
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:1062
Event arguments for IQ queries.
Definition: IqEventArgs.cs:12
XmlElement Query
Query element, if found, null otherwise.
Definition: IqEventArgs.cs:119
Event arguments for responses to IQ queries.
bool Ok
If the response is an OK result response (true), or an error response (false).
object State
State object passed to the original request.
XmlElement FirstElement
First child element of the Response element.
Client used for SOCKS5 communication.
Definition: Socks5Client.cs:61
async Task DisposeAsync()
IDisposable.Dispose
Task CONNECT(IPAddress DestinationAddress, int Port)
Connects to the target.
Class managing a SOCKS5 proxy associated with the current XMPP server.
Definition: Socks5Proxy.cs:19
bool HasProxy
If a SOCKS5 proxy has been detected.
Definition: Socks5Proxy.cs:69
Task InitiateSession(string DestinationJid, string StreamId, EventHandlerAsync< StreamEventArgs > Callback, object State)
Initiates a mediated SOCKS5 session with another.
Definition: Socks5Proxy.cs:222
Socks5Proxy(XmppClient Client)
Class managing a SOCKS5 proxy associated with the current XMPP server.
Definition: Socks5Proxy.cs:36
Task StartSearch(EventHandlerAsync Callback)
Starts the search of SOCKS5 proxies.
Definition: Socks5Proxy.cs:90
override void Dispose()
Disposes of the extension.
Definition: Socks5Proxy.cs:55
override string[] Extensions
Implemented extensions.
Definition: Socks5Proxy.cs:64
Task InitiateSession(string DestinationJid, EventHandlerAsync< StreamEventArgs > Callback, object State)
Initiates a mediated SOCKS5 session with another.
Definition: Socks5Proxy.cs:210
int Port
Port number of SOCKS5 proxy.
Definition: Socks5Proxy.cs:84
void Use(string Host, int Port, string JID)
Sets the SOCKS5 proxy to use. This method can be called, if searching for a SOCKS5 proxy is not desir...
Definition: Socks5Proxy.cs:196
const string Namespace
http://jabber.org/protocol/bytestreams
Definition: Socks5Proxy.cs:23
string Host
Host name or IP address of SOCKS5 proxy.
Definition: Socks5Proxy.cs:79
async Task InitiateSession(string DestinationJid, string StreamId, bool InstantiateSocks5Client, EventHandlerAsync< StreamEventArgs > Callback, object State)
Initiates a mediated SOCKS5 session with another.
Definition: Socks5Proxy.cs:236
Socks5Proxy(XmppClient Client, IEndToEndEncryption E2E)
Class managing a SOCKS5 proxy associated with the current XMPP server.
Definition: Socks5Proxy.cs:46
EventHandlerAsync< ValidateStreamEventArgs > OnOpen
Event raised when a remote entity tries to open a SOCKS5 bytestream for transmission of data to/from ...
Definition: Socks5Proxy.cs:540
Contains information about an item of an entity.
Definition: Item.cs:11
The sender has sent a stanza containing XML that does not conform to the appropriate schema or that c...
Access cannot be granted because an existing resource exists with the same name or address; the assoc...
The recipient or server understands the request but cannot process it because the request does not me...
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:58
XmppState State
Current state of connection.
Definition: XmppClient.cs:985
string Host
Host or IP address of XMPP server.
Definition: XmppClient.cs:868
async Task DisposeAsync()
Closes the connection and disposes of all resources.
Definition: XmppClient.cs:1145
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers an IQ-Set handler.
Definition: XmppClient.cs:2748
string Domain
Current Domain.
Definition: XmppClient.cs:3492
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters an IQ-Set handler.
Definition: XmppClient.cs:2815
Task SendServiceItemsDiscoveryRequest(string To, EventHandlerAsync< ServiceItemsDiscoveryEventArgs > Callback, object State)
Sends a service items discovery request
Definition: XmppClient.cs:6118
Task< uint > SendIqSet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Set request.
Definition: XmppClient.cs:3646
Base class for XMPP Extensions.
XmppClient client
XMPP Client used by the extension.
XmppClient Client
XMPP Client.
Interface for objects that contain a reference to a host.
End-to-end encryption interface.
Task< uint > SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Set request.
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.
Socks5State
SOCKS5 connection state.
Definition: Socks5Client.cs:18
E2ETransmission
End-to-end encryption mode.