-Apply login_refac (from old refac branch) in same way as char_refac, each interface now have his own file.

-Remove _sql from some file.
Thx to Euphy for helping with documentation.
This commit is contained in:
lighta
2014-07-15 19:49:32 -04:00
parent 7ccd13e904
commit a24da6d302
22 changed files with 2858 additions and 1844 deletions

View File

@@ -180,5 +180,11 @@ client_hash_check: off
//client_hash: 10, cb1ea78023d337c38e8ba5124e2338ae
//client_hash: 99, disabled
//New registration flood protection [Kevin]
//Number of new registration allowed
allowed_regs: 1
//Time in second before the counter for the number of registration is reset
time_allowed: 10
import: conf/inter_athena.conf
import: conf/import/login_conf.txt

View File

@@ -1,6 +1,7 @@
// Subnet support file
// Format is:
// subnet: net-submask:char_ip:map_ip
// you can add more than one subnet
// you can add more than one subnet (max 16)
// check is if((net-submask & char_ip ) == (net-submask & servip)) => ok
subnet: 255.0.0.0:127.0.0.1:127.0.0.1

View File

@@ -1968,43 +1968,11 @@ int char_lan_subnetcheck(uint32 ip){
}
// Console Command Parser [Wizputer]
int parse_console(const char* buf)
{
char type[64];
char command[64];
int n=0;
if( ( n = sscanf(buf, "%63[^:]:%63[^\n]", type, command) ) < 2 ){
if((n = sscanf(buf, "%63[^\n]", type))<1) return -1; //nothing to do no arg
}
if( n != 2 ){ //end string
ShowNotice("Type: '%s'\n",type);
command[0] = '\0';
}
else
ShowNotice("Type of command: '%s' || Command: '%s'\n",type,command);
if( n == 2 && strcmpi("server", type) == 0 ){
if( strcmpi("shutdown", command) == 0 || strcmpi("exit", command) == 0 || strcmpi("quit", command) == 0 ){
runflag = CHARSERVER_ST_SHUTDOWN;
}
else if( strcmpi("alive", command) == 0 || strcmpi("status", command) == 0 )
ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
}
else if( strcmpi("ers_report", type) == 0 ){
ers_report();
}
else if( strcmpi("help", type) == 0 ){
ShowInfo("Available commands:\n");
ShowInfo("\t server:shutdown => Stops the server.\n");
ShowInfo("\t server:alive => Checks if the server is running.\n");
ShowInfo("\t ers_report => Displays database usage.\n");
}
return 0;
//FIXME to be remove (moved to cnslif / will be done once map/char/login, all have their cnslif interface ready)
int parse_console(const char* buf){
return cnslif_parse(buf);
}
//------------------------------------------------
//Pincode system
//------------------------------------------------

View File

@@ -89,6 +89,9 @@
/* Feb 1st 2012 */
#if PACKETVER >= 20120201
#define NEW_CARTS
#ifndef ENABLE_SC_SAVING
#warning "Cart won't be able to be saved for relog"
#endif
#define MAX_CARTS 9
#else
#define MAX_CARTS 5

View File

@@ -1,5 +1,11 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
/**
* @file account.c
* Module purpose is to save, load, and update changes into the account table or file.
* Licensed under GNU GPL.
* For more information, see LICENCE in the main folder.
* @author Athena Dev Teams < r15k
* @author rAthena Dev Team
*/
#include "../common/malloc.h"
#include "../common/mmo.h"
@@ -16,8 +22,7 @@
#define ACCOUNT_SQL_DB_VERSION 20110114
/// internal structure
typedef struct AccountDB_SQL
{
typedef struct AccountDB_SQL {
AccountDB vtable; // public interface
Sql* accounts; // SQL accounts storage
@@ -44,10 +49,8 @@ typedef struct AccountDB_SQL
} AccountDB_SQL;
/// internal structure
typedef struct AccountDBIterator_SQL
{
typedef struct AccountDBIterator_SQL {
AccountDBIterator vtable; // public interface
AccountDB_SQL* db;
int last_account_id;
} AccountDBIterator_SQL;
@@ -70,8 +73,7 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new);
/// public constructor
AccountDB* account_db_sql(void)
{
AccountDB* account_db_sql(void) {
AccountDB_SQL* db = (AccountDB_SQL*)aCalloc(1, sizeof(AccountDB_SQL));
// set up the vtable
@@ -114,9 +116,11 @@ AccountDB* account_db_sql(void)
/* ------------------------------------------------------------------------- */
/// establishes database connection
static bool account_db_sql_init(AccountDB* self)
{
/**
* Establish the database connection.
* @param self: pointer to db
*/
static bool account_db_sql_init(AccountDB* self) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
Sql* sql_handle;
const char* username;
@@ -162,9 +166,11 @@ static bool account_db_sql_init(AccountDB* self)
return true;
}
/// disconnects from database
static void account_db_sql_destroy(AccountDB* self)
{
/**
* Destroy the database and close the connection to it.
* @param self: pointer to db
*/
static void account_db_sql_destroy(AccountDB* self){
AccountDB_SQL* db = (AccountDB_SQL*)self;
Sql_Free(db->accounts);
@@ -172,7 +178,15 @@ static void account_db_sql_destroy(AccountDB* self)
aFree(db);
}
/// Gets a property from this database.
/**
* Get configuration information into buf.
* If the option is supported, adjust the internal state.
* @param self: pointer to db
* @param key: config keyword
* @param buf: value set of the keyword
* @param buflen: size of buffer to avoid out of bound
* @return true if successful, false if something has failed
*/
static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen)
{
AccountDB_SQL* db = (AccountDB_SQL*)self;
@@ -259,9 +273,15 @@ static bool account_db_sql_get_property(AccountDB* self, const char* key, char*
return false;// not found
}
/// if the option is supported, adjusts the internal state
static bool account_db_sql_set_property(AccountDB* self, const char* key, const char* value)
{
/**
* Read and set configuration.
* If the option is supported, adjust the internal state.
* @param self: pointer to db
* @param key: config keyword
* @param value: config value for keyword
* @return true if successful, false if something has failed
*/
static bool account_db_sql_set_property(AccountDB* self, const char* key, const char* value) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
const char* signature;
@@ -330,11 +350,15 @@ static bool account_db_sql_set_property(AccountDB* self, const char* key, const
return false;// not found
}
/// create a new account entry
/// If acc->account_id is -1, the account id will be auto-generated,
/// and its value will be written to acc->account_id if everything succeeds.
static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc)
{
/**
* Create a new account entry.
* If acc->account_id is -1, the account id will be auto-generated,
* and its value will be written to acc->account_id if everything succeeds.
* @param self: pointer to db
* @param acc: pointer of mmo_account to save
* @return true if successful, false if something has failed
*/
static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
Sql* sql_handle = db->accounts;
@@ -383,9 +407,13 @@ static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc)
return mmo_auth_tosql(db, acc, true);
}
/// delete an existing account entry + its regs
static bool account_db_sql_remove(AccountDB* self, const int account_id)
{
/**
* Delete an existing account entry and its regs.
* @param self: pointer to db
* @param account_id: id of user account
* @return true if successful, false if something has failed
*/
static bool account_db_sql_remove(AccountDB* self, const int account_id) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
Sql* sql_handle = db->accounts;
bool result = false;
@@ -402,23 +430,40 @@ static bool account_db_sql_remove(AccountDB* self, const int account_id)
return result;
}
/// update an existing account with the provided new data (both account and regs)
static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc)
{
/**
* Update an existing account with the new data provided (both account and regs).
* @param self: pointer to db
* @param acc: pointer of mmo_account to save
* @return true if successful, false if something has failed
*/
static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
return mmo_auth_tosql(db, acc, false);
}
/// retrieve data from db and store it in the provided data structure
static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const int account_id)
{
/**
* Retrieve data from db and store it in the provided data structure.
* Filled data structure is done by delegation to mmo_auth_fromsql.
* @param self: pointer to db
* @param acc: pointer of mmo_account to fill
* @param account_id: id of user account
* @return true if successful, false if something has failed
*/
static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const int account_id) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
return mmo_auth_fromsql(db, acc, account_id);
}
/// retrieve data from db and store it in the provided data structure
static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid)
{
/**
* Retrieve data from db and store it in the provided data structure.
* Doesn't actually retrieve data yet: escapes and checks userid, then transforms it to accid for fetching.
* Filled data structure is done by delegation to account_db_sql_load_num.
* @param self: pointer to db
* @param acc: pointer of mmo_account to fill
* @param userid: name of user account
* @return true if successful, false if something has failed
*/
static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
Sql* sql_handle = db->accounts;
char esc_userid[2*NAME_LENGTH+1];
@@ -454,10 +499,12 @@ static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, co
return account_db_sql_load_num(self, acc, account_id);
}
/// Returns a new forward iterator.
static AccountDBIterator* account_db_sql_iterator(AccountDB* self)
{
/**
* Create a new forward iterator.
* @param self: pointer to db iterator
* @return a new db iterator
*/
static AccountDBIterator* account_db_sql_iterator(AccountDB* self) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)aCalloc(1, sizeof(AccountDBIterator_SQL));
@@ -472,18 +519,22 @@ static AccountDBIterator* account_db_sql_iterator(AccountDB* self)
return &iter->vtable;
}
/// Destroys this iterator, releasing all allocated memory (including itself).
static void account_db_sql_iter_destroy(AccountDBIterator* self)
{
/**
* Destroys this iterator, releasing all allocated memory (including itself).
* @param self: pointer to db iterator
*/
static void account_db_sql_iter_destroy(AccountDBIterator* self) {
AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
aFree(iter);
}
/// Fetches the next account in the database.
static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc)
{
/**
* Fetches the next account in the database.
* @param self: pointer to db iterator
* @param acc: pointer of mmo_account to fill
* @return true if next account found and filled, false if something has failed
*/
static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc) {
AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
AccountDB_SQL* db = (AccountDB_SQL*)iter->db;
Sql* sql_handle = db->accounts;
@@ -514,9 +565,14 @@ static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account
return false;
}
static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id)
{
/**
* Fetch a struct mmo_account from sql.
* @param db: pointer to db
* @param acc: pointer of mmo_account to fill
* @param account_id: id of user account to take data from
* @return true if successful, false if something has failed
*/
static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id) {
Sql* sql_handle = db->accounts;
char* data;
int i = 0;
@@ -587,8 +643,14 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
return true;
}
static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new)
{
/**
* Save a struct mmo_account in sql.
* @param db: pointer to db
* @param acc: pointer of mmo_account to save
* @param is_new: if it's a new entry or should we update
* @return true if successful, false if something has failed
*/
static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new) {
Sql* sql_handle = db->accounts;
SqlStmt* stmt = SqlStmt_Malloc(sql_handle);
bool result = false;

View File

@@ -1,5 +1,11 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
/**
* @file account.h
* Module purpose is to save, load, and update changes into the account table or file.
* Licensed under GNU GPL.
* For more information, see LICENCE in the main folder.
* @author Athena Dev Teams < r15k
* @author rAthena Dev Team
*/
#ifndef __ACCOUNT_H_INCLUDED__
#define __ACCOUNT_H_INCLUDED__
@@ -61,8 +67,7 @@ struct mmo_account {
};
struct AccountDBIterator
{
struct AccountDBIterator {
/// Destroys this iterator, releasing all allocated memory (including itself).
///
/// @param self Iterator
@@ -77,8 +82,7 @@ struct AccountDBIterator
};
struct AccountDB
{
struct AccountDB {
/// Initializes this database, making it ready for use.
/// Call this after setting the properties.
///

View File

@@ -1,5 +1,12 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
/**
* @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/db.h"
@@ -35,82 +42,90 @@ static Sql* sql_handle = NULL;
static int cleanup_timer_id = INVALID_TIMER;
static bool ipban_inited = false;
//early declaration
int ipban_cleanup(int tid, unsigned int tick, int id, intptr_t data);
// initialize
void ipban_init(void)
{
const char* username;
const char* password;
const char* hostname;
uint16 port;
const char* database;
const char* codepage;
ipban_inited = 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;// ipban disabled
return false;// ipban disabled
if( ipban_db_hostname[0] != '\0' )
{// local settings
username = ipban_db_username;
password = ipban_db_password;
hostname = ipban_db_hostname;
port = ipban_db_port;
database = ipban_db_database;
codepage = ipban_codepage;
}
else
{// global settings
username = global_db_username;
password = global_db_password;
hostname = global_db_hostname;
port = global_db_port;
database = global_db_database;
codepage = global_codepage;
}
// establish connections
sql_handle = Sql_Malloc();
if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
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')",
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);
Sql_Free(sql_handle);
exit(EXIT_FAILURE);
// close connection because we can't verify their connectivity.
return true;
}
if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
Sql_ShowDebug(sql_handle);
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");
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);
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 );
}
// finalize
void ipban_final(void)
{
/**
* 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
if( login_config.ipban_cleanup_interval > 0 )
// release data
delete_timer(cleanup_timer_id, ipban_cleanup);
ipban_cleanup(0,0,0,0); // always clean up on login-server stop
failures = loginlog_failedattempts(ip, login_config.dynamic_pass_failure_ban_interval);// how many times failed account? in one ip.
// close connections
Sql_Free(sql_handle);
sql_handle = NULL;
// 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')",
ipban_table, p[3], p[2], p[1], login_config.dynamic_pass_failure_ban_duration) )
Sql_ShowDebug(sql_handle);
}
}
// load configuration options
bool ipban_config_read(const char* key, const char* value)
{
/**
* 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_inited )
@@ -197,62 +212,79 @@ bool ipban_config_read(const char* key, const char* value)
return false;// not found
}
// check ip against active bans list
bool ipban_check(uint32 ip)
{
uint8* p = (uint8*)&ip;
char* data = NULL;
int matches;
if( !login_config.ipban )
return false;// ipban disabled
/// Constructor destructor
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')",
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;
}
/**
* Initialize the module.
* Launched at login-serv start, create db or other long scope variable here.
*/
void ipban_init(void) {
const char* username;
const char* password;
const char* hostname;
uint16 port;
const char* database;
const char* codepage;
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 failed attempt
void ipban_log(uint32 ip)
{
unsigned long failures;
ipban_inited = true;
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')",
ipban_table, p[3], p[2], p[1], login_config.dynamic_pass_failure_ban_duration) )
Sql_ShowDebug(sql_handle);
if( ipban_db_hostname[0] != '\0' )
{// local settings
username = ipban_db_username;
password = ipban_db_password;
hostname = ipban_db_hostname;
port = ipban_db_port;
database = ipban_db_database;
codepage = ipban_codepage;
}
else
{// global settings
username = global_db_username;
password = global_db_password;
hostname = global_db_hostname;
port = global_db_port;
database = global_db_database;
codepage = global_codepage;
}
}
// remove expired bans
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()") )
// establish connections
sql_handle = Sql_Malloc();
if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
{
Sql_ShowDebug(sql_handle);
Sql_Free(sql_handle);
exit(EXIT_FAILURE);
}
if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
Sql_ShowDebug(sql_handle);
return 0;
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");
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(cleanup_timer_id, ipban_cleanup);
ipban_cleanup(0,0,0,0); // always clean up on login-server stop
// close connections
Sql_Free(sql_handle);
sql_handle = NULL;
}

View File

@@ -1,25 +1,50 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
/**
* @file ipban.h
* 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
*/
#ifndef __IPBAN_H_INCLUDED__
#define __IPBAN_H_INCLUDED__
#include "../common/cbasetypes.h"
// initialize
void ipban_init(void);
// finalize
void ipban_final(void);
// check ip against ban list
/**
* 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);
// increases failure count for the specified IP
/**
* 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);
// parses configuration option
/**
* 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);
/**
* Initialize the module.
* Launched at login-serv start, create db or other long scope variable here.
*/
void ipban_init(void);
/**
* Destroy the module.
* Launched at login-serv end, cleanup db connection or other thing here.
*/
void ipban_final(void);
#endif // __IPBAN_H_INCLUDED__

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,22 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
/**
* @file login.h
* Module purpose is to read configuration for login-server and handle accounts,
* and also to synchronise 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
*/
#ifndef _LOGIN_H_
#define _LOGIN_H_
#include "../common/mmo.h" // NAME_LENGTH,SEX_*
#include "../common/core.h" // CORE_ST_LAST
#include "account.h"
#include "../config/core.h"
enum E_LOGINSERVER_ST
{
enum E_LOGINSERVER_ST {
LOGINSERVER_ST_RUNNING = CORE_ST_LAST,
LOGINSERVER_ST_STARTING,
LOGINSERVER_ST_SHUTDOWN,
@@ -27,7 +34,7 @@ struct login_session_data {
char sex; /// 'F','M','S'
char userid[NAME_LENGTH]; /// account name
char passwd[32+1]; /// 23+1 for plaintext, 32+1 for md5-ed passwords
char passwd[PASSWD_LENGTH]; // 23+1 for plaintext, 32+1 for md5-ed passwords
int passwdenc; /// was the passwd transmited encrypted or clear ?
char md5key[20]; /// md5 key of session (each connection could be encrypted with a md5 key)
uint16 md5keylen; /// len of the md5 key
@@ -43,6 +50,7 @@ struct login_session_data {
int fd; ///socket of client
};
#define MAX_SERVERS 30 //max number of mapserv that could be attach
///Struct describing 1 char-serv attach to us
struct mmo_char_server {
char name[20]; ///char-serv name
@@ -53,9 +61,10 @@ struct mmo_char_server {
uint16 type; /// 0=normal, 1=maintenance, 2=over 18, 3=paying, 4=P2P
uint16 new_; /// should display as 'new'?
};
extern struct mmo_char_server ch_server[MAX_SERVERS];
struct client_hash_node {
unsigned int group_id; ///group
unsigned int group_id; //inferior or egal group to apply restriction
uint8 hash[16]; ///hash required for that groupid or below
struct client_hash_node *next; ///next entry
};
@@ -84,10 +93,16 @@ struct Login_Config {
bool use_dnsbl; /// dns blacklist blocking ?
char dnsbl_servs[1024]; /// comma-separated list of dnsbl servers
char account_engine[256]; /// name of the engine to use (defaults to auto, for the first available engine)
char account_engine[256]; // name of the engine to use (defaults to auto, for the first available engine)
int allowed_regs; //max number of registration
int time_allowed; //registration intervall in seconds
int client_hash_check; /// flags for checking client md5
struct client_hash_node *client_hash_nodes; /// linked list containg md5 hash for each gm group
int client_hash_check; // flags for checking client md5
struct client_hash_node *client_hash_nodes; // linked list containg md5 hash for each gm group
char loginconf_name[256]; //name of main config file
char msgconf_name[256]; //name of msg_conf config file
char lanconf_name[256]; //name of lan config file
int char_per_account; /// number of characters an account can have
#ifdef VIP_ENABLE
struct {
@@ -96,6 +111,7 @@ struct Login_Config {
} vip_sys;
#endif
};
extern struct Login_Config login_config;
#define sex_num2str(num) ( (num == SEX_FEMALE ) ? 'F' : (num == SEX_MALE ) ? 'M' : 'S' )
#define sex_str2num(str) ( (str == 'F' ) ? SEX_FEMALE : (str == 'M' ) ? SEX_MALE : SEX_SERVER )
@@ -103,15 +119,119 @@ struct Login_Config {
#define msg_config_read(cfgName) login_msg_config_read(cfgName)
#define msg_txt(msg_number) login_msg_txt(msg_number)
#define do_final_msg() login_do_final_msg()
int login_msg_config_read(char *cfgName);
const char* login_msg_txt(int msg_number);
void login_do_final_msg(void);
/// Online User Database [Wizputer]
struct online_login_data {
int account_id;
int waiting_disconnect;
int char_server;
};
extern DBMap* online_db; // int account_id -> struct online_login_data*
#define MAX_SERVERS 30 ///number of charserv loginserv can handle
extern struct mmo_char_server ch_server[MAX_SERVERS]; ///array of char-servs data
extern struct Login_Config login_config; ///config of login serv
/// Auth database
#define AUTH_TIMEOUT 30000
struct auth_node {
int account_id;
uint32 login_id1;
uint32 login_id2;
uint32 ip;
char sex;
uint32 version;
uint8 clienttype;
};
extern DBMap* auth_db; // int account_id -> struct auth_node*
///Accessors
AccountDB* login_get_accounts_db(void);
/**
* Sub function to create an online_login_data and save it to db.
* @param key: Key of the database entry
* @param ap: args
* @return : Data identified by the key to be put in the database
* @see DBCreateData
*/
DBData login_create_online_user(DBKey key, va_list args);
/**
* Function to add a user in online_db.
* Checking if the user is already registered in the db.
* Stop disconnection timer if set.
* @param char_server: id of char-serv on wich the player is
* @param account_id: the account identifier
* @return the new|registered online data
*/
struct online_login_data* login_add_online_user(int char_server, int account_id);
/**
* Function to remove a user from online_db.
* Checking if user was already scheduled for deletion, and remove that timer if found.
* @param account_id: the account identifier
*/
void login_remove_online_user(int account_id);
/**
* Timered function to disconnect a user from login.
* This is done either after auth_ok or kicked by char-server.
* Removing user from auth_db and online_db.
* Delay is AUTH_TIMEOUT by default.
* @param tid: timer id
* @param tick: tick of execution
* @param id: user account id
* @param data: unused
* @return :0
*/
int login_waiting_disconnect_timer(int tid, unsigned int tick, int id, intptr_t data);
/**
* Sub function to apply on online_db.
* Mark a character as offline.
* @param data: 1 entry in the db
* @param ap: args
* @return : Value to be added up by the function that is applying this
* @see DBApply
*/
int login_online_db_setoffline(DBKey key, DBData *data, va_list ap);
/**
* Test to determine if an IP come from LAN or WAN.
* @param ip: ip to check if in auth network
* @return 0 if from wan, or subnet_char_ip if lan
*/
int lan_subnetcheck(uint32 ip);
/**
* Create a new account and save it in db/sql.
* @param userid: string for user login
* @param pass: string for user pass
* @param sex: should be M|F|S (todo make an enum ?)
* @param last_ip:
* @return :
* -1: success
* 0: unregistered id (wrong sex fail to create in db);
* 1: incorrect pass or userid (userid|pass too short or already exist);
* 3: registration limit exceeded;
*/
int login_mmo_auth_new(const char* userid, const char* pass, const char sex, const char* last_ip);
/**
* Check/authentication of a connection.
* @param sd: string (atm:md5key or dbpass)
* @param isServer: string (atm:md5key or dbpass)
* @return :
* -1: success
* 0: unregistered id;
* 1: incorrect pass;
* 2: expired id
* 3: blacklisted (or registration limit exceeded if new acc);
* 5: invalid client_version|hash;
* 6: banned
* x: acc state (TODO document me deeper)
*/
int login_mmo_auth(struct login_session_data* sd, bool isServer);
#endif /* _LOGIN_H_ */

967
src/login/loginchrif.c Normal file
View File

@@ -0,0 +1,967 @@
/**
* @file loginchrif.c
* Module purpose is to handle incoming and outgoing requests with char-server.
* Licensed under GNU GPL.
* For more information, see LICENCE in the main folder.
* @author Athena Dev Teams originally in login.c
* @author rAthena Dev Team
*/
#include "../common/timer.h" //difftick
#include "../common/strlib.h" //safeprint
#include "../common/showmsg.h" //show notice
#include "../common/socket.h" //wfifo session
#include "../common/malloc.h"
#include "account.h"
#include "ipban.h" //ipban_check
#include "login.h"
#include "loginlog.h"
#include "loginclif.h"
#include "loginchrif.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//early declaration
void logchrif_on_disconnect(int id);
/**
* Packet send to all char-servers, except one. (wos: without our self)
* @param sfd: fd to discard sending to
* @param buf: packet to send in form of an array buffer
* @param len: size of packet
* @return : the number of char-serv the packet was sent to
*/
int logchrif_sendallwos(int sfd, uint8* buf, size_t len) {
int i, c;
for( i = 0, c = 0; i < ARRAYLENGTH(ch_server); ++i ) {
int fd = ch_server[i].fd;
if( session_isValid(fd) && fd != sfd ){
WFIFOHEAD(fd,len);
memcpy(WFIFOP(fd,0), buf, len);
WFIFOSET(fd,len);
++c;
}
}
return c;
}
/**
* Timered function to synchronize ip addresses.
* Requesting all char 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
*/
static int logchrif_sync_ip_addresses(int tid, unsigned int tick, int id, intptr_t data) {
uint8 buf[2];
ShowInfo("IP Sync in progress...\n");
WBUFW(buf,0) = 0x2735;
logchrif_sendallwos(-1, buf, 2);
return 0;
}
/// Parsing handlers
/**
* Request from char-server to authenticate an account.
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @param ip: char-serv ip (used for info)
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_reqauth(int fd, int id,char* ip){
if( RFIFOREST(fd) < 23 )
return 0;
else{
struct auth_node* node;
int account_id = RFIFOL(fd,2);
uint32 login_id1 = RFIFOL(fd,6);
uint32 login_id2 = RFIFOL(fd,10);
uint8 sex = RFIFOB(fd,14);
//uint32 ip_ = ntohl(RFIFOL(fd,15));
int request_id = RFIFOL(fd,19);
RFIFOSKIP(fd,23);
node = (struct auth_node*)idb_get(auth_db, account_id);
if( runflag == LOGINSERVER_ST_RUNNING &&
node != NULL &&
node->account_id == account_id &&
node->login_id1 == login_id1 &&
node->login_id2 == login_id2 &&
node->sex == sex_num2str(sex) /*&&
node->ip == ip_*/ ){// found
//ShowStatus("Char-server '%s': authentication of the account %d accepted (ip: %s).\n", server[id].name, account_id, ip);
// send ack
WFIFOHEAD(fd,25);
WFIFOW(fd,0) = 0x2713;
WFIFOL(fd,2) = account_id;
WFIFOL(fd,6) = login_id1;
WFIFOL(fd,10) = login_id2;
WFIFOB(fd,14) = sex;
WFIFOB(fd,15) = 0;// ok
WFIFOL(fd,16) = request_id;
WFIFOL(fd,20) = node->version;
WFIFOB(fd,24) = node->clienttype;
WFIFOSET(fd,25);
// each auth entry can only be used once
idb_remove(auth_db, account_id);
}else{// authentication not found
ShowStatus("Char-server '%s': authentication of the account %d REFUSED (ip: %s).\n", ch_server[id].name, account_id, ip);
WFIFOHEAD(fd,25);
WFIFOW(fd,0) = 0x2713;
WFIFOL(fd,2) = account_id;
WFIFOL(fd,6) = login_id1;
WFIFOL(fd,10) = login_id2;
WFIFOB(fd,14) = sex;
WFIFOB(fd,15) = 1;// auth failed
WFIFOL(fd,16) = request_id;
WFIFOL(fd,20) = 0;
WFIFOB(fd,24) = 0;
WFIFOSET(fd,25);
}
}
return 1;
}
/**
* Receive a request to update user count for char-server identified by id.
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_ackusercount(int fd, int id){
if( RFIFOREST(fd) < 6 )
return 0;
else{
int users = RFIFOL(fd,2);
RFIFOSKIP(fd,6);
// how many users on world? (update)
if( ch_server[id].users != users ){
ShowStatus("set users %s : %d\n", ch_server[id].name, users);
ch_server[id].users = users;
}
}
return 1;
}
/**
* Receive a request from char-server to change e-mail from default "a@a.com".
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @param ip: char-serv ip (used for info)
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_updmail(int fd, int id, char* ip){
if (RFIFOREST(fd) < 46)
return 0;
else{
AccountDB* accounts = login_get_accounts_db();
struct mmo_account acc;
char email[40];
int account_id = RFIFOL(fd,2);
safestrncpy(email, (char*)RFIFOP(fd,6), 40); remove_control_chars(email);
RFIFOSKIP(fd,46);
if( e_mail_check(email) == 0 )
ShowNotice("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - e-mail is invalid (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
else if( !accounts->load_num(accounts, &acc, account_id) || strcmp(acc.email, "a@a.com") == 0 || acc.email[0] == '\0' )
ShowNotice("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - account doesn't exist or e-mail of account isn't default e-mail (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
else{
memcpy(acc.email, email, 40);
ShowNotice("Char-server '%s': Create an e-mail on an account with a default e-mail (account: %d, new e-mail: %s, ip: %s).\n", ch_server[id].name, account_id, email, ip);
// Save
accounts->save(accounts, &acc);
}
}
return 1;
}
/**
* Transmit account data to char_server
* S 2717 aid.W email.40B exp_time.L group_id.B char_slot.B birthdate.11B pincode.5B pincode_change.L bank_vault.L
* isvip.1B char_vip.1B max_billing.1B (tot 79)
* @return -1 : account not found, 1:sucess
*/
int logchrif_send_accdata(int fd, uint32 aid) {
struct mmo_account acc;
time_t expiration_time = 0;
char email[40] = "";
int group_id = 0;
char birthdate[10+1] = "";
char pincode[PINCODE_LENGTH+1];
int bank_vault = 0;
char isvip = false;
uint8 char_slots = MIN_CHARS, char_vip = 0;
memset(pincode,0,PINCODE_LENGTH+1);
AccountDB* accounts = login_get_accounts_db();
if( !accounts->load_num(accounts, &acc, aid) )
return -1;
else {
safestrncpy(email, acc.email, sizeof(email));
expiration_time = acc.expiration_time;
group_id = acc.group_id;
safestrncpy(birthdate, acc.birthdate, sizeof(birthdate));
safestrncpy(pincode, acc.pincode, sizeof(pincode));
bank_vault = acc.bank_vault;
#ifdef VIP_ENABLE
char_vip = login_config.vip_sys.char_increase;
if( acc.vip_time > time(NULL) ) {
isvip = true;
char_slots = login_config.char_per_account + char_vip;
} else
char_slots = login_config.char_per_account;
#endif
}
WFIFOHEAD(fd,79);
WFIFOW(fd,0) = 0x2717;
WFIFOL(fd,2) = aid;
safestrncpy((char*)WFIFOP(fd,6), email, 40);
WFIFOL(fd,46) = (uint32)expiration_time;
WFIFOB(fd,50) = (unsigned char)group_id;
WFIFOB(fd,51) = char_slots;
safestrncpy((char*)WFIFOP(fd,52), birthdate, 10+1);
safestrncpy((char*)WFIFOP(fd,63), pincode, 4+1 );
WFIFOL(fd,68) = (uint32)acc.pincode_change;
WFIFOL(fd,72) = bank_vault;
WFIFOB(fd,76) = isvip;
WFIFOB(fd,77) = char_vip;
WFIFOB(fd,78) = MAX_CHAR_BILLING; //TODO create a config for this
WFIFOSET(fd,79);
return 1;
}
/**
* Transmit vip specific data to char-serv (will be transfered to mapserv)
*/
int logchrif_sendvipdata(int fd, struct mmo_account acc, char isvip, char isgm, int mapfd) {
#ifdef VIP_ENABLE
WFIFOHEAD(fd,19);
WFIFOW(fd,0) = 0x2743;
WFIFOL(fd,2) = acc.account_id;
WFIFOL(fd,6) = (int)acc.vip_time;
WFIFOB(fd,10) = isvip;
WFIFOL(fd,11) = acc.group_id; //new group id
WFIFOL(fd,15) = isgm;
WFIFOL(fd,16) = mapfd; //link to mapserv
WFIFOSET(fd,20);
chrif_send_accdata(fd,acc.account_id); //refresh char with new setting
#endif
return 1;
}
/**
* Receive a request for account data reply by sending all mmo_account information.
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @param ip: char-serv ip (used for info)
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_reqaccdata(int fd, int id, char *ip){
if( RFIFOREST(fd) < 6 )
return 0;
else {
uint32 aid = RFIFOL(fd,2);
RFIFOSKIP(fd,6);
if( logchrif_send_accdata(fd,aid) < 0 )
ShowNotice("Char-server '%s': account %d NOT found (ip: %s).\n", ch_server[id].name, aid, ip);
}
return 1;
}
/**
* Ping request from char-server to send a reply.
* @param fd: fd to parse from (char-serv)
* @return 1 success
*/
int logchrif_parse_keepalive(int fd){
RFIFOSKIP(fd,2);
WFIFOHEAD(fd,2);
WFIFOW(fd,0) = 0x2718;
WFIFOSET(fd,2);
return 1;
}
/**
* Map server send information to change an email of an account via char-server.
* 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @param ip: char-serv ip (used for info)
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_reqchangemail(int fd, int id, char* ip){
if (RFIFOREST(fd) < 86)
return 0;
else{
struct mmo_account acc;
AccountDB* accounts = login_get_accounts_db();
char actual_email[40];
char new_email[40];
int account_id = RFIFOL(fd,2);
safestrncpy(actual_email, (char*)RFIFOP(fd,6), 40);
safestrncpy(new_email, (char*)RFIFOP(fd,46), 40);
RFIFOSKIP(fd, 86);
if( e_mail_check(actual_email) == 0 )
ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
else if( e_mail_check(new_email) == 0 )
ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a invalid new e-mail (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
else if( strcmpi(new_email, "a@a.com") == 0 )
ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
else if( !accounts->load_num(accounts, &acc, account_id) )
ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but account doesn't exist (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
else if( strcmpi(acc.email, actual_email) != 0 )
ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual e-mail is incorrect (account: %d (%s), actual e-mail: %s, proposed e-mail: %s, ip: %s).\n", ch_server[id].name, account_id, acc.userid, acc.email, actual_email, ip);
else{
safestrncpy(acc.email, new_email, 40);
ShowNotice("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s).\n", ch_server[id].name, account_id, acc.userid, new_email, ip);
// Save
accounts->save(accounts, &acc);
}
}
return 1;
}
/**
* Receiving an account state update request from a map-server (relayed via char-server).
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @param ip: char-serv ip (used for info)
* @return 0 not enough info transmitted, 1 success
* TODO seems pretty damn close to logchrif_parse_reqbanacc
*/
int logchrif_parse_requpdaccstate(int fd, int id, char* ip){
if (RFIFOREST(fd) < 10)
return 0;
else{
struct mmo_account acc;
int account_id = RFIFOL(fd,2);
unsigned int state = RFIFOL(fd,6);
RFIFOSKIP(fd,10);
AccountDB* accounts = login_get_accounts_db();
if( !accounts->load_num(accounts, &acc, account_id) )
ShowNotice("Char-server '%s': Error of Status change (account: %d not found, suggested status %d, ip: %s).\n", ch_server[id].name, account_id, state, ip);
else if( acc.state == state )
ShowNotice("Char-server '%s': Error of Status change - actual status is already the good status (account: %d, status %d, ip: %s).\n", ch_server[id].name, account_id, state, ip);
else{
ShowNotice("Char-server '%s': Status change (account: %d, new status %d, ip: %s).\n", ch_server[id].name, account_id, state, ip);
acc.state = state;
// Save
accounts->save(accounts, &acc);
// notify other servers
if (state != 0){
uint8 buf[11];
WBUFW(buf,0) = 0x2731;
WBUFL(buf,2) = account_id;
WBUFB(buf,6) = 0; // 0: change of state, 1: ban
WBUFL(buf,7) = state; // status or final date of a banishment
logchrif_sendallwos(-1, buf, 11);
}
}
}
return 1;
}
/**
* Receiving a ban request from map-server via char-server.
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @param ip: char-serv ip (used for info)
* @return 0 not enough info transmitted, 1 success
* TODO check logchrif_parse_requpdaccstate for possible merge
*/
int logchrif_parse_reqbanacc(int fd, int id, char* ip){
if (RFIFOREST(fd) < 10)
return 0;
else{
struct mmo_account acc;
AccountDB* accounts = login_get_accounts_db();
int account_id = RFIFOL(fd,2);
int timediff = RFIFOL(fd,6);
RFIFOSKIP(fd,10);
if( !accounts->load_num(accounts, &acc, account_id) )
ShowNotice("Char-server '%s': Error of ban request (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
else{
time_t timestamp;
if (acc.unban_time == 0 || acc.unban_time < time(NULL))
timestamp = time(NULL); // new ban
else
timestamp = acc.unban_time; // add to existing ban
timestamp += timediff;
if (timestamp == -1)
ShowNotice("Char-server '%s': Error of ban request (account: %d, invalid date, ip: %s).\n", ch_server[id].name, account_id, ip);
else if( timestamp <= time(NULL) || timestamp == 0 )
ShowNotice("Char-server '%s': Error of ban request (account: %d, new date unbans the account, ip: %s).\n", ch_server[id].name, account_id, ip);
else{
uint8 buf[11];
char tmpstr[24];
timestamp2string(tmpstr, sizeof(tmpstr), timestamp, login_config.date_format);
ShowNotice("Char-server '%s': Ban request (account: %d, new final date of banishment: %d (%s), ip: %s).\n", ch_server[id].name, account_id, timestamp, tmpstr, ip);
acc.unban_time = timestamp;
// Save
accounts->save(accounts, &acc);
WBUFW(buf,0) = 0x2731;
WBUFL(buf,2) = account_id;
WBUFB(buf,6) = 1; // 0: change of status, 1: ban
WBUFL(buf,7) = (uint32)timestamp; // status or final date of a banishment
logchrif_sendallwos(-1, buf, 11);
}
}
}
return 1;
}
/**
* Receiving a sex change request (sex is reversed).
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @param ip: char-serv ip (used for info)
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_reqchgsex(int fd, int id, char* ip){
if( RFIFOREST(fd) < 6 )
return 0;
else{
struct mmo_account acc;
AccountDB* accounts = login_get_accounts_db();
int account_id = RFIFOL(fd,2);
RFIFOSKIP(fd,6);
if( !accounts->load_num(accounts, &acc, account_id) )
ShowNotice("Char-server '%s': Error of sex change (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
else if( acc.sex == 'S' )
ShowNotice("Char-server '%s': Error of sex change - account to change is a Server account (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
else{
unsigned char buf[7];
char sex = ( acc.sex == 'M' ) ? 'F' : 'M'; //Change gender
ShowNotice("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s).\n", ch_server[id].name, account_id, sex, ip);
acc.sex = sex;
// Save
accounts->save(accounts, &acc);
// announce to other servers
WBUFW(buf,0) = 0x2723;
WBUFL(buf,2) = account_id;
WBUFB(buf,6) = sex_str2num(sex);
logchrif_sendallwos(-1, buf, 7);
}
}
return 1;
}
/**
* We receive account_reg2 from a char-server, and we send them to other char-servers.
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @param ip: char-serv ip (used for info)
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_updreg2(int fd, int id, char* ip){
int j;
if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) )
return 0;
else{
struct mmo_account acc;
AccountDB* accounts = login_get_accounts_db();
int account_id = RFIFOL(fd,4);
if( !accounts->load_num(accounts, &acc, account_id) )
ShowStatus("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
else{
int len;
int p;
ShowNotice("char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
for( j = 0, p = 13; j < ACCOUNT_REG2_NUM && p < RFIFOW(fd,2); ++j ){
sscanf((char*)RFIFOP(fd,p), "%31c%n", acc.account_reg2[j].str, &len);
acc.account_reg2[j].str[len]='\0';
p +=len+1; //+1 to skip the '\0' between strings.
sscanf((char*)RFIFOP(fd,p), "%255c%n", acc.account_reg2[j].value, &len);
acc.account_reg2[j].value[len]='\0';
p +=len+1;
remove_control_chars(acc.account_reg2[j].str);
remove_control_chars(acc.account_reg2[j].value);
}
acc.account_reg2_num = j;
// Save
accounts->save(accounts, &acc);
// Sending information towards the other char-servers.
RFIFOW(fd,0) = 0x2729;// reusing read buffer
logchrif_sendallwos(fd, RFIFOP(fd,0), RFIFOW(fd,2));
}
RFIFOSKIP(fd,RFIFOW(fd,2));
}
return 1;
}
/**
* Receiving an unban request from map-server via char-server.
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @param ip: char-serv ip (used for info)
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_requnbanacc(int fd, int id, char* ip){
if( RFIFOREST(fd) < 6 )
return 0;
else{
struct mmo_account acc;
AccountDB* accounts = login_get_accounts_db();
int account_id = RFIFOL(fd,2);
RFIFOSKIP(fd,6);
if( !accounts->load_num(accounts, &acc, account_id) )
ShowNotice("Char-server '%s': Error of UnBan request (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
else if( acc.unban_time == 0 )
ShowNotice("Char-server '%s': Error of UnBan request (account: %d, no change for unban date, ip: %s).\n", ch_server[id].name, account_id, ip);
else{
ShowNotice("Char-server '%s': UnBan request (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
acc.unban_time = 0;
accounts->save(accounts, &acc);
}
}
return 1;
}
/**
* Set account_id to online.
* @author [Wizputer]
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_setacconline(int fd, int id){
if( RFIFOREST(fd) < 6 )
return 0;
login_add_online_user(id, RFIFOL(fd,2));
RFIFOSKIP(fd,6);
return 1;
}
/**
* Set account_id to offline.
* @author [Wizputer]
* @param fd: fd to parse from (char-serv)
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_setaccoffline(int fd){
if( RFIFOREST(fd) < 6 )
return 0;
login_remove_online_user(RFIFOL(fd,2));
RFIFOSKIP(fd,6);
return 1;
}
/**
* Receive list of all online accounts.
* @author [Skotlex]
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_updonlinedb(int fd, int id){
if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
return 0;
else{
uint32 i, users;
online_db->foreach(online_db, login_online_db_setoffline, id); //Set all chars from this char-server offline first
users = RFIFOW(fd,4);
for (i = 0; i < users; i++) {
int aid = RFIFOL(fd,6+i*4);
struct online_login_data *p = idb_ensure(online_db, aid, login_create_online_user);
p->char_server = id;
if (p->waiting_disconnect != INVALID_TIMER){
delete_timer(p->waiting_disconnect, login_waiting_disconnect_timer);
p->waiting_disconnect = INVALID_TIMER;
}
}
RFIFOSKIP(fd,RFIFOW(fd,2));
}
return 1;
}
/**
* Request account_reg2 for a character.
* @param fd: fd to parse from (char-serv)
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_reqacc2reg(int fd){
int j;
if (RFIFOREST(fd) < 10)
return 0;
else{
struct mmo_account acc;
AccountDB* accounts = login_get_accounts_db();
size_t off;
int account_id = RFIFOL(fd,2);
int char_id = RFIFOL(fd,6);
RFIFOSKIP(fd,10);
WFIFOHEAD(fd,ACCOUNT_REG2_NUM*sizeof(struct global_reg));
WFIFOW(fd,0) = 0x2729;
WFIFOL(fd,4) = account_id;
WFIFOL(fd,8) = char_id;
WFIFOB(fd,12) = 1; //Type 1 for Account2 registry
off = 13;
if( accounts->load_num(accounts, &acc, account_id) ){
for( j = 0; j < acc.account_reg2_num; j++ ){
if( acc.account_reg2[j].str[0] != '\0' ){
off += sprintf((char*)WFIFOP(fd,off), "%s", acc.account_reg2[j].str)+1; //We add 1 to consider the '\0' in place.
off += sprintf((char*)WFIFOP(fd,off), "%s", acc.account_reg2[j].value)+1;
}
}
}
WFIFOW(fd,2) = (uint16)off;
WFIFOSET(fd,WFIFOW(fd,2));
}
return 1;
}
/**
* Received new charip from char-serv, update information.
* @param fd: char-serv file descriptor
* @param id: char-serv id
* @return 0 not enough info transmitted, 1 success
*/
int logchrif_parse_updcharip(int fd, int id){
if( RFIFOREST(fd) < 6 )
return 0;
ch_server[id].ip = ntohl(RFIFOL(fd,2));
ShowInfo("Updated IP of Server #%d to %d.%d.%d.%d.\n",id, CONVIP(ch_server[id].ip));
RFIFOSKIP(fd,6);
return 1;
}
/**
* Request to set all accounts offline.
* @param fd: fd to parse from (char-serv)
* @param id: id of char-serv (char-serv)
* @return 1 success
*/
int logchrif_parse_setalloffline(int fd, int id){
ShowInfo("Setting accounts from char-server %d offline.\n", id);
online_db->foreach(online_db, login_online_db_setoffline, id);
RFIFOSKIP(fd,2);
return 1;
}
/**
* Request to change PIN Code for an account.
* @param fd: fd to parse from (char-serv)
* @return 0 fail (packet does not have enough data), 1 success
*/
int logchrif_parse_updpincode(int fd){
if( RFIFOREST(fd) < 11 )
return 0;
else{
struct mmo_account acc;
AccountDB* accounts = login_get_accounts_db();
if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ){
strncpy( acc.pincode, (char*)RFIFOP(fd,6), 5 );
acc.pincode_change = time( NULL );
accounts->save(accounts, &acc);
}
RFIFOSKIP(fd,11);
}
return 1;
}
/**
* PIN Code was incorrectly entered too many times.
* @param fd: fd to parse from (char-serv)
* @return 0 fail (packet does not have enough data), 1 success (continue parsing)
*/
int logchrif_parse_pincode_authfail(int fd){
if( RFIFOREST(fd) < 6 )
return 0;
else{
struct mmo_account acc;
AccountDB* accounts = login_get_accounts_db();
if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ){
struct online_login_data* ld;
ld = (struct online_login_data*)idb_get(online_db,acc.account_id);
if( ld == NULL )
return 0;
login_log( host2ip(acc.last_ip), acc.userid, 100, "PIN Code check failed" );
}
login_remove_online_user(acc.account_id);
RFIFOSKIP(fd,6);
}
return 1;
}
/**
* Request the bank info of login
* @param fd: fd to parse from (char-serv)
* @param id: char serv id
* @param ip: char-serv ip (used for info)
* @return 0 fail (packet does not have enough data), 1 success (continue parsing)
*/
int logchrif_parse_bankvault(int fd, int id, char* ip){
if( RFIFOREST(fd) < 11 )
return 0;
else {
struct mmo_account acc;
int account_id = RFIFOL(fd,2);
char type = RFIFOB(fd,6);
int32 data = RFIFOL(fd,7);
RFIFOSKIP(fd,11);
AccountDB* accounts = login_get_accounts_db();
if( !accounts->load_num(accounts, &acc, account_id) )
ShowNotice("Char-server '%s': Error on banking (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
else{
unsigned char buf[12];
if(type==2){ // upd and Save
acc.bank_vault = data;
accounts->save(accounts, &acc);
WBUFB(buf,10) = 1;
} else {
WBUFB(buf,10) = 0;
}
// announce to other servers
WBUFW(buf,0) = 0x2741;
WBUFL(buf,2) = account_id;
WBUFL(buf,6) = acc.bank_vault;
logchrif_sendallwos(-1, buf, 11);
}
}
return 1;
}
/**
* Received a vip data reqest from char
* type is the query to perform
* &1 : Select info and update old_groupid
* &2 : Update vip time
* @param fd link to charserv
* @return 0 missing data, 1 succeeded
*/
int logchrif_parse_reqvipdata(int fd) {
#ifdef VIP_ENABLE
if( RFIFOREST(fd) < 15 )
return 0;
else { //request vip info
struct mmo_account acc;
int aid = RFIFOL(fd,2);
int8 type = RFIFOB(fd,6);
int32 timediff = RFIFOL(fd,7);
int mapfd = RFIFOL(fd,11);
RFIFOSKIP(fd,15);
if( accounts->load_num(accounts, &acc, aid ) ) {
time_t now = time(NULL);
time_t vip_time = acc.vip_time;
bool isvip = false;
if( acc.group_id > login_config.vip_sys.group ) { //Don't change group if it's higher.
chrif_sendvipdata(fd,acc,false,true,mapfd);
return 1;
}
if( type&2 ) {
if(!vip_time) vip_time = now; //new entry
vip_time += timediff; // set new duration
}
if( now < vip_time ) { //isvip
if(acc.group_id != login_config.vip_sys.group) //only upd this if we're not vip already
acc.old_group = acc.group_id;
acc.group_id = login_config.vip_sys.group;
acc.char_slots = login_config.char_per_account + login_config.vip_sys.char_increase;
isvip = true;
} else { //expired or @vip -xx
vip_time = 0;
if(acc.group_id == login_config.vip_sys.group) //prevent alteration in case account wasn't registered as vip yet
acc.group_id = acc.old_group;
acc.old_group = 0;
acc.char_slots = login_config.char_per_account;
}
acc.vip_time = vip_time;
accounts->save(accounts,&acc);
if( type&1 ) chrif_sendvipdata(fd,acc,isvip,false,mapfd);
}
}
#endif
return 1;
}
/**
* Entry point from char-server to log-server.
* Function that checks incoming command, then splits it to the correct handler.
* @param fd: file descriptor to parse, (link to char-serv)
* @return 0=invalid server,marked for disconnection,unknow packet; 1=success
*/
int logchrif_parse(int fd){
int cid; //char-serv id
uint32 ipl;
char ip[16];
ARR_FIND( 0, ARRAYLENGTH(ch_server), cid, ch_server[cid].fd == fd );
if( cid == ARRAYLENGTH(ch_server) ){// not a char server
ShowDebug("logchrif_parse: Disconnecting invalid session #%d (is not a char-server)\n", fd);
set_eof(fd);
do_close(fd);
return 0;
}
if( session[fd]->flag.eof ){
do_close(fd);
ch_server[cid].fd = -1;
logchrif_on_disconnect(cid);
return 0;
}
ipl = ch_server[cid].ip;
ip2str(ipl, ip);
while( RFIFOREST(fd) >= 2 ){
int next = 1;
uint16 command = RFIFOW(fd,0);
switch( command ){
case 0x2712: next = logchrif_parse_reqauth(fd, cid, ip); break;
case 0x2714: next = logchrif_parse_ackusercount(fd, cid); break;
case 0x2715: next = logchrif_parse_updmail(fd, cid, ip); break;
case 0x2716: next = logchrif_parse_reqaccdata(fd, cid, ip); break;
case 0x2719: next = logchrif_parse_keepalive(fd); break;
case 0x2722: next = logchrif_parse_reqchangemail(fd,cid,ip); break;
case 0x2724: next = logchrif_parse_requpdaccstate(fd,cid,ip); break;
case 0x2725: next = logchrif_parse_reqbanacc(fd,cid,ip); break;
case 0x2727: next = logchrif_parse_reqchgsex(fd,cid,ip); break;
case 0x2728: next = logchrif_parse_updreg2(fd,cid,ip); break;
case 0x272a: next = logchrif_parse_requnbanacc(fd,cid,ip); break;
case 0x272b: next = logchrif_parse_setacconline(fd,cid); break;
case 0x272c: next = logchrif_parse_setaccoffline(fd); break;
case 0x272d: next = logchrif_parse_updonlinedb(fd,cid); break;
case 0x272e: next = logchrif_parse_reqacc2reg(fd); break;
case 0x2736: next = logchrif_parse_updcharip(fd,cid); break;
case 0x2737: next = logchrif_parse_setalloffline(fd,cid); break;
case 0x2738: next = logchrif_parse_updpincode(fd); break;
case 0x2739: next = logchrif_parse_pincode_authfail(fd); break;
case 0x2740: next = logchrif_parse_bankvault(fd,cid,ip); break;
case 0x2742: next = logchrif_parse_reqvipdata(fd); break; //Vip sys
default:
ShowError("logchrif_parse: Unknown packet 0x%x from a char-server! Disconnecting!\n", command);
set_eof(fd);
return 0;
} // switch
if(next==0) return 0; // avoid processing of followup packets (prev was probably incomplete)
} // while
return 1; //or 0
}
/// Constructor destructor and signal handlers
/**
* Initializes a server structure.
* @param id: id of char-serv (should be >0, FIXME)
*/
void logchrif_server_init(int id) {
memset(&ch_server[id], 0, sizeof(ch_server[id]));
ch_server[id].fd = -1;
}
/**
* Destroys a server structure.
* @param id: id of char-serv (should be >0, FIXME)
*/
void logchrif_server_destroy(int id){
if( ch_server[id].fd != -1 ) {
do_close(ch_server[id].fd);
ch_server[id].fd = -1;
}
}
/**
* Resets all the data related to a server.
* Actually destroys then recreates the struct.
* @param id: id of char-serv (should be >0, FIXME)
*/
void logchrif_server_reset(int id) {
online_db->foreach(online_db, login_online_db_setoffline, id); //Set all chars from this char server to offline.
logchrif_server_destroy(id);
logchrif_server_init(id);
}
/**
* Called when the connection to Char Server is disconnected.
* @param id: id of char-serv (should be >0, FIXME)
*/
void logchrif_on_disconnect(int id) {
ShowStatus("Char-server '%s' has disconnected.\n", ch_server[id].name);
logchrif_server_reset(id);
}
/**
* loginchrif constructor
* Initialisation, function called at start of the login-serv.
*/
void do_init_loginchrif(void){
int i;
for( i = 0; i < ARRAYLENGTH(ch_server); ++i )
logchrif_server_init(i);
// add timer to detect ip address change and perform update
if (login_config.ip_sync_interval) {
add_timer_func_list(logchrif_sync_ip_addresses, "sync_ip_addresses");
add_timer_interval(gettick() + login_config.ip_sync_interval, logchrif_sync_ip_addresses, 0, 0, login_config.ip_sync_interval);
}
}
/**
* Signal handler
* This function attempts to properly close the server when an interrupt signal is received.
* current signal catch : SIGTERM, SIGINT
*/
void do_shutdown_loginchrif(void){
int id;
for( id = 0; id < ARRAYLENGTH(ch_server); ++id )
logchrif_server_reset(id);
}
/**
* loginchrif destructor
* dealloc..., function called at exit of the login-serv
*/
void do_final_loginchrif(void){
int i;
for( i = 0; i < ARRAYLENGTH(ch_server); ++i )
logchrif_server_destroy(i);
}

56
src/login/loginchrif.h Normal file
View File

@@ -0,0 +1,56 @@
/**
* @file loginchrif.h
* Module purpose is to handle incoming and outgoing requests with char-server.
* Licensed under GNU GPL.
* For more information, see LICENCE in the main folder.
* @author Athena Dev Teams originally in login.c
* @author rAthena Dev Team
*/
#ifndef LOGINCHRIF_H
#define LOGINCHRIF_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* Entry point from char-server to log-server.
* Function that checks incoming command, then splits it to the correct handler.
* @param fd: file descriptor to parse, (link to char-serv)
* @return 0=invalid server,marked for disconnection,unknow packet; 1=success
*/
int logchrif_parse(int fd);
/**
* Packet send to all char-servers, except one. (wos: without our self)
* @param sfd: fd to discard sending to
* @param buf: packet to send in form of an array buffer
* @param len: size of packet
* @return : the number of char-serv the packet was sent to
*/
int logchrif_sendallwos(int sfd, uint8* buf, size_t len);
/**
* loginchrif constructor
* Initialisation, function called at start of the login-serv.
*/
void do_init_loginchrif(void);
/**
* Signal handler
* This function attempts to properly close the server when an interrupt signal is received.
* current signal catch : SIGTERM, SIGINT
*/
void do_shutdown_loginchrif(void);
/**
* loginchrif destructor
* dealloc..., function called at exit of the login-serv
*/
void do_final_loginchrif(void);
#ifdef __cplusplus
}
#endif
#endif /* LOGINCHRIF_H */

564
src/login/loginclif.c Normal file
View File

@@ -0,0 +1,564 @@
/**
* @file loginclif.c
* Module purpose is to handle incoming and outgoing requests with client.
* Licensed under GNU GPL.
* For more information, see LICENCE in the main folder.
* @author Athena Dev Teams originally in login.c
* @author rAthena Dev Team
*/
#include "../common/timer.h" //difftick
#include "../common/strlib.h" //safeprint
#include "../common/showmsg.h" //show notice
#include "../common/socket.h" //wfifo session
#include "../common/malloc.h"
#include "../common/utils.h"
#include "../common/md5calc.h"
#include "../common/random.h"
#include "account.h"
#include "ipban.h" //ipban_check
#include "login.h"
#include "loginlog.h"
#include "loginclif.h"
#include "loginchrif.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* Transmit auth result to client.
* @param fd: client file desciptor link
* @param result: result to transmit to client, see below
* 1 : Server closed
* 2 : Someone has already logged in with this id
* 8 : already online
* <result>.B (SC_NOTIFY_BAN)
*/
static void logclif_sent_auth_result(int fd,char result){
WFIFOHEAD(fd,3);
WFIFOW(fd,0) = 0x81;
WFIFOB(fd,2) = result;
WFIFOSET(fd,3);
}
/**
* Auth successful, inform client and create a temp auth_node.
* @param sd: player session
*/
static void logclif_auth_ok(struct login_session_data* sd) {
int fd = sd->fd;
uint32 ip = session[fd]->client_addr;
uint8 server_num, n;
uint32 subnet_char_ip;
struct auth_node* node;
int i;
if( runflag != LOGINSERVER_ST_RUNNING ){
// players can only login while running
logclif_sent_auth_result(fd,1); // server closed
return;
}
if( login_config.group_id_to_connect >= 0 && sd->group_id != login_config.group_id_to_connect ) {
ShowStatus("Connection refused: the required group id for connection is %d (account: %s, group: %d).\n", login_config.group_id_to_connect, sd->userid, sd->group_id);
logclif_sent_auth_result(fd,1); // server closed
return;
} else if( login_config.min_group_id_to_connect >= 0 && login_config.group_id_to_connect == -1 && sd->group_id < login_config.min_group_id_to_connect ) {
ShowStatus("Connection refused: the minium group id required for connection is %d (account: %s, group: %d).\n", login_config.min_group_id_to_connect, sd->userid, sd->group_id);
logclif_sent_auth_result(fd,1); // server closed
return;
}
server_num = 0;
for( i = 0; i < ARRAYLENGTH(ch_server); ++i )
if( session_isActive(ch_server[i].fd) )
server_num++;
if( server_num == 0 )
{// if no char-server, don't send void list of servers, just disconnect the player with proper message
ShowStatus("Connection refused: there is no char-server online (account: %s).\n", sd->userid);
logclif_sent_auth_result(fd,1); // server closed
return;
}
{
struct online_login_data* data = (struct online_login_data*)idb_get(online_db, sd->account_id);
if( data )
{// account is already marked as online!
if( data->char_server > -1 )
{// Request char servers to kick this account out. [Skotlex]
uint8 buf[6];
ShowNotice("User '%s' is already online - Rejected.\n", sd->userid);
WBUFW(buf,0) = 0x2734;
WBUFL(buf,2) = sd->account_id;
logchrif_sendallwos(-1, buf, 6);
if( data->waiting_disconnect == INVALID_TIMER )
data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, login_waiting_disconnect_timer, sd->account_id, 0);
logclif_sent_auth_result(fd,8); // 08 = Server still recognizes your last login
return;
}
else
if( data->char_server == -1 )
{// client has authed but did not access char-server yet
// wipe previous session
idb_remove(auth_db, sd->account_id);
login_remove_online_user(sd->account_id);
data = NULL;
}
}
}
login_log(ip, sd->userid, 100, "login ok");
ShowStatus("Connection of the account '%s' accepted.\n", sd->userid);
WFIFOHEAD(fd,47+32*server_num);
WFIFOW(fd,0) = 0x69;
WFIFOW(fd,2) = 47+32*server_num;
WFIFOL(fd,4) = sd->login_id1;
WFIFOL(fd,8) = sd->account_id;
WFIFOL(fd,12) = sd->login_id2;
WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used)
//memcpy(WFIFOP(fd,20), sd->lastlogin, 24); // in old version, that was for name (not more used)
memset(WFIFOP(fd,20), 0, 24);
WFIFOW(fd,44) = 0; // unknown
WFIFOB(fd,46) = sex_str2num(sd->sex);
for( i = 0, n = 0; i < ARRAYLENGTH(ch_server); ++i ) {
if( !session_isValid(ch_server[i].fd) )
continue;
subnet_char_ip = lan_subnetcheck(ip); // Advanced subnet check [LuzZza]
WFIFOL(fd,47+n*32) = htonl((subnet_char_ip) ? subnet_char_ip : ch_server[i].ip);
WFIFOW(fd,47+n*32+4) = ntows(htons(ch_server[i].port)); // [!] LE byte order here [!]
memcpy(WFIFOP(fd,47+n*32+6), ch_server[i].name, 20);
WFIFOW(fd,47+n*32+26) = ch_server[i].users;
WFIFOW(fd,47+n*32+28) = ch_server[i].type;
WFIFOW(fd,47+n*32+30) = ch_server[i].new_;
n++;
}
WFIFOSET(fd,47+32*server_num);
// create temporary auth entry
CREATE(node, struct auth_node, 1);
node->account_id = sd->account_id;
node->login_id1 = sd->login_id1;
node->login_id2 = sd->login_id2;
node->sex = sd->sex;
node->ip = ip;
node->version = sd->version;
node->clienttype = sd->clienttype;
idb_put(auth_db, sd->account_id, node);
{
struct online_login_data* data;
// mark client as 'online'
data = login_add_online_user(-1, sd->account_id);
// schedule deletion of this node
data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, login_waiting_disconnect_timer, sd->account_id, 0);
}
}
/**
* Inform client that auth has failed.
* @param sd: player session
* @param result: nb (msg define in conf)
0 = Unregistered ID
1 = Incorrect Password
2 = This ID is expired
3 = Rejected from Server
4 = You have been blocked by the GM Team
5 = Your Game's EXE file is not the latest version
6 = Your are Prohibited to log in until %s
7 = Server is jammed due to over populated
8 = No more accounts may be connected from this company
9 = MSI_REFUSE_BAN_BY_DBA
10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED
11 = MSI_REFUSE_BAN_BY_GM
12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK
13 = MSI_REFUSE_SELF_LOCK
14 = MSI_REFUSE_NOT_PERMITTED_GROUP
15 = MSI_REFUSE_NOT_PERMITTED_GROUP
99 = This ID has been totally erased
100 = Login information remains at %s
101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information
102 = This account has been temporarily prohibited from login due to a bug-related investigation
103 = This character is being deleted. Login is temporarily unavailable for the time being
104 = This character is being deleted. Login is temporarily unavailable for the time being
default = Unknown Error.
*/
static void logclif_auth_failed(struct login_session_data* sd, int result) {
int fd = sd->fd;
uint32 ip = session[fd]->client_addr;
if (login_config.log_login)
{
if(result >= 0 && result <= 15)
login_log(ip, sd->userid, result, msg_txt(result));
else if(result >= 99 && result <= 104)
login_log(ip, sd->userid, result, msg_txt(result-83)); //-83 offset
else
login_log(ip, sd->userid, result, msg_txt(22)); //unknow error
}
if( result == 1 && login_config.dynamic_pass_failure_ban )
ipban_log(ip); // log failed password attempt
//#if PACKETVER >= 20120000 /* not sure when this started */
if( sd->version >= date2version(20120000) ){ /* not sure when this started */
WFIFOHEAD(fd,26);
WFIFOW(fd,0) = 0x83e;
WFIFOL(fd,2) = result;
if( result != 6 )
memset(WFIFOP(fd,6), '\0', 20);
else { // 6 = Your are Prohibited to log in until %s
struct mmo_account acc;
AccountDB* accounts = login_get_accounts_db();
time_t unban_time = ( accounts->load_str(accounts, &acc, sd->userid) ) ? acc.unban_time : 0;
timestamp2string((char*)WFIFOP(fd,6), 20, unban_time, login_config.date_format);
}
WFIFOSET(fd,26);
}
//#else
else {
WFIFOHEAD(fd,23);
WFIFOW(fd,0) = 0x6a;
WFIFOB(fd,2) = (uint8)result;
if( result != 6 )
memset(WFIFOP(fd,3), '\0', 20);
else { // 6 = Your are Prohibited to log in until %s
struct mmo_account acc;
AccountDB* accounts = login_get_accounts_db();
time_t unban_time = ( accounts->load_str(accounts, &acc, sd->userid) ) ? acc.unban_time : 0;
timestamp2string((char*)WFIFOP(fd,3), 20, unban_time, login_config.date_format);
}
WFIFOSET(fd,23);
}
//#endif
}
/**
* Received a keepalive packet to maintain connection.
* 0x200 <account.userid>.24B.
* @param fd: fd to parse from (client fd)
* @return 0 not enough info transmitted, 1 success
*/
static int logclif_parse_keepalive(int fd){
if (RFIFOREST(fd) < 26)
return 0;
RFIFOSKIP(fd,26);
return 1;
}
/**
* Received a keepalive packet to maintain connection.
* S 0204 <md5 hash>.16B (kRO 2004-05-31aSakexe langtype 0 and 6)
* @param fd: fd to parse from (client fd)
* @return 0 not enough info transmitted, 1 success
*/
static int logclif_parse_updclhash(int fd, struct login_session_data *sd){
if (RFIFOREST(fd) < 18)
return 0;
sd->has_client_hash = 1;
memcpy(sd->client_hash, RFIFOP(fd, 2), 16);
RFIFOSKIP(fd,18);
return 1;
}
/**
* Received a connection request.
* @param fd: file descriptor to parse from (client)
* @param sd: client session
* @param command: packet type sent
* @param ip: ipv4 address (client)
* S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B
* S 0277 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B
* S 02b0 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B <g_isGravityID>.B
* S 01dd <version>.L <username>.24B <password hash>.16B <clienttype>.B
* S 01fa <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.B(index of the connection in the clientinfo file (+10 if the command-line contains "pc"))
* S 027c <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.13B(junk)
* S 0825 <packetsize>.W <version>.L <clienttype>.B <userid>.24B <password>.27B <mac>.17B <ip>.15B <token>.(packetsize - 0x5C)B
* @param fd: fd to parse from (client fd)
* @return 0 failure, 1 success
*/
static int logclif_parse_reqauth(int fd, struct login_session_data *sd, int command, char* ip){
size_t packet_len = RFIFOREST(fd);
if( (command == 0x0064 && packet_len < 55)
|| (command == 0x0277 && packet_len < 84)
|| (command == 0x02b0 && packet_len < 85)
|| (command == 0x01dd && packet_len < 47)
|| (command == 0x01fa && packet_len < 48)
|| (command == 0x027c && packet_len < 60)
|| (command == 0x0825 && (packet_len < 4 || packet_len < RFIFOW(fd, 2))) )
return 0;
else {
int result;
uint32 version;
char username[NAME_LENGTH];
char password[PASSWD_LENGTH];
unsigned char passhash[16];
uint8 clienttype;
bool israwpass = (command==0x0064 || command==0x0277 || command==0x02b0 || command == 0x0825);
// Shinryo: For the time being, just use token as password.
if(command == 0x0825) {
char *accname = (char *)RFIFOP(fd, 9);
char *token = (char *)RFIFOP(fd, 0x5C);
size_t uAccLen = strlen(accname);
size_t uTokenLen = RFIFOREST(fd) - 0x5C;
version = RFIFOL(fd,4);
if(uAccLen > NAME_LENGTH - 1 || uAccLen <= 0 || uTokenLen > NAME_LENGTH - 1 || uTokenLen <= 0)
{
logclif_auth_failed(sd, 3);
return 0;
}
safestrncpy(username, accname, uAccLen + 1);
safestrncpy(password, token, uTokenLen + 1);
clienttype = RFIFOB(fd, 8);
}
else
{
version = RFIFOL(fd,2);
safestrncpy(username, (const char*)RFIFOP(fd,6), NAME_LENGTH);
if( israwpass )
{
safestrncpy(password, (const char*)RFIFOP(fd,30), PASSWD_LENGTH);
clienttype = RFIFOB(fd,54);
}
else
{
memcpy(passhash, RFIFOP(fd,30), 16);
clienttype = RFIFOB(fd,46);
}
}
RFIFOSKIP(fd,RFIFOREST(fd)); // assume no other packet was sent
sd->clienttype = clienttype;
sd->version = version;
safestrncpy(sd->userid, username, NAME_LENGTH);
if( israwpass )
{
ShowStatus("Request for connection of %s (ip: %s) version=%d\n", sd->userid, ip,sd->version);
safestrncpy(sd->passwd, password, NAME_LENGTH);
if( login_config.use_md5_passwds )
MD5_String(sd->passwd, sd->passwd);
sd->passwdenc = 0;
}
else
{
ShowStatus("Request for connection (passwdenc mode) of %s (ip: %s) version=%d\n", sd->userid, ip,sd->version);
bin2hex(sd->passwd, passhash, 16); // raw binary data here!
sd->passwdenc = PASSWORDENC;
}
if( sd->passwdenc != 0 && login_config.use_md5_passwds )
{
logclif_auth_failed(sd, 3); // send "rejected from server"
return 0;
}
result = login_mmo_auth(sd, false);
if( result == -1 )
logclif_auth_ok(sd);
else
logclif_auth_failed(sd, result);
}
return 1;
}
/**
* Client requests an md5key for his session: keys will be generated and sent back.
* @param fd: file descriptor to parse from (client)
* @param sd: client session
* @return 1 success
*/
static int logclif_parse_reqkey(int fd, struct login_session_data *sd){
RFIFOSKIP(fd,2);
{
memset(sd->md5key, '\0', sizeof(sd->md5key));
sd->md5keylen = (uint16)(12 + rnd() % 4);
MD5_Salt(sd->md5keylen, sd->md5key);
WFIFOHEAD(fd,4 + sd->md5keylen);
WFIFOW(fd,0) = 0x01dc;
WFIFOW(fd,2) = 4 + sd->md5keylen;
memcpy(WFIFOP(fd,4), sd->md5key, sd->md5keylen);
WFIFOSET(fd,WFIFOW(fd,2));
}
return 1;
}
/**
* Char-server request to connect to the login-server.
* This is needed to exchange packets.
* @param fd: file descriptor to parse from (client)
* @param sd: client session
* @param ip: ipv4 address (client)
* @return 0 packet received too shirt, 1 success
*/
static int logclif_parse_reqcharconnec(int fd, struct login_session_data *sd, char* ip){
if (RFIFOREST(fd) < 86)
return 0;
{
int result;
char server_name[20];
char message[256];
uint32 server_ip;
uint16 server_port;
uint16 type;
uint16 new_;
safestrncpy(sd->userid, (char*)RFIFOP(fd,2), NAME_LENGTH);
safestrncpy(sd->passwd, (char*)RFIFOP(fd,26), NAME_LENGTH);
if( login_config.use_md5_passwds )
MD5_String(sd->passwd, sd->passwd);
sd->passwdenc = 0;
sd->version = login_config.client_version_to_connect; // hack to skip version check
server_ip = ntohl(RFIFOL(fd,54));
server_port = ntohs(RFIFOW(fd,58));
safestrncpy(server_name, (char*)RFIFOP(fd,60), 20);
type = RFIFOW(fd,82);
new_ = RFIFOW(fd,84);
RFIFOSKIP(fd,86);
ShowInfo("Connection request of the char-server '%s' @ %u.%u.%u.%u:%u (account: '%s', pass: '%s', ip: '%s')\n", server_name, CONVIP(server_ip), server_port, sd->userid, sd->passwd, ip);
sprintf(message, "charserver - %s@%u.%u.%u.%u:%u", server_name, CONVIP(server_ip), server_port);
login_log(session[fd]->client_addr, sd->userid, 100, message);
result = login_mmo_auth(sd, true);
if( runflag == LOGINSERVER_ST_RUNNING &&
result == -1 &&
sd->sex == 'S' &&
sd->account_id >= 0 && sd->account_id < ARRAYLENGTH(ch_server) &&
!session_isValid(ch_server[sd->account_id].fd) )
{
ShowStatus("Connection of the char-server '%s' accepted.\n", server_name);
safestrncpy(ch_server[sd->account_id].name, server_name, sizeof(ch_server[sd->account_id].name));
ch_server[sd->account_id].fd = fd;
ch_server[sd->account_id].ip = server_ip;
ch_server[sd->account_id].port = server_port;
ch_server[sd->account_id].users = 0;
ch_server[sd->account_id].type = type;
ch_server[sd->account_id].new_ = new_;
session[fd]->func_parse = logchrif_parse;
session[fd]->flag.server = 1;
realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
// send connection success
WFIFOHEAD(fd,3);
WFIFOW(fd,0) = 0x2711;
WFIFOB(fd,2) = 0;
WFIFOSET(fd,3);
}
else
{
ShowNotice("Connection of the char-server '%s' REFUSED.\n", server_name);
WFIFOHEAD(fd,3);
WFIFOW(fd,0) = 0x2711;
WFIFOB(fd,2) = 3;
WFIFOSET(fd,3);
}
}
return 1;
}
/**
* Entry point from client to log-server.
* Function that checks incoming command, then splits it to the correct handler.
* @param fd: file descriptor to parse, (link to client)
* @return 0=invalid session,marked for disconnection,unknow packet, banned..; 1=success
*/
int logclif_parse(int fd) {
struct login_session_data* sd = (struct login_session_data*)session[fd]->session_data;
char ip[16];
uint32 ipl = session[fd]->client_addr;
ip2str(ipl, ip);
if( session[fd]->flag.eof )
{
ShowInfo("Closed connection from '"CL_WHITE"%s"CL_RESET"'.\n", ip);
do_close(fd);
return 0;
}
if( sd == NULL )
{
// Perform ip-ban check
if( login_config.ipban && ipban_check(ipl) )
{
ShowStatus("Connection refused: IP isn't authorised (deny/allow, ip: %s).\n", ip);
login_log(ipl, "unknown", -3, "ip banned");
WFIFOHEAD(fd,23);
WFIFOW(fd,0) = 0x6a;
WFIFOB(fd,2) = 3; // 3 = Rejected from Server
WFIFOSET(fd,23);
set_eof(fd);
return 0;
}
// create a session for this new connection
CREATE(session[fd]->session_data, struct login_session_data, 1);
sd = (struct login_session_data*)session[fd]->session_data;
sd->fd = fd;
}
while( RFIFOREST(fd) >= 2 )
{
uint16 command = RFIFOW(fd,0);
int next=1;
switch( command )
{
// New alive packet: used to verify if client is always alive.
case 0x0200: next = logclif_parse_keepalive(fd); break;
// client md5 hash (binary)
case 0x0204: next = logclif_parse_updclhash(fd,sd); break;
// request client login (raw password)
case 0x0064: // S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B
case 0x0277: // S 0277 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B
case 0x02b0: // S 02b0 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B <g_isGravityID>.B
// request client login (md5-hashed password)
case 0x01dd: // S 01dd <version>.L <username>.24B <password hash>.16B <clienttype>.B
case 0x01fa: // S 01fa <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.B(index of the connection in the clientinfo file (+10 if the command-line contains "pc"))
case 0x027c: // S 027c <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.13B(junk)
case 0x0825: // S 0825 <packetsize>.W <version>.L <clienttype>.B <userid>.24B <password>.27B <mac>.17B <ip>.15B <token>.(packetsize - 0x5C)B
next = logclif_parse_reqauth(fd, sd, command, ip);
break;
// Sending request of the coding key
case 0x01db: next = logclif_parse_reqkey(fd, sd); break;
// Connection request of a char-server
case 0x2710: logclif_parse_reqcharconnec(fd,sd, ip); return 0; // processing will continue elsewhere
default:
ShowNotice("Abnormal end of connection (ip: %s): Unknown packet 0x%x\n", ip, command);
set_eof(fd);
return 0;
}
if(next==0) return 0; // avoid processing of followup packets (prev was probably incomplete)
}
return 0;
}
/// Constructor destructor
/**
* Initialize the module.
* Launched at login-serv start, create db or other long scope variable here.
*/
void do_init_loginclif(void){
return;
}
/**
* loginclif destructor
* dealloc..., function called at exit of the login-serv
*/
void do_final_loginclif(void){
return;
}

42
src/login/loginclif.h Normal file
View File

@@ -0,0 +1,42 @@
/**
* @file loginclif.h
* Module purpose is to handle incoming and outgoing requests with client.
* Licensed under GNU GPL.
* For more information, see LICENCE in the main folder.
* @author Athena Dev Teams originally in login.c
* @author rAthena Dev Team
*/
#ifndef _LOGINCLIF_H
#define _LOGINCLIF_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* Entry point from client to log-server.
* Function that checks incoming command, then splits it to the correct handler.
* @param fd: file descriptor to parse, (link to client)
* @return 0=invalid session,marked for disconnection,unknow packet, banned..; 1=success
*/
int logclif_parse(int fd);
/**
* Initialize the module.
* Launched at login-serv start, create db or other long scope variable here.
*/
void do_init_loginclif(void);
/**
* loginclif destructor
* dealloc..., function called at exit of the login-serv
*/
void do_final_loginclif(void);
#ifdef __cplusplus
}
#endif
#endif /* _LOGINCLIF_H */

