123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877 |
- /**
- * @file login.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
- */
- #pragma warning(disable:4800)
- #include "../common/core.h"
- #include "../common/db.h"
- #include "../common/malloc.h"
- #include "../common/md5calc.h"
- #include "../common/random.h"
- #include "../common/showmsg.h"
- #include "../common/socket.h" //ip2str
- #include "../common/strlib.h"
- #include "../common/timer.h"
- #include "../common/msg_conf.h"
- #include "../common/cli.h"
- #include "../common/utils.h"
- #include "../common/mmo.h"
- #include "../config/core.h"
- #include "account.h"
- #include "ipban.h"
- #include "login.h"
- #include "loginlog.h"
- #include "loginclif.h"
- #include "loginchrif.h"
- #include "logincnslif.h"
- #include <stdlib.h>
- #define LOGIN_MAX_MSG 30 /// Max number predefined in msg_conf
- static char* msg_table[LOGIN_MAX_MSG]; /// Login Server messages_conf
- //definition of exported var declared in .h
- struct mmo_char_server ch_server[MAX_SERVERS]; /// char server data
- struct Login_Config login_config; /// Configuration of login-serv
- DBMap* online_db;
- DBMap* auth_db;
- // account database
- AccountDB* accounts = NULL;
- // Advanced subnet check [LuzZza]
- struct s_subnet {
- uint32 mask;
- uint32 char_ip;
- uint32 map_ip;
- } subnet[16];
- int subnet_count = 0; //number of subnet config
- int login_fd; // login server file descriptor socket
- //early declaration
- bool login_check_password(const char* md5key, int passwdenc, const char* passwd, const char* refpass);
- ///Accessors
- AccountDB* login_get_accounts_db(void){
- return accounts;
- }
- // Console Command Parser [Wizputer]
- //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);
- }
- /**
- * 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) {
- struct online_login_data* p;
- CREATE(p, struct online_login_data, 1);
- p->account_id = key.i;
- p->char_server = -1;
- p->waiting_disconnect = INVALID_TIMER;
- return db_ptr2data(p);
- }
- /**
- * Receive info from char-serv that this user is online
- * This function will start a timer to recheck if that user still online
- * @param char_server : Serv id where account_id is connected
- * @param account_id : aid connected
- * @return the new online_login_data for that user
- */
- struct online_login_data* login_add_online_user(int char_server, uint32 account_id){
- struct online_login_data* p;
- p = (struct online_login_data*)idb_ensure(online_db, account_id, login_create_online_user);
- p->char_server = char_server;
- if( p->waiting_disconnect != INVALID_TIMER ) {
- delete_timer(p->waiting_disconnect, login_waiting_disconnect_timer);
- p->waiting_disconnect = INVALID_TIMER;
- }
- return p;
- }
- /**
- * Received info from char serv that the account_id is now offline
- * remove the user from online_db
- * Checking if user was already scheduled for deletion, and remove that timer if found.
- * @param account_id : aid to remove from db
- */
- void login_remove_online_user(uint32 account_id) {
- struct online_login_data* p;
- p = (struct online_login_data*)idb_get(online_db, account_id);
- if( p == NULL )
- return;
- if( p->waiting_disconnect != INVALID_TIMER )
- delete_timer(p->waiting_disconnect, login_waiting_disconnect_timer);
- idb_remove(online_db, 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) {
- struct online_login_data* p = (struct online_login_data*)idb_get(online_db, id);
- if( p != NULL && p->waiting_disconnect == tid && p->account_id == id ){
- p->waiting_disconnect = INVALID_TIMER;
- login_remove_online_user(id);
- idb_remove(auth_db, id);
- }
- return 0;
- }
- /**
- * 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) {
- struct online_login_data* p = (struct online_login_data*)db_data2ptr(data);
- int server = va_arg(ap, int);
- if( server == -1 ) {
- p->char_server = -1;
- if( p->waiting_disconnect != INVALID_TIMER ) {
- delete_timer(p->waiting_disconnect, login_waiting_disconnect_timer);
- p->waiting_disconnect = INVALID_TIMER;
- }
- }
- else if( p->char_server == server )
- p->char_server = -2; //Char server disconnected.
- return 0;
- }
- /**
- * Sub function of login_online_data_cleanup.
- * Checking if all users in db are still connected to a char-server, and remove them if they aren't.
- * @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
- */
- static int login_online_data_cleanup_sub(DBKey key, DBData *data, va_list ap) {
- struct online_login_data *character= (struct online_login_data*)db_data2ptr(data);
- if (character->char_server == -2) //Unknown server.. set them offline
- login_remove_online_user(character->account_id);
- return 0;
- }
- /**
- * Timered function to check if user is still connected.
- * Launches every 600s by default.
- * @param tid: timer id
- * @param tick: tick of execution
- * @param id: unused
- * @param data: unused
- * @return : 0
- */
- static int login_online_data_cleanup(int tid, unsigned int tick, int id, intptr_t data) {
- online_db->foreach(online_db, login_online_data_cleanup_sub);
- return 0;
- }
- /**
- * 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) {
- static int num_regs = 0; // registration counter
- static unsigned int new_reg_tick = 0;
- unsigned int tick = gettick();
- struct mmo_account acc;
- //Account Registration Flood Protection by [Kevin]
- if( new_reg_tick == 0 )
- new_reg_tick = gettick();
- if( DIFF_TICK(tick, new_reg_tick) < 0 && num_regs >= login_config.allowed_regs ) {
- ShowNotice("Account registration denied (registration limit exceeded)\n");
- return 3;
- }
- if( login_config.new_acc_length_limit && ( strlen(userid) < 4 || strlen(pass) < 4 ) )
- return 1;
- // check for invalid inputs
- if( sex != 'M' && sex != 'F' )
- return 0; // 0 = Unregistered ID
- // check if the account doesn't exist already
- if( accounts->load_str(accounts, &acc, userid) ) {
- ShowNotice("Attempt of creation of an already existant account (account: %s, sex: %c)\n", userid, sex);
- return 1; // 1 = Incorrect Password
- }
- memset(&acc, '\0', sizeof(acc));
- acc.account_id = -1; // assigned by account db
- safestrncpy(acc.userid, userid, sizeof(acc.userid));
- safestrncpy(acc.pass, pass, sizeof(acc.pass));
- acc.sex = sex;
- safestrncpy(acc.email, "a@a.com", sizeof(acc.email));
- acc.expiration_time = ( login_config.start_limited_time != -1 ) ? time(NULL) + login_config.start_limited_time : 0;
- safestrncpy(acc.lastlogin, "", sizeof(acc.lastlogin));
- safestrncpy(acc.last_ip, last_ip, sizeof(acc.last_ip));
- safestrncpy(acc.birthdate, "", sizeof(acc.birthdate));
- safestrncpy(acc.pincode, "", sizeof(acc.pincode));
- acc.pincode_change = 0;
- acc.char_slots = MIN_CHARS;
- #ifdef VIP_ENABLE
- acc.vip_time = 0;
- acc.old_group = 0;
- #endif
- if( !accounts->create(accounts, &acc) )
- return 0;
- ShowNotice("Account creation (account %s, id: %d, sex: %c)\n", acc.userid, acc.account_id, acc.sex);
- if( DIFF_TICK(tick, new_reg_tick) > 0 ) {// Update the registration check.
- num_regs = 0;
- new_reg_tick = tick + login_config.time_allowed*1000;
- }
- ++num_regs;
- return -1;
- }
- /**
- * 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) {
- struct mmo_account acc;
- int len;
- char ip[16];
- ip2str(session[sd->fd]->client_addr, ip);
- // DNS Blacklist check
- if( login_config.use_dnsbl ) {
- char r_ip[16];
- char ip_dnsbl[256];
- char* dnsbl_serv;
- uint8* sin_addr = (uint8*)&session[sd->fd]->client_addr;
- sprintf(r_ip, "%u.%u.%u.%u", sin_addr[0], sin_addr[1], sin_addr[2], sin_addr[3]);
- for( dnsbl_serv = strtok(login_config.dnsbl_servs,","); dnsbl_serv != NULL; dnsbl_serv = strtok(NULL,",") ) {
- sprintf(ip_dnsbl, "%s.%s", r_ip, trim(dnsbl_serv));
- if( host2ip(ip_dnsbl) ) {
- ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n", r_ip);
- return 3;
- }
- }
- }
- len = strnlen(sd->userid, NAME_LENGTH);
- // Account creation with _M/_F
- if( login_config.new_account_flag ) {
- if( len > 2 && strnlen(sd->passwd, NAME_LENGTH) > 0 && // valid user and password lengths
- sd->passwdenc == 0 && // unencoded password
- sd->userid[len-2] == '_' && memchr("FfMm", sd->userid[len-1], 4) ) // _M/_F suffix
- {
- int result;
- // remove the _M/_F suffix
- len -= 2;
- sd->userid[len] = '\0';
- result = login_mmo_auth_new(sd->userid, sd->passwd, TOUPPER(sd->userid[len+1]), ip);
- if( result != -1 )
- return result;// Failed to make account. [Skotlex].
- }
- }
- if( !accounts->load_str(accounts, &acc, sd->userid) ) {
- ShowNotice("Unknown account (account: %s, ip: %s)\n", sd->userid, ip);
- return 0; // 0 = Unregistered ID
- }
- if( !login_check_password(sd->md5key, sd->passwdenc, sd->passwd, acc.pass) ) {
- ShowNotice("Invalid password (account: '%s', ip: %s)\n", sd->userid, ip);
- return 1; // 1 = Incorrect Password
- }
- if( acc.expiration_time != 0 && acc.expiration_time < time(NULL) ) {
- ShowNotice("Connection refused (account: %s, expired ID, ip: %s)\n", sd->userid, ip);
- return 2; // 2 = This ID is expired
- }
- if( acc.unban_time != 0 && acc.unban_time > time(NULL) ) {
- char tmpstr[24];
- timestamp2string(tmpstr, sizeof(tmpstr), acc.unban_time, login_config.date_format);
- ShowNotice("Connection refused (account: %s, banned until %s, ip: %s)\n", sd->userid, tmpstr, ip);
- return 6; // 6 = Your are Prohibited to log in until %s
- }
- if( acc.state != 0 ) {
- ShowNotice("Connection refused (account: %s, state: %d, ip: %s)\n", sd->userid, acc.state, ip);
- return acc.state - 1;
- }
- if( login_config.client_hash_check && !isServer ) {
- struct client_hash_node *node = NULL;
- bool match = false;
- for( node = login_config.client_hash_nodes; node; node = node->next ) {
- if( acc.group_id < node->group_id )
- continue;
- if( *node->hash == '\0' // Allowed to login without hash
- || (sd->has_client_hash && memcmp(node->hash, sd->client_hash, 16) == 0 ) // Correct hash
- ) {
- match = true;
- break;
- }
- }
- if( !match ) {
- char smd5[33];
- int i;
- if( !sd->has_client_hash ) {
- ShowNotice("Client didn't send client hash (account: %s, ip: %s)\n", sd->userid, acc.state, ip);
- return 5;
- }
- for( i = 0; i < 16; i++ )
- sprintf(&smd5[i * 2], "%02x", sd->client_hash[i]);
- ShowNotice("Invalid client hash (account: %s, sent md5: %d, ip: %s)\n", sd->userid, smd5, ip);
- return 5;
- }
- }
- ShowNotice("Authentication accepted (account: %s, id: %d, ip: %s)\n", sd->userid, acc.account_id, ip);
- // update session data
- sd->account_id = acc.account_id;
- sd->login_id1 = rnd() + 1;
- sd->login_id2 = rnd() + 1;
- safestrncpy(sd->lastlogin, acc.lastlogin, sizeof(sd->lastlogin));
- sd->sex = acc.sex;
- sd->group_id = acc.group_id;
- // update account data
- timestamp2string(acc.lastlogin, sizeof(acc.lastlogin), time(NULL), "%Y-%m-%d %H:%M:%S");
- safestrncpy(acc.last_ip, ip, sizeof(acc.last_ip));
- acc.unban_time = 0;
- acc.logincount++;
- accounts->save(accounts, &acc);
- if( sd->sex != 'S' && sd->account_id < START_ACCOUNT_NUM )
- ShowWarning("Account %s has account id %d! Account IDs must be over %d to work properly!\n", sd->userid, sd->account_id, START_ACCOUNT_NUM);
- return -1; // account OK
- }
- /**
- * Sub function of login_check_password.
- * Checking if password matches the one in db hashed with client md5key.
- * Test if(md5(str1+str2)==passwd).
- * @param str1: string (atm:md5key or dbpass)
- * @param str2: string (atm:md5key or dbpass)
- * @param passwd: pass to check
- * @return true if matching else false
- */
- bool login_check_encrypted(const char* str1, const char* str2, const char* passwd) {
- char tmpstr[64+1], md5str[32+1];
- safesnprintf(tmpstr, sizeof(tmpstr), "%s%s", str1, str2);
- MD5_String(tmpstr, md5str);
- return (0==strcmp(passwd, md5str));
- }
- /**
- * Verify if a password is correct.
- * @param md5key: md5key of client
- * @param passwdenc: encode key of client
- * @param passwd: pass to check
- * @param refpass: pass register in db
- * @return true if matching else false
- */
- bool login_check_password(const char* md5key, int passwdenc, const char* passwd, const char* refpass) {
- if(passwdenc == 0){
- return (0==strcmp(passwd, refpass));
- }
- else {
- // password mode set to 1 -> md5(md5key, refpass) enable with <passwordencrypt></passwordencrypt>
- // password mode set to 2 -> md5(refpass, md5key) enable with <passwordencrypt2></passwordencrypt2>
- return ((passwdenc&0x01) && login_check_encrypted(md5key, refpass, passwd)) ||
- ((passwdenc&0x02) && login_check_encrypted(refpass, md5key, passwd));
- }
- }
- /**
- * 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) {
- int i;
- ARR_FIND( 0, subnet_count, i, (subnet[i].char_ip & subnet[i].mask) == (ip & subnet[i].mask) );
- return ( i < subnet_count ) ? subnet[i].char_ip : 0;
- }
- /// Msg_conf tayloring
- int login_msg_config_read(char *cfgName){
- return _msg_config_read(cfgName,LOGIN_MAX_MSG,msg_table);
- }
- const char* login_msg_txt(int msg_number){
- return _msg_txt(msg_number,LOGIN_MAX_MSG,msg_table);
- }
- void login_do_final_msg(void){
- _do_final_msg(LOGIN_MAX_MSG,msg_table);
- }
- /// Set and read Configurations
- /**
- * Reading Lan Support configuration.
- * @param lancfgName: Name of the lan configuration (could be fullpath)
- * @return 0:success, 1:failure (file not found|readable)
- */
- int login_lan_config_read(const char *lancfgName) {
- FILE *fp;
- int line_num = 0, s_subnet=ARRAYLENGTH(subnet);
- char line[1024], w1[64], w2[64], w3[64], w4[64];
- if((fp = fopen(lancfgName, "r")) == NULL) {
- ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName);
- return 1;
- }
- while(fgets(line, sizeof(line), fp))
- {
- line_num++;
- if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n')
- continue;
- if(sscanf(line,"%63[^:]: %63[^:]:%63[^:]:%63[^\r\n]", w1, w2, w3, w4) != 4)
- {
- ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num);
- continue;
- }
- if( strcmpi(w1, "subnet") == 0 ){
- if(subnet_count>=s_subnet) { //We skip instead of break in case we want to add other conf in that file.
- ShowError("%s: Too many subnets defined, skipping line %d...\n", lancfgName, line_num);
- continue;
- }
- subnet[subnet_count].mask = str2ip(w2);
- subnet[subnet_count].char_ip = str2ip(w3);
- subnet[subnet_count].map_ip = str2ip(w4);
- if( (subnet[subnet_count].char_ip & subnet[subnet_count].mask) != (subnet[subnet_count].map_ip & subnet[subnet_count].mask) )
- {
- ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4);
- continue;
- }
- subnet_count++;
- }
- }
- if( subnet_count > 1 ) /* only useful if there is more than 1 available */
- ShowStatus("Read information about %d subnetworks.\n", subnet_count);
- fclose(fp);
- return 0;
- }
- /**
- * Reading main configuration file.
- * @param cfgName: Name of the configuration (could be fullpath)
- * @param normal: Config read normally when server started
- * @return True:success, Fals:failure (file not found|readable)
- */
- bool login_config_read(const char* cfgName, bool normal) {
- char line[1024], w1[32], w2[1024];
- FILE* fp = fopen(cfgName, "r");
- if (fp == NULL) {
- ShowError("Configuration file (%s) not found.\n", cfgName);
- return false;
- }
- while(fgets(line, sizeof(line), fp)) {
- if (line[0] == '/' && line[1] == '/')
- continue;
- if (sscanf(line, "%31[^:]: %1023[^\r\n]", w1, w2) < 2)
- continue;
- // Config that loaded only when server started, not by reloading config file
- if (normal) {
- if( !strcmpi(w1, "bind_ip") ) {
- login_config.login_ip = host2ip(w2);
- if( login_config.login_ip ) {
- char ip_str[16];
- ShowStatus("Login server binding IP address : %s -> %s\n", w2, ip2str(login_config.login_ip, ip_str));
- }
- }
- else if( !strcmpi(w1, "login_port") )
- login_config.login_port = (uint16)atoi(w2);
- else if(!strcmpi(w1, "console"))
- login_config.console = (bool)config_switch(w2);
- }
- if(!strcmpi(w1,"timestamp_format"))
- safestrncpy(timestamp_format, w2, 20);
- else if(strcmpi(w1,"db_path")==0)
- safestrncpy(db_path, w2, ARRAYLENGTH(db_path));
- else if(!strcmpi(w1,"stdout_with_ansisequence"))
- stdout_with_ansisequence = config_switch(w2);
- else if(!strcmpi(w1,"console_silent")) {
- msg_silent = atoi(w2);
- if( msg_silent ) /* only bother if we actually have this enabled */
- ShowInfo("Console Silent Setting: %d\n", atoi(w2));
- }
- else if (strcmpi(w1, "console_msg_log") == 0)
- console_msg_log = atoi(w2);
- else if (strcmpi(w1, "console_log_filepath") == 0)
- safestrncpy(console_log_filepath, w2, sizeof(console_log_filepath));
- else if(!strcmpi(w1, "log_login"))
- login_config.log_login = (bool)config_switch(w2);
- else if(!strcmpi(w1, "new_account"))
- login_config.new_account_flag = (bool)config_switch(w2);
- else if(!strcmpi(w1, "new_acc_length_limit"))
- login_config.new_acc_length_limit = (bool)config_switch(w2);
- else if(!strcmpi(w1, "start_limited_time"))
- login_config.start_limited_time = atoi(w2);
- else if(!strcmpi(w1, "use_MD5_passwords"))
- login_config.use_md5_passwds = (bool)config_switch(w2);
- else if(!strcmpi(w1, "group_id_to_connect"))
- login_config.group_id_to_connect = atoi(w2);
- else if(!strcmpi(w1, "min_group_id_to_connect"))
- login_config.min_group_id_to_connect = atoi(w2);
- else if(!strcmpi(w1, "date_format"))
- safestrncpy(login_config.date_format, w2, sizeof(login_config.date_format));
- else if(!strcmpi(w1, "allowed_regs")) //account flood protection system
- login_config.allowed_regs = atoi(w2);
- else if(!strcmpi(w1, "time_allowed"))
- login_config.time_allowed = atoi(w2);
- else if(!strcmpi(w1, "use_dnsbl"))
- login_config.use_dnsbl = (bool)config_switch(w2);
- else if(!strcmpi(w1, "dnsbl_servers"))
- safestrncpy(login_config.dnsbl_servs, w2, sizeof(login_config.dnsbl_servs));
- else if(!strcmpi(w1, "ipban_cleanup_interval"))
- login_config.ipban_cleanup_interval = (unsigned int)atoi(w2);
- else if(!strcmpi(w1, "ip_sync_interval"))
- login_config.ip_sync_interval = (unsigned int)1000*60*atoi(w2); //w2 comes in minutes.
- else if(!strcmpi(w1, "client_hash_check"))
- login_config.client_hash_check = config_switch(w2);
- else if(!strcmpi(w1, "client_hash")) {
- int group = 0;
- char md5[33];
- if (sscanf(w2, "%3d, %32s", &group, md5) == 2) {
- struct client_hash_node *nnode;
- CREATE(nnode, struct client_hash_node, 1);
- if (strcmpi(md5, "disabled") == 0) {
- nnode->hash[0] = '\0';
- } else {
- int i;
- for (i = 0; i < 32; i += 2) {
- char buf[3];
- unsigned int byte;
- memcpy(buf, &md5[i], 2);
- buf[2] = 0;
- sscanf(buf, "%2x", &byte);
- nnode->hash[i / 2] = (uint8)(byte & 0xFF);
- }
- }
- nnode->group_id = group;
- nnode->next = login_config.client_hash_nodes;
- login_config.client_hash_nodes = nnode;
- }
- } else if(strcmpi(w1, "chars_per_account") == 0) { //maxchars per account [Sirius]
- login_config.char_per_account = atoi(w2);
- if( login_config.char_per_account <= 0 || login_config.char_per_account > MAX_CHARS ) {
- if( login_config.char_per_account > MAX_CHARS ) {
- ShowWarning("Max chars per account '%d' exceeded limit. Defaulting to '%d'.\n", login_config.char_per_account, MAX_CHARS);
- login_config.char_per_account = MAX_CHARS;
- }
- login_config.char_per_account = MIN_CHARS;
- }
- }
- #ifdef VIP_ENABLE
- else if(strcmpi(w1,"vip_group")==0)
- login_config.vip_sys.group = cap_value(atoi(w2),0,99);
- else if(strcmpi(w1,"vip_char_increase")==0) {
- if (atoi(w2) == -1)
- login_config.vip_sys.char_increase = MAX_CHAR_VIP;
- else
- login_config.vip_sys.char_increase = atoi(w2);
- if (login_config.vip_sys.char_increase > (unsigned int) MAX_CHARS-login_config.char_per_account) {
- ShowWarning("vip_char_increase too high, can only go up to %d, according to your char_per_account config %d\n",
- MAX_CHARS-login_config.char_per_account,login_config.char_per_account);
- login_config.vip_sys.char_increase = MAX_CHARS-login_config.char_per_account;
- }
- }
- #endif
- else if(!strcmpi(w1, "import"))
- login_config_read(w2, normal);
- else {// try the account engines
- if (!normal)
- continue;
- if (accounts && accounts->set_property(accounts, w1, w2))
- continue;
- // try others
- ipban_config_read(w1, w2);
- loginlog_config_read(w1, w2);
- }
- }
- fclose(fp);
- ShowInfo("Finished reading %s.\n", cfgName);
- return true;
- }
- /**
- * Init login-serv default configuration.
- */
- void login_set_defaults() {
- login_config.login_ip = INADDR_ANY;
- login_config.login_port = 6900;
- login_config.ipban_cleanup_interval = 60;
- login_config.ip_sync_interval = 0;
- login_config.log_login = true;
- safestrncpy(login_config.date_format, "%Y-%m-%d %H:%M:%S", sizeof(login_config.date_format));
- login_config.console = false;
- login_config.new_account_flag = true;
- login_config.new_acc_length_limit = true;
- login_config.use_md5_passwds = false;
- login_config.group_id_to_connect = -1;
- login_config.min_group_id_to_connect = -1;
- login_config.ipban = true;
- login_config.dynamic_pass_failure_ban = true;
- login_config.dynamic_pass_failure_ban_interval = 5;
- login_config.dynamic_pass_failure_ban_limit = 7;
- login_config.dynamic_pass_failure_ban_duration = 5;
- login_config.use_dnsbl = false;
- safestrncpy(login_config.dnsbl_servs, "", sizeof(login_config.dnsbl_servs));
- login_config.allowed_regs = 1;
- login_config.time_allowed = 10; //in second
- login_config.client_hash_check = 0;
- login_config.client_hash_nodes = NULL;
- login_config.char_per_account = MAX_CHARS - MAX_CHAR_VIP - MAX_CHAR_BILLING;
- #ifdef VIP_ENABLE
- login_config.vip_sys.char_increase = MAX_CHAR_VIP;
- login_config.vip_sys.group = 5;
- #endif
- //other default conf
- safestrncpy(login_config.loginconf_name, "conf/login_athena.conf", sizeof(login_config.loginconf_name));
- safestrncpy(login_config.lanconf_name, "conf/subnet_athena.conf", sizeof(login_config.lanconf_name));
- safestrncpy(login_config.msgconf_name, "conf/msg_conf/login_msg.conf", sizeof(login_config.msgconf_name));
- }
- /// Constructor destructor and signal handlers
- /**
- * Login-serv destructor
- * dealloc..., function called at exit of the login-serv
- */
- void do_final(void) {
- struct client_hash_node *hn = login_config.client_hash_nodes;
- AccountDB* db = accounts;
- while (hn)
- {
- struct client_hash_node *tmp = hn;
- hn = hn->next;
- aFree(tmp);
- }
- login_log(0, "login server", 100, "login server shutdown");
- ShowStatus("Terminating...\n");
- if( login_config.log_login )
- loginlog_final();
- do_final_msg();
- ipban_final();
- do_final_loginclif();
- do_final_logincnslif();
- if (db) { // destroy account engine
- db->destroy(db);
- db = NULL;
- }
- accounts = NULL; // destroyed in account_engine
- online_db->destroy(online_db, NULL);
- auth_db->destroy(auth_db, NULL);
- do_final_loginchrif();
- if( login_fd != -1 )
- {
- do_close(login_fd);
- login_fd = -1;
- }
- ShowStatus("Finished.\n");
- }
- /**
- * Signal handler
- * This function attempts to properly close the server when an interrupt signal is received.
- * current signal catch : SIGTERM, SIGINT
- */
- void do_shutdown(void) {
- if( runflag != LOGINSERVER_ST_SHUTDOWN ) {
- runflag = LOGINSERVER_ST_SHUTDOWN;
- ShowStatus("Shutting down...\n");
- // TODO proper shutdown procedure; kick all characters, wait for acks, ... [FlavioJS]
- do_shutdown_loginchrif();
- flush_fifos();
- runflag = CORE_ST_STOP;
- }
- }
- /**
- * Signal handler
- * Function called when the server has received a crash signal.
- * current signal catch : SIGSEGV, SIGFPE
- */
- void do_abort(void) {
- }
- // Is this still used ??
- void set_server_type(void) {
- SERVER_TYPE = ATHENA_SERVER_LOGIN;
- }
- /**
- * Login serv constructor
- * Initialisation, function called at start of the login-serv.
- * @param argc : number of argument from main()
- * @param argv : arguments values from main()
- * @return 0 everything ok else stopping programme execution.
- */
- int do_init(int argc, char** argv) {
- runflag = LOGINSERVER_ST_STARTING;
- // Init default value
- safestrncpy(console_log_filepath, "./log/login-msg_log.log", sizeof(console_log_filepath));
- // initialize engine
- accounts = account_db_sql();
- // read login-server configuration
- login_set_defaults();
- logcnslif_get_options(argc,argv);
- login_config_read(login_config.loginconf_name, true);
- msg_config_read(login_config.msgconf_name);
- login_lan_config_read(login_config.lanconf_name);
- //end config
- rnd_init();
- do_init_loginclif();
- do_init_loginchrif();
- // initialize logging
- if( login_config.log_login )
- loginlog_init();
- // initialize static and dynamic ipban system
- ipban_init();
- // Online user database init
- online_db = idb_alloc(DB_OPT_RELEASE_DATA);
- add_timer_func_list(login_waiting_disconnect_timer, "waiting_disconnect_timer");
- // Interserver auth init
- auth_db = idb_alloc(DB_OPT_RELEASE_DATA);
- // set default parser as parse_login function
- set_defaultparse(logclif_parse);
- // every 10 minutes cleanup online account db.
- add_timer_func_list(login_online_data_cleanup, "online_data_cleanup");
- add_timer_interval(gettick() + 600*1000, login_online_data_cleanup, 0, 0, 600*1000);
- // Account database init
- if( accounts == NULL ) {
- ShowFatalError("do_init: account engine not found.\n");
- exit(EXIT_FAILURE);
- } else {
- if(!accounts->init(accounts)) {
- ShowFatalError("do_init: Failed to initialize account engine.\n");
- exit(EXIT_FAILURE);
- }
- }
- // server port open & binding
- if( (login_fd = make_listen_bind(login_config.login_ip,login_config.login_port)) == -1 ) {
- ShowFatalError("Failed to bind to port '" CL_WHITE "%d" CL_RESET "'\n",login_config.login_port);
- exit(EXIT_FAILURE);
- }
- if( runflag != CORE_ST_STOP ) {
- shutdown_callback = do_shutdown;
- runflag = LOGINSERVER_ST_RUNNING;
- }
- do_init_logincnslif();
- ShowStatus("The login-server is " CL_GREEN "ready" CL_RESET " (Server is listening on the port %u).\n\n", login_config.login_port);
- login_log(0, "login server", 100, "login server started");
- return 0;
- }
|