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: Gustavo Lopes <cataphract@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include <php.h>
20 #include "php_sockets.h"
21 #include "sendrecvmsg.h"
22 #include "conversions.h"
23 #include <limits.h>
24 #include <Zend/zend_llist.h>
25 #ifdef ZTS
26 #include <TSRM/TSRM.h>
27 #endif
28
29 #define MAX_USER_BUFF_SIZE ((size_t)(100*1024*1024))
30 #define DEFAULT_BUFF_SIZE 8192
31 #define MAX_ARRAY_KEY_SIZE 128
32
33 #ifdef PHP_WIN32
34 #include "windows_common.h"
35 #include <Mswsock.h>
36 #define IPV6_RECVPKTINFO IPV6_PKTINFO
37 #define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
38 #define msghdr _WSAMSG
39
40 static GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
41 static __declspec(thread) LPFN_WSARECVMSG WSARecvMsg = NULL;
recvmsg(int sockfd,struct msghdr * msg,int flags)42 inline ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
43 {
44 DWORD recvd = 0,
45 bytesReturned;
46
47 if (WSARecvMsg == NULL) {
48 int res = WSAIoctl((SOCKET) sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
49 &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
50 &WSARecvMsg, sizeof(WSARecvMsg),
51 &bytesReturned, NULL, NULL);
52 if (res != 0) {
53 return -1;
54 }
55 }
56
57 msg->dwFlags = (DWORD)flags;
58 return WSARecvMsg((SOCKET)sockfd, msg, &recvd, NULL, NULL) == 0
59 ? (ssize_t)recvd
60 : -1;
61 }
sendmsg(int sockfd,const struct msghdr * msg,int flags)62 inline ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
63 {
64 DWORD sent = 0;
65 return WSASendMsg((SOCKET)sockfd, (struct msghdr*)msg, (DWORD)flags, &sent, NULL, NULL) == 0
66 ? (ssize_t)sent
67 : -1;
68 }
69 #endif
70
71 #define LONG_CHECK_VALID_INT(l) \
72 do { \
73 if ((l) < INT_MIN && (l) > INT_MAX) { \
74 php_error_docref0(NULL, E_WARNING, "The value " ZEND_LONG_FMT " does not fit inside " \
75 "the boundaries of a native integer", (l)); \
76 return; \
77 } \
78 } while (0)
79
80 static struct {
81 int initialized;
82 HashTable ht;
83 } ancillary_registry;
84
85
ancillary_registery_free_elem(zval * el)86 static void ancillary_registery_free_elem(zval *el) {
87 pefree(Z_PTR_P(el), 1);
88 }
89
90 #ifdef ZTS
91 static MUTEX_T ancillary_mutex;
92 #endif
init_ancillary_registry(void)93 static void init_ancillary_registry(void)
94 {
95 ancillary_reg_entry entry;
96 anc_reg_key key;
97 ancillary_registry.initialized = 1;
98
99 zend_hash_init(&ancillary_registry.ht, 32, NULL, ancillary_registery_free_elem, 1);
100
101 #define PUT_ENTRY(sizev, var_size, calc, from, to, level, type) \
102 entry.size = sizev; \
103 entry.var_el_size = var_size; \
104 entry.calc_space = calc; \
105 entry.from_array = from; \
106 entry.to_array = to; \
107 key.cmsg_level = level; \
108 key.cmsg_type = type; \
109 zend_hash_str_update_mem(&ancillary_registry.ht, (char*)&key, sizeof(key), (void*)&entry, sizeof(entry))
110
111 #if defined(IPV6_PKTINFO) && HAVE_IPV6
112 PUT_ENTRY(sizeof(struct in6_pktinfo), 0, 0, from_zval_write_in6_pktinfo,
113 to_zval_read_in6_pktinfo, IPPROTO_IPV6, IPV6_PKTINFO);
114 #endif
115
116 #if defined(IPV6_HOPLIMIT) && HAVE_IPV6
117 PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
118 to_zval_read_int, IPPROTO_IPV6, IPV6_HOPLIMIT);
119 #endif
120
121 #if defined(IPV6_TCLASS) && HAVE_IPV6
122 PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
123 to_zval_read_int, IPPROTO_IPV6, IPV6_TCLASS);
124 #endif
125
126 #ifdef SO_PASSCRED
127 PUT_ENTRY(sizeof(struct ucred), 0, 0, from_zval_write_ucred,
128 to_zval_read_ucred, SOL_SOCKET, SCM_CREDENTIALS);
129 #endif
130
131 #ifdef SCM_RIGHTS
132 PUT_ENTRY(0, sizeof(int), calculate_scm_rights_space, from_zval_write_fd_array,
133 to_zval_read_fd_array, SOL_SOCKET, SCM_RIGHTS);
134 #endif
135
136 }
destroy_ancillary_registry(void)137 static void destroy_ancillary_registry(void)
138 {
139 if (ancillary_registry.initialized) {
140 zend_hash_destroy(&ancillary_registry.ht);
141 ancillary_registry.initialized = 0;
142 }
143 }
get_ancillary_reg_entry(int cmsg_level,int msg_type)144 ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type)
145 {
146 anc_reg_key key = { cmsg_level, msg_type };
147 ancillary_reg_entry *entry;
148
149 #ifdef ZTS
150 tsrm_mutex_lock(ancillary_mutex);
151 #endif
152 if (!ancillary_registry.initialized) {
153 init_ancillary_registry();
154 }
155 #ifdef ZTS
156 tsrm_mutex_unlock(ancillary_mutex);
157 #endif
158
159 if ((entry = zend_hash_str_find_ptr(&ancillary_registry.ht, (char*)&key, sizeof(key))) != NULL) {
160 return entry;
161 } else {
162 return NULL;
163 }
164 }
165
PHP_FUNCTION(socket_sendmsg)166 PHP_FUNCTION(socket_sendmsg)
167 {
168 zval *zsocket,
169 *zmsg;
170 zend_long flags = 0;
171 php_socket *php_sock;
172 struct msghdr *msghdr;
173 zend_llist *allocations;
174 struct err_s err = {0};
175 ssize_t res;
176
177 /* zmsg should be passed by ref */
178 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra|l", &zsocket, &zmsg, &flags) == FAILURE) {
179 return;
180 }
181
182 LONG_CHECK_VALID_INT(flags);
183
184 if ((php_sock = (php_socket *)zend_fetch_resource(Z_RES_P(zsocket),
185 php_sockets_le_socket_name, php_sockets_le_socket())) == NULL) {
186 RETURN_FALSE;
187 }
188
189 msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_send,
190 sizeof(*msghdr), "msghdr", &allocations, &err);
191
192 if (err.has_error) {
193 err_msg_dispose(&err);
194 RETURN_FALSE;
195 }
196
197 res = sendmsg(php_sock->bsd_socket, msghdr, (int)flags);
198
199 if (res != -1) {
200 zend_llist_destroy(allocations);
201 efree(allocations);
202
203 RETURN_LONG((zend_long)res);
204 } else {
205 PHP_SOCKET_ERROR(php_sock, "error in sendmsg", errno);
206 RETURN_FALSE;
207 }
208 }
209
PHP_FUNCTION(socket_recvmsg)210 PHP_FUNCTION(socket_recvmsg)
211 {
212 zval *zsocket,
213 *zmsg;
214 zend_long flags = 0;
215 php_socket *php_sock;
216 ssize_t res;
217 struct msghdr *msghdr;
218 zend_llist *allocations;
219 struct err_s err = {0};
220
221 //ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
222 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra/|l",
223 &zsocket, &zmsg, &flags) == FAILURE) {
224 return;
225 }
226
227 LONG_CHECK_VALID_INT(flags);
228
229 if ((php_sock = (php_socket *)zend_fetch_resource(Z_RES_P(zsocket),
230 php_sockets_le_socket_name, php_sockets_le_socket())) == NULL) {
231 RETURN_FALSE;
232 }
233
234 msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_recv,
235 sizeof(*msghdr), "msghdr", &allocations, &err);
236
237 if (err.has_error) {
238 err_msg_dispose(&err);
239 RETURN_FALSE;
240 }
241
242 res = recvmsg(php_sock->bsd_socket, msghdr, (int)flags);
243
244 if (res != -1) {
245 zval *zres, tmp;
246 struct key_value kv[] = {
247 {KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET), &res},
248 {0}
249 };
250
251
252 zres = to_zval_run_conversions((char *)msghdr, to_zval_read_msghdr,
253 "msghdr", kv, &err, &tmp);
254
255 /* we don;t need msghdr anymore; free it */
256 msghdr = NULL;
257 allocations_dispose(&allocations);
258
259 zval_dtor(zmsg);
260 if (!err.has_error) {
261 ZVAL_COPY_VALUE(zmsg, zres);
262 } else {
263 err_msg_dispose(&err);
264 ZVAL_FALSE(zmsg);
265 /* no need to destroy/free zres -- it's NULL in this circumstance */
266 assert(zres == NULL);
267 }
268 } else {
269 SOCKETS_G(last_error) = errno;
270 php_error_docref(NULL, E_WARNING, "error in recvmsg [%d]: %s",
271 errno, sockets_strerror(errno));
272 RETURN_FALSE;
273 }
274
275 RETURN_LONG((zend_long)res);
276 }
277
PHP_FUNCTION(socket_cmsg_space)278 PHP_FUNCTION(socket_cmsg_space)
279 {
280 zend_long level,
281 type,
282 n = 0;
283 ancillary_reg_entry *entry;
284
285 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|l",
286 &level, &type, &n) == FAILURE) {
287 return;
288 }
289
290 LONG_CHECK_VALID_INT(level);
291 LONG_CHECK_VALID_INT(type);
292 LONG_CHECK_VALID_INT(n);
293
294 if (n < 0) {
295 php_error_docref0(NULL, E_WARNING, "The third argument "
296 "cannot be negative");
297 return;
298 }
299
300 entry = get_ancillary_reg_entry(level, type);
301 if (entry == NULL) {
302 php_error_docref0(NULL, E_WARNING, "The pair level " ZEND_LONG_FMT "/type " ZEND_LONG_FMT " is "
303 "not supported by PHP", level, type);
304 return;
305 }
306
307 if (entry->var_el_size > 0 && n > (zend_long)((ZEND_LONG_MAX - entry->size -
308 CMSG_SPACE(0) - 15L) / entry->var_el_size)) {
309 /* the -15 is to account for any padding CMSG_SPACE may add after the data */
310 php_error_docref0(NULL, E_WARNING, "The value for the "
311 "third argument (" ZEND_LONG_FMT ") is too large", n);
312 return;
313 }
314
315 RETURN_LONG((zend_long)CMSG_SPACE(entry->size + n * entry->var_el_size));
316 }
317
318 #if HAVE_IPV6
php_do_setsockopt_ipv6_rfc3542(php_socket * php_sock,int level,int optname,zval * arg4)319 int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *arg4)
320 {
321 struct err_s err = {0};
322 zend_llist *allocations = NULL;
323 void *opt_ptr;
324 socklen_t optlen;
325 int retval;
326
327 assert(level == IPPROTO_IPV6);
328
329 switch (optname) {
330 #ifdef IPV6_PKTINFO
331 case IPV6_PKTINFO:
332 #ifdef PHP_WIN32
333 if (Z_TYPE_P(arg4) == IS_ARRAY) {
334 php_error_docref0(NULL, E_WARNING, "Windows does not "
335 "support sticky IPV6_PKTINFO");
336 return FAILURE;
337 } else {
338 /* windows has no IPV6_RECVPKTINFO, and uses IPV6_PKTINFO
339 * for the same effect. We define IPV6_RECVPKTINFO to be
340 * IPV6_PKTINFO, so assume the assume user used IPV6_RECVPKTINFO */
341 return 1;
342 }
343 #endif
344 opt_ptr = from_zval_run_conversions(arg4, php_sock, from_zval_write_in6_pktinfo,
345 sizeof(struct in6_pktinfo), "in6_pktinfo", &allocations, &err);
346 if (err.has_error) {
347 err_msg_dispose(&err);
348 return FAILURE;
349 }
350
351 optlen = sizeof(struct in6_pktinfo);
352 goto dosockopt;
353 #endif
354 }
355
356 /* we also support IPV6_TCLASS, but that can be handled by the default
357 * integer optval handling in the caller */
358 return 1;
359
360 dosockopt:
361 retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
362 if (retval != 0) {
363 PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
364 }
365 allocations_dispose(&allocations);
366
367 return retval != 0 ? FAILURE : SUCCESS;
368 }
369
php_do_getsockopt_ipv6_rfc3542(php_socket * php_sock,int level,int optname,zval * result)370 int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result)
371 {
372 struct err_s err = {0};
373 void *buffer;
374 socklen_t size;
375 int res;
376 to_zval_read_field *reader;
377
378 assert(level == IPPROTO_IPV6);
379
380 switch (optname) {
381 #ifdef IPV6_PKTINFO
382 case IPV6_PKTINFO:
383 size = sizeof(struct in6_pktinfo);
384 reader = &to_zval_read_in6_pktinfo;
385 break;
386 #endif
387 default:
388 return 1;
389 }
390
391 buffer = ecalloc(1, size);
392 res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size);
393 if (res != 0) {
394 PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno);
395 } else {
396 zval tmp;
397 zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo",
398 empty_key_value_list, &err, &tmp);
399 if (err.has_error) {
400 err_msg_dispose(&err);
401 res = -1;
402 } else {
403 ZVAL_COPY_VALUE(result, zv);
404 }
405 }
406 efree(buffer);
407
408 return res == 0 ? SUCCESS : FAILURE;
409 }
410 #endif /* HAVE_IPV6 */
411
php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)412 void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)
413 {
414 /* IPv6 ancillary data */
415 #if defined(IPV6_RECVPKTINFO) && HAVE_IPV6
416 REGISTER_LONG_CONSTANT("IPV6_RECVPKTINFO", IPV6_RECVPKTINFO, CONST_CS | CONST_PERSISTENT);
417 REGISTER_LONG_CONSTANT("IPV6_PKTINFO", IPV6_PKTINFO, CONST_CS | CONST_PERSISTENT);
418 #endif
419 #if defined(IPV6_RECVHOPLIMIT) && HAVE_IPV6
420 REGISTER_LONG_CONSTANT("IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT, CONST_CS | CONST_PERSISTENT);
421 REGISTER_LONG_CONSTANT("IPV6_HOPLIMIT", IPV6_HOPLIMIT, CONST_CS | CONST_PERSISTENT);
422 #endif
423 /* would require some effort:
424 REGISTER_LONG_CONSTANT("IPV6_RECVRTHDR", IPV6_RECVRTHDR, CONST_CS | CONST_PERSISTENT);
425 REGISTER_LONG_CONSTANT("IPV6_RECVHOPOPTS", IPV6_RECVHOPOPTS, CONST_CS | CONST_PERSISTENT);
426 REGISTER_LONG_CONSTANT("IPV6_RECVDSTOPTS", IPV6_RECVDSTOPTS, CONST_CS | CONST_PERSISTENT);
427 */
428 #if defined(IPV6_RECVTCLASS) && HAVE_IPV6
429 REGISTER_LONG_CONSTANT("IPV6_RECVTCLASS", IPV6_RECVTCLASS, CONST_CS | CONST_PERSISTENT);
430 REGISTER_LONG_CONSTANT("IPV6_TCLASS", IPV6_TCLASS, CONST_CS | CONST_PERSISTENT);
431 #endif
432
433 /*
434 REGISTER_LONG_CONSTANT("IPV6_RTHDR", IPV6_RTHDR, CONST_CS | CONST_PERSISTENT);
435 REGISTER_LONG_CONSTANT("IPV6_HOPOPTS", IPV6_HOPOPTS, CONST_CS | CONST_PERSISTENT);
436 REGISTER_LONG_CONSTANT("IPV6_DSTOPTS", IPV6_DSTOPTS, CONST_CS | CONST_PERSISTENT);
437 */
438
439 #ifdef SCM_RIGHTS
440 REGISTER_LONG_CONSTANT("SCM_RIGHTS", SCM_RIGHTS, CONST_CS | CONST_PERSISTENT);
441 #endif
442 #ifdef SO_PASSCRED
443 REGISTER_LONG_CONSTANT("SCM_CREDENTIALS", SCM_CREDENTIALS, CONST_CS | CONST_PERSISTENT);
444 REGISTER_LONG_CONSTANT("SO_PASSCRED", SO_PASSCRED, CONST_CS | CONST_PERSISTENT);
445 #endif
446
447 #ifdef ZTS
448 ancillary_mutex = tsrm_mutex_alloc();
449 #endif
450 }
451
php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)452 void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)
453 {
454 #ifdef ZTS
455 tsrm_mutex_free(ancillary_mutex);
456 #endif
457
458 destroy_ancillary_registry();
459 }
460