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 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "php.h"
24
25 #include "php_network.h"
26 #ifdef PHP_WIN32
27 # include "windows_common.h"
28 #else
29 #include <sys/socket.h>
30 #include <sys/ioctl.h>
31 #include <net/if.h>
32 #ifdef HAVE_SYS_SOCKIO_H
33 #include <sys/sockio.h>
34 #endif
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #endif
38
39 #include "php_sockets.h"
40 #include "multicast.h"
41 #include "sockaddr_conv.h"
42 #include "main/php_network.h"
43
44
45 enum source_op {
46 JOIN_SOURCE,
47 LEAVE_SOURCE,
48 BLOCK_SOURCE,
49 UNBLOCK_SOURCE
50 };
51
52 static int _php_mcast_join_leave(php_socket *sock, int level, struct sockaddr *group, socklen_t group_len, unsigned int if_index, int join);
53 #ifdef HAS_MCAST_EXT
54 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);
55 #endif
56
57 #ifdef RFC3678_API
58 static int _php_source_op_to_rfc3678_op(enum source_op sop);
59 #elif HAS_MCAST_EXT
60 static const char *_php_source_op_to_string(enum source_op sop);
61 static int _php_source_op_to_ipv4_op(enum source_op sop);
62 #endif
63
php_string_to_if_index(const char * val,unsigned * out)64 int php_string_to_if_index(const char *val, unsigned *out)
65 {
66 #if HAVE_IF_NAMETOINDEX
67 unsigned int ind;
68
69 ind = if_nametoindex(val);
70 if (ind == 0) {
71 php_error_docref(NULL, E_WARNING,
72 "no interface with name \"%s\" could be found", val);
73 return FAILURE;
74 } else {
75 *out = ind;
76 return SUCCESS;
77 }
78 #else
79 php_error_docref(NULL, E_WARNING,
80 "this platform does not support looking up an interface by "
81 "name, an integer interface index must be supplied instead");
82 return FAILURE;
83 #endif
84 }
85
php_get_if_index_from_zval(zval * val,unsigned * out)86 static int php_get_if_index_from_zval(zval *val, unsigned *out)
87 {
88 int ret;
89
90 if (Z_TYPE_P(val) == IS_LONG) {
91 if (Z_LVAL_P(val) < 0 || (zend_ulong)Z_LVAL_P(val) > UINT_MAX) {
92 php_error_docref(NULL, E_WARNING,
93 "the interface index cannot be negative or larger than %u;"
94 " given " ZEND_LONG_FMT, UINT_MAX, Z_LVAL_P(val));
95 ret = FAILURE;
96 } else {
97 *out = Z_LVAL_P(val);
98 ret = SUCCESS;
99 }
100 } else {
101 zend_string *tmp_str;
102 zend_string *str = zval_get_tmp_string(val, &tmp_str);
103 ret = php_string_to_if_index(ZSTR_VAL(str), out);
104 zend_tmp_string_release(tmp_str);
105 }
106
107 return ret;
108 }
109
110
111
php_get_if_index_from_array(const HashTable * ht,const char * key,php_socket * sock,unsigned int * if_index)112 static int php_get_if_index_from_array(const HashTable *ht, const char *key,
113 php_socket *sock, unsigned int *if_index)
114 {
115 zval *val;
116
117 if ((val = zend_hash_str_find(ht, key, strlen(key))) == NULL) {
118 *if_index = 0; /* default: 0 */
119 return SUCCESS;
120 }
121
122 return php_get_if_index_from_zval(val, if_index);
123 }
124
php_get_address_from_array(const HashTable * ht,const char * key,php_socket * sock,php_sockaddr_storage * ss,socklen_t * ss_len)125 static int php_get_address_from_array(const HashTable *ht, const char *key,
126 php_socket *sock, php_sockaddr_storage *ss, socklen_t *ss_len)
127 {
128 zval *val;
129 zend_string *str, *tmp_str;
130
131 if ((val = zend_hash_str_find(ht, key, strlen(key))) == NULL) {
132 php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", key);
133 return FAILURE;
134 }
135 str = zval_get_tmp_string(val, &tmp_str);
136 if (!php_set_inet46_addr(ss, ss_len, ZSTR_VAL(str), sock)) {
137 zend_tmp_string_release(tmp_str);
138 return FAILURE;
139 }
140 zend_tmp_string_release(tmp_str);
141 return SUCCESS;
142 }
143
php_do_mcast_opt(php_socket * php_sock,int level,int optname,zval * arg4)144 static int php_do_mcast_opt(php_socket *php_sock, int level, int optname, zval *arg4)
145 {
146 HashTable *opt_ht;
147 unsigned int if_index;
148 int retval;
149 int (*mcast_req_fun)(php_socket *, int, struct sockaddr *, socklen_t,
150 unsigned);
151 #ifdef HAS_MCAST_EXT
152 int (*mcast_sreq_fun)(php_socket *, int, struct sockaddr *, socklen_t,
153 struct sockaddr *, socklen_t, unsigned);
154 #endif
155
156 switch (optname) {
157 case PHP_MCAST_JOIN_GROUP:
158 mcast_req_fun = &php_mcast_join;
159 goto mcast_req_fun;
160 case PHP_MCAST_LEAVE_GROUP:
161 {
162 php_sockaddr_storage group = {0};
163 socklen_t glen;
164
165 mcast_req_fun = &php_mcast_leave;
166 mcast_req_fun:
167 convert_to_array_ex(arg4);
168 opt_ht = Z_ARRVAL_P(arg4);
169
170 if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
171 &glen) == FAILURE) {
172 return FAILURE;
173 }
174 if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
175 &if_index) == FAILURE) {
176 return FAILURE;
177 }
178
179 retval = mcast_req_fun(php_sock, level, (struct sockaddr*)&group,
180 glen, if_index);
181 break;
182 }
183
184 #ifdef HAS_MCAST_EXT
185 case PHP_MCAST_BLOCK_SOURCE:
186 mcast_sreq_fun = &php_mcast_block_source;
187 goto mcast_sreq_fun;
188 case PHP_MCAST_UNBLOCK_SOURCE:
189 mcast_sreq_fun = &php_mcast_unblock_source;
190 goto mcast_sreq_fun;
191 case PHP_MCAST_JOIN_SOURCE_GROUP:
192 mcast_sreq_fun = &php_mcast_join_source;
193 goto mcast_sreq_fun;
194 case PHP_MCAST_LEAVE_SOURCE_GROUP:
195 {
196 php_sockaddr_storage group = {0},
197 source = {0};
198 socklen_t glen,
199 slen;
200
201 mcast_sreq_fun = &php_mcast_leave_source;
202 mcast_sreq_fun:
203 convert_to_array_ex(arg4);
204 opt_ht = Z_ARRVAL_P(arg4);
205
206 if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
207 &glen) == FAILURE) {
208 return FAILURE;
209 }
210 if (php_get_address_from_array(opt_ht, "source", php_sock, &source,
211 &slen) == FAILURE) {
212 return FAILURE;
213 }
214 if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
215 &if_index) == FAILURE) {
216 return FAILURE;
217 }
218
219 retval = mcast_sreq_fun(php_sock, level, (struct sockaddr*)&group,
220 glen, (struct sockaddr*)&source, slen, if_index);
221 break;
222 }
223 #endif
224 default:
225 php_error_docref(NULL, E_WARNING,
226 "unexpected option in php_do_mcast_opt (level %d, option %d). "
227 "This is a bug.", level, optname);
228 return FAILURE;
229 }
230
231 if (retval != 0) {
232 if (retval != -2) { /* error, but message already emitted */
233 PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
234 }
235 return FAILURE;
236 }
237 return SUCCESS;
238 }
239
php_do_setsockopt_ip_mcast(php_socket * php_sock,int level,int optname,zval * arg4)240 int php_do_setsockopt_ip_mcast(php_socket *php_sock,
241 int level,
242 int optname,
243 zval *arg4)
244 {
245 unsigned int if_index;
246 struct in_addr if_addr;
247 void *opt_ptr;
248 socklen_t optlen;
249 unsigned char ipv4_mcast_ttl_lback;
250 int retval;
251
252 switch (optname) {
253 case PHP_MCAST_JOIN_GROUP:
254 case PHP_MCAST_LEAVE_GROUP:
255 #ifdef HAS_MCAST_EXT
256 case PHP_MCAST_BLOCK_SOURCE:
257 case PHP_MCAST_UNBLOCK_SOURCE:
258 case PHP_MCAST_JOIN_SOURCE_GROUP:
259 case PHP_MCAST_LEAVE_SOURCE_GROUP:
260 #endif
261 if (php_do_mcast_opt(php_sock, level, optname, arg4) == FAILURE) {
262 return FAILURE;
263 } else {
264 return SUCCESS;
265 }
266
267 case IP_MULTICAST_IF:
268 if (php_get_if_index_from_zval(arg4, &if_index) == FAILURE) {
269 return FAILURE;
270 }
271
272 if (php_if_index_to_addr4(if_index, php_sock, &if_addr) == FAILURE) {
273 return FAILURE;
274 }
275 opt_ptr = &if_addr;
276 optlen = sizeof(if_addr);
277 goto dosockopt;
278
279 case IP_MULTICAST_LOOP:
280 convert_to_boolean_ex(arg4);
281 ipv4_mcast_ttl_lback = (unsigned char) (Z_TYPE_P(arg4) == IS_TRUE);
282 goto ipv4_loop_ttl;
283
284 case IP_MULTICAST_TTL:
285 convert_to_long_ex(arg4);
286 if (Z_LVAL_P(arg4) < 0L || Z_LVAL_P(arg4) > 255L) {
287 php_error_docref(NULL, E_WARNING,
288 "Expected a value between 0 and 255");
289 return FAILURE;
290 }
291 ipv4_mcast_ttl_lback = (unsigned char) Z_LVAL_P(arg4);
292 ipv4_loop_ttl:
293 opt_ptr = &ipv4_mcast_ttl_lback;
294 optlen = sizeof(ipv4_mcast_ttl_lback);
295 goto dosockopt;
296 }
297
298 return 1;
299
300 dosockopt:
301 retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
302 if (retval != 0) {
303 PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
304 return FAILURE;
305 }
306
307 return SUCCESS;
308 }
309
php_do_setsockopt_ipv6_mcast(php_socket * php_sock,int level,int optname,zval * arg4)310 int php_do_setsockopt_ipv6_mcast(php_socket *php_sock,
311 int level,
312 int optname,
313 zval *arg4)
314 {
315 unsigned int if_index;
316 void *opt_ptr;
317 socklen_t optlen;
318 int ov;
319 int retval;
320
321 switch (optname) {
322 case PHP_MCAST_JOIN_GROUP:
323 case PHP_MCAST_LEAVE_GROUP:
324 #ifdef HAS_MCAST_EXT
325 case PHP_MCAST_BLOCK_SOURCE:
326 case PHP_MCAST_UNBLOCK_SOURCE:
327 case PHP_MCAST_JOIN_SOURCE_GROUP:
328 case PHP_MCAST_LEAVE_SOURCE_GROUP:
329 #endif
330 if (php_do_mcast_opt(php_sock, level, optname, arg4) == FAILURE) {
331 return FAILURE;
332 } else {
333 return SUCCESS;
334 }
335
336 case IPV6_MULTICAST_IF:
337 if (php_get_if_index_from_zval(arg4, &if_index) == FAILURE) {
338 return FAILURE;
339 }
340
341 opt_ptr = &if_index;
342 optlen = sizeof(if_index);
343 goto dosockopt;
344
345 case IPV6_MULTICAST_LOOP:
346 convert_to_boolean_ex(arg4);
347 ov = (int) Z_TYPE_P(arg4) == IS_TRUE;
348 goto ipv6_loop_hops;
349 case IPV6_MULTICAST_HOPS:
350 convert_to_long_ex(arg4);
351 if (Z_LVAL_P(arg4) < -1L || Z_LVAL_P(arg4) > 255L) {
352 php_error_docref(NULL, E_WARNING,
353 "Expected a value between -1 and 255");
354 return FAILURE;
355 }
356 ov = (int) Z_LVAL_P(arg4);
357 ipv6_loop_hops:
358 opt_ptr = &ov;
359 optlen = sizeof(ov);
360 goto dosockopt;
361 }
362
363 return 1; /* not handled */
364
365 dosockopt:
366 retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
367 if (retval != 0) {
368 PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
369 return FAILURE;
370 }
371
372 return SUCCESS;
373 }
374
php_mcast_join(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,unsigned int if_index)375 int php_mcast_join(
376 php_socket *sock,
377 int level,
378 struct sockaddr *group,
379 socklen_t group_len,
380 unsigned int if_index)
381 {
382 return _php_mcast_join_leave(sock, level, group, group_len, if_index, 1);
383 }
384
php_mcast_leave(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,unsigned int if_index)385 int php_mcast_leave(
386 php_socket *sock,
387 int level,
388 struct sockaddr *group,
389 socklen_t group_len,
390 unsigned int if_index)
391 {
392 return _php_mcast_join_leave(sock, level, group, group_len, if_index, 0);
393 }
394
395 #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)396 int php_mcast_join_source(
397 php_socket *sock,
398 int level,
399 struct sockaddr *group,
400 socklen_t group_len,
401 struct sockaddr *source,
402 socklen_t source_len,
403 unsigned int if_index)
404 {
405 return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, JOIN_SOURCE);
406 }
407
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)408 int php_mcast_leave_source(
409 php_socket *sock,
410 int level,
411 struct sockaddr *group,
412 socklen_t group_len,
413 struct sockaddr *source,
414 socklen_t source_len,
415 unsigned int if_index)
416 {
417 return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, LEAVE_SOURCE);
418 }
419
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)420 int php_mcast_block_source(
421 php_socket *sock,
422 int level,
423 struct sockaddr *group,
424 socklen_t group_len,
425 struct sockaddr *source,
426 socklen_t source_len,
427 unsigned int if_index)
428 {
429 return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, BLOCK_SOURCE);
430 }
431
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)432 int php_mcast_unblock_source(
433 php_socket *sock,
434 int level,
435 struct sockaddr *group,
436 socklen_t group_len,
437 struct sockaddr *source,
438 socklen_t source_len,
439 unsigned int if_index)
440 {
441 return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, UNBLOCK_SOURCE);
442 }
443 #endif /* HAS_MCAST_EXT */
444
445
_php_mcast_join_leave(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,unsigned int if_index,int join)446 static int _php_mcast_join_leave(
447 php_socket *sock,
448 int level,
449 struct sockaddr *group, /* struct sockaddr_in/sockaddr_in6 */
450 socklen_t group_len,
451 unsigned int if_index,
452 int join)
453 {
454 #ifdef RFC3678_API
455 struct group_req greq = {0};
456
457 memcpy(&greq.gr_group, group, group_len);
458 assert(greq.gr_group.ss_family != 0); /* the caller has set this */
459 greq.gr_interface = if_index;
460
461 return setsockopt(sock->bsd_socket, level,
462 join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (char*)&greq,
463 sizeof(greq));
464 #else
465 if (sock->type == AF_INET) {
466 struct ip_mreq mreq = {0};
467 struct in_addr addr;
468
469 assert(group_len == sizeof(struct sockaddr_in));
470
471 if (if_index != 0) {
472 if (php_if_index_to_addr4(if_index, sock, &addr) ==
473 FAILURE)
474 return -2; /* failure, but notice already emitted */
475 mreq.imr_interface = addr;
476 } else {
477 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
478 }
479 mreq.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
480 return setsockopt(sock->bsd_socket, level,
481 join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (char*)&mreq,
482 sizeof(mreq));
483 }
484 #if HAVE_IPV6
485 else if (sock->type == AF_INET6) {
486 struct ipv6_mreq mreq = {0};
487
488 assert(group_len == sizeof(struct sockaddr_in6));
489
490 mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)group)->sin6_addr;
491 mreq.ipv6mr_interface = if_index;
492
493 return setsockopt(sock->bsd_socket, level,
494 join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, (char*)&mreq,
495 sizeof(mreq));
496 }
497 #endif
498 else {
499 php_error_docref(NULL, E_WARNING,
500 "Option %s is inapplicable to this socket type",
501 join ? "MCAST_JOIN_GROUP" : "MCAST_LEAVE_GROUP");
502 return -2;
503 }
504 #endif
505 }
506
507 #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)508 static int _php_mcast_source_op(
509 php_socket *sock,
510 int level,
511 struct sockaddr *group,
512 socklen_t group_len,
513 struct sockaddr *source,
514 socklen_t source_len,
515 unsigned int if_index,
516 enum source_op sop)
517 {
518 #ifdef RFC3678_API
519 struct group_source_req gsreq = {0};
520
521 memcpy(&gsreq.gsr_group, group, group_len);
522 assert(gsreq.gsr_group.ss_family != 0);
523 memcpy(&gsreq.gsr_source, source, source_len);
524 assert(gsreq.gsr_source.ss_family != 0);
525 gsreq.gsr_interface = if_index;
526
527 return setsockopt(sock->bsd_socket, level,
528 _php_source_op_to_rfc3678_op(sop), (char*)&gsreq, sizeof(gsreq));
529 #else
530 if (sock->type == AF_INET) {
531 struct ip_mreq_source mreqs = {0};
532 struct in_addr addr;
533
534 mreqs.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
535 mreqs.imr_sourceaddr = ((struct sockaddr_in*)source)->sin_addr;
536
537 assert(group_len == sizeof(struct sockaddr_in));
538 assert(source_len == sizeof(struct sockaddr_in));
539
540 if (if_index != 0) {
541 if (php_if_index_to_addr4(if_index, sock, &addr) ==
542 FAILURE)
543 return -2; /* failure, but notice already emitted */
544 mreqs.imr_interface = addr;
545 } else {
546 mreqs.imr_interface.s_addr = htonl(INADDR_ANY);
547 }
548
549 return setsockopt(sock->bsd_socket, level,
550 _php_source_op_to_ipv4_op(sop), (char*)&mreqs, sizeof(mreqs));
551 }
552 #if HAVE_IPV6
553 else if (sock->type == AF_INET6) {
554 php_error_docref(NULL, E_WARNING,
555 "This platform does not support %s for IPv6 sockets",
556 _php_source_op_to_string(sop));
557 return -2;
558 }
559 #endif
560 else {
561 php_error_docref(NULL, E_WARNING,
562 "Option %s is inapplicable to this socket type",
563 _php_source_op_to_string(sop));
564 return -2;
565 }
566 #endif
567 }
568
569 #if RFC3678_API
_php_source_op_to_rfc3678_op(enum source_op sop)570 static int _php_source_op_to_rfc3678_op(enum source_op sop)
571 {
572 switch (sop) {
573 case JOIN_SOURCE:
574 return MCAST_JOIN_SOURCE_GROUP;
575 case LEAVE_SOURCE:
576 return MCAST_LEAVE_SOURCE_GROUP;
577 case BLOCK_SOURCE:
578 return MCAST_BLOCK_SOURCE;
579 case UNBLOCK_SOURCE:
580 return MCAST_UNBLOCK_SOURCE;
581 }
582
583 assert(0);
584 return 0;
585 }
586 #else
_php_source_op_to_string(enum source_op sop)587 static const char *_php_source_op_to_string(enum source_op sop)
588 {
589 switch (sop) {
590 case JOIN_SOURCE:
591 return "MCAST_JOIN_SOURCE_GROUP";
592 case LEAVE_SOURCE:
593 return "MCAST_LEAVE_SOURCE_GROUP";
594 case BLOCK_SOURCE:
595 return "MCAST_BLOCK_SOURCE";
596 case UNBLOCK_SOURCE:
597 return "MCAST_UNBLOCK_SOURCE";
598 }
599
600 assert(0);
601 return "";
602 }
603
_php_source_op_to_ipv4_op(enum source_op sop)604 static int _php_source_op_to_ipv4_op(enum source_op sop)
605 {
606 switch (sop) {
607 case JOIN_SOURCE:
608 return IP_ADD_SOURCE_MEMBERSHIP;
609 case LEAVE_SOURCE:
610 return IP_DROP_SOURCE_MEMBERSHIP;
611 case BLOCK_SOURCE:
612 return IP_BLOCK_SOURCE;
613 case UNBLOCK_SOURCE:
614 return IP_UNBLOCK_SOURCE;
615 }
616
617 assert(0);
618 return 0;
619 }
620 #endif
621
622 #endif /* HAS_MCAST_EXT */
623
624 #ifdef PHP_WIN32
php_if_index_to_addr4(unsigned if_index,php_socket * php_sock,struct in_addr * out_addr)625 int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr)
626 {
627 MIB_IPADDRTABLE *addr_table;
628 ULONG size;
629 DWORD retval;
630 DWORD i;
631
632 (void) php_sock; /* not necessary */
633
634 if (if_index == 0) {
635 out_addr->s_addr = INADDR_ANY;
636 return SUCCESS;
637 }
638
639 size = 4 * (sizeof *addr_table);
640 addr_table = emalloc(size);
641 retry:
642 retval = GetIpAddrTable(addr_table, &size, 0);
643 if (retval == ERROR_INSUFFICIENT_BUFFER) {
644 efree(addr_table);
645 addr_table = emalloc(size);
646 goto retry;
647 }
648 if (retval != NO_ERROR) {
649 efree(addr_table);
650 php_error_docref(NULL, E_WARNING,
651 "GetIpAddrTable failed with error %lu", retval);
652 return FAILURE;
653 }
654 for (i = 0; i < addr_table->dwNumEntries; i++) {
655 MIB_IPADDRROW r = addr_table->table[i];
656 if (r.dwIndex == if_index) {
657 out_addr->s_addr = r.dwAddr;
658 efree(addr_table);
659 return SUCCESS;
660 }
661 }
662 efree(addr_table);
663 php_error_docref(NULL, E_WARNING,
664 "No interface with index %u was found", if_index);
665 return FAILURE;
666 }
667
php_add4_to_if_index(struct in_addr * addr,php_socket * php_sock,unsigned * if_index)668 int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index)
669 {
670 MIB_IPADDRTABLE *addr_table;
671 ULONG size;
672 DWORD retval;
673 DWORD i;
674
675 (void) php_sock; /* not necessary */
676
677 if (addr->s_addr == INADDR_ANY) {
678 *if_index = 0;
679 return SUCCESS;
680 }
681
682 size = 4 * (sizeof *addr_table);
683 addr_table = emalloc(size);
684 retry:
685 retval = GetIpAddrTable(addr_table, &size, 0);
686 if (retval == ERROR_INSUFFICIENT_BUFFER) {
687 efree(addr_table);
688 addr_table = emalloc(size);
689 goto retry;
690 }
691 if (retval != NO_ERROR) {
692 efree(addr_table);
693 php_error_docref(NULL, E_WARNING,
694 "GetIpAddrTable failed with error %lu", retval);
695 return FAILURE;
696 }
697 for (i = 0; i < addr_table->dwNumEntries; i++) {
698 MIB_IPADDRROW r = addr_table->table[i];
699 if (r.dwAddr == addr->s_addr) {
700 *if_index = r.dwIndex;
701 efree(addr_table);
702 return SUCCESS;
703 }
704 }
705 efree(addr_table);
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