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