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