Last active
June 25, 2023 07:45
-
-
Save vstumpf/47051f55009bffbbb42462549de4b9a5 to your computer and use it in GitHub Desktop.
The diff for trogproxy for rathena servers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/db/import-tmpl/proxy_servers.yml b/db/import-tmpl/proxy_servers.yml | |
new file mode 100644 | |
index 000000000..6f8996863 | |
--- /dev/null | |
+++ b/db/import-tmpl/proxy_servers.yml | |
@@ -0,0 +1,25 @@ | |
+########################################################################### | |
+# Proxy Database | |
+########################################################################### | |
+# | |
+# Proxy Settings | |
+# | |
+########################################################################### | |
+# - Name Name of the server, only used for logging. Can | |
+# be anything, doesn't have to be unique. | |
+# ProxyServer: | |
+# PublicIP: Public IP or Hostname of the proxy server | |
+# Port: Port of the proxy server | |
+# LocalIP: IP to check for in case of subnet issues, docker containers, etc | |
+# only read by proxy server | |
+# TargetServer: | |
+# PublicIP: Public IP or Hostname of the destination server | |
+# Port: Port of the destination server | |
+# LocalIP: IP to check for in case of subnet issues, docker containers, etc | |
+# only read by target servers | |
+# Secret: Secret to send to server | |
+########################################################################### | |
+ | |
+Header: | |
+ Type: PROXY_DB | |
+ Version: 1 | |
diff --git a/db/proxy_servers.yml b/db/proxy_servers.yml | |
new file mode 100644 | |
index 000000000..49e7b09cf | |
--- /dev/null | |
+++ b/db/proxy_servers.yml | |
@@ -0,0 +1,60 @@ | |
+########################################################################### | |
+# Proxy Database | |
+########################################################################### | |
+# | |
+# Proxy Settings | |
+# | |
+########################################################################### | |
+# - Name Name of the server, only used for logging. Can | |
+# be anything, doesn't have to be unique. | |
+# ProxyServer: | |
+# PublicIP: Public IP or Hostname of the proxy server | |
+# Port: Port of the proxy server | |
+# LocalIP: IP to check for in case of subnet issues, docker containers, etc | |
+# only read by proxy server (optional) | |
+# TargetServer: | |
+# PublicIP: Public IP or Hostname of the destination server | |
+# Port: Port of the destination server | |
+# LocalIP: IP to check for in case of subnet issues, docker containers, etc | |
+# only read by target servers (optional) | |
+# Secret: Secret to send to server | |
+########################################################################### | |
+ | |
+Header: | |
+ Type: PROXY_DB | |
+ Version: 1 | |
+ | |
+Body: | |
+ # - Name: "login-server" | |
+ # ProxyServer: | |
+ # PublicIP: "11.22.33.44" | |
+ # Port: 6900 | |
+ # LocalIP: "172.1.2.3" | |
+ # TargetServer: | |
+ # PublicIP: "myroserver.com" | |
+ # Port: 6900 | |
+ # Secret: "12345" | |
+ | |
+ # - Name: "char-server" | |
+ # ProxyServer: | |
+ # PublicIP: "11.22.33.44" | |
+ # Port: 6121 | |
+ # LocalIP: "172.1.2.3" | |
+ # TargetServer: | |
+ # PublicIP: "myroserver.com" | |
+ # Port: 6121 | |
+ # Secret: "34245" | |
+ | |
+ # - Name: "map-server" | |
+ # ProxyServer: | |
+ # PublicIP: "11.22.33.44" | |
+ # Port: 5121 | |
+ # LocalIP: "172.1.2.3" | |
+ # TargetServer: | |
+ # PublicIP: "myroserver.com" | |
+ # Port: 5121 | |
+ # Secret: "151992" | |
+ | |
+Footer: | |
+ Imports: | |
+ - Path: db/import/proxy_servers.yml | |
\ No newline at end of file | |
diff --git a/src/char/char.cpp b/src/char/char.cpp | |
index 82b27799a..4aab2673c 100644 | |
--- a/src/char/char.cpp | |
+++ b/src/char/char.cpp | |
@@ -3247,6 +3247,7 @@ int do_init(int argc, char **argv) | |
do_init_chlogif(); | |
do_init_chmapif(); | |
+ do_init_chclif(); | |
// periodically update the overall user count on all mapservers + login server | |
add_timer_func_list(chlogif_broadcast_user_count, "broadcast_user_count"); | |
diff --git a/src/char/char_clif.cpp b/src/char/char_clif.cpp | |
index b3b27f6fe..57c4f0135 100644 | |
--- a/src/char/char_clif.cpp | |
+++ b/src/char/char_clif.cpp | |
@@ -6,6 +6,7 @@ | |
#include <stdlib.h> | |
#include <string.h> | |
+#include "../common/proxy.hpp" | |
#include "../common/malloc.hpp" | |
#include "../common/mapindex.hpp" | |
#include "../common/mmo.hpp" | |
@@ -712,6 +713,8 @@ int chclif_parse_reqtoconnect(int fd, struct char_session_data* sd,uint32 ipl){ | |
int sex = RFIFOB(fd,16); | |
RFIFOSKIP(fd,17); | |
+ session[fd]->flag.parsed = 1; | |
+ | |
ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", account_id, login_id1, login_id2); | |
if (sd) { | |
@@ -794,8 +797,19 @@ void chclif_send_map_data( int fd, struct mmo_charstatus *cd, uint32 ipl, int ma | |
WFIFOL(fd,2) = cd->char_id; | |
mapindex_getmapname_ext(mapindex_id2name(cd->last_point.map), WFIFOCP(fd,6)); | |
uint32 subnet_map_ip = char_lan_subnetcheck(ipl); // Advanced subnet check [LuzZza] | |
- WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : map_server[map_server_index].ip); | |
- WFIFOW(fd,26) = ntows(htons(map_server[map_server_index].port)); // [!] LE byte order here [!] | |
+ auto * proxy = proxy_db.searchTargetIPPortProxyIp(map_server[map_server_index].ip, map_server[map_server_index].port, session[fd]->proxy_addr); | |
+ uint32 destip = map_server[map_server_index].ip; | |
+ uint16 destport = htons(map_server[map_server_index].port); | |
+ if (subnet_map_ip) { | |
+ destip = subnet_map_ip; | |
+ destport = htons(map_server[map_server_index].port); | |
+ } | |
+ if (proxy) { | |
+ destip = proxy->proxy.public_ip; | |
+ destport = ntohs(proxy->proxy.port); | |
+ } | |
+ WFIFOL(fd,22) = htonl(destip); | |
+ WFIFOW(fd,26) = ntows(destport); // [!] LE byte order here [!] | |
#if PACKETVER >= 20170315 | |
memset(WFIFOP(fd, 28), 0, 128); // Unknown | |
#endif | |
@@ -1417,6 +1431,8 @@ int chclif_parse(int fd) { | |
// character movement request | |
case 0x8d4: next=chclif_parse_moveCharSlot(fd,sd); break; | |
case 0x9a1: next=chclif_parse_req_charlist(fd,sd); break; | |
+ | |
+ case 0xbeef: next=clif_parse_proxy(fd); break; | |
// unknown packet received | |
default: | |
ShowError("parse_char: Received unknown packet " CL_WHITE "0x%x" CL_RESET " from ip '" CL_WHITE "%s" CL_RESET "'! Disconnecting!\n", RFIFOW(fd,0), ip2str(ipl, NULL)); | |
@@ -1430,3 +1446,23 @@ int chclif_parse(int fd) { | |
RFIFOFLUSH(fd); | |
return 0; | |
} | |
+ | |
+/// Constructor destructor | |
+ | |
+/** | |
+ * Initialize the module. | |
+ * Launched at char-serv start, create db or other long scope variable here. | |
+ */ | |
+void do_init_chclif(void){ | |
+ proxy_db.load(); | |
+ proxy_db.filterTargetIPPort(charserv_config.char_ip, charserv_config.char_port); | |
+ return; | |
+} | |
+ | |
+/** | |
+ * chclif destructor | |
+ * dealloc..., function called at exit of the char-serv | |
+ */ | |
+void do_final_chclif(void){ | |
+ return; | |
+} | |
diff --git a/src/char/char_clif.hpp b/src/char/char_clif.hpp | |
index 944cf500c..aa402c8c3 100644 | |
--- a/src/char/char_clif.hpp | |
+++ b/src/char/char_clif.hpp | |
@@ -53,4 +53,8 @@ void chclif_block_character( int fd, struct char_session_data* sd); | |
int chclif_parse(int fd); | |
+ | |
+void do_init_chclif(void); | |
+void do_final_chclif(void); | |
+ | |
#endif /* CHAR_CLIF_HPP */ | |
diff --git a/src/common/Makefile.in b/src/common/Makefile.in | |
index 29425a623..2b5011307 100644 | |
--- a/src/common/Makefile.in | |
+++ b/src/common/Makefile.in | |
@@ -1,7 +1,7 @@ | |
COMMON_OBJ = core.o socket.o timer.o db.o nullpo.o malloc.o showmsg.o strlib.o utils.o utilities.o \ | |
grfio.o mapindex.o ers.o md5calc.o minicore.o minisocket.o minimalloc.o random.o des.o \ | |
- conf.o msg_conf.o cli.o sql.o database.o | |
+ conf.o msg_conf.o cli.o sql.o database.o proxy.o | |
COMMON_DIR_OBJ = $(COMMON_OBJ:%=obj/%) | |
COMMON_H = $(shell ls ../common/*.hpp) | |
COMMON_AR = obj/common.a | |
@@ -70,9 +70,9 @@ obj/%.o: %.cpp $(COMMON_H) $(LIBCONFIG_H) $(RAPIDYAML_H) | |
@echo " CXX $<" | |
@@CXX@ @CXXFLAGS@ @CFLAGS_AR@ $(LIBCONFIG_INCLUDE) $(RAPIDYAML_INCLUDE) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< | |
-obj/mini%.o: %.cpp $(COMMON_H) $(LIBCONFIG_H) $(YAML_CPP_H) | |
+obj/mini%.o: %.cpp $(COMMON_H) $(LIBCONFIG_H) $(YAML_CPP_H) $(RAPIDYAML_H) | |
@echo " CXX $<" | |
- @@CXX@ @CXXFLAGS@ @CFLAGS_AR@ $(LIBCONFIG_INCLUDE) $(YAML_CPP_INCLUDE) @MYSQL_CFLAGS@ -DMINICORE @CPPFLAGS@ -c $(OUTPUT_OPTION) $< | |
+ @@CXX@ @CXXFLAGS@ @CFLAGS_AR@ $(LIBCONFIG_INCLUDE) $(RAPIDYAML_INCLUDE) $(YAML_CPP_INCLUDE) @MYSQL_CFLAGS@ -DMINICORE @CPPFLAGS@ -c $(OUTPUT_OPTION) $< | |
# missing object files | |
$(LIBCONFIG_AR): | |
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj | |
index 182c7c2ef..f170fb91d 100644 | |
--- a/src/common/common.vcxproj | |
+++ b/src/common/common.vcxproj | |
@@ -34,6 +34,7 @@ | |
<ClInclude Include="mmo.hpp" /> | |
<ClInclude Include="msg_conf.hpp" /> | |
<ClInclude Include="nullpo.hpp" /> | |
+ <ClInclude Include="proxy.hpp" /> | |
<ClInclude Include="random.hpp" /> | |
<ClInclude Include="showmsg.hpp" /> | |
<ClInclude Include="socket.hpp" /> | |
@@ -58,6 +59,7 @@ | |
<ClCompile Include="md5calc.cpp" /> | |
<ClCompile Include="msg_conf.cpp" /> | |
<ClCompile Include="nullpo.cpp" /> | |
+ <ClCompile Include="proxy.cpp" /> | |
<ClCompile Include="random.cpp" /> | |
<ClCompile Include="showmsg.cpp" /> | |
<ClCompile Include="socket.cpp" /> | |
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters | |
index 8aff9e447..b935a8613 100644 | |
--- a/src/common/common.vcxproj.filters | |
+++ b/src/common/common.vcxproj.filters | |
@@ -56,6 +56,9 @@ | |
<ClInclude Include="nullpo.hpp"> | |
<Filter>Header Files</Filter> | |
</ClInclude> | |
+ <ClInclude Include="proxy.hpp"> | |
+ <Filter>Header Files</Filter> | |
+ </ClInclude> | |
<ClInclude Include="random.hpp"> | |
<Filter>Header Files</Filter> | |
</ClInclude> | |
@@ -124,6 +127,9 @@ | |
<ClCompile Include="nullpo.cpp"> | |
<Filter>Source Files</Filter> | |
</ClCompile> | |
+ <ClCompile Include="proxy.cpp"> | |
+ <Filter>Source Files</Filter> | |
+ </ClCompile> | |
<ClCompile Include="random.cpp"> | |
<Filter>Source Files</Filter> | |
</ClCompile> | |
diff --git a/src/common/proxy.cpp b/src/common/proxy.cpp | |
new file mode 100644 | |
index 000000000..6850f24d8 | |
--- /dev/null | |
+++ b/src/common/proxy.cpp | |
@@ -0,0 +1,238 @@ | |
+#include "proxy.hpp" | |
+ | |
+#include <algorithm> | |
+#include <vector> | |
+#include <stdio.h> | |
+ | |
+#ifdef WIN32 | |
+ #include "winapi.hpp" | |
+ #include <winsock2.h> | |
+ #include <iphlpapi.h> | |
+ #pragma comment(lib, "IPHLPAPI.lib") | |
+#else | |
+ #include <arpa/inet.h> | |
+ #include <sys/socket.h> | |
+ #include <netdb.h> | |
+ #include <ifaddrs.h> | |
+ #include <unistd.h> | |
+ #include <linux/if_link.h> | |
+#endif | |
+#include "core.hpp" | |
+#include "malloc.hpp" | |
+#include "utils.hpp" | |
+#include "socket.hpp" | |
+#include "showmsg.hpp" | |
+ | |
+ProxyDatabase proxy_db; | |
+ | |
+bool ProxyDatabase::asIP(const c4::yml::NodeRef &node, const std::string &name, uint32 &out) { | |
+ std::string ips; | |
+ bool ret = this->asString(node, name, ips); | |
+ if (!ret) | |
+ return ret; | |
+ out = host2ip(ips.c_str()); | |
+ return true; | |
+} | |
+ | |
+bool ProxyDatabase::asServerInfo(const ryml::NodeRef& node, const std::string &name, struct ServerInfo &out) { | |
+ if (!this->nodeExists(node, name)) { | |
+ ShowError("Missing %s node\n", name.c_str()); | |
+ return false; | |
+ } | |
+ auto proxyNode = node[c4::to_csubstr(name)]; | |
+ | |
+ if (!this->asIP(proxyNode, "PublicIP", out.public_ip)) | |
+ return false; | |
+ if (!this->asUInt16(proxyNode, "Port", out.port)) | |
+ return false; | |
+ if (this->nodeExists(proxyNode, "LocalIP") && !this->asIP(proxyNode, "LocalIP", out.local_ip)) | |
+ return false; | |
+ | |
+ return true; | |
+} | |
+ | |
+/** | |
+ * Reads and parses an entry from the proxy_db. | |
+ * @param node: YAML node containing the entry. | |
+ * @return count of successfully parsed rows | |
+ */ | |
+uint64 ProxyDatabase::parseBodyNode(const ryml::NodeRef& node) { | |
+ Proxy proxy = {}; | |
+ | |
+ if (this->nodeExists(node, "Name")) { | |
+ if (!this->asString(node, "Name", proxy.name)) | |
+ return 0; | |
+ } | |
+ if (!this->asServerInfo(node, "ProxyServer", proxy.proxy)) | |
+ return 0; | |
+ if (!this->asServerInfo(node, "TargetServer", proxy.target)) | |
+ return 0; | |
+ if (!this->asString(node, "Secret", proxy.secret)) | |
+ return 0; | |
+ | |
+ if (proxy.secret.length() >= PROXY_SECRET_LEN) { | |
+ ShowError("Secret %s must be less than %d characters", proxy.secret.c_str(), PROXY_SECRET_LEN - 1); | |
+ return 0; | |
+ } | |
+ | |
+ proxyServers.push_back(proxy); | |
+ return 1; | |
+} | |
+ | |
+const Proxy* ProxyDatabase::searchProxyIp(uint32 ip) { | |
+ auto result = std::find_if(listenServers.cbegin(), listenServers.cend(), [&](const Proxy &proxy){ | |
+ return proxy.proxy.public_ip == ip; | |
+ }); | |
+ | |
+ if (result == listenServers.end()) | |
+ return nullptr; | |
+ return &*result; | |
+} | |
+ | |
+const Proxy* ProxyDatabase::searchTargetIPPortProxyIp(uint32 target_ip, uint16 target_port, uint32 proxy_ip) { | |
+ ShowInfo("Searching for target_ip %s\n", ip2str(target_ip, nullptr)); | |
+ ShowInfo("Searching for target_port %hu\n", target_port); | |
+ ShowInfo("Searching for proxy_ip %s\n", ip2str(proxy_ip, nullptr)); | |
+ auto result = std::find_if(proxyServers.cbegin(), proxyServers.cend(), [&](const Proxy &proxy){ | |
+ return (proxy.target.public_ip == target_ip || proxy.target.local_ip == target_ip) | |
+ && proxy.target.port == target_port | |
+ && proxy.proxy.public_ip == proxy_ip; | |
+ }); | |
+ | |
+ if (result == proxyServers.end()) | |
+ return nullptr; | |
+ return &*result; | |
+} | |
+ | |
+ | |
+// if ip == 0, use getifaddr | |
+void ProxyDatabase::filterTargetIPPort(uint32 ip, uint16 port) { | |
+ ShowInfo("Matching ip %s and port %lu\n", ip2str(ip, nullptr), port); | |
+ ShowInfo("List of loaded proxy servers:\n"); | |
+ for (const auto &proxy : proxyServers) { | |
+ ShowInfo("%s : %s:%d\n", proxy.name.c_str(), ip2str(proxy.proxy.public_ip, nullptr), proxy.proxy.port); | |
+ } | |
+ | |
+ if (ip) { | |
+ std::copy_if(proxyServers.begin(), proxyServers.end(), std::back_inserter(listenServers), [&](Proxy &proxy) { | |
+ return (proxy.target.public_ip == ip || proxy.target.local_ip == ip) && proxy.target.port == port; | |
+ }); | |
+ | |
+ ShowInfo("List of available proxy servers:\n"); | |
+ for (const auto &proxy : listenServers) { | |
+ ShowInfo("%s : %s:%d\n", proxy.name.c_str(), ip2str(proxy.proxy.public_ip, nullptr), proxy.proxy.port); | |
+ } | |
+ return; | |
+ } | |
+ | |
+#ifdef WIN32 | |
+ ULONG outBufLen = 15000; | |
+ PIP_ADAPTER_ADDRESSES pAddresses = nullptr; | |
+ int counter = 0; | |
+ ULONG ret = 0; | |
+ do { | |
+ pAddresses = (IP_ADAPTER_ADDRESSES*)aMalloc(outBufLen); | |
+ ret = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen); | |
+ if (ret == ERROR_BUFFER_OVERFLOW) { | |
+ aFree(pAddresses); | |
+ pAddresses = nullptr; | |
+ } | |
+ else { | |
+ break; | |
+ } | |
+ counter++; | |
+ } while (ret == ERROR_BUFFER_OVERFLOW && counter < 3); | |
+ if (ret == ERROR_BUFFER_OVERFLOW) { | |
+ ShowError("Failed to get adapter addresses (%d), no proxies will be loaded\n", ret); | |
+ } | |
+ | |
+ std::copy_if(proxyServers.begin(), proxyServers.end(), std::back_inserter(listenServers), [&](Proxy& proxy) { | |
+ auto curr = pAddresses; | |
+ while (curr) { | |
+ for (auto uni = curr->FirstUnicastAddress; uni != nullptr; uni = uni->Next) { | |
+ auto* sa = reinterpret_cast<struct sockaddr_in*>(uni->Address.lpSockaddr); | |
+ if (ntohl(sa->sin_addr.s_addr) == proxy.target.public_ip) | |
+ return true; | |
+ if (ntohl(sa->sin_addr.s_addr) == proxy.target.local_ip) | |
+ return true; | |
+ } | |
+ curr = curr->Next; | |
+ } | |
+ return false; | |
+ }); | |
+ aFree(pAddresses); | |
+#else | |
+ struct ifaddrs *ifaddr = nullptr; | |
+ if (getifaddrs(&ifaddr) == -1) { | |
+ perror("getifaddrs"); | |
+ exit(EXIT_FAILURE); | |
+ } | |
+ | |
+ std::copy_if(proxyServers.begin(), proxyServers.end(), std::back_inserter(listenServers), [&](Proxy &proxy) { | |
+ if (proxy.target.port != port) { | |
+ return false; | |
+ } | |
+ | |
+ auto *ifa = ifaddr; | |
+ for (; ifa != nullptr; ifa = ifa->ifa_next) { | |
+ if (ifa->ifa_addr == nullptr) | |
+ continue; | |
+ | |
+ if (ifa->ifa_addr->sa_family != AF_INET) | |
+ continue; | |
+ | |
+ auto * sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr); | |
+ if (ntohl(sa->sin_addr.s_addr) == proxy.target.public_ip) | |
+ return true; | |
+ if (ntohl(sa->sin_addr.s_addr) == proxy.target.local_ip) | |
+ return true; | |
+ } | |
+ return false; | |
+ }); | |
+ freeifaddrs(ifaddr); | |
+#endif | |
+ ShowInfo("List of available proxy servers:\n"); | |
+ for (const auto &proxy : listenServers) { | |
+ ShowInfo("%s : %s:%d\n", proxy.name.c_str(), ip2str(proxy.proxy.public_ip, nullptr), proxy.proxy.port); | |
+ } | |
+} | |
+ | |
+int clif_parse_proxy(int fd) { | |
+ const size_t packet_len = 2 + 4 + PROXY_SECRET_LEN; | |
+ if (RFIFOREST(fd) < packet_len) | |
+ return 0; | |
+ | |
+ if (session[fd]->flag.parsed) { | |
+ ShowInfo("Connection refused from '" CL_WHITE "%s" CL_RESET "'. \n", ip2str(session[fd]->client_addr, nullptr)); | |
+ ShowInfo("\tClient already sent info!\n"); | |
+ RFIFOSKIP(fd, packet_len); | |
+ do_close(fd); | |
+ return 0; | |
+ } | |
+ | |
+ auto proxy = proxy_db.searchProxyIp(session[fd]->client_addr); | |
+ if (!proxy) { | |
+ ShowInfo("Connection refused from '" CL_WHITE "%s" CL_RESET "'. \n", ip2str(session[fd]->client_addr, nullptr)); | |
+ ShowInfo("\tReceived proxy packet from an unknown proxy!\n"); | |
+ RFIFOSKIP(fd, packet_len); | |
+ do_close(fd); | |
+ return 0; | |
+ } | |
+ | |
+ if (strncmp(RFIFOCP(fd, 6), proxy->secret.c_str(), PROXY_SECRET_LEN)) { | |
+ ShowInfo("Connection refused from '" CL_WHITE "%s" CL_RESET "'. \n", ip2str(session[fd]->client_addr, nullptr)); | |
+ ShowInfo("\tProxy packet has incorrect secret! %s != %s\n", RFIFOCP(fd, 6), proxy->secret.c_str()); | |
+ RFIFOSKIP(fd, packet_len); | |
+ do_close(fd); | |
+ return 0; | |
+ } | |
+ | |
+ | |
+ uint32 ip = RFIFOL(fd, 2); | |
+ session[fd]->proxy_addr = session[fd]->client_addr; | |
+ session[fd]->client_addr = ip; | |
+ RFIFOSKIP(fd, packet_len); | |
+ ShowInfo("Proxy connection detected from '" CL_WHITE "%s" CL_RESET "'!\n", ip2str(session[fd]->client_addr, nullptr)); | |
+ ShowInfo("\tProxy ip is '" CL_WHITE "%s" CL_RESET "'.\n", ip2str(session[fd]->proxy_addr, nullptr)); | |
+ return 1; | |
+} | |
diff --git a/src/common/proxy.hpp b/src/common/proxy.hpp | |
new file mode 100644 | |
index 000000000..474aeb325 | |
--- /dev/null | |
+++ b/src/common/proxy.hpp | |
@@ -0,0 +1,69 @@ | |
+#ifndef COMMON_PROXY_HPP | |
+#define COMMON_PROXY_HPP | |
+ | |
+#include <vector> | |
+#include <string> | |
+#include <unordered_map> | |
+ | |
+#include "cbasetypes.hpp" | |
+#include "database.hpp" | |
+ | |
+#define PROXY_SECRET_LEN 30 | |
+ | |
+struct ServerInfo { | |
+ uint32 public_ip; | |
+ uint16 port; | |
+ uint32 local_ip{0}; | |
+}; | |
+ | |
+struct Proxy { | |
+ struct ServerInfo proxy; | |
+ struct ServerInfo target; | |
+ std::string name; | |
+ std::string secret; | |
+}; | |
+ | |
+class ProxyDatabase : public YamlDatabase { | |
+private: | |
+ std::vector<struct Proxy> proxyServers; | |
+ std::vector<struct Proxy> listenServers; // used for login/char/map | |
+ std::string filename; | |
+ | |
+public: | |
+ ProxyDatabase() : YamlDatabase("PROXY_DB", 1, 1), filename(std::string(db_path) + "/proxy_servers.yml") { | |
+ | |
+ } | |
+ | |
+ class iterator : public std::vector<struct Proxy>::iterator {}; | |
+ | |
+ const std::string getDefaultLocation() override { | |
+ return filename; | |
+ }; | |
+ void setDefaultLocation(const std::string& s) { | |
+ filename = s; | |
+ } | |
+ uint64 parseBodyNode(const ryml::NodeRef& node) override; | |
+ void loadingFinished() override {}; | |
+ void clear() override{ | |
+ proxyServers.clear(); | |
+ } | |
+ | |
+ // Additional | |
+ const Proxy* searchTargetIPPortProxyIp(uint32 target_ip, uint16 target_port, uint32 proxy_ip); | |
+ const Proxy* searchProxyIp(uint32 ip); | |
+ void filterTargetIPPort(uint32 ip, uint16 port); | |
+ | |
+ const std::vector<struct Proxy>::iterator begin() { | |
+ return proxyServers.begin(); | |
+ } | |
+ const std::vector<struct Proxy>::iterator end() { | |
+ return proxyServers.end(); | |
+ } | |
+ | |
+ bool asIP(const c4::yml::NodeRef &node, const std::string &name, uint32 &out); | |
+ bool asServerInfo(const ryml::NodeRef& node, const std::string &name, struct ServerInfo &out); | |
+}; | |
+ | |
+extern ProxyDatabase proxy_db; | |
+int clif_parse_proxy(int fd); | |
+#endif | |
diff --git a/src/common/socket.cpp b/src/common/socket.cpp | |
index 35f42398d..7e3ba5ba2 100644 | |
--- a/src/common/socket.cpp | |
+++ b/src/common/socket.cpp | |
@@ -43,6 +43,7 @@ | |
#include "cbasetypes.hpp" | |
#include "malloc.hpp" | |
#include "mmo.hpp" | |
+#include "proxy.hpp" | |
#include "showmsg.hpp" | |
#include "strlib.hpp" | |
#include "timer.hpp" | |
@@ -352,8 +353,11 @@ void setsocketopts(int fd,int delay_timeout){ | |
/*====================================== | |
* CORE : Socket Sub Function | |
*--------------------------------------*/ | |
-void set_eof(int fd) | |
+void set_eof_(int fd, const char *file, int line, const char *func) | |
{ | |
+#ifdef PROXY_DEBUG | |
+ ShowDebug("set_eof: Session #%d (from %s:%d in %s)\n", fd, file, line, func); | |
+#endif | |
if( session_isActive(fd) ) | |
{ | |
#ifdef SEND_SHORTLIST | |
@@ -521,6 +525,9 @@ int connect_client(int listen_fd) | |
create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse); | |
session[fd]->client_addr = ntohl(client_address.sin_addr.s_addr); | |
+#ifdef PROXY_DEBUG | |
+ ShowInfo("New client connected from '" CL_WHITE "%s" CL_RESET "'!\n", ip2str(session[fd]->client_addr, nullptr)); | |
+#endif | |
return fd; | |
} | |
@@ -1128,6 +1135,16 @@ static int connect_check_(uint32 ip) | |
break; | |
} | |
} | |
+ // search the proxy list, always allow proxy servers | |
+ auto proxy = proxy_db.searchProxyIp(ip); | |
+ if (proxy) { | |
+ if (access_debug) { | |
+ ShowInfo("connect_check: Found match from proxy list:%d.%d.%d.%d IP:%d.%d.%d.%d\n", | |
+ CONVIP(ip), | |
+ CONVIP(proxy->proxy.public_ip)); | |
+ } | |
+ is_allowip = 1; | |
+ } | |
// Search the deny list | |
for( i=0; i < access_denynum; ++i ){ | |
if( (ip & access_deny[i].mask) == (access_deny[i].ip & access_deny[i].mask) ){ | |
@@ -1428,6 +1445,7 @@ void do_close(int fd) | |
#ifndef SOCKET_EPOLL | |
// Select based Event Dispatcher | |
+ if (sFD_ISSET(fd, &readfds)) | |
sFD_CLR(fd, &readfds);// this needs to be done before closing the socket | |
#else | |
// Epoll based Event Dispatcher | |
diff --git a/src/common/socket.hpp b/src/common/socket.hpp | |
index 3a5eeeef6..422988bb2 100644 | |
--- a/src/common/socket.hpp | |
+++ b/src/common/socket.hpp | |
@@ -89,9 +89,11 @@ struct socket_data | |
unsigned char eof : 1; | |
unsigned char server : 1; | |
unsigned char ping : 2; | |
+ unsigned char parsed : 1; // already sent a packet? | |
} flag; | |
uint32 client_addr; // remote client address | |
+ uint32 proxy_addr; // if client is using proxy | |
uint8 *rdata, *wdata; | |
size_t max_rdata, max_wdata; | |
@@ -174,7 +176,8 @@ int socket_getips(uint32* ips, int max); | |
extern uint32 addr_[16]; // ip addresses of local host (host byte order) | |
extern int naddr_; // # of ip addresses | |
-void set_eof(int fd); | |
+void set_eof_(int fd, const char *file, int line, const char *func); | |
+#define set_eof(fd) set_eof_(fd, __FILE__, __LINE__, __func__) | |
/// Use a shortlist of sockets instead of iterating all sessions for sockets | |
/// that have data to send or need eof handling. | |
diff --git a/src/login/loginclif.cpp b/src/login/loginclif.cpp | |
index e94eb36e0..9c4e304fd 100644 | |
--- a/src/login/loginclif.cpp | |
+++ b/src/login/loginclif.cpp | |
@@ -8,6 +8,7 @@ | |
#include "../common/malloc.hpp" | |
#include "../common/md5calc.hpp" | |
+#include "../common/proxy.hpp" | |
#include "../common/random.hpp" | |
#include "../common/showmsg.hpp" //show notice | |
#include "../common/socket.hpp" //wfifo session | |
@@ -136,8 +137,21 @@ static void logclif_auth_ok(struct login_session_data* sd) { | |
if( !session_isValid(ch_server[i].fd) ) | |
continue; | |
subnet_char_ip = lan_subnetcheck(ip); // Advanced subnet check [LuzZza] | |
- WFIFOL(fd,header+n*size) = htonl((subnet_char_ip) ? subnet_char_ip : ch_server[i].ip); | |
- WFIFOW(fd,header+n*size+4) = ntows(htons(ch_server[i].port)); // [!] LE byte order here [!] | |
+ auto * proxy = proxy_db.searchTargetIPPortProxyIp(ch_server[i].ip, ch_server[i].port, session[fd]->proxy_addr); | |
+ ShowInfo("Proxy addr is %s\n", ip2str(session[fd]->proxy_addr, nullptr)); | |
+ uint32 destip = ch_server[i].ip; | |
+ uint16 destport = htons(ch_server[i].port); | |
+ if (subnet_char_ip) { | |
+ destip = subnet_char_ip; | |
+ destport = htons(ch_server[i].port); | |
+ } | |
+ if (proxy) { | |
+ destip = proxy->proxy.public_ip; | |
+ destport = ntohs(proxy->proxy.port); | |
+ ShowInfo("Using proxy! sending %s\n", ip2str(destip, nullptr)); | |
+ } | |
+ WFIFOL(fd,header+n*size) = htonl(destip); | |
+ WFIFOW(fd,header+n*size+4) = ntows(destport); // [!] LE byte order here [!] | |
memcpy(WFIFOP(fd,header+n*size+6), ch_server[i].name, 20); | |
WFIFOW(fd,header+n*size+26) = login_get_usercount( ch_server[i].users ); | |
WFIFOW(fd,header+n*size+28) = ch_server[i].type; | |
@@ -279,6 +293,19 @@ static int logclif_parse_updclhash(int fd, struct login_session_data *sd){ | |
static int logclif_parse_reqauth(int fd, struct login_session_data *sd, int command, char* ip){ | |
size_t packet_len = RFIFOREST(fd); | |
+ // Perform ip-ban check | |
+ if( login_config.ipban && ipban_check(session[fd]->client_addr) ) | |
+ { | |
+ ShowStatus("Connection refused: IP isn't authorised (deny/allow, ip: %s).\n", ip); | |
+ login_log(session[fd]->client_addr, "unknown", -3, "ip banned"); | |
+ logclif_auth_failed(sd, 3); | |
+ set_eof(fd); | |
+ return 0; | |
+ } | |
+ | |
+ | |
+ session[fd]->flag.parsed = 1; | |
+ | |
if( (command == 0x0064 && packet_len < 55) | |
|| (command == 0x0277 && packet_len < 84) | |
|| (command == 0x02b0 && packet_len < 85) | |
@@ -541,6 +568,7 @@ int logclif_parse(int fd) { | |
break; | |
// Connection request of a char-server | |
case 0x2710: logclif_parse_reqcharconnec(fd,sd, ip); return 0; // processing will continue elsewhere | |
+ case 0xbeef: next = clif_parse_proxy(fd); break; | |
default: | |
ShowNotice("Abnormal end of connection (ip: %s): Unknown packet 0x%x\n", ip, command); | |
set_eof(fd); | |
@@ -561,6 +589,8 @@ int logclif_parse(int fd) { | |
* Launched at login-serv start, create db or other long scope variable here. | |
*/ | |
void do_init_loginclif(void){ | |
+ proxy_db.load(); | |
+ proxy_db.filterTargetIPPort(login_config.login_ip, login_config.login_port); | |
return; | |
} | |
diff --git a/src/map/clif.cpp b/src/map/clif.cpp | |
index de9bbf1cd..e62a64c8a 100644 | |
--- a/src/map/clif.cpp | |
+++ b/src/map/clif.cpp | |
@@ -16,6 +16,7 @@ | |
#include "../common/grfio.hpp" | |
#include "../common/malloc.hpp" | |
#include "../common/nullpo.hpp" | |
+#include "../common/proxy.hpp" | |
#include "../common/random.hpp" | |
#include "../common/showmsg.hpp" | |
#include "../common/socket.hpp" | |
@@ -10732,6 +10733,8 @@ void clif_parse_WantToConnection(int fd, struct map_session_data* sd) | |
int cmd, account_id, char_id, login_id1, sex, err; | |
t_tick client_tick; //The client tick is a tick, therefore it needs be unsigned. [Skotlex] | |
+ session[fd]->flag.parsed = 1; | |
+ | |
if (sd) { | |
ShowError("clif_parse_WantToConnection : invalid request (character already logged in)\n"); | |
return; | |
@@ -23443,6 +23446,25 @@ void clif_parse_laphine_upgrade( int fd, struct map_session_data* sd ){ | |
#endif | |
} | |
+int clif_parse_proxy_closing(int fd) { | |
+ const size_t packet_len = 4; | |
+ if (RFIFOREST(fd) < packet_len) | |
+ return 0; | |
+ | |
+ if (!session[fd]->session_data) | |
+ return 0; | |
+ | |
+ uint16 mins = RFIFOW(fd, 2); | |
+ | |
+ char msg[CHAT_SIZE_MAX]; | |
+ | |
+ safesnprintf(msg, CHAT_SIZE_MAX, "Please relog before auto-disconnect, %d minutes remaining.", mins); | |
+ | |
+ clif_broadcast(&((TBL_PC *)session[fd]->session_data)->bl, msg, strlen(msg) + 1, 0, SELF); | |
+ RFIFOSKIP(fd, packet_len); | |
+ return 1; | |
+} | |
+ | |
/*========================================== | |
* Main client packet processing function | |
*------------------------------------------*/ | |
@@ -23506,6 +23528,17 @@ static int clif_parse(int fd) | |
} | |
#endif | |
+ if (cmd == 0xbeef) { | |
+ if (clif_parse_proxy(fd) == 0) | |
+ return 0; | |
+ continue; | |
+ } | |
+ if (cmd == 0xbeee && sd) { | |
+ if (clif_parse_proxy_closing(fd) == 0) | |
+ return 0; | |
+ continue; | |
+ } | |
+ | |
// filter out invalid / unsupported packets | |
if (cmd > MAX_PACKET_DB || cmd < MIN_PACKET_DB || packet_db[cmd].len == 0) { | |
ShowWarning("clif_parse: Received unsupported packet (packet 0x%04x, %d bytes received), disconnecting session #%d.\n", cmd, RFIFOREST(fd), fd); | |
@@ -23674,7 +23707,8 @@ void do_init_clif(void) { | |
add_timer_interval( gettick() + battle_config.ping_timer_interval * 1000, clif_ping_timer, 0, 0, battle_config.ping_timer_interval * 1000 ); | |
} | |
#endif | |
- | |
+ proxy_db.load(); | |
+ proxy_db.filterTargetIPPort(bind_ip, map_port); | |
delay_clearunit_ers = ers_new(sizeof(struct block_list),"clif.cpp::delay_clearunit_ers",ERS_OPT_CLEAR); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment