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