1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
<?php
/**
* Service discovery using DNS SRV records
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
/**
* @since 1.29
*/
class DnsSrvDiscoverer {
/**
* @var string
*/
private $service;
/**
* @var string
*/
private $protocol;
/**
* @var string|null
*/
private $domain;
/**
* @var callable
*/
private $resolver;
/**
* Construct a new discoverer for the given domain, service, and protocol.
*
* @param string $service Name of the service to discover.
* @param string $protocol Service protocol. Defaults to 'tcp'
* @param ?string $domain The hostname/domain on which to perform discovery
* of the given service and protocol. Defaults to null which effectively
* performs a query relative to the host's configured search domain.
* @param ?callable $resolver Resolver function. Defaults to using
* dns_get_record. Primarily useful in testing.
*/
public function __construct(
string $service,
string $protocol = 'tcp',
?string $domain = null,
?callable $resolver = null
) {
$this->service = $service;
$this->protocol = $protocol;
$this->domain = $domain;
$this->resolver = $resolver ?? static function ( $srv ) {
return dns_get_record( $srv, DNS_SRV );
};
}
/**
* Queries the resolver for an SRV resource record matching the service,
* protocol, and domain and returns all target/port/priority/weight
* records.
*
* @return array
*/
public function getRecords() {
$result = [];
$records = ( $this->resolver )( $this->getSrvName() );
// Respect RFC 2782 with regard to a single '.' entry denoting a valid
// empty response
if (
!$records
|| ( count( $records ) === 1 && $records[0]['target'] === '.' )
) {
return $result;
}
foreach ( $records as $record ) {
$result[] = [
'target' => $record['target'],
'port' => (int)$record['port'],
'pri' => (int)$record['pri'],
'weight' => (int)$record['weight'],
];
}
return $result;
}
/**
* Performs discovery for the domain, service, and protocol, and returns a
* list of resolved server name/ip and port number pairs sorted by each
* record's priority, with servers of the same priority randomly shuffled.
*
* @return array
*/
public function getServers() {
$records = $this->getRecords();
usort( $records, static function ( $a, $b ) {
if ( $a['pri'] === $b['pri'] ) {
return mt_rand( 0, 1 ) ? 1 : -1;
}
return $a['pri'] - $b['pri'];
} );
$serversAndPorts = [];
foreach ( $records as $record ) {
$serversAndPorts[] = [ $record['target'], $record['port'] ];
}
return $serversAndPorts;
}
/**
* Returns the SRV resource record name.
*/
public function getSrvName(): string {
$srv = "_{$this->service}._{$this->protocol}";
if ( $this->domain === null || $this->domain === '' ) {
return $srv;
}
return "$srv.{$this->domain}";
}
}
|