Add the files

This commit is contained in:
iPedroLB 2023-07-15 01:45:29 -03:00
parent f52694709e
commit 50aabc8afc
312 changed files with 95444 additions and 0 deletions

View file

@ -0,0 +1,88 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Reflection;
using System.Runtime.CompilerServices;
/*
General Information about an assembly is controlled through the following
set of attributes. Change these attribute values to modify the information
associated with an assembly.
*/
[assembly: AssemblyTitle("Mentalis Proxy")]
[assembly: AssemblyDescription("An open source Proxy server, written in C#")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("The KPD-Team")]
[assembly: AssemblyProduct("Mentalis Proxy")]
[assembly: AssemblyCopyright("Copyright © The KPD-Team, 2002")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
/*
Version information for an assembly consists of the following four values:
Major Version
Minor Version
Build Number
Revision
You can specify all the values or you can default the Revision and Build Numbers
by using the '*' as shown below:
*/
[assembly: AssemblyVersion("1.0.0.*")]
/*
In order to sign your assembly you must specify a key to use. Refer to the
Microsoft .NET Framework documentation for more information on assembly signing.
Use the attributes below to control which key is used for signing.
Notes:
(*) If no key is specified, the assembly is not signed.
(*) KeyName refers to a key that has been installed in the Crypto Service
Provider (CSP) on your machine. KeyFile refers to a file which contains
a key.
(*) If the KeyFile and the KeyName values are both specified, the
following processing occurs:
(1) If the KeyName can be found in the CSP, that key is used.
(2) If the KeyName does not exist and the KeyFile does exist, the key
in the KeyFile is installed into the CSP and used.
(*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
When specifying the KeyFile, the location of the KeyFile should be
relative to the project output directory which is
%Project Directory%\obj\<configuration>. For example, if your KeyFile is
located in the project directory, you would specify the AssemblyKeyFile
attribute as <assembly: AssemblyKeyFile("..\\..\\mykey.snk")>
(*) Delay Signing is an advanced option - see the Microsoft .NET Framework
documentation for more information on this.
*/
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

108
ProxyServer/AuthBase.cs Normal file
View file

@ -0,0 +1,108 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using Org.Mentalis.Proxy;
using Org.Mentalis.Proxy.Socks;
namespace Org.Mentalis.Proxy.Socks.Authentication {
///<summary>Defines the signature of the method to be called when the authentication is complete.</summary>
///<param name="Success">Specifies whether the authentication was successfull or not.</param>
internal delegate void AuthenticationCompleteDelegate(bool Success);
///<summary>Authenticates a user on a SOCKS5 server according to the implemented subprotocol.</summary>
///<remarks>This is an abstract class. The subprotocol that's used to authenticate a user is specified in the subclasses of this base class.</remarks>
internal abstract class AuthBase {
///<summary>Initializes a new instance of the AuthBase class.</summary>
public AuthBase() {}
///<summary>Starts the authentication process.</summary>
///<remarks>This abstract method must be implemented in the subclasses, according to the selected subprotocol.</remarks>
///<param name="Connection">The connection with the SOCKS client.</param>
///<param name="Callback">The method to call when the authentication is complete.</param>
internal abstract void StartAuthentication(Socket Connection, AuthenticationCompleteDelegate Callback);
///<summary>Gets or sets the Socket connection between the proxy server and the SOCKS client.</summary>
///<value>A Socket instance defining the connection between the proxy server and the local client.</value>
protected Socket Connection {
get {
return m_Connection;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Connection = value;
}
}
///<summary>Gets a buffer that can be used to receive data from the client connection.</summary>
///<value>An array of bytes that can be used to receive data from the client connection.</value>
protected byte [] Buffer {
get {
return m_Buffer;
}
}
///<summary>Gets or sets an array of bytes that can be used to store all received data.</summary>
///<value>An array of bytes that can be used to store all received data.</value>
protected byte [] Bytes {
get {
return m_Bytes;
}
set {
m_Bytes = value;
}
}
///<summary>Adds bytes to the array returned by the Bytes property.</summary>
///<param name="NewBytes">The bytes to add.</param>
///<param name="Cnt">The number of bytes to add.</param>
protected void AddBytes(byte [] NewBytes, int Cnt) {
if (Cnt <= 0 || NewBytes == null || Cnt > NewBytes.Length)
return;
if (Bytes == null) {
Bytes = new byte[Cnt];
} else {
byte [] tmp = Bytes;
Bytes = new byte[Bytes.Length + Cnt];
Array.Copy(tmp, 0, Bytes, 0, tmp.Length);
}
Array.Copy(NewBytes, 0, Bytes, Bytes.Length - Cnt, Cnt);
}
///<summary>The method to call when the authentication is complete.</summary>
protected AuthenticationCompleteDelegate Callback;
// private variables
/// <summary>Holds the value of the Connection property.</summary>
private Socket m_Connection;
/// <summary>Holds the value of the Buffer property.</summary>
private byte [] m_Buffer = new byte[1024];
/// <summary>Holds the value of the Bytes property.</summary>
private byte [] m_Bytes;
}
}

48
ProxyServer/AuthNone.cs Normal file
View file

@ -0,0 +1,48 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using Org.Mentalis.Proxy.Socks.Authentication;
namespace Org.Mentalis.Proxy.Socks.Authentication {
///<summary>Authenticates a user on a SOCKS5 server according to the 'No Authentication' subprotocol.</summary>
internal sealed class AuthNone : AuthBase {
///<summary>Initializes a new instance of the AuthNone class.</summary>
public AuthNone() {}
///<summary>Calls the parent class to inform it authentication is complete.</summary>
///<param name="Connection">The connection with the SOCKS client.</param>
///<param name="Callback">The method to call when the authentication is complete.</param>
internal override void StartAuthentication(Socket Connection, AuthenticationCompleteDelegate Callback) {
Callback(true);
}
}
}

143
ProxyServer/AuthUserPass.cs Normal file
View file

@ -0,0 +1,143 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Text;
using System.Net;
using System.Net.Sockets;
using Org.Mentalis.Proxy;
using Org.Mentalis.Proxy.Socks;
using Org.Mentalis.Proxy.Socks.Authentication;
namespace Org.Mentalis.Proxy.Socks.Authentication {
///<summary>Authenticates a user on a SOCKS5 server according to the username/password authentication subprotocol.</summary>
internal sealed class AuthUserPass : AuthBase {
///<summary>Initializes a new instance of the AuthUserPass class.</summary>
///<param name="AuthList">An AuthenticationList object that contains the list of all valid username/password combinations.</param>
///<remarks>If the AuthList parameter is null, any username/password combination will be accepted.</remarks>
public AuthUserPass(AuthenticationList AuthList) {
this.AuthList = AuthList;
}
///<summary>Starts the authentication process.</summary>
///<param name="Connection">The connection with the SOCKS client.</param>
///<param name="Callback">The method to call when the authentication is complete.</param>
internal override void StartAuthentication(Socket Connection, AuthenticationCompleteDelegate Callback) {
this.Connection = Connection;
this.Callback = Callback;
try {
Bytes = null;
Connection.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnRecvRequest), Connection);
} catch {
Callback(false);
}
}
///<summary>Called when we have received the initial authentication data from the SOCKS client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnRecvRequest(IAsyncResult ar) {
try {
int Ret = Connection.EndReceive(ar);
if (Ret <= 0) {
Callback(false);
return;
}
AddBytes(Buffer, Ret);
if (IsValidQuery(Bytes))
ProcessQuery(Bytes);
else
Connection.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnRecvRequest), Connection);
} catch {
Callback(false);
}
}
///<summary>Checks whether the specified authentication query is a valid one.</summary>
///<param name="Query">The query to check.</param>
///<returns>True if the query is a valid authentication query, false otherwise.</returns>
private bool IsValidQuery(byte [] Query) {
try {
return (Query.Length == Query[1] + Query[Query[1] + 2] + 3);
} catch {
return false;
}
}
///<summary>Processes an authentication query.</summary>
///<param name="Query">The query to process.</param>
private void ProcessQuery(byte [] Query) {
try {
string User = Encoding.ASCII.GetString(Query, 2, Query[1]);
string Pass = Encoding.ASCII.GetString(Query, Query[1] + 3, Query[Query[1] + 2]);
byte [] ToSend;
if (AuthList == null || AuthList.IsItemPresent(User, Pass)) {
ToSend = new byte[]{5, 0};
Connection.BeginSend(ToSend, 0, ToSend.Length, SocketFlags.None, new AsyncCallback(this.OnOkSent), Connection);
} else {
ToSend = new Byte[]{5, 1};
Connection.BeginSend(ToSend, 0, ToSend.Length, SocketFlags.None, new AsyncCallback(this.OnUhohSent), Connection);
}
} catch {
Callback(false);
}
}
///<summary>Called when an OK reply has been sent to the client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnOkSent(IAsyncResult ar) {
try {
if (Connection.EndSend(ar) <= 0)
Callback(false);
else
Callback(true);
} catch {
Callback(false);
}
}
///<summary>Called when a negatiev reply has been sent to the client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnUhohSent(IAsyncResult ar) {
try {
Connection.EndSend(ar);
} catch {}
Callback(false);
}
///<summary>Gets or sets the AuthenticationList to use when a computer tries to authenticate on the proxy server.</summary>
///<value>An instance of the AuthenticationList class that contains all the valid username/password combinations.</value>
private AuthenticationList AuthList {
get {
return m_AuthList;
}
set {
m_AuthList = value;
}
}
// private variables
/// <summary>Holds the value of the AuthList property.</summary>
private AuthenticationList m_AuthList;
}
}

View file

@ -0,0 +1,132 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Text;
using System.Security.Cryptography;
using System.Collections;
using System.Collections.Specialized;
namespace Org.Mentalis.Proxy.Socks.Authentication {
///<summary>Stores a dictionary with username/password combinations.</summary>
///<remarks>This class can be used by a SOCKS5 listener.</remarks>
///<remarks>This class uses an MD5 has to store the passwords in a secure manner.</remarks>
///<remarks>The username is treated in a case-insensitive manner, the password is treated case-sensitive.</remarks>
public class AuthenticationList {
///<summary>Initializes a new instance of the AuthenticationList class.</summary>
public AuthenticationList() {}
///<summary>Adds an item to the list.</summary>
///<param name="Username">The username to add.</param>
///<param name="Password">The corresponding password to add.</param>
///<exception cref="ArgumentNullException">Either Username or Password is null.</exception>
public void AddItem(string Username, string Password) {
if (Password == null)
throw new ArgumentNullException();
AddHash(Username, Convert.ToBase64String(new MD5CryptoServiceProvider().ComputeHash(Encoding.ASCII.GetBytes(Password))));
}
///<summary>Adds an item to the list.</summary>
///<param name="Username">The username to add.</param>
///<param name="PassHash">The hashed password to add.</param>
///<exception cref="ArgumentNullException">Either Username or Password is null.</exception>
public void AddHash(string Username, string PassHash) {
if (Username == null || PassHash == null)
throw new ArgumentNullException();
if (Listing.ContainsKey(Username)) {
Listing[Username] = PassHash;
} else {
Listing.Add(Username, PassHash);
}
}
///<summary>Removes an item from the list.</summary>
///<param name="Username">The username to remove.</param>
///<exception cref="ArgumentNullException">Username is null.</exception>
public void RemoveItem(string Username) {
if (Username == null)
throw new ArgumentNullException();
Listing.Remove(Username);
}
///<summary>Checks whether a user/pass combination is present in the collection or not.</summary>
///<param name="Username">The username to search for.</param>
///<param name="Password">The corresponding password to search for.</param>
///<returns>True when the user/pass combination is present in the collection, false otherwise.</returns>
public bool IsItemPresent(string Username, string Password) {
return IsHashPresent(Username, Convert.ToBase64String(new MD5CryptoServiceProvider().ComputeHash(Encoding.ASCII.GetBytes(Password))));
}
///<summary>Checks whether a username is present in the collection or not.</summary>
///<param name="Username">The username to search for.</param>
///<returns>True when the username is present in the collection, false otherwise.</returns>
public bool IsUserPresent(string Username) {
return Listing.ContainsKey(Username);
}
///<summary>Checks whether a user/passhash combination is present in the collection or not.</summary>
///<param name="Username">The username to search for.</param>
///<param name="PassHash">The corresponding password hash to search for.</param>
///<returns>True when the user/passhash combination is present in the collection, false otherwise.</returns>
public bool IsHashPresent(string Username, string PassHash) {
return Listing.ContainsKey(Username) && Listing[Username].Equals(PassHash);
}
///<summary>Gets the StringDictionary that's used to store the user/pass combinations.</summary>
///<value>A StringDictionary object that's used to store the user/pass combinations.</value>
protected StringDictionary Listing {
get {
return m_Listing;
}
}
///<summary>Gets an array with all the keys in the authentication list.</summary>
///<value>An array of strings containing all the keys in the authentication list.</value>
public string[] Keys {
get {
ICollection keys = Listing.Keys;
string [] ret = new string[keys.Count];
keys.CopyTo(ret, 0);
return ret;
}
}
///<summary>Gets an array with all the hashes in the authentication list.</summary>
///<value>An array of strings containing all the hashes in the authentication list.</value>
public string[] Hashes {
get {
ICollection values = Listing.Values;
string [] ret = new string[values.Count];
values.CopyTo(ret, 0);
return ret;
}
}
///<summary>Clears the authentication list.</summary>
public void Clear() {
Listing.Clear();
}
// private variables
/// <summary>Holds the value of the Listing property.</summary>
private StringDictionary m_Listing = new StringDictionary();
}
}

206
ProxyServer/Client.cs Normal file
View file

@ -0,0 +1,206 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
namespace Org.Mentalis.Proxy {
/// <summary>References the callback method to be called when the <c>Client</c> object disconnects from the local client and the remote server.</summary>
/// <param name="client">The <c>Client</c> that has closed its connections.</param>
public delegate void DestroyDelegate(Client client);
///<summary>Specifies the basic methods and properties of a <c>Client</c> object. This is an abstract class and must be inherited.</summary>
///<remarks>The Client class provides an abstract base class that represents a connection to a local client and a remote server. Descendant classes further specify the protocol that is used between those two connections.</remarks>
public abstract class Client : IDisposable {
///<summary>Initializes a new instance of the Client class.</summary>
///<param name="ClientSocket">The <see cref ="Socket">Socket</see> connection between this proxy server and the local client.</param>
///<param name="Destroyer">The callback method to be called when this Client object disconnects from the local client and the remote server.</param>
public Client(Socket ClientSocket, DestroyDelegate Destroyer) {
this.ClientSocket = ClientSocket;
this.Destroyer = Destroyer;
}
///<summary>Initializes a new instance of the Client object.</summary>
///<remarks>Both the ClientSocket property and the DestroyDelegate are initialized to null.</remarks>
public Client() {
this.ClientSocket = null;
this.Destroyer = null;
}
///<summary>Gets or sets the Socket connection between the proxy server and the local client.</summary>
///<value>A Socket instance defining the connection between the proxy server and the local client.</value>
///<seealso cref ="DestinationSocket"/>
internal Socket ClientSocket {
get {
return m_ClientSocket;
}
set {
if (m_ClientSocket != null)
m_ClientSocket.Close();
m_ClientSocket = value;
}
}
///<summary>Gets or sets the Socket connection between the proxy server and the remote host.</summary>
///<value>A Socket instance defining the connection between the proxy server and the remote host.</value>
///<seealso cref ="ClientSocket"/>
internal Socket DestinationSocket {
get {
return m_DestinationSocket;
}
set {
if (m_DestinationSocket != null)
m_DestinationSocket.Close();
m_DestinationSocket = value;
}
}
///<summary>Gets the buffer to store all the incoming data from the local client.</summary>
///<value>An array of bytes that can be used to store all the incoming data from the local client.</value>
///<seealso cref ="RemoteBuffer"/>
protected byte[] Buffer {
get {
return m_Buffer;
}
}
///<summary>Gets the buffer to store all the incoming data from the remote host.</summary>
///<value>An array of bytes that can be used to store all the incoming data from the remote host.</value>
///<seealso cref ="Buffer"/>
protected byte[] RemoteBuffer {
get {
return m_RemoteBuffer;
}
}
///<summary>Disposes of the resources (other than memory) used by the Client.</summary>
///<remarks>Closes the connections with the local client and the remote host. Once <c>Dispose</c> has been called, this object should not be used anymore.</remarks>
///<seealso cref ="System.IDisposable"/>
public void Dispose() {
try {
ClientSocket.Shutdown(SocketShutdown.Both);
} catch {}
try {
DestinationSocket.Shutdown(SocketShutdown.Both);
} catch {}
//Close the sockets
if (ClientSocket != null)
ClientSocket.Close();
if (DestinationSocket != null)
DestinationSocket.Close();
//Clean up
ClientSocket = null;
DestinationSocket = null;
if (Destroyer != null)
Destroyer(this);
}
///<summary>Returns text information about this Client object.</summary>
///<returns>A string representing this Client object.</returns>
public override string ToString() {
try {
return "Incoming connection from " + ((IPEndPoint)DestinationSocket.RemoteEndPoint).Address.ToString();
} catch {
return "Client connection";
}
}
///<summary>Starts relaying data between the remote host and the local client.</summary>
///<remarks>This method should only be called after all protocol specific communication has been finished.</remarks>
public void StartRelay() {
try {
ClientSocket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnClientReceive), ClientSocket);
DestinationSocket.BeginReceive(RemoteBuffer, 0, RemoteBuffer.Length, SocketFlags.None, new AsyncCallback(this.OnRemoteReceive), DestinationSocket);
} catch {
Dispose();
}
}
///<summary>Called when we have received data from the local client.<br>Incoming data will immediately be forwarded to the remote host.</br></summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected void OnClientReceive(IAsyncResult ar) {
try {
int Ret = ClientSocket.EndReceive(ar);
if (Ret <= 0) {
Dispose();
return;
}
DestinationSocket.BeginSend(Buffer, 0, Ret, SocketFlags.None, new AsyncCallback(this.OnRemoteSent), DestinationSocket);
} catch {
Dispose();
}
}
///<summary>Called when we have sent data to the remote host.<br>When all the data has been sent, we will start receiving again from the local client.</br></summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected void OnRemoteSent(IAsyncResult ar) {
try {
int Ret = DestinationSocket.EndSend(ar);
if (Ret > 0) {
ClientSocket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnClientReceive), ClientSocket);
return;
}
} catch {}
Dispose();
}
///<summary>Called when we have received data from the remote host.<br>Incoming data will immediately be forwarded to the local client.</br></summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected void OnRemoteReceive(IAsyncResult ar) {
try {
int Ret = DestinationSocket.EndReceive(ar);
if (Ret <= 0){
Dispose();
return;
}
ClientSocket.BeginSend(RemoteBuffer, 0, Ret, SocketFlags.None, new AsyncCallback(this.OnClientSent), ClientSocket);
} catch {
Dispose();
}
}
///<summary>Called when we have sent data to the local client.<br>When all the data has been sent, we will start receiving again from the remote host.</br></summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected void OnClientSent(IAsyncResult ar) {
try {
int Ret = ClientSocket.EndSend(ar);
if (Ret > 0) {
DestinationSocket.BeginReceive(RemoteBuffer, 0, RemoteBuffer.Length, SocketFlags.None, new AsyncCallback(this.OnRemoteReceive), DestinationSocket);
return;
}
} catch {}
Dispose();
}
///<summary>Starts communication with the local client.</summary>
public abstract void StartHandshake();
// private variables
/// <summary>Holds the address of the method to call when this client is ready to be destroyed.</summary>
private DestroyDelegate Destroyer;
/// <summary>Holds the value of the ClientSocket property.</summary>
private Socket m_ClientSocket;
/// <summary>Holds the value of the DestinationSocket property.</summary>
private Socket m_DestinationSocket;
/// <summary>Holds the value of the Buffer property.</summary>
private byte[] m_Buffer = new byte[4096]; //0<->4095 = 4096
/// <summary>Holds the value of the RemoteBuffer property.</summary>
private byte[] m_RemoteBuffer = new byte[1024];
}
}

