Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion src/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Config
*
* Note that this method will try to access its files and/or commands and
* try to parse its output. Currently, this will only parse valid nameserver
* entries from `/etc/resolv.conf` and will ignore all other output without
* entries from its output and will ignore all other output without
* complaining.
*
* Note that the previous section implies that this may return an empty
Expand All @@ -28,6 +28,12 @@ class Config
*/
public static function loadSystemConfigBlocking()
{
// Use WMIC output on Windows
if (DIRECTORY_SEPARATOR === '\\') {
return self::loadWmicBlocking();
}

// otherwise (try to) load from resolv.conf
try {
return self::loadResolvConfBlocking();
} catch (RuntimeException $ignored) {
Expand Down Expand Up @@ -84,5 +90,38 @@ public static function loadResolvConfBlocking($path = null)
return $config;
}

/**
* Loads the DNS configurations from Windows's WMIC (from the given command or default command)
*
* Note that this method blocks while loading the given command and should
* thus be used with care! While this should be relatively fast for normal
* WMIC commands, it remains unknown if this may block under certain
* circumstances. In particular, this method should only be executed before
* the loop starts, not while it is running.
*
* Note that this method will only try to execute the given command try to
* parse its output, irrespective of whether this command exists. In
* particular, this command is only available on Windows. Currently, this
* will only parse valid nameserver entries from the command output and will
* ignore all other output without complaining.
*
* Note that the previous section implies that this may return an empty
* `Config` object if no valid nameserver entries can be found.
*
* @param ?string $command (advanced) should not be given (NULL) unless you know what you're doing
* @return self
* @link https://ss64.com/nt/wmic.html
*/
public static function loadWmicBlocking($command = null)
{
$contents = shell_exec($command === null ? 'wmic NICCONFIG get "DNSServerSearchOrder" /format:CSV' : $command);
preg_match_all('/(?:{|,|")([\da-f.:]{4,})(?:}|,|")/i', $contents, $matches);

$config = new self();
$config->nameservers = $matches[1];

return $config;
}

public $nameservers = array();
}
76 changes: 76 additions & 0 deletions tests/Config/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,80 @@ public function testParsesFileAndIgnoresCommentsAndInvalidNameserverEntries()
$config = Config::loadResolvConfBlocking('data://text/plain;base64,' . base64_encode($contents));
$this->assertEquals($expected, $config->nameservers);
}

public function testLoadsFromWmicOnWindows()
{
if (DIRECTORY_SEPARATOR !== '\\') {
$this->markTestSkipped('Only on Windows');
}

$config = Config::loadWmicBlocking();

$this->assertInstanceOf('React\Dns\Config\Config', $config);
}

public function testLoadsSingleEntryFromWmicOutput()
{
$contents = '
Node,DNSServerSearchOrder
ACE,
ACE,{192.168.2.1}
ACE,
';
$expected = array('192.168.2.1');

$config = Config::loadWmicBlocking($this->echoCommand($contents));

$this->assertEquals($expected, $config->nameservers);
}

public function testLoadsEmptyListFromWmicOutput()
{
$contents = '
Node,DNSServerSearchOrder
ACE,
';
$expected = array();

$config = Config::loadWmicBlocking($this->echoCommand($contents));

$this->assertEquals($expected, $config->nameservers);
}

public function testLoadsSingleEntryForMultipleNicsFromWmicOutput()
{
$contents = '
Node,DNSServerSearchOrder
ACE,
ACE,{192.168.2.1}
ACE,
ACE,{192.168.2.2}
ACE,
';
$expected = array('192.168.2.1', '192.168.2.2');

$config = Config::loadWmicBlocking($this->echoCommand($contents));

$this->assertEquals($expected, $config->nameservers);
}

public function testLoadsMultipleEntriesForSingleNicFromWmicOutput()
{
$contents = '
Node,DNSServerSearchOrder
ACE,
ACE,{"192.168.2.1","192.168.2.2"}
ACE,
';
$expected = array('192.168.2.1', '192.168.2.2');

$config = Config::loadWmicBlocking($this->echoCommand($contents));

$this->assertEquals($expected, $config->nameservers);
}

private function echoCommand($output)
{
return 'echo ' . escapeshellarg($output);
}
}