php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
sendrecvmsg.c
Go to the documentation of this file.
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 msghdr _WSAMSG
39
40static GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
41static __declspec(thread) LPFN_WSARECVMSG WSARecvMsg = NULL;
42inline 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}
62inline 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, arg_pos) \
72 do { \
73 if ((l) < INT_MIN || (l) > INT_MAX) { \
74 zend_argument_value_error((arg_pos), "must be between %d and %d", INT_MIN, INT_MAX); \
75 RETURN_THROWS(); \
76 } \
77 } while (0)
78
79static struct {
82} ancillary_registry;
83
84
85static void ancillary_registery_free_elem(zval *el) {
86 pefree(Z_PTR_P(el), 1);
87}
88
89#ifdef ZTS
90static MUTEX_T ancillary_mutex;
91#endif
92static void init_ancillary_registry(void)
93{
96 ancillary_registry.initialized = 1;
97
98 zend_hash_init(&ancillary_registry.ht, 32, NULL, ancillary_registery_free_elem, 1);
99
100#define PUT_ENTRY(sizev, var_size, calc, from, to, level, type) \
101 entry.size = sizev; \
102 entry.var_el_size = var_size; \
103 entry.calc_space = calc; \
104 entry.from_array = from; \
105 entry.to_array = to; \
106 key.cmsg_level = level; \
107 key.cmsg_type = type; \
108 zend_hash_str_update_mem(&ancillary_registry.ht, (char*)&key, sizeof(key), (void*)&entry, sizeof(entry))
109
110#if defined(IPV6_PKTINFO) && defined(HAVE_IPV6)
111 PUT_ENTRY(sizeof(struct in6_pktinfo), 0, 0, from_zval_write_in6_pktinfo,
112 to_zval_read_in6_pktinfo, IPPROTO_IPV6, IPV6_PKTINFO);
113#endif
114
115#if defined(IPV6_HOPLIMIT) && defined(HAVE_IPV6)
116 PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
118#endif
119
120#if defined(IPV6_TCLASS) && defined(HAVE_IPV6)
121 PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
123#endif
124
125#ifdef SO_PASSCRED
126#ifdef HAVE_STRUCT_UCRED
127 PUT_ENTRY(sizeof(struct ucred), 0, 0, from_zval_write_ucred,
128 to_zval_read_ucred, SOL_SOCKET, SCM_CREDENTIALS);
129#else
130 PUT_ENTRY(sizeof(struct cmsgcred), 0, 0, from_zval_write_ucred,
131 to_zval_read_ucred, SOL_SOCKET, SCM_CREDS);
132#endif
133#endif
134
135#if defined(LOCAL_CREDS_PERSISTENT)
136 PUT_ENTRY(SOCKCRED2SIZE(1), 1, 0, from_zval_write_ucred,
137 to_zval_read_ucred, SOL_SOCKET, SCM_CREDS2);
138#elif defined(LOCAL_CREDS)
139 PUT_ENTRY(SOCKCREDSIZE(1), 1, 0, from_zval_write_ucred,
140 to_zval_read_ucred, SOL_SOCKET, SCM_CREDS);
141#endif
142
143#ifdef SCM_RIGHTS
144 PUT_ENTRY(0, sizeof(int), calculate_scm_rights_space, from_zval_write_fd_array,
145 to_zval_read_fd_array, SOL_SOCKET, SCM_RIGHTS);
146#endif
147
148}
149static void destroy_ancillary_registry(void)
150{
151 if (ancillary_registry.initialized) {
152 zend_hash_destroy(&ancillary_registry.ht);
153 ancillary_registry.initialized = 0;
154 }
155}
156ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type)
157{
158 anc_reg_key key = { cmsg_level, msg_type };
159 ancillary_reg_entry *entry;
160
161#ifdef ZTS
162 tsrm_mutex_lock(ancillary_mutex);
163#endif
164 if (!ancillary_registry.initialized) {
165 init_ancillary_registry();
166 }
167#ifdef ZTS
168 tsrm_mutex_unlock(ancillary_mutex);
169#endif
170
171 if ((entry = zend_hash_str_find_ptr(&ancillary_registry.ht, (char*)&key, sizeof(key))) != NULL) {
172 return entry;
173 } else {
174 return NULL;
175 }
176}
177
179{
180 zval *zsocket,
181 *zmsg;
182 zend_long flags = 0;
183 php_socket *php_sock;
184 struct msghdr *msghdr;
185 zend_llist *allocations;
186 struct err_s err = {0};
187 ssize_t res;
188
189 /* zmsg should be passed by ref */
190 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oa|l", &zsocket, socket_ce, &zmsg, &flags) == FAILURE) {
192 }
193
195
196 php_sock = Z_SOCKET_P(zsocket);
197 ENSURE_SOCKET_VALID(php_sock);
198
200 sizeof(*msghdr), "msghdr", &allocations, &err);
201
202 if (err.has_error) {
205 }
206
207 res = sendmsg(php_sock->bsd_socket, msghdr, (int)flags);
208
209 if (res != -1) {
211 } else {
212 PHP_SOCKET_ERROR(php_sock, "Error in sendmsg", errno);
214 }
215
216 allocations_dispose(&allocations);
217}
218
220{
221 zval *zsocket,
222 *zmsg;
223 zend_long flags = 0;
224 php_socket *php_sock;
225 ssize_t res;
226 struct msghdr *msghdr;
227 zend_llist *allocations;
228 struct err_s err = {0};
229
230 //ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
231 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oa|l", &zsocket, socket_ce, &zmsg, &flags) == FAILURE) {
233 }
234
236
237 php_sock = Z_SOCKET_P(zsocket);
238 ENSURE_SOCKET_VALID(php_sock);
239
241 sizeof(*msghdr), "msghdr", &allocations, &err);
242
243 if (err.has_error) {
246 }
247
248 res = recvmsg(php_sock->bsd_socket, msghdr, (int)flags);
249
250 if (res != -1) {
251 zval *zres, tmp;
252 struct key_value kv[] = {
254 {0}
255 };
256
257
258 zres = to_zval_run_conversions((char *)msghdr, to_zval_read_msghdr,
259 "msghdr", kv, &err, &tmp);
260
261 /* we don;t need msghdr anymore; free it */
262 msghdr = NULL;
263
264 zval_ptr_dtor(zmsg);
265 if (!err.has_error) {
266 ZVAL_COPY_VALUE(zmsg, zres);
267 } else {
269 ZVAL_FALSE(zmsg);
270 /* no need to destroy/free zres -- it's NULL in this circumstance */
271 assert(zres == NULL);
272 }
274 } else {
275 SOCKETS_G(last_error) = errno;
276 php_error_docref(NULL, E_WARNING, "Error in recvmsg [%d]: %s",
279 }
280
281 allocations_dispose(&allocations);
282}
283
285{
286 zend_long level,
287 type,
288 n = 0;
289 ancillary_reg_entry *entry;
290
292 &level, &type, &n) == FAILURE) {
294 }
295
296 LONG_CHECK_VALID_INT(level, 1);
299
300 if (n < 0) {
301 zend_argument_value_error(3, "must be greater than or equal to 0");
303 }
304
305 entry = get_ancillary_reg_entry(level, type);
306 if (entry == NULL) {
307 zend_value_error("Pair level " ZEND_LONG_FMT " and/or type " ZEND_LONG_FMT " is not supported",
308 level, type);
310 }
311
312 if (entry->var_el_size > 0) {
313 /* Leading underscore to avoid symbol collision on AIX. */
314 size_t _rem_size = ZEND_LONG_MAX - entry->size;
315 size_t n_max = _rem_size / entry->var_el_size;
316 size_t size = entry->size + n * entry->var_el_size;
317 size_t total_size = CMSG_SPACE(size);
318 if (n > n_max /* zend_long overflow */
319 || total_size > ZEND_LONG_MAX
320 || total_size < size /* align overflow */) {
321 zend_argument_value_error(3, "is too large");
323 }
324 }
325
326 RETURN_LONG((zend_long)CMSG_SPACE(entry->size + n * entry->var_el_size));
327}
328
329#ifdef HAVE_IPV6
330int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *arg4)
331{
332 struct err_s err = {0};
333 zend_llist *allocations = NULL;
334 void *opt_ptr;
335 socklen_t optlen;
336 int retval;
337
339
340 switch (optname) {
341#ifdef IPV6_PKTINFO
342 case IPV6_PKTINFO:
343#ifdef PHP_WIN32
344 if (Z_TYPE_P(arg4) == IS_ARRAY) {
345 php_error_docref(NULL, E_WARNING, "Windows does not "
346 "support sticky IPV6_PKTINFO");
347 return FAILURE;
348 } else {
349 /* windows has no IPV6_RECVPKTINFO, and uses IPV6_PKTINFO
350 * for the same effect. We define IPV6_RECVPKTINFO to be
351 * IPV6_PKTINFO, so assume the assume user used IPV6_RECVPKTINFO */
352 return 1;
353 }
354#endif
355 opt_ptr = from_zval_run_conversions(arg4, php_sock, from_zval_write_in6_pktinfo,
356 sizeof(struct in6_pktinfo), "in6_pktinfo", &allocations, &err);
357 if (err.has_error) {
359 return FAILURE;
360 }
361
362 optlen = sizeof(struct in6_pktinfo);
363 goto dosockopt;
364#endif
365 }
366
367 /* we also support IPV6_TCLASS, but that can be handled by the default
368 * integer optval handling in the caller */
369 return 1;
370
371dosockopt:
372 retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
373 if (retval != 0) {
374 PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
375 }
376 allocations_dispose(&allocations);
377
378 return retval != 0 ? FAILURE : SUCCESS;
379}
380
381int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result)
382{
383 struct err_s err = {0};
384 void *buffer;
386 int res;
387 to_zval_read_field *reader;
388
390
391 switch (optname) {
392#ifdef IPV6_PKTINFO
393 case IPV6_PKTINFO:
394 size = sizeof(struct in6_pktinfo);
395 reader = &to_zval_read_in6_pktinfo;
396 break;
397#endif
398 default:
399 return 1;
400 }
401
402 buffer = ecalloc(1, size);
403 res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size);
404 if (res != 0) {
405 PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno);
406 } else {
407 zval tmp;
408 zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo",
409 empty_key_value_list, &err, &tmp);
410 if (err.has_error) {
412 res = -1;
413 } else {
415 }
416 }
417 efree(buffer);
418
419 return res == 0 ? SUCCESS : FAILURE;
420}
421#endif /* HAVE_IPV6 */
422
424{
425#ifdef ZTS
426 ancillary_mutex = tsrm_mutex_alloc();
427#endif
428}
429
431{
432#ifdef ZTS
433 tsrm_mutex_free(ancillary_mutex);
434#endif
435
436 destroy_ancillary_registry();
437}
assert(mixed $assertion, Throwable|string|null $description=null)
#define KEY_RECVMSG_RET
void * from_zval_run_conversions(const zval *container, php_socket *sock, from_zval_write_field *writer, size_t struct_size, const char *top_name, zend_llist **allocations, struct err_s *err)
void to_zval_read_msghdr(const char *msghdr_c, zval *zv, res_context *ctx)
void from_zval_write_msghdr_recv(const zval *container, char *msghdr_c, ser_context *ctx)
void allocations_dispose(zend_llist **allocations)
const struct key_value empty_key_value_list[]
void from_zval_write_int(const zval *arr_value, char *field, ser_context *ctx)
void err_msg_dispose(struct err_s *err)
void from_zval_write_msghdr_send(const zval *container, char *msghdr_c, ser_context *ctx)
void to_zval_read_int(const char *data, zval *zv, res_context *ctx)
zval * to_zval_run_conversions(const char *structure, to_zval_read_field *reader, const char *top_name, const struct key_value *key_value_pairs, struct err_s *err, zval *zv)
void to_zval_read_field(const char *data, zval *zv, res_context *ctx)
Definition conversions.h:46
#define DWORD
Definition exif.c:1762
zend_class_entry * socket_ce
Definition sockets.c:103
char * sockets_strerror(int error)
Definition sockets.c:367
unsigned int socklen_t
Definition fastcgi.c:87
zend_ffi_type * type
Definition ffi.c:3812
zval * zv
Definition ffi.c:3975
zend_long n
Definition ffi.c:4979
new_type size
Definition ffi.c:4365
zend_string * res
Definition ffi.c:4692
char * err
Definition ffi.c:3029
HashTable * ht
Definition ffi.c:4838
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
#define PHP_FUNCTION
Definition php.h:364
int last_error
Definition php_pcntl.h:48
unsigned char key[REFLECTION_KEY_LEN]
char * msg
Definition phpdbg.h:289
#define PUT_ENTRY(sizev, var_size, calc, from, to, level, type)
#define LONG_CHECK_VALID_INT(l, arg_pos)
Definition sendrecvmsg.c:71
void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)
ancillary_reg_entry * get_ancillary_reg_entry(int cmsg_level, int msg_type)
int initialized
Definition sendrecvmsg.c:80
void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)
int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *arg4)
int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result)
const IPV6_PKTINFO
const IPV6_TCLASS
const SCM_CREDS2
const SOL_SOCKET
const SCM_CREDS
socket_cmsg_space(int $level, int $type, int $num=0)
const SCM_CREDENTIALS
const IPPROTO_IPV6
socket_sendmsg(Socket $socket, array $message, int $flags=0)
const SCM_RIGHTS
const IPV6_HOPLIMIT
socket_recvmsg(Socket $socket, array &$message, int $flags=0)
Definition sendrecvmsg.h:28
socklen_t size
Definition sendrecvmsg.h:29
socklen_t var_el_size
Definition sendrecvmsg.h:30
Definition file.h:177
int level
Definition conversions.h:29
#define errno
ZEND_API ZEND_COLD void zend_value_error(const char *format,...)
Definition zend.c:1849
ZEND_API zend_result zend_parse_parameters(uint32_t num_args, const char *type_spec,...)
Definition zend_API.c:1300
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
#define ZEND_NUM_ARGS()
Definition zend_API.h:530
#define RETURN_FALSE
Definition zend_API.h:1058
#define RETURN_LONG(l)
Definition zend_API.h:1037
#define RETURN_THROWS()
Definition zend_API.h:1060
#define RETVAL_LONG(l)
Definition zend_API.h:1011
#define RETVAL_FALSE
Definition zend_API.h:1032
#define ecalloc(nmemb, size)
Definition zend_alloc.h:158
#define efree(ptr)
Definition zend_alloc.h:155
#define pefree(ptr, persistent)
Definition zend_alloc.h:191
struct _zval_struct zval
#define E_WARNING
Definition zend_errors.h:24
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
Definition zend_hash.c:1727
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
Definition zend_hash.h:108
struct _zend_llist zend_llist
int32_t zend_long
Definition zend_long.h:42
#define ZEND_LONG_FMT
Definition zend_long.h:87
#define ZEND_LONG_MAX
Definition zend_long.h:45
#define INIT_FUNC_ARGS
#define SHUTDOWN_FUNC_ARGS
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define ZVAL_FALSE(z)
struct _zend_array HashTable
Definition zend_types.h:386
#define IS_ARRAY
Definition zend_types.h:607
#define Z_PTR_P(zval_p)
@ FAILURE
Definition zend_types.h:61
#define ZVAL_COPY_VALUE(z, v)
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval retval
bool result