2using System.Collections.Generic;
4using System.Threading.Tasks;
23 public const string Namespace =
"http://jabber.org/protocol/bytestreams";
25 private readonly Dictionary<string, Socks5Client> streams =
new Dictionary<string, Socks5Client>();
27 private bool hasProxy =
false;
28 private string jid =
null;
29 private string host =
null;
65 public override string[]
Extensions =>
new string[] {
"XEP-0065" };
75 public string JID => this.jid;
80 public string Host => this.host;
85 public int Port => this.port;
93 this.hasProxy =
false;
104 SearchState State =
new SearchState(
this, e.
Items, Callback);
105 return State.DoQuery();
108 private class SearchState
110 public Socks5Proxy Proxy;
112 public string Component =
string.Empty;
120 this.Callback = Callback;
122 this.NrItems = Items.Length;
125 public Task Advance()
128 return this.DoQuery();
131 public Task DoQuery()
133 if (this.Pos < this.NrItems)
134 return this.Proxy.client.SendServiceDiscoveryRequest(this.Items[this.Pos].
JID, this.ItemDiscoveryResponse,
null);
136 return this.SearchDone();
143 this.Component = this.Items[this.Pos].
JID;
144 await this.Proxy.client.SendIqGet(this.Component,
"<query xmlns=\"" +
Namespace +
"\"/>", this.SocksQueryResponse,
null);
147 await this.Advance();
156 if (E.LocalName ==
"query" && E.NamespaceURI ==
Namespace)
158 E = (XmlElement)E.FirstChild;
160 if (E.LocalName ==
"streamhost" && E.NamespaceURI ==
Namespace)
165 this.Proxy.hasProxy = !
string.IsNullOrEmpty(this.Proxy.jid) &&
166 !
string.IsNullOrEmpty(this.Proxy.host) &&
169 if (this.Proxy.hasProxy)
170 return this.SearchDone();
172 return this.Advance();
175 return this.Advance();
178 return this.Advance();
181 return this.Advance();
184 private Task SearchDone()
186 return this.Callback.Raise(this.Proxy, EventArgs.Empty);
202 this.hasProxy = !
string.IsNullOrEmpty(this.jid) && !
string.IsNullOrEmpty(this.host) && this.port > 0;
211 public Task
InitiateSession(
string DestinationJid, EventHandlerAsync<StreamEventArgs> Callback,
object State)
213 return this.
InitiateSession(DestinationJid,
null,
true, Callback, State);
223 public Task
InitiateSession(
string DestinationJid,
string StreamId, EventHandlerAsync<StreamEventArgs> Callback,
object State)
225 return this.
InitiateSession(DestinationJid, StreamId,
true, Callback, State);
237 public async Task
InitiateSession(
string DestinationJid,
string StreamId,
bool InstantiateSocks5Client,
238 EventHandlerAsync<StreamEventArgs> Callback,
object State)
242 await this.Callback(Callback, State,
false,
null,
null);
248 if (
string.IsNullOrEmpty(StreamId))
252 StreamId = Guid.NewGuid().ToString().Replace(
"-",
string.Empty);
254 while (this.streams.ContainsKey(StreamId));
256 else if (this.streams.ContainsKey(StreamId))
259 if (!(StreamId is
null))
260 this.streams[StreamId] =
null;
263 if (StreamId is
null)
265 await this.Callback(Callback, State,
false,
null,
null);
269 StringBuilder Xml =
new StringBuilder();
271 Xml.Append(
"<query xmlns=\"");
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>");
283 InitiationRec Rec =
new InitiationRec()
285 destinationJid = DestinationJid,
290 instantiateSocks5Client = InstantiateSocks5Client
293 if (!(this.e2e is
null))
294 await this.e2e.SendIqSet(this.
client,
E2ETransmission.NormalIfNotE2E, DestinationJid, Xml.ToString(),
this.InitiationResponse, Rec);
296 await this.
client.
SendIqSet(DestinationJid, Xml.ToString(),
this.InitiationResponse, Rec);
299 private class InitiationRec
301 public string destinationJid;
302 public string streamId;
304 public bool instantiateSocks5Client;
305 public EventHandlerAsync<StreamEventArgs> callback;
309 internal async Task StateChanged(
object Sender, EventArgs e)
311 switch (this.stream.
State)
318 StringBuilder Xml =
new StringBuilder();
320 Xml.Append(
"<query xmlns=\"");
322 Xml.Append(
"\" sid=\"");
323 Xml.Append(this.streamId);
324 Xml.Append(
"\"><activate>");
325 Xml.Append(this.destinationJid);
326 Xml.Append(
"</activate></query>");
328 if (!(this.proxy.e2e is
null))
331 this.proxy.ActivationResponse,
this);
334 await this.proxy.
client.
SendIqSet(this.proxy.jid, Xml.ToString(),
this.proxy.ActivationResponse,
this);
340 await this.proxy.Callback(this.callback, this.state,
false,
null, this.streamId);
341 this.callback =
null;
349 InitiationRec Rec = (InitiationRec)e.
State;
355 if (!(E is
null) && E.LocalName ==
"query" && E.NamespaceURI ==
Namespace &&
XML.
Attribute(E,
"sid") == Rec.streamId)
358 string StreamHostUsed =
null;
360 foreach (XmlNode N
in E.ChildNodes)
362 E2 = N as XmlElement;
363 if (E2.LocalName ==
"streamhost-used" && E2.NamespaceURI ==
Namespace)
370 if (!
string.IsNullOrEmpty(StreamHostUsed) && StreamHostUsed == this.host)
372 if (Rec.instantiateSocks5Client)
374 Rec.stream =
new Socks5Client(this.host, this.port, this.jid);
375 Rec.stream.OnStateChange += Rec.StateChanged;
379 this.streams[Rec.streamId] = Rec.stream;
383 await this.Callback(Rec.callback, Rec.state,
true,
null, Rec.streamId);
386 await this.Callback(Rec.callback, Rec.state,
false,
null, Rec.streamId);
389 await this.Callback(Rec.callback, Rec.state,
false,
null, Rec.streamId);
392 await this.Callback(Rec.callback, Rec.state,
false,
null, Rec.streamId);
397 InitiationRec Rec = (InitiationRec)e.
State;
400 await this.Callback(Rec.callback, Rec.state,
true, Rec.stream, Rec.streamId);
403 Rec.stream.Dispose();
404 await this.Callback(Rec.callback, Rec.state,
false,
null, Rec.streamId);
410 private async Task Callback(EventHandlerAsync<StreamEventArgs> Callback,
object State,
bool Ok, Socks5Client Stream,
string StreamId)
412 if (!Ok && !
string.IsNullOrEmpty(StreamId))
416 this.streams.Remove(StreamId);
420 await Callback.Raise(
this,
new StreamEventArgs(Ok, Stream, State));
423 private async Task QueryHandler(
object Sender,
IqEventArgs e)
428 if (
string.IsNullOrEmpty(StreamId) || StreamId != Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(StreamId)))
435 foreach (XmlNode N
in e.
Query.ChildNodes)
441 if (E.LocalName ==
"streamhost" && E.NamespaceURI ==
Namespace)
451 if (
string.IsNullOrEmpty(
JID) ||
string.IsNullOrEmpty(
Host) || Port <= 0 || Port >= 0x10000)
454 ValidateStreamEventArgs e2 =
new ValidateStreamEventArgs(this.
client, e, StreamId);
455 await this.
OnOpen.Raise(
this, e2,
false);
457 if (e2.DataCallback is
null || e2.CloseCallback is
null)
464 if (this.streams.ContainsKey(StreamId))
469 CallbackState = e2.State
472 this.streams[StreamId] =
Client;
475 Client.Tag =
new Socks5QueryState()
482 Client.OnDataReceived += e2.DataCallback;
483 Client.OnStateChange += this.ClientStateChanged;
486 private class Socks5QueryState
488 public string streamId;
490 public ValidateStreamEventArgs eventargs2;
493 private async Task ClientStateChanged(
object Sender, EventArgs e3)
495 Socks5Client
Client = (Socks5Client)Sender;
496 Socks5QueryState State = (Socks5QueryState)
Client.Tag;
501 await
Client.CONNECT(State.streamId, State.eventargs.From,
this.client.FullJID);
505 StringBuilder Xml =
new StringBuilder();
507 Xml.Append(
"<query xmlns=\"");
509 Xml.Append(
"\" sid=\"");
510 Xml.Append(State.streamId);
511 Xml.Append(
"\"><streamhost-used jid=\"");
513 Xml.Append(
"\"/></query>");
515 await State.eventargs.IqResult(Xml.ToString());
521 await State.eventargs.IqError(
new BadRequestException(
"Unable to establish a SOCKS5 connection.", State.eventargs.IQ));
527 this.streams.Remove(State.streamId);
530 await State.eventargs2.CloseCallback.Raise(
this,
new StreamEventArgs(
false,
Client, State.eventargs2.State));
539 public event EventHandlerAsync<ValidateStreamEventArgs>
OnOpen =
null;
Helps with common XML-related tasks.
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Event arguments for IQ queries.
XmlElement Query
Query element, if found, null otherwise.
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.
Socks5State State
Current state.
Task CONNECT(IPAddress DestinationAddress, int Port)
Connects to the target.
void Dispose()
IDisposable.Dispose
Class managing a SOCKS5 proxy associated with the current XMPP server.
bool HasProxy
If a SOCKS5 proxy has been detected.
Task InitiateSession(string DestinationJid, string StreamId, EventHandlerAsync< StreamEventArgs > Callback, object State)
Initiates a mediated SOCKS5 session with another.
Socks5Proxy(XmppClient Client)
Class managing a SOCKS5 proxy associated with the current XMPP server.
Task StartSearch(EventHandlerAsync Callback)
Starts the search of SOCKS5 proxies.
override void Dispose()
Disposes of the extension.
override string[] Extensions
Implemented extensions.
Task InitiateSession(string DestinationJid, EventHandlerAsync< StreamEventArgs > Callback, object State)
Initiates a mediated SOCKS5 session with another.
int Port
Port number of SOCKS5 proxy.
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...
const string Namespace
http://jabber.org/protocol/bytestreams
string Host
Host name or IP address of SOCKS5 proxy.
async Task InitiateSession(string DestinationJid, string StreamId, bool InstantiateSocks5Client, EventHandlerAsync< StreamEventArgs > Callback, object State)
Initiates a mediated SOCKS5 session with another.
Socks5Proxy(XmppClient Client, IEndToEndEncryption E2E)
Class managing a SOCKS5 proxy associated with the current XMPP server.
EventHandlerAsync< ValidateStreamEventArgs > OnOpen
Event raised when a remote entity tries to open a SOCKS5 bytestream for transmission of data to/from ...
string JID
JID of SOCKS5 proxy.
Contains information about an item of an entity.
Event arguments for service discovery responses.
Dictionary< string, bool > Features
Features
Event arguments for service items discovery responses.
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....
XmppState State
Current state of connection.
string Host
Host or IP address of XMPP server.
async void Dispose()
Closes the connection and disposes of all resources.
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers an IQ-Set handler.
string Domain
Current Domain.
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters an IQ-Set handler.
Task SendServiceItemsDiscoveryRequest(string To, EventHandlerAsync< ServiceItemsDiscoveryEventArgs > Callback, object State)
Sends a service items discovery request
Task< uint > SendIqSet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Set request.
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.
E2ETransmission
End-to-end encryption mode.