This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of the NNTP library. |
||
5 | * |
||
6 | * (c) Robin van der Vleuten <[email protected]> |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | */ |
||
11 | |||
12 | namespace Rvdv\Nntp\Connection; |
||
13 | |||
14 | use Rvdv\Nntp\Command\CommandInterface; |
||
15 | use Rvdv\Nntp\Exception\InvalidArgumentException; |
||
16 | use Rvdv\Nntp\Exception\RuntimeException; |
||
17 | use Rvdv\Nntp\Exception\UnknownHandlerException; |
||
18 | use Rvdv\Nntp\Response\MultiLineResponse; |
||
19 | use Rvdv\Nntp\Response\Response; |
||
20 | use Rvdv\Nntp\Response\ResponseInterface; |
||
21 | use Rvdv\Nntp\Socket\Socket; |
||
22 | use Rvdv\Nntp\Socket\SocketInterface; |
||
23 | |||
24 | /** |
||
25 | * @author Robin van der Vleuten <[email protected]> |
||
26 | */ |
||
27 | class Connection implements ConnectionInterface |
||
28 | { |
||
29 | const BUFFER_SIZE = 1024; |
||
30 | |||
31 | /** |
||
32 | * @var string |
||
33 | */ |
||
34 | private $host; |
||
35 | |||
36 | /** |
||
37 | * @var int |
||
38 | */ |
||
39 | private $port; |
||
40 | |||
41 | /** |
||
42 | * @var bool |
||
43 | */ |
||
44 | private $secure; |
||
45 | |||
46 | /** |
||
47 | * @var SocketInterface |
||
48 | */ |
||
49 | private $socket; |
||
50 | |||
51 | /** |
||
52 | * Constructor. |
||
53 | * |
||
54 | * @param string $host the host of the NNTP server |
||
55 | * @param int $port the port of the NNTP server |
||
56 | * @param bool $secure a bool indicating if a secure connection should be established |
||
57 | * @param SocketInterface $socket an optional socket wrapper instance |
||
58 | */ |
||
59 | 7 | public function __construct($host, $port, $secure = false, SocketInterface $socket = null) |
|
60 | { |
||
61 | 7 | $this->host = $host; |
|
62 | 7 | $this->port = $port; |
|
63 | 7 | $this->secure = $secure; |
|
64 | 7 | $this->socket = $socket ?: new Socket(); |
|
65 | 7 | } |
|
66 | |||
67 | /** |
||
68 | * {@inheritdoc} |
||
69 | */ |
||
70 | 3 | public function connect() |
|
71 | { |
||
72 | 3 | $this->socket->connect(sprintf('tcp://%s:%d', $this->host, $this->port)); |
|
73 | |||
74 | 2 | if ($this->secure) { |
|
75 | 1 | $this->socket->enableCrypto(true); |
|
76 | 1 | } |
|
77 | |||
78 | 2 | return $this->getResponse(); |
|
79 | } |
||
80 | |||
81 | /** |
||
82 | * {@inheritdoc} |
||
83 | */ |
||
84 | 1 | public function disconnect() |
|
85 | { |
||
86 | 1 | $this->socket->disconnect(); |
|
87 | 1 | } |
|
88 | |||
89 | /** |
||
90 | * {@inheritdoc} |
||
91 | */ |
||
92 | 3 | public function sendCommand(CommandInterface $command) |
|
93 | { |
||
94 | 3 | $commandString = $command(); |
|
95 | |||
96 | // NNTP/RFC977 only allows command up to 512 (-2 \r\n) chars. |
||
97 | 3 | if (strlen($commandString) > 510) { |
|
98 | 1 | throw new InvalidArgumentException('Failed to write to socket: command exceeded 510 characters'); |
|
99 | } |
||
100 | |||
101 | 2 | if (strlen($commandString."\r\n") !== $this->socket->write($commandString."\r\n")) { |
|
102 | 1 | throw new RuntimeException('Failed to write to socket'); |
|
103 | } |
||
104 | |||
105 | 1 | $response = $this->getResponse(); |
|
106 | |||
107 | 1 | if ($command->isMultiLine() && ($response->getStatusCode() >= 200 && $response->getStatusCode() <= 399)) { |
|
108 | $response = $command->isCompressed() ? $this->getCompressedResponse($response) : $this->getMultiLineResponse($response); |
||
109 | } |
||
110 | |||
111 | 1 | return $this->callCommandHandlerForResponse($command, $response); |
|
0 ignored issues
–
show
|
|||
112 | } |
||
113 | |||
114 | public function sendArticle(CommandInterface $command) |
||
115 | { |
||
116 | $commandString = $command(); |
||
117 | |||
118 | if (strlen($commandString."\r\n.\r\n") !== $this->socket->write($commandString."\r\n.\r\n")) { |
||
119 | throw new RuntimeException('Failed to write to socket'); |
||
120 | } |
||
121 | |||
122 | $response = $this->getResponse(); |
||
123 | |||
124 | return $this->callCommandHandlerForResponse($command, $response); |
||
125 | } |
||
126 | |||
127 | 3 | private function getResponse() |
|
128 | { |
||
129 | 3 | $buffer = ''; |
|
130 | |||
131 | 3 | while (!$this->socket->eof()) { |
|
132 | 3 | $buffer .= $this->socket->gets(self::BUFFER_SIZE); |
|
133 | |||
134 | 3 | if ("\r\n" === substr($buffer, -2)) { |
|
135 | 3 | break; |
|
136 | } |
||
137 | |||
138 | if (false === $buffer) { |
||
139 | $this->disconnect(); |
||
140 | |||
141 | throw new RuntimeException('Incorrect data received from buffer'); |
||
142 | } |
||
143 | } |
||
144 | |||
145 | 3 | return Response::createFromString($buffer); |
|
146 | } |
||
147 | |||
148 | private function getMultiLineResponse(Response $response) |
||
149 | { |
||
150 | $lines = []; |
||
151 | |||
152 | while (!$this->socket->eof()) { |
||
153 | $line = $this->socket->gets(self::BUFFER_SIZE); |
||
154 | if ("\r\n" !== substr($line, -2) || strlen($line) < 2) { |
||
155 | continue; |
||
156 | } |
||
157 | |||
158 | // Remove CR LF from the end of the line. |
||
159 | $line = substr($line, 0, -2); |
||
160 | |||
161 | // Check if the line terminates the text response. |
||
162 | if ('.' === $line) { |
||
163 | return new MultiLineResponse($response, $lines); |
||
164 | } |
||
165 | |||
166 | // If 1st char is '.' it's doubled (NNTP/RFC977 2.4.1). |
||
167 | if ('..' === substr($line, 0, 2)) { |
||
168 | $line = substr($line, 1); |
||
169 | } |
||
170 | |||
171 | // Add the line to the array of lines. |
||
172 | $lines[] = $line; |
||
173 | } |
||
174 | } |
||
175 | |||
176 | private function getCompressedResponse(Response $response) |
||
177 | { |
||
178 | // Determine encoding by fetching first line. |
||
179 | $line = $this->socket->gets(self::BUFFER_SIZE); |
||
180 | |||
181 | if ('=ybegin' == substr($line, 0, 7)) { |
||
182 | $this->disconnect(); |
||
183 | |||
184 | throw new RuntimeException('yEnc encoded overviews are not currently supported.'); |
||
185 | } |
||
186 | |||
187 | $uncompressed = ''; |
||
188 | |||
189 | while (!$this->socket->eof()) { |
||
190 | $buffer = $this->socket->gets(self::BUFFER_SIZE); |
||
191 | |||
192 | if (0 === strlen($buffer)) { |
||
193 | $uncompressed = @gzuncompress($line); |
||
194 | |||
195 | if (false !== $uncompressed) { |
||
196 | break; |
||
197 | } |
||
198 | } |
||
199 | |||
200 | if (false === $buffer) { |
||
201 | $this->disconnect(); |
||
202 | |||
203 | throw new RuntimeException('Incorrect data received from buffer'); |
||
204 | } |
||
205 | |||
206 | $line .= $buffer; |
||
207 | } |
||
208 | |||
209 | $lines = explode("\r\n", trim($uncompressed)); |
||
210 | if ('.' === end($lines)) { |
||
211 | array_pop($lines); |
||
212 | } |
||
213 | |||
214 | return new MultiLineResponse($response, array_filter($lines)); |
||
215 | } |
||
216 | |||
217 | 1 | private function callCommandHandlerForResponse(CommandInterface $command, ResponseInterface $response) |
|
218 | { |
||
219 | 1 | if (in_array($response->getStatusCode(), [Response::$codes['CommandUnknown'], Response::$codes['CommandUnavailable']])) { |
|
220 | throw new RuntimeException('Sent command is either unknown or unavailable on server'); |
||
221 | } |
||
222 | |||
223 | // Check if we received a response code that we're aware of. |
||
224 | 1 | if (false === ($responseName = array_search($response->getStatusCode(), Response::$codes, true))) { |
|
225 | throw new RuntimeException(sprintf( |
||
226 | 'Unexpected response received: [%d] %s', |
||
227 | $response->getStatusCode(), |
||
228 | $response->getMessage() |
||
229 | )); |
||
230 | } |
||
231 | |||
232 | 1 | $responseHandlerMethod = 'on'.$responseName; |
|
233 | |||
234 | 1 | if (!is_callable([$command, $responseHandlerMethod])) { |
|
235 | throw new UnknownHandlerException(sprintf('Response handler (%s) is not a callable method on given command object', $responseHandlerMethod)); |
||
236 | } |
||
237 | |||
238 | 1 | return call_user_func([$command, $responseHandlerMethod], $response); |
|
239 | } |
||
240 | } |
||
241 |
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.