xref: /PHP-7.4/sapi/phpdbg/phpdbg_io.c (revision d087a356)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 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 				zend_quiet_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