Connecting to IRC via a proxy (.NET) - c#

I wish to hide my IP when connecting to IRC via my .NET app. I currently use the IrcDotNet library but it doesn't seems to support proxies.
I've not had much experience with sockets, so I think modifying IrcDotNet would be easier than making my own IRC library. I looked around for socket libraries that handle proxy connections that I could implement in IrcDotNet. I found one called ProxySocket but it only supports BeginConnect not the new ASyncConnect method that IrcDotNet uses.
To break it down, in order of preference, here's what I'm looking for;
An IRC library that supports connecting via a HTTP/SOCKS proxy
A socket library that supports connecting via a HTTP/SOCKS proxy via
ASyncConnect
Example code on how to extend the socket class to support connecting
via a HTTP/SOCKS proxy via ASyncConnect
The version of IrcDotNet I am using is 0.4.1 found at https://launchpad.net/ircdotnet.
Update 1: Still no luck i'm afraid. Fredrik92's answer, while helpful, is not applicable to the version of IrcDotNet I am using (see above).

The IRC.NET library uses the standard Socket class in the System.Net.Sockets namespace.
So you could just modify the IrcDotNet/IrcClient.cs file in the IRC.NET source code (# http://ircdotnet.codeplex.com/SourceControl/latest).
You should add a constructor for proxy enabled IRC clients and call the default constructor.
Then all you should need to do is to modify the Connect methods in the same file (almost at the bottom). Each time they call this.client.BeginConnect(..) you have to add code for connecting to the Proxy (instead of the remote host)
Now you only have to create a new Connect-callback method that sends a HTTP CONNECT request to the proxy. Read the response from the HTTP Proxy and then everything else should work.
In this case I would write the HTTP request as raw ASCII bytes to the Proxy (instead of using the HttpWebRequest class), so that you have full control over the network stream you get in return...
You should add sth. like this to the IrcClient class:
private bool useProxy = false;
private IWebProxy proxy;
private IEnumerable<Uri> proxyRemoteUris;
public IrcClient(IWebProxy proxy)
: this()
{
this.useProxy = true;
this.proxy = proxy;
}
private void ProxyPerformHttpConnect(Uri remoteIrcUri)
{
string httpConnectRequest = string.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {2}\r\n\r\n",
remoteIrcUri.Host, remoteIrcUri.Port, this.proxy.GetProxy(remoteIrcUri));
byte[] httpConnectData = Encoding.ASCII.GetBytes(httpConnectRequest);
this.stream.Write(httpConnectData, 0, httpConnectData.Length);
bool responseReady = false;
string responseText = string.Empty;
// Byte-by-byte reading required, because StringReader will read more than just the HTTP response header
do
{
int readByte = this.stream.ReadByte();
if (readByte < 0)
throw new WebException(message: null, status: WebExceptionStatus.ConnectionClosed);
char readChar = (char)(readByte); // Only works because HTTP Headers are ASCII encoded.
responseText += readChar;
responseReady = responseText.EndsWith("\r\n\r\n");
} while (!responseReady);
int statusStart = responseText.IndexOf(' ') + 1;
int reasonStart = responseText.IndexOf(' ', statusStart) + 1;
int reasonEnd = responseText.IndexOfAny(new char[] { '\r', '\n'});
HttpStatusCode responseStatus = (HttpStatusCode)(int.Parse(responseText.Substring(responseText.IndexOf(' ') + 1, length: 3)));
if (responseStatus != HttpStatusCode.OK)
{
string reasonText = responseText.Substring(reasonStart, reasonEnd - reasonStart);
if (string.IsNullOrWhiteSpace(reasonText))
reasonText = null;
throw new WebException(reasonText, WebExceptionStatus.ConnectFailure);
}
// Finished Response Header read...
}
private void ProxyConnectCallback(IAsyncResult ar)
{
try
{
this.client.EndConnect(ar);
this.stream = this.client.GetStream();
bool proxyTunnelEstablished = false;
WebException lastWebException = null;
foreach (Uri remoteIrcUri in this.proxyRemoteUris)
{
if (this.client.Connected == false)
{
// Re-establish connection with proxy...
Uri proxyUri = this.proxy.GetProxy(remoteIrcUri);
this.client.Connect(proxyUri.Host, proxyUri.Port);
}
try
{
ProxyPerformHttpConnect(remoteIrcUri);
proxyTunnelEstablished = true;
break;
}
catch (WebException webExcept)
{
lastWebException = webExcept;
}
}
if (!proxyTunnelEstablished)
{
OnConnectFailed(new IrcErrorEventArgs(lastWebException));
return;
}
this.writer = new StreamWriter(this.stream, Encoding.Default);
this.reader = new StreamReader(this.stream, Encoding.Default);
HandleClientConnected((IrcRegistrationInfo)ar.AsyncState);
this.readThread.Start();
this.writeThread.Start();
OnConnected(new EventArgs());
}
catch (Exception ex)
{
OnConnectFailed(new IrcErrorEventArgs(ex));
}
}
The code for the proxy handling in all Connect methods of the IrcClient class would thus look sth. like this:
// Code snippet to insert before the call to this.client.BeginConnect(...)
if (this.useProxy)
{
// Assign host and port variables for EndPoint objects:
// var host = remoteEP.Address;
// var port = remoteEP.Port;
this.proxyRemoteUris = new Uri[] { new Uri(string.Format("irc://{0}:{1}/", host, port)) };
// Replace the line above with the following line in the method where an array of IP addresses is specified as a parameter
// this.proxyRemoteUris = from ip in addresses select new Uri(string.Format("irc://{0}:{1}/", ip, port));
Uri proxyUri = this.proxy.GetProxy(this.proxyRemoteUris.First());
string proxyHost = proxyUri.Host;
int proxyPort = proxyUri.Port;
this.client.BeginConnect(proxyHost, proxyPort, ProxyConnectCallback, registrationInfo);
}
else
// Original this.client.BeginConnect(...) call here...

