Annotation of embedaddon/php/ext/sockets/multicast.c, revision 1.1.1.2

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | PHP Version 5                                                        |
                      4:    +----------------------------------------------------------------------+
1.1.1.2 ! misho       5:    | Copyright (c) 1997-2013 The PHP Group                                |
1.1       misho       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 */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>