From c2de8094b25f213b29861894bbdedf9e144103db Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Pueyo Date: Fri, 19 Oct 2012 02:21:54 +0200 Subject: [PATCH] First commit --- .gitattributes | 22 ++ .gitignore | 169 +++++++++++++ LICENSE | 165 +++++++++++++ README | 28 +++ classes/MinecraftInterface.class.php | 115 +++++++++ classes/Packet.class.php | 192 +++++++++++++++ classes/PocketMinecraftClient.class.php | 33 +++ classes/PocketMinecraftServer.class.php | 153 ++++++++++++ classes/Session.class.php | 33 +++ classes/Socket.class.php | 98 ++++++++ classes/Utils.class.php | 314 ++++++++++++++++++++++++ common/config.php | 39 +++ common/dependencies.php | 77 ++++++ common/functions.php | 180 ++++++++++++++ pstruct/RakNet.php | 90 +++++++ pstruct/packetName.php | 37 +++ server.php | 33 +++ 17 files changed, 1778 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README create mode 100644 classes/MinecraftInterface.class.php create mode 100644 classes/Packet.class.php create mode 100644 classes/PocketMinecraftClient.class.php create mode 100644 classes/PocketMinecraftServer.class.php create mode 100644 classes/Session.class.php create mode 100644 classes/Socket.class.php create mode 100644 classes/Utils.class.php create mode 100644 common/config.php create mode 100644 common/dependencies.php create mode 100644 common/functions.php create mode 100644 pstruct/RakNet.php create mode 100644 pstruct/packetName.php create mode 100644 server.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..412eeda78 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..159d6c975 --- /dev/null +++ b/.gitignore @@ -0,0 +1,169 @@ +*.log +*.ini +test.php +*.bat +raknet/* + +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..02bbb60bc --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/README b/README new file mode 100644 index 000000000..03c87a679 --- /dev/null +++ b/README @@ -0,0 +1,28 @@ +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . + + + - + / \ + / \ + / POCKET \ +/ MINECRAFT PHP \ +|\ @shoghicp /| +|. \ / .| +| .. \ / .. | +| .. | .. | +| .. | .. | +\ | / + \ | / + \ | / + \ | / \ No newline at end of file diff --git a/classes/MinecraftInterface.class.php b/classes/MinecraftInterface.class.php new file mode 100644 index 000000000..2bee2c9b9 --- /dev/null +++ b/classes/MinecraftInterface.class.php @@ -0,0 +1,115 @@ +server = new Socket($server, $port, (bool) $listen); + $this->protocol = (int) $protocol; + require("pstruct/RakNet.php"); + require("pstruct/packetName.php"); + $this->pstruct = $pstruct; + $this->name = $packetName; + } + + public function close(){ + return $this->server->close(); + } + + protected function getStruct($pid){ + if(isset($this->pstruct[$pid])){ + return $this->pstruct[$pid]; + } + return false; + } + + protected function writeDump($pid, $raw, $data, $origin = "client", $ip = "", $port = 0){ + if(LOG === true and DEBUG >= 2){ + $p = "[".microtime(true)."] [".($origin === "client" ? "CLIENT->SERVER":"SERVER->CLIENT")." ".$ip.":".$port."]: ".$this->name[$pid]." (0x".Utils::strTohex(chr($pid)).") [lenght ".strlen($raw)."]".PHP_EOL; + $p .= hexdump($raw, false, false, true); + if(is_array($data)){ + foreach($data as $i => $d){ + $p .= $i ." => ".(!is_array($d) ? $this->pstruct[$pid][$i]."(".(($this->pstruct[$pid][$i] === "magic" or substr($this->pstruct[$pid][$i], 0, 7) === "special") ? Utils::strToHex($d):$d).")":$this->pstruct[$pid][$i]."(***)").PHP_EOL; + } + } + $p .= PHP_EOL; + logg($p, "packets", false); + } + + } + + public function readPacket($port = false){ + if($this->server->connected === false){ + //return array("pid" => "ff", "data" => array(0 => 'Connection error')); + } + $data = $this->server->read(); + if($data[3] === false){ + return false; + } + $pid = $data[0]{0}; + $pid = ord($pid); + $struct = $this->getStruct($pid); + if($struct === false){ + $p = "[".microtime(true)."] [SERVER->CLIENT]: Error, bad packet id 0x".Utils::strToHex(chr($pid)).PHP_EOL; + $p .= hexdump($data[0], false, false, true); + $p .= PHP_EOL . "--------------- (1024 byte max extract) ----------" .PHP_EOL; + logg($p, "packets", true, 3); + + $this->buffer = ""; + //$this->server->recieve("\xff".Utils::writeString('Bad packet id '.$pid.'')); + //$this->writePacket("ff", array(0 => 'Bad packet id '.$pid.'')); + //return array("pid" => "ff", "data" => array(0 => 'Bad packet id '.$pid.'')); + return false; + } + + $packet = new Packet($pid, $struct, $data[0]); + $packet->protocol = $this->protocol; + $packet->parse(); + $this->writeDump($pid, $data[0], $packet->data, "server", $data[1], $data[2]); + return array("pid" => $pid, "data" => $packet->data, "raw" => $data[0], "ip" => $data[1], "port" => $data[2]); + } + + public function writePacket($pid, $data = array(), $raw = false, $dest = false, $port = false){ + $struct = $this->getStruct($pid); + if($raw === false){ + $packet = new Packet($pid, $struct); + $packet->protocol = $this->protocol; + $packet->data = $data; + $packet->create(); + $write = $this->server->write($packet->raw, $dest, $port); + $this->writeDump($pid, $packet->raw, $data, "client"); + }else{ + $write = $this->server->write($data, $dest, $port); + $this->writeDump($pid, $data, false, "client", $dest, $port); + } + return true; + } + +} + +?> \ No newline at end of file diff --git a/classes/Packet.class.php b/classes/Packet.class.php new file mode 100644 index 000000000..197465a0b --- /dev/null +++ b/classes/Packet.class.php @@ -0,0 +1,192 @@ +pid = $pid; + $this->offset = 1; + $this->raw = $data; + $this->data = array(); + if($pid !== false){ + $this->addRaw(chr($pid)); + } + $this->struct = $struct; + $this->sock = $sock; + } + + public function create($raw = false){ + foreach($this->struct as $field => $type){ + if(!isset($this->data[$field])){ + $this->data[$field] = ""; + } + if($raw === true){ + $this->addRaw($this->data[$field]); + continue; + } + if(is_int($type)){ + $this->addRaw($this->data[$field]); + continue; + } + switch($type){ + case "special1": + switch($this->pid){ + case 0x05: + $this->addRaw($this->data[$field]); + break; + } + break; + case "magic": + $this->addRaw(MAGIC); + break; + case "float": + $this->addRaw(Utils::writeFloat($this->data[$field])); + break; + case "int": + $this->addRaw(Utils::writeInt($this->data[$field])); + break; + case "double": + $this->addRaw(Utils::writeDouble($this->data[$field])); + break; + case "long": + $this->addRaw(Utils::writeLong($this->data[$field])); + break; + case "bool": + case "boolean": + $this->addRaw(Utils::writeBool($this->data[$field])); + break; + case "ubyte": + case "byte": + $this->addRaw(Utils::writeByte($this->data[$field])); + break; + case "short": + $this->addRaw(Utils::writeShort($this->data[$field])); + break; + case "byteArray": + $this->addRaw($this->data[$field]); + break; + case "string": + $this->addRaw(Utils::writeShort(strlen($this->data[$field]))); + $this->addRaw($this->data[$field]); + break; + case "slotData": + $this->addRaw(Utils::writeShort($this->data[$field][0])); + if($this->data[$field][0]!=-1){ + $this->addRaw(Utils::writeByte($this->data[$field][1])); + $this->addRaw(Utils::writeShort($this->data[$field][2])); + } + break; + default: + $this->addRaw(Utils::writeByte($this->data[$field])); + break; + } + } + } + + private function get($len = true){ + if($len === true){ + $data = substr($this->raw, $this->offset); + $this->offset = strlen($this->raw); + return $data; + } + $data = substr($this->raw, $this->offset, $len); + $this->offset += $len; + return $data; + } + + protected function addRaw($str){ + $this->raw .= $str; + return $str; + } + + public function parse(){ + $continue = true; + foreach($this->struct as $field => $type){ + if(is_int($type)){ + $this->data[] = $this->get($type); + continue; + } + switch($type){ + case "special1": + switch($this->pid){ + case 0x05: + $this->data[] = $this->get(true); + break; + } + break; + case "magic": + $this->data[] = $this->get(16); + break; + case "int": + $this->data[] = Utils::readInt($this->get(4)); + if($field === 5 and $this->pid === "17" and $this->data[$field] === 0){ + $continue = false; + } + break; + case "string": + $this->data[] = $this->get(Utils::readShort($this->get(2)) << 1); + break; + case "long": + $this->data[] = Utils::readLong($this->get(8)); + break; + case "byte": + $this->data[] = Utils::readByte($this->get(1)); + break; + case "ubyte": + $this->data[] = ord($this->get(1)); + break; + case "float": + $this->data[] = Utils::readFloat($this->get(4)); + break; + case "double": + $this->data[] = Utils::readDouble($this->get(8)); + break; + case "ushort": + $this->data[] = Utils::readShort($this->get(2), false); + break; + case "short": + $this->data[] = Utils::readShort($this->get(2)); + break; + case "bool": + case "boolean": + $this->data[] = Utils::readBool($this->get(1)); + break; + } + if($continue === false){ + break; + } + } + } + + + + +} \ No newline at end of file diff --git a/classes/PocketMinecraftClient.class.php b/classes/PocketMinecraftClient.class.php new file mode 100644 index 000000000..0df1c85e6 --- /dev/null +++ b/classes/PocketMinecraftClient.class.php @@ -0,0 +1,33 @@ +player = new Player($username); + $this->version = (int) $version; + $this->username = $username; + $this->cnt = 1; + $this->serverID = Utils::readDouble(substr(Utils::generateKey(), 0, 8)); + $this->events = array("disabled" => array()); + $this->protocol = (int) $protocol; + $this->interface = new MinecraftInterface("255.255.255.255", $this->protocol, 19132, true); + console("[INFO] Creating Minecraft Server"); + console("[INFO] Username: ".$this->username); + console("[INFO] Protocol: ".$this->protocol); + $this->stop = false; + } + + public function start(){ + $this->event("onReceivedPacket", "packetHandler", true); + $this->process(); + } + + public function packetHandler($packet, $event){ + $data =& $packet["data"]; + switch($packet["pid"]){ + case 0x02: + $this->send(0x1c, array( + $data[0], + $this->serverID, + MAGIC, + "MCCPP;Demo;". $this->username, + ), false, $packet["ip"], $packet["port"]); + break; + case 0x05: + $version = $data[1]; + $size = strlen($data[2]); + console("[DEBUG] ".$packet["ip"].":".$packet["port"]." v".$version." handshake (".$size.")", true, true, 2); + $this->send(0x06, array( + MAGIC, + $this->serverID, + $this->version, + $size, + ), false, $packet["ip"], $packet["port"]); + break; + case 0x07: + $bytes = $data[1]; + $port = $data[2]; + $size = $data[3]; + $x = $data[4]; + $sess = $data[5]; + //console("[DEBUG] ".$packet["ip"].":".$packet["port"]." v".$version." response (".$size.")", true, true, 2); + $sess2 = Utils::readInt(substr(Utils::generateKey(), 0, 4)); + $this->send(0x08, array( + MAGIC, + $x, + $sess2, + $bytes, + $packet["port"], + $size, + 0x00 + ), false, $packet["ip"], $packet["port"]); + break; + } + } + + public function send($pid, $data = array(), $raw = false, $dest = false, $port = false){ + $this->trigger($pid, $data); + $this->trigger("onSentPacket", $data); + $this->interface->writePacket($pid, $data, $raw, $dest, $port); + } + + public function process(){ + while($this->stop === false){ + $packet = $this->interface->readPacket(); + if($packet !== false){ + $this->trigger("onReceivedPacket", $packet); + $this->trigger($packet["pid"], $packet); + } + usleep(10000); + } + } + + public function trigger($event, $data = ""){ + console("[INTERNAL] Event ". $event, true, true, 3); + if(isset($this->events[$event]) and !isset($this->events["disabled"][$event])){ + foreach($this->events[$event] as $eid => $ev){ + if(isset($ev[1]) and ($ev[1] === true or is_object($ev[1]))){ + $this->responses[$eid] = call_user_func(array(($ev[1] === true ? $this:$ev[1]), $ev[0]), $data, $event, $this); + }else{ + $this->responses[$eid] = call_user_func($ev[0], $data, $event, $this); + } + } + } + } + public function toggleEvent($event){ + if(isset($this->events["disabled"][$event])){ + unset($this->events["disabled"][$event]); + console("[INTERNAL] Enabled event ".$event, true, true, 3); + }else{ + $this->events["disabled"][$event] = false; + console("[INTERNAL] Disabled event ".$event, true, true, 3); + } + } + + public function event($event, $func, $in = false){ + ++$this->cnt; + if(!isset($this->events[$event])){ + $this->events[$event] = array(); + } + $this->events[$event][$this->cnt] = array($func, $in); + console("[INTERNAL] Attached to event ".$event, true, true, 3); + return $this->cnt; + } + + public function deleteEvent($event, $id = -1){ + if($id === -1){ + unset($this->events[$event]); + }else{ + unset($this->events[$event][$id]); + if(isset($this->events[$event]) and count($this->events[$event]) === 0){ + unset($this->events[$event]); + } + } + } + +} \ No newline at end of file diff --git a/classes/Session.class.php b/classes/Session.class.php new file mode 100644 index 000000000..f0f25aad4 --- /dev/null +++ b/classes/Session.class.php @@ -0,0 +1,33 @@ +errors = array_fill(88,(125 - 88) + 1, true); + $this->server = $server; + $this->port = $port; + if($socket !== false){ + $this->sock = $socket; + $this->connected = true; + $this->buffer = array(); + $this->unblock(); + }else{ + $this->sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + socket_set_option($this->sock, SOL_SOCKET, SO_BROADCAST, 1); + if($listen !== true){ + $this->connected = true; + $this->buffer = array(); + $this->unblock(); + }else{ + socket_bind($this->sock, "0.0.0.0", $port); + $this->unblock(); + } + } + } + + function listenSocket(){ + $sock = @socket_accept($this->sock); + if($sock !== false){ + $sock = new Socket(false, false, false, $sock); + $sock->unblock(); + return $sock; + } + return false; + } + + public function close($error = 125){ + $this->connected = false; + if($error === false){ + console("[ERROR] [Socket] Socket closed, Error: End of Stream"); + }else{ + console("[ERROR] [Socket] Socket closed, Error $error: ".socket_strerror($error)); + } + return @socket_close($this->sock); + } + + public function block(){ + socket_set_block($this->sock); + } + + public function unblock(){ + socket_set_nonblock($this->sock); + } + + public function read(){ + $source = false; + $port = 1; + $len = @socket_recvfrom($this->sock, $buf, 65536, 0, $source, $port); + return array($buf, $source, $port, $len); + } + + public function write($data, $dest = false, $port = false){ + return @socket_sendto($this->sock, $data, strlen($data), 0, ($dest === false ? $this->server:$dest), ($port === false ? $this->port:$port)); + } + +} + +?> \ No newline at end of file diff --git a/classes/Utils.class.php b/classes/Utils.class.php new file mode 100644 index 000000000..7c042863c --- /dev/null +++ b/classes/Utils.class.php @@ -0,0 +1,314 @@ + "0000", + "1" => "0001", + "2" => "0010", + "3" => "0011", + "4" => "0100", + "5" => "0101", + "6" => "0110", + "7" => "0111", + "8" => "1000", + "9" => "1001", + "a" => "1010", + "b" => "1011", + "c" => "1100", + "d" => "1101", + "e" => "1110", + "f" => "1111", + ); + + public static function round($number){ + return round($number, 0, PHP_ROUND_HALF_DOWN); + } + + public static function generateKey($startEntropy = ""){ + //not much entropy, but works ^^ + $entropy = array( + implode(stat(__FILE__)), + lcg_value(), + print_r($_SERVER, true), + implode(mt_rand(0,394),get_defined_constants()), + get_current_user(), + print_r(ini_get_all(),true), + (string) memory_get_usage(), + php_uname(), + phpversion(), + zend_version(), + getmypid(), + mt_rand(), + rand(), + implode(get_loaded_extensions()), + sys_get_temp_dir(), + disk_free_space("."), + disk_total_space("."), + (function_exists("openssl_random_pseudo_bytes") and version_compare(PHP_VERSION, "5.3.4", ">=")) ? openssl_random_pseudo_bytes(16):microtime(true), + function_exists("mcrypt_create_iv") ? mcrypt_create_iv(16, MCRYPT_DEV_URANDOM):microtime(true), + uniqid(microtime(true),true), + file_exists("/dev/urandom") ? fread(fopen("/dev/urandom", "rb"),16):microtime(true), + ); + shuffle($entropy); + $value = Utils::hexToStr(md5((string) $startEntropy)); + unset($startEntropy); + foreach($entropy as $c){ + $c = (string) $c; + for($i = 0; $i < 4; ++$i){ + $value ^= md5($i . $c . microtime(true), true); + $value ^= substr(sha1($i . $c . microtime(true), true),$i,16); + } + } + return $value; + } + + public static function sha1($input){ + $binary = Utils::hexToBin(sha1($input)); + $negative = false; + $len = strlen($binary); + if($binary{0} === "1"){ + $negative = true; + for($i = 0; $i < $len; ++$i){ + $binary{$i} = $binary{$i} === "1" ? "0":"1"; + } + for($i = $len - 1; $i >= 0; --$i){ + if($binary{$i} === "1"){ + $binary{$i} = "0"; + }else{ + $binary{$i} = "1"; + break; + } + } + } + + $hash = Utils::binToHex($binary); + $len = strlen($hash); + for($i = 0; $i < $len; ++$i){ + if($hash{$i} === "0"){ + $hash{$i} = "x"; + }else{ + break; + } + } + + return ($negative === true ? "-":"").str_replace("x", "", $hash); + } + + public static function microtime(){ + return microtime(true); + } + + public static function curl_get($page){ + $ch = curl_init($page); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('User-Agent: Minecraft PHP Client 2')); + curl_setopt($ch, CURLOPT_AUTOREFERER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); + $ret = curl_exec($ch); + curl_close($ch); + return $ret; + } + + public static function curl_post($page, $args, $timeout = 10){ + $ch = curl_init($page); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $args); + curl_setopt($ch, CURLOPT_AUTOREFERER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('User-Agent: Minecraft PHP Client 2')); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout); + $ret = curl_exec($ch); + curl_close($ch); + return $ret; + } + + public static function strToBin($str){ + return Utils::hexToBin(Utils::strToHex($str)); + } + + public static function hexToBin($hex){ + $bin = ""; + $len = strlen($hex); + for($i = 0; $i < $len; ++$i){ + $bin .= Utils::$hexToBin[$hex{$i}]; + } + return $bin; + } + + public static function binToStr($bin){ + $len = strlen($bin); + if(($len % 8) != 0){ + $bin = substr($bin, 0, -($len % 8)); + } + $str = ""; + for($i = 0; $i < $len; $i += 8){ + $str .= chr(bindec(substr($bin, $i, 8))); + } + return $str; + } + public static function binToHex($bin){ + $len = strlen($bin); + if(($len % 8) != 0){ + $bin = substr($bin, 0, -($len % 8)); + } + $hex = ""; + for($i = 0; $i < $len; $i += 4){ + $hex .= dechex(bindec(substr($bin, $i, 4))); + } + return $hex; + } + + public static function strToHex($str){ + return bin2hex($str); + } + + public static function hexToStr($hex){ + if(HEX2BIN === true){ + return hex2bin($hex); + } + return pack("H*" , $hex); + } + + public static function readString($str){ + return preg_replace('/\x00(.)/s', '$1', $str); + } + + public static function writeString($str){ + return preg_replace('/(.)/s', "\x00$1", $str); + } + + public static function readBool($b){ + return Utils::readByte($b, false) === 0 ? false:true; + } + + public static function writeBool($b){ + return Utils::writeByte($b === true ? 1:0); + } + + public static function readByte($c, $signed = true){ + $b = ord($c{0}); + if($signed === true and ($b & 0x80) === 0x80){ //calculate Two's complement + $b = -0x80 + ($b & 0x7f); + } + return $b; + } + + public static function writeByte($c){ + if($c > 0xff){ + return false; + } + if($c < 0 and $c >= -0x80){ + $c = 0xff + $c + 1; + } + return chr($c); + } + + public static function readShort($str, $signed = true){ + list(,$unpacked) = unpack("n", $str); + if($unpacked > 0x7fff and $signed === true){ + $unpacked -= 0x10000; // Convert unsigned short to signed short + } + return $unpacked; + } + + public static function writeShort($value){ + if($value < 0){ + $value += 0x10000; + } + return pack("n", $value); + } + + public static function readInt($str){ + list(,$unpacked) = unpack("N", $str); + return (int) $unpacked; + } + + public static function writeInt($value){ + if($value < 0){ + $value += 0x100000000; + } + return pack("N", $value); + } + + public static function readFloat($str){ + list(,$value) = ENDIANNESS === BIG_ENDIAN?unpack('f', $str):unpack('f', strrev($str)); + return $value; + } + + public static function writeFloat($value){ + return ENDIANNESS === BIG_ENDIAN?pack('f', $value):strrev(pack('f', $value)); + } + + public static function readDouble($str){ + list(,$value) = ENDIANNESS === BIG_ENDIAN?unpack('d', $str):unpack('d', strrev($str)); + return $value; + } + + public static function writeDouble($value){ + return ENDIANNESS === BIG_ENDIAN?pack('d', $value):strrev(pack('d', $value)); + } + + public static function readLong($str){ + list(,$firstHalf,$secondHalf) = unpack("N*", $str); + if(GMPEXT === true){ + $value = gmp_add($secondHalf, gmp_mul($firstHalf, "0x100000000")); + if(gmp_cmp($value, "0x8000000000000000") >= 0){ + $value = gmp_sub($value, "0x10000000000000000"); + } + return gmp_strval($value); + }else{ + $value = bcadd($secondHalf, bcmul($firstHalf, "4294967296")); + if(bccomp($value, "9223372036854775808") >= 0){ + $value = bcsub($value, "18446744073709551616"); + } + return $value; + } + } + + public static function writeLong($value){ + return ENDIANNESS === BIG_ENDIAN?pack('d', $value):strrev(pack('d', $value)); + } + +} \ No newline at end of file diff --git a/common/config.php b/common/config.php new file mode 100644 index 000000000..fa58cddc2 --- /dev/null +++ b/common/config.php @@ -0,0 +1,39 @@ + 0){ + console("[ERROR] Use PHP >= 5.3.3", true, true, 0); + ++$errors; +} + +if(version_compare("5.4.0", PHP_VERSION) > 0){ + console("[NOTICE] Use PHP >= 5.4.0 to increase performance", true, true, 0); + define("HEX2BIN", false); +}else{ + define("HEX2BIN", true); +} + +if(php_sapi_name()!=="cli"){ + console("[ERROR] Use PHP-CLI to execute the client or create your own", true, true, 0); + ++$errors; +} + +if(!extension_loaded("gmp")){ + console("[NOTICE] Enable GMP extension to increase performance", true, true, 0); + define("GMPEXT", false); +}else{ + define("GMPEXT", true); +} + +if(!function_exists("gzinflate")){ + console("[ERROR] Unable to find Zlib extension", true, true, 0); + ++$errors; +} + +if(!function_exists("socket_create")){ + console("[ERROR] Unable to find Socket functions", true, true, 0); + ++$errors; +} + +if($errors > 0){ + die(); +} + +require_once("classes/Utils.class.php"); +require_once("classes/Socket.class.php"); +require_once("classes/Packet.class.php"); +require_once("classes/MinecraftInterface.class.php"); + +?> \ No newline at end of file diff --git a/common/functions.php b/common/functions.php new file mode 100644 index 000000000..314c5bf60 --- /dev/null +++ b/common/functions.php @@ -0,0 +1,180 @@ + array(), + 'commands' => array(), + 'flags' => array() + ); + + foreach ( $args as $arg ) { + + // Is it a command? (prefixed with --) + if ( substr( $arg, 0, 2 ) === '--' ) { + + $value = preg_split( '/[= ]/', $arg, 2 ); + $com = substr( array_shift($value), 2 ); + $value = join($value); + + $ret['commands'][$com] = !empty($value) ? $value : true; + continue; + + } + + // Is it a flag? (prefixed with -) + if ( substr( $arg, 0, 1 ) === '-' ) { + $ret['flags'][] = substr( $arg, 1 ); + continue; + } + + $ret['input'][] = $arg; + continue; + + } + + return $ret; +} + +function console($message, $EOL = true, $log = true, $level = 1){ + //global $path; + if(!defined("DEBUG") or DEBUG >= $level){ + $message .= $EOL === true ? PHP_EOL:""; + $message = date("H:i:s"). " ". $message; + if($log === true and (!defined("LOG") or LOG === true)){ + logg($message, "console", false, $level); + } + echo $message; + } +} + +function logg($message, $name, $EOL = true, $level = 2, $close = false){ + global $fpointers; + if((!defined("DEBUG") or DEBUG >= $level) and (!defined("LOG") or LOG === true)){ + $message .= $EOL === true ? PHP_EOL:""; + if(!isset($fpointers)){ + $fpointers = array(); + } + if(!isset($fpointers[$name])){ + $fpointers[$name] = fopen(FILE_PATH."/".$name.".log", "ab"); + } + fwrite($fpointers[$name], $message); + if($close === true){ + fclose($fpointers[$name]); + unset($fpointers[$name]); + } + } +} + +function hexdump($data, $htmloutput = true, $uppercase = false, $return = false) +{ + // Init + $hexi = ''; + $ascii = ''; + $dump = ($htmloutput === true) ? '
' : '';
+    $offset = 0;
+    $len    = strlen($data);
+ 
+    // Upper or lower case hexadecimal
+    $x = ($uppercase === false) ? 'x' : 'X';
+ 
+    // Iterate string
+    for ($i = $j = 0; $i < $len; $i++)
+    {
+        // Convert to hexidecimal
+        $hexi .= Utils::strToHex($data[$i]);
+ 
+        // Replace non-viewable bytes with '.'
+        if (ord($data[$i]) >= 0x20 and ord($data[$i]) < 0x80) {
+            $ascii .= ($htmloutput === true) ?
+                            htmlentities($data[$i]) :
+                            $data[$i];
+        } else {
+            $ascii .= '.';
+        }
+ 
+        // Add extra column spacing
+        if ($j === 7) {
+            $hexi  .= ' ';
+            $ascii .= ' ';
+        }
+ 
+        // Add row
+        if (++$j === 16 || $i === $len - 1) {
+            // Join the hexi / ascii output
+            $dump .= sprintf("%04$x  %-49s  %s", $offset, $hexi, $ascii);
+ 
+            // Reset vars
+            $hexi   = $ascii = '';
+            $offset += 16;
+            $j      = 0;
+ 
+            // Add newline
+            if ($i !== $len - 1) {
+                $dump .= "\n";
+            }
+        }
+    }
+ 
+    // Finish dump
+    $dump .= $htmloutput === true ?
+                '
' : + ''; + $dump .= "\n"; + + $dump = preg_replace("/[^[:print:]\\r\\n]/", ".", $dump); + + // Output method + if ($return === false) { + echo $dump; + } else { + return $dump; + } +} \ No newline at end of file diff --git a/pstruct/RakNet.php b/pstruct/RakNet.php new file mode 100644 index 000000000..3ef187ccc --- /dev/null +++ b/pstruct/RakNet.php @@ -0,0 +1,90 @@ + array( + "double", + "magic", + ), + + 0x05 => array( + "magic", + "byte", + "special1", + ), + + 0x06 => array( + "magic", + "double", + "byte", + "short", + ), + + 0x07 => array( + "magic", + 5, + "short", + "short", + "int", + "int", + ), + + 0x08 => array( + "magic", + "int", + "int", + 5, + "short", + "short", + "byte", + ), + + 0x1c => array( + "double", + "double", + "magic", + "string", + ), + + 0x1d => array( + "double", + "double", + "magic", + "string", + ), + + 0x84 => array( + "byte", + 9, + "int", + "int", + 5, + 3, + "byte", + ), + +); \ No newline at end of file diff --git a/pstruct/packetName.php b/pstruct/packetName.php new file mode 100644 index 000000000..b06215082 --- /dev/null +++ b/pstruct/packetName.php @@ -0,0 +1,37 @@ + "ID_UNCONNECTED_PING_OPEN_CONNECTIONS", + 0x05 => "ID_OPEN_CONNECTION_REQUEST_1", + 0x06 => "ID_OPEN_CONNECTION_REPLY_1", + 0x07 => "ID_OPEN_CONNECTION_REQUEST_2", + 0x08 => "ID_OPEN_CONNECTION_REPLY_2", + 0x1c => "ID_UNCONNECTED_PONG", + 0x1d => "ID_ADVERTISE_SYSTEM", + +); \ No newline at end of file diff --git a/server.php b/server.php new file mode 100644 index 000000000..256faa32f --- /dev/null +++ b/server.php @@ -0,0 +1,33 @@ +start(); \ No newline at end of file