]> granicus.if.org Git - php/commitdiff
Merge branch 'PHP-5.4' into PHP-5.5
authorMichael Wallner <mike@php.net>
Wed, 2 Oct 2013 13:55:38 +0000 (15:55 +0200)
committerMichael Wallner <mike@php.net>
Wed, 2 Oct 2013 13:55:38 +0000 (15:55 +0200)
* PHP-5.4:
  fix bug #65808  the socket_connect() won't work with IPv6 address
  5.4.22-dev now

Conflicts:
configure.in
ext/sockets/sockets.c
main/php_version.h

1  2 
ext/sockets/multicast.c
ext/sockets/multicast.h
ext/sockets/sockaddr_conv.c

index 7466c6266e7f1fea6fade0324290e8838c8e025e,43b6f7dddf4b4fe9d23771f8d34cd07b8be537c0..ecf3a65a325e6d5c50fd1be8b38bea3a29027c53
@@@ -63,309 -73,6 +63,317 @@@ static const char *_php_source_op_to_st
  static int _php_source_op_to_ipv4_op(enum source_op sop);
  #endif
  
- #if HAVE_IF_NAMETOINDEX
-               unsigned int ind;
++int php_string_to_if_index(const char *val, unsigned *out TSRMLS_DC)
++{
++#if HAVE_IF_NAMETOINDEX
++      unsigned int ind;
++
++      ind = if_nametoindex(val);
++      if (ind == 0) {
++              php_error_docref(NULL TSRMLS_CC, E_WARNING,
++                      "no interface with name \"%s\" could be found", val);
++              return FAILURE;
++      } else {
++              *out = ind;
++              return SUCCESS;
++      }
++#else
++      php_error_docref(NULL TSRMLS_CC, E_WARNING,
++                      "this platform does not support looking up an interface by "
++                      "name, an integer interface index must be supplied instead");
++      return FAILURE;
++#endif
++}
++
 +static int php_get_if_index_from_zval(zval *val, unsigned *out TSRMLS_DC)
 +{
 +      int ret;
 +
 +      if (Z_TYPE_P(val) == IS_LONG) {
 +              if (Z_LVAL_P(val) < 0 || Z_LVAL_P(val) > UINT_MAX) {
 +                      php_error_docref(NULL TSRMLS_CC, E_WARNING,
 +                              "the interface index cannot be negative or larger than %u;"
 +                              " given %ld", UINT_MAX, Z_LVAL_P(val));
 +                      ret = FAILURE;
 +              } else {
 +                      *out = Z_LVAL_P(val);
 +                      ret = SUCCESS;
 +              }
 +      } else {
-               ind = if_nametoindex(Z_STRVAL_P(val));
-               if (ind == 0) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                               "no interface with name \"%s\" could be found", Z_STRVAL_P(val));
-                       ret = FAILURE;
-               } else {
-                       *out = ind;
-                       ret = SUCCESS;
-               }
 +              zval_add_ref(&val);
 +              convert_to_string_ex(&val);
- #else
-               php_error_docref(NULL TSRMLS_CC, E_WARNING,
-                               "this platform does not support looking up an interface by "
-                               "name, an integer interface index must be supplied instead");
-               ret = FAILURE;
- #endif
++              ret = php_string_to_if_index(Z_STRVAL_P(val), out TSRMLS_CC);
 +              zval_ptr_dtor(&val);
 +      }
 +
 +      return ret;
 +}
 +
