Skip to content

Instantly share code, notes, and snippets.

@vstumpf
Last active June 25, 2023 07:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save vstumpf/47051f55009bffbbb42462549de4b9a5 to your computer and use it in GitHub Desktop.
Save vstumpf/47051f55009bffbbb42462549de4b9a5 to your computer and use it in GitHub Desktop.
The diff for trogproxy for rathena servers
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