diff options
author | ultramage <ultramage@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2008-03-27 12:06:12 +0000 |
---|---|---|
committer | ultramage <ultramage@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2008-03-27 12:06:12 +0000 |
commit | b7bda5cccb691b35415a7aa1f51190c4ca487fc9 (patch) | |
tree | 66f63255ee81b51bda20c5e5d89fee29f0fab9d3 | |
parent | a011f250a762ed62403cf25f61f52e7b35193aec (diff) | |
download | hercules-b7bda5cccb691b35415a7aa1f51190c4ca487fc9.tar.gz hercules-b7bda5cccb691b35415a7aa1f51190c4ca487fc9.tar.bz2 hercules-b7bda5cccb691b35415a7aa1f51190c4ca487fc9.tar.xz hercules-b7bda5cccb691b35415a7aa1f51190c4ca487fc9.zip |
Partial rewrite of the login server's auth system.
* replaced the cyclic, size-limited auth_fifo data structure with the more appropriate DBMap-based alternative (stops some erratic behavior)
* added code to simulate the pseudo-status "online on login server"
* auth data will now expire after 30 seconds instead of persisting
* better-than-aegis handling of login cancellation (the server will wipe all previous auth data instead of making you wait for it to expire)
* proper status message - no more generic "rejected from server", now you'll get "the server still recognizes your last connection"
* fixed a typo in r10110 which caused disconnect timer removal to fail
* split off some parsing code to login_auth_ok() and login_auth_failed()
* extended the auth confirmation packet so that the login_id1/2 values are sent along with the associated account id (stops charserver from making wrong choices if two incoming sessions have the same account id)
* fixed a bug in the disconnect part of the main charserver parsing loop, where a non-authed client would erase the online db entry for a client that's already online, thus bypassing any dual-login checks
* added code to stop the waiting_disconnect timer when the associated online entry is removed right away, instead of doing checks later
* removed code that would periodically wipe the online status of clients that are in the auth process (producing yet more erratic behavior)
* commented out some TXT-only reconnect prevention code (bugreport:1281)
git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@12441 54d463be-8e91-2dee-dedb-b68131a5f0ec
-rw-r--r-- | Changelog-Trunk.txt | 24 | ||||
-rw-r--r-- | src/char/char.c | 27 | ||||
-rw-r--r-- | src/char_sql/char.c | 27 | ||||
-rw-r--r-- | src/login/admin.c | 8 | ||||
-rw-r--r-- | src/login/login.c | 366 | ||||
-rw-r--r-- | src/login/login.h | 14 | ||||
-rw-r--r-- | src/login_sql/login.c | 493 | ||||
-rw-r--r-- | src/login_sql/login.h | 12 |
8 files changed, 546 insertions, 425 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt index f6b0eb8ac..bdf97bc14 100644 --- a/Changelog-Trunk.txt +++ b/Changelog-Trunk.txt @@ -3,7 +3,29 @@ Date Added AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK. IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. -2007/03/27 +2008/03/27 + * Partial rewrite of the login server's auth system. + - replaced the cyclic, size-limited auth_fifo data structure with the + more appropriate DBMap-based alternative (stops some erratic behavior) + - added code to simulate the pseudo-status "online on login server" + - auth data will now expire after 30 seconds instead of persisting + - better-than-aegis handling of login cancellation (the server will wipe + all previous auth data instead of making you wait for it to expire) + - proper status message - no more generic "rejected from server", now + you'll get "the server still recognizes your last connection" + - fixed a typo in r10110 which caused disconnect timer removal to fail + - split off some parsing code to login_auth_ok() and login_auth_failed() + - extended the auth confirmation packet so that the login_id1/2 values + are sent along with the associated account id (stops charserver from + making wrong choices if two incoming sessions have the same acc_id) + - fixed a bug in the disconnect part of the main charserver parsing + loop, where a non-authed client would erase the online db entry for + a client that's already online, thus bypassing any dual-login checks + - added code to stop the waiting_disconnect timer when the associated + online entry is removed right away, instead of doing checks later + - removed code that would periodically wipe the online status of clients + that are in the auth process (producing yet more erratic behavior) + - commented out some TXT-only reconnect prevention code (bugreport:1281) * merged the charserver acc modification code cleanup to TXT (r11324) * changed trunk's default server->client PACKETVER to 9 2008/03/26 diff --git a/src/char/char.c b/src/char/char.c index 2a9c00943..0acb967ad 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -2009,27 +2009,36 @@ int parse_fromlogin(int fd) // acknowledgement of account authentication request case 0x2713: - if (RFIFOREST(fd) < 51) + if (RFIFOREST(fd) < 59) return 0; + { + int account_id = RFIFOL(fd,2); + int login_id1 = RFIFOL(fd,6); + int login_id2 = RFIFOL(fd,10); + bool result = RFIFOB(fd,14); + const char* email = (const char*)RFIFOP(fd,15); + time_t connect_until = (time_t)RFIFOL(fd,55); // find the session with this account id - ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2) ); + ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && + sd->account_id == account_id && sd->login_id1 == login_id1 && sd->login_id2 == login_id2 ); if( i < fd_max ) { - if( RFIFOB(fd,6) != 0 ) { // failure + if( result ) { // failure WFIFOHEAD(i,3); WFIFOW(i,0) = 0x6c; WFIFOB(i,2) = 0x42; WFIFOSET(i,3); } else { // success - memcpy(sd->email, RFIFOP(fd,7), 40); + memcpy(sd->email, email, 40); if (e_mail_check(sd->email) == 0) strncpy(sd->email, "a@a.com", 40); // default e-mail - sd->connect_until_time = (time_t)RFIFOL(fd,47); + sd->connect_until_time = connect_until; char_auth_ok(i, sd); } } - RFIFOSKIP(fd,51); + } + RFIFOSKIP(fd,59); break; // Receiving of an e-mail/time limit from the login-server (answer of a request because a player comes back from map-server to char-server) by [Yor] @@ -3223,12 +3232,12 @@ int parse_char(int fd) if(session[fd]->flag.eof) { - if (sd != NULL) + if( sd != NULL && sd->auth ) { struct online_char_data* data = (struct online_char_data*)idb_get(online_char_db, sd->account_id); - if (!data || data->server == -1) //If it is not in any server, send it offline. [Skotlex] + if( data == NULL || data->server == -1) //If it is not in any server, send it offline. [Skotlex] set_char_offline(99,sd->account_id); - if (data && data->fd == fd) + if( data != NULL && data->fd == fd) data->fd = -1; } do_close(fd); diff --git a/src/char_sql/char.c b/src/char_sql/char.c index f3a25f5fe..917d0ae93 100644 --- a/src/char_sql/char.c +++ b/src/char_sql/char.c @@ -1710,25 +1710,34 @@ int parse_fromlogin(int fd) // acknowledgement of account authentication request case 0x2713: - if (RFIFOREST(fd) < 51) + if (RFIFOREST(fd) < 59) return 0; + { + int account_id = RFIFOL(fd,2); + int login_id1 = RFIFOL(fd,6); + int login_id2 = RFIFOL(fd,10); + bool result = RFIFOB(fd,14); + const char* email = (const char*)RFIFOP(fd,15); + time_t connect_until = (time_t)RFIFOL(fd,55); // find the session with this account id - ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2) ); + ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && + sd->account_id == account_id && sd->login_id1 == login_id1 && sd->login_id2 == login_id2 ); if( i < fd_max ) { - if( RFIFOB(fd,6) != 0 ) { // failure + if( result ) { // failure WFIFOHEAD(i,3); WFIFOW(i,0) = 0x6c; WFIFOB(i,2) = 0x42; WFIFOSET(i,3); } else { // success - memcpy(sd->email, RFIFOP(fd,7), 40); - sd->connect_until_time = (time_t)RFIFOL(fd,47); + memcpy(sd->email, email, 40); + sd->connect_until_time = connect_until; char_auth_ok(i, sd); } } - RFIFOSKIP(fd,51); + } + RFIFOSKIP(fd,59); break; // acknowledgement of e-mail/limited time request @@ -2855,12 +2864,12 @@ int parse_char(int fd) if(session[fd]->flag.eof) { - if (sd != NULL) + if( sd != NULL && sd->auth ) { // already authed client struct online_char_data* data = (struct online_char_data*)idb_get(online_char_db, sd->account_id); - if (!data || data->server == -1) //If it is not in any server, send it offline. [Skotlex] + if( data == NULL || data->server == -1) //If it is not in any server, send it offline. [Skotlex] set_char_offline(99,sd->account_id); - if (data && data->fd == fd) + if( data != NULL && data->fd == fd) data->fd = -1; } do_close(fd); diff --git a/src/login/admin.c b/src/login/admin.c index 32b89fc4d..02e7a83ba 100644 --- a/src/login/admin.c +++ b/src/login/admin.c @@ -270,9 +270,11 @@ int parse_admin(int fd) WBUFB(buf,6) = 0; // 0: change of statut, 1: ban WBUFL(buf,7) = statut; // status or final date of a banishment charif_sendallwos(-1, buf, 11); +/* for(j = 0; j < AUTH_FIFO_SIZE; j++) if (auth_fifo[j].account_id == auth_dat[i].account_id) auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentication) +*/ } auth_dat[i].state = statut; memcpy(auth_dat[i].error_message, error_message, 20); @@ -363,9 +365,11 @@ int parse_admin(int fd) if (auth_dat[i].sex != ((sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'))) { unsigned char buf[16]; WFIFOL(fd,2) = auth_dat[i].account_id; +/* for(j = 0; j < AUTH_FIFO_SIZE; j++) if (auth_fifo[j].account_id == auth_dat[i].account_id) auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentication) +*/ auth_dat[i].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); ShowNotice("'ladmin': Modification of a sex (account: %s, new sex: %c, ip: %s)\n", auth_dat[i].userid, sex, ip); mmo_auth_sync(); @@ -635,9 +639,11 @@ int parse_admin(int fd) WBUFB(buf,6) = 1; // 0: change of statut, 1: ban WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment charif_sendallwos(-1, buf, 11); +/* for(j = 0; j < AUTH_FIFO_SIZE; j++) if (auth_fifo[j].account_id == auth_dat[i].account_id) auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentication) +*/ } auth_dat[i].ban_until_time = timestamp; mmo_auth_sync(); @@ -693,11 +699,13 @@ int parse_admin(int fd) WBUFB(buf,6) = 1; // 0: change of statut, 1: ban WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment charif_sendallwos(-1, buf, 11); +/* for(j = 0; j < AUTH_FIFO_SIZE; j++) if (auth_fifo[j].account_id == auth_dat[i].account_id) { auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentication) break; } +*/ } auth_dat[i].ban_until_time = timestamp; mmo_auth_sync(); diff --git a/src/login/login.c b/src/login/login.c index 7051ffb5e..01da9716c 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -79,10 +79,6 @@ struct login_session_data { char md5key[20]; }; -// auth information of incoming clients -struct _auth_fifo auth_fifo[AUTH_FIFO_SIZE]; -int auth_fifo_pos = 0; - // account database struct auth_data* auth_dat = NULL; uint32 auth_num = 0, auth_max = 0; @@ -104,6 +100,21 @@ int level_new_gm = 60; int parse_admin(int fd); //----------------------------------------------------- +// Auth database +//----------------------------------------------------- +#define AUTH_TIMEOUT 30000 + +struct auth_node { + int account_id; + uint32 login_id1; + uint32 login_id2; + uint32 ip; + uint8 sex; +}; + +static DBMap* auth_db; // int account_id -> struct auth_node* + +//----------------------------------------------------- // Online User Database [Wizputer] //----------------------------------------------------- @@ -126,11 +137,11 @@ static void* create_online_user(DBKey key, va_list args) return p; } -void add_online_user(int char_server, int account_id) +struct online_login_data* add_online_user(int char_server, int account_id) { struct online_login_data* p; if( !login_config.online_check ) - return; + return NULL; p = (struct online_login_data*)idb_ensure(online_db, account_id, create_online_user); p->char_server = char_server; if( p->waiting_disconnect != -1 ) @@ -138,17 +149,20 @@ void add_online_user(int char_server, int account_id) delete_timer(p->waiting_disconnect, waiting_disconnect_timer); p->waiting_disconnect = -1; } + return p; } void remove_online_user(int account_id) { + struct online_login_data* p; if( !login_config.online_check ) return; - if( account_id == 99 ) - {// reset all to offline - online_db->clear(online_db, NULL); // purge db + p = (struct online_login_data*)idb_get(online_db, account_id); + if( p == NULL ) return; - } + if( p->waiting_disconnect != -1 ) + delete_timer(p->waiting_disconnect, waiting_disconnect_timer); + idb_remove(online_db, account_id); } @@ -159,6 +173,7 @@ static int waiting_disconnect_timer(int tid, unsigned int tick, int id, int data { p->waiting_disconnect = -1; remove_online_user(id); + idb_remove(auth_db, id); } return 0; } @@ -1148,28 +1163,6 @@ int mmo_auth(struct mmo_account* account, int fd) return auth_dat[i].state - 1; } - if( login_config.online_check ) - { - struct online_login_data* data = (struct online_login_data*)idb_get(online_db,auth_dat[i].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[8]; - ShowNotice("User '%s' is already online - Rejected.\n", auth_dat[i].userid); - WBUFW(buf,0) = 0x2734; - WBUFL(buf,2) = auth_dat[i].account_id; - charif_sendallwos(-1, buf, 6); - if( data->waiting_disconnect == -1 ) - data->waiting_disconnect = add_timer(gettick()+30000, waiting_disconnect_timer, auth_dat[i].account_id, 0); - return 3; // Rejected - } - else - ; // the client disconnects after doing auth, so can't really kick it... need some form of expiration timer - } - } - ShowNotice("Authentication accepted (account: %s, id: %d, ip: %s)\n", account->userid, auth_dat[i].account_id, ip); // auth start : time seed @@ -1266,6 +1259,8 @@ int parse_fromchar(int fd) if( RFIFOREST(fd) < 19 ) return 0; { + struct auth_node* node; + int account_id = RFIFOL(fd,2); int login_id1 = RFIFOL(fd,6); int login_id2 = RFIFOL(fd,10); @@ -1273,42 +1268,29 @@ int parse_fromchar(int fd) uint32 ip_ = ntohl(RFIFOL(fd,15)); RFIFOSKIP(fd,19); - ARR_FIND( 0, AUTH_FIFO_SIZE, i, - auth_fifo[i].account_id == account_id && - auth_fifo[i].login_id1 == login_id1 && - auth_fifo[i].login_id2 == login_id2 && - auth_fifo[i].sex == sex && - auth_fifo[i].ip == ip_ && - auth_fifo[i].delflag == 0 ); - - if( i == AUTH_FIFO_SIZE || account_id <= 0 ) - {// authentication not found - ShowStatus("Char-server '%s': authentication of the account %d REFUSED (ip: %s).\n", server[id].name, account_id, ip); - WFIFOHEAD(fd,51); - WFIFOW(fd,0) = 0x2713; - WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = 1; - // It is unnecessary to send email - // It is unnecessary to send validity date of the account - WFIFOSET(fd,51); - } - else + node = (struct auth_node*)idb_get(auth_db, account_id); + if( node != NULL && + node->account_id == account_id && + node->login_id1 == login_id1 && + node->login_id2 == login_id2 && + node->sex == sex && + node->ip == ip_ ) {// found - time_t connect_until_time; + uint32 connect_until_time; char email[40]; unsigned int k; //ShowStatus("Char-server '%s': authentication of the account %d accepted (ip: %s).\n", server[id].name, account_id, ip); // each auth entry can only be used once - auth_fifo[i].delflag = 1; + idb_remove(auth_db, account_id); // retrieve email and account expiration time ARR_FIND( 0, auth_num, k, auth_dat[k].account_id == account_id ); if( k < auth_num ) { strcpy(email, auth_dat[k].email); - connect_until_time = auth_dat[k].connect_until_time; + connect_until_time = (uint32)auth_dat[k].connect_until_time; } else { @@ -1317,13 +1299,28 @@ int parse_fromchar(int fd) } // send ack - WFIFOHEAD(fd,51); + WFIFOHEAD(fd,59); + WFIFOW(fd,0) = 0x2713; + WFIFOL(fd,2) = account_id; + WFIFOL(fd,6) = login_id1; + WFIFOL(fd,10) = login_id2; + WFIFOB(fd,14) = 0; + memcpy(WFIFOP(fd,15), email, 40); + WFIFOL(fd,55) = connect_until_time; + WFIFOSET(fd,59); + } + else + {// authentication not found + ShowStatus("Char-server '%s': authentication of the account %d REFUSED (ip: %s).\n", server[id].name, account_id, ip); + WFIFOHEAD(fd,59); WFIFOW(fd,0) = 0x2713; WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = 0; - memcpy(WFIFOP(fd, 7), email, 40); - WFIFOL(fd,47) = (unsigned long)connect_until_time; - WFIFOSET(fd,51); + WFIFOL(fd,6) = login_id1; + WFIFOL(fd,10) = login_id2; + WFIFOB(fd,14) = 1; + // It is unnecessary to send email + // It is unnecessary to send validity date of the account + WFIFOSET(fd,59); } } break; @@ -1500,15 +1497,17 @@ int parse_fromchar(int fd) else { ShowNotice("Char-server '%s': Status change (account: %d, new status %d, ip: %s).\n", server[id].name, account_id, state, ip); if (state != 0) { - unsigned char buf[16]; + 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 charif_sendallwos(-1, buf, 11); +/* ARR_FIND( 0, AUTH_FIFO_SIZE, j, auth_fifo[j].account_id == account_id ); if( j < AUTH_FIFO_SIZE ) auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentication) +*/ } auth_dat[i].state = state; // Save @@ -1565,10 +1564,11 @@ int parse_fromchar(int fd) WBUFB(buf,6) = 1; // 0: change of status, 1: ban WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment charif_sendallwos(-1, buf, 11); +/* ARR_FIND( 0, AUTH_FIFO_SIZE, j, auth_fifo[j].account_id == account_id ); if( j < AUTH_FIFO_SIZE ) auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentication) - +*/ auth_dat[i].ban_until_time = timestamp; // Save mmo_auth_sync(); @@ -1596,11 +1596,11 @@ int parse_fromchar(int fd) uint8 sex = ( auth_dat[i].sex == 0 ) ? 1 : 0; // invert sex auth_dat[i].sex = sex; ShowNotice("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s).\n", server[id].name, account_id, (sex == 1 ? 'M' : 'F'), ip); - +/* ARR_FIND( 0, AUTH_FIFO_SIZE, j, auth_fifo[j].account_id == account_id ); if( j < AUTH_FIFO_SIZE ) auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentication) - +*/ WBUFW(buf,0) = 0x2723; WBUFL(buf,2) = account_id; WBUFB(buf,6) = sex; @@ -1826,6 +1826,144 @@ int lan_subnetcheck(uint32 ip) return ( i < subnet_count ) ? subnet[i].char_ip : 0; } +void login_auth_ok(struct mmo_account* account, int fd) +{ + uint32 ip = session[fd]->client_addr; + + uint8 server_num, n; + uint32 subnet_char_ip; + struct auth_node* node; + int i; + + account->level = isGM(account->account_id); + + if( account->level < login_config.min_level_to_connect ) + { + ShowStatus("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d).\n", login_config.min_level_to_connect, account->userid, account->level); + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + return; + } + + server_num = 0; + for( i = 0; i < MAX_SERVERS; ++i ) + if( session_isValid(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", account->userid); + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + return; + } + + if( login_config.online_check ) + { + struct online_login_data* data = (struct online_login_data*)idb_get(online_db, account->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[8]; + ShowNotice("User '%s' is already online - Rejected.\n", auth_dat[i].userid); + WBUFW(buf,0) = 0x2734; + WBUFL(buf,2) = account->account_id; + charif_sendallwos(-1, buf, 6); + if( data->waiting_disconnect == -1 ) + data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, waiting_disconnect_timer, account->account_id, 0); + + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 8; // 08 = Server still recognizes your last login + WFIFOSET(fd,3); + return; + } + else + if( data->char_server == -1 ) + {// client has authed but did not access char-server yet + // wipe previous session + idb_remove(auth_db, account->account_id); + remove_online_user(account->account_id); + data = NULL; + } + } + } + + if( account->level > 0 ) + ShowStatus("Connection of the GM (level:%d) account '%s' accepted.\n", account->level, account->userid); + else + ShowStatus("Connection of the account '%s' accepted.\n", account->userid); + + WFIFOHEAD(fd,47+32*server_num); + WFIFOW(fd,0) = 0x69; + WFIFOW(fd,2) = 47+32*server_num; + WFIFOL(fd,4) = account->login_id1; + WFIFOL(fd,8) = account->account_id; + WFIFOL(fd,12) = account->login_id2; + WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used) + //memcpy(WFIFOP(fd,20), account->lastlogin, 24); // in old version, that was for name (not more used) + WFIFOW(fd,44) = 0; // unknown + WFIFOB(fd,46) = account->sex; + for( i = 0, n = 0; i < MAX_SERVERS; ++i ) + { + if( !session_isValid(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 : server[i].ip); + WFIFOW(fd,47+n*32+4) = ntows(htons(server[i].port)); // [!] LE byte order here [!] + memcpy(WFIFOP(fd,47+n*32+6), server[i].name, 20); + WFIFOW(fd,47+n*32+26) = server[i].users; + WFIFOW(fd,47+n*32+28) = server[i].maintenance; + WFIFOW(fd,47+n*32+30) = server[i].new_; + n++; + } + WFIFOSET(fd,47+32*server_num); + + // create temporary auth entry + CREATE(node, struct auth_node, 1); + node->account_id = account->account_id; + node->login_id1 = account->login_id1; + node->login_id2 = account->login_id2; + node->sex = account->sex; + node->ip = ip; + idb_put(auth_db, account->account_id, node); + + if( login_config.online_check ) + { + struct online_login_data* data; + + // mark client as 'online' + data = add_online_user(-1, account->account_id); + + // schedule deletion of this node + data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, waiting_disconnect_timer, account->account_id, 0); + } +} + +void login_auth_failed(struct mmo_account* account, int fd, int result) +{ + 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 + char tmpstr[20]; + int i = search_account_index(account->userid); + time_t ban_until_time = ( i >= 0 ) ? auth_dat[i].ban_until_time : 0; + strftime(tmpstr, 20, login_config.date_format, localtime(&ban_until_time)); + safestrncpy((char*)WFIFOP(fd,3), tmpstr, 20); // ban timestamp goes here + } + WFIFOSET(fd,23); +} + //---------------------------------------------------------------------------------------- // Default packet parsing (normal players or administation/char-server connection requests) //---------------------------------------------------------------------------------------- @@ -1912,97 +2050,9 @@ int parse_login(int fd) result = mmo_auth(&account, fd); if( result == -1 ) - { // auth success - int gm_level = isGM(account.account_id); - if( login_config.min_level_to_connect > gm_level ) - { - ShowStatus("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d, ip: %s).\n", login_config.min_level_to_connect, account.userid, gm_level, ip); - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - } - else - { - uint8 server_num, n; - uint32 subnet_char_ip; - - server_num = 0; - for( i = 0; i < MAX_SERVERS; ++i ) - if( session_isValid(server[i].fd) ) - server_num++; - - if( server_num > 0 ) - {// if at least 1 char-server - if (gm_level) - ShowStatus("Connection of the GM (level:%d) account '%s' accepted.\n", gm_level, account.userid); - else - ShowStatus("Connection of the account '%s' accepted.\n", account.userid); - - WFIFOHEAD(fd,47+32*server_num); - WFIFOW(fd,0) = 0x69; - WFIFOW(fd,2) = 47+32*server_num; - WFIFOL(fd,4) = account.login_id1; - WFIFOL(fd,8) = account.account_id; - WFIFOL(fd,12) = account.login_id2; - WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used) - //memcpy(WFIFOP(fd,20), account.lastlogin, 24); // in old version, that was for name (not more used) - WFIFOW(fd,44) = 0; // unknown - WFIFOB(fd,46) = account.sex; - for( i = 0, n = 0; i < MAX_SERVERS; ++i ) - { - if( !session_isValid(server[i].fd) ) - continue; - - subnet_char_ip = lan_subnetcheck(ipl); // Advanced subnet check [LuzZza] - WFIFOL(fd,47+n*32) = htonl((subnet_char_ip) ? subnet_char_ip : server[i].ip); - WFIFOW(fd,47+n*32+4) = ntows(htons(server[i].port)); // [!] LE byte order here [!] - memcpy(WFIFOP(fd,47+n*32+6), server[i].name, 20); - WFIFOW(fd,47+n*32+26) = server[i].users; - WFIFOW(fd,47+n*32+28) = server[i].maintenance; - WFIFOW(fd,47+n*32+30) = server[i].new_; - n++; - } - WFIFOSET(fd,47+32*server_num); - - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - auth_fifo[auth_fifo_pos].account_id = account.account_id; - auth_fifo[auth_fifo_pos].login_id1 = account.login_id1; - auth_fifo[auth_fifo_pos].login_id2 = account.login_id2; - auth_fifo[auth_fifo_pos].sex = account.sex; - auth_fifo[auth_fifo_pos].delflag = 0; - auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr; - auth_fifo_pos++; - } - else - {// 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, ip: %s).\n", account.userid, ip); - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - } - } - } + login_auth_ok(&account, fd); else - { // auth failed - 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 - char tmpstr[20]; - time_t ban_until_time; - i = search_account_index(account.userid); - ban_until_time = (i) ? auth_dat[i].ban_until_time : 0; - strftime(tmpstr, 20, login_config.date_format, localtime(&ban_until_time)); - safestrncpy((char*)WFIFOP(fd,3), tmpstr, 20); // ban timestamp goes here - } - WFIFOSET(fd,23); - } + login_auth_failed(&account, fd, result); } break; @@ -2256,9 +2306,6 @@ static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) struct online_login_data *character= (struct online_login_data*)data; if (character->char_server == -2) //Unknown server.. set them offline remove_online_user(character->account_id); - else if (character->char_server < 0) - //Free data from players that have not been online for a while. - db_remove(online_db, key); return 0; } @@ -2615,6 +2662,7 @@ void do_final(void) mmo_auth_sync(); online_db->destroy(online_db, NULL); + auth_db->destroy(auth_db, NULL); if(auth_dat) aFree(auth_dat); if(gm_account_db) aFree(gm_account_db); @@ -2666,9 +2714,6 @@ int do_init(int argc, char** argv) srand((unsigned int)time(NULL)); - for( i = 0; i < AUTH_FIFO_SIZE; i++ ) - auth_fifo[i].delflag = 1; - for( i = 0; i < MAX_SERVERS; i++ ) server[i].fd = -1; @@ -2677,6 +2722,7 @@ int do_init(int argc, char** argv) add_timer_func_list(waiting_disconnect_timer, "waiting_disconnect_timer"); // Auth init + auth_db = idb_alloc(DB_OPT_RELEASE_DATA); mmo_auth_init(); // Read account information. diff --git a/src/login/login.h b/src/login/login.h index ab6396a5d..2929f7b4f 100644 --- a/src/login/login.h +++ b/src/login/login.h @@ -21,10 +21,9 @@ struct mmo_account { int account_id; long login_id1; long login_id2; - long char_id; char lastlogin[24]; char sex; - //uint8 level; + uint8 level; }; struct mmo_char_server { @@ -84,15 +83,4 @@ extern struct auth_data { struct global_reg account_reg2[ACCOUNT_REG2_NUM]; // account script variables (stored on login server) } *auth_dat; -// stores auth information of incoming clients -// used during charserver auth validation process -#define AUTH_FIFO_SIZE 256 -extern struct _auth_fifo { - int account_id; - uint32 login_id1, login_id2; - uint32 ip; - uint8 sex; - bool delflag; -} auth_fifo[AUTH_FIFO_SIZE]; - #endif /* _LOGIN_H_ */ diff --git a/src/login_sql/login.c b/src/login_sql/login.c index 57ec484cb..e104d0367 100644 --- a/src/login_sql/login.c +++ b/src/login_sql/login.c @@ -65,16 +65,26 @@ char login_db_userid[256] = "userid"; char login_db_user_pass[256] = "user_pass"; char login_db_level[256] = "level"; -//----------------------------------------------------- struct login_session_data { uint16 md5keylen; char md5key[20]; }; -// auth information of incoming clients -struct _auth_fifo auth_fifo[AUTH_FIFO_SIZE]; -int auth_fifo_pos = 0; +//----------------------------------------------------- +// Auth database +//----------------------------------------------------- +#define AUTH_TIMEOUT 30000 + +struct auth_node { + int account_id; + uint32 login_id1; + uint32 login_id2; + uint32 ip; + uint8 sex; +}; + +static DBMap* auth_db; // int account_id -> struct auth_node* //----------------------------------------------------- // Online User Database [Wizputer] @@ -99,11 +109,11 @@ static void* create_online_user(DBKey key, va_list args) return p; } -void add_online_user(int char_server, int account_id) +struct online_login_data* add_online_user(int char_server, int account_id) { struct online_login_data* p; if( !login_config.online_check ) - return; + return NULL; p = (struct online_login_data*)idb_ensure(online_db, account_id, create_online_user); p->char_server = char_server; if( p->waiting_disconnect != -1 ) @@ -111,27 +121,31 @@ void add_online_user(int char_server, int account_id) delete_timer(p->waiting_disconnect, waiting_disconnect_timer); p->waiting_disconnect = -1; } + return p; } void remove_online_user(int account_id) { + struct online_login_data* p; if( !login_config.online_check ) return; - if( account_id == 99 ) - {// reset all to offline - online_db->clear(online_db, NULL); // purge db + p = (struct online_login_data*)idb_get(online_db, account_id); + if( p == NULL ) return; - } + if( p->waiting_disconnect != -1 ) + delete_timer(p->waiting_disconnect, waiting_disconnect_timer); + idb_remove(online_db, account_id); } static int waiting_disconnect_timer(int tid, unsigned int tick, int id, int data) { struct online_login_data* p = (struct online_login_data*)idb_get(online_db, id); - if( p != NULL && p->waiting_disconnect == id ) + if( p != NULL && p->waiting_disconnect == tid && p->account_id == id ) { p->waiting_disconnect = -1; remove_online_user(id); + idb_remove(auth_db, id); } return 0; } @@ -550,28 +564,6 @@ int mmo_auth(struct mmo_account* account, int fd) return state - 1; } - if( login_config.online_check ) - { - struct online_login_data* data = (struct online_login_data*)idb_get(online_db, account->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[8]; - ShowNotice("User '%s' is already online - Rejected.\n", account->userid); - WBUFW(buf,0) = 0x2734; - WBUFL(buf,2) = account->account_id; - charif_sendallwos(-1, buf, 6); - if( data->waiting_disconnect == -1 ) - data->waiting_disconnect = add_timer(gettick()+30000, waiting_disconnect_timer, account->account_id, 0); - return 3; // Rejected - } - else - ; // the client disconnects after doing auth, so can't really kick it... need some form of expiration timer - - } - } - account->login_id1 = rand(); account->login_id2 = rand(); @@ -659,6 +651,8 @@ int parse_fromchar(int fd) if( RFIFOREST(fd) < 19 ) return 0; { + struct auth_node* node; + int account_id = RFIFOL(fd,2); int login_id1 = RFIFOL(fd,6); int login_id2 = RFIFOL(fd,10); @@ -666,32 +660,19 @@ int parse_fromchar(int fd) uint32 ip_ = ntohl(RFIFOL(fd,15)); RFIFOSKIP(fd,19); - ARR_FIND( 0, AUTH_FIFO_SIZE, i, - auth_fifo[i].account_id == account_id && - auth_fifo[i].login_id1 == login_id1 && - auth_fifo[i].login_id2 == login_id2 && - auth_fifo[i].sex == sex && - auth_fifo[i].ip == ip_ && - auth_fifo[i].delflag == 0 ); - - if( i == AUTH_FIFO_SIZE || account_id <= 0 ) - {// authentication not found - ShowStatus("Char-server '%s': authentication of the account %d REFUSED (ip: %s).\n", server[id].name, account_id, ip); - WFIFOHEAD(fd,51); - WFIFOW(fd,0) = 0x2713; - WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = 1; - // It is unnecessary to send email - // It is unnecessary to send validity date of the account - WFIFOSET(fd,51); - } - else + node = (struct auth_node*)idb_get(auth_db, account_id); + if( node != NULL && + node->account_id == account_id && + node->login_id1 == login_id1 && + node->login_id2 == login_id2 && + node->sex == sex && + node->ip == ip_ ) {// found uint32 connect_until_time; char email[40]; // each auth entry can only be used once - auth_fifo[i].delflag = 1; + idb_remove(auth_db, account_id); // retrieve email and account expiration time if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `email`,`connect_until` FROM `%s` WHERE `%s`='%d'", login_db, login_db_account_id, account_id) ) @@ -715,13 +696,28 @@ int parse_fromchar(int fd) } // send ack - WFIFOHEAD(fd,51); + WFIFOHEAD(fd,59); WFIFOW(fd,0) = 0x2713; WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = 0; - memcpy(WFIFOP(fd, 7), email, 40); - WFIFOL(fd,47) = connect_until_time; - WFIFOSET(fd,51); + WFIFOL(fd,6) = login_id1; + WFIFOL(fd,10) = login_id2; + WFIFOB(fd,14) = 0; + memcpy(WFIFOP(fd,15), email, 40); + WFIFOL(fd,55) = connect_until_time; + WFIFOSET(fd,59); + } + else + {// authentication not found + ShowStatus("Char-server '%s': authentication of the account %d REFUSED (ip: %s).\n", server[id].name, account_id, ip); + WFIFOHEAD(fd,59); + WFIFOW(fd,0) = 0x2713; + WFIFOL(fd,2) = account_id; + WFIFOL(fd,6) = login_id1; + WFIFOL(fd,10) = login_id2; + WFIFOB(fd,14) = 1; + // It is unnecessary to send email + // It is unnecessary to send validity date of the account + WFIFOSET(fd,59); } } break; @@ -1191,12 +1187,222 @@ int login_ip_ban_check(uint32 ip) return 1; } +void login_auth_ok(struct mmo_account* account, int fd) +{ + char esc_userid[NAME_LENGTH*2+1]; + uint32 ip = session[fd]->client_addr; + + uint8 server_num, n; + uint32 subnet_char_ip; + struct auth_node* node; + int i; + + Sql_EscapeStringLen(sql_handle, esc_userid, account->userid, strlen(account->userid)); + + if( account->level < login_config.min_level_to_connect ) + { + ShowStatus("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d).\n", login_config.min_level_to_connect, account->userid, account->level); + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + return; + } + + server_num = 0; + for( i = 0; i < MAX_SERVERS; ++i ) + if( session_isValid(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", account->userid); + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + return; + } + + if( login_config.online_check ) + { + struct online_login_data* data = (struct online_login_data*)idb_get(online_db, account->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", account->userid); + WBUFW(buf,0) = 0x2734; + WBUFL(buf,2) = account->account_id; + charif_sendallwos(-1, buf, 6); + if( data->waiting_disconnect == -1 ) + data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, waiting_disconnect_timer, account->account_id, 0); + + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 8; // 08 = Server still recognizes your last login + WFIFOSET(fd,3); + return; + } + else + if( data->char_server == -1 ) + {// client has authed but did not access char-server yet + // wipe previous session + idb_remove(auth_db, account->account_id); + remove_online_user(account->account_id); + data = NULL; + } + } + } + + if( login_config.log_login && SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s','100', 'login ok')", loginlog_db, ip, esc_userid) ) + Sql_ShowDebug(sql_handle); + + if( account->level > 0 ) + ShowStatus("Connection of the GM (level:%d) account '%s' accepted.\n", account->level, account->userid); + else + ShowStatus("Connection of the account '%s' accepted.\n", account->userid); + + WFIFOHEAD(fd,47+32*server_num); + WFIFOW(fd,0) = 0x69; + WFIFOW(fd,2) = 47+32*server_num; + WFIFOL(fd,4) = account->login_id1; + WFIFOL(fd,8) = account->account_id; + WFIFOL(fd,12) = account->login_id2; + WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used) + //memcpy(WFIFOP(fd,20), account->lastlogin, 24); // in old version, that was for name (not more used) + WFIFOW(fd,44) = 0; // unknown + WFIFOB(fd,46) = account->sex; + for( i = 0, n = 0; i < MAX_SERVERS; ++i ) + { + if( !session_isValid(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 : server[i].ip); + WFIFOW(fd,47+n*32+4) = ntows(htons(server[i].port)); // [!] LE byte order here [!] + memcpy(WFIFOP(fd,47+n*32+6), server[i].name, 20); + WFIFOW(fd,47+n*32+26) = server[i].users; + WFIFOW(fd,47+n*32+28) = server[i].maintenance; + WFIFOW(fd,47+n*32+30) = server[i].new_; + n++; + } + WFIFOSET(fd,47+32*server_num); + + // create temporary auth entry + CREATE(node, struct auth_node, 1); + node->account_id = account->account_id; + node->login_id1 = account->login_id1; + node->login_id2 = account->login_id2; + node->sex = account->sex; + node->ip = ip; + idb_put(auth_db, account->account_id, node); + + if( login_config.online_check ) + { + struct online_login_data* data; + + // mark client as 'online' + data = add_online_user(-1, account->account_id); + + // schedule deletion of this node + data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, waiting_disconnect_timer, account->account_id, 0); + } +} + +void login_auth_failed(struct mmo_account* account, int fd, int result) +{ + char esc_userid[NAME_LENGTH*2+1]; + uint32 ip = session[fd]->client_addr; + + Sql_EscapeStringLen(sql_handle, esc_userid, account->userid, strlen(account->userid)); + + if (login_config.log_login) + { + const char* error; + switch( result ) { + case 0: error = "Unregistered ID."; break; // 0 = Unregistered ID + case 1: error = "Incorrect Password."; break; // 1 = Incorrect Password + case 2: error = "Account Expired."; break; // 2 = This ID is expired + case 3: error = "Rejected from server."; break; // 3 = Rejected from Server + case 4: error = "Blocked by GM."; break; // 4 = You have been blocked by the GM Team + case 5: error = "Not latest game EXE."; break; // 5 = Your Game's EXE file is not the latest version + case 6: error = "Banned."; break; // 6 = Your are Prohibited to log in until %s + case 7: error = "Server Over-population."; break; // 7 = Server is jammed due to over populated + case 8: error = "Account limit from company"; break; // 8 = No more accounts may be connected from this company + case 9: error = "Ban by DBA"; break; // 9 = MSI_REFUSE_BAN_BY_DBA + case 10: error = "Email not confirmed"; break; // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED + case 11: error = "Ban by GM"; break; // 11 = MSI_REFUSE_BAN_BY_GM + case 12: error = "Working in DB"; break; // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK + case 13: error = "Self Lock"; break; // 13 = MSI_REFUSE_SELF_LOCK + case 14: error = "Not Permitted Group"; break; // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP + case 15: error = "Not Permitted Group"; break; // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP + case 99: error = "Account gone."; break; // 99 = This ID has been totally erased + case 100: error = "Login info remains."; break; // 100 = Login information remains at %s + case 101: error = "Hacking investigation."; break; // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information + case 102: error = "Bug investigation."; break; // 102 = This account has been temporarily prohibited from login due to a bug-related investigation + case 103: error = "Deleting char."; break; // 103 = This character is being deleted. Login is temporarily unavailable for the time being + case 104: error = "Deleting spouse char."; break; // 104 = This character is being deleted. Login is temporarily unavailable for the time being + default : error = "Unknown Error."; break; + } + + if( SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s', '%d','login failed : %s')", loginlog_db, ip, esc_userid, result, error) ) + Sql_ShowDebug(sql_handle); + } + + if( result == 1 && login_config.dynamic_pass_failure_ban && login_config.log_login ) // failed password + { + unsigned long failures = 0; + if( SQL_ERROR == Sql_Query(sql_handle, "SELECT count(*) FROM `%s` WHERE `ip` = '%u' AND `rcode` = '1' AND `time` > NOW() - INTERVAL %d MINUTE", + loginlog_db, ip, login_config.dynamic_pass_failure_ban_interval) )// how many times failed account? in one ip. + Sql_ShowDebug(sql_handle); + + //check query result + if( SQL_SUCCESS == Sql_NextRow(sql_handle) ) + { + char* data; + Sql_GetData(sql_handle, 0, &data, NULL); + failures = strtoul(data, NULL, 10); + Sql_FreeResult(sql_handle); + } + if( failures >= login_config.dynamic_pass_failure_ban_limit ) + { + uint8* p = (uint8*)&ip; + if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `ipbanlist`(`list`,`btime`,`rtime`,`reason`) VALUES ('%u.%u.%u.*', NOW() , NOW() + INTERVAL %d MINUTE ,'Password error ban: %s')", p[3], p[2], p[1], login_config.dynamic_pass_failure_ban_duration, esc_userid) ) + Sql_ShowDebug(sql_handle); + } + } + + 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 + if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `ban_until` FROM `%s` WHERE `%s` = %s '%s'", login_db, login_db_userid, (login_config.case_sensitive ? "BINARY" : ""), esc_userid) ) + Sql_ShowDebug(sql_handle); + else if( SQL_SUCCESS == Sql_NextRow(sql_handle) ) + { + char* data; + time_t ban_until_time; + + Sql_GetData(sql_handle, 0, &data, NULL); + ban_until_time = (time_t)strtoul(data, NULL, 10); + Sql_FreeResult(sql_handle); + + strftime((char*)WFIFOP(fd,3), 20, login_config.date_format, localtime(&ban_until_time)); + } + } + WFIFOSET(fd,23); +} + //---------------------------------------------------------------------------------------- // Default packet parsing (normal players or administation/char-server connection requests) //---------------------------------------------------------------------------------------- int parse_login(int fd) { - char esc_userid[NAME_LENGTH*2+1];// escaped username struct mmo_account account; int result, i; uint32 ipl; @@ -1272,165 +1478,13 @@ int parse_login(int fd) RFIFOSKIP(fd,packet_len); account.passwdenc = (command == 0x01dd) ? PASSWORDENC : 0; - Sql_EscapeStringLen(sql_handle, esc_userid, account.userid, strlen(account.userid)); result = mmo_auth(&account, fd); + if( result == -1 ) - { // auth success - if( login_config.min_level_to_connect > account.level ) - { - ShowStatus("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d, ip: %s).\n", login_config.min_level_to_connect, account.userid, account.level, ip); - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - } - else - { - uint8 server_num, n; - uint32 subnet_char_ip; - - server_num = 0; - for( i = 0; i < MAX_SERVERS; ++i ) - if( session_isValid(server[i].fd) ) - server_num++; - - if( server_num > 0 ) - {// if at least 1 char-server - if( login_config.log_login && SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s','100', 'login ok')", loginlog_db, ipl, esc_userid) ) - Sql_ShowDebug(sql_handle); - if( account.level ) - ShowStatus("Connection of the GM (level:%d) account '%s' accepted.\n", account.level, account.userid); - else - ShowStatus("Connection of the account '%s' accepted.\n", account.userid); - - WFIFOHEAD(fd,47+32*server_num); - WFIFOW(fd,0) = 0x69; - WFIFOW(fd,2) = 47+32*server_num; - WFIFOL(fd,4) = account.login_id1; - WFIFOL(fd,8) = account.account_id; - WFIFOL(fd,12) = account.login_id2; - WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used) - //memcpy(WFIFOP(fd,20), account.lastlogin, 24); // in old version, that was for name (not more used) - WFIFOW(fd,44) = 0; // unknown - WFIFOB(fd,46) = account.sex; - for( i = 0, n = 0; i < MAX_SERVERS; ++i ) - { - if( !session_isValid(server[i].fd) ) - continue; - - subnet_char_ip = lan_subnetcheck(ipl); // Advanced subnet check [LuzZza] - WFIFOL(fd,47+n*32) = htonl((subnet_char_ip) ? subnet_char_ip : server[i].ip); - WFIFOW(fd,47+n*32+4) = ntows(htons(server[i].port)); // [!] LE byte order here [!] - memcpy(WFIFOP(fd,47+n*32+6), server[i].name, 20); - WFIFOW(fd,47+n*32+26) = server[i].users; - WFIFOW(fd,47+n*32+28) = server[i].maintenance; - WFIFOW(fd,47+n*32+30) = server[i].new_; - n++; - } - WFIFOSET(fd,47+32*server_num); - - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - auth_fifo[auth_fifo_pos].account_id = account.account_id; - auth_fifo[auth_fifo_pos].login_id1 = account.login_id1; - auth_fifo[auth_fifo_pos].login_id2 = account.login_id2; - auth_fifo[auth_fifo_pos].sex = account.sex; - auth_fifo[auth_fifo_pos].delflag = 0; - auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr; - auth_fifo_pos++; - } - else - {// 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, ip: %s).\n", account.userid, ip); - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - } - } - } + login_auth_ok(&account, fd); else - { // auth failed - if (login_config.log_login) - { - const char* error; - switch( result ) { - case 0: error = "Unregistered ID."; break; // 0 = Unregistered ID - case 1: error = "Incorrect Password."; break; // 1 = Incorrect Password - case 2: error = "Account Expired."; break; // 2 = This ID is expired - case 3: error = "Rejected from server."; break; // 3 = Rejected from Server - case 4: error = "Blocked by GM."; break; // 4 = You have been blocked by the GM Team - case 5: error = "Not latest game EXE."; break; // 5 = Your Game's EXE file is not the latest version - case 6: error = "Banned."; break; // 6 = Your are Prohibited to log in until %s - case 7: error = "Server Over-population."; break; // 7 = Server is jammed due to over populated - case 8: error = "Account limit from company"; break; // 8 = No more accounts may be connected from this company - case 9: error = "Ban by DBA"; break; // 9 = MSI_REFUSE_BAN_BY_DBA - case 10: error = "Email not confirmed"; break; // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED - case 11: error = "Ban by GM"; break; // 11 = MSI_REFUSE_BAN_BY_GM - case 12: error = "Working in DB"; break; // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK - case 13: error = "Self Lock"; break; // 13 = MSI_REFUSE_SELF_LOCK - case 14: error = "Not Permitted Group"; break; // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP - case 15: error = "Not Permitted Group"; break; // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP - case 99: error = "Account gone."; break; // 99 = This ID has been totally erased - case 100: error = "Login info remains."; break; // 100 = Login information remains at %s - case 101: error = "Hacking investigation."; break; // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information - case 102: error = "Bug investigation."; break; // 102 = This account has been temporarily prohibited from login due to a bug-related investigation - case 103: error = "Deleting char."; break; // 103 = This character is being deleted. Login is temporarily unavailable for the time being - case 104: error = "Deleting spouse char."; break; // 104 = This character is being deleted. Login is temporarily unavailable for the time being - default : error = "Unknown Error."; break; - } - - if( SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s', '%d','login failed : %s')", loginlog_db, ipl, esc_userid, result, error) ) - Sql_ShowDebug(sql_handle); - } - - if( result == 1 && login_config.dynamic_pass_failure_ban && login_config.log_login ) // failed password - { - unsigned long failures = 0; - if( SQL_ERROR == Sql_Query(sql_handle, "SELECT count(*) FROM `%s` WHERE `ip` = '%u' AND `rcode` = '1' AND `time` > NOW() - INTERVAL %d MINUTE", - loginlog_db, ipl, login_config.dynamic_pass_failure_ban_interval) )// how many times failed account? in one ip. - Sql_ShowDebug(sql_handle); - - //check query result - if( SQL_SUCCESS == Sql_NextRow(sql_handle) ) - { - char* data; - Sql_GetData(sql_handle, 0, &data, NULL); - failures = strtoul(data, NULL, 10); - Sql_FreeResult(sql_handle); - } - if( failures >= login_config.dynamic_pass_failure_ban_limit ) - { - uint8* p = (uint8*)&ipl; - if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `ipbanlist`(`list`,`btime`,`rtime`,`reason`) VALUES ('%u.%u.%u.*', NOW() , NOW() + INTERVAL %d MINUTE ,'Password error ban: %s')", p[3], p[2], p[1], login_config.dynamic_pass_failure_ban_duration, esc_userid) ) - Sql_ShowDebug(sql_handle); - } - } - - 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 - if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `ban_until` FROM `%s` WHERE `%s` = %s '%s'", login_db, login_db_userid, (login_config.case_sensitive ? "BINARY" : ""), esc_userid) ) - Sql_ShowDebug(sql_handle); - else if( SQL_SUCCESS == Sql_NextRow(sql_handle) ) - { - char* data; - time_t ban_until_time; - - Sql_GetData(sql_handle, 0, &data, NULL); - ban_until_time = (time_t)strtoul(data, NULL, 10); - Sql_FreeResult(sql_handle); - - strftime((char*)WFIFOP(fd,3), 20, login_config.date_format, localtime(&ban_until_time)); - } - } - WFIFOSET(fd,23); - } + login_auth_failed(&account, fd, result); } break; @@ -1466,6 +1520,7 @@ int parse_login(int fd) if (RFIFOREST(fd) < 86) return 0; { + char esc_userid[NAME_LENGTH*2+1]; char server_name[20]; char esc_server_name[20*2+1]; uint32 server_ip; @@ -1598,9 +1653,6 @@ static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) struct online_login_data *character= (struct online_login_data*)data; if (character->char_server == -2) //Unknown server.. set them offline remove_online_user(character->account_id); - else if (character->char_server < 0) - //Free data from players that have not been online for a while. - db_remove(online_db, key); return 0; } @@ -1846,6 +1898,7 @@ void do_final(void) mmo_db_close(); online_db->destroy(online_db, NULL); + auth_db->destroy(auth_db, NULL); if(gm_account_db) aFree(gm_account_db); @@ -1890,9 +1943,6 @@ int do_init(int argc, char** argv) srand((unsigned int)time(NULL)); - for( i = 0; i < AUTH_FIFO_SIZE; i++ ) - auth_fifo[i].delflag = 1; - for( i = 0; i < MAX_SERVERS; i++ ) server[i].fd = -1; @@ -1901,6 +1951,7 @@ int do_init(int argc, char** argv) add_timer_func_list(waiting_disconnect_timer, "waiting_disconnect_timer"); // Auth init + auth_db = idb_alloc(DB_OPT_RELEASE_DATA); mmo_auth_sqldb_init(); // Read account information. diff --git a/src/login_sql/login.h b/src/login_sql/login.h index b67d202cb..02de3c0e8 100644 --- a/src/login_sql/login.h +++ b/src/login_sql/login.h @@ -22,7 +22,6 @@ struct mmo_account { int account_id; long login_id1; long login_id2; - int char_id; char lastlogin[24]; int sex; uint8 level; @@ -65,15 +64,4 @@ extern struct Login_Config { } login_config; -// stores auth information of incoming clients -// used during charserver auth validation process -#define AUTH_FIFO_SIZE 256 -extern struct _auth_fifo { - int account_id; - uint32 login_id1, login_id2; - uint32 ip; - uint8 sex; - bool delflag; -} auth_fifo[AUTH_FIFO_SIZE]; - #endif /* _LOGIN_SQL_H_ */ |