++
++
 +static int php_get_if_index_from_array(const HashTable *ht, const char *key,
 +      php_socket *sock, unsigned int *if_index TSRMLS_DC)
 +{
 +      zval **val;
 +
 +      if (zend_hash_find(ht, key, strlen(key) + 1, (void **)&val) == FAILURE) {
 +              *if_index = 0; /* default: 0 */
 +              return SUCCESS;
 +      }
 +
 +      return php_get_if_index_from_zval(*val, if_index TSRMLS_CC);
 +}
 +
 +static int php_get_address_from_array(const HashTable *ht, const char *key,
 +      php_socket *sock, php_sockaddr_storage *ss, socklen_t *ss_len TSRMLS_DC)
 +{
 +      zval **val,
 +               *valcp;
 +
 +      if (zend_hash_find(ht, key, strlen(key) + 1, (void **)&val) == FAILURE) {
 +              php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", key);
 +              return FAILURE;
 +      }
 +      valcp = *val;
 +      zval_add_ref(&valcp);
 +      convert_to_string_ex(val);
 +      if (!php_set_inet46_addr(ss, ss_len, Z_STRVAL_P(valcp), sock TSRMLS_CC)) {
 +              zval_ptr_dtor(&valcp);
 +              return FAILURE;
 +      }
 +      zval_ptr_dtor(&valcp);
 +      return SUCCESS;
 +}
 +
 +static int php_do_mcast_opt(php_socket *php_sock, int level, int optname, zval **arg4 TSRMLS_DC)
 +{
 +      HashTable                               *opt_ht;
 +      unsigned int                    if_index;
 +      int                                             retval;
 +      int (*mcast_req_fun)(php_socket *, int, struct sockaddr *, socklen_t,
 +              unsigned TSRMLS_DC);
 +#ifdef HAS_MCAST_EXT
 +      int (*mcast_sreq_fun)(php_socket *, int, struct sockaddr *, socklen_t,
 +              struct sockaddr *, socklen_t, unsigned TSRMLS_DC);
 +#endif
 +
 +      switch (optname) {
 +      case PHP_MCAST_JOIN_GROUP:
 +              mcast_req_fun = &php_mcast_join;
 +              goto mcast_req_fun;
 +      case PHP_MCAST_LEAVE_GROUP:
 +              {
 +                      php_sockaddr_storage    group = {0};
 +                      socklen_t                               glen;
 +
 +                      mcast_req_fun = &php_mcast_leave;
 +mcast_req_fun:
 +                      convert_to_array_ex(arg4);
 +                      opt_ht = HASH_OF(*arg4);
 +
 +                      if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
 +                              &glen TSRMLS_CC) == FAILURE) {
 +                                      return FAILURE;
 +                      }
 +                      if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
 +                              &if_index TSRMLS_CC) == FAILURE) {
 +                                      return FAILURE;
 +                      }
 +
 +                      retval = mcast_req_fun(php_sock, level, (struct sockaddr*)&group,
 +                              glen, if_index TSRMLS_CC);
 +                      break;
 +              }
 +
 +#ifdef HAS_MCAST_EXT
 +      case PHP_MCAST_BLOCK_SOURCE:
 +              mcast_sreq_fun = &php_mcast_block_source;
 +              goto mcast_sreq_fun;
 +      case PHP_MCAST_UNBLOCK_SOURCE:
 +              mcast_sreq_fun = &php_mcast_unblock_source;
 +              goto mcast_sreq_fun;
 +      case PHP_MCAST_JOIN_SOURCE_GROUP:
 +              mcast_sreq_fun = &php_mcast_join_source;
 +              goto mcast_sreq_fun;
 +      case PHP_MCAST_LEAVE_SOURCE_GROUP:
 +              {
 +                      php_sockaddr_storage    group = {0},
 +                                                                      source = {0};
 +                      socklen_t                               glen,
 +                                                                      slen;
 +
 +                      mcast_sreq_fun = &php_mcast_leave_source;
 +              mcast_sreq_fun:
 +                      convert_to_array_ex(arg4);
 +                      opt_ht = HASH_OF(*arg4);
 +
 +                      if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
 +                                      &glen TSRMLS_CC) == FAILURE) {
 +                              return FAILURE;
 +                      }
 +                      if (php_get_address_from_array(opt_ht, "source", php_sock, &source,
 +                                      &slen TSRMLS_CC) == FAILURE) {
 +                              return FAILURE;
 +                      }
 +                      if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
 +                                      &if_index TSRMLS_CC) == FAILURE) {
 +                              return FAILURE;
 +                      }
 +
 +                      retval = mcast_sreq_fun(php_sock, level, (struct sockaddr*)&group,
 +                                      glen, (struct sockaddr*)&source, slen, if_index TSRMLS_CC);
 +                      break;
 +              }
 +#endif
 +      default:
 +              php_error_docref(NULL TSRMLS_CC, E_WARNING,
 +                      "unexpected option in php_do_mcast_opt (level %d, option %d). "
 +                      "This is a bug.", level, optname);
 +              return FAILURE;
 +      }
 +
 +      if (retval != 0) {
 +              if (retval != -2) { /* error, but message already emitted */
 +                      PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
 +              }
 +              return FAILURE;
 +      }
 +      return SUCCESS;
 +}
 +
 +int php_do_setsockopt_ip_mcast(php_socket *php_sock,
 +                                                         int level,
 +                                                         int optname,
 +                                                         zval **arg4 TSRMLS_DC)
 +{
 +      unsigned int    if_index;
 +      struct in_addr  if_addr;
 +      void                    *opt_ptr;
 +      socklen_t               optlen;
 +      unsigned char   ipv4_mcast_ttl_lback;
 +      int                             retval;
 +
 +      switch (optname) {
 +      case PHP_MCAST_JOIN_GROUP:
 +      case PHP_MCAST_LEAVE_GROUP:
 +#ifdef HAS_MCAST_EXT
 +      case PHP_MCAST_BLOCK_SOURCE:
 +      case PHP_MCAST_UNBLOCK_SOURCE:
 +      case PHP_MCAST_JOIN_SOURCE_GROUP:
 +      case PHP_MCAST_LEAVE_SOURCE_GROUP:
 +#endif
 +              if (php_do_mcast_opt(php_sock, level, optname, arg4 TSRMLS_CC) == FAILURE) {
 +                      return FAILURE;
 +              } else {
 +                      return SUCCESS;
 +              }
 +
 +      case IP_MULTICAST_IF:
 +              if (php_get_if_index_from_zval(*arg4, &if_index TSRMLS_CC) == FAILURE) {
 +                      return FAILURE;
 +              }
 +
 +              if (php_if_index_to_addr4(if_index, php_sock, &if_addr TSRMLS_CC) == FAILURE) {
 +                      return FAILURE;
 +              }
 +              opt_ptr = &if_addr;
 +              optlen  = sizeof(if_addr);
 +              goto dosockopt;
 +
 +      case IP_MULTICAST_LOOP:
 +              convert_to_boolean_ex(arg4);
 +              goto ipv4_loop_ttl;
 +
 +      case IP_MULTICAST_TTL:
 +              convert_to_long_ex(arg4);
 +              if (Z_LVAL_PP(arg4) < 0L || Z_LVAL_PP(arg4) > 255L) {
 +                      php_error_docref(NULL TSRMLS_CC, E_WARNING,
 +                                      "Expected a value between 0 and 255");
 +                      return FAILURE;
 +              }
 +ipv4_loop_ttl:
 +              ipv4_mcast_ttl_lback = (unsigned char) Z_LVAL_PP(arg4);
 +              opt_ptr = &ipv4_mcast_ttl_lback;
 +              optlen  = sizeof(ipv4_mcast_ttl_lback);
 +              goto dosockopt;
 +      }
 +
 +      return 1;
 +
 +dosockopt:
 +      retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
 +      if (retval != 0) {
 +              PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
 +              return FAILURE;
 +      }
 +
 +      return SUCCESS;
 +}
 +
 +int php_do_setsockopt_ipv6_mcast(php_socket *php_sock,
 +                                                               int level,
 +                                                               int optname,
 +                                                               zval **arg4 TSRMLS_DC)
 +{
 +      unsigned int    if_index;
 +      void                    *opt_ptr;
 +      socklen_t               optlen;
 +      int                             ov;
 +      int                             retval;
 +
 +      switch (optname) {
 +      case PHP_MCAST_JOIN_GROUP:
 +      case PHP_MCAST_LEAVE_GROUP:
 +#ifdef HAS_MCAST_EXT
 +      case PHP_MCAST_BLOCK_SOURCE:
 +      case PHP_MCAST_UNBLOCK_SOURCE:
 +      case PHP_MCAST_JOIN_SOURCE_GROUP:
 +      case PHP_MCAST_LEAVE_SOURCE_GROUP:
 +#endif
 +              if (php_do_mcast_opt(php_sock, level, optname, arg4 TSRMLS_CC) == FAILURE) {
 +                      return FAILURE;
 +              } else {
 +                      return SUCCESS;
 +              }
 +
 +      case IPV6_MULTICAST_IF:
 +              if (php_get_if_index_from_zval(*arg4, &if_index TSRMLS_CC) == FAILURE) {
 +                      return FAILURE;
 +              }
 +
 +              opt_ptr = &if_index;
 +              optlen  = sizeof(if_index);
 +              goto dosockopt;
 +
 +      case IPV6_MULTICAST_LOOP:
 +              convert_to_boolean_ex(arg4);
 +              goto ipv6_loop_hops;
 +      case IPV6_MULTICAST_HOPS:
 +              convert_to_long_ex(arg4);
 +              if (Z_LVAL_PP(arg4) < -1L || Z_LVAL_PP(arg4) > 255L) {
 +                      php_error_docref(NULL TSRMLS_CC, E_WARNING,
 +                                      "Expected a value between -1 and 255");
 +                      return FAILURE;
 +              }
 +ipv6_loop_hops:
 +              ov = (int) Z_LVAL_PP(arg4);
 +              opt_ptr = &ov;
 +              optlen  = sizeof(ov);
 +              goto dosockopt;
 +      }
 +
 +      return 1; /* not handled */
 +
 +dosockopt:
 +      retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
 +      if (retval != 0) {
 +              PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
 +              return FAILURE;
 +      }
 +
 +      return SUCCESS;
 +}
 +
  int php_mcast_join(
        php_socket *sock,
        int level,
index 3614306bbb2195c36b8fd295736f51be894b087a,f46a6a8f61b7d33b8f40f21de7895837acb410e0..81a1bca799322e94eaf4bdca618ceec5950d038b
@@@ -65,6 -39,6 +65,8 @@@ int php_add4_to_if_index
          php_socket *php_sock,
          unsigned *if_index TSRMLS_DC);
  
