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