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: multicast.c,v 1.1.1.3 2014/06/15 20:03:55 misho Exp $ */
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:
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:
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
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:
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:
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:
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:
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
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
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
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:
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
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:
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:
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 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>