176
src/login/logincnslif.c Normal file
View File

@@ -0,0 +1,176 @@
/**
* @file logincnslif.c
* Module purpose is to handle incoming and outgoing requests with console.
* Licensed under GNU GPL.
* For more information, see LICENCE in the main folder.
* @author Athena Dev Teams originally in login.c
* @author rAthena Dev Team
*/
#include "../common/mmo.h" //cbasetype + NAME_LENGTH
#include "../common/showmsg.h" //show notice
#include "../common/md5calc.h"
#include "../common/ers.h"
#include "../common/cli.h"
#include "../common/timer.h"
#include "../common/strlib.h"
#include "login.h"
#include "logincnslif.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* Login-server console help: starting option info.
* Do not rename function used as extern.
* @param do_exit: terminate program execution ?
*/
void display_helpscreen(bool do_exit) {
ShowInfo("Usage: %s [options]\n", SERVER_NAME);
ShowInfo("\n");
ShowInfo("Options:\n");
ShowInfo(" -?, -h [--help]\t\tDisplays this help screen.\n");
ShowInfo(" -v [--version]\t\tDisplays the server's version.\n");
ShowInfo(" --run-once\t\t\tCloses server after loading (testing).\n");
ShowInfo(" --login-config <file>\t\tAlternative login-server configuration.\n");
ShowInfo(" --lan-config <file>\t\tAlternative lag configuration.\n");
ShowInfo(" --msg-config <file>\t\tAlternative message configuration.\n");
if( do_exit )
exit(EXIT_SUCCESS);
}
/**
* Read the option specified in command line
* and assign the confs used by the different server.
* @param argc:
* @param argv:
* @return true or Exit on failure.
*/
int logcnslif_get_options(int argc, char ** argv) {
int i = 0;
for (i = 1; i < argc; i++) {
const char* arg = argv[i];
if (arg[0] != '-' && (arg[0] != '/' || arg[1] == '-')) {// -, -- and /
ShowError("Unknown option '%s'.\n", argv[i]);
exit(EXIT_FAILURE);
} else if ((++arg)[0] == '-') {// long option
arg++;
if (strcmp(arg, "help") == 0) {
display_helpscreen(true);
} else if (strcmp(arg, "version") == 0) {
display_versionscreen(true);
} else if (strcmp(arg, "run-once") == 0){ // close the map-server as soon as its done.. for testing [Celest]
runflag = CORE_ST_STOP;
} else if (SERVER_TYPE & (ATHENA_SERVER_LOGIN)) { //login
if (strcmp(arg, "lan-config") == 0) {
if (opt_has_next_value(arg, i, argc)) safestrncpy(login_config.lanconf_name, argv[++i], sizeof(login_config.lanconf_name));
}
if (strcmp(arg, "login-config") == 0) {
if (opt_has_next_value(arg, i, argc)) safestrncpy(login_config.loginconf_name, argv[++i], sizeof(login_config.loginconf_name));
}
if (strcmp(arg, "msg-config") == 0) {
if (opt_has_next_value(arg, i, argc)) safestrncpy(login_config.msgconf_name, argv[++i], sizeof(login_config.msgconf_name));
} else {
ShowError("Unknown option '%s'.\n", argv[i]);
exit(EXIT_FAILURE);
}
}
} else switch (arg[0]) {// short option
case '?':
case 'h':
display_helpscreen(true);
break;
case 'v':
display_versionscreen(true);
break;
default:
ShowError("Unknown option '%s'.\n", argv[i]);
exit(EXIT_FAILURE);
}
}
return 1;
}
/**
* Console Command Parser
* Transmited from command cli.c
* note common name for all serv do not rename (extern in cli)
* @author [Wizputer]
* @param buf: buffer to parse, (from console)
* @return 1=success
*/
int cnslif_parse(const char* buf){
char type[64];
char command[64];
int n=0;
if( ( n = sscanf(buf, "%127[^:]:%255[^\n\r]", type, command) ) < 2 ){
if((n = sscanf(buf, "%63[^\n]", type))<1) return -1; //nothing to do no arg
}
if( n != 2 ){ //end string
ShowNotice("Type: '%s'\n",type);
command[0] = '\0';
}
else
ShowNotice("Type of command: '%s' || Command: '%s'\n",type,command);
if( n == 2 ){
if(strcmpi("server", type) == 0 ){
if( strcmpi("shutdown", command) == 0 || strcmpi("exit", command) == 0 || strcmpi("quit", command) == 0 ){
runflag = 0;
}
else if( strcmpi("alive", command) == 0 || strcmpi("status", command) == 0 )
ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
}
if( strcmpi("create",type) == 0 )
{
char username[NAME_LENGTH], password[NAME_LENGTH], md5password[32+1], sex; //23+1 plaintext 32+1 md5
bool md5 = 0;
if( sscanf(command, "%23s %23s %c", username, password, &sex) < 3 || strnlen(username, sizeof(username)) < 4 || strnlen(password, sizeof(password)) < 1 ){
ShowWarning("Console: Invalid parameters for '%s'. Usage: %s <username> <password> <sex:F/M>\n", type, type);
return 0;
}
if( login_config.use_md5_passwds ){
MD5_String(password,md5password);
md5 = 1;
}
if( login_mmo_auth_new(username,(md5?md5password:password), TOUPPER(sex), "0.0.0.0") != -1 ){
ShowError("Console: Account creation failed.\n");
return 0;
}
ShowStatus("Console: Account '%s' created successfully.\n", username);
}
}
else if( strcmpi("ers_report", type) == 0 ){
ers_report();
}
else if( strcmpi("help", type) == 0 ){
ShowInfo("Available commands:\n");
ShowInfo("\t server:shutdown => Stops the server.\n");
ShowInfo("\t server:alive => Checks if the server is running.\n");
ShowInfo("\t ers_report => Displays database usage.\n");
ShowInfo("\t create:<username> <password> <sex:M|F> => Creates a new account.\n");
}
return 1;
}
/**
* Initialize the module.
* Launched at login-serv start, create db or other long scope variable here.
*/
void do_init_logincnslif(void){
if( login_config.console ) {
add_timer_func_list(parse_console_timer, "parse_console_timer");
add_timer_interval(gettick()+1000, parse_console_timer, 0, 0, 1000); //start in 1s each 1sec
}
}
/**
* Handler to cleanup module, called when login-server stops.
*/
void do_final_logincnslif(void){
return;
}

