-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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
//------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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__
|
||||
|
||||
1884
src/login/login.c
1884
src/login/login.c
File diff suppressed because it is too large
Load Diff
@@ -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
967
src/login/loginchrif.c
Normal 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
56
src/login/loginchrif.h
Normal 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
564
src/login/loginclif.c
Normal 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
42
src/login/loginclif.h
Normal 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
176
src/login/logincnslif.c
Normal 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
58
src/login/logincnslif.h
Normal 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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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__
|
||||
|
||||
@@ -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} )
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user