1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2014 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 #if HAVE_SOCKETS
28
29 #include "php_network.h"
30 #ifdef PHP_WIN32
31 # include "win32/inet.h"
32 # include <winsock2.h>
33 # include <windows.h>
34 # include <Ws2tcpip.h>
35 # include <Ws2ipdef.h>
36 # include "php_sockets.h"
37 # include "win32/sockets.h"
38 # define NTDDI_XP NTDDI_WINXP /* bug in SDK */
39 # include <IPHlpApi.h>
40 # undef NTDDI_XP
41 #else
42 #include <sys/socket.h>
43 #include <sys/ioctl.h>
44 #include <net/if.h>
45 #ifdef HAVE_SYS_SOCKIO_H
46 #include <sys/sockio.h>
47 #endif
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #endif
51
52 #include "php_sockets.h"
53 #include "multicast.h"
54 #include "main/php_network.h"
55
56
57 enum source_op {
58 JOIN_SOURCE,
59 LEAVE_SOURCE,
60 BLOCK_SOURCE,
61 UNBLOCK_SOURCE
62 };
63
64 static int _php_mcast_join_leave(php_socket *sock, int level, struct sockaddr *group, socklen_t group_len, unsigned int if_index, int join TSRMLS_DC);
65 #ifdef HAS_MCAST_EXT
66 static int _php_mcast_source_op(php_socket *sock, int level, struct sockaddr *group, socklen_t group_len, struct sockaddr *source, socklen_t source_len, unsigned int if_index, enum source_op sop TSRMLS_DC);
67 #endif
68
69 #ifdef RFC3678_API
70 static int _php_source_op_to_rfc3678_op(enum source_op sop);
71 #elif HAS_MCAST_EXT
72 static const char *_php_source_op_to_string(enum source_op sop);
73 static int _php_source_op_to_ipv4_op(enum source_op sop);
74 #endif
75
php_mcast_join(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,unsigned int if_index TSRMLS_DC)76 int php_mcast_join(
77 php_socket *sock,
78 int level,
79 struct sockaddr *group,
80 socklen_t group_len,
81 unsigned int if_index TSRMLS_DC)
82 {
83 return _php_mcast_join_leave(sock, level, group, group_len, if_index, 1 TSRMLS_CC);
84 }
85
php_mcast_leave(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,unsigned int if_index TSRMLS_DC)86 int php_mcast_leave(
87 php_socket *sock,
88 int level,
89 struct sockaddr *group,
90 socklen_t group_len,
91 unsigned int if_index TSRMLS_DC)
92 {
93 return _php_mcast_join_leave(sock, level, group, group_len, if_index, 0 TSRMLS_CC);
94 }
95
96 #ifdef HAS_MCAST_EXT
php_mcast_join_source(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,struct sockaddr * source,socklen_t source_len,unsigned int if_index TSRMLS_DC)97 int php_mcast_join_source(
98 php_socket *sock,
99 int level,
100 struct sockaddr *group,
101 socklen_t group_len,
102 struct sockaddr *source,
103 socklen_t source_len,
104 unsigned int if_index TSRMLS_DC)
105 {
106 return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, JOIN_SOURCE TSRMLS_CC);
107 }
108
php_mcast_leave_source(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,struct sockaddr * source,socklen_t source_len,unsigned int if_index TSRMLS_DC)109 int php_mcast_leave_source(
110 php_socket *sock,
111 int level,
112 struct sockaddr *group,
113 socklen_t group_len,
114 struct sockaddr *source,
115 socklen_t source_len,
116 unsigned int if_index TSRMLS_DC)
117 {
118 return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, LEAVE_SOURCE TSRMLS_CC);
119 }
120
php_mcast_block_source(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,struct sockaddr * source,socklen_t source_len,unsigned int if_index TSRMLS_DC)121 int php_mcast_block_source(
122 php_socket *sock,
123 int level,
124 struct sockaddr *group,
125 socklen_t group_len,
126 struct sockaddr *source,
127 socklen_t source_len,
128 unsigned int if_index TSRMLS_DC)
129 {
130 return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, BLOCK_SOURCE TSRMLS_CC);
131 }
132
php_mcast_unblock_source(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,struct sockaddr * source,socklen_t source_len,unsigned int if_index TSRMLS_DC)133 int php_mcast_unblock_source(
134 php_socket *sock,
135 int level,
136 struct sockaddr *group,
137 socklen_t group_len,
138 struct sockaddr *source,
139 socklen_t source_len,
140 unsigned int if_index TSRMLS_DC)
141 {
142 return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, UNBLOCK_SOURCE TSRMLS_CC);
143 }
144 #endif /* HAS_MCAST_EXT */
145
146
_php_mcast_join_leave(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,unsigned int if_index,int join TSRMLS_DC)147 static int _php_mcast_join_leave(
148 php_socket *sock,
149 int level,
150 struct sockaddr *group, /* struct sockaddr_in/sockaddr_in6 */
151 socklen_t group_len,
152 unsigned int if_index,
153 int join TSRMLS_DC)
154 {
155 #ifdef RFC3678_API
156 struct group_req greq = {0};
157
158 memcpy(&greq.gr_group, group, group_len);
159 assert(greq.gr_group.ss_family != 0); /* the caller has set this */
160 greq.gr_interface = if_index;
161
162 return setsockopt(sock->bsd_socket, level,
163 join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (char*)&greq,
164 sizeof(greq));
165 #else
166 if (sock->type == AF_INET) {
167 struct ip_mreq mreq = {0};
168 struct in_addr addr;
169
170 assert(group_len == sizeof(struct sockaddr_in));
171
172 if (if_index != 0) {
173 if (php_if_index_to_addr4(if_index, sock, &addr TSRMLS_CC) ==
174 FAILURE)
175 return -2; /* failure, but notice already emitted */
176 mreq.imr_interface = addr;
177 } else {
178 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
179 }
180 mreq.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
181 return setsockopt(sock->bsd_socket, level,
182 join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (char*)&mreq,
183 sizeof(mreq));
184 }
185 #if HAVE_IPV6
186 else if (sock->type == AF_INET6) {
187 struct ipv6_mreq mreq = {0};
188
189 assert(group_len == sizeof(struct sockaddr_in6));
190
191 mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)group)->sin6_addr;
192 mreq.ipv6mr_interface = if_index;
193
194 return setsockopt(sock->bsd_socket, level,
195 join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, (char*)&mreq,
196 sizeof(mreq));
197 }
198 #endif
199 else {
200 php_error_docref(NULL TSRMLS_CC, E_WARNING,
201 "Option %s is inapplicable to this socket type",
202 join ? "MCAST_JOIN_GROUP" : "MCAST_LEAVE_GROUP");
203 return -2;
204 }
205 #endif
206 }
207
208 #ifdef HAS_MCAST_EXT
_php_mcast_source_op(php_socket * sock,int level,struct sockaddr * group,socklen_t group_len,struct sockaddr * source,socklen_t source_len,unsigned int if_index,enum source_op sop TSRMLS_DC)209 static int _php_mcast_source_op(
210 php_socket *sock,
211 int level,
212 struct sockaddr *group,
213 socklen_t group_len,
214 struct sockaddr *source,
215 socklen_t source_len,
216 unsigned int if_index,
217 enum source_op sop TSRMLS_DC)
218 {
219 #ifdef RFC3678_API
220 struct group_source_req gsreq = {0};
221
222 memcpy(&gsreq.gsr_group, group, group_len);
223 assert(gsreq.gsr_group.ss_family != 0);
224 memcpy(&gsreq.gsr_source, source, source_len);
225 assert(gsreq.gsr_source.ss_family != 0);
226 gsreq.gsr_interface = if_index;
227
228 return setsockopt(sock->bsd_socket, level,
229 _php_source_op_to_rfc3678_op(sop), (char*)&gsreq, sizeof(gsreq));
230 #else
231 if (sock->type == AF_INET) {
232 struct ip_mreq_source mreqs = {0};
233 struct in_addr addr;
234
235 mreqs.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
236 mreqs.imr_sourceaddr = ((struct sockaddr_in*)source)->sin_addr;
237
238 assert(group_len == sizeof(struct sockaddr_in));
239 assert(source_len == sizeof(struct sockaddr_in));
240
241 if (if_index != 0) {
242 if (php_if_index_to_addr4(if_index, sock, &addr TSRMLS_CC) ==
243 FAILURE)
244 return -2; /* failure, but notice already emitted */
245 mreqs.imr_interface = addr;
246 } else {
247 mreqs.imr_interface.s_addr = htonl(INADDR_ANY);
248 }
249
250 return setsockopt(sock->bsd_socket, level,
251 _php_source_op_to_ipv4_op(sop), (char*)&mreqs, sizeof(mreqs));
252 }
253 #if HAVE_IPV6
254 else if (sock->type == AF_INET6) {
255 php_error_docref(NULL TSRMLS_CC, E_WARNING,
256 "This platform does not support %s for IPv6 sockets",
257 _php_source_op_to_string(sop));
258 return -2;
259 }
260 #endif
261 else {
262 php_error_docref(NULL TSRMLS_CC, E_WARNING,
263 "Option %s is inapplicable to this socket type",
264 _php_source_op_to_string(sop));
265 return -2;
266 }
267 #endif
268 }
269
270 #if RFC3678_API
_php_source_op_to_rfc3678_op(enum source_op sop)271 static int _php_source_op_to_rfc3678_op(enum source_op sop)
272 {
273 switch (sop) {
274 case JOIN_SOURCE:
275 return MCAST_JOIN_SOURCE_GROUP;
276 case LEAVE_SOURCE:
277 return MCAST_LEAVE_SOURCE_GROUP;
278 case BLOCK_SOURCE:
279 return MCAST_BLOCK_SOURCE;
280 case UNBLOCK_SOURCE:
281 return MCAST_UNBLOCK_SOURCE;
282 }
283
284 assert(0);
285 return 0;
286 }
287 #else
_php_source_op_to_string(enum source_op sop)288 static const char *_php_source_op_to_string(enum source_op sop)
289 {
290 switch (sop) {
291 case JOIN_SOURCE:
292 return "MCAST_JOIN_SOURCE_GROUP";
293 case LEAVE_SOURCE:
294 return "MCAST_LEAVE_SOURCE_GROUP";
295 case BLOCK_SOURCE:
296 return "MCAST_BLOCK_SOURCE";
297 case UNBLOCK_SOURCE:
298 return "MCAST_UNBLOCK_SOURCE";
299 }
300
301 assert(0);
302 return "";
303 }
304
_php_source_op_to_ipv4_op(enum source_op sop)305 static int _php_source_op_to_ipv4_op(enum source_op sop)
306 {
307 switch (sop) {
308 case JOIN_SOURCE:
309 return IP_ADD_SOURCE_MEMBERSHIP;
310 case LEAVE_SOURCE:
311 return IP_DROP_SOURCE_MEMBERSHIP;
312 case BLOCK_SOURCE:
313 return IP_BLOCK_SOURCE;
314 case UNBLOCK_SOURCE:
315 return IP_UNBLOCK_SOURCE;
316 }
317
318 assert(0);
319 return 0;
320 }
321 #endif
322
323 #endif /* HAS_MCAST_EXT */
324
325 #if PHP_WIN32
php_if_index_to_addr4(unsigned if_index,php_socket * php_sock,struct in_addr * out_addr TSRMLS_DC)326 int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr TSRMLS_DC)
327 {
328 MIB_IPADDRTABLE *addr_table;
329 ULONG size;
330 DWORD retval;
331 DWORD i;
332
333 (void) php_sock; /* not necessary */
334
335 if (if_index == 0) {
336 out_addr->s_addr = INADDR_ANY;
337 return SUCCESS;
338 }
339
340 size = 4 * (sizeof *addr_table);
341 addr_table = emalloc(size);
342 retry:
343 retval = GetIpAddrTable(addr_table, &size, 0);
344 if (retval == ERROR_INSUFFICIENT_BUFFER) {
345 efree(addr_table);
346 addr_table = emalloc(size);
347 goto retry;
348 }
349 if (retval != NO_ERROR) {
350 php_error_docref(NULL TSRMLS_CC, E_WARNING,
351 "GetIpAddrTable failed with error %lu", retval);
352 return FAILURE;
353 }
354 for (i = 0; i < addr_table->dwNumEntries; i++) {
355 MIB_IPADDRROW r = addr_table->table[i];
356 if (r.dwIndex == if_index) {
357 out_addr->s_addr = r.dwAddr;
358 return SUCCESS;
359 }
360 }
361 php_error_docref(NULL TSRMLS_CC, E_WARNING,
362 "No interface with index %u was found", if_index);
363 return FAILURE;
364 }
365
php_add4_to_if_index(struct in_addr * addr,php_socket * php_sock,unsigned * if_index TSRMLS_DC)366 int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index TSRMLS_DC)
367 {
368 MIB_IPADDRTABLE *addr_table;
369 ULONG size;
370 DWORD retval;
371 DWORD i;
372
373 (void) php_sock; /* not necessary */
374
375 if (addr->s_addr == INADDR_ANY) {
376 *if_index = 0;
377 return SUCCESS;
378 }
379
380 size = 4 * (sizeof *addr_table);
381 addr_table = emalloc(size);
382 retry:
383 retval = GetIpAddrTable(addr_table, &size, 0);
384 if (retval == ERROR_INSUFFICIENT_BUFFER) {
385 efree(addr_table);
386 addr_table = emalloc(size);
387 goto retry;
388 }
389 if (retval != NO_ERROR) {
390 php_error_docref(NULL TSRMLS_CC, E_WARNING,
391 "GetIpAddrTable failed with error %lu", retval);
392 return FAILURE;
393 }
394 for (i = 0; i < addr_table->dwNumEntries; i++) {
395 MIB_IPADDRROW r = addr_table->table[i];
396 if (r.dwAddr == addr->s_addr) {
397 *if_index = r.dwIndex;
398 return SUCCESS;
399 }
400 }
401
402 {
403 char addr_str[17] = {0};
404 inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
405 php_error_docref(NULL TSRMLS_CC, E_WARNING,
406 "The interface with IP address %s was not found", addr_str);
407 }
408 return FAILURE;
409 }
410
411 #else
412
php_if_index_to_addr4(unsigned if_index,php_socket * php_sock,struct in_addr * out_addr TSRMLS_DC)413 int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr TSRMLS_DC)
414 {
415 struct ifreq if_req;
416
417 if (if_index == 0) {
418 out_addr->s_addr = INADDR_ANY;
419 return SUCCESS;
420 }
421
422 #if !defined(ifr_ifindex) && defined(ifr_index)
423 #define ifr_ifindex ifr_index
424 #endif
425
426 #if defined(SIOCGIFNAME)
427 if_req.ifr_ifindex = if_index;
428 if (ioctl(php_sock->bsd_socket, SIOCGIFNAME, &if_req) == -1) {
429 #elif defined(HAVE_IF_INDEXTONAME)
430 if (if_indextoname(if_index, if_req.ifr_name) == NULL) {
431 #else
432 #error Neither SIOCGIFNAME nor if_indextoname are available
433 #endif
434 php_error_docref(NULL TSRMLS_CC, E_WARNING,
435 "Failed obtaining address for interface %u: error %d", if_index, errno);
436 return FAILURE;
437 }
438
439 if (ioctl(php_sock->bsd_socket, SIOCGIFADDR, &if_req) == -1) {
440 php_error_docref(NULL TSRMLS_CC, E_WARNING,
441 "Failed obtaining address for interface %u: error %d", if_index, errno);
442 return FAILURE;
443 }
444
445 memcpy(out_addr, &((struct sockaddr_in *) &if_req.ifr_addr)->sin_addr,
446 sizeof *out_addr);
447 return SUCCESS;
448 }
449
450 int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index TSRMLS_DC)
451 {
452 struct ifconf if_conf = {0};
453 char *buf = NULL,
454 *p;
455 int size = 0,
456 lastsize = 0;
457 size_t entry_len;
458
459 if (addr->s_addr == INADDR_ANY) {
460 *if_index = 0;
461 return SUCCESS;
462 }
463
464 for(;;) {
465 size += 5 * sizeof(struct ifreq);
466 buf = ecalloc(size, 1);
467 if_conf.ifc_len = size;
468 if_conf.ifc_buf = buf;
469
470 if (ioctl(php_sock->bsd_socket, SIOCGIFCONF, (char*)&if_conf) == -1 &&
471 (errno != EINVAL || lastsize != 0)) {
472 php_error_docref(NULL TSRMLS_CC, E_WARNING,
473 "Failed obtaining interfaces list: error %d", errno);
474 goto err;
475 }
476
477 if (if_conf.ifc_len == lastsize)
478 /* not increasing anymore */
479 break;
480 else {
481 lastsize = if_conf.ifc_len;
482 efree(buf);
483 buf = NULL;
484 }
485 }
486
487 for (p = if_conf.ifc_buf;
488 p < if_conf.ifc_buf + if_conf.ifc_len;
489 p += entry_len) {
490 struct ifreq *cur_req;
491
492 /* let's hope the pointer is aligned */
493 cur_req = (struct ifreq*) p;
494
495 #ifdef HAVE_SOCKADDR_SA_LEN
496 entry_len = cur_req->ifr_addr.sa_len + sizeof(cur_req->ifr_name);
497 #else
498 /* if there's no sa_len, assume the ifr_addr field is a sockaddr */
499 entry_len = sizeof(struct sockaddr) + sizeof(cur_req->ifr_name);
500 #endif
501 entry_len = MAX(entry_len, sizeof(*cur_req));
502
503 if ((((struct sockaddr*)&cur_req->ifr_addr)->sa_family == AF_INET) &&
504 (((struct sockaddr_in*)&cur_req->ifr_addr)->sin_addr.s_addr ==
505 addr->s_addr)) {
506 #if defined(SIOCGIFINDEX)
507 if (ioctl(php_sock->bsd_socket, SIOCGIFINDEX, (char*)cur_req)
508 == -1) {
509 #elif defined(HAVE_IF_NAMETOINDEX)
510 unsigned index_tmp;
511 if ((index_tmp = if_nametoindex(cur_req->ifr_name)) == 0) {
512 #else
513 #error Neither SIOCGIFINDEX nor if_nametoindex are available
514 #endif
515 php_error_docref(NULL TSRMLS_CC, E_WARNING,
516 "Error converting interface name to index: error %d",
517 errno);
518 goto err;
519 } else {
520 #if defined(SIOCGIFINDEX)
521 *if_index = cur_req->ifr_ifindex;
522 #else
523 *if_index = index_tmp;
524 #endif
525 efree(buf);
526 return SUCCESS;
527 }
528 }
529 }
530
531 {
532 char addr_str[17] = {0};
533 inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
534 php_error_docref(NULL TSRMLS_CC, E_WARNING,
535 "The interface with IP address %s was not found", addr_str);
536 }
537
538 err:
539 if (buf != NULL)
540 efree(buf);
541 return FAILURE;
542 }
543 #endif
544
545 #endif /* HAVE_SOCKETS */
546