58
src/login/logincnslif.h Normal file
View File

@@ -0,0 +1,58 @@
/**
* @file logincnslif.h
* Module purpose is to handle incoming and outgoing requests with console.
* Licensed under GNU GPL.
* For more information, see LICENCE in the main folder.
* @author Athena Dev Teams originally in login.c
* @author rAthena Dev Team
*/
#ifndef CONSOLEIF_H
#define CONSOLEIF_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* Console Command Parser
* Transmited from command cli.c
* note common name for all serv do not rename (extern in cli)
* @author [Wizputer]
* @param buf: buffer to parse, (from console)
* @return 1=success
*/
int cnslif_parse(const char* buf);
/**
* Read the option specified in command line
* and assign the confs used by the different server.
* @param argc:
* @param argv:
* @return true or Exit on failure.
*/
int logcnslif_get_options(int argc, char ** argv);
/**
* Login-server console help: starting option info.
* Do not rename function used as extern.
* @param do_exit: terminate program execution ?
*/
void display_helpscreen(bool do_exit);
/**
* Initialize the module.
* Launched at login-serv start, create db or other long scope variable here.
*/
void do_init_logincnslif(void);
/**
* Handler to cleanup module, called when login-server stops.
*/
void do_final_logincnslif(void);
#ifdef __cplusplus
}
#endif
#endif /* CONSOLEIF_H */

