MSNPSharp/ProxyServer/ProxyConfig.cs
2023-07-15 01:45:29 -03:00

488 lines
19 KiB
C#

/*
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>
*/