View file

@ -0,0 +1,478 @@
/*
ConsoleAttributes class for C#
Version: 1.0 Date: 2002/04/14
*/
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Text;
using System.Runtime.InteropServices;
// <summary>The Org.Mentalis.Utilities.ConsoleAttributes namespace defines classes that can be used to interact with the console to change its layout and behavior.</summary>
namespace Org.Mentalis.Utilities.ConsoleAttributes {
/// <summary>
/// The CONSOLE_CURSOR_INFO structure contains information about the console cursor.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct CONSOLE_CURSOR_INFO {
/// <summary>Specifies a number between 1 and 100, indicating the percentage of the character cell that is filled by the cursor. The cursor appearance varies, ranging from completely filling the cell to showing up as a horizontal line at the bottom of the cell.</summary>
public int dwSize;
/// <summary>Specifies the visibility of the cursor. If the cursor is visible, this member is TRUE (nonzero).</summary>
public int bVisible;
}
/// <summary>
/// The COORD structure defines the coordinates of a character cell in a console screen buffer. The origin of the coordinate system (0,0) is at the top, left cell of the buffer.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct COORD {
/// <summary>Horizontal or column value.</summary>
public short x;
/// <summary>Vertical or row value.</summary>
public short y;
}
/// <summary>
/// The SMALL_RECT structure defines the coordinates of the upper left and lower right corners of a rectangle.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct SMALL_RECT {
/// <summary>Specifies the x-coordinate of the upper left corner of the rectangle.</summary>
public short Left;
/// <summary>Specifies the y-coordinate of the upper left corner of the rectangle.</summary>
public short Top;
/// <summary>Specifies the x-coordinate of the lower right corner of the rectangle.</summary>
public short Right;
/// <summary>Specifies the y-coordinate of the lower right corner of the rectangle.</summary>
public short Bottom;
}
/// <summary>
/// The CONSOLE_SCREEN_BUFFER_INFO structure contains information about a console screen buffer.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct CONSOLE_SCREEN_BUFFER_INFO {
/// <summary>Specifies the size, in character columns and rows, of the screen buffer.</summary>
public COORD dwSize;
/// <summary>Specifies the column and row coordinates of the cursor in the screen buffer.</summary>
public COORD dwCursorPosition;
/// <summary>Specifies the foreground (text) and background color attributes to be used for characters that are written to a screen buffer by the WriteFile and WriteConsole functions, or echoed to a screen buffer by the ReadFile and ReadConsole functions. The attribute values are some combination of the following values: FOREGROUND_BLUE, FOREGROUND_GREEN, FOREGROUND_RED, FOREGROUND_INTENSITY, BACKGROUND_BLUE, BACKGROUND_GREEN, BACKGROUND_RED, and BACKGROUND_INTENSITY.</summary>
public short wAttributes;
/// <summary>Specifies a SMALL_RECT structure that contains the screen buffer coordinates of the upper-left and lower-right corners of the display window.</summary>
public SMALL_RECT srWindow;
/// <summary>Specifies the maximum size of the console window, given the current screen buffer size and font and the screen size.</summary>
public COORD dwMaximumWindowSize;
}
/// <summary>Enumerates all available colors for the forecolor or the backcolor of the console.</summary>
public enum ConsoleColor : int {
/// <summary>Black</summary>
Black = 0,
/// <summary>Red</summary>
Red = 1,
/// <summary>Light red</summary>
LightRed = 2,
/// <summary>Green</summary>
Green = 3,
/// <summary>Light green</summary>
LightGreen = 4,
/// <summary>Blue</summary>
Blue = 5,
/// <summary>Light blue</summary>
LightBlue = 6,
/// <summary>Gold</summary>
Gold = 7,
/// <summary>Yellow</summary>
Yellow = 8,
/// <summary>Cyan</summary>
Cyan = 9,
/// <summary>Light cyan</summary>
LightCyan = 10,
/// <summary>Purple</summary>
Purple = 11,
/// <summary>Light purple</summary>
LightPurple = 12,
/// <summary>Gray</summary>
Gray = 13,
/// <summary>White</summary>
White = 14
}
/// <summary>The ConsoleAttributes class can change several attributes of your console window.</summary>
/// <example>
/// The following example wil change the forecolor of te console, disable 'EchoInput', ask for a string and show that string.
/// <code>
/// ConsoleAttributes.ForeColor = ConsoleColor.White;
/// Console.Write("Please enter your password: ");
/// ConsoleAttributes.EchoInput = false;
/// string ThePass = Console.ReadLine();
/// ConsoleAttributes.EchoInput = true;
/// ConsoleAttributes.ForeColor = ConsoleColor.Gray;
/// Console.WriteLine("");
/// Console.WriteLine("The password you entered was: " + ThePass);
/// Console.WriteLine("Press enter to exit...");
/// Console.Read();
/// </code>
/// </example>
public class ConsoleAttributes {
/// <summary>
/// Lists all the possible background color values.
/// </summary>
private static int [] BacgroundColors = {0x0, 0x40, 0x80 | 0x40, 0x20, 0x80 | 0x20, 0x10, 0x80 | 0x10, 0x40 | 0x20, 0x80 | 0x40 | 0x20, 0x20 | 0x10, 0x80 | 0x20 | 0x10, 0x40 | 0x10, 0x80 | 0x40 | 0x10, 0x40 | 0x20 | 0x10, 0x80 | 0x40 | 0x20 | 0x10};
/// <summary>
/// Lists all the possible foreground color values.
/// </summary>
private static int [] ForegroundColors = {0x0, 0x4, 0x8 | 0x4, 0x2, 0x8 | 0x2, 0x1, 0x8 | 0x1, 0x4 | 0x2, 0x8 | 0x4 | 0x2, 0x2 | 0x1, 0x8 | 0x2 | 0x1, 0x4 | 0x1, 0x8 | 0x4 | 0x1, 0x4 | 0x2 | 0x1, 0x8 | 0x4 | 0x2 | 0x1};
/// <summary>
/// The SetConsoleTextAttribute function sets the foreground (text) and background color attributes of characters written to the screen buffer by the WriteFile or WriteConsole function, or echoed by the ReadFile or ReadConsole function. This function affects only text written after the function call.
/// </summary>
/// <param name="hConsoleOutput">Handle to a console screen buffer. The handle must have GENERIC_READ access.</param>
/// <param name="wAttributes">Specifies the foreground and background color attributes. Any combination of the following values can be specified: FOREGROUND_BLUE, FOREGROUND_GREEN, FOREGROUND_RED, FOREGROUND_INTENSITY, BACKGROUND_BLUE, BACKGROUND_GREEN, BACKGROUND_RED, and BACKGROUND_INTENSITY.</param>
/// <returns>If the function succeeds, the return value is nonzero.<br></br><br>If the function fails, the return value is zero. To get extended error information, call GetLastError.</br></returns>
[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleTextAttribute", CharSet=CharSet.Ansi)]
internal static extern int SetConsoleTextAttribute (int hConsoleOutput, int wAttributes);
/// <summary>
/// The GetStdHandle function returns a handle for the standard input, standard output, or standard error device.
/// </summary>
/// <param name="nStdHandle">Specifies the device for which to return the handle. This parameter can have one of the following values:
/// <list type="bullet">
/// <listheader>
/// <value>Value</value>
/// <meaning>Meaning</meaning>
/// </listheader>
/// <item>
/// <value>STD_INPUT_HANDLE</value>
/// <meaning>Standard input handle.</meaning>
/// </item>
/// <item>
/// <value>STD_OUTPUT_HANDLE</value>
/// <meaning>Standard output handle.</meaning>
/// </item>
/// <item>
/// <value>STD_ERROR_HANDLE</value>
/// <meaning>Standard error handle.</meaning>
/// </item>
/// </list>
/// </param>
/// <returns>If the function succeeds, the return value is a handle to the specified device.<br></br><br>If the function fails, the return value is the INVALID_HANDLE_VALUE flag. To get extended error information, call GetLastError.</br></returns>
[DllImport("KERNEL32.DLL", EntryPoint="GetStdHandle")]
internal static extern int GetStdHandle (int nStdHandle);
/// <summary>
/// The SetConsoleCursorInfo function sets the size and visibility of the cursor for the specified console screen buffer.
/// </summary>
/// <param name="hConsoleOutput">Handle to a console screen buffer. The handle must have GENERIC_WRITE access.</param>
/// <param name="lpConsoleCursorInfo">Pointer to a CONSOLE_CURSOR_INFO structure containing the new specifications for the screen buffer's cursor.</param>
/// <returns>If the function succeeds, the return value is nonzero.<br></br><br>If the function fails, the return value is zero. To get extended error information, call GetLastError.</br></returns>
[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleCursorInfo")]
internal static extern int SetConsoleCursorInfo (int hConsoleOutput, ref CONSOLE_CURSOR_INFO lpConsoleCursorInfo);
/// <summary>
/// The GetConsoleMode function reports the current input mode of a console's input buffer or the current output mode of a console screen buffer.
/// </summary>
/// <param name="hConsoleHandle">Handle to a console input buffer or a screen buffer. The handle must have GENERIC_READ access.</param>
/// <param name="lpConsoleCursorInfo">
/// Pointer to a 32-bit variable that indicates the current mode of the specified buffer.<br>If the hConsoleHandle parameter is an input handle, the mode can be a combination of the following values. When a console is created, all input modes except ENABLE_WINDOW_INPUT are enabled by default.</br>
/// <list type="bullet">
/// <listheader>
/// <value>Value</value>
/// <meaning>Meaning</meaning>
/// </listheader>
/// <item>
/// <value>ENABLE_LINE_INPUT</value>
/// <meaning>The ReadFile or ReadConsole function returns only when a carriage return character is read. If this mode is disabled, the functions return when one or more characters are available.</meaning>
/// </item>
/// <item>
/// <value>ENABLE_ECHO_INPUT</value>
/// <meaning>Characters read by the ReadFile or ReadConsole function are written to the active screen buffer as they are read. This mode can be used only if the ENABLE_LINE_INPUT mode is also enabled.</meaning>
/// </item>
/// <item>
/// <value>ENABLE_PROCESSED_INPUT</value>
/// <meaning>ctrl+c is processed by the system and is not placed in the input buffer. If the input buffer is being read by ReadFile or ReadConsole, other control keys are processed by the system and are not returned in the ReadFile or ReadConsole buffer. If the ENABLE_LINE_INPUT mode is also enabled, backspace, carriage return, and linefeed characters are handled by the system.</meaning>
/// </item>
/// <item>
/// <value>ENABLE_WINDOW_INPUT</value>
/// <meaning>User interactions that change the size of the console screen buffer are reported in the console's input buffer. Information about these events can be read from the input buffer by applications using the ReadConsoleInput function, but not by those using ReadFile or ReadConsole.</meaning>
/// </item>
/// <item>
/// <value>ENABLE_MOUSE_INPUT</value>
/// <meaning>If the mouse pointer is within the borders of the console window and the window has the keyboard focus, mouse events generated by mouse movement and button presses are placed in the input buffer. These events are discarded by ReadFile or ReadConsole, even when this mode is enabled.</meaning>
/// </item>
/// </list>
/// If the hConsoleHandle parameter is a screen buffer handle, the mode can be a combination of the following values. When a screen buffer is created, both output modes are enabled by default.
/// <list type="bullet">
/// <listheader>
/// <value>Value</value>
/// <meaning>Meaning</meaning>
/// </listheader>
/// <item>
/// <value>ENABLE_PROCESSED_OUTPUT</value>
/// <meaning>Characters written by the WriteFile or WriteConsole function or echoed by the ReadFile or ReadConsole function are parsed for ASCII control sequences, and the correct action is performed. Backspace, tab, bell, carriage return, and linefeed characters are processed.</meaning>
/// </item>
/// <item>
/// <value>ENABLE_WRAP_AT_EOL_OUTPUT</value>
/// <meaning>When writing with WriteFile or WriteConsole or echoing with ReadFile or ReadConsole, the cursor moves to the beginning of the next row when it reaches the end of the current row. This causes the rows displayed in the console window to scroll up automatically when the cursor advances beyond the last row in the window. It also causes the contents of the screen buffer to scroll up (discarding the top row of the screen buffer) when the cursor advances beyond the last row in the screen buffer. If this mode is disabled, the last character in the row is overwritten with any subsequent characters.</meaning>
/// </item>
/// </list>
/// </param>
/// <returns>If the function succeeds, the return value is nonzero.<br></br><br>If the function fails, the return value is zero. To get extended error information, call GetLastError.</br></returns>
[DllImport("KERNEL32.DLL", EntryPoint="GetConsoleMode")]
internal static extern int GetConsoleMode (int hConsoleHandle, ref int lpConsoleCursorInfo);
/// <summary>
/// The SetConsoleMode function sets the input mode of a console's input buffer or the output mode of a console screen buffer.
/// </summary>
/// <param name="hConsoleHandle">Handle to a console input buffer or a screen buffer. The handle must have GENERIC_WRITE access.</param>
/// <param name="lpConsoleCursorInfo">
/// Pointer to a 32-bit variable that indicates the current mode of the specified buffer.<br>If the hConsoleHandle parameter is an input handle, the mode can be a combination of the following values. When a console is created, all input modes except ENABLE_WINDOW_INPUT are enabled by default.</br>
/// <list type="bullet">
/// <listheader>
/// <value>Value</value>
/// <meaning>Meaning</meaning>
/// </listheader>
/// <item>
/// <value>ENABLE_LINE_INPUT</value>
/// <meaning>The ReadFile or ReadConsole function returns only when a carriage return character is read. If this mode is disabled, the functions return when one or more characters are available.</meaning>
/// </item>
/// <item>
/// <value>ENABLE_ECHO_INPUT</value>
/// <meaning>Characters read by the ReadFile or ReadConsole function are written to the active screen buffer as they are read. This mode can be used only if the ENABLE_LINE_INPUT mode is also enabled.</meaning>
/// </item>
/// <item>
/// <value>ENABLE_PROCESSED_INPUT</value>
/// <meaning>ctrl+c is processed by the system and is not placed in the input buffer. If the input buffer is being read by ReadFile or ReadConsole, other control keys are processed by the system and are not returned in the ReadFile or ReadConsole buffer. If the ENABLE_LINE_INPUT mode is also enabled, backspace, carriage return, and linefeed characters are handled by the system.</meaning>
/// </item>
/// <item>
/// <value>ENABLE_WINDOW_INPUT</value>
/// <meaning>User interactions that change the size of the console screen buffer are reported in the console's input buffer. Information about these events can be read from the input buffer by applications using the ReadConsoleInput function, but not by those using ReadFile or ReadConsole.</meaning>
/// </item>
/// <item>
/// <value>ENABLE_MOUSE_INPUT</value>
/// <meaning>If the mouse pointer is within the borders of the console window and the window has the keyboard focus, mouse events generated by mouse movement and button presses are placed in the input buffer. These events are discarded by ReadFile or ReadConsole, even when this mode is enabled.</meaning>
/// </item>
/// </list>
/// If the hConsoleHandle parameter is a screen buffer handle, the mode can be a combination of the following values. When a screen buffer is created, both output modes are enabled by default.
/// <list type="bullet">
/// <listheader>
/// <value>Value</value>
/// <meaning>Meaning</meaning>
/// </listheader>
/// <item>
/// <value>ENABLE_PROCESSED_OUTPUT</value>
/// <meaning>Characters written by the WriteFile or WriteConsole function or echoed by the ReadFile or ReadConsole function are parsed for ASCII control sequences, and the correct action is performed. Backspace, tab, bell, carriage return, and linefeed characters are processed.</meaning>
/// </item>
/// <item>
/// <value>ENABLE_WRAP_AT_EOL_OUTPUT</value>
/// <meaning>When writing with WriteFile or WriteConsole or echoing with ReadFile or ReadConsole, the cursor moves to the beginning of the next row when it reaches the end of the current row. This causes the rows displayed in the console window to scroll up automatically when the cursor advances beyond the last row in the window. It also causes the contents of the screen buffer to scroll up (discarding the top row of the screen buffer) when the cursor advances beyond the last row in the screen buffer. If this mode is disabled, the last character in the row is overwritten with any subsequent characters.</meaning>
/// </item>
/// </list>
/// </param>
/// <returns>If the function succeeds, the return value is nonzero.<br></br><br>If the function fails, the return value is zero. To get extended error information, call GetLastError.</br></returns>
[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleMode")]
internal static extern int SetConsoleMode (int hConsoleHandle, int lpConsoleCursorInfo);
/// <summary>
/// The SetConsoleTitle function sets the title bar string for the current console window.
/// </summary>
/// <param name="lpConsoleTitle">Pointer to a null-terminated string that contains the string to appear in the title bar of the console window.</param>
/// <returns>If the function succeeds, the return value is nonzero.<br></br><br>If the function fails, the return value is zero. To get extended error information, call GetLastError.</br></returns>
[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleTitleA", CharSet=CharSet.Ansi)]
internal static extern int SetConsoleTitle (string lpConsoleTitle);
/// <summary>
/// The GetConsoleTitle function retrieves the title bar string for the current console window.
/// </summary>
/// <param name="lpConsoleTitle">Pointer to a buffer that receives a null-terminated string containing the text that appears in the title bar of the console window.</param>
/// <param name="nSize">Specifies the size, in characters, of the buffer pointed to by the lpConsoleTitle parameter.</param>
/// <returns>If the function succeeds, the return value is the length, in characters, of the string copied to the buffer.<br></br><br>If the function fails, the return value is zero. To get extended error information, call GetLastError.</br></returns>
[DllImport("KERNEL32.DLL", EntryPoint="GetConsoleTitleA", CharSet=CharSet.Ansi)]
internal static extern int GetConsoleTitle (StringBuilder lpConsoleTitle, int nSize);
/// <summary>
/// The GetConsoleScreenBufferInfo function retrieves information about the specified console screen buffer.
/// </summary>
/// <param name="hConsoleOutput">Handle to a console screen buffer. The handle must have GENERIC_READ access.</param>
/// <param name="lpConsoleScreenBufferInfo">Pointer to a CONSOLE_SCREEN_BUFFER_INFO structure in which the screen buffer information is returned.</param>
/// <returns>If the function succeeds, the return value is nonzero.<br></br><br>If the function fails, the return value is zero. To get extended error information, call GetLastError.</br></returns>
[DllImport("KERNEL32.DLL", EntryPoint="GetConsoleScreenBufferInfo")]
internal static extern int GetConsoleScreenBufferInfo (int hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
/// <summary>
/// The SetConsoleCursorPosition function sets the cursor position in the specified console screen buffer.
/// </summary>
/// <param name="hConsoleOutput">Handle to a console screen buffer. The handle must have GENERIC_WRITE access.</param>
/// <param name="dwCursorPosition">Specifies a COORD structure containing the new cursor position. The coordinates are the column and row of a screen buffer character cell. The coordinates must be within the boundaries of the screen buffer.</param>
/// <returns>If the function succeeds, the return value is nonzero.<br></br><br>If the function fails, the return value is zero. To get extended error information, call GetLastError.</br></returns>
[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleCursorPosition")]
internal static extern int SetConsoleCursorPosition (int hConsoleOutput, ref COORD dwCursorPosition);
/// <summary>Gets or sets the color of the console font.</summary>
/// <value>A value of the ConsoleColor enum that specifies the color of the console font.</value>
public static ConsoleColor ForeColor {
get {
return m_ForeColor;
}
set {
m_ForeColor = value;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), ForegroundColors[(int)value] | BacgroundColors[(int)BackColor]);
}
}
/// <summary>Gets or sets the color of the console background.</summary>
/// <value>A value of the ConsoleColor enum that specifies the color of the console background.</value>
public static ConsoleColor BackColor {
get {
return m_BackColor;
}
set {
m_BackColor = value;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), ForegroundColors[(int)ForeColor] | BacgroundColors[(int)value]);
}
}
/// <summary>Gets or sets whether the cursor is visible or not.</summary>
/// <value>A boolean value that specifies the visibility of the cursor.</value>
public static bool CursorVisible {
get {
return m_CursorVisible;
}
set {
m_CursorVisible = value;
ChangeCursor();
}
}
/// <summary>Gets or sets whether the cursor is in overwrite-mode or not.</summary>
/// <value>A boolean value that specifies the mode of the cursor.</value>
/// <remarks>In overwrite mode, the cursor size will be 50% of the character space instead of 25% in normal mode</remarks>
public static bool OvrMode {
get {
return m_OvrMode;
}
set {
m_OvrMode = value;
ChangeCursor();
}
}
/// <summary>Applies the current cursor settings.</summary>
/// <remarks>This method applies changes in the CursorVisible and OvrMode properties.</remarks>
private static void ChangeCursor() {
CONSOLE_CURSOR_INFO CCI;
CCI.bVisible = System.Convert.ToInt32(CursorVisible);
CCI.dwSize = (int)(OvrMode ? 50 : 25);
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), ref CCI);
}
/// <summary>Gets or sets whether the console must echo the input or not.</summary>
/// <value>A boolean value that specifies the console must echo the input or not.</value>
/// <remarks>EchoInput is often turned off when the program asks the user to type in a password.</remarks>
public static bool EchoInput {
get {
return m_EchoInput;
}
set {
m_EchoInput = value;
int Ret = 0;
GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ref Ret);
if (EchoInput)
Ret = Ret | ENABLE_ECHO_INPUT;
else
Ret = Ret & ~ENABLE_ECHO_INPUT;
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), Ret);
}
}
/// <summary>Gets or sets the caption of the console.</summary>
/// <value>A String that specifies the caption of the console.</value>
public static string Caption {
get {
StringBuilder sb = new StringBuilder(256);
GetConsoleTitle(sb, 256);
return sb.ToString();
}
set {
SetConsoleTitle(value);
}
}
/// <summary>Gets or sets the current cursos position on the x axis in the console.</summary>
/// <value>A short that specifies the current cursos position on the x axis in the console.</value>
public static short CursorX {
get {
CONSOLE_SCREEN_BUFFER_INFO SBI = new CONSOLE_SCREEN_BUFFER_INFO();
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), ref SBI);
return SBI.dwCursorPosition.x;
}
set {
MoveCursor(value, CursorY);
}
}
/// <summary>Gets or sets the current cursos position on the y axis in the console.</summary>
/// <value>A short value that specifies the current cursos position on the y axis in the console.</value>
public static short CursorY {
get {
CONSOLE_SCREEN_BUFFER_INFO SBI = new CONSOLE_SCREEN_BUFFER_INFO();
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), ref SBI);
return SBI.dwCursorPosition.y;
}
set {
MoveCursor(CursorX, value);
}
}
/// <summary>Moves the cursor to the specified location.</summary>
/// <param name="x">Specifies the x value of the new location.</param>
/// <param name="y">Specifies the y value of the new location.</param>
public static void MoveCursor(short x, short y) {
COORD Crd;
Crd.x = x;
Crd.y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), ref Crd);
}
/// <summary>Gets the width (in characters) of the console window.</summary>
/// <value>An integer that holds the width of the console window in characters.</value>
public static int WindowWidth {
get {
CONSOLE_SCREEN_BUFFER_INFO SBI = new CONSOLE_SCREEN_BUFFER_INFO();
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), ref SBI);
return SBI.srWindow.Right - SBI.srWindow.Left + 1;
}
}
/// <summary>Gets the height (in characters) of the console window.</summary>
/// <value>An integer that holds the height of the console window in characters.</value>
public static int WindowHeight {
get {
CONSOLE_SCREEN_BUFFER_INFO SBI = new CONSOLE_SCREEN_BUFFER_INFO();
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), ref SBI);
return SBI.srWindow.Bottom - SBI.srWindow.Top + 1;
}
}
// Private constants
/// <summary>Standard output handle.</summary>
private static int STD_OUTPUT_HANDLE = -11;
/// <summary>Standard input handle.</summary>
private static int STD_INPUT_HANDLE = -10;
/// <summary>Characters read by the ReadFile or ReadConsole function are written to the active screen buffer as they are read. This mode can be used only if the ENABLE_LINE_INPUT mode is also enabled.</summary>
private static int ENABLE_ECHO_INPUT = 0x4;
//Private variables
/// <summary>Holds the forecolor of the console window.</summary>
private static ConsoleColor m_ForeColor = ConsoleColor.Gray;
/// <summary>Holds the backcolor of the console window.</summary>
private static ConsoleColor m_BackColor = ConsoleColor.Black;
/// <summary>Holds the value of the CursorVisible property.</summary>
private static bool m_CursorVisible = true;
/// <summary>Holds the value of the OvrMode property.</summary>
private static bool m_OvrMode = false;
/// <summary>Holds the value of the EchoInput property.</summary>
private static bool m_EchoInput = true;
}
}

