1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 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 || 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 %pd", 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 php_error_docref(NULL, E_WARNING,
656 "GetIpAddrTable failed with error %lu", retval);
657 return FAILURE;
658 }
659 for (i = 0; i < addr_table->dwNumEntries; i++) {
660 MIB_IPADDRROW r = addr_table->table[i];
661 if (r.dwIndex == if_index) {
662 out_addr->s_addr = r.dwAddr;
663 return SUCCESS;
664 }
665 }
666 php_error_docref(NULL, E_WARNING,
667 "No interface with index %u was found", if_index);
668 return FAILURE;
669 }
670
php_add4_to_if_index(struct in_addr * addr,php_socket * php_sock,unsigned * if_index)671 int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index)
672 {
673 MIB_IPADDRTABLE *addr_table;
674 ULONG size;
675 DWORD retval;
676 DWORD i;
677
678 (void) php_sock; /* not necessary */
679
680 if (addr->s_addr == INADDR_ANY) {
681 *if_index = 0;
682 return SUCCESS;
683 }
684
685 size = 4 * (sizeof *addr_table);
686 addr_table = emalloc(size);
687 retry:
688 retval = GetIpAddrTable(addr_table, &size, 0);
689 if (retval == ERROR_INSUFFICIENT_BUFFER) {
690 efree(addr_table);
691 addr_table = emalloc(size);
692 goto retry;
693 }
694 if (retval != NO_ERROR) {
695 php_error_docref(NULL, E_WARNING,
696 "GetIpAddrTable failed with error %lu", retval);
697 return FAILURE;
698 }
699 for (i = 0; i < addr_table->dwNumEntries; i++) {
700 MIB_IPADDRROW r = addr_table->table[i];
701 if (r.dwAddr == addr->s_addr) {
702 *if_index = r.dwIndex;
703 return SUCCESS;
704 }
705 }
706
707 {
708 char addr_str[17] = {0};
709 inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
710 php_error_docref(NULL, E_WARNING,
711 "The interface with IP address %s was not found", addr_str);
712 }
713 return FAILURE;
714 }
715
716 #else
717
php_if_index_to_addr4(unsigned if_index,php_socket * php_sock,struct in_addr * out_addr)718 int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr)
719 {
720 struct ifreq if_req;
721
722 if (if_index == 0) {
723 out_addr->s_addr = INADDR_ANY;
724 return SUCCESS;
725 }
726
727 #if !defined(ifr_ifindex) && defined(ifr_index)
728 #define ifr_ifindex ifr_index
729 #endif
730
731 #if defined(SIOCGIFNAME)
732 if_req.ifr_ifindex = if_index;
733 if (ioctl(php_sock->bsd_socket, SIOCGIFNAME, &if_req) == -1) {
734 #elif defined(HAVE_IF_INDEXTONAME)
735 if (if_indextoname(if_index, if_req.ifr_name) == NULL) {
736 #else
737 #error Neither SIOCGIFNAME nor if_indextoname are available
738 #endif
739 php_error_docref(NULL, E_WARNING,
740 "Failed obtaining address for interface %u: error %d", if_index, errno);
741 return FAILURE;
742 }
743
744 if (ioctl(php_sock->bsd_socket, SIOCGIFADDR, &if_req) == -1) {
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 memcpy(out_addr, &((struct sockaddr_in *) &if_req.ifr_addr)->sin_addr,
751 sizeof *out_addr);
752 return SUCCESS;
753 }
754
755 int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index)
756 {
757 struct ifconf if_conf = {0};
758 char *buf = NULL,
759 *p;
760 int size = 0,
761 lastsize = 0;
762 size_t entry_len;
763
764 if (addr->s_addr == INADDR_ANY) {
765 *if_index = 0;
766 return SUCCESS;
767 }
768
769 for(;;) {
770 size += 5 * sizeof(struct ifreq);
771 buf = ecalloc(size, 1);
772 if_conf.ifc_len = size;
773 if_conf.ifc_buf = buf;
774
775 if (ioctl(php_sock->bsd_socket, SIOCGIFCONF, (char*)&if_conf) == -1 &&
776 (errno != EINVAL || lastsize != 0)) {
777 php_error_docref(NULL, E_WARNING,
778 "Failed obtaining interfaces list: error %d", errno);
779 goto err;
780 }
781
782 if (if_conf.ifc_len == lastsize)
783 /* not increasing anymore */
784 break;
785 else {
786 lastsize = if_conf.ifc_len;
787 efree(buf);
788 buf = NULL;
789 }
790 }
791
792 for (p = if_conf.ifc_buf;
793 p < if_conf.ifc_buf + if_conf.ifc_len;
794 p += entry_len) {
795 struct ifreq *cur_req;
796
797 /* let's hope the pointer is aligned */
798 cur_req = (struct ifreq*) p;
799
800 #ifdef HAVE_SOCKADDR_SA_LEN
801 entry_len = cur_req->ifr_addr.sa_len + sizeof(cur_req->ifr_name);
802 #else
803 /* if there's no sa_len, assume the ifr_addr field is a sockaddr */
804 entry_len = sizeof(struct sockaddr) + sizeof(cur_req->ifr_name);
805 #endif
806 entry_len = MAX(entry_len, sizeof(*cur_req));
807
808 if ((((struct sockaddr*)&cur_req->ifr_addr)->sa_family == AF_INET) &&
809 (((struct sockaddr_in*)&cur_req->ifr_addr)->sin_addr.s_addr ==
810 addr->s_addr)) {
811 #if defined(SIOCGIFINDEX)
812 if (ioctl(php_sock->bsd_socket, SIOCGIFINDEX, (char*)cur_req)
813 == -1) {
814 #elif defined(HAVE_IF_NAMETOINDEX)
815 unsigned index_tmp;
816 if ((index_tmp = if_nametoindex(cur_req->ifr_name)) == 0) {
817 #else
818 #error Neither SIOCGIFINDEX nor if_nametoindex are available
819 #endif
820 php_error_docref(NULL, E_WARNING,
821 "Error converting interface name to index: error %d",
822 errno);
823 goto err;
824 } else {
825 #if defined(SIOCGIFINDEX)
826 *if_index = cur_req->ifr_ifindex;
827 #else
828 *if_index = index_tmp;
829 #endif
830 efree(buf);
831 return SUCCESS;
832 }
833 }
834 }
835
836 {
837 char addr_str[17] = {0};
838 inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
839 php_error_docref(NULL, E_WARNING,
840 "The interface with IP address %s was not found", addr_str);
841 }
842
843 err:
844 if (buf != NULL)
845 efree(buf);
846 return FAILURE;
847 }
848 #endif
849