#W# Initial Commit: Avatars Conquest
This commit is contained in:
commit
5df497787a
7510 changed files with 416048 additions and 0 deletions
46
Scripts/Accounting/AccessRestrictions.cs
Normal file
46
Scripts/Accounting/AccessRestrictions.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Server;
|
||||
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 ) )
|
||||
{
|
||||
Console.WriteLine( "Client: {0}: Firewall blocked connection attempt.", ip );
|
||||
e.AllowConnection = false;
|
||||
return;
|
||||
}
|
||||
else if ( IPLimiter.SocketBlock && !IPLimiter.Verify( ip ) )
|
||||
{
|
||||
Console.WriteLine( "Client: {0}: Past IP limit threshold", ip );
|
||||
|
||||
using ( StreamWriter op = new StreamWriter( "ipLimits.log", true ) )
|
||||
op.WriteLine( "{0}\tPast IP limit threshold\t{1}", ip, DateTime.Now );
|
||||
|
||||
e.AllowConnection = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
e.AllowConnection = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1094
Scripts/Accounting/Account.cs
Normal file
1094
Scripts/Accounting/Account.cs
Normal file
File diff suppressed because it is too large
Load diff
148
Scripts/Accounting/AccountAttackLimiter.cs
Normal file
148
Scripts/Accounting/AccountAttackLimiter.cs
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Server;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Accounting
|
||||
{
|
||||
public class AccountAttackLimiter
|
||||
{
|
||||
public static bool Enabled = true;
|
||||
|
||||
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( NetState ns )
|
||||
{
|
||||
InvalidAccountAccessLog accessLog = FindAccessLog( ns );
|
||||
|
||||
if ( accessLog == null )
|
||||
return true;
|
||||
|
||||
return ( DateTime.Now >= (accessLog.LastAccessTime + ComputeThrottle( accessLog.Counts )) );
|
||||
}
|
||||
|
||||
private static List<InvalidAccountAccessLog> m_List = new List<InvalidAccountAccessLog>();
|
||||
|
||||
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.Now,
|
||||
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 IPAddress Address
|
||||
{
|
||||
get{ return m_Address; }
|
||||
set{ m_Address = value; }
|
||||
}
|
||||
|
||||
public DateTime LastAccessTime
|
||||
{
|
||||
get{ return m_LastAccessTime; }
|
||||
set{ m_LastAccessTime = value; }
|
||||
}
|
||||
|
||||
public bool HasExpired
|
||||
{
|
||||
get{ return ( DateTime.Now >= ( m_LastAccessTime + TimeSpan.FromHours( 1.0 ) ) ); }
|
||||
}
|
||||
|
||||
public int Counts
|
||||
{
|
||||
get{ return m_Counts; }
|
||||
set{ m_Counts = value; }
|
||||
}
|
||||
|
||||
public void RefreshAccessTime()
|
||||
{
|
||||
m_LastAccessTime = DateTime.Now;
|
||||
}
|
||||
|
||||
public InvalidAccountAccessLog( IPAddress address )
|
||||
{
|
||||
m_Address = address;
|
||||
RefreshAccessTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
77
Scripts/Accounting/AccountComment.cs
Normal file
77
Scripts/Accounting/AccountComment.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace Server.Accounting
|
||||
{
|
||||
public class AccountComment
|
||||
{
|
||||
private string m_AddedBy;
|
||||
private string m_Content;
|
||||
private DateTime m_LastModified;
|
||||
|
||||
/// <summary>
|
||||
/// A string representing who added this comment.
|
||||
/// </summary>
|
||||
public string AddedBy
|
||||
{
|
||||
get{ return m_AddedBy; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body of this comment. Setting this value will reset LastModified.
|
||||
/// </summary>
|
||||
public string Content
|
||||
{
|
||||
get{ return m_Content; }
|
||||
set{ m_Content = value; m_LastModified = DateTime.Now; }
|
||||
}
|
||||
|
||||
/// <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 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 )
|
||||
{
|
||||
m_AddedBy = addedBy;
|
||||
m_Content = content;
|
||||
m_LastModified = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <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 )
|
||||
{
|
||||
m_AddedBy = Utility.GetAttribute( node, "addedBy", "empty" );
|
||||
m_LastModified = Utility.GetXMLDateTime( Utility.GetAttribute( node, "lastModified" ), DateTime.Now );
|
||||
m_Content = Utility.GetText( node, "" );
|
||||
}
|
||||
|
||||
/// <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", m_AddedBy );
|
||||
|
||||
xml.WriteAttributeString( "lastModified", XmlConvert.ToString( m_LastModified, XmlDateTimeSerializationMode.Local ) );
|
||||
|
||||
xml.WriteString( m_Content );
|
||||
|
||||
xml.WriteEndElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
372
Scripts/Accounting/AccountHandler.cs
Normal file
372
Scripts/Accounting/AccountHandler.cs
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using Server;
|
||||
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
|
||||
}
|
||||
|
||||
public class AccountHandler
|
||||
{
|
||||
private static int MaxAccountsPerIP = Server.Misc.Settings.MaxAccountsPerIP();
|
||||
private static bool AutoAccountCreation = Server.Misc.Settings.AutoAccountCreation();
|
||||
private static bool RestrictDeletion = Server.Misc.Settings.RestrictDeletion();
|
||||
private static TimeSpan DeleteDelay = TimeSpan.FromDays( Server.Misc.Settings.DeleteDelay() );
|
||||
|
||||
public static PasswordProtection ProtectPasswords = PasswordProtection.NewCrypt;
|
||||
|
||||
private static AccessLevel m_LockdownLevel;
|
||||
|
||||
public static AccessLevel LockdownLevel
|
||||
{
|
||||
get{ return m_LockdownLevel; }
|
||||
set{ m_LockdownLevel = value; }
|
||||
}
|
||||
|
||||
private static CityInfo[] StartingCities = new CityInfo[]
|
||||
{
|
||||
new CityInfo( " ", " ", 1150168, 2081, 1585, 0 ),
|
||||
};
|
||||
|
||||
private static bool PasswordCommandEnabled = true;
|
||||
|
||||
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] < 0x80 );
|
||||
|
||||
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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
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.Now < (m.CreationTime + DeleteDelay) )
|
||||
{
|
||||
state.Send( new DeleteResult( DeleteResultType.CharTooYoung ) );
|
||||
state.Send( new CharacterListUpdate( acct ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine( "Client: {0}: Deleting character {1} (0x{2:X})", state, index, m.Serial.Value );
|
||||
|
||||
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 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CanCreate( IPAddress ip )
|
||||
{
|
||||
if ( !IPTable.ContainsKey( ip ) )
|
||||
return true;
|
||||
|
||||
return ( IPTable[ip] < MaxAccountsPerIP );
|
||||
}
|
||||
|
||||
private static Dictionary<IPAddress, Int32> m_IPTable;
|
||||
|
||||
public static Dictionary<IPAddress, Int32> IPTable
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( m_IPTable == null )
|
||||
{
|
||||
m_IPTable = new Dictionary<IPAddress, Int32>();
|
||||
|
||||
foreach ( Account a in Accounts.GetAccounts() )
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private static Account CreateAccount( NetState state, string un, string pw )
|
||||
{
|
||||
if ( un.Length == 0 || pw.Length == 0 )
|
||||
return null;
|
||||
|
||||
bool isSafe = true;
|
||||
|
||||
for ( int i = 0; isSafe && i < un.Length; ++i )
|
||||
isSafe = ( un[i] >= 0x20 && un[i] < 0x80 );
|
||||
|
||||
for ( int i = 0; isSafe && i < pw.Length; ++i )
|
||||
isSafe = ( pw[i] >= 0x20 && pw[i] < 0x80 );
|
||||
|
||||
if ( !isSafe )
|
||||
return null;
|
||||
|
||||
if ( !CanCreate( state.Address ) )
|
||||
{
|
||||
Console.WriteLine( "Login: {0}: Account '{1}' not created, ip already has {2} account{3}.", state, un, MaxAccountsPerIP, MaxAccountsPerIP == 1 ? "" : "s" );
|
||||
return null;
|
||||
}
|
||||
|
||||
Console.WriteLine( "Login: {0}: Creating new account '{1}'", state, un );
|
||||
|
||||
Account a = new Account( un, pw );
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
public static void EventSink_AccountLogin( AccountLoginEventArgs e )
|
||||
{
|
||||
if ( !IPLimiter.SocketBlock && !IPLimiter.Verify( e.State.Address ) )
|
||||
{
|
||||
e.Accepted = false;
|
||||
e.RejectReason = ALRReason.InUse;
|
||||
|
||||
Console.WriteLine( "Login: {0}: Past IP limit threshold", e.State );
|
||||
|
||||
using ( StreamWriter op = new StreamWriter( "ipLimits.log", true ) )
|
||||
op.WriteLine( "{0}\tPast IP limit threshold\t{1}", e.State, DateTime.Now );
|
||||
|
||||
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
|
||||
{
|
||||
Console.WriteLine( "Login: {0}: Invalid username '{1}'", e.State, un );
|
||||
e.RejectReason = ALRReason.Invalid;
|
||||
}
|
||||
}
|
||||
else if ( !acct.HasAccess( e.State ) )
|
||||
{
|
||||
Console.WriteLine( "Login: {0}: Access denied for '{1}'", e.State, un );
|
||||
e.RejectReason = ( m_LockdownLevel > AccessLevel.Player ? ALRReason.BadComm : ALRReason.BadPass );
|
||||
}
|
||||
else if ( !acct.CheckPassword( pw ) )
|
||||
{
|
||||
Console.WriteLine( "Login: {0}: Invalid password for '{1}'", e.State, un );
|
||||
e.RejectReason = ALRReason.BadPass;
|
||||
}
|
||||
else if ( acct.Banned )
|
||||
{
|
||||
Console.WriteLine( "Login: {0}: Banned account '{1}'", e.State, un );
|
||||
e.RejectReason = ALRReason.Blocked;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine( "Login: {0}: Valid credentials for '{1}'", e.State, un );
|
||||
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;
|
||||
|
||||
Console.WriteLine( "Login: {0}: Past IP limit threshold", e.State );
|
||||
|
||||
using ( StreamWriter op = new StreamWriter( "ipLimits.log", true ) )
|
||||
op.WriteLine( "{0}\tPast IP limit threshold\t{1}", e.State, DateTime.Now );
|
||||
|
||||
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 ) )
|
||||
{
|
||||
Console.WriteLine( "Login: {0}: Access denied for '{1}'", e.State, un );
|
||||
e.Accepted = false;
|
||||
}
|
||||
else if ( !acct.CheckPassword( pw ) )
|
||||
{
|
||||
Console.WriteLine( "Login: {0}: Invalid password for '{1}'", e.State, un );
|
||||
e.Accepted = false;
|
||||
}
|
||||
else if ( acct.Banned )
|
||||
{
|
||||
Console.WriteLine( "Login: {0}: Banned account '{1}'", e.State, un );
|
||||
e.Accepted = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
acct.LogAccess( e.State );
|
||||
|
||||
Console.WriteLine( "Login: {0}: Account '{1}' at character list", e.State, un );
|
||||
e.State.Account = acct;
|
||||
e.Accepted = true;
|
||||
e.CityInfo = StartingCities;
|
||||
}
|
||||
|
||||
if ( !e.Accepted )
|
||||
AccountAttackLimiter.RegisterInvalidAccess( e.State );
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Scripts/Accounting/AccountTag.cs
Normal file
61
Scripts/Accounting/AccountTag.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace Server.Accounting
|
||||
{
|
||||
public class AccountTag
|
||||
{
|
||||
private string m_Name, m_Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of this tag.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get{ return m_Name; }
|
||||
set{ m_Name = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of this tag.
|
||||
/// </summary>
|
||||
public string Value
|
||||
{
|
||||
get{ return m_Value; }
|
||||
set{ m_Value = 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 )
|
||||
{
|
||||
m_Name = name;
|
||||
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 )
|
||||
{
|
||||
m_Name = Utility.GetAttribute( node, "name", "empty" );
|
||||
m_Value = Utility.GetText( node, "" );
|
||||
}
|
||||
|
||||
/// <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", m_Name );
|
||||
xml.WriteString( m_Value );
|
||||
xml.WriteEndElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
110
Scripts/Accounting/Accounts.cs
Normal file
110
Scripts/Accounting/Accounts.cs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
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()
|
||||
{
|
||||
#if !MONO
|
||||
return m_Accounts.Values;
|
||||
#else
|
||||
return new List<IAccount>( m_Accounts.Values );
|
||||
#endif
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
344
Scripts/Accounting/Firewall.cs
Normal file
344
Scripts/Accounting/Firewall.cs
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
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
|
||||
{
|
||||
IPAddress m_Address;
|
||||
public IPFirewallEntry( IPAddress address )
|
||||
{
|
||||
m_Address = address;
|
||||
}
|
||||
|
||||
public bool IsBlocked( IPAddress address )
|
||||
{
|
||||
return m_Address.Equals( address );
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return m_Address.ToString();
|
||||
}
|
||||
|
||||
public override bool Equals( object obj )
|
||||
{
|
||||
if( obj is IPAddress )
|
||||
{
|
||||
return obj.Equals( m_Address );
|
||||
}
|
||||
else if( obj is string )
|
||||
{
|
||||
IPAddress otherAddress;
|
||||
|
||||
if( IPAddress.TryParse( (string)obj, out otherAddress ) )
|
||||
return otherAddress.Equals( m_Address );
|
||||
}
|
||||
else if( obj is IPFirewallEntry )
|
||||
{
|
||||
return m_Address.Equals( ((IPFirewallEntry)obj).m_Address );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_Address.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public class CIDRFirewallEntry : IFirewallEntry
|
||||
{
|
||||
IPAddress m_CIDRPrefix;
|
||||
int m_CIDRLength;
|
||||
|
||||
public CIDRFirewallEntry( IPAddress cidrPrefix, int cidrLength )
|
||||
{
|
||||
m_CIDRPrefix = cidrPrefix;
|
||||
m_CIDRLength = cidrLength;
|
||||
}
|
||||
|
||||
public bool IsBlocked( IPAddress address )
|
||||
{
|
||||
return Utility.IPMatchCIDR( m_CIDRPrefix, address, m_CIDRLength );
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format( "{0}/{1}", m_CIDRPrefix, 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 m_CIDRPrefix.Equals( cidrPrefix ) && m_CIDRLength.Equals( cidrLength );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( obj is CIDRFirewallEntry )
|
||||
{
|
||||
CIDRFirewallEntry entry = obj as CIDRFirewallEntry;
|
||||
|
||||
return m_CIDRPrefix.Equals( entry.m_CIDRPrefix ) && m_CIDRLength.Equals( entry.m_CIDRLength );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_CIDRPrefix.GetHashCode() ^ m_CIDRLength.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public class WildcardIPFirewallEntry : IFirewallEntry
|
||||
{
|
||||
string m_Entry;
|
||||
|
||||
bool m_Valid = true;
|
||||
|
||||
public WildcardIPFirewallEntry( string entry )
|
||||
{
|
||||
m_Entry = entry;
|
||||
}
|
||||
|
||||
public bool IsBlocked( IPAddress address )
|
||||
{
|
||||
if( !m_Valid )
|
||||
return false; //Why process if it's invalid? it'll return false anyway after processing it.
|
||||
|
||||
return Utility.IPMatch( m_Entry, address, ref m_Valid );
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return m_Entry.ToString();
|
||||
}
|
||||
|
||||
public override bool Equals( object obj )
|
||||
{
|
||||
if( obj is string )
|
||||
return obj.Equals( m_Entry );
|
||||
else if( obj is WildcardIPFirewallEntry )
|
||||
return m_Entry.Equals( ((WildcardIPFirewallEntry)obj).m_Entry );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return 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;
|
||||
* */
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Scripts/Accounting/IPLimiter.cs
Normal file
59
Scripts/Accounting/IPLimiter.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Server;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue