/* ====================================================================
* Copyright (c) 2009 Andre Luis Azevedo (az.andrel@yahoo.com.br)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* In addition, the source code must keep original namespace names.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution. In addition, the binary form must keep the original
* namespace names and original file name.
*
* 3. The name "ALAZ" or "ALAZ Library" must not be used to endorse or promote
* products derived from this software without prior written permission.
*
* 4. Products derived from this software may not be called "ALAZ" or
* "ALAZ Library" nor may "ALAZ" or "ALAZ Library" appear in their
* names without prior written permission of the author.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
namespace ALAZ.SystemEx.NetEx.SocketsEx
{
public class ProxyUtils
{
#region GetProxyRequestData
internal static byte[] GetProxyRequestData(ProxyInfo proxyInfo, IPEndPoint remoteEndPoint)
{
byte[] result = null;
switch (proxyInfo.ProxyType)
{
case ProxyType.ptHTTP:
if (proxyInfo.ProxyCredential == null)
{
result = Encoding.GetEncoding(1252).GetBytes(String.Format("CONNECT {0}:{1} HTTP/1.1\r\nnHost: {0}:{1}\r\n\r\n", remoteEndPoint.Address, remoteEndPoint.Port));
}
else
{
string base64Encoding = Convert.ToBase64String(Encoding.GetEncoding(1252).GetBytes(proxyInfo.ProxyCredential.UserName + ":" + proxyInfo.ProxyCredential.Password));
result = Encoding.GetEncoding(1252).GetBytes(String.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}\r\nAuthorization: Basic {2}\r\nProxy-Authorization: Basic {2}\r\n\r\n", remoteEndPoint.Address, remoteEndPoint.Port, base64Encoding));
}
break;
case ProxyType.ptSOCKS4:
case ProxyType.ptSOCKS4a:
if (proxyInfo.ProxyType == ProxyType.ptSOCKS4)
{
if (proxyInfo.ProxyCredential == null)
{
result = new byte[8 + 1];
}
else
{
result = new byte[8 + proxyInfo.ProxyCredential.UserName.Length + 1];
}
}
else
{
if (proxyInfo.ProxyCredential == null)
{
result = new byte[8 + 1 + 1];
}
else
{
result = new byte[8 + proxyInfo.ProxyCredential.UserName.Length + 1 + proxyInfo.ProxyCredential.Domain.Length + 1];
}
}
result[0] = 4;
result[1] = 1;
result[2] = Convert.ToByte((remoteEndPoint.Port & 0xFF00) >> 8);
result[3] = Convert.ToByte(remoteEndPoint.Port & 0xFF);
if (proxyInfo.ProxyType == ProxyType.ptSOCKS4)
{
Buffer.BlockCopy(remoteEndPoint.Address.GetAddressBytes(), 0, result, 4, 4);
}
else
{
result[4] = 0;
result[5] = 0;
result[6] = 0;
result[7] = 1;
}
if ((proxyInfo.ProxyCredential != null) && (proxyInfo.ProxyCredential.UserName != null))
{
Buffer.BlockCopy(Encoding.GetEncoding(1252).GetBytes(proxyInfo.ProxyCredential.UserName), 0, result, 8, proxyInfo.ProxyCredential.UserName.Length);
}
if ((proxyInfo.ProxyType == ProxyType.ptSOCKS4a) && (proxyInfo.ProxyCredential != null))
{
Buffer.BlockCopy(Encoding.GetEncoding(1252).GetBytes(proxyInfo.ProxyCredential.Domain), 0, result, 8 + proxyInfo.ProxyCredential.UserName.Length + 1, proxyInfo.ProxyCredential.Domain.Length);
}
break;
case ProxyType.ptSOCKS5:
switch (proxyInfo.SOCKS5Phase)
{
case SOCKS5Phase.spIdle:
if (proxyInfo.ProxyCredential == null)
{
result = new byte[3];
result[0] = 5;
result[1] = 1;
result[2] = 0;
}
else
{
result = new byte[4];
result[0] = 5;
result[1] = 2;
result[2] = 0;
result[3] = 2;
}
proxyInfo.SOCKS5Phase = SOCKS5Phase.spGreeting;
break;
case SOCKS5Phase.spConnecting:
result = new byte[10];
result[0] = 5;
result[1] = 1;
result[2] = 0;
result[3] = 1;
Buffer.BlockCopy(remoteEndPoint.Address.GetAddressBytes(), 0, result, 4, 4);
result[8] = Convert.ToByte((remoteEndPoint.Port & 0xFF00) >> 8);
result[9] = Convert.ToByte(remoteEndPoint.Port & 0xFF);
break;
case SOCKS5Phase.spAuthenticating:
result = new byte[3 + proxyInfo.ProxyCredential.UserName.Length + proxyInfo.ProxyCredential.Password.Length];
result[0] = 5;
result[1] = Convert.ToByte(proxyInfo.ProxyCredential.UserName.Length);
Buffer.BlockCopy(Encoding.GetEncoding(1252).GetBytes(proxyInfo.ProxyCredential.UserName), 0, result, 2, proxyInfo.ProxyCredential.UserName.Length);
int passOffSet = 2 + proxyInfo.ProxyCredential.UserName.Length;
result[passOffSet] = Convert.ToByte(proxyInfo.ProxyCredential.Password.Length);
Buffer.BlockCopy(Encoding.GetEncoding(1252).GetBytes(proxyInfo.ProxyCredential.Password), 0, result, passOffSet + 1, proxyInfo.ProxyCredential.Password.Length);
break;
}
break;
}
return result;
}
#endregion
#region GetProxyResponseStatus
internal static void GetProxyResponseStatus(ProxyInfo proxyInfo, byte[] response)
{
switch (proxyInfo.ProxyType)
{
case ProxyType.ptHTTP:
Match m = Regex.Match(Encoding.GetEncoding(1252).GetString(response), @"[HTTP/]\d[.]\d\s(?<code>\d+)\s(?<reason>.+)");
if (m.Success)
{
int code = Convert.ToInt32(m.Groups["code"].Value);
if (code >= 200 && code <= 299)
{
proxyInfo.Completed = true;
}
else
{
throw new ProxyAuthenticationException(code, m.Groups["reason"].Value);
}
}
else
{
throw new ProxyAuthenticationException(0, "Invalid proxy message response.");
}
break;
case ProxyType.ptSOCKS4:
case ProxyType.ptSOCKS4a:
if (response[1] == 0x5A)
{
proxyInfo.Completed = true;
}
else
{
switch (response[1])
{
case 0x5B:
throw new ProxyAuthenticationException(response[1], "Request rejected or failed.");
case 0x5C:
throw new ProxyAuthenticationException(response[1], "Client is not running identd.");
case 0x5D:
throw new ProxyAuthenticationException(response[1], "Client's identd could not confirm the user ID string in the request.");
}
}
break;
case ProxyType.ptSOCKS5:
switch (proxyInfo.SOCKS5Phase)
{
case SOCKS5Phase.spGreeting:
if (response[1] != 0xFF)
{
proxyInfo.SOCKS5Authentication = (SOCKS5AuthMode)Enum.ToObject(typeof(SOCKS5AuthMode), response[1]);
switch (proxyInfo.SOCKS5Authentication)
{
case SOCKS5AuthMode.saNoAuth:
proxyInfo.SOCKS5Phase = SOCKS5Phase.spConnecting;
break;
case SOCKS5AuthMode.ssUserPass:
proxyInfo.SOCKS5Phase = SOCKS5Phase.spAuthenticating;
break;
}
}
else
{
throw new ProxyAuthenticationException(0xFF, "Authentication method not supported.");
}
break;
case SOCKS5Phase.spConnecting:
case SOCKS5Phase.spAuthenticating:
if (response[1] == 0x00)
{
switch (proxyInfo.SOCKS5Phase)
{
case SOCKS5Phase.spConnecting:
proxyInfo.Completed = true;
break;
case SOCKS5Phase.spAuthenticating:
proxyInfo.SOCKS5Phase = SOCKS5Phase.spConnecting;
break;
}
}
else
{
switch (response[1])
{
case 0x01:
throw new ProxyAuthenticationException(response[1], "General Failure.");
case 0x02:
throw new ProxyAuthenticationException(response[1], "Connection not allowed by ruleset.");
case 0x03:
throw new ProxyAuthenticationException(response[1], "Network unreachable.");
case 0x04:
throw new ProxyAuthenticationException(response[1], "Host unreachable.");
case 0x05:
throw new ProxyAuthenticationException(response[1], "Connection refused by destination host.");
case 0x06:
throw new ProxyAuthenticationException(response[1], "TTL expired.");
case 0x07:
throw new ProxyAuthenticationException(response[1], "Command not supported / protocol error.");
case 0x08:
throw new ProxyAuthenticationException(response[1], "Address type not supported.");
}
}
break;
}
break;
}
}
#endregion
}
}