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