mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-01 07:39:57 +00:00
Improved UPnP exception handling
This commit is contained in:
parent
ec7ea98ead
commit
2ee86f0edf
@ -1108,11 +1108,7 @@ class Server{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if((bool) $this->configGroup->getProperty("network.upnp-forwarding", false)){
|
if((bool) $this->configGroup->getProperty("network.upnp-forwarding", false)){
|
||||||
try{
|
|
||||||
$this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort()));
|
$this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort()));
|
||||||
}catch(\RuntimeException $e){
|
|
||||||
$this->logger->alert("UPnP portforward failed: " . $e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if((bool) $this->configGroup->getProperty("settings.send-usage", true)){
|
if((bool) $this->configGroup->getProperty("settings.send-usage", true)){
|
||||||
|
@ -95,13 +95,16 @@ class UPnP{
|
|||||||
throw new \RuntimeException("PCRE error: $message");
|
throw new \RuntimeException("PCRE error: $message");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws UPnPException
|
||||||
|
*/
|
||||||
public static function getServiceUrl() : string{
|
public static function getServiceUrl() : string{
|
||||||
$socket = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
|
$socket = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
|
||||||
if($socket === false){
|
if($socket === false){
|
||||||
throw new \RuntimeException("Socket error: " . trim(socket_strerror(socket_last_error())));
|
throw new AssumptionFailedError("Socket error: " . trim(socket_strerror(socket_last_error())));
|
||||||
}
|
}
|
||||||
if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ["sec" => 3, "usec" => 0])){
|
if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ["sec" => 3, "usec" => 0])){
|
||||||
throw new \RuntimeException("Socket error: " . trim(socket_strerror(socket_last_error($socket))));
|
throw new AssumptionFailedError("Socket error: " . trim(socket_strerror(socket_last_error($socket))));
|
||||||
}
|
}
|
||||||
$contents =
|
$contents =
|
||||||
"M-SEARCH * HTTP/1.1\r\n" .
|
"M-SEARCH * HTTP/1.1\r\n" .
|
||||||
@ -113,17 +116,17 @@ class UPnP{
|
|||||||
for($i = 0; $i < self::MAX_DISCOVERY_ATTEMPTS; ++$i){
|
for($i = 0; $i < self::MAX_DISCOVERY_ATTEMPTS; ++$i){
|
||||||
$sendbyte = @socket_sendto($socket, $contents, strlen($contents), 0, "239.255.255.250", 1900);
|
$sendbyte = @socket_sendto($socket, $contents, strlen($contents), 0, "239.255.255.250", 1900);
|
||||||
if($sendbyte === false){
|
if($sendbyte === false){
|
||||||
throw new \RuntimeException("Socket error: " . trim(socket_strerror(socket_last_error($socket))));
|
throw new UPnPException("Socket error: " . trim(socket_strerror(socket_last_error($socket))));
|
||||||
}
|
}
|
||||||
if($sendbyte !== strlen($contents)){
|
if($sendbyte !== strlen($contents)){
|
||||||
throw new \RuntimeException("Socket error: Unable to send the entire contents.");
|
throw new UPnPException("Socket error: Unable to send the entire contents.");
|
||||||
}
|
}
|
||||||
while(true){
|
while(true){
|
||||||
if(@socket_recvfrom($socket, $buffer, 1024, 0, $responseHost, $responsePort) === false){
|
if(@socket_recvfrom($socket, $buffer, 1024, 0, $responseHost, $responsePort) === false){
|
||||||
if(socket_last_error($socket) === SOCKET_ETIMEDOUT){
|
if(socket_last_error($socket) === SOCKET_ETIMEDOUT){
|
||||||
continue 2;
|
continue 2;
|
||||||
}
|
}
|
||||||
throw new \RuntimeException("Socket error: " . trim(socket_strerror(socket_last_error($socket))));
|
throw new UPnPException("Socket error: " . trim(socket_strerror(socket_last_error($socket))));
|
||||||
}
|
}
|
||||||
$pregResult = preg_match('/location\s*:\s*(.+)\n/i', $buffer, $matches);
|
$pregResult = preg_match('/location\s*:\s*(.+)\n/i', $buffer, $matches);
|
||||||
if($pregResult === false){
|
if($pregResult === false){
|
||||||
@ -138,33 +141,33 @@ class UPnP{
|
|||||||
}
|
}
|
||||||
socket_close($socket);
|
socket_close($socket);
|
||||||
if($location === null){
|
if($location === null){
|
||||||
throw new \RuntimeException("Unable to find the router. Ensure that network discovery is enabled in Control Panel.");
|
throw new UPnPException("Unable to find the router. Ensure that network discovery is enabled in Control Panel.");
|
||||||
}
|
}
|
||||||
$url = parse_url($location);
|
$url = parse_url($location);
|
||||||
if($url === false){
|
if($url === false){
|
||||||
throw new \RuntimeException("Failed to parse the router's url: {$location}");
|
throw new UPnPException("Failed to parse the router's url: {$location}");
|
||||||
}
|
}
|
||||||
if(!isset($url['host'])){
|
if(!isset($url['host'])){
|
||||||
throw new \RuntimeException("Failed to recognize the host name from the router's url: {$location}");
|
throw new UPnPException("Failed to recognize the host name from the router's url: {$location}");
|
||||||
}
|
}
|
||||||
$urlHost = $url['host'];
|
$urlHost = $url['host'];
|
||||||
if(!isset($url['port'])){
|
if(!isset($url['port'])){
|
||||||
throw new \RuntimeException("Failed to recognize the port number from the router's url: {$location}");
|
throw new UPnPException("Failed to recognize the port number from the router's url: {$location}");
|
||||||
}
|
}
|
||||||
$urlPort = $url['port'];
|
$urlPort = $url['port'];
|
||||||
$response = Internet::getURL($location, 3, [], $err);
|
$response = Internet::getURL($location, 3, [], $err);
|
||||||
if($response === null){
|
if($response === null){
|
||||||
throw new \RuntimeException("Unable to access XML: {$err}");
|
throw new UPnPException("Unable to access XML: {$err}");
|
||||||
}
|
}
|
||||||
if($response->getCode() !== 200){
|
if($response->getCode() !== 200){
|
||||||
throw new \RuntimeException("Unable to access XML: {$response->getBody()}");
|
throw new UPnPException("Unable to access XML: {$response->getBody()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
$defaultInternalError = libxml_use_internal_errors(true);
|
$defaultInternalError = libxml_use_internal_errors(true);
|
||||||
try{
|
try{
|
||||||
$root = new \SimpleXMLElement($response->getBody());
|
$root = new \SimpleXMLElement($response->getBody());
|
||||||
}catch(\Exception $e){
|
}catch(\Exception $e){
|
||||||
throw new \RuntimeException("Broken XML.");
|
throw new UPnPException("Broken XML.");
|
||||||
}
|
}
|
||||||
libxml_use_internal_errors($defaultInternalError);
|
libxml_use_internal_errors($defaultInternalError);
|
||||||
$root->registerXPathNamespace("upnp", "urn:schemas-upnp-org:device-1-0");
|
$root->registerXPathNamespace("upnp", "urn:schemas-upnp-org:device-1-0");
|
||||||
@ -180,13 +183,16 @@ class UPnP{
|
|||||||
throw new AssumptionFailedError("xpath query should not error here");
|
throw new AssumptionFailedError("xpath query should not error here");
|
||||||
}
|
}
|
||||||
if(count($xpathResult) === 0){
|
if(count($xpathResult) === 0){
|
||||||
throw new \RuntimeException("Your router does not support portforwarding");
|
throw new UPnPException("Your router does not support portforwarding");
|
||||||
}
|
}
|
||||||
$controlURL = (string) $xpathResult[0];
|
$controlURL = (string) $xpathResult[0];
|
||||||
$serviceURL = sprintf("%s:%d/%s", $urlHost, $urlPort, $controlURL);
|
$serviceURL = sprintf("%s:%d/%s", $urlHost, $urlPort, $controlURL);
|
||||||
return $serviceURL;
|
return $serviceURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws UPnPException
|
||||||
|
*/
|
||||||
public static function portForward(string $serviceURL, string $internalIP, int $internalPort, int $externalPort) : void{
|
public static function portForward(string $serviceURL, string $internalIP, int $internalPort, int $externalPort) : void{
|
||||||
$body =
|
$body =
|
||||||
'<u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">' .
|
'<u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">' .
|
||||||
@ -211,7 +217,7 @@ class UPnP{
|
|||||||
];
|
];
|
||||||
|
|
||||||
if(Internet::postURL($serviceURL, $contents, 3, $headers, $err) === null){
|
if(Internet::postURL($serviceURL, $contents, 3, $headers, $err) === null){
|
||||||
throw new \RuntimeException("Failed to portforward using UPnP: " . $err);
|
throw new UPnPException("Failed to portforward using UPnP: " . $err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
src/network/upnp/UPnPException.php
Normal file
28
src/network/upnp/UPnPException.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\network\upnp;
|
||||||
|
|
||||||
|
final class UPnPException extends \RuntimeException{
|
||||||
|
|
||||||
|
}
|
@ -52,9 +52,12 @@ final class UPnPNetworkInterface implements NetworkInterface{
|
|||||||
$this->logger->info("Attempting to portforward...");
|
$this->logger->info("Attempting to portforward...");
|
||||||
$this->serviceURL = UPnP::getServiceUrl();
|
$this->serviceURL = UPnP::getServiceUrl();
|
||||||
|
|
||||||
|
try{
|
||||||
UPnP::portForward($this->serviceURL, Internet::getInternalIP(), $this->port, $this->port);
|
UPnP::portForward($this->serviceURL, Internet::getInternalIP(), $this->port, $this->port);
|
||||||
|
|
||||||
$this->logger->info("Forwarded $this->ip:$this->port to external port $this->port");
|
$this->logger->info("Forwarded $this->ip:$this->port to external port $this->port");
|
||||||
|
}catch(UPnPException $e){
|
||||||
|
$this->logger->error("UPnP portforward failed: " . $e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setName(string $name) : void{
|
public function setName(string $name) : void{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user