342
ProxyServer/FtpClient.cs Normal file
View file

@ -0,0 +1,342 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Text;
using System.Net;
using System.Net.Sockets;
using Org.Mentalis.Proxy;
namespace Org.Mentalis.Proxy.Ftp {
///<summary>Relays FTP commands between a remote host and a local client.</summary>
///<remarks>This class supports the 'OPEN' command, 'USER user@host:port' and 'USER user@host port'.</remarks>
public sealed class FtpClient : Client {
///<summary>Initializes a new instance of the FtpClient class.</summary>
///<param name="ClientSocket">The Socket connection between this proxy server and the local client.</param>
///<param name="Destroyer">The callback method to be called when this Client object disconnects from the local client and the remote server.</param>
public FtpClient(Socket ClientSocket, DestroyDelegate Destroyer) : base(ClientSocket, Destroyer) {}
///<summary>Sends a welcome message to the client.</summary>
public override void StartHandshake() {
try {
string ToSend = "220 Mentalis.org FTP proxy server ready.\r\n";
ClientSocket.BeginSend(Encoding.ASCII.GetBytes(ToSend), 0, ToSend.Length, SocketFlags.None, new AsyncCallback(this.OnHelloSent), ClientSocket);
} catch {
Dispose();
}
}
///<summary>Called when the welcome message has been sent to the client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnHelloSent(IAsyncResult ar) {
try {
if (ClientSocket.EndSend(ar) <= 0) {
Dispose();
return;
}
ClientSocket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceiveCommand), ClientSocket);
} catch {
Dispose();
}
}
///<summary>Called when we have received some bytes from the client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnReceiveCommand(IAsyncResult ar) {
try {
int Ret = ClientSocket.EndReceive(ar);
string Command;
if (Ret <= 0) {
Dispose();
return;
}
FtpCommand += Encoding.ASCII.GetString(Buffer, 0, Ret);
if (FtpClient.IsValidCommand(FtpCommand)) {
Command = FtpCommand;
if (ProcessCommand(Command))
DestinationSocket.BeginSend(Encoding.ASCII.GetBytes(Command), 0, Command.Length, SocketFlags.None, new AsyncCallback(this.OnCommandSent), DestinationSocket);
FtpCommand = "";
} else {
ClientSocket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceiveCommand), ClientSocket);
}
} catch {
Dispose();
}
}
///<summary>Processes an FTP command, sent from the client.</summary>
///<param name="Command">The command to process.</param>
///<returns>True if the command may be sent to the server, false otherwise.</returns>
private bool ProcessCommand(string Command) {
try {
int Ret = Command.IndexOf(' ');
if (Ret < 0)
Ret = Command.Length;
switch (Command.Substring(0, Ret).ToUpper().Trim()) {
case "OPEN":
ConnectTo(ParseIPPort(Command.Substring(Ret + 1)));
break;
case "USER":
Ret = Command.IndexOf('@');
if (Ret < 0) {
return true;
} else {
User = Command.Substring(0, Ret).Trim() + "\r\n";
ConnectTo(ParseIPPort(Command.Substring(Ret + 1)));
}
break;
case "PORT":
ProcessPortCommand(Command.Substring(5).Trim());
break;
case "PASV":
DataForward = new FtpDataConnection();
DataForward.ProcessPasv(this);
return true;
default:
return true;
}
return false;
} catch {
Dispose();
return false;
}
}
///<summary>Processes a PORT command, sent from the client.</summary>
///<param name="Input">The parameters of the PORT command.</param>
private void ProcessPortCommand(string Input) {
try {
string [] Parts = Input.Split(',');
if (Parts.Length == 6) {
DataForward = new FtpDataConnection();
string Reply = DataForward.ProcessPort(new IPEndPoint(IPAddress.Parse(String.Join(".", Parts, 0, 4)), int.Parse(Parts[4]) * 256 + int.Parse(Parts[5])));
DestinationSocket.BeginSend(Encoding.ASCII.GetBytes(Reply), 0, Reply.Length, SocketFlags.None, new AsyncCallback(this.OnCommandSent), DestinationSocket);
}
} catch {
Dispose();
}
}
///<summary>Parses an IP address and port from a specified input string.</summary>
///<remarks>The input string is of the following form:<br> <c>HOST:PORT</c></br><br><em>or</em></br><br> <c>HOST PORT</c></br></remarks>
///<param name="Input">The string to parse.</param>
///<returns>An instance of the IPEndPoint class if successful, null otherwise.</returns>
private IPEndPoint ParseIPPort(string Input) {
Input = Input.Trim();
int Ret = Input.IndexOf(':');
if (Ret < 0)
Ret = Input.IndexOf(' ');
try {
if (Ret > 0) {
return new IPEndPoint(Dns.GetHostEntry(Input.Substring(0, Ret)).AddressList[0], int.Parse(Input.Substring(Ret + 1)));
} else {
return new IPEndPoint(Dns.GetHostEntry(Input).AddressList[0], 21);
}
} catch {
return null;
}
}
///<summary>Connects to the specified endpoint.</summary>
///<param name="RemoteServer">The IPEndPoint to connect to.</param>
///<exception cref="SocketException">There was an error connecting to the specified endpoint</exception>
private void ConnectTo(IPEndPoint RemoteServer) {
if (DestinationSocket != null) {
try {
DestinationSocket.Shutdown(SocketShutdown.Both);
} catch {
} finally {
DestinationSocket.Close();
}
}
try {
DestinationSocket = new Socket(RemoteServer.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
DestinationSocket.BeginConnect(RemoteServer, new AsyncCallback(this.OnRemoteConnected), DestinationSocket);
} catch {
throw new SocketException();
}
}
///<summary>Called when we're connected to the remote FTP server.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnRemoteConnected(IAsyncResult ar) {
try {
DestinationSocket.EndConnect(ar);
ClientSocket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceiveCommand), ClientSocket);
if (User.Equals(""))
DestinationSocket.BeginReceive(RemoteBuffer, 0, RemoteBuffer.Length, SocketFlags.None, new AsyncCallback(this.OnReplyReceived), DestinationSocket);
else
DestinationSocket.BeginReceive(RemoteBuffer, 0, RemoteBuffer.Length, SocketFlags.None, new AsyncCallback(this.OnIgnoreReply), DestinationSocket);
} catch {
Dispose();
}
}
///<summary>Called when we receive a reply from the FTP server that should be ignored.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnIgnoreReply(IAsyncResult ar) {
try {
int Ret = DestinationSocket.EndReceive(ar);
if (Ret <= 0) {
Dispose();
return;
}
FtpReply += Encoding.ASCII.GetString(RemoteBuffer, 0, Ret);
if (FtpClient.IsValidReply(FtpReply)) {
DestinationSocket.BeginReceive(RemoteBuffer, 0, RemoteBuffer.Length, SocketFlags.None, new AsyncCallback(this.OnReplyReceived), DestinationSocket);
DestinationSocket.BeginSend(Encoding.ASCII.GetBytes(User), 0, User.Length, SocketFlags.None, new AsyncCallback(this.OnCommandSent), DestinationSocket);
} else {
DestinationSocket.BeginReceive(RemoteBuffer, 0, RemoteBuffer.Length, SocketFlags.None, new AsyncCallback(this.OnIgnoreReply), DestinationSocket);
}
} catch {
Dispose();
}
}
///<summary>Called when an FTP command has been successfully sent to the FTP server.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnCommandSent(IAsyncResult ar) {
try {
if (DestinationSocket.EndSend(ar) <= 0) {
Dispose();
return;
}
ClientSocket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceiveCommand), ClientSocket);
} catch {
Dispose();
}
}
///<summary>Called when we receive a reply from the FTP server.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnReplyReceived(IAsyncResult ar) {
try {
int Ret = DestinationSocket.EndReceive(ar);
if (Ret <= 0) {
Dispose();
return;
}
if (DataForward != null && DataForward.ExpectsReply) {
if (!DataForward.ProcessPasvReplyRecv(Encoding.ASCII.GetString(RemoteBuffer, 0, Ret)))
DestinationSocket.BeginReceive(RemoteBuffer, 0, RemoteBuffer.Length, SocketFlags.None, new AsyncCallback(this.OnReplyReceived), DestinationSocket);
} else {
ClientSocket.BeginSend(RemoteBuffer, 0, Ret, SocketFlags.None, new AsyncCallback(this.OnReplySent), ClientSocket);
}
} catch {
Dispose();
}
}
///<summary>Called when the reply from the FTP server has been sent to the local FTP client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnReplySent(IAsyncResult ar) {
try {
int Ret = ClientSocket.EndSend(ar);
if (Ret <= 0) {
Dispose();
return;
}
DestinationSocket.BeginReceive(RemoteBuffer, 0, RemoteBuffer.Length, SocketFlags.None, new AsyncCallback(this.OnReplyReceived), DestinationSocket);
} catch {
Dispose();
}
}
///<summary>Sends a string to the local FTP client.</summary>
///<param name="Command">The result of the asynchronous operation.</param>
internal void SendCommand(string Command) {
ClientSocket.BeginSend(Encoding.ASCII.GetBytes(Command), 0, Command.Length, SocketFlags.None, new AsyncCallback(this.OnReplySent), ClientSocket);
}
///<summary>Checks whether a specified command is a complete FTP command or not.</summary>
///<param name="Command">A string containing the command to check.</param>
///<returns>True if the command is complete, false otherwise.</returns>
internal static bool IsValidCommand(string Command) {
return (Command.IndexOf("\r\n") >= 0);
}
///<summary>Checks whether a specified reply is a complete FTP reply or not.</summary>
///<param name="Input">A string containing the reply to check.</param>
///<returns>True is the reply is complete, false otherwise.</returns>
internal static bool IsValidReply(string Input) {
string [] Lines = Input.Split('\n');
try {
if (Lines[Lines.Length - 2].Trim().Substring(3, 1).Equals(" "))
return true;
} catch {}
return false;
}
///<summary>Gets or sets a property that can be used to store the received FTP command.</summary>
///<value>A string that can be used to store the received FTP command.</value>
private string FtpCommand {
get {
return m_FtpCommand;
}
set {
m_FtpCommand = value;
}
}
///<summary>Gets or sets a property that can be used to store the received FTP reply.</summary>
///<value>A string that can be used to store the received FTP reply.</value>
private string FtpReply {
get {
return m_FtpReply;
}
set {
m_FtpReply = value;
}
}
///<summary>Gets or sets a string containing the logged on username.</summary>
///<value>A string containing the logged on username.</value>
private string User {
get {
return m_User;
}
set {
m_User = value;
}
}
///<summary>Gets or sets the dataconnection object used to transmit files and other data from and to the FTP server.</summary>
///<value>An FtpDataConnection object that's used to transmit files and other data from and to the FTP server.</value>
internal FtpDataConnection DataForward {
get {
return m_DataForward;
}
set {
m_DataForward = value;
}
}
///<summary>Returns text information about this FtpClient object.</summary>
///<returns>A string representing this FtpClient object.</returns>
public override string ToString() {
try {
return "FTP connection from " + ((IPEndPoint)ClientSocket.RemoteEndPoint).Address.ToString() + " to " + ((IPEndPoint)DestinationSocket.RemoteEndPoint).Address.ToString();
} catch {
return "Incoming FTP connection";
}
}
// private variables
/// <summary>Holds the value of the User property.</summary>
private string m_User = "";
/// <summary>Holds the value of the FtpCommand property.</summary>
private string m_FtpCommand = "";
/// <summary>Holds the value of the FtpReply property.</summary>
private string m_FtpReply = "";
/// <summary>Holds the value of the DataForward property.</summary>
private FtpDataConnection m_DataForward;
}
}

View file

