xref: /PHP-7.2/ext/sockets/multicast.c (revision 900bdcbd)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Gustavo Lopes    <cataphract@php.net>                       |
16    +----------------------------------------------------------------------+
17  */
18 
19 /* $Id$ */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "php.h"
26 
27 #include "php_network.h"
28 #ifdef PHP_WIN32
29 # include "windows_common.h"
30 #else
31 #include <sys/socket.h>
32 #include <sys/ioctl.h>
33 #include <net/if.h>
34 #ifdef HAVE_SYS_SOCKIO_H
35 #include <sys/sockio.h>
36 #endif
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #endif
40 
41 #include "php_sockets.h"
42 #include "multicast.h"
43 #include "sockaddr_conv.h"
44 #include "main/php_network.h"
45 
46 
47 enum source_op {
48 	JOIN_SOURCE,
49 	LEAVE_SOURCE,
50 	BLOCK_SOURCE,
51 	UNBLOCK_SOURCE
52 };
53 
54 static int _php_mcast_join_leave(php_socket *sock, int level, struct sockaddr *group, socklen_t group_len, unsigned int if_index, int join);
55 #ifdef HAS_MCAST_EXT
56 static int _php_mcast_source_op(php_socket *sock, int level, struct sockaddr *group, socklen_t group_len, struct sockaddr *source, socklen_t source_len, unsigned int if_index, enum source_op sop);
57 #endif
58 
59 #ifdef RFC3678_API
60 static int _php_source_op_to_rfc3678_op(enum source_op sop);
61 #elif HAS_MCAST_EXT
62 static const char *_php_source_op_to_string(enum source_op sop);
63 static int _php_source_op_to_ipv4_op(enum source_op sop);
64 #endif
65 
php_string_to_if_index(const char * val,unsigned * out)66 int php_string_to_if_index(const char *val, unsigned *out)
67 {
68 #if HAVE_IF_NAMETOINDEX
69 	unsigned int ind;
70 
71 	ind = if_nametoindex(val);
72 	if (ind == 0) {
73 		php_error_docref(NULL, E_WARNING,
74 			"no interface with name \"%s\" could be found", val);
75 		return FAILURE;
76 	} else {
77 		*out = ind;
78 		return SUCCESS;
79 	}
80 #else
81 	php_error_docref(NULL, E_WARNING,
82 			"this platform does not support looking up an interface by "
83 			"name, an integer interface index must be supplied instead");
84 	return FAILURE;
85 #endif
86 }
87 
php_get_if_index_from_zval(zval * val,unsigned * out)88 static int php_get_if_index_from_zval(zval *val, unsigned *out)
89 {
90 	int ret;
91 
92 	if (Z_TYPE_P(val) == IS_LONG) {
93 		if (Z_LVAL_P(val) < 0 || (zend_ulong)Z_LVAL_P(val) > UINT_MAX) {
94 			php_error_docref(NULL, E_WARNING,
95 				"the interface index cannot be negative or larger than %u;"
96 				" given " ZEND_LONG_FMT, UINT_MAX, Z_LVAL_P(val));
97 			ret = FAILURE;
98 		} else {
99 			*out = Z_LVAL_P(val);
100 			ret = SUCCESS;
101 		}
102 	} else {
103 		if (Z_REFCOUNTED_P(val)) {
104 			Z_ADDREF_P(val);
105 		}
106 		convert_to_string_ex(val);
107 		ret = php_string_to_if_index(Z_STRVAL_P(val), out);
108 		zval_ptr_dtor(val);
109 	}
110 
111 	return ret;
112 }
113 
114 
115 
php_get_if_index_from_array(const HashTable * ht,const char * key,php_socket * sock,unsigned int * if_index)116 static int php_get_if_index_from_array(const HashTable *ht, const char *key,
117 	php_socket *sock, unsigned int *if_index)
118 {
119 	zval *val;
120 
121 	if ((val = zend_hash_str_find(ht, key, strlen(key))) == NULL) {
122 		*if_index = 0; /* default: 0 */
123 		return SUCCESS;
124 	}
125 
126 	return php_get_if_index_from_zval(val, if_index);
127 }
128 
php_get_address_from_array(const HashTable * ht,const char * key,php_socket * sock,php_sockaddr_storage * ss,socklen_t * ss_len)129 static int php_get_address_from_array(const HashTable *ht, const char *key,
130 	php_socket *sock, php_sockaddr_storage *ss, socklen_t *ss_len)
131 {
132 	zval *val;
133 
134 	if ((val = zend_hash_str_find(ht, key, strlen(key))) == NULL) {
135 		php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", key);
136 		return FAILURE;
137 	}
138 	if (Z_REFCOUNTED_P(val)) {
139 		Z_ADDREF_P(val);
140 	}
141 	convert_to_string_ex(val);
142 	if (!php_set_inet46_addr(ss, ss_len, Z_STRVAL_P(val), sock)) {
143 		zval_ptr_dtor(val);
144 		return FAILURE;
145 	}
146 	zval_ptr_dtor(val);
147 	return SUCCESS;
148 }
149 
php_do_mcast_opt(php_socket * php_sock,int level,int optname,zval * arg4)150 static int php_do_mcast_opt(php_socket *php_sock, int level, int optname, zval *arg4)
151 {
152 	HashTable		 		*opt_ht;
153 	unsigned int			if_index;
154 	int						retval;
155 	int (*mcast_req_fun)(php_socket *, int, struct sockaddr *, socklen_t,
156 		unsigned);
157 #ifdef HAS_MCAST_EXT
158 	int (*mcast_sreq_fun)(php_socket *, int, struct sockaddr *, socklen_t,
159 		struct sockaddr *, socklen_t, unsigned);
160 #endif
161 
162 	switch (optname) {
163 	case PHP_MCAST_JOIN_GROUP:
164 		mcast_req_fun = &php_mcast_join;
165 		goto mcast_req_fun;
166 	case PHP_MCAST_LEAVE_GROUP:
167 		{
168 			php_sockaddr_storage	group = {0};
169 			socklen_t				glen;
170 
171 			mcast_req_fun = &php_mcast_leave;
172 mcast_req_fun:
173 			convert_to_array_ex(arg4);
174 			opt_ht = Z_ARRVAL_P(arg4);
175 
176 			if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
177 				&glen) == FAILURE) {
178 					return FAILURE;
179 			}
180 			if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
181 				&if_index) == FAILURE) {
182 					return FAILURE;
183 			}
184 
185 			retval = mcast_req_fun(php_sock, level, (struct sockaddr*)&group,
186 				glen, if_index);
187 			break;
188 		}
189 
190 #ifdef HAS_MCAST_EXT
191 	case PHP_MCAST_BLOCK_SOURCE:
192 		mcast_sreq_fun = &php_mcast_block_source;
193 		goto mcast_sreq_fun;
194 	case PHP_MCAST_UNBLOCK_SOURCE:
195 		mcast_sreq_fun = &php_mcast_unblock_source;
196 		goto mcast_sreq_fun;
197 	case PHP_MCAST_JOIN_SOURCE_GROUP:
198 		mcast_sreq_fun = &php_mcast_join_source;
199 		goto mcast_sreq_fun;
200 	case PHP_MCAST_LEAVE_SOURCE_GROUP:
201 		{
202 			php_sockaddr_storage	group = {0},
203 									source = {0};
204 			socklen_t				glen,
205 									slen;
206 
207 			mcast_sreq_fun = &php_mcast_leave_source;
208 		mcast_sreq_fun:
209 			convert_to_array_ex(arg4);
210 			opt_ht = Z_ARRVAL_P(arg4);
211 
212 			if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
213 					&glen) == FAILURE) {
214 				return FAILURE;
215 			}
216 			if (php_get_address_from_array(opt_ht, "source", php_sock, &source,
217 					&slen) == FAILURE) {
218 				return FAILURE;
219 			}
220 			if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
221 					&if_index) == FAILURE) {
222 				return FAILURE;
223 			}
224 
225 			retval = mcast_sreq_fun(php_sock, level, (struct sockaddr*)&group,
226 					glen, (struct sockaddr*)&source, slen, if_index);
227 			break;
228 		}
229 #endif
230 	default:
231 		php_error_docref(NULL, E_WARNING,
232 			"unexpected option in php_do_mcast_opt (level %d, option %d). "
233 			"This is a bug.", level, optname);
234 		return FAILURE;
235 	}
236 
237 	if (retval != 0) {
238 		if (retval != -2) { /* error, but message already emitted */
239 			PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
240 		}
241 		return FAILURE;
242 	}
243 	return SUCCESS;
244 }
245 
php_do_setsockopt_ip_mcast(php_socket * php_sock,int level,int optname,zval * arg4)246 int php_do_setsockopt_ip_mcast(php_socket *php_sock,
247 							   int level,
248 							   int optname,
249 							   zval *arg4)
250 {
251 	unsigned int	if_index;
252 	struct in_addr	if_addr;
253 	void 			*opt_ptr;
254 	socklen_t		optlen;
255 	unsigned char	ipv4_mcast_ttl_lback;
256 	int				retval;
257 
258 	switch (optname) {
259 	case PHP_MCAST_JOIN_GROUP:
260 	case PHP_MCAST_LEAVE_GROUP:
261 #ifdef HAS_MCAST_EXT
262 	case PHP_MCAST_BLOCK_SOURCE:
263 	case PHP_MCAST_UNBLOCK_SOURCE:
264 	case PHP_MCAST_JOIN_SOURCE_GROUP:
265 	case PHP_MCAST_LEAVE_SOURCE_GROUP:
266 #endif
267 		if (php_do_mcast_opt(php_sock, level, optname, arg4) == FAILURE) {
268 			return FAILURE;
269 		} else {
270 			return SUCCESS;
271 		}
272 
273 	case IP_MULTICAST_IF:
274 		if (php_get_if_index_from_zval(arg4, &if_index) == FAILURE) {
275 			return FAILURE;
276 		}
277 
278 		if (php_if_index_to_addr4(if_index, php_sock, &if_addr) == FAILURE) {
279 			return FAILURE;
280 		}
281 		opt_ptr = &if_addr;
282 		optlen	= sizeof(if_addr);
283 		goto dosockopt;
284 
285 	case IP_MULTICAST_LOOP:
286 		convert_to_boolean_ex(arg4);
287 		ipv4_mcast_ttl_lback = (unsigned char) (Z_TYPE_P(arg4) == IS_TRUE);
288 		goto ipv4_loop_ttl;
289 
290 	case IP_MULTICAST_TTL:
291 		convert_to_long_ex(arg4);
292 		if (Z_LVAL_P(arg4) < 0L || Z_LVAL_P(arg4) > 255L) {
293 			php_error_docref(NULL, E_WARNING,
294 					"Expected a value between 0 and 255");
295 			return FAILURE;
296 		}
297 		ipv4_mcast_ttl_lback = (unsigned char) Z_LVAL_P(arg4);
298 ipv4_loop_ttl:
299 		opt_ptr = &ipv4_mcast_ttl_lback;
300 		optlen	= sizeof(ipv4_mcast_ttl_lback);
301 		goto dosockopt;
302 	}
303 
304 	return 1;
305 
306 dosockopt:
307 	retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
308 	if (retval != 0) {
309 		PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
310 		return FAILURE;
311 	}
312 
313 	return SUCCESS;
314 }
315 
php_do_setsockopt_ipv6_mcast(php_socket * php_sock,int level,int optname,zval * arg4)316 int php_do_setsockopt_ipv6_mcast(php_socket *php_sock,
317 								 int level,
318 								 int optname,
319 								 zval *arg4)
320 {
321 	unsigned int	if_index;
322 	void			*opt_ptr;
323 	socklen_t		optlen;
324 	int				ov;
325 	int				retval;
326 
327 	switch (optname) {
328 	case PHP_MCAST_JOIN_GROUP:
329 	case PHP_MCAST_LEAVE_GROUP:
330 #ifdef HAS_MCAST_EXT
331 	case PHP_MCAST_BLOCK_SOURCE:
332 	case PHP_MCAST_UNBLOCK_SOURCE:
333 	case PHP_MCAST_JOIN_SOURCE_GROUP:
334 	case PHP_MCAST_LEAVE_SOURCE_GROUP:
335 #endif
336 		if (php_do_mcast_opt(php_sock, level, optname, arg4) == FAILURE) {
337 			return FAILURE;
338 		} else {
339 			return SUCCESS;
340 		}
341 
342 	case IPV6_MULTICAST_IF:
343 		if (php_get_if_index_from_zval(arg4, &if_index) == FAILURE) {
344 			return FAILURE;
345 		}
346 
347 		opt_ptr = &if_index;
348 		optlen	= sizeof(if_index);
349 		goto dosockopt;
350 
351 	case IPV6_MULTICAST_LOOP:
352 		convert_to_boolean_ex(arg4);
353 		ov = (int) Z_TYPE_P(arg4) == IS_TRUE;
354 		goto ipv6_loop_hops;
355 	case IPV6_MULTICAST_HOPS:
356 		convert_to_long_ex(arg4);
357 		if (Z_LVAL_P(arg4) < -1L || Z_LVAL_P(arg4) > 255L) {
358 			php_error_docref(NULL, E_WARNING,
359 					"Expected a value between -1 and 255");
360 			return FAILURE;
361 		}
362 		ov = (int) Z_LVAL_P(arg4);
363 ipv6_loop_hops:
364 		opt_ptr = &ov;
365 		optlen	= sizeof(ov);
366 		goto dosockopt;
367 	}
368 
369 	return 1; /* not handled */
370 
371 dosockopt:
372 	retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
373 	if (retval != 0) {
374 		PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
375 		return FAILURE;
376 	}
377 
378 	return SUCCESS;
379 }
380 
php_mcast_join(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,unsigned int if_index)381 int php_mcast_join(
382 	php_socket *sock,
383 	int level,
384 	struct sockaddr *group,
385 	socklen_t group_len,
386 	unsigned int if_index)
387 {
388 	return _php_mcast_join_leave(sock, level, group, group_len, if_index, 1);
389 }
390 
php_mcast_leave(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,unsigned int if_index)391 int php_mcast_leave(
392 	php_socket *sock,
393 	int level,
394 	struct sockaddr *group,
395 	socklen_t group_len,
396 	unsigned int if_index)
397 {
398 	return _php_mcast_join_leave(sock, level, group, group_len, if_index, 0);
399 }
400 
401 #ifdef HAS_MCAST_EXT
php_mcast_join_source(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,struct sockaddr * source,socklen_t source_len,unsigned int if_index)402 int php_mcast_join_source(
403 	php_socket *sock,
404 	int level,
405 	struct sockaddr *group,
406 	socklen_t group_len,
407 	struct sockaddr *source,
408 	socklen_t source_len,
409 	unsigned int if_index)
410 {
411 	return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, JOIN_SOURCE);
412 }
413 
php_mcast_leave_source(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,struct sockaddr * source,socklen_t source_len,unsigned int if_index)414 int php_mcast_leave_source(
415 	php_socket *sock,
416 	int level,
417 	struct sockaddr *group,
418 	socklen_t group_len,
419 	struct sockaddr *source,
420 	socklen_t source_len,
421 	unsigned int if_index)
422 {
423 	return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, LEAVE_SOURCE);
424 }
425 
php_mcast_block_source(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,struct sockaddr * source,socklen_t source_len,unsigned int if_index)426 int php_mcast_block_source(
427 	php_socket *sock,
428 	int level,
429 	struct sockaddr *group,
430 	socklen_t group_len,
431 	struct sockaddr *source,
432 	socklen_t source_len,
433 	unsigned int if_index)
434 {
435 	return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, BLOCK_SOURCE);
436 }
437 
php_mcast_unblock_source(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,struct sockaddr * source,socklen_t source_len,unsigned int if_index)438 int php_mcast_unblock_source(
439 	php_socket *sock,
440 	int level,
441 	struct sockaddr *group,
442 	socklen_t group_len,
443 	struct sockaddr *source,
444 	socklen_t source_len,
445 	unsigned int if_index)
446 {
447 	return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, UNBLOCK_SOURCE);
448 }
449 #endif /* HAS_MCAST_EXT */
450 
451 
_php_mcast_join_leave(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,unsigned int if_index,int join)452 static int _php_mcast_join_leave(
453 	php_socket *sock,
454 	int level,
455 	struct sockaddr *group, /* struct sockaddr_in/sockaddr_in6 */
456 	socklen_t group_len,
457 	unsigned int if_index,
458 	int join)
459 {
460 #ifdef RFC3678_API
461 	struct group_req greq = {0};
462 
463 	memcpy(&greq.gr_group, group, group_len);
464 	assert(greq.gr_group.ss_family != 0); /* the caller has set this */
465 	greq.gr_interface = if_index;
466 
467 	return setsockopt(sock->bsd_socket, level,
468 			join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (char*)&greq,
469 			sizeof(greq));
470 #else
471 	if (sock->type == AF_INET) {
472 		struct ip_mreq mreq = {0};
473 		struct in_addr addr;
474 
475 		assert(group_len == sizeof(struct sockaddr_in));
476 
477 		if (if_index != 0) {
478 			if (php_if_index_to_addr4(if_index, sock, &addr) ==
479 					FAILURE)
480 				return -2; /* failure, but notice already emitted */
481 			mreq.imr_interface = addr;
482 		} else {
483 			mreq.imr_interface.s_addr = htonl(INADDR_ANY);
484 		}
485 		mreq.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
486 		return setsockopt(sock->bsd_socket, level,
487 				join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (char*)&mreq,
488 				sizeof(mreq));
489 	}
490 #if HAVE_IPV6
491 	else if (sock->type == AF_INET6) {
492 		struct ipv6_mreq mreq = {0};
493 
494 		assert(group_len == sizeof(struct sockaddr_in6));
495 
496 		mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)group)->sin6_addr;
497 		mreq.ipv6mr_interface = if_index;
498 
499 		return setsockopt(sock->bsd_socket, level,
500 				join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, (char*)&mreq,
501 				sizeof(mreq));
502 	}
503 #endif
504 	else {
505 		php_error_docref(NULL, E_WARNING,
506 			"Option %s is inapplicable to this socket type",
507 			join ? "MCAST_JOIN_GROUP" : "MCAST_LEAVE_GROUP");
508 		return -2;
509 	}
510 #endif
511 }
512 
513 #ifdef HAS_MCAST_EXT
_php_mcast_source_op(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,struct sockaddr * source,socklen_t source_len,unsigned int if_index,enum source_op sop)514 static int _php_mcast_source_op(
515 	php_socket *sock,
516 	int level,
517 	struct sockaddr *group,
518 	socklen_t group_len,
519 	struct sockaddr *source,
520 	socklen_t source_len,
521 	unsigned int if_index,
522 	enum source_op sop)
523 {
524 #ifdef RFC3678_API
525 	struct group_source_req gsreq = {0};
526 
527 	memcpy(&gsreq.gsr_group, group, group_len);
528 	assert(gsreq.gsr_group.ss_family != 0);
529 	memcpy(&gsreq.gsr_source, source, source_len);
530 	assert(gsreq.gsr_source.ss_family != 0);
531 	gsreq.gsr_interface = if_index;
532 
533 	return setsockopt(sock->bsd_socket, level,
534 			_php_source_op_to_rfc3678_op(sop), (char*)&gsreq, sizeof(gsreq));
535 #else
536 	if (sock->type == AF_INET) {
537 		struct ip_mreq_source mreqs = {0};
538 		struct in_addr addr;
539 
540 		mreqs.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
541 		mreqs.imr_sourceaddr =  ((struct sockaddr_in*)source)->sin_addr;
542 
543 		assert(group_len == sizeof(struct sockaddr_in));
544 		assert(source_len == sizeof(struct sockaddr_in));
545 
546 		if (if_index != 0) {
547 			if (php_if_index_to_addr4(if_index, sock, &addr) ==
548 					FAILURE)
549 				return -2; /* failure, but notice already emitted */
550 			mreqs.imr_interface = addr;
551 		} else {
552 			mreqs.imr_interface.s_addr = htonl(INADDR_ANY);
553 		}
554 
555 		return setsockopt(sock->bsd_socket, level,
556 				_php_source_op_to_ipv4_op(sop), (char*)&mreqs, sizeof(mreqs));
557 	}
558 #if HAVE_IPV6
559 	else if (sock->type == AF_INET6) {
560 		php_error_docref(NULL, E_WARNING,
561 			"This platform does not support %s for IPv6 sockets",
562 			_php_source_op_to_string(sop));
563 		return -2;
564 	}
565 #endif
566 	else {
567 		php_error_docref(NULL, E_WARNING,
568 			"Option %s is inapplicable to this socket type",
569 			_php_source_op_to_string(sop));
570 		return -2;
571 	}
572 #endif
573 }
574 
575 #if RFC3678_API
_php_source_op_to_rfc3678_op(enum source_op sop)576 static int _php_source_op_to_rfc3678_op(enum source_op sop)
577 {
578 	switch (sop) {
579 	case JOIN_SOURCE:
580 		return MCAST_JOIN_SOURCE_GROUP;
581 	case LEAVE_SOURCE:
582 		return MCAST_LEAVE_SOURCE_GROUP;
583 	case BLOCK_SOURCE:
584 		return MCAST_BLOCK_SOURCE;
585 	case UNBLOCK_SOURCE:
586 		return MCAST_UNBLOCK_SOURCE;
587 	}
588 
589 	assert(0);
590 	return 0;
591 }
592 #else
_php_source_op_to_string(enum source_op sop)593 static const char *_php_source_op_to_string(enum source_op sop)
594 {
595 	switch (sop) {
596 	case JOIN_SOURCE:
597 		return "MCAST_JOIN_SOURCE_GROUP";
598 	case LEAVE_SOURCE:
599 		return "MCAST_LEAVE_SOURCE_GROUP";
600 	case BLOCK_SOURCE:
601 		return "MCAST_BLOCK_SOURCE";
602 	case UNBLOCK_SOURCE:
603 		return "MCAST_UNBLOCK_SOURCE";
604 	}
605 
606 	assert(0);
607 	return "";
608 }
609 
_php_source_op_to_ipv4_op(enum source_op sop)610 static int _php_source_op_to_ipv4_op(enum source_op sop)
611 {
612 	switch (sop) {
613 	case JOIN_SOURCE:
614 		return IP_ADD_SOURCE_MEMBERSHIP;
615 	case LEAVE_SOURCE:
616 		return IP_DROP_SOURCE_MEMBERSHIP;
617 	case BLOCK_SOURCE:
618 		return IP_BLOCK_SOURCE;
619 	case UNBLOCK_SOURCE:
620 		return IP_UNBLOCK_SOURCE;
621 	}
622 
623 	assert(0);
624 	return 0;
625 }
626 #endif
627 
628 #endif /* HAS_MCAST_EXT */
629 
630 #ifdef PHP_WIN32
php_if_index_to_addr4(unsigned if_index,php_socket * php_sock,struct in_addr * out_addr)631 int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr)
632 {
633 	MIB_IPADDRTABLE *addr_table;
634     ULONG size;
635     DWORD retval;
636 	DWORD i;
637 
638 	(void) php_sock; /* not necessary */
639 
640 	if (if_index == 0) {
641 		out_addr->s_addr = INADDR_ANY;
642 		return SUCCESS;
643 	}
644 
645 	size = 4 * (sizeof *addr_table);
646 	addr_table = emalloc(size);
647 retry:
648 	retval = GetIpAddrTable(addr_table, &size, 0);
649 	if (retval == ERROR_INSUFFICIENT_BUFFER) {
650 		efree(addr_table);
651 		addr_table = emalloc(size);
652 		goto retry;
653 	}
654 	if (retval != NO_ERROR) {
655 		efree(addr_table);
656 		php_error_docref(NULL, E_WARNING,
657 			"GetIpAddrTable failed with error %lu", retval);
658 		return FAILURE;
659 	}
660 	for (i = 0; i < addr_table->dwNumEntries; i++) {
661 		MIB_IPADDRROW r = addr_table->table[i];
662 		if (r.dwIndex == if_index) {
663 			out_addr->s_addr = r.dwAddr;
664 			efree(addr_table);
665 			return SUCCESS;
666 		}
667 	}
668 	efree(addr_table);
669 	php_error_docref(NULL, E_WARNING,
670 		"No interface with index %u was found", if_index);
671 	return FAILURE;
672 }
673 
php_add4_to_if_index(struct in_addr * addr,php_socket * php_sock,unsigned * if_index)674 int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index)
675 {
676 	MIB_IPADDRTABLE *addr_table;
677     ULONG size;
678     DWORD retval;
679 	DWORD i;
680 
681 	(void) php_sock; /* not necessary */
682 
683 	if (addr->s_addr == INADDR_ANY) {
684 		*if_index = 0;
685 		return SUCCESS;
686 	}
687 
688 	size = 4 * (sizeof *addr_table);
689 	addr_table = emalloc(size);
690 retry:
691 	retval = GetIpAddrTable(addr_table, &size, 0);
692 	if (retval == ERROR_INSUFFICIENT_BUFFER) {
693 		efree(addr_table);
694 		addr_table = emalloc(size);
695 		goto retry;
696 	}
697 	if (retval != NO_ERROR) {
698 		efree(addr_table);
699 		php_error_docref(NULL, E_WARNING,
700 			"GetIpAddrTable failed with error %lu", retval);
701 		return FAILURE;
702 	}
703 	for (i = 0; i < addr_table->dwNumEntries; i++) {
704 		MIB_IPADDRROW r = addr_table->table[i];
705 		if (r.dwAddr == addr->s_addr) {
706 			*if_index = r.dwIndex;
707 			efree(addr_table);
708 			return SUCCESS;
709 		}
710 	}
711 	efree(addr_table);
712 
713 	{
714 		char addr_str[17] = {0};
715 		inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
716 		php_error_docref(NULL, E_WARNING,
717 			"The interface with IP address %s was not found", addr_str);
718 	}
719 	return FAILURE;
720 }
721 
722 #else
723 
php_if_index_to_addr4(unsigned if_index,php_socket * php_sock,struct in_addr * out_addr)724 int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr)
725 {
726 	struct ifreq if_req;
727 
728 	if (if_index == 0) {
729 		out_addr->s_addr = INADDR_ANY;
730 		return SUCCESS;
731 	}
732 
733 #if !defined(ifr_ifindex) && defined(ifr_index)
734 #define ifr_ifindex ifr_index
735 #endif
736 
737 #if defined(SIOCGIFNAME)
738 	if_req.ifr_ifindex = if_index;
739 	if (ioctl(php_sock->bsd_socket, SIOCGIFNAME, &if_req) == -1) {
740 #elif defined(HAVE_IF_INDEXTONAME)
741 	if (if_indextoname(if_index, if_req.ifr_name) == NULL) {
742 #else
743 #error Neither SIOCGIFNAME nor if_indextoname are available
744 #endif
745 		php_error_docref(NULL, E_WARNING,
746 			"Failed obtaining address for interface %u: error %d", if_index, errno);
747 		return FAILURE;
748 	}
749 
750 	if (ioctl(php_sock->bsd_socket, SIOCGIFADDR, &if_req) == -1) {
751 		php_error_docref(NULL, E_WARNING,
752 			"Failed obtaining address for interface %u: error %d", if_index, errno);
753 		return FAILURE;
754 	}
755 
756 	memcpy(out_addr, &((struct sockaddr_in *) &if_req.ifr_addr)->sin_addr,
757 		sizeof *out_addr);
758 	return SUCCESS;
759 }
760 
761 int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index)
762 {
763 	struct ifconf	if_conf = {0};
764 	char			*buf = NULL,
765 					*p;
766 	int				size = 0,
767 					lastsize = 0;
768 	size_t			entry_len;
769 
770 	if (addr->s_addr == INADDR_ANY) {
771 		*if_index = 0;
772 		return SUCCESS;
773 	}
774 
775 	for(;;) {
776 		size += 5 * sizeof(struct ifreq);
777 		buf = ecalloc(size, 1);
778 		if_conf.ifc_len = size;
779 		if_conf.ifc_buf = buf;
780 
781 		if (ioctl(php_sock->bsd_socket, SIOCGIFCONF, (char*)&if_conf) == -1 &&
782 				(errno != EINVAL || lastsize != 0)) {
783 			php_error_docref(NULL, E_WARNING,
784 				"Failed obtaining interfaces list: error %d", errno);
785 			goto err;
786 		}
787 
788 		if (if_conf.ifc_len == lastsize)
789 			/* not increasing anymore */
790 			break;
791 		else {
792 			lastsize = if_conf.ifc_len;
793 			efree(buf);
794 			buf = NULL;
795 		}
796 	}
797 
798 	for (p = if_conf.ifc_buf;
799 		 p < if_conf.ifc_buf + if_conf.ifc_len;
800 		 p += entry_len) {
801 		struct ifreq *cur_req;
802 
803 		/* let's hope the pointer is aligned */
804 		cur_req = (struct ifreq*) p;
805 
806 #ifdef HAVE_SOCKADDR_SA_LEN
807 		entry_len = cur_req->ifr_addr.sa_len + sizeof(cur_req->ifr_name);
808 #else
809 		/* if there's no sa_len, assume the ifr_addr field is a sockaddr */
810 		entry_len = sizeof(struct sockaddr) + sizeof(cur_req->ifr_name);
811 #endif
812 		entry_len = MAX(entry_len, sizeof(*cur_req));
813 
814 		if ((((struct sockaddr*)&cur_req->ifr_addr)->sa_family == AF_INET) &&
815 				(((struct sockaddr_in*)&cur_req->ifr_addr)->sin_addr.s_addr ==
816 					addr->s_addr)) {
817 #if defined(SIOCGIFINDEX)
818 			if (ioctl(php_sock->bsd_socket, SIOCGIFINDEX, (char*)cur_req)
819 					== -1) {
820 #elif defined(HAVE_IF_NAMETOINDEX)
821 			unsigned index_tmp;
822 			if ((index_tmp = if_nametoindex(cur_req->ifr_name)) == 0) {
823 #else
824 #error Neither SIOCGIFINDEX nor if_nametoindex are available
825 #endif
826 				php_error_docref(NULL, E_WARNING,
827 					"Error converting interface name to index: error %d",
828 					errno);
829 				goto err;
830 			} else {
831 #if defined(SIOCGIFINDEX)
832 				*if_index = cur_req->ifr_ifindex;
833 #else
834 				*if_index = index_tmp;
835 #endif
836 				efree(buf);
837 				return SUCCESS;
838 			}
839 		}
840 	}
841 
842 	{
843 		char addr_str[17] = {0};
844 		inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
845 		php_error_docref(NULL, E_WARNING,
846 			"The interface with IP address %s was not found", addr_str);
847 	}
848 
849 err:
850 	if (buf != NULL)
851 		efree(buf);
852 	return FAILURE;
853 }
854 #endif
855