View File

@@ -1,5 +1,11 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
/**
* @file loginlog.c
* Module purpose is to register (log) events into a file or sql database.
* Licensed under GNU GPL.
* For more information, see LICENCE in the main folder.
* @author Athena Dev Teams rev < 15k
* @author rAthena Dev Team
*/
#include "../common/cbasetypes.h"
#include "../common/mmo.h"
@@ -13,7 +19,7 @@
static char global_db_hostname[32] = "127.0.0.1";
static uint16 global_db_port = 3306;
static char global_db_username[32] = "ragnarok";
static char global_db_password[32] = "";
static char global_db_password[32] = ""; //empty by default since mysql is empty by default as well
static char global_db_database[32] = "ragnarok";
static char global_codepage[32] = "";
// local sql settings
@@ -29,9 +35,13 @@ static Sql* sql_handle = NULL;
static bool enabled = false;
// Returns the number of failed login attemps by the ip in the last minutes.
unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes)
{
/**
* Get the number of failed login attempts by the ip in the last minutes.
* @param ip: ip to search attempt from
* @param minutes: intervall to search
* @return number of failed attempts
*/
unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes) {
unsigned long failures = 0;
if( !enabled )
@@ -52,11 +62,14 @@ unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes)
}
/*=============================================
* Records an event in the login log
*---------------------------------------------*/
void login_log(uint32 ip, const char* username, int rcode, const char* message)
{
/**
* Records an event in the login log.
* @param ip:
* @param username:
* @param rcode:
* @param message:
*/
void login_log(uint32 ip, const char* username, int rcode, const char* message) {
char esc_username[NAME_LENGTH*2+1];
char esc_message[255*2+1];
int retcode;
@@ -75,60 +88,13 @@ void login_log(uint32 ip, const char* username, int rcode, const char* message)
Sql_ShowDebug(sql_handle);
}
bool loginlog_init(void)
{
const char* username;
const char* password;
const char* hostname;
uint16 port;
const char* database;
const char* codepage;
if( log_db_hostname[0] != '\0' )
{// local settings
username = log_db_username;
password = log_db_password;
hostname = log_db_hostname;
port = log_db_port;
database = log_db_database;
codepage = log_codepage;
}
else
{// global settings
username = global_db_username;
password = global_db_password;
hostname = global_db_hostname;
port = global_db_port;
database = global_db_database;
codepage = global_codepage;
}
sql_handle = Sql_Malloc();
if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
{
Sql_ShowDebug(sql_handle);
Sql_Free(sql_handle);
exit(EXIT_FAILURE);
}
if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
Sql_ShowDebug(sql_handle);
enabled = true;
return true;
}
bool loginlog_final(void)
{
Sql_Free(sql_handle);
sql_handle = NULL;
return true;
}
bool loginlog_config_read(const char* key, const char* value)
{
/**
* 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 loginlog_config_read(const char* key, const char* value) {
const char* signature;
signature = "sql.";
@@ -182,3 +148,67 @@ bool loginlog_config_read(const char* key, const char* value)
return true;
}
/// Constructor destructor
/**
* Initialize the module.
* Launched at login-serv start, create db or other long scope variable here.
* @return true if success else exit execution
*/
bool loginlog_init(void) {
const char* username;
const char* password;
const char* hostname;
uint16 port;
const char* database;
const char* codepage;
if( log_db_hostname[0] != '\0' )
{// local settings
username = log_db_username;
password = log_db_password;
hostname = log_db_hostname;
port = log_db_port;
database = log_db_database;
codepage = log_codepage;
}
else
{// global settings
username = global_db_username;
password = global_db_password;
hostname = global_db_hostname;
port = global_db_port;
database = global_db_database;
codepage = global_codepage;
}
sql_handle = Sql_Malloc();
if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
{
Sql_ShowDebug(sql_handle);
Sql_Free(sql_handle);
exit(EXIT_FAILURE);
}
if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
Sql_ShowDebug(sql_handle);
enabled = true;
return true;
}
/**
* Handler to cleanup module, called when login-server stops.
* Currently closing sql connection to log schema.
* @return true success
*/
bool loginlog_final(void) {
Sql_Free(sql_handle);
sql_handle = NULL;
return true;
}

View File

@@ -1,15 +1,61 @@
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
/**
* @file loginlog.h
* Module purpose is to register (log) events into a file or sql database.
* Licensed under GNU GPL.
* For more information, see LICENCE in the main folder.
* @author Athena Dev Teams rev < 15k
* @author rAthena Dev Team
*/
#ifndef __LOGINLOG_H_INCLUDED__
#define __LOGINLOG_H_INCLUDED__
#ifdef __cplusplus
extern "C" {
#endif
/**
* Get the number of failed login attempts by the ip in the last minutes.
* @param ip: ip to search attempt from
* @param minutes: intervall to search
* @return number of failed attempts
*/
unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes);
/**
* Records an event in the login log.
* @param ip:
* @param username:
* @param rcode:
* @param message:
*/
void login_log(uint32 ip, const char* username, int rcode, const char* message);
bool loginlog_init(void);
bool loginlog_final(void);
/**
* 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 loginlog_config_read(const char* w1, const char* w2);
/**
* Initialize the module.
* Launched at login-serv start, create db or other long scope variable here.
* @return true if success else exit execution
*/
bool loginlog_init(void);
/**
* Handler to cleanup module, called when login-server stops.
* atm closing sql connection to log schema
* @return true success
*/
bool loginlog_final(void);
#ifdef __cplusplus
}
#endif
#endif // __LOGINLOG_H_INCLUDED__

View File

@@ -8,13 +8,19 @@ set( SQL_LOGIN_HEADERS
"${SQL_LOGIN_SOURCE_DIR}/account.h"
"${SQL_LOGIN_SOURCE_DIR}/ipban.h"
"${SQL_LOGIN_SOURCE_DIR}/login.h"
"${SQL_LOGIN_SOURCE_DIR}/loginclif.h"
"${SQL_LOGIN_SOURCE_DIR}/loginchrif.h"
"${SQL_LOGIN_SOURCE_DIR}/logincnslif.h"
"${SQL_LOGIN_SOURCE_DIR}/loginlog.h"
)
set( SQL_LOGIN_SOURCES
"${SQL_LOGIN_SOURCE_DIR}/account_sql.c"
"${SQL_LOGIN_SOURCE_DIR}/ipban_sql.c"
"${SQL_LOGIN_SOURCE_DIR}/account.c"
"${SQL_LOGIN_SOURCE_DIR}/ipban.c"
"${SQL_LOGIN_SOURCE_DIR}/login.c"
"${SQL_LOGIN_SOURCE_DIR}/loginlog_sql.c"
"${SQL_LOGIN_SOURCE_DIR}/loginclif.c"
"${SQL_LOGIN_SOURCE_DIR}/loginchrif.c"
"${SQL_LOGIN_SOURCE_DIR}/logincnslif.c"
"${SQL_LOGIN_SOURCE_DIR}/loginlog.c"
)
set( DEPENDENCIES common_sql )
set( LIBRARIES ${GLOBAL_LIBRARIES} )

View File

@@ -147,6 +147,9 @@
<ClInclude Include="..\src\login\account.h" />
<ClInclude Include="..\src\login\ipban.h" />
<ClInclude Include="..\src\login\login.h" />
<ClInclude Include="..\src\login\loginchrif.h" />
<ClInclude Include="..\src\login\loginclif.h" />
<ClInclude Include="..\src\login\logincnslif.h" />
<ClInclude Include="..\src\login\loginlog.h" />
<ClInclude Include="..\src\common\cbasetypes.h" />
<ClInclude Include="..\src\common\core.h" />
@@ -178,10 +181,13 @@
<ClCompile Include="..\src\common\mutex.c" />
<ClCompile Include="..\src\common\raconf.c" />
<ClCompile Include="..\src\common\thread.c" />
<ClCompile Include="..\src\login\account_sql.c" />
<ClCompile Include="..\src\login\ipban_sql.c" />
<ClCompile Include="..\src\login\account.c" />
<ClCompile Include="..\src\login\ipban.c" />
<ClCompile Include="..\src\login\login.c" />
<ClCompile Include="..\src\login\loginlog_sql.c" />
<ClCompile Include="..\src\login\loginchrif.c" />
<ClCompile Include="..\src\login\loginclif.c" />
<ClCompile Include="..\src\login\logincnslif.c" />
<ClCompile Include="..\src\login\loginlog.c" />
<ClCompile Include="..\src\common\core.c" />
<ClCompile Include="..\src\common\db.c" />
<ClCompile Include="..\src\common\ers.c" />

View File

@@ -151,6 +151,9 @@
<ClInclude Include="..\src\login\ipban.h" />
<ClInclude Include="..\src\login\login.h" />
<ClInclude Include="..\src\login\loginlog.h" />
<ClInclude Include="..\src\login\loginchrif.h" />
<ClInclude Include="..\src\login\loginclif.h" />
<ClInclude Include="..\src\login\logincnslif.h" />
<ClInclude Include="..\src\common\cbasetypes.h" />
<ClInclude Include="..\src\common\core.h" />
<ClInclude Include="..\src\common\conf.h" />
@@ -181,10 +184,13 @@
<ClCompile Include="..\src\common\mutex.c" />
<ClCompile Include="..\src\common\raconf.c" />
<ClCompile Include="..\src\common\thread.c" />
<ClCompile Include="..\src\login\account_sql.c" />
<ClCompile Include="..\src\login\ipban_sql.c" />
<ClCompile Include="..\src\login\account.c" />
<ClCompile Include="..\src\login\ipban.c" />
<ClCompile Include="..\src\login\login.c" />
<ClCompile Include="..\src\login\loginlog_sql.c" />
<ClCompile Include="..\src\login\loginlog.c" />
<ClCompile Include="..\src\login\loginchrif.c" />
<ClCompile Include="..\src\login\loginclif.c" />
<ClCompile Include="..\src\login\logincnslif.c" />
<ClCompile Include="..\src\common\core.c" />
<ClCompile Include="..\src\common\conf.c" />
<ClCompile Include="..\src\common\db.c" />

View File

@@ -211,7 +211,7 @@
>
</File>
<File
RelativePath="..\src\login\account_sql.c"
RelativePath="..\src\login\account.c"
>
</File>
<File
@@ -219,7 +219,7 @@
>
</File>
<File
RelativePath="..\src\login\ipban_sql.c"
RelativePath="..\src\login\ipban.c"
>
</File>
<File
@@ -230,12 +230,36 @@
RelativePath="..\src\login\login.h"
>
</File>
<File
RelativePath="..\src\login\loginchrif.c"
>
</File>
<File
RelativePath="..\src\login\loginchrif.h"
>
</File>
<File
RelativePath="..\src\login\loginclif.c"
>
</File>
<File
RelativePath="..\src\login\loginclif.h"
>
</File>
<File
RelativePath="..\src\login\logincnslif.c"
>
</File>
<File
RelativePath="..\src\login\logincnslif.h"
>
</File>
<File
RelativePath="..\src\login\loginlog.h"
>
</File>
<File
RelativePath="..\src\login\loginlog_sql.c"
RelativePath="..\src\login\loginlog.c"
>
</File>
</Filter>