@ -0,0 +1,216 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Text;
using System.Net;
using System.Net.Sockets;
using Org.Mentalis.Proxy;
namespace Org.Mentalis.Proxy.Ftp {
///<summary>Relays FTP data between a remote host and a local client.</summary>
internal sealed class FtpDataConnection : Client {
///<summary>Initializes a new instance of the FtpDataConnection class.</summary>
public FtpDataConnection() : base() {}
///<summary>Initializes a new instance of the FtpDataConnection class.</summary>
///<param name="RemoteAddress">The address on the local FTP client to connect to.</param>
///<returns>The PORT command string to send to the FTP server.</returns>
public string ProcessPort(IPEndPoint RemoteAddress) {
try {
ListenSocket = new Socket(IPAddress.Any.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
ListenSocket.Bind(new IPEndPoint(IPAddress.Any, 0));
ListenSocket.Listen(1);
ListenSocket.BeginAccept(new AsyncCallback(this.OnPortAccept), ListenSocket);
ClientSocket = new Socket(RemoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
ClientSocket.BeginConnect(RemoteAddress, new AsyncCallback(this.OnPortConnected), ClientSocket);
return "PORT " + Listener.GetLocalExternalIP().ToString().Replace('.', ',') + "," + Math.Floor((double)((IPEndPoint)ListenSocket.LocalEndPoint).Port / 256).ToString() + "," + (((IPEndPoint)ListenSocket.LocalEndPoint).Port % 256).ToString() + "\r\n";
} catch {
Dispose();
return "PORT 0,0,0,0,0,0\r\n";
}
}
///<summary>Called when we're connected to the data port on the local FTP client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnPortConnected(IAsyncResult ar) {
try {
ClientSocket.EndConnect(ar);
StartHandshake();
} catch {
Dispose();
}
}
///<summary>Called when there's a connection from the remote FTP server waiting to be accepted.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnPortAccept(IAsyncResult ar) {
try {
DestinationSocket = ListenSocket.EndAccept(ar);
ListenSocket.Close();
ListenSocket = null;
StartHandshake();
} catch {
Dispose();
}
}
///<summary>Starts relaying data between the remote FTP server and the local FTP client.</summary>
public override void StartHandshake() {
if (DestinationSocket != null && ClientSocket != null && DestinationSocket.Connected && ClientSocket.Connected)
StartRelay();
}
///<summary>Gets or sets the Socket that's used to listen for incoming connections.</summary>
///<value>A Socket that's used to listen for incoming connections.</value>
private Socket ListenSocket {
get {
return m_ListenSocket;
}
set {
if (m_ListenSocket != null)
m_ListenSocket.Close();
m_ListenSocket = value;
}
}
///<summary>Gets or sets the parent of this FtpDataConnection.</summary>
///<value>The FtpClient object that's the parent of this FtpDataConnection object.</value>
private FtpClient Parent {
get {
return m_Parent;
}
set {
m_Parent = value;
}
}
///<summary>Gets or sets a string that stores the reply that has been sent from the remote FTP server.</summary>
///<value>A string that stores the reply that has been sent from the remote FTP server.</value>
private string FtpReply {
get {
return m_FtpReply;
}
set {
m_FtpReply = value;
}
}
///<summary>Gets or sets a boolean value that indicates whether the FtpDataConnection expects a reply from the remote FTP server or not.</summary>
///<value>A boolean value that indicates whether the FtpDataConnection expects a reply from the remote FTP server or not.</value>
internal bool ExpectsReply {
get {
return m_ExpectsReply;
}
set {
m_ExpectsReply = value;
}
}
///<summary>Called when the proxy server processes a PASV command.</summary>
///<param name="Parent">The parent FtpClient object.</param>
public void ProcessPasv(FtpClient Parent) {
this.Parent = Parent;
ExpectsReply = true;
}
///<summary>Called when the FtpClient receives a reply on the PASV command from the server.</summary>
///<param name="Input">The received reply.</param>
///<returns>True if the input has been processed successfully, false otherwise.</returns>
internal bool ProcessPasvReplyRecv(string Input) {
FtpReply += Input;
if (FtpClient.IsValidReply(FtpReply)) {
ExpectsReply = false;
ProcessPasvReply(FtpReply);
FtpReply = "";
return true;
}
return false;
}
///<summary>Processes a PASV reply from the server.</summary>
///<param name="Reply">The reply to process.</param>
private void ProcessPasvReply(string Reply) {
try {
IPEndPoint ConnectTo = ParsePasvIP(Reply);
DestinationSocket = new Socket(ConnectTo.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
DestinationSocket.BeginConnect(ConnectTo, new AsyncCallback(this.OnPasvConnected), DestinationSocket);
} catch {
Dispose();
}
}
///<summary>Parses a PASV reply into an instance of the IPEndPoint class.</summary>
///<param name="Reply">The reply to parse into an IPEndPoint.</param>
///<returns>An instance of the IPEndPoint class when successful, null otherwise.</returns>
private IPEndPoint ParsePasvIP(string Reply) {
int StartIndex, StopIndex;
string IPString;
StartIndex = Reply.IndexOf("(");
if (StartIndex == -1) {
return null;
} else {
StopIndex = Reply.IndexOf(")", StartIndex);
if (StopIndex == -1)
return null;
else
IPString = Reply.Substring(StartIndex + 1, StopIndex - StartIndex - 1);
}
string [] Parts = IPString.Split(',');
if (Parts.Length == 6)
return new IPEndPoint(IPAddress.Parse(String.Join(".", Parts, 0, 4)), int.Parse(Parts[4]) * 256 + int.Parse(Parts[5]));
else
return null;
}
///<summary>Called when we're connected to the data port of the remote FTP server.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnPasvConnected(IAsyncResult ar) {
try {
DestinationSocket.EndConnect(ar);
ListenSocket = new Socket(IPAddress.Any.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
ListenSocket.Bind(new IPEndPoint(IPAddress.Any, 0));
ListenSocket.Listen(1);
ListenSocket.BeginAccept(new AsyncCallback(this.OnPasvAccept), ListenSocket);
Parent.SendCommand("227 Entering Passive Mode (" + Listener.GetLocalInternalIP().ToString().Replace('.', ',') + "," + Math.Floor((double)((IPEndPoint)ListenSocket.LocalEndPoint).Port / 256).ToString() + "," + (((IPEndPoint)ListenSocket.LocalEndPoint).Port % 256).ToString() + ").\r\n");
} catch {
Dispose();
}
}
///<summary>Called when there's a connection from the local FTP client waiting to be accepted.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnPasvAccept(IAsyncResult ar) {
try {
ClientSocket = ListenSocket.EndAccept(ar);
StartHandshake();
} catch {
Dispose();
}
}
// private variables
/// <summary>Holds the value of the ListenSocket property.</summary>
private Socket m_ListenSocket;
/// <summary>Holds the value of the Parent property.</summary>
private FtpClient m_Parent;
/// <summary>Holds the value of the FtpReply property.</summary>
private string m_FtpReply = "";
/// <summary>Holds the value of the ExpectsReply property.</summary>
private bool m_ExpectsReply = false;
}
}

View file

@ -0,0 +1,83 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using Org.Mentalis.Proxy;
namespace Org.Mentalis.Proxy.Ftp {
///<summary>Listens on a specific port on the proxy server and forwards all incoming FTP traffic to the appropriate server.</summary>
public sealed class FtpListener : Listener {
///<summary>Initializes a new instance of the FtpListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<remarks>The FtpListener will start listening on all installed network cards.</remarks>
///<exception cref="ArgumentException">Port is not positive.</exception>
public FtpListener(int Port) : this(IPAddress.Any, Port) {}
///<summary>Initializes a new instance of the FtpListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<param name="Address">The address to listen on. You can specify IPAddress.Any to listen on all installed network cards.</param>
///<remarks>For the security of your server, try to avoid to listen on every network card (IPAddress.Any). Listening on a local IP address is usually sufficient and much more secure.</remarks>
///<exception cref="ArgumentNullException">Address is null.</exception>
///<exception cref="ArgumentException">Port is not positive.</exception>
public FtpListener(IPAddress Address, int Port) : base(Port, Address) {}
///<summary>Called when there's an incoming client connection waiting to be accepted.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
public override void OnAccept(IAsyncResult ar) {
try {
Socket NewSocket = ListenSocket.EndAccept(ar);
if (NewSocket != null) {
FtpClient NewClient = new FtpClient(NewSocket, new DestroyDelegate(this.RemoveClient));
AddClient(NewClient);
NewClient.StartHandshake();
}
} catch {}
try {
ListenSocket.BeginAccept(new AsyncCallback(this.OnAccept), ListenSocket);
} catch {
Dispose();
}
}
///<summary>Returns a string representation of this object.</summary>
///<returns>A string with information about this object.</returns>
public override string ToString() {
return "FTP service on " + Address.ToString() + ":" + Port.ToString();
}
///<summary>Returns a string that holds all the construction information for this object.</summary>
///<value>A string that holds all the construction information for this object.</value>
public override string ConstructString {
get {
return "host:" + Address.ToString() + ";int:" + Port.ToString();
}
}
}
}

368
ProxyServer/HttpClient.cs Normal file
View file

@ -0,0 +1,368 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using System.Collections;
using System.Collections.Specialized;
using System.Threading;
using System.Globalization;
using Org.Mentalis.Proxy;
namespace Org.Mentalis.Proxy.Http {
///<summary>Relays HTTP data between a remote host and a local client.</summary>
///<remarks>This class supports both HTTP and HTTPS.</remarks>
public sealed class HttpClient : Client {
///<summary>Initializes a new instance of the HttpClient class.</summary>
///<param name="ClientSocket">The <see cref ="Socket">Socket</see> connection between this proxy server and the local client.</param>
///<param name="Destroyer">The callback method to be called when this Client object disconnects from the local client and the remote server.</param>
public HttpClient(Socket ClientSocket, DestroyDelegate Destroyer) : base(ClientSocket, Destroyer) {}
///<summary>Gets or sets a StringDictionary that stores the header fields.</summary>
///<value>A StringDictionary that stores the header fields.</value>
private StringDictionary HeaderFields {
get {
return m_HeaderFields;
}
set {
m_HeaderFields = value;
}
}
///<summary>Gets or sets the HTTP version the client uses.</summary>
///<value>A string representing the requested HTTP version.</value>
private string HttpVersion {
get {
return m_HttpVersion;
}
set {
m_HttpVersion = value;
}
}
///<summary>Gets or sets the HTTP request type.</summary>
///<remarks>
///Usually, this string is set to one of the three following values:
///<list type="bullet">
///<item>GET</item>
///<item>POST</item>
///<item>CONNECT</item>
///</list>
///</remarks>
///<value>A string representing the HTTP request type.</value>
private string HttpRequestType {
get {
return m_HttpRequestType;
}
set {
m_HttpRequestType = value;
}
}
///<summary>Gets or sets the requested path.</summary>
///<value>A string representing the requested path.</value>
public string RequestedPath {
get {
return m_RequestedPath;
}
set {
m_RequestedPath = value;
}
}
///<summary>Gets or sets the query string, received from the client.</summary>
///<value>A string representing the HTTP query string.</value>
private string HttpQuery {
get {
return m_HttpQuery;
}
set {
if (value == null)
throw new ArgumentNullException();
m_HttpQuery = value;
}
}
///<summary>Starts receiving data from the client connection.</summary>
public override void StartHandshake() {
try {
ClientSocket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceiveQuery), ClientSocket);
} catch {
Dispose();
}
}
///<summary>Checks whether a specified string is a valid HTTP query string.</summary>
///<param name="Query">The query to check.</param>
///<returns>True if the specified string is a valid HTTP query, false otherwise.</returns>
private bool IsValidQuery(string Query) {
int index = Query.IndexOf("\r\n\r\n");
if (index == -1)
return false;
HeaderFields = ParseQuery(Query);
if (HttpRequestType.ToUpper().Equals("POST")) {
try {
int length = int.Parse((string)HeaderFields["Content-Length"]);
return Query.Length >= index + 6 + length;
} catch {
SendBadRequest();
return true;
}
} else {
return true;
}
}
///<summary>Processes a specified query and connects to the requested HTTP web server.</summary>
///<param name="Query">A string containing the query to process.</param>
///<remarks>If there's an error while processing the HTTP request or when connecting to the remote server, the Proxy sends a "400 - Bad Request" error to the client.</remarks>
private void ProcessQuery(string Query) {
HeaderFields = ParseQuery(Query);
if (HeaderFields == null || !HeaderFields.ContainsKey("Host")) {
SendBadRequest();
return;
}
int Port;
string Host;
int Ret;
if (HttpRequestType.ToUpper().Equals("CONNECT")) { //HTTPS
Ret = RequestedPath.IndexOf(":");
if (Ret >= 0) {
Host = RequestedPath.Substring(0, Ret);
if (RequestedPath.Length > Ret + 1)
Port = int.Parse(RequestedPath.Substring(Ret + 1));
else
Port = 443;
} else {
Host = RequestedPath;
Port = 443;
}
} else { //Normal HTTP
Ret = ((string)HeaderFields["Host"]).IndexOf(":");
if (Ret > 0) {
Host = ((string)HeaderFields["Host"]).Substring(0, Ret);
Port = int.Parse(((string)HeaderFields["Host"]).Substring(Ret + 1));
} else {
Host = (string)HeaderFields["Host"];
Port = 80;
}
if (HttpRequestType.ToUpper().Equals("POST")) {
int index = Query.IndexOf("\r\n\r\n");
m_HttpPost = Query.Substring(index + 4);
}
}
try {
IPEndPoint DestinationEndPoint = new IPEndPoint(Dns.GetHostEntry(Host).AddressList[0], Port);
DestinationSocket = new Socket(DestinationEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
if (HeaderFields.ContainsKey("Proxy-Connection") && HeaderFields["Proxy-Connection"].ToLower(CultureInfo.InvariantCulture).Equals("keep-alive"))
DestinationSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
DestinationSocket.BeginConnect(DestinationEndPoint, new AsyncCallback(this.OnConnected), DestinationSocket);
} catch {
SendBadRequest();
return;
}
}
///<summary>Parses a specified HTTP query into its header fields.</summary>
///<param name="Query">The HTTP query string to parse.</param>
///<returns>A StringDictionary object containing all the header fields with their data.</returns>
///<exception cref="ArgumentNullException">The specified query is null.</exception>
private StringDictionary ParseQuery(string Query) {
StringDictionary retdict = new StringDictionary();
string [] Lines = Query.Replace("\r\n", "\n").Split('\n');
int Cnt, Ret;
//Extract requested URL
if (Lines.Length > 0) {
//Parse the Http Request Type
Ret = Lines[0].IndexOf(' ');
if (Ret > 0) {
HttpRequestType = Lines[0].Substring(0, Ret);
Lines[0] = Lines[0].Substring(Ret).Trim();
}
//Parse the Http Version and the Requested Path
Ret = Lines[0].LastIndexOf(' ');
if (Ret > 0) {
HttpVersion = Lines[0].Substring(Ret).Trim();
RequestedPath = Lines[0].Substring(0, Ret);
} else {
RequestedPath = Lines[0];
}
//Remove http:// if present
if (RequestedPath.Length >= 7 && RequestedPath.Substring(0, 7).ToLower(CultureInfo.InvariantCulture).Equals("http://")) {
Ret = RequestedPath.IndexOf('/', 7);
if (Ret == -1)
RequestedPath = "/";
else
RequestedPath = RequestedPath.Substring(Ret);
}
}
for(Cnt = 1; Cnt < Lines.Length; Cnt++) {
Ret = Lines[Cnt].IndexOf(":");
if (Ret > 0 && Ret < Lines[Cnt].Length - 1) {
try {
retdict.Add(Lines[Cnt].Substring(0, Ret), Lines[Cnt].Substring(Ret + 1).Trim());
} catch {}
}
}
return retdict;
}
///<summary>Sends a "400 - Bad Request" error to the client.</summary>
private void SendBadRequest() {
string brs = "HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n<html><head><title>400 Bad Request</title></head><body><div align=\"center\"><table border=\"0\" cellspacing=\"3\" cellpadding=\"3\" bgcolor=\"#C0C0C0\"><tr><td><table border=\"0\" width=\"500\" cellspacing=\"3\" cellpadding=\"3\"><tr><td bgcolor=\"#B2B2B2\"><p align=\"center\"><strong><font size=\"2\" face=\"Verdana\">400 Bad Request</font></strong></p></td></tr><tr><td bgcolor=\"#D1D1D1\"><font size=\"2\" face=\"Verdana\"> The proxy server could not understand the HTTP request!<br><br> Please contact your network administrator about this problem.</font></td></tr></table></center></td></tr></table></div></body></html>";
try {
ClientSocket.BeginSend(Encoding.ASCII.GetBytes(brs), 0, brs.Length, SocketFlags.None, new AsyncCallback(this.OnErrorSent), ClientSocket);
} catch {
Dispose();
}
}
///<summary>Rebuilds the HTTP query, starting from the HttpRequestType, RequestedPath, HttpVersion and HeaderFields properties.</summary>
///<returns>A string representing the rebuilt HTTP query string.</returns>
private string RebuildQuery() {
string ret = HttpRequestType + " " + RequestedPath + " " + HttpVersion + "\r\n";
if (HeaderFields != null) {
foreach (string sc in HeaderFields.Keys) {
if (sc.Length < 6 || !sc.Substring(0, 6).Equals("proxy-"))
ret += System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(sc) + ": " + (string)HeaderFields[sc] + "\r\n";
}
ret += "\r\n";
if (m_HttpPost != null)
ret += m_HttpPost;
}
return ret;
}
///<summary>Returns text information about this HttpClient object.</summary>
///<returns>A string representing this HttpClient object.</returns>
public override string ToString() {
return ToString(false);
}
///<summary>Returns text information about this HttpClient object.</summary>
///<returns>A string representing this HttpClient object.</returns>
///<param name="WithUrl">Specifies whether or not to include information about the requested URL.</param>
public string ToString(bool WithUrl) {
string Ret;
try {
if (DestinationSocket == null || DestinationSocket.RemoteEndPoint == null)
Ret = "Incoming HTTP connection from " + ((IPEndPoint)ClientSocket.RemoteEndPoint).Address.ToString();
else
Ret = "HTTP connection from " + ((IPEndPoint)ClientSocket.RemoteEndPoint).Address.ToString() + " to " + ((IPEndPoint)DestinationSocket.RemoteEndPoint).Address.ToString() + " on port " + ((IPEndPoint)DestinationSocket.RemoteEndPoint).Port.ToString();
if (HeaderFields != null && HeaderFields.ContainsKey("Host") && RequestedPath != null)
Ret += "\r\n" + " requested URL: http://" + HeaderFields["Host"] + RequestedPath;
} catch {
Ret = "HTTP Connection";
}
return Ret;
}
///<summary>Called when we received some data from the client connection.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnReceiveQuery(IAsyncResult ar) {
int Ret;
try {
Ret = ClientSocket.EndReceive(ar);
} catch {
Ret = -1;
}
if (Ret <= 0) { //Connection is dead :(
Dispose();
return;
}
HttpQuery += Encoding.ASCII.GetString(Buffer, 0, Ret);
//if received data is valid HTTP request...
if (IsValidQuery(HttpQuery)) {
ProcessQuery(HttpQuery);
//else, keep listening
} else {
try {
ClientSocket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceiveQuery), ClientSocket);
} catch {
Dispose();
}
}
}
///<summary>Called when the Bad Request error has been sent to the client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnErrorSent(IAsyncResult ar) {
try {
ClientSocket.EndSend(ar);
} catch {}
Dispose();
}
///<summary>Called when we're connected to the requested remote host.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnConnected(IAsyncResult ar) {
try {
DestinationSocket.EndConnect(ar);
string rq;
if (HttpRequestType.ToUpper().Equals("CONNECT")) { //HTTPS
rq = HttpVersion + " 200 Connection established\r\nProxy-Agent: Mentalis Proxy Server\r\n\r\n";
ClientSocket.BeginSend(Encoding.ASCII.GetBytes(rq), 0, rq.Length, SocketFlags.None, new AsyncCallback(this.OnOkSent), ClientSocket);
} else { //Normal HTTP
rq = RebuildQuery();
DestinationSocket.BeginSend(Encoding.ASCII.GetBytes(rq), 0, rq.Length, SocketFlags.None, new AsyncCallback(this.OnQuerySent), DestinationSocket);
}
} catch {
Dispose();
}
}
///<summary>Called when the HTTP query has been sent to the remote host.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnQuerySent(IAsyncResult ar) {
try {
if (DestinationSocket.EndSend(ar) == -1) {
Dispose();
return;
}
StartRelay();
} catch {
Dispose();
}
}
///<summary>Called when an OK reply has been sent to the local client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnOkSent(IAsyncResult ar) {
try {
if (ClientSocket.EndSend(ar) == -1) {
Dispose();
return;
}
StartRelay();
} catch {
Dispose();
}
}
// private variables
/// <summary>Holds the value of the HttpQuery property.</summary>
private string m_HttpQuery = "";
/// <summary>Holds the value of the RequestedPath property.</summary>
private string m_RequestedPath = null;
/// <summary>Holds the value of the HeaderFields property.</summary>
private StringDictionary m_HeaderFields = null;
/// <summary>Holds the value of the HttpVersion property.</summary>
private string m_HttpVersion = "";
/// <summary>Holds the value of the HttpRequestType property.</summary>
private string m_HttpRequestType = "";
/// <summary>Holds the POST data</summary>
private string m_HttpPost = null;
}
}

