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