++int php_string_to_if_index(const char *val, unsigned *out TSRMLS_DC);
++
  int php_mcast_join(
        php_socket *sock,
        int level,
index a40b6b4936cbbd8c8228beff47fdc35214451632,0000000000000000000000000000000000000000..64523c3191d0b9d0496e6efd7d7f03a3e7e035e5
mode 100644,000000..100644
--- /dev/null
@@@ -1,119 -1,0 +1,136 @@@
 +#include <php.h>
 +#include <php_network.h>
 +#include "php_sockets.h"
 +
 +#ifdef PHP_WIN32
 +#include "windows_common.h"
 +#else
 +#include <netdb.h>
 +#include <arpa/inet.h>
 +#endif
 +
 +#if HAVE_IPV6
 +/* Sets addr by hostname, or by ip in string form (AF_INET6) */
 +int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
 +{
 +      struct in6_addr tmp;
 +#if HAVE_GETADDRINFO
 +      struct addrinfo hints;
 +      struct addrinfo *addrinfo = NULL;
 +#endif
++      char *scope = strchr(string, '%');
 +
 +      if (inet_pton(AF_INET6, string, &tmp)) {
 +              memcpy(&(sin6->sin6_addr.s6_addr), &(tmp.s6_addr), sizeof(struct in6_addr));
 +      } else {
 +#if HAVE_GETADDRINFO
 +
 +              memset(&hints, 0, sizeof(struct addrinfo));
 +              hints.ai_family = AF_INET6;
 +              hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
 +              getaddrinfo(string, NULL, &hints, &addrinfo);
 +              if (!addrinfo) {
 +#ifdef PHP_WIN32
 +                      PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError());
 +#else
 +                      PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno));
 +#endif
 +                      return 0;
 +              }
 +              if (addrinfo->ai_family != PF_INET6 || addrinfo->ai_addrlen != sizeof(struct sockaddr_in6)) {
 +                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: Non AF_INET6 domain returned on AF_INET6 socket");
 +                      freeaddrinfo(addrinfo);
 +                      return 0;
 +              }
 +
 +              memcpy(&(sin6->sin6_addr.s6_addr), ((struct sockaddr_in6*)(addrinfo->ai_addr))->sin6_addr.s6_addr, sizeof(struct in6_addr));
 +              freeaddrinfo(addrinfo);
 +
 +#else
 +              /* No IPv6 specific hostname resolution is available on this system? */
 +              php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: getaddrinfo() not available on this system");
 +              return 0;
 +#endif
 +
 +      }
 +