View file

@ -0,0 +1,81 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using Org.Mentalis.Proxy;
namespace Org.Mentalis.Proxy.Http {
///<summary>Listens on a specific port on the proxy server and forwards all incoming HTTP traffic to the appropriate server.</summary>
public sealed class HttpListener : Listener {
///<summary>Initializes a new instance of the HttpListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<remarks>The HttpListener will start listening on all installed network cards.</remarks>
public HttpListener(int Port) : this(IPAddress.Any, Port) {}
///<summary>Initializes a new instance of the HttpListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<param name="Address">The address to listen on. You can specify IPAddress.Any to listen on all installed network cards.</param>
///<remarks>For the security of your server, try to avoid to listen on every network card (IPAddress.Any). Listening on a local IP address is usually sufficient and much more secure.</remarks>
public HttpListener(IPAddress Address, int Port) : base(Port, Address) {}
///<summary>Called when there's an incoming client connection waiting to be accepted.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
public override void OnAccept(IAsyncResult ar) {
try {
Socket NewSocket = ListenSocket.EndAccept(ar);
if (NewSocket != null) {
HttpClient NewClient = new HttpClient(NewSocket, new DestroyDelegate(this.RemoveClient));
AddClient(NewClient);
NewClient.StartHandshake();
}
} catch {}
try {
//Restart Listening
ListenSocket.BeginAccept(new AsyncCallback(this.OnAccept), ListenSocket);
} catch {
Dispose();
}
}
///<summary>Returns a string representation of this object.</summary>
///<returns>A string with information about this object.</returns>
public override string ToString() {
return "HTTP service on " + Address.ToString() + ":" + Port.ToString();
}
///<summary>Returns a string that holds all the construction information for this object.</summary>
///<value>A string that holds all the construction information for this object.</value>
public override string ConstructString {
get {
return "host:" + Address.ToString() + ";int:" + Port.ToString();
}
}
}
}

261
ProxyServer/Listener.cs Normal file
View file

