diff options
Diffstat (limited to 'src/login')
-rw-r--r-- | src/login/HPMlogin.c | 2 | ||||
-rw-r--r-- | src/login/Makefile.in | 25 | ||||
-rw-r--r-- | src/login/lclif.c | 566 | ||||
-rw-r--r-- | src/login/lclif.h | 150 | ||||
-rw-r--r-- | src/login/lclif.p.h | 324 | ||||
-rw-r--r-- | src/login/login.c | 357 | ||||
-rw-r--r-- | src/login/login.h | 15 |
7 files changed, 1102 insertions, 337 deletions
diff --git a/src/login/HPMlogin.c b/src/login/HPMlogin.c index f12996915..472cba3c9 100644 --- a/src/login/HPMlogin.c +++ b/src/login/HPMlogin.c @@ -25,6 +25,8 @@ #include "common/cbasetypes.h" #include "login/account.h" +#include "login/lclif.h" +#include "login/lclif.p.h" #include "login/login.h" #include "common/HPMi.h" #include "common/conf.h" diff --git a/src/login/Makefile.in b/src/login/Makefile.in index 6560e6497..274a82fc8 100644 --- a/src/login/Makefile.in +++ b/src/login/Makefile.in @@ -1,7 +1,7 @@ # This file is part of Hercules. # http://herc.ws - http://github.com/HerculesWS/Hercules # -# Copyright (C) 2012-2015 Hercules Dev Team +# Copyright (C) 2012-2016 Hercules Dev Team # Copyright (C) Athena Dev Teams # # Hercules is free software: you can redistribute it and/or modify @@ -23,7 +23,7 @@ CONFIG_D = ../config CONFIG_H = $(wildcard $(CONFIG_D)/*.h) $(wildcard $(CONFIG_D)/*/*.h) COMMON_D = ../common -COMMON_H = $(wildcard $(COMMON_D)/*.h) +COMMON_H = $(filter-out %.p.h, $(wildcard $(COMMON_D)/*.h)) SYSINFO_INC = $(COMMON_D)/sysinfo.inc COMMON_INCLUDE = -I.. @@ -40,9 +40,10 @@ MT19937AR_D = $(THIRDPARTY_D)/mt19937ar MT19937AR_OBJ = $(MT19937AR_D)/mt19937ar.o MT19937AR_H = $(MT19937AR_D)/mt19937ar.h -LOGIN_C = account_sql.c HPMlogin.c ipban_sql.c login.c loginlog_sql.c +LOGIN_C = account_sql.c HPMlogin.c ipban_sql.c lclif.c login.c loginlog_sql.c LOGIN_OBJ = $(addprefix obj_sql/, $(patsubst %.c,%.o,$(LOGIN_C))) -LOGIN_H = login.h account.h HPMlogin.h ipban.h loginlog.h +LOGIN_H = login.h account.h HPMlogin.h ipban.h lclif.h loginlog.h +LOGIN_PH = lclif.p.h HAVE_MYSQL=@HAVE_MYSQL@ ifeq ($(HAVE_MYSQL),yes) @@ -85,7 +86,7 @@ help: Makefile: Makefile.in @$(MAKE) -C ../.. src/login/Makefile -$(SYSINFO_INC): $(LOGIN_C) $(LOGIN_H) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) +$(SYSINFO_INC): $(LOGIN_C) $(LOGIN_PH) $(LOGIN_H) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) @echo " MAKE $@" @$(MAKE) -C ../.. sysinfo @@ -108,12 +109,6 @@ login-server: ../../login-server@EXEEXT@ @$(CC) @STATIC@ @LDFLAGS@ -o ../../login-server@EXEEXT@ $(LOGIN_OBJ) $(COMMON_D)/obj_sql/common_sql.a \ $(COMMON_D)/obj_all/common.a $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) @LIBS@ @MYSQL_LIBS@ -# login object files - -obj_sql/%.o: %.c $(LOGIN_H) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) | obj_sql - @echo " CC $<" - @$(CC) @CFLAGS@ @DEFS@ $(COMMON_INCLUDE) $(THIRDPARTY_INCLUDE) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< - # missing object files $(COMMON_D)/obj_all/common.a: @echo " MAKE $@" @@ -130,3 +125,11 @@ $(MT19937AR_OBJ): $(LIBCONFIG_OBJ): @echo " MAKE $@" @$(MAKE) -C $(LIBCONFIG_D) + +.SECONDEXPANSION: + +# login object files + +obj_sql/%.o: %.c $$(filter %.p.h, $(LOGIN_PH)) $(LOGIN_H) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) | obj_sql + @echo " CC $<" + @$(CC) @CFLAGS@ @DEFS@ $(COMMON_INCLUDE) $(THIRDPARTY_INCLUDE) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< diff --git a/src/login/lclif.c b/src/login/lclif.c new file mode 100644 index 000000000..9515c5940 --- /dev/null +++ b/src/login/lclif.c @@ -0,0 +1,566 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2016 Hercules Dev Team + * + * Hercules is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#define HERCULES_CORE + +#include "lclif.p.h" + +#include "login/ipban.h" +#include "login/login.h" +#include "login/loginlog.h" +#include "common/HPM.h" +#include "common/cbasetypes.h" +#include "common/db.h" +#include "common/md5calc.h" +#include "common/memmgr.h" +#include "common/mmo.h" +#include "common/nullpo.h" +#include "common/random.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/strlib.h" +#include "common/utils.h" + +/** @file + * Implementation of the login client interface. + */ + +struct lclif_interface lclif_s; +struct lclif_interface_private lclif_p; +struct lclif_interface_dbs lclif_dbs; +struct lclif_interface *lclif; + +/// @copydoc lclif_interface::connection_error() +void lclif_connection_error(int fd, uint8 error) +{ + struct packet_SC_NOTIFY_BAN *packet = NULL; + WFIFOHEAD(fd, sizeof(*packet)); + packet = WP2PTR(fd); + packet->packet_id = PACKET_ID_SC_NOTIFY_BAN; + packet->error_code = error; + WFIFOSET(fd, sizeof(*packet)); +} + +/// @copydoc lclif_interface_private::parse_CA_CONNECT_INFO_CHANGED() +enum parsefunc_rcode lclif_parse_CA_CONNECT_INFO_CHANGED(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_CONNECT_INFO_CHANGED(int fd, struct login_session_data *sd) +{ + return PACKET_VALID; +} + +/// @copydoc lclif_interface_private::parse_CA_EXE_HASHCHECK() +enum parsefunc_rcode lclif_parse_CA_EXE_HASHCHECK(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_EXE_HASHCHECK(int fd, struct login_session_data *sd) +{ + const struct packet_CA_EXE_HASHCHECK *packet = RP2PTR(fd); + sd->has_client_hash = 1; + memcpy(sd->client_hash, packet->hash_value, 16); + return PACKET_VALID; +} + +/// @copydoc lclif_interface_private::parse_CA_LOGIN() +enum parsefunc_rcode lclif_parse_CA_LOGIN(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_LOGIN(int fd, struct login_session_data *sd) +{ + const struct packet_CA_LOGIN *packet = RP2PTR(fd); + + sd->version = packet->version; + sd->clienttype = packet->clienttype; + safestrncpy(sd->userid, packet->id, NAME_LENGTH); + safestrncpy(sd->passwd, packet->password, PASSWD_LEN); + + if (login->config->use_md5_passwds) + MD5_String(sd->passwd, sd->passwd); + sd->passwdenc = PWENC_NONE; + + login->client_login(fd, sd); + return PACKET_VALID; +} + +/// @copydoc lclif_interface_private::parse_CA_LOGIN2() +enum parsefunc_rcode lclif_parse_CA_LOGIN2(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_LOGIN2(int fd, struct login_session_data *sd) +{ + const struct packet_CA_LOGIN2 *packet = RP2PTR(fd); + + sd->version = packet->version; + sd->clienttype = packet->clienttype; + safestrncpy(sd->userid, packet->id, NAME_LENGTH); + bin2hex(sd->passwd, packet->password_md5, 16); + sd->passwdenc = PASSWORDENC; + + login->client_login(fd, sd); + return PACKET_VALID; +} + +/// @copydoc lclif_interface_private::parse_CA_LOGIN3() +enum parsefunc_rcode lclif_parse_CA_LOGIN3(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_LOGIN3(int fd, struct login_session_data *sd) +{ + const struct packet_CA_LOGIN3 *packet = RP2PTR(fd); + + sd->version = packet->version; + sd->clienttype = packet->clienttype; + /* unused */ + /* sd->clientinfo = packet->clientinfo; */ + safestrncpy(sd->userid, packet->id, NAME_LENGTH); + bin2hex(sd->passwd, packet->password_md5, 16); + sd->passwdenc = PASSWORDENC; + + login->client_login(fd, sd); + return PACKET_VALID; +} + +/// @copydoc lclif_interface_private::parse_CA_LOGIN4() +enum parsefunc_rcode lclif_parse_CA_LOGIN4(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_LOGIN4(int fd, struct login_session_data *sd) +{ + const struct packet_CA_LOGIN4 *packet = RP2PTR(fd); + + sd->version = packet->version; + sd->clienttype = packet->clienttype; + /* unused */ + /* safestrncpy(sd->mac_address, packet->mac_address, sizeof(sd->mac_address)); */ + safestrncpy(sd->userid, packet->id, NAME_LENGTH); + bin2hex(sd->passwd, packet->password_md5, 16); + sd->passwdenc = PASSWORDENC; + + login->client_login(fd, sd); + return PACKET_VALID; +} + +/// @copydoc lclif_interface_private::parse_CA_LOGIN_PCBANG() +enum parsefunc_rcode lclif_parse_CA_LOGIN_PCBANG(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_LOGIN_PCBANG(int fd, struct login_session_data *sd) +{ + const struct packet_CA_LOGIN_PCBANG *packet = RP2PTR(fd); + + sd->version = packet->version; + sd->clienttype = packet->clienttype; + /* unused */ + /* safestrncpy(sd->ip, packet->ip, sizeof(sd->ip)); */ + /* safestrncpy(sd->mac_address, packet->mac_address, sizeof(sd->mac_address)); */ + safestrncpy(sd->userid, packet->id, NAME_LENGTH); + safestrncpy(sd->passwd, packet->password, PASSWD_LEN); + + if (login->config->use_md5_passwds) + MD5_String(sd->passwd, sd->passwd); + sd->passwdenc = PWENC_NONE; + + login->client_login(fd, sd); + return PACKET_VALID; +} + +/// @copydoc lclif_interface_private::parse_CA_LOGIN_HAN() +enum parsefunc_rcode lclif_parse_CA_LOGIN_HAN(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_LOGIN_HAN(int fd, struct login_session_data *sd) +{ + const struct packet_CA_LOGIN_HAN *packet = RP2PTR(fd); + + sd->version = packet->version; + sd->clienttype = packet->clienttype; + /* unused */ + /* safestrncpy(sd->ip, packet->ip, sizeof(sd->ip)); */ + /* safestrncpy(sd->mac_address, packet->mac_address, sizeof(sd->mac_address)); */ + /* sd->ishan = packet->is_han_game_user; */ + safestrncpy(sd->userid, packet->id, NAME_LENGTH); + safestrncpy(sd->passwd, packet->password, PASSWD_LEN); + + if (login->config->use_md5_passwds) + MD5_String(sd->passwd, sd->passwd); + sd->passwdenc = PWENC_NONE; + + login->client_login(fd, sd); + return PACKET_VALID; +} + +/// @copydoc lclif_interface_private::parse_CA_SSO_LOGIN_REQ() +enum parsefunc_rcode lclif_parse_CA_SSO_LOGIN_REQ(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_SSO_LOGIN_REQ(int fd, struct login_session_data *sd) +{ + const struct packet_CA_SSO_LOGIN_REQ *packet = RP2PTR(fd); + int tokenlen = (int)RFIFOREST(fd) - (int)sizeof(*packet); + + if (tokenlen > PASSWD_LEN || tokenlen < 1) { + ShowError("packet_CA_SSO_LOGIN_REQ: Token length is not between allowed password length, kicking player ('%s')", packet->id); + sockt->eof(fd); + return PACKET_VALID; + } + + sd->clienttype = packet->clienttype; + sd->version = packet->version; + safestrncpy(sd->userid, packet->id, NAME_LENGTH); + safestrncpy(sd->passwd, packet->t1, min(tokenlen + 1, PASSWD_LEN)); // Variable-length field, don't copy more than necessary + + if (login->config->use_md5_passwds) + MD5_String(sd->passwd, sd->passwd); + sd->passwdenc = PWENC_NONE; + + login->client_login(fd, sd); + return PACKET_VALID; +} + +/// @copydoc lclif_interface_private::parse_CA_REQ_HASH() +enum parsefunc_rcode lclif_parse_CA_REQ_HASH(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_REQ_HASH(int fd, struct login_session_data *sd) +{ + memset(sd->md5key, '\0', sizeof(sd->md5key)); + sd->md5keylen = (uint16)(12 + rnd() % 4); + MD5_Salt(sd->md5keylen, sd->md5key); + + lclif->coding_key(fd, sd); + return PACKET_VALID; +} + +/// @copydoc lclif_interface_private::parse_CA_CHARSERVERCONNECT() +enum parsefunc_rcode lclif_parse_CA_CHARSERVERCONNECT(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +enum parsefunc_rcode lclif_parse_CA_CHARSERVERCONNECT(int fd, struct login_session_data *sd) +{ + char ip[16]; + uint32 ipl = sockt->session[fd]->client_addr; + sockt->ip2str(ipl, ip); + + login->parse_request_connection(fd, sd, ip, ipl); + + return PACKET_STOPPARSE; +} + +/// @copydoc lclif_interface::server_list() +bool lclif_send_server_list(struct login_session_data *sd) +{ + int server_num = 0, i, n, length; + uint32 ip; + struct packet_AC_ACCEPT_LOGIN *packet = NULL; + + for (i = 0; i < ARRAYLENGTH(server); ++i) { + if (sockt->session_is_active(server[i].fd)) + server_num++; + } + if (server_num == 0) + return false; + + length = sizeof(*packet) + sizeof(packet->server_list[0]) * server_num; + ip = sockt->session[sd->fd]->client_addr; + + // Allocate the packet + WFIFOHEAD(sd->fd, length); + packet = WP2PTR(sd->fd); + + packet->packet_id = PACKET_ID_AC_ACCEPT_LOGIN; + packet->packet_len = length; + packet->auth_code = sd->login_id1; + packet->aid = sd->account_id; + packet->user_level = sd->login_id2; + packet->last_login_ip = 0; // Not used anymore + memset(packet->last_login_time, '\0', sizeof(packet->last_login_time)); // Not used anymore + packet->sex = sex_str2num(sd->sex); + for (i = 0, n = 0; i < ARRAYLENGTH(server); ++i) { + uint32 subnet_char_ip; + + if (!sockt->session_is_valid(server[i].fd)) + continue; + + subnet_char_ip = login->lan_subnet_check(ip); + packet->server_list[n].ip = htonl((subnet_char_ip) ? subnet_char_ip : server[i].ip); + packet->server_list[n].port = sockt->ntows(htons(server[i].port)); // [!] LE byte order here [!] + safestrncpy(packet->server_list[n].name, server[i].name, 20); + packet->server_list[n].usercount = server[i].users; + + if (server[i].type == CST_PAYING && sd->expiration_time > time(NULL)) + packet->server_list[n].property = CST_NORMAL; + else + packet->server_list[n].property = server[i].type; + + packet->server_list[n].state = server[i].new_; + ++n; + } + WFIFOSET(sd->fd, length); + + return true; +} + +/// @copydoc lclif_interface::auth_failed() +void lclif_send_auth_failed(int fd, time_t ban, uint32 error) +{ +#if PACKETVER >= 20120000 /* not sure when this started */ + struct packet_AC_REFUSE_LOGIN_R2 *packet = NULL; + int packet_id = PACKET_ID_AC_REFUSE_LOGIN_R2; +#else + struct packet_AC_REFUSE_LOGIN *packet = NULL; + int packet_id = PACKET_ID_AC_REFUSE_LOGIN; +#endif + WFIFOHEAD(fd, sizeof(*packet)); + packet = WP2PTR(fd); + packet->packet_id = packet_id; + packet->error_code = error; + if (error == 6) + timestamp2string(packet->block_date, sizeof(packet->block_date), ban, login->config->date_format); + else + memset(packet->block_date, '\0', sizeof(packet->block_date)); + WFIFOSET(fd, sizeof(*packet)); +} + +/// @copydoc lclif_interface::login_error() +void lclif_send_login_error(int fd, uint8 error) +{ + struct packet_AC_REFUSE_LOGIN *packet = NULL; + WFIFOHEAD(fd, sizeof(*packet)); + packet = WP2PTR(fd); + packet->packet_id = PACKET_ID_AC_REFUSE_LOGIN; + packet->error_code = error; + memset(packet->block_date, '\0', sizeof(packet->block_date)); + WFIFOSET(fd, sizeof(*packet)); +} + +/// @copydoc lclif_interface::coding_key() +void lclif_send_coding_key(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +void lclif_send_coding_key(int fd, struct login_session_data *sd) +{ + struct packet_AC_ACK_HASH *packet = NULL; + int16 size = sizeof(*packet) + sd->md5keylen; + + WFIFOHEAD(fd, size); + packet = WP2PTR(fd); + packet->packet_id = PACKET_ID_AC_ACK_HASH; + packet->packet_len = size; + memcpy(packet->secret, sd->md5key, sd->md5keylen); + WFIFOSET(fd, size); +} + +/// @copydoc lclif_interface::parse() +int lclif_parse(int fd) +{ + struct login_session_data *sd = NULL; + int i; + char ip[16]; + uint32 ipl = sockt->session[fd]->client_addr; + sockt->ip2str(ipl, ip); + + if (sockt->session[fd]->flag.eof) { + ShowInfo("Closed connection from '"CL_WHITE"%s"CL_RESET"'.\n", ip); + sockt->close(fd); + return 0; + } + + if ((sd = sockt->session[fd]->session_data) == NULL) { + // Perform ip-ban check + if (login->config->ipban && !sockt->trusted_ip_check(ipl) && ipban_check(ipl)) { + ShowStatus("Connection refused: IP isn't authorized (deny/allow, ip: %s).\n", ip); + login_log(ipl, "unknown", -3, "ip banned"); + lclif->login_error(fd, 3); // 3 = Rejected from Server + sockt->eof(fd); + return 0; + } + + // create a session for this new connection + CREATE(sockt->session[fd]->session_data, struct login_session_data, 1); + sd = sockt->session[fd]->session_data; + sd->fd = fd; + } + + for (i = 0; i < MAX_PROCESSED_PACKETS; ++i) { + enum parsefunc_rcode result; + int16 packet_id = RFIFOW(fd, 0); + int packet_len = (int)RFIFOREST(fd); + + if (packet_len < 2) + return 0; + + result = lclif->p->parse_sub(fd, sd); + + switch (result) { + case PACKET_SKIP: + continue; + case PACKET_INCOMPLETE: + case PACKET_STOPPARSE: + return 0; + case PACKET_UNKNOWN: + ShowWarning("lclif_parse: Received unsupported packet (packet 0x%04x, %d bytes received), disconnecting session #%d.\n", (unsigned int)packet_id, packet_len, fd); +#ifdef DUMP_INVALID_PACKET + ShowDump(RFIFOP(fd, 0), RFIFOREST(fd)); +#endif + sockt->eof(fd); + return 0; + case PACKET_INVALIDLENGTH: + ShowWarning("lclif_parse: Received packet 0x%04x specifies invalid packet_len (%d), disconnecting session #%d.\n", (unsigned int)packet_id, packet_len, fd); +#ifdef DUMP_INVALID_PACKET + ShowDump(RFIFOP(fd, 0), RFIFOREST(fd)); +#endif + sockt->eof(fd); + return 0; + } + } + return 0; +} + +/// @copydoc lclif_interface_private::parse_sub() +enum parsefunc_rcode lclif_parse_sub(int fd, struct login_session_data *sd) +{ + int packet_len = (int)RFIFOREST(fd); + int16 packet_id = RFIFOW(fd, 0); + const struct login_packet_db *lpd; + + if (VECTOR_LENGTH(HPM->packets[hpParse_Login]) > 0) { + int result = HPM->parse_packets(fd, packet_id, hpParse_Login); + if (result == 1) + return PACKET_VALID; + if (result == 2) + return PACKET_INCOMPLETE; // Packet not completed yet + } + + lpd = lclif->packet(packet_id); + + if (lpd == NULL) + return PACKET_UNKNOWN; + + if (lpd->len == 0) + return PACKET_UNKNOWN; + + if (lpd->len > 0 && lpd->pFunc == NULL) + return PACKET_UNKNOWN; //This Packet is defined for length purpose ? should never be sent from client ? + + if (lpd->len == -1) { + uint16 packet_var_len = 0; //Max Variable Packet length is signed int16 size + + if (packet_len < 4) + return PACKET_INCOMPLETE; //Packet incomplete + + packet_var_len = RFIFOW(fd, 2); + + if (packet_var_len < 4 || packet_var_len > SINT16_MAX) + return PACKET_INVALIDLENGTH; //Something is wrong, close connection. + + if (RFIFOREST(fd) < packet_var_len) + return PACKET_INCOMPLETE; //Packet incomplete again. + + return lclif->parse_packet(lpd, fd, sd); + } else if (lpd->len <= packet_len) { + return lclif->parse_packet(lpd, fd, sd); + } + + return PACKET_VALID; +} + +/// @copydoc lclif_interface::packet() +const struct login_packet_db *lclif_packet(int16 packet_id) +{ + if (packet_id == PACKET_ID_CA_CHARSERVERCONNECT) + return &lclif->p->dbs->packet_db[0]; + + if (packet_id > MAX_PACKET_DB || packet_id < MIN_PACKET_DB) + return NULL; + + return &lclif->p->dbs->packet_db[packet_id]; +} + +/// @copydoc lclif_interface::parse_packet() +enum parsefunc_rcode lclif_parse_packet(const struct login_packet_db *lpd, int fd, struct login_session_data *sd) +{ + int result; + result = (*lpd->pFunc)(fd, sd); + RFIFOSKIP(fd, (lpd->len == -1) ? RFIFOW(fd, 2) : lpd->len); + return result; +} + +/// @copydoc lclif_interface_private::packetdb_loaddb() +void packetdb_loaddb(void) +{ + int i; + struct packet { + int16 packet_id; + int16 packet_len; + LoginParseFunc **pFunc; + } packet[] = { +#define packet_def(name) { PACKET_ID_ ## name, sizeof(struct packet_ ## name), &lclif->p->parse_ ## name } +#define packet_def2(name, len) { PACKET_ID_ ## name, (len), &lclif->p->parse_ ## name } + packet_def(CA_CONNECT_INFO_CHANGED), + packet_def(CA_EXE_HASHCHECK), + packet_def(CA_LOGIN), + packet_def(CA_LOGIN2), + packet_def(CA_LOGIN3), + packet_def(CA_LOGIN4), + packet_def(CA_LOGIN_PCBANG), + packet_def(CA_LOGIN_HAN), + packet_def2(CA_SSO_LOGIN_REQ, -1), + packet_def(CA_REQ_HASH), +#undef packet_def +#undef packet_def2 + }; + int length = ARRAYLENGTH(packet); + + memset(lclif->p->dbs->packet_db, '\0', sizeof(lclif->p->dbs->packet_db)); + + for (i = 0; i < length; ++i) { + int16 packet_id = packet[i].packet_id; + Assert_retb(packet_id >= MIN_PACKET_DB && packet_id < MAX_PACKET_DB); + lclif->p->dbs->packet_db[packet_id].len = packet[i].packet_len; + lclif->p->dbs->packet_db[packet_id].pFunc = packet[i].pFunc; + } + + //Explict case, we will save character login packet in position 0 which is unused and not valid by normal + lclif->p->dbs->packet_db[0].len = sizeof(struct packet_CA_CHARSERVERCONNECT); + lclif->p->dbs->packet_db[0].pFunc = &lclif->p->parse_CA_CHARSERVERCONNECT; +} + +/// @copydoc lclif_interface::init() +void lclif_init(void) +{ + lclif->p->packetdb_loaddb(); +} + +/// @copydoc lclif_interface::final() +void lclif_final(void) +{ +} + +/// Interface base initialization. +void lclif_defaults(void) +{ + lclif = &lclif_s; + lclif->p = &lclif_p; + lclif->p->dbs = &lclif_dbs; + + lclif->init = lclif_init; + lclif->final = lclif_final; + + lclif->connection_error = lclif_connection_error; + lclif->server_list = lclif_send_server_list; + lclif->auth_failed = lclif_send_auth_failed; + lclif->login_error = lclif_send_login_error; + lclif->coding_key = lclif_send_coding_key; + + lclif->packet = lclif_packet; + lclif->parse_packet = lclif_parse_packet; + lclif->parse = lclif_parse; + + lclif->p->packetdb_loaddb = packetdb_loaddb; + lclif->p->parse_sub = lclif_parse_sub; + + lclif->p->parse_CA_CONNECT_INFO_CHANGED = lclif_parse_CA_CONNECT_INFO_CHANGED; + lclif->p->parse_CA_EXE_HASHCHECK = lclif_parse_CA_EXE_HASHCHECK; + lclif->p->parse_CA_LOGIN = lclif_parse_CA_LOGIN; + lclif->p->parse_CA_LOGIN2 = lclif_parse_CA_LOGIN2; + lclif->p->parse_CA_LOGIN3 = lclif_parse_CA_LOGIN3; + lclif->p->parse_CA_LOGIN4 = lclif_parse_CA_LOGIN4; + lclif->p->parse_CA_LOGIN_PCBANG = lclif_parse_CA_LOGIN_PCBANG; + lclif->p->parse_CA_LOGIN_HAN = lclif_parse_CA_LOGIN_HAN; + lclif->p->parse_CA_SSO_LOGIN_REQ = lclif_parse_CA_SSO_LOGIN_REQ; + lclif->p->parse_CA_REQ_HASH = lclif_parse_CA_REQ_HASH; + lclif->p->parse_CA_CHARSERVERCONNECT = lclif_parse_CA_CHARSERVERCONNECT; +} diff --git a/src/login/lclif.h b/src/login/lclif.h new file mode 100644 index 000000000..d1e4317a2 --- /dev/null +++ b/src/login/lclif.h @@ -0,0 +1,150 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2016 Hercules Dev Team + * + * Hercules is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef LOGIN_LCLIF_H +#define LOGIN_LCLIF_H + +#include "common/hercules.h" + +/** @file + * Login Client Interface. + **/ + +/* Forward Declarations */ +struct login_session_data; +struct lclif_interface_private; + +/* Enums */ +/// Parse function return code +enum parsefunc_rcode { + PACKET_VALID = 1, + PACKET_INCOMPLETE = 0, + PACKET_UNKNOWN = -1, + PACKET_INVALIDLENGTH = -2, + PACKET_STOPPARSE = -3, + PACKET_SKIP = -4, //internal parser will skip this packet and go parser another, meant for plugins. [hemagx] +}; + +/* Function Typedefs */ +typedef enum parsefunc_rcode (LoginParseFunc)(int fd, struct login_session_data *sd); + +/* Structs */ + +/// Login packet DB entry +struct login_packet_db { + int16 len; ///< Packet length + LoginParseFunc **pFunc; ///< Packet parsing function +}; + +/// The login clif (client interface) interface. +struct lclif_interface { + struct lclif_interface_private *p; ///< Private interface + + /// Interface initialization. + void (*init)(void); + + /// Interface finalization. + void (*final)(void); + + /** + * Reports a connection error to the client. + * + * @param fd Client connection file descriptor. + * @param error Error code. + * @see #PACKET_SC_NOTIFY_BAN. + */ + void (*connection_error)(int fd, uint8 error); + + /** + * Sends the character server list to the client. + * + * @param sd The client to send to. + * @return Success status. + * @retval false in case of failure (no server available). + * @see #PACKET_AC_ACCEPT_LOGIN. + */ + bool (*server_list)(struct login_session_data *sd); + + /** + * Reports an authentication failure to the client. + * + * @param fd The client connection file descriptor. + * @param ban The ban duration (if error == 6). + * @param error The authentication error code. + * @see #PACKET_ID_AC_REFUSE_LOGIN. + * @see #PACKET_ID_AC_REFUSE_LOGIN_R2. + */ + void (*auth_failed)(int fd, time_t ban, uint32 error); + + /** + * Reports a login error to the client. + * + * @param fd Client connection file descriptor. + * @param error Error code. + * @see #PACKET_AC_REFUSE_LOGIN. + */ + void (*login_error)(int fd, uint8 error); + + /** + * Sends an authentication challenge to the client. + * + * @param fd Client connection file descriptor. + * @param sd The client to send to. + * @see #PACKET_AC_ACK_HASH. + */ + void (*coding_key)(int fd, struct login_session_data *sd); + + /** + * Retrieves a packet's data from the packet db. + * + * @param packet_id The packet id. + * @return The packet data. + * @retval NULL if the packet doesn't exist. + */ + const struct login_packet_db *(*packet)(int16 packet_id); + + /** + * Parses a packet. + * + * @param lpd Packet database entry. + * @param fd Client connection file descriptor. + * @param sd Session data. + * @return Parse result error code. + */ + enum parsefunc_rcode (*parse_packet)(const struct login_packet_db *lpd, int fd, struct login_session_data *sd); + + /** + * Packet parser loop function. + * + * Parses packets received from a client. + * + * @param fd Client connection file descriptor. + * @return error code. + * @retval 0 in case of success. + */ + int (*parse)(int fd); +}; + +#ifdef HERCULES_CORE +void lclif_defaults(void); +#endif + +HPShared struct lclif_interface *lclif; + +#endif // LOGIN_LCLIF_H diff --git a/src/login/lclif.p.h b/src/login/lclif.p.h new file mode 100644 index 000000000..ae9d1bc14 --- /dev/null +++ b/src/login/lclif.p.h @@ -0,0 +1,324 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2016 Hercules Dev Team + * + * Hercules is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef LOGIN_LCLIF_P_H +#define LOGIN_LCLIF_P_H + +/** @file + * Private header for the login client interface. + */ + +#include "login/lclif.h" + +#include "common/hercules.h" +#include "common/mmo.h" + +/* Definitions and macros */ +/// Maximum amount of packets processed at once from the same client +#define MAX_PROCESSED_PACKETS (3) + +// Packet DB +#define MIN_PACKET_DB 0x0064 +#define MAX_PACKET_DB 0x08ff + +/* Enums */ + +/// Packet IDs +enum login_packet_id { + // CA (Client to Login) + PACKET_ID_CA_LOGIN = 0x0064, + PACKET_ID_CA_LOGIN2 = 0x01dd, + PACKET_ID_CA_LOGIN3 = 0x01fa, + PACKET_ID_CA_CONNECT_INFO_CHANGED = 0x0200, + PACKET_ID_CA_EXE_HASHCHECK = 0x0204, + PACKET_ID_CA_LOGIN_PCBANG = 0x0277, + PACKET_ID_CA_LOGIN4 = 0x027c, + PACKET_ID_CA_LOGIN_HAN = 0x02b0, + PACKET_ID_CA_SSO_LOGIN_REQ = 0x0825, + PACKET_ID_CA_REQ_HASH = 0x01db, + PACKET_ID_CA_CHARSERVERCONNECT = 0x2710, // Custom Hercules Packet + //PACKET_ID_CA_SSO_LOGIN_REQa = 0x825a, /* unused */ + + // AC (Login to Client) + PACKET_ID_AC_ACCEPT_LOGIN = 0x0069, + PACKET_ID_AC_REFUSE_LOGIN = 0x006a, + PACKET_ID_SC_NOTIFY_BAN = 0x0081, + PACKET_ID_AC_ACK_HASH = 0x01dc, + PACKET_ID_AC_REFUSE_LOGIN_R2 = 0x083e, +}; + +/* Packets Structs */ +#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute +#pragma pack(push, 1) +#endif // not NetBSD < 6 / Solaris + +/** + * Packet structure for CA_LOGIN. + */ +struct packet_CA_LOGIN { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_LOGIN) + uint32 version; ///< Client Version + char id[24]; ///< Username + char password[24]; ///< Password + uint8 clienttype; ///< Client Type +} __attribute__((packed)); + +/** + * Packet structure for CA_LOGIN2. + */ +struct packet_CA_LOGIN2 { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_LOGIN2) + uint32 version; ///< Client Version + char id[24]; ///< Username + uint8 password_md5[16]; ///< Password hash + uint8 clienttype; ///< Client Type +} __attribute__((packed)); + +/** + * Packet structure for CA_LOGIN3. + */ +struct packet_CA_LOGIN3 { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_LOGIN3) + uint32 version; ///< Client Version + char id[24]; ///< Username + uint8 password_md5[16]; ///< Password hash + uint8 clienttype; ///< Client Type + uint8 clientinfo; ///< Index of the connection in the clientinfo file (+10 if the command-line contains "pc") +} __attribute__((packed)); + +/** + * Packet structure for CA_LOGIN4. + */ +struct packet_CA_LOGIN4 { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_LOGIN4) + uint32 version; ///< Client Version + char id[24]; ///< Username + uint8 password_md5[16]; ///< Password hash + uint8 clienttype; ///< Client Type + char mac_address[13]; ///< MAC Address +} __attribute__((packed)); + +/** + * Packet structure for CA_LOGIN_PCBANG. + */ +struct packet_CA_LOGIN_PCBANG { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_LOGIN_PCBANG) + uint32 version; ///< Client Version + char id[24]; ///< Username + char password[24]; ///< Password + uint8 clienttype; ///< Client Type + char ip[16]; ///< IP Address + char mac_address[13]; ///< MAC Address +} __attribute__((packed)); + +/** + * Packet structure for CA_LOGIN_HAN. + */ +struct packet_CA_LOGIN_HAN { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_LOGIN_HAN) + uint32 version; ///< Client Version + char id[24]; ///< Username + char password[24]; ///< Password + uint8 clienttype; ///< Client Type + char ip[16]; ///< IP Address + char mac_address[13]; ///< MAC Address + uint8 is_han_game_user; ///< 'isGravityID' +} __attribute__((packed)); + +/** + * Packet structure for CA_SSO_LOGIN_REQ. + * + * Variable-length packet. + */ +struct packet_CA_SSO_LOGIN_REQ { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_SSO_LOGIN_REQ) + int16 packet_len; ///< Length (variable length) + uint32 version; ///< Clientver + uint8 clienttype; ///< Clienttype + char id[24]; ///< Username + char password[27]; ///< Password + int8 mac_address[17]; ///< MAC Address + char ip[15]; ///< IP Address + char t1[]; ///< SSO Login Token (variable length) +} __attribute__((packed)); + +#if 0 // Unused +struct packet_CA_SSO_LOGIN_REQa { + int16 packet_id; + int16 packet_len; + uint32 version; + uint8 clienttype; + char id[24]; + int8 mac_address[17]; + char ip[15]; + char t1[]; +} __attribute__((packed)); +#endif // unused + +/** + * Packet structure for CA_CONNECT_INFO_CHANGED. + * + * New alive packet. Used to verify if client is always alive. + */ +struct packet_CA_CONNECT_INFO_CHANGED { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_CONNECT_INFO_CHANGED) + char id[24]; ///< account.userid +} __attribute__((packed)); + +/** + * Packet structure for CA_EXE_HASHCHECK. + * + * (kRO 2004-05-31aSakexe langtype 0 and 6) + */ +struct packet_CA_EXE_HASHCHECK { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_EXE_HASHCHECK) + uint8 hash_value[16]; ///< Client MD5 hash +} __attribute__((packed)); + +/** + * Packet structure for CA_REQ_HASH. + */ +struct packet_CA_REQ_HASH { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_REQ_HASH) +} __attribute__((packed)); + +/** + * Packet structure for CA_CHARSERVERCONNECT. + * + * This packet is used internally, to signal a char-server connection. + */ +struct packet_CA_CHARSERVERCONNECT { + int16 packet_id; ///< Packet ID (#PACKET_ID_CA_CHARSERVERCONNECT) + char userid[24]; ///< Username + char password[24]; ///< Password + int32 unknown; + int32 ip; ///< Charserver IP + int16 port; ///< Charserver port + char name[20]; ///< Charserver name + int16 unknown2; + int16 type; ///< Charserver type + int16 new; ///< Whether charserver is to be marked as new +} __attribute__((packed)); + +/** + * Packet structure for SC_NOTIFY_BAN. + */ +struct packet_SC_NOTIFY_BAN { + int16 packet_id; ///< Packet ID (#PACKET_ID_SC_NOTIFY_BAN) + uint8 error_code; ///< Error code +} __attribute__((packed)); + +/** + * Packet structure for AC_REFUSE_LOGIN. + */ +struct packet_AC_REFUSE_LOGIN { + int16 packet_id; ///< Packet ID (#PACKET_ID_AC_REFUSE_LOGIN) + uint8 error_code; ///< Error code + char block_date[20]; ///< Ban expiration date +} __attribute__((packed)); + +/** + * Packet structure for AC_REFUSE_LOGIN_R2. + */ +struct packet_AC_REFUSE_LOGIN_R2 { + int16 packet_id; ///< Packet ID (#PACKET_ID_AC_REFUSE_LOGIN_R2) + uint32 error_code; ///< Error code + char block_date[20]; ///< Ban expiration date +} __attribute__((packed)); + +/** + * Packet structure for AC_ACCEPT_LOGIN. + * + * Variable-length packet. + */ +struct packet_AC_ACCEPT_LOGIN { + int16 packet_id; ///< Packet ID (#PACKET_ID_AC_ACCEPT_LOGIN) + int16 packet_len; ///< Packet length (variable length) + int32 auth_code; ///< Authentication code + uint32 aid; ///< Account ID + uint32 user_level; ///< User level + uint32 last_login_ip; ///< Last login IP + char last_login_time[26]; ///< Last login timestamp + uint8 sex; ///< Account sex + struct { + uint32 ip; ///< Server IP address + int16 port; ///< Server port + char name[20]; ///< Server name + uint16 usercount; ///< Online users + uint16 state; ///< Server state + uint16 property; ///< Server property + } server_list[]; ///< List of charservers +} __attribute__((packed)); + +/** + * Packet structure for AC_ACK_HASH. + * + * Variable-length packet + */ +struct packet_AC_ACK_HASH { + int16 packet_id; ///< Packet ID (#PACKET_ID_AC_ACK_HASH) + int16 packet_len; ///< Packet length (variable length) + uint8 secret[]; ///< Challenge string +} __attribute__((packed)); + +#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute +#pragma pack(pop) +#endif // not NetBSD < 6 / Solaris + +/** + * Login Client Interface additional data + */ +struct lclif_interface_dbs { + struct login_packet_db packet_db[MAX_PACKET_DB + 1]; ///< Packet database. +}; + +/** + * Login Client Interface Private Interface + */ +struct lclif_interface_private { + struct lclif_interface_dbs *dbs; + + /** + * Populates the packet database. + */ + void (*packetdb_loaddb)(void); + + /** + * Attempts to validate and parse a received packet. + * + * @param fd Client connection file descriptor. + * @param sd Session data. + * @return Parse result error code. + */ + enum parsefunc_rcode (*parse_sub)(int fd, struct login_session_data *sd); + + LoginParseFunc *parse_CA_CONNECT_INFO_CHANGED; ///< Packet handler for #packet_CA_CONNECT_INFO_CHANGED. + LoginParseFunc *parse_CA_EXE_HASHCHECK; ///< Packet handler for #packet_CA_EXE_HASHCHECK. + LoginParseFunc *parse_CA_LOGIN; ///< Packet handler for #packet_CA_LOGIN. + LoginParseFunc *parse_CA_LOGIN2; ///< Packet handler for #packet_CA_LOGIN2. + LoginParseFunc *parse_CA_LOGIN3; ///< Packet handler for #packet_CA_LOGIN3. + LoginParseFunc *parse_CA_LOGIN4; ///< Packet handler for #packet_CA_LOGIN4. + LoginParseFunc *parse_CA_LOGIN_PCBANG; ///< Packet handler for #packet_CA_LOGIN_PCBANG. + LoginParseFunc *parse_CA_LOGIN_HAN; ///< Packet handler for #packet_CA_LOGIN_HAN. + LoginParseFunc *parse_CA_SSO_LOGIN_REQ; ///< Packet handler for #packet_CA_SSO_LOGIN_REQ. + LoginParseFunc *parse_CA_REQ_HASH; ///< Packet handler for #packet_CA_REQ_HASH. + LoginParseFunc *parse_CA_CHARSERVERCONNECT; ///< Packet handler for #packet_CA_CHARSERVERCONNECT. +}; + +#endif // LOGIN_LCLIF_P_H diff --git a/src/login/login.c b/src/login/login.c index f7babde86..d7c7321d8 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -26,6 +26,7 @@ #include "login/account.h" #include "login/ipban.h" #include "login/loginlog.h" +#include "login/lclif.h" #include "common/HPM.h" #include "common/cbasetypes.h" #include "common/core.h" @@ -43,6 +44,10 @@ #include <stdio.h> #include <stdlib.h> +/** @file + * Implementation of the login interface. + */ + struct login_interface login_s; struct login_interface *login; struct Login_Config login_config_; @@ -1180,14 +1185,6 @@ int login_mmo_auth(struct login_session_data* sd, bool isServer) { return -1; // account OK } -void login_connection_problem(int fd, uint8 status) -{ - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = status; - WFIFOSET(fd,3); -} - void login_kick(struct login_session_data* sd) { uint8 buf[6]; @@ -1201,9 +1198,7 @@ void login_auth_ok(struct login_session_data* sd) { int fd = 0; uint32 ip; - uint8 server_num, n; struct login_auth_node* node; - int i; nullpo_retv(sd); fd = sd->fd; @@ -1211,29 +1206,17 @@ void login_auth_ok(struct login_session_data* sd) if( core->runflag != LOGINSERVER_ST_RUNNING ) { // players can only login while running - login->connection_problem(fd, 1); // 01 = server closed + lclif->connection_error(fd, 1); // 01 = 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); - login->connection_problem(fd, 1); // 01 = server closed + lclif->connection_error(fd, 1); // 01 = 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); - login->connection_problem(fd, 1); // 01 = server closed - return; - } - - server_num = 0; - for( i = 0; i < ARRAYLENGTH(server); ++i ) - if (sockt->session_is_active(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); - login->connection_problem(fd, 1); // 01 = server closed + lclif->connection_error(fd, 1); // 01 = server closed return; } @@ -1248,7 +1231,7 @@ void login_auth_ok(struct login_session_data* sd) if( data->waiting_disconnect == INVALID_TIMER ) data->waiting_disconnect = timer->add(timer->gettick()+AUTH_TIMEOUT, login->waiting_disconnect_timer, sd->account_id, 0); - login->connection_problem(fd, 8); // 08 = Server still recognizes your last login + lclif->connection_error(fd, 8); // 08 = Server still recognizes your last login return; } else @@ -1262,42 +1245,16 @@ void login_auth_ok(struct login_session_data* sd) } } + if (!lclif->server_list(sd)) { + // 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); + lclif->connection_error(fd, 1); // 01 = server closed + return; + } + login_log(ip, sd->userid, 100, "login ok"); ShowStatus("Connection of the account '%s' accepted.\n", sd->userid); - WFIFOHEAD(fd,47+32*server_num); - WFIFOW(fd,0) = 0x69; - WFIFOW(fd,2) = 47+32*server_num; - WFIFOL(fd,4) = sd->login_id1; - WFIFOL(fd,8) = sd->account_id; - WFIFOL(fd,12) = sd->login_id2; - WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used) - //memcpy(WFIFOP(fd,20), sd->lastlogin, 24); // in old version, that was for name (not more used) - memset(WFIFOP(fd,20), 0, 24); - WFIFOW(fd,44) = 0; // unknown - WFIFOB(fd,46) = sex_str2num(sd->sex); - for (i = 0, n = 0; i < ARRAYLENGTH(server); ++i) { - uint32 subnet_char_ip; - - if (!sockt->session_is_valid(server[i].fd)) - continue; - - subnet_char_ip = login->lan_subnet_check(ip); - WFIFOL(fd,47+n*32) = htonl((subnet_char_ip) ? subnet_char_ip : server[i].ip); - WFIFOW(fd,47+n*32+4) = sockt->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; - - if( server[i].type == CST_PAYING && sd->expiration_time > time(NULL) ) - WFIFOW(fd,47+n*32+28) = CST_NORMAL; - else - WFIFOW(fd,47+n*32+28) = server[i].type; - - WFIFOW(fd,47+n*32+30) = server[i].new_; - n++; - } - WFIFOSET(fd,47+32*server_num); - // create temporary auth entry CREATE(node, struct login_auth_node, 1); node->account_id = sd->account_id; @@ -1322,10 +1279,11 @@ void login_auth_ok(struct login_session_data* sd) } } -void login_auth_failed(struct login_session_data* sd, int result) +void login_auth_failed(struct login_session_data *sd, int result) { int fd; uint32 ip; + time_t ban_time = 0; nullpo_retv(sd); fd = sd->fd; @@ -1364,121 +1322,23 @@ void login_auth_failed(struct login_session_data* sd, int result) if (result == 1 && login->config->dynamic_pass_failure_ban && !sockt->trusted_ip_check(ip)) ipban_log(ip); // log failed password attempt -#if PACKETVER >= 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; - time_t unban_time = ( accounts->load_str(accounts, &acc, sd->userid) ) ? acc.unban_time : 0; - timestamp2string(WFIFOP(fd,6), 20, unban_time, login->config->date_format); - } - WFIFOSET(fd,26); -#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; - time_t unban_time = ( accounts->load_str(accounts, &acc, sd->userid) ) ? acc.unban_time : 0; - timestamp2string(WFIFOP(fd,3), 20, unban_time, login->config->date_format); + if (result == 6) { + struct mmo_account acc = { 0 }; + if (accounts->load_str(accounts, &acc, sd->userid)) + ban_time = acc.unban_time; } - WFIFOSET(fd,23); -#endif -} - -void login_login_error(int fd, uint8 status) -{ - WFIFOHEAD(fd,23); - WFIFOW(fd,0) = 0x6a; - WFIFOB(fd,2) = status; - WFIFOSET(fd,23); -} - -void login_parse_ping(int fd, struct login_session_data* sd) __attribute__((nonnull (2))); -void login_parse_ping(int fd, struct login_session_data* sd) -{ - RFIFOSKIP(fd,26); -} - -void login_parse_client_md5(int fd, struct login_session_data* sd) __attribute__((nonnull (2))); -void login_parse_client_md5(int fd, struct login_session_data* sd) -{ - sd->has_client_hash = 1; - memcpy(sd->client_hash, RFIFOP(fd, 2), 16); - - RFIFOSKIP(fd,18); + lclif->auth_failed(fd, ban_time, result); } -bool login_parse_client_login(int fd, struct login_session_data* sd, const char *const ip) __attribute__((nonnull (2))); -bool login_parse_client_login(int fd, struct login_session_data* sd, const char *const ip) +bool login_client_login(int fd, struct login_session_data *sd) __attribute__((nonnull (2))); +bool login_client_login(int fd, struct login_session_data *sd) { - uint32 version; - char username[NAME_LENGTH]; - char password[PASSWD_LEN]; - unsigned char passhash[16]; - uint8 clienttype; int result; - uint16 command = RFIFOW(fd,0); - bool israwpass = (command==0x0064 || command==0x0277 || command==0x02b0 || command == 0x0825); - - // Shinryo: For the time being, just use token as password. - if(command == 0x0825) - { - const char *accname = RFIFOP(fd, 9); - const char *token = RFIFOP(fd, 0x5C); - size_t uAccLen = strlen(accname); - size_t uTokenLen = RFIFOREST(fd) - 0x5C; - - version = RFIFOL(fd,4); - - if(uAccLen <= 0 || uTokenLen <= 0) { - login->auth_failed(sd, 3); - return true; - } - - safestrncpy(username, accname, NAME_LENGTH); - safestrncpy(password, token, min(uTokenLen+1, PASSWD_LEN)); // Variable-length field, don't copy more than necessary - clienttype = RFIFOB(fd, 8); - } - else - { - version = RFIFOL(fd,2); - safestrncpy(username, RFIFOP(fd,6), NAME_LENGTH); - if( israwpass ) - { - safestrncpy(password, RFIFOP(fd,30), NAME_LENGTH); - clienttype = RFIFOB(fd,54); - } - else - { - memcpy(passhash, RFIFOP(fd,30), 16); - clienttype = RFIFOB(fd,46); - } - } - RFIFOSKIP(fd,RFIFOREST(fd)); // assume no other packet was sent + char ip[16]; + uint32 ipl = sockt->session[fd]->client_addr; + sockt->ip2str(ipl, ip); - sd->clienttype = clienttype; - sd->version = version; - safestrncpy(sd->userid, username, NAME_LENGTH); - if( israwpass ) - { - ShowStatus("Request for connection of %s (ip: %s).\n", sd->userid, ip); - safestrncpy(sd->passwd, password, PASSWD_LEN); - if (login->config->use_md5_passwds) - MD5_String(sd->passwd, sd->passwd); - sd->passwdenc = PWENC_NONE; - } - else - { - ShowStatus("Request for connection (passwdenc mode) of %s (ip: %s).\n", sd->userid, ip); - bin2hex(sd->passwd, passhash, 16); // raw binary data here! - sd->passwdenc = PASSWORDENC; - } + ShowStatus("Request for connection %sof %s (ip: %s).\n", sd->passwdenc == PASSWORDENC ? " (passwdenc mode)" : "", sd->userid, ip); if (sd->passwdenc != PWENC_NONE && login->config->use_md5_passwds) { login->auth_failed(sd, 3); // send "rejected from server" @@ -1486,32 +1346,12 @@ bool login_parse_client_login(int fd, struct login_session_data* sd, const char } result = login->mmo_auth(sd, false); - if( result == -1 ) login->auth_ok(sd); else login->auth_failed(sd, result); - return false; -} -void login_send_coding_key(int fd, struct login_session_data* sd) __attribute__((nonnull (2))); -void login_send_coding_key(int fd, struct login_session_data* sd) -{ - WFIFOHEAD(fd,4 + sd->md5keylen); - WFIFOW(fd,0) = 0x01dc; - WFIFOW(fd,2) = 4 + sd->md5keylen; - memcpy(WFIFOP(fd,4), sd->md5key, sd->md5keylen); - WFIFOSET(fd,WFIFOW(fd,2)); -} - -void login_parse_request_coding_key(int fd, struct login_session_data* sd) __attribute__((nonnull (2))); -void login_parse_request_coding_key(int fd, struct login_session_data* sd) -{ - memset(sd->md5key, '\0', sizeof(sd->md5key)); - sd->md5keylen = (uint16)(12 + rnd() % 4); - MD5_Salt(sd->md5keylen, sd->md5key); - - login->send_coding_key(fd, sd); + return false; } void login_char_server_connection_status(int fd, struct login_session_data* sd, uint8 status) __attribute__((nonnull (2))); @@ -1523,6 +1363,7 @@ void login_char_server_connection_status(int fd, struct login_session_data* sd, WFIFOSET(fd,3); } +// CA_CHARSERVERCONNECT void login_parse_request_connection(int fd, struct login_session_data* sd, const char *const ip, uint32 ipl) __attribute__((nonnull (2, 3))); void login_parse_request_connection(int fd, struct login_session_data* sd, const char *const ip, uint32 ipl) { @@ -1545,7 +1386,6 @@ void login_parse_request_connection(int fd, struct login_session_data* sd, const safestrncpy(server_name, RFIFOP(fd,60), 20); type = RFIFOW(fd,82); new_ = RFIFOW(fd,84); - RFIFOSKIP(fd,86); ShowInfo("Connection request of the char-server '%s' @ %u.%u.%u.%u:%u (account: '%s', pass: '%s', ip: '%s')\n", server_name, CONVIP(server_ip), server_port, sd->userid, sd->passwd, ip); sprintf(message, "charserver - %s@%u.%u.%u.%u:%u", server_name, CONVIP(server_ip), server_port); @@ -1583,121 +1423,6 @@ void login_parse_request_connection(int fd, struct login_session_data* sd, const } } -//---------------------------------------------------------------------------------------- -// Default packet parsing (normal players or char-server connection requests) -//---------------------------------------------------------------------------------------- -int login_parse_login(int fd) -{ - struct login_session_data* sd = (struct login_session_data*)sockt->session[fd]->session_data; - - char ip[16]; - uint32 ipl = sockt->session[fd]->client_addr; - sockt->ip2str(ipl, ip); - - if( sockt->session[fd]->flag.eof ) - { - ShowInfo("Closed connection from '"CL_WHITE"%s"CL_RESET"'.\n", ip); - sockt->close(fd); - return 0; - } - - if( sd == NULL ) - { - // Perform ip-ban check - if (login->config->ipban && !sockt->trusted_ip_check(ipl) && ipban_check(ipl)) { - ShowStatus("Connection refused: IP isn't authorized (deny/allow, ip: %s).\n", ip); - login_log(ipl, "unknown", -3, "ip banned"); - login->login_error(fd, 3); // 3 = Rejected from Server - sockt->eof(fd); - return 0; - } - - // create a session for this new connection - CREATE(sockt->session[fd]->session_data, struct login_session_data, 1); - sd = (struct login_session_data*)sockt->session[fd]->session_data; - sd->fd = fd; - } - - while (RFIFOREST(fd) >= 2) { - uint16 command = RFIFOW(fd,0); - - if (VECTOR_LENGTH(HPM->packets[hpParse_Login]) > 0) { - int result = HPM->parse_packets(fd,command,hpParse_Login); - if (result == 1) - continue; - if (result == 2) - return 0; - } - - switch (command) { - - case 0x0200: // New alive packet: structure: 0x200 <account.userid>.24B. used to verify if client is always alive. - if (RFIFOREST(fd) < 26) - return 0; - login->parse_ping(fd, sd); - break; - - // client md5 hash (binary) - case 0x0204: // S 0204 <md5 hash>.16B (kRO 2004-05-31aSakexe langtype 0 and 6) - if (RFIFOREST(fd) < 18) - return 0; - - login->parse_client_md5(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 - { - size_t packet_len = RFIFOREST(fd); - - if( (command == 0x0064 && packet_len < 55) - || (command == 0x0277 && packet_len < 84) - || (command == 0x02b0 && packet_len < 85) - || (command == 0x01dd && packet_len < 47) - || (command == 0x01fa && packet_len < 48) - || (command == 0x027c && packet_len < 60) - || (command == 0x0825 && (packet_len < 4 || packet_len < RFIFOW(fd, 2))) ) - return 0; - } - { - if (login->parse_client_login(fd, sd, ip)) - return 0; - } - break; - - case 0x01db: // Sending request of the coding key - RFIFOSKIP(fd,2); - { - login->parse_request_coding_key(fd, sd); - } - break; - - case 0x2710: // Connection request of a char-server - if (RFIFOREST(fd) < 86) - return 0; - { - login->parse_request_connection(fd, sd, ip, ipl); - } - return 0; // processing will continue elsewhere - - default: - ShowNotice("Abnormal end of connection (ip: %s): Unknown packet 0x%x\n", ip, command); - sockt->eof(fd); - return 0; - } - } - - return 0; -} - - void login_config_set_defaults(void) { login->config->login_ip = INADDR_ANY; @@ -1892,6 +1617,8 @@ int do_final(void) { login->fd = -1; } + lclif->final(); + HPM_login_do_final(); aFree(login->LOGIN_CONF_NAME); @@ -1981,6 +1708,7 @@ int do_init(int argc, char** argv) } login_defaults(); + lclif_defaults(); // read login-server configuration login->config_set_defaults(); @@ -1988,6 +1716,8 @@ int do_init(int argc, char** argv) login->LOGIN_CONF_NAME = aStrdup("conf/login-server.conf"); login->NET_CONF_NAME = aStrdup("conf/network.conf"); + lclif->init(); + HPM_login_do_init(); cmdline->exec(argc, argv, CMDLINE_OPT_PREINIT); HPM->config_read(); @@ -2014,8 +1744,8 @@ int do_init(int argc, char** argv) // Interserver auth init login->auth_db = idb_alloc(DB_OPT_RELEASE_DATA); - // set default parser as login_parse_login function - sockt->set_defaultparse(login->parse_login); + // set default parser as lclif->parse function + sockt->set_defaultparse(lclif->parse); // every 10 minutes cleanup online account db. timer->add_func_list(login->online_data_cleanup, "login->online_data_cleanup"); @@ -2107,19 +1837,12 @@ void login_defaults(void) { login->fromchar_parse_accinfo = login_fromchar_parse_accinfo; login->parse_fromchar = login_parse_fromchar; - login->parse_login = login_parse_login; - login->parse_ping = login_parse_ping; - login->parse_client_md5 = login_parse_client_md5; - login->parse_client_login = login_parse_client_login; - login->parse_request_coding_key = login_parse_request_coding_key; + login->client_login = login_client_login; login->parse_request_connection = login_parse_request_connection; login->auth_ok = login_auth_ok; login->auth_failed = login_auth_failed; login->char_server_connection_status = login_char_server_connection_status; - login->connection_problem = login_connection_problem; login->kick = login_kick; - login->login_error = login_login_error; - login->send_coding_key = login_send_coding_key; login->config_set_defaults = login_config_set_defaults; login->config_read = login_config_read; diff --git a/src/login/login.h b/src/login/login.h index 1aca47c85..36085ae91 100644 --- a/src/login/login.h +++ b/src/login/login.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -26,6 +26,10 @@ #include "common/db.h" #include "common/mmo.h" // NAME_LENGTH,SEX_* +/** @file + * Login interface. + */ + struct mmo_account; struct AccountDB; @@ -196,19 +200,12 @@ struct login_interface { bool (*fromchar_parse_wrong_pincode) (int fd); void (*fromchar_parse_accinfo) (int fd); int (*parse_fromchar) (int fd); - void (*connection_problem) (int fd, uint8 status); void (*kick) (struct login_session_data* sd); void (*auth_ok) (struct login_session_data* sd); void (*auth_failed) (struct login_session_data* sd, int result); - void (*login_error) (int fd, uint8 status); - void (*parse_ping) (int fd, struct login_session_data* sd); - void (*parse_client_md5) (int fd, struct login_session_data* sd); - bool (*parse_client_login) (int fd, struct login_session_data* sd, const char *ip); - void (*send_coding_key) (int fd, struct login_session_data* sd); - void (*parse_request_coding_key) (int fd, struct login_session_data* sd); + bool (*client_login) (int fd, struct login_session_data *sd); void (*char_server_connection_status) (int fd, struct login_session_data* sd, uint8 status); void (*parse_request_connection) (int fd, struct login_session_data* sd, const char *ip, uint32 ipl); - int (*parse_login) (int fd); void (*config_set_defaults) (void); int (*config_read) (const char *cfgName); char *LOGIN_CONF_NAME; |