++      if (scope++) {
++              long lval = 0;
++              double dval = 0;
++              unsigned scope_id = 0;
++
++              if (IS_LONG == is_numeric_string(scope, strlen(scope), &lval, &dval, 0)) {
++                      if (lval > 0 && lval <= UINT_MAX) {
++                              scope_id = lval;
++                      }
++              } else {
++                      php_string_to_if_index(scope, &scope_id TSRMLS_CC);
++              }
++
++              sin6->sin6_scope_id = scope_id;
++      }
++
 +      return 1;
 +}
 +/* }}} */
 +#endif
 +
 +/* Sets addr by hostname, or by ip in string form (AF_INET)  */
 +int php_set_inet_addr(struct sockaddr_in *sin, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
 +{
 +      struct in_addr tmp;
 +      struct hostent *host_entry;
 +
 +      if (inet_aton(string, &tmp)) {
 +              sin->sin_addr.s_addr = tmp.s_addr;
 +      } else {
 +              if (! (host_entry = gethostbyname(string))) {
 +                      /* Note: < -10000 indicates a host lookup error */
 +#ifdef PHP_WIN32
 +                      PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError());
 +#else
 +                      PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno));
 +#endif
 +                      return 0;
 +              }
 +              if (host_entry->h_addrtype != AF_INET) {
 +                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: Non AF_INET domain returned on AF_INET socket");
 +                      return 0;
 +              }
 +              memcpy(&(sin->sin_addr.s_addr), host_entry->h_addr_list[0], host_entry->h_length);
 +      }
 +
 +      return 1;
 +}
 +/* }}} */
 +
 +/* Sets addr by hostname or by ip in string form (AF_INET or AF_INET6,
 + * depending on the socket) */
 +int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
 +{
 +      if (php_sock->type == AF_INET) {
 +              struct sockaddr_in t = {0};
 +              if (php_set_inet_addr(&t, string, php_sock TSRMLS_CC)) {
 +                      memcpy(ss, &t, sizeof t);
 +                      ss->ss_family = AF_INET;
 +                      *ss_len = sizeof(t);
 +                      return 1;
 +              }
 +      }
 +#if HAVE_IPV6
 +      else if (php_sock->type == AF_INET6) {
 +              struct sockaddr_in6 t = {0};
 +              if (php_set_inet6_addr(&t, string, php_sock TSRMLS_CC)) {
 +                      memcpy(ss, &t, sizeof t);
 +                      ss->ss_family = AF_INET6;
 +                      *ss_len = sizeof(t);
 +                      return 1;
 +              }
 +      }
 +#endif
 +      else {
 +              php_error_docref(NULL TSRMLS_CC, E_WARNING,
 +                      "IP address used in the context of an unexpected type of socket");
 +      }
 +      return 0;
 +}