@ -0,0 +1,261 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using System.Collections;
namespace Org.Mentalis.Proxy {
///<summary>Specifies the basic methods and properties of a <c>Listener</c> object. This is an abstract class and must be inherited.</summary>
///<remarks>The Listener class provides an abstract base class that represents a listening socket of the proxy server. Descendant classes further specify the protocol that is used between those two connections.</remarks>
public abstract class Listener : IDisposable{
///<summary>Initializes a new instance of the Listener class.</summary>
///<param name="Port">The port to listen on.</param>
///<param name="Address">The address to listen on. You can specify IPAddress.Any to listen on all installed network cards.</param>
///<remarks>For the security of your server, try to avoid to listen on every network card (IPAddress.Any). Listening on a local IP address is usually sufficient and much more secure.</remarks>
public Listener (int Port, IPAddress Address){
this.Port = Port;
this.Address = Address;
}
///<summary>Gets or sets the port number on which to listen on.</summary>
///<value>An integer defining the port number to listen on.</value>
///<seealso cref ="Address"/>
///<exception cref="ArgumentException">The specified value is less than or equal to zero.</exception>
protected int Port {
get {
return m_Port;
}
set {
if (value <= 0)
throw new ArgumentException();
m_Port = value;
Restart();
}
}
///<summary>Gets or sets the address on which to listen on.</summary>
///<value>An IPAddress instance defining the IP address to listen on.</value>
///<seealso cref ="Port"/>
///<exception cref="ArgumentNullException">The specified value is null.</exception>
protected IPAddress Address {
get {
return m_Address;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Address = value;
Restart();
}
}
///<summary>Gets or sets the listening Socket.</summary>
///<value>An instance of the Socket class that's used to listen for incoming connections.</value>
///<exception cref="ArgumentNullException">The specified value is null.</exception>
protected Socket ListenSocket {
get {
return m_ListenSocket;
}
set {
if (value == null)
throw new ArgumentNullException();
m_ListenSocket = value;
}
}
///<summary>Gets the list of connected clients.</summary>
///<value>An instance of the ArrayList class that's used to store all the connections.</value>
protected ArrayList Clients {
get {
return m_Clients;
}
}
///<summary>Gets a value indicating whether the Listener has been disposed or not.</summary>
///<value>An boolean that specifies whether the object has been disposed or not.</value>
public bool IsDisposed {
get {
return m_IsDisposed;
}
}
///<summary>Starts listening on the selected IP address and port.</summary>
///<exception cref="SocketException">There was an error while creating the listening socket.</exception>
public void Start() {
try {
ListenSocket = new Socket(Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
ListenSocket.Bind(new IPEndPoint(Address, Port));
ListenSocket.Listen(50);
ListenSocket.BeginAccept(new AsyncCallback(this.OnAccept), ListenSocket);
} catch {
ListenSocket = null;
throw new SocketException();
}
}
///<summary>Restarts listening on the selected IP address and port.</summary>
///<remarks>This method is automatically called when the listening port or the listening IP address are changed.</remarks>
///<exception cref="SocketException">There was an error while creating the listening socket.</exception>
protected void Restart() {
//If we weren't listening, do nothing
if (ListenSocket == null)
return;
ListenSocket.Close();
Start();
}
///<summary>Adds the specified Client to the client list.</summary>
///<remarks>A client will never be added twice to the list.</remarks>
///<param name="client">The client to add to the client list.</param>
protected void AddClient(Client client) {
if (Clients.IndexOf(client) == -1)
Clients.Add(client);
}
///<summary>Removes the specified Client from the client list.</summary>
///<param name="client">The client to remove from the client list.</param>
protected void RemoveClient(Client client) {
Clients.Remove(client);
}
///<summary>Returns the number of clients in the client list.</summary>
///<returns>The number of connected clients.</returns>
public int GetClientCount() {
return Clients.Count;
}
///<summary>Returns the requested client from the client list.</summary>
///<param name="Index">The index of the requested client.</param>
///<returns>The requested client.</returns>
///<remarks>If the specified index is invalid, the GetClientAt method returns null.</remarks>
public Client GetClientAt(int Index) {
if (Index < 0 || Index >= GetClientCount())
return null;
return (Client)Clients[Index];
}
///<summary>Gets a value indicating whether the Listener is currently listening or not.</summary>
///<value>A boolean that indicates whether the Listener is currently listening or not.</value>
public bool Listening {
get {
return ListenSocket != null;
}
}
///<summary>Disposes of the resources (other than memory) used by the Listener.</summary>
///<remarks>Stops listening and disposes <em>all</em> the client objects. Once disposed, this object should not be used anymore.</remarks>
///<seealso cref ="System.IDisposable"/>
public void Dispose() {
if (IsDisposed)
return;
while (Clients.Count > 0) {
((Client)Clients[0]).Dispose();
}
try {
ListenSocket.Shutdown(SocketShutdown.Both);
} catch {}
if (ListenSocket != null)
ListenSocket.Close();
m_IsDisposed = true;
}
///<summary>Finalizes the Listener.</summary>
///<remarks>The destructor calls the Dispose method.</remarks>
~Listener() {
Dispose();
}
///<summary>Returns an external IP address of this computer, if present.</summary>
///<returns>Returns an external IP address of this computer; if this computer does not have an external IP address, it returns the first local IP address it can find.</returns>
///<remarks>If this computer does not have any configured IP address, this method returns the IP address 0.0.0.0.</remarks>
public static IPAddress GetLocalExternalIP() {
try {
IPHostEntry he = Dns.GetHostEntry(Dns.GetHostName());
for (int Cnt = 0; Cnt < he.AddressList.Length; Cnt++) {
if (IsRemoteIP(he.AddressList[Cnt]))
return he.AddressList[Cnt];
}
return he.AddressList[0];
} catch {
return IPAddress.Any;
}
}
///<summary>Checks whether the specified IP address is a remote IP address or not.</summary>
///<param name="IP">The IP address to check.</param>
///<returns>True if the specified IP address is a remote address, false otherwise.</returns>
protected static bool IsRemoteIP(IPAddress IP) {
byte First = IP.GetAddressBytes()[0];
byte Second = IP.GetAddressBytes()[1];
//Not 10.x.x.x And Not 172.16.x.x <-> 172.31.x.x And Not 192.168.x.x
//And Not Any And Not Loopback And Not Broadcast
return (First != 10) &&
(First != 172 || (Second < 16 || Second > 31)) &&
(First != 192 || Second != 168) &&
(!IP.Equals(IPAddress.Any)) &&
(!IP.Equals(IPAddress.Loopback)) &&
(!IP.Equals(IPAddress.Broadcast));
}
///<summary>Checks whether the specified IP address is a local IP address or not.</summary>
///<param name="IP">The IP address to check.</param>
///<returns>True if the specified IP address is a local address, false otherwise.</returns>
protected static bool IsLocalIP(IPAddress IP) {
byte First = IP.GetAddressBytes()[0];
byte Second = IP.GetAddressBytes()[1];
//10.x.x.x Or 172.16.x.x <-> 172.31.x.x Or 192.168.x.x
return (First == 10) ||
(First == 172 && (Second >= 16 && Second <= 31)) ||
(First == 192 && Second == 168);
}
///<summary>Returns an internal IP address of this computer, if present.</summary>
///<returns>Returns an internal IP address of this computer; if this computer does not have an internal IP address, it returns the first local IP address it can find.</returns>
///<remarks>If this computer does not have any configured IP address, this method returns the IP address 0.0.0.0.</remarks>
public static IPAddress GetLocalInternalIP() {
try {
IPHostEntry he = Dns.GetHostEntry(Dns.GetHostName());
for (int Cnt = 0; Cnt < he.AddressList.Length; Cnt++) {
if (IsLocalIP(he.AddressList[Cnt]))
return he.AddressList[Cnt];
}
return he.AddressList[0];
} catch {
return IPAddress.Any;
}
}
///<summary>Called when there's an incoming client connection waiting to be accepted.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
public abstract void OnAccept(IAsyncResult ar);
///<summary>Returns a string representation of this object.</summary>
///<returns>A string with information about this object.</returns>
public override abstract string ToString();
///<summary>Returns a string that holds all the construction information for this object.</summary>
///<value>A string that holds all the construction information for this object.</value>
public abstract string ConstructString {get;}
// private variables
/// <summary>Holds the value of the Port property.</summary>
private int m_Port;
/// <summary>Holds the value of the Address property.</summary>
private IPAddress m_Address;
/// <summary>Holds the value of the ListenSocket property.</summary>
private Socket m_ListenSocket;
/// <summary>Holds the value of the Clients property.</summary>
private ArrayList m_Clients = new ArrayList();
/// <summary>Holds the value of the IsDisposed property.</summary>
private bool m_IsDisposed = false;
}
}

View file

@ -0,0 +1,95 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using Org.Mentalis.Proxy;
namespace Org.Mentalis.Proxy.PortMap {
///<summary>Relays data between a remote host and a local client.</summary>
public sealed class PortMapClient : Client {
///<summary>Initializes a new instance of the PortMapClient class.</summary>
///<param name="ClientSocket">The <see cref ="Socket">Socket</see> connection between this proxy server and the local client.</param>
///<param name="Destroyer">The callback method to be called when this Client object disconnects from the local client and the remote server.</param>
///<param name="MapTo">The IP EndPoint to send the incoming data to.</param>
public PortMapClient(Socket ClientSocket, DestroyDelegate Destroyer, IPEndPoint MapTo) : base(ClientSocket, Destroyer) {
this.MapTo = MapTo;
}
///<summary>Gets or sets the IP EndPoint to map all incoming traffic to.</summary>
///<value>An IPEndPoint that holds the IP address and port to use when redirecting incoming traffic.</value>
///<exception cref="ArgumentNullException">The specified value is null.</exception>
///<returns>An IP EndPoint specifying the host and port to map all incoming traffic to.</returns>
private IPEndPoint MapTo {
get {
return m_MapTo;
}
set {
if (value == null)
throw new ArgumentNullException();
m_MapTo = value;
}
}
///<summary>Starts connecting to the remote host.</summary>
override public void StartHandshake() {
try {
DestinationSocket = new Socket(MapTo.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
DestinationSocket.BeginConnect(MapTo, new AsyncCallback(this.OnConnected), DestinationSocket);
} catch {
Dispose();
}
}
///<summary>Called when the socket is connected to the remote host.</summary>
///<remarks>When the socket is connected to the remote host, the PortMapClient begins relaying traffic between the host and the client, until one of them closes the connection.</remarks>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnConnected(IAsyncResult ar) {
try {
DestinationSocket.EndConnect(ar);
StartRelay();
} catch {
Dispose();
}
}
///<summary>Returns text information about this PortMapClient object.</summary>
///<returns>A string representing this PortMapClient object.</returns>
public override string ToString() {
try {
return "Forwarding port from " + ((IPEndPoint)ClientSocket.RemoteEndPoint).Address.ToString() + " to " + MapTo.ToString();
} catch {
return "Incoming Port forward connection";
}
}
// private variables
/// <summary>Holds the value of the MapTo property.</summary>
private IPEndPoint m_MapTo;
}
}

View file

@ -0,0 +1,115 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using Org.Mentalis.Proxy;
namespace Org.Mentalis.Proxy.PortMap {
///<summary>Listens on a specific port on the proxy server and forwards all incoming data to a specific port on another server.</summary>
public sealed class PortMapListener : Listener {
///<summary>Initializes a new instance of the PortMapListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<param name="MapToIP">The address to forward to.</param>
///<remarks>The object will listen on all network addresses on the computer.</remarks>
///<exception cref="ArgumentException"><paramref name="Port">Port</paramref> is not positive.</exception>
///<exception cref="ArgumentNullException"><paramref name="MapToIP">MapToIP</paramref> is null.</exception>
public PortMapListener(int Port, IPEndPoint MapToIP) : this(IPAddress.Any, Port, MapToIP) {}
///<summary>Initializes a new instance of the PortMapListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<param name="Address">The network address to listen on.</param>
///<param name="MapToIP">The address to forward to.</param>
///<remarks>For security reasons, <paramref name="Address">Address</paramref> should not be IPAddress.Any.</remarks>
///<exception cref="ArgumentNullException">Address or <paramref name="MapToIP">MapToIP</paramref> is null.</exception>
///<exception cref="ArgumentException">Port is not positive.</exception>
public PortMapListener(IPAddress Address, int Port, IPEndPoint MapToIP) : base(Port, Address) {
MapTo = MapToIP;
}
///<summary>Initializes a new instance of the PortMapListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<param name="Address">The network address to listen on.</param>
///<param name="MapToPort">The port to forward to.</param>
///<param name="MapToAddress">The IP address to forward to.</param>
///<remarks>For security reasons, Address should not be IPAddress.Any.</remarks>
///<exception cref="ArgumentNullException">Address or MapToAddress is null.</exception>
///<exception cref="ArgumentException">Port or MapToPort is invalid.</exception>
public PortMapListener(IPAddress Address, int Port, IPAddress MapToAddress, int MapToPort) : this(Address, Port, new IPEndPoint(MapToAddress, MapToPort)) {}
///<summary>Gets or sets the IP EndPoint to map all incoming traffic to.</summary>
///<value>An IPEndPoint that holds the IP address and port to use when redirecting incoming traffic.</value>
///<exception cref="ArgumentNullException">The specified value is null.</exception>
///<returns>An IP EndPoint specifying the host and port to map all incoming traffic to.</returns>
private IPEndPoint MapTo {
get {
return m_MapTo;
}
set {
if (value == null)
throw new ArgumentNullException();
m_MapTo = value;
}
}
///<summary>Called when there's an incoming client connection waiting to be accepted.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
public override void OnAccept(IAsyncResult ar) {
try {
Socket NewSocket = ListenSocket.EndAccept(ar);
if (NewSocket != null) {
PortMapClient NewClient = new PortMapClient(NewSocket, new DestroyDelegate(this.RemoveClient), MapTo);
AddClient(NewClient);
NewClient.StartHandshake();
}
} catch {}
try {
//Restart Listening
ListenSocket.BeginAccept(new AsyncCallback(this.OnAccept), ListenSocket);
} catch {
Dispose();
}
}
///<summary>Returns a string representation of this object.</summary>
///<returns>A string with information about this object.</returns>
public override string ToString() {
return "PORTMAP service on " + Address.ToString() + ":" + Port.ToString();
}
///<summary>Returns a string that holds all the construction information for this object.</summary>
///<value>A string that holds all the construction information for this object.</value>
public override string ConstructString {
get {
return "host:" + Address.ToString() + ";int:" + Port.ToString() + ";host:" + MapTo.Address.ToString() + ";int:" + MapTo.Port.ToString();
}
}
// private variables
/// <summary>Holds the value of the MapTo property.</summary>
private IPEndPoint m_MapTo;
}
}

429
ProxyServer/Proxy.cs Normal file
View file

@ -0,0 +1,429 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Collections;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Globalization;
using Org.Mentalis.Proxy;
using Org.Mentalis.Proxy.Ftp;
using Org.Mentalis.Proxy.Http;
using Org.Mentalis.Proxy.Socks;
using Org.Mentalis.Proxy.PortMap;
using Org.Mentalis.Proxy.Socks.Authentication;
using Org.Mentalis.Utilities.ConsoleAttributes;
namespace Org.Mentalis.Proxy {
/// <summary>
/// Represents an item in a Listeners collection.
/// </summary>
public struct ListenEntry {
/// <summary>
/// The Listener object.
/// </summary>
public Listener listener;
/// <summary>
/// The Listener's ID. It must be unique troughout the Listeners collection.
/// </summary>
public Guid guid;
/// <summary>
/// Determines whether the specified Object is equal to the current Object.
/// </summary>
/// <param name="obj">The Object to compare with the current Object.</param>
/// <returns>True if the specified Object is equal to the current Object; otherwise, false.</returns>
public override bool Equals(object obj) {
return ((ListenEntry)obj).guid.Equals(guid);
}
/// <summary>
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return guid.GetHashCode();
}
}
/// <summary>
/// Defines the class that controls the settings and listener objects.
/// </summary>
public class Proxy {
/// <summary>
/// Entry point of the application.
/// </summary>
public static void Main() {
try {
string dir = Environment.CurrentDirectory;
if (!dir.Substring(dir.Length - 1, 1).Equals(@"\"))
dir += @"\";
Proxy prx = new Proxy(dir + "config.xml");
prx.Start();
} catch {
Console.WriteLine("The program ended abnormally!");
}
}
/// <summary>
/// Initializes a new Proxy instance.
/// </summary>
/// <param name="file">The XML configuration file to use.</param>
public Proxy(string file) {
Config = new ProxyConfig(this, file);
}
/// <summary>
/// Starts a new Proxy server by reading the data from the configuration file and start listening on the specified ports.
/// </summary>
public void Start() {
// Initialize some objects
StartTime = DateTime.Now;
if (System.IO.File.Exists(Config.File))
Config.LoadData();
// Start the proxy
string command;
Console.WriteLine("\r\n Mentalis.org Proxy\r\n ~~~~~~~~~~~~~~~~~~\r\n\r\n (type 'help' for the command list)");
Console.Write("\r\n>");
command = Console.ReadLine().ToLower(CultureInfo.InvariantCulture);
while (!command.Equals("exit")) {
switch(command) {
case "help":
ShowHelp();
break;
case "uptime":
ShowUpTime();
break;
case "version":
ShowVersion();
break;
case "adduser":
ShowAddUser();
break;
case "deluser":
ShowDelUser();
break;
case "listusers":
ShowUsers();
break;
case "addlistener":
ShowAddListener();
break;
case "listlisteners":
ShowListeners();
break;
case "dellistener":
ShowDelListener();
break;
default:
Console.WriteLine("Command not understood.");
break;
}
Console.Write("\r\n>");
command = Console.ReadLine().ToLower(CultureInfo.InvariantCulture);
}
Stop();
Console.WriteLine("Goodbye...");
}
/// <summary>
/// Asks the user which listener to delete.
/// </summary>
protected void ShowDelListener() {
Console.WriteLine("Please enter the ID of the listener you want to delete:\r\n (use the 'listlisteners' command to show all the listener IDs)");
string id = Console.ReadLine();
if (id != "") {
try {
ListenEntry le = new ListenEntry();
le.guid = new Guid(id);
if (!Listeners.Contains(le)) {
Console.WriteLine("Specified ID not found in list!");
return;
} else {
this[Listeners.IndexOf(le)].Dispose();
Listeners.Remove(le);
Config.SaveData();
}
} catch {
Console.WriteLine("Invalid ID tag!");
return;
}
Console.WriteLine("Listener removed from the list.");
}
}
/// <summary>
/// Shows the Listeners list.
/// </summary>
protected void ShowListeners() {
for(int i = 0; i < Listeners.Count; i++) {
Console.WriteLine(((ListenEntry)Listeners[i]).listener.ToString());
Console.WriteLine(" id: " + ((ListenEntry)Listeners[i]).guid.ToString("N"));
}
}
/// <summary>
/// Asks the user which listener to add.
/// </summary>
protected void ShowAddListener() {
Console.WriteLine("Please enter the full class name of the Listener object you're trying to add:\r\n (ie. Org.Mentalis.Proxy.Http.HttpListener)");
string classtype = Console.ReadLine();
if (classtype == "")
return;
else if(Type.GetType(classtype) == null) {
Console.WriteLine("The specified class does not exist!");
return;
}
Console.WriteLine("Please enter the construction parameters:");
string construct = Console.ReadLine();
object listenObject = CreateListener(classtype, construct);
if (listenObject == null) {
Console.WriteLine("Invalid construction string.");
return;
}
Listener listener;
try {
listener = (Listener)listenObject;
} catch {
Console.WriteLine("The specified object is not a valid Listener object.");
return;
}
try {
listener.Start();
AddListener(listener);
} catch {
Console.WriteLine("Error while staring the Listener.\r\n(Perhaps the specified port is already in use?)");
return;
}
Config.SaveData();
}
/// <summary>
/// Shows a list of commands in the console.
/// </summary>
protected void ShowHelp() {
Console.WriteLine(" help - Shows this help message\r\n uptime - Shows the uptime of the proxy server\r\n version - Prints the version of this program\r\n listusers - Lists all users\r\n adduser - Adds a user to the user list\r\n deluser - Deletes a user from the user list\r\n listlisteners - Lists all the listeners\r\n addlistener - Adds a new listener\r\n dellistener - Deletes a listener\r\n\r\n Read the readme.txt file for more help.");
}
/// <summary>
/// Shows the uptime of this proxy server.
/// </summary>
protected void ShowUpTime() {
TimeSpan uptime = DateTime.Now.Subtract(StartTime);
Console.WriteLine("Up " + uptime.ToString());
}
/// <summary>
/// Shows the version number of this proxy server.
/// </summary>
protected void ShowVersion() {
Console.WriteLine("This is version " + Assembly.GetCallingAssembly().GetName().Version.ToString(3) + " of the Mentalis.org proxy server.");
}
/// <summary>
/// Asks the user which username to add.
/// </summary>
protected void ShowAddUser() {
Console.Write("Please enter the username to add: ");
string name = Console.ReadLine();
if (Config.UserList.IsUserPresent(name)) {
Console.WriteLine("Username already exists in database.");
return;
}
Console.Write("Please enter the password: ");
ConsoleAttributes.EchoInput = false;
string pass1 = Console.ReadLine();
Console.Write("\r\nPlease enter the password again: ");
string pass2 = Console.ReadLine();
ConsoleAttributes.EchoInput = true;
if (!pass1.Equals(pass2)) {
Console.WriteLine("\r\nThe passwords do not match.");
return;
}
Config.SaveUserPass(name, pass1);
Console.WriteLine("\r\nUser successfully added.");
}
/// <summary>
/// Asks the user which username to delete.
/// </summary>
protected void ShowDelUser() {
Console.Write("Please enter the username to remove: ");
string name = Console.ReadLine();
if (!Config.UserList.IsUserPresent(name)) {
Console.WriteLine("Username not present in database.");
return;
}
Config.RemoveUser(name);
Console.WriteLine("User '" + name + "' successfully removed.");
}
/// <summary>
/// Shows a list of usernames in the console.
/// </summary>
protected void ShowUsers() {
if (Config.UserList == null || Config.UserList.Keys.Length == 0) {
Console.WriteLine("There are no users in the user list.");
} else {
Console.WriteLine("The following " + Config.UserList.Keys.Length.ToString() + " users are allowed to use the SOCKS5 proxy:");
Console.WriteLine(string.Join(", ", Config.UserList.Keys));
}
}
/// <summary>
/// Stops the proxy server.
/// </summary>
/// <remarks>When this method is called, all listener and client objects will be disposed.</remarks>
public void Stop() {
// Stop listening and clear the Listener list
for (int i = 0; i < ListenerCount; i++) {
Console.WriteLine(this[i].ToString() + " stopped.");
this[i].Dispose();
}
Listeners.Clear();
}
/// <summary>
/// Adds a listener to the Listeners list.
/// </summary>
/// <param name="newItem">The new Listener to add.</param>
public void AddListener(Listener newItem) {
if (newItem == null)
throw new ArgumentNullException();
ListenEntry le = new ListenEntry();
le.listener = newItem;
le.guid = Guid.NewGuid();
while (Listeners.Contains(le)) {
le.guid = Guid.NewGuid();
}
Listeners.Add(le);
Console.WriteLine(newItem.ToString() + " started.");
}
/// <summary>
/// Creates a new Listener obejct from a given listener name and a given listener parameter string.
/// </summary>
/// <param name="type">The type of object to instantiate.</param>
/// <param name="cpars"></param>
/// <returns></returns>
public Listener CreateListener(string type, string cpars) {
try {
string [] parts = cpars.Split(';');
object [] pars = new object[parts.Length];
string oval = null, otype = null;
int ret;
// Start instantiating the objects to give to the constructor
for(int i = 0; i < parts.Length; i++) {
ret = parts[i].IndexOf(':');
if (ret >= 0) {
otype = parts[i].Substring(0, ret);
oval = parts[i].Substring(ret + 1);
} else {
otype = parts[i];
}
switch (otype.ToLower(CultureInfo.InvariantCulture)) {
case "int":
pars[i] = int.Parse(oval);
break;
case "host":
pars[i] = Dns.GetHostEntry(oval).AddressList[0];
break;
case "authlist":
pars[i] = Config.UserList;
break;
case "null":
pars[i] = null;
break;
case "string":
pars[i] = oval;
break;
case "ip":
pars[i] = IPAddress.Parse(oval);
break;
default:
pars[i] = null;
break;
}
}
return (Listener)Activator.CreateInstance(Type.GetType(type), pars);
} catch {
return null;
}
}
/// <summary>
/// Gets the collection that contains all the Listener objects.
/// </summary>
/// <value>An ArrayList object that contains all the Listener objects.</value>
protected ArrayList Listeners {
get {
return m_Listeners;
}
}
/// <summary>
/// Gets the number of Listener objects.
/// </summary>
/// <value>An integer specifying the number of Listener objects.</value>
internal int ListenerCount {
get {
return Listeners.Count;
}
}
/// <summary>
/// Gets the Listener object at the specified position.
/// </summary>
/// <value>The Listener instance at position <c>index</c>.</value>
internal virtual Listener this[int index] {
get {
return ((ListenEntry)Listeners[index]).listener;
}
}
/// <summary>
/// Gets or sets the date when this Proxy server was first started.
/// </summary>
/// <value>A DateTime structure that indicates when this Proxy server was first started.</value>
protected DateTime StartTime {
get {
return m_StartTime;
}
set {
m_StartTime = value;
}
}
/// <summary>
/// Gets or sets the configuration object for this Proxy server.
/// </summary>
/// <value>A ProxyConfig instance that represents the configuration object for this Proxy server.</value>
protected ProxyConfig Config {
get {
return m_Config;
}
set {
m_Config = value;
}
}
// private variables
/// <summary>Holds the value of the StartTime property.</summary>
private DateTime m_StartTime;
/// <summary>Holds the value of the Config property.</summary>
private ProxyConfig m_Config;
/// <summary>Holds the value of the Listeners property.</summary>
private ArrayList m_Listeners = new ArrayList();
}
}

488
ProxyServer/ProxyConfig.cs Normal file
View file

@ -0,0 +1,488 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.IO;
using System.Xml;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using System.Security.Cryptography;
using System.Reflection;
using System.Reflection.Emit;
using System.Net;
using Org.Mentalis.Proxy;
using Org.Mentalis.Proxy.Socks.Authentication;
using System.Globalization;
namespace Org.Mentalis.Proxy {
/// <summary>
/// Stores the configuration settings of this proxy server.
/// </summary>
public sealed class ProxyConfig {
/// <summary>
/// Initializes a new ProxyConfig instance.
/// </summary>
/// <param name="parent">The parent of this ProxyCondif instance.</param>
/// <param name="file">The XML file to read data from and store data to.</param>
/// <exception cref="ArgumentNullException"><c>file</c> is null -or- <c>parent</c> is null.</exception>
public ProxyConfig(Proxy parent, string file) {
if (file == null || parent == null)
throw new ArgumentNullException();
m_File = file;
m_Parent = parent;
}
/// <summary>
/// Reads a string from the settings section.
/// </summary>
/// <param name="name">The key to read from.</param>
/// <returns>The string value that corresponds with the specified key, or an empty string if the specified key was not found in the collection.</returns>
public string ReadString(string name) {
return ReadString(name, "");
}
/// <summary>
/// Reads a string from the settings section.
/// </summary>
/// <param name="name">The key to read from.</param>
/// <param name="def">The default string to return.</param>
/// <returns>The string value that corresponds with the specified key, or <c>def</c> if the specified key was not found in the collection.</returns>
public string ReadString(string name, string def) {
if (name == null)
return def;
if (!Settings.ContainsKey(name))
return def;
return Settings[name];
}
/// <summary>
/// Reads a byte array from the settings section.
/// </summary>
/// <param name="name">The key to read from.</param>
/// <returns>The array of bytes that corresponds with the specified key, or <c>null</c> if the specified key was not found in the collection.</returns>
public byte[] ReadBytes(string name) {
string ret = ReadString(name, null);
if (ret == null)
return null;
return Convert.FromBase64String(ret);
}
/// <summary>
/// Reads an integer from the settings section.
/// </summary>
/// <param name="name">The key to read from.</param>
/// <returns>The integer that corresponds with the specified key, or 0 if the specified key was not found in the collection.</returns>
public int ReadInt(string name) {
return ReadInt(name, 0);
}
/// <summary>
/// Reads an integer from the settings section.
/// </summary>
/// <param name="name">The key to read from.</param>
/// <param name="def">The default integer to return.</param>
/// <returns>The integer that corresponds with the specified key, or <c>def</c> if the specified key was not found in the collection.</returns>
public int ReadInt(string name, int def) {
if (name == null)
return def;
if (!Settings.ContainsKey(name))
return def;
return int.Parse(Settings[name]);
}
/// <summary>
/// Saves a string to the settings section.
/// </summary>
/// <param name="name">The key of the setting.</param>
/// <param name="data">The string data of the setting.</param>
public void SaveSetting(string name, string data) {
SaveSetting(name, data, true);
}
/// <summary>
/// Saves a string to the settings section.
/// </summary>
/// <param name="name">The key of the setting.</param>
/// <param name="data">The string data of the setting.</param>
/// <param name="saveData">True if the data has to be written to the XML file, false otherwise.</param>
public void SaveSetting(string name, string data, bool saveData) {
if (name == null || data == null)
throw new ArgumentNullException();
if (Settings.ContainsKey(name))
Settings[name] = data;
else
Settings.Add(name, data);
if (saveData)
SaveData();
}
/// <summary>
/// Saves an integer to the settings section.
/// </summary>
/// <param name="name">The key of the setting.</param>
/// <param name="data">The integer data of the setting.</param>
public void SaveSetting(string name, int data) {
SaveSetting(name, data, true);
}
/// <summary>
/// Saves an integer to the settings section.
/// </summary>
/// <param name="name">The key of the setting.</param>
/// <param name="data">The integer data of the setting.</param>
/// <param name="saveData">True if the data has to be written to the XML file, false otherwise.</param>
public void SaveSetting(string name, int data, bool saveData) {
SaveSetting(name, data.ToString(), saveData);
}
/// <summary>
/// Saves an array of bytes to the settings section.
/// </summary>
/// <param name="name">The key of the setting.</param>
/// <param name="data">The byte data of the setting.</param>
public void SaveSetting(string name, byte [] data) {
SaveSetting(name, data, true);
}
/// <summary>
/// Saves an array of bytes to the settings section.
/// </summary>
/// <param name="name">The key of the setting.</param>
/// <param name="data">The byte data of the setting.</param>
/// <param name="saveData">True if the data has to be written to the XML file, false otherwise.</param>
public void SaveSetting(string name, byte [] data, bool saveData) {
if (data == null)
throw new ArgumentNullException();
SaveSetting(name, Convert.ToBase64String(data), saveData);
}
/// <summary>
/// Saves a username and password combination to the authentication list.
/// </summary>
/// <param name="username">The username to add.</param>
/// <param name="password">The password to add.</param>
/// <exception cref="ArgumentNullException"><c>username</c> is null -or- <c>password</c> is null.</exception>
/// <exception cref="ArgumentException">The specified username is invalid.</exception>
/// <remarks><p>If the user already exists in the collection, the old password will be changed to the new one.</p><p>The username 'users' is invalid because it is used internally to store the usernames.</p></remarks>
public void SaveUserPass(string username, string password) {
SaveUserPass(username, password, true);
}
/// <summary>
/// Saves a username and password combination to the authentication list.
/// </summary>
/// <param name="username">The username to add.</param>
/// <param name="password">The password to add.</param>
/// <param name="saveData">True if the data has to be written to the XML file, false otherwise.</param>
/// <exception cref="ArgumentNullException"><c>username</c> is null -or- <c>password</c> is null.</exception>
/// <exception cref="ArgumentException">The specified username is invalid.</exception>
/// <remarks><p>If the user already exists in the collection, the old password will be changed to the new one.</p><p>The username 'users' is invalid because it is used internally to store the usernames.</p><p>The password will be hashed before it is stored in the authentication list.</p></remarks>
public void SaveUserPass(string username, string password, bool saveData) {
if (username == null || password == null)
throw new ArgumentNullException();
if (username.ToLower(CultureInfo.InvariantCulture) == "users" || username == "")
throw new ArgumentException();
if (UserList.IsUserPresent(username))
UserList.RemoveItem(username);
UserList.AddItem(username, password);
if (saveData)
SaveData();
}
/// <summary>
/// Saves a username and password hash combination to the authentication list.
/// </summary>
/// <param name="username">The username to add.</param>
/// <param name="passHash">The password hash to add.</param>
/// <exception cref="ArgumentNullException"><c>username</c> is null -or- <c>passHash</c> is null.</exception>
/// <exception cref="ArgumentException">The specified username is invalid.</exception>
/// <remarks><p>If the user already exists in the collection, the old password hash will be changed to the new one.</p><p>The username 'users' is invalid because it is used internally to store the usernames.</p><p>The password will <em>not</em> be hashed before it is stored in the authentication list. The user must make sure it is a valid MD5 hash.</p></remarks>
public void SaveUserHash(string username, string passHash) {
SaveUserHash(username, passHash, true);
}
/// <summary>
/// Saves a username and password hash combination to the authentication list.
/// </summary>
/// <param name="username">The username to add.</param>
/// <param name="passHash">The password hash to add.</param>
/// <param name="saveData">True if the data has to be written to the XML file, false otherwise.</param>
/// <exception cref="ArgumentNullException"><c>username</c> is null -or- <c>passHash</c> is null.</exception>
/// <exception cref="ArgumentException">The specified username is invalid.</exception>
/// <remarks><p>If the user already exists in the collection, the old password hash will be changed to the new one.</p><p>The username 'users' is invalid because it is used internally to store the usernames.</p><p>The password will <em>not</em> be hashed before it is stored in the authentication list. The user must make sure it is a valid MD5 hash.</p></remarks>
public void SaveUserHash(string username, string passHash, bool saveData) {
if (username == null || passHash == null)
throw new ArgumentNullException();
if (username.ToLower(CultureInfo.InvariantCulture) == "users" || username == "")
throw new ArgumentException();
UserList.AddHash(username, passHash);
if (saveData)
SaveData();
}
/// <summary>
/// Removes a user from the authentication list.
/// </summary>
/// <param name="user">The user to remove.</param>
public void RemoveUser(string user) {
RemoveUser(user, true);
}
/// <summary>
/// Removes a user from the authentication list.
/// </summary>
/// <param name="user">The user to remove.</param>
/// <param name="save">True if the data has to be written to the XML file, false otherwise.</param>
public void RemoveUser(string user, bool save) {
if (user == null)
throw new ArgumentNullException();
UserList.RemoveItem(user);
if (save)
SaveData();
}
/// <summary>
/// Saves the data in this class to an XML file.
/// </summary>
public void SaveData() {
XmlTextWriter writer = null;
try {
writer = new XmlTextWriter(File, Encoding.ASCII);
writer.Indentation = 2;
writer.Formatting = Formatting.Indented;
writer.WriteStartElement("MentalisProxy");
// Write the version
writer.WriteStartElement("Version");
writer.WriteStartAttribute("", "value", "");
writer.WriteString(Assembly.GetCallingAssembly().GetName().Version.ToString(2));
writer.WriteEndAttribute();
writer.WriteEndElement();
// Write the settings
SaveSettings(writer);
// Write the Authentication list to the file
SaveUsers(writer);
// Write the Listeners list to the file
SaveListeners(writer);
// Clean up
writer.WriteEndElement();
} catch {
} finally {
if (writer != null)
writer.Close();
}
}
/// <summary>
/// Saves the settings in this class to an XML writer.
/// </summary>
/// <param name="writer">The XML writer to save the data to.</param>
private void SaveSettings(XmlTextWriter writer) {
writer.WriteStartElement("Settings");
string [] keys = new string[Settings.Count];
Settings.Keys.CopyTo(keys, 0);
for (int i = 0; i < keys.Length; i++) {
writer.WriteStartElement(keys[i]);
writer.WriteStartAttribute("", "value", "");
writer.WriteString(Settings[keys[i]]);
writer.WriteEndAttribute();
writer.WriteEndElement();
}
writer.WriteEndElement();
}
/// <summary>
/// Saves the authentication list to an XML writer.
/// </summary>
/// <param name="writer">The XML writer to save the users to.</param>
private void SaveUsers(XmlTextWriter writer) {
writer.WriteStartElement("Users");
string [] keys = UserList.Keys;
string [] hashes = UserList.Hashes;
for (int i = 0; i < keys.Length; i++) {
writer.WriteStartElement(keys[i]);
writer.WriteStartAttribute("", "value", "");
writer.WriteString(hashes[i]);
writer.WriteEndAttribute();
writer.WriteEndElement();
}
writer.WriteEndElement();
}
/// <summary>
/// Saves the listeners to an XML writer.
/// </summary>
/// <param name="writer">The XML writer to save the users to.</param>
private void SaveListeners(XmlTextWriter writer) {
writer.WriteStartElement("Listeners");
lock (Parent) {
for (int i = 0; i < Parent.ListenerCount; i++) {
writer.WriteStartElement("listener");
// Write the type, eg 'Org.Mentalis.Proxy.Http.HttpListener'
writer.WriteStartAttribute("", "type", "");
writer.WriteString(Parent[i].GetType().FullName);
writer.WriteEndAttribute();
// Write the construction string
writer.WriteStartAttribute("", "value", "");
writer.WriteString(Parent[i].ConstructString);
writer.WriteEndAttribute();
writer.WriteEndElement();
}
}
writer.WriteEndElement();
}
/// <summary>
/// Loads the data from an XML file.
/// </summary>
public void LoadData() {
if (!System.IO.File.Exists(File))
throw new FileNotFoundException();
XmlTextReader reader = null;
try {
reader = new XmlTextReader(File);
// Read until we reach the MentalisProxy element
while (reader.Read() && reader.Name.ToLower(CultureInfo.InvariantCulture) != "mentalisproxy") {}
// Read until we reach the MentalisProxy element again (the end tag)
while (reader.Read() && reader.Name.ToLower(CultureInfo.InvariantCulture) != "mentalisproxy") {
if (!reader.IsEmptyElement) {
switch(reader.Name.ToLower(CultureInfo.InvariantCulture)) {
case "settings":
Settings.Clear();
LoadSettings(reader);
break;
case "users":
UserList.Clear();
LoadUsers(reader);
break;
case "listeners":
LoadListeners(reader);
break;
}
}
}
} catch {
throw new XmlException ("Malformed XML initialisation file.", null);
} finally {
if (reader != null)
reader.Close();
}
}
/// <summary>
/// Loads the settings from an XML file.
/// </summary>
/// <param name="reader">The XML reader to read the settings from.</param>
private void LoadSettings(XmlTextReader reader) {
// Read until we reach the Settings element end tag
while (reader.Read() && reader.Name.ToLower(CultureInfo.InvariantCulture) != "settings") {
if (reader.Name != null && reader["value"] != null)
SaveSetting(reader.Name, reader["value"], false);
}
}
/// <summary>
/// Loads the userlist from an XML file.
/// </summary>
/// <param name="reader">The XML reader to read the users from.</param>
private void LoadUsers(XmlTextReader reader) {
// Read until we reach the Settings element end tag
while (reader.Read() && reader.Name.ToLower(CultureInfo.InvariantCulture) != "users") {
if (reader.Name != null && reader["value"] != null)
SaveUserHash(reader.Name, reader["value"], false);
}
}
/// <summary>
/// Loads the listeners list from an XML file.
/// </summary>
/// <param name="reader">The XML reader to read the users from.</param>
private void LoadListeners(XmlTextReader reader) {
// Read until we reach the Listeners element end tag
Listener listener = null;
while (reader.Read() && reader.Name.ToLower(CultureInfo.InvariantCulture) != "listeners") {
if (reader.Name != null && reader["value"] != null && reader["type"] != null) {
listener = Parent.CreateListener(reader["type"], reader["value"]);
if (listener != null) {
try {
listener.Start();
} catch {}
Parent.AddListener(listener);
}
}
}
}
/// <summary>
/// Gets the full path to the XML data file.
/// </summary>
/// <value>A String that holds the full path to the XML data file.</value>
public string File {
get {
return m_File;
}
}
/// <summary>
/// Gets the parent object of this ProxyConfig class.
/// </summary>
/// <value>An instance of the Proxy class.</value>
public Proxy Parent {
get {
return m_Parent;
}
}
/// <summary>
/// Gets the dictionary that holds the settings.
/// </summary>
/// <value>An instance of the StringDictionary class that holds the settings.</value>
private StringDictionary Settings {
get {
return m_Settings;
}
}
/// <summary>
/// Gets the userlist.
/// </summary>
/// <value>An instance of the AuthenticationList class that holds all the users and their corresponding password hashes.</value>
internal AuthenticationList UserList {
get {
return m_UserList;
}
}
// private variables
/// <summary>Holds the value of the File property.</summary>
private string m_File;
/// <summary>Holds the value of the Settings property.</summary>
private StringDictionary m_Settings = new StringDictionary();
/// <summary>Holds the value of the UserList property.</summary>
private AuthenticationList m_UserList = new AuthenticationList();
/// <summary>Holds the value of the Parent property.</summary>
private Proxy m_Parent;
}
}
/*
<MentalisProxy>
<Version value="1.0" />
<Settings>
<authorize value="never" />
<admin_hash value="Gh3JHJBzJcaScd3wyUS8cg==" />
<config_ip value="localhost" />
<config_port value="4" />
<errorlog value="errors.log" />
</Settings>
<Users>
<user value="myhash" />
<pieter value="WhBei51A4TKXgNYuoiZdig==" />
<kris value="X03MO1qnZdYdgyfeuILPmQ==" />
</Users>
<Listeners>
<listener type="Org.Mentalis.Proxy.Http.HttpListener" value="host:0.0.0.0;int:100" />
<listener type="Org.Mentalis.Proxy.Ftp.FtpListener" value="host:0.0.0.0;int:21" />
<listener type="Org.Mentalis.Proxy.Socks.SocksListener" value="host:0.0.0.0;int:1080;authlist" />
<listener type="Org.Mentalis.Proxy.PortMap.PortMapListener" value="host:0.0.0.0;int:12345;host:msnews.microsoft.com;int:119" />
</Listeners>
</MentalisProxy>
*/

View file

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{EB773C85-DDF7-4031-A993-102B9C8ED02C}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>ProxyServer</AssemblyName>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Library</OutputType>
<RootNamespace>ProxyServer</RootNamespace>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<OldToolsVersion>2.0</OldToolsVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\Debug\</OutputPath>
<BaseAddress>285212672</BaseAddress>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<Optimize>false</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<WarningLevel>4</WarningLevel>
<DebugType>full</DebugType>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\Release\</OutputPath>
<BaseAddress>285212672</BaseAddress>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<FileAlignment>4096</FileAlignment>
<Optimize>true</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<WarningLevel>4</WarningLevel>
<DebugType>none</DebugType>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Data">
<Name>System.Data</Name>
</Reference>
<Reference Include="System.Xml">
<Name>System.XML</Name>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="AuthBase.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="AuthenticationList.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="AuthNone.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="AuthUserPass.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Client.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ConsoleAttributes.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="FtpClient.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="FtpDataConnection.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="FtpListener.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="HttpClient.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="HttpListener.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Listener.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="PortMapClient.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="PortMapListener.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Proxy.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ProxyConfig.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Socks4Handler.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Socks5Handler.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="SocksClient.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="SocksHandler.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="SocksListener.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishUrlHistory />
<InstallUrlHistory />
<SupportUrlHistory />
<UpdateUrlHistory />
<BootstrapperUrlHistory />
<ErrorReportUrlHistory />
<FallbackCulture>en-US</FallbackCulture>
<VerifyUploadedFiles>false</VerifyUploadedFiles>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,151 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Text;
using System.Net;
using System.Net.Sockets;
using Org.Mentalis.Proxy;
namespace Org.Mentalis.Proxy.Socks {
///<summary>Implements the SOCKS4 and SOCKS4a protocols.</summary>
internal sealed class Socks4Handler : SocksHandler {
///<summary>Initializes a new instance of the Socks4Handler class.</summary>
///<param name="ClientConnection">The connection with the client.</param>
///<param name="Callback">The method to call when the SOCKS negotiation is complete.</param>
///<exception cref="ArgumentNullException"><c>Callback</c> is null.</exception>
public Socks4Handler(Socket ClientConnection, NegotiationCompleteDelegate Callback) : base(ClientConnection, Callback) {}
///<summary>Checks whether a specific request is a valid SOCKS request or not.</summary>
///<param name="Request">The request array to check.</param>
///<returns>True is the specified request is valid, false otherwise</returns>
protected override bool IsValidRequest(byte [] Request) {
try {
if (Request[0] != 1 && Request[0] != 2) { //CONNECT or BIND
Dispose(false);
} else {
if (Request[3] == 0 && Request[4] == 0 && Request[5] == 0 && Request[6] != 0) { //Use remote DNS
int Ret = Array.IndexOf(Request, (byte)0, 7);
if (Ret > -1)
return Array.IndexOf(Request, (byte)0, Ret + 1) != -1;
} else {
return Array.IndexOf(Request, (byte)0, 7) != -1;
}
}
} catch {}
return false;
}
///<summary>Processes a SOCKS request from a client.</summary>
///<param name="Request">The request to process.</param>
protected override void ProcessRequest(byte [] Request) {
int Ret;
try {
if (Request[0] == 1) { // CONNECT
IPAddress RemoteIP;
int RemotePort = Request[1] * 256 + Request[2];
Ret = Array.IndexOf(Request, (byte)0, 7);
Username = Encoding.ASCII.GetString(Request, 7, Ret - 7);
if (Request[3] == 0 && Request[4] == 0 && Request[5] == 0 && Request[6] != 0) {// Use remote DNS
Ret = Array.IndexOf(Request, (byte)0, Ret + 1);
RemoteIP = Dns.GetHostEntry(Encoding.ASCII.GetString(Request, Username.Length + 8, Ret - Username.Length - 8)).AddressList[0];
} else { //Do not use remote DNS
RemoteIP = IPAddress.Parse(Request[3].ToString() + "." + Request[4].ToString() + "." + Request[5].ToString() + "." + Request[6].ToString());
}
RemoteConnection = new Socket(RemoteIP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
RemoteConnection.BeginConnect(new IPEndPoint(RemoteIP, RemotePort), new AsyncCallback(this.OnConnected), RemoteConnection);
} else if (Request[0] == 2) { // BIND
byte[] Reply = new byte[8];
byte[] LocalIP = Listener.GetLocalExternalIP().GetAddressBytes();
AcceptSocket = new Socket(IPAddress.Any.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
AcceptSocket.Bind(new IPEndPoint(IPAddress.Any, 0));
AcceptSocket.Listen(50);
RemoteBindIP = IPAddress.Parse(Request[3].ToString() + "." + Request[4].ToString() + "." + Request[5].ToString() + "." + Request[6].ToString());
Reply[0] = 0; //Reply version 0
Reply[1] = 90; //Everything is ok :)
Reply[2] = (byte)(Math.Floor((double)((IPEndPoint)AcceptSocket.LocalEndPoint).Port / 256)); //Port/1
Reply[3] = (byte)(((IPEndPoint)AcceptSocket.LocalEndPoint).Port % 256); //Port/2
Reply[4] = LocalIP[0];// (byte)(Math.Floor((double)(LocalIP % 256))); //IP Address/1
Reply[5] = LocalIP[1];// (byte)(Math.Floor((double)(LocalIP % 65536) / 256)); //IP Address/2
Reply[6] = LocalIP[2];// (byte)(Math.Floor((double)(LocalIP % 16777216) / 65536)); //IP Address/3
Reply[7] = LocalIP[3];// (byte)(Math.Floor((double)LocalIP / 16777216)); //IP Address/4
Connection.BeginSend(Reply, 0, Reply.Length, SocketFlags.None, new AsyncCallback(this.OnStartAccept), Connection);
}
} catch {
Dispose(91);
}
}
///<summary>Called when we're successfully connected to the remote host.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnConnected(IAsyncResult ar) {
try {
RemoteConnection.EndConnect(ar);
Dispose(90);
} catch {
Dispose(91);
}
}
///<summary>Sends a reply to the client connection and disposes it afterwards.</summary>
///<param name="Value">A byte that contains the reply code to send to the client.</param>
protected override void Dispose(byte Value) {
byte [] ToSend;
try {
ToSend = new byte[]{0, Value, (byte)(Math.Floor((double)((IPEndPoint)RemoteConnection.RemoteEndPoint).Port / 256)),
(byte)(((IPEndPoint)RemoteConnection.RemoteEndPoint).Port % 256),
((IPEndPoint)RemoteConnection.RemoteEndPoint).Address.GetAddressBytes()[0],
((IPEndPoint)RemoteConnection.RemoteEndPoint).Address.GetAddressBytes()[1],
((IPEndPoint)RemoteConnection.RemoteEndPoint).Address.GetAddressBytes()[2],
((IPEndPoint)RemoteConnection.RemoteEndPoint).Address.GetAddressBytes()[3]};
} catch {
ToSend = new byte[]{0, 91, 0, 0, 0, 0, 0, 0};
}
try {
Connection.BeginSend(ToSend, 0, ToSend.Length, SocketFlags.None, (AsyncCallback)(ToSend[1] == 90 ? new AsyncCallback(this.OnDisposeGood) : new AsyncCallback(this.OnDisposeBad)), Connection);
} catch {
Dispose(false);
}
}
///<summary>Called when there's an incoming connection in the AcceptSocket queue.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected override void OnAccept(IAsyncResult ar) {
try {
RemoteConnection = AcceptSocket.EndAccept(ar);
AcceptSocket.Close();
AcceptSocket = null;
if (RemoteBindIP.Equals(((IPEndPoint)RemoteConnection.RemoteEndPoint).Address))
Dispose(90);
else
Dispose(91);
} catch {
Dispose(91);
}
}
}
}

View file

@ -0,0 +1,277 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using System.Text;
using Org.Mentalis.Proxy;
using Org.Mentalis.Proxy.Socks.Authentication;
namespace Org.Mentalis.Proxy.Socks {
///<summary>Implements the SOCKS5 protocol.</summary>
internal sealed class Socks5Handler : SocksHandler {
///<summary>Initializes a new instance of the Socks5Handler class.</summary>
///<param name="ClientConnection">The connection with the client.</param>
///<param name="Callback">The method to call when the SOCKS negotiation is complete.</param>
///<param name="AuthList">The authentication list to use when clients connect.</param>
///<exception cref="ArgumentNullException"><c>Callback</c> is null.</exception>
///<remarks>If the AuthList parameter is null, no authentication will be required when a client connects to the proxy server.</remarks>
public Socks5Handler(Socket ClientConnection, NegotiationCompleteDelegate Callback, AuthenticationList AuthList) : base(ClientConnection, Callback) {
this.AuthList = AuthList;
}
///<summary>Initializes a new instance of the Socks5Handler class.</summary>
///<param name="ClientConnection">The connection with the client.</param>
///<param name="Callback">The method to call when the SOCKS negotiation is complete.</param>
///<exception cref="ArgumentNullException"><c>Callback</c> is null.</exception>
public Socks5Handler(Socket ClientConnection, NegotiationCompleteDelegate Callback) : this(ClientConnection, Callback, null) {}
///<summary>Checks whether a specific request is a valid SOCKS request or not.</summary>
///<param name="Request">The request array to check.</param>
///<returns>True is the specified request is valid, false otherwise</returns>
protected override bool IsValidRequest(byte [] Request) {
try {
return (Request.Length == Request[0] + 1);
} catch {
return false;
}
}
///<summary>Processes a SOCKS request from a client and selects an authentication method.</summary>
///<param name="Request">The request to process.</param>
protected override void ProcessRequest(byte [] Request) {
try {
byte Ret = 255;
for (int Cnt = 1; Cnt < Request.Length; Cnt++) {
if (Request[Cnt] == 0 && AuthList == null) { //0 = No authentication
Ret = 0;
AuthMethod = new AuthNone();
break;
} else if (Request[Cnt] == 2 && AuthList != null) { //2 = user/pass
Ret = 2;
AuthMethod = new AuthUserPass(AuthList);
if (AuthList != null)
break;
}
}
Connection.BeginSend(new byte[]{5, Ret}, 0, 2, SocketFlags.None, new AsyncCallback(this.OnAuthSent), Connection);
} catch {
Dispose(false);
}
}
///<summary>Called when client has been notified of the selected authentication method.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnAuthSent(IAsyncResult ar) {
try {
if (Connection.EndSend(ar) <= 0 || AuthMethod == null) {
Dispose(false);
return;
}
AuthMethod.StartAuthentication(Connection, new AuthenticationCompleteDelegate(this.OnAuthenticationComplete));
} catch {
Dispose(false);
}
}
///<summary>Called when the authentication is complete.</summary>
///<param name="Success">Indicates whether the authentication was successful ot not.</param>
private void OnAuthenticationComplete(bool Success) {
try {
if (Success) {
Bytes = null;
Connection.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnRecvRequest), Connection);
} else {
Dispose(false);
}
} catch {
Dispose(false);
}
}
///<summary>Called when we received the request of the client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnRecvRequest(IAsyncResult ar) {
try {
int Ret = Connection.EndReceive(ar);
if (Ret <= 0) {
Dispose(false);
return;
}
AddBytes(Buffer, Ret);
if (IsValidQuery(Bytes))
ProcessQuery(Bytes);
else
Connection.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnRecvRequest), Connection);
} catch {
Dispose(false);
}
}
///<summary>Checks whether a specified query is a valid query or not.</summary>
///<param name="Query">The query to check.</param>
///<returns>True if the query is valid, false otherwise.</returns>
private bool IsValidQuery(byte [] Query) {
try {
switch(Query[3]) {
case 1: //IPv4 address
return (Query.Length == 10);
case 3: //Domain name
return (Query.Length == Query[4] + 7);
case 4: //IPv6 address
//Not supported
Dispose(8);
return false;
default:
Dispose(false);
return false;
}
} catch {
return false;
}
}
///<summary>Processes a received query.</summary>
///<param name="Query">The query to process.</param>
private void ProcessQuery(byte [] Query) {
try {
switch(Query[1]) {
case 1: //CONNECT
IPAddress RemoteIP = null;
int RemotePort = 0;
if (Query[3] == 1) {
RemoteIP = IPAddress.Parse(Query[4].ToString() + "." + Query[5].ToString() + "." + Query[6].ToString() + "." + Query[7].ToString());
RemotePort = Query[8] * 256 + Query[9];
} else if( Query[3] == 3) {
RemoteIP = Dns.GetHostEntry(Encoding.ASCII.GetString(Query, 5, Query[4])).AddressList[0];
RemotePort = Query[4] + 5;
RemotePort = Query[RemotePort] * 256 + Query[RemotePort + 1];
}
RemoteConnection = new Socket(RemoteIP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
RemoteConnection.BeginConnect(new IPEndPoint(RemoteIP, RemotePort), new AsyncCallback(this.OnConnected), RemoteConnection);
break;
case 2: //BIND
byte[] Reply = new byte[10];
byte[] LocalIP = Listener.GetLocalExternalIP().GetAddressBytes();
AcceptSocket = new Socket(IPAddress.Any.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
AcceptSocket.Bind(new IPEndPoint(IPAddress.Any, 0));
AcceptSocket.Listen(50);
Reply[0] = 5; //Version 5
Reply[1] = 0; //Everything is ok :)
Reply[2] = 0; //Reserved
Reply[3] = 1; //We're going to send a IPv4 address
Reply[4] = LocalIP[0];// (byte)(Math.Floor((double)(LocalIP % 256))); //IP Address/1
Reply[5] = LocalIP[1];//(byte)(Math.Floor((double)(LocalIP % 65536) / 256)); //IP Address/2
Reply[6] = LocalIP[2];//(byte)(Math.Floor((double)(LocalIP % 16777216) / 65536)); //IP Address/3
Reply[7] = LocalIP[3];//(byte)(Math.Floor((double)(LocalIP / 16777216))); //IP Address/4
Reply[8] = (byte)(Math.Floor((double)((IPEndPoint)AcceptSocket.LocalEndPoint).Port / 256)); //Port/1
Reply[9] = (byte)(((IPEndPoint)AcceptSocket.LocalEndPoint).Port % 256); //Port/2
Connection.BeginSend(Reply, 0, Reply.Length, SocketFlags.None, new AsyncCallback(this.OnStartAccept), Connection);
break;
case 3: //ASSOCIATE
//ASSOCIATE is not implemented (yet?)
Dispose(7);
break;
default:
Dispose(7);
break;
}
} catch {
Dispose(1);
}
}
///<summary>Called when we're successfully connected to the remote host.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnConnected(IAsyncResult ar) {
try {
RemoteConnection.EndConnect(ar);
Dispose(0);
} catch {
Dispose(1);
}
}
///<summary>Called when there's an incoming connection in the AcceptSocket queue.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected override void OnAccept(IAsyncResult ar) {
try {
RemoteConnection = AcceptSocket.EndAccept(ar);
AcceptSocket.Close();
AcceptSocket = null;
Dispose(0);
} catch {
Dispose(1);
}
}
///<summary>Sends a reply to the client connection and disposes it afterwards.</summary>
///<param name="Value">A byte that contains the reply code to send to the client.</param>
protected override void Dispose(byte Value) {
byte [] ToSend;
try {
ToSend = new byte[]{5, Value, 0, 1,
((IPEndPoint)RemoteConnection.LocalEndPoint).Address.GetAddressBytes()[0],
((IPEndPoint)RemoteConnection.LocalEndPoint).Address.GetAddressBytes()[1],
((IPEndPoint)RemoteConnection.LocalEndPoint).Address.GetAddressBytes()[2],
((IPEndPoint)RemoteConnection.LocalEndPoint).Address.GetAddressBytes()[3],
(byte)(Math.Floor((double)((IPEndPoint)RemoteConnection.LocalEndPoint).Port / 256)),
(byte)(((IPEndPoint)RemoteConnection.LocalEndPoint).Port % 256)};
} catch {
ToSend = new byte[] {5, 1, 0, 1, 0, 0, 0, 0, 0, 0};
}
try {
Connection.BeginSend(ToSend, 0, ToSend.Length, SocketFlags.None, (AsyncCallback)(ToSend[1] == 0 ? new AsyncCallback(this.OnDisposeGood) : new AsyncCallback(this.OnDisposeBad)), Connection);
} catch {
Dispose(false);
}
}
///<summary>Gets or sets the the AuthBase object to use when trying to authenticate the SOCKS client.</summary>
///<value>The AuthBase object to use when trying to authenticate the SOCKS client.</value>
///<exception cref="ArgumentNullException">The specified value is null.</exception>
private AuthBase AuthMethod {
get {
return m_AuthMethod;
}
set {
if (value == null)
throw new ArgumentNullException();
m_AuthMethod = value;
}
}
///<summary>Gets or sets the AuthenticationList object to use when trying to authenticate the SOCKS client.</summary>
///<value>The AuthenticationList object to use when trying to authenticate the SOCKS client.</value>
private AuthenticationList AuthList {
get {
return m_AuthList;
}
set {
m_AuthList = value;
}
}
// private variables
/// <summary>Holds the value of the AuthList property.</summary>
private AuthenticationList m_AuthList;
/// <summary>Holds the value of the AuthMethod property.</summary>
private AuthBase m_AuthMethod;
}
}

