Return to multicast.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / sockets |
1.1 ! misho 1: /* ! 2: +----------------------------------------------------------------------+ ! 3: | PHP Version 5 | ! 4: +----------------------------------------------------------------------+ ! 5: | Copyright (c) 1997-2012 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: ! 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 */