* Change some config variables that use `char` to `StringBuf`. So, don't need to change the char length anymore. * Renamed some inter table config variables postfix "_db" to "_table". * Added table checks for login-server (Account, IP Ban, & Login Log), map-server. * Regroup map-server table variables to `struct MapServer_Schema mapserv_schema_config`. * Regroup map-server global config to `struct Map_Config map_config`. * Split log file or table names in `log_athena.conf`. File path to `log_path: log/` and extension to `log_extension: .log`. So, if logging is enabled by using file or table, don't need to rename all log names. * Removed unnecessary configs in map_athena.conf: `help_txt`, `help2_txt`, and `charhelp_txt`. * Removed unused file 'conf/charhelp.txt'. * Added new StringBuf function * `StringBuf* StringBuf_MallocInitial` * `StringBuf* StringBuf_FromStr` * `void StringBuf_InitialInit` * `int StringBuf_PrintfClear` * Thanks to @aleos89 and @Mendonn at #91 for that StringBuf Signed-off-by: Cydh Ramdh <house.bad@gmail.com>
282 lines
8.9 KiB
C
282 lines
8.9 KiB
C
/**
|
|
* @file ipban.c
|
|
* Module purpose is to read configuration for login-server and handle accounts,
|
|
* and also to synchronize all login interfaces: loginchrif, loginclif, logincnslif.
|
|
* Licensed under GNU GPL.
|
|
* For more information, see LICENCE in the main folder.
|
|
* @author Athena Dev Teams < r15k
|
|
* @author rAthena Dev Team
|
|
*/
|
|
|
|
#include "../common/cbasetypes.h"
|
|
#include "../common/showmsg.h"
|
|
#include "../common/sql.h"
|
|
#include "../common/strlib.h"
|
|
#include "../common/timer.h"
|
|
#include "login.h"
|
|
#include "ipban.h"
|
|
#include "loginlog.h"
|
|
#include <stdlib.h>
|
|
|
|
// globals
|
|
static Sql* sql_handle = NULL;
|
|
|
|
// login sql settings
|
|
struct Ipban_Config {
|
|
uint16 ipban_db_port;
|
|
StringBuf *ipban_db_hostname;
|
|
StringBuf *ipban_db_username;
|
|
StringBuf *ipban_db_password;
|
|
StringBuf *ipban_db_database;
|
|
StringBuf *ipban_codepage;
|
|
StringBuf *ipban_table;
|
|
|
|
int cleanup_timer_id;
|
|
bool ipban_inited;
|
|
};
|
|
|
|
struct Ipban_Config ipban_config;
|
|
|
|
//early declaration
|
|
int ipban_cleanup(int tid, unsigned int tick, int id, intptr_t data);
|
|
|
|
void ipban_config_init(void) {
|
|
ipban_config.ipban_db_port = 3306;
|
|
ipban_config.ipban_db_hostname = StringBuf_FromStr("127.0.0.1");
|
|
ipban_config.ipban_db_username = StringBuf_FromStr("ragnarok");
|
|
ipban_config.ipban_db_password = StringBuf_FromStr("");
|
|
ipban_config.ipban_db_database = StringBuf_FromStr("ragnarok");
|
|
ipban_config.ipban_codepage = StringBuf_FromStr("");
|
|
ipban_config.ipban_table = StringBuf_FromStr("ipbanlist");
|
|
|
|
ipban_config.cleanup_timer_id = INVALID_TIMER;
|
|
ipban_config.ipban_inited = false;
|
|
}
|
|
|
|
static void ipban_config_final(void) {
|
|
StringBuf_Free(ipban_config.ipban_db_hostname);
|
|
StringBuf_Free(ipban_config.ipban_db_username);
|
|
StringBuf_Free(ipban_config.ipban_db_password);
|
|
StringBuf_Free(ipban_config.ipban_db_database);
|
|
StringBuf_Free(ipban_config.ipban_codepage);
|
|
StringBuf_Free(ipban_config.ipban_table);
|
|
}
|
|
|
|
static bool ipban_check_table(void) {
|
|
ShowInfo("Start checking DB integrity (IP Ban)\n");
|
|
|
|
// IP ban List
|
|
if( SQL_ERROR == Sql_Query(sql_handle,
|
|
"SELECT `list`, `btime`, `rtime`, `reason` "
|
|
"FROM `%s`;", StringBuf_Value(ipban_config.ipban_table)) )
|
|
{
|
|
Sql_ShowDebug(sql_handle);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if ip is in the active bans list.
|
|
* @param ip: ipv4 ip to check if ban
|
|
* @return true if found or error, false if not in list
|
|
*/
|
|
bool ipban_check(uint32 ip) {
|
|
uint8* p = (uint8*)&ip;
|
|
char* data = NULL;
|
|
int matches;
|
|
|
|
if( !login_config.ipban )
|
|
return false;// ipban disabled
|
|
|
|
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT count(*) FROM `%s` WHERE `rtime` > NOW() AND (`list` = '%u.*.*.*' OR `list` = '%u.%u.*.*' OR `list` = '%u.%u.%u.*' OR `list` = '%u.%u.%u.%u')",
|
|
StringBuf_Value(ipban_config.ipban_table), p[3], p[3], p[2], p[3], p[2], p[1], p[3], p[2], p[1], p[0]) )
|
|
{
|
|
Sql_ShowDebug(sql_handle);
|
|
// close connection because we can't verify their connectivity.
|
|
return true;
|
|
}
|
|
|
|
if( SQL_ERROR == Sql_NextRow(sql_handle) )
|
|
return true;// Shouldn't happen, but just in case...
|
|
|
|
Sql_GetData(sql_handle, 0, &data, NULL);
|
|
matches = atoi(data);
|
|
Sql_FreeResult(sql_handle);
|
|
|
|
return( matches > 0 );
|
|
}
|
|
|
|
/**
|
|
* Log a failed attempt.
|
|
* Also bans the user if too many failed attempts are made.
|
|
* @param ip: ipv4 ip to record the failure
|
|
*/
|
|
void ipban_log(uint32 ip) {
|
|
unsigned long failures;
|
|
|
|
if( !login_config.ipban )
|
|
return;// ipban disabled
|
|
|
|
failures = loginlog_failedattempts(ip, login_config.dynamic_pass_failure_ban_interval);// how many times failed account? in one ip.
|
|
|
|
// if over the limit, add a temporary ban entry
|
|
if( failures >= login_config.dynamic_pass_failure_ban_limit )
|
|
{
|
|
uint8* p = (uint8*)&ip;
|
|
if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`list`,`btime`,`rtime`,`reason`) VALUES ('%u.%u.%u.*', NOW() , NOW() + INTERVAL %d MINUTE ,'Password error ban')",
|
|
StringBuf_Value(ipban_config.ipban_table), p[3], p[2], p[1], login_config.dynamic_pass_failure_ban_duration) )
|
|
Sql_ShowDebug(sql_handle);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Timered function to remove expired bans.
|
|
* Request all characters to update their registered ip and transmit their new ip.
|
|
* Performed each ip_sync_interval.
|
|
* @param tid: timer id
|
|
* @param tick: tick of execution
|
|
* @param id: unused
|
|
* @param data: unused
|
|
* @return 0
|
|
*/
|
|
int ipban_cleanup(int tid, unsigned int tick, int id, intptr_t data) {
|
|
if( !login_config.ipban )
|
|
return 0;// ipban disabled
|
|
|
|
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()") )
|
|
Sql_ShowDebug(sql_handle);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read configuration options.
|
|
* @param key: config keyword
|
|
* @param value: config value for keyword
|
|
* @return true if successful, false if config not complete or server already running
|
|
*/
|
|
bool ipban_config_read(const char* key, const char* value) {
|
|
const char* signature;
|
|
|
|
if( ipban_config.ipban_inited )
|
|
return false;// settings can only be changed before init
|
|
|
|
signature = "ipban_db_";
|
|
if( strncmpi(key, signature, strlen(signature)) == 0 )
|
|
{
|
|
key += strlen(signature);
|
|
if( strcmpi(key, "ip") == 0 )
|
|
StringBuf_PrintfClear(ipban_config.ipban_db_hostname, value);
|
|
else
|
|
if( strcmpi(key, "port") == 0 )
|
|
ipban_config.ipban_db_port = (uint16)strtoul(value, NULL, 10);
|
|
else
|
|
if( strcmpi(key, "id") == 0 )
|
|
StringBuf_PrintfClear(ipban_config.ipban_db_username, value);
|
|
else
|
|
if( strcmpi(key, "pw") == 0 )
|
|
StringBuf_PrintfClear(ipban_config.ipban_db_password, value);
|
|
else
|
|
if( strcmpi(key, "db") == 0 )
|
|
StringBuf_PrintfClear(ipban_config.ipban_db_database, value);
|
|
else
|
|
return false;// not found
|
|
return true;
|
|
}
|
|
|
|
signature = "ipban_";
|
|
if( strncmpi(key, signature, strlen(signature)) == 0 )
|
|
{
|
|
key += strlen(signature);
|
|
if( strcmpi(key, "codepage") == 0 )
|
|
StringBuf_PrintfClear(ipban_config.ipban_codepage, value);
|
|
else
|
|
if( strcmpi(key, "ipban_table") == 0 )
|
|
StringBuf_PrintfClear(ipban_config.ipban_table, value);
|
|
else
|
|
if( strcmpi(key, "enable") == 0 )
|
|
login_config.ipban = (bool)config_switch(value);
|
|
else
|
|
if( strcmpi(key, "dynamic_pass_failure_ban") == 0 )
|
|
login_config.dynamic_pass_failure_ban = (bool)config_switch(value);
|
|
else
|
|
if( strcmpi(key, "dynamic_pass_failure_ban_interval") == 0 )
|
|
login_config.dynamic_pass_failure_ban_interval = atoi(value);
|
|
else
|
|
if( strcmpi(key, "dynamic_pass_failure_ban_limit") == 0 )
|
|
login_config.dynamic_pass_failure_ban_limit = atoi(value);
|
|
else
|
|
if( strcmpi(key, "dynamic_pass_failure_ban_duration") == 0 )
|
|
login_config.dynamic_pass_failure_ban_duration = atoi(value);
|
|
else
|
|
return false;// not found
|
|
return true;
|
|
}
|
|
|
|
return false;// not found
|
|
}
|
|
|
|
|
|
/// Constructor destructor
|
|
|
|
/**
|
|
* Initialize the module.
|
|
* Launched at login-serv start, create db or other long scope variable here.
|
|
*/
|
|
void ipban_init(void) {
|
|
ipban_config.ipban_inited = true;
|
|
|
|
if( !login_config.ipban )
|
|
return;// ipban disabled
|
|
|
|
// establish connections
|
|
sql_handle = Sql_Malloc();
|
|
if( SQL_ERROR == Sql_Connect(sql_handle, StringBuf_Value(ipban_config.ipban_db_username), StringBuf_Value(ipban_config.ipban_db_password), StringBuf_Value(ipban_config.ipban_db_hostname), ipban_config.ipban_db_port, StringBuf_Value(ipban_config.ipban_db_database)) )
|
|
{
|
|
ShowError("Couldn't connect with uname='%s',passwd='%s',host='%s',port='%d',database='%s'\n",
|
|
StringBuf_Value(ipban_config.ipban_db_username), StringBuf_Value(ipban_config.ipban_db_password), StringBuf_Value(ipban_config.ipban_db_hostname), ipban_config.ipban_db_port, StringBuf_Value(ipban_config.ipban_db_database));
|
|
Sql_ShowDebug(sql_handle);
|
|
Sql_Free(sql_handle);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
ShowInfo("Ipban conection made\n");
|
|
|
|
if( StringBuf_Length(ipban_config.ipban_codepage) && SQL_ERROR == Sql_SetEncoding(sql_handle, StringBuf_Value(ipban_config.ipban_codepage)) )
|
|
Sql_ShowDebug(sql_handle);
|
|
|
|
if (!ipban_check_table()) {
|
|
ShowFatalError("login-server (ipban) : A table is missing in sql-server, please fix it, see (sql-files/main.sql for structure) \n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ShowStatus("Ipban connection: Database '"CL_WHITE"%s"CL_RESET"' at '"CL_WHITE"%s"CL_RESET"'\n", StringBuf_Value(ipban_config.ipban_db_database), StringBuf_Value(ipban_config.ipban_db_hostname));
|
|
|
|
if( login_config.ipban_cleanup_interval > 0 )
|
|
{ // set up periodic cleanup of connection history and active bans
|
|
add_timer_func_list(ipban_cleanup, "ipban_cleanup");
|
|
ipban_config.cleanup_timer_id = add_timer_interval(gettick()+10, ipban_cleanup, 0, 0, login_config.ipban_cleanup_interval*1000);
|
|
} else // make sure it gets cleaned up on login-server start regardless of interval-based cleanups
|
|
ipban_cleanup(0,0,0,0);
|
|
}
|
|
|
|
/**
|
|
* Destroy the module.
|
|
* Launched at login-serv end, cleanup db connection or other thing here.
|
|
*/
|
|
void ipban_final(void) {
|
|
if( !login_config.ipban )
|
|
return;// ipban disabled
|
|
|
|
if( login_config.ipban_cleanup_interval > 0 )
|
|
// release data
|
|
delete_timer(ipban_config.cleanup_timer_id, ipban_cleanup);
|
|
|
|
ipban_cleanup(0,0,0,0); // always clean up on login-server stop
|
|
ipban_config_final();
|
|
|
|
// close connections
|
|
Sql_Free(sql_handle);
|
|
sql_handle = NULL;
|
|
}
|