3
3
namespace NoGlitchYo \Dealdoh \Client ;
4
4
5
5
use Exception ;
6
- use InvalidArgumentException ;
7
6
use LogicException ;
7
+ use NoGlitchYo \Dealdoh \Client \Transport \DnsTransportInterface ;
8
8
use NoGlitchYo \Dealdoh \Entity \Dns \Message \Header ;
9
9
use NoGlitchYo \Dealdoh \Entity \Dns \MessageInterface ;
10
10
use NoGlitchYo \Dealdoh \Entity \DnsUpstream ;
@@ -22,112 +22,117 @@ class StdClient implements DnsClientInterface
22
22
* @var MessageFactoryInterface
23
23
*/
24
24
private $ dnsMessageFactory ;
25
+ /**
26
+ * @var DnsTransportInterface
27
+ */
28
+ private $ tcpTransport ;
29
+ /**
30
+ * @var DnsTransportInterface
31
+ */
32
+ private $ udpTransport ;
25
33
26
- public function __construct (MessageFactoryInterface $ dnsMessageFactory )
27
- {
34
+ public function __construct (
35
+ MessageFactoryInterface $ dnsMessageFactory ,
36
+ DnsTransportInterface $ tcpTransport ,
37
+ DnsTransportInterface $ udpTransport
38
+ ) {
28
39
$ this ->dnsMessageFactory = $ dnsMessageFactory ;
40
+ $ this ->tcpTransport = $ tcpTransport ;
41
+ $ this ->udpTransport = $ udpTransport ;
29
42
}
30
43
31
44
/**
32
45
* Resolve message using regular UDP/TCP queries towards DNS upstream
33
46
*
34
- * @param DnsUpstream $dnsUpstream
47
+ * @param DnsUpstream $dnsUpstream
35
48
* @param MessageInterface $dnsRequestMessage
36
49
*
37
50
* @return MessageInterface
38
51
* @throws Exception
39
52
*/
40
53
public function resolve (DnsUpstream $ dnsUpstream , MessageInterface $ dnsRequestMessage ): MessageInterface
41
54
{
42
- $ scheme = $ dnsUpstream ->getScheme ();
43
-
44
55
$ dnsRequestMessage = $ this ->enableRecursionForDnsMessage ($ dnsRequestMessage );
56
+ $ address = $ this ->getSanitizedUpstreamAddress ($ dnsUpstream );
45
57
46
- // Clean up the protocol from URI supported by the client but which can not be used with sockets (e.g. dns://).
47
- $ address = str_replace ($ scheme . ':// ' , '' , $ dnsUpstream ->getUri ());
48
-
49
- if (in_array ($ scheme , ['udp ' , 'dns ' ]) || $ dnsUpstream ->getScheme () === null ) {
50
- $ dnsWireResponseMessage = $ this ->sendWithSocket ('udp ' , $ address , $ dnsRequestMessage );
51
- } elseif ($ dnsUpstream ->getScheme () === 'tcp ' ) {
52
- $ dnsWireResponseMessage = $ this ->sendWithSocket ('tcp ' , $ address , $ dnsRequestMessage );
58
+ if ($ this ->isUdp ($ dnsUpstream )) {
59
+ $ dnsWireResponseMessage = $ this ->sendWith ('udp ' , $ address , $ dnsRequestMessage );
60
+ } elseif ($ this ->isTcp ($ dnsUpstream )) {
61
+ $ dnsWireResponseMessage = $ this ->sendWith ('tcp ' , $ address , $ dnsRequestMessage );
53
62
} else {
54
- throw new LogicException (sprintf ('Scheme `%s` is not supported ' , $ scheme ));
63
+ throw new LogicException (sprintf ('Scheme `%s` is not supported ' , $ dnsUpstream -> getScheme () ));
55
64
}
56
65
57
66
return $ dnsWireResponseMessage ;
58
67
}
59
68
60
-
61
69
public function supports (DnsUpstream $ dnsUpstream ): bool
62
70
{
63
- return in_array ($ dnsUpstream ->getScheme (), ['udp ' , 'tcp ' , 'dns ' ]) || $ dnsUpstream ->getScheme () === null ;
71
+ return $ this ->isUdp ($ dnsUpstream ) || $ this ->isTcp ($ dnsUpstream );
72
+ }
73
+
74
+ private function isUdp ($ dnsUpstream ): bool
75
+ {
76
+ return in_array ($ dnsUpstream ->getScheme (), ['udp ' , 'dns ' ]) || $ dnsUpstream ->getScheme () === null ;
77
+ }
78
+
79
+ private function isTcp ($ dnsUpstream ): bool
80
+ {
81
+ return $ dnsUpstream ->getScheme () === 'tcp ' ;
64
82
}
65
83
66
84
/**
67
- * Send DNS message using socket with the given protocol: UDP or TCP
68
- * @param string $protocol
85
+ * Send DNS message using socket with the chosen protocol: `udp` or `tcp`
86
+ * Allow a sender to force usage of a specific protocol (e.g. protocol blocked by network/firewall)
87
+ *
88
+ * @param string $protocol Protocol to use to send the message
69
89
* @param string $address
70
90
* @param MessageInterface $dnsRequestMessage
71
91
*
72
92
* @return MessageInterface
73
93
* @throws Exception
74
94
*/
75
- private function sendWithSocket (
95
+ private function sendWith (
76
96
string $ protocol ,
77
97
string $ address ,
78
98
MessageInterface $ dnsRequestMessage
79
99
): MessageInterface {
80
- $ url = parse_url ( $ address );
100
+ $ dnsWireMessage = $ this -> dnsMessageFactory -> createDnsWireMessageFromMessage ( $ dnsRequestMessage );
81
101
82
- $ socket = stream_socket_client ($ protocol . ':// ' . $ url ['host ' ] . ': ' . $ url ['port ' ], $ errno , $ errstr , 4 );
102
+ if ($ protocol === 'udp ' ) {
103
+ if (strlen ($ dnsWireMessage ) <= static ::EDNS_SIZE ) { // Must use TCP if message is bigger
104
+ $ dnsWireResponseMessage = $ this ->udpTransport ->send ($ address , $ dnsWireMessage );
83
105
84
- if ($ socket === false ) {
85
- throw new Exception ('Unable to connect: ' . $ errno . ' - ' . $ errstr );
86
- } else {
87
- $ dnsMessage = $ this ->dnsMessageFactory ->createDnsWireMessageFromMessage ($ dnsRequestMessage );
88
-
89
- switch ($ protocol ) {
90
- case 'udp ' :
91
- if (isset ($ dnsMessage [static ::EDNS_SIZE ])) { // Must use TCP if message is bigger
92
- return $ this ->sendWithSocket ('tcp ' , $ address , $ dnsRequestMessage );
93
- }
94
-
95
- \fputs ($ socket , $ dnsMessage );
96
-
97
- $ dnsWireResponseMessage = \fread ($ socket , static ::EDNS_SIZE );
98
- if ($ dnsWireResponseMessage === false ) {
99
- throw new Exception ('something happened ' );
100
- }
101
-
102
- break ;
103
- case 'tcp ' :
104
- \fputs ($ socket , $ dnsMessage );
105
- $ dnsWireResponseMessage = '' ;
106
- while (!feof ($ socket )) {
107
- $ dnsWireResponseMessage .= fgets ($ socket , 512 );
108
- }
109
- break ;
110
- default :
111
- throw new InvalidArgumentException (
112
- "Only `tcp`, `udp` are supported protocol to be used with socket. "
113
- );
106
+ $ message = $ this ->dnsMessageFactory ->createMessageFromDnsWireMessage ($ dnsWireResponseMessage );
107
+ // Only if message is not truncated response is returned, otherwise retry with TCP
108
+ if (!$ message ->getHeader ()->isTc ()) {
109
+ return $ message ;
110
+ }
114
111
}
115
112
}
116
113
117
- \fclose ( $ socket );
114
+ $ dnsWireResponseMessage = $ this -> tcpTransport -> send ( $ address , $ dnsWireMessage );
118
115
119
116
$ message = $ this ->dnsMessageFactory ->createMessageFromDnsWireMessage ($ dnsWireResponseMessage );
120
117
121
- // Message was truncated, retry with TCP
122
- if ($ message ->getHeader ()->isTc ()) {
123
- return $ this ->sendWithSocket ('tcp ' , $ address , $ dnsRequestMessage );
124
- }
125
-
126
118
return $ message ;
127
119
}
128
120
121
+ /**
122
+ * Clean up the protocol from URI supported by the client but which can not be used with transport (e.g. dns://).
123
+ *
124
+ * @param DnsUpstream $dnsUpstream
125
+ *
126
+ * @return string
127
+ */
128
+ private function getSanitizedUpstreamAddress (DnsUpstream $ dnsUpstream ): string
129
+ {
130
+ return str_replace ($ dnsUpstream ->getScheme () . ':// ' , '' , $ dnsUpstream ->getUri ());
131
+ }
132
+
129
133
/**
130
134
* Enable recursion for the given DNS message
135
+ *
131
136
* @param MessageInterface $dnsRequestMessage
132
137
*
133
138
* @return MessageInterface
0 commit comments