154
ProxyServer/SocksClient.cs Normal file
View file

@ -0,0 +1,154 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using Org.Mentalis.Proxy;
using Org.Mentalis.Proxy.Socks.Authentication;
namespace Org.Mentalis.Proxy.Socks {
///<summary>Relays data between a remote host and a local client, using the SOCKS protocols.</summary>
///<remarks>This class implements the SOCKS4, SOCKS4a and SOCKS5 protocols.</remarks>
///<remarks>If the MustAuthenticate property is set, only SOCKS5 connections are allowed and the AuthList parameter of the constructor should not be null.</remarks>
public sealed class SocksClient : Client {
///<summary>Initializes a new instance of the SocksClient class.</summary>
///<param name="ClientSocket">The Socket connection between this proxy server and the local client.</param>
///<param name="Destroyer">The method to be called when this SocksClient object disconnects from the local client and the remote server.</param>
///<param name="AuthList">The list with valid username/password combinations.</param>
///<remarks>If the AuthList is non-null, every client has to authenticate before he can use this proxy server to relay data. If it is null, the clients don't have to authenticate.</remarks>
public SocksClient(Socket ClientSocket, DestroyDelegate Destroyer, AuthenticationList AuthList) : base(ClientSocket, Destroyer) {
this.AuthList = AuthList;
}
///<summary>Gets or sets the SOCKS handler to be used when communicating with the client.</summary>
///<value>The SocksHandler to be used when communicating with the client.</value>
internal SocksHandler Handler {
get {
return m_Handler;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Handler = value;
}
}
///<summary>Gets or sets the SOCKS handler to be used when communicating with the client.</summary>
///<value>The SocksHandler to be used when communicating with the client.</value>
public bool MustAuthenticate {
get {
return m_MustAuthenticate;
}
set {
m_MustAuthenticate = value;
}
}
///<summary>Starts communication with the client.</summary>
public override void StartHandshake() {
try {
ClientSocket.BeginReceive(Buffer, 0, 1, SocketFlags.None, new AsyncCallback(this.OnStartSocksProtocol), ClientSocket);
} catch {
Dispose();
}
}
///<summary>Called when we have received some data from the client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
private void OnStartSocksProtocol(IAsyncResult ar) {
int Ret;
try {
Ret = ClientSocket.EndReceive(ar);
if (Ret <= 0) {
Dispose();
return;
}
if (Buffer[0] == 4) { //SOCKS4 Protocol
if (MustAuthenticate) {
Dispose();
return;
} else {
Handler = new Socks4Handler(ClientSocket, new NegotiationCompleteDelegate(this.OnEndSocksProtocol));
}
} else if(Buffer[0] == 5) { //SOCKS5 Protocol
if (MustAuthenticate && AuthList == null) {
Dispose();
return;
}
Handler = new Socks5Handler(ClientSocket, new NegotiationCompleteDelegate(this.OnEndSocksProtocol), AuthList);
} else {
Dispose();
return;
}
Handler.StartNegotiating();
} catch {
Dispose();
}
}
///<summary>Called when the SOCKS protocol has ended. We can no start relaying data, if the SOCKS authentication was successful.</summary>
///<param name="Success">Specifies whether the SOCKS negotiation was successful or not.</param>
///<param name="Remote">The connection with the remote server.</param>
private void OnEndSocksProtocol(bool Success, Socket Remote) {
DestinationSocket = Remote;
if (Success)
StartRelay();
else
Dispose();
}
///<summary>Gets or sets the AuthenticationList to use when a computer tries to authenticate on the proxy server.</summary>
///<value>An instance of the AuthenticationList class that contains all the valid username/password combinations.</value>
private AuthenticationList AuthList {
get {
return m_AuthList;
}
set {
m_AuthList = value;
}
}
///<summary>Returns text information about this SocksClient object.</summary>
///<returns>A string representing this SocksClient object.</returns>
public override string ToString() {
try {
if (Handler != null)
return Handler.Username + " (" + ((IPEndPoint)ClientSocket.LocalEndPoint).Address.ToString() +") connected to " + DestinationSocket.RemoteEndPoint.ToString();
else
return "SOCKS connection from " + ((IPEndPoint)ClientSocket.LocalEndPoint).Address.ToString();
} catch {
return "Incoming SOCKS connection";
}
}
// private variables
/// <summary>Holds the value of the AuthList property.</summary>
private AuthenticationList m_AuthList;
/// <summary>Holds the value of the MustAuthenticate property.</summary>
private bool m_MustAuthenticate = false;
/// <summary>Holds the value of the Handler property.</summary>
private SocksHandler m_Handler;
}
}

