Skip to content

Commit d75f553

Browse files
committed
[3.0] Respect attempts and timeout from resolv.conf
1 parent 30c5e43 commit d75f553

File tree

5 files changed

+85
-25
lines changed

5 files changed

+85
-25
lines changed

src/Config/Config.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,29 @@ public static function loadResolvConfBlocking($path = null)
9797
}
9898
}
9999

100+
$matches = [];
101+
preg_match_all('/^options.*\s*$/m', $contents, $matches);
102+
if (isset($matches[0][0])) {
103+
$options = preg_split('/\s+/', trim($matches[0][0]));
104+
array_shift($options);
105+
106+
foreach ($options as $option) {
107+
$value = null;
108+
if (strpos($option, ':') !== false) {
109+
[$option, $value] = explode(':', $option, 2);
110+
}
111+
112+
switch ($option) {
113+
case 'attempts':
114+
$config->options->attempts = ((int) $value) > 5 ? 5 : (int) $value;
115+
break;
116+
case 'timeout':
117+
$config->options->timeout = ((int) $value) > 30 ? 30 : (int) $value;
118+
break;
119+
}
120+
}
121+
}
122+
100123
return $config;
101124
}
102125

@@ -134,4 +157,12 @@ public static function loadWmicBlocking($command = null)
134157
}
135158

136159
public $nameservers = [];
160+
/**
161+
* @var Options
162+
*/
163+
public $options;
164+
165+
public function __construct() {
166+
$this->options = new Options();
167+
}
137168
}

src/Config/Options.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace React\Dns\Config;
4+
5+
use RuntimeException;
6+
7+
final class Options
8+
{
9+
/**
10+
* @var int<1, 5>
11+
*/
12+
public $attempts = 2;
13+
/**
14+
* @var int<1, 30>
15+
*/
16+
public $timeout = 5;
17+
}

src/Resolver/Factory.php

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use React\Cache\CacheInterface;
77
use React\Dns\Config\Config;
88
use React\Dns\Config\HostsFile;
9+
use React\Dns\Config\Options;
910
use React\Dns\Query\CachingExecutor;
1011
use React\Dns\Query\CoopExecutor;
1112
use React\Dns\Query\ExecutorInterface;
@@ -125,54 +126,60 @@ private function createExecutor($nameserver, LoopInterface $loop)
125126

126127
if ($tertiary !== false) {
127128
// 3 DNS servers given => nest first with fallback for second and third
128-
return new CoopExecutor(
129-
new RetryExecutor(
129+
return $this->createTopLevelDecoratingExectors(
130+
new FallbackExecutor(
131+
$this->createSingleExecutor($primary, $nameserver->options, $loop),
130132
new FallbackExecutor(
131-
$this->createSingleExecutor($primary, $loop),
132-
new FallbackExecutor(
133-
$this->createSingleExecutor($secondary, $loop),
134-
$this->createSingleExecutor($tertiary, $loop)
135-
)
133+
$this->createSingleExecutor($secondary, $nameserver->options, $loop),
134+
$this->createSingleExecutor($tertiary, $nameserver->options, $loop)
136135
)
137-
)
136+
),
137+
$nameserver
138138
);
139139
} elseif ($secondary !== false) {
140140
// 2 DNS servers given => fallback from first to second
141-
return new CoopExecutor(
142-
new RetryExecutor(
143-
new FallbackExecutor(
144-
$this->createSingleExecutor($primary, $loop),
145-
$this->createSingleExecutor($secondary, $loop)
146-
)
147-
)
141+
return $this->createTopLevelDecoratingExectors(
142+
new FallbackExecutor(
143+
$this->createSingleExecutor($primary, $nameserver->options, $loop),
144+
$this->createSingleExecutor($secondary, $nameserver->options, $loop)
145+
),
146+
$nameserver
148147
);
149148
} else {
150149
// 1 DNS server given => use single executor
151150
$nameserver = $primary;
152151
}
153152
}
154153

155-
return new CoopExecutor(new RetryExecutor($this->createSingleExecutor($nameserver, $loop)));
154+
return $this->createTopLevelDecoratingExectors($this->createSingleExecutor($nameserver, new Options(), $loop), $nameserver);
155+
}
156+
157+
private function createTopLevelDecoratingExectors(ExecutorInterface $executor, $nameserver)
158+
{
159+
$executor = new RetryExecutor($executor, (is_string($nameserver) ? new Options() : $nameserver->options)->attempts);
160+
161+
return new CoopExecutor($executor);
156162
}
157163

