Overwrite

Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
Unstable Kitsune
2023-11-28 23:20:26 -05:00
parent 3cd54811de
commit b918192e4e
11608 changed files with 2644205 additions and 47 deletions

View File

@@ -0,0 +1,48 @@
using System;
using System.IO;
using System.Net;
using Server.Misc;
namespace Server
{
public class AccessRestrictions
{
public static void Initialize()
{
EventSink.SocketConnect += new SocketConnectEventHandler(EventSink_SocketConnect);
}
private static void EventSink_SocketConnect(SocketConnectEventArgs e)
{
try
{
IPAddress ip = ((IPEndPoint)e.Socket.RemoteEndPoint).Address;
if (Firewall.IsBlocked(ip))
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Client: {0}: Firewall blocked connection attempt.", ip);
Utility.PopColor();
e.AllowConnection = false;
return;
}
else if (IPLimiter.SocketBlock && !IPLimiter.Verify(ip))
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Client: {0}: Past IP limit threshold", ip);
Utility.PopColor();
using (StreamWriter op = new StreamWriter("ipLimits.log", true))
op.WriteLine("{0}\tPast IP limit threshold\t{1}", ip, DateTime.UtcNow);
e.AllowConnection = false;
return;
}
}
catch
{
e.AllowConnection = false;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Server.Network;
namespace Server.Accounting
{
public class AccountAttackLimiter
{
public static bool Enabled = true;
private static readonly List<InvalidAccountAccessLog> m_List = new List<InvalidAccountAccessLog>();
public static void Initialize()
{
if (!Enabled)
return;
PacketHandlers.RegisterThrottler(0x80, new ThrottlePacketCallback(Throttle_Callback));
PacketHandlers.RegisterThrottler(0x91, new ThrottlePacketCallback(Throttle_Callback));
PacketHandlers.RegisterThrottler(0xCF, new ThrottlePacketCallback(Throttle_Callback));
}
public static bool Throttle_Callback(byte packetID, NetState ns, out bool drop)
{
drop = false;
InvalidAccountAccessLog accessLog = FindAccessLog(ns);
if (accessLog == null)
return true;
return (DateTime.UtcNow >= (accessLog.LastAccessTime + ComputeThrottle(accessLog.Counts)));
}
public static InvalidAccountAccessLog FindAccessLog(NetState ns)
{
if (ns == null)
return null;
IPAddress ipAddress = ns.Address;
for (int i = 0; i < m_List.Count; ++i)
{
InvalidAccountAccessLog accessLog = m_List[i];
if (accessLog.HasExpired)
m_List.RemoveAt(i--);
else if (accessLog.Address.Equals(ipAddress))
return accessLog;
}
return null;
}
public static void RegisterInvalidAccess(NetState ns)
{
if (ns == null || !Enabled)
return;
InvalidAccountAccessLog accessLog = FindAccessLog(ns);
if (accessLog == null)
m_List.Add(accessLog = new InvalidAccountAccessLog(ns.Address));
accessLog.Counts += 1;
accessLog.RefreshAccessTime();
if (accessLog.Counts >= 3)
{
try
{
using (StreamWriter op = new StreamWriter("throttle.log", true))
{
op.WriteLine(
"{0}\t{1}\t{2}",
DateTime.UtcNow,
ns,
accessLog.Counts);
}
}
catch
{
}
}
}
public static TimeSpan ComputeThrottle(int counts)
{
if (counts >= 15)
return TimeSpan.FromMinutes(5.0);
if (counts >= 10)
return TimeSpan.FromMinutes(1.0);
if (counts >= 5)
return TimeSpan.FromSeconds(20.0);
if (counts >= 3)
return TimeSpan.FromSeconds(10.0);
if (counts >= 1)
return TimeSpan.FromSeconds(2.0);
return TimeSpan.Zero;
}
}
public class InvalidAccountAccessLog
{
private IPAddress m_Address;
private DateTime m_LastAccessTime;
private int m_Counts;
public InvalidAccountAccessLog(IPAddress address)
{
this.m_Address = address;
this.RefreshAccessTime();
}
public IPAddress Address
{
get
{
return this.m_Address;
}
set
{
this.m_Address = value;
}
}
public DateTime LastAccessTime
{
get
{
return this.m_LastAccessTime;
}
set
{
this.m_LastAccessTime = value;
}
}
public bool HasExpired
{
get
{
return (DateTime.UtcNow >= (this.m_LastAccessTime + TimeSpan.FromHours(1.0)));
}
}
public int Counts
{
get
{
return this.m_Counts;
}
set
{
this.m_Counts = value;
}
}
public void RefreshAccessTime()
{
this.m_LastAccessTime = DateTime.UtcNow;
}
}
}

View File

@@ -0,0 +1,86 @@
using System;
using System.Xml;
namespace Server.Accounting
{
public class AccountComment
{
private readonly string m_AddedBy;
private string m_Content;
private DateTime m_LastModified;
/// <summary>
/// Constructs a new AccountComment instance.
/// </summary>
/// <param name="addedBy">Initial AddedBy value.</param>
/// <param name="content">Initial Content value.</param>
public AccountComment(string addedBy, string content)
{
this.m_AddedBy = addedBy;
this.m_Content = content;
this.m_LastModified = DateTime.UtcNow;
}
/// <summary>
/// Deserializes an AccountComment instance from an xml element.
/// </summary>
/// <param name="node">The XmlElement instance from which to deserialize.</param>
public AccountComment(XmlElement node)
{
this.m_AddedBy = Utility.GetAttribute(node, "addedBy", "empty");
this.m_LastModified = Utility.GetXMLDateTime(Utility.GetAttribute(node, "lastModified"), DateTime.UtcNow);
this.m_Content = Utility.GetText(node, "");
}
/// <summary>
/// A string representing who added this comment.
/// </summary>
public string AddedBy
{
get
{
return this.m_AddedBy;
}
}
/// <summary>
/// Gets or sets the body of this comment. Setting this value will reset LastModified.
/// </summary>
public string Content
{
get
{
return this.m_Content;
}
set
{
this.m_Content = value;
this.m_LastModified = DateTime.UtcNow;
}
}
/// <summary>
/// The date and time when this account was last modified -or- the comment creation time, if never modified.
/// </summary>
public DateTime LastModified
{
get
{
return this.m_LastModified;
}
}
/// <summary>
/// Serializes this AccountComment instance to an XmlTextWriter.
/// </summary>
/// <param name="xml">The XmlTextWriter instance from which to serialize.</param>
public void Save(XmlTextWriter xml)
{
xml.WriteStartElement("comment");
xml.WriteAttributeString("addedBy", this.m_AddedBy);
xml.WriteAttributeString("lastModified", XmlConvert.ToString(this.m_LastModified, XmlDateTimeSerializationMode.Utc));
xml.WriteString(this.m_Content);
xml.WriteEndElement();
}
}
}

View File

@@ -0,0 +1,527 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using Server.Accounting;
using Server.Commands;
using Server.Engines.Help;
using Server.Network;
using Server.Regions;
namespace Server.Misc
{
public enum PasswordProtection
{
None,
Crypt,
NewCrypt,
NewSecureCrypt
}
public class AccountHandler
{
public static PasswordProtection ProtectPasswords = Config.GetEnum(
"Accounts.ProtectPasswords",
PasswordProtection.NewSecureCrypt);
private static readonly int MaxAccountsPerIP = Config.Get("Accounts.AccountsPerIp", 1);
private static readonly bool AutoAccountCreation = Config.Get("Accounts.AutoCreateAccounts", true);
private static readonly bool RestrictDeletion = Config.Get("Accounts.RestrictDeletion", !TestCenter.Enabled);
private static readonly TimeSpan DeleteDelay = Config.Get("Accounts.DeleteDelay", TimeSpan.FromDays(7.0));
private static readonly CityInfo[] StartingCitiesT2A = new CityInfo[]
{
new CityInfo("New Haven", "New Haven Bank", 1150168, 3503, 2574, 14, Map.Felucca),
new CityInfo("Yew", "The Empath Abbey", 1075072, 633, 858, 0, Map.Felucca),
new CityInfo("Minoc", "The Barnacle", 1075073, 2476, 413, 15, Map.Felucca),
new CityInfo("Britain", "The Wayfarer's Inn", 1075074, 1602, 1591, 20, Map.Felucca),
new CityInfo("Moonglow", "The Scholars Inn", 1075075, 4408, 1168, 0, Map.Felucca),
new CityInfo("Trinsic", "The Traveler's Inn", 1075076, 1845, 2745, 0, Map.Felucca),
new CityInfo("Jhelom", "The Mercenary Inn", 1075078, 1374, 3826, 0, Map.Felucca),
new CityInfo("Skara Brae", "The Falconer's Inn", 1075079, 618, 2234, 0, Map.Felucca),
new CityInfo("Vesper", "The Ironwood Inn", 1075080, 2771, 976, 0, Map.Felucca)
};
private static readonly CityInfo[] StartingCities = new CityInfo[]
{
new CityInfo("New Haven", "New Haven Bank", 1150168, 3503, 2574, 14),
new CityInfo("Yew", "The Empath Abbey", 1075072, 633, 858, 0),
new CityInfo("Minoc", "The Barnacle", 1075073, 2476, 413, 15),
new CityInfo("Britain", "The Wayfarer's Inn", 1075074, 1602, 1591, 20),
new CityInfo("Moonglow", "The Scholars Inn", 1075075, 4408, 1168, 0),
new CityInfo("Trinsic", "The Traveler's Inn", 1075076, 1845, 2745, 0),
new CityInfo("Jhelom", "The Mercenary Inn", 1075078, 1374, 3826, 0),
new CityInfo("Skara Brae", "The Falconer's Inn", 1075079, 618, 2234, 0),
new CityInfo("Vesper", "The Ironwood Inn", 1075080, 2771, 976, 0)
};
private static readonly CityInfo[] StartingCitiesSA = new CityInfo[]
{
new CityInfo("New Haven", "New Haven Bank", 1150168, 3503, 2574, 14),
new CityInfo("Yew", "The Empath Abbey", 1075072, 633, 858, 0),
new CityInfo("Minoc", "The Barnacle", 1075073, 2476, 413, 15),
new CityInfo("Britain", "The Wayfarer's Inn", 1075074, 1602, 1591, 20),
new CityInfo("Moonglow", "The Scholars Inn", 1075075, 4408, 1168, 0),
new CityInfo("Trinsic", "The Traveler's Inn", 1075076, 1845, 2745, 0),
new CityInfo("Jhelom", "The Mercenary Inn", 1075078, 1374, 3826, 0),
new CityInfo("Skara Brae", "The Falconer's Inn", 1075079, 618, 2234, 0),
new CityInfo("Vesper", "The Ironwood Inn", 1075080, 2771, 976, 0),
new CityInfo("Royal City", "Royal City Inn", 1150169, 738, 3486, -19, Map.TerMur)
};
private static readonly CityInfo[] SiegeStartingCities = new CityInfo[]
{
new CityInfo("Britain", "The Wayfarer's Inn", 1075074, 1602, 1591, 20, Map.Felucca),
new CityInfo("Royal City", "Royal City Inn", 1150169, 738, 3486, -19, Map.TerMur)
};
/* Old Haven/Magincia Locations
new CityInfo( "Britain", "Sweet Dreams Inn", 1496, 1628, 10 );
// ..
// Trinsic
new CityInfo( "Magincia", "The Great Horns Tavern", 3734, 2222, 20 ),
// Jhelom
// ..
new CityInfo( "Haven", "Buckler's Hideaway", 3667, 2625, 0 )
if ( Core.AOS )
{
//CityInfo haven = new CityInfo( "Haven", "Uzeraan's Mansion", 3618, 2591, 0 );
CityInfo haven = new CityInfo( "Haven", "Uzeraan's Mansion", 3503, 2574, 14 );
StartingCities[StartingCities.Length - 1] = haven;
}
*/
private static readonly bool PasswordCommandEnabled = Config.Get("Accounts.PasswordCommandEnabled", false);
private static readonly char[] m_ForbiddenChars = new char[]
{
'<', '>', ':', '"', '/', '\\', '|', '?', '*', ' '
};
private static AccessLevel m_LockdownLevel;
private static Dictionary<IPAddress, Int32> m_IPTable;
public static AccessLevel LockdownLevel
{
get
{
return m_LockdownLevel;
}
set
{
m_LockdownLevel = value;
}
}
public static Dictionary<IPAddress, Int32> IPTable
{
get
{
if (m_IPTable == null)
{
m_IPTable = new Dictionary<IPAddress, Int32>();
foreach (var a in Accounts.GetAccounts().OfType<Account>())
{
if (a.LoginIPs.Length > 0)
{
IPAddress ip = a.LoginIPs[0];
if (m_IPTable.ContainsKey(ip))
m_IPTable[ip]++;
else
m_IPTable[ip] = 1;
}
}
}
return m_IPTable;
}
}
public static void Initialize()
{
EventSink.DeleteRequest += new DeleteRequestEventHandler(EventSink_DeleteRequest);
EventSink.AccountLogin += new AccountLoginEventHandler(EventSink_AccountLogin);
EventSink.GameLogin += new GameLoginEventHandler(EventSink_GameLogin);
if (PasswordCommandEnabled)
CommandSystem.Register("Password", AccessLevel.Player, new CommandEventHandler(Password_OnCommand));
}
[Usage("Password <newPassword> <repeatPassword>")]
[Description("Changes the password of the commanding players account. Requires the same C-class IP address as the account's creator.")]
public static void Password_OnCommand(CommandEventArgs e)
{
Mobile from = e.Mobile;
Account acct = from.Account as Account;
if (acct == null)
return;
IPAddress[] accessList = acct.LoginIPs;
if (accessList.Length == 0)
return;
NetState ns = from.NetState;
if (ns == null)
return;
if (e.Length == 0)
{
from.SendMessage("You must specify the new password.");
return;
}
else if (e.Length == 1)
{
from.SendMessage("To prevent potential typing mistakes, you must type the password twice. Use the format:");
from.SendMessage("Password \"(newPassword)\" \"(repeated)\"");
return;
}
string pass = e.GetString(0);
string pass2 = e.GetString(1);
if (pass != pass2)
{
from.SendMessage("The passwords do not match.");
return;
}
bool isSafe = true;
for (int i = 0; isSafe && i < pass.Length; ++i)
isSafe = (pass[i] >= 0x20 && pass[i] < 0x7F);
if (!isSafe)
{
from.SendMessage("That is not a valid password.");
return;
}
try
{
IPAddress ipAddress = ns.Address;
if (Utility.IPMatchClassC(accessList[0], ipAddress))
{
acct.SetPassword(pass);
from.SendMessage("The password to your account has changed.");
}
else
{
PageEntry entry = PageQueue.GetEntry(from);
if (entry != null)
{
if (entry.Message.StartsWith("[Automated: Change Password]"))
from.SendMessage("You already have a password change request in the help system queue.");
else
from.SendMessage("Your IP address does not match that which created this account.");
}
else if (PageQueue.CheckAllowedToPage(from))
{
from.SendMessage("Your IP address does not match that which created this account. A page has been entered into the help system on your behalf.");
from.SendLocalizedMessage(501234, "", 0x35); /* The next available Counselor/Game Master will respond as soon as possible.
* Please check your Journal for messages every few minutes.
*/
PageQueue.Enqueue(new PageEntry(from, String.Format("[Automated: Change Password]<br>Desired password: {0}<br>Current IP address: {1}<br>Account IP address: {2}", pass, ipAddress, accessList[0]), PageType.Account));
}
}
}
catch
{
}
}
public static bool CanCreate(IPAddress ip)
{
if (!IPTable.ContainsKey(ip) || IPLimiter.IsExempt(ip))
return true;
return (IPTable[ip] < MaxAccountsPerIP);
}
public static void EventSink_AccountLogin(AccountLoginEventArgs e)
{
// If the login attempt has already been rejected by another event handler
// then just return
if (e.Accepted == false)
return;
if (!IPLimiter.SocketBlock && !IPLimiter.Verify(e.State.Address))
{
e.Accepted = false;
e.RejectReason = ALRReason.InUse;
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Login: {0}: Past IP limit threshold", e.State);
Utility.PopColor();
using (StreamWriter op = new StreamWriter("ipLimits.log", true))
op.WriteLine("{0}\tPast IP limit threshold\t{1}", e.State, DateTime.UtcNow);
return;
}
string un = e.Username;
string pw = e.Password;
e.Accepted = false;
Account acct = Accounts.GetAccount(un) as Account;
if (acct == null)
{
if (AutoAccountCreation && un.Trim().Length > 0) // To prevent someone from making an account of just '' or a bunch of meaningless spaces
{
e.State.Account = acct = CreateAccount(e.State, un, pw);
e.Accepted = acct == null ? false : acct.CheckAccess(e.State);
if (!e.Accepted)
e.RejectReason = ALRReason.BadComm;
}
else
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Login: {0}: Invalid username '{1}'", e.State, un);
Utility.PopColor();
e.RejectReason = ALRReason.Invalid;
}
}
else if (!acct.HasAccess(e.State))
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Login: {0}: Access denied for '{1}'", e.State, un);
Utility.PopColor();
e.RejectReason = (m_LockdownLevel > AccessLevel.VIP ? ALRReason.BadComm : ALRReason.BadPass);
}
else if (!acct.CheckPassword(pw))
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Login: {0}: Invalid password for '{1}'", e.State, un);
Utility.PopColor();
e.RejectReason = ALRReason.BadPass;
}
else if (acct.Banned)
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Login: {0}: Banned account '{1}'", e.State, un);
Utility.PopColor();
e.RejectReason = ALRReason.Blocked;
}
else
{
Utility.PushColor(ConsoleColor.Green);
Console.WriteLine("Login: {0}: Valid credentials for '{1}'", e.State, un);
Console.WriteLine("Client Type: {0}: {1}", e.State, e.State.IsEnhancedClient ? "Enhanced Client" : "Classic Client");
Utility.PopColor();
e.State.Account = acct;
e.Accepted = true;
acct.LogAccess(e.State);
}
if (!e.Accepted)
AccountAttackLimiter.RegisterInvalidAccess(e.State);
}
public static void EventSink_GameLogin(GameLoginEventArgs e)
{
if (!IPLimiter.SocketBlock && !IPLimiter.Verify(e.State.Address))
{
e.Accepted = false;
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Login: {0}: Past IP limit threshold", e.State);
Utility.PopColor();
using (StreamWriter op = new StreamWriter("ipLimits.log", true))
op.WriteLine("{0}\tPast IP limit threshold\t{1}", e.State, DateTime.UtcNow);
return;
}
string un = e.Username;
string pw = e.Password;
Account acct = Accounts.GetAccount(un) as Account;
if (acct == null)
{
e.Accepted = false;
}
else if (!acct.HasAccess(e.State))
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Login: {0}: Access denied for '{1}'", e.State, un);
Utility.PopColor();
e.Accepted = false;
}
else if (!acct.CheckPassword(pw))
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Login: {0}: Invalid password for '{1}'", e.State, un);
Utility.PopColor();
e.Accepted = false;
}
else if (acct.Banned)
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Login: {0}: Banned account '{1}'", e.State, un);
Utility.PopColor();
e.Accepted = false;
}
else
{
acct.LogAccess(e.State);
Utility.PushColor(ConsoleColor.Yellow);
Console.WriteLine("Login: {0}: Account '{1}' at character list", e.State, un);
Utility.PopColor();
e.State.Account = acct;
e.Accepted = true;
if(Siege.SiegeShard)
{
e.CityInfo = SiegeStartingCities;
}
else if (!Core.UOR)
{
e.CityInfo = StartingCitiesT2A;
}
else if (!Core.SA)
{
e.CityInfo = StartingCities;
}
else
{
e.CityInfo = StartingCitiesSA;
}
}
if (!e.Accepted)
AccountAttackLimiter.RegisterInvalidAccess(e.State);
}
public static bool CheckAccount(Mobile mobCheck, Mobile accCheck)
{
if (accCheck != null)
{
Account a = accCheck.Account as Account;
if (a != null)
{
for (int i = 0; i < a.Length; ++i)
{
if (a[i] == mobCheck)
return true;
}
}
}
return false;
}
private static void EventSink_DeleteRequest(DeleteRequestEventArgs e)
{
NetState state = e.State;
int index = e.Index;
Account acct = state.Account as Account;
if (acct == null)
{
state.Dispose();
}
else if (index < 0 || index >= acct.Length)
{
state.Send(new DeleteResult(DeleteResultType.BadRequest));
state.Send(new CharacterListUpdate(acct));
}
else
{
Mobile m = acct[index];
if (m == null)
{
state.Send(new DeleteResult(DeleteResultType.CharNotExist));
state.Send(new CharacterListUpdate(acct));
}
else if (m.NetState != null)
{
state.Send(new DeleteResult(DeleteResultType.CharBeingPlayed));
state.Send(new CharacterListUpdate(acct));
}
else if (RestrictDeletion && DateTime.UtcNow < (m.CreationTime + DeleteDelay))
{
state.Send(new DeleteResult(DeleteResultType.CharTooYoung));
state.Send(new CharacterListUpdate(acct));
}
else if (m.IsPlayer() && Region.Find(m.LogoutLocation, m.LogoutMap).GetRegion(typeof(Jail)) != null) //Don't need to check current location, if netstate is null, they're logged out
{
state.Send(new DeleteResult(DeleteResultType.BadRequest));
state.Send(new CharacterListUpdate(acct));
}
else
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Client: {0}: Deleting character {1} (0x{2:X})", state, index, m.Serial.Value);
Utility.PopColor();
acct.Comments.Add(new AccountComment("System", String.Format("Character #{0} {1} deleted by {2}", index + 1, m, state)));
m.Delete();
state.Send(new CharacterListUpdate(acct));
}
}
}
private static bool IsForbiddenChar(char c)
{
for (int i = 0; i < m_ForbiddenChars.Length; ++i)
if (c == m_ForbiddenChars[i])
return true;
return false;
}
private static Account CreateAccount(NetState state, string un, string pw)
{
if (un.Length == 0 || pw.Length == 0)
return null;
bool isSafe = !(un.StartsWith(" ") || un.EndsWith(" ") || un.EndsWith("."));
for (int i = 0; isSafe && i < un.Length; ++i)
isSafe = (un[i] >= 0x20 && un[i] < 0x7F && !IsForbiddenChar(un[i]));
for (int i = 0; isSafe && i < pw.Length; ++i)
isSafe = (pw[i] >= 0x20 && pw[i] < 0x7F);
if (!isSafe)
return null;
if (!CanCreate(state.Address))
{
Utility.PushColor(ConsoleColor.DarkYellow);
Console.WriteLine("Login: {0}: Account '{1}' not created, ip already has {2} account{3}.", state, un, MaxAccountsPerIP, MaxAccountsPerIP == 1 ? "" : "s");
Utility.PopColor();
return null;
}
Utility.PushColor(ConsoleColor.Green);
Console.WriteLine("Login: {0}: Creating new account '{1}'", state, un);
Utility.PopColor();
Account a = new Account(un, pw);
return a;
}
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.Xml;
namespace Server.Accounting
{
public class AccountTag
{
private string m_Name, m_Value;
/// <summary>
/// Constructs a new AccountTag instance with a specific name and value.
/// </summary>
/// <param name="name">Initial name.</param>
/// <param name="value">Initial value.</param>
public AccountTag(string name, string value)
{
this.m_Name = name;
this.m_Value = value;
}
/// <summary>
/// Deserializes an AccountTag instance from an xml element.
/// </summary>
/// <param name="node">The XmlElement instance from which to deserialize.</param>
public AccountTag(XmlElement node)
{
this.m_Name = Utility.GetAttribute(node, "name", "empty");
this.m_Value = Utility.GetText(node, "");
}
/// <summary>
/// Gets or sets the name of this tag.
/// </summary>
public string Name
{
get
{
return this.m_Name;
}
set
{
this.m_Name = value;
}
}
/// <summary>
/// Gets or sets the value of this tag.
/// </summary>
public string Value
{
get
{
return this.m_Value;
}
set
{
this.m_Value = value;
}
}
/// <summary>
/// Serializes this AccountTag instance to an XmlTextWriter.
/// </summary>
/// <param name="xml">The XmlTextWriter instance from which to serialize.</param>
public void Save(XmlTextWriter xml)
{
xml.WriteStartElement("tag");
xml.WriteAttributeString("name", this.m_Name);
xml.WriteString(this.m_Value);
xml.WriteEndElement();
}
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
namespace Server.Accounting
{
public class Accounts
{
private static Dictionary<string, IAccount> m_Accounts = new Dictionary<string, IAccount>();
public static void Configure()
{
EventSink.WorldLoad += new WorldLoadEventHandler(Load);
EventSink.WorldSave += new WorldSaveEventHandler(Save);
}
static Accounts()
{
}
public static int Count
{
get
{
return m_Accounts.Count;
}
}
public static ICollection<IAccount> GetAccounts()
{
return m_Accounts.Values;
}
public static IAccount GetAccount(string username)
{
IAccount a;
m_Accounts.TryGetValue(username, out a);
return a;
}
public static void Add(IAccount a)
{
m_Accounts[a.Username] = a;
}
public static void Remove(string username)
{
m_Accounts.Remove(username);
}
public static void Load()
{
m_Accounts = new Dictionary<string, IAccount>(32, StringComparer.OrdinalIgnoreCase);
string filePath = Path.Combine("Saves/Accounts", "accounts.xml");
if (!File.Exists(filePath))
return;
XmlDocument doc = new XmlDocument();
doc.Load(filePath);
XmlElement root = doc["accounts"];
foreach (XmlElement account in root.GetElementsByTagName("account"))
{
try
{
Account acct = new Account(account);
}
catch
{
Console.WriteLine("Warning: Account instance load failed");
}
}
}
public static void Save(WorldSaveEventArgs e)
{
if (!Directory.Exists("Saves/Accounts"))
Directory.CreateDirectory("Saves/Accounts");
string filePath = Path.Combine("Saves/Accounts", "accounts.xml");
using (StreamWriter op = new StreamWriter(filePath))
{
XmlTextWriter xml = new XmlTextWriter(op);
xml.Formatting = Formatting.Indented;
xml.IndentChar = '\t';
xml.Indentation = 1;
xml.WriteStartDocument(true);
xml.WriteStartElement("accounts");
xml.WriteAttributeString("count", m_Accounts.Count.ToString());
foreach (Account a in GetAccounts())
a.Save(xml);
xml.WriteEndElement();
xml.Close();
}
}
}
}

View File

@@ -0,0 +1,335 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
namespace Server
{
public class Firewall
{
#region Firewall Entries
public interface IFirewallEntry
{
bool IsBlocked(IPAddress address);
}
public class IPFirewallEntry : IFirewallEntry
{
readonly IPAddress m_Address;
public IPFirewallEntry(IPAddress address)
{
this.m_Address = address;
}
public bool IsBlocked(IPAddress address)
{
return this.m_Address.Equals(address);
}
public override string ToString()
{
return this.m_Address.ToString();
}
public override bool Equals(object obj)
{
if (obj is IPAddress)
{
return obj.Equals(this.m_Address);
}
else if (obj is string)
{
IPAddress otherAddress;
if (IPAddress.TryParse((string)obj, out otherAddress))
return otherAddress.Equals(this.m_Address);
}
else if (obj is IPFirewallEntry)
{
return this.m_Address.Equals(((IPFirewallEntry)obj).m_Address);
}
return false;
}
public override int GetHashCode()
{
return this.m_Address.GetHashCode();
}
}
public class CIDRFirewallEntry : IFirewallEntry
{
readonly IPAddress m_CIDRPrefix;
readonly int m_CIDRLength;
public CIDRFirewallEntry(IPAddress cidrPrefix, int cidrLength)
{
this.m_CIDRPrefix = cidrPrefix;
this.m_CIDRLength = cidrLength;
}
public bool IsBlocked(IPAddress address)
{
return Utility.IPMatchCIDR(this.m_CIDRPrefix, address, this.m_CIDRLength);
}
public override string ToString()
{
return String.Format("{0}/{1}", this.m_CIDRPrefix, this.m_CIDRLength);
}
public override bool Equals(object obj)
{
if (obj is string)
{
string entry = (string)obj;
string[] str = entry.Split('/');
if (str.Length == 2)
{
IPAddress cidrPrefix;
if (IPAddress.TryParse(str[0], out cidrPrefix))
{
int cidrLength;
if (int.TryParse(str[1], out cidrLength))
return this.m_CIDRPrefix.Equals(cidrPrefix) && this.m_CIDRLength.Equals(cidrLength);
}
}
}
else if (obj is CIDRFirewallEntry)
{
CIDRFirewallEntry entry = obj as CIDRFirewallEntry;
return this.m_CIDRPrefix.Equals(entry.m_CIDRPrefix) && this.m_CIDRLength.Equals(entry.m_CIDRLength);
}
return false;
}
public override int GetHashCode()
{
return this.m_CIDRPrefix.GetHashCode() ^ this.m_CIDRLength.GetHashCode();
}
}
public class WildcardIPFirewallEntry : IFirewallEntry
{
readonly string m_Entry;
bool m_Valid = true;
public WildcardIPFirewallEntry(string entry)
{
this.m_Entry = entry;
}
public bool IsBlocked(IPAddress address)
{
if (!this.m_Valid)
return false; //Why process if it's invalid? it'll return false anyway after processing it.
return Utility.IPMatch(this.m_Entry, address, ref this.m_Valid);
}
public override string ToString()
{
return this.m_Entry.ToString();
}
public override bool Equals(object obj)
{
if (obj is string)
return obj.Equals(this.m_Entry);
else if (obj is WildcardIPFirewallEntry)
return this.m_Entry.Equals(((WildcardIPFirewallEntry)obj).m_Entry);
return false;
}
public override int GetHashCode()
{
return this.m_Entry.GetHashCode();
}
}
#endregion
private static List<IFirewallEntry> m_Blocked;
static Firewall()
{
m_Blocked = new List<IFirewallEntry>();
string path = "firewall.cfg";
if (File.Exists(path))
{
using (StreamReader ip = new StreamReader(path))
{
string line;
while ((line = ip.ReadLine()) != null)
{
line = line.Trim();
if (line.Length == 0)
continue;
m_Blocked.Add(ToFirewallEntry(line));
/*
object toAdd;
IPAddress addr;
if( IPAddress.TryParse( line, out addr ) )
toAdd = addr;
else
toAdd = line;
m_Blocked.Add( toAdd.ToString() );
* */
}
}
}
}
public static List<IFirewallEntry> List
{
get
{
return m_Blocked;
}
}
public static IFirewallEntry ToFirewallEntry(object entry)
{
if (entry is IFirewallEntry)
return (IFirewallEntry)entry;
else if (entry is IPAddress)
return new IPFirewallEntry((IPAddress)entry);
else if (entry is string)
return ToFirewallEntry((string)entry);
return null;
}
public static IFirewallEntry ToFirewallEntry(string entry)
{
IPAddress addr;
if (IPAddress.TryParse(entry, out addr))
return new IPFirewallEntry(addr);
//Try CIDR parse
string[] str = entry.Split('/');
if (str.Length == 2)
{
IPAddress cidrPrefix;
if (IPAddress.TryParse(str[0], out cidrPrefix))
{
int cidrLength;
if (int.TryParse(str[1], out cidrLength))
return new CIDRFirewallEntry(cidrPrefix, cidrLength);
}
}
return new WildcardIPFirewallEntry(entry);
}
public static void RemoveAt(int index)
{
m_Blocked.RemoveAt(index);
Save();
}
public static void Remove(object obj)
{
IFirewallEntry entry = ToFirewallEntry(obj);
if (entry != null)
{
m_Blocked.Remove(entry);
Save();
}
}
public static void Add(object obj)
{
if (obj is IPAddress)
Add((IPAddress)obj);
else if (obj is string)
Add((string)obj);
else if (obj is IFirewallEntry)
Add((IFirewallEntry)obj);
}
public static void Add(IFirewallEntry entry)
{
if (!m_Blocked.Contains(entry))
m_Blocked.Add(entry);
Save();
}
public static void Add(string pattern)
{
IFirewallEntry entry = ToFirewallEntry(pattern);
if (!m_Blocked.Contains(entry))
m_Blocked.Add(entry);
Save();
}
public static void Add(IPAddress ip)
{
IFirewallEntry entry = new IPFirewallEntry(ip);
if (!m_Blocked.Contains(entry))
m_Blocked.Add(entry);
Save();
}
public static void Save()
{
string path = "firewall.cfg";
using (StreamWriter op = new StreamWriter(path))
{
for (int i = 0; i < m_Blocked.Count; ++i)
op.WriteLine(m_Blocked[i]);
}
}
public static bool IsBlocked(IPAddress ip)
{
for (int i = 0; i < m_Blocked.Count; i++)
{
if (m_Blocked[i].IsBlocked(ip))
return true;
}
return false;
/*
bool contains = false;
for ( int i = 0; !contains && i < m_Blocked.Count; ++i )
{
if ( m_Blocked[i] is IPAddress )
contains = ip.Equals( m_Blocked[i] );
else if ( m_Blocked[i] is String )
{
string s = (string)m_Blocked[i];
contains = Utility.IPMatchCIDR( s, ip );
if( !contains )
contains = Utility.IPMatch( s, ip );
}
}
return contains;
* */
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Net;
using Server.Network;
namespace Server.Misc
{
public class IPLimiter
{
public static bool Enabled = true;
public static bool SocketBlock = true;// true to block at connection, false to block at login request
public static int MaxAddresses = 10;
public static IPAddress[] Exemptions = new IPAddress[] //For hosting services where there are cases where IPs can be proxied
{
IPAddress.Parse( "127.0.0.1" ),
};
public static bool IsExempt(IPAddress ip)
{
for (int i = 0; i < Exemptions.Length; i++)
{
if (ip.Equals(Exemptions[i]))
return true;
}
return false;
}
public static bool Verify(IPAddress ourAddress)
{
if (!Enabled || IsExempt(ourAddress))
return true;
List<NetState> netStates = NetState.Instances;
int count = 0;
for (int i = 0; i < netStates.Count; ++i)
{
NetState compState = netStates[i];
if (ourAddress.Equals(compState.Address))
{
++count;
if (count >= MaxAddresses)
return false;
}
}
return true;
}
}
}