241
ProxyServer/SocksHandler.cs Normal file
View file

@ -0,0 +1,241 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using Org.Mentalis.Proxy;
namespace Org.Mentalis.Proxy.Socks {
///<summary>Defines the signature of the method that's called when the SOCKS negotiation is complete.</summary>
///<param name="Success">Indicates whether the negotiation was successful or not.</param>
///<param name="Remote">The connection with the remote server.</param>
internal delegate void NegotiationCompleteDelegate(bool Success, Socket Remote);
///<summary>Implements a specific version of the SOCKS protocol.</summary>
internal abstract class SocksHandler {
///<summary>Initializes a new instance of the SocksHandler class.</summary>
///<param name="ClientConnection">The connection with the client.</param>
///<param name="Callback">The method to call when the SOCKS negotiation is complete.</param>
///<exception cref="ArgumentNullException"><c>Callback</c> is null.</exception>
public SocksHandler(Socket ClientConnection, NegotiationCompleteDelegate Callback) {
if (Callback == null)
throw new ArgumentNullException();
Connection = ClientConnection;
Signaler = Callback;
}
///<summary>Gets or sets the username of the SOCKS user.</summary>
///<value>A String representing the username of the logged on user.</value>
///<exception cref="ArgumentNullException">The specified value is null.</exception>
internal string Username {
get {
return m_Username;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Username = value;
}
}
///<summary>Gets or sets the connection with the client.</summary>
///<value>A Socket representing the connection between the proxy server and the SOCKS client.</value>
///<exception cref="ArgumentNullException">The specified value is null.</exception>
protected Socket Connection {
get {
return m_Connection;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Connection = value;
}
}
///<summary>Gets a buffer that can be used when receiving bytes from the client.</summary>
///<value>A byte array that can be used when receiving bytes from the client.</value>
protected byte[] Buffer {
get {
return m_Buffer;
}
}
///<summary>Gets or sets a byte array that can be used to store received bytes from the client.</summary>
///<value>A byte array that can be used to store bytes from the client.</value>
protected byte[] Bytes {
get {
return m_Bytes;
}
set {
m_Bytes = value;
}
}
///<summary>Gets or sets the connection with the remote host.</summary>
///<value>A Socket representing the connection between the proxy server and the remote host.</value>
///<exception cref="ArgumentNullException">The specified value is null.</exception>
protected Socket RemoteConnection {
get {
return m_RemoteConnection;
}
set {
m_RemoteConnection = value;
try {
m_RemoteConnection.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
} catch {}
}
}
///<summary>Gets or sets the socket that is used to accept incoming connections.</summary>
///<value>A Socket that is used to accept incoming connections.</value>
protected Socket AcceptSocket {
get {
return m_AcceptSocket;
}
set {
m_AcceptSocket = value;
}
}
///<summary>Gets or sets the IP address of the requested remote server.</summary>
///<value>An IPAddress object specifying the address of the requested remote server.</value>
protected IPAddress RemoteBindIP {
get {
return m_RemoteBindIP;
}
set {
m_RemoteBindIP = value;
}
}
///<summary>Closes the listening socket if present, and signals the parent object that SOCKS negotiation is complete.</summary>
///<param name="Success">Indicates whether the SOCKS negotiation was successful or not.</param>
protected void Dispose(bool Success) {
if (AcceptSocket != null)
AcceptSocket.Close();
Signaler(Success, RemoteConnection);
}
///<summary>Starts accepting bytes from the client.</summary>
public void StartNegotiating() {
try {
Connection.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceiveBytes), Connection);
} catch {
Dispose(false);
}
}
///<summary>Called when we receive some bytes from the client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected void OnReceiveBytes(IAsyncResult ar) {
try {
int Ret = Connection.EndReceive(ar);
if (Ret <= 0)
Dispose(false);
AddBytes(Buffer, Ret);
if (IsValidRequest(Bytes))
ProcessRequest(Bytes);
else
Connection.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceiveBytes), Connection);
} catch {
Dispose(false);
}
}
///<summary>Called when an OK reply has been sent to the client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected void OnDisposeGood(IAsyncResult ar) {
try {
if (Connection.EndSend(ar) > 0) {
Dispose(true);
return;
}
} catch {}
Dispose(false);
}
///<summary>Called when a negative reply has been sent to the client.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected void OnDisposeBad(IAsyncResult ar) {
try {
Connection.EndSend(ar);
} catch {}
Dispose(false);
}
///<summary>Adds some bytes to a byte aray.</summary>
///<param name="NewBytes">The new bytes to add.</param>
///<param name="Cnt">The number of bytes to add.</param>
protected void AddBytes(byte [] NewBytes, int Cnt) {
if (Cnt <= 0 || NewBytes == null || Cnt > NewBytes.Length)
return;
if (Bytes == null) {
Bytes = new byte[Cnt];
} else {
byte [] tmp = Bytes;
Bytes = new byte[Bytes.Length + Cnt];
Array.Copy(tmp, 0, Bytes, 0, tmp.Length);
}
Array.Copy(NewBytes, 0, Bytes, Bytes.Length - Cnt, Cnt);
}
///<summary>Called when the AcceptSocket should start accepting incoming connections.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected void OnStartAccept(IAsyncResult ar) {
try {
if (Connection.EndSend(ar) <= 0)
Dispose(false);
else
AcceptSocket.BeginAccept(new AsyncCallback(this.OnAccept), AcceptSocket);
} catch {
Dispose(false);
}
}
///<summary>Called when there's an incoming connection in the AcceptSocket queue.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
protected abstract void OnAccept(IAsyncResult ar);
///<summary>Sends a reply to the client connection and disposes it afterwards.</summary>
///<param name="Value">A byte that contains the reply code to send to the client.</param>
protected abstract void Dispose(byte Value);
///<summary>Checks whether a specific request is a valid SOCKS request or not.</summary>
///<param name="Request">The request array to check.</param>
///<returns>True is the specified request is valid, false otherwise</returns>
protected abstract bool IsValidRequest(byte [] Request);
///<summary>Processes a SOCKS request from a client.</summary>
///<param name="Request">The request to process.</param>
protected abstract void ProcessRequest(byte [] Request);
// private variables
/// <summary>Holds the value of the Username property.</summary>
private string m_Username;
/// <summary>Holds the value of the Buffer property.</summary>
private byte [] m_Buffer = new byte[1024];
/// <summary>Holds the value of the Bytes property.</summary>
private byte [] m_Bytes;
/// <summary>Holds the value of the RemoteConnection property.</summary>
private Socket m_RemoteConnection;
/// <summary>Holds the value of the Connection property.</summary>
private Socket m_Connection;
/// <summary>Holds the value of the AcceptSocket property.</summary>
private Socket m_AcceptSocket;
/// <summary>Holds the value of the RemoteBindIP property.</summary>
private IPAddress m_RemoteBindIP;
/// <summary>Holds the address of the method to call when the SOCKS negotiation is complete.</summary>
private NegotiationCompleteDelegate Signaler;
}
}

View file

@ -0,0 +1,114 @@
/*
Copyright © 2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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.Net.Sockets;
using Org.Mentalis.Proxy;
using Org.Mentalis.Proxy.Socks.Authentication;
namespace Org.Mentalis.Proxy.Socks {
///<summary>Listens on a specific port on the proxy server for incoming SOCKS4 and SOCKS5 requests.</summary>
///<remarks>This class also implements the SOCKS4a protocol.</remarks>
public sealed class SocksListener : Listener {
///<summary>Initializes a new instance of the SocksListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<remarks>The SocksListener will listen on all available network cards and it will not use an AuthenticationList.</remarks>
public SocksListener(int Port) : this(IPAddress.Any, Port, null) {}
///<summary>Initializes a new instance of the SocksListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<param name="Address">The address to listen on. You can specify IPAddress.Any to listen on all installed network cards.</param>
///<remarks>For the security of your server, try to avoid to listen on every network card (IPAddress.Any). Listening on a local IP address is usually sufficient and much more secure.</remarks>
///<remarks>The SocksListener object will not use an AuthenticationList.</remarks>
public SocksListener(IPAddress Address, int Port) : this(Address, Port, null) {}
///<summary>Initializes a new instance of the SocksListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<param name="AuthList">The list of valid login/password combinations. If you do not need password authentication, set this parameter to null.</param>
///<remarks>The SocksListener will listen on all available network cards.</remarks>
public SocksListener(int Port, AuthenticationList AuthList) : this(IPAddress.Any, Port, AuthList) {}
///<summary>Initializes a new instance of the SocksListener class.</summary>
///<param name="Port">The port to listen on.</param>
///<param name="Address">The address to listen on. You can specify IPAddress.Any to listen on all installed network cards.</param>
///<param name="AuthList">The list of valid login/password combinations. If you do not need password authentication, set this parameter to null.</param>
///<remarks>For the security of your server, try to avoid to listen on every network card (IPAddress.Any). Listening on a local IP address is usually sufficient and much more secure.</remarks>
public SocksListener(IPAddress Address, int Port, AuthenticationList AuthList) : base(Port, Address) {
this.AuthList = AuthList;
}
///<summary>Called when there's an incoming client connection waiting to be accepted.</summary>
///<param name="ar">The result of the asynchronous operation.</param>
public override void OnAccept(IAsyncResult ar) {
try {
Socket NewSocket = ListenSocket.EndAccept(ar);
if (NewSocket != null) {
SocksClient NewClient = new SocksClient(NewSocket, new DestroyDelegate(this.RemoveClient), AuthList);
AddClient(NewClient);
NewClient.StartHandshake();
}
} catch {}
try {
//Restart Listening
ListenSocket.BeginAccept(new AsyncCallback(this.OnAccept), ListenSocket);
} catch {
Dispose();
}
}
///<summary>Gets or sets the AuthenticationList to be used when a SOCKS5 client connects.</summary>
///<value>An AuthenticationList that is to be used when a SOCKS5 client connects.</value>
///<remarks>This value can be null.</remarks>
private AuthenticationList AuthList {
get {
return m_AuthList;
}
set {
m_AuthList = value;
}
}
///<summary>Returns a string representation of this object.</summary>
///<returns>A string with information about this object.</returns>
public override string ToString() {
return "SOCKS service on " + Address.ToString() + ":" + Port.ToString();
}
///<summary>Returns a string that holds all the construction information for this object.</summary>
///<value>A string that holds all the construction information for this object.</value>
public override string ConstructString {
get {
if (AuthList == null)
return "host:" + Address.ToString() + ";int:" + Port.ToString()+ ";null";
else
return "host:" + Address.ToString() + ";int:" + Port.ToString()+ ";authlist";
}
}
// private variables
/// <summary>Holds the value of the AuthList property.</summary>
private AuthenticationList m_AuthList;
}
}