File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / sockets / multicast.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:03:55 2014 UTC (10 years, 4 months ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29, HEAD
php 5.4.29

    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>