xref: /PHP-8.4/ext/sockets/conversions.c (revision 5853cdb7)
1 #ifdef __sun
2 /* to enable 'new' ancillary data layout instead */
3 # define _XPG4_2
4 #endif
5 #include "sockaddr_conv.h"
6 #include "conversions.h"
7 #include "sendrecvmsg.h" /* for ancillary registry */
8 #ifdef PHP_WIN32
9 # include "windows_common.h"
10 #endif
11 
12 #include <Zend/zend_llist.h>
13 #include <zend_smart_str.h>
14 
15 #ifndef PHP_WIN32
16 # include <sys/types.h>
17 # include <sys/socket.h>
18 # include <arpa/inet.h>
19 # include <netinet/in.h>
20 # include <sys/un.h>
21 # include <sys/ioctl.h>
22 # include <net/if.h>
23 #else
24 # include <stdint.h>
25 #endif
26 
27 #include <limits.h>
28 #include <stdarg.h>
29 #include <stddef.h>
30 
31 #ifdef PHP_WIN32
32 typedef unsigned short sa_family_t;
33 # define msghdr			_WSAMSG
34 /*
35 struct _WSAMSG {
36     LPSOCKADDR       name;				//void *msg_name
37     INT              namelen;			//socklen_t msg_namelen
38     LPWSABUF         lpBuffers;			//struct iovec *msg_iov
39     ULONG            dwBufferCount;		//size_t msg_iovlen
40     WSABUF           Control;			//void *msg_control, size_t msg_controllen
41     DWORD            dwFlags;			//int msg_flags
42 }
43 */
44 # define msg_name		name
45 # define msg_namelen	namelen
46 # define msg_iov		lpBuffers
47 # define msg_iovlen		dwBufferCount
48 # define msg_control	Control.buf
49 # define msg_controllen	Control.len
50 # define msg_flags		dwFlags
51 # define iov_base		buf
52 # define iov_len		len
53 
54 # ifdef CMSG_DATA
55 #  undef CMSG_DATA
56 # endif
57 # define CMSG_DATA		WSA_CMSG_DATA
58 #endif
59 
60 #define MAX_USER_BUFF_SIZE ((size_t)(100*1024*1024))
61 #define DEFAULT_BUFF_SIZE 8192
62 
63 /* The CMSG_DATA macro does pointer arithmetics on NULL which triggers errors in the Clang UBSAN build */
64 #ifdef __has_feature
65 # if __has_feature(undefined_behavior_sanitizer)
66 #  undef CMSG_DATA
67 #  define CMSG_DATA(cmsg) ((unsigned char *) ((uintptr_t) (cmsg) + sizeof(struct cmsghdr)))
68 # endif
69 #endif
70 
71 struct _ser_context {
72 	HashTable		params; /* stores pointers; has to be first */
73 	struct err_s	err;
74 	zend_llist		keys,
75 	/* common part to res_context ends here */
76 					allocations;
77 	php_socket		*sock;
78 };
79 struct _res_context {
80 	HashTable		params; /* stores pointers; has to be first */
81 	struct err_s	err;
82 	zend_llist		keys;
83 };
84 
85 typedef struct {
86 	/* zval info */
87 	const char *name;
88 	unsigned name_size;
89 	int required;
90 
91 	/* structure info */
92 	size_t field_offset; /* 0 to pass full structure, e.g. when more than
93 							one field is to be changed; in that case the
94 							callbacks need to know the name of the fields */
95 
96 	/* callbacks */
97 	from_zval_write_field *from_zval;
98 	to_zval_read_field *to_zval;
99 } field_descriptor;
100 
101 #define KEY_FILL_SOCKADDR "fill_sockaddr"
102 #define KEY_RECVMSG_RET   "recvmsg_ret"
103 #define KEY_CMSG_LEN	  "cmsg_len"
104 
105 const struct key_value empty_key_value_list[] = {{0}};
106 
107 /* PARAMETERS */
param_get_bool(void * ctx,const char * key,int def)108 static int param_get_bool(void *ctx, const char *key, int def)
109 {
110 	int *elem;
111 	if ((elem = zend_hash_str_find_ptr(ctx, key, strlen(key))) != NULL) {
112 		return *elem;
113 	} else {
114 		return def;
115 	}
116 }
117 
118 /* MEMORY */
accounted_emalloc(size_t alloc_size,ser_context * ctx)119 static inline void *accounted_emalloc(size_t alloc_size, ser_context *ctx)
120 {
121 	void *ret = emalloc(alloc_size);
122 	zend_llist_add_element(&ctx->allocations, &ret);
123 	return ret;
124 }
accounted_ecalloc(size_t nmemb,size_t alloc_size,ser_context * ctx)125 static inline void *accounted_ecalloc(size_t nmemb, size_t alloc_size, ser_context *ctx)
126 {
127 	void *ret = ecalloc(nmemb, alloc_size);
128 	zend_llist_add_element(&ctx->allocations, &ret);
129 	return ret;
130 }
accounted_safe_ecalloc(size_t nmemb,size_t alloc_size,size_t offset,ser_context * ctx)131 static inline void *accounted_safe_ecalloc(size_t nmemb, size_t alloc_size, size_t offset, ser_context *ctx)
132 {
133 	void *ret = safe_emalloc(nmemb, alloc_size, offset);
134 	memset(ret, '\0', nmemb * alloc_size + offset);
135 	zend_llist_add_element(&ctx->allocations, &ret);
136 	return ret;
137 }
138 
139 /* ERRORS */
do_from_to_zval_err(struct err_s * err,zend_llist * keys,const char * what_conv,const char * fmt,va_list ap)140 static void do_from_to_zval_err(struct err_s *err,
141 								zend_llist *keys,
142 								const char *what_conv,
143 								const char *fmt,
144 								va_list ap)
145 {
146 	smart_str			path = {0};
147 	const char			**node;
148 	char				*user_msg;
149 	int					user_msg_size;
150 	zend_llist_position	pos;
151 
152 	if (err->has_error) {
153 		return;
154 	}
155 
156 	for (node = zend_llist_get_first_ex(keys, &pos);
157 			node != NULL;
158 			node = zend_llist_get_next_ex(keys, &pos)) {
159 		smart_str_appends(&path, *node);
160 		smart_str_appends(&path, " > ");
161 	}
162 
163 	if (path.s && ZSTR_LEN(path.s) > 3) {
164 		ZSTR_LEN(path.s) -= 3;
165 	}
166 	smart_str_0(&path);
167 
168 	user_msg_size = vspprintf(&user_msg, 0, fmt, ap);
169 
170 	err->has_error = 1;
171 	err->level = E_WARNING;
172 	spprintf(&err->msg, 0, "error converting %s data (path: %s): %.*s",
173 			what_conv,
174 			path.s && *ZSTR_VAL(path.s) != '\0' ? ZSTR_VAL(path.s) : "unavailable",
175 			user_msg_size, user_msg);
176 	err->should_free = 1;
177 
178 	efree(user_msg);
179 	smart_str_free(&path);
180 }
181 ZEND_ATTRIBUTE_FORMAT(printf, 2 ,3)
do_from_zval_err(ser_context * ctx,const char * fmt,...)182 static void do_from_zval_err(ser_context *ctx, const char *fmt, ...)
183 {
184 	va_list ap;
185 
186 	va_start(ap, fmt);
187 	do_from_to_zval_err(&ctx->err, &ctx->keys, "user", fmt, ap);
188 	va_end(ap);
189 }
190 ZEND_ATTRIBUTE_FORMAT(printf, 2 ,3)
do_to_zval_err(res_context * ctx,const char * fmt,...)191 static void do_to_zval_err(res_context *ctx, const char *fmt, ...)
192 {
193 	va_list ap;
194 
195 	va_start(ap, fmt);
196 	do_from_to_zval_err(&ctx->err, &ctx->keys, "native", fmt, ap);
197 	va_end(ap);
198 }
199 
err_msg_dispose(struct err_s * err)200 void err_msg_dispose(struct err_s *err)
201 {
202 	if (err->msg != NULL) {
203 		php_error_docref(NULL, err->level, "%s", err->msg);
204 		if (err->should_free) {
205 			efree(err->msg);
206 		}
207 	}
208 }
allocations_dispose(zend_llist ** allocations)209 void allocations_dispose(zend_llist **allocations)
210 {
211 	zend_llist_destroy(*allocations);
212 	efree(*allocations);
213 	*allocations = NULL;
214 }
215 
from_array_iterate(const zval * arr,void (* func)(zval * elem,unsigned i,void ** args,ser_context * ctx),void ** args,ser_context * ctx)216 static unsigned from_array_iterate(const zval *arr,
217 								   void (*func)(zval *elem, unsigned i, void **args, ser_context *ctx),
218 								   void **args,
219 								   ser_context *ctx)
220 {
221 	unsigned		i;
222 	zval			*elem;
223 	char			buf[sizeof("element #4294967295")];
224 	char			*bufp = buf;
225 
226 	/* Note i starts at 1, not 0! */
227 	i = 1;
228 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), elem) {
229 		if ((size_t)snprintf(buf, sizeof(buf), "element #%u", i) >= sizeof(buf)) {
230 			memcpy(buf, "element", sizeof("element"));
231 		}
232 		zend_llist_add_element(&ctx->keys, &bufp);
233 
234 		func(elem, i, args, ctx);
235 
236 		zend_llist_remove_tail(&ctx->keys);
237 		if (ctx->err.has_error) {
238 			break;
239 		}
240 		i++;
241 	} ZEND_HASH_FOREACH_END();
242 
243 	return i -1;
244 }
245 
246 /* Generic Aggregated conversions */
from_zval_write_aggregation(const zval * container,char * structure,const field_descriptor * descriptors,ser_context * ctx)247 static void from_zval_write_aggregation(const zval *container,
248 										char *structure,
249 										const field_descriptor *descriptors,
250 										ser_context *ctx)
251 {
252 	const field_descriptor	*descr;
253 	zval					*elem;
254 
255 	if (Z_TYPE_P(container) != IS_ARRAY) {
256 		do_from_zval_err(ctx, "%s", "expected an array here");
257 	}
258 
259 	for (descr = descriptors; descr->name != NULL && !ctx->err.has_error; descr++) {
260 		if ((elem = zend_hash_str_find(Z_ARRVAL_P(container),
261 				descr->name, descr->name_size - 1)) != NULL) {
262 
263 			if (descr->from_zval == NULL) {
264 				do_from_zval_err(ctx, "No information on how to convert value "
265 						"of key '%s'", descr->name);
266 				break;
267 			}
268 
269 			zend_llist_add_element(&ctx->keys, (void*)&descr->name);
270 			descr->from_zval(elem, ((char*)structure) + descr->field_offset, ctx);
271 			zend_llist_remove_tail(&ctx->keys);
272 
273 		} else if (descr->required) {
274 			do_from_zval_err(ctx, "The key '%s' is required", descr->name);
275 			break;
276 		}
277 	}
278 }
to_zval_read_aggregation(const char * structure,zval * zarr,const field_descriptor * descriptors,res_context * ctx)279 static void to_zval_read_aggregation(const char *structure,
280 									 zval *zarr, /* initialized array */
281 									 const field_descriptor *descriptors,
282 									 res_context *ctx)
283 {
284 	const field_descriptor	*descr;
285 
286 	assert(Z_TYPE_P(zarr) == IS_ARRAY);
287 	assert(Z_ARRVAL_P(zarr) != NULL);
288 
289 	for (descr = descriptors; descr->name != NULL && !ctx->err.has_error; descr++) {
290 		zval *new_zv, tmp;
291 
292 		if (descr->to_zval == NULL) {
293 			do_to_zval_err(ctx, "No information on how to convert native "
294 					"field into value for key '%s'", descr->name);
295 			break;
296 		}
297 
298 		ZVAL_NULL(&tmp);
299 		new_zv = zend_symtable_str_update(Z_ARRVAL_P(zarr), descr->name, descr->name_size - 1, &tmp);
300 
301 		zend_llist_add_element(&ctx->keys, (void*)&descr->name);
302 		descr->to_zval(structure + descr->field_offset, new_zv, ctx);
303 		zend_llist_remove_tail(&ctx->keys);
304 	}
305 }
306 
307 /* CONVERSIONS for integers */
from_zval_integer_common(const zval * arr_value,ser_context * ctx)308 static zend_long from_zval_integer_common(const zval *arr_value, ser_context *ctx)
309 {
310 	zend_long ret = 0;
311 	zval lzval;
312 
313 	ZVAL_NULL(&lzval);
314 	if (Z_TYPE_P(arr_value) != IS_LONG) {
315 		ZVAL_COPY(&lzval, (zval *)arr_value);
316 		arr_value = &lzval;
317 	}
318 
319 	switch (Z_TYPE_P(arr_value)) {
320 	case IS_LONG:
321 long_case:
322 		ret = Z_LVAL_P(arr_value);
323 		break;
324 
325 	/* if not long we're operating on lzval */
326 	case IS_DOUBLE:
327 double_case:
328 		convert_to_long(&lzval);
329 		goto long_case;
330 
331 	case IS_OBJECT:
332 	case IS_STRING: {
333 		zend_long lval;
334 		double dval;
335 
336 		if (!try_convert_to_string(&lzval)) {
337 			ctx->err.has_error = 1;
338 			break;
339 		}
340 
341 		switch (is_numeric_string(Z_STRVAL(lzval), Z_STRLEN(lzval), &lval, &dval, 0)) {
342 		case IS_DOUBLE:
343 			zval_ptr_dtor_str(&lzval);
344 			ZVAL_DOUBLE(&lzval, dval);
345 			goto double_case;
346 
347 		case IS_LONG:
348 			zval_ptr_dtor_str(&lzval);
349 			ZVAL_LONG(&lzval, lval);
350 			goto long_case;
351 		}
352 
353 		/* if we get here, we don't have a numeric string */
354 		do_from_zval_err(ctx, "expected an integer, but got a non numeric "
355 				"string (possibly from a converted object): '%s'", Z_STRVAL_P(arr_value));
356 		break;
357 	}
358 
359 	default:
360 		do_from_zval_err(ctx, "%s", "expected an integer, either of a PHP "
361 				"integer type or of a convertible type");
362 		break;
363 	}
364 
365 	zval_ptr_dtor(&lzval);
366 
367 	return ret;
368 }
from_zval_write_int(const zval * arr_value,char * field,ser_context * ctx)369 void from_zval_write_int(const zval *arr_value, char *field, ser_context *ctx)
370 {
371 	zend_long lval;
372 	int ival;
373 
374 	lval = from_zval_integer_common(arr_value, ctx);
375 	if (ctx->err.has_error) {
376 		return;
377 	}
378 
379 	if (lval > INT_MAX || lval < INT_MIN) {
380 		do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
381 				"for a native int");
382 		return;
383 	}
384 
385 	ival = (int)lval;
386 	memcpy(field, &ival, sizeof(ival));
387 }
from_zval_write_uint32(const zval * arr_value,char * field,ser_context * ctx)388 static void from_zval_write_uint32(const zval *arr_value, char *field, ser_context *ctx)
389 {
390 	zend_long lval;
391 	uint32_t ival;
392 
393 	lval = from_zval_integer_common(arr_value, ctx);
394 	if (ctx->err.has_error) {
395 		return;
396 	}
397 
398 	if (sizeof(zend_long) > sizeof(uint32_t) && (lval < 0 || lval > 0xFFFFFFFF)) {
399 		do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
400 				"for an unsigned 32-bit integer");
401 		return;
402 	}
403 
404 	ival = (uint32_t)lval;
405 	memcpy(field, &ival, sizeof(ival));
406 }
from_zval_write_net_uint16(const zval * arr_value,char * field,ser_context * ctx)407 static void from_zval_write_net_uint16(const zval *arr_value, char *field, ser_context *ctx)
408 {
409 	zend_long lval;
410 	uint16_t ival;
411 
412 	lval = from_zval_integer_common(arr_value, ctx);
413 	if (ctx->err.has_error) {
414 		return;
415 	}
416 
417 	if (lval < 0 || lval > 0xFFFF) {
418 		do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
419 				"for an unsigned 16-bit integer");
420 		return;
421 	}
422 
423 	ival = htons((uint16_t)lval);
424 	memcpy(field, &ival, sizeof(ival));
425 }
from_zval_write_sa_family(const zval * arr_value,char * field,ser_context * ctx)426 static void from_zval_write_sa_family(const zval *arr_value, char *field, ser_context *ctx)
427 {
428 	zend_long lval;
429 	sa_family_t ival;
430 
431 	lval = from_zval_integer_common(arr_value, ctx);
432 	if (ctx->err.has_error) {
433 		return;
434 	}
435 
436 	if (lval < 0 || lval > (sa_family_t)-1) { /* sa_family_t is unsigned */
437 		do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
438 				"for a sa_family_t value");
439 		return;
440 	}
441 
442 	ival = (sa_family_t)lval;
443 	memcpy(field, &ival, sizeof(ival));
444 }
445 
446 #if defined(SO_PASSCRED) || defined(LOCAL_CREDS_PERSISTENT) || defined(LOCAL_CREDS)
from_zval_write_pid_t(const zval * arr_value,char * field,ser_context * ctx)447 static void from_zval_write_pid_t(const zval *arr_value, char *field, ser_context *ctx)
448 {
449 	zend_long lval;
450 	pid_t ival;
451 
452 	lval = from_zval_integer_common(arr_value, ctx);
453 	if (ctx->err.has_error) {
454 		return;
455 	}
456 
457 	if (lval < 0 || (pid_t)lval != lval) { /* pid_t is signed */
458 		do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
459 				"for a pid_t value");
460 		return;
461 	}
462 
463 	ival = (pid_t)lval;
464 	memcpy(field, &ival, sizeof(ival));
465 }
from_zval_write_uid_t(const zval * arr_value,char * field,ser_context * ctx)466 static void from_zval_write_uid_t(const zval *arr_value, char *field, ser_context *ctx)
467 {
468 	zend_long lval;
469 	uid_t ival;
470 
471 	lval = from_zval_integer_common(arr_value, ctx);
472 	if (ctx->err.has_error) {
473 		return;
474 	}
475 
476 	/* uid_t can be signed or unsigned (generally unsigned) */
477 	if ((uid_t)-1 > (uid_t)0) {
478 		if (sizeof(zend_long) > sizeof(uid_t) && (lval < 0 || (uid_t)lval != lval)) {
479 			do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
480 					"for a uid_t value");
481 			return;
482 		}
483 	} else {
484 		if (sizeof(zend_long) > sizeof(uid_t) && (uid_t)lval != lval) {
485 			do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
486 					"for a uid_t value");
487 			return;
488 		}
489 	}
490 
491 	ival = (uid_t)lval;
492 	memcpy(field, &ival, sizeof(ival));
493 }
494 #endif
495 
to_zval_read_int(const char * data,zval * zv,res_context * ctx)496 void to_zval_read_int(const char *data, zval *zv, res_context *ctx)
497 {
498 	int ival;
499 	memcpy(&ival, data, sizeof(ival));
500 
501 	ZVAL_LONG(zv, (zend_long)ival);
502 }
to_zval_read_net_uint16(const char * data,zval * zv,res_context * ctx)503 static void to_zval_read_net_uint16(const char *data, zval *zv, res_context *ctx)
504 {
505 	uint16_t ival;
506 	memcpy(&ival, data, sizeof(ival));
507 
508 	ZVAL_LONG(zv, (zend_long)ntohs(ival));
509 }
to_zval_read_sa_family(const char * data,zval * zv,res_context * ctx)510 static void to_zval_read_sa_family(const char *data, zval *zv, res_context *ctx)
511 {
512 	sa_family_t ival;
513 	memcpy(&ival, data, sizeof(ival));
514 
515 	ZVAL_LONG(zv, (zend_long)ival);
516 }
517 #ifdef HAVE_IPV6
to_zval_read_unsigned(const char * data,zval * zv,res_context * ctx)518 static void to_zval_read_unsigned(const char *data, zval *zv, res_context *ctx)
519 {
520 	unsigned ival;
521 	memcpy(&ival, data, sizeof(ival));
522 
523 	ZVAL_LONG(zv, (zend_long)ival);
524 }
to_zval_read_uint32(const char * data,zval * zv,res_context * ctx)525 static void to_zval_read_uint32(const char *data, zval *zv, res_context *ctx)
526 {
527 	uint32_t ival;
528 	memcpy(&ival, data, sizeof(ival));
529 
530 	ZVAL_LONG(zv, (zend_long)ival);
531 }
532 #endif
533 #if defined(SO_PASSCRED) || defined(LOCAL_CREDS_PERSISTENT) || defined(LOCAL_CREDS)
to_zval_read_pid_t(const char * data,zval * zv,res_context * ctx)534 static void to_zval_read_pid_t(const char *data, zval *zv, res_context *ctx)
535 {
536 	pid_t ival;
537 	memcpy(&ival, data, sizeof(ival));
538 
539 	ZVAL_LONG(zv, (zend_long)ival);
540 }
to_zval_read_uid_t(const char * data,zval * zv,res_context * ctx)541 static void to_zval_read_uid_t(const char *data, zval *zv, res_context *ctx)
542 {
543 	uid_t ival;
544 	memcpy(&ival, data, sizeof(ival));
545 
546 	ZVAL_LONG(zv, (zend_long)ival);
547 }
548 #endif
549 
550 /* CONVERSIONS for sockaddr */
from_zval_write_sin_addr(const zval * zaddr_str,char * inaddr,ser_context * ctx)551 static void from_zval_write_sin_addr(const zval *zaddr_str, char *inaddr, ser_context *ctx)
552 {
553 	int					res;
554 	struct sockaddr_in	saddr = {0};
555 	zend_string			*addr_str, *tmp_addr_str;
556 
557 	addr_str = zval_get_tmp_string((zval *) zaddr_str, &tmp_addr_str);
558 	res = php_set_inet_addr(&saddr, ZSTR_VAL(addr_str), ctx->sock);
559 	if (res) {
560 		memcpy(inaddr, &saddr.sin_addr, sizeof saddr.sin_addr);
561 	} else {
562 		/* error already emitted, but let's emit another more relevant */
563 		do_from_zval_err(ctx, "could not resolve address '%s' to get an AF_INET "
564 				"address", ZSTR_VAL(addr_str));
565 	}
566 
567 	zend_tmp_string_release(tmp_addr_str);
568 }
to_zval_read_sin_addr(const char * data,zval * zv,res_context * ctx)569 static void to_zval_read_sin_addr(const char *data, zval *zv, res_context *ctx)
570 {
571 	const struct in_addr *addr = (const struct in_addr *)data;
572 	socklen_t size = INET_ADDRSTRLEN;
573 	zend_string *str = zend_string_alloc(size - 1, 0);
574 	memset(ZSTR_VAL(str), '\0', size);
575 
576 	ZVAL_NEW_STR(zv, str);
577 
578 	if (inet_ntop(AF_INET, addr, Z_STRVAL_P(zv), size) == NULL) {
579 		do_to_zval_err(ctx, "could not convert IPv4 address to string "
580 				"(errno %d)", errno);
581 		return;
582 	}
583 
584 	Z_STRLEN_P(zv) = strlen(Z_STRVAL_P(zv));
585 }
586 static const field_descriptor descriptors_sockaddr_in[] = {
587 		{"family", sizeof("family"), 0, offsetof(struct sockaddr_in, sin_family), from_zval_write_sa_family, to_zval_read_sa_family},
588 		{"addr", sizeof("addr"), 0, offsetof(struct sockaddr_in, sin_addr), from_zval_write_sin_addr, to_zval_read_sin_addr},
589 		{"port", sizeof("port"), 0, offsetof(struct sockaddr_in, sin_port), from_zval_write_net_uint16, to_zval_read_net_uint16},
590 		{0}
591 };
from_zval_write_sockaddr_in(const zval * container,char * sockaddr,ser_context * ctx)592 static void from_zval_write_sockaddr_in(const zval *container, char *sockaddr, ser_context *ctx)
593 {
594 	from_zval_write_aggregation(container, sockaddr, descriptors_sockaddr_in, ctx);
595 }
to_zval_read_sockaddr_in(const char * data,zval * zv,res_context * ctx)596 static void to_zval_read_sockaddr_in(const char *data, zval *zv, res_context *ctx)
597 {
598 	to_zval_read_aggregation(data, zv, descriptors_sockaddr_in, ctx);
599 }
600 #ifdef HAVE_IPV6
from_zval_write_sin6_addr(const zval * zaddr_str,char * addr6,ser_context * ctx)601 static void from_zval_write_sin6_addr(const zval *zaddr_str, char *addr6, ser_context *ctx)
602 {
603 	int					res;
604 	struct sockaddr_in6	saddr6 = {0};
605 	zend_string			*addr_str, *tmp_addr_str;
606 
607 	addr_str = zval_get_tmp_string((zval *) zaddr_str, &tmp_addr_str);
608 	res = php_set_inet6_addr(&saddr6, ZSTR_VAL(addr_str), ctx->sock);
609 	if (res) {
610 		memcpy(addr6, &saddr6.sin6_addr, sizeof saddr6.sin6_addr);
611 	} else {
612 		/* error already emitted, but let's emit another more relevant */
613 		do_from_zval_err(ctx, "could not resolve address '%s' to get an AF_INET6 "
614 				"address", Z_STRVAL_P(zaddr_str));
615 	}
616 
617 	zend_tmp_string_release(tmp_addr_str);
618 }
to_zval_read_sin6_addr(const char * data,zval * zv,res_context * ctx)619 static void to_zval_read_sin6_addr(const char *data, zval *zv, res_context *ctx)
620 {
621 	const struct in6_addr *addr = (const struct in6_addr *)data;
622 	socklen_t size = INET6_ADDRSTRLEN;
623 	zend_string *str = zend_string_alloc(size - 1, 0);
624 
625 	memset(ZSTR_VAL(str), '\0', size);
626 
627 	ZVAL_NEW_STR(zv, str);
628 
629 	if (inet_ntop(AF_INET6, addr, Z_STRVAL_P(zv), size) == NULL) {
630 		do_to_zval_err(ctx, "could not convert IPv6 address to string "
631 				"(errno %d)", errno);
632 		return;
633 	}
634 
635 	Z_STRLEN_P(zv) = strlen(Z_STRVAL_P(zv));
636 }
637 static const field_descriptor descriptors_sockaddr_in6[] = {
638 		{"family", sizeof("family"), 0, offsetof(struct sockaddr_in6, sin6_family), from_zval_write_sa_family, to_zval_read_sa_family},
639 		{"addr", sizeof("addr"), 0, offsetof(struct sockaddr_in6, sin6_addr), from_zval_write_sin6_addr, to_zval_read_sin6_addr},
640 		{"port", sizeof("port"), 0, offsetof(struct sockaddr_in6, sin6_port), from_zval_write_net_uint16, to_zval_read_net_uint16},
641 		{"flowinfo", sizeof("flowinfo"), 0, offsetof(struct sockaddr_in6, sin6_flowinfo), from_zval_write_uint32, to_zval_read_uint32},
642 		{"scope_id", sizeof("scope_id"), 0, offsetof(struct sockaddr_in6, sin6_scope_id), from_zval_write_uint32, to_zval_read_uint32},
643 		{0}
644 };
from_zval_write_sockaddr_in6(const zval * container,char * sockaddr6,ser_context * ctx)645 static void from_zval_write_sockaddr_in6(const zval *container, char *sockaddr6, ser_context *ctx)
646 {
647 	from_zval_write_aggregation(container, sockaddr6, descriptors_sockaddr_in6, ctx);
648 }
to_zval_read_sockaddr_in6(const char * data,zval * zv,res_context * ctx)649 static void to_zval_read_sockaddr_in6(const char *data, zval *zv, res_context *ctx)
650 {
651 	to_zval_read_aggregation(data, zv, descriptors_sockaddr_in6, ctx);
652 }
653 #endif /* HAVE_IPV6 */
from_zval_write_sun_path(const zval * path,char * sockaddr_un_c,ser_context * ctx)654 static void from_zval_write_sun_path(const zval *path, char *sockaddr_un_c, ser_context *ctx)
655 {
656 	zend_string			*path_str, *tmp_path_str;
657 	struct sockaddr_un	*saddr = (struct sockaddr_un*)sockaddr_un_c;
658 
659 	path_str = zval_get_tmp_string((zval *) path, &tmp_path_str);
660 
661 	/* code in this file relies on the path being nul terminated, even though
662 	 * this is not required, at least on linux for abstract paths. It also
663 	 * assumes that the path is not empty */
664 	if (ZSTR_LEN(path_str) == 0) {
665 		do_from_zval_err(ctx, "%s", "the path is must not be empty");
666 		zend_tmp_string_release(tmp_path_str);
667 		return;
668 	}
669 	if (ZSTR_LEN(path_str) >= sizeof(saddr->sun_path)) {
670 		do_from_zval_err(ctx, "the path is too long, the maximum permitted "
671 				"length is %zd", sizeof(saddr->sun_path) - 1);
672 		zend_tmp_string_release(tmp_path_str);
673 		return;
674 	}
675 
676 	memcpy(&saddr->sun_path, ZSTR_VAL(path_str), ZSTR_LEN(path_str));
677 	saddr->sun_path[ZSTR_LEN(path_str)] = '\0';
678 
679 	zend_tmp_string_release(tmp_path_str);
680 }
to_zval_read_sun_path(const char * data,zval * zv,res_context * ctx)681 static void to_zval_read_sun_path(const char *data, zval *zv, res_context *ctx) {
682 	struct sockaddr_un	*saddr = (struct sockaddr_un*)data;
683 	char *nul_pos;
684 
685 	nul_pos = memchr(&saddr->sun_path, '\0', sizeof(saddr->sun_path));
686 	if (nul_pos == NULL) {
687 		do_to_zval_err(ctx, "could not find a NUL in the path");
688 		return;
689 	}
690 
691 	ZVAL_STRINGL(zv, saddr->sun_path, nul_pos - (char*)&saddr->sun_path);
692 }
693 static const field_descriptor descriptors_sockaddr_un[] = {
694 		{"family", sizeof("family"), 0, offsetof(struct sockaddr_un, sun_family), from_zval_write_sa_family, to_zval_read_sa_family},
695 		{"path", sizeof("path"), 0, 0, from_zval_write_sun_path, to_zval_read_sun_path},
696 		{0}
697 };
from_zval_write_sockaddr_un(const zval * container,char * sockaddr,ser_context * ctx)698 static void from_zval_write_sockaddr_un(const zval *container, char *sockaddr, ser_context *ctx)
699 {
700 	from_zval_write_aggregation(container, sockaddr, descriptors_sockaddr_un, ctx);
701 }
to_zval_read_sockaddr_un(const char * data,zval * zv,res_context * ctx)702 static void to_zval_read_sockaddr_un(const char *data, zval *zv, res_context *ctx)
703 {
704 	to_zval_read_aggregation(data, zv, descriptors_sockaddr_un, ctx);
705 }
from_zval_write_sockaddr_aux(const zval * container,struct sockaddr ** sockaddr_ptr,socklen_t * sockaddr_len,ser_context * ctx)706 static void from_zval_write_sockaddr_aux(const zval *container,
707 										 struct sockaddr **sockaddr_ptr,
708 										 socklen_t *sockaddr_len,
709 										 ser_context *ctx)
710 {
711 	int		family;
712 	zval	*elem;
713 	int		fill_sockaddr;
714 
715 	*sockaddr_ptr = NULL;
716 	*sockaddr_len = 0;
717 
718 	if (Z_TYPE_P(container) != IS_ARRAY) {
719 		do_from_zval_err(ctx, "%s", "expected an array here");
720 		return;
721 	}
722 
723 	fill_sockaddr = param_get_bool(ctx, KEY_FILL_SOCKADDR, 1);
724 
725 	if ((elem = zend_hash_str_find(Z_ARRVAL_P(container), "family", sizeof("family") - 1)) != NULL
726 			&& Z_TYPE_P(elem) != IS_NULL) {
727 		const char *node = "family";
728 		zend_llist_add_element(&ctx->keys, &node);
729 		family = 0; /* Silence compiler warning */
730 		from_zval_write_int(elem, (char*)&family, ctx);
731 		zend_llist_remove_tail(&ctx->keys);
732 
733 		if (UNEXPECTED(ctx->err.has_error)) {
734 			return;
735 		}
736 	} else {
737 		family = ctx->sock->type;
738 	}
739 
740 	switch (family) {
741 	case AF_INET:
742 		/* though not all OSes support sockaddr_in used in IPv6 sockets */
743 		if (ctx->sock->type != AF_INET && ctx->sock->type != AF_INET6) {
744 			do_from_zval_err(ctx, "the specified family (number %d) is not "
745 					"supported on this socket", family);
746 			return;
747 		}
748 		*sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_in), ctx);
749 		*sockaddr_len = sizeof(struct sockaddr_in);
750 		if (fill_sockaddr) {
751 			from_zval_write_sockaddr_in(container, (char*)*sockaddr_ptr, ctx);
752 			(*sockaddr_ptr)->sa_family = AF_INET;
753 		}
754 		break;
755 
756 #ifdef HAVE_IPV6
757 	case AF_INET6:
758 		if (ctx->sock->type != AF_INET6) {
759 			do_from_zval_err(ctx, "the specified family (AF_INET6) is not "
760 					"supported on this socket");
761 			return;
762 		}
763 		*sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_in6), ctx);
764 		*sockaddr_len = sizeof(struct sockaddr_in6);
765 		if (fill_sockaddr) {
766 			from_zval_write_sockaddr_in6(container, (char*)*sockaddr_ptr, ctx);
767 			(*sockaddr_ptr)->sa_family = AF_INET6;
768 		}
769 		break;
770 #endif /* HAVE_IPV6 */
771 
772 	case AF_UNIX:
773 		if (ctx->sock->type != AF_UNIX) {
774 			do_from_zval_err(ctx, "the specified family (AF_UNIX) is not "
775 					"supported on this socket");
776 			return;
777 		}
778 		*sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_un), ctx);
779 		if (fill_sockaddr) {
780 			struct sockaddr_un *sock_un = (struct sockaddr_un*)*sockaddr_ptr;
781 
782 			from_zval_write_sockaddr_un(container, (char*)*sockaddr_ptr, ctx);
783 			(*sockaddr_ptr)->sa_family = AF_UNIX;
784 
785 			/* calculating length is more complicated here. Giving the size of
786 			 * struct sockaddr_un here and relying on the nul termination of
787 			 * sun_path does not work for paths in the abstract namespace. Note
788 			 * that we always assume the path is not empty and nul terminated */
789 			*sockaddr_len = offsetof(struct sockaddr_un, sun_path) +
790 					(sock_un->sun_path[0] == '\0'
791 					? (1 + strlen(&sock_un->sun_path[1]))
792 					: strlen(sock_un->sun_path));
793 		} else {
794 			*sockaddr_len = sizeof(struct sockaddr_un);
795 		}
796 		break;
797 
798 	default:
799 		do_from_zval_err(ctx, "%s", "the only families currently supported are "
800 				"AF_INET, AF_INET6 and AF_UNIX");
801 		break;
802 	}
803 }
to_zval_read_sockaddr_aux(const char * sockaddr_c,zval * zv,res_context * ctx)804 static void to_zval_read_sockaddr_aux(const char *sockaddr_c, zval *zv, res_context *ctx)
805 {
806 	const struct sockaddr *saddr = (struct sockaddr *)sockaddr_c;
807 
808 	if (saddr->sa_family == 0) {
809 		ZVAL_NULL(zv);
810 		return;
811 	}
812 
813 	array_init(zv);
814 
815 	switch (saddr->sa_family) {
816 	case AF_INET:
817 		to_zval_read_sockaddr_in(sockaddr_c, zv, ctx);
818 		break;
819 
820 #ifdef HAVE_IPV6
821 	case AF_INET6:
822 		to_zval_read_sockaddr_in6(sockaddr_c, zv, ctx);
823 		break;
824 #endif /* HAVE_IPV6 */
825 
826 	case AF_UNIX:
827 		to_zval_read_sockaddr_un(sockaddr_c, zv, ctx);
828 		break;
829 
830 	default:
831 		do_to_zval_err(ctx, "cannot read struct sockaddr with family %d; "
832 				"not supported",
833 				(int)saddr->sa_family);
834 		break;
835 	}
836 }
837 
838 /* CONVERSIONS for cmsghdr */
839 /*
840  * [ level => , type => , data => [],]
841  * struct cmsghdr {
842  *  socklen_t cmsg_len;    // data byte count, including header
843  *  int       cmsg_level;  // originating protocol
844  *  int       cmsg_type;   // protocol-specific type
845  *  // followed by unsigned char cmsg_data[];
846  * };
847  */
from_zval_write_control(const zval * arr,void ** control_buf,zend_llist_element * alloc,size_t * control_len,size_t * offset,ser_context * ctx)848 static void from_zval_write_control(const zval			*arr,
849 									void				**control_buf,
850 									zend_llist_element	*alloc,
851 									size_t				*control_len,
852 									size_t				*offset,
853 									ser_context			*ctx)
854 {
855 	struct cmsghdr		*cmsghdr;
856 	int					level,
857 						type;
858 	size_t				data_len,
859 						req_space,
860 						space_left;
861 	ancillary_reg_entry	*entry;
862 
863 	static const field_descriptor descriptor_level[] = {
864 			{"level", sizeof("level"), 0, 0, from_zval_write_int, 0},
865 			{0}
866 	};
867 	static const field_descriptor descriptor_type[] = {
868 			{"type", sizeof("type"), 0, 0, from_zval_write_int, 0},
869 			{0}
870 	};
871 	field_descriptor descriptor_data[] = {
872 			{"data", sizeof("data"), 0, 0, 0, 0},
873 			{0}
874 	};
875 
876 	from_zval_write_aggregation(arr, (char *)&level, descriptor_level, ctx);
877 	if (ctx->err.has_error) {
878 		return;
879 	}
880 	from_zval_write_aggregation(arr, (char *)&type, descriptor_type, ctx);
881 	if (ctx->err.has_error) {
882 		return;
883 	}
884 
885 	entry = get_ancillary_reg_entry(level, type);
886 	if (entry == NULL) {
887 		do_from_zval_err(ctx, "cmsghdr with level %d and type %d not supported",
888 				level, type);
889 		return;
890 	}
891 
892 	if (entry->calc_space) {
893 		zval *data_elem;
894 		/* arr must be an array at this point */
895 		if ((data_elem = zend_hash_str_find(Z_ARRVAL_P(arr), "data", sizeof("data") - 1)) == NULL) {
896 			do_from_zval_err(ctx, "cmsghdr should have a 'data' element here");
897 			return;
898 		}
899 		data_len = entry->calc_space(data_elem, ctx);
900 		if (ctx->err.has_error) {
901 			return;
902 		}
903 	} else {
904 		data_len = entry->size;
905 	}
906 	req_space = CMSG_SPACE(data_len);
907 	space_left = *control_len - *offset;
908 	assert(*control_len >= *offset);
909 
910 	if (space_left < req_space) {
911 		*control_buf = safe_erealloc(*control_buf, 2, req_space, *control_len);
912 		*control_len += 2 * req_space;
913 		memset((char *)*control_buf + *offset, '\0', *control_len - *offset);
914 		memcpy(&alloc->data, control_buf, sizeof *control_buf);
915 	}
916 
917 	cmsghdr = (struct cmsghdr*)(((char*)*control_buf) + *offset);
918 	cmsghdr->cmsg_level	= level;
919 	cmsghdr->cmsg_type	= type;
920 	cmsghdr->cmsg_len	= CMSG_LEN(data_len);
921 
922 	descriptor_data[0].from_zval = entry->from_array;
923 	from_zval_write_aggregation(arr, (char*)CMSG_DATA(cmsghdr), descriptor_data, ctx);
924 
925 	*offset += req_space;
926 }
from_zval_write_control_array(const zval * arr,char * msghdr_c,ser_context * ctx)927 static void from_zval_write_control_array(const zval *arr, char *msghdr_c, ser_context *ctx)
928 {
929 	char				buf[sizeof("element #4294967295")];
930 	char				*bufp = buf;
931 	zval				*elem;
932 	uint32_t			i = 0;
933 	int					num_elems;
934 	void				*control_buf;
935 	zend_llist_element	*alloc;
936 	size_t				control_len,
937 						cur_offset;
938 	struct msghdr		*msg = (struct msghdr*)msghdr_c;
939 
940 	if (Z_TYPE_P(arr) != IS_ARRAY) {
941 		do_from_zval_err(ctx, "%s", "expected an array here");
942 		return;
943 	}
944 
945 	num_elems = zend_hash_num_elements(Z_ARRVAL_P(arr));
946 	if (num_elems == 0) {
947 		return;
948 	}
949 
950 	/* estimate each message at 20 bytes */
951 	control_buf	= accounted_safe_ecalloc(num_elems, CMSG_SPACE(20), 0, ctx);
952 	alloc		= ctx->allocations.tail;
953 	control_len = (size_t)num_elems * CMSG_SPACE(20);
954 	cur_offset	= 0;
955 
956 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), elem) {
957 		if (ctx->err.has_error) {
958 			break;
959 		}
960 
961 		if ((size_t)snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
962 			memcpy(buf, "element", sizeof("element"));
963 		}
964 		zend_llist_add_element(&ctx->keys, &bufp);
965 
966 		from_zval_write_control(elem, &control_buf, alloc, &control_len, &cur_offset, ctx);
967 
968 		zend_llist_remove_tail(&ctx->keys);
969 	} ZEND_HASH_FOREACH_END();
970 
971 	msg->msg_control = control_buf;
972 	msg->msg_controllen = cur_offset; /* not control_len, which may be larger */
973 }
to_zval_read_cmsg_data(const char * cmsghdr_c,zval * zv,res_context * ctx)974 static void to_zval_read_cmsg_data(const char *cmsghdr_c, zval *zv, res_context *ctx)
975 {
976 	const struct cmsghdr	*cmsg = (const struct cmsghdr *)cmsghdr_c;
977 	ancillary_reg_entry		*entry;
978 	size_t					len,
979 							*len_p = &len;
980 
981 	entry = get_ancillary_reg_entry(cmsg->cmsg_level, cmsg->cmsg_type);
982 	if (entry == NULL) {
983 		do_to_zval_err(ctx, "cmsghdr with level %d and type %d not supported",
984 				cmsg->cmsg_level, cmsg->cmsg_type);
985 		return;
986 	}
987 	if (CMSG_LEN(entry->size) > cmsg->cmsg_len) {
988 		do_to_zval_err(ctx, "the cmsghdr structure is unexpectedly small; "
989 				"expected a length of at least " ZEND_LONG_FMT ", but got " ZEND_LONG_FMT,
990 				(zend_long)CMSG_LEN(entry->size), (zend_long)cmsg->cmsg_len);
991 		return;
992 	}
993 
994 	len = (size_t)cmsg->cmsg_len; /* use another var because type of cmsg_len varies */
995 
996 	if (zend_hash_str_add_ptr(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN) - 1, len_p) == NULL) {
997 		do_to_zval_err(ctx, "%s", "could not set parameter " KEY_CMSG_LEN);
998 		return;
999 	}
1000 
1001 	entry->to_array((const char *)CMSG_DATA(cmsg), zv, ctx);
1002 
1003 	zend_hash_str_del(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN) - 1);
1004 }
to_zval_read_control(const char * cmsghdr_c,zval * zv,res_context * ctx)1005 static void to_zval_read_control(const char *cmsghdr_c, zval *zv, res_context *ctx)
1006 {
1007 	/* takes a cmsghdr, not a msghdr like from_zval_write_control */
1008 	static const field_descriptor descriptors[] = {
1009 			{"level", sizeof("level"), 0, offsetof(struct cmsghdr, cmsg_level), 0, to_zval_read_int},
1010 			{"type", sizeof("type"), 0, offsetof(struct cmsghdr, cmsg_type), 0, to_zval_read_int},
1011 			{"data", sizeof("data"), 0, 0 /* cmsghdr passed */, 0, to_zval_read_cmsg_data},
1012 			{0}
1013 	};
1014 
1015 	array_init_size(zv, 3);
1016 	to_zval_read_aggregation(cmsghdr_c, zv, descriptors, ctx);
1017 }
to_zval_read_control_array(const char * msghdr_c,zval * zv,res_context * ctx)1018 static void to_zval_read_control_array(const char *msghdr_c, zval *zv, res_context *ctx)
1019 {
1020 	struct msghdr	*msg = (struct msghdr *)msghdr_c;
1021 	struct cmsghdr	*cmsg;
1022 	char			buf[sizeof("element #4294967295")];
1023 	char			*bufp = buf;
1024 	uint32_t		i = 1;
1025 
1026 	array_init(zv);
1027 
1028 	for (cmsg = CMSG_FIRSTHDR(msg);
1029 			cmsg != NULL && !ctx->err.has_error;
1030 			cmsg = CMSG_NXTHDR(msg, cmsg)) {
1031 		zval *elem, tmp;
1032 
1033 		ZVAL_NULL(&tmp);
1034 		elem = zend_hash_next_index_insert(Z_ARRVAL_P(zv), &tmp);
1035 
1036 		if ((size_t)snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
1037 			memcpy(buf, "element", sizeof("element"));
1038 		}
1039 		zend_llist_add_element(&ctx->keys, &bufp);
1040 
1041 		to_zval_read_control((const char *)cmsg, elem, ctx);
1042 
1043 		zend_llist_remove_tail(&ctx->keys);
1044 	}
1045 }
1046 
1047 /* CONVERSIONS for msghdr */
from_zval_write_name(const zval * zname_arr,char * msghdr_c,ser_context * ctx)1048 static void from_zval_write_name(const zval *zname_arr, char *msghdr_c, ser_context *ctx)
1049 {
1050 	struct sockaddr	*sockaddr;
1051 	socklen_t		sockaddr_len;
1052 	struct msghdr	*msghdr = (struct msghdr *)msghdr_c;
1053 
1054 	from_zval_write_sockaddr_aux(zname_arr, &sockaddr, &sockaddr_len, ctx);
1055 
1056 	msghdr->msg_name = sockaddr;
1057 	msghdr->msg_namelen = sockaddr_len;
1058 }
to_zval_read_name(const char * sockaddr_p,zval * zv,res_context * ctx)1059 static void to_zval_read_name(const char *sockaddr_p, zval *zv, res_context *ctx)
1060 {
1061 	void *name = (void*)*(void**)sockaddr_p;
1062 	if (name == NULL) {
1063 		ZVAL_NULL(zv);
1064 	} else {
1065 		to_zval_read_sockaddr_aux(name, zv, ctx);
1066 	}
1067 }
from_zval_write_msghdr_buffer_size(const zval * elem,char * msghdr_c,ser_context * ctx)1068 static void from_zval_write_msghdr_buffer_size(const zval *elem, char *msghdr_c, ser_context *ctx)
1069 {
1070 	zend_long lval;
1071 	struct msghdr *msghdr = (struct msghdr *)msghdr_c;
1072 
1073 	lval = from_zval_integer_common(elem, ctx);
1074 	if (ctx->err.has_error) {
1075 		return;
1076 	}
1077 
1078 	if (lval < 0 || (zend_ulong)lval > MAX_USER_BUFF_SIZE) {
1079 		do_from_zval_err(ctx, "the buffer size must be between 1 and " ZEND_LONG_FMT "; "
1080 				"given " ZEND_LONG_FMT, (zend_long)MAX_USER_BUFF_SIZE, lval);
1081 		return;
1082 	}
1083 
1084 	msghdr->msg_iovlen = 1;
1085 	msghdr->msg_iov = accounted_emalloc(sizeof(*msghdr->msg_iov) * 1, ctx);
1086 	msghdr->msg_iov[0].iov_base = accounted_emalloc((size_t)lval, ctx);
1087 	msghdr->msg_iov[0].iov_len = (size_t)lval;
1088 }
from_zval_write_iov_array_aux(zval * elem,unsigned i,void ** args,ser_context * ctx)1089 static void from_zval_write_iov_array_aux(zval *elem, unsigned i, void **args, ser_context *ctx)
1090 {
1091 	struct msghdr	*msg = args[0];
1092 	zend_string     *str, *tmp_str;
1093 
1094 	str = zval_get_tmp_string(elem, &tmp_str);
1095 
1096 	msg->msg_iov[i - 1].iov_base = accounted_emalloc(ZSTR_LEN(str), ctx);
1097 	msg->msg_iov[i - 1].iov_len = ZSTR_LEN(str);
1098 	memcpy(msg->msg_iov[i - 1].iov_base, ZSTR_VAL(str), ZSTR_LEN(str));
1099 
1100 	zend_tmp_string_release(tmp_str);
1101 }
from_zval_write_iov_array(const zval * arr,char * msghdr_c,ser_context * ctx)1102 static void from_zval_write_iov_array(const zval *arr, char *msghdr_c, ser_context *ctx)
1103 {
1104 	int				num_elem;
1105 	struct msghdr	*msg = (struct msghdr*)msghdr_c;
1106 
1107 	if (Z_TYPE_P(arr) != IS_ARRAY) {
1108 		do_from_zval_err(ctx, "%s", "expected an array here");
1109 		return;
1110 	}
1111 
1112 	num_elem = zend_hash_num_elements(Z_ARRVAL_P(arr));
1113 	if (num_elem == 0) {
1114 		return;
1115 	}
1116 
1117 	msg->msg_iov = accounted_safe_ecalloc(num_elem, sizeof *msg->msg_iov, 0, ctx);
1118 	msg->msg_iovlen = (size_t)num_elem;
1119 
1120 	from_array_iterate(arr, from_zval_write_iov_array_aux, (void**)&msg, ctx);
1121 }
from_zval_write_controllen(const zval * elem,char * msghdr_c,ser_context * ctx)1122 static void from_zval_write_controllen(const zval *elem, char *msghdr_c, ser_context *ctx)
1123 {
1124 	struct msghdr *msghdr = (struct msghdr *)msghdr_c;
1125 	uint32_t len = 0; /* Silence compiler warning */
1126 
1127 	/* controllen should be an unsigned with at least 32-bit. Let's assume
1128 	 * this least common denominator
1129 	 */
1130 	from_zval_write_uint32(elem, (char*)&len, ctx);
1131 	if (ctx->err.has_error) {
1132 		return;
1133 	}
1134 	if (len == 0) {
1135 		do_from_zval_err(ctx, "controllen cannot be 0");
1136 		return;
1137 	}
1138 	msghdr->msg_control = accounted_emalloc(len, ctx);
1139 	msghdr->msg_controllen = len;
1140 }
from_zval_write_msghdr_send(const zval * container,char * msghdr_c,ser_context * ctx)1141 void from_zval_write_msghdr_send(const zval *container, char *msghdr_c, ser_context *ctx)
1142 {
1143 	static const field_descriptor descriptors[] = {
1144 			{"name", sizeof("name"), 0, 0, from_zval_write_name, 0},
1145 			{"iov", sizeof("iov"), 0, 0, from_zval_write_iov_array, 0},
1146 			{"control", sizeof("control"), 0, 0, from_zval_write_control_array, 0},
1147 			{0}
1148 	};
1149 
1150 	from_zval_write_aggregation(container, msghdr_c, descriptors, ctx);
1151 }
from_zval_write_msghdr_recv(const zval * container,char * msghdr_c,ser_context * ctx)1152 void from_zval_write_msghdr_recv(const zval *container, char *msghdr_c, ser_context *ctx)
1153 {
1154 	/* zval to struct msghdr, version for recvmsg(). It differs from the version
1155 	 * for sendmsg() in that it:
1156 	 * 	- has a buffer_size instead of an iov array;
1157 	 * 	- has no control element; has a controllen element instead
1158 	 * struct msghdr {
1159 	 *    void *msg_name;
1160 	 *    socklen_t msg_namelen;
1161 	 *    struct iovec *msg_iov;
1162 	 *    size_t msg_iovlen;
1163 	 *    void *msg_control;
1164 	 *    size_t msg_controllen; //can also be socklen_t
1165 	 *    int msg_flags;
1166 	 * };
1167 	 */
1168 	static const field_descriptor descriptors[] = {
1169 			{"name", sizeof("name"), 0, 0, from_zval_write_name, 0},
1170 			{"buffer_size", sizeof("buffer_size"), 0, 0, from_zval_write_msghdr_buffer_size, 0},
1171 			{"controllen", sizeof("controllen"), 1, 0, from_zval_write_controllen, 0},
1172 			{0}
1173 	};
1174 	struct msghdr 	*msghdr = (struct msghdr *)msghdr_c;
1175 	const int		falsev = 0,
1176 					*falsevp = &falsev;
1177 
1178 	if (zend_hash_str_add_ptr(&ctx->params, KEY_FILL_SOCKADDR, sizeof(KEY_FILL_SOCKADDR) - 1, (void *)falsevp) == NULL) {
1179 		do_from_zval_err(ctx, "could not add fill_sockaddr; this is a bug");
1180 		return;
1181 	}
1182 
1183 	from_zval_write_aggregation(container, msghdr_c, descriptors, ctx);
1184 
1185 	zend_hash_str_del(&ctx->params, KEY_FILL_SOCKADDR, sizeof(KEY_FILL_SOCKADDR) - 1);
1186 	if (ctx->err.has_error) {
1187 		return;
1188 	}
1189 
1190 	if (msghdr->msg_iovlen == 0) {
1191 		msghdr->msg_iovlen = 1;
1192 		msghdr->msg_iov = accounted_emalloc(sizeof(*msghdr->msg_iov) * 1, ctx);
1193 		msghdr->msg_iov[0].iov_base = accounted_emalloc((size_t)DEFAULT_BUFF_SIZE, ctx);
1194 		msghdr->msg_iov[0].iov_len = (size_t)DEFAULT_BUFF_SIZE;
1195 	}
1196 }
1197 
to_zval_read_iov(const char * msghdr_c,zval * zv,res_context * ctx)1198 static void to_zval_read_iov(const char *msghdr_c, zval *zv, res_context *ctx)
1199 {
1200 	const struct msghdr	*msghdr = (const struct msghdr *)msghdr_c;
1201 	size_t				iovlen = msghdr->msg_iovlen;
1202 	ssize_t				*recvmsg_ret,
1203 						bytes_left;
1204 	uint32_t			i;
1205 
1206 	if (iovlen > UINT_MAX) {
1207 		do_to_zval_err(ctx, "unexpectedly large value for iov_len: %lu",
1208 				(unsigned long)iovlen);
1209 	}
1210 	array_init_size(zv, (uint32_t)iovlen);
1211 
1212 	if ((recvmsg_ret = zend_hash_str_find_ptr(&ctx->params, KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET) - 1)) == NULL) {
1213 		do_to_zval_err(ctx, "recvmsg_ret not found in params. This is a bug");
1214 		return;
1215 	}
1216 	bytes_left = *recvmsg_ret;
1217 
1218 	for (i = 0; bytes_left > 0 && i < (uint32_t)iovlen; i++) {
1219 		zval elem;
1220 		size_t len = MIN(msghdr->msg_iov[i].iov_len, (size_t)bytes_left);
1221 		zend_string	*buf = zend_string_alloc(len, 0);
1222 
1223 		memcpy(ZSTR_VAL(buf), msghdr->msg_iov[i].iov_base, ZSTR_LEN(buf));
1224 		ZSTR_VAL(buf)[ZSTR_LEN(buf)] = '\0';
1225 
1226 		ZVAL_NEW_STR(&elem, buf);
1227 		add_next_index_zval(zv, &elem);
1228 		bytes_left -= len;
1229 	}
1230 }
to_zval_read_msghdr(const char * msghdr_c,zval * zv,res_context * ctx)1231 void to_zval_read_msghdr(const char *msghdr_c, zval *zv, res_context *ctx)
1232 {
1233 	static const field_descriptor descriptors[] = {
1234 			{"name", sizeof("name"), 0, offsetof(struct msghdr, msg_name), 0, to_zval_read_name},
1235 			{"control", sizeof("control"), 0, 0, 0, to_zval_read_control_array},
1236 			{"iov", sizeof("iov"), 0, 0, 0, to_zval_read_iov},
1237 			{"flags", sizeof("flags"), 0, offsetof(struct msghdr, msg_flags), 0, to_zval_read_int},
1238 			{0}
1239 	};
1240 
1241 	array_init_size(zv, 4);
1242 
1243 	to_zval_read_aggregation(msghdr_c, zv, descriptors, ctx);
1244 }
1245 
1246 #if defined(IPV6_PKTINFO) && defined(HAVE_IPV6)
1247 /* CONVERSIONS for if_index */
from_zval_write_ifindex(const zval * zv,char * uinteger,ser_context * ctx)1248 static void from_zval_write_ifindex(const zval *zv, char *uinteger, ser_context *ctx)
1249 {
1250 	unsigned ret = 0;
1251 
1252 	if (Z_TYPE_P(zv) == IS_LONG) {
1253 		if (Z_LVAL_P(zv) < 0 || (zend_ulong)Z_LVAL_P(zv) > UINT_MAX) { /* allow 0 (unspecified interface) */
1254 			do_from_zval_err(ctx, "the interface index cannot be negative or "
1255 					"larger than %u; given " ZEND_LONG_FMT, UINT_MAX, Z_LVAL_P(zv));
1256 		} else {
1257 			ret = (unsigned)Z_LVAL_P(zv);
1258 		}
1259 	} else {
1260 		zend_string *str, *tmp_str;
1261 
1262 		str = zval_get_tmp_string((zval *) zv, &tmp_str);
1263 
1264 #ifdef HAVE_IF_NAMETOINDEX
1265 		ret = if_nametoindex(ZSTR_VAL(str));
1266 		if (ret == 0) {
1267 			do_from_zval_err(ctx, "no interface with name \"%s\" could be found", ZSTR_VAL(str));
1268 		}
1269 #elif defined(SIOCGIFINDEX)
1270 		{
1271 			struct ifreq ifr;
1272 			if (strlcpy(ifr.ifr_name, ZSTR_VAL(str), sizeof(ifr.ifr_name))
1273 					>= sizeof(ifr.ifr_name)) {
1274 				do_from_zval_err(ctx, "the interface name \"%s\" is too large ", ZSTR_VAL(str));
1275 			} else if (ioctl(ctx->sock->bsd_socket, SIOCGIFINDEX, &ifr) < 0) {
1276 				if (errno == ENODEV) {
1277 					do_from_zval_err(ctx, "no interface with name \"%s\" could be "
1278 							"found", ZSTR_VAL(str));
1279 				} else {
1280 					do_from_zval_err(ctx, "error fetching interface index for "
1281 							"interface with name \"%s\" (errno %d)",
1282 							ZSTR_VAL(str), errno);
1283 				}
1284 			} else {
1285 				ret = (unsigned)ifr.ifr_ifindex;
1286 			}
1287 		}
1288 #else
1289 		do_from_zval_err(ctx,
1290 				"this platform does not support looking up an interface by "
1291 				"name, an integer interface index must be supplied instead");
1292 #endif
1293 
1294 		zend_tmp_string_release(tmp_str);
1295 	}
1296 
1297 	if (!ctx->err.has_error) {
1298 		memcpy(uinteger, &ret, sizeof(ret));
1299 	}
1300 }
1301 
1302 /* CONVERSIONS for struct in6_pktinfo */
1303 static const field_descriptor descriptors_in6_pktinfo[] = {
1304 		{"addr", sizeof("addr"), 1, offsetof(struct in6_pktinfo, ipi6_addr), from_zval_write_sin6_addr, to_zval_read_sin6_addr},
1305 		{"ifindex", sizeof("ifindex"), 1, offsetof(struct in6_pktinfo, ipi6_ifindex), from_zval_write_ifindex, to_zval_read_unsigned},
1306 		{0}
1307 };
from_zval_write_in6_pktinfo(const zval * container,char * in6_pktinfo_c,ser_context * ctx)1308 void from_zval_write_in6_pktinfo(const zval *container, char *in6_pktinfo_c, ser_context *ctx)
1309 {
1310 	from_zval_write_aggregation(container, in6_pktinfo_c, descriptors_in6_pktinfo, ctx);
1311 }
to_zval_read_in6_pktinfo(const char * data,zval * zv,res_context * ctx)1312 void to_zval_read_in6_pktinfo(const char *data, zval *zv, res_context *ctx)
1313 {
1314 	array_init_size(zv, 2);
1315 
1316 	to_zval_read_aggregation(data, zv, descriptors_in6_pktinfo, ctx);
1317 }
1318 #endif
1319 
1320 /* CONVERSIONS for struct ucred */
1321 #if defined(SO_PASSCRED) || defined(LOCAL_CREDS_PERSISTENT) || defined(LOCAL_CREDS)
1322 static const field_descriptor descriptors_ucred[] = {
1323 #if defined(LOCAL_CREDS_PERSISTENT)
1324 		{"pid", sizeof("pid"), 1, offsetof(struct sockcred2, sc_pid), from_zval_write_pid_t, to_zval_read_pid_t},
1325 		{"uid", sizeof("uid"), 1, offsetof(struct sockcred2, sc_euid), from_zval_write_uid_t, to_zval_read_uid_t},
1326 		/* the type gid_t is the same as uid_t: */
1327 		{"gid", sizeof("gid"), 1, offsetof(struct sockcred2, sc_egid), from_zval_write_uid_t, to_zval_read_uid_t},
1328 #elif defined(LOCAL_CREDS)
1329 		{"pid", sizeof("pid"), 1, offsetof(struct sockcred, sc_pid), from_zval_write_pid_t, to_zval_read_pid_t},
1330 		{"uid", sizeof("uid"), 1, offsetof(struct sockcred, sc_euid), from_zval_write_uid_t, to_zval_read_uid_t},
1331 		/* the type gid_t is the same as uid_t: */
1332 		{"gid", sizeof("gid"), 1, offsetof(struct sockcred, sc_egid), from_zval_write_uid_t, to_zval_read_uid_t},
1333 #elif defined(HAVE_STRUCT_CMSGCRED)
1334 		{"pid", sizeof("pid"), 1, offsetof(struct cmsgcred, cmcred_pid), from_zval_write_pid_t, to_zval_read_pid_t},
1335 		{"uid", sizeof("uid"), 1, offsetof(struct cmsgcred, cmcred_uid), from_zval_write_uid_t, to_zval_read_uid_t},
1336 		/* assume the type gid_t is the same as uid_t: */
1337 		{"gid", sizeof("gid"), 1, offsetof(struct cmsgcred, cmcred_gid), from_zval_write_uid_t, to_zval_read_uid_t},
1338 #elif defined(SO_PASSCRED)
1339 		{"pid", sizeof("pid"), 1, offsetof(struct ucred, pid), from_zval_write_pid_t, to_zval_read_pid_t},
1340 		{"uid", sizeof("uid"), 1, offsetof(struct ucred, uid), from_zval_write_uid_t, to_zval_read_uid_t},
1341 		/* assume the type gid_t is the same as uid_t: */
1342 		{"gid", sizeof("gid"), 1, offsetof(struct ucred, gid), from_zval_write_uid_t, to_zval_read_uid_t},
1343 #endif
1344 		{0}
1345 };
from_zval_write_ucred(const zval * container,char * ucred_c,ser_context * ctx)1346 void from_zval_write_ucred(const zval *container, char *ucred_c, ser_context *ctx)
1347 {
1348 	from_zval_write_aggregation(container, ucred_c, descriptors_ucred, ctx);
1349 }
to_zval_read_ucred(const char * data,zval * zv,res_context * ctx)1350 void to_zval_read_ucred(const char *data, zval *zv, res_context *ctx)
1351 {
1352 	array_init_size(zv, 3);
1353 
1354 	to_zval_read_aggregation(data, zv, descriptors_ucred, ctx);
1355 }
1356 #endif
1357 
1358 /* CONVERSIONS for SCM_RIGHTS */
1359 #ifdef SCM_RIGHTS
calculate_scm_rights_space(const zval * arr,ser_context * ctx)1360 size_t calculate_scm_rights_space(const zval *arr, ser_context *ctx)
1361 {
1362 	int num_elems;
1363 
1364 	if (Z_TYPE_P(arr) != IS_ARRAY) {
1365 		do_from_zval_err(ctx, "%s", "expected an array here");
1366 		return (size_t)-1;
1367 	}
1368 
1369 	num_elems = zend_hash_num_elements(Z_ARRVAL_P(arr));
1370 	if (num_elems == 0) {
1371 		do_from_zval_err(ctx, "%s", "expected at least one element in this array");
1372 		return (size_t)-1;
1373 	}
1374 
1375 	return zend_hash_num_elements(Z_ARRVAL_P(arr)) * sizeof(int);
1376 }
from_zval_write_fd_array_aux(zval * elem,unsigned i,void ** args,ser_context * ctx)1377 static void from_zval_write_fd_array_aux(zval *elem, unsigned i, void **args, ser_context *ctx)
1378 {
1379 	int *iarr = args[0];
1380 
1381 	if (Z_TYPE_P(elem) == IS_OBJECT && Z_OBJCE_P(elem) == socket_ce) {
1382 		php_socket *sock = Z_SOCKET_P(elem);
1383 		if (IS_INVALID_SOCKET(sock)) {
1384 			do_from_zval_err(ctx, "socket is already closed");
1385 			return;
1386 		}
1387 
1388 		iarr[i] = sock->bsd_socket;
1389 		return;
1390 	}
1391 
1392 	if (Z_TYPE_P(elem) == IS_RESOURCE) {
1393 		php_stream *stream;
1394 
1395 		stream = (php_stream *)zend_fetch_resource2_ex(elem, NULL, php_file_le_stream(), php_file_le_pstream());
1396 		if (stream == NULL) {
1397 			do_from_zval_err(ctx, "resource is not a stream");
1398 			return;
1399 		}
1400 
1401 		if (php_stream_cast(stream, PHP_STREAM_AS_FD, (void **)&iarr[i - 1], REPORT_ERRORS) == FAILURE) {
1402 			do_from_zval_err(ctx, "cast stream to file descriptor failed");
1403 			return;
1404 		}
1405 	} else {
1406 		do_from_zval_err(ctx, "expected a Socket object or a stream resource");
1407 	}
1408 }
from_zval_write_fd_array(const zval * arr,char * int_arr,ser_context * ctx)1409 void from_zval_write_fd_array(const zval *arr, char *int_arr, ser_context *ctx)
1410 {
1411 	if (Z_TYPE_P(arr) != IS_ARRAY) {
1412 		do_from_zval_err(ctx, "%s", "expected an array here");
1413 		return;
1414 	}
1415 
1416 	from_array_iterate(arr, &from_zval_write_fd_array_aux, (void**)&int_arr, ctx);
1417 }
to_zval_read_fd_array(const char * data,zval * zv,res_context * ctx)1418 void to_zval_read_fd_array(const char *data, zval *zv, res_context *ctx)
1419 {
1420 	size_t			*cmsg_len;
1421 	int				num_elems,
1422 					i;
1423 	struct cmsghdr	*dummy_cmsg = 0;
1424 	size_t			data_offset;
1425 
1426 	data_offset = (unsigned char *)CMSG_DATA(dummy_cmsg)
1427 			- (unsigned char *)dummy_cmsg;
1428 
1429 	if ((cmsg_len = zend_hash_str_find_ptr(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN) - 1)) == NULL) {
1430 		do_to_zval_err(ctx, "could not get value of parameter " KEY_CMSG_LEN);
1431 		return;
1432 	}
1433 
1434 	if (*cmsg_len < data_offset) {
1435 		do_to_zval_err(ctx, "length of cmsg is smaller than its data member "
1436 				"offset (" ZEND_LONG_FMT " vs " ZEND_LONG_FMT ")", (zend_long)*cmsg_len, (zend_long)data_offset);
1437 		return;
1438 	}
1439 	num_elems = (*cmsg_len - data_offset) / sizeof(int);
1440 
1441 	array_init_size(zv, num_elems);
1442 
1443 	for (i = 0; i < num_elems; i++) {
1444 		zval		elem;
1445 		int			fd;
1446 		struct stat	statbuf;
1447 
1448 		fd = *((int *)data + i);
1449 
1450 		/* determine whether we have a socket */
1451 		if (fstat(fd, &statbuf) == -1) {
1452 			do_to_zval_err(ctx, "error creating resource for received file "
1453 					"descriptor %d: fstat() call failed with errno %d", fd, errno);
1454 			return;
1455 		}
1456 		if (S_ISSOCK(statbuf.st_mode)) {
1457 			object_init_ex(&elem, socket_ce);
1458 			php_socket *sock = Z_SOCKET_P(&elem);
1459 
1460 			socket_import_file_descriptor(fd, sock);
1461 		} else {
1462 			php_stream *stream = php_stream_fopen_from_fd(fd, "rw", NULL);
1463 			php_stream_to_zval(stream, &elem);
1464 		}
1465 
1466 		add_next_index_zval(zv, &elem);
1467 	}
1468 }
1469 #endif
1470 
1471 /* ENTRY POINT for conversions */
free_from_zval_allocation(void * alloc_ptr_ptr)1472 static void free_from_zval_allocation(void *alloc_ptr_ptr)
1473 {
1474 	efree(*(void**)alloc_ptr_ptr);
1475 }
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)1476 void *from_zval_run_conversions(const zval			*container,
1477 								php_socket			*sock,
1478 								from_zval_write_field	*writer,
1479 								size_t				struct_size,
1480 								const char			*top_name,
1481 								zend_llist			**allocations /* out */,
1482 								struct err_s			*err /* in/out */)
1483 {
1484 	ser_context ctx;
1485 	char *structure;
1486 
1487 	*allocations = NULL;
1488 
1489 	if (err->has_error) {
1490 		return NULL;
1491 	}
1492 
1493 	memset(&ctx, 0, sizeof(ctx));
1494 	zend_hash_init(&ctx.params, 8, NULL, NULL, 0);
1495 	zend_llist_init(&ctx.keys, sizeof(const char *), NULL, 0);
1496 	zend_llist_init(&ctx.allocations, sizeof(void *), &free_from_zval_allocation, 0);
1497 	ctx.sock = sock;
1498 
1499 	structure = ecalloc(1, struct_size);
1500 
1501 	zend_llist_add_element(&ctx.keys, &top_name);
1502 	zend_llist_add_element(&ctx.allocations, &structure);
1503 
1504 	/* main call */
1505 	writer(container, structure, &ctx);
1506 
1507 	if (ctx.err.has_error) {
1508 		zend_llist_destroy(&ctx.allocations); /* deallocates structure as well */
1509 		structure = NULL;
1510 		*err = ctx.err;
1511 	} else {
1512 		*allocations = emalloc(sizeof **allocations);
1513 		**allocations = ctx.allocations;
1514 	}
1515 
1516 	zend_llist_destroy(&ctx.keys);
1517 	zend_hash_destroy(&ctx.params);
1518 
1519 	return structure;
1520 }
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)1521 zval *to_zval_run_conversions(const char *structure,
1522 							  to_zval_read_field *reader,
1523 							  const char *top_name,
1524 							  const struct key_value *key_value_pairs,
1525 							  struct err_s *err, zval *zv)
1526 {
1527 	res_context				ctx;
1528 	const struct key_value	*kv;
1529 
1530 	if (err->has_error) {
1531 		return NULL;
1532 	}
1533 
1534 	memset(&ctx, 0, sizeof(ctx));
1535 	zend_llist_init(&ctx.keys, sizeof(const char *), NULL, 0);
1536 	zend_llist_add_element(&ctx.keys, &top_name);
1537 
1538 	zend_hash_init(&ctx.params, 8, NULL, NULL, 0);
1539 	for (kv = key_value_pairs; kv->key != NULL; kv++) {
1540 		zend_hash_str_update_ptr(&ctx.params, kv->key, kv->key_size - 1, kv->value);
1541 	}
1542 
1543 	ZVAL_NULL(zv);
1544 	/* main call */
1545 	reader(structure, zv, &ctx);
1546 
1547 	if (ctx.err.has_error) {
1548 		zval_ptr_dtor(zv);
1549 		ZVAL_UNDEF(zv);
1550 		*err = ctx.err;
1551 	}
1552 
1553 	zend_llist_destroy(&ctx.keys);
1554 	zend_hash_destroy(&ctx.params);
1555 
1556 	return Z_ISUNDEF_P(zv)? NULL : zv;
1557 }
1558