Related

Not able to connect to WCF-XMLRPC-Server using other client in a different language

I have created a sample xmlrpc C# server-client using Vaster Clemens tutorials
http://vasters.com/clemensv/PermaLink,guid,679ca50b-c907-4831-81c4-369ef7b85839.aspx
I am successfully able to connect to my C# server to the C# client, but whenever I try to connect to the C# server using my Java Client , I get only this error :
HTTP server returned unexpected status: Internal Server Error
Here is the C# server API exposed :
[ServiceContract]
public interface ITestAPI {
[OperationContract(Action = "test.returnSum")]
int returnSum(
int a,
int b);
This is the server part:
Uri baseAddress = new UriBuilder(Uri.UriSchemeHttp, Environment.MachineName, 8080, "/testDemo/").Uri;
ServiceHost serviceHost = new ServiceHost(typeof(TestAPI));
var epXmlRpc = serviceHost.AddServiceEndpoint(typeof(ITestAPI), new WebHttpBinding(WebHttpSecurityMode.None), new Uri(baseAddress, "./test"));
epXmlRpc.Behaviors.Add(new XmlRpcEndpointBehavior());
serviceHost.Open();
Console.ReadLine();
serviceHost.Close();
The C# client goes here :
Uri blogAddress = new UriBuilder(Uri.UriSchemeHttp, Environment.MachineName, PORT_NUMBER, pathValue).Uri;
ChannelFactory<ITestAPI> testAPIFactory = new ChannelFactory<ITestAPI>(new WebHttpBinding(WebHttpSecurityMode.None), new EndpointAddress(blogAddress));
testAPIFactory.Endpoint.Behaviors.Add(new XmlRpcEndpointBehavior());
testAPI = testAPIFactory.CreateChannel();
testAPI.returnSum(1,2);
After this I tried to implement a sample Java XML-RPC client given here and tried to connect it to the running C# server
http://www.tutorialspoint.com/xml-rpc/xml_rpc_examples.htm
public class JavaClient {
public static void main (String [] args) {
try {
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://localhost:8080/testDemo/test"));
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
Vector params = new Vector();
params.addElement( new Integer(5) );
params.addElement( new Integer(5) );
Integer result = (Integer)client.execute( "returnSum", params );
if ( result != null )
System.out.println( "result" + result);
} catch (XmlRpcException exception) {
System.err.println("JavaClient: XML−RPC Fault #" +
Integer.toString(exception.code) + ": " +
exception.toString());
} catch (Exception exception) {
System.err.println("JavaClient: " + exception.toString());
}
}
}
And nothing works.
Any kind of help would be much appreciated.
I was able to solve this problem guys: the API is supposed to be called like this : client.execute( "test.returnSum", params ); from your client. i.e. the same string you mentioned in your operation contract.

WebSockets in firefox

For implementing my websocket server in C# I'm using Alchemy framework. I'm stuck with this issue. In the method OnReceive when I try to deserialize json object, I get a FormatException:
"Incorrect format of the input string." (maybe it's different in english, but I'm getting a localized exception message and that's my translation :P). What is odd about this is that when I print out the context.DataFrame I get: 111872281.1341000479.1335108793.1335108793.1335108793.1; __ad which is a substring of the cookies sent by the browser: __gutp=entrystamp%3D1288455757%7Csid%3D65a51a83cbf86945d0fd994e15eb94f9%7Cstamp%3D1288456520%7Contime%3D155; __utma=111872281.1341000479.1335108793.1335108793.1335108793.1; __adtaily_ui=cupIiq90q9.
JS code:
// I'm really not doing anything more than this
var ws = new WebSocket("ws://localhost:8080");
C# code:
static void Main(string[] args) {
int port = 8080;
WebSocketServer wsServer = new WebSocketServer(port, IPAddress.Any) {
OnReceive = OnReceive,
OnSend = OnSend,
OnConnect = OnConnect,
OnConnected = OnConnected,
OnDisconnect = OnDisconnect,
TimeOut = new TimeSpan(0, 5, 0)
};
wsServer.Start();
Console.WriteLine("Server started listening on port: " + port + "...");
string command = string.Empty;
while (command != "exit") {
command = Console.ReadLine();
}
Console.WriteLine("Server stopped listening on port: " + port + "...");
wsServer.Stop();
Console.WriteLine("Server exits...");
}
public static void OnReceive(UserContext context) {
string json = "";
dynamic obj;
try {
json = context.DataFrame.ToString();
Console.WriteLine(json);
obj = JsonConvert.DeserializeObject(json);
} catch (Exception e) {
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
return;
}
}
On the C# side I'm using Newtonsoft.Json, though it's not a problem with this library...
EDIT:
One more thing - I browsed through the code in here: https://github.com/Olivine-Labs/Alchemy-Websockets-Example and found nothing - I mean, I'm doing everything the same way authors did in this tutorial...
EDIT:
I was testing the above code in Firefox v 17.0.1, and it didn't work, so I tested it under google chrome, and it works. So let me rephrase the question - what changes can be made in js, so that firefox would not send aforementioned string?
I ran into the same issue - simply replacing
var ws = new WebSocket("ws://localhost:8080");
with
var ws = new WebSocket("ws://127.0.0.1:8080");
fixed the issue for me.
In C# console app I connect the client to the server using :
var aClient = new WebSocketClient(#"ws://127.0.0.1:81/beef");
Your code above is connecting using
var ws = new WebSocket("ws://localhost:8080");
There could be one of two issues -
First is to see if WebSocketClient works instead.
To make sure your url is of the format ws://ur:port/context. This threw me off for a while.

c# Socket Closing

I'm working on a http proxy application, everything is working fine (client can connect to server and get contents), the problem is neither of HTTP Server or browser close the TCP connection.. I'm not sure if I'm doing it right, here is the code:
while (tcp_link.Connected && _tcp.Connected && !ioError)
{
try
{
Thread.Sleep(100);
if (streamLink.DataAvailable)
{
byte[] l_buffer = new byte[10000];
int l_read = streamLink.Read(l_buffer, 0, l_buffer.Length);
byte[] l_data = new byte[l_read];
Array.Copy(l_buffer, l_data, l_data.Length);
_stream.Write(l_data, 0, l_data.Length);
}
if (_stream.DataAvailable)
{
byte[] c_buffer = new byte[10500];
int c_read = _stream.Read(c_buffer, 0, c_buffer.Length);
byte[] c_data = new byte[c_read];
Array.Copy(c_buffer, c_data, c_data.Length);
streamLink.Write(c_data, 0, c_data.Length);
}
}
catch
{
ioError = true;
}
}
I have same code both sides (proxy client, and proxy server)
NOTE: browser will connect to proxy client (which is on the same computer), and proxy client will connect to proxy server, and obviously proxy server will connect to http server, the reason is i wan't to encode data before sending it out
How long have you observed the connection open for?
It's very likely that the client uses HTTP 1.1 where Persistent Connections are on by default.
If you're writing a proxy then you should consider: http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html
Okay i found the problem, who ever is out there having problem with closing socket connection..
Actually, Socket.Available isn't working as i expected, to see if a socket is really connected you should check both Available and Poll properties, below function should resolve your problem: thanks to: zendar
bool SocketConnected(Socket s)
{
bool part1 = s.Poll(1000, SelectMode.SelectRead);
bool part2 = (s.Available == 0);
if (part1 & part2)
return false;
else
return true;
}
i hope this solve your problem too ;)

IPAddress.Parse() using port on IPv4

I'm trying to parse a string containing an IP address and a port using IPAddress.Parse. This works well with IPv6 addresses but not with IPv4 addresses. Can somone explain why this happens?
The code I'm using is:
IPAddress.Parse("[::1]:5"); //Valid
IPAddress.Parse("127.0.0.1:5"); //null
Uri url;
IPAddress ip;
if (Uri.TryCreate(String.Format("http://{0}", "127.0.0.1:5"), UriKind.Absolute, out url) &&
IPAddress.TryParse(url.Host, out ip))
{
IPEndPoint endPoint = new IPEndPoint(ip, url.Port);
}
This happens because the port is not part of the IP address. It belongs to TCP/UDP, and you'll have to strip it out first. The Uri class might be helpful for this.
IPAddress is not IP+Port. You want IPEndPoint.
Example from http://www.java2s.com/Code/CSharp/Network/ParseHostString.htm
public static void ParseHostString(string hostString, ref string hostName, ref int port)
{
hostName = hostString;
if (hostString.Contains(":"))
{
string[] hostParts = hostString.Split(':');
if (hostParts.Length == 2)
{
hostName = hostParts[0];
int.TryParse(hostParts[1], out port);
}
}
}
Edit: Ok, I'll admit that wasn't the most elegant solution. Try this one I wrote (just for you) instead:
// You need to include some usings:
using System.Text.RegularExpressions;
using System.Net;
// Then this code (static is not required):
private static Regex hostPortMatch = new Regex(#"^(?<ip>(?:\[[\da-fA-F:]+\])|(?:\d{1,3}\.){3}\d{1,3})(?::(?<port>\d+))?$", System.Text.RegularExpressions.RegexOptions.Compiled);
public static IPEndPoint ParseHostPort(string hostPort)
{
Match match = hostPortMatch.Match(hostPort);
if (!match.Success)
return null;
return new IPEndPoint(IPAddress.Parse(match.Groups["ip"].Value), int.Parse(match.Groups["port"].Value));
}
Note that this one ONLY accepts IP address, not hostname. If you want to support hostname you'll either have to resolve it to IP or not use IPAddress/IPEndPoint.
IPAddress.Parse is meant to take A string that contains an IP address in dotted-quad notation for IPv4 and in colon-hexadecimal notation for IPv6. So your first example works for IPv6 and your second example fails because it doesnt support a port for IPv4. Link http://msdn.microsoft.com/en-us/library/system.net.ipaddress.parse.aspx
As Tedd Hansen pointed out, what you are trying to parse is not an IP address but an IP endpoint (IP address + port). And since .NET Core 3.0, you can use IPEndPoint.TryParse to parse a string as an IPEndPoint:
if (IPEndPoint.TryParse("127.0.0.1:5", out IPEndPoint endpoint))
{
// parsed successfully, you can use the "endpoint" variable
Console.WriteLine(endpoint.Address.ToString()); // writes "127.0.0.1"
Console.WriteLine(endpoint.Port.ToString()); // writes "5"
}
else
{
// failed to parse
}
If you work on older versions of .net you can take IPEndPoint.Parse implementation from open source: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Primitives/src/System/Net/IPEndPoint.cs
To add my two cents... Since Microsoft itself implemented TryParse in NET Core 3.0 I've opted to stop using my custom IP+Port parser and kindly borrowed their code with some adaptations:
public static class IPEndPointParserExtension
{
public static bool TryParseAsIPEndPoint(this string s, out IPEndPoint result) {
#if NETCOREAPP3_0_OR_GREATER
return IPEndPoint.TryParse(s, out result);
#else
int addressLength = s.Length; // If there's no port then send the entire string to the address parser
int lastColonPos = s.LastIndexOf(':');
// Look to see if this is an IPv6 address with a port.
if (lastColonPos > 0) {
if (s[lastColonPos - 1] == ']')
addressLength = lastColonPos;
// Look to see if this is IPv4 with a port (IPv6 will have another colon)
else if (s.Substring(0, lastColonPos).LastIndexOf(':') == -1)
addressLength = lastColonPos;
}
if (IPAddress.TryParse(s.Substring(0, addressLength), out IPAddress address)) {
long port = 0;
if (addressLength == s.Length ||
(long.TryParse(s.Substring(addressLength + 1), out port)
&& port <= IPEndPoint.MaxPort)) {
result = new IPEndPoint(address, (int)port);
return true;
}
}
result = null;
return false;
#endif
}
public static IPEndPoint AsIPEndPoint(this string s) =>
s.TryParseAsIPEndPoint(out var endpoint)
? endpoint
: throw new FormatException($"'{s}' is not a valid IP Endpoint");
}
My changes were to basically exchange Span<char> for string and make it an extension method of the class String itself. I've also conditionally compile to use Microsoft's implementation if it is available (NET Core 3.0 or greater).
The following nUnit tests show how to use the code:
[Test]
public void CanParseIpv4WithPort() {
var sIp = "192.168.0.233:8080";
if (sIp.TryParseAsIPEndPoint(out var endpoint)) {
var expected = new IPEndPoint(new IPAddress(new byte[] { 192, 168, 0, 233 }), 8080);
Assert.AreEqual(expected, endpoint);
} else
Assert.Fail($"Failed to parse {sIp}");
}
[Test]
public void CanParseIpv6WithPort() {
var sIp = "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443";
if (sIp.TryParseAsIPEndPoint(out var endpoint)) {
var expected = new IPEndPoint(IPAddress.Parse("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 443);
Assert.AreEqual(expected, endpoint);
} else
Assert.Fail($"Failed to parse {sIp}");
}
You can also use AsIpEndPoint which will throw an exception if it fails to parse the IP address and port (port is optional):
var ep = "127.0.0.1:9000".AsIPEndPoint();

How to send arbitrary FTP commands in C#

I have implemented the ability to upload, download, delete, etc. using the FtpWebRequest class in C#. That is fairly straight forward.
What I need to do now is support sending arbitrary FTP commands such as
quote SITE LRECL=132 RECFM=FB
or
quote SYST
Here's an example configuration straight from our app.config:
<!-- The following commands will be executed before any uploads occur -->
<extraCommands>
<command>quote SITE LRECL=132 RECFM=FB</command>
</extraCommands>
I'm still researching how to do this using FtpWebRequest. I'll probably try WebClient class next. Anyone can point me in the right direction quicker? Thanks!
UPDATE:
I've come to that same conclusion, as of .NET Framework 3.5 FtpWebRequest doesn't support anything except what's in WebRequestMethods.Ftp.*. I'll try a third party app recommended by some of the other posts. Thanks for the help!
I don't think it can be done with FtpWebRequest... The only way to specify a FTP command is through the Method property, and the documentation states :
Note that the strings defined in the WebRequestMethods.Ftp class are the only supported options for the Method property. Setting the Method property to any other value will result in an ArgumentException exception.
SITE and SYST are not among the predefined options, so I guess you're stuck...
Don't waste time to try the WebClient class, it will give you even less flexibility than FtpWebRequest.
However, there are plenty of third-party FTP implementation, open source or commercial, and I'm pretty sure some of them can handle custom commands...
The FtpWebRequest won't help you as Thomas Levesque has said in his answer. You can use some third party solutions or the following, simplified TcpClient based code which I have refactored from an answer written in Visual Basic:
public static void SendFtpCommand()
{
var serverName = "[FTP_SERVER_NAME]";
var port = 21;
var userName = "[FTP_USER_NAME]";
var password = "[FTP_PASSWORD]"
var command = "SITE CHMOD 755 [FTP_FILE_PATH]";
var tcpClient = new TcpClient();
try
{
tcpClient.Connect(serverName, port);
Flush(tcpClient);
var response = TransmitCommand(tcpClient, "user " + userName);
if (response.IndexOf("331", StringComparison.OrdinalIgnoreCase) < 0)
throw new Exception(string.Format("Error \"{0}\" while sending user name \"{1}\".", response, userName));
response = TransmitCommand(tcpClient, "pass " + password);
if (response.IndexOf("230", StringComparison.OrdinalIgnoreCase) < 0)
throw new Exception(string.Format("Error \"{0}\" while sending password.", response));
response = TransmitCommand(tcpClient, command);
if (response.IndexOf("200", StringComparison.OrdinalIgnoreCase) < 0)
throw new Exception(string.Format("Error \"{0}\" while sending command \"{1}\".", response, command));
}
finally
{
if (tcpClient.Connected)
tcpClient.Close();
}
}
private static string TransmitCommand(TcpClient tcpClient, string cmd)
{
var networkStream = tcpClient.GetStream();
if (!networkStream.CanWrite || !networkStream.CanRead)
return string.Empty;
var sendBytes = Encoding.ASCII.GetBytes(cmd + "\r\n");
networkStream.Write(sendBytes, 0, sendBytes.Length);
var streamReader = new StreamReader(networkStream);
return streamReader.ReadLine();
}
private static string Flush(TcpClient tcpClient)
{
try
{
var networkStream = tcpClient.GetStream();
if (!networkStream.CanWrite || !networkStream.CanRead)
return string.Empty;
var receiveBytes = new byte[tcpClient.ReceiveBufferSize];
networkStream.ReadTimeout = 10000;
networkStream.Read(receiveBytes, 0, tcpClient.ReceiveBufferSize);
return Encoding.ASCII.GetString(receiveBytes);
}
catch
{
// Ignore all irrelevant exceptions
}
return string.Empty;
}
You can expect the following flow while getting through the FTP:
220 (vsFTPd 2.2.2)
user [FTP_USER_NAME]
331 Please specify the password.
pass [FTP_PASSWORD]
230 Login successful.
SITE CHMOD 755 [FTP_FILE_PATH]
200 SITE CHMOD command ok.
You can try our Rebex FTP component:
// create client and connect
Ftp client = new Ftp();
client.Connect("ftp.example.org");
client.Login("username", "password");
// send SITE command
// note that QUOTE and SITE are ommited. QUOTE is command line ftp syntax only.
client.Site("LRECL=132 RECFM=FB");
// send SYST command
client.SendCommand("SYST");
FtpResponse response = client.ReadResponse();
if (response.Group != 2)
; // handle error
// disconnect
client.Disconnect();
Use sendCommand("SITE LRECL=242 BLKSIZE=0 RECFM=FB");

Categories

Resources