/** * 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 == 0 || 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(WFIFOCP(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(WFIFOCP(fd,3), 20, unban_time, login_config.date_format); } WFIFOSET(fd,23); } //#endif }
/** * 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; }
/** * 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 PACKETVER < 20170315 int cmd = 0x69; // AC_ACCEPT_LOGIN int header = 47; int size = 32; #else int cmd = 0xac4; // AC_ACCEPT_LOGIN3 int header = 64; int size = 160; #endif 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 minimum 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,header+size*server_num); WFIFOW(fd,0) = cmd; WFIFOW(fd,2) = header+size*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); #if PACKETVER >= 20170315 memset(WFIFOP(fd,47),0,17); // Unknown #endif 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,header+n*size) = htonl((subnet_char_ip) ? subnet_char_ip : ch_server[i].ip); WFIFOW(fd,header+n*size+4) = ntows(htons(ch_server[i].port)); // [!] LE byte order here [!] memcpy(WFIFOP(fd,header+n*size+6), ch_server[i].name, 20); WFIFOW(fd,header+n*size+26) = ch_server[i].users; WFIFOW(fd,header+n*size+28) = ch_server[i].type; WFIFOW(fd,header+n*size+30) = ch_server[i].new_; #if PACKETVER >= 20170315 memset(WFIFOP(fd, header+n*size+32), 0, 128); // Unknown #endif n++; } WFIFOSET(fd,header+size*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->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); } }
/** * 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; }
/** * 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; else { int result; char server_name[20]; char message[256]; uint32 server_ip; uint16 server_port; uint16 type; uint16 new_; safestrncpy(sd->userid, RFIFOCP(fd,2), NAME_LENGTH); safestrncpy(sd->passwd, RFIFOCP(fd,26), NAME_LENGTH); if( login_config.use_md5_passwds ) MD5_String(sd->passwd, sd->passwd); sd->passwdenc = 0; server_ip = ntohl(RFIFOL(fd,54)); server_port = ntohs(RFIFOW(fd,58)); safestrncpy(server_name, RFIFOCP(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', ip: '%s')\n", server_name, CONVIP(server_ip), server_port, sd->userid, 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 < 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; }