summaryrefslogtreecommitdiff
path: root/src/login
diff options
context:
space:
mode:
authorIbrahem Hossam <hemagx2@gmail.com>2016-04-16 15:37:47 +0200
committerIbrahem Hossam <hemagx2@gmail.com>2016-04-16 15:37:47 +0200
commitbbcb0406e5a430d1c85b3448f88a72f342530cf0 (patch)
treecc2348d9a18ea4d7e462e0247aeb8d23a1808abf /src/login
parentc7410453a15110a12d9fffb06b70e9557fb9f73a (diff)
parent8448e3f8cdcbf820e304a68c7507a0ed96d53544 (diff)
downloadhercules-bbcb0406e5a430d1c85b3448f88a72f342530cf0.tar.gz
hercules-bbcb0406e5a430d1c85b3448f88a72f342530cf0.tar.bz2
hercules-bbcb0406e5a430d1c85b3448f88a72f342530cf0.tar.xz
hercules-bbcb0406e5a430d1c85b3448f88a72f342530cf0.zip
Merge pull request #1255 from HerculesWS/login-clif_rewrite
Diffstat (limited to 'src/login')
-rw-r--r--src/login/HPMlogin.c2
-rw-r--r--src/login/Makefile.in25
-rw-r--r--src/login/lclif.c566
-rw-r--r--src/login/lclif.h150
-rw-r--r--src/login/lclif.p.h324
-rw-r--r--src/login/login.c357
-rw-r--r--src/login/login.h15
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;