1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Gustavo Lopes <cataphract@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef __sun
18 /* to enable 'new' ancillary data layout instead */
19 # define _XPG4_2
20 #endif
21 #include <php.h>
22 #include "php_sockets.h"
23 #include "sendrecvmsg.h"
24 #include "conversions.h"
25 #include <limits.h>
26 #include <Zend/zend_llist.h>
27 #ifdef ZTS
28 #include <TSRM/TSRM.h>
29 #endif
30
31 #define MAX_USER_BUFF_SIZE ((size_t)(100*1024*1024))
32 #define DEFAULT_BUFF_SIZE 8192
33 #define MAX_ARRAY_KEY_SIZE 128
34
35 #ifdef PHP_WIN32
36 #include "windows_common.h"
37 #include <Mswsock.h>
38 #define IPV6_RECVPKTINFO IPV6_PKTINFO
39 #define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
40 #define msghdr _WSAMSG
41
42 static GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
43 static __declspec(thread) LPFN_WSARECVMSG WSARecvMsg = NULL;
recvmsg(int sockfd,struct msghdr * msg,int flags)44 inline ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
45 {
46 DWORD recvd = 0,
47 bytesReturned;
48
49 if (WSARecvMsg == NULL) {
50 int res = WSAIoctl((SOCKET) sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
51 &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
52 &WSARecvMsg, sizeof(WSARecvMsg),
53 &bytesReturned, NULL, NULL);
54 if (res != 0) {
55 return -1;
56 }
57 }
58
59 msg->dwFlags = (DWORD)flags;
60 return WSARecvMsg((SOCKET)sockfd, msg, &recvd, NULL, NULL) == 0
61 ? (ssize_t)recvd
62 : -1;
63 }
sendmsg(int sockfd,const struct msghdr * msg,int flags)64 inline ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
65 {
66 DWORD sent = 0;
67 return WSASendMsg((SOCKET)sockfd, (struct msghdr*)msg, (DWORD)flags, &sent, NULL, NULL) == 0
68 ? (ssize_t)sent
69 : -1;
70 }
71 #endif
72
73 #define LONG_CHECK_VALID_INT(l, arg_pos) \
74 do { \
75 if ((l) < INT_MIN || (l) > INT_MAX) { \
76 zend_argument_value_error((arg_pos), "must be between %d and %d", INT_MIN, INT_MAX); \
77 RETURN_THROWS(); \
78 } \
79 } while (0)
80
81 static struct {
82 int initialized;
83 HashTable ht;
84 } ancillary_registry;
85
86
ancillary_registery_free_elem(zval * el)87 static void ancillary_registery_free_elem(zval *el) {
88 pefree(Z_PTR_P(el), 1);
89 }
90
91 #ifdef ZTS
92 static MUTEX_T ancillary_mutex;
93 #endif
init_ancillary_registry(void)94 static void init_ancillary_registry(void)
95 {
96 ancillary_reg_entry entry;
97 anc_reg_key key;
98 ancillary_registry.initialized = 1;
99
100 zend_hash_init(&ancillary_registry.ht, 32, NULL, ancillary_registery_free_elem, 1);
101
102 #define PUT_ENTRY(sizev, var_size, calc, from, to, level, type) \
103 entry.size = sizev; \
104 entry.var_el_size = var_size; \
105 entry.calc_space = calc; \
106 entry.from_array = from; \
107 entry.to_array = to; \
108 key.cmsg_level = level; \
109 key.cmsg_type = type; \
110 zend_hash_str_update_mem(&ancillary_registry.ht, (char*)&key, sizeof(key), (void*)&entry, sizeof(entry))
111
112 #if defined(IPV6_PKTINFO) && HAVE_IPV6
113 PUT_ENTRY(sizeof(struct in6_pktinfo), 0, 0, from_zval_write_in6_pktinfo,
114 to_zval_read_in6_pktinfo, IPPROTO_IPV6, IPV6_PKTINFO);
115 #endif
116
117 #if defined(IPV6_HOPLIMIT) && HAVE_IPV6
118 PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
119 to_zval_read_int, IPPROTO_IPV6, IPV6_HOPLIMIT);
120 #endif
121
122 #if defined(IPV6_TCLASS) && HAVE_IPV6
123 PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
124 to_zval_read_int, IPPROTO_IPV6, IPV6_TCLASS);
125 #endif
126
127 #ifdef SO_PASSCRED
128 #ifdef ANC_CREDS_UCRED
129 PUT_ENTRY(sizeof(struct ucred), 0, 0, from_zval_write_ucred,
130 to_zval_read_ucred, SOL_SOCKET, SCM_CREDENTIALS);
131 #else
132 PUT_ENTRY(sizeof(struct cmsgcred), 0, 0, from_zval_write_ucred,
133 to_zval_read_ucred, SOL_SOCKET, SCM_CREDS);
134 #endif
135 #endif
136
137 #ifdef SCM_RIGHTS
138 PUT_ENTRY(0, sizeof(int), calculate_scm_rights_space, from_zval_write_fd_array,
139 to_zval_read_fd_array, SOL_SOCKET, SCM_RIGHTS);
140 #endif
141
142 }
destroy_ancillary_registry(void)143 static void destroy_ancillary_registry(void)
144 {
145 if (ancillary_registry.initialized) {
146 zend_hash_destroy(&ancillary_registry.ht);
147 ancillary_registry.initialized = 0;
148 }
149 }
get_ancillary_reg_entry(int cmsg_level,int msg_type)150 ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type)
151 {
152 anc_reg_key key = { cmsg_level, msg_type };
153 ancillary_reg_entry *entry;
154
155 #ifdef ZTS
156 tsrm_mutex_lock(ancillary_mutex);
157 #endif
158 if (!ancillary_registry.initialized) {
159 init_ancillary_registry();
160 }
161 #ifdef ZTS
162 tsrm_mutex_unlock(ancillary_mutex);
163 #endif
164
165 if ((entry = zend_hash_str_find_ptr(&ancillary_registry.ht, (char*)&key, sizeof(key))) != NULL) {
166 return entry;
167 } else {
168 return NULL;
169 }
170 }
171
PHP_FUNCTION(socket_sendmsg)172 PHP_FUNCTION(socket_sendmsg)
173 {
174 zval *zsocket,
175 *zmsg;
176 zend_long flags = 0;
177 php_socket *php_sock;
178 struct msghdr *msghdr;
179 zend_llist *allocations;
180 struct err_s err = {0};
181 ssize_t res;
182
183 /* zmsg should be passed by ref */
184 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oa|l", &zsocket, socket_ce, &zmsg, &flags) == FAILURE) {
185 RETURN_THROWS();
186 }
187
188 LONG_CHECK_VALID_INT(flags, 3);
189
190 php_sock = Z_SOCKET_P(zsocket);
191 ENSURE_SOCKET_VALID(php_sock);
192
193 msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_send,
194 sizeof(*msghdr), "msghdr", &allocations, &err);
195
196 if (err.has_error) {
197 err_msg_dispose(&err);
198 RETURN_FALSE;
199 }
200
201 res = sendmsg(php_sock->bsd_socket, msghdr, (int)flags);
202
203 if (res != -1) {
204 RETVAL_LONG((zend_long)res);
205 } else {
206 PHP_SOCKET_ERROR(php_sock, "Error in sendmsg", errno);
207 RETVAL_FALSE;
208 }
209
210 allocations_dispose(&allocations);
211 }
212
PHP_FUNCTION(socket_recvmsg)213 PHP_FUNCTION(socket_recvmsg)
214 {
215 zval *zsocket,
216 *zmsg;
217 zend_long flags = 0;
218 php_socket *php_sock;
219 ssize_t res;
220 struct msghdr *msghdr;
221 zend_llist *allocations;
222 struct err_s err = {0};
223
224 //ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
225 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oa|l", &zsocket, socket_ce, &zmsg, &flags) == FAILURE) {
226 RETURN_THROWS();
227 }
228
229 LONG_CHECK_VALID_INT(flags, 3);
230
231 php_sock = Z_SOCKET_P(zsocket);
232 ENSURE_SOCKET_VALID(php_sock);
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
258 zval_ptr_dtor(zmsg);
259 if (!err.has_error) {
260 ZVAL_COPY_VALUE(zmsg, zres);
261 } else {
262 err_msg_dispose(&err);
263 ZVAL_FALSE(zmsg);
264 /* no need to destroy/free zres -- it's NULL in this circumstance */
265 assert(zres == NULL);
266 }
267 RETVAL_LONG((zend_long)res);
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 RETVAL_FALSE;
273 }
274
275 allocations_dispose(&allocations);
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_THROWS();
288 }
289
290 LONG_CHECK_VALID_INT(level, 1);
291 LONG_CHECK_VALID_INT(type, 2);
292 LONG_CHECK_VALID_INT(n, 3);
293
294 if (n < 0) {
295 zend_argument_value_error(3, "must be greater than or equal to 0");
296 RETURN_THROWS();
297 }
298
299 entry = get_ancillary_reg_entry(level, type);
300 if (entry == NULL) {
301 zend_value_error("Pair level " ZEND_LONG_FMT " and/or type " ZEND_LONG_FMT " is not supported",
302 level, type);
303 RETURN_THROWS();
304 }
305
306 if (entry->var_el_size > 0) {
307 /* Leading underscore to avoid symbol collision on AIX. */
308 size_t _rem_size = ZEND_LONG_MAX - entry->size;
309 size_t n_max = _rem_size / entry->var_el_size;
310 size_t size = entry->size + n * entry->var_el_size;
311 size_t total_size = CMSG_SPACE(size);
312 if (n > n_max /* zend_long overflow */
313 || total_size > ZEND_LONG_MAX
314 || total_size < size /* align overflow */) {
315 zend_argument_value_error(3, "is too large");
316 RETURN_THROWS();
317 }
318 }
319
320 RETURN_LONG((zend_long)CMSG_SPACE(entry->size + n * entry->var_el_size));
321 }
322
323 #if HAVE_IPV6
php_do_setsockopt_ipv6_rfc3542(php_socket * php_sock,int level,int optname,zval * arg4)324 int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *arg4)
325 {
326 struct err_s err = {0};
327 zend_llist *allocations = NULL;
328 void *opt_ptr;
329 socklen_t optlen;
330 int retval;
331
332 assert(level == IPPROTO_IPV6);
333
334 switch (optname) {
335 #ifdef IPV6_PKTINFO
336 case IPV6_PKTINFO:
337 #ifdef PHP_WIN32
338 if (Z_TYPE_P(arg4) == IS_ARRAY) {
339 php_error_docref(NULL, E_WARNING, "Windows does not "
340 "support sticky IPV6_PKTINFO");
341 return FAILURE;
342 } else {
343 /* windows has no IPV6_RECVPKTINFO, and uses IPV6_PKTINFO
344 * for the same effect. We define IPV6_RECVPKTINFO to be
345 * IPV6_PKTINFO, so assume the assume user used IPV6_RECVPKTINFO */
346 return 1;
347 }
348 #endif
349 opt_ptr = from_zval_run_conversions(arg4, php_sock, from_zval_write_in6_pktinfo,
350 sizeof(struct in6_pktinfo), "in6_pktinfo", &allocations, &err);
351 if (err.has_error) {
352 err_msg_dispose(&err);
353 return FAILURE;
354 }
355
356 optlen = sizeof(struct in6_pktinfo);
357 goto dosockopt;
358 #endif
359 }
360
361 /* we also support IPV6_TCLASS, but that can be handled by the default
362 * integer optval handling in the caller */
363 return 1;
364
365 dosockopt:
366 retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
367 if (retval != 0) {
368 PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
369 }
370 allocations_dispose(&allocations);
371
372 return retval != 0 ? FAILURE : SUCCESS;
373 }
374
php_do_getsockopt_ipv6_rfc3542(php_socket * php_sock,int level,int optname,zval * result)375 int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result)
376 {
377 struct err_s err = {0};
378 void *buffer;
379 socklen_t size;
380 int res;
381 to_zval_read_field *reader;
382
383 assert(level == IPPROTO_IPV6);
384
385 switch (optname) {
386 #ifdef IPV6_PKTINFO
387 case IPV6_PKTINFO:
388 size = sizeof(struct in6_pktinfo);
389 reader = &to_zval_read_in6_pktinfo;
390 break;
391 #endif
392 default:
393 return 1;
394 }
395
396 buffer = ecalloc(1, size);
397 res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size);
398 if (res != 0) {
399 PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno);
400 } else {
401 zval tmp;
402 zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo",
403 empty_key_value_list, &err, &tmp);
404 if (err.has_error) {
405 err_msg_dispose(&err);
406 res = -1;
407 } else {
408 ZVAL_COPY_VALUE(result, zv);
409 }
410 }
411 efree(buffer);
412
413 return res == 0 ? SUCCESS : FAILURE;
414 }
415 #endif /* HAVE_IPV6 */
416
php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)417 void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)
418 {
419 /* IPv6 ancillary data */
420 #if defined(IPV6_RECVPKTINFO) && HAVE_IPV6
421 REGISTER_LONG_CONSTANT("IPV6_RECVPKTINFO", IPV6_RECVPKTINFO, CONST_CS | CONST_PERSISTENT);
422 REGISTER_LONG_CONSTANT("IPV6_PKTINFO", IPV6_PKTINFO, CONST_CS | CONST_PERSISTENT);
423 #endif
424 #if defined(IPV6_RECVHOPLIMIT) && HAVE_IPV6
425 REGISTER_LONG_CONSTANT("IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT, CONST_CS | CONST_PERSISTENT);
426 REGISTER_LONG_CONSTANT("IPV6_HOPLIMIT", IPV6_HOPLIMIT, CONST_CS | CONST_PERSISTENT);
427 #endif
428 /* would require some effort:
429 REGISTER_LONG_CONSTANT("IPV6_RECVRTHDR", IPV6_RECVRTHDR, CONST_CS | CONST_PERSISTENT);
430 REGISTER_LONG_CONSTANT("IPV6_RECVHOPOPTS", IPV6_RECVHOPOPTS, CONST_CS | CONST_PERSISTENT);
431 REGISTER_LONG_CONSTANT("IPV6_RECVDSTOPTS", IPV6_RECVDSTOPTS, CONST_CS | CONST_PERSISTENT);
432 */
433 #if defined(IPV6_RECVTCLASS) && HAVE_IPV6
434 REGISTER_LONG_CONSTANT("IPV6_RECVTCLASS", IPV6_RECVTCLASS, CONST_CS | CONST_PERSISTENT);
435 REGISTER_LONG_CONSTANT("IPV6_TCLASS", IPV6_TCLASS, CONST_CS | CONST_PERSISTENT);
436 #endif
437
438 /*
439 REGISTER_LONG_CONSTANT("IPV6_RTHDR", IPV6_RTHDR, CONST_CS | CONST_PERSISTENT);
440 REGISTER_LONG_CONSTANT("IPV6_HOPOPTS", IPV6_HOPOPTS, CONST_CS | CONST_PERSISTENT);
441 REGISTER_LONG_CONSTANT("IPV6_DSTOPTS", IPV6_DSTOPTS, CONST_CS | CONST_PERSISTENT);
442 */
443
444 #ifdef SCM_RIGHTS
445 REGISTER_LONG_CONSTANT("SCM_RIGHTS", SCM_RIGHTS, CONST_CS | CONST_PERSISTENT);
446 #endif
447 #ifdef SO_PASSCRED
448 #ifdef SCM_CREDENTIALS
449 REGISTER_LONG_CONSTANT("SCM_CREDENTIALS", SCM_CREDENTIALS, CONST_CS | CONST_PERSISTENT);
450 #else
451 REGISTER_LONG_CONSTANT("SCM_CREDS", SCM_CREDS, CONST_CS | CONST_PERSISTENT);
452 #endif
453 REGISTER_LONG_CONSTANT("SO_PASSCRED", SO_PASSCRED, CONST_CS | CONST_PERSISTENT);
454 #endif
455
456 #ifdef ZTS
457 ancillary_mutex = tsrm_mutex_alloc();
458 #endif
459 }
460
php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)461 void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)
462 {
463 #ifdef ZTS
464 tsrm_mutex_free(ancillary_mutex);
465 #endif
466
467 destroy_ancillary_registry();
468 }
469