1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Anatol Belski <ab@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "phpdbg_io.h"
24
25 #ifdef PHP_WIN32
26 #undef UNICODE
27 #include "win32/inet.h"
28 #include <winsock2.h>
29 #include <windows.h>
30 #include <Ws2tcpip.h>
31 #include "win32/sockets.h"
32
33 #else
34
35 #if HAVE_SYS_TYPES_H
36 #include <sys/types.h>
37 #endif
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #if HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
42 #endif
43 #include <netdb.h>
44 #include <fcntl.h>
45 #include <poll.h>
46 #endif
47
ZEND_EXTERN_MODULE_GLOBALS(phpdbg)48 ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
49
50 /* is easy to generalize ... but not needed for now */
51 PHPDBG_API int phpdbg_consume_stdin_line(char *buf) {
52 int bytes = PHPDBG_G(input_buflen), len = 0;
53
54 if (PHPDBG_G(input_buflen)) {
55 memcpy(buf, PHPDBG_G(input_buffer), bytes);
56 }
57
58 PHPDBG_G(last_was_newline) = 1;
59
60 do {
61 int i;
62 if (bytes <= 0) {
63 continue;
64 }
65
66 for (i = len; i < len + bytes; i++) {
67 if (buf[i] == '\x03') {
68 if (i != len + bytes - 1) {
69 memmove(buf + i, buf + i + 1, len + bytes - i - 1);
70 }
71 len--;
72 i--;
73 continue;
74 }
75 if (buf[i] == '\n') {
76 PHPDBG_G(input_buflen) = len + bytes - 1 - i;
77 if (PHPDBG_G(input_buflen)) {
78 memcpy(PHPDBG_G(input_buffer), buf + i + 1, PHPDBG_G(input_buflen));
79 }
80 if (i != PHPDBG_MAX_CMD - 1) {
81 buf[i + 1] = 0;
82 }
83 return i;
84 }
85 }
86
87 len += bytes;
88 } while ((bytes = phpdbg_mixed_read(PHPDBG_G(io)[PHPDBG_STDIN].fd, buf + len, PHPDBG_MAX_CMD - len, -1)) > 0);
89
90 if (bytes <= 0) {
91 PHPDBG_G(flags) |= PHPDBG_IS_QUITTING | PHPDBG_IS_DISCONNECTED;
92 zend_bailout();
93 }
94
95 return bytes;
96 }
97
phpdbg_consume_bytes(int sock,char * ptr,int len,int tmo)98 PHPDBG_API int phpdbg_consume_bytes(int sock, char *ptr, int len, int tmo) {
99 int got_now, i = len, j;
100 char *p = ptr;
101 #ifndef PHP_WIN32
102 struct pollfd pfd;
103
104 if (tmo < 0) goto recv_once;
105 pfd.fd = sock;
106 pfd.events = POLLIN;
107
108 j = poll(&pfd, 1, tmo);
109
110 if (j == 0) {
111 #else
112 struct fd_set readfds;
113 struct timeval ttmo;
114
115 if (tmo < 0) goto recv_once;
116 FD_ZERO(&readfds);
117 FD_SET(sock, &readfds);
118
119 ttmo.tv_sec = 0;
120 ttmo.tv_usec = tmo*1000;
121
122 j = select(0, &readfds, NULL, NULL, &ttmo);
123
124 if (j <= 0) {
125 #endif
126 return -1;
127 }
128
129 recv_once:
130 while(i > 0) {
131 if (tmo < 0) {
132 /* There's something to read. Read what's available and proceed
133 disregarding whether len could be exhausted or not.*/
134 int can_read = recv(sock, p, i, MSG_PEEK);
135 #ifndef _WIN32
136 if (can_read == -1 && errno == EINTR) {
137 continue;
138 }
139 #endif
140 i = can_read;
141 }
142
143 #ifdef _WIN32
144 got_now = recv(sock, p, i, 0);
145 #else
146 do {
147 got_now = recv(sock, p, i, 0);
148 } while (got_now == -1 && errno == EINTR);
149 #endif
150
151 if (got_now == -1) {
152 zend_quiet_write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("Read operation timed out!\n"));
153 return -1;
154 }
155 i -= got_now;
156 p += got_now;
157 }
158
159 return p - ptr;
160 }
161
162 PHPDBG_API int phpdbg_send_bytes(int sock, const char *ptr, int len) {
163 int sent, i = len;
164 const char *p = ptr;
165 /* XXX poll/select needed here? */
166 while(i > 0) {
167 sent = send(sock, p, i, 0);
168 if (sent == -1) {
169 return -1;
170 }
171 i -= sent;
172 p += sent;
173 }
174
175 return len;
176 }
177
178
179 PHPDBG_API int phpdbg_mixed_read(int sock, char *ptr, int len, int tmo) {
180 int ret;
181
182 if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
183 return phpdbg_consume_bytes(sock, ptr, len, tmo);
184 }
185
186 do {
187 ret = read(sock, ptr, len);
188 } while (ret == -1 && errno == EINTR);
189
190 return ret;
191 }
192
193 static int phpdbg_output_pager(int sock, const char *ptr, int len) {
194 int count = 0, bytes = 0;
195 const char *p = ptr, *endp = ptr + len;
196
197 while ((p = memchr(p, '\n', endp - p))) {
198 count++;
199 p++;
200
201 if (count % PHPDBG_G(lines) == 0) {
202 bytes += write(sock, ptr + bytes, (p - ptr) - bytes);
203
204 if (memchr(p, '\n', endp - p)) {
205 char buf[PHPDBG_MAX_CMD];
206 zend_quiet_write(sock, ZEND_STRL("\r---Type <return> to continue or q <return> to quit---"));
207 phpdbg_consume_stdin_line(buf);
208 if (*buf == 'q') {
209 break;
210 }
211 write(sock, "\r", 1);
212 } else break;
213 }
214 }
215 if (bytes && count % PHPDBG_G(lines) != 0) {
216 bytes += write(sock, ptr + bytes, len - bytes);
217 } else if (!bytes) {
218 bytes += write(sock, ptr, len);
219 }
220 return bytes;
221 }
222
223 PHPDBG_API int phpdbg_mixed_write(int sock, const char *ptr, int len) {
224 if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
225 return phpdbg_send_bytes(sock, ptr, len);
226 }
227
228 if ((PHPDBG_G(flags) & PHPDBG_HAS_PAGINATION)
229 && !(PHPDBG_G(flags) & PHPDBG_WRITE_XML)
230 && PHPDBG_G(io)[PHPDBG_STDOUT].fd == sock
231 && PHPDBG_G(lines) > 0) {
232 return phpdbg_output_pager(sock, ptr, len);
233 }
234
235 return write(sock, ptr, len);
236 }
237
238 PHPDBG_API int phpdbg_open_socket(const char *interface, unsigned short port) {
239 struct addrinfo res;
240 int fd = phpdbg_create_listenable_socket(interface, port, &res);
241
242 if (fd == -1) {
243 return -1;
244 }
245
246 if (bind(fd, res.ai_addr, res.ai_addrlen) == -1) {
247 phpdbg_close_socket(fd);
248 return -4;
249 }
250
251 listen(fd, 5);
252
253 return fd;
254 }
255
256
257 PHPDBG_API int phpdbg_create_listenable_socket(const char *addr, unsigned short port, struct addrinfo *addr_res) {
258 int sock = -1, rc;
259 int reuse = 1;
260 struct in6_addr serveraddr;
261 struct addrinfo hints, *res = NULL;
262 char port_buf[8];
263 int8_t any_addr = *addr == '*';
264
265 do {
266 memset(&hints, 0, sizeof hints);
267 if (any_addr) {
268 hints.ai_flags = AI_PASSIVE;
269 } else {
270 hints.ai_flags = AI_NUMERICSERV;
271 }
272 hints.ai_family = AF_UNSPEC;
273 hints.ai_socktype = SOCK_STREAM;
274
275 rc = inet_pton(AF_INET, addr, &serveraddr);
276 if (1 == rc) {
277 hints.ai_family = AF_INET;
278 if (!any_addr) {
279 hints.ai_flags |= AI_NUMERICHOST;
280 }
281 } else {
282 rc = inet_pton(AF_INET6, addr, &serveraddr);
283 if (1 == rc) {
284 hints.ai_family = AF_INET6;
285 if (!any_addr) {
286 hints.ai_flags |= AI_NUMERICHOST;
287 }
288 } else {
289 /* XXX get host by name ??? */
290 }
291 }
292
293 snprintf(port_buf, sizeof(port_buf), "%u", port);
294 if (!any_addr) {
295 rc = getaddrinfo(addr, port_buf, &hints, &res);
296 } else {
297 rc = getaddrinfo(NULL, port_buf, &hints, &res);
298 }
299
300 if (0 != rc) {
301 #ifndef PHP_WIN32
302 if (rc == EAI_SYSTEM) {
303 char buf[128];
304
305 snprintf(buf, sizeof(buf), "Could not translate address '%s'", addr);
306
307 zend_quiet_write(PHPDBG_G(io)[PHPDBG_STDERR].fd, buf, strlen(buf));
308
309 return sock;
310 } else {
311 #endif
312 char buf[256];
313
314 snprintf(buf, sizeof(buf), "Host '%s' not found. %s", addr, estrdup(gai_strerror(rc)));
315
316 zend_quiet_write(PHPDBG_G(io)[PHPDBG_STDERR].fd, buf, strlen(buf));
317
318 return sock;
319 #ifndef PHP_WIN32
320 }
321 #endif
322 return sock;
323 }
324
325 if ((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) {
326 const char *msg = "Unable to create socket";
327
328 zend_quiet_write(PHPDBG_G(io)[PHPDBG_STDERR].fd, msg, strlen(msg));
329
330 return sock;
331 }
332
333 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse)) == -1) {
334 phpdbg_close_socket(sock);
335 return sock;
336 }
337
338
339 } while (0);
340
341 *addr_res = *res;
342
343 return sock;
344 }
345
346 PHPDBG_API void phpdbg_close_socket(int sock) {
347 if (sock >= 0) {
348 #ifdef _WIN32
349 closesocket(sock);
350 #else
351 shutdown(sock, SHUT_RDWR);
352 close(sock);
353 #endif
354 }
355 }
356