/// <summary> /// Performs a mediated transfer, meaning we send the data over a proxy server. /// </summary> /// <param name="session">The SI session whose data to transfer.</param> /// <param name="proxies"></param> /// <exception cref="Socks5Exception">The SOCKS5 connection to the designated /// proxy server could not be established.</exception> /// <exception cref="XmppErrorException">The server returned an XMPP error code. /// Use the Error property of the XmppErrorException to obtain the specific /// error condition.</exception> /// <exception cref="XmppException">The server returned invalid data or another /// unspecified XMPP error occurred.</exception> void MediatedTransfer(SISession session, IEnumerable <Streamhost> proxies) { var proxy = NegotiateProxy(session, proxies); // Connect to the designated proxy. using (var client = new Socks5Client(proxy.Host, proxy.Port)) { // Send the SOCKS5 Connect command. string hostname = Sha1(session.Sid + session.From + session.To); SocksReply reply = client.Request(SocksCommand.Connect, hostname, 0); if (reply.Status != ReplyStatus.Succeeded) { throw new Socks5Exception("SOCKS5 Connect request failed."); } // Activate the bytetream. var xml = Xml.Element("query", "http://jabber.org/protocol/bytestreams") .Attr("sid", session.Sid).Child( Xml.Element("activate").Text(session.To.ToString())); Iq iq = im.IqRequest(IqType.Set, proxy.Jid, im.Jid, xml); if (iq.Type == IqType.Error) { throw Util.ExceptionFromError(iq, "Could not activate the bytestream."); } // Finally, go ahead and send the data to the proxy. SendData(session, client.GetStream()); } }
/// <summary> /// Sends the specified SOCKS5 reply to the connected client. /// </summary> /// <param name="reply">The SOCKS5 reply to send to the connected /// client.</param> /// <exception cref="ArgumentNullException">The reply parameter is /// null.</exception> /// <exception cref="ObjectDisposedException">The object has been /// disposed.</exception> public void Reply(SocksReply reply) { AssertValid(); var bytes = reply.Serialize(); stream.Write(bytes, 0, bytes.Length); }
/// <summary> /// Performs the specified SOCKS5 request. /// </summary> /// <param name="request">The SOCKS5 request to issue to the server.</param> /// <returns>The SOCKS5 reply sent by the server.</returns> /// <exception cref="ArgumentNullException">The request parameter is /// null.</exception> /// <exception cref="ObjectDisposedException">The object has been /// disposed.</exception> /// <exception cref="Socks5Exception">The request could not be performed. /// Consult the InnerException property of the Socks5Exception to learn /// the reason.</exception> public SocksReply Request(SocksRequest request) { AssertValid(); try { byte[] bytes = request.Serialize(); stream.Write(bytes, 0, bytes.Length); ByteBuilder b = new ByteBuilder(); using (var r = new BinaryReader(stream, Encoding.UTF8, true)) { bytes = r.ReadBytes(4); b.Append(bytes); ATyp atyp = (ATyp)bytes[3]; switch (atyp) { case ATyp.IPv4: case ATyp.IPv6: b.Append(r.ReadBytes(atyp == ATyp.IPv4 ? 4 : 16)); break; case ATyp.Domain: byte length = r.ReadByte(); b.Append(length).Append(r.ReadBytes(length)); break; } b.Append(r.ReadBytes(2)); } return(SocksReply.Deserialize(b.ToArray())); } catch (Exception e) { throw new Socks5Exception("The request could not be performed.", e); } }
/// <summary> /// Establishes a SOCKS5 connection with one of the streamhosts in the /// specified collection of streamhosts. /// </summary> /// <param name="stanza">The original requesting IQ stanza.</param> /// <param name="sid">The session-id associated with this request.</param> /// <param name="hosts">An enumerable collection of Streamhost /// instances.</param> /// <returns>An initialized instance of the Socks5Client class /// representing the established SOCKS5 connection.</returns> /// <exception cref="Socks5Exception">An error occurred during SOCKS5 /// negotiation.</exception> /// <exception cref="XmppException">No connection to any of the provided /// streamhosts could be established.</exception> private async Task <Socks5Client> EstablishConnection(Iq stanza, string sid, IEnumerable <Streamhost> hosts) { // Try to establish a SOCKS5 connection to any of the streamhosts in the // collection. bool connected = false; foreach (var host in hosts) { try { var client = await Socks5Client.Create(host.Host, host.Port); connected = true; // Send the SOCKS5 Connect command. string hostname = Sha1(sid + stanza.From + stanza.To); SocksReply reply = await client.Request(SocksCommand.Connect, hostname, 0); if (reply.Status != ReplyStatus.Succeeded) { throw new Socks5Exception("SOCKS5 Connect request failed."); } // Send acknowledging IQ-result. await im.IqResult(stanza, Xml.Element("query", "http://jabber.org/protocol/bytestreams") .Attr("sid", sid).Child(Xml.Element("streamhost-used") .Attr("jid", host.Jid.ToString())) ); return(client); } catch { // Fall through and try the next streamhost. if (connected) { break; } } } // Still here means we couldn't connect to any of the streamhosts or // an error occurred during SOCKS5 negotiation. await im.IqError(stanza, ErrorType.Cancel, ErrorCondition.ItemNotFound); // Error out. throw new XmppException("Couldn't connect to streamhost."); }
private void HandleProxyCommandError(SocksReply replyCode) { string proxyErrorText; switch (replyCode) { case SocksReply.GeneralSOCKSServerFailure: proxyErrorText = "a general socks destination failure occurred"; break; case SocksReply.NotAllowedByRuleset: proxyErrorText = "the connection is not allowed by proxy destination rule set"; break; case SocksReply.NetworkUnreachable: proxyErrorText = "the network was unreachable"; break; case SocksReply.HostUnreachable: proxyErrorText = "the host was unreachable"; break; case SocksReply.ConnectionRefused: proxyErrorText = "the connection was refused by the remote network"; break; case SocksReply.TTLExpired: proxyErrorText = "the time to live (TTL) has expired"; break; case SocksReply.CommandNotSupported: proxyErrorText = "the command issued by the proxy client is not supported by the proxy destination"; break; case SocksReply.AddressTypeNotSupported: proxyErrorText = "the address type specified is not supported"; break; default: proxyErrorText = $"an unknown SOCKS reply with the code value '{replyCode}' was received"; break; } _socketStream.Close(); throw new SocksProxyException($"Proxy error: {proxyErrorText} for destination host {_destinationHost} port number {_destinationPort}."); }