158164
/**
159165
* @param string $nameserver
166+
* @param Options $options
160167
* @param LoopInterface $loop
161168
* @return ExecutorInterface
162169
* @throws \InvalidArgumentException for invalid DNS server address
163170
*/
164-
private function createSingleExecutor($nameserver, LoopInterface $loop)
171+
private function createSingleExecutor($nameserver, Options $options, LoopInterface $loop)
165172
{
166173
$parts = \parse_url($nameserver);
167174

168175
if (isset($parts['scheme']) && $parts['scheme'] === 'tcp') {
169-
$executor = $this->createTcpExecutor($nameserver, $loop);
176+
$executor = $this->createTcpExecutor($nameserver, $options->timeout, $loop);
170177
} elseif (isset($parts['scheme']) && $parts['scheme'] === 'udp') {
171-
$executor = $this->createUdpExecutor($nameserver, $loop);
178+
$executor = $this->createUdpExecutor($nameserver, $options->timeout, $loop);
172179
} else {
173180
$executor = new SelectiveTransportExecutor(
174-
$this->createUdpExecutor($nameserver, $loop),
175-
$this->createTcpExecutor($nameserver, $loop)
181+
$this->createUdpExecutor($nameserver, $options->timeout, $loop),
182+
$this->createTcpExecutor($nameserver, $options->timeout, $loop)
176183
);
177184
}
178185

@@ -181,33 +188,35 @@ private function createSingleExecutor($nameserver, LoopInterface $loop)
181188

182189
/**
183190
* @param string $nameserver
191+
* @param int $timeout
184192
* @param LoopInterface $loop
185193
* @return TimeoutExecutor
186194
* @throws \InvalidArgumentException for invalid DNS server address
187195
*/
188-
private function createTcpExecutor($nameserver, LoopInterface $loop)
196+
private function createTcpExecutor($nameserver, int $timeout, LoopInterface $loop)
189197
{
190198
return new TimeoutExecutor(
191199
new TcpTransportExecutor($nameserver, $loop),
192-
5.0,
200+
$timeout,
193201
$loop
194202
);
195203
}
196204

197205
/**
198206
* @param string $nameserver
207+
* @param int $timeout
199208
* @param LoopInterface $loop
200209
* @return TimeoutExecutor
201210
* @throws \InvalidArgumentException for invalid DNS server address
202211
*/
203-
private function createUdpExecutor($nameserver, LoopInterface $loop)
212+
private function createUdpExecutor($nameserver, int $timeout, LoopInterface $loop)
204213
{
205214
return new TimeoutExecutor(
206215
new UdpTransportExecutor(
207216
$nameserver,
208217
$loop
209218
),
210-
5.0,
219+
$timeout,
211220
$loop
212221
);
213222
}

tests/Config/ConfigTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public function testLoadsFromExplicitPath()
3030
$config = Config::loadResolvConfBlocking(__DIR__ . '/../Fixtures/etc/resolv.conf');
3131

3232
$this->assertEquals(['8.8.8.8'], $config->nameservers);
33+
$this->assertEquals(4, $config->options->attempts);
34+
$this->assertEquals(29, $config->options->timeout);
3335
}
3436

3537
public function testLoadThrowsWhenPathIsInvalid()

tests/Fixtures/etc/resolv.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
nameserver 8.8.8.8
2+
options timeout:29 trust-ad attempts:4

0 commit comments

Comments
 (0)