XKoreProxy.pm
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:15k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. #  OpenKore - X-Kore
  3. #
  4. #  This software is open source, licensed under the GNU General Public
  5. #  License, version 2.
  6. #  Basically, this means that you're allowed to modify and distribute
  7. #  this software. However, if you distribute modified versions, you MUST
  8. #  also distribute the source code.
  9. #  See http://www.gnu.org/licenses/gpl.html for the full license.
  10. #
  11. #  $Revision: 6168 $
  12. #  $Id: XKoreProxy.pm 6168 2008-01-11 16:26:53Z kaliwanagan $
  13. #
  14. #########################################################################
  15. # Note: the difference between XKore2 and XKoreProxy is that XKore2 can
  16. # work headless (it handles all server messages by itself), while
  17. # XKoreProxy lets the RO client handle many server messages.
  18. package Network::XKoreProxy;
  19. # FIXME: $syncSync is not set correctly (required for ropp)
  20. use strict;
  21. use base qw(Exporter);
  22. use Exporter;
  23. use IO::Socket::INET;
  24. use Time::HiRes qw(time usleep);
  25. use encoding 'utf8';
  26. use Modules 'register';
  27. use Globals;
  28. use Log qw(message warning error debug);
  29. use Utils qw(dataWaiting timeOut makeIP encodeIP swrite existsInList);
  30. use Misc qw(configModify visualDump);
  31. use Translation qw(T TF);
  32. use I18N qw(bytesToString);
  33. use Interface;
  34. use Network;
  35. use Network::Send ();
  36. use Utils::Exceptions;
  37. my $clientBuffer;
  38. my %flushTimer;
  39. # Members:
  40. #
  41. # Socket proxy_listen
  42. #    A server socket which accepts new connections from the RO client.
  43. #    This is only defined when the RO client hasn't already connected
  44. #    to XKoreProxy.
  45. #
  46. # Socket proxy
  47. #    A client socket, which connects XKoreProxy with the RO client.
  48. #    This is only defined when the RO client has connected to XKoreProxy.
  49. ##
  50. # Network::XKoreProxy->new()
  51. #
  52. # Initialize X-Kore-Proxy mode.
  53. sub new {
  54. my $class = shift;
  55. my $ip = $config{XKore_listenIp} || '0.0.0.0';
  56. my $port = $config{XKore_listenPort} || 6901;
  57. my $self = bless {}, $class;
  58. # Reuse code from Network::DirectConnection to connect to the server
  59. require Network::DirectConnection;
  60. $self->{server} = new Network::DirectConnection($self);
  61. $self->{client_state} = 0;
  62. $self->{nextIp} = undef;
  63. $self->{nextPort} = undef;
  64. $self->{charServerIp} = undef;
  65. $self->{charServerPort} = undef;
  66. $self->{gotError} = 0;
  67. $self->{waitingClient} = 1;
  68. {
  69. no encoding 'utf8';
  70. $self->{packetPending} = '';
  71. $clientBuffer = '';
  72. }
  73. message T("X-Kore mode intialized.n"), "startup";
  74. if (defined($config{gameGuard}) && $config{gameGuard} ne '2') {
  75. require Poseidon::EmbedServer;
  76. Modules::register("Poseidon::EmbedServer");
  77. $self->{poseidon} = new Poseidon::EmbedServer;
  78. }
  79. return $self;
  80. }
  81. sub version {
  82. return 1;
  83. }
  84. sub DESTROY {
  85. my $self = shift;
  86. close($self->{proxy_listen});
  87. close($self->{proxy});
  88. }
  89. ######################
  90. ## Server Functions ##
  91. ######################
  92. sub serverAlive {
  93. my $self = shift;
  94. return $self->{server}->serverAlive;
  95. }
  96. sub serverConnect {
  97. my $self = shift;
  98. my $host = shift;
  99. my $port = shift;
  100. return $self->{server}->serverConnect($host, $port);
  101. }
  102. sub serverPeerHost {
  103. my $self = shift;
  104. return $self->{server}->serverPeerHost if ($self->serverAlive);
  105. return undef;
  106. }
  107. sub serverPeerPort {
  108. my $self = shift;
  109. return $self->{server}->serverPeerPort if ($self->serverAlive);
  110. return undef;
  111. }
  112. sub serverRecv {
  113. my $self = shift;
  114. return $self->{server}->serverRecv();
  115. }
  116. sub serverSend {
  117. my $self = shift;
  118. my $msg = shift;
  119. $self->{server}->serverSend($msg);
  120. }
  121. sub serverDisconnect {
  122. my $self = shift;
  123. my $preserveClient = shift;
  124. return unless ($self->serverAlive);
  125. close($self->{proxy}) unless $preserveClient;
  126. $self->{waitClientDC} = 1 if $preserveClient;
  127. # user has played with relog command.
  128. if ($timeout_ex{'master'}{'time'}) {
  129. undef $timeout_ex{'master'}{'time'};
  130. $self->{waitingClient} = 1;
  131. }
  132. return $self->{server}->serverDisconnect();
  133. }
  134. sub serverAddress {
  135. my ($self) = @_;
  136. return $self->{server}->serverAddress();
  137. }
  138. sub getState {
  139. my ($self) = @_;
  140. return $self->{server}->getState();
  141. }
  142. sub setState {
  143. my ($self, $state) = @_;
  144. $self->{server}->setState($state);
  145. }
  146. ######################
  147. ## Client Functions ##
  148. ######################
  149. sub clientAlive {
  150. my $self = shift;
  151. return $self->proxyAlive();
  152. }
  153. sub proxyAlive {
  154. my $self = shift;
  155. return $self->{proxy} && $self->{proxy}->connected;
  156. }
  157. sub clientPeerHost {
  158. my $self = shift;
  159. return $self->{proxy}->peerhost if ($self->proxyAlive);
  160. return undef;
  161. }
  162. sub clientPeerPort {
  163. my $self = shift;
  164. return $self->{proxy}->peerport if ($self->proxyAlive);
  165. return undef;
  166. }
  167. sub clientSend {
  168. my $self = shift;
  169. my $msg = shift;
  170. my $dontMod = shift;
  171. return unless ($self->proxyAlive);
  172. $msg = $self->modifyPacketIn($msg) unless ($dontMod);
  173. if ($config{debugPacket_ro_received}) {
  174. debug "Modified packet sent to clientn";
  175. visualDump($msg, 'clientSend');
  176. }
  177. # queue message instead of sending directly
  178. $clientBuffer .= $msg;
  179. }
  180. sub clientFlush {
  181. my $self = shift;
  182. return unless (length($clientBuffer));
  183. $self->{proxy}->send($clientBuffer);
  184. debug "Client network buffer flushed outn";
  185. $clientBuffer = '';
  186. }
  187. sub clientRecv {
  188. my $self = shift;
  189. my $msg;
  190. return undef unless ($self->proxyAlive && dataWaiting($self->{proxy}));
  191. $self->{proxy}->recv($msg, 1024 * 32);
  192. if (length($msg) == 0) {
  193. # Connection from client closed
  194. close($self->{proxy});
  195. return undef;
  196. }
  197. # return $self->realClientRecv;
  198. return $self->modifyPacketOut($msg);
  199. }
  200. sub checkConnection {
  201. my $self = shift;
  202. # Check connection to the client
  203. $self->checkProxy();
  204. # Check server connection
  205. $self->checkServer();
  206. # Check the Poseidon Embed Server
  207. if ($self->clientAlive() && $self->getState() == Network::IN_GAME
  208.  && defined($config{gameGuard}) && $config{gameGuard} ne '2') {
  209. $self->{poseidon}->iterate($self);
  210. }
  211. }
  212. sub checkProxy {
  213. my $self = shift;
  214. if (defined $self->{proxy_listen}) {
  215. # Listening for a client
  216. if (dataWaiting($self->{proxy_listen})) {
  217. # Client is connecting...
  218. $self->{proxy} = $self->{proxy_listen}->accept;
  219. # Tell 'em about the new client
  220. my $host = $self->clientPeerHost;
  221. my $port = $self->clientPeerPort;
  222. debug "XKore Proxy: RO Client connected ($host:$port).n", "connection";
  223. # Stop listening and clear errors.
  224. close($self->{proxy_listen});
  225. undef $self->{proxy_listen};
  226. $self->{gotError} = 0;
  227. }
  228. #return;
  229. } elsif (!$self->proxyAlive) {
  230. # Client disconnected... (or never existed)
  231. if ($self->serverAlive()) {
  232. message T("Client disconnectedn"), "connection";
  233. $self->setState(Network::NOT_CONNECTED) if ($self->getState() == Network::IN_GAME);
  234. $self->{waitingClient} = 1;
  235. $self->serverDisconnect();
  236. }
  237. close($self->{proxy});
  238. $self->{waitClientDC} = undef;
  239. debug "Removing pending packet from queuen" if (defined $self->{packetPending});
  240. $self->{packetPending} = '';
  241. # FIXME: there's a racing condition here. If the RO client tries to connect
  242. # to the listening port before we've set it up (this happens if sleepTime is
  243. # sufficiently high), then the client will freeze.
  244. # (Re)start listening...
  245. my $ip = $config{XKore_listenIp} || '127.0.0.1';
  246. my $port = $config{XKore_listenPort} || 6901;
  247. $self->{proxy_listen} = new IO::Socket::INET(
  248. LocalAddr => $ip,
  249. LocalPort => $port,
  250. Listen => 5,
  251. Proto => 'tcp',
  252. ReuseAddr   => 1);
  253. die "Unable to start the X-Kore proxy ($ip:$port): $@n" . 
  254. "You can only run one X-Kore session at the same time.n" .
  255. "And make sure no other servers are running on port $port." unless $self->{proxy_listen};
  256. # setup master server if necessary
  257. getMainServer();
  258. message TF("Waiting Ragnarok Client to connect on (%s:%s)n", ($ip eq '127.0.0.1' ? 'localhost' : $ip), $port), "startup" if ($self->{waitingClient} == 1);
  259. $self->{waitingClient} = 0;
  260. return;
  261. }
  262. if ($self->proxyAlive() && defined($self->{packetPending})) {
  263. checkPacketReplay();
  264. }
  265. }
  266. sub checkServer {
  267. my $self = shift;
  268. # Do nothing until the client has (re)connected to us
  269. return if (!$self->proxyAlive() || $self->{waitClientDC});
  270. # Connect to the next server for proxying the packets
  271. if (!$self->serverAlive()) {
  272. # Setup the next server to connect.
  273. if (!$self->{nextIp} || !$self->{nextPort}) {
  274. # if no next server was defined by received packets, setup a primary server.
  275. my $master = $masterServer = $masterServers{$config{'master'}};
  276. $self->{nextIp} = $master->{ip};
  277. $self->{nextPort} = $master->{port};
  278. message TF("Proxying to [%s]n", $config{master}), "connection" unless ($self->{gotError});
  279. eval {
  280. $packetParser = Network::Receive->create($config{serverType});
  281. $messageSender = Network::Send->create($self, $config{serverType});
  282. };
  283. if (my $e = caught('Exception::Class::Base')) {
  284. $interface->errorDialog($e->message());
  285. $quit = 1;
  286. return;
  287. }
  288. }
  289. $self->serverConnect($self->{nextIp}, $self->{nextPort}) unless ($self->{gotError});
  290. if (!$self->serverAlive()) {
  291. $self->{charServerIp} = undef;
  292. $self->{charServerPort} = undef;
  293. close($self->{proxy});
  294. error T("Invalid server specified or server does not exist...n"), "connection" if (!$self->{gotError});
  295. $self->{gotError} = 1;
  296. }
  297. # clean Next Server uppon connection
  298. $self->{nextIp} = undef;
  299. $self->{nextPort} = undef;
  300. }
  301. }
  302. ##
  303. # $Network_XKoreProxy->checkPacketReplay()
  304. #
  305. # Setup a timer to repeat the received logon/server change packet to the client
  306. # in case it didn't responded in an appropriate time.
  307. #
  308. # This is an internal function.
  309. sub checkPacketReplay {
  310. my $self = shift;
  311. #message "Pending packet checkn";
  312. if ($self->{replayTimeout}{time} && timeOut($self->{replayTimeout})) {
  313. if ($self->{packetReplayTrial} < 3) {
  314. warning TF("Client did not respond in time.n" . 
  315. "Trying to replay the packet for %s of 3 timesn", $self->{packetReplayTrial}++);
  316. $self->clientSend($self->{packetPending});
  317. $self->{replayTimeout}{time} = time;
  318. $self->{replayTimeout}{timeout} = 2.5;
  319. } else {
  320. error T("Client did not respond. Forcing disconnectionn");
  321. close($self->{proxy});
  322. return;
  323. }
  324. } elsif (!$self->{replayTimeout}{time}) {
  325. $self->{replayTimeout}{time} = time;
  326. $self->{replayTimeout}{timeout} = 2.5;
  327. }
  328. sub modifyPacketIn {
  329. my ($self, $msg) = @_;
  330. return undef if (length($msg) < 1);
  331. my $switch = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1)));
  332. if ($switch eq "02AE") {
  333. $msg = ""; 
  334. }
  335. # packet replay check: reset status for every different packet received
  336. if ($self->{packetPending} && ($self->{packetPending} ne $msg)) {
  337. debug "Removing pending packet from queuen";
  338. use bytes; no encoding 'utf8';
  339. delete $self->{replayTimeout};
  340. $self->{packetPending} = '';
  341. $self->{packetReplayTrial} = 0;
  342. } elsif ($self->{packetPending} && ($self->{packetPending} eq $msg)) {
  343. # avoid doubled 0259 message: could mess the character selection and hang up the client
  344. if ($switch eq "0259") {
  345. debug T("Logon-grant packet received twice! Avoiding bug in client.n");
  346. $self->{packetPending} = undef;
  347. return undef;
  348. }
  349. }
  350. if ($switch eq "0069") {
  351. use bytes; no encoding 'utf8';
  352. # queue the packet as requiring client's response in time
  353. $self->{packetPending} = $msg;
  354. # Modify the server config'ed on Kore to point to proxy
  355. my $accountInfo = substr($msg, 0, 47);
  356. my $serverInfo = substr($msg, 47, length($msg));
  357. my $newServers = '';
  358. my $serverCount = 0;
  359. my $msg_size = length($serverInfo);
  360. debug "Modifying Account Info packet...";
  361. for (my $i = 0; $i < $msg_size; $i+=32) {
  362. if ($config{'server'} == $serverCount++) {
  363. $self->{nextIp} = makeIP(substr($serverInfo, $i, 4));
  364. $self->{nextIp} = $masterServer->{ip} if ($masterServer && $masterServer->{private});
  365. $self->{nextPort} = unpack("v1", substr($serverInfo, $i+4, 2));
  366. debug " next server to connect ($self->{nextIp}:$self->{nextPort})n", "connection";
  367. $self->{charServerIp} = $self->{nextIp};
  368. $self->{charServerPort} = $self->{nextPort};
  369. my $newName = unpack("Z*", substr($serverInfo, $i + 6, 20));
  370. #$newName = "$newName (proxied)";
  371. $newServers .= encodeIP($self->{proxy}->sockhost) . pack("v*", $self->{proxy}->sockport) . 
  372. pack("Z20", $newName) . substr($serverInfo, $i + 26, 4) . pack("v1", 0);
  373. } else {
  374. $newServers .= substr($serverInfo, $i, 32);
  375. }
  376. }
  377. message T("Closing connection to Account Servern"), 'connection' if (!$self->{packetReplayTrial});
  378. $self->serverDisconnect(1);
  379. $msg = $accountInfo . $newServers;
  380. } elsif ($switch eq "0071" || $switch eq "0092") {
  381. # queue the packet as requiring client's response in time
  382. $self->{packetPending} = $msg;
  383. # Proxy the Logon to Map server
  384. debug "Modifying Map Logon packet...", "connection";
  385. my $logonInfo = substr($msg, 0, 22);
  386. my @mapServer = unpack("x22 a4 v1", $msg);
  387. my $mapIP = $mapServer[0];
  388. my $mapPort = $mapServer[1];
  389. $self->{nextIp} = makeIP($mapIP);
  390. $self->{nextIp} = $masterServer->{ip} if ($masterServer && $masterServer->{private});
  391. $self->{nextPort} = $mapPort;
  392. debug " next server to connect ($self->{nextIp}:$self->{nextPort})n", "connection";
  393. if ($switch eq "0071") {
  394. message T("Closing connection to Character Servern"), 'connection' if (!$self->{packetReplayTrial});
  395. } else {
  396. message T("Closing connection to Map Servern"), "connection" if (!$self->{packetReplayTrial});
  397. }
  398. $self->serverDisconnect(1);
  399. $msg = $logonInfo . encodeIP($self->{proxy}->sockhost) . pack("v*", $self->{proxy}->sockport);
  400. } elsif ($switch eq "006A" || $switch eq "006C" || $switch eq "0081") {
  401. # An error occurred. Restart proxying
  402. $self->{gotError} = 1;
  403. $self->{nextIp} = undef;
  404. $self->{nextPort} = undef;
  405. $self->{charServerIp} = undef;
  406. $self->{charServerPort} = undef;
  407. $self->serverDisconnect(1);
  408. } elsif ($switch eq "00B3") {
  409. $self->{nextIp} = $self->{charServerIp};
  410. $self->{nextPort} = $self->{charServerPort};
  411. $self->serverDisconnect(1);
  412. } elsif ($switch eq "0259") {
  413. # queue the packet as requiring client's response in time
  414. $self->{packetPending} = $msg;
  415. }
  416. return $msg;
  417. }
  418. sub modifyPacketOut {
  419. my ($self, $msg) = @_;
  420. use bytes; no encoding 'utf8';
  421. return undef if (length($msg) < 1);
  422. my $switch = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1)));
  423. if (($switch eq "007E" && (
  424. $config{serverType} == 0 ||
  425. $config{serverType} == 1 ||
  426. $config{serverType} == 2 ||
  427. $config{serverType} == 6 )) ||
  428. ($switch eq "0089" && (
  429. $config{serverType} == 3 ||
  430. $config{serverType} == 5 )) ||
  431. ($switch eq "0116" &&
  432. $config{serverType} == 4 ) ||
  433. ($switch eq "00F3" &&
  434.   $config{serverType} == 10)) { #for vRO
  435. # Intercept the RO client's sync
  436. $msg = "";
  437. $messageSender->sendSync() if ($messageSender);
  438. } if ($switch eq "0228" && $self->getState() == Network::IN_GAME && $config{gameGuard} ne '2') {
  439. if ($self->{poseidon}->awaitingResponse) {
  440. $self->{poseidon}->setResponse($msg);
  441. $msg = '';
  442. }
  443. return $msg;
  444. }
  445. sub getMainServer {
  446. if ($config{'master'} eq "" || $config{'master'} =~ /^d+$/ || !exists $masterServers{$config{'master'}}) {
  447. my @servers = sort { lc($a) cmp lc($b) } keys(%masterServers);
  448. my $choice = $interface->showMenu(
  449. T("Please choose a master server to connect to."),
  450. @servers,
  451. title => T("Master servers"));
  452. if ($choice == -1) {
  453. exit;
  454. } else {
  455. configModify('master', $servers[$choice], 1);
  456. }
  457. }
  458. }
  459. return 1;