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: 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_docref(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 RETVAL_LONG((zend_long)res);
201 } else {
202 PHP_SOCKET_ERROR(php_sock, "error in sendmsg", errno);
203 RETVAL_FALSE;
204 }
205
206 allocations_dispose(&allocations);
207 }
208
PHP_FUNCTION(socket_recvmsg)209 PHP_FUNCTION(socket_recvmsg)
210 {
211 zval *zsocket,
212 *zmsg;
213 zend_long flags = 0;
214 php_socket *php_sock;
215 ssize_t res;
216 struct msghdr *msghdr;
217 zend_llist *allocations;
218 struct err_s err = {0};
219
220 //ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
221 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra|l",
222 &zsocket, &zmsg, &flags) == FAILURE) {
223 return;
224 }
225
226 LONG_CHECK_VALID_INT(flags);
227
228 if ((php_sock = (php_socket *)zend_fetch_resource(Z_RES_P(zsocket),
229 php_sockets_le_socket_name, php_sockets_le_socket())) == NULL) {
230 RETURN_FALSE;
231 }
232
233 msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_recv,
234 sizeof(*msghdr), "msghdr", &allocations, &err);
235
236 if (err.has_error) {
237 err_msg_dispose(&err);
238 RETURN_FALSE;
239 }
240
241 res = recvmsg(php_sock->bsd_socket, msghdr, (int)flags);
242
243 if (res != -1) {
244 zval *zres, tmp;
245 struct key_value kv[] = {
246 {KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET), &res},
247 {0}
248 };
249
250
251 zres = to_zval_run_conversions((char *)msghdr, to_zval_read_msghdr,
252 "msghdr", kv, &err, &tmp);
253
254 /* we don;t need msghdr anymore; free it */
255 msghdr = NULL;
256
257 zval_ptr_dtor(zmsg);
258 if (!err.has_error) {
259 ZVAL_COPY_VALUE(zmsg, zres);
260 } else {
261 err_msg_dispose(&err);
262 ZVAL_FALSE(zmsg);
263 /* no need to destroy/free zres -- it's NULL in this circumstance */
264 assert(zres == NULL);
265 }
266 RETVAL_LONG((zend_long)res);
267 } else {
268 SOCKETS_G(last_error) = errno;
269 php_error_docref(NULL, E_WARNING, "error in recvmsg [%d]: %s",
270 errno, sockets_strerror(errno));
271 RETVAL_FALSE;
272 }
273
274 allocations_dispose(&allocations);
275 }
276
PHP_FUNCTION(socket_cmsg_space)277 PHP_FUNCTION(socket_cmsg_space)
278 {
279 zend_long level,
280 type,
281 n = 0;
282 ancillary_reg_entry *entry;
283
284 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|l",
285 &level, &type, &n) == FAILURE) {
286 return;
287 }
288
289 LONG_CHECK_VALID_INT(level);
290 LONG_CHECK_VALID_INT(type);
291 LONG_CHECK_VALID_INT(n);
292
293 if (n < 0) {
294 php_error_docref(NULL, E_WARNING, "The third argument "
295 "cannot be negative");
296 return;
297 }
298
299 entry = get_ancillary_reg_entry(level, type);
300 if (entry == NULL) {
301 php_error_docref(NULL, E_WARNING, "The pair level " ZEND_LONG_FMT "/type " ZEND_LONG_FMT " is "
302 "not supported by PHP", level, type);
303 return;
304 }
305
306 if (entry->var_el_size > 0 && n > (zend_long)((ZEND_LONG_MAX - entry->size -
307 CMSG_SPACE(0) - 15L) / entry->var_el_size)) {
308 /* the -15 is to account for any padding CMSG_SPACE may add after the data */
309 php_error_docref(NULL, E_WARNING, "The value for the "
310 "third argument (" ZEND_LONG_FMT ") is too large", n);
311 return;
312 }
313
314 RETURN_LONG((zend_long)CMSG_SPACE(entry->size + n * entry->var_el_size));
315 }
316
317 #if HAVE_IPV6
php_do_setsockopt_ipv6_rfc3542(php_socket * php_sock,int level,int optname,zval * arg4)318 int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *arg4)
319 {
320 struct err_s err = {0};
321 zend_llist *allocations = NULL;
322 void *opt_ptr;
323 socklen_t optlen;
324 int retval;
325
326 assert(level == IPPROTO_IPV6);
327
328 switch (optname) {
329 #ifdef IPV6_PKTINFO
330 case IPV6_PKTINFO:
331 #ifdef PHP_WIN32
332 if (Z_TYPE_P(arg4) == IS_ARRAY) {
333 php_error_docref(NULL, E_WARNING, "Windows does not "
334 "support sticky IPV6_PKTINFO");
335 return FAILURE;
336 } else {
337 /* windows has no IPV6_RECVPKTINFO, and uses IPV6_PKTINFO
338 * for the same effect. We define IPV6_RECVPKTINFO to be
339 * IPV6_PKTINFO, so assume the assume user used IPV6_RECVPKTINFO */
340 return 1;
341 }
342 #endif
343 opt_ptr = from_zval_run_conversions(arg4, php_sock, from_zval_write_in6_pktinfo,
344 sizeof(struct in6_pktinfo), "in6_pktinfo", &allocations, &err);
345 if (err.has_error) {
346 err_msg_dispose(&err);
347 return FAILURE;
348 }
349
350 optlen = sizeof(struct in6_pktinfo);
351 goto dosockopt;
352 #endif
353 }
354
355 /* we also support IPV6_TCLASS, but that can be handled by the default
356 * integer optval handling in the caller */
357 return 1;
358
359 dosockopt:
360 retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
361 if (retval != 0) {
362 PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
363 }
364 allocations_dispose(&allocations);
365
366 return retval != 0 ? FAILURE : SUCCESS;
367 }
368
php_do_getsockopt_ipv6_rfc3542(php_socket * php_sock,int level,int optname,zval * result)369 int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result)
370 {
371 struct err_s err = {0};
372 void *buffer;
373 socklen_t size;
374 int res;
375 to_zval_read_field *reader;
376
377 assert(level == IPPROTO_IPV6);
378
379 switch (optname) {
380 #ifdef IPV6_PKTINFO
381 case IPV6_PKTINFO:
382 size = sizeof(struct in6_pktinfo);
383 reader = &to_zval_read_in6_pktinfo;
384 break;
385 #endif
386 default:
387 return 1;
388 }
389
390 buffer = ecalloc(1, size);
391 res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size);
392 if (res != 0) {
393 PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno);
394 } else {
395 zval tmp;
396 zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo",
397 empty_key_value_list, &err, &tmp);
398 if (err.has_error) {
399 err_msg_dispose(&err);
400 res = -1;
401 } else {
402 ZVAL_COPY_VALUE(result, zv);
403 }
404 }
405 efree(buffer);
406
407 return res == 0 ? SUCCESS : FAILURE;
408 }
409 #endif /* HAVE_IPV6 */
410
php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)411 void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)
412 {
413 /* IPv6 ancillary data */
414 #if defined(IPV6_RECVPKTINFO) && HAVE_IPV6
415 REGISTER_LONG_CONSTANT("IPV6_RECVPKTINFO", IPV6_RECVPKTINFO, CONST_CS | CONST_PERSISTENT);
416 REGISTER_LONG_CONSTANT("IPV6_PKTINFO", IPV6_PKTINFO, CONST_CS | CONST_PERSISTENT);
417 #endif
418 #if defined(IPV6_RECVHOPLIMIT) && HAVE_IPV6
419 REGISTER_LONG_CONSTANT("IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT, CONST_CS | CONST_PERSISTENT);
420 REGISTER_LONG_CONSTANT("IPV6_HOPLIMIT", IPV6_HOPLIMIT, CONST_CS | CONST_PERSISTENT);
421 #endif
422 /* would require some effort:
423 REGISTER_LONG_CONSTANT("IPV6_RECVRTHDR", IPV6_RECVRTHDR, CONST_CS | CONST_PERSISTENT);
424 REGISTER_LONG_CONSTANT("IPV6_RECVHOPOPTS", IPV6_RECVHOPOPTS, CONST_CS | CONST_PERSISTENT);
425 REGISTER_LONG_CONSTANT("IPV6_RECVDSTOPTS", IPV6_RECVDSTOPTS, CONST_CS | CONST_PERSISTENT);
426 */
427 #if defined(IPV6_RECVTCLASS) && HAVE_IPV6
428 REGISTER_LONG_CONSTANT("IPV6_RECVTCLASS", IPV6_RECVTCLASS, CONST_CS | CONST_PERSISTENT);
429 REGISTER_LONG_CONSTANT("IPV6_TCLASS", IPV6_TCLASS, CONST_CS | CONST_PERSISTENT);
430 #endif
431
432 /*
433 REGISTER_LONG_CONSTANT("IPV6_RTHDR", IPV6_RTHDR, CONST_CS | CONST_PERSISTENT);
434 REGISTER_LONG_CONSTANT("IPV6_HOPOPTS", IPV6_HOPOPTS, CONST_CS | CONST_PERSISTENT);
435 REGISTER_LONG_CONSTANT("IPV6_DSTOPTS", IPV6_DSTOPTS, CONST_CS | CONST_PERSISTENT);
436 */
437
438 #ifdef SCM_RIGHTS
439 REGISTER_LONG_CONSTANT("SCM_RIGHTS", SCM_RIGHTS, CONST_CS | CONST_PERSISTENT);
440 #endif
441 #ifdef SO_PASSCRED
442 REGISTER_LONG_CONSTANT("SCM_CREDENTIALS", SCM_CREDENTIALS, CONST_CS | CONST_PERSISTENT);
443 REGISTER_LONG_CONSTANT("SO_PASSCRED", SO_PASSCRED, CONST_CS | CONST_PERSISTENT);
444 #endif
445
446 #ifdef ZTS
447 ancillary_mutex = tsrm_mutex_alloc();
448 #endif
449 }
450
php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)451 void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)
452 {
453 #ifdef ZTS
454 tsrm_mutex_free(ancillary_mutex);
455 #endif
456
457 destroy_ancillary_registry();
458 }
459