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;
64 public override string[]
Extensions =>
new string[] {
"XEP-0065" };
74 public string JID => this.jid;
79 public string Host => this.host;
84 public int Port => this.port;
92 this.hasProxy =
false;
103 SearchState State =
new SearchState(
this, e.
Items, Callback);
104 return State.DoQuery();
107 private class SearchState
109 public Socks5Proxy Proxy;
111 public string Component =
string.Empty;
119 this.Callback = Callback;
121 this.NrItems = Items.Length;
124 public Task Advance()
127 return this.DoQuery();
130 public Task DoQuery()
132 if (this.Pos < this.NrItems)
133 return this.Proxy.client.SendServiceDiscoveryRequest(this.Items[this.Pos].
JID, this.ItemDiscoveryResponse,
null);
135 return this.SearchDone();
142 this.Component = this.Items[this.Pos].
JID;
143 await this.Proxy.client.SendIqGet(this.Component,
"<query xmlns=\"" +
Namespace +
"\"/>", this.SocksQueryResponse,
null);
146 await this.Advance();
155 if (E.LocalName ==
"query" && E.NamespaceURI ==
Namespace)
157 E = (XmlElement)E.FirstChild;
159 if (E.LocalName ==
"streamhost" && E.NamespaceURI ==
Namespace)
164 this.Proxy.hasProxy = !
string.IsNullOrEmpty(this.Proxy.jid) &&
165 !
string.IsNullOrEmpty(this.Proxy.host) &&
168 if (this.Proxy.hasProxy)
169 return this.SearchDone();
171 return this.Advance();
174 return this.Advance();
177 return this.Advance();
180 return this.Advance();
183 private Task SearchDone()
185 return this.Callback.Raise(this.Proxy, EventArgs.Empty);
201 this.hasProxy = !
string.IsNullOrEmpty(this.jid) && !
string.IsNullOrEmpty(this.host) && this.port > 0;
210 public Task
InitiateSession(
string DestinationJid, EventHandlerAsync<StreamEventArgs> Callback,
object State)
212 return this.
InitiateSession(DestinationJid,
null,
true, Callback, State);
222 public Task
InitiateSession(
string DestinationJid,
string StreamId, EventHandlerAsync<StreamEventArgs> Callback,
object State)
224 return this.
InitiateSession(DestinationJid, StreamId,
true, Callback, State);
236 public async Task
InitiateSession(
string DestinationJid,
string StreamId,
bool InstantiateSocks5Client,
237 EventHandlerAsync<StreamEventArgs> Callback,
object State)
241 await this.Callback(Callback, State,
false,
null,
null);
247 if (
string.IsNullOrEmpty(StreamId))
251 StreamId = Guid.NewGuid().ToString().Replace(
"-",
string.Empty);
253 while (this.streams.ContainsKey(StreamId));
255 else if (this.streams.ContainsKey(StreamId))
258 if (!(StreamId is
null))
259 this.streams[StreamId] =
null;
262 if (StreamId is
null)
264 await this.Callback(Callback, State,
false,
null,
null);
268 StringBuilder Xml =
new StringBuilder();
270 Xml.Append(
"<query xmlns=\"");
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>");
282 InitiationRec Rec =
new InitiationRec()
284 destinationJid = DestinationJid,
289 instantiateSocks5Client = InstantiateSocks5Client
292 if (!(this.e2e is
null))
293 await this.e2e.SendIqSet(this.
client,
E2ETransmission.NormalIfNotE2E, DestinationJid, Xml.ToString(),
this.InitiationResponse, Rec);
295 await this.
client.
SendIqSet(DestinationJid, Xml.ToString(),
this.InitiationResponse, Rec);
298 private class InitiationRec
300 public string destinationJid;
301 public string streamId;
303 public bool instantiateSocks5Client;
304 public EventHandlerAsync<StreamEventArgs> callback;
308 internal async Task StateChanged(
object Sender, EventArgs e)
310 switch (this.stream.
State)
317 StringBuilder Xml =
new StringBuilder();
319 Xml.Append(
"<query xmlns=\"");
321 Xml.Append(
"\" sid=\"");
322 Xml.Append(this.streamId);
323 Xml.Append(
"\"><activate>");
324 Xml.Append(this.destinationJid);
325 Xml.Append(
"</activate></query>");
327 if (!(this.proxy.e2e is
null))
330 this.proxy.ActivationResponse,
this);
333 await this.proxy.
client.
SendIqSet(this.proxy.jid, Xml.ToString(),
this.proxy.ActivationResponse,
this);
338 if (!(this.stream is
null))
341 await this.proxy.Callback(this.callback, this.state,
false,
null, this.streamId);
342 this.callback =
null;
350 InitiationRec Rec = (InitiationRec)e.
State;
356 if (!(E is
null) && E.LocalName ==
"query" && E.NamespaceURI ==
Namespace &&
XML.
Attribute(E,
"sid") == Rec.streamId)
359 string StreamHostUsed =
null;
361 foreach (XmlNode N
in E.ChildNodes)
363 E2 = N as XmlElement;
364 if (E2.LocalName ==
"streamhost-used" && E2.NamespaceURI ==
Namespace)
371 if (!
string.IsNullOrEmpty(StreamHostUsed) && StreamHostUsed == this.host)
373 if (Rec.instantiateSocks5Client)
375 Rec.stream =
new Socks5Client(this.host, this.port, this.jid);
376 Rec.stream.OnStateChange += Rec.StateChanged;
380 this.streams[Rec.streamId] = Rec.stream;
384 await this.Callback(Rec.callback, Rec.state,
true,
null, Rec.streamId);
387 await this.Callback(Rec.callback, Rec.state,
false,
null, Rec.streamId);
390 await this.Callback(Rec.callback, Rec.state,
false,
null, Rec.streamId);
393 await this.Callback(Rec.callback, Rec.state,
false,
null, Rec.streamId);
398 InitiationRec Rec = (InitiationRec)e.
State;
401 await this.Callback(Rec.callback, Rec.state,
true, Rec.stream, Rec.streamId);
404 await Rec.stream.DisposeAsync();
405 await this.Callback(Rec.callback, Rec.state,
false,
null, Rec.streamId);
411 private async Task Callback(EventHandlerAsync<StreamEventArgs> Callback,
object State,
bool Ok, Socks5Client Stream,
string StreamId)
413 if (!Ok && !
string.IsNullOrEmpty(StreamId))
417 this.streams.Remove(StreamId);
421 await Callback.Raise(
this,
new StreamEventArgs(Ok, Stream, State));
424 private async Task QueryHandler(
object Sender,
IqEventArgs e)
429 if (
string.IsNullOrEmpty(StreamId) || StreamId != Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(StreamId)))
436 foreach (XmlNode N
in e.
Query.ChildNodes)
442 if (E.LocalName ==
"streamhost" && E.NamespaceURI ==
Namespace)
452 if (
string.IsNullOrEmpty(
JID) ||
string.IsNullOrEmpty(
Host) || Port <= 0 || Port >= 0x10000)
455 ValidateStreamEventArgs e2 =
new ValidateStreamEventArgs(this.
client, e, StreamId);
456 await this.
OnOpen.Raise(
this, e2,
false);
458 if (e2.DataCallback is
null || e2.CloseCallback is
null)
465 if (this.streams.ContainsKey(StreamId))
470 CallbackState = e2.State
473 this.streams[StreamId] =
Client;
476 Client.Tag =
new Socks5QueryState()
483 Client.OnDataReceived += e2.DataCallback;
484 Client.OnStateChange += this.ClientStateChanged;
487 private class Socks5QueryState
489 public string streamId;
491 public ValidateStreamEventArgs eventargs2;
494 private async Task ClientStateChanged(
object Sender, EventArgs e3)
496 Socks5Client
Client = (Socks5Client)Sender;
497 Socks5QueryState State = (Socks5QueryState)
Client.Tag;
502 await
Client.CONNECT(State.streamId, State.eventargs.From,
this.client.FullJID);
506 StringBuilder Xml =
new StringBuilder();
508 Xml.Append(
"<query xmlns=\"");
510 Xml.Append(
"\" sid=\"");
511 Xml.Append(State.streamId);
512 Xml.Append(
"\"><streamhost-used jid=\"");
514 Xml.Append(
"\"/></query>");
516 await State.eventargs.IqResult(Xml.ToString());
522 await State.eventargs.IqError(
new BadRequestException(
"Unable to establish a SOCKS5 connection.", State.eventargs.IQ));
528 this.streams.Remove(State.streamId);
531 await State.eventargs2.CloseCallback.Raise(
this,
new StreamEventArgs(
false,
Client, State.eventargs2.State));
540 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.
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.
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 Task DisposeAsync()
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.