1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2016 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