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;
2using System.Collections.Generic;
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 base.Dispose();
58
59 this.client.UnregisterIqSetHandler("query", Namespace, this.QueryHandler, true);
60 }
61
65 public override string[] Extensions => new string[] { "XEP-0065" };
66
70 public bool HasProxy => this.hasProxy;
71
75 public string JID => this.jid;
76
80 public string Host => this.host;
81
85 public int Port => this.port;
86
91 public Task StartSearch(EventHandlerAsync Callback)
92 {
93 this.hasProxy = false;
94 this.jid = null;
95 this.host = null;
96 this.port = 0;
97
98 return this.client.SendServiceItemsDiscoveryRequest(this.client.Domain, this.SearchResponse, Callback);
99 }
100
101 private Task SearchResponse(object Sender, ServiceItemsDiscoveryEventArgs e)
102 {
104 SearchState State = new SearchState(this, e.Items, Callback);
105 return State.DoQuery();
106 }
107
108 private class SearchState
109 {
110 public Socks5Proxy Proxy;
111 public EventHandlerAsync Callback;
112 public string Component = string.Empty;
113 public Item[] Items;
114 public int Pos = 0;
115 public int NrItems;
116
117 public SearchState(Socks5Proxy Proxy, Item[] Items, EventHandlerAsync Callback)
118 {
119 this.Proxy = Proxy;
120 this.Callback = Callback;
121 this.Items = Items;
122 this.NrItems = Items.Length;
123 }
124
125 public Task Advance()
126 {
127 this.Pos++;
128 return this.DoQuery();
129 }
130
131 public Task DoQuery()
132 {
133 if (this.Pos < this.NrItems)
134 return this.Proxy.client.SendServiceDiscoveryRequest(this.Items[this.Pos].JID, this.ItemDiscoveryResponse, null);
135 else
136 return this.SearchDone();
137 }
138
139 private async Task ItemDiscoveryResponse(object Sender, ServiceDiscoveryEventArgs e2)
140 {
141 if (e2.Features.ContainsKey(Namespace))
142 {
143 this.Component = this.Items[this.Pos].JID;
144 await this.Proxy.client.SendIqGet(this.Component, "<query xmlns=\"" + Namespace + "\"/>", this.SocksQueryResponse, null);
145 }
146 else
147 await this.Advance();
148 }
149
150 private Task SocksQueryResponse(object Sender, IqResultEventArgs e3)
151 {
152 if (e3.Ok)
153 {
154 XmlElement E = e3.FirstElement;
155
156 if (E.LocalName == "query" && E.NamespaceURI == Namespace)
157 {
158 E = (XmlElement)E.FirstChild;
159
160 if (E.LocalName == "streamhost" && E.NamespaceURI == Namespace)
161 {
162 this.Proxy.jid = XML.Attribute(E, "jid");
163 this.Proxy.port = XML.Attribute(E, "port", 0);
164 this.Proxy.host = XML.Attribute(E, "host");
165 this.Proxy.hasProxy = !string.IsNullOrEmpty(this.Proxy.jid) &&
166 !string.IsNullOrEmpty(this.Proxy.host) &&
167 this.Proxy.port > 0;
168
169 if (this.Proxy.hasProxy)
170 return this.SearchDone();
171 else
172 return this.Advance();
173 }
174 else
175 return this.Advance();
176 }
177 else
178 return this.Advance();
179 }
180 else
181 return this.Advance();
182 }
183
184 private Task SearchDone()
185 {
186 return this.Callback.Raise(this.Proxy, EventArgs.Empty);
187 }
188 }
189
197 public void Use(string Host, int Port, string JID)
198 {
199 this.host = Host;
200 this.port = Port;
201 this.jid = JID;
202 this.hasProxy = !string.IsNullOrEmpty(this.jid) && !string.IsNullOrEmpty(this.host) && this.port > 0;
203 }
204
211 public Task InitiateSession(string DestinationJid, EventHandlerAsync<StreamEventArgs> Callback, object State)
212 {
213 return this.InitiateSession(DestinationJid, null, true, Callback, State);
214 }
215
223 public Task InitiateSession(string DestinationJid, string StreamId, EventHandlerAsync<StreamEventArgs> Callback, object State)
224 {
225 return this.InitiateSession(DestinationJid, StreamId, true, Callback, State);
226 }
227
237 public async Task InitiateSession(string DestinationJid, string StreamId, bool InstantiateSocks5Client,
238 EventHandlerAsync<StreamEventArgs> Callback, object State)
239 {
240 if (!this.hasProxy)
241 {
242 await this.Callback(Callback, State, false, null, null);
243 return;
244 }
245
246 lock (this.streams)
247 {
248 if (string.IsNullOrEmpty(StreamId))
249 {
250 do
251 {
252 StreamId = Guid.NewGuid().ToString().Replace("-", string.Empty);
253 }
254 while (this.streams.ContainsKey(StreamId));
255 }
256 else if (this.streams.ContainsKey(StreamId))
257 StreamId = null;
258
259 if (!(StreamId is null))
260 this.streams[StreamId] = null;
261 }
262
263 if (StreamId is null)
264 {
265 await this.Callback(Callback, State, false, null, null);
266 return;
267 }
268
269 StringBuilder Xml = new StringBuilder();
270
271 Xml.Append("<query xmlns=\"");
272 Xml.Append(Namespace);
273 Xml.Append("\" sid=\"");
274 Xml.Append(StreamId);
275 Xml.Append("\"><streamhost host=\"");
276 Xml.Append(this.host);
277 Xml.Append("\" jid=\"");
278 Xml.Append(this.jid);
279 Xml.Append("\" port=\"");
280 Xml.Append(this.port.ToString());
281 Xml.Append("\"/></query>");
282
283 InitiationRec Rec = new InitiationRec()
284 {
285 destinationJid = DestinationJid,
286 streamId = StreamId,
287 callback = Callback,
288 state = State,
289 proxy = this,
290 instantiateSocks5Client = InstantiateSocks5Client
291 };
292
293 if (!(this.e2e is null))
294 await this.e2e.SendIqSet(this.client, E2ETransmission.NormalIfNotE2E, DestinationJid, Xml.ToString(), this.InitiationResponse, Rec);
295 else
296 await this.client.SendIqSet(DestinationJid, Xml.ToString(), this.InitiationResponse, Rec);
297 }
298
299 private class InitiationRec
300 {
301 public string destinationJid;
302 public string streamId;
303 public object state;
304 public bool instantiateSocks5Client;
305 public EventHandlerAsync<StreamEventArgs> callback;
306 public Socks5Client stream = null;
307 public Socks5Proxy proxy;
308
309 internal async Task StateChanged(object Sender, EventArgs e)
310 {
311 switch (this.stream.State)
312 {
313 case Socks5State.Authenticated:
314 await this.stream.CONNECT(this.streamId, this.proxy.client.FullJID, this.destinationJid);
315 break;
316
317 case Socks5State.Connected:
318 StringBuilder Xml = new StringBuilder();
319
320 Xml.Append("<query xmlns=\"");
321 Xml.Append(Namespace);
322 Xml.Append("\" sid=\"");
323 Xml.Append(this.streamId);
324 Xml.Append("\"><activate>");
325 Xml.Append(this.destinationJid);
326 Xml.Append("</activate></query>");
327
328 if (!(this.proxy.e2e is null))
329 {
330 await this.proxy.e2e.SendIqSet(this.proxy.client, E2ETransmission.NormalIfNotE2E, this.proxy.jid, Xml.ToString(),
331 this.proxy.ActivationResponse, this);
332 }
333 else
334 await this.proxy.client.SendIqSet(this.proxy.jid, Xml.ToString(), this.proxy.ActivationResponse, this);
335 break;
336
337 case Socks5State.Error:
338 case Socks5State.Offline:
339 this.stream?.Dispose();
340 await this.proxy.Callback(this.callback, this.state, false, null, this.streamId);
341 this.callback = null;
342 break;
343 }
344 }
345 }
346
347 private async Task InitiationResponse(object Sender, IqResultEventArgs e)
348 {
349 InitiationRec Rec = (InitiationRec)e.State;
350
351 if (e.Ok)
352 {
353 XmlElement E = e.FirstElement;
354
355 if (!(E is null) && E.LocalName == "query" && E.NamespaceURI == Namespace && XML.Attribute(E, "sid") == Rec.streamId)
356 {
357 XmlElement E2;
358 string StreamHostUsed = null;
359
360 foreach (XmlNode N in E.ChildNodes)
361 {
362 E2 = N as XmlElement;
363 if (E2.LocalName == "streamhost-used" && E2.NamespaceURI == Namespace)
364 {
365 StreamHostUsed = XML.Attribute(E2, "jid");
366 break;
367 }
368 }
369
370 if (!string.IsNullOrEmpty(StreamHostUsed) && StreamHostUsed == this.host)
371 {
372 if (Rec.instantiateSocks5Client)
373 {
374 Rec.stream = new Socks5Client(this.host, this.port, this.jid);
375 Rec.stream.OnStateChange += Rec.StateChanged;
376
377 lock (this.streams)
378 {
379 this.streams[Rec.streamId] = Rec.stream;
380 }
381 }
382 else
383 await this.Callback(Rec.callback, Rec.state, true, null, Rec.streamId);
384 }
385 else
386 await this.Callback(Rec.callback, Rec.state, false, null, Rec.streamId);
387 }
388 else
389 await this.Callback(Rec.callback, Rec.state, false, null, Rec.streamId);
390 }
391 else
392 await this.Callback(Rec.callback, Rec.state, false, null, Rec.streamId);
393 }
394
395 private async Task ActivationResponse(object Sender, IqResultEventArgs e)
396 {
397 InitiationRec Rec = (InitiationRec)e.State;
398
399 if (e.Ok)
400 await this.Callback(Rec.callback, Rec.state, true, Rec.stream, Rec.streamId);
401 else
402 {
403 Rec.stream.Dispose();
404 await this.Callback(Rec.callback, Rec.state, false, null, Rec.streamId);
405 }
406
407 Rec.callback = null;
408 }
409
410 private async Task Callback(EventHandlerAsync<StreamEventArgs> Callback, object State, bool Ok, Socks5Client Stream, string StreamId)
411 {
412 if (!Ok && !string.IsNullOrEmpty(StreamId))
413 {
414 lock (this.streams)
415 {
416 this.streams.Remove(StreamId);
417 }
418 }
419
420 await Callback.Raise(this, new StreamEventArgs(Ok, Stream, State));
421 }
422
423 private async Task QueryHandler(object Sender, IqEventArgs e)
424 {
425 string StreamId = XML.Attribute(e.Query, "sid");
426 XmlElement E;
427
428 if (string.IsNullOrEmpty(StreamId) || StreamId != Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(StreamId)))
429 throw new NotAcceptableException("Invalid Stream ID.", e.IQ);
430
431 string Host = null;
432 string JID = null;
433 int Port = 0;
434
435 foreach (XmlNode N in e.Query.ChildNodes)
436 {
437 E = N as XmlElement;
438 if (E is null)
439 continue;
440
441 if (E.LocalName == "streamhost" && E.NamespaceURI == Namespace)
442 {
443 Host = XML.Attribute(E, "host");
444 JID = XML.Attribute(E, "jid");
445 Port = XML.Attribute(E, "port", 0);
446
447 break;
448 }
449 }
450
451 if (string.IsNullOrEmpty(JID) || string.IsNullOrEmpty(Host) || Port <= 0 || Port >= 0x10000)
452 throw new BadRequestException("Invalid parameters.", e.IQ);
453
454 ValidateStreamEventArgs e2 = new ValidateStreamEventArgs(this.client, e, StreamId);
455 await this.OnOpen.Raise(this, e2, false);
456
457 if (e2.DataCallback is null || e2.CloseCallback is null)
458 throw new NotAcceptableException("Stream not expected.", e.IQ);
459
460 Socks5Client Client;
461
462 lock (this.streams)
463 {
464 if (this.streams.ContainsKey(StreamId))
465 throw new ConflictException("Stream already exists.", e.IQ);
466
467 Client = new Socks5Client(Host, Port, JID)
468 {
469 CallbackState = e2.State
470 };
471
472 this.streams[StreamId] = Client;
473 }
474
475 Client.Tag = new Socks5QueryState()
476 {
477 streamId = StreamId,
478 eventargs = e,
479 eventargs2 = e2
480 };
481
482 Client.OnDataReceived += e2.DataCallback;
483 Client.OnStateChange += this.ClientStateChanged;
484 }
485
486 private class Socks5QueryState
487 {
488 public string streamId;
489 public IqEventArgs eventargs;
490 public ValidateStreamEventArgs eventargs2;
491 }
492
493 private async Task ClientStateChanged(object Sender, EventArgs e3)
494 {
495 Socks5Client Client = (Socks5Client)Sender;
496 Socks5QueryState State = (Socks5QueryState)Client.Tag;
497
498 switch (Client.State)
499 {
500 case Socks5State.Authenticated:
501 await Client.CONNECT(State.streamId, State.eventargs.From, this.client.FullJID);
502 break;
503
504 case Socks5State.Connected:
505 StringBuilder Xml = new StringBuilder();
506
507 Xml.Append("<query xmlns=\"");
508 Xml.Append(Namespace);
509 Xml.Append("\" sid=\"");
510 Xml.Append(State.streamId);
511 Xml.Append("\"><streamhost-used jid=\"");
512 Xml.Append(Client.Host);
513 Xml.Append("\"/></query>");
514
515 await State.eventargs.IqResult(Xml.ToString());
516 break;
517
518 case Socks5State.Error:
519 case Socks5State.Offline:
520 if (Client.State == Socks5State.Error)
521 await State.eventargs.IqError(new BadRequestException("Unable to establish a SOCKS5 connection.", State.eventargs.IQ));
522
523 Client.Dispose();
524
525 lock (this.streams)
526 {
527 this.streams.Remove(State.streamId);
528 }
529
530 await State.eventargs2.CloseCallback.Raise(this, new StreamEventArgs(false, Client, State.eventargs2.State));
531 break;
532 }
533 }
534
539 public event EventHandlerAsync<ValidateStreamEventArgs> OnOpen = null;
540
541 }
542}
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
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
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:70
Task InitiateSession(string DestinationJid, string StreamId, EventHandlerAsync< StreamEventArgs > Callback, object State)
Initiates a mediated SOCKS5 session with another.
Definition: Socks5Proxy.cs:223
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:91
override void Dispose()
Disposes of the extension.
Definition: Socks5Proxy.cs:55
override string[] Extensions
Implemented extensions.
Definition: Socks5Proxy.cs:65
Task InitiateSession(string DestinationJid, EventHandlerAsync< StreamEventArgs > Callback, object State)
Initiates a mediated SOCKS5 session with another.
Definition: Socks5Proxy.cs:211
int Port
Port number of SOCKS5 proxy.
Definition: Socks5Proxy.cs:85
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:197
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:80
async Task InitiateSession(string DestinationJid, string StreamId, bool InstantiateSocks5Client, EventHandlerAsync< StreamEventArgs > Callback, object State)
Initiates a mediated SOCKS5 session with another.
Definition: Socks5Proxy.cs:237
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:539
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:59
XmppState State
Current state of connection.
Definition: XmppClient.cs:985
string Host
Host or IP address of XMPP server.
Definition: XmppClient.cs:868
async void Dispose()
Closes the connection and disposes of all resources.
Definition: XmppClient.cs:1103
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers an IQ-Set handler.
Definition: XmppClient.cs:2729
string Domain
Current Domain.
Definition: XmppClient.cs:3453
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters an IQ-Set handler.
Definition: XmppClient.cs:2791
Task SendServiceItemsDiscoveryRequest(string To, EventHandlerAsync< ServiceItemsDiscoveryEventArgs > Callback, object State)
Sends a service items discovery request
Definition: XmppClient.cs:6061
Task< uint > SendIqSet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Set request.
Definition: